AI智能
改变未来

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


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             25static inline int startCode3(char* buf){if(buf[0] == 0 && buf[1] == 0 && buf[2] == 1)return 1;elsereturn 0;}static inline int startCode4(char* buf){if(buf[0] == 0 && buf[1] == 0 && buf[2] == 0 && buf[3] == 1)return 1;elsereturn 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; // startelse if (remainPktSize == 0 && i == pktNum - 1) //最后一包数据rtpPacket->payload[1] |= 0x40; // endmemcpy(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; //endmemcpy(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;elsestartCode = 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 codeInvalid UE golomb codeInvalid 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 ?)

赞(0) 打赏
未经允许不得转载:爱站程序员基地 » Ubuntu上实现rtp协议发送rtp包(进行H.264数据传输功能)——数据传输为UDP传输协议