网上关于rtsp的文章很多,但大多是抽象的理论介绍,从理论学习到实际上手开发往往还有一段距离。然而,没有实际开发经验的支撑,理论又很难理解到位。
本系列文章将从流媒体协议的基础原理开始,通过抓包分析,并结合具体的代码例程,以[原理]->[抓包]->[代码]相结合的方式,循序渐进由浅入深的介绍rtsp/rtp/rtcp开发相关的内容。
希望通过本系列内容的学习,能让大家快速入门流媒体开发需要掌握的技能。
欢迎大家关注[断点实验室]流媒体开发系列文章。
rtsp协议开发指南
rtsp协议格式解析
rtsp协议报文解析-请求行解析
rtsp协议报文解析-首部字段解析
rtsp协议在语法及一些消息参数等方面与http协议类似,这里之所以引入http的相关内容,是为了通过我们熟悉的http协议,来帮助大家快速理解rtsp协议的工作原理。
rtsp是一种基于文本的协议,在语法及消息参数等方面与http协议类似,它通过一系列格式化的文本信息,传递与实时视频流传输相关的一些控制命令及状态参数。
rtsp采用与http协议类似的报文结构,包括请求行、首部行以及实体主体三部分,因此可以通过抓包很直观的对协议内容进行分析,下图为http协议报文格式。

rtsp协议引入了大多数http1.1的状态码,来反馈当前的响应状态,客户端根据这些状态码来判断服务器状态。
static const rtsp_msg_int2str_tbl_s rtsp_msg_status_code_tbl[] = {
{100, 0, "Continue"},
{200, 0, "OK"},
{201, 0, "Created"},
{250, 0, "Low on Storage Space"},
{300, 0, "Multiple Choices"},
{301, 0, "Moved Permanently"},
{302, 0, "Moved Temporarily"},
{303, 0, "See Other"},
{305, 0, "Use Proxy"},
{400, 0, "Bad Request"},
{401, 0, "Unauthorized"},
{402, 0, "Payment Required"},
{403, 0, "Forbidden"},
{404, 0, "Not Found"},
{405, 0, "Method Not Allowed"},
{406, 0, "Not Acceptable"},
{407, 0, "Proxy Authentication Required"},
{408, 0, "Request Timeout"},
{410, 0, "Gone"},
{411, 0, "Length Required"},
{412, 0, "Precondition Failed"},
{413, 0, "Request Entity Too Large"},
{414, 0, "Request-URI Too Long"},
{415, 0, "Unsupported Media Type"},
{451, 0, "Invalid parameter"},
{452, 0, "Illegal Conference Identifier"},
{453, 0, "Not Enough Bandwidth"},
{454, 0, "Session Not Found"},
{455, 0, "Method Not Valid In This State"},
{456, 0, "Header Field Not Valid"},
{457, 0, "Invalid Range"},
{458, 0, "Parameter Is Read-Only"},
{459, 0, "Aggregate Operation Not Allowed"},
{460, 0, "Only Aggregate Operation Allowed"},
{461, 0, "Unsupported Transport"},
{462, 0, "Destination Unreachable"},
{500, 0, "Internal Server Error"},
{501, 0, "Not Implemented"},
{502, 0, "Bad Gateway"},
{503, 0, "Service Unavailable"},
{504, 0, "Gateway Timeout"},
{505, 0, "RTSP Version Not Supported"},
{551, 0, "Option not support"},
};
http协议是无状态的协议,它的每个请求都是完全独立的,每个请求包含了处理这个请求所需的完整的数据,发送请求不涉及状态变更。
例如,当浏览器第一次发送请求给服务端时,服务端响应了,如果同一浏览器向服务端再次发送相同请求时,服务端还是会执行相同的响应(当然服务端可以通过cookie记录与客户端通信的状态信息,这是另一个问题)。
与http服务端不同的是,rtsp服务端几乎在所有情况下都需要默认维护状态,这与http的无状态性质相反。
此外,rtsp与http的根本不同在于,http的数据传输(如照片等)是通过http协议本身实现的,客户端发出资源请求,服务端在响应中传递数据。在rtsp中,视频流是通过rtp协议传输的,rtsp可以设置参数并在传输结束后很长时间内继续控制媒体流。
与http协议类似,rtsp协议也是面向文本的(text-oriented)协议,报文内容可以看作是由ASCII码组成字符缓存,对协议的封装与解析,就是按照协议的报文格式,组装或解析对应的字符串码。
rtsp报文由三部分组成,即开始行、首部行和实体主体。在请求报文中,开始行就是请求行。rtsp请求报文的结构如下图所示。

