概念先知道
- MJPEG(Motion JPEG):把视频按“一帧一帧 JPEG 图片”压缩的方式,每帧独立编码,简单可靠,常用于摄像头预览/存储。
- JPEG 头(header):JPEG 文件以 SOI(
0xFFD8)开头、EOI(0xFFD9)结尾,中间是图像数据。例程用JpegHeadCreate生成头并填到编码器。 - YUV422:一种像素格式(亮度 Y + 两个色差 UV),MJPEG 编码器的输入格式为
MJPEG_FORMAT_YUV422_YUYV。 - 硬件编码器:BL616/BL618 内置 MJPEG 编码器外设(设备名
"mjpeg"),例程用bflb_mjpeg_*API 驱动。
例程功能简介
本页对应博流官方 SDK 的 mjpeg_no_camera 例程(examples/peripherals/mjpeg/mjpeg_no_camera),演示 MJPEG 硬件编码器的基础用法:
- 初始化 MJPEG 编码器(
bflb_mjpeg_init),配置 YUV422 输入、质量与输出缓冲区; - 生成 JPEG 头(
JpegHeadCreate)并写入编码器(bflb_mjpeg_fill_jpeg_header_tail); - 用内置 64×64 测试图触发压缩(
bflb_mjpeg_sw_run),中断里取回一帧数据; - 打印帧地址/大小并十六进制输出,验证压缩结果。
- 同族例程(
examples/peripherals/mjpeg/):mjpeg_cam_normal(摄像头采集)、mjpeg_cam_crop(裁剪)、mjpeg_cam_swap(像素交换)、mjpeg_recv_from_uart(串口接收)。
提示
本页是无摄像头版,先用测试图跑通编码流程;需要真实画面时改用 mjpeg_cam_* 例程并接 DVP 摄像头。
操作步骤
本页无需摄像头(例程用内置测试图压缩 MJPEG)。在终端进入 SDK 的 MJPEG 无摄像头例程目录(前提:已按快速开始(Linux)或Windows搭建好环境):
cd examples/peripherals/mjpeg/mjpeg_no_camera执行编译命令。Ai-M62(BL616)与 Ai-M61(BL618)同属一个系列,统一填写引脚最少的 bl616 即可:
make CHIP=bl616 BOARD=bl616dk用 USB 线连接开发板,按住 BOOT 键(Ai-M61-32S-Kit 为 IO2)不放、短按 EN/RST 进入下载模式,然后执行烧录(把串口号换成实际值):
make flash CHIP=bl616 COMX=/dev/ttyUSB0打开串口助手(波特率 2000000)。程序把内置 64×64 测试图压缩为 JPEG 后打印帧地址与大小(jpg addr:... ,jpg size:...),并以十六进制输出该 JPEG 数据,随后每 2 秒重复。
代码执行流程
例程从启动到输出 JPEG 的完整流程如下:
例程调用的 API 介绍
bflb_mjpeg_init(mjpeg, &config)
初始化 MJPEG 编码器。config 中需配置输入格式(MJPEG_FORMAT_YUV422_YUYV)、质量、分辨率、输入/输出缓冲区等。
参数:
mjpeg:编码器设备句柄(bflb_device_get_by_name("mjpeg"))config:bflb_mjpeg_config_s配置结构体
返回值:成功返回 0;失败返回负值
JpegHeadCreate(mode, quality, x, y, buf)
生成 JPEG 文件头(SOI、量化表等),例程按 YUV422 模式生成后写入编码器。
参数:
mode:色彩模式(例程YUV_MODE_422)quality:压缩质量x/y:图像宽高buf:接收 JPEG 头的缓冲区
返回值:生成的头部长度
bflb_mjpeg_fill_jpeg_header_tail(mjpeg, buf, len)
把 JPEG 头写入编码器,之后的压缩帧会自动带上该头。
参数:
mjpeg:编码器设备句柄buf/len:JPEG 头数据与长度
返回值:成功返回 0
bflb_mjpeg_sw_run(mjpeg, 1)
软件触发一次压缩。压缩完成后产生中断(MJPEG_INTSTS_ONE_FRAME),在中断里用 bflb_mjpeg_get_frame_info 取帧。
参数:
mjpeg:编码器设备句柄run:1触发
返回值:无
bflb_mjpeg_get_frame_info / bflb_mjpeg_pop_one_frame(mjpeg, &addr) / (mjpeg)
get_frame_info 返回当前帧地址与长度;pop_one_frame 取走该帧释放缓冲。
参数:
mjpeg:编码器设备句柄addr:返回帧地址
返回值:get_frame_info 返回帧长度
完整代码
以下为 mjpeg_no_camera/main.c 完整源码,与官方示例(examples/peripherals/mjpeg/mjpeg_no_camera)一致,默认折叠,点击展开:
📜 点击展开 mjpeg_no_camera/main.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
程序卡在等待 pic_count
确认开发板具备 PSRAM(输出缓冲区放在 ATTR_NOINIT_PSRAM_SECTION)且初始化正常;压缩中断未产生时检查 bflb_irq_attach/enable 与编码器配置。
如何接摄像头取真实画面
改用 mjpeg_cam_normal 等例程,并接线 DVP 摄像头(数据/时钟/行场同步引脚按板子配置);摄像头采集的 YUV 数据作为编码器输入即可。
输出的 JPEG 打不开
确认 JpegHeadCreate 生成的头部长度与图像匹配,且使用的是同款 YUV422 输入格式;质量参数过低时画面会明显变糊,但不影响打开。
遇到问题?
如有其他问题,请到统一的提问与讨论区:Ai-Thinker Discussions

