Monday, March 31, 2008

ffmpeg的编译大全

 

August 5, 2006

ffmpeg的编译大全

ffmpeg的编译大全

最近互联网视频共享的网站很火,公司也想搞类似的网站,初步是用fms+ffmpeg形式

fms负责在线录制,播放,ffmpeg则在后台处理上传的资源转换成一定的格式。

为了让ffmpeg支持的格式尽量多,所以特把我的编译笔记分享一下

环境:redhat as4

1。首先获取ffmpeg

很多人找不到怎么下载,其实之前ffmpeg可以通过cvs下载,不过最近他已经换成了更加强大的svn

如何使用SVN我这里不再介绍,网上还有大量的安装和使用的文章可以借鉴,这里简单罗列几个SVN辅助的软件:

SubVersion,从 http://subversion.tigris.org/ 下载,支持linux,我们这里就装这个

TortoiseSVN,从 http://tortoisesvn.tigris.org/ 下载,是很不错的SVN客户端程序,为windows外壳程序集成到windows资源管理器和文件管理系统的Subversion客户端,用起来很方便,commit动作变得就像Winrar右键压缩一样方便。


ok,那我们先装subversion,记住最好之前装过apr和apr-util,在apache.org网站能下到

wget http://subversion.tigris.org/downloads/subversion-1.3.2.tar.gz
tar zvxf subversion-1.3.2.tar.gz
cd subversion-1.3.2
./configure --with-apr=/usr/local/apr-httpd --with-apr-util=/usr/local/apr-util-httpd/
make
make install


到此,我们就可以通过svn命令获取最新的ffmpeg了

svn checkout svn://svn.mplayerhq.hu/ffmpeg/trunk ffmpeg

你会发现在你所在的目录,自动出现一个ffmpeg的目录,就是你下载的源代码。


我们还不能这么快编译ffmpeg,应该如果要让ffmpeg支持更多格式的转换,还需做一些前期工作


2.支持mp3,linux当然是lame,下载解压

cd lame-3.96.1
./configure --enable-shared --prefix=/usr
这里推荐尽量装在/usr下,默认是装在/usr/local下。这样ffmpeg编译都能顺利的找到库文件


3.支持Ogg Vorbis:
as4自带相应的rpm包,你可以安装一下如下rpm包
libvorbis, libvorbis-devel,libogg, libogg-devel


4.支持xvid x264,现在最流行的两种高质量的压缩格式
xvid的编译安装
wget http://downloads.xvid.org/downloads/xvidcore-1.1.0.tar.gz
tar zvxf xvidcore-1.1.0.tar.gz
./configure --prefix=/usr
make
make install

x264的获取同样是采用svn方式,看来svn取代cvs不远了
svn co svn://svn.videolan.org/x264/trunk x264
cd x264
./configure --prefix=/usr --enable-shared
make
make install


5.AC3和dts编码的支持
as4系统似乎已经支持ac3编码,编译的时候只要加--enable-a52 --enable-gpl参数就行

libdts编译参数
./configure --prefix=/usr
make
make install


6.mpg4 aac格式支持,由于服务器还针对手机用户服务,所以,类似aac,mpg4铃声格式的支持,我们也得做。这里我们安装faad2和faac就行
下载请到http://www.audiocoding.com/modules/mydownloads/

FAAD2的编译
cd faad2
autoreconf -vif
./configure --prefix=/usr --with-mp4v2 --enable-shared
make
make install

faac的编译
cd faac
chmod +x bootstrap
./bootstrap
./configure --prefix=/usr --with-mp4v2 --enable-shared
make
make install


7.支持3gp格式,这也是现在好多手机支持的格式,因为手机用户是我们的主要用户,所以也得支持编译

编译的时候加上--enable-amr_nb --enable-amr_wb参数就行,根据编译系统的提示,所以我们得下载一

些编译3gp所需得文件。

wget http://www.3gpp.org/ftp/Specs/archive/26_series/26.204/26204-510.zip
解压以后把里面的文件都拷贝到libavcodec/amrwb_float

wget http://www.3gpp.org/ftp/Specs/archive/26_series/26.104/26104-510.zip
解压以后把里面的文件都拷贝到libavcodec/amr_float


好了,基本ffmpeg支持的格式,我们基本都做好前期准备了,下面是ffmpeg的编译


./configure --prefix=/usr --enable-gpl --enable-shared --enable-mp3lame --enable-amr_nb --enable-amr_wb --enable-amr_if2 --enable-libogg --enable-vorbis --enable-xvid --enable-a52 --enable-a52bin --enable-faadbin --enable-dts --enable-pp --enable-faad --enable-faac --enable-x264 --enable-pthreads --disable-ffserver --disable-ffplay
make
make install

补充1:
关于3gp的编译,如果大家要编译--enable-amr_nb-fixed,那就不能跟--enable-amr_nb同时编译,我不大清楚这两者到底有什么区别,似乎fixed是修正版,管他呢,编译的方法:
wget http://www.3gpp.org/ftp/Specs/archive/26_series/26.073/26073-510.zip
解压以后把里面的文件都拷贝到libavcodec/amr目录下

修改libavcodec/amr/makefile 找到CFLAGS = -Wall -pedantic-errors -I. $(CFLAGS_$(MODE)) -D$(VAD) 换成CFLAGS = -Wall -I. $(CFLAGS_$(MODE)) -D$(VAD) -DMMS_IO

整体编译参数就是
./configure --prefix=/usr --enable-gpl --enable-shared --enable-mp3lame --enable-amr_nb-fixed --enable-amr_wb --enable-amr_if2 --enable-libogg --enable-vorbis --enable-xvid --enable-a52 --enable-a52bin --enable-dts --enable-pp --enable-faad --enable-faadbin --enable-faac --enable-x264 --enable-pthreads --disable-ffserver --disable-ffplay
make
make install

补充2:
有朋友说在当在cpu是x86的情况下,不能编译过去,我这里没发生过这情况,如果你真编译不过去,可以试着加上cpu=cpu

ok,一个强大的ffmpeg编译好了,不过目前管方的ffmpeg还不支持wma9和rmvb,不过网上有很多解决方法,大家去自己找找。

 

Friday, March 21, 2008

GCC笔记

The History of GCC


1984年,Richard Stallman发起了自由软件运动,GNU (Gnu's Not Unix)项目应运而生,3年后,最初版的GCC横空出世,成为第一款可移植、可优化、支持ANSI C的开源C编译器。
GCC最初的全名是GNU C Compiler,之后,随着GCC支持的语言越来越多,它的名称变成了GNU Compiler Collection。
这里介绍的gcc是GCC的前端,C编译器.


警告信息


    -Wall : 显示所有常用的编译警告信息。
    -W    : 显示更多的常用编译警告,如:变量未使用、一些逻辑错误。
    -Wconversion : 警告隐式类型转换。
    -Wshadow : 警告影子变量(在代码块中再次声明已声明的变量)
    -Wcast-qual :警告指针修改了变量的修饰符。如:指针修改const变量。
    -Wwrite-strings : 警告修改const字符串。
    -Wtraditional : 警告ANSI编译器与传统C编译器有不同的解释。
    -Werror : 即使只有警告信息,也不编译。(gcc默认:若只有警告信息,则进行编译,若有错误信息,则不编译)


C语言标准


你可以在gcc的命令行中通过指定选项来选择相应的C语言标准: 从传统c到最新的GNU扩展C. 默认情况下, gcc使用最新的GNU C扩展.

    -ansi : 关闭GNU扩展中与ANSI C相抵触的部分。
    -pedantic          : 关闭所有的GNU扩展。
    -std=c89           : 遵循C89标准
    -std=c99           : 遵循C99标准
    -std=traditional : 使用原始C
注意:后4个选项可以与-ansi结合使用,也可以单独使用。

可在gcc中使用大量GNU C扩展.

生成特定格式的文件


以hello.c为例子,可以设置选项生成hello.i, hello.s, hello.o以及最终的hello文件:

    hello.c : 最初的源代码文件;
    hello.i : 经过编译预处理的源代码;
    hello.s : 汇编处理后的汇编代码;
    hello.o : 编译后的目标文件,即含有最终编译出的机器码,但它里面所引用的其他文件中函数的内存位置尚未定义。
    hello / a.out : 最终的可执行文件
    (还有.a(静态库文件), .so(动态库文件), .s(汇编源文件)留待以后讨论)

如果你不通过-o指定生成可执行文件名,那么会默认生成a.out. 不指定生成文件名肯能覆盖你上次生成的a.out.

e.g.
$ gcc hello.c
在不给gcc传递任何参数的情况下, gcc执行默认的操作: 将源文件编译为目标文件--> 将目标文件连接为可执行文件(名为a.out) --> 删除目标文件.

-c生成.o文件时,默认生成与源代码的主干同名的.o文件。比如对应hello.c生成hello.o. 但也可在生成目标文件时指定目标文件名(注意同时要给出.o后缀): $ gcc -c -o demo.o demo.c

    $ gcc -Wall -c hello.c              : 生成hello.o
    $ gcc -Wall -c -save-temps hello.c  : 生成hello.i, hello.s, hello.o
    注意-Wall 选项的使用场合:仅在涉及到编译(即会生成.o文件时,用-Wall)  

多文件编译、连接


如果原文件分布于多个文件中:file1.c, file2,c
    $ gcc -Wall file1.c file2.c -o name

若对其中一个文件作了修改,则可只重新编译该文件,再连接所有文件:
    $ gcc -Wall -c file2.c
    $ gcc file1.c file2.o -c name

注意:若编译器在命令行中从左向右顺序读取.o文件,则它们的出现顺序有限制:含有某函数定义的文件必须出现在含有调用该函数的文件之后。好在GCC无此限制。

编译预处理


以上述的hello.c为例, 要对它进行编译预备处理, 有两种方法: 在gcc中指定-E选项, 或直接调用cpp.gcc的编译预处理命令程序为cpp,比较新版本的gcc已经将cpp集成了,但仍提供了cpp命令. 可以直接调用cpp命令, 也可以在gcc中指定-E选项指定它只进行编译预处理.

$ gcc -E hello.c                            ==  $ cpp hello.c
上述命令马上将预处理结果显示出来. 不利于观看. 可采用-c将预处理结果保存:
$ gcc -E -c hello.i hello.c              ==  $ cpp -o hello.i hello.c
注意, -c指定名称要给出".i"后缀.

另外, gcc针对编译预处理提供了一些选项:
(1) 除了直接在源代码中用 #define NAME来定义宏外,gcc可在命令行中定义宏:-DNAME(其中NAME为宏名),  也可对宏赋值: -DNAME=value 注意等号两边不能有空格! 由于宏扩展只是一个替换过程,也可以将value换成表达式,但要在两边加上双括号: -DNAME="statement"
e.g. $ gcc -Wall -DVALUE="2+2" tmp.c -o tmp
如果不显示地赋值,如上例子,只给出:-DVALUE,gcc将使用默认值:1.

(2) 除了用户定义的宏外, 有一些宏是编译器自动定义的,它们以__开头,运行: $ cpp -dM /dev/null, 可以看到这些宏. 注意, 其中含有不以__开头的非ANSI宏,它们可以通过-ansi选项被禁止。
 
查看宏扩展

1, 运行 $ gcc -E test.c ,gcc对test.c进行编译预处理,并立马显示结果. (不执行编译) 2, 运行 $ gcc -c -save-temps test.c ,不光产生test.o,还产生test.i, test.s,前者是编译预处理结果, 后者是汇编结果.
   
利用Emacs查看编译预处理结果

针对含有编译预处理命令的代码,可以利用emacs方便地查看预处理结果,而不需执行编译,更为方便的是,可以只选取一段代码,而非整个文件:
1,选择想要查看的代码
2,C-c C-e (M-x c-macro-expand)
这样,就自动在一个名为"Macroexpansion"的buffer中显示pre-processed结果.

生成汇编代码

使用"-S"选项指定gcc生成以".s"为后缀的汇编代码:
$ gcc -S hello.c
$ gcc -S -o hello.s hello.c

生成汇编语言的格式取决于目标平台. 另外, 如果是多个.c文件, 那么针对每一个.c文件生成一个.s文件.

包含头文件


在程序中包含与连接库对应的头文件是很重要的方面,要使用库,就一定要能正确地引用头文件。一般在代码中通过#include引入头文件, 如果头文件位于系统默认的包含路径(/usr/includes), 则只需在#include中给出头文件的名字, 不需指定完整路径.  但若要包含的头文件位于系统默认包含路径之外, 则有其它的工作要做: 可以(在源文件中)同时指定头文件的全路径. 但考虑到可移植性,最好通过-I在调用gcc的编译命令中指定。

下面看这个求立方的小程序(阴影语句表示刚开始不存在):

#include <stdio.h>
#include <math.h>
int main(int argc, char *argv[])
{
  double x = pow (2.0, 3.0);
  printf("The cube of 2.0 is %f\n", x);
  return 0;
}

使用gcc-2.95来编译它(-lm选项在后面的连接选项中有介绍, 这里只讨论头文件的包含问题):
$ gcc-2.95 -Wall pow.c -lm -o pow_2.95
pow.c: In function `main':
pow.c:5: warning: implicit declaration of function `pow'

程序编译成功,但gcc给出警告: pow函数隐式声明。
$ ./pow_2.95
The cube of 2.0 is 1.000000

明显执行结果是错误的,在源程序中引入头文件(#include <math.h>),消除了错误。

不要忽略Warning信息!它可能预示着,程序虽然编译成功,但运行结果可能有错。故,起码加上"-Wall"编译选项!并尽量修正Warning警告。

搜索路径

首先要理解 #include<file.h>和#include"file.h"的区别:
#include<file.h>只在默认的系统包含路径搜索头文件
#include"file.h"首先在当前目录搜索头文件, 若头文件不位于当前目录, 则到系统默认的包含路径搜索头文件.

UNIX类系统默认的系统路径为:

头文件,包含路径: /usr/local/include/  or  /usr/include/
库文件,连接路径: /usr/local/lib/          or  /usr/lib/   

对于标准c库(glibc或其它c库)的头文件, 我们可以直接在源文件中使用#include <file.h>来引入头文件.

如果要在源文件中引入自己的头文件, 就需要考虑下面的问题:

1, 如果使用非系统头文件, 头文件和源文件位于同一个目录, 如何引用头文件呢?
――我们可以简单地在源文件中使用 #include "file.h", gcc将当前目录的file.h引入到源文件. 如果你很固执, 仍想使用#include <file.h>语句, 可以在调用gcc时添加"-I."来将当前目录添加到系统包含路径. 细心的朋友可能会想到: 这样对引用其它头文件会不会有影响? 比如, #include<file.h>之后紧接着一个#include<math.h>, 它能正确引入math.h吗? 答案是: 没有影响. 仍然能正确引用math.h. 我的理解是: "-I."将当前目录作为包含路径的第一选择, 若在当前目录找不到头文件, 则在默认路径搜索头文件. 这实际上和#include"file.h"是一个意思.

2, 对于比较大型的工程, 会有许多用户自定义的头文件, 并且头文件和.c文件会位于不同的目录. 又该如何在.c文件中引用头文件呢?
―― 可以直接在.c文件中利用#include"/path/file.h", 通过指定头文件的路径(可以是绝对路径, 也可以是相对路径)来包含头文件. 但这明显降低了程序的可移植性. 在别的系统环境下编译可能会出现问题. 所以还是利用"-I"选项指定头文件完整的包含路径.

针对头文件比较多的情况, 最好把它们统一放在一个目录中, 比如~/project/include. 这样就不需为不同的头文件指定不同的路径. 如果你嫌每次输入这么多选项太麻烦, 你可以通过设置环境变量来添加路径:
$ C_INCLUDE_PATH=/opt/gdbm-1.8.3/include
$ export C_INCLUDE_PATH
$ LIBRART_PATH=/opt/gdbm-1.8.3/lib
$ export LIBRART_PATH

可一次指定多个搜索路径,":"用于分隔它们,"."表示当前路径,如:
$ C_INCLUDE_PATH=.:/opt/gdbm-1.8.3/include:/net/include
$ LIBRARY_PATH=.:/opt/gdbm-1.8.3/lib:/net/lib
(可以添加多个路径,路径之间用:相隔,.代表当前目录,若.在最前头,也可省略)

当然,若想永久地添加这些路径,可以在.bash_profile中添加上述语句.

3, 还有一个比较猥琐的办法: 系统默认的包含路径不是/usr/include或/usr/local/include么? 我把自己的头文件拷贝到其中的一个目录, 不就可以了么? 的确可以这样, 如果你只想在你自己的机器上编译运行这个程序的话.

前面介绍了三种添加搜索路径的方法,如果这三种方法一起使用,优先级如何呢?
命令行设置 > 环境变量设置 > 系统默认

与外部库连接


前面介绍了如何包含头文件. 而头文件和库是息息相关的, 使用库时, 要在源代码中包含适当的头文件,这样才能声明库中函数的原型(发布库时, 就需要给出相应的头文件).

和包含路径一样, 系统也有默认的连接路径:
头文件,包含路径: /usr/local/include/  or  /usr/include/
库文件,连接路径: /usr/local/lib/          or  /usr/lib/  

同样地, 我们想要使用某个库里的函数, 必须将这个库连接到使用那些函数的程序中.

有一个例外: libc.a或libc.so (C标准库,它包含了ANSI C所定义的C函数)是不需要你显式连接的, 所有的C程序在运行时都会自动加载c标准库.

除了C标准库之外的库称之为"外部库", 它可能是别人提供给你的, 也可能是你自己创建的(后面有介绍如何创建库的内容).

外部库有两种:(1)静态连接库lib.a
                     (2)共享连接库lib.so

两者的共同点:
    .a, .so都是.o目标文件的集合,这些目标文件中含有一些函数的定义(机器码),而这些函数将在连接时会被最终的可执行文件用到。

两者的区别:
    静态库.a  : 当程序与静态库连接时,库中目标文件所含的所有将被程序使用的函数的机器码被copy到最终的可执行文件中. 静态库有个缺点: 占用磁盘和内存空间. 静态库会被添加到和它连接的每个程序中, 而且这些程序运行时, 都会被加载到内存中. 无形中又多消耗了更多的内存空间.

    共享库.so : 与共享库连接的可执行文件只包含它需要的函数的引用表,而不是所有的函数代码,只有在程序执行时, 那些需要的函数代码才被拷贝到内存中, 这样就使可执行文件比较小, 节省磁盘空间(更进一步,操作系统使用虚拟内存,使得一份共享库驻留在内存中被多个程序使用).共享库还有个优点: 若库本身被更新, 不需要重新编译与它连接的源程序。

静态库

下面我们来看一个简单的例子,计算2.0的平方根(假设文件名为sqrt.c):

#include <math.h>
#include <stdio.h>
int
main (void)
{
double x = sqrt (2.0);
printf ("The square root of 2.0 is %f\n", x);
return 0;
}

用gcc将它编译为可执行文件:
$ gcc -Wall sqrt.c -o sqrt
编译成功,没有任何警告或错误信息。执行结果也正确。
$ ./sqrt
The square root of 2.0 is 1.414214
    
下面我们来看看刚才使用的gcc版本:
$ gcc --version
  gcc (GCC) 4.0.2 20050808 (prerelease) (Ubuntu 4.0.1-4ubuntu9)

现在我用2.95版的gcc把sqrt.c再编译一次:
$ gcc-2.95 -Wall sqrt.c -o sqrt_2.95
  /tmp/ccVBJd2H.o: In function `main':
  sqrt.c:(.text+0x16): undefined reference to `sqrt'
     collect2: ld returned 1 exit status
    
编译器会给出上述错误信息,这是因为sqrt函数不能与外部数学库"libm.a"相连。sqrt函数没有在程序中定义,也不存在于默认C库 "libc.a"中,如果用gcc-2.95,应该显式地选择连接库。上述出错信息中的"/tmp/ccVBJd2H.o"是gcc创造的临时目标文件,用作连接时用。

使用下列的命令可以成功编译:
$ gcc-2.95 -Wall sqrt.c /usr/lib/libm.a -o sqrt_2.95
它告知gcc:在编译sqrt.c时,加入位于/usr/lib中的libm.a库(C数学库)。

C库文件默认位于/usr/lib, /usr/local/lib系统目录中; gcc默认地从/usr/local/lib, /usr/lib中搜索库文件。(在我的Ubuntu系统中,C库文件位于/urs/lib中。

这里还要注意连接顺序的问题,比如上述命令,如果我改成:
$ gcc-2.95 -Wall /usr/lib/libm.a sqrt.c -o sqrt_2.95
gcc会给出出错信息:
 /tmp/cc6b3bIa.o: In function `main':
 sqrt.c:(.text+0x16): undefined reference to `sqrt'
 collect2: ld returned 1 exit status

正如读取目标文件的顺序,gcc也在命令行中从左向右读取库文件――任何包含某函数定义的库文件必须位于调用该函数的目标文件之后!

指定库文件的绝对路径比较繁琐,有一种简化方法,相对于上述命令,可以用下面的命令来替代:
$ gcc-2.95 -Wall sqrt.c -lm -o sqrt_2.95
其中的"-l"表示与库文件连接,"m"代表"libm.a"中的m。一般而言,"-lNAME"选项会使gcc将目标文件与名为"libNAME.a"的库文件相连。(这里假设使用默认目录中的库,对于其他目录中的库文件,参考后面的"搜索路径"。)

上面所提到的"libm.a"就是静态库文件,所有静态库文件的扩展名都是.a!
$ whereis libm.a
  libm: /usr/lib/libm.a /usr/lib/libm.so

正如前面所说,默认的库文件位于/usr/lib/或/usr/local/lib/目录中。其中,libm.a是静态库文件,libm.so是后面会介绍的动态共享库文件。

如果调用的函数都包含在libc.a中(C标准库被包含在/usr/lib/libc.a中,它包含了ANSI C所定义的C函数)。那么没有必要显式指定libc.a:所有的C程序运行时都自动包含了C标准库!(试试 $ gcc-2.95 -Wall hello.c -o hello)。

共享库

正因为共享库的优点,如果系统中存在.so库,gcc默认使用共享库(在/usr/lib/目录中,库文件以共享和静态两种版本存在)。 

运行:$ gcc -Wall -L. hello.c -lNAME -o hello
gcc先检查是否有替代的libNAME.so库可用。   

正如前面所说,共享库以.so为扩展名(so == shared object)。

那么,如果不想用共享库,而只用静态库呢?可以加上 -static选项
$ gcc -Wall -static hello.c -lNAME -o hello
它等价于:
$ gcc -Wall hello.c libNAME.a -o hello

$ gcc-2.95 -Wall sqrt.c -static -lm -o sqrt_2.95_static
$ gcc-2.95 -Wall sqrt.c -lm -o sqrt_2.95_default
$ gcc-2.95 -Wall sqrt.c /usr/lib/libm.a -o sqrt_2.95_a
$ gcc-2.95 -Wall sqrt.c /usr/lib/libm.so -o sqrt_2.95_so

$ ls -l sqrt*
-rwxr-xr-x  1 zp zp  21076 2006-04-25 14:52 sqrt_2.95_a
-rwxr-xr-x  1 zp zp   7604 2006-04-25 14:52 sqrt_2.95_default
-rwxr-xr-x  1 zp zp   7604 2006-04-25 14:52 sqrt_2.95_so
-rwxr-xr-x  1 zp zp 487393 2006-04-25 14:52 sqrt_2.95_static

上述用四种方式编译sqrt.c,并比较了可执行文件的大小。奇怪的是,-static -lm 和 /lib/libm.a为什么有区别?有知其原因着,恳请指明,在此谢谢了! :)

如果libNAME.a在当前目录,应执行下面的命令:
$ gcc -Wall -L. hello.c -lNAME -o hello
-L.表示将当前目录加到连接路径。

利用GNU archiver创建库

$ ar cr libhello.a hello_fn.o by_fn.o
从hello_fn.o和by_fn.o创建libihello.a,其中cr表示:creat & replace
$ ar t libhello.a
列出libhello.a中的内容,t == table
(也可创建libhello.so)

关于创建库的详细介绍,可参考本blog的GNU binutils笔记


调试
一般地,可执行文件中是不包含任何对源代码的参考的,而debugger要工作,就要知道目标文件/可执行文件中的机器码对应的源代码的信息(如:哪条语句、函数名、变量名...). debugger工作原理:将函数名、变量名,对它们的引用,将所有这些对象对应的代码行号储存到目标文件或可执行文件的符号表中。

GCC提供-g选项,将调试信息加入到目标文件或可执行文件中。
$ gcc -Wall -g hello.c -o hello

注意:若发生了段错误,但没有core dump,是由于系统禁止core文件的生成!
$ ulimit -c  ,若显示为0,则系统禁止了core dump

解决方法:
$ ulimit -c unlimited  (只对当前shell进程有效)
或在~/.bashrc 的最后加入: ulimit -c unlimited (一劳永逸)

优化


GCC具有优化代码的功能,代码的优化是一项比较复杂的工作,它可归为:源代码级优化、速度与空间的权衡、执行代码的调度。

GCC提供了下列优化选项:
    -O0  : 默认不优化(若要生成调试信息,最好不优化)
    -O1  : 简单优化,不进行速度与空间的权衡优化;   
    -O2  : 进一步的优化,包括了调度。(若要优化,该选项最适合,它是GNU发布软件的默认优化级别;
    -O3  : 鸡肋,兴许使程序速度更慢;
    -funroll-loops  : 展开循环,会使可执行文件增大,而速度是否增加取决于特定环境;
    -Os  : 生成最小执行文件;

一般来说,调试时不优化,一般的优化选项用-O2(gcc允许-g与-O2联用,这也是GNU软件包发布的默认选项),embedded可以考虑-Os。

注意:此处为O!(非0或小写的o,-o是指定可执行文件名)。

检验优化结果的方法:$ time ./prog

time测量指定程序的执行时间,结果由三部分组成:
    real : 进程总的执行时间, 它和系统负载有关(包括了进程调度,切换的时间)
    user: 被测量进程中用户指令的执行时间
    sys  : 被测量进程中内核代用户指令执行的时间

user和sys的和被称为CPU时间.

注意:对代码的优化可能会引发警告信息,移出警告的办法不是关闭优化,而是调整代码。

 原文地址 http://blog.chinaunix.net/u/13991/showart_96714.html

Tuesday, March 18, 2008

3G视频点播系统中流媒体协议栈的实现

1、概述

  1.1 3G视频点播系统概述

  视频点播技术即VOD(Video On Demand),最初出现是缘于人们对广播电视的需求,但之前VOD一直局限于有线网络,从而无法给用户提供一个完全自主便捷的环境。在这样的情况下,无线移动视频点播业务的出现提供了一个可以和外界交流的平台。3G技术的成熟,更是为这项业务提供了一个可靠的实现基础,人们在任何时间,任何地点,只要通过一部3G手机,就可以像在家通过电视或电脑连接上有线网络一样的在整个网络环境中随意浏览任何自己感兴趣的节目,从而提供给人们一个交互式的主控权利,随机随时的获取网络资源。在3G视频点播系统中,手机客户端接受来自基站服务器发送的媒体数据,经过一系列的处理呈现给用户播放的图像,服务器接收客户端返回的质量报告进行分析,并根据网络的实际状况给出合适的传输方式以及合适的图像编码格式,进行流量控制。客户端完全是被动的数据处理,媒体数据解码,视频和音频的同步。而服务器则承担了大部分的网络质量状况监测任务。这个方式有点类似于HTTP方式下的客户机/服务器模式。而上述这种实时流媒体传输的运行架构则需要完善可靠的流媒体协议栈来支持。

  本文即介绍一个3G视频点播系统(如图1所示)中流媒体协议栈的实现,该系统由3部分组成:服务器,Internet和手持设备,针对该系统,本文首先介绍了实时流媒体协议栈的概念、特点及其发展背景,然后在此基础上讨论了实时流媒体协议栈在此3G视频点播系统中的软件架构,最后详细论述各个模块的设计及流媒体传输的关键环节――同步机制。

 

图1 系统概述图

  1.2 系统平台

  系统工作的硬件平台、软件平台如下:

  硬件平台:SH-mobile solution includes a SH3-DSP core MCU,memory,IO,and LCD etc,Abase-band controller (AT91 RM9200 Base-Band board),一台服务器(即PC机)。

  软件平台:SH-7300实时操作系统(Norti4),MPEG-4 audio/video encoder/decoder middleware,流媒体协议栈(如图2所示)。

 

图2 系统软件平台

2、基于3G终端的流媒体协议栈的架构

  2.1 流媒体协议栈及其特点

  以3G协议栈为基础的实时流媒体协议栈(real-time streaming media Protocols)具有强大的兼容性,能根据基站服务器通信准则建立最优播放效果,并根据网络状况,实时适应以改变通信策略和媒体播放效果。协议栈将可以保证以下业务:

  (1)进行视频通话,三方举行视频会议;

  (2)替代以电视为媒体的广告与节目播放,提供更具吸引力的多媒体点播等互动服务;

  (3)享受移动银行,股票信息,以及电子交易等各种信息服务。

  实时流媒体协议栈可以设计为一个与系统无关的模块,以实现在目前3种3G标准 WCDMA、cdma2000、 TD-SCDMA之上无缝移植和嵌入。我们以协议为指导,根据无线移动网络的实际情况,做出合适的裁减和改变。流传输控制机制将根据3G网络的特性和嵌入式实时系统的要求定制,使协议栈能够发挥可靠的,高效率的作用。同时协议栈不仅提供标准的应用程序接口,还可以根据客户的要求特别定制专用的应用程序接口。

  2.2 流媒体协议栈整体架构

  基于3G终端的流媒体协议栈由 RTSP协议栈,RTP/RTCP协议栈,TCP/IP协议栈组成。

  2.2.1 TCP/IP协议栈

  TCP/IP协议栈是由3G的协议栈提供,负责对流媒体数据的传送。TCP、UDP的协议都将使用到,并且根据不同的网络情况,分别使用。TCP是用于可靠的连接,RTSP协议将尽量使用这个协议进行传输,UDP是无连接的协议,RTP/RTCP协议栈将通过这个协议传送数据。当然这也不是绝对的,在必要的时候,RTSP可以使用UDP协议,比如防火墙的强制隔离,要求代理服务器转发,这时需要由协议栈来保障RTSP协议的可靠性,包括使用重发机制;RTP/RTCP也可以使用TCP连接,比如要求跨防火墙,建立直接连接的通道,这时可能牵涉到RTSP和RTP/RTCP协议的算法,需要由协议栈来提供。

  2.2.2 RTP/RTCP协议栈

  RTP/RTCP协议是流媒体协议栈中关键的一部分,它承担了媒体数据的传送,由2个相互紧凑的协议组成,数据报文实时传输使用的RTP协议和QoS监视的RTCP协议。协议设计者并不考虑RTP协议的纠错功能,而要求下层协议来保证,以提高媒体帧传输的数量,节省带宽,节省程序的开销,其传输机制专注于媒体本身的可靠性传输。RTP直接面向媒体数据,是一种以带宽和网络质量为先决条件的传输协议,其传输方式是随着带宽和网络质量变化而动态调整的协议,其宗旨是以最大的可能性利用网络的负载能力,确保大容量的多媒体数据能及时的传输。在这样的设计思想下,3G信道带宽不至于过度浪费,因此适合于手机终端的使用。同时,RTCP协议作为传输控制协议,也是网络质量的监测者,它为互动的双方提供了统计意义上的报告,为双方提供网络实际的质量,也为流量控制,编码方式,提供了可靠的保证和参考。作为独立于3G协议栈的应用层媒体协议栈,RTCP根据其机制,提供质量服务QoS,为网络运营商监视网络情况提供参数。

  2.2.3 RTSP协议栈

  RTSP协议栈是流媒体协议栈中与界面和RTP/RTCP协议相关的控制协商操作。RTSP提供响应界面操作的接口,直接响应界面发送的命令。同时RTSP也提供互联的双方或多方的一个传输方式和编码方式的协商操作,在网络允许情况下,建立一条最佳传输通道。以最匹配的情况传输数据,而无须每次传输都要求双方解析,节省了大量的时间,也减少了出错的可能性。

  RTSP和RTP/RTCP协议栈组成整个流媒体协议栈的核心部分,他们各自的控制机制是需要根据无线移动网络的实际情况和媒体编码格式统筹设计,在协议中是没有硬性规定的。

3、3G视频点播系统中流媒体协议栈的模块设计

  系统的模块化有利于整体功能的实现,本系统框架从流媒体协议栈进行规划,分为5个模块:人机界面、RTSP模块、RTP/RTCP模块,以及硬件媒体编解码器模块。模块架构如图3所示。

 

图3 视频点播系统架构

  3.1 人机界面(MMI)

  界面部分是手机终端提供给用户的交互界面。用户可以使用它来控制播放的动作,比如通过点击Web的链接,接入流媒体服务器。可以进行播放、暂停、终止、快进、后退等操作,当然,所有操作是在服务允许的范围,超出服务范围的操作将被禁止。通过界面,用户就可以享受到视频/音频的多媒体服务,可以点播电影,也可以召开会议。

  3.2 RTSP模块

  RTSP模块是以客户端为主的应用控制模块,以适应3G手机终端对媒体点播的需要。主要内容包括:RTSP协议栈的会话的建立、会话的传输、会话的协商和会话的终止,以及文本指令的解析。在流媒体协议栈架构中,RTSP处于TCP/IP层之上,使用TCP协议传输会话数据。处于界面控制程序直接操作下,为应用界面提供编程接口。同时RTSP对媒体数据层和RTP协议有着控制的权利,可以调整RTP会话参数,以及媒体层同步等等。RTSP是一个类似HTTP的服务器-客户端的模型,但与HTTP不同的是双方都可发送请求并都可以响应请求,是一个对等互动的协商协议。

  在此视频点播系统中,RTSP模块通过TCP协议的三次握手机制来保证命令消息通道的可靠性。一方面接收服务器的确认信息传给应用层进行处理;另一方面接收来自客户端的命令信息,解析后反馈给服务器。此外,RTSP模块支持以下操作:

  (1)从媒体服务器端获取媒体。客户端能够通过HTTP或者其它模式来请求一个的图像描述。如果图像正被多点传输,那么图像描述就包含了用来传输连续媒体多点传输的地址和端口。如果图像只能被单点传送,那客户端就要因为安全原因而提供给目的地址。

  (2)邀请媒体服务器参加会议。一个媒体服务器端能够被"邀请"参加一个存在的会议,可以回放媒体成为图像,或者记录图像中的媒体的全部或者一部分。

  (3)在存在图像中增加媒体。尤其对于活动图像,如果服务器能告诉客户端,增加的媒体是有用的。

  3.3 RTP/RTCP模块

  RTP/RTCP模块是以客户端为主的应用传输模块。主要内容包括:RTP/RTCP协议栈的会话的建立,会话的传输,会话的控制和会话的终止。此模块位于TCP/IP层之上,使用 UDP协议传输数据。当应用程序开始一个RTP会话时通常使用两个端口:一个给RTP,一个给RTCP。在RTP会话期间,各参与者周期性地传送RTCP包。RTCP包中含有已发送的数据包的数量、丢失的数据包的数量等统计资料,因此,服务器可以利用这些信息动态地改变传输速率,甚至改变有效载荷类型。RTP的媒体数据载荷加载准则依赖于不同的媒体编码格式而不同,数据报文的格式按照RFC规定的实现,如图4所示。

 

图4 RTP报文头格式

  同时应用程序可以通过此模块调整传输频率去和接受者的能力相匹配,或者以适应网络拥塞。通过参加多点传送组的适当的子集,接受者能适应不同的网络并控制他们的接受带宽。此外模块中所有的多媒体会话,都将视频和音频分别存放,这是为了保证在与某些不具有视频功能的终端通信或者在网络质量恶劣的情况下,可以只提供音频服务,而将视频服务关闭。

  3.4 硬件媒体编解码模块

  硬件媒体编解码器是基于MPEG-4实现的硬件Codec,采集的视频音频原始数据通过它压缩后形成MP4的数据格式,通过传输协议发往服务器;来自服务器的MP4视频音频数据通过Codec还原为原始数据,送往终端显示器,提供用户动态界面。当然画面可能因为压缩和传输的损伤有所下降,针对这样的情况,协议栈将提供纠错,补偿,同步功能来修复损伤,力图保持最完美的视频语音效果。

4、流媒体同步机制

  流媒体数据和传统数据的一个主要不同是不同媒体流的集成,主要表现为同步方式。在3G视频点播系统中,流媒体传输的同步机制是一个非常关键的问题,同步机制设计的好坏直接涉及到了播放效果,而播放效果则是直接面向用户,是检验媒体播放质量的直接证据。

  媒体同步定义是不同媒体流之间以及数据流内的基于时间的关系。目前有3层同步,分别是系统同步(流内同步)、媒体间同步(流间同步)和用户层同步(目标间同步)。

  媒体数据的同步丢失是由于从服务器发往客户端的媒体数据报文因为不同的路由路径导致,而且所有媒体数据的存储转发都将产生延迟和抖动。延迟以及延迟的可变性将导致以上3种同步的丢失。因此,媒体间同步机制是必须的,以确保在客户端正确的播放媒体数据。

  4.1 系统同步(流内同步)

  系统同步(流内同步)是底层同步。

  连续媒体或者时间相关的数据(比如,视频和音频)的媒体层同步是最底一层。媒体层的最小单位是逻辑数据单位(LDU),比如视频和音频帧,需要严格的按照时间顺序以确保用户可以精确的回放。系统同步缺失将导致播放暂停或跳跃。

  4.2 媒体间同步(流间同步)

  时间相关数据的流层同步是第二层。流层的最小单位是整个流。没有流间的同步将导致不同媒体数据的失调。

  网络的带宽是完成流媒体传输的物质基础,在传输声音、图像、视频等多媒体信息流时,即使这些媒体流予以压缩,所需的带宽仍然比文字文件大,但并不是有足够的带宽就可以完全解决流媒体传输问题。一般而言,所需带宽的多少是与应用密切相关的,从应用角度来看,只要用户数不断增加、信息服务量不断增加,带宽有多少都是不够的。同步是媒体流的基本控制方法。流媒体是时间属性的表现,这依赖于RTSP协议栈。

  4.3 用户层同步(目标间同步)

  这是多媒体文献中规定的最高层次的同步,是对象之间的同步,它集成了流和与时间无关的数据。对象间同步要求,如果某个以前定义的与时间相关的媒体目标到达客户端,那么在一个允许的时间间隔内,必须开始与之相对应的与时间无关的数据,同时停止与之不匹配的当前的与时间无关的数据。

  用户层同步或交互同步,是最上层的同步,要求能反映和满足用户的交互性,容易为用户理解接受。用户层同步是交互性参与的同步,用户可以控制和使用信息,如反复调用感兴趣的内容、快速掠过不感兴趣的部分。虽然RTSP协议支持类似录像机的功能:播放、快进、暂停、停止,但流媒体的交互性同步能力主要体现在数据流编码过程中对交互性能的考虑。

5、小结与展望

  近年来,无线移动通信技术的发展日新月异,日趋成熟的第三代移动通信不仅能够提供现有的各种移动电话业务,还能提供高速率的宽带视频业务,支持高质量的话音、分组数据业务,以及实时的视频传输。3G技术融合了无线通信与互联网、视频等技术,由此产生的无线视频和无线IP业务也将成为未来无线移动通信业务新的增长点,而流媒体技术正是因为3G提供的可靠平台成为其应用之一,本文所介绍的视频点播系统就是流媒体协议栈基于无线移动通信技术的典型应用方案。而今,功能日渐增强的流媒体协议栈以其优势已经应用于各个领域,尤其在嵌入式领域与无线Web浏览技术相融合,并将一定程度上推动3G手机的发展。

Tuesday, March 04, 2008

用gdb查看内存

用gdb查看内存

格式: x /nfu <addr>

说明
x 是 examine 的缩写

n表示要显示的内存单元的个数

f表示显示方式, 可取如下值
x 按十六进制格式显示变量。
d 按十进制格式显示变量。
u 按十进制格式显示无符号整型。
o 按八进制格式显示变量。
t 按二进制格式显示变量。
a 按十六进制格式显示变量。
i 指令地址格式
c 按字符格式显示变量。
f 按浮点数格式显示变量。

u表示一个地址单元的长度
b表示单字节,
h表示双字节,
w表示四字节,
g表示八字节


Format letters are o(octal), x(hex), d(decimal), u(unsigned decimal),
t(binary), f(float), a(address), i(instruction), c(char) and s(string).
Size letters are b(byte), h(halfword), w(word), g(giant, 8 bytes)

举例
x/3uh buf
表示从内存地址buf读取内容,
h表示以双字节为一个单位,
3表示三个单位,
u表示按十六进制显示