ESP32-CAM MJPEG流直存SD卡:嵌入式本地录像方案
2026/9/16 2:11:25 网站建设 项目流程

简介:本资源是基于ESP32-CAM模块实现MJPEG视频流实时传输并支持SD卡本地缓存的完整嵌入式开发项目,面向物联网初学者、智能硬件开发者及嵌入式图像传输实践者,解决低功耗设备端视频采集、压缩编码与网络推流的核心技术问题。压缩包共16个文件(150KB),含7个C++源码(如mjpeg2sd.cpp、app_httpd.cpp)、4个头文件(camera_pins.h、myConfig.h等)用于硬件配置与功能模块划分,2张PNG示意图辅助理解系统架构,另有README.md说明文档、主程序INO文件及LICENSE授权文件,结构清晰、模块职责明确,便于快速上手与二次开发。已有2451人学习下载,项目覆盖Wi-Fi连接、OV2640摄像头驱动、JPEG帧编码、HTTP流式传输及SD卡缓冲机制等关键环节,附带OTA升级与运动检测扩展代码,提供从硬件初始化到服务器端呈现的全链路参考实现,是深入理解ESP32-CAM视频应用的高价值实践样本。

1. 用 ESP32-CAM 把 MJPEG 流直接存到 SD 卡:不是“拍照存图”,而是持续录像级的本地缓存方案

你手上的 ESP32-CAM 开发板,摄像头能拍、Wi-Fi 能连、SD 卡槽也焊好了——但默认示例里,要么是 Web Server 实时推 MJPEG 流给浏览器看,要么是单张 JPEG 拍完存卡。而ESP32-CAM_MJPEG2SD-master这个开源项目干了一件更硬核的事:不依赖 PC 或云服务,让 ESP32-CAM 自己把连续的 MJPEG 视频流(非逐帧 JPEG)原样切片、打包、写入 SD 卡,生成可被 VLC/FFmpeg 直接识别的.mjpeg文件。它不是“拍照存图”,而是轻量级嵌入式录像机——适合无人值守的工地监控、农业温棚记录、设备运行状态抓取等场景。对新手,这是理解 ESP32-CAM 存储层与视频流协议协同的关键入口;对有经验的开发者,它暴露了 IDF v4.4+ 中 SDMMC 驱动与 UVC/MJPEG 流解析的边界条件。本文不讲 Blinker App 配置或手机远程预览,只聚焦:如何让这块板子真正“自己录下来”。


2. 为什么选 MJPEG 流直存而非 JPEG 单帧?从协议层看 ESP32-CAM 的存储瓶颈

2.1 MJPEG 流的本质:不是“视频文件”,而是 HTTP 分块传输的 JPEG 序列

MJPEG(Motion JPEG)在 ESP32-CAM 上并非编码后的.avi.mp4,而是服务器通过 HTTP 响应头Content-Type: multipart/x-mixed-replace; boundary=frame,持续推送多个以--frame\r\nContent-Type: image/jpeg\r\n\r\n[JPEG_DATA]\r\n包裹的 JPEG 数据块。每个块含完整 JPEG 头(SOI)、DHT/DQT 表、SOS、图像数据和 EOI。关键点在于:这些块之间无时间戳、无帧率元数据,且长度可变。若按单帧 JPEG 存储,需逐块解包、校验、写文件,I/O 开销极大;而MJPEG2SD的设计哲学是——跳过解码,原样截取并拼接

提示:ESP32-CAM 的 PSRAM 仅 4MB,无法缓存整段流;SD 卡写入速度(尤其 Class 4 卡)约 2–5 MB/s,但 MJPEG 流峰值带宽常达 1–3 MB/s(QVGA@15fps)。必须控制写入粒度,避免 FAT32 文件系统频繁分配簇导致卡顿。

2.2 对比三种常见存储策略:为何 MJPEG2SD 是折中解

方案帧处理方式存储格式CPU 占用SD 卡寿命影响适用场景
单帧 JPEG 存储每帧 decode → encode → write.jpg高(需 JPEG 解码+重编码)高(小文件多,FAT32 碎片化严重)定时抓拍、AI 推理前采样
Raw YUV 存储DMA 直接捕获 sensor 输出.yuv极低中(大文件,但无文件系统开销)调试 sensor 时序、后续 PC 端转码
MJPEG 流直存(本项目)截取 HTTP boundary 块,原样写入.mjpeg低(仅字符串匹配+memcpy)低(单大文件,顺序写入)长时本地录像、离线回放

MJPEG2SD选择第三条路:它不碰 JPEG 数据内容,只识别--frame边界,将每个块(含\r\n\r\n后的 JPEG 二进制)追加到 SD 卡文件末尾。最终生成的.mjpeg文件,可用ffplay -f mjpeg input.mjpeg直接播放,或用ffmpeg -i input.mjpeg -c:v libx264 output.mp4转为标准视频。

2.3 ESP32-CAM 硬件约束下的关键参数取舍

  • 摄像头配置:必须启用PIXFORMAT_JPEG(不可用 RGB565/YUV),否则无 MJPEG 流输出;
  • SD 卡接口:使用SDMMC模式(非 SPI),因 SPI 带宽上限约 20 Mbps,而 QVGA@10fps MJPEG 流常超 25 Mbps;
  • PSRAM 使用:开启CONFIG_ESP32_SPIRAM_SUPPORT,但MJPEG2SD实际不依赖 PSRAM 缓存流——它用双缓冲区(各 8KB)交替接收 HTTP chunk,避免内存碎片;
  • FreeRTOS 任务优先级camera_task设为CONFIG_CAMERA_TASK_PRIORITY=10sd_write_task设为9,确保图像采集不被 I/O 阻塞。

3. 从零编译烧录:ESP32-CAM 刷机程序 + MJPEG2SD 固件下载与配置

3.1 开发环境准备:ESP-IDF v4.4.6 是当前最稳版本

ESP32-CAM_MJPEG2SD-master仓库未声明 IDF 版本,但实测在 IDF v5.1 上因sdmmc_host_t结构体变更导致编译失败。强烈建议锁定 v4.4.6(2022 年 10 月 LTS 版):

# 清理旧环境 rm -rf $HOME/esp mkdir -p $HOME/esp && cd $HOME/esp # 下载并安装 IDF v4.4.6 git clone -b release/v4.4.6 --recursive https://github.com/espressif/esp-idf.git cd esp-idf ./install.sh . ./export.sh

注意:不要用idf.py set-target esp32s2等命令切换芯片——ESP32-CAM 是 ESP32-D0WDQ6,必须保持esp32目标。

3.2 获取源码并修正关键引脚定义

从 GitHub 克隆项目(注意分支):

cd $HOME/esp git clone https://github.com/username/ESP32-CAM_MJPEG2SD.git cd ESP32-CAM_MJPEG2SD git checkout master # 确认是原始 master 分支

ESP32-CAM 开发板存在多种 PCB 版本(AI-Thinker、TTL 模块、第三方兼容板),SD 卡引脚定义极易出错。打开main/camera_pins.h,确认以下宏匹配你的板子:

// AI-Thinker ESP32-CAM 标准版(最常见) #define SD_MISO_GPIO 13 #define SD_MOSI_GPIO 15 #define SD_SCLK_GPIO 14 #define SD_CS_GPIO 2 // 注意:不是 4!部分教程误写为 GPIO4,会导致初始化失败

若使用 TTL 转 USB 模块烧录,还需设置UART0引脚(通常 GPIO1/TX, GPIO3/RX),并在sdkconfig中启用:

CONFIG_ESPTOOLPY_PORT="/dev/ttyUSB0" CONFIG_ESPTOOLPY_BAUD_921600=y CONFIG_ESPTOOLPY_FLASHMODE_QIO=y CONFIG_ESPTOOLPY_FLASHFREQ_40M=y

