Search code examples
node.jsffmpegvideo-streaminghttp-live-streamingm3u8

HLS Streaming using node JS


I'm trying to stream HLS content using node.js. And somehow it is not working. It'll be of great help if someone helps me out.

Problem:- Trying to serve HLS content from node.js (not live stream, but a set of .ts files and .m3u8 playlist, or in other words VOD content)

Folder Structure

stream_test
|--- app.js
|--- node_modules
|--- streamcontent
        |--- test.m3u8
        |--- segment0.ts
        |--- segment1.ts
        .
        .
        .
        |--- segment127.ts

My app.js looks like this

var http = require('http'),
    url = require('url'),
    path = require('path'),
    fs = require('fs');
var mimeTypes = {
    "html": "text/html",
    "jpeg": "image/jpeg",
    "jpg": "image/jpeg",
    "png": "image/png",
    "js": "text/javascript",
    "css": "text/css",
    "ts": "video/MP2T",
    "m3u8": "application/vnd.apple.mpegurl"};

http.createServer(function(req, res) {
    var uri = url.parse(req.url).pathname;
    var filename = path.join(process.cwd(), unescape(uri));
    var stats;

    console.log('filename '+filename);

    try {
        stats = fs.lstatSync(filename); // throws if path doesn't exist
    } catch (e) {
        res.writeHead(404, {'Content-Type': 'text/plain'});
        res.write('404 Not Found\n');
        res.end();
        return;
    }


    if (stats.isFile()) {
        // path exists, is a file
        var mimeType = mimeTypes[path.extname(filename).split(".")[1]];
        res.writeHead(200, {'Content-Type': mimeType} );

        var fileStream = fs.createReadStream(filename);
        fileStream.pipe(res);
    } else if (stats.isDirectory()) {
        // path exists, is a directory
        res.writeHead(200, {'Content-Type': 'text/plain'});
        res.write('Index of '+uri+'\n');
        res.write('TODO, show index?\n');
        res.end();
    } else {
        // Symbolic link, other?
        // TODO: follow symlinks?  security?
        res.writeHead(500, {'Content-Type': 'text/plain'});
        res.write('500 Internal server error\n');
        res.end();
    }

}).listen(8000);

The test.m3u8 looks like this

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-MEDIA-SEQUENCE:0
#EXT-X-ALLOW-CACHE:YES
#EXT-X-TARGETDURATION:19
#EXT-X-PLAYLIST-TYPE:VOD
#EXTINF:12.595922,
segment0.ts
.
.
.

I used ffmpeg to create the segments and palylist

ffmpeg -i video-a.mp4  -c:a libmp3lame -ar 48000 -ab 64k  -c:v libx264   -b:v 128k -flags -global_header -map 0 -f segment  -segment_list test.m3u8 -segment_time 30 -segment_format mpegts segment_%05d.ts

Test Scenraio:- Works fine if served from Apache, does not if served from node.

Test Tool:- VNC Player


Solution

  • With the idea from Brad, I was able to do this using express.static. Here goes the solution.

    The app.js is changed like this

    var express = require('express');
    var app = express();
    var path = require('path');
    
    app.use(express.static(path.join(__dirname,'streamcontent')));
    
    app.listen(8000);
    console.log('Listening on Port 8000');
    

    and the .m3u8 playlist changed to this

    #EXTM3U
    #EXT-X-VERSION:3
    #EXT-X-MEDIA-SEQUENCE:0
    #EXT-X-ALLOW-CACHE:YES
    #EXT-X-TARGETDURATION:19
    #EXT-X-PLAYLIST-TYPE:VOD
    #EXTINF:12.595922,
    http://localhost:8000/segment0.ts
    #EXTINF:10.135133,
    http://localhost:8000/segment1.ts
    #EXTINF:11.511511,
    http://localhost:8000/segment2.ts
    

    And thats it. Voila !!!