Windows平台RTMP推送|轻量级RTSP服务实现本地摄像头|屏幕|叠加数据预览

背景

大家在做Windows平台RTMP推送或轻量级RTSP服务的时候,不管是采集屏幕还是采集摄像头,亦或屏幕摄像头的叠加模式,总会有这样的诉求,采集到的数据,希望能本地看看具体采集的数据或者图像实际效果,也就是本次介绍的“预览”功能。

废话不多说,想上图:

如何实现

开始预览

开始预览,大概的流程是,调用OpenPublisherHandle(),进行初始的数据源类型设置,然后调用Open()接口,获取推送handler,并设置event回调。

bool CSmartPublisherDemoDlg::OpenPublisherHandle()
{if ( publisher_handle_ != NULL ){return true;}// 视频auto video_option = NT_PB_E_VIDEO_OPTION_NO_VIDEO;if ( BST_CHECKED == btn_check_desktop_camera_switch_.GetCheck()|| BST_CHECKED == btn_check_camera_overlay_to_desktop_.GetCheck() || BST_CHECKED == btn_check_desktop_overlay_to_camera_.GetCheck() ){// 使用叠加模式video_option = NT_PB_E_VIDEO_OPTION_LAYER;}else if (BST_CHECKED == btn_check_desktop_input_.GetCheck() && BST_CHECKED == btn_check_scale_desktop_.GetCheck()){// 使用叠加模式来实现缩放video_option = NT_PB_E_VIDEO_OPTION_LAYER;}else if (BST_CHECKED == btn_check_desktop_input_.GetCheck() && BST_CHECKED != btn_check_scale_desktop_.GetCheck() ){video_option = NT_PB_E_VIDEO_OPTION_SCREEN;}else if (BST_CHECKED == btn_check_window_input_.GetCheck()){video_option = NT_PB_E_VIDEO_OPTION_WINDOW;// video_option = NT_PB_E_VIDEO_OPTION_LAYER;}else if (BST_CHECKED == btn_check_camera_input_.GetCheck()){video_option = NT_PB_E_VIDEO_OPTION_CAMERA;}// 音频auto audio_option = NT_PB_E_AUDIO_OPTION_NO_AUDIO;audio_option = NT_PB_E_AUDIO_OPTION_NO_AUDIO;if ( BST_CHECKED == btn_check_auido_mic_input_.GetCheck()&& BST_CHECKED == btn_check_auido_speaker_input_.GetCheck() ){audio_option = NT_PB_E_AUDIO_OPTION_CAPTURE_MIC_SPEAKER_MIXER;}else if ( BST_CHECKED == btn_check_auido_mic_input_.GetCheck() ){audio_option = NT_PB_E_AUDIO_OPTION_CAPTURE_MIC;}else if (BST_CHECKED == btn_check_auido_speaker_input_.GetCheck()){audio_option = NT_PB_E_AUDIO_OPTION_CAPTURE_SPEAKER;}publisher_handle_count_ = 0;if ( NT_ERC_OK != publisher_api_.Open(&publisher_handle_,video_option, audio_option, 0, NULL)){AfxMessageBox(_T("Call open failed!"));return false;}if ( publisher_handle_ != NULL ){// 设置事件回调publisher_api_.SetEventCallBack(publisher_handle_, GetSafeHwnd(), &NT_PB_SDKPublisherEventHandle);return true;}else{return false;}
}

目前我们的SDK支持以下几种视频源类型,实时预览仅限于编码前数据类型:

/*定义Video源选项*/
typedef enum _NT_PB_E_VIDEO_OPTION
{NT_PB_E_VIDEO_OPTION_NO_VIDEO = 0x0,NT_PB_E_VIDEO_OPTION_SCREEN   = 0x1, // 采集屏幕NT_PB_E_VIDEO_OPTION_CAMERA	  = 0x2, // 摄像头采集NT_PB_E_VIDEO_OPTION_LAYER    = 0x3, // 视频合并,比如桌面叠加摄像头等NT_PB_E_VIDEO_OPTION_ENCODED_DATA = 0x4, // 已经编码的视频数据,目前支持H264NT_PB_E_VIDEO_OPTION_WINDOW   = 0x5, // 采集窗口
} NT_PB_E_VIDEO_OPTION;

接着是调用SetCommonOptionToPublisherSDK(),完成叠加和其他参数设定。

