如何强制第一帧成为关键帧?

如何强制第一帧成为关键帧?

我想在寻找到某个位置后进行编码,并且我想将第一帧设为关键帧,下面是我使用的命令:

ffmpeg -ss 300  -i howimet.mp4 -acodec libfaac -ar 48000 -ab 128k -ac 2  -vcodec libx264 -vf "scale=480:270" -f mpegts -force_key_frames 300 -t 120 howimet2.ts

-force_key_frames 设置为寻找位置以在该位置创建关键帧。我使用以下脚本(来自这里)检查第一帧是否是关键帧

ffprobe -show_frames -v quiet howimet2.ts | awk -F= '   /pict_type=/ { if (index($2, "I")) { i=1; } else { i=0; } } 
  /pkt_pts_time/ { if (i && ($2 >= 0)) print $2; }  
' | head -n 1

结果表明第一个关键帧并不位于第 0 秒。

我想我的命令不正确。我遗漏了什么?

答案1

在对视频进行编码时,第一帧作为关键帧。它将是第一个完全编码的帧,后续帧可能会使用它进行帧间预测。此外,在编码视频序列的开头,您将有一个 H.264 访问单元,用于通知解码器进行刷新。

因此,无论您做什么:除非您只是复制比特流,否则您都会重新编码视频,并且您的第一帧必须是关键帧。

现在,无论出于什么原因,您的流的开始时间都会有偏移。这意味着所有显示时间戳也会根据此偏移进行移动。如果您检查输出的头部ffprobe -show_frames,您会发现第 0 帧确实是关键帧,但 PTS 不同。

为了弥补这一点,您可以从所有 PTS 中减去开始时间。

答案2

如上所述,视频的第一帧必须是 I 帧。您的问题不是来自编码(或者复制,如果您喜欢的话),而是来自解码。在输入之前使用 -ss 是不准确的,因此 ffmpeg 会尽其所能让您到达您想要的位置。Ffmpeg 传递了该位置,因此告诉您一个非零的第一个时间戳。

我建议在 -i 之后尝试第二个 -ss。例如 -ss 299 -i 输入 -ss 1。

这表示转到您想要的位置,然后在处理之前解码 1 秒。我不确定这是否能解决您的 pts 问题,但希望它能让您朝着正确的方向前进。

需要指出的是,我认为 force_key_frames 命令并没有像您想象的那样执行操作。如果它执行任何操作,则可能只是告诉它每 300 秒在输出中添加一个关键帧,但这只是猜测。这意味着只有输出的第一帧才是关键帧 (t=120)。

https://ffmpeg.org/ffmpeg.html

答案3

使用“-g”设置 GOP 大小

为了证明准确性,以下是 1 秒的片段

ffmpeg -i  in.mkv -g 30  -hls_time 1 -hls_list_size 0 index.m3u8

m3u8 的样子如下:

    #EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:2
#EXT-X-MEDIA-SEQUENCE:0
#EXTINF:1.001000,
index0.ts
#EXTINF:1.001000,
index1.ts
#EXTINF:1.001000,
index2.ts
#EXTINF:1.001000,
index3.ts
#EXTINF:1.001000,
index4.ts
#EXTINF:1.001000,
index5.ts
#EXTINF:1.001000,
index6.ts
#EXTINF:1.001000,
index7.ts
#EXTINF:1.001000,
index8.ts
#EXTINF:1.001000,
index9.ts
#EXTINF:1.001000,
index10.ts
#EXTINF:1.001000,
index11.ts
#EXTINF:1.001000,
index12.ts
#EXTINF:1.001000,
index13.ts
#EXTINF:1.001000,
....

相关内容