一个好的转发模块,首先要低延迟!其次足够稳定灵活、有状态反馈机制、资源占用低,跨平台,最好以接口形式提供,便于第三方系统集成。

以Windows平台为例,我们的考虑的点如下

1. 拉流:通过RTSP直播播放SDK的数据回调接口,拿到音视频数据;

2. 转推:通过RTMP直播推送SDK的编码后数据输入接口,把回调上来的数据,传给RTMP直播推送模块,实现RTSP数据流到RTMP服务器的转发;

3. 录像:如果需要录像,借助RTSP直播播放SDK,拉到音视频数据后,直接存储MP4文件即可;

4. 快照:如果需要实时快照,拉流后,解码调用播放端快照接口,生成快照,因为快照涉及到video数据解码,如无必要,可不必开启,不然会额外消耗性能。

5. 拉流预览:如需预览拉流数据,只要调用播放端的播放接口,即可实现拉流数据预览;

6. 数据转AAC后转发:考虑到好多监控设备出来的音频可能是PCMA/PCMU的,如需要更通用的音频格式,可以转AAC后,在通过RTMP推送;

7. 转推RTMP实时静音:只需要在传audio数据的地方,加个判断即可;

8. 拉流速度反馈:通过RTSP播放端的实时码率反馈event,拿到实时带宽占用即可;

9. 整体网络状态反馈:考虑到有些摄像头可能会临时或异常关闭,RTMP服务器亦是,可以通过推拉流的event回调状态,查看那整体网络情况,如此界定:是拉不到流,还是推不到RTMP服务器。

系统设计架构图

Windows转发demo分析

大牛直播SDK的转发demo,Windows平台,对应C++ demo工程:WIN-RelaySDK-CPP-Demo,如需下载demo源码,参看 Github

1. 拉流:拉流和播放有些类似,但不需要播放(也就是说不要解码,资源消耗非常低),在做过基础的参数配置之后(对应demo里面OpenPullHandle()),设置音视频数据回调,然后调用StartPullStream()即可:

1.1 基础参数设置:

bool nt_stream_relay_wrapper::OpenPullHandle(const std::string& url, bool is_rtsp_tcp_mode, bool is_mute)
{if ( pull_handle_ != NULL )return true;if ( url.empty() )return false;duration_ = 0;NT_HANDLE pull_handle = NULL;ASSERT( pull_api_ != NULL );if (NT_ERC_OK != pull_api_->Open(&pull_handle, render_wnd_, 0, NULL)){return false;}ASSERT(pull_handle != NULL);pull_api_->SetEventCallBack(pull_handle, this, &NT_Pull_SDKEventHandle);pull_api_->SetBuffer(pull_handle, 0);pull_api_->SetFastStartup(pull_handle, 1);pull_api_->SetRTSPTcpMode(pull_handle, is_rtsp_tcp_mode ? 1 : 0);pull_api_->SetMute(pull_handle, is_mute ? 1 : 0);if ( NT_ERC_OK != pull_api_->SetURL(pull_handle, url.c_str()) ){pull_api_->Close(pull_handle);pull_handle = NULL;return false;}if ( setting_pos_ >= 0ll ){pull_api_->SetPos(pull_handle, setting_pos_);}pull_handle_ = pull_handle;return true;
}

1.2 设置音视频数据回调:

 pull_api_->SetPullStreamVideoDataCallBack(pull_handle_, this, &SP_SDKPullStreamVideoDataHandle);pull_api_->SetPullStreamAudioDataCallBack(pull_handle_, this, &SP_SDKPullStreamAudioDataHandle);

1.3 开始拉流:

 auto ret = pull_api_->StartPullStream(pull_handle_);if ( NT_ERC_OK != ret ){if ( !is_playing_ ){pull_api_->Close(pull_handle_);pull_handle_ = NULL;}return false;}

拉流整体代码如下:

bool nt_stream_relay_wrapper::StartPull(const std::string& url, bool is_rtsp_tcp_mode, bool is_transcode_aac)
{if ( is_pulling_ )return false;if ( !OpenPullHandle(url, is_rtsp_tcp_mode) )return false;pull_api_->SetPullStreamVideoDataCallBack(pull_handle_, this, &SP_SDKPullStreamVideoDataHandle);pull_api_->SetPullStreamAudioDataCallBack(pull_handle_, this, &SP_SDKPullStreamAudioDataHandle);pull_api_->SetPullStreamAudioTranscodeAAC(pull_handle_, is_transcode_aac? 1: 0);auto ret = pull_api_->StartPullStream(pull_handle_);if ( NT_ERC_OK != ret ){if ( !is_playing_ ){pull_api_->Close(pull_handle_);pull_handle_ = NULL;}return false;}is_pulling_ = true;return true;
}

2. 停止拉流:

停止拉流流程比较简单,先判断是否在拉流状态,如果拉流,调用StopPullStream() 即可,如没有预览画面,调用Close()接口关闭拉流实例。

void nt_stream_relay_wrapper::StopPull()
{if ( !is_pulling_ )return;pull_api_->StopPullStream(pull_handle_);if ( !is_playing_ ){pull_api_->Close(pull_handle_);pull_handle_ = NULL;}is_pulling_ = false;
}

