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

news2024/12/28 19:38:06

技术背景

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

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

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

相关文章

关于堆排序

今天我们不刷力扣了&#xff0c;我们来复习&#xff08;手撕&#xff09;一下数据结构中的八大排序算法之一&#xff0c;堆排序 基本概念&#xff1a; 堆是一种特殊的树形数据结构&#xff0c;即完全二叉树。 堆分为大顶堆和小顶堆&#xff1a; 大顶堆&#xff1a;每个节点的值…

手机上制作证件照

最近由于需要给老姐弄一组证件照&#xff0c;找了一通手机上的软件&#xff0c;找到一款性价比较高的&#xff0c;详细流程记录下来。vx小程序上搜索"泰世茂证件照"&#xff0c;打开首页如下图所示∶ 单击"开始制作" &#xff0c;选择一个证件照类别&#…

Python中Web开发-FastAPI框架

大家好&#xff0c;在当今Web开发领域&#xff0c;高性能、易用性和可扩展性是开发者们追求的目标。Python作为一种流行的编程语言&#xff0c;在Web开发领域也有着强大的影响力。而在众多的Python Web框架中&#xff0c;FastAPI凭借其快速、现代和易用的特性&#xff0c;成为了…

语雀——云知识库/笔记

对于日常进行学习/创作或是记录学习、工作内容与心得的群体来说&#xff0c;能够及时同步的云笔记应用有着广泛的应用场景。近期&#xff0c;我也探索了许多款不同的软件应用&#xff0c;今天来分享一款很有特点的应用——语雀。 语雀&#xff0c;为每一个人提供优秀的文档和知…

docker-file 网络

docker挂载 1.绑定挂载&#xff08;Bind Mounts&#xff09;&#xff1a;绑定挂载是将主机上的文件或目录挂载到容器中。 docker run -v /host/path:/container/path image_name 2.卷挂载&#xff08;Volume Mounts&#xff09;&#xff1a;卷挂载将 Docker 数据卷挂载到容器中…

[数据结构1.0]计数排序

读者老爷好&#xff0c;本鼠鼠最近学了计数排序&#xff0c;浅浅介绍一下&#xff01; 目录 1.统计相同元素出现次数 2.根据统计的结果将序列回填到原来的序列中 3.相对映射计数排序 计数排序又称为鸽巢原理&#xff0c;是对哈希直接定址法的变形应用&#xff0c;是非比较排…

AI语音及其应用

文章目录 一、基本认识二、AI语音应用场景三、真人录音与AI配音的区别四、AI语音创作基本步骤 本文将简单了解下AI语音、应用场景及其优势和创作核心步骤。 一、基本认识 AI语音是指基于人工智能技术开发的语音识别和语音合成系统。语音识别是指计算机识别和理解人类语音的能力…

决定了,将ChatGPTer开源!主打一个大模型人人可用。

一个快速上手且极易部署的类ChatGPT开源应用&#xff0c;可接入 OPENAI API 或 通义千问API 开源地址&#xff1a; https://github.com/isnl/EsChat 大声(偷偷)告诉你&#xff1a;通义千问有免费API额度可白嫖&#xff01;&#xff01;&#xff01; 版本特性 OPENAI 和 通义千…

北航提出part2whole:可控⼈体图像⽣成的统⼀参考框架,可从任何数量和不同来源的参考人体部位图像中生成逼真的、高质量的各种姿势的人体人物图。

Parts2Whole&#xff0c;它可以从任何数量和不同来源的参考人体部位图像中生成逼真的、高质量的各种姿势的人体人物。我们的方法保持了与相应条件语义区域的高度一致性&#xff0c;同时保证了整体之间的多样性和协调性。 (a)以不同人的参考图像为条件生成人的图像。每对包括4个…

6.Redis之String命令

1.String类型基本介绍 redis 所有的 key 都是字符串, value 的类型是存在差异的~~ 一般来说,redis 遇到乱码问题的概率更小~~ Redis 中的字符串,直接就是按照二进制数据的方式存储的. (不会做任何的编码转换【讲 mysql 的时候,知道 mysql 默认的字符集, 是拉丁文,插入中文…

ChatGLM2-6B 模型基于 [P-Tuning v2]的微调

ChatGLM2-6B-PT 一、介绍 1、本文实现对于 ChatGLM2-6B 模型基于 [P-Tuning v2](https://github.com/THUDM/P-tuning-v2) 的微调 2、运行至少需要 7GB 显存 3、以 [ADGEN](https://aclanthology.org/D19-1321.pdf) (广告生成) 数据集为例介绍代码的使用方法。 模型部署参考…

OC笔记之foundation框架

OC学习笔记&#xff08;三&#xff09; 文章目录 OC学习笔记&#xff08;三&#xff09;常用Foundation框架结构体NSRangeNSRange结构体的定义定义 NSRange 的方法打印Range的相关信息NSRange的实际运用查找子字符串返回NSRange结构体 NSPointNSRect NSStringNSString的创建NSS…

Python文件操作(Excel、PDF、XML、Word)

大家好&#xff0c;在现代数据驱动的世界中&#xff0c;对于数据的处理和管理是至关重要的。Python作为一种强大而灵活的编程语言&#xff0c;提供了丰富的工具和库来处理各种文件格式。本文将探讨Python中的文件操作&#xff0c;重点介绍如何使用Python处理Excel、PDF、XML和W…

【python】python tkinter 计算器GUI版本(模仿windows计算器 源码)【独一无二】

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…

每日一题《leetcode--2816.翻倍以链表形式表示的数字》

https://leetcode.cn/problems/double-a-number-represented-as-a-linked-list/ 这里我们直接模拟翻倍后链表的数字&#xff0c;首先我们得先考虑链表的头结点的值是否大于4(*2后是否需要进位)。 处理完头结点后&#xff0c;就需要考虑其余结点。因为求的是链表中每个结点翻倍后…

这个开源的多模态模型无敌。。。

InternVL 由 OpenGVLab 开发&#xff0c;是一个开源的多模态对话模型&#xff0c;其性能接近商业化的 GPT-4V 模型。 GPT-4V 是 OpenAI 去年推出的多模态模型&#xff0c;使用它你可以分析所需的任何类型的图像并获取有关该图像的信息。 1. InternVL 开源模型 而今天的主角研究…

基于python实现的深度学习web多格式纠错系统

基于python实现的深度学习web多格式纠错系统 开发语言:Python 数据库&#xff1a;MySQL所用到的知识&#xff1a;Django框架工具&#xff1a;pycharm、Navicat、Maven 系统功能实现 用户登录 登录功能是本系统一个非常重要的功能&#xff0c;这极大的保护了系统的安全。登录…

跨平台之用VisualStudio开发APK嵌入OpenCV(一)

序 本篇是杂谈以及准备工作&#xff08;此处应无掌声&#xff09; 暂时不管iOS&#xff08;因为开发hello world都要年费&#xff09; 软件&#xff1a; Visual Studio 2019&#xff08;含Android SDK和NDK编译器等&#xff09; OpenCV 这是一个女仆级的系列文章&#xf…

php之sql代码审计

1 SQL注入代码审计流程 1.1 反向查找流程 通过可控变量(输入点)回溯危险函数 查找危险函数确定可控变量 传递的过程中触发漏洞 1.2 反向查找流程特点 暴力&#xff1a;全局搜索危险函数 简单&#xff1a;无需过多理解目标网站功能与架构 快速&#xff1a;适用于自动化代码审…

vue实现可拖拽移动悬浮球

封装悬浮球组件&#xff0c;文件名s-icons.vue <template><div ref"icons" class"icons-container" :style"{ left: left px, top: top px }"><slot></slot></div> </template> <script> export …