在FFmpeg中进行帧编码之前计算PTS

问题描述 投票:16回答:3

如何在使用FFmpeg C API进行编码之前为帧计算正确的PTS值?

对于编码,我正在使用函数avcodec_encode_video2,然后由av_interleaved_write_frame编写。

我找到了一些公式,但是没有一个不起作用。

doxygen example中,他们正在使用

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

This blog说公式必须像这样:

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

某人仅使用帧号来设置点数:

frame->pts = videoCodecCtx->frame_number;

或其他方式:

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

还有最后一个:

// 40 * 90 means 40 ms and 90 because of the 90kHz by the standard for PTS-values. 
frame->pts = encodedFrames * 40 * 90;

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

libav pts
3个回答
9
投票

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

您正在做的是将3个“时间集”啮合在一起。第一个是我们习惯的时间,基于每秒1000毫秒,每分钟60秒等等。第二个是您正在使用的特定编解码器的编解码器时间。每个编解码器都有它想要表示时间的某种方式,通常以1 /数字格式表示,这意味着每一秒都有“数字”数量的刻度。第三种格式的工作方式与第二种类似,不同的是它是所用容器的时基。

[有些人更喜欢从实际时间开始,其他人更喜欢从帧数开始,都不是“错误的”。

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

类似地,您需要进行一次实时转换,即从current_time-start_time转换为视频编解码器时间。

任何仅使用帧计数器的人都可能使用time_base等于其帧速率的编解码器。大多数编解码器无法像这样工作,并且其hack不能移植。示例:

frame->pts = videoCodecCtx->frame_number;  // BAD

此外,任何在其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)的选项,但我也不确定这是否是正确的方法。

我真的希望能有一个最佳实践指南来解决这个问题。您是否有机会自行找到解决方案?如果可以使用解决方案更新您的帖子,那就太好了。


0
投票

时间的度量单位不是秒或毫秒,也不是任何标准单位。而是由avCodecContext的时基来衡量。

因此,如果将codecContext-> time_base设置为1/1,则意味着使用秒进行测量。

cctx->time_base = (AVRational){1, 1};

假设您想以30的稳定fps进行编码。那么,对帧进行编码的时间为framenumber * (1.0/fps)

但是同样,PTS也不以秒或任何标准单位进行测量。它由avStream的time_base度量。

问题中,作者提到90k是pts的标准分辨率。但是,您会发现并非总是如此。确切的分辨率保存在avstream中。您可以通过以下方式阅读:

    if ((err = avformat_write_header(ofctx, NULL)) < 0) {
        std::cout << "Failed to write header" << err << std::endl;
        return -1;
    }

    av_dump_format(ofctx, 0, "test.webm", 1);
    std::cout << stream->time_base.den  << " " << stream->time_base.num << std::endl;

仅在调用avformat_write_header之后填充stream-> time_stamp的值

因此,计算PTS的正确公式是:

//The following assumes that codecContext->time_base = (AVRational){1, 1};
videoFrame->pts = (frameCounter++) * stream->time_base.den / (stream->time_base.num * fps);

因此公式中确实包含3个成分,

  1. fps
  2. codecContext-> time_base
  3. stream-> time_base

所以pts = fps*codecContext->time_base/stream->time_base

我已经详细说明了我的发现here

© www.soinside.com 2019 - 2024. All rights reserved.