@@ -35,7 +35,8 @@ function MistVideo(streamName,options) {
3535 maxheight : false , //no max height (apart from targets dimensions)
3636 ABR_resize : true , //for supporting wrappers: when the player resizes, request a video track that matches the resolution best
3737 ABR_bitrate : true , //for supporting wrappers: when there are playback issues, request a lower bitrate video track
38- useDateTime : true , //when the unix timestamp of the stream is known, display the date/time
38+ useDateTime : true , //when the unix timestamp of the stream is known, display the date/time,
39+ subscribeToMetaTrack : false , //pass [[track index,callback]]; the callback function will be called whenever the specified meta data track receives a message.
3940 MistVideoObject : false //no reference object is passed
4041 } , options ) ;
4142 if ( options . host ) { options . host = MistUtil . http . url . sanitizeHost ( options . host ) ; }
@@ -115,7 +116,6 @@ function MistVideo(streamName,options) {
115116 this . log ( "A reloadDelay of more than an hour was set: assuming milliseconds were intended. ReloadDelay is now " + options . reloadDelay + "s" ) ;
116117 }
117118
118-
119119 new MistSkin ( this ) ;
120120
121121 this . checkCombo = function ( options , quiet ) {
@@ -507,7 +507,7 @@ function MistVideo(streamName,options) {
507507 if ( MistVideo . reporting ) {
508508 MistVideo . reporting . init ( ) ;
509509 }
510-
510+
511511 if ( "api" in MistVideo . player ) {
512512
513513 //add monitoring
@@ -663,7 +663,235 @@ function MistVideo(streamName,options) {
663663 MistUtil . event . addListener ( MistVideo . video , events [ i ] , function ( ) {
664664 if ( MistVideo . monitor ) { MistVideo . monitor . reset ( ) ; }
665665 } ) ;
666- }
666+ }
667+
668+ if ( "currentTime" in MistVideo . player . api ) {
669+ var json_source = MistUtil . sources . find ( MistVideo . info . source , {
670+ type : "html5/text/javascript" ,
671+ protocol : "ws" + ( location . protocol . charAt ( location . protocol . length - 2 ) == "s" ? "s" : "" ) + ":"
672+ } ) ;
673+ if ( json_source ) {
674+ MistVideo . metaTrackSubscriptions = {
675+ subscriptions : { } ,
676+ socket : null ,
677+ listeners : { } ,
678+ init : function ( ) {
679+ var me = this ;
680+ this . socket = new WebSocket ( MistUtil . http . url . addParam ( MistVideo . urlappend ( json_source . url ) , { rate :1 } ) ) ;
681+ me . send_queue = [ ] ;
682+ me . checktimer = null ;
683+ me . s = function ( obj ) {
684+ if ( me . socket . readyState == me . socket . OPEN ) {
685+ me . socket . send ( JSON . stringify ( obj ) ) ;
686+ return true ;
687+ }
688+ if ( me . socket . readyState >= me . socket . CLOSING ) {
689+ //reopen websocket
690+ me . init ( ) ;
691+ }
692+ //add message to queue
693+ this . send_queue . push ( obj ) ;
694+ } ;
695+
696+ var stayahead = 5 ; //ask MistServer to fastforward to stayahead seconds ahead, so we receive messages earlier
697+
698+ me . socket . setTracks = function ( ) {
699+ me . s ( { type :"tracks" , meta :MistUtil . object . keys ( me . subscriptions ) . join ( "," ) } ) ;
700+ } ;
701+ me . socket . onopen = function ( ) {
702+ MistVideo . log ( "Metadata socket opened" ) ;
703+
704+ me . socket . setTracks ( ) ;
705+ me . s ( { type :"set_speed" , play_rate :MistVideo . player . api . playbackRate } ) ;
706+ me . s ( { type :"seek" , seek_time :Math . round ( MistVideo . player . api . currentTime * 1e3 ) , ff_to :Math . round ( ( MistVideo . player . api . currentTime + stayahead ) * 1e3 ) } ) ;
707+ me . socket . addEventListener ( "message" , function ( e ) {
708+ if ( ! e . data ) { MistVideo . log ( "Subtitle websocket received empty message." ) ; return ; }
709+ var message = JSON . parse ( e . data ) ;
710+ if ( ! message ) { MistVideo . log ( "Subtitle websocket received invalid message." ) ; return ; }
711+
712+ if ( ( "time" in message ) && ( "track" in message ) && ( "data" in message ) ) {
713+ if ( message . track in me . subscriptions ) {
714+ //console.warn("received:",message.track,message.data);
715+ me . subscriptions [ message . track ] . buffer . push ( message ) ;
716+ //console.warn("received:",message.track,message.time,"currentTime:",MistVideo.player.api.currentTime,"bufferlength:",me.subscriptions[message.track].buffer.length,"timer:",!!me.checktimer);
717+
718+ if ( ! me . checktimer ) {
719+ me . check ( ) ;
720+ }
721+ else {
722+ var willCheckAt = MistVideo . timers . list [ me . checktimer ] ;
723+ if ( willCheckAt ) {
724+ var messageAt = ( new Date ( ) ) . getTime ( ) + message . time - MistVideo . player . api . currentTime * 1e3 ;
725+ if ( willCheckAt > messageAt ) {
726+ MistVideo . log ( "The metadata socket received a message that should be displayed sooner than the current check time; resetting" ) ;
727+ MistVideo . timers . stop ( me . checktimer ) ;
728+ me . checktimer = null ;
729+ me . check ( ) ;
730+ }
731+ }
732+ }
733+ }
734+ }
735+ //per track, the messages should arrive in the correct order and we shouldn't need to do sorting
736+
737+
738+ if ( "type" in message ) {
739+ switch ( message . type ) {
740+ case "seek" : {
741+ for ( var i in me . subscriptions ) {
742+ me . subscriptions [ i ] . buffer = [ ] ;
743+ }
744+ MistVideo . log ( "Cleared metadata buffer after completed seek" ) ;
745+ if ( me . checktimer ) {
746+ //there might be a timer going for some time in the future: stop it,
747+ MistVideo . timers . stop ( me . checktimer ) ;
748+ me . checktimer = null ;
749+ }
750+ }
751+ }
752+ }
753+
754+ } ) ;
755+ me . socket . onclose = function ( ) {
756+ //dont me.init();, send function will reopen if needed instead
757+ MistVideo . log ( "Metadata socket closed" ) ;
758+ }
759+
760+ while ( me . send_queue . length && ( me . socket . readyState == me . socket . OPEN ) ) {
761+ me . s ( me . send_queue . shift ( ) ) ;
762+ }
763+ } ;
764+ if ( ! ( "seeked" in this . listeners ) ) { //prevent duplication
765+ var lastff = 0 ;
766+
767+ me . check = function ( ) {
768+ //console.warn(me.checktimer,"check");
769+ if ( me . checktimer ) {
770+ MistVideo . timers . stop ( me . checktimer ) ;
771+ me . checktimer = null ;
772+ }
773+
774+ if ( MistVideo . player . api . paused ) { return ; }
775+
776+ var nextAtGlobal = null ;
777+ for ( var i in me . subscriptions ) {
778+ var buffer = me . subscriptions [ i ] . buffer ;
779+ while ( buffer . length && ( buffer [ 0 ] . time <= MistVideo . player . api . currentTime * 1e3 ) ) {
780+ var message = buffer . shift ( ) ;
781+
782+ if ( message . time < ( MistVideo . player . api . currentTime - 5 ) * 1e3 ) {
783+ //the message is at least 5 seconds older than the video time
784+ continue ;
785+ }
786+ else {
787+ for ( var j in me . subscriptions [ i ] . callbacks ) {
788+ me . subscriptions [ i ] . callbacks [ j ] . call ( MistVideo , message ) ;
789+ }
790+ }
791+ }
792+ if ( buffer . length ) {
793+ //save when the next message should be played
794+ nextAtGlobal = Math . min ( nextAtGlobal === null ? 1e9 : nextAtGlobal , buffer [ 0 ] . time ) ;
795+ }
796+ }
797+
798+ //add rate limiting: do not ask for fast forward more than once a second
799+ var now = ( new Date ( ) ) . getTime ( )
800+ if ( now > lastff + 1e3 ) {
801+ me . s ( { type :"fast_forward" , ff_to :Math . round ( ( MistVideo . player . api . currentTime + stayahead ) * 1e3 ) } ) ;
802+ lastff = now ;
803+ }
804+
805+
806+ if ( nextAtGlobal ) {
807+ var delay = nextAtGlobal - MistVideo . player . api . currentTime * 1e3 ;
808+ me . checktimer = MistVideo . timers . start ( function ( ) {
809+ me . check ( ) ;
810+ } , delay ) ;
811+ }
812+
813+ } ;
814+
815+ this . listeners . seeked = MistUtil . event . addListener ( MistVideo . video , "seeked" , function ( ) {
816+ for ( var i in me . subscriptions ) {
817+ me . subscriptions [ i ] . buffer = [ ] ;
818+ }
819+ me . s ( { type :"seek" , seek_time :Math . round ( MistVideo . player . api . currentTime * 1e3 ) , ff_to :Math . round ( ( MistVideo . player . api . currentTime + stayahead ) * 1e3 ) } ) ;
820+ //console.warn("seek to",Math.round(MistVideo.player.api.currentTime*1e3));
821+ } ) ;
822+ this . listeners . pause = MistUtil . event . addListener ( MistVideo . video , "pause" , function ( ) {
823+ me . s ( { type :"hold" } ) ;
824+ MistVideo . timers . stop ( me . checktimer ) ;
825+ me . checktimer = null ;
826+ } ) ;
827+ this . listeners . playing = MistUtil . event . addListener ( MistVideo . video , "playing" , function ( ) {
828+ me . s ( { type :"play" } ) ;
829+ if ( ! me . checktimer ) me . check ( ) ;
830+ } ) ;
831+ this . listeners . ratechange = MistUtil . event . addListener ( MistVideo . video , "ratechange" , function ( ) {
832+ me . s ( { type :"set_speed" , play_rate :MistVideo . player . api . playbackRate } ) ;
833+ } ) ;
834+ }
835+ } ,
836+ destroy : function ( ) {
837+ MistVideo . log ( "Closing metadata socket.." ) ;
838+ this . socket . close ( ) ;
839+ this . socket = null ;
840+ this . subscriptions = { } ;
841+ for ( var i in this . listeners ) {
842+ MistUtil . event . removeListener ( this . listeners [ i ] ) ;
843+ }
844+ this . listeners = { } ;
845+ } ,
846+ add : function ( trackid , callback ) {
847+ if ( typeof callback != "function" ) { return ; }
848+
849+ if ( ! ( trackid in this . subscriptions ) ) {
850+ this . subscriptions [ trackid ] = {
851+ buffer : [ ] ,
852+ callbacks : [ ]
853+ } ;
854+ }
855+ this . subscriptions [ trackid ] . callbacks . push ( callback ) ;
856+
857+ if ( this . socket === null ) {
858+ this . init ( ) ;
859+ }
860+ else {
861+ this . socket . setTracks ( ) ;
862+ }
863+ } ,
864+ remove : function ( trackid , callback ) {
865+ if ( trackid in this . subscriptions ) {
866+ for ( var i in this . subscriptions [ trackid ] . callbacks ) {
867+ if ( callback == this . subscriptions [ trackid ] . callbacks [ i ] ) {
868+ this . subscriptions [ trackid ] . callbacks . splice ( i , 1 ) ;
869+ break ;
870+ }
871+ }
872+ if ( this . subscriptions [ trackid ] . callbacks . length == 0 ) {
873+ delete this . subscriptions [ trackid ] ;
874+ if ( MistUtil . object . keys ( this . subscriptions ) . length ) {
875+ this . socket . setTracks ( ) ;
876+ }
877+ else {
878+ this . destroy ( ) ;
879+ }
880+ }
881+ }
882+ }
883+ } ;
884+ if ( options . subscribeToMetaTrack . length ) {
885+ if ( typeof options . subscribeToMetaTrack [ 0 ] != "object" ) {
886+ options . subscribeToMetaTrack = [ options . subscribeToMetaTrack ] ;
887+ }
888+ for ( var i in options . subscribeToMetaTrack ) {
889+ MistVideo . metaTrackSubscriptions . add . apply ( MistVideo . metaTrackSubscriptions , options . subscribeToMetaTrack [ i ] ) ;
890+ }
891+ }
892+ }
893+ }
894+
667895 }
668896
669897 //remove placeholder and add UI structure
@@ -1051,7 +1279,7 @@ function MistVideo(streamName,options) {
10511279 //switch to polling-mode if websockets are not supported
10521280
10531281 function openWithGet ( ) {
1054- var url = MistUtil . http . url . addParam ( MistVideo . urlappend ( options . host + "/json_" + encodeURIComponent ( MistVideo . stream ) + ".js" ) , { metaeverywhere :1 } ) ;
1282+ var url = MistUtil . http . url . addParam ( MistVideo . urlappend ( options . host + "/json_" + encodeURIComponent ( MistVideo . stream ) + ".js" ) , { metaeverywhere :1 , inclzero : 1 } ) ;
10551283 MistVideo . log ( "Requesting stream info from " + url ) ;
10561284 MistUtil . http . get ( url , function ( d ) {
10571285 if ( MistVideo . destroyed ) { return ; }
@@ -1070,7 +1298,7 @@ function MistVideo(streamName,options) {
10701298 function openSocket ( ) {
10711299 MistVideo . log ( "Opening stream status stream through websocket.." ) ;
10721300 var url = MistVideo . options . host . replace ( / ^ h t t p / i, "ws" ) ;
1073- url = MistUtil . http . url . addParam ( MistVideo . urlappend ( url + "/json_" + encodeURIComponent ( MistVideo . stream ) + ".js" ) , { metaeverywhere :1 } ) ;
1301+ url = MistUtil . http . url . addParam ( MistVideo . urlappend ( url + "/json_" + encodeURIComponent ( MistVideo . stream ) + ".js" ) , { metaeverywhere :1 , inclzero : 1 } ) ;
10741302 var socket ;
10751303 try {
10761304 socket = new WebSocket ( url ) ;
@@ -1415,10 +1643,8 @@ function MistVideo(streamName,options) {
14151643 if ( diff ) {
14161644 //console.log("Difference",diff,data,MistVideo.info);
14171645
1418- if ( "source" in diff ) {
1419- if ( "error" in MistVideo . info ) {
1420- MistVideo . reload ( "Reloading, stream info has error" ) ;
1421- }
1646+ if ( ( "source" in diff ) && ( "error" in MistVideo . info ) ) {
1647+ MistVideo . reload ( "Reloading, stream info has error" ) ;
14221648 return ;
14231649 }
14241650
@@ -1513,6 +1739,9 @@ function MistVideo(streamName,options) {
15131739 }
15141740 }
15151741 }
1742+ if ( this . metaTrackSubscriptions && this . metaTrackSubscriptions . socket ) {
1743+ this . metaTrackSubscriptions . destroy ( ) ;
1744+ }
15161745 if ( ( this . UI ) && ( this . UI . elements ) ) {
15171746 for ( var i in this . UI . elements ) {
15181747 var e = this . UI . elements [ i ] ;
0 commit comments