用NetCore手撸RTSP交互协议

 注意点

1. DESCRIBE 第1次发送时返回401错误,返回信息带 realm,nonce,参数使用MD5校验后重新发送;

2. SETUP 的url信息,由DESCRIBE返回的head中的sdp字符串中解析,readonly,x-dimensions(视频分辨率),control(视频url),rtpmap(编解码信息)

3.PLAY的Session信息,由SETUP返回的head中的Session

4.解析完后,tcpclient接收rtp数据包,可用ffmepg进行解码

 

核心代码RTSPClient.cs

using System.Net.Sockets;
using System.Text;

namespace RtspClientCore
{
    class RtspClient
    {
        string rtspUrl = "rtsp://192.168.0.2:554/h264/ch1/main/av_stream";
        string username = "admin";
        string password = "thzn123456";

        TcpClient tcpClient;
        NetworkStream tcpStream;

        private int cseq;

        public Uri rtspUri { get; set; }

        public int NewCSeq
        {
            get
            {
                return ++cseq;
            }
        }

        string Authorization = "";
        string Session = "";
        string UserAgent = "C# RTSP Client";

        public RtspClient(string rtspUrl, string username, string password)
        {
            this.rtspUrl = rtspUrl;
            this.username = username;
            this.password = password;

            this.rtspUri = new Uri(rtspUrl);
            this.tcpClient = new TcpClient(rtspUri.Host, rtspUri.Port);
            this.tcpStream = tcpClient.GetStream();
        }

        RTSPResponse ExcuteRequest(string method, string url, string request, bool skip = false)
        {
            string temp = request + "\r\n";
            temp = temp.Replace("@CSeq", NewCSeq.ToString());

            Logger.Info(temp);

            // SendRequest
            byte[] requestBytes = Encoding.ASCII.GetBytes(temp);
            tcpStream.Write(requestBytes, 0, requestBytes.Length);

            // ReadResponse 
            StreamReader reader = new StreamReader(tcpStream);

            RTSPResponse r1 = new RTSPResponse(reader, skip); 

            switch (r1.StatusCode)
            { 
                case "401":
                    {
                        //RTSP / 1.0 401 Unauthorized
                        //CSeq: 1
                        //WWW - Authenticate: Digest realm = "IP Camera(G7574)", nonce = "d355a9a6a081d0d5ce50d0dd90a14148", stale = "FALSE"
                        //Date: Thu, Mar 16 2023 10:34:24 GMT 
                        string realm = string.Empty;
                        string nonce = string.Empty;
                        string authType = string.Empty;

                        var auth = r1.Headers.Where(x => x.Key == "WWW-Authenticate").FirstOrDefault(); 

                        if (auth.Value.Contains("Digest"))
                        {
                            // 摘要认证
                            authType = "Digest";
                            RtspUtil.GetDigestParams(auth.Value, ref realm, ref nonce); 
                        }
                        else if (auth.Value.Contains("Basic"))
                        {
                            // 基本认证
                            authType = "Basic";// Authorization: Basic YWRtaW46YWRtaW4=\r\n\r\n
                        }
                        else
                        {
                            throw new Exception("Server auth mode not support:" + auth);
                        }
                         
                        Authorization = RtspUtil.GetAuthorization(authType, url, username, password, realm, nonce, method);

                        request += "Authorization:" + Authorization + "\r\n";

                        r1 = ExcuteRequest(method, url, request, false);
                    }
                    break;
            }

            return r1;
        }

        public RTSPResponse DESCRIBE()
        {
            string mothed = "DESCRIBE";
            StringBuilder request = new StringBuilder();
            request.Append(mothed + " " + rtspUrl + " RTSP/1.0\r\n");
            request.Append("CSeq: @CSeq\r\n");
            request.Append("User-Agent: " + UserAgent + "\r\n");
            request.Append("Accept: application/sdp\r\n");
            return ExcuteRequest(mothed, rtspUrl, request.ToString());  
        }

        public RTSPResponse SETUP(string url)
        {
            string mothed = "SETUP";
            StringBuilder request = new StringBuilder();
            request.Append(mothed + " " + url + " RTSP/1.0\r\n");
            request.Append("CSeq: @CSeq\r\n"); 
            request.Append("Authorization: " + Authorization + "\r\n");
            request.Append("User-Agent: " + UserAgent + "\r\n");
            request.Append("Transport: RTP/AVP/TCP;unicast;interleaved=0-1\r\n");
            return ExcuteRequest(mothed, rtspUrl, request.ToString()); 
        }

        public RTSPResponse PLAY(string url)
        {
            string mothed = "PLAY";
            StringBuilder request = new StringBuilder();
            request.Append(mothed + " " + url + " RTSP/1.0\r\n");
            request.Append("CSeq: @CSeq\r\n");
            request.Append("Authorization: " + Authorization + "\r\n");
            request.Append("User-Agent: " + UserAgent + "\r\n");
            request.Append("Session: " + this.Session + "\r\n");
            request.Append("Range: npt=0.000-\r\n");
            return ExcuteRequest(mothed, rtspUrl, request.ToString(), true);
        }

