Интересное поведение в расширениях источника мультимедиа

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

  1. Видеопроигрыватель воспроизводит 1-й сегмент
  2. В исходном буфере заканчиваются данные, из-за чего видео кажется приостановленным
  3. Когда пользователь готов, он нажимает кнопку, которая добавляет 2-й сегмент в исходный буфер.
  4. Видео продолжается воспроизведением 2-го сегмента

Это работает хорошо, за исключением того, что когда видео появляется на паузе во время шага 2, оно не останавливается на последнем кадре 1-го сегмента. Вместо этого он останавливается за два кадра до конца 1-го сегмента. Последние два кадра не удаляются, они просто воспроизводятся после того, как пользователь нажимает кнопку для перехода к следующему видео. Это проблема для моего приложения, и я пытаюсь найти способ убедиться, что все кадры из 1-го сегмента воспроизводятся до окончания шага 2.

Я подозреваю, что эти последние два кадра задерживаются в буфере видеодекодера. Тем более, что вызов endOfStream() в моем медиа-источнике после добавления 1-го сегмента в исходный буфер приводит к тому, что 1-й сегмент воспроизводится полностью без оставшихся кадров.

Дополнительная информация

  • Я создал каждый файл видеосегмента из серии PNG, используя следующую команду ffmpeg

ffmpeg -i %04d.png -movflags frag_keyframe+empty_moov+default_base_moof video_segment.mp4

  • Может это подсказка? Ситуации конца потока обрабатываются неправильно (последние кадры отбрасываются)
  • Еще одна интересная вещь, на которую стоит обратить внимание, это то, что если в видео всего 2 кадра или меньше, MSE вообще не воспроизводит его.
  • Я использую браузер Chrome. Код для моего проигрывателя MSE просто взят из примера Google Developers, но я опубликую его здесь для полноты картины. Этот код охватывает только шаг 2, поскольку именно в этом заключается проблема.
<script>
const mediaSource = new MediaSource();
video.src = URL.createObjectURL(mediaSource);
mediaSource.addEventListener('sourceopen', sourceOpen, { once: true });

function sourceOpen() {
  URL.revokeObjectURL(video.src);
  const sourceBuffer = mediaSource.addSourceBuffer('video/mp4; codecs="avc1.64001f"');
  sourceBuffer.mode = 'sequence';

  // Fetch the video and add it to the Source Buffer
  fetch('https://s3.amazonaws.com/bucket_name/video_file.mp4')
  .then(response => response.arrayBuffer())
  .then(data => sourceBuffer.appendBuffer(data));
}


person newtonian_fig    schedule 26.04.2020    source источник


Ответы (1)


Это работает хорошо, за исключением того, что когда видео появляется на паузе во время шага 2, оно не останавливается на последнем кадре 1-го сегмента. Вместо этого он останавливается за два кадра до конца 1-го сегмента. Последние два кадра не удаляются, они просто воспроизводятся после...

Это поведение зависит от браузера. Начнем с спецификации:

Когда медиа-элементу требуется больше данных, пользовательский агент ДОЛЖЕН перевести его из HAVE_ENOUGH_DATA в HAVE_FUTURE_DATA достаточно рано, чтобы веб-приложение могло ответить, не вызывая прерывания воспроизведения. Например, переход, когда текущая позиция воспроизведения находится за 500 мс до конца буферизованных данных, дает приложению примерно 500 мс для добавления дополнительных данных, прежде чем воспроизведение остановится.

Поведение, которое вы видите, заключается в том, что браузер, совместимый с MSE, знает, что поток еще не завершен, но также знает, что у него заканчиваются данные. Он указывает на необходимость дополнительных данных, изменяя свое состояние готовности однако он не обязан воспроизводить каждый уже имеющийся кадр. Он входит в состояние буферизации на основе времени текущего воспроизведения по сравнению с концом доступных данных.

Хотя ссылка выше говорит...

Например, в видео это соответствует тому, что пользовательский агент имеет данные из текущего кадра, но не из следующего кадра.

... реальные реализации могут интерпретировать это по-другому и переключиться на HAVE_CURRENT_DATA слишком рано, то есть удерживать еще несколько видеокадров, но зная, что это еще не конец потока, а дальнейшие кадры отсутствуют. Это своего рода специфика реализации браузера, с которой вам просто нужно жить.

person Roman R.    schedule 28.05.2020