Media Source Extensions (MSE) — это JavaScript API, который позволяет вам создавать потоки для воспроизведения из сегментов аудио или видео. Хотя это и не рассматривается в этой статье, понимание MSE необходимо, если вы хотите встроить видео на свой сайт, которые делают следующее:
- Адаптивная потоковая передача, другими словами, адаптация к возможностям устройства и условиям сети.
- Адаптивное сращивание, например, вставка рекламы
- Сдвиг во времени
- Контроль производительности и размера загрузки

Вы можете представить MSE как цепочку. Как показано на рисунке, между загруженным файлом и элементами мультимедиа находится несколько слоев.
- Элемент
<audio>
или<video>
для воспроизведения мультимедиа. - Экземпляр
MediaSource
сSourceBuffer
для подачи медиа-элемента. - Вызов
fetch()
или XHR для извлечения медиаданных в объектеResponse
. - Вызов
Response.arrayBuffer()
для подачиMediaSource.SourceBuffer
.
На практике цепочка выглядит так:
var vidElement = document.querySelector('video');
if (window.MediaSource) {
var mediaSource = new MediaSource();
vidElement.src = URL.createObjectURL(mediaSource);
mediaSource.addEventListener('sourceopen', sourceOpen);
} else {
console.log('The Media Source Extensions API is not supported.');
}
function sourceOpen(e) {
URL.revokeObjectURL(vidElement.src);
var mime = 'video/webm; codecs="opus, vp09.00.10.08"';
var mediaSource = e.target;
var sourceBuffer = mediaSource.addSourceBuffer(mime);
var videoUrl = 'droid.webm';
fetch(videoUrl)
.then(function (response) {
return response.arrayBuffer();
})
.then(function (arrayBuffer) {
sourceBuffer.addEventListener('updateend', function (e) {
if (!sourceBuffer.updating && mediaSource.readyState === 'open') {
mediaSource.endOfStream();
}
});
sourceBuffer.appendBuffer(arrayBuffer);
});
}
Если вы смогли разобраться в объяснениях, то можете смело прекращать чтение. Если вам нужны более подробные объяснения, то, пожалуйста, продолжайте читать. Я собираюсь пройти по этой цепочке, построив базовый пример MSE. Каждый из шагов сборки будет добавлять код к предыдущему шагу.
Примечание о ясности
Расскажет ли эта статья все, что вам нужно знать о воспроизведении медиа на веб-странице? Нет, она предназначена только для того, чтобы помочь вам понять более сложный код, который вы можете найти в другом месте. Для ясности этот документ упрощает и исключает многие вещи. Мы думаем, что можем обойтись без этого, поскольку мы также рекомендуем использовать библиотеку, такую как Shaka Player от Google . Я буду отмечать повсюду, где я намеренно упрощаю.
Несколько вещей, не охваченных
Вот несколько вещей, которые я не буду описывать (перечислять в произвольном порядке).
- Элементы управления воспроизведением. Мы получаем их бесплатно благодаря использованию элементов HTML5
<audio>
и<video>
. - Обработка ошибок.
Для использования в производственных условиях
Вот несколько рекомендаций по использованию API, связанных с MSE, в производственных условиях:
- Перед вызовами этих API обработайте любые события ошибок или исключения API и проверьте
HTMLMediaElement.readyState
иMediaSource.readyState
. Эти значения могут измениться до того, как будут доставлены связанные события. - Убедитесь, что предыдущие вызовы
appendBuffer()
иremove()
еще не выполняются, проверив логическое значениеSourceBuffer.updating
перед обновлениемmode
SourceBuffer
,timestampOffset
,appendWindowStart
,appendWindowEnd
или вызовомappendBuffer()
илиremove()
дляSourceBuffer
. - Для всех экземпляров
SourceBuffer
, добавленных в вашMediaSource
, убедитесь, что ни одно из ихupdating
значений не является истинным, прежде чем вызыватьMediaSource.endOfStream()
или обновлятьMediaSource.duration
. - Если значение
MediaSource.readyState
равноended
, вызовы вродеappendBuffer()
иremove()
или установкаSourceBuffer.mode
илиSourceBuffer.timestampOffset
приведут к тому, что это значение перейдет вopen
. Это означает, что вы должны быть готовы обрабатывать несколько событийsourceopen
. - При обработке событий
HTMLMediaElement error
содержимоеMediaError.message
может быть полезно для определения основной причины сбоя, особенно для ошибок, которые трудно воспроизвести в тестовых средах.
Присоедините экземпляр MediaSource к медиа-элементу
Как и во многих вещах в веб-разработке в наши дни, вы начинаете с обнаружения функций. Затем получите элемент media, либо элемент <audio>
, либо <video>
. Наконец, создайте экземпляр MediaSource
. Он преобразуется в URL и передается в атрибут source элемента media.
var vidElement = document.querySelector('video');
if (window.MediaSource) {
var mediaSource = new MediaSource();
vidElement.src = URL.createObjectURL(mediaSource);
// Is the MediaSource instance ready?
} else {
console.log('The Media Source Extensions API is not supported.');
}

То, что объект MediaSource
может быть передан атрибуту src
, может показаться немного странным. Обычно это строки, но они также могут быть blob . Если вы проверите страницу со встроенными медиа и изучите ее элемент media, вы поймете, что я имею в виду.
Готов ли экземпляр MediaSource?
URL.createObjectURL()
сам по себе синхронен; однако он обрабатывает вложение асинхронно. Это вызывает небольшую задержку, прежде чем вы сможете что-либо сделать с экземпляром MediaSource
. К счастью, есть способы проверить это. Самый простой способ — использовать свойство MediaSource
с именем readyState
. Свойство readyState
описывает связь между экземпляром MediaSource
и элементом мультимедиа. Оно может иметь одно из следующих значений:
-
closed
— экземплярMediaSource
не прикреплен к элементу мультимедиа. -
open
— экземплярMediaSource
присоединен к медиа-элементу и готов к приему данных или получает данные. -
ended
— экземплярMediaSource
присоединен к медиа-элементу, и все его данные переданы этому элементу.
Запрос этих параметров напрямую может негативно повлиять на производительность. К счастью, MediaSource
также запускает события при изменении readyState
, в частности sourceopen
, sourceclosed
, sourceended
. Для примера, который я создаю, я собираюсь использовать событие sourceopen
для указания того, когда следует извлекать и буферизировать видео.
var vidElement = document.querySelector('video');
if (window.MediaSource) {
var mediaSource = new MediaSource();
vidElement.src = URL.createObjectURL(mediaSource);
<strong>mediaSource.addEventListener('sourceopen', sourceOpen);</strong>
} else {
console.log("The Media Source Extensions API is not supported.")
}
<strong>function sourceOpen(e) {
URL.revokeObjectURL(vidElement.src);
// Create a SourceBuffer and get the media file.
}</strong>
Обратите внимание, что я также вызвал revokeObjectURL()
. Я знаю, что это кажется преждевременным, но я могу сделать это в любое время после того, как атрибут src
элемента media будет подключен к экземпляру MediaSource
. Вызов этого метода не уничтожает никакие объекты. Он позволяет платформе обрабатывать сборку мусора в подходящее время, поэтому я вызываю его немедленно.
Создать SourceBuffer
Теперь пришло время создать SourceBuffer
, который является объектом, который фактически выполняет работу по перемещению данных между источниками медиа и элементами медиа. SourceBuffer
должен быть специфичным для типа загружаемого вами медиа-файла.
На практике это можно сделать, вызвав addSourceBuffer()
с соответствующим значением. Обратите внимание, что в примере ниже строка типа mime содержит тип mime и два кодека. Это строка mime для видеофайла, но она использует отдельные кодеки для видео- и аудиочастей файла.
Версия 1 спецификации MSE позволяет агентам пользователей различаться в вопросе о том, требовать ли и тип MIME, и кодек. Некоторые агенты пользователей не требуют, но разрешают только тип MIME. Некоторые агенты пользователей, например Chrome, требуют кодек для типов MIME, которые не описывают свои кодеки самостоятельно. Вместо того, чтобы пытаться разобраться со всем этим, лучше просто включить оба.
var vidElement = document.querySelector('video');
if (window.MediaSource) {
var mediaSource = new MediaSource();
vidElement.src = URL.createObjectURL(mediaSource);
mediaSource.addEventListener('sourceopen', sourceOpen);
} else {
console.log('The Media Source Extensions API is not supported.');
}
function sourceOpen(e) {
URL.revokeObjectURL(vidElement.src);
<strong>
var mime = 'video/webm; codecs="opus, vp09.00.10.08"'; // e.target refers to
the mediaSource instance. // Store it in a variable so it can be used in a
closure. var mediaSource = e.target; var sourceBuffer =
mediaSource.addSourceBuffer(mime); // Fetch and process the video.
</strong>;
}
Получить медиа-файл
Если вы поищете в интернете примеры MSE, вы найдете множество примеров, которые извлекают медиафайлы с помощью XHR. Чтобы быть более передовым, я собираюсь использовать Fetch API и возвращаемый им Promise . Если вы попытаетесь сделать это в Safari, это не сработает без полифилла fetch()
.
function sourceOpen(e) {
URL.revokeObjectURL(vidElement.src);
var mime = 'video/webm; codecs="opus, vp09.00.10.08"';
var mediaSource = e.target;
var sourceBuffer = mediaSource.addSourceBuffer(mime);
var videoUrl = 'droid.webm';
<strong>
fetch(videoUrl) .then(function(response){' '}
{
// Process the response object.
}
);
</strong>;
}
Плеер производственного качества будет иметь один и тот же файл в нескольких версиях для поддержки разных браузеров. Он может использовать отдельные файлы для аудио и видео, чтобы позволить выбирать аудио на основе языковых настроек.
Реальный код также будет иметь несколько копий медиафайлов с разным разрешением, чтобы он мог адаптироваться к различным возможностям устройства и сетевым условиям. Такое приложение может загружать и воспроизводить видео порциями, используя запросы диапазона или сегменты. Это позволяет адаптироваться к сетевым условиям во время воспроизведения медиа . Вы могли слышать термины DASH или HLS, которые являются двумя методами достижения этого. Полное обсуждение этой темы выходит за рамки этого введения.
Обработать объект ответа
Код выглядит почти готовым, но медиа не воспроизводится. Нам нужно получить медиаданные из объекта Response
в SourceBuffer
.
Типичный способ передачи данных из объекта ответа в экземпляр MediaSource
— получить ArrayBuffer
из объекта ответа и передать его в SourceBuffer
. Начните с вызова response.arrayBuffer()
, который возвращает обещание буферу. В своем коде я передал это обещание второму предложению then()
, где я добавляю его в SourceBuffer
.
function sourceOpen(e) {
URL.revokeObjectURL(vidElement.src);
var mime = 'video/webm; codecs="opus, vp09.00.10.08"';
var mediaSource = e.target;
var sourceBuffer = mediaSource.addSourceBuffer(mime);
var videoUrl = 'droid.webm';
fetch(videoUrl)
.then(function(response) {
<strong>return response.arrayBuffer();</strong>
})
<strong>.then(function(arrayBuffer) {
sourceBuffer.appendBuffer(arrayBuffer);
});</strong>
}
Вызов endOfStream()
После того, как все ArrayBuffers
будут добавлены, и никаких дополнительных медиаданных не ожидается, вызовите MediaSource.endOfStream()
. Это изменит MediaSource.readyState
на ended
и запустит событие sourceended
.
function sourceOpen(e) {
URL.revokeObjectURL(vidElement.src);
var mime = 'video/webm; codecs="opus, vp09.00.10.08"';
var mediaSource = e.target;
var sourceBuffer = mediaSource.addSourceBuffer(mime);
var videoUrl = 'droid.webm';
fetch(videoUrl)
.then(function(response) {
return response.arrayBuffer();
})
.then(function(arrayBuffer) {
<strong>sourceBuffer.addEventListener('updateend', function(e) {
if (!sourceBuffer.updating && mediaSource.readyState === 'open') {
mediaSource.endOfStream();
}
});</strong>
sourceBuffer.appendBuffer(arrayBuffer);
});
}
Окончательная версия
Вот полный пример кода. Надеюсь, вы узнали что-то новое о Media Source Extensions.
var vidElement = document.querySelector('video');
if (window.MediaSource) {
var mediaSource = new MediaSource();
vidElement.src = URL.createObjectURL(mediaSource);
mediaSource.addEventListener('sourceopen', sourceOpen);
} else {
console.log('The Media Source Extensions API is not supported.');
}
function sourceOpen(e) {
URL.revokeObjectURL(vidElement.src);
var mime = 'video/webm; codecs="opus, vp09.00.10.08"';
var mediaSource = e.target;
var sourceBuffer = mediaSource.addSourceBuffer(mime);
var videoUrl = 'droid.webm';
fetch(videoUrl)
.then(function (response) {
return response.arrayBuffer();
})
.then(function (arrayBuffer) {
sourceBuffer.addEventListener('updateend', function (e) {
if (!sourceBuffer.updating && mediaSource.readyState === 'open') {
mediaSource.endOfStream();
}
});
sourceBuffer.appendBuffer(arrayBuffer);
});
}
Обратная связь
,Media Source Extensions (MSE) — это JavaScript API, который позволяет вам создавать потоки для воспроизведения из сегментов аудио или видео. Хотя это и не рассматривается в этой статье, понимание MSE необходимо, если вы хотите встроить видео на свой сайт, которые делают следующее:
- Адаптивная потоковая передача, другими словами, адаптация к возможностям устройства и условиям сети.
- Адаптивное сращивание, например, вставка рекламы
- Сдвиг во времени
- Контроль производительности и размера загрузки

Вы можете представить MSE как цепочку. Как показано на рисунке, между загруженным файлом и элементами мультимедиа находится несколько слоев.
- Элемент
<audio>
или<video>
для воспроизведения мультимедиа. - Экземпляр
MediaSource
сSourceBuffer
для подачи медиа-элемента. - Вызов
fetch()
или XHR для извлечения медиаданных в объектеResponse
. - Вызов
Response.arrayBuffer()
для подачиMediaSource.SourceBuffer
.
На практике цепочка выглядит так:
var vidElement = document.querySelector('video');
if (window.MediaSource) {
var mediaSource = new MediaSource();
vidElement.src = URL.createObjectURL(mediaSource);
mediaSource.addEventListener('sourceopen', sourceOpen);
} else {
console.log('The Media Source Extensions API is not supported.');
}
function sourceOpen(e) {
URL.revokeObjectURL(vidElement.src);
var mime = 'video/webm; codecs="opus, vp09.00.10.08"';
var mediaSource = e.target;
var sourceBuffer = mediaSource.addSourceBuffer(mime);
var videoUrl = 'droid.webm';
fetch(videoUrl)
.then(function (response) {
return response.arrayBuffer();
})
.then(function (arrayBuffer) {
sourceBuffer.addEventListener('updateend', function (e) {
if (!sourceBuffer.updating && mediaSource.readyState === 'open') {
mediaSource.endOfStream();
}
});
sourceBuffer.appendBuffer(arrayBuffer);
});
}
Если вы смогли разобраться в объяснениях, то можете смело прекращать чтение. Если вам нужны более подробные объяснения, то, пожалуйста, продолжайте читать. Я собираюсь пройти по этой цепочке, построив базовый пример MSE. Каждый из шагов сборки будет добавлять код к предыдущему шагу.
Примечание о ясности
Расскажет ли эта статья все, что вам нужно знать о воспроизведении медиа на веб-странице? Нет, она предназначена только для того, чтобы помочь вам понять более сложный код, который вы можете найти в другом месте. Для ясности этот документ упрощает и исключает многие вещи. Мы думаем, что можем обойтись без этого, поскольку мы также рекомендуем использовать библиотеку, такую как Shaka Player от Google . Я буду отмечать повсюду, где я намеренно упрощаю.
Несколько вещей, не охваченных
Вот несколько вещей, которые я не буду описывать (перечислять в произвольном порядке).
- Элементы управления воспроизведением. Мы получаем их бесплатно благодаря использованию элементов HTML5
<audio>
и<video>
. - Обработка ошибок.
Для использования в производственных условиях
Вот несколько рекомендаций по использованию API, связанных с MSE, в производственных условиях:
- Перед вызовами этих API обработайте любые события ошибок или исключения API и проверьте
HTMLMediaElement.readyState
иMediaSource.readyState
. Эти значения могут измениться до того, как будут доставлены связанные события. - Убедитесь, что предыдущие вызовы
appendBuffer()
иremove()
еще не выполняются, проверив логическое значениеSourceBuffer.updating
перед обновлениемmode
SourceBuffer
,timestampOffset
,appendWindowStart
,appendWindowEnd
или вызовомappendBuffer()
илиremove()
дляSourceBuffer
. - Для всех экземпляров
SourceBuffer
, добавленных в вашMediaSource
, убедитесь, что ни одно из ихupdating
значений не является истинным, прежде чем вызыватьMediaSource.endOfStream()
или обновлятьMediaSource.duration
. - Если значение
MediaSource.readyState
равноended
, вызовы вродеappendBuffer()
иremove()
или установкаSourceBuffer.mode
илиSourceBuffer.timestampOffset
приведут к тому, что это значение перейдет вopen
. Это означает, что вы должны быть готовы обрабатывать несколько событийsourceopen
. - При обработке событий
HTMLMediaElement error
содержимоеMediaError.message
может быть полезно для определения основной причины сбоя, особенно для ошибок, которые трудно воспроизвести в тестовых средах.
Присоедините экземпляр MediaSource к медиа-элементу
Как и во многих вещах в веб-разработке в наши дни, вы начинаете с обнаружения функций. Затем получите элемент media, либо элемент <audio>
, либо <video>
. Наконец, создайте экземпляр MediaSource
. Он преобразуется в URL и передается в атрибут source элемента media.
var vidElement = document.querySelector('video');
if (window.MediaSource) {
var mediaSource = new MediaSource();
vidElement.src = URL.createObjectURL(mediaSource);
// Is the MediaSource instance ready?
} else {
console.log('The Media Source Extensions API is not supported.');
}

То, что объект MediaSource
может быть передан атрибуту src
, может показаться немного странным. Обычно это строки, но они также могут быть blob . Если вы проверите страницу со встроенными медиа и изучите ее элемент media, вы поймете, что я имею в виду.
Готов ли экземпляр MediaSource?
URL.createObjectURL()
сам по себе синхронен; однако он обрабатывает вложение асинхронно. Это вызывает небольшую задержку, прежде чем вы сможете что-либо сделать с экземпляром MediaSource
. К счастью, есть способы проверить это. Самый простой способ — использовать свойство MediaSource
с именем readyState
. Свойство readyState
описывает связь между экземпляром MediaSource
и элементом мультимедиа. Оно может иметь одно из следующих значений:
-
closed
— экземплярMediaSource
не прикреплен к элементу мультимедиа. -
open
— экземплярMediaSource
присоединен к медиа-элементу и готов к приему данных или получает данные. -
ended
— экземплярMediaSource
присоединен к медиа-элементу, и все его данные переданы этому элементу.
Запрос этих параметров напрямую может негативно повлиять на производительность. К счастью, MediaSource
также запускает события при изменении readyState
, в частности sourceopen
, sourceclosed
, sourceended
. Для примера, который я создаю, я собираюсь использовать событие sourceopen
для указания того, когда следует извлекать и буферизировать видео.
var vidElement = document.querySelector('video');
if (window.MediaSource) {
var mediaSource = new MediaSource();
vidElement.src = URL.createObjectURL(mediaSource);
<strong>mediaSource.addEventListener('sourceopen', sourceOpen);</strong>
} else {
console.log("The Media Source Extensions API is not supported.")
}
<strong>function sourceOpen(e) {
URL.revokeObjectURL(vidElement.src);
// Create a SourceBuffer and get the media file.
}</strong>
Обратите внимание, что я также вызвал revokeObjectURL()
. Я знаю, что это кажется преждевременным, но я могу сделать это в любое время после того, как атрибут src
элемента media будет подключен к экземпляру MediaSource
. Вызов этого метода не уничтожает никакие объекты. Он позволяет платформе обрабатывать сборку мусора в подходящее время, поэтому я вызываю его немедленно.
Создать SourceBuffer
Теперь пришло время создать SourceBuffer
, который является объектом, который фактически выполняет работу по перемещению данных между источниками медиа и элементами медиа. SourceBuffer
должен быть специфичным для типа загружаемого вами медиа-файла.
На практике это можно сделать, вызвав addSourceBuffer()
с соответствующим значением. Обратите внимание, что в примере ниже строка типа mime содержит тип mime и два кодека. Это строка mime для видеофайла, но она использует отдельные кодеки для видео- и аудиочастей файла.
Версия 1 спецификации MSE позволяет агентам пользователей различаться в вопросе о том, требовать ли и тип MIME, и кодек. Некоторые агенты пользователей не требуют, но разрешают только тип MIME. Некоторые агенты пользователей, например Chrome, требуют кодек для типов MIME, которые не описывают свои кодеки самостоятельно. Вместо того, чтобы пытаться разобраться со всем этим, лучше просто включить оба.
var vidElement = document.querySelector('video');
if (window.MediaSource) {
var mediaSource = new MediaSource();
vidElement.src = URL.createObjectURL(mediaSource);
mediaSource.addEventListener('sourceopen', sourceOpen);
} else {
console.log('The Media Source Extensions API is not supported.');
}
function sourceOpen(e) {
URL.revokeObjectURL(vidElement.src);
<strong>
var mime = 'video/webm; codecs="opus, vp09.00.10.08"'; // e.target refers to
the mediaSource instance. // Store it in a variable so it can be used in a
closure. var mediaSource = e.target; var sourceBuffer =
mediaSource.addSourceBuffer(mime); // Fetch and process the video.
</strong>;
}
Получить медиа-файл
Если вы поищете в интернете примеры MSE, вы найдете множество примеров, которые извлекают медиафайлы с помощью XHR. Чтобы быть более передовым, я собираюсь использовать Fetch API и возвращаемый им Promise . Если вы попытаетесь сделать это в Safari, это не сработает без полифилла fetch()
.
function sourceOpen(e) {
URL.revokeObjectURL(vidElement.src);
var mime = 'video/webm; codecs="opus, vp09.00.10.08"';
var mediaSource = e.target;
var sourceBuffer = mediaSource.addSourceBuffer(mime);
var videoUrl = 'droid.webm';
<strong>
fetch(videoUrl) .then(function(response){' '}
{
// Process the response object.
}
);
</strong>;
}
Плеер производственного качества будет иметь один и тот же файл в нескольких версиях для поддержки разных браузеров. Он может использовать отдельные файлы для аудио и видео, чтобы позволить выбирать аудио на основе языковых настроек.
Реальный код также будет иметь несколько копий медиафайлов с разным разрешением, чтобы он мог адаптироваться к различным возможностям устройства и сетевым условиям. Такое приложение может загружать и воспроизводить видео порциями, используя запросы диапазона или сегменты. Это позволяет адаптироваться к сетевым условиям во время воспроизведения медиа . Вы могли слышать термины DASH или HLS, которые являются двумя методами достижения этого. Полное обсуждение этой темы выходит за рамки этого введения.
Обработать объект ответа
Код выглядит почти готовым, но медиа не воспроизводится. Нам нужно получить медиаданные из объекта Response
в SourceBuffer
.
Типичный способ передачи данных из объекта ответа в экземпляр MediaSource
— получить ArrayBuffer
из объекта ответа и передать его в SourceBuffer
. Начните с вызова response.arrayBuffer()
, который возвращает обещание буферу. В своем коде я передал это обещание второму предложению then()
, где я добавляю его в SourceBuffer
.
function sourceOpen(e) {
URL.revokeObjectURL(vidElement.src);
var mime = 'video/webm; codecs="opus, vp09.00.10.08"';
var mediaSource = e.target;
var sourceBuffer = mediaSource.addSourceBuffer(mime);
var videoUrl = 'droid.webm';
fetch(videoUrl)
.then(function(response) {
<strong>return response.arrayBuffer();</strong>
})
<strong>.then(function(arrayBuffer) {
sourceBuffer.appendBuffer(arrayBuffer);
});</strong>
}
Вызов endOfStream()
После того, как все ArrayBuffers
будут добавлены, и никаких дополнительных медиаданных не ожидается, вызовите MediaSource.endOfStream()
. Это изменит MediaSource.readyState
на ended
и запустит событие sourceended
.
function sourceOpen(e) {
URL.revokeObjectURL(vidElement.src);
var mime = 'video/webm; codecs="opus, vp09.00.10.08"';
var mediaSource = e.target;
var sourceBuffer = mediaSource.addSourceBuffer(mime);
var videoUrl = 'droid.webm';
fetch(videoUrl)
.then(function(response) {
return response.arrayBuffer();
})
.then(function(arrayBuffer) {
<strong>sourceBuffer.addEventListener('updateend', function(e) {
if (!sourceBuffer.updating && mediaSource.readyState === 'open') {
mediaSource.endOfStream();
}
});</strong>
sourceBuffer.appendBuffer(arrayBuffer);
});
}
Окончательная версия
Вот полный пример кода. Надеюсь, вы узнали что-то новое о Media Source Extensions.
var vidElement = document.querySelector('video');
if (window.MediaSource) {
var mediaSource = new MediaSource();
vidElement.src = URL.createObjectURL(mediaSource);
mediaSource.addEventListener('sourceopen', sourceOpen);
} else {
console.log('The Media Source Extensions API is not supported.');
}
function sourceOpen(e) {
URL.revokeObjectURL(vidElement.src);
var mime = 'video/webm; codecs="opus, vp09.00.10.08"';
var mediaSource = e.target;
var sourceBuffer = mediaSource.addSourceBuffer(mime);
var videoUrl = 'droid.webm';
fetch(videoUrl)
.then(function (response) {
return response.arrayBuffer();
})
.then(function (arrayBuffer) {
sourceBuffer.addEventListener('updateend', function (e) {
if (!sourceBuffer.updating && mediaSource.readyState === 'open') {
mediaSource.endOfStream();
}
});
sourceBuffer.appendBuffer(arrayBuffer);
});
}