Merge remote-tracking branch 'origin/master'

This commit is contained in:
Yukino Song
2025-02-06 09:14:18 +08:00
195 changed files with 9602 additions and 13677 deletions

View File

@@ -4,8 +4,10 @@
*/
#pragma once
// platform includes
#import <AVFoundation/AVFoundation.h>
// lib includes
#include "third-party/TPCircularBuffer/TPCircularBuffer.h"
#define kBufferLength 4096

View File

@@ -2,12 +2,13 @@
* @file src/platform/macos/av_audio.m
* @brief Definitions for audio capture on macOS.
*/
// local includes
#import "av_audio.h"
@implementation AVAudio
+ (NSArray<AVCaptureDevice *> *)microphones {
if ([[NSProcessInfo processInfo] isOperatingSystemAtLeastVersion:((NSOperatingSystemVersion) { 10, 15, 0 })]) {
if ([[NSProcessInfo processInfo] isOperatingSystemAtLeastVersion:((NSOperatingSystemVersion) {10, 15, 0})]) {
// This will generate a warning about AVCaptureDeviceDiscoverySession being
// unavailable before macOS 10.15, but we have a guard to prevent it from
// being called on those earlier systems.
@@ -16,14 +17,12 @@
// a different method.
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunguarded-availability-new"
AVCaptureDeviceDiscoverySession *discoverySession = [AVCaptureDeviceDiscoverySession discoverySessionWithDeviceTypes:@[AVCaptureDeviceTypeBuiltInMicrophone,
AVCaptureDeviceTypeExternalUnknown]
AVCaptureDeviceDiscoverySession *discoverySession = [AVCaptureDeviceDiscoverySession discoverySessionWithDeviceTypes:@[AVCaptureDeviceTypeBuiltInMicrophone, AVCaptureDeviceTypeExternalUnknown]
mediaType:AVMediaTypeAudio
position:AVCaptureDevicePositionUnspecified];
return discoverySession.devices;
#pragma clang diagnostic pop
}
else {
} else {
// We're intentionally using a deprecated API here specifically for versions
// of macOS where it's not deprecated, so we can ignore any deprecation
// warnings:
@@ -75,8 +74,7 @@
if ([self.audioCaptureSession canAddInput:audioInput]) {
[self.audioCaptureSession addInput:audioInput];
}
else {
} else {
[audioInput dealloc];
return -1;
}
@@ -92,17 +90,14 @@
(NSString *) AVLinearPCMIsNonInterleaved: @NO
}];
dispatch_queue_attr_t qos = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_CONCURRENT,
QOS_CLASS_USER_INITIATED,
DISPATCH_QUEUE_PRIORITY_HIGH);
dispatch_queue_attr_t qos = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_CONCURRENT, QOS_CLASS_USER_INITIATED, DISPATCH_QUEUE_PRIORITY_HIGH);
dispatch_queue_t recordingQueue = dispatch_queue_create("audioSamplingQueue", qos);
[audioOutput setSampleBufferDelegate:self queue:recordingQueue];
if ([self.audioCaptureSession canAddOutput:audioOutput]) {
[self.audioCaptureSession addOutput:audioOutput];
}
else {
} else {
[audioInput release];
[audioOutput release];
return -1;

View File

@@ -4,17 +4,20 @@
*/
#pragma once
#include "src/platform/common.h"
// platform includes
#include <CoreMedia/CoreMedia.h>
#include <CoreVideo/CoreVideo.h>
// local includes
#include "src/platform/common.h"
namespace platf {
struct av_sample_buf_t {
CMSampleBufferRef buf;
explicit av_sample_buf_t(CMSampleBufferRef buf):
buf((CMSampleBufferRef) CFRetain(buf)) {}
buf((CMSampleBufferRef) CFRetain(buf)) {
}
~av_sample_buf_t() {
if (buf != nullptr) {
@@ -29,12 +32,12 @@ namespace platf {
// Constructor
explicit av_pixel_buf_t(CMSampleBufferRef sb):
buf(
CMSampleBufferGetImageBuffer(sb)) {
CMSampleBufferGetImageBuffer(sb)
) {
CVPixelBufferLockBaseAddress(buf, kCVPixelBufferLock_ReadOnly);
}
[[nodiscard]] uint8_t *
data() const {
[[nodiscard]] uint8_t *data() const {
return static_cast<uint8_t *>(CVPixelBufferGetBaseAddress(buf));
}
@@ -59,8 +62,11 @@ namespace platf {
temp_retain_av_img_t(
std::shared_ptr<av_sample_buf_t> sb,
std::shared_ptr<av_pixel_buf_t> pb,
uint8_t *dt):
uint8_t *dt
):
sample_buffer(std::move(sb)),
pixel_buffer(std::move(pb)), data(dt) {}
pixel_buffer(std::move(pb)),
data(dt) {
}
};
} // namespace platf

View File

@@ -4,8 +4,9 @@
*/
#pragma once
#import <AVFoundation/AVFoundation.h>
// platform includes
#import <AppKit/AppKit.h>
#import <AVFoundation/AVFoundation.h>
struct CaptureSession {
AVCaptureVideoDataOutput *output;

View File

@@ -2,6 +2,7 @@
* @file src/platform/macos/av_video.m
* @brief Definitions for video capture on macOS.
*/
// local includes
#import "av_video.h"
@implementation AVVideo
@@ -62,8 +63,7 @@
if ([self.session canAddInput:screenInput]) {
[self.session addInput:screenInput];
}
else {
} else {
[screenInput release];
return nil;
}
@@ -97,9 +97,7 @@
(NSString *) AVVideoScalingModeKey: AVVideoScalingModeResizeAspect,
}];
dispatch_queue_attr_t qos = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL,
QOS_CLASS_USER_INITIATED,
DISPATCH_QUEUE_PRIORITY_HIGH);
dispatch_queue_attr_t qos = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_USER_INITIATED, DISPATCH_QUEUE_PRIORITY_HIGH);
dispatch_queue_t recordingQueue = dispatch_queue_create("videoCaptureQueue", qos);
[videoOutput setSampleBufferDelegate:self queue:recordingQueue];
@@ -107,8 +105,7 @@
if ([self.session canAddOutput:videoOutput]) {
[self.session addOutput:videoOutput];
}
else {
} else {
[videoOutput release];
return nil;
}

View File

@@ -2,15 +2,15 @@
* @file src/platform/macos/display.mm
* @brief Definitions for display capture on macOS.
*/
// local includes
#include "src/config.h"
#include "src/logging.h"
#include "src/platform/common.h"
#include "src/platform/macos/av_img_t.h"
#include "src/platform/macos/av_video.h"
#include "src/platform/macos/misc.h"
#include "src/platform/macos/nv12_zero_device.h"
#include "src/config.h"
#include "src/logging.h"
// Avoid conflict between AVFoundation and libavutil both defining AVMediaType
#define AVMediaType AVMediaType_FFmpeg
#include "src/video.h"
@@ -29,8 +29,7 @@ namespace platf {
[av_capture release];
}
capture_e
capture(const push_captured_image_cb_t &push_captured_image_cb, const pull_free_image_cb_t &pull_free_image_cb, bool *cursor) override {
capture_e capture(const push_captured_image_cb_t &push_captured_image_cb, const pull_free_image_cb_t &pull_free_image_cb, bool *cursor) override {
auto signal = [av_capture capture:^(CMSampleBufferRef sampleBuffer) {
auto new_sample_buffer = std::make_shared<av_sample_buf_t>(sampleBuffer);
auto new_pixel_buffer = std::make_shared<av_pixel_buf_t>(new_sample_buffer->buf);
@@ -46,7 +45,8 @@ namespace platf {
auto old_data_retainer = std::make_shared<temp_retain_av_img_t>(
av_img->sample_buffer,
av_img->pixel_buffer,
img_out->data);
img_out->data
);
av_img->sample_buffer = new_sample_buffer;
av_img->pixel_buffer = new_pixel_buffer;
@@ -74,33 +74,28 @@ namespace platf {
return capture_e::ok;
}
std::shared_ptr<img_t>
alloc_img() override {
std::shared_ptr<img_t> alloc_img() override {
return std::make_shared<av_img_t>();
}
std::unique_ptr<avcodec_encode_device_t>
make_avcodec_encode_device(pix_fmt_e pix_fmt) override {
std::unique_ptr<avcodec_encode_device_t> make_avcodec_encode_device(pix_fmt_e pix_fmt) override {
if (pix_fmt == pix_fmt_e::yuv420p) {
av_capture.pixelFormat = kCVPixelFormatType_32BGRA;
return std::make_unique<avcodec_encode_device_t>();
}
else if (pix_fmt == pix_fmt_e::nv12 || pix_fmt == pix_fmt_e::p010) {
} else if (pix_fmt == pix_fmt_e::nv12 || pix_fmt == pix_fmt_e::p010) {
auto device = std::make_unique<nv12_zero_device>();
device->init(static_cast<void *>(av_capture), pix_fmt, setResolution, setPixelFormat);
return device;
}
else {
} else {
BOOST_LOG(error) << "Unsupported Pixel Format."sv;
return nullptr;
}
}
int
dummy_img(img_t *img) override {
int dummy_img(img_t *img) override {
if (!platf::is_screen_capture_allowed()) {
// If we don't have the screen capture permission, this function will hang
// indefinitely without doing anything useful. Exit instead to avoid this.
@@ -117,7 +112,8 @@ namespace platf {
auto old_data_retainer = std::make_shared<temp_retain_av_img_t>(
av_img->sample_buffer,
av_img->pixel_buffer,
img->data);
img->data
);
av_img->sample_buffer = new_sample_buffer;
av_img->pixel_buffer = new_pixel_buffer;
@@ -146,19 +142,16 @@ namespace platf {
* width --> the intended capture width
* height --> the intended capture height
*/
static void
setResolution(void *display, int width, int height) {
static void setResolution(void *display, int width, int height) {
[static_cast<AVVideo *>(display) setFrameWidth:width frameHeight:height];
}
static void
setPixelFormat(void *display, OSType pixelFormat) {
static void setPixelFormat(void *display, OSType pixelFormat) {
static_cast<AVVideo *>(display).pixelFormat = pixelFormat;
}
};
std::shared_ptr<display_t>
display(platf::mem_type_e hwdevice_type, const std::string &display_name, const video::config_t &config) {
std::shared_ptr<display_t> display(platf::mem_type_e hwdevice_type, const std::string &display_name, const video::config_t &config) {
if (hwdevice_type != platf::mem_type_e::system && hwdevice_type != platf::mem_type_e::videotoolbox) {
BOOST_LOG(error) << "Could not initialize display with the given hw device type."sv;
return nullptr;
@@ -200,8 +193,7 @@ namespace platf {
return display;
}
std::vector<std::string>
display_names(mem_type_e hwdevice_type) {
std::vector<std::string> display_names(mem_type_e hwdevice_type) {
__block std::vector<std::string> display_names;
auto display_array = [AVVideo displayNames];
@@ -219,8 +211,7 @@ namespace platf {
* @brief Returns if GPUs/drivers have changed since the last call to this function.
* @return `true` if a change has occurred or if it is unknown whether a change occurred.
*/
bool
needs_encoder_reenumeration() {
bool needs_encoder_reenumeration() {
// We don't track GPU state, so we will always reenumerate. Fortunately, it is fast on macOS.
return true;
}

View File

@@ -2,22 +2,24 @@
* @file src/platform/macos/input.cpp
* @brief Definitions for macOS input handling.
*/
#include "src/input.h"
#import <Carbon/Carbon.h>
// standard includes
#include <chrono>
#include <iostream>
#include <thread>
// platform includes
#include <ApplicationServices/ApplicationServices.h>
#import <Carbon/Carbon.h>
#include <CoreFoundation/CoreFoundation.h>
#include <mach/mach.h>
// local includes
#include "src/display_device.h"
#include "src/input.h"
#include "src/logging.h"
#include "src/platform/common.h"
#include "src/utility.h"
#include <ApplicationServices/ApplicationServices.h>
#include <CoreFoundation/CoreFoundation.h>
#include <iostream>
#include <thread>
/**
* @brief Delay for a double click, in milliseconds.
* @todo Make this configurable.
@@ -50,8 +52,7 @@ namespace platf {
};
// Customized less operator for using std::lower_bound() on a KeyCodeMap array.
bool
operator<(const KeyCodeMap &a, const KeyCodeMap &b) {
bool operator<(const KeyCodeMap &a, const KeyCodeMap &b) {
return a.win_keycode < b.win_keycode;
}
@@ -227,13 +228,15 @@ const KeyCodeMap kKeyCodesMap[] = {
};
// clang-format on
int
keysym(int keycode) {
int keysym(int keycode) {
KeyCodeMap key_map {};
key_map.win_keycode = keycode;
const KeyCodeMap *temp_map = std::lower_bound(
kKeyCodesMap, kKeyCodesMap + sizeof(kKeyCodesMap) / sizeof(kKeyCodesMap[0]), key_map);
kKeyCodesMap,
kKeyCodesMap + sizeof(kKeyCodesMap) / sizeof(kKeyCodesMap[0]),
key_map
);
if (temp_map >= kKeyCodesMap + sizeof(kKeyCodesMap) / sizeof(kKeyCodesMap[0]) ||
temp_map->win_keycode != keycode || temp_map->mac_keycode == -1) {
@@ -243,8 +246,7 @@ const KeyCodeMap kKeyCodesMap[] = {
return temp_map->mac_keycode;
}
void
keyboard_update(input_t &input, uint16_t modcode, bool release, uint8_t flags) {
void keyboard_update(input_t &input, uint16_t modcode, bool release, uint8_t flags) {
auto key = keysym(modcode);
BOOST_LOG(debug) << "got keycode: 0x"sv << std::hex << modcode << ", translated to: 0x" << std::hex << key << ", release:" << release;
@@ -284,8 +286,7 @@ const KeyCodeMap kKeyCodesMap[] = {
macos_input->kb_flags = release ? macos_input->kb_flags & ~mask : macos_input->kb_flags | mask;
CGEventSetType(event, kCGEventFlagsChanged);
CGEventSetFlags(event, macos_input->kb_flags);
}
else {
} else {
CGEventSetIntegerValueField(event, kCGKeyboardEventKeycode, key);
CGEventSetType(event, release ? kCGEventKeyUp : kCGEventKeyDown);
}
@@ -293,30 +294,25 @@ const KeyCodeMap kKeyCodesMap[] = {
CGEventPost(kCGHIDEventTap, event);
}
void
unicode(input_t &input, char *utf8, int size) {
void unicode(input_t &input, char *utf8, int size) {
BOOST_LOG(info) << "unicode: Unicode input not yet implemented for MacOS."sv;
}
int
alloc_gamepad(input_t &input, const gamepad_id_t &id, const gamepad_arrival_t &metadata, feedback_queue_t feedback_queue) {
int alloc_gamepad(input_t &input, const gamepad_id_t &id, const gamepad_arrival_t &metadata, feedback_queue_t feedback_queue) {
BOOST_LOG(info) << "alloc_gamepad: Gamepad not yet implemented for MacOS."sv;
return -1;
}
void
free_gamepad(input_t &input, int nr) {
void free_gamepad(input_t &input, int nr) {
BOOST_LOG(info) << "free_gamepad: Gamepad not yet implemented for MacOS."sv;
}
void
gamepad_update(input_t &input, int nr, const gamepad_state_t &gamepad_state) {
void gamepad_update(input_t &input, int nr, const gamepad_state_t &gamepad_state) {
BOOST_LOG(info) << "gamepad: Gamepad not yet implemented for MacOS."sv;
}
// returns current mouse location:
util::point_t
get_mouse_loc(input_t &input) {
util::point_t get_mouse_loc(input_t &input) {
// Creating a new event every time to avoid any reuse risk
const auto macos_input = static_cast<macos_input_t *>(input.get());
const auto snapshot_event = CGEventCreate(macos_input->source);
@@ -328,14 +324,14 @@ const KeyCodeMap kKeyCodesMap[] = {
};
}
void
post_mouse(
void post_mouse(
input_t &input,
const CGMouseButton button,
const CGEventType type,
const util::point_t raw_location,
const util::point_t previous_location,
const int click_count) {
const int click_count
) {
BOOST_LOG(debug) << "mouse_event: "sv << button << ", type: "sv << type << ", location:"sv << raw_location.x << ":"sv << raw_location.y << " click_count: "sv << click_count;
const auto macos_input = static_cast<macos_input_t *>(input.get());
@@ -368,8 +364,7 @@ const KeyCodeMap kKeyCodesMap[] = {
CGWarpMouseCursorPosition(location);
}
inline CGEventType
event_type_mouse(input_t &input) {
inline CGEventType event_type_mouse(input_t &input) {
const auto macos_input = static_cast<macos_input_t *>(input.get());
if (macos_input->mouse_down[0]) {
@@ -384,28 +379,28 @@ const KeyCodeMap kKeyCodesMap[] = {
return kCGEventMouseMoved;
}
void
move_mouse(
void move_mouse(
input_t &input,
const int deltaX,
const int deltaY) {
const int deltaY
) {
const auto current = get_mouse_loc(input);
const auto location = util::point_t { current.x + deltaX, current.y + deltaY };
const auto location = util::point_t {current.x + deltaX, current.y + deltaY};
post_mouse(input, kCGMouseButtonLeft, event_type_mouse(input), location, current, 0);
}
void
abs_mouse(
void abs_mouse(
input_t &input,
const touch_port_t &touch_port,
const float x,
const float y) {
const float y
) {
const auto macos_input = static_cast<macos_input_t *>(input.get());
const auto scaling = macos_input->displayScaling;
const auto display = macos_input->display;
auto location = util::point_t { x * scaling, y * scaling };
auto location = util::point_t {x * scaling, y * scaling};
CGRect display_bounds = CGDisplayBounds(display);
// in order to get the correct mouse location for capturing display , we need to add the display bounds to the location
location.x += display_bounds.origin.x;
@@ -414,8 +409,7 @@ const KeyCodeMap kKeyCodesMap[] = {
post_mouse(input, kCGMouseButtonLeft, event_type_mouse(input), location, get_mouse_loc(input), 0);
}
void
button_mouse(input_t &input, const int button, const bool release) {
void button_mouse(input_t &input, const int button, const bool release) {
CGMouseButton mac_button;
CGEventType event;
@@ -447,26 +441,26 @@ const KeyCodeMap kKeyCodesMap[] = {
if (now < macos_input->last_mouse_event[mac_button][release] + MULTICLICK_DELAY_MS) {
post_mouse(input, mac_button, event, mouse_position, mouse_position, 2);
}
else {
} else {
post_mouse(input, mac_button, event, mouse_position, mouse_position, 1);
}
macos_input->last_mouse_event[mac_button][release] = now;
}
void
scroll(input_t &input, const int high_res_distance) {
void scroll(input_t &input, const int high_res_distance) {
CGEventRef upEvent = CGEventCreateScrollWheelEvent(
nullptr,
kCGScrollEventUnitLine,
2, high_res_distance > 0 ? 1 : -1, high_res_distance);
2,
high_res_distance > 0 ? 1 : -1,
high_res_distance
);
CGEventPost(kCGHIDEventTap, upEvent);
CFRelease(upEvent);
}
void
hscroll(input_t &input, int high_res_distance) {
void hscroll(input_t &input, int high_res_distance) {
// Unimplemented
}
@@ -475,8 +469,7 @@ const KeyCodeMap kKeyCodesMap[] = {
* @param input The global input context.
* @return A unique pointer to a per-client input data context.
*/
std::unique_ptr<client_input_t>
allocate_client_input_context(input_t &input) {
std::unique_ptr<client_input_t> allocate_client_input_context(input_t &input) {
// Unused
return nullptr;
}
@@ -487,8 +480,7 @@ const KeyCodeMap kKeyCodesMap[] = {
* @param touch_port The current viewport for translating to screen coordinates.
* @param touch The touch event.
*/
void
touch_update(client_input_t *input, const touch_port_t &touch_port, const touch_input_t &touch) {
void touch_update(client_input_t *input, const touch_port_t &touch_port, const touch_input_t &touch) {
// Unimplemented feature - platform_caps::pen_touch
}
@@ -498,8 +490,7 @@ const KeyCodeMap kKeyCodesMap[] = {
* @param touch_port The current viewport for translating to screen coordinates.
* @param pen The pen event.
*/
void
pen_update(client_input_t *input, const touch_port_t &touch_port, const pen_input_t &pen) {
void pen_update(client_input_t *input, const touch_port_t &touch_port, const pen_input_t &pen) {
// Unimplemented feature - platform_caps::pen_touch
}
@@ -508,8 +499,7 @@ const KeyCodeMap kKeyCodesMap[] = {
* @param input The global input context.
* @param touch The touch event.
*/
void
gamepad_touch(input_t &input, const gamepad_touch_t &touch) {
void gamepad_touch(input_t &input, const gamepad_touch_t &touch) {
// Unimplemented feature - platform_caps::controller_touch
}
@@ -518,8 +508,7 @@ const KeyCodeMap kKeyCodesMap[] = {
* @param input The global input context.
* @param motion The motion event.
*/
void
gamepad_motion(input_t &input, const gamepad_motion_t &motion) {
void gamepad_motion(input_t &input, const gamepad_motion_t &motion) {
// Unimplemented
}
@@ -528,14 +517,12 @@ const KeyCodeMap kKeyCodesMap[] = {
* @param input The global input context.
* @param battery The battery event.
*/
void
gamepad_battery(input_t &input, const gamepad_battery_t &battery) {
void gamepad_battery(input_t &input, const gamepad_battery_t &battery) {
// Unimplemented
}
input_t
input() {
input_t result { new macos_input_t() };
input_t input() {
input_t result {new macos_input_t()};
const auto macos_input = static_cast<macos_input_t *>(result.get());
@@ -550,8 +537,7 @@ const KeyCodeMap kKeyCodesMap[] = {
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 {
} else {
for (int i = 0; i < display_count; i++) {
CGDirectDisplayID display_id = displays[i];
if (display_id == std::atoi(output_name.c_str())) {
@@ -581,8 +567,7 @@ const KeyCodeMap kKeyCodesMap[] = {
return result;
}
void
freeInput(void *p) {
void freeInput(void *p) {
const auto *input = static_cast<macos_input_t *>(p);
CFRelease(input->source);
@@ -592,10 +577,9 @@ const KeyCodeMap kKeyCodesMap[] = {
delete input;
}
std::vector<supported_gamepad_t> &
supported_gamepads(input_t *input) {
std::vector<supported_gamepad_t> &supported_gamepads(input_t *input) {
static std::vector gamepads {
supported_gamepad_t { "", false, "gamepads.macos_not_implemented" }
supported_gamepad_t {"", false, "gamepads.macos_not_implemented"}
};
return gamepads;
@@ -605,8 +589,7 @@ const KeyCodeMap kKeyCodesMap[] = {
* @brief Returns the supported platform capabilities to advertise to the client.
* @return Capability flags.
*/
platform_caps::caps_t
get_capabilities() {
platform_caps::caps_t get_capabilities() {
return 0;
}
} // namespace platf

View File

@@ -2,11 +2,11 @@
* @file src/platform/macos/microphone.mm
* @brief Definitions for microphone capture on macOS.
*/
#include "src/platform/common.h"
#include "src/platform/macos/av_audio.h"
// local includes
#include "src/config.h"
#include "src/logging.h"
#include "src/platform/common.h"
#include "src/platform/macos/av_audio.h"
namespace platf {
using namespace std::literals;
@@ -18,8 +18,7 @@ namespace platf {
[av_audio_capture release];
}
capture_e
sample(std::vector<float> &sample_in) override {
capture_e sample(std::vector<float> &sample_in) override {
auto sample_size = sample_in.size();
uint32_t length = 0;
@@ -45,14 +44,12 @@ namespace platf {
AVCaptureDevice *audio_capture_device {};
public:
int
set_sink(const std::string &sink) override {
int set_sink(const std::string &sink) override {
BOOST_LOG(warning) << "audio_control_t::set_sink() unimplemented: "sv << sink;
return 0;
}
std::unique_ptr<mic_t>
microphone(const std::uint8_t *mapping, int channels, std::uint32_t sample_rate, std::uint32_t frame_size) override {
std::unique_ptr<mic_t> microphone(const std::uint8_t *mapping, int channels, std::uint32_t sample_rate, std::uint32_t frame_size) override {
auto mic = std::make_unique<av_mic_t>();
const char *audio_sink = "";
@@ -81,22 +78,19 @@ namespace platf {
return mic;
}
bool
is_sink_available(const std::string &sink) override {
bool is_sink_available(const std::string &sink) override {
BOOST_LOG(warning) << "audio_control_t::is_sink_available() unimplemented: "sv << sink;
return true;
}
std::optional<sink_t>
sink_info() override {
std::optional<sink_t> sink_info() override {
sink_t sink;
return sink;
}
};
std::unique_ptr<audio_control_t>
audio_control() {
std::unique_ptr<audio_control_t> audio_control() {
return std::make_unique<macos_audio_control_t>();
}
} // namespace platf

View File

@@ -4,21 +4,20 @@
*/
#pragma once
// standard includes
#include <vector>
// platform includes
#include <CoreGraphics/CoreGraphics.h>
namespace platf {
bool
is_screen_capture_allowed();
bool is_screen_capture_allowed();
}
namespace dyn {
typedef void (*apiproc)();
int
load(void *handle, const std::vector<std::tuple<apiproc *, const char *>> &funcs, bool strict = true);
void *
handle(const std::vector<const char *> &libs);
int load(void *handle, const std::vector<std::tuple<apiproc *, const char *>> &funcs, bool strict = true);
void *handle(const std::vector<const char *> &libs);
} // namespace dyn

View File

@@ -8,24 +8,29 @@
#define __APPLE_USE_RFC_3542 1
#endif
#include <Foundation/Foundation.h>
#include <arpa/inet.h>
#include <dlfcn.h>
// standard includes
#include <fcntl.h>
#include <ifaddrs.h>
// platform includes
#include <arpa/inet.h>
#include <dlfcn.h>
#include <Foundation/Foundation.h>
#include <mach-o/dyld.h>
#include <net/if_dl.h>
#include <pwd.h>
// lib includes
#include <boost/asio/ip/address.hpp>
#include <boost/asio/ip/host_name.hpp>
#include <boost/process/v1.hpp>
// local includes
#include "misc.h"
#include "src/entry_handler.h"
#include "src/logging.h"
#include "src/platform/common.h"
#include <boost/asio/ip/address.hpp>
#include <boost/asio/ip/host_name.hpp>
#include <boost/process/v1.hpp>
using namespace std::literals;
namespace fs = std::filesystem;
namespace bp = boost::process;
@@ -37,24 +42,20 @@ namespace platf {
#if __MAC_OS_X_VERSION_MAX_ALLOWED < 110000 // __MAC_11_0
// If they're not in the SDK then we can use our own function definitions.
// Need to use weak import so that this will link in macOS 10.14 and earlier
extern "C" bool
CGPreflightScreenCaptureAccess(void) __attribute__((weak_import));
extern "C" bool
CGRequestScreenCaptureAccess(void) __attribute__((weak_import));
extern "C" bool CGPreflightScreenCaptureAccess(void) __attribute__((weak_import));
extern "C" bool CGRequestScreenCaptureAccess(void) __attribute__((weak_import));
#endif
namespace {
auto screen_capture_allowed = std::atomic<bool> { false };
auto screen_capture_allowed = std::atomic<bool> {false};
} // namespace
// Return whether screen capture is allowed for this process.
bool
is_screen_capture_allowed() {
bool is_screen_capture_allowed() {
return screen_capture_allowed;
}
std::unique_ptr<deinit_t>
init() {
std::unique_ptr<deinit_t> init() {
// This will generate a warning about CGPreflightScreenCaptureAccess and
// CGRequestScreenCaptureAccess being unavailable before macOS 10.15, but
// we have a guard to prevent it from being called on those earlier systems.
@@ -69,7 +70,7 @@ namespace platf {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunguarded-availability-new"
#pragma clang diagnostic ignored "-Wtautological-pointer-compare"
if ([[NSProcessInfo processInfo] isOperatingSystemAtLeastVersion:((NSOperatingSystemVersion) { 10, 15, 0 })] &&
if ([[NSProcessInfo processInfo] isOperatingSystemAtLeastVersion:((NSOperatingSystemVersion) {10, 15, 0})] &&
// Double check that these weakly-linked symbols have been loaded:
CGPreflightScreenCaptureAccess != nullptr && CGRequestScreenCaptureAccess != nullptr &&
!CGPreflightScreenCaptureAccess()) {
@@ -84,66 +85,55 @@ namespace platf {
return std::make_unique<deinit_t>();
}
fs::path
appdata() {
fs::path appdata() {
const char *homedir;
if ((homedir = getenv("HOME")) == nullptr) {
homedir = getpwuid(geteuid())->pw_dir;
}
return fs::path { homedir } / ".config/sunshine"sv;
return fs::path {homedir} / ".config/sunshine"sv;
}
using ifaddr_t = util::safe_ptr<ifaddrs, freeifaddrs>;
ifaddr_t
get_ifaddrs() {
ifaddrs *p { nullptr };
ifaddr_t get_ifaddrs() {
ifaddrs *p {nullptr};
getifaddrs(&p);
return ifaddr_t { p };
return ifaddr_t {p};
}
std::string
from_sockaddr(const sockaddr *const ip_addr) {
std::string from_sockaddr(const sockaddr *const ip_addr) {
char data[INET6_ADDRSTRLEN] = {};
auto family = ip_addr->sa_family;
if (family == AF_INET6) {
inet_ntop(AF_INET6, &((sockaddr_in6 *) ip_addr)->sin6_addr, data,
INET6_ADDRSTRLEN);
}
else if (family == AF_INET) {
inet_ntop(AF_INET, &((sockaddr_in *) ip_addr)->sin_addr, data,
INET_ADDRSTRLEN);
inet_ntop(AF_INET6, &((sockaddr_in6 *) ip_addr)->sin6_addr, data, INET6_ADDRSTRLEN);
} else if (family == AF_INET) {
inet_ntop(AF_INET, &((sockaddr_in *) ip_addr)->sin_addr, data, INET_ADDRSTRLEN);
}
return std::string { data };
return std::string {data};
}
std::pair<std::uint16_t, std::string>
from_sockaddr_ex(const sockaddr *const ip_addr) {
std::pair<std::uint16_t, std::string> from_sockaddr_ex(const sockaddr *const ip_addr) {
char data[INET6_ADDRSTRLEN] = {};
auto family = ip_addr->sa_family;
std::uint16_t port = 0;
if (family == AF_INET6) {
inet_ntop(AF_INET6, &((sockaddr_in6 *) ip_addr)->sin6_addr, data,
INET6_ADDRSTRLEN);
inet_ntop(AF_INET6, &((sockaddr_in6 *) ip_addr)->sin6_addr, data, INET6_ADDRSTRLEN);
port = ((sockaddr_in6 *) ip_addr)->sin6_port;
}
else if (family == AF_INET) {
inet_ntop(AF_INET, &((sockaddr_in *) ip_addr)->sin_addr, data,
INET_ADDRSTRLEN);
} else if (family == AF_INET) {
inet_ntop(AF_INET, &((sockaddr_in *) ip_addr)->sin_addr, data, INET_ADDRSTRLEN);
port = ((sockaddr_in *) ip_addr)->sin_port;
}
return { port, std::string { data } };
return {port, std::string {data}};
}
std::string
get_mac_address(const std::string_view &address) {
std::string get_mac_address(const std::string_view &address) {
auto ifaddrs = get_ifaddrs();
for (auto pos = ifaddrs.get(); pos != nullptr; pos = pos->ifa_next) {
@@ -160,8 +150,7 @@ namespace platf {
ptr = (unsigned char *) LLADDR((struct sockaddr_dl *) (ifaptr)->ifa_addr);
char buff[100];
snprintf(buff, sizeof(buff), "%02x:%02x:%02x:%02x:%02x:%02x",
*ptr, *(ptr + 1), *(ptr + 2), *(ptr + 3), *(ptr + 4), *(ptr + 5));
snprintf(buff, sizeof(buff), "%02x:%02x:%02x:%02x:%02x:%02x", *ptr, *(ptr + 1), *(ptr + 2), *(ptr + 3), *(ptr + 4), *(ptr + 5));
mac_address = buff;
break;
}
@@ -182,13 +171,11 @@ namespace platf {
}
// TODO: return actual IP
std::string
get_local_ip_for_gateway() {
std::string get_local_ip_for_gateway() {
return "";
}
bp::child
run_command(bool elevated, bool interactive, const std::string &cmd, boost::filesystem::path &working_dir, const bp::environment &env, FILE *file, std::error_code &ec, bp::group *group) {
bp::child run_command(bool elevated, bool interactive, const std::string &cmd, boost::filesystem::path &working_dir, const bp::environment &env, FILE *file, std::error_code &ec, bp::group *group) {
// clang-format off
if (!group) {
if (!file) {
@@ -213,8 +200,7 @@ namespace platf {
* @brief Open a url in the default web browser.
* @param url The url to open.
*/
void
open_url(const std::string &url) {
void open_url(const std::string &url) {
boost::filesystem::path working_dir;
std::string cmd = R"(open ")" + url + R"(")";
@@ -223,30 +209,25 @@ namespace platf {
auto child = run_command(false, false, cmd, working_dir, _env, nullptr, ec, nullptr);
if (ec) {
BOOST_LOG(warning) << "Couldn't open url ["sv << url << "]: System: "sv << ec.message();
}
else {
} else {
BOOST_LOG(info) << "Opened url ["sv << url << "]"sv;
child.detach();
}
}
void
adjust_thread_priority(thread_priority_e priority) {
void adjust_thread_priority(thread_priority_e priority) {
// Unimplemented
}
void
streaming_will_start() {
void streaming_will_start() {
// Nothing to do
}
void
streaming_will_stop() {
void streaming_will_stop() {
// Nothing to do
}
void
restart_on_exit() {
void restart_on_exit() {
char executable[2048];
uint32_t size = sizeof(executable);
if (_NSGetExecutablePath(executable, &size) < 0) {
@@ -267,42 +248,35 @@ namespace platf {
}
}
void
restart() {
void restart() {
// Gracefully clean up and restart ourselves instead of exiting
atexit(restart_on_exit);
lifetime::exit_sunshine(0, true);
}
int
set_env(const std::string &name, const std::string &value) {
int set_env(const std::string &name, const std::string &value) {
return setenv(name.c_str(), value.c_str(), 1);
}
int
unset_env(const std::string &name) {
int unset_env(const std::string &name) {
return unsetenv(name.c_str());
}
bool
request_process_group_exit(std::uintptr_t native_handle) {
bool request_process_group_exit(std::uintptr_t native_handle) {
if (killpg((pid_t) native_handle, SIGTERM) == 0 || errno == ESRCH) {
BOOST_LOG(debug) << "Successfully sent SIGTERM to process group: "sv << native_handle;
return true;
}
else {
} else {
BOOST_LOG(warning) << "Unable to send SIGTERM to process group ["sv << native_handle << "]: "sv << errno;
return false;
}
}
bool
process_group_running(std::uintptr_t native_handle) {
bool process_group_running(std::uintptr_t native_handle) {
return waitpid(-((pid_t) native_handle), nullptr, WNOHANG) >= 0;
}
struct sockaddr_in
to_sockaddr(boost::asio::ip::address_v4 address, uint16_t port) {
struct sockaddr_in to_sockaddr(boost::asio::ip::address_v4 address, uint16_t port) {
struct sockaddr_in saddr_v4 = {};
saddr_v4.sin_family = AF_INET;
@@ -314,8 +288,7 @@ namespace platf {
return saddr_v4;
}
struct sockaddr_in6
to_sockaddr(boost::asio::ip::address_v6 address, uint16_t port) {
struct sockaddr_in6 to_sockaddr(boost::asio::ip::address_v6 address, uint16_t port) {
struct sockaddr_in6 saddr_v6 = {};
saddr_v6.sin6_family = AF_INET6;
@@ -328,14 +301,12 @@ namespace platf {
return saddr_v6;
}
bool
send_batch(batched_send_info_t &send_info) {
bool send_batch(batched_send_info_t &send_info) {
// Fall back to unbatched send calls
return false;
}
bool
send(send_info_t &send_info) {
bool send(send_info_t &send_info) {
auto sockfd = (int) send_info.native_socket;
struct msghdr msg = {};
@@ -347,8 +318,7 @@ namespace platf {
msg.msg_name = (struct sockaddr *) &taddr_v6;
msg.msg_namelen = sizeof(taddr_v6);
}
else {
} else {
taddr_v4 = to_sockaddr(send_info.target_address.to_v4(), send_info.target_port);
msg.msg_name = (struct sockaddr *) &taddr_v4;
@@ -359,6 +329,7 @@ namespace platf {
char buf[std::max(CMSG_SPACE(sizeof(struct in_pktinfo)), CMSG_SPACE(sizeof(struct in6_pktinfo)))];
struct cmsghdr alignment;
} cmbuf {};
socklen_t cmbuflen = 0;
msg.msg_control = cmbuf.buf;
@@ -378,8 +349,7 @@ namespace platf {
pktinfo_cm->cmsg_type = IPV6_PKTINFO;
pktinfo_cm->cmsg_len = CMSG_LEN(sizeof(pktInfo));
memcpy(CMSG_DATA(pktinfo_cm), &pktInfo, sizeof(pktInfo));
}
else {
} else {
struct in_pktinfo pktInfo {};
struct sockaddr_in saddr_v4 = to_sockaddr(send_info.source_address.to_v4(), 0);
@@ -444,7 +414,8 @@ namespace platf {
class qos_t: public deinit_t {
public:
qos_t(int sockfd, std::vector<std::tuple<int, int, int>> options):
sockfd(sockfd), options(options) {
sockfd(sockfd),
options(options) {
qos_ref_count++;
}
@@ -472,8 +443,7 @@ namespace platf {
* @param data_type The type of traffic sent on this socket.
* @param dscp_tagging Specifies whether to enable DSCP tagging on outgoing traffic.
*/
std::unique_ptr<deinit_t>
enable_socket_qos(uintptr_t native_socket, boost::asio::ip::address &address, uint16_t port, qos_data_type_e data_type, bool dscp_tagging) {
std::unique_ptr<deinit_t> enable_socket_qos(uintptr_t native_socket, boost::asio::ip::address &address, uint16_t port, qos_data_type_e data_type, bool dscp_tagging) {
int sockfd = (int) native_socket;
std::vector<std::tuple<int, int, int>> reset_options;
@@ -495,8 +465,7 @@ namespace platf {
if (setsockopt(sockfd, SOL_SOCKET, SO_NET_SERVICE_TYPE, &service_type, sizeof(service_type)) == 0) {
// Reset SO_NET_SERVICE_TYPE to best-effort when QoS is disabled
reset_options.emplace_back(std::make_tuple(SOL_SOCKET, SO_NET_SERVICE_TYPE, NET_SERVICE_TYPE_BE));
}
else {
} else {
BOOST_LOG(error) << "Failed to set SO_NET_SERVICE_TYPE: "sv << errno;
}
}
@@ -507,8 +476,7 @@ namespace platf {
if (address.is_v6()) {
level = IPPROTO_IPV6;
option = IPV6_TCLASS;
}
else {
} else {
level = IPPROTO_IP;
option = IP_TOS;
}
@@ -535,8 +503,7 @@ namespace platf {
if (setsockopt(sockfd, level, option, &dscp, sizeof(dscp)) == 0) {
// Reset TOS to -1 when QoS is disabled
reset_options.emplace_back(std::make_tuple(level, option, -1));
}
else {
} else {
BOOST_LOG(error) << "Failed to set TOS/TCLASS: "sv << errno;
}
}
@@ -545,12 +512,10 @@ namespace platf {
return std::make_unique<qos_t>(sockfd, reset_options);
}
std::string
get_host_name() {
std::string get_host_name() {
try {
return boost::asio::ip::host_name();
}
catch (boost::system::system_error &err) {
} catch (boost::system::system_error &err) {
BOOST_LOG(error) << "Failed to get hostname: "sv << err.what();
return "Sunshine"s;
}
@@ -558,8 +523,7 @@ namespace platf {
class macos_high_precision_timer: public high_precision_timer {
public:
void
sleep_for(const std::chrono::nanoseconds &duration) override {
void sleep_for(const std::chrono::nanoseconds &duration) override {
std::this_thread::sleep_for(duration);
}
@@ -568,8 +532,7 @@ namespace platf {
}
};
std::unique_ptr<high_precision_timer>
create_high_precision_timer() {
std::unique_ptr<high_precision_timer> create_high_precision_timer() {
return std::make_unique<macos_high_precision_timer>();
}
@@ -587,8 +550,7 @@ namespace platf {
} // namespace platf
namespace dyn {
void *
handle(const std::vector<const char *> &libs) {
void *handle(const std::vector<const char *> &libs) {
void *handle;
for (auto lib : libs) {
@@ -611,8 +573,7 @@ namespace dyn {
return nullptr;
}
int
load(void *handle, const std::vector<std::tuple<apiproc *, const char *>> &funcs, bool strict) {
int load(void *handle, const std::vector<std::tuple<apiproc *, const char *>> &funcs, bool strict) {
int err = 0;
for (auto &func : funcs) {
TUPLE_2D_REF(fn, name, func);

View File

@@ -2,11 +2,12 @@
* @file src/platform/macos/nv12_zero_device.cpp
* @brief Definitions for NV12 zero copy device on macOS.
*/
// standard includes
#include <utility>
// local includes
#include "src/platform/macos/av_img_t.h"
#include "src/platform/macos/nv12_zero_device.h"
#include "src/video.h"
extern "C" {
@@ -15,20 +16,17 @@ extern "C" {
namespace platf {
void
free_frame(AVFrame *frame) {
void free_frame(AVFrame *frame) {
av_frame_free(&frame);
}
void
free_buffer(void *opaque, uint8_t *data) {
void free_buffer(void *opaque, uint8_t *data) {
CVPixelBufferRelease((CVPixelBufferRef) data);
}
util::safe_ptr<AVFrame, free_frame> av_frame;
int
nv12_zero_device::convert(platf::img_t &img) {
int nv12_zero_device::convert(platf::img_t &img) {
auto *av_img = (av_img_t *) &img;
// Release any existing CVPixelBuffer previously retained for encoding
@@ -47,8 +45,7 @@ namespace platf {
return 0;
}
int
nv12_zero_device::set_frame(AVFrame *frame, AVBufferRef *hw_frames_ctx) {
int nv12_zero_device::set_frame(AVFrame *frame, AVBufferRef *hw_frames_ctx) {
this->frame = frame;
av_frame.reset(frame);
@@ -58,11 +55,8 @@ namespace platf {
return 0;
}
int
nv12_zero_device::init(void *display, pix_fmt_e pix_fmt, resolution_fn_t resolution_fn, const pixel_format_fn_t &pixel_format_fn) {
pixel_format_fn(display, pix_fmt == pix_fmt_e::nv12 ?
kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange :
kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange);
int nv12_zero_device::init(void *display, pix_fmt_e pix_fmt, resolution_fn_t resolution_fn, const pixel_format_fn_t &pixel_format_fn) {
pixel_format_fn(display, pix_fmt == pix_fmt_e::nv12 ? kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange : kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange);
this->display = display;
this->resolution_fn = std::move(resolution_fn);

View File

@@ -4,13 +4,13 @@
*/
#pragma once
// local includes
#include "src/platform/common.h"
struct AVFrame;
namespace platf {
void
free_frame(AVFrame *frame);
void free_frame(AVFrame *frame);
class nv12_zero_device: public avcodec_encode_device_t {
// display holds a pointer to an av_video object. Since the namespaces of AVFoundation
@@ -24,13 +24,10 @@ namespace platf {
resolution_fn_t resolution_fn;
using pixel_format_fn_t = std::function<void(void *display, int pixelFormat)>;
int
init(void *display, pix_fmt_e pix_fmt, resolution_fn_t resolution_fn, const pixel_format_fn_t &pixel_format_fn);
int init(void *display, pix_fmt_e pix_fmt, resolution_fn_t resolution_fn, const pixel_format_fn_t &pixel_format_fn);
int
convert(img_t &img) override;
int
set_frame(AVFrame *frame, AVBufferRef *hw_frames_ctx) override;
int convert(img_t &img) override;
int set_frame(AVFrame *frame, AVBufferRef *hw_frames_ctx) override;
private:
util::safe_ptr<AVFrame, free_frame> av_frame;

View File

@@ -2,9 +2,13 @@
* @file src/platform/macos/publish.cpp
* @brief Definitions for publishing services on macOS.
*/
#include <dns_sd.h>
// standard includes
#include <thread>
// platform includes
#include <dns_sd.h>
// local includes
#include "src/logging.h"
#include "src/network.h"
#include "src/nvhttp.h"
@@ -17,12 +21,13 @@ namespace platf::publish {
/** @brief Custom deleter intended to be used for `std::unique_ptr<DNSServiceRef>`. */
struct ServiceRefDeleter {
typedef DNSServiceRef pointer; ///< Type of object to be deleted.
void
operator()(pointer serviceRef) {
void operator()(pointer serviceRef) {
DNSServiceRefDeallocate(serviceRef);
BOOST_LOG(info) << "Deregistered DNS service."sv;
}
};
/** @brief This class encapsulates the polling and deinitialization of our connection with
* the mDNS service. Implements the `::platf::deinit_t` interface.
*/
@@ -37,25 +42,25 @@ namespace platf::publish {
*/
deinit_t(DNSServiceRef serviceRef):
unique_ptr(serviceRef) {
_thread = std::thread { [serviceRef, &_stopRequested = std::as_const(_stopRequested)]() {
_thread = std::thread {[serviceRef, &_stopRequested = std::as_const(_stopRequested)]() {
const auto socket = DNSServiceRefSockFD(serviceRef);
while (!_stopRequested) {
auto fdset = fd_set {};
FD_ZERO(&fdset);
FD_SET(socket, &fdset);
auto timeout = timeval { .tv_sec = 3, .tv_usec = 0 }; // 3 second timeout
auto timeout = timeval {.tv_sec = 3, .tv_usec = 0}; // 3 second timeout
const auto ready = select(socket + 1, &fdset, nullptr, nullptr, &timeout);
if (ready == -1) {
BOOST_LOG(error) << "Failed to obtain response from DNS service."sv;
break;
}
else if (ready != 0) {
} else if (ready != 0) {
DNSServiceProcessResult(serviceRef);
break;
}
}
} };
}};
}
/** @brief Ensure that we gracefully finish polling the mDNS service before freeing our
* connection to it.
*/
@@ -63,9 +68,9 @@ namespace platf::publish {
_stopRequested = true;
_thread.join();
}
deinit_t(const deinit_t &) = delete;
deinit_t &
operator=(const deinit_t &) = delete;
deinit_t &operator=(const deinit_t &) = delete;
private:
std::thread _thread; ///< Thread for polling the mDNS service for a response.
@@ -75,10 +80,7 @@ namespace platf::publish {
/** @brief Callback that will be invoked when the mDNS service finishes registering our service.
* @param errorCode Describes whether the registration was successful.
*/
void
registrationCallback(DNSServiceRef /*serviceRef*/, DNSServiceFlags /*flags*/,
DNSServiceErrorType errorCode, const char * /*name*/,
const char * /*regtype*/, const char * /*domain*/, void * /*context*/) {
void registrationCallback(DNSServiceRef /*serviceRef*/, DNSServiceFlags /*flags*/, DNSServiceErrorType errorCode, const char * /*name*/, const char * /*regtype*/, const char * /*domain*/, void * /*context*/) {
if (errorCode != kDNSServiceErr_NoError) {
BOOST_LOG(error) << "Failed to register DNS service: Error "sv << errorCode;
return;
@@ -98,8 +100,7 @@ namespace platf::publish {
* which will manage polling for a response from the mDNS service, and then, when
* deconstructed, will deregister the service.
*/
[[nodiscard]] std::unique_ptr<::platf::deinit_t>
start() {
[[nodiscard]] std::unique_ptr<::platf::deinit_t> start() {
auto serviceRef = DNSServiceRef {};
const auto status = DNSServiceRegister(
&serviceRef,