Closed AmpletecGo closed 5 years ago
What is HTTP-TS?
https://github.com/ossrs/srs/wiki/v2_EN_DeliveryHttpStream
HTTP TS Live Stream SRS supports remux the rtmp stream to http ts stream, when publish rtmp stream on SRS, SRS will mount a http ts url and when user access the ts url, SRS will remux the rtmp stream to user.
So you want to use SRS as the server? I don't think streaming over HTTP is possible in a cross browser way right now.
1) There's no cross-browser way to read binary data of an AJAX call, while the call is still loading 2) The data loaded from the stream would accumulate. We can't throw away old data, so watching the stream will eat more and more RAM
Maybe we could use HTTP Range-Requests here as well, but it seems like a huge hack.
Imho, the better option would be for SRS to support WebSockets :)
Do you konw https://github.com/Bilibili/flv.js
question 1. We can use nginx http proxy question 2. flv.js do not have this problem.
SRS to support WebSockets it seems like a huge hack.
I had a closer look at your implementation for the fetch source and cleaned it up a little. It works great, but only on Chrome - which means I'm not going to put it into the library proper at this point. I'll leave the implementation here as a reference. Thanks!
JSMpeg.Source.Fetch = (function(){ "use strict";
var FetchSource = function(url, options) {
this.url = url;
this.destination = null;
this.request = null;
this.completed = false;
this.established = false;
this.progress = 0;
this.aborted = false;
};
FetchSource.prototype.connect = function(destination) {
this.destination = destination;
};
FetchSource.prototype.start = function() {
var params = {
method: 'GET',
headers: new Headers(),
cache: 'default'
};
self.fetch(this.url, params).then(function(res) {
console.log(res.value);
if (res.ok && (res.status >= 200 && res.status <= 299)) {
this.progress = 1;
this.established = true;
return this.pump(res.body.getReader());
}
else {
//error
}
}.bind(this)).catch(function(err) {
throw(err);
});
};
FetchSource.prototype.pump = function(reader) {
return reader.read().then(function(result) {
if (result.done) {
this.completed = true;
}
else {
if (this.aborted) {
return reader.cancel();
}
if (this.destination) {
this.destination.write(result.value.buffer);
}
return this.pump(reader);
}
}.bind(this)).catch(function(err) {
throw(err);
});
};
FetchSource.prototype.resume = function(secondsHeadroom) {
// Nothing to do here
};
FetchSource.prototype.abort = function() {
this.aborted = true;
};
return FetchSource;
})();
Thanks.
Hi @AmpletecGo @phoboslab , I think supporting HTTP-TS streaming is really awesome. May I have a customer built to support HTTP-TS? Or any branch/forked repo is available?
Thank you very much.
Things have changed since February 2017. Most browsers support fetch now. Notably not IE (never IE) and to make it work in Firefox you must toggle some advanced settings.
Last month I developed a new working fetch streaming input module for jsmpeg, based on something I wrote for a different project. Here it is and theoretically it might be more robust than the one posted above. It works great for me anyway. Please feel free to use this however you see fit.
// This source can stream a .ts file using http. No websocket required.
// For browser compatiblity, read: https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream
JSMpeg.Source.FetchStream = (function ()
{
"use strict";
var FetchSource = function (url, options)
{
this.url = url;
this.options = options;
this.streamer = null;
this.includeCredentialsAllOrigins = false;
this.destination = null;
this.reconnectInterval = options.reconnectInterval !== undefined
? options.reconnectInterval
: 5;
this.shouldAttemptReconnect = !!this.reconnectInterval;
this.completed = false;
this.established = false;
this.progress = 0;
this.reconnectTimeoutId = 0;
};
FetchSource.prototype.connect = function (destination)
{
this.destination = destination;
};
FetchSource.prototype.destroy = function ()
{
clearTimeout(this.reconnectTimeoutId);
this.shouldAttemptReconnect = false;
if (this.streamer)
this.streamer.close();
};
FetchSource.prototype.start = function ()
{
this.shouldAttemptReconnect = !!this.reconnectInterval;
this.progress = 0;
this.established = false;
this.streamer = new FetchStreamer(this);
};
FetchSource.prototype.resume = function (secondsHeadroom)
{
// Nothing to do here
};
FetchSource.prototype.onClose = function ()
{
if (this.shouldAttemptReconnect)
{
clearTimeout(this.reconnectTimeoutId);
this.reconnectTimeoutId = setTimeout(function ()
{
this.start();
}.bind(this), this.reconnectInterval * 1000);
}
};
function FetchStreamer(source)
{
var self = this;
var abort_controller = null;
var reader = null;
var cancel_streaming = false;
/**
* Call when it is time to close the connection.
*/
this.close = function ()
{
cancel_streaming = true;
if (abort_controller)
{
abort_controller.abort();
abort_controller = null;
}
if (reader)
{
var cancelPromise = reader.cancel("Streaming canceled");
if (cancelPromise && cancelPromise["catch"])
{
cancelPromise["catch"](function (err)
{
if (DOMException && DOMException.ABORT_ERR && err && err.code === DOMException.ABORT_ERR)
{
// Expected result. Don't spam console.
}
else if (DOMException && DOMException.INVALID_STATE_ERR && err && err.code === DOMException.INVALID_STATE_ERR)
{
// Expected result in MS Edge.
}
else
console.error(err);
});
}
reader = null;
}
source.onClose();
};
function pump()
{
// Do NOT return before the first reader.read() or the fetch can be left in a bad state!
// Except if reader is null of course.
if (reader === null)
return;
reader.read().then(function (result)
{
try
{
if (result.done || cancel_streaming)
{
self.close();
return;
}
if (source.destination)
source.destination.write(result.value);
return pump();
}
catch (ex)
{
console.error(ex);
self.close();
}
}
)["catch"](function (err)
{
console.error(err);
self.close();
});
}
// Prepare to open connection
var fetchArgs = { credentials: "same-origin" };
if (source.includeCredentialsAllOrigins)
fetchArgs.credentials = "include";
if (typeof AbortController === "function")
{
// FF 57+, Edge 16+ (in theory)
abort_controller = new AbortController();
fetchArgs.signal = abort_controller.signal;
}
// Open connection
fetch(source.url, fetchArgs)
.then(function (res)
{
try
{
source.progress = 1;
source.established = true;
if (!res.ok)
responseError = res.status + " " + res.statusText;
// Do NOT return before the first reader.read() or the fetch can be left in a bad state!
reader = res.body.getReader();
return pump(reader);
}
catch (ex)
{
console.error(ex);
self.close();
}
})["catch"](function (err)
{
console.error(err);
self.close();
});
}
return FetchSource;
})();
@bp2008 Do you have an update version that works with latest jsmpeg?
No, I just have what I posted above.
Hello master, JSMpeg can support HTTP-TS live-streaming ?