在FFmpeg中计算帧编码前的PTS

Ily*_*kov 15 pts libav

如何在FFmpeg C API编码之前计算帧的正确PTS值?

对于编码我正在使用函数avcodec_encode_video2,然后通过编写它av_interleaved_write_frame.

我发现了一些公式,但其中没有一个不起作用.

doxygen示例中,他们正在使用

frame->pts = 0;
for (;;) {
    // encode & write frame
    // ...
    frame->pts += av_rescale_q(1, video_st->codec->time_base, video_st->time_base);
}
Run Code Online (Sandbox Code Playgroud)

这篇博客说公式必须是这样的:

(1/FPS)*采样率*帧数

有人只使用帧号来设置pts:

frame->pts = videoCodecCtx->frame_number;
Run Code Online (Sandbox Code Playgroud)

或者替代方式:

int64_t now = av_gettime();
frame->pts = av_rescale_q(now, (AVRational){1, 1000000}, videoCodecCtx->time_base);
Run Code Online (Sandbox Code Playgroud)

最后一个:

// 40 * 90 means 40 ms and 90 because of the 90kHz by the standard for PTS-values. 
frame->pts = encodedFrames * 40 * 90;
Run Code Online (Sandbox Code Playgroud)

哪一个是正确的?我认为这个问题的答案不仅对我有帮助.

小智 9

在尝试代码之前,最好更抽象地考虑PTS.

你正在做的是将3个"时间集"整合在一起.第一个是我们习惯的时间,基于每秒1000毫秒,每分钟60秒,依此类推.第二个是您正在使用的特定编解码器的编解码器时间.每个编解码器都有一种想要表示时间的特定方式,通常是1 /数字格式,这意味着每秒都有"数量"的刻度.第三种格式与第二种格式类似,不同之处在于它是您使用容器的时基.

有些人喜欢从实际时间开始,有些人更喜欢帧数,也不是"错误".

从帧数开始,您需要先根据帧速率进行转换.请注意我所说的所有转换都使用av_rescale_q(...).此转换的目的是将计数器转换为时间,因此您需要使用帧速率(通常为视频流时基)进行重新缩放.然后,您必须在编码之前将其转换为视频编解码器的time_base.

同样,实时,您的第一次转换需要从current_time - start_time缩放到您的视频编解码器时间.

任何只使用帧计数器的人可能正在使用time_base等于其帧速率的编解码器.大多数编解码器不能像这样工作,他们的黑客不可移植.例:

frame->pts = videoCodecCtx->frame_number;  // BAD
Run Code Online (Sandbox Code Playgroud)

此外,任何在av_rescale_q中使用硬编码数字的人都在利用他们知道他们的time_base是什么的事实,这应该避免.该代码无法移植到其他视频格式.而是使用video_st->那么time_base,video_st-> codec->那么time_base和output_ctx->那么time_base理出头绪.

我希望从更高层次理解它会帮助你看到哪些是"正确的",哪些是"坏习惯".没有单一的答案,但也许现在您可以决定哪种方法最适合您.


小智 3

还有一种设置它的选项frame->pts = av_frame_get_best_effort_timestamp(frame),但我也不确定这是否是正确的方法。