被测试的USB摄像头
效果
源码说明
主要功能模拟设备端,完成注册、注销、心跳等,同时当服务端下发指令播放视频时 设备端实时读取USB摄像头视频并通过OpenCV处理后实时转ps格式后封包rtp进行推送给服务端播放。
源码
/***
*@remark: pes头的封装,里面的具体数据的填写已经占位,可以参考标准
*@param : pData [in] 填充ps头数据的地址
* stream_id [in] 码流类型
* paylaod_len[in] 负载长度
* pts [in] 时间戳
* dts [in]
*@return: 0 success, others failed
*/
int gb28181_make_pes_header(char *pData, int stream_id, int payload_len, int64_t pts, int64_t dts)
{
bits_buffer_t bitsBuffer;
bitsBuffer.i_size = PES_HDR_LEN;
bitsBuffer.i_data = 0;
bitsBuffer.i_mask = 0x80;
bitsBuffer.p_data = (unsigned char *)(pData);
memset(bitsBuffer.p_data, 0, PES_HDR_LEN);
bits_write(&bitsBuffer, 24, 0x000001); //*start code*//*
bits_write(&bitsBuffer, 8, (stream_id)); //*streamID*//*
bits_write(&bitsBuffer, 16, (payload_len)+13); //*packet_len*//* //指出pes分组中数据长度和该字节后的长度和
bits_write(&bitsBuffer, 2, 2); //*'10'*//*
bits_write(&bitsBuffer, 2, 0); //*scrambling_control*//*
bits_write(&bitsBuffer, 1, 0); //*priority*//*
bits_write(&bitsBuffer, 1, 0); //*data_alignment_indicator*//*
bits_write(&bitsBuffer, 1, 0); //*copyright*//*
bits_write(&bitsBuffer, 1, 0); //*original_or_copy*//*
bits_write(&bitsBuffer, 1, 1); //*PTS_flag*//*
bits_write(&bitsBuffer, 1, 1); //*DTS_flag*//*
bits_write(&bitsBuffer, 1, 0); //*ESCR_flag*//*
bits_write(&bitsBuffer, 1, 0); //*ES_rate_flag*//*
bits_write(&bitsBuffer, 1, 0); //*DSM_trick_mode_flag*//*
bits_write(&bitsBuffer, 1, 0); //*additional_copy_info_flag*//*
bits_write(&bitsBuffer, 1, 0); //*PES_CRC_flag*//*
bits_write(&bitsBuffer, 1, 0); //*PES_extension_flag*//*
bits_write(&bitsBuffer, 8, 10); //*header_data_length*//*
// 指出包含在 PES 分组标题中的可选字段和任何填充字节所占用的总字节数。该字段之前
//的字节指出了有无可选字段。
//*PTS,DTS PTS DTS均为1的情况*//
bits_write(&bitsBuffer, 4, 3); //*'0011'*//*
bits_write(&bitsBuffer, 3, ((pts) >> 30) & 0x07); //*PTS[32..30]*//*
bits_write(&bitsBuffer, 1, 1);
bits_write(&bitsBuffer, 15, ((pts) >> 15) & 0x7FFF); //*PTS[29..15]*//*
bits_write(&bitsBuffer, 1, 1);
bits_write(&bitsBuffer, 15, (pts) & 0x7FFF); //*PTS[14..0]*//*
bits_write(&bitsBuffer, 1, 1);
bits_write(&bitsBuffer, 4, 1); //*'0001'*//*
bits_write(&bitsBuffer, 3, ((dts) >> 30) & 0x07); //*DTS[32..30]*//*
bits_write(&bitsBuffer, 1, 1);
bits_write(&bitsBuffer, 15, ((dts) >> 15) & 0x7FFF); //*DTS[29..15]*//*
bits_write(&bitsBuffer, 1, 1);
bits_write(&bitsBuffer, 15, (dts) & 0x7FFF); //*DTS[14..0]*//*
bits_write(&bitsBuffer, 1, 1);
return 0;
}
/**
* RTP头封装
* @param pData buffer地址
* @param seqNum 序号
* @param timestamp 时间戳
* @param ssrc 标识
* @return
*/
int gb28181_make_rtp_header(char *pData, int seqNum, int64_t timestamp, int ssrc, int isEnd)
{
bits_buffer_t bitsBuffer;
bitsBuffer.i_size = RTP_HDR_LEN;
bitsBuffer.i_data = 0;
bitsBuffer.i_mask = 0x80;
bitsBuffer.p_data = (unsigned char *)(pData);
memset(bitsBuffer.p_data, 0, RTP_HDR_LEN);
bits_write(&bitsBuffer, 2, 2); /*协议版本*/
bits_write(&bitsBuffer, 1, 0); /*P*/
bits_write(&bitsBuffer, 1, 0); /*X*/
bits_write(&bitsBuffer, 4, 0); /*CSRC个数*/
bits_write(&bitsBuffer, 1, isEnd); /*一帧是否结束*/
bits_write(&bitsBuffer, 7, 96); /*载荷的数据类型*/
bits_write(&bitsBuffer, 16, seqNum); /*序列号,第几个*/
bits_write(&bitsBuffer, 32, timestamp); /*时间戳,第一个 */
bits_write(&bitsBuffer, 32, ssrc); /*同步信源(SSRC)标识符*/
return 0;
}
/***
*@remark: sys头的封装,里面的具体数据的填写已经占位,可以参考标准
*@param : pData [in] 填充ps头数据的地址
*@return: 0 success, others failed
*/
int32_t __gb28181_make_sys_header(char *pData, int32_t audioCnt, int32_t videoCnt, int32_t audioStreamID, int32_t videoStreamID)
{
bits_buffer_t bitsBuffer;
bitsBuffer.i_size = SYS_HDR_LEN; // 18
bitsBuffer.i_data = 0;
bitsBuffer.i_mask = 0x80;
bitsBuffer.p_data = (unsigned char *)(pData);
memset(bitsBuffer.p_data, 0, SYS_HDR_LEN);
/*system header*/
bits_write(&bitsBuffer, 32, 0x000001BB); /*start code*/
bits_write(&bitsBuffer, 16, SYS_HDR_LEN - SYS_BASE_HEAD_LEN); /*header_length 表示此字段后面的长度*/
bits_write(&bitsBuffer, 1, 1); /*marker_bit*/
// bits_write(&bitsBuffer, 22, 3967); /*rate_bound*/
bits_write(&bitsBuffer, 22, 26234); // 抓包读取
bits_write(&bitsBuffer, 1, 1); /*marker_bit*/
bits_write(&bitsBuffer, 6, audioCnt); /*audio_bound >= audio_stream_cnt*/
bits_write(&bitsBuffer, 1, 0); /*fixed_flag,1为固定比特率,0为可变比特率 */
// bits_write(&bitsBuffer, 1, 1); /* CSPS_flag=1表示满足ps标准 */
bits_write(&bitsBuffer, 1, 0); // 抓包CSPS_flag=0
bits_write(&bitsBuffer, 1, 1); /*system_audio_lock_flag*/
bits_write(&bitsBuffer, 1, 1); /*system_video_lock_flag*/
bits_write(&bitsBuffer, 1, 1); /*marker_bit*/
bits_write(&bitsBuffer, 5, videoCnt); /*video_bound >= video_stream_cnt*/
bits_write(&bitsBuffer, 1, 0); /*dif from mpeg1,if CSPS_flag=0,inavailable*/
bits_write(&bitsBuffer, 7, 0x7F); /*reserver*/
/*video stream bound*/
bits_write(&bitsBuffer, 8, videoStreamID); /*stream_id*/
bits_write(&bitsBuffer, 2, 3); /*marker_bit */
bits_write(&bitsBuffer, 1, 1); /*PSTD_buffer_bound_scale*/
// bits_write(&bitsBuffer, 13, 2048); /*PSTD_buffer_size_bound*/
bits_write(&bitsBuffer, 13, 232); // 抓包为232
/*audio stream bound*/
bits_write(&bitsBuffer, 8, audioStreamID); /*stream_id*/
bits_write(&bitsBuffer, 2, 3); /*marker_bit */
bits_write(&bitsBuffer, 1, 0); /*PSTD_buffer_bound_scale*/
// bits_write(&bitsBuffer, 13, 512); /*PSTD_buffer_size_bound*/
bits_write(&bitsBuffer, 13, 40); // 抓包为40
return 0;
}
// 推流线程
void Push_rtp_Stream()
{
std::cout << "\n................ 进入rtp线程 ................" << std::endl;
//std::this_thread::sleep_for(std::chrono::seconds(1));
// 套接字初始化
if (!BindSocket()) { std::cout << "\n!!!绑定套接字失败,无法发送数据!!!" << std::endl; return; }
char ps_header[PS_HDR_LEN] = { 0 };
char ps_system_header[SYS_HDR_LEN] = { 0 };
char ps_map_header[PSM_HDR_LEN] = { 0 };
char pes_header[PES_HDR_LEN] = { 0 };
char rtp_header[RTP_HDR_LEN] = { 0 };
int time_base = 90000;
int fps = 20;
int send_packet_interval = 1000 / fps;
int interval = time_base / fps;
long pts = 0;
//char frame[1024 * 128];
char *frame = new char[1024 * 1024]; memset(frame, 0, 1024 * 1024);
// int single_packet_max_length = 1400;
// char rtp_packet[RTP_HDR_LEN + 1400];
int single_packet_max_length = 10000;
char *rtp_packet = new char[RTP_HDR_LEN + 10000]; memset(frame, 0, RTP_HDR_LEN + 10000);
// int ssrc = 0xffffffff;
int rtp_seq = 0;
//while (PushThreadFlag)
{
for (auto i = 0; i < nalu_vector.size(); i++)
{
auto nalu = nalu_vector.at(i);
NaluType type = nalu->type;
int length = nalu->length;
char * packet = nalu->packet;
int index = 0;
if (NALU_TYPE_IDR == type)
{
gb28181_make_ps_header(ps_header, pts);
memcpy(frame, ps_header, PS_HDR_LEN);
index += PS_HDR_LEN;
gb28181_make_sys_header(ps_system_header, 0x3f);
memcpy(frame + index, ps_system_header, SYS_HDR_LEN);
index += SYS_HDR_LEN;
gb28181_make_psm_header(ps_map_header);
memcpy(frame + index, ps_map_header, PSM_HDR_LEN);
index += PSM_HDR_LEN;
}
else
{
gb28181_make_ps_header(ps_header, pts);
memcpy(frame, ps_header, PS_HDR_LEN);
index += PS_HDR_LEN;
}
//封装pes
gb28181_make_pes_header(pes_header, 0xe0, length, pts, pts);
memcpy(frame + index, pes_header, PES_HDR_LEN);
index += PES_HDR_LEN;
memcpy(frame + index, packet, length);
index += length;
//组包rtp
int rtp_packet_count = ((index - 1) / single_packet_max_length) + 1;
for (int i = 0; i < rtp_packet_count; i++)
{
gb28181_make_rtp_header(rtp_header, rtp_seq, pts, atoi(ssrc.c_str()), i == (rtp_packet_count - 1));
int writed_count = single_packet_max_length;
if ((i + 1)*single_packet_max_length > index)
{
writed_count = index - (i* single_packet_max_length);
}
//添加包长字节
int rtp_start_index = 0;
unsigned short rtp_packet_length = RTP_HDR_LEN + writed_count;
if (rtp_protocol == "TCP/RTP/AVP")
{
unsigned char packt_length_ary[2];
packt_length_ary[0] = (rtp_packet_length >> 8) & 0xff;
packt_length_ary[1] = rtp_packet_length & 0xff;
memcpy(rtp_packet, packt_length_ary, 2);
rtp_start_index = 2;
}
memcpy(rtp_packet + rtp_start_index, rtp_header, RTP_HDR_LEN);
memcpy(rtp_packet + +rtp_start_index + RTP_HDR_LEN, frame + (i* single_packet_max_length), writed_count);
rtp_seq++;
if (PushThreadFlag)
{
send_network_packet(rtp_packet, rtp_start_index + rtp_packet_length);
}
else
{
if (nalu != nullptr)
{
delete nalu;
nalu = nullptr;
}
return;
}
}
pts += interval;
std::this_thread::sleep_for(std::chrono::milliseconds(send_packet_interval));
}
}
std::cout << "Push_rtp_Stream - 视频文件发送结束..." << std::endl;
}
// 注册:参数true注册、false注销
bool RegisterToServer(const bool rFlag = true)
{
// 分配eXosip上下文
sip_context = eXosip_malloc();
if (!sip_context) { std::cout << "eXosip_malloc fail!" << std::endl; return false; }
std::cout << "完成 - 分配eXosip上下文" << std::endl;
// 初始化eXosip上下文
eXosip_lock(sip_context);
if(eXosip_init(sip_context) != OSIP_SUCCESS) { std::cout << "eXosip_init fail!" << std::endl; eXosip_unlock(sip_context); return false; }
eXosip_unlock(sip_context);
std::cout << "完成 - 初始化eXosip上下文" << std::endl;
/*
eXosip_guess_localip
描述:监听套接字
参数:
参数2: 17代表用户数据报协议-udp、6代表tcp
参数5: 2代表IP协议系列
*/
eXosip_lock(sip_context);
if (eXosip_listen_addr(sip_context, 6, nullptr, GbParameter.device.Port, 2, 0) != OSIP_SUCCESS) { std::cout << "eXosip_listen_addr fail!" << std::endl; eXosip_unlock(sip_context); return false; }
eXosip_unlock(sip_context);
std::cout << "完成 - 套接字监听" << std::endl;
// 清除eXosip中存储的所有身份验证凭据
eXosip_lock(sip_context);
if (eXosip_clear_authentication_info(sip_context) != OSIP_SUCCESS) { std::cout << "eXosip_clear_authentication_info fail!" << std::endl; eXosip_unlock(sip_context); return false; }
eXosip_unlock(sip_context);
std::cout << "完成 - 清除eXosip中存储的所有身份验证凭据" << std::endl;
/*
eXosip_guess_localip
描述:查找当前网络(具有默认路由的接口)
参数:
参数2: 2代表IP协议系列
*/
eXosip_lock(sip_context);
if(eXosip_guess_localip(sip_context, 2, local_ip, 64) != OSIP_SUCCESS) { std::cout << "eXosip_guess_localip fail!" << std::endl; eXosip_unlock(sip_context); return false; }
eXosip_unlock(sip_context);
std::cout << "完成 - 查找当前网络:" << local_ip << std::endl;
// 组合sip字符串
from_sip = "sip:" + GbParameter.device.Id + "@" + local_ip + ":" + std::to_string(GbParameter.device.Port);
std::string contact = "sip:" + GbParameter.device.Id + "@" + local_ip + ":" + std::to_string(GbParameter.device.Port);
proxy_sip = "sip:" + GbParameter.server.Id + "@" + GbParameter.server.Ip + ":" + std::to_string(GbParameter.server.Port);
// 生成初始REGISTER请求
osip_message_t *register_message = nullptr;
eXosip_lock(sip_context);
int register_id = eXosip_register_build_initial_register
(
sip_context,
from_sip.c_str(),
proxy_sip.c_str(),
contact.c_str(),
rFlag?3600:0, // 0为注销[当为0时,设备是注销;当大于0时(Expires最小值为3600),设备是注册]
®ister_message
);
eXosip_unlock(sip_context);
if (!register_message) { std::cout << "eXosip_register_build_initial_register fail!" << std::endl; return false; }
std::cout << "完成 - 生成初始REGISTER请求:\n\t"
<< "from_sip:" << from_sip << "\n\t"
<< "proxy_sip:" << proxy_sip << "\n\t"
<< "contact:" << contact << "\n\t"
<< "register_id:" << register_id << "\n\t" << std::endl;
// 发送对现有注册的REGISTER请求
eXosip_lock(sip_context);
if (eXosip_register_send_register(sip_context, register_id, register_message) != OSIP_SUCCESS) { std::cout << "eXosip_register_send_register fail!" << std::endl; eXosip_unlock(sip_context); return false; }
eXosip_unlock(sip_context);
std::cout << (rFlag ? "完成 - 发送注册REGISTER请求" : "完成 - 发送注销REGISTER请求......................") << std::endl;
// 开启线程定时发送心跳
static bool HeartbeatWorkFlag = true;
if (HeartbeatWorkFlag)
{
HeartbeatWorkFlag = false;
std::thread heartbeat_task_thread(HeartbeatWork);
heartbeat_task_thread.detach();
}
return true;
}
下载完整源码
关注
笔者 - jxd