void CSmartPublisherDemoDlg::SetCommonOptionToPublisherSDK()
{ASSERT(publisher_handle_ != NULL);layer_conf_wrappers_.clear();// 视频相关设置if ( BST_CHECKED == btn_check_desktop_camera_switch_.GetCheck()|| BST_CHECKED == btn_check_camera_overlay_to_desktop_.GetCheck()|| BST_CHECKED == btn_check_desktop_overlay_to_camera_.GetCheck()|| BST_CHECKED == btn_check_desktop_input_.GetCheck()|| BST_CHECKED == btn_check_window_input_.GetCheck()|| BST_CHECKED == btn_check_camera_input_.GetCheck() ){if ( BST_CHECKED == btn_check_desktop_camera_switch_.GetCheck() ){auto left = GetIntValueFromEdit(&edit_clip_left_);auto top = GetIntValueFromEdit(&edit_clip_top_);auto w = GetIntValueFromEdit(&edit_clip_width_);auto h = GetIntValueFromEdit(&edit_clip_height_);// 有一个是0, 就使用全屏if ( w == 0 || h == 0 ){left = 0;top  = 0;GetScreenSize(w, h);}else{// 保证4字节对齐w = NT_ByteAlign(w, 2);h = NT_ByteAlign(h, 4);}auto index = 0;// 第0层填充RGBA矩形, 目的是保证帧率, 颜色就填充全黑auto rgba_layer_c0 = std::make_shared<nt_pb_sdk::RGBARectangleLayerConfigWrapper>(index++, true,0, 0, w, h);rgba_layer_c0->conf_.red_	= 0;rgba_layer_c0->conf_.green_ = 0;rgba_layer_c0->conf_.blue_	= 0;rgba_layer_c0->conf_.alpha_ = 255;layer_conf_wrappers_.push_back(rgba_layer_c0);// 第1层是摄像头if ( CB_ERR != cur_sel_camera_index_ ){auto& camera = cameras_[cur_sel_camera_index_];auto camera_layer_c1 = std::make_shared<nt_pb_sdk::CameraLayerConfigWrapperV2>(index++, false,0, 0, w, h);strcpy_s(camera_layer_c1->conf_.device_unique_id_utf8_, camera.id_.c_str());camera_layer_c1->conf_.is_flip_horizontal_ = BST_CHECKED == btn_check_flip_horizontal_camera_.GetCheck();camera_layer_c1->conf_.is_flip_vertical_   = BST_CHECKED == btn_check_flip_vertical_camera_.GetCheck();// 这种叠加模式下不要旋转,否则变形厉害, 要么就定好一个角度,调整宽高,但不要动态旋转camera_layer_c1->conf_.rotate_degress_ = 0;layer_conf_wrappers_.push_back(camera_layer_c1);}// 第2层是屏幕auto screen_layer_c2 = std::make_shared<nt_pb_sdk::ScreenLayerConfigWrapper>(index++, true,0, 0, w, h);screen_layer_c2->conf_.reserve_ = nullptr;screen_layer_c2->conf_.clip_region_.x_ = left;screen_layer_c2->conf_.clip_region_.y_ = top;screen_layer_c2->conf_.clip_region_.width_  = w;screen_layer_c2->conf_.clip_region_.height_ = h;layer_conf_wrappers_.push_back(screen_layer_c2);// 第3层填充RGBA矩形, 增加半透明效果auto r = GetIntValueFromEdit(&edit_rgba_rect_layer_red_);r = ClipIntValue(r, 0, 255);auto g = GetIntValueFromEdit(&edit_rgba_rect_layer_green_);g = ClipIntValue(g, 0, 255);auto b = GetIntValueFromEdit(&edit_rgba_rect_layer_blue_);b = ClipIntValue(b, 0, 255);auto a = GetIntValueFromEdit(&edit_rgba_rect_layer_alpha_);a = ClipIntValue(a, 0, 255);auto rgba_layer_c3 = std::make_shared<nt_pb_sdk::RGBARectangleLayerConfigWrapper>(index++, a != 0,0, 0, w, h);rgba_layer_c3->conf_.red_	= r;rgba_layer_c3->conf_.green_ = g;rgba_layer_c3->conf_.blue_	= b;rgba_layer_c3->conf_.alpha_ = a;layer_conf_wrappers_.push_back(rgba_layer_c3);// 如果有图片,增加图片层if ( !image_layer_file_name_utf8_.empty()&& image_layer_width_ > 0 && image_layer_height_ > 0){auto image_layer_c4 = std::make_shared<nt_pb_sdk::ImageLayerConfigWrapper>(index++, true,image_layer_left_, image_layer_top_, image_layer_width_, image_layer_height_ );strcpy_s(image_layer_c4->conf_.file_name_utf8_, image_layer_file_name_utf8_.c_str());// 图片层是可以增加背景的,有兴趣的话,可以打开看看效果/*image_layer_c4->conf_.is_setting_background_ = 1;image_layer_c4->conf_.bk_red_ = 255;image_layer_c4->conf_.bk_green_ = 255;image_layer_c4->conf_.bk_blue_ = 255;*/layer_conf_wrappers_.push_back(image_layer_c4);}SetLayersConfig();publisher_api_.SetFrameRate(publisher_handle_, GetIntValueFromEdit(&edit_frame_rate_));}else if (BST_CHECKED == btn_check_camera_overlay_to_desktop_.GetCheck()){auto left = GetIntValueFromEdit(&edit_clip_left_);auto top  = GetIntValueFromEdit(&edit_clip_top_);auto w = GetIntValueFromEdit(&edit_clip_width_);auto h = GetIntValueFromEdit(&edit_clip_height_);// 有一个是0, 就使用全屏if ( w == 0 || h == 0 ){left = 0;top = 0;GetScreenSize(w, h);}else{// 保证4字节对齐w = NT_ByteAlign(w, 2);h = NT_ByteAlign(h, 4);}auto index = 0;/*auto image_layer_c0 = std::make_shared<nt_pb_sdk::ImageLayerConfigWrapper>(index++, true,0, 0, w, h);strcpy_s(image_layer_c0->conf_.file_name_utf8_, "D:\\myxxyy\\testpng\\ggdwdd.png");image_layer_c0->conf_.is_setting_background_ = 1;image_layer_c0->conf_.bk_red_ = 255;image_layer_c0->conf_.bk_green_ = 255;image_layer_c0->conf_.bk_blue_ = 255;layer_conf_wrappers_.push_back(image_layer_c0);*/// 第0层是屏幕auto screen_layer_c0 = std::make_shared<nt_pb_sdk::ScreenLayerConfigWrapper>(index++, true,0, 0, w, h);screen_layer_c0->conf_.reserve_ = nullptr;screen_layer_c0->conf_.clip_region_.x_ = left;screen_layer_c0->conf_.clip_region_.y_ = top;screen_layer_c0->conf_.clip_region_.width_ = w;screen_layer_c0->conf_.clip_region_.height_ = h;layer_conf_wrappers_.push_back(screen_layer_c0);// 第1层是摄像头if ( CB_ERR != cur_sel_camera_index_ ){auto& camera = cameras_[cur_sel_camera_index_];auto c_l = GetIntValueFromEdit(&edit_camera_overlay_left_);auto c_t = GetIntValueFromEdit(&edit_camera_overlay_top_);auto c_w = GetIntValueFromEdit(&edit_camera_overlay_width_);auto c_h = GetIntValueFromEdit(&edit_camera_overlay_height_);if ( c_w == 0 ){c_w = w / 2;}if ( c_h == 0 ){c_h = h / 2;}if ( c_w >0 && c_h > 0 ){auto camera_layer_c1 = std::make_shared<nt_pb_sdk::CameraLayerConfigWrapperV2>(index++, true,c_l, c_t, c_w, c_h);strcpy_s(camera_layer_c1->conf_.device_unique_id_utf8_, camera.id_.c_str());camera_layer_c1->conf_.is_flip_horizontal_ = BST_CHECKED == btn_check_flip_horizontal_camera_.GetCheck();camera_layer_c1->conf_.is_flip_vertical_ = BST_CHECKED == btn_check_flip_vertical_camera_.GetCheck();camera_layer_c1->conf_.rotate_degress_ = GetCameraRotateDegress();layer_conf_wrappers_.push_back(camera_layer_c1);}	}SetLayersConfig();publisher_api_.SetFrameRate(publisher_handle_, GetIntValueFromEdit(&edit_frame_rate_));}else if (BST_CHECKED == btn_check_desktop_overlay_to_camera_.GetCheck()){if (CB_ERR != cur_sel_camera_index_&& CB_ERR != cur_sel_camera_resolutions_index_&& CB_ERR != cur_sel_camera_frame_rate_index_){auto& camera = cameras_[cur_sel_camera_index_];auto& cap = camera.capabilities_[cur_sel_camera_resolutions_index_];auto index = 0;// 第0层是摄像头auto camera_layer_c0 = std::make_shared<nt_pb_sdk::CameraLayerConfigWrapperV2>(index++, true,0, 0, cap.width_, cap.height_);strcpy_s(camera_layer_c0->conf_.device_unique_id_utf8_, camera.id_.c_str());camera_layer_c0->conf_.is_flip_horizontal_ = BST_CHECKED == btn_check_flip_horizontal_camera_.GetCheck();camera_layer_c0->conf_.is_flip_vertical_   = BST_CHECKED == btn_check_flip_vertical_camera_.GetCheck();// 这种叠加模式下不要旋转,否则变形厉害, 要么就定好一个角度,调整宽高,但不要动态旋转camera_layer_c0->conf_.rotate_degress_ = 0;layer_conf_wrappers_.push_back(camera_layer_c0);// 第1层是屏幕auto screen_layer_c1 = std::make_shared<nt_pb_sdk::ScreenLayerConfigWrapper>(index++, true,0, 0, cap.width_ / 2, cap.height_/2);screen_layer_c1->conf_.reserve_ = nullptr;screen_layer_c1->conf_.clip_region_.x_ = 0;screen_layer_c1->conf_.clip_region_.y_ = 0;screen_layer_c1->conf_.clip_region_.width_ = cap.width_ / 2;screen_layer_c1->conf_.clip_region_.height_ = cap.height_ / 2;layer_conf_wrappers_.push_back(screen_layer_c1);SetLayersConfig();publisher_api_.SetFrameRate(publisher_handle_, cur_sel_camera_frame_rate_index_ + 1);}}else if (BST_CHECKED == btn_check_desktop_input_.GetCheck() && BST_CHECKED == btn_check_scale_desktop_.GetCheck()){int left = 0, top = 0, w = 0, h = 0, scale_w = 0, scale_h = 0;GetScreenScaleConfigInfo(left, top, w, h, scale_w, scale_h);auto index = 0;// 第0层填充RGBA矩形, 目的是保证帧率, 颜色就填充全黑auto rgba_layer_c0 = std::make_shared<nt_pb_sdk::RGBARectangleLayerConfigWrapper>(index++, true,0, 0, scale_w, scale_h);rgba_layer_c0->conf_.red_ = 0;rgba_layer_c0->conf_.green_ = 0;rgba_layer_c0->conf_.blue_ = 0;rgba_layer_c0->conf_.alpha_ = 255;layer_conf_wrappers_.push_back(rgba_layer_c0);// 第1层是屏幕auto screen_layer_c1 = std::make_shared<nt_pb_sdk::ScreenLayerConfigWrapperV2>(index++, true,0, 0, scale_w, scale_h);screen_layer_c1->conf_.reserve1_ = nullptr;screen_layer_c1->conf_.reserve2_ = 0;screen_layer_c1->conf_.clip_region_.x_ = left;screen_layer_c1->conf_.clip_region_.y_ = top;screen_layer_c1->conf_.clip_region_.width_ = w;screen_layer_c1->conf_.clip_region_.height_ = h;screen_layer_c1->conf_.scale_filter_mode_ = 3; // 屏幕缩放考虑到清晰度,这里用3,最高缩放质量layer_conf_wrappers_.push_back(screen_layer_c1);SetLayersConfig();publisher_api_.SetFrameRate(publisher_handle_, GetIntValueFromEdit(&edit_frame_rate_));}else if (BST_CHECKED == btn_check_desktop_input_.GetCheck() && BST_CHECKED != btn_check_scale_desktop_.GetCheck()){publisher_api_.SetScreenClip(publisher_handle_,GetIntValueFromEdit(&edit_clip_left_),GetIntValueFromEdit(&edit_clip_top_),GetIntValueFromEdit(&edit_clip_width_),GetIntValueFromEdit(&edit_clip_height_));publisher_api_.SetFrameRate(publisher_handle_, GetIntValueFromEdit(&edit_frame_rate_));}else if ( BST_CHECKED == btn_check_window_input_.GetCheck() ){if ( nullptr != cur_sel_capture_window_ ){publisher_api_.SetCaptureWindow(publisher_handle_, cur_sel_capture_window_);publisher_api_.SetFrameRate(publisher_handle_, GetIntValueFromEdit(&edit_frame_rate_));// 窗口层测试和演示代码++/*auto index = 0;auto rgba_layer_c0 = std::make_shared<nt_pb_sdk::RGBARectangleLayerConfigWrapper>(index++, true,0, 0, 1280, 720);rgba_layer_c0->conf_.red_ = 0;rgba_layer_c0->conf_.green_ = 255;rgba_layer_c0->conf_.blue_ = 0;rgba_layer_c0->conf_.alpha_ = 255;layer_conf_wrappers_.push_back(rgba_layer_c0);auto window_layer_c1 = std::make_shared<nt_pb_sdk::WindowLayerConfigWrapper>(index++, true,100, 100, 800, 600);window_layer_c1->conf_.window_ = cur_sel_capture_window_;layer_conf_wrappers_.push_back(window_layer_c1);SetLayersConfig();*/// 窗口层测试和演示代码--}}else if (BST_CHECKED == btn_check_camera_input_.GetCheck()){if (CB_ERR != cur_sel_camera_index_&& CB_ERR != cur_sel_camera_resolutions_index_&& CB_ERR != cur_sel_camera_frame_rate_index_){auto& camera = cameras_[cur_sel_camera_index_];auto& cap = camera.capabilities_[cur_sel_camera_resolutions_index_];publisher_api_.SetVideoCaptureDeviceBaseParameter(publisher_handle_,camera.id_.c_str(), cap.width_, cap.height_);publisher_api_.SetFrameRate(publisher_handle_, cur_sel_camera_frame_rate_index_+ 1);publisher_api_.FlipVerticalCamera(publisher_handle_,BST_CHECKED == btn_check_flip_vertical_camera_.GetCheck());publisher_api_.FlipHorizontalCamera(publisher_handle_, BST_CHECKED == btn_check_flip_horizontal_camera_.GetCheck());auto degress = GetCameraRotateDegress();publisher_api_.RotateCamera(publisher_handle_, degress);}}publisher_api_.EnableDXGIScreenCapturer(publisher_handle_, BST_CHECKED == btn_check_dxgi_screen_capturer_.GetCheck());publisher_api_.DisableAeroScreenCapturer(publisher_handle_, BST_CHECKED == btn_check_capturer_disable_aero_.GetCheck());publisher_api_.EnableScreenCaptureLayeredWindow(publisher_handle_, BST_CHECKED == check_capture_layered_window_.GetCheck()?1:0);publisher_api_.SetCaptureWindowWay(publisher_handle_, BST_CHECKED == btn_check_wr_way_capture_window_.GetCheck() ? 2 : 1);auto cur_video_codec_id = btn_check_h265_encoder_.GetCheck() != BST_CHECKED? NT_MEDIA_CODEC_ID_H264 : NT_MEDIA_CODEC_ID_H265;NT_INT32 cur_sel_encoder_id = 0;NT_INT32 cur_sel_gpu = 0;auto is_hw_encoder = btn_check_video_hardware_encoder_.GetCheck() == BST_CHECKED;if (is_hw_encoder){auto cur_sel_hw = combobox_video_encoders_.GetCurSel();if (cur_sel_hw >=0 ){cur_sel_encoder_id = combobox_video_encoders_.GetItemData(cur_sel_hw);cur_sel_gpu = -1;auto cur_sel_hw_dev = combobox_video_hardware_encoder_devices_.GetCurSel();if (cur_sel_hw_dev >= 0){cur_sel_gpu = combobox_video_hardware_encoder_devices_.GetItemData(cur_sel_hw_dev);}}else{is_hw_encoder = false;}}if (!is_hw_encoder){if (NT_MEDIA_CODEC_ID_H264 == cur_video_codec_id){cur_sel_encoder_id = btn_check_openh264_encoder_.GetCheck() == BST_CHECKED ? 1 : 0;}}publisher_api_.SetVideoEncoder(publisher_handle_, is_hw_encoder? 1 : 0, cur_sel_encoder_id, cur_video_codec_id, cur_sel_gpu);if ( BST_CHECKED != btn_check_window_input_.GetCheck() ){publisher_api_.SetVideoBitRate(publisher_handle_, GetIntValueFromEdit(&edit_bit_rate_));}else{// 窗口的分辨率会变, 所以设置一组码率下去auto frame_rate = GetIntValueFromEdit(&edit_frame_rate_);SetBitrateGroup(publisher_handle_, frame_rate);}publisher_api_.SetVideoQualityV2(publisher_handle_, GetIntValueFromEdit(&edit_video_quality_));publisher_api_.SetVideoMaxBitRate(publisher_handle_, GetIntValueFromEdit(&edit_video_max_bitrate_));publisher_api_.SetVideoKeyFrameInterval(publisher_handle_, GetIntValueFromEdit(&edit_key_frame_));if (NT_MEDIA_CODEC_ID_H264 == cur_video_codec_id){auto profile_sel = combox_h264_profile_.GetCurSel();if (profile_sel != CB_ERR){publisher_api_.SetVideoEncoderProfile(publisher_handle_, profile_sel + 1);}}publisher_api_.SetVideoEncoderSpeed(publisher_handle_, GetIntValueFromEdit(&edit_video_encode_speed_));// 清除编码器所有的特定的参数publisher_api_.ClearVideoEncoderSpecialOptions(publisher_handle_);if (cur_sel_encoder_id == 1){// qp_max 和 qp_min 当前只对openh264有效, 这里也就只在openh264使用的场景下设置配置值publisher_api_.SetVideoEncoderQPMax(publisher_handle_, GetIntValueFromEdit(&edit_qp_max_));publisher_api_.SetVideoEncoderQPMin(publisher_handle_, GetIntValueFromEdit(&edit_qp_min_));// openh264 配置特定参数publisher_api_.SetVideoEncoderSpecialInt32Option(publisher_handle_, "usage_type", btn_check_openh264_ppt_usage_type_.GetCheck() == BST_CHECKED ? 1 : 0);publisher_api_.SetVideoEncoderSpecialInt32Option(publisher_handle_, "rc_mode", btn_check_openh264_rc_bitrate_mode_.GetCheck() == BST_CHECKED ? 1 : 0);publisher_api_.SetVideoEncoderSpecialInt32Option(publisher_handle_, "enable_frame_skip", btn_check_openh264_frame_skip_.GetCheck() == BST_CHECKED ? 1 : 0);}else{publisher_api_.SetVideoEncoderQPMax(publisher_handle_, -1);publisher_api_.SetVideoEncoderQPMin(publisher_handle_, -1);}}// 音频相关设置if ( BST_CHECKED == btn_check_auido_mic_input_.GetCheck() ){auto count = combox_auido_input_devices_.GetCount();if (count != CB_ERR && count > 0){auto cur_sel = combox_auido_input_devices_.GetCurSel();if (cur_sel != CB_ERR){publisher_api_.SetAuidoInputDeviceId(publisher_handle_, cur_sel);}}}// 只采集扬声器时做静音补偿if ( BST_CHECKED != btn_check_auido_mic_input_.GetCheck()&& BST_CHECKED == btn_check_auido_speaker_input_.GetCheck() ){publisher_api_.SetCaptureSpeakerCompensateMute(publisher_handle_, 1);}if ( BST_CHECKED == btn_check_speex_encoder_.GetCheck()){publisher_api_.SetPublisherAudioCodecType(publisher_handle_, 2);publisher_api_.SetPublisherSpeexEncoderQuality(publisher_handle_, GetIntValueFromEdit(&edit_speex_quality_));}else{publisher_api_.SetPublisherAudioCodecType(publisher_handle_, 1);}if ( BST_CHECKED == btn_check_auido_mic_input_.GetCheck()|| BST_CHECKED == btn_check_auido_speaker_input_.GetCheck()){if (BST_CHECKED == btn_check_set_mute_.GetCheck()){publisher_api_.SetMute(publisher_handle_, 1);}}if ( BST_CHECKED == btn_check_echo_cancel_.GetCheck() ){publisher_api_.SetEchoCancellation(publisher_handle_, 1, GetIntValueFromEdit(&edit_echo_delay_));}else{publisher_api_.SetEchoCancellation(publisher_handle_, 0, 0);}if ( BST_CHECKED == btn_check_noise_suppression_.GetCheck() ){publisher_api_.SetNoiseSuppression(publisher_handle_, 1);}else{publisher_api_.SetNoiseSuppression(publisher_handle_, 0);}if ( BST_CHECKED == btn_check_agc_.GetCheck() ){publisher_api_.SetAGC(publisher_handle_, 1);}else{publisher_api_.SetAGC(publisher_handle_, 0);}if (BST_CHECKED == btn_check_vad_.GetCheck()){publisher_api_.SetVAD(publisher_handle_, 1);}else{publisher_api_.SetVAD(publisher_handle_, 0);}if (BST_CHECKED == btn_check_auido_mic_input_.GetCheck()&& BST_CHECKED == btn_check_auido_speaker_input_.GetCheck()){publisher_api_.SetInputAudioVolume(publisher_handle_, 0, GetDoubleValueFromEdit(&edit_audio_input_volume_));publisher_api_.SetInputAudioVolume(publisher_handle_, 1, GetDoubleValueFromEdit(&edit_audio_speaker_input_volume_));}else if (BST_CHECKED == btn_check_auido_mic_input_.GetCheck()){publisher_api_.SetInputAudioVolume(publisher_handle_, 0, GetDoubleValueFromEdit(&edit_audio_input_volume_));}else if (BST_CHECKED == btn_check_auido_speaker_input_.GetCheck()){publisher_api_.SetInputAudioVolume(publisher_handle_, 0, GetDoubleValueFromEdit(&edit_audio_speaker_input_volume_));}
}

