Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(macos/capture): support for capture display other than main display #2449

Merged
merged 19 commits into from
Apr 22, 2024
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
a161573
Complete output_name implementation in macos to select a different m…
TimmyOVO Apr 19, 2024
59c3978
Update locale for both en.json and en_GB.json
TimmyOVO Apr 19, 2024
81cf98f
Improved detected displays startup log format (now every display has …
TimmyOVO Apr 19, 2024
4d079a4
Update macOS output_name doc
TimmyOVO Apr 19, 2024
ecc86b8
Update macOS output_name example doc
TimmyOVO Apr 19, 2024
79326e1
Merge branch 'nightly' into nightly
TimmyOVO Apr 19, 2024
50f47d5
Reordered some entries & locale cleanup.
TimmyOVO Apr 20, 2024
1bc0b2c
Merge remote-tracking branch 'origin/nightly' into nightly
TimmyOVO Apr 20, 2024
6a377ec
Unify startup display detection log format in macOS and Linux
TimmyOVO Apr 20, 2024
7286502
Merge output_name doc Linux and macOS section into one.
TimmyOVO Apr 20, 2024
22762e7
Update config.html to match the macOS & Linux display detection code.
TimmyOVO Apr 20, 2024
a3c3f01
Merge macOS and linux output_name config ui into one.
TimmyOVO Apr 20, 2024
e9dd2aa
Merge branch 'nightly' into nightly
TimmyOVO Apr 20, 2024
70447d6
Separated output_name linux and macos in doc examples and config ui
TimmyOVO Apr 22, 2024
a23b491
Merge remote-tracking branch 'origin/nightly' into nightly
TimmyOVO Apr 22, 2024
ee4014b
Merge branch 'nightly' into nightly
TimmyOVO Apr 22, 2024
32f5c1c
SUNSHINE_EXTERNAL_LIBRARIES in macos reordered.
TimmyOVO Apr 22, 2024
8a6b0bb
Merge remote-tracking branch 'origin/nightly' into nightly
TimmyOVO Apr 22, 2024
a755c7e
doc spacing issue fixed.
TimmyOVO Apr 22, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion cmake/compile_definitions/macos.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ list(APPEND SUNSHINE_EXTERNAL_LIBRARIES
${CORE_MEDIA_LIBRARY}
${CORE_VIDEO_LIBRARY}
${VIDEO_TOOLBOX_LIBRARY}
${FOUNDATION_LIBRARY})
${FOUNDATION_LIBRARY}
${APP_KIT_LIBRARY})
TimmyOVO marked this conversation as resolved.
Show resolved Hide resolved

set(PLATFORM_INCLUDE_DIRS
${Boost_INCLUDE_DIR})
Expand Down
1 change: 1 addition & 0 deletions cmake/dependencies/macos.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ FIND_LIBRARY(CORE_MEDIA_LIBRARY CoreMedia)
FIND_LIBRARY(CORE_VIDEO_LIBRARY CoreVideo)
FIND_LIBRARY(FOUNDATION_LIBRARY Foundation)
FIND_LIBRARY(VIDEO_TOOLBOX_LIBRARY VideoToolbox)
FIND_LIBRARY(APP_KIT_LIBRARY AppKit)
TimmyOVO marked this conversation as resolved.
Show resolved Hide resolved

if(SUNSHINE_ENABLE_TRAY)
FIND_LIBRARY(COCOA Cocoa REQUIRED)
Expand Down
16 changes: 14 additions & 2 deletions docs/source/about/advanced_usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -587,7 +587,16 @@ keybindings

You need to use the value before the colon in the output, e.g. ``1``.

.. todo:: macOS
**macOS**
During Sunshine startup, you should see the list of detected displays:

.. code-block:: text

Info: Detecting displays...
Info: Detected display: M27P20 (id: 3)
Info: Detected display: 27M2U Pro (id: 2)

You need to use the value inside the parenthesis, e.g. ``3``.

**Windows**
.. code-block:: batch
Expand All @@ -603,7 +612,10 @@ keybindings

output_name = 0

.. todo:: macOS
**macOS**
.. code-block:: text

output_name = 3

**Windows**
.. code-block:: text
Expand Down
2 changes: 2 additions & 0 deletions src/platform/macos/av_video.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#pragma once

#import <AVFoundation/AVFoundation.h>
#import <AppKit/AppKit.h>

struct CaptureSession {
AVCaptureVideoDataOutput *output;
Expand All @@ -29,6 +30,7 @@ typedef bool (^FrameCallbackBlock)(CMSampleBufferRef);
@property (nonatomic, assign) NSMapTable<AVCaptureConnection *, dispatch_semaphore_t> *captureSignals;

+ (NSArray<NSDictionary *> *)displayNames;
+ (NSString *)getDisplayName:(CGDirectDisplayID)displayID;

- (id)initWithDisplay:(CGDirectDisplayID)displayID frameRate:(int)frameRate;

Expand Down
13 changes: 12 additions & 1 deletion src/platform/macos/av_video.m
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,24 @@ @implementation AVVideo
for (uint32_t i = 0; i < count; i++) {
[result addObject:@{
@"id": [NSNumber numberWithUnsignedInt:displays[i]],
@"name": [NSString stringWithFormat:@"%d", displays[i]]
@"name": [NSString stringWithFormat:@"%d", displays[i]],
@"displayName": [self getDisplayName:displays[i]],
}];
}

return [NSArray arrayWithArray:result];
}

+ (NSString *)getDisplayName:(CGDirectDisplayID)displayID {
NSScreen *screens = [NSScreen screens];
for (NSScreen *screen in screens) {
if (screen.deviceDescription[@"NSScreenNumber"] == [NSNumber numberWithUnsignedInt:displayID]) {
return screen.localizedName;
}
}
return nil;
}

