看完了 av_log_set_flags(AV_LOG_SKIP_REPEATED); parse_loglevel(argc, argv, options); 这两个函数接下来就是一系列的注册函数
- 该函数在allcodecs.c,allcodecs.c在libavcodec包中
该函数的注释为:
/**
* Register all the codecs, parsers and bitstream filters which were enabled at
* configuration time. If you do not call this function you can select exactly
* which formats you want to support, by using the individual registration
* functions.
*
* @see avcodec_register
* @see av_register_codec_parser
* @see av_register_bitstream_filter
*/
注意:下面的内容参考了
翻译过来即是:
注册所有的编解码器、参数以及比特流滤波器,这些都是在配置阶段就启用了;如果你不想调用这个函数,你可以准确的悬着你想要支持的格式,当然这 得通过你自己的注册函数;各位,这就是说在我们实际应用的时候,没必要非得把所有的编解码器格式都注册一遍,可以选择自己能用到的,其他的,嘿 嘿,就让他们玩去吧
要看懂这个函数的代码部分需要明白复杂的宏定义知识
avcodec_register_all() 函数主要调用三个函数来完成编解码器、参数以及比特流滤波器的注册。这三个函数是:
音频视频字幕编解码器的注册
编解码器解析器的注册
数据流的滤波器的注册
注册流程是:
(1)avcodec_register_all() 函数调用宏定义
(2)宏定义调用具体的注册函数完成注册,就是指上面的三个函数
下面具体分析一下某些格式的注册问题,例如 FFMPEG 和 H264 的注册:
注意:FFMPEG 本身含有 H264 的解码器,但是不含有编码器,只是带有编码器的接口信息,所以想生成 h264 格式的视频格式,必须在编译 FFMPEG 时将 libx264 编译进去
a. 注册硬件加速
REGISTER_HWACCEL 的宏定义为:
- REGISTER_HWACCEL(H264_DXVA2, h264_dxva2);
- REGISTER_HWACCEL(H264_VAAPI, h264_vaapi);
- REGISTER_HWACCEL(H264_VDA, h264_vda);
- #define REGISTER_HWACCEL(X, x) {\extern AVHWAccel ff_##x##_hwaccel;\
- if (CONFIG_##X##_HWACCEL) av_register_hwaccel( & ff_##x##_hwaccel);
- }
b. 注册编解码器
- REGISTER_DECODER(H264, h264);
- REGISTER_DECODER(H264_VDPAU, h264_vdpau);
REGISTER_DECODER 的宏定义为:
- #define REGISTER_DECODER(X, x) {\extern AVCodec ff_##x##_decoder;\
/***********************************************************************************/
这里为了我的某个需求我需要看看
REGISTER_ENCDEC (PNG, png);
根据这样的宏定义
- #define REGISTER_ENCODER(X, x) {\extern AVCodec ff_##x##_encoder;\
- if (CONFIG_##X##_ENCODER) avcodec_register( & ff_##x##_encoder);
- }#define REGISTER_DECODER(X, x) {\extern AVCodec ff_##x##_decoder;\
- if (CONFIG_##X##_DECODER) avcodec_register( & ff_##x##_decoder);
- }#define REGISTER_ENCDEC(X, x) REGISTER_ENCODER(X, x);
- REGISTER_DECODER(X, x)
替换后:
- extern AVCodec ff_png_encoder;
- if (CONFIG_PNG_ENCODER) avcodec_register( & ff_png_encoder);
- extern AVCodec ff_png_decoder;
- if (CONFIG_PNG_ENCODER) avcodec_register( & ff_png_decoder);
既然 ff_png_encoder 声明时前面加了一个 extern 关键字,表明这个变量在其它文件中声明定义了,那么我们去找找看看它在哪里。 ff_png_encoder 即 png 的编码器是个什么东东?
经过千心万苦的查询才找到原来这个东西在 pngenc.c 源文件中,当然这个源文件在 libavcodec 包中,当然属于 ffmpeg 本身!
感想和教训:其实我很多次都经过这里,可是看到上面那个复杂不已的宏定义就绕道而走,于是一次次的绕开了真相,今天当有了宏的完整知识后将其展开 ,我艹!原来我要的东西就在这里!所以 "最远的距离就是天涯海角而是你就在眼前而我却不识货!!!艹" !所以真的需要相应的扎实的 C 语言知识后在来看看 ffmpeg 的源码!当然这句话对自己说的。
- AVCodec ff_png_encoder = {.name = "png",
- //名字
- .type = AVMEDIA_TYPE_VIDEO,
- //编码器的数据类型
- .id = CODEC_ID_PNG,
- //编码器的ID号
- .priv_data_size = sizeof(PNGEncContext),
- //数据的大小
- .init = png_enc_init,
- //编码器的初始化 一个函数指针
- .encode = encode_frame,
- //这个看看一看,也没注释,要深入进去看了给出答案
- .pix_fmts = (const enum PixelFormat[]) {
- PIX_FMT_RGB24,
- PIX_FMT_RGB32,
- PIX_FMT_PAL8,
- PIX_FMT_GRAY8,
- PIX_FMT_MONOBLACK,
- PIX_FMT_NONE
- },
- //数组支持的像素格式,或NULL如果未知,数组终止1
- .long_name = NULL_IF_CONFIG_SMALL("PNG image"),
- // 这个也需要看看
- };
为了更好的阅读上面的代码,我们先看看 AVCodec 这个结构体
刚开始我还写了对个数据结构的解释,后来看了一位更牛逼的人物写的解释,索性一个链接
就不在这里卖丑了。
由于篇幅的原因 AVFrame 应该具体的看看 , 帧数据结构是一个重要的数据结构下面是关于这个数据结构的注释:
这里有个链接详细的解释了这个数据结构的这位大神级人物也相当了得。
/**
* Audio Video Frame.
* New fields can be added to the end of AVFRAME with minor version
* bumps. Removal, reordering and changes to existing fields require
* a major version bump.
* sizeof(AVFrame) must not be used outside libav*.
a. png_enc_init 函数 在 pngenc.c 源文件中
- static av_cold int png_enc_init(AVCodecContext * avctx) {
- PNGEncContext * s = avctx - >priv_data;
- avcodec_get_frame_defaults( & s - >picture);
- avctx - >coded_frame = &s - >picture;
- dsputil_init( & s - >dsp, avctx);
- s - >filter_type = av_clip(avctx - >prediction_method, PNG_FILTER_VALUE_NONE, PNG_FILTER_VALUE_MIXED);
- if (avctx - >pix_fmt == PIX_FMT_MONOBLACK) s - >filter_type = PNG_FILTER_VALUE_NONE;
- return 0;
- }
b.encode_frame 在 pngenc.c 源文件中
上面两个函数前面都加了个 static 也就是说在本源文件有效,目前阶段不需要去深入的研究是怎么实现的,只知道有这么一回事,等编码阶段在回国头来看看。
- static int encode_frame(AVCodecContext * avctx, unsigned char * buf, int buf_size, void * data) {
- PNGEncContext * s = avctx - >priv_data;
- AVFrame * pict = data;
- AVFrame * const p = &s - >picture;
- int bit_depth,
- color_type,
- y,
- len,
- row_size,
- ret,
- is_progressive;
- int bits_per_pixel,
- pass_row_size;
- int compression_level;
- uint8_t * ptr,
- *top;
- uint8_t * crow_base = NULL,
- *crow_buf,
- *crow;
- uint8_t * progressive_buf = NULL;
- uint8_t * rgba_buf = NULL;
- uint8_t * top_buf = NULL; * p = *pict;
- p - >pict_type = AV_PICTURE_TYPE_I;
- p - >key_frame = 1;
- s - >bytestream_start = s - >bytestream = buf;
- s - >bytestream_end = buf + buf_size;
- is_progressive = !!(avctx - >flags & CODEC_FLAG_INTERLACED_DCT);
- switch (avctx - >pix_fmt) {
- case PIX_FMT_RGB32:
- bit_depth = 8;
- color_type = PNG_COLOR_TYPE_RGB_ALPHA;
- break;
- case PIX_FMT_RGB24:
- bit_depth = 8;
- color_type = PNG_COLOR_TYPE_RGB;
- break;
- case PIX_FMT_GRAY8:
- bit_depth = 8;
- color_type = PNG_COLOR_TYPE_GRAY;
- break;
- case PIX_FMT_MONOBLACK:
- bit_depth = 1;
- color_type = PNG_COLOR_TYPE_GRAY;
- break;
- case PIX_FMT_PAL8:
- bit_depth = 8;
- color_type = PNG_COLOR_TYPE_PALETTE;
- break;
- default:
- return - 1;
- }
- bits_per_pixel = ff_png_get_nb_channels(color_type) * bit_depth;
- row_size = (avctx - >width * bits_per_pixel + 7) >> 3;
- s - >zstream.zalloc = ff_png_zalloc;
- s - >zstream.zfree = ff_png_zfree;
- s - >zstream.opaque = NULL;
- compression_level = avctx - >compression_level == FF_COMPRESSION_DEFAULT ? Z_DEFAULT_COMPRESSION: av_clip(avctx - >compression_level, 0, 9);
- ret = deflateInit2( & s - >zstream, compression_level, Z_DEFLATED, 15, 8, Z_DEFAULT_STRATEGY);
- if (ret != Z_OK) return - 1;
- crow_base = av_malloc((row_size + 32) << (s - >filter_type == PNG_FILTER_VALUE_MIXED));
- if (!crow_base) goto fail;
- crow_buf = crow_base + 15; // pixel data should be aligned, but there's a control byte before it
- if (is_progressive) {
- progressive_buf = av_malloc(row_size + 1);
- if (!progressive_buf) goto fail;
- }
- if (color_type == PNG_COLOR_TYPE_RGB_ALPHA) {
- rgba_buf = av_malloc(row_size + 1);
- if (!rgba_buf) goto fail;
- }
- if (is_progressive || color_type == PNG_COLOR_TYPE_RGB_ALPHA) {
- top_buf = av_malloc(row_size + 1);
- if (!top_buf) goto fail;
- }
- /* write png header */
- memcpy(s - >bytestream, ff_pngsig, 8);
- s - >bytestream += 8;
- AV_WB32(s - >buf, avctx - >width);
- AV_WB32(s - >buf + 4, avctx - >height);
- s - >buf[8] = bit_depth;
- s - >buf[9] = color_type;
- s - >buf[10] = 0;
- /* compression type */
- s - >buf[11] = 0;
- /* filter type */
- s - >buf[12] = is_progressive;
- /* interlace type */
- png_write_chunk( & s - >bytestream, MKTAG('I', 'H', 'D', 'R'), s - >buf, 13);
- /* put the palette if needed */
- if (color_type == PNG_COLOR_TYPE_PALETTE) {
- int has_alpha,
- alpha,
- i;
- unsigned int v;
- uint32_t * palette;
- uint8_t * alpha_ptr;
- palette = (uint32_t * ) p - >data[1];
- ptr = s - >buf;
- alpha_ptr = s - >buf + 256 * 3;
- has_alpha = 0;
- for (i = 0; i < 256; i++) {
- v = palette[i];
- alpha = v >> 24;
- if (alpha && alpha != 0xff) has_alpha = 1; * alpha_ptr++=alpha;
- bytestream_put_be24( & ptr, v);
- }
- png_write_chunk( & s - >bytestream, MKTAG('P', 'L', 'T', 'E'), s - >buf, 256 * 3);
- if (has_alpha) {
- png_write_chunk( & s - >bytestream, MKTAG('t', 'R', 'N', 'S'), s - >buf + 256 * 3, 256);
- }
- }
- /* now put each row */
- s - >zstream.avail_out = IOBUF_SIZE;
- s - >zstream.next_out = s - >buf;
- if (is_progressive) {
- int pass;
- for (pass = 0; pass < NB_PASSES; pass++) {
- /* NOTE: a pass is completely omited if no pixels would be
- output */
- pass_row_size = ff_png_pass_row_size(pass, bits_per_pixel, avctx - >width);
- if (pass_row_size > 0) {
- top = NULL;
- for (y = 0; y < avctx - >height; y++) {
- if ((ff_png_pass_ymask[pass] << (y & 7)) & 0x80) {
- ptr = p - >data[0] + y * p - >linesize[0];
- FFSWAP(uint8_t * , progressive_buf, top_buf);
- if (color_type == PNG_COLOR_TYPE_RGB_ALPHA) {
- convert_from_rgb32(rgba_buf, ptr, avctx - >width);
- ptr = rgba_buf;
- }
- png_get_interlaced_row(progressive_buf, pass_row_size, bits_per_pixel, pass, ptr, avctx - >width);
- crow = png_choose_filter(s, crow_buf, progressive_buf, top, pass_row_size, bits_per_pixel >> 3);
- png_write_row(s, crow, pass_row_size + 1);
- top = progressive_buf;
- }
- }
- }
- }
- } else {
- top = NULL;
- for (y = 0; y < avctx - >height; y++) {
- ptr = p - >data[0] + y * p - >linesize[0];
- if (color_type == PNG_COLOR_TYPE_RGB_ALPHA) {
- FFSWAP(uint8_t * , rgba_buf, top_buf);
- convert_from_rgb32(rgba_buf, ptr, avctx - >width);
- ptr = rgba_buf;
- }
- crow = png_choose_filter(s, crow_buf, ptr, top, row_size, bits_per_pixel >> 3);
- png_write_row(s, crow, row_size + 1);
- top = ptr;
- }
- }
- /* compress last bytes */
- for (;;) {
- ret = deflate( & s - >zstream, Z_FINISH);
- if (ret == Z_OK || ret == Z_STREAM_END) {
- len = IOBUF_SIZE - s - >zstream.avail_out;
- if (len > 0 && s - >bytestream_end - s - >bytestream > len + 100) {
- png_write_chunk( & s - >bytestream, MKTAG('I', 'D', 'A', 'T'), s - >buf, len);
- }
- s - >zstream.avail_out = IOBUF_SIZE;
- s - >zstream.next_out = s - >buf;
- if (ret == Z_STREAM_END) break;
- } else {
- goto fail;
- }
- }
- png_write_chunk( & s - >bytestream, MKTAG('I', 'E', 'N', 'D'), NULL, 0);
- ret = s - >bytestream - s - >bytestream_start;
- the_end: av_free(crow_base);
- av_free(progressive_buf);
- av_free(rgba_buf);
- av_free(top_buf);
- deflateEnd( & s - >zstream);
- return ret;
- fail: ret = -1;
- goto the_end;
- }
关于分析 ff_png_encodec 到目前为止就到这里,接下来还要看看一个函数:
/**
* Register the codec codec and initialize libavcodec.
*
* @warning either this function or avcodec_register_all() must be called
* before any other libavcodec functions.
*
* @see avcodec_register_all()
*/
也就是说这个函数的主要功能就是注册所有编解码器库,而且警告说必须在所有函数之前调用。
该函数的声明在 avcodec.h 中,但实现在 utils.c 中
将编解码器保存在静态全局变量 first_avcodec 中,这是一个链表结构体,当用到时,遍历这个链表即可
- void avcodec_register(AVCodec * codec) {
- AVCodec * *p; //将p定义成二级指针,估计是为了下面将编解码器连成编解码器链表的方便;p指向整个链表的首地址,使用*p存放每一个编解码器的首地址,**p就是表示编解码器的具体的字符串形式;
- avcodec_init(); //编解码器的初始化,且只能初始化一次。其中负责静态查找表结构的初始化的函数:ff_dsputil_static_init(),主要是两个表结构的初始化,ff_cropTbl和ff_squareTbl的初始化;其中ff_cropTble[i]实现的功能实际上是a = i<0 ? 0 : (i>255 ? 255 : i);这个表的大小是2*MAX_NEG_CROP+256, 是将-1024到1024之间的任意数转化为0~255的数,1024就是MAX_NEG_CROP的值。这样设计的实质是用查表的方式来减少所需执行的指令数量,是用空间上的代价来换取速度的提升,这是一种典型的方式。
- p = &first_avcodec; //前面有first_avcodec的静态全局变量初始化,即static AVCodec* first_avcodec = NULL;除了第一次添加avcodec时p会指向NULL,其他时候都是指向链表的首地址,实际上first_avcode存放的就是avcodec链表的首地址;不过感觉每一次调用avcodec_register()函数都要从开始遍历编解码器链表,这样不是很浪费时间么
- while ( * p != NULL) p = &( * p) - >next; * p = codec;
- codec - >next = NULL; //上边几行代码一块来看,就是将编解码器连接成链表的过程;先遍历已经存在的编解码器链表,然后将当前的编解码器插入到链表的结尾
- if (codec - >init_static_data) //这个函数指针主要完成编解码器静态数据的初始化
- codec - >init_static_data(codec);
- }
现在具体的看一看 avcodec_init()
调用了 ff_dsputil_static_init() 函数,那就继续跟进看一下:
- void avcodec_init(void) {
- static int initialized = 0;
- if (initialized != 0) return;
- initialized = 1;
- dsputil_static_init();
- }
看看 ff_dsputil_static_init
- /* init static data */
- av_cold void dsputil_static_init(void) {
- int i;
- for (i = 0; i < 256; i++) ff_cropTbl[i + MAX_NEG_CROP] = i;
- for (i = 0; i < MAX_NEG_CROP; i++) {
- ff_cropTbl[i] = 0;
- ff_cropTbl[i + MAX_NEG_CROP + 256] = 255;
- }其中ff_cropTble[i]实现的功能实际上是a = i < 0 ? 0 : (i > 255 ? 255 : i);这个表的大小是2 * MAX_NEG_CROP + 256,
- 是将 - 1024到1024之间的任意数转化为0~255的数,1024就是MAX_NEG_CROP的值。这样设计的实质是用查表的方式来减少所需执行的指令数量,是用空间上的代价来换取速度的提升,这是一种典型的方式
- for (i = 0; i < 512; i++) {
- ff_squareTbl[i] = (i - 256) * (i - 256);
- }
- for (i = 0; i < 64; i++) inv_zigzag_direct16[ff_zigzag_direct[i]] = i + 1;
- }
说了这么多其实 avcodec_register_all 函数就是完成各种编码解码器的注册作用,以一个链表来管理这些编解码器的,关于这些编码解码器什么时候用就等后面的持续的代码分析
注意:没有详细的阅读关于编码的那段代码也就是 encode_frame()函数,后面肯定还要回过头去看的。
来源: http://lib.csdn.net/article/liveplay/42777