GithubHelp home page GithubHelp logo

phillijw / sharprtsp Goto Github PK

View Code? Open in Web Editor NEW

This project forked from ngraziano/sharprtsp

0.0 3.0 1.0 436 KB

A dotnet core version of an RTSP handling library

License: Other

C# 99.89% Batchfile 0.11%

sharprtsp's Introduction

Sharp RTSP

Build Status Coverage Status

A C# library to handle RTSP connections.

โš ๏ธ : This library do not handle the decoding of the video. It is limited to the transport layer. You mut use a library like FFMPEG to do the decoding.

RTSP Client Example

  • STEP 1 - Open TCP Socket connection to the RTSP Server

              // Connect to a RTSP Server
              tcp_socket = new Rtsp.RtspTcpTransport(host,port);
    
              if (tcp_socket.Connected == false)
              {
                  Console.WriteLine("Error - did not connect");
                  return;
              }

    This opens a connection for a 'TCP' mode RTSP/RTP session where RTP packets are set in the RTSP socket.

  • STEP 2 - Create a RTSP Listener and attach it to the RTSP TCP Socket

              // Connect a RTSP Listener to the TCP Socket to send messages and listen for replies
              rtsp_client = new Rtsp.RtspListener(tcp_socket);
    
              rtsp_client.MessageReceived += Rtsp_client_MessageReceived;
              rtsp_client.DataReceived += Rtsp_client_DataReceived;
    
              rtsp_client.Start(); // start reading messages from the server

    The RTSP Listener class lets you SEND messages to the RTSP Server (see below).
    The RTSP Listner class has a worker thread that listens for replies from the RTSP Server.
    When replies are received the MessageReceived Event is fired.
    When RTP packets are received the DataReceived Event is fired.

  • STEP 3 - Send Messages to the RTSP Server

    The samples below show how to send messages.

    Send OPTIONS with this code :

              Rtsp.Messages.RtspRequest options_message = new Rtsp. Messages.RtspRequestOptions();
              options_message.RtspUri = new Uri(url);
              rtsp_client.SendMessage(options_message);

    Send DESCRIBE with this code :

              // send the Describe
              Rtsp.Messages.RtspRequest describe_message = new Rtsp.Messages.RtspRequestDescribe();
              describe_message.RtspUri = new Uri(url);
              rtsp_client.SendMessage(describe_message);
              // The reply will include the SDP data

    Send SETUP with this code :

              // the value of 'control' comes from parsing the SDP for the desired video or audio sub-stream
              Rtsp.Messages.RtspRequest setup_message = new Rtsp.Messages.RtspRequestSetup();
              setup_message.RtspUri = new Uri(url + "/" + control);
              setup_message.AddHeader("Transport: RTP/AVP/TCP;interleaved=0");
              rtsp_client.SendMessage(setup_message);
              // The reply will include the Session

    Send PLAY with this code :

              // the value of 'session' comes from the reply of the SETUP command
              Rtsp.Messages.RtspRequest play_message = new Rtsp.Messages.RtspRequestPlay();
              play_message.RtspUri = new Uri(url);
              play_message.Session = session;
              rtsp_client.SendMessage(play_message);
  • STEP 4 - Handle Replies when the MessageReceived event is fired

    This example assumes the main program sends an OPTIONS Command.
    It looks for a reply from the server for OPTIONS and then sends DESCRIBE.
    It looks for a reply from the server for DESCRIBE and then sends SETUP (for the video stream)
    It looks for a reply from the server for SETUP and then sends PLAY.
    Once PLAY has been sent the video, in the form of RTP packets, will be received.

          private void Rtsp_client_MessageReceived(object sender, Rtsp.RtspChunkEventArgs e)
          {
              Rtsp.Messages.RtspResponse message = e.Message as Rtsp.Messages.RtspResponse;
    
              Console.WriteLine("Received " + message.OriginalRequest.ToString());
    
              if (message.OriginalRequest != null && message.OriginalRequest is Rtsp.Messages.RtspRequestOptions)
              {
                  // send the DESCRIBE
                  Rtsp.Messages.RtspRequest describe_message = new Rtsp.Messages.RtspRequestDescribe();
                  describe_message.RtspUri = new Uri(url);
                  rtsp_client.SendMessage(describe_message);
              }
    
              if (message.OriginalRequest != null && message.OriginalRequest is Rtsp.Messages.RtspRequestDescribe)
              {
                  // Got a reply for DESCRIBE
                  // Examine the SDP
                  Console.Write(System.Text.Encoding.UTF8.GetString(message.Data));
    
                  Rtsp.Sdp.SdpFile sdp_data;
                  using (StreamReader sdp_stream = new StreamReader(new MemoryStream(message.Data)))
                  {
                      sdp_data = Rtsp.Sdp.SdpFile.Read(sdp_stream);
                  }
    
                  // Process each 'Media' Attribute in the SDP.
                  // If the attribute is for Video, then send a SETUP
                  for (int x = 0; x < sdp_data.Medias.Count; x++)
                  {
                      if (sdp_data.Medias[x].GetMediaType() == Rtsp.Sdp.Media.MediaType.video)
                      {
                          // seach the atributes for control, fmtp and rtpmap
                          String control = "";  // the "track" or "stream id"
                          String fmtp = ""; // holds SPS and PPS
                          String rtpmap = ""; // holds the Payload format, 96 is often used with H264
                          foreach (Rtsp.Sdp.Attribut attrib in sdp_data.Medias[x].Attributs)
                          {
                              if (attrib.Key.Equals("control")) control = attrib.Value;
                              if (attrib.Key.Equals("fmtp")) fmtp = attrib.Value;
                              if (attrib.Key.Equals("rtpmap")) rtpmap = attrib.Value;
                          }
                          
                          // Get the Payload format number for the Video Stream
                          String[] split_rtpmap = rtpmap.Split(' ');
                          video_payload = 0;
                          bool result = Int32.TryParse(split_rtpmap[0], out video_payload);
    
                          // Send SETUP for the Video Stream
                          // using Interleaved mode (RTP frames over the RTSP socket)
                          Rtsp.Messages.RtspRequest setup_message = new Rtsp.Messages.RtspRequestSetup();
                          setup_message.RtspUri = new Uri(url + "/" + control);
                          setup_message.AddHeader("Transport: RTP/AVP/TCP;interleaved=0");
                          rtsp_client.SendMessage(setup_message);
                      }
                  }
              }
    
              if (message.OriginalRequest != null && message.OriginalRequest is Rtsp.Messages.RtspRequestSetup)
              {
                  // Got Reply to SETUP
                  Console.WriteLine("Got reply from Setup. Session is " + message.Session);
    
                  String session = message.Session; // Session value used with Play, Pause, Teardown
                  
                  // Send PLAY
                  Rtsp.Messages.RtspRequest play_message = new Rtsp.Messages.RtspRequestPlay();
                  play_message.RtspUri = new Uri(url);
                  play_message.Session = session;
                  rtsp_client.SendMessage(play_message);
              }
    
              if (message.OriginalRequest != null && message.OriginalRequest is Rtsp.Messages.RtspRequestPlay)
              {
                  // Got Reply to PLAY
                  Console.WriteLine("Got reply from Play  " + message.Command);
              }
          }
  • STEP 5 - Handle RTP Video

    This code handles each incoming RTP packet, combining RTP packets that are all part of the same frame of vdeo (using the Marker Bit). Once a full frame is received it can be passed to a De-packetiser to get the compressed video data

          List<byte[]> temporary_rtp_payloads = new List<byte[]>();
    
          private void Rtsp_client_DataReceived(object sender, Rtsp.RtspChunkEventArgs e)
          {
              // RTP Packet Header
              // 0 - Version, P, X, CC, M, PT and Sequence Number
              //32 - Timestamp
              //64 - SSRC
              //96 - CSRCs (optional)
              //nn - Extension ID and Length
              //nn - Extension header
    
              int rtp_version =      (e.Message.Data[0] >> 6);
              int rtp_padding =      (e.Message.Data[0] >> 5) & 0x01;
              int rtp_extension =    (e.Message.Data[0] >> 4) & 0x01;
              int rtp_csrc_count =   (e.Message.Data[0] >> 0) & 0x0F;
              int rtp_marker =       (e.Message.Data[1] >> 7) & 0x01;
              int rtp_payload_type = (e.Message.Data[1] >> 0) & 0x7F;
              uint rtp_sequence_number = ((uint)e.Message.Data[2] << 8) + (uint)(e.Message.Data[3]);
              uint rtp_timestamp = ((uint)e.Message.Data[4] <<24) + (uint)(e.Message.Data[5] << 16) + (uint)(e.Message.Data[6] << 8) + (uint)(e.Message.Data[7]);
              uint rtp_ssrc =      ((uint)e.Message.Data[8] << 24) + (uint)(e.Message.Data[9] << 16) + (uint)(e.Message.Data[10] << 8) + (uint)(e.Message.Data[11]);
    
              int rtp_payload_start = 4 // V,P,M,SEQ
                                  + 4 // time stamp
                                  + 4 // ssrc
                                  + (4 * rtp_csrc_count); // zero or more csrcs
    
              uint rtp_extension_id = 0;
              uint rtp_extension_size = 0;
              if (rtp_extension == 1)
              {
                  rtp_extension_id = ((uint)e.Message.Data[rtp_payload_start + 0] << 8) + (uint)(e.Message.Data[rtp_payload_start + 1] << 0);
                  rtp_extension_size = ((uint)e.Message.Data[rtp_payload_start + 2] << 8) + (uint)(e.Message.Data[rtp_payload_start + 3] << 0);
                  rtp_payload_start += 4 + (int)rtp_extension_size;  // extension header and extension payload
              }
    
              Console.WriteLine("RTP Data"
                                 + " V=" + rtp_version
                                 + " P=" + rtp_padding
                                 + " X=" + rtp_extension
                                 + " CC=" + rtp_csrc_count
                                 + " M=" + rtp_marker
                                 + " PT=" + rtp_payload_type
                                 + " Seq=" + rtp_sequence_number
                                 + " Time=" + rtp_timestamp
                                 + " SSRC=" + rtp_ssrc
                                 + " Size=" + e.Message.Data.Length);
    
    
              if (rtp_payload_type != video_payload)
              {
                  Console.WriteLine("Ignoring this RTP payload");
                  return; // ignore this data
              }
    
    
              // If rtp_marker is '1' then this is the final transmission for this packet.
              // If rtp_marker is '0' we need to accumulate data with the same timestamp
    
              // ToDo - Check Timestamp matches
    
              // Add to the tempoary_rtp List
              byte[] rtp_payload = new byte[e.Message.Data.Length - rtp_payload_start]; // payload with RTP header removed
              System.Array.Copy(e.Message.Data, rtp_payload_start, rtp_payload, 0, rtp_payload.Length); // copy payload
              temporary_rtp_payloads.Add(rtp_payload);
    
              if (rtp_marker == 1)
              {
                  // Process the RTP frame
                  Process_RTP_Frame(temporary_rtp_payloads);
                  temporary_rtp_payloads.Clear();
              }
          }
  • STEP 6 - Process RTP frame

    An RTP frame consists of 1 or more RTP packets
    H264 video is packed into one or more RTP packets and this sample extracts Normal Packing and Fragmented Unit type A packing (the common two)
    This example writes the video to a .264 file which can be played with FFPLAY

          FileStream fs = null;
          byte[] nal_header = new byte[]{ 0x00, 0x00, 0x00, 0x01 };
          int norm, fu_a, fu_b, stap_a, stap_b, mtap16, mtap24 = 0; // stats counters
    
          public void Process_RTP_Frame(List<byte[]>rtp_payloads)
          {
              Console.WriteLine("RTP Data comprised of " + rtp_payloads.Count + " rtp packets");
    
              if (fs == null)
              {
                  // Create the file
                  String filename = "rtsp_capture_" + DateTime.Now.ToString("yyyyMMdd_HHmmss") + ".h264";
                  fs = new FileStream(filename, FileMode.Create);
                  
                  // TODO. Get SPS and PPS from the SDP Attributes (the fmtp attribute) and write to the file
                  // for IP cameras that only out the SPS and PPS out-of-band
              }
    
              for (int payload_index = 0; payload_index < rtp_payloads.Count; payload_index++) {
                  // Examine the first rtp_payload and the first byte (the NAL header)
                  int nal_header_f_bit = (rtp_payloads[payload_index][0] >> 7) & 0x01;
                  int nal_header_nri = (rtp_payloads[payload_index][0] >> 5) & 0x03;
                  int nal_header_type = (rtp_payloads[payload_index][0] >> 0) & 0x1F;
    
                  // If the NAL Header Type is in the range 1..23 this is a normal NAL (not fragmented)
                  // So write the NAL to the file
                  if (nal_header_type >= 1 && nal_header_type <= 23)
                  {
                      Console.WriteLine("Normal NAL");
                      norm++;
                      fs.Write(nal_header, 0, nal_header.Length);
                      fs.Write(rtp_payloads[payload_index], 0, rtp_payloads[payload_index].Length);
                  }
                  else if (nal_header_type == 24)
                  {
                      // There are 4 types of Aggregation Packet (multiple NALs in one RTP packet)
                      Console.WriteLine("Agg STAP-A not supported");
                      stap_a++;
                  }
                  else if (nal_header_type == 25)
                  {
                      // There are 4 types of Aggregation Packet (multiple NALs in one RTP packet)
                      Console.WriteLine("Agg STAP-B not supported");
                      stap_b++;
                  }
                  else if (nal_header_type == 26)
                  {
                      // There are 4 types of Aggregation Packet (multiple NALs in one RTP packet)
                      Console.WriteLine("Agg MTAP16 not supported");
                      mtap16++;
                  }
                  else if (nal_header_type == 27)
                  {
                      // There are 4 types of Aggregation Packet (multiple NALs in one RTP packet)
                      Console.WriteLine("Agg MTAP24 not supported");
                      mtap24++;
                  }
                  else if (nal_header_type == 28)
                  {
                      Console.WriteLine("Fragmented Packet Type FU-A");
                      fu_a++;
    
                      // Parse Fragmentation Unit Header
                      int fu_header_s = (rtp_payloads[payload_index][1] >> 7) & 0x01;  // start marker
                      int fu_header_e = (rtp_payloads[payload_index][1] >> 6) & 0x01;  // end marker
                      int fu_header_r = (rtp_payloads[payload_index][1] >> 5) & 0x01;  // reserved. should be 0
                      int fu_header_type = (rtp_payloads[payload_index][1] >> 0) & 0x1F; // Original NAL unit header
    
                      Console.WriteLine("Frag FU-A s="+fu_header_s + "e="+fu_header_e);
    
                      // Start Flag set
                      if (fu_header_s == 1)
                      {
                          // Write 00 00 00 01 header
                          fs.Write(nal_header, 0, nal_header.Length); // 0x00 0x00 0x00 0x01
    
                          // Modify the NAL Header that was at the start of the RTP packet
                          // Keep the F and NRI flags but substitute the type field with the fu_header_type
                          byte reconstructed_nal_type = (byte)((nal_header_nri << 5) + fu_header_type);
                          fs.WriteByte(reconstructed_nal_type); // NAL Unit Type
                          fs.Write(rtp_payloads[payload_index], 2, rtp_payloads[payload_index].Length - 2); // start after NAL Unit Type and FU Header byte
                      }
    
                      if (fu_header_s == 0)
                      {
                          // append this payload to the output NAL stream
                          // Data starts after the NAL Unit Type byte and the FU Header byte
    
                          fs.Write(rtp_payloads[payload_index], 2, rtp_payloads[payload_index].Length-2); // start after NAL Unit Type and FU Header byte
                      }
                  }
    
                  else if (nal_header_type == 29)
                  {
                      Console.WriteLine("Fragmented Packet  FU-B not supported");
                      fu_b++;
                  }
                  else
                  {
                      Console.WriteLine("Unknown NAL header " + nal_header_type);
                  }
    
              }
              // ensure video is written to disk
              fs.Flush(true);
              
              // Print totals
              Console.WriteLine("Norm=" + norm + " ST-A=" + stap_a + " ST-B=" + stap_b + " M16=" + mtap16 + " M24=" + mtap24 + " FU-A=" + fu_a + " FU-B=" + fu_b);
          }

sharprtsp's People

Contributors

jjbott avatar ngraziano avatar phillijw avatar rogerhardiman avatar sheacodes avatar ypenev avatar

Watchers

 avatar  avatar  avatar

Forkers

jjbott

sharprtsp's Issues

Add BASIC auth

Someone added DIGEST auth which is great. Need to get BASIC auth to work. Right now the camera just throws a 401 because the nonce is null. DIGEST stuff probably could use a bit of refactoring as part of this to separate the logic a little nicer

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.