rtsp响应报文的结构如下图所示。

报文中的部分字段(如url字段等)长度是不固定的,首部行包含的首部字段个数也不是固定的,因此,协议通过空格及CRLF将各个字段内容信息分割开来,用空格作为一个字段结束的标识,用CRLF作为一行结束的标识,协议的封装解析可以以此为依据进行设计。
协议报文方法包括OPTIONS,DESCRIBE,SETUP,PLAY,TEARDOWN等,具体含义如下表所示,其中C标识客户端,S标识服务器。
| 方向 | 方法 | 描述 | 作用 |
|---|---|---|---|
| C->S | OPTION request | 询问对端有哪些可用方法 | OPTION用于查询服务器端可用方法 |
| S->C | OPTION response | 回应S所有可用方法 | |
| C->S | DESCRIBE request | 取得S媒体描述信息 | DESCRIBE用于取得媒体描述信息 |
| S->C | DESCRIBE response | 回应媒体描述信息 | |
| C->S | SETUP request | 请求建立会话连接 | SETUP用于建立RTSP会话连接 |
| S->C | SETUP response | 建立会话连接 | |
| C->S | PLAY request | C请求S开始发送数据 | PLAY用于请求开始发送数据 |
| S->C | PLAY response | S回应该请求的信息 | |
| C->S | TEARDOWN request | 请求关闭会话 | TEARDOWN用于结束RTSP会话 |
| S->C | TEARDOWN response | S回应退出请求 |
与http协议类似,rtsp协议的首部字段是构成rtsp报文的要素之一,它为客户端和服务端提供报文交互所需的报文序列号、使用的语言、报文日期等内容,如:
下表列出了rtsp协议使用的首部行字段,在type列中类型"g"表示在请求和响应报文中都会用到的通用字段,"R"表示请求报文字段,"r"表示响应报文字段,"e"表示实体主体字段。
support列中的"req."字段表示对应的首部行字段,在相关类型的报文中必须要包含的,而标记为"opt."的字段是可选的,如:
methods列中列出的方法为各个首部行字段实际使用的报文场景,如Range字段一般应用于PLAY, PAUSE, RECORD这三个报文中,表示视频流在PLAY, PAUSE, RECORD这三个操作中的作用范围。
| Header | type | support | methods |
|---|---|---|---|
| Accept | R | opt. | entity |
| Accept-Encoding | R | opt. | entity |
| Accept-Language | R | opt. | all |
| Allow | r | opt. | all |
| Authorization | R | opt. | all |
| Bandwidth | R | opt. | all |
| Blocksize | R | opt. | all but OPTIONS, TEARDOWN |
| Cache-Control | g | opt. | SETUP |
| Conference | R | opt. | SETUP |
| Connection | g | req. | all |
| Content-Base | e | opt. | entity |
| Content-Encoding | e | req. | SET_PARAMETER |
| Content-Encoding | e | req. | DESCRIBE, ANNOUNCE |
| Content-Language | e | req. | DESCRIBE, ANNOUNCE |
| Content-Length | e | req. | SET_PARAMETER, ANNOUNCE |
| Content-Length | e | req. | entity |
| Content-Location | e | opt. | entity |
| Content-Type | e | req. | SET_PARAMETER, ANNOUNCE |
| Content-Type | r | req. | entity |
| CSeq | g | req. | all |
| Date | g | opt. | all |
| Expires | e | opt. | DESCRIBE, ANNOUNCE |
| From | R | opt. | all |
| If-Modified-Since | R | opt. | DESCRIBE, SETUP |
| Last-Modified | e | opt. | entity |
| Proxy-Require | R | req. | all |
| Public | r | opt. | all |
| Range | R | opt. | PLAY, PAUSE, RECORD |
| Range | r | opt. | PLAY, PAUSE, RECORD |
| Referer | R | opt. | all |
| Require | R | req. | all |
| Retry-After | r | opt. | all |
| RTP-Info | r | req. | PLAY |
| Scale | Rr | opt. | PLAY, RECORD |
| Session | Rr | req. | all but SETUP, OPTIONS |
| Server | r | opt. | all |
| Speed | Rr | opt. | PLAY |
| Transport | Rr | req. | SETUP |
| Unsupported | r | req. | all |
| User-Agent | R | opt. | all |
| Via | g | opt. | all |
| WWW-Authenticate | r | opt. | all |
以上内容对报文首部行字段做了简要的介绍,限于篇幅的原因无法对每个字段进行深入的介绍,感兴趣的读者可以参考rtsp协议规范RFC2326中[12. Header Field Definitions]中的相关内容,也可直接参考http协议中关于这部分内容的相关描述,网上应该很容易找到,后续文章会对代码中涉及的相关信息再做进一步的解读。
协议的基本原理讲完了,下面带大家分析协议的交互过程及抓包分析。
一个基本的rtsp会话流程,包括客户端向服务端请求支持的操作类型,发送建立会话请求,请求发送流媒体数据等,会话流程如下图所示。

