用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 | 全网最详细网... 一篇超超超长,超超超全面网络图绘制教程,本篇基本能讲清楚所有绘制要点&#...
IHome主页 - 让你的浏览... 随着互联网的发展,人们越来越离不开浏览器了。每天上班、学习、娱乐,浏览器...
TCP 协议 一、TCP 协议概念 TCP即传输控制协议(Transmission Control ...
营业执照的经营范围有哪些 营业执照的经营范围有哪些 经营范围是指企业可以从事的生产经营与服务项目,是进行公司注册...
C++ 可变体(variant... 一、可变体(variant) 基础用法 Union的问题: 无法知道当前使用的类型是什...
血压计语音芯片,电子医疗设备声... 语音电子血压计是带有语音提示功能的电子血压计,测量前至测量结果全程语音播报࿰...
MySQL OCP888题解0... 文章目录1、原题1.1、英文原题1.2、答案2、题目解析2.1、题干解析2.2、选项解析3、知识点3...
【2023-Pytorch-检... (肆十二想说的一些话)Yolo这个系列我们已经更新了大概一年的时间,现在基本的流程也走走通了,包含数...
实战项目:保险行业用户分类 这里写目录标题1、项目介绍1.1 行业背景1.2 数据介绍2、代码实现导入数据探索数据处理列标签名异...
记录--我在前端干工地(thr... 这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 前段时间接触了Th...
43 openEuler搭建A... 文章目录43 openEuler搭建Apache服务器-配置文件说明和管理模块43.1 配置文件说明...