目前,生活中很多事情都可以在电脑前完成,读书、写程序、听音乐、看视频等。如果也可以在电脑上收看有线电视节目的话,那就更好了。为此,我购买了圆刚视频采集卡AverMedia C725B。如下图所示。

官方给出的此卡介绍为(详见这里):

C725标清采集卡是一张支持AV端子、S端子以及立体声输入的PCI-E撷取卡,可将PAL、NTSC和SECAM等模拟格式影像数字化,撷取并另存为 无压缩的AVI格式档案。C725标清采集卡随附的软件开发工具包(SDK)提供常用功能,能帮助开发者或系统整合商轻松且有效率地完成工作。此外,这套 SDK可兼容于Visual C++和 Visual Basic等主流程序语言,让开发者可更轻易上手。

官方声称支持Linux,而且驱动程序需要向其索取,网站不提供下载。然而,AverMedia C725是否真的可以在自己的Debian Wheezy(3.2.0-4-amd64 #1 SMP Debian 3.2.51-1 x86_64 GNU/Linux)上工作就不好说了,只好冒点风险试一下。

在安装好AverMedia PCI卡后,首先向官方联系取得驱动程序。需要注意的是,一定要获得与自己的Linux内核版本一致的驱动,否则编译极有可能不通过。我自己就是在尝试了官方默认提供的适用于老版本内核的驱动失败后,才要求圆刚的技术人员重新为自己编译了一个适用于Linux 3.2.51的版本。有了官方提供的驱动,再安装linux-source包与linux-headers包,就可以开始编译驱动了。首先,需要导出环境变量C_INCLUDE_PATH,其中包含了linux-source提供的dvb相关文件:

export C_INCLUDE_PATH=/usr/include/:/usr/src/linux-source-3.2/drivers/media/dvb/dvb-core/:/usr/src/linux-source-3.2/drivers/media/dvb/frontends/

之后的编译过程就是通常的make三步曲,一切都很顺利。

有了硬件基础后,电视播放软件我选择使用mplayer,录制视频则是mencoder。由于命令行所需的参数很多,我写了一个脚本程序rtv.sh,可以方便的播放或录制节目。通过指定命令行参数watch、nowatch、onlywatch,该脚本可以在三种模式下运行:

  • watch:在播放电视的同时,将视频存为avi文件;
  • nowatch:只录制节目存为avi文件,不实时播放;
  • onlywatch:只用mplayer看节目而不录制。

该脚本用到了如下几个程序:

  • mencoder:从视频采集卡以指定的格式获取视频流并将其输出到一个名称FIFO管道(named FIFO pipe);
  • tee:从标准输入获得数据流再将其转向至标准输出。利用管道行,可以将名称FIFO管道的内容作为tee命令的标准输入,而tee命令的标准输出作为mplayer程序的标准输入予以播放。同时,tee命令可以使用命令行参数-a filename。这样,在将数据流送至标准输出乃至mplayer的同时,也会将其保存至指定的文件。利用这个机制,就可以实现同时播放并录制电视节目了;
  • mplayer:播放由tee转来的视频流。

脚本程序的执行过程是:首先检测用户的命令行参数输入,然后查看是否已有mencoder、tee、mplayer进程运行。若存在,则表明已经在播放或录制节目了,从而提示用户后退出;否则,执行如下的流程:

  1. 首先,使用mkfifo创建名称FIFO管道/tmp/tv.fifo;
  2. 其次,用cat命令输出该管道的内容至标准输出,利用管道行将该输出作为tee命令的标准输入。在录制与播放的模式下(watch),将其分为两路,一路送至avi文件,一路送至mplayer用于播放;在只录制不播放的模式下(nowatch),只输出至avi文件;在纯播放模式下(onlywatch),只送至mplayer。
  3. 最后,用mencoder从AverMedia C725B采集卡在系统中的设备节点/dev/video0截取视频流,以指定的格式输出到之前创建的名称管道。

脚本程序的源代码为:

#!/bin/bash

script_name="rtv.sh"
script_usage=$(cat <<EOF
rtv MODE [FILE]
EOF
)
script_function=$(cat <<EOF
Record or watch TV. MODE can be 'watch', 'onlywatch', 'nowatch'. When 'watch' or 'nowatch' is specified, the file name must be provided.
EOF
)
script_doc=$(cat <<EOF
-h Display this help.
EOF
)
script_examples=$(cat <<EOF
rtv nowatch test.avi
rtv watch test.avi
rtv onlywatch
EOF
)
state_prefix="==="
warning_prefix="***"
error_prefix="!!!" function display_help() {
if [ -n "$script_usage" ]; then
echo -e "Usage: $script_usage"
fi if [ -n "$script_function" ]; then
echo -e "$script_function"
fi if [ -n "$script_doc" ] ; then
echo -e "\n$script_doc"
fi if [ -n "$script_examples" ]; then
echo -e "\nExamples"
echo -e "$script_examples"
fi
} # Process command options
while getopts ":h" opt; do
case $opt in
h ) display_help
exit ;;
\? ) display_help
exit ;;
esac
done
shift $(($OPTIND - )) # 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 `
if [ -n "$process_str" ]; then
# The process for grep appears in the second field
process_str=`echo $process_str | cut -s -d ' ' -f `
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
} # Start execute the command
if [ $OSTYPE = 'linux-gnu' ] && [ `hostname` = "QuantumHome" ]; then
if [ -z "$1" ]; then
echo "$error_prefix Please specify the recording and watching mode: watch|nowatch|onlywatch"
exit
fi if [ "$1" != "onlywatch" ] && [ -z "$2" ]; then
echo "$error_prefix Please provide the video file name to be saved!"
exit
fi # Declare the pid as integers
declare -i temp_pid=- mplayer_pid=- mencoder_pid=- tee_pid=- get_pid_by_name mencoder
mencoder_pid=$temp_pid
temp_pid=- get_pid_by_name tee
tee_pid=$temp_pid
temp_pid=- get_pid_by_name mplayer
mplayer_pid=$temp_pid
temp_pid=- if [ $(($mencoder_pid!=- && $mplayer_pid!=- && $tee_pid!=-)) = ]; then
echo "$error_prefix A tv recording or watching activity is now working, please exit it first!"
exit
fi # Create FIFO named pipe
if [ ! -e "/tmp/tv.fifo" ]; then
echo "$state_prefix FIFO does not exist, now being created..."
mkfifo /tmp/tv.fifo && echo "$state_prefix Creating FIFO successful!"
fi # Start tee and mplayer
case "$1" in
watch ) echo "$state_prefix 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 -framedrop -ao sdl -vo xv - & ;;
nowatch ) echo "$state_prefix Start recording tv without watching it..."
cat /tmp/tv.fifo | tee -a "${2%.avi}.avi" & ;;
onlywatch ) echo "$state_prefix Start watching tv without recording it..."
# Note: "tee -a -" will not work here
cat /tmp/tv.fifo | tee | sudo -u orlando DISPLAY=:0.0 mplayer -cache -framedrop -ao sdl -vo xv - & ;;
* ) echo "$error_prefix Please specify the recording and watching mode: watch|nowatch|onlywatch"
exit ;
esac # Start mencoder to feed the video stream into FIFO
echo "$state_prefix 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
else
echo "$warning_prefix Operating system or host name is not supported!"
fi

