From 6e69c370562e7c436a429b3b1caf43aff6a972f1 Mon Sep 17 00:00:00 2001 From: dijunkun Date: Mon, 26 May 2025 15:29:46 +0800 Subject: [PATCH] [feat] use display name as video stream id --- .../linux/screen_capturer_x11.cpp | 2 +- .../macosx/screen_capturer_sck_impl.mm | 108 ++++++++++++++---- src/screen_capturer/screen_capturer.h | 2 +- .../windows/screen_capturer_wgc.cpp | 2 +- src/single_window/render.cpp | 45 +++++--- src/single_window/render.h | 1 + thirdparty/projectx | 2 +- 7 files changed, 116 insertions(+), 46 deletions(-) diff --git a/src/screen_capturer/linux/screen_capturer_x11.cpp b/src/screen_capturer/linux/screen_capturer_x11.cpp index 646a1a9..704ce06 100644 --- a/src/screen_capturer/linux/screen_capturer_x11.cpp +++ b/src/screen_capturer/linux/screen_capturer_x11.cpp @@ -104,7 +104,7 @@ void ScreenCapturerX11::OnFrame() { nv12.insert(nv12.end(), uv_plane_.begin(), uv_plane_.end()); if (callback_) { - callback_(nv12.data(), width_ * height_ * 3 / 2, width_, height_); + callback_(nv12.data(), width_ * height_ * 3 / 2, width_, height_, ""); } XDestroyImage(image); diff --git a/src/screen_capturer/macosx/screen_capturer_sck_impl.mm b/src/screen_capturer/macosx/screen_capturer_sck_impl.mm index 0208bb0..a216221 100644 --- a/src/screen_capturer/macosx/screen_capturer_sck_impl.mm +++ b/src/screen_capturer/macosx/screen_capturer_sck_impl.mm @@ -10,7 +10,10 @@ #include "screen_capturer_sck.h" +#include #include +#include +#include #include #include #include @@ -69,6 +72,7 @@ class API_AVAILABLE(macos(14.0)) ScreenCapturerSckImpl : public ScreenCapturer { std::vector display_info_list_; std::map display_id_map_; std::map display_id_map_reverse_; + std::map display_id_name_map_; unsigned char *nv12_frame_ = nullptr; int width_ = 0; int height_ = 0; @@ -107,6 +111,66 @@ class API_AVAILABLE(macos(14.0)) ScreenCapturerSckImpl : public ScreenCapturer { CGDirectDisplayID current_display_ = 0; }; +std::string GetDisplayName(CGDirectDisplayID display_id) { + io_iterator_t iter; + io_service_t serv = 0, matched_serv = 0; + + CFMutableDictionaryRef matching = IOServiceMatching("IODisplayConnect"); + if (IOServiceGetMatchingServices(kIOMainPortDefault, matching, &iter) != KERN_SUCCESS) { + return ""; + } + + while ((serv = IOIteratorNext(iter)) != 0) { + CFDictionaryRef info = IODisplayCreateInfoDictionary(serv, kIODisplayOnlyPreferredName); + if (info) { + CFNumberRef vendorID = (CFNumberRef)CFDictionaryGetValue(info, CFSTR(kDisplayVendorID)); + CFNumberRef productID = (CFNumberRef)CFDictionaryGetValue(info, CFSTR(kDisplayProductID)); + uint32_t vID = 0, pID = 0; + if (vendorID && productID && CFNumberGetValue(vendorID, kCFNumberIntType, &vID) && + CFNumberGetValue(productID, kCFNumberIntType, &pID) && + vID == CGDisplayVendorNumber(display_id) && pID == CGDisplayModelNumber(display_id)) { + matched_serv = serv; + CFRelease(info); + break; + } + CFRelease(info); + } + IOObjectRelease(serv); + } + IOObjectRelease(iter); + + if (!matched_serv) return ""; + + CFDictionaryRef display_info = + IODisplayCreateInfoDictionary(matched_serv, kIODisplayOnlyPreferredName); + IOObjectRelease(matched_serv); + if (!display_info) return ""; + + CFDictionaryRef product_name_dict = + (CFDictionaryRef)CFDictionaryGetValue(display_info, CFSTR(kDisplayProductName)); + std::string result; + if (product_name_dict) { + CFIndex count = CFDictionaryGetCount(product_name_dict); + if (count > 0) { + std::vector keys(count); + std::vector values(count); + CFDictionaryGetKeysAndValues(product_name_dict, keys.data(), values.data()); + CFStringRef name_ref = (CFStringRef)values[0]; + if (name_ref) { + CFIndex maxSize = + CFStringGetMaximumSizeForEncoding(CFStringGetLength(name_ref), kCFStringEncodingUTF8) + + 1; + std::vector buffer(maxSize); + if (CFStringGetCString(name_ref, buffer.data(), buffer.size(), kCFStringEncodingUTF8)) { + result = buffer.data(); + } + } + } + } + CFRelease(display_info); + return result; +} + ScreenCapturerSckImpl::ScreenCapturerSckImpl() { helper_ = [[SckHelper alloc] initWithCapturer:this]; } @@ -115,6 +179,7 @@ ScreenCapturerSckImpl::~ScreenCapturerSckImpl() { display_info_list_.clear(); display_id_map_.clear(); display_id_map_reverse_.clear(); + display_id_name_map_.clear(); if (nv12_frame_) { delete[] nv12_frame_; @@ -147,6 +212,10 @@ int ScreenCapturerSckImpl::Init(const int fps, cb_desktop_data cb) { return 0; } + CGDirectDisplayID displays[10]; + uint32_t count; + CGGetActiveDisplayList(10, displays, &count); + int unnamed_count = 1; for (SCDisplay *display in content.displays) { CGDirectDisplayID display_id = display.displayID; @@ -154,12 +223,7 @@ int ScreenCapturerSckImpl::Init(const int fps, cb_desktop_data cb) { bool is_primary = CGDisplayIsMain(display_id); std::string name; - if ([display respondsToSelector:@selector(displayName)]) { - NSString *display_name = [display performSelector:@selector(displayName)]; - if (display_name && [display_name length] > 0) { - name = [display_name UTF8String]; - } - } + name = GetDisplayName(display_id); if (name.empty()) { name = "Display " + std::to_string(unnamed_count++); @@ -173,6 +237,7 @@ int ScreenCapturerSckImpl::Init(const int fps, cb_desktop_data cb) { display_info_list_.push_back(info); display_id_map_[display_info_list_.size() - 1] = display_id; display_id_map_reverse_[display_id] = display_info_list_.size() - 1; + display_id_name_map_[display_id] = name; } return 0; @@ -184,16 +249,15 @@ int ScreenCapturerSckImpl::Start() { } int ScreenCapturerSckImpl::SwitchTo(int monitor_index) { - bool stream_started = false; - { - std::lock_guard lock(lock_); + if (stream_) { + [stream_ stopCaptureWithCompletionHandler:^(NSError *error) { + std::lock_guard lock(lock_); + stream_ = nil; + current_display_ = display_id_map_[monitor_index]; + StartOrReconfigureCapturer(); + }]; + } else { current_display_ = display_id_map_[monitor_index]; - if (stream_) { - stream_started = true; - } - } - // If the capturer was already started, reconfigure it. Otherwise, wait until Start() gets called. - if (stream_started) { StartOrReconfigureCapturer(); } return 0; @@ -217,19 +281,15 @@ void ScreenCapturerSckImpl::OnShareableContentCreated(SCShareableContent *conten std::lock_guard lock(lock_); for (SCDisplay *display in content.displays) { if (current_display_ == display.displayID) { - LOG_WARN("current_display_: {}", current_display_); + LOG_WARN("current display: {}, name: {}", current_display_, + display_id_name_map_[current_display_]); captured_display = display; break; } } if (!captured_display) { - if (kFullDesktopScreenId == current_display_) { - LOG_ERROR("Full screen capture is not supported, falling back to first " - "display"); - } else { - LOG_ERROR("Display [{}] not found, falling back to first display", current_display_); - } captured_display = content.displays.firstObject; + current_display_ = captured_display.displayID; } } @@ -240,7 +300,7 @@ void ScreenCapturerSckImpl::OnShareableContentCreated(SCShareableContent *conten config.showsCursor = false; config.width = filter.contentRect.size.width * filter.pointPixelScale; config.height = filter.contentRect.size.height * filter.pointPixelScale; - config.captureResolution = SCCaptureResolutionNominal; + config.captureResolution = SCCaptureResolutionAutomatic; config.minimumFrameInterval = CMTimeMake(1, fps_); std::lock_guard lock(lock_); @@ -320,7 +380,7 @@ void ScreenCapturerSckImpl::OnNewCVPixelBuffer(CVPixelBufferRef pixelBuffer, } _on_data(nv12_frame_, width * height * 3 / 2, width, height, - display_id_map_reverse_[current_display_]); + display_id_name_map_[current_display_].c_str()); CVPixelBufferUnlockBaseAddress(pixelBuffer, kCVPixelBufferLock_ReadOnly); } diff --git a/src/screen_capturer/screen_capturer.h b/src/screen_capturer/screen_capturer.h index 8b214af..0152ca7 100644 --- a/src/screen_capturer/screen_capturer.h +++ b/src/screen_capturer/screen_capturer.h @@ -13,7 +13,7 @@ class ScreenCapturer { public: - typedef std::function + typedef std::function cb_desktop_data; public: diff --git a/src/screen_capturer/windows/screen_capturer_wgc.cpp b/src/screen_capturer/windows/screen_capturer_wgc.cpp index 9294fc2..7838837 100644 --- a/src/screen_capturer/windows/screen_capturer_wgc.cpp +++ b/src/screen_capturer/windows/screen_capturer_wgc.cpp @@ -255,7 +255,7 @@ void ScreenCapturerWgc::OnFrame(const WgcSession::wgc_session_frame &frame, frame.width, frame.width, frame.height); on_data_(nv12_frame_, frame.width * frame.height * 3 / 2, frame.width, - frame.height, id); + frame.height, display_info_list_[id].name.c_str()); } } diff --git a/src/single_window/render.cpp b/src/single_window/render.cpp index 19b4ddc..45c31ef 100644 --- a/src/single_window/render.cpp +++ b/src/single_window/render.cpp @@ -282,7 +282,7 @@ int Render::LoadSettingsFromCacheFile() { return 0; } -int Render::StartScreenCapturer() { +int Render::ScreenCapturerInit() { screen_capturer_ = (ScreenCapturer*)screen_capturer_factory_->Create(); last_frame_time_ = std::chrono::duration_cast( std::chrono::steady_clock::now().time_since_epoch()) @@ -291,7 +291,7 @@ int Render::StartScreenCapturer() { int screen_capturer_init_ret = screen_capturer_->Init( 60, [this](unsigned char* data, int size, int width, int height, - int display_id) -> void { + const char* display_name) -> void { auto now_time = std::chrono::duration_cast( std::chrono::steady_clock::now().time_since_epoch()) .count(); @@ -303,19 +303,30 @@ int Render::StartScreenCapturer() { frame.width = width; frame.height = height; frame.captured_timestamp = GetSystemTimeMicros(peer_); - SendVideoFrame(peer_, &frame, - display_id == 0 ? video_primary_label_.c_str() - : video_secondary_label_.c_str()); + SendVideoFrame(peer_, &frame, display_name); last_frame_time_ = now_time; } }); if (0 == screen_capturer_init_ret) { - screen_capturer_->Start(); + LOG_INFO("Init screen capturer success"); + if (display_info_list_.empty()) { + display_info_list_ = screen_capturer_->GetDisplayInfoList(); + } + return 0; } else { + LOG_ERROR("Init screen capturer failed"); screen_capturer_->Destroy(); delete screen_capturer_; screen_capturer_ = nullptr; + return -1; + } +} + +int Render::StartScreenCapturer() { + if (screen_capturer_) { + LOG_INFO("Start screen capturer") + screen_capturer_->Start(); } return 0; @@ -370,9 +381,6 @@ int Render::StartMouseController() { mouse_controller_ = (MouseController*)device_controller_factory_->Create( DeviceControllerFactory::Device::Mouse); - if (screen_capturer_ && display_info_list_.empty()) { - display_info_list_ = screen_capturer_->GetDisplayInfoList(); - } int mouse_controller_init_ret = mouse_controller_->Init(display_info_list_); if (0 != mouse_controller_init_ret) { LOG_INFO("Destroy mouse controller") @@ -461,12 +469,17 @@ int Render::CreateConnectionPeer() { LOG_INFO("Create peer [{}] instance failed", client_id_); } - AddVideoStream(peer_, video_primary_label_.c_str()); - AddVideoStream(peer_, video_secondary_label_.c_str()); - AddAudioStream(peer_, audio_label_.c_str()); - AddDataStream(peer_, data_label_.c_str()); + if (0 == ScreenCapturerInit()) { + for (auto& display_info : display_info_list_) { + AddVideoStream(peer_, display_info.name.c_str()); + } - return 0; + AddAudioStream(peer_, audio_label_.c_str()); + AddDataStream(peer_, data_label_.c_str()); + return 0; + } else { + return -1; + } } int Render::AudioDeviceInit() { @@ -940,10 +953,6 @@ void Render::MainLoop() { } if (need_to_send_host_info_) { - if (screen_capturer_ && display_info_list_.empty()) { - display_info_list_ = screen_capturer_->GetDisplayInfoList(); - } - RemoteAction remote_action; remote_action.i.display_num = display_info_list_.size(); remote_action.i.display_list = diff --git a/src/single_window/render.h b/src/single_window/render.h index 7502350..9bb5fee 100644 --- a/src/single_window/render.h +++ b/src/single_window/render.h @@ -212,6 +212,7 @@ class Render { int SaveSettingsIntoCacheFile(); int LoadSettingsFromCacheFile(); + int ScreenCapturerInit(); int StartScreenCapturer(); int StopScreenCapturer(); diff --git a/thirdparty/projectx b/thirdparty/projectx index a09fbd0..b3802e6 160000 --- a/thirdparty/projectx +++ b/thirdparty/projectx @@ -1 +1 @@ -Subproject commit a09fbd0be41b2538e1cc59952f4d5ea56b96863b +Subproject commit b3802e65ee1db2ad99efa8213f5ba82773b3a53d