3. 拉流端预览:

拉流端预览,说白了就是播放拉流数据,流程比较简单,demo调用如下,如不需要播放声音,调用SetMute(),实时打开/关闭即可:

bool nt_stream_relay_wrapper::StartPlay(const std::string& url, bool is_rtsp_tcp_mode, bool is_mute)
{if ( is_playing_ )return false;if ( !OpenPullHandle(url, is_rtsp_tcp_mode, is_mute) )return false;pull_api_->SetMute(pull_handle_, is_mute ? 1 : 0);auto ret = pull_api_->StartPlay(pull_handle_);if ( NT_ERC_OK != ret ){if ( !is_pulling_ ){pull_api_->Close(pull_handle_);pull_handle_ = NULL;}return false;}is_playing_ = true;return true;
}

4. 拉流端关闭预览:

void nt_stream_relay_wrapper::StopPlay()
{if ( !is_playing_ )return;pull_api_->StopPlay(pull_handle_);if ( !is_pulling_ ){pull_api_->Close(pull_handle_);pull_handle_ = NULL;}is_playing_ = false;
}

5. 开始推流到RTMP服务器:

推流的流程,如之前所述,调用RTMP推送模块,然后数据源传编码后的音视频数据即可,下图的demo源码,同时展示了,RTSP流获取到后,转推RTMP的时候,数据解密的处理:

bool nt_stream_relay_wrapper::StartPush(const std::string& url)
{if ( is_pushing_ )return false;if ( url.empty() )return false;if ( !OpenPushHandle() )return false;auto push_handle = GetPushHandle();ASSERT(push_handle != nullptr);ASSERT(push_api_ != NULL);if ( NT_ERC_OK != push_api_->SetURL(push_handle, url.c_str(), NULL) ){if ( !is_started_rtsp_stream_ ){push_api_->Close(push_handle);SetPushHandle(nullptr);}return false;}// 加密测试 +++// push_api_->SetRtmpEncryptionOption(push_handle, url.c_str(), 1, 1);// NT_BYTE test_key[16] = {'1', '2', '3'};// push_api_->SetRtmpEncryptionKey(push_handle, url.c_str(), test_key, 16);// 加密测试 --if ( NT_ERC_OK != push_api_->StartPublisher(push_handle, NULL) ){if ( !is_started_rtsp_stream_ ){push_api_->Close(push_handle);SetPushHandle(nullptr);}return false;}// // test push rtsp ++// push_api_->SetPushRtspTransportProtocol(push_handle, 1);// // push_api_->SetPushRtspTransportProtocol(push_handle, 2);// push_api_->SetPushRtspURL(push_handle, "rtsp://player.daniulive.com:554/liverelay111.sdp");// push_api_->StartPushRtsp(push_handle, 0);// // test push rtsp--is_pushing_ = true;return true;
}

6. 传递转推RTMP数据:

void nt_stream_relay_wrapper::OnVideoDataHandle(NT_HANDLE handle, NT_UINT32 video_codec_id, NT_BYTE* data, NT_UINT32 size, NT_SP_PullStreamVideoDataInfo* info)
{if (!is_pushing_ && !is_started_rtsp_stream_)return;if ( pull_handle_ != handle )return;if (data == NULL)return;if (size < 1)return;if (info == NULL)return;std::unique_lock<std::recursive_mutex> lock(push_handle_mutex_);if (!is_pushing_ && !is_started_rtsp_stream_)return;if (push_handle_ == NULL)return;push_api_->PostVideoEncodedDataV2(push_handle_, video_codec_id,data, size, info->is_key_frame_, info->timestamp_, info->presentation_timestamp_);
}void nt_stream_relay_wrapper::OnAudioDataHandle(NT_HANDLE handle, NT_UINT32 auido_codec_id,NT_BYTE* data, NT_UINT32 size, NT_SP_PullStreamAuidoDataInfo* info)
{if (!is_pushing_ && !is_started_rtsp_stream_)return;if (pull_handle_ != handle)return;if (data == NULL)return;if (size < 1)return;if (info == NULL)return;std::unique_lock<std::recursive_mutex> lock(push_handle_mutex_);if (!is_pushing_ && !is_started_rtsp_stream_)return;if (push_handle_ == NULL)return;push_api_->PostAudioEncodedData(push_handle_, auido_codec_id, data, size,info->is_key_frame_, info->timestamp_, info->parameter_info_, info->parameter_info_size_);
}

7. 关闭实时RTMP转推

void nt_stream_relay_wrapper::StopPush()
{if ( !is_pushing_ )return;is_pushing_ = false;std::unique_lock<std::recursive_mutex> lock(push_handle_mutex_);if ( nullptr == push_handle_ )return;push_api_->StopPublisher(push_handle_);// // test push rtsp ++// push_api_->StopPushRtsp(push_handle_);// // test push rtsp--if ( !is_started_rtsp_stream_ ){push_api_->Close(push_handle_);push_handle_ = nullptr;}
}

