RTP封装&h264原理&实现传输h264的RTSP服务器

打印 上一主题 下一主题

主题 582|帖子 582|积分 1746


媒介

实现:客户端建立与RTSP服务端的连接后,并且在RTSP服务端回复了客户端的Play请求以后,服务端必要源源不断的读取一个本地h264视频文件,并将读取到的h264视频流封装到RTP数据包中,再推送至客户端。这样我们就实现了一个简单的支持RTSP协议流媒体分发服务。

一、RTP封装



  • RTP头的结构体
  1. struct RtpHeader
  2. {
  3.     /* byte 0 */
  4.     uint8_t csrcLen : 4;//CSRC计数器,占4位,指示CSRC 标识符的个数。
  5.     uint8_t extension : 1;//占1位,如果X=1,则在RTP报头后跟有一个扩展报头。
  6.     uint8_t padding : 1;//填充标志,占1位,如果P=1,则在该报文的尾部填充一个或多个额外的八位组,它们不是有效载荷的一部分。
  7.     uint8_t version : 2;//RTP协议的版本号,占2位,当前协议版本号为2。
  8.     /* byte 1 */
  9.     uint8_t payloadType : 7;//有效载荷类型,占7位,用于说明RTP报文中有效载荷的类型,如GSM音频、JPEM图像等。
  10.     uint8_t marker : 1;//标记,占1位,不同的有效载荷有不同的含义,对于视频,标记一帧的结束;对于音频,标记会话的开始。
  11.     /* bytes 2,3 */
  12.     uint16_t seq;//占16位,用于标识发送者所发送的RTP报文的序列号,每发送一个报文,序列号增1。接收者通过序列号来检测报文丢失情况,重新排序报文,恢复数据。
  13.     /* bytes 4-7 */
  14.     uint32_t timestamp;//占32位,时戳反映了该RTP报文的第一个八位组的采样时刻。接收者使用时戳来计算延迟和延迟抖动,并进行同步控制。
  15.     /* bytes 8-11 */
  16.     uint32_t ssrc;//占32位,用于标识同步信源。该标识符是随机选择的,参加同一视频会议的两个同步信源不能有相同的SSRC。
  17.    /*标准的RTP Header 还可能存在 0-15个特约信源(CSRC)标识符
  18.    
  19.    每个CSRC标识符占32位,可以有0~15个。每个CSRC标识了包含在该RTP报文有效载荷中的所有特约信源
  20.    */
  21. };
复制代码

  • RTP包的结构体
  1. struct RtpPacket
  2. {
  3.     struct RtpHeader rtpHeader;
  4.     uint8_t payload[0];
  5. };
  6. // 包含一个RTP头部和RTP载荷
复制代码
二、H264码流举行RTP封装

1.明白H264编码

H.264由一个一个的NALU组成,每个NALU之间使用00 00 00 01或00 00 01分隔开,每个NALU的第一次字节都有特别的寄义,



  • F(forbiden):克制位,占用NAL头的第一个位,当克制位值为1时表现语法错误;
  • NRI:参考级别,占用NAL头的第二到第三个位;值越大,该NAL越紧张。
  • Type:Nal单元数据范例,也就是标识该NAL单元的数据范例是哪种,占用NAL头的第四到第8个位;
  1. 常用Nalu_type:
  2. 0x06 (0 00 00110) SEI      type = 6
  3. 0x67 (0 11 00111) SPS      type = 7
  4. 0x68 (0 11 01000) PPS      type = 8
  5. 0x65 (0 11 00101) IDR      type = 5
  6. 0x65 (0 10 00101) IDR      type = 5
  7. 0x65 (0 01 00101) IDR      type = 5
  8. 0x65 (0 00 00101) IDR      type = 5
  9. 0x61 (0 11 00001) I帧      type = 1
  10. 0x41 (0 10 00001) P帧      type = 1
  11. 0x01 (0 00 00001) B帧      type = 1
复制代码
对于H.264格式了解这些就够了,目标是想从一个H.264的文件中将一个一个的NALU提取出来,然后封装成RTP包,下面介绍如何将NALU封装成RTP包。
2.H.264打包

H.264可以由三种RTP打包方式

  • 单NALU打包: 一个RTP包包含一个完备的NALU
  • 聚合打包:对于较小的NALU,一个RTP包可包含多个完备的NALU
  • 分片打包:对于较大的NALU,一个NALU可以分为多个RTP包发送
注意:这里要区分好概念,每一个RTP包都包含一个RTP头部和RTP荷载,这是固定的。而H.264发送数据可支持三种RTP打包方式
比较常用的是单NALU打包和分片打包,这里只介绍两种
单NALU打包
所谓单NALU打包就是将一整个NALU的数据放入RTP包的载荷中,这是最简单的一种方式。
分片打包
每个RTP包都有巨细限制的,因为RTP一般都是使用UDP发送,UDP没有流量控制,所以要限制每一次发送的巨细,所以假如一个NALU的太大,就必要分成多个RTP包发送,至于如何分成多个RTP包,如下:
首先要明白,RTP包的格式是绝不会变的,永远多是RTP头+RTP载荷

RTP头部是固定的,那么只能在RTP载荷中去添加额外信息来说明这个RTP包是表现同一个NALU
假如是分片打包的话,那么在RTP载荷开始有两个字节的信息,然后再是NALU的内容

第一个字节位FU Indicator,其格式如下

高三位:与NALU第一个字节的高三位雷同
Type:28,表现该RTP包一个分片,为什么是28?因为H.264的规范中定义的,别的还有许多其他Type,这里不详讲
第二个字节位FU Header,其格式如下

S:标记该分片打包的第一个RTP包
E:比较该分片打包的末了一个RTP包
Type:NALU的Type
三、实现一个传输h264的RTSP服务器

