最近在做一个python项目要求把视频延迟几分钟播放,对视频流这个概念不明觉厉的我就构思了一个不用流的方案:
方案一(很辣鸡):fork一个子进程。主进程负责拍视频,子进程负责调用命令行播放视频。两个进程之间用Queue通讯,主进程拍摄完一段5分钟视频后调用Queue().put放一个字符进去,等子进程放完上一个视频,把刚才put进Queue()的字符get出来,再由子进程put一个字符然后开始播放刚拍好的视频,主进程get到子进程put进Queue()的字符以后才继续拍下一个视频。即,父子进程通过Queue做进程同步。
这个方案,首先一个显眼的bug是同步的时候子进程总是会不小心把自己刚put进去的字符get出来。抛开这个bug,还有一个早就发现却迟迟不能解决的bug: 树莓派的两款视频播放器omxplayer和vlc在执行这个方案的时候都各自有问题。
一开始用omxplayer发现播放的速度总是快于实际拍摄的速度。几乎是拍摄速度的一半。
后来发现h264的系统默认播放器vlc可以以正常速度播放,但在多进程调用时却始终黑屏放不出来,偶尔在视频的最末会突然不黑屏了,不知道是为什么。于是想减少一个进程,不fork了,而改用subprocess的Popen函数直接开vlc播放,结果发现还是会黑屏。在确认了树莓派是4核CPU以后,弄了好久才想到可能是vlc不能和PiCamera同时工作。
中途又尝试搜索了一下流的方案,发现网上讲的都太无关或者太繁了。但我隐约觉得文件如果可以好好操作的话也可以在一个文件里做流啊
于是还是回到了omxplayer的怀抱。但那个播放速度是拍摄速度一半的问题怎么解决呢?
这个时候我的代码已经删掉了好多原来写的非常臃肿的print和多进程的内容显得很清洁。
我抱着尝试的心理重写了一下,用了subprocess的Popen来调用omxplayer播放上一个周期的视频文件。因为Popen是非阻塞的,所以可以直接继续拍下一个周期的视频文件。这会居然莫名其妙地成功了,omxplayer没有快很多。基本上和拍摄的时间持平了!这会我才意识到可能是omxplayer在播放h264文件的时候就是默认它是流,所以会以恰好两倍的速率dedicatedly解码,这样拍摄的编码和播放的编码同时进行,各占用一半的计算时间,就正好就能以正常的速率实时播放了。原来omxplayer是为了配合h264进行视频流播放而专门设计成这样的啊。
接下来还有一个bug,就是两个视频接续之间会有一些时间误差:有时候是上个视频没播完下个视频已经开始放了,就会呈现画面不停地跳帧;有时候视频结束得太早,会空出没有视频的空档。 这怎么办呢?
方案二:那试试看直接播放正在拍摄的文件吧?!我去,这个想法在我看来一直是一个错误的做法,文件正在被写的同时怎么可能还可以读呢!事实证明是我too naive
最后采用这种方案几行代码就完美搞定了。原来这就是流!流弊!
from picamera import PiCamera
import subprocess
import os
FILE = "/home/pi/Documents/Python/1.h264"
Delay_period = 60
def play_while_rec_with_delay():
cmd = "omxplayer " + FILE
cam = PiCamera()
cam.start_recording(FILE)
cam.wait_recording(Delay_period)
subprocess.Popen(cmd.split())
if __name__ == "__main__":
play_while_rec_with_delay()
所以之所以omxplayer单独录会快一倍,是因为它就是设计给流视频,一边录一边放的呀!