接下来,设置preview回调:

	publisher_api_.SetVideoPreviewImageCallBack(publisher_handle_, NT_PB_E_IMAGE_FORMAT_RGB32, GetSafeHwnd(), &NT_PB_SDKVideoPreviewImageHandle);

然后,调用preview接口:

void CSmartPublisherDemoDlg::OnBnClickedButtonStartPreview()
{if ( BST_CHECKED == btn_check_window_input_.GetCheck() ){if (nullptr == cur_sel_capture_window_){AfxMessageBox(_T("请先下拉选择采集窗口"));return;}}if ( publisher_handle_ == NULL ){if (!OpenPublisherHandle()){return;}}if ( publisher_handle_count_ < 1 ){SetCommonOptionToPublisherSDK();}ASSERT(publisher_handle_ != NULL);publisher_api_.SetVideoPreviewImageCallBack(publisher_handle_, NT_PB_E_IMAGE_FORMAT_RGB32, GetSafeHwnd(), &NT_PB_SDKVideoPreviewImageHandle);if (NT_ERC_OK != publisher_api_.StartPreview(publisher_handle_, 0, NULL)){if ( 0 == publisher_handle_count_ ){publisher_api_.Close(publisher_handle_);publisher_handle_ = NULL;}AfxMessageBox(_T("预览失败, 请确保选择了视频采集选项"));return;}publisher_handle_count_++;btn_preview_.EnableWindow(FALSE);btn_stop_preview_.EnableWindow(TRUE);if ( 1 == publisher_handle_count_ ){if (BST_CHECKED == btn_check_desktop_input_.GetCheck()){btn_choose_screen_region_.SetWindowTextW(L"移动屏幕区域");}btn_check_dxgi_screen_capturer_.EnableWindow(FALSE);check_capture_layered_window_.EnableWindow(FALSE);btn_check_wr_way_capture_window_.EnableWindow(FALSE);btn_check_desktop_camera_switch_.EnableWindow(FALSE);btn_check_camera_overlay_to_desktop_.EnableWindow(FALSE);btn_check_desktop_overlay_to_camera_.EnableWindow(FALSE);btn_add_image_watermark_.EnableWindow(FALSE);if (BST_CHECKED == btn_check_desktop_camera_switch_.GetCheck()|| BST_CHECKED == btn_check_camera_overlay_to_desktop_.GetCheck()|| BST_CHECKED == btn_check_desktop_overlay_to_camera_.GetCheck()){}else{btn_check_desktop_input_.EnableWindow(FALSE);btn_check_scale_desktop_.EnableWindow(FALSE);edit_desktop_scale_.EnableWindow(FALSE);btn_check_camera_input_.EnableWindow(FALSE);btn_check_window_input_.EnableWindow(FALSE);}DisableAuidoInputControl();}CreatePreViewWindow();ShowPreViewWindow(true);
}

NT_PB_SDKVideoPreviewImageHandle相关回调实现:

extern "C"  NT_VOID NT_CALLBACK NT_PB_SDKVideoPreviewImageHandle(NT_HANDLE handle, NT_PVOID user_data,const NT_PB_Image* image)
{if ( image != nullptr && image->format_ == NT_PB_E_IMAGE_FORMAT_RGB32&& image->width_ > 0 && image->height_ > 0&& image->plane_[0] != nullptr&& image->stride_[0] > 0){std::unique_ptr<nt_pb_rgbx_image> rgbx_image(new nt_pb_rgbx_image());rgbx_image->width_  = image->width_;rgbx_image->height_ = image->height_;rgbx_image->stride_ = image->stride_[0];rgbx_image->size_   = image->stride_[0] * image->height_;rgbx_image->data_.reset(new NT_BYTE[rgbx_image->size_]);if ( rgbx_image->data_ ){memcpy(rgbx_image->data_.get(), image->plane_[0], rgbx_image->size_);HWND hwnd = reinterpret_cast<HWND>(user_data);if (hwnd != NULL && ::IsWindow(hwnd)){::PostMessage(hwnd, WM_USER_PB_SDK_PREVIEW_RGBX_IMAGE, (WPARAM)(rgbx_image.release()), 0);}}}/*std::ostringstream ss;ss << "OnPreviewImage handle:" << handle << " w=" << image->width_<< " h=" << image->height_<< " format=" << image->format_ << " \r\n";OutputDebugStringA(ss.str().c_str());*/
}

