private static byte[] PacketizeSingleNAL(Span <byte> raw_nal, UInt32 rtp_timestamp) { // 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[RTPPacketUtil.HeaderLength + raw_nal.Length]; // 12 is header size when there are no CSRCs or extensions // Create an single RTP fragment bool rtp_marker = (raw_nal[0] & 0x1F) <= 5; 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 raw_nal.CopyTo(new Span <byte>(rtp_packet, 12, raw_nal.Length)); return(rtp_packet); }
private static void PacketizeNAL_FUA(ref List <byte[]> rtp_packets, Span <byte> raw_nal, UInt32 rtp_timestamp) { 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]; raw_nal = raw_nal.Slice(1); while (raw_nal.Length > 0) { int payload_size = Math.Min(RTPPacketUtil.MaxPayloadSize - 2, raw_nal.Length); if (raw_nal.Length - payload_size == 0) { end_bit = 1; } //for FU-A the marker is set when this is the last RTP packet bool rtp_marker = end_bit == 1; byte[] rtp_packet = new byte[RTPPacketUtil.HeaderLength + 2 + payload_size]; // 2 bytes for FU-A header. 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)); raw_nal.Slice(0, payload_size).CopyTo(new Span <byte>(rtp_packet, 14, payload_size)); raw_nal = raw_nal.Slice(payload_size); rtp_packets.Add(rtp_packet); start_bit = 0; } }
//TODO: reimplement these using Memory and Span<byte> public static List <byte[]> PacketizeSamples(Memory <byte> samples, ulong tsMs) { List <byte[]> rtp_packets = new List <byte[]>(); Span <byte> smp = samples.Span; double diffTs = 0; while (smp.Length > 0) { int dataLen = smp.Length > RTPPacketUtil.MaxPayloadSize ? RTPPacketUtil.MaxPayloadSize : smp.Length; byte[] packet = new byte[dataLen + RTPPacketUtil.HeaderLength]; Span <byte> packetData = new Span <byte>(packet, RTPPacketUtil.HeaderLength, dataLen); for (int i = 0; i < dataLen; i += 2) { packetData[i] = smp[i + 1]; packetData[i + 1] = smp[i]; } RTPPacketUtil.WriteHeader(packet, rtp_version, rtp_padding, rtp_extension, rtp_csrc_count, false, rtp_payload_type); UInt32 empty_sequence_id = 0; RTPPacketUtil.WriteSequenceNumber(packet, empty_sequence_id); RTPPacketUtil.WriteTS(packet, (UInt32)((tsMs + diffTs) * 48)); UInt32 empty_ssrc = 0; RTPPacketUtil.WriteSSRC(packet, empty_ssrc); rtp_packets.Add(packet); smp = smp.Slice(dataLen); diffTs += AudiolenRtpPackeMs; } return(rtp_packets); }
void PushRtspData(StreamKind stream, List <byte[]> rtp_packets, ulong tsMsec) { int s = (int)stream; 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; } #if DEBUG && LOG String connection_type = ""; if (connection[s].client_transport.LowerTransport == Rtsp.Messages.RtspTransport.LowerTransportType.TCP) { connection_type = "TCP"; } if (connection[s].client_transport.LowerTransport == Rtsp.Messages.RtspTransport.LowerTransportType.UDP && connection[s].client_transport.IsMulticast == false) { connection_type = "UDP"; } if (connection[s].client_transport.LowerTransport == Rtsp.Messages.RtspTransport.LowerTransportType.UDP && connection[s].client_transport.IsMulticast == true) { connection_type = "Multicast"; } Console.WriteLine($"[{stream}] Sending video session {connection[s].session_id} {connection_type} Timestamp(ms)={tsMsec} Sequence={connection[s].sequence_number}"); #endif // 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[s].sequence_number); connection[s].sequence_number++; // Add the specific SSRC for each transmission RTPPacketUtil.WriteSSRC(rtp_packet, connection.ssrc); //if (stream == StreamKind.Video) bin.Write(rtp_packet); // Send as RTP over RTSP (Interleaved) if (connection[s].transport_reply.LowerTransport == Rtsp.Messages.RtspTransport.LowerTransportType.TCP) { int video_channel = connection[s].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($"[{stream}] Error writing to listener " + connection.listener.RemoteAdress); write_error = true; break; // exit out of foreach loop } } // Send as RTP over UDP if (connection[s].transport_reply.LowerTransport == Rtsp.Messages.RtspTransport.LowerTransportType.UDP && connection[s].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[s].udp_pair.Write_To_Data_Port(rtp_packet, connection.client_hostname, connection[s].client_transport.ClientPort.First); } catch (Exception e) { Console.WriteLine($"[{stream}] UDP Write Exception " + e.ToString()); Console.WriteLine($"[{stream}] Error writing to listener " + connection.listener.RemoteAdress); write_error = true; break; // exit out of foreach loop } } // TODO. Add Multicast } if (write_error) { Console.WriteLine($"[{stream}] Removing session " + connection[s].session_id + " due to write error"); connection.sessions[s].Close(); rtsp_list.Remove(connection); connection.Dispose(); } } } }