【RTSP从零实践】1、根据RTSP协议实现一个RTSP服务

😁博客主页😁:🚀https://blog.csdn.net/wkd_007🚀
🤑博客内容🤑:🍭嵌入式开发、Linux、C语言、C++、数据结构、音视频🍭
🤣本文内容🤣:🍭介绍怎么实现一个RTSP服务器🍭
😎金句分享😎:🍭你不能选择最好的,但最好的会来选择你——泰戈尔🍭
⏰发布时间⏰: 2025-06-07 20:57:14

本文未经允许,不得转发!!!

目录

  • 🎄一、概述
  • 🎄二、实现步骤、实现细节
    • ✨2.1、使用 TCP 实现RTSP服务器
    • ✨2.2、解析 RTSP 客户端的请求报文
    • ✨2.3、处理 OPTION 请求
    • ✨2.4、处理 DESCRIBE 方法
    • ✨2.5、处理 SETUP 方法
    • ✨2.6、处理 PLAY 方法
    • ✨2.7、处理 TEARDOWN 方法
  • 🎄三、RTSP协议的简单实现源码
  • 🎄四、总结


在这里插入图片描述

在这里插入图片描述

🎄一、概述

前面的文章(RTSP协议详解 及 抓包例子解析)详细地介绍了RTSP协议,学完之后,如果不动手去做一下,可能会觉得有点抽象,知识掌握不牢固。这篇文章就是从服务器的角度来看看RTSP协议是怎么实现的,需要注意哪些细节,带着读者动手做一篇。

注意:本文只实现RTSP服务,并没有实现音视频流传输的RTP,这个协议的内容在后面文章介绍。

在实现之前,对RTSP协议做一些必要的回顾:

  • RTSP,全称时 Real Time Streaming Protocol,实时流媒体协议。其传输层协议使用的是TCP。这点要特别注意,我们后面还会学习到RTP_OVER_UDPRTP_OVER_TCP,容易混淆;
  • RTSP协议的方法有很多,但一个最简单的RTSP服务器可以只提供这5个方法:OPTIONS、DESCRIBE、SETUP、PLAY、TEARDOWN
  • RTSP协议是通过 请求报文响应报文 来传输数据的,这些报文都是可读的文本,需要按协议要求解析和填写。

在这里插入图片描述

🎄二、实现步骤、实现细节

这个小写介绍一些实现步骤和细节,下个小节会提供源码,可以结合着源码看帮助理解消化。

✨2.1、使用 TCP 实现RTSP服务器

RTSP协议是使用TCP作为传输层协议的,所以我们实现RTSP服务器的第一个步骤可以是使用创建一个TCP服务器,绑定的监听端口一般是5548554。下面是创建一个TCP服务端的基本流程:

  • 1、创建TCP套接字;
  • 2、绑定指定端口;
  • 3、开始监听;

在这里插入图片描述


✨2.2、解析 RTSP 客户端的请求报文

作为RTSP服务端,开始监听之后,如果有RTSP客户端连接上来,就需要跟客户端进行会话(session)沟通了。服务端首先要做的就是读取客户端发过来的数据(请求报文),分析报文并获取一些必要的数据如:RTSP方法、请求的url、请求的序列号CSeq、客户端的端口等,本文用到的具体的分析如下图:

在这里插入图片描述

解析完请求报文之后,接下来就是处理各个方法的请求报文,本文例子只实现了5个RTSP方法的处理:OPTIONS、DESCRIBE、SETUP、PLAY、TEARDOWN。下面再分别介绍这些方法的处理。


✨2.3、处理 OPTION 请求

客户端发送OPTION请求,要求服务端返回所支持的RTSP方法。作为服务器,我们只需要将自己的方法按格式拼接成字符串回复给客户端即可。
在这里插入图片描述
关于请求报文格式、响应报文格式不了解的,可以看上一篇文章的第三节:RTSP协议详解 及 抓包例子解析,这里不再赘述。


✨2.4、处理 DESCRIBE 方法

