Concepts First
- MJPEG (Motion JPEG): video encoded as a sequence of independent JPEG frames; simple and reliable, commonly used for camera preview/storage.
- JPEG header: a JPEG file starts with SOI (
0xFFD8) and ends with EOI (0xFFD9). The example generates the header withJpegHeadCreateand feeds it to the encoder. - YUV422: a pixel format (luma Y + two chroma UV); the MJPEG encoder input format is
MJPEG_FORMAT_YUV422_YUYV. - Hardware encoder: BL616/BL618 embed an MJPEG encoder peripheral (device name
"mjpeg"); the example drives it withbflb_mjpeg_*APIs.
Example Overview
This page is based on the mjpeg_no_camera example in the official Bouffalo SDK (examples/peripherals/mjpeg/mjpeg_no_camera), which demonstrates the basic usage of the MJPEG hardware encoder:
- Initializes the encoder (
bflb_mjpeg_init) with YUV422 input, quality, and output buffer; - Generates the JPEG header (
JpegHeadCreate) and feeds it to the encoder (bflb_mjpeg_fill_jpeg_header_tail); - Triggers compression with the built-in 64x64 test image (
bflb_mjpeg_sw_run) and retrieves a frame in the interrupt; - Prints the frame address/size and dumps the JPEG data in hex to verify the result.
- Sibling examples (
examples/peripherals/mjpeg/):mjpeg_cam_normal(camera capture),mjpeg_cam_crop(cropping),mjpeg_cam_swap(pixel swap),mjpeg_recv_from_uart(UART receive).
Note
This page is the camera-less variant: run the encode flow with a test image first; for real frames, use the mjpeg_cam_* examples with a DVP camera.
Operation Steps
This page needs no camera (the example compresses a built-in test image to MJPEG). Open a terminal and enter the SDK no-camera MJPEG example directory (prerequisite: set up the environment as in Quick Start (Linux) or Windows):
cd examples/peripherals/mjpeg/mjpeg_no_cameraRun the build command. The Ai-M62 (BL616) and Ai-M61 (BL618) belong to the same series, so both use bl616:
make CHIP=bl616 BOARD=bl616dkConnect the board with a USB cable, hold the BOOT button (IO2 on the Ai-M61-32S-Kit), briefly press EN/RST to enter download mode, then flash (replace the serial port with the one on your computer):
make flash CHIP=bl616 COMX=/dev/ttyUSB0Open a serial tool (baud rate 2000000). The program compresses the built-in 64x64 test image, prints the frame address and size (jpg addr:... ,jpg size:...), dumps the JPEG data in hex, then repeats every 2 seconds.
Code Execution Flow
The complete flow from startup to JPEG output is shown below:
APIs Used by the Example
bflb_mjpeg_init(mjpeg, &config)
Initializes the MJPEG encoder. config sets the input format (MJPEG_FORMAT_YUV422_YUYV), quality, resolution, and input/output buffers.
Parameters:
mjpeg: encoder device handle (bflb_device_get_by_name("mjpeg"))config:bflb_mjpeg_config_sconfig struct
Return: 0 on success; negative on failure
JpegHeadCreate(mode, quality, x, y, buf)
Generates the JPEG file header (SOI, quantization tables, etc.); the example creates it in YUV422 mode and feeds it to the encoder.
Parameters:
mode: color mode (YUV_MODE_422in the example)quality: compression qualityx/y: image width/heightbuf: buffer receiving the header
Return: generated header length
bflb_mjpeg_fill_jpeg_header_tail(mjpeg, buf, len)
Feeds the JPEG header into the encoder; subsequent compressed frames carry it automatically.
Parameters:
mjpeg: encoder device handlebuf/len: header data and length
Return: 0 on success
bflb_mjpeg_sw_run(mjpeg, 1)
Software-triggers one compression. When done, the encoder raises an interrupt (MJPEG_INTSTS_ONE_FRAME); retrieve the frame in the ISR with bflb_mjpeg_get_frame_info.
Parameters:
mjpeg: encoder device handlerun:1to trigger
Return: none
bflb_mjpeg_get_frame_info / bflb_mjpeg_pop_one_frame(mjpeg, &addr) / (mjpeg)
get_frame_info returns the current frame address and length; pop_one_frame consumes the frame and releases the buffer.
Parameters:
mjpeg: encoder device handleaddr: returns the frame address
Return: get_frame_info returns the frame length
Complete Code
The complete source below matches the official example (examples/peripherals/mjpeg/mjpeg_no_camera) verbatim. Collapsed by default, click to expand:
📜 Click to expand mjpeg_no_camera/main.c full code
#include "bflb_mtimer.h"
#include "bflb_mjpeg.h"
#include "board.h"
#include "../jpeg_head.h"
#include "test_64x64.h"
#define X 64
#define Y 64
uint8_t jpg_head_buf[800] = { 0 };
uint32_t jpg_head_len;
uint8_t MJPEG_QUALITY = 50;
static __attribute__((aligned(32))) ATTR_NOINIT_PSRAM_SECTION uint8_t mjpeg_buffer[50 * 1024];
void bflb_mjpeg_dump_hex(uint8_t *data, uint32_t len)
{
uint32_t i = 0;
for (i = 0; i < len; i++) {
if (i % 16 == 0) {
printf("\r\n");
}
printf("%02x ", data[i]);
}
printf("\r\n");
}
static struct bflb_device_s *mjpeg;
volatile uint32_t pic_count = 0;
volatile uint8_t *pic_addr;
volatile uint32_t pic_len;
void mjpeg_isr(int irq, void *arg)
{
uint32_t intstatus = bflb_mjpeg_get_intstatus(mjpeg);
if (intstatus & MJPEG_INTSTS_ONE_FRAME) {
bflb_mjpeg_int_clear(mjpeg, MJPEG_INTCLR_ONE_FRAME);
uint8_t *tmp_addr;
pic_len = bflb_mjpeg_get_frame_info(mjpeg, &tmp_addr);
pic_addr = tmp_addr;
pic_count = 1;
bflb_mjpeg_pop_one_frame(mjpeg);
}
}
void mjpeg_init(uint32_t x, uint32_t y, uint8_t *yuv, uint8_t *jpeg, uint32_t size, uint8_t quality)
{
struct bflb_mjpeg_config_s config;
mjpeg = bflb_device_get_by_name("mjpeg");
config.format = MJPEG_FORMAT_YUV422_YUYV;
config.quality = quality;
config.rows = y;
config.resolution_x = x;
config.resolution_y = y;
config.input_bufaddr0 = (uint32_t)yuv;
config.input_bufaddr1 = 0;
config.output_bufaddr = (uint32_t)jpeg;
config.output_bufsize = size;
config.input_yy_table = NULL; /* use default table */
config.input_uv_table = NULL; /* use default table */
bflb_mjpeg_init(mjpeg, &config);
jpg_head_len = JpegHeadCreate(YUV_MODE_422, quality, x, y, jpg_head_buf);
bflb_mjpeg_fill_jpeg_header_tail(mjpeg, jpg_head_buf, jpg_head_len);
bflb_mjpeg_tcint_mask(mjpeg, false);
bflb_irq_attach(mjpeg->irq_num, mjpeg_isr, NULL);
bflb_irq_enable(mjpeg->irq_num);
}
void mjpeg_compress(uint8_t *yuv, uint8_t *jpeg, uint32_t size)
{
bflb_mjpeg_update_input_output_buff(mjpeg, yuv, NULL, jpeg, size);
bflb_mjpeg_sw_run(mjpeg, 1);
}
int main(void)
{
board_init();
mjpeg_init(X, Y, NULL, NULL, 0, MJPEG_QUALITY);
/* compress one pic */
mjpeg_compress(test_64x64, mjpeg_buffer, 50 * 1024);
while (pic_count != 1) {
bflb_mtimer_delay_ms(200);
}
printf("jpg addr:%08x ,jpg size:%d\r\n", pic_addr, pic_len);
bflb_mjpeg_dump_hex((uint8_t *)pic_addr, pic_len);
while (1) {
bflb_mtimer_delay_ms(2000);
}
}FAQ
The program hangs waiting for pic_count
Confirm the board has PSRAM (the output buffer is placed in ATTR_NOINIT_PSRAM_SECTION) and it initializes correctly; if the compress interrupt never fires, check bflb_irq_attach/enable and the encoder config.
How do I capture real frames from a camera
Use the mjpeg_cam_normal examples and connect a DVP camera (data/clock/hsync/vsync pins per your board); feed the camera's YUV data to the encoder as input.
The output JPEG cannot be opened
Confirm the header generated by JpegHeadCreate matches the image and the YUV422 input format; a very low quality value makes the image blurry but still openable.
Have questions?
For any other questions, visit the unified Q&A and discussion board: Ai-Thinker Discussions

