对mp4封装的文件进行解封装并抽取h264
重要结构体:
AVFormatContext{
....
unsigned int nb_streams;//流的数量
AVStream **streams; //存储的流数组
void *priv_data; //存放参数信息
AVIOContext *pb; //I/O 在封装的时候用来写入文件
int64_t duration; //整个媒体的时长
int64_t bit_rate; //整个媒体的比特率
char *url; //打开网络端的视频就要传这个
....
};
AVStream{
...
int index; //流的索引
AVRational time_base; //时间基数
AVRational avg_frame_rate; //帧率
AVCodecParameters *codecpar;//包含配置参数信息, 解码用到的参数
...
};
AVCodecParameters{
...
AVMediaType codec_type; //媒体类型:视频, 音频, 字幕
AVCodecID codec_id; //编码器ID号
int format; //像素格式
int64_t bit_rate; //比特率
int width;
int height;
uint64_t channel_layout; //音频通道数
int channels; //通道
int sample_rate;//采样率
int frame_size; //帧大小
...
};
#include <iostream>
#include <fstream>
using namespace std;
extern "C"{
#include <libavformat/avformat.h>
}
//预处理指令导入库
#pragma comment(lib, "avformat.lib")
#pragma comment(lib, "avutil.lib")
#pragma comment(lib, "avcodec.lib")
#define CERR(err) do{if(err!=0)\
{printErr(err); return -1;}\
}while(0)
void printErr(int err)
{
char buf[1024] = { 0 };
av_strerror(err, buf, sizeof(buf) - 1);
cerr << buf << endl;
}
int main(int argc, char* argv[])
{
//1,打开mp4媒体文件, 初始化解封装上下文AVFormatContext, 需要释放空间的
const char* url = "v1080.mp4";
AVFormatContext* ic = nullptr;
int re = avformat_open_input(&ic,
url,
NULL, //解封装格式,自动探测,以后缀名,或者文件头
NULL);//参数设置, rtsp需要设置,里面有对应的AVOption, 解码时不需要, 填NULL, 参数配置在 libavformat/options_table.h 中
CERR(re);
//2,获取媒体信息
re = avformat_find_stream_info(ic, NULL);
CERR(re);
//打印封装信息
av_dump_format(ic, 0, url, 0);
//3,提取出视频流/音频流
AVStream* as = nullptr;
AVStream* vs = nullptr;
for (int i = 0; i < ic->nb_streams; ++i) {
if (ic->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
as = ic->streams[i];
cout << "===音频===" << endl;
cout << "sample_rate:" << as->codecpar->sample_rate << endl;
}
else if (ic->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
vs = ic->streams[i];
cout << "===视频===" << endl;
cout << "width:" << vs->codecpar->width << endl;
cout << "height" << vs->codecpar->height << endl;
}
}
//4,申请一个AVPacket用于存放提取的视频流
AVPacket pkt;
/**************************************/
//从mp4中抽取的AVPacket没有头部信息(SPS, PPS, IDR), 无法直接写入文件形成h264码流
//使用AVBSFContext包装
AVBSFContext* absCtx = NULL; //要释放空间
const AVBitStreamFilter* absFilter = NULL;
AVCodecParameters* codecpar = NULL;
absFilter = av_bsf_get_by_name("h264_mp4toannexb");
if(!absFilter){
cout << "get bsFilter failed! << endl;
return -1;
}
re = av_bsf_alloc(absFilter, &absCtx);
CERR(re);
//流信息复制进上下文中
codecpar = vs->codecpar;
avcodec_parameters_copy(absCtx->par_in, codecpar);
re = av_bsf_init(absCtx);
CERR(re);
ofstream ofs;
ofs.open("test__h264.h264", ios::binary);
//申请一个用于存放添加了码流信息的包
AVPacket pkt2;//如果是指针的形式需要初始化//av_init_packet(pkt2);
/**************************************/
//使用了AVPacket时都要考虑到其内存的释放
//5,开始从mp4提取AVPacket
while(1){
re = av_read_frame(ic, &pkt);
if (re == AVERROR(EAGAIN) || re == AVERROR_EOF) break;
else if(re != 0) break;
if(vs && pkt.stream_index == vs->index){//抽取成功
cout << "视频流";
//pkt发送给过滤线程
re = av_bsf_send_packet(absCtx, &pkt);
CERR(re);
while (1) {
re = av_bsf_receive_packet(absCtx, &pkt2);
if (re == AVERROR(EAGAIN) || re == AVERROR_EOF)
break;
ofs.write((char*)pkt2.data, pkt2.size);
av_packet_unref(&pkt2);
}
}
if (as && pkt.stream_index == as->index) {
cout << "音频流";
}
av_packet_unref(&pkt);
}
av_bsf_free(&absCtx);
avformat_close_input(&ic);
ofs.close();
}