mirror of
https://github.com/ossrs/srs.git
synced 2025-02-15 04:42:04 +00:00
Merge branch 'feature/rtc' into develop
This commit is contained in:
commit
a617d362da
41 changed files with 2768 additions and 1520 deletions
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
|
@ -1,76 +0,0 @@
|
|||
"undefined"==typeof jwplayer&&(jwplayer=function(f){if(jwplayer.api)return jwplayer.api.selectPlayer(f)},jwplayer.version="6.4.3359",jwplayer.vid=document.createElement("video"),jwplayer.audio=document.createElement("audio"),jwplayer.source=document.createElement("source"),function(f){function a(g){return function(){return c(g)}}var l=document,e=window,j=navigator,b=f.utils=function(){};b.exists=function(g){switch(typeof g){case "string":return 0<g.length;case "object":return null!==g;case "undefined":return!1}return!0};
|
||||
b.styleDimension=function(g){return g+(0<g.toString().indexOf("%")?"":"px")};b.getAbsolutePath=function(g,a){b.exists(a)||(a=l.location.href);if(b.exists(g)){var c;if(b.exists(g)){c=g.indexOf("://");var j=g.indexOf("?");c=0<c&&(0>j||j>c)}else c=void 0;if(c)return g;c=a.substring(0,a.indexOf("://")+3);var j=a.substring(c.length,a.indexOf("/",c.length+1)),d;0===g.indexOf("/")?d=g.split("/"):(d=a.split("?")[0],d=d.substring(c.length+j.length+1,d.lastIndexOf("/")),d=d.split("/").concat(g.split("/")));
|
||||
for(var h=[],e=0;e<d.length;e++)d[e]&&(b.exists(d[e])&&"."!=d[e])&&(".."==d[e]?h.pop():h.push(d[e]));return c+j+"/"+h.join("/")}};b.extend=function(){var a=b.extend.arguments;if(1<a.length){for(var c=1;c<a.length;c++)b.foreach(a[c],function(c,d){try{b.exists(d)&&(a[0][c]=d)}catch(j){}});return a[0]}return null};b.log=function(a,b){"undefined"!=typeof console&&"undefined"!=typeof console.log&&(b?console.log(a,b):console.log(a))};var c=b.userAgentMatch=function(a){return null!==j.userAgent.toLowerCase().match(a)};
|
||||
b.isIE=a(/msie/i);b.isFF=a(/firefox/i);b.isChrome=a(/chrome/i);b.isIOS=a(/iP(hone|ad|od)/i);b.isIPod=a(/iP(hone|od)/i);b.isIPad=a(/iPad/i);b.isSafari602=a(/Macintosh.*Mac OS X 10_8.*6\.0\.\d* Safari/i);b.isAndroid=function(a){return a?c(RegExp("android.*"+a,"i")):c(/android/i)};b.isMobile=function(){return b.isIOS()||b.isAndroid()};b.saveCookie=function(a,b){l.cookie="jwplayer."+a+"\x3d"+b+"; path\x3d/"};b.getCookies=function(){for(var a={},b=l.cookie.split("; "),c=0;c<b.length;c++){var d=b[c].split("\x3d");
|
||||
0==d[0].indexOf("jwplayer.")&&(a[d[0].substring(9,d[0].length)]=d[1])}return a};b.typeOf=function(a){var b=typeof a;return"object"===b?!a?"null":a instanceof Array?"array":b:b};b.translateEventResponse=function(a,c){var d=b.extend({},c);a==f.events.JWPLAYER_FULLSCREEN&&!d.fullscreen?(d.fullscreen="true"==d.message?!0:!1,delete d.message):"object"==typeof d.data?(d=b.extend(d,d.data),delete d.data):"object"==typeof d.metadata&&b.deepReplaceKeyName(d.metadata,["__dot__","__spc__","__dsh__","__default__"],
|
||||
["."," ","-","default"]);b.foreach(["position","duration","offset"],function(a,b){d[b]&&(d[b]=Math.round(1E3*d[b])/1E3)});return d};b.flashVersion=function(){if(b.isAndroid())return 0;var a=j.plugins,d;try{if("undefined"!==a&&(d=a["Shockwave Flash"]))return parseInt(d.description.replace(/\D+(\d+)\..*/,"$1"))}catch(c){}if("undefined"!=typeof e.ActiveXObject)try{if(d=new ActiveXObject("ShockwaveFlash.ShockwaveFlash"))return parseInt(d.GetVariable("$version").split(" ")[1].split(",")[0])}catch(f){}return 0};
|
||||
b.getScriptPath=function(a){for(var b=l.getElementsByTagName("script"),d=0;d<b.length;d++){var c=b[d].src;if(c&&0<=c.indexOf(a))return c.substr(0,c.indexOf(a))}return""};b.deepReplaceKeyName=function(a,d,c){switch(f.utils.typeOf(a)){case "array":for(var j=0;j<a.length;j++)a[j]=f.utils.deepReplaceKeyName(a[j],d,c);break;case "object":b.foreach(a,function(b,h){var j;if(d instanceof Array&&c instanceof Array){if(d.length!=c.length)return;j=d}else j=[d];for(var e=b,l=0;l<j.length;l++)e=e.replace(RegExp(d[l],
|
||||
"g"),c[l]);a[e]=f.utils.deepReplaceKeyName(h,d,c);b!=e&&delete a[b]})}return a};var d=b.pluginPathType={ABSOLUTE:0,RELATIVE:1,CDN:2};b.getPluginPathType=function(a){if("string"==typeof a){a=a.split("?")[0];var c=a.indexOf("://");if(0<c)return d.ABSOLUTE;var j=a.indexOf("/");a=b.extension(a);return 0>c&&0>j&&(!a||!isNaN(a))?d.CDN:d.RELATIVE}};b.getPluginName=function(a){return a.replace(/^(.*\/)?([^-]*)-?.*\.(swf|js)$/,"$2")};b.getPluginVersion=function(a){return a.replace(/[^-]*-?([^\.]*).*$/,"$1")};
|
||||
b.isYouTube=function(a){return-1<a.indexOf("youtube.com")||-1<a.indexOf("youtu.be")};b.isRtmp=function(a,b){return 0==a.indexOf("rtmp")||"rtmp"==b};b.foreach=function(a,b){var d,c;for(d in a)a.hasOwnProperty(d)&&(c=a[d],b(d,c))};b.isHTTPS=function(){return 0==e.location.href.indexOf("https")};b.repo=function(){var a=""+f.version.split(/\W/).splice(0,2).join("/")+"/";try{b.isHTTPS()&&(a=a.replace("http://","https://ssl."))}catch(d){}return a}}(jwplayer),function(f){var a="video/",
|
||||
l=f.foreach,e={mp4:a+"mp4",vorbis:"audio/ogg",ogg:a+"ogg",webm:a+"webm",aac:"audio/mp4",mp3:"audio/mpeg",hls:"application/vnd.apple.mpegurl"},j={mp4:e.mp4,f4v:e.mp4,m4v:e.mp4,mov:e.mp4,m4a:e.aac,f4a:e.aac,aac:e.aac,mp3:e.mp3,ogv:e.ogg,ogg:e.vorbis,oga:e.vorbis,webm:e.webm,m3u8:e.hls,hls:e.hls},a="video",a={flv:a,f4v:a,mov:a,m4a:a,m4v:a,mp4:a,aac:a,f4a:a,mp3:"sound",smil:"rtmp",m3u8:"hls",hls:"hls"},b=f.extensionmap={};l(j,function(a,d){b[a]={html5:d}});l(a,function(a,d){b[a]||(b[a]={});b[a].flash=
|
||||
d});b.types=e;b.mimeType=function(a){var b;l(e,function(j,e){!b&&e==a&&(b=j)});return b};b.extType=function(a){return b.mimeType(j[a])}}(jwplayer.utils),function(f){var a=f.loaderstatus={NEW:0,LOADING:1,ERROR:2,COMPLETE:3},l=document;f.scriptloader=function(e){function j(){c=a.ERROR;g.sendEvent(d.ERROR)}function b(){c=a.COMPLETE;g.sendEvent(d.COMPLETE)}var c=a.NEW,d=jwplayer.events,g=new d.eventdispatcher;f.extend(this,g);this.load=function(){var g=f.scriptloader.loaders[e];if(g&&(g.getStatus()==
|
||||
a.NEW||g.getStatus()==a.LOADING))g.addEventListener(d.ERROR,j),g.addEventListener(d.COMPLETE,b);else if(f.scriptloader.loaders[e]=this,c==a.NEW){c=a.LOADING;var m=l.createElement("script");m.addEventListener?(m.onload=b,m.onerror=j):m.readyState&&(m.onreadystatechange=function(){("loaded"==m.readyState||"complete"==m.readyState)&&b()});l.getElementsByTagName("head")[0].appendChild(m);m.src=e}};this.getStatus=function(){return c}};f.scriptloader.loaders={}}(jwplayer.utils),function(f){f.trim=function(a){return a.replace(/^\s*/,
|
||||
"").replace(/\s*$/,"")};f.pad=function(a,f,e){for(e||(e="0");a.length<f;)a=e+a;return a};f.xmlAttribute=function(a,f){for(var e=0;e<a.attributes.length;e++)if(a.attributes[e].name&&a.attributes[e].name.toLowerCase()==f.toLowerCase())return a.attributes[e].value.toString();return""};f.extension=function(a){if(!a||"rtmp"==a.substr(0,4))return"";a=a.substring(a.lastIndexOf("/")+1,a.length).split("?")[0].split("#")[0];if(-1<a.lastIndexOf("."))return a.substr(a.lastIndexOf(".")+1,a.length).toLowerCase()};
|
||||
f.stringToColor=function(a){a=a.replace(/(#|0x)?([0-9A-F]{3,6})$/gi,"$2");3==a.length&&(a=a.charAt(0)+a.charAt(0)+a.charAt(1)+a.charAt(1)+a.charAt(2)+a.charAt(2));return parseInt(a,16)}}(jwplayer.utils),function(f){f.key=function(a){var l,e,j;this.edition=function(){return j&&j.getTime()<(new Date).getTime()?"invalid":l};this.token=function(){return e};f.exists(a)||(a="");try{a=f.tea.decrypt(a,"36QXq4W@GSBV^teR");var b=a.split("/");(l=b[0])||(l="free");e=b[1];b[2]&&0<parseInt(b[2])&&(j=new Date,j.setTime(String(b[2])))}catch(c){l=
|
||||
"invalid"}}}(jwplayer.utils),function(f){var a=f.tea={};a.encrypt=function(j,b){if(0==j.length)return"";var c=a.strToLongs(e.encode(j));1>=c.length&&(c[1]=0);for(var d=a.strToLongs(e.encode(b).slice(0,16)),g=c.length,f=c[g-1],m=c[0],n,k=Math.floor(6+52/g),h=0;0<k--;){h+=2654435769;n=h>>>2&3;for(var r=0;r<g;r++)m=c[(r+1)%g],f=(f>>>5^m<<2)+(m>>>3^f<<4)^(h^m)+(d[r&3^n]^f),f=c[r]+=f}c=a.longsToStr(c);return l.encode(c)};a.decrypt=function(j,b){if(0==j.length)return"";for(var c=a.strToLongs(l.decode(j)),
|
||||
d=a.strToLongs(e.encode(b).slice(0,16)),g=c.length,f=c[g-1],m=c[0],n,k=2654435769*Math.floor(6+52/g);0!=k;){n=k>>>2&3;for(var h=g-1;0<=h;h--)f=c[0<h?h-1:g-1],f=(f>>>5^m<<2)+(m>>>3^f<<4)^(k^m)+(d[h&3^n]^f),m=c[h]-=f;k-=2654435769}c=a.longsToStr(c);c=c.replace(/\0+$/,"");return e.decode(c)};a.strToLongs=function(a){for(var b=Array(Math.ceil(a.length/4)),c=0;c<b.length;c++)b[c]=a.charCodeAt(4*c)+(a.charCodeAt(4*c+1)<<8)+(a.charCodeAt(4*c+2)<<16)+(a.charCodeAt(4*c+3)<<24);return b};a.longsToStr=function(a){for(var b=
|
||||
Array(a.length),c=0;c<a.length;c++)b[c]=String.fromCharCode(a[c]&255,a[c]>>>8&255,a[c]>>>16&255,a[c]>>>24&255);return b.join("")};var l={code:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\x3d",encode:function(a,b){var c,d,g,f,m=[],n="",k,h,r=l.code;h=("undefined"==typeof b?0:b)?e.encode(a):a;k=h.length%3;if(0<k)for(;3>k++;)n+="\x3d",h+="\x00";for(k=0;k<h.length;k+=3)c=h.charCodeAt(k),d=h.charCodeAt(k+1),g=h.charCodeAt(k+2),f=c<<16|d<<8|g,c=f>>18&63,d=f>>12&63,g=f>>6&63,f&=63,m[k/
|
||||
3]=r.charAt(c)+r.charAt(d)+r.charAt(g)+r.charAt(f);m=m.join("");return m=m.slice(0,m.length-n.length)+n},decode:function(a,b){b="undefined"==typeof b?!1:b;var c,d,g,f,m,n=[],k,h=l.code;k=b?e.decode(a):a;for(var r=0;r<k.length;r+=4)c=h.indexOf(k.charAt(r)),d=h.indexOf(k.charAt(r+1)),f=h.indexOf(k.charAt(r+2)),m=h.indexOf(k.charAt(r+3)),g=c<<18|d<<12|f<<6|m,c=g>>>16&255,d=g>>>8&255,g&=255,n[r/4]=String.fromCharCode(c,d,g),64==m&&(n[r/4]=String.fromCharCode(c,d)),64==f&&(n[r/4]=String.fromCharCode(c));
|
||||
f=n.join("");return b?e.decode(f):f}},e={encode:function(a){a=a.replace(/[\u0080-\u07ff]/g,function(a){a=a.charCodeAt(0);return String.fromCharCode(192|a>>6,128|a&63)});return a=a.replace(/[\u0800-\uffff]/g,function(a){a=a.charCodeAt(0);return String.fromCharCode(224|a>>12,128|a>>6&63,128|a&63)})},decode:function(a){a=a.replace(/[\u00e0-\u00ef][\u0080-\u00bf][\u0080-\u00bf]/g,function(a){a=(a.charCodeAt(0)&15)<<12|(a.charCodeAt(1)&63)<<6|a.charCodeAt(2)&63;return String.fromCharCode(a)});return a=
|
||||
a.replace(/[\u00c0-\u00df][\u0080-\u00bf]/g,function(a){a=(a.charCodeAt(0)&31)<<6|a.charCodeAt(1)&63;return String.fromCharCode(a)})}}}(jwplayer.utils),function(f){f.events={COMPLETE:"COMPLETE",ERROR:"ERROR",API_READY:"jwplayerAPIReady",JWPLAYER_READY:"jwplayerReady",JWPLAYER_FULLSCREEN:"jwplayerFullscreen",JWPLAYER_RESIZE:"jwplayerResize",JWPLAYER_ERROR:"jwplayerError",JWPLAYER_MEDIA_BEFOREPLAY:"jwplayerMediaBeforePlay",JWPLAYER_MEDIA_BEFORECOMPLETE:"jwplayerMediaBeforeComplete",JWPLAYER_COMPONENT_SHOW:"jwplayerComponentShow",
|
||||
JWPLAYER_COMPONENT_HIDE:"jwplayerComponentHide",JWPLAYER_MEDIA_BUFFER:"jwplayerMediaBuffer",JWPLAYER_MEDIA_BUFFER_FULL:"jwplayerMediaBufferFull",JWPLAYER_MEDIA_ERROR:"jwplayerMediaError",JWPLAYER_MEDIA_LOADED:"jwplayerMediaLoaded",JWPLAYER_MEDIA_COMPLETE:"jwplayerMediaComplete",JWPLAYER_MEDIA_SEEK:"jwplayerMediaSeek",JWPLAYER_MEDIA_TIME:"jwplayerMediaTime",JWPLAYER_MEDIA_VOLUME:"jwplayerMediaVolume",JWPLAYER_MEDIA_META:"jwplayerMediaMeta",JWPLAYER_MEDIA_MUTE:"jwplayerMediaMute",JWPLAYER_MEDIA_LEVELS:"jwplayerMediaLevels",
|
||||
JWPLAYER_MEDIA_LEVEL_CHANGED:"jwplayerMediaLevelChanged",JWPLAYER_CAPTIONS_CHANGED:"jwplayerCaptionsChanged",JWPLAYER_CAPTIONS_LIST:"jwplayerCaptionsList",JWPLAYER_PLAYER_STATE:"jwplayerPlayerState",state:{BUFFERING:"BUFFERING",IDLE:"IDLE",PAUSED:"PAUSED",PLAYING:"PLAYING"},JWPLAYER_PLAYLIST_LOADED:"jwplayerPlaylistLoaded",JWPLAYER_PLAYLIST_ITEM:"jwplayerPlaylistItem",JWPLAYER_PLAYLIST_COMPLETE:"jwplayerPlaylistComplete",JWPLAYER_DISPLAY_CLICK:"jwplayerViewClick",JWPLAYER_CONTROLS:"jwplayerViewControls",
|
||||
JWPLAYER_INSTREAM_CLICK:"jwplayerInstreamClicked",JWPLAYER_INSTREAM_DESTROYED:"jwplayerInstreamDestroyed"}}(jwplayer),function(f){var a=jwplayer.utils;f.eventdispatcher=function(f,e){var j,b;this.resetEventListeners=function(){j={};b=[]};this.resetEventListeners();this.addEventListener=function(b,d,g){try{a.exists(j[b])||(j[b]=[]),"string"==a.typeOf(d)&&(d=(new Function("return "+d))()),j[b].push({listener:d,count:g})}catch(e){a.log("error",e)}return!1};this.removeEventListener=function(b,d){if(j[b]){try{for(var g=
|
||||
0;g<j[b].length;g++)if(j[b][g].listener.toString()==d.toString()){j[b].splice(g,1);break}}catch(e){a.log("error",e)}return!1}};this.addGlobalListener=function(c,d){try{"string"==a.typeOf(c)&&(c=(new Function("return "+c))()),b.push({listener:c,count:d})}catch(g){a.log("error",g)}return!1};this.removeGlobalListener=function(c){if(c){try{for(var d=0;d<b.length;d++)if(b[d].listener.toString()==c.toString()){b.splice(d,1);break}}catch(g){a.log("error",g)}return!1}};this.sendEvent=function(c,d){a.exists(d)||
|
||||
(d={});a.extend(d,{id:f,version:jwplayer.version,type:c});e&&a.log(c,d);if("undefined"!=a.typeOf(j[c]))for(var g=0;g<j[c].length;g++){try{j[c][g].listener(d)}catch(q){a.log("There was an error while handling a listener: "+q.toString(),j[c][g].listener)}j[c][g]&&(1===j[c][g].count?delete j[c][g]:0<j[c][g].count&&(j[c][g].count-=1))}for(g=0;g<b.length;g++){try{b[g].listener(d)}catch(m){a.log("There was an error while handling a listener: "+m.toString(),b[g].listener)}b[g]&&(1===b[g].count?delete b[g]:
|
||||
0<b[g].count&&(b[g].count-=1))}}}}(jwplayer.events),function(f){var a={},l={};f.plugins=function(){};f.plugins.loadPlugins=function(e,j){l[e]=new f.plugins.pluginloader(new f.plugins.model(a),j);return l[e]};f.plugins.registerPlugin=function(e,j,b,c){var d=f.utils.getPluginName(e);a[d]||(a[d]=new f.plugins.plugin(e));a[d].registerPlugin(e,j,b,c)}}(jwplayer),function(f){f.plugins.model=function(a){this.addPlugin=function(l){var e=f.utils.getPluginName(l);a[e]||(a[e]=new f.plugins.plugin(l));return a[e]};
|
||||
this.getPlugins=function(){return a}}}(jwplayer),function(f){var a=jwplayer.utils,l=jwplayer.events;f.pluginmodes={FLASH:0,JAVASCRIPT:1,HYBRID:2};f.plugin=function(e){function j(){switch(a.getPluginPathType(e)){case a.pluginPathType.ABSOLUTE:return e;case a.pluginPathType.RELATIVE:return a.getAbsolutePath(e,window.location.href)}}function b(){n=setTimeout(function(){d=a.loaderstatus.COMPLETE;k.sendEvent(l.COMPLETE)},1E3)}function c(){d=a.loaderstatus.ERROR;k.sendEvent(l.ERROR)}var d=a.loaderstatus.NEW,
|
||||
g,q,m,n,k=new l.eventdispatcher;a.extend(this,k);this.load=function(){if(d==a.loaderstatus.NEW)if(0<e.lastIndexOf(".swf"))g=e,d=a.loaderstatus.COMPLETE,k.sendEvent(l.COMPLETE);else if(a.getPluginPathType(e)==a.pluginPathType.CDN)d=a.loaderstatus.COMPLETE,k.sendEvent(l.COMPLETE);else{d=a.loaderstatus.LOADING;var h=new a.scriptloader(j());h.addEventListener(l.COMPLETE,b);h.addEventListener(l.ERROR,c);h.load()}};this.registerPlugin=function(b,c,e,j){n&&(clearTimeout(n),n=void 0);m=c;e&&j?(g=j,q=e):"string"==
|
||||
typeof e?g=e:"function"==typeof e?q=e:!e&&!j&&(g=b);d=a.loaderstatus.COMPLETE;k.sendEvent(l.COMPLETE)};this.getStatus=function(){return d};this.getPluginName=function(){return a.getPluginName(e)};this.getFlashPath=function(){if(g)switch(a.getPluginPathType(g)){case a.pluginPathType.ABSOLUTE:return g;case a.pluginPathType.RELATIVE:return 0<e.lastIndexOf(".swf")?a.getAbsolutePath(g,window.location.href):a.getAbsolutePath(g,j())}return null};this.getJS=function(){return q};this.getTarget=function(){return m};
|
||||
this.getPluginmode=function(){if("undefined"!=typeof g&&"undefined"!=typeof q)return f.pluginmodes.HYBRID;if("undefined"!=typeof g)return f.pluginmodes.FLASH;if("undefined"!=typeof q)return f.pluginmodes.JAVASCRIPT};this.getNewInstance=function(a,b,d){return new q(a,b,d)};this.getURL=function(){return e}}}(jwplayer.plugins),function(f){var a=f.utils,l=f.events,e=a.foreach;f.plugins.pluginloader=function(j,b){function c(){m?h.sendEvent(l.ERROR,{message:n}):q||(q=!0,g=a.loaderstatus.COMPLETE,h.sendEvent(l.COMPLETE))}
|
||||
function d(){k||c();if(!q&&!m){var b=0,d=j.getPlugins();a.foreach(k,function(g){g=a.getPluginName(g);var e=d[g];g=e.getJS();var h=e.getTarget(),e=e.getStatus();if(e==a.loaderstatus.LOADING||e==a.loaderstatus.NEW)b++;else if(g&&(!h||parseFloat(h)>parseFloat(f.version)))m=!0,n="Incompatible player version",c()});0==b&&c()}}var g=a.loaderstatus.NEW,q=!1,m=!1,n,k=b,h=new l.eventdispatcher;a.extend(this,h);this.setupPlugins=function(b,d,c){var g={length:0,plugins:{}},h=0,f={},r=j.getPlugins();e(d.plugins,
|
||||
function(e,j){var k=a.getPluginName(e),l=r[k],m=l.getFlashPath(),n=l.getJS(),q=l.getURL();m&&(g.plugins[m]=a.extend({},j),g.plugins[m].pluginmode=l.getPluginmode(),g.length++);try{if(n&&d.plugins&&d.plugins[q]){var v=document.createElement("div");v.id=b.id+"_"+k;v.style.position="absolute";v.style.top=0;v.style.zIndex=h+10;f[k]=l.getNewInstance(b,a.extend({},d.plugins[q]),v);h++;b.onReady(c(f[k],v,!0));b.onResize(c(f[k],v))}}catch(B){a.log("ERROR: Failed to load "+k+".")}});b.plugins=f;return g};
|
||||
this.load=function(){if(!(a.exists(b)&&"object"!=a.typeOf(b))){g=a.loaderstatus.LOADING;e(b,function(b){a.exists(b)&&(b=j.addPlugin(b),b.addEventListener(l.COMPLETE,d),b.addEventListener(l.ERROR,r))});var c=j.getPlugins();e(c,function(a,b){b.load()})}d()};var r=this.pluginFailed=function(){m||(m=!0,n="File not found",c())};this.getStatus=function(){return g}}}(jwplayer),function(f){f.playlist=function(a){var l=[];if("array"==f.utils.typeOf(a))for(var e=0;e<a.length;e++)l.push(new f.playlist.item(a[e]));
|
||||
else l.push(new f.playlist.item(a));return l}}(jwplayer),function(f){var a=f.item=function(l){var e=jwplayer.utils,j=e.extend({},a.defaults,l);j.tracks=e.exists(l.tracks)?l.tracks:[];0==j.sources.length&&(j.sources=[new f.source(j)]);for(var b=0;b<j.sources.length;b++){var c=j.sources[b]["default"];j.sources[b]["default"]=c?"true"==c.toString():!1;j.sources[b]=new f.source(j.sources[b])}if(j.captions&&!e.exists(l.tracks)){for(l=0;l<j.captions.length;l++)j.tracks.push(j.captions[l]);delete j.captions}for(b=
|
||||
0;b<j.tracks.length;b++)j.tracks[b]=new f.track(j.tracks[b]);return j};a.defaults={description:"",image:"",mediaid:"",title:"",sources:[],tracks:[]}}(jwplayer.playlist),function(f){var a=jwplayer.utils,l={file:void 0,label:void 0,type:void 0,"default":void 0};f.source=function(e){var j=a.extend({},l);a.foreach(l,function(b){a.exists(e[b])&&(j[b]=e[b],delete e[b])});j.type&&0<j.type.indexOf("/")&&(j.type=a.extensionmap.mimeType(j.type));"m3u8"==j.type&&(j.type="hls");"smil"==j.type&&(j.type="rtmp");
|
||||
return j}}(jwplayer.playlist),function(f){var a=jwplayer.utils,l={file:void 0,label:void 0,kind:"captions","default":!1};f.track=function(e){var j=a.extend({},l);e||(e={});a.foreach(l,function(b){a.exists(e[b])&&(j[b]=e[b],delete e[b])});return j}}(jwplayer.playlist),function(f){var a=f.utils,l=f.events,e=document,j=f.embed=function(b){function c(b,d){a.foreach(d,function(a,d){"function"==typeof b[a]&&b[a].call(b,d)})}function d(a){q(n,t+a.message)}function g(){q(n,t+"No playable sources found")}
|
||||
function q(b,d){if(m.fallback){var c=b.style;c.backgroundColor="#000";c.color="#FFF";c.width=a.styleDimension(m.width);c.height=a.styleDimension(m.height);c.display="table";c.opacity=1;var c=document.createElement("p"),g=c.style;g.verticalAlign="middle";g.textAlign="center";g.display="table-cell";g.font="15px/20px Arial, Helvetica, sans-serif";c.innerHTML=d.replace(":",":\x3cbr\x3e");b.innerHTML="";b.appendChild(c)}}var m=new j.config(b.config),n,k,h,r=m.width,p=m.height,t="Error loading player: ",
|
||||
s=f.plugins.loadPlugins(b.id,m.plugins);m.fallbackDiv&&(h=m.fallbackDiv,delete m.fallbackDiv);m.id=b.id;k=e.getElementById(b.id);m.aspectratio?b.config.aspectratio=m.aspectratio:delete b.config.aspectratio;n=e.createElement("div");n.id=k.id;n.style.width=0<r.toString().indexOf("%")?r:r+"px";n.style.height=0<p.toString().indexOf("%")?p:p+"px";k.parentNode.replaceChild(n,k);f.embed.errorScreen=q;s.addEventListener(l.COMPLETE,function(){if("array"==a.typeOf(m.playlist)&&2>m.playlist.length&&(0==m.playlist.length||
|
||||
!m.playlist[0].sources||0==m.playlist[0].sources.length))g();else if(s.getStatus()==a.loaderstatus.COMPLETE){for(var e=0;e<m.modes.length;e++)if(m.modes[e].type&&j[m.modes[e].type]){var f=a.extend({},m),r=new j[m.modes[e].type](n,m.modes[e],f,s,b);if(r.supportsConfig())return r.addEventListener(l.ERROR,d),r.embed(),c(b,f.events),b}m.fallback?(a.log("No suitable players found and fallback enabled"),new j.download(n,m,g)):(a.log("No suitable players found and fallback disabled"),n.parentNode.replaceChild(h,
|
||||
n))}});s.addEventListener(l.ERROR,function(a){q(n,"Could not load plugins: "+a.message)});s.load();return b}}(jwplayer),function(f){function a(a){if(a.playlist)for(var c=0;c<a.playlist.length;c++)a.playlist[c]=new j(a.playlist[c]);else{var d={};e.foreach(j.defaults,function(c){l(a,d,c)});d.sources||(a.levels?(d.sources=a.levels,delete a.levels):(c={},l(a,c,"file"),l(a,c,"type"),d.sources=c.file?[c]:[]));a.playlist=[new j(d)]}}function l(a,c,d){e.exists(a[d])&&(c[d]=a[d],delete a[d])}var e=f.utils,
|
||||
j=f.playlist.item;(f.embed.config=function(b){var c={fallback:!0,height:270,primary:"html5",width:480,base:b.base?b.base:e.getScriptPath("jwplayer.js"),aspectratio:""};b=e.extend(c,f.defaults,b);var c={type:"html5",src:b.base+"jwplayer.html5.js"},d={type:"flash",src:b.base+"jwplayer.flash.swf"};b.modes="flash"==b.primary?[d,c]:[c,d];b.listbar&&(b.playlistsize=b.listbar.size,b.playlistposition=b.listbar.position);b.flashplayer&&(d.src=b.flashplayer);b.html5player&&(c.src=b.html5player);a(b);d=b.aspectratio;
|
||||
if("string"!=typeof d||!e.exists(d))c=0;else{var g=d.indexOf(":");-1==g?c=0:(c=parseFloat(d.substr(0,g)),d=parseFloat(d.substr(g+1)),c=0>=c||0>=d?0:100*(d/c)+"%")}-1==b.width.toString().indexOf("%")?delete b.aspectratio:c?b.aspectratio=c:delete b.aspectratio;return b}).addConfig=function(b,c){a(c);return e.extend(b,c)}}(jwplayer),function(f){var a=f.utils,l=document;f.embed.download=function(e,f,b){function c(b,d){for(var c=l.querySelectorAll(b),g=0;g<c.length;g++)a.foreach(d,function(a,b){c[g].style[a]=
|
||||
b})}function d(a,b,d){a=l.createElement(a);b&&(a.className="jwdownload"+b);d&&d.appendChild(a);return a}var g=a.extend({},f),q=g.width?g.width:480,m=g.height?g.height:320,n;f=f.logo?f.logo:{prefix:a.repo(),file:"logo.png",margin:10};var k,h,r,g=g.playlist,p,t=["mp4","aac","mp3"];if(g&&g.length){p=g[0];n=p.sources;for(g=0;g<n.length;g++){var s=n[g],u=s.type?s.type:a.extensionmap.extType(a.extension(s.file));s.file&&a.foreach(t,function(b){u==t[b]?(k=s.file,h=p.image):a.isYouTube(s.file)&&(r=s.file)})}k?
|
||||
(n=k,b=h,e&&(g=d("a","display",e),d("div","icon",g),d("div","logo",g),n&&g.setAttribute("href",a.getAbsolutePath(n))),g="#"+e.id+" .jwdownload",e.style.width="",e.style.height="",c(g+"display",{width:a.styleDimension(Math.max(320,q)),height:a.styleDimension(Math.max(180,m)),background:"black center no-repeat "+(b?"url("+b+")":""),backgroundSize:"contain",position:"relative",border:"none",display:"block"}),c(g+"display div",{position:"absolute",width:"100%",height:"100%"}),c(g+"logo",{top:f.margin+
|
||||
"px",right:f.margin+"px",background:"top right no-repeat url("+f.prefix+f.file+")"}),c(g+"icon",{background:"center no-repeat url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAA8CAYAAAA6/NlyAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAgNJREFUeNrs28lqwkAYB/CZqNVDDj2r6FN41QeIy8Fe+gj6BL275Q08u9FbT8ZdwVfotSBYEPUkxFOoks4EKiJdaDuTjMn3wWBO0V/+sySR8SNSqVRKIR8qaXHkzlqS9jCfzzWcTCYp9hF5o+59sVjsiRzcegSckFzcjT+ruN80TeSlAjCAAXzdJSGPFXRpAAMYwACGZQkSdhG4WCzehMNhqV6vG6vVSrirKVEw66YoSqDb7cqlUilE8JjHd/y1MQefVzqdDmiaJpfLZWHgXMHn8F6vJ1cqlVAkEsGuAn83J4gAd2RZymQygX6/L1erVQt+9ZPWb+CDwcCC2zXGJaewl/DhcHhK3DVj+KfKZrMWvFarcYNLomAv4aPRSFZVlTlcSPA5fDweW/BoNIqFnKV53JvncjkLns/n/cLdS+92O7RYLLgsKfv9/t8XlDn4eDyiw+HA9Jyz2eyt0+kY2+3WFC5hluej0Ha7zQQq9PPwdDq1Et1sNsx/nFBgCqWJ8oAK1aUptNVqcYWewE4nahfU0YQnk4ntUEfGMIU2m01HoLaCKbTRaDgKtaVLk9tBYaBcE/6Artdr4RZ5TB6/dC+9iIe/WgAMYADDpAUJAxjAAAYwgGFZgoS/AtNNTF7Z2bL0BYPBV3Jw5xFwwWcYxgtBP5OkE8i9G7aWGOOCruvauwADALMLMEbKf4SdAAAAAElFTkSuQmCC)"})):
|
||||
r?(f=r,e=d("embed","",e),e.src="http://www.youtube.com/v/"+/v[=\/](\w*)|\/(\w+)$|^(\w+)$/i.exec(f).slice(1).join(""),e.type="application/x-shockwave-flash",e.width=q,e.height=m):b()}}}(jwplayer),function(f){var a=f.utils,l=f.events,e={};(f.embed.flash=function(j,b,c,d,g){function q(a,b,d){var c=document.createElement("param");c.setAttribute("name",b);c.setAttribute("value",d);a.appendChild(c)}function m(a,b,d){return function(){try{d&&document.getElementById(g.id+"_wrapper").appendChild(b);var c=
|
||||
document.getElementById(g.id).getPluginConfig("display");"function"==typeof a.resize&&a.resize(c.width,c.height);b.style.left=c.x;b.style.top=c.h}catch(e){}}}function n(b){if(!b)return{};var d={},c=[];a.foreach(b,function(b,g){var e=a.getPluginName(b);c.push(b);a.foreach(g,function(a,b){d[e+"."+a]=b})});d.plugins=c.join(",");return d}var k=new f.events.eventdispatcher,h=a.flashVersion();a.extend(this,k);this.embed=function(){c.id=g.id;if(10>h)return k.sendEvent(l.ERROR,{message:"Flash version must be 10.0 or greater"}),
|
||||
!1;var f,p,t=g.config.listbar,s=a.extend({},c);if(j.id+"_wrapper"==j.parentNode.id)f=document.getElementById(j.id+"_wrapper");else{f=document.createElement("div");p=document.createElement("div");p.style.display="none";p.id=j.id+"_aspect";f.id=j.id+"_wrapper";f.style.position="relative";f.style.display="block";f.style.width=a.styleDimension(s.width);f.style.height=a.styleDimension(s.height);if(g.config.aspectratio){var u=parseFloat(g.config.aspectratio);p.style.display="block";p.style.marginTop=g.config.aspectratio;
|
||||
f.style.height="auto";f.style.display="inline-block";t&&("bottom"==t.position?p.style.paddingBottom=t.size+"px":"right"==t.position&&(p.style.marginBottom=-1*t.size*(u/100)+"px"))}j.parentNode.replaceChild(f,j);f.appendChild(j);f.appendChild(p)}f=d.setupPlugins(g,s,m);0<f.length?a.extend(s,n(f.plugins)):delete s.plugins;"undefined"!=typeof s["dock.position"]&&"false"==s["dock.position"].toString().toLowerCase()&&(s.dock=s["dock.position"],delete s["dock.position"]);f=s.wmode?s.wmode:s.height&&40>=
|
||||
s.height?"transparent":"opaque";p="height width modes events primary base fallback volume".split(" ");for(t=0;t<p.length;t++)delete s[p[t]];p=a.getCookies();a.foreach(p,function(a,b){"undefined"==typeof s[a]&&(s[a]=b)});p=window.location.href.split("/");p.splice(p.length-1,1);p=p.join("/");s.base=p+"/";e[j.id]=s;a.isIE()?(p='\x3cobject classid\x3d"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" " width\x3d"100%" height\x3d"100%"id\x3d"'+j.id+'" name\x3d"'+j.id+'" tabindex\x3d0""\x3e',p+='\x3cparam name\x3d"movie" value\x3d"'+
|
||||
b.src+'"\x3e',p+='\x3cparam name\x3d"allowfullscreen" value\x3d"true"\x3e\x3cparam name\x3d"allowscriptaccess" value\x3d"always"\x3e',p+='\x3cparam name\x3d"seamlesstabbing" value\x3d"true"\x3e',p+='\x3cparam name\x3d"wmode" value\x3d"'+f+'"\x3e',p+='\x3cparam name\x3d"bgcolor" value\x3d"#000000"\x3e',p+="\x3c/object\x3e",j.outerHTML=p,f=document.getElementById(j.id)):(p=document.createElement("object"),p.setAttribute("type","application/x-shockwave-flash"),p.setAttribute("data",b.src),p.setAttribute("width",
|
||||
"100%"),p.setAttribute("height","100%"),p.setAttribute("bgcolor","#000000"),p.setAttribute("id",j.id),p.setAttribute("name",j.id),p.setAttribute("tabindex",0),q(p,"allowfullscreen","true"),q(p,"allowscriptaccess","always"),q(p,"seamlesstabbing","true"),q(p,"wmode",f),j.parentNode.replaceChild(p,j),f=p);g.config.aspectratio&&(f.style.position="absolute");g.container=f;g.setPlayer(f,"flash")};this.supportsConfig=function(){if(h)if(c){if("string"==a.typeOf(c.playlist))return!0;try{var b=c.playlist[0].sources;
|
||||
if("undefined"==typeof b)return!0;for(var d=0;d<b.length;d++){var g;if(g=b[d].file){var e=b[d].file,f=b[d].type;if(a.isYouTube(e)||a.isRtmp(e,f)||"hls"==f)g=!0;else{var j=a.extensionmap[f?f:a.extension(e)];g=!j?!1:!!j.flash}}if(g)return!0}}catch(k){}}else return!0;return!1}}).getVars=function(a){return e[a]}}(jwplayer),function(f){var a=f.utils,l=a.extensionmap,e=f.events;f.embed.html5=function(j,b,c,d,g){function q(a,b,d){return function(){try{var c=document.querySelector("#"+j.id+" .jwmain");d&&
|
||||
c.appendChild(b);"function"==typeof a.resize&&(a.resize(c.clientWidth,c.clientHeight),setTimeout(function(){a.resize(c.clientWidth,c.clientHeight)},400));b.left=c.style.left;b.top=c.style.top}catch(g){}}}function m(a){n.sendEvent(a.type,{message:"HTML5 player not found"})}var n=this,k=new e.eventdispatcher;a.extend(n,k);n.embed=function(){if(f.html5){d.setupPlugins(g,c,q);j.innerHTML="";var h=f.utils.extend({},c);delete h.volume;h=new f.html5.player(h);g.container=document.getElementById(g.id);g.setPlayer(h,
|
||||
"html5")}else h=new a.scriptloader(b.src),h.addEventListener(e.ERROR,m),h.addEventListener(e.COMPLETE,n.embed),h.load()};n.supportsConfig=function(){if(f.vid.canPlayType)try{if("string"==a.typeOf(c.playlist))return!0;for(var b=c.playlist[0].sources,d=0;d<b.length;d++){var g;var e=b[d].file,j=b[d].type;if(null!==navigator.userAgent.match(/BlackBerry/i)||a.isAndroid()&&("m3u"==a.extension(e)||"m3u8"==a.extension(e))||a.isRtmp(e,j))g=!1;else{var k=l[j?j:a.extension(e)],m;if(!k||k.flash&&!k.html5)m=!1;
|
||||
else{var n=k.html5,q=f.vid;if(n)try{m=q.canPlayType(n)?!0:!1}catch(z){m=!1}else m=!0}g=m}if(g)return!0}}catch(A){}return!1}}}(jwplayer),function(f){var a=f.embed,l=f.utils,e=l.extend(function(e){var b=l.repo(),c=l.extend({},f.defaults),d=l.extend({},c,e.config),g=e.config,q=d.plugins,m=d.analytics,n=b+"jwpsrv.js",k=b+"sharing.js",h=b+"related.js",r=b+"gapro.js",c=f.key?f.key:c.key,p="premium"/*(new f.utils.key(c)).edition()*/,q=q?q:{}; /*alert(c);*/ "ads"==p&&d.advertising&&(d.advertising.client.match(".js$|.swf$")?q[d.advertising.client]=
|
||||
d.advertising:q[b+d.advertising.client+".js"]=d.advertising);delete g.advertising;g.key=c;d.analytics&&(d.analytics.client&&d.analytics.client.match(".js$|.swf$"))&&(n=d.analytics.client);delete g.analytics;if("free"==p||!m||!1!==m.enabled)q[n]=m?m:{};delete q.sharing;delete q.related;if("premium"==p||"ads"==p)d.sharing&&(d.sharing.client&&d.sharing.client.match(".js$|.swf$")&&(k=d.sharing.client),q[k]=d.sharing),d.related&&(d.related.client&&d.related.client.match(".js$|.swf$")&&(h=d.related.client),
|
||||
q[h]=d.related),d.ga&&(d.ga.client&&d.ga.client.match(".js$|.swf$")&&(r=d.ga.client),q[r]=d.ga),d.skin&&(g.skin=d.skin.replace(/^(beelden|bekle|five|glow|modieus|roundster|stormtrooper|vapor)$/i,l.repo()+"skins/$1.xml"));g.plugins=q;return new a(e)},a);f.embed=e}(jwplayer),function(f){var a=[],l=f.utils,e=f.events,j=e.state,b=document,c=f.api=function(a){function g(a,b){return function(d){return b(a,d)}}function q(a,b){p[a]||(p[a]=[],n(e.JWPLAYER_PLAYER_STATE,function(b){var d=b.newstate;b=b.oldstate;
|
||||
if(d==a){var c=p[d];if(c)for(var g=0;g<c.length;g++)"function"==typeof c[g]&&c[g].call(this,{oldstate:b,newstate:d})}}));p[a].push(b);return h}function m(a,b){try{a.jwAddEventListener(b,'function(dat) { jwplayer("'+h.id+'").dispatchEvent("'+b+'", dat); }')}catch(d){l.log("Could not add internal listener")}}function n(a,b){r[a]||(r[a]=[],t&&s&&m(t,a));r[a].push(b);return h}function k(){if(s){for(var a=arguments[0],b=[],d=1;d<arguments.length;d++)b.push(arguments[d]);if("undefined"!=typeof t&&"function"==
|
||||
typeof t[a])switch(b.length){case 4:return t[a](b[0],b[1],b[2],b[3]);case 3:return t[a](b[0],b[1],b[2]);case 2:return t[a](b[0],b[1]);case 1:return t[a](b[0]);default:return t[a]()}return null}u.push(arguments)}var h=this,r={},p={},t=void 0,s=!1,u=[],w=void 0,x={},y={};h.container=a;h.id=a.id;h.getBuffer=function(){return k("jwGetBuffer")};h.getContainer=function(){return h.container};h.addButton=function(a,b,d,c){try{y[c]=d,k("jwDockAddButton",a,b,"jwplayer('"+h.id+"').callback('"+c+"')",c)}catch(g){l.log("Could not add dock button"+
|
||||
g.message)}};h.removeButton=function(a){k("jwDockRemoveButton",a)};h.callback=function(a){if(y[a])y[a]()};h.forceState=function(a){k("jwForceState",a);return h};h.releaseState=function(){return k("jwReleaseState")};h.getDuration=function(){return k("jwGetDuration")};h.getFullscreen=function(){return k("jwGetFullscreen")};h.getStretching=function(){return k("jwGetStretching")};h.getHeight=function(){return k("jwGetHeight")};h.getLockState=function(){return k("jwGetLockState")};h.getMeta=function(){return h.getItemMeta()};
|
||||
h.getMute=function(){return k("jwGetMute")};h.getPlaylist=function(){var a=k("jwGetPlaylist");"flash"==h.renderingMode&&l.deepReplaceKeyName(a,["__dot__","__spc__","__dsh__","__default__"],["."," ","-","default"]);return a};h.getPlaylistItem=function(a){l.exists(a)||(a=h.getCurrentItem());return h.getPlaylist()[a]};h.getPosition=function(){return k("jwGetPosition")};h.getRenderingMode=function(){return h.renderingMode};h.getState=function(){return k("jwGetState")};h.getVolume=function(){return k("jwGetVolume")};
|
||||
h.getWidth=function(){return k("jwGetWidth")};h.setFullscreen=function(a){l.exists(a)?k("jwSetFullscreen",a):k("jwSetFullscreen",!k("jwGetFullscreen"));return h};h.setStretching=function(a){k("jwSetStretching",a);return h};h.setMute=function(a){l.exists(a)?k("jwSetMute",a):k("jwSetMute",!k("jwGetMute"));return h};h.lock=function(){return h};h.unlock=function(){return h};h.load=function(a){k("jwLoad",a);return h};h.playlistItem=function(a){k("jwPlaylistItem",parseInt(a));return h};h.playlistPrev=function(){k("jwPlaylistPrev");
|
||||
return h};h.playlistNext=function(){k("jwPlaylistNext");return h};h.resize=function(a,d){if("flash"!=h.renderingMode){var c=document.getElementById(h.id);c.className=c.className.replace(/\s+aspectMode/,"");c.style.display="block";k("jwResize",a,d)}else{var c=b.getElementById(h.id+"_wrapper"),g=b.getElementById(h.id+"_aspect");g&&(g.style.display="none");c&&(c.style.display="block",c.style.width=l.styleDimension(a),c.style.height=l.styleDimension(d))}return h};h.play=function(a){"undefined"==typeof a?
|
||||
(a=h.getState(),a==j.PLAYING||a==j.BUFFERING?k("jwPause"):k("jwPlay")):k("jwPlay",a);return h};h.pause=function(a){"undefined"==typeof a?(a=h.getState(),a==j.PLAYING||a==j.BUFFERING?k("jwPause"):k("jwPlay")):k("jwPause",a);return h};h.stop=function(){k("jwStop");return h};h.seek=function(a){k("jwSeek",a);return h};h.setVolume=function(a){k("jwSetVolume",a);return h};h.loadInstream=function(a,b){return w=new c.instream(this,t,a,b)};h.getQualityLevels=function(){return k("jwGetQualityLevels")};h.getCurrentQuality=
|
||||
function(){return k("jwGetCurrentQuality")};h.setCurrentQuality=function(a){k("jwSetCurrentQuality",a)};h.getCaptionsList=function(){return k("jwGetCaptionsList")};h.getCurrentCaptions=function(){return k("jwGetCurrentCaptions")};h.setCurrentCaptions=function(a){k("jwSetCurrentCaptions",a)};h.getControls=function(){return k("jwGetControls")};h.getSafeRegion=function(){return k("jwGetSafeRegion")};h.setControls=function(a){k("jwSetControls",a)};h.destroyPlayer=function(){k("jwPlayerDestroy")};var z=
|
||||
{onBufferChange:e.JWPLAYER_MEDIA_BUFFER,onBufferFull:e.JWPLAYER_MEDIA_BUFFER_FULL,onError:e.JWPLAYER_ERROR,onFullscreen:e.JWPLAYER_FULLSCREEN,onMeta:e.JWPLAYER_MEDIA_META,onMute:e.JWPLAYER_MEDIA_MUTE,onPlaylist:e.JWPLAYER_PLAYLIST_LOADED,onPlaylistItem:e.JWPLAYER_PLAYLIST_ITEM,onPlaylistComplete:e.JWPLAYER_PLAYLIST_COMPLETE,onReady:e.API_READY,onResize:e.JWPLAYER_RESIZE,onComplete:e.JWPLAYER_MEDIA_COMPLETE,onSeek:e.JWPLAYER_MEDIA_SEEK,onTime:e.JWPLAYER_MEDIA_TIME,onVolume:e.JWPLAYER_MEDIA_VOLUME,
|
||||
onBeforePlay:e.JWPLAYER_MEDIA_BEFOREPLAY,onBeforeComplete:e.JWPLAYER_MEDIA_BEFORECOMPLETE,onDisplayClick:e.JWPLAYER_DISPLAY_CLICK,onControls:e.JWPLAYER_CONTROLS,onQualityLevels:e.JWPLAYER_MEDIA_LEVELS,onQualityChange:e.JWPLAYER_MEDIA_LEVEL_CHANGED,onCaptionsList:e.JWPLAYER_CAPTIONS_LIST,onCaptionsChange:e.JWPLAYER_CAPTIONS_CHANGED};l.foreach(z,function(a){h[a]=g(z[a],n)});var A={onBuffer:j.BUFFERING,onPause:j.PAUSED,onPlay:j.PLAYING,onIdle:j.IDLE};l.foreach(A,function(a){h[a]=g(A[a],q)});h.remove=
|
||||
function(){if(!s)throw"Cannot call remove() before player is ready";u=[];c.destroyPlayer(this.id)};h.setup=function(a){if(f.embed){var d=b.getElementById(h.id);d&&(a.fallbackDiv=d);d=h;u=[];c.destroyPlayer(d.id);d=f(h.id);d.config=a;return new f.embed(d)}return h};h.registerPlugin=function(a,b,d,c){f.plugins.registerPlugin(a,b,d,c)};h.setPlayer=function(a,b){t=a;h.renderingMode=b};h.detachMedia=function(){if("html5"==h.renderingMode)return k("jwDetachMedia")};h.attachMedia=function(a){if("html5"==
|
||||
h.renderingMode)return k("jwAttachMedia",a)};h.dispatchEvent=function(a,b){if(r[a])for(var d=l.translateEventResponse(a,b),c=0;c<r[a].length;c++)if("function"==typeof r[a][c])try{a==e.JWPLAYER_PLAYLIST_LOADED&&l.deepReplaceKeyName(d.playlist,["__dot__","__spc__","__dsh__","__default__"],["."," ","-","default"]),r[a][c].call(this,d)}catch(g){l.log("There was an error calling back an event handler")}};h.dispatchInstreamEvent=function(a){w&&w.dispatchEvent(a,arguments)};h.callInternal=k;h.playerReady=
|
||||
function(a){s=!0;t||h.setPlayer(b.getElementById(a.id));h.container=b.getElementById(h.id);l.foreach(r,function(a){m(t,a)});n(e.JWPLAYER_PLAYLIST_ITEM,function(){x={}});n(e.JWPLAYER_MEDIA_META,function(a){l.extend(x,a.metadata)});for(h.dispatchEvent(e.API_READY);0<u.length;)k.apply(this,u.shift())};h.getItemMeta=function(){return x};h.getCurrentItem=function(){return k("jwGetPlaylistIndex")};return h};c.selectPlayer=function(d){var g;l.exists(d)||(d=0);d.nodeType?g=d:"string"==typeof d&&(g=b.getElementById(d));
|
||||
return g?(d=c.playerById(g.id))?d:c.addPlayer(new c(g)):"number"==typeof d?a[d]:null};c.playerById=function(b){for(var c=0;c<a.length;c++)if(a[c].id==b)return a[c];return null};c.addPlayer=function(b){for(var c=0;c<a.length;c++)if(a[c]==b)return b;a.push(b);return b};c.destroyPlayer=function(d){for(var c=-1,e,f=0;f<a.length;f++)a[f].id==d&&(c=f,e=a[f]);0<=c&&(d=e.id,f=b.getElementById(d+("flash"==e.renderingMode?"_wrapper":"")),l.clearCss&&l.clearCss("#"+d),f&&("html5"==e.renderingMode&&e.destroyPlayer(),
|
||||
e=b.createElement("div"),e.id=d,f.parentNode.replaceChild(e,f)),a.splice(c,1));return null};f.playerReady=function(a){var b=f.api.playerById(a.id);b?b.playerReady(a):f.api.selectPlayer(a.id).playerReady(a)}}(jwplayer),function(f){var a=f.events,l=f.utils,e=a.state;f.api.instream=function(f,b,c,d){function g(a,b){k[a]||(k[a]=[],n.jwInstreamAddEventListener(a,'function(dat) { jwplayer("'+m.id+'").dispatchInstreamEvent("'+a+'", dat); }'));k[a].push(b);return this}function q(b,c){h[b]||(h[b]=[],g(a.JWPLAYER_PLAYER_STATE,
|
||||
function(a){var c=a.newstate,d=a.oldstate;if(c==b){var e=h[c];if(e)for(var f=0;f<e.length;f++)"function"==typeof e[f]&&e[f].call(this,{oldstate:d,newstate:c,type:a.type})}}));h[b].push(c);return this}var m=f,n=b,k={},h={};this.dispatchEvent=function(a,b){if(k[a])for(var c=l.translateEventResponse(a,b[1]),d=0;d<k[a].length;d++)"function"==typeof k[a][d]&&k[a][d].call(this,c)};this.onError=function(b){return g(a.JWPLAYER_ERROR,b)};this.onFullscreen=function(b){return g(a.JWPLAYER_FULLSCREEN,b)};this.onMeta=
|
||||
function(b){return g(a.JWPLAYER_MEDIA_META,b)};this.onMute=function(b){return g(a.JWPLAYER_MEDIA_MUTE,b)};this.onComplete=function(b){return g(a.JWPLAYER_MEDIA_COMPLETE,b)};this.onTime=function(b){return g(a.JWPLAYER_MEDIA_TIME,b)};this.onBuffer=function(a){return q(e.BUFFERING,a)};this.onPause=function(a){return q(e.PAUSED,a)};this.onPlay=function(a){return q(e.PLAYING,a)};this.onIdle=function(a){return q(e.IDLE,a)};this.onClick=function(b){return g(a.JWPLAYER_INSTREAM_CLICK,b)};this.onInstreamDestroyed=
|
||||
function(b){return g(a.JWPLAYER_INSTREAM_DESTROYED,b)};this.play=function(a){n.jwInstreamPlay(a)};this.pause=function(a){n.jwInstreamPause(a)};this.destroy=function(){n.jwInstreamDestroy()};m.callInternal("jwLoadInstream",c,d?d:{})}}(jwplayer),function(f){var a=f.api,l=a.selectPlayer;a.selectPlayer=function(a){return(a=l(a))?a:{registerPlugin:function(a,b,c){f.plugins.registerPlugin(a,b,c)}}}}(jwplayer));
|
|
@ -34,8 +34,6 @@ function update_nav() {
|
|||
$("#nav_srs_publisher").attr("href", "srs_publisher.html" + window.location.search);
|
||||
$("#nav_srs_chat").attr("href", "srs_chat.html" + window.location.search);
|
||||
$("#nav_srs_bwt").attr("href", "srs_bwt.html" + window.location.search);
|
||||
$("#nav_jwplayer6").attr("href", "jwplayer6.html" + window.location.search);
|
||||
$("#nav_osmf").attr("href", "osmf.html" + window.location.search);
|
||||
$("#nav_vlc").attr("href", "vlc.html" + window.location.search);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,149 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>SRS</title>
|
||||
<meta charset="utf-8">
|
||||
<link rel="stylesheet" type="text/css" href="css/bootstrap.min.css"/>
|
||||
<script type="text/javascript" src="js/jquery-1.10.2.min.js"></script>
|
||||
<script type="text/javascript" src="js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="js/srs.page.js"></script>
|
||||
<script type="text/javascript" src="js/srs.log.js"></script>
|
||||
<script type="text/javascript" src="js/srs.player.js"></script>
|
||||
<script type="text/javascript" src="js/srs.publisher.js"></script>
|
||||
<script type="text/javascript" src="js/srs.utility.js"></script>
|
||||
<script type="text/javascript" src="js/winlin.utility.js"></script>
|
||||
<style>
|
||||
body{
|
||||
padding-top: 55px;
|
||||
}
|
||||
#main_modal {
|
||||
width: 700px;
|
||||
margin-left: -350px;
|
||||
}
|
||||
</style>
|
||||
<script type="text/javascript" src="js/jwplayer.js" ></script>
|
||||
<script type='text/javascript'>jwplayer.key = 'N8zhkmYvvRwOhz4aTGkySoEri4x+9pQwR7GHIQ=='; </script>
|
||||
<script type="text/javascript">
|
||||
/****
|
||||
* The parameters for this page:
|
||||
* schema, the protocol schema, rtmp or http.
|
||||
* server, the ip of the url.
|
||||
* port, the rtmp port of url.
|
||||
* vhost, the vhost of url, can equals to server.
|
||||
* app, the app of url.
|
||||
* stream, the stream of url, can endwith .flv or .mp4 or nothing for RTMP.
|
||||
* autostart, whether auto play the stream.
|
||||
* Additional params:
|
||||
* hls_vhost, the vhost for hls.
|
||||
* hls_port, the port for hls play.
|
||||
* hls_autostart, whether auto play the hls stream.
|
||||
*/
|
||||
var _player = null;
|
||||
var _url = null;
|
||||
$(function(){
|
||||
// get the vhost and port to set the default url.
|
||||
// for example: http://192.168.1.213/players/jwplayer6.html?port=1935&vhost=demo
|
||||
// url set to: rtmp://demo:1935/live/livestream
|
||||
srs_init("#txt_rtmp_url", "#txt_hls_url", "#main_modal");
|
||||
|
||||
$("#main_modal").on("hide", function(){
|
||||
$("#div_container").remove();
|
||||
_player.stop();
|
||||
});
|
||||
|
||||
$("#main_modal").on("show", function(){
|
||||
$("#div_container").remove();
|
||||
|
||||
var div_container = $("<div/>");
|
||||
$(div_container).attr("id", "div_container");
|
||||
$("#player").append(div_container);
|
||||
|
||||
var player = $("<div/>");
|
||||
$(player).attr("id", "player_id");
|
||||
$(div_container).append(player);
|
||||
|
||||
var conf = {
|
||||
file: _url,
|
||||
width: srs_get_player_width(),
|
||||
height: srs_get_player_height(),
|
||||
autostart: true,
|
||||
analytics: { enabled: false}
|
||||
};
|
||||
_player = jwplayer('player_id').setup(conf);
|
||||
});
|
||||
|
||||
$("#btn_play_rtmp").click(function(){
|
||||
_url = $("#txt_rtmp_url").val();
|
||||
$("#main_modal").modal({show:true, keyboard:false});
|
||||
});
|
||||
|
||||
$("#btn_play_hls").click(function(){
|
||||
_url = $("#txt_hls_url").val();
|
||||
$("#main_modal").modal({show:true, keyboard:false});
|
||||
});
|
||||
|
||||
var query = parse_query_string();
|
||||
if (query.hls_autostart == "true") {
|
||||
_url = $("#txt_hls_url").val();
|
||||
$("#main_modal").modal({show:true, keyboard:false});
|
||||
} else if (query.rtmp_autostart == "true") {
|
||||
_url = $("#txt_rtmp_url").val();
|
||||
$("#main_modal").modal({show:true, keyboard:false});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<img src='https://ossrs.net/gif/v1/sls.gif?site=ossrs.net&path=/player/jwplayer'/>
|
||||
<div class="navbar navbar-fixed-top">
|
||||
<div class="navbar-inner">
|
||||
<div class="container">
|
||||
<a id="srs_index" class="brand" href="#">SRS</a>
|
||||
<div class="nav-collapse collapse">
|
||||
<ul class="nav">
|
||||
<li><a id="nav_srs_player" href="srs_player.html">SRS播放器</a></li>
|
||||
<li><a id="nav_srs_publisher" href="srs_publisher.html">SRS编码器</a></li>
|
||||
<li><a id="nav_srs_chat" href="srs_chat.html">SRS会议</a></li>
|
||||
<li><a id="nav_srs_bwt" href="srs_bwt.html">SRS测网速</a></li>
|
||||
<li class="active"><a id="nav_jwplayer6" href="jwplayer6.html">JWPlayer6播放器</a></li>
|
||||
<li><a id="nav_osmf" href="osmf.html">AdobeOSMF播放器</a></li>
|
||||
<li><a id="nav_vlc" href="vlc.html">VLC播放器</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container">
|
||||
<div class="alert alert-info fade in">
|
||||
<button type="button" class="close" data-dismiss="alert">×</button>
|
||||
<strong><span>Usage:</span></strong> <span>输入地址后点击播放按钮</span>
|
||||
</div>
|
||||
<div class="form-inline">
|
||||
URL:
|
||||
<input type="text" id="txt_rtmp_url" class="input-xxlarge" value=""></input>
|
||||
<button class="btn btn-primary" id="btn_play_rtmp">播放RTMP</button>
|
||||
</div>
|
||||
<hr/>
|
||||
<div class="form-inline">
|
||||
URL:
|
||||
<input type="text" id="txt_hls_url" class="input-xxlarge" value=""></input>
|
||||
<button class="btn btn-primary" id="btn_play_hls"> 播放HLS </button>
|
||||
</div>
|
||||
<div id="main_modal" class="modal hide fade">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h3>JWPlayer6</h3>
|
||||
</div>
|
||||
<div class="modal-body" id="player">
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-primary" data-dismiss="modal" aria-hidden="true"> 关闭 </button>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<footer>
|
||||
<p><a href="https://github.com/ossrs/srs">SRS Team © 2013</a></p>
|
||||
</footer>
|
||||
</div>
|
||||
</body>
|
||||
|
|
@ -1,125 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>SRS</title>
|
||||
<meta charset="utf-8">
|
||||
<link rel="stylesheet" type="text/css" href="css/bootstrap.min.css"/>
|
||||
<style>
|
||||
body{
|
||||
padding-top: 55px;
|
||||
}
|
||||
#main_modal {
|
||||
width: 700px;
|
||||
margin-left: -350px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<img src='https://ossrs.net/gif/v1/sls.gif?site=ossrs.net&path=/player/osmf'/>
|
||||
<div class="navbar navbar-fixed-top">
|
||||
<div class="navbar-inner">
|
||||
<div class="container">
|
||||
<a id="srs_index" class="brand" href="#">SRS</a>
|
||||
<div class="nav-collapse collapse">
|
||||
<ul class="nav">
|
||||
<li><a id="nav_srs_player" href="srs_player.html">SRS播放器</a></li>
|
||||
<li><a id="nav_srs_publisher" href="srs_publisher.html">SRS编码器</a></li>
|
||||
<li><a id="nav_srs_chat" href="srs_chat.html">SRS会议</a></li>
|
||||
<li><a id="nav_srs_bwt" href="srs_bwt.html">SRS测网速</a></li>
|
||||
<li><a id="nav_jwplayer6" href="jwplayer6.html">JWPlayer6播放器</a></li>
|
||||
<li class="active"><a id="nav_osmf" href="osmf.html">AdobeOSMF播放器</a></li>
|
||||
<li><a id="nav_vlc" href="vlc.html">VLC播放器</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container">
|
||||
<div class="alert alert-info fade in">
|
||||
<button type="button" class="close" data-dismiss="alert">×</button>
|
||||
<strong><span>Usage:</span></strong> <span>输入地址后点击播放按钮</span>
|
||||
</div>
|
||||
<div class="form-inline">
|
||||
URL:
|
||||
<input type="text" id="txt_url" class="input-xxlarge" value=""></input>
|
||||
<button class="btn btn-primary" id="btn_play">播放视频</button>
|
||||
</div>
|
||||
<div id="main_modal" class="modal hide fade">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h3>AdobeOSMF</h3>
|
||||
</div>
|
||||
<div class="modal-body" id="player">
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-primary" data-dismiss="modal" aria-hidden="true"> 关闭 </button>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<footer>
|
||||
<p><a href="https://github.com/ossrs/srs">SRS Team © 2013</a></p>
|
||||
</footer>
|
||||
</div>
|
||||
</body>
|
||||
<script type="text/javascript" src="js/jquery-1.10.2.min.js"></script>
|
||||
<script type="text/javascript" src="js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="js/swfobject.js"></script>
|
||||
<script type="text/javascript" src="js/srs.page.js"></script>
|
||||
<script type="text/javascript" src="js/srs.log.js"></script>
|
||||
<script type="text/javascript" src="js/srs.player.js"></script>
|
||||
<script type="text/javascript" src="js/srs.publisher.js"></script>
|
||||
<script type="text/javascript" src="js/srs.utility.js"></script>
|
||||
<script type="text/javascript" src="js/winlin.utility.js"></script>
|
||||
<script type="text/javascript">
|
||||
function osmf_play(url) {
|
||||
$("#div_container").remove();
|
||||
|
||||
var div_container = $("<div/>");
|
||||
$(div_container).attr("id", "div_container");
|
||||
$("#player").append(div_container);
|
||||
|
||||
var player = $("<div/>");
|
||||
$(player).attr("id", "player_id");
|
||||
$(div_container).append(player);
|
||||
|
||||
var flashvars = {};
|
||||
flashvars.src = url;
|
||||
flashvars.streamType = "live"; // live or recorded
|
||||
flashvars.autoPlay = true;
|
||||
flashvars.controlBarAutoHide = false;
|
||||
flashvars.scaleMode = "stretch";
|
||||
flashvars.bufferTime = 0.8;
|
||||
|
||||
var params = {};
|
||||
params.allowFullScreen = true;
|
||||
|
||||
var attributes = {};
|
||||
|
||||
swfobject.embedSWF(
|
||||
"js/StrobeMediaPlayback.swf", "player_id",
|
||||
srs_get_player_width(), srs_get_player_height(),
|
||||
"11.1", "js/AdobeFlashPlayerInstall.swf",
|
||||
flashvars, params, attributes
|
||||
);
|
||||
}
|
||||
$(function(){
|
||||
// get the vhost and port to set the default url.
|
||||
// for example: http://192.168.1.213/players/jwplayer6.html?port=1935&vhost=demo
|
||||
// url set to: rtmp://demo:1935/live/livestream
|
||||
srs_init_rtmp("#txt_url", "#main_modal");
|
||||
|
||||
$("#main_modal").on("hide", function(){
|
||||
osmf_play("http://localhost");
|
||||
$("#div_container").remove();
|
||||
});
|
||||
$("#main_modal").on("show", function(){
|
||||
var url = $("#txt_url").val();
|
||||
osmf_play(url);
|
||||
});
|
||||
|
||||
$("#btn_play").click(function(){
|
||||
$("#main_modal").modal({show:true, keyboard:false});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</html>
|
|
@ -28,8 +28,6 @@
|
|||
<!--<li><a id="nav_srs_publisher" href="srs_publisher.html">SRS编码器</a></li>-->
|
||||
<!--<li><a id="nav_srs_chat" href="srs_chat.html">SRS会议</a></li>-->
|
||||
<!--<li><a id="nav_srs_bwt" href="srs_bwt.html">SRS测网速</a></li>-->
|
||||
<!--<li><a id="nav_jwplayer6" href="jwplayer6.html">JWPlayer6播放器</a></li>-->
|
||||
<!--<li><a id="nav_osmf" href="osmf.html">AdobeOSMF播放器</a></li>-->
|
||||
<!--<li><a id="nav_vlc" href="vlc.html">VLC播放器</a></li>-->
|
||||
<li><a id="nav_gb28181" href="srs_gb28181.html">GB28181</a></li>
|
||||
<li><a href="https://github.com/ossrs/srs">源码</a></li>
|
||||
|
|
|
@ -28,8 +28,6 @@
|
|||
<!--<li><a id="nav_srs_publisher" href="srs_publisher.html">SRS编码器</a></li>-->
|
||||
<!--<li><a id="nav_srs_chat" href="srs_chat.html">SRS会议</a></li>-->
|
||||
<!--<li><a id="nav_srs_bwt" href="srs_bwt.html">SRS测网速</a></li>-->
|
||||
<!--<li><a id="nav_jwplayer6" href="jwplayer6.html">JWPlayer6播放器</a></li>-->
|
||||
<!--<li><a id="nav_osmf" href="osmf.html">AdobeOSMF播放器</a></li>-->
|
||||
<!--<li><a id="nav_vlc" href="vlc.html">VLC播放器</a></li>-->
|
||||
<li><a id="nav_gb28181" href="srs_gb28181.html">GB28181</a></li>
|
||||
<li><a href="https://github.com/ossrs/srs">源码</a></li>
|
||||
|
|
|
@ -28,8 +28,6 @@
|
|||
<li><a id="nav_srs_publisher" href="srs_publisher.html">SRS编码器</a></li>
|
||||
<li><a id="nav_srs_chat" href="srs_chat.html">SRS会议</a></li>
|
||||
<li class="active"><a id="nav_srs_bwt" href="srs_bwt.html">SRS测网速</a></li>
|
||||
<!--<li><a id="nav_jwplayer6" href="jwplayer6.html">JWPlayer6播放器</a></li>-->
|
||||
<!--<li><a id="nav_osmf" href="osmf.html">AdobeOSMF播放器</a></li>-->
|
||||
<li><a id="nav_vlc" href="vlc.html">VLC播放器</a></li>
|
||||
<li><a id="nav_gb28181" href="srs_gb28181.html">SRS-GB28181</a></li>
|
||||
</ul>
|
||||
|
|
|
@ -27,8 +27,6 @@
|
|||
<li><a id="nav_srs_publisher" href="srs_publisher.html">SRS编码器</a></li>
|
||||
<li class="active"><a id="nav_srs_chat" href="srs_chat.html">SRS会议</a></li>
|
||||
<li><a id="nav_srs_bwt" href="srs_bwt.html">SRS测网速</a></li>
|
||||
<!--<li><a id="nav_jwplayer6" href="jwplayer6.html">JWPlayer6播放器</a></li>-->
|
||||
<!--<li><a id="nav_osmf" href="osmf.html">AdobeOSMF播放器</a></li>-->
|
||||
<li><a id="nav_vlc" href="vlc.html">VLC播放器</a></li>
|
||||
<li><a id="nav_gb28181" href="srs_gb28181.html">SRS-GB28181</a></li>
|
||||
</ul>
|
||||
|
|
|
@ -41,8 +41,6 @@
|
|||
<!--<li><a id="nav_srs_publisher" href="srs_publisher.html">SRS编码器</a></li>-->
|
||||
<!--<li><a id="nav_srs_chat" href="srs_chat.html">SRS会议</a></li>-->
|
||||
<!--<li><a id="nav_srs_bwt" href="srs_bwt.html">SRS测网速</a></li>-->
|
||||
<!--<li><a id="nav_jwplayer6" href="jwplayer6.html">JWPlayer6播放器</a></li>-->
|
||||
<!--<li><a id="nav_osmf" href="osmf.html">AdobeOSMF播放器</a></li>-->
|
||||
<!--<li><a id="nav_vlc" href="vlc.html">VLC播放器</a></li>-->
|
||||
<li class="active" ><a id="nav_gb28181" href="srs_gb28181.html">GB28181</a></li>
|
||||
<li><a href="https://github.com/ossrs/srs">源码</a></li>
|
||||
|
@ -673,7 +671,7 @@
|
|||
var query = parse_query_string();
|
||||
|
||||
// get the vhost and port to set the default url.
|
||||
// for example: http://192.168.1.213/players/jwplayer6.html?port=1935&vhost=demo
|
||||
// for example: http://192.168.1.213/players/srs_player.html?port=1935&vhost=demo
|
||||
// url set to: rtmp://demo:1935/live/livestream
|
||||
srs_init_rtmp("#txt_url", "#main_modal");
|
||||
srs_init_rtmp("#txt_url", "#rtc_player_modal");
|
||||
|
@ -886,17 +884,6 @@
|
|||
});
|
||||
}
|
||||
|
||||
var jwplayer_url = "http://" + query.host + query.dir + "/jwplayer6.html?vhost=demo.srs.com&app=live&hls_autostart=true";
|
||||
if (true) {
|
||||
$("#srs_publish_hls").attr("href", jwplayer_url + "&stream=livestream");
|
||||
$("#srs_publish_ld_hls").attr("href", jwplayer_url + "&stream=livestream_ld");
|
||||
$("#srs_publish_sd_hls").attr("href", jwplayer_url + "&stream=livestream_sd");
|
||||
var jwplayer_url = "http://" + query.host + query.dir + "/jwplayer6.html?vhost=demo.srs.com&app=forward/live&hls_autostart=true";
|
||||
$("#srs_publish_fw_hls").attr("href", jwplayer_url + "&stream=livestream");
|
||||
$("#srs_publish_fw_ld_hls").attr("href", jwplayer_url + "&stream=livestream_ld");
|
||||
$("#srs_publish_fw_sd_hls").attr("href", jwplayer_url + "&stream=livestream_sd");
|
||||
}
|
||||
|
||||
if (true) {
|
||||
$("#btn_dar_original").click(function(){
|
||||
select_dar("#btn_dar_original", 0, 0);
|
||||
|
|
|
@ -38,8 +38,6 @@
|
|||
<!--<li><a id="nav_srs_publisher" href="srs_publisher.html">SRS编码器</a></li>-->
|
||||
<!--<li><a id="nav_srs_chat" href="srs_chat.html">SRS会议</a></li>-->
|
||||
<!--<li><a id="nav_srs_bwt" href="srs_bwt.html">SRS测网速</a></li>-->
|
||||
<!--<li><a id="nav_jwplayer6" href="jwplayer6.html">JWPlayer6播放器</a></li>-->
|
||||
<!--<li><a id="nav_osmf" href="osmf.html">AdobeOSMF播放器</a></li>-->
|
||||
<!--li><a id="nav_vlc" href="vlc.html">VLC播放器</a></li>-->
|
||||
<li><a id="nav_gb28181" href="srs_gb28181.html">GB28181</a></li>
|
||||
<li><a href="https://github.com/ossrs/srs">源码</a></li>
|
||||
|
@ -418,7 +416,7 @@
|
|||
var query = parse_query_string();
|
||||
|
||||
// get the vhost and port to set the default url.
|
||||
// for example: http://192.168.1.213/players/jwplayer6.html?port=1935&vhost=demo
|
||||
// for example: http://192.168.1.213/players/srs_player.html?port=1935&vhost=demo
|
||||
// url set to: rtmp://demo:1935/live/livestream
|
||||
srs_init_rtmp("#txt_url", "#main_modal");
|
||||
|
||||
|
@ -628,17 +626,6 @@
|
|||
});
|
||||
}
|
||||
|
||||
var jwplayer_url = "http://" + query.host + query.dir + "/jwplayer6.html?vhost=demo.srs.com&app=live&hls_autostart=true";
|
||||
if (true) {
|
||||
$("#srs_publish_hls").attr("href", jwplayer_url + "&stream=livestream");
|
||||
$("#srs_publish_ld_hls").attr("href", jwplayer_url + "&stream=livestream_ld");
|
||||
$("#srs_publish_sd_hls").attr("href", jwplayer_url + "&stream=livestream_sd");
|
||||
var jwplayer_url = "http://" + query.host + query.dir + "/jwplayer6.html?vhost=demo.srs.com&app=forward/live&hls_autostart=true";
|
||||
$("#srs_publish_fw_hls").attr("href", jwplayer_url + "&stream=livestream");
|
||||
$("#srs_publish_fw_ld_hls").attr("href", jwplayer_url + "&stream=livestream_ld");
|
||||
$("#srs_publish_fw_sd_hls").attr("href", jwplayer_url + "&stream=livestream_sd");
|
||||
}
|
||||
|
||||
if (true) {
|
||||
$("#btn_dar_original").click(function(){
|
||||
select_dar("#btn_dar_original", 0, 0);
|
||||
|
|
|
@ -27,8 +27,6 @@
|
|||
<li class="active"><a id="nav_srs_publisher" href="srs_publisher.html">SRS编码器</a></li>
|
||||
<li><a id="nav_srs_chat" href="srs_chat.html">SRS会议</a></li>
|
||||
<li><a id="nav_srs_bwt" href="srs_bwt.html">SRS测网速</a></li>
|
||||
<!--<li><a id="nav_jwplayer6" href="jwplayer6.html">JWPlayer6播放器</a></li>-->
|
||||
<!--<li><a id="nav_osmf" href="osmf.html">AdobeOSMF播放器</a></li>-->
|
||||
<li><a id="nav_vlc" href="vlc.html">VLC播放器</a></li>
|
||||
<li><a id="nav_gb28181" href="srs_gb28181.html">SRS-GB28181</a></li>
|
||||
</ul>
|
||||
|
|
|
@ -22,8 +22,6 @@
|
|||
<li class="active"><a id="nav_srs_publisher" href="srs_publisher.html">SRS编码器</a></li>
|
||||
<li><a id="nav_srs_chat" href="srs_chat.html">SRS会议</a></li>
|
||||
<li><a id="nav_srs_bwt" href="srs_bwt.html">SRS测网速</a></li>
|
||||
<!--<li><a id="nav_jwplayer6" href="jwplayer6.html">JWPlayer6播放器</a></li>-->
|
||||
<!--<li><a id="nav_osmf" href="osmf.html">AdobeOSMF播放器</a></li>-->
|
||||
<li><a id="nav_vlc" href="vlc.html">VLC播放器</a></li>
|
||||
<li><a id="nav_gb28181" href="srs_gb28181.html">SRS-GB28181</a></li>
|
||||
</ul>
|
||||
|
@ -317,7 +315,7 @@
|
|||
var query = parse_query_string();
|
||||
var autoLoadPage = function() {
|
||||
// get the vhost and port to set the default url.
|
||||
// for example: http://192.168.1.213/players/jwplayer6.html?port=1935&vhost=demo
|
||||
// for example: http://192.168.1.213/players/srs_player.html?port=1935&vhost=demo
|
||||
// url set to: rtmp://demo:1935/live/livestream
|
||||
srs_init_rtmp("#txt_url", null);
|
||||
|
||||
|
|
|
@ -24,8 +24,6 @@
|
|||
<li><a id="nav_srs_publisher" href="srs_publisher.html">SRS编码器</a></li>
|
||||
<li><a id="nav_srs_chat" href="srs_chat.html">SRS会议</a></li>
|
||||
<li><a id="nav_srs_bwt" href="srs_bwt.html">SRS测网速</a></li>
|
||||
<!--<li><a id="nav_jwplayer6" href="jwplayer6.html">JWPlayer6播放器</a></li>-->
|
||||
<!--<li><a id="nav_osmf" href="osmf.html">AdobeOSMF播放器</a></li>-->
|
||||
<li class="active"><a id="nav_vlc" href="vlc.html">VLC播放器</a></li>
|
||||
<li><a id="nav_gb28181" href="srs_gb28181.html">SRS-GB28181</a></li>
|
||||
</ul>
|
||||
|
|
|
@ -542,13 +542,11 @@ srs_error_t SrsUdpMuxListener::cycle()
|
|||
}
|
||||
// Use pithy print to show more smart information.
|
||||
if (err != srs_success) {
|
||||
if (pp_pkt_handler_err->can_print(err)) {
|
||||
uint32_t nn = 0;
|
||||
if (pp_pkt_handler_err->can_print(err, &nn)) {
|
||||
// Append more information.
|
||||
if (true) {
|
||||
char* data = skt.data(); int size = skt.size();
|
||||
err = srs_error_wrap(err, "size=%u, data=[%s]", size, srs_string_dumps_hex(data, size, 8).c_str());
|
||||
}
|
||||
srs_warn("handle udp pkt, count=%u, err: %s", pp_pkt_handler_err->nn_count, srs_error_desc(err).c_str());
|
||||
err = srs_error_wrap(err, "size=%u, data=[%s]", skt.size(), srs_string_dumps_hex(skt.data(), skt.size(), 8).c_str());
|
||||
srs_warn("handle udp pkt, count=%u/%u, err: %s", pp_pkt_handler_err->nn_count, nn, srs_error_desc(err).c_str());
|
||||
}
|
||||
srs_freep(err);
|
||||
}
|
||||
|
|
|
@ -31,11 +31,13 @@ using namespace std;
|
|||
#include <srs_kernel_error.hpp>
|
||||
#include <srs_kernel_utility.hpp>
|
||||
|
||||
SrsStageInfo::SrsStageInfo(int _stage_id)
|
||||
SrsStageInfo::SrsStageInfo(int _stage_id, double ratio)
|
||||
{
|
||||
stage_id = _stage_id;
|
||||
nb_clients = 0;
|
||||
age = 0;
|
||||
nn_count = 0;
|
||||
interval_ratio = ratio;
|
||||
|
||||
update_print_time();
|
||||
|
||||
|
@ -59,7 +61,7 @@ void SrsStageInfo::elapse(srs_utime_t diff)
|
|||
|
||||
bool SrsStageInfo::can_print()
|
||||
{
|
||||
srs_utime_t can_print_age = nb_clients * interval;
|
||||
srs_utime_t can_print_age = nb_clients * (srs_utime_t)(interval_ratio * interval);
|
||||
|
||||
bool can_print = age >= can_print_age;
|
||||
if (can_print) {
|
||||
|
@ -114,31 +116,39 @@ SrsStageInfo* SrsStageManager::fetch_or_create(int stage_id, bool* pnew)
|
|||
return stage;
|
||||
}
|
||||
|
||||
SrsErrorPithyPrint::SrsErrorPithyPrint()
|
||||
SrsErrorPithyPrint::SrsErrorPithyPrint(double ratio)
|
||||
{
|
||||
nn_count = 0;
|
||||
ratio_ = ratio;
|
||||
}
|
||||
|
||||
SrsErrorPithyPrint::~SrsErrorPithyPrint()
|
||||
{
|
||||
}
|
||||
|
||||
bool SrsErrorPithyPrint::can_print(srs_error_t err)
|
||||
bool SrsErrorPithyPrint::can_print(srs_error_t err, uint32_t* pnn)
|
||||
{
|
||||
int error_code = srs_error_code(err);
|
||||
return can_print(error_code);
|
||||
return can_print(error_code, pnn);
|
||||
}
|
||||
|
||||
bool SrsErrorPithyPrint::can_print(int error_code)
|
||||
bool SrsErrorPithyPrint::can_print(int error_code, uint32_t* pnn)
|
||||
{
|
||||
nn_count++;
|
||||
|
||||
bool new_stage = false;
|
||||
SrsStageInfo* stage = stages.fetch_or_create(error_code, &new_stage);
|
||||
|
||||
// Increase the count.
|
||||
stage->nn_count++;
|
||||
nn_count++;
|
||||
|
||||
if (pnn) {
|
||||
*pnn = stage->nn_count;
|
||||
}
|
||||
|
||||
// Always and only one client.
|
||||
if (new_stage) {
|
||||
stage->nb_clients = 1;
|
||||
stage->interval_ratio = ratio_;
|
||||
}
|
||||
|
||||
srs_utime_t tick = ticks[error_code];
|
||||
|
|
|
@ -37,10 +37,14 @@ public:
|
|||
int stage_id;
|
||||
srs_utime_t interval;
|
||||
int nb_clients;
|
||||
// The number of call of can_print().
|
||||
uint32_t nn_count;
|
||||
// The ratio for interval, 1.0 means no change.
|
||||
double interval_ratio;
|
||||
public:
|
||||
srs_utime_t age;
|
||||
public:
|
||||
SrsStageInfo(int _stage_id);
|
||||
SrsStageInfo(int _stage_id, double ratio = 1.0);
|
||||
virtual ~SrsStageInfo();
|
||||
virtual void update_print_time();
|
||||
public:
|
||||
|
@ -72,16 +76,17 @@ public:
|
|||
// The number of call of can_print().
|
||||
uint32_t nn_count;
|
||||
private:
|
||||
double ratio_;
|
||||
SrsStageManager stages;
|
||||
std::map<int, srs_utime_t> ticks;
|
||||
public:
|
||||
SrsErrorPithyPrint();
|
||||
SrsErrorPithyPrint(double ratio = 1.0);
|
||||
virtual ~SrsErrorPithyPrint();
|
||||
public:
|
||||
// Whether specified stage is ready for print.
|
||||
bool can_print(srs_error_t err);
|
||||
bool can_print(srs_error_t err, uint32_t* pnn = NULL);
|
||||
// We also support int error code.
|
||||
bool can_print(int err);
|
||||
bool can_print(int err, uint32_t* pnn = NULL);
|
||||
};
|
||||
|
||||
// The stage is used for a collection of object to do print,
|
||||
|
|
|
@ -135,10 +135,12 @@ srs_error_t SrsGoApiRtcPlay::do_serve_http(ISrsHttpResponseWriter* w, ISrsHttpMe
|
|||
// For client to specifies the EIP of server.
|
||||
string eip = r->query_get("eip");
|
||||
// For client to specifies whether encrypt by SRTP.
|
||||
string encrypt = r->query_get("encrypt");
|
||||
string srtp = r->query_get("encrypt");
|
||||
string dtls = r->query_get("dtls");
|
||||
|
||||
srs_trace("RTC play %s, api=%s, clientip=%s, app=%s, stream=%s, offer=%dB, eip=%s, encrypt=%s",
|
||||
streamurl.c_str(), api.c_str(), clientip.c_str(), app.c_str(), stream_name.c_str(), remote_sdp_str.length(), eip.c_str(), encrypt.c_str());
|
||||
srs_trace("RTC play %s, api=%s, clientip=%s, app=%s, stream=%s, offer=%dB, eip=%s, srtp=%s, dtls=%s",
|
||||
streamurl.c_str(), api.c_str(), clientip.c_str(), app.c_str(), stream_name.c_str(), remote_sdp_str.length(), eip.c_str(),
|
||||
srtp.c_str(), dtls.c_str());
|
||||
|
||||
// TODO: FIXME: It seems remote_sdp doesn't represents the full SDP information.
|
||||
SrsSdp remote_sdp;
|
||||
|
@ -178,15 +180,19 @@ srs_error_t SrsGoApiRtcPlay::do_serve_http(ISrsHttpResponseWriter* w, ISrsHttpMe
|
|||
server_enabled, rtc_enabled, request.vhost.c_str());
|
||||
}
|
||||
|
||||
bool srtp_enabled = true;
|
||||
if (srtp.empty()) {
|
||||
srtp_enabled = _srs_config->get_rtc_server_encrypt();
|
||||
} else {
|
||||
srtp_enabled = (srtp != "false");
|
||||
}
|
||||
|
||||
bool dtls_enabled = (dtls != "false");
|
||||
|
||||
// TODO: FIXME: When server enabled, but vhost disabled, should report error.
|
||||
SrsRtcConnection* session = NULL;
|
||||
if ((err = server_->create_session(&request, remote_sdp, local_sdp, eip, false, &session)) != srs_success) {
|
||||
return srs_error_wrap(err, "create session");
|
||||
}
|
||||
if (encrypt.empty()) {
|
||||
session->set_encrypt(_srs_config->get_rtc_server_encrypt());
|
||||
} else {
|
||||
session->set_encrypt(encrypt != "false");
|
||||
if ((err = server_->create_session(&request, remote_sdp, local_sdp, eip, false, dtls_enabled, srtp_enabled, &session)) != srs_success) {
|
||||
return srs_error_wrap(err, "create session, dtls=%u, srtp=%u, eip=%s", dtls_enabled, srtp_enabled, eip.c_str());
|
||||
}
|
||||
|
||||
ostringstream os;
|
||||
|
@ -206,8 +212,8 @@ srs_error_t SrsGoApiRtcPlay::do_serve_http(ISrsHttpResponseWriter* w, ISrsHttpMe
|
|||
res->set("sdp", SrsJsonAny::str(local_sdp_str.c_str()));
|
||||
res->set("sessionid", SrsJsonAny::str(session->username().c_str()));
|
||||
|
||||
srs_trace("RTC username=%s, offer=%dB, answer=%dB", session->username().c_str(),
|
||||
remote_sdp_str.length(), local_sdp_str.length());
|
||||
srs_trace("RTC username=%s, dtls=%u, srtp=%u, offer=%dB, answer=%dB", session->username().c_str(),
|
||||
dtls_enabled, srtp_enabled, remote_sdp_str.length(), local_sdp_str.length());
|
||||
srs_trace("RTC remote offer: %s", srs_string_replace(remote_sdp_str.c_str(), "\r\n", "\\r\\n").c_str());
|
||||
srs_trace("RTC local answer: %s", local_sdp_str.c_str());
|
||||
|
||||
|
@ -537,7 +543,7 @@ srs_error_t SrsGoApiRtcPublish::do_serve_http(ISrsHttpResponseWriter* w, ISrsHtt
|
|||
|
||||
// TODO: FIXME: When server enabled, but vhost disabled, should report error.
|
||||
SrsRtcConnection* session = NULL;
|
||||
if ((err = server_->create_session(&request, remote_sdp, local_sdp, eip, true, &session)) != srs_success) {
|
||||
if ((err = server_->create_session(&request, remote_sdp, local_sdp, eip, true, true, true, &session)) != srs_success) {
|
||||
return srs_error_wrap(err, "create session");
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -72,12 +72,6 @@ const uint8_t kRtpFb = 205;
|
|||
const uint8_t kPsFb = 206;
|
||||
const uint8_t kXR = 207;
|
||||
|
||||
// @see: https://tools.ietf.org/html/rfc4585#section-6.3
|
||||
const uint8_t kPLI = 1;
|
||||
const uint8_t kSLI = 2;
|
||||
const uint8_t kRPSI = 3;
|
||||
const uint8_t kAFB = 15;
|
||||
|
||||
enum SrsRtcConnectionStateType
|
||||
{
|
||||
// TODO: FIXME: Should prefixed by enum name.
|
||||
|
@ -89,8 +83,26 @@ enum SrsRtcConnectionStateType
|
|||
CLOSED = 5,
|
||||
};
|
||||
|
||||
// The transport for RTC connection.
|
||||
class ISrsRtcTransport : public ISrsDtlsCallback
|
||||
{
|
||||
public:
|
||||
ISrsRtcTransport();
|
||||
virtual ~ISrsRtcTransport();
|
||||
public:
|
||||
virtual srs_error_t initialize(SrsSessionConfig* cfg) = 0;
|
||||
virtual srs_error_t start_active_handshake() = 0;
|
||||
virtual srs_error_t on_dtls(char* data, int nb_data) = 0;
|
||||
public:
|
||||
virtual srs_error_t protect_rtp(const char* plaintext, char* cipher, int& nb_cipher) = 0;
|
||||
virtual srs_error_t protect_rtcp(const char* plaintext, char* cipher, int& nb_cipher) = 0;
|
||||
virtual srs_error_t protect_rtp2(void* rtp_hdr, int* len_ptr) = 0;
|
||||
virtual srs_error_t unprotect_rtp(const char* cipher, char* plaintext, int& nb_plaintext) = 0;
|
||||
virtual srs_error_t unprotect_rtcp(const char* cipher, char* plaintext, int& nb_plaintext) = 0;
|
||||
};
|
||||
|
||||
// The security transport, use DTLS/SRTP to protect the data.
|
||||
class SrsSecurityTransport : public ISrsDtlsCallback
|
||||
class SrsSecurityTransport : public ISrsRtcTransport
|
||||
{
|
||||
private:
|
||||
SrsRtcConnection* session_;
|
||||
|
@ -128,6 +140,41 @@ private:
|
|||
srs_error_t srtp_initialize();
|
||||
};
|
||||
|
||||
// Semi security transport, setup DTLS and SRTP, with SRTP decrypt, without SRTP encrypt.
|
||||
class SrsSemiSecurityTransport : public SrsSecurityTransport
|
||||
{
|
||||
public:
|
||||
SrsSemiSecurityTransport(SrsRtcConnection* s);
|
||||
virtual ~SrsSemiSecurityTransport();
|
||||
public:
|
||||
virtual srs_error_t protect_rtp(const char* plaintext, char* cipher, int& nb_cipher);
|
||||
virtual srs_error_t protect_rtcp(const char* plaintext, char* cipher, int& nb_cipher);
|
||||
virtual srs_error_t protect_rtp2(void* rtp_hdr, int* len_ptr);
|
||||
};
|
||||
|
||||
// Plaintext transport, without DTLS or SRTP.
|
||||
class SrsPlaintextTransport : public ISrsRtcTransport
|
||||
{
|
||||
private:
|
||||
SrsRtcConnection* session_;
|
||||
public:
|
||||
SrsPlaintextTransport(SrsRtcConnection* s);
|
||||
virtual ~SrsPlaintextTransport();
|
||||
public:
|
||||
virtual srs_error_t initialize(SrsSessionConfig* cfg);
|
||||
virtual srs_error_t start_active_handshake();
|
||||
virtual srs_error_t on_dtls(char* data, int nb_data);
|
||||
virtual srs_error_t on_dtls_handshake_done();
|
||||
virtual srs_error_t on_dtls_application_data(const char* data, const int len);
|
||||
virtual srs_error_t write_dtls_data(void* data, int size);
|
||||
public:
|
||||
virtual srs_error_t protect_rtp(const char* plaintext, char* cipher, int& nb_cipher);
|
||||
virtual srs_error_t protect_rtcp(const char* plaintext, char* cipher, int& nb_cipher);
|
||||
virtual srs_error_t protect_rtp2(void* rtp_hdr, int* len_ptr);
|
||||
virtual srs_error_t unprotect_rtp(const char* cipher, char* plaintext, int& nb_plaintext);
|
||||
virtual srs_error_t unprotect_rtcp(const char* cipher, char* plaintext, int& nb_plaintext);
|
||||
};
|
||||
|
||||
// A group of RTP packets for outgoing(send to players).
|
||||
class SrsRtcPlayStreamStatistic
|
||||
{
|
||||
|
@ -168,6 +215,8 @@ private:
|
|||
SrsCoroutine* trd;
|
||||
SrsRtcConnection* session_;
|
||||
private:
|
||||
SrsRequest* req_;
|
||||
SrsRtcStream* source_;
|
||||
SrsHourGlass* timer_;
|
||||
// key: publish_ssrc, value: send track to process rtp/rtcp
|
||||
std::map<uint32_t, SrsRtcAudioSendTrack*> audio_tracks_;
|
||||
|
@ -201,21 +250,20 @@ public:
|
|||
virtual srs_error_t cycle();
|
||||
private:
|
||||
srs_error_t send_packets(SrsRtcStream* source, const std::vector<SrsRtpPacket2*>& pkts, SrsRtcPlayStreamStatistic& info);
|
||||
public:
|
||||
void nack_fetch(std::vector<SrsRtpPacket2*>& pkts, uint32_t ssrc, uint16_t seq);
|
||||
public:
|
||||
// Directly set the status of track, generally for init to set the default value.
|
||||
void set_all_tracks_status(bool status);
|
||||
// interface ISrsHourGlass
|
||||
public:
|
||||
virtual srs_error_t notify(int type, srs_utime_t interval, srs_utime_t tick);
|
||||
public:
|
||||
srs_error_t on_rtcp(char* data, int nb_data);
|
||||
srs_error_t on_rtcp(SrsRtcpCommon* rtcp);
|
||||
private:
|
||||
srs_error_t on_rtcp_sr(char* buf, int nb_buf);
|
||||
srs_error_t on_rtcp_xr(char* buf, int nb_buf);
|
||||
srs_error_t on_rtcp_feedback(char* data, int nb_data);
|
||||
srs_error_t on_rtcp_ps_feedback(char* data, int nb_data);
|
||||
srs_error_t on_rtcp_rr(char* data, int nb_data);
|
||||
srs_error_t on_rtcp_xr(SrsRtcpXr* rtcp);
|
||||
srs_error_t on_rtcp_nack(SrsRtcpNack* rtcp);
|
||||
srs_error_t on_rtcp_ps_feedback(SrsRtcpPsfbCommon* rtcp);
|
||||
srs_error_t on_rtcp_rr(SrsRtcpRR* rtcp);
|
||||
uint32_t get_video_publish_ssrc(uint32_t play_ssrc);
|
||||
};
|
||||
|
||||
|
@ -267,15 +315,13 @@ public:
|
|||
private:
|
||||
srs_error_t send_periodic_twcc();
|
||||
public:
|
||||
srs_error_t on_rtcp(char* data, int nb_data);
|
||||
srs_error_t on_rtcp(SrsRtcpCommon* rtcp);
|
||||
private:
|
||||
srs_error_t on_rtcp_sr(char* buf, int nb_buf);
|
||||
srs_error_t on_rtcp_xr(char* buf, int nb_buf);
|
||||
srs_error_t on_rtcp_feedback(char* data, int nb_data);
|
||||
srs_error_t on_rtcp_ps_feedback(char* data, int nb_data);
|
||||
srs_error_t on_rtcp_rr(char* data, int nb_data);
|
||||
srs_error_t on_rtcp_sr(SrsRtcpSR* rtcp);
|
||||
srs_error_t on_rtcp_xr(SrsRtcpXr* rtcp);
|
||||
public:
|
||||
void request_keyframe(uint32_t ssrc);
|
||||
void on_consumers_finished();
|
||||
// interface ISrsHourGlass
|
||||
public:
|
||||
virtual srs_error_t notify(int type, srs_utime_t interval, srs_utime_t tick);
|
||||
|
@ -309,6 +355,18 @@ public:
|
|||
std::string summary();
|
||||
};
|
||||
|
||||
// Callback for RTC connection.
|
||||
class ISrsRtcConnectionHijacker
|
||||
{
|
||||
public:
|
||||
ISrsRtcConnectionHijacker();
|
||||
virtual ~ISrsRtcConnectionHijacker();
|
||||
public:
|
||||
virtual srs_error_t on_dtls_done() = 0;
|
||||
// Notify when all consumers of publisher(specified by url) is finished.
|
||||
virtual void on_consumers_finished(std::string url) = 0;
|
||||
};
|
||||
|
||||
// A RTC Peer Connection, SDP level object.
|
||||
class SrsRtcConnection : virtual public ISrsHourGlass
|
||||
{
|
||||
|
@ -318,14 +376,21 @@ class SrsRtcConnection : virtual public ISrsHourGlass
|
|||
public:
|
||||
bool disposing_;
|
||||
SrsRtcConnectionStatistic* stat_;
|
||||
ISrsRtcConnectionHijacker* hijacker_;
|
||||
private:
|
||||
SrsRtcServer* server_;
|
||||
SrsRtcConnectionStateType state_;
|
||||
SrsSecurityTransport* transport_;
|
||||
SrsRtcPlayStream* player_;
|
||||
SrsRtcPublishStream* publisher_;
|
||||
bool is_publisher_;
|
||||
ISrsRtcTransport* transport_;
|
||||
SrsHourGlass* timer_;
|
||||
private:
|
||||
// key: stream id
|
||||
std::map<std::string, SrsRtcPlayStream*> players_;
|
||||
//key: player track's ssrc
|
||||
std::map<uint32_t, SrsRtcPlayStream*> players_ssrc_map_;
|
||||
// key: stream id
|
||||
std::map<std::string, SrsRtcPublishStream*> publishers_;
|
||||
// key: publisher track's ssrc
|
||||
std::map<uint32_t, SrsRtcPublishStream*> publishers_ssrc_map_;
|
||||
private:
|
||||
// The local:remote username, such as m5x0n128:jvOm where local name is m5x0n128.
|
||||
std::string username_;
|
||||
|
@ -340,13 +405,8 @@ private:
|
|||
private:
|
||||
// For each RTC session, we use a specified cid for debugging logs.
|
||||
SrsContextId cid;
|
||||
// For each RTC session, whether requires encrypt.
|
||||
// Read config value, rtc_server.encrypt, default to on.
|
||||
// Sepcifies by HTTP API, query encrypt, optional.
|
||||
// TODO: FIXME: Support reload.
|
||||
bool encrypt;
|
||||
// TODO: FIXME: Rename to req_.
|
||||
SrsRequest* req;
|
||||
SrsRtcStream* source_;
|
||||
SrsSdp remote_sdp;
|
||||
SrsSdp local_sdp;
|
||||
private:
|
||||
|
@ -373,7 +433,6 @@ public:
|
|||
// Get all addresses client used.
|
||||
std::vector<SrsUdpMuxSocket*> peer_addresses();
|
||||
public:
|
||||
void set_encrypt(bool v);
|
||||
void switch_to_context();
|
||||
SrsContextId context_id();
|
||||
public:
|
||||
|
@ -383,17 +442,24 @@ public:
|
|||
srs_error_t add_player2(SrsRequest* request, SrsSdp& local_sdp);
|
||||
public:
|
||||
// Before initialize, user must set the local SDP, which is used to inititlize DTLS.
|
||||
srs_error_t initialize(SrsRtcStream* source, SrsRequest* r, bool is_publisher, std::string username);
|
||||
srs_error_t initialize(SrsRequest* r, bool dtls, bool srtp, std::string username);
|
||||
// The peer address may change, we can identify that by STUN messages.
|
||||
srs_error_t on_stun(SrsUdpMuxSocket* skt, SrsStunPacket* r);
|
||||
srs_error_t on_dtls(char* data, int nb_data);
|
||||
srs_error_t on_rtp(char* data, int nb_data);
|
||||
srs_error_t on_rtcp(char* data, int nb_data);
|
||||
srs_error_t on_rtcp_feedback(char* buf, int nb_buf);
|
||||
private:
|
||||
srs_error_t dispatch_rtcp(SrsRtcpCommon* rtcp);
|
||||
public:
|
||||
srs_error_t on_rtcp_feedback_twcc(char* buf, int nb_buf);
|
||||
srs_error_t on_rtcp_feedback_remb(SrsRtcpPsfbCommon *rtcp);
|
||||
public:
|
||||
void on_consumers_finished(std::string url);
|
||||
void set_hijacker(ISrsRtcConnectionHijacker* h);
|
||||
public:
|
||||
srs_error_t on_connection_established();
|
||||
srs_error_t start_play();
|
||||
srs_error_t start_publish();
|
||||
srs_error_t start_play(std::string stream_uri);
|
||||
srs_error_t start_publish(std::string stream_uri);
|
||||
bool is_stun_timeout();
|
||||
void update_sendonly_socket(SrsUdpMuxSocket* skt);
|
||||
// interface ISrsHourGlass
|
||||
|
@ -401,6 +467,7 @@ public:
|
|||
virtual srs_error_t notify(int type, srs_utime_t interval, srs_utime_t tick);
|
||||
public:
|
||||
// send rtcp
|
||||
srs_error_t send_rtcp(char *data, int nb_data);
|
||||
void check_send_nacks(SrsRtpNackForReceiver* nack, uint32_t ssrc, uint32_t& sent_nacks);
|
||||
srs_error_t send_rtcp_rr(uint32_t ssrc, SrsRtpRingBuffer* rtp_queue, const uint64_t& last_send_systime, const SrsNtp& last_send_ntp);
|
||||
srs_error_t send_rtcp_xr_rrtr(uint32_t ssrc);
|
||||
|
@ -411,14 +478,16 @@ public:
|
|||
void simulate_player_drop_packet(SrsRtpHeader* h, int nn_bytes);
|
||||
srs_error_t do_send_packets(const std::vector<SrsRtpPacket2*>& pkts, SrsRtcPlayStreamStatistic& info);
|
||||
// Directly set the status of play track, generally for init to set the default value.
|
||||
void set_all_tracks_status(bool status);
|
||||
void set_all_tracks_status(std::string stream_uri, bool is_publish, bool status);
|
||||
private:
|
||||
srs_error_t on_binding_request(SrsStunPacket* r);
|
||||
// publish media capabilitiy negotiate
|
||||
srs_error_t negotiate_publish_capability(SrsRequest* req, const SrsSdp& remote_sdp, SrsRtcStreamDescription* stream_desc);
|
||||
srs_error_t generate_publish_local_sdp(SrsRequest* req, SrsSdp& local_sdp, SrsRtcStreamDescription* stream_desc);
|
||||
// play media capabilitiy negotiate
|
||||
//TODO: Use StreamDescription to negotiate and remove first negotiate_play_capability function
|
||||
srs_error_t negotiate_play_capability(SrsRequest* req, const SrsSdp& remote_sdp, std::map<uint32_t, SrsRtcTrackDescription*>& sub_relations);
|
||||
srs_error_t negotiate_play_capability(SrsRequest* req, SrsRtcStreamDescription* req_stream_desc, std::map<uint32_t, SrsRtcTrackDescription*>& sub_relations);
|
||||
srs_error_t fetch_source_capability(SrsRequest* req, std::map<uint32_t, SrsRtcTrackDescription*>& sub_relations);
|
||||
srs_error_t generate_play_local_sdp(SrsRequest* req, SrsSdp& local_sdp, SrsRtcStreamDescription* stream_desc);
|
||||
srs_error_t create_player(SrsRequest* request, std::map<uint32_t, SrsRtcTrackDescription*> sub_relations);
|
||||
|
@ -431,10 +500,18 @@ public:
|
|||
ISrsRtcHijacker();
|
||||
virtual ~ISrsRtcHijacker();
|
||||
public:
|
||||
// When start publisher by RTC.
|
||||
// Initialize the hijacker.
|
||||
virtual srs_error_t initialize() = 0;
|
||||
// When create publisher, SDP is done, DTLS is not ready.
|
||||
virtual srs_error_t on_create_publish(SrsRtcConnection* session, SrsRtcPublishStream* publisher, SrsRequest* req) = 0;
|
||||
// When start publisher by RTC, SDP and DTLS are done.
|
||||
virtual srs_error_t on_start_publish(SrsRtcConnection* session, SrsRtcPublishStream* publisher, SrsRequest* req) = 0;
|
||||
// When stop publish by RTC.
|
||||
virtual void on_stop_publish(SrsRtcConnection* session, SrsRtcPublishStream* publisher, SrsRequest* req) = 0;
|
||||
// When got RTP plaintext packet.
|
||||
virtual srs_error_t on_rtp_packet(SrsRtcConnection* session, SrsRtcPublishStream* publisher, SrsRequest* req, SrsRtpPacket2* pkt) = 0;
|
||||
// When before play by RTC. (wait source to ready in cascade scenario)
|
||||
virtual srs_error_t on_before_play(SrsRtcConnection* session, SrsRequest* req) = 0;
|
||||
// When start player by RTC.
|
||||
virtual srs_error_t on_start_play(SrsRtcConnection* session, SrsRtcPlayStream* player, SrsRequest* req) = 0;
|
||||
// When start consuming for player for RTC.
|
||||
|
|
|
@ -32,6 +32,8 @@ using namespace std;
|
|||
#include <srs_app_config.hpp>
|
||||
#include <srs_core_autofree.hpp>
|
||||
#include <srs_rtmp_stack.hpp>
|
||||
#include <srs_app_utility.hpp>
|
||||
#include <srs_kernel_rtc_rtp.hpp>
|
||||
|
||||
#include <srtp2/srtp.h>
|
||||
#include <openssl/ssl.h>
|
||||
|
@ -45,15 +47,83 @@ using namespace std;
|
|||
// can however retrieve the error code of the last verification error using SSL_get_verify_result(3) or by maintaining
|
||||
// its own error storage managed by verify_callback.
|
||||
// @see https://www.openssl.org/docs/man1.0.2/man3/SSL_CTX_set_verify.html
|
||||
static int verify_callback(int preverify_ok, X509_STORE_CTX *ctx)
|
||||
int srs_verify_callback(int preverify_ok, X509_STORE_CTX *ctx)
|
||||
{
|
||||
// Always OK, we don't check the certificate of client,
|
||||
// because we allow client self-sign certificate.
|
||||
return 1;
|
||||
}
|
||||
|
||||
SSL_CTX* srs_build_dtls_ctx(SrsDtlsVersion version)
|
||||
{
|
||||
SSL_CTX* dtls_ctx;
|
||||
#if OPENSSL_VERSION_NUMBER < 0x10002000L // v1.0.2
|
||||
dtls_ctx = SSL_CTX_new(DTLSv1_method());
|
||||
#else
|
||||
if (version == SrsDtlsVersion1_0) {
|
||||
dtls_ctx = SSL_CTX_new(DTLSv1_method());
|
||||
} else if (version == SrsDtlsVersion1_2) {
|
||||
dtls_ctx = SSL_CTX_new(DTLSv1_2_method());
|
||||
} else {
|
||||
// SrsDtlsVersionAuto, use version-flexible DTLS methods
|
||||
dtls_ctx = SSL_CTX_new(DTLS_method());
|
||||
}
|
||||
#endif
|
||||
|
||||
if (_srs_rtc_dtls_certificate->is_ecdsa()) { // By ECDSA, https://stackoverflow.com/a/6006898
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10002000L // v1.0.2
|
||||
// For ECDSA, we could set the curves list.
|
||||
// @see https://www.openssl.org/docs/man1.0.2/man3/SSL_CTX_set1_curves_list.html
|
||||
SSL_CTX_set1_curves_list(dtls_ctx, "P-521:P-384:P-256");
|
||||
#endif
|
||||
|
||||
// For openssl <1.1, we must set the ECDH manually.
|
||||
// @see https://stackoverrun.com/cn/q/10791887
|
||||
#if OPENSSL_VERSION_NUMBER < 0x10100000L // v1.1.x
|
||||
#if OPENSSL_VERSION_NUMBER < 0x10002000L // v1.0.2
|
||||
SSL_CTX_set_tmp_ecdh(dtls_ctx, _srs_rtc_dtls_certificate->get_ecdsa_key());
|
||||
#else
|
||||
SSL_CTX_set_ecdh_auto(dtls_ctx, 1);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
// Setup DTLS context.
|
||||
if (true) {
|
||||
// We use "ALL", while you can use "DEFAULT" means "ALL:!EXPORT:!LOW:!aNULL:!eNULL:!SSLv2"
|
||||
// @see https://www.openssl.org/docs/man1.0.2/man1/ciphers.html
|
||||
srs_assert(SSL_CTX_set_cipher_list(dtls_ctx, "ALL") == 1);
|
||||
|
||||
// Setup the certificate.
|
||||
srs_assert(SSL_CTX_use_certificate(dtls_ctx, _srs_rtc_dtls_certificate->get_cert()) == 1);
|
||||
srs_assert(SSL_CTX_use_PrivateKey(dtls_ctx, _srs_rtc_dtls_certificate->get_public_key()) == 1);
|
||||
|
||||
// Server will send Certificate Request.
|
||||
// @see https://www.openssl.org/docs/man1.0.2/man3/SSL_CTX_set_verify.html
|
||||
// TODO: FIXME: Config it, default to off to make the packet smaller.
|
||||
SSL_CTX_set_verify(dtls_ctx, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, srs_verify_callback);
|
||||
// The depth count is "level 0:peer certificate", "level 1: CA certificate",
|
||||
// "level 2: higher level CA certificate", and so on.
|
||||
// @see https://www.openssl.org/docs/man1.0.2/man3/SSL_CTX_set_verify.html
|
||||
SSL_CTX_set_verify_depth(dtls_ctx, 4);
|
||||
|
||||
// Whether we should read as many input bytes as possible (for non-blocking reads) or not.
|
||||
// @see https://www.openssl.org/docs/man1.0.2/man3/SSL_CTX_set_read_ahead.html
|
||||
SSL_CTX_set_read_ahead(dtls_ctx, 1);
|
||||
|
||||
// TODO: Maybe we can use SRTP-GCM in future.
|
||||
// @see https://bugs.chromium.org/p/chromium/issues/detail?id=713701
|
||||
// @see https://groups.google.com/forum/#!topic/discuss-webrtc/PvCbWSetVAQ
|
||||
// @remark Only support SRTP_AES128_CM_SHA1_80, please read ssl/d1_srtp.c
|
||||
srs_assert(SSL_CTX_set_tlsext_use_srtp(dtls_ctx, "SRTP_AES128_CM_SHA1_80") == 0);
|
||||
}
|
||||
|
||||
return dtls_ctx;
|
||||
}
|
||||
|
||||
SrsDtlsCertificate::SrsDtlsCertificate()
|
||||
{
|
||||
ecdsa_mode = true;
|
||||
dtls_cert = NULL;
|
||||
dtls_pkey = NULL;
|
||||
eckey = NULL;
|
||||
|
@ -242,20 +312,28 @@ ISrsDtlsCallback::~ISrsDtlsCallback()
|
|||
{
|
||||
}
|
||||
|
||||
SrsDtls::SrsDtls(ISrsDtlsCallback* cb)
|
||||
SrsDtls::SrsDtls(ISrsDtlsCallback* callback)
|
||||
{
|
||||
dtls_ctx = NULL;
|
||||
dtls = NULL;
|
||||
|
||||
callback = cb;
|
||||
handshake_done = false;
|
||||
callback_ = callback;
|
||||
handshake_done_for_us = false;
|
||||
|
||||
last_outgoing_packet_cache = new uint8_t[kRtpPacketSize];
|
||||
nn_last_outgoing_packet = 0;
|
||||
|
||||
role_ = SrsDtlsRoleServer;
|
||||
version_ = SrsDtlsVersionAuto;
|
||||
|
||||
trd = NULL;
|
||||
state_ = SrsDtlsStateInit;
|
||||
}
|
||||
|
||||
SrsDtls::~SrsDtls()
|
||||
{
|
||||
srs_freep(trd);
|
||||
|
||||
if (dtls_ctx) {
|
||||
SSL_CTX_free(dtls_ctx);
|
||||
dtls_ctx = NULL;
|
||||
|
@ -266,73 +344,8 @@ SrsDtls::~SrsDtls()
|
|||
SSL_free(dtls);
|
||||
dtls = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
SSL_CTX* SrsDtls::build_dtls_ctx()
|
||||
{
|
||||
SSL_CTX* dtls_ctx;
|
||||
#if OPENSSL_VERSION_NUMBER < 0x10002000L // v1.0.2
|
||||
dtls_ctx = SSL_CTX_new(DTLSv1_method());
|
||||
#else
|
||||
if (version_ == SrsDtlsVersion1_0) {
|
||||
dtls_ctx = SSL_CTX_new(DTLSv1_method());
|
||||
} else if (version_ == SrsDtlsVersion1_2) {
|
||||
dtls_ctx = SSL_CTX_new(DTLSv1_2_method());
|
||||
} else {
|
||||
// SrsDtlsVersionAuto, use version-flexible DTLS methods
|
||||
dtls_ctx = SSL_CTX_new(DTLS_method());
|
||||
}
|
||||
#endif
|
||||
|
||||
if (_srs_rtc_dtls_certificate->is_ecdsa()) { // By ECDSA, https://stackoverflow.com/a/6006898
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10002000L // v1.0.2
|
||||
// For ECDSA, we could set the curves list.
|
||||
// @see https://www.openssl.org/docs/man1.0.2/man3/SSL_CTX_set1_curves_list.html
|
||||
SSL_CTX_set1_curves_list(dtls_ctx, "P-521:P-384:P-256");
|
||||
#endif
|
||||
|
||||
// For openssl <1.1, we must set the ECDH manually.
|
||||
// @see https://stackoverrun.com/cn/q/10791887
|
||||
#if OPENSSL_VERSION_NUMBER < 0x10100000L // v1.1.x
|
||||
#if OPENSSL_VERSION_NUMBER < 0x10002000L // v1.0.2
|
||||
SSL_CTX_set_tmp_ecdh(dtls_ctx, _srs_rtc_dtls_certificate->get_ecdsa_key());
|
||||
#else
|
||||
SSL_CTX_set_ecdh_auto(dtls_ctx, 1);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
// Setup DTLS context.
|
||||
if (true) {
|
||||
// We use "ALL", while you can use "DEFAULT" means "ALL:!EXPORT:!LOW:!aNULL:!eNULL:!SSLv2"
|
||||
// @see https://www.openssl.org/docs/man1.0.2/man1/ciphers.html
|
||||
srs_assert(SSL_CTX_set_cipher_list(dtls_ctx, "ALL") == 1);
|
||||
|
||||
// Setup the certificate.
|
||||
srs_assert(SSL_CTX_use_certificate(dtls_ctx, _srs_rtc_dtls_certificate->get_cert()) == 1);
|
||||
srs_assert(SSL_CTX_use_PrivateKey(dtls_ctx, _srs_rtc_dtls_certificate->get_public_key()) == 1);
|
||||
|
||||
// Server will send Certificate Request.
|
||||
// @see https://www.openssl.org/docs/man1.0.2/man3/SSL_CTX_set_verify.html
|
||||
// TODO: FIXME: Config it, default to off to make the packet smaller.
|
||||
SSL_CTX_set_verify(dtls_ctx, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, verify_callback);
|
||||
// The depth count is "level 0:peer certificate", "level 1: CA certificate",
|
||||
// "level 2: higher level CA certificate", and so on.
|
||||
// @see https://www.openssl.org/docs/man1.0.2/man3/SSL_CTX_set_verify.html
|
||||
SSL_CTX_set_verify_depth(dtls_ctx, 4);
|
||||
|
||||
// Whether we should read as many input bytes as possible (for non-blocking reads) or not.
|
||||
// @see https://www.openssl.org/docs/man1.0.2/man3/SSL_CTX_set_read_ahead.html
|
||||
SSL_CTX_set_read_ahead(dtls_ctx, 1);
|
||||
|
||||
// TODO: Maybe we can use SRTP-GCM in future.
|
||||
// @see https://bugs.chromium.org/p/chromium/issues/detail?id=713701
|
||||
// @see https://groups.google.com/forum/#!topic/discuss-webrtc/PvCbWSetVAQ
|
||||
// @remark Only support SRTP_AES128_CM_SHA1_80, please read ssl/d1_srtp.c
|
||||
srs_assert(SSL_CTX_set_tlsext_use_srtp(dtls_ctx, "SRTP_AES128_CM_SHA1_80") == 0);
|
||||
}
|
||||
|
||||
return dtls_ctx;
|
||||
srs_freepa(last_outgoing_packet_cache);
|
||||
}
|
||||
|
||||
srs_error_t SrsDtls::initialize(std::string role, std::string version)
|
||||
|
@ -352,17 +365,16 @@ srs_error_t SrsDtls::initialize(std::string role, std::string version)
|
|||
version_ = SrsDtlsVersionAuto;
|
||||
}
|
||||
|
||||
dtls_ctx = build_dtls_ctx();
|
||||
dtls_ctx = srs_build_dtls_ctx(version_);
|
||||
|
||||
// TODO: FIXME: Leak for SSL_CTX* return by build_dtls_ctx.
|
||||
if ((dtls = SSL_new(dtls_ctx)) == NULL) {
|
||||
return srs_error_new(ERROR_OpenSslCreateSSL, "SSL_new dtls");
|
||||
}
|
||||
|
||||
if (role == "active") {
|
||||
if (role_ == SrsDtlsRoleClient) {
|
||||
// Dtls setup active, as client role.
|
||||
SSL_set_connect_state(dtls);
|
||||
SSL_set_max_send_fragment(dtls, 1500);
|
||||
SSL_set_max_send_fragment(dtls, kRtpPacketSize);
|
||||
} else {
|
||||
// Dtls setup passive, as server role.
|
||||
SSL_set_accept_state(dtls);
|
||||
|
@ -382,42 +394,12 @@ srs_error_t SrsDtls::initialize(std::string role, std::string version)
|
|||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsDtls::do_handshake()
|
||||
srs_error_t SrsDtls::start_active_handshake()
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
int ret = SSL_do_handshake(dtls);
|
||||
|
||||
unsigned char *out_bio_data;
|
||||
int out_bio_len = BIO_get_mem_data(bio_out, &out_bio_data);
|
||||
|
||||
int ssl_err = SSL_get_error(dtls, ret);
|
||||
switch(ssl_err) {
|
||||
case SSL_ERROR_NONE: {
|
||||
handshake_done = true;
|
||||
if (((err = callback->on_dtls_handshake_done()) != srs_success)) {
|
||||
return srs_error_wrap(err, "dtls done");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case SSL_ERROR_WANT_READ: {
|
||||
break;
|
||||
}
|
||||
|
||||
case SSL_ERROR_WANT_WRITE: {
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (out_bio_len) {
|
||||
if ((err = callback->write_dtls_data(out_bio_data, out_bio_len)) != srs_success) {
|
||||
return srs_error_wrap(err, "dtls send size=%u", out_bio_len);
|
||||
}
|
||||
if (role_ == SrsDtlsRoleClient) {
|
||||
return do_handshake();
|
||||
}
|
||||
|
||||
return err;
|
||||
|
@ -426,43 +408,248 @@ srs_error_t SrsDtls::do_handshake()
|
|||
srs_error_t SrsDtls::on_dtls(char* data, int nb_data)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
if (BIO_reset(bio_in) != 1) {
|
||||
return srs_error_new(ERROR_OpenSslBIOReset, "BIO_reset");
|
||||
}
|
||||
if (BIO_reset(bio_out) != 1) {
|
||||
return srs_error_new(ERROR_OpenSslBIOReset, "BIO_reset");
|
||||
|
||||
// When got packet, stop the ARQ if server in the first ARQ state SrsDtlsStateServerHello.
|
||||
// @note But for ARQ state, we should never stop the ARQ, for example, we are in the second ARQ sate
|
||||
// SrsDtlsStateServerDone, but we got previous late wrong packet ServeHello, which is not the expect
|
||||
// packet SessionNewTicket, we should never stop the ARQ thread.
|
||||
if (role_ == SrsDtlsRoleClient && state_ == SrsDtlsStateServerHello) {
|
||||
stop_arq();
|
||||
}
|
||||
|
||||
if (BIO_write(bio_in, data, nb_data) <= 0) {
|
||||
if ((err = do_on_dtls(data, nb_data)) != srs_success) {
|
||||
return srs_error_wrap(err, "on_dtls size=%u, data=[%s]", nb_data,
|
||||
srs_string_dumps_hex(data, nb_data, 32).c_str());
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsDtls::do_on_dtls(char* data, int nb_data)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
int r0 = 0;
|
||||
if ((r0 = BIO_reset(bio_in)) != 1) {
|
||||
return srs_error_new(ERROR_OpenSslBIOReset, "BIO_reset r0=%d", r0);
|
||||
}
|
||||
if ((r0 = BIO_reset(bio_out)) != 1) {
|
||||
return srs_error_new(ERROR_OpenSslBIOReset, "BIO_reset r0=%d", r0);
|
||||
}
|
||||
|
||||
// Trace the detail of DTLS packet.
|
||||
state_trace((uint8_t*)data, nb_data, true, r0, SSL_ERROR_NONE, false, false);
|
||||
|
||||
if ((r0 = BIO_write(bio_in, data, nb_data)) <= 0) {
|
||||
// TODO: 0 or -1 maybe block, use BIO_should_retry to check.
|
||||
return srs_error_new(ERROR_OpenSslBIOWrite, "BIO_write");
|
||||
return srs_error_new(ERROR_OpenSslBIOWrite, "BIO_write r0=%d", r0);
|
||||
}
|
||||
|
||||
// Always do handshake, even the handshake is done, because the last DTLS packet maybe dropped,
|
||||
// so we thought the DTLS is done, but client need us to retransmit the last packet.
|
||||
if ((err = do_handshake()) != srs_success) {
|
||||
return srs_error_wrap(err, "do handshake");
|
||||
}
|
||||
|
||||
if (!handshake_done) {
|
||||
err = do_handshake();
|
||||
} else {
|
||||
while (BIO_ctrl_pending(bio_in) > 0) {
|
||||
char dtls_read_buf[8092];
|
||||
int nb = SSL_read(dtls, dtls_read_buf, sizeof(dtls_read_buf));
|
||||
char buf[8092];
|
||||
int nb = SSL_read(dtls, buf, sizeof(buf));
|
||||
if (nb <= 0) {
|
||||
continue;
|
||||
}
|
||||
srs_trace("DTLS: read nb=%d, data=[%s]", nb, srs_string_dumps_hex(buf, nb, 32).c_str());
|
||||
|
||||
if (nb > 0 && callback) {
|
||||
if ((err = callback->on_dtls_application_data(dtls_read_buf, nb)) != srs_success) {
|
||||
return srs_error_wrap(err, "on DTLS data, size=%u", nb);
|
||||
}
|
||||
}
|
||||
if ((err = callback_->on_dtls_application_data(buf, nb)) != srs_success) {
|
||||
return srs_error_wrap(err, "on DTLS data, size=%u, data=[%s]", nb,
|
||||
srs_string_dumps_hex(buf, nb, 32).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsDtls::start_active_handshake()
|
||||
srs_error_t SrsDtls::do_handshake()
|
||||
{
|
||||
if (role_ == SrsDtlsRoleClient) {
|
||||
return do_handshake();
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
// Do handshake and get the result.
|
||||
int r0 = SSL_do_handshake(dtls);
|
||||
int r1 = SSL_get_error(dtls, r0);
|
||||
|
||||
// Fatal SSL error, for example, no available suite when peer is DTLS 1.0 while we are DTLS 1.2.
|
||||
if (r0 < 0 && (r1 != SSL_ERROR_NONE && r1 != SSL_ERROR_WANT_READ && r1 != SSL_ERROR_WANT_WRITE)) {
|
||||
return srs_error_new(ERROR_RTC_DTLS, "handshake r0=%d, r1=%d", r0, r1);
|
||||
}
|
||||
|
||||
return srs_success;
|
||||
// OK, Handshake is done, note that it maybe done many times.
|
||||
if (r1 == SSL_ERROR_NONE) {
|
||||
handshake_done_for_us = true;
|
||||
}
|
||||
|
||||
// The data to send out to peer.
|
||||
uint8_t* data = NULL;
|
||||
int size = BIO_get_mem_data(bio_out, &data);
|
||||
|
||||
// If outgoing packet is empty, we use the last cache.
|
||||
// @remark Only for DTLS server, because DTLS client use ARQ thread to send cached packet.
|
||||
bool cache = false;
|
||||
if (role_ != SrsDtlsRoleClient && size <= 0 && nn_last_outgoing_packet) {
|
||||
size = nn_last_outgoing_packet;
|
||||
data = last_outgoing_packet_cache;
|
||||
cache = true;
|
||||
}
|
||||
|
||||
// Trace the detail of DTLS packet.
|
||||
state_trace((uint8_t*)data, size, false, r0, r1, cache, false);
|
||||
|
||||
// Update the packet cache.
|
||||
if (size > 0 && data != last_outgoing_packet_cache && size < kRtpPacketSize) {
|
||||
memcpy(last_outgoing_packet_cache, data, size);
|
||||
nn_last_outgoing_packet = size;
|
||||
}
|
||||
|
||||
// Driven ARQ and state for DTLS client.
|
||||
if (role_ == SrsDtlsRoleClient) {
|
||||
// If we are sending client hello, change from init to new state.
|
||||
if (state_ == SrsDtlsStateInit && size > 14 && data[13] == 1) {
|
||||
state_ = SrsDtlsStateClientHello;
|
||||
}
|
||||
// If we are sending certificate, change from SrsDtlsStateServerHello to new state.
|
||||
if (state_ == SrsDtlsStateServerHello && size > 14 && data[13] == 11) {
|
||||
state_ = SrsDtlsStateClientCertificate;
|
||||
}
|
||||
|
||||
// Try to start the ARQ for client.
|
||||
if ((state_ == SrsDtlsStateClientHello || state_ == SrsDtlsStateClientCertificate)) {
|
||||
if (state_ == SrsDtlsStateClientHello) {
|
||||
state_ = SrsDtlsStateServerHello;
|
||||
} else if (state_ == SrsDtlsStateClientCertificate) {
|
||||
state_ = SrsDtlsStateServerDone;
|
||||
}
|
||||
|
||||
if ((err = start_arq()) != srs_success) {
|
||||
return srs_error_wrap(err, "start arq");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (size > 0 && (err = callback_->write_dtls_data(data, size)) != srs_success) {
|
||||
return srs_error_wrap(err, "dtls send size=%u, data=[%s]", size,
|
||||
srs_string_dumps_hex((char*)data, size, 32).c_str());
|
||||
}
|
||||
|
||||
if (handshake_done_for_us) {
|
||||
// When handshake done, stop the ARQ.
|
||||
if (role_ == SrsDtlsRoleClient) {
|
||||
state_ = SrsDtlsStateClientDone;
|
||||
stop_arq();
|
||||
}
|
||||
|
||||
// Notify connection the DTLS is done.
|
||||
if (((err = callback_->on_dtls_handshake_done()) != srs_success)) {
|
||||
return srs_error_wrap(err, "dtls done");
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsDtls::cycle()
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
// The first ARQ delay.
|
||||
srs_usleep(50 * SRS_UTIME_MILLISECONDS);
|
||||
|
||||
while (true) {
|
||||
srs_info("arq cycle, state=%u", state_);
|
||||
|
||||
// We ignore any error for ARQ thread.
|
||||
if ((err = trd->pull()) != srs_success) {
|
||||
srs_freep(err);
|
||||
return err;
|
||||
}
|
||||
|
||||
// If done, should stop ARQ.
|
||||
if (handshake_done_for_us) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// For DTLS client ARQ, the state should be specified.
|
||||
if (state_ != SrsDtlsStateServerHello && state_ != SrsDtlsStateServerDone) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// Try to retransmit the packet.
|
||||
uint8_t* data = last_outgoing_packet_cache;
|
||||
int size = nn_last_outgoing_packet;
|
||||
|
||||
if (size) {
|
||||
// Trace the detail of DTLS packet.
|
||||
state_trace((uint8_t*)data, size, false, 1, SSL_ERROR_NONE, true, true);
|
||||
|
||||
if ((err = callback_->write_dtls_data(data, size)) != srs_success) {
|
||||
return srs_error_wrap(err, "dtls send size=%u, data=[%s]", size,
|
||||
srs_string_dumps_hex((char*)data, size, 32).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Use ARQ step timeouts.
|
||||
srs_usleep(100 * SRS_UTIME_MILLISECONDS);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
void SrsDtls::state_trace(uint8_t* data, int length, bool incoming, int r0, int r1, bool cache, bool arq)
|
||||
{
|
||||
uint8_t content_type = 0;
|
||||
if (length >= 1) {
|
||||
content_type = (uint8_t)data[0];
|
||||
}
|
||||
|
||||
uint16_t size = 0;
|
||||
if (length >= 13) {
|
||||
size = uint16_t(data[11])<<8 | uint16_t(data[12]);
|
||||
}
|
||||
|
||||
uint8_t handshake_type = 0;
|
||||
if (length >= 14) {
|
||||
handshake_type = (uint8_t)data[13];
|
||||
}
|
||||
|
||||
srs_trace("DTLS: %s %s, done=%u, cache=%u, arq=%u, state=%u, r0=%d, r1=%d, len=%u, cnt=%u, size=%u, hs=%u",
|
||||
(role_ == SrsDtlsRoleClient? "Active":"Passive"), (incoming? "RECV":"SEND"), handshake_done_for_us, cache, arq,
|
||||
state_, r0, r1, length, content_type, size, handshake_type);
|
||||
}
|
||||
|
||||
srs_error_t SrsDtls::start_arq()
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
if (role_ != SrsDtlsRoleClient) {
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_info("start arq, state=%u", state_);
|
||||
|
||||
// Dispose the previous ARQ thread.
|
||||
srs_freep(trd);
|
||||
trd = new SrsSTCoroutine("dtls", this, _srs_context->get_id());
|
||||
|
||||
// We should start the ARQ thread for DTLS client.
|
||||
if ((err = trd->start()) != srs_success) {
|
||||
return srs_error_wrap(err, "arq start");
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
void SrsDtls::stop_arq()
|
||||
{
|
||||
srs_info("stop arq, state=%u", state_);
|
||||
srs_freep(trd);
|
||||
srs_info("stop arq, done");
|
||||
}
|
||||
|
||||
const int SRTP_MASTER_KEY_KEY_LEN = 16;
|
||||
|
|
|
@ -27,12 +27,15 @@
|
|||
#include <srs_core.hpp>
|
||||
|
||||
#include <string>
|
||||
|
||||
class SrsRequest;
|
||||
#include <vector>
|
||||
|
||||
#include <openssl/ssl.h>
|
||||
#include <srtp2/srtp.h>
|
||||
|
||||
#include <srs_app_st.hpp>
|
||||
|
||||
class SrsRequest;
|
||||
|
||||
class SrsDtlsCertificate
|
||||
{
|
||||
private:
|
||||
|
@ -92,17 +95,37 @@ public:
|
|||
virtual srs_error_t write_dtls_data(void* data, int size) = 0;
|
||||
};
|
||||
|
||||
class SrsDtls
|
||||
// The state for DTLS client.
|
||||
enum SrsDtlsState {
|
||||
SrsDtlsStateInit, // Start.
|
||||
SrsDtlsStateClientHello, // Should start ARQ thread.
|
||||
SrsDtlsStateServerHello, // We are in the first ARQ state.
|
||||
SrsDtlsStateClientCertificate, // Should start ARQ thread again.
|
||||
SrsDtlsStateServerDone, // We are in the second ARQ state.
|
||||
SrsDtlsStateClientDone, // Done.
|
||||
};
|
||||
|
||||
class SrsDtls : public ISrsCoroutineHandler
|
||||
{
|
||||
private:
|
||||
SSL_CTX* dtls_ctx;
|
||||
SSL* dtls;
|
||||
BIO* bio_in;
|
||||
BIO* bio_out;
|
||||
|
||||
ISrsDtlsCallback* callback;
|
||||
bool handshake_done;
|
||||
|
||||
ISrsDtlsCallback* callback_;
|
||||
private:
|
||||
// Whether the handhshake is done, for us only.
|
||||
// @remark For us only, means peer maybe not done, we also need to handle the DTLS packet.
|
||||
bool handshake_done_for_us;
|
||||
// DTLS packet cache, only last out-going packet.
|
||||
uint8_t* last_outgoing_packet_cache;
|
||||
int nn_last_outgoing_packet;
|
||||
// ARQ thread, for role active(DTLS client).
|
||||
// @note If passive(DTLS server), the ARQ is driven by DTLS client.
|
||||
SrsCoroutine* trd;
|
||||
// The DTLS-client state to drive the ARQ thread.
|
||||
SrsDtlsState state_;
|
||||
private:
|
||||
// @remark: dtls_role_ default value is DTLS_SERVER.
|
||||
SrsDtlsRole role_;
|
||||
// @remark: dtls_version_ default value is SrsDtlsVersionAuto.
|
||||
|
@ -112,15 +135,25 @@ public:
|
|||
virtual ~SrsDtls();
|
||||
public:
|
||||
srs_error_t initialize(std::string role, std::string version);
|
||||
public:
|
||||
// As DTLS client, start handshake actively, send the ClientHello packet.
|
||||
srs_error_t start_active_handshake();
|
||||
// When got DTLS packet, may handshake packets or application data.
|
||||
// @remark When we are passive(DTLS server), we start handshake when got DTLS packet.
|
||||
srs_error_t on_dtls(char* data, int nb_data);
|
||||
srs_error_t get_srtp_key(std::string& recv_key, std::string& send_key);
|
||||
private:
|
||||
SSL_CTX* build_dtls_ctx();
|
||||
srs_error_t do_on_dtls(char* data, int nb_data);
|
||||
srs_error_t do_handshake();
|
||||
// interface ISrsCoroutineHandler
|
||||
public:
|
||||
virtual srs_error_t cycle();
|
||||
private:
|
||||
void state_trace(uint8_t* data, int length, bool incoming, int r0, int r1, bool cache, bool arq);
|
||||
private:
|
||||
srs_error_t start_arq();
|
||||
void stop_arq();
|
||||
public:
|
||||
srs_error_t get_srtp_key(std::string& recv_key, std::string& send_key);
|
||||
};
|
||||
|
||||
class SrsSRTP
|
||||
|
|
|
@ -29,6 +29,7 @@ using namespace std;
|
|||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
#include <srs_kernel_error.hpp>
|
||||
#include <srs_kernel_log.hpp>
|
||||
|
@ -314,8 +315,13 @@ vector<SrsMediaPayloadType> SrsMediaDesc::find_media_with_encoding_name(const st
|
|||
{
|
||||
std::vector<SrsMediaPayloadType> payloads;
|
||||
|
||||
std::string lower_name, upper_name;
|
||||
transform(encoding_name.begin(), encoding_name.end(), lower_name.begin(), ::tolower);
|
||||
transform(encoding_name.begin(), encoding_name.end(), upper_name.begin(), ::toupper);
|
||||
|
||||
for (size_t i = 0; i < payload_types_.size(); ++i) {
|
||||
if (payload_types_[i].encoding_name_ == encoding_name) {
|
||||
if (payload_types_[i].encoding_name_ == std::string(lower_name.c_str()) ||
|
||||
payload_types_[i].encoding_name_ == std::string(upper_name.c_str())) {
|
||||
payloads.push_back(payload_types_[i]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -204,9 +204,18 @@ ISrsRtcServerHandler::~ISrsRtcServerHandler()
|
|||
{
|
||||
}
|
||||
|
||||
ISrsRtcServerHijacker::ISrsRtcServerHijacker()
|
||||
{
|
||||
}
|
||||
|
||||
ISrsRtcServerHijacker::~ISrsRtcServerHijacker()
|
||||
{
|
||||
}
|
||||
|
||||
SrsRtcServer::SrsRtcServer()
|
||||
{
|
||||
handler = NULL;
|
||||
hijacker = NULL;
|
||||
timer = new SrsHourGlass(this, 1 * SRS_UTIME_SECONDS);
|
||||
}
|
||||
|
||||
|
@ -257,6 +266,11 @@ void SrsRtcServer::set_handler(ISrsRtcServerHandler* h)
|
|||
handler = h;
|
||||
}
|
||||
|
||||
void SrsRtcServer::set_hijacker(ISrsRtcServerHijacker* h)
|
||||
{
|
||||
hijacker = h;
|
||||
}
|
||||
|
||||
srs_error_t SrsRtcServer::listen_udp()
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
@ -309,6 +323,18 @@ srs_error_t SrsRtcServer::on_udp_packet(SrsUdpMuxSocket* skt)
|
|||
}
|
||||
}
|
||||
|
||||
// Notify hijack to handle the UDP packet.
|
||||
if (hijacker) {
|
||||
bool consumed = false;
|
||||
if ((err = hijacker->on_udp_packet(skt, session, &consumed)) != srs_success) {
|
||||
return srs_error_wrap(err, "hijack consumed=%u", consumed);
|
||||
}
|
||||
|
||||
if (consumed) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
// For STUN, the peer address may change.
|
||||
if (srs_is_stun((uint8_t*)data, size)) {
|
||||
SrsStunPacket ping;
|
||||
|
@ -378,7 +404,8 @@ srs_error_t SrsRtcServer::listen_api()
|
|||
}
|
||||
|
||||
srs_error_t SrsRtcServer::create_session(
|
||||
SrsRequest* req, const SrsSdp& remote_sdp, SrsSdp& local_sdp, const std::string& mock_eip, bool publish,
|
||||
SrsRequest* req, const SrsSdp& remote_sdp, SrsSdp& local_sdp, const std::string& mock_eip,
|
||||
bool publish, bool dtls, bool srtp,
|
||||
SrsRtcConnection** psession
|
||||
) {
|
||||
srs_error_t err = srs_success;
|
||||
|
@ -390,14 +417,13 @@ srs_error_t SrsRtcServer::create_session(
|
|||
return srs_error_wrap(err, "create source");
|
||||
}
|
||||
|
||||
// TODO: FIXME: Refine the API for stream status manage.
|
||||
if (publish && !source->can_publish(false)) {
|
||||
if (publish && !source->can_publish()) {
|
||||
return srs_error_new(ERROR_RTC_SOURCE_BUSY, "stream %s busy", req->get_stream_url().c_str());
|
||||
}
|
||||
|
||||
// TODO: FIXME: add do_create_session to error process.
|
||||
SrsRtcConnection* session = new SrsRtcConnection(this, cid);
|
||||
if ((err = do_create_session(session, req, remote_sdp, local_sdp, mock_eip, publish, source)) != srs_success) {
|
||||
if ((err = do_create_session(session, req, remote_sdp, local_sdp, mock_eip, publish, dtls, srtp)) != srs_success) {
|
||||
srs_freep(session);
|
||||
return srs_error_wrap(err, "create session");
|
||||
}
|
||||
|
@ -408,8 +434,8 @@ srs_error_t SrsRtcServer::create_session(
|
|||
}
|
||||
|
||||
srs_error_t SrsRtcServer::do_create_session(
|
||||
SrsRtcConnection* session, SrsRequest* req, const SrsSdp& remote_sdp, SrsSdp& local_sdp, const std::string& mock_eip, bool publish,
|
||||
SrsRtcStream* source
|
||||
SrsRtcConnection* session, SrsRequest* req, const SrsSdp& remote_sdp, SrsSdp& local_sdp, const std::string& mock_eip,
|
||||
bool publish, bool dtls, bool srtp
|
||||
)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
@ -426,7 +452,7 @@ srs_error_t SrsRtcServer::do_create_session(
|
|||
}
|
||||
|
||||
// All tracks default as inactive, so we must enable them.
|
||||
session->set_all_tracks_status(true);
|
||||
session->set_all_tracks_status(req->get_stream_url(), publish, true);
|
||||
|
||||
std::string local_pwd = srs_random_str(32);
|
||||
std::string local_ufrag = "";
|
||||
|
@ -477,11 +503,13 @@ srs_error_t SrsRtcServer::do_create_session(
|
|||
session->set_state(WAITING_STUN);
|
||||
|
||||
// Before session initialize, we must setup the local SDP.
|
||||
if ((err = session->initialize(source, req, publish, username)) != srs_success) {
|
||||
if ((err = session->initialize(req, dtls, srtp, username)) != srs_success) {
|
||||
return srs_error_wrap(err, "init");
|
||||
}
|
||||
|
||||
// We allows username is optional, but it never empty here.
|
||||
map_username_session.insert(make_pair(username, session));
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -535,18 +563,14 @@ srs_error_t SrsRtcServer::setup_session2(SrsRtcConnection* session, SrsRequest*
|
|||
return err;
|
||||
}
|
||||
|
||||
SrsRtcStream* source = NULL;
|
||||
if ((err = _srs_rtc_sources->fetch_or_create(req, &source)) != srs_success) {
|
||||
return srs_error_wrap(err, "create source");
|
||||
}
|
||||
|
||||
// TODO: FIXME: Collision detect.
|
||||
string username = session->get_local_sdp()->get_ice_ufrag() + ":" + remote_sdp.get_ice_ufrag();
|
||||
|
||||
if ((err = session->initialize(source, req, false, username)) != srs_success) {
|
||||
if ((err = session->initialize(req, true, true, username)) != srs_success) {
|
||||
return srs_error_wrap(err, "init");
|
||||
}
|
||||
|
||||
// We allows username is optional, but it never empty here.
|
||||
map_username_session.insert(make_pair(username, session));
|
||||
|
||||
session->set_remote_sdp(remote_sdp);
|
||||
|
@ -564,8 +588,9 @@ void SrsRtcServer::destroy(SrsRtcConnection* session)
|
|||
|
||||
std::map<std::string, SrsRtcConnection*>::iterator it;
|
||||
|
||||
// We allows username is optional.
|
||||
string username = session->username();
|
||||
if ((it = map_username_session.find(username)) != map_username_session.end()) {
|
||||
if (!username.empty() && (it = map_username_session.find(username)) != map_username_session.end()) {
|
||||
map_username_session.erase(it);
|
||||
}
|
||||
|
||||
|
|
|
@ -72,6 +72,17 @@ public:
|
|||
virtual void on_timeout(SrsRtcConnection* session) = 0;
|
||||
};
|
||||
|
||||
// The hijacker to hook server.
|
||||
class ISrsRtcServerHijacker
|
||||
{
|
||||
public:
|
||||
ISrsRtcServerHijacker();
|
||||
virtual ~ISrsRtcServerHijacker();
|
||||
public:
|
||||
// If consumed set to true, server will ignore the packet.
|
||||
virtual srs_error_t on_udp_packet(SrsUdpMuxSocket* skt, SrsRtcConnection* session, bool* pconsumed) = 0;
|
||||
};
|
||||
|
||||
// The RTC server instance, listen UDP port, handle UDP packet, manage RTC connections.
|
||||
class SrsRtcServer : virtual public ISrsUdpMuxHandler, virtual public ISrsHourGlass
|
||||
{
|
||||
|
@ -79,6 +90,7 @@ private:
|
|||
SrsHourGlass* timer;
|
||||
std::vector<SrsUdpMuxListener*> listeners;
|
||||
ISrsRtcServerHandler* handler;
|
||||
ISrsRtcServerHijacker* hijacker;
|
||||
private:
|
||||
// TODO: FIXME: Rename it.
|
||||
std::map<std::string, SrsRtcConnection*> map_username_session; // key: username(local_ufrag + ":" + remote_ufrag)
|
||||
|
@ -93,6 +105,7 @@ public:
|
|||
virtual srs_error_t initialize();
|
||||
// Set the handler for server events.
|
||||
void set_handler(ISrsRtcServerHandler* h);
|
||||
void set_hijacker(ISrsRtcServerHijacker* h);
|
||||
public:
|
||||
// TODO: FIXME: Support gracefully quit.
|
||||
// TODO: FIXME: Support reload.
|
||||
|
@ -102,13 +115,14 @@ public:
|
|||
public:
|
||||
// Peer start offering, we answer it.
|
||||
srs_error_t create_session(
|
||||
SrsRequest* req, const SrsSdp& remote_sdp, SrsSdp& local_sdp, const std::string& mock_eip, bool publish,
|
||||
SrsRequest* req, const SrsSdp& remote_sdp, SrsSdp& local_sdp, const std::string& mock_eip,
|
||||
bool publish, bool dtls, bool srtp,
|
||||
SrsRtcConnection** psession
|
||||
);
|
||||
private:
|
||||
srs_error_t do_create_session(
|
||||
SrsRtcConnection* session, SrsRequest* req, const SrsSdp& remote_sdp, SrsSdp& local_sdp,
|
||||
const std::string& mock_eip, bool publish, SrsRtcStream* source
|
||||
const std::string& mock_eip, bool publish, bool dtls, bool srtp
|
||||
);
|
||||
public:
|
||||
// We start offering, create_session2 to generate offer, setup_session2 to handle answer.
|
||||
|
|
|
@ -290,7 +290,9 @@ ISrsRtcPublishStream::~ISrsRtcPublishStream()
|
|||
|
||||
SrsRtcStream::SrsRtcStream()
|
||||
{
|
||||
_can_publish = true;
|
||||
is_created_ = false;
|
||||
is_delivering_packets_ = false;
|
||||
|
||||
publish_stream_ = NULL;
|
||||
stream_desc_ = NULL;
|
||||
|
||||
|
@ -404,11 +406,22 @@ void SrsRtcStream::on_consumer_destroy(SrsRtcConsumer* consumer)
|
|||
if (it != consumers.end()) {
|
||||
consumers.erase(it);
|
||||
}
|
||||
|
||||
// When all consumers finished, notify publisher to handle it.
|
||||
if (publish_stream_ && consumers.empty()) {
|
||||
publish_stream_->on_consumers_finished();
|
||||
}
|
||||
}
|
||||
|
||||
bool SrsRtcStream::can_publish(bool is_edge)
|
||||
bool SrsRtcStream::can_publish()
|
||||
{
|
||||
return _can_publish;
|
||||
return !is_created_;
|
||||
}
|
||||
|
||||
void SrsRtcStream::set_stream_created()
|
||||
{
|
||||
srs_assert(!is_created_ && !is_delivering_packets_);
|
||||
is_created_ = true;
|
||||
}
|
||||
|
||||
srs_error_t SrsRtcStream::on_publish()
|
||||
|
@ -418,7 +431,10 @@ srs_error_t SrsRtcStream::on_publish()
|
|||
// update the request object.
|
||||
srs_assert(req);
|
||||
|
||||
_can_publish = false;
|
||||
// For RTC, DTLS is done, and we are ready to deliver packets.
|
||||
// @note For compatible with RTMP, we also set the is_created_, it MUST be created here.
|
||||
is_created_ = true;
|
||||
is_delivering_packets_ = true;
|
||||
|
||||
// whatever, the publish thread is the source or edge source,
|
||||
// save its id to srouce id.
|
||||
|
@ -434,13 +450,15 @@ srs_error_t SrsRtcStream::on_publish()
|
|||
void SrsRtcStream::on_unpublish()
|
||||
{
|
||||
// ignore when already unpublished.
|
||||
if (_can_publish) {
|
||||
if (!is_created_) {
|
||||
return;
|
||||
}
|
||||
|
||||
srs_trace("cleanup when unpublish");
|
||||
srs_trace("cleanup when unpublish, created=%u, deliver=%u", is_created_, is_delivering_packets_);
|
||||
|
||||
is_created_ = false;
|
||||
is_delivering_packets_ = false;
|
||||
|
||||
_can_publish = true;
|
||||
_source_id = SrsContextId();
|
||||
|
||||
// TODO: FIXME: Handle by statistic.
|
||||
|
@ -1138,11 +1156,13 @@ SrsMediaPayloadType SrsCodecPayload::generate_media_payload_type()
|
|||
|
||||
SrsVideoPayload::SrsVideoPayload()
|
||||
{
|
||||
type_ = "video";
|
||||
}
|
||||
|
||||
SrsVideoPayload::SrsVideoPayload(uint8_t pt, std::string encode_name, int sample)
|
||||
:SrsCodecPayload(pt, encode_name, sample)
|
||||
{
|
||||
type_ = "video";
|
||||
h264_param_.profile_level_id = "";
|
||||
h264_param_.packetization_mode = "";
|
||||
h264_param_.level_asymmerty_allow = "";
|
||||
|
@ -1193,10 +1213,18 @@ SrsMediaPayloadType SrsVideoPayload::generate_media_payload_type()
|
|||
srs_error_t SrsVideoPayload::set_h264_param_desc(std::string fmtp)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
std::vector<std::string> vec = split_str(fmtp, ";");
|
||||
for (size_t i = 0; i < vec.size(); ++i) {
|
||||
std::vector<std::string> kv = split_str(vec[i], "=");
|
||||
if (kv.size() == 2) {
|
||||
|
||||
// For example: level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f
|
||||
std::vector<std::string> attributes = split_str(fmtp, ";");
|
||||
|
||||
for (size_t i = 0; i < attributes.size(); ++i) {
|
||||
std::string attribute = attributes.at(i);
|
||||
|
||||
std::vector<std::string> kv = split_str(attribute, "=");
|
||||
if (kv.size() != 2) {
|
||||
return srs_error_new(ERROR_RTC_SDP_DECODE, "invalid h264 param=%s", attribute.c_str());
|
||||
}
|
||||
|
||||
if (kv[0] == "profile-level-id") {
|
||||
h264_param_.profile_level_id = kv[1];
|
||||
} else if (kv[0] == "packetization-mode") {
|
||||
|
@ -1214,9 +1242,6 @@ srs_error_t SrsVideoPayload::set_h264_param_desc(std::string fmtp)
|
|||
} else {
|
||||
return srs_error_new(ERROR_RTC_SDP_DECODE, "invalid h264 param=%s", kv[0].c_str());
|
||||
}
|
||||
} else {
|
||||
return srs_error_new(ERROR_RTC_SDP_DECODE, "invalid h264 param=%s", vec[i].c_str());
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
|
@ -1224,11 +1249,13 @@ srs_error_t SrsVideoPayload::set_h264_param_desc(std::string fmtp)
|
|||
|
||||
SrsAudioPayload::SrsAudioPayload()
|
||||
{
|
||||
type_ = "audio";
|
||||
}
|
||||
|
||||
SrsAudioPayload::SrsAudioPayload(uint8_t pt, std::string encode_name, int sample, int channel)
|
||||
:SrsCodecPayload(pt, encode_name, sample)
|
||||
{
|
||||
type_ = "audio";
|
||||
channel_ = channel;
|
||||
opus_param_.minptime = 0;
|
||||
opus_param_.use_inband_fec = false;
|
||||
|
@ -1344,6 +1371,46 @@ SrsMediaPayloadType SrsRedPayload::generate_media_payload_type()
|
|||
return media_payload_type;
|
||||
}
|
||||
|
||||
SrsRtxPayloadDes::SrsRtxPayloadDes()
|
||||
{
|
||||
}
|
||||
|
||||
SrsRtxPayloadDes::SrsRtxPayloadDes(uint8_t pt, uint8_t apt):SrsCodecPayload(pt, "rtx", 8000), apt_(apt)
|
||||
{
|
||||
}
|
||||
|
||||
SrsRtxPayloadDes::~SrsRtxPayloadDes()
|
||||
{
|
||||
}
|
||||
|
||||
SrsRtxPayloadDes* SrsRtxPayloadDes::copy()
|
||||
{
|
||||
SrsRtxPayloadDes* cp = new SrsRtxPayloadDes();
|
||||
|
||||
cp->type_ = type_;
|
||||
cp->pt_ = pt_;
|
||||
cp->name_ = name_;
|
||||
cp->sample_ = sample_;
|
||||
cp->rtcp_fbs_ = rtcp_fbs_;
|
||||
cp->apt_ = apt_;
|
||||
|
||||
return cp;
|
||||
}
|
||||
|
||||
SrsMediaPayloadType SrsRtxPayloadDes::generate_media_payload_type()
|
||||
{
|
||||
SrsMediaPayloadType media_payload_type(pt_);
|
||||
|
||||
media_payload_type.encoding_name_ = name_;
|
||||
media_payload_type.clock_rate_ = sample_;
|
||||
std::ostringstream format_specific_param;
|
||||
format_specific_param << "fmtp:" << pt_ << " apt="<< apt_;
|
||||
|
||||
media_payload_type.format_specific_param_ = format_specific_param.str();
|
||||
|
||||
return media_payload_type;
|
||||
}
|
||||
|
||||
SrsRtcTrackDescription::SrsRtcTrackDescription()
|
||||
{
|
||||
ssrc_ = 0;
|
||||
|
@ -1405,7 +1472,8 @@ void SrsRtcTrackDescription::create_auxiliary_payload(const std::vector<SrsMedia
|
|||
red_ = new SrsRedPayload(payload.payload_type_, "red", payload.clock_rate_, ::atol(payload.encoding_param_.c_str()));
|
||||
} else if (payload.encoding_name_ == "rtx") {
|
||||
srs_freep(rtx_);
|
||||
rtx_ = new SrsCodecPayload(payload.payload_type_, "rtx", payload.clock_rate_);
|
||||
// TODO: FIXME: Rtx clock_rate should be payload.clock_rate_
|
||||
rtx_ = new SrsRtxPayloadDes(payload.payload_type_, ::atol(payload.encoding_param_.c_str()));
|
||||
} else if (payload.encoding_name_ == "ulpfec") {
|
||||
srs_freep(ulpfec_);
|
||||
ulpfec_ = new SrsCodecPayload(payload.payload_type_, "ulpfec", payload.clock_rate_);
|
||||
|
@ -1752,13 +1820,21 @@ bool SrsRtcSendTrack::has_ssrc(uint32_t ssrc)
|
|||
|
||||
SrsRtpPacket2* SrsRtcSendTrack::fetch_rtp_packet(uint16_t seq)
|
||||
{
|
||||
if (rtp_queue_) {
|
||||
return rtp_queue_->at(seq);
|
||||
SrsRtpPacket2* pkt = rtp_queue_->at(seq);
|
||||
|
||||
if (pkt == NULL) {
|
||||
return pkt;
|
||||
}
|
||||
|
||||
// For NACK, it sequence must match exactly, or it cause SRTP fail.
|
||||
if (pkt->header.get_sequence() != seq) {
|
||||
srs_trace("miss match seq=%u, pkt seq=%u", seq, pkt->header.get_sequence());
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return pkt;
|
||||
}
|
||||
|
||||
// TODO: FIXME: Should refine logs, set tracks in a time.
|
||||
bool SrsRtcSendTrack::set_track_status(bool active)
|
||||
{
|
||||
|
@ -1815,6 +1891,7 @@ srs_error_t SrsRtcAudioSendTrack::on_rtp(SrsRtpPacket2* pkt, SrsRtcPlayStreamSta
|
|||
session_->stat_->nn_out_audios++;
|
||||
|
||||
// track level statistic
|
||||
// TODO: FIXME: if send packets failed, statistic is no correct.
|
||||
statistic_->packets++;
|
||||
statistic_->bytes += pkt->nb_bytes();
|
||||
|
||||
|
@ -1870,6 +1947,7 @@ srs_error_t SrsRtcVideoSendTrack::on_rtp(SrsRtpPacket2* pkt, SrsRtcPlayStreamSta
|
|||
session_->stat_->nn_out_videos++;
|
||||
|
||||
// track level statistic
|
||||
// TODO: FIXME: if send packets failed, statistic is no correct.
|
||||
statistic->packets++;
|
||||
statistic->bytes += pkt->nb_bytes();
|
||||
|
||||
|
|
|
@ -129,7 +129,10 @@ public:
|
|||
ISrsRtcPublishStream();
|
||||
virtual ~ISrsRtcPublishStream();
|
||||
public:
|
||||
// Request keyframe(PLI) from publisher, for fresh consumer.
|
||||
virtual void request_keyframe(uint32_t ssrc) = 0;
|
||||
// Notify publisher that all consumers is finished.
|
||||
virtual void on_consumers_finished() = 0;
|
||||
};
|
||||
|
||||
// A Source is a stream, to publish and to play with, binding to SrsRtcPublishStream and SrsRtcPlayStream.
|
||||
|
@ -152,8 +155,10 @@ private:
|
|||
private:
|
||||
// To delivery stream to clients.
|
||||
std::vector<SrsRtcConsumer*> consumers;
|
||||
// Whether source is avaiable for publishing.
|
||||
bool _can_publish;
|
||||
// Whether stream is created, that is, SDP is done.
|
||||
bool is_created_;
|
||||
// Whether stream is delivering data, that is, DTLS is done.
|
||||
bool is_delivering_packets_;
|
||||
public:
|
||||
SrsRtcStream();
|
||||
virtual ~SrsRtcStream();
|
||||
|
@ -178,8 +183,11 @@ public:
|
|||
// @param dg, whether dumps the gop cache.
|
||||
virtual srs_error_t consumer_dumps(SrsRtcConsumer* consumer, bool ds = true, bool dm = true, bool dg = true);
|
||||
virtual void on_consumer_destroy(SrsRtcConsumer* consumer);
|
||||
// TODO: FIXME: Remove the param is_edge.
|
||||
virtual bool can_publish(bool is_edge);
|
||||
// Whether we can publish stream to the source, return false if it exists.
|
||||
// @remark Note that when SDP is done, we set the stream is not able to publish.
|
||||
virtual bool can_publish();
|
||||
// For RTC, the stream is created when SDP is done, and then do DTLS
|
||||
virtual void set_stream_created();
|
||||
// When start publish stream.
|
||||
virtual srs_error_t on_publish();
|
||||
// When stop publish stream.
|
||||
|
@ -330,6 +338,20 @@ public:
|
|||
virtual SrsMediaPayloadType generate_media_payload_type();
|
||||
};
|
||||
|
||||
class SrsRtxPayloadDes : public SrsCodecPayload
|
||||
{
|
||||
public:
|
||||
uint8_t apt_;
|
||||
public:
|
||||
SrsRtxPayloadDes();
|
||||
SrsRtxPayloadDes(uint8_t pt, uint8_t apt);
|
||||
virtual ~SrsRtxPayloadDes();
|
||||
|
||||
public:
|
||||
virtual SrsRtxPayloadDes* copy();
|
||||
virtual SrsMediaPayloadType generate_media_payload_type();
|
||||
};
|
||||
|
||||
class SrsRtcTrackDescription
|
||||
{
|
||||
public:
|
||||
|
|
|
@ -24,6 +24,6 @@
|
|||
#ifndef SRS_CORE_VERSION4_HPP
|
||||
#define SRS_CORE_VERSION4_HPP
|
||||
|
||||
#define SRS_VERSION4_REVISION 37
|
||||
#define SRS_VERSION4_REVISION 38
|
||||
|
||||
#endif
|
||||
|
|
|
@ -368,6 +368,13 @@ SrsSample::SrsSample()
|
|||
bframe = false;
|
||||
}
|
||||
|
||||
SrsSample::SrsSample(char* b, int s)
|
||||
{
|
||||
size = s;
|
||||
bytes = b;
|
||||
bframe = false;
|
||||
}
|
||||
|
||||
SrsSample::~SrsSample()
|
||||
{
|
||||
}
|
||||
|
|
|
@ -538,6 +538,7 @@ public:
|
|||
bool bframe;
|
||||
public:
|
||||
SrsSample();
|
||||
SrsSample(char* b, int s);
|
||||
~SrsSample();
|
||||
public:
|
||||
// If we need to know whether sample is bframe, we have to parse the NALU payload.
|
||||
|
|
|
@ -354,9 +354,12 @@
|
|||
#define ERROR_RTC_INVALID_PARAMS 5023
|
||||
#define ERROR_RTC_DUMMY_BRIDGER 5024
|
||||
#define ERROR_RTC_STREM_STARTED 5025
|
||||
#define ERROR_RTC_STREAM_DESC 5026
|
||||
#define ERROR_RTC_TRACK_CODEC 5027
|
||||
#define ERROR_RTC_NO_PLAYER 5028
|
||||
#define ERROR_RTC_TRACK_CODEC 5026
|
||||
#define ERROR_RTC_NO_PLAYER 5027
|
||||
#define ERROR_RTC_NO_PUBLISHER 5028
|
||||
#define ERROR_RTC_DUPLICATED_SSRC 5029
|
||||
#define ERROR_RTC_NO_TRACK 5030
|
||||
#define ERROR_RTC_RTCP_EMPTY_RR 5031
|
||||
|
||||
///////////////////////////////////////////////////////
|
||||
// GB28181 API error.
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -50,7 +50,14 @@ enum SrsRtcpType
|
|||
SrsRtcpType_xr = 207,
|
||||
};
|
||||
|
||||
// @see: https://tools.ietf.org/html/rfc4585#section-6.3
|
||||
const uint8_t kPLI = 1;
|
||||
const uint8_t kSLI = 2;
|
||||
const uint8_t kRPSI = 3;
|
||||
const uint8_t kAFB = 15;
|
||||
|
||||
// RTCP Header, @see http://tools.ietf.org/html/rfc3550#section-6.1
|
||||
// @remark The header must be 4 bytes, which align with the max field size 2B.
|
||||
struct SrsRtcpHeader
|
||||
{
|
||||
uint16_t rc:5;
|
||||
|
@ -65,8 +72,12 @@ class SrsRtcpCommon: public ISrsCodec
|
|||
{
|
||||
protected:
|
||||
SrsRtcpHeader header_;
|
||||
uint32_t ssrc_;
|
||||
uint8_t payload_[kRtcpPacketSize];
|
||||
int payload_len_;
|
||||
|
||||
char* data_;
|
||||
int nb_data_;
|
||||
protected:
|
||||
srs_error_t decode_header(SrsBuffer *buffer);
|
||||
srs_error_t encode_header(SrsBuffer *buffer);
|
||||
|
@ -74,6 +85,13 @@ public:
|
|||
SrsRtcpCommon();
|
||||
virtual ~SrsRtcpCommon();
|
||||
virtual uint8_t type() const;
|
||||
virtual uint8_t get_rc() const;
|
||||
|
||||
uint32_t get_ssrc();
|
||||
void set_ssrc(uint32_t ssrc);
|
||||
|
||||
char* data();
|
||||
int size();
|
||||
// interface ISrsCodec
|
||||
public:
|
||||
virtual srs_error_t decode(SrsBuffer *buffer);
|
||||
|
@ -84,23 +102,19 @@ public:
|
|||
class SrsRtcpApp : public SrsRtcpCommon
|
||||
{
|
||||
private:
|
||||
SrsRtcpHeader header_;
|
||||
uint32_t ssrc_;
|
||||
uint8_t name_[4];
|
||||
uint8_t payload_[kRtcpPacketSize];
|
||||
int payload_len_;
|
||||
public:
|
||||
SrsRtcpApp();
|
||||
virtual ~SrsRtcpApp();
|
||||
|
||||
static bool is_rtcp_app(uint8_t *data, int nb_data);
|
||||
|
||||
virtual uint8_t type() const;
|
||||
|
||||
uint32_t get_ssrc() const;
|
||||
uint8_t get_subtype() const;
|
||||
std::string get_name() const;
|
||||
srs_error_t get_payload(uint8_t*& payload, int& len);
|
||||
|
||||
void set_ssrc(uint32_t ssrc);
|
||||
srs_error_t set_subtype(uint8_t type);
|
||||
srs_error_t set_name(std::string name);
|
||||
srs_error_t set_payload(uint8_t* payload, int len);
|
||||
|
@ -125,11 +139,11 @@ struct SrsRtcpRB
|
|||
class SrsRtcpSR : public SrsRtcpCommon
|
||||
{
|
||||
private:
|
||||
uint32_t sender_ssrc_;
|
||||
uint64_t ntp_;
|
||||
uint32_t rtp_ts_;
|
||||
uint32_t send_rtp_packets_;
|
||||
uint32_t send_rtp_bytes_;
|
||||
|
||||
public:
|
||||
SrsRtcpSR();
|
||||
virtual ~SrsRtcpSR();
|
||||
|
@ -137,13 +151,11 @@ public:
|
|||
uint8_t get_rc() const;
|
||||
// overload SrsRtcpCommon
|
||||
virtual uint8_t type() const;
|
||||
uint32_t get_sender_ssrc() const;
|
||||
uint64_t get_ntp() const;
|
||||
uint32_t get_rtp_ts() const;
|
||||
uint32_t get_rtp_send_packets() const;
|
||||
uint32_t get_rtp_send_bytes() const;
|
||||
|
||||
void set_sender_ssrc(uint32_t ssrc);
|
||||
void set_ntp(uint64_t ntp);
|
||||
void set_rtp_ts(uint32_t ts);
|
||||
void set_rtp_send_packets(uint32_t packets);
|
||||
|
@ -158,7 +170,6 @@ public:
|
|||
class SrsRtcpRR : public SrsRtcpCommon
|
||||
{
|
||||
private:
|
||||
uint32_t sender_ssrc_;
|
||||
SrsRtcpRB rb_;
|
||||
public:
|
||||
SrsRtcpRR(uint32_t sender_ssrc = 0);
|
||||
|
@ -235,7 +246,6 @@ public:
|
|||
class SrsRtcpTWCC : public SrsRtcpCommon
|
||||
{
|
||||
private:
|
||||
uint32_t sender_ssrc_;
|
||||
uint32_t media_ssrc_;
|
||||
uint16_t base_sn_;
|
||||
uint16_t packet_count_;
|
||||
|
@ -244,7 +254,7 @@ private:
|
|||
std::vector<uint16_t> encoded_chucks_;
|
||||
std::vector<uint16_t> pkt_deltas_;
|
||||
|
||||
std::map<uint16_t, srs_utime_t> recv_packes_;
|
||||
std::map<uint16_t, srs_utime_t> recv_packets_;
|
||||
std::set<uint16_t, SrsSeqCompareLess> recv_sns_;
|
||||
|
||||
struct SrsRtcpTWCCChunk {
|
||||
|
@ -252,6 +262,7 @@ private:
|
|||
uint16_t size;
|
||||
bool all_same;
|
||||
bool has_large_delta;
|
||||
SrsRtcpTWCCChunk();
|
||||
};
|
||||
|
||||
int pkt_len;
|
||||
|
@ -288,6 +299,8 @@ public:
|
|||
void add_recv_delta(uint16_t delta);
|
||||
|
||||
srs_error_t recv_packet(uint16_t sn, srs_utime_t ts);
|
||||
bool need_feedback();
|
||||
|
||||
// interface ISrsCodec
|
||||
public:
|
||||
virtual srs_error_t decode(SrsBuffer *buffer);
|
||||
|
@ -306,7 +319,6 @@ private:
|
|||
bool in_use;
|
||||
};
|
||||
|
||||
uint32_t sender_ssrc_;
|
||||
uint32_t media_ssrc_;
|
||||
std::set<uint16_t, SrsSeqCompareLess> lost_sns_;
|
||||
public:
|
||||
|
@ -325,18 +337,105 @@ public:
|
|||
virtual srs_error_t encode(SrsBuffer *buffer);
|
||||
};
|
||||
|
||||
class SrsRtcpPsfbCommon : public SrsRtcpCommon
|
||||
{
|
||||
protected:
|
||||
uint32_t media_ssrc_;
|
||||
public:
|
||||
SrsRtcpPsfbCommon();
|
||||
virtual ~SrsRtcpPsfbCommon();
|
||||
|
||||
uint32_t get_media_ssrc() const;
|
||||
void set_media_ssrc(uint32_t ssrc);
|
||||
|
||||
// interface ISrsCodec
|
||||
public:
|
||||
virtual srs_error_t decode(SrsBuffer *buffer);
|
||||
virtual int nb_bytes();
|
||||
virtual srs_error_t encode(SrsBuffer *buffer);
|
||||
};
|
||||
|
||||
class SrsRtcpPli : public SrsRtcpPsfbCommon
|
||||
{
|
||||
public:
|
||||
SrsRtcpPli(uint32_t sender_ssrc = 0);
|
||||
virtual ~SrsRtcpPli();
|
||||
|
||||
// interface ISrsCodec
|
||||
public:
|
||||
virtual srs_error_t decode(SrsBuffer *buffer);
|
||||
virtual int nb_bytes();
|
||||
virtual srs_error_t encode(SrsBuffer *buffer);
|
||||
};
|
||||
|
||||
class SrsRtcpSli : public SrsRtcpPsfbCommon
|
||||
{
|
||||
private:
|
||||
uint16_t first_;
|
||||
uint16_t number_;
|
||||
uint8_t picture_;
|
||||
public:
|
||||
SrsRtcpSli(uint32_t sender_ssrc = 0);
|
||||
virtual ~SrsRtcpSli();
|
||||
|
||||
// interface ISrsCodec
|
||||
public:
|
||||
virtual srs_error_t decode(SrsBuffer *buffer);
|
||||
virtual int nb_bytes();
|
||||
virtual srs_error_t encode(SrsBuffer *buffer);
|
||||
};
|
||||
|
||||
class SrsRtcpRpsi : public SrsRtcpPsfbCommon
|
||||
{
|
||||
private:
|
||||
uint8_t pb_;
|
||||
uint8_t payload_type_;
|
||||
char* native_rpsi_;
|
||||
int nb_native_rpsi_;
|
||||
|
||||
public:
|
||||
SrsRtcpRpsi(uint32_t sender_ssrc = 0);
|
||||
virtual ~SrsRtcpRpsi();
|
||||
|
||||
// interface ISrsCodec
|
||||
public:
|
||||
virtual srs_error_t decode(SrsBuffer *buffer);
|
||||
virtual int nb_bytes();
|
||||
virtual srs_error_t encode(SrsBuffer *buffer);
|
||||
};
|
||||
|
||||
class SrsRtcpXr : public SrsRtcpCommon
|
||||
{
|
||||
public:
|
||||
SrsRtcpXr (uint32_t ssrc = 0);
|
||||
virtual ~SrsRtcpXr();
|
||||
|
||||
// interface ISrsCodec
|
||||
public:
|
||||
virtual srs_error_t decode(SrsBuffer *buffer);
|
||||
virtual int nb_bytes();
|
||||
virtual srs_error_t encode(SrsBuffer *buffer);
|
||||
};
|
||||
|
||||
class SrsRtcpCompound : public ISrsCodec
|
||||
{
|
||||
private:
|
||||
std::vector<SrsRtcpCommon*> rtcps_;
|
||||
int nb_bytes_;
|
||||
char* data_;
|
||||
int nb_data_;
|
||||
public:
|
||||
SrsRtcpCompound();
|
||||
virtual ~SrsRtcpCompound();
|
||||
|
||||
// TODO: FIXME: Should rename it to pop(), because it's not a GET method.
|
||||
SrsRtcpCommon* get_next_rtcp();
|
||||
srs_error_t add_rtcp(SrsRtcpCommon *rtcp);
|
||||
void clear();
|
||||
|
||||
char* data();
|
||||
int size();
|
||||
|
||||
// interface ISrsCodec
|
||||
public:
|
||||
virtual srs_error_t decode(SrsBuffer *buffer);
|
||||
|
|
|
@ -137,25 +137,21 @@ srs_error_t SrsRtpExtensionTwcc::decode(SrsBuffer* buf)
|
|||
|
||||
int SrsRtpExtensionTwcc::nb_bytes()
|
||||
{
|
||||
return 4;
|
||||
return 3;
|
||||
}
|
||||
|
||||
srs_error_t SrsRtpExtensionTwcc::encode(SrsBuffer* buf)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
// TODO: FIXME: Only requires 3 bytes.
|
||||
if(!buf->require(4)) {
|
||||
return srs_error_new(ERROR_RTC_RTP_MUXER, "requires %d bytes", 4);
|
||||
if(!buf->require(3)) {
|
||||
return srs_error_new(ERROR_RTC_RTP_MUXER, "requires %d bytes", 3);
|
||||
}
|
||||
|
||||
uint8_t id_len = (id_ & 0x0F)<< 4| 0x01;
|
||||
buf->write_1bytes(id_len);
|
||||
buf->write_2bytes(sn_);
|
||||
|
||||
// TODO: FIXME: Should padding in the final of SrsRtpExtensions::encode.
|
||||
buf->write_1bytes(0x00);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -265,7 +261,10 @@ srs_error_t SrsRtpExtensions::decode_0xbede(SrsBuffer* buf)
|
|||
|
||||
int SrsRtpExtensions::nb_bytes()
|
||||
{
|
||||
return 4 + (twcc_.has_twcc_ext() ? twcc_.nb_bytes() : 0);
|
||||
int size = 4 + (twcc_.has_twcc_ext() ? twcc_.nb_bytes() : 0);
|
||||
// add padding
|
||||
size += (size % 4 == 0) ? 0 : (4 - size % 4);
|
||||
return size;
|
||||
}
|
||||
|
||||
srs_error_t SrsRtpExtensions::encode(SrsBuffer* buf)
|
||||
|
@ -274,18 +273,31 @@ srs_error_t SrsRtpExtensions::encode(SrsBuffer* buf)
|
|||
|
||||
buf->write_2bytes(0xBEDE);
|
||||
|
||||
// Write length.
|
||||
int len = 0;
|
||||
//TODO: When add new rtp extension, it should add the extension length into len
|
||||
|
||||
if (twcc_.has_twcc_ext()) {
|
||||
len += twcc_.nb_bytes();
|
||||
}
|
||||
|
||||
int padding_count = (len % 4 == 0) ? 0 : (4 - len % 4);
|
||||
len += padding_count;
|
||||
|
||||
buf->write_2bytes(len / 4);
|
||||
|
||||
// Write extensions.
|
||||
if (twcc_.has_twcc_ext()) {
|
||||
if (srs_success != (err = twcc_.encode(buf))) {
|
||||
return srs_error_wrap(err, "encode twcc extension");
|
||||
}
|
||||
}
|
||||
|
||||
// add padding
|
||||
while(padding_count > 0) {
|
||||
buf->write_1bytes(0);
|
||||
padding_count--;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
|
|
@ -379,20 +379,6 @@ srs_error_t srs_write_large_iovs(ISrsProtocolReadWriter* skt, iovec* iovs, int s
|
|||
return err;
|
||||
}
|
||||
|
||||
string srs_join_vector_string(vector<string>& vs, string separator)
|
||||
{
|
||||
string str = "";
|
||||
|
||||
for (int i = 0; i < (int)vs.size(); i++) {
|
||||
str += vs.at(i);
|
||||
if (i != (int)vs.size() - 1) {
|
||||
str += separator;
|
||||
}
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
bool srs_is_ipv4(string domain)
|
||||
{
|
||||
for (int i = 0; i < (int)domain.length(); i++) {
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
|
||||
#include <srs_kernel_consts.hpp>
|
||||
|
||||
|
@ -108,7 +109,20 @@ extern std::string srs_generate_rtmp_url(std::string server, int port, std::stri
|
|||
extern srs_error_t srs_write_large_iovs(ISrsProtocolReadWriter* skt, iovec* iovs, int size, ssize_t* pnwrite = NULL);
|
||||
|
||||
// join string in vector with indicated separator
|
||||
extern std::string srs_join_vector_string(std::vector<std::string>& vs, std::string separator);
|
||||
template <typename T>
|
||||
std::string srs_join_vector_string(std::vector<T>& vs, std::string separator)
|
||||
{
|
||||
std::stringstream ss;
|
||||
|
||||
for (int i = 0; i < (int)vs.size(); i++) {
|
||||
ss << vs.at(i);
|
||||
if (i != (int)vs.size() - 1) {
|
||||
ss << separator;
|
||||
}
|
||||
}
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
// Whether domain is an IPv4 address.
|
||||
extern bool srs_is_ipv4(std::string domain);
|
||||
|
|
|
@ -28,6 +28,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|||
#include <srs_app_server.hpp>
|
||||
#include <srs_app_config.hpp>
|
||||
#include <srs_app_log.hpp>
|
||||
#include <srs_app_rtc_dtls.hpp>
|
||||
|
||||
#include <string>
|
||||
using namespace std;
|
||||
|
@ -57,6 +58,10 @@ srs_error_t prepare_main() {
|
|||
return srs_error_wrap(err, "init st");
|
||||
}
|
||||
|
||||
if ((err = _srs_rtc_dtls_certificate->initialize()) != srs_success) {
|
||||
return srs_error_wrap(err, "rtc dtls certificate initialize");
|
||||
}
|
||||
|
||||
srs_freep(_srs_context);
|
||||
_srs_context = new SrsThreadContext();
|
||||
|
||||
|
|
|
@ -56,19 +56,24 @@ extern int _srs_tmp_port;
|
|||
extern srs_utime_t _srs_tmp_timeout;
|
||||
|
||||
// For errors.
|
||||
// @remark we directly delete the err, because we allow user to append message if fail.
|
||||
#define HELPER_EXPECT_SUCCESS(x) \
|
||||
if ((err = x) != srs_success) fprintf(stderr, "err %s", srs_error_desc(err).c_str()); \
|
||||
EXPECT_TRUE(srs_success == err); \
|
||||
srs_freep(err)
|
||||
#define HELPER_EXPECT_FAILED(x) EXPECT_TRUE(srs_success != (err = x)); srs_freep(err)
|
||||
if (err != srs_success) delete err; \
|
||||
EXPECT_TRUE(srs_success == err)
|
||||
#define HELPER_EXPECT_FAILED(x) \
|
||||
if ((err = x) != srs_success) delete err; \
|
||||
EXPECT_TRUE(srs_success != err)
|
||||
|
||||
// For errors, assert.
|
||||
// @remark The err is leak when error, but it's ok in utest.
|
||||
// @remark we directly delete the err, because we allow user to append message if fail.
|
||||
#define HELPER_ASSERT_SUCCESS(x) \
|
||||
if ((err = x) != srs_success) fprintf(stderr, "err %s", srs_error_desc(err).c_str()); \
|
||||
ASSERT_TRUE(srs_success == err); \
|
||||
srs_freep(err)
|
||||
#define HELPER_ASSERT_FAILED(x) ASSERT_TRUE(srs_success != (err = x)); srs_freep(err)
|
||||
if (err != srs_success) delete err; \
|
||||
ASSERT_TRUE(srs_success == err)
|
||||
#define HELPER_ASSERT_FAILED(x) \
|
||||
if ((err = x) != srs_success) delete err; \
|
||||
ASSERT_TRUE(srs_success != err)
|
||||
|
||||
// For init array data.
|
||||
#define HELPER_ARRAY_INIT(buf, sz, val) \
|
||||
|
|
|
@ -29,10 +29,314 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|||
#include <srs_kernel_rtc_rtp.hpp>
|
||||
#include <srs_app_rtc_source.hpp>
|
||||
#include <srs_app_rtc_conn.hpp>
|
||||
#include <srs_kernel_codec.hpp>
|
||||
|
||||
#include <vector>
|
||||
using namespace std;
|
||||
|
||||
extern SSL_CTX* srs_build_dtls_ctx(SrsDtlsVersion version);
|
||||
|
||||
class MockDtls
|
||||
{
|
||||
public:
|
||||
SSL_CTX* dtls_ctx;
|
||||
SSL* dtls;
|
||||
BIO* bio_in;
|
||||
BIO* bio_out;
|
||||
ISrsDtlsCallback* callback_;
|
||||
bool handshake_done_for_us;
|
||||
SrsDtlsRole role_;
|
||||
SrsDtlsVersion version_;
|
||||
public:
|
||||
MockDtls(ISrsDtlsCallback* callback);
|
||||
virtual ~MockDtls();
|
||||
srs_error_t initialize(std::string role, std::string version);
|
||||
srs_error_t start_active_handshake();
|
||||
srs_error_t on_dtls(char* data, int nb_data);
|
||||
srs_error_t do_handshake();
|
||||
};
|
||||
|
||||
MockDtls::MockDtls(ISrsDtlsCallback* callback)
|
||||
{
|
||||
dtls_ctx = NULL;
|
||||
dtls = NULL;
|
||||
|
||||
callback_ = callback;
|
||||
handshake_done_for_us = false;
|
||||
|
||||
role_ = SrsDtlsRoleServer;
|
||||
version_ = SrsDtlsVersionAuto;
|
||||
}
|
||||
|
||||
MockDtls::~MockDtls()
|
||||
{
|
||||
if (dtls_ctx) {
|
||||
SSL_CTX_free(dtls_ctx);
|
||||
dtls_ctx = NULL;
|
||||
}
|
||||
|
||||
if (dtls) {
|
||||
SSL_free(dtls);
|
||||
dtls = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
srs_error_t MockDtls::initialize(std::string role, std::string version)
|
||||
{
|
||||
role_ = SrsDtlsRoleServer;
|
||||
if (role == "active") {
|
||||
role_ = SrsDtlsRoleClient;
|
||||
}
|
||||
|
||||
if (version == "dtls1.0") {
|
||||
version_ = SrsDtlsVersion1_0;
|
||||
} else if (version == "dtls1.2") {
|
||||
version_ = SrsDtlsVersion1_2;
|
||||
} else {
|
||||
version_ = SrsDtlsVersionAuto;
|
||||
}
|
||||
|
||||
dtls_ctx = srs_build_dtls_ctx(version_);
|
||||
dtls = SSL_new(dtls_ctx);
|
||||
srs_assert(dtls);
|
||||
|
||||
if (role_ == SrsDtlsRoleClient) {
|
||||
SSL_set_connect_state(dtls);
|
||||
SSL_set_max_send_fragment(dtls, kRtpPacketSize);
|
||||
} else {
|
||||
SSL_set_accept_state(dtls);
|
||||
}
|
||||
|
||||
bio_in = BIO_new(BIO_s_mem());
|
||||
srs_assert(bio_in);
|
||||
|
||||
bio_out = BIO_new(BIO_s_mem());
|
||||
srs_assert(bio_out);
|
||||
|
||||
SSL_set_bio(dtls, bio_in, bio_out);
|
||||
return srs_success;
|
||||
}
|
||||
|
||||
srs_error_t MockDtls::start_active_handshake()
|
||||
{
|
||||
if (role_ == SrsDtlsRoleClient) {
|
||||
return do_handshake();
|
||||
}
|
||||
return srs_success;
|
||||
}
|
||||
|
||||
srs_error_t MockDtls::on_dtls(char* data, int nb_data)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
srs_assert(BIO_reset(bio_in) == 1);
|
||||
srs_assert(BIO_reset(bio_out) == 1);
|
||||
srs_assert(BIO_write(bio_in, data, nb_data) > 0);
|
||||
|
||||
if ((err = do_handshake()) != srs_success) {
|
||||
return srs_error_wrap(err, "do handshake");
|
||||
}
|
||||
|
||||
while (BIO_ctrl_pending(bio_in) > 0) {
|
||||
char buf[8092];
|
||||
int nb = SSL_read(dtls, buf, sizeof(buf));
|
||||
if (nb <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((err = callback_->on_dtls_application_data(buf, nb)) != srs_success) {
|
||||
return srs_error_wrap(err, "on DTLS data, size=%u", nb);
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t MockDtls::do_handshake()
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
int r0 = SSL_do_handshake(dtls);
|
||||
int r1 = SSL_get_error(dtls, r0);
|
||||
if (r0 < 0 && (r1 != SSL_ERROR_NONE && r1 != SSL_ERROR_WANT_READ && r1 != SSL_ERROR_WANT_WRITE)) {
|
||||
return srs_error_new(ERROR_RTC_DTLS, "handshake r0=%d, r1=%d", r0, r1);
|
||||
}
|
||||
if (r1 == SSL_ERROR_NONE) {
|
||||
handshake_done_for_us = true;
|
||||
}
|
||||
|
||||
uint8_t* data = NULL;
|
||||
int size = BIO_get_mem_data(bio_out, &data);
|
||||
|
||||
if (size > 0 && (err = callback_->write_dtls_data(data, size)) != srs_success) {
|
||||
return srs_error_wrap(err, "dtls send size=%u", size);
|
||||
}
|
||||
|
||||
if (handshake_done_for_us) {
|
||||
return callback_->on_dtls_handshake_done();
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
class MockDtlsCallback : virtual public ISrsDtlsCallback, virtual public ISrsCoroutineHandler
|
||||
{
|
||||
public:
|
||||
SrsDtls* peer;
|
||||
MockDtls* peer2;
|
||||
SrsCoroutine* trd;
|
||||
srs_error_t r0;
|
||||
bool done;
|
||||
std::vector<SrsSample> samples;
|
||||
MockDtlsCallback();
|
||||
virtual ~MockDtlsCallback();
|
||||
virtual srs_error_t on_dtls_handshake_done();
|
||||
virtual srs_error_t on_dtls_application_data(const char* data, const int len);
|
||||
virtual srs_error_t write_dtls_data(void* data, int size);
|
||||
virtual srs_error_t cycle();
|
||||
};
|
||||
|
||||
MockDtlsCallback::MockDtlsCallback()
|
||||
{
|
||||
peer = NULL;
|
||||
peer2 = NULL;
|
||||
r0 = srs_success;
|
||||
done = false;
|
||||
trd = new SrsSTCoroutine("mock", this);
|
||||
srs_assert(trd->start() == srs_success);
|
||||
}
|
||||
|
||||
MockDtlsCallback::~MockDtlsCallback()
|
||||
{
|
||||
srs_freep(trd);
|
||||
srs_freep(r0);
|
||||
for (vector<SrsSample>::iterator it = samples.begin(); it != samples.end(); ++it) {
|
||||
delete[] it->bytes;
|
||||
}
|
||||
}
|
||||
|
||||
srs_error_t MockDtlsCallback::on_dtls_handshake_done()
|
||||
{
|
||||
done = true;
|
||||
return srs_success;
|
||||
}
|
||||
|
||||
srs_error_t MockDtlsCallback::on_dtls_application_data(const char* data, const int len)
|
||||
{
|
||||
return srs_success;
|
||||
}
|
||||
|
||||
srs_error_t MockDtlsCallback::write_dtls_data(void* data, int size)
|
||||
{
|
||||
char* cp = new char[size];
|
||||
memcpy(cp, data, size);
|
||||
|
||||
samples.push_back(SrsSample((char*)cp, size));
|
||||
return srs_success;
|
||||
}
|
||||
|
||||
srs_error_t MockDtlsCallback::cycle()
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
while (err == srs_success) {
|
||||
if ((err = trd->pull()) != srs_success) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (samples.empty()) {
|
||||
srs_usleep(0);
|
||||
continue;
|
||||
}
|
||||
|
||||
SrsSample p = *samples.erase(samples.begin());
|
||||
if (peer) {
|
||||
err = peer->on_dtls((char*)p.bytes, p.size);
|
||||
} else {
|
||||
err = peer2->on_dtls((char*)p.bytes, p.size);
|
||||
}
|
||||
|
||||
srs_freepa(p.bytes);
|
||||
}
|
||||
|
||||
// Copy it for utest to check it.
|
||||
r0 = srs_error_copy(err);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
// Wait for mock io to done, try to switch to coroutine many times.
|
||||
#define mock_wait_dtls_io_done(cio, sio) \
|
||||
for (int i = 0; i < 100 && (!cio.samples.empty() || !sio.samples.empty()); i++) { \
|
||||
srs_usleep(0 * SRS_UTIME_MILLISECONDS); \
|
||||
}
|
||||
|
||||
struct DTLSServerFlowCase
|
||||
{
|
||||
int id;
|
||||
|
||||
string ClientVersion;
|
||||
string ServerVersion;
|
||||
|
||||
bool ClientDone;
|
||||
bool ServerDone;
|
||||
|
||||
bool ClientError;
|
||||
bool ServerError;
|
||||
};
|
||||
|
||||
std::ostream& operator<< (std::ostream& stream, const DTLSServerFlowCase& c)
|
||||
{
|
||||
stream << "Case #" << c.id
|
||||
<< ", client(" << c.ClientVersion << ",done=" << c.ClientDone << ",err=" << c.ClientError << ")"
|
||||
<< ", server(" << c.ServerVersion << ",done=" << c.ServerDone << ",err=" << c.ServerError << ")";
|
||||
return stream;
|
||||
}
|
||||
|
||||
VOID TEST(KernelRTCTest, DTLSServerFlowTest)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
DTLSServerFlowCase cases[] = {
|
||||
// OK, Client, Server: DTLS v1.0
|
||||
{0, "dtls1.0", "dtls1.0", true, true, false, false},
|
||||
// OK, Client, Server: DTLS v1.2
|
||||
{1, "dtls1.2", "dtls1.2", true, true, false, false},
|
||||
// OK, Client: DTLS v1.0, Server: DTLS auto(v1.0 or v1.2).
|
||||
{2, "dtls1.0", "auto", true, true, false, false},
|
||||
// OK, Client: DTLS v1.2, Server: DTLS auto(v1.0 or v1.2).
|
||||
{3, "dtls1.2", "auto", true, true, false, false},
|
||||
// OK, Client: DTLS auto(v1.0 or v1.2), Server: DTLS v1.0
|
||||
{4, "auto", "dtls1.0", true, true, false, false},
|
||||
// OK, Client: DTLS auto(v1.0 or v1.2), Server: DTLS v1.0
|
||||
{5, "auto", "dtls1.2", true, true, false, false},
|
||||
// Fail, Client: DTLS v1.0, Server: DTLS v1.2
|
||||
{6, "dtls1.0", "dtls1.2", false, false, false, true},
|
||||
// Fail, Client: DTLS v1.2, Server: DTLS v1.0
|
||||
{7, "dtls1.2", "dtls1.0", false, false, true, false},
|
||||
};
|
||||
|
||||
for (int i = 0; i < (int)(sizeof(cases) / sizeof(DTLSServerFlowCase)); i++) {
|
||||
DTLSServerFlowCase c = cases[i];
|
||||
|
||||
MockDtlsCallback cio; MockDtls client(&cio);
|
||||
MockDtlsCallback sio; SrsDtls server(&sio);
|
||||
cio.peer = &server; sio.peer2 = &client;
|
||||
HELPER_EXPECT_SUCCESS(client.initialize("active", c.ClientVersion)) << c;
|
||||
HELPER_EXPECT_SUCCESS(server.initialize("passive", c.ServerVersion)) << c;
|
||||
|
||||
HELPER_EXPECT_SUCCESS(client.start_active_handshake()) << c;
|
||||
mock_wait_dtls_io_done(cio, sio);
|
||||
|
||||
// Note that the cio error is generated from server, vice versa.
|
||||
EXPECT_EQ(c.ClientError, sio.r0 != srs_success) << c;
|
||||
EXPECT_EQ(c.ServerError, cio.r0 != srs_success) << c;
|
||||
|
||||
EXPECT_EQ(c.ClientDone, cio.done) << c;
|
||||
EXPECT_EQ(c.ServerDone, sio.done) << c;
|
||||
}
|
||||
}
|
||||
|
||||
VOID TEST(KernelRTCTest, SequenceCompare)
|
||||
{
|
||||
if (true) {
|
||||
|
@ -129,6 +433,33 @@ VOID TEST(KernelRTCTest, SequenceCompare)
|
|||
}
|
||||
}
|
||||
|
||||
VOID TEST(KernelRTCTest, DecodeHeaderWithPadding)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
// RTP packet cipher with padding.
|
||||
uint8_t data[] = {
|
||||
0xb0, 0x66, 0x0a, 0x97, 0x7e, 0x32, 0x10, 0xee, 0x7d, 0xe6, 0xd0, 0xe6, 0xbe, 0xde, 0x00, 0x01, 0x31, 0x00, 0x16, 0x00, 0x25, 0xcd, 0xef, 0xce, 0xd7, 0x24, 0x57, 0xd9, 0x3c, 0xfd, 0x0f, 0x77, 0xea, 0x89, 0x61, 0xcb, 0x67, 0xa1, 0x65, 0x4a, 0x7d, 0x1f, 0x10, 0x4e, 0xed, 0x5e, 0x74, 0xe8, 0x7e, 0xce, 0x4d, 0xcf, 0xd5, 0x58, 0xd1, 0x2c, 0x30, 0xf1, 0x26, 0x62, 0xd3, 0x0c, 0x6a, 0x48,
|
||||
0x29, 0x83, 0xd2, 0x3d, 0x30, 0xa1, 0x7c, 0x6f, 0xa1, 0x5c, 0x9f, 0x08, 0x43, 0x50, 0x34, 0x2b, 0x3c, 0xa1, 0xf0, 0xb0, 0xe2, 0x0e, 0xc8, 0xf9, 0x79, 0x06, 0x51, 0xfe, 0xbb, 0x13, 0x54, 0x3e, 0xb4, 0x37, 0x91, 0x96, 0x94, 0xb7, 0x61, 0x2e, 0x97, 0x09, 0xb8, 0x27, 0x10, 0x6a, 0x2e, 0xe0, 0x62, 0xe4, 0x37, 0x41, 0xab, 0x4f, 0xbf, 0x06, 0x0a, 0x89, 0x80, 0x18, 0x0d, 0x6e, 0x0a, 0xd1,
|
||||
0x9f, 0xf1, 0xdd, 0x12, 0xbd, 0x1a, 0x70, 0x72, 0x33, 0xcc, 0xaa, 0x82, 0xdf, 0x92, 0x90, 0x45, 0xda, 0x3e, 0x88, 0x1c, 0x63, 0x83, 0xbc, 0xc8, 0xff, 0xfd, 0x64, 0xe3, 0xd4, 0x68, 0xe6, 0xc8, 0xdc, 0x81, 0x72, 0x5f, 0x38, 0x5b, 0xab, 0x63, 0x7b, 0x96, 0x03, 0x03, 0x54, 0xc5, 0xe6, 0x35, 0xf6, 0x86, 0xcc, 0xac, 0x74, 0xb0, 0xf4, 0x07, 0x9e, 0x19, 0x30, 0x4f, 0x90, 0xd6, 0xdb, 0x8b,
|
||||
0x0d, 0xcb, 0x76, 0x71, 0x55, 0xc7, 0x4a, 0x6e, 0x1b, 0xb4, 0x42, 0xf4, 0xae, 0x81, 0x17, 0x08, 0xb7, 0x50, 0x61, 0x5a, 0x42, 0xde, 0x1f, 0xf3, 0xfd, 0xe2, 0x30, 0xff, 0xb7, 0x07, 0xdd, 0x4b, 0xb1, 0x00, 0xd9, 0x6c, 0x43, 0xa0, 0x9a, 0xfa, 0xbb, 0xec, 0xdf, 0x51, 0xce, 0x33, 0x79, 0x4b, 0xa7, 0x02, 0xf3, 0x96, 0x62, 0x42, 0x25, 0x28, 0x85, 0xa7, 0xe7, 0xd1, 0xd3, 0xf3,
|
||||
};
|
||||
|
||||
// If not plaintext, the padding in body is invalid,
|
||||
// so it will fail if decoding the header with padding.
|
||||
if (true) {
|
||||
SrsBuffer b((char*)data, sizeof(data)); SrsRtpHeader h;
|
||||
HELPER_EXPECT_FAILED(h.decode(&b));
|
||||
}
|
||||
|
||||
// Should ok if ignore padding.
|
||||
if (true) {
|
||||
SrsBuffer b((char*)data, sizeof(data)); SrsRtpHeader h;
|
||||
h.ignore_padding(true);
|
||||
HELPER_EXPECT_SUCCESS(h.decode(&b));
|
||||
}
|
||||
}
|
||||
|
||||
VOID TEST(KernelRTCTest, DumpsHexToString)
|
||||
{
|
||||
if (true) {
|
||||
|
@ -164,6 +495,46 @@ VOID TEST(KernelRTCTest, DumpsHexToString)
|
|||
}
|
||||
}
|
||||
|
||||
VOID TEST(KernelRTCTest, NACKFetchRTPPacket)
|
||||
{
|
||||
SrsRtcConnection s(NULL, SrsContextId());
|
||||
SrsRtcPlayStream play(&s, SrsContextId());
|
||||
|
||||
SrsRtcTrackDescription ds;
|
||||
SrsRtcVideoSendTrack *track = new SrsRtcVideoSendTrack(&s, &ds);
|
||||
|
||||
// The RTP queue will free the packet.
|
||||
if (true) {
|
||||
SrsRtpPacket2* pkt = new SrsRtpPacket2();
|
||||
pkt->header.set_sequence(100);
|
||||
track->rtp_queue_->set(pkt->header.get_sequence(), pkt);
|
||||
}
|
||||
|
||||
// If sequence not match, packet not found.
|
||||
if (true) {
|
||||
SrsRtpPacket2* pkt = track->fetch_rtp_packet(10);
|
||||
EXPECT_TRUE(pkt == NULL);
|
||||
}
|
||||
|
||||
// The sequence matched, we got the packet.
|
||||
if (true) {
|
||||
SrsRtpPacket2* pkt = track->fetch_rtp_packet(100);
|
||||
EXPECT_TRUE(pkt != NULL);
|
||||
}
|
||||
|
||||
// NACK special case.
|
||||
if (true) {
|
||||
// The sequence is the "same", 1100%1000 is 100,
|
||||
// so we can also get it from the RTP queue.
|
||||
SrsRtpPacket2* pkt = track->rtp_queue_->at(1100);
|
||||
EXPECT_TRUE(pkt != NULL);
|
||||
|
||||
// But the track requires exactly match, so it returns NULL.
|
||||
pkt = track->fetch_rtp_packet(1100);
|
||||
EXPECT_TRUE(pkt == NULL);
|
||||
}
|
||||
}
|
||||
|
||||
extern bool srs_is_stun(const uint8_t* data, size_t size);
|
||||
extern bool srs_is_dtls(const uint8_t* data, size_t len);
|
||||
extern bool srs_is_rtp_or_rtcp(const uint8_t* data, size_t len);
|
||||
|
|
Loading…
Reference in a new issue