学习改造源码
0 背景介绍
FFmpeg is a name of a free software project for the multimedia handling licensed under GNU General Public License. The most popular part of the project is ffmpeg command line tool for video and audio encoding/decoding and its main features are the high speed, quality of output and small file sizes. "FF" from FFmpeg means Fast Forward - a control button on media players, and "mpeg" is an abbreviation of the Moving Pictures Experts Group. The FFmpeg logo contains a zig-zag pattern, that is characteristic for the entropy coding scheme illustrated with 8x8 block in the picture.
调用ffmpeg可以非常容易合并左右声道的音频为立体声比如
ffmpeg -i 1.wav -i 2.wav -filter_complex "amovie=1.wav [l]; amovie=2.wav [r]; [l] [r] amerge" 1_2.mp3
具体细节请参考博文http://www.cnblogs.com/hdu-2010/p/5791097.html。但是每次调用命令都会频繁切换进程,涉及OS的调度。在数据量很大,且实时性要求很高的时候,难免会浪费不必要的时间。所以下面介绍怎么改造ffmpeg的源码,使得ffmpeg常住内存,并转换我们的待合并文件。
1 ffmpeg源码
ffmpeg-3.1.2的源码解压后的目录结构如图所示
ffmpeg.c中main函数如下,当输入命令行的时候,下面的main函数开始执行诸如,初始化,参数解析,合并转换,内存释放等步骤。本文的重点就是改造下面的main函数。
1 int main(int argc, char **argv) //main 2 { 3 int ret; 4 int64_t ti; 5 6 init_dynload(); 7 8 register_exit(ffmpeg_cleanup); 9 10 setvbuf(stderr,NULL,_IONBF,0); /* win32 runtime needs this */ 11 12 av_log_set_flags(AV_LOG_SKIP_REPEATED); 13 parse_loglevel(argc, argv, options); 14 15 if(argc>1 && !strcmp(argv[1], "-d")){ 16 run_as_daemon=1; 17 av_log_set_callback(log_callback_null); 18 argc--; 19 argv++; 20 } 21 22 avcodec_register_all(); 23 #if CONFIG_AVDEVICE 24 avdevice_register_all(); 25 #endif 26 avfilter_register_all(); 27 av_register_all(); 28 avformat_network_init(); 29 30 show_banner(argc, argv, options); 31 32 term_init(); 33 34 /* parse options and open all input/output files */ 35 ret = ffmpeg_parse_options(argc, argv); 36 if (ret < 0) 37 exit_program(1); 38 39 if (nb_output_files <= 0 && nb_input_files == 0) { 40 show_usage(); 41 av_log(NULL, AV_LOG_WARNING, "Use -h to get full help or, even better, run 'man %s'\n", program_name); 42 exit_program(1); 43 } 44 45 /* file converter / grab */ 46 if (nb_output_files <= 0) { 47 av_log(NULL, AV_LOG_FATAL, "At least one output file must be specified\n"); 48 exit_program(1); 49 } 50 51 // if (nb_input_files == 0) { 52 // av_log(NULL, AV_LOG_FATAL, "At least one input file must be specified\n"); 53 // exit_program(1); 54 // } 55 56 current_time = ti = getutime(); 57 if (transcode() < 0) 58 exit_program(1); 59 ti = getutime() - ti; 60 if (do_benchmark) { 61 av_log(NULL, AV_LOG_INFO, "bench: utime=%0.3fs\n", ti / 1000000.0); 62 } 63 av_log(NULL, AV_LOG_DEBUG, "%"PRIu64" frames successfully decoded, %"PRIu64" decoding errors\n", 64 decode_error_stat[0], decode_error_stat[1]); 65 if ((decode_error_stat[0] + decode_error_stat[1]) * max_error_rate < decode_error_stat[1]) 66 exit_program(69); 67 68 exit_program(received_nb_signals ? 255 : main_return_code); 69 return main_return_code; 70 }
2 编译
为了快速调试代码,需要有如下几点的准备工作。下面的工作是在LINUX Red Hat Enterprise Linux Server release 5.4 (Tikanga) 版本中测试的。
1 在Linux上安装ffmpeg,具体安装步骤可参看篇首提供的博文。 2 在安装ffmpeg源码包中,复制在执行./configure时生成的config.h文件,还有(cmdutils.h ,ffmpeg.h) 2个。
3 复制在编译阶段生成的.o 文件。主要是(ffmpeg_opt.o cmdutils.o ffmpeg_filter.o)3个。
4 新建工程目录new_ffmpeg, 把上述复制的文件放入其中,外加ffmpeg.c。此时,一共有7个文件,但是在编译之前需要对ffmpeg.c做些改变,否则编译通不过。
5 编译
6 链接
7 测试
如果需要在windows下面测试,可参考已故音频专家雷霄骅的博客,里面有许多有价值的可debug的工程文件。
3 测试
为了常驻内存
1 就需要对main函数中的exit_program函数改造,该函数 在register_exit(ffmpeg_cleanup)步骤, 这里是调用了ffmpeg_cleanup函数。删除exit_program,用ffmpeg_cleanup函数代替。 2 改造ffmpeg.c中的全局变量,防止转换出错。
4 静态编译和动态编译
1 //hello.h 2 3 #ifndef __TEST_LIB__ 4 #define __TEST_LIB__ 5 6 void hello(const char *name); 7 #endif 8 9 //hello.c 10 11 #include <stdio.h> 12 13 void hello(const char *name) 14 { 15 printf("hello %s\n", name); 16 } 17 18 //main.c 19 20 #include "hello.h" 21 22 int main() 23 { 24 hello("everyone"); 25 return 0; 26 }
4.1 静态编译
0 原始文件 hello.c hello.h main.c
1 $ gcc -c hello.c #生成.o文件 2 $ ar cr libmyhello.a hello.o #生成静态文件libmyhello.a,注意库文件的命名方式 3 $ gcc -o hello main.c -L. -lmyhello #连接库文件,生成可执行的静态文件 hello 4 $ ./hello #测试 hello everyone
4.2 动态编译
0 原始文件 hello.c hello.h main.c 1 $gcc -c -fPIC hello.c #生成共享的.o文件 2 $gcc -shared -fPIC -o libmyhello.so hello.o #生成动态库libmyhello.so 3 $gcc -o hello main.c -L. -lmyhello #链接动态库 4 $./hello #测试 ./hello: error while loading shared libraries: libmyhello.so: cannot open shared object file: No such file or directory 只需把当前目录动态库libmyhello.so放入 /usr/local/lib下即可。 或者在第3步指定链接器的路径,即 3 $gcc -o hello main.c -L. -lmyhello -Wl,-rpath=. #指定链接目录 4 $./hello #测试 hello everyone
参考资料
1 ffmpeg下载 http://ffmpeg.org/releases/ffmpeg-3.1.2.tar.bz2
2 雷霄骅(leixiaohua1020)的专栏 ffmpeg.c函数结构简单分析(画图) http://blog.csdn.net/leixiaohua1020/article/details/39760711
3 [总结]FFMPEG视音频编解码零基础学习方法 http://blog.csdn.net/leixiaohua1020/article/details/15811977/
4 linux动态编译和静态编译 http://blog.csdn.net/l_yangliu/article/details/6951005
5 java amr格式转mp3格式(完美解决Linux下转换0K问题)
6 ffmpeg+ffserver多媒体服务器开发入门 http://blog.csdn.net/rootusers/article/details/49822499