声明:此文档只做学习交流使用,请勿用作其他商业用途
author:朝阳_tonyE-mail : linzhaolover@gmail.com
Create Date:2014-12-3 11:02:48 Wednesday
Last Change:2014-12-3 14:42:20 Wednesday
转载请注明出处:http://blog.csdn.net/linzhaolover
摘要:
FFmpeg 一个开源的视频编解码库,几乎市面上的各大视频播放软件都带用它,随着其支持平台的增多 ,如arm,android,window, linux,mac, 越来越多的平台都开始主动支持它;下面我总结了一些从Power8 big endian 移植到power8 little endian的经验,希望大家能从中受到启发!
环境搭建
下载源码
FFmpeg官网http://www.ffmpeg.org/
FFmpeg开发文档http://www.ffmpeg.org/developer.html
FFmpeg Fate测试环境搭建文档http://www.ffmpeg.org/fate.html
mkdir -p /home/work/ffmpeg_project
cd /home/work/ffmpeg_project
git clone git://source.ffmpeg.org/ffmpeg src
Fate 测试
如果你想验证自己的代码或者向社区提交patch,那么你一定要做fate test
cd /home/work/ffmpeg_project
cp src/doc/fate_config.sh.template fate_config
更改fate_config 文件
slot=ppc64le-linux-qemu-ubuntu14.04-gcc-4.8.2-linzhaolover # some unique identifier
repo=git://source.ffmpeg.org/ffmpeg.git # the source repository
samples=/home/work/ffmpeg_project/fate-suite # path to samples directory
workdir=/home/work/ffmpeg_project # directory in which to do all the work
#fate_recv="ssh -T -i /home/work/ffmpeg_project/ffmpegsshkey/ffmpegkey fate@fate.ffmpeg.org" # command to submit report
comment="test --cpu=power8" # optional description
build_only= # set to "yes" for a compile-only instance that skips tests
# the following are optional and map to configure options
arch=
cpu=
cross_prefix=
as=
cc=
ld=
target_os=
sysroot=
target_exec=
target_path=
target_samples=
extra_cflags=
extra_ldflags=
extra_libs=
extra_conf="--cpu=power8" # extra configure options not covered above
#make= # name of GNU make if not 'make'
makeopts="-j16" # extra options passed to 'make'
#tar= # command to create a tar archive from its arguments on stdout,
# defaults to 'tar c'
需要注意和修改的地方
slot=ppc64le-linux-qemu-ubuntu14.04-gcc-4.8.2-linzhaolover # some unique identifier
注意:slot后面的平台信息,一定要写的唯一,否则你会将其他人提交了的report覆盖掉,
ppc64le 是cpu信息,
ubuntu14.04是系统信息,
gcc-4.8.2 是 gcc的版本号
linzhaolover 是我写的额外的字符,方便更其他人区分,防止雷同,覆盖掉其人的测试
samples=/home/work/ffmpeg_project/fate-suite # path to samples directory
workdir=/home/work/ffmpeg_project # directory in which to do all the work
samples目录会下载最新的原始测试音频视频文件,方便你做所有的测试
workdir 当然就是你的工作目录了
#fate_recv="ssh -T -i /home/work/ffmpeg_project/ffmpegsshkey/ffmpegkey fate@fate.ffmpeg.org" # command to submit report
如果你想向社区提交报告,就要打开它,但前提你已经在FFmpeg进行了应有的注册和审查;
>>>制作key
# mkdir ffmpegsshkey
# ssh-keygen -t rsa -f ffmpegsshkey/ffmpegkey -C "linzhaolover@163.com"
Generating public/private rsa key pair.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in ffmpegsshkey/ffmpegkey.
Your public key has been saved in ffmpegsshkey/ffmpegkey.pub.
The key fingerprint is:
0b:ae:ee:2e:72:c6:75:de:66:f3:6a:4a:f2:19:ef:d3 linzhaolover@163.com
The key's randomart image is:
+--[ RSA 2048]----+
| |
| |
| |
| |
| . S |
| .... . |
| . ..o+... |
|. = =.+B E |
| + == +*+=. |
+-----------------+
# ssh -T -i /home/work/ffmpeg_project/ffmpegsshkey/ffmpegkey fate@fate.ffmpeg.org
The authenticity of host 'fate.ffmpeg.org (195.154.232.35)' can't be established.
RSA key fingerprint is d3:f1:83:97:a4:75:2b:a6:fb:d6:e8:aa:81:93:97:51.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'fate.ffmpeg.org,195.154.232.35' (RSA) to the list of known hosts.
fate@fate.ffmpeg.org's password:
生成秘钥好,要测试一下,为了是去掉yes那个提示,方便你后期脚本自动提交,O(∩_∩)O~
将ffmpegsshkey目录下的ffmpegkey.pub公钥发送给fate-admin@ffmpeg.org
ssh -T -i /home/work/ffmpeg_project/ffmpegsshkey/ffmpegkey fate@fate.ffmpeg.org
-i 参数是为了指定你的私钥所在位置,最好使用全路径,
comment="test --cpu=power8"
我写的一些注释,方便在fate提交报告后查找到
Fate Report 主页http://fate.ffmpeg.org/
build_only="yes" # set to "yes" for a compile-only instance that skips tests
如果你只想编译不想做fate测试,那么就将这儿设置为yes,否则不填,就像我上面的配置一样
extra_conf="--cpu=power8" # extra configure options not covered above
我是移植到power8平台,所以添加了编译参数,这个参数是传给configure的
makeopts="-j16" # extra options passed to 'make'
我的平台有16个hard threads 硬件线程,所以让其make -j16 快速并行编译;
执行fate测试
fate_config文件改好了,那我们就开始做fate测试,可以先做本地的,一定要联网啊,会从官网下载测试需要的视频文件的,不大,目前也就882M,O(∩_∩)O哈哈~
测试脚本在哪儿???
cp src/tests/fate.sh fate.sh
./fate.sh /home/work/ffmpeg_project/fate_config
一定要指定fate_config的路径,否则 fate.sh 不识别
看它是在下载东西吧,看你的网速了,写喝杯茶休息一下;下载一次就够了,以后就是更新了
最终在/home/work/ffmpeg_project目录下多出这些文件
version-ppc64le-linux-qemu-ubuntu14.04-gcc-4.8.2-linzhaolover
configure.log
compile.log
test.log
report
build/
fate-suite/
install/
fate.lock
version-ppc64le-linux-qemu-ubuntu14.04-gcc-4.8.2 你的平台信息和版本信息了,如果提交,会被发送到fate官网首页的;
fate-suite 你下载的测试文件
build就是你编译的产生的文件
install就是你临时的安装文件夹了
fate.lock这个文件要注意,如果你测试一半,中途ctrl-c了,你可以要删除这个文件啊,这可是锁,防止同一时刻多次fate test的;
执行单个fate测试
有时候修改点code,不可能每次都全部fate测试,就需要单个测试进行调试
还记得刚才的build目录吗,该派上用场了
/home/work/ffmpeg_project/build
make fate-list > allfatelist.log
将list结果重定向保存到allfatelist.log文件中,去看看有少,2328个,这么多测试,随便选个吧
# make V=1 fate-aac-al07_96
TEST aac-al07_96
/home/work/ffmpeg_project/src/tests/fate-run.sh fate-aac-al07_96 "/home/work/ffmpeg_project/fate-suite" ""
"/home/work/ffmpeg_project/build" 'pcm -i /home/work/ffmpeg_project/fate-suite/aac/al07_96.mp4' 'oneoff'
'/home/work/ffmpeg_project/fate-suite/aac/al07_96_reorder.s16' '2' '1' '' '' '' '' '' '' ''
/home/work/ffmpeg_project/build/ffmpeg_project -nostats -cpuflags all -threads 1 -thread_type frame+slice -i
/home/work/ffmpeg_project/fate-suite/aac/al07_96.mp4 -vn -f s16le -
make V=1 fate-aac-al07_96
测试fate-aac-al07_96
V=1 是为了显示执行时用到测试,和详细结果,方便后期调试用
当然这个测试正确的,如果是错误的,会提示Error的;
做移植操作
添加自己的平台
假设没有你当前的平台,那就要去修改src 目录中的configure文件,来增加自己的平台
power[3-8]*)
cpuflags="-mcpu=$cpu"
;;
在configure文件中,原先只有3-7,今年才开始有的p8所以,我们将7改成了8,你要更加你的平台,去更改类似的地方;或者create一个新的平台;
编译的时候别忘了指定你的平台,
例如
./configure --cpu=power8
找到需要移植的部分
# cd /home/work/ffmpeg_project/src
# find ./ -type d -name ppc -print
./libswscale/ppc
./libavutil/ppc
./libavcodec/ppc
我以目录为查找目标,发现在FFmpeg源码中有三个目录是关于ppc ( power pc ) 的目录,
为什么这么容易选定目标, 这要归功于FFmpeg的代码架构,它的代码中好多都用到了函数回调,上层架构主要提供接口,具体的实现和平台依赖只要将你的实现函数赋值给相应的函数指针就可以了
例如libavcodec/h264dsp.c 文件中的ff_h264dsp_init() 函数, 在函数的开始部分初始化的 是普通c语言写的函数,在最后开始对应每个平台的初始化
初始化函数开始位置
libavcodec/h264dsp.c
av_cold void ff_h264dsp_init(H264DSPContext *c, const int bit_depth,
const int chroma_format_idc)
{
#undef FUNC
#define FUNC(a, depth) a ## _ ## depth ## _c
#define ADDPX_DSP(depth) \
c->h264_add_pixels4_clear = FUNC(ff_h264_add_pixels4, depth);\
c->h264_add_pixels8_clear = FUNC(ff_h264_add_pixels8, depth)
初始化函数最后位置
if (ARCH_AARCH64) ff_h264dsp_init_aarch64(c, bit_depth, chroma_format_idc);
if (ARCH_ARM) ff_h264dsp_init_arm(c, bit_depth, chroma_format_idc);
if (ARCH_PPC) ff_h264dsp_init_ppc(c, bit_depth, chroma_format_idc);
if (ARCH_X86) ff_h264dsp_init_x86(c, bit_depth, chroma_format_idc);
我们去看一下ff_h264dsp_init_ppc() 函数,看看她在哪儿,都做了些什么
av_cold void ff_h264dsp_init_ppc(H264DSPContext *c, const int bit_depth,
const int chroma_format_idc)
{
#if HAVE_ALTIVEC
if (!PPC_ALTIVEC(av_get_cpu_flags()))
return;
if (bit_depth == 8) {
c->h264_idct_add = h264_idct_add_altivec;
if (chroma_format_idc <= 1)
c->h264_idct_add8 = h264_idct_add8_altivec;
c->h264_idct_add16 = h264_idct_add16_altivec;
c->h264_idct_add16intra = h264_idct_add16intra_altivec;
c->h264_idct_dc_add= h264_idct_dc_add_altivec;
c->h264_idct8_dc_add = h264_idct8_dc_add_altivec;
c->h264_idct8_add = h264_idct8_add_altivec;
c->h264_idct8_add4 = h264_idct8_add4_altivec;
c->h264_v_loop_filter_luma= h264_v_loop_filter_luma_altivec;
c->h264_h_loop_filter_luma= h264_h_loop_filter_luma_altivec;
c->weight_h264_pixels_tab[0] = weight_h264_pixels16_altivec;
c->weight_h264_pixels_tab[1] = weight_h264_pixels8_altivec;
c->biweight_h264_pixels_tab[0] = biweight_h264_pixels16_altivec;
c->biweight_h264_pixels_tab[1] = biweight_h264_pixels8_altivec;
}
#endif /* HAVE_ALTIVEC */
}
在libavcodec/ppc/h264dsp.c 文件中,它只是进行函数指针的赋值,具体的实现当然是在各个小函数中了 ;
我们只需要修改ppc 目录下的各个小函数,就可以将big endian的代码移植到了little endian上来运行;
将底层实现函数各个击破
函数调用先后顺序
这么多的子函数,我们怎么知道那个用到那个没用到,他们的先后调用关系是啥
printf("footprint : %s %4d %s\n", __FILE__, __LINE__, __func__);
fflush(stdout);
采用传统print大法,看看先调用的哪一个,在每个底层函数的入口处加入打印信息,然后执行单个fate测试,看看打印的信息;
单独选个fate测试,好好去测试一把吧;刚才我选择的是h264的code,我就以他的测试为了,
make fate-list 的里,是不是有 fate-h264-conformance-frext-brcm_freh11 这个测试,
make V=1 fate-h264-conformance-frext-brcm_freh11
狙击函数的输入输出结果
函数调用先后顺序是找到了,接下来要一个一个修改函数了
怎么修改,如何修改,修改参考的依据是什么?
我的平台是p8 little endian, 我之前是p8的big endian,所以我知道p8的big endian 肯定是正确的,我就需要参考big endian,去修改我的程序,只要单个函数,输出结果值一致,处理流程相同就行,这种平台的错误,是由于指令不兼容,等问题造成的;
找到第一个别调用的函数,看一下输入输出结果和 另一个平台big endian的输入输出是否相同,不相同,就去修改,直到相同为止,
结果相同了,将打印信息都去掉,看看单个测试是否正确了,屡试不爽,O(∩_∩)O~
做整体的fate测试
当个搞定了,那就去做个整体的fate测试,测试看看有哪些都已经正确,并且被fix了;
注意,先将你修改的code生成一个patch,因为在做整体fate测试时,会更新到最新的code,会把你的修改清除掉的
修改一下fate.sh测试脚本,让其不清除掉你的修改,只允许你手动更新
在fate.sh中查找update关键词,
#test -d "$src" && update || checkout || die "Error fetching source"
找到这一行,就注释掉就可以了,
./fate.sh /home/work/ffmpeg_project/fate_config
error,怎么不测试呢???
原来你刚才禁止掉了update,就会导致你src中的版本和你上次执行的fate整体测试的中产生的version-ppc64le-linux-qemu-ubuntu14.04-gcc-4.8.2-linzhaolover中的版本一样,
那就删除它们,再测试
rm -rf version-ppc64le-linux-qemu-ubuntu14.04-gcc-4.8.2-linzhaolover
rm -rf configure.log compile.log test.log report build/ install/ fate.lock
./fate.sh /home/work/ffmpeg_project/fate_config
向FFmpeg社区提交patch
你感觉ffmpeg好用,而且想完善其功能,fix bug,那就给它提交patch吧;
假设你刚才的fate整体测试,使测试结果通过了不少,那么请好好整理一下你的代码,生成patch,提交到到社区里面去,
cd src
git config user.name "Your Name"
git config user.email "mailname@gmail.com"
git diff #查看你修改的代码
git commit - am " avcode/ppc/h264dsp : POWER LE support for functionName() "
git format-patch -1
./tools/patcheck 0001-patchname.patch
format-patch -1 由于你只是提交了一次,所以生成一个patch就行了;
name email 是你在注册ffmpeg bug report 时填写的那个名字和mail地址,别搞错了啊,
patchcheck 是 FFmpeg自带的一个patch检查工具,很好的,他忙你检查,你就能够避免很多错,如果错误太多,你就要重新修改code,重复上面测fate测试
生成patch这些步骤,慢慢来吧,一回生二回熟,O(∩_∩)O~
最后吧patch发出去就行了,发给ffmpeg-devel@ffmpeg.org
注意,小激动了一把,亲,你确定你在ffmpeg社区审核通过了吗,O(∩_∩)O~
友情提示,为了使FFmpeg质量精良,请好好审查你的code,提交前多做检查,有不懂的地方,我们可以一起探讨,O(∩_∩)O~