第二十三章 录放视频-K230视频录制与回放能力
🎯 本章目标 :
掌握在 K230(CanMV 平台)上实现视频录制与回放的核心方法,学会将摄像头采集的图像帧保存为视频文件(如 MJPEG 流),并实现本地存储与回放,构建嵌入式监控、记录仪等应用。
1. 视频处理能力概述
1.1 K230 视频支持现状
- ✅ 支持 摄像头实时采集
- ✅ 支持 JPEG 图像编码(由摄像头硬件完成)
- ✅ 支持 MJPEG 视频流(连续 JPEG 帧)
- ❌ 不支持 H.264/HEVC 等高压缩编码
- ✅ 支持 SD 卡存储
- ❌ 无硬件视频编码器,依赖摄像头输出 JPEG
📌 结论 :
可通过 摄像头输出 MJPEG 流 + SD 卡存储 实现"类视频"录制。
2. 视频格式说明
2.1 MJPEG(Motion JPEG)
- 将视频每一帧作为独立 JPEG 图像存储
- 文件扩展名通常为
.avi
或.mjpeg
- 优点:简单、兼容性好、无需复杂编码
- 缺点:文件大、压缩率低
✅ K230 最可行方案
3. 视频录制:将图像帧保存为 MJPEG
3.1 硬件准备
- K230 开发板
- 摄像头(OV2640 推荐)
- SD 卡(≥8GB,Class 10)
- FPIOA 映射 SPI 引脚(见 SPI 章节)
3.2 SD 卡挂载
python
import os
import sdcard
from machine import SPI, Pin
from fpioa_manager import fm
# SPI 引脚定义
SCLK_PIN = 6
MOSI_PIN = 8
MISO_PIN = 9
CS_PIN = 7
# FPIOA 映射
fm.register(SCLK_PIN, fm.fpioa.SPI1_SCLK)
fm.register(MOSI_PIN, fm.fpioa.SPI1_D0)
fm.register(MISO_PIN, fm.fpioa.SPI1_D1)
fm.register(CS_PIN, fm.fpioa.GPIOHS0)
# 初始化 SPI
spi = SPI(SPI.SPI1, baudrate=20_000_000, polarity=0, phase=0)
cs = Pin(CS_PIN, Pin.OUT, value=1)
# 挂载 SD 卡
sd = sdcard.SDCard(spi, cs)
os.mount(sd, "/sd")
print("SD 卡已挂载")
3.3 视频录制主程序
python
import sensor
import time
import os
# 初始化摄像头
sensor.reset()
sensor.set_pixformat(sensor.JPEG) # 必须使用 JPEG 格式
sensor.set_framesize(sensor.VGA) # 640x480
sensor.skip_frames(time=2000)
# 设置 JPEG 质量
sensor.set_quality(10) # 1~63,越小质量越高
print("开始视频录制...")
# 创建视频目录
try:
os.mkdir("/sd/video")
except:
pass
# 视频文件名
filename = "/sd/video/vid_%d.mjpeg" % time.time()
f = open(filename, "wb")
# 录制时长(秒)
DURATION = 30
FPS = 10
INTERVAL = int(1000 / FPS) # 毫秒
clock = time.clock()
start_time = time.ticks_ms()
try:
while time.ticks_diff(time.ticks_ms(), start_time) < DURATION * 1000:
clock.tick()
# 拍摄一帧(JPEG 格式)
img = sensor.snapshot()
# 获取原始 JPEG 数据
jpeg_data = img.compress_to_jpeg() # 或直接使用 img.bytearray()
# 写入文件
f.write(jpeg_data)
# 可选:显示进度
elapsed = time.ticks_diff(time.ticks_ms(), start_time) / 1000
print("录制中: %.1fs / %ds, FPS: %.1f" % (elapsed, DURATION, clock.fps()))
# 控制帧率
time.sleep_ms(max(1, INTERVAL - clock.fps()))
print("✅ 视频录制完成:", filename)
except Exception as e:
print("❌ 录制出错:", e)
finally:
f.close()
🔧 说明:
- 生成的
.mjpeg
文件可在 VLC、FFmpeg 等工具中播放- 每帧为独立 JPEG,拼接成 MJPEG 流
4. 视频回放:从文件读取并显示
4.1 MJPEG 解码与显示(逐帧)
python
import os
import image
from machine import Timer
# 视频文件路径
video_file = "/sd/video/vid_1717000000.mjpeg"
# 初始化 TFT 屏幕(见第13章)
# tft = st7789.ST7789(...)
def play_video():
try:
f = open(video_file, "rb")
print("开始回放视频...")
frame_count = 0
start_time = time.ticks_ms()
while True:
# 尝试读取 JPEG 帧(查找 0xFFD8 和 0xFFD9)
data = f.read(1024)
if not data:
break
# 查找 JPEG 起始与结束
start = data.find(b'\xFF\xD8')
end = data.find(b'\xFF\xD9')
if start != -1 and end != -1 and end > start:
jpeg_frame = data[start:end+2]
# 创建图像对象并显示
try:
img = image.Image(jpeg_frame)
# 缩放显示到 TFT
# tft.blit_buffer(img.bytearray(), ...)
frame_count += 1
except:
continue
duration = time.ticks_diff(time.ticks_ms(), start_time) / 1000
print(f"✅ 回放完成,共 {frame_count} 帧,耗时 {duration:.1f}s")
except Exception as e:
print("❌ 回放失败:", e)
finally:
f.close()
# 开始回放
play_video()
⚠️ 限制:K230 无硬件 MJPEG 解码,需逐帧解析,效率较低。
5. 实战项目:带状态显示的视频记录仪
python
# 结合 OLED 或 TFT 显示录制状态
import sensor
import lcd # 假设有 lcd 模块
import time
import os
sensor.reset()
sensor.set_pixformat(sensor.JPEG)
sensor.set_framesize(sensor.QVGA) # 320x240
sensor.set_quality(12)
sensor.skip_frames(time=2000)
# 录制函数
def record_video(duration=10):
filename = f"/sd/vid_{time.time()}.mjpeg"
f = open(filename, "wb")
lcd.fill(0)
lcd.draw_string(10, 10, "REC", color=(255,0,0))
lcd.draw_string(10, 30, filename, color=(255,255,255))
start = time.ticks_ms()
frame_count = 0
while time.ticks_diff(time.ticks_ms(), start) < duration * 1000:
img = sensor.snapshot()
f.write(img.compress_to_jpeg())
frame_count += 1
# 更新显示
lcd.draw_string(10, 50, f"Frame: {frame_count}", color=(0,255,0))
time.sleep_ms(100)
f.close()
lcd.draw_string(10, 80, "SAVED", color=(0,255,0))
6. 高级技巧与优化
6.1 控制录制大小
python
MAX_SIZE = 100 * 1024 * 1024 # 100MB
if f.tell() > MAX_SIZE:
print("视频达到最大尺寸,停止")
break
6.2 使用环形缓冲(覆盖最旧视频)
python
# 策略:删除最早生成的视频文件
files = os.listdir("/sd/video")
if len(files) > 10:
files.sort()
os.remove("/sd/video/" + files[0])
6.3 添加时间戳水印
python
img = sensor.snapshot()
timestamp = "%02d:%02d:%02d" % (time.localtime()[3:6])
img.draw_string(200, 220, timestamp, color=(255,255,255))
f.write(img.compress_to_jpeg())
7. 常见问题与调试
❌ 问题1:视频文件无法播放
解决:
使用 VLC 播放器 或 FFmpeg:
bashffplay -f mjpeg -i vid_1717000000.mjpeg
确保每帧以
0xFFD8
开始、0xFFD9
结束检查文件是否完整
❌ 问题2:录制帧率低
优化:
- 使用
QVGA
或QQVGA
分辨率- 降低 JPEG 质量(
set_quality(20)
)- 使用高速 SD 卡
❌ 问题3:SD 卡写入失败
排查:
- 检查 SPI 接线
- 确认 SD 卡已格式化为 FAT32
- 避免频繁打开/关闭文件
8. 性能与存储估算
分辨率 | 质量 | 每帧大小 | 10fps 下每分钟存储 |
---|---|---|---|
QVGA (320x240) | 10 | ~8KB | ~4.8MB |
VGA (640x480) | 10 | ~20KB | ~12MB |
SVGA (800x600) | 10 | ~35KB | ~21MB |
✅ 建议:使用 32GB SD 卡可录制数小时 QVGA 视频。
✅ 本章你已掌握:
- K230 视频录制原理(MJPEG 流)
- 摄像头配置为 JPEG 输出
- SD 卡存储视频帧
- 视频回放与显示
- 性能优化与问题排查