这里通过调用封装后的nt_pb_ui_preview_wnd, 来完成本地实时预览:

LRESULT CSmartPublisherDemoDlg::OnSDKPreviewRGBXImage(WPARAM wParam, LPARAM lParam)
{nt_pb_rgbx_image* rgbx_image = (nt_pb_rgbx_image*)(wParam);if ( rgbx_image != nullptr ){std::shared_ptr<nt_pb_rgbx_image> sp_rgbx_image(rgbx_image);preview_wnd_.OnRGBXImage(sp_rgbx_image);}return S_OK;
}

具体实现如下:

void nt_pb_ui_preview_wnd::OnRGBXImage(const std::shared_ptr<nt_pb_rgbx_image>& sp_image)
{rgbx_image_ = sp_image;if (m_hWnd != NULL && ::IsWindow(m_hWnd) && ::IsWindowVisible(m_hWnd)){InvalidateRect(NULL, FALSE);}
}

OnPaint()实时刷新数据:

void nt_pb_ui_preview_wnd::OnPaint()
{CPaintDC dc(this); // device context for painting// TODO: Add your message handler code here// Do not call CWnd::OnPaint() for painting messagesif ( IsIconic() ){return;}CRect rc_client(0, 0, 0, 0);GetClientRect(rc_client);if ( rc_client.IsRectNull()|| rc_client.IsRectEmpty() ){return;}auto mem_dc = ::CreateCompatibleDC(dc.GetSafeHdc());auto mem_bitmap = ::CreateCompatibleBitmap(dc.GetSafeHdc(), rc_client.Width(), rc_client.Height());::SelectObject(mem_dc, mem_bitmap);HBRUSH brush = ::CreateSolidBrush(RGB(238, 243, 250));::FillRect(mem_dc, &rc_client, brush);::DeleteObject(brush);DrawImage(mem_dc, rc_client);::BitBlt(dc.GetSafeHdc(),0, 0,rc_client.Width(), rc_client.Height(),mem_dc,0, 0,SRCCOPY);::DeleteObject(mem_bitmap);::DeleteDC(mem_dc);
}

