Windows平台C#版RTSP转RTMP直播推送定制版

技术背景

前几年我们发布了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、麦克风、扬声器或麦克风扬声器混音。

<?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>

简单的读取代码如下:

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 callbackvideo_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_I420video_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#版本,只是做了基础的封装,方便开发者二次定制处理,如果有更复杂的界面和逻辑需求,基于此版本继续开发就好。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/16140.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【启程Golang之旅】深入解析函数的奥秘与技巧

欢迎来到Golang的世界&#xff01;在当今快节奏的软件开发领域&#xff0c;选择一种高效、简洁的编程语言至关重要。而在这方面&#xff0c;Golang&#xff08;又称Go&#xff09;无疑是一个备受瞩目的选择。在本文中&#xff0c;带领您探索Golang的世界&#xff0c;一步步地了…

【全开源】海报在线制作系统源码(ThinkPHP+FastAdmin+UniApp)

打造个性化创意海报的利器 引言 在数字化时代&#xff0c;海报作为一种重要的宣传媒介&#xff0c;其设计质量和效率直接影响着宣传效果。为了满足广大用户对于个性化、高效制作海报的需求&#xff0c;海报在线制作系统源码应运而生。本文将详细介绍海报在线制作系统源码的特…

AbMole - 肿瘤发展与免疫器官的“舞蹈”:一场细胞层面的时间赛跑

在生物医学领域&#xff0c;肿瘤与免疫系统之间的相互作用一直是研究的热点话题。肿瘤细胞不是孤立存在的&#xff0c;它们与宿主的免疫系统进行着一场复杂的“舞蹈”。 最近&#xff0c;一项发表在《Molecular & Cellular Proteomics》杂志上的研究&#xff0c;为我们揭开…

【C++】二分查找算法

1.题目 2.算法思路 暴力解法&#xff1a;可以将数组遍历一遍&#xff0c;就可以找到。时间复杂度为O(n)。不算太差&#xff0c;可以接受。 但是有更优秀的解法&#xff1a; 就是二分查找算法。 算法的特点&#xff1a;我们所查找的“数组”具有二段性。这里的二段性不一定有…

Oracle 并行和 session 数量的

这也就是为什么我们指定parallel为4&#xff0c;而实际并行度为8的原因。 insert create index&#xff0c;发现并行数都是加倍的 Indexes seem always created with parallel degree 1 during import as seen from a sqlfile. The sql file shows content like: CREATE INDE…

滑不动窗口的秘密—— “滑动窗口“算法 (Java版)

本篇会加入个人的所谓鱼式疯言 ❤️❤️❤️鱼式疯言:❤️❤️❤️此疯言非彼疯言 而是理解过并总结出来通俗易懂的大白话, 小编会尽可能的在每个概念后插入鱼式疯言,帮助大家理解的. &#x1f92d;&#x1f92d;&#x1f92d;可能说的不是那么严谨.但小编初心是能让更多人能接…

《python编程从入门到实践》day39

# 昨日知识点回顾 创建主页、继承模版、显示特定主题页面 # view.py from django.shortcuts import render# 导入所需数据相关联的模型 from .models import Topic# Create your views here. def index(request):"""学习笔记的主页"""#…

Java进阶学习笔记13——抽象类

认识抽象类&#xff1a; 当我们在做子类共性功能抽取的时候&#xff0c;有些方法在父类中并没有具体的体现&#xff0c;这个时候就需要抽象类了。在Java中&#xff0c;一个没有方法体的方法应该定义为抽象方法&#xff0c;而类中如果有抽象方法&#xff0c;该类就定义为抽象类…

ISCC2024个人挑战赛WP-迷失之门

&#xff08;非官方解&#xff0c;以下内容均互联网收集的信息和个人思路&#xff0c;仅供学习参考&#xff09; 迷失之门 方法一&#xff1a; IDA看一下 check函数逻辑 进入到check2函数 R键将ascii码转字符&#xff0c;写出逆向脚本 #include <stdio.h> #include &l…

Linux C++ Socket 套接字、select、poll、epoll 实例

