0

I want to make a player that supports flv, so I have to stream video files through a node http server. For mp4, it works perfect. But, I don't know why, for flv, the browser will not send another request when I click beyong the buffer line.
The server is based on Streaming a video file to an html5 video player with Node.js so that the video controls continue to work?
You can try it by starting the server and opening http://localhost:8080/?path=E:/1.flv

"use strict";
var fs = require("fs");
var http = require("http");
var url = require("url");
var path = require("path");
var MIMETypes = new Map();
MIMETypes.set(".flv", "video/x-flv").set(".mp4", "video/mp4").set(".avi", "video/x-msvideo");
exports.server = http.createServer(function (req, res) {
    var query = url.parse(req.url, true).query;
    if (!query.path) {
        res.end("Empty query");
    }
    else if (query.path && !query.getStream) {
        // return a page.
        res.end(`
<head>
  <link href="http://vjs.zencdn.net/5.9.2/video-js.css" rel="stylesheet">
</head>
<body>
  <video id="my-video" class="video-js" controls preload="auto" width="640" height="264" data-setup="{}">
    <source src="http://localhost:8080/?getStream=true&path=${query.path}" type='${MIMETypes.get(path.extname(query.path))}'>
  </video>
  <script src="http://vjs.zencdn.net/5.9.2/video.js"></script>
</body>`);
    }
    else {
        var mimetype = MIMETypes.get(path.extname(query.path));
        fs.stat(query.path, function (err, stats) {
            if (err) {
                console.log("fail to read file stat", err);
                res.end(JSON.stringify(query));
            }
            else {
                var range = req.headers.range;
                if (!range) {
                    res.writeHead(206, {
                        "Accept-Ranges": "bytes",
                        "Content-Length": stats.size,
                        "Content-Type": mimetype
                    });
                    var stream = fs.createReadStream(query.path);
                    stream.pipe(res);
                }
                else {
                    var positions = range.replace(/bytes=/, "").split("-");
                    var start = parseInt(positions[0], 10);
                    var total = stats.size;
                    var end = positions[1] ? parseInt(positions[1], 10) : total - 1;
                    var chunksize = (end - start) + 1;
                    res.writeHead(206, {
                        "Content-Range": "bytes " + start + "-" + end + "/" + total,
                        "Accept-Ranges": "bytes",
                        "Content-Length": chunksize,
                        "Content-Type": mimetype
                    });
                    var stream_1 = fs.createReadStream(query.path, { start: start, end: end })
                        .on("open", function () {
                            stream_1.pipe(res);
                        }).on("error", function (err) {
                            res.end(err);
                        });
                }
            }
        });
    }
});
exports.server.listen(8080, "localhost");
Community
  • 1
  • 1
Zen
  • 5,065
  • 8
  • 29
  • 49
  • Likely your request is not landing on a keyframe in the FLV. Assuming your FLV has H264 video.... 1) Open your FLV in a hex editor and scroll about halfway down. 2) Use "search" to find two bytes `17` & `01`. Either use `0x1701` or `1701` in the search box (depends on program). 3) Now if you check 11 steps backwards from that `17` position, do you reach an `09` byte? Here it means `09` is start of a video frame and we know its a keyframe because 11 bytes ahead is a `17` (1= keyframe + 7= type H264). 4) Note the offset position of the `09` and pass that through your test range as start-pos. – VC.One May 28 '16 at 21:02
  • For a test, wait until FLV is playing as usual, now force any seek action to go that test position. If it plays then that's your solution, to make sure the start pos of your range begins on a keyframe. – VC.One May 28 '16 at 21:06
  • Sorry, I don't really understand what you meant. When `req.headers.range` is undefined, I should return stream starting from the `09` byte? After FLV is playing, which position should I seek? When I seek beyond the buffer line. Browser won't send another request. @VC.One – Zen May 29 '16 at 06:16
  • You can play the FLV but there's a problem when you seek ahead of buffered amount, right? So now as a test... Play the FLV as you would but set a function that when a button is clicked, FLV "seeks" to the offset of `09` byte. If the video jumps to and plays correctly then you have a solution (seek ahead must land on a keyframe). Anyway does your server allow byte ranges? Or are you cheating by using PHP tricks? Is there any way to check some bytes after passing offset? Check the first 16 bytes (as hex digits), do they begin `09` etc and include a `17 01` at 12th position? – VC.One May 29 '16 at 13:16
  • I subspect it's because videojs doesn't support flv well. And yes, my server accepts byte ranges. When I click beyond the buffer line, browser will send a new request with `start` specified if it is playing mp4. As for flv, the browser will not send a new request. I have checked all my test flv files and they are H264 encoded. – Zen May 29 '16 at 15:53

0 Answers0