文章目录
- 一、音频流参数解析
- 1、音频流类型判定
- 2、获取音频流索引编号
- 3、获取音频流采样率
- 4、获取音频流的采样格式
- 5、获取音频流的通道数
- 6、获取音频流的压缩编码 id
- 7、获取音频流的播放时长
- 8、计算音频流的播放时长
- 二、视频流参数解析
- 1、视频流类型判定
- 2、获取视频流索引编号
- 3、获取视频流帧率
- 4、获取视频流压缩编码格式
- 5、获取视频流压缩编码格式
- 6、获取视频流播放时长
- 三、数据包解析
- 1、遍历数据包
- 2、获取数据包解码时间戳 - DTS
- 3、获取数据包显示时间戳 - PTS
- 4、获取数据包大小
- 5、获取数据包字节位置
- 四、完整代码示例
一、音频流参数解析
1、音频流类型判定
AVFormatContext 结构体 的 AVStream **streams 成员 就是 视频文件中的 所有的媒体流 , 在遍历 媒体流时 , 通过 AVStream 结构体中的 codecpar->codec_type 成员的成员参数 确定媒体流的类型 ,
如果 媒体流类型是 AVMEDIA_TYPE_AUDIO , 说明这是 音频流 ;
音频流判定代码如下 :
for (uint32_t i = 0; i < fmt_ctx->nb_streams; i++)
{
// 获取第 i 个媒体流 , 该媒体流可以是 音频流、视频流、字幕流
AVStream *in_stream = fmt_ctx->streams[i];
if (AVMEDIA_TYPE_AUDIO == in_stream->codecpar->codec_type) // 解析音频流参数
{
}
}
2、获取音频流索引编号
AVStream 结构体的 int index 成员是 媒体流 的索引编号 , 这是一个通用的字段 , 所有类型的媒体流都有该属性 ;
// 音频流 索引 编号
printf("index : %d\n", in_stream->index);
3、获取音频流采样率
要从 AVStream 中获取音频流的采样格式,可以通过关联的 AVCodecParameters 获取相关信息 ;
音频流 的 采样率 封装在 编解码器参数 AVCodecParameters *codecpar 结构体成员 中 , 是 int sample_rate 参数 , 如 : 44100Hz , 48000Hz ;
// 音频流 采样率 一般是 44100Hz 48000Hz
printf("sample_rate : %dHz\n", in_stream->codecpar->sample_rate);
4、获取音频流的采样格式
要从 AVStream 中获取音频流的采样格式 , 可以通过关联的 AVCodecParameters 获取相关信息 ;
获取的是 AVStream 结构体的 AVCodecParameters *codecpar 结构体成员的 int sample_rate 成员 ;
// 音频流 采样格式 , 也就是采样位数 , 如 :
printf("format : %d \n", in_stream->codecpar->format);
采样格式 定义在 libavutil/samplefmt.h 头文件中 的 AVSampleFormat 枚举类型中 , 如下代码所示 :
// 定义音频采样格式的枚举类型
enum AVSampleFormat {
AV_SAMPLE_FMT_NONE = -1, // 未指定的采样格式
AV_SAMPLE_FMT_U8 = 0, // 无符号8位整数
AV_SAMPLE_FMT_S16, // 有符号16位整数 (1)
AV_SAMPLE_FMT_S32, // 有符号32位整数 (2)
AV_SAMPLE_FMT_FLT, // 浮点型 (3)
AV_SAMPLE_FMT_DBL, // 双精度浮点型 (4)
AV_SAMPLE_FMT_U8P, // 无符号8位整数,平面格式 (5)
AV_SAMPLE_FMT_S16P, // 有符号16位整数,平面格式 (6)
AV_SAMPLE_FMT_S32P, // 有符号32位整数,平面格式 (7)
AV_SAMPLE_FMT_FLTP, // 浮点型,平面格式 (8)
AV_SAMPLE_FMT_DBLP, // 双精度浮点型,平面格式 (9)
AV_SAMPLE_FMT_S64, // 有符号64位整数 (10)
AV_SAMPLE_FMT_S64P, // 有符号64位整数,平面格式 (11)
AV_SAMPLE_FMT_NB // 采样格式的数量。动态链接时不要使用 (12)
};
上述 AVSampleFormat 枚举中 , 第一个枚举值是 -1 , 之后的值依次递增 , 因此枚举对应的实际值为 :
AV_SAMPLE_FMT_NONE = -1
AV_SAMPLE_FMT_U8 = 0
AV_SAMPLE_FMT_S16 = 1
AV_SAMPLE_FMT_S32 = 2
AV_SAMPLE_FMT_FLT = 3
AV_SAMPLE_FMT_DBL = 4
AV_SAMPLE_FMT_U8P = 5
AV_SAMPLE_FMT_S16P = 6
AV_SAMPLE_FMT_S32P = 7
AV_SAMPLE_FMT_FLTP = 8
AV_SAMPLE_FMT_DBLP = 9
AV_SAMPLE_FMT_S64 = 10
AV_SAMPLE_FMT_S64P = 11
AV_SAMPLE_FMT_NB = 12
获取采样格式的代码运行后 , 得到的结果是 :
format : 8
命令行 打印出的 采样格式 值为 8 , 对应上述 enum AVSampleFormat 枚举 中的值就是 AV_SAMPLE_FMT_FLTP
- 采样位数 : 是 float 单精度浮点数 , 每个采样点使用 32 位 占 4 字节 ;
- 存储布局 : 该采样的 存储布局 是 平面格式 , 也就是说 每个声道的数据存储在单独的缓冲区中 ;
与 " 平面格式 “ 存储布局相对的是 ” 交错格式 " 存储布局 , 交错格式 是 将所有声道的数据交错存储在一个缓冲区中 ;
5、获取音频流的通道数
要从 AVStream 结构体 中获取音频流的通道数 , 可以通过 与之关联的 AVCodecParameters 的 channels 字段获取 ;
每个 AVStream 都包含一个 AVCodecParameters *codecpar 结构体成员
av_stream->codecpar->channels 就可以 获取 音频流的通道数
- 单声道为 1
- 立体声为 2
- 5.1 声道为 6
代码示例如下 :
// 音频流 声道数 , 单声道有 1 个 , 立体声有 2 个
printf("channel num : %d \n", in_stream->codecpar->channels);
执行后 , 得到 如下结果 , 有 2 个声道 , 是 双声道 立体声 ;
channel num : 2
6、获取音频流的压缩编码 id
通过 AVStream 媒体流 结构体的 AVCodecParameters *codecpar 成员 的 enum AVCodecID codec_id 字段 , 可以获取 音频流的压缩编码器 ID ;
音视频编解码器 ID 都定义在 enum AVCodecID 枚举 中 , 该枚举 定义在 libavcodec/avcodec.h 头文件中 ;
使用如下代码 , 可打印出 编解码器 ID ;
// 音频流 压缩编码 ID
printf("codec_id : %d \n", in_stream->codecpar->codec_id);
最终打印出的结果 : 86018 ;
codec_id : 86018
十进制 86018 对应的 十六进制数为 0x15002 ,
到 libavcodec/avcodec.h 头文件 中查找 enum AVCodecID 枚举 , 可以看到 音频编解码器 从 0x15000 开始 , 第三个就是 AV_CODEC_ID_AAC ;
在 MediaInfo 中打开 该 test.flv 文件 , 可以看到音频流的 编码格式是 AAC 格式 ;
7、获取音频流的播放时长
通过 AVStream 媒体流 结构体的 int64_t duration 成员 , 可以获取 音频流的播放时长 ;
这个 播放时长 的 时间单位 不是固定的 , 还需要 结合 AVStream 中的 时间基 AVRational time_base 成员进行计算 ;
AVStream->duration 播放时长 是以 time_base 为单位表示的 ;
如果 AVStream->duration 的值为 AV_NOPTS_VALUE , 说明没有得到有效的播放时长 , FLV 格式 或者 网络流 会得到该 无效时间 ;
#define AV_NOPTS_VALUE ((int64_t)UINT64_C(0x8000000000000000))
十六进制的 0x8000000000000000 值 , 对应 十进制值为 -9223372036854775808 ;
打印 音频流 时长 代码示例 : 下面的代码中打印出的时长是 -9223372036854775808 ( 十进制 ) , 对应的十六进制数为 0x8000000000000000 值 , 因此没有得到有效的 音频播放时长 ;
// 音频流 播放时长
printf("audio duration : %lld\n", in_stream->duration);
// 音频流 播放时长 , 获取的是总的秒数
if(in_stream->duration == AV_NOPTS_VALUE)
{
// #define AV_NOPTS_VALUE ((int64_t)UINT64_C(0x8000000000000000))
// 得到一个无效的播放时长
printf("audio duration unknown");
}
执行结果 :
audio duration : -9223372036854775808
duration_audio unknown
8、计算音频流的播放时长
AVFormatContext 结构体 中的 int64_t duration 成员 表示 的 播放时长 , 单位是 微秒 ;
#define AV_TIME_BASE 1000000
计算该时长的时候 , 只需要 除以 AV_TIME_BASE 值 即可 得到 播放时长 对应的 秒数 ;
// 计算总的秒数
total_seconds = (fmt_ctx->duration) / AV_TIME_BASE;
FFmpeg 中 需要保存 精确值 的时候 , 一般会保存分数 , 尽量将精确的数值展现给开发者 ;
AVStream 中的其单位是 AVStream 中的 AVRational time_base 成员 ;
AVRational 是一个分数结构体 , int num 成员是 分数的 分子 , int den 成员是 分数的 分母 ;
typedef struct AVRational {
int num; // 分子
int den; // 分母
} AVRational;
48000Hz 采样率的音频的 分子是 1 , 分母是 48000 ;
av_q2d 函数 用于将 AVRational 分数 类型 转换为 双精度浮点数 ;
av_q2d 函数原型 :
static inline double av_q2d(AVRational a);
计算 音频播放时长 代码示例 :
// 计算出实际播放时长 , 单位秒
int duration_audio = (in_stream->duration) * av_q2d(in_stream->time_base);
printf("audio duration : %d\n", duration_audio);
执行结果 :
audio duration : 222 , num : 1 , den : 48000
使用 MediaInfo 打开该 test.mp4 视频文件 , 可以看到该视频的 时长是 3分42秒 , 正好是 222 秒 ;
二、视频流参数解析
1、视频流类型判定
遍历 AVFormatContext 结构体 的 AVStream **streams 指针数组
判定 AVStream 结构体中的 AVCodecParameters *codecpar 成员 的 enum AVMediaType codec_type 成员 , 该枚举成员是 媒体流的类型 , 如果 媒体流类型是 AVMEDIA_TYPE_VIDEO , 说明这是 视频流 ;
音频流判定代码如下 :
if (AVMEDIA_TYPE_VIDEO == in_stream->codecpar->codec_type) //解析视频流参数
{
}
2、获取视频流索引编号
AVStream 结构体 的 int index 成员 , 就是 媒体流 的索引编号
// 视频流 索引 编号
printf("index : %d\n", in_stream->index);
3、获取视频流帧率
在 FFmpeg 中 , 可以通过 AVStream 结构体 的 AVRational avg_frame_rate 成员 或 AVRational r_frame_rate 成员 获取 视频流 的帧率 , 帧率 表示 视频 每秒显示的帧数 , 单位 fps ( Frame per Second ) ;
- avg_frame_rate 表示 视频流 的 平均帧率
- r_frame_rate 表示 视频流 中 帧率估计值
代码示例 : 平均帧率是 14.464607 FPS , 帧率估值是 15 FPS ;
// 视频流 帧率 , avg_frame_rate 单位 fps , frame per second 每秒多少帧
// 平均帧率 , 适用于可变帧率的视频
printf("avg_frame_rate fps : %lffps , num : %d , den ; %d\n", av_q2d(in_stream->avg_frame_rate), in_stream->avg_frame_rate.num, in_stream->avg_frame_rate.den);
// 帧率的估计值
printf("r_frame_rate fps : %lffps , num : %d , den ; %d\n", av_q2d(in_stream->r_frame_rate), in_stream->r_frame_rate.num, in_stream->r_frame_rate.den);
执行结果 :
avg_frame_rate fps : 14.464607fps , num : 48225 , den ; 3334
r_frame_rate fps : 15.000000fps , num : 15 , den ; 1
4、获取视频流压缩编码格式
通过 AVStream 媒体流 结构体的 AVCodecParameters *codecpar 成员 的 enum AVCodecID codec_id 字段 , 可以获取 视频流的压缩编码器 ID ;
之前的音频流的压缩编码 ID 也是通过这个方式获取的 , 参考 一、音频流参数解析 6、获取音频流的压缩编码 id 章节 ;
代码示例 : 使用下面的代码可以打印 视频流的 编解码器 ID ;
// 视频流 的 压缩编码格式
// AV_CODEC_ID_AAC
printf("codec_id:%d\n", in_stream->codecpar->codec_id);
执行结果是 :
codec_id:27
在 enum AVCodecID 枚举字段中查找 27 , 是 AV_CODEC_ID_H264 , 这是 H.264 视频格式 ;
5、获取视频流压缩编码格式
在 FFmpeg 中 , 通过 AVStream 结构体 中的 AVCodecParameters *codecpar 成员 来 获取视频画面的 宽度 和 高度 ,
- AVCodecParameters 结构体 中的 int width 成员 是 视频画面的 宽度
- AVCodecParameters 结构体 中的 int height 成员 是 视频画面的 高度
代码示例 : 在下面的代码中 , 打印视频画面 宽高 ;
// 视频画面 宽度 和 高度
printf("width : %d , height : %d\n", in_stream->codecpar->width, in_stream->codecpar->height);
执行结果 :
width : 1920 , height : 1080
使用 MediaInfo 打开 test.mp4 视频文件 , 可以看到 该视频文件的宽高 , 就是 1920 x 1080 ;
6、获取视频流播放时长
视频流 的 播放时长 与 音频流 的播放时长 获取方式 是一样的 ;
- AVStream 结构体中的 int64_t duration 成员 就是视频流的播放时长 ;
- 视频播放时长 的 单位 是 AVStream 中的 时间基 AVRational time_base 成员 ;
更多细节 参考 上面的 一、音频流参数解析 7、获取音频流的播放时长 章节 ;
代码示例 : 下面的代码 , 打印出了视频的播放时长 , 以及将其转为 秒 单位的值 ;
// 视频流 播放时长
printf("vedio duration : %lld\n", in_stream->duration);
// 视频流 播放时长 , 获取的是总的秒数
if(in_stream->duration == AV_NOPTS_VALUE)
{
// #define AV_NOPTS_VALUE ((int64_t)UINT64_C(0x8000000000000000))
// 得到一个无效的播放时长
printf("vedio duration unknown");
}
else
{
// 计算出实际播放时长 , 单位秒
int duration_audio = (in_stream->duration) * av_q2d(in_stream->time_base);
printf("vedio duration : %d , num : %d , den : %d\n",
duration_audio, in_stream->time_base.num, in_stream->time_base.den);
}
三、数据包解析
1、遍历数据包
遍历 AVPacket 数据包 , 首先要 初始化 AVPacket 结构体 , 之后 视频流 或 音频流 中的数据 填充到该结构体中 ;
启动一个循环 , 在循环体中 , 调用 av_read_frame 函数 , 读取 AVPacket 数据包 , 开始遍历 流 中的数据包 ;
// 开始解析 数据包
AVPacket *pkt = av_packet_alloc();
while (1)
{
// 读取数据包
ret = av_read_frame(fmt_ctx, pkt);
// 解析 AVPacket 数据包
}
2、获取数据包解码时间戳 - DTS
DTS 解码时间戳 ( Decoding Timestamp ) 是指 数据帧 被解码的时间点 , 单位是 时间基 time_base , 用于确保解码器按正确的顺序解码帧 ;
DTS 主要用于控制帧的 解码顺序 , 特别是在包含 B 帧 的编码中 , B 帧可能需要先解码后面的帧 ;
- B 帧 是 双向内插帧 ( Bi-directional Predicted Frames ) , 记录的是 本帧 B 帧 与 前后 I 帧 或 P 帧 的差别 ;
AVPacket 中的 int64_t pts 成员就是 数据包 的 解码时间戳 ;
typedef struct AVPacket {
/**
* 表示时间戳,单位为 AVStream->time_base;即解压后的数据包将呈现给用户的时间。
* 如果文件中未存储该值,可以为 AV_NOPTS_VALUE。
* pts 必须大于或等于 dts,因为呈现不能发生在解压之前,除非是想查看十六进制转储。
* 一些格式错误使用了 dts 和 pts/cts 的术语来表示不同的含义。
* 在存储到 AVPacket 之前,必须将这些时间戳转换为真正的 pts/dts。
*/
int64_t pts;
}
代码示例 : 直接获取 AVPacket 中的 pts 成员 ;
printf("audio pts: %lld\n", pkt->pts);
3、获取数据包显示时间戳 - PTS
PTS 显示时间戳 ( Presentation Timestamp ) 是 帧或音频样本 在播放过程中应该 显示或播放 的时间 , 单位是 时间基 time_base , 主要用于同步音视频的播放 ;
AVPacket 中的 int64_t pts 成员就是 数据包 的 解码时间戳 ;
typedef struct AVPacket {
/**
* 解压时间戳,单位为 AVStream->time_base;即数据包被解压的时间。
* 如果文件中未存储该值,可以为 AV_NOPTS_VALUE。
*/
int64_t dts;
}
代码示例 : 直接获取 AVPacket 中的 dts 成员 ;
printf("audio dts: %lld\n", pkt->dts);
4、获取数据包大小
在 AVPacket 结构体中 , size 字段的作用是表示数据包中实际存储的数据大小 , 单位 字节 , 这是 data 字段指向的缓冲区的大小 ;
printf("audio size: %d\n", pkt->size);
5、获取数据包字节位置
AVPacket 中的 int64_t pos 成员就是 数据包 在 媒体流 的 字节位置 ;
typedef struct AVPacket {
int64_t pos; ///< byte position in stream, -1 if unknown
}
代码示例 :
printf("audio pos: %lld\n", pkt->pos);
四、完整代码示例
完整代码示例 :
#include <stdio.h>
#include <libavutil/avutil.h>
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
int main(int argc, char **argv)
{
// 打印 FFmpeg 版本号
printf("FFmpeg version is %s\n", av_version_info());
// 初始化网络环境
// 如果只打开本地文件 , 则不需要调用该函数
//avformat_network_init();
// 1. 获取命令行参数
// 要解封装的 视频文件 的 相对路径
char *file_name = "test.ts";
// 打印参数基本信息
printf("argc = %d , argv[0] = %s\n", argc, argv[0]);
// 检测 参数中 是否传入了 参数
// 命令行中 可执行程序 是第 0 个参数 , 之后的附加参数是第 1 个参数
if(argc >= 2 && argv[1] != NULL) {
file_name = argv[1];
// 打印传入的参数
printf("argv[1] = %s\n", argv[1]);
}
// 2. 打开视频文件
// 描述 媒体文件 或 媒体流 的构成和基本信息
AVFormatContext *fmt_ctx = NULL;
// 打开文件 , 探测协议类型 , 将获取的参数信息 填充到 AVFormatContext 结构体中
int ret = avformat_open_input(&fmt_ctx, file_name, NULL, NULL);
// avformat_open_input 函数 : 执行成功返回 0 , 执行失败返回负数 ( 错误码 )
if (ret < 0)
{
// 用于接收错误信息的字符串
char buf[1024] = { 0 };
// 获取 负数错误码 对应的 错误日志信息
av_strerror(ret, buf, sizeof(buf) - 1);
// 打印错误信息
printf("avformat_open_input failed : %s\n", file_name, buf);
goto failed;
}
// 3. 获取 媒体流 详细信息
ret = avformat_find_stream_info(fmt_ctx, NULL);
// 获取 码流 信息失败 , 返回 负数错误码
if (ret < 0)
{
// 用于接收错误信息的字符串
char buf[1024] = { 0 };
// 获取 负数错误码 对应的 错误日志信息
av_strerror(ret, buf, sizeof(buf) - 1);
// 打印错误信息
printf("avformat_find_stream_info failed:%s\n", file_name, buf);
goto failed;
}
// 4. 打印 视频文件 的 参数信息 ☆☆☆
printf_s("\n==== av_dump_format start ===\n\n");
av_dump_format(fmt_ctx, 0, file_name, 0);
printf_s("\n==== av_dump_format end ===\n");
// 5. 获取文件路径或地址
printf("url : %s\n", fmt_ctx->url);
// 6. 获取码流个数
printf("stream number : %d\n", fmt_ctx->nb_streams);
// 7. 获取播放时长
int total_seconds, hour, minute, second;
// duration: 播放时长 , 单位 微妙 (us) , 1000us = 1ms , 1000ms = 1秒
// 计算总的秒数
total_seconds = (fmt_ctx->duration) / AV_TIME_BASE;
// 计算小时数
hour = total_seconds / 3600;
// 计算分钟数
minute = (total_seconds % 3600) / 60;
// 计算秒数
second = (total_seconds % 60);
printf("duration: %02d:%02d:%02d\n\n", hour, minute, second);
// 8. 获取视频文件的总比特率 , 单位为bps
printf("\nbit_rate : %lld bps\n",(int64_t)(fmt_ctx->bit_rate));
// 9. 遍历媒体流
int video_index = -1; // 视频索引
int audio_index = -1; // 音频索引
for (uint32_t i = 0; i < fmt_ctx->nb_streams; i++)
{
// 获取第 i 个媒体流 , 该媒体流可以是 音频流、视频流、字幕流
AVStream *in_stream = fmt_ctx->streams[i];
if (AVMEDIA_TYPE_AUDIO == in_stream->codecpar->codec_type) // 解析音频流参数
{
printf("\n\n==== Audio info ===\n\n");
// 设置 音频流 参数索引
audio_index = i;
// 音频流 索引 编号
printf("index : %d\n", in_stream->index);
// 音频流 采样率 一般是 44100Hz 48000Hz
printf("sample_rate : %dHz\n", in_stream->codecpar->sample_rate);
// 音频流 采样格式 , 也就是采样位数
printf("format : %d \n", in_stream->codecpar->format);
// 音频流 声道数 , 单声道有 1 个 , 立体声有 2 个
printf("channel num : %d \n", in_stream->codecpar->channels);
// 音频流 压缩编码 ID
// AV_CODEC_ID_AAC
printf("codec_id : %d \n", in_stream->codecpar->codec_id);
// 音频流 播放时长
printf("audio duration : %lld\n", in_stream->duration);
// 音频流 播放时长 , 获取的是总的秒数
if(in_stream->duration == AV_NOPTS_VALUE)
{
// #define AV_NOPTS_VALUE ((int64_t)UINT64_C(0x8000000000000000))
// 得到一个无效的播放时长
printf("audio duration unknown");
}
else
{
// 计算出实际播放时长 , 单位秒
int duration_audio = (in_stream->duration) * av_q2d(in_stream->time_base);
printf("audio duration : %d , num : %d , den : %d\n",
duration_audio, in_stream->time_base.num, in_stream->time_base.den);
}
}
else if (AVMEDIA_TYPE_VIDEO == in_stream->codecpar->codec_type) //解析视频流参数
{
printf("\n\n==== Vedio info ===\n\n");
// 设置 视频流 参数索引
video_index = i;
// 视频流 索引 编号
printf("index : %d\n", in_stream->index);
// 视频流 帧率 , avg_frame_rate 单位 fps , frame per second 每秒多少帧
// 平均帧率 , 适用于可变帧率的视频
printf("avg_frame_rate fps : %lf FPS , num : %d , den ; %d\n", av_q2d(in_stream->avg_frame_rate), in_stream->avg_frame_rate.num, in_stream->avg_frame_rate.den);
// 帧率的估计值
printf("r_frame_rate fps : %lf FPS , num : %d , den ; %d\n", av_q2d(in_stream->r_frame_rate), in_stream->r_frame_rate.num, in_stream->r_frame_rate.den);
// 视频流 的 压缩编码格式
// AV_CODEC_ID_AAC
printf("codec_id:%d\n", in_stream->codecpar->codec_id);
// 视频画面 宽度 和 高度
printf("width : %d , height : %d\n", in_stream->codecpar->width, in_stream->codecpar->height);
// 视频流 播放时长
printf("vedio duration : %lld\n", in_stream->duration);
// 视频流 播放时长 , 获取的是总的秒数
if(in_stream->duration == AV_NOPTS_VALUE)
{
// #define AV_NOPTS_VALUE ((int64_t)UINT64_C(0x8000000000000000))
// 得到一个无效的播放时长
printf("vedio duration unknown");
}
else
{
// 计算出实际播放时长 , 单位秒
int duration_audio = (in_stream->duration) * av_q2d(in_stream->time_base);
printf("vedio duration : %d , num : %d , den : %d\n",
duration_audio, in_stream->time_base.num, in_stream->time_base.den);
}
}
}
// 开始解析 数据包
AVPacket *pkt = av_packet_alloc();
// 统计数据包个数
int pkt_count = 0;
printf("\n\n==== Packet info ===\n\n");
while (1)
{
// 读取数据包
ret = av_read_frame(fmt_ctx, pkt);
// 数据包读取失败处理
if (ret < 0)
{
printf("av_read_frame failed\n");
break;
}
// 只解析 5 个数据包
if(pkt_count++ < 5)
{
if (pkt->stream_index == audio_index) // 音频包
{
printf("audio pts: %lld\n", pkt->pts);
printf("audio dts: %lld\n", pkt->dts);
printf("audio size: %d\n", pkt->size);
printf("audio pos: %lld\n", pkt->pos);
printf("audio duration: %lf\n\n",
pkt->duration * av_q2d(fmt_ctx->streams[audio_index]->time_base));
}
else if (pkt->stream_index == video_index) // 视频包
{
printf("video pts: %lld\n", pkt->pts);
printf("video dts: %lld\n", pkt->dts);
printf("video size: %d\n", pkt->size);
printf("video pos: %lld\n", pkt->pos);
printf("video duration: %lf\n\n",
pkt->duration * av_q2d(fmt_ctx->streams[video_index]->time_base));
}
else
{
printf("unknown stream_index:\n", pkt->stream_index);
}
}
// 解析玩后 解除 AVPacket 引用
av_packet_unref(pkt);
}
if(pkt)
{
// 释放 AVPacket
av_packet_free(&pkt);
}
failed: // FFmpeg 出错 , 函数执行失败后 释放内存
if(fmt_ctx){
// 与 avformat_open_input 配对使用 , 防止内存泄漏
avformat_close_input(&fmt_ctx);
}
// 执行结束
printf("\nFFmpeg End\n");
// 防止 命令行终端 打印完信息 后 马上退出
//getchar();
return 0;
}
执行结果 :
FFmpeg version is 4.2.1
argc = 2 , argv[0] = D:\002_Project\008_Qt\build-FFmpegC-Desktop_Qt_5_14_2_MSVC2015_32bit-Debug\debug\FFmpegC.exe
argv[1] = test.mp4
==== av_dump_format start ===
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'test.mp4':
Metadata:
major_brand : isom
minor_version : 512
compatible_brands: isomiso2avc1mp41
encoder : Lavf56.38.102
comment : www.ieway.cn
Duration: 00:03:42.53, start: 0.000000, bitrate: 281 kb/s
Stream #0:0(und): Video: h264 (Constrained Baseline) (avc1 / 0x31637661), yuv420p, 1920x1080, 150 kb/s, 14.46 fps, 15 tbr, 15360 tbn, 30 tbc (default)
Metadata:
handler_name : VideoHandler
Stream #0:1(und): Audio: aac (LC) (mp4a / 0x6134706D), 48000 Hz, stereo, fltp, 128 kb/s (default)
Metadata:
handler_name : SoundHandler
==== av_dump_format end ===
url : test.mp4
stream number : 2
duration: 00:03:42
bit_rate : 281340 bps
==== Vedio info ===
index : 0
avg_frame_rate fps : 14.464607 FPS , num : 48225 , den ; 3334
r_frame_rate fps : 15.000000 FPS , num : 15 , den ; 1
codec_id:27
width : 1920 , height : 1080
vedio duration : 3414016
vedio duration : 222 , num : 1 , den : 15360
==== Audio info ===
index : 1
sample_rate : 48000Hz
format : 8
channel num : 2
codec_id : 86018
audio duration : 10680624
audio duration : 222 , num : 1 , den : 48000
==== Packet info ===
audio pts: -678
audio dts: -678
audio size: 341
audio pos: 48
audio duration: 0.021333
video pts: 0
video dts: 0
video size: 66736
video pos: 389
video duration: 0.066667
audio pts: 346
audio dts: 346
audio size: 341
audio pos: 67125
audio duration: 0.021333
audio pts: 1370
audio dts: 1370
audio size: 342
audio pos: 67466
audio duration: 0.021333
audio pts: 2394
audio dts: 2394
audio size: 341
audio pos: 67808
audio duration: 0.021333
av_read_frame failed
FFmpeg End