范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文

认识流媒体协议,从RTSP协议解析开始

  RTSP 是 Internet 协议规范,是 TCP/IP 协议体系中的一个应用层协议级网络通信系统。专为娱乐(如音频和视频)和通信系统的使用,以控制流媒体服务器。该协议用于在端点之间建立和控制媒体会话。媒体服务器的客户端发出 VHS 样式的命令,例如:PLAY、PAUSE、SETUP、DESCRIBE、RECORD 等等。以促进对从服务器到客户端或从客户端到服务器的媒体流进行实时控制。
  RTSP 传输过程 当用户或应用程序尝试从远程源流式传输视频时,客户端设备会向服务器发送 RTSP 请求,以确定可用选项,例如 PLAY,PAUSE、SETUP… 然后,服务器返回它可以通过 RTSP 接受的请求类型的列表。 客户端知道如何发出请求后,便将媒体描述请求发送到流服务器。 服务器以媒体描述作为响应。 客户端从那里发送设置请求,服务器以有关传输机制的信息作为响应。 设置过程完成后,客户端将通过告诉服务器使用设置请求中指定的传输机制发送位流(二进制序列)来启动流传输过程。 客户端 ->服务器:DESCRIBE
  服务器 ->客户端: 200 OK (SDP)
  客户端 ->服务器:SETUP
  服务器 ->客户端: 200 OK
  客户端 ->服务器:PAUSE
  ...
  协议的分析和学习少不了抓包,截屏个 RTSP 协议抓包的图:
  为什么 RTS 协议那么重要 RTSP 最初是一种允许用户直接从 Internet 播放音频和视频,而不必将媒体文件下载到其设备的方法。该协议已被应用于多种用途,包括互联网摄像机站点,在线教育和互联网广播。 RTSP 使用与基本 HTTP 相同的概念,在很大程度上是为了兼容现有的 Web 基础结构。正因如此,HTTP 的扩展机制大都可以直接引入到 RTSP 中。 RTSP 协议还具有很大的灵活性。客户端可以请求他们要使用的功能,以找出媒体服务器是否支持它们。同样,拥有媒体的任何人都可以从多个服务器传递媒体流。该协议还旨在适应媒体的未来发展,以便媒体创建者可以在必要时修改协议。
  RTSP 协议指令
  尽管 RTSP 在某些方面类似于 HTTP,但它定义了可用于控制多媒体播放的控制序列。尽管 HTTP 是无状态的,但 RTSP 却具有状态。
  在需要跟踪并发会话时使用标识符。像 HTTP 一样,RTSP 使用 TCP 来维护端到端连接,端口号为 554。
  尽管大多数 RTSP 控制消息是由客户端发送到服务器的,但是某些命令却是朝着另一个方向(即从服务器到客户端)传递的。
  下面我们来介绍基本的 RTSP 请求:
  SETUP
  SETUP 请求指定必须如何传输单个媒体流。必须在发送 PLAY 请求之前完成此操作。
  该请求包含媒体流 URL 和传输说明符。
  该说明符通常包括一个本地端口,用于接收 RTP 数据(音频或视频),另一个用于 RTCP 数据(元信息)。
  服务器答复通常会确认选定的参数,并填写缺少的部分,例如服务器的选定端口。必须先使用 SETUP 配置每个媒体流,然后才能发送聚合播放请求。
  PLAY
  PLAY 请求将导致播放一个或所有媒体流。可以通过发送多个 PLAY 请求来堆叠播放请求。该 URL 可以是聚合 URL(以播放所有媒体流),也可以是单个媒体流 URL(仅播放该流)。
  可以指定范围。如果未指定范围,则从头开始播放并播放到结尾,或者,如果流已暂停,则在暂停点恢复播放。
  PAUSE
  PAUSE 请求会暂时中止一个或所有媒体流,因此稍后可以通过 PLAY 请求将其恢复。该请求包含聚合或媒体流 URL。
  PAUSE 请求上的 range 参数指定何时暂停。如果省略 range 参数,则暂停将立即无限期地发生。
  RECORD
  此方法根据演示说明开始记录一系列媒体数据。时间戳反映开始时间和结束时间(UTC)。如果没有给出时间范围,请使用演示说明中提供的开始时间或结束时间。
  如果会话已经开始,请立即开始录制。服务器决定是否将记录的数据存储在请求 URl 或其他 URI 下。
  如果服务器未使用请求 URI,则响应应为 201,并包含描述请求状态并引用新资源的实体和位置标头。
  【腾讯文档】FFmpegWebRTCRTMPRTSPHLSRTP播放器-音视频流媒体高级开发-资料领取FFmpegWebRTCRTMPRTSPHLSRTP鎾斁鍣�-闊宠棰戞祦濯掍綋楂樼骇寮€鍙�-璧勬枡棰嗗彇
  ANNOUNCE
  当从客户端发送到服务器时,ANNOUNCE 将请求 URL 标识的演示或媒体对象的描述发布到服务器。ANNOUNCE 会实时更新会话描述。
  如果将新的媒体流添加到演示文稿中(例如,在现场演示文稿中),则应再次发送整个演示文稿说明,而不仅仅是其他组件,以便可以删除这些组件。
  TEARDOWN
  TEARDOWN 请求用于终止会话。它停止所有媒体流并释放服务器上所有与会话相关的数据。
  GET_PARAMETER
  GET_PARAMETER 请求检索 URI 中指定的表示形式或流的参数值。答复和响应的内容留给实现。
  SET_PARAMETER
  此方法要求为 URI 指定的表示或流设置参数值。
  Wireshark RTSP 协议解析实现
  对 RTSP 协议的使用有了一个大概的了解之后,我们来解析实现一下 RTSP 协议。 #include  #include  #include  #include  #include  #include  #include  #include  #include  #include  #include  #include   /*RTSP 端口*/ #define RTSP_TCP_PORT_RANGE      554   typedef enum {     RTSP_REQUEST,     RTSP_REPLY,     RTSP_NOT_FIRST_LINE } rtsp_type_t;  static const char *rtsp_methods[] = {     "DESCRIBE",     "ANNOUNCE",     "GET_PARAMETER",     "OPTIONS",     "PAUSE",     "PLAY",     "RECORD",     "REDIRECT",     "SETUP",     "SET_PARAMETER",     "TEARDOWN" };  /* 用于RTSP统计  */ struct rtsp_info_value_t {   char  *request_method;   unsigned long int  response_code; };  /*   假定一个字节数组(假定包含一个以空值结尾的字符串)作为参数,   并返回字符串的长度-即该数组的大小,对于空终止符的值减去1。   */ #define STRLEN_CONST(str)   (sizeof (str) - 1)      static const char rtsp_content_type[]      = "Content-Type:"; static const char rtsp_transport[]         = "Transport:"; static const char rtsp_sps_server_port[]   = "server_port="; static const char rtsp_cps_server_port[]   = "client_port="; static const char rtsp_sps_dest_addr[]     = "dest_addr="; static const char rtsp_cps_src_addr[]      = "src_addr="; static const char rtsp_rtp_udp_default[]   = "rtp/avp"; static const char rtsp_rtp_udp[]           = "rtp/avp/udp"; static const char rtsp_rtp_tcp[]           = "rtp/avp/tcp"; static const char rtsp_rdt_feature_level[] = "RDTFeatureLevel"; static const char rtsp_real_rdt[]          = "x-real-rdt/"; static const char rtsp_real_tng[]          = "x-pn-tng/"; /* synonym for x-real-rdt */ static const char rtsp_inter[]             = "interleaved="; static const char rtsp_content_length[] = "Content-Length:";    static void rtsp_create_conversation(u_char *line_begin, size_t line_len,rtsp_type_t rtsp_type_packet) {     char    buf[256];     char   *tmp;     bool  rtp_udp_transport = false;     bool  rtp_tcp_transport = false;     bool  rdt_transport = false;     //bool  is_video      = false; /* 是否需要显示视频  */   unsigned int     c_data_port, c_mon_port;     unsigned int     s_data_port, s_mon_port;   unsigned int     ipv4_1, ipv4_2, ipv4_3, ipv4_4;      if (rtsp_type_packet != RTSP_REPLY) {         return;     }       /* 将行复制到buf */     if (line_len > sizeof(buf) - 1)     {         /* 避免溢出缓冲区。 */         line_len = sizeof(buf) - 1;     }      memcpy(buf, line_begin, line_len);     buf[line_len] = "";   printf("%s ",buf);     /* Get past "Transport:" and spaces */     tmp = buf + STRLEN_CONST(rtsp_transport);   //printf("tmp %s ",tmp);     while (*tmp && isspace(*tmp))         tmp++;    if ((tmp = strstr(buf, rtsp_cps_src_addr)))    {          tmp += strlen(rtsp_cps_src_addr);     //printf("tmp ======  %s ",tmp);         if (sscanf(tmp, ""%u.%u.%u.%u:%u"", &ipv4_1, &ipv4_2, &ipv4_3, &ipv4_4, &c_data_port) == 5)      {             char *tmp2;             char *tmp3;       //printf("ipv4_1 %d ",ipv4_1);       //printf("ipv4_2 %d ",ipv4_2);       //printf("ipv4_3 %d ",ipv4_3);       //printf("ipv4_4 %d ",ipv4_4);       printf("c_data_port %d ",c_data_port);             //Skip leading               tmp++;             tmp2=strstr(tmp,":");             tmp3=strndup(tmp,tmp2-tmp);             printf("src_addr  %s ",tmp3);                                free(tmp3);         }     }   if ((tmp = strstr(buf, rtsp_sps_dest_addr)))    {         tmp += strlen(rtsp_sps_dest_addr);         if (sscanf(tmp, "":%u"", &s_data_port) == 1)      {             /* :9 mean ignore */             if (s_data_port == 9) {                 s_data_port = 0;             }       printf("s_data_port %d ",s_data_port);         }   }            if ((tmp = strstr(buf, rtsp_sps_server_port))) {         tmp += strlen(rtsp_sps_server_port);         if (sscanf(tmp, "%u", &s_mon_port) == 1) {                          printf("s_mon_port %d ",s_mon_port);         }     }      }   static bool is_rtsp_request_or_reply( unsigned char *line, int offset, rtsp_type_t *type) {     unsigned int   ii = 0;   char *data = reinterpret_cast(line);     int           tokenlen;     char         response_chars[4];   struct rtsp_info_value_t rtsp_info;   char *token, *next_token;     /*这是RTSP的回复 ?  */     if ( strncasecmp("RTSP/", data, 5) == 0) {         /*          * Yes.          */         *type = RTSP_REPLY;          /* 第一个标记是版本。  */     offset += 9;          memcpy(response_chars, data + offset, 3);     response_chars[3] = "";     rtsp_info.response_code = strtoul(response_chars, NULL, 10);     //printf("rtsp_info.response_code %d ",rtsp_info.response_code);              return true;     }      /*     这是RTSP请求吗?     检查该行是否以RTSP请求方法之一开头。       */     for (ii = 0; ii < sizeof rtsp_methods / sizeof rtsp_methods[0]; ii++) {         size_t len = strlen(rtsp_methods[ii]);         if (strncasecmp(rtsp_methods[ii], data, len) == 0 &&(isspace(data[len])))         {             *type = RTSP_REQUEST;             rtsp_info.request_method = strndupa(rtsp_methods[ii], len+1);       //printf("request_method: %s ",rtsp_info.request_method);              return true;         }     }      /* 既不是请求也不是回应 */     *type = RTSP_NOT_FIRST_LINE;     return false; }   /* 阅读回复消息的第一行  */ static void process_rtsp_reply(u_char *rtsp_data, int offset,rtsp_type_t rtsp_type_packet) {   char *lineend  = reinterpret_cast(rtsp_data + offset);     char *status   = reinterpret_cast(rtsp_data );     char *status_start;     unsigned int         status_i;       /* status code */      /* Skip protocol/version */     while (status < lineend && !isspace(*status))         status++;     /* Skip spaces */     while (status < lineend && isspace(*status))         status++;      /* Actual code number now */     status_start = status;   //printf("status_start %s ",status_start);     status_i = 0;     while (status < lineend && isdigit(*status))         status_i = status_i * 10 + *status++ - "0";      //printf("status_i %d ",status_i);      offset += strlen(lineend);   rtsp_create_conversation(rtsp_data,offset,rtsp_type_packet);  }  static void process_rtsp_request(u_char *rtsp_data, int offset,rtsp_type_t rtsp_type_packet) {   char *lineend  = reinterpret_cast(rtsp_data + offset);    // u_char *lineend  = rtsp_data + offset;     unsigned int      ii = 0;     char *url;     char *url_start;   char    buf[256];   char   *tmp;   int content_length = 0;   char content_type[256];     /* Request Methods */     for (ii = 0; ii < sizeof rtsp_methods / sizeof rtsp_methods[0]; ii++) {         size_t len = strlen(rtsp_methods[ii]);         if (strncasecmp(rtsp_methods[ii], lineend, len) == 0 &&(isspace(lineend[len])))             break;     }     //printf("process_rtsp_request 0x%.2X,0x%.2X,0x%.2X,0x%.2X ",lineend[0],lineend[1],lineend[2],lineend[3]);       /* URL */     url = lineend;      /* Skip method name again */     while (url < lineend && !isspace(*url))         url++;     /* Skip spaces */     while (url < lineend && isspace(*url))         url++;     /* URL starts here */     url_start = url;        /* Scan to end of URL */     while (url < lineend && !isspace(*url))         url++;      printf("%s ",url_start);   printf("111url %s ",url);      if ((tmp = strstr(url_start, rtsp_content_type)))    {         tmp += strlen(rtsp_content_type);         if (sscanf(tmp, "%s", content_type) == 1)      {                          //printf("content_type %s ",content_type);         }     }        //Content-Length   if ((tmp = strstr(url_start, rtsp_content_length)))    {         tmp += strlen(rtsp_content_length);         if (sscanf(tmp, "%u", &content_length) == 1)      {                          //printf("content_length %d ",content_length);         }     }      }    void dissect_rtsp(u_char *rtsp_data) {   int offset = 0;   rtsp_type_t   rtsp_type_packet;   bool      is_request_or_reply;     u_char *linep, *lineend;   u_char    c;   //bool      is_header = false;   is_request_or_reply = is_rtsp_request_or_reply(rtsp_data, offset, &rtsp_type_packet);        if (is_request_or_reply)     goto is_rtsp;       is_rtsp:       switch(rtsp_type_packet)   {     case RTSP_REQUEST:        process_rtsp_request(rtsp_data, offset,rtsp_type_packet);        break;      case RTSP_REPLY:        process_rtsp_reply(rtsp_data, offset,rtsp_type_packet);              break;      case RTSP_NOT_FIRST_LINE:       /* Drop through, it may well be a header line */       break;     default:       break;   }     }  static void dissect_rtsp_tcp(struct ip *pIp) {   int iHeadLen = pIp->ip_hl*4;   int iPacketLen = ntohs(pIp->ip_len) - iHeadLen;    int offset = 0;   int nFragSeq = 0;   struct tcphdr *pTcpHdr = (struct tcphdr *)(((char  *)pIp) + iHeadLen);      if (pIp->ip_p == IPPROTO_TCP && (ntohs(pTcpHdr->dest) == RTSP_TCP_PORT_RANGE)    || (ntohs(pTcpHdr->source) == RTSP_TCP_PORT_RANGE) )/*仅处理TCP协议*/   {                   int iPayloadLen = iPacketLen - pTcpHdr->doff*4;     //printf("TCP Payload Len %d ",iPayloadLen);         u_char *RtspHdr = (u_char*)(pTcpHdr+1);     if (RtspHdr == NULL)       return;     u_char *RtspData = RtspHdr + 12; /*skip OPtions */         //printf("NtpHdr 0x%.2X,0x%.2X,0x%.2X,0x%.2X ",RtspData[0],RtspData[1],RtspData[2],RtspData[3]);         dissect_rtsp(RtspData);   }   }    复制代码
  编译运行
  RTSP 是一种基于文本的协议,用回车换行(r )作为每一行的结束符,其好处是,在使用过程中可以方便地增加自定义参数,也方便抓包分析。
  从消息传送方向上来分,RTSP 的报文有两类:请求报文和响应报文。请求报文是指从客户端向服务器发送的请求,响应报文是指从服务器到客户端的回应。
  总结
  RTSP 对流媒体提供了诸如 PLAY,PAUSE、SETUP 等控制,但它本身并不传输数据,RTSP 的作用相当于流媒体服务器的远程控制。
  服务器端可以自行选择使用 TCP 或 UDP 来传送串流内容,它的语法和运作跟 HTTP 类似。更多解析请参考 RFC 官方文档,也是最权威的文档。
  作者:声网 链接:https://juejin.cn/post/7016675635942670366 来源:稀土掘金 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

黄长旺全面做活做旺佛山新城,全力打造北部都会中心顺德两会新城进驻企业达2319多家,较2021年同比增长42全镇市场主体总量突破6万户,位列全区各镇街第一乐华家居总部生产基地实现投产,深国际佛山顺德智慧物流产业园顺利动工联东U谷上华智造幸福感来自于共同体感觉在这个追求效率节奏飞快的社会,相信不少人时常会陷入迷茫中,不明白日复一日年复一年的工作到底有什么意义,尤其是那种每天重复的工作。或许你会很无奈地说,没办法,不管多么枯燥的工作,该做21硬核投研丨未来已来!提前曝光的iPhone15细节酝酿哪些供应链概念股波澜21世纪资本研究院张赛男实习生邹晨钰上海报道2023年刚刚开局,按照惯例将在下半年才发布的iPhone15,已经传来最新消息。根据媒体曝光的产品细节,全新的iPhone15系列将继侃财丨建议专家不要再建议了点蓝字关注,不迷路前两天有个专家建议说中国居民存款又增加了15万亿元,如果有13拿出来恢复对房屋装修等购买,中国经济不就能顺利恢复了吗?这个说法迅速引爆网络。这位专家今天回应说,应帜境墅装荣获两项意大利IIDAAWARD国际设计大奖真高端设计走向世界红网时刻新闻1月11日讯(记者黄怡萍)2023年1月,由CEIDA中欧国际设计协会CIID88国际室内设计网与意大利米兰中国文化中心联合举办的2022年意大利IIDAAWARD国际美媒汽车巨头在美国消费电子展上勾画未来能变色的汽车。会飞的出租车。还有一款能驾驶车辆的电子游戏式平板电脑。在汽车行业应对技术变化之际,汽车企业本周来到在美国拉斯维加斯举办的消费电子展,展示它们的最新创意有些非常奇特远离挑战与机遇并存,国际教育未来如何?教育大咖齐聚探讨南都讯记者李梓毅实习生胡欣妍近日,由顶思粤港澳办学团体发展协作会集思未来联合主办的RAISE2022教育创新发展大会在广州举行。本届大会以锐意进取,思深悟远为主题,聚焦国际教育在新南方观察奋进的福田,奔赴中国式现代化典范城区九万里风鹏正举,新征程未来可期。作为深圳市中心城区的福田,一直以来都是推动城市发展的核心力量重要引擎,踏上新征程,在推动中国式现代化建设事业上更是责无旁贷。此次在此前提出的首善发展杰瑞股份累计向484名优秀员工赠送汽车总价值超过7300万元中证网讯(记者董添)1月11日,杰瑞股份2022年度金钥匙奖授车仪式举行,39名优秀员工获得奖励。据公司介绍,杰瑞金钥匙奖自2008年设立,至今已经连续举办了15届,共有484名优全球财经连线人民银行发布2022年金融统计数据,金价走势强劲,鲍威尔新年首秀视频加载中南方财经全媒体记者施诗上海报道人民银行发布2022年金融统计数据1月10日,人民银行发布2022年金融统计数据报告。数据显示,2022年全年人民币贷款增加21。31万亿元湖南花鼓戏与中国民族声乐的交流互鉴与创新发展研究课题研讨会举行1月11日,湖南花鼓戏与中国民族声乐的交流互鉴与创新发展研究课题研讨会在湖南省艺术研究院举行。(湖南省艺术研究院供图)红网时刻新闻1月11日讯(记者袁思蕾)花鼓戏是湖湘文化的一张闪
小米手机现在还能买吗?第一台小米2s,使用近4年后淘汰,无故障。第二台小米note顶配版。使用一年多后卖掉,无故障。卖出时屏幕存在一定不影响使用的亮度不均,据说是通病,我怀疑是回收商故意压价。第三台小米联想一年利润38亿,杨元庆年薪1。7亿多不多?多的吓人。不论多大的公司,包括世界上知名的,500强的公司。高层的年薪都是和本公司的赢利多少相挂钩的。公司剩润高,高层拿的多是天经地义的。是你领导的好,应得的。但如果公司在你的领导联想集团和柳传志被冤枉了吗?別号召老百姓讨论,我们老百姓能知道多少內目,联想问题上支持司马南作揖作揖有没问题,自然有执法执纪部门作出结论。但是,有些问题还是要清清楚楚明明白白告诉大家,给大家一个交代!比如,请目前有哪些好的二本院校?菜鸟来谈谈吧,欢迎大家关注菜鸟!O题主的这个问题,在菜鸟看来就是按照高考录取的分数来分的一本和二本院校。有些高校的录取分数一般都是一本分数线,但有个别的专业还是会按照二本分数线来录女孩成绩不好,上高中,还是上32?有什么区别?女孩成绩不好,不高中,还是上32?有什么区别?小徐老师常年带毕业班,关心孩子有毕业选择和就业前景等问题,据小徐老师多年的观察和统计,可以肯定的告诉大家能上高中一定要上高中,实在没考男性口腔hpv该怎么检查?近年来随着性行为方式的变化,口腔尖锐湿疣口腔癌的发病率逐年增高。根据由AHNSASCOASTRO和SNM赞助的多学科口腔癌研讨会上发表的一项研究表明口腔HPV感染在男性中比女性更常脑梗是一种很严重的病吗?脑梗死当然是一种很严重的疾病,这是一个对广大中老年人危害很大的一种疾病,致残率很高,每年直接或间接死于脑梗死的患者有许多。脑梗死又称缺血性脑卒中,也就是大家平常说的中风。是由各种原花呗借呗的突然降额让你清醒了吗?个人觉得这种降额情况现实中有点比较普遍了,听说的也比以前多了。记得好像是今年夏天开始我身边有好多人都遇到这种情况了,并且基本上没有还款记录不良的,因为几乎人人都有房贷车贷啥的,无论为什么越来越多人后悔买新能源汽车?我开了6年纯电动汽车,目前换了第二辆,有话想吐槽,纯电动汽车有4大优势5大缺点,缺点才是硬伤,个个戳心,只有弄明白纯电动混动燃油车它们之间的优势与不足,买了新能源汽车才不会后悔。我部分员工退休后的工资比一线员工工资还高,这个现象合理吗?有什么不合理的呢?人家在自己的岗位上奋斗了几十年,那些退休金也是自己所交养老金的正常所得,不偷你的不抢你的,眼红什么呢?自己做好自己的本职工作,有本事也像他们那样在一个岗位上奋斗个为什么单位开全体会的时候,领导让大家往前坐,而员工都抢离领导远的座位?你说的这种现象,带有普遍性。单位全体会议,甭管来早的,还是来晚的,多数人有个比较统一的习惯往后坐。这里,我说的是多数人。不排除也有喜欢往前凑的少数人,他们希望让领导们看见。正如你看