3.3 编译与烧录:两步命令搞定 ESP32-CAM 刷机程序

# 生成项目配置(会启动 menuconfig) idf.py menuconfig

menuconfig中重点检查:

  • Serial flasher configDefault serial port设为你的串口(如/dev/ttyUSB0);
  • Component configCamera ConfigurationCamera pinoutAI Thinker ESP32-CAM
  • SDMMC ConfigurationSDMMC HostSDMMC(非 SPI);
  • MJPEG2SD ConfigurationOutput filename prefix设为rec_(生成rec_0001.mjpeg);
  • MJPEG2SD ConfigurationMax file size (MB)设为100(防 SD 卡写满)。

保存退出后编译烧录:

# 编译(耗时约 2 分钟) idf.py build # 烧录(自动复位,无需手动按 BOOT 键) idf.py -p /dev/ttyUSB0 flash # 查看日志(观察 SD 卡是否 mount 成功) idf.py -p /dev/ttyUSB0 monitor

成功日志关键行:

I (234) MJPEG2SD: SD card mounted successfully I (235) MJPEG2SD: Creating new file: /sdcard/rec_0001.mjpeg I (236) MJPEG2SD: HTTP server started on http://192.168.4.1/stream

此时开发板会创建 AP 热点ESP32-CAM-XXXX,手机连接后访问http://192.168.4.1/stream即可见实时流——同时 SD 卡已开始写入.mjpeg文件


4. 参数调优与排错:当 ESP32-CAM 固件下载后不录像的 5 类典型问题

4.1 SD 卡无法识别:硬件层排查清单

现象检查项解决方案
SD card mount failedSD 卡是否为 FAT32 格式?mkfs.fat -F32 /dev/sdX重格式化(Linux);Windows 用磁盘管理工具设为 FAT32(非 exFAT)
SD card mount failedGPIO2 是否被其他外设占用?拔掉所有扩展模块,确认SD_CS_GPIO未接 LED 或传感器
SD card mount failedSD 卡是否 >32GB?ESP32 SDMMC 驱动对 >32GB 卡支持不稳定,优先用 16GB Class 10 卡
SD card mount failed供电是否不足?ESP32-CAM 拍照+SD 写入瞬时电流达 500mA,必须用 ≥1A 电源,禁用 USB 2.0 口直供

提示:在monitor日志中搜索sdmmc_init,若出现SDMMC_ERR_TIMEOUT,90% 是供电或接触问题;若出现SDMMC_ERR_INVALID_ARG,则是引脚配置错误。

4.2 录像文件为空或中断:流协议与缓冲区协同调试

MJPEG2SD默认每 10 秒新建一个文件(防止单文件过大)。若rec_0001.mjpeg只有几 KB,说明流未正确捕获。检查:

  • HTTP 流是否真实存在?在 PC 上用curl -s http://192.168.4.1/stream | head -c 1000 | hexdump -C,应看到--frame\r\nContent-Type: image/jpeg\r\n\r\n\xff\xd8\xff\xe0(JPEG SOI 标记);
  • 缓冲区是否溢出?修改main/mjpeg2sd.cBUFFER_SIZE(默认 8192)为16384,重新编译;
  • 写入速率是否超限?sd_write_task中添加计时:
    int64_t start = esp_timer_get_time(); ret = f_write(&fil, buf, len, &bw); int64_t end = esp_timer_get_time(); ESP_LOGI(TAG, "Write %d bytes in %lld us", len, end - start);
    若单次写入 >50000 μs(50ms),说明 SD 卡性能不足,需降帧率。

4.3 帧率与画质平衡:ESP32-CAM 开发板管理地址中的关键配置

main/app_camera.c中调整摄像头参数:

