技术背景
前几年我们发布了C++版的多路RTMP/RTSP转RTMP转发官方定制版。在秉承低延迟、灵活稳定、低资源占用的前提下,客户无需关注开发细节,只需图形化配置转发等各类参数,实现产品快速上线目的。
如监控类摄像机、NVR等,通过厂商说明或Onvif工具,获取拉流的RTSP地址,图形化配置,完成拉流转发等操作,轻松实现标准RTMP服务器对接。
视频转发支持H.264、H.265(需要RTMP服务器或CDN支持扩展H.265),音频支持配置PCMA/PCMU转AAC后转发,并支持只转发/录制视频或音频,RTSP拉流端支持鉴权和TCP/UDP模式设置和TCP/UDP模式自动切换,整个拉流、转发模块都有非常完善的自动重连机制。
运维方面,官方定制版转发系统支持7*24小时不间断运行,自带守护进程,转发程序被误关等各种操作后,会自动启动运行,此外,还支持开机自动启动转发或录像。
技术实现
随着开发者不同的技术诉求,好多公司都是基于我们C#的demo进一步开发,本次demo,我们在原有C#的转发程序的基础上,稍作调整,实现了开机自启动、推拉流xml配置、实时预览和自动转发操作:
开机自启动
开机自启动,是好多开发者做rtsp转rtmp程序的时候,比较关注的功能。简单的实现如下:
private void SetAutoStart(bool is_auto_start) { try { string exePath = Assembly.GetExecutingAssembly().Location; string name = Path.GetFileNameWithoutExtension(exePath); bool exist = false; using (RegistryKey key = Registry.CurrentUser.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Run", true)) { if (key != null) { string[] valueNames = key.GetValueNames(); foreach (string valueName in valueNames) { string valueData = key.GetValue(valueName).ToString(); if (valueData.Contains(exePath)) { exist = true; break; } } if (exist) { if (!is_auto_start) { key.DeleteValue(name, false); } return; } if (is_auto_start) { key.SetValue(name, exePath); } } } } catch (Exception ex) { Console.WriteLine(ex.Message); } }
通过配置xml的形式,程序启动后,从configure.xml读取相关的参数,实现一键拉流转发。
常规的参数配置,比如推拉流的rtsp rtmp url,如果需要自采集audio,设置采集的audio类型,比如rtsp自带audio、麦克风、扬声器或麦克风扬声器混音。
<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>
简单的读取代码如下:
private void GetXmlConfigure() { List<StreamRelayConfig> streamRelayConfigList = new List<StreamRelayConfig>(); try { XmlDocument xmlDoc = new XmlDocument(); xmlDoc.Load(AppDomain.CurrentDomain.BaseDirectory + @"configure.xml"); XmlNode rootNode = xmlDoc.SelectSingleNode("StreamRelays"); XmlNodeList streamRelayNodeList = rootNode.ChildNodes; foreach (XmlNode skNode in streamRelayNodeList) { StreamRelayConfig streamRelayConfig = new StreamRelayConfig(); XmlNodeList fileNodeList = skNode.ChildNodes; foreach (XmlNode fileNode in fileNodeList) { if (fileNode.Name == "id") { int id = Int32.Parse(fileNode.InnerText); streamRelayConfig.Id = id; } if (fileNode.Name == "AudioOption") { int audio_option = Int32.Parse(fileNode.InnerText); streamRelayConfig.AudioOption = audio_option; } else if (fileNode.Name == "PullUrl") { streamRelayConfig.PullUrl = fileNode.InnerText; } else if (fileNode.Name == "PushUrl") { streamRelayConfig.PushUrl = fileNode.InnerText; } } streamRelayConfigList.Add(streamRelayConfig); } } catch (Exception ex) { Console.WriteLine(ex.ToString()); } int i = 0; stream_relay_instance_count_ = streamRelayConfigList.Count(); foreach (StreamRelayConfig steamrelay in streamRelayConfigList) { stream_relay_config_[i].AudioOption = steamrelay.AudioOption; stream_relay_config_[i].PullUrl = steamrelay.PullUrl; stream_relay_config_[i].PushUrl = steamrelay.PushUrl; lable_audio_option_[i].Text = ConvertAudioOption(steamrelay.AudioOption); Console.WriteLine(steamrelay); i++; } }
如果需要预览,直接点预览按钮即可。
大概的封装实现如下:
/* * nt_relay_wrapper.cs.cs * nt_relay_wrapper.cs * * 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.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(); } } }
播放端封装关键代码如下:
/* * nt_player_wrapper.cs * nt_player_wrapper * * Github: 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); }
推送端封装核心代码如下:
/* * nt_publisher_wrapper.cs * nt_publisher_wrapper * * Github: 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); } } }
总结
Windows平台RTSP转RTMP推送定制版,目前发布的C#版本,只是做了基础的封装,方便开发者二次定制处理,如果有更复杂的界面和逻辑需求,基于此版本继续开发就好。