A parser that reads piped data from ffmpeg containing a fragmented mp4 and splits it into an initialization segment and media segments. Designed for streaming live video relayed from cctv cameras.
npm install mp4frag######     
A parser that reads piped data from ffmpeg containing a fragmented mp4 and splits it into an initialization segment and media segments. Designed for streaming live video relayed from cctv cameras.
You must use the correct output args with ffmpeg to create a compatible fragmented mp4 format similar to the following real world examples:
* ffmpeg -loglevel quiet -rtsp_transport tcp -i rtsp://192.168.1.21:554/user=admin_password=pass_channel=0_stream=1.sdp?real_stream -reset_timestamps 1 -an -c:v copy -f mp4 -movflags +frag_every_frame+empty_moov+default_base_moof -min_frag_duration 500000 pipe:1
* ffmpeg -loglevel quiet -rtsp_transport tcp -i rtsp://192.168.1.18:554/user=admin&password=pass&channel=1&stream=1.sdp -reset_timestamps 1 -an -c:v copy -f mp4 -movflags +frag_keyframe+empty_moov+default_base_moof pipe:1
count parameter{ segment, sequence, duration, timestamp, keyframe }keyframe >= 0* hlsBase => hlsPlaylistBase _string_, accepts _, a-z, and A-Z
* hlsSize => hlsPlaylistSize _integer_, ranges from 2 to 20, defaults to 4
* hlsInit => hlsPlaylistInit _boolean_, defaults to true
* bufferListSize => segmentCount _integer_, ranges from 2 to 30, defaults to 2
``js`
mp4frag.on('segment', data => {
console.log(data);
});{ segment, sequence, duration, timestamp }
* previously, data was a Buffer.
* currently, data is a segmentObject structured as
#### segmentCount: integer (2 - 30), setting this value will store specified number of media segments in the buffer
* will be ignored if setting hlsPlaylistBase`javascript`
const mp4frag = new Mp4Frag({segmentCount: 3});
#### hlsPlaylistBase: string (_, a-z, and A-Z), setting this will generate a live fmp4 HLS m3u8 playlist
#### hlsPlaylistSize: integer (2 - 20), setting this will determine the number of segments in the fmp4 HLS m3u8 playlist
`javascript`
const mp4frag = new Mp4Frag({hlsPlaylistSize: 4, hlsPlaylistBase: 'my_String'});
`javascript
const { spawn } = require('child_process');
const Mp4Frag = require('mp4frag');
const mp4frag = new Mp4Frag({hlsPlaylistSize: 3, hlsPlaylistBase: 'back_yard'});
const ffmpeg = spawn(
'ffmpeg',
['-loglevel', 'quiet', '-probesize', '64', '-analyzeduration', '100000', '-reorder_queue_size', '5', '-rtsp_transport', 'tcp', '-i', 'rtsp://216.4.116.29:554/axis-media/media.3gp', '-an', '-c:v', 'copy', '-f', 'mp4', '-movflags', '+frag_keyframe+empty_moov+default_base_moof', '-metadata', 'title="ip 216.4.116.29"', '-reset_timestamps', '1', 'pipe:1'],
{ stdio: ['ignore', 'pipe', 'inherit'] }
);
ffmpeg.stdio[1].pipe(mp4frag);
`mp4frag.m3u8
* m3u8 playlist will now be available via and can be served to a client browser via expressmp4frag.getSegmentObject(6)
* segments in playlist can be accessed by sequence number via , with 6 being the current sequence number
#### Generated m3u8 playlist will look like the following example pulled from my live feed
``
#EXTM3U
#EXT-X-VERSION:7
#EXT-X-ALLOW-CACHE:NO
#EXT-X-TARGETDURATION:4
#EXT-X-MEDIA-SEQUENCE:6
#EXT-X-MAP:URI="init-back_yard.mp4"
#EXTINF:4.780000,
back_yard6.m4s
#EXTINF:5.439000,
back_yard7.m4s
#EXTINF:4.269000,
back_yard8.m4s
#### Setting up server routes to respond to http requests for playing live HLS feed
`javascript
app.get('/back_yard.m3u8', (req, res) => {
if (mp4frag.m3u8) {
res.writeHead(200, {'Content-Type': 'application/vnd.apple.mpegurl'});
res.end(mp4frag.m3u8);
} else {
res.sendStatus(503);
}
});
app.get('/init-back_yard.mp4', (req, res) => {
if (mp4frag.initialization) {
res.writeHead(200, {'Content-Type': 'video/mp4'});
res.end(mp4frag.initialization);
} else {
res.sendStatus(503);
}
});
app.get('/back_yard:id.m4s', (req, res) => {
const segmentObject = mp4frag.getSegmentObject(req.params.id);
if (segmentObject) {
res.writeHead(200, {'Content-Type': 'video/mp4'});
res.end(segmentObject.segment);
} else {
res.sendStatus(503);
}
});
`
`javascript
const { spawn } = require('child_process');
const Mp4Frag = require('mp4frag');
// 3 past segments will be held in memory for later access via mp4frag.segmentObjects
// if each segment has a duration of 2 seconds, then buffer will contain 6 seconds of video
const mp4frag = new Mp4Frag({segmentCount: 3});
const ffmpeg = spawn(
'ffmpeg',
['-loglevel', 'quiet', '-probesize', '64', '-analyzeduration', '100000', '-reorder_queue_size', '5', '-rtsp_transport', 'tcp', '-i', 'rtsp://131.95.3.162:554/axis-media/media.3gp', '-an', '-c:v', 'copy', '-f', 'mp4', '-movflags', '+frag_keyframe+empty_moov+default_base_moof', '-metadata', 'title="ip 131.95.3.162"', '-reset_timestamps', '1', 'pipe:1'],
{ stdio: ['ignore', 'pipe', 'inherit'] }
);
ffmpeg.stdio[1].pipe(mp4frag);
`
##### Moments later, a triggering event occurs such as motion detection, and we need to record buffered video from before the event occurred:
`javascript
const fs = require('fs');
const { initialization, segmentObjects } = mp4frag;
if (initialization && segmentObjects) {
const fileName = ${Date.now()}.mp4;
const writeStream = fs.createWriteStream(fileName);
// write the initialization fragment
writeStream.write(initialization);
// write the media segments
segmentObjects.forEach(({segment}) => {
writeStream.write(segment);
});
// end
writeStream.end();
}
``