ffmpeg小觑

Ffmpeg developers' guide

-----------------by Alan Wang

1,Debug

在ffmpeg的源码上进行调试自然免不了打log,在ffmpeg中打log可以使用Ffmpeg中的API

/**
* Send the specified message to the log if the level is less than or equal
* to the current av_log_level. By default, all logging messages are sent to
* stderr. This behavior can be altered by setting a different av_vlog callback
* function.
*
* @param avcl A pointer to an arbitrary struct of which the first field is a
* pointer to an AVClass struct.
* @param level The importance level of the message, lower values signifying
* higher importance.
* @param fmt The format string (printf-compatible) that specifies how
* subsequent arguments are converted to output.
* @see av_vlog
*/

 

//注意默认情况下,所有的logging msg都被输出到stderr上。
void av_log(void *avcl, int level, const char *fmt, ...) av_printf_format(3, 4);
Ffmpeg中logging system的调用关系主要如下图:












其中这三个函数的原型如下:
1,void av_log(void *avcl, int level, const char *fmt, ...) av_printf_format(3, 4); //av_printf_format(3,4) ???
2,void av_vlog(void *avcl, int level, const char *fmt, va_list);
//注意va_list是一个依赖编译器实现的c宏,va_list代表一个指向参数列表的指针(char *)
3,void av_log_default_callback(void* ptr, int level, const char* fmt, va_list vl);

默认的情况下,av_vlog函数会调用默认的回调函数 av_log_default_callback来完成log的输出,其实这个函数内部调用的就是fprintf(stderr,....),所以默认的log都会输出到stderr,通过API
void av_log_set_callback(void (*)(void*, int, const char*, va_list));
来设置一个新的回调函数指针例如my_logging_callback,那么在这个函数的实现中可以自定义把函数输出到其他地方入stdout。my_logging_callback的实现自然可以用fprintf。

Ffmpeg强烈推荐使用av_log API来Debug,但是这样确实带来的不便性,开发者自然会想到使用printf()来打log调试。但是Ffmpeg做了一定的限制,有时候使用printf()会带来错误信息。原因是Ffmpeg在libavutil/internal.h文件中将 printf ,fprintf, malloc, free,exit....等等这些函数都重新define过了事例如下:

#undef printf
#define printf please_use_av_log_instead_of_printf
#undef fprintf
#define fprintf please_use_av_log_instead_of_fprintf
#undef puts
#define puts please_use_av_log_instead_of_puts

 

所以如果向使用printf函数,需要在使用前重新 #undef一下。

#undef printf
printf(“Msg you want to sent to stdout”);

需要补充的是如果 ffplay.c ffmpeg.c ...这些测试程序中之间用printf来打log是没问题的,但是在这些程序中打log没有太大意义。Debug ffmpeg主要目的是取debug libavformat,libavcodec...这些库,在这些库中用printf会得到下面的编译阶段错误信息:
libavformat/sbgdec.c:347: error: implicit declaration of function ‘please_use_av_log_instead_of_printf’
libavformat/sbgdec.c:348: warning: ISO C90 forbids mixed declarations and code
make: *** [libavformat/sbgdec.o] Error 1



这里补充一个知识点:

//下面是av_log的函数原型,后面的av_printf_format(3, 4)实际上是一个宏。
void av_log(void *avcl, int level, const char *fmt, ...) av_printf_format(3, 4); //av_printf_format(3,4) ???

//该宏定义的文件位于 libavutil/attributes.h
av_printf_format(3,4) 这个宏展开后的结果是 :__attribute__((__format__(__printf__, fmtpos, attrpos)))

#ifdef __GNUC__
# define av_builtin_constant_p __builtin_constant_p
# define av_printf_format(fmtpos, attrpos) __attribute__((__format__(__printf__, fmtpos, attrpos)))
#else
# define av_builtin_constant_p(x) 0
# define av_printf_format(fmtpos, attrpos)
#endif 


 

可以看到这个__attribute__属性是一个gun c编译器的一个特色,这个attribute告诉编译器对这个av_log函数进行format检查,怎么检查?按照 printf函数的格式化方式进行参数检查。其中fmtpos(3) 代表需要格式化的字符串在av_log函数中的参数的位置是第3个,attrpos(4)表示第一个被格式化的变量值(可能是%s,%d)出现在av_log函数中的参数的位置是第4个。Gnu c的__attribute__作用是可以告诉编译器,让编译器做进一步的检查。

