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 ?)