Support for WebRTC data tracks in the player

This commit is contained in:
Cat 2024-01-17 16:39:54 +01:00 committed by Thulinma
parent ebe783666f
commit 72bc25cef0
4 changed files with 140 additions and 9 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -677,7 +677,13 @@ function MistVideo(streamName,options) {
listeners: {}, listeners: {},
init: function(){ init: function(){
var me = this; var me = this;
this.socket = new WebSocket(MistUtil.http.url.addParam(MistVideo.urlappend(json_source.url),{rate:1})); if (MistVideo.player.api.metaTrackSocket) {
this.socket = new MistVideo.player.api.metaTrackSocket();
}
else {
this.socket = new WebSocket(MistUtil.http.url.addParam(MistVideo.urlappend(json_source.url),{rate:1}));
}
me.send_queue = []; me.send_queue = [];
me.checktimer = null; me.checktimer = null;
me.s = function(obj){ me.s = function(obj){
@ -711,11 +717,16 @@ function MistVideo(streamName,options) {
if (!message) { MistVideo.log("Subtitle websocket received invalid message."); return; } if (!message) { MistVideo.log("Subtitle websocket received invalid message."); return; }
if (("time" in message) && ("track" in message) && ("data" in message)) { if (("time" in message) && ("track" in message) && ("data" in message)) {
var pushed = false;
if ("all" in me.subscriptions) {
me.subscriptions.all.buffer.push(message);
pushed = true;
}
if (message.track in me.subscriptions) { if (message.track in me.subscriptions) {
//console.warn("received:",message.track,message.data); me.subscriptions[message.track].buffer.push(message);
me.subscriptions[message.track].buffer.push(message); pushed = true;
console.warn("received:",message.track,message.time*1e-3,"currentTime:",MistVideo.player.api.currentTime,"latency",Math.round(MistVideo.player.api.currentTime-message.time*1e-3),"bufferlength:",me.subscriptions[message.track].buffer.length,"timer:",!!me.checktimer); }
if (pushed) {
if (!me.checktimer) { if (!me.checktimer) {
me.check(); me.check();
} }
@ -860,6 +871,10 @@ function MistVideo(streamName,options) {
this.listeners = {}; this.listeners = {};
}, },
add: function (trackid,callback) { add: function (trackid,callback) {
if ((typeof trackid == "function") && (!callback)) {
callback = trackid;
trackid = "all";
}
if (typeof callback != "function") { return; } if (typeof callback != "function") { return; }
if (!(trackid in this.subscriptions)) { if (!(trackid in this.subscriptions)) {
@ -897,6 +912,9 @@ function MistVideo(streamName,options) {
} }
} }
}; };
if (typeof options.subscribeToMetaTrack == "function") {
options.subscribeToMetaTrack = [["all",options.subscribeToMetaTrack]];
}
if (options.subscribeToMetaTrack.length) { if (options.subscribeToMetaTrack.length) {
if (typeof options.subscribeToMetaTrack[0] != "object") { if (typeof options.subscribeToMetaTrack[0] != "object") {
options.subscribeToMetaTrack = [options.subscribeToMetaTrack]; options.subscribeToMetaTrack = [options.subscribeToMetaTrack];

View file

@ -193,6 +193,7 @@ p.prototype.build = function (MistVideo,callback) {
//track type not found, this should not happen //track type not found, this should not happen
continue; continue;
} }
if (type == "subtitle") { continue; }
//create an event to pass this to the skin //create an event to pass this to the skin
MistUtil.event.send("playerUpdate_trackChanged",{ MistUtil.event.send("playerUpdate_trackChanged",{
@ -209,7 +210,7 @@ p.prototype.build = function (MistVideo,callback) {
MistVideo.reporting.stats.d.tracks = ev.tracks.join(","); MistVideo.reporting.stats.d.tracks = ev.tracks.join(",");
} }
}, },
on_seek: function(e){ seek: function(e){
var thisPlayer = this; var thisPlayer = this;
MistUtil.event.send("seeked",seekoffset,video); MistUtil.event.send("seeked",seekoffset,video);
@ -231,7 +232,7 @@ p.prototype.build = function (MistVideo,callback) {
} }
else { video.play(); } else { video.play(); }
}, },
on_speed: function(e){ set_speed: function(e){
this.webrtc.play_rate = e.play_rate_curr; this.webrtc.play_rate = e.play_rate_curr;
MistUtil.event.send("ratechange",e,video); MistUtil.event.send("ratechange",e,video);
}, },
@ -271,6 +272,11 @@ p.prototype.build = function (MistVideo,callback) {
thisWebRTCPlayer.isConnected = false; thisWebRTCPlayer.isConnected = false;
break; break;
} }
case "on_error": {
MistVideo.showError("WebRTC error: "+MistUtil.format.ucFirst(ev.message));
return;
break;
}
} }
if (ev.type in me.listeners) { if (ev.type in me.listeners) {
return me.listeners[ev.type].call(me,("data" in ev)?ev.data:ev); return me.listeners[ev.type].call(me,("data" in ev)?ev.data:ev);
@ -323,10 +329,15 @@ p.prototype.build = function (MistVideo,callback) {
opts.iceServers = MistVideo.source.RTCIceServers; opts.iceServers = MistVideo.source.RTCIceServers;
} }
thisWebRTCPlayer.peerConn = new RTCPeerConnection(opts); thisWebRTCPlayer.peerConn = new RTCPeerConnection(opts);
thisWebRTCPlayer.MetaDataTrack = thisWebRTCPlayer.peerConn.createDataChannel("*",{protocol:"JSON"});
thisWebRTCPlayer.peerConn.ontrack = function(ev) { thisWebRTCPlayer.peerConn.ontrack = function(ev) {
video.srcObject = ev.streams[0]; video.srcObject = ev.streams[0];
if (callback) { callback(); } if (callback) { callback(); }
}; };
thisWebRTCPlayer.peerConn.ondatachannel = function(){
console.warn("ondatachannel",arguments);
};
thisWebRTCPlayer.peerConn.onconnectionstatechange = function(e){ thisWebRTCPlayer.peerConn.onconnectionstatechange = function(e){
if (MistVideo.destroyed) { return; } //the player doesn't exist any more if (MistVideo.destroyed) { return; } //the player doesn't exist any more
switch (this.connectionState) { switch (this.connectionState) {
@ -368,6 +379,8 @@ p.prototype.build = function (MistVideo,callback) {
} }
} }
}; };
MistUtil.event.send("webrtc_ready",null,video);
}); });
}; };
@ -393,6 +406,7 @@ p.prototype.build = function (MistVideo,callback) {
this.stop = function(){ this.stop = function(){
if (!this.isConnected) { throw "Not connected, cannot stop." } if (!this.isConnected) { throw "Not connected, cannot stop." }
n_
this.signaling.send({type: "stop"}); this.signaling.send({type: "stop"});
}; };
this.seek = function(seekTime){ this.seek = function(seekTime){
@ -747,6 +761,105 @@ p.prototype.build = function (MistVideo,callback) {
track.onload = correctSubtitleSync; track.onload = correctSubtitleSync;
} }
}; };
me.api.metaTrackSocket = function(){
//console.warn("new metaTrackSocket");
this.origin = {};
this.CONNECTING = 0;
this.OPEN = 1;
this.CLOSING = 2;
this.CLOSED = 3;
this.readyState = 0;
//follow readystate of origin, except when self is asked to close, then pretend to close and remove event listeners.
this.listeners = [];
var me = this;
MistUtil.event.addListener(MistVideo.video,"webrtc_ready",function(){
me.init();
});
this.init = function(){
this.origin = MistVideo.player.webrtc && MistVideo.player.webrtc.MetaDataTrack ? MistVideo.player.webrtc.MetaDataTrack : {};
//console.warn("init",this.origin);
if ("readyState" in this.origin) {
//console.warn("origin readystate",this.origin.readyState);
function onopen() {
me.readyState = me.OPEN;
me.onopen();
}
this.origin.addEventListener("open",onopen);
this.origin.onmessage = function(e){
//console.warn("metadata message",e);
};
this.origin.addEventListener("close",function(){
me.readyState = me.CLOSED;
me.onclose();
});
if (this.origin.readyState == "open") { onopen(); }
return true;
}
else {
return false;
}
};
this.open = function(){
//should be open once webrtc is active
if (this.readyState == this.OPEN) return; //already open
switch (this.origin.readyState) {
case "connecting": { this.readyState = this.CONNECTING; break; }
case "open": { this.readyState = this.OPEN; break; }
case "closing": { this.readyState = this.CLOSING; break; }
case "closed": { this.readyState = this.CLOSED; break; }
}
for (var i in this.listeners) {
this.origin.addEventListener.apply(this.origin,this.listeners[i]);
}
};
this.close = function(){
//don't actually close, but pretend
if (this.readyState >= this.CLOSING) return; //already closed
this.readyState = this.CLOSED;
//remove listeners
for (var i in this.listeners) {
this.removeEventListener.apply(this,this.listeners[i]);
}
};
this.send = function(){
if (this.origin.readyState == "open") return this.origin.send.apply(this,arguments);
return false;
};
this.onopen = function(){};
this.onclose = function(){};
this.addEventListener = function(){
this.listeners.push(arguments);
return this.origin.addEventListener.apply(this.origin,arguments);
};
this.removeEventListener = function(name,func){
//remove them from the listeners array and the origin
for (var i = this.listeners.length-1; i >= 0; i--) {
if ((name == this.listeners[i][0]) && (func == this.listeners[i][1])) {
this.listeners.splice(i,1);
break;
}
}
return this.origin.removeEventListener.apply(this.origin,arguments);
};
this.init();
return this;
};
//loop //loop
MistUtil.event.addListener(video,"ended",function(){ MistUtil.event.addListener(video,"ended",function(){