下面的抓包截图反映了客户端与服务端的实际交互过程。

C->S:DESCRIBE request //请求S提供的媒体描述信息

S->C:DESCRIBE response //S回应媒体描述信息,一般是sdp格式信息

C->S:SETUPrequest //通过Transport头字段列出可接受的传输选项,请求S建立会话

S->C:SETUPresponse //S建立会话,通过Transport头字段返回选择的具体转输选项,并返回建立的Session ID

C->S:PLAY request //C请求S开始发送数据

S->C:PLAYresponse //S回应该请求的信息

S->C:发送流媒体数据 //通过RTP协议传送数据

前面内容介绍了协议的基础原理及抓包分析,下面我们来结合代码继续讲解。
这里我们以流媒体开发系列文章rtsp协议入门指南中引入的推流端代码为例,讲述rtsp协议报文的解析及封装过程。
推流端代码按照rtsp协议报文格式,设计一个存储协议各格式化信息的数据结构,数据结构中的各个结构分别描述了格式化报文信息中的相应内容。
报文的解析及组装过程,可以看作在依照协议文本格式及语法规则的情况下,在报文文本信息与协议数据结构之间相互转换,即
报文解析:从网络接口读报文信息到缓存,将缓存中的文本信息按照不同报文格式提取出来,存储到协议数据结构中,即字符串码->协议数据结构
报文组装:创建报文缓存,根据服务端当前的状态将待发送的报文内容存储到协议数据结构中,最后将协议数据结构中的内容,按照报文格式组装到报文字符串码缓存中并发送,即协议数据结构->字符串码
下面我们来看下协议请求行响应行数据结构具体实现。
代码通过一系列的枚举类型,定义了协议的操作类型、传输方式、版本信息、报文类型等信息,并以此为基础定义了请求行、响应行数据结构。
服务端支持的操作类型
typedef enum __rtsp_msg_method_e {
RTSP_MSG_METHOD_OPTIONS = 0,//用于查询服务器端可用方法
RTSP_MSG_METHOD_DESCRIBE,//用于取得媒体描述信息
RTSP_MSG_METHOD_SETUP,//用于指定建立RTSP会话连接的传输机制
RTSP_MSG_METHOD_PLAY,//用于请求开始发送数据
RTSP_MSG_METHOD_RECORD,//用于请求服务端录制指定时间范围媒体数据
RTSP_MSG_METHOD_PAUSE,//用于暂停RTSP会话
RTSP_MSG_METHOD_TEARDOWN,//用于结束RTSP会话
RTSP_MSG_METHOD_ANNOUNCE,//用于把要推送的音视频信息通过sdp格式传给对端
RTSP_MSG_METHOD_SET_PARAMETER,//用于设置流参数
RTSP_MSG_METHOD_GET_PARAMETER,//用于获取流参数
RTSP_MSG_METHOD_REDIRECT,//用于重定向
RTSP_MSG_METHOD_BUTT,//未匹配到类型
} rtsp_msg_method_e;
rtsp传输方式
typedef enum __rtsp_msg_uri_scheme_e {
RTSP_MSG_URI_SCHEME_RTSP = 0,//TCP传输
RTSP_MSG_URI_SCHEME_RTSPU,//UDP传输
RTSP_MSG_URI_SCHEME_BUTT,//未匹配到类型
} rtsp_msg_uri_scheme_e;
协议url地址
typedef struct __rtsp_msg_uri_s {
rtsp_msg_uri_scheme_e scheme;//RTSP传输方式
uint16_t port;//端口号
char ipaddr[32];//ip地址
char abspath[64];//url <path>
} rtsp_msg_uri_s;
协议版本
typedef enum __rtsp_msg_version_e {
RTSP_MSG_VERSION_1_0 = 0,
RTSP_MSG_VERSION_BUTT,
} rtsp_msg_version_e;
请求行结构
typedef struct __rtsp_msg_request_line_s {
rtsp_msg_method_e method;//方法
rtsp_msg_uri_s uri;//url
rtsp_msg_version_e version;//版本
} rtsp_msg_request_line_s;
响应行结构
typedef struct __rtsp_msg_response_line_s {
rtsp_msg_version_e version;//版本
uint32_t status_code;//状态码
} rtsp_msg_response_line_s;
这里的报文类型增加了RTSP_MSG_TYPE_INTERLEAVED这种类型,表示复用rtsp链路,将rtp/rtcp报文发送到rtsp链路中进行传输,rtp/rtcp不再新建通信链路。
rtsp报文类型
typedef enum __rtsp_msg_type_e {
RTSP_MSG_TYPE_REQUEST = 0,//rtsp请求报文
RTSP_MSG_TYPE_RESPONSE,//rtsp应答报文
RTSP_MSG_TYPE_INTERLEAVED,//rtp/rtcp over rtsp
RTSP_MSG_TYPE_BUTT,//未匹配到类型
} rtsp_msg_type_e;
rtsp interleaved frame,用于将rtp/rtcp报文发送到rtsp链路中进行传输
typedef struct __rtsp_msg_interleaved_line_s {
uint8_t channel;//channel identifier(rtp/rtcp)
uint16_t length;//the length of rtp/rtcp packet
uint8_t reserved;
} rtsp_msg_interleaved_line_s;
rtsp请求-应答序列号
typedef struct __rtsp_msg_cseq_s {
uint32_t cseq;
} rtsp_msg_cseq_s;
日期
typedef struct __rtsp_msg_date_s {
char http_date[32];
} rtsp_msg_date_s;
会话id
typedef struct __rtsp_msg_session_s {
uint32_t session;
} rtsp_msg_session_s;
rtp传输层类型
typedef enum __rtsp_msg_transport_type_e {
RTSP_MSG_TRANSPORT_TYPE_RTP_AVP = 0,//RTPoverUDP
RTSP_MSG_TRANSPORT_TYPE_RTP_AVP_TCP,//RTPoverTCP
RTSP_MSG_TRANSPORT_TYPE_BUTT,
} rtsp_msg_transport_type_e;
header Transport字段
typedef struct __rtsp_msg_transport_s {
rtsp_msg_transport_type_e type;//rtp传输层类型(tcp/udp)
uint32_t flags;//以下flag列表实际取值
#define RTSP_MSG_TRANSPORT_FLAG_SSRC (1<<0)
#define RTSP_MSG_TRANSPORT_FLAG_UNICAST (1<<1)
#define RTSP_MSG_TRANSPORT_FLAG_MULTICAST (1<<2)
#define RTSP_MSG_TRANSPORT_FLAG_CLIENT_PORT (1<<3)
#define RTSP_MSG_TRANSPORT_FLAG_SERVER_PORT (1<<4)
#define RTSP_MSG_TRANSPORT_FLAG_INTERLEAVED (1<<5)
uint32_t ssrc;//rtp ssrc
uint16_t client_port;//rtcp is rtp + 1
uint16_t server_port;
uint8_t interleaved;//rtp/rtcp channel range in rtsp interleaved frame
} rtsp_msg_transport_s;
服务端时间类型结构
typedef enum __rtsp_msg_time_type_e {
RTSP_MSG_TIME_TYPE_SMPTE = 0,//SMPTE timecode
RTSP_MSG_TIME_TYPE_NPT,//network time protocol
RTSP_MSG_TIME_TYPE_UTC,//Coordinated Universal Time
RTSP_MSG_TIME_TYPE_BUTT,//未匹配到类型
} rtsp_msg_time_type_e;
SMPTE时间码,主要参数格式是:Hours:Minutes:Second:frames,通常用时间码来识别和记录视频数据流中的每一帧,从一段视频的起始帧到终止帧,其间的每一帧都有一个唯一的时间码地址
typedef struct __rtsp_msg_time_smpte_s {
//10:07:33:05.01
uint32_t seconds; //10*3600 + 07*60 + 33
uint32_t subframes; //05*100 + 01
} rtsp_msg_time_smpte_s;
网络时间结构
typedef struct __rtsp_msg_time_npt_s {
//123.45
uint32_t secords;//123
uint32_t usecords;//45
} rtsp_msg_time_npt_s;
协调世界时结构
typedef struct __rtsp_msg_time_utc_s {
//19961108T142730.25Z
uint32_t secords;//1996/11/08 14:27:30 - 1900/1/1 0:0:0
uint32_t usecords;//25
} rtsp_msg_time_utc_s;
Range Rr opt. PLAY,PAUSE,RECORD 时间范围
typedef struct __rtsp_msg_range_s {
rtsp_msg_time_type_e type;//服务端时间类型结构
union __start_u {
rtsp_msg_time_smpte_s smpte;//SMPTE时间码
rtsp_msg_time_npt_s npt;//网络时间结构
rtsp_msg_time_utc_s utc;//协调世界时结构
} start;//开始时间
union __end_u {
rtsp_msg_time_smpte_s smpte;//SMPTE时间码
rtsp_msg_time_npt_s npt;//网络时间结构
rtsp_msg_time_utc_s utc;//协调世界时结构
} end;//结束时间
} rtsp_msg_range_s;
Content-Type类型
typedef enum __rtsp_msg_content_type_e {
RTSP_MSG_CONTENT_TYPE_SDP = 0,//sdp格式
RTSP_MSG_CONTENT_TYPE_RTSL,//rtsl格式
RTSP_MSG_CONTENT_TYPE_MHEG,//mheg格式(Multimedia and Hypermedia Expert Group)
RTSP_MSG_CONTENT_TYPE_BUTT,
} rtsp_msg_content_type_e;
Accept字段
typedef struct __rtsp_msg_accept_s {
uint32_t accept;//Accept取值
#define RTSP_MSG_ACCEPT_SDP (1<<RTSP_MSG_CONTENT_TYPE_SDP)//1<<0
#define RTSP_MSG_ACCEPT_RTSL (1<<RTSP_MSG_CONTENT_TYPE_RTSL)//1<<1
#define RTSP_MSG_ACCEPT_MHEG (1<<RTSP_MSG_CONTENT_TYPE_MHEG)//1<<2
} rtsp_msg_accept_s;
Authorization字段
typedef struct __rtsp_msg_authorization_s {
char authorization[128];
} rtsp_msg_authorization_s;
User-Agent字段
typedef struct __rtsp_msg_user_agent_s {
char user_agent[64];
} rtsp_msg_user_agent_s;
header public字段
typedef struct __rtsp_msg_public_s {
uint32_t public_;//header public字段取值
#define RTSP_MSG_PUBLIC_OPTIONS (1<<RTSP_MSG_METHOD_OPTIONS)//1<<0
#define RTSP_MSG_PUBLIC_DESCRIBE (1<<RTSP_MSG_METHOD_DESCRIBE)//1<<1
#define RTSP_MSG_PUBLIC_SETUP (1<<RTSP_MSG_METHOD_SETUP)//1<<2
#define RTSP_MSG_PUBLIC_PLAY (1<<RTSP_MSG_METHOD_PLAY)//1<<3
#define RTSP_MSG_PUBLIC_RECORD (1<<RTSP_MSG_METHOD_RECORD)//1<<4
#define RTSP_MSG_PUBLIC_PAUSE (1<<RTSP_MSG_METHOD_PAUSE)//1<<5
#define RTSP_MSG_PUBLIC_TEARDOWN (1<<RTSP_MSG_METHOD_TEARDOWN)//1<<6
#define RTSP_MSG_PUBLIC_ANNOUNCE (1<<RTSP_MSG_METHOD_ANNOUNCE)//1<<7
#define RTSP_MSG_PUBLIC_SET_PARAMETER (1<<RTSP_MSG_METHOD_SET_PARAMETER)//1<<8
#define RTSP_MSG_PUBLIC_GET_PARAMETER (1<<RTSP_MSG_METHOD_GET_PARAMETER)//1<<1
#define RTSP_MSG_PUBLIC_REDIRECT (1<<RTSP_MSG_METHOD_REDIRECT)//1<<10
} rtsp_msg_public_s;
RTP-Info字段结构
typedef struct __rtsp_msg_rtp_subinfo_s {
rtsp_msg_uri_s url;//__param_u对应的url地址
uint32_t isseq;
union __param_u {
uint32_t rtptime;//rtp时间戳
uint32_t seq;//rtp流数据包序列号
} param;
} rtsp_msg_rtp_subinfo_s;
RTP-Info结构队列
typedef struct __rtsp_msg_rtp_info_s {
uint32_t ninfos;//队列序号
rtsp_msg_rtp_subinfo_s **info_array;//RTP-Info结构队列指针
} rtsp_msg_rtp_info_s;
Server字段
typedef struct __rtsp_msg_server_s {
char server[64];
} rtsp_msg_server_s;
Content-Length 实体主体长度
typedef struct __rtsp_msg_content_length_s {
uint32_t length;
} rtsp_msg_content_length_s;
Content-Type 实体主体类型
typedef struct __rtsp_msg_content_type_s {
rtsp_msg_content_type_e type;//Content-Type类型类型
} rtsp_msg_content_type_s;
将协议请求行、响应行、首部行等数据结构再进一步封装,就得到了用于描述rtsp报头信息的rtsp_msg_hdr_s、描述rtsp报文实体主体的rtsp_msg_body_s,以及最终描述rtsp报文信息的rtsp_msg_s结构。
rtsp报头结构 message header
typedef struct __rtsp_msg_hdr_s {
union __start_line_u {
rtsp_msg_request_line_s reqline;//请求行
rtsp_msg_response_line_s resline;//响应行
rtsp_msg_interleaved_line_s interline;//rtsp interleaved frame
} startline;//开始行
//general-headers field 通用首部字段
rtsp_msg_cseq_s *cseq;//序列号
rtsp_msg_date_s *date;//日期
rtsp_msg_session_s *session;//会话id
rtsp_msg_transport_s *transport;//header Transport字段
rtsp_msg_range_s *range;//时间范围
//request-headers field 请求首部字段
rtsp_msg_accept_s *accept;//header Accept字段
rtsp_msg_authorization_s *authorization;//header Authorization字段
rtsp_msg_user_agent_s *user_agent;//header User-Agent字段
//response-headers field 响应首部字段
rtsp_msg_public_s *public_;//header public字段
rtsp_msg_rtp_info_s *rtp_info;//header RTP-Info字段
rtsp_msg_server_s *server;//header Server字段
//entity-headers field 实体主体首部字段
rtsp_msg_content_length_s *content_length;//header Content-Length 实体主体长度
rtsp_msg_content_type_s *content_type;//header Content-Type 实体主体类型
} rtsp_msg_hdr_s;
rtsp报文实体主体
typedef struct __rtsp_msg_body_s {
void *body;//报文实体主体指针
} rtsp_msg_body_s;
rtsp报文结构
typedef struct __rtsp_msg_s {
rtsp_msg_type_e type;//报文类型
rtsp_msg_hdr_s hdrs;//报头结构
rtsp_msg_body_s body;//实体主体
} rtsp_msg_s;
至此,我们通过一系列的结构体,完成了对协议报文结构的描述。通过下面的方法,从网络接口中读取报文并解析到报文结构体对象中,或从报文结构体对象中提取报文信息并组装发送到网络接口,完成与客户端的交互。
int rtsp_msg_set_request (rtsp_msg_s *msg, rtsp_msg_method_e mt, const char *ipaddr, const char *abspath);
int rtsp_msg_set_response (rtsp_msg_s *msg, int status_code);
int rtsp_msg_get_cseq (const rtsp_msg_s *msg, uint32_t *cseq);
int rtsp_msg_set_cseq (rtsp_msg_s *msg, uint32_t cseq);
int rtsp_msg_get_session (const rtsp_msg_s *msg, uint32_t *session);
int rtsp_msg_set_session (rtsp_msg_s *msg, uint32_t session);
int rtsp_msg_get_date (const rtsp_msg_s *msg, char *date, int len);
int rtsp_msg_set_date (rtsp_msg_s *msg, const char *date);
int rtsp_msg_set_transport_udp (rtsp_msg_s *msg, uint32_t ssrc, int client_port, int server_port);
int rtsp_msg_set_transport_tcp (rtsp_msg_s *msg, uint32_t ssrc, int interleaved);
int rtsp_msg_get_accept (const rtsp_msg_s *msg, uint32_t *accept);
int rtsp_msg_set_accept (rtsp_msg_s *msg, uint32_t accept);
int rtsp_msg_get_user_agent (const rtsp_msg_s *msg, char *user_agent, int len);
int rtsp_msg_set_user_agent (rtsp_msg_s *msg, const char *user_agent);
int rtsp_msg_get_public (const rtsp_msg_s *msg, uint32_t *public_);
int rtsp_msg_set_public (rtsp_msg_s *msg, uint32_t public_);
int rtsp_msg_get_server (const rtsp_msg_s *msg, char *server, int len);
int rtsp_msg_set_server (rtsp_msg_s *msg, const char *server);
int rtsp_msg_get_content_type (const rtsp_msg_s *msg, int *type);
int rtsp_msg_set_content_type (rtsp_msg_s *msg, int type);
int rtsp_msg_get_content_length (const rtsp_msg_s *msg, int *length);
int rtsp_msg_set_content_length (rtsp_msg_s *msg, int length);
uint32_t rtsp_msg_gen_session_id (void);
本篇为流媒体开发系列文章的第二篇,通过引入http协议,介绍了rtsp协议报文格式,通过抓包分析了报文交互流程,并最终结合代码探讨背后的实现机制。
在下一篇文章中,我们会结合rtsp报文数据结构以及4.3节中提到的方法,讲述如果对请求报文进行解析,以及如何根据报文数据结构组织应答报文,欢迎大家继续关注。
ffmpeg播放器实现详解 - FFmpeg编译安装
ffmpeg播放器实现详解 - FFPlay源码编译
ffmpeg播放器实现详解 - 框架搭建
ffmpeg播放器实现详解 - 视频显示
ffmpeg播放器实现详解 - 音频播放
ffmpeg播放器实现详解 - 创建线程
ffmpeg播放器实现详解 - 视频同步控制
ffmpeg播放器实现详解 - 音频同步控制
ffmpeg播放器实现详解 - 快进快退控制
// 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
我有一个字符串input="maybe(thisis|thatwas)some((nice|ugly)(day|night)|(strange(weather|time)))"Ruby中解析该字符串的最佳方法是什么?我的意思是脚本应该能够像这样构建句子:maybethisissomeuglynightmaybethatwassomenicenightmaybethiswassomestrangetime等等,你明白了......我应该一个字符一个字符地读取字符串并构建一个带有堆栈的状态机来存储括号值以供以后计算,还是有更好的方法?也许为此目的准备了一个开箱即用的库?
我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h
我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i
我正在使用ruby1.9解析以下带有MacRoman字符的csv文件#encoding:ISO-8859-1#csv_parse.csvName,main-dialogue"Marceu","Giveittohimóhe,hiswife."我做了以下解析。require'csv'input_string=File.read("../csv_parse.rb").force_encoding("ISO-8859-1").encode("UTF-8")#=>"Name,main-dialogue\r\n\"Marceu\",\"Giveittohim\x97he,hiswife.\"\
这个问题在这里已经有了答案:Railsformattingdate(4个答案)关闭4年前。我想格式化Time.Now函数以显示YYYY-MM-DDHH:MM:SS而不是:“2018-03-0909:47:19+0000”该函数需要放在时间中.现在功能。require‘roo’require‘roo-xls’require‘byebug’file_name=ARGV.first||“Template.xlsx”excel_file=Roo::Spreadsheet.open(“./#{file_name}“,extension::xlsx)xml=Nokogiri::XML::Build
我喜欢使用Textile或Markdown为我的项目编写自述文件,但是当我生成RDoc时,自述文件被解释为RDoc并且看起来非常糟糕。有没有办法让RDoc通过RedCloth或BlueCloth而不是它自己的格式化程序运行文件?它可以配置为自动检测文件后缀的格式吗?(例如README.textile通过RedCloth运行,但README.mdown通过BlueCloth运行) 最佳答案 使用YARD直接代替RDoc将允许您包含Textile或Markdown文件,只要它们的文件后缀是合理的。我经常使用类似于以下Rake任务的东西:
简而言之错误:NOTE:Gem::SourceIndex#add_specisdeprecated,useSpecification.add_spec.Itwillberemovedonorafter2011-11-01.Gem::SourceIndex#add_speccalledfrom/opt/local/lib/ruby/site_ruby/1.8/rubygems/source_index.rb:91./opt/local/lib/ruby/gems/1.8/gems/rails-2.3.8/lib/rails/gem_dependency.rb:275:in`==':und
给定一个复杂的对象层次结构,幸运的是它不包含循环引用,我如何实现支持各种格式的序列化?我不是来讨论实际实现的。相反,我正在寻找可能会派上用场的设计模式提示。更准确地说:我正在使用Ruby,我想解析XML和JSON数据以构建复杂的对象层次结构。此外,应该可以将该层次结构序列化为JSON、XML和可能的HTML。我可以为此使用Builder模式吗?在任何提到的情况下,我都有某种结构化数据-无论是在内存中还是文本中-我想用它来构建其他东西。我认为将序列化逻辑与实际业务逻辑分开会很好,这样我以后就可以轻松支持多种XML格式。 最佳答案 我最
是否有简单的方法来更改默认ISO格式(yyyy-mm-dd)的ActiveAdmin日期过滤器显示格式? 最佳答案 您可以像这样为日期选择器提供额外的选项,而不是覆盖js:=f.input:my_date,as::datepicker,datepicker_options:{dateFormat:"mm/dd/yy"} 关于ruby-on-rails-事件管理员日期过滤器日期格式自定义,我们在StackOverflow上找到一个类似的问题: https://s
最近在学习CAN,记录一下,也供大家参考交流。推荐几个我觉得很好的CAN学习,本文也是在看了他们的好文之后做的笔记首先是瑞萨的CAN入门,真的通透;秀!靠这篇我竟然2天理解了CAN协议!实战STM32F4CAN!原文链接:https://blog.csdn.net/XiaoXiaoPengBo/article/details/116206252CAN详解(小白教程)原文链接:https://blog.csdn.net/xwwwj/article/details/105372234一篇易懂的CAN通讯协议指南1一篇易懂的CAN通讯协议指南1-知乎(zhihu.com)视频推荐CAN总线个人知识总