[feat] use display name as video stream id

This commit is contained in:
dijunkun
2025-05-26 15:29:46 +08:00
parent 8f5dd21e75
commit 6e69c37056
7 changed files with 116 additions and 46 deletions

View File

@@ -104,7 +104,7 @@ void ScreenCapturerX11::OnFrame() {
nv12.insert(nv12.end(), uv_plane_.begin(), uv_plane_.end()); nv12.insert(nv12.end(), uv_plane_.begin(), uv_plane_.end());
if (callback_) { if (callback_) {
callback_(nv12.data(), width_ * height_ * 3 / 2, width_, height_); callback_(nv12.data(), width_ * height_ * 3 / 2, width_, height_, "");
} }
XDestroyImage(image); XDestroyImage(image);

View File

@@ -10,7 +10,10 @@
#include "screen_capturer_sck.h" #include "screen_capturer_sck.h"
#include <ApplicationServices/ApplicationServices.h>
#include <CoreGraphics/CoreGraphics.h> #include <CoreGraphics/CoreGraphics.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/graphics/IOGraphicsLib.h>
#include <IOSurface/IOSurface.h> #include <IOSurface/IOSurface.h>
#include <ScreenCaptureKit/ScreenCaptureKit.h> #include <ScreenCaptureKit/ScreenCaptureKit.h>
#include <atomic> #include <atomic>
@@ -69,6 +72,7 @@ class API_AVAILABLE(macos(14.0)) ScreenCapturerSckImpl : public ScreenCapturer {
std::vector<DisplayInfo> display_info_list_; std::vector<DisplayInfo> display_info_list_;
std::map<int, CGDirectDisplayID> display_id_map_; std::map<int, CGDirectDisplayID> display_id_map_;
std::map<CGDirectDisplayID, int> display_id_map_reverse_; std::map<CGDirectDisplayID, int> display_id_map_reverse_;
std::map<CGDirectDisplayID, std::string> display_id_name_map_;
unsigned char *nv12_frame_ = nullptr; unsigned char *nv12_frame_ = nullptr;
int width_ = 0; int width_ = 0;
int height_ = 0; int height_ = 0;
@@ -107,6 +111,66 @@ class API_AVAILABLE(macos(14.0)) ScreenCapturerSckImpl : public ScreenCapturer {
CGDirectDisplayID current_display_ = 0; 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<const void *> keys(count);
std::vector<const void *> 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<char> buffer(maxSize);
if (CFStringGetCString(name_ref, buffer.data(), buffer.size(), kCFStringEncodingUTF8)) {
result = buffer.data();
}
}
}
}
CFRelease(display_info);
return result;
}
ScreenCapturerSckImpl::ScreenCapturerSckImpl() { ScreenCapturerSckImpl::ScreenCapturerSckImpl() {
helper_ = [[SckHelper alloc] initWithCapturer:this]; helper_ = [[SckHelper alloc] initWithCapturer:this];
} }
@@ -115,6 +179,7 @@ ScreenCapturerSckImpl::~ScreenCapturerSckImpl() {
display_info_list_.clear(); display_info_list_.clear();
display_id_map_.clear(); display_id_map_.clear();
display_id_map_reverse_.clear(); display_id_map_reverse_.clear();
display_id_name_map_.clear();
if (nv12_frame_) { if (nv12_frame_) {
delete[] nv12_frame_; delete[] nv12_frame_;
@@ -147,6 +212,10 @@ int ScreenCapturerSckImpl::Init(const int fps, cb_desktop_data cb) {
return 0; return 0;
} }
CGDirectDisplayID displays[10];
uint32_t count;
CGGetActiveDisplayList(10, displays, &count);
int unnamed_count = 1; int unnamed_count = 1;
for (SCDisplay *display in content.displays) { for (SCDisplay *display in content.displays) {
CGDirectDisplayID display_id = display.displayID; 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); bool is_primary = CGDisplayIsMain(display_id);
std::string name; std::string name;
if ([display respondsToSelector:@selector(displayName)]) { name = GetDisplayName(display_id);
NSString *display_name = [display performSelector:@selector(displayName)];
if (display_name && [display_name length] > 0) {
name = [display_name UTF8String];
}
}
if (name.empty()) { if (name.empty()) {
name = "Display " + std::to_string(unnamed_count++); 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_info_list_.push_back(info);
display_id_map_[display_info_list_.size() - 1] = display_id; display_id_map_[display_info_list_.size() - 1] = display_id;
display_id_map_reverse_[display_id] = display_info_list_.size() - 1; display_id_map_reverse_[display_id] = display_info_list_.size() - 1;
display_id_name_map_[display_id] = name;
} }
return 0; return 0;
@@ -184,16 +249,15 @@ int ScreenCapturerSckImpl::Start() {
} }
int ScreenCapturerSckImpl::SwitchTo(int monitor_index) { int ScreenCapturerSckImpl::SwitchTo(int monitor_index) {
bool stream_started = false;
{
std::lock_guard<std::mutex> lock(lock_);
current_display_ = display_id_map_[monitor_index];
if (stream_) { if (stream_) {
stream_started = true; [stream_ stopCaptureWithCompletionHandler:^(NSError *error) {
} std::lock_guard<std::mutex> lock(lock_);
} stream_ = nil;
// If the capturer was already started, reconfigure it. Otherwise, wait until Start() gets called. current_display_ = display_id_map_[monitor_index];
if (stream_started) { StartOrReconfigureCapturer();
}];
} else {
current_display_ = display_id_map_[monitor_index];
StartOrReconfigureCapturer(); StartOrReconfigureCapturer();
} }
return 0; return 0;
@@ -217,19 +281,15 @@ void ScreenCapturerSckImpl::OnShareableContentCreated(SCShareableContent *conten
std::lock_guard<std::mutex> lock(lock_); std::lock_guard<std::mutex> lock(lock_);
for (SCDisplay *display in content.displays) { for (SCDisplay *display in content.displays) {
if (current_display_ == display.displayID) { 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; captured_display = display;
break; break;
} }
} }
if (!captured_display) { 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; captured_display = content.displays.firstObject;
current_display_ = captured_display.displayID;
} }
} }
@@ -240,7 +300,7 @@ void ScreenCapturerSckImpl::OnShareableContentCreated(SCShareableContent *conten
config.showsCursor = false; config.showsCursor = false;
config.width = filter.contentRect.size.width * filter.pointPixelScale; config.width = filter.contentRect.size.width * filter.pointPixelScale;
config.height = filter.contentRect.size.height * filter.pointPixelScale; config.height = filter.contentRect.size.height * filter.pointPixelScale;
config.captureResolution = SCCaptureResolutionNominal; config.captureResolution = SCCaptureResolutionAutomatic;
config.minimumFrameInterval = CMTimeMake(1, fps_); config.minimumFrameInterval = CMTimeMake(1, fps_);
std::lock_guard<std::mutex> lock(lock_); std::lock_guard<std::mutex> lock(lock_);
@@ -320,7 +380,7 @@ void ScreenCapturerSckImpl::OnNewCVPixelBuffer(CVPixelBufferRef pixelBuffer,
} }
_on_data(nv12_frame_, width * height * 3 / 2, width, height, _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); CVPixelBufferUnlockBaseAddress(pixelBuffer, kCVPixelBufferLock_ReadOnly);
} }

View File

@@ -13,7 +13,7 @@
class ScreenCapturer { class ScreenCapturer {
public: public:
typedef std::function<void(unsigned char*, int, int, int, int)> typedef std::function<void(unsigned char*, int, int, int, const char*)>
cb_desktop_data; cb_desktop_data;
public: public:

View File

@@ -255,7 +255,7 @@ void ScreenCapturerWgc::OnFrame(const WgcSession::wgc_session_frame &frame,
frame.width, frame.width, frame.height); frame.width, frame.width, frame.height);
on_data_(nv12_frame_, frame.width * frame.height * 3 / 2, frame.width, on_data_(nv12_frame_, frame.width * frame.height * 3 / 2, frame.width,
frame.height, id); frame.height, display_info_list_[id].name.c_str());
} }
} }

View File

@@ -282,7 +282,7 @@ int Render::LoadSettingsFromCacheFile() {
return 0; return 0;
} }
int Render::StartScreenCapturer() { int Render::ScreenCapturerInit() {
screen_capturer_ = (ScreenCapturer*)screen_capturer_factory_->Create(); screen_capturer_ = (ScreenCapturer*)screen_capturer_factory_->Create();
last_frame_time_ = std::chrono::duration_cast<std::chrono::milliseconds>( last_frame_time_ = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now().time_since_epoch()) std::chrono::steady_clock::now().time_since_epoch())
@@ -291,7 +291,7 @@ int Render::StartScreenCapturer() {
int screen_capturer_init_ret = screen_capturer_->Init( int screen_capturer_init_ret = screen_capturer_->Init(
60, 60,
[this](unsigned char* data, int size, int width, int height, [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::milliseconds>( auto now_time = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now().time_since_epoch()) std::chrono::steady_clock::now().time_since_epoch())
.count(); .count();
@@ -303,19 +303,30 @@ int Render::StartScreenCapturer() {
frame.width = width; frame.width = width;
frame.height = height; frame.height = height;
frame.captured_timestamp = GetSystemTimeMicros(peer_); frame.captured_timestamp = GetSystemTimeMicros(peer_);
SendVideoFrame(peer_, &frame, SendVideoFrame(peer_, &frame, display_name);
display_id == 0 ? video_primary_label_.c_str()
: video_secondary_label_.c_str());
last_frame_time_ = now_time; last_frame_time_ = now_time;
} }
}); });
if (0 == screen_capturer_init_ret) { 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 { } else {
LOG_ERROR("Init screen capturer failed");
screen_capturer_->Destroy(); screen_capturer_->Destroy();
delete screen_capturer_; delete screen_capturer_;
screen_capturer_ = nullptr; screen_capturer_ = nullptr;
return -1;
}
}
int Render::StartScreenCapturer() {
if (screen_capturer_) {
LOG_INFO("Start screen capturer")
screen_capturer_->Start();
} }
return 0; return 0;
@@ -370,9 +381,6 @@ int Render::StartMouseController() {
mouse_controller_ = (MouseController*)device_controller_factory_->Create( mouse_controller_ = (MouseController*)device_controller_factory_->Create(
DeviceControllerFactory::Device::Mouse); 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_); int mouse_controller_init_ret = mouse_controller_->Init(display_info_list_);
if (0 != mouse_controller_init_ret) { if (0 != mouse_controller_init_ret) {
LOG_INFO("Destroy mouse controller") LOG_INFO("Destroy mouse controller")
@@ -461,12 +469,17 @@ int Render::CreateConnectionPeer() {
LOG_INFO("Create peer [{}] instance failed", client_id_); LOG_INFO("Create peer [{}] instance failed", client_id_);
} }
AddVideoStream(peer_, video_primary_label_.c_str()); if (0 == ScreenCapturerInit()) {
AddVideoStream(peer_, video_secondary_label_.c_str()); for (auto& display_info : display_info_list_) {
AddVideoStream(peer_, display_info.name.c_str());
}
AddAudioStream(peer_, audio_label_.c_str()); AddAudioStream(peer_, audio_label_.c_str());
AddDataStream(peer_, data_label_.c_str()); AddDataStream(peer_, data_label_.c_str());
return 0; return 0;
} else {
return -1;
}
} }
int Render::AudioDeviceInit() { int Render::AudioDeviceInit() {
@@ -940,10 +953,6 @@ void Render::MainLoop() {
} }
if (need_to_send_host_info_) { if (need_to_send_host_info_) {
if (screen_capturer_ && display_info_list_.empty()) {
display_info_list_ = screen_capturer_->GetDisplayInfoList();
}
RemoteAction remote_action; RemoteAction remote_action;
remote_action.i.display_num = display_info_list_.size(); remote_action.i.display_num = display_info_list_.size();
remote_action.i.display_list = remote_action.i.display_list =

View File

@@ -212,6 +212,7 @@ class Render {
int SaveSettingsIntoCacheFile(); int SaveSettingsIntoCacheFile();
int LoadSettingsFromCacheFile(); int LoadSettingsFromCacheFile();
int ScreenCapturerInit();
int StartScreenCapturer(); int StartScreenCapturer();
int StopScreenCapturer(); int StopScreenCapturer();