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 all 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
5 changes: 3 additions & 2 deletions cmake/compile_definitions/macos.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@ link_directories(/opt/homebrew/lib)
ADD_DEFINITIONS(-DBOOST_LOG_DYN_LINK)

list(APPEND SUNSHINE_EXTERNAL_LIBRARIES
${APP_KIT_LIBRARY}
TimmyOVO marked this conversation as resolved.
Show resolved Hide resolved
${APP_SERVICES_LIBRARY}
${AV_FOUNDATION_LIBRARY}
${CORE_MEDIA_LIBRARY}
${CORE_VIDEO_LIBRARY}
${VIDEO_TOOLBOX_LIBRARY}
${FOUNDATION_LIBRARY})
${FOUNDATION_LIBRARY}
${VIDEO_TOOLBOX_LIBRARY})

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
@@ -1,5 +1,6 @@
# macos specific dependencies

FIND_LIBRARY(APP_KIT_LIBRARY AppKit)
FIND_LIBRARY(APP_SERVICES_LIBRARY ApplicationServices)
FIND_LIBRARY(AV_FOUNDATION_LIBRARY AVFoundation)
FIND_LIBRARY(CORE_MEDIA_LIBRARY CoreMedia)
Expand Down
32 changes: 22 additions & 10 deletions docs/source/about/advanced_usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -576,20 +576,29 @@ keybindings
.. tip:: To find the name of the appropriate values follow these instructions.

**Linux**
During Sunshine startup, you should see the list of detected monitors:
During Sunshine startup, you should see the list of detected displays:

.. code-block:: text

Info: Detecting connected monitors
Info: Detected monitor 0: DVI-D-0, connected: false
Info: Detected monitor 1: HDMI-0, connected: true
Info: Detected monitor 2: DP-0, connected: true
Info: Detected monitor 3: DP-1, connected: false
Info: Detected monitor 4: DVI-D-1, connected: false
Info: Detecting displays
Info: Detected display: DVI-D-0 (id: 0) connected: false
Info: Detected display: HDMI-0 (id: 1) connected: true
Info: Detected display: DP-0 (id: 2) connected: true
Info: Detected display: DP-1 (id: 3) connected: false
Info: Detected display: DVI-D-1 (id: 4) connected: false

You need to use the value before the colon in the output, e.g. ``1``.
You need to use the id value inside the parenthesis, 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: Monitor-0 (id: 3) connected: true
Info: Detected display: Monitor-1 (id: 2) connected: true

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

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

output_name = 0

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

output_name = 3

**Windows**
.. code-block:: text
Expand Down
6 changes: 3 additions & 3 deletions src/platform/linux/x11grab.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -421,7 +421,7 @@ namespace platf {
}

if (streamedMonitor != -1) {
BOOST_LOG(info) << "Configuring selected monitor ("sv << streamedMonitor << ") to stream"sv;
BOOST_LOG(info) << "Configuring selected display ("sv << streamedMonitor << ") to stream"sv;
screen_res_t screenr { x11::rr::GetScreenResources(xdisplay.get(), xwindow) };
int output = screenr->noutput;

Expand Down Expand Up @@ -806,7 +806,7 @@ namespace platf {
return {};
}

BOOST_LOG(info) << "Detecting monitors"sv;
BOOST_LOG(info) << "Detecting displays"sv;

x11::xdisplay_t xdisplay { x11::OpenDisplay(nullptr) };
if (!xdisplay) {
Expand All @@ -821,7 +821,7 @@ namespace platf {
for (int x = 0; x < output; ++x) {
output_info_t out_info { x11::rr::GetOutputInfo(xdisplay.get(), screenr.get(), screenr->outputs[x]) };
if (out_info) {
BOOST_LOG(info) << "Detected monitor "sv << monitor << ": "sv << out_info->name << ", connected: "sv << (out_info->connection == RR_Connected);
BOOST_LOG(info) << "Detected display: "sv << out_info->name << " (id: "sv << monitor << ")"sv << out_info->name << " connected: "sv << (out_info->connection == RR_Connected);
++monitor;
}
}
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 @@
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;

Check warning on line 41 in src/platform/macos/av_video.m

View check run for this annotation

Codecov / codecov/patch

src/platform/macos/av_video.m#L41

Added line #L41 was not covered by tests
}

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

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

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;
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"];
// We are using CGGetActiveDisplayList that only returns active displays so hardcoded connected value in log to true
BOOST_LOG(info) << "Detected display: "sv << name.UTF8String << " (id: "sv << [NSString stringWithFormat:@"%@", display_id].UTF8String << ") connected: true"sv;
if (!display_name.empty() && std::atoi(display_name.c_str()) == [display_id unsignedIntValue]) {
display->display_id = [display_id unsignedIntValue];
}
}
BOOST_LOG(info) << "Configuring selected display ("sv << display->display_id << ") to stream"sv;

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 @@

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];

Check warning on line 520 in src/platform/macos/input.cpp

View check run for this annotation

Codecov / codecov/patch

src/platform/macos/input.cpp#L518-L520

Added lines #L518 - L520 were not covered by tests
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];

Check warning on line 526 in src/platform/macos/input.cpp

View check run for this annotation

Codecov / codecov/patch

src/platform/macos/input.cpp#L526

Added line #L526 was not covered by tests
if (display_id == std::atoi(output_name.c_str())) {
macos_input->display = display_id;

Check warning on line 528 in src/platform/macos/input.cpp

View check run for this annotation

Codecov / codecov/patch

src/platform/macos/input.cpp#L528

Added line #L528 was not covered by tests
}
}
}
}

// 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
25 changes: 15 additions & 10 deletions src_assets/common/assets/web/config.html
Original file line number Diff line number Diff line change
Expand Up @@ -392,19 +392,24 @@ <h2 class="accordion-header">
<pre>tools\dxgi-info.exe</pre>
</div>
</div>
<div class="mb-3" v-if="platform === 'linux'">
<label for="output_name" class="form-label">{{ $t('config.output_name_linux') }}</label>
<div class="mb-3" v-if="platform === 'linux' || platform === 'macos'">
<label for="output_name" class="form-label">{{ $t('config.output_name_unix') }}</label>
<input type="text" class="form-control" id="output_name" placeholder="0" v-model="config.output_name" />
<div class="form-text">
{{ $t('config.output_name_desc_linux') }}<br>
{{ $t('config.output_name_desc_unix') }}<br>
<br>
<pre style="white-space: pre-line;">
Info: Detecting connected monitors
Info: Detected monitor 0: DVI-D-0, connected: false
Info: Detected monitor 1: HDMI-0, connected: true
Info: Detected monitor 2: DP-0, connected: true
Info: Detected monitor 3: DP-1, connected: false
Info: Detected monitor 4: DVI-D-1, connected: false
<pre style="white-space: pre-line;" v-if="platform === 'linux'">
Info: Detecting displays
Info: Detected display: DVI-D-0 (id: 0) connected: false
Info: Detected display: HDMI-0 (id: 1) connected: true
Info: Detected display: DP-0 (id: 2) connected: true
Info: Detected display: DP-1 (id: 3) connected: false
Info: Detected display: DVI-D-1 (id: 4) connected: false
TimmyOVO marked this conversation as resolved.
Show resolved Hide resolved
</pre>
<pre style="white-space: pre-line;" v-if="platform === 'macos'">
Info: Detecting displays
Info: Detected display: Monitor-0 (id: 3) connected: true
Info: Detected display: Monitor-1 (id: 2) connected: true
</pre>
</div>
</div>
Expand Down
4 changes: 2 additions & 2 deletions src_assets/common/assets/web/public/assets/locale/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -243,9 +243,9 @@
"origin_web_ui_allowed_lan": "Only those in LAN may access Web UI",
"origin_web_ui_allowed_pc": "Only localhost may access Web UI",
"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_unix": "During Sunshine startup, you should see the list of detected displays. Note: You need to use the id value inside the parenthesis.",
"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_linux": "Monitor number",
"output_name_unix": "Display number",
"output_name_win": "Output Name",
"ping_timeout": "Ping Timeout",
"ping_timeout_desc": "How long to wait in milliseconds for data from moonlight before shutting down the stream",
Expand Down
Loading