解复用器(解封装)的相关函数
◼ avformat_alloc_context();
负责申请一个AVFormatContext结构的内存,并进行简单初始化
◼ avformat_free_context();
释放该结构里的所有东西以及该结构本身
◼ avformat_close_input();
关闭解复用器。关闭后就不再需要使用avformat_free_context 进行释放。
◼ avformat_open_input();
打开输入视频文件
◼ avformat_find_stream_info();
获取视频文件信息
◼ av_read_frame();
读取音视频包
◼ avformat_seek_file();
定位文件
◼ av_seek_frame():
定位帧
流程
测试
#include <iostream>
#include <thread>
using namespace std;
extern "C"
{
#include "libavformat/avformat.h"
#include "libavcodec/avcodec.h"
}
#pragma comment(lib,"avformat.lib")
#pragma comment(lib,"avutil.lib")
#pragma comment(lib,"avcodec.lib")
static double r2d(AVRational r)
{
return r.den == 0 ? 0 : (double)r.num / (double)r.den;
}
void XSleep(int ms)
{
//C++ 11
chrono::milliseconds du(ms);//延迟ms
this_thread::sleep_for(du);
}
int main(int argc, char* argv[])
{
//解封装
cout << " test Demux FFmpeg club" << endl;
const char* path = "1.mp4";
//1、初始化封装库
av_register_all();
//初始化网络库(可以打开rtsp rtmp http 协议的流媒体视频)
//avformat_network_init();
//参数设置
AVDictionary* opts = NULL;
//设置rtsp流已tcp协议打开
//av_dict_set(&opts, "rtsp_transport", "tcp", 0);
//设置网络延迟时间
//av_dict_set(&opts, "max_delay", "500", 0);
//解封装上下文
AVFormatContext* ic = NULL;
int re = avformat_open_input(
&ic, //上下文结构体:保存音视频的构成和基本信息
path, //音视频文件路径
0, //自动选择解封器
&opts//参数设置,比如rtsp的延迟设置
);
if (re != 0)//0为成功打开文件
{
char buf[1024] = {0};
av_strerror(re, buf, sizeof(buf) - 1);//打印错误原因
cout << " open--" << path << "--failed! : " << buf << endl;
getchar();
return -1;
}
cout << "open--" << path << "--success! : " << endl;
//获取音视频流信息,包含了视频和音频
//mp4在这里flv有所不同
re = avformat_find_stream_info(ic, 0);
//AV_TIME_BASE是时间的计数百万, ic->duration / AV_TIME_BASE是秒。
//打印总时长 ms
int total_time = ic->duration / (AV_TIME_BASE/1000);
cout << "total time : " << total_time << " ms" << endl;
//打印视频流的详细信息
av_dump_format(ic,0,path,0);
//音视频索引
int videoStream = 0;
int audioStream = 1;
//获取音视频流信息(遍历,函数获取),ic->nb_streams是媒体流的数量
for (int i = 0; i < ic->nb_streams; i++)
{
AVStream* as = ic->streams[i];
//as->codecpar->codec_type 中保存流的类别
//音频
if (as->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
{
audioStream = i;
cout << i << " : 音频信息" << endl;
cout << "sample_rate = " << as->codecpar->sample_rate << endl;
//cout << "format = " << as->codecpar->format << endl;//AVSampleFormat
cout << "channnels = " << as->codecpar->channels << endl;
//cout << "codec_id = " << as->codecpar->codec_id << endl;//AVCodecID
cout << "audio fps = " << r2d(as->avg_frame_rate) << endl;//存储浮点数是按照分子分母保存的
//音频一帧数据 是 一定量的样本数 单通道
cout<< "frame_size = " << as->codecpar->frame_size << endl;
//fps = sample_rate/frame_size
}
//视频
else if (as->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
{
videoStream = i;
cout << i <<" : 视频信息" << endl;
cout << "width = " << as->codecpar->width << endl;
cout << "height = " << as->codecpar->height << endl;
//帧率 fps 分数转换
cout << "vedio fps = " << r2d(as->avg_frame_rate) << endl;
//打印时长
int as_duration = (as->duration) * av_q2d(as->time_base);
cout << "视频的总时长为: " << as_duration / 3600 << ":" << as_duration % 3600 / 60 << ":" << as_duration % 60 << endl;
//av_q2d;
}
}
//获取视频流 另一种方法
//videoStream = av_find_best_stream(ic, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
//ic->streams[videoStream];
//获取一个包的数据
AVPacket* pkt = av_packet_alloc();//分配包的内存(堆上),需要手动释放
int pkt_count = 0;
int print_max_count = 10;//打印10个包
cout << "*****av_read_frame start*****" << endl;
while (1)
{
int ret = av_read_frame(ic, pkt);
if (ret < 0)
{
cout << "******av_read_frame end*****" << endl;
break;//开辟空间失败,就不需要再释放,即减少引用计数
//循环播放,跳转帧的位置
//int ms = 100;//根据时间基数转换位置
//long long pos = (double)ms / 1000 * r2d(ic->streams[pkt->stream_index]->time_base);
//av_seek_frame(ic, videoStream, pos, AVSEEK_FLAG_BACKWARD | AVSEEK_FLAG_FRAME);//往后找一帧,且找到关键帧
//getchar();
//continue;
}
if (pkt_count++ < print_max_count)
{
if (pkt->stream_index == audioStream)
{
//显示时间
cout << "******audio pts :"<< pkt->pts << endl;
//显示为ms,方便同步
cout << pkt->pts * r2d(ic->streams[pkt->stream_index]->time_base) * 1000 << endl;
//解码时间
cout << "******audio dts :" << pkt->dts << endl;
cout << "******audio size :" << pkt->size << endl;
cout << "******audio pos :" << pkt->pos << endl;
cout << endl;
}
else if (pkt->stream_index == videoStream)
{
cout << "******video pts :" << pkt->pts << endl;
//显示为ms
cout << pkt->pts * r2d(ic->streams[pkt->stream_index]->time_base) * 1000 << endl;
cout << "******video dts :" << pkt->dts << endl;
cout << "******video size :" << pkt->size << endl;
cout << "******video pos :" << pkt->pos << endl;
cout << endl;
}
else
cout << "unknow stream_index" << pkt->stream_index << endl;
}
//每次拿到包的数据后减少引用计数
av_packet_unref(pkt);
}
//最后释放包的内存
av_packet_free(&pkt);
if (ic)
{
//释放封装上下文,并且把ic置为0
avformat_close_input(&ic);
}
getchar();
return 0;
}