从0到1实现RTSP服务器
前言RTSP服务器是一个网络服务器,它使用实时流媒体传输协议(RTSP)提供对媒体流的控制。RTSP是一种应用层协议,用于控制多媒体会话,包括控制媒体会话中的媒体流的传输。这种控制包括播放、暂停、前进、后退等操作。
RTSP服务器通常用于流媒体系统,如网络电视直播,视频监控等。它可以将媒体数据流动态的发送给客户端,而无需将整个媒体文件先发给客户端。
RTSP服务器能够处理和响应客户端发送的RTSP请求,并管理媒体流的传输。它还可以动态地为每个客户端会话选择传输协议(如RTP、UDP或TCP)以及数据格式(如MPEG、AVI或QuickTime)。
一、需求功能需求
RTSP服务器需要要有UDP和TCP两种连接方式,即能够通过以下两种方式来获取服务器的媒体数据。123ffplay -i rtsp://127.0.0.1:8554ffplay -i -rtsp_transport tcp rtsp://127.0.0.1:8554
服务器能够上传视频 (H264) 和音频 (AAC),且用户在获取媒体数据时,音画需要同步性能需求支持多用户 (5+) 同时访问
二、架构想要实现高性能RT ...
RTSP与SDP
简介RTSP是RTP中负责定义具体的控制消息、操作方法、状态码等的协议,但它本身并不传输数据,而是必须依赖于下层传输协议所提供的某些服务。
RTSP 可以实现如下的指令集,这些指令是面向客户端编写的。12345enum Method{ OPTIONS, DESCRIBE, ANNOUNCE, SETUP, PLAY, RECORD, PAUSE, GET_PARAMETER, TEARDOWN, NONE,};
本文仅实现下面的子集12345enum Method{ OPTIONS, DESCRIBE, SETUP, PLAY, NONE,};
指令集每个指令对应一个字符串,在每次调用之后,需要将字符串发送给客户端。
为了代码阅读方便、美观,每一个字符串最后都省略了"\r\n"
每个指令都有相同的头部:
12"RTSP/1.0 200 OK""CSeq: %u"
RTSP/1.0 200 OK 用于回应请求成功
CSeq 代表该消息的序列号
和相同的尾部:1& ...
AAC
AACAAC(Advanced Audio Coding)是一种声音数据的文件压缩格式,分为 ADTS 和 ADIF 两种文件格式。
ADIF 的解码必须从明确的定义位置开始执行,故常用于磁盘文件
ADTS 是具有同步字的比特流,解码可以从流的任意位置开始
因此音频流传输一般采用了 ADTS
ADTS帧结构与一般帧并无差别[帧头]+[帧数据]
ADTS 头格式如下:
12345678910111213141516171819202122232425262728struct AdtsHeader{ unsigned int syncword; // 12位,同步字 '1111 1111 1111',一个ADTS帧开始的标志 uint8_t id; // 1位,0代表MPEG-4,1代表MPEG-2 uint8_t layer; // 2位,必须为0 uint8_t protectionAbsent; // 1位,1代表没有CRC,0代表有CRC uint8_t profile; ...
RTP与RTCP
RTP实时传输协议(Real-time Transport Protocol 或 RTP)是一个网络协议,用于在网络上提供端对端的实时数据传输,如音频、视频或者模拟数据。此协议在自由软件和开源环境中应用广泛。与其他传输层协议相比,RTP 并不保证传输的可靠性,但提供了实时处理的支持,如QoS(质量服务),抖动补偿,跨载体交互等。
RTP头格式如下:1234567891011 0 1 2 3+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+|V=2|P|X| CC |M| PT | sequence number |+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+| timestamp ...
回调函数中的void指针
函数指针主要用于将函数定义为参数类型,便于传入1234// C++typedef std::function<void(void*)>CallBack;// Ctypedef void (*Callback)(void*);
Event1234void Event::handleEvent(Callback cb, void* arg){ cb(arg);}
TaskTask主要定义了 Event 要使用的回调函数123456789101112void Task::callback(void* arg){ Task* task = (Task*)arg; Task->handle();}// 可能根据 Task 的类型变化而变化void Task::handle(){ // do something // 特别是要使用类成员等非 static 内容等情况}
Server根据使用场景不同,可以通过回调函数的方式,在业务场景末端(顶层)如Server,灵活调整每个 Event 处理的事务12345678 ...
ifdef/ifndef与C声明冲突
ifdef 若标识符被定义,则编译ifndef 若标识符未被定义,则不编译ifdef1234567#ifdef WIN32// Windows环境下编译 printf("windows")#else// 非Windows环境下编译 printf("linux")#endif WIN32
ifndef1234567#ifndef WIN32// 非Windows环境下编译 printf("linux")#else// Windows环境下编译 printf("windows")#endif !WIN32
有了预备知识,对于.h头文件的规则也易于理解了Person.h1234#ifndef ALI5669_PROJECT_PERSON_H#define ALI5669_PROJECT_PERSON_H// Person.h内容#endif !ALI5669_PROJECT_PERSON_H在不同文件中同时引用了同一个头文件,当这些文件要合成一个可执行文件时就出现了重复引用冲突使用这个方法,就可以让.h文 ...
H264中的NAL、NALU,以及RTP封装方式
H.264主要由视频编码层(VCL)和网络适配层(NAL)组成NAL包格式如下12345 NAL Header NALU 0 1 2 3 4 5 6 7 8 9+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-++-+-+-+-+-+-+-+-+|F|NRI| Type | a single NAL unit ... |+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-++-+-+-+-+-+-+-+-+NAL包由头部和NALU组成,VCL有效载荷数据被封装在NALU中123456789101112struct NalHeader{ uint8_t type : 5; // 5 位,指出NALU 的类型 SEI:6, SPS: 7, PPS: 8 uint8_t nal_ref_idc : 2; // 2 位,用来指示该NALU 的重要性等级。值越大,表示当前NALU越重要。具体大于0 时取何值,没有明确规定 uint8_t forbidd ...
C struct 中字节的存储方式
在学习音视频时学习到了RTP协议,遂定义了RTP struct
RTP头格式如下:123456789101112 0 1 2 3 7 6 5 4 3 2 1 0|7 6 5 4 3 2 1 0|7 6 5 4 3 2 1 0|7 6 5 4 3 2 1 0+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+|V=2|P|X| CC |M| PT | sequence number |+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+| timestamp |+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ...
VS C++ LNK2019 无法解析的外部符号
配置项目第三方库时报错:LNK2019 无法解析的外部符号 _sws_scale,函数 “int __cdecl vPlayer_sd12(char *)” (?vPlayer_sd12@@YAHPAD@Z) 中引用了该符号
按照网上教程配置多次仍未成功SDL2给出了两个编译环境(x86 x64)生成的lib,而ffmpeg仅有x64环境下的lib。来回更换SDL2的lib后发现忘记更换.dll文件的版本(x86),与.lib文件不匹配(x64)导致了LNK2019错误