Inheritance: BaseProtocol
        public void SignalDetachedFromInStream()
        {
            var protocol = ProtocolManager.GetProtocol(_rtpClient.protocolId);

            protocol?.EnqueueForDelete();
            _rtspProtocol = null;
        }
        public OutNetRTPUDPH264Stream(RtspProtocol pProtocol, StreamsManager pStreamsManager, string name, bool forceTcp) : base(pProtocol, pStreamsManager, name)
        {
            _forceTcp = forceTcp;
            _maxRTPPacketSize = _forceTcp ? 1500 : MAX_RTP_PACKET_SIZE;
            _videoData = new MsgHdr { Buffers = new[] { new byte[14],new byte[0],  } };
            _videoData.Buffers[0][0] = 0x80;
            _videoData.Buffers[0].Write(8,VideoSSRC);
            _audioData = new MsgHdr { Buffers = new[] { new byte[14],new byte[16], null } };
            _audioData.Buffers[0][0] = 0x80;
            _audioData.Buffers[0][1] = 0xe0;
            _audioData.Buffers[0].Write(8, AudioSSRC);

        }
Example #3
0
 public OutNetRTPUDPH264Stream(RtspProtocol pProtocol, StreamsManager pStreamsManager, string name, bool forceTcp) : base(pProtocol, pStreamsManager, name)
 {
     _forceTcp         = forceTcp;
     _maxRTPPacketSize = _forceTcp ? 1500 : MAX_RTP_PACKET_SIZE;
     _videoData        = new MsgHdr {
         Buffers = new[] { new byte[14], new byte[0], }
     };
     _videoData.Buffers[0][0] = 0x80;
     _videoData.Buffers[0].Write(8, VideoSSRC);
     _audioData = new MsgHdr {
         Buffers = new[] { new byte[14], new byte[16], null }
     };
     _audioData.Buffers[0][0] = 0x80;
     _audioData.Buffers[0][1] = 0xe0;
     _audioData.Buffers[0].Write(8, AudioSSRC);
 }
 public OutboundConnectivity(bool forceTcp, RtspProtocol pRTSPProtocol)
 {
     _forceTcp     = forceTcp;
     _rtspProtocol = pRTSPProtocol;
     _dataMessage  = new MsgHdr();
     _rtcpMessage  = new MsgHdr {
         Buffers = new[] { new byte[28] }
     };
     _rtcpMessage.Buffers[0][0] = 0x80;
     _rtcpMessage.Buffers[0][1] = 0xc8;
     _rtcpMessage.Buffers[0].Write(2, (ushort)6);
     _rtcpNTP     = new BufferWithOffset(_rtcpMessage.Buffers[0], 8);
     _rtcpRTP     = new BufferWithOffset(_rtcpMessage.Buffers[0], 16);
     _rtcpSPC     = new BufferWithOffset(_rtcpMessage.Buffers[0], 20);
     _rtcpSOC     = new BufferWithOffset(_rtcpMessage.Buffers[0], 24);
     _startupTime = DateTime.Now;
 }
        public InboundConnectivity(RtspProtocol rtsp, string streamName, uint bandwidthHint, TimeSpan rtcpDetectionInterval)
        {
            _rtsp       = rtsp;
            _audioRR[0] = (byte)'$'; //marker
            _audioRR[1] = 0;         //channel
            _audioRR[2] = 0;         //size
            _audioRR[3] = 56;        //size
            _audioRR[4] = 0x81;      //V,P,RC
            _audioRR[5] = 0xc9;      //PT
            _audioRR[6] = 0x00;      //length
            _audioRR[7] = 0x07;      //length
            _audioRR.Write(16, 0x00FFFFFF);

            _audioRR[36] = 0x81; //V,P,RC
            _audioRR[37] = 0xca; //PT
            _audioRR[38] = 0x00; //length
            _audioRR[39] = 0x05; //length
            _audioRR[44] = 0x01; //type
            _audioRR[45] = 0x0d; //length

            _audioRR.Write(46, "machine.local");

            _videoRR[0] = (byte)'$'; //marker
            _videoRR[1] = 0;         //channel
            _videoRR[2] = 0;         //size
            _videoRR[3] = 56;        //size
            _videoRR[4] = 0x81;      //V,P,RC
            _videoRR[5] = 0xc9;      //PT
            _videoRR[6] = 0x00;      //length
            _videoRR[7] = 0x07;      //length
            _videoRR.Write(16, 0x00FFFFFF);

            _videoRR[36] = 0x81; //V,P,RC
            _videoRR[37] = 0xca; //PT
            _videoRR[38] = 0x00; //length
            _videoRR[39] = 0x05; //length
            _videoRR[44] = 0x01; //type
            _videoRR[45] = 0x0d; //length

            _videoRR.Write(46, "machine.local");

            _streamName            = streamName;
            _bandwidthHint         = bandwidthHint;
            _rtcpDetectionInterval = rtcpDetectionInterval;
        }
        public InboundConnectivity(RtspProtocol rtsp, string streamName, uint bandwidthHint, TimeSpan rtcpDetectionInterval)
        {
            _rtsp = rtsp;
            _audioRR[0] = (byte) '$'; //marker
            _audioRR[1] = 0; //channel
            _audioRR[2] = 0; //size
            _audioRR[3] = 56; //size
            _audioRR[4] = 0x81; //V,P,RC
            _audioRR[5] = 0xc9; //PT
            _audioRR[6] = 0x00; //length
            _audioRR[7] = 0x07; //length
            _audioRR.Write(16,0x00FFFFFF);

            _audioRR[36] = 0x81; //V,P,RC
            _audioRR[37] = 0xca; //PT
            _audioRR[38] = 0x00; //length
            _audioRR[39] = 0x05; //length
            _audioRR[44] = 0x01; //type
            _audioRR[45] = 0x0d; //length

            _audioRR.Write(46,"machine.local");

            _videoRR[0] = (byte) '$'; //marker
            _videoRR[1] = 0; //channel
            _videoRR[2] = 0; //size
            _videoRR[3] = 56; //size
            _videoRR[4] = 0x81; //V,P,RC
            _videoRR[5] = 0xc9; //PT
            _videoRR[6] = 0x00; //length
            _videoRR[7] = 0x07; //length
            _videoRR.Write(16, 0x00FFFFFF);

            _videoRR[36] = 0x81; //V,P,RC
            _videoRR[37] = 0xca; //PT
            _videoRR[38] = 0x00; //length
            _videoRR[39] = 0x05; //length
            _videoRR[44] = 0x01; //type
            _videoRR[45] = 0x0d; //length

            _videoRR.Write(46, "machine.local");

            _streamName = streamName;
            _bandwidthHint = bandwidthHint;
            _rtcpDetectionInterval = rtcpDetectionInterval;
        }
        public bool HandleRTSPResponse(RtspProtocol rtspProtocol, Variant responseHeaders,ref string responseContent)
        {
           
            if (responseHeaders[RTSP_HEADERS, RTSP_HEADERS_SESSION] != null)
            {
                rtspProtocol.SessionId = responseHeaders[RTSP_HEADERS, RTSP_HEADERS_SESSION];
            }
            if (responseHeaders[RTSP_HEADERS, RTSP_HEADERS_CSEQ] == null)
            {
                FATAL("Invalid response:\n{0}", (responseHeaders.ToString()));
                return false;
            }
            Variant requestHeaders = Variant.Get();
            string requestContent = "";
            rtspProtocol.GetRequest(responseHeaders[RTSP_HEADERS, RTSP_HEADERS_CSEQ], requestHeaders, ref requestContent);
            
            //2. Get the request, get the response and call the stack further
            return HandleRTSPResponse(rtspProtocol,
                    requestHeaders,
                   ref requestContent,
                    responseHeaders,
                   ref responseContent);

        }
        private bool HandleRTSPRequestSetupInbound(RtspProtocol @from, Variant requestHeaders, string requestContent)
        {
            //1. get the transport line and split it into parts
            if (requestHeaders[RTSP_HEADERS, RTSP_HEADERS_TRANSPORT] == null)
            {
                FATAL("No transport line");
                return false;
            }
            string transportLine = requestHeaders[RTSP_HEADERS, RTSP_HEADERS_TRANSPORT];
            Variant transport = Variant.Get();
            if (!SDP.ParseTransportLine(transportLine, transport))
            {
                FATAL("Unable to parse transport line");
                return false;
            }

            //2. Check and see if it has RTP/AVP/TCP,RTP/AVP/UDP or RTP/AVP
            if ((transport["rtp/avp/tcp"] == null)
                    && (transport["rtp/avp/udp"] == null)
                    && (transport["rtp/avp"] == null))
            {
                FATAL("Invalid transport line: {0}", (transportLine));
                return false;
            }

            //3. Check to see if it has either client_port OR interleaved
            if ((transport["client_port"] == null)
                    && (transport["interleaved"] == null))
            {
                FATAL("Invalid transport line: {0}", (transportLine));
                return false;
            }
            if ((transport["client_port"] != null)
                    && (transport["interleaved"]!=null))
            {
                FATAL("Invalid transport line: {0}", (transportLine));
                return false;
            }

            //4. Get the InboundConnectivity
            InboundConnectivity pConnectivity = from.InboundConnectivity;

            //4. Find the track inside the pendingTracks collection and setup the ports or channels
            if (from.CustomParameters["pendingTracks"] != VariantType.Map)
            {
                FATAL("Invalid state. No pending tracks");
                return false;
            }
            string controlUri = requestHeaders[RTSP_FIRST_LINE,RTSP_URL];


            var track =
                @from.CustomParameters["pendingTracks"].Children.Values.SingleOrDefault(x => x["controlUri"] == controlUri);
                    
            if (track == null)
            {
                FATAL("track {0} not found", (controlUri));
                return false;
            }
                if (transport["client_port"]!=null)
                {
                    track["portsOrChannels"] = transport["client_port"];
                    track["isTcp"] =false;
                }
                else
                {
                    track["portsOrChannels"] = transport["interleaved"];
                    track["isTcp"] = true;
                }
                if (!pConnectivity.AddTrack(track, (bool)track["isAudio"]))
                {
                    FATAL("Unable to add audio track");
                    return false;
                }
                transportLine = pConnectivity.GetTransportHeaderLine(track["isAudio"], false);
  
            //5. Create a session
            from.GenerateSessionId();

            //6. prepare the response
            from.PushResponseFirstLine(RTSP_VERSION_1_0, 200, "OK");
            from.PushResponseHeader(RTSP_HEADERS_TRANSPORT, transportLine);

            //7. Send it
            return from.SendResponseMessage();
        }
        private OutboundConnectivity GetOutboundConnectivity(RtspProtocol pFrom, bool forceTcp)
        {
            //1. Get the inbound stream
            var pInNetStream =
                    (IInNetStream)Application.StreamsManager.FindByUniqueId(
                    pFrom.CustomParameters["streamId"]);
            if (pInNetStream == null)
            {
                FATAL("Inbound stream {0} not found",pFrom.CustomParameters["streamId"]);
                return null;
            }

            //2. Get the outbound connectivity
            OutboundConnectivity pOutboundConnectivity = pFrom.GetOutboundConnectivity(pInNetStream, forceTcp);
                    
            if (pOutboundConnectivity == null)
            {
                FATAL("Unable to get the outbound connectivity");
                return null;
            }
            return pOutboundConnectivity;
        }
        private bool HandleRTSPRequestSetupOutbound(RtspProtocol @from, Variant requestHeaders, string requestContent)
        {
            //1. Minimal sanitize
            if (requestHeaders[RTSP_HEADERS, RTSP_HEADERS_TRANSPORT] == null)
            {
                FATAL("RTSP %s request doesn't have %s header line",
                        RTSP_METHOD_SETUP,
                        RTSP_HEADERS_TRANSPORT);
                return false;
            }

            //2. get the transport header line
            string raw = requestHeaders[RTSP_HEADERS, RTSP_HEADERS_TRANSPORT];
            Variant transport = Variant.Get();
            if (!Rtsp.SDP.ParseTransportLine(raw, transport))
            {
                FATAL("Unable to parse transport line {0}", (raw));
                return false;
            }
            bool forceTcp = false;
            if (transport["client_port"]!=null && (transport["rtp/avp/udp"]!=null || transport["rtp/avp"]!=null))
            {
                forceTcp = false;
            }
            else if (transport["interleaved"]!=null && transport["rtp/avp/tcp"]!=null)
            {
                forceTcp = true;
            }
            else
            {
                FATAL("Invalid transport line: {0}", (transport.ToString()));
                return false;
            }
            from.CustomParameters["forceTcp"] = forceTcp;
            
            //3. Get the outbound connectivity
            OutboundConnectivity pOutboundConnectivity = GetOutboundConnectivity(from, forceTcp);
            if (pOutboundConnectivity == null)
            {
                FATAL("Unable to get the outbound connectivity");
                return false;
            }

            //5. Find out if this is audio or video
            bool isAudioTrack;
            string rawUrl = requestHeaders[RTSP_FIRST_LINE,RTSP_URL];
            string audioTrackId = "trackID=" + from.CustomParameters["audioTrackId"];
            string videoTrackId = "trackID=" + from.CustomParameters["videoTrackId"];
            /*FINEST("rawUrl: %s; audioTrackId: %s; videoTrackId: %s; fa: %d; fv: %d",
                (rawUrl),
                (audioTrackId),
                (videoTrackId),
                rawUrl.find(audioTrackId) != string::npos,
                rawUrl.find(videoTrackId) != string::npos
            );*/
            if (rawUrl.Contains(audioTrackId))
            {
                isAudioTrack = true;
            }
            else if (rawUrl.Contains(videoTrackId))
            {
                isAudioTrack = false;
            }
            else
            {
                FATAL("Invalid track. Wanted: {0} or {1}; Got: {2}",
                        (from.CustomParameters["audioTrackId"]),
                        (from.CustomParameters["videoTrackId"]),
                        (requestHeaders[RTSP_FIRST_LINE,RTSP_URL]));
                return false;
            }
            from.CustomParameters["isAudioTrack"] = isAudioTrack;
            
            if (isAudioTrack)
            {
                if (forceTcp)
                {
                    from.CustomParameters["audioDataChannelNumber"] = transport["interleaved","data"];
                    from.CustomParameters["audioRtcpChannelNumber"] = transport["interleaved","rtcp"];
                    from.CustomParameters["audioTrackUri"] = requestHeaders[RTSP_FIRST_LINE,RTSP_URL];
                    pOutboundConnectivity.HasAudio = true;
                }
                else
                {
                    from.CustomParameters["audioDataPortNumber"] = transport["client_port","data"];
                    from.CustomParameters["audioRtcpPortNumber"] = transport["client_port","rtcp"];
                    from.CustomParameters["audioTrackUri"] = requestHeaders[RTSP_FIRST_LINE,RTSP_URL];
                    pOutboundConnectivity.HasAudio = true;
                }
            }
            else
            {
                if (forceTcp)
                {
                    from.CustomParameters["videoDataChannelNumber"] = transport["interleaved","data"];
                    from.CustomParameters["videoRtcpChannelNumber"] = transport["interleaved","rtcp"];
                    from.CustomParameters["videoTrackUri"] = requestHeaders[RTSP_FIRST_LINE,RTSP_URL];
                    pOutboundConnectivity.HasVideo = true;
                }
                else
                {
                    from.CustomParameters["videoDataPortNumber"] = transport["client_port","data"];
                    from.CustomParameters["videoRtcpPortNumber"] = transport["client_port","rtcp"];
                    from.CustomParameters["videoTrackUri"] = requestHeaders[RTSP_FIRST_LINE,RTSP_URL];
                    pOutboundConnectivity.HasVideo = true;
                }
            }
            
            //10. Create a session
            from.GenerateSessionId();

            //11 Compose the response
            from.PushResponseFirstLine(RTSP_VERSION_1_0, 200, "OK");
            @from.PushResponseHeader(RTSP_HEADERS_TRANSPORT,
                forceTcp
                    ? "RTP/AVP/TCP;unicast;interleaved="+ transport["interleaved","all"]
                    : $"RTP/AVP/UDP;unicast;source={((TCPCarrier) @from.IOHandler).NearIP};client_port={(string)(transport["client_port","all"])};server_port={(isAudioTrack ? (pOutboundConnectivity.AudioPorts) : (pOutboundConnectivity.VideoPorts))};ssrc={(isAudioTrack ? pOutboundConnectivity.AudioSSRC : pOutboundConnectivity.VideoSSRC)}");
            //12. Done
            return from.SendResponseMessage();
        }
 private bool HandleRTSPResponse200Options(RtspProtocol rtspProtocol, Variant requestHeaders, ref string requestContent, Variant responseHeaders, ref string responseContent)
 {
     if (rtspProtocol.HasConnectivity) return true;
     if (rtspProtocol.CustomParameters["connectionType"] == null)
     {
         FATAL("Bogus connection");
         rtspProtocol.EnqueueForDelete();
         return false;
     }
     //1. Sanitize
     if (responseHeaders[RTSP_HEADERS, RTSP_HEADERS_PUBLIC] == null)
     {
         FATAL("Invalid response:\n{0}", (responseHeaders.ToString()));
         return false;
     }
     //2. get the raw options
     string raw = responseHeaders[RTSP_HEADERS, RTSP_HEADERS_PUBLIC];
     //3. split and normalize the options
     var parts = raw.Split(',').Select(x => x.Split(':')).ToDictionary(x => x[0], x => x[1]);
     string url = requestHeaders[RTSP_FIRST_LINE, RTSP_URL];
     switch ((string)rtspProtocol.CustomParameters["connectionType"])
     {
         case "pull":
             //4. Test the presence of the wanted methods
             if (!parts.ContainsKey(RTSP_METHOD_DESCRIBE) || !parts.ContainsKey(RTSP_METHOD_SETUP) ||
                 !parts.ContainsKey(RTSP_METHOD_PLAY))
             {
                 FATAL("Some of the supported methods are missing: {0}", (raw));
                 return false;
             }
             rtspProtocol.PushRequestFirstLine(RTSP_METHOD_DESCRIBE, url, RTSP_VERSION_1_0);
             rtspProtocol.PushRequestHeader(RTSP_HEADERS_ACCEPT, RTSP_HEADERS_ACCEPT_APPLICATIONSDP);
             return rtspProtocol.SendRequestMessage();
         case "push":
             //4. Test the presence of the wanted methods
             if (!parts.ContainsKey(RTSP_METHOD_ANNOUNCE) || !parts.ContainsKey(RTSP_METHOD_SETUP) ||
                 !parts.ContainsKey(RTSP_METHOD_RECORD))
             {
                 FATAL("Some of the supported methods are missing: {0}", (raw));
                 return false;
             }
             var parameters = rtspProtocol.CustomParameters;
             rtspProtocol.PushRequestFirstLine(RTSP_METHOD_ANNOUNCE,url,RTSP_VERSION_1_0);
             var sdp = ComputeSDP(rtspProtocol,
                 parameters["customParameters","localStreamConfig","localStreamName"],
                 parameters["customParameters","localStreamConfig","targetStreamName"],
                 parameters["customParameters","localStreamConfig","targetUri","host"]);
             if (sdp == "")
             {
                 FATAL("Unable to compute sdp");
                 return false;
             }
             rtspProtocol.PushRequestHeader(RTSP_HEADERS_CONTENT_TYPE, RTSP_HEADERS_ACCEPT_APPLICATIONSDP);
             rtspProtocol.PushRequestContent(sdp,false);
             return rtspProtocol.SendRequestMessage();
         default:
             FATAL("Bogus connection");
             rtspProtocol.EnqueueForDelete();
             return false;
     }
 }
 private bool HandleRTSPResponse200Play(RtspProtocol rtspProtocol, Variant requestHeaders, ref string requestContent, Variant responseHeaders, ref string responseContent)
 {
     //1. Get the inbound connectivity
     if (rtspProtocol.InboundConnectivity == null)
     {
         FATAL("Unable to get inbound connectivity");
         return false;
     }
     //2. Create the stream
     if (!rtspProtocol.InboundConnectivity.Initialize())
     {
         FATAL("Unable to initialize inbound connectivity");
         return false;
     }
     //3. Enable keep alive
     return rtspProtocol.EnableKeepAlive(10, rtspProtocol.CustomParameters["uri","fullUri"]);
 }
        private string GetAudioTrack(RtspProtocol pFrom, StreamCapabilities pCapabilities)
        {
            pFrom.CustomParameters["audioTrackId"] = "1"; //md5(format("A%u%s",pFrom->GetId(), STR(generateRandomString(4))), true);
            string result = "";
            
            switch (pCapabilities.AudioCodecId)
            {
                case AudioCodec.Aac:
                    result += "m=audio 0 RTP/AVP 96\r\n";
                    result += "a=recvonly\r\n";
                    result += $"a=rtpmap:96 mpeg4-generic/{pCapabilities.Aac._sampleRate}/2\r\n";
                    //FINEST("result: %s", STR(result));
                    result += "a=control:trackID="
                              + pFrom.CustomParameters["audioTrackId"] + "\r\n";
                    //rfc3640-fmtp-explained.txt Chapter 4.1
                    result +=
                        $"a=fmtp:96 streamtype=5; profile-level-id=15; mode=AAC-hbr; {pCapabilities.Aac.GetRTSPFmtpConfig()}; SizeLength=13; IndexLength=3; IndexDeltaLength=3;\r\n";
                    break;
                case AudioCodec.Speex:
                    result += "m=audio 0 RTP/AVP 98\r\n";
                    result += "a=rtpmap:98 speex/16000\r\n";
                    //FINEST("result: %s", STR(result));
                    result += "a=control:trackID="+ pFrom.CustomParameters["audioTrackId"] + "\r\n";

                    //http://www.rfc-editor.org/rfc/rfc5574.txt
                    result +="a=fmtp:98 mode=\"7,any\"\r\n";
                    break;
                default:
                    WARN("Unsupported audio codec: {0}", pCapabilities.AudioCodecId);
                    break;
            }
            return result;
        }
        private string ComputeSDP(RtspProtocol pFrom,string localStreamName, string targetStreamName, string host)
        {
            StreamCapabilities pCapabilities = null;
            var pInboundStream = GetInboundStream(localStreamName);
           
            if (pInboundStream == null)
                FATAL("Stream {0} not found", localStreamName);
            else
                pCapabilities = pInboundStream.Capabilities;
            if (pCapabilities == null)
            {
                FATAL("Inbound stream {0} not found", (localStreamName));
                return "";
            }
            Debug(pCapabilities.AudioCodecId.ToString());
            string audioTrack = GetAudioTrack(pFrom, pCapabilities);
            string videoTrack = GetVideoTrack(pFrom, pCapabilities);
            if (audioTrack == "" && videoTrack == "")
                return "";

            string nearAddress = "0.0.0.0";
            string farAddress = "0.0.0.0";
            if ((pFrom.IOHandler != null)
                    && (pFrom.IOHandler.Type == IOHandlerType.IOHT_TCP_CARRIER))
            {
                nearAddress = ((TCPCarrier)pFrom.IOHandler).NearIP;
                farAddress = ((TCPCarrier)pFrom.IOHandler).FarIP;
            }

            if (targetStreamName == "")
                targetStreamName = localStreamName;

            //3. Prepare the body of the response

            var sw = new StringBuilder();
            sw.AppendLine("v=0");
            sw.AppendLine($"o=- {pFrom.Id} 0 IN IP4 {nearAddress}");
            sw.AppendLine("s=" + targetStreamName);
            sw.AppendLine("u=http://www.linkage.com");
            //result += "[email protected]\r\n";
            sw.AppendLine("c=IN IP4 " + nearAddress);
            sw.AppendLine("t=0 0");
            //result += "a=recvonly\r\n";
            sw.Append(audioTrack);
            sw.Append(videoTrack);
            
            //FINEST("result:\n%s", STR(result));
            return sw.ToString();
        }
        private string GetVideoTrack(RtspProtocol pFrom, StreamCapabilities pCapabilities)
        {
            pFrom.CustomParameters["videoTrackId"] = "2"; //md5(format("V%u%s",pFrom->GetId(), STR(generateRandomString(4))), true);
            var sw = new StringBuilder();
            
            if (pCapabilities.VideoCodecId == VideoCodec.H264)
            {
                sw.AppendLine("m=video 0 RTP/AVP 97");
                sw.AppendLine("a=recvonly");
                sw.Append("a=control:trackID=");
                sw.AppendLine(pFrom.CustomParameters["videoTrackId"]);
                sw.AppendLine("a=rtpmap:97 H264/90000");
                sw.Append("a=fmtp:97 profile-level-id=");
                sw.Append($"{pCapabilities.Avc.SPS[1]:X2}{pCapabilities.Avc.SPS[2]:X2}{pCapabilities.Avc.SPS[3]:X2}");
                sw.Append("; packetization-mode=1; sprop-parameter-sets=");
                sw.Append(Convert.ToBase64String(pCapabilities.Avc.SPS) + ",");
                sw.AppendLine(Convert.ToBase64String(pCapabilities.Avc.PPS));
            }
            else
            {

                WARN("Unsupported video codec: %s", pCapabilities.VideoCodecId);
            }
            return sw.ToString();
        }
        private bool HandleRTSPRequestPlay(RtspProtocol pFrom, Variant requestHeaders, string requestContent)
        {
            //1. Get the outbound connectivity
            bool forceTcp = pFrom.CustomParameters["forceTcp"];
            var pOutboundConnectivity = GetOutboundConnectivity(pFrom, true);
            if (pOutboundConnectivity == null)
            {
                FATAL("Unable to get the outbound connectivity");
                return false;
            }

            if (forceTcp)
            {
                //3. Get the audio/video client ports
                byte videoDataChannelNumber = 0xff;
                byte videoRtcpChannelNumber = 0xff;
                byte audioDataChannelNumber = 0xff;
                byte audioRtcpChannelNumber = 0xff;
                if (pFrom.CustomParameters["audioDataChannelNumber"]!=null)
                {
                    audioDataChannelNumber = pFrom.CustomParameters["audioDataChannelNumber"];
                    audioRtcpChannelNumber = pFrom.CustomParameters["audioRtcpChannelNumber"];
                }
                if (pFrom.CustomParameters["videoDataChannelNumber"] != null)
                {
                    videoDataChannelNumber = pFrom.CustomParameters["videoDataChannelNumber"];
                    videoRtcpChannelNumber = pFrom.CustomParameters["videoRtcpChannelNumber"];
                }

                //4.register the video
                if (videoDataChannelNumber != 0xff)
                {
                    if (!pOutboundConnectivity.RegisterTCPVideoClient(pFrom.Id,
                            videoDataChannelNumber, videoRtcpChannelNumber))
                    {
                        FATAL("Unable to register video stream");
                        return false;
                    }
                }

                //5. Register the audio
                if (audioDataChannelNumber != 0xff)
                {
                    if (!pOutboundConnectivity.RegisterTCPAudioClient(pFrom.Id,
                            audioDataChannelNumber, audioRtcpChannelNumber))
                    {
                        FATAL("Unable to register audio stream");
                        return false;
                    }
                }
            }
            else
            {
                //3. Get the audio/video client ports
                ushort videoDataPortNumber = 0;
                ushort videoRtcpPortNumber = 0;
                ushort audioDataPortNumber = 0;
                ushort audioRtcpPortNumber = 0;
                if (pFrom.CustomParameters["audioDataPortNumber"] != null)
                {
                    audioDataPortNumber = pFrom.CustomParameters["audioDataPortNumber"];
                    audioRtcpPortNumber = pFrom.CustomParameters["audioRtcpPortNumber"];
                }
                if (pFrom.CustomParameters["videoDataPortNumber"] != null)
                {
                    videoDataPortNumber = pFrom.CustomParameters["videoDataPortNumber"];
                    videoRtcpPortNumber = pFrom.CustomParameters["videoRtcpPortNumber"];
                }

                //4.register the video
                if (videoDataPortNumber != 0)
                {
                    var videoDataAddress = ((TCPCarrier)pFrom.IOHandler).FarInfo;
                    videoDataAddress.Port = videoDataPortNumber;
                    var videoRtcpAddress = ((TCPCarrier)pFrom.IOHandler).FarInfo;
                    videoRtcpAddress.Port = videoRtcpPortNumber;
                    if (!pOutboundConnectivity.RegisterUDPVideoClient(pFrom.Id,
                            videoDataAddress, videoRtcpAddress))
                    {
                        FATAL("Unable to register video stream");
                        return false;
                    }
                }

                //5. Register the audio
                if (audioDataPortNumber != 0)
                {
                    var audioDataAddress = ((TCPCarrier)pFrom.IOHandler).FarInfo;
                    audioDataAddress.Port = audioDataPortNumber;
                    var audioRtcpAddress = ((TCPCarrier)pFrom.IOHandler).FarInfo;
                    audioRtcpAddress.Port = audioRtcpPortNumber;
                    if (!pOutboundConnectivity.RegisterUDPAudioClient(pFrom.Id,audioDataAddress, audioRtcpAddress))
                    {
                        FATAL("Unable to register audio stream");
                        return false;
                    }
                }
            }

            //6. prepare the response
            pFrom.PushResponseFirstLine(RTSP_VERSION_1_0, 200, "OK");
            //7. Done
            return pFrom.SendResponseMessage();
        }
        private bool HandleRTSPRequestAnnounce(RtspProtocol pFrom, Variant requestHeaders, string requestContent)
        {
            //1. Make sure we ONLY handle application/sdp
            if ((string)requestHeaders[RTSP_HEADERS,RTSP_HEADERS_CONTENT_TYPE]!= RTSP_HEADERS_ACCEPT_APPLICATIONSDP)
            {
                FATAL("Invalid ANNOUNCE request:\n{0}", (requestHeaders.ToString()));
                return false;
            }

            //2. Get the SDP
            var sdp = pFrom.InboundSDP;

            //3. Parse the SDP
            if (!SDP.ParseSDP(sdp, requestContent))
            {
                FATAL("Unable to parse the SDP");
                return false;
            }

            //4. Get the first video track
            var videoTrack = sdp.GetVideoTrack(0,requestHeaders[RTSP_FIRST_LINE,RTSP_URL]);
                    
            var audioTrack = sdp.GetAudioTrack(0,requestHeaders[RTSP_FIRST_LINE,RTSP_URL]);
                    

            //5. Store the tracks inside the session for later use
            if (audioTrack != VariantType.Null)
            {
                pFrom.CustomParameters["pendingTracks",audioTrack["globalTrackIndex"]] = audioTrack;
            }
            if (videoTrack != VariantType.Null)
            {
                pFrom.CustomParameters["pendingTracks",videoTrack["globalTrackIndex"]] = videoTrack;
            }

            //6. Mark this connection as inbound connection
            pFrom.CustomParameters["isInbound"] = true;

            //7. Save the streamName
            string streamName = sdp.GetStreamName();
            if (streamName == "")
            {
                streamName = $"rtsp_stream_{pFrom.Id}";
            }
            pFrom.CustomParameters["sdpStreamName"] = streamName;
            streamName = new Uri(requestHeaders[RTSP_FIRST_LINE, RTSP_URL],UriKind.Absolute).Segments.Last();
            //8. Save the bandwidth hint
            pFrom.CustomParameters["sdpBandwidthHint"] = sdp.GetTotalBandwidth();

            //9. Get the inbound connectivity
            InboundConnectivity pInboundConnectivity = pFrom.GetInboundConnectivity(
                    streamName,
                    sdp.GetTotalBandwidth(),Application.Configuration[CONF_APPLICATION_RTCPDETECTIONINTERVAL]);
            if (pInboundConnectivity == null)
            {
                FATAL("Unable to create inbound connectivity");
                return false;
            }

            //8. Send back the response
            pFrom.PushResponseFirstLine(RTSP_VERSION_1_0, 200, "OK");
            return pFrom.SendResponseMessage();
        }
        public bool HandleRTSPRequest(RtspProtocol from,Variant requestHeaders,string requestContent)
        {
            string method = requestHeaders[RTSP_FIRST_LINE, RTSP_METHOD];
            //1. we need a CSeq
            if (requestHeaders[RTSP_HEADERS, RTSP_HEADERS_CSEQ] == null)
            {
                FATAL("Request doesn't have {0}:\n{1}", RTSP_HEADERS_CSEQ, requestHeaders);
                return false;
            }
            //2. Validate session id
            string wantedSessionId = from.SessionId;
            if (!string.IsNullOrEmpty(wantedSessionId))
            {
                var requestSessionId = "";
                if (requestHeaders[RTSP_HEADERS, RTSP_HEADERS_SESSION] == null)
                {
                    FATAL("No session id");
                    return false;
                }
                requestSessionId = requestHeaders[RTSP_HEADERS, RTSP_HEADERS_SESSION];
                var parts = requestSessionId.Split(';');
                if (parts.Length >= 1)
                {
                    requestSessionId = parts[0];
                }
                if (requestSessionId != wantedSessionId)
                {
                    FATAL("Invalid session ID. Wanted: `{0}`; Got: `{1}`",
                            (wantedSessionId),
                            (requestSessionId));
                    return false;
                }
            }
            //4. Prepare a fresh new response. Add the sequence number
            from.ClearResponseMessage();
            from.PushResponseHeader(RTSP_HEADERS_CSEQ, requestHeaders[RTSP_HEADERS, RTSP_HEADERS_CSEQ]);
            //5. Do we have authentication? We will authenticate everything except "OPTIONS"
            if ( !string.IsNullOrEmpty(_usersFile) && NeedAuthentication(from,requestHeaders,requestContent))
            {
                //6. Re-parse authentication file if necessary
                if (!ParseUsersFile())
                {
                    FATAL("Unable to parse authentication file");
                    return false;
                }
                //7. Get the real name to use it further in authentication process
                string realmName = GetAuthenticationRealm(from, requestHeaders,
                        requestContent);
                //8. Do we have that realm?
                if (_realms[realmName]==null)
                {
                    FATAL("Realm `{0}` not found", (realmName));
                    return false;
                }
                Variant realm = _realms[realmName];
                //9. Is the user even trying to authenticate?
                if (requestHeaders[RTSP_HEADERS, RTSP_HEADERS_AUTHORIZATION] == null)
                {
                    return SendAuthenticationChallenge(from, realm);
                }
                else
                {
                    //14. The client sent us some response. Validate it now
                    //Did we ever sent him an authorization challange?
                    if (from.CustomParameters["wwwAuthenticate"] ==null)
                    {
                        FATAL("Client tried to authenticate and the server didn't required that");
                        return false;
                    }

                    //15. Get the server challenge
                    string wwwAuthenticate = from.CustomParameters["wwwAuthenticate"];

                    //16. Get the client response
                    string authorization = requestHeaders[RTSP_HEADERS, RTSP_HEADERS_AUTHORIZATION];

                    //17. Try to authenticate
                    if (!HTTPAuthHelper.ValidateAuthRequest(wwwAuthenticate,
                            authorization,
                            method,
                            (string)requestHeaders[RTSP_FIRST_LINE,RTSP_URL],
                            realm))
                    {
                        WARN("Authorization failed: challenge: {0}; response: {1}",
                                wwwAuthenticate, authorization);
                        return SendAuthenticationChallenge(from, realm);
                    }

                    //18. Success. User authenticated
                    //INFO("User authenticated: %s", (authorization));
                }
            }
            switch (method)
            {
                case RTSP_METHOD_OPTIONS:
                    from.PushResponseFirstLine(RTSP_VERSION_1_0, 200, "OK");
                    from.PushResponseHeader(RTSP_HEADERS_PUBLIC, "DESCRIBE, OPTIONS, PAUSE, PLAY, SETUP, TEARDOWN, ANNOUNCE, RECORD");
                    return from.SendResponseMessage();
                case RTSP_METHOD_DESCRIBE:
                    //1. get the stream name
                    Uri uri = new Uri(requestHeaders[RTSP_FIRST_LINE,RTSP_URL]);
                    
                    string streamName = (uri.Segments.LastOrDefault(x=>!x.EndsWith("/"))??"")+uri.Query;
                    if (streamName == "")
                    {
                        FATAL("Invalid stream name");
                        return false;
                    }

                    //2. Get the inbound stream capabilities
                    IInNetStream pInStream = GetInboundStream(streamName);

                    //3. Prepare the body of the response
                    string outboundContent = ComputeSDP(from, streamName, "", "0.0.0.0");
                    if (outboundContent == "")
                    {
                        FATAL("Unable to compute SDP");
                        return false;
                    }

                    //4. Save the stream id for later usage
                    from.CustomParameters["streamId"] = pInStream.UniqueId;

                    //5. Mark this connection as outbound connection
                    from.CustomParameters["isInbound"] = false;

                    //6. prepare the response
                    from.PushResponseFirstLine(RTSP_VERSION_1_0, 200, "OK");
                    from.PushResponseHeader(RTSP_HEADERS_CONTENT_TYPE, RTSP_HEADERS_ACCEPT_APPLICATIONSDP);
                    from.PushResponseContent(outboundContent, false);

                    //7. Done
                    return from.SendResponseMessage();
                case RTSP_METHOD_SETUP:
                    if (from.CustomParameters["isInbound"] != VariantType.Boolean)
                    {
                        FATAL("Invalid state");
                        return false;
                    }

                    return @from.CustomParameters["isInbound"] ? HandleRTSPRequestSetupInbound(@from, requestHeaders, requestContent) : HandleRTSPRequestSetupOutbound(@from, requestHeaders, requestContent);
                case RTSP_METHOD_PLAY:
                    return HandleRTSPRequestPlay(from, requestHeaders, requestContent);
                case RTSP_METHOD_TEARDOWN:
                    from.EnqueueForDelete();
                    return true;
                case RTSP_METHOD_ANNOUNCE:
                    return HandleRTSPRequestAnnounce(from, requestHeaders, requestContent);
                case RTSP_METHOD_RECORD:
                    //1. Make sure we have everything and we are in the proper state
                    if ((from.CustomParameters["isInbound"] != VariantType.Boolean)
                            || ((bool)from.CustomParameters["isInbound"] != true))
                    {
                        FATAL("Invalid state");
                        return false;
                    }

                    if (from.CustomParameters["pendingTracks"] != VariantType.Map)
                    {
                        FATAL("Invalid state");
                        return false;
                    }

                    //3. Get the inbound connectivity
                    InboundConnectivity pConnectivity = from.InboundConnectivity;
                    if (pConnectivity == null)
                    {
                        FATAL("Unable to get inbound connectivity");
                        return false;
                    }
                    if (!pConnectivity.Initialize())
                    {
                        FATAL("Unable to initialize inbound connectivity");
                        return false;
                    }

                    //4. Send back the response
                    from.PushResponseFirstLine(RTSP_VERSION_1_0, 200, "OK");
                    return from.SendResponseMessage();
                case RTSP_METHOD_PAUSE:
                    from.PushResponseFirstLine(RTSP_VERSION_1_0, 200, "OK");
                    return from.SendResponseMessage();
                default:
                    return false;
            }
        }
 private bool HandleRTSPResponse(RtspProtocol rtspProtocol, Variant requestHeaders,ref string requestContent, Variant responseHeaders,ref string responseContent)
 {
     switch ((uint)responseHeaders[RTSP_FIRST_LINE, RTSP_STATUS_CODE])
     {
         case 200:
             switch ((string)requestHeaders[RTSP_FIRST_LINE,RTSP_METHOD])
             {
                 case RTSP_METHOD_OPTIONS:
                     return HandleRTSPResponse200Options(rtspProtocol,requestHeaders,ref requestContent, responseHeaders,ref responseContent);
                 case RTSP_METHOD_DESCRIBE:
                     return HandleRTSPResponse200Describe(rtspProtocol, requestHeaders, ref requestContent, responseHeaders, ref responseContent);
                 case RTSP_METHOD_SETUP:
                     return HandleRTSPResponse200Setup(rtspProtocol, requestHeaders, ref requestContent, responseHeaders, ref responseContent);
                 case RTSP_METHOD_PLAY:
                     return HandleRTSPResponse200Play(rtspProtocol, requestHeaders, ref requestContent, responseHeaders, ref responseContent);
                 case RTSP_METHOD_ANNOUNCE:
                     return HandleRTSPResponse200Announce(rtspProtocol, requestHeaders, ref requestContent, responseHeaders, ref responseContent);
                 case RTSP_METHOD_RECORD:
                     return HandleRTSPResponse200Record(rtspProtocol, requestHeaders, ref requestContent, responseHeaders, ref responseContent);
                 case RTSP_METHOD_TEARDOWN:
                     return true;
                 default:
                     return false;
             }
         case 401:
             var username = rtspProtocol.CustomParameters["uri", "userName"];
             var password = rtspProtocol.CustomParameters["uri", "password"];
             if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password))
             {
                 FATAL("No username/password provided");
                 return false;
             }
             string auth = responseHeaders[RTSP_HEADERS, HTTP_HEADERS_WWWAUTHENTICATE];
             if (string.IsNullOrEmpty(auth))
             {
                 FATAL("Invalid 401 response: {0}", (responseHeaders.ToString()));
                 return false;
             }
             if (!rtspProtocol.SetAuthentication(auth, username, password))
             {
                 FATAL("Unable to authenticate: request headers:\n{0}\nresponseHeaders:\n{1}",
         (requestHeaders.ToString()),
         (responseHeaders.ToString()));
                 return false;
             }
             return true;
         case 404:
             switch ((string)requestHeaders[RTSP_FIRST_LINE, RTSP_METHOD])
             {
                 case RTSP_METHOD_PLAY:
                     FATAL("PLAY: Resource not found: "+(requestHeaders[RTSP_FIRST_LINE][RTSP_URL]));
                     return false;
                 case RTSP_METHOD_DESCRIBE:
                     FATAL("DESCRIBE: Resource not found: "+(requestHeaders[RTSP_FIRST_LINE][RTSP_URL]));
                     return false;
                 default:
                     FATAL("Response for method {0} not implemented yet\n{1}", ((string)requestHeaders[RTSP_FIRST_LINE, RTSP_METHOD]),
         (responseHeaders.ToString()));
                     return false;
             }
         default:
             return false;
     }
 }
 private bool HandleRTSPResponse200Describe(RtspProtocol rtspProtocol, Variant requestHeaders, ref string requestContent, Variant responseHeaders, ref string responseContent)
 {
     //1. Make sure we ONLY handle application/sdp
     if (responseHeaders[RTSP_HEADERS, RTSP_HEADERS_CONTENT_TYPE] == null)
     {
         FATAL("Invalid DESCRIBE response:\n{0}", (requestHeaders.ToString()));
         return false;
     }
     if (responseHeaders[RTSP_HEADERS, RTSP_HEADERS_CONTENT_TYPE] == RTSP_HEADERS_ACCEPT_APPLICATIONSDP)
     {
         FATAL("Invalid DESCRIBE response:\n{0}", (requestHeaders.ToString()));
         return false;
     }
     //2. Get the SDP
     var sdp = rtspProtocol.InboundSDP;
     //3. Parse the SDP
     if (!SDP.ParseSDP(sdp, responseContent))
     {
         FATAL("Unable to parse the SDP");
         return false;
     }
     //4. Get the first video track
     var videoTrack = sdp.GetVideoTrack(0,requestHeaders[RTSP_FIRST_LINE,RTSP_URL]);
     var audioTrack = sdp.GetAudioTrack(0,requestHeaders[RTSP_FIRST_LINE,RTSP_URL]);
     if ((videoTrack == VariantType.Null) && (audioTrack == VariantType.Null))
     {
         FATAL("No compatible tracks found");
         return false;
     }
     var forceTcp = rtspProtocol.CustomParameters["forceTcp"];
     var rtcpDetectionInterval = Application.Configuration[CONF_APPLICATION_RTCPDETECTIONINTERVAL];
     if (rtspProtocol.CustomParameters[CONF_APPLICATION_RTCPDETECTIONINTERVAL]!=null)
         rtcpDetectionInterval = (byte)rtspProtocol.CustomParameters[CONF_APPLICATION_RTCPDETECTIONINTERVAL];
     //5. Store the tracks inside the session for later use
     if (audioTrack != VariantType.Null)
     {
         audioTrack["isTcp"] = (bool)forceTcp;
         rtspProtocol.CustomParameters["pendingTracks"][(int)audioTrack["globalTrackIndex"]] = audioTrack;
     }
     if (videoTrack != VariantType.Null)
     {
         videoTrack["isTcp"] = (bool)forceTcp;
         rtspProtocol.CustomParameters["pendingTracks"][(int)videoTrack["globalTrackIndex"]] = videoTrack;
     }
     //6. Save the streamName
     string streamName = sdp.GetStreamName();
     if (streamName == "")
     {
         streamName = "rtsp_stream_"+rtspProtocol.Id;
     }
     rtspProtocol.CustomParameters["sdpStreamName"] = streamName;
     //7. Save the bandwidth hint
     rtspProtocol.CustomParameters["sdpBandwidthHint"] = sdp.GetTotalBandwidth();
     //8. Get the inbound connectivity
     var pInboundConnectivity = rtspProtocol.GetInboundConnectivity(streamName, sdp.GetTotalBandwidth(),
         rtcpDetectionInterval);
     if (pInboundConnectivity == null)
     {
         FATAL("Unable to create inbound connectivity");
         return false;
     }
     //9. Start sending the setup commands on the pending tracks;
     return SendSetupTrackMessages(rtspProtocol);
 }
        private bool HandleRTSPResponse200Setup(RtspProtocol rtspProtocol, Variant requestHeaders, ref string requestContent, Variant responseHeaders, ref string responseContent)
        {
            if (rtspProtocol.CustomParameters["connectionType"] == "pull")
            {
                if (responseHeaders[RTSP_FIRST_LINE, RTSP_STATUS_CODE] != 200)
                {
                    FATAL("request {0} failed with response {1}",
                        (requestHeaders.ToString()),
                        (responseHeaders.ToString()));
                    return false;
                }
                if (rtspProtocol.CustomParameters["pendingTracks"].ArrayLength != 0)
                    return SendSetupTrackMessages(rtspProtocol);
                //2. Do the play command
                var uri = rtspProtocol.CustomParameters["uri", "fullUri"];
                //3. prepare the play command
                rtspProtocol.PushRequestFirstLine(RTSP_METHOD_PLAY, uri, RTSP_VERSION_1_0);
                return rtspProtocol.SendRequestMessage();
            }
            else
            {
                if (responseHeaders[RTSP_HEADERS, RTSP_HEADERS_TRANSPORT] == null)
                {
                    FATAL("RTSP {0} request doesn't have {1} header line",
                    RTSP_METHOD_SETUP,
                    RTSP_HEADERS_TRANSPORT);
                    return false;
                }
                //3. get the transport header line
                var raw = responseHeaders[RTSP_HEADERS, RTSP_HEADERS_TRANSPORT];
                var transport = Variant.Get();
                if (!SDP.ParseTransportLine(raw, transport))
                {
                    FATAL("Unable to parse transport line {0}", (raw));
                    return false;
                }
                bool forceTcp;
                if (transport["server_port"] != null &&
                    (transport["rtp/avp/udp"] != null || transport["rtp/avp"] != null))
                {
                    forceTcp = false;
                }else if (transport["interleaved"] != null && transport["rtp/avp/tcp"] != null)
                {
                    forceTcp = true;
                }
                else
                {
                    FATAL("Invalid transport line: {0}", (transport.ToString()));
                    return false;
                }
                if (forceTcp != (bool)rtspProtocol.CustomParameters["forceTcp"])
                {
                    FATAL("Invalid transport line: {0}", (transport.ToString()));
                    return false;
                }
                var pConnectivity = GetOutboundConnectivity(rtspProtocol, forceTcp);
                if (pConnectivity == null)
                {
                    FATAL("Unable to get outbound connectivity");
                    return false;
                }
                var param = rtspProtocol.CustomParameters;
                param[param["lastSetup"] == "audio" ? "audioTransport" : "videoTransport"] = transport;
                var variantUri = param["uri"];
                string trackId = "";
                bool isAudio = false;
                if (param["audioTrackId"] != null)
                {
                    trackId = param["audioTrackId"];
                    param["audioTrackId"] = null;
                    param["lastSetup"] = "audio";
                    isAudio = true;
                    pConnectivity.HasAudio = true;
                }
                else
                {
                    if (param["videoTrackId"] != null)
                    {
                        trackId = param["videoTrackId"];
                        param["videoTrackId"] = null;
                        param["lastSetup"] = "video";
                        pConnectivity.HasVideo = true;
                    }
                }
                if (trackId != "")
                {
                    var uri = variantUri["fullUri"] + "/trackID=" + trackId;
                    rtspProtocol.PushRequestFirstLine(RTSP_METHOD_SETUP, uri, RTSP_VERSION_1_0);
             
                    transport = forceTcp ? $"RTP/AVP/TCP;unicast;interleaved={(isAudio ? pConnectivity.AudioChannels : pConnectivity.VideoChannels)};mode=record" : $"RTP/AVP;unicast;client_port={(isAudio ? pConnectivity.AudioChannels : pConnectivity.VideoChannels)};mode=record";
                    rtspProtocol.PushRequestHeader(RTSP_HEADERS_TRANSPORT, transport);
                    return rtspProtocol.SendRequestMessage();
                }
                else
                {
                    rtspProtocol.PushRequestFirstLine(RTSP_METHOD_RECORD,variantUri["fullUri"],RTSP_VERSION_1_0);
                    return rtspProtocol.SendRequestMessage();
                }

            }
        }
 public OutboundConnectivity(bool forceTcp, RtspProtocol pRTSPProtocol)
 {
     _forceTcp = forceTcp;
     _rtspProtocol = pRTSPProtocol;
     _dataMessage = new MsgHdr();
     _rtcpMessage = new MsgHdr {Buffers = new[] {new byte[28]}};
     _rtcpMessage.Buffers[0][0] = 0x80;
     _rtcpMessage.Buffers[0][1] = 0xc8;
     _rtcpMessage.Buffers[0].Write(2, (ushort) 6);
     _rtcpNTP = new BufferWithOffset(_rtcpMessage.Buffers[0], 8);
     _rtcpRTP = new BufferWithOffset(_rtcpMessage.Buffers[0], 16);
     _rtcpSPC = new BufferWithOffset(_rtcpMessage.Buffers[0], 20);
     _rtcpSOC = new BufferWithOffset(_rtcpMessage.Buffers[0], 24);
     _startupTime = DateTime.Now;
 }
        public override BaseProtocol SpawnProtocol(ulong type, Variant parameters)
        {
            BaseProtocol pResult = null;
            switch (type)
            {
                case ProtocolTypes.PT_TCP:
                    pResult = new TCPProtocol();
                    break;
                case ProtocolTypes.PT_UDP:
                    pResult = new UDPProtocol();
                    break;
                case ProtocolTypes.PT_INBOUND_SSL:
                    pResult = new InboundSSLProtocol();
                    break;
                case ProtocolTypes.PT_OUTBOUND_SSL:
                    pResult = new OutboundSSLProtocol();
                    break;
                case ProtocolTypes.PT_INBOUND_RTMP:
                    pResult = new InboundRTMPProtocol();
                    break;
                case ProtocolTypes.PT_INBOUND_RTMPS_DISC:
                    break;
                case ProtocolTypes.PT_OUTBOUND_RTMP:
                    pResult = new OutboundRTMPProtocol();
                    break;
                case ProtocolTypes.PT_INBOUND_RTMFP:
                    pResult = new InboundRTMFPProtocol();
                    break;
                case ProtocolTypes.PT_INBOUND_CLUSTER:
                    pResult = new InboundClusterProtocol();
                    break;
                case ProtocolTypes.PT_OUTBOUND_CLUSTER:
                    pResult = new OutboundClusterProtocol();
                    break;
                case ProtocolTypes.PT_RTSP:
                    pResult = new RtspProtocol();
                    break;
                case ProtocolTypes.PT_RTP_NAT_TRAVERSAL:
                    pResult = new NATTraversalProtocol();
                    break;
                case ProtocolTypes.PT_INBOUND_RTP:
                    pResult = new InboundRtpProtocol();
                    break;
                case ProtocolTypes.PT_RTCP:
                    pResult = new RtcpProtocol();
                    break;
                case ProtocolTypes.PT_INBOUND_WEBSOCKET:
                    pResult = new WebSocketProtocol();
                    break;
                case ProtocolTypes.PT_INBOUND_WEBRTC_SIGNAL:
                    pResult = new WebRtcSignalProtocol();
                    break;
                case ProtocolTypes.PT_INBOUND_MP4:
                    pResult = new Mp4Protocol();
                    break;
                default:
                    Logger.FATAL("Spawning protocol {0} not yet implemented",
                        type.TagToString());
                    break;
            }
            if (pResult != null)
            {
                if (!pResult.Initialize(parameters))
                {
                    Logger.FATAL("Unable to initialize protocol {0}",
                            type.TagToString());

                    pResult = null;
                }
            }
            return pResult;
        }
        private bool HandleRTSPResponse200Announce(RtspProtocol rtspProtocol, Variant requestHeaders, ref string requestContent, Variant responseHeaders, ref string responseContent)
        {
            bool forceTcp = rtspProtocol.CustomParameters["forceTcp"];

            //3. Get the outbound connectivity
            var pConnectivity = GetOutboundConnectivity(rtspProtocol,forceTcp);
            if (pConnectivity == null)
            {
                FATAL("Unable to get the outbound connectivity");
                return false;
            }
            var param = rtspProtocol.CustomParameters;
            string trackId = "";
            bool isAudio = false;
            if (param["audioTrackId"] != null)
            {
                trackId = param["audioTrackId"];
                param["audioTrackId"] = null;
                param["lastSetup"] = "audio";
                isAudio = true;
                pConnectivity.HasAudio = true;
            }
            else
            {
                if (param["videoTrackId"] != null)
                {
                    trackId = param["videoTrackId"];
                    param["videoTrackId"] = null;
                    param["lastSetup"] = "video";
                    pConnectivity.HasVideo = true;
                }
            }
            if (trackId != "")
            {
                var variantUri = param["uri"];
                var uri = variantUri["fullUri"] + "/trackID=" + trackId;
                rtspProtocol.PushRequestFirstLine(RTSP_METHOD_SETUP, uri, RTSP_VERSION_1_0);

                var transport = forceTcp ? $"RTP/AVP/TCP;unicast;interleaved={(isAudio ? pConnectivity.AudioChannels : pConnectivity.VideoChannels)};mode=record" : $"RTP/AVP;unicast;client_port={(isAudio ? pConnectivity.AudioChannels : pConnectivity.VideoChannels)};mode=record";
                rtspProtocol.PushRequestHeader(RTSP_HEADERS_TRANSPORT, transport);
                return rtspProtocol.SendRequestMessage();
            }
            else
            {
                FATAL("Bogus RTSP connection");
               rtspProtocol.EnqueueForDelete();
                return false;
            }
        }
        private bool SendAuthenticationChallenge(RtspProtocol from, Variant realm)
        {
            //10. Ok, the user doesn't know that this needs authentication. We
            //will respond back with a nice 401. Generate the line first
            string wwwAuthenticate = HTTPAuthHelper.GetWWWAuthenticateHeader(
                    realm["method"],
                    realm["name"]);

            //12. Save the nonce for later validation when new requests are coming in again
            from.CustomParameters["wwwAuthenticate"] = wwwAuthenticate;

            //13. send the response
            from.PushResponseFirstLine(RTSP_VERSION_1_0, 401, "Unauthorized");
            from.PushResponseHeader(HTTP_HEADERS_WWWAUTHENTICATE, wwwAuthenticate);
            return from.SendResponseMessage();
        }
        private bool SendSetupTrackMessages(RtspProtocol rtspProtocol)
        {
            //1. Get the pending tracks
            if (rtspProtocol.CustomParameters["pendingTracks"].ArrayLength == 0)
            {
                WARN("No more tracks");
                return true;
            }
            //2. Get the inbound connectivity
            if (rtspProtocol.InboundConnectivity == null)
            {
                FATAL("Unable to get inbound connectivity");
                return false;
            }
            //3. Get the first pending track
            var track = rtspProtocol.CustomParameters["pendingTracks"][0];
            if (!track.IsMap)
            {
                FATAL("Invalid track");
                return false;
            }
            //4. Add the track to the inbound connectivity
            if (!rtspProtocol.InboundConnectivity.AddTrack(track,track["isAudio"]))
            {
                FATAL("Unable to add the track to inbound connectivity");
                return false;
            }

            //6. Prepare the SETUP request
            rtspProtocol.PushRequestFirstLine(RTSP_METHOD_SETUP,
           track["controlUri"], RTSP_VERSION_1_0);
            rtspProtocol.PushRequestHeader(RTSP_HEADERS_TRANSPORT, rtspProtocol.InboundConnectivity.GetTransportHeaderLine(track["isAudio"], true));
            //7. Remove the track from pending
            rtspProtocol.CustomParameters["pendingTracks"].RemoveAt(0);
            //8. Send the request message
            return rtspProtocol.SendRequestMessage();
        }
 public void SignalDetachedFromInStream()
 {
     var protocol = ProtocolManager.GetProtocol(_rtpClient.protocolId);
     protocol?.EnqueueForDelete();
     _rtspProtocol = null;
 }
 protected virtual string GetAuthenticationRealm(RtspProtocol @from, Variant requestHeaders, string requestContent) => _realms.Count > 0 ? _realms.Children.First().Key : "";
 protected virtual bool NeedAuthentication(RtspProtocol @from, Variant requestHeaders, string requestContent) => requestHeaders[RTSP_FIRST_LINE, RTSP_METHOD] != RTSP_METHOD_OPTIONS;
 private bool HandleRTSPResponse200Record(RtspProtocol rtspProtocol, Variant requestHeaders, ref string requestContent, Variant responseHeaders, ref string responseContent)
 {
     bool forceTcp = rtspProtocol.CustomParameters["forceTcp"];
     var pConnectivity = GetOutboundConnectivity(rtspProtocol, forceTcp);
     if (pConnectivity == null)
     {
         FATAL("Unable to get outbound connectivity");
         return false;
     }
     bool result = false;
     var param = rtspProtocol.CustomParameters;
     if (param["audioTransport"] != null)
     {
         if (forceTcp)
         {
             if (
                 !pConnectivity.RegisterTCPAudioClient(rtspProtocol.Id,
                     param["audioTransport", "interleaved", "data"],
                     param["audioTransport", "interleaved", "rtcp"]))
             {
                 FATAL("Unable to register audio stream");
                 return false;
             }
         }
         else
         {
             
             var dataAddress = new IPEndPoint((rtspProtocol.IOHandler as TCPCarrier).FarInfo.Address, (param["audioTransport", "interleaved", "data"]));
             var rtcpAddress = new IPEndPoint((rtspProtocol.IOHandler as TCPCarrier).FarInfo.Address, (param["audioTransport", "interleaved", "rtcp"]));
             if (
                 !pConnectivity.RegisterUDPAudioClient(rtspProtocol.Id,dataAddress, rtcpAddress))   
             {
                 FATAL("Unable to register audio stream");
                 return false;
             }
         }
         result = true;
     }
     if (param["videoTransport"] != null)
     {
         if (forceTcp)
         {
             if (
                 !pConnectivity.RegisterTCPVideoClient(rtspProtocol.Id,
                     param["videoTransport", "interleaved", "data"],
                     param["videoTransport", "interleaved", "rtcp"]))
             {
                 FATAL("Unable to register video stream");
                 return false;
             }
         }
         else
         {
             var dataAddress = new IPEndPoint((rtspProtocol.IOHandler as TCPCarrier).FarInfo.Address,(param["videoTransport", "interleaved", "data"]));
             var rtcpAddress = new IPEndPoint((rtspProtocol.IOHandler as TCPCarrier).FarInfo.Address,(param["videoTransport", "interleaved", "rtcp"]));
             if (
                 !pConnectivity.RegisterUDPVideoClient(rtspProtocol.Id, dataAddress, rtcpAddress))
             {
                 FATAL("Unable to register video stream");
                 return false;
             }
         }
         result = true;
     }
     return result;
 }