Промежуточные данные из MediaRecorder теряются при использовании ловушек React - PullRequest
1 голос
/ 03 апреля 2019

Я работаю над компонентом более высокого порядка, который предоставит возможность захвата мультимедиа с помощью API MediaRecorder. Однако, когда я пытаюсь использовать захваченное видео (в виде BLOB-объекта, переданного createObjectURL), я получаю сообщение об ошибке ERR_REQUEST_RANGE_NOT_SATISFIABLE. Когда я веду консольный журнал BLOB-объекта, который передается упакованному компоненту, он имеет длину 0. Я включил свой код в конец этого поста.

Для диагностики проблемы я попробовал следующие тесты:

  • Запись в консоли newChunks в handleDataAvailable записывает правильное значение (т. Е. [Blob]).
  • Я добавил React.useEffect(() => console.log(chunks), [chunks]);, чтобы посмотреть, действительно ли обновляются чанки. Это также приводит к регистрации правильного значения (т. Е. [Blob]).
  • Я добавил React.useEffect(() => console.log(captured), [captured]);, чтобы увидеть, обновляется ли захваченный. Это приводит к регистрации большого размера размером 0.
  • В handleStop я поддерживаю порции журнала и большой двоичный объект, созданный путем объединения порций. В результате получается пустой массив и BLOB-объект с размером 0 соответственно.

Это наводит меня на мысль, что handleDataAvailable правильно добавляет каждый чанк в массив чанков, но каким-то образом массив очищается к моменту запуска handleStop.

Кто-нибудь видит, что может быть причиной этого?

Код:

import React from 'react';

import { getUserMedia, getConstraints } from '../../utils/general';

const withMediaCapture = (WrappedComponent, recordingType, facingMode, deviceID) => {
  const constraints = getConstraints(recordingType, facingMode, deviceID);

  const type = recordingType === 'audio'
    ? 'audio/ogg; codecs=opus'
    : 'video/webm; codecs=vp9';

  return props => {
    const [mediaStream, setMediaStream] = React.useState(undefined);
    const [mediaRecorder, setMediaRecorder] = React.useState(undefined);
    const [isRecording, setIsRecording] = React.useState(false);
    const [captured, setCaptured] = React.useState(undefined);
    const [chunks, setChunks] = React.useState([]);

    // On mount, get the mediaStream:
    const setupStream = () => {
      getUserMedia(constraints)
      .then(setMediaStream)
      .catch(error => {/* TODO: Handle error */});
    };
    React.useEffect(setupStream, []);

    // Once we have gotten the mediaStream, get the mediaRecorder:
    const handleDataAvailable = ({ data }) => {
      const newChunks = [...chunks, data];
      setChunks(newChunks);
    };
    const handleStop = foo => {
      const blob = new Blob(chunks, { type });
      setCaptured(blob);
      setChunks([]);
    }
    const getMediaRecorder = () => {
      mediaStream && setMediaRecorder(Object.assign(
        new MediaRecorder(mediaStream),
        {
          ondataavailable: handleDataAvailable,
          onstop: handleStop,
        },
      ));
    }
    React.useEffect(getMediaRecorder, [mediaStream]);

    const toggleRecording = () => {
      isRecording
        ? mediaRecorder.stop()
        : mediaRecorder.start();
      setIsRecording(!isRecording);
    };

    return <WrappedComponent {...{ preview: mediaStream, captured, toggleRecording, isRecording, ...props }} />;
  };
};

const VideoCaptureDemo = ({ preview, captured, toggleRecording, isRecording }) => {
  const previewRef = React.useRef(null);
  const capturedRef = React.useRef(null);

  const setupPreview = () => {
    previewRef.current.srcObject = preview;
  };
  React.useEffect(setupPreview, [preview]);

  const setupCaptured = () => {
    const url = captured && window.URL.createObjectURL(captured);
    capturedRef.current.src = url;
  };
  React.useEffect(setupCaptured, [captured]);

  return (
    <div>
      <video ref={previewRef} autoPlay={true} muted={true} />
      <video ref={capturedRef} controls />
      <button onClick={toggleRecording}>
        {isRecording ? 'Stop Recording' : 'Start Recording'}
      </button>
    </div>
  );
};

export default withMediaCapture(VideoCaptureDemo, 'videoAndAudio');

1 Ответ

1 голос
/ 03 апреля 2019

handleStop и handleDataAvailable закрываются над исходным, пустым chunks массивом.Если handleDataAvailable вызывается более одного раза, более ранние чанки будут потеряны, а handleStop всегда будет создавать BLOB-объекты из пустого массива чанков.Повторные визуализации, вызванные setChunks, приведут к созданию новых версий методов handle, но MediaRecorder будет по-прежнему использовать версии, начиная с момента создания MediaRecorder.

Вы можете исправить handleDataAvailableиспользуя функциональные обновления , но для исправления handleStop я думаю, что было бы лучше перейти на использование useReducer (с редуктором, управляющим как chunks, так и captured), так что вы можете простоотправьте действие, и тогда редуктор сможет получить доступ к текущим чанкам и соответствующим образом создать BLOB-объект.

...