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

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

背景

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


废话不多说,想上图:

20210512151006750.png

如何实现

开始预览

开始预览,大概的流程是,调用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, // 已经编码的视频数据,目前支持H264
    NT_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 messages
 
    if ( 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);
}


相关实践学习
基于阿里云DeepGPU实例,用AI画唯美国风少女
本实验基于阿里云DeepGPU实例,使用aiacctorch加速stable-diffusion-webui,用AI画唯美国风少女,可提升性能至高至原性能的2.6倍。
相关文章
|
15小时前
|
存储 数据安全/隐私保护 Windows
4.5 Windows驱动开发:内核中实现进程数据转储
多数ARK反内核工具中都存在驱动级别的内存转存功能,该功能可以将应用层中运行进程的内存镜像转存到特定目录下,内存转存功能在应对加壳程序的分析尤为重要,当进程在内存中解码后,我们可以很容易的将内存镜像导出,从而更好的对样本进行分析,当然某些加密壳可能无效但绝大多数情况下是可以被转存的。
4.5 Windows驱动开发:内核中实现进程数据转储
|
10月前
|
存储 监控 数据可视化
在Windows下使用JMeter集群+Influxdb存储+Grafana实时数据展示
在Windows下使用JMeter集群+Influxdb存储+Grafana实时数据展示
212 0
在Windows下使用JMeter集群+Influxdb存储+Grafana实时数据展示
|
12月前
|
JSON 关系型数据库 MySQL
windows python web flask获取请求参数数据
windows python web flask获取请求参数数据
windows python web flask获取请求参数数据
|
12月前
|
JSON 关系型数据库 MySQL
windows python flask读取文件数据并返回表格
windows python flask读取文件数据并返回表格
windows python flask读取文件数据并返回表格
|
12月前
|
JSON Linux API
windows python flask返回json数据
windows python flask返回json数据
windows python flask返回json数据
|
移动开发 Unix Linux
ACM题目中关于数据的生成以及OJ上Linux和Windows里回车和换行的处理
ACM题目中关于数据的生成以及OJ上Linux和Windows里回车和换行的处理
ACM题目中关于数据的生成以及OJ上Linux和Windows里回车和换行的处理
|
消息中间件 安全 API
C#实现操作Windows窗口句柄:SendMessage/PostMessage发送系统消息、事件和数据【窗口句柄总结之二】
SendMessage/PostMessage API 可以实现发送系统消息,这些消息可以定义为常见的鼠标或键盘事件、数据的发送等各种系统操作......
3053 1
C#实现操作Windows窗口句柄:SendMessage/PostMessage发送系统消息、事件和数据【窗口句柄总结之二】
|
SQL 网络协议 数据挖掘
Windows下载安装配置SQL Server、SSMS,使用Python连接读写数据
Windows下载安装配置SQL Server、SSMS,使用Python连接读写数据
286 0
Windows下载安装配置SQL Server、SSMS,使用Python连接读写数据
|
XML API TensorFlow
如何在Windows系统上使用Object Detection API训练自己的数据?
之前写了一篇如何在windows系统上安装Tensorflow Object Detection API?(点击跳转) 然后就想着把数据集换成自己的数据集进行训练得到自己的目标检测模型。动手之前先学习了一波别人是如何实现的,看了大多数教程都有一个小问题:用VOC2012数据集进行训练当做用自己的数据集。 然而,初心想看的是自己的数据集啊!于是就自己来撸一篇教程,方便自己也给别人一些参考吧~
211 0
如何在Windows系统上使用Object Detection API训练自己的数据?
|
Windows
【Windows 逆向】OD 调试器工具 ( OD 调试数据时硬件断点对应的关键代码 | 删除硬件端点恢复运行 )
【Windows 逆向】OD 调试器工具 ( OD 调试数据时硬件断点对应的关键代码 | 删除硬件端点恢复运行 )
232 0
【Windows 逆向】OD 调试器工具 ( OD 调试数据时硬件断点对应的关键代码 | 删除硬件端点恢复运行 )
相关产品
云迁移中心
推荐文章
更多