文章目录 1. 概述2. TCP 网络编程实例2.1 服务器端2.2 客户端2.3 运行截图 3. I/O 模型3.1 阻塞式I/O模型3.2 非阻塞I/O模型3.3 I/O 复用模型3.4 信号驱动式I/O3.5 异步I/O模型 4. I/O复用之 select4.1 select 函数描述4.2 服务端代码4.3 客户端代码4.4 运行截图 5. I/O复用之 …

RocketMq局部顺序消息

package com.ldj.rocketmq.producer;import org.apache.rocketmq.client.producer.DefaultMQProducer; import org.apache.rocketmq.common.message.Message;import java.nio.charset.StandardCharsets;/*** User: ldj* Date: 2024/5/26* Time: 15:09* Description: 局部顺序消…

css卡片翻转 父元素翻转子元素不翻转效果

css卡片翻转 父元素翻转子元素不翻转效果 vue <div class"moduleBox"><div class"headTitle"><span class"headName">大额案例</span></div><div class"moduleItem"><span class"module…

three.js判断物体在人的前面,还是后面

three.js判断物体在人的前面&#xff0c;还是后面 const player new THREE.Vectors(10, 0, 5); const mesh new THREE.Vectors(15, 0, 6);上面&#xff0c;两个变量分别表示&#xff0c;玩家的位置&#xff0c;物体的位置。 从这发现&#xff0c;当玩家和物体的角度关系 小…

spring boot 整合j2cache 项目启动警告 Redis mode [null] not defined. Using ‘single‘

好 之前的文章 spring boot 整合j2cache 基础操作 在spring boot环境中整合了 j2cache 我们 项目启动时 日志会有一个关键信息 Redis的模式 没有定义 默认使用 single Redis 的这个模式有四种 大家可以自己去网上找一下 做个了解 不用很纠结 我们直接在 j2cache.properties …

一文读懂Apollo客户端配置加载流程

本文基于 apollo-client 2.1.0 版本源码进行分析 Apollo 是携程开源的配置中心&#xff0c;能够集中化管理应用不同环境、不同集群的配置&#xff0c;配置修改后能够实时推送到应用端&#xff0c;并且具备规范的权限、流程治理等特性。 Apollo支持4个维度管理Key-Value格式的配…

比勤奋更重要的是系统思考的能力

不要在接近你问题症状的地方寻找解决办法&#xff0c;要追溯过去&#xff0c;查找问题的根源。通常&#xff0c;最有效的活动是最微妙的。有时最好按兵不动&#xff0c;使系统自我修正&#xff0c;或让系统引导行动。有时会发现&#xff0c;最好的解决办法出现在完全出乎预料的…

HTML蓝色爱心

目录 写在前面 HTML入门 完整代码 代码分析 运行结果 系列推荐 写在后面 写在前面 最近好冷吖&#xff0c;小编给大家准备了一个超级炫酷的爱心&#xff0c;一起来看看吧&#xff01; HTML入门 HTML全称为HyperText Markup Language&#xff0c;是一种标记语言&#…

C++-指针

在C中&#xff0c;指针是至关重要的组成部分。它是C语言最强大的功能之一&#xff0c;也是最棘手的功能之一。 指针具有强大的能力&#xff0c;其本质是协助程序员完成内存的直接操纵。 指针&#xff1a;特定类型数据在内存中的存储地址&#xff0c;即内存地址。 指针变量的定…

2024.5组队学习——MetaGPT(0.8.1)智能体理论与实战(下):多智能体开发

传送门&#xff1a; 《2024.5组队学习——MetaGPT&#xff08;0.8.1&#xff09;智能体理论与实战&#xff08;上&#xff09;&#xff1a;MetaGPT安装、单智能体开发》《2024.5组队学习——MetaGPT&#xff08;0.8.1&#xff09;智能体理论与实战&#xff08;中&#xff09;&…

ModelBuilder之GDP空间化——批量值提取

一、前言 前面明确说到对于空间化过程中其实只有两个过程可以进行批量操作,一个是我们灯光指数提取过程和批量进行值提取,这里补充一点,对于灯光指数计算可以实现批量计算总灯光指数和平均灯光指数,综合灯光指数需要用平均灯光指数乘以面积占比求得,面积比就是(DN大于0的…