(十)在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的输出方式全景
三、静态图像编码: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 | 关键帧间隔 | 15 | 60 | 120 |
| bitrate | 目标码率 | 4Mbps | 8Mbps | 1Mbps |
| qp_min | 画质上限 | 15 | 5 | 20 |
| qp_max | 画质下限 | 35 | 25 | 45 |
⚠️注意:丢失一个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、网络摄像头
