媒体播放器播放一个媒体文件,需要以下步骤:解协议,解封装,解码,视音频同步,如果播放本地文件则不需要解协议。其中解封装(解复用)的作用是将输入的封装格式(AVI、MP4、TS、FLV等封装格式)的数据分离成音频流压缩编码数据和视频压缩编码数据,把这些视音频以及字幕等基本流分离出来。在这个解封装的过程中,我们可以获取到媒体文件的一些基本信息。
1.封装格式的相关函数
AVFormatContext
主要存储视音频封装格式中包含的信息
2.解封装流程
- (1)分配解复用器上下文(AVFormatContext)<非必须>
- (2)打开文件、获取封装信息上下文
AVFormatContext
- (3)获取取媒体文件音视频信息,这一步会将
AVFormatContext
内部变量填充 - (4)读取码流信息:循环处理
- 4.1 从文件中读取数据包`av_read_frame
- 4.2 定位文件avformat_seek_file或av_seek_frame
- (5)关闭解复用器
3.示例
3.1 对视频文件MP4进行解封装
#pragma execution_character_set("utf-8")
#include <stdio.h>
#include <libavformat/avformat.h>
/**
* @brief 将一个AVRational类型的分数转换为double类型的浮点数
* @param r:r为一个AVRational类型的结构体变量,成员num表示分子,成员den表示分母,r的值即为(double)r.num / (double)r.den。
* 用这种方法表示可以最大程度地避免精度的损失
* @return 如果变量r的分母den为0,则返回0(为了避免除数为0导致程序死掉);其余情况返回(double)r.num / (double)r.den
*/
static double r2d(AVRational r)
{
return r.den == 0 ? 0 : (double)r.num / (double)r.den;
}
int main(int argc,char **argv)
{
//mp4文件路径
const char *path = argv[1];
//初始化所有组件,调用了该函数,才能使用复用器和编解码器。否则,调用函数avformat_open_input会失败,无法获取媒体文件的信息
av_register_all();
//打开网络流。这里如果只需要读取本地媒体文件,不需要用到网络功能,可以不用加上这一句
avformat_network_init();
AVDictionary *opts = NULL;
//AVFormatContext是描述一个媒体文件或媒体流的构成和基本信息的结构体
AVFormatContext *ic = NULL;
//打开媒体文件
//媒体打开函数,调用该函数可以获得路径为path的媒体文件的信息,
//并把这些信息保存到指针ic指向的空间中(调用该函数后会分配一个空间,让指针ic指向该空间)
int ret = avformat_open_input(&ic,path,NULL,&opts);
if(ret < 0)
{
char buf[1024] = { 0 };
av_strerror(ret,buf,sizeof(buf)-1);
printf("open %s failed:%s\n",path,buf);
}
else //打开成功
{
printf("open media file %s success\n",path);
//调用该函数可以进一步读取一部分视音频数据并且获得一些相关的信息。
//调用avformat_open_input之后,我们无法获取到正确和所有的媒体参数,
//所以还得要调用avformat_find_stream_info进一步的去获取
avformat_find_stream_info(ic,NULL);
printf("the media name is:%s\n",ic->filename);
printf("s