vanguy
vanguy

Reputation: 11

video using MediaSource API showing unending loading screen

I am trying to build a small streaming project using the MediaSource API.
I have a segmented video called earth into 6 segments of around 6 seconds each.
With the current project structure

vids/  
 earth/  
    playlist.json  
    segments/
      segment-000.mp4
      segment-001.mp4
      segment-002.mp4
      segment-003.mp4
      segment-004.mp4
      segment-005.mp4
      segment-006.mp4
index.js
index.html

the playlist.json file is as such

{
  "segments": [
    {
      "url": "http://localhost:3000/vids/earth/segments/segment-000.mp4"
    },
    {
      "url": "http://localhost:3000/vids/earth/segments/segment-001.mp4"
    },
    {
      "url": "http://localhost:3000/vids/earth/segments/segment-002.mp4"
    },
    {
      "url": "http://localhost:3000/vids/earth/segments/segment-003.mp4"
    },
    {
      "url": "http://localhost:3000/vids/earth/segments/segment-004.mp4"
    },
    {
      "url": "http://localhost:3000/vids/earth/segments/segment-005.mp4"
    },
    {
      "url": "http://localhost:3000/vids/earth/segments/segment-006.mp4"
    }
  ]
}

The index.js file is the server code

const express = require('express');
const path = require('path');

const app = express();
const PORT = 3000;

app.get('/index', (req, res)=> res.sendFile(path.join(__dirname, 'index.html')));
app.get('/earth', (req, res)=> res.sendFile(path.join(__dirname, 'earth.mp4')))
app.use('/vids', express.static(path.join(__dirname, 'vids')));
app.listen(PORT, () => {
  console.log(`Server is running on http://localhost:${PORT}`);
});

Finally the index.html file

<!DOCTYPE html>
<html>
<head>
  <title>Streaming App</title>
  <style>
    video {
      width: 100%;
      max-width: 800px;
    }
  </style>
</head>
<body>
  <h1>Streaming App</h1>
  <div id="video-container">
    <video id="video-player" controls></video>
  </div>
  <script>
    const videoPlayer = document.getElementById('video-player');

    let playlist;
    let currentSegmentIndex = 0;
    let mediaSource = null;
    let sourceBuffer = null;

    // Fetch playlist
    fetch('/vids/earth/playlist.json')
        .then(response => response.json())
        .then(data => {
            playlist = data;
            initMediaSource();
        })
        .catch(error => console.error('Error fetching playlist:', error));

    // Initialize the MediaSource API
    function initMediaSource() {
        mediaSource = new MediaSource();
        videoPlayer.src = URL.createObjectURL(mediaSource);
        mediaSource.addEventListener('sourceopen', () => {
            sourceBuffer = mediaSource.addSourceBuffer('video/mp4; codecs="avc1.42E01E"');
            loadNextVideoSegment();
        });
    }

    // Load the next video segment
    function loadNextVideoSegment() {
        if (currentSegmentIndex < playlist.segments.length) {
            const segmentUrl = playlist.segments[currentSegmentIndex].url;
            fetch(segmentUrl)
                .then(response => response.arrayBuffer())
                .then(data => {
                    sourceBuffer.appendBuffer(data);
                    currentSegmentIndex++;
                })
                .catch(error => console.error('Error loading video segment:', error));
        } else {
            mediaSource.endOfStream();
        }
    }

    // Move to the next segment when the current one ends
    videoPlayer.addEventListener('ended', () => {
        loadNextVideoSegment();
    });
  </script>
</body>
</html>

With this code when clicking the play button, in FF it displays an unending loading screen

Upvotes: 1

Views: 48

Answers (1)

Chinedu Otutu
Chinedu Otutu

Reputation: 244

You're using video/mp4; codecs="avc1.42E01E". If your video segments don't match this exact codec, it could cause issues. Try removing the codec specification to simplify it.

sourceBuffer = mediaSource.addSourceBuffer('video/mp4');

You need to wait for the updateend event on the sourceBuffer before appending the next video segment. Otherwise, segments may be appended too quickly, causing the unending load screen. You can read more about the updateend event here: Difference between "update" and "updateend" events in Media Source Extensions

<script>
  const videoPlayer = document.getElementById('video-player');
  let playlist;
  let currentSegmentIndex = 0;
  let mediaSource = null;
  let sourceBuffer = null;

  fetch('/vids/earth/playlist.json')
      .then(response => response.json())
      .then(data => {
          playlist = data;
          initMediaSource();
      }).catch(error => console.error('Error fetching playlist:', error));

  function initMediaSource() {
      mediaSource = new MediaSource();
      videoPlayer.src = URL.createObjectURL(mediaSource);
      mediaSource.addEventListener('sourceopen', () => {
          sourceBuffer = mediaSource.addSourceBuffer('video/mp4');
          loadNextVideoSegment();
      });
  }

function loadNextVideoSegment() {
    if (currentSegmentIndex < playlist.segments.length) {
        const segmentUrl = playlist.segments[currentSegmentIndex].url;
        fetch(segmentUrl)
            .then(response => response.arrayBuffer())
            .then(data => {
        
                if (sourceBuffer.buffered.length === 0) {
                    sourceBuffer.appendBuffer(data);
                    currentSegmentIndex++;
                }

                // Wait for buffer to be ready before appending next segment
                sourceBuffer.addEventListener('updateend', () => {
                    if (currentSegmentIndex < playlist.segments.length) {
                        sourceBuffer.appendBuffer(data);
                        currentSegmentIndex++;
                    }
                });
            })
            .catch(error => console.error('Error loading video segment:', error));
    } else {
        mediaSource.endOfStream();
    }
}

  videoPlayer.addEventListener('ended', () => {
      loadNextVideoSegment();
  });
</script>

Upvotes: 3

Related Questions