行者无疆 始于足下 - 行走,思考,在路上

一个Shell Script的诞生

任务:一批视频文件,需要

  1. 自动化地转码成指定的格式
  2. 上传到服务器
  3. 得到文件的url地址

对于转码工作,目前为止依然没有顺利的完成,由于网上资料贫乏,各种视频音频格式、编解码器、专利开源问题比较纠结,需要很长的时间理清这些关系。我重点研究了ffmpegyamdi这两个工具。但是今天用yamdi的时候发现一个很奇怪的bug——它会自动改变原始视频文件的fps和bitrate作为输出文件,非常奇怪,可能会比较棘手。

对于第二个问题,经过几天的探索,顺带复习许久之前的Bash Scripting知识和众多的Unix Power Tools,终于想出了比较完善可行的方案,诞生了人生第一个比较“成形”的Shell脚本,惭愧……

首先给出我的脚本和模板配置文件,然后再逐步分析——

send_file.sh:

#!/bin/bash 

################################################################################
# Purpose:	This interactive script is used for upload files onto a ftp server automatically.	
# Author:	Xiao Hanyu(xiaohanyu@taohua.com)
# Usage:	./send_file.sh
# Depends: 
#       lftp:       used to transfer files
#       tree:       used to get the directory tree
#       sed:        used to get the filename
#       sort:       used to match the url and the filename into a csv file
# Notes:
#       This script need a configuration files which is set by -f parameter. 
#       I have given a sample configuration file: sample.conf
#       After running this script, it will output the [filename:url] list to a file
################################################################################

# This function checks whether the necessary tools are available
function usage
{
    cat << EOF
`basename $0`: A utility to send files to a remote ftp server automatically

Usage: `basename $0` [Action]
Example: `basename $0` -f config_file

Actions:
    -f:         set the configuration files
    -h:         show this help
EOF
}

# check the necessary tools
function check_version
{
    $1 --version > /dev/null

    if [ $? -ne 0 ]
    then
        echo "You must install $1 before executing this script."
        echo "You can type \"sudo apt-get install $1\" to finish this task under ubuntu os."
        echo "exit ..."
        exit 1
    fi
}

# make sure that you have permission to create a temporary file under current directory
function check_perm
{
    touch tmp_lftp_script_file
    if [ $? -ne "0" ]
    then	
        echo "You don't have permission to create temporary file under current directory."
        echo "Type \"chmod u+w current_directory\" to give users write permissions."
        echo "exit ..."
        exit 1
    else
        rm -f tmp_lftp_script_file
    fi
}

# this script use some file to store something, so 
# if the file exists, we backup it into file.bak, and 
# create a new empty file
function check_bak_file
{
    if [ -e $1 ]
    then
        cp $1 $1.bak
    fi

    cat /dev/null > $1
}

# parse parameters
#   -f for configuration file
#   -h for command help
while getopts "f:h" arg
do  
    case $arg in
        f)
            config_file=$OPTARG
            ;;
        h)
            usage
            exit 0
            ;;
        ?)
            echo "!!Wrong command options!"
            usage
            exit 1
            ;;
    esac
done

# check whether or not configuration file exists
if [ -e $config_file ]
then    
    # if $config_file exist, import the necessary variable
    source $config_file
else
    echo "configuration file not exist." 
    echo "exit ..."
    exit 1
fi

# check lftp version
echo 
check_version lftp

# check tree version
echo 
check_version tree

# check permission
check_perm

# This file containts the command executed by lftp after login ftp server"
lftp_script=lftp_sh
check_bak_file $lftp_script 

# $url_file store the [key:value] for filenames and urls
check_bak_file $url_file

# ftp anonymous login
username=${username:-"anonymous"}
password=${password:-"anonymous"}

# ftp default port
port=${port:-"21"}

# create lftp script executed by lftp 
echo "lftp $username:$password@$host:$port" >> $lftp_script
echo "ls" >> $lftp_script
echo "cd $rdir" >> $lftp_script

for file in $lfiles
do
	if [ -d $file ]                 # if $file is a directory, we should use 'lftp mirror -R' command
	then
		echo "mirror -R $file" >> $lftp_script

        # use $(tree -ifp --noreport $file | grep "\[" | grep -v "\[d" | tr -s ' ' | cut -d' ' -f2 | sed -e 's/\.\{1,2\}\///g') 
        # to get all the filenames(contains relative path such "../../", "./", "../", "/", so we should use sed to get rid of these
        for tmp_file in $(tree -ifp --noreport $file | grep "\[" | grep -v "\[d" | tr -s ' ' | cut -d' ' -f2 | sed -e 's/\.\{1,2\}\///g')
        do
            if [[ $rdir == "." || $rdir == "" ]]    # if $rdir==".", we shouldn't give a url like 'http://hostname/./filename'
            then
                echo -e "$(basename $tmp_file | sed -e 's/\..*//g')\thttp://$host/$tmp_file" >> $url_file
            else
                echo -e "$(basename $tmp_file | sed -s 's/\..*//g')\thttp://$host/$rdir/$tmp_file" >> $url_file
            fi
        done
	elif [ -e $file ]
	then 
		echo "put $file" >> $lftp_script
		if [[ $rdir == "." || $rdir == "" ]]
		then
			echo -e "$file\thttp://$host/$file" >> $url_file
		else
			echo -e "$file\thttp://$host/$rdir/$file" >> $url_file
		fi
        else
                echo "!!Warning: $file not exist!"
        fi
done

lftp -f $lftp_script

if [ $? -ne 0 ]
then
	echo "Sending file failed, please check your ftp information."
	echo "exit ..."
else
	echo "Sending file successfully!"
fi

# echo "rm -f $lftp_script"

sample.conf:

########################################
# Purpose: This file is the sample configuration file for the send_file utility
# Author: Xiao Hanyu(xiaohanyu@taohua.com)
# Warning:
#   This file use bash script grammer to config, which means, you can't leave any space around '='
#   Examples:
#       a=b     <<-->>      right grammer
#       b =c    <<-->>      wrong grammer
#       c= d    <<-->>      wrong grammer
#       d = f   <<-->>      wrong grammer
#   Second, all the variables marked '!!' is necessary, others have default values
#   Examples:
#       config_file=        #!!(necessary variable)
#       username=           (not necessary variable)
#   Third, all the parameter should be quoted by ""
########################################

# username and password to login an ftp server
username="tiger"
password="tiger"

# hostname or ip of the remote ftp server
host="10.36.100.9"          #!! necessary variable

# port, default is 21
port=

# local files, you should give the right absolute path
# or the right relative path
# both files are directories are allowed
# files and directories are seperated by [space] or [tab]
lfiles="send_file.sh sh_test sample.conf ../tmp t f g h ./sh_test"

# remote directory which you upload your files into
rdir="videos"

# specify the url_file
# url_file consists of two columns: filename and urls
url_file="url_list"

代码的注释比较详尽了,函数名称基本也能如实反映函数的作用,我来说明下基本思路。

首先是命令行选项的解析,这个根据复杂度不同有三种方法:

  1. 直接用$1, $2, $3手工处理,暴力解析。这里你需要知道几个Bash变量,如$0代表bash脚本的名字,$1~$9分别代表着第1~9个命令行参数等等。优点是比较简单,缺点是太“简单”了。
  2. getopts,Bash内置,只支持短选项如'-a -b -c','-a option1 -b -c','-abc‘,不支持长选项如'--version'这样的,使用比较简单(因为是Bash内置嘛)。
  3. getopt,外部命令,比较复杂,支持长选项,我还不会用。

C++ Boost库提供Options组件,用来解析命令行参数。具体的实例可以参见Bash Shell中命令行选项/参数处理。我的脚本中用的是第二种方法。

第二个大问题是参数选项的问题。我们可以通过两种方式配置参数,从而让我们的脚本自动化地做出适应性的处理。第一种方法是通过命令行参数,就是上面谈的getopt/getopts,这种方法的好处就是方便直观快捷,变量解析可以用Bash内置的read或者高级一点的TCL/Expect(这个我也不会),缺点在于每次敲命令的时候都要敲这一堆命令行参数,而且对于运维人员来说是一种非常不user-friendly的方式;第二种方法就是通过配置文件,让我们的Bash脚本自己解析指定的配置文件来获取相应的信息——比如ftp登录的username和password、需要上传的文件、上传的远端目录等等。

配置文件的格式有多种选择,pluskid大神的闲谈程序的配置文件是篇很不错的说明。我的脚本功能比较简单,配置文件自然也不会太复杂,因此我想出了一个非常“卑鄙无耻”的方法——就是直接将配置文件写成bash script变量赋值的形式,然后在脚本中通过这么一句:

source $config_file

直接引入配置变量。我承认我太卑鄙了,当然好处是简单可行——但是对于运维人员(使用这个脚本的人来说),可能会莫名奇妙——为啥等号后面不能有空格,为啥变量赋值最好要加引号——因为他们不懂Bash Script的语法——所以每次写脚本的时候、想象一下假设你就是那个要使用脚本的人,怎样才算友好的脚本?——但是我没有时间研究更复杂的脚本解析了——欢迎指正。

第三个大问题是ftp自动登录上传文件的问题。如果我们把平时的ftp登录操作比作用vim编辑文件,那么自动化的ftp登录就是用sed来处理文件。想象一下,我们平时登录ftp,windows下,我们会点开一个ftp软件,点击快速链接,输入username和password,然后下载上传。linux有万能的lftp命令行工具,因此实现自动化的功能,从lftp的参数选项着手是比较有希望的选择。

功夫不负有心人,lftp有两种手段能够实现自动化的登录上传下载。第一种方式是通过lftp -f lftp_script_file的方式,-f指定一个文件lftp_script_file,这个文件里面包含登录lftp的命令和上传下载文件的命令。第二种方式是通过lftp的-u参数指定登 录名密码和-e选项指定登录后执行的lftp命令。这种方式的缺点在于每执行一条命令都要登录一下ftp——不过登录ftp所耗费的时间与上传文件的时间相比几乎可以忽略不计,所以也算不上一个大的缺点。

除了以上两种方式,我在扫ABS的时候偶然发现了Here Documents这个东西——这个曾经听说过但从来没有认真看过的东西,才发现这东西也有很多妙处,使用的当,同样可以实现lftp的自动登录上传。我采用的是lftp的-f选项,touch一个临时文件完成自动登录上传的。

第四个问题是url提取的问题。具体来说,比如你远端ftp和http服务器的地址是hostname,远端目录是videos,本地上传文件是send_file.sh、hpm.avi,你需要生成如下的[filename:url]的list:

send_file    http://hostname/videos/send_file.sh
hpm    http://hostname/videos/hpm.avi

然后存储这个list到一个文件里面,供后面进一步的URL生成映射处理之用。这个问题是耗时最久的一个问题。我的脚本里面有这么一段:

for file in $lfiles
do
	if [ -d $file ]                 # if $file is a directory, we should use 'lftp mirror -R' command
	then
		echo "mirror -R $file" >> $lftp_script

        # use $(tree -ifp --noreport $file | grep "\[" | grep -v "\[d" | tr -s ' ' | cut -d' ' -f2 | sed -e 's/\.\{1,2\}\///g') 
        # to get all the filenames(contains relative path such "../../", "./", "../", "/", so we should use sed to get rid of these
        for tmp_file in $(tree -ifp --noreport $file | grep "\[" | grep -v "\[d" | tr -s ' ' | cut -d' ' -f2 | sed -e 's/\.\{1,2\}\///g')
        do
            if [[ $rdir == "." || $rdir == "" ]]    # if $rdir==".", we shouldn't give a url like 'http://hostname/./filename'
            then
                echo -e "$(basename $tmp_file | sed -e 's/\..*//g')\thttp://$host/$tmp_file" >> $url_file
            else
                echo -e "$(basename $tmp_file | sed -s 's/\..*//g')\thttp://$host/$rdir/$tmp_file" >> $url_file
            fi
        done
	elif [ -e $file ]
	then 
		echo "put $file" >> $lftp_script
		if [[ $rdir == "." || $rdir == "" ]]
		then
			echo -e "$file\thttp://$host/$file" >> $url_file
		else
			echo -e "$file\thttp://$host/$rdir/$file" >> $url_file
		fi
        else
                echo "!!Warning: $file not exist!"
        fi
done

其中针对目录的处理尤为复杂,比如你可以指定../../tmp这样的目录,如果你不作合适的处理,生成的URL可能是http://hostname/../../tmp/之类的东西。我最开始想的方法是递归目录的处理方法,但是写了好几个版本依然没有写出Bash的递归目录遍历。后来偶然间想到了tree,这个可以列出目录树的命令,仔细研究了它的参数选项,同时以管道的方式结合其他命令如grep(正向和反向匹配)、tr(压缩相同字符)、cut(提取某个column,可以用awk的print实现同样的功能)、sed(字符串处理,去除文件的路径和后缀),终于胜利地完成了这个任务。所谓成就感就是这么来的,哈。

至此,脚本需要解决的主要问题都已经阐述完毕,其余的问题都是一些小技俩,比如检查相关依赖工具是否安装、检查用户权限、提供帮助信息等等。目前发现一个bug,还是目录上传的时候有时会出现递归上传的问题,非常奇怪。

脚本的改进之处也有很多,比如:

  • 给出更加友好的提示帮助信息
  • 给出更健壮的配置文件语法
  • 自动检查每个文件是否上传成功,如果没有成功,能否实现断点续传
  • 支持log文件输出,便于时候分析和故障分析
  • 如果磁盘空间不够给出警告信息等等

ok,到此为止,睡觉去。

一个人的自在

6月15日开始找实习,到了25日左右迎来了人生第一个offer——华数淘宝,待遇是60元/天+10元/天的晚餐票。找实习的过程很简短,百度和创新工厂预料之中的悲剧,DeviceVM莫名其妙的简短面试和之后的杳无音信,普联TP-Link的5分钟快餐面试,阿里一封没有下文的邮件、一个打到一半我手机却没电的尴尬电面、一个没有下文的快餐电话,以及来自上海紫竹科技园Intel的漫不经心的“聊聊“,还有十几封泥牛入海的邮件。两周不到,能拿到这个offer,也算是一个不错的结果了。至少这样的结果给了我信心,成绩差没关系,关键是自己有几斤几两。

而后就是无奈的期末考试,算不上奋力一搏,只是应付了事,弃了一门,挂了两门,剩下的几门70的、80的、90的各占三成。至此我的大学还剩下十门考试:

  1. 电路原理(重修)
  2. 线性代数(重修)
  3. 数字电子技术(重修)
  4. 编译原理
  5. 编译系统设计
  6. 计算机组成
  7. 计算机体系结构
  8. 计算机网路
  9. c#程序设计
  10. 软件体系结构

这样的结果,在我的同届预科生中应该算是很悲惨的了。至少很多同学由于多了预科半年的学习时间,到了大三下这个时候,已经完成了除了毕业设计外的全部学业,更有甚者,比如一位我只有过一面之缘的HDJ,已经提前一年毕业,拿了港大的offer——此mm是浙大李志文商学班的,同时也是浙大游泳队队员之一。对于这样的mm,除了客套的ym,更多的是一种敬仰吧——有规划有理想有追求并且能坚持不懈的做下去。

熬到7月6号,总算考完了最后一门——软件工程,在我看来是一门非常虚无非常飘渺的课程,其结果就是我只上过一次课,成绩也算对得起我的到课率了——68分——刚好达到浙大学位证最低成绩要求……

再之后是帮毕业的mm收拾寝室,给mm拍傻傻的学士服照片……12号早晨送走了mm,一个人去淘宝报道。没想到第一天报道也是一番纠结,首先是华数淘宝正式从淘宝剥离,从文二路淘宝公司内部搬到了紫荆花路府苑新村,然后我放下自行车导了三辆公交才到华数淘宝。第一天工作很简单,让我在windows上配置个trac,看看nginx服务器。都是从未听说过的东西。

我的理解,trac是个python语言写的svn前端+wiki的集合体,著名的c++ boost库用的就是trac,ruby也有类似的东西,叫redmine,当然,我对ruby一无所知,对python也仅仅是看过《Learning Python》的前两章,还出于ym的阶段。安装参考这篇How To Install Trac on Windows,其中有几个链接已经过时,需要自己手工下载安装。安装的过程还算顺利——比安装gentoo顺利多了。不过还是挺麻烦的——如果在linux下,一两条命令应该就可以了,也不用“鼠标点击——next——install“。

nginx是轻量级的http服务器,其功能或许不如apache全,但是性能却比apache强很多,尤其是在flv视频支持方面,其原理大概在于nginx针对不同的平台编码采用了不同的I/O模型。nginx的代码不算长,大概10w行左右,用c写成,所有的东西甚至包括./configure脚本都是作者手工写成,是一个锻炼阅读源码的好项目。国内的血多视频网站如youku、tudoo等后台服务器都是nginx。

第二天我就参加了小组会议,头儿给我讲了整个项目的架构和周期,听的我非常兴奋。大体思路就是我们要搭成一个全新的视频点播平台,做成中国的itunes app store。整个一周我都在了解各方面的信息,也对taobao这样的互联网公司有了进一步的了解。网上浏览的时候接触了很多“牛“的资源:

这些实际的项目、经验、团队、感想,感觉就在自己的身边,让我兴奋不已。阿里巴巴,马云,中国能有这样的企业,我觉得这是中国的未来。Open Source Never Die。

除了完成头给定的任务,我在网上快速的学习一些科普知识,各个方面的都有,很多东西扫一眼百度百科也比毫不了解要强,这些入门知识包括:

 吓到了吧?其实很多我都不会。我现在出于“我不知道我不知道“到“我知道我不知道“这样一个转变的过程。下一个目标是:“我知道我知道”。

除此之外,每个桌上那一抹绿色,厕所里的“蹲位以待“,中午和老总一起吃饭的气氛,自由宽松的工作环境时间,这些都是我喜欢的。不过日子久了,每天面对着电脑,看着一行行的字母滚动而过,不免麻木。每天晚上出了公司,抬头看看杭州最近让人淡定不能的天空,心里总有一些别样的悸动。

“我一个人吃饭、旅行,到处走走停停;也一个人看书、写信,自己对话谈心”,mm在上海入职培训,无暇顾我,我也就不再好意思打扰她。我告诫自己要忍让要懂得理解,千万不要因为这样的事情跟mm生气,如果连这点小事都忍受不了,以后又该如何支持mm的工作?我这样自我安慰,mm却不太理解,怪我对她的事情逐渐冷漠,搞得我伤心不已。

“你喜欢这个啊,你喜欢玩所有的毛绒玩具,你喜欢妈妈,喜欢爸爸,喜欢你的小睡衣。你什么都喜欢,是不是?知道吗?等你长大了,曾经喜欢的东西也许会变得不 再特别,就像这个玩偶盒,你会发现那只是一片铁皮加一个人偶,那些原本钟爱的东西会渐渐被你遗忘,到了我这个年纪,钟爱的东西也许只剩下一两件了,对我来 说只剩一件了。”《拆弹部队》,James对他的女儿说完这些,又独自回到了战场。

我这个人是个有点“无趣”的人,我不喜欢电脑游戏,什么dota、星际,我觉得要学会这些比修一门电路原理还难;我也不喜欢追星,我觉得谁当选了世界足球先生谁谁拿了NBA总冠军与我没啥关系,听说世界杯决赛在夜里两点,设了一点的闹钟,结果闹钟响了我按了继续睡……;我觉得一大群人去KTV去杀人游戏玩三国杀绝大多数时间也是纯属无聊找点消遣,阿桑还说:“孤单是一个人的狂欢,狂欢是一群人的狂欢”;我在校内上很低调,因为我觉得那里很浮躁,每天去看看无非就是了解下朋友的动向;所谓朋友,多数时候只是在特定的时间特定的地点和特定的环境下的朋友,到了人生关口,大家各奔东西,所谓友情也被岁月冲淡,我觉得真正的朋友不需要经常的联系来保持朋友关系,但现实中大多数的朋友需要经常联系保持这种朋友关系,这或许就是贺岁短信的终极目的吧。

所以说人生得一知己死而无憾。“什么?你了解我?笑话,我自己都不了解我,你会了解我?”

每个人都是孤独的行者。

人生在世,所谓何求?

所谓成长,就是信仰一个个崩塌的过程?

当有人要教育你的时候,你一定要提高警惕!

我们的教科书告诉我们要正视历史、以史为镜,可是广电却封杀了《活着》、封杀了《蓝风筝》、封杀了《颐和园》,这就是所谓“以史为镜”?

我们的社会主义赋予人所谓言论自由集会自由,同时我们的社会主义也用GFW提前给我们确定了考试范围和作文题目——我不要这种阉割的自由,我是个成年人,你凭啥单方面的决定哪些东西我该看哪些东西我不该看?

我们的党告诉我们只有共产党才能就中国,国民党的老巢台湾也没比大陆差到那里,真是一句屁话,我认为大学生盲目入党是中国当代大学生最大的悲哀。

其实有时想想,我们到底有没有必要那么执着?我们执着的入党出国学习深造进入名企做什么电子商务网游平台SNS,可是我们的国民却有很多人上不起学吃不起饭不知道电脑为何物。

欲望是痛苦之源。

我最近关注豆瓣上一个同城的活动:我们一起去流浪动物救助基地做义工吧,看了土豆网上一个视频,我开始思考佛教,审视素食主义。我想去基地做些事情,可是两次都睡过了。我开始思考李连杰,重温他的《霍元甲》;我看了《甘地传》,思考着爱因斯坦的那句话:“后代子孙将无法相信,世上曾活生生的出现过这号人物”;我想到了感动中国颁奖台上徐本禹滚滚而下的两行泪水;我想到了丛飞之死;我想到我的朋友db,放弃了学业,“抛弃了一切”,去做他“想做的事情”:

我想到了陆川的《可可西里》;我想到了雀儿山口常年护路的道班战士,想到了青海湖边那个没有电的小镇。

没有什么能够阻挡,你对自由的向往

天马行空的生涯,你的心了无牵挂

穿过幽暗的岁月,也曾感到彷徨

当你低头的瞬间

才发现脚下的路

心中那自由的世界

如此的清澈高远

2008.5 青海湖

2008.8 四川甘孜

请叫我们:行者。

CLI派视频音频转码工具用法小结(ffmpeg)1

1 ffmpeg 264编码支持

2 ffmpeg 参数选项简要说明

2.1 main options

  • -i filename: 指定输入文件
  • -y: Overwrite output files
  • -target: Specify target file type ("vcd", "svcd", "dvd", "dv", "dv50", "pal-vcd", "ntsc-svcd", … ). All the format options (bitrate, codecs, buffer sizes) are then set automatically. It is one of the most useful options of ffmpeg. It instructs ffmpeg to just "do what it takes" for the target file to be usable.

2.2 视频剪辑相关参数

  • -ss: 设定剪辑开始时间[支持[hh:mm:ss[.xxx]]格式
  • -t: 设定剪辑长度[支持[hh:mm:ss[.xxx]]格式
  • -fs: 设定文件大小限制

2.3 video options

  • -b bitrate: 设定比特率(默认为200kb/s)
  • -r fps: frame rate(默认为25)
  • -s size: set frame size
    • wxh(width x height) or
    • abbreviations(qcif(176x144), vga(640x480), wvga(852x480)
  • -aspect aspect: 设定aspect ratio(4:3, 16:9, 30:9, 1:3 or 1.333, 1.777 etc.)
  • -sameq: use same video quality as source (implies VBR).
  • -vcodec: codec: force video codec to codec. Use the copy special value to tell that the raw codec data must be copied as is.
  • -newvideo: add a new video stream to the current output stream.
  • -pass n 选择处理遍数(1或者2)。两遍编码非常有用。第一遍生成统计信息,第二遍生成精确的请求的码率
  • -map inputstreamid[:syncstreamid]: Set stream mapping from input streams to output streams.
  • -itsoffset offset Set the input time offset in seconds. "[-]hh:mm:ss[.xxx]" syntax is also supported. This option affects all the input files that follow it.

2.3.1 crop options(视频剪裁选项说明)

  • -crop x:y:width:height: Crop the input video to x:y:width:height.
  • -croptop
  • -cropbottom
  • -cropleft
  • -cropright

2.3.2 pad and filter options

  • -vf filter_graph
  • -vf pad=width:height:x:y:color(这个命令可以给裁剪后的视频增加上下两个pad,形成电影似的效果)

2.4 audio options

  • -ar freq: set the audio sampling frequency(default=44100Hz)
  • -ab bitrate: 设定比特率(默认为64K)
  • -aq q: set the audio quality
  • -ac channels: set the number of audio channels(default=1)
  • -acodec codec: force audio codec to codec. Use the copy special value to specify that the raw codec data must be copied as is.
  • -newaudio: add a new audio track to the output file. If you want to specify parameters, do so before -newaudio (-acodec, -ab, etc..).

2.5 subtitle options

  • -scodec codec: force subtitle codec ('copy' to copy stream).
  • -newsubtitle: add a new subtitle stream to the current output stream.

2.6 disable options

  • -an: disable audio recording.
  • -vn: disable video recording.
  • -sn: disable subtitle recording.

2.7 frames

  • -dframes number: set the number of data frames to record
  • -vframes number: set the number of video frames to record
  • -aframes number: set the number of audio frames to record

2.8 preset files

  • -vpre
  • -apre
  • -spre
  • -fpre

3 ffmpeg 用法示例(see http://www.catswhocode.com/blog/19-ffmpeg-commands-for-all-needs for more examples)

  • Basic Usage: ffmpeg [input options] -i [input file] [output options] [output file]
  • Normally the minimum one should specify in an ffmpeg command line would be something like: ffmpeg -i INFILE -acodec ACODEC -ab 96k -vcodec VCODEC -b 500k OUTFILE …replacing the capitalised values as appropriate. When using x264, we need to add some extra bits as the defaults are bad.
  • Getting infos from a video file: ffmpeg -i video.avi
  • Turn X images to a video sequence: ffmpeg -f image2 -i image%d.jpg video.mpg
  • Turn a video to X images: ffmpeg -i video.mpg image%d.jpg
  • Extracting sound from a video, and save it as Mp3: ffmpeg -i source_video.avi -vn -ar 44100 -ac 2 -ab 192 -f mp3 sound.mp3
  • Convert .avi video to .mpg: ffmpeg -i video_origine.avi video_finale.mpg
  • Convert .avi to animated gif(uncompressed): ffmpeg -i video_origine.avi -pix_fmt rgb24 gif_anime.gif
  • Mix a video with a sound file: ffmpeg -i son.wav -i video_origine.avi video_finale.mpg
  • Compress .avi to VCD mpeg2 NTSC format: ffmpeg -i video_origine.avi -target ntsc-vcd video_finale.mpg
  • Multi-pass encoding with ffmpeg: ffmpeg -i fichierentree -pass 2 -passlogfile ffmpeg2pass fichiersortie-2
  • Crop top 30 pixels and bottom 30 pixels to get a new video file: ffmpeg -i input_file.flv -croptop 30 -cropbottom 30 -target ntsc-dvd -aspect 16:9 output_file.mpg
  • Delaying the audio or the video: ffmpeg -i input1 -itsoffset 00:00:03.5 -i input2 ………..

4 ffmpeg learning resources

5 Related tools

5.1 transcode

5.2 mencode(from mplayer)

6 yamdi给视频增加关键帧

  • Yet Another MetaData Injector, see http://yamdi.sourceforge.net/
  • options:
    • -i: 输入文件
    • -o: 输出文件
    • -x: An XML file with the resulting metadata information.
    • -l: Adds the onLastSecond event(?).
  • other tools:
    • flvtool2: written by Ruby, slow, need more memory
    • flvmdi: not open source

7 ffmpegthumbnailer截取视频的thumbnail

  • ubuntu: sudo apt-get install ffmpegthumbnailer
  • options:
    • -i<s>: input file
    • -o<s>: output file
    • -s<n>: thumbnail size (default: 128)
    • -q<n>: image quality (0 = bad, 10 = best) (default: 8) (only for jpeg)
    • -c<s>: override image format (jpeg or png) (default: determined by filename)
    • -t<n|s>: time to seek to (percentage or absolute time hh:mm:ss) (default: 10%)
    • -a: ignore aspect ratio and generate square thumbnail
    • -f: create a movie strip overlay

8 Youku的一些探索

  • Youku网站上的大部分视频都是分段的,一般每段视频在5-10分钟之内
  • 目前Youku网上的视频格式大概有三种:
    • 高清mp4格式(h264编码)
    • 高清flv格式(h264编码)
    • 普通flv格式(应该是h263编码)

Author: Lox Freeman <xiaohanyu1988@gmail.com>

HTML generated by org-mode 6.21b in emacs 23

新鲜的刚出炉的热乎乎的淘宝和DeviceVM面经

折腾的一天,两场面试,感觉都还凑合。

第一场是华数淘宝的面试,关于华数淘宝和淘宝之间有什么区别和联系,我也没有细究,反正上午十点到了西湖国际大厦后只看到了taobao的字样。来到了前台,填写了一份履历表,一会一位男员工走了出来带着我来到了一个沙发处。有好几个桌子,都是面试官和学生们面对面地谈。

首先还是自我介绍。我就毫不客气巴拉巴拉地说了一通,连自己骑车玩户外的事情都吹了一通。面试官微笑,蛮厉害的嘛。接下来就开始考题目了。第一道题目是c语言指针的问题。问:

char *p = "abcd";
printf("%d", sizeof(p));
char p1[] = "abcd";
printf("%d", sizeof(p1));

我大概是脑子进水了,两个简单的问题都答错了,弄了一个非常尴尬的开端。然后又问我有没有用过内存拷贝函数?"memset?"我问。“不是,memcpy。算了,有没有用过strcpy”。“这个,用过”。“那你写一下这个函数的实现”。然个后我就很不争气的写了一个非常糙的实现,还是在面试官的提醒下才改对的,两个明显的bug。尴尬。

接下来问我对排序了解多少,知道哪些排序。我说我对那种[tex]O(\lg n)[/tex]的排序比较熟,他让我举例,我就把能想到的选择排序、冒泡排序、插入排序、快速排序、归并排序、希尔排序等等都说了。然后他让我描述一下快速排序的实现,我就balabala讲了一通——其实要让我写,短时间内我还真不一定能写出来。快排到此为止。又问我那些是稳定排序和不稳定排序,我说这个我真不清楚了,可能要看具体实现——很尴尬。“还有别的排序方法吗”,“有,当然有,比如桶排序——还是创新工厂的面试时学会的……”。我又balabala的吹了一通,把决策树都说上了,这些排序总算是问完了。

问我了解设计模式不?我说看过几页《大话设计模式》。然后他问了一个单例模式?我就傻眼了,嘟囔着说不出来。其实这东西一直想去研究,一直在拖。接下来问我程序的内存空间是如何管理的。我就说栈和堆的东西。蒙混过关。然后问我“给你两个栈,如何模拟一个全功能的队列?”我想了想,给了一两套方案,不过终究不完美。

中间问我java的vector是怎么回事?java有vector的?我愣了。然后他说那就说说c++ stl里面的vector和list是怎么实现的吧,又什么区别。这个问到点子上了。记得大二时扫《c++ primer 3rd edition》时恰好仔细研究过,我就balabala的说了一下vector<int> v的v.capacity()和v.size()的区别,看得懂的应该不用细讲,看不懂的看官,你的c++比我还差,赶紧回家补补。问完这个又问我进程和线程有啥关系。一个进程开辟出来的内存,其子线程是否能够访问等等,答得一般。

又问我在实验室做的项目(一个windows mobile界面开发的项目),问我GUI库中的button的onclicked()的动作是如何相映处理的。我就把Win32 API里面的消息循环机制又吹了一通。面试官表示很满意。

最后问我linux shell命令的东西,比如如何找出一个分区内占用空间最大的目录,我说用"du -h . | sort",如何看磁盘使用量——"df -h",如何查找文件等等,由于日常经常用linux小打小闹,所以这部分答的也还算可以。

大概就谈了这么多,最后他送我出去,还郑重地握了握手,感觉还凑合吧。

然后回到玉泉吃了个午饭,就赶往滨江区DeviceVM参加下午一点的面试。公交+打的跌跌撞撞到了盾安大厦DeviceVM公司,接待我的是一个ppmm。二话不说,先给了我一套题目,说时间一个小时,标记的必做,没有标记的选作——这个标记主要根据你应聘的职位来标的。我的娘,题目总共有十几页,大概是16种类型,不过我只需要做6、7种必做的。类型分布大概如下:

  1. c/c++(what's operator overloading, what's copy constructor)
  2. oo(class, instance, inheritance, polymorphism, how to modeling)
  3. gtk(layout, glib)
  4. qt(qmake)
  5. linux file system(/proc directory, how to mount multiple file systems)
  6. linux device driver(what's io port? how to install and remove driver?)
  7. php
  8. javascript
  9. regular expression
  10. windows API
  11. xml
  12. uml
  13. networking
  14. python(list, tuple, dict)
  15. shell script($PATH, find command)
  16. logic
  17. personal information

我不管是否必答,先把自己会的都答上了。其实题目都不是很难,只是考的比较广泛,这就看你平时的积累了。而且每种职位大概只有7个部分是必答的。我应聘的是linux c++开发工程师实习,Qt和Gtk必考,可是我只有了解,却基本不会,倒是把Shell和Filesystem基本都答上了……汗。

答完了给了我一份履历表,填完履历表过来一位男面试官。看着我简历问我一些问题。首先让我阐述下c++的多态性是怎么回事。我就balabala胡侃了一通,把RTTI和函数指针都侃出来了,面试官表示满意。然后让我介绍了下实验室项目,我又balabala说了一通,面试官表示还不错。问我有没有用过javascript,我说只用过document.write()……又问我如何写一个程序,探知另外一个c++可执行程序的堆内存使用情况。这个问题不太明了,他说就是让你写个调试器类似的东西,我说没什么思路,他说可以利用c++的运算符重载,重载一下new和delete,让new和delete输出一些信息。不过我头脑中没有任何重载new和delete的想法和回忆,晕了。

最后他问我实习时间可以保证多少。我说最多到十月中旬,问我什么时候考完试,我说7月6号。然后就这样结束了。我也不知道这么问究竟什么意思,淘宝的面试官也这么问我,不知是悲剧呢,还是心里觉得我这个人还有点潜质,打算发offer了。

Ok,到此为止。

zoj 1601

小数的分数逼近,题目的思路是挺简单的,假设A=N/D,那么N=[D*A]或者N=[D*A]+1,按照这个公式对D进行枚举,不断更新最佳值即可。下面是我的WA的代码:

#include <stdio.h>
#include <math.h>

int main(int argc, char *argv[])
{
    double A;
    int L;
    int N, D;
    double delta;
    double min;
    
    while (scanf("%lf%d", &A, &L) != EOF)
    {
        min = 10 * L;

        int i;
        
        for (i = 0; i * A <= L && i <= L; ++i)
        {
            int temp_D = i;
            int N1 = (int) i * A;
            double delta1 = fabs(A - (double) N1 / i);

            int N2 = (int) i * A + 1;
            double delta2 = fabs(A - (double)N2 / i);

            if (delta1 < delta2 && delta1 < min)
            {
                D = temp_D;
                N = N1;
                min = delta1;
            }
            else if(delta1 > delta2 && delta2 < min)
            {
                D = temp_D;
                N = N2;
                min = delta2;
            }
        }

        printf("%d %d\n", N, D);
    }
    
    return 0;
}

由于没有测试数据,所以也不知道这道题目究竟错在哪里。我也找来了AC的代码

#include<stdio.h>
#include<math.h>

int main(void)
{
    double min;
    double A;
    long i;
    long j;
    long N;
    long D;
    long L;
    long pd;
    long pn;

    while (scanf("%lf %ld", &A, &L) != EOF)
    {
        pn = -999999;
        pd = 1;
        min = 9999999;

        for (D=1; D<=L; ++D)
        {
            N = (long)(D * A);
   
            if (N > L)
            {
                break;
            }
   
            for (i=0; i<=1; ++i)
            {
                if (fabs(min - A) > fabs((double)(N+i)/(double)D - A))
                {
                    pn = N+i;
                    pd = D;
                    min = (double)(N+i)/(double)D;
                }
            }
        }
  
        if (pn == -999999)
        {
            for (D=1; D<=L; ++D)
            {
                for (N=1; N<=L; ++N)
                {
                    if (fabs(min - A) > fabs((double)N/(double)D - A))
                    {
                        pn = N;
                        pd = D;
                        min = (double)N/(double)D;
                    }
                }
            }
        }
  
        printf("%ld %ld\n", pn, pd);
    }

    return 0;
}

但是这段AC的代码在输入3.33 9的时候给出的答案是10 3,显然是错误的答案。困惑中。求高人指点。




Host by is-Programmer.com | Power by Chito 1.3.3 beta | © 2007 LinuxGem | Design by Matthew "Agent Spork" McGee