(十)在50块钱的芯片上做视频推流:H.264硬件编码+RTSP实战

在50块钱的芯片上做视频推流:H.264硬件编码+RTSP实战

文章目录

  • 在50块钱的芯片上做视频推流:H.264硬件编码+RTSP实战
    • 一、ESP32-P4凭什么值那多出来的几十块?
    • 二、ESP-VISION的输出方式全景
    • 三、静态图像编码:JPEG vs PNG
      • 3.1 JPEG编码
      • 3.2 PNG编码
      • 3.3 编码器对比
    • 四、ImageIO:帧序列录制
    • 五、H.264硬件编码:视频压缩的核心
      • 5.1 为什么不用逐帧JPEG?
      • 5.2 H.264编码器配置
      • 5.3 关键参数详解
    • 六、RTSP推流:让VLC看到你的摄像头
      • 6.1 完整RTSP推流示例
      • 6.2 客户端播放
      • 6.3 RTSP服务器的队列机制
    • 七、USB CDC预览:开发调试最快的方式
    • 八、一个完整的监控摄像头方案
    • 参考链接
    • 总结与下篇预告

一、ESP32-P4凭什么值那多出来的几十块?

如果你看了本系列第二篇的芯片对比,你一定记得:H.264硬件编码和RTSP推流是ESP32-P4的独占功能

但这到底意味着什么?简单说——你可以在ESP32-P4X-EYE(不到200元)上实现一个完整的网络摄像头,VLC播放器直接打开rtsp://192.168.1.100:8554/就能看到实时画面。

今天这篇文章,我把编解码和推流的完整链路讲清楚。

二、ESP-VISION的输出方式全景

开发调试

静态图像

静态图像

帧序列

实时视频

LCD显示

📷 sensor.snapshot()

输出方式

USB CDC预览
img.flush()

JPEG编码
img.to_jpeg()

PNG编码
img.to_png()

ImageIO录制
imageio.ImageIO

H.264编码
h264.H264Encoder

display.write()

RTSP推流
rtsp.RTSPServer

保存到SD卡
.h264文件

三、静态图像编码:JPEG vs PNG

3.1 JPEG编码

importsensor,image sensor.reset()sensor.set_pixformat(sensor.RGB565)sensor.set_framesize(sensor.VGA)sensor.skip_frames(time=2000)img=sensor.snapshot()# 编码为JPEGjpeg_data=img.to_jpeg(quality=85)# quality: 1-100# 或者用别名jpeg_data=img.compress(quality=85)# 保存到SD卡withopen("/sdcard/capture.jpg","wb")asf:f.write(jpeg_data)# 调整质量与大小small_jpeg=img.to_jpeg(quality=50)# 更小,画质更低hq_jpeg=img.to_jpeg(quality=95)# 更大,画质更高# 色度子采样控制img.to_jpeg(quality=85,subsampling=image.JPEG_SUBSAMPLING_420)# 最小文件img.to_jpeg(quality=85,subsampling=image.JPEG_SUBSAMPLING_444)# 最佳画质

3.2 PNG编码

# 无损PNG,适合保存参考帧png_data=img.to_png()withopen("/sdcard/reference.png","wb")asf:f.write(png_data)

3.3 编码器对比

编码方式文件大小(VGA)画质编码速度硬件加速
JPEG(quality=85)~30KB优秀✅ P4硬件
JPEG(quality=50)~10KB一般✅ P4硬件
PNG~200KB无损❌ 纯软件

四、ImageIO:帧序列录制

不止能拍单张,还能录制帧序列:

importsensor,image,imageio,time sensor.reset()sensor.set_pixformat(sensor.RGB565)sensor.set_framesize(sensor.QVGA)sensor.skip_frames(time=2000)# 录制5秒recorder=imageio.ImageIO("/sdcard/sequence.imgio","w")start=time.ticks_ms()whiletime.ticks_diff(time.ticks_ms(),start)<5000:img=sensor.snapshot()recorder.write(img)# 保留帧间时间戳recorder.close()# 回放player=imageio.ImageIO("/sdcard/sequence.imgio","r")whileTrue:img=player.read()ifimgisNone:break# 处理/显示帧player.close()

五、H.264硬件编码:视频压缩的核心

5.1 为什么不用逐帧JPEG?

逐帧JPEG做视频的问题:每帧都是独立压缩的,无法利用帧间相似性

一帧VGA JPEG约30KB,30fps就是900KB/s。而H.264利用帧间预测,同样的画质可能只需要200KB/s——压缩率提升4倍以上

5.2 H.264编码器配置

importsensor,h264 sensor.reset()sensor.set_pixformat(sensor.RGB565)sensor.set_framesize(sensor.VGA)sensor.skip_frames(time=2000)# 创建编码器encoder=h264.H264Encoder(gop=30,# 关键帧间隔(每30帧一个I帧)bitrate=2000000,# 目标码率 2Mbpsqp_min=10,# 最小量化参数(画质上限)qp_max=40,# 最大量化参数(画质下限))# 编码一帧img=sensor.snapshot()nal_units=encoder.encode(img)# 返回NAL单元

5.3 关键参数详解

参数含义低延迟高质量低带宽
gop关键帧间隔1560120
bitrate目标码率4Mbps8Mbps1Mbps
qp_min画质上限15520
qp_max画质下限352545

⚠️注意:丢失一个P帧会导致画面损坏,直到下一个I帧(关键帧)为止。gop越小,恢复越快,但文件越大。

六、RTSP推流:让VLC看到你的摄像头

6.1 完整RTSP推流示例

importsensor,h264,rtsp,network,time# 1. 连接WiFiwlan=network.WLAN(network.STA_IF)wlan.active(True)wlan.connect("YOUR_SSID","YOUR_PASSWORD")whilenotwlan.isconnected():time.sleep_ms(100)print(f"IP:{wlan.ifconfig()[0]}")# 2. 初始化摄像头sensor.reset()sensor.set_pixformat(sensor.RGB565)sensor.set_framesize(sensor.VGA)sensor.skip_frames(time=2000)# 3. 创建H.264编码器encoder=h264.H264Encoder(gop=30,bitrate=2000000)# 4. 创建RTSP服务器server=rtsp.RTSPServer(port=8554)print(f"RTSP: rtsp://{wlan.ifconfig()[0]}:8554/")# 5. 采集-编码-推流循环clock=time.clock()whileTrue:clock.tick()img=sensor.snapshot()nal_units=encoder.encode(img)server.send(nal_units)# 发送编码后的数据print(f"FPS:{clock.fps():.1f}")

6.2 客户端播放

# VLCvlc rtsp://192.168.1.100:8554/# ffplayffplay rtsp://192.168.1.100:8554/# OpenCVimportcv2 cap=cv2.VideoCapture("rtsp://192.168.1.100:8554/")whileTrue: ret, frame=cap.read()cv2.imshow("ESP32", frame)

6.3 RTSP服务器的队列机制

RTSP服务器维护一个短小且有序的帧队列。如果客户端缓慢或缺席:

  • 服务器会整帧丢弃,而不是阻塞采集循环
  • 不会发送会破坏码流的半帧
  • 采集循环不受影响,保持稳定帧率

七、USB CDC预览:开发调试最快的方式

开发期间,最快看到摄像头画面的方式不是RTSP,而是USB CDC:

importsensor,image,time sensor.reset()sensor.set_pixformat(sensor.RGB565)sensor.set_framesize(sensor.QVGA)sensor.skip_frames(time=2000)whileTrue:img=sensor.snapshot()img.flush()# 通过USB CDC发送JPEG预览到主机time.sleep_ms(50)

不需要WiFi、不需要SD卡、不需要网络配置。USB线插上就能看,VSCode扩展和Web IDE都能直接显示。

八、一个完整的监控摄像头方案

""" ESP32-P4 网络监控摄像头 功能:H.264编码 + RTSP推流 + 定时截图保存 """importsensor,h264,rtsp,network,time,os# === WiFi连接 ===wlan=network.WLAN(network.STA_IF)wlan.active(True)wlan.connect("MyWiFi","password")whilenotwlan.isconnected():time.sleep_ms(100)# === 摄像头初始化 ===sensor.reset()sensor.set_pixformat(sensor.RGB565)sensor.set_framesize(sensor.VGA)sensor.skip_frames(time=2000)# === 编码器与推流 ===encoder=h264.H264Encoder(gop=30,bitrate=2000000)server=rtsp.RTSPServer(port=8554)print(f"📡 RTSP: rtsp://{wlan.ifconfig()[0]}:8554/")# === 主循环 ===clock=time.clock()last_snapshot=0snapshot_interval=60# 每60秒截图一次whileTrue:clock.tick()img=sensor.snapshot()# H.264编码 + RTSP推流nal_units=encoder.encode(img)server.send(nal_units)# 定时保存截图now=time.time()ifnow-last_snapshot>snapshot_interval:withopen(f"/sdcard/snapshot_{int(now)}.jpg","wb")asf:f.write(img.to_jpeg(quality=85))last_snapshot=nowprint(f"FPS:{clock.fps():.1f}")

参考链接

  • ESP-VISION 编解码与推流
  • ESP-VISION API - h264
  • ESP-VISION API - rtsp

总结与下篇预告

ESP32-P4的H.264硬件编码和RTSP推流,让200块的开发板具备了完整的网络摄像头能力。核心参数调优记住三个:gop控制恢复速度,bitrate控制码率,qp控制画质。开发调试用USB CDC预览(最快),产品部署走RTSP推流。

下篇我们聊聊软件包管理和部署——你的Python代码怎么组织、怎么分发、怎么更新。


作者:码农阿虎

关键词:H.264、RTSP、视频推流、硬件编码、ESP32-P4、网络摄像头