您的位置:首页 > 编程语言 > PHP开发

Ubuntu上实现rtp协议发送rtp包(进行H.264数据传输功能)------数据传输为UDP传输协议

2021-09-02 15:22 597 查看

1、代码功能

  通过编写相应结构体实现RTP包的封装,并编写对应的函数实现H.264数据的打包(单NALU打包和分片打包)---(udp传输),最后vlc视频播放器通过sdp文件实现RTP包的接收。

RTP报头格式具体见https://www.geek-share.com/detail/2776428160.html

RTP包的格式:

/*
* 作者:Frank
*/

#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.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = inet_addr(ip);

rtpPacket->rtpHeader.seq = htons(rtpPacket->rtpHeader.seq);
rtpPacket->rtpHeader.timestamp = htonl(rtpPacket->rtpHeader.timestamp);
rtpPacket->rtpHeader.ssrc = htonl(rtpPacket->rtpHeader.ssrc);

ret = sendto(socket, (void*)rtpPacket, dataSize+RTP_HEADER_SIZE, 0,
(struct sockaddr*)&addr, sizeof(addr));

rtpPacket->rtpHeader.seq = ntohs(rtpPacket->rtpHeader.seq);
rtpPacket->rtpHeader.timestamp = ntohl(rtpPacket->rtpHeader.timestamp);
rtpPacket->rtpHeader.ssrc = ntohl(rtpPacket->rtpHeader.ssrc);

return ret;
}
View Code

4.2、rtp.h

/*
* 作者:Frank
*/

#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;

/* byte 1 */
uint8_t payloadType:7;
uint8_t marker:1;

/* bytes 2,3 */
uint16_t seq;

/* bytes 4-7 */
uint32_t timestamp;

/* bytes 8-11 */
uint32_t ssrc;
};

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_
View Code

4.3、rtp_h264

/*
* 作者:Frank
*/

#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;

if(len < 3)
return NULL;

for(i = 0; i < len-3; ++i)
{
if(startCode3(buf) || startCode4(buf))
return buf;

++buf;
}

if(startCode3(buf))
return buf;

return NULL;
}

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

if(fd < 0)
return fd;

rSize = read(fd, frame, size);
if(!startCode3(frame) && !startCode4(frame))
return -1;

nextStartCode = findNextStartCode(frame+3, rSize-3);
if(!nextStartCode)
{
lseek(fd, 0, SEEK_SET);
frameSize = rSize;
}
else
{
frameSize = (nextStartCode-frame);
lseek(fd, frameSize-rSize, SEEK_CUR);
}

return frameSize;
}

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

fd = socket(AF_INET, SOCK_DGRAM, 0);
if(fd < 0)
return -1;

setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(on));

return fd;
}

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 = frame[0];

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

rtpPacket->rtpHeader.seq++;
sendBytes += ret;
if ((naluType & 0x1F) == 7 || (naluType & 0x1F) == 8) // 如果是SPS、PPS就不需要加时间戳
goto out;
}
else // nalu长度小于最大包场:分片模式
{
/*
*  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   ...  |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/

/*
*     FU Indicator
*    0 1 2 3 4 5 6 7
*   +-+-+-+-+-+-+-+-+
*   |F|NRI|  Type   |
*   +---------------+
*/

/*
*      FU Header
*    0 1 2 3 4 5 6 7
*   +-+-+-+-+-+-+-+-+
*   |S|E|R|  Type   |
*   +---------------+
*/

int pktNum = frameSize / RTP_MAX_PKT_SIZE;       // 有几个完整的包
int remainPktSize = frameSize % RTP_MAX_PKT_SIZE; // 剩余不完整包的大小
int i, pos = 1;

/* 发送完整的包 */
for (i = 0; i < pktNum; i++)
{
rtpPacket->payload[0] = (naluType & 0x60) | 28;
rtpPacket->payload[1] = naluType & 0x1F;

if (i == 0) //第一包数据
rtpPacket->payload[1] |= 0x80; // start
else if (remainPktSize == 0 && i == pktNum - 1) //最后一包数据
rtpPacket->payload[1] |= 0x40; // end

memcpy(rtpPacket->payload+2, frame+pos, RTP_MAX_PKT_SIZE);
ret = rtpSendPacket(socket, ip, port, rtpPacket, RTP_MAX_PKT_SIZE+2);
if(ret < 0)
return -1;

rtpPacket->rtpHeader.seq++;
sendBytes += ret;
pos += RTP_MAX_PKT_SIZE;
}

/* 发送剩余的数据 */
if (remainPktSize > 0)
{
rtpPacket->payload[0] = (naluType & 0x60) | 28;
rtpPacket->payload[1] = naluType & 0x1F;
rtpPacket->payload[1] |= 0x40; //end

memcpy(rtpPacket->payload+2, frame+pos, remainPktSize+2);
ret = rtpSendPacket(socket, ip, port, rtpPacket, remainPktSize+2);
if(ret < 0)
return -1;

rtpPacket->rtpHeader.seq++;
sendBytes += ret;
}
}