        public RTSPResponse TEARDOWN(string url, string session)
        {
            string mothed = "TEARDOWN";
            StringBuilder request = new StringBuilder();
            request.Append(mothed + " " + url + " RTSP/1.0\r\n");
            request.Append("CSeq: @CSeq\r\n");
            request.Append("Authorization: " + Authorization + "\r\n");
            request.Append("User-Agent: " + UserAgent + "\r\n");
            request.Append("Session: " + this.Session + "\r\n"); 
            return ExcuteRequest(mothed, rtspUrl, request.ToString(), true);
        } 

        public void Start()
        {
            RTSPResponse r1 = DESCRIBE();

            if ("200" == r1.StatusCode)
            {
                SDP sdp_data = new SDP(r1.Response);

                bool find = false; bool video1 = false; bool video2 = false; var dimensions = string.Empty; var control = string.Empty; var rtpmap = string.Empty;

                for (int x = 0; x < sdp_data.MediaDescribes.Count; x++)
                {
                    var items = sdp_data.MediaDescribes[x].a;

                    foreach (var item in items)
                    {
                        string mediainfo = item.ToLower();

                        if (mediainfo.Contains("recvonly"))
                        {
                            video1 = true;
                        }
                        if (mediainfo.Contains("x-dimensions"))
                        {
                            video2 = true;
                            dimensions = item;
                        }
                        if (mediainfo.Contains("control"))
                        {
                            control = item.Replace("control:", "");
                        }
                        if (mediainfo.Contains("rtpmap"))
                        {
                            rtpmap = item.Replace("rtpmap:", "");
                        } 
                        if (video1 && video2 & !string.IsNullOrEmpty(control) & !string.IsNullOrEmpty(rtpmap))
                        {
                            find = true;
                            break;
                        }
                    }

                    if (find)
                    {
                        break;
                    }
                }

                RTSPResponse r2 = SETUP(control);
                if ("200" == r2.StatusCode)
                { 
                    string sessionVal = r2.Headers.Where(x => x.Key == "Session").FirstOrDefault().Value;
                    if (!string.IsNullOrEmpty(sessionVal))
                    {
                        string[] sessionParms = sessionVal.Split(';');
                        if (sessionParms.Length > 1)
                        {
                            this.Session = sessionParms[0];
                        }
                        else
                        {
                            this.Session = sessionVal;
                        }
                        RTSPResponse r3 = PLAY(control);
                        if ("200" == r3.StatusCode)
                        {
                            // 接收rtp数据
                            byte[] buffer = new byte[1024];
                            while (true)
                            {
                                int bytesRead = tcpStream.Read(buffer, 0, buffer.Length);
                                if (bytesRead == 0)
                                {
                                    break;
                                }
                                // 处理接收到的RTP数据
                                //Console.WriteLine($"Received {bytesRead} bytes of RTP data.");
                            }
                        }
                    }
                }
            }
        }
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
NetCore是一个跨平台的开源框架,用于开发和运行各种应用程序。如果要在NetCore使用MQTT协议,可以按照以下步骤进行操作: 1. 添加MQTT NuGet包:在NetCore项目中,可以使用MQTT客户端库来实现与MQTT代理的通信。可以通过NuGet包管理器或在项目文件中动添加以下NuGet包:Microsoft.Azure.Devices.Client。安装后,将能够在项目中使用MQTT客户端。 2. 创建MQTT客户端:使用MQTT客户端库创建一个MQTT客户端实例。在创建客户端实例时,需要指定MQTT代理的连接字符串和客户端ID。例如: ``` MqttClient mqttClient = new MqttClient(connectionString, clientId); ``` 3. 连接到MQTT代理:使用客户端实例连接到MQTT代理。需要通过调用Connect方法来连接。例如: ``` mqttClient.Connect(); ``` 4. 订阅主题:使用客户端实例订阅感兴趣的主题。通过调用Subscribe方法,并指定要订阅的主题,可以接收该主题下的消息。例如: ``` mqttClient.Subscribe(topic); ``` 5. 发布消息:使用客户端实例发布消息到特定的主题。通过调用Publish方法,并指定要发布的主题和消息内容,可以向指定的主题下发送消息。例如: ``` mqttClient.Publish(topic, message); ``` 6. 处理接收的消息:通过订阅特定的主题,可以接收到MQTT代理发布的消息。可以通过注册消息处理程序来处理接收到的消息。例如: ``` mqttClient.MqttMsgPublishReceived += MqttMsgReceived; ``` 在MqttMsgReceived方法中,可以处理接收到的消息。 以上是在NetCore使用MQTT协议的基本步骤。通过使用MQTT客户端库,可以方便地实现与MQTT代理的通信,并进行消息的订阅和发布。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值