该脚本程序中,mencoder的参数较为复杂,这里对其解释如下:

  • driver=v4l2:使用video for linux驱动;
  • device=/dev/video0:视频卡设备节点。只有当驱动安装正确后,才会出现该节点;
  • norm=PAL:指定模拟电视制式,中国为PAL;
  • alsa:从ALSA捕获音频;
  • adevice=hw.2,0:指定音频设备。当使用OSS音频系统时,adevice的值为/dev/xxx,当使用ALSA音频系统时,则为形如hw:2,0的硬件ID。需注意的是,由于命令行不能出现冒号,应将hw:2,0中的冒号改为逗号;
  • amode=1:指定声音模式。0为单声道,1为立体声;
  • audiorate=48000:输入声音采样率;
  • forceaudio:强制采集音频,即便v4l库未发现有音频源;
  • volume=100:指定视频采集卡上混频器的音量;
  • immediatemode=0:0表示同时采集与缓存音频与视频流(mencoder的默认值),1表示只采集视频流,而音频流则会通过回环线路由电视卡送至声卡(mplayer的默认值)。由于我使用的是mencoder,所以设为0;
  • normid=8:采集卡的电视标准编号;
  • buffersize=1024:采集缓冲区的大小,单位为MB
  • width=768:height=576:输出视频像素宽度与高度;
  • outfmt=i420:输出视频数据格式;
  • -oac mp3lame:指定音频编码器为mp3lame;
  • -lameopts fast:preset=standard:指定音频编码器的选项;
  • -ovc lavc:指定视频编码器为libavcodec;
  • -lavcopts vcodec=mpeg4:vhq:vbitrate=1800:指定视频编码器选项。

需要说明的是,由rtv.sh录制的avi文件没有索引,所以直接用mplayer播放无法快进与快退。为此,可以用下面的脚本程序自动生成索引后再播放(其中,参数$1为avi视频文件名):

#!/bin/bash

mplayer -forceidx -saveidx "${1%avi}idx" "$1"

若想回放已经录制过且生成了索引的视频,则可以使用下面的脚本程序(其中,参数$1为avi视频文件名):

#!/bin/bash

mplayer -loadidx "${1%avi}idx" "$1"

到这里,用于播放与录制电视节目的脚本程序rtv.sh就介绍完了。下面再来介绍用于停止播放或录制的脚本程序stop_rtv.sh。这个就比较简单了,无非就是杀死相应的进程而已。源代码如下:

#!/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 `
if [ -n "$process_str" ]; then
# The process for grep appears in the second field
process_str=`echo $process_str | cut -s -d ' ' -f `
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=- mplayer_pid=- mencoder_pid=- tee_pid=- # Kill mencoder process
get_pid_by_name mencoder
mencoder_pid=$temp_pid
temp_pid=- if [ $(($mencoder_pid!=-)) = ]; then
# The SIGINT has no effect on mencoder processes while SIGKILL will cause loss of /dev/video0 node
kill - $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=- if [ $(($tee_pid!=-)) = ]; then
kill - $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=- if [ $(($mplayer_pid!=-)) = ]; then
# Note: mplayer is started by using sudo, therefore when killing it, sudo should also be used
sudo -u orlando kill - $mplayer_pid && echo "mplayer has been killed!"
else
echo "mplayer process does not exist!"
fi
fi echo "TV recording and playing have been stopped!"

有了rtv.sh与stop_rtv.sh两个脚本,再将其与at命令结合,则可以实现定时录制与播放节目了。例如:

$ at : today
warning: commands will be executed using /bin/sh
at> rtv watch 我爱发明
at> <EOT> # Input Ctrl+D
job at Wed Feb ::
$ at : today
warning: commands will be executed using /bin/sh
at> stop_rtv watch
at> <EOT> # Input Ctrl+D
job at Wed Feb ::

由于晚上播出的电视节目大部分在第二天白天会重播,因此在自己上班的同时,这些节目便可以按计划一个不落地录下来。同时,原本需要晚上熬夜看的节目也可以保存起来等到第二天再看。

还有一个问题就是,电视卡是插在台式机上的,所以只能在这台电脑上观看电视节目。那么自己在厨房做饭的时候怎么看电视呢?比如晚上7点的新闻联播?由于自己有iPhone,实现这个功能就不难了。首先,在电脑上运行rtv.sh程序(rtv.sh nowatch filename.avi),将电视节目录到文件中。然后,将文件所在的目录共享到 Samba服务器上。最后,用iPhone上的视频播放器OPlayer访问该服务器并在线播放即可。看到的节目会稍微有一点延迟,不过也没有什么太大的关系。下图就是iPhone上看到的电视节目截图:

