Skip to content

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 with JpegHeadCreate and 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 with bflb_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

1
Enter the Example Directory

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_camera
2
Build the Project

Run the build command. The Ai-M62 (BL616) and Ai-M61 (BL618) belong to the same series, so both use bl616:

make CHIP=bl616 BOARD=bl616dk
3
Flash the Firmware

Connect 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/ttyUSB0
4
Run and Verify

Open 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_s config 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_422 in the example)
  • quality: compression quality
  • x / y: image width/height
  • buf: 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 handle
  • buf / 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 handle
  • run: 1 to 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 handle
  • addr: 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
c
#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

Released under the MIT License. Build Time 2026-09-11 14:52:23