TypeError: не удалось выполнить «appendBuffer» в «SourceBuffer»: не найдена функция, соответствующая предоставленной подписи - PullRequest
3 голосов
/ 28 мая 2020

Изменить: Чтобы проиллюстрировать ошибку, которую я продолжаю получать, я создал CodePen проблемы, которую я вижу. Откройте консоль, и вы увидите ошибку. [https://codepen.io/FifthCloud/pen/eYpqJLN]

Я хотел создать свой собственный сервис потоковой камеры у себя дома с помощью React / Node. До сих пор я прошел долгий путь с клиент-серверным подходом. Сервер представляет собой raspberry pi с прикрепленной к нему камерой. Используя FFMpeg, я могу видеть все видео из него, а с помощью веб-сокетов я могу успешно отправлять данные (я имею в виду, что я вижу, что данные, отправляемые клиенту, выяснят, почему они не будут отображаться).

Проблема I я сталкиваюсь, почему я не могу добавить данные в буфер. Для начала я использую эту демонстрацию об источнике мультимедиа в качестве руководства и примера того, что я должен делать. http://nickdesaulniers.github.io/netfix/demo/bufferAll.html Я получил эту ссылку из своего исследования об источниках СМИ здесь: https://developer.mozilla.org/en-US/docs/Web/API/MediaSource/readyState Также хотел бы отметить, что я получил много вдохновения из этого сообщения и зашел очень далеко из-за этого ответа HTML5 Видео: потоковое видео с URL-адресами Blob

Имея это в виду, я могу успешно создать MediaSource и открыть буфер, но я не могу понять, почему я не могу добавить в буфер? Я получаю следующую ошибку:

TypeError: не удалось выполнить 'appendBuffer' в 'SourceBuffer': не было найдено ни одной функции, соответствующей предоставленной подписи.

Код на стороне клиента следующий:

class ProductDetail extends React.Component {
  constructor(props) {
    super(props);
    this.handleData = this.handleData.bind(this);
    this.handleOpen = this.handleOpen.bind(this);
    this.appendToSourceBuffer = this.appendToSourceBuffer.bind(this);
    this.mediaStreaming = new MediaSource();
    this.blobArr = [];
    this.buffer = null;
    this.myRef = React.createRef();

    console.log("buffer initialized");
    console.log("ReadyState [Initialized]: " + this.mediaStreaming.readyState);
    this.state = {
      msg: "",
      stream: "",
      streaming: URL.createObjectURL(this.mediaStreaming),
    };
  }

  componentDidMount() {
    this.mediaStreaming.addEventListener("sourceopen", () => {
      console.log(
        "ReadyState [Source Open]: " + this.mediaStreaming.readyState
      );
      this.buffer = this.mediaStreaming.addSourceBuffer(
        'video/mp4;codecs="avc1.42E01E"'
      );
      this.buffer.addEventListener("updateend", () => {
        this.mediaStreaming.endOfStream();
        document.getElementById("streaming").play();
        console.log(
          "ReadyState [UpdateEnd]: " + this.mediaStreaming.readyState
        );
      });
    });
  }

  handleData(data) {
    const videoStream = JSON.parse(data);
    if (videoStream.msg === "videoStream") {
      this.blobArr.push(videoStream.chunk.data);
      if (
        this.mediaStreaming.readyState === "open" &&
        this.buffer &&
        this.buffer.updating === false &&
        this.blobArr.length > 0
      ) {
        console.log(
          "ReadyState [AppendBuffer]: " + this.mediaStreaming.readyState
        );
        this.buffer.appendBuffer(this.blobArr.shift()); // Error message shows at this line!
        document.getElementById("streaming").play();
        console.log("Buffer Updating", this.buffer.updating);
      }
    }
  }

  handleOpen() {
    console.log("Status: connected");
  }

  handleClose() {
    console.log("Status: disconnected");
  }

  render() {
    return (
      <div>
        <Websocket
          url="ws://192.168.1.20:5000"
          onMessage={this.handleData}
          onOpen={this.handleOpen}
          onClose={this.handleClose}
          reconnect={true}
          debug={true}
          ref={(Websocket) => {
            this.refWebSocket = Websocket;
          }}
          protocol="stream-protocol"
        />
        <video width="640" height="480" id="streaming" controls autoPlay>
          <source
            ref={this.myRef}
            src={this.state.streaming}
            type="video/mp4"
          />
          Your browser does not support the video tag.
        </video>
      </div>
    );
  }
}

Ошибка возникает из-за события handleData из моего WebSocket, когда сервер отправляет следующие данные массива байтов потоковой передачи.

Как я уже упоминал о примере и руководстве по ссылке, которую я разместил, я посмотрел на их код и прошел через него. По их словам, Append Buffer должен работать нормально, однако даже в их коде appendbuffer() указан только в разделе proto , см. Здесь: https://drive.google.com/open?id=1vOU4oM-XoKe71DofQuLU2THxMdgiown_, но код не жалуется об их appendbuffer (). Когда я консоль логирую, мой буфер выглядит как их https://drive.google.com/open?id=1T4Ix-y124NgQJ9xu97xv4U2yFTn2k7vF. Что мне не хватает?

Я чувствую, что ответ очень очевиден, но я не понимаю, что я делаю неправильно.

1 Ответ

1 голос
/ 01 июня 2020

Я сам создаю аналогичный проект, используя несколько высококачественных IP-камер с поддержкой RTSP, и сегодня столкнулся с той же ошибкой. Прежде всего, имейте в виду, что этот API является экспериментальным, согласно документации Mozilla Developer Network (MDN) (ссылка ниже).

При этом, похоже, я нашел причину root проблема, хотя я ни в коем случае не эксперт по JavaScript. Оказывается, вы не можете напрямую передать объект «Blob» из data.data в handleData() в метод appendBuffer(). Вместо этого вам нужно сначала преобразовать Blob в тип данных, который поддерживается методом appendBuffer(), прежде чем передавать его.

  1. Превратите обработчик событий в функцию async
  2. Используйте Blob.arrayBuffer(), чтобы преобразовать блоки данных в JavaScript ArrayBuffer

Измените это:

  handleData(data) {
    const videoStream = JSON.parse(data);
    if (videoStream.msg === "videoStream") {
      this.blobArr.push(videoStream.chunk.data);
      if (
        this.mediaStreaming.readyState === "open" &&
        this.buffer &&
        this.buffer.updating === false &&
        this.blobArr.length > 0
      ) {
        console.log(
          "ReadyState [AppendBuffer]: " + this.mediaStreaming.readyState
        );
        this.buffer.appendBuffer(this.blobArr.shift()); // Error message shows at this line!
        document.getElementById("streaming").play();
        console.log("Buffer Updating", this.buffer.updating);
      }
    }
  }

На это:

  async handleData(event) {
    var buffer = await event.data.arrayBuffer(); // convert the message Blob into an ArrayBuffer
    appendBuffer(buffer);
    document.getElementById("streaming").play();
    console.log("Buffer Updating", this.buffer.updating);
  }

Проблема в том, что как только вы это сделаете, вы начнете получать новую ошибку, потому что в объект SourceBuffer на MediaSource передается слишком много данных. Поскольку он не успевает за входным потоком, вы увидите что-то похожее на следующую ошибку:

Не удалось выполнить 'appendBuffer' в 'SourceBuffer': этот SourceBuffer все еще обрабатывает ' appendBuffer 'или операция' remove '

Похоже, нам нужно «буферизовать буферы», как бы странно это ни звучало.

Я еще не решил эту новую ошибку. Если вы найдете решение, мне нужна помощь.

Дополнительная литература

...