异步udpserver接收rtp转html5

Posted qianbo_insist

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了异步udpserver接收rtp转html5相关的知识,希望对你有一定的参考价值。

udpserver

假定服务端有一个端口接收rtp over udp,接收到数据以后转成html5能展示的,这种效果有作用否?

我们知道ffmpeg是可以直接发送rtp流的,gb28181 也是可以发送rtp流的,那么我们制作一个简洁的服务器,在udp端口接收到以后转成tcp,让后在网页上展示。

ffmpeg发送命令ffmpeg \\

ffmpeg -re -i video.mp4 -an -c:v copy -f rtp -sdp_file video.sdp “rtp://192.168.1.109:5004”
使用ffmpeg来发送文件,去除音频,当然也可以接收自己写的程序发送rtp,下面,我们使用asio来制作一个异步的服务器,来接收udp流。

code

#pragma once

#include <cstdlib>
#include <iostream>
#include <memory>
#include <unordered_map>
#include <boost/asio.hpp>
#include <boost/asio/spawn.hpp>
#include "c_rtp.h"
using boost::asio::ip::udp;

using namespace boost;

#include "c_hub.h"
#include "c_rtp.h"

#define DEFINE_EC \\
boost::system::error_code ec;

class c_flvserver;

class c_udpserver:public std::enable_shared_from_this<c_udpserver>

	//asio::strand<asio::io_context::executor_type> v_strand;
	asio::io_context &v_context;

	asio::ip::udp::socket v_socket;
	c_flvserver *v_flvserver = NULL;
	unordered_map<uint32_t, s_rtp_h264_context*> v_ctxs;
	//s_rtp_h264_context v_rtp;
	s_rtp_h264_context * getctx(uint32_t ssrc)
	
		auto it = v_ctxs.find(ssrc);
		if (it != v_ctxs.end())
			return it->second;
		else
		
			s_rtp_h264_context *sc = new s_rtp_h264_context();
			v_ctxs[ssrc] = sc;
			return sc;
		
	
	void data_call(s_rtp_h264_context *ctx);
	s_flvhub * func_context(uint32_t ssrc);

	int live_rtp_unpack_h264(s_rtp_h264_context *ctx, uint8_t *data, int inlen);
	//int live_rtp_unpack_h265(s_rtp_h264_context *ctx, uint8_t *data, int inlen);
	//int live_rtp_unpack_aac(s_rtp_h264_context *ctx, uint8_t *data, int inlen);
	//int live_rtp_unpack_opus(s_rtp_h264_context *ctx, uint8_t *data, int inlen);
	//int live_rtp_unpack(s_rtp_h264_context *ctx, uint8_t *data, int inlen);

public:
	c_udpserver(asio::io_context& io_context, short port, c_flvserver *flvserver):
		//v_strand(io_context.get_executor()),
		v_context(io_context), v_flvserver(flvserver),
		v_socket(v_context)
		 //v_socket(v_context, udp::endpoint(udp::v4(), port))
	
		//boost::asio::ip::udp::endpoint ep(boost::asio::ip::address::from_string("192.168.1.206"),port);
		udp::endpoint ep(udp::v4(), port);
		v_socket.open(ep.protocol());
		v_socket.set_option(boost::asio::ip::udp::socket::reuse_address(true));
		boost::asio::socket_base::receive_buffer_size recv_option(1*1024*1024);
		v_socket.set_option(recv_option);
		v_socket.bind(ep);
	
	~c_udpserver()
	
		//v_context_maps.clear();
	

	
	//to flv server
	void do_receive();

#ifdef _WEBRTC_
	void do_receive_webrtc()
	
		//std::cout << "start to receive" << std::endl;
		//auto self(shared_from_this());
		udp::endpoint from;
		v_socket.async_receive_from(asio::buffer(data_, max_length),
			from, [this](boost::system::error_code ec, std::size_t len)
		
			uint8_t *d = (uint8_t*)&(data_[0]);
			if (len < 12)   //如果RTP包的长度小于12,说明包传输有错误
				return;
			if ((*d >> 6) != 2) /* RTP version number(rtp版本号必须为2) */
				return;
			if (*d & 0x20)
			
				std::cout << "padding" << std::endl;
			
			RTP_FIXED_HEADER *rtp_hdr = (RTP_FIXED_HEADER*)d;
			uint32_t ssrc = *((uint32_t*)(d + 8));
			int type = (*(d+1)) & 0x7f;
			ssrc = ntohl(ssrc);
			//std::cout << "seq:" << ntohs(rtp_hdr->seq_no) << 
			//	"--payload-->"<<type<<std::endl;
			std::cout<<"len is:"<<len<<"-->" << "the ssrc:""--" << ssrc << std::endl;

			s_hub * hub = func_context(ssrc);
			auto iter = hub->v_list.begin();
			while (iter != hub->v_list.end())
			
				std::cout << "send data" << std::endl;
				bool ret = (*iter)->send_data_need_protect(
					1, 1, len, data_);

				if (!ret)

				
					hub->v_list.erase(iter++);
				
				else
					iter++;
			
			do_receive_webrtc();
		);
#endif
#if 0
		boost::asio::spawn(v_context,
			[this](asio::yield_context yield)
		
			std::cout << "ddd" << std::endl;
			udp::endpoint se;
			DEFINE_EC

			for (;;)
			
				if (v_socket.is_open())
				
					size_t len = v_socket.async_receive_from(boost::asio::buffer(data_, max_length),
						se, yield[ec]);
					RTP_FIXED_HEADER *rtp_hdr = (RTP_FIXED_HEADER*)&data_[0];
					uint32_t ssrc = rtp_hdr->ssrc;
					std::cout << "the ssrc:" << ssrc << std::endl;
					s_hub * hub = func_context(ssrc);
					auto iter = hub->v_list.begin();
					while (iter != hub->v_list.end())
					
						bool ret = (*iter)->send_data_need_protect(
							1, 1, len, data_);

						if (!ret)
						
							hub->v_list.erase(iter++);
						
						else
							iter++;
					
				
				else
					v_context.post(yield);
			
		);
#endif

	

	void do_send(std::size_t length);
	
	void do_judge_spspps(s_rtp_h264_context *ctx);
private:
	udp::endpoint sender_endpoint_;
	enum  max_length = 1500;
	char data_[max_length];
;

//int main(int argc, char* argv[])
//
//	try
//	
//		if (argc != 2)
//		
//			std::cerr << "Usage: async_udp_echo_server <port>\\n";
//			return 1;
//		
//
//		boost::asio::io_context io_context;
//
//		server s(io_context, 6000);
//
//		io_context.run();
//	
//	catch (std::exception& e)
//	
//		std::cerr << "Exception: " << e.what() << "\\n";
//	
//
//	return 0;
//

udp 接收细节

void c_udpserver::do_receive()

	udp::endpoint from;
	v_socket.async_receive_from(asio::buffer(data_, max_length),
		from, [this](boost::system::error_code ec, std::size_t len)
	
		uint8_t *data = (uint8_t*)&(data_[0]);
		if (len < 12)   //if rtp payload length <12,the error occur
			return;
		if ((*data >> 6) != 2) /* RTP version number is 2*/
			return;
		if (*data & 0x20)
		
			std::cout << "padding" << std::endl;
		
		rtp_header *rtp = (rtp_header *)data;
		//big(net) to little(home)
		uint32_t ssrc = b2l(rtp->ssrc);
		
		uint8_t payload = rtp->type;
		s_rtp_h264_context *ctx = getctx(ssrc);
		ctx->v_payload = payload;
		switch (ctx->v_payload)
		
		case 96:
			live_rtp_unpack_h264(ctx, data, (int)len);
			break;
		case 97:
			//live_rtp_unpack_aac(ctx, data, len);
			break;
		

		//0 :no frame 1:one frame
		/*if (live_rtp_unpack(ctx, data, (int)len) == 1)
		
			data_call(ctx);
		*/
		do_receive();
	);

解包函数

