前言
随着网络技术的不断发展,实时流媒体传输在各种应用场景中越来越广泛地被应用。而实现实时流媒体传输的关键之一就是选择合适的协议来控制媒体流的传输和播放。在众多的流媒体传输协议中,RTSP(Real-Time Streaming Protocol)是一种被广泛应用的协议之一,它能够实现对实时流媒体的控制和传输,为用户提供了高质量的实时播放体验。
本篇博客将记录我学习 RTSP 协议的过程,以及探索 RTSP 媒体传输的交互过程。通过分析 RTSP 协议的基本原理和常见交互流程,以及实现一个简单的 demo 演示,希望能够为读者提供一些有价值的参考和思路,加深对流媒体传输技术的理解和应用。
一、什么是RTSP协议?
RTSP是一个实时传输流协议,是一个应用层的协议。通常说的RTSP 包括RTSP协议、RTP协议、RTCP协议,对于这些协议的作用简单的理解如下:
RTSP协议:负责服务器与客户端之间的请求与响应
RTP协议: 负责服务器与客户端之间传输媒体数据
RTCP协议:负责提供有关RTP传输质量的反馈,就是确保RTP传输的质量
三者的关系: rtsp并不会发送媒体数据,只是完成服务器和客户端之间的信令交互,rtp协议负责媒体数据传输,rtcp负责rtp数据包的监视和反馈。rtp和rtcp并没有规定传输层的类型,可以选择udp和tcp。Rtsp的传输层则要求是基于tcp。
二、代码实现
提示:下来通过一个ffmpeg 客户端拉流播放一个rtsp视频流的方式,讲解一下RTSP的交互过程。并通过Wireshark抓包分析。用到的工具包括ffmpeg命令行,wireshark
1.wireshark 抓包&RTSP媒体传输交互过程
下面是 RTSP 协议客户端与服务器之间进行流媒体传输的主要交互过程,包括 OPTIONS、DESCRIBE、SETUP 和 PLAY 四个步骤:
- OPTIONS:
客户端向服务器发送 OPTIONS 请求,查询服务器支持的方法和功能。
服务器收到 OPTIONS 请求后,返回支持的方法列表,如 OPTIONS、DESCRIBE、SETUP、PLAY 等。
这一步是为了协商客户端与服务器之间支持的协议和功能,以便后续的交互过程。
- DESCRIBE:
客户端向服务器发送 DESCRIBE 请求,获取媒体资源的描述信息,如媒体类型、编码格式、时长等。
服务器收到 DESCRIBE 请求后,返回媒体资源的描述信息,通常使用 SDP(Session Description Protocol)格式来描述。
客户端根据服务器返回的描述信息,准备好播放所需的媒体流。
- SETUP:
客户端向服务器发送 SETUP 请求,建立媒体流的传输通道,并指定传输的协议、端口等参数。
服务器收到 SETUP 请求后,根据客户端的请求建立媒体流的传输通道,并返回相关信息,如传输通道的状态、端口号等。
这一步是为了在客户端和服务器之间建立实际的媒体流传输通道,以便后续的流媒体传输。
- PLAY:
客户端向服务器发送 PLAY 请求,开始播放媒体流。
服务器收到 PLAY 请求后,开始向客户端发送媒体数据,实现流媒体的实时传输。
客户端接收到媒体数据后,根据播放时间戳进行播放,实现实时的流媒体播放。
2.demo代码实现:
代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <WinSock2.h>
#include <WS2tcpip.h>
#include <windows.h>
#include <string>
#pragma comment(lib, "ws2_32.lib")
#include <stdint.h>
#pragma warning( disable : 4996 )
#define SERVER_PORT 8554
#define SERVER_RTP_PORT 55532
#define SERVER_RTCP_PORT 55533
static int createTcpSocket()
{
int sockfd;
int on = 1;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
return -1;
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(on));
return sockfd;
}
static int bindSocketAddr(int sockfd, const char* ip, int port)
{
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = inet_addr(ip);
if (bind(sockfd, (struct sockaddr*)&addr, sizeof(struct sockaddr)