RTP发送H264

53 篇文章 0 订阅
51 篇文章 0 订阅

网上有几份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;
}



  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值