void video_source_ReceivedYUVFrame(uint timestamp_ms, int width, int height, byte[] yuv_data) { // Check if there are any clients. Only run the encoding if someone is connected // Could exand this to check if someone is connected and in PLAY mode int current_rtp_count = rtp_list.Count; if (current_rtp_count == 0) { return; } // Take the YUV image and encode it into a H264 NAL // This returns a NAL with no headers (no 00 00 00 01 header and no 32 bit sizes) Console.WriteLine("Compressing video at time(ms) " + timestamp_ms + " " + current_rtp_count + " RTSP clients connected"); byte[] raw_nal = h264_encoder.CompressFrame(yuv_data); UInt32 ts = timestamp_ms * 90; // 90kHz clock // The H264 Payload could be sent as one large RTP packet (assuming the receiver can handle it) // or as a Fragmented Data, split over several RTP packets with the same Timestamp. bool fragmenting = false; if (raw_nal.Length > 1400) { fragmenting = true; } // Build a list of 1 or more RTP packets List <byte[]> rtp_packets = new List <byte[]>(); if (fragmenting == false) { // Put the whole NAL into one RTP packet. // Note some receivers will have maximum buffers and be unable to handle large RTP packets. // Also with RTP over RTSP there is a limit of 65535 bytes for the RTP packet. byte[] rtp_packet = new byte[12 + raw_nal.Length]; // 12 is header size when there are no CSRCs or extensions // Create an single RTP fragment // RTP Packet Header // 0 - Version, P, X, CC, M, PT and Sequence Number //32 - Timestamp. H264 uses a 90kHz clock //64 - SSRC //96 - CSRCs (optional) //nn - Extension ID and Length //nn - Extension header int rtp_version = 2; int rtp_padding = 0; int rtp_extension = 0; int rtp_csrc_count = 0; int rtp_marker = 1; int rtp_payload_type = 96; RTPPacketUtil.WriteHeader(rtp_packet, rtp_version, rtp_padding, rtp_extension, rtp_csrc_count, rtp_marker, rtp_payload_type); UInt32 empty_sequence_id = 0; RTPPacketUtil.WriteSequenceNumber(rtp_packet, empty_sequence_id); Console.WriteLine("adjusted TS at 90khz=" + ts); RTPPacketUtil.WriteTS(rtp_packet, ts); UInt32 empty_ssrc = 0; RTPPacketUtil.WriteSSRC(rtp_packet, empty_ssrc); // Now append the raw NAL System.Array.Copy(raw_nal, 0, rtp_packet, 12, raw_nal.Length); rtp_packets.Add(rtp_packet); } else { int data_remaining = raw_nal.Length; int nal_pointer = 0; int start_bit = 1; int end_bit = 0; // consume first byte of the raw_nal. It is used in the FU header byte first_byte = raw_nal[0]; nal_pointer++; data_remaining--; while (data_remaining > 0) { int payload_size = Math.Min(1400, data_remaining); if (data_remaining - payload_size == 0) { end_bit = 1; } byte[] rtp_packet = new byte[12 + 2 + payload_size]; // 12 is header size. 2 bytes for FU-A header. Then payload // RTP Packet Header // 0 - Version, P, X, CC, M, PT and Sequence Number //32 - Timestamp. H264 uses a 90kHz clock //64 - SSRC //96 - CSRCs (optional) //nn - Extension ID and Length //nn - Extension header int rtp_version = 2; int rtp_padding = 0; int rtp_extension = 0; int rtp_csrc_count = 0; int rtp_marker = (end_bit == 1 ? 1 : 0); // Marker set to 1 on last packet int rtp_payload_type = 96; RTPPacketUtil.WriteHeader(rtp_packet, rtp_version, rtp_padding, rtp_extension, rtp_csrc_count, rtp_marker, rtp_payload_type); UInt32 empty_sequence_id = 0; RTPPacketUtil.WriteSequenceNumber(rtp_packet, empty_sequence_id); Console.WriteLine("adjusted TS at 90khz=" + ts); RTPPacketUtil.WriteTS(rtp_packet, ts); UInt32 empty_ssrc = 0; RTPPacketUtil.WriteSSRC(rtp_packet, empty_ssrc); // Now append the Fragmentation Header (with Start and End marker) and part of the raw_nal byte f_bit = 0; byte nri = (byte)((first_byte >> 5) & 0x03); // Part of the 1st byte of the Raw NAL (NAL Reference ID) byte type = 28; // FU-A Fragmentation rtp_packet[12] = (byte)((f_bit << 7) + (nri << 5) + type); rtp_packet[13] = (byte)((start_bit << 7) + (end_bit << 6) + (0 << 5) + (first_byte & 0x1F)); System.Array.Copy(raw_nal, nal_pointer, rtp_packet, 14, payload_size); nal_pointer = nal_pointer + payload_size; data_remaining = data_remaining - payload_size; rtp_packets.Add(rtp_packet); start_bit = 0; } } lock (rtp_list) { // Go through each RTSP session and output the NAL foreach (RTPSession session in rtp_list.ToArray()) // ToArray makes a temp copy of the list. // This lets us delete items in the foreach { // Only process Sessions in Play Mode if (session.play == false) { continue; } // There could be more than 1 RTP packet (if the data is fragmented) Boolean write_error = false; foreach (byte[] rtp_packet in rtp_packets) { // Add the specific data for each transmission RTPPacketUtil.WriteSequenceNumber(rtp_packet, session.sequence_number); session.sequence_number++; // Add the specific SSRC for each transmission RTPPacketUtil.WriteSSRC(rtp_packet, session.ssrc); // Send as RTP over RTSP if (session.transport_reply.LowerTransport == Rtsp.Messages.RtspTransport.LowerTransportType.TCP) { int video_channel = session.transport_reply.Interleaved.First; // second is for RTCP status messages) object state = new object(); try { // send the whole NAL. With RTP over RTSP we do not need to Fragment the NAL (as we do with UDP packets or Multicast) //session.listener.BeginSendData(video_channel, rtp_packet, new AsyncCallback(session.listener.EndSendData), state); session.listener.SendData(video_channel, rtp_packet); } catch { Console.WriteLine("Error writing to listener " + session.listener.RemoteAdress); write_error = true; break; // exit out of foreach loop } } // TODO. Add UDP and Multicast } if (write_error) { session.play = false; // stop sending data session.listener.Dispose(); rtp_list.Remove(session); // remove the session. It is dead Console.WriteLine(rtp_list.Count + " remaining sessions open"); } } } }
// The 'Camera' (YUV TestCard) has generated a YUV image. // If there are RTSP clients connected then Compress the Video Frame (with H264) and send it to the client void video_source_ReceivedYUVFrame(uint timestamp_ms, int width, int height, byte[] yuv_data) { DateTime now = DateTime.UtcNow; int current_rtp_play_count = 0; int current_rtp_count = 0; int timeout_in_seconds = 70; // must have a RTSP message every 70 seconds or we will close the connection lock (rtsp_list) { current_rtp_count = rtsp_list.Count; foreach (RTSPConnection connection in rtsp_list.ToArray()) // Convert to Array to allow us to delete from rtsp_list // RTSP Timeout (for UDP) { if ((connection.video_client_transport != null) && (connection.video_client_transport.LowerTransport == Rtsp.Messages.RtspTransport.LowerTransportType.UDP) && ((now - connection.time_since_last_rtsp_keepalive).TotalSeconds > timeout_in_seconds)) { connection.play = false; Console.WriteLine("Removing session " + connection.video_session_id + " due to TIMEOUT"); connection.play = false; // stop sending data if (connection.video_udp_pair != null) { connection.video_udp_pair.Stop(); connection.video_udp_pair = null; } connection.listener.Dispose(); rtsp_list.Remove(connection); continue; } else if (connection.play) { current_rtp_play_count++; } } } // Take the YUV image and encode it into a H264 NAL // This returns a NAL with no headers (no 00 00 00 01 header and no 32 bit sizes) Console.WriteLine(current_rtp_count + " RTSP clients connected. " + current_rtp_play_count + " RTSP clients in PLAY mode"); if (current_rtp_play_count == 0) { return; } // Compress the video (YUV to H264) byte[] raw_nal = h264_encoder.CompressFrame(yuv_data); UInt32 rtp_timestamp = timestamp_ms * 90; // 90kHz clock // The H264 Payload could be sent as one large RTP packet (assuming the receiver can handle it) // or as a Fragmented Data, split over several RTP packets with the same Timestamp. bool fragmenting = false; int packetMTU = 65000; //1400; if (raw_nal.Length > packetMTU) { fragmenting = true; } // Build a list of 1 or more RTP packets List <byte[]> rtp_packets = new List <byte[]>(); if (fragmenting == false) { // Put the whole NAL into one RTP packet. // Note some receivers will have maximum buffers and be unable to handle large RTP packets. // Also with RTP over RTSP there is a limit of 65535 bytes for the RTP packet. byte[] rtp_packet = new byte[12 + raw_nal.Length]; // 12 is header size when there are no CSRCs or extensions // Create an single RTP fragment // RTP Packet Header // 0 - Version, P, X, CC, M, PT and Sequence Number //32 - Timestamp. H264 uses a 90kHz clock //64 - SSRC //96 - CSRCs (optional) //nn - Extension ID and Length //nn - Extension header int rtp_version = 2; int rtp_padding = 0; int rtp_extension = 0; int rtp_csrc_count = 0; int rtp_marker = 1; int rtp_payload_type = 96; RTPPacketUtil.WriteHeader(rtp_packet, rtp_version, rtp_padding, rtp_extension, rtp_csrc_count, rtp_marker, rtp_payload_type); UInt32 empty_sequence_id = 0; RTPPacketUtil.WriteSequenceNumber(rtp_packet, empty_sequence_id); RTPPacketUtil.WriteTS(rtp_packet, rtp_timestamp); UInt32 empty_ssrc = 0; RTPPacketUtil.WriteSSRC(rtp_packet, empty_ssrc); // Now append the raw NAL System.Array.Copy(raw_nal, 0, rtp_packet, 12, raw_nal.Length); rtp_packets.Add(rtp_packet); } else { int data_remaining = raw_nal.Length; int nal_pointer = 0; int start_bit = 1; int end_bit = 0; // consume first byte of the raw_nal. It is used in the FU header byte first_byte = raw_nal[0]; nal_pointer++; data_remaining--; while (data_remaining > 0) { int payload_size = Math.Min(packetMTU, data_remaining); if (data_remaining - payload_size == 0) { end_bit = 1; } byte[] rtp_packet = new byte[12 + 2 + payload_size]; // 12 is header size. 2 bytes for FU-A header. Then payload // RTP Packet Header // 0 - Version, P, X, CC, M, PT and Sequence Number //32 - Timestamp. H264 uses a 90kHz clock //64 - SSRC //96 - CSRCs (optional) //nn - Extension ID and Length //nn - Extension header int rtp_version = 2; int rtp_padding = 0; int rtp_extension = 0; int rtp_csrc_count = 0; int rtp_marker = (end_bit == 1 ? 1 : 0); // Marker set to 1 on last packet int rtp_payload_type = 96; RTPPacketUtil.WriteHeader(rtp_packet, rtp_version, rtp_padding, rtp_extension, rtp_csrc_count, rtp_marker, rtp_payload_type); UInt32 empty_sequence_id = 0; RTPPacketUtil.WriteSequenceNumber(rtp_packet, empty_sequence_id); RTPPacketUtil.WriteTS(rtp_packet, rtp_timestamp); UInt32 empty_ssrc = 0; RTPPacketUtil.WriteSSRC(rtp_packet, empty_ssrc); // Now append the Fragmentation Header (with Start and End marker) and part of the raw_nal byte f_bit = 0; byte nri = (byte)((first_byte >> 5) & 0x03); // Part of the 1st byte of the Raw NAL (NAL Reference ID) byte type = 28; // FU-A Fragmentation rtp_packet[12] = (byte)((f_bit << 7) + (nri << 5) + type); rtp_packet[13] = (byte)((start_bit << 7) + (end_bit << 6) + (0 << 5) + (first_byte & 0x1F)); System.Array.Copy(raw_nal, nal_pointer, rtp_packet, 14, payload_size); nal_pointer = nal_pointer + payload_size; data_remaining = data_remaining - payload_size; rtp_packets.Add(rtp_packet); start_bit = 0; } } lock (rtsp_list) { // Go through each RTSP connection and output the NAL on the Video Session foreach (RTSPConnection connection in rtsp_list.ToArray()) // ToArray makes a temp copy of the list. // This lets us delete items in the foreach // eg when there is Write Error { // Only process Sessions in Play Mode if (connection.play == false) { continue; } String connection_type = ""; if (connection.video_client_transport.LowerTransport == Rtsp.Messages.RtspTransport.LowerTransportType.TCP) { connection_type = "TCP"; } if (connection.video_client_transport.LowerTransport == Rtsp.Messages.RtspTransport.LowerTransportType.UDP && connection.video_client_transport.IsMulticast == false) { connection_type = "UDP"; } if (connection.video_client_transport.LowerTransport == Rtsp.Messages.RtspTransport.LowerTransportType.UDP && connection.video_client_transport.IsMulticast == true) { connection_type = "Multicast"; } Console.WriteLine("Sending video session " + connection.video_session_id + " " + connection_type + " Timestamp(ms)=" + timestamp_ms + ". RTP timestamp=" + rtp_timestamp + ". Sequence=" + connection.video_sequence_number); // There could be more than 1 RTP packet (if the data is fragmented) Boolean write_error = false; foreach (byte[] rtp_packet in rtp_packets) { // Add the specific data for each transmission RTPPacketUtil.WriteSequenceNumber(rtp_packet, connection.video_sequence_number); connection.video_sequence_number++; // Add the specific SSRC for each transmission RTPPacketUtil.WriteSSRC(rtp_packet, connection.ssrc); // Send as RTP over RTSP (Interleaved) if (connection.video_transport_reply.LowerTransport == Rtsp.Messages.RtspTransport.LowerTransportType.TCP) { int video_channel = connection.video_transport_reply.Interleaved.First; // second is for RTCP status messages) object state = new object(); try { // send the whole NAL. With RTP over RTSP we do not need to Fragment the NAL (as we do with UDP packets or Multicast) //session.listener.BeginSendData(video_channel, rtp_packet, new AsyncCallback(session.listener.EndSendData), state); connection.listener.SendData(video_channel, rtp_packet); } catch { Console.WriteLine("Error writing to listener " + connection.listener.RemoteAdress); write_error = true; break; // exit out of foreach loop } } // Send as RTP over UDP if (connection.video_transport_reply.LowerTransport == Rtsp.Messages.RtspTransport.LowerTransportType.UDP && connection.video_transport_reply.IsMulticast == false) { try { // send the whole NAL. ** We could fragment the RTP packet into smaller chuncks that fit within the MTU // Send to the IP address of the Client // Send to the UDP Port the Client gave us in the SETUP command connection.video_udp_pair.Write_To_Data_Port(rtp_packet, connection.client_hostname, connection.video_client_transport.ClientPort.First); } catch (Exception e) { Console.WriteLine("UDP Write Exception " + e.ToString()); Console.WriteLine("Error writing to listener " + connection.listener.RemoteAdress); write_error = true; break; // exit out of foreach loop } } // TODO. Add Multicast } if (write_error) { Console.WriteLine("Removing session " + connection.video_session_id + " due to write error"); connection.play = false; // stop sending data if (connection.video_udp_pair != null) { connection.video_udp_pair.Stop(); connection.video_udp_pair = null; } connection.listener.Dispose(); rtsp_list.Remove(connection); // remove the session. It is dead } } } }
private void Nvr_StreamFrameReceived(StreamFrame obj) { if (obj.StreamId != streamId) { return; } if (obj.FrameType != FrameType.Video) { return; } Console.WriteLine(obj.Time); Console.WriteLine((obj.Time & 0xFFFFFFFF)); if (obj.KeyFrame) { nvr.StreamChange(streamId); // nvr.StreamStop(streamId); // nvr.StreamStart(0); // ++streamId; } // // } // The 'Camera' (YUV TestCard) has generated a YUV image. // If there are RTSP clients connected then Compress the Video Frame (with H264) and send it to the client // void video_source_ReceivedYUVFrame(uint timestamp_ms, int width, int height, byte[] yuv_data) // { DateTime now = DateTime.UtcNow; int current_rtp_play_count = 0; int current_rtp_count = 0; int timeout_in_seconds = 70; // must have a RTSP message every 70 seconds or we will close the connection lock (rtsp_list) { current_rtp_count = rtsp_list.Count; foreach (RTSPConnection connection in rtsp_list.ToArray()) { // Convert to Array to allow us to delete from rtsp_list // RTSP Timeout (clients receiving RTP video over the RTSP session // do not need to send a keepalive (so we check for Socket write errors) bool sending_rtp_via_tcp = false; if ((connection.video_client_transport != null) && (connection.video_client_transport.LowerTransport == Rtsp.Messages.RtspTransport.LowerTransportType.TCP)) { sending_rtp_via_tcp = true; } if (sending_rtp_via_tcp == false && ((now - connection.time_since_last_rtsp_keepalive).TotalSeconds > timeout_in_seconds)) { Console.WriteLine("Removing session " + connection.video_session_id + " due to TIMEOUT"); connection.play = false; // stop sending data if (connection.video_udp_pair != null) { connection.video_udp_pair.Stop(); connection.video_udp_pair = null; } connection.listener.Dispose(); rtsp_list.Remove(connection); continue; } else if (connection.play) { current_rtp_play_count++; } } } UpdateClients(); // Take the YUV image and encode it into a H264 NAL // This returns a NAL with no headers (no 00 00 00 01 header and no 32 bit sizes) Console.WriteLine(current_rtp_count + " RTSP clients connected. " + current_rtp_play_count + " RTSP clients in PLAY mode"); if (current_rtp_play_count == 0) { return; } byte[] raw_video_nal = obj.Data; bool isKeyframe = obj.KeyFrame; List <byte[]> nal_array = new List <byte[]>(); // We may want to add the SPS and PPS to the H264 stream as in-band data. // This may be of use if the client did not parse the SPS/PPS in the SDP // or if the H264 encoder changes properties (eg a new resolution or framerate which // gives a new SPS or PPS). // Also looking towards H265, the VPS/SPS/PPS do not need to be in the SDP so would be added here. #if false Boolean add_sps_pps_to_keyframe = true; if (add_sps_pps_to_keyframe && isKeyframe) { nal_array.Add(raw_sps); nal_array.Add(raw_pps); } #endif // add the rest of the NALs nal_array.Add(raw_video_nal); /* * uint timestamp_ms = (uint)(obj.Time / 1000); // CHECK THIS * Console.WriteLine("timestamp_ms: {0}", timestamp_ms); * UInt32 rtp_timestamp = timestamp_ms * 90; // 90kHz clock */ uint rtp_timestamp = (uint)((obj.Time & 0xFFFFFFFF) * 90 / 1000); if (firstStamp == 0) { firstStamp = rtp_timestamp; } rtp_timestamp -= firstStamp; if (rtp_timestamp - lastStamp > 90000) { uint override_timestamp = lastStamp + 6000; firstStamp = (uint)((obj.Time & 0xFFFFFFFF) * 90 / 1000) - override_timestamp; rtp_timestamp = override_timestamp; } lastStamp = rtp_timestamp; uint timestamp_ms = rtp_timestamp / 90; // Build a list of 1 or more RTP packets // The last packet will have the M bit set to '1' List <byte[]> rtp_packets = new List <byte[]>(); for (int x = 0; x < nal_array.Count; x++) { byte[] raw_nal = nal_array[x]; bool last_nal = false; if (x == nal_array.Count - 1) { last_nal = true; // last NAL in our nal_array } // The H264 Payload could be sent as one large RTP packet (assuming the receiver can handle it) // or as a Fragmented Data, split over several RTP packets with the same Timestamp. bool fragmenting = false; int packetMTU = 1400; //65500; // if (raw_nal.Length > packetMTU) { fragmenting = true; } if (fragmenting == false) { // Put the whole NAL into one RTP packet. // Note some receivers will have maximum buffers and be unable to handle large RTP packets. // Also with RTP over RTSP there is a limit of 65535 bytes for the RTP packet. byte[] rtp_packet = new byte[12 + raw_nal.Length]; // 12 is header size when there are no CSRCs or extensions // Create an single RTP fragment // RTP Packet Header // 0 - Version, P, X, CC, M, PT and Sequence Number //32 - Timestamp. H264 uses a 90kHz clock //64 - SSRC //96 - CSRCs (optional) //nn - Extension ID and Length //nn - Extension header int rtp_version = 2; int rtp_padding = 0; int rtp_extension = 0; int rtp_csrc_count = 0; int rtp_marker = (last_nal == true ? 1 : 0); // set to 1 if the last NAL in the array int rtp_payload_type = 96; RTPPacketUtil.WriteHeader(rtp_packet, rtp_version, rtp_padding, rtp_extension, rtp_csrc_count, rtp_marker, rtp_payload_type); uint empty_sequence_id = 0; RTPPacketUtil.WriteSequenceNumber(rtp_packet, empty_sequence_id); RTPPacketUtil.WriteTS(rtp_packet, rtp_timestamp); uint empty_ssrc = 0; RTPPacketUtil.WriteSSRC(rtp_packet, empty_ssrc); // Now append the raw NAL System.Array.Copy(raw_nal, 0, rtp_packet, 12, raw_nal.Length); rtp_packets.Add(rtp_packet); } else { int data_remaining = raw_nal.Length; int nal_pointer = 0; int start_bit = 1; int end_bit = 0; // consume first byte of the raw_nal. It is used in the FU header byte first_byte = raw_nal[0]; nal_pointer++; data_remaining--; while (data_remaining > 0) { int payload_size = Math.Min(packetMTU, data_remaining); if (data_remaining - payload_size == 0) { end_bit = 1; } byte[] rtp_packet = new byte[12 + 2 + payload_size]; // 12 is header size. 2 bytes for FU-A header. Then payload // RTP Packet Header // 0 - Version, P, X, CC, M, PT and Sequence Number //32 - Timestamp. H264 uses a 90kHz clock //64 - SSRC //96 - CSRCs (optional) //nn - Extension ID and Length //nn - Extension header int rtp_version = 2; int rtp_padding = 0; int rtp_extension = 0; int rtp_csrc_count = 0; int rtp_marker = (last_nal == true ? 1 : 0); // Marker set to 1 on last packet int rtp_payload_type = 96; RTPPacketUtil.WriteHeader(rtp_packet, rtp_version, rtp_padding, rtp_extension, rtp_csrc_count, rtp_marker, rtp_payload_type); uint empty_sequence_id = 0; RTPPacketUtil.WriteSequenceNumber(rtp_packet, empty_sequence_id); RTPPacketUtil.WriteTS(rtp_packet, rtp_timestamp); uint empty_ssrc = 0; RTPPacketUtil.WriteSSRC(rtp_packet, empty_ssrc); // Now append the Fragmentation Header (with Start and End marker) and part of the raw_nal byte f_bit = 0; byte nri = (byte)((first_byte >> 5) & 0x03); // Part of the 1st byte of the Raw NAL (NAL Reference ID) byte type = 28; // FU-A Fragmentation rtp_packet[12] = (byte)((f_bit << 7) + (nri << 5) + type); rtp_packet[13] = (byte)((start_bit << 7) + (end_bit << 6) + (0 << 5) + (first_byte & 0x1F)); System.Array.Copy(raw_nal, nal_pointer, rtp_packet, 14, payload_size); nal_pointer = nal_pointer + payload_size; data_remaining = data_remaining - payload_size; rtp_packets.Add(rtp_packet); start_bit = 0; } } } lock (rtsp_list) { // Go through each RTSP connection and output the NAL on the Video Session foreach (RTSPConnection connection in rtsp_list.ToArray()) // ToArray makes a temp copy of the list. // This lets us delete items in the foreach // eg when there is Write Error { // Only process Sessions in Play Mode if (connection.play == false) { continue; } string connection_type = ""; if (connection.video_client_transport.LowerTransport == Rtsp.Messages.RtspTransport.LowerTransportType.TCP) { connection_type = "TCP"; } if (connection.video_client_transport.LowerTransport == Rtsp.Messages.RtspTransport.LowerTransportType.UDP && connection.video_client_transport.IsMulticast == false) { connection_type = "UDP"; } if (connection.video_client_transport.LowerTransport == Rtsp.Messages.RtspTransport.LowerTransportType.UDP && connection.video_client_transport.IsMulticast == true) { connection_type = "Multicast"; } Console.WriteLine("Sending video session " + connection.video_session_id + " " + connection_type + " Timestamp(ms)=" + timestamp_ms + ". RTP timestamp=" + rtp_timestamp + ". Sequence=" + connection.video_sequence_number); // There could be more than 1 RTP packet (if the data is fragmented) bool write_error = false; foreach (byte[] rtp_packet in rtp_packets) { // Add the specific data for each transmission RTPPacketUtil.WriteSequenceNumber(rtp_packet, connection.video_sequence_number); connection.video_sequence_number++; // Add the specific SSRC for each transmission RTPPacketUtil.WriteSSRC(rtp_packet, connection.ssrc); // Send as RTP over RTSP (Interleaved) if (connection.video_transport_reply.LowerTransport == Rtsp.Messages.RtspTransport.LowerTransportType.TCP) { int video_channel = connection.video_transport_reply.Interleaved.First; // second is for RTCP status messages) object state = new object(); try { // send the whole NAL. With RTP over RTSP we do not need to Fragment the NAL (as we do with UDP packets or Multicast) //session.listener.BeginSendData(video_channel, rtp_packet, new AsyncCallback(session.listener.EndSendData), state); connection.listener.SendData(video_channel, rtp_packet); } catch { Console.WriteLine("Error writing to listener " + connection.listener.RemoteAdress); write_error = true; break; // exit out of foreach loop } } // Send as RTP over UDP if (connection.video_transport_reply.LowerTransport == Rtsp.Messages.RtspTransport.LowerTransportType.UDP && connection.video_transport_reply.IsMulticast == false) { try { // send the whole NAL. ** We could fragment the RTP packet into smaller chuncks that fit within the MTU // Send to the IP address of the Client // Send to the UDP Port the Client gave us in the SETUP command connection.video_udp_pair.Write_To_Data_Port(rtp_packet, connection.client_hostname, connection.video_client_transport.ClientPort.First); } catch (Exception e) { Console.WriteLine("UDP Write Exception " + e.ToString()); Console.WriteLine("Error writing to listener " + connection.listener.RemoteAdress); write_error = true; break; // exit out of foreach loop } } // TODO. Add Multicast } if (write_error) { Console.WriteLine("Removing session " + connection.video_session_id + " due to write error"); connection.play = false; // stop sending data if (connection.video_udp_pair != null) { connection.video_udp_pair.Stop(); connection.video_udp_pair = null; } connection.listener.Dispose(); rtsp_list.Remove(connection); // remove the session. It is dead } } } UpdateClients(); }