K230基础-录放视频
第二十三章 录放视频-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 卡挂载
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 视频录制主程序
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) / 1000print("录制中: %.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 解码与显示(逐帧)
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 = 0start_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 += 1except:continueduration = time.ticks_diff(time.ticks_ms(), start_time) / 1000print(f"✅ 回放完成,共 {frame_count} 帧,耗时 {duration:.1f}s")except Exception as e:print("❌ 回放失败:", e)finally:f.close()# 开始回放
play_video()
⚠️ 限制:K230 无硬件 MJPEG 解码,需逐帧解析,效率较低。
5. 实战项目:带状态显示的视频记录仪
# 结合 OLED 或 TFT 显示录制状态import sensor
import lcd # 假设有 lcd 模块
import time
import ossensor.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 = 0while 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 控制录制大小
MAX_SIZE = 100 * 1024 * 1024 # 100MB
if f.tell() > MAX_SIZE:print("视频达到最大尺寸,停止")break
6.2 使用环形缓冲(覆盖最旧视频)
# 策略:删除最早生成的视频文件
files = os.listdir("/sd/video")
if len(files) > 10:files.sort()os.remove("/sd/video/" + files[0])
6.3 添加时间戳水印
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:
ffplay -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 卡存储视频帧
- 视频回放与显示
- 性能优化与问题排查