FFmpeg入门--函数

FFmpeg解码函数

  1. 注册所有容器格式和CODEC:av_register_all()
  2. 打开文件:avformat_open_input()
  3. 从文件中提取流信息:avformat_find_stream_info()
  4. 穷举所有的流信息,查找其中种类为CODEC_TYPE_VIDEO
  5. 查找对应的解码器:avcodec_find_decoder()
  6. 打开编解码器:avcodec_open()
  7. 为解码帧分配内存:avcodec_alloc_frame()
  8. 不停的从码流中提取出帧数据:av_read_frame()

    解码一帧压缩数据: avcodec_send_packet()
    接收解码后的数据 :avcodec_receive_frame()
    发送未编码的数据: avcodec_send_frame()
    接收编码后的数据: avcodec_receive_packet()

  9. 判断帧的类型,对于视频帧调用:avcodec_decodec_video()
  10. 解码完成,释放解码器:avcodec_close()
  11. 关闭输入文件:avformat_close_input_file()

FFmpeg解码过程:
在这里插入图片描述

  • void avcodec_init(void);
    初始化libavcodec,一般最先调用该函数
    引入头文件: #include “libavcodec/avcodec.h”
    实现在: \ffmpeg\libavcodec\utils.c
    该函数必须在调用libavcodec里的其它函数前调用,一般在程序启动或模块初始化时调用,如果你调用了多次也无所谓,因为后面的调用不会做任何事情.从函数的实现里你可以发现,代码中对多次调用进行了控制.
    该函数是非线程安全的

  • 注册
    av_register_all函数的作用是注册一系列的(解)复用器、编/解码器等。它在所用基于FFmpeg的应用程序中几乎都是第一个被调用的,只有调用了该函数,才能使用复用器,编码器等。

    1. 声明
      void register_all(void)
      {
      	avcodec_register_all();
      	……
      		REGISTER_MUXDEMUX(FLV, flv);
      	……
      }
      
      REGISTER_MUXDEMUX实际调用的是av_register_input_format和av_register_output_format,通过这两个方法,将(解)复用器分别添加到全局变量first_iformat与first_oformat链表的最后位置。
    2. 说明
      初始化 libavformat和注册所有的muxers、demuxers和protocols,
      一般在调用avcodec_init后调用该方法
      引入头文件:#include “libavformat/avformat.h”
      实现在:\ffmpeg\libavformat\allformats.c
      其中会调用avcodec_register_all()注册多种音视频格式的编解码器,并注册各种文件的编解复用器
      当然,你也可以不调用该函数,而通过选择调用特定的方法来提供支持
  • avformat_alloc_context()

AVFormatContext *avformat_alloc_context(void);
// 分配一个AVFormatContext结构
// 引入头文件:#include "libavformat/avformat.h"
// 实现在:\ffmpeg\libavformat\options.c
其中负责申请一个AVFormatContext结构的内存,并进行简单初始化
// avformat_free_context()可以用来释放该结构里的所有东西以及该结构本身
// 也是就说使用 avformat_alloc_context()分配的结构,需要使用avformat_free_context()来释放
// 有些版本中函数名可能为: av_alloc_format_context();
  • avformat_free_context()
void avformat_free_context(AVFormatContext *s);
 释放一个AVFormatContext结构
// 引入头文件:#include "libavformat/avformat.h"
// 实现在:\ffmpeg\libavformat\utils.c
// 使用 avformat_alloc_context()分配的结构,采用该函数进行释放,除释放AVFormatContext结构本身内存之外,AVFormatContext中指针所指向的内存也会一并释放
// 有些版本中函数名猜测可能为: av_free_format_context();
  • avio_alloc_context()
AVIOContext *avio_alloc_context(
                  unsigned char *buffer,
                  int buffer_size,
                  int write_flag,
                  void *opaque,
                  int (*read_packet)(void *opaque, uint8_t *buf,int buf_size),
                  int (*write_packet)(void *opaque, uint8_t *buf,int buf_size),
                  int64_t (*seek)(void *opaque, int64_t offset,int whence));
为I/0缓存申请并初始化一个AVIOContext结构,结束使用时必须使用av_free()进行释放
// unsigned char *buffer: 输入/输出缓存内存块,必须是使用av_malloc()分配的
// int buffer_size: 缓存大小是非常重要的
// int write_flag: 如果缓存为可写则设置为1,否则设置为0
// void *opaque: 指针,用于回调时使用
// int (*read_packet): 读包函数指针
// int (*write_packet): 写包函数指针
// int64_t (*seek): seek文件函数指针
  • av_open_input_file()
attribute_deprecated int av_open_input_file(
                       AVFormatContext **ic_ptr,
                       constchar *filename,
                       AVInputFormat *fmt,
                       int buf_size,
                       AVFormatParameters *ap
                       );
// 以输入方式打开一个媒体文件,也即源文件,codecs并没有打开,只读取了文件的头信息.
// 引入头文件:#include "libavformat/avformat.h"
// AVFormatContext **ic_ptr 输入文件容器
//constchar *filename 输入文件名,全路径,并且保证文件存在
// AVInputFormat *fmt 输入文件格式,填NULL即可
//int buf_size,缓冲区大小,直接填0即可
// AVFormatParameters *ap, 格式参数,添NULL即可
// 成功返回0,其它失败
// 不赞成使用 avformat_open_input 代替
  • avformat_open_input
    文件打开
    FFmpeg读取媒体数据的过程始于avformat_open_input, 该方法完成了媒体文件的打开和格式测试的功能。avformat_open_input方法中调用了init_input函数,在这里面完成了查找流媒体协议和解复用器的工作。

    static intinit_input(AVFormatContext *s, const char *filename,AVDictionary **options)
    {
    	int ret;
    	……
    		if ((ret = s->io_open(s, &s->pb, filename, AVIO_FLAG_READ | s->avio_flags, options)) < 0)
    			return ret;
    	    if (s->iformat)
    	    	return 0; 
    	    return av_probe_input_buffer2(s->pb, &s->iformat, filename,	s, 0, s->format_probesize);
    }
    
    static conststructURLProtocol *url_find_protocol(const char *filename)
    {
    	constURLProtocol **protocols;
    	……
    		protocols = ffurl_get_protocols(NULL, NULL);
    	    if (!protocols)
    	    	return NULL;
            for (i = 0; protocols[i]; i++) {
                constURLProtocol *up = protocols[i];
    		    if (!strcmp(proto_str, up->name)) {
    			    av_freep(&protocols);
    				return up;
    		    }
                if (up->flags & URL_PROTOCOL_FLAG_NESTED_SCHEME &&!strcmp(proto_nested, up->name)) {
                    av_freep(&protocols);
    				return up;
    			}
    		}
    
    	av_freep(&protocols);
        return NULL;
    }
    
    

    ffurl_get_protocols可以得到当前编译的FFmpeg支持的所有流媒体协议,通过url的scheme和protocol->name相比较,得到正确的protocol。

    [if !supportLists]1. [endif]av_probe_input_buffer2最终调用到av_probe_input_format3,该方法遍历所有的解复用器,即first_iformat链表中的所有节点,调用它们的read_probe()函数计算匹配得分,函数最终返回计算找到的最匹配的解复用器。本例中AVInputFormat最终指向了libavformat/flvdec.c中的ff_flv_demuxer。

  • av_find_stream_info()

int av_find_stream_info(AVFormatContext *ic);
通过读取媒体文件的中的包来获取媒体文件中的流信息,对于没有头信息的文件如(mpeg)是非常有用的,
// 该函数通常重算类似mpeg-2帧模式的真实帧率,该函数并未改变逻辑文件的position.
// 引入头文件:#include "libavformat/avformat.h"
也就是把媒体文件中的音视频流等信息读出来,保存在容器中,以便解码时使用
返回>=0时成功,否则失败

  • avcodec_find_decoder()
AVCodec *avcodec_find_decoder(enum CodecID id);
 通过code ID查找一个已经注册的音视频解码器
// 引入 #include "libavcodec/avcodec.h"
// 实现在: \ffmpeg\libavcodec\utils.c
// 查找解码器之前,必须先调用av_register_all注册所有支持的解码器
 查找成功返回解码器指针,否则返回NULL
 音视频解码器保存在一个链表中,查找过程中,函数从头到尾遍历链表,通过比较解码器的ID来查找
  • avcodec_find_decoder_by_name()
AVCodec *avcodec_find_decoder_by_name(constchar *name);
// 通过一个指定的名称查找一个已经注册的音视频解码器
// 引入 #include "libavcodec/avcodec.h"
// 实现在: \ffmpeg\libavcodec\utils.c
// 查找解码器之前,必须先调用av_register_all注册所有支持的解码器
// 查找成功返回解码器指针,否则返回NULL
// 音视频解码器保存在一个链表中,查找过程中,函数从头到尾遍历链表,通过比较解码器的name来查找
  • avcodec_find_encoder()
AVCodec *avcodec_find_encoder(enum CodecID id);
 通过code ID查找一个已经注册的音视频编码器
// 引入 #include "libavcodec/avcodec.h"
// 实现在: \ffmpeg\libavcodec\utils.c
// 查找编码器之前,必须先调用av_register_all注册所有支持的编码器
 查找成功返回编码器指针,否则返回NULL
// 音视频编码器保存在一个链表中,查找过程中,函数从头到尾遍历链表,通过比较编码器的ID来查找
  • avcodec_find_encoder_by_name()
AVCodec *avcodec_find_encoder_by_name(constchar *name);
// 通过一个指定的名称查找一个已经注册的音视频编码器
// 引入 #include "libavcodec/avcodec.h"
// 实现在: \ffmpeg\libavcodec\utils.c
// 查找编码器之前,必须先调用av_register_all注册所有支持的编码器
// 查找成功返回编码器指针,否则返回NULL
// 音视频编码器保存在一个链表中,查找过程中,函数从头到尾遍历链表,通过比较编码器的名称来查找
  • avcodec_open()
int avcodec_open(AVCodecContext *avctx, AVCodec *codec);
// 使用给定的AVCodec初始化AVCodecContext
// 引入#include "libavcodec/avcodec.h"
// 方法: avcodec_find_decoder_by_name(), avcodec_find_encoder_by_name(), avcodec_find_decoder() and avcodec_find_encoder() 提供了快速获取一个codec的途径
// 该方法在编码和解码时都会用到
 返回0时成功,打开作为输出时,参数设置不对的话,调用会失败
  • av_guess_format()
AVOutputFormat *av_guess_format(constchar *short_name,
                                constchar *filename,
                                constchar *mime_type);
返回一个已经注册的最合适的输出格式
// 引入#include "libavformat/avformat.h"
// 可以通过 const char *short_name 获取,如"mpeg"
// 也可以通过 const char *filename 获取,如"E:\a.mp4"
  • av_new_stream()
AVStream *av_new_stream(AVFormatContext *s, int id);
// 为媒体文件添加一个流,一般为作为输出的媒体文件容器添加音视频流
// 引入 #include "libavformat/avformat.h"
// 再打开源文件时用户一般不需要直接调用该方法
  • dump_format()
attribute_deprecated void dump_format(AVFormatContext *ic,
                                      int index,
                                      constchar *url,
                                      int is_output);
 该函数的作用就是检查下初始化过程中设置的参数是否符合规范
 有些版本中为 av_dump_format
  • av_set_parameters()
attribute_deprecated int av_set_parameters(
          AVFormatContext *s, 
          AVFormatParameters *ap
          );
// 设置初始化参数
// 不赞成跳过该方法,直接调用 avformat_write_header/av_write_header
  • av_write_header()
attribute_deprecated int av_write_header(AVFormatContext *s);
// 把流头信息写入到媒体文件中
// 返回0成功

  • av_init_packet()
void av_init_packet(AVPacket *pkt);
// 使用默认值初始化AVPacket
// 定义AVPacket对象后,请使用av_init_packet进行初始化
  • av_free_packet()
void av_free_packet(AVPacket *pkt);
// 释放AVPacket对象
  • av_read_frame()
    av_read_frame作用是读取媒体数据中的每一个音视频帧,该方法中最关键的地方就是调用了AVInputFormat的read_packet()方法。AVInputFormat的read_packet()是一个函数指针,指向当前的AVInputFormat的读取数据的函数。
int av_read_frame(AVFormatContext *s, AVPacket *pkt);
// 从输入源文件容器中读取一个AVPacket数据包
 该函数读出的包并不每次都是有效的,对于读出的包我们都应该进行相应的解码(视频解码/音频解码),
在返回值>=0,循环调用该函数进行读取,循环调用之前请调用av_free_packet函数清理AVPacket
  • avcodec_decode_video2()
int avcodec_decode_video2(AVCodecContext *avctx, 
                          AVFrame *picture,
                          int *got_picture_ptr,
                          AVPacket *avpkt
                          );
 解码视频流AVPacket