DrawImage()封装实现

void nt_pb_ui_preview_wnd::DrawImage(HDC hdc, const CRect& rc_client)
{ASSERT(hdc != NULL);ASSERT(!rc_client.IsRectNull());ASSERT(!rc_client.IsRectEmpty());if ( !rgbx_image_ )return;if (rc_client.Width() < 100 || rc_client.Height() < 100)return;if (draw_api_ == nullptr)return;NT_PB_Image image;memset(&image, 0, sizeof(image));image.cb_size_ = sizeof(image);image.format_  = NT_PB_E_IMAGE_FORMAT_RGB32;image.width_   = rgbx_image_->width_;image.height_  = rgbx_image_->height_;image.timestamp_ = 0;image.plane_[0]		 = rgbx_image_->data_.get();image.stride_[0]	 = rgbx_image_->stride_;image.plane_size_[0] = rgbx_image_->size_;auto limit_w = rc_client.Width() - 8;auto limit_h = rc_client.Height() - 8;auto  d_w = 0, d_h = 0;if ( rgbx_image_->width_ <= limit_w && rgbx_image_->height_ <= limit_h ){d_w = rgbx_image_->width_;d_h = rgbx_image_->height_;}else{CalScaleSize(limit_w, limit_h, rgbx_image_->width_, rgbx_image_->height_, d_w, d_h);}if ( d_w > 0 && d_h > 0 ){auto d_x = rc_client.Width()/2 - d_w/2;auto d_y = rc_client.Height()/2 - d_h/2;draw_api_->Draw(hdc, d_x, d_y,d_w, d_h,0, 0,rgbx_image_->width_, rgbx_image_->height_,&image);}
}

