reactjsexpresshtml5-videomedia-source

sourceBuffer.appendBuffer successfully but the h5 video player stuck


Here is my fontend code with React.js

import React, { useEffect, useRef } from 'react';

const VideoPage = () => {
  const videoInited = useRef(false)

  useEffect(() => {
    const video = document.querySelector("#stream-media-video") as HTMLMediaElement;

    // Using Bento4 to parse my mp4 video file
    // mp4info test.mp4 | grep Codec
    const mimeCodec = 'video/mp4; codecs="avc1.64081F, mp4a.40.2"';

    if ("MediaSource" in window && MediaSource.isTypeSupported(mimeCodec)) {
      const mediaSource = new MediaSource();
      video.src = URL.createObjectURL(mediaSource);
      mediaSource.addEventListener("sourceopen", sourceOpen);
    } else {
      console.error("Unsupported MIME type or codec: ", mimeCodec);
    }

    function sourceOpen(_) {
      console.log('[mediaSource sourceopen event trigger]: ', this.readyState); // open
      const mediaSource = this;

      const playBtn = document.querySelector('.play-btn');
      const init = () => {
        const sourceBuffer = mediaSource.addSourceBuffer(mimeCodec);

        // Get some video clips through xhr request (the video file is 1.5M in total, first get the first 1M part, and then get the next 0.5M part)
        fetchAB("/api/media/testmp4/part-v2?start=0&end=1000000", function (buf) {
          sourceBuffer.addEventListener('error', (e) => {
            console.error("sourceBuffer error", e);
          });
          
          sourceBuffer.addEventListener("updateend", function (_) {
            // mediaSource.endOfStream();

            video.play();
            videoInited.current = true;
            playBtn.removeEventListener("click", init);
  
          });
          sourceBuffer.appendBuffer(buf);

          // get the next 0.5M part
          fetchAB("/api/media/testmp4/part-v2?start=1000000&end=2000000", function (secondBuf){
            sourceBuffer.appendBuffer(secondBuf);
          });

        });
      }
      playBtn.addEventListener("click", init)

    }

    function fetchAB(url, cb) {
      const xhr = new XMLHttpRequest();
      xhr.open("get", url);
      xhr.responseType = "arraybuffer";
      xhr.onload = function () {
        cb(xhr.response);
      };
      xhr.send();
    }

  }, [])

  const handleClickPlay = () => {
    const video = document.querySelector("video")
    if (videoInited.current) {
      video.play()
    }
  }

  return (
    <div>
      <video 
        id='stream-media-video'
        width="400px"
        height="400px"
        style={{
          objectFit: 'contain',
        }}
        controls
      />
      <div>
        <button className='play-btn' onClick={handleClickPlay}>click to play video</button>
      </div>
    </div>
  )
}

export default VideoPage

And Here is my backend code with Express.js

  app.get("/api/media/testmp4/part-v2", function (req, res) {
    const {
      start,
      end,
    } = req.query;
    const mp4FilePath = "test.mp4";
    const _start = Number(start);
    const videoSize = fs.statSync(mp4FilePath).size;
    const _end = Math.min(Number(end), videoSize - 1);

    // Get video clips and return them to the front end
    const videoStream = fs.createReadStream(mp4FilePath, { 
      start: _start, 
      end: _end,
    });

    videoStream.pipe(res);
  });

The progress bar in the video player shows that the remaining video clips have not been loaded and cannot continue to play. And here is my screenshot of problem.

enter image description here

There are no errors on the console, how can I make the video continue to play?


Solution

  • "I have controlled the returned file byte range on the server side:
    Math.min(Number(end), videoSize - 1)"

    Looking at:

    const _end = Math.min(Number(end), videoSize - 1);
    

    Math.min will return the smallest one from those two input numbers.
    Your end is smaller than videoSize-1 so now it is being used as the limit of bytes returned back into your app for playback:

     var End  : 1000000
    (Size-1)  : 1499999
    file Size : 1500000
    

    "The progress bar in the video player shows that the remaining video clip data has not been loaded and it cannot continue to play."

    To fix: