Problem statement :
I have placed a movie file in a node.js server.
I want to stream the movie in my browser and display
I have a node server which serves byte range request under http://localhost/video url. The browser sends the request. I get 206 successful byte-range request status code. But the fetched segment is not playing in html5 video element. I am using firefox.
Error : No video with supported format or mime type found
async function fetchRange(url, startByte, endByte) {
try {
const response = await fetch(url, {
headers: {'Range': `bytes=${startByte}-${endByte}`}
});
const data = await response.blob();
console.log(`Fetched ${data.size} bytes from ${startByte} to ${endByte}.`);
return data;
} catch (error) {
console.error('Error fetching range:', error);
}
}
// Example usage:
const fileUrl = 'http://localhost:8000/video'; // Replace with your file URL
const start = 0;
const end = 1023; // Requesting the first 1024 bytes (0-1023)
fetchRange(fileUrl, start, end)
.then(data => {
if (data) {
// Process the received data (e.g., display it, save it)
console.log('Received data:', data);
let f = new File([data],'haah',{ type: "video/mp4" })
document.getElementById('vid2').src = URL.createObjectURL(f)
}
});
html:
<video controls width="350" id="vid2" >
<source src='' type="video/mp4" >
Sorry, not supported by browser
</source>
</video>
node server code :
if(req.url === '/video' && req.method === 'GET'){
console.log('video get')
//var filePath = 'audiofile2.flac';
var filePath = "output_ah.mp4"
var stat = fs.statSync(filePath);
var total = stat.size;
console.log(req.headers.range)
var range = req.headers.range;
var parts = range.replace(/bytes=/, "").split("-");
var partialstart = parts[0];
var partialend = parts[1];
var start = parseInt(partialstart, 10);
var end = partialend ? parseInt(partialend, 10) : total-1;
//console.log(start,end)
var chunksize = (end-start)+1;
var file = fs.createReadStream(filePath, {start: start, end: end});
res.writeHead(206, {
'Content-Range': 'bytes ' + start + '-' + end + '/' + total,
// 'Content-Length' : total,
'Accept-Ranges': 'bytes',
'Content-Length': chunksize,
'Content-Type': 'video/mp4'
// 'ETag' : '"'+"abcd-efgh"+'"'
});
file.pipe(res)
}
1 Answer 1
MP4 files are a container for video and audio data, meta data, embedded subtitles and who knows what else. Video data is compressed with keyframes separating frames that only define changes since the previous frame (roughly speaking).
Taking a slice of a (MP4) file between specified byte offsets could return almost anything, with high likelihood a corruption of a segment of valid content in the file - with no meta data describing what it is.
More usefully the file needs to be split based on its timeline, between specified start and end times, with the server responsible for extracting a playable segment using a package like FFMPEG or a tool to pre-split the video file into a sequential set of videos. Splitting should occur on keyframes to avoid artifacts at the beginning and end of segments.
The MediaStream API may provide alternative solutions.
This question has probably been asked in a different format before - and there are a range of solutions. Hopefully this answer can clarify the underlying issue.
See also:
- FFMPEG Splitting MP4 with Same Quality on SuperUser
- How to split a mp4 file into multiple .m4s chunks with one init.mp4 file
- Seekable player with WebRTC
and web searches with terms for splitting MP4 files, seekable streams etc.
5 Comments
Explore related questions
See similar questions with these tags.
http://localhost/videourl." - okay, then stop there. Leave it up to the client, whether it wants to make byte range requests, or not. If it does, then it will know what kind of range sizes to request. Your own client-side attempt to request chunks of arbitrary sizes defined by you, is not very promising. You'd probably have to look into the specifics of video encoding & container formats in a lot more detail, if you wanted to implement that part of the equation yourself as well.