0
点赞
收藏
分享

微信扫一扫

RTP传输H.264

老榆 2022-04-29 阅读 73
图像处理

从零开始写一个RTSP服务器系列

★我的开源项目-RtspServer

从零开始写一个RTSP服务器(一)RTSP协议讲解

从零开始写一个RTSP服务器(二)RTSP协议的实现

从零开始写一个RTSP服务器(三)RTP传输H.264

从零开始写一个RTSP服务器(四)一个传输H.264的RTSP服务器

从零开始写一个RTSP服务器(五)RTP传输AAC

从零开始写一个RTSP服务器(六)一个传输AAC的RTSP服务器

从零开始写一个RTSP服务器(七)多播传输RTP包

从零开始写一个RTSP服务器(八)一个多播的RTSP服务器

从零开始写一个RTSP服务器(九)一个RTP OVER RTSP/TCP的RTSP服务器

从零开始写一个RTSP服务器(三)RTP传输H.264

文章目录


本篇文章目标,使用vlc打开sdp文件后,可以观看到视频数据

一、RTP封装

1.1 RTP数据结构

RTP包格式前面已经比较详细的介绍过,参考从零开始写一个RTSP服务器(一)不一样的RTSP协议讲解

看一张RTP头的格式图回忆一下

在这里插入图片描述

每个RTP包都包含这样一个RTP头部和RTP数据,为了方便,我将这个头部封装成一个结构体,还有发送包封装成一个函数,下面来看一看

  • RTP头结构体
/*
* 作者:_JT_
* 博客:https://blog.csdn.net/weixin_42462202
*/

struct RtpHeader
{
/* byte 0 */
uint8_t csrcLen:4;
uint8_t extension:1;
uint8_t padding:1;
uint8_t version:2;

 <span class="token comment">/* byte 1 */</span>
 uint8_t payloadType<span class="token punctuation">:</span><span class="token number">7</span><span class="token punctuation">;</span>
 uint8_t marker<span class="token punctuation">:</span><span class="token number">1</span><span class="token punctuation">;</span>
 
 <span class="token comment">/* bytes 2,3 */</span>
 uint16_t seq<span class="token punctuation">;</span>
 
 <span class="token comment">/* bytes 4-7 */</span>
 uint32_t timestamp<span class="token punctuation">;</span>
 
 <span class="token comment">/* bytes 8-11 */</span>
 uint32_t ssrc<span class="token punctuation">;</span>

};

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

其中的:n是一种位表示法,这个结构体跟RTP的头部一一对应

  • RTP的发包函数

    RTP包

    struct RtpPacket
    {
        struct RtpHeader rtpHeader;
        uint8_t payload[0];
    };
    
       

    • 1
    • 2
    • 3
    • 4
    • 5

    这是我封装的一个RTP包,包含一个RTP头部和RTP载荷,uint8_t payload[0]并不占用空间,它表示rtp头部接下来紧跟着的地址

    RTP的发包函数

    /*
     * 函数功能:发送RTP包
     * 参数 socket:表示本机的udp套接字
     * 参数 ip:表示目的ip地址
     * 参数 port:表示目的的端口号
     * 参数 rtpPacket:表示rtp包
     * 参数 dataSize:表示rtp包中载荷的大小
     * 放回值:发送字节数
     */
    int rtpSendPacket(int socket, char* ip, int16_t port, struct RtpPacket* rtpPacket, uint32_t dataSize)
    {
        struct sockaddr_in addr;
        int ret;
    
    addr<span class="token punctuation">.</span>sin_family <span class="token operator">=</span> AF_INET<span class="token punctuation">;</span>
    addr<span class="token punctuation">.</span>sin_port <span class="token operator">=</span> <span class="token function">htons</span><span class="token punctuation">(</span>port<span class="token punctuation">)</span><span class="token punctuation">;</span>
    addr<span class="token punctuation">.</span>sin_addr<span class="token punctuation">.</span>s_addr <span class="token operator">=</span> <span class="token function">inet_addr</span><span class="token punctuation">(</span>ip<span class="token punctuation">)</span><span class="token punctuation">;</span>
    
    rtpPacket<span class="token operator">-&gt;</span>rtpHeader<span class="token punctuation">.</span>seq <span class="token operator">=</span> <span class="token function">htons</span><span class="token punctuation">(</span>rtpPacket<span class="token operator">-&gt;</span>rtpHeader<span class="token punctuation">.</span>seq<span class="token punctuation">)</span><span class="token punctuation">;</span>
    rtpPacket<span class="token operator">-&gt;</span>rtpHeader<span class="token punctuation">.</span>timestamp <span class="token operator">=</span> <span class="token function">htonl</span><span class="token punctuation">(</span>rtpPacket<span class="token operator">-&gt;</span>rtpHeader<span class="token punctuation">.</span>timestamp<span class="token punctuation">)</span><span class="token punctuation">;</span>
    rtpPacket<span class="token operator">-&gt;</span>rtpHeader<span class="token punctuation">.</span>ssrc <span class="token operator">=</span> <span class="token function">htonl</span><span class="token punctuation">(</span>rtpPacket<span class="token operator">-&gt;</span>rtpHeader<span class="token punctuation">.</span>ssrc<span class="token punctuation">)</span><span class="token punctuation">;</span>
    
    ret <span class="token operator">=</span> <span class="token function">sendto</span><span class="token punctuation">(</span>socket<span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token keyword">void</span><span class="token operator">*</span><span class="token punctuation">)</span>rtpPacket<span class="token punctuation">,</span> dataSize<span class="token operator">+</span>RTP_HEADER_SIZE<span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span>
                    <span class="token punctuation">(</span><span class="token keyword">struct</span> sockaddr<span class="token operator">*</span><span class="token punctuation">)</span><span class="token operator">&amp;</span>addr<span class="token punctuation">,</span> <span class="token keyword">sizeof</span><span class="token punctuation">(</span>addr<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    
    rtpPacket<span class="token operator">-&gt;</span>rtpHeader<span class="token punctuation">.</span>seq <span class="token operator">=</span> <span class="token function">ntohs</span><span class="token punctuation">(</span>rtpPacket<span class="token operator">-&gt;</span>rtpHeader<span class="token punctuation">.</span>seq<span class="token punctuation">)</span><span class="token punctuation">;</span>
    rtpPacket<span class="token operator">-&gt;</span>rtpHeader<span class="token punctuation">.</span>timestamp <span class="token operator">=</span> <span class="token function">ntohl</span><span class="token punctuation">(</span>rtpPacket<span class="token operator">-&gt;</span>rtpHeader<span class="token punctuation">.</span>timestamp<span class="token punctuation">)</span><span class="token punctuation">;</span>
    rtpPacket<span class="token operator">-&gt;</span>rtpHeader<span class="token punctuation">.</span>ssrc <span class="token operator">=</span> <span class="token function">ntohl</span><span class="token punctuation">(</span>rtpPacket<span class="token operator">-&gt;</span>rtpHeader<span class="token punctuation">.</span>ssrc<span class="token punctuation">)</span><span class="token punctuation">;</span>
    
    <span class="token keyword">return</span> ret<span class="token punctuation">;</span>
    

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

仔细看这个函数你应该可以看懂

我们设置好一个包之后,就会调用这个函数发送指定目标

这个函数中多处使用htons等函数,是因为RTP是采用网络字节序(大端模式),所以要将主机字节字节序转换为网络字节序

下面给出源码,rtp.hrtp.c,这两个文件在后面讲经常使用

1.2 源码

rtp.h

/*
 * 作者:_JT_
 * 博客:https://blog.csdn.net/weixin_42462202
 */

#ifndef RTP_H
#define RTP_H
#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
{
/
byte 0 */
uint8_t csrcLen:4;
uint8_t extension:1;
uint8_t padding:1;
uint8_t version:2;

<span class="token comment">/* byte 1 */</span>
uint8_t payloadType<span class="token punctuation">:</span><span class="token number">7</span><span class="token punctuation">;</span>
uint8_t marker<span class="token punctuation">:</span><span class="token number">1</span><span class="token punctuation">;</span>

<span class="token comment">/* bytes 2,3 */</span>
uint16_t seq<span class="token punctuation">;</span>

<span class="token comment">/* bytes 4-7 */</span>
uint32_t timestamp<span class="token punctuation">;</span>

<span class="token comment">/* bytes 8-11 */</span>
uint32_t ssrc<span class="token punctuation">;</span>

};

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 rtpSendPacket(int socket, char ip, int16_t port, struct RtpPacket* rtpPacket, uint32_t dataSize);

#endif //RTP_H

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67

rtp.c

/*
 * 作者:_JT_
 * 博客:https://blog.csdn.net/weixin_42462202
 */

#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include “rtp.h”

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)
{
rtpPacket->rtpHeader.csrcLen = csrcLen;
rtpPacket->rtpHeader.extension = extension;
rtpPacket->rtpHeader.padding = padding;
rtpPacket->rtpHeader.version = version;
rtpPacket->rtpHeader.payloadType = payloadType;
rtpPacket->rtpHeader.marker = marker;
rtpPacket->rtpHeader.seq = seq;
rtpPacket->rtpHeader.timestamp = timestamp;
rtpPacket->rtpHeader.ssrc = ssrc;
}

int rtpSendPacket(int socket, char ip, int16_t port, struct RtpPacket rtpPacket, uint32_t dataSize)
{
struct sockaddr_in addr;
int ret;

addr<span class="token punctuation">.</span>sin_family <span class="token operator">=</span> AF_INET<span class="token punctuation">;</span>
addr<span class="token punctuation">.</span>sin_port <span class="token operator">=</span> <span class="token function">htons</span><span class="token punctuation">(</span>port<span class="token punctuation">)</span><span class="token punctuation">;</span>
addr<span class="token punctuation">.</span>sin_addr<span class="token punctuation">.</span>s_addr <span class="token operator">=</span> <span class="token function">inet_addr</span><span class="token punctuation">(</span>ip<span class="token punctuation">)</span><span class="token punctuation">;</span>

rtpPacket<span class="token operator">-&gt;</span>rtpHeader<span class="token punctuation">.</span>seq <span class="token operator">=</span> <span class="token function">htons</span><span class="token punctuation">(</span>rtpPacket<span class="token operator">-&gt;</span>rtpHeader<span class="token punctuation">.</span>seq<span class="token punctuation">)</span><span class="token punctuation">;</span>
rtpPacket<span class="token operator">-&gt;</span>rtpHeader<span class="token punctuation">.</span>timestamp <span class="token operator">=</span> <span class="token function">htonl</span><span class="token punctuation">(</span>rtpPacket<span class="token operator">-&gt;</span>rtpHeader<span class="token punctuation">.</span>timestamp<span class="token punctuation">)</span><span class="token punctuation">;</span>
rtpPacket<span class="token operator">-&gt;</span>rtpHeader<span class="token punctuation">.</span>ssrc <span class="token operator">=</span> <span class="token function">htonl</span><span class="token punctuation">(</span>rtpPacket<span class="token operator">-&gt;</span>rtpHeader<span class="token punctuation">.</span>ssrc<span class="token punctuation">)</span><span class="token punctuation">;</span>

ret <span class="token operator">=</span> <span class="token function">sendto</span><span class="token punctuation">(</span>socket<span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token keyword">void</span><span class="token operator">*</span><span class="token punctuation">)</span>rtpPacket<span class="token punctuation">,</span> dataSize<span class="token operator">+</span>RTP_HEADER_SIZE<span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span>
                <span class="token punctuation">(</span><span class="token keyword">struct</span> sockaddr<span class="token operator">*</span><span class="token punctuation">)</span><span class="token operator">&amp;</span>addr<span class="token punctuation">,</span> <span class="token keyword">sizeof</span><span class="token punctuation">(</span>addr<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

rtpPacket<span class="token operator">-&gt;</span>rtpHeader<span class="token punctuation">.</span>seq <span class="token operator">=</span> <span class="token function">ntohs</span><span class="token punctuation">(</span>rtpPacket<span class="token operator">-&gt;</span>rtpHeader<span class="token punctuation">.</span>seq<span class="token punctuation">)</span><span class="token punctuation">;</span>
rtpPacket<span class="token operator">-&gt;</span>rtpHeader<span class="token punctuation">.</span>timestamp <span class="token operator">=</span> <span class="token function">ntohl</span><span class="token punctuation">(</span>rtpPacket<span class="token operator">-&gt;</span>rtpHeader<span class="token punctuation">.</span>timestamp<span class="token punctuation">)</span><span class="token punctuation">;</span>
rtpPacket<span class="token operator">-&gt;</span>rtpHeader<span class="token punctuation">.</span>ssrc <span class="token operator">=</span> <span class="token function">ntohl</span><span class="token punctuation">(</span>rtpPacket<span class="token operator">-&gt;</span>rtpHeader<span class="token punctuation">.</span>ssrc<span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">return</span> ret<span class="token punctuation">;</span>

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50

二、H.264的RTP打包

2.1 H.264格式

H.264由一个一个的NALU组成,每个NALU之间使用00 00 00 0100 00 01分隔开

每个NALU的第一次字节都有特殊的含义,其内容如下

描述
bit[7]必须为0
bit[5-6]标记该NALU的重要性
bit[0-4]NALU单元的类型

好,对于H.264格式了解这么多就够了,我们的目的是想从一个H.264的文件中将一个一个的NALU提取出来,然后封装成RTP包,下面介绍如何将NALU封装成RTP包

2.2 H.264的RTP打包方式

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

2.3 H.264 RTP包的时间戳计算

RTP包的时间戳起始值是随机的

RTP包的时间戳增量怎么计算?

假设时钟频率为90000,帧率为25

频率为90000表示一秒用90000点来表示

帧率为25,那么一帧就是1/25秒

所以一帧有90000*(1/25)=3600个点来表示

因此每一帧数据的时间增量为3600

2.4 源码

rtp_h264.c

这里给出rtp发送H.264的源码

/*
 * 作者:_JT_
 * 博客:https://blog.csdn.net/weixin_42462202
 */

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>

#include “rtp.h”

#define H264_FILE_NAME “test.h264”
#define CLIENT_IP “127.0.0.1”
#define CLIENT_PORT 9832

#define FPS 25

static inline int startCode3(char* buf)
{
if(buf[0] 0 && buf[1] 0 && buf[2] == 1)
return 1;
else
return 0;
}

static inline int startCode4(char* buf)
{
if(buf[0] 0 && buf[1] 0 && buf[2] 0 && buf[3] 1)
return 1;
else
return 0;
}

static char findNextStartCode(char buf, int len)
{
int i;

<span class="token keyword">if</span><span class="token punctuation">(</span>len <span class="token operator">&lt;</span> <span class="token number">3</span><span class="token punctuation">)</span>
    <span class="token keyword">return</span> <span class="token constant">NULL</span><span class="token punctuation">;</span>

<span class="token keyword">for</span><span class="token punctuation">(</span>i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator">&lt;</span> len<span class="token operator">-</span><span class="token number">3</span><span class="token punctuation">;</span> <span class="token operator">++</span>i<span class="token punctuation">)</span>
<span class="token punctuation">{<!-- --></span>
    <span class="token keyword">if</span><span class="token punctuation">(</span><span class="token function">startCode3</span><span class="token punctuation">(</span>buf<span class="token punctuation">)</span> <span class="token operator">||</span> <span class="token function">startCode4</span><span class="token punctuation">(</span>buf<span class="token punctuation">)</span><span class="token punctuation">)</span>
        <span class="token keyword">return</span> buf<span class="token punctuation">;</span>
    
    <span class="token operator">++</span>buf<span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">if</span><span class="token punctuation">(</span><span class="token function">startCode3</span><span class="token punctuation">(</span>buf<span class="token punctuation">)</span><span class="token punctuation">)</span>
    <span class="token keyword">return</span> buf<span class="token punctuation">;</span>

<span class="token keyword">return</span> <span class="token constant">NULL</span><span class="token punctuation">;</span>

}

static int getFrameFromH264File(int fd, char frame, int size)
{
int rSize, frameSize;
char nextStartCode;

<span class="token keyword">if</span><span class="token punctuation">(</span>fd <span class="token operator">&lt;</span> <span class="token number">0</span><span class="token punctuation">)</span>
    <span class="token keyword">return</span> fd<span class="token punctuation">;</span>

rSize <span class="token operator">=</span> <span class="token function">read</span><span class="token punctuation">(</span>fd<span class="token punctuation">,</span> frame<span class="token punctuation">,</span> size<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">if</span><span class="token punctuation">(</span><span class="token operator">!</span><span class="token function">startCode3</span><span class="token punctuation">(</span>frame<span class="token punctuation">)</span> <span class="token operator">&amp;&amp;</span> <span class="token operator">!</span><span class="token function">startCode4</span><span class="token punctuation">(</span>frame<span class="token punctuation">)</span><span class="token punctuation">)</span>
    <span class="token keyword">return</span> <span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">;</span>

nextStartCode <span class="token operator">=</span> <span class="token function">findNextStartCode</span><span class="token punctuation">(</span>frame<span class="token operator">+</span><span class="token number">3</span><span class="token punctuation">,</span> rSize<span class="token operator">-</span><span class="token number">3</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">if</span><span class="token punctuation">(</span><span class="token operator">!</span>nextStartCode<span class="token punctuation">)</span>
<span class="token punctuation">{<!-- --></span>
    <span class="token function">lseek</span><span class="token punctuation">(</span>fd<span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token constant">SEEK_SET</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    frameSize <span class="token operator">=</span> rSize<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token keyword">else</span>
<span class="token punctuation">{<!-- --></span>
    frameSize <span class="token operator">=</span> <span class="token punctuation">(</span>nextStartCode<span class="token operator">-</span>frame<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token function">lseek</span><span class="token punctuation">(</span>fd<span class="token punctuation">,</span> frameSize<span class="token operator">-</span>rSize<span class="token punctuation">,</span> <span class="token constant">SEEK_CUR</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">return</span> frameSize<span class="token punctuation">;</span>

}

static int createUdpSocket()
{
int fd;
int on = 1;

fd <span class="token operator">=</span> <span class="token function">socket</span><span class="token punctuation">(</span>AF_INET<span class="token punctuation">,</span> SOCK_DGRAM<span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">if</span><span class="token punctuation">(</span>fd <span class="token operator">&lt;</span> <span class="token number">0</span><span class="token punctuation">)</span>
    <span class="token keyword">return</span> <span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">;</span>

<span class="token function">setsockopt</span><span class="token punctuation">(</span>fd<span class="token punctuation">,</span> SOL_SOCKET<span class="token punctuation">,</span> SO_REUSEADDR<span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token keyword">const</span> <span class="token keyword">char</span><span class="token operator">*</span><span class="token punctuation">)</span><span class="token operator">&amp;</span>on<span class="token punctuation">,</span> <span class="token keyword">sizeof</span><span class="token punctuation">(</span>on<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">return</span> fd<span class="token punctuation">;</span>

}

static int rtpSendH264Frame(int socket, char ip, int16_t port,
struct RtpPacket rtpPacket, uint8_t* frame, uint32_t frameSize)
{
uint8_t naluType; // nalu第一个字节
int sendBytes = 0;
int ret;

naluType <span class="token operator">=</span> frame<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">;</span>

<span class="token keyword">if</span> <span class="token punctuation">(</span>frameSize <span class="token operator">&lt;=</span> RTP_MAX_PKT_SIZE<span class="token punctuation">)</span> <span class="token comment">// nalu长度小于最大包场:单一NALU单元模式</span>
<span class="token punctuation">{<!-- --></span>
    <span class="token comment">/*
     *   0 1 2 3 4 5 6 7 8 9
     *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     *  |F|NRI|  Type   | a single NAL unit ... |
     *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     */</span>
    <span class="token function">memcpy</span><span class="token punctuation">(</span>rtpPacket<span class="token operator">-&gt;</span>payload<span class="token punctuation">,</span> frame<span class="token punctuation">,</span> frameSize<span class="token punctuation">)</span><span class="token punctuation">;</span>
    ret <span class="token operator">=</span> <span class="token function">rtpSendPacket</span><span class="token punctuation">(</span>socket<span class="token punctuation">,</span> ip<span class="token punctuation">,</span> port<span class="token punctuation">,</span> rtpPacket<span class="token punctuation">,</span> frameSize<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">if</span><span class="token punctuation">(</span>ret <span class="token operator">&lt;</span> <span class="token number">0</span><span class="token punctuation">)</span>
        <span class="token keyword">return</span> <span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">;</span>

    rtpPacket<span class="token operator">-&gt;</span>rtpHeader<span class="token punctuation">.</span>seq<span class="token operator">++</span><span class="token punctuation">;</span>
    sendBytes <span class="token operator">+</span><span class="token operator">=</span> ret<span class="token punctuation">;</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token punctuation">(</span>naluType <span class="token operator">&amp;</span> <span class="token number">0x1F</span><span class="token punctuation">)</span> <span class="token operator">==</span> <span class="token number">7</span> <span class="token operator">||</span> <span class="token punctuation">(</span>naluType <span class="token operator">&amp;</span> <span class="token number">0x1F</span><span class="token punctuation">)</span> <span class="token operator">==</span> <span class="token number">8</span><span class="token punctuation">)</span> <span class="token comment">// 如果是SPS、PPS就不需要加时间戳</span>
        <span class="token keyword">goto</span> out<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token keyword">else</span> <span class="token comment">// nalu长度小于最大包场:分片模式</span>
<span class="token punctuation">{<!-- --></span>
    <span class="token comment">/*
     *  0                   1                   2
     *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3
     * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     * | FU indicator  |   FU header   |   FU payload   ...  |
     * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     */</span>

    <span class="token comment">/*
     *     FU Indicator
     *    0 1 2 3 4 5 6 7
     *   +-+-+-+-+-+-+-+-+
     *   |F|NRI|  Type   |
     *   +---------------+
     */</span>

    <span class="token comment">/*
     *      FU Header
     *    0 1 2 3 4 5 6 7
     *   +-+-+-+-+-+-+-+-+
     *   |S|E|R|  Type   |
     *   +---------------+
     */</span>

    <span class="token keyword">int</span> pktNum <span class="token operator">=</span> frameSize <span class="token operator">/</span> RTP_MAX_PKT_SIZE<span class="token punctuation">;</span>       <span class="token comment">// 有几个完整的包</span>
    <span class="token keyword">int</span> remainPktSize <span class="token operator">=</span> frameSize <span class="token operator">%</span> RTP_MAX_PKT_SIZE<span class="token punctuation">;</span> <span class="token comment">// 剩余不完整包的大小</span>
    <span class="token keyword">int</span> i<span class="token punctuation">,</span> pos <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span>

    <span class="token comment">/* 发送完整的包 */</span>
    <span class="token keyword">for</span> <span class="token punctuation">(</span>i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator">&lt;</span> pktNum<span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span>
    <span class="token punctuation">{<!-- --></span>
        rtpPacket<span class="token operator">-&gt;</span>payload<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">(</span>naluType <span class="token operator">&amp;</span> <span class="token number">0x60</span><span class="token punctuation">)</span> <span class="token operator">|</span> <span class="token number">28</span><span class="token punctuation">;</span>
        rtpPacket<span class="token operator">-&gt;</span>payload<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span> <span class="token operator">=</span> naluType <span class="token operator">&amp;</span> <span class="token number">0x1F</span><span class="token punctuation">;</span>
        
        <span class="token keyword">if</span> <span class="token punctuation">(</span>i <span class="token operator">==</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token comment">//第一包数据</span>
            rtpPacket<span class="token operator">-&gt;</span>payload<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span> <span class="token operator">|</span><span class="token operator">=</span> <span class="token number">0x80</span><span class="token punctuation">;</span> <span class="token comment">// start</span>
        <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>remainPktSize <span class="token operator">==</span> <span class="token number">0</span> <span class="token operator">&amp;&amp;</span> i <span class="token operator">==</span> pktNum <span class="token operator">-</span> <span class="token number">1</span><span class="token punctuation">)</span> <span class="token comment">//最后一包数据</span>
            rtpPacket<span class="token operator">-&gt;</span>payload<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span> <span class="token operator">|</span><span class="token operator">=</span> <span class="token number">0x40</span><span class="token punctuation">;</span> <span class="token comment">// end</span>

        <span class="token function">memcpy</span><span class="token punctuation">(</span>rtpPacket<span class="token operator">-&gt;</span>payload<span class="token operator">+</span><span class="token number">2</span><span class="token punctuation">,</span> frame<span class="token operator">+</span>pos<span class="token punctuation">,</span> RTP_MAX_PKT_SIZE<span class="token punctuation">)</span><span class="token punctuation">;</span>
        ret <span class="token operator">=</span> <span class="token function">rtpSendPacket</span><span class="token punctuation">(</span>socket<span class="token punctuation">,</span> ip<span class="token punctuation">,</span> port<span class="token punctuation">,</span> rtpPacket<span class="token punctuation">,</span> RTP_MAX_PKT_SIZE<span class="token operator">+</span><span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">if</span><span class="token punctuation">(</span>ret <span class="token operator">&lt;</span> <span class="token number">0</span><span class="token punctuation">)</span>
            <span class="token keyword">return</span> <span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">;</span>

        rtpPacket<span class="token operator">-&gt;</span>rtpHeader<span class="token punctuation">.</span>seq<span class="token operator">++</span><span class="token punctuation">;</span>
        sendBytes <span class="token operator">+</span><span class="token operator">=</span> ret<span class="token punctuation">;</span>
        pos <span class="token operator">+</span><span class="token operator">=</span> RTP_MAX_PKT_SIZE<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token comment">/* 发送剩余的数据 */</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>remainPktSize <span class="token operator">&gt;</span> <span class="token number">0</span><span class="token punctuation">)</span>
    <span class="token punctuation">{<!-- --></span>
        rtpPacket<span class="token operator">-&gt;</span>payload<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">(</span>naluType <span class="token operator">&amp;</span> <span class="token number">0x60</span><span class="token punctuation">)</span> <span class="token operator">|</span> <span class="token number">28</span><span class="token punctuation">;</span>
        rtpPacket<span class="token operator">-&gt;</span>payload<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span> <span class="token operator">=</span> naluType <span class="token operator">&amp;</span> <span class="token number">0x1F</span><span class="token punctuation">;</span>
        rtpPacket<span class="token operator">-&gt;</span>payload<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span> <span class="token operator">|</span><span class="token operator">=</span> <span class="token number">0x40</span><span class="token punctuation">;</span> <span class="token comment">//end</span>

        <span class="token function">memcpy</span><span class="token punctuation">(</span>rtpPacket<span class="token operator">-&gt;</span>payload<span class="token operator">+</span><span class="token number">2</span><span class="token punctuation">,</span> frame<span class="token operator">+</span>pos<span class="token punctuation">,</span> remainPktSize<span class="token operator">+</span><span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        ret <span class="token operator">=</span> <span class="token function">rtpSendPacket</span><span class="token punctuation">(</span>socket<span class="token punctuation">,</span> ip<span class="token punctuation">,</span> port<span class="token punctuation">,</span> rtpPacket<span class="token punctuation">,</span> remainPktSize<span class="token operator">+</span><span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">if</span><span class="token punctuation">(</span>ret <span class="token operator">&lt;</span> <span class="token number">0</span><span class="token punctuation">)</span>
            <span class="token keyword">return</span> <span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">;</span>

        rtpPacket<span class="token operator">-&gt;</span>rtpHeader<span class="token punctuation">.</span>seq<span class="token operator">++</span><span class="token punctuation">;</span>
        sendBytes <span class="token operator">+</span><span class="token operator">=</span> ret<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

out:

<span class="token keyword">return</span> sendBytes<span class="token punctuation">;</span>

}

int main(int argc, char argv[])
{
int socket;
int fd;
int fps = 25;
int startCode;
struct RtpPacket rtpPacket;
uint8_t* frame;
uint32_t frameSize;

fd <span class="token operator">=</span> <span class="token function">open</span><span class="token punctuation">(</span>H264_FILE_NAME<span class="token punctuation">,</span> O_RDONLY<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">if</span><span class="token punctuation">(</span>fd <span class="token operator">&lt;</span> <span class="token number">0</span><span class="token punctuation">)</span>
<span class="token punctuation">{<!-- --></span>
    <span class="token function">printf</span><span class="token punctuation">(</span><span class="token string">"failed to open %s\n"</span><span class="token punctuation">,</span> H264_FILE_NAME<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">return</span> <span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

socket <span class="token operator">=</span> <span class="token function">createUdpSocket</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">if</span><span class="token punctuation">(</span>socket <span class="token operator">&lt;</span> <span class="token number">0</span><span class="token punctuation">)</span>
<span class="token punctuation">{<!-- --></span>
    <span class="token function">printf</span><span class="token punctuation">(</span><span class="token string">"failed to create socket\n"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">return</span> <span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

rtpPacket <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token keyword">struct</span> RtpPacket<span class="token operator">*</span><span class="token punctuation">)</span><span class="token function">malloc</span><span class="token punctuation">(</span><span class="token number">500000</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
frame <span class="token operator">=</span> <span class="token punctuation">(</span>uint8_t<span class="token operator">*</span><span class="token punctuation">)</span><span class="token function">malloc</span><span class="token punctuation">(</span><span class="token number">500000</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token function">rtpHeaderInit</span><span class="token punctuation">(</span>rtpPacket<span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> RTP_VESION<span class="token punctuation">,</span> RTP_PAYLOAD_TYPE_H264<span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span>
                <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0x88923423</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">while</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span>
<span class="token punctuation">{<!-- --></span>
    frameSize <span class="token operator">=</span> <span class="token function">getFrameFromH264File</span><span class="token punctuation">(</span>fd<span class="token punctuation">,</span> frame<span class="token punctuation">,</span> <span class="token number">500000</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">if</span><span class="token punctuation">(</span>frameSize <span class="token operator">&lt;</span> <span class="token number">0</span><span class="token punctuation">)</span>
    <span class="token punctuation">{<!-- --></span>
        <span class="token function">printf</span><span class="token punctuation">(</span><span class="token string">"read err\n"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">continue</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">if</span><span class="token punctuation">(</span><span class="token function">startCode3</span><span class="token punctuation">(</span>frame<span class="token punctuation">)</span><span class="token punctuation">)</span>
        startCode <span class="token operator">=</span> <span class="token number">3</span><span class="token punctuation">;</span>
    <span class="token keyword">else</span>
        startCode <span class="token operator">=</span> <span class="token number">4</span><span class="token punctuation">;</span>

    frameSize <span class="token operator">-</span><span class="token operator">=</span> startCode<span class="token punctuation">;</span>
    <span class="token function">rtpSendH264Frame</span><span class="token punctuation">(</span>socket<span class="token punctuation">,</span> CLIENT_IP<span class="token punctuation">,</span> CLIENT_PORT<span class="token punctuation">,</span>
                        rtpPacket<span class="token punctuation">,</span> frame<span class="token operator">+</span>startCode<span class="token punctuation">,</span> frameSize<span class="token punctuation">)</span><span class="token punctuation">;</span>
    rtpPacket<span class="token operator">-&gt;</span>rtpHeader<span class="token punctuation">.</span>timestamp <span class="token operator">+</span><span class="token operator">=</span> <span class="token number">90000</span><span class="token operator">/</span>FPS<span class="token punctuation">;</span>

    <span class="token function">usleep</span><span class="token punctuation">(</span><span class="token number">1000</span><span class="token operator">*</span><span class="token number">1000</span><span class="token operator">/</span>fps<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token function">free</span><span class="token punctuation">(</span>rtpPacket<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">free</span><span class="token punctuation">(</span>frame<span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span>

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257

三、H.264 RTP打包的sdp描述

sdp文件有什么用?

sdp描述着媒体信息,当使用vlc打开这个sdp文件后,会根据这些信息做相应的操作(创建套接字…),然后等待接收RTP包

这里给出RTP打包H.264的sdp文件,并描述每一行是什么意思

m=video 9832 RTP/AVP 96 
a=rtpmap:96 H264/90000
a=framerate:25
c=IN IP4 127.0.0.1

 

  • 1
  • 2
  • 3
  • 4

这个一个媒体级的sdp描述,关于sdp文件描述详情可看从零开始写一个RTSP服务器(一)不一样的RTSP协议讲解

  • m=video 9832 RTP/AVP 96

    格式为 m=<媒体类型> <端口号> <传输协议> <媒体格式 >
    媒体类型:video,表示这是一个视频流

    端口号:9832,表示UDP发送的目的端口为9832

    传输协议:RTP/AVP,表示RTP OVER UDP,通过UDP发送RTP包

    媒体格式:表示负载类型(payload type),一般使用96表示H.264

  • a=rtpmap:96 H264/90000

    格式为a=rtpmap:<媒体格式><编码格式>/<时钟频率>

  • a=framerate:25

    表示帧率

  • c=IN IP4 127.0.0.1

    IN:表示internet

    IP4:表示IPV4

    127.0.0.1:表示UDP发送的目的地址为127.0.0.1

特别注意:这段sdp文件描述的udp发送的目的IP为127.0.0.1,目的端口为9832

四、测试

讲上面给出的源码rtp.crtp.hrtp_h264.c保存下来,然后编译运行

注意:该程序默认打开的是test.h264,如果你没有视频源,可以从RtspServer的example目录下获取

# gcc rtp.c rtp_h264.c
# ./a.out

 

  • 1
  • 2

讲上面的sdp文件保存为rtp_h264.sdp,使用vlc打开,即可观看到视频

# vlc rtp_h264.sdp

 

  • 1
  • 运行效果

    在这里插入图片描述

至此,我们已经完成了RTSP协议交互和RTP打包H.264,下一篇文章就可以来实现一个播放H.264的RTSP服务器了

举报

相关推荐

0 条评论