技术选型
RTSP转RTMP推送到流媒体服务器,说起来技术实现不难,简单来说,获取RTSP流后,拿到未经解码的H.264/H.265和audio数据,重新打包RTMP发送出去即可。需要注意的是,大多RTSP转RTMP模块,需要长时间运行,所以,需要有好多错误处理和自动重连机制,确保转发模块的稳定性。以下是两个可选的技术方案:
方案1:FFMPEG命令转发
rtsp://[摄像头地址]/[流媒体地址]
是摄像头的RTSP流地址。-c:v libx264
指定视频编码器为libx264。-preset veryfast
设置编码速度为非常快,以牺牲一些压缩效率换取更快的编码速度。-maxrate
和-bufsize
设置最大码率和缓冲区大小。-pix_fmt yuv420p
设置像素格式为YUV420P,这是RTMP兼容的格式。-g 50
设置关键帧间隔为50帧。-c:a aac
指定音频编码器为AAC。-b:a
、-ac
和-ar
分别设置音频比特率、声道数和采样率。-f flv
指定输出格式为FLV,RTMP流通常以FLV格式封装。rtmp://[服务器地址]/[应用名称]/[流密钥]
是目标RTMP服务器的推送地址。
上述命令中的参数可能需要根据实际情况进行调整。
方案2:SmartRelaySDK
大牛直播SDK发布的RTSP转RTMP推送模块(SmartRelaySDK)C#的界面如下:
技术设计:
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服务器。
上述是C#的基础demo,如果对C++比较熟悉,也可以直接用C++的,大牛直播SDK的RTSP转RTMP推送模块,通过配置xml的形式,程序启动后,从configure.xml读取相关的参数,实现一键拉流转发。
常规的参数配置,比如推拉流的rtsp rtmp url,如果需要自采集audio,设置采集的audio类型,比如rtsp自带audio、麦克风、扬声器或麦克风扬声器混音。
<?xml version="1.0" encoding="utf-8" ?>
<StreamRelays>
<Relay>
<id>0</id>
<AudioOption>4</AudioOption>
<PullUrl>rtsp://admin:daniulive12345@192.168.0.120:554/h264/ch1/main/av_stream</PullUrl>
<PushUrl>rtmp://192.168.0.103:1935/hls/stream00</PushUrl>
</Relay>
<Relay>
<id>1</id>
<AudioOption>1</AudioOption>
<PullUrl>rtsp://admin:admin123456@192.168.0.121:554/cam/realmonitor?channel=1<![CDATA[&]]>subtype=0</PullUrl>
<PushUrl>rtmp://192.168.0.103:1935/hls/stream01</PushUrl>
</Relay>
<Relay>
<id>2</id>
<AudioOption>3</AudioOption>
<PullUrl>rtsp://admin:daniulive12345@192.168.0.120:554/h264/ch1/main/av_stream</PullUrl>
<PushUrl>rtmp://192.168.0.103:1935/hls/stream02</PushUrl>
</Relay>
<Relay>
<id>3</id>
<AudioOption>3</AudioOption>
<PullUrl>rtsp://admin:admin123456@192.168.0.121:554/cam/realmonitor?channel=1<![CDATA[&]]>subtype=0</PullUrl>
<PushUrl>rtmp://192.168.0.103:1935/hls/stream03</PushUrl>
</Relay>
<Relay>
<id>4</id>
<AudioOption>4</AudioOption>
<PullUrl>rtsp://admin:daniulive12345@192.168.0.120:554/h264/ch1/main/av_stream</PullUrl>
<PushUrl>rtmp://192.168.0.103:1935/hls/stream04</PushUrl>
</Relay>
<Relay>
<id>5</id>
<AudioOption>1</AudioOption>
<PullUrl>rtsp://admin:admin123456@192.168.0.121:554/cam/realmonitor?channel=1<![CDATA[&]]>subtype=0</PullUrl>
<PushUrl>rtmp://192.168.0.103:1935/hls/stream05</PushUrl>
</Relay>
<Relay>
<id>6</id>
<AudioOption>4</AudioOption>
<PullUrl>rtsp://admin:daniulive12345@192.168.0.120:554/h264/ch1/main/av_stream</PullUrl>
<PushUrl>rtmp://192.168.0.103:1935/hls/stream06</PushUrl>
</Relay>
<Relay>
<id>7</id>
<AudioOption>2</AudioOption>
<PullUrl>rtsp://admin:admin123456@192.168.0.121:554/cam/realmonitor?channel=1<![CDATA[&]]>subtype=0</PullUrl>
<PushUrl>rtmp://192.168.0.103:1935/hls/stream07</PushUrl>
</Relay>
</StreamRelays>
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
封装代码如下:
/*
* nt_relay_wrapper.cs.cs
* nt_relay_wrapper.cs
*
* WebSite: https://daniusdk.com
* WeChat: xinsheng120
*
* Created by DaniuLive on 2017/11/14.
* Copyright © 2014~2024 DaniuLive. All rights reserved.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SmartRelayDemo
{
class nt_relay_wrapper
{
int relay_index_;
nt_player_wrapper player_wrapper_;
nt_publisher_wrapper publisher_wrapper_;
UInt32 video_option_ = (UInt32)NT.NTSmartPublisherDefine.NT_PB_E_VIDEO_OPTION.NT_PB_E_VIDEO_OPTION_ENCODED_DATA;
UInt32 audio_option_ = (UInt32)NT.NTSmartPublisherDefine.NT_PB_E_AUDIO_OPTION.NT_PB_E_AUDIO_OPTION_ENCODED_DATA;
public nt_player_wrapper GetPlayerWrapper() { return player_wrapper_; }
public nt_publisher_wrapper GetPublisherWrapper() { return publisher_wrapper_; }
public nt_relay_wrapper(int index, System.Windows.Forms.Control render_wnd, System.ComponentModel.ISynchronizeInvoke sync_invoke)
{
relay_index_ = index;
player_wrapper_ = new nt_player_wrapper(index, render_wnd, sync_invoke);
publisher_wrapper_ = new nt_publisher_wrapper(index, render_wnd, sync_invoke);
}
~nt_relay_wrapper() { }
private void OnVideoDataHandle(IntPtr handle, IntPtr user_data,
UInt32 video_codec_id, IntPtr data, UInt32 size,
IntPtr info, IntPtr reserve)
{
if (publisher_wrapper_.is_rtmp_publishing())
{
publisher_wrapper_.OnVideoDataHandle(handle, user_data, video_codec_id, data, size, info, reserve);
}
}
private void OnAudioDataHandle(IntPtr handle, IntPtr user_data,
UInt32 audio_codec_id, IntPtr data, UInt32 size,
IntPtr info, IntPtr reserve)
{
if (publisher_wrapper_.is_rtmp_publishing())
{
publisher_wrapper_.OnAudioDataHandle(handle, user_data, audio_codec_id, data, size, info, reserve);
}
}
public void StartPull(String url)
{
if (!player_wrapper_.is_pulling())
{
player_wrapper_.SetBuffer(0);
if (!player_wrapper_.StartPull(url, false))
return;
player_wrapper_.EventOnVideoDataHandle += new nt_player_wrapper.DelOnVideoDataHandle(OnVideoDataHandle);
if (audio_option_ == (UInt32)NT.NTSmartPublisherDefine.NT_PB_E_AUDIO_OPTION.NT_PB_E_AUDIO_OPTION_ENCODED_DATA)
{
player_wrapper_.EventOnAudioDataHandle += new nt_player_wrapper.DelOnAudioDataHandle(OnAudioDataHandle);
}
}
}
public void StopPull()
{
player_wrapper_.StopPull();
}
public void StartPlayer(String url, bool is_rtsp_tcp_mode, bool is_mute)
{
player_wrapper_.SetBuffer(0);
if (!player_wrapper_.StartPlay(url, is_rtsp_tcp_mode, is_mute))
return;
}
public void StopPlayer()
{
player_wrapper_.StopPlay();
}
public void PlayerDispose()
{
player_wrapper_.Dispose();
}
public void SetPusherOption(UInt32 video_option, UInt32 audio_option)
{
video_option_ = video_option;
audio_option_ = audio_option;
}
public void StartPublisher(String url)
{
if (!publisher_wrapper_.OpenPublisherHandle(video_option_, audio_option_))
return;
if (url.Length < 8)
{
publisher_wrapper_.try_close_handle();
return;
}
if (!publisher_wrapper_.StartPublisher(url))
{
return;
}
}
public void StopPublisher()
{
publisher_wrapper_.StopPublisher();
}
public void PublisherDispose()
{
publisher_wrapper_.Dispose();
}
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
- 58.
- 59.
- 60.
- 61.
- 62.
- 63.
- 64.
- 65.
- 66.
- 67.
- 68.
- 69.
- 70.
- 71.
- 72.
- 73.
- 74.
- 75.
- 76.
- 77.
- 78.
- 79.
- 80.
- 81.
- 82.
- 83.
- 84.
- 85.
- 86.
- 87.
- 88.
- 89.
- 90.
- 91.
- 92.
- 93.
- 94.
- 95.
- 96.
- 97.
- 98.
- 99.
- 100.
- 101.
- 102.
- 103.
- 104.
- 105.
- 106.
- 107.
- 108.
- 109.
- 110.
- 111.
- 112.
- 113.
- 114.
- 115.
- 116.
- 117.
- 118.
- 119.
- 120.
- 121.
- 122.
- 123.
- 124.
- 125.
- 126.
- 127.
- 128.
- 129.
- 130.
- 131.
- 132.
- 133.
- 134.
如需播放,播放端封装逻辑实现如下:
/*
* nt_player_wrapper.cs
* nt_player_wrapper
*
* WebSite: https://daniusdk.com
*
* Created by DaniuLive on 2017/11/14.
* Copyright © 2014~2024 DaniuLive. All rights reserved.
*/
public bool is_playing() { return is_playing_; }
public bool is_pulling() { return is_pulling_; }
public bool is_recording() { return is_recording_; }
public static bool is_zero_ptr(IntPtr ptr) { return IntPtr.Zero == ptr; }
public bool is_empty_handle() { return is_zero_ptr(player_handle_); }
private bool is_running()
{
if (is_empty_handle())
return false;
return is_playing_ || is_recording_ || is_pulling_;
}
public bool OpenPullHandle(String url, bool is_rtsp_tcp_mode, bool is_mute)
{
if ( player_handle_ != IntPtr.Zero )
return true;
if ( String.IsNullOrEmpty(url) )
return false;
IntPtr pull_handle = IntPtr.Zero;
if (NTBaseCodeDefine.NT_ERC_OK != NTSmartPlayerSDK.NT_SP_Open(out pull_handle, IntPtr.Zero, 0, IntPtr.Zero))
{
return false;
}
if (pull_handle == IntPtr.Zero)
{
return false;
}
pull_event_call_back_ = new SP_SDKEventCallBack(SDKPullEventCallBack);
NTSmartPlayerSDK.NT_SP_SetEventCallBack(pull_handle, IntPtr.Zero, pull_event_call_back_);
resolution_notify_callback_ = new ResolutionNotifyCallback(PlaybackWindowResized);
set_video_frame_call_back_ = new VideoFrameCallBack(SDKVideoFrameCallBack);
NTSmartPlayerSDK.NT_SP_SetBuffer(pull_handle, play_buffer_);
NTSmartPlayerSDK.NT_SP_SetFastStartup(pull_handle, 1);
NTSmartPlayerSDK.NT_SP_SetRtspAutoSwitchTcpUdp(pull_handle, 1);
NTSmartPlayerSDK.NT_SP_SetRTSPTcpMode(pull_handle, is_rtsp_tcp_mode ? 1 : 0);
NTSmartPlayerSDK.NT_SP_SetMute(pull_handle, is_mute_ ? 1 : 0);
NTSmartPlayerSDK.NT_SP_SetAudioVolume(pull_handle, cur_audio_volume_);
Int32 is_report = 1;
Int32 report_interval = 3;
NTSmartPlayerSDK.NT_SP_SetReportDownloadSpeed(pull_handle, is_report, report_interval);
//RTSP timeout设置
Int32 rtsp_timeout = 10;
NTSmartPlayerSDK.NT_SP_SetRtspTimeout(pull_handle, rtsp_timeout);
//RTSP TCP/UDP自动切换设置
Int32 is_auto_switch_tcp_udp = 1;
NTSmartPlayerSDK.NT_SP_SetRtspAutoSwitchTcpUdp(pull_handle, is_auto_switch_tcp_udp);
if (NTBaseCodeDefine.NT_ERC_OK != NTSmartPlayerSDK.NT_SP_SetURL(pull_handle, url))
{
NTSmartPlayerSDK.NT_SP_Close(pull_handle);
pull_handle = IntPtr.Zero;
return false;
}
player_handle_ = pull_handle;
return true;
}
private void PlaybackWindowResized(Int32 width, Int32 height)
{
String resolution = width + "*" + height;
EventGetVideoSize(player_index_, resolution);
}
public void SP_SDKVideoSizeHandle(IntPtr handle, IntPtr userData, Int32 width, Int32 height)
{
if (null == sync_invoke_)
return;
System.ComponentModel.ISynchronizeInvoke sync_invoke_target = sync_invoke_.Target as System.ComponentModel.ISynchronizeInvoke;
if (sync_invoke_target != null)
{
if (sync_invoke_target.InvokeRequired)
{
sync_invoke_target.BeginInvoke(resolution_notify_callback_, new object[] { width, height });
}
else
{
resolution_notify_callback_(width, height);
}
}
}
public bool StartPlay(String url, bool is_rtsp_tcp_mode, bool is_mute)
{
if ( is_playing_ )
return false;
if (!is_pulling() && !is_recording())
{
if (!OpenPullHandle(url, is_rtsp_tcp_mode, is_mute))
return false;
}
//video resolution callback
video_size_call_back_ = new SP_SDKVideoSizeCallBack(SP_SDKVideoSizeHandle);
NTSmartPlayerSDK.NT_SP_SetVideoSizeCallBack(player_handle_, IntPtr.Zero, video_size_call_back_);
bool is_support_d3d_render = false;
Int32 in_support_d3d_render = 0;
if (NT.NTBaseCodeDefine.NT_ERC_OK == NTSmartPlayerSDK.NT_SP_IsSupportD3DRender(player_handle_, render_wnd_.Handle, ref in_support_d3d_render))
{
if (1 == in_support_d3d_render)
{
is_support_d3d_render = true;
}
}
// is_support_d3d_render = false;
if (is_support_d3d_render)
{
// 支持d3d绘制的话,就用D3D绘制
NTSmartPlayerSDK.NT_SP_SetRenderWindow(player_handle_, render_wnd_.Handle);
NTSmartPlayerSDK.NT_SP_SetRenderScaleMode(player_handle_, 1);
}
else
{
// 不支持D3D就让播放器吐出数据来,用GDI绘制,本demo仅用来展示一对一互动使用,具体可参考播放端的demo
//video frame callback (YUV/RGB)
//format请参见 NT_SP_E_VIDEO_FRAME_FORMAT,如需回调YUV,请设置为 NT_SP_E_VIDEO_FRAME_FROMAT_I420
video_frame_call_back_ = new SP_SDKVideoFrameCallBack(SetVideoFrameCallBack);
NTSmartPlayerSDK.NT_SP_SetVideoFrameCallBack(player_handle_, (Int32)NT.NTSmartPlayerDefine.NT_SP_E_VIDEO_FRAME_FORMAT.NT_SP_E_VIDEO_FRAME_FORMAT_RGB32, IntPtr.Zero, video_frame_call_back_);
}
uint ret = NTSmartPlayerSDK.NT_SP_StartPlay(player_handle_);
if ( NTBaseCodeDefine.NT_ERC_OK != ret )
{
NTSmartPlayerSDK.NT_SP_Close(player_handle_);
player_handle_ = IntPtr.Zero;
return false;
}
is_playing_ = true;
return true;
}
public void StopPlay(bool is_update_ui =true)
{
if ( !is_playing_ )
return;
NTSmartPlayerSDK.NT_SP_StopPlay(player_handle_);
if (!is_pulling() && !is_recording())
{
NTSmartPlayerSDK.NT_SP_Close(player_handle_);
player_handle_ = IntPtr.Zero;
}
is_playing_ = false;
if (is_update_ui && render_wnd_ != null)
{
render_wnd_.Invalidate();
}
}
public bool StartPull(String url, bool is_rtsp_tcp_mode)
{
if (is_pulling())
return false;
if (!is_playing() && !is_recording())
{
if (!OpenPullHandle(url, is_rtsp_tcp_mode, is_mute_))
return false;
}
pull_stream_video_data_call_back_ = new SP_SDKPullStreamVideoDataCallBack(OnVideoDataHandle);
pull_stream_audio_data_call_back_ = new SP_SDKPullStreamAudioDataCallBack(OnAudioDataHandle);
NTSmartPlayerSDK.NT_SP_SetPullStreamVideoDataCallBack(player_handle_, IntPtr.Zero, pull_stream_video_data_call_back_);
NTSmartPlayerSDK.NT_SP_SetPullStreamAudioDataCallBack(player_handle_, IntPtr.Zero, pull_stream_audio_data_call_back_);
int is_transcode_aac = 1; //PCMA/PCMU/Speex格式转AAC后 再转发
NTSmartPlayerSDK.NT_SP_SetPullStreamAudioTranscodeAAC(player_handle_, is_transcode_aac);
UInt32 ret = NTSmartPlayerSDK.NT_SP_StartPullStream(player_handle_);
if (NTBaseCodeDefine.NT_ERC_OK != ret)
{
if (!is_playing_)
{
NTSmartPlayerSDK.NT_SP_Close(player_handle_);
player_handle_ = IntPtr.Zero;
}
return false;
}
is_pulling_ = true;
return true;
}
public void StopPull()
{
if (!is_pulling_)
return;
NTSmartPlayerSDK.NT_SP_StopPullStream(player_handle_);
if (!is_playing() && !is_recording())
{
NTSmartPlayerSDK.NT_SP_Close(player_handle_);
player_handle_ = IntPtr.Zero;
}
is_pulling_ = false;
}
private void OnVideoDataHandle(IntPtr handle, IntPtr user_data,
UInt32 video_codec_id, IntPtr data, UInt32 size,
IntPtr info, IntPtr reserve)
{
EventOnVideoDataHandle(handle, user_data, video_codec_id, data, size, info, reserve);
}
private void OnAudioDataHandle(IntPtr handle, IntPtr user_data,
UInt32 audio_codec_id, IntPtr data, UInt32 size,
IntPtr info, IntPtr reserve)
{
EventOnAudioDataHandle(handle, user_data, audio_codec_id, data, size, info, reserve);
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
- 58.
- 59.
- 60.
- 61.
- 62.
- 63.
- 64.
- 65.
- 66.
- 67.
- 68.
- 69.
- 70.
- 71.
- 72.
- 73.
- 74.
- 75.
- 76.
- 77.
- 78.
- 79.
- 80.
- 81.
- 82.
- 83.
- 84.
- 85.
- 86.
- 87.
- 88.
- 89.
- 90.
- 91.
- 92.
- 93.
- 94.
- 95.
- 96.
- 97.
- 98.
- 99.
- 100.
- 101.
- 102.
- 103.
- 104.
- 105.
- 106.
- 107.
- 108.
- 109.
- 110.
- 111.
- 112.
- 113.
- 114.
- 115.
- 116.
- 117.
- 118.
- 119.
- 120.
- 121.
- 122.
- 123.
- 124.
- 125.
- 126.
- 127.
- 128.
- 129.
- 130.
- 131.
- 132.
- 133.
- 134.
- 135.
- 136.
- 137.
- 138.
- 139.
- 140.
- 141.
- 142.
- 143.
- 144.
- 145.
- 146.
- 147.
- 148.
- 149.
- 150.
- 151.
- 152.
- 153.
- 154.
- 155.
- 156.
- 157.
- 158.
- 159.
- 160.
- 161.
- 162.
- 163.
- 164.
- 165.
- 166.
- 167.
- 168.
- 169.
- 170.
- 171.
- 172.
- 173.
- 174.
- 175.
- 176.
- 177.
- 178.
- 179.
- 180.
- 181.
- 182.
- 183.
- 184.
- 185.
- 186.
- 187.
- 188.
- 189.
- 190.
- 191.
- 192.
- 193.
- 194.
- 195.
- 196.
- 197.
- 198.
- 199.
- 200.
- 201.
- 202.
- 203.
- 204.
- 205.
- 206.
- 207.
- 208.
- 209.
- 210.
- 211.
- 212.
- 213.
- 214.
- 215.
- 216.
- 217.
- 218.
- 219.
- 220.
- 221.
- 222.
- 223.
- 224.
- 225.
- 226.
- 227.
- 228.
- 229.
- 230.
- 231.
- 232.
- 233.
- 234.
- 235.
- 236.
- 237.
- 238.
- 239.
- 240.
- 241.
- 242.
- 243.
- 244.
- 245.
- 246.
- 247.
- 248.
- 249.
- 250.
- 251.
- 252.
- 253.
- 254.
- 255.
- 256.
- 257.
- 258.
- 259.
- 260.
- 261.
转推RTMP部分,推送端封装核心代码设计如下:
/*
* nt_publisher_wrapper.cs
* nt_publisher_wrapper
*
* WebSite: https://daniusdk.com
*
* Created by DaniuLive on 2017/11/14.
* Copyright © 2014~2024 DaniuLive. All rights reserved.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Drawing;
using System.Drawing.Drawing2D;
using NT;
using System.Threading;
namespace SmartRelayDemo
{
public struct NT_VideoFrame
{
public Int32 width_; // 图像宽
public Int32 height_; // 图像高
public IntPtr plane_;
public Int32 stride_;
}
public struct CameraInfo
{
public String name_;
public String id_;
public List<NT_PB_VideoCaptureCapability> capabilities_;
};
class nt_publisher_wrapper : IDisposable
{
public delegate void DelGetPublisherEventMsg(int publisher_index, String msg); //推送端Event消息
[DllImport("kernel32", EntryPoint = "CopyMemory")]
static extern void CopyMemory(IntPtr Destination, IntPtr Source, uint Length);
private bool disposed_ = false;
private System.Windows.Forms.Control render_wnd_ = null;
private System.Windows.Forms.PaintEventHandler render_wnd_paint_event_ = null;
private IntPtr rtsp_handle_ = IntPtr.Zero;
private const int enter_read_lock_timeout_ms_ = 1; // 暂时定义1毫秒, 也可以考虑0毫秒
private ReaderWriterLockSlim shared_lock_ = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);
private volatile IntPtr handle_ = IntPtr.Zero;
private volatile int handle_reference_count_ = 0;
private volatile bool is_rtmp_publishing_ = false;
private volatile bool is_previewing_ = false;
private volatile bool is_recording_ = false;
private volatile bool is_rtsp_service_running_ = false; //RTSP服务状态
private volatile bool is_rtsp_publishing_ = false; //RTSP流发布状态
private WeakReference sync_invoke_ = null;
private uint video_option_ = (uint)NTSmartPublisherDefine.NT_PB_E_VIDEO_OPTION.NT_PB_E_VIDEO_OPTION_ENCODED_DATA;
private uint audio_option_ = (uint)NTSmartPublisherDefine.NT_PB_E_AUDIO_OPTION.NT_PB_E_AUDIO_OPTION_ENCODED_DATA;
private int video_width_ = 1280;
private int video_height_ = 720;
private volatile int external_video_layer_index_ = 0;
private volatile int text_layer_index_ = 1;
//event事件回调
NT_PB_SDKEventCallBack pb_event_call_back_;
delegate void PbSetEventCallBack(UInt32 event_id,
Int64 param1,
Int64 param2,
UInt64 param3,
UInt64 param4,
[MarshalAs(UnmanagedType.LPStr)] String param5,
[MarshalAs(UnmanagedType.LPStr)] String param6,
IntPtr param7);
PbSetEventCallBack pb_set_event_call_back_;
//预览数据回调
NT_PB_SDKVideoPreviewImageCallBack video_preview_image_callback_;
delegate void SetVideoPreviewImageCallBack(NT_VideoFrame frame);
SetVideoPreviewImageCallBack set_video_preview_image_callback_;
private NT_VideoFrame cur_image_ = new NT_VideoFrame();
public event DelGetPublisherEventMsg EventGetPublisherEventMsg;
private NT_PB_RectRegion layer_regin_ = new NT_PB_RectRegion();
private int publisher_index_ = 0;
public nt_publisher_wrapper(int index, System.Windows.Forms.Control render_wnd, System.ComponentModel.ISynchronizeInvoke sync_invoke)
{
publisher_index_ = index;
render_wnd_ = render_wnd;
sync_invoke_ = new WeakReference(sync_invoke);
pb_set_event_call_back_ = new PbSetEventCallBack(PbEventCallBack);
if (render_wnd_ != null)
{
render_wnd_paint_event_ = new System.Windows.Forms.PaintEventHandler(this.OnRenderWindowPaint);
render_wnd_.Paint += render_wnd_paint_event_;
}
layer_regin_.x_ = 0;
layer_regin_.y_ = 0;
layer_regin_.width_ = 0;
layer_regin_.height_ = 0;
}
public void Dispose()
{
Dispose(true);
// This object will be cleaned up by the Dispose method.
// Therefore, you should call GC.SupressFinalize to
// take this object off the finalization queue
// and prevent finalization code for this object
// from executing a second time.
// GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
// Check to see if Dispose has already been called.
if (!this.disposed_)
{
if (disposing)
{
}
if (render_wnd_ != null && render_wnd_paint_event_ != null)
{
render_wnd_.Paint -= render_wnd_paint_event_;
}
render_wnd_paint_event_ = null;
if (cur_image_.plane_ != IntPtr.Zero)
{
Marshal.FreeHGlobal(cur_image_.plane_);
cur_image_.plane_ = IntPtr.Zero;
}
// Note disposing has been done.
disposed_ = true;
}
}
~nt_publisher_wrapper()
{
Dispose(false);
}
private void PbEventCallBack(UInt32 event_id,
Int64 param1,
Int64 param2,
UInt64 param3,
UInt64 param4,
[MarshalAs(UnmanagedType.LPStr)] String param5,
[MarshalAs(UnmanagedType.LPStr)] String param6,
IntPtr param7)
{
String event_log = "";
switch (event_id)
{
case (uint)NTSmartPublisherDefine.NT_PB_E_EVENT_ID.NT_PB_E_EVENT_ID_CONNECTING:
event_log = "连接中";
if (!String.IsNullOrEmpty(param5))
{
event_log = event_log + " url:" + param5;
}
break;
case (uint)NTSmartPublisherDefine.NT_PB_E_EVENT_ID.NT_PB_E_EVENT_ID_CONNECTION_FAILED:
event_log = "连接失败";
if (!String.IsNullOrEmpty(param5))
{
event_log = event_log + " url:" + param5;
}
break;
case (uint)NTSmartPublisherDefine.NT_PB_E_EVENT_ID.NT_PB_E_EVENT_ID_CONNECTED:
event_log = "已连接";
if (!String.IsNullOrEmpty(param5))
{
event_log = event_log + " url:" + param5;
}
break;
case (uint)NTSmartPublisherDefine.NT_PB_E_EVENT_ID.NT_PB_E_EVENT_ID_RECORDER_START_NEW_FILE:
event_log = " start new recorder file";
byte[] utf8_bytes = Encoding.Default.GetBytes(param5);
byte[] default_bytes = Encoding.Convert(Encoding.UTF8, Encoding.Default, utf8_bytes);
String file_name = Encoding.Default.GetString(default_bytes);
if (!String.IsNullOrEmpty(file_name))
{
event_log = event_log + " file name:" + file_name;
}
break;
case (uint)NTSmartPublisherDefine.NT_PB_E_EVENT_ID.NT_PB_E_EVENT_ID_ONE_RECORDER_FILE_FINISHED:
event_log = " finish recorder file";
byte[] finished_utf8_bytes = Encoding.Default.GetBytes(param5);
byte[] finished_default_bytes = Encoding.Convert(Encoding.UTF8, Encoding.Default, finished_utf8_bytes);
String finished_file_name = Encoding.Default.GetString(finished_default_bytes);
if (!String.IsNullOrEmpty(finished_file_name))
{
event_log = event_log + " file name:" + finished_file_name;
}
break;
case (uint)NTSmartPublisherDefine.NT_PB_E_EVENT_ID.NT_PB_E_EVENT_ID_DISCONNECTED:
event_log = "断开连接";
if (!String.IsNullOrEmpty(param5))
{
event_log = event_log + " url:" + param5;
}
break;
case (uint)NTSmartPublisherDefine.NT_PB_E_EVENT_ID.NT_PB_E_EVENT_ID_RTSP_URL:
event_log = "RTSP URL: " + param5;
break;
default:
break;
}
EventGetPublisherEventMsg(publisher_index_, event_log);
}
public void SetResolution(int width, int height)
{
video_width_ = width;
video_height_ = height;
}
public int CalBitRate(int frame_rate, int w, int h)
{
int kbit_rate = 2000;
int area = w * h;
if (area <= (320 * 300))
{
kbit_rate = 280;
}
else if (area <= (360 * 320))
{
kbit_rate = 360;
}
else if (area <= (640 * 480))
{
kbit_rate = 580;
}
else if (area <= (800 * 600))
{
kbit_rate = 620;
}
else if (area <= (900 * 700))
{
kbit_rate = 820;
}
else if (area <= (1280 * 720))
{
kbit_rate = 2000;
}
else if (area <= (1366 * 768))
{
kbit_rate = 2100;
}
else if (area <= (1600 * 900))
{
kbit_rate = 2500;
}
else if (area <= (1600 * 1050))
{
kbit_rate = 2700;
}
else if (area <= (1920 * 1088))
{
kbit_rate = 4500;
}
else
{
kbit_rate = 6000;
}
kbit_rate = kbit_rate * frame_rate / 25;
if (kbit_rate < 80)
kbit_rate = 80;
return kbit_rate;
}
public int CalMaxKBitRate(int frame_rate, int w, int h, bool is_var_bitrate)
{
int max_kbit_rate = 2000;
int area = w * h;
if (area <= (320 * 300))
{
max_kbit_rate = is_var_bitrate ? 320 : 600;
}
else if (area <= (360 * 320))
{
max_kbit_rate = is_var_bitrate ? 400 : 800;
}
else if (area <= (640 * 360))
{
max_kbit_rate = is_var_bitrate ? 600 : 1000;
}
else if (area <= (640 * 480))
{
max_kbit_rate = is_var_bitrate ? 680 : 1300;
}
else if (area <= (800 * 600))
{
max_kbit_rate = is_var_bitrate ? 700 : 1500;
}
else if (area <= (900 * 700))
{
max_kbit_rate = is_var_bitrate ? 920 : 2200;
}
else if (area <= (1280 * 720))
{
max_kbit_rate = is_var_bitrate ? 2200 : 3200;
}
else if (area <= (1366 * 768))
{
max_kbit_rate = is_var_bitrate ? 2300 : 3300;
}
else if (area <= (1600 * 900))
{
max_kbit_rate = is_var_bitrate ? 2600 : 3500;
}
else if (area <= (1600 * 1050))
{
max_kbit_rate = is_var_bitrate ? 2700 : 3700;
}
else if (area <= (1920 * 1088))
{
max_kbit_rate = is_var_bitrate ? 4000 : 6000;
}
else
{
max_kbit_rate = is_var_bitrate ? 6000 : 7000;
}
max_kbit_rate = max_kbit_rate * frame_rate / 25;
if (area <= (320 * 240))
{
if (max_kbit_rate < 150)
max_kbit_rate = 150;
}
else if (area <= (640 * 480))
{
if (max_kbit_rate < 300)
max_kbit_rate = 300;
}
else if (area <= (1280 * 720))
{
if (max_kbit_rate < 600)
max_kbit_rate = 600;
}
else if (area <= (1920 * 1080))
{
if (max_kbit_rate < 960)
max_kbit_rate = 960;
}
else
{
if (max_kbit_rate < 1500)
max_kbit_rate = 1500;
}
return max_kbit_rate;
}
public int CalVideoQuality(int w, int h, bool is_h264)
{
int area = w * h;
int quality = is_h264 ? 23 : 28;
if (area <= (320 * 240))
{
quality = is_h264 ? 23 : 27;
}
else if (area <= (640 * 360))
{
quality = is_h264 ? 25 : 28;
}
else if (area <= (640 * 480))
{
quality = is_h264 ? 25 : 28;
}
else if (area <= (960 * 600))
{
quality = is_h264 ? 26 : 28;
}
else if (area <= (1280 * 720))
{
quality = is_h264 ? 27 : 29;
}
else if (area <= (1600 * 900))
{
quality = is_h264 ? 28 : 30;
}
else if (area <= (1920 * 1080))
{
quality = is_h264 ? 29 : 31;
}
else
{
quality = is_h264 ? 30 : 32;
}
return quality;
}
public int CalVideoEncoderSpeed(int w, int h, bool is_h264)
{
int area = w * h;
if (is_h264)
{
if (area <= (1280 * 720))
{
return 6;
}
else
{
return 2;
}
}
if (area <= (960 * 600))
{
return 3;
}
else if (area <= (1280 * 720))
{
return 2;
}
else
{
return 1;
}
}
public int GetAudioInputDeviceNumber()
{
int auido_devices = 0;
NTSmartPublisherSDK.NT_PB_GetAuidoInputDeviceNumber(ref auido_devices);
return auido_devices;
}
public List<String> GetAudioInputDeviceName(int auido_devices)
{
List<String> audio_device_name = new List<string>();
if (auido_devices > 0)
{
for (int i = 0; i < auido_devices; ++i)
{
byte[] deviceNameBuffer = new byte[512];
string name = "";
if (NTBaseCodeDefine.NT_ERC_OK == NTSmartPublisherSDK.NT_PB_GetAuidoInputDeviceName((uint)i, deviceNameBuffer, 512))
{
int count = 0;
for (int j = 0; j < deviceNameBuffer.Length; ++j)
{
if (deviceNameBuffer[j] != 0)
{
count++;
}
else
{
break;
}
}
if (count > 0)
{
name = Encoding.UTF8.GetString(deviceNameBuffer, 0, count);
}
}
var audio_name = "";
if (name.Length == 0)
{
audio_name = "音频采集设备-";
}
else
{
audio_name = name + "-";
}
audio_name = audio_name + (i + 1);
audio_device_name.Add(name);
}
}
return audio_device_name;
}
public bool IsCanCaptureSpeaker()
{
int is_capture_speaker = 0;
if (NTBaseCodeDefine.NT_ERC_OK == NTSmartPublisherSDK.NT_PB_IsCanCaptureSpeaker(ref is_capture_speaker))
{
if (1 == is_capture_speaker)
{
return true;
}
}
return false;
}
private void SetCommonOptionToPublisherSDK()
{
if (is_empty_handle())
{
Console.Write("SetCommonOptionToPublisherSDK, publisher_handle_ with null..");
return;
}
if (handle_reference_count() > 0)
return;
// 音频相关设置
if (audio_option_ == (UInt32)NTSmartPublisherDefine.NT_PB_E_AUDIO_OPTION.NT_PB_E_AUDIO_OPTION_CAPTURE_MIC ||
audio_option_ == (UInt32)NTSmartPublisherDefine.NT_PB_E_AUDIO_OPTION.NT_PB_E_AUDIO_OPTION_CAPTURE_MIC_SPEAKER_MIXER)
{
int audio_device_number = GetAudioInputDeviceNumber();
if(audio_device_number > 0 )
{
NTSmartPublisherSDK.NT_PB_SetAuidoInputDeviceId(handle_, 0);
}
}
else if (audio_option_ == (UInt32)NTSmartPublisherDefine.NT_PB_E_AUDIO_OPTION.NT_PB_E_AUDIO_OPTION_CAPTURE_SPEAKER ||
audio_option_ == (UInt32)NTSmartPublisherDefine.NT_PB_E_AUDIO_OPTION.NT_PB_E_AUDIO_OPTION_CAPTURE_MIC_SPEAKER_MIXER)
{
if (IsCanCaptureSpeaker())
{
NTSmartPublisherSDK.NT_PB_SetCaptureSpeakerCompensateMute(handle_, 1);
}
}
NTSmartPublisherSDK.NT_PB_SetPublisherAudioCodecType(handle_, 1);
}
public bool OpenPublisherHandle(UInt32 video_option, UInt32 audio_option)
{
if (!is_empty_handle())
return false;
video_option_ = video_option;
audio_option_ = audio_option;
IntPtr handle = IntPtr.Zero;
if (NTBaseCodeDefine.NT_ERC_OK != NTSmartPublisherSDK.NT_PB_Open(out handle, video_option_, audio_option_, 0, IntPtr.Zero))
return false;
if (is_zero_ptr(handle))
return false;
if (null == pb_event_call_back_)
pb_event_call_back_ = new NT_PB_SDKEventCallBack(PbSDKEventCallBack);
NTSmartPublisherSDK.NT_PB_SetEventCallBack(handle, IntPtr.Zero, pb_event_call_back_);
shared_lock_.EnterWriteLock();
try
{
handle_reference_count_ = 0;
handle_ = handle;
}
finally
{
shared_lock_.ExitWriteLock();
}
if (null == set_video_preview_image_callback_)
set_video_preview_image_callback_ = new SetVideoPreviewImageCallBack(VideoPreviewImageCallBack);
return true;
}
public void PbSDKEventCallBack(IntPtr handle, IntPtr user_data,
UInt32 event_id,
Int64 param1,
Int64 param2,
UInt64 param3,
UInt64 param4,
[MarshalAs(UnmanagedType.LPStr)] String param5,
[MarshalAs(UnmanagedType.LPStr)] String param6,
IntPtr param7)
{
if (sync_invoke_ != null)
{
object target = sync_invoke_.Target;
if (target != null)
{
System.ComponentModel.ISynchronizeInvoke sync_invoke_target = target as System.ComponentModel.ISynchronizeInvoke;
if (sync_invoke_target != null)
{
if (sync_invoke_target.InvokeRequired)
{
sync_invoke_target.BeginInvoke(pb_set_event_call_back_, new object[] { event_id, param1, param2, param3, param4, param5, param6, param7 });
}
else
{
pb_set_event_call_back_(event_id, param1, param2, param3, param4, param5, param6, param7);
}
}
}
}
}
//预览数据回调
public void SDKVideoPreviewImageCallBack(IntPtr handle, IntPtr user_data, IntPtr image)
{
NT_PB_Image pb_image = (NT_PB_Image)Marshal.PtrToStructure(image, typeof(NT_PB_Image));
NT_VideoFrame pVideoFrame = new NT_VideoFrame();
pVideoFrame.width_ = pb_image.width_;
pVideoFrame.height_ = pb_image.height_;
pVideoFrame.stride_ = pb_image.stride_[0];
Int32 argb_size = pb_image.stride_[0] * pb_image.height_;
pVideoFrame.plane_ = Marshal.AllocHGlobal(argb_size);
CopyMemory(pVideoFrame.plane_, pb_image.plane_[0], (UInt32)argb_size);
if (sync_invoke_ != null)
{
System.ComponentModel.ISynchronizeInvoke sync_invoke_target = sync_invoke_.Target as System.ComponentModel.ISynchronizeInvoke;
if (sync_invoke_target != null)
{
if (sync_invoke_target.InvokeRequired)
{
sync_invoke_target.BeginInvoke(set_video_preview_image_callback_, new object[] { pVideoFrame });
}
else
{
set_video_preview_image_callback_(pVideoFrame);
}
}
}
}
public void VideoPreviewImageCallBack(NT_VideoFrame frame)
{
if (cur_image_.plane_ != IntPtr.Zero)
{
Marshal.FreeHGlobal(cur_image_.plane_);
cur_image_.plane_ = IntPtr.Zero;
}
cur_image_ = frame;
if (render_wnd_ != null)
{
render_wnd_.Invalidate();
}
}
public List<CameraInfo> GetCameraInfos()
{
List<CameraInfo> cameras = new List<CameraInfo>();
int device_number = 0;
if (NTBaseCodeDefine.NT_ERC_OK != NTSmartPublisherSDK.NT_PB_GetVideoCaptureDeviceNumber(ref device_number))
{
return cameras;
}
if (device_number < 1)
{
return cameras;
}
for (int i = 0; i < device_number; ++i)
{
CameraInfo info = new CameraInfo();
info.capabilities_ = new List<NT_PB_VideoCaptureCapability>();
StringBuilder name = new StringBuilder(256);
StringBuilder id = new StringBuilder(1024);
if (NTBaseCodeDefine.NT_ERC_OK != NTSmartPublisherSDK.NT_PB_GetVideoCaptureDeviceInfo(i,
name, 256,
id, 1024))
{
continue;
}
info.name_ = name.ToString();
info.id_ = id.ToString();
int capability_number = 0;
if (NTBaseCodeDefine.NT_ERC_OK != NTSmartPublisherSDK.NT_PB_GetVideoCaptureDeviceCapabilityNumber(
id.ToString(), ref capability_number))
{
continue;
}
bool is_failed = false;
for (int j = 0; j < capability_number; ++j)
{
NT_PB_VideoCaptureCapability capability = new NT_PB_VideoCaptureCapability();
if (NTBaseCodeDefine.NT_ERC_OK != NTSmartPublisherSDK.NT_PB_GetVideoCaptureDeviceCapability(
id.ToString(), j, ref capability))
{
is_failed = true;
break;
}
info.capabilities_.Add(capability);
}
if (!is_failed)
{
cameras.Add(info);
}
}
return cameras;
}
public bool StartPreview()
{
if (is_empty_handle() || is_previewing())
return false;
video_preview_image_callback_ = new NT_PB_SDKVideoPreviewImageCallBack(SDKVideoPreviewImageCallBack);
NTSmartPublisherSDK.NT_PB_SetVideoPreviewImageCallBack(handle_, (int)NTSmartPublisherDefine.NT_PB_E_IMAGE_FORMAT.NT_PB_E_IMAGE_FORMAT_RGB32, IntPtr.Zero, video_preview_image_callback_);
if (NTBaseCodeDefine.NT_ERC_OK != NTSmartPublisherSDK.NT_PB_StartPreview(handle_, 0x800000, IntPtr.Zero))
{
try_close_handle();
return false;
}
shared_lock_.EnterWriteLock();
try
{
handle_reference_count_++;
is_previewing_ = true;
}
finally
{
shared_lock_.ExitWriteLock();
}
return true;
}
public void StopPreview()
{
if (is_empty_handle() || !is_previewing())
return;
shared_lock_.EnterWriteLock();
try
{
is_previewing_ = false;
handle_reference_count_--;
}
finally
{
shared_lock_.ExitWriteLock();
}
NTSmartPublisherSDK.NT_PB_StopPreview(handle_);
try_close_handle();
if (render_wnd_ != null)
render_wnd_.Invalidate();
}
public bool StartPublisher(String url)
{
if (is_empty_handle() || is_rtmp_publishing())
return false;
SetCommonOptionToPublisherSDK();
if (!String.IsNullOrEmpty(url))
NTSmartPublisherSDK.NT_PB_SetURL(handle_, url, IntPtr.Zero);
if (NTBaseCodeDefine.NT_ERC_OK != NTSmartPublisherSDK.NT_PB_StartPublisher(handle_, IntPtr.Zero))
{
try_close_handle();
return false;
}
shared_lock_.EnterWriteLock();
try
{
handle_reference_count_++;
is_rtmp_publishing_ = true;
}
finally
{
shared_lock_.ExitWriteLock();
}
return true;
}
public void StopPublisher()
{
if (is_empty_handle() || !is_rtmp_publishing())
return;
shared_lock_.EnterWriteLock();
try
{
is_rtmp_publishing_ = false;
handle_reference_count_--;
}
finally
{
shared_lock_.ExitWriteLock();
}
NTSmartPublisherSDK.NT_PB_StopPublisher(handle_);
try_close_handle();
}
public bool StartRecorder()
{
if (is_empty_handle() || is_recording())
return false;
//string edit_rec_dir = "D:\\dntest";
string edit_rec_dir = "D:\\推送端录像\\上海站";
if (String.IsNullOrEmpty(edit_rec_dir))
{
Console.WriteLine("请设置录像目录");
return false;
}
uint ret = NTSmartPublisherSDK.NT_PB_SetRecorderDirectoryW(handle_, edit_rec_dir, IntPtr.Zero);
if (NTBaseCodeDefine.NT_ERC_OK != ret)
{
try_close_handle();
return false;
}
uint rec_max_file_size = 512 * 1024;
NTSmartPublisherSDK.NT_PB_SetRecorderFileMaxSize(handle_, rec_max_file_size);
NT_PB_RecorderFileNameRuler rec_name_ruler = new NT_PB_RecorderFileNameRuler();
String rec_file_name_prefix_ = "transcode-rec";
rec_name_ruler.file_name_prefix_ = rec_file_name_prefix_.ToString();
rec_name_ruler.append_date_ = 1;
rec_name_ruler.append_time_ = 1;
NTSmartPublisherSDK.NT_PB_SetRecorderFileNameRuler(handle_, ref rec_name_ruler);
if (NTBaseCodeDefine.NT_ERC_OK != NTSmartPublisherSDK.NT_PB_StartRecorder(handle_, IntPtr.Zero))
{
try_close_handle();
return false;
}
shared_lock_.EnterWriteLock();
try
{
handle_reference_count_++;
is_recording_ = true;
}
finally
{
shared_lock_.ExitWriteLock();
}
return true;
}
public UInt32 PauseRecorder(bool is_pause)
{
if (is_empty_handle() || !is_recording())
return NTBaseCodeDefine.NT_ERC_FAILED;
UInt32 ret = NTBaseCodeDefine.NT_ERC_OK;
if (is_pause)
{
ret = NTSmartPublisherSDK.NT_PB_PauseRecorder(handle_, 1);
if ((UInt32)NT.NTSmartPublisherDefine.NT_PB_E_ERROR_CODE.NT_ERC_PB_NEED_RETRY == ret)
{
Console.WriteLine("暂停录像失败, 请重新尝试!");
return ret;
}
else if (NTBaseCodeDefine.NT_ERC_OK == ret)
{
//btn_pause_rec.Text = "恢复录像";
}
}
else
{
ret = NTSmartPublisherSDK.NT_PB_PauseRecorder(handle_, 0);
if ((UInt32)NT.NTSmartPublisherDefine.NT_PB_E_ERROR_CODE.NT_ERC_PB_NEED_RETRY == ret)
{
Console.WriteLine("恢复录像失败, 请重新尝试!");
return ret;
}
else if (NTBaseCodeDefine.NT_ERC_OK == ret)
{
//btn_pause_rec.Text = "暂停录像";
}
}
return ret;
}
public void StopRecorder()
{
if (is_empty_handle() || !is_recording())
return;
shared_lock_.EnterWriteLock();
try
{
is_recording_ = false;
handle_reference_count_--;
}
finally
{
shared_lock_.ExitWriteLock();
}
NTSmartPublisherSDK.NT_PB_StopRecorder(handle_);
try_close_handle();
}
public bool is_previewing() { return is_previewing_; }
public bool is_rtmp_publishing() { return is_rtmp_publishing_; }
public bool is_recording() { return is_recording_; }
public bool IsRTSPSerivceRunning()
{
return is_rtsp_service_running_;
}
public bool is_rtsp_publishing() { return is_rtsp_publishing_; }
public static bool is_zero_ptr(IntPtr ptr) { return IntPtr.Zero == ptr; }
public bool is_empty_handle() { return is_zero_ptr(handle_); }
public int handle_reference_count() { return handle_reference_count_; }
private bool is_running()
{
if (is_empty_handle())
return false;
return is_rtmp_publishing_ || is_recording_ || is_rtsp_publishing_ || is_previewing_;
}
private bool is_audio_running()
{
if (is_empty_handle())
return false;
return is_rtmp_publishing_ || is_recording_ || is_rtsp_publishing_;
}
private bool is_video_running()
{
if (is_empty_handle())
return false;
return is_rtmp_publishing_ || is_recording_ || is_rtsp_publishing_;
}
public void post_audio_pcm_data(IntPtr data, UInt32 size, UInt64 timestamp,
Int32 sample_rate, Int32 channels, Int32 per_channel_sample_number)
{
if (is_zero_ptr(data) || size < 1)
return;
if (!is_audio_running())
return;
if (shared_lock_.TryEnterReadLock(enter_read_lock_timeout_ms_))
{
try
{
if (is_audio_running())
NTSmartPublisherSDK.NT_PB_PostAudioPCMData(handle_, data, size, timestamp, sample_rate, channels, per_channel_sample_number);
}
finally
{
shared_lock_.ExitReadLock();
}
}
}
public void OnVideoDataHandle(IntPtr handle, IntPtr user_data,
UInt32 video_codec_id, IntPtr data, UInt32 size,
IntPtr info, IntPtr reserve)
{
if (is_zero_ptr(data) || size < 1)
return;
if (!is_video_running())
return;
if (shared_lock_.TryEnterReadLock(enter_read_lock_timeout_ms_))
{
try
{
if (is_video_running())
{
NT_SP_PullStreamVideoDataInfo video_info = (NT_SP_PullStreamVideoDataInfo)Marshal.PtrToStructure(info, typeof(NT_SP_PullStreamVideoDataInfo));
NTSmartPublisherSDK.NT_PB_PostVideoEncodedDataV2(handle_, video_codec_id,
data, size, video_info.is_key_frame_, video_info.timestamp_, video_info.presentation_timestamp_);
}
}
finally
{
shared_lock_.ExitReadLock();
}
}
}
public void OnAudioDataHandle(IntPtr handle, IntPtr user_data,
UInt32 audio_codec_id, IntPtr data, UInt32 size,
IntPtr info, IntPtr reserve)
{
if (is_zero_ptr(data) || size < 1)
return;
if (!is_audio_running())
return;
if (shared_lock_.TryEnterReadLock(enter_read_lock_timeout_ms_))
{
try
{
if (is_audio_running() && audio_option_ == (UInt32)NT.NTSmartPublisherDefine.NT_PB_E_AUDIO_OPTION.NT_PB_E_AUDIO_OPTION_ENCODED_DATA)
{
NT_SP_PullStreamAuidoDataInfo audio_info = (NT_SP_PullStreamAuidoDataInfo)Marshal.PtrToStructure(info, typeof(NT_SP_PullStreamAuidoDataInfo));
NTSmartPublisherSDK.NT_PB_PostAudioEncodedData(handle_, audio_codec_id, data, size,
audio_info.is_key_frame_, audio_info.timestamp_,
audio_info.parameter_info_, audio_info.parameter_info_size_);
}
}
finally
{
shared_lock_.ExitReadLock();
}
}
}
public bool enable_layer(int index, bool enable)
{
if (index < 1)
return false;
if (is_empty_handle())
return false;
return NTBaseCodeDefine.NT_ERC_OK == NTSmartPublisherSDK.NT_PB_EnableLayer(handle_, 0, index, enable ? 1 : 0);
}
public bool update_layer_region(int index, int x, int y, int w, int h)
{
if (index < 1)
return false;
if (is_empty_handle())
return false;
NT_PB_RectRegion region = new NT_PB_RectRegion();
region.x_ = x;
region.y_ = y;
region.width_ = w;
region.height_ = h;
return NTBaseCodeDefine.NT_ERC_OK == NTSmartPublisherSDK.NT_PB_UpdateLayerRegion(handle_, 0, index, ref region);
}
private bool add_layer_config(object layer_config, int layer_type)
{
IntPtr layer_ptr = Marshal.AllocHGlobal(Marshal.SizeOf(layer_config));
Marshal.StructureToPtr(layer_config, layer_ptr, false);
UInt32 ret = NTSmartPublisherSDK.NT_PB_AddLayerConfig(handle_, 0,
layer_ptr, layer_type, 0, IntPtr.Zero);
Marshal.FreeHGlobal(layer_ptr);
return NTBaseCodeDefine.NT_ERC_OK == ret;
}
private void fill_layer_base(object layer, out NT_PB_LayerBaseConfig layer_base, int type, int index, bool enable, int x, int y, int w, int h)
{
layer_base.type_ = type;
layer_base.index_ = index;
layer_base.enable_ = enable ? 1 : 0;
layer_base.region_.x_ = 0;
layer_base.region_.y_ = 0;
layer_base.region_.width_ = w;
layer_base.region_.height_ = h;
layer_base.offset_ = Marshal.OffsetOf(layer.GetType(), "base_").ToInt32();
layer_base.cb_size_ = (uint)Marshal.SizeOf(layer);
}
public bool config_layers(bool is_add_rgbx_zero_layer)
{
if (video_option_ != (uint)NTSmartPublisherDefine.NT_PB_E_VIDEO_OPTION.NT_PB_E_VIDEO_OPTION_LAYER)
return false;
if (is_empty_handle())
return false;
int w = video_width_;
int h = video_height_;
if ((w & 0x1) != 0)
--w;
if ((h & 0x1) != 0)
--h;
if (w < 2 || h < 2)
return false;
NTSmartPublisherSDK.NT_PB_ClearLayersConfig(handle_, 0, 0, IntPtr.Zero);
int type, index = 0;
if (is_add_rgbx_zero_layer)
{
NT_PB_RGBARectangleLayerConfig rgba_layer = new NT_PB_RGBARectangleLayerConfig();
type = (Int32)NTSmartPublisherDefine.NT_PB_E_LAYER_TYPE.NT_PB_E_LAYER_TYPE_RGBA_RECTANGLE;
fill_layer_base(rgba_layer, out rgba_layer.base_, type, index, true, 0, 0, w, h);
rgba_layer.red_ = 0;
rgba_layer.green_ = 0;
rgba_layer.blue_ = 0;
rgba_layer.alpha_ = 255;
if (add_layer_config(rgba_layer, type))
index++;
}
NT_PB_ExternalVideoFrameLayerConfig external_video_layer = new NT_PB_ExternalVideoFrameLayerConfig();
type = (Int32)NTSmartPublisherDefine.NT_PB_E_LAYER_TYPE.NT_PB_E_LAYER_TYPE_EXTERNAL_VIDEO_FRAME;
fill_layer_base(external_video_layer, out external_video_layer.base_, type, index, true, 0, 0, w, h);
if (add_layer_config(external_video_layer, type))
external_video_layer_index_ = index++;
//叠加的文本层
NT_PB_ExternalVideoFrameLayerConfig text_layer = new NT_PB_ExternalVideoFrameLayerConfig();
type = (Int32)NTSmartPublisherDefine.NT_PB_E_LAYER_TYPE.NT_PB_E_LAYER_TYPE_EXTERNAL_VIDEO_FRAME;
fill_layer_base(text_layer, out text_layer.base_, type, index, false, 0, 0, 64, 64);
if (add_layer_config(text_layer, type))
text_layer_index_ = index++;
return index > 0;
}
public int get_external_video_layer_index() { return external_video_layer_index_; }
public int get_text_layer_index() { return text_layer_index_; }
public void SetVideoCaptureDeviceBaseParameter(String camera_id, UInt32 width, UInt32 height)
{
NTSmartPublisherSDK.NT_PB_SetVideoCaptureDeviceBaseParameter(handle_, camera_id, width, height);
}
public void SetFrameRate(UInt32 frame_rate)
{
NTSmartPublisherSDK.NT_PB_SetFrameRate(handle_, frame_rate);
}
public void SetVideoEncoder(Int32 type, Int32 encoder_id, UInt32 codec_id, Int32 param1)
{
NTSmartPublisherSDK.NT_PB_SetVideoEncoder(handle_, type, encoder_id, codec_id, param1);
}
public void SetVideoQualityV2(Int32 quality)
{
NTSmartPublisherSDK.NT_PB_SetVideoQualityV2(handle_, quality);
}
public void SetVideoMaxBitRate(Int32 kbit_rate)
{
NTSmartPublisherSDK.NT_PB_SetVideoMaxBitRate(handle_, kbit_rate);
}
public void SetVideoBitRate(Int32 kbit_rate)
{
NTSmartPublisherSDK.NT_PB_SetVideoBitRate(handle_, kbit_rate);
}
public void SetVideoKeyFrameInterval(Int32 interval)
{
NTSmartPublisherSDK.NT_PB_SetVideoKeyFrameInterval(handle_, interval);
}
public void SetVideoEncoderProfile(Int32 profile)
{
NTSmartPublisherSDK.NT_PB_SetVideoEncoderProfile(handle_, profile);
}
public void SetVideoEncoderSpeed(Int32 speed)
{
NTSmartPublisherSDK.NT_PB_SetVideoEncoderSpeed(handle_, speed);
}
public void ClearVideoEncoderSpecialOptions()
{
// 清除编码器所有的特定的参数
NTSmartPublisherSDK.NT_PB_ClearVideoEncoderSpecialOptions(handle_);
}
public void SetVideoEncoderQPMax(Int32 qp_max)
{
NTSmartPublisherSDK.NT_PB_SetVideoEncoderQPMax(handle_, qp_max);
}
public void SetVideoEncoderQPMin(Int32 qp_min)
{
NTSmartPublisherSDK.NT_PB_SetVideoEncoderQPMin(handle_, qp_min);
}
public void SetVideoEncoderSpecialInt32Option(String option_name, Int32 option_value)
{
NTSmartPublisherSDK.NT_PB_SetVideoEncoderSpecialInt32Option(handle_, option_name, option_value);
}
public void SetAuidoInputDeviceId(UInt32 device_id)
{
NTSmartPublisherSDK.NT_PB_SetAuidoInputDeviceId(handle_, device_id);
}
public void SetPublisherAudioCodecType(Int32 type)
{
NTSmartPublisherSDK.NT_PB_SetPublisherAudioCodecType(handle_, type);
}
public void SetPublisherMute(bool is_mute)
{
NTSmartPublisherSDK.NT_PB_SetMute(handle_, is_mute ? 1 : 0);
}
public void SetEchoCancellation(Int32 isCancel, Int32 delay)
{
NTSmartPublisherSDK.NT_PB_SetEchoCancellation(handle_, isCancel, delay);
}
public void SetNoiseSuppression(Int32 isNS)
{
NTSmartPublisherSDK.NT_PB_SetNoiseSuppression(handle_, isNS);
}
public void SetAGC(Int32 isAGC)
{
NTSmartPublisherSDK.NT_PB_SetAGC(handle_, isAGC);
}
public void SetVAD(Int32 isVAD)
{
NTSmartPublisherSDK.NT_PB_SetVAD(handle_, isVAD);
}
public void SetInputAudioVolume(float audio_input_volume)
{
NTSmartPublisherSDK.NT_PB_SetInputAudioVolume(handle_, 0, audio_input_volume);
}
public bool StartRtspService()
{
if (NTBaseCodeDefine.NT_ERC_OK != NTSmartPublisherSDK.NT_PB_OpenRtspServer(ref rtsp_handle_, 0))
{
Console.WriteLine("创建rtsp server实例失败! 请检查sdk有效性.");
return false;
}
if (IntPtr.Zero == rtsp_handle_)
{
Console.WriteLine("创建rtsp server实例失败! 请检查sdk有效性.");
return false;
}
int port = 28554;
if (NTBaseCodeDefine.NT_ERC_OK != NTSmartPublisherSDK.NT_PB_SetRtspServerPort(rtsp_handle_, port))
{
NTSmartPublisherSDK.NT_PB_CloseRtspServer(rtsp_handle_);
rtsp_handle_ = IntPtr.Zero;
Console.WriteLine("设置rtsp server端口失败,请检查端口是否重复或者端口不在范围内!");
return false;
}
//String user_name = "admin";
//String password = "123456";
//NTSmartPublisherSDK.NT_PB_SetRtspServerUserNamePassword(rtsp_handle, user_name, password);
if (NTBaseCodeDefine.NT_ERC_OK == NTSmartPublisherSDK.NT_PB_StartRtspServer(rtsp_handle_, 0))
{
Console.WriteLine("StartRtspServer suc..");
}
else
{
NTSmartPublisherSDK.NT_PB_CloseRtspServer(rtsp_handle_);
rtsp_handle_ = IntPtr.Zero;
Console.WriteLine("启动rtsp server失败, 请检查设置的端口是否被占用!");
return false;
}
is_rtsp_service_running_ = true;
return true;
}
public void StopRtspService()
{
NTSmartPublisherSDK.NT_PB_StopRtspServer(rtsp_handle_);
NTSmartPublisherSDK.NT_PB_CloseRtspServer(rtsp_handle_);
rtsp_handle_ = IntPtr.Zero;
is_rtsp_service_running_ = false;
}
public bool StartRtspStream()
{
if (is_empty_handle() || is_rtsp_publishing())
return false;
String rtsp_stream_name = "stream1";
NTSmartPublisherSDK.NT_PB_SetRtspStreamName(handle_, rtsp_stream_name);
NTSmartPublisherSDK.NT_PB_ClearRtspStreamServer(handle_);
NTSmartPublisherSDK.NT_PB_AddRtspStreamServer(handle_, rtsp_handle_, 0);
if (NTBaseCodeDefine.NT_ERC_OK != NTSmartPublisherSDK.NT_PB_StartRtspStream(handle_, 0))
{
try_close_handle();
return false;
}
shared_lock_.EnterWriteLock();
try
{
handle_reference_count_++;
is_rtsp_publishing_ = true;
}
finally
{
shared_lock_.ExitWriteLock();
}
return true;
}
public void StopRtspStream()
{
if (is_empty_handle() || !is_rtsp_publishing())
return;
shared_lock_.EnterWriteLock();
try
{
is_rtsp_publishing_ = false;
handle_reference_count_--;
}
finally
{
shared_lock_.ExitWriteLock();
}
NTSmartPublisherSDK.NT_PB_StopRtspStream(handle_);
try_close_handle();
}
public int GetRtspSessionNumbers()
{
int num = 0;
if (rtsp_handle_ != IntPtr.Zero)
{
if (NTBaseCodeDefine.NT_ERC_OK != NTSmartPublisherSDK.NT_PB_GetRtspServerClientSessionNumbers(rtsp_handle_, ref num))
{
Console.WriteLine("Call NT_PB_GetRtspServerClientSessionNumbers failed..");
}
}
return num;
}
public void try_close_handle()
{
if (is_empty_handle() || handle_reference_count_ > 0)
return;
IntPtr handle = IntPtr.Zero;
shared_lock_.EnterWriteLock();
try
{
handle = handle_;
handle_ = IntPtr.Zero;
}
finally
{
shared_lock_.ExitWriteLock();
}
if (!is_zero_ptr(handle))
NTSmartPublisherSDK.NT_PB_Close(handle);
}
public void close()
{
if (is_empty_handle())
return;
IntPtr handle = IntPtr.Zero;
shared_lock_.EnterWriteLock();
try
{
handle = handle_;
handle_ = IntPtr.Zero;
}
finally
{
shared_lock_.ExitWriteLock();
}
if (!is_zero_ptr(handle))
NTSmartPublisherSDK.NT_PB_Close(handle);
}
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
- 58.
- 59.
- 60.
- 61.
- 62.
- 63.
- 64.
- 65.
- 66.
- 67.
- 68.
- 69.
- 70.
- 71.
- 72.
- 73.
- 74.
- 75.
- 76.
- 77.
- 78.
- 79.
- 80.
- 81.
- 82.
- 83.
- 84.
- 85.
- 86.
- 87.
- 88.
- 89.
- 90.
- 91.
- 92.
- 93.
- 94.
- 95.
- 96.
- 97.
- 98.
- 99.
- 100.
- 101.
- 102.
- 103.
- 104.
- 105.
- 106.
- 107.
- 108.
- 109.
- 110.
- 111.
- 112.
- 113.
- 114.
- 115.
- 116.
- 117.
- 118.
- 119.
- 120.
- 121.
- 122.
- 123.
- 124.
- 125.
- 126.
- 127.
- 128.
- 129.
- 130.
- 131.
- 132.
- 133.
- 134.
- 135.
- 136.
- 137.
- 138.
- 139.
- 140.
- 141.
- 142.
- 143.
- 144.
- 145.
- 146.
- 147.
- 148.
- 149.
- 150.
- 151.
- 152.
- 153.
- 154.
- 155.
- 156.
- 157.
- 158.
- 159.
- 160.
- 161.
- 162.
- 163.
- 164.
- 165.
- 166.
- 167.
- 168.
- 169.
- 170.
- 171.
- 172.
- 173.
- 174.
- 175.
- 176.
- 177.
- 178.
- 179.
- 180.
- 181.
- 182.
- 183.
- 184.
- 185.
- 186.
- 187.
- 188.
- 189.
- 190.
- 191.
- 192.
- 193.
- 194.
- 195.
- 196.
- 197.
- 198.
- 199.
- 200.
- 201.
- 202.
- 203.
- 204.
- 205.
- 206.
- 207.
- 208.
- 209.
- 210.
- 211.
- 212.
- 213.
- 214.
- 215.
- 216.
- 217.
- 218.
- 219.
- 220.
- 221.
- 222.
- 223.
- 224.
- 225.
- 226.
- 227.
- 228.
- 229.
- 230.
- 231.
- 232.
- 233.
- 234.
- 235.
- 236.
- 237.
- 238.
- 239.
- 240.
- 241.
- 242.
- 243.
- 244.
- 245.
- 246.
- 247.
- 248.
- 249.
- 250.
- 251.
- 252.
- 253.
- 254.
- 255.
- 256.
- 257.
- 258.
- 259.
- 260.
- 261.
- 262.
- 263.
- 264.
- 265.
- 266.
- 267.
- 268.
- 269.
- 270.
- 271.
- 272.
- 273.
- 274.
- 275.
- 276.
- 277.
- 278.
- 279.
- 280.
- 281.
- 282.
- 283.
- 284.
- 285.
- 286.
- 287.
- 288.
- 289.
- 290.
- 291.
- 292.
- 293.
- 294.
- 295.
- 296.
- 297.
- 298.
- 299.
- 300.
- 301.
- 302.
- 303.
- 304.
- 305.
- 306.
- 307.
- 308.
- 309.
- 310.
- 311.
- 312.
- 313.
- 314.
- 315.
- 316.
- 317.
- 318.
- 319.
- 320.
- 321.
- 322.
- 323.
- 324.
- 325.
- 326.
- 327.
- 328.
- 329.
- 330.
- 331.
- 332.
- 333.
- 334.
- 335.
- 336.
- 337.
- 338.
- 339.
- 340.
- 341.
- 342.
- 343.
- 344.
- 345.
- 346.
- 347.
- 348.
- 349.
- 350.
- 351.
- 352.
- 353.
- 354.
- 355.
- 356.
- 357.
- 358.
- 359.
- 360.
- 361.
- 362.
- 363.
- 364.
- 365.
- 366.
- 367.
- 368.
- 369.
- 370.
- 371.
- 372.
- 373.
- 374.
- 375.
- 376.
- 377.
- 378.
- 379.
- 380.
- 381.
- 382.
- 383.
- 384.
- 385.
- 386.
- 387.
- 388.
- 389.
- 390.
- 391.
- 392.
- 393.
- 394.
- 395.
- 396.
- 397.
- 398.
- 399.
- 400.
- 401.
- 402.
- 403.
- 404.
- 405.
- 406.
- 407.
- 408.
- 409.
- 410.
- 411.
- 412.
- 413.
- 414.
- 415.
- 416.
- 417.
- 418.
- 419.
- 420.
- 421.
- 422.
- 423.
- 424.
- 425.
- 426.
- 427.
- 428.
- 429.
- 430.
- 431.
- 432.
- 433.
- 434.
- 435.
- 436.
- 437.
- 438.
- 439.
- 440.
- 441.
- 442.
- 443.
- 444.
- 445.
- 446.
- 447.
- 448.
- 449.
- 450.
- 451.
- 452.
- 453.
- 454.
- 455.
- 456.
- 457.
- 458.
- 459.
- 460.
- 461.
- 462.
- 463.
- 464.
- 465.
- 466.
- 467.
- 468.
- 469.
- 470.
- 471.
- 472.
- 473.
- 474.
- 475.
- 476.
- 477.
- 478.
- 479.
- 480.
- 481.
- 482.
- 483.
- 484.
- 485.
- 486.
- 487.
- 488.
- 489.
- 490.
- 491.
- 492.
- 493.
- 494.
- 495.
- 496.
- 497.
- 498.
- 499.
- 500.
- 501.
- 502.
- 503.
- 504.
- 505.
- 506.
- 507.
- 508.
- 509.
- 510.
- 511.
- 512.
- 513.
- 514.
- 515.
- 516.
- 517.
- 518.
- 519.
- 520.
- 521.
- 522.
- 523.
- 524.
- 525.
- 526.
- 527.
- 528.
- 529.
- 530.
- 531.
- 532.
- 533.
- 534.
- 535.
- 536.
- 537.
- 538.
- 539.
- 540.
- 541.
- 542.
- 543.
- 544.
- 545.
- 546.
- 547.
- 548.
- 549.
- 550.
- 551.
- 552.
- 553.
- 554.
- 555.
- 556.
- 557.
- 558.
- 559.
- 560.
- 561.
- 562.
- 563.
- 564.
- 565.
- 566.
- 567.
- 568.
- 569.
- 570.
- 571.
- 572.
- 573.
- 574.
- 575.
- 576.
- 577.
- 578.
- 579.
- 580.
- 581.
- 582.
- 583.
- 584.
- 585.
- 586.
- 587.
- 588.
- 589.
- 590.
- 591.
- 592.
- 593.
- 594.
- 595.
- 596.
- 597.
- 598.
- 599.
- 600.
- 601.
- 602.
- 603.
- 604.
- 605.
- 606.
- 607.
- 608.
- 609.
- 610.
- 611.
- 612.
- 613.
- 614.
- 615.
- 616.
- 617.
- 618.
- 619.
- 620.
- 621.
- 622.
- 623.
- 624.
- 625.
- 626.
- 627.
- 628.
- 629.
- 630.
- 631.
- 632.
- 633.
- 634.
- 635.
- 636.
- 637.
- 638.
- 639.
- 640.
- 641.
- 642.
- 643.
- 644.
- 645.
- 646.
- 647.
- 648.
- 649.
- 650.
- 651.
- 652.
- 653.
- 654.
- 655.
- 656.
- 657.
- 658.
- 659.
- 660.
- 661.
- 662.
- 663.
- 664.
- 665.
- 666.
- 667.
- 668.
- 669.
- 670.
- 671.
- 672.
- 673.
- 674.
- 675.
- 676.
- 677.
- 678.
- 679.
- 680.
- 681.
- 682.
- 683.
- 684.
- 685.
- 686.
- 687.
- 688.
- 689.
- 690.
- 691.
- 692.
- 693.
- 694.
- 695.
- 696.
- 697.
- 698.
- 699.
- 700.
- 701.
- 702.
- 703.
- 704.
- 705.
- 706.
- 707.
- 708.
- 709.
- 710.
- 711.
- 712.
- 713.
- 714.
- 715.
- 716.
- 717.
- 718.
- 719.
- 720.
- 721.
- 722.
- 723.
- 724.
- 725.
- 726.
- 727.
- 728.
- 729.
- 730.
- 731.
- 732.
- 733.
- 734.
- 735.
- 736.
- 737.
- 738.
- 739.
- 740.
- 741.
- 742.
- 743.
- 744.
- 745.
- 746.
- 747.
- 748.
- 749.
- 750.
- 751.
- 752.
- 753.
- 754.
- 755.
- 756.
- 757.
- 758.
- 759.
- 760.
- 761.
- 762.
- 763.
- 764.
- 765.
- 766.
- 767.
- 768.
- 769.
- 770.
- 771.
- 772.
- 773.
- 774.
- 775.
- 776.
- 777.
- 778.
- 779.
- 780.
- 781.
- 782.
- 783.
- 784.
- 785.
- 786.
- 787.
- 788.
- 789.
- 790.
- 791.
- 792.
- 793.
- 794.
- 795.
- 796.
- 797.
- 798.
- 799.
- 800.
- 801.
- 802.
- 803.
- 804.
- 805.
- 806.
- 807.
- 808.
- 809.
- 810.
- 811.
- 812.
- 813.
- 814.
- 815.
- 816.
- 817.
- 818.
- 819.
- 820.
- 821.
- 822.
- 823.
- 824.
- 825.
- 826.
- 827.
- 828.
- 829.
- 830.
- 831.
- 832.
- 833.
- 834.
- 835.
- 836.
- 837.
- 838.
- 839.
- 840.
- 841.
- 842.
- 843.
- 844.
- 845.
- 846.
- 847.
- 848.
- 849.
- 850.
- 851.
- 852.
- 853.
- 854.
- 855.
- 856.
- 857.
- 858.
- 859.
- 860.
- 861.
- 862.
- 863.
- 864.
- 865.
- 866.
- 867.
- 868.
- 869.
- 870.
- 871.
- 872.
- 873.
- 874.
- 875.
- 876.
- 877.
- 878.
- 879.
- 880.
- 881.
- 882.
- 883.
- 884.
- 885.
- 886.
- 887.
- 888.
- 889.
- 890.
- 891.
- 892.
- 893.
- 894.
- 895.
- 896.
- 897.
- 898.
- 899.
- 900.
- 901.
- 902.
- 903.
- 904.
- 905.
- 906.
- 907.
- 908.
- 909.
- 910.
- 911.
- 912.
- 913.
- 914.
- 915.
- 916.
- 917.
- 918.
- 919.
- 920.
- 921.
- 922.
- 923.
- 924.
- 925.
- 926.
- 927.
- 928.
- 929.
- 930.
- 931.
- 932.
- 933.
- 934.
- 935.
- 936.
- 937.
- 938.
- 939.
- 940.
- 941.
- 942.
- 943.
- 944.
- 945.
- 946.
- 947.
- 948.
- 949.
- 950.
- 951.
- 952.
- 953.
- 954.
- 955.
- 956.
- 957.
- 958.
- 959.
- 960.
- 961.
- 962.
- 963.
- 964.
- 965.
- 966.
- 967.
- 968.
- 969.
- 970.
- 971.
- 972.
- 973.
- 974.
- 975.
- 976.
- 977.
- 978.
- 979.
- 980.
- 981.
- 982.
- 983.
- 984.
- 985.
- 986.
- 987.
- 988.
- 989.
- 990.
- 991.
- 992.
- 993.
- 994.
- 995.
- 996.
- 997.
- 998.
- 999.
- 1000.
- 1001.
- 1002.
- 1003.
- 1004.
- 1005.
- 1006.
- 1007.
- 1008.
- 1009.
- 1010.
- 1011.
- 1012.
- 1013.
- 1014.
- 1015.
- 1016.
- 1017.
- 1018.
- 1019.
- 1020.
- 1021.
- 1022.
- 1023.
- 1024.
- 1025.
- 1026.
- 1027.
- 1028.
- 1029.
- 1030.
- 1031.
- 1032.
- 1033.
- 1034.
- 1035.
- 1036.
- 1037.
- 1038.
- 1039.
- 1040.
- 1041.
- 1042.
- 1043.
- 1044.
- 1045.
- 1046.
- 1047.
- 1048.
- 1049.
- 1050.
- 1051.
- 1052.
- 1053.
- 1054.
- 1055.
- 1056.
- 1057.
- 1058.
- 1059.
- 1060.
- 1061.
- 1062.
- 1063.
- 1064.
- 1065.
- 1066.
- 1067.
- 1068.
- 1069.
- 1070.
- 1071.
- 1072.
- 1073.
- 1074.
- 1075.
- 1076.
- 1077.
- 1078.
- 1079.
- 1080.
- 1081.
- 1082.
- 1083.
- 1084.
- 1085.
- 1086.
- 1087.
- 1088.
- 1089.
- 1090.
- 1091.
- 1092.
- 1093.
- 1094.
- 1095.
- 1096.
- 1097.
- 1098.
- 1099.
- 1100.
- 1101.
- 1102.
- 1103.
- 1104.
- 1105.
- 1106.
- 1107.
- 1108.
- 1109.
- 1110.
- 1111.
- 1112.
- 1113.
- 1114.
- 1115.
- 1116.
- 1117.
- 1118.
- 1119.
- 1120.
- 1121.
- 1122.
- 1123.
- 1124.
- 1125.
- 1126.
- 1127.
- 1128.
- 1129.
- 1130.
- 1131.
- 1132.
- 1133.
- 1134.
- 1135.
- 1136.
- 1137.
- 1138.
- 1139.
- 1140.
- 1141.
- 1142.
- 1143.
- 1144.
- 1145.
- 1146.
- 1147.
- 1148.
- 1149.
- 1150.
- 1151.
- 1152.
- 1153.
- 1154.
- 1155.
- 1156.
- 1157.
- 1158.
- 1159.
- 1160.
- 1161.
- 1162.
- 1163.
- 1164.
- 1165.
- 1166.
- 1167.
- 1168.
- 1169.
- 1170.
- 1171.
- 1172.
- 1173.
- 1174.
- 1175.
- 1176.
- 1177.
- 1178.
- 1179.
- 1180.
- 1181.
- 1182.
- 1183.
- 1184.
- 1185.
- 1186.
- 1187.
- 1188.
- 1189.
- 1190.
- 1191.
- 1192.
- 1193.
- 1194.
- 1195.
- 1196.
- 1197.
- 1198.
- 1199.
- 1200.
- 1201.
- 1202.
- 1203.
- 1204.
- 1205.
- 1206.
- 1207.
- 1208.
- 1209.
- 1210.
- 1211.
- 1212.
- 1213.
- 1214.
- 1215.
- 1216.
- 1217.
- 1218.
- 1219.
- 1220.
- 1221.
- 1222.
- 1223.
- 1224.
- 1225.
- 1226.
- 1227.
- 1228.
- 1229.
- 1230.
- 1231.
- 1232.
- 1233.
- 1234.
- 1235.
- 1236.
- 1237.
- 1238.
- 1239.
- 1240.
- 1241.
- 1242.
- 1243.
- 1244.
- 1245.
- 1246.
- 1247.
- 1248.
- 1249.
- 1250.
- 1251.
- 1252.
- 1253.
- 1254.
- 1255.
- 1256.
- 1257.
- 1258.
- 1259.
- 1260.
- 1261.
- 1262.
- 1263.
- 1264.
- 1265.
- 1266.
- 1267.
- 1268.
- 1269.
- 1270.
- 1271.
- 1272.
- 1273.
- 1274.
- 1275.
- 1276.
- 1277.
- 1278.
- 1279.
- 1280.
- 1281.
- 1282.
- 1283.
- 1284.
- 1285.
- 1286.
- 1287.
- 1288.
- 1289.
- 1290.
- 1291.
- 1292.
- 1293.
- 1294.
- 1295.
- 1296.
- 1297.
- 1298.
- 1299.
- 1300.
- 1301.
- 1302.
- 1303.
- 1304.
- 1305.
- 1306.
- 1307.
- 1308.
- 1309.
- 1310.
- 1311.
- 1312.
- 1313.
- 1314.
- 1315.
- 1316.
- 1317.
- 1318.
- 1319.
- 1320.
- 1321.
- 1322.
- 1323.
- 1324.
- 1325.
- 1326.
- 1327.
- 1328.
- 1329.
- 1330.
- 1331.
- 1332.
- 1333.
- 1334.
- 1335.
- 1336.
- 1337.
- 1338.
- 1339.
- 1340.
- 1341.
- 1342.
- 1343.
- 1344.
- 1345.
- 1346.
- 1347.
- 1348.
- 1349.
- 1350.
- 1351.
- 1352.
- 1353.
- 1354.
- 1355.
- 1356.
- 1357.
- 1358.
- 1359.
- 1360.
- 1361.
- 1362.
- 1363.
- 1364.
- 1365.
- 1366.
- 1367.
- 1368.
- 1369.
- 1370.
- 1371.
- 1372.
- 1373.
- 1374.
- 1375.
- 1376.
- 1377.
- 1378.
- 1379.
- 1380.
- 1381.
- 1382.
- 1383.
- 1384.
- 1385.
- 1386.
- 1387.
- 1388.
- 1389.
- 1390.
- 1391.
- 1392.
- 1393.
- 1394.
- 1395.
- 1396.
- 1397.
- 1398.
- 1399.
- 1400.
- 1401.
- 1402.
- 1403.
- 1404.
- 1405.
- 1406.
- 1407.
- 1408.
- 1409.
- 1410.
- 1411.
- 1412.
- 1413.
- 1414.
- 1415.
- 1416.
- 1417.
- 1418.
- 1419.
- 1420.
- 1421.
- 1422.
- 1423.
- 1424.
- 1425.
- 1426.
- 1427.
- 1428.
- 1429.
- 1430.
- 1431.
- 1432.
- 1433.
- 1434.
- 1435.
- 1436.
- 1437.
- 1438.
- 1439.
- 1440.
- 1441.
- 1442.
- 1443.
- 1444.
- 1445.
- 1446.
- 1447.
- 1448.
- 1449.
- 1450.
- 1451.
- 1452.
- 1453.
- 1454.
- 1455.
- 1456.
- 1457.
- 1458.
- 1459.
- 1460.
- 1461.
- 1462.
- 1463.
- 1464.
- 1465.
- 1466.
- 1467.
- 1468.
- 1469.
- 1470.
- 1471.
- 1472.
- 1473.
- 1474.
- 1475.
- 1476.
- 1477.
- 1478.
- 1479.
- 1480.
- 1481.
- 1482.
- 1483.
- 1484.
- 1485.
- 1486.
- 1487.
- 1488.
总结
RTSP转RTMP模块设计,可以用ffmpeg直接命令行转发,也可以用方案二的非常成熟的转发设计,ffmpeg转发,需要有一定的代码基础,有问题的话,bug修复需要对底层逻辑非常了解才行,方案二,技术成熟,二次开发难度不大,很同意集成到自己现有系统,感兴趣的开发者,可以单独跟我沟通交流。