那么av_log的函数声明就可以被解释了。

Void av_log(void *avcl, int level, const char *fmt, …) __attribute__((_format_(_printf_, 3, 4)));






-----------------------------------------------------------------------------------






2,Ffmpeg上常见的数据结构及其关系,

先来看一张图:





-----------------------------------------------------------------------------------

Ffmpeg中常见的数据结构都在上面的图中了。

Ffmpeg常见数据结构介绍

1,AVFormatContext (Format也有格式化的意思)

ffmpeg中基础的数据结构,有很多attributes,其他重要的数据结构大部分要依赖它。AVFormatContext是对多媒体文件的抽象。
其中多媒体文件,包括本地的file也包括来之网络的媒体流,甚至是一个媒体流(音频流,视频流...)。之所以说AVFormatContext
是对多媒体文件的抽象,是因为其结构中对一个多媒体文件的I/O都做了封装了划分,达到了结构化的特点。
AVFormatContext通常被API avformat_alloc_context()调用作为第一个被初始化的数据结构。
对于输入和输出,因为共用的是同一个结构体,所以需要分别对该结构中如下定义的iformat或oformat成员赋值。


 

struct AVInputFormat *iformat; 
struct AVOutputFormat *oformat; 



对一个AVFormatContext来说,二个成员不能同时有值,即一个AVFormatContext不能同时含有demuxer和muxer。
nb_streams和streams所表示的AVStream结构指针数组包含了所有内嵌媒体流的描述;
iformat和oformat指向对应的demuxer和muxer指针;
pb则指向一个控制底层数据读写的ByteIOContext结构。
packet_buffer 和 packet_buffer_end,是两个AVPacketList的指针,分别表示已经缓存下来,但是还没有decodeAV的Packet的指针

2.AVStream

该结构体描述一个媒体流 主要域的释义如下,其中大部分域的值可以由av_open_input_file根据文件头的信息确定,缺少的信息需要通过调用av_find_stream_info读帧及软解码进一步获取:
index/id:index对应流的索引,这个数字是自动生成的,根据index可以从AVFormatContext.streams表中索引到该流。而id则是流的标识,依赖于具体的容器格式。比如对于MPEG TS格式,


id就是pid。
time_base:流的时间基准,是一个实数(使用两个field,分子,分母,开表示这个实数),该流中媒体数据的pts和dts都将以这个时间基准为粒度。
通常,使用av_rescale/av_rescale_q可以实现不同时间基准的转换。
start_time:流的起始时间,以流的时间基准为单位,通常是该流中第一个帧的pts。
duration:流的总时间,以流的时间基准为单位。
need_parsing:对该流parsing过程的控制域。
nb_frames:流内的帧数目。
r_frame_rate/framerate/avg_frame_rate:帧率。
codec:指向该流对应的AVCodecContext结构,调用av_open_input_file时生成。
parser:指向该流对应的AVCodecParserContext结构,调用av_find_stream_info时生成。

 

/**
* Indicates that everything up to the next keyframe
* should be discarded.
*/


int skip_to_keyframe;//这个字段在AVStream里面可以直接跳到下一个关键帧处。


3.AVPacket

Ffmpeg使用AVPacket来暂存解复用之后、解码之前的媒体数据(一a/v帧、一个subtitle包等)及附加信息(解码时间戳、显示时间戳、时长等)。
其中: dts表示解码时间戳,pts表示显示时间戳,它们的单位是所属媒体流的时间基准(time_base)。
stream_index给出所属媒体流的索引。
data为数据缓冲区指针,size为长度。
duration为数据的时长,也是以所属媒体流的时间基准为单位。
pos表示该数据在媒体流中的字节偏移量。
destruct为用于释放数据缓冲区的函数指针;。
flags为标志域,其中,最低为置1表示该数据是一个关键帧。
AVPacket结构本身只是个容器,它使用data成员引用实际的数据缓冲区。这个缓冲区通常是由av_new_packet创建的,但也可能由Ffmpeg的API创建(如av_read_frame)。
当某个AVPacket结构的数据缓冲区不再被使用时,要需要通过调用av_free_packet释放。av_free_packet调用的是结构体本身的destruct函数,它的值有两种情况:


