一个好的转发模块,首先要低延迟!
其次足够稳定、灵巧、有状态反馈机制、资源占用低,跨平台,最好以接口形式供应,便于第三方系统集成,整体功能设计如下:
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做事器。
下面分别先容下两种技能方案:
FFmpeg技能方案安装FFmpeg:首先,您须要安装FFmpeg。FFmpeg是一个开源的跨平台视频和音频处理工具,它支持将RTSP流转换为RTMP流。您可以从FFmpeg官方网站下载适用于Windows的二进制安装程序,并按照解释进行安装。配置FFmpeg:安装完FFmpeg后,您须要配置其命令行参数,以便将RTSP流转换为RTMP流,并将其推送到目标做事器。您可以利用以下命令行参数: ffmpeg -i rtsp://[摄像头地址]/[流媒体地址] -f flv rtmp://[做事器地址]/[直播频道]
个中,rtsp://[摄像头地址]/[流媒体地址]是摄像头的RTSP流地址,rtmp://[做事器地址]/[直播频道]是目标做事器的RTMP流地址。您可以根据实际情形修正这些参数。
运行FFmpeg:配置完FFmpeg后,您可以利用命令行或脚本文件来运行FFmpeg。您可以在命令行中直接运行上述命令,或者将命令写入脚本文件(例如bat文件),然后运行脚本文件。须要把稳的是,上述方案中的摄像头地址、流媒体地址、做事器地址和直播频道都须要更换为实际的地址和信息。此外,您还须要确保摄像头的RTSP流可公开访问,并且目标做事器的RTMP流地址已经配置精确。集成到运用程序中:如果您须要在运用程序中实现实时视频流推送,您可以将FFmpeg集成到运用程序中。您可以利用FFmpeg的API或命令行接口,通过编程办法调用FFmpeg的功能,并将摄像头的RTSP流转换为RTMP流,并将其推送到目标做事器。本文福利, 免费领取C++音视频学习资料包+学习路线大纲、技能视频/代码,内容包括(音视频开拓,口试题,FFmpeg ,webRTC ,rtmp ,hls ,rtsp ,ffplay ,编解码,推拉流,srs),有须要的可以进企鹅裙927239107领取哦~
SDK技能方案以大牛直播SDK的Windows平台RTSP转RTMP推送C++的demo为例:
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;}if ( NT_ERC_OK != push_api_->StartPublisher(push_handle, NULL) ){if ( !is_started_rtsp_stream_ ){push_api_->Close(push_handle);SetPushHandle(nullptr);}return false;}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_);if ( !is_started_rtsp_stream_ ){push_api_->Close(push_handle_);push_handle_ = nullptr;}}
总结
无论您选择哪种方案,须要确保以下几点:
选择一个稳定可靠的第三方库或做事,以确保转换的质量和可靠性;理解和节制干系的技能和协议,例如RTSP和RTMP,以及如何利用干系的库和工具进行转换和处理;考虑性能和资源的问题,特殊是在处理大量视频流或高并发的场景下。须要确保系统具有足够的处理能力和带宽,以避免延迟或丢帧等问题。Windows平台上的RTSP转RTMP推送须要一些技能准备和方案,以及对干系协议和工具的理解和利用履历,做个根本的demo,用FFmpeg就可以,但是如果产品话,须要考虑的点实在太多了。