Windows平台RTMP直播推送集成简要说明
好多開發者在集成大牛直播SDK (官方)的Windows平臺RTMP推送模塊時嚇一跳,怎么這么多接口?本文做個簡單的拆分:
初始化
初始化之前,如需設置日志路徑,調用NTSmartLog.NT_SL_SetPath(log_path); 設置日志存放路徑。
設置過后,調用NT_PB_Init()接口,完成SDK初始化動作,注意,哪怕多實例推送,Init()接口也僅需調一次,同理,UnInit()接口也是。
然后,代碼會判斷系統是不是支持WR模式采集窗口,WR這種只有Win10高版本的才支持,如果不需要用到采集窗口,這個接口可忽略。
/** 檢查是否支持WR方式采集窗口* is_supported: 輸出參數, 輸出1表示支持, 0表示不支持* 注意:這個需要win10較高版本才支持* 成功返回 NT_ERC_OK*/[DllImport(@"SmartPublisherSDK.dll")]public static extern UInt32 NT_PB_IsWRCaptureWindowSupported(ref Int32 is_supported);再往下,是遍歷系統支持的硬解、攝像頭等信息,比如LoadHWVideoEncoderInfos():
private void LoadHWVideoEncoderInfos(){ hw_video_encoder_infos_.Clear();Int32 count = 0;UInt32 ret = NTSmartPublisherSDK.NT_PB_GetHWVideoEncoderInfoCount(ref count);if (NTBaseCodeDefine.NT_ERC_OK == ret && count > 0){IntPtr ptr_hw_video_encoder_infos = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(NT_PB_HWVideoEncoderInfo)) * count);Int32 out_count = 0;ret = NTSmartPublisherSDK.NT_PB_GetHWVideoEncoderInfos(ptr_hw_video_encoder_infos, count, ref out_count);if (ret != NTBaseCodeDefine.NT_ERC_OK || out_count < 1){hw_video_encoder_infos_.Clear();}else{for (int i = 0; i < out_count; i++){NT_PB_HWVideoEncoderInfo hw_video_encoder_info = (NT_PB_HWVideoEncoderInfo)Marshal.PtrToStructure(ptr_hw_video_encoder_infos + i * Marshal.SizeOf(typeof(NT_PB_HWVideoEncoderInfo)), typeof(NT_PB_HWVideoEncoderInfo));hw_video_encoder_infos_.Add(hw_video_encoder_info);}}Marshal.FreeHGlobal(ptr_hw_video_encoder_infos);}} if (hw_video_encoder_infos_.Count > 0){EnableHWVideoEncoderControls(true);FillVideoEncodersControl((uint)NTCommonMediaDefine.NT_MEDIA_CODEC_ID.NT_MEDIA_CODEC_ID_H264);}緊接著是Audio和camera相關:
int auido_devices = 0;if (NTBaseCodeDefine.NT_ERC_OK == NTSmartPublisherSDK.NT_PB_GetAuidoInputDeviceNumber(ref auido_devices)){if (auido_devices > 0){btn_check_auido_mic_input_.Enabled = true;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);combox_auido_input_devices_.Items.Add(name);}combox_auido_input_devices_.SelectedIndex = 0;}} publisher_handle_ = new IntPtr();region_choose_tool_handle_ = new IntPtr();win_form_wnd_ = GetForegroundWindow();cameras_ = new List<CameraInfo>();btn_check_video_bitrate_.CheckState = CheckState.Checked;if (IsCanCaptureSpeaker()){btn_check_auido_speaker_input_.Enabled = true;}else{btn_check_auido_speaker_input_.Enabled = false;}if (btn_check_auido_mic_input_.Checked|| btn_check_auido_speaker_input_.Checked){btn_check_speex_encoder_.Enabled = true;edit_speex_quality_.Enabled = true;btn_check_noise_suppression_.Enabled = true;btn_check_agc_.Enabled = true;btn_check_vad_.Enabled = true;}if ( btn_check_auido_mic_input_.Checked&& btn_check_auido_speaker_input_.Checked){btn_check_echo_cancel_.Enabled = false;edit_echo_delay_.Enabled = false;}edit_audio_input_volume_.Text = "1.0";edit_audio_speaker_input_volume_.Text = "1.0";FillCameraInfo();InitCameraControl();OpenPublisherHandle()
OpenPublisherHandle()主要是確認選擇數據源類型,然后獲取推送句柄,等待做下一步的操作。
選擇video option和 audio option
// 視頻UInt32 video_option = (UInt32)NTSmartPublisherDefine.NT_PB_E_VIDEO_OPTION.NT_PB_E_VIDEO_OPTION_NO_VIDEO;if (btn_desktop_camera_switch.Checked|| btn_camera_overlay_to_desktop.Checked|| btn_desktop_overlay_to_camera.Checked){// 使用疊加模式video_option = (UInt32)NTSmartPublisherDefine.NT_PB_E_VIDEO_OPTION.NT_PB_E_VIDEO_OPTION_LAYER;}else if (btn_check_window_input_.Checked){video_option = (UInt32)NTSmartPublisherDefine.NT_PB_E_VIDEO_OPTION.NT_PB_E_VIDEO_OPTION_WINDOW;}else if (btn_check_desktop_input_.Checked && btn_check_scale_desktop_.Checked){// 使用疊加模式來實現縮放video_option = (UInt32)NTSmartPublisherDefine.NT_PB_E_VIDEO_OPTION.NT_PB_E_VIDEO_OPTION_LAYER;}else if (btn_check_desktop_input_.Checked && !btn_check_scale_desktop_.Checked){//屏幕模式video_option = (UInt32)NTSmartPublisherDefine.NT_PB_E_VIDEO_OPTION.NT_PB_E_VIDEO_OPTION_SCREEN;}else if (btn_check_camera_input_.Checked){//攝像頭模式video_option = (UInt32)NTSmartPublisherDefine.NT_PB_E_VIDEO_OPTION.NT_PB_E_VIDEO_OPTION_CAMERA;}// 音頻UInt32 audio_option = (UInt32)NTSmartPublisherDefine.NT_PB_E_AUDIO_OPTION.NT_PB_E_AUDIO_OPTION_NO_AUDIO;if (btn_check_auido_mic_input_.Checked&& btn_check_auido_speaker_input_.Checked){audio_option = (UInt32)NTSmartPublisherDefine.NT_PB_E_AUDIO_OPTION.NT_PB_E_AUDIO_OPTION_CAPTURE_MIC_SPEAKER_MIXER;}else if (btn_check_auido_mic_input_.Checked){//麥克風模式audio_option = (UInt32)NTSmartPublisherDefine.NT_PB_E_AUDIO_OPTION.NT_PB_E_AUDIO_OPTION_CAPTURE_MIC;}else if (btn_check_auido_speaker_input_.Checked){//揚聲器模式audio_option = (UInt32)NTSmartPublisherDefine.NT_PB_E_AUDIO_OPTION.NT_PB_E_AUDIO_OPTION_CAPTURE_SPEAKER;}調用Open接口獲取publisher handle,然設置event callback
if (NTBaseCodeDefine.NT_ERC_OK != NTSmartPublisherSDK.NT_PB_Open(out publisher_handle_,video_option, audio_option, 0, IntPtr.Zero)){MessageBox.Show("Call open failed!");return false;}if (publisher_handle_ != IntPtr.Zero){pb_event_call_back_ = new NT_PB_SDKEventCallBack(PbSDKEventCallBack);NTSmartPublisherSDK.NT_PB_SetEventCallBack(publisher_handle_, win_form_wnd_, pb_event_call_back_);return true;}else{return false;}event callback相關ID
/*事件ID*/public enum NT_PB_E_EVENT_ID : uint{NT_PB_E_EVENT_ID_BASE = NTBaseCodeDefine.NT_EVENT_ID_SMART_PUBLISHER_SDK,NT_PB_E_EVENT_ID_CONNECTING = NT_PB_E_EVENT_ID_BASE | 0x2, /*連接中, param5表示推送URL */NT_PB_E_EVENT_ID_CONNECTION_FAILED = NT_PB_E_EVENT_ID_BASE | 0x3, /*連接失敗, param5表示推送URL*/NT_PB_E_EVENT_ID_CONNECTED = NT_PB_E_EVENT_ID_BASE | 0x4, /*已連接, param5表示推送URL*/NT_PB_E_EVENT_ID_DISCONNECTED = NT_PB_E_EVENT_ID_BASE | 0x5, /*斷開連接, param5表示推送URL*/NT_PB_E_EVENT_ID_RECORDER_START_NEW_FILE = NT_PB_E_EVENT_ID_BASE | 0x7, /*錄像寫入新文件, param5表示錄像文件名*/NT_PB_E_EVENT_ID_ONE_RECORDER_FILE_FINISHED = NT_PB_E_EVENT_ID_BASE | 0x8, /*一個錄像文件完成, param5表示錄像文件名*/NT_PB_E_EVENT_ID_CAPTURE_WINDOW_INVALID = NT_PB_E_EVENT_ID_BASE | 0xd, /*捕獲窗口時,如果窗口句柄無效則通知用戶, param1為窗口句柄*/NT_PB_E_EVENT_ID_RTSP_URL = NT_PB_E_EVENT_ID_BASE | 0xe, /* 通知rtsp url, param1表示rtsp server handle, param5 表示rtsp url */NT_PB_E_EVENT_ID_PUSH_RTSP_SERVER_RESPONSE_STATUS_CODE = NT_PB_E_EVENT_ID_BASE | 0xf, /* 推送rtsp時服務端相應的status code上報,目前只上報401, param1表示status code, param5表示推送URL */NT_PB_E_EVENT_ID_PUSH_RTSP_SERVER_NOT_SUPPORT = NT_PB_E_EVENT_ID_BASE | 0x10, /* 推送rtsp時服務器不支持rtsp推送, param5表示推送URL */}SetCommonOptionToPublisherSDK()
SetCommonOptionToPublisherSDK()主要是指定具體采集的音視頻數據類型,比如攝像頭數據、屏幕數據、攝像頭和屏幕疊加后的數據(以層級模式實現)、窗口等,這塊比較復雜,好在作為SDK調用者,你只要搞清楚你需要采集的類型,直接移植就可以了。
// 視頻相關設置if (btn_desktop_camera_switch.Checked|| btn_camera_overlay_to_desktop.Checked|| btn_desktop_overlay_to_camera.Checked|| btn_check_desktop_input_.Checked|| btn_check_window_input_.Checked|| btn_check_camera_input_.Checked){if (btn_desktop_camera_switch.Checked){//攝像頭和屏幕相互切換int left = Int32.Parse(edit_clip_left_.Text);int top = Int32.Parse(edit_clip_top_.Text);int w = Int32.Parse(edit_clip_width_.Text);int h = Int32.Parse(edit_clip_height_.Text);// 有一個是0, 就使用全屏if (w == 0 || h == 0){left = 0;top = 0;w = screenArea_.Width;h = screenArea_.Height;}else{// 保證4字節對齊w = NT_ByteAlign(w, 4);h = NT_ByteAlign(h, 4);}NTSmartPublisherSDK.NT_PB_ClearLayersConfig(publisher_handle_, 0,0, IntPtr.Zero);// 第0層填充RGBA矩形, 目的是保證幀率, 顏色就填充全黑int red = 0;int green = 0;int blue = 0;int alpha = 255;NT_PB_RGBARectangleLayerConfig rgba_layer_c0 = new NT_PB_RGBARectangleLayerConfig();rgba_layer_c0.base_.type_ = (Int32)NTSmartPublisherDefine.NT_PB_E_LAYER_TYPE.NT_PB_E_LAYER_TYPE_RGBA_RECTANGLE;rgba_layer_c0.base_.index_ = 0;rgba_layer_c0.base_.enable_ = 1;rgba_layer_c0.base_.region_.x_ = left;rgba_layer_c0.base_.region_.y_ = top;rgba_layer_c0.base_.region_.width_ = w;rgba_layer_c0.base_.region_.height_ = h;rgba_layer_c0.base_.offset_ = Marshal.OffsetOf(rgba_layer_c0.GetType(), "base_").ToInt32();rgba_layer_c0.base_.cb_size_ = (uint)Marshal.SizeOf(rgba_layer_c0);rgba_layer_c0.red_ = System.BitConverter.GetBytes(red)[0];rgba_layer_c0.green_ = System.BitConverter.GetBytes(green)[0];rgba_layer_c0.blue_ = System.BitConverter.GetBytes(blue)[0];rgba_layer_c0.alpha_ = System.BitConverter.GetBytes(alpha)[0];IntPtr rgba_conf = Marshal.AllocHGlobal(Marshal.SizeOf(rgba_layer_c0));Marshal.StructureToPtr(rgba_layer_c0, rgba_conf, true);UInt32 rgba_r = NTSmartPublisherSDK.NT_PB_AddLayerConfig(publisher_handle_, 0,rgba_conf, (int)NTSmartPublisherDefine.NT_PB_E_LAYER_TYPE.NT_PB_E_LAYER_TYPE_RGBA_RECTANGLE,0, IntPtr.Zero);Console.WriteLine("[攝像頭和屏幕相互切換] NT_PB_AddLayerConfig, rgba: " + rgba_r + Environment.NewLine);Marshal.FreeHGlobal(rgba_conf);//第一層:攝像頭NT_PB_CameraLayerConfigV2 camera_layer_c1 = new NT_PB_CameraLayerConfigV2();CameraInfo camera = cameras_[cur_sel_camera_index_];NT_PB_VideoCaptureCapability cap = camera.capabilities_[cur_sel_camera_resolutions_index_];camera_layer_c1.device_unique_id_utf8_ = camera.id_;camera_layer_c1.base_.type_ = (Int32)NTSmartPublisherDefine.NT_PB_E_LAYER_TYPE.NT_PB_E_LAYER_TYPE_CAMERA;camera_layer_c1.base_.index_ = 1;camera_layer_index_ = camera_layer_c1.base_.index_;camera_layer_c1.base_.enable_ = 1;camera_layer_c1.base_.region_.x_ = left;camera_layer_c1.base_.region_.y_ = top;camera_layer_c1.base_.region_.width_ = w;camera_layer_c1.base_.region_.height_ = h;if (btn_check_flip_horizontal_camera_.Checked){camera_layer_c1.is_flip_horizontal_ = 1;}else{camera_layer_c1.is_flip_horizontal_ = 0;}if (btn_check_flip_vertical_camera_.Checked){camera_layer_c1.is_flip_vertical_ = 1;}else{camera_layer_c1.is_flip_vertical_ = 0;}// 這種疊加模式下不要旋轉,否則變形厲害, 要么就定好一個角度,調整寬高,但不要動態旋轉camera_layer_c1.rotate_degress_ = 0;camera_layer_c1.base_.offset_ = Marshal.OffsetOf(camera_layer_c1.GetType(), "base_").ToInt32(); //offsetof(T, base_);camera_layer_c1.base_.cb_size_ = (uint)Marshal.SizeOf(camera_layer_c1);IntPtr cmr_conf = Marshal.AllocHGlobal(Marshal.SizeOf(camera_layer_c1));Marshal.StructureToPtr(camera_layer_c1, cmr_conf, true);UInt32 c_r = NTSmartPublisherSDK.NT_PB_AddLayerConfig(publisher_handle_, 0,cmr_conf, (int)NTSmartPublisherDefine.NT_PB_E_LAYER_TYPE.NT_PB_E_LAYER_TYPE_CAMERA,0, IntPtr.Zero);Marshal.FreeHGlobal(cmr_conf);//第二層NT_PB_ScreenLayerConfig screen_layer_c2 = new NT_PB_ScreenLayerConfig();screen_layer_c2.base_.type_ = (Int32)NTSmartPublisherDefine.NT_PB_E_LAYER_TYPE.NT_PB_E_LAYER_TYPE_SCREEN;screen_layer_c2.base_.index_ = 2;screen_layer_index_ = screen_layer_c2.base_.index_;screen_layer_c2.base_.enable_ = 1;screen_layer_c2.base_.region_.x_ = left;screen_layer_c2.base_.region_.y_ = top;screen_layer_c2.base_.region_.width_ = w;screen_layer_c2.base_.region_.height_ = h;screen_layer_c2.base_.offset_ = Marshal.OffsetOf(screen_layer_c2.GetType(), "base_").ToInt32(); //offsetof(T, base_);screen_layer_c2.base_.cb_size_ = (uint)Marshal.SizeOf(screen_layer_c2);screen_layer_c2.clip_region_.x_ = left;screen_layer_c2.clip_region_.y_ = top;screen_layer_c2.clip_region_.width_ = w;screen_layer_c2.clip_region_.height_ = h;screen_layer_c2.reserve_ = IntPtr.Zero;IntPtr scr_conf = Marshal.AllocHGlobal(Marshal.SizeOf(screen_layer_c2));Marshal.StructureToPtr(screen_layer_c2, scr_conf, true);UInt32 s_r = NTSmartPublisherSDK.NT_PB_AddLayerConfig(publisher_handle_, 0,scr_conf, (int)NTSmartPublisherDefine.NT_PB_E_LAYER_TYPE.NT_PB_E_LAYER_TYPE_SCREEN,0, IntPtr.Zero);Marshal.FreeHGlobal(scr_conf);// 第三層填充RGBA矩形, 目的是保證幀率, 顏色就填充全黑red = Int32.Parse(edit_rgba_rect_layer_red_.Text);red = ClipIntValue(red, 0, 255);green = Int32.Parse(edit_rgba_rect_layer_green_.Text);green = ClipIntValue(green, 0, 255);blue = Int32.Parse(edit_rgba_rect_layer_blue_.Text);blue = ClipIntValue(blue, 0, 255);alpha = Int32.Parse(edit_rgba_rect_layer_alpha_.Text);alpha = ClipIntValue(alpha, 0, 255);NT_PB_RGBARectangleLayerConfig rgba_layer_c3 = new NT_PB_RGBARectangleLayerConfig();rgba_layer_c3.base_.type_ = (Int32)NTSmartPublisherDefine.NT_PB_E_LAYER_TYPE.NT_PB_E_LAYER_TYPE_RGBA_RECTANGLE;rgba_layer_c3.base_.index_ = 3;rgba_layer_index_ = rgba_layer_c3.base_.index_;rgba_layer_c3.base_.enable_ = 1;rgba_layer_c3.base_.region_.x_ = left; //這個只是demo演示,實際以需要遮蓋位置為準rgba_layer_c3.base_.region_.y_ = top;rgba_layer_c3.base_.region_.width_ = 160;rgba_layer_c3.base_.region_.height_ = 160;rgba_layer_c3.base_.offset_ = Marshal.OffsetOf(rgba_layer_c3.GetType(), "base_").ToInt32();rgba_layer_c3.base_.cb_size_ = (uint)Marshal.SizeOf(rgba_layer_c3);rgba_layer_c3.red_ = System.BitConverter.GetBytes(red)[0];rgba_layer_c3.green_ = System.BitConverter.GetBytes(green)[0];rgba_layer_c3.blue_ = System.BitConverter.GetBytes(blue)[0];rgba_layer_c3.alpha_ = System.BitConverter.GetBytes(alpha)[0];IntPtr rgba_conf_3 = Marshal.AllocHGlobal(Marshal.SizeOf(rgba_layer_c3));Marshal.StructureToPtr(rgba_layer_c3, rgba_conf_3, true);UInt32 rgba_r_3 = NTSmartPublisherSDK.NT_PB_AddLayerConfig(publisher_handle_, 0,rgba_conf_3, (int)NTSmartPublisherDefine.NT_PB_E_LAYER_TYPE.NT_PB_E_LAYER_TYPE_RGBA_RECTANGLE,0, IntPtr.Zero);Console.WriteLine("NT_PB_AddLayerConfig, rgba: " + rgba_r_3 + Environment.NewLine);Marshal.FreeHGlobal(rgba_conf_3);// 第四層填充png水印(注意,實時開啟、關閉水印,是根據圖層的index來的,如此demo,png水印的index為4)// 如果有圖片,增加圖片層if (!String.IsNullOrEmpty(image_layer_file_name_utf8_)&& image_layer_width_ > 0&& image_layer_height_ > 0){NT_PB_ImageLayerConfig image_layer_c4 = new NT_PB_ImageLayerConfig();image_layer_c4.base_.type_ = (Int32)NTSmartPublisherDefine.NT_PB_E_LAYER_TYPE.NT_PB_E_LAYER_TYPE_IMAGE;image_layer_c4.base_.index_ = 4;image_layer_index_ = image_layer_c4.base_.index_;image_layer_c4.base_.enable_ = 1;image_layer_c4.base_.region_.x_ = image_layer_left_;image_layer_c4.base_.region_.y_ = image_layer_top_;image_layer_c4.base_.region_.width_ = image_layer_width_;image_layer_c4.base_.region_.height_ = image_layer_height_;image_layer_c4.base_.offset_ = Marshal.OffsetOf(image_layer_c4.GetType(), "base_").ToInt32();image_layer_c4.base_.cb_size_ = (uint)Marshal.SizeOf(image_layer_c4);byte[] buffer1 = Encoding.Default.GetBytes(image_layer_file_name_utf8_);byte[] buffer2 = Encoding.Convert(Encoding.UTF8, Encoding.Default, buffer1, 0, buffer1.Length);string strBuffer = Encoding.Default.GetString(buffer2, 0, buffer2.Length);image_layer_c4.file_name_utf8_ = strBuffer;image_layer_c4.is_setting_background_ = 0;image_layer_c4.bk_red_ = 0;image_layer_c4.bk_green_ = 0;image_layer_c4.bk_blue_ = 0;image_layer_c4.reserve_ = 0;IntPtr image_conf = Marshal.AllocHGlobal(Marshal.SizeOf(image_layer_c4));Marshal.StructureToPtr(image_layer_c4, image_conf, true);UInt32 image_r = NTSmartPublisherSDK.NT_PB_AddLayerConfig(publisher_handle_, 0,image_conf, (int)NTSmartPublisherDefine.NT_PB_E_LAYER_TYPE.NT_PB_E_LAYER_TYPE_IMAGE,0, IntPtr.Zero);Console.WriteLine("NT_PB_AddLayerConfig, image: " + image_r + Environment.NewLine);Marshal.FreeHGlobal(image_conf);NTSmartPublisherSDK.NT_PB_SetFrameRate(publisher_handle_, UInt32.Parse(edit_frame_rate_.Text));}}else if (btn_camera_overlay_to_desktop.Checked){//攝像頭overlay到桌面int left = Int32.Parse(edit_clip_left_.Text);int top = Int32.Parse(edit_clip_top_.Text);int w = Int32.Parse(edit_clip_width_.Text);int h = Int32.Parse(edit_clip_height_.Text);// 有一個是0, 就使用全屏if ( w == 0 || h == 0 ){left = 0;top = 0;w = screenArea_.Width;h = screenArea_.Height;}else{// 保證4字節對齊w = NT_ByteAlign(w, 4);h = NT_ByteAlign(h, 4);}//第一層:屏幕NT_PB_ScreenLayerConfig screen_layer_c0 = new NT_PB_ScreenLayerConfig();screen_layer_c0.base_.type_ = (Int32)NTSmartPublisherDefine.NT_PB_E_LAYER_TYPE.NT_PB_E_LAYER_TYPE_SCREEN;screen_layer_c0.base_.index_ = 0;screen_layer_index_ = screen_layer_c0.base_.index_;screen_layer_c0.base_.enable_ = 1;screen_layer_c0.base_.region_.x_ = left;screen_layer_c0.base_.region_.y_ = top;screen_layer_c0.base_.region_.width_ = w;screen_layer_c0.base_.region_.height_ = h;screen_layer_c0.base_.offset_ = Marshal.OffsetOf(screen_layer_c0.GetType(), "base_").ToInt32(); //offsetof(T, base_);screen_layer_c0.base_.cb_size_ = (uint)Marshal.SizeOf(screen_layer_c0);screen_layer_c0.clip_region_.x_ = left;screen_layer_c0.clip_region_.y_ = top;screen_layer_c0.clip_region_.width_ = w;screen_layer_c0.clip_region_.height_ = h;screen_layer_c0.reserve_ = IntPtr.Zero;NTSmartPublisherSDK.NT_PB_ClearLayersConfig(publisher_handle_, 0,0, IntPtr.Zero);IntPtr scr_conf = Marshal.AllocHGlobal(Marshal.SizeOf(screen_layer_c0));Marshal.StructureToPtr(screen_layer_c0, scr_conf, true);UInt32 s_r = NTSmartPublisherSDK.NT_PB_AddLayerConfig(publisher_handle_, 0,scr_conf, (int)NTSmartPublisherDefine.NT_PB_E_LAYER_TYPE.NT_PB_E_LAYER_TYPE_SCREEN,0, IntPtr.Zero);Marshal.FreeHGlobal(scr_conf);//第二層:攝像頭if (-1 != cur_sel_camera_index_){int c_l = Int32.Parse(edit_camera_overlay_left_.Text);int c_t = Int32.Parse(edit_camera_overlay_top_.Text);int c_w = Int32.Parse(edit_camera_overlay_width_.Text);int c_h = Int32.Parse(edit_camera_overlay_height_.Text);if (c_w == 0){c_w = w / 2;}if (c_h == 0){c_h = h / 2;}ctos_camera_layer_c1_ = new NT_PB_CameraLayerConfigV2();CameraInfo camera = cameras_[cur_sel_camera_index_];NT_PB_VideoCaptureCapability cap = camera.capabilities_[cur_sel_camera_resolutions_index_];ctos_camera_layer_c1_.device_unique_id_utf8_ = camera.id_;ctos_camera_layer_c1_.base_.type_ = (Int32)NTSmartPublisherDefine.NT_PB_E_LAYER_TYPE.NT_PB_E_LAYER_TYPE_CAMERA;ctos_camera_layer_c1_.base_.index_ = 1;camera_layer_index_ = ctos_camera_layer_c1_.base_.index_;ctos_camera_layer_c1_.base_.enable_ = 1;ctos_camera_layer_c1_.base_.region_.x_ = c_l;ctos_camera_layer_c1_.base_.region_.y_ = c_t;ctos_camera_layer_c1_.base_.region_.width_ = c_w;ctos_camera_layer_c1_.base_.region_.height_ = c_h;if (btn_check_flip_horizontal_camera_.Checked){ctos_camera_layer_c1_.is_flip_horizontal_ = 1;}else{ctos_camera_layer_c1_.is_flip_horizontal_ = 0;}if (btn_check_flip_vertical_camera_.Checked){ctos_camera_layer_c1_.is_flip_vertical_ = 1;}else{ctos_camera_layer_c1_.is_flip_vertical_ = 0;}ctos_camera_layer_c1_.rotate_degress_ = GetCameraRotateDegress();ctos_camera_layer_c1_.base_.offset_ = Marshal.OffsetOf(ctos_camera_layer_c1_.GetType(), "base_").ToInt32(); //offsetof(T, base_);ctos_camera_layer_c1_.base_.cb_size_ = (uint)Marshal.SizeOf(ctos_camera_layer_c1_);IntPtr cmr_conf = Marshal.AllocHGlobal(Marshal.SizeOf(ctos_camera_layer_c1_));Marshal.StructureToPtr(ctos_camera_layer_c1_, cmr_conf, true);UInt32 c_r = NTSmartPublisherSDK.NT_PB_AddLayerConfig(publisher_handle_, 0,cmr_conf, (int)NTSmartPublisherDefine.NT_PB_E_LAYER_TYPE.NT_PB_E_LAYER_TYPE_CAMERA,0, IntPtr.Zero);Marshal.FreeHGlobal(cmr_conf);}NTSmartPublisherSDK.NT_PB_SetFrameRate(publisher_handle_, UInt32.Parse(edit_frame_rate_.Text));}else if (btn_desktop_overlay_to_camera.Checked){//桌面overlay到攝像頭//第一層:攝像頭if (-1 != cur_sel_camera_index_&& -1 != cur_sel_camera_resolutions_index_&& -1 != cur_sel_camera_frame_rate_index_){NT_PB_CameraLayerConfigV2 camera_layer_c0 = new NT_PB_CameraLayerConfigV2();CameraInfo camera = cameras_[cur_sel_camera_index_];NT_PB_VideoCaptureCapability cap = camera.capabilities_[cur_sel_camera_resolutions_index_];camera_layer_c0.device_unique_id_utf8_ = camera.id_;camera_layer_c0.base_.type_ = (Int32)NTSmartPublisherDefine.NT_PB_E_LAYER_TYPE.NT_PB_E_LAYER_TYPE_CAMERA;camera_layer_c0.base_.index_ = 0;camera_layer_index_ = camera_layer_c0.base_.index_;camera_layer_c0.base_.enable_ = 1;camera_layer_c0.base_.region_.x_ = 0;camera_layer_c0.base_.region_.y_ = 0;camera_layer_c0.base_.region_.width_ = cap.width_;camera_layer_c0.base_.region_.height_ = cap.height_;if (btn_check_flip_horizontal_camera_.Checked){camera_layer_c0.is_flip_horizontal_ = 1;}else{camera_layer_c0.is_flip_horizontal_ = 0;}if (btn_check_flip_vertical_camera_.Checked){camera_layer_c0.is_flip_vertical_ = 1;}else{camera_layer_c0.is_flip_vertical_ = 0;}// 這種疊加模式下不要旋轉,否則變形厲害, 要么就定好一個角度,調整寬高,但不要動態旋轉camera_layer_c0.rotate_degress_ = 0;camera_layer_c0.base_.offset_ = Marshal.OffsetOf(camera_layer_c0.GetType(), "base_").ToInt32(); //offsetof(T, base_);camera_layer_c0.base_.cb_size_ = (uint)Marshal.SizeOf(camera_layer_c0);NTSmartPublisherSDK.NT_PB_ClearLayersConfig(publisher_handle_, 0,0, IntPtr.Zero);IntPtr cmr_conf = Marshal.AllocHGlobal(Marshal.SizeOf(camera_layer_c0));Marshal.StructureToPtr(camera_layer_c0, cmr_conf, true);UInt32 r = NTSmartPublisherSDK.NT_PB_AddLayerConfig(publisher_handle_, 0,cmr_conf, (int)NTSmartPublisherDefine.NT_PB_E_LAYER_TYPE.NT_PB_E_LAYER_TYPE_CAMERA,0, IntPtr.Zero);Marshal.FreeHGlobal(cmr_conf);//第二層:屏幕NT_PB_ScreenLayerConfig screen_layer_c1 = new NT_PB_ScreenLayerConfig();screen_layer_c1.base_.type_ = (Int32)NTSmartPublisherDefine.NT_PB_E_LAYER_TYPE.NT_PB_E_LAYER_TYPE_SCREEN;screen_layer_c1.base_.index_ = 1;screen_layer_index_ = screen_layer_c1.base_.index_;screen_layer_c1.base_.enable_ = 1;screen_layer_c1.base_.region_.x_ = 0;screen_layer_c1.base_.region_.y_ = 0;screen_layer_c1.base_.region_.width_ = cap.width_ / 2;screen_layer_c1.base_.region_.height_ = cap.height_ / 2;screen_layer_c1.base_.offset_ = Marshal.OffsetOf(screen_layer_c1.GetType(), "base_").ToInt32(); //offsetof(T, base_);screen_layer_c1.base_.cb_size_ = (uint)Marshal.SizeOf(screen_layer_c1);screen_layer_c1.clip_region_.x_ = 0;screen_layer_c1.clip_region_.y_ = 0;screen_layer_c1.clip_region_.width_ = cap.width_ / 2;screen_layer_c1.clip_region_.height_ = cap.height_ / 2;screen_layer_c1.reserve_ = IntPtr.Zero;IntPtr scr_conf = Marshal.AllocHGlobal(Marshal.SizeOf(screen_layer_c1));Marshal.StructureToPtr(screen_layer_c1, scr_conf, true);UInt32 s_r = NTSmartPublisherSDK.NT_PB_AddLayerConfig(publisher_handle_, 0,scr_conf, (int)NTSmartPublisherDefine.NT_PB_E_LAYER_TYPE.NT_PB_E_LAYER_TYPE_SCREEN,0, IntPtr.Zero);Marshal.FreeHGlobal(scr_conf);}NTSmartPublisherSDK.NT_PB_SetFrameRate(publisher_handle_, (uint)(cur_sel_camera_frame_rate_index_ + 1));}else if (btn_check_desktop_input_.Checked && btn_check_scale_desktop_.Checked){int left = 0;int top = 0;int w = 0;int h = 0;int scale_w = 0;int scale_h = 0;GetScreenScaleConfigInfo(ref left, ref top, ref w, ref h, ref scale_w, ref scale_h);NTSmartPublisherSDK.NT_PB_ClearLayersConfig(publisher_handle_, 0,0, IntPtr.Zero);// 第0層填充RGBA矩形, 目的是保證幀率, 顏色就填充全黑int red = 0;int green = 0;int blue = 0; int alpha = 255;NT_PB_RGBARectangleLayerConfig rgba_layer_c0 = new NT_PB_RGBARectangleLayerConfig();rgba_layer_c0.base_.type_ = (Int32)NTSmartPublisherDefine.NT_PB_E_LAYER_TYPE.NT_PB_E_LAYER_TYPE_RGBA_RECTANGLE;rgba_layer_c0.base_.index_ = 0;rgba_layer_index_ = rgba_layer_c0.base_.index_;rgba_layer_c0.base_.enable_ = 1;rgba_layer_c0.base_.region_.x_ = 0;rgba_layer_c0.base_.region_.y_ = 0;rgba_layer_c0.base_.region_.width_ = scale_w;rgba_layer_c0.base_.region_.height_ = scale_h;rgba_layer_c0.base_.offset_ = Marshal.OffsetOf(rgba_layer_c0.GetType(), "base_").ToInt32();rgba_layer_c0.base_.cb_size_ = (uint)Marshal.SizeOf(rgba_layer_c0);rgba_layer_c0.red_ = 0;rgba_layer_c0.green_ = 0;rgba_layer_c0.blue_ = 0;rgba_layer_c0.alpha_ = 255;IntPtr rgba_conf_0 = Marshal.AllocHGlobal(Marshal.SizeOf(rgba_layer_c0));Marshal.StructureToPtr(rgba_layer_c0, rgba_conf_0, true);UInt32 rgba_r_0 = NTSmartPublisherSDK.NT_PB_AddLayerConfig(publisher_handle_, 0,rgba_conf_0, (int)NTSmartPublisherDefine.NT_PB_E_LAYER_TYPE.NT_PB_E_LAYER_TYPE_RGBA_RECTANGLE,0, IntPtr.Zero);Console.WriteLine("NT_PB_AddLayerConfig, rgba: " + rgba_r_0 + Environment.NewLine);Marshal.FreeHGlobal(rgba_conf_0);//第1層NT_PB_ScreenLayerConfigV2 screen_layer_c1 = new NT_PB_ScreenLayerConfigV2();screen_layer_c1.base_.type_ = (Int32)NTSmartPublisherDefine.NT_PB_E_LAYER_TYPE.NT_PB_E_LAYER_TYPE_SCREEN;screen_layer_c1.base_.index_ = 1;screen_layer_index_ = screen_layer_c1.base_.index_;screen_layer_c1.base_.enable_ = checkbox_black_screen_.Checked?0:1;screen_layer_c1.base_.region_.x_ = left;screen_layer_c1.base_.region_.y_ = top;screen_layer_c1.base_.region_.width_ = scale_w;screen_layer_c1.base_.region_.height_ = scale_h;screen_layer_c1.base_.offset_ = Marshal.OffsetOf(screen_layer_c1.GetType(), "base_").ToInt32(); //offsetof(T, base_);screen_layer_c1.base_.cb_size_ = (uint)Marshal.SizeOf(screen_layer_c1);screen_layer_c1.clip_region_.x_ = left;screen_layer_c1.clip_region_.y_ = top;screen_layer_c1.clip_region_.width_ = w;screen_layer_c1.clip_region_.height_ = h;screen_layer_c1.reserve1_ = IntPtr.Zero;screen_layer_c1.reserve2_ = 0;screen_layer_c1.scale_filter_mode_ = 3;IntPtr scr_conf = Marshal.AllocHGlobal(Marshal.SizeOf(screen_layer_c1));Marshal.StructureToPtr(screen_layer_c1, scr_conf, true);UInt32 s_r = NTSmartPublisherSDK.NT_PB_AddLayerConfig(publisher_handle_, 0,scr_conf, (int)NTSmartPublisherDefine.NT_PB_E_LAYER_TYPE.NT_PB_E_LAYER_TYPE_SCREEN,0, IntPtr.Zero);Marshal.FreeHGlobal(scr_conf);NTSmartPublisherSDK.NT_PB_SetSleepMode(publisher_handle_, checkbox_black_screen_.Checked ? 1 : 0, 0);NTSmartPublisherSDK.NT_PB_SetFrameRate(publisher_handle_, UInt32.Parse(edit_frame_rate_.Text));}else if (btn_check_desktop_input_.Checked && !btn_check_scale_desktop_.Checked){//桌面NTSmartPublisherSDK.NT_PB_SetScreenClip(publisher_handle_,UInt32.Parse(edit_clip_left_.Text),UInt32.Parse(edit_clip_top_.Text),UInt32.Parse(edit_clip_width_.Text),UInt32.Parse(edit_clip_height_.Text));NTSmartPublisherSDK.NT_PB_SetFrameRate(publisher_handle_, UInt32.Parse(edit_frame_rate_.Text));}else if (btn_check_window_input_.Checked){if (IntPtr.Zero != cur_sel_capture_window_){NTSmartPublisherSDK.NT_PB_SetCaptureWindow(publisher_handle_, cur_sel_capture_window_);NTSmartPublisherSDK.NT_PB_SetFrameRate(publisher_handle_, UInt32.Parse(edit_frame_rate_.Text));NTSmartPublisherSDK.NT_PB_ClearLayersConfig(publisher_handle_, 0, 0, IntPtr.Zero);}}else if (btn_check_camera_input_.Checked){//攝像頭if (-1 != cur_sel_camera_index_&& -1 != cur_sel_camera_resolutions_index_&& -1 != cur_sel_camera_frame_rate_index_){CameraInfo camera = cameras_[cur_sel_camera_index_];NT_PB_VideoCaptureCapability cap = camera.capabilities_[cur_sel_camera_resolutions_index_];NTSmartPublisherSDK.NT_PB_SetVideoCaptureDeviceBaseParameter(publisher_handle_,camera.id_.ToString(), (UInt32)cap.width_, (UInt32)cap.height_);NTSmartPublisherSDK.NT_PB_SetFrameRate(publisher_handle_, (UInt32)(cur_sel_camera_frame_rate_index_ + 1));if (btn_check_flip_vertical_camera_.Checked){NTSmartPublisherSDK.NT_PB_FlipVerticalCamera(publisher_handle_, 1);}else{NTSmartPublisherSDK.NT_PB_FlipVerticalCamera(publisher_handle_, 0);}if (btn_check_flip_horizontal_camera_.Checked){NTSmartPublisherSDK.NT_PB_FlipHorizontalCamera(publisher_handle_, 1);}else{NTSmartPublisherSDK.NT_PB_FlipHorizontalCamera(publisher_handle_, 0);}Int32 degress = GetCameraRotateDegress();NTSmartPublisherSDK.NT_PB_RotateCamera(publisher_handle_, degress);}}音視頻參數設定
其他音視頻相關接口參數設定,比是否啟用DXGI, Aero模式,軟硬編碼模式,幀率關鍵幀間隔碼率等設定。
if (btn_check_dxgi_screen_capturer_.Checked){NTSmartPublisherSDK.NT_PB_EnableDXGIScreenCapturer(publisher_handle_, 1);}else{NTSmartPublisherSDK.NT_PB_EnableDXGIScreenCapturer(publisher_handle_, 0);}if (check_capture_layered_window_.Checked){NTSmartPublisherSDK.NT_PB_EnableScreenCaptureLayeredWindow(publisher_handle_, 1);}else{NTSmartPublisherSDK.NT_PB_EnableScreenCaptureLayeredWindow(publisher_handle_, 0);}if (btn_check_capturer_disable_aero_.Checked){NTSmartPublisherSDK.NT_PB_DisableAeroScreenCapturer(publisher_handle_, 1);}else{NTSmartPublisherSDK.NT_PB_DisableAeroScreenCapturer(publisher_handle_, 0);}if (btn_check_wr_way_capture_window_.Checked){NTSmartPublisherSDK.NT_PB_SetCaptureWindowWay(publisher_handle_, 2);}else{NTSmartPublisherSDK.NT_PB_SetCaptureWindowWay(publisher_handle_, 1);}int cur_video_codec_id = (int)NTCommonMediaDefine.NT_MEDIA_CODEC_ID.NT_MEDIA_CODEC_ID_H264;if (btn_check_h265_encoder_.Checked){cur_video_codec_id = (int)NTCommonMediaDefine.NT_MEDIA_CODEC_ID.NT_MEDIA_CODEC_ID_H265;}bool is_hw_encoder = false;if ( btn_check_video_hardware_encoder_.Checked){is_hw_encoder = true;}Int32 cur_sel_encoder_id = 0;Int32 cur_sel_gpu = 0;if (is_hw_encoder){int cur_sel_hw = combobox_video_encoders_.SelectedIndex;if (cur_sel_hw >= 0){cur_sel_encoder_id = Convert.ToInt32(combobox_video_encoders_.SelectedValue);cur_sel_gpu = -1;int cur_sel_hw_dev = combobox_video_hardware_encoder_devices_.SelectedIndex;if (cur_sel_hw_dev >= 0){cur_sel_gpu = Convert.ToInt32(combobox_video_hardware_encoder_devices_.SelectedValue);}}else{is_hw_encoder = false;}}if (!is_hw_encoder){if ((int)NTCommonMediaDefine.NT_MEDIA_CODEC_ID.NT_MEDIA_CODEC_ID_H264 == cur_video_codec_id){cur_sel_encoder_id = btn_check_openh264_encoder_.Checked ? 1 : 0;}}NTSmartPublisherSDK.NT_PB_SetVideoEncoder(publisher_handle_, (int)(is_hw_encoder ? 1 : 0), (int)cur_sel_encoder_id, (uint)cur_video_codec_id, (int)cur_sel_gpu);if (!btn_check_window_input_.Checked){NTSmartPublisherSDK.NT_PB_SetVideoBitRate(publisher_handle_, Int32.Parse(edit_bit_rate_.Text));}else{// 窗口的分辨率會變, 所以設置一組碼率下去Int32 frame_rate = Int32.Parse(edit_bit_rate_.Text);SetBitrateGroup(publisher_handle_, frame_rate);}NTSmartPublisherSDK.NT_PB_SetVideoQualityV2(publisher_handle_, Int32.Parse(edit_video_quality_.Text));NTSmartPublisherSDK.NT_PB_SetVideoMaxBitRate(publisher_handle_, Int32.Parse(edit_video_max_bitrate_.Text));NTSmartPublisherSDK.NT_PB_SetVideoKeyFrameInterval(publisher_handle_, Int32.Parse(edit_key_frame_.Text));if (cur_video_codec_id == (int)NTCommonMediaDefine.NT_MEDIA_CODEC_ID.NT_MEDIA_CODEC_ID_H264){int profile_sel = combox_h264_profile_.SelectedIndex;if (profile_sel != -1){NTSmartPublisherSDK.NT_PB_SetVideoEncoderProfile(publisher_handle_, profile_sel + 1);}}NTSmartPublisherSDK.NT_PB_SetVideoEncoderSpeed(publisher_handle_, Int32.Parse(edit_video_encode_speed_.Text));// 清除編碼器所有的特定的參數NTSmartPublisherSDK.NT_PB_ClearVideoEncoderSpecialOptions(publisher_handle_);if (cur_sel_encoder_id == 1){// qp_max 和 qp_min 當前只對openh264有效, 這里也就只在openh264使用的場景下設置配置值NTSmartPublisherSDK.NT_PB_SetVideoEncoderQPMax(publisher_handle_, Int32.Parse(edit_qp_max_.Text));NTSmartPublisherSDK.NT_PB_SetVideoEncoderQPMin(publisher_handle_, Int32.Parse(edit_qp_min_.Text));// openh264 配置特定參數NTSmartPublisherSDK.NT_PB_SetVideoEncoderSpecialInt32Option(publisher_handle_, "usage_type", btn_check_openh264_ppt_usage_type_.Checked ? 1 : 0);NTSmartPublisherSDK.NT_PB_SetVideoEncoderSpecialInt32Option(publisher_handle_, "rc_mode", btn_check_openh264_rc_bitrate_mode_.Checked ? 1 : 0);NTSmartPublisherSDK.NT_PB_SetVideoEncoderSpecialInt32Option(publisher_handle_, "enable_frame_skip", btn_check_openh264_frame_skip_.Checked ? 1 : 0);}else{NTSmartPublisherSDK.NT_PB_SetVideoEncoderQPMax(publisher_handle_, -1);NTSmartPublisherSDK.NT_PB_SetVideoEncoderQPMin(publisher_handle_, -1);}// 音頻相關設置if (btn_check_auido_mic_input_.Checked){int count = combox_auido_input_devices_.Items.Count;if (count != -1 && count > 0){int cur_sel = combox_auido_input_devices_.SelectedIndex;if (cur_sel != -1){NTSmartPublisherSDK.NT_PB_SetAuidoInputDeviceId(publisher_handle_, (uint)cur_sel);}}}// 只采集揚聲器時做靜音補償if (!btn_check_auido_mic_input_.Checked&& btn_check_auido_speaker_input_.Checked){NTSmartPublisherSDK.NT_PB_SetCaptureSpeakerCompensateMute(publisher_handle_, 1);}if (btn_check_speex_encoder_.Checked){NTSmartPublisherSDK.NT_PB_SetPublisherAudioCodecType(publisher_handle_, 2);NTSmartPublisherSDK.NT_PB_SetPublisherSpeexEncoderQuality(publisher_handle_, Int32.Parse(edit_speex_quality_.Text));}else{NTSmartPublisherSDK.NT_PB_SetPublisherAudioCodecType(publisher_handle_, 1);}if (btn_check_auido_mic_input_.Checked|| btn_check_auido_speaker_input_.Checked){if (btn_check_set_mute_.Checked){NTSmartPublisherSDK.NT_PB_SetMute(publisher_handle_, 1);}}if (btn_check_echo_cancel_.Checked){NTSmartPublisherSDK.NT_PB_SetEchoCancellation(publisher_handle_, 1, Int32.Parse(edit_echo_delay_.Text));}else{NTSmartPublisherSDK.NT_PB_SetEchoCancellation(publisher_handle_, 0, 0);}if (btn_check_noise_suppression_.Checked){NTSmartPublisherSDK.NT_PB_SetNoiseSuppression(publisher_handle_, 1);}else{NTSmartPublisherSDK.NT_PB_SetNoiseSuppression(publisher_handle_, 0);}if (btn_check_agc_.Checked){NTSmartPublisherSDK.NT_PB_SetAGC(publisher_handle_, 1);}else{NTSmartPublisherSDK.NT_PB_SetAGC(publisher_handle_, 0);}if (btn_check_vad_.Checked){NTSmartPublisherSDK.NT_PB_SetVAD(publisher_handle_, 1);}else{NTSmartPublisherSDK.NT_PB_SetVAD(publisher_handle_, 0);}if (btn_check_auido_mic_input_.Checked&& btn_check_auido_speaker_input_.Checked){NTSmartPublisherSDK.NT_PB_SetInputAudioVolume(publisher_handle_, 0, Convert.ToSingle(edit_audio_input_volume_.Text));NTSmartPublisherSDK.NT_PB_SetInputAudioVolume(publisher_handle_, 1, Convert.ToSingle(edit_audio_speaker_input_volume_.Text));}else if (btn_check_auido_mic_input_.Checked){NTSmartPublisherSDK.NT_PB_SetInputAudioVolume(publisher_handle_, 0, Convert.ToSingle(edit_audio_input_volume_.Text));}else if (btn_check_auido_speaker_input_.Checked){NTSmartPublisherSDK.NT_PB_SetInputAudioVolume(publisher_handle_, 0, Convert.ToSingle(edit_audio_speaker_input_volume_.Text));}獲取視頻碼率默認值,不是每個開發者都有音視頻開發背景,如果不想自行設置碼率等一些參數,可參考我們的碼率設定。
private void FillBitrateControlDefValue(){int w = 640, h = 480;int frame_rate = 5;bool is_var_bitrate = false;GetVideoConfigInfo(ref w, ref h, ref frame_rate, ref is_var_bitrate);if (btn_check_openh264_encoder_.Checked){is_var_bitrate = false;}int kbit_rate = CalBitRate(frame_rate, w, h);int max_kbit_rate = CalMaxKBitRate(frame_rate, w, h, is_var_bitrate);if (is_var_bitrate){btn_check_video_bitrate_.CheckState = CheckState.Unchecked;}else{btn_check_video_bitrate_.CheckState = CheckState.Checked;}if (is_var_bitrate){edit_bit_rate_.Enabled = false;edit_video_quality_.Enabled = true;}else{edit_bit_rate_.Enabled = true;edit_video_quality_.Enabled = false;}if (btn_check_video_bitrate_.Checked){edit_bit_rate_.Text = kbit_rate.ToString();edit_video_max_bitrate_.Text = max_kbit_rate.ToString();}else{edit_bit_rate_.Text = "0";edit_video_max_bitrate_.Text = max_kbit_rate.ToString();}bool is_h264 = false;if (btn_check_h265_encoder_.Checked){is_h264 = false;}else{is_h264 = true;}edit_video_quality_.Text = CalVideoQuality(w, h, is_h264).ToString();combox_h264_profile_.SelectedIndex = 2;edit_video_encode_speed_.Text = CalVideoEncoderSpeed(w, h, is_h264).ToString();// 默認關鍵幀間隔設置為幀率的2倍edit_key_frame_.Text = (frame_rate * 2).ToString();}開始推送
設置推送URL后,調用StartPublisher接口開始推流,如需發送擴展SEI用戶數據,推送之前設置下數據發送對接大小。
if (publisher_handle_ == IntPtr.Zero){MessageBox.Show("[publish] handle with null");}if (!String.IsNullOrEmpty(url)){NTSmartPublisherSDK.NT_PB_SetURL(publisher_handle_, url, IntPtr.Zero);}//設置用戶數據發送隊列大小NTSmartPublisherSDK.NT_PB_SetPostUserDataQueueMaxSize(publisher_handle_, 3, 0);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;MessageBox.Show("調用推流接口失敗");return;}停止推送
調用NT_PB_StopPublisher()即可,停止推送后,如果沒有錄像等,可調用NT_PB_Close()接口,關掉實例,并把handle置 IntPtr.Zero。
private void btn_stop_publish_Click(object sender, EventArgs e){publisher_handle_count_--;NTSmartPublisherSDK.NT_PB_StopPublisher(publisher_handle_);rtmp_play_urls_.Clear();UpdateDisplayURLs();if (0 == publisher_handle_count_){NTSmartPublisherSDK.NT_PB_Close(publisher_handle_);publisher_handle_ = IntPtr.Zero;}btn_publish.Enabled = true;btn_stop_publish.Enabled = false;is_publishing_ = false;if (0 == publisher_handle_count_){if (btn_check_desktop_input_.Checked){btn_choose_screen_region_.Text = "選擇屏幕區域";}btn_check_dxgi_screen_capturer_.Enabled = true;check_capture_layered_window_.Enabled = true;btn_check_wr_way_capture_window_.Enabled = true;btn_desktop_camera_switch_.Text = "切換到攝像頭";btn_disable_image_watermark_.Text = "停止水印";btn_disable_camera_overlay_.Text = "停止疊加攝像頭";btn_disable_desktop_overlay_.Text = "停止疊加屏幕";btn_desktop_camera_switch.Enabled = true;btn_camera_overlay_to_desktop.Enabled = true;btn_desktop_overlay_to_camera.Enabled = true;btn_desktop_camera_switch.Enabled = true;btn_check_desktop_input_.Enabled = true;btn_check_scale_desktop_.Enabled = true;edit_desktop_scale_.Enabled = true;btn_check_camera_input_.Enabled = true;btn_add_image_watermark_.Enabled = true;timer_clock_.Enabled = false;if (btn_desktop_camera_switch.Checked|| btn_camera_overlay_to_desktop.Checked|| btn_desktop_overlay_to_camera.Checked){btn_check_desktop_input_.CheckState = CheckState.Checked;btn_check_camera_input_.CheckState = CheckState.Checked;}else{}EnableAuidoInputControl();}}預覽推送數據
設置NT_PB_SetVideoPreviewImageCallBack(),調用NT_PB_StartPreview()接口即可。
private void btn_preview_Click(object sender, EventArgs e){if (btn_check_window_input_.Checked){if (IntPtr.Zero == cur_sel_capture_window_){MessageBox.Show("請先下拉選擇采集窗口");return;}}if (publisher_handle_ == IntPtr.Zero){if (!OpenPublisherHandle()){return;}}if (publisher_handle_count_ < 1){SetCommonOptionToPublisherSDK();}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;}MessageBox.Show("預覽失敗, 請確保選擇了視頻采集選項");return;}publisher_handle_count_++;btn_preview.Enabled = false;btn_stop_preview.Enabled = true;if (1 == publisher_handle_count_){if (btn_check_desktop_input_.Checked){btn_choose_screen_region_.Text = "移動屏幕區域";}btn_check_dxgi_screen_capturer_.Enabled = false;check_capture_layered_window_.Enabled = false;btn_check_wr_way_capture_window_.Enabled = false;btn_desktop_camera_switch.Enabled = false;btn_camera_overlay_to_desktop.Enabled = false;btn_desktop_overlay_to_camera.Enabled = false;btn_add_image_watermark_.Enabled = false;if (btn_desktop_camera_switch.Checked|| btn_camera_overlay_to_desktop.Checked|| btn_desktop_overlay_to_camera.Checked){}else{btn_check_desktop_input_.Enabled = false;btn_check_camera_input_.Enabled = false;}DisableAuidoInputControl();}if (ui_preview_wnd_ == null){ui_preview_wnd_ = new nt_pb_ui_preview_wnd();}ui_preview_wnd_.Show();} 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 (ui_preview_wnd_ != null){ui_preview_wnd_.OnRGBXImage(cur_image_);}}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 (InvokeRequired){BeginInvoke(set_video_preview_image_callback_, pVideoFrame);}else{set_video_preview_image_callback_(pVideoFrame);}}停止預覽更簡單,調用NT_PB_StopPreview()。
private void btn_stop_preview_Click(object sender, EventArgs e){publisher_handle_count_--;NTSmartPublisherSDK.NT_PB_StopPreview(publisher_handle_);if (0 == publisher_handle_count_){NTSmartPublisherSDK.NT_PB_Close(publisher_handle_);publisher_handle_ = IntPtr.Zero;}btn_preview.Enabled = true;btn_stop_preview.Enabled = false;if (0 == publisher_handle_count_){if (btn_check_desktop_input_.Checked){btn_choose_screen_region_.Text = "選擇屏幕區域";}btn_check_dxgi_screen_capturer_.Enabled = true;check_capture_layered_window_.Enabled = true;btn_check_wr_way_capture_window_.Enabled = true;btn_desktop_camera_switch_.Text = "切換到攝像頭";btn_disable_image_watermark_.Text = "停止水印";btn_disable_camera_overlay_.Text = "停止疊加攝像頭";btn_disable_desktop_overlay_.Text = "停止疊加屏幕";btn_desktop_camera_switch.Enabled = true;btn_camera_overlay_to_desktop.Enabled = true;btn_desktop_overlay_to_camera.Enabled = true;btn_desktop_camera_switch.Enabled = true;btn_check_desktop_input_.Enabled = true;btn_check_camera_input_.Enabled = true;btn_add_image_watermark_.Enabled = true;timer_clock_.Enabled = false;if (btn_desktop_camera_switch.Checked|| btn_camera_overlay_to_desktop.Checked|| btn_desktop_overlay_to_camera.Checked){btn_check_desktop_input_.CheckState = CheckState.Checked;btn_check_camera_input_.CheckState = CheckState.Checked;}else{}EnableAuidoInputControl();}ui_preview_wnd_.Hide();ui_preview_wnd_ = null;}實時截圖
if (String.IsNullOrEmpty(capture_image_path_)){MessageBox.Show("請先設置保存截圖文件的目錄! 點擊截圖左邊的按鈕設置!");return;}if (publisher_handle_ == IntPtr.Zero){return;}String name = capture_image_path_ + "\\" + DateTime.Now.ToString("hh-mm-ss") + ".png";byte[] buffer1 = Encoding.Default.GetBytes(name);byte[] buffer2 = Encoding.Convert(Encoding.Default, Encoding.UTF8, buffer1, 0, buffer1.Length);byte[] buffer3 = new byte[buffer2.Length + 1];buffer3[buffer2.Length] = 0;Array.Copy(buffer2, buffer3, buffer2.Length);IntPtr file_name_ptr = Marshal.AllocHGlobal(buffer3.Length);Marshal.Copy(buffer3, 0, file_name_ptr, buffer3.Length);capture_image_call_back_ = new NT_PB_SDKCaptureImageCallBack(SDKCaptureImageCallBack);UInt32 ret = NTSmartPublisherSDK.NT_PB_CaptureImage(publisher_handle_, file_name_ptr, IntPtr.Zero, capture_image_call_back_);Marshal.FreeHGlobal(file_name_ptr);if (NT.NTBaseCodeDefine.NT_ERC_OK == ret){// 發送截圖請求成功}else if ((UInt32)NT.NTSmartPublisherDefine.NT_PB_E_ERROR_CODE.NT_ERC_PB_TOO_MANY_CAPTURE_IMAGE_REQUESTS == ret){// 通知用戶延時MessageBox.Show("Too many capture image requests!");}else{// 其他失敗} private void ImageCallBack(UInt32 result, String file_name){if (file_name == null && file_name.Length == 0)return;MessageBox.Show(file_name);}public void SDKCaptureImageCallBack(IntPtr handle, IntPtr userData, UInt32 result, IntPtr file_name){if (file_name == IntPtr.Zero)return;int index = 0;while (true){if (0 == Marshal.ReadByte(file_name, index))break;index++;}byte[] file_name_buffer = new byte[index];Marshal.Copy(file_name, file_name_buffer, 0, index);byte[] dst_buffer = Encoding.Convert(Encoding.UTF8, Encoding.Default, file_name_buffer, 0, file_name_buffer.Length);String image_name = Encoding.Default.GetString(dst_buffer, 0, dst_buffer.Length);if (InvokeRequired){BeginInvoke(set_capture_image_call_back_, result, image_name);}else{set_capture_image_call_back_(result, image_name);}}問答式參考
1視頻采集設置
?
說明:
1.?屏幕和攝像頭相互切換:用于在線教育或者無紙化等場景,推送或錄像過程中,隨時切換屏幕或攝像頭數據(切換數據源),如需實時切換,點擊頁面“切換到攝像頭”按鈕即可;
2.?設置遮蓋層,用于設定一個長方形或正方形區域(可自指定區域大小),遮蓋不想給用戶展示的部分;
3.?水印:添加PNG水印,支持推送或錄像過程中,隨時添加、取消水印;
4.?攝像頭疊加到屏幕:意在用于同屏過程中,主講人攝像頭懸浮于屏幕之上(可指定疊加坐標),實現雙畫面展示,推送或錄像過程中,可以隨時取消攝像頭疊加;
?
5.?屏幕疊加到攝像頭:同4,效果展示,實際根據需求實現;
6.?采集桌面:可以通過點擊“選擇屏幕區域”獲取采集區域,并可在采集過程中,隨時切換區域位置,如不設定,默認全屏采集;
7.?使用DXGI采集屏幕,采集時停用Aero;
8.?采集窗口:可設定需要采集的窗口,窗口放大或縮小,推送端會自適應碼率和分辨率;
9.?采集幀率(幀/秒):默認屏幕采集12幀,可根據實際場景需求設定到期望幀率;
10.?縮放屏幕大小縮放比:用于高清或超高清屏,通過設定一定的比例因子,縮放屏幕采集分辨率;
11.?采集攝像頭:可選擇需要采集的攝像頭、采集分辨率、幀率、是否需要水平或者垂直反轉、是否需要旋轉;
追加提問:
問題[確認數據源]:采集桌面還是攝像頭?如果桌面,全屏還是部分區域?
回答:
如果是攝像頭:可以選擇攝像頭列表,然后分辨率、幀率。
如果是屏幕:默認幀率是12幀,可以根據實際場景調整,選取屏幕區域,可以實時拉取選擇需要采集或錄像區域;
如果是疊加模式:可選擇攝像頭疊加到屏幕,還是屏幕疊加到攝像頭;
更高需求的用戶,可以設置水印或應用層遮蓋。
問題:如果是攝像頭,采集到的攝像頭角度不對怎么辦?
回答:我們支持攝像頭鏡像和翻轉設置,攝像頭可通過SDK接口輕松實現水平/垂直翻轉、鏡像效果。
2?視頻碼率控制
如何選擇適合我的碼率?
回答:如果不是有音視頻背景的開發人員,可點擊“獲取視頻碼率默認值”,參考我們默認的碼率推薦,如果覺得推薦碼率過高或不夠,可根據實際情況酌情調整。
265編碼還是H.264編碼?
回答:Windows平臺支持H.265特定機型硬編碼,如果推RTMP流,需要服務器支持RTMP H.265擴展,播放器SDK,也需要同步支持RTMP H.265擴展播放。
如果是輕量級RTSP服務SDK對接的話,只需要播放器支持RTSP H.265即可。
如果推攝像頭數據,建議采用可變碼率+H.265編碼。
如何設置碼率參數更合理?
回答:
關鍵幀間隔:一般來說,設置到幀率的2-4倍,比如幀率20,關鍵幀間隔可以設置到40-80;
平均碼率:可以點擊“獲取視頻碼率默認值”,最大碼率是平均碼率的2倍;
視頻質量:如果使用可變碼率,建議采用大牛直播SDK默認推薦視頻質量值;
編碼速度:如高分辨率,建議1-3,值越小,編碼速度越快;
H.264 Profile:默認baseline profile,可根據需要,酌情設置High profile;
NOTE:點擊“推送”或“錄像”或啟動內置RTSP服務SDK之前,請務必設置視頻碼率,如不想手動設置,請點擊“獲取視頻碼率默認值”!!!
3?音頻采集設置
問答式:采集音頻嗎?如果采集,采集麥克風還是揚聲器的,亦或混音?
回答:
如果想采集電腦輸出的音頻(比如音樂之類),可以選擇“采集揚聲器”;
如果想采集麥克風音頻,可以選擇“采集麥克風”,并選擇相關設備;
如果兩個都想采集,可以兩個都選擇,混音輸出。
4?實時音量調節
問答式:采集過程中可以改變麥克風或揚聲器采集音量嗎?
回答:可以,如果二者都選中,處于混音模式,也可單獨調整麥克風或揚聲器音量。
5?音頻編碼
問題:是AAC還是SPEEX?
回答:我們默認是AAC編碼模式,如果需要碼率更低,可以選擇SPEEX編碼模式,當然我們的AAC編碼碼率也不高,如果沒有太高要求,考慮到通用性,建議使用AAC。
6?音頻處理
問題:我想過濾背景噪音怎么辦?
回答:選中“噪音抑制”,“噪音抑制“請和“自動增益控制”組合使用,“端點檢測(VAD)”可選設置。
問題:我想做一對一互動怎么辦?
回答:選中“回音消除”,可以和“噪音抑制”、“自動增益控制”組合使用,具體可參看回音消除的demo工程:WIN-EchoCancellation-CSharp-Demo。
問題:我推送或者錄像過程中,隨時靜音怎么辦?
回答:推送過程中,隨時選擇或取消選擇“靜音”功能。
7多路推送
問題:我想同時推送到多個url怎么辦(比如一個內網服務器,一個外網服務器)?
回答:同時填寫多個url(最多3個),然后點推送即可。
8?截圖(快照)
問題:我想推送或者錄像過程中,截取當前圖像怎么辦?
回答:那就設置好截圖路徑,推送或錄像過程中,隨時點擊“截圖”。
9?錄像
問題:我還想錄像,怎么辦?
回答:設置錄像文件存放目錄,文件前綴、單個文件大小,是否加日期、時間,隨時錄制即可,此外,我們的SDK還支持錄像過程中,暫停錄像,恢復錄像。
10 實時預覽
問題:我還想看看推出去視頻,特別是合成后的效果,怎么辦?
回答:點擊頁面的“預覽”按鈕,就可以看到。
?
總結
以上是生活随笔為你收集整理的Windows平台RTMP直播推送集成简要说明的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【Python】图解Pandas的宝藏函
- 下一篇: 如何用u盘装系统