Giter Club home page Giter Club logo

videostream's Introduction

Videostream

Streams data from a file-like seekable object into a <video> or <audio> node (a HTMLMediaElement). Seeking the media element will request a different byte range from the incoming file-like object.

For now only mp4 files are supported. The goal is to support most files that conform to ISO/IEC 14496-12.

Version 2 is completely rewritten and substantially more robust than the previous version that relied on mp4box.js. The only major regression compared to the previous architecture is that fragmented mp4 files aren't supported. If this is a problem I may add support again at some point.

Support for most other formats will take significant work.

Usage

Videostream just exports a function. Use it like this:

const VideoStream = require('videostream')

const exampleFile = {
  createReadStream (opts) {
    const { start, end } = opts
    // Return a readable stream that provides the bytes
    // between offsets "start" and "end" inclusive
  }
}

const video = document.createElement('video')
const videostream = new VideoStream(exampleFile, video)

video.addEventListener('error', () => {
  // listen for errors on the video/audio element directly
  const errorCode = elem.error
  const detailedError = videostream.detailedError
  // videostream.detailedError will often have a more detailed error message
})

Errors

Handle errors by listening to the 'error' event on the <video> or <audio> tag.

Some (but not all) errors will also cause videostream.detailedError to be set to an error value that has a more informative error message.

License

MIT. Copyright (c) John Hiesey.

videostream's People

Contributors

amilajack avatar chocobozzz avatar daniel-abrecht avatar dependabot[bot] avatar feross avatar jhiesey avatar jimfilippou avatar jimmywarting avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

videostream's Issues

How to wait for data

Let's say that I am manually generating the data. I can't really see the way to do it here:

var exampleFile = {
    length: 100000, // Total size of the file
    createReadStream: function (opts) {
        var start = opts.start;
        var end = opts.end;
        // Return a readable stream that provides the bytes
        // between offsets "start" and "end" inclusive
    }
}

Let's say that start=100 end=200 is requested and I do not have and cannot yet generate the data when this request has happened. I will however will be able to provide the data in 5 seconds. Is it possible to do this?

Also, is there a documentation that defines the interface of the object to be returned in createReadStream?

Thanks.

P.S. The environment is the latest Chromium browser.

Support fragmented mp4 files

Since the rewrite, fragmented mp4 files aren't supported any more.

Is this a feature people want? If so, let me know. If not, this isn't a high priority issue.

value out of bounds and slice undefined error (in WebTorrent)

Copied at @jhiesey request from webtorrent/webtorrent#1440

What version of WebTorrent?
0.99.3
What operating system and Node.js version?.
OSX - not in node
What browser and version? (if using WebTorrent in the browser)
Chrome 67.0.3396.99
What did you expect to happen?
Download and play
What actually happened?
Errors ... see below especially the "value" argument is out of bounds and Uncaught TypeError: Cannot read property 'slice' of undefined

The URL is https://dweb.me/arc/archive.org/details/hanabookstory?version=true
I also had a report of this page complaining about an invalid hash, but can't repeat that.
Note that https://dweb.me/arc/archive.org/details/commute?verbose=true works, so it seems to be specific to WT not handling errors for this specific file.

dweb-transports-bundle.js:147 WebSocket connection to 'wss://tracker.fastcast.nz/' failed: Error in connection establishment: net::ERR_CONNECTION_TIMED_OUT
f @ dweb-transports-bundle.js:147
dweb-transports-bundle.js:147 WebTorrent Torrent WARNING: connection error to wss://tracker.fastcast.nz (hanabookstory)
n.on.e @ dweb-transports-bundle.js:147
r.emit @ dweb-transports-bundle.js:8
(anonymous) @ dweb-transports-bundle.js:147
r.emit @ dweb-transports-bundle.js:8
r._onWarning @ dweb-transports-bundle.js:147
r.emit @ dweb-transports-bundle.js:8
p._onSocketError @ dweb-transports-bundle.js:147
e._onSocketErrorBound @ dweb-transports-bundle.js:147
i @ dweb-transports-bundle.js:8
r.emit @ dweb-transports-bundle.js:8
f._destroy @ dweb-transports-bundle.js:147
f.destroy @ dweb-transports-bundle.js:147
r._ws.onerror @ dweb-transports-bundle.js:147
index.js:1259 Uncaught RangeError: "value" argument is out of bounds
    at checkInt (index.js:1259)
    at Uint8Array.writeUInt32BE (index.js:1369)
    at exports.trun.encode (boxes.js:842)
    at Object.Box._encode (index.js:78)
    at eval (index.js:66)
    at Array.forEach (<anonymous>)
    at Object.Box._encode (index.js:57)
    at eval (index.js:62)
    at Array.forEach (<anonymous>)
    at eval (index.js:61)
checkInt @ index.js:1259
writeUInt32BE @ index.js:1369
exports.trun.encode @ boxes.js:842
Box._encode @ index.js:78
(anonymous) @ index.js:66
Box._encode @ index.js:57
(anonymous) @ index.js:62
(anonymous) @ index.js:61
Box._encode @ index.js:57
Box.encode @ index.js:33
Encoder.box @ encode.js:63
writeFragment @ mp4-remuxer.js:318
(anonymous) @ mp4-remuxer.js:314
MP4Remuxer.seek @ mp4-remuxer.js:294
VideoStream._pump @ videostream.js:78
VideoStream.self._onWaiting @ videostream.js:34
mp4-remuxer.js:321 Uncaught TypeError: Cannot read property 'slice' of undefined
    at eval (mp4-remuxer.js:321)
    at Item.run (browser.js:153)
    at drainQueue (browser.js:123)
(anonymous) @ mp4-remuxer.js:321
Item.run @ browser.js:153
drainQueue @ browser.js:123
setTimeout (async)
runTimeout @ browser.js:41
process.nextTick @ browser.js:143
nextTick @ index.js:26
maybeReadMore @ _stream_readable.js:517
addChunk @ _stream_readable.js:300
readableAddChunk @ _stream_readable.js:278
Readable.push @ _stream_readable.js:245
Encoder.box @ encode.js:64
writeFragment @ mp4-remuxer.js:318
(anonymous) @ mp4-remuxer.js:314
MP4Remuxer.seek @ mp4-remuxer.js:294
VideoStream._pump @ videostream.js:78
VideoStream.self._onWaiting @ videostream.js:34
dweb-transports-bundle.js:147 WebSocket connection to 'wss://tracker.fastcast.nz/' failed: Error in connection establishment: net::ERR_CONNECTION_TIMED_OUT
f @ dweb-transports-bundle.js:147
p._openSocket @ dweb-transports-bundle.js:147
(anonymous) @ dweb-transports-bundle.js:147
dweb-transports-bundle.js:147 WebTorrent Torrent WARNING: connection error to wss://tracker.fastcast.nz (hanabookstory)
n.on.e @ dweb-transports-bundle.js:147
r.emit @ dweb-transports-bundle.js:8
(anonymous) @ dweb-transports-bundle.js:147
r.emit @ dweb-transports-bundle.js:8
r._onWarning @ dweb-transports-bundle.js:147
r.emit @ dweb-transports-bundle.js:8
p._onSocketError @ dweb-transports-bundle.js:147
e._onSocketErrorBound @ dweb-transports-bundle.js:147
i @ dweb-transports-bundle.js:8
r.emit @ dweb-transports-bundle.js:8
f._destroy @ dweb-transports-bundle.js:147
f.destroy @ dweb-transports-bundle.js:147
r._ws.onerror @ dweb-transports-bundle.js:147
error (async)
f @ dweb-transports-bundle.js:147
p._openSocket @ dweb-transports-bundle.js:147
(anonymous) @ dweb-transports-bundle.js:147
setTimeout (async)
p._startReconnectTimer @ dweb-transports-bundle.js:147
p._onSocketError @ dweb-transports-bundle.js:147
e._onSocketErrorBound @ dweb-transports-bundle.js:147
i @ dweb-transports-bundle.js:8
r.emit @ dweb-transports-bundle.js:8
f._destroy @ dweb-transports-bundle.js:147
f.destroy @ dweb-transports-bundle.js:147
r._ws.onerror @ dweb-transports-bundle.js:147
dweb-transports-bundle.js:147 WebSocket connection to 'wss://tracker.fastcast.nz/' failed: Error in connection establishment: net::ERR_CONNECTION_TIMED_OUT
f @ dweb-transports-bundle.js:147
p._openSocket @ dweb-transports-bundle.js:147
(anonymous) @ dweb-transports-bundle.js:147
dweb-transports-bundle.js:147 WebTorrent Torrent WARNING: connection error to wss://tracker.fastcast.nz (hanabookstory)

[Bug] Some encode can't seek video in Chrome

Hello
Please check video below.
http://www.mediafire.com/file/zywvmzq6i3eowt4/demo.mp4/file

It play well with basic mp4 method. But got issue with mediasource. Can't seek.
When seek video, player will buff infinity.
If not seek, video play normal.

Play with video tag + mp4 url: All device and browsers.
Play with mediasource: Only work with Firefox, got issue with Chrome.

Info file

Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'demo.mp4':
Metadata:
major_brand : mp42
minor_version : 0
compatible_brands: isommp42
encoder : Google
Duration: 00:44:40.65, start: 0.000000, bitrate: 364 kb/s
Stream #0:0(und): Video: h264 (Main) (avc1 / 0x31637661), yuv420p(tv, bt709), 640x360 [SAR 1:1 DAR 16:9], 264 kb/s, 25 fps, 25 tbr, 12800 tbn, 50 tbc (default)
Metadata:
handler_name : ISO Media file produced by Google Inc.
Stream #0:1(und): Audio: aac (LC) (mp4a / 0x6134706D), 44100 Hz, stereo, fltp, 96 kb/s (default)
Metadata:
handler_name : ISO Media file produced by Google Inc.

Some files don't play

There are lots of files that don't work. Add a comment here for each file that doesn't work and I'll try to see what's wrong and make individual issues for the various problems I see.

Problems when streaming with preload="none"

I can't stream to a video element with preload="none" since the rewrite, I had a look at the code and it seems that the event sourceopen is not fired because of the lack of preload and then I have an error when playing video:

Uncaught Error: The argument to MediaElementWrapper.createWriteStream must be a string or a previous stream returned from that function

In fact it is a previous stream object but the buffer has not been created yet.

Second problem in this configuration is that the stream begins to be pumped before the video is actually played and this is the preloading I was trying to avoid.

What do you think about this?

MP4Box.js returns invalid offset when given .mp3 file

If you give videostream a file of the wrong type (like .mp3) it passes it to mp4box.js which doesn't realize that the file is not an MP4. It then asks for a crazy huge offset way bigger than the actual file length, which gets passed through to file.createReadStream({ start: xxx, end: xxx }) and fails.

Not a hugely important issue, since I shouldn't be giving it an .mp3, but just sharing since it would be nice if it was handled better.

When the media element emits 'error', destroy everything

When an 'error' event is emitted on the <video> tag, I expect videostream to destroy all it's state and stop doing all work. Instead, it continues to call popBuffers in a tight loop (despite the 5ms -> 5s fix I just pushed), printing 1000s of errors to my console in seconds!

I think videostream should listen for 'error' on the media element and destroy itself if it's ever emitted.

Not able to use videostrem in next.js

Hi,

I have posted question here - https://stackoverflow.com/q/67980499/9640177

I am trying to play video using media stream in next js. My api looks like this

import fs from 'fs'
import path from 'path'

export default function (req, res) {
  let f = 'file_name';
  if (req.method === 'POST') {
    const data = JSON.parse(req.body);
    f = data.path;
  }
  const filePath = path.resolve('.', f)
  const stat = fs.statSync(filePath)
  res.writeHead(200, {
    'Content-Type': 'audio/mpeg',
    'Content-Length': stat.size
  });
  const readStream = fs.createReadStream(filePath);
  readStream.pipe(res);
}

and in the index.js

fetch('/api/hello', {
  method: 'POST',
  body: JSON.stringify({ path: 'my_file' })
}).then(res => res.blob()).then((blob) => {
  const url = URL.createObjectURL(blob);
  vel.current.src = url;
})

This works well for small videos. However, I am trying to use media stream to avoid pulling all data into blob before I can play that.

I tried various ways including Videostream with no success.

Specifically, I don't understand how to create the "readable stream that provides the bytes between offsets "start" and "end" inclusive" - required by Videostream.

The goal is to allow only authenticated users to access the videos exposed by api and make it difficult to download while keeping the ux as smooth as possible.

https://stackoverflow.com/q/67980499/9640177

Installing via npm fails

When installing via npm, it fails on mp4box dep:

root@scw-60409e:~/nodetorrent# npm install videostream
npm WARN package.json [email protected] No description
npm WARN package.json [email protected] No repository field.
npm WARN package.json [email protected] No README data
npm ERR! not a package /tmp/npm-11021-70qzHGrw/github.com/jhiesey/mp4box.js.git
npm ERR! Error: ENOENT, open '/tmp/npm-11021-70qzHGrw/github.com/jhiesey/mp4box.js.git-unpack/package.json'
npm ERR! If you need help, you may report this *entire* log,
npm ERR! including the npm and node versions, at:
npm ERR!     <http://github.com/npm/npm/issues>

npm ERR! System Linux 3.2.34-30
npm ERR! command "/usr/bin/node" "/usr/bin/npm" "install" "videostream"
npm ERR! cwd /root/nodetorrent
npm ERR! node -v v0.10.40
npm ERR! npm -v 1.4.28
npm ERR! path /tmp/npm-11021-70qzHGrw/github.com/jhiesey/mp4box.js.git-unpack/package.json
npm ERR! code ENOENT
npm ERR! errno 34
npm ERR! not ok code 0

Failed to load resource: the server responded with a status of 416 (Requested Range Not Satisfiable)

I used the example to try to stream a mkv video and a mp4 video but for both of them I've got the error above. Indeed the first request is ok with a range of 0-1999999/1524563968, the second is OK too because my file is big but has not the good range : 1380533830-1382533829/1524563968 and then the third an others http request have too large range like 2080634147-2082634146/1524563968 and I get of course the error above.

I've just cloned the github, and changed the file name inside it, nothing else so I've tried to understand the code but it's a bit annoying for me so could you explain me why I get this error ? 🎯

MKV support

Right now, WebTorrent uses the videostream package to provide support for MP4. It would be great to be able to stream MKV files since it's a very popular container format.

Problem using webworkers with videostream

I have trouble running the code below with videostream. It's essentially what you did in example/ but moving out the XHR to a web worker.

In my code, videostream only goes through 3 streams and then stops completely.

This is index.js:

var MultiStream = require('multistream')
var stream = require('stream');
var inherits = require('inherits');
var videostream = require('videostream');
var Buffer = require('buffer/').Buffer;
var worker = new Worker('build/worker.js');

var MB = 1000000;
var REQUEST_SIZE = 2*MB;


inherits(WorkerStream, stream.Readable);
function WorkerStream(opt) {
  opt = opt || {};
  stream.Readable.call(this, opt);
}

WorkerStream.prototype._read = function() {};

var file = function(path) {
  var self = this
  self.path = path
}

file.prototype.createReadStream = function(opts) {
  var self = this;
  opts = opts || {};
  var start = opts.start || 0;
  var fileSize = -1;

  var multi = new MultiStream(function(cb) {
    var end = opts.end ? (opts.end + 1) : fileSize;

    var reqStart = start;
    var reqEnd = start + REQUEST_SIZE;

    if (end >= 0 && reqEnd > end) {
      reqEnd = end;
    }
    if (reqStart >= reqEnd) {
      return cb(null, null);
    }

    console.log(reqStart, reqEnd)
    /*
    Only prints
    0 2000000
    24 2000024  // probably for the headers
    2000000 4000000  // doesn't start from 2000024?
    */

    var payload = {
      path: self.path,
      start: reqStart,
      end: reqEnd,
    };

    worker.onmessage = function(e) {
      fileSize = e.data.fileSize;
      var buf = new Buffer(e.data.chunk);

      var workerStream = new WorkerStream();
      workerStream.push(buf);
      workerStream.push(null);

      cb(null, workerStream);
    }

    worker.postMessage(payload);

    // For the next request
    start = reqEnd;
  });

  var destroy = multi.destroy
  multi.destroy = function() {
    destroy.call(multi);
  };

  return multi;
}

var video = document.querySelector('video');
video.addEventListener('error', function(err) {
  console.error(video.error);
});

videostream(new file('../the-social-network.mp4'), video);

worker.js

self.onmessage = function(e) {
  var opts = e.data;
  var {start, end, path} = opts;

  var xhr = new XMLHttpRequest();

  xhr.onreadystatechange = function() {
    if (xhr.readyState == XMLHttpRequest.DONE) {
      var chunk = xhr.response;
      var contentRange = xhr.getResponseHeader('Content-Range');
      var fileSize = parseInt(contentRange.split('/')[1], 10);

      postMessage({ fileSize, chunk });
    }
  }

  var range = 'bytes=' + start + '-' + (end-1);
  xhr.open("GET", path);
  xhr.setRequestHeader('Range', range);
  xhr.responseType = 'arraybuffer';
  xhr.send();
};

Object prototype may only be an Object or null

It breaks just doing a require

var videostream = require('videostream')

Trace:

util.js:555
  ctor.prototype = Object.create(superCtor.prototype, {
                          ^
TypeError: Object prototype may only be an Object or null
    at Function.create (native)
    at exports.inherits (util.js:555:27)
    at Object.<anonymous> (/fakepath/webtorrent-test/node_modules/videostream/mp4-remuxer.js:20:1)
    at Module._compile (module.js:456:26)
    at Object.Module._extensions..js (module.js:474:10)
    at Module.load (module.js:356:32)
    at Function.Module._load (module.js:312:12)
    at Module.require (module.js:364:17)
    at require (module.js:380:17)
    at Object.<anonymous> (/fakepath/webtorrent-test/node_modules/videostream/videostream.js:6:18)

Any ideas ?

Backpressure doesn't work properly

Currently backpressure is only applied once the browser starts throwing exceptions indicating that too much data is buffered. This is causing very high cpu usage in chrome and is just very inelegant in general.

Instead the amount of data given to the browser should be limited directly, by counting the number of bytes, frames, or seconds currently buffered. Figuring out how to do this elegantly may be a bit complex.

Converting data to an ArrayBuffer in node

Hi, I want to use videostream with webtorrent in node. This is because I'm trying to make an electron app which runs the hybrid client version of webtorrent, for streaming videos. When I try to use videostream as a module in node I get:

"Uncaught TypeError: data.arrayBuffer is not a function", source: /usr/lib/node_modules/webtorrent/node_modules/dezalgo/node_modules/asap/asap.js

I think this is because node doesn't support toArrayBuffer. I tried using the node buffer API instead but I couldn't get it to work. Could you please help? Thanks.

Firefox 43 error

Visit https://webtorrent.io with Firefox 43. Video fails to load, and this error is in the console:

"InvalidStateError: An attempt was made to use an object that is not, or is no longer, usable"

Works fine on Firefox Nightly (46). Thoughts?

Support additional codecs and video formats?

Are there plans to add support for additional codecs and video formats? We are using this plugin in the LBRY Desktop App (https://github.com/lbryio/lbry-desktop) as part of RenderMedia and we'd like to be able to support other formats/codecs like AVI/MKV/MOV/MPEG/FLV (not streaming necessarily, but at least the ability to play them when fully downloaded)

We would tip in LBRY Credits (LBC) for anyone that would want to help out.

MP3 audio seems to be broken

Hi,

I spent some time trying to figure out why a specific video wouldn't play (it plays fine over http, but not so much via webtorrent+videostream). Thought it had something to do with atoms and all, but it looks like videostream just has some problem with the mp3 track (reencoding with -vcodec copy -acodec aac completely solved the issue).

Sorry, I don't currently have an isolated test case so I'm just speculating. But if that's indeed the case then I think it should be documented.

Edit: yep, tried reencoding Sintel audio as mp3 and uploading it to https://instant.io - interestingly, it plays on the upload (seed) page, but not when opening the link in another tab.

Edit 2: ok, it seems to have something to do with the atoms, too.

Intermittent, unrecoverable freezes in Firefox when seeking

I am getting intermittent, unrecoverable freezes whilst seeking an H.264 encoded MP4 in Firefox 84 on MacOS Big Sur. The video hangs as if it is waiting for data, even if the video has finished downloading completely. I am using WebTorrent to download the popular CC-licensed "Sintel" video. The same behaviour can be seen on WebTorrent's own website.

I have not tested any other OS or version of Firefox; however, I can confidently state that this same issue is not present in Chrome.

This behaviour occurs even when the video has finished downloading completely, leading me to believe it's an issue with how the video is buffered into the HTML5 video element. Safari has had issues with seeking for quite some time. I wonder if this is related?

Is there any way we can make sure the data is buffered prior to the seek event being fully triggered?

I've attached a video which illustrates the problem I am facing.

Seeking.mov

Raspberry Pi performance

It is possible to increase performance for raspberry pi?
Raspberry Pi is powerful to watch 1080p 60 fps video but in app with this plugin videos lag so much.

All AudioTracks {Feature Request}

Hello,

at the moment the code select the first possible audio track. But is it possible to "load" all audiotracks, and switch it at runtime?

Thank you!

Best regards
Stephan

Playback error: The SourceBuffer is full, and cannot free space to append additional buffers.

Following the example, given in the readme, I tried creating a stream that simply referenced a file on my harddrive:

const exampleFile = {
    createReadStream: function (opts) {
        // Return a readable stream that provides the bytes
        // between offsets "start" and "end" inclusive
	console.log('Stream between ' + opts.start + ' and ' + opts.end + '.');
	return fs.createReadStream(path.join(process.env.HOME, "Downloads", "dopeman.mp4"), opts)
    }
}

When trying to play the video, I get this error:
image

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.