添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

本文是我的《FFMPEG Tips》系列的第三篇文章,上篇文章 介绍了如何提取整个音视频码流的媒体信息,包括:封装格式、编码格式、视频的分辨率、帧率、码率、音频的采样率、位宽、通道数等等,而本文则关注得更细一点,看看如何利用 ffmpeg 读取码流中每一帧的信息。

1. 码流中每一帧的哪些信息值得关注 ?

[ ] 音频帧还是视频帧
[ ] 关键帧还是非关键帧
[ ] 帧的数据和大小
[ ] 时间戳信息

2. 为什么要关注这些信息 ?

[ ] 音频帧还是视频帧 -> 分别送入音频/视频×××
[ ] 关键帧还是非关键帧 -> 追帧优化
[ ] 帧的数据和大小 -> 取出帧的内容
[ ] 时间戳信息 -> 音视频同步

3. 如何从 ffmpeg 取出这些信息 ?

ffmpeg 提供了一个函数 av_read_frame 来完成解封装的过程,它会从码流里面提取每一个音频、视频帧,它使用了结构体 AVPacket 来记录每一帧的信息。

读取一帧数据的代码示例如下(ic 即为 AVFormatContext 对象,码流的上下文句柄):

AVPacket avpkt;
av_init_packet(&avpkt);
while (!interrupt) {
    int ret = av_read_frame(ic, &avpkt);
    if (ret < 0) {
        break;
    // processing
av_free_packet(&avpkt);

每循环一次,就从码流中解封装并且提取了一帧数据,并存放在了 AVPacket 结构体中。

3.1 如何判断是音频帧还是视频帧

上一篇文章我们提到过,使用下面的方法,获取码流中的 video_stream_idx 和 audio_stream_idx

int video_stream_idx = av_find_best_stream(ic, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
int audio_stream_idx = av_find_best_stream(ic, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);

那么,此时就派上用场了,每一个 AVPacket 都有一个成员变量:stream_index,由该成员变量即可判断这个 Packet 到底是音频还是视频了:

if (avpkt.stream_index == video_stream_idx) {
    LOGD("read a video frame");
} else if (avpkt.stream_index == audio_stream_idx) {
    LOGD("read audio frame);

3.2 如何判断是否为关键帧

判断是否为关键帧的方法也比较简单,示例如下:

if (avpkt.flags & AV_PKT_FLAG_KEY) {
    LOGD("read a key frame");

3.3 如何获取帧的数据和大小

帧的数据和大小直接定义在 AVPacket 结构体中,对应的成员变量如下:

// 压缩编码的数据,一帧音频/视频
uint8_t *data;
// 数据的大小
int size;

3.4 如何获取帧的时间戳信息

每一个帧都可能携带有 2 个时间戳信息,一个是解码时间戳 dts,一个是显示时间戳 pts,解码时间戳告诉我们什么时候需要解码,显示时间戳告诉我们什么时候需要显示,只有在码流中存在 B 帧的情况下,这两个时间戳才会不一致。

这些时间戳信息不一定存在于码流中(取决于生产端),如果不存在,则其值为:AV_NOPTS_VALUE

一定要选择正确地方式打印时间戳,时间戳是使用 long long 来表示的,即 int64_t,因此打印的时候,需要使用 “%lld” 来打印,例如:

while (!interrupt) {
    int ret = av_read_frame(player->ic, &avpkt);
    if (ret < 0) {
        break;
    if (avpkt.stream_index == video_stream_idx) {
        LOGD("read video frame, timestamp = %lld \n”, avpkt.pts);
    } else if (avpkt.stream_index == audio_stream_idx) {
        LOGD("read audio frame, timestamp = %lld \n”, avpkt.pts);

由此,我们就可以通过这些 log 信息调试一下某一段音视频流的时间戳是否正确,比如是否出现了时间戳的回滚和错乱,则必然会导致播放端出现音视频不同步或者显示异常等情况。

4. 小结

关于如何使用 FFMPEG 如何读取每一帧的信息就介绍到这儿了。

转载于:https://blog.51cto.com/ticktick/1872008

本文是我的《FFMPEG Tips》系列的第三篇文章,上篇文章 介绍了如何提取整个音视频码流的媒体信息,包括:封装格式、编码格式、视频的分辨率、帧率、码率、音频的采样率、位宽、通道数等等,而本文则关注得更细一点,看看如何利用 ffmpeg 读取码流中每一帧的信息。1. 码流中每一帧的哪些信息值得关注 ?[ ] 音频帧还是视频帧[ ] 关键帧还是非关键帧[ ] 帧的数据和大小[ ]... 当用脚本从网页中获取视频流时,怎么才能从已下载的数据中得到还有多长时间视频才可以下载完成。 使用ffmpeg工具中的ffprobe命令工具可以得到所有帧信息,我们只要知道最后一帧的时间和视频的总时长,就可以得到还有多长时间的视频没有下载到 使用下载的命令,我们可以得到视频总时长信息 ffprobe -show_format video.mp4 得到的信息
使用-r参数可以对视频的帧率进行设置,那么是写成ffmpeg -i input -r n output还是ffmpeg -r n -i input output呢? 实际上-r参数用来设置输入文件和输出文件的效果是不一样的 前者决定了文件的时长,如果总帧数为N我们设置帧率为n,那么文件的长度就是N/n。如果未指定帧率,默认为25。 后者可以用来降低码流,也就说对于一秒的n帧,我们想让输出文件
对于视频的编解码来说,要对数据进行解码,那么首先要获取视频帧的压缩数据。 av_read_frame()的作用就是获取视频的数据。 注:av_read_frame()获取视频的一帧,不存在半帧说法。但可以获取音频的若干帧。 说明①:av_read_frame()函数是ffmpeg新型的用法,就用法之所以被抛弃,就是因为以前获取的数据可能不是完整的,而av_read_frame()保证了视频数据一帧...
//ffmpeg参数 x264是一个 H.264/MPEG4 AVC 编码器,本指南将指导新手如何创建高质量的H.264视频。 对于普通用户通常有两种码率控制模式:crf(Constant Rate Factor)和Two pass ABR。码率控制是一种决定为每一个视频帧分配多少比特数的方法,它将决定文件的大小和质量的分配。 如果你在编译和安装libx264 方面需要帮助,请查
在做实验的时候发现视频序列的长度(帧数)有些不同,为了更加公平地对比,想把序列都分成100帧来比较,那么怎么把一个YUV序列,比如300帧,分成0#-99#,100#-199#,200#-299#呢? 用ffmpeg工具可以完成提取指定帧数的效果: ffmpeg -s 352x288 -i E:\BUS_352x288_30_orig_01.yuv -c:v rawvideo -filter:v select=“between(n, 100, 199)” E:\out.yuv 用这行代码可以提取出100#-
ffmpeg学习日记19-判断AVPacket中的一帧数据是否为关键帧 从视频转到h264,其264数据存放结构是AVPacket,所以要利用AVPacket结构中的数据来判断关键帧,关键帧就是I帧。 方法一:通过数据解析协议来判断 最简单的办法是找0x65或0x25(I frame启始位),或者去找0x67或0x27(SPS)和0x68或0x28(PPS)后面的完整包。 SPS和PPS后面必然跟着I frame。 方法二:通过AVPacket中的标志来判断 如何判断当前AVPacket是否为关键帧呢? 其中,`input.mp4`是您要截取的视频文件名,`output_%04d.jpg`是输出文件名的格式,`%04d`表示输出文件名的数字部分会自动编号,每个数字占4位,例如,输出的文件名可能是`output_0001.jpg`、`output_0002.jpg`等。`fps=1`表示每秒输出1帧,即每秒截取一张图片作为输出。 请注意,该命令会将视频的每一帧都截取出来,因此输出文件可能非常多,需要根据实际情况进行调整。