/// <summary> /// Allows an arbitrary block of bytes to be sent on the RTP channel. This is mainly used for the Gingle /// client which needs to send a STUN binding request to the Google Voice gateway. /// </summary> public void SendRTPRaw(byte[] buffer, int length) { if (_rtpAudioChannel != null) { _rtpAudioChannel.SendRTPRaw(buffer); } }
/// <summary> /// Event handler for receiving an audio sample that is ready for encoding, packaging into RTP and sending to the remote end /// of the VoIP call. /// </summary> private void RTPChannelSampleAvailable(object sender, WaveInEventArgs e) { TimeSpan samplePeriod = DateTime.Now.Subtract(_lastInputSampleReceivedAt); _lastInputSampleReceivedAt = DateTime.Now; _inputSampleCount++; _audioLogger.Debug(_inputSampleCount + " sample period " + samplePeriod.TotalMilliseconds + "ms, sample bytes " + e.BytesRecorded + "."); byte[] sample = new byte[e.Buffer.Length / 2]; int sampleIndex = 0; for (int index = 0; index < e.Buffer.Length; index += 2) { var ulawByte = MuLawEncoder.LinearToMuLawSample(BitConverter.ToInt16(e.Buffer, index)); sample[sampleIndex++] = ulawByte; } m_rtpChannel.SendRTPRaw(sample); }
private void Uac_CallAnswered(ISIPClientUserAgent uac, SIPResponse sipResponse) { logger.Debug($"{ prefix } Call answered; { sipResponse.StatusCode } { sipResponse.Status }"); switch (sipResponse.StatusCode) { case 404: logger.Error($"{ prefix } Received 404 Not Found from remote endpoint"); break; case 486: // Busy logger.Error($"{ prefix } Received 486 Remote endpoint is busy; try again later"); break; case 488: // Possible audio format issue logger.Error($"{ prefix } Received 488 Not Acceptable from remote endpoint; check audio format"); break; case 503: // Check Twilio service and geo-permissions logger.Error($"{ prefix } Received 503 Service Unavailable from remote endpoint; check service permissions"); break; case 200: if (sipResponse.Header.ContentType != SDP.SDP_MIME_CONTENTTYPE) { logger.Error($"{ prefix } Received incorrect content type"); Stop(); return; } if (sipResponse.Body.IsNullOrBlank()) { logger.Error($"{ prefix } Received an empty SDP payload"); Stop(); return; } var sdp = SDP.ParseSDPDescription(sipResponse.Body); var ip = IPAddress.Parse(sdp.Connection.ConnectionAddress); var announcement = sdp.Media.Where(x => x.Media == SDPMediaTypesEnum.audio).FirstOrDefault(); if (announcement != null) { if (announcement.Port != 0) { rtpChannel.OnControlDataReceived += (b) => { logger.Debug($"{prefix} Control Data Received; {b.Length} bytes"); }; rtpChannel.OnControlSocketDisconnected += () => { logger.Debug($"{prefix} Control Socket Disconnected"); }; rtpChannel.RemoteEndPoint = new IPEndPoint(ip, announcement.Port); rtpChannel.Start(); // Send some setup parameters to punch a hole in the firewall/router rtpChannel.SendRTPRaw(new byte[] { 80, 95, 198, 88, 55, 96, 225, 141, 215, 205, 185, 242, 00 }); } else { logger.Error($"{ prefix } Remote endpoint did not specify a port number"); return; } } else { logger.Error($"{ prefix } Remote endpoint has not valid audio announcement"); return; } break; } }
private void Transport_SIPTransportRequestReceived(SIPEndPoint localSIPEndPoint, SIPEndPoint remoteEndPoint, SIPRequest sipRequest) { var endpoint = new SIPEndPoint(SIPProtocolsEnum.udp, publicIPAddress, localSIPEndPoint.Port); if (sipRequest.Method == SIPMethodsEnum.INVITE) { if (transaction != null) { return; } logger.DebugFormat("{0} Incoming call from {1}", prefix, sipRequest.Header.From.FromURI.User); transaction = transport.CreateUASTransaction(sipRequest, remoteEndPoint, endpoint, null); agent = new SIPServerUserAgent( transport, null, sipRequest.Header.From.FromURI.User, null, SIPCallDirection.In, null, null, null, transaction); agent.CallCancelled += Agent_CallCancelled; agent.TransactionComplete += Agent_TransactionComplete; agent.Progress(SIPResponseStatusCodesEnum.Trying, null, null, null, null); agent.Progress(SIPResponseStatusCodesEnum.Ringing, null, null, null, null); var answer = SDP.ParseSDPDescription(agent.CallRequest.Body); var address = IPAddress.Parse(answer.Connection.ConnectionAddress); var port = answer.Media.FirstOrDefault(m => m.Media == SDPMediaTypesEnum.audio).Port; var random = Crypto.GetRandomInt(5).ToString(); var sdp = new SDP { Version = 2, Username = "******", SessionId = random, Address = localIPEndPoint.Address.ToString(), SessionName = "redfox_" + random, Timing = "0 0", Connection = new SDPConnectionInformation(publicIPAddress.ToString()) }; rtpChannel = new RTPChannel { DontTimeout = true, RemoteEndPoint = new IPEndPoint(address, port) }; rtpChannel.SetFrameType(FrameTypesEnum.Audio); // TODO Fix hardcoded ports rtpChannel.ReservePorts(15000, 15090); rtpChannel.OnFrameReady += Channel_OnFrameReady; rtpChannel.Start(); // Send some setup parameters to punch a hole in the firewall/router rtpChannel.SendRTPRaw(new byte[] { 80, 95, 198, 88, 55, 96, 225, 141, 215, 205, 185, 242, 00 }); rtpChannel.OnControlDataReceived += (b) => { logger.Debug($"{prefix} Control Data Received; {b.Length} bytes"); }; rtpChannel.OnControlSocketDisconnected += () => { logger.Debug($"{prefix} Control Socket Disconnected"); }; var announcement = new SDPMediaAnnouncement { Media = SDPMediaTypesEnum.audio, MediaFormats = new List <SDPMediaFormat>() { new SDPMediaFormat((int)SDPMediaFormatsEnum.PCMU, "PCMU", 8000) }, Port = rtpChannel.RTPPort }; sdp.Media.Add(announcement); SetState(State.Listening, sipRequest.Header.From.FromURI.User); agent.Progress(SIPResponseStatusCodesEnum.Accepted, null, null, null, null); agent.Answer(SDP.SDP_MIME_CONTENTTYPE, sdp.ToString(), null, SIPDialogueTransferModesEnum.NotAllowed); SetState(State.Busy, ""); return; } if (sipRequest.Method == SIPMethodsEnum.BYE) { if (State != State.Busy) { return; } logger.DebugFormat("{0} Hangup from {1}", prefix, sipRequest.Header.From.FromURI.User); var noninvite = transport.CreateNonInviteTransaction(sipRequest, remoteEndPoint, endpoint, null); var response = SIPTransport.GetResponse(sipRequest, SIPResponseStatusCodesEnum.Ok, null); noninvite.SendFinalResponse(response); SetState(State.Finished, Endpoint); rtpChannel.OnFrameReady -= Channel_OnFrameReady; rtpChannel.Close(); agent.TransactionComplete -= Agent_TransactionComplete; agent.CallCancelled -= Agent_CallCancelled; agent = null; transaction = null; SetState(State.Ready, Endpoint); return; } if (sipRequest.Method == SIPMethodsEnum.ACK) { } if (sipRequest.Method == SIPMethodsEnum.CANCEL) { } }