首页 > 代码库 > 在Linux下播放与录制电视
在Linux下播放与录制电视
最近,在自己的Debian 7.2 64位系统上成功安装了圆刚AverMedia C725B视频卡驱动。于是,可以使用mplayer与mencoder来看电视与录节目了。其中,用于播放电视的命令如下:
mplayer tv:// -tv driver=v4l2:device=/dev/video0:norm=PAL:alsa:adevice=hw.2,0:amode=1:audiorate=48000:forceaudio:volume=100:immediatemode=0:normid=8:input=1:buffersize=1024:width=768:height=576:outfmt=i420 -vo xv -ao sdl -aspect 4:3
但该命令存在的问题时,当电视播放一段时间后视频与音频便不再同步。
录制节目则可以用mencoder来实现:
mencoder tv:// -tv driver=v4l2:device=/dev/video0:norm=PAL:alsa:adevice=hw.2,0:amode=1:audiorate=48000:forceaudio:volume=100:immediatemode=0:normid=8:input=1:buffersize=64:width=720:outfmt=yuy2-oac mp3lame -lameopts fast:preset=standard -ovc lavc -lavcopts vcodec=mpeg4:vhq:vbitrate=1800 -o output.avi
上面两条命令虽然能用,但却无法实现电视节目的边放边录。通过网上的搜索(参考)和自己的测试,电视的录制与同时播放可以采用命名管道+tee+mplayer来实现。为了方便操作,写了一个脚本程序rtv,其可用三种模式运行:
watch:在播放电视的同时,将其存为avi文件。
nowatch:只录制节目不用mplayer播放。
onlywatch:只用mplayer看节目而不录制。
脚本首先检测用户的命令行参数输入,然后查看是否已有mencoder、tee、mplayer进程运行。若存在,则不再执行后面的部分;否则,执行如下的流程:
首先,使用mkfifo创建命名管道;
其次,用tee命令读取管道:在录制与播放的模式下(watch)将其分为两路,一路送至avi文件,一种送至mplayer用于播放;在只录制不播放的模式下(nowatch),只输出至avi文件;在纯播放模式下(onlywatch),只送至mplayer。
最后,用mencoder从/dev/video0截取视频流并输出到之前创建的命名管道。
rtv脚本内容如下:
#!/bin/bash # Define a function for returning a process id function get_pid_by_name() { local process_str echo "Searching process $1..." process_str=`ps aux | grep "$1" | tr --squeeze-repeats ‘[:blank:]+‘ ‘\t‘ | cut -f 2` if [ -n "$process_str" ]; then # The process for grep appears in the second field process_str=`echo $process_str | cut -s -d ‘ ‘ -f 1` if [ -n "$process_str" ]; then temp_pid=$process_str echo "The process id is $temp_pid!" else echo "The process $1 cannot be found, perfect!" fi else echo "The process $1 cannot be found, perfect!" fi } if [ -z "$1" ]; then echo "Please specify the recording and watching mode: watch|nowatch|onlywatch" exit 0 fi if [ "$1" != "onlywatch" ] && [ -z "$2" ]; then echo "Please provide the video file name to be saved!" exit 0 fi # Declare the pid as integers declare -i temp_pid=-1 mplayer_pid=-1 mencoder_pid=-1 tee_pid=-1 get_pid_by_name mencoder mencoder_pid=$temp_pid temp_pid=-1 get_pid_by_name tee tee_pid=$temp_pid temp_pid=-1 get_pid_by_name mplayer mplayer_pid=$temp_pid temp_pid=-1 if [ $(($mencoder_pid!=-1 && $mplayer_pid!=-1 && $tee_pid!=-1)) = 1 ]; then echo "A tv recording or watching activity is now working, please exit it first!" exit 0 fi # Create FIFO named pipe if [ ! -e "/tmp/tv.fifo" ]; then echo "FIFO does not exist, now being created..." mkfifo /tmp/tv.fifo && echo "Creating FIFO successful!" fi # Start tee and mplayer case "$1" in watch ) echo "Start recording tv and watch it using mplayer..." # Note: sudo must be used in order to make mplayer appear cat /tmp/tv.fifo | tee -a "${2%.avi}.avi" | sudo -u orlando DISPLAY=:0.0 mplayer -cache 5120 -ao sdl -vo xv - & ;; nowatch ) echo "Start recording tv without watching it..." cat /tmp/tv.fifo | tee -a "${2%.avi}.avi" & ;; onlywatch ) echo "Start watching tv without recording it..." # Note: "tee -a -" will not work here # Cache size should not be too large and 1024 kB is a reasonable value, otherwise, the response when changing the channel will be too slow cat /tmp/tv.fifo | tee | sudo -u orlando DISPLAY=:0.0 mplayer -cache 1024 -ao sdl -vo xv - & ;; * ) echo "Please specify the recording and watching mode: watch|nowatch|onlywatch" exit 0; esac # Start mencoder to feed the video stream into FIFO echo "Now start mencoder to capture tv..." mencoder tv:// -tv driver=v4l2:device=/dev/video0:norm=PAL:alsa:adevice=hw.2,0:amode=1:audiorate=48000:forceaudio:volume=100:immediatemode=0:normid=8:input=1:buffersize=1024:width=768:height=576:outfmt=i420 -oac mp3lame -lameopts fast:preset=standard -ovc lavc -lavcopts vcodec=mpeg4:vhq:vbitrate=1800 -o /tmp/tv.fifo
<style></style>
停止录制与播放电视的脚本stop_rtv为:
#!/bin/bash # Define a function for returning a process id function get_pid_by_name() { local process_str echo "Searching process $1..." process_str=`ps aux | grep "$1" | tr --squeeze-repeats ‘[:blank:]+‘ ‘\t‘ | cut -f 2` if [ -n "$process_str" ]; then # The process for grep appears in the second field process_str=`echo $process_str | cut -s -d ‘ ‘ -f 1` if [ -n "$process_str" ]; then temp_pid=$process_str echo "The process id is $temp_pid!" else echo "The process $1 cannot be found, perfect!" fi else echo "The process $1 cannot be found, perfect!" fi } # Declare pid as integers declare -i temp_pid=-1 mplayer_pid=-1 mencoder_pid=-1 tee_pid=-1 # Kill mencoder process get_pid_by_name mencoder mencoder_pid=$temp_pid temp_pid=-1 if [ $(($mencoder_pid!=-1)) = 1 ]; then # The SIGINT has no effect on mencoder processes while SIGKILL will cause loss of /dev/video0 node kill -2 $mencoder_pid && echo "mencoder has been killed!" else echo "mencoder process does not exist!" fi # Kill tee process get_pid_by_name tee tee_pid=$temp_pid temp_pid=-1 if [ $(($tee_pid!=-1)) = 1 ]; then kill -2 $tee_pid && echo "tee has been killed!" else echo "tee process does not exist!" fi # Kill mplayer process if not in nowatch mode if [ "$1" != "nowatch" ]; then get_pid_by_name mplayer mplayer_pid=$temp_pid temp_pid=-1 if [ $(($mplayer_pid!=-1)) = 1 ]; then # Note: mplayer is started by using sudo, therefore when killing it, sudo should also be used sudo -u orlando kill -2 $mplayer_pid && echo "mplayer has been killed!" else echo "mplayer process does not exist!" fi fi echo "TV recording and playing have been stopped!"
<style></style>
将上面的两个脚本与at命令结合,则可以实现定时录制与播放节目了。例如:
$ at 20:00 todaywarning: commands will be executed using /bin/sh at> rtv watch 我爱发明at> <EOT> # Input Ctrl+Djob 21 at Wed Feb 5 20:00:00 2014$ at 21:00 todaywarning: commands will be executed using /bin/sh at> stop_rtv watchat> <EOT> # Input Ctrl+Djob 22 at Wed Feb 5 21:00:00 2014
<style></style>
由于晚上播出的电视节目大部分在第二天白天会重播,因此在自己上班的同时,这些节目便可以按计划一个不落地录下来。同时,原本需要晚上熬夜看的节目也可以保存起来等到第二天再看。
14年6月15日 与此同时,为了方便播放与停止播放,在~/.bashrc中定义了如下aliases:
alias ptv="mplayer -forceidx"alias tv="rtv onlywatch"alias stop_tv="stop_rtv onlywatch"
<style></style>
其中,ptv在调用mplayer时使用-foceidx选项强制生成index信息,从而可以在播放的时候快进或者是快退。这是由于从视频卡直接采集生成的avi文件默认没有index信息(奇怪的是甚至即便是用mplayer生成了idx文件,用其他的播放器也没有办法播放)。不过,只使用-forceidx并不会将index信息保存下来,因此,下一次再次播放的时候,还需要重新生成index,比较耗时间。为此,编写gen_idx与ptv_idx两个脚本程序。前者用于生成并保存index信息,后者用于在启动播放时加载已经生成的index文件。两个程序的内容如下:
gen_idx:
#!/bin/bashmplayer -forceidx -saveidx "`basename \"$1\" \".avi\"`".idx "$1"
ptv_idx:
#!/bin/bashmplayer -loadidx "`basename \"$1\" \".avi\"`".idx "$1"
下面的脚本程序tv_nomen,则直接使用mplayer播放视频,而不经过mencoder的中转。这也是目前看电视直播的方法。采用该方法原本的想法是能够解决电视播放一断时间后,视频变得迟缓,开始与声音不同步的问题。但事实证明该问题并不能通过直接使用mplayer播放来解决。似乎这个问题是由于系统性能不高导致的。
#!/bin/bash# Play the tv using only mplayer: when a long time is elapsed, audio and video will be asynchronous using this methodsudo -u orlando DISPLAY=:0.0 mplayer tv:// -tv driver=v4l2:device=/dev/video0:norm=PAL:alsa:adevice=hw.2,0:amode=1:audiorate=48000:forceaudio:volume=100:immediatemode=0:normid=8:input=1:buffersize=1024:width=768:height=576:outfmt=i420 -vo xv -ao sdl -aspect 4:3 -nocache -framedrop