使用GNU/Linux播放电视节目

目前,生活中很多事情都可以在电脑前完成,学习、工作、音乐、视频等。如果也可以在电脑上收看有线电视节目的话,那就更好了。为此,我购买了圆刚视频采集卡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 0 ;;
    \? )  display_help
        exit 1 ;;
    esac
done
shift $(($OPTIND - 1))

# 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
}

# 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 0
    fi

    if [ "$1" != "onlywatch" ] && [ -z "$2" ]; then
    echo "$error_prefix 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 "$error_prefix 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 "$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 51200 -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 51200 -framedrop -ao sdl -vo xv - & ;;
    * ) echo "$error_prefix Please specify the recording and watching mode: watch|nowatch|onlywatch"
            exit 0;
    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 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!"

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

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

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

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

时间: 2024-10-23 01:40:09

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

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

尽管GNU/Linux并非无所不能,但确实能在很多时候提供免费.开放的解决方案.这两天我想做一个简谱,在网上搜索乐谱排版软件,发现了基于GPL协议的Lilypond软件.只不过Lilypond是用来做五线谱的.幸好,又找到剑桥大学 Silas S. Brown 编写的一个 jianpu-ly.py 脚本,通过调用Lilypond能够最终生成简谱.关于它的详细介绍请看这里. 该脚本支持的简谱语法规则如下: 音阶:1 2 3 4 5 6 7 1' 空拍:0 升.降音:#1 b2 低两个八度.低八度.

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

为了使GNU/Linux桌面环境下加载的程序较少以节省内存资源和提高启动时间,我目前并不使用重量级的桌面环境KDE和Gnome,甚至连登录窗界面gdm或xdm都不用,而是直接启动到控制台,登录后调用startx进入X视窗环境.所使用的工具组合列举如下: X视窗环境启动:startx 窗口管理器:Sawfish amixer:系统音量设置 键盘与鼠标配置:xmodmap 网络管理器:wicd(需删除NetworkManager) xscreensaver:屏幕保护程序 类似于Windows的底部工

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

以前我欣赏古典音乐都是听的CD,因而珍藏了不少光盘以及下载到电脑上的ape与flac格式的音乐文件.随着手机硬件性能(如电池续航能力.处理器速度.音质.存储容量等)和软件功能(音乐播放器对于曲目的管理)的提升,便需要考虑如何将这些资源转换成高质量的mp3文件放到手机上聆听.本文介绍如何基于GNU/Linux下的Audacity.k3b.easytag软件,以及自己写的Bash脚本程序来实现此功能. 从光盘抓取音乐并转为mp3 k3b是KDE环境下默认的光盘刻录与抓取软件.其界面如下图所示. 选择

Debian GNU/Linux 6.0 图形安装

一.准备安装Debian系统 1.Debian简介 Debian是由GPL和其他自由软件许可协议授权的自由软件组成的操作系统,由Debian计划(Debian Project)组织维护.Debian计划没有任何的营利组织支持,它的开发团队完全由来自世界各地的志愿者组成,官方开发者的总数超过1000名,非官方开发者为数更多. Debian计划组织跟其他自由操作系统(如Ubuntu.openSUSE.Fedora.Mandriva.OpenSolaris等)的开发组织不同.上述这些自由操作系统的开发

【贪心】电视节目安排

问题 W: [贪心]电视节目安排 时间限制: 1 Sec  内存限制: 64 MB提交: 22  解决: 16[提交][状态][讨论版] 题目描述 李旭琳发现小墨老师在班上是最顽劣的学生(没有之一),但他也有安静的时候,例如在看电视的时候.像什么“谍战剧”啊,“翻拍剧”啊,“婆媳戏”啊,“后宫剧”啊都是他的最爱.他甚至会事先查询所有喜欢看的电视节目的转播时间表并煞有介事的用红蓝铅笔列出计划,然后合理安排,以看到尽量多的完整节目. 输入 输入数据包含多个测试实例,每个测试实例的第一行只有一个整数n

俄罗斯军方的 GNU/Linux 发行版:Astra Linux

俄罗斯陆军以及情报部门对于信息基础架构安全防护的需求,由 RusBitTech 基于 Debian GNU/Linux 开发了 Astra Linux. Astra Linux 宣称其许可证既符合俄罗斯的法律也不违反 GPL 自由软件许可证.这里可以找到 Astra Linux 的源代码.从 1.1 的特性可以看出:除了像 Firefox, LibreOffice 等常见应用外,Astra 中集成了曾经备受关注的俄罗斯的国家密码算法 GOST 和基于 Linux 4.2 内核的高度定制版本,从源

GNU/Linux复习笔记(1)

第一次接触GNU/Linux还是大四上学期实习的那两个月在window里装了 个虚拟机玩红帽的系统,那段时间稍微学了一点命令就不玩了.后来大四下学期认识了王总,装了双系统,那段时间又对linux有了进一步认识并产生了很大的 兴趣.直到上学期突然发疯把笔记本装debian8以后才完全进入linux的世界.学习真的是一个螺旋式上升的过程.下面进入正题: ---Linux的基本原则: 1.由目的单一的小程序组成,组合小程序完成复杂任务(KISS:keep it simple,stupid)2.一切皆文

为什么网络银行不支持GNU/Linux操作系统下的浏览器操作

当年Linux没出时,银行就开始信息化建设了. 所为信息化,就是指用计算机工作了.服务客户了. 顺带着,慢慢的建服务器,连网(内部网).外网(网上银行) 这样下来, unix, dos, win nt, Win ** 经过这么多年的发展.一套系统或多套系统已经建成了. 现在想改为前端支持 Linux ,就像让微软把 IE 6, 7, 8, 9, 10, 支持好 w3c 的网络标准一样困难. 几乎是不可能的. 但也不是没有办法的.我想到的有二 一,等现在的软件寿命到期,如同等 IE 6, 7, 8

GNU、Linux和GNU/Linux之间的关系

来自:http://os.51cto.com/art/200608/30399.htm 1. 什么是Linux?2. 什么是GNU?3. GNU/Linux是什么玩意儿?什么是Linux相信了解一些IT知识的人都听过Linux这个名词.维基百科上的Linux词条是最经典和最准确的说明(http://zh.wikipedia.org/wiki/Linux),在此直接应用,免去班门弄斧之嫌.Linux操作系统(Linux),是一种计算机操作系统.Linux操作系统的内核的名字也是"Linux&quo