// 关键参数表(实测稳定值) config.xclk_freq_hz = 10000000; // XCLK 降低至 10MHz(原 20MHz 易丢帧) config.pixel_format = PIXFORMAT_JPEG; // 必须 JPEG config.frame_size = FRAMESIZE_QVGA; // QVGA(320x240) 最稳;VGA(640x480) 需 PSRAM 且易卡顿 config.jpeg_quality = 12; // 10~15 间权衡:12 为画质/体积最佳点 config.fb_count = 2; // 双帧缓冲,防采集阻塞

注意:FRAMESIZE_UXGA(1600x1200)在 ESP32-CAM 上不可用——sensor OV2640 不支持该分辨率 JPEG 输出,强行设置会导致CAMERA_FAIL_TO_SET_FRAMESIZE错误。

4.4 文件系统损坏恢复:SD 卡异常断电后的修复流程

若开发板意外断电,SD 卡 FAT32 表可能损坏,表现为f_mount返回FR_NO_FILESYSTEM。此时:

  1. 将 SD 卡插入 PC,用chkdsk /f X:(Windows)或sudo fsck.fat -a /dev/sdX1(Linux)修复;
  2. 删除根目录下所有rec_*.mjpeglost+found/
  3. 重启 ESP32-CAM,它会自动创建新文件。

禁止在录像中拔卡!MJPEG2SD无 journaling 机制,强制拔卡必损文件。


5. 进阶技巧:用 FFmpeg 验证录像完整性与提取关键帧

5.1 一键验证.mjpeg文件是否可播

将 SD 卡取出,插入 PC,执行:

# 检查文件头是否合法(必须以 --frame 开头) head -c 50 rec_0001.mjpeg | strings | head -n 1 # 输出应为:--frame # 检查是否含 JPEG 数据(搜索 ff d8 ff) xxd -l 100 rec_0001.mjpeg | grep "ff d8 ff" # 用 FFmpeg 探测流信息(确认帧率/分辨率) ffmpeg -v quiet -i rec_0001.mjpeg -show_entries stream=width,height,r_frame_rate -of default=nw=1 # 正常输出:width=320, height=240, r_frame_rate=15/1

5.2 从.mjpeg提取指定时间点的 JPEG 图像

MJPEG2SD不存时间戳,但可通过字节偏移估算。假设帧率 12fps,想提取第 60 秒的帧(即第 720 帧):

# 计算平均帧大小(单位:字节) FILE_SIZE=$(stat -c "%s" rec_0001.mjpeg) TOTAL_FRAMES=$(ffprobe -v quiet -show_entries stream=nb_read_frames -of default=nw=1 rec_0001.mjpeg | cut -d= -f2) AVG_FRAME_SIZE=$((FILE_SIZE / TOTAL_FRAMES)) # 定位第 720 帧起始位置(跳过前 719 帧) OFFSET=$((719 * AVG_FRAME_SIZE)) # 提取该帧(从 --frame 开始到下一个 --frame 前) dd if=rec_0001.mjpeg of=frame_720.jpg bs=1 skip=$OFFSET count=100000 2>/dev/null | \ sed -n '/\xff\xd8\xff/,/\xff\xd9/p' > frame_720.jpg

5.3 生成可分享的 MP4:用 H.264 压缩降低体积

原始.mjpeg文件体积巨大(1 小时约 4–6 GB)。转为 H.264:

ffmpeg -f mjpeg -i rec_0001.mjpeg \ -c:v libx264 -preset fast -crf 23 \ -vf "scale=640:480:force_original_aspect_ratio=decrease,pad=640:480:(ow-iw)/2:(oh-ih)/2" \ -r 15 \ rec_0001.mp4

参数说明:

  • -crf 23:质量恒定,18~28 间调节(值越小质量越高);
  • -vf scale=...:保持宽高比并填充黑边,适配手机播放;
  • -r 15:强制输出帧率为 15fps,匹配原始流。

生成的rec_0001.mp4体积可降至原.mjpeg的 1/10,且兼容所有设备。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询