😁博客主页😁:🚀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_UDP
,RTP_OVER_TCP
,容易混淆; - RTSP协议的方法有很多,但一个最简单的RTSP服务器可以只提供这5个方法:
OPTIONS、DESCRIBE、SETUP、PLAY、TEARDOWN
。 - RTSP协议是通过 请求报文 与 响应报文 来传输数据的,这些报文都是可读的文本,需要按协议要求解析和填写。
🎄二、实现步骤、实现细节
这个小写介绍一些实现步骤和细节,下个小节会提供源码,可以结合着源码看帮助理解消化。
✨2.1、使用 TCP 实现RTSP服务器
RTSP协议是使用TCP作为传输层协议的,所以我们实现RTSP服务器的第一个步骤可以是使用创建一个TCP服务器,绑定的监听端口一般是554
或8554
。下面是创建一个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服务端的实现。
如果文章有帮助的话,点赞👍、收藏⭐,支持一波,谢谢 😁😁😁