处理 DESCRIBE 方法,就是要告诉客户端可以向其提供怎样的服务。一般会使用sdp协议来描述这些服务,例如:“提供一个什么样的视频流”、“提供一个什么样的音频流”、"提供一个视频流、一个音频流"等等。下面是处理DESCRIBE方法的回复:
"Content-Type: application/sdp\r\n" 指明响应体的协议是sdp
"Content-Length: %zu\r\n\r\n%s" 指明响应体的长度。

在这里插入图片描述
关于sdp协议的内容,可以参考这篇文章:SDP(会话描述协议)详解 及 抓包例子分析。下面是本例子的sdp内容:
在这里插入图片描述


✨2.5、处理 SETUP 方法

SETUP方法主要作用是,客户端告诉服务端按照什么传输协议(TCP/UDP)来发送 RTP 包(音视频数据)。如果是UDP协议传输,通过什么端口来发送数据。下面代码是本文例子的实现:
Session: 10086001 指明当前的会话ID;
Transport: RTP/AVP; 指明传输协议的UDP;
client_port=58714-58715 指明客户端通过58714来接收RTP包,通过58715来接收RTCP包。
在这里插入图片描述


✨2.6、处理 PLAY 方法

PLAY是 客户端 告诉 RTSP服务端 可以开始发送音视频数据(RTP)包了。服务器这边回复成功后,就开始向响应端口发送数据。
在这里插入图片描述


✨2.7、处理 TEARDOWN 方法

TEARDOWN方法的作用是,客户端告诉服务器停止发送数据流,结束会话。服务器需要回复后做一些停止发送码流的动作。
在这里插入图片描述


在这里插入图片描述

🎄三、RTSP协议的简单实现源码

基本上按照上个小节的步骤就可以实现一个简单的RTSP服务端了,这里给出源码 rtspSever.c,已经编译通过了,可供读者测试使用。

/*** @file rtspServer.c* @author wkd_007, csdn主页(https://blog.csdn.net/wkd_007)* @brief* @version 0.1* @date 2025-06-05** @copyright Copyright (c) 2025**/#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>#define RTSP_PORT 8554
#define MAX_CLIENTS 5
#define SESSION_ID 10086001
#define SESSION_TIMEOUT 60// 解析RTSP请求
static void rtsp_request_parse(char *buffer, char *method, char *url, int *cseq, int *pRtpPort)
{char *line = strtok(buffer, "\r\n");sscanf(line, "%s %s RTSP/1.0", method, url);while ((line = strtok(NULL, "\r\n")) != NULL){if (strncmp(line, "CSeq:", 5) == 0){sscanf(line, "CSeq: %d", cseq);}char *pCliPort = strstr(line, "client_port=");if (pCliPort != NULL){int rtcpPort = 0;sscanf(pCliPort, "client_port=%d-%d", pRtpPort, &rtcpPort);// printf("rtpPort: %d-%d\n",*pRtpPort, rtcpPort);}}
}// 生成SDP描述
static const char *generate_sdp()
{return "v=0\r\n""o=- 0 0 IN IP4 0.0.0.0\r\n""s=Example Stream\r\n""t=0 0\r\n""m=video 0 RTP/AVP 96\r\n""a=rtpmap:96 H264/90000\r\n""a=control:streamid=0\r\n";
}static void rtsp_handle_OPTION(char *response, int cseq)
{sprintf(response,"RTSP/1.0 200 OK\r\n""CSeq: %d\r\n""Public: OPTIONS, DESCRIBE, SETUP, PLAY, TEARDOWN\r\n\r\n",cseq);
}static void rtsp_handle_DESCRIBE(char *response, int cseq)
{sprintf(response,"RTSP/1.0 200 OK\r\n""CSeq: %d\r\n""Content-Type: application/sdp\r\n""Content-Length: %zu\r\n\r\n%s",cseq, strlen(generate_sdp()), generate_sdp());
}static void rtsp_handle_SETUP(char *response, int cseq, int rtpPort)
{sprintf(response,"RTSP/1.0 200 OK\r\n""CSeq: %d\r\n""Session: %u; timeout=%d\r\n""Transport: RTP/AVP;unicast;client_port=%d-%d\r\n\r\n",cseq, SESSION_ID, SESSION_TIMEOUT, rtpPort, rtpPort + 1);
}static void rtsp_handle_PLAY(char *response, int cseq)
{sprintf(response,"RTSP/1.0 200 OK\r\n""CSeq: %d\r\n""Session: %u; timeout=%d\r\n""Range: npt=0.000-\r\n\r\n",cseq, SESSION_ID, SESSION_TIMEOUT);
}static void rtsp_handle_TEARDOWN(char *response, int cseq)
{sprintf(response,"RTSP/1.0 200 OK\r\n""CSeq: %d\r\n""Session: %d; timeout=%d\r\n\r\n",cseq, SESSION_ID, SESSION_TIMEOUT);
}// 处理客户端连接
void *handle_client(void *arg)
{int client_sock = *(int *)arg;char buffer[1024] = {0};int cseq = 0;int rtpPort = 0;while (1){memset(buffer, 0, sizeof(buffer));int len = read(client_sock, buffer, sizeof(buffer) - 1);if (len <= 0)break;char method[16] = {0};char url[128] = {0};rtsp_request_parse(buffer, method, url, &cseq, &rtpPort);printf("[%s]\n", buffer);char response[1024] = {0}; // 构造响应if (strcmp(method, "OPTIONS") == 0){rtsp_handle_OPTION(response, cseq);}else if (strcmp(method, "DESCRIBE") == 0){rtsp_handle_DESCRIBE(response, cseq);}else if (strcmp(method, "SETUP") == 0){rtsp_handle_SETUP(response, cseq, rtpPort);}else if (strcmp(method, "PLAY") == 0){rtsp_handle_PLAY(response, cseq);}else if (strcmp(method, "TEARDOWN") == 0){rtsp_handle_TEARDOWN(response, cseq);}else{snprintf(response, sizeof(response),"RTSP/1.0 501 Not Implemented\r\nCSeq: %d\r\n\r\n", cseq);}write(client_sock, response, strlen(response));}close(client_sock);return NULL;
}int main()
{int server_fd, client_fd;struct sockaddr_in address;int opt = 1;socklen_t addrlen = sizeof(address);// 创建套接字if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0){perror("socket failed");return -1;}// 设置套接字选项if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))){perror("setsockopt");return -1;}address.sin_family = AF_INET;address.sin_addr.s_addr = INADDR_ANY;address.sin_port = htons(RTSP_PORT);// 绑定端口if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0){perror("bind failed");return -1;}// 开始监听if (listen(server_fd, MAX_CLIENTS) < 0){perror("listen");return -1;}printf("RTSP Server listening on port %d\n", RTSP_PORT);// 主循环接受连接while (1){if ((client_fd = accept(server_fd, (struct sockaddr *)&address, &addrlen)) < 0){perror("accept");return -1;}printf("RTSP Server listening on port %d 1\n", RTSP_PORT);handle_client((void *)&client_fd);}return 0;
}

👉编译:

gcc rtspServer.c 

👉运行结果:

  • 1、执行./a.out 运行服务端;
  • 2、打开 Wireshark 软件抓取RTSP数据包(这一步根据需要决定做不做);
  • 3、在vlc操作,媒体->打开网络串流,输入rtsp://192.168.2.183:8554,把这里的ip地址换成你自己运行了a.out的电脑的ip,然后点击播放。
    在这里插入图片描述
  • 4、如果你使用了Wireshark抓包,就会得到整个rtsp交互的过程,如下图:
    在这里插入图片描述

在这里插入图片描述

🎄四、总结

本文介绍了实现一个最简单的 RTSP服务端 的一些步骤和细节,也提供了实现源码和运行结果,可以帮助读者快速了解RTSP服务端的实现。

在这里插入图片描述
如果文章有帮助的话,点赞👍、收藏⭐,支持一波,谢谢 😁😁😁

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/news/908616.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Python网页数据抓取常用的库及方法介绍

Python网页数据抓取常用的库及方法介绍 摘要:以下是Python网络数据抓取常用的6个库的详细介绍,包括它们的概述以及每个库中最常用的10个函数(如果某些库常用函数不足10个,则列出所有常用函数)。每个函数都附带功能描述、用法说明和使用示例。这些库在网络爬虫、数据…

6.5 note

一个很有意思的dfs模拟题_前序遍历 这个问题的话前置内容显然是字典序&#xff0c;什么是字典序呢&#xff1f; 顾名思义&#xff0c;就是词语在字典中的顺序&#xff0c;也就是我们最常说的a,abandon,ability&#xff08;我记得前三个是这个&#xff09; 这是一种字符串之间比…

day027-Shell自动化编程-基础

文章目录 1. 修改vim配置文件自动添加注释2. 故障案例&#xff1a;Windows上写的Shell脚本上传到Linux系统上运行报错3. 脚本运行方法4. 变量4.1 普通变量4.2 环境变量4.3 特殊变量4.4 案例&#xff1a;书写ping检查脚本&#xff0c;检查脚本传入的第一个参数4.5 面试题&#x…

2025年渗透测试面试题总结-腾讯[实习]科恩实验室-安全工程师(题目+回答)

安全领域各种资源&#xff0c;学习文档&#xff0c;以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具&#xff0c;欢迎关注。 目录 腾讯[实习]科恩实验室-安全工程师 一、网络与协议 1. TCP三次握手 2. SYN扫描原理 3. HTTPS证书机制 二…

人工智能赋能高中学科教学的应用与前景研究

一、引言 1.1 研究背景 在科技飞速发展的当下&#xff0c;人工智能&#xff08;Artificial Intelligence&#xff0c;简称 AI&#xff09;已成为全球瞩目的关键技术领域&#xff0c;深刻地改变着人们的生活、工作和学习方式。从智能家居设备到智能交通系统&#xff0c;从医疗…

八、【ESP32开发全栈指南:UDP客户端】

1. 环境准备 安装ESP-IDF v4.4 (官方指南)确保Python 3.7 和Git已安装 2. 创建项目 idf.py create-project udp_client cd udp_client3. 完整优化代码 (main/main.c) #include <string.h> #include "freertos/FreeRTOS.h" #include "freertos/task.h&…

Android Studio 解决首次安装时下载 Gradle 慢问题

1、问题描述 第一次安装 Android Studio 时&#xff0c; 新建工程后&#xff0c;在编译时会自动去下载 Gradle&#xff0c;但是一般都会下载失败&#xff0c;提示链接超时&#xff1a; Could not install Gradle distribution from https://services.gradle.org/distributions…

hive聚合函数多行合并

在数据仓库和大数据处理的场景中&#xff0c;Hive提供了强大的SQL查询能力&#xff0c;其中包括聚合函数用于处理和合并多行数据。本文将深入探讨Hive中的几种常见聚合函数及其在多行合并中的应用。 一、Hive中的常见聚合函数 Hive提供了多种聚合函数&#xff0c;这些函数可以…

关于物联网的基础知识(一)

成长路上不孤单&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a; 【14后&#x1f60a;///计算机爱好者&#x1f60a;///持续分享所学&#x1f60a;///如有需要欢迎收藏转发///&#x1f60a;】 今日分享关于物联网的基础知识&#xff08;一&a…

迁移科技3D视觉系统:重塑纸箱拆垛场景的智能革命

一、传统拆垛场景的困局与破局之道 在汽车零部件仓库中&#xff0c;每天有超过2万只异形纸箱需要拆垛分拣。传统人工拆垛面临三大挑战&#xff1a; 效率瓶颈&#xff1a;工人每小时仅能处理200-300件&#xff0c;且存在间歇性疲劳安全隐患&#xff1a;20kg以上重箱搬运导致年…

微软重磅发布Magentic UI,交互式AI Agent助手实测!

微软重磅发布Magentic UI,交互式AI Agent助手实测! 何为Magentic UI? Magentic UI 是微软于5.19重磅发布的开源Agent助手,并于24日刚更新了第二个版本0.04版 从官方的介绍来看,目标是打造一款 以人为中心 的智能助手,其底层由多个不同的智能体系统驱动,能够实现网页浏览…

Python实现快速排序的三种经典写法及算法解析

今天想熟悉一下python的基础写法&#xff0c;那就从最经典的快速排序来开始吧&#xff1a; 1、经典分治写法&#xff08;原地排序&#xff09; 时间复杂度&#xff1a;平均O(nlogn)&#xff0c;最坏O(n) 空间复杂度&#xff1a;O(logn)递归栈空间 特点&#xff1a;通过左右指针…

海康网络摄像头实时取帧转Opencv数组格式(h,w,3),已实现python、C#

海康摄像头取帧都是有官方demo的&#xff0c;但是将海康格式的数据转为Opencv格式的没有相关demo&#xff0c;而大部分深度学习图像检测算法(如YOLO)&#xff0c;都是用opencv格式的图像作为输入&#xff0c;因此将海康格式数据转为opencv格式兼容性更强 需要代码请私信联系&a…

职坐标IT教育物联网全栈开发实战:传感器到云平台全链路

物联网全栈开发涉及从终端感知到云端服务的全流程技术整合&#xff0c;其核心在于构建完整的“端-管-云-用”技术链条。为帮助开发者系统掌握这一能力&#xff0c;课程围绕四大模块展开&#xff1a;传感器数据采集与处理、通信协议适配与优化、云平台架构设计及跨平台应用开发。…

LUFFY(路飞): 使用DeepSeek指导Qwen强化学习

论文标题 Learning to Reason under Off-Policy Guidance 论文地址 https://arxiv.org/pdf/2504.14945 代码地址 https://github.com/ElliottYan/LUFFY 作者背景 上海人工智能实验室&#xff0c;西湖大学&#xff0c;南京大学&#xff0c;香港中文大学 动机 目前大模型…

Android Camera Hal中通过Neon指令优化数据拷贝

背景描述&#xff1a; Camera apk普通相机模式录像操作时&#xff0c;一般是同时请求两个流&#xff0c;即预览流和录像流。对于两个流输出图像格式和分辨率相同的情况下&#xff0c;是不是可以通过一个流拷贝得到另一个流的数据&#xff0c;进而节省掉一个Sensor输出处理两次…

WPS word 已有多级列表序号

wps的word中&#xff0c;原来已生成的文档里&#xff0c;已存在序号。比如&#xff0c;存在2、2.1、2.1.1、2.1.1.1、2.1.1.1.1 5层序号&#xff0c;而且已分为5级。但增加内容的时候&#xff0c;并不会自动增加序号&#xff0c;应该如何解决&#xff1f; 原来长这样&#xff…

从零开始制作小程序简单概述

以下是结合案例的“从零制作小红书风格小程序”的全流程指南&#xff0c;采用小红书爆款笔记的结构呈现&#xff0c;并附CSDN参考资源&#x1f447;&#xff1a; 一、核心开发步骤&#xff08;附工具推荐&#xff09; 账号与定位 ✅ 注册类型选择&#xff1a;个人店&#xff08…

【Go语言基础【13】】函数、闭包、方法

文章目录 零、概述一、函数基础1、函数基础概念2、参数传递机制3、返回值特性3.1. 多返回值3.2. 命名返回值3.3. 错误处理 二、函数类型与高阶函数1. 函数类型定义2. 高阶函数&#xff08;函数作为参数、返回值&#xff09; 三、匿名函数与闭包1. 匿名函数&#xff08;Lambda函…

网络编程之服务器模型与UDP编程

一、服务器模型 在网络通信中&#xff0c;通常要求一个服务器连接多个客户端 为了处理多个客户端的请求&#xff0c;通常有多种表现形式 1、循环服务器模型 一个服务器可以连接多个客户端&#xff0c;但同一时间只能连接并处理一个客户的请求 socket() 结构体 bind() listen() …