diff --git a/trunk/research/players/js/StrobeMediaPlayback.swf b/trunk/research/players/js/StrobeMediaPlayback.swf deleted file mode 100644 index cba589120..000000000 Binary files a/trunk/research/players/js/StrobeMediaPlayback.swf and /dev/null differ diff --git a/trunk/research/players/js/jwplayer.flash.swf b/trunk/research/players/js/jwplayer.flash.swf deleted file mode 100644 index b5889888a..000000000 Binary files a/trunk/research/players/js/jwplayer.flash.swf and /dev/null differ diff --git a/trunk/research/players/js/jwplayer.html5.js b/trunk/research/players/js/jwplayer.html5.js deleted file mode 100644 index 8d041732f..000000000 --- a/trunk/research/players/js/jwplayer.html5.js +++ /dev/null @@ -1,194 +0,0 @@ -(function(f){f.html5={};f.html5.version="6.4.3359"})(jwplayer); -(function(f){function h(a){return function(){a("Error loading file")}}function e(a,b,c,e){return function(){try{var g=a.responseXML;if(g&&g.firstChild)return c(a)}catch(d){}(g=f.parseXML(a.responseText))&&g.firstChild?(a=f.extend({},a,{responseXML:g}),c(a)):e&&e(a.responseText?"Invalid XML":b)}}var d=window;f.serialize=function(a){return null==a?null:"true"==a.toString().toLowerCase()?!0:"false"==a.toString().toLowerCase()?!1:isNaN(Number(a))||5c?"0":"")+c+":"+(10>a?"0":"")+a}return"00:00"};f.seconds=function(a){a=a.replace(",",".");var b=a.split(":"),c=0;"s"==a.substr(-1)?c=Number(a.substr(0, -a.length-1)):"m"==a.substr(-1)?c=60*Number(a.substr(0,a.length-1)):"h"==a.substr(-1)?c=3600*Number(a.substr(0,a.length-1)):1e.indexOf("url")?"url("+e+")":e+k;else switch(a){case "z-index":case "opacity":b=e+k;break a;default:b=a.match(/color/i)?"#"+f.pad(e.toString(16).replace(/^0x/i,""),6)+k:0===e?0+k:Math.ceil(e)+"px"+k}}}g(c[d][a])&&!g(b)?delete c[d][a]:g(b)&&(c[d][a]=b)}); -0p?(c*=g,k*=g):(c*=p,k*=p);case h.NONE:g=p=1;case h.EXACTFIT:l=!0;break;default:g>p?0.95=B.length||(B[x].data?w.populate(B[x].data):(H=a=B[x].file,h.ajax(a,q,n)),s(!1))}function A(){var a=[];a.push({label:"Off"});for(var b=0;b=q)){b=c;break}-1==b?a(""):b!=l&&(l=b,a(g[c].text))}function f(a,b){h(b, -function(b,c){a.style[b]=c})}var g,p,j,l,q,n="visible",r;this.hide=function(){f(p,{display:"none"});r&&(clearInterval(r),r=null)};this.populate=function(a){l=-1;g=a;c()};this.resize=function(){b()};p=document.createElement("div");j=document.createElement("span");p.appendChild(j);d.appendChild(p);f(p,{display:"block",height:"auto",position:"absolute",bottom:"20px",textAlign:"center",width:"100%"});f(j,{color:"#"+e.color.substr(-6),display:"inline-block",fontFamily:e.fontFamily,fontStyle:e.fontStyle, -fontWeight:e.fontWeight,height:"auto",margin:"auto",position:"relative",textAlign:"center",textDecoration:e.textDecoration,wordWrap:"break-word",width:"auto"});e.back?f(j,{background:"#000"}):f(j,{textShadow:"-2px 0px 1px #000,2px 0px 1px #000,0px -2px 1px #000,0px 2px 1px #000,-1px 1px 1px #000,1px 1px 1px #000,1px -1px 1px #000,1px 1px 1px #000"});this.show=function(){f(p,{display:"block"});r||(r=setInterval(b,250));b()};this.update=function(a){q=a;g&&c()}}})(jwplayer.html5); -(function(f){var h=f.html5,e=f.utils,d=f.events,a=d.state,b=e.css;f=e.transitionStyle;var c="button",k="text",g="divider",p="slider",j="100%",l={display:"none"},q={display:v},n=!1,r=!0,s=null,v=void 0,u=window,A=document;h.controlbar=function(f,y){function t(a,b,c){return{name:a,type:b,className:c}}function w(a){var b=n,c;K.elapsed&&(c=e.timeFormat(a.position),K.elapsed.innerHTML=c,b=c.length!=e.timeFormat(va).length);K.duration&&(c=e.timeFormat(a.duration),K.duration.innerHTML=c,b=b||c.length!=e.timeFormat(ia).length); -0ka.right?a.offsetX(ka.right-b.right):b.lefta&&(a=0);0.9da.maxwidth;c=wa?0:da.margin;b(D(),{left:a?"50%":c,right:a?v:c,"margin-left":a?N.clientWidth/-2:v,width:a?j:v});ka=e.bounds(N);e.foreach(Ea,function(a,b){pa(b)})};ha.audioMode=function(a){a!=wa&&(wa=a,b(D(".jwfullscreen"),{display:a?"none":v}),b(D(".jwhd"),{display:a?"none":v}),b(D(".jwcc"),{display:a?"none":v}),ya())};ha.element=function(){return N};ha.margin=function(){return parseInt(da.margin)}; -ha.height=function(){return U};ha.show=function(){ha.visible||(clearTimeout(Ha),Ha=v,ha.visible=!0,N.style.display="inline-block",ya(),z(),Ha=setTimeout(function(){N.style.opacity=1},10))};ha.hide=function(){ha.visible&&(ha.visible=!1,N.style.opacity=0,clearTimeout(Ha),Ha=v,Ha=setTimeout(function(){N.style.display="none"},150))};K={};G=f;$=G.id+"_controlbar";ia=va=0;N=L();N.id=$;N.className="jwcontrolbar";R=G.skin;ga=R.getComponentLayout("controlbar");ga||(ga=W.layout);e.clearCss("#"+$);H();var Ia= -I("capLeft"),T=I("capRight"),ab=I("background",{position:"absolute",left:O("capLeft").width,right:O("capRight").width,"background-repeat":"repeat-x"},r);ab&&N.appendChild(ab);Ia&&N.appendChild(Ia);Ga("left");Ga("center");Ga("right");N.appendChild(xa.left);N.appendChild(xa.center);N.appendChild(xa.right);K.hd&&(la=new h.menu("hd",$+"_hd",R,Ta),ba(la,K.hd,oa,ua),Ea.hd=la);K.cc&&(ma=new h.menu("cc",$+"_cc",R,Ua),ba(ma,K.cc,aa,P),Ea.cc=ma);K.mute&&(K.volume&&K.volume.vertical)&&(ra=new h.overlay($+"_volumeoverlay", -R),ra.setContents(K.volume),ba(ra,K.mute,X),Ea.volume=ra);K.fullscreen&&(Ma=new h.overlay($+"_fullscreenoverlay",R),Ia=A.createElement("div"),Ia.className="jwoverlaytext",Ia.innerHTML="Fullscreen",Ma.setContents(Ia),ba(Ma,K.fullscreen,ea),Ea.fullscreen=Ma);b(D(".jwright"),{right:O("capRight").width});T&&N.appendChild(T);G.jwAddEventListener(d.JWPLAYER_MEDIA_TIME,w);G.jwAddEventListener(d.JWPLAYER_PLAYER_STATE,function(c){switch(c.newstate){case a.BUFFERING:case a.PLAYING:b(D(".jwtimeSliderThumb"), -{opacity:1});Z("play",r);break;case a.PAUSED:ta||Z("play",n);break;case a.IDLE:Z("play",n),b(D(".jwtimeSliderThumb"),{opacity:0}),K.timeRail&&(K.timeRail.className="jwrail",setTimeout(function(){K.timeRail.className+=" jwsmooth"},100)),Da(0),w({position:0,duration:0})}});G.jwAddEventListener(d.JWPLAYER_PLAYLIST_ITEM,function(a){a=G.jwGetPlaylist()[a.index].tracks;if("array"==e.typeOf(a))for(var b=0;bb.left&&a.offsetX(c.left-b.left+8);n.show();h.foreach(y,function(a,b){a!=g&&b.hide()})},!1);j.addEventListener("mouseout",function(){s=setTimeout(n.hide,100)},!1);t.appendChild(n.element());y[g]=n}A++;r()}};z.removeButton=function(a){m[a]&&(w.removeChild(m[a].element),w.removeChild(m[a].divider),delete m[a],A--,r())};z.numButtons=function(){return A};z.visible=!1;t=q("div","jwdock");w=q("div","jwdockbuttons");t.appendChild(w);t.id=v;var x=n("button"),B=n("buttonOver"), -C=n("buttonActive");x&&(e(l(),{height:x.height,padding:s.margin}),e(a,{height:x.height}),e(l("button"),{width:x.width,cursor:"pointer",border:c,background:x.src}),B.src&&e(l("button:hover"),{background:B.src}),C.src&&e(l("button:active"),{background:C.src}),e(l("button\x3ediv"),{opacity:s.iconalpha}),e(l("button:hover\x3ediv"),{opacity:s.iconalphaover}),e(l("button:active\x3ediv"),{opacity:s.iconalphaactive}),e(l(".jwoverlay"),{top:s.margin+x.height}),j("capLeft",w),j("capRight",w),j("divider")); -setTimeout(function(){d(t)})};e(".jwdock",{opacity:0,display:c});e(".jwdock \x3e *",{height:"100%","float":"left"});e(".jwdock \x3e .jwoverlay",{height:"auto","float":c,"z-index":99});e(a+" button",{position:"relative"});e(a+" \x3e *",{height:"100%","float":"left"});e(a+" .divider",{display:c});e(a+" button ~ .divider",{display:k});e(a+" .capLeft, "+a+" .capRight",{display:c});e(a+" .capRight",{"float":"right"});e(a+" button \x3e div",{left:0,right:0,top:0,bottom:0,margin:5,position:"absolute","background-position":"center", -"background-repeat":"no-repeat"});h.transitionStyle(".jwdock","background .15s, opacity .15s");h.transitionStyle(".jwdock .jwoverlay","opacity .15s");h.transitionStyle(a+" button div","opacity .15s")})(jwplayer.html5); -(function(f){var h=jwplayer,e=h.utils,d=h.events,a=d.state,b=h.playlist;f.instream=function(c,h,g,p){function j(a){E&&H.sendEvent(a.type,a);I=!0;F.jwInstreamDestroy(!1)}function l(a){E&&E&&H.sendEvent(a.type,a)}function q(){E&&x.play()}function n(){E&&setTimeout(function(){F.jwInstreamDestroy(!0)},10)}function r(a){a.width&&a.height&&m.resizeMedia()}function s(){B&&B.redraw();C&&C.redraw()}var v={controlbarseekable:"never",controlbarpausable:!0,controlbarstoppable:!0,playlistclickable:!0},u,A,m=g, -y,t,w,z,x,B,C,E=!1,H,D,L,F=this,I=!1,Q=!0;this.load=function(g,M){e.isAndroid(2.3)?j({type:d.JWPLAYER_ERROR,message:"Error loading instream: Cannot play instream on Android 2.3"}):(E=!0,A=e.extend(v,M),u=new b.item(g),D=document.createElement("div"),D.id=F.id+"_instream_container",y=p.detachMedia(),x=new f.video(y),x.addGlobalListener(l),x.addEventListener(d.JWPLAYER_MEDIA_META,r),x.addEventListener(d.JWPLAYER_MEDIA_COMPLETE,n),x.addEventListener(d.JWPLAYER_MEDIA_BUFFER_FULL,q),x.attachMedia(),x.mute(h.mute), -x.volume(h.volume),L=new f.model({},x),L.setVolume(h.volume),L.setMute(h.mute),L.addEventListener(d.JWPLAYER_ERROR,j),z=h.playlist[h.item],w=h.getVideo().checkComplete()?a.IDLE:c.jwGetState(),p.checkBeforePlay()&&(w=a.PLAYING,Q=!1),t=y.currentTime,L.setPlaylist([g]),I||((w==a.BUFFERING||w==a.PLAYING)&&y.pause(),C=new f.display(F),C.setAlternateClickHandler(function(b){L.state==a.PAUSED?F.jwInstreamPlay():(F.jwInstreamPause(),E&&H.sendEvent(d.JWPLAYER_INSTREAM_CLICK,b))}),D.appendChild(C.element()), -e.isMobile()||(B=new f.controlbar(F),D.appendChild(B.element()),B.show()),m.setupInstream(D,y),s(),x.load(L.playlist[0])))};this.jwInstreamDestroy=function(b){if(E){E=!1;w!=a.IDLE?x.load(z,!1):x.stop();H.resetEventListeners();I||C.revertAlternateClickHandler();x.detachMedia();m.destroyInstream();if(B)try{B.element().parentNode.removeChild(B.getDisplayElement())}catch(c){}H.sendEvent(d.JWPLAYER_INSTREAM_DESTROYED,{reason:b?"complete":"destroyed"});p.attachMedia();if(w==a.BUFFERING||w==a.PLAYING)y.play(), -h.playlist[h.item]==z&&Q&&h.getVideo().seek(t)}};this.jwInstreamAddEventListener=function(a,b){H.addEventListener(a,b)};this.jwInstreamRemoveEventListener=function(a,b){H.removeEventListener(a,b)};this.jwInstreamPlay=function(){E&&(x.play(!0),h.state=jwplayer.events.state.PLAYING,C.show())};this.jwInstreamPause=function(){E&&(x.pause(!0),h.state=jwplayer.events.state.PAUSED,C.show())};this.jwInstreamSeek=function(a){E&&x.seek(a)};this.jwPlay=function(){"true"==A.controlbarpausable.toString().toLowerCase()&& -this.jwInstreamPlay()};this.jwPause=function(){"true"==A.controlbarpausable.toString().toLowerCase()&&this.jwInstreamPause()};this.jwStop=function(){"true"==A.controlbarstoppable.toString().toLowerCase()&&(this.jwInstreamDestroy(),c.jwStop())};this.jwSeek=function(a){switch(A.controlbarseekable.toLowerCase()){case "always":this.jwInstreamSeek(a);break;case "backwards":L.position>a&&this.jwInstreamSeek(a)}};this.jwSeekDrag=function(a){L.seekDrag(a)};this.jwGetPosition=function(){};this.jwGetDuration= -function(){};this.jwGetWidth=c.jwGetWidth;this.jwGetHeight=c.jwGetHeight;this.jwGetFullscreen=c.jwGetFullscreen;this.jwSetFullscreen=c.jwSetFullscreen;this.jwGetVolume=function(){return h.volume};this.jwSetVolume=function(a){L.setVolume(a);c.jwSetVolume(a)};this.jwGetMute=function(){return h.mute};this.jwSetMute=function(a){L.setMute(a);c.jwSetMute(a)};this.jwGetState=function(){return h.state};this.jwGetPlaylist=function(){return[u]};this.jwGetPlaylistIndex=function(){return 0};this.jwGetStretching= -function(){return h.config.stretching};this.jwAddEventListener=function(a,b){H.addEventListener(a,b)};this.jwRemoveEventListener=function(a,b){H.removeEventListener(a,b)};this.jwSetCurrentQuality=function(){};this.jwGetQualityLevels=function(){return[]};this.skin=c.skin;this.id=c.id+"_instream";H=new d.eventdispatcher;c.jwAddEventListener(d.JWPLAYER_RESIZE,s);c.jwAddEventListener(d.JWPLAYER_FULLSCREEN,function(a){l(a);s()});return this}})(jwplayer.html5); -(function(f){var h=f.utils,e=h.css,d=f.events.state,a=f.html5.logo=function(b,c){function k(a){h.exists(a)&&a.stopPropagation();if(!n||!j.link)g.jwGetState()==d.IDLE||g.jwGetState()==d.PAUSED?g.jwPlay():g.jwPause();n&&j.link&&(g.jwPause(),g.jwSetFullscreen(!1),window.open(j.link,j.linktarget))}var g=b,p=g.id+"_logo",j,l,q=a.defaults,n=!1;this.resize=function(){};this.element=function(){return l};this.offset=function(a){e("#"+p+" ",{"margin-bottom":a})};this.position=function(){return j.position}; -this.margin=function(){return parseInt(j.margin)};this.hide=function(a){if(j.hide||a)n=!1,l.style.visibility="hidden",l.style.opacity=0};this.show=function(){n=!0;l.style.visibility="visible";l.style.opacity=1};var r="o";g.edition&&(r=g.edition(),r="pro"==r?"p":"premium"==r?"r":"ads"==r?"a":"free"==r?"f":"o");if("o"==r||"f"==r)q.link="http://www.longtailvideo.com/jwpabout/?a\x3dl\x26v\x3d"+f.version+"\x26m\x3dh\x26e\x3d"+r;j=h.extend({},q,c);j.hide="true"==j.hide.toString();l=document.createElement("img"); -l.className="jwlogo";l.id=p;if(j.file){var q=/(\w+)-(\w+)/.exec(j.position),r={},s=j.margin;3==q.length?(r[q[1]]=s,r[q[2]]=s):r.top=r.right=s;e("#"+p+" ",r);l.src=(j.prefix?j.prefix:"")+j.file;l.onclick=k}else l.style.display="none";return this};a.defaults={prefix:h.repo(),file:"logo.png",linktarget:"_top",margin:8,hide:!1,position:"top-right"};e(".jwlogo",{cursor:"pointer",position:"absolute","z-index":100,opacity:0});h.transitionStyle(".jwlogo","visibility .15s, opacity .15s")})(jwplayer); -(function(f){var h=f.html5,e=f.utils,d=e.css,a=void 0;h.menu=function(b,c,f,g){function p(a,b){return function(){v(a);q&&q(b)}}function j(a,b){var c=document.createElement("div");a&&(c.className=a);b&&b.appendChild(c);return c}function l(b){return(b=f.getSkinElement("tooltip",b))?b:{width:0,height:0,src:a}}var q=g,n=new h.overlay(c+"_overlay",f);g=e.extend({fontcase:a,fontcolor:"#cccccc",fontsize:11,fontweight:a,activecolor:"#ffffff",overcolor:"#ffffff"},f.getComponentSettings("tooltip"));var r,s= -[];this.element=function(){return n.element()};this.addOption=function(a,b){var e=j("jwoption",r);e.id=c+"_option_"+b;e.innerHTML=a;e.addEventListener("click",p(s.length,b));s.push(e)};this.clearOptions=function(){for(;0a?(a= -0,b=!0):a=-1==a||a>c.playlist.length?c.playlist.length-1:a;if(b||a!=c.item)c.item=a,c.sendEvent(e.JWPLAYER_PLAYLIST_ITEM,{index:c.item})};c.setVolume=function(a){c.mute&&0a&&(a=0);1parseFloat(jwplayer.version))&&s("Incompatible player version");if(0===a.length)r(n);else for(b=0;bp[b].end;)b++;b==p.length&&b--;if(p[b].text)if(a=p[b].text,0>a.indexOf("://")&&(a=j?j+"/"+a:a),0x)&&Z(x);f()}},emptied:c,ended:function(){F&&C!=d.IDLE&&(Q=-1,X=!0,b(e.JWPLAYER_MEDIA_BEFORECOMPLETE), -F&&(n(d.IDLE),b(e.JWPLAYER_MEDIA_COMPLETE),X=!1))},error:function(){F&&(h.log("Error playing media: %o",m.error),L.sendEvent(e.JWPLAYER_MEDIA_ERROR,{message:"Error loading media: File could not be played"}),n(d.IDLE))},loadeddata:c,loadedmetadata:g,loadstart:c,pause:p,play:p,playing:p,progress:function(){w&&(0Q&&(Q=0);for(a=0;aa)&&(Q=a,h.saveCookie("qualityLabel",I[a].label),b(e.JWPLAYER_MEDIA_LEVEL_CHANGED, -{currentQuality:a,levels:l(I)}),a=m.currentTime,q(),M.seek(a)))};M.getCurrentQuality=function(){return Q};M.getQualityLevels=function(){return l(I)};m=a;h.foreach(v,function(a,b){m.addEventListener(a,b,!1)});m.controls=!0;m.controls=!1;m.setAttribute("x-webkit-airplay","allow");F=!0}})(jwplayer); -(function(f){var h=jwplayer.utils,e=jwplayer.events,d=e.state,a=h.css,b=h.isMobile(),c=h.isIPad(),k=h.isIPod(),g=h.isAndroid(),p=h.isIOS(),j=document,l="aspectMode",q="jwmain",n="jwvideo",r="jwplaylistcontainer",s=!0,v=!1,u="hidden",A="none",m="block";f.view=function(y,t){function w(a){a&&(a.element().addEventListener("mousemove",B,v),a.element().addEventListener("mouseout",C,v))}function z(a,b){var c=j.createElement(a);b&&(c.className=b);return c}function x(){clearTimeout(pa);if(S.jwGetState()== -d.PLAYING||S.jwGetState()==d.PAUSED)oa(),Aa||(pa=setTimeout(E,Da))}function B(){clearTimeout(pa);Aa=s}function C(){Aa=v}function E(){S.jwGetState()!=d.BUFFERING&&(G&&(!U&&!N)&&G.hide(),T&&(!ia&&!N)&&T.hide(),X());clearTimeout(pa);pa=0}function H(a){ca.sendEvent(a.type,a)}function D(b,c){h.exists(b)&&h.exists(c)&&(J.width=b,J.height=c);P.style.width=isNaN(b)?b:b+"px";-1==P.className.indexOf(l)&&(P.style.height=isNaN(c)?c:c+"px");R&&R.redraw();G&&G.redraw();W&&(W.offset(G&&0<=W.position().indexOf("bottom")? -G.height()+G.margin():0),setTimeout(function(){T&&T.offset("top-left"==W.position()?W.element().clientWidth+W.margin():0)},500));var d=J.playlistsize,f=J.playlistposition;if(K&&d&&("right"==f||"bottom"==f)){K.redraw();var g={display:m},j={};g[f]=0;j[f]=d;"right"==f?g.width=d:g.height=d;a(fa(r),g);a(fa(q),j)}L(c);F();ca.sendEvent(e.JWPLAYER_RESIZE)}function L(a){U=(!b||N)&&40>=a&&0>a.toString().indexOf("%");G&&(U?(G.audioMode(s),oa(),R.hidePreview(s),Z(),aa(v)):(G.audioMode(v),Pa(S.jwGetState()))); -W&&U&&X();P.style.backgroundColor=U?"transparent":"#000"}function F(){Y&&h.stretch(J.stretching,Y,ja.clientWidth,ja.clientHeight,Y.videoWidth,Y.videoHeight)}function I(a){if(J.fullscreen)switch(a.keyCode){case 27:Ba(v)}}function Q(a){p||(a?(P.className+=" jwfullscreen",j.getElementsByTagName("body")[0].style["overflow-y"]=u):(P.className=P.className.replace(/\s+jwfullscreen/,""),j.getElementsByTagName("body")[0].style["overflow-y"]=""))}function V(){var a;a:{a=[j.mozFullScreenElement,j.webkitCurrentFullScreenElement, -Y.webkitDisplayingFullscreen];for(var b=0;bj||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;ec&&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=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>>2&3;for(var r=0;r>>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>>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>>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(0k++;)n+="\x3d",h+="\x00";for(k=0;k>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>>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;gparseFloat(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;em.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=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;gh)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= -s.height?"transparent":"opaque";p="height width modes events primary base fallback volume".split(" ");for(t=0;t - - - SRS - - - - - - - - - - - - - - - - - - -
-
- - Usage: 输入地址后点击播放按钮 -
-
- URL: - - -
-
-
- URL: - - -
- -
- -
- - diff --git a/trunk/research/players/osmf.html b/trunk/research/players/osmf.html deleted file mode 100644 index afead964f..000000000 --- a/trunk/research/players/osmf.html +++ /dev/null @@ -1,125 +0,0 @@ - - - - SRS - - - - - - - -
-
- - Usage: 输入地址后点击播放按钮 -
-
- URL: - - -
- -
- -
- - - - - - - - - - - - diff --git a/trunk/research/players/rtc_player.html b/trunk/research/players/rtc_player.html index 2a5ed1ec4..c55e1594d 100644 --- a/trunk/research/players/rtc_player.html +++ b/trunk/research/players/rtc_player.html @@ -28,8 +28,6 @@ - -
  • GB28181
  • 源码
  • diff --git a/trunk/research/players/rtc_publisher.html b/trunk/research/players/rtc_publisher.html index 41186a61e..ba78ef08f 100644 --- a/trunk/research/players/rtc_publisher.html +++ b/trunk/research/players/rtc_publisher.html @@ -28,8 +28,6 @@ - -
  • GB28181
  • 源码
  • diff --git a/trunk/research/players/srs_bwt.html b/trunk/research/players/srs_bwt.html index 07b1ca8b5..3a96b62f2 100644 --- a/trunk/research/players/srs_bwt.html +++ b/trunk/research/players/srs_bwt.html @@ -28,8 +28,6 @@
  • SRS编码器
  • SRS会议
  • SRS测网速
  • - -
  • VLC播放器
  • SRS-GB28181
  • diff --git a/trunk/research/players/srs_chat.html b/trunk/research/players/srs_chat.html index aedc4c71e..cb6fb14f4 100644 --- a/trunk/research/players/srs_chat.html +++ b/trunk/research/players/srs_chat.html @@ -27,8 +27,6 @@
  • SRS编码器
  • SRS会议
  • SRS测网速
  • - -
  • VLC播放器
  • SRS-GB28181
  • diff --git a/trunk/research/players/srs_gb28181.html b/trunk/research/players/srs_gb28181.html index 10ca8d967..97a63e276 100644 --- a/trunk/research/players/srs_gb28181.html +++ b/trunk/research/players/srs_gb28181.html @@ -41,8 +41,6 @@ - -
  • GB28181
  • 源码
  • @@ -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); diff --git a/trunk/research/players/srs_player.html b/trunk/research/players/srs_player.html index 53e454c72..afe7c194c 100755 --- a/trunk/research/players/srs_player.html +++ b/trunk/research/players/srs_player.html @@ -38,8 +38,6 @@ - -
  • GB28181
  • 源码
  • @@ -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); diff --git a/trunk/research/players/srs_publisher.html b/trunk/research/players/srs_publisher.html index c3c33a1a0..e727412f5 100644 --- a/trunk/research/players/srs_publisher.html +++ b/trunk/research/players/srs_publisher.html @@ -27,8 +27,6 @@
  • SRS编码器
  • SRS会议
  • SRS测网速
  • - -
  • VLC播放器
  • SRS-GB28181
  • diff --git a/trunk/research/players/srs_publisher2.html b/trunk/research/players/srs_publisher2.html index bf75f3983..479026555 100644 --- a/trunk/research/players/srs_publisher2.html +++ b/trunk/research/players/srs_publisher2.html @@ -22,8 +22,6 @@
  • SRS编码器
  • SRS会议
  • SRS测网速
  • - -
  • VLC播放器
  • SRS-GB28181
  • @@ -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); diff --git a/trunk/research/players/vlc.html b/trunk/research/players/vlc.html index ebb49dd84..afc5932d0 100644 --- a/trunk/research/players/vlc.html +++ b/trunk/research/players/vlc.html @@ -24,8 +24,6 @@
  • SRS编码器
  • SRS会议
  • SRS测网速
  • - -
  • VLC播放器
  • SRS-GB28181
  • diff --git a/trunk/src/app/srs_app_listener.cpp b/trunk/src/app/srs_app_listener.cpp index ac5a0d6b3..cd847fa36 100755 --- a/trunk/src/app/srs_app_listener.cpp +++ b/trunk/src/app/srs_app_listener.cpp @@ -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); } diff --git a/trunk/src/app/srs_app_pithy_print.cpp b/trunk/src/app/srs_app_pithy_print.cpp index 3f5ca5fc8..f408d08c5 100644 --- a/trunk/src/app/srs_app_pithy_print.cpp +++ b/trunk/src/app/srs_app_pithy_print.cpp @@ -31,11 +31,13 @@ using namespace std; #include #include -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]; diff --git a/trunk/src/app/srs_app_pithy_print.hpp b/trunk/src/app/srs_app_pithy_print.hpp index 970810340..0fa3b6da2 100644 --- a/trunk/src/app/srs_app_pithy_print.hpp +++ b/trunk/src/app/srs_app_pithy_print.hpp @@ -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 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, diff --git a/trunk/src/app/srs_app_rtc_api.cpp b/trunk/src/app/srs_app_rtc_api.cpp index 52c78cf45..fd14b41d0 100644 --- a/trunk/src/app/srs_app_rtc_api.cpp +++ b/trunk/src/app/srs_app_rtc_api.cpp @@ -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"); } diff --git a/trunk/src/app/srs_app_rtc_conn.cpp b/trunk/src/app/srs_app_rtc_conn.cpp index e7ff1a8d3..f7b72048d 100644 --- a/trunk/src/app/srs_app_rtc_conn.cpp +++ b/trunk/src/app/srs_app_rtc_conn.cpp @@ -60,6 +60,14 @@ using namespace std; #define SRS_TICKID_RTCP 0 +ISrsRtcTransport::ISrsRtcTransport() +{ +} + +ISrsRtcTransport::~ISrsRtcTransport() +{ +} + SrsSecurityTransport::SrsSecurityTransport(SrsRtcConnection* s) { session_ = s; @@ -182,6 +190,98 @@ srs_error_t SrsSecurityTransport::unprotect_rtcp(const char* cipher, char* plain return srtp_->unprotect_rtcp(cipher, plaintext, nb_plaintext); } +SrsSemiSecurityTransport::SrsSemiSecurityTransport(SrsRtcConnection* s) : SrsSecurityTransport(s) +{ +} + +SrsSemiSecurityTransport::~SrsSemiSecurityTransport() +{ +} + +srs_error_t SrsSemiSecurityTransport::protect_rtp(const char* plaintext, char* cipher, int& nb_cipher) +{ + return srs_success; +} + +srs_error_t SrsSemiSecurityTransport::protect_rtcp(const char* plaintext, char* cipher, int& nb_cipher) +{ + return srs_success; +} + +srs_error_t SrsSemiSecurityTransport::protect_rtp2(void* rtp_hdr, int* len_ptr) +{ + return srs_success; +} + +SrsPlaintextTransport::SrsPlaintextTransport(SrsRtcConnection* s) +{ + session_ = s; +} + +SrsPlaintextTransport::~SrsPlaintextTransport() +{ +} + +srs_error_t SrsPlaintextTransport::initialize(SrsSessionConfig* cfg) +{ + return srs_success; +} + +srs_error_t SrsPlaintextTransport::start_active_handshake() +{ + return on_dtls_handshake_done(); +} + +srs_error_t SrsPlaintextTransport::on_dtls(char* data, int nb_data) +{ + return srs_success; +} + +srs_error_t SrsPlaintextTransport::on_dtls_handshake_done() +{ + srs_trace("RTC: DTLS handshake done."); + return session_->on_connection_established(); +} + +srs_error_t SrsPlaintextTransport::on_dtls_application_data(const char* data, const int len) +{ + return srs_success; +} + +srs_error_t SrsPlaintextTransport::write_dtls_data(void* data, int size) +{ + return srs_success; +} + +srs_error_t SrsPlaintextTransport::protect_rtp(const char* plaintext, char* cipher, int& nb_cipher) +{ + memcpy(cipher, plaintext, nb_cipher); + return srs_success; +} + +srs_error_t SrsPlaintextTransport::protect_rtcp(const char* plaintext, char* cipher, int& nb_cipher) +{ + memcpy(cipher, plaintext, nb_cipher); + return srs_success; +} + +srs_error_t SrsPlaintextTransport::protect_rtp2(void* rtp_hdr, int* len_ptr) +{ + return srs_success; +} + +srs_error_t SrsPlaintextTransport::unprotect_rtp(const char* cipher, char* plaintext, int& nb_plaintext) +{ + memcpy(plaintext, cipher, nb_plaintext); + return srs_success; +} + +srs_error_t SrsPlaintextTransport::unprotect_rtcp(const char* cipher, char* plaintext, int& nb_plaintext) +{ + memcpy(plaintext, cipher, nb_plaintext); + return srs_success; +} + SrsRtcPlayStreamStatistic::SrsRtcPlayStreamStatistic() { nn_rtp_pkts = 0; @@ -200,6 +300,10 @@ SrsRtcPlayStream::SrsRtcPlayStream(SrsRtcConnection* s, SrsContextId parent_cid) _parent_cid = parent_cid; trd = new SrsDummyCoroutine(); + req_ = NULL; + source_ = NULL; + + is_started = false; session_ = s; mw_msgs = 0; @@ -215,6 +319,8 @@ SrsRtcPlayStream::~SrsRtcPlayStream() { _srs_config->unsubscribe(this); + srs_freep(req_); + srs_freep(trd); srs_freep(timer_); @@ -237,6 +343,12 @@ srs_error_t SrsRtcPlayStream::initialize(SrsRequest* req, std::mapcopy(); + + if ((err = _srs_rtc_sources->fetch_or_create(req_, &source_)) != srs_success) { + return srs_error_wrap(err, "rtc fetch source failed"); + } + if (true) { std::map::iterator it = sub_relations.begin(); while (it != sub_relations.end()) { @@ -262,14 +374,12 @@ srs_error_t SrsRtcPlayStream::initialize(SrsRequest* req, std::mapreq; - - if (req->vhost != vhost) { + if (req_->vhost != vhost) { return srs_success; } - realtime = _srs_config->get_realtime_enabled(req->vhost, true); - mw_msgs = _srs_config->get_mw_msgs(req->vhost, realtime, true); + realtime = _srs_config->get_realtime_enabled(req_->vhost, true); + mw_msgs = _srs_config->get_mw_msgs(req_->vhost, realtime, true); srs_trace("Reload play realtime=%d, mw_msgs=%d", realtime, mw_msgs); @@ -309,7 +419,7 @@ srs_error_t SrsRtcPlayStream::start() } if (_srs_rtc_hijacker) { - if ((err = _srs_rtc_hijacker->on_start_play(session_, this, session_->req)) != srs_success) { + if ((err = _srs_rtc_hijacker->on_start_play(session_, this, req_)) != srs_success) { return srs_error_wrap(err, "on start play"); } } @@ -328,30 +438,29 @@ srs_error_t SrsRtcPlayStream::cycle() { srs_error_t err = srs_success; - SrsRtcStream* source = NULL; - SrsRequest* req = session_->req; - - if ((err = _srs_rtc_sources->fetch_or_create(req, &source)) != srs_success) { - return srs_error_wrap(err, "fetch source"); - } + SrsRtcStream* source = source_; SrsRtcConsumer* consumer = NULL; SrsAutoFree(SrsRtcConsumer, consumer); if ((err = source->create_consumer(consumer)) != srs_success) { - return srs_error_wrap(err, "create consumer, source=%s", req->get_stream_url().c_str()); + return srs_error_wrap(err, "create consumer, source=%s", req_->get_stream_url().c_str()); } // TODO: FIXME: Dumps the SPS/PPS from gop cache, without other frames. if ((err = source->consumer_dumps(consumer)) != srs_success) { - return srs_error_wrap(err, "dumps consumer, url=%s", req->get_stream_url().c_str()); + return srs_error_wrap(err, "dumps consumer, url=%s", req_->get_stream_url().c_str()); } - realtime = _srs_config->get_realtime_enabled(req->vhost, true); - mw_msgs = _srs_config->get_mw_msgs(req->vhost, realtime, true); + realtime = _srs_config->get_realtime_enabled(req_->vhost, true); + mw_msgs = _srs_config->get_mw_msgs(req_->vhost, realtime, true); // TODO: FIXME: Add cost in ms. - srs_trace("RTC: start play, url=%s, source_id=[%d][%s], encrypt=%d, realtime=%d, mw_msgs=%d", req->get_stream_url().c_str(), - ::getpid(), source->source_id().c_str(), session_->encrypt, realtime, mw_msgs); + SrsContextId cid = source->source_id(); + srs_trace("RTC: start play url=%s, source_id=[%d][%s], realtime=%d, mw_msgs=%d", req_->get_stream_url().c_str(), + ::getpid(), cid.c_str(), realtime, mw_msgs); + + SrsErrorPithyPrint* epp = new SrsErrorPithyPrint(); + SrsAutoFree(SrsErrorPithyPrint, epp); SrsPithyPrint* pprint = SrsPithyPrint::create_rtc_play(); SrsAutoFree(SrsPithyPrint, pprint); @@ -361,9 +470,10 @@ srs_error_t SrsRtcPlayStream::cycle() // TODO: FIXME: Use cache for performance? vector pkts; + uint64_t total_pkts = 0; if (_srs_rtc_hijacker) { - if ((err = _srs_rtc_hijacker->on_start_consume(session_, this, session_->req, consumer)) != srs_success) { + if ((err = _srs_rtc_hijacker->on_start_consume(session_, this, req_, consumer)) != srs_success) { return srs_error_wrap(err, "on start consuming"); } } @@ -386,16 +496,24 @@ srs_error_t SrsRtcPlayStream::cycle() // Update stats for session. session_->stat_->nn_out_rtp += msg_count; + total_pkts += msg_count; - // Send-out all RTP packets and do cleanup. - // TODO: FIXME: Handle error. - send_packets(source, pkts, info); + // Send-out all RTP packets and do cleanup + if (true) { + if ((err = send_packets(source, pkts, info)) != srs_success) { + uint32_t nn = 0; + if (epp->can_print(err, &nn)) { + srs_warn("play send packets=%u, nn=%u/%u, err: %s", pkts.size(), epp->nn_count, nn, srs_error_desc(err).c_str()); + } + srs_freep(err); + } - for (int i = 0; i < msg_count; i++) { - SrsRtpPacket2* pkt = pkts[i]; - srs_freep(pkt); + for (int i = 0; i < msg_count; i++) { + SrsRtpPacket2* pkt = pkts[i]; + srs_freep(pkt); + } + pkts.clear(); } - pkts.clear(); // Stat for performance analysis. if (!stat_enabled) { @@ -416,7 +534,7 @@ srs_error_t SrsRtcPlayStream::cycle() if (pprint->can_print()) { // TODO: FIXME: Print stat like frame/s, packet/s, loss_packets. srs_trace("-> RTC PLAY %d msgs, %d/%d packets, %d audios, %d extras, %d videos, %d samples, %d/%d/%d bytes, %d pad, %d/%d cache", - msg_count, msg_count, info.nn_rtp_pkts, info.nn_audios, info.nn_extras, info.nn_videos, info.nn_samples, info.nn_bytes, + total_pkts, msg_count, info.nn_rtp_pkts, info.nn_audios, info.nn_extras, info.nn_videos, info.nn_samples, info.nn_bytes, info.nn_rtp_bytes, info.nn_padding_bytes, info.nn_paddings, msg_count, msg_count); } } @@ -433,22 +551,24 @@ srs_error_t SrsRtcPlayStream::send_packets(SrsRtcStream* source, const vectorheader.get_ssrc()) && !video_tracks_.count(pkt->header.get_ssrc())) { + srs_warn("ssrc %u not found", pkt->header.get_ssrc()); continue; } // For audio, we transcoded AAC to opus in extra payloads. if (pkt->is_audio()) { - SrsRtcAudioSendTrack* audio_track = audio_tracks_[pkt->header.get_ssrc()]; // TODO: FIXME: Any simple solution? + SrsRtcAudioSendTrack* audio_track = audio_tracks_[pkt->header.get_ssrc()]; if ((err = audio_track->on_rtp(pkt, info)) != srs_success) { - return srs_error_wrap(err, "audio_track on rtp"); + return srs_error_wrap(err, "audio track, SSRC=%u, SEQ=%u", pkt->header.get_ssrc(), pkt->header.get_sequence()); } + // TODO: FIXME: Padding audio to the max payload in RTP packets. } else { - SrsRtcVideoSendTrack* video_track = video_tracks_[pkt->header.get_ssrc()]; // TODO: FIXME: Any simple solution? + SrsRtcVideoSendTrack* video_track = video_tracks_[pkt->header.get_ssrc()]; if ((err = video_track->on_rtp(pkt, info)) != srs_success) { - return srs_error_wrap(err, "audio_track on rtp"); + return srs_error_wrap(err, "video track, SSRC=%u, SEQ=%u", pkt->header.get_ssrc(), pkt->header.get_sequence()); } } @@ -462,40 +582,48 @@ srs_error_t SrsRtcPlayStream::send_packets(SrsRtcStream* source, const vector& pkts, uint32_t ssrc, uint16_t seq) { - if (true) { - std::map::iterator it; - for (it = audio_tracks_.begin(); it != audio_tracks_.end(); ++it) { - SrsRtcAudioSendTrack* track = it->second; + for (map::iterator it = audio_tracks_.begin(); it != audio_tracks_.end(); ++it) { + SrsRtcAudioSendTrack* track = it->second; - if (track->has_ssrc(ssrc)) { - // update recv nack statistic - track->on_recv_nack(); - - SrsRtpPacket2* pkt = track->fetch_rtp_packet(seq); - if (pkt != NULL) { - pkts.push_back(pkt); - } - return; - } + // If track is inactive, not process nack request. + if (!track->get_track_status()){ + continue; } + + if (!track->has_ssrc(ssrc)) { + continue; + } + + // update recv nack statistic + track->on_recv_nack(); + + SrsRtpPacket2* pkt = track->fetch_rtp_packet(seq); + if (pkt != NULL) { + pkts.push_back(pkt); + } + return; } - if (true) { - std::map::iterator it; - for (it = video_tracks_.begin(); it != video_tracks_.end(); ++it) { - SrsRtcVideoSendTrack* track = it->second; + for (map::iterator it = video_tracks_.begin(); it != video_tracks_.end(); ++it) { + SrsRtcVideoSendTrack* track = it->second; - if (track->has_ssrc(ssrc)) { - // update recv nack statistic - track->on_recv_nack(); - - SrsRtpPacket2* pkt = track->fetch_rtp_packet(seq); - if (pkt != NULL) { - pkts.push_back(pkt); - } - return; - } + // If track is inactive, not process nack request. + if (!track->get_track_status()){ + continue; } + + if (!track->has_ssrc(ssrc)) { + continue; + } + + // update recv nack statistic + track->on_recv_nack(); + + SrsRtpPacket2* pkt = track->fetch_rtp_packet(seq); + if (pkt != NULL) { + pkts.push_back(pkt); + } + return; } } @@ -539,75 +667,27 @@ srs_error_t SrsRtcPlayStream::notify(int type, srs_utime_t interval, srs_utime_t return err; } -srs_error_t SrsRtcPlayStream::on_rtcp(char* data, int nb_data) +srs_error_t SrsRtcPlayStream::on_rtcp(SrsRtcpCommon* rtcp) { - srs_error_t err = srs_success; - - // TODO: Use SrsBuffer to parse it. - char* ph = data; - int nb_left = nb_data; - - while (nb_left) { - uint8_t payload_type = ph[1]; - uint16_t length_4bytes = (((uint16_t)ph[2]) << 8) | ph[3]; - - int length = (length_4bytes + 1) * 4; - - if (length > nb_data) { - return srs_error_new(ERROR_RTC_RTCP, "invalid length=%u/%u, left=%u, bytes=%s", - length, nb_data, nb_left, srs_string_dumps_hex(ph, nb_left, 8).c_str()); - } - - srs_verbose("on rtcp, payload_type=%u", payload_type); - - switch (payload_type) { - case kSR: { - err = on_rtcp_sr(ph, length); - break; - } - case kRR: { - err = on_rtcp_rr(ph, length); - break; - } - case kSDES: { - break; - } - case kBye: { - break; - } - case kApp: { - break; - } - case kRtpFb: { - err = on_rtcp_feedback(ph, length); - break; - } - case kPsFb: { - err = on_rtcp_ps_feedback(ph, length); - break; - } - case kXR: { - err = on_rtcp_xr(ph, length); - break; - } - default:{ - return srs_error_new(ERROR_RTC_RTCP_CHECK, "unknown rtcp type=%u", payload_type); - break; - } - } - - if (err != srs_success) { - return srs_error_wrap(err, "rtcp left=%u, bytes=%s", nb_left, srs_string_dumps_hex(ph, nb_left, 8).c_str()); - } - - ph += length; - nb_left -= length; + if(SrsRtcpType_rr == rtcp->type()) { + SrsRtcpRR* rr = dynamic_cast(rtcp); + return on_rtcp_rr(rr); + } else if(SrsRtcpType_rtpfb == rtcp->type()) { + //currently rtpfb of nack will be handle by player. TWCC will be handled by SrsRtcConnection + SrsRtcpNack* nack = dynamic_cast(rtcp); + return on_rtcp_nack(nack); + } else if(SrsRtcpType_psfb == rtcp->type()) { + SrsRtcpPsfbCommon* psfb = dynamic_cast(rtcp); + return on_rtcp_ps_feedback(psfb); + } else if(SrsRtcpType_xr == rtcp->type()) { + SrsRtcpXr* xr = dynamic_cast(rtcp); + return on_rtcp_xr(xr); + } else { + return srs_error_new(ERROR_RTC_RTCP_CHECK, "unknown rtcp type=%u", rtcp->type()); } - - return err; } -srs_error_t SrsRtcPlayStream::on_rtcp_sr(char* buf, int nb_buf) +srs_error_t SrsRtcPlayStream::on_rtcp_rr(SrsRtcpRR* rtcp) { srs_error_t err = srs_success; @@ -618,7 +698,7 @@ srs_error_t SrsRtcPlayStream::on_rtcp_sr(char* buf, int nb_buf) return err; } -srs_error_t SrsRtcPlayStream::on_rtcp_xr(char* buf, int nb_buf) +srs_error_t SrsRtcPlayStream::on_rtcp_xr(SrsRtcpXr* rtcp) { srs_error_t err = srs_success; @@ -629,73 +709,24 @@ srs_error_t SrsRtcPlayStream::on_rtcp_xr(char* buf, int nb_buf) return err; } -srs_error_t SrsRtcPlayStream::on_rtcp_feedback(char* buf, int nb_buf) +srs_error_t SrsRtcPlayStream::on_rtcp_nack(SrsRtcpNack* rtcp) { srs_error_t err = srs_success; - if (nb_buf < 12) { - return srs_error_new(ERROR_RTC_RTCP_CHECK, "invalid rtp feedback packet, nb_buf=%d", nb_buf); - } - - SrsBuffer* stream = new SrsBuffer(buf, nb_buf); - SrsAutoFree(SrsBuffer, stream); - - // @see: https://tools.ietf.org/html/rfc4585#section-6.1 - /* - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - |V=2|P| FMT | PT | length | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | SSRC of packet sender | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | SSRC of media source | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - : Feedback Control Information (FCI) : - : : - */ - uint8_t first = stream->read_1bytes(); - //uint8_t version = first & 0xC0; - //uint8_t padding = first & 0x20; - uint8_t fmt = first & 0x1F; - if(15 == fmt) { - return session_->on_rtcp_feedback(buf, nb_buf); - } - - /*uint8_t payload_type = */stream->read_1bytes(); - /*uint16_t length = */stream->read_2bytes(); - /*uint32_t ssrc_of_sender = */stream->read_4bytes(); - uint32_t ssrc_of_media_source = stream->read_4bytes(); - - /* - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | PID | BLP | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - */ - - uint16_t pid = stream->read_2bytes(); - int blp = stream->read_2bytes(); - - // TODO: FIXME: Support ARQ. - vector resend_pkts; - nack_fetch(resend_pkts, ssrc_of_media_source, pid); - // If NACK disabled, print a log. if (!nack_enabled_) { - srs_trace("RTC NACK seq=%u, ignored", pid); + vector sns = rtcp->get_lost_sns(); + srs_trace("RTC NACK ssrc=%u, seq=%s, ignored", rtcp->get_media_ssrc(), srs_join_vector_string(sns, ",").c_str()); return err; } - uint16_t mask = 0x01; - for (int i = 1; i < 16 && blp; ++i, mask <<= 1) { - if (!(blp & mask)) { - continue; - } + // TODO: FIXME: Support ARQ. + vector resend_pkts; - uint32_t loss_seq = pid + i; - nack_fetch(resend_pkts, ssrc_of_media_source, loss_seq); + vector sns = rtcp->get_lost_sns(); + for(int i = 0; i < sns.size(); ++i) { + uint16_t seq = sns.at(i); + nack_fetch(resend_pkts, rtcp->get_media_ssrc(), seq); } for (int i = 0; i < (int)resend_pkts.size(); ++i) { @@ -715,32 +746,16 @@ srs_error_t SrsRtcPlayStream::on_rtcp_feedback(char* buf, int nb_buf) return err; } -srs_error_t SrsRtcPlayStream::on_rtcp_ps_feedback(char* buf, int nb_buf) +srs_error_t SrsRtcPlayStream::on_rtcp_ps_feedback(SrsRtcpPsfbCommon* rtcp) { srs_error_t err = srs_success; - if (nb_buf < 12) { - return srs_error_new(ERROR_RTC_RTCP_CHECK, "invalid rtp feedback packet, nb_buf=%d", nb_buf); - } - - SrsBuffer* stream = new SrsBuffer(buf, nb_buf); - SrsAutoFree(SrsBuffer, stream); - - uint8_t first = stream->read_1bytes(); - //uint8_t version = first & 0xC0; - //uint8_t padding = first & 0x20; - uint8_t fmt = first & 0x1F; - - /*uint8_t payload_type = */stream->read_1bytes(); - /*uint16_t length = */stream->read_2bytes(); - /*uint32_t ssrc_of_sender = */stream->read_4bytes(); - uint32_t ssrc_of_media_source = stream->read_4bytes(); - + uint8_t fmt = rtcp->get_rc(); switch (fmt) { case kPLI: { - ISrsRtcPublishStream* publisher = session_->source_->publish_stream(); + ISrsRtcPublishStream* publisher = source_->publish_stream(); if (publisher) { - uint32_t ssrc = get_video_publish_ssrc(ssrc_of_media_source); + uint32_t ssrc = get_video_publish_ssrc(rtcp->get_media_ssrc()); if (ssrc != 0) { publisher->request_keyframe(ssrc); srs_trace("RTC request PLI"); @@ -770,17 +785,6 @@ srs_error_t SrsRtcPlayStream::on_rtcp_ps_feedback(char* buf, int nb_buf) return err; } -srs_error_t SrsRtcPlayStream::on_rtcp_rr(char* data, int nb_data) -{ - srs_error_t err = srs_success; - - // TODO: FIXME: Implements it. - - session_->stat_->nn_rr++; - - return err; -} - uint32_t SrsRtcPlayStream::get_video_publish_ssrc(uint32_t play_ssrc) { std::map::iterator it; @@ -797,6 +801,7 @@ SrsRtcPublishStream::SrsRtcPublishStream(SrsRtcConnection* session) { timer_ = new SrsHourGlass(this, 200 * SRS_UTIME_MILLISECONDS); + is_started = false; session_ = session; request_keyframe_ = false; @@ -813,12 +818,17 @@ SrsRtcPublishStream::SrsRtcPublishStream(SrsRtcConnection* session) SrsRtcPublishStream::~SrsRtcPublishStream() { - // TODO: FIXME: Do unpublish when session timeout. + if (_srs_rtc_hijacker) { + _srs_rtc_hijacker->on_stop_publish(session_, this, req); + } + if (source) { source->set_publish_stream(NULL); source->on_unpublish(); } + // TODO: FIXME: Should remove and delete source. + srs_freep(req); srs_freep(timer_); } @@ -993,7 +1003,7 @@ srs_error_t SrsRtcPublishStream::on_rtp(char* data, int nb_data) // For NACK simulator, drop packet. if (nn_simulate_nack_drop) { - SrsBuffer b(data, nb_data); SrsRtpHeader h; h.decode(&b); + SrsBuffer b(data, nb_data); SrsRtpHeader h; h.ignore_padding(true); h.decode(&b); simulate_drop_packet(&h, nb_data); return err; } @@ -1033,7 +1043,7 @@ srs_error_t SrsRtcPublishStream::on_rtp(char* data, int nb_data) char* unprotected_buf = new char[kRtpPacketSize]; if ((err = session_->transport_->unprotect_rtp(data, unprotected_buf, nb_unprotected_buf)) != srs_success) { // We try to decode the RTP header for more detail error informations. - SrsBuffer b(data, nb_data); SrsRtpHeader h; h.decode(&b); + SrsBuffer b(data, nb_data); SrsRtpHeader h; h.ignore_padding(true); h.decode(&b); err = srs_error_wrap(err, "marker=%u, pt=%u, seq=%u, ts=%u, ssrc=%u, pad=%u, payload=%uB", h.get_marker(), h.get_payload_type(), h.get_sequence(), h.get_timestamp(), h.get_ssrc(), h.get_padding(), nb_data - b.pos()); @@ -1050,7 +1060,7 @@ srs_error_t SrsRtcPublishStream::on_rtp(char* data, int nb_data) int nb_header = h.nb_bytes(); const char* body = unprotected_buf + nb_header; int nb_body = nb_unprotected_buf - nb_header; - return srs_error_wrap(err, "cipher=%u, plaintext=%u, body=%s", nb_data, nb_unprotected_buf, + return srs_error_wrap(err, "cipher=%u, plaintext=%u, body=[%s]", nb_data, nb_unprotected_buf, srs_string_dumps_hex(body, nb_body, 8).c_str()); } @@ -1099,6 +1109,7 @@ srs_error_t SrsRtcPublishStream::do_on_rtp(char* plaintext, int nb_plaintext) } if (_srs_rtc_hijacker) { + // TODO: FIXME: copy pkt by hijacker itself if ((err = _srs_rtc_hijacker->on_rtp_packet(session_, this, req, pkt->copy())) != srs_success) { return srs_error_wrap(err, "on rtp packet"); } @@ -1133,6 +1144,10 @@ srs_error_t SrsRtcPublishStream::send_periodic_twcc() { srs_error_t err = srs_success; + if (!rtcp_twcc_.need_feedback()) { + return err; + } + char pkt[kRtcpPacketSize]; SrsBuffer *buffer = new SrsBuffer(pkt, sizeof(pkt)); SrsAutoFree(SrsBuffer, buffer); @@ -1153,162 +1168,36 @@ srs_error_t SrsRtcPublishStream::send_periodic_twcc() return session_->sendonly_skt->sendto(protected_buf, nb_protected_buf, 0); } -srs_error_t SrsRtcPublishStream::on_rtcp(char* data, int nb_data) +srs_error_t SrsRtcPublishStream::on_rtcp(SrsRtcpCommon* rtcp) { - srs_error_t err = srs_success; - - char* ph = data; - int nb_left = nb_data; - while (nb_left) { - uint8_t payload_type = ph[1]; - uint16_t length_4bytes = (((uint16_t)ph[2]) << 8) | ph[3]; - - int length = (length_4bytes + 1) * 4; - - if (length > nb_data) { - return srs_error_new(ERROR_RTC_RTCP, "invalid rtcp packet, length=%u", length); - } - - srs_verbose("on rtcp, payload_type=%u", payload_type); - - switch (payload_type) { - case kSR: { - err = on_rtcp_sr(ph, length); - break; - } - case kRR: { - err = on_rtcp_rr(ph, length); - break; - } - case kSDES: { - break; - } - case kBye: { - break; - } - case kApp: { - break; - } - case kRtpFb: { - err = on_rtcp_feedback(ph, length); - break; - } - case kPsFb: { - err = on_rtcp_ps_feedback(ph, length); - break; - } - case kXR: { - err = on_rtcp_xr(ph, length); - break; - } - default:{ - return srs_error_new(ERROR_RTC_RTCP_CHECK, "unknown rtcp type=%u", payload_type); - break; - } - } - - if (err != srs_success) { - return srs_error_wrap(err, "rtcp"); - } - - ph += length; - nb_left -= length; + if(SrsRtcpType_sr == rtcp->type()) { + SrsRtcpSR* sr = dynamic_cast(rtcp); + return on_rtcp_sr(sr); + } else if(SrsRtcpType_xr == rtcp->type()) { + SrsRtcpXr* xr = dynamic_cast(rtcp); + return on_rtcp_xr(xr); + } else if(SrsRtcpType_sdes == rtcp->type()) { + //ignore RTCP SDES + return srs_success; + } else { + return srs_error_new(ERROR_RTC_RTCP_CHECK, "unknown rtcp type=%u", rtcp->type()); } - - return err; } -srs_error_t SrsRtcPublishStream::on_rtcp_sr(char* buf, int nb_buf) +srs_error_t SrsRtcPublishStream::on_rtcp_sr(SrsRtcpSR* rtcp) { srs_error_t err = srs_success; + SrsNtp srs_ntp = SrsNtp::to_time_ms(rtcp->get_ntp()); - if (nb_buf < 28) { - return srs_error_new(ERROR_RTC_RTCP_CHECK, "invalid rtp sender report packet, nb_buf=%d", nb_buf); - } - - SrsBuffer* stream = new SrsBuffer(buf, nb_buf); - SrsAutoFree(SrsBuffer, stream); - - // @see: https://tools.ietf.org/html/rfc3550#section-6.4.1 - /* - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -header |V=2|P| RC | PT=SR=200 | length | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | SSRC of sender | - +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ -sender | NTP timestamp, most significant word | -info +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | NTP timestamp, least significant word | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | RTP timestamp | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | sender's packet count | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | sender's octet count | - +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ -report | SSRC_1 (SSRC of first source) | -block +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - 1 | fraction lost | cumulative number of packets lost | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | extended highest sequence number received | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | interarrival jitter | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | last SR (LSR) | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | delay since last SR (DLSR) | - +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ -report | SSRC_2 (SSRC of second source) | -block +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - 2 : ... : - +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ - | profile-specific extensions | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - */ - uint8_t first = stream->read_1bytes(); - uint8_t rc = first & 0x1F; - - uint8_t payload_type = stream->read_1bytes(); - srs_assert(payload_type == kSR); - uint16_t length = stream->read_2bytes(); - - if (((length + 1) * 4) != (rc * 24 + 28)) { - return srs_error_new(ERROR_RTC_RTCP_CHECK, "invalid rtcp sender report packet, length=%u, rc=%u", length, rc); - } - - uint32_t ssrc_of_sender = stream->read_4bytes(); - uint64_t ntp = stream->read_8bytes(); - SrsNtp srs_ntp = SrsNtp::to_time_ms(ntp); - uint32_t rtp_time = stream->read_4bytes(); - uint32_t sender_packet_count = stream->read_4bytes(); - uint32_t sender_octec_count = stream->read_4bytes(); - - (void)sender_packet_count; (void)sender_octec_count; (void)rtp_time; srs_verbose("sender report, ssrc_of_sender=%u, rtp_time=%u, sender_packet_count=%u, sender_octec_count=%u", - ssrc_of_sender, rtp_time, sender_packet_count, sender_octec_count); + rtcp->get_ssrc(), rtcp->get_rtp_ts(), rtcp->get_rtp_send_packets(), rtcp->get_rtp_send_bytes()); - for (int i = 0; i < rc; ++i) { - uint32_t ssrc = stream->read_4bytes(); - uint8_t fraction_lost = stream->read_1bytes(); - uint32_t cumulative_number_of_packets_lost = stream->read_3bytes(); - uint32_t highest_seq = stream->read_4bytes(); - uint32_t jitter = stream->read_4bytes(); - uint32_t lst = stream->read_4bytes(); - uint32_t dlsr = stream->read_4bytes(); - - (void)ssrc; (void)fraction_lost; (void)cumulative_number_of_packets_lost; (void)highest_seq; (void)jitter; (void)lst; (void)dlsr; - srs_verbose("sender report, ssrc=%u, fraction_lost=%u, cumulative_number_of_packets_lost=%u, highest_seq=%u, jitter=%u, lst=%u, dlst=%u", - ssrc, fraction_lost, cumulative_number_of_packets_lost, highest_seq, jitter, lst, dlsr); - } - - update_send_report_time(ssrc_of_sender, srs_ntp); + update_send_report_time(rtcp->get_ssrc(), srs_ntp); return err; } -srs_error_t SrsRtcPublishStream::on_rtcp_xr(char* buf, int nb_buf) +srs_error_t SrsRtcPublishStream::on_rtcp_xr(SrsRtcpXr* rtcp) { srs_error_t err = srs_success; @@ -1326,15 +1215,15 @@ srs_error_t SrsRtcPublishStream::on_rtcp_xr(char* buf, int nb_buf) +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ - SrsBuffer stream(buf, nb_buf); + SrsBuffer stream(rtcp->data(), rtcp->size()); /*uint8_t first = */stream.read_1bytes(); uint8_t pt = stream.read_1bytes(); srs_assert(pt == kXR); uint16_t length = (stream.read_2bytes() + 1) * 4; /*uint32_t ssrc = */stream.read_4bytes(); - if (length != nb_buf) { - return srs_error_new(ERROR_RTC_RTCP_CHECK, "invalid XR packet, length=%u, nb_buf=%d", length, nb_buf); + if (length != rtcp->size()) { + return srs_error_new(ERROR_RTC_RTCP_CHECK, "invalid XR packet, length=%u, nb_buf=%d", length, rtcp->size()); } while (stream.pos() + 4 < length) { @@ -1342,8 +1231,8 @@ srs_error_t SrsRtcPublishStream::on_rtcp_xr(char* buf, int nb_buf) stream.skip(1); uint16_t block_length = (stream.read_2bytes() + 1) * 4; - if (stream.pos() + block_length - 4 > nb_buf) { - return srs_error_new(ERROR_RTC_RTCP_CHECK, "invalid XR packet block, block_length=%u, nb_buf=%d", block_length, nb_buf); + if (stream.pos() + block_length - 4 > rtcp->size()) { + return srs_error_new(ERROR_RTC_RTCP_CHECK, "invalid XR packet block, block_length=%u, nb_buf=%d", block_length, rtcp->size()); } if (bt == 5) { @@ -1368,128 +1257,6 @@ srs_error_t SrsRtcPublishStream::on_rtcp_xr(char* buf, int nb_buf) return err; } -srs_error_t SrsRtcPublishStream::on_rtcp_feedback(char* buf, int nb_buf) -{ - srs_error_t err = srs_success; - // TODO: FIXME: Implements it. - return err; -} - -srs_error_t SrsRtcPublishStream::on_rtcp_ps_feedback(char* buf, int nb_buf) -{ - srs_error_t err = srs_success; - - if (nb_buf < 12) { - return srs_error_new(ERROR_RTC_RTCP_CHECK, "invalid rtp feedback packet, nb_buf=%d", nb_buf); - } - - SrsBuffer* stream = new SrsBuffer(buf, nb_buf); - SrsAutoFree(SrsBuffer, stream); - - uint8_t first = stream->read_1bytes(); - //uint8_t version = first & 0xC0; - //uint8_t padding = first & 0x20; - uint8_t fmt = first & 0x1F; - - /*uint8_t payload_type = */stream->read_1bytes(); - /*uint16_t length = */stream->read_2bytes(); - /*uint32_t ssrc_of_sender = */stream->read_4bytes(); - /*uint32_t ssrc_of_media_source = */stream->read_4bytes(); - - switch (fmt) { - case kPLI: { - srs_verbose("pli"); - break; - } - case kSLI: { - srs_verbose("sli"); - break; - } - case kRPSI: { - srs_verbose("rpsi"); - break; - } - case kAFB: { - srs_verbose("afb"); - break; - } - default: { - return srs_error_new(ERROR_RTC_RTCP, "unknown payload specific feedback=%u", fmt); - } - } - - return err; -} - -srs_error_t SrsRtcPublishStream::on_rtcp_rr(char* buf, int nb_buf) -{ - srs_error_t err = srs_success; - - if (nb_buf < 8) { - return srs_error_new(ERROR_RTC_RTCP_CHECK, "invalid rtp receiver report packet, nb_buf=%d", nb_buf); - } - - SrsBuffer* stream = new SrsBuffer(buf, nb_buf); - SrsAutoFree(SrsBuffer, stream); - - // @see: https://tools.ietf.org/html/rfc3550#section-6.4.2 - /* - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -header |V=2|P| RC | PT=RR=201 | length | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | SSRC of packet sender | - +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ -report | SSRC_1 (SSRC of first source) | -block +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - 1 | fraction lost | cumulative number of packets lost | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | extended highest sequence number received | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | interarrival jitter | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | last SR (LSR) | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | delay since last SR (DLSR) | - +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ -report | SSRC_2 (SSRC of second source) | -block +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - 2 : ... : - +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ - | profile-specific extensions | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - */ - uint8_t first = stream->read_1bytes(); - //uint8_t version = first & 0xC0; - //uint8_t padding = first & 0x20; - uint8_t rc = first & 0x1F; - - /*uint8_t payload_type = */stream->read_1bytes(); - uint16_t length = stream->read_2bytes(); - /*uint32_t ssrc_of_sender = */stream->read_4bytes(); - - if (((length + 1) * 4) != (rc * 24 + 8)) { - return srs_error_new(ERROR_RTC_RTCP_CHECK, "invalid rtcp receiver packet, length=%u, rc=%u", length, rc); - } - - for (int i = 0; i < rc; ++i) { - uint32_t ssrc = stream->read_4bytes(); - uint8_t fraction_lost = stream->read_1bytes(); - uint32_t cumulative_number_of_packets_lost = stream->read_3bytes(); - uint32_t highest_seq = stream->read_4bytes(); - uint32_t jitter = stream->read_4bytes(); - uint32_t lst = stream->read_4bytes(); - uint32_t dlsr = stream->read_4bytes(); - - (void)ssrc; (void)fraction_lost; (void)cumulative_number_of_packets_lost; (void)highest_seq; (void)jitter; (void)lst; (void)dlsr; - srs_verbose("ssrc=%u, fraction_lost=%u, cumulative_number_of_packets_lost=%u, highest_seq=%u, jitter=%u, lst=%u, dlst=%u", - ssrc, fraction_lost, cumulative_number_of_packets_lost, highest_seq, jitter, lst, dlsr); - } - - return err; -} - // TODO: FIXME: Use async request PLI to prevent dup requests. void SrsRtcPublishStream::request_keyframe(uint32_t ssrc) { @@ -1505,6 +1272,11 @@ void SrsRtcPublishStream::request_keyframe(uint32_t ssrc) session_->stat_->nn_pli++; } +void SrsRtcPublishStream::on_consumers_finished() +{ + session_->on_consumers_finished(req->get_stream_url()); +} + srs_error_t SrsRtcPublishStream::notify(int type, srs_utime_t interval, srs_utime_t tick) { srs_error_t err = srs_success; @@ -1644,18 +1416,22 @@ string SrsRtcConnectionStatistic::summary() return ss.str(); } +ISrsRtcConnectionHijacker::ISrsRtcConnectionHijacker() +{ +} + +ISrsRtcConnectionHijacker::~ISrsRtcConnectionHijacker() +{ +} + SrsRtcConnection::SrsRtcConnection(SrsRtcServer* s, SrsContextId context_id) { req = NULL; - is_publisher_ = false; - encrypt = true; cid = context_id; stat_ = new SrsRtcConnectionStatistic(); timer_ = new SrsHourGlass(this, 1000 * SRS_UTIME_MILLISECONDS); + hijacker_ = NULL; - source_ = NULL; - publisher_ = NULL; - player_ = NULL; sendonly_skt = NULL; server_ = s; transport_ = new SrsSecurityTransport(this); @@ -1673,11 +1449,10 @@ SrsRtcConnection::SrsRtcConnection(SrsRtcServer* s, SrsContextId context_id) SrsRtcConnection::~SrsRtcConnection() { srs_freep(timer_); - srs_freep(player_); - srs_freep(publisher_); srs_freep(transport_); srs_freep(req); srs_freep(stat_); + srs_freep(pp_address_change); // Note that we should never delete the sendonly_skt, // it's just point to the object in peer_addresses_. @@ -1687,7 +1462,21 @@ SrsRtcConnection::~SrsRtcConnection() srs_freep(addr); } - srs_freep(pp_address_change); + // Cleanup publishers. + for(map::iterator it = publishers_.begin(); it != publishers_.end(); ++it) { + SrsRtcPublishStream* publisher = it->second; + srs_freep(publisher); + } + publishers_.clear(); + publishers_ssrc_map_.clear(); + + // Cleanup players. + for(map::iterator it = players_.begin(); it != players_.end(); ++it) { + SrsRtcPlayStream* player = it->second; + srs_freep(player); + } + players_.clear(); + players_ssrc_map_.clear(); } SrsSdp* SrsRtcConnection::get_local_sdp() @@ -1738,11 +1527,6 @@ vector SrsRtcConnection::peer_addresses() return addresses; } -void SrsRtcConnection::set_encrypt(bool v) -{ - encrypt = v; -} - void SrsRtcConnection::switch_to_context() { _srs_context->set_id(cid); @@ -1759,6 +1543,7 @@ srs_error_t SrsRtcConnection::add_publisher(SrsRequest* req, const SrsSdp& remot SrsRtcStreamDescription* stream_desc = new SrsRtcStreamDescription(); SrsAutoFree(SrsRtcStreamDescription, stream_desc); + if ((err = negotiate_publish_capability(req, remote_sdp, stream_desc)) != srs_success) { return srs_error_wrap(err, "publish negotiate"); } @@ -1772,8 +1557,17 @@ srs_error_t SrsRtcConnection::add_publisher(SrsRequest* req, const SrsSdp& remot return srs_error_wrap(err, "create source"); } + // When SDP is done, we set the stream to create state, to prevent multiple publisher. + // @note Here, we check the stream again. + if (!source->can_publish()) { + return srs_error_new(ERROR_RTC_SOURCE_BUSY, "stream %s busy", req->get_stream_url().c_str()); + } + source->set_stream_created(); + + // Apply the SDP to source. source->set_stream_desc(stream_desc->copy()); + // TODO: FIXME: What happends when error? if ((err = create_publisher(req, stream_desc)) != srs_success) { return srs_error_wrap(err, "create publish"); } @@ -1785,6 +1579,13 @@ srs_error_t SrsRtcConnection::add_publisher(SrsRequest* req, const SrsSdp& remot srs_error_t SrsRtcConnection::add_player(SrsRequest* req, const SrsSdp& remote_sdp, SrsSdp& local_sdp) { srs_error_t err = srs_success; + + if (_srs_rtc_hijacker) { + if ((err = _srs_rtc_hijacker->on_before_play(this, req)) != srs_success) { + return srs_error_wrap(err, "before play"); + } + } + std::map play_sub_relations; if ((err = negotiate_play_capability(req, remote_sdp, play_sub_relations)) != srs_success) { return srs_error_wrap(err, "play negotiate"); @@ -1825,6 +1626,12 @@ srs_error_t SrsRtcConnection::add_player2(SrsRequest* req, SrsSdp& local_sdp) { srs_error_t err = srs_success; + if (_srs_rtc_hijacker) { + if ((err = _srs_rtc_hijacker->on_before_play(this, req)) != srs_success) { + return srs_error_wrap(err, "before play"); + } + } + std::map play_sub_relations; if ((err = fetch_source_capability(req, play_sub_relations)) != srs_success) { return srs_error_wrap(err, "play negotiate"); @@ -1862,14 +1669,21 @@ srs_error_t SrsRtcConnection::add_player2(SrsRequest* req, SrsSdp& local_sdp) return err; } -srs_error_t SrsRtcConnection::initialize(SrsRtcStream* source, SrsRequest* r, bool is_publisher, string username) +srs_error_t SrsRtcConnection::initialize(SrsRequest* r, bool dtls, bool srtp, string username) { srs_error_t err = srs_success; username_ = username; req = r->copy(); - is_publisher_ = is_publisher; - source_ = source; + + if (!srtp) { + srs_freep(transport_); + if (dtls) { + transport_ = new SrsSemiSecurityTransport(this); + } else { + transport_ = new SrsPlaintextTransport(this); + } + } SrsSessionConfig* cfg = &local_sdp.session_config_; if ((err = transport_->initialize(cfg)) != srs_success) { @@ -1884,8 +1698,8 @@ srs_error_t SrsRtcConnection::initialize(SrsRtcStream* source, SrsRequest* r, bo session_timeout = _srs_config->get_rtc_stun_timeout(req->vhost); last_stun_time = srs_get_system_time(); - srs_trace("RTC init session, user=%s, url=%s, DTLS(role=%s, version=%s), timeout=%dms", username.c_str(), - r->get_stream_url().c_str(), cfg->dtls_role.c_str(), cfg->dtls_version.c_str(), srsu2msi(session_timeout)); + srs_trace("RTC init session, user=%s, url=%s, encrypt=%u/%u, DTLS(role=%s, version=%s), timeout=%dms", username.c_str(), + r->get_stream_url().c_str(), dtls, srtp, cfg->dtls_role.c_str(), cfg->dtls_version.c_str(), srsu2msi(session_timeout)); return err; } @@ -1935,73 +1749,237 @@ srs_error_t SrsRtcConnection::on_rtcp(char* data, int nb_data) _srs_blackhole->sendto(unprotected_buf, nb_unprotected_buf); } - if (player_) { - err = player_->on_rtcp(unprotected_buf, nb_unprotected_buf); + SrsBuffer* buffer = new SrsBuffer(unprotected_buf, nb_unprotected_buf); + SrsAutoFree(SrsBuffer, buffer); + + SrsRtcpCompound rtcp_compound; + if(srs_success != (err = rtcp_compound.decode(buffer))) { + return srs_error_wrap(err, "decode rtcp plaintext=%u, bytes=[%s], at=%s", nb_unprotected_buf, + srs_string_dumps_hex(unprotected_buf, nb_unprotected_buf, 8).c_str(), + srs_string_dumps_hex(buffer->head(), buffer->left(), 8).c_str()); } - if (publisher_) { - err = publisher_->on_rtcp(unprotected_buf, nb_unprotected_buf); - } + SrsRtcpCommon* rtcp = NULL; + while(NULL != (rtcp = rtcp_compound.get_next_rtcp())) { + err = dispatch_rtcp(rtcp); + SrsAutoFree(SrsRtcpCommon, rtcp); - if (err != srs_success) { - return srs_error_wrap(err, "cipher=%u, plaintext=%u, bytes=%s", nb_data, nb_unprotected_buf, - srs_string_dumps_hex(unprotected_buf, nb_unprotected_buf, 8).c_str()); + if(srs_success != err) { + return srs_error_wrap(err, "cipher=%u, plaintext=%u, bytes=[%s], rtcp=(%u,%u,%u,%u)", nb_data, nb_unprotected_buf, + srs_string_dumps_hex(unprotected_buf, nb_unprotected_buf, 8).c_str(), + rtcp->get_rc(), rtcp->type(), rtcp->get_ssrc(), rtcp->size()); + } } return err; } -srs_error_t SrsRtcConnection::on_rtcp_feedback(char* data, int nb_data) +srs_error_t SrsRtcConnection::dispatch_rtcp(SrsRtcpCommon* rtcp) +{ + srs_error_t err = srs_success; + + // For TWCC packet. + if (SrsRtcpType_rtpfb == rtcp->type() && 15 == rtcp->get_rc()) { + return on_rtcp_feedback_twcc(rtcp->data(), rtcp->size()); + } + + // For REMB packet. + if (SrsRtcpType_psfb == rtcp->type()) { + SrsRtcpPsfbCommon* psfb = dynamic_cast(rtcp); + if (15 == psfb->get_rc()) { + return on_rtcp_feedback_remb(psfb); + } + } + + // Ignore special packet. + if (SrsRtcpType_rr == rtcp->type()) { + SrsRtcpRR* rr = dynamic_cast(rtcp); + if (rr->get_rb_ssrc() == 0) { //for native client + return err; + } + } + + // The feedback packet for specified SSRC. + // For example, if got SR packet, we required a publisher to handle it. + uint32_t required_publisher_ssrc = 0, required_player_ssrc = 0; + if (SrsRtcpType_sr == rtcp->type()) { + required_publisher_ssrc = rtcp->get_ssrc(); + } else if (SrsRtcpType_rr == rtcp->type()) { + SrsRtcpRR* rr = dynamic_cast(rtcp); + required_player_ssrc = rr->get_rb_ssrc(); + } else if (SrsRtcpType_rtpfb == rtcp->type()) { + if(1 == rtcp->get_rc()) { + SrsRtcpNack* nack = dynamic_cast(rtcp); + required_player_ssrc = nack->get_media_ssrc(); + } + } else if(SrsRtcpType_psfb == rtcp->type()) { + SrsRtcpPsfbCommon* psfb = dynamic_cast(rtcp); + required_player_ssrc = psfb->get_media_ssrc(); + } + + // Find the publisher or player by SSRC, always try to got one. + SrsRtcPlayStream* player = NULL; + SrsRtcPublishStream* publisher = NULL; + if (true) { + uint32_t ssrc = required_publisher_ssrc? required_publisher_ssrc : rtcp->get_ssrc(); + map::iterator it = publishers_ssrc_map_.find(ssrc); + if (it != publishers_ssrc_map_.end()) { + publisher = it->second; + } + } + + if (true) { + uint32_t ssrc = required_player_ssrc? required_player_ssrc : rtcp->get_ssrc(); + map::iterator it = players_ssrc_map_.find(ssrc); + if (it != players_ssrc_map_.end()) { + player = it->second; + } + } + + // Ignore if packet is required by publisher or player. + if (required_publisher_ssrc && !publisher) { + srs_warn("no ssrc %u in publishers. rtcp type:%u", required_publisher_ssrc, rtcp->type()); + return err; + } + if (required_player_ssrc && !player) { + srs_warn("no ssrc %u in players. rtcp type:%u", required_player_ssrc, rtcp->type()); + return err; + } + + // Handle packet by publisher or player. + if (publisher && srs_success != (err = publisher->on_rtcp(rtcp))) { + return srs_error_wrap(err, "handle rtcp"); + } + if (player && srs_success != (err = player->on_rtcp(rtcp))) { + return srs_error_wrap(err, "handle rtcp"); + } + + return err; +} + +srs_error_t SrsRtcConnection::on_rtcp_feedback_twcc(char* data, int nb_data) { return srs_success; } +srs_error_t SrsRtcConnection::on_rtcp_feedback_remb(SrsRtcpPsfbCommon *rtcp) +{ + //ignore REMB + return srs_success; +} + +void SrsRtcConnection::on_consumers_finished(std::string url) +{ + if (hijacker_) { + hijacker_->on_consumers_finished(url); + } +} + +void SrsRtcConnection::set_hijacker(ISrsRtcConnectionHijacker* h) +{ + hijacker_ = h; +} + srs_error_t SrsRtcConnection::on_rtp(char* data, int nb_data) { - if (publisher_ == NULL) { + srs_error_t err = srs_success; + + if (publishers_.size() == 0) { return srs_error_new(ERROR_RTC_RTCP, "no publisher"); } - //TODO: FIXME: add unprotect_rtcp. - return publisher_->on_rtp(data, nb_data); + SrsRtpHeader header; + if (true) { + SrsBuffer* buffer = new SrsBuffer(data, nb_data); + SrsAutoFree(SrsBuffer, buffer); + header.ignore_padding(true); + if(srs_success != (err = header.decode(buffer))) { + return srs_error_wrap(err, "decode rtp header"); + } + } + + map::iterator it = publishers_ssrc_map_.find(header.get_ssrc()); + if(it == publishers_ssrc_map_.end()) { + return srs_error_new(ERROR_RTC_NO_PUBLISHER, "no publisher for ssrc:%u", header.get_ssrc()); + } + + SrsRtcPublishStream* publisher = it->second; + return publisher->on_rtp(data, nb_data); } srs_error_t SrsRtcConnection::on_connection_established() { srs_error_t err = srs_success; - srs_trace("RTC: session %s, to=%dms connection established", (is_publisher_? "Publisher":"Subscriber"), + // If DTLS done packet received many times, such as ARQ, ignore. + if(ESTABLISHED == state_) { + return err; + } + state_ = ESTABLISHED; + + srs_trace("RTC: session pub=%u, sub=%u, to=%dms connection established", publishers_.size(), players_.size(), srsu2msi(session_timeout)); - if (is_publisher_) { - if ((err = start_publish()) != srs_success) { + // start all publisher + for(map::iterator it = publishers_.begin(); it != publishers_.end(); ++it) { + string url = it->first; + SrsRtcPublishStream* publisher = it->second; + + srs_trace("RTC: Publisher url=%s established", url.c_str()); + + if ((err = publisher->start()) != srs_success) { return srs_error_wrap(err, "start publish"); } - } else { - if ((err = start_play()) != srs_success) { + } + + // start all player + for(map::iterator it = players_.begin(); it != players_.end(); ++it) { + string url = it->first; + SrsRtcPlayStream* player = it->second; + + srs_trace("RTC: Subscriber url=%s established", url.c_str()); + + if ((err = player->start()) != srs_success) { return srs_error_wrap(err, "start play"); } } + if (hijacker_) { + if ((err = hijacker_->on_dtls_done()) != srs_success) { + return srs_error_wrap(err, "hijack on dtls done"); + } + } + return err; } -srs_error_t SrsRtcConnection::start_play() +srs_error_t SrsRtcConnection::start_play(string stream_uri) { srs_error_t err = srs_success; - if ((err = player_->start()) != srs_success) { + map::iterator it = players_.find(stream_uri); + if(it == players_.end()) { + return srs_error_new(ERROR_RTC_NO_PLAYER, "not subscribe %s", stream_uri.c_str()); + } + + SrsRtcPlayStream* player = it->second; + if ((err = player->start()) != srs_success) { return srs_error_wrap(err, "start"); } return err; } -srs_error_t SrsRtcConnection::start_publish() +srs_error_t SrsRtcConnection::start_publish(std::string stream_uri) { srs_error_t err = srs_success; - if ((err = publisher_->start()) != srs_success) { + map::iterator it = publishers_.find(stream_uri); + if(it == publishers_.end()) { + return srs_error_new(ERROR_RTC_NO_PUBLISHER, "no %s publisher", stream_uri.c_str()); + } + + if ((err = it->second->start()) != srs_success) { return srs_error_wrap(err, "start"); } @@ -2038,9 +2016,12 @@ void SrsRtcConnection::update_sendonly_socket(SrsUdpMuxSocket* skt) // Show address change log. if (prev_peer_id.empty()) { srs_trace("RTC: session address init %s", peer_id.c_str()); - } else if (pp_address_change->can_print(skt->get_peer_port())) { - srs_trace("RTC: session address change %s -> %s, cached=%d, nn_change=%u, nn_address=%u", prev_peer_id.c_str(), - peer_id.c_str(), (addr_cache? 1:0), pp_address_change->nn_count, peer_addresses_.size()); + } else { + uint32_t nn = 0; + if (pp_address_change->can_print(skt->get_peer_port(), &nn)) { + srs_trace("RTC: session address change %s -> %s, cached=%d, nn_change=%u/%u, nn_address=%u", prev_peer_id.c_str(), + peer_id.c_str(), (addr_cache? 1:0), pp_address_change->nn_count, nn, peer_addresses_.size()); + } } // If no cache, build cache and setup the relations in connection. @@ -2059,6 +2040,23 @@ srs_error_t SrsRtcConnection::notify(int type, srs_utime_t interval, srs_utime_t return err; } +srs_error_t SrsRtcConnection::send_rtcp(char *data, int nb_data) +{ + srs_error_t err = srs_success; + + int nb_buf = nb_data; + char protected_buf[kRtpPacketSize]; + if ((err = transport_->protect_rtcp(data, protected_buf, nb_buf)) != srs_success) { + return srs_error_wrap(err, "protect rtcp"); + } + + if ((err = sendonly_skt->sendto(protected_buf, nb_buf, 0)) != srs_success) { + return srs_error_wrap(err, "send"); + } + + return err; +} + void SrsRtcConnection::check_send_nacks(SrsRtpNackForReceiver* nack, uint32_t ssrc, uint32_t& sent_nacks) { // @see: https://tools.ietf.org/html/rfc4585#section-6.1 @@ -2231,8 +2229,9 @@ srs_error_t SrsRtcConnection::send_rtcp_fb_pli(uint32_t ssrc) void SrsRtcConnection::simulate_nack_drop(int nn) { - if (publisher_) { - publisher_->simulate_nack_drop(nn); + for(map::iterator it = publishers_.begin(); it != publishers_.end(); ++it) { + SrsRtcPublishStream* publisher = it->second; + publisher->simulate_nack_drop(nn); } nn_simulate_player_nack_drop = nn; @@ -2273,8 +2272,8 @@ srs_error_t SrsRtcConnection::do_send_packets(const std::vector& iov->iov_len = stream.pos(); } - // Whether encrypt the RTP bytes. - if (encrypt) { + // Cipher RTP to SRTP packet. + if (true) { int nn_encrypt = (int)iov->iov_len; if ((err = transport_->protect_rtp2(iov->iov_base, &nn_encrypt)) != srs_success) { return srs_error_wrap(err, "srtp protect"); @@ -2305,15 +2304,28 @@ srs_error_t SrsRtcConnection::do_send_packets(const std::vector& return err; } -void SrsRtcConnection::set_all_tracks_status(bool status) +void SrsRtcConnection::set_all_tracks_status(std::string stream_uri, bool is_publish, bool status) { - if (player_) { - player_->set_all_tracks_status(status); + // For publishers. + if (is_publish) { + map::iterator it = publishers_.find(stream_uri); + if (publishers_.end() == it) { + return; + } + + SrsRtcPublishStream* publisher = it->second; + publisher->set_all_tracks_status(status); + return; } - if (publisher_) { - publisher_->set_all_tracks_status(status); + // For players. + map::iterator it = players_.find(stream_uri); + if (players_.end() == it) { + return; } + + SrsRtcPlayStream* player = it->second; + player->set_all_tracks_status(status); } #ifdef SRS_OSX @@ -2703,7 +2715,7 @@ srs_error_t SrsRtcConnection::negotiate_play_capability(SrsRequest* req, const S // TODO: check opus format specific param std::vector payloads = remote_media_desc.find_media_with_encoding_name("H264"); if (payloads.empty()) { - return srs_error_new(ERROR_RTC_SDP_EXCHANGE, "no valid found opus payload type"); + return srs_error_new(ERROR_RTC_SDP_EXCHANGE, "no valid found h264 payload type"); } SrsMediaPayloadType payload = payloads.at(0); @@ -2752,6 +2764,47 @@ srs_error_t SrsRtcConnection::negotiate_play_capability(SrsRequest* req, const S return err; } +srs_error_t SrsRtcConnection::negotiate_play_capability(SrsRequest* req, SrsRtcStreamDescription* req_stream_desc, + std::map& sub_relations) +{ + srs_error_t err = srs_success; + + SrsRtcStream* source = NULL; + if ((err = _srs_rtc_sources->fetch_or_create(req, &source)) != srs_success) { + return srs_error_wrap(err, "fetch rtc source"); + } + + std::vector src_track_descs; + //negotiate audio media + if(NULL != req_stream_desc->audio_track_desc_) { + src_track_descs = source->get_track_desc("audio", "opus"); + if (src_track_descs.size() > 0) { + // FIXME: use source sdp or subscribe sdp? native subscribe may have no sdp + SrsRtcTrackDescription *track = src_track_descs[0]->copy(); + sub_relations.insert(make_pair(track->ssrc_, track)); + track->ssrc_ = SrsRtcSSRCGenerator::instance()->generate_ssrc(); + } + } + + //negotiate video media + std::vector req_video_tracks = req_stream_desc->video_track_descs_; + src_track_descs = source->get_track_desc("video", "h264"); + for(int i = 0; i < req_video_tracks.size(); ++i) { + SrsRtcTrackDescription* req_video = req_video_tracks.at(i); + for(int j = 0; j < src_track_descs.size(); ++j) { + SrsRtcTrackDescription* src_video = src_track_descs.at(j); + if(req_video->id_ == src_video->id_) { + // FIXME: use source sdp or subscribe sdp? native subscribe may have no sdp + SrsRtcTrackDescription *track = src_video->copy(); + sub_relations.insert(make_pair(track->ssrc_, track)); + track->ssrc_ = SrsRtcSSRCGenerator::instance()->generate_ssrc(); + } + } + } + + return err; +} + srs_error_t SrsRtcConnection::fetch_source_capability(SrsRequest* req, std::map& sub_relations) { srs_error_t err = srs_success; @@ -2959,14 +3012,44 @@ srs_error_t SrsRtcConnection::create_player(SrsRequest* req, std::mapget_stream_url())) { return err; } - player_ = new SrsRtcPlayStream(this, _srs_context->get_id()); - if ((err = player_->initialize(req, sub_relations)) != srs_success) { + SrsRtcPlayStream* player = new SrsRtcPlayStream(this, _srs_context->get_id()); + if ((err = player->initialize(req, sub_relations)) != srs_success) { + srs_freep(player); return srs_error_wrap(err, "SrsRtcPlayStream init"); } + players_.insert(make_pair(req->get_stream_url(), player)); + + // make map between ssrc and player for fastly searching + for(map::iterator it = sub_relations.begin(); it != sub_relations.end(); ++it) { + SrsRtcTrackDescription* track_desc = it->second; + map::iterator it_player = players_ssrc_map_.find(track_desc->ssrc_); + if((players_ssrc_map_.end() != it_player) && (player != it_player->second)) { + return srs_error_new(ERROR_RTC_DUPLICATED_SSRC, "duplicate ssrc %d, track id: %s", + track_desc->ssrc_, track_desc->id_.c_str()); + } + players_ssrc_map_[track_desc->ssrc_] = player; + + if(0 != track_desc->fec_ssrc_) { + if(players_ssrc_map_.end() != players_ssrc_map_.find(track_desc->fec_ssrc_)) { + return srs_error_new(ERROR_RTC_DUPLICATED_SSRC, "duplicate fec ssrc %d, track id: %s", + track_desc->fec_ssrc_, track_desc->id_.c_str()); + } + players_ssrc_map_[track_desc->fec_ssrc_] = player; + } + + if(0 != track_desc->rtx_ssrc_) { + if(players_ssrc_map_.end() != players_ssrc_map_.find(track_desc->rtx_ssrc_)) { + return srs_error_new(ERROR_RTC_DUPLICATED_SSRC, "duplicate rtx ssrc %d, track id: %s", + track_desc->rtx_ssrc_, track_desc->id_.c_str()); + } + players_ssrc_map_[track_desc->rtx_ssrc_] = player; + } + } // TODO: FIXME: Support reload. // The TWCC ID is the ext-map ID in local SDP, and we set to enable GCC. @@ -2984,6 +3067,14 @@ srs_error_t SrsRtcConnection::create_player(SrsRequest* req, std::mapstart())) { + return srs_error_wrap(err, "start player"); + } + } + return err; } @@ -2991,18 +3082,84 @@ srs_error_t SrsRtcConnection::create_publisher(SrsRequest* req, SrsRtcStreamDesc { srs_error_t err = srs_success; - if (!stream_desc) { - return srs_error_new(ERROR_RTC_STREAM_DESC, "rtc publisher init"); - } + srs_assert(stream_desc); - if (publisher_) { + // Ignore if exists. + if(publishers_.end() != publishers_.find(req->get_stream_url())) { return err; } - publisher_ = new SrsRtcPublishStream(this); - if ((err = publisher_->initialize(req, stream_desc)) != srs_success) { + SrsRtcPublishStream* publisher = new SrsRtcPublishStream(this); + if ((err = publisher->initialize(req, stream_desc)) != srs_success) { + srs_freep(publisher); return srs_error_wrap(err, "rtc publisher init"); } + publishers_[req->get_stream_url()] = publisher; + + if(NULL != stream_desc->audio_track_desc_) { + if(publishers_ssrc_map_.end() != publishers_ssrc_map_.find(stream_desc->audio_track_desc_->ssrc_)) { + return srs_error_new(ERROR_RTC_DUPLICATED_SSRC, " duplicate ssrc %d, track id: %s", + stream_desc->audio_track_desc_->ssrc_, stream_desc->audio_track_desc_->id_.c_str()); + } + publishers_ssrc_map_[stream_desc->audio_track_desc_->ssrc_] = publisher; + + if(0 != stream_desc->audio_track_desc_->fec_ssrc_ + && stream_desc->audio_track_desc_->ssrc_ != stream_desc->audio_track_desc_->fec_ssrc_) { + if(publishers_ssrc_map_.end() != publishers_ssrc_map_.find(stream_desc->audio_track_desc_->fec_ssrc_)) { + return srs_error_new(ERROR_RTC_DUPLICATED_SSRC, " duplicate fec ssrc %d, track id: %s", + stream_desc->audio_track_desc_->fec_ssrc_, stream_desc->audio_track_desc_->id_.c_str()); + } + publishers_ssrc_map_[stream_desc->audio_track_desc_->fec_ssrc_] = publisher; + } + + if(0 != stream_desc->audio_track_desc_->rtx_ssrc_ + && stream_desc->audio_track_desc_->ssrc_ != stream_desc->audio_track_desc_->rtx_ssrc_) { + if(publishers_ssrc_map_.end() != publishers_ssrc_map_.find(stream_desc->audio_track_desc_->rtx_ssrc_)) { + return srs_error_new(ERROR_RTC_DUPLICATED_SSRC, " duplicate rtx ssrc %d, track id: %s", + stream_desc->audio_track_desc_->rtx_ssrc_, stream_desc->audio_track_desc_->id_.c_str()); + } + publishers_ssrc_map_[stream_desc->audio_track_desc_->rtx_ssrc_] = publisher; + } + } + + for(int i = 0; i < stream_desc->video_track_descs_.size(); ++i) { + SrsRtcTrackDescription* track_desc = stream_desc->video_track_descs_.at(i); + if(publishers_ssrc_map_.end() != publishers_ssrc_map_.find(track_desc->ssrc_)) { + return srs_error_new(ERROR_RTC_DUPLICATED_SSRC, " duplicate ssrc %d, track id: %s", + track_desc->ssrc_, track_desc->id_.c_str()); + } + publishers_ssrc_map_[track_desc->ssrc_] = publisher; + + if(0 != track_desc->fec_ssrc_ && track_desc->ssrc_ != track_desc->fec_ssrc_) { + if(publishers_ssrc_map_.end() != publishers_ssrc_map_.find(track_desc->fec_ssrc_)) { + return srs_error_new(ERROR_RTC_DUPLICATED_SSRC, " duplicate fec ssrc %d, track id: %s", + track_desc->fec_ssrc_, track_desc->id_.c_str()); + } + publishers_ssrc_map_[track_desc->fec_ssrc_] = publisher; + } + + if(0 != track_desc->rtx_ssrc_ && track_desc->rtx_ssrc_ != track_desc->fec_ssrc_) { + if(publishers_ssrc_map_.end() != publishers_ssrc_map_.find(track_desc->rtx_ssrc_)) { + return srs_error_new(ERROR_RTC_DUPLICATED_SSRC, " duplicate rtx ssrc %d, track id: %s", + track_desc->rtx_ssrc_, track_desc->id_.c_str()); + } + publishers_ssrc_map_[track_desc->rtx_ssrc_] = publisher; + } + } + + if (_srs_rtc_hijacker) { + if ((err = _srs_rtc_hijacker->on_create_publish(this, publisher, req)) != srs_success) { + return srs_error_wrap(err, "on create publish"); + } + } + + // If DTLS done, start the publisher. Because maybe create some publishers after DTLS done. + // For example, for single PC, we maybe start publisher when create it, because DTLS is done. + if(ESTABLISHED == state()) { + if(srs_success != (err = publisher->start())) { + return srs_error_wrap(err, "start publisher"); + } + } return err; } diff --git a/trunk/src/app/srs_app_rtc_conn.hpp b/trunk/src/app/srs_app_rtc_conn.hpp index 35d86752a..c2a037a4b 100644 --- a/trunk/src/app/srs_app_rtc_conn.hpp +++ b/trunk/src/app/srs_app_rtc_conn.hpp @@ -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 audio_tracks_; @@ -201,21 +250,20 @@ public: virtual srs_error_t cycle(); private: srs_error_t send_packets(SrsRtcStream* source, const std::vector& pkts, SrsRtcPlayStreamStatistic& info); -public: void nack_fetch(std::vector& 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 players_; + //key: player track's ssrc + std::map players_ssrc_map_; + // key: stream id + std::map publishers_; + // key: publisher track's ssrc + std::map 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 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& 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& sub_relations); + srs_error_t negotiate_play_capability(SrsRequest* req, SrsRtcStreamDescription* req_stream_desc, std::map& sub_relations); srs_error_t fetch_source_capability(SrsRequest* req, std::map& 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 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. diff --git a/trunk/src/app/srs_app_rtc_dtls.cpp b/trunk/src/app/srs_app_rtc_dtls.cpp index 7c6c52e50..a54d8ddc6 100644 --- a/trunk/src/app/srs_app_rtc_dtls.cpp +++ b/trunk/src/app/srs_app_rtc_dtls.cpp @@ -32,6 +32,8 @@ using namespace std; #include #include #include +#include +#include #include #include @@ -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); } - 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)); + // 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 (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); - } - } + while (BIO_ctrl_pending(bio_in) > 0) { + 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 ((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; diff --git a/trunk/src/app/srs_app_rtc_dtls.hpp b/trunk/src/app/srs_app_rtc_dtls.hpp index 65ae8d708..b15da73c1 100644 --- a/trunk/src/app/srs_app_rtc_dtls.hpp +++ b/trunk/src/app/srs_app_rtc_dtls.hpp @@ -27,12 +27,15 @@ #include #include - -class SrsRequest; +#include #include #include +#include + +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 diff --git a/trunk/src/app/srs_app_rtc_sdp.cpp b/trunk/src/app/srs_app_rtc_sdp.cpp index 86fd5a2fd..84e56f758 100644 --- a/trunk/src/app/srs_app_rtc_sdp.cpp +++ b/trunk/src/app/srs_app_rtc_sdp.cpp @@ -29,6 +29,7 @@ using namespace std; #include #include #include +#include #include #include @@ -314,8 +315,13 @@ vector SrsMediaDesc::find_media_with_encoding_name(const st { std::vector 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]); } } diff --git a/trunk/src/app/srs_app_rtc_server.cpp b/trunk/src/app/srs_app_rtc_server.cpp index 413dea282..393ee709e 100644 --- a/trunk/src/app/srs_app_rtc_server.cpp +++ b/trunk/src/app/srs_app_rtc_server.cpp @@ -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::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); } diff --git a/trunk/src/app/srs_app_rtc_server.hpp b/trunk/src/app/srs_app_rtc_server.hpp index 6ac0106f9..4a166f659 100644 --- a/trunk/src/app/srs_app_rtc_server.hpp +++ b/trunk/src/app/srs_app_rtc_server.hpp @@ -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 listeners; ISrsRtcServerHandler* handler; + ISrsRtcServerHijacker* hijacker; private: // TODO: FIXME: Rename it. std::map 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. diff --git a/trunk/src/app/srs_app_rtc_source.cpp b/trunk/src/app/srs_app_rtc_source.cpp index 9f37932ad..452083fc6 100644 --- a/trunk/src/app/srs_app_rtc_source.cpp +++ b/trunk/src/app/srs_app_rtc_source.cpp @@ -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,29 +1213,34 @@ 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 vec = split_str(fmtp, ";"); - for (size_t i = 0; i < vec.size(); ++i) { - std::vector kv = split_str(vec[i], "="); - if (kv.size() == 2) { - if (kv[0] == "profile-level-id") { - h264_param_.profile_level_id = kv[1]; - } else if (kv[0] == "packetization-mode") { - // 6.3. Non-Interleaved Mode - // This mode is in use when the value of the OPTIONAL packetization-mode - // media type parameter is equal to 1. This mode SHOULD be supported. - // It is primarily intended for low-delay applications. Only single NAL - // unit packets, STAP-As, and FU-As MAY be used in this mode. STAP-Bs, - // MTAPs, and FU-Bs MUST NOT be used. The transmission order of NAL - // units MUST comply with the NAL unit decoding order. - // @see https://tools.ietf.org/html/rfc6184#section-6.3 - h264_param_.packetization_mode = kv[1]; - } else if (kv[0] == "level-asymmetry-allowed") { - h264_param_.level_asymmerty_allow = kv[1]; - } else { - return srs_error_new(ERROR_RTC_SDP_DECODE, "invalid h264 param=%s", kv[0].c_str()); - } + + // For example: level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f + std::vector attributes = split_str(fmtp, ";"); + + for (size_t i = 0; i < attributes.size(); ++i) { + std::string attribute = attributes.at(i); + + std::vector 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") { + // 6.3. Non-Interleaved Mode + // This mode is in use when the value of the OPTIONAL packetization-mode + // media type parameter is equal to 1. This mode SHOULD be supported. + // It is primarily intended for low-delay applications. Only single NAL + // unit packets, STAP-As, and FU-As MAY be used in this mode. STAP-Bs, + // MTAPs, and FU-Bs MUST NOT be used. The transmission order of NAL + // units MUST comply with the NAL unit decoding order. + // @see https://tools.ietf.org/html/rfc6184#section-6.3 + h264_param_.packetization_mode = kv[1]; + } else if (kv[0] == "level-asymmetry-allowed") { + h264_param_.level_asymmerty_allow = kv[1]; } else { - return srs_error_new(ERROR_RTC_SDP_DECODE, "invalid h264 param=%s", vec[i].c_str()); + return srs_error_new(ERROR_RTC_SDP_DECODE, "invalid h264 param=%s", kv[0].c_str()); } } @@ -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::vectorat(seq); + SrsRtpPacket2* pkt = rtp_queue_->at(seq); + + if (pkt == NULL) { + return pkt; } - return NULL; + // 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. @@ -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(); diff --git a/trunk/src/app/srs_app_rtc_source.hpp b/trunk/src/app/srs_app_rtc_source.hpp index 2911fe4c2..09893c6a1 100644 --- a/trunk/src/app/srs_app_rtc_source.hpp +++ b/trunk/src/app/srs_app_rtc_source.hpp @@ -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 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: diff --git a/trunk/src/core/srs_core_version4.hpp b/trunk/src/core/srs_core_version4.hpp index 8c04a607d..71356be0b 100644 --- a/trunk/src/core/srs_core_version4.hpp +++ b/trunk/src/core/srs_core_version4.hpp @@ -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 diff --git a/trunk/src/kernel/srs_kernel_codec.cpp b/trunk/src/kernel/srs_kernel_codec.cpp index e3d3ca4a3..a937662f5 100644 --- a/trunk/src/kernel/srs_kernel_codec.cpp +++ b/trunk/src/kernel/srs_kernel_codec.cpp @@ -368,6 +368,13 @@ SrsSample::SrsSample() bframe = false; } +SrsSample::SrsSample(char* b, int s) +{ + size = s; + bytes = b; + bframe = false; +} + SrsSample::~SrsSample() { } diff --git a/trunk/src/kernel/srs_kernel_codec.hpp b/trunk/src/kernel/srs_kernel_codec.hpp index a5bb960cb..cc2bcb711 100644 --- a/trunk/src/kernel/srs_kernel_codec.hpp +++ b/trunk/src/kernel/srs_kernel_codec.hpp @@ -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. diff --git a/trunk/src/kernel/srs_kernel_error.hpp b/trunk/src/kernel/srs_kernel_error.hpp index e77923ae1..80a43cf68 100644 --- a/trunk/src/kernel/srs_kernel_error.hpp +++ b/trunk/src/kernel/srs_kernel_error.hpp @@ -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. diff --git a/trunk/src/kernel/srs_kernel_rtc_rtcp.cpp b/trunk/src/kernel/srs_kernel_rtc_rtcp.cpp index ef51e4ff3..785d21eef 100644 --- a/trunk/src/kernel/srs_kernel_rtc_rtcp.cpp +++ b/trunk/src/kernel/srs_kernel_rtc_rtcp.cpp @@ -29,7 +29,7 @@ #include using namespace std; -SrsRtcpCommon::SrsRtcpCommon() +SrsRtcpCommon::SrsRtcpCommon(): ssrc_(0), data_(NULL), nb_data_(0) { } @@ -42,18 +42,58 @@ uint8_t SrsRtcpCommon::type() const return header_.type; } +uint8_t SrsRtcpCommon::get_rc() const +{ + return header_.rc; +} + +uint32_t SrsRtcpCommon::get_ssrc() +{ + return ssrc_; +} + +void SrsRtcpCommon::set_ssrc(uint32_t ssrc) +{ + ssrc_ = ssrc; +} + +char* SrsRtcpCommon::data() +{ + return data_; +} + +int SrsRtcpCommon::size() +{ + return nb_data_; +} + srs_error_t SrsRtcpCommon::decode_header(SrsBuffer *buffer) { + if (!buffer->require(sizeof(SrsRtcpHeader) + 4)) { + return srs_error_new(ERROR_RTC_RTCP, "require %d", sizeof(SrsRtcpHeader) + 4); + } + buffer->read_bytes((char*)(&header_), sizeof(SrsRtcpHeader)); header_.length = ntohs(header_.length); + int payload_len = header_.length * 4; + if (payload_len > buffer->left()) { + return srs_error_new(ERROR_RTC_RTCP, + "require payload len=%u, buffer left=%u", payload_len, buffer->left()); + } + ssrc_ = buffer->read_4bytes(); + return srs_success; } srs_error_t SrsRtcpCommon::encode_header(SrsBuffer *buffer) { + if(! buffer->require(sizeof(SrsRtcpHeader) + 4)) { + return srs_error_new(ERROR_RTC_RTCP, "require %d", sizeof(SrsRtcpHeader) + 4); + } header_.length = htons(header_.length); buffer->write_bytes((char*)(&header_), sizeof(SrsRtcpHeader)); + buffer->write_4bytes(ssrc_); return srs_success; } @@ -61,12 +101,14 @@ srs_error_t SrsRtcpCommon::encode_header(SrsBuffer *buffer) srs_error_t SrsRtcpCommon::decode(SrsBuffer *buffer) { srs_error_t err = srs_success; + data_ = buffer->head(); + nb_data_ = buffer->left(); if(srs_success != (err = decode_header(buffer))) { return srs_error_wrap(err, "decode header"); } - payload_len_ = (header_.length + 1) * 4 - sizeof(SrsRtcpHeader); + payload_len_ = (header_.length + 1) * 4 - sizeof(SrsRtcpHeader) - 4; buffer->read_bytes((char *)payload_, payload_len_); return err; @@ -74,7 +116,7 @@ srs_error_t SrsRtcpCommon::decode(SrsBuffer *buffer) int SrsRtcpCommon::nb_bytes() { - return sizeof(SrsRtcpHeader) + payload_len_; + return sizeof(SrsRtcpHeader) + 4 + payload_len_; } srs_error_t SrsRtcpCommon::encode(SrsBuffer *buffer) @@ -82,24 +124,40 @@ srs_error_t SrsRtcpCommon::encode(SrsBuffer *buffer) return srs_error_new(ERROR_RTC_RTCP, "not implement"); } -SrsRtcpApp::SrsRtcpApp():ssrc_(0) +SrsRtcpApp::SrsRtcpApp() { + ssrc_ = 0; + header_.padding = 0; + header_.type = SrsRtcpType_app; + header_.rc = 0; + header_.version = kRtcpVersion; } SrsRtcpApp::~SrsRtcpApp() { } +bool SrsRtcpApp::is_rtcp_app(uint8_t *data, int nb_data) +{ + if (!data || nb_data <12) { + return false; + } + + SrsRtcpHeader *header = (SrsRtcpHeader*)data; + if (header->version == kRtcpVersion + && header->type == SrsRtcpType_app + && ntohs(header->length) >= 2) { + return true; + } + + return false; +} + uint8_t SrsRtcpApp::type() const { return SrsRtcpType_app; } -uint32_t SrsRtcpApp::get_ssrc() const -{ - return ssrc_; -} - uint8_t SrsRtcpApp::get_subtype() const { return header_.rc; @@ -147,31 +205,51 @@ srs_error_t SrsRtcpApp::set_payload(uint8_t* payload, int len) return srs_error_new(ERROR_RTC_RTCP, "invalid payload length %d", len); } - payload_len_ = len; + payload_len_ = (len + 3)/ 4 * 4;; memcpy(payload_, payload, len); + if (payload_len_ > len) { + memset(&payload_[len], 0, payload_len_ - len); //padding + } + header_.length = payload_len_/4 + 3 - 1; return srs_success; } -void SrsRtcpApp::set_ssrc(uint32_t ssrc) -{ - ssrc_ = ssrc; -} - srs_error_t SrsRtcpApp::decode(SrsBuffer *buffer) { + /* + @doc: https://tools.ietf.org/html/rfc3550#section-6.7 + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |V=2|P| subtype | PT=APP=204 | length | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | SSRC/CSRC | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | name (ASCII) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | application-dependent data ... + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ srs_error_t err = srs_success; + data_ = buffer->head(); + nb_data_ = buffer->left(); if(srs_success != (err = decode_header(buffer))) { return srs_error_wrap(err, "decode header"); } - ssrc_ = buffer->read_4bytes(); + if (header_.type != SrsRtcpType_app || !buffer->require(4)) { + return srs_error_new(ERROR_RTC_RTCP, "not rtcp app"); + } + buffer->read_bytes((char *)name_, sizeof(name_)); // TODO: FIXME: Should check size? - payload_len_ = (header_.length + 1) * 4 - sizeof(SrsRtcpHeader) - sizeof(name_) - sizeof(ssrc_); - buffer->read_bytes((char *)payload_, payload_len_); + payload_len_ = (header_.length + 1) * 4 - 8 - sizeof(name_); + if (payload_len_ > 0) { + buffer->read_bytes((char *)payload_, payload_len_); + } return srs_success; } @@ -183,6 +261,20 @@ int SrsRtcpApp::nb_bytes() srs_error_t SrsRtcpApp::encode(SrsBuffer *buffer) { + /* + @doc: https://tools.ietf.org/html/rfc3550#section-6.7 + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |V=2|P| subtype | PT=APP=204 | length | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | SSRC/CSRC | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | name (ASCII) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | application-dependent data ... + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ srs_error_t err = srs_success; if(!buffer->require(nb_bytes())) { @@ -193,7 +285,6 @@ srs_error_t SrsRtcpApp::encode(SrsBuffer *buffer) return srs_error_wrap(err, "encode header"); } - buffer->write_4bytes(ssrc_); buffer->write_bytes((char*)name_, sizeof(name_)); buffer->write_bytes((char*)payload_, payload_len_); @@ -223,11 +314,6 @@ uint8_t SrsRtcpSR::type() const return SrsRtcpType_sr; } -uint32_t SrsRtcpSR::get_sender_ssrc() const -{ - return sender_ssrc_; -} - uint64_t SrsRtcpSR::get_ntp() const { return ntp_; @@ -248,11 +334,6 @@ uint32_t SrsRtcpSR::get_rtp_send_bytes() const return send_rtp_bytes_; } -void SrsRtcpSR::set_sender_ssrc(uint32_t ssrc) -{ - sender_ssrc_ = ssrc; -} - void SrsRtcpSR::set_ntp(uint64_t ntp) { ntp_ = ntp; @@ -275,13 +356,51 @@ void SrsRtcpSR::set_rtp_send_bytes(uint32_t bytes) srs_error_t SrsRtcpSR::decode(SrsBuffer *buffer) { + /* @doc: https://tools.ietf.org/html/rfc3550#section-6.4.1 + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +header |V=2|P| RC | PT=SR=200 | length | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | SSRC of sender | + +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +sender | NTP timestamp, most significant word | +info +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | NTP timestamp, least significant word | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | RTP timestamp | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | sender's packet count | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | sender's octet count | + +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +report | SSRC_1 (SSRC of first source) | +block +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + 1 | fraction lost | cumulative number of packets lost | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | extended highest sequence number received | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | interarrival jitter | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | last SR (LSR) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | delay since last SR (DLSR) | + +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +report | SSRC_2 (SSRC of second source) | +block +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + 2 : ... : + +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + | profile-specific extensions | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ srs_error_t err = srs_success; + data_ = buffer->head(); + nb_data_ = buffer->left(); if(srs_success != (err = decode_header(buffer))) { return srs_error_wrap(err, "decode header"); } - sender_ssrc_ = buffer->read_4bytes(); ntp_ = buffer->read_8bytes(); rtp_ts_ = buffer->read_4bytes(); send_rtp_packets_ = buffer->read_4bytes(); @@ -303,7 +422,43 @@ int SrsRtcpSR::nb_bytes() srs_error_t SrsRtcpSR::encode(SrsBuffer *buffer) { srs_error_t err = srs_success; - + /* @doc: https://tools.ietf.org/html/rfc3550#section-6.4.1 + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +header |V=2|P| RC | PT=SR=200 | length | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | SSRC of sender | + +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +sender | NTP timestamp, most significant word | +info +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | NTP timestamp, least significant word | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | RTP timestamp | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | sender's packet count | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | sender's octet count | + +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +report | SSRC_1 (SSRC of first source) | +block +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + 1 | fraction lost | cumulative number of packets lost | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | extended highest sequence number received | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | interarrival jitter | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | last SR (LSR) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | delay since last SR (DLSR) | + +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +report | SSRC_2 (SSRC of second source) | +block +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + 2 : ... : + +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + | profile-specific extensions | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ if(!buffer->require(nb_bytes())) { return srs_error_new(ERROR_RTC_RTCP, "requires %d bytes", nb_bytes()); } @@ -312,7 +467,6 @@ srs_error_t SrsRtcpSR::encode(SrsBuffer *buffer) return srs_error_wrap(err, "encode header"); } - buffer->write_4bytes(sender_ssrc_); buffer->write_8bytes(ntp_); buffer->write_4bytes(rtp_ts_); buffer->write_4bytes(send_rtp_packets_); @@ -321,13 +475,15 @@ srs_error_t SrsRtcpSR::encode(SrsBuffer *buffer) return err; } -SrsRtcpRR::SrsRtcpRR(uint32_t sender_ssrc): sender_ssrc_(sender_ssrc) +SrsRtcpRR::SrsRtcpRR(uint32_t sender_ssrc) { header_.padding = 0; header_.type = SrsRtcpType_rr; header_.rc = 0; header_.version = kRtcpVersion; header_.length = 7; + ssrc_ = sender_ssrc; + memset(&rb_, 0, sizeof(SrsRtcpRB)); } SrsRtcpRR::~SrsRtcpRR() @@ -417,16 +573,52 @@ void SrsRtcpRR::set_sender_ntp(uint64_t ntp) srs_error_t SrsRtcpRR::decode(SrsBuffer *buffer) { + /* + @doc: https://tools.ietf.org/html/rfc3550#section-6.4.2 + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +header |V=2|P| RC | PT=RR=201 | length | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | SSRC of packet sender | + +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +report | SSRC_1 (SSRC of first source) | +block +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + 1 | fraction lost | cumulative number of packets lost | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | extended highest sequence number received | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | interarrival jitter | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | last SR (LSR) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | delay since last SR (DLSR) | + +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +report | SSRC_2 (SSRC of second source) | +block +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + 2 : ... : + +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + | profile-specific extensions | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ srs_error_t err = srs_success; + data_ = buffer->head(); + nb_data_ = buffer->left(); if(srs_success != (err = decode_header(buffer))) { return srs_error_wrap(err, "decode header"); } - sender_ssrc_ = buffer->read_4bytes(); - if(header_.rc < 1) { - return err; + // @doc https://tools.ietf.org/html/rfc3550#section-6.4.2 + // An empty RR packet (RC = 0) MUST be put at the head of a compound + // RTCP packet when there is no data transmission or reception to + // report. e.g. {80 c9 00 01 00 00 00 01} + if(header_.rc == 0) { + return srs_error_new(ERROR_RTC_RTCP_EMPTY_RR, "rc=0"); } + + // TODO: FIXME: Security check for read. rb_.ssrc = buffer->read_4bytes(); rb_.fraction_lost = buffer->read_1bytes(); rb_.lost_packets = buffer->read_3bytes(); @@ -434,7 +626,8 @@ srs_error_t SrsRtcpRR::decode(SrsBuffer *buffer) rb_.jitter = buffer->read_4bytes(); rb_.lsr = buffer->read_4bytes(); rb_.dlsr = buffer->read_4bytes(); - + + // TODO: FIXME: Security check for read. if(header_.rc > 1) { char buf[1500]; buffer->read_bytes(buf, (header_.rc -1 ) * 24); @@ -450,6 +643,35 @@ int SrsRtcpRR::nb_bytes() srs_error_t SrsRtcpRR::encode(SrsBuffer *buffer) { + /* + @doc: https://tools.ietf.org/html/rfc3550#section-6.4.2 + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +header |V=2|P| RC | PT=RR=201 | length | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | SSRC of packet sender | + +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +report | SSRC_1 (SSRC of first source) | +block +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + 1 | fraction lost | cumulative number of packets lost | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | extended highest sequence number received | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | interarrival jitter | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | last SR (LSR) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | delay since last SR (DLSR) | + +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +report | SSRC_2 (SSRC of second source) | +block +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + 2 : ... : + +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + | profile-specific extensions | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ srs_error_t err = srs_success; if(!buffer->require(nb_bytes())) { @@ -460,7 +682,6 @@ srs_error_t SrsRtcpRR::encode(SrsBuffer *buffer) if(srs_success != (err = encode_header(buffer))) { return srs_error_wrap(err, "encode header"); } - buffer->write_4bytes(sender_ssrc_); buffer->write_4bytes(rb_.ssrc); buffer->write_1bytes(rb_.fraction_lost); @@ -473,14 +694,20 @@ srs_error_t SrsRtcpRR::encode(SrsBuffer *buffer) return err; } -SrsRtcpTWCC::SrsRtcpTWCC(uint32_t sender_ssrc) : sender_ssrc_(sender_ssrc), pkt_len(0) +SrsRtcpTWCC::SrsRtcpTWCCChunk::SrsRtcpTWCCChunk() + : size(0), all_same(true), has_large_delta(false) +{ +} + +SrsRtcpTWCC::SrsRtcpTWCC(uint32_t sender_ssrc) : pkt_len(0) { header_.padding = 0; header_.type = SrsRtcpType_rtpfb; header_.rc = 15; header_.version = kRtcpVersion; + ssrc_ = sender_ssrc; } - + SrsRtcpTWCC::~SrsRtcpTWCC() { } @@ -489,7 +716,7 @@ void SrsRtcpTWCC::clear() { encoded_chucks_.clear(); pkt_deltas_.clear(); - recv_packes_.clear(); + recv_packets_.clear(); recv_sns_.clear(); } @@ -563,21 +790,63 @@ void SrsRtcpTWCC::add_recv_delta(uint16_t delta) srs_error_t SrsRtcpTWCC::recv_packet(uint16_t sn, srs_utime_t ts) { - map::iterator it = recv_packes_.find(sn); - if(it != recv_packes_.end()) { + map::iterator it = recv_packets_.find(sn); + if(it != recv_packets_.end()) { return srs_error_new(ERROR_RTC_RTCP, "TWCC dup seq: %d", sn); } - recv_packes_[sn] = ts; + recv_packets_[sn] = ts; recv_sns_.insert(sn); return srs_success; } +bool SrsRtcpTWCC::need_feedback() +{ + return recv_packets_.size() > 0; +} + srs_error_t SrsRtcpTWCC::decode(SrsBuffer *buffer) { + /* + @doc: https://tools.ietf.org/html/draft-holmer-rmcat-transport-wide-cc-extensions-01#section-3.1 + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |V=2|P| FMT=15 | PT=205 | length | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | SSRC of packet sender | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | SSRC of media source | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | base sequence number | packet status count | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | reference time | fb pkt. count | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | packet chunk | packet chunk | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + . . + . . + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | packet chunk | recv delta | recv delta | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + . . + . . + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | recv delta | recv delta | zero padding | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ srs_error_t err = srs_success; - + data_ = buffer->head(); + nb_data_ = buffer->left(); + + if(srs_success != (err = decode_header(buffer))) { + return srs_error_wrap(err, "decode header"); + } + + payload_len_ = (header_.length + 1) * 4 - sizeof(SrsRtcpHeader) - 4; + buffer->read_bytes((char *)payload_, payload_len_); + return err; } @@ -782,6 +1051,34 @@ srs_error_t SrsRtcpTWCC::encode(SrsBuffer *buffer) srs_error_t SrsRtcpTWCC::do_encode(SrsBuffer *buffer) { + /* + @doc: https://tools.ietf.org/html/draft-holmer-rmcat-transport-wide-cc-extensions-01#section-3.1 + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |V=2|P| FMT=15 | PT=205 | length | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | SSRC of packet sender | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | SSRC of media source | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | base sequence number | packet status count | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | reference time | fb pkt. count | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | packet chunk | packet chunk | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + . . + . . + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | packet chunk | recv delta | recv delta | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + . . + . . + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | recv delta | recv delta | zero padding | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ srs_error_t err = srs_success; if(!buffer->require(nb_bytes())) { @@ -791,19 +1088,19 @@ srs_error_t SrsRtcpTWCC::do_encode(SrsBuffer *buffer) pkt_len = kTwccFbPktHeaderSize; set::iterator it_sn = recv_sns_.begin(); base_sn_ = *it_sn; - map::iterator it_ts = recv_packes_.find(base_sn_); + map::iterator it_ts = recv_packets_.find(base_sn_); srs_utime_t ts = it_ts->second; reference_time_ = (ts % kTwccFbReferenceTimeDivisor) / kTwccFbTimeMultiplier; srs_utime_t last_ts = (srs_utime_t)(reference_time_) * kTwccFbTimeMultiplier; uint16_t last_sn = base_sn_; - packet_count_ = recv_packes_.size(); + packet_count_ = recv_packets_.size(); // encode chunk SrsRtcpTWCC::SrsRtcpTWCCChunk chunk; for(; it_sn != recv_sns_.end(); ++it_sn) { uint16_t current_sn = *it_sn; // calculate delta - it_ts = recv_packes_.find(current_sn); + it_ts = recv_packets_.find(current_sn); srs_utime_t delta_us = calculate_delta_us(it_ts->second, last_ts); int16_t delta = delta_us; if(delta != delta_us) { @@ -847,7 +1144,6 @@ srs_error_t SrsRtcpTWCC::do_encode(SrsBuffer *buffer) if(srs_success != (err = encode_header(buffer))) { return srs_error_wrap(err, "encode header"); } - buffer->write_4bytes(sender_ssrc_); buffer->write_4bytes(media_ssrc_); buffer->write_2bytes(base_sn_); buffer->write_2bytes(packet_count_); @@ -875,12 +1171,13 @@ srs_error_t SrsRtcpTWCC::do_encode(SrsBuffer *buffer) return err; } -SrsRtcpNack::SrsRtcpNack(uint32_t sender_ssrc): sender_ssrc_(sender_ssrc) +SrsRtcpNack::SrsRtcpNack(uint32_t sender_ssrc) { header_.padding = 0; header_.type = SrsRtcpType_rtpfb; header_.rc = 1; header_.version = kRtcpVersion; + ssrc_ = sender_ssrc; } SrsRtcpNack::~SrsRtcpNack() @@ -913,13 +1210,35 @@ void SrsRtcpNack::add_lost_sn(uint16_t sn) srs_error_t SrsRtcpNack::decode(SrsBuffer *buffer) { + /* + @doc: https://tools.ietf.org/html/rfc4585#section-6.1 + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |V=2|P| FMT | PT | length | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | SSRC of packet sender | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | SSRC of media source | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + : Feedback Control Information (FCI) : + : : + + Generic NACK + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | PID | BLP | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ srs_error_t err = srs_success; + data_ = buffer->head(); + nb_data_ = buffer->left(); if(srs_success != (err = decode_header(buffer))) { return srs_error_wrap(err, "decode header"); } - sender_ssrc_ = buffer->read_4bytes(); media_ssrc_ = buffer->read_4bytes(); char bitmask[20]; for(int i = 0; i < (header_.length - 2); i++) { @@ -945,6 +1264,27 @@ int SrsRtcpNack::nb_bytes() srs_error_t SrsRtcpNack::encode(SrsBuffer *buffer) { + /* + @doc: https://tools.ietf.org/html/rfc4585#section-6.1 + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |V=2|P| FMT | PT | length | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | SSRC of packet sender | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | SSRC of media source | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + : Feedback Control Information (FCI) : + : : + + Generic NACK + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | PID | BLP | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ srs_error_t err = srs_success; if(!buffer->require(nb_bytes())) { return srs_error_new(ERROR_RTC_RTCP, "requires %d bytes", nb_bytes()); @@ -983,7 +1323,7 @@ srs_error_t SrsRtcpNack::encode(SrsBuffer *buffer) err = srs_error_wrap(err, "encode header"); break; } - buffer->write_4bytes(sender_ssrc_); + buffer->write_4bytes(media_ssrc_); for(vector::iterator it_chunk = chunks.begin(); it_chunk != chunks.end(); it_chunk++) { buffer->write_2bytes(it_chunk->pid); @@ -994,7 +1334,341 @@ srs_error_t SrsRtcpNack::encode(SrsBuffer *buffer) return err; } -SrsRtcpCompound::SrsRtcpCompound(): nb_bytes_(0) +SrsRtcpPsfbCommon::SrsRtcpPsfbCommon() +{ + header_.padding = 0; + header_.type = SrsRtcpType_psfb; + header_.rc = 1; + header_.version = kRtcpVersion; + //ssrc_ = sender_ssrc; +} + +SrsRtcpPsfbCommon::~SrsRtcpPsfbCommon() +{ + +} + +uint32_t SrsRtcpPsfbCommon::get_media_ssrc() const +{ + return media_ssrc_; +} + +void SrsRtcpPsfbCommon::set_media_ssrc(uint32_t ssrc) +{ + media_ssrc_ = ssrc; +} + +srs_error_t SrsRtcpPsfbCommon::decode(SrsBuffer *buffer) +{ + /* + @doc: https://tools.ietf.org/html/rfc4585#section-6.1 + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |V=2|P| FMT | PT | length | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | SSRC of packet sender | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | SSRC of media source | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + : Feedback Control Information (FCI) : + : : + */ + + srs_error_t err = srs_success; + data_ = buffer->head(); + nb_data_ = buffer->left(); + + if(srs_success != (err = decode_header(buffer))) { + return srs_error_wrap(err, "decode header"); + } + + media_ssrc_ = buffer->read_4bytes(); + int len = (header_.length + 1) * 4 - 12; + buffer->skip(len); + return err; +} + +int SrsRtcpPsfbCommon::nb_bytes() +{ + return kRtcpPacketSize; +} + +srs_error_t SrsRtcpPsfbCommon::encode(SrsBuffer *buffer) +{ + return srs_error_new(ERROR_RTC_RTCP, "not support"); +} + +SrsRtcpPli::SrsRtcpPli(uint32_t sender_ssrc/*= 0*/) +{ + header_.padding = 0; + header_.type = SrsRtcpType_psfb; + header_.rc = kPLI; + header_.version = kRtcpVersion; + ssrc_ = sender_ssrc; +} + +SrsRtcpPli::~SrsRtcpPli() +{ +} + +srs_error_t SrsRtcpPli::decode(SrsBuffer *buffer) +{ + /* + @doc: https://tools.ietf.org/html/rfc4585#section-6.1 + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |V=2|P| FMT | PT | length | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | SSRC of packet sender | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | SSRC of media source | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + : Feedback Control Information (FCI) : + : : + */ + + srs_error_t err = srs_success; + data_ = buffer->head(); + nb_data_ = buffer->left(); + + if(srs_success != (err = decode_header(buffer))) { + return srs_error_wrap(err, "decode header"); + } + + media_ssrc_ = buffer->read_4bytes(); + return err; +} + +int SrsRtcpPli::nb_bytes() +{ + return 12; +} + +srs_error_t SrsRtcpPli::encode(SrsBuffer *buffer) +{ + /* + @doc: https://tools.ietf.org/html/rfc4585#section-6.1 + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |V=2|P| FMT | PT | length | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | SSRC of packet sender | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | SSRC of media source | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + : Feedback Control Information (FCI) : + : : + */ + srs_error_t err = srs_success; + if(!buffer->require(nb_bytes())) { + return srs_error_new(ERROR_RTC_RTCP, "requires %d bytes", nb_bytes()); + } + + header_.length = 2; + if(srs_success != (err = encode_header(buffer))) { + return srs_error_wrap(err, "encode header"); + } + + buffer->write_4bytes(media_ssrc_); + + return err; +} + +SrsRtcpSli::SrsRtcpSli(uint32_t sender_ssrc/*= 0*/) +{ + first_ = 0; + number_ = 0; + picture_ = 0; + + header_.padding = 0; + header_.type = SrsRtcpType_psfb; + header_.rc = kSLI; + header_.version = kRtcpVersion; + ssrc_ = sender_ssrc; +} + +SrsRtcpSli::~SrsRtcpSli() +{ +} + + +srs_error_t SrsRtcpSli::decode(SrsBuffer *buffer) +{ + /* + @doc: https://tools.ietf.org/html/rfc4585#section-6.1 + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |V=2|P| FMT | PT | length | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | SSRC of packet sender | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | SSRC of media source | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + : Feedback Control Information (FCI) : + : : + + + @doc: https://tools.ietf.org/html/rfc4585#section-6.3.2 + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | First | Number | PictureID | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + + srs_error_t err = srs_success; + data_ = buffer->head(); + nb_data_ = buffer->left(); + + if(srs_success != (err = decode_header(buffer))) { + return srs_error_wrap(err, "decode header"); + } + + media_ssrc_ = buffer->read_4bytes(); + int len = (header_.length + 1) * 4 - 12; + buffer->skip(len); + return err; +} + +int SrsRtcpSli::nb_bytes() +{ + return kRtcpPacketSize; +} + +srs_error_t SrsRtcpSli::encode(SrsBuffer *buffer) +{ + srs_error_t err = srs_success; + + return err; +} + +SrsRtcpRpsi::SrsRtcpRpsi(uint32_t sender_ssrc/* = 0*/) +{ + pb_ = 0; + payload_type_ = 0; + native_rpsi_ = NULL; + nb_native_rpsi_ = 0; + + header_.padding = 0; + header_.type = SrsRtcpType_psfb; + header_.rc = kRPSI; + header_.version = kRtcpVersion; + ssrc_ = sender_ssrc; +} + +SrsRtcpRpsi::~SrsRtcpRpsi() +{ +} + +srs_error_t SrsRtcpRpsi::decode(SrsBuffer *buffer) +{ +/* + @doc: https://tools.ietf.org/html/rfc4585#section-6.1 + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |V=2|P| FMT | PT | length | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | SSRC of packet sender | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | SSRC of media source | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + : Feedback Control Information (FCI) : + : : + + + @doc: https://tools.ietf.org/html/rfc4585#section-6.3.3 + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | PB |0| Payload Type| Native RPSI bit string | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | defined per codec ... | Padding (0) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + + srs_error_t err = srs_success; + data_ = buffer->head(); + nb_data_ = buffer->left(); + + if(srs_success != (err = decode_header(buffer))) { + return srs_error_wrap(err, "decode header"); + } + + media_ssrc_ = buffer->read_4bytes(); + int len = (header_.length + 1) * 4 - 12; + buffer->skip(len); + return err; +} + +int SrsRtcpRpsi::nb_bytes() +{ + return kRtcpPacketSize; +} + +srs_error_t SrsRtcpRpsi::encode(SrsBuffer *buffer) +{ + srs_error_t err = srs_success; + + return err; +} + +SrsRtcpXr::SrsRtcpXr(uint32_t ssrc/*= 0*/) +{ + header_.padding = 0; + header_.type = SrsRtcpType_xr; + header_.rc = 0; + header_.version = kRtcpVersion; + ssrc_ = ssrc; +} + +SrsRtcpXr::~SrsRtcpXr() +{ +} + +srs_error_t SrsRtcpXr::decode(SrsBuffer *buffer) +{ +/* + @doc: https://tools.ietf.org/html/rfc3611#section-2 + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |V=2|P|reserved | PT=XR=207 | length | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | SSRC | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + : report blocks : + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + + srs_error_t err = srs_success; + data_ = buffer->head(); + nb_data_ = buffer->left(); + + if(srs_success != (err = decode_header(buffer))) { + return srs_error_wrap(err, "decode header"); + } + + int len = (header_.length + 1) * 4 - 8; + buffer->skip(len); + return err; +} + +int SrsRtcpXr::nb_bytes() +{ + return kRtcpPacketSize; +} + +srs_error_t SrsRtcpXr::encode(SrsBuffer *buffer) +{ + return srs_error_new(ERROR_RTC_RTCP, "not support"); +} + +SrsRtcpCompound::SrsRtcpCompound(): nb_bytes_(0), data_(NULL), nb_data_(0) { } @@ -1009,7 +1683,6 @@ SrsRtcpCommon* SrsRtcpCompound::get_next_rtcp() return NULL; } SrsRtcpCommon *rtcp = rtcps_.back(); - nb_bytes_ -= rtcp->nb_bytes(); rtcps_.pop_back(); return rtcp; } @@ -1029,31 +1702,60 @@ srs_error_t SrsRtcpCompound::add_rtcp(SrsRtcpCommon *rtcp) srs_error_t SrsRtcpCompound::decode(SrsBuffer *buffer) { srs_error_t err = srs_success; + data_ = buffer->data(); + nb_data_ = buffer->size(); - while (buffer->empty()) { + while (!buffer->empty()) { + SrsRtcpCommon* rtcp = NULL; SrsRtcpHeader* header = (SrsRtcpHeader*)(buffer->head()); if (header->type == SrsRtcpType_sr) { - SrsRtcpSR *rtcp = new SrsRtcpSR; - if(srs_success != (err = rtcp->decode(buffer))) { - return srs_error_wrap(err, "decode sr"); - } - nb_bytes_ += rtcp->nb_bytes(); - rtcps_.push_back(rtcp); + rtcp = new SrsRtcpSR(); } else if (header->type == SrsRtcpType_rr) { - SrsRtcpRR *rtcp = new SrsRtcpRR; - if(srs_success != (err = rtcp->decode(buffer))) { - return srs_error_wrap(err, "decode rr"); + rtcp = new SrsRtcpRR(); + } else if (header->type == SrsRtcpType_rtpfb) { + if(1 == header->rc) { + //nack + rtcp = new SrsRtcpNack(); + } else if (15 == header->rc) { + //twcc + rtcp = new SrsRtcpTWCC(); } - nb_bytes_ += rtcp->nb_bytes(); - rtcps_.push_back(rtcp); + } else if(header->type == SrsRtcpType_psfb) { + if(1 == header->rc) { + // pli + rtcp = new SrsRtcpPli(); + } else if(2 == header->rc) { + //sli + rtcp = new SrsRtcpSli(); + } else if(3 == header->rc) { + //rpsi + rtcp = new SrsRtcpRpsi(); + } else { + // common psfb + rtcp = new SrsRtcpPsfbCommon(); + } + } else if(header->type == SrsRtcpType_xr) { + rtcp = new SrsRtcpXr(); } else { - SrsRtcpCommon *rtcp = new SrsRtcpCommon; - if(srs_success != (err = rtcp->decode(buffer))) { - return srs_error_wrap(err, "decode type: %#x", header->type); - } - nb_bytes_ += rtcp->nb_bytes(); - rtcps_.push_back(rtcp); + rtcp = new SrsRtcpCommon(); } + + if(srs_success != (err = rtcp->decode(buffer))) { + srs_freep(rtcp); + + // @doc https://tools.ietf.org/html/rfc3550#section-6.4.2 + // An empty RR packet (RC = 0) MUST be put at the head of a compound + // RTCP packet when there is no data transmission or reception to + // report. e.g. {80 c9 00 01 00 00 00 01} + if (ERROR_RTC_RTCP_EMPTY_RR == srs_error_code(err)) { + srs_freep(err); + continue; + } + + return srs_error_wrap(err, "decode rtcp type=%u rc=%u", header->type, header->rc); + } + + rtcps_.push_back(rtcp); } return err; @@ -1061,7 +1763,7 @@ srs_error_t SrsRtcpCompound::decode(SrsBuffer *buffer) int SrsRtcpCompound::nb_bytes() { - return nb_bytes_; + return kRtcpPacketSize; } srs_error_t SrsRtcpCompound::encode(SrsBuffer *buffer) @@ -1094,3 +1796,14 @@ void SrsRtcpCompound::clear() rtcps_.clear(); nb_bytes_ = 0; } + +char* SrsRtcpCompound::data() +{ + return data_; +} + +int SrsRtcpCompound::size() +{ + return nb_data_; +} + diff --git a/trunk/src/kernel/srs_kernel_rtc_rtcp.hpp b/trunk/src/kernel/srs_kernel_rtc_rtcp.hpp index de311305b..c59759dd2 100644 --- a/trunk/src/kernel/srs_kernel_rtc_rtcp.hpp +++ b/trunk/src/kernel/srs_kernel_rtc_rtcp.hpp @@ -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 encoded_chucks_; std::vector pkt_deltas_; - std::map recv_packes_; + std::map recv_packets_; std::set 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 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 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); diff --git a/trunk/src/kernel/srs_kernel_rtc_rtp.cpp b/trunk/src/kernel/srs_kernel_rtc_rtp.cpp index 4e15a5637..87f4c86da 100644 --- a/trunk/src/kernel/srs_kernel_rtc_rtp.cpp +++ b/trunk/src/kernel/srs_kernel_rtc_rtp.cpp @@ -137,24 +137,20 @@ 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; } diff --git a/trunk/src/protocol/srs_protocol_utility.cpp b/trunk/src/protocol/srs_protocol_utility.cpp index 69b5bc3bd..e5f1b28ba 100644 --- a/trunk/src/protocol/srs_protocol_utility.cpp +++ b/trunk/src/protocol/srs_protocol_utility.cpp @@ -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& 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++) { diff --git a/trunk/src/protocol/srs_protocol_utility.hpp b/trunk/src/protocol/srs_protocol_utility.hpp index aaf907ec3..84e07bcc7 100644 --- a/trunk/src/protocol/srs_protocol_utility.hpp +++ b/trunk/src/protocol/srs_protocol_utility.hpp @@ -34,6 +34,7 @@ #include #include #include +#include #include @@ -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& vs, std::string separator); +template +std::string srs_join_vector_string(std::vector& 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); diff --git a/trunk/src/utest/srs_utest.cpp b/trunk/src/utest/srs_utest.cpp index 286b925e6..8e400f1f0 100644 --- a/trunk/src/utest/srs_utest.cpp +++ b/trunk/src/utest/srs_utest.cpp @@ -28,6 +28,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include #include +#include #include 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(); diff --git a/trunk/src/utest/srs_utest.hpp b/trunk/src/utest/srs_utest.hpp index 252c9928f..b6bd666ed 100644 --- a/trunk/src/utest/srs_utest.hpp +++ b/trunk/src/utest/srs_utest.hpp @@ -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) \ diff --git a/trunk/src/utest/srs_utest_rtc.cpp b/trunk/src/utest/srs_utest_rtc.cpp index 9621f5d40..a8182c441 100644 --- a/trunk/src/utest/srs_utest_rtc.cpp +++ b/trunk/src/utest/srs_utest_rtc.cpp @@ -29,10 +29,314 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include #include +#include #include 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 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::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);