If I have a remote mp4 file on a server that supports Byte Ranges, is it possible to retrieve a single byte range and create a new/self-contained mp4 from that range data?
If I try and write a returned byte range data directly to an mp4 file using fs.createWriteStream(remoteFilename)
it doesn't get the video meta data (duration, dimensions, etc) that it needs to be playable.
When I get a byte range that starts with 0 and ends with XX the output mp4 is playable, but will have the duration meta-data of the entire video length and will freeze the screen when the byte range is done for the remainder of the duration time.
How else can I take a byte range and create a stand-alone .mp4 file from that stream object?
The whole point of this is to avoid downloading the entire 10 minute file before I can make a 5 second clip using ffmpeg. If I can calculate and download the byte range, there should be a way to write it to a standalone mp4 file.
Thanks in advance for any help you can provide.
MP4 files are structured with boxes. Two main of them being moov and mdat (general case of non-fragmented MP4):
- moov box: contains other boxes :) - each of them contains information about the encoded data present in the mdat box (moov = metadata about the MP4 file). Typical metadatas are duration, framerate, codecs information, reference to the video/audio frames ...
- mdat box: contains the actual encoded data for the file. It can come from various codecs and includes audio and video data (or only one of them if it happens to be). For H264 NAL units are contains within the mdat box.
The moov box is (should be) at the beginning of the file for MP4 file web delivery so if you write a byte range request from 0 to XX you will likely get the whole moov box + a certain amount of mdat data. Hence the file can be played up to a certain point. If you byte range from YY to XX chances are you will not get a decent moov box but a lot of mdat which as such cannot be used unless they are repack in a MP4 file with a proper moov box referencing information about the "cut" mdat.
It is possible to recreate a valid MP4 file from a byte range chunk but it requires an advanced knowledge of the MP4 file format structure (you need to retrieve the moov box as well to make it bearable). MP4 file format is based on ISO base media file format - that was specified as ISO/IEC 14496-12 (MPEG-4 Part 12).
I know 2 libs that could help doing what you want: one in PHP and one in Java. I do not know if such a lib exists for node.js (I guess it could be ported). Even if you do not use them the 2 libs above contain valuable information about the subject.
To provide an answer to your question you could tackle the issue with a different angle. Knowing which part of the file you want in milliseconds you could execute an ffmpeg command to splice the full-length MP4 file server-side into a smaller one and then do what you need with this new smaller MP4 file (as so you do not need to download unnecessary data on the client).
ffmpeg command for that is (in this case cut at 1 minute from beginning of file):
ffmpeg -i input.mp4 -ss 00:00:00.000 -t 00:01:00.000 -c:a copy -c:v copy output.mp4
See this post for more info on the above command line
This is done pretty fast as the MP4 file structure is just re-organised with no re-transcoding.
EDIT: Or can I use ffmpeg on a remote file and create the new clip locally?
ffmpeg -ss 00:01:00.000 -i "http://myfile.mp4" -t 00:02:00.000 -c:a copy -c:v copy output.mp4
Assuming you have ffmpeg on your client (app/web) if you run the above command ffmpeg will fetch the mp4 to the input URL then seek to 1 min and cut 2 min from there and so write the generated content to output.mp4 locally (without downloading the full file of course).
ffmpeg needs to be built with support for http protocol input (which you will find in most binaries). You can read here for further information on where to place the -ss parameter (pros/cons).