停止预览

调用StopPreview()即可。

void CSmartPublisherDemoDlg::OnBnClickedButtonStopPreview()
{publisher_handle_count_--;publisher_api_.StopPreview(publisher_handle_);if (0 == publisher_handle_count_){publisher_api_.Close(publisher_handle_);publisher_handle_ = NULL;}btn_preview_.EnableWindow(TRUE);btn_stop_preview_.EnableWindow(FALSE);if (0 == publisher_handle_count_){if (BST_CHECKED == btn_check_desktop_input_.GetCheck()){btn_choose_screen_region_.SetWindowTextW(L"选择屏幕区域");}btn_check_dxgi_screen_capturer_.EnableWindow(TRUE);check_capture_layered_window_.EnableWindow(TRUE);btn_check_wr_way_capture_window_.EnableWindow(TRUE);btn_desktop_camera_switch_.SetWindowTextW(L"切换到摄像头");btn_disable_image_watermark_.SetWindowTextW(L"停止水印");btn_disable_camera_overlay_.SetWindowTextW(L"停止叠加摄像头");btn_disable_desktop_overlay_.SetWindowTextW(L"停止叠加屏幕");btn_check_desktop_camera_switch_.EnableWindow(TRUE);btn_check_camera_overlay_to_desktop_.EnableWindow(TRUE);btn_check_desktop_overlay_to_camera_.EnableWindow(TRUE);btn_add_image_watermark_.EnableWindow(TRUE);if (BST_CHECKED == btn_check_desktop_camera_switch_.GetCheck()|| BST_CHECKED == btn_check_camera_overlay_to_desktop_.GetCheck()|| BST_CHECKED == btn_check_desktop_overlay_to_camera_.GetCheck()){}else{btn_check_desktop_input_.EnableWindow(TRUE);btn_check_scale_desktop_.EnableWindow(TRUE);edit_desktop_scale_.EnableWindow(TRUE);btn_check_camera_input_.EnableWindow(TRUE);btn_check_window_input_.EnableWindow(TRUE);}EnableAuidoInputControl();}ShowPreViewWindow(false);
}

 

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

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

相关文章

Outlook2016怎么修改默认数据库

Outlook2016中想要修改默认数据库&#xff0c;该怎么修改呢?下面我们就来看看详细的教程。 Outlook2016怎么修改默认数据库? 1、下载安装outlook软件。 Outlook2016怎么修改默认数据库? 2、双击打开outlook软件。 Outlook2016怎么修改默认数据库? 3、点击“文件”——…

Windows平台RTMP|RTSP播放器为什么要兼容GDI绘制

为什么要支持GDI 先说结论&#xff0c;Windows平台播放渲染这块&#xff0c;一般来说99%以上的机器都是支持D3D的&#xff0c;实现GDI模式绘制&#xff0c;除了为了好的兼容性外&#xff0c;在远程连接的场景下&#xff0c;D3D创建不成功&#xff0c;需要使用GDI模式。 简单来…

Windows平台RTMP|RTSP播放器实现画面全屏功能

我们在Windows平台实现RTSP或者RTMP播放的时候&#xff0c;有个功能是绕不开的&#xff0c;那就是播放窗口全屏。本文就以大牛直播SDK&#xff08;官方&#xff09;的Windows播放器为例&#xff0c;大概讲下大概实现&#xff1a; 全屏播放需要考虑的点不多&#xff1a; 第一&…

tim怎么设置检测到新版本自动安装 tim安全自动更新的开启方法

TIM想要开启自动检测新版本并下载安装&#xff0c;该怎么设置呢?下面我们就来看看详细的教程。 1、首先&#xff0c;在你的 电脑中找到TIM; tim怎么设置检测到新版本自动安装?tim安全自动更新的开启方法 2、打开TIM&#xff0c;登录你的个人账号&#xff0c;登陆之后&…

Android平台实现Unity3D下RTMP推送

像Unity3D下的RTMP或RTSP播放器一样&#xff0c;好多开发者苦于在Unity环境下&#xff0c;如何高效率低延迟的把数据采集并编码实时推送到流媒体服务器&#xff0c;实现Unity场景下的低延迟推拉流方案。 关于屏幕采集&#xff0c;有两种方案&#xff1a; 1. 直接封装Android原…

Windows平台实现Unity下窗体|摄像头|屏幕采集推送

技术背景 随着Unity3D的应用范围越来越广&#xff0c;越来越多的行业开始基于Unity3D开发产品&#xff0c;如传统行业中虚拟仿真教育、航空工业、室内设计、城市规划、工业仿真等领域。 基于此&#xff0c;好多开发者苦于在Unity环境下&#xff0c;没有低延迟的推拉流解决方案…

钉钉调岗申请单怎么写 钉钉申请调岗的教程

