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: {},
init: function(){
var me = this;
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.checktimer = null;
me.s = function(obj){
@ -711,11 +717,16 @@ function MistVideo(streamName,options) {
if (!message) { MistVideo.log("Subtitle websocket received invalid message."); return; }
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) {
//console.warn("received:",message.track,message.data);
me.subscriptions[message.track].buffer.push(message);
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);
pushed = true;
}
if (pushed) {
if (!me.checktimer) {
me.check();
}
@ -860,6 +871,10 @@ function MistVideo(streamName,options) {
this.listeners = {};
},
add: function (trackid,callback) {
if ((typeof trackid == "function") && (!callback)) {
callback = trackid;
trackid = "all";
}
if (typeof callback != "function") { return; }
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 (typeof options.subscribeToMetaTrack[0] != "object") {
options.subscribeToMetaTrack = [options.subscribeToMetaTrack];

View file

@ -193,6 +193,7 @@ p.prototype.build = function (MistVideo,callback) {
//track type not found, this should not happen
continue;
}
if (type == "subtitle") { continue; }
//create an event to pass this to the skin
MistUtil.event.send("playerUpdate_trackChanged",{
@ -209,7 +210,7 @@ p.prototype.build = function (MistVideo,callback) {
MistVideo.reporting.stats.d.tracks = ev.tracks.join(",");
}
},
on_seek: function(e){
seek: function(e){
var thisPlayer = this;
MistUtil.event.send("seeked",seekoffset,video);
@ -231,7 +232,7 @@ p.prototype.build = function (MistVideo,callback) {
}
else { video.play(); }
},
on_speed: function(e){
set_speed: function(e){
this.webrtc.play_rate = e.play_rate_curr;
MistUtil.event.send("ratechange",e,video);
},
@ -271,6 +272,11 @@ p.prototype.build = function (MistVideo,callback) {
thisWebRTCPlayer.isConnected = false;
break;
}
case "on_error": {
MistVideo.showError("WebRTC error: "+MistUtil.format.ucFirst(ev.message));
return;
break;
}
}
if (ev.type in me.listeners) {
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;
}
thisWebRTCPlayer.peerConn = new RTCPeerConnection(opts);
thisWebRTCPlayer.MetaDataTrack = thisWebRTCPlayer.peerConn.createDataChannel("*",{protocol:"JSON"});
thisWebRTCPlayer.peerConn.ontrack = function(ev) {
video.srcObject = ev.streams[0];
if (callback) { callback(); }
};
thisWebRTCPlayer.peerConn.ondatachannel = function(){
console.warn("ondatachannel",arguments);
};
thisWebRTCPlayer.peerConn.onconnectionstatechange = function(e){
if (MistVideo.destroyed) { return; } //the player doesn't exist any more
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(){
if (!this.isConnected) { throw "Not connected, cannot stop." }
n_
this.signaling.send({type: "stop"});
};
this.seek = function(seekTime){
@ -748,6 +762,105 @@ p.prototype.build = function (MistVideo,callback) {
}
};
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
MistUtil.event.addListener(video,"ended",function(){
if (me.api.loop) {