异步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的主要内容,如果未能解决你的问题,请参考以下文章