- (id)initWithDisplay:(CGDirectDisplayID)displayID frameRate:(int)frameRate {
self = [super init];

Expand Down
22 changes: 13 additions & 9 deletions src/platform/macos/display.mm
Original file line number Diff line number Diff line change
Expand Up @@ -142,18 +142,22 @@

auto display = std::make_shared<av_display_t>();

// Default to main display
display->display_id = CGMainDisplayID();
if (!display_name.empty()) {
auto display_array = [AVVideo displayNames];

for (NSDictionary *item in display_array) {
NSString *name = item[@"name"];
if (name.UTF8String == display_name) {
NSNumber *display_id = item[@"id"];
display->display_id = [display_id unsignedIntValue];
}

// Print all displays available with it's name and id
auto display_array = [AVVideo displayNames];
BOOST_LOG(info) << "Detecting displays..."sv;
TimmyOVO marked this conversation as resolved.
Show resolved Hide resolved
for (NSDictionary *item in display_array) {
NSNumber *display_id = item[@"id"];
// We need show display's product name and corresponding display number given by user
NSString *name = item[@"displayName"];
BOOST_LOG(info) << "Detected display: "sv << name.UTF8String << " (id: "sv << [NSString stringWithFormat:@"%@", display_id].UTF8String << ")"sv;
TimmyOVO marked this conversation as resolved.
Show resolved Hide resolved
if (!display_name.empty() && std::atoi(display_name.c_str()) == [display_id unsignedIntValue]) {
display->display_id = [display_id unsignedIntValue];
}
}
BOOST_LOG(info) << "Selected display id: "sv << display->display_id;

display->av_capture = [[AVVideo alloc] initWithDisplay:display->display_id frameRate:config.framerate];

Expand Down
21 changes: 20 additions & 1 deletion src/platform/macos/input.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -509,9 +509,28 @@ const KeyCodeMap kKeyCodesMap[] = {

auto macos_input = (macos_input_t *) result.get();

// If we don't use the main display in the future, this has to be adapted
// Default to main display
macos_input->display = CGMainDisplayID();

auto output_name = config::video.output_name;
// If output_name is set, try to find the display with that display id
if (!output_name.empty()) {
uint32_t max_display = 32;
uint32_t display_count;
CGDirectDisplayID displays[max_display];
if (CGGetActiveDisplayList(max_display, displays, &display_count) != kCGErrorSuccess) {
BOOST_LOG(error) << "Unable to get active display list , error: "sv << std::endl;
}
else {
for (int i = 0; i < display_count; i++) {
CGDirectDisplayID display_id = displays[i];
if (display_id == std::atoi(output_name.c_str())) {
macos_input->display = display_id;
}
}
}
}

// Input coordinates are based on the virtual resolution not the physical, so we need the scaling factor
CGDisplayModeRef mode = CGDisplayCopyDisplayMode(macos_input->display);
macos_input->displayScaling = ((CGFloat) CGDisplayPixelsWide(macos_input->display)) / ((CGFloat) CGDisplayModeGetPixelWidth(mode));
Expand Down
8 changes: 8 additions & 0 deletions src_assets/common/assets/web/config.html
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,14 @@ <h2 class="accordion-header">
</pre>
</div>
</div>
<div class="mb-3" v-if="platform === 'macos'">
<label for="output_name" class="form-label">{{ $t('config.output_name_macos') }}</label>
<input type="text" class="form-control" id="output_name" placeholder="2"
v-model="config.output_name" />
<div class="form-text">
{{ $t('config.output_name_desc_macos') }}<br>
</div>
</div>

<!-- Display Modes -->
<div class="mb-3">
Expand Down
2 changes: 2 additions & 0 deletions src_assets/common/assets/web/public/assets/locale/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -245,8 +245,10 @@
"origin_web_ui_allowed_wan": "Anyone may access Web UI",
"output_name_desc_linux": "During Sunshine startup, you should see the list of detected monitors. You need to use the value before the colon in the output. e.g.:",
"output_name_desc_win": "Manually specify a display to use for capture. If unset, the primary display is captured. Note: If you specified a GPU above, this display must be connected to that GPU. The appropriate values can be found using the following command:",
"output_name_desc_macos": "Manually specify a display to use for capture. If unset, the primary display is captured. Note: The display must be active. You can find a list of display IDs in the startup logs.",
TimmyOVO marked this conversation as resolved.
Show resolved Hide resolved
"output_name_linux": "Monitor number",
"output_name_win": "Output Name",
"output_name_macos": "Display number",
"ping_timeout": "Ping Timeout",
"ping_timeout_desc": "How long to wait in milliseconds for data from moonlight before shutting down the stream",
"pkey": "Private Key",
Expand Down
2 changes: 2 additions & 0 deletions src_assets/common/assets/web/public/assets/locale/en_GB.json
TimmyOVO marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -236,8 +236,10 @@
"origin_web_ui_allowed_wan": "Anyone may access Web UI",
"output_name_desc_linux": "During Sunshine startup, you should see the list of detected monitors. You need to use the value before the colon in the output. e.g.:",
"output_name_desc_win": "Manually specify a display to use for capture. If unset, the primary display is captured. Note: If you specified a GPU above, this display must be connected to that GPU. The appropriate values can be found using the following command:",
"output_name_desc_macos": "Manually specify a display to use for capture. If unset, the primary display is captured. Note: The display must be active. You can find a list of display IDs in the startup logs.",
"output_name_linux": "Monitor number",
"output_name_win": "Output Name",
"output_name_macos": "Display number",
"ping_timeout": "Ping Timeout",
"ping_timeout_desc": "How long to wait in milliseconds for data from moonlight before shutting down the stream",
"pkey": "Private Key",
Expand Down
2 changes: 2 additions & 0 deletions src_assets/common/assets/web/public/assets/locale/en_US.json
Original file line number Diff line number Diff line change
Expand Up @@ -236,8 +236,10 @@
"origin_web_ui_allowed_wan": "Anyone may access Web UI",
"output_name_desc_linux": "During Sunshine startup, you should see the list of detected monitors. You need to use the value before the colon in the output. e.g.:",
"output_name_desc_win": "Manually specify a display to use for capture. If unset, the primary display is captured. Note: If you specified a GPU above, this display must be connected to that GPU. The appropriate values can be found using the following command:",
"output_name_desc_macos": "Manually specify a display to use for capture. If unset, the primary display is captured. Note: The display must be active. You can find a list of display IDs in the startup logs.",
"output_name_linux": "Monitor number",
"output_name_win": "Output Name",
"output_name_macos": "Display number",
"ping_timeout": "Ping Timeout",
"ping_timeout_desc": "How long to wait in milliseconds for data from moonlight before shutting down the stream",
"pkey": "Private Key",
Expand Down
2 changes: 2 additions & 0 deletions src_assets/common/assets/web/public/assets/locale/zh.json
Original file line number Diff line number Diff line change
Expand Up @@ -236,8 +236,10 @@
"origin_web_ui_allowed_wan": "任何人都可以访问 Web UI",
"output_name_desc_linux": "在阳光启动过程中,您应该看到检测到的显示器列表。您需要在输出中的冒号之前使用该值,例如:",
"output_name_desc_win": "手动指定用于捕捉的显示器。如果未设置,则捕捉主显示屏。注意:如果在上面指定了 GPU,则该显示器必须连接到该 GPU。可以使用以下命令找到相应的值:",
"output_name_desc_macos": "手动指定用于捕捉的显示器。如果未设置,则捕捉主显示屏。注意:该显示器必须处于激活状态。你可以在启动日志中找到显示器编号列表。",
"output_name_linux": "显示器编号",
"output_name_win": "输出名称",
"output_name_macos": "显示器编号",
"ping_timeout": "Ping 超时",
"ping_timeout_desc": "关闭串流前等待 Moonlight 数据的时间(以毫秒计)",
"pkey": "私钥",
Expand Down