ffmpeg的代码简要

各人用途不同,详细程度不同,渐渐扩充吧。
【博客只是做个记录,技术性不强,能给游客提供帮助,最好不过。错误或模棱两可的地方,还望不吝斧正。——写在前面】

1. 关于转码【F4V(the same container as MP4/3GP)转到 FLV】

首先,安装ffmpeg-0.11.1就不说了,编译时需要的第三方库也不说了,一搜一大堆,不过我写的这些,也可能是一大堆,尽量写的与他人不同吧,并扼要。

转码代码分析:ffmpeg.c中main函数就是框架了,略过前头的初始化工作,(注意,调试的ffmpeg的用  gdb ffmpeg_g)

先看parse_options这个函数,看/* parse options */部分,这里需要注意该选项数组,static const OptionDef options[],解析输入命令的时候靠它。比如,设置输入文件的命令 -i **.avi 会进入(ffmpeg.c)opt_input_file 函数。很容易看到,这里面会分析媒体文件,多个input文件则多次调用,用 grow_array 函数来增长数组 static InputFile   **input_files。

Demux等过程中,ffmpeg会用av_dict_set,字典统计metadata信息,并print出来,这对我们跟代码,关注一些参数比较有用。


然后再看转码部分主要的函数 transcode,起先会走到 transcode_init 函数,略过注释比较明确的,对每个输出流计算编码参数时,循环 for (i = 0; i < nb_output_streams; i++) 中,由参数 ost->stream_copy 分成两个分支,这跟参数 -acodec copy / -vcodec copy 有关。过了编码参数确认,就是打开编码器了,初始化输入流,丢弃没用到的AVProgram(结构中,有metadata成员,猜想是对应不同的流及脚本信息等),打开输出文件并写入文件头,根据注释,里头功能模块分的很清晰。接着是,格式参数的转储,dump_format标记后面会打印出很多信息,源与目标的。

其中get_input_stream函数是,入出文件一一对应的话,这里利用输出流的index可以从input_stream[]中直接取到与之对应的输入流(audio/video/subtitle and so on),另外也有将多个单通道流mux为文件,应该对应filter吧(猜的,暂未看)。

看看avformat_write_header函数,这里面写入的很多metadata信息,在此前av_dump_format中打印出来,这里可以供我们check一下。里面就不多说了,最终会调到AVOutputFormat的write_header,进入具体的容器的头信息构造。

然后看看transcode中,output_packet函数,略去前面特殊的时戳计算、EOF处理,后面由ist->decoding_needed引出了两条分支。先看第二条,处理直接流拷贝的,它先修正时戳(对应于媒体文件里的单个数据包),紧接着for循环:check_output_constraints检查时序(audio、video按时戳排序)。

再来看do_streamcopy函数:数据有效性检测、流的packet数目统计,pts dts duration_per_packet flags_of_key_frame,。

看看write_frame函数,video_sync_method有几种模式(VSYNC_AUTO VSYNC_PASSTHROUGH VSYNC_PASSTHROUGH VSYNC_VFR VSYNC_DROP),比如当前(video_sync_method == VSYNC_AUTO , audio_sync_method == VSYNC_PASSTHROUGH )。跳跃。

看看av_interleaved_write_frame函数,里面 interleave_packet 和 write_packet。


2. 有一个需求,需要F4V(H264)-->FLV(VP6),需要从服务器截流并转码,用ffmpeg会很方便。不过呢,看了下configure文件,发现ffmpeg不支持VP6的encoder,相关的只有libvpx选项,基本支持VP8,这解决不了问题。决定,找开源VP6的codec,集成进去,下面做一些循序渐进的记录。

3. ffmpeg中如何添加一个codec?比如encoder:(正在进行。总结相关注意点。)

         首先,此次是为了扩展vp6 encoder,没有找到官方源码,就参考的mencoder/mplayer,其中有个封装的动态库vp6vfw.dll,这是很容易找到的,其中要注意它编码的输入流是RGB,有个config.h头文件中定义了所有的需要的宏等,可以在一个成功编译并安装的mplayer中拷贝过来,注意版本对应。

         测试demo时,简易makefile如下(其中含从mplayer中抽出的文件):

#---------------------------------------------------------------------------------------------------------#

#headers
vpath %.h libmpcodecs\
                     osdep\
                     loader\
                     loader/dmo\
                     loader/dshow\
                     loader/qtx\
                     loader/qtx/qtxsdk\
                     loader/wine

vpath %.c \
                    osdep\
                    libavutil\
                    libmpcodecs\
                    loader\
                    loader/dmo\
                    loader/dshow\
                    loader/qtx
                    
         #compilingoptions
         DEFS += -D__FALLENINK__

#-w 忽略所有警告-Wall 显示所有警告-Werror警告当成错误处理
FLAGS += -w -lpthread -lm -g

#tools
CC = gcc

#defines
OBJECTS = demo_vp6enc.o\
                       vp6enc.o\
                       ve_vfw.o\
                       vfl.o\
                       drv.o\
                       win32.o\
                       module.o\
                       ext.o\
                       ldt_keeper.o\
                       cpudetect.o\
                       mmap_anon.o\
                       resource.o\
                       registry.o\
                       avstring.o\
                       path.o\
                       pe_image.o\
                       pe_resource.o\
                       mem.o