out:

return sendBytes;
}

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 = open(H264_FILE_NAME, O_RDONLY);
if(fd < 0)
{
printf("failed to open %s\n", H264_FILE_NAME);
return -1;
}

socket = createUdpSocket();
if(socket < 0)
{
printf("failed to create socket\n");
return -1;
}

rtpPacket = (struct RtpPacket*)malloc(500000);
frame = (uint8_t*)malloc(500000);

rtpHeaderInit(rtpPacket, 0, 0, 0, RTP_VESION, RTP_PAYLOAD_TYPE_H264, 0,
0, 0, 0x88923423);

while(1)
{
frameSize = getFrameFromH264File(fd, frame, 500000);
if(frameSize < 0)
{
printf("read err\n");
continue;
}

if(startCode3(frame))
startCode = 3;
else
startCode = 4;

frameSize -= startCode;
rtpSendH264Frame(socket, CLIENT_IP, CLIENT_PORT,
rtpPacket, frame+startCode, frameSize);
rtpPacket->rtpHeader.timestamp += 90000/FPS;

usleep(1000*1000/fps);
}

free(rtpPacket);
free(frame);

return 0;
}
View Code

5、功能实现

5.1、文件目录

5.2、编写过程

gcc -o rtp_h264 rtp_h264.c rtp.c

5.3、运行结果(有报错情况)

运行: vlc rtp_h264.sdp之前,需要先运行./rtp_h264

但是最后报错了:(视频会卡住,不在回放,而且对于大的视频文件.264,运行会直接报错)

Invalid UE golomb code
Invalid UE golomb code
Invalid UE golomb code
[00007f38a4c1e008] avcodec decoder error: more than 5 seconds of late video -> dropping frame (computer too slow ?)
[00007f38a4c1e008] avcodec decoder error: more than 5 seconds of late video -> dropping frame (computer too slow ?)
[00007f38a4c1e008] avcodec decoder error: more than 5 seconds of late video -> dropping frame (computer too slow ?)
[00007f38a4c1e008] avcodec decoder error: more than 5 seconds of late video -> dropping frame (computer too slow ?)
[00007f38a4c1e008] avcodec decoder error: more than 5 seconds of late video -> dropping frame (computer too slow ?)
[00007f38a4c1e008] avcodec decoder error: more than 5 seconds of late video -> dropping frame (computer too slow ?)
[00007f38a4c1e008] avcodec decoder error: more than 5 seconds of late video -> dropping frame (computer too slow ?)
[00007f38a4c1e008] avcodec decoder error: more than 5 seconds of late video -> dropping frame (computer too slow ?)
[00007f38a4c1e008] avcodec decoder error: more than 5 seconds of late video -> dropping frame (computer too slow ?)
[00007f38a4c1e008] avcodec decoder error: more than 5 seconds of late video -> dropping frame (computer too slow ?)
[00007f38a4c1e008] avcodec decoder error: more than 5 seconds of late video -> dropping frame (computer too slow ?)

 

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: