结构
AVFormatContext
1> AVIOContext *pb:文件IO的上下文,自定义格式时使用
2> char filename[1024]:保存打开的文件名,经常用到,例如断开重连
3> unsigned int nb_streams:流数量
4> AVStream **streams:具体流内容,通常只有视频、音频,偶尔也会有字幕之类的
5> int64_t duration:总长度,以AV_TIME_BASE(通常为1000000)为单位,相当于使用微秒(us)为单位,注意这个值不一定能够获取到,如果获取不到可以通过帧数计算
6> int64_t bit_rate:比特率 1s中有多少bit
AVStream
1> AVCodecContext *codec:解码器,该参数已经过时
2> AVRational time_base:时间基数,分数,通常分子=>1,分母=>9000
3> int64_t duration:时长,duration * (time_base.num / time_base.den) 需要考虑除零
4> AVRational avg_frame_rate:帧率,对于视频来说一帧数据就是一张图片,对于音频来说就是一定量的样本数,具体一帧数据存多少样本数由codecpar->frame_size决定
5> AVCodecParameters *codecpar:音视频参数,主要用于替代codec
AVCodecParameters
1> enum AVMediaType codec_type:编码类型,音频/视频
2> enum AVCodecID codec_id:编码格式,H264格式等
3> uint32_t codec_tag:用四个字节表示编码器,通常用不到
4> int format:视频像素格式/音频采样格式
5> int width, int height:视频宽高,仅视频有;不一定有,如果没有可以使用解码后的frame中的宽高
6> uint64_t channel_layout,int channels,int sample_rate,int frame_size:声道,如三声道(数值与channels二进制十进制数相同),声道数,样本率,样本大小(单通道样本数),仅音频有
AVPacket
1> AVBufferRef *buf:用于存储引用计数的一块空间,packet增加的时候引用计数+1,减少的时候引用计数-1
2> int64_t pts:显示时间
3> int64_t dts:解码时间
4> uint8_t *data, int size:由ffmpeg创建和删除(不同帧数据量不同),保存帧数据
注意:在没有B帧的情况下pts = dts
AVPacket相关函数:av_packet_alloc(创建并初始化一个packet)、av_packet_clone(复制并增加一次引用计数)、av_packet_ref(手动加一次引用)、av_packet_unref(手动减一次引用)、av_packet_free(空间清理)、av_init_packet(为packet设置默认值)、av_packet_from_data(给定数据生成一个packet)、av_copy_packet(废弃的函数,注意不要再使用)
函数
1> av_register_all:注册所有的解封装和加封装格式,新版本的ffmpeg已经废弃该函数,可以不需要再调用
2> avformat_network_init:支持网络rtsp/rtmp/http数据流
3> avformat_open_input:打开音视频文件
@param AVFormatContext **ps:需要注意ps不能为空,*ps可以为空,当*ps为空,会在内部创建存储空间,如果不传空可在外部先创建好空间但清理需要在外部处理
@param const char *url:支持网络rtsp、http、本地路径
@AVInputFormat *fmt:输入文件的格式,通常不需要指定,不指定的情况下由ffmpeg自己检测输入文件格式
@AVDictionary **options:输入参数字典,具体有哪些参数可以参考源码ffmpeg-4.3.1\libavcodec\options_table.h里面的定义;可以使用方法av_dict_set设置参数,比如设置rtsp超时时间
@return:0表示正常,非0返回错误码
4> avformat_find_stream_info:获取流信息
5> av_dump_format:打印流详细信息
@param AVFormatContext **ps
@param int index:用于打印(没啥用)
@param const char *url:用于打印(没啥用)
@param int is_output:context是输入(0)或输出(1)
6> av_find_best_stream:获取音视频流信息
@param AVFormatContext **ps
@param enum AVMediaType type
@param int wanted_stream_nb:通常设为-1,自动选择
@param int ratedstream:相关流,通常用不到,设为-1
@param AVCodec **decoder_ret:解码时用到,通常也不设置
@param int flags:保留字段
7> av_read_frame:读取一帧数据
@param AVFormatContext **ps
@param AVPacket *pkt:不能传null,需要预分配空间作为输出参数
8> av_seek_frame:移动到索引的frame
@param AVFormatContext **ps
@param stream_index:索引,-1表示default,通常使用视频来做seek,使用音频seek有可能移到视频某个非关键帧的位置
@int64_t timestamp:移动到位置的时间戳
@int flags:标志位 AV_SEEKFLAG_BACKWARD[1]:往后找 AV_SEEKFLAG_FRAME[8]:只跳到关键帧
9> avformat_close_input:关闭打开的音视频文件
10> av_strerror:失败时存放错误信息
示例程序
代码:
#include <QCoreApplication>
#include <iostream>
using namespace std;
#include <thread>
extern "C" {
#include <libavformat/avformat.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 : r.num / r.den;
}
void xsleep(uint32_t ms)
{
chrono::milliseconds du(ms);
this_thread::sleep_for(du);
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
// 初始化封装库 @deprecated
// av_register_all();
// 初始化网络库
avformat_network_init();
// 解封装上下文
AVFormatContext *ic = nullptr;
const char *path = "1080.mp4";
// 参数设置
AVDictionary *opts = nullptr;
// 设置rtsp以tcp方式传输
av_dict_set(&opts, "rtsp_transport", "tcp", 0);
// 设置网络超时时间
av_dict_set(&opts, "max_delay", "500", 0);
// 打开解封装的音视频文件
int ret = avformat_open_input(&ic, path, nullptr, &opts);
if (0 != ret) {
char buf[1024] = {0};
av_strerror(ret, buf, sizeof(buf) - 1);
cout << "file open failed:" << buf << endl;
return -1;
}
// 获取流信息
avformat_find_stream_info(ic, nullptr);
// 打印时长 s
int nTime = ic->duration / AV_TIME_BASE;
cout << "total time : " << nTime << endl;
// 打印流详细信息
av_dump_format(ic, 0, nullptr, 0);
// 打印分割
cout << endl;
// 记录音视频索引号,便于读取frame
int VideoStreamIndex = 0;
int AudioStreamIndex = 1;
// 获取音视频流信息(遍历方式)
for (uint32_t i = 0; i < ic->nb_streams; ++i) {
AVStream *stream = ic->streams[i];
// 音频
if (stream->codecpar->codec_type == AVMediaType::AVMEDIA_TYPE_AUDIO) {
AudioStreamIndex = i;
cout << i << "#Audio" << endl;
cout << "codecid:" << stream->codecpar->codec_id << endl;
cout << "channels:" << stream->codecpar->channels << endl;
cout << "channel_layout:" << stream->codecpar->channel_layout << endl;
cout << "format:" << stream->codecpar->format << endl;
cout << "samplerate:" << stream->codecpar->sample_rate << endl;
// 帧率
cout << "av_rate:" << r2d(stream->avg_frame_rate) << endl;
cout << "frame_size:" << stream->codecpar->frame_size << endl;
// fps
cout << "fps:" << stream->codecpar->sample_rate / stream->codecpar->frame_size << endl;
}
// 视频
if (stream->codecpar->codec_type == AVMediaType::AVMEDIA_TYPE_VIDEO) {
VideoStreamIndex = i;
cout << i << "#Video" << endl;
cout << "codecid:" << stream->codecpar->codec_id << endl;
cout << "width:" << stream->codecpar->width << endl;
cout << "height:" << stream->codecpar->height << endl;
cout << "format:" << stream->codecpar->format << endl;
// 帧率 fps
cout << "av_rate:" << r2d(stream->avg_frame_rate) << endl;
cout << "frame_size:" << stream->codecpar->frame_size << endl;
}
// 打印分割
cout << endl;
}
// 获取音视频流信息(索引方式)
AudioStreamIndex = av_find_best_stream(ic, AVMediaType::AVMEDIA_TYPE_AUDIO, -1, -1, 0, 0);
// AVStream *auStream = ic->streams[AudioStreamIndex];
// VideoStreamIndex = av_find_best_stream(ic, AVMediaType::AVMEDIA_TYPE_VIDEO, -1, -1, 0, 0);
// 分配并初始化AVPacket
AVPacket *pkt = av_packet_alloc();
while(1) {
// 读取帧数据
ret = av_read_frame(ic, pkt);
if (0 != ret) {
cout << "Read Frame End!" << endl;
getchar();
// 播放结束从3s处重新播放
int ms = 3000; // 3s位置
long long pos = ms / 1000.0 * r2d(ic->streams[pkt->stream_index]->time_base);
av_seek_frame(ic, VideoStreamIndex, pos, AVSEEK_FLAG_BACKWARD | AVSEEK_FLAG_FRAME);
continue;
}
// 大小
cout << "size = " << pkt->size << endl;
// 显示时间
cout << "pts = " << pkt->pts << endl;
cout << "pts ms = " << pkt->pts * (r2d(ic->streams[pkt->stream_index]->time_base) * 1000) << endl;
// 解码时间
cout << "dts = " << pkt->dts << endl;
if (pkt->stream_index == AudioStreamIndex) {
cout << "Audio Frame" << endl;
}
if (pkt->stream_index == VideoStreamIndex) {
cout << "Video Frame" << endl;
}
cout << endl;
// 线程延迟,不要打印太快
xsleep(500);
// 引用计数减1,为0时释放空间
av_packet_unref(pkt);
}
av_packet_free(&pkt);
// 关闭解封装的音视频文件
if (nullptr != ic) {
avformat_close_input(&ic);
ic = nullptr;
}
return a.exec();
}
输出: