@74383773

Как решить долгий запрос blob?

Есть код который меняет mp4 на блоб протокол, но если взять довольно длинное видео, скажем на 25 минут, то видео не сразу появляется, а спустя минуты 3, но если взять какое-то фильм, то вообще ждать можно вечно. Как решить эту проблему?
function App() {
  const [src, setSrc] = React.useState('')
  
  React.useEffect(() => {
    const fetchVideo = async () => {
      try {
        const response = await axios.get('//assets.mixkit.co/videos/preview/mixkit-white-cat-lying-among-the-grasses-seen-up-close-22732-large.mp4', { responseType: 'blob' })

        console.log(response)

        setSrc(URL.createObjectURL(response.data))
      } catch (err) {
        console.log(err)
      }
    };

    fetchVideo()
  }, [])

  console.log(src)

  return (<video src={src} controls />);
}
  • Вопрос задан
  • 357 просмотров
Решения вопроса 1
@risejs
У тебя видео скачивается полностью. Если хочешь по частям, то надо сделать подобие потока. Blob для этого не подходит, то что в ссылке написано blob: не значит что URL.createObjectURL() принимает только Blob, там еще есть MediaSource.

Потоковые медиа в браузерах работают через Media Source API. Видео для него должно соответствовать определенным требованиям, например фрагментированный mp4. Видео по твоей ссылке таким не является. Можно проверить здесь mp4info.

Если для транспорта используется fetch или XMLHttpRequest, то есть парочка относительно простых способов это реализовать:

1. Через тип ответа ArrayBuffer и заголовок запроса Range. В fetch поддерживается через response.arrayBuffer(), в XMLHttpRequest через responseType='arraybuffer'.
Например.

2. Через тип ответа ReadableStream (Streams API). В fetch поддерживается через response.body, в axios и других xmlhttprequest-основанных библиотеках не поддерживается. Также для Streams API требуется протокол HTTP/2+.
Например 1, 2, 3, 4.

Краткие примеры:
const rangeVideo = async (player, mimeCodec, resource) => {
    const mediaSource = new MediaSource;
    player.src = URL.createObjectURL(mediaSource);
    await new Promise(resolve => mediaSource.onsourceopen = resolve);
    URL.revokeObjectURL(player.src);
    const sourceBuffer = mediaSource.addSourceBuffer(mimeCodec);

    const response = await fetch(resource, { method: 'HEAD' });
    const contentLength = response.headers.get('Content-Length');
    const segmentLength = 1 * 1024 * 1024;
    let fetchedLength = -1;

    while (true) {
        if (fetchedLength >= contentLength) break;
        const response = await fetch(resource, {
          headers:{'Range':`bytes=${fetchedLength+1}-${fetchedLength+=segmentLength}`}
        });
        const value = await response.arrayBuffer();
        sourceBuffer.appendBuffer(value);
        await new Promise(resolve => sourceBuffer.onupdateend = resolve);
        //console.count();
        //await delay(2000);
    }
};

const streamVideo = async (player, mimeCodec, resource) => {
    const mediaSource = new MediaSource;
    player.src = URL.createObjectURL(mediaSource);
    await new Promise(resolve => mediaSource.onsourceopen = resolve);
    URL.revokeObjectURL(player.src);
    const sourceBuffer = mediaSource.addSourceBuffer(mimeCodec);

    const response = await fetch(resource);
    const readableStream = response.body;
    const reader = readableStream.getReader();

    while (true) {
        const { value, done } = await reader.read();
        if (done) break;
        sourceBuffer.appendBuffer(value);
        await new Promise(resolve => sourceBuffer.onupdateend = resolve);
        //console.count();
    }
};

//const delay = ms => new Promise(resolve => setTimeout(resolve, ms));

const data = [
    document.querySelector('video'),
    'video/mp4; codecs="avc1.42E01E, mp4a.40.2"',
    'https://nickdesaulniers.github.io/netfix/demo/frag_bunny.mp4',
];

//rangeVideo(...data);
//streamVideo(...data);
Ответ написан
Комментировать
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы