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 卡挂载

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

    bash 复制代码
    ffplay -f mjpeg -i vid_1717000000.mjpeg
  • 确保每帧以 0xFFD8 开始、0xFFD9 结束

  • 检查文件是否完整

❌ 问题2:录制帧率低

优化

  • 使用 QVGAQQVGA 分辨率
  • 降低 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 卡存储视频帧
  • 视频回放与显示
  • 性能优化与问题排查

相关推荐
风象南34 分钟前
Claude Code这个隐藏技能,让我告别PPT焦虑
人工智能·后端
Mintopia1 小时前
OpenClaw 对软件行业产生的影响
人工智能
陈广亮2 小时前
构建具有长期记忆的 AI Agent:从设计模式到生产实践
人工智能
会写代码的柯基犬2 小时前
DeepSeek vs Kimi vs Qwen —— AI 生成俄罗斯方块代码效果横评
人工智能·llm
Mintopia2 小时前
OpenClaw 是什么?为什么节后热度如此之高?
人工智能
爱可生开源社区3 小时前
DBA 的未来?八位行业先锋的年度圆桌讨论
人工智能·dba
叁两5 小时前
用opencode打造全自动公众号写作流水线,AI 代笔太香了!
前端·人工智能·agent
前端付豪6 小时前
LangChain记忆:通过Memory记住上次的对话细节
人工智能·python·langchain
strayCat232556 小时前
Clawdbot 源码解读 7: 扩展机制
人工智能·开源