钉钉中想要申请调岗申请单&#xff0c;调岗申请单在哪&#xff0c;该怎么填写呢?下面我们就来看看详细的教程。 1、首先打开钉钉客户端&#xff0c;点击下方的工作。 钉钉调岗申请单怎么写? 钉钉申请调岗的教程 2、向下滑动找到并打开调岗申请单应用。 钉钉调岗申请单怎么…

Android平台RTMP推送端实现外部数据对接推送和录像

背景 好多开发者在做Android平台RTMP推送对接的同时&#xff0c;除了编码前的数据外&#xff0c;还有些外部编码数据推送诉求&#xff0c;他们希望外部的编码音视频数据不止可以实现RTMP推送&#xff0c;还可以同时在推送端实时录制下来&#xff0c;本文以我们&#xff08;官方…

excel怎么删除浏览记录 方法介绍

Excel 2013工作表删除浏览记录的步骤&#xff1a; 1、鼠标左键双击计算机桌面Excel2013程序图标&#xff0c;将其打开运行。在打开的Excel2013程序窗口&#xff0c;点击“空白工作薄”选项&#xff0c;新建一个空白Excel工作薄。如图所示; excel怎么删除浏览记录&#xff1f;…

爱奇艺怎么开启数据流量自动播放

1、打开手机&#xff0c;点击爱奇艺 爱奇艺怎么开启数据流量自动播放 2、进入爱奇艺&#xff0c;点击我的 爱奇艺怎么开启数据流量自动播放 3、然后在我的界面&#xff0c;点击设置 爱奇艺怎么开启数据流量自动播放 4、进入设置界面&#xff0c;点击播放与下载 爱奇艺怎…

Win7系统账户被禁用的解决方法

我们都知道Win7系统中可以建立多个账户来进行不同的登录使用&#xff0c;十分的方便&#xff0c;但是有的用户建立了多个账户&#xff0c;却发现所有的账号都被禁用&#xff0c;那遇到这个问题应该怎么解决呢&#xff0c;下面就为小伙伴们带来 了Win7系统账户被禁用的解决方法&…

Win11系统如何隐藏快速搜索

近日网络中已经出现了很多Windows 11的泄露版本&#xff0c;意味着Win11版本正式确定&#xff0c;现在只是等待微软正式发布了。Windows系统中菜单栏都会默认放一个快速搜索的图标&#xff0c;快速搜索一直是Windows系统中的一个重要功能&#xff0c;那么在Win11系统中该如何隐…

网易邮箱广告标签怎么关 163邮箱去广告标签的教程

1、打开百度输入网易邮箱 网易邮箱广告标签怎么关? 163邮箱去广告标签的教程 网易邮箱广告标签怎么关? 163邮箱去广告标签的教程 2、输入账号和密码&#xff0c;进入邮箱主页&#xff0c;在标签栏看到广告标签 网易邮箱广告标签怎么关? 163邮箱去广告标签的教程 3、选中…

Linux|麒麟操作系统实现多路RTMP|RTSP播放

技术背景 无论是Windows平台还是Linux&#xff0c;多路播放诉求非常普遍&#xff0c;比如针对智慧工地、展馆、教育等宏观场景下的摄像头展示&#xff0c;关于RTSP或RTMP直播播放器开发需要注意的点&#xff0c;可参考之前博客&#xff0c;总的来说有以下一些点&#xff1a; …

TIM提示“个人文件夹被占用,请稍候再登录”怎么解决

在登录TIM的时候&#xff0c;弹出提示“个人文件夹被占用&#xff0c;请稍后再登录”的提示&#xff0c;无法正常登录TIM TIM提示“个人文件夹被占用&#xff0c;请稍候再登录”怎么解决&#xff1f; 这个时候&#xff0c;我们可以直接关闭上面的登录窗口&#xff0c;然后过一…

数据推送选择GB28181、RTSP还是RTMP?

GB/T28181 国标GB/T28181协议全称《安全防范视频监控联网系统信息传输、交换、控制技术要求》&#xff0c;是一个定义视频联网传输和设备控制标准的白皮书&#xff0c;由公安部科技信息化局提出&#xff0c;该标准规定了城市监控报警联网系统中信息传输、交换、控制的互联结构…

Windows平台RTMP推送摄像头对接介绍

背景 好多开发者在对接大牛直播SDK&#xff08;官方&#xff09;的Windows平台RTMP推送时&#xff0c;不熟悉摄像头调用&#xff0c;实际上&#xff0c;摄像头调用逻辑并不复杂&#xff0c;以下是大概流程&#xff1a; 首先调用我们sdk接口获取摄像头个数&#xff0c;调用接口…

Outlook2016未读邮件怎么设置字体颜色

Outlook2016中想要设置未读邮件的字体颜色&#xff0c;该怎么设置呢?下面我们就来看看详细的教程。 Outlook2016未读邮件怎么设置字体颜色? 1、下载安装outlook软件。 Outlook2016未读邮件怎么设置字体颜色? 2、双击打开outlook软件&#xff0c;登入邮箱账户。 Outlook…

Android平台RTMP推送模块如何对接NV21、YV12、RGB、YUV等编码前数据

前言 我们在对接Android平台摄像头或者屏幕采集、编码打包推送场景的时候&#xff0c;随着采集设备的不同&#xff0c;出来的数据也是多样化的&#xff0c;比如NV21、YV12、RGB、YUV等&#xff0c;更有图像数据甚至是翻转或者倒置的&#xff0c;如果上层处理&#xff0c;效率低…

庖丁解牛之-Android平台RTSP|RTMP播放器设计

背景 我们在做Android平台RTSP或者RTMP播放器开发的时候&#xff0c;需要注意的点非常多&#xff0c;以下&#xff0c;以大牛直播SDK(官方)的接口为例&#xff0c;大概介绍下相关接口设计&#xff1a; 接口设计 1. Open() 接口 Open接口的目的&#xff0c;主要是创建实例&a…