以上就是RTSP或RTMP流转RTMP推送的流程,感兴趣的开发者,可做设计参考。

如何实现多路海康大华等RTSP数据转RTMP推送相关推荐

  1. 大华的支持rtmp推流吗_RTSP安防摄像机(海康大华宇视等)如何推送到RTMP流媒体服务器进行直播...

    方案介绍 目前互联网直播的CDN和标准RTMP流媒体服务器通常只能接收RTMP格式的音视频推流.目前市场上有一些自带RTMP推流的摄像机和编码器,可以直接在其rtmp推流配置里面配置推送到RTMP流媒 ...

  2. 海康/大华 IpCamera RTSP地址和格式

    海康: rtsp://[username]:[password]@[ip]:[port]/[codec]/[channel]/[subtype]/av_stream 说明: username: 用户名 ...

  3. 海康大华设备RTSP地址格式

    海康老版本IPC的RTSP地址格式: 1.地址格式分段含义说明: rtsp://[username]:[password]@[address]:[port]/[codec]/[channel]/[su ...

  4. 海康大华相机rtsp

    海康: rtsp://[username]:[password]@[ip]:[port]/[codec]/[channel]/[subtype]/av_stream 说明: username: 用户名 ...

  5. 摄像头拼接技术-远超海康大华

    先来看一下海康大华 海康大华的技术挺好,但是这方面产品实用性不强,这样看对眼睛并不利,产品价格贵,造价5000以上 我们用两个枪机拼接 造价500,是不是好多了 技术应用很简单,没有必要使用华而不实的 ...

  6. LiveGBS流媒体平台国标GB/T28181作为下级支持国标级联海康大华宇视华为等第三方国标平台支持对接政务公安内网国标视频平台

    LiveGBS流媒体平台国标GB/T28181作为下级支持国标级联海康大华宇视华为等第三方国标平台支持对接政务公安内网国标视频平台 1.什么是GB/T28181级联 2.搭建GB28181国标流媒体平 ...

  7. Qt编写安防视频监控系统(支持win/linux/mac/海康/大华/宇视/264/265等)

    一.前言 视频监控系统在整个安防领域,已经做到了烂大街的程序,全国起码几百家公司做过类似的系统,当然这一方面的需求量也是非常旺盛的,各种定制化的需求越来越多,尤其是这几年借着人脸识别的东风,发展更加迅 ...

  8. 将海康大华宇视等网络摄像机RTSP流采用websecket和H5进行RTSP网页无插件直播点播的技术方案

    一. 背景分析 随着移动互联网时代的到来,安防监控领域为了适应互联网的发展要求,首先由国内安防监控龙头企业(海康.大华.宇视)带头先后开发了萤石云.乐橙云等互联网视频云服务,为广大个人或者企业监控用户 ...

  9. 华为海康大华摄像头编译RTSP转RTMP和HTTPFLV

    华为海康大华摄像头编译RTSP转RTMP和HTTPFLV 项目需求要看摄像头实时画面谷歌浏览器不支持RTSP流直接展示 方案一 通过Java+nginx+rtsp转rtmp流 方案二 通过Java+ ...

最新文章

  1. 一文看懂机器视觉芯片 ​
  2. 关闭CISCO不必要的服务
  3. python变量需要声明吗_python中可以声明变量类型吗
  4. MAC安装iterm2及配置
  5. 因误开 IDC 灭火器,导致 Azure 在欧洲罢工超过 7 小时!
  6. o00o0o php,PHP $O00OO0=urldecode eval 解密,记一次商业源码的去后门
  7. Java中的原型设计模式
  8. 进阶之路(基础篇) - 003 I/O的模拟的读取
  9. 经典枚举——百钱百鸡问题
  10. java 多音词语转拼音_一种多音字汉字转拼音全拼的方法与流程
  11. java fadein_原生JS实现 fadeIn / fadeOut 方法
  12. IOl流的分类与使用
  13. outlook邮箱撤回邮件
  14. VirtualBox下Windows和Ubuntu的文件夹共享
  15. 苹果双系统怎么切换_Mac可以这样卸载双系统中的windows系统
  16. Java学习笔记——Number类
  17. 所有毕业生的论文都要查重吗?
  18. 《程序员健康指南》作者Joe Kutner:在运动中工作
  19. 卡尔曼滤波(附python代码)
  20. android 程序包名,android系统一些应用包名的整理

热门文章

  1. 9.关于Unicode字符集
  2. 同步关键词synchronized
  3. Android应用检查更新下载安装打开
  4. tkmybatis 子查询_真假童子命符箓道长教你如何查询
  5. vue的html自动刷新,Vue页面刷新记住页面状态的实现
  6. sp烘焙流程_小手雷-PBR材质流程(一)——(基本材质)
  7. python批量雷达图_python批量制作雷达图
  8. python语言程序设计及医学应用_Python语言程序设计(高等学校计算机专业规划教材)...
  9. 内蒙古师范大学计算机科学技术学院,内蒙古师范大学计算机科学技术硕士生导师——李成城...
  10. pythonmysqldb_python中MySQLdb的使用