phoboslab / jsmpeg

MPEG1 Video Decoder in JavaScript
MIT License
6.38k stars 1.43k forks source link

recording from the websocket stream #3

Closed donpdonp closed 11 years ago

donpdonp commented 11 years ago

Hello. I've got a simple node.js script to connect to the stream server and write the video data to a file. I cannot get ffmpeg to understand the file contents nor get vlc to play the video. Can you suggest how I might archive the stream? The startRecording() method is not an option because this is server-side (aka not a browser).

Thanks

phoboslab commented 11 years ago

I'd still suggest to have a look at how the startRecording() function is implemented. You have to provide an MPEG file header and start recording at an intra frame (PICTURE_TYPE_I). Maybe VLC and ffmpeg can handle the non-intra-frame start, but the header is mandatory, I believe.

What exactly are you trying to do? Maybe there are easier ways, not using jsmpeg, to record a video stream somewhere.

donpdonp commented 11 years ago

Thanks for the feedback. I looked at the stream code and saw it was starting with an mpeg headed of some sort, so I was hoping that would be enough for vlc to start playing if the websocket stream was simply saved to disk. I guess not.

My goal is to have a 24/7 streaming webcam, and conceptually its very simple/appealing if there is but one node.js server that can handle webbrowser clients, and clients that are persisting the stream to disk. I can try and augment my node.js websocket client to use the header you mentioned contained in startRecording()

phoboslab commented 11 years ago

You're right. When you tune in into the WebSocket stream, chances are that you'll get a START_PICTURE code first. ffmpeg sends a whole picture to the stream server as soon as it is ready (encoded). So there are always small bursts of data coming in.

I don't know much about the internals of nodejs, but it's possible that the request.on('data', callback) is only fired once per frame (this probably depends on the encoded size of the frame). So you could assume that the first message you get from the WebSocket is likely to be a START_PICTURE code. Again, I don't know if this actually holds true.

This START_PICTURE code however does not provide enough info for VLC or any other player to decode it. The player has to know the width and height of the stream as well - which is provided in the file header.

So my guess is you only have to prepend this header to your mpeg file and you should be fine:

    // 3 bytes width & height, 12 bits each
    var wh1 = (this.width >> 4),
        wh2 = ((this.width & 0xf) << 4) | (this.height >> 8),
        wh3 = (this.height & 0xff);

    this.recordBuffers.push(new Uint8Array([
        0x00, 0x00, 0x01, 0xb3, // Sequence Start Code
        wh1, wh2, wh3, // Width & height
        0x13, // aspect ratio & framerate
        0xff, 0xff, 0xe1, 0x58, // Meh. Bitrate and other boring stuff
        0x00, 0x00, 0x01, 0xb8, 0x00, 0x08, 0x00 // GOP

        // 0x00, 0x00, 0x00, 0x01, 0x00 // First Picture Start Code; should be already provided by the incoming WS stream
    ]));
donpdonp commented 11 years ago

no luck with this approach.

var WebSocket = require('ws');
var ws = new WebSocket('ws://localhost:8084/');
ws.on('open', function() {
  process.stderr.write('open')
    var width = 320
    var height = 240
    var wh1 = (width >> 4),
        wh2 = ((width & 0xf) << 4) | (height >> 8),
        wh3 = (height & 0xff);
    var blob = new Buffer(new Uint8Array([
        0x00, 0x00, 0x01, 0xb3, // Sequence Start Code
        wh1, wh2, wh3, // Width & height
        0x13, // aspect ratio & framerate
        0xff, 0xff, 0xe1, 0x58, // Meh. Bitrate and other boring stuff
        0x00, 0x00, 0x01, 0xb8, 0x00, 0x08, 0x00, // GOP
        0x00, 0x00, 0x00, 0x01, 0x00 // First Picture Start Code
    ]));
  process.stdout.write(blob.toString('binary'))
});
ws.on('message', function(data, flags) {
  process.stdout.write(data)
 });

$ node save-stream.js > out.mpeg

the first few bytes of out.mpeg look correct, ascii 0,0,1,... 'file out.mpeg' still calls it 'data' instead of a video file.

phoboslab commented 11 years ago

You can't write binary data as a string.

Try this:

process.stdout.write(blob, 'binary');

// or just
process.stdout.write(blob); // blobs are always written as binary
donpdonp commented 11 years ago

that did it. its saving as an mpeg file now. thanks!