Sharp RTSP

repository·dotnetcore·Indexed 20 days ago

https://github.com/ngraziano/sharprtsp

A C# library for implementing RTSP clients and servers and handling RTP data streams. It operates at the transport layer, supporting UDP, TCP, and Multicast transports for formats such as H264, H265/HEVC, G711, AAC, and AMR. The library provides raw data for video and audio formats without performing decoding.

Tokens
1.8K
Snippets
4
Records
8
Agent score
21%

What's inside Sharp RTSP

  1. Overview of Sharp RTSP

    dotnetcore

    Sharp RTSP is a C# library designed for building RTSP Clients and RTSP Servers, and for handling RTP data streams. It supports various video and audio formats including H264, H265/HEVC, G711, AAC, and AMR, using UDP, TCP, or Multicast transports.

    Important Limitation: This library operates strictly at the transport layer. It does not perform video or audio decoding (e.g., it will not convert H264 into a bitmap). It provides the raw data which you must then feed into a decoder like FFMPEG or hardware-accelerated OS APIs.

  2. How to build an RTSP Client

    dotnetcore

    Building an RTSP client involves a multi-step process of establishing a transport connection, attaching a listener, and performing a handshake of RTSP commands (OPTIONS $\rightarrow$ DESCRIBE $\rightarrow$ SETUP $\rightarrow$ PLAY).

    // 1. Establish TCP Transport
    var tcp_socket = new Rtsp.RtspTcpTransport(host, port);
    
    // 2. Attach Listener
    var rtsp_client = new Rtsp.RtspListener(tcp_socket);
    rtsp_client.MessageReceived += Rtsp_client_MessageReceived;
    rtsp_client.DataReceived += Rtsp_client_DataReceived;
    rtsp_client.Start();
    
    // 3. Send initial command
    var options_message = new Rtsp.Messages.RtspRequestOptions();
    options_message.RtspUri = new Uri(url);
    rtsp_client.SendMessage(options_message);
  3. Handle incoming RTP packets

    dotnetcore

    RTP packets arrive via the DataReceived event. To reconstruct a full video or audio frame, you must:

    1. Parse the RTP header to extract the Marker Bit, Payload Type, and Timestamp.
    2. Accumulate payloads in a buffer.
    3. When the Marker Bit is set to 1, it indicates the end of a frame/unit. Process the accumulated buffer as a single frame.
    private void Rtsp_client_DataReceived(object sender, Rtsp.RtspChunkEventArgs e)
    {
        // Extract header info (simplified example)
        int rtp_marker = (e.Message.Data[1] >> 7) & 0x01;
        int rtp_payload_type = (e.Message.Data[1] >> 0) & 0x7F;
    
        // Accumulate payload
        byte[] rtp_payload = new byte[e.Message.Data.Length - rtp_payload_start];
        Array.Copy(e.Message.Data, rtp_payload_start, rtp_payload, 0, rtp_payload.Length);
        temporary_rtp_payloads.Add(rtp_payload);
    
        // If marker bit is 1, the frame is complete
        if (rtp_marker == 1)
        {
            Process_RTP_Frame(temporary_rtp_payloads);
            temporary_rtp_payloads.Clear();
        }
    }
  4. Reconstruct H264 NAL units from RTP payloads

    dotnetcore

    H264 video is often delivered via RTP using either Normal Packing or Fragmented Unit (FU-A) types. To save to a playable .h264 file, you must prepend the NAL start code 0x00 0x00 0x00 0x01 and handle fragmentation:

    • Normal NAL: Write the start code followed by the payload.
    • FU-A (Fragmented):
      • For the Start Fragment (fu_header_s == 1): Write the start code, reconstruct the NAL header using the original NRI/F bits and the FU header's type, then write the payload.
      • For Subsequent Fragments (fu_header_s == 0): Write the payload directly (skipping the FU header bytes).
    // Example logic for FU-A reconstruction
    if (fu_header_s == 1)
    {
        fs.Write(nal_header, 0, nal_header.Length); // 0x00 0x00 0x00 0x01
        byte reconstructed_nal_type = (byte)((nal_header_nri << 5) + fu_header_type);
        fs.WriteByte(reconstructed_nal_type);
        fs.Write(rtp_payloads[payload_index], 2, rtp_payloads[payload_index].Length - 2);
    }
    else if (fu_header_s == 0)
    {
        fs.Write(rtp_payloads[payload_index], 2, rtp_payloads[payload_index].Length - 2);
    }
  5. Use RtspListener to manage communication

    dotnetcore

    The Rtsp.RtspListener class is the primary interface for interacting with an RTSP server. It runs a worker thread to listen for replies and provides events for handling incoming data.

    • SendMessage(RtspRequest message): Sends an RTSP command to the server.
    • MessageReceived Event: Fired when the server sends an RTSP response (e.g., a reply to a DESCRIBE request).
    • DataReceived Event: Fired when RTP packets (the actual media data) are received.
  6. Parse SDP data from DESCRIBE replies

    dotnetcore

    When you receive a reply to a DESCRIBE request, the message.Data contains the SDP information. You can use Rtsp.Sdp.SdpFile.Read to parse this into a structured object to find media tracks, control URLs, and payload types.

    // Inside MessageReceived event
    if (message.OriginalRequest is Rtsp.Messages.RtspRequestDescribe)
    {
        using (StreamReader sdp_stream = new StreamReader(new MemoryStream(message.Data)))
        {
            var sdp_data = Rtsp.Sdp.SdpFile.Read(sdp_stream);
            // Iterate through sdp_data.Medias to find video/audio tracks
        }
    }
  7. Send RTSP Request Messages

    dotnetcore

    To control the session, you must send specific request types using rtsp_client.SendMessage():

    Message TypePurpose
    RtspRequestOptionsRequests the server's supported methods.
    RtspRequestDescribeRequests the SDP (Session Description Protocol) data.
    RtspRequestSetupConfigures the transport (e.g., RTP/AVP/TCP) for a specific stream.
    RtspRequestPlayStarts the media transmission.
    RtspRequestPausePauses the media transmission.
    RtspRequestTeardownTerminates the session.