Reputation: 11
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
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