网上有几份RTP发送H264代码,里面有些许错误而且可读性很差,对于初学者很难修正其中的问题,以下是我的源码,相信你研究一下RTP H264格式就能看懂,可复用性也高。
#include <stdio.h>
#include <ATLFile.h>
#include <Winsock2.h>
#include "Timer.h"
#include "getopt/getopt.h"
#include "RTPHead.h"
//
#pragma comment(lib, "WS2_32.lib")
//
enum
{
NAL_TYPE_UNKNOWN = 0,
NAL_TYPE_SLICE = 1,
NAL_TYPE_SLICE_DPA = 2,
NAL_TYPE_SLICE_DPB = 3,
NAL_TYPE_SLICE_DPC = 4,
NAL_TYPE_SLICE_IDR = 5,
NAL_TYPE_SEI = 6,
NAL_TYPE_SPS = 7,
NAL_TYPE_PPS = 8,
NAL_TYPE_AUD = 9,
NAL_TYPE_EOSEQ = 10,
NAL_TYPE_ESTREAM = 11,
NAL_TYPE_FILLER = 12,
NAL_TYPE_SPS_EXT = 13,
NAL_TYPE_PREFIX = 14,
NAL_TYPE_SUB_SPS = 15,
NAL_TYPE_SLC_EXT = 20,
NAL_TYPE_STAP_A = 24,
NAL_TYPE_STAP_B = 25,
NAL_TYPE_MTAP16 = 26,
NAL_TYPE_MTAP24 = 27,
NAL_TYPE_FU_A = 28,
NAL_TYPE_FU_B = 29,
NAL_TYPE_CUSTOM = 31, // Custom NAL
};
//
template <class TargetType, class SourceType>
TargetType ForceCast(SourceType source)
{
union TypeUnion {
TargetType targetType;
SourceType sourceType;
} _TypeUnion;
_TypeUnion.sourceType = source;
return _TypeUnion.targetType;
}
//
const uint8_t * FindNALHead(const uint8_t *lpStart, size_t nLength)
{
const uint8_t *lpBeg= lpStart + 0;
const uint8_t *lpEnd= lpStart + nLength;
uint32_t lValue = 0;
for (; lpBeg != lpEnd; ++lpBeg) {
lValue = (lValue << 8) | (*lpBeg);
if ((lValue & 0x00FFFFFF) == 0x000001) {
return &lpBeg[1];
}
}
return NULL;
}
const uint8_t * FindNALPrefix(const uint8_t *lpStart, size_t nLength)
{
const uint8_t *lpBeg= lpStart + 0;
const uint8_t *lpEnd= lpStart + nLength;
uint32_t lValue = 0xFFFFFFFF;
for (; lpBeg != lpEnd; ++lpBeg) {
lValue = (lValue << 8) | (*lpBeg);
if ((lValue & 0xFFFFFFFF) == 0x01) {
return &lpBeg[-3];
}
if ((lValue & 0x00FFFFFF) == 0x01) {
return &lpBeg[-2];
}
}
return NULL;
}
//
enum
{
PAYLOAD_H264 = 96,
RTP_HEADER_SIZE = 12,
RTP_STREAM_SIZE = 1400,
RTP_PACKET_SIZE = RTP_STREAM_SIZE + 2,
MAX_PACKET_SIZE = RTP_HEADER_SIZE + RTP_PACKET_SIZE,
};
//
template <typename RTPHEAD_T>
int SendRTPPacket(SOCKET hSocket, PSOCKADDR pTargetAddr, RTPHEAD_T &rtpHeader, const uint8_t *pStream, size_t nLength, uint32_t nTimestamp)
{
const uint8_t *pPayload = NULL;
if (pStream == NULL || nLength == 0) {
return -1;
}
pPayload = FindNALHead(pStream, nLength);
if (pPayload == NULL) {
return -1;
}
uint32_t nPayload = nLength - (pPayload - pStream);
uint8_t nNALHead = pPayload[0];
uint8_t nNALType = nNALHead & 0x1F;
DWORD dwBytesSent = 0;
WSABUF wsaBuffer[3] = { 0 };
rtpHeader.last_stamp= nTimestamp;
int nError = 0;
if (nPayload <= RTP_STREAM_SIZE) {
wsaBuffer[0].len= sizeof(RTP_HEADER);
wsaBuffer[0].buf= (PCHAR) &rtpHeader;
wsaBuffer[1].len= nPayload;
wsaBuffer[1].buf= (PCHAR) pPayload;
rtpHeader.NextPacket(1, nTimestamp);
nError = ::WSASendTo(hSocket, &wsaBuffer[0], 2, &dwBytesSent, 0, pTargetAddr, sizeof(*pTargetAddr), NULL, NULL);
return nError;
}
//
size_t nLeftSize = nPayload;
uint8_t byPacket[2] = { 0 };
byPacket[0] = (nNALHead & 0xE0) | 0x1C;
//
byPacket[1] = 0x80 | nNALType;
wsaBuffer[0].len= sizeof(RTP_HEADER);
wsaBuffer[0].buf= (PCHAR) &rtpHeader;
wsaBuffer[1].len= sizeof(byPacket);
wsaBuffer[1].buf= (PCHAR) &byPacket[0];
wsaBuffer[2].len= RTP_STREAM_SIZE;
wsaBuffer[2].buf= (PCHAR) &pPayload[1];
rtpHeader.NextPacket(0, nTimestamp);
nError = ::WSASendTo(hSocket, &wsaBuffer[0], 3, &dwBytesSent, 0, pTargetAddr, sizeof(*pTargetAddr), NULL, NULL);
nLeftSize -= (RTP_STREAM_SIZE + 1);
pPayload += (RTP_STREAM_SIZE + 1);
//
while (nLeftSize > RTP_STREAM_SIZE) {
byPacket[1] = 0x00 | nNALType;
wsaBuffer[0].len= sizeof(RTP_HEADER);
wsaBuffer[0].buf= (PCHAR) &rtpHeader;
wsaBuffer[1].len= sizeof(byPacket);
wsaBuffer[1].buf= (PCHAR) &byPacket[0];
wsaBuffer[2].len= RTP_STREAM_SIZE;
wsaBuffer[2].buf= (PCHAR) &pPayload[0];
rtpHeader.NextPacket(0, 0);
nError = ::WSASendTo(hSocket, &wsaBuffer[0], 3, &dwBytesSent, 0, pTargetAddr, sizeof(*pTargetAddr), NULL, NULL);
nLeftSize -= (RTP_STREAM_SIZE + 0);
pPayload += (RTP_STREAM_SIZE + 0);
}
//
byPacket[1] = 0x40 | nNALType;
wsaBuffer[0].len= sizeof(RTP_HEADER);
wsaBuffer[0].buf= (PCHAR) &rtpHeader;
wsaBuffer[1].len= sizeof(byPacket);
wsaBuffer[1].buf= (PCHAR) &byPacket[0];
wsaBuffer[2].len= nLeftSize;
wsaBuffer[2].buf= (PCHAR) &pPayload[0];
rtpHeader.NextPacket(1, 0);
nError = ::WSASendTo(hSocket, &wsaBuffer[0], 3, &dwBytesSent, 0, pTargetAddr, sizeof(*pTargetAddr), NULL, NULL);
nLeftSize -= (nLeftSize + 0);
pPayload += (nLeftSize + 0);
return 0;
}
//
template <uint32_t PAYLOAD_TYPE>
struct BUILD_RTP_HEADER : public RTP_HEADER
{
BUILD_RTP_HEADER()
{
this->csrc = 0;
this->extension = 0;
this->padding = 0;
this->version = 2;
this->payload = PAYLOAD_TYPE;
this->marker = 0;
this->sequence = 0;
this->timestamp = 0;
this->ssrc = htonl(10);
//
this->last_seq = 0;
this->last_stamp= 0;
}
void NextPacket(uint8_t marker, uint32_t timestamp)
{
this->marker = marker;
this->sequence = htons(++(this->last_seq));
this->timestamp = htonl(this->last_stamp);
}
//
uint16_t last_seq;
uint32_t last_stamp;
};
//
struct RTPContext
{
const uint8_t *pBegin;
const uint8_t *pFinish;
int32_t frame_number;
int32_t frame_interval;
int32_t stamp_increment;
int32_t stamp_lasttime;
SOCKET client_socket;
SOCKADDR_IN remote_addr;
BUILD_RTP_HEADER<PAYLOAD_H264> rtp_header;
};
void CALLBACK TimeCallback(UINT uTimerID, UINT uMsg, DWORD_PTR dwUser, DWORD_PTR dw1, DWORD_PTR dw2)
{
CTimer::Delete(uTimerID);
//
RTPContext *pRTPContext = ForceCast<RTPContext *>(dwUser);
const uint8_t *pBegin = pRTPContext->pBegin;
const uint8_t *pFinish = pRTPContext->pFinish;
int32_t frame_interval = pRTPContext->frame_interval;
int32_t stamp_increment = pRTPContext->stamp_increment;
const uint8_t *pNext = FindNALPrefix(pBegin + 4, pFinish - (pBegin + 4));
if (pNext == NULL) {
pNext = pFinish;
}
++(pRTPContext->frame_number);
SendRTPPacket(pRTPContext->client_socket, (PSOCKADDR) &(pRTPContext->remote_addr), pRTPContext->rtp_header, pBegin, pNext - pBegin, (pRTPContext->frame_number * stamp_increment));
pRTPContext->pBegin = pNext;
if (pRTPContext->pBegin < pRTPContext->pFinish) {
int32_t tick_now = GetTickCount();
int32_t time_increment = tick_now - pRTPContext->stamp_lasttime;
time_increment = (time_increment < frame_interval) ? (frame_interval - time_increment) : 1;
pRTPContext->stamp_lasttime = tick_now;
printf("time_increment = %-6d\n", time_increment);
CTimer::Create(TimeCallback, dwUser, time_increment, TIME_ONESHOT);
}
}
//
int main(int argc, char *argv[])
{
char input_file[MAX_PATH]= { 0 };
char client_addr[128]= { 0 };
int32_t client_port = 5004;
int32_t framerate = 25;
if (argc == 1) {
printf("usage: %s -iinputfile.264 -f25 -c127.0.0.1 -p5004\n", PathFindFileNameA(argv[0]));
return 0;
}
strcpy(client_addr, "127.0.0.1");
while (1) {
int option_index = 0;
static struct option long_options[] = {
{ "input", 1, NULL, 'i', },
{ "client", 2, NULL, 'c', },
{ "port", 2, NULL, 'p', },
{ "framerate", 2, NULL, 'f', },
{ 0, 0, NULL, 0, },
};
int32_t c = getopt_long(argc, argv, "i:c:p:f:", long_options, &option_index);
if (c == -1) {
break;
}
switch (c) {
case 'i':
strcpy(input_file, optarg);
break;
case 'c':
if (optarg != NULL) {
strcpy(client_addr, optarg);
}
break;
case 'p':
if (optarg != NULL) {
client_port = atoi(optarg);
}
break;
case 'f':
if (optarg != NULL) {
framerate = atoi(optarg);
framerate = (framerate <= 0) ? 25 : framerate;
}
break;
default:
break;
}
}
if (input_file[0] == '\0') {
printf("usage: %s -iinputfile.264 -f25 -c127.0.0.1 -p5004\n", PathFindFileNameA(argv[0]));
return 0;
}
CAtlFile atlfile;
if (atlfile.Create(input_file, GENERIC_READ, 0, OPEN_EXISTING) != S_OK) {
return 0;
}
WSADATA wsaData;
WSAStartup(MAKEWORD(2,2), &wsaData);
{
RTPContext *pRTPContext = new RTPContext;
pRTPContext->client_socket = WSASocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP, NULL, 0, 0);
pRTPContext->remote_addr.sin_family = AF_INET;
pRTPContext->remote_addr.sin_port = htons(client_port);
pRTPContext->remote_addr.sin_addr.s_addr = inet_addr(client_addr);
//
ULONGLONG lFileSize = 0;
atlfile.GetSize(lFileSize);
LPBYTE lpH264 = new BYTE[lFileSize];
atlfile.Read(lpH264, lFileSize);
atlfile.Close();
pRTPContext->pBegin = lpH264 + 0;
pRTPContext->pFinish= lpH264 + lFileSize;
pRTPContext->stamp_increment= 90000 / framerate;
pRTPContext->frame_interval = 1000 / framerate;
pRTPContext->frame_number= 0;
pRTPContext->stamp_lasttime= GetTickCount();
CTimer::Create(TimeCallback, ForceCast<DWORD>(pRTPContext), 1, TIME_ONESHOT);
while (pRTPContext->pBegin < pRTPContext->pFinish) {
Sleep(1000);
}
delete []lpH264;
closesocket(pRTPContext->client_socket);
Sleep(1000*1);
delete pRTPContext;
}
WSACleanup();
return 0;
}