main : $(OBJECTS)
         $(CC)$(FLAGS) -o main $(OBJECTS)
        
$(OBJECTS) : %.o : %.c
         $(CC)$(DEFS) $(FLAGS) -c $< -o $@

.PHONY : clean
clean :
         -rm main$(OBJECTS)

#---------------------------------------------------------------------------------------------------------#


         然后看看,ffmpeg中需要做的。

可参考的地方有:

http://wiki.multimedia.cx/index.php?title=On2_VP6

http://blog.csdn.net/phenixzhong/article/details/7375838

代码相关

在libavcodec/avcodec.h中,enum CodecID类型中添加对应枚举值(命名要尽量符合ffmpeg的风格),如:CODEC_ID_VP6VFW = 0x0ffff。

 

在libavcodec/allcodecs.c的avcodec_register_all()函数中添加vp6vfw解码器注册,比如:

REGISTER_ENCODER (VP6VFW, vp6vfw);

 

在libavformat/xxx.c文件中,这里比如,添加对于可包含vp6数据的文件格式对vp6vfw的识别,如static const AVCodecTag flv_video_codec_ids[];flv_write_packet中也需要添加;(这里修改各异)

 

创建vp6vfwenc.c源文件并实现必要的函数,且需要定义一个全局AVCodec变量,比如:

AVCodecff_vp6vfw_encoder = {

    .name           = "vp6vfw",

    .type           = AVMEDIA_TYPE_VIDEO,

    .id             = CODEC_ID_VP6VFW,

    .priv_data_size = 0,//sizeof(VP56Context),

    .init           = __born,

    .close          = __die,

    .decode         = __working,

    .capabilities   = 0,

    .pix_fmts       = (const enum PixelFormat[]){PIX_FMT_RGB24, PIX_FMT_NONE },   

.long_name      = NULL_IF_CONFIG_SMALL("On2 VP6(Fallenink version, implemented by vp6vfw.dll)"),

};

 

执行的输入选项,在ffmpeg.c中static const OptionDef options[]数组的"vcodec"对应着opt_video_codec函数。


编译相关

Configure

test_deps _encoder _decoder????

添加了:    vp6vfw                                                                                                    \

 

ENCODER_LIST=$(find_things encoder  ENC      libavcodec/allcodecs.c)?????

echo "Creating config.mak and config.h..."????

 

Libavcodec下的makefile里:

添加一行,如:OBJS-$(CONFIG_VP6VFW_ENCODER)          += vp6vfwenc.o

 

如有子目录,则参考其他子目录的makefile写法。


4. 结构性的记录
文件/文件协议,io,url的层次
【file format】【io】【protocol】
文件上组织如: flvenc.c|aviobuf.c|avio.c|rtmproto.c,这么说只是帮助自己看清楚关系,没什么高明的。另外,多层协议栈,是在avio.c中复用的,如:
typedef struct URLContext {
    const AVClass *av_class;    /**< information for av_log(). Set by url_open(). */
    struct URLProtocol *prot;
……
int ffurl_read(URLContext *h, unsigned char *buf, int size)
{
    if (!(h->flags & AVIO_FLAG_READ))
        return AVERROR(EIO);
    return retry_transfer_wrapper(h, buf, size, 1, h->prot->url_read);
}
……

5. 【小问题】【今天不小心在libavutil/log.c中定制自己的log输出,为了有外面传参,则定义了俩全局函数,如create_log_file(char *url),结果链接居然出错。由于linux某些知识缺乏,找了半天,过程如下】

开始废了很大力气去猜测:
是没编译进去?胡乱猜的,objdump出来,这猜测纯属胡闹。
是函数名被修改了?也是胡闹。
是被置为static了?有点靠谱,接着看。

先发现了libavutil.v文件,内容:【 相当于linux 的导出文件
LIBAVUTIL_$MAJOR {
        global: av_*; ff_*; avutil_*;
        local: *;
};
很明显,这个意思看上去,的确,只有上面三种前缀的才被设置为全局函数,其他为本地的(在当前库中可以互相链接,并非当前文件),没有直接去看ld链接器,而是去看这些文件啥时候用到了呃。
在configure之后,也在config.mak文件了,可以看到:
SHFLAGS=-shared -Wl,-soname,$$(@F) -Wl,-Bsymbolic -Wl,--version-script,$(SUBDIR)lib$(NAME).ver

很明显了。接下来就找到下面一段: http://blog.csdn.net/hh2000/article/details/3201716

gcc 编译动态库,默认将所有函数都导出。

使用version-script,仅仅导出要使用的符号表

$gcc -o mylib.so --version-script a.map  a.o b.o


global: 导出函数名; 
local: *;
};

再在 ld 时用 --version-script  选项来 load 你 文件。都完事后再使用 readelf 观察static 与 dynamic section 发现只导出了你指定的函数名即符号。

1,only meaningful for ELF platforms that support shared libraries

2,could improve reduce the img size.

3,could improve the speed of load library, if library havn't been prelinked.

另外可以参考:http://login.sina.com.cn/sso/login.php?useticket=0&returntype=META&service=blog&gateway=1&url=http://blog.sina.com.cn/s/blog_493667730100csde.html


……待补

你可能感兴趣的:(ffmpeg的代码简要)