ffmpeg小觑

来源:互联网 发布:张艺兴sheep知乎 编辑:程序博客网 时间:2024/05/16 08:41
Ffmpeg developers' guide-----------------by Alan Wang1Debug        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);Ffmpeglogging system的调用关系主要如下图:其中这三个函数的原型如下:1void av_log(void *avcl, int level, const char *fmt, ...) av_printf_format(3, 4);  //av_printf_format(3,4) ???2void av_vlog(void *avcl, int level, const char *fmt, va_list);//注意va_list是一个依赖编译器实现的c宏,va_list代表一个指向参数列表的指针(char *)3void 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,通过APIvoid av_log_set_callback(void (*)(void*, int, const char*, va_list));来设置一个新的回调函数指针例如my_logging_callback,那么在这个函数的实现中可以自定义把函数输出到其他地方入stdoutmy_logging_callback的实现自然可以用fprintfFfmpeg强烈推荐使用av_log APIDebug,但是这样确实带来的不便性,开发者自然会想到使用printf()来打log调试。但是Ffmpeg做了一定的限制,有时候使用printf()会带来错误信息。原因是Ffmpeglibavutil/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 printfprintf(“Msg you want to sent to stdout”);需要补充的是如果 ffplay.c ffmpeg.c ...这些测试程序中之间用printf来打log是没问题的,但是在这些程序中打log没有太大意义。Debug ffmpeg主要目的是取debug libavformatlibavcodec...这些库,在这些库中用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 codemake: *** [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.hav_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常见数据结构介绍1AVFormatContext (Format也有格式化的意思)    ffmpeg中基础的数据结构,有很多attributes,其他重要的数据结构大部分要依赖它。AVFormatContext是对多媒体文件的抽象。其中多媒体文件,包括本地的file也包括来之网络的媒体流,甚至是一个媒体流(音频流,视频流...)。之所以说AVFormatContext是对多媒体文件的抽象,是因为其结构中对一个多媒体文件的I/O都做了封装了划分,达到了结构化的特点。    AVFormatContext通常被API avformat_alloc_context()调用作为第一个被初始化的数据结构。    对于输入和输出,因为共用的是同一个结构体,所以需要分别对该结构中如下定义的iformatoformat成员赋值。     struct AVInputFormat *iformat;     struct AVOutputFormat *oformat;     对一个AVFormatContext来说,二个成员不能同时有值,即一个AVFormatContext不能同时含有demuxermuxer    nb_streamsstreams所表示的AVStream结构指针数组包含了所有内嵌媒体流的描述;    iformatoformat指向对应的demuxermuxer指针;     pb则指向一个控制底层数据读写的ByteIOContext结构。    packet_buffer packet_buffer_end,是两个AVPacketList的指针,分别表示已经缓存下来,但是还没有decodeAVPacket的指针    2.AVStream         该结构体描述一个媒体流 主要域的释义如下,其中大部分域的值可以由av_open_input_file根据文件头的信息确定,缺少的信息需要通过调用av_find_stream_info读帧及软解码进一步获取:    index/idindex对应流的索引,这个数字是自动生成的,根据index可以从AVFormatContext.streams表中索引到该流。而id则是流的标识,依赖于具体的容器格式。比如对于MPEG TS格式,id就是pid    time_base:流的时间基准,是一个实数(使用两个field,分子,分母,开表示这个实数),该流中媒体数据的ptsdts都将以这个时间基准为粒度。通常,使用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创建的,但也可能由FfmpegAPI创建(如av_read_frame)。 当某个AVPacket结构的数据缓冲区不再被使用时,要需要通过调用av_free_packet释放。av_free_packet调用的是结构体本身的destruct函数,它的值有两种情况: 1)av_destruct_packet_nofree0 2)av_destruct_packet,其中,情况1)仅仅是将datasize的值清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_nofreeAVPacket新建一个缓冲区, 然后将原缓冲区的数据拷贝至新缓冲区,置data的值为新缓冲区的地址,同时设destruct指针为av_destruct_packet 4.AVCodecContext    这是一个描述编解码器上下文的数据结构,包含了众多编解码器需要的参数信息,使用整个Ffmpeg库,这部分信息在调用API av_open_input_fileav_find_stream_info的过程中根据文件的头信息及媒体流内的头部信息完成初始化。    其中几个主要 域的释义如下:     extradata/extradata_size: 这个buffer中存放了解码器可能会用到的额外信息,在av_read_frame中填充。一般来说,首先,某种具体格式的demuxer在读取格式头信息的时候会填充extradata其次,如果demuxer没有做这个事情,比如可能在头部压没有相关的编解码信息,则相应的parser会继续从已经解复用出来的媒体流中继续寻找。在没有找到任何额外信息的情况下,这个buffer指针为空。     time_basewidth/height:视频的宽和高。     sample_rate/channels:音频的采样率和信道数目。     sample_fmt: 音频的原始采样格式。     codec_name/codec_type/codec_id/codec_tag:编解码器的信息。 保存AVCodec指针和与codec相关的数据,如videowidthheightaudiosample rate等。    AVCodecContext中的codec_typecodec_id二个变量对于 encoder/decoder的匹配来说,最为重要。     enum CodecType codec_type; enum CodecID codec_id; AVCodecContext中有两个成员数据结构:AVCodecAVFrame    AVCodec记录了所要使用的Codec信息并且含有 5个函数:initencoderclosedecodeflush来完成编解码工作。AVFrame中主要是包含了编码后的帧信息,包括本帧是否是key frame*data[4]定义的YCbCr(颜色空间相关)信息。 5.AVInputStream/ AVOutputStream     根据输入和输出流的不同,前述的AVStream结构都是封装在AVInputStreamAVOutputStream结构中,在av_encode( )函数中使用。 AVInputStream中还保存的有与时间有关的信息。 AVOutputStream中还保存有与音视频同步等相关的信息。 6,AVPictureAVPicture结构的数据成员很少,其结构如下:/** * 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常常被转换成AVPictureAVPicture中数据成员的意义其实很简单,第一个字段一般是一个数据指针数组(uint8*),数组大小一般为4,也就是说AVPicture对于一个图像的数据认定是有4种数据,且最后一个数据类型代表了图像的透明度(alpha)即data[AV_NUM_DATA_POINTERS – 1]代表了alpha数据。根据dranger大神的tutorial02可以看出来,对于YUV数据而言前三个数据类型data[0], data[1], data[2]分别代表了YUV数据的亮度(Y)两种色度(CbCr)。为什么数据会是这种布局,请自行google YUV第二个字段代表了第一个字段中各data域的size,这样的设置也说明了一个问题,AVPicture中的数据域data是连续的,原因自行google



PS:有一部分是抄的,TL总是找我要文档输出把我烦死了,排版也懒得搞了,就当是个备份吧。读者们,sorry啦

原创粉丝点击