/// <summary> /// Event handler for receiving data on the RTP channel. /// </summary> /// <param name="remoteEndPoint">The remote end point the data was received from.</param> /// <param name="buffer">The data received.</param> private void RtpPacketReceived(IPEndPoint remoteEndPoint, byte[] buffer) { if (m_lastReceiveFromEndPoint == null || !m_lastReceiveFromEndPoint.Equals(remoteEndPoint)) { OnReceiveFromEndPointChanged?.Invoke(m_lastReceiveFromEndPoint, remoteEndPoint); m_lastReceiveFromEndPoint = remoteEndPoint; } // Quick sanity check on whether this is not an RTP or RTCP packet. if (buffer?.Length > RTPHeader.MIN_HEADER_LEN && buffer[0] >= 128 && buffer[0] <= 191) { var rtpPacket = new RTPPacket(buffer); OnRtpPacketReceived?.Invoke(rtpPacket); if (RemoteRtpEventPayloadID != 0 && rtpPacket.Header.PayloadType == RemoteRtpEventPayloadID) { RTPEvent rtpEvent = new RTPEvent(rtpPacket.Payload); OnRtpEvent?.Invoke(rtpEvent); } else { OnReceivedSampleReady?.Invoke(rtpPacket.Payload); } } }
/// <summary> /// Event handler for receiving data on the RTP channel. /// </summary> /// <param name="remoteEndPoint">The remote end point the data was received from.</param> /// <param name="buffer">The data received.</param> private void RtpPacketReceived(IPEndPoint remoteEndPoint, byte[] buffer) { if (m_lastReceiveFromEndPoint == null || !m_lastReceiveFromEndPoint.Equals(remoteEndPoint)) { OnReceiveFromEndPointChanged?.Invoke(m_lastReceiveFromEndPoint, remoteEndPoint); m_lastReceiveFromEndPoint = remoteEndPoint; } var rtpPacket = new RTPPacket(buffer); OnRtpPacketReceived?.Invoke(rtpPacket); if (m_remoteRtpEventPayloadID != 0 && rtpPacket.Header.PayloadType == m_remoteRtpEventPayloadID) { RTPEvent rtpEvent = new RTPEvent(rtpPacket.Payload); OnRtpEvent?.Invoke(rtpEvent); } else { OnReceivedSampleReady?.Invoke(rtpPacket.Payload); } }
/// <summary> /// Sends an RTP event for a DTMF tone as per RFC2833. Sending the event requires multiple packets to be sent. /// This method will hold onto the socket until all the packets required for the event have been sent. The send /// can be cancelled using the cancellation token. /// </summary> /// <param name="srcRtpSocket">The local RTP socket to send the event from.</param> /// <param name="dstRtpSocket">The remote RTP socket to send the event to.</param> /// <param name="rtpEvent">The RTP event to send.</param> /// <param name="startTimestamp">The RTP timestamp at the start of the event.</param> /// <param name="samplePeriod">The sample period in milliseconds being used for the media stream that the event /// is being inserted into. Should be set to 50ms if main media stream is dynamic or sample period is unknown.</param> /// <param name="timestampStep">The RTP timestamp step corresponding to the sampling period. This can change depending /// on the codec being used. For example using PCMU with a sampling frequency of 8000Hz the timestamp step /// for a sample period of 50ms is 400 (8000 / (1000 / 50)). For a sample period of 20ms it's 160 (8000 / (1000 / 20)).</param> /// <param name="cts">Token source to allow the operation to be cancelled prematurely.</param> public async Task SendDtmfEvent(Socket srcRtpSocket, IPEndPoint dstRtpSocket, RTPEvent rtpEvent, uint startTimestamp, ushort samplePeriod, ushort timestampStep, CancellationTokenSource cts) { try { // If only the minimum number of packets are being sent then they are both the start and end of the event. rtpEvent.EndOfEvent = (rtpEvent.TotalDuration <= timestampStep); rtpEvent.Duration = timestampStep; // Send the start of event packets. for (int i = 0; i < RTPEvent.DUPLICATE_COUNT && !cts.IsCancellationRequested; i++) { byte[] buffer = rtpEvent.GetEventPayload(); int markerBit = (i == 0) ? 1 : 0; // Set marker bit for the first packet in the event. SendRtpPacket(srcRtpSocket, dstRtpSocket, buffer, startTimestamp, markerBit, rtpEvent.PayloadTypeID); SeqNum++; PacketsSent++; } await Task.Delay(samplePeriod, cts.Token); if (!rtpEvent.EndOfEvent) { // Send the progressive event packets while ((rtpEvent.Duration + timestampStep) < rtpEvent.TotalDuration && !cts.IsCancellationRequested) { rtpEvent.Duration += timestampStep; byte[] buffer = rtpEvent.GetEventPayload(); SendRtpPacket(srcRtpSocket, dstRtpSocket, buffer, startTimestamp, 0, rtpEvent.PayloadTypeID); PacketsSent++; SeqNum++; await Task.Delay(samplePeriod, cts.Token); } // Send the end of event packets. for (int j = 0; j < RTPEvent.DUPLICATE_COUNT && !cts.IsCancellationRequested; j++) { rtpEvent.EndOfEvent = true; rtpEvent.Duration = rtpEvent.TotalDuration; byte[] buffer = rtpEvent.GetEventPayload(); SendRtpPacket(srcRtpSocket, dstRtpSocket, buffer, startTimestamp, 0, rtpEvent.PayloadTypeID); SeqNum++; PacketsSent++; } } } catch (System.Net.Sockets.SocketException sockExcp) { logger.LogError("SocketException SendDtmfEvent. " + sockExcp.Message); } catch (System.Threading.Tasks.TaskCanceledException) { logger.LogWarning("SendDtmfEvent was cancelled by caller."); } }
/// <summary> /// Sends an RTP event for a DTMF tone as per RFC2833. Sending the event requires multiple packets to be sent. /// This method will hold onto the socket until all the packets required for the event have been sent. The send /// can be cancelled using the cancellation token. /// </summary> /// <param name="rtpEvent">The RTP event to send.</param> /// <param name="cancellationToken">CancellationToken to allow the operation to be cancelled prematurely.</param> public async Task SendDtmfEvent( RTPEvent rtpEvent, CancellationToken cancellationToken) { if (m_rtpEventInProgress == true || DestinationEndPoint == null) { logger.LogWarning("SendDtmfEvent request ignored as an RTP event is already in progress."); } try { m_rtpEventInProgress = true; uint startTimestamp = m_lastRtpTimestamp; // The sample period in milliseconds being used for the media stream that the event // is being inserted into. Should be set to 50ms if main media stream is dynamic or // sample period is unknown. int samplePeriod = RTP_EVENT_DEFAULT_SAMPLE_PERIOD_MS; int clockRate = MediaFormat.ClockRate; // If the clock rate is unknown or dynamic cross our fingers and use 8KHz. if (clockRate == 0) { clockRate = DEFAULT_AUDIO_CLOCK_RATE; } // The RTP timestamp step corresponding to the sampling period. This can change depending // on the codec being used. For example using PCMU with a sampling frequency of 8000Hz and a sample period of 50ms // the timestamp step is 400 (8000 / (1000 / 50)). For a sample period of 20ms it's 160 (8000 / (1000 / 20)). ushort rtpTimestampStep = (ushort)(clockRate * samplePeriod / 1000); // If only the minimum number of packets are being sent then they are both the start and end of the event. rtpEvent.EndOfEvent = (rtpEvent.TotalDuration <= rtpTimestampStep); // The DTMF tone is generally multiple RTP events. Each event has a duration of the RTP timestamp step. rtpEvent.Duration = rtpTimestampStep; // Send the start of event packets. for (int i = 0; i < RTPEvent.DUPLICATE_COUNT && !cancellationToken.IsCancellationRequested; i++) { byte[] buffer = rtpEvent.GetEventPayload(); int markerBit = (i == 0) ? 1 : 0; // Set marker bit for the first packet in the event. SendRtpPacket(RtpChannel, DestinationEndPoint, buffer, startTimestamp, markerBit, rtpEvent.PayloadTypeID); SeqNum++; } await Task.Delay(samplePeriod, cancellationToken); if (!rtpEvent.EndOfEvent) { // Send the progressive event packets while ((rtpEvent.Duration + rtpTimestampStep) < rtpEvent.TotalDuration && !cancellationToken.IsCancellationRequested) { rtpEvent.Duration += rtpTimestampStep; byte[] buffer = rtpEvent.GetEventPayload(); SendRtpPacket(RtpChannel, DestinationEndPoint, buffer, startTimestamp, 0, rtpEvent.PayloadTypeID); SeqNum++; await Task.Delay(samplePeriod, cancellationToken); } // Send the end of event packets. for (int j = 0; j < RTPEvent.DUPLICATE_COUNT && !cancellationToken.IsCancellationRequested; j++) { rtpEvent.EndOfEvent = true; rtpEvent.Duration = rtpEvent.TotalDuration; byte[] buffer = rtpEvent.GetEventPayload(); SendRtpPacket(RtpChannel, DestinationEndPoint, buffer, startTimestamp, 0, rtpEvent.PayloadTypeID); SeqNum++; } } } catch (SocketException sockExcp) { logger.LogError("SocketException SendDtmfEvent. " + sockExcp.Message); } catch (TaskCanceledException) { logger.LogWarning("SendDtmfEvent was cancelled by caller."); } finally { m_rtpEventInProgress = false; } }
/// <summary> /// Event handler for receiving data on the RTP and Control channels. For multiplexed /// sessions both RTP and RTCP packets will be received on the RTP channel. /// </summary> /// <param name="remoteEndPoint">The remote end point the data was received from.</param> /// <param name="buffer">The data received.</param> private void OnReceive(IPEndPoint remoteEndPoint, byte[] buffer) { //if (m_lastReceiveFromEndPoint == null || !m_lastReceiveFromEndPoint.Equals(remoteEndPoint)) //{ // OnReceiveFromEndPointChanged?.Invoke(m_lastReceiveFromEndPoint, remoteEndPoint); // m_lastReceiveFromEndPoint = remoteEndPoint; //} // Quick sanity check on whether this is not an RTP or RTCP packet. if (buffer?.Length > RTPHeader.MIN_HEADER_LEN && buffer[0] >= 128 && buffer[0] <= 191) { if (buffer[1] == 0xC8 /* RTCP SR */ || buffer[1] == 0xC9 /* RTCP RR */) { //logger.LogDebug($"RTCP packet received from {remoteEndPoint} before: {buffer.HexStr()}"); #region RTCP packet. if (m_srtcpControlUnprotect != null) { int outBufLen = 0; int res = m_srtcpControlUnprotect(buffer, buffer.Length, out outBufLen); if (res != 0) { logger.LogWarning($"SRTCP unprotect failed, result {res}."); return; } else { buffer = buffer.Take(outBufLen).ToArray(); } } var rtcpPkt = new RTCPCompoundPacket(buffer); if (rtcpPkt != null) { if (rtcpPkt.Bye != null) { // TODO: Close stream and if only stream close session. logger.LogDebug($"RTCP BYE received for SSRC ${rtcpPkt.Bye.SSRC}."); } else if (!m_isClosed) { var rtcpSession = GetRtcpSession(rtcpPkt); if (rtcpSession != null) { rtcpSession.ReportReceived(remoteEndPoint, rtcpPkt); OnReceiveReport?.Invoke(rtcpSession.MediaType, rtcpPkt); } else { logger.LogWarning("Could not match an RTCP packet against any SSRC's in the session."); } } } else { logger.LogWarning("Failed to parse RTCP compound report."); } #endregion } else { #region RTP packet. if (!m_isClosed) { if (m_srtpUnprotect != null) { int outBufLen = 0; int res = m_srtpUnprotect(buffer, buffer.Length, out outBufLen); if (res != 0) { logger.LogWarning($"SRTP unprotect failed, result {res}."); return; } else { buffer = buffer.Take(outBufLen).ToArray(); } } var rtpPacket = new RTPPacket(buffer); SDPMediaTypesEnum?rtpMediaType = GetMediaTypeForRtpPacket(rtpPacket.Header); if (RemoteRtpEventPayloadID != 0 && rtpPacket.Header.PayloadType == RemoteRtpEventPayloadID) { RTPEvent rtpEvent = new RTPEvent(rtpPacket.Payload); OnRtpEvent?.Invoke(rtpEvent); } else { OnRtpPacketReceived?.Invoke(rtpPacket); } // Used for reporting purposes. if (rtpMediaType == SDPMediaTypesEnum.audio) { m_audioRtcpSession.RecordRtpPacketReceived(rtpPacket); } else if (rtpMediaType == SDPMediaTypesEnum.video) { m_videoRtcpSession.RecordRtpPacketReceived(rtpPacket); } } #endregion } } }