解决相机录制线程锁致丢帧问题的合适设计模式问询
解决方案:双缓冲区(Double Buffer)设计模式
问题根源
场景二中的丢帧问题源于单个共享缓冲区的长时间锁定:写入线程需要锁定缓冲区并完成读取、清空操作,这段时间内相机无法写入新帧,最终导致丢帧。
双缓冲区模式的核心思路
维护两个独立的内存缓冲区:
- 相机主线程始终向活跃缓冲区写入帧数据
- 磁盘写入线程处理非活跃缓冲区的数据并写入磁盘
- 当满足切换条件(如固定时间间隔、缓冲区达到阈值)时,仅需短暂锁定完成缓冲区切换,几乎不会阻塞相机写入
优化后的代码实现
import io import time import os from threading import Thread, Event, Lock from picamera2 import Picamera2 from picamera2.encoders import Encoder as NullEncoder from picamera2.outputs import FileOutput def disk_writer(buffers: list[io.BytesIO], bin_fd, write_interval: int, stop_event: Event, lock: Lock): while not stop_event.is_set(): start_loop = time.perf_counter() # 短暂锁定,完成活跃/非活跃缓冲区切换 lock.acquire() buffers[0], buffers[1] = buffers[1], buffers[0] inactive_buff = buffers[1] lock.release() # 处理非活跃缓冲区(此时相机不会写入该缓冲区,无需锁定) curr_buff_pos = inactive_buff.tell() if curr_buff_pos > 0: bin_fd.write(inactive_buff.getvalue()) inactive_buff.seek(0) inactive_buff.truncate(0) elapsed = time.perf_counter() - start_loop if elapsed < write_interval: time.sleep(write_interval - elapsed) # 处理录制结束后剩余的所有缓冲区数据 lock.acquire() for buff in buffers: if buff.tell() > 0: bin_fd.write(buff.getvalue()) buff.seek(0) buff.truncate(0) lock.release() # 初始化相机 cam = Picamera2() # 初始化双缓冲区 buff1 = io.BytesIO() buff2 = io.BytesIO() # 初始将buff1设为活跃缓冲区,供相机写入 mem_out = FileOutput(buff1) # 打开磁盘输出文件 out_fpath = "video.bin" bin_fd = open(out_fpath, "wb") # 启动相机 cam.start() # 创建并启动磁盘写入线程 stop_event = Event() lock = Lock() write_interval = 5 writer_thread = Thread(target=disk_writer, args=([buff1, buff2], bin_fd, write_interval, stop_event, lock)) writer_thread.start() # 配置无编码编码器(直接写入BGR888格式帧数据) encoder = NullEncoder() # 开始录制 to_record = 10 print(f"开始录制,时长{to_record}秒") cam.start_recording(encoder, mem_out) time.sleep(to_record) cam.stop_recording() print("录制结束") # 停止写入线程并等待其完成 stop_event.set() writer_thread.join() cam.close() bin_fd.close() # 统计视频帧数 totbytes = os.path.getsize(out_fpath) byte_per_frame = 2304 * 1296 * 3 # 帧宽度 * 帧高度 * 通道数 num_frames = totbytes / byte_per_frame print(f"视频总帧数:{num_frames}")
额外优化建议
- 如果磁盘写入速度波动较大,可扩展为多缓冲区队列(生产者-消费者模式):用线程安全队列存储多个缓冲区,相机生产满缓冲区,写入线程消费缓冲区,进一步降低阻塞风险
- 可根据单帧大小设置缓冲区阈值,当缓冲区达到指定帧数时触发切换,而非固定时间间隔,适配不同帧率的相机
内容的提问来源于stack exchange,提问作者rober_dinero
相关产品推荐
相关产品推荐

