введение
Базовая реализация функции поиска относительно проста.,Однако он должен быть постоянно положительным.&Обеспечить регрессseekНа самом деле есть довольно много моментов по оптимизации, которые необходимо сделать, чтобы обеспечить плавное воспроизведение без задержек.
В этой статье описывается только то, как использовать FFmpeg для реализации самых простых и точных функций поиска.
seek api
FFmpeg реализует функцию поиска, которую можно реализовать через два интерфейса, представленных в avformat.h.
av_seek_frame
avformat_seek_file
avformat_seek_file
函数内部调用链路如下
可以看到内部优先执行read_seek2
,Если не поддерживается, вернитесь кav_seek_frame
Параметры интерфейса двух API схожи.,Здесь мы используемavformat_seek_file
Например
AVFormatContext *s
:Указатель структуры открытого контекста медиафайла
int stream_index
:индекс потока;如果指定индекс потока,основан на AVStream.time_base,если -1,Поток будет выбран по умолчанию на основе AV_TIME_BASE.
int64_t min_ts
:Минимально приемлемая временная метка
int64_t max_ts
:Максимально допустимая временная метка
int64_t ts
:целевая временная метка,Вот где мы будем искать
int flags
:
// seek backward
// искать самый последний ключевой кадр перед запрошенной меткой времени
#define AVSEEK_FLAG_BACKWARD 1
// seeking based on position in bytes
// Поиск по позиции байта. Если flags содержит этот флаг, то единицу измерения времени необходимо преобразовать в байты, то есть на основе координат в файле.
#define AVSEEK_FLAG_BYTE 2
// seek to any frame, even non-keyframes
// Вы можете искать любой кадр, не обязательно ключевой, что может вызвать такие проблемы, как размытие экрана и мозаика.
#define AVSEEK_FLAG_ANY 4
// seeking based on frame number
// Перемотка вперед по номеру кадра
#define AVSEEK_FLAG_FRAME 8
Ключевые моменты операции поиска:
avcodec_flush_buffers
Демо-реализация
Базовая функция поиска
Пользовательский интерфейс использует SeekBar,Здесь мы только слушаемonStopTrackingTouch
при обратном звонке Выполнить поискдействовать
override fun onStopTrackingTouch(seekBar: SeekBar?) {
seekBar?.let {
val timestamp = seekBar.progress / 100f * mDuration
mPlayer.seek(timestamp)
}
}
Поиск по видеопотоку
// Единица измерения pos — с, преобразованная во временную метку видеопотока.
int64_t seekPos = av_rescale_q((int64_t)(pos * AV_TIME_BASE), AV_TIME_BASE_Q, mTimeBase);
// Выполнить поиск
int ret = avformat_seek_file(mFtx, mVideoStreamIndex,
INT64_MIN, seekPos, INT64_MAX, AVSEEK_FLAG_BACKWARD);
// Очистить буфер кодека
avcodec_flush_buffers(mCodecContext);
Обновить очередь видео avpacket
mVideoPacketQueue->clear();
Точный поиск
После выполнения описанного выше метода изображение, которое мы видим, представляет собой изображение ключевого кадра, ближайшего к целевой временной метке.
Если вы хотите добиться точного поиска, просто декодируйте один за другим от положения ключевого кадра, ближайшего к точке поиска, до целевой точки времени.
// precision seek
if (mAvFrame->pts < mSeekPos) {
// discard
}
Сбросьте всю информацию о видеокадре демонстрационного видео через ffprobe.
ffprobe -show_frames -select_streams v oceans.mp4 > v_info.txt
Вы можете видеть, что в видео всего 28 ключевых кадров, причем первые два ключевых кадра разделены более чем 5 секундами.
То есть, когда нам нужно точно найти 2 и 4, мы сначала будем искать ключевой кадр с pts = 0, а затем один за другим декодировать до целевой точки времени поиска (если мы не оптимизируем, вы Могу себе представить, что время зависания экрана относительно велико)
Демо-эффект
Поиск может быть запущен при перетаскивании конца SeekBar.
ссылка
1. [Анализ исходного кода FFmpeg: av_seek_frame() и avformat_seek_file()]
https://blog.csdn.net/u011686167/article/details/123029573