// 使用av_read_frame读取媒体流后需要进行判断,如果为视频流则调用该函数解码
// 返回结果<0时失败,此时程序应该退出检查原因
// 返回>=0时正常,假设 读取包为:AVPacket vPacket 返回值为 int vLen; 每次解码正常时,对vPacket做
// 如下处理:
//   vPacket.size -= vLen;
//   vPacket.data += vLen;
// 如果 vPacket.size==0,则继续读下一流包,否则继续调度该方法进行解码,直到vPacket.size==0
// 返回 got_picture_ptr > 0 时,表示解码到了AVFrame *picture,其后可以对picture进程处理
  • avcodec_decode_audio3()
int avcodec_decode_audio3(AVCodecContext *avctx, 
                          int16_t *samples,
                          int *frame_size_ptr,
                          AVPacket *avpkt
                          );
 解码音频流AVPacket
// 使用av_read_frame读取媒体流后需要进行判断,如果为音频流则调用该函数解码
// 返回结果<0时失败,此时程序应该退出检查原因
// 返回>=0时正常,假设 读取包为:AVPacket vPacket 返回值为 int  vLen; 每次解码正常时,对vPacket做如下处理:
//   vPacket.size -= vLen;
//   vPacket.data += vLen;
// 如果 vPacket.size==0,则继续读下一流包,否则继续调度该方法进行解码,直到vPacket.size==0
  • av_close_input_file()
void av_close_input_file(AVFormatContext *s);
 关闭使用avformat_close_input()打开的输入文件容器,但并不关系它的codecs
// 引入头文件:#include "libavformat/avformat.h"
// 使用av_open_input_file 打开的文件容器,可以使用该函数关闭
// 使用 av_close_input_file 关闭后,就不再需要使用avformat_free_context 进行释放了
  • av_image_get_buffer_size
    给缓冲区设置类型,得到缓冲区大小
av_image_get_buffer_size(enum AVPixelFormat pix_fmt, int width, int height, int align) 
								//pix_fmt: 视频像素数据格式类型->YUV420P格式 
								//width: 一帧视频像素数据宽 = 视频宽  
								//height: 一帧视频像素数据高 = 视频高   
								//align: 字节对齐方式->默认是1
  • av_image_fill_arrays
    向avframe_yuv420p->填充数据
av_image_fill_arrays(uint8_t **dst_data, int *dst_linesize, const uint8_t *src, enum AVPixelFormat pix_fmt, int width, int height, int align)   
							//dst_data: 目标->填充数据(avframe_yuv420p)   
							//dst_linesize: 目标->每一行大小   
							//src: 原始数据   
							//pix_fmt: 目标->格式类型    
							//width: 宽    
							//height: 高    
							//align: 字节对齐方式
  • sws_getContext
    初始化SWS,图片格式转换
    把帧从原始格式(例如:pCodecCtx->pix_fmt)转换成为(yuv)格式。
sws_getContext(int srcW, int srcH, enum AVPixelFormat srcFormat, int dstW, int dstH, enum AVPixelFormat dstFormat, int flags, SwsFilter *srcFilter, SwsFilter *dstFilter, const double *param)  
	 				// scrW: 原始格式宽度    
					// scrH: 原始格式高度   
					// scrFormat: 原始数据格式   
					// 目标数据    
					// dstW: 目标格式宽度    
					// dstH: 目标格式高度   
					// dstFormat: 目标数据格式   
                   // 当遇到Assertion desc failed at src/libswscale/swscale_internal.h:668这个问题就是获取元数据的高度有问题
                 
  • avcodec_send_packet
    解码之后得到视频的像素数据
avcodec_send_packet(AVCodecContext *avctx, AVPacket *pkt)   
				    // avctx: 解码器上下文          
					// pkt: 获取到数据包     
  • ==
    将解码出来的这一帧数据,统一转类型
sws_scale(struct SwsContext *c, const uint8_t *const *srcSlice, const int *srcStride, int srcSliceY, int srcSliceH, uint8_t *const *dst, const int *dstStride)     
						// SwsContext *c: 视频像素格式的上下文          
						// srcSlice: 原始视频输入数据             
						// srcStride: 原数据每一行的大小          
						// srcSliceY: 输入画面的开始位置,一般从0开始   
						// srcSliceH: 原始数据的长度            
						// dst: 输出的视频格式            
						// dstStride: 输出的画面大小
						//调整解码出来的图像,解码出来的可能含有填充的信息。

猜你喜欢

转载自blog.csdn.net/qq_41498261/article/details/84202503
今日推荐