Начать подгружать видео, до вставки его в html
Задача в том, чтобы начать подгружать видео, а затем вставить его. Точнее изменить значение текущего атрибута src так, чтобы видео начало проигрываться без подвисания.
Как делаю это я.
- В текущий момент проигрывается видео, создаю второй объект video в javascript
- Вещаю на объект слушатель onloadeddata
- Известен url, медиафайла, который необходимо проиграть следующим
- До окончания проигрывания текущего video, остается 5 секунд. За 5 секунд, вставляю во второй video которого нет html документе, вернее меняю src атрибут.
- Далее срабатывает слушатель onloadeddata, говорит о том, что данные подгружены.
- Из html документа, удаляю текущий video тег и меняю его на второй объект который. Сразу как сработал слушатель.
Такое впечатление, что я делаю что то не то.
Вопрос собственно в следующем. Возможно ли подгружать данные последующего видео, точнее сделать так, чтобы не удалять текущий тег video. А просто изменить его атрибут src, но так чтобы знать что слушатель onloadeddata дал добро. Что данные видео подгружены и оно может начаться без подвисания. Сразу как меняем атрибут src??
Надеюсь, что я был услышан.
Не понимаю, что тут делаю не так
video.src = URL.createObjectURL(source = new MediaSource); source.onsourceopen = _ => {
// Начинаем подгрузку
(buffer = source.addSourceBuffer('video/mp4; codecs="avc1.42E01E, mp4a.40.2"')).onupdateend = _ => {
// Подгрузка закончена, видео полностью загружено
source.endOfStream();
video.muted = true;
video.play();
};
buffer.appendBuffer(fetch('frag_bunny.mp4').then(r => r.arrayBuffer()))
};
Ответы (1 шт):
Начинайте грузить видео заранее через fetch в Blob. Затем задайте URL для video с помощью URL.createObjectURL().
const videoElement = document.querySelector('video');
let preloadedVideo;
let preloading = false;
/**
* Предзагрузка следующего видео в виде блоба
*/
async preloadNextVideo = (url) => {
preloading = true;
const blob = await fetch(url).then(res => res.blob());
return URL.createObjectURL(blob);
};
/**
* Когда видео в videoElement почти закончилось
*/
videoElement.timeupdate = (event) => { // или не timeupdate
// Определяем, что текущее видео почти закончилось, начинаем предзагрузку следующего
preloadedVideo = null;
if (almostEnded && !preloading && !preloadedVideo) preloadedVideo = preloadNextVideo(url);
};
/**
* Когда видео в videoElement закончилось
*/
videoElement.ended = (event) => {
videoElement.src = preloadedVideo;
};
Таким образом даже не надо будет создавать второй элемент video, а использовать уже созданный.
Код не тестировался, считайте, что это псевдокод, чтобы передать суть.
UPD
Как заметил @Qwertiy, нужно копать в сторону MediaSource. Это определенно лучше чем метод с Blob, т.к новое видео может не успеть догрузиться до конца предыдущего. MediaSource эту проблему решает, т.к будет подгружать новое видео даже когда начнется его воспроизведение.
Должно получится нечто подобное:
const videoElement = document.querySelector('video');
let preloadedVideo;
const MIME = 'video/mp4; codecs="avc1.42E01E, mp4a.40.2"';
/**
* Предзагрузка следующего видео в виде ArrayBuffer
*/
async preloadNextVideo = (url) => {
const arrayBuffer = await fetch(url).then(res => res.arrayBuffer());
return arrayBuffer;
};
/**
* Когда видео в videoElement почти закончилось
*/
videoElement.timeupdate = (event) => { // или не timeupdate
// Определяем, что текущее видео почти закончилось, начинаем предзагрузку следующего
if (almostEnded && !(preloadedVideo?.readyState == 'open')) {
if ('MediaSource' in window && MediaSource.isTypeSupported(MIME)) {
const preloadedVideo = new MediaSource();
preloadedVideo.addEventListener('sourceopen', function(arrayBuffer) {
// Начинаем подгрузку
const sourceBuffer = this.addSourceBuffer(MIME);
sourceBuffer.addEventListener('updateend', (_event) => {
this.endOfStream();
// Подгрузка закончена, видео полностью загружено
});
sourceBuffer.appendBuffer(preloadNextVideo(url));
});
}
}
};
/**
* Когда видео в videoElement закончилось
*/
videoElement.ended = (event) => {
videoElement.src = URL.createObjectURL(preloadedVideo);
videoElement.play();
};
Чисто в теории должно работать.