public void Send(byte[] buffer) { try { Peer.LastTimestamp = (Peer.LastTimestamp == 0) ? RTSPSession.DateTimeToNptTimestamp32(DateTime.Now) : Peer.LastTimestamp + TIMESTAMP_SPACING; for (int index = 0; index * RTP_MAX_PAYLOAD < buffer.Length; index++) { int offset = (index == 0) ? 0 : (index * RTP_MAX_PAYLOAD); int payloadLength = (offset + RTP_MAX_PAYLOAD < buffer.Length) ? RTP_MAX_PAYLOAD : buffer.Length - offset; byte[] vp8HeaderBytes = (index == 0) ? new byte[] { 0x10 } : new byte[] { 0x00 }; RTPPacket rtpPacket = new RTPPacket(payloadLength + SRTP_AUTH_KEY_LENGTH + vp8HeaderBytes.Length); rtpPacket.Header.SyncSource = Peer.SSRC; rtpPacket.Header.SequenceNumber = Peer.SequenceNumber++; rtpPacket.Header.Timestamp = Peer.LastTimestamp; rtpPacket.Header.MarkerBit = ((offset + payloadLength) >= buffer.Length) ? 1 : 0; // Set marker bit for the last packet in the frame. rtpPacket.Header.PayloadType = PAYLOAD_TYPE_ID; Buffer.BlockCopy(vp8HeaderBytes, 0, rtpPacket.Payload, 0, vp8HeaderBytes.Length); Buffer.BlockCopy(buffer, offset, rtpPacket.Payload, vp8HeaderBytes.Length, payloadLength); var rtpBuffer = rtpPacket.GetBytes(); int rtperr = SrtpContext.ProtectRTP(rtpBuffer, rtpBuffer.Length - SRTP_AUTH_KEY_LENGTH); if (rtperr != 0) { logger.Warn("SRTP packet protection failed, result " + rtperr + "."); } else { var connectedIceCandidate = Peer.LocalIceCandidates.Where(y => y.RemoteRtpEndPoint != null).First(); connectedIceCandidate.LocalRtpSocket.SendTo(rtpBuffer, connectedIceCandidate.RemoteRtpEndPoint); } } } catch (Exception sendExcp) { // logger.Error("SendRTP exception sending to " + client.SocketAddress + ". " + sendExcp.Message); } }
public void Send(byte[] buffer, uint samplePeriod) { if (m_remoteEndPoint == null) { logger.Warn("RTP packet could not be sent as remote end point has not yet been set."); } else { m_sendRTPHeader.SequenceNumber++; m_sendRTPHeader.Timestamp += samplePeriod; RTPPacket rtpPacket = new RTPPacket() { Header = m_sendRTPHeader, Payload = buffer }; logger.Debug("Sending RTP packet to " + m_remoteEndPoint + ", seq# " + rtpPacket.Header.SequenceNumber + ", timestamp " + rtpPacket.Header.Timestamp + "."); byte[] rtpOut = rtpPacket.GetBytes(); m_rtpListener.Send(m_remoteEndPoint, rtpOut); } }
private void ProcessRTPPackets() { try { Thread.CurrentThread.Name = "rtspclient-rtp"; _lastRTPReceivedAt = DateTime.Now; _lastBWCalcAt = DateTime.Now; while (!_isClosed) { while (_rtspSession.HasRTPPacket()) { RTPPacket rtpPacket = _rtspSession.GetNextRTPPacket(); if (rtpPacket != null) { _lastRTPReceivedAt = DateTime.Now; _bytesSinceLastBWCalc += RTPHeader.MIN_HEADER_LEN + rtpPacket.Payload.Length; if (_rtpTrackingAction != null) { double bwCalcSeconds = DateTime.Now.Subtract(_lastBWCalcAt).TotalSeconds; if (bwCalcSeconds > BANDWIDTH_CALCULATION_SECONDS) { _lastBWCalc = _bytesSinceLastBWCalc * 8 / bwCalcSeconds; _lastFrameRate = _framesSinceLastCalc / bwCalcSeconds; _bytesSinceLastBWCalc = 0; _framesSinceLastCalc = 0; _lastBWCalcAt = DateTime.Now; } var abbrevURL = (_url.Length <= 50) ? _url : _url.Substring(0, 50); string rtpTrackingText = String.Format("Url: {0}\r\nRcvd At: {1}\r\nSeq Num: {2}\r\nTS: {3}\r\nPayoad: {4}\r\nFrame Size: {5}\r\nBW: {6}\r\nFrame Rate: {7}", abbrevURL, DateTime.Now.ToString("HH:mm:ss:fff"), rtpPacket.Header.SequenceNumber, rtpPacket.Header.Timestamp, ((SDPMediaFormatsEnum)rtpPacket.Header.PayloadType).ToString(), _lastFrameSize + " bytes", _lastBWCalc.ToString("0.#") + "bps", _lastFrameRate.ToString("0.##") + "fps"); _rtpTrackingAction(rtpTrackingText); } if (rtpPacket.Header.Timestamp < _lastCompleteFrameTimestamp) { System.Diagnostics.Debug.WriteLine("Ignoring RTP packet with timestamp " + rtpPacket.Header.Timestamp + " as it's earlier than the last complete frame."); } else { while (_frames.Count > MAX_FRAMES_QUEUE_LENGTH) { var oldestFrame = _frames.OrderBy(x => x.Timestamp).First(); _frames.Remove(oldestFrame); System.Diagnostics.Debug.WriteLine("Receive queue full, dropping oldest frame with timestamp " + oldestFrame.Timestamp + "."); } var frame = _frames.Where(x => x.Timestamp == rtpPacket.Header.Timestamp).SingleOrDefault(); if (frame == null) { frame = new RTPFrame() { Timestamp = rtpPacket.Header.Timestamp, HasMarker = rtpPacket.Header.MarkerBit == 1 }; frame.AddRTPPacket(rtpPacket); _frames.Add(frame); } else { frame.HasMarker = (rtpPacket.Header.MarkerBit == 1); frame.AddRTPPacket(rtpPacket); } if (frame.IsComplete()) { // The frame is ready for handing over to the UI. byte[] imageBytes = frame.GetFramePayload(); _lastFrameSize = imageBytes.Length; _framesSinceLastCalc++; _lastCompleteFrameTimestamp = rtpPacket.Header.Timestamp; //System.Diagnostics.Debug.WriteLine("Frame ready " + frame.Timestamp + ", sequence numbers " + frame.StartSequenceNumber + " to " + frame.EndSequenceNumber + ", payload length " + imageBytes.Length + "."); //logger.LogDebug("Frame ready " + frame.Timestamp + ", sequence numbers " + frame.StartSequenceNumber + " to " + frame.EndSequenceNumber + ", payload length " + imageBytes.Length + "."); _frames.Remove(frame); // Also remove any earlier frames as we don't care about anything that's earlier than the current complete frame. foreach (var oldFrame in _frames.Where(x => x.Timestamp <= rtpPacket.Header.Timestamp).ToList()) { System.Diagnostics.Debug.WriteLine("Discarding old frame for timestamp " + oldFrame.Timestamp + "."); logger.LogWarning("Discarding old frame for timestamp " + oldFrame.Timestamp + "."); _frames.Remove(oldFrame); } if (OnFrameReady != null) { try { //if (frame.FramePackets.Count == 1) //{ // // REMOVE. // logger.LogWarning("Discarding frame as there should have been more than 1 RTP packets."); //} //else //{ //System.Diagnostics.Debug.WriteLine("RTP frame ready for timestamp " + frame.Timestamp + "."); OnFrameReady(this, frame); //} } catch (Exception frameReadyExcp) { logger.LogError("Exception RTSPClient.ProcessRTPPackets OnFrameReady. " + frameReadyExcp); } } } } } } if (DateTime.Now.Subtract(_lastRTPReceivedAt).TotalSeconds > RTP_TIMEOUT_SECONDS) { logger.LogWarning("No RTP packets were received on RTSP session " + _rtspSession.SessionID + " for " + RTP_TIMEOUT_SECONDS + ". The session will now be closed."); Close(); } else { Thread.Sleep(1); } } } catch (Exception excp) { logger.LogError("Exception RTSPClient.ProcessRTPPackets. " + excp); } }
/// <summary> /// Sends a dynamically sized frame. The RTP marker bit will be set for the last transmitted packet in the frame. /// </summary> /// <param name="frame">The frame to transmit.</param> /// <param name="payloadType">The payload type to set on the RTP packet.</param> public void SendVP8Frame(byte[] frame, int payloadType) { try { if (_closed) { logger.Warn("SendVP8Frame cannot be called on a closed session."); } else if (_rtpSocketError != SocketError.Success) { logger.Warn("SendVP8Frame was called for an RTP socket in an error state of " + _rtpSocketError + "."); } else if (_remoteEndPoint == null) { logger.Warn("SendVP8Frame frame not sent as remote end point is not yet set."); } else { RecalculateTimestampStep(); _timestamp += _timestampStep; //System.Diagnostics.Debug.WriteLine("Sending " + frame.Length + " encoded bytes to client, timestamp " + _timestamp + ", starting sequence number " + _sequenceNumber + "."); for (int index = 0; index * RTP_MAX_PAYLOAD < frame.Length; index++) { byte[] vp8HeaderBytes = (index == 0) ? new byte[VP8_RTP_HEADER_LENGTH] { 0x90, 0x80, (byte)(_sequenceNumber % 128) } : new byte[VP8_RTP_HEADER_LENGTH] { 0x80, 0x80, (byte)(_sequenceNumber % 128) }; int offset = index * RTP_MAX_PAYLOAD; int payloadLength = ((index + 1) * RTP_MAX_PAYLOAD < frame.Length) ? RTP_MAX_PAYLOAD : frame.Length - index * RTP_MAX_PAYLOAD; //RTPPacket rtpPacket = new RTPPacket(payloadLength + VP8_RTP_HEADER_LENGTH + ((_srtp != null) ? SRTP_SIGNATURE_LENGTH : 0)); RTPPacket rtpPacket = new RTPPacket(payloadLength + VP8_RTP_HEADER_LENGTH); rtpPacket.Header.SyncSource = _syncSource; rtpPacket.Header.SequenceNumber = _sequenceNumber++; rtpPacket.Header.Timestamp = _timestamp; rtpPacket.Header.MarkerBit = (offset + payloadLength >= frame.Length) ? 1 : 0; rtpPacket.Header.PayloadType = payloadType; Buffer.BlockCopy(vp8HeaderBytes, 0, rtpPacket.Payload, 0, vp8HeaderBytes.Length); Buffer.BlockCopy(frame, offset, rtpPacket.Payload, vp8HeaderBytes.Length, payloadLength); byte[] rtpBytes = rtpPacket.GetBytes(); if (RtpProtect != null) { rtpBytes = RtpProtect(rtpBytes); } //System.Diagnostics.Debug.WriteLine(" offset " + (index * RTP_MAX_PAYLOAD) + ", payload length " + payloadLength + ", sequence number " + rtpPacket.Header.SequenceNumber + ", marker " + rtpPacket.Header .MarkerBit + "."); //Stopwatch sw = new Stopwatch(); //sw.Start(); // _rtpSocket.SendTo(rtpBytes, rtpBytes.Length, SocketFlags.None, _remoteEndPoint); //SocketAsyncEventArgs socketSendArgs = new SocketAsyncEventArgs(); //socketSendArgs.SetBuffer(rtpBytes, 0, rtpBytes.Length); //socketSendArgs.RemoteEndPoint = _remoteEndPoint; //_rtpSocket.SendToAsync(socketSendArgs); _rtpSocket.BeginSendTo(rtpBytes, 0, rtpBytes.Length, SocketFlags.None, _remoteEndPoint, null, null); //sw.Stop(); //if (sw.ElapsedMilliseconds > 15) //{ // logger.Warn(" SendVP8Frame offset " + offset + ", payload length " + payloadLength + ", sequence number " + rtpPacket.Header.SequenceNumber + ", marker " + rtpPacket.Header.MarkerBit + ", took " + sw.ElapsedMilliseconds + "ms."); //} } } } catch (Exception excp) { if (!_closed) { logger.Warn("Exception RTSPSession.SendVP8Frame attempting to send to the RTP socket at " + _remoteEndPoint + ". " + excp); if (OnRTPSocketDisconnected != null) { OnRTPSocketDisconnected(_sessionID); } } } }
private void Listen() { try { UdpClient udpSvr = m_udpListener; if (udpSvr == null) { logger.Error("The UDP server was not correctly initialised in the RTP sink when attempting to start the listener, the RTP stream has not been intialised."); return; } else { logger.Debug("RTP Listener now listening on " + m_localEndPoint.Address + ":" + m_localEndPoint.Port + "."); } IPEndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0); byte[] rcvdBytes = null; m_startRTPReceiveTime = DateTime.MinValue; m_lastRTPReceivedTime = DateTime.MinValue; DateTime previousRTPReceiveTime = DateTime.MinValue; uint previousTimestamp = 0; UInt16 sequenceNumber = 0; UInt16 previousSeqNum = 0; uint senderSendSpacing = 0; uint lastSenderSendSpacing = 0; while (!StopListening) { rcvdBytes = null; try { rcvdBytes = udpSvr.Receive(ref remoteEndPoint); } catch { //logger.Warn("Remote socket closed on receive. Last RTP received " + m_lastRTPReceivedTime.ToString("dd MMM yyyy HH:mm:ss") + ", last RTP successfull send " + m_lastRTPSentTime.ToString("dd MMM yyyy HH:mm:ss") + "."); } if (rcvdBytes != null && rcvdBytes.Length > 0) { // Check whether this is an RTCP report. UInt16 firstWord = BitConverter.ToUInt16(rcvdBytes, 0); if (BitConverter.IsLittleEndian) { firstWord = NetConvert.DoReverseEndian(firstWord); } ushort packetType = 0; if (BitConverter.IsLittleEndian) { packetType = Convert.ToUInt16(firstWord & 0x00ff); } if (packetType == RTCPHeader.RTCP_PACKET_TYPE) { logger.Debug("RTP Listener received remote RTCP report from " + remoteEndPoint + "."); try { RTCPPacket rtcpPacket = new RTCPPacket(rcvdBytes); RTCPReportPacket rtcpReportPacket = new RTCPReportPacket(rtcpPacket.Reports); if (RTCPReportReceived != null) { RTCPReportReceived(this, rtcpReportPacket); } } catch (Exception rtcpExcp) { logger.Error("Exception processing remote RTCP report. " + rtcpExcp.Message); } continue; } // Channel statistics. DateTime rtpReceiveTime = DateTime.Now; if (m_startRTPReceiveTime == DateTime.MinValue) { m_startRTPReceiveTime = rtpReceiveTime; //m_sampleStartTime = rtpReceiveTime; } previousRTPReceiveTime = new DateTime(m_lastRTPReceivedTime.Ticks); m_lastRTPReceivedTime = rtpReceiveTime; m_packetsReceived++; m_bytesReceived += rcvdBytes.Length; previousSeqNum = sequenceNumber; // This stops the thread running the ListenerTimeout method from deciding the strema has recieved no RTP and therefore should be shutdown. m_lastPacketReceived.Set(); // Let whoever has subscribed that an RTP packet has been received. if (DataReceived != null) { try { DataReceived(m_streamId, rcvdBytes, remoteEndPoint); } catch (Exception excp) { logger.Error("Exception RTPSink DataReceived. " + excp.Message); } } if (m_packetsReceived % 500 == 0) { logger.Debug("Total packets received from " + remoteEndPoint.ToString() + " " + m_packetsReceived + ", bytes " + NumberFormatter.ToSIByteFormat(m_bytesReceived, 2) + "."); } try { RTPPacket rtpPacket = new RTPPacket(rcvdBytes); uint syncSource = rtpPacket.Header.SyncSource; uint timestamp = rtpPacket.Header.Timestamp; sequenceNumber = rtpPacket.Header.SequenceNumber; //logger.Debug("seqno=" + rtpPacket.Header.SequenceNumber + ", timestamp=" + timestamp); if (previousRTPReceiveTime != DateTime.MinValue) { //uint senderSendSpacing = rtpPacket.Header.Timestamp - previousTimestamp; // Need to cope with cases where the timestamp has looped, if this timestamp is < last timesatmp and there is a large difference in them then it's because the timestamp counter has looped. lastSenderSendSpacing = senderSendSpacing; senderSendSpacing = (Math.Abs(timestamp - previousTimestamp) > (uint.MaxValue / 2)) ? timestamp + uint.MaxValue - previousTimestamp : timestamp - previousTimestamp; if (previousTimestamp > timestamp) { logger.Error("BUG: Listener previous timestamp (" + previousTimestamp + ") > timestamp (" + timestamp + "), last seq num=" + previousSeqNum + ", seqnum=" + sequenceNumber + "."); // Cover for this bug until it's nailed down. senderSendSpacing = lastSenderSendSpacing; } double senderSpacingMilliseconds = (double)senderSendSpacing / (double)TIMESTAMP_FACTOR; double interarrivalReceiveTime = m_lastRTPReceivedTime.Subtract(previousRTPReceiveTime).TotalMilliseconds; #region RTCP reporting. if (m_rtcpSampler == null) { //resultsLogger.Info("First Packet: " + rtpPacket.Header.SequenceNumber + "," + m_arrivalTime.ToString("HH:mm:fff")); m_rtcpSampler = new RTCPReportSampler(m_streamId, syncSource, remoteEndPoint, rtpPacket.Header.SequenceNumber, m_lastRTPReceivedTime, rcvdBytes.Length); m_rtcpSampler.RTCPReportReady += new RTCPSampleReadyDelegate(m_rtcpSampler_RTCPReportReady); m_rtcpSampler.StartSampling(); } else { //m_receiverReports[syncSource].RecordRTPReceive(rtpPacket.Header.SequenceNumber, sendTime, rtpReceiveTime, rcvdBytes.Length); // Transit time is calculated by knowing that the sender sent a packet at a certain time after the last send and the receiver received a pakcet a certain time after the last receive. // The difference in these two times is the jitter present. The transit time can change with each transimission and as this methid relies on two sends two packet // arrivals to calculate the transit time it's not going to be perfect (you'd need synchronised NTP clocks at each end to be able to be accurate). // However if used tor an average calculation it should be pretty close. //double transitTime = Math.Abs(interarrivalReceiveTime - senderSpacingMilliseconds); uint jitter = (interarrivalReceiveTime - senderSpacingMilliseconds > 0) ? Convert.ToUInt32(interarrivalReceiveTime - senderSpacingMilliseconds) : 0; if(jitter > 75) { logger.Debug("seqno=" + rtpPacket.Header.SequenceNumber + ", timestmap=" + timestamp + ", ts-prev=" + previousTimestamp + ", receive spacing=" + interarrivalReceiveTime + ", send spacing=" + senderSpacingMilliseconds + ", jitter=" + jitter); } else { //logger.Debug("seqno=" + rtpPacket.Header.SequenceNumber + ", receive spacing=" + interarrivalReceiveTime + ", timestamp=" + timestamp + ", transit time=" + transitTime); } m_rtcpSampler.RecordRTPReceive(m_lastRTPReceivedTime, rtpPacket.Header.SequenceNumber, rcvdBytes.Length, jitter); } #endregion } else { logger.Debug("RTPSink Listen SyncSource=" + rtpPacket.Header.SyncSource + "."); } previousTimestamp = timestamp; } catch (Exception excp) { logger.Error("Received data was not a valid RTP packet. " + excp.Message); } #region Switching endpoint if required to cope with NAT. // If a packet is recieved from an endpoint that wasn't expected treat the stream as being NATted and switch the endpoint to the socket on the NAT server. try { if (m_streamEndPoint != null && m_streamEndPoint.Address != null && remoteEndPoint != null && remoteEndPoint.Address != null && (m_streamEndPoint.Address.ToString() != remoteEndPoint.Address.ToString() || m_streamEndPoint.Port != remoteEndPoint.Port)) { logger.Debug("Expecting RTP on " + IPSocket.GetSocketString(m_streamEndPoint) + " but received on " + IPSocket.GetSocketString(remoteEndPoint) + ", now sending to " + IPSocket.GetSocketString(remoteEndPoint) + "."); m_streamEndPoint = remoteEndPoint; if (RemoteEndPointChanged != null) { try { RemoteEndPointChanged(m_streamId, remoteEndPoint); } catch (Exception changeExcp) { logger.Error("Exception RTPListener Changing Remote EndPoint. " + changeExcp.Message); } } } } catch (Exception setSendExcp) { logger.Error("Exception RTPListener setting SendTo Socket. " + setSendExcp.Message); } #endregion } else if (!StopListening) // Empty packet was received possibly indicating connection closure so check for timeout. { double noRTPRcvdDuration = (m_lastRTPReceivedTime != DateTime.MinValue) ? DateTime.Now.Subtract(m_lastRTPReceivedTime).TotalSeconds : 0; double noRTPSentDuration = (m_lastRTPSentTime != DateTime.MinValue) ? DateTime.Now.Subtract(m_lastRTPSentTime).TotalSeconds : 0; //logger.Warn("Remote socket closed on receive on " + m_localEndPoint.Address.ToString() + ":" + + m_localEndPoint.Port + ", reinitialising. No rtp for " + noRTPRcvdDuration + "s. last rtp " + m_lastRTPReceivedTime.ToString("dd MMM yyyy HH:mm:ss") + "."); // If this check is not done then the stream will never time out if it doesn't receive the first packet. if (m_lastRTPReceivedTime == DateTime.MinValue) { m_lastRTPReceivedTime = DateTime.Now; } remoteEndPoint = new IPEndPoint(IPAddress.Any, 0); if ((noRTPRcvdDuration > NO_RTP_TIMEOUT || noRTPSentDuration > NO_RTP_TIMEOUT) && StopIfNoData) { logger.Warn("Disconnecting RTP listener on " + m_localEndPoint.ToString() + " due to not being able to send or receive any RTP for " + NO_RTP_TIMEOUT + "s."); Shutdown(); } } } } catch (Exception excp) { logger.Error("Exception Listen RTPSink: " + excp.Message); } finally { #region Shut down socket. Shutdown(); if (ListenerClosed != null) { try { ListenerClosed(m_streamId, m_callDescriptorId); } catch (Exception excp) { logger.Error("Exception RTPSink ListenerClosed. " + excp.Message); } } #endregion } }
/// <summary> /// Sends a dynamically sized frame. The RTP marker bit will be set for the last transmitted packet in the frame. /// </summary> /// <param name="frame">The frame to transmit.</param> /// <param name="frameSpacing">The increment to add to the RTP timestamp for each new frame.</param> /// <param name="payloadType">The payload type to set on the RTP packet.</param> public void SendVP8Frame(byte[] frame, uint frameSpacing, int payloadType) { try { if (_isClosed) { logger.Warn("SendVP8Frame cannot be called on a closed RTP channel."); } else if (_rtpSocketError != SocketError.Success) { logger.Warn("SendVP8Frame was called for an RTP socket in an error state of " + _rtpSocketError + "."); } else { _timestamp = (_timestamp == 0) ? DateTimeToNptTimestamp32(DateTime.Now) : (_timestamp + frameSpacing) % UInt32.MaxValue; //System.Diagnostics.Debug.WriteLine("Sending " + frame.Length + " encoded bytes to client, timestamp " + _timestamp + ", starting sequence number " + _sequenceNumber + "."); for (int index = 0; index * RTP_MAX_PAYLOAD < frame.Length; index++) { //byte[] vp8HeaderBytes = (index == 0) ? new byte[VP8_RTP_HEADER_LENGTH] { 0x90, 0x80, (byte)(_sequenceNumber % 128) } : new byte[VP8_RTP_HEADER_LENGTH] { 0x80, 0x80, (byte)(_sequenceNumber % 128) }; byte[] vp8HeaderBytes = (index == 0) ? new byte[VP8_RTP_HEADER_LENGTH] { 0x10 } : new byte[VP8_RTP_HEADER_LENGTH] { 0x00 }; int offset = index * RTP_MAX_PAYLOAD; int payloadLength = ((index + 1) * RTP_MAX_PAYLOAD < frame.Length) ? RTP_MAX_PAYLOAD : frame.Length - index * RTP_MAX_PAYLOAD; // RTPPacket rtpPacket = new RTPPacket(payloadLength + VP8_RTP_HEADER_LENGTH + ((_srtp != null) ? SRTP_SIGNATURE_LENGTH : 0)); RTPPacket rtpPacket = new RTPPacket(payloadLength + VP8_RTP_HEADER_LENGTH); rtpPacket.Header.SyncSource = _syncSource; rtpPacket.Header.SequenceNumber = _sequenceNumber++; rtpPacket.Header.Timestamp = _timestamp; rtpPacket.Header.MarkerBit = (offset + payloadLength >= frame.Length) ? 1 : 0; rtpPacket.Header.PayloadType = payloadType; Buffer.BlockCopy(vp8HeaderBytes, 0, rtpPacket.Payload, 0, vp8HeaderBytes.Length); Buffer.BlockCopy(frame, offset, rtpPacket.Payload, vp8HeaderBytes.Length, payloadLength); byte[] rtpBytes = rtpPacket.GetBytes(); //if (_srtp != null) //{ // int rtperr = _srtp.ProtectRTP(rtpBytes, rtpBytes.Length - SRTP_SIGNATURE_LENGTH); // if (rtperr != 0) // { // logger.Warn("An error was returned attempting to sign an SRTP packet for " + _remoteEndPoint + ", error code " + rtperr + "."); // } //} //System.Diagnostics.Debug.WriteLine(" offset " + (index * RTP_MAX_PAYLOAD) + ", payload length " + payloadLength + ", sequence number " + rtpPacket.Header.SequenceNumber + ", marker " + rtpPacket.Header .MarkerBit + "."); //Stopwatch sw = new Stopwatch(); //sw.Start(); _rtpSocket.SendTo(rtpBytes, rtpBytes.Length, SocketFlags.None, _remoteEndPoint); //sw.Stop(); //if (sw.ElapsedMilliseconds > 15) //{ // logger.Warn(" SendVP8Frame offset " + offset + ", payload length " + payloadLength + ", sequence number " + rtpPacket.Header.SequenceNumber + ", marker " + rtpPacket.Header.MarkerBit + ", took " + sw.ElapsedMilliseconds + "ms."); //} } } } catch (Exception excp) { if (!_isClosed) { logger.Warn("Exception RTPChannel.SendVP8Frame attempting to send to the RTP socket at " + _remoteEndPoint + ". " + excp); if (OnRTPSocketDisconnected != null) { OnRTPSocketDisconnected(); } } } }
/// <summary> /// H264 frames need a two byte header when transmitted over RTP. /// </summary> /// <param name="frame">The H264 encoded frame to transmit.</param> /// <param name="payloadType">The payload type to set on the RTP packet.</param> public void SendH264Frame(byte[] frame, int payloadType) { try { if (_closed) { logger.Warn("SendH264Frame cannot be called on a closed session."); } else if (_rtpSocketError != SocketError.Success) { logger.Warn("SendH264Frame was called for an RTP socket in an error state of " + _rtpSocketError + "."); } else if (_remoteEndPoint == null) { logger.Warn("SendH264Frame frame not sent as remote end point is not yet set."); } else { RecalculateTimestampStep(); _timestamp += _timestampStep; //System.Diagnostics.Debug.WriteLine("Sending " + frame.Length + " H264 encoded bytes to client, timestamp " + _timestamp + ", starting sequence number " + _sequenceNumber + "."); for (int index = 0; index * RTP_MAX_PAYLOAD < frame.Length; index++) { uint offset = Convert.ToUInt32(index * RTP_MAX_PAYLOAD); int payloadLength = ((index + 1) * RTP_MAX_PAYLOAD < frame.Length) ? RTP_MAX_PAYLOAD : frame.Length - index * RTP_MAX_PAYLOAD; RTPPacket rtpPacket = new RTPPacket(payloadLength + H264_RTP_HEADER_LENGTH); rtpPacket.Header.SyncSource = _syncSource; rtpPacket.Header.SequenceNumber = _sequenceNumber++; rtpPacket.Header.Timestamp = _timestamp; rtpPacket.Header.MarkerBit = 0; rtpPacket.Header.PayloadType = payloadType; // Start RTP packet in frame 0x1c 0x89 // Middle RTP packet in frame 0x1c 0x09 // Last RTP packet in frame 0x1c 0x49 byte[] h264Header = new byte[] { 0x1c, 0x09 }; if (index == 0 && frame.Length < RTP_MAX_PAYLOAD) { // First and last RTP packet in the frame. h264Header = new byte[] { 0x1c, 0x49 }; rtpPacket.Header.MarkerBit = 1; } else if (index == 0) { h264Header = new byte[] { 0x1c, 0x89 }; } else if ((index + 1) * RTP_MAX_PAYLOAD > frame.Length) { h264Header = new byte[] { 0x1c, 0x49 }; rtpPacket.Header.MarkerBit = 1; } var h264Stream = frame.Skip(index * RTP_MAX_PAYLOAD).Take(payloadLength).ToList(); h264Stream.InsertRange(0, h264Header); rtpPacket.Payload = h264Stream.ToArray(); byte[] rtpBytes = rtpPacket.GetBytes(); //System.Diagnostics.Debug.WriteLine(" offset " + (index * RTP_MAX_PAYLOAD) + ", payload length " + payloadLength + ", sequence number " + rtpPacket.Header.SequenceNumber + ", marker " + rtpPacket.Header .MarkerBit + "."); //Stopwatch sw = new Stopwatch(); //sw.Start(); //_rtpSocket.SendTo(rtpBytes, rtpBytes.Length, SocketFlags.None, _remoteEndPoint); //SocketAsyncEventArgs socketSendArgs = new SocketAsyncEventArgs(); //socketSendArgs.SetBuffer(rtpBytes, 0, rtpBytes.Length); //socketSendArgs.RemoteEndPoint = _remoteEndPoint; //_rtpSocket.SendToAsync(socketSendArgs); _rtpSocket.BeginSendTo(rtpBytes, 0, rtpBytes.Length, SocketFlags.None, _remoteEndPoint, null, null); //sw.Stop(); //if (sw.ElapsedMilliseconds > 15) //{ // logger.Warn(" SendH264Frame offset " + offset + ", payload length " + payloadLength + ", sequence number " + rtpPacket.Header.SequenceNumber + ", marker " + rtpPacket.Header.MarkerBit + ", took " + sw.ElapsedMilliseconds + "ms."); //} } } } catch (Exception excp) { if (!_closed) { logger.Warn("Exception RTSPSession.SendH264Frame attempting to send to the RTP socket at " + _remoteEndPoint + ". " + excp); if (OnRTPSocketDisconnected != null) { OnRTPSocketDisconnected(_sessionID); } } } }
private static void SendRTPFromRawRTPFile(string file) { try { StreamReader sr = new StreamReader(file); List<string> samples = new List<string>(); while (!sr.EndOfStream) { samples.Add(sr.ReadLine()); } sr.Close(); logger.Debug(samples.Count + " encoded samples loaded."); //_newRTPReceiverSRTP = new SRTPManaged(Convert.FromBase64String(_sourceSRTPKey)); //_newRTPReceiverSRTP = new SRTPManaged(); int sampleIndex = 0; while (true) { if (_webRTCClients.Count != 0) { var sampleItem = samples[sampleIndex]; string[] sampleFields = sampleItem.Split(','); uint timestamp = Convert.ToUInt32(sampleFields[0]); int markerBit = Convert.ToInt32(sampleFields[1]); byte[] sample = Convert.FromBase64String(sampleFields[2]); lock (_webRTCClients) { foreach (var client in _webRTCClients.Where(x => x.STUNExchangeComplete)) { try { if (client.LastTimestamp == 0) { client.LastTimestamp = RTSPSession.DateTimeToNptTimestamp32(DateTime.Now); } //for (int index = 0; index * RTP_MAX_PAYLOAD < sample.Length; index++) //{ // int offset = (index == 0) ? 0 : (index * RTP_MAX_PAYLOAD) - 1; // int payloadLength = (offset + RTP_MAX_PAYLOAD < sample.Length - 1) ? RTP_MAX_PAYLOAD : sample.Length - 1 - offset; RTPPacket rtpPacket = new RTPPacket(sample.Length + SRTP_AUTH_KEY_LENGTH); rtpPacket.Header.SyncSource = client.SSRC; rtpPacket.Header.SequenceNumber = client.SequenceNumber++; rtpPacket.Header.Timestamp = client.LastTimestamp; rtpPacket.Header.MarkerBit = markerBit; rtpPacket.Header.PayloadType = 100; //if (offset + RTP_MAX_PAYLOAD > sample.Length - 1) //{ // Last packet in the frame. // rtpPacket.Header.MarkerBit = 1; //} Buffer.BlockCopy(sample, 0, rtpPacket.Payload, 0, sample.Length); var rtpBuffer = rtpPacket.GetBytes(); int rtperr = client.SrtpContext.ProtectRTP(rtpBuffer, rtpBuffer.Length - SRTP_AUTH_KEY_LENGTH); if (rtperr != 0) { logger.Debug("New RTP packet protect result " + rtperr + "."); } //logger.Debug("Sending RTP " + sample.Length + " bytes to " + client.SocketAddress + ", timestamp " + client.LastTimestamp + ", marker " + rtpPacket.Header.MarkerBit + "."); _webRTCReceiverClient.Send(rtpBuffer, rtpBuffer.Length, client.SocketAddress); if (markerBit == 1) { client.LastTimestamp += TIMESTAMP_SPACING; } //} } catch (Exception sendExcp) { logger.Error("SendRTPFromFile exception sending to " + client.SocketAddress + ". " + sendExcp.Message); } } } sampleIndex++; if (sampleIndex >= samples.Count - 1) { sampleIndex = 0; } //Thread.Sleep(30); } } } catch (Exception excp) { Console.WriteLine("Exception SendRTPFromFile. " + excp); } }
private static void SendRTPFromRawRTPFileNewVP8Header(string file) { try { StreamReader sr = new StreamReader(file); List<string> samples = new List<string>(); while (!sr.EndOfStream) { samples.Add(sr.ReadLine()); } sr.Close(); logger.Debug(samples.Count + " encoded samples loaded."); //_newRTPReceiverSRTP = new SRTPManaged(Convert.FromBase64String(_sourceSRTPKey)); //_newRTPReceiverSRTP = new SRTPManaged(); int sampleIndex = 0; while (true) { if (_webRTCClients.Count != 0) { var sampleItem = samples[sampleIndex]; string[] sampleFields = sampleItem.Split(','); //uint timestamp = Convert.ToUInt32(sampleFields[0]); int markerBit = Convert.ToInt32(sampleFields[1]); byte[] sample = Convert.FromBase64String(sampleFields[2]); lock (_webRTCClients) { foreach (var client in _webRTCClients.Where(x => x.STUNExchangeComplete)) { try { if (client.LastTimestamp == 0) { client.LastTimestamp = RTSPSession.DateTimeToNptTimestamp32(DateTime.Now); } RTPVP8Header origVP8Header = RTPVP8Header.GetVP8Header(sample); if (origVP8Header.IsKeyFrame) { Console.WriteLine("Key frame"); } RTPPacket rtpPacket = new RTPPacket(sample.Length + SRTP_AUTH_KEY_LENGTH); rtpPacket.Header.SyncSource = client.SSRC; rtpPacket.Header.SequenceNumber = client.SequenceNumber++; rtpPacket.Header.Timestamp = client.LastTimestamp; rtpPacket.Header.MarkerBit = markerBit; rtpPacket.Header.PayloadType = 100; if (origVP8Header.StartOfVP8Partition && markerBit == 1) { Console.WriteLine("My VP8 Header : " + BitConverter.ToString(origVP8Header.GetBytes()) + "."); Console.WriteLine("Sample VP8 Header: " + BitConverter.ToString(sample, 0, 6) + "."); Buffer.BlockCopy(origVP8Header.GetBytes(), 0, rtpPacket.Payload, 0, origVP8Header.Length); Buffer.BlockCopy(sample, origVP8Header.Length, rtpPacket.Payload, origVP8Header.Length, sample.Length - origVP8Header.Length); } else { Buffer.BlockCopy(sample, 0, rtpPacket.Payload, 0, sample.Length); } var rtpBuffer = rtpPacket.GetBytes(); _webRTCReceiverClient.Send(rtpBuffer, rtpBuffer.Length - SRTP_AUTH_KEY_LENGTH, _wiresharpEP); int rtperr = client.SrtpContext.ProtectRTP(rtpBuffer, rtpBuffer.Length - SRTP_AUTH_KEY_LENGTH); if (rtperr != 0) { logger.Debug("New RTP packet protect result " + rtperr + "."); } logger.Debug("Sending RTP " + sample.Length + " bytes to " + client.SocketAddress + ", timestamp " + client.LastTimestamp + ", marker " + rtpPacket.Header.MarkerBit + "."); _webRTCReceiverClient.Send(rtpBuffer, rtpBuffer.Length, client.SocketAddress); if (markerBit == 1) { client.LastTimestamp += TIMESTAMP_SPACING; } //} } catch (Exception sendExcp) { logger.Error("SendRTPFromFile exception sending to " + client.SocketAddress + ". " + sendExcp.Message); } } } sampleIndex++; if (sampleIndex >= samples.Count - 1) { sampleIndex = 0; } //Thread.Sleep(30); } } } catch (Exception excp) { Console.WriteLine("Exception SendRTPFromFile. " + excp); } }
private static void RelayRTP(UdpClient rtpClient) { try { DateTime lastCleanup = DateTime.Now; //_newRTPReceiverSRTP = new SRTPManaged(Convert.FromBase64String(_sourceSRTPKey)); //_newRTPReceiverSRTP = new SRTPManaged(); IPEndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0); byte[] buffer = rtpClient.Receive(ref remoteEndPoint); StreamWriter sw = new StreamWriter("rtpPackets.txt"); byte[] frame = new byte[1000000]; int framePosition = 0; int sampleCount = 0; DateTime lastReceiveTime = DateTime.Now; while (buffer != null && buffer.Length > 0 && !m_exit) { int packetSpacingMilli = Convert.ToInt32(DateTime.Now.Subtract(lastReceiveTime).TotalMilliseconds); Console.WriteLine("Packet spacing " + packetSpacingMilli + "ms."); lastReceiveTime = DateTime.Now; if (_webRTCClients.Count != 0) { RTPPacket triggerRTPPacket = new RTPPacket(buffer); RTPVP8Header vp8Header = RTPVP8Header.GetVP8Header(triggerRTPPacket.Payload); if (sampleCount < 1000) { sw.WriteLine(triggerRTPPacket.Header.Timestamp + "," + triggerRTPPacket.Header.MarkerBit + "," + Convert.ToBase64String(triggerRTPPacket.Payload)); //if (triggerRTPPacket.Header.MarkerBit == 1 && vp8Header.StartOfVP8Partition == true) //{ // // This is a single packet frame. // sw.WriteLine(Convert.ToBase64String(vp8Header.GetBytes()) + "," + Convert.ToBase64String(triggerRTPPacket.Payload, vp8Header.Length, triggerRTPPacket.Payload.Length - vp8Header.Length)); //} //else if (vp8Header.StartOfVP8Partition == true) //{ // // This is a first packet in a multi-packet frame. // sw.Write(Convert.ToBase64String(vp8Header.GetBytes()) + ","); // Buffer.BlockCopy(triggerRTPPacket.Payload, vp8Header.Length, frame, 0, triggerRTPPacket.Payload.Length - vp8Header.Length); // framePosition = triggerRTPPacket.Payload.Length - vp8Header.Length; //} //else if (triggerRTPPacket.Header.MarkerBit == 1) //{ // // This is the last continuation frame. // Buffer.BlockCopy(triggerRTPPacket.Payload, vp8Header.Length, frame, framePosition, triggerRTPPacket.Payload.Length - vp8Header.Length); // framePosition += triggerRTPPacket.Payload.Length - vp8Header.Length; // sw.WriteLine(Convert.ToBase64String(frame, 0, framePosition)); // framePosition = 0; //} //else //{ // // This is a middle continuation packet // Buffer.BlockCopy(triggerRTPPacket.Payload, vp8Header.Length, frame, framePosition, triggerRTPPacket.Payload.Length - vp8Header.Length); // framePosition += triggerRTPPacket.Payload.Length - vp8Header.Length; //} sampleCount++; if (sampleCount == 1000) { Console.WriteLine("Sample collection complete."); sw.Close(); } } lock (_webRTCClients) { foreach (var client in _webRTCClients.Where(x => x.STUNExchangeComplete)) { try { if (client.LastTimestamp == 0) { client.LastTimestamp = RTSPSession.DateTimeToNptTimestamp32(DateTime.Now); } else if (vp8Header.StartOfVP8Partition) { client.LastTimestamp += 11520; } RTPPacket rtpPacket = new RTPPacket(triggerRTPPacket.Payload.Length + SRTP_AUTH_KEY_LENGTH); rtpPacket.Header.SyncSource = client.SSRC; rtpPacket.Header.SequenceNumber = client.SequenceNumber++; rtpPacket.Header.Timestamp = client.LastTimestamp; //triggerRTPPacket.Header.Timestamp; // client.LastTimestamp; rtpPacket.Header.MarkerBit = triggerRTPPacket.Header.MarkerBit; rtpPacket.Header.PayloadType = 100; Buffer.BlockCopy(triggerRTPPacket.Payload, 0, rtpPacket.Payload, 0, triggerRTPPacket.Payload.Length); var rtpBuffer = rtpPacket.GetBytes(); _webRTCReceiverClient.Send(rtpBuffer, rtpBuffer.Length - SRTP_AUTH_KEY_LENGTH, _wiresharpEP); if (vp8Header.IsKeyFrame) { Console.WriteLine("key frame."); } //int rtperr = _newRTPReceiverSRTP.ProtectRTP(rtpBuffer, rtpBuffer.Length - 10); int rtperr = client.SrtpContext.ProtectRTP(rtpBuffer, rtpBuffer.Length - SRTP_AUTH_KEY_LENGTH); if (rtperr != 0) { logger.Debug("New RTP packet protect result " + rtperr + "."); } logger.Debug("Sending RTP " + rtpBuffer.Length + " bytes to " + client.SocketAddress + ", timestamp " + rtpPacket.Header.Timestamp + ", trigger timestamp " + triggerRTPPacket.Header.Timestamp + ", marker bit " + rtpPacket.Header.MarkerBit + "."); _webRTCReceiverClient.Send(rtpBuffer, rtpBuffer.Length, client.SocketAddress); } catch (Exception sendExcp) { logger.Error("RelayRTP exception sending to " + client.SocketAddress + ". " + sendExcp.Message); } } } } buffer = rtpClient.Receive(ref remoteEndPoint); } } catch (Exception excp) { logger.Error("Exception RelayRTP. " + excp); } }
private static void SendRTPFromCamera() { try { unsafe { SIPSorceryMedia.MFVideoSampler videoSampler = new SIPSorceryMedia.MFVideoSampler(); //List<VideoMode> webcamModes = new List<VideoMode>(); //int deviceCount = videoSampler.GetVideoDevices(ref webcamModes); //foreach (var videoMode in webcamModes) //{ // Console.WriteLine(videoMode.DeviceFriendlyName + " " + (videoMode.VideoSubTypeFriendlyName ?? videoMode.VideoSubType.ToString()) + " " + videoMode.Width + "x" + videoMode.Height + "."); //} videoSampler.Init(_webcamIndex, _webcamVideoSubType, _webcamWidth, _webcamHeight); SIPSorceryMedia.VPXEncoder vpxEncoder = new VPXEncoder(); vpxEncoder.InitEncoder(_webcamWidth, _webcamHeight); SIPSorceryMedia.ImageConvert colorConverter = new ImageConvert(); byte pictureID = 0x1; byte[] sampleBuffer = null; byte[] encodedBuffer = new byte[4096]; while (true) { if (_webRTCClients.Any(x => x.STUNExchangeComplete == true && x.IsDtlsNegotiationComplete == true)) { int result = videoSampler.GetSample(ref sampleBuffer); if (result != 0) { Console.WriteLine("Video sampler returned a null sample."); } else { //Console.WriteLine("Got managed sample " + sample.Buffer.Length + ", is key frame " + sample.IsKeyFrame + "."); fixed (byte* p = sampleBuffer) { byte[] convertedFrame = null; colorConverter.ConvertToI420(p, _webcamVideoSubType, Convert.ToInt32(_webcamWidth), Convert.ToInt32(_webcamHeight), ref convertedFrame); //int encodeResult = vpxEncoder.Encode(p, sampleBuffer.Length, 1, ref encodedBuffer); fixed (byte* q = convertedFrame) { int encodeResult = vpxEncoder.Encode(q, sampleBuffer.Length, 1, ref encodedBuffer); if (encodeResult != 0) { Console.WriteLine("VPX encode of video sample failed."); continue; } } } lock (_webRTCClients) { foreach (var client in _webRTCClients.Where(x => x.STUNExchangeComplete && x.IsDtlsNegotiationComplete == true)) { try { //if (client.LastRtcpSenderReportSentAt == DateTime.MinValue) //{ // logger.Debug("Sending RTCP report to " + client.SocketAddress + "."); // // Send RTCP report. // RTCPPacket rtcp = new RTCPPacket(client.SSRC, 0, 0, 0, 0); // byte[] rtcpBuffer = rtcp.GetBytes(); // _webRTCReceiverClient.BeginSend(rtcpBuffer, rtcpBuffer.Length, client.SocketAddress, null, null); // //int rtperr = client.SrtpContext.ProtectRTP(rtcpBuffer, rtcpBuffer.Length - SRTP_AUTH_KEY_LENGTH); //} //Console.WriteLine("Sending VP8 frame of " + encodedBuffer.Length + " bytes to " + client.SocketAddress + "."); client.LastTimestamp = (client.LastTimestamp == 0) ? RTSPSession.DateTimeToNptTimestamp32(DateTime.Now) : client.LastTimestamp + TIMESTAMP_SPACING; for (int index = 0; index * RTP_MAX_PAYLOAD < encodedBuffer.Length; index++) { int offset = (index == 0) ? 0 : (index * RTP_MAX_PAYLOAD); int payloadLength = (offset + RTP_MAX_PAYLOAD < encodedBuffer.Length) ? RTP_MAX_PAYLOAD : encodedBuffer.Length - offset; byte[] vp8HeaderBytes = (index == 0) ? new byte[] { 0x10 } : new byte[] { 0x00 }; RTPPacket rtpPacket = new RTPPacket(payloadLength + SRTP_AUTH_KEY_LENGTH + vp8HeaderBytes.Length); rtpPacket.Header.SyncSource = client.SSRC; rtpPacket.Header.SequenceNumber = client.SequenceNumber++; rtpPacket.Header.Timestamp = client.LastTimestamp; rtpPacket.Header.MarkerBit = ((offset + payloadLength) >= encodedBuffer.Length) ? 1 : 0; // Set marker bit for the last packet in the frame. rtpPacket.Header.PayloadType = PAYLOAD_TYPE_ID; Buffer.BlockCopy(vp8HeaderBytes, 0, rtpPacket.Payload, 0, vp8HeaderBytes.Length); Buffer.BlockCopy(encodedBuffer, offset, rtpPacket.Payload, vp8HeaderBytes.Length, payloadLength); var rtpBuffer = rtpPacket.GetBytes(); //_webRTCReceiverClient.Send(rtpBuffer, rtpBuffer.Length, _wiresharpEP); int rtperr = client.SrtpContext.ProtectRTP(rtpBuffer, rtpBuffer.Length - SRTP_AUTH_KEY_LENGTH); if (rtperr != 0) { logger.Warn("SRTP packet protection failed, result " + rtperr + "."); } else { //logger.Debug("Sending RTP, offset " + offset + ", frame bytes " + payloadLength + ", vp8 header bytes " + vp8HeaderBytes.Length + ", timestamp " + rtpPacket.Header.Timestamp + ", seq # " + rtpPacket.Header.SequenceNumber + " to " + client.SocketAddress + "."); //_webRTCReceiverClient.Send(rtpBuffer, rtpBuffer.Length, client.SocketAddress); _webRTCReceiverClient.BeginSend(rtpBuffer, rtpBuffer.Length, client.SocketAddress, null, null); } } } catch (Exception sendExcp) { logger.Error("SendRTP exception sending to " + client.SocketAddress + ". " + sendExcp.Message); } } } pictureID++; if (pictureID > 127) { pictureID = 1; } encodedBuffer = null; sampleBuffer = null; } } } } } catch (Exception excp) { Console.WriteLine("Exception SendRTP. " + excp); } }
private static void SendRTPFromVP8FramesFile(string file) { try { StreamReader sr = new StreamReader(file); List<string> samples = new List<string>(); while (!sr.EndOfStream) { string sample = sr.ReadLine(); samples.Add(sample); //Console.WriteLine(sample); //string[] sampleFields = sample.Split(','); //RTPVP8Header frameVP8Header = RTPVP8Header.GetVP8Header(Convert.FromBase64String(sampleFields[0])); //byte[] rtpPaylaod = Convert.FromBase64String(sampleFields[1]); //Console.WriteLine((frameVP8Header.IsKeyFrame) ? "K" : "." + " " + frameVP8Header.FirstPartitionSize + " " + rtpPaylaod.Length + "."); } sr.Close(); logger.Debug(samples.Count + " encoded samples loaded."); //_newRTPReceiverSRTP = new SRTPManaged(Convert.FromBase64String(_sourceSRTPKey)); //_newRTPReceiverSRTP = new SRTPManaged(); int sampleIndex = 0; while (true) { if (_webRTCClients.Count != 0) { var sampleItem = samples[sampleIndex]; string[] sampleFields = sampleItem.Split(','); RTPVP8Header frameVP8Header = RTPVP8Header.GetVP8Header(Convert.FromBase64String(sampleFields[0])); byte[] sample = Convert.FromBase64String(sampleFields[1]); if (frameVP8Header.IsKeyFrame) { Console.WriteLine("Key frame."); } lock (_webRTCClients) { foreach (var client in _webRTCClients.Where(x => x.STUNExchangeComplete)) { try { if (client.LastTimestamp == 0) { client.LastTimestamp = RTSPSession.DateTimeToNptTimestamp32(DateTime.Now); } for (int index = 0; index * RTP_MAX_PAYLOAD < sample.Length; index++) { int offset = (index == 0) ? 0 : (index * RTP_MAX_PAYLOAD); int payloadLength = (offset + RTP_MAX_PAYLOAD < sample.Length) ? RTP_MAX_PAYLOAD : sample.Length - offset; RTPVP8Header packetVP8Header = new RTPVP8Header() { ExtendedControlBitsPresent = true, IsPictureIDPresent = true, ShowFrame = true, }; if (index == 0) { packetVP8Header.StartOfVP8Partition = true; //packetVP8Header.FirstPartitionSize = frameVP8Header.FirstPartitionSize; packetVP8Header.IsKeyFrame = frameVP8Header.IsKeyFrame; packetVP8Header.PictureID = (frameVP8Header.IsKeyFrame) ? (byte)0x00 : frameVP8Header.PictureID; } byte[] vp8HeaderBytes = packetVP8Header.GetBytes(); RTPPacket rtpPacket = new RTPPacket(packetVP8Header.Length + payloadLength + SRTP_AUTH_KEY_LENGTH); rtpPacket.Header.SyncSource = client.SSRC; rtpPacket.Header.SequenceNumber = client.SequenceNumber++; rtpPacket.Header.Timestamp = client.LastTimestamp; rtpPacket.Header.MarkerBit = ((offset + payloadLength) >= sample.Length) ? 1 : 0; rtpPacket.Header.PayloadType = 100; Buffer.BlockCopy(vp8HeaderBytes, 0, rtpPacket.Payload, 0, packetVP8Header.Length); Buffer.BlockCopy(sample, offset, rtpPacket.Payload, packetVP8Header.Length, payloadLength); var rtpBuffer = rtpPacket.GetBytes(); _webRTCReceiverClient.Send(rtpBuffer, rtpBuffer.Length - SRTP_AUTH_KEY_LENGTH, _wiresharpEP); int rtperr = client.SrtpContext.ProtectRTP(rtpBuffer, rtpBuffer.Length - SRTP_AUTH_KEY_LENGTH); if (rtperr != 0) { logger.Debug("New RTP packet protect result " + rtperr + "."); } logger.Debug("Sending RTP " + sample.Length + " bytes to " + client.SocketAddress + ", timestamp " + client.LastTimestamp + ", marker " + rtpPacket.Header.MarkerBit + "."); _webRTCReceiverClient.Send(rtpBuffer, rtpBuffer.Length, client.SocketAddress); } client.LastTimestamp += TIMESTAMP_SPACING; } catch (Exception sendExcp) { logger.Error("SendRTPFromVP8FramesFile exception sending to " + client.SocketAddress + ". " + sendExcp.Message); } } } sampleIndex++; if (sampleIndex >= samples.Count - 1) { sampleIndex = 0; } Thread.Sleep(30); } } } catch (Exception excp) { Console.WriteLine("Exception SendRTPFromVP8FramesFile. " + excp); } }
/// <summary> /// Audio frames are generally contained within a single RTP packet. This method is a shortcut /// to construct a frame from a single RTP packet. /// </summary> public static RTPFrame MakeSinglePacketFrame(RTPPacket rtpPacket) { RTPFrame frame = new RTPFrame(); frame.AddRTPPacket(rtpPacket); frame.Timestamp = rtpPacket.Header.Timestamp; return frame; }
private void Send() { try { int payloadSize = RTPPacketSendSize; RTPPacket rtpPacket = new RTPPacket(RTPPacketSendSize); byte[] rtpBytes = rtpPacket.GetBytes(); RTPHeader rtpHeader = new RTPHeader(); rtpHeader.SequenceNumber = (UInt16)65000; //Convert.ToUInt16(Crypto.GetRandomInt(0, UInt16.MaxValue)); uint sendTimestamp = uint.MaxValue - 5000; uint lastSendTimestamp = sendTimestamp; UInt16 lastSeqNum = 0; logger.Debug("RTP send stream starting to " + IPSocket.GetSocketString(m_streamEndPoint) + " with payload size " + payloadSize + " bytes."); Sending = true; m_startRTPSendTime = DateTime.MinValue; m_lastRTPSentTime = DateTime.MinValue; m_sampleStartSeqNo = rtpHeader.SequenceNumber; DateTime lastRTPSendAttempt = DateTime.Now; while (m_udpListener != null && !StopListening) { // This may be changed by the listener so it needs to be set each iteration. IPEndPoint dstEndPoint = m_streamEndPoint; //logger.Info("Sending RTP packet to " + dstEndPoint.Address + ":" + dstEndPoint.Port); if (payloadSize != m_rtpPacketSendSize) { payloadSize = m_rtpPacketSendSize; logger.Info("Changing RTP payload to " + payloadSize); rtpPacket = new RTPPacket(RTP_HEADER_SIZE + m_rtpPacketSendSize); rtpBytes = rtpPacket.GetBytes(); } try { if (m_startRTPSendTime == DateTime.MinValue) { m_startRTPSendTime = DateTime.Now; rtpHeader.MarkerBit = 0; logger.Debug("RTPSink Send SyncSource=" + rtpPacket.Header.SyncSource + "."); } else { lastSendTimestamp = sendTimestamp; double milliSinceLastSend = DateTime.Now.Subtract(m_lastRTPSentTime).TotalMilliseconds; sendTimestamp = Convert.ToUInt32((lastSendTimestamp + (milliSinceLastSend * TIMESTAMP_FACTOR)) % uint.MaxValue); if (lastSendTimestamp > sendTimestamp) { logger.Error("RTP Sender previous timestamp (" + lastSendTimestamp + ") > timestamp (" + sendTimestamp + ") ms since last send=" + milliSinceLastSend + ", lastseqnum=" + lastSeqNum + ", seqnum=" + rtpHeader.SequenceNumber + "."); } if (DateTime.Now.Subtract(m_lastRTPSentTime).TotalMilliseconds > 75) { logger.Debug("delayed send: " + rtpHeader.SequenceNumber + ", time since last send " + DateTime.Now.Subtract(m_lastRTPSentTime).TotalMilliseconds + "ms."); } } rtpHeader.Timestamp = sendTimestamp; byte[] rtpHeaderBytes = rtpHeader.GetBytes(); Array.Copy(rtpHeaderBytes, 0, rtpBytes, 0, rtpHeaderBytes.Length); // Send RTP packets and any extra channels required to emulate mutliple calls. for (int channelCount = 0; channelCount < m_channels; channelCount++) { //logger.Debug("Send rtp getting wallclock timestamp for " + DateTime.Now.ToString("dd MMM yyyy HH:mm:ss:fff")); //DateTime sendTime = DateTime.Now; //rtpHeader.Timestamp = RTPHeader.GetWallclockUTCStamp(sendTime); //logger.Debug(rtpHeader.SequenceNumber + "," + rtpHeader.Timestamp); m_udpListener.Send(rtpBytes, rtpBytes.Length, dstEndPoint); m_lastRTPSentTime = DateTime.Now; m_packetsSent++; m_bytesSent += rtpBytes.Length; if (m_packetsSent % 500 == 0) { logger.Debug("Total packets sent to " + dstEndPoint.ToString() + " " + m_packetsSent + ", bytes " + NumberFormatter.ToSIByteFormat(m_bytesSent, 2) + "."); } //sendLogger.Info(m_lastRTPSentTime.ToString("dd MMM yyyy HH:mm:ss:fff") + "," + m_lastRTPSentTime.Subtract(m_startRTPSendTime).TotalMilliseconds.ToString("0") + "," + rtpHeader.SequenceNumber + "," + rtpBytes.Length); //sendLogger.Info(rtpHeader.SequenceNumber + "," + DateTime.Now.ToString("dd MMM yyyy HH:mm:ss:fff")); if (DataSent != null) { try { DataSent(m_streamId, rtpBytes, dstEndPoint); } catch (Exception excp) { logger.Error("Exception RTPSink DataSent. " + excp.Message); } } lastSeqNum = rtpHeader.SequenceNumber; if (rtpHeader.SequenceNumber == UInt16.MaxValue) { //logger.Debug("RTPSink looping the sequence number in sample."); rtpHeader.SequenceNumber = 0; } else { rtpHeader.SequenceNumber++; } } } catch (Exception excp) { logger.Error("Exception RTP Send. " + excp.GetType() + ". " + excp.Message); if (excp.GetType() == typeof(SocketException)) { logger.Error("socket exception errorcode=" + ((SocketException)excp).ErrorCode + "."); } logger.Warn("Remote socket closed on send. Last RTP recevied " + m_lastRTPReceivedTime.ToString("dd MMM yyyy HH:mm:ss") + ", last RTP successfull send " + m_lastRTPSentTime.ToString("dd MMM yyyy HH:mm:ss") + "."); } Thread.Sleep(RTPFrameSize); #region Check for whether the stream has timed out on a send or receive and if so shut down the stream. double noRTPRcvdDuration = (m_lastRTPReceivedTime != DateTime.MinValue) ? DateTime.Now.Subtract(m_lastRTPReceivedTime).TotalSeconds : 0; double noRTPSentDuration = (m_lastRTPSentTime != DateTime.MinValue) ? DateTime.Now.Subtract(m_lastRTPSentTime).TotalSeconds : 0; double testDuration = DateTime.Now.Subtract(m_startRTPSendTime).TotalSeconds; if (( noRTPRcvdDuration > NO_RTP_TIMEOUT || noRTPSentDuration > NO_RTP_TIMEOUT || (m_lastRTPReceivedTime == DateTime.MinValue && testDuration > NO_RTP_TIMEOUT)) // If the test request comes from a private or unreachable IP address then no RTP will ever be received. && StopIfNoData) { logger.Warn("Disconnecting RTP stream on " + m_localEndPoint.Address.ToString() + ":" + m_localEndPoint.Port + " due to not being able to send any RTP for " + NO_RTP_TIMEOUT + "s."); StopListening = true; } // Shutdown the socket even if there is still RTP but the stay alive limit has been exceeded. if (RTPMaxStayAlive > 0 && DateTime.Now.Subtract(m_startRTPSendTime).TotalSeconds > RTPMaxStayAlive) { logger.Warn("Shutting down RTPSink due to passing RTPMaxStayAlive time."); Shutdown(); StopListening = true; } #endregion } } catch (Exception excp) { logger.Error("Exception Send RTPSink: " + excp.Message); } finally { #region Shut down socket. Shutdown(); if (SenderClosed != null) { try { SenderClosed(m_streamId, m_callDescriptorId); } catch (Exception excp) { logger.Error("Exception RTPSink SenderClosed. " + excp.Message); } } #endregion } }
private void SendRTPPacket(string sourceSocket, string destinationSocket) { try { //logger.Debug("Attempting to send RTP packet from " + sourceSocket + " to " + destinationSocket + "."); Log("Attempting to send RTP packet from " + sourceSocket + " to " + destinationSocket + "."); IPEndPoint sourceEP = IPSocket.GetIPEndPoint(sourceSocket); IPEndPoint destEP = IPSocket.GetIPEndPoint(destinationSocket); RTPPacket rtpPacket = new RTPPacket(80); rtpPacket.Header.SequenceNumber = (UInt16)6500; rtpPacket.Header.Timestamp = 100000; UDPPacket udpPacket = new UDPPacket(sourceEP.Port, destEP.Port, rtpPacket.GetBytes()); IPv4Header ipHeader = new IPv4Header(ProtocolType.Udp, Crypto.GetRandomInt(6), sourceEP.Address, destEP.Address); IPv4Packet ipPacket = new IPv4Packet(ipHeader, udpPacket.GetBytes()); byte[] data = ipPacket.GetBytes(); Socket rawSocket = new Socket(AddressFamily.InterNetwork, SocketType.Raw, ProtocolType.IP); rawSocket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.HeaderIncluded, 1); rawSocket.SendTo(data, destEP); } catch (Exception excp) { logger.Error("Exception SendRTPPacket. " + excp.Message); } }
private void Listen() { try { UdpClient udpSvr = m_udpListener; if (udpSvr == null) { logger.LogError("The UDP server was not correctly initialised in the RTP sink when attempting to start the listener, the RTP stream has not been intialised."); return; } else { logger.LogDebug("RTP Listener now listening on " + m_localEndPoint.Address + ":" + m_localEndPoint.Port + "."); } IPEndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0); byte[] rcvdBytes = null; m_startRTPReceiveTime = DateTime.MinValue; m_lastRTPReceivedTime = DateTime.MinValue; DateTime previousRTPReceiveTime = DateTime.MinValue; uint previousTimestamp = 0; UInt16 sequenceNumber = 0; UInt16 previousSeqNum = 0; uint senderSendSpacing = 0; uint lastSenderSendSpacing = 0; while (!StopListening) { rcvdBytes = null; try { rcvdBytes = udpSvr.Receive(ref remoteEndPoint); } catch { //logger.LogWarning("Remote socket closed on receive. Last RTP received " + m_lastRTPReceivedTime.ToString("dd MMM yyyy HH:mm:ss") + ", last RTP successfull send " + m_lastRTPSentTime.ToString("dd MMM yyyy HH:mm:ss") + "."); } if (rcvdBytes != null && rcvdBytes.Length > 0) { // Check whether this is an RTCP report. UInt16 firstWord = BitConverter.ToUInt16(rcvdBytes, 0); if (BitConverter.IsLittleEndian) { firstWord = NetConvert.DoReverseEndian(firstWord); } ushort packetType = 0; if (BitConverter.IsLittleEndian) { packetType = Convert.ToUInt16(firstWord & 0x00ff); } if (packetType == RTCPHeader.RTCP_PACKET_TYPE) { logger.LogDebug("RTP Listener received remote RTCP report from " + remoteEndPoint + "."); try { RTCPPacket rtcpPacket = new RTCPPacket(rcvdBytes); RTCPReportPacket rtcpReportPacket = new RTCPReportPacket(rtcpPacket.Reports); if (RTCPReportReceived != null) { RTCPReportReceived(this, rtcpReportPacket); } } catch (Exception rtcpExcp) { logger.LogError("Exception processing remote RTCP report. " + rtcpExcp.Message); } continue; } // Channel statistics. DateTime rtpReceiveTime = DateTime.Now; if (m_startRTPReceiveTime == DateTime.MinValue) { m_startRTPReceiveTime = rtpReceiveTime; //m_sampleStartTime = rtpReceiveTime; } previousRTPReceiveTime = new DateTime(m_lastRTPReceivedTime.Ticks); m_lastRTPReceivedTime = rtpReceiveTime; m_packetsReceived++; m_bytesReceived += rcvdBytes.Length; previousSeqNum = sequenceNumber; // This stops the thread running the ListenerTimeout method from deciding the strema has recieved no RTP and therefore should be shutdown. m_lastPacketReceived.Set(); // Let whoever has subscribed that an RTP packet has been received. if (DataReceived != null) { try { DataReceived(m_streamId, rcvdBytes, remoteEndPoint); } catch (Exception excp) { logger.LogError("Exception RTPSink DataReceived. " + excp.Message); } } if (m_packetsReceived % 500 == 0) { logger.LogDebug("Total packets received from " + remoteEndPoint.ToString() + " " + m_packetsReceived + ", bytes " + NumberFormatter.ToSIByteFormat(m_bytesReceived, 2) + "."); } try { RTPPacket rtpPacket = new RTPPacket(rcvdBytes); uint syncSource = rtpPacket.Header.SyncSource; uint timestamp = rtpPacket.Header.Timestamp; sequenceNumber = rtpPacket.Header.SequenceNumber; //logger.LogDebug("seqno=" + rtpPacket.Header.SequenceNumber + ", timestamp=" + timestamp); if (previousRTPReceiveTime != DateTime.MinValue) { //uint senderSendSpacing = rtpPacket.Header.Timestamp - previousTimestamp; // Need to cope with cases where the timestamp has looped, if this timestamp is < last timesatmp and there is a large difference in them then it's because the timestamp counter has looped. lastSenderSendSpacing = senderSendSpacing; senderSendSpacing = (Math.Abs(timestamp - previousTimestamp) > (uint.MaxValue / 2)) ? timestamp + uint.MaxValue - previousTimestamp : timestamp - previousTimestamp; if (previousTimestamp > timestamp) { logger.LogError("BUG: Listener previous timestamp (" + previousTimestamp + ") > timestamp (" + timestamp + "), last seq num=" + previousSeqNum + ", seqnum=" + sequenceNumber + "."); // Cover for this bug until it's nailed down. senderSendSpacing = lastSenderSendSpacing; } double senderSpacingMilliseconds = (double)senderSendSpacing / (double)TIMESTAMP_FACTOR; double interarrivalReceiveTime = m_lastRTPReceivedTime.Subtract(previousRTPReceiveTime).TotalMilliseconds; #region RTCP reporting. if (m_rtcpSampler == null) { //resultslogger.LogInformation("First Packet: " + rtpPacket.Header.SequenceNumber + "," + m_arrivalTime.ToString("HH:mm:fff")); m_rtcpSampler = new RTCPReportSampler(m_streamId, syncSource, remoteEndPoint, rtpPacket.Header.SequenceNumber, m_lastRTPReceivedTime, rcvdBytes.Length); m_rtcpSampler.RTCPReportReady += new RTCPSampleReadyDelegate(m_rtcpSampler_RTCPReportReady); m_rtcpSampler.StartSampling(); } else { //m_receiverReports[syncSource].RecordRTPReceive(rtpPacket.Header.SequenceNumber, sendTime, rtpReceiveTime, rcvdBytes.Length); // Transit time is calculated by knowing that the sender sent a packet at a certain time after the last send and the receiver received a pakcet a certain time after the last receive. // The difference in these two times is the jitter present. The transit time can change with each transimission and as this methid relies on two sends two packet // arrivals to calculate the transit time it's not going to be perfect (you'd need synchronised NTP clocks at each end to be able to be accurate). // However if used tor an average calculation it should be pretty close. //double transitTime = Math.Abs(interarrivalReceiveTime - senderSpacingMilliseconds); uint jitter = (interarrivalReceiveTime - senderSpacingMilliseconds > 0) ? Convert.ToUInt32(interarrivalReceiveTime - senderSpacingMilliseconds) : 0; if (jitter > 75) { logger.LogDebug("seqno=" + rtpPacket.Header.SequenceNumber + ", timestmap=" + timestamp + ", ts-prev=" + previousTimestamp + ", receive spacing=" + interarrivalReceiveTime + ", send spacing=" + senderSpacingMilliseconds + ", jitter=" + jitter); } else { //logger.LogDebug("seqno=" + rtpPacket.Header.SequenceNumber + ", receive spacing=" + interarrivalReceiveTime + ", timestamp=" + timestamp + ", transit time=" + transitTime); } m_rtcpSampler.RecordRTPReceive(m_lastRTPReceivedTime, rtpPacket.Header.SequenceNumber, rcvdBytes.Length, jitter); } #endregion } else { logger.LogDebug("RTPSink Listen SyncSource=" + rtpPacket.Header.SyncSource + "."); } previousTimestamp = timestamp; } catch (Exception excp) { logger.LogError("Received data was not a valid RTP packet. " + excp.Message); } #region Switching endpoint if required to cope with NAT. // If a packet is recieved from an endpoint that wasn't expected treat the stream as being NATted and switch the endpoint to the socket on the NAT server. try { if (m_streamEndPoint != null && m_streamEndPoint.Address != null && remoteEndPoint != null && remoteEndPoint.Address != null && (m_streamEndPoint.Address.ToString() != remoteEndPoint.Address.ToString() || m_streamEndPoint.Port != remoteEndPoint.Port)) { logger.LogDebug("Expecting RTP on " + IPSocket.GetSocketString(m_streamEndPoint) + " but received on " + IPSocket.GetSocketString(remoteEndPoint) + ", now sending to " + IPSocket.GetSocketString(remoteEndPoint) + "."); m_streamEndPoint = remoteEndPoint; if (RemoteEndPointChanged != null) { try { RemoteEndPointChanged(m_streamId, remoteEndPoint); } catch (Exception changeExcp) { logger.LogError("Exception RTPListener Changing Remote EndPoint. " + changeExcp.Message); } } } } catch (Exception setSendExcp) { logger.LogError("Exception RTPListener setting SendTo Socket. " + setSendExcp.Message); } #endregion } else if (!StopListening) // Empty packet was received possibly indicating connection closure so check for timeout. { double noRTPRcvdDuration = (m_lastRTPReceivedTime != DateTime.MinValue) ? DateTime.Now.Subtract(m_lastRTPReceivedTime).TotalSeconds : 0; double noRTPSentDuration = (m_lastRTPSentTime != DateTime.MinValue) ? DateTime.Now.Subtract(m_lastRTPSentTime).TotalSeconds : 0; //logger.LogWarning("Remote socket closed on receive on " + m_localEndPoint.Address.ToString() + ":" + + m_localEndPoint.Port + ", reinitialising. No rtp for " + noRTPRcvdDuration + "s. last rtp " + m_lastRTPReceivedTime.ToString("dd MMM yyyy HH:mm:ss") + "."); // If this check is not done then the stream will never time out if it doesn't receive the first packet. if (m_lastRTPReceivedTime == DateTime.MinValue) { m_lastRTPReceivedTime = DateTime.Now; } remoteEndPoint = new IPEndPoint(IPAddress.Any, 0); if ((noRTPRcvdDuration > NO_RTP_TIMEOUT || noRTPSentDuration > NO_RTP_TIMEOUT) && StopIfNoData) { logger.LogWarning("Disconnecting RTP listener on " + m_localEndPoint.ToString() + " due to not being able to send or receive any RTP for " + NO_RTP_TIMEOUT + "s."); Shutdown(); } } } } catch (Exception excp) { logger.LogError("Exception Listen RTPSink: " + excp.Message); } finally { #region Shut down socket. Shutdown(); if (ListenerClosed != null) { try { ListenerClosed(m_streamId, m_callDescriptorId); } catch (Exception excp) { logger.LogError("Exception RTPSink ListenerClosed. " + excp.Message); } } #endregion } }
/// <summary> /// Sends an audio frame where the payload size is less than the maximum RTP packet payload size. /// </summary> /// <param name="payload">The audio payload to transmit.</param> /// <param name="frameSpacing">The increment to add to the RTP timestamp for each new frame.</param> /// <param name="payloadType">The payload type to set on the RTP packet.</param> public void SendAudioFrame(byte[] payload, uint frameSpacing, int payloadType) { try { if (_isClosed) { logger.Warn("SendAudioFrame cannot be called on a closed RTP channel."); } else if (_rtpSocketError != SocketError.Success) { logger.Warn("SendAudioFrame was called for an RTP socket in an error state of " + _rtpSocketError + "."); } else { _timestamp = (_timestamp == 0) ? DateTimeToNptTimestamp32(DateTime.Now) : (_timestamp + frameSpacing) % UInt32.MaxValue; RTPPacket rtpPacket = new RTPPacket(payload.Length); rtpPacket.Header.SyncSource = _syncSource; rtpPacket.Header.SequenceNumber = _sequenceNumber++; rtpPacket.Header.Timestamp = _timestamp; rtpPacket.Header.MarkerBit = 1; rtpPacket.Header.PayloadType = payloadType; Buffer.BlockCopy(payload, 0, rtpPacket.Payload, 0, payload.Length); byte[] rtpBytes = rtpPacket.GetBytes(); //Stopwatch sw = new Stopwatch(); //sw.Start(); _rtpSocket.SendTo(rtpBytes, rtpBytes.Length, SocketFlags.None, _remoteEndPoint); //sw.Stop(); //if (sw.ElapsedMilliseconds > 15) //{ // logger.Warn(" SendAudioFrame offset " + offset + ", payload length " + payloadLength + ", sequence number " + rtpPacket.Header.SequenceNumber + ", marker " + rtpPacket.Header.MarkerBit + ", took " + sw.ElapsedMilliseconds + "ms."); //} } } catch (Exception excp) { if (!_isClosed) { logger.Warn("Exception RTPChannel.SendAudioFrame attempting to send to the RTP socket at " + _remoteEndPoint + ". " + excp); if (OnRTPSocketDisconnected != null) { OnRTPSocketDisconnected(); } } } }
private void Send() { try { int payloadSize = RTPPacketSendSize; RTPPacket rtpPacket = new RTPPacket(RTPPacketSendSize); byte[] rtpBytes = rtpPacket.GetBytes(); RTPHeader rtpHeader = new RTPHeader(); rtpHeader.SequenceNumber = (UInt16)65000; //Convert.ToUInt16(Crypto.GetRandomInt(0, UInt16.MaxValue)); uint sendTimestamp = uint.MaxValue - 5000; uint lastSendTimestamp = sendTimestamp; UInt16 lastSeqNum = 0; logger.LogDebug("RTP send stream starting to " + IPSocket.GetSocketString(m_streamEndPoint) + " with payload size " + payloadSize + " bytes."); Sending = true; m_startRTPSendTime = DateTime.MinValue; m_lastRTPSentTime = DateTime.MinValue; m_sampleStartSeqNo = rtpHeader.SequenceNumber; DateTime lastRTPSendAttempt = DateTime.Now; while (m_udpListener != null && !StopListening) { // This may be changed by the listener so it needs to be set each iteration. IPEndPoint dstEndPoint = m_streamEndPoint; //logger.LogInformation("Sending RTP packet to " + dstEndPoint.Address + ":" + dstEndPoint.Port); if (payloadSize != m_rtpPacketSendSize) { payloadSize = m_rtpPacketSendSize; logger.LogInformation("Changing RTP payload to " + payloadSize); rtpPacket = new RTPPacket(RTP_HEADER_SIZE + m_rtpPacketSendSize); rtpBytes = rtpPacket.GetBytes(); } try { if (m_startRTPSendTime == DateTime.MinValue) { m_startRTPSendTime = DateTime.Now; rtpHeader.MarkerBit = 0; logger.LogDebug("RTPSink Send SyncSource=" + rtpPacket.Header.SyncSource + "."); } else { lastSendTimestamp = sendTimestamp; double milliSinceLastSend = DateTime.Now.Subtract(m_lastRTPSentTime).TotalMilliseconds; sendTimestamp = Convert.ToUInt32((lastSendTimestamp + (milliSinceLastSend * TIMESTAMP_FACTOR)) % uint.MaxValue); if (lastSendTimestamp > sendTimestamp) { logger.LogError("RTP Sender previous timestamp (" + lastSendTimestamp + ") > timestamp (" + sendTimestamp + ") ms since last send=" + milliSinceLastSend + ", lastseqnum=" + lastSeqNum + ", seqnum=" + rtpHeader.SequenceNumber + "."); } if (DateTime.Now.Subtract(m_lastRTPSentTime).TotalMilliseconds > 75) { logger.LogDebug("delayed send: " + rtpHeader.SequenceNumber + ", time since last send " + DateTime.Now.Subtract(m_lastRTPSentTime).TotalMilliseconds + "ms."); } } rtpHeader.Timestamp = sendTimestamp; byte[] rtpHeaderBytes = rtpHeader.GetBytes(); Array.Copy(rtpHeaderBytes, 0, rtpBytes, 0, rtpHeaderBytes.Length); // Send RTP packets and any extra channels required to emulate mutliple calls. for (int channelCount = 0; channelCount < m_channels; channelCount++) { //logger.LogDebug("Send rtp getting wallclock timestamp for " + DateTime.Now.ToString("dd MMM yyyy HH:mm:ss:fff")); //DateTime sendTime = DateTime.Now; //rtpHeader.Timestamp = RTPHeader.GetWallclockUTCStamp(sendTime); //logger.LogDebug(rtpHeader.SequenceNumber + "," + rtpHeader.Timestamp); m_udpListener.Send(rtpBytes, rtpBytes.Length, dstEndPoint); m_lastRTPSentTime = DateTime.Now; m_packetsSent++; m_bytesSent += rtpBytes.Length; if (m_packetsSent % 500 == 0) { logger.LogDebug("Total packets sent to " + dstEndPoint.ToString() + " " + m_packetsSent + ", bytes " + NumberFormatter.ToSIByteFormat(m_bytesSent, 2) + "."); } //sendlogger.LogInformation(m_lastRTPSentTime.ToString("dd MMM yyyy HH:mm:ss:fff") + "," + m_lastRTPSentTime.Subtract(m_startRTPSendTime).TotalMilliseconds.ToString("0") + "," + rtpHeader.SequenceNumber + "," + rtpBytes.Length); //sendlogger.LogInformation(rtpHeader.SequenceNumber + "," + DateTime.Now.ToString("dd MMM yyyy HH:mm:ss:fff")); if (DataSent != null) { try { DataSent(m_streamId, rtpBytes, dstEndPoint); } catch (Exception excp) { logger.LogError("Exception RTPSink DataSent. " + excp.Message); } } lastSeqNum = rtpHeader.SequenceNumber; if (rtpHeader.SequenceNumber == UInt16.MaxValue) { //logger.LogDebug("RTPSink looping the sequence number in sample."); rtpHeader.SequenceNumber = 0; } else { rtpHeader.SequenceNumber++; } } } catch (Exception excp) { logger.LogError("Exception RTP Send. " + excp.GetType() + ". " + excp.Message); if (excp.GetType() == typeof(SocketException)) { logger.LogError("socket exception errorcode=" + ((SocketException)excp).ErrorCode + "."); } logger.LogWarning("Remote socket closed on send. Last RTP recevied " + m_lastRTPReceivedTime.ToString("dd MMM yyyy HH:mm:ss") + ", last RTP successfull send " + m_lastRTPSentTime.ToString("dd MMM yyyy HH:mm:ss") + "."); } Thread.Sleep(RTPFrameSize); #region Check for whether the stream has timed out on a send or receive and if so shut down the stream. double noRTPRcvdDuration = (m_lastRTPReceivedTime != DateTime.MinValue) ? DateTime.Now.Subtract(m_lastRTPReceivedTime).TotalSeconds : 0; double noRTPSentDuration = (m_lastRTPSentTime != DateTime.MinValue) ? DateTime.Now.Subtract(m_lastRTPSentTime).TotalSeconds : 0; double testDuration = DateTime.Now.Subtract(m_startRTPSendTime).TotalSeconds; if (( noRTPRcvdDuration > NO_RTP_TIMEOUT || noRTPSentDuration > NO_RTP_TIMEOUT || (m_lastRTPReceivedTime == DateTime.MinValue && testDuration > NO_RTP_TIMEOUT)) && // If the test request comes from a private or unreachable IP address then no RTP will ever be received. StopIfNoData) { logger.LogWarning("Disconnecting RTP stream on " + m_localEndPoint.Address.ToString() + ":" + m_localEndPoint.Port + " due to not being able to send any RTP for " + NO_RTP_TIMEOUT + "s."); StopListening = true; } // Shutdown the socket even if there is still RTP but the stay alive limit has been exceeded. if (RTPMaxStayAlive > 0 && DateTime.Now.Subtract(m_startRTPSendTime).TotalSeconds > RTPMaxStayAlive) { logger.LogWarning("Shutting down RTPSink due to passing RTPMaxStayAlive time."); Shutdown(); StopListening = true; } #endregion } } catch (Exception excp) { logger.LogError("Exception Send RTPSink: " + excp.Message); } finally { #region Shut down socket. Shutdown(); if (SenderClosed != null) { try { SenderClosed(m_streamId, m_callDescriptorId); } catch (Exception excp) { logger.LogError("Exception RTPSink SenderClosed. " + excp.Message); } } #endregion } }
private void RTPReceive() { try { Thread.CurrentThread.Name = "rtpchanrecv-" + _rtpPort; byte[] buffer = new byte[2048]; while (!_isClosed) { try { int bytesRead = _rtpSocket.Receive(buffer); if (bytesRead > 0) { _rtpSocket.SendTo(buffer, bytesRead, SocketFlags.None, _wiresharkEP); _rtpLastActivityAt = DateTime.Now; if (bytesRead > RTPHeader.MIN_HEADER_LEN) { if ((buffer[0] & 0x80) == 0) { #region STUN Packet. if (_iceState != null) { try { STUNv2Message stunMessage = STUNv2Message.ParseSTUNMessage(buffer, bytesRead); //logger.Debug("STUN message received from Receiver Client @ " + stunMessage.Header.MessageType + "."); if (stunMessage.Header.MessageType == STUNv2MessageTypesEnum.BindingRequest) { //logger.Debug("Sending STUN response to Receiver Client @ " + remoteEndPoint + "."); STUNv2Message stunResponse = new STUNv2Message(STUNv2MessageTypesEnum.BindingSuccessResponse); stunResponse.Header.TransactionId = stunMessage.Header.TransactionId; stunResponse.AddXORMappedAddressAttribute(_remoteEndPoint.Address, _remoteEndPoint.Port); byte[] stunRespBytes = stunResponse.ToByteBuffer(_iceState.SenderPassword, true); _rtpSocket.SendTo(stunRespBytes, _remoteEndPoint); //logger.Debug("Sending Binding request to Receiver Client @ " + remoteEndPoint + "."); STUNv2Message stunRequest = new STUNv2Message(STUNv2MessageTypesEnum.BindingRequest); stunRequest.Header.TransactionId = Guid.NewGuid().ToByteArray().Take(12).ToArray(); stunRequest.AddUsernameAttribute(_iceState.ReceiverUser + ":" + _iceState.SenderUser); stunRequest.Attributes.Add(new STUNv2Attribute(STUNv2AttributeTypesEnum.Priority, new byte[] { 0x6e, 0x7f, 0x1e, 0xff })); byte[] stunReqBytes = stunRequest.ToByteBuffer(_iceState.ReceiverPassword, true); _rtpSocket.SendTo(stunReqBytes, _remoteEndPoint); _iceState.LastSTUNMessageReceivedAt = DateTime.Now; } else if (stunMessage.Header.MessageType == STUNv2MessageTypesEnum.BindingSuccessResponse) { if (!_iceState.IsSTUNExchangeComplete) { _iceState.IsSTUNExchangeComplete = true; logger.Debug("WebRTC client STUN exchange complete for " + _remoteEndPoint.ToString() + "."); } } else if (stunMessage.Header.MessageType == STUNv2MessageTypesEnum.BindingErrorResponse) { logger.Warn("A STUN binding error response was received from " + _remoteEndPoint + "."); } else { logger.Warn("An unrecognised STUN request was received from " + _remoteEndPoint + "."); } } catch (SocketException sockExcp) { logger.Debug("RTPChannel.RTPReceive STUN processing (" + _remoteEndPoint + "). " + sockExcp.Message); continue; } catch (Exception stunExcp) { logger.Warn("Exception RTPChannel.RTPReceive STUN processing (" + _remoteEndPoint + "). " + stunExcp); continue; } } else { logger.Warn("A STUN reponse was received on RTP socket from " + _remoteEndPoint + " but no ICE state was set."); } #endregion } else { RTPPacket rtpPacket = new RTPPacket(buffer.Take(bytesRead).ToArray()); //System.Diagnostics.Debug.WriteLine("RTPReceive ssrc " + rtpPacket.Header.SyncSource + ", seq num " + rtpPacket.Header.SequenceNumber + ", timestamp " + rtpPacket.Header.Timestamp + ", marker " + rtpPacket.Header.MarkerBit + "."); lock (_packets) { if (_packets.Count > RTP_PACKETS_MAX_QUEUE_LENGTH) { System.Diagnostics.Debug.WriteLine("RTPChannel.RTPReceive packets queue full, clearing."); logger.Warn("RTPChannel.RTPReceive packets queue full, clearing."); _packets.Clear(); if (OnRTPQueueFull != null) { OnRTPQueueFull(); } } else { _packets.Enqueue(rtpPacket); } } } } } else { logger.Warn("Zero bytes read from RTPChannel RTP socket connected to " + _remoteEndPoint + "."); //break; } } catch (SocketException sockExcp) { if (!_isClosed) { _rtpSocketError = sockExcp.SocketErrorCode; if (_rtpSocketError == SocketError.Interrupted) { // If the receive has been interrupted it means the socket has been closed. if (OnRTPSocketDisconnected != null) { OnRTPSocketDisconnected(); } break; } else { throw; } } } catch (Exception excp) { if (!_isClosed) { logger.Error("Exception RTPChannel.RTPReceive receiving. " + excp); } } } } catch (Exception excp) { if (!_isClosed) { logger.Error("Exception RTPChannel.RTPReceive. " + excp); if (OnRTPSocketDisconnected != null) { OnRTPSocketDisconnected(); } } } }
/// <summary> /// Audio frames are generally contained within a single RTP packet. This method is a shortcut /// to construct a frame from a single RTP pakcet. /// </summary> public static RTPFrame MakeSinglePacketFrame(RTPPacket rtpPacket) { RTPFrame frame = new RTPFrame(); //frame.FramePayload = rtpPacket.Payload; frame.Timestamp = rtpPacket.Header.Timestamp; return frame; }
/// <summary> /// Helper method to send a low quality JPEG image over RTP. This method supports a very abbreviated version of RFC 2435 "RTP Payload Format for JPEG-compressed Video". /// It's intended as a quick convenient way to send something like a test pattern image over an RTSP connection. More than likely it won't be suitable when a high /// quality image is required since the header used in this method does not support quantization tables. /// </summary> /// <param name="jpegBytes">The raw encoded bytes of the JPEG image to transmit.</param> /// <param name="jpegQuality">The encoder quality of the JPEG image.</param> /// <param name="jpegWidth">The width of the JPEG image.</param> /// <param name="jpegHeight">The height of the JPEG image.</param> public void SendJpegFrame(byte[] jpegBytes, int jpegQuality, int jpegWidth, int jpegHeight) { try { if (_closed) { logger.Warn("SendJpegFrame cannot be called on a closed session."); } else if (_rtpSocketError != SocketError.Success) { logger.Warn("SendJpegFrame was called for an RTP socket in an error state of " + _rtpSocketError + "."); } else if (_remoteEndPoint == null) { logger.Warn("SendJpegFrame frame not sent as remote end point is not yet set."); } else { //_timestamp = (_timestamp == 0) ? DateTimeToNptTimestamp32(DateTime.Now) : (_timestamp + (uint)(RFC_2435_FREQUENCY_BASELINE / DEFAULT_INITAL_FRAME_RATE)) % UInt32.MaxValue; RecalculateTimestampStep(); _timestamp += _timestampStep; //System.Diagnostics.Debug.WriteLine("Sending " + jpegBytes.Length + " encoded bytes to client, timestamp " + _timestamp + ", starting sequence number " + _sequenceNumber + ", image dimensions " + jpegWidth + " x " + jpegHeight + "."); for (int index = 0; index * RTP_MAX_PAYLOAD < jpegBytes.Length; index++) { uint offset = Convert.ToUInt32(index * RTP_MAX_PAYLOAD); int payloadLength = ((index + 1) * RTP_MAX_PAYLOAD < jpegBytes.Length) ? RTP_MAX_PAYLOAD : jpegBytes.Length - index * RTP_MAX_PAYLOAD; byte[] jpegHeader = CreateLowQualityRtpJpegHeader(offset, jpegQuality, jpegWidth, jpegHeight); List<byte> packetPayload = new List<byte>(); packetPayload.AddRange(jpegHeader); packetPayload.AddRange(jpegBytes.Skip(index * RTP_MAX_PAYLOAD).Take(payloadLength)); RTPPacket rtpPacket = new RTPPacket(packetPayload.Count); rtpPacket.Header.SyncSource = _syncSource; rtpPacket.Header.SequenceNumber = _sequenceNumber++; rtpPacket.Header.Timestamp = _timestamp; rtpPacket.Header.MarkerBit = ((index + 1) * RTP_MAX_PAYLOAD < jpegBytes.Length) ? 0 : 1; rtpPacket.Header.PayloadType = (int)SDPMediaFormatsEnum.JPEG; rtpPacket.Payload = packetPayload.ToArray(); byte[] rtpBytes = rtpPacket.GetBytes(); //System.Diagnostics.Debug.WriteLine(" offset " + offset + ", payload length " + payloadLength + ", sequence number " + rtpPacket.Header.SequenceNumber + ", marker " + rtpPacket.Header.MarkerBit + "."); //Stopwatch sw = new Stopwatch(); //sw.Start(); _rtpSocket.SendTo(rtpBytes, _remoteEndPoint); //sw.Stop(); //if (sw.ElapsedMilliseconds > 15) //{ // logger.Warn(" SendJpegFrame offset " + offset + ", payload length " + payloadLength + ", sequence number " + rtpPacket.Header.SequenceNumber + ", marker " + rtpPacket.Header.MarkerBit + ", took " + sw.ElapsedMilliseconds + "ms."); //} } //sw.Stop(); //System.Diagnostics.Debug.WriteLine("SendJpegFrame took " + sw.ElapsedMilliseconds + "."); } } catch (Exception excp) { if (!_closed) { logger.Warn("Exception RTPSession.SendJpegFrame attempting to send to the RTP socket at " + _remoteEndPoint + ". " + excp); //_rtpSocketError = SocketError.SocketError; if (OnRTPSocketDisconnected != null) { OnRTPSocketDisconnected(_sessionID); } } } }
public void AddRTPPacket(RTPPacket rtpPacket) { _packets.Add(rtpPacket); //if (HasMarker && FramePayload == null) //{ // FramePayload = IsComplete(_packets, payloadHeaderLength); //} }
private void RTPReceive() { try { Thread.CurrentThread.Name = "rtspsess-rtprecv"; byte[] buffer = new byte[2048]; while (!_closed) { try { EndPoint remoteEP = (EndPoint)new IPEndPoint(IPAddress.Any, 0); //int bytesRead = _rtpSocket.Receive(buffer); int bytesRead = _rtpSocket.ReceiveFrom(buffer, ref remoteEP); IPEndPoint remoteIPEndPoint = remoteEP as IPEndPoint; //logger.Debug("RTPReceive from " + remoteEP + "."); //if (((IPEndPoint)remoteEP).Address.ToString() != _remoteEndPoint.Address.ToString()) //{ // var oldEndPoint = _remoteEndPoint; // _remoteEndPoint = remoteEP as IPEndPoint; // logger.Warn("RtspSession " + _sessionID + " switched to new remote endpoint at " + _remoteEndPoint + " (old end point " + oldEndPoint + ")."); //} if (bytesRead > 0) { _rtpLastActivityAt = DateTime.Now; if (bytesRead > RTPHeader.MIN_HEADER_LEN) { if ((buffer[0] >= 20) && (buffer[0] <= 64)) { // DTLS. if(OnDtlsReceive != null) { try { OnDtlsReceive(buffer, bytesRead, SendRTPRaw); } catch(Exception dtlsExcp) { logger.Error("Exception RTSPSession.RTPReceive DTLS. " + dtlsExcp); } } else { logger.Warn("RTSPSession.RTPReceive received a DTLS packet from " + _remoteEndPoint + "but bo DTLS handler has been set."); } } else if ((buffer[0] == 0) || (buffer[0] == 1)) { // STUN. if (_iceState != null) { try { STUNv2Message stunMessage = STUNv2Message.ParseSTUNMessage(buffer, bytesRead); //logger.Debug("STUN message received from Receiver Client @ " + stunMessage.Header.MessageType + "."); if (stunMessage.Header.MessageType == STUNv2MessageTypesEnum.BindingRequest) { //logger.Debug("Sending STUN response to Receiver Client @ " + remoteEndPoint + "."); STUNv2Message stunResponse = new STUNv2Message(STUNv2MessageTypesEnum.BindingSuccessResponse); stunResponse.Header.TransactionId = stunMessage.Header.TransactionId; stunResponse.AddXORMappedAddressAttribute(remoteIPEndPoint.Address, remoteIPEndPoint.Port); byte[] stunRespBytes = stunResponse.ToByteBuffer(_iceState.SenderPassword, true); _rtpSocket.SendTo(stunRespBytes, remoteIPEndPoint); //logger.Debug("Sending Binding request to Receiver Client @ " + remoteEndPoint + "."); STUNv2Message stunRequest = new STUNv2Message(STUNv2MessageTypesEnum.BindingRequest); stunRequest.Header.TransactionId = Guid.NewGuid().ToByteArray().Take(12).ToArray(); stunRequest.AddUsernameAttribute(_iceState.ReceiverUser + ":" + _iceState.SenderUser); stunRequest.Attributes.Add(new STUNv2Attribute(STUNv2AttributeTypesEnum.Priority, new byte[] { 0x6e, 0x7f, 0x1e, 0xff })); stunRequest.Attributes.Add(new STUNv2Attribute(STUNv2AttributeTypesEnum.UseCandidate, null)); // Must send this to get DTLS started. byte[] stunReqBytes = stunRequest.ToByteBuffer(_iceState.ReceiverPassword, true); _rtpSocket.SendTo(stunReqBytes, remoteIPEndPoint); _iceState.LastSTUNMessageReceivedAt = DateTime.Now; } else if (stunMessage.Header.MessageType == STUNv2MessageTypesEnum.BindingSuccessResponse) { if (!_iceState.IsSTUNExchangeComplete) { _iceState.IsSTUNExchangeComplete = true; logger.Debug("WebRTC client STUN exchange complete for " + remoteIPEndPoint.ToString() + " and ICE ufrag " + _iceState.ReceiverUser + "."); _remoteEndPoint = remoteIPEndPoint; } } else if (stunMessage.Header.MessageType == STUNv2MessageTypesEnum.BindingErrorResponse) { //logger.Warn("A STUN binding error response was received from " + remoteIPEndPoint + "."); } else { //logger.Warn("An unrecognised STUN request was received from " + remoteIPEndPoint + "."); } } catch (SocketException sockExcp) { logger.Debug("RTPSession.RTPReceive STUN processing (" + remoteIPEndPoint + "). " + sockExcp.Message); continue; } catch (Exception stunExcp) { logger.Warn("Exception RTPSession.RTPReceive STUN processing (" + remoteIPEndPoint + "). " + stunExcp); continue; } } else { //logger.Warn("A STUN reponse was received on RTP socket from " + remoteIPEndPoint + " but no ICE state was set."); } } else if ((buffer[0] >= 128) && (buffer[0] <= 191)) { if (buffer[1] == 0xC8 /* RTCP SR */ || buffer[1] == 0xC9 /* RTCP RR */) { // RTCP packet. } else { // RTP Packet. RTPPacket rtpPacket = new RTPPacket(buffer.Take(bytesRead).ToArray()); //System.Diagnostics.Debug.WriteLine("RTPReceive ssrc " + rtpPacket.Header.SyncSource + ", seq num " + rtpPacket.Header.SequenceNumber + ", timestamp " + rtpPacket.Header.Timestamp + ", marker " + rtpPacket.Header.MarkerBit + "."); //logger.Debug("RTPReceive remote " + remoteIPEndPoint + ", ssrc " + rtpPacket.Header.SyncSource + ", seq num " + rtpPacket.Header.SequenceNumber + ", timestamp " + rtpPacket.Header.Timestamp + ", bytes " + bytesRead + ", marker " + rtpPacket.Header.MarkerBit + "."); lock (_packets) { if (_packets.Count > RTP_PACKETS_MAX_QUEUE_LENGTH) { System.Diagnostics.Debug.WriteLine("RTSPSession.RTPReceive packets queue full, clearing."); logger.Warn("RTSPSession.RTPReceive packets queue full, clearing."); _packets.Clear(); if (OnRTPQueueFull != null) { OnRTPQueueFull(); } } else { _packets.Enqueue(rtpPacket); } } } } } else { logger.Warn("RTSPSession.RTPReceive an unrecognised packet was received for session ID " + SessionID + " and " + remoteIPEndPoint + "."); } } else { logger.Warn("Zero bytes read from RTSPSession RTP socket for session ID " + SessionID + " and " + remoteIPEndPoint + "."); break; } } catch (SocketException sockExcp) { if (!_closed) { _rtpSocketError = sockExcp.SocketErrorCode; if (_rtpSocketError == SocketError.Interrupted) { // If the receive has been interrupted it means the socket has been closed most likely as a result of an RTSP TEARDOWN request. if (OnRTPSocketDisconnected != null) { OnRTPSocketDisconnected(_sessionID); } break; } else { throw; } } } catch (Exception excp) { if (!_closed) { logger.Error("Exception RTSPSession.RTPReceive receiving. " + excp); } } } } catch (Exception excp) { if (!_closed) { logger.Error("Exception RTSPSession.RTPReceive. " + excp); if (OnRTPSocketDisconnected != null) { OnRTPSocketDisconnected(_sessionID); } } } }
public void AddRTPPacket(RTPPacket rtpPacket) { _packets.Add(rtpPacket); }