1)av_destruct_packet_nofree或0
2)av_destruct_packet,其中,情况1)仅仅是将data和size的值清0而已,情况2)才会真正地释放缓冲区。 Ffmpeg内部使用AVPacket结构建立缓冲区装载数据,同时提供destruct函数,
如果Ffmpeg打算自己维护缓冲区,则将destruct设为av_destruct_packet_nofree,用户调用av_free_packet清理缓冲区时并不能够将其释放.如果FFMPEG打算自己维护缓冲区打算将
该缓冲区彻底交给调用者,则将destruct设为av_destruct_packet,表示它能够被释放。安全起见,如果用户希望自由地使用一个Ffmpeg内部创建的AVPacket结构,最好调用av_dup_packet
进行缓冲区的克隆,将其转化为缓冲区能够被释放的AVPacket,以免对缓冲区的不当占用造成异常错误。av_dup_packet会为destruct指针为av_destruct_packet_nofree的AVPacket新建一个缓冲区,
然后将原缓冲区的数据拷贝至新缓冲区,置data的值为新缓冲区的地址,同时设destruct指针为av_destruct_packet。

4.AVCodecContext

这是一个描述编解码器上下文的数据结构,包含了众多编解码器需要的参数信息,使用整个Ffmpeg库,这部分信息在调用API av_open_input_file和av_find_stream_info
的过程中根据文件的头信息及媒体流内的头部信息完成初始化。
其中几个主要 域的释义如下:
extradata/extradata_size: 这个buffer中存放了解码器可能会用到的额外信息,在av_read_frame中填充。一般来说,首先,某种具体格式的demuxer在读取格式头信息的时候会填充extradata,
其次,如果demuxer没有做这个事情,比如可能在头部压没有相关的编解码信息,则相应的parser会继续从已经解复用出来的媒体流中继续寻找。在没有找到任何额外信息的情况下,这个buffer指针为空。
time_base: width/height:视频的宽和高。
sample_rate/channels:音频的采样率和信道数目。
sample_fmt: 音频的原始采样格式。
codec_name/codec_type/codec_id/codec_tag:编解码器的信息。 保存AVCodec指针和与codec相关的数据,如video的width、height,audio的sample rate等。
AVCodecContext中的codec_type,codec_id二个变量对于 encoder/decoder的匹配来说,最为重要。
enum CodecType codec_type; enum CodecID codec_id; AVCodecContext中有两个成员数据结构:AVCodec、AVFrame。
AVCodec记录了所要使用的Codec信息并且含有 5个函数:init、encoder、close、decode、flush来完成编解码工作。AVFrame中主要是包含了编码后的帧信息,
包括本帧是否是key frame、*data[4]定义的Y、Cb和Cr(颜色空间相关)信息。

5.AVInputStream/ AVOutputStream

根据输入和输出流的不同,前述的AVStream结构都是封装在AVInputStream和AVOutputStream结构中,在av_encode( )函数中使用。 AVInputStream中还保存的有与时间有关的信息。
AVOutputStream中还保存有与音视频同步等相关的信息。

6,AVPicture

AVPicture结构的数据成员很少,其结构如下:

/**
* four components are given, that's all.
* the last component is alpha
*/
typedef struct AVPicture {
uint8_t *data[AV_NUM_DATA_POINTERS];
int linesize[AV_NUM_DATA_POINTERS]; ///< number of bytes per line
} AVPicture;


因为,AVPicture的数据成员与AVFrame的前两个数据成员完全一致,所以AVFrame常常被转换成AVPicture。

AVPicture中数据成员的意义其实很简单,第一个字段一般是一个数据指针数组(uint8*),数组大小一般为4,也就是说AVPicture对于一个图像的数据认定是有4种数据,且最后一个数据类型代表了图像的透明度(alpha)即data[AV_NUM_DATA_POINTERS – 1]代表了alpha数据。根据dranger大神的tutorial02可以看出来,对于YUV数据而言前三个数据类型data[0], data[1], data[2]分别代表了YUV数据的亮度(Y)两种色度(Cb,Cr)。为什么数据会是这种布局,请自行google YUV。

第二个字段代表了第一个字段中各data域的size,这样的设置也说明了一个问题,AVPicture中的数据域data是连续的,原因自行google。

原帖地址:http://blog.csdn.net/mci2004/article/details/8150929

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值