视频封装概述
MP4 格式分析
解封装流程
AVFormatContext
nb_streams: 有多少条流,比如视频流、音频流
streams: 流的相关信息
AVStream
AVCodecParameters
avformat_open_input 打开文件或者网络地址
avformat_find_stream_info
av_read_frame 读取一帧数
测试代码
121_test_demux.cpp
#include <iostream>
using namespace std;
extern "C" // 指定函数是 C 语言函数,函数目标名不包含重载标识,C++ 中调用 C 函数需要使用 extern "C"
{
// 引用 ffmpeg 头文件
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
}
// 预处理指令导入库
#pragma comment(lib, "avcodec.lib")
#pragma comment(lib, "avformat.lib")
#pragma comment(lib, "avutil.lib")
#define CERR(err) if(err != 0) \
{ \
PrintError(err); \
return -1; \
}
static void PrintError(int err)
{
char buf[1024] = { 0 };
av_strerror(err, buf, sizeof(buf) - 1);
cerr << buf << endl;
}
int main()
{
int ret = 0;
AVFormatContext* ic = nullptr; // 解封装输入上下文
const char* url = "v1080.mp4";
AVPacket packet;
AVStream* vs = nullptr; //视频流
AVStream* as = nullptr; //音频流
ret = avformat_open_input(&ic, url,
nullptr, // 封装器格式 nullptr 自动探测 根据后缀名或者文件头
nullptr // 参数设置,rtsp需要设置
);
CERR(ret);
// 获取媒体信息 无头部格式
ret = avformat_find_stream_info(ic, nullptr);
CERR(ret);
// 打印封装信息
av_dump_format(ic, 0, url,
0 // 0表示上下文是输入 1 输出
);
for (int i = 0; i < ic->nb_streams; i++)
{
if (ic->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
{
vs = ic->streams[i];
cout << "video: " << "width = " << vs->codecpar->width << " hwight = " << vs->codecpar->height << " bitrate = " << vs->codecpar->bit_rate << endl;
}
else if (ic->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
{
as = ic->streams[i];
cout << "audio: " << "sample rate = " << as->codecpar->sample_rate << endl;
}
}
while (1)
{
ret = av_read_frame(ic, &packet); // 此函数不会是否 packet 中原先的 buf 空间,需要手动调用 av_packet_unref 来释放 buf 空间
CERR(ret);
if (vs && (packet.stream_index == vs->codecpar->codec_type))
{
cout << "video ";
}
else if (as && (packet.stream_index == as->codecpar->codec_type))
{
cout << "audio ";
}
cout << packet.pts << " : " << packet.dts << endl;
av_packet_unref(&packet);
}
avformat_close_input(&ic);
return 0;
}
测试结果如下图所示: