Windows平台基于RTMP实现一对一互动直播

简介: 目前市面上大多一对一互动都是基于WebRTC,缺点如下:1. 服务器部署非常复杂,不利于私有部署,在一些私密性高的场景下,无法使用,如公安、市政等体系;2. 传输基于UDP,很难保证传输质量,由于UDP是不可靠的传输协议,在复杂的公网网络环境下,各种突发流量、偶尔的传输错误、网络抖动、超时等等都会引起丢包异常,都会在一定程度上影响音视频通信的质量;3. 难以应对复杂的互联网环境,如跨区跨运营商、低带宽、高丢包等场景;4. 整个框架体系不够灵活,代码复杂度高,行话说的好:从demo到实用,中间还差1万个WebRTC。

目前市面上大多一对一互动都是基于WebRTC,缺点如下:

  1. 服务器部署非常复杂,不利于私有部署,在一些私密性高的场景下,无法使用,如公安、市政等体系;
  2. 传输基于UDP,很难保证传输质量,由于UDP是不可靠的传输协议,在复杂的公网网络环境下,各种突发流量、偶尔的传输错误、网络抖动、超时等等都会引起丢包异常,都会在一定程度上影响音视频通信的质量;
  3. 难以应对复杂的互联网环境,如跨区跨运营商、低带宽、高丢包等场景;
  4. 整个框架体系不够灵活,代码复杂度高,行话说的好:从demo到实用,中间还差1万个WebRTC

RTMP一对一互动技术特点:

  • 基于现有RTMP推拉流体系,产品稳定度高,整体延迟低;
  • 加入噪音抑制、回音消除、自动增益控制等特性,确保通话效果;
  • 采用通用的RTMP和RTSP服务器,如nginx、SRS,更有利于私有部署;
  • 支持H.264的扩展SEI消息发送机制;
  • 支持H.265编码和H.264可变码率设定;
  • 支持H.265解码,直播播放器支持的功能,一对一互动模块都可以有选择的支持;
  • 适用于应急指挥、教育培训等领域。

废话不多说,上封装代码:

基于 https://github.com/daniulive/SmarterStreaming/ 拉流端封装的代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Drawing;
using System.Drawing.Drawing2D;
using NT;
namespace SmartEchoCancellationDemo
{
    public delegate void DelGetPlayerEventMsg(String msg);
    public delegate void DelGetVideoSize(String size);
    class nt_player_wrapper : IDisposable
    {
        [DllImport("kernel32", EntryPoint = "CopyMemory")]
        static extern void CopyMemory(IntPtr Destination, IntPtr Source, uint Length);
        private bool disposed_ = false;
        private IntPtr player_handle_ = IntPtr.Zero;
        private System.Windows.Forms.Control render_wnd_ = null;
        private System.Windows.Forms.PaintEventHandler render_wnd_paint_event_ = null;
        private bool is_playing_ = false;
        private bool is_mute_ = false;
        private int  play_buffer_ = 100;
        private NT_SP_VideoFrame cur_video_frame_ = new NT_SP_VideoFrame();
        private WeakReference sync_invoke_ = null;
        //分辨率信息回调
        delegate void ResolutionNotifyCallback(Int32 width, Int32 height);
        ResolutionNotifyCallback resolution_notify_callback_;
        SP_SDKVideoSizeCallBack video_size_call_back_;
        //视频数据回调
        SP_SDKVideoFrameCallBack video_frame_call_back_;
        delegate void VideoFrameCallBack(UInt32 status, NT_SP_VideoFrame frame);
        VideoFrameCallBack set_video_frame_call_back_;
        //event事件回调
        //拉流端事件
        SP_SDKEventCallBack pull_event_call_back_;
        delegate void SetPullEventCallBack(UInt32 event_id,
                Int64 param1,
                Int64 param2,
                UInt64 param3,
                [MarshalAs(UnmanagedType.LPStr)] String param4,
                [MarshalAs(UnmanagedType.LPStr)] String param5,
                IntPtr param6);
        SetPullEventCallBack set_pull_event_call_back_;
        private UInt32 connection_status_ = 0;
        private UInt32 buffer_status_ = 0;
        private Int32 buffer_percent_ = 0;
        private Int32 download_speed_ = -1;
        public event DelGetPlayerEventMsg EventGetPlayerEventMsg;
        public event DelGetVideoSize EventGetVideoSize;
        public nt_player_wrapper(System.Windows.Forms.Control render_wnd, System.ComponentModel.ISynchronizeInvoke sync_invoke)
        {
            render_wnd_ = render_wnd;
            sync_invoke_ =  new WeakReference(sync_invoke);
            set_pull_event_call_back_ = new SetPullEventCallBack(PullEventCallBack);
            if (render_wnd_ != null)
            {
                render_wnd_paint_event_ = new System.Windows.Forms.PaintEventHandler(this.OnRenderWindowPaint);
                render_wnd_.Paint += render_wnd_paint_event_;
            }
        }
        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 (IsPlaying())
                {
                   StopPlay(false);
                }
                if (render_wnd_ != null && render_wnd_paint_event_ != null)
                {
                    render_wnd_.Paint -= render_wnd_paint_event_;
                }
                render_wnd_paint_event_ = null;
                if (cur_video_frame_.plane0_ != IntPtr.Zero)
                {
                    Marshal.FreeHGlobal(cur_video_frame_.plane0_);
                    cur_video_frame_.plane0_ = IntPtr.Zero;
                }
                // Note disposing has been done.
                disposed_ = true;
            }
        }
        ~nt_player_wrapper()
        {
            Dispose(false);
        }
        public void SetVideoFrameCallBack(IntPtr handle, IntPtr userData, UInt32 status, IntPtr frame)
        {
            if (frame == IntPtr.Zero)
            {
                return;
            }
            //如需直接处理RGB数据,请参考以下流程
            NT_SP_VideoFrame video_frame = (NT_SP_VideoFrame)Marshal.PtrToStructure(frame, typeof(NT_SP_VideoFrame));
            if (video_frame.format_ != (Int32)NT.NTSmartPlayerDefine.NT_SP_E_VIDEO_FRAME_FORMAT.NT_SP_E_VIDEO_FRAME_FORMAT_RGB32)
                return;
            NT_SP_VideoFrame pVideoFrame = new NT_SP_VideoFrame();
            pVideoFrame.format_ = video_frame.format_;
            pVideoFrame.width_ = video_frame.width_;
            pVideoFrame.height_ = video_frame.height_;
            pVideoFrame.timestamp_ = video_frame.timestamp_;
            pVideoFrame.stride0_ = video_frame.stride0_;
            pVideoFrame.stride1_ = video_frame.stride1_;
            pVideoFrame.stride2_ = video_frame.stride2_;
            pVideoFrame.stride3_ = video_frame.stride3_;
            if (sync_invoke_ != null)
            {
                System.ComponentModel.ISynchronizeInvoke sync_invoke_target = sync_invoke_.Target as System.ComponentModel.ISynchronizeInvoke;
                if (sync_invoke_target != null)
                {
                    Int32 argb_size = video_frame.stride0_ * video_frame.height_;
                    pVideoFrame.plane0_ = Marshal.AllocHGlobal(argb_size);
                    CopyMemory(pVideoFrame.plane0_, video_frame.plane0_, (UInt32)argb_size);
                    if (sync_invoke_target.InvokeRequired)
                    {
                        sync_invoke_target.BeginInvoke(set_video_frame_call_back_, new object[] { status, pVideoFrame });
                    }
                    else
                    {
                        set_video_frame_call_back_(status, pVideoFrame);
                    }
                }
            }
        }
        public void SDKVideoFrameCallBack(UInt32 status, NT_SP_VideoFrame frame)
        {
            if (cur_video_frame_.plane0_ != IntPtr.Zero)
            {
                Marshal.FreeHGlobal(cur_video_frame_.plane0_);
                cur_video_frame_.plane0_ = IntPtr.Zero;
            }
            cur_video_frame_ = frame;
            if (render_wnd_ != null)
            {
                render_wnd_.Invalidate();
            }
        }
        public void SDKPullEventCallBack(IntPtr handle, IntPtr user_data,
            UInt32 event_id,
            Int64 param1,
            Int64 param2,
            UInt64 param3,
            [MarshalAs(UnmanagedType.LPStr)] String param4,
            [MarshalAs(UnmanagedType.LPStr)] String param5,
            IntPtr param6)
        {
            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_pull_event_call_back_, new object[] { event_id, param1, param2, param3, param4, param5, param6 });
                    }
                    else
                    {
                        set_pull_event_call_back_(event_id, param1, param2, param3, param4, param5, param6);
                    }
                }
            }
        }
        private void PullEventCallBack(UInt32 event_id,
            Int64 param1,
            Int64 param2,
            UInt64 param3,
            [MarshalAs(UnmanagedType.LPStr)] String param4,
            [MarshalAs(UnmanagedType.LPStr)] String param5,
            IntPtr param6)
        {
            if (!is_playing_)
            {
                return;
            }
            String show_str = "";
            if ((UInt32)NTSmartPlayerDefine.NT_SP_E_EVENT_ID.NT_SP_E_EVENT_ID_PLAYBACK_REACH_EOS == event_id)
            {
                StopPlay();
                return;
            }
            else if ((UInt32)NTSmartPlayerDefine.NT_SP_E_EVENT_ID.NT_SP_E_EVENT_ID_RTSP_STATUS_CODE == event_id)
            {
                int status_code = (int)param1;
                show_str = "RTSP incorrect status code received: " + status_code.ToString() + ", 请确保用户名/密码正确";
            }
            if ((UInt32)NTSmartPlayerDefine.NT_SP_E_EVENT_ID.NT_SP_E_EVENT_ID_CONNECTING == event_id
                || (UInt32)NTSmartPlayerDefine.NT_SP_E_EVENT_ID.NT_SP_E_EVENT_ID_CONNECTION_FAILED == event_id
                || (UInt32)NTSmartPlayerDefine.NT_SP_E_EVENT_ID.NT_SP_E_EVENT_ID_CONNECTED == event_id
                || (UInt32)NTSmartPlayerDefine.NT_SP_E_EVENT_ID.NT_SP_E_EVENT_ID_DISCONNECTED == event_id)
            {
                connection_status_ = event_id;
            }
            if ((UInt32)NTSmartPlayerDefine.NT_SP_E_EVENT_ID.NT_SP_E_EVENT_ID_START_BUFFERING == event_id
                || (UInt32)NTSmartPlayerDefine.NT_SP_E_EVENT_ID.NT_SP_E_EVENT_ID_BUFFERING == event_id
                || (UInt32)NTSmartPlayerDefine.NT_SP_E_EVENT_ID.NT_SP_E_EVENT_ID_STOP_BUFFERING == event_id)
            {
                buffer_status_ = event_id;
                if ((UInt32)NTSmartPlayerDefine.NT_SP_E_EVENT_ID.NT_SP_E_EVENT_ID_BUFFERING == event_id)
                {
                    buffer_percent_ = (Int32)param1;
                }
            }
            if ((UInt32)NTSmartPlayerDefine.NT_SP_E_EVENT_ID.NT_SP_E_EVENT_ID_DOWNLOAD_SPEED == event_id)
            {
                download_speed_ = (Int32)param1;
            }
            if (connection_status_ != 0)
            {
                show_str += "连接状态: ";
                if ((UInt32)NTSmartPlayerDefine.NT_SP_E_EVENT_ID.NT_SP_E_EVENT_ID_CONNECTING == connection_status_)
                {
                    show_str += "连接中";
                }
                else if ((UInt32)NTSmartPlayerDefine.NT_SP_E_EVENT_ID.NT_SP_E_EVENT_ID_CONNECTION_FAILED == connection_status_)
                {
                    show_str += "连接失败";
                }
                else if ((UInt32)NTSmartPlayerDefine.NT_SP_E_EVENT_ID.NT_SP_E_EVENT_ID_CONNECTED == connection_status_)
                {
                    show_str += "连接成功";
                }
                else if ((UInt32)NTSmartPlayerDefine.NT_SP_E_EVENT_ID.NT_SP_E_EVENT_ID_DISCONNECTED == connection_status_)
                {
                    show_str += "断开连接";
                }
            }
            if (download_speed_ != -1)
            {
                String ss = "  下载速度: " + (download_speed_ * 8 / 1000).ToString() + "kbps " + (download_speed_ / 1024).ToString() + "KB/s";
                show_str += ss;
            }
            if (buffer_status_ != 0)
            {
                show_str += "  缓冲状态: ";
                if ((UInt32)NTSmartPlayerDefine.NT_SP_E_EVENT_ID.NT_SP_E_EVENT_ID_START_BUFFERING == buffer_status_)
                {
                    show_str += "开始缓冲";
                }
                else if ((UInt32)NTSmartPlayerDefine.NT_SP_E_EVENT_ID.NT_SP_E_EVENT_ID_BUFFERING == buffer_status_)
                {
                    String ss = "缓冲中 " + buffer_percent_.ToString() + "%";
                    show_str += ss;
                }
                else if ((UInt32)NTSmartPlayerDefine.NT_SP_E_EVENT_ID.NT_SP_E_EVENT_ID_STOP_BUFFERING == buffer_status_)
                {
                    show_str += "结束缓冲";
                }
            }
            if ((UInt32)NTSmartPlayerDefine.NT_SP_E_EVENT_ID.NT_SP_E_EVENT_ID_NEED_KEY == event_id)
            {
                show_str = "RTMP加密流,请设置播放需要的Key..";
            }
            else if ((UInt32)NTSmartPlayerDefine.NT_SP_E_EVENT_ID.NT_SP_E_EVENT_ID_KEY_ERROR == event_id)
            {
                show_str = "RTMP加密流,Key错误,请重新设置..";
            }
            EventGetPlayerEventMsg(show_str);
        }
        public void SetMute(bool is_mute)
        {
            is_mute_ = is_mute;
          if ( !is_playing_ )
            return;
            NTSmartPlayerSDK.NT_SP_SetMute(player_handle_, is_mute ? 1 : 0); 
        }
        public void SetBuffer(int buffer_time)
        {
            if (buffer_time >= 0)
            {
                play_buffer_ = buffer_time;
            }
        }
        public bool IsPlaying()
        { 
            return is_playing_; 
        }
        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);
            //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);
            NTSmartPlayerSDK.NT_SP_SetMute(pull_handle, is_mute ? 1 : 0);
            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(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 ( !OpenPullHandle(url, is_rtsp_tcp_mode, is_mute) )
            return false;
          NTSmartPlayerSDK.NT_SP_SetMute(player_handle_, is_mute ? 1 : 0);
            //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_);
            NTSmartPlayerSDK.NT_SP_Close(player_handle_);
        player_handle_ = IntPtr.Zero;
          is_playing_ = false;
            if (is_update_ui && render_wnd_ != null)
            {
                render_wnd_.Invalidate();
            }
        }
        private void GetRenderRect(int limtWidth, int limtHeight, int image_w, int image_h, ref int left_offset, ref int top_offset, ref int dw, ref int dh)
        {
            if (limtWidth < 1 || limtHeight < 1)
            {
                left_offset = 0;
                top_offset = 0;
                dw = limtWidth;
                dh = limtHeight;
                return;
            }
            if (image_w < 1 || image_h < 1)
            {
                left_offset = 0;
                top_offset = 0;
                dw = limtWidth;
                dh = limtHeight;
                return;
            }
            // 按比例
            double limit_ratio = limtWidth * 1.0 / limtHeight;
            double video_ratio = image_w * 1.0 / image_h;
            if (video_ratio > limit_ratio)
            {
                dw = limtWidth;
                dh = (int)(dw * image_h * 1.0 / image_w);
                if (dh > limtHeight)
                    dh = limtHeight;
            }
            else
            {
                dh = limtHeight;
                dw = (int)(dh * image_w * 1.0 / image_h);
                if (dw > limtWidth)
                    dw = limtWidth;
            }
            left_offset = limtWidth / 2 - dw / 2;
            if (left_offset < 0)
                left_offset = 0;
            top_offset = limtHeight / 2 - dh / 2;
            if (top_offset < 0)
                top_offset = 0;
        }
        private void OnRenderWindowPaint(object sender, PaintEventArgs e)
        {
            if (render_wnd_.Width < 1 || render_wnd_.Height < 1)
                return;
            Graphics g = e.Graphics;
            Brush brush = new SolidBrush(Color.Black);
            g.FillRectangle(brush, 0, 0, render_wnd_.Width, render_wnd_.Height);
            if ( IsPlaying() && cur_video_frame_.plane0_ != IntPtr.Zero)
            {
                g.SmoothingMode = SmoothingMode.HighSpeed;
                int image_width = cur_video_frame_.width_;
                int image_height = cur_video_frame_.height_;
                Bitmap bitmap = new Bitmap(image_width, image_height, cur_video_frame_.stride0_,
                System.Drawing.Imaging.PixelFormat.Format32bppRgb, cur_video_frame_.plane0_);
                int d_w = 0, d_h = 0;
                int left_offset = 0;
                int top_offset = 0;
                GetRenderRect(render_wnd_.Width, render_wnd_.Height, image_width, image_height, ref left_offset, ref top_offset, ref d_w, ref d_h);
                g.DrawImage(bitmap,  left_offset, top_offset, d_w, d_h);   //在窗体的画布中绘画出内存中的图像
            }         
        }
    }
}

基于 https://github.com/daniulive/SmarterStreaming/ 推流端封装的代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Drawing;
using System.Drawing.Drawing2D;
using NT;
namespace SmartEchoCancellationDemo
{
    public delegate void DelGetPublisherEventMsg(String msg);   //推送端Event消息
    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
    {
        [DllImport("kernel32", EntryPoint = "CopyMemory")]
        static extern void CopyMemory(IntPtr Destination, IntPtr Source, uint Length);
        private bool disposed_ = false;
        private IntPtr publisher_handle_ = IntPtr.Zero;
        private System.Windows.Forms.Control render_wnd_ = null;
        private System.Windows.Forms.PaintEventHandler render_wnd_paint_event_ = null;
        private int publisher_handle_count_;
        private bool is_publishing_ = false;
        private bool is_previewing_ = false;
        private WeakReference sync_invoke_ = null;
        //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;
        public nt_publisher_wrapper(System.Windows.Forms.Control render_wnd, System.ComponentModel.ISynchronizeInvoke sync_invoke)
        {
            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_;
            }
        }
        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_DISCONNECTED:
                    event_log = "断开连接";
                    if (!String.IsNullOrEmpty(param5))
                    {
                        event_log = event_log + " url:" + param5;
                    }
                    break;
                default:
                    break;
            }
            EventGetPublisherEventMsg(event_log);
        }
        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 = 1600;
            }
            else if (area <= (1366 * 768))
            {
                kbit_rate = 2000;
            }
            else if (area <= (1600 * 900))
            {
                kbit_rate = 2300;
            }
            else if (area <= (1600 * 1050))
            {
                kbit_rate = 2500;
            }
            else
            {
                kbit_rate = 2800;
            }
            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 ? 1600 : 3000;
            }
            else if (area <= (1366 * 768))
            {
                max_kbit_rate = is_var_bitrate ? 1700 : 3300;
            }
            else if (area <= (1600 * 900))
            {
                max_kbit_rate = is_var_bitrate ? 2400 : 3400;
            }
            else if (area <= (1600 * 1050))
            {
                max_kbit_rate = is_var_bitrate ? 2600 : 3600;
            }
            else if (area <= (1920 * 1080))
            {
                max_kbit_rate = is_var_bitrate ? 2900 : 3800;
            }
            else
            {
                max_kbit_rate = is_var_bitrate ? 3500 : 5500;
            }
            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)
        {
            if (is_h264)
                return 3;
            int area = w * h;
            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_speader = 0;
            if (NTBaseCodeDefine.NT_ERC_OK == NTSmartPublisherSDK.NT_PB_IsCanCaptureSpeaker(ref is_capture_speader))
            {
                if (1 == is_capture_speader)
                {
                    return true;
                }
            }
            return false;
        }
        public bool OpenPublisherHandle(uint video_option, uint audio_option)
        {
            if (publisher_handle_ != IntPtr.Zero)
            {
                return true;
            }
            publisher_handle_count_ = 0;
            if (NTBaseCodeDefine.NT_ERC_OK != NTSmartPublisherSDK.NT_PB_Open(out publisher_handle_,
                video_option, audio_option, 0, IntPtr.Zero))
            {
                return false;
            }
            if (publisher_handle_ != IntPtr.Zero)
            {
                pb_event_call_back_ = new NT_PB_SDKEventCallBack(PbSDKEventCallBack);
                NTSmartPublisherSDK.NT_PB_SetEventCallBack(publisher_handle_, IntPtr.Zero, pb_event_call_back_);
                set_video_preview_image_callback_ = new SetVideoPreviewImageCallBack(VideoPreviewImageCallBack);
                return true;
            }
            else
            {
                return false;
            }
        }
        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)
            {
                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(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()
        {
            video_preview_image_callback_ = new NT_PB_SDKVideoPreviewImageCallBack(SDKVideoPreviewImageCallBack);
            NTSmartPublisherSDK.NT_PB_SetVideoPreviewImageCallBack(publisher_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(publisher_handle_, 0, IntPtr.Zero))
            {
                if (0 == publisher_handle_count_)
                {
                    NTSmartPublisherSDK.NT_PB_Close(publisher_handle_);
                    publisher_handle_ = IntPtr.Zero;
                }
                return false;
            }
            publisher_handle_count_++;
            is_previewing_ = true;
            return true;
        }
        public void StopPreview()
        {
            is_previewing_ = false;
            publisher_handle_count_--;
            NTSmartPublisherSDK.NT_PB_StopPreview(publisher_handle_);
            if (0 == publisher_handle_count_)
            {
                NTSmartPublisherSDK.NT_PB_Close(publisher_handle_);
                publisher_handle_ = IntPtr.Zero;
            }
            if (render_wnd_ != null)
            {
                render_wnd_.Invalidate();
            }
        }
        public bool StartPublisher(String url)
        {
            if (publisher_handle_ == IntPtr.Zero)
            {
                return false;
            }
            if (!String.IsNullOrEmpty(url))
            {
                NTSmartPublisherSDK.NT_PB_SetURL(publisher_handle_, url, IntPtr.Zero);
            }
            if (NTBaseCodeDefine.NT_ERC_OK != NTSmartPublisherSDK.NT_PB_StartPublisher(publisher_handle_, IntPtr.Zero))
            {
                if (0 == publisher_handle_count_)
                {
                    NTSmartPublisherSDK.NT_PB_Close(publisher_handle_);
                    publisher_handle_ = IntPtr.Zero;
                }
                is_publishing_ = false;
                return false;
            }
            publisher_handle_count_++;
            is_publishing_ = true;
            return true;
        }
        public void StopPublisher()
        {
            publisher_handle_count_--;
            NTSmartPublisherSDK.NT_PB_StopPublisher(publisher_handle_);
            if (0 == publisher_handle_count_)
            {
                NTSmartPublisherSDK.NT_PB_Close(publisher_handle_);
                publisher_handle_ = IntPtr.Zero;
            }
            is_publishing_ = false;
        }
        public void Close()
        {
            if (0 == publisher_handle_count_)
            {
                NTSmartPublisherSDK.NT_PB_Close(publisher_handle_);
                publisher_handle_ = IntPtr.Zero;
            }
        }
        public bool IsPreviewing()
        {
            return is_previewing_;
        }
        public bool IsPublishing()
        {
            return is_publishing_;
        }
        public bool IsPublisherHandleAvailable()
        {
            return publisher_handle_ != IntPtr.Zero ? true : false;
        }
        public int GetPublisherHandleCount()
        {
            return publisher_handle_count_;
        }
        public void SetVideoCaptureDeviceBaseParameter(String camera_id, UInt32 width, UInt32 height)
        {
            NTSmartPublisherSDK.NT_PB_SetVideoCaptureDeviceBaseParameter(publisher_handle_, camera_id, width, height);
        }
        public void SetFrameRate(UInt32 frame_rate)
        {
            NTSmartPublisherSDK.NT_PB_SetFrameRate(publisher_handle_, frame_rate);
        }
        public void SetVideoEncoderType(Int32 encode_type)
        {
            NTSmartPublisherSDK.NT_PB_SetVideoEncoderType(publisher_handle_, encode_type);
        }
        public void SetVideoQualityV2(Int32 quality)
        {
            NTSmartPublisherSDK.NT_PB_SetVideoQualityV2(publisher_handle_, quality);
        }
        public void SetVideoMaxBitRate(Int32 kbit_rate)
        {
            NTSmartPublisherSDK.NT_PB_SetVideoMaxBitRate(publisher_handle_, kbit_rate);
        }
        public void SetVideoKeyFrameInterval(Int32 interval)
        {
            NTSmartPublisherSDK.NT_PB_SetVideoKeyFrameInterval(publisher_handle_, interval);
        }
        public void SetVideoEncoderProfile(Int32 profile)
        {
            NTSmartPublisherSDK.NT_PB_SetVideoEncoderProfile(publisher_handle_, profile);
        }
        public void SetVideoEncoderSpeed(Int32 speed)
        {
            NTSmartPublisherSDK.NT_PB_SetVideoEncoderSpeed(publisher_handle_, speed);
        }
        public void SetAuidoInputDeviceId(UInt32 device_id)
        {
            NTSmartPublisherSDK.NT_PB_SetAuidoInputDeviceId(publisher_handle_, device_id);
        }
        public void SetPublisherAudioCodecType(Int32 type)
        {
            NTSmartPublisherSDK.NT_PB_SetPublisherAudioCodecType(publisher_handle_, type);
        }
        public void SetPublisherMute(bool is_mute)
        {
            NTSmartPublisherSDK.NT_PB_SetMute(publisher_handle_, is_mute ? 1 : 0); 
        }
        public void SetEchoCancellation(Int32 isCancel, Int32 delay)
        {
            NTSmartPublisherSDK.NT_PB_SetEchoCancellation(publisher_handle_, isCancel, delay);
        }
        public void SetNoiseSuppression(Int32 isNS)
        {
            NTSmartPublisherSDK.NT_PB_SetNoiseSuppression(publisher_handle_, isNS);
        }
        public void SetAGC(Int32 isAGC)
        {
            NTSmartPublisherSDK.NT_PB_SetAGC(publisher_handle_, isAGC);
        }
        public void SetVAD(Int32 isVAD)
        {
            NTSmartPublisherSDK.NT_PB_SetVAD(publisher_handle_, isVAD);
        }
        private void GetRenderRect(int limtWidth, int limtHeight, int image_w, int image_h, ref int left_offset, ref int top_offset, ref int dw, ref int dh)
        {
            if (limtWidth < 1 || limtHeight < 1)
            {
                left_offset = 0;
                top_offset = 0;
                dw = limtWidth;
                dh = limtHeight;
                return;
            }
            if (image_w < 1 || image_h < 1)
            {
                left_offset = 0;
                top_offset = 0;
                dw = limtWidth;
                dh = limtHeight;
                return;
            }
            // 按比例
            double limit_ratio = limtWidth * 1.0 / limtHeight;
            double video_ratio = image_w * 1.0 / image_h;
            if (video_ratio > limit_ratio)
            {
                dw = limtWidth;
                dh = (int)(dw * image_h * 1.0 / image_w);
                if (dh > limtHeight)
                    dh = limtHeight;
            }
            else
            {
                dh = limtHeight;
                dw = (int)(dh * image_w * 1.0 / image_h);
                if (dw > limtWidth)
                    dw = limtWidth;
            }
            left_offset = limtWidth / 2 - dw / 2;
            if (left_offset < 0)
                left_offset = 0;
            top_offset = limtHeight / 2 - dh / 2;
            if (top_offset < 0)
                top_offset = 0;
        }
        private void OnRenderWindowPaint(object sender, PaintEventArgs e)
        {
            if (render_wnd_.Width < 1 || render_wnd_.Height < 1)
                return;
            Graphics g = e.Graphics;
            Brush brush = new SolidBrush(Color.Black);
            g.FillRectangle(brush, 0, 0, render_wnd_.Width, render_wnd_.Height);
            if (is_previewing_ && cur_image_.plane_ != IntPtr.Zero)
            {
                g.SmoothingMode = SmoothingMode.HighSpeed;
                int image_width = cur_image_.width_;
                int image_height = cur_image_.height_;
                Bitmap bitmap = new Bitmap(image_width, image_height, cur_image_.stride_,
                System.Drawing.Imaging.PixelFormat.Format32bppRgb, cur_image_.plane_);
                int d_w = 0, d_h = 0;
                int left_offset = 0;
                int top_offset = 0;
                GetRenderRect(render_wnd_.Width, render_wnd_.Height, image_width, image_height, ref left_offset, ref top_offset, ref d_w, ref d_h);
                g.DrawImage(bitmap, left_offset, top_offset, d_w, d_h);   //在窗体的画布中绘画出内存中的图像
            }
        }
    }
}

由于RTMP在0缓冲下,延迟在200-400毫秒区间,常规的对延迟不是非常苛刻的场景下,足够用了。


相关文章
|
27天前
|
XML C# 数据格式
掌握了在Windows平台上查看DLL依赖的方法
掌握了在Windows平台上查看DLL依赖的方法
173 4
|
2月前
|
监控 Windows
Windows平台RTSP|RTMP播放器如何实时调节音量
我们在做Windows平台RTSP、RTMP播放器的时候,有这样的技术需求,特别是多路监控的时候,并不是每一路audio都需要播放出来的,所以,这时候,需要有针对音量调节的设计
|
1月前
|
NoSQL Shell MongoDB
Windows 平台安装 MongoDB
10月更文挑战第10天
42 0
Windows 平台安装 MongoDB
|
2月前
|
监控 C# 块存储
Windows平台RTSP|RTMP播放器如何叠加OSD文字
做Windows平台RTSP|RTMP播放器的时候,特别是多路播放场景下,开发者希望可以给每一路RTSP或RTMP流添加个额外的OSD台标,以区分不同的设备信息(比如添加摄像头所在位置),本文主要探讨,如何动态添加OSD台标。
Windows平台RTSP|RTMP播放器如何叠加OSD文字
|
2月前
|
Linux Android开发 iOS开发
Windows平台RTSP|RTMP播放器如何实现实时录像功能
Windows平台RTSP、RTMP播放器实时录像接口设计,实际上,除了Windows平台,我们Linux、Android、iOS平台也是一样的设计,单纯的录像模块,如果做的全面,也不是一两个接口可以搞定的
|
1月前
|
并行计算 开发工具 异构计算
在Windows平台使用源码编译和安装PyTorch3D指定版本
【10月更文挑战第6天】在 Windows 平台上,编译和安装指定版本的 PyTorch3D 需要先安装 Python、Visual Studio Build Tools 和 CUDA(如有需要),然后通过 Git 获取源码。建议创建虚拟环境以隔离依赖,并使用 `pip` 安装所需库。最后,在源码目录下运行 `python setup.py install` 进行编译和安装。完成后即可在 Python 中导入 PyTorch3D 使用。
165 0
WK
|
3月前
|
存储 JavaScript 前端开发
如何在Windows平台上手micro:bit
micro:bit是一款口袋大小的可编程计算机,使用ARM处理器,跨平台兼容性强,适用于青少年学习编程。通过USB接口轻松连接Windows电脑,找到“MICROBIT”文件夹开始互动。提供MakeCode(支持拖拽编程及JavaScript)和MicroPython平台,满足不同编程需求。创建项目后,下载.hex文件至micro:bit,即可运行程序,展现创意成果。
WK
59 1
|
3月前
|
编解码 开发工具 数据安全/隐私保护
如何快速实现Windows平台屏幕摄像头采集并推送RTMP|轻量级RTSP服务能力?
一个好的推送模块,除了实现高效率的编码传输外,还要有好的音视频采集机制和灵活的架构支持,便于后期功能扩展,比如实时快照、预览、实时录像等。除此之外,还要有好的交互机制(比如envent callback)、低延迟和长期运行稳定的性能。
|
7天前
|
监控 安全 网络安全
Windows Server管理:配置与管理技巧
Windows Server管理:配置与管理技巧
31 3
|
10天前
|
存储 安全 网络安全
Windows Server 本地安全策略
由于广泛使用及历史上存在的漏洞,Windows服务器成为黑客和恶意行为者的主要攻击目标。这些系统通常存储敏感数据并支持关键服务,因此组织需优先缓解风险,保障业务的完整性和连续性。常见的威胁包括勒索软件、拒绝服务攻击、内部威胁、恶意软件感染等。本地安全策略是Windows操作系统中用于管理计算机本地安全性设置的工具,主要包括用户账户策略、安全选项、安全设置等。实施强大的安全措施,如定期补丁更新、网络分段、入侵检测系统、数据加密等,对于加固Windows服务器至关重要。