int c_udpserver::live_rtp_unpack_h264(s_rtp_h264_context *ctx, uint8_t *data, int inlen)

	int jump = 0;
	int len = 0;
	uint8_t *buffer = rtp_payload(data, inlen, &len,ctx->v_last_ts , ctx->v_ssrc,
		ctx->v_seq, ctx->v_payload);
	if (len < 1) 
		printf("nalu is null\\n");
		return -1;
	
	uint8_t m = *(data + 1) & 0x80; //m!=1 then the data is end
	/* H.264 depay */
	uint8_t fragment = *buffer & 0x1F;

	/* Frame manipulation */
	if ((fragment > 0) && (fragment < 24)) 
		//Add a start code here
		//maybe single < MTU data, maybe sps or pps in here
		switch (fragment)// == 0x07) //sps
		
		case 0x07:
			ctx->set_sps(buffer, len);
			break;
		case 0x08:
			ctx->set_pps(buffer, len);
			do_judge_spspps(ctx);
			break;
		default:
		
			uint8_t *temp = ctx->v_buffer + ctx->v_len;
			*temp = 0;
			*(temp + 1) = 0;
			*(temp + 2) = 0;
			*(temp + 3) = 1;
			ctx->v_len += 4;
			memcpy(ctx->v_buffer + ctx->v_len, buffer, len);
			ctx->v_len += len;
		
		break;
		

		//std::cout << " AAAAA ";
	
	else if (fragment == 24) 	/*18  STAP-A */
	 /* De-aggregate the NALs and write each of them separately */
	 /*STAP-A single time  ,maybe sps pps in there ,like ffmpeg */
	 /**/
		buffer++;
		int tot = len - 1;
		uint16_t psize = 0;
		int is_add = 0;
		while (tot > 0) 
			memcpy(&psize, buffer, 2);
			psize = b2l(psize);
			buffer += 2;
			tot -= 2;
			/* Now we have a single NAL */
			uint8_t nal = *(buffer) & 0x1F;
			switch (nal)
			
			case 0x07:
				ctx->set_sps(buffer, psize);
				break;
			case 0x08:
				ctx->set_pps(buffer, psize);
				do_judge_spspps(ctx);
				break;
			default:
				is_add = 1;
				break;
			
			if (is_add)
			
				//data_call_1(ctx, buffer, psize);
#if 1 //this is copy to buffer
				uint8_t *temp = ctx->v_buffer + ctx->v_len;
				*temp = 0x00;
				*(temp + 1) = 0x00;
				*(temp + 2) = 0x00;
				*(temp + 3) = 0x01;
				ctx->v_len += 4;
				memcpy(ctx->v_buffer + ctx->v_len, buffer, psize);
				ctx->v_len += psize;
#endif
			
			/* Go on */
			buffer += psize;
			tot -= psize;
		
		//ctx->reset();
		//std::cout << "STAP-A";
	
	else if ((fragment == 28) || (fragment == 29)) 
		/* fix me just for FU-A, not for FU-B */
		uint8_t indicator = *buffer;
		uint8_t header = *(buffer + 1);
		jump = 2;
		len -= 2;
		
		if ((header & 0x80) && !(header &0x40)) 
			/* First part of fragmented packet (S bit set) */
			ctx->v_current_FU = 1;
			uint8_t *temp = ctx->v_buffer + ctx->v_len;
			*temp = 0x00;
			*(temp + 1) = 0x00;
			*(temp + 2) = 0x00;
			*(temp + 3) = 0x01;
			*(temp + 4) = (indicator & 0xE0) | (header & 0x1F);
			ctx->v_len += 5;
			memcpy(ctx->v_buffer + ctx->v_len, buffer + jump, len);
			ctx->v_len += len;
		
		else if (!(header & 0x80) && !(header & 0x40))
		
			if (ctx->v_current_FU)
			
				ctx->v_current_FU++;
				memcpy(ctx->v_buffer + ctx->v_len, buffer + jump, len);
				ctx->v_len += len;
			
			else//分片丢失,RTP丢包
			
				ctx->reset();
				//ctx->v_current_FU = 0; 
				return -1;
			
		
		else if (!(header & 0x80) && (header & 0x40)) 
			/* Last part of fragmented packet (E bit set) */
			if (ctx->v_current_FU) 
				ctx->v_current_FU = 0;
				memcpy(ctx->v_buffer + ctx->v_len, buffer + jump, len);
				ctx->v_len += len;
				//data_call(ctx);
				//ctx->reset();
				//return 0;
				//printf("end of m:%d", m);
				//cout << m <<" come here " <<"end "<< endl;
			
			else
			
				ctx->reset();
				return -1;
			
		
		else if ((header & 0x80) && (header & 0x40))
		//fragment error 分片错误,RTP丢包
			cout << "error rtp" << endl;
			ctx->reset();
			return -1;
		
	
	
	if (m > 0)
	
		data_call(ctx);
		//cout << "the ts is " << ctx->v_last_ts << endl;
		ctx->reset();
	
	else
	
		//cout << "come here" << endl;
	
	return 以上是关于异步udpserver接收rtp转html5的主要内容,如果未能解决你的问题,请参考以下文章

RTP 接发ps流工具改进

转:关于H264通过RTP传输的打包方式

gb28181 ps流文件解析

在 HTML5 中通过 RTSP 或 RTP 流式传输

在 HTML5 中通过 RTSP 或 RTP 流式传输

gstreamer分析-rtp流的接收