Вычислить PTS перед кодированием кадра в FFmpeg

Как рассчитать правильное значение PTS для кадра перед кодированием в API FFmpeg C?

Для кодирования я использую функцию 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);
}

В этом блоге говорится, что формула должна быть такой:

(1/FPS) * частота дискретизации * номер кадра

Кто-то использует только номер кадра для установки pts:

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;

Какой из них правильный? Я думаю, что ответ на этот вопрос будет полезен не только для меня.

Ответ 1

Лучше подумать о PTS более абстрактно, прежде чем попробовать код.

То, что вы делаете, объединяет 3 "набора времени" вместе. Первое - это время, к которому мы привыкли, на основе 1000 мс в секунду, 60 секунд в минуту и ​​т.д. Второй - время кодека для конкретного кодека, который вы используете. Каждый кодек имеет определенный способ, чтобы он представлял время, обычно в формате 1/номер, что означает, что на каждую секунду имеется количество "количества" тиков. Третий формат работает аналогично второму, за исключением того, что вы используете временную базу для контейнера.

Некоторые люди предпочитают начинать с фактического времени, другие отсчеты кадров, и не являются "неправильными".

Начиная со счета кадра, вам нужно сначала преобразовать его на основе частоты кадров. Примечание все конверсии, которые я говорю об использовании av_rescale_q (...). Цель этого преобразования состоит в том, чтобы включить счетчик во времени, так что вы перемасштабируете свою частоту кадров (обычно используется база времени паролей). Затем вам нужно преобразовать это в time_base вашего видеокодека перед кодированием.

Аналогично, в реальном времени ваше первое преобразование должно быть от current_time - start_time, масштабированного до вашего времени видеокодека.

Любой, кто использует только счетчик кадров, вероятно, использует кодек с временем_базой, равным их частоте кадров. Большинство кодеков не работают так, и их хак не переносится. Пример:

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

Кроме того, любой, кто использует жестко закодированные числа в своем av_rescale_q, использует тот факт, что они знают, что такое их time_base, и этого следует избегать. Код не переносится в другие видеоформаты. Вместо этого используйте video_st- > time_base, video_st- > codec- > time_base и output_ctx- > time_base, чтобы понять, что происходит.

Я надеюсь, что понимание этого с более высокого уровня поможет вам понять, какие из них являются "правильными" и которые являются "плохой практикой". Нет единого ответа, но, возможно, теперь вы можете решить, какой из них лучше всего подходит вам.

Ответ 2

Также есть опция с настройкой как frame->pts = av_frame_get_best_effort_timestamp(frame), но я не уверен, что это правильный подход.

Я действительно хотел бы, чтобы было лучшее руководство для практики, как справиться с этим. Вы случайно нашли решение самостоятельно? Было бы здорово, если бы вы могли обновить сообщение своим решением.