用NetCore手撸RTSP交互协议
迪丽瓦拉
2025-05-29 01:44:30
0

 注意点

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);// SendRequestbyte[] 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.");}}}}}}}
}

相关内容

热门资讯

linux入门---制作进度条 了解缓冲区 我们首先来看看下面的操作: 我们首先创建了一个文件并在这个文件里面添加了...
C++ 机房预约系统(六):学... 8、 学生模块 8.1 学生子菜单、登录和注销 实现步骤: 在Student.cpp的...
A.机器学习入门算法(三):基... 机器学习算法(三):K近邻(k-nearest neigh...
数字温湿度传感器DHT11模块... 模块实例https://blog.csdn.net/qq_38393591/article/deta...
有限元三角形单元的等效节点力 文章目录前言一、重新复习一下有限元三角形单元的理论1、三角形单元的形函数(Nÿ...
Redis 所有支持的数据结构... Redis 是一种开源的基于键值对存储的 NoSQL 数据库,支持多种数据结构。以下是...
win下pytorch安装—c... 安装目录一、cuda安装1.1、cuda版本选择1.2、下载安装二、cudnn安装三、pytorch...
MySQL基础-多表查询 文章目录MySQL基础-多表查询一、案例及引入1、基础概念2、笛卡尔积的理解二、多表查询的分类1、等...
keil调试专题篇 调试的前提是需要连接调试器比如STLINK。 然后点击菜单或者快捷图标均可进入调试模式。 如果前面...
MATLAB | 全网最详细网... 一篇超超超长,超超超全面网络图绘制教程,本篇基本能讲清楚所有绘制要点&#...