第一步:注册组件
av_register_all()
注册编码器、解码器等等…
第二步:打开封装格式->打开文件
打开.mp4、.mov、.wmv...格式文件
avformat_open_input();
api解释(文档人肉翻译😓):
/**
* Open an input stream and read the header. The codecs are not opened.
--->打开一个输入流并且读取它的头文件。注意:编解码器不会被打开。
* The stream must be closed with avformat_close_input().
--->记住:一定要用 avformat_close_input() 关闭打开的流。
*
* @param ps Pointer to user-supplied AVFormatContext (allocated by avformat_alloc_context).
* May be a pointer to NULL, in which case an AVFormatContext is allocated by this
* function and written into ps.
* Note that a user-supplied AVFormatContext will be freed on failure.
--->使用用户提供的 AVFormatContext(封装格式上下文,使用 avformat_alloc_context 分配空间)创建。这个 AVFormatContext 大概是一个指向 NULL 的指针,在这种情况下,AVFormatContext 被这个函数(avformat_alloc_context)创建后,会被当前的函数写入到 ps 这个参数中。(人话:先自己创建一个空的内存空间,再传入此函数中,此函数会将信息写入到这个内存空间内)
--->注意:如果此函数失败了,用户提供的 AVFormatContext 将会被释放。
*
* @param url URL of the stream to open.
--->需要打开的流路径。
* @param fmt If non-NULL, this parameter forces a specific input format.
--->该参数如果不为空,就必须传入一个明确的输入格式。
* Otherwise the format is autodetected.
--->否则如果为空,会自动检测格式。
* @param options A dictionary filled with AVFormatContext and demuxer-private options.
--->一个参数字典(由 AVFormatContext 和 demuxer-private 填充)
* On return this parameter will be destroyed and replaced with a dict containing
* options that were not found. May be NULL.
--->如果填写的操作选项没有找到,则这个参数变为 NULL
*
* @return 0 on success, a negative AVERROR on failure.
--->返回值 0 表示成功,负数表示失败
*
* @note If you want to use custom IO, preallocate the format context and set its pb field.
--->注意:如果你想使用自己 IO 读取,需要给封装格式上下文预分配空间,并且设置 AVFormatContext 的 pb 属性。
*/
int avformat_open_input(AVFormatContext **ps, const char *url, AVInputFormat *fmt, AVDictionary **options);
第三步:查找视频流
如果是视频解码,那么查找视频流,如果是音频解码,那么就查找音频流
avformat_find_stream_info();
api解释:
/**
* Read packets of a media file to get stream information. This
* is useful for file formats with no headers such as MPEG. This
* function also computes the real framerate in case of MPEG-2 repeat
* frame mode.
--->读取媒体文件的包来获取流信息,对于没有头部的文件(如:MPEG)来说是很有用的。该函数也会重新计算类似于 MPEG-2 帧模式的真实帧率。
* The logical file position is not changed by this function;
--->这个函数不会改变逻辑文件的位置。
* examined packets may be buffered for later processing.
--->被检查的数据包可以被缓冲以便以后的处理。
*
* @param ic media file handle
--->媒体文件句柄(封装格式上下文)
* @param options If non-NULL, an ic.nb_streams long array of pointers to
* dictionaries, where i-th member contains options for
* codec corresponding to i-th stream.
* On return each dictionary will be filled with options that were not found.
--->options 如果不为空,将会有一个指向多个字典的数组(长度:封装格式上下文的 nb_streams),该数组的每一个元素(字典指针)包含的编解码 options 将会赋给每一流。
--->(人话:你如果传入了 options 配置,那么你查找到的所有流就会使用这些 option 配置)
* @return >=0 if OK, AVERROR_xxx on error
--->返回值:>=0表示成功,其他表示错误。
*
* @note this function isn't guaranteed to open all the codecs, so
* options being non-empty at return is a perfectly normal behavior.
--->注意:这个函数不能保证能打开所有的编解码器,所以传一个指定的 options 配置是一个非常正常的。
--->(人话:这个参数你最好自己传进来)
*
* @todo Let the user decide somehow what information is needed so that
* we do not waste time getting stuff the user does not need.
--->还有:让使用者自己来决定他们需要什么信息,所以我们不会浪费时间去给使用者不想要的东西。
*/
int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options);
第四步:查找音频/视频解码器
1、查找音频/视频流索引位置
2、根据音频/视频流索引,获取解码器上下文
3、根据解码器上下文,获得解码器ID,然后查找解码器
第五步:打开解码器
avcodec_open2();
第六步:读取音频/视频压缩数据->循环读取
每读取一帧数据,立即解码一帧数据
第七步:音频/视频解码->得到音频采样数据/视频像素数据->播放音频/视频
第八步:关闭解码器->解码完成