2013-12-22 3 views
3

Проблема: исходный код omxplayer называет метод FFmpeg av_seek_frame() используя AVSEEK_FLAG_BACKWARD флаг. Хотя не на 100% уверен, я считаю, что это стремится к ближайшему i-кадру. Вместо этого я хочу искать точные местоположения, поэтому я изменил исходный код таким образом, что теперь метод av_seek_frame() использует флаг AVSEEK_FLAG_ANY. Теперь, когда фильм загружается, я получаю серый экран, обычно в течение 1 секунды, во время которого я слышу звук. Я пробовал это на нескольких компьютерах (поэтому я фактически синхронизую их, в то же время), поэтому это не отдельный инцидент. Моя догадка заключается в том, что поиск не-i-кадров является более дорогостоящим вычислительным процессом, что приводит к первоначальному серому экрану.FFmpeg av_seek_frame с AVSEEK_FLAG_ANY вызывает серый экран

Вопрос: Как с помощью ffmpeg я могу дать указание аудио ждать, пока видео будет готово, прежде чем продолжить.

ответ

6

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

Используя AVSEEK_FLAG_ANY, вы получите фрейм, который соответствует точно, к отметке времени, которую вы просили. Но этот кадр не может быть ключевым кадром, а это значит, что он не может быть полностью декодирован. Это объясняет ваш «серый экран», который появляется до тех пор, пока не будет достигнут следующий ключевой кадр. Поэтому

Решение будет искать назад используя AVSEEK_FLAG_BACKWARD и от этого ключевого кадра, прочитайте следующие кадры (например, с помощью av_read_frame()), пока вы не дойдете до одной, соответствующей вашей временной метки. На этом этапе ваш кадр будет полностью декодирован и больше не будет отображаться как «серый экран».

ПРИМЕЧАНИЯ: Оказывается, что по какой-то причине, av_seek_frame() с помощью AVSEEK_FLAG_BACKWARD возвращает следующий ключевого кадром, когда кадр, что я ищу это один непосредственно перед этим ключевым кадром. В противном случае он возвращает предыдущий ключевой кадр (именно это я и хочу). Мое решение - изменить временную метку, которую я даю av_seek_frame(), чтобы гарантировать, что она вернет ключевой кадр до кадр, который я ищу.

+0

Я думаю, что ваш последний абзац имеет ошибку – puk

+0

«* не может быть ключевым кадром, а это значит, что он не может быть полностью декодирован *« Что это значит, только ключевые кадры могут быть полностью ** декодированы? – puk

+0

Я не эксперт, но вот как я его вижу: видео кодируется с помощью «_keyframes_», которые являются независимыми изображениями и «_frames_», которые полагаются на ключевые кадры. Другими словами, когда вы читаете ключевой кадр, вы можете декодировать соответствующее изображение. Но кадр не содержит всю информацию, необходимую для его декодирования, поэтому вам нужны кадры перед тем, как их декодировать (в отличие от ключевого кадра). Поэтому, если вы «прыгаете» в кадр, не прочитав ранее ключевой кадр, вы пропускаете информацию об этом кадре. Яснее? – JonesV

0

Комплектующие JonesV ответ с некоторым кодом:

void seekFrame(int FrameIndex) 
{ 
    // Seek is done on packet dts 
    int64_t target_dts_usecs = (int64_t)round(frameIndex * (double)_video_stream->r_frame_rate.den/_video_stream->r_frame_rate.num * AV_TIME_BASE); 
    // Remove first dts: when non zero seek should be more accurate 
    auto first_dts_usecs = (int64_t)round(_video_stream->first_dts * (double)_video_stream->time_base.num/_video_stream->time_base.den * AV_TIME_BASE); 
    target_dts_usecs += first_dts_usecs; 
    int rv = av_seek_frame(_format_ctx, -1, target_dts_usecs, AVSEEK_FLAG_BACKWARD); 
    if (rv < 0) 
     throw exception(); 

    avcodec_flush_buffers(_codec_ctx); 
} 

Затем вы можете начать декодирование проверки AVPacket.dts от первоначальной цели, вычисленных DTS на AVStream.time_base. Как только вы достигнете целевого dts, следующий декодированный кадр должен быть нужным фреймом.

Смежные вопросы