代码如下:
main.cpp

  1. //
  2. // Created by sun on 10/11/21.
  3. //
  4. #include <stdio.h>
  5. #include <stdlib.h>
  6. #include <stdint.h>
  7. #include <string.h>
  8. #include <time.h>
  9. #include <sys/types.h>
  10. #include <sys/stat.h>
  11. #include <fcntl.h>
  12. #include <WinSock2.h>
  13. #include <WS2tcpip.h>
  14. #include <windows.h>
  15. #include "rtp.h"
  16. #define H264_FILE_NAME   "../data/test.h264"
  17. #define SERVER_PORT      8554
  18. #define SERVER_RTP_PORT  55532
  19. #define SERVER_RTCP_PORT 55533
  20. #define BUF_MAX_SIZE     (1024*1024)
  21. static int createTcpSocket()
  22. {
  23.     int sockfd;
  24.     int on = 1;
  25.     sockfd = socket(AF_INET, SOCK_STREAM, 0);
  26.     if (sockfd < 0)
  27.         return -1;
  28.     setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(on));
  29.     return sockfd;
  30. }
  31. static int createUdpSocket()
  32. {
  33.     int sockfd;
  34.     int on = 1;
  35.     sockfd = socket(AF_INET, SOCK_DGRAM, 0);
  36.     if (sockfd < 0)
  37.         return -1;
  38.     setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(on));
  39.     return sockfd;
  40. }
  41. static int bindSocketAddr(int sockfd, const char* ip, int port)
  42. {
  43.     struct sockaddr_in addr;
  44.     addr.sin_family = AF_INET;
  45.     addr.sin_port = htons(port);
  46.     addr.sin_addr.s_addr = inet_addr(ip);
  47.     if (bind(sockfd, (struct sockaddr*)&addr, sizeof(struct sockaddr)) < 0)
  48.         return -1;
  49.     return 0;
  50. }
  51. static int acceptClient(int sockfd, char* ip, int* port)
  52. {
  53.     int clientfd;
  54.     socklen_t len = 0;
  55.     struct sockaddr_in addr;
  56.     memset(&addr, 0, sizeof(addr));
  57.     len = sizeof(addr);
  58.     clientfd = accept(sockfd, (struct sockaddr*)&addr, &len);
  59.     if (clientfd < 0)
  60.         return -1;
  61.     strcpy(ip, inet_ntoa(addr.sin_addr));
  62.     *port = ntohs(addr.sin_port);
  63.     return clientfd;
  64. }
  65. static inline int startCode3(char* buf)
  66. {
  67.     if (buf[0] == 0 && buf[1] == 0 && buf[2] == 1)
  68.         return 1;
  69.     else
  70.         return 0;
  71. }
  72. static inline int startCode4(char* buf)
  73. {
  74.     if (buf[0] == 0 && buf[1] == 0 && buf[2] == 0 && buf[3] == 1)
  75.         return 1;
  76.     else
  77.         return 0;
  78. }
  79. static char* findNextStartCode(char* buf, int len)
  80. {
  81.     int i;
  82.     if (len < 3)
  83.         return NULL;
  84.     for (i = 0; i < len - 3; ++i)
  85.     {
  86.         if (startCode3(buf) || startCode4(buf))
  87.             return buf;
  88.         ++buf;
  89.     }
  90.     if (startCode3(buf))
  91.         return buf;
  92.     return NULL;
  93. }
  94. static int getFrameFromH264File(FILE* fp, char* frame, int size) {  //  从H.264 文件中读取一帧视频数据
  95.     int rSize, frameSize;   // rSize:读取到的数据大小,frameSize:帧数据的大小
  96.     char* nextStartCode;    // nextStartCode:指向下一个起始码的指针
  97.     if (!fp)
  98.         return -1;
  99.     rSize = fread(frame, 1, size, fp);
  100.     if (!startCode3(frame) && !startCode4(frame))
  101.         return -1;
  102.     nextStartCode = findNextStartCode(frame + 3, rSize - 3);
  103.     if (!nextStartCode)
  104.     {
  105.         //lseek(fd, 0, SEEK_SET);
  106.         //frameSize = rSize;
  107.         return -1;
  108.     }
  109.     else
  110.     {
  111.         frameSize = (nextStartCode - frame);    // 如果找到 计算帧长度
  112.         fseek(fp, frameSize - rSize, SEEK_CUR); // 返回原来位置
  113.     }
  114.     return frameSize;   // 返回帧长度
  115. }
  116. /*
  117. serverRtpSockfd: 服务器 RTP 套接字文件描述符; ip: 客户端 IP 地址; port: 客户端 RTP 端口
  118. rtpPacket: RTP 包结构体,用于存储 RTP 头和负载; frame: H.264 视频帧数据; frameSize: 视频帧大小
  119. */
  120. static int rtpSendH264Frame(int serverRtpSockfd, const char* ip, int16_t port,
  121.     struct RtpPacket* rtpPacket, char* frame, uint32_t frameSize)
  122. {
  123.     uint8_t naluType;   // nalu第一个字节,用于指示 NALU 类型
  124.     int sendBytes = 0;  // 已发送的字节数
  125.     int ret;
  126.     naluType = frame[0];    // 获取 NALU 类型
  127.     printf("frameSize=%d \n", frameSize);
  128.     if (frameSize <= RTP_MAX_PKT_SIZE) // nalu长度小于最大包长:单一NALU单元模式
  129.     {
  130.          //*   0 1 2 3 4 5 6 7 8 9
  131.          //*  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  132.          //*  |F|NRI|  Type   | a single NAL unit ... |
  133.          //*  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  134.         memcpy(rtpPacket->payload, frame, frameSize);   // 将帧数据复制到 RTP 负载中
  135.         ret = rtpSendPacketOverUdp(serverRtpSockfd, ip, port, rtpPacket, frameSize);
  136.         if(ret < 0)
  137.             return -1;
  138.         rtpPacket->rtpHeader.seq++;
  139.         sendBytes += ret;
  140.         if ((naluType & 0x1F) == 7 || (naluType & 0x1F) == 8) // 如果是SPS、PPS就不需要加时间戳
  141.             goto out;
  142.     }
  143.     else // nalu长度小于最大包场:分片模式
  144.     {
  145.          //*  0                   1                   2
  146.          //*  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3
  147.          //* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  148.          //* | FU indicator  |   FU header   |   FU payload   ...  |
  149.          //* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  150.          //*     FU Indicator
  151.          //*    0 1 2 3 4 5 6 7
  152.          //*   +-+-+-+-+-+-+-+-+
  153.          //*   |F|NRI|  Type   |
  154.          //*   +---------------+
  155.          //*      FU Header
  156.          //*    0 1 2 3 4 5 6 7
  157.          //*   +-+-+-+-+-+-+-+-+
  158.          //*   |S|E|R|  Type   |
  159.          //*   +---------------+
  160.         int pktNum = frameSize / RTP_MAX_PKT_SIZE;       // 有几个完整的包
  161.         int remainPktSize = frameSize % RTP_MAX_PKT_SIZE; // 剩余不完整包的大小
  162.         int i, pos = 1;
  163.         // 循环发送完整的RTP包
  164.         for (i = 0; i < pktNum; i++)
  165.         {
  166.             rtpPacket->payload[0] = (naluType & 0x60) | 28;
  167.             rtpPacket->payload[1] = naluType & 0x1F;
  168.             if (i == 0) //第一包数据
  169.                 rtpPacket->payload[1] |= 0x80; // start
  170.             else if (remainPktSize == 0 && i == pktNum - 1) //最后一包数据
  171.                 rtpPacket->payload[1] |= 0x40; // end
  172.             memcpy(rtpPacket->payload+2, frame+pos, RTP_MAX_PKT_SIZE);  // 复制数据到 RTP 负载
  173.             ret = rtpSendPacketOverUdp(serverRtpSockfd, ip, port, rtpPacket, RTP_MAX_PKT_SIZE+2);
  174.             if(ret < 0)
  175.                 return -1;
  176.             rtpPacket->rtpHeader.seq++;
  177.             sendBytes += ret;
  178.             pos += RTP_MAX_PKT_SIZE;    // 增加 RTP 序列号和已发送字节数
  179.         }
  180.         // 发送剩余的不完整 RTP 包(如果有)
  181.         if (remainPktSize > 0)
  182.         {
  183.             rtpPacket->payload[0] = (naluType & 0x60) | 28;
  184.             rtpPacket->payload[1] = naluType & 0x1F;
  185.             rtpPacket->payload[1] |= 0x40; //end    设置 FU 指示器和 FU 头,标记为结束(E)
  186.             memcpy(rtpPacket->payload+2, frame+pos, remainPktSize+2);   // 复制剩余的数据到 RTP 负载
  187.             ret = rtpSendPacketOverUdp(serverRtpSockfd, ip, port, rtpPacket, remainPktSize+2);  // 调用 rtpSendPacketOverUdp 发送 RTP 包
  188.             if(ret < 0)
  189.                 return -1;
  190.             rtpPacket->rtpHeader.seq++;
  191.             sendBytes += ret;   // 增加 RTP 序列号和已发送字节数
  192.         }
  193.     }
  194.     rtpPacket->rtpHeader.timestamp += 90000 / 25;   // 增加 RTP 时间戳,假设帧率为 25 fps
  195.     out:
  196.     return sendBytes;
  197. }
  198. static int handleCmd_OPTIONS(char* result, int cseq)
  199. {
  200.     sprintf(result, "RTSP/1.0 200 OK\r\n"
  201.         "CSeq: %d\r\n"
  202.         "Public: OPTIONS, DESCRIBE, SETUP, PLAY\r\n"
  203.         "\r\n",
  204.         cseq);
  205.     return 0;
  206. }
  207. static int handleCmd_DESCRIBE(char* result, int cseq, char* url)
  208. {
  209.     char sdp[500];
  210.     char localIp[100];
  211.     sscanf(url, "rtsp://%[^:]:", localIp);
  212.     sprintf(sdp, "v=0\r\n"
  213.         "o=- 9%ld 1 IN IP4 %s\r\n"
  214.         "t=0 0\r\n"
  215.         "a=control:*\r\n"
  216.         "m=video 0 RTP/AVP 96\r\n"
  217.         "a=rtpmap:96 H264/90000\r\n"
  218.         "a=control:track0\r\n",
  219.         time(NULL), localIp);
  220.     sprintf(result, "RTSP/1.0 200 OK\r\nCSeq: %d\r\n"
  221.         "Content-Base: %s\r\n"
  222.         "Content-type: application/sdp\r\n"
  223.         "Content-length: %zu\r\n\r\n"
  224.         "%s",
  225.         cseq,
  226.         url,
  227.         strlen(sdp),
  228.         sdp);
  229.     return 0;
  230. }
  231. static int handleCmd_SETUP(char* result, int cseq, int clientRtpPort)
  232. {
  233.     sprintf(result, "RTSP/1.0 200 OK\r\n"
  234.         "CSeq: %d\r\n"
  235.         "Transport: RTP/AVP;unicast;client_port=%d-%d;server_port=%d-%d\r\n"
  236.         "Session: 66334873\r\n"
  237.         "\r\n",
  238.         cseq,
  239.         clientRtpPort,
  240.         clientRtpPort + 1,
  241.         SERVER_RTP_PORT,
  242.         SERVER_RTCP_PORT);
  243.     return 0;
  244. }
  245. static int handleCmd_PLAY(char* result, int cseq)
  246. {
  247.     sprintf(result, "RTSP/1.0 200 OK\r\n"
  248.         "CSeq: %d\r\n"
  249.         "Range: npt=0.000-\r\n"
  250.         "Session: 66334873; timeout=10\r\n\r\n",
  251.         cseq);
  252.     return 0;
  253. }
  254. static void doClient(int clientSockfd, const char* clientIP, int clientPort) {
  255.     char method[40];
  256.     char url[100];
  257.     char version[40];
  258.     int CSeq;
  259.     int serverRtpSockfd = -1, serverRtcpSockfd = -1;
  260.     int clientRtpPort, clientRtcpPort;
  261.     char* rBuf = (char*)malloc(BUF_MAX_SIZE);
  262.     char* sBuf = (char*)malloc(BUF_MAX_SIZE);
  263.     while (true) {
  264.         int recvLen;
  265.         recvLen = recv(clientSockfd, rBuf, BUF_MAX_SIZE, 0);
  266.         if (recvLen <= 0) {
  267.             break;
  268.         }
  269.         rBuf[recvLen] = '\0';
  270.         printf("%s rBuf = %s \n",__FUNCTION__,rBuf);
  271.         const char* sep = "\n";
  272.         char* line = strtok(rBuf, sep);
  273.         while (line) {
  274.             if (strstr(line, "OPTIONS") ||
  275.                 strstr(line, "DESCRIBE") ||
  276.                 strstr(line, "SETUP") ||
  277.                 strstr(line, "PLAY")) {
  278.                 if (sscanf(line, "%s %s %s\r\n", method, url, version) != 3) {
  279.                     // error
  280.                 }
  281.             }
  282.             else if (strstr(line, "CSeq")) {
  283.                 if (sscanf(line, "CSeq: %d\r\n", &CSeq) != 1) {
  284.                     // error
  285.                 }
  286.             }
  287.             else if (!strncmp(line, "Transport:", strlen("Transport:"))) {
  288.                 // Transport: RTP/AVP/UDP;unicast;client_port=13358-13359
  289.                 // Transport: RTP/AVP;unicast;client_port=13358-13359
  290.                 if (sscanf(line, "Transport: RTP/AVP/UDP;unicast;client_port=%d-%d\r\n",
  291.                     &clientRtpPort, &clientRtcpPort) != 2) {
  292.                     // error
  293.                     printf("parse Transport error \n");
  294.                 }
  295.             }
  296.             line = strtok(NULL, sep);
  297.         }
  298.         if (!strcmp(method, "OPTIONS")) {
  299.             if (handleCmd_OPTIONS(sBuf, CSeq))
  300.             {
  301.                 printf("failed to handle options\n");
  302.                 break;
  303.             }
  304.         }
  305.         else if (!strcmp(method, "DESCRIBE")) {
  306.             if (handleCmd_DESCRIBE(sBuf, CSeq, url))
  307.             {
  308.                 printf("failed to handle describe\n");
  309.                 break;
  310.             }
  311.         }
  312.         else if (!strcmp(method, "SETUP")) {
  313.             if (handleCmd_SETUP(sBuf, CSeq, clientRtpPort))
  314.             {
  315.                 printf("failed to handle setup\n");
  316.                 break;
  317.             }
  318.             serverRtpSockfd = createUdpSocket();
  319.             serverRtcpSockfd = createUdpSocket();
  320.             if (serverRtpSockfd < 0 || serverRtcpSockfd < 0)
  321.             {
  322.                 printf("failed to create udp socket\n");
  323.                 break;
  324.             }
  325.             if (bindSocketAddr(serverRtpSockfd, "0.0.0.0", SERVER_RTP_PORT) < 0 ||
  326.                 bindSocketAddr(serverRtcpSockfd, "0.0.0.0", SERVER_RTCP_PORT) < 0)
  327.             {
  328.                 printf("failed to bind addr\n");
  329.                 break;
  330.             }
  331.         }
  332.         else if (!strcmp(method, "PLAY")) {
  333.             if (handleCmd_PLAY(sBuf, CSeq))
  334.             {
  335.                 printf("failed to handle play\n");
  336.                 break;
  337.             }
  338.         }
  339.         else {
  340.             printf("未定义的method = %s \n", method);
  341.             break;
  342.         }
  343.         printf("sBuf = %s \n", sBuf);
  344.         printf("%s sBuf = %s \n", __FUNCTION__, sBuf);
  345.         send(clientSockfd, sBuf, strlen(sBuf), 0);
  346.         //开始播放,发送RTP包
  347.         if (!strcmp(method, "PLAY")) {
  348.             int frameSize, startCode;               // 用于处理视频帧和起始码
  349.             char* frame = (char*)malloc(500000);    // 用于存储读取的视频帧数据
  350.             struct RtpPacket* rtpPacket = (struct RtpPacket*)malloc(500000);    // 用于存储RTP包
  351.             FILE* fp = fopen(H264_FILE_NAME, "rb");
  352.             if (!fp) {
  353.                 printf("读取 %s 失败\n", H264_FILE_NAME);
  354.                 break;
  355.             }
  356.             rtpHeaderInit(rtpPacket, 0, 0, 0, RTP_VESION, RTP_PAYLOAD_TYPE_H264, 0,
  357.                 0, 0, 0x88923423);                  // 初始化 RTP 包头,设置相关参数如版本、负载类型等。
  358.             printf("start play\n");
  359.             printf("client ip:%s\n", clientIP);
  360.             printf("client port:%d\n", clientRtpPort);
  361.             while (true) {
  362.                 frameSize = getFrameFromH264File(fp, frame, 500000);
  363.                 if (frameSize < 0)
  364.                 {
  365.                     printf("读取%s结束,frameSize=%d \n", H264_FILE_NAME, frameSize);
  366.                     break;
  367.                 }
  368.                 if (startCode3(frame))
  369.                     startCode = 3;
  370.                 else
  371.                     startCode = 4;
  372.                 frameSize -= startCode;
  373.                 rtpSendH264Frame(serverRtpSockfd, clientIP, clientRtpPort,
  374.                     rtpPacket, frame + startCode, frameSize);   // 将视频帧数据封装成 RTP 包并发送给客户端
  375.                
  376.                 Sleep(40);  // 用于控制发送间隔,模拟帧率(此处为每秒 25 帧)
  377.                 //usleep(40000);//1000/25 * 1000
  378.             }
  379.             free(frame);
  380.             free(rtpPacket);
  381.             break;
  382.         }
  383.         memset(method,0,sizeof(method)/sizeof(char));
  384.         memset(url,0,sizeof(url)/sizeof(char));
  385.         CSeq = 0;
  386.     }
  387.     closesocket(clientSockfd);
  388.     if (serverRtpSockfd) {
  389.         closesocket(serverRtpSockfd);
  390.     }
  391.     if (serverRtcpSockfd > 0) {
  392.         closesocket(serverRtcpSockfd);
  393.     }
  394.     free(rBuf);
  395.     free(sBuf);
  396. }
  397. int main(int argc, char* argv[])
  398. {
  399.     // 启动windows socket start
  400.     WSADATA wsaData;
  401.     if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
  402.     {
  403.         printf("PC Server Socket Start Up Error \n");
  404.         return -1;
  405.     }
  406.     // 启动windows socket end
  407.     int rtspServerSockfd;
  408.     rtspServerSockfd = createTcpSocket();
  409.     if (rtspServerSockfd < 0)
  410.     {
  411.         WSACleanup();
  412.         printf("failed to create tcp socket\n");
  413.         return -1;
  414.     }
  415.     if (bindSocketAddr(rtspServerSockfd, "0.0.0.0", SERVER_PORT) < 0)
  416.     {
  417.         printf("failed to bind addr\n");
  418.         return -1;
  419.     }
  420.     if (listen(rtspServerSockfd, 10) < 0)
  421.     {
  422.         printf("failed to listen\n");
  423.         return -1;
  424.     }
  425.     printf("%s rtsp://127.0.0.1:%d\n", __FILE__, SERVER_PORT);
  426.     while (true) {
  427.         int clientSockfd;
  428.         char clientIp[40];
  429.         int clientPort;
  430.         clientSockfd = acceptClient(rtspServerSockfd, clientIp, &clientPort);
  431.         if (clientSockfd < 0)
  432.         {
  433.             printf("failed to accept client\n");
  434.             return -1;
  435.         }
  436.         printf("accept client;client ip:%s,client port:%d\n", clientIp, clientPort);
  437.         doClient(clientSockfd, clientIp, clientPort);
  438.     }
  439.     closesocket(rtspServerSockfd);
  440.     return 0;
  441. }
复制代码
rtp.h

  1. #pragma once#pragma comment(lib, "ws2_32.lib")#include <stdint.h>#define RTP_VESION              2#define RTP_PAYLOAD_TYPE_H264   96#define RTP_PAYLOAD_TYPE_AAC    97#define RTP_HEADER_SIZE         12#define RTP_MAX_PKT_SIZE        1400 /*  *    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                           |  *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+  *   |           synchronization source (SSRC) identifier            |  *   +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+  *   |            contributing source (CSRC) identifiers             |  *   :                             ....                              :  *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+  *  */struct RtpHeader
  2. {
  3.     /* byte 0 */
  4.     uint8_t csrcLen : 4;//CSRC计数器,占4位,指示CSRC 标识符的个数。
  5.     uint8_t extension : 1;//占1位,如果X=1,则在RTP报头后跟有一个扩展报头。
  6.     uint8_t padding : 1;//填充标志,占1位,如果P=1,则在该报文的尾部填充一个或多个额外的八位组,它们不是有效载荷的一部分。
  7.     uint8_t version : 2;//RTP协议的版本号,占2位,当前协议版本号为2。
  8.     /* byte 1 */
  9.     uint8_t payloadType : 7;//有效载荷类型,占7位,用于说明RTP报文中有效载荷的类型,如GSM音频、JPEM图像等。
  10.     uint8_t marker : 1;//标记,占1位,不同的有效载荷有不同的含义,对于视频,标记一帧的结束;对于音频,标记会话的开始。
  11.     /* bytes 2,3 */
  12.     uint16_t seq;//占16位,用于标识发送者所发送的RTP报文的序列号,每发送一个报文,序列号增1。接收者通过序列号来检测报文丢失情况,重新排序报文,恢复数据。
  13.     /* bytes 4-7 */
  14.     uint32_t timestamp;//占32位,时戳反映了该RTP报文的第一个八位组的采样时刻。接收者使用时戳来计算延迟和延迟抖动,并进行同步控制。
  15.     /* bytes 8-11 */
  16.     uint32_t ssrc;//占32位,用于标识同步信源。该标识符是随机选择的,参加同一视频会议的两个同步信源不能有相同的SSRC。
  17.    /*标准的RTP Header 还可能存在 0-15个特约信源(CSRC)标识符
  18.    
  19.    每个CSRC标识符占32位,可以有0~15个。每个CSRC标识了包含在该RTP报文有效载荷中的所有特约信源
  20.    */
  21. };
  22. struct RtpPacket{    struct RtpHeader rtpHeader;    uint8_t payload[0];};void rtpHeaderInit(struct RtpPacket* rtpPacket, uint8_t csrcLen, uint8_t extension,    uint8_t padding, uint8_t version, uint8_t payloadType, uint8_t marker,    uint16_t seq, uint32_t timestamp, uint32_t ssrc);int rtpSendPacketOverTcp(int clientSockfd, struct RtpPacket* rtpPacket, uint32_t dataSize);int rtpSendPacketOverUdp(int serverRtpSockfd, const char* ip, int16_t port, struct RtpPacket* rtpPacket, uint32_t dataSize);
复制代码
rtp.cpp

  1. #include <sys/types.h>
  2. #include <WinSock2.h>
  3. #include <WS2tcpip.h>
  4. #include <windows.h>
  5. #include "rtp.h"
  6. void rtpHeaderInit(struct RtpPacket* rtpPacket, uint8_t csrcLen, uint8_t extension,
  7.     uint8_t padding, uint8_t version, uint8_t payloadType, uint8_t marker,
  8.     uint16_t seq, uint32_t timestamp, uint32_t ssrc)
  9. {
  10.     rtpPacket->rtpHeader.csrcLen = csrcLen;
  11.     rtpPacket->rtpHeader.extension = extension;
  12.     rtpPacket->rtpHeader.padding = padding;
  13.     rtpPacket->rtpHeader.version = version;
  14.     rtpPacket->rtpHeader.payloadType = payloadType;
  15.     rtpPacket->rtpHeader.marker = marker;
  16.     rtpPacket->rtpHeader.seq = seq;
  17.     rtpPacket->rtpHeader.timestamp = timestamp;
  18.     rtpPacket->rtpHeader.ssrc = ssrc;
  19. }
  20. int rtpSendPacketOverTcp(int clientSockfd, struct RtpPacket* rtpPacket, uint32_t dataSize)
  21. {
  22.     rtpPacket->rtpHeader.seq = htons(rtpPacket->rtpHeader.seq);
  23.     rtpPacket->rtpHeader.timestamp = htonl(rtpPacket->rtpHeader.timestamp);
  24.     rtpPacket->rtpHeader.ssrc = htonl(rtpPacket->rtpHeader.ssrc);
  25.     uint32_t rtpSize = RTP_HEADER_SIZE + dataSize;
  26.     char* tempBuf = (char *)malloc(4 + rtpSize);
  27.     tempBuf[0] = 0x24;//$
  28.     tempBuf[1] = 0x00;
  29.     tempBuf[2] = (uint8_t)(((rtpSize) & 0xFF00) >> 8);
  30.     tempBuf[3] = (uint8_t)((rtpSize) & 0xFF);
  31.     memcpy(tempBuf + 4, (char*)rtpPacket, rtpSize);
  32.     int ret = send(clientSockfd, tempBuf, 4 + rtpSize, 0);
  33.     rtpPacket->rtpHeader.seq = ntohs(rtpPacket->rtpHeader.seq);
  34.     rtpPacket->rtpHeader.timestamp = ntohl(rtpPacket->rtpHeader.timestamp);
  35.     rtpPacket->rtpHeader.ssrc = ntohl(rtpPacket->rtpHeader.ssrc);
  36.     free(tempBuf);
  37.     tempBuf = NULL;
  38.     return ret;
  39. }
  40. int rtpSendPacketOverUdp(int serverRtpSockfd, const char* ip, int16_t port, struct RtpPacket* rtpPacket, uint32_t dataSize)
  41. {
  42.    
  43.     struct sockaddr_in addr;
  44.     int ret;
  45.     addr.sin_family = AF_INET;
  46.     addr.sin_port = htons(port);
  47.     addr.sin_addr.s_addr = inet_addr(ip);
  48.     rtpPacket->rtpHeader.seq = htons(rtpPacket->rtpHeader.seq);//从主机字节顺序转变成网络字节顺序(大端字节序)
  49.     rtpPacket->rtpHeader.timestamp = htonl(rtpPacket->rtpHeader.timestamp);
  50.     rtpPacket->rtpHeader.ssrc = htonl(rtpPacket->rtpHeader.ssrc);
  51.     ret = sendto(serverRtpSockfd, (char *)rtpPacket, dataSize + RTP_HEADER_SIZE, 0,
  52.         (struct sockaddr*)&addr, sizeof(addr));
  53.     rtpPacket->rtpHeader.seq = ntohs(rtpPacket->rtpHeader.seq);
  54.     rtpPacket->rtpHeader.timestamp = ntohl(rtpPacket->rtpHeader.timestamp);
  55.     rtpPacket->rtpHeader.ssrc = ntohl(rtpPacket->rtpHeader.ssrc);
  56.     return ret;
  57. }
复制代码
四、参考

RTP明白
H264简介
H264基础知识入门


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

北冰洋以北

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表