使用GNU/Linux播放电视节目的更多相关文章

  1. 在GNU/Linux下使用Lilypond排版简谱

    尽管GNU/Linux并非无所不能,但确实能在很多时候提供免费.开放的解决方案.这两天我想做一个简谱,在网上搜索乐谱排版软件,发现了基于GPL协议的Lilypond软件.只不过Lilypond是用来做 ...

  2. 我在GNU/Linux下使用的桌面环境工具组合

    为了使GNU/Linux桌面环境下加载的程序较少以节省内存资源和提高启动时间,我目前并不使用重量级的桌面环境KDE和Gnome,甚至连登录窗界面gdm或xdm都不用,而是直接启动到控制台,登录后调用s ...

  3. 在GNU/Linux下将CD音乐转为mp3

    以前我欣赏古典音乐都是听的CD,因而珍藏了不少光盘以及下载到电脑上的ape与flac格式的音乐文件.随着手机硬件性能(如电池续航能力.处理器速度.音质.存储容量等)和软件功能(音乐播放器对于曲目的管理 ...

  4. linux系统电视盒子到底是什么

    经常看到各种大神说今天刷了什么linux系统可以干嘛干嘛了,刷了乌班图可以干嘛干嘛了,但是身为一个小白,对这种名词都是一知半解.所以这边给大家科普一下,什么是linux系统?电视盒子刷了这个可以干啥? ...

  5. GNU/Linux 介绍

    在了解Linux之前要先了解什么是GNU / GNU官方解释? GNU是一个自由软件操作系统.就是说,它尊重其使用者的自由.GNU操作系统包括GNU软件包(专门由GNU工程发布的程序)和由第三方发布的 ...

  6. 从C过渡到C++(1)——GNU/Linux

    从C过渡到C++(1)--GNU/Linux 目录 从C过渡到C++(1)--GNU/Linux 大名鼎鼎的GNU/Linux GNU GNU的组成 一点补充 MinGW 运行时库 额外的内容 Min ...

  7. (转)完全用GNU/Linux工作 by 王珢

    完全用GNU/Linux工作 王珢      (看完这篇博文,非常喜欢王珢的这篇博客,也我坚定了学gnu/linux的决心,并努力去按照国外的计算机思维模式去学习编程提高自己.看完这篇文章令我热血沸腾 ...

  8. Gnu/Linux的学习探索

    1.Gnu/Linux是一个基于POSIX和UNIX的多用户多任务 支持多线程多CPU的类UNIX的操作系统. 继承了UNIX以网络为核心的设计思想 是性能稳定的多用户网络操作系统. 1991年10月 ...

  9. GNU/Linux复习笔记(1)

    第一次接触GNU/Linux还是大四上学期实习的那两个月在window里装了 个虚拟机玩红帽的系统,那段时间稍微学了一点命令就不玩了.后来大四下学期认识了王总,装了双系统,那段时间又对linux有了进 ...

随机推荐

  1. java http url post json

    import java.io.IOException; import java.io.InputStream; import java.io.OutputStreamWriter; import ja ...

  2. ios微信支付成功后点击左上角返回不走回调的问题

    最近做微信支付发现ios9以后出现的跳转其他app后左上角有返回xxx功能会影响微信支付回调,情况如图 返回后不走下面的方法 - (BOOL)application:(UIApplication *) ...

  3. java自带的监控工具VisualVM一

    转自:http://www.cnblogs.com/wade-xu/p/4369094.html 这篇总结的很不错(本人亲自操手学习),留着以后复习备用,很适合入门级的学习者: VisualVM 是一 ...

  4. android应用编译失败 ResXMLTree_node size 类错误,以及 android studio 项目内搜索

    今天很郁闷,又遇到个很让人崩溃的问题: ResXMLTree_node size 0 is smaller than header size 0x45. 类似这样的错误,提示中看不出任何有用的内容,网 ...

  5. CodeForces 677D Vanya and Treasure

    $dp$,树状数组. 很明显这是一个$DAG$上的$dp$,由于边太多,暴力$dp$会超时,需要优化. 例如计算$dp[x][y]$,可以将区域分成四块,$dp[x][y]$取四块中的最小值,每一块用 ...

  6. 关于Android平台的搭建的心得---汪永骏

    我本来是.net开发的,但看到目前互联网形式都朝着移动端开发迈进.大势所向,我便也开始学习Android的开发 今天就是要聊一下,我对Android开发的一些心得.今天讲的是,我在搭建Android平 ...

  7. ListBox 如何改变某行的字体颜色

    Option Explicit Private Type RECT Left As Long Top As Long Right As Long Bottom As Long End Type Pri ...

  8. label 不同颜色

    label  不同颜色 UILabel* noteLabel = [[UILabel alloc] init]; noteLabel.frame = CGRectMake(60, 100, 200, ...

  9. 关闭Pycharm拼写检查(Mac)

    1.关闭拼写检查,preference-->Editor-->Inspections-->Spelling-->Typo,取消勾选 2.关闭代码风格检查,preference- ...

  10. PHP程序员的技术成长规划(转)

    按照了解的很多PHP/LNMP程序员的发展轨迹,结合个人经验体会,抽象出很多程序员对未来的迷漫,特别对技术学习的盲目和慌乱,简单梳理了这个每个阶段PHP程序员的技术要求,来帮助很多PHP程序做对照设定 ...