如何實現多路海康大華等RTSP數據轉RTMP推送


一個好的轉發模塊,首先要低延遲!其次足夠穩定靈活、有狀態反饋機制、資源占用低,跨平台,最好以接口形式提供,便於第三方系統集成。

以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推送的流程,感興趣的開發者,可做設計參考。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM