Use existing config option for selecting monitor

This commit is contained in:
loki
2021-04-24 23:41:56 +02:00
parent fe3784454a
commit 0049b36471
5 changed files with 375 additions and 447 deletions
+1 -1
View File
@@ -95,7 +95,7 @@
# !! Linux only !! # !! Linux only !!
# Set the display number to stream. I have no idea how they are numbered. They start from 1, usually. # Set the display number to stream. I have no idea how they are numbered. They start from 1, usually.
# linux_monitor_id = 2 # output_name = 1
############################################### ###############################################
# FFmpeg software encoding parameters # FFmpeg software encoding parameters
-3
View File
@@ -106,7 +106,6 @@ video_t video {
{}, // encoder {}, // encoder
{}, // adapter_name {}, // adapter_name
{}, // output_name {}, // output_name
-1
}; };
audio_t audio {}; audio_t audio {};
@@ -372,8 +371,6 @@ void apply_config(std::unordered_map<std::string, std::string> &&vars) {
string_f(vars, "audio_sink", audio.sink); string_f(vars, "audio_sink", audio.sink);
int_between_f(vars, "linux_monitor_id", video.linux_monitor_id, { 0, 32 });
string_restricted_f(vars, "origin_pin_allowed", nvhttp.origin_pin_allowed, { string_restricted_f(vars, "origin_pin_allowed", nvhttp.origin_pin_allowed, {
"pc"sv, "lan"sv, "wan"sv "pc"sv, "lan"sv, "wan"sv
}); });
-2
View File
@@ -29,8 +29,6 @@ struct video_t {
std::string encoder; std::string encoder;
std::string adapter_name; std::string adapter_name;
std::string output_name; std::string output_name;
int linux_monitor_id; //Only used on linux
}; };
struct audio_t { struct audio_t {
+373 -440
View File
@@ -43,561 +43,494 @@ using xdisplay_t = util::safe_ptr_v2<Display, int, XCloseDisplay>;
using ximg_t = util::safe_ptr<XImage, freeImage>; using ximg_t = util::safe_ptr<XImage, freeImage>;
using xcursor_t = util::safe_ptr<XFixesCursorImage, freeX>; using xcursor_t = util::safe_ptr<XFixesCursorImage, freeX>;
class shm_id_t class shm_id_t {
{
public: public:
shm_id_t() : id { -1 } shm_id_t() : id { -1 } {}
{ shm_id_t(int id) : id { id } {}
} shm_id_t(shm_id_t &&other) noexcept : id(other.id) {
shm_id_t(int id) : id { id } other.id = -1;
{ }
}
shm_id_t(shm_id_t &&other) noexcept : id(other.id)
{
other.id = -1;
}
~shm_id_t() ~shm_id_t() {
{ if (id != -1) {
if (id != -1) shmctl(id, IPC_RMID, nullptr);
{ id = -1;
shmctl(id, IPC_RMID, nullptr); }
id = -1; }
} int id;
}
int id;
}; };
class shm_data_t class shm_data_t {
{
public: public:
shm_data_t() : data { (void*) -1 } shm_data_t() : data { (void*) -1 }
{ {
} }
shm_data_t(void *data) : data { data } shm_data_t(void *data) : data { data }
{ {
} }
shm_data_t(shm_data_t &&other) noexcept : data(other.data) shm_data_t(shm_data_t &&other) noexcept : data(other.data)
{ {
other.data = (void*) -1; other.data = (void*) -1;
} }
~shm_data_t() ~shm_data_t()
{ {
if ((std::uintptr_t) data != -1) if ((std::uintptr_t) data != -1)
{ {
shmdt(data); shmdt(data);
data = (void*) -1; data = (void*) -1;
} }
} }
void *data; void *data;
}; };
struct x11_img_t: public img_t struct x11_img_t: public img_t {
{ ximg_t img;
ximg_t img;
}; };
struct shm_img_t: public img_t struct shm_img_t: public img_t {
{ ~shm_img_t() override
~shm_img_t() override {
{ delete[] data;
delete[] data; data = nullptr;
data = nullptr; }
}
}; };
void blend_cursor(Display *display, img_t &img, int offsetX,int offsetY) void blend_cursor(Display *display, img_t &img, int offsetX, int offsetY) {
{ xcursor_t overlay { XFixesGetCursorImage(display) };
xcursor_t overlay { XFixesGetCursorImage(display) };
if (!overlay) if (!overlay) {
{ BOOST_LOG(error)
BOOST_LOG(error) << "Couldn't get cursor from XFixesGetCursorImage"sv;
<< "Couldn't get cursor from XFixesGetCursorImage"sv; return;
return; }
}
overlay->x -= overlay->xhot; overlay->x -= overlay->xhot;
overlay->y -= overlay->yhot; overlay->y -= overlay->yhot;
overlay->x -= offsetX; overlay->x -= offsetX;
overlay->y -= offsetY; overlay->y -= offsetY;
overlay->x = std::max((short) 0, overlay->x); overlay->x = std::max((short) 0, overlay->x);
overlay->y = std::max((short) 0, overlay->y); overlay->y = std::max((short) 0, overlay->y);
auto pixels = (int*) img.data; auto pixels = (int*) img.data;
auto screen_height = img.height; auto screen_height = img.height;
auto screen_width = img.width; auto screen_width = img.width;
auto delta_height = std::min<uint16_t>(overlay->height,std::max(0, screen_height - overlay->y)); auto delta_height = std::min<uint16_t>(overlay->height,std::max(0, screen_height - overlay->y));
auto delta_width = std::min<uint16_t>(overlay->width,std::max(0, screen_width - overlay->x)); auto delta_width = std::min<uint16_t>(overlay->width,std::max(0, screen_width - overlay->x));
for (auto y = 0; y < delta_height; ++y) for (auto y = 0; y < delta_height; ++y) {
{ auto overlay_begin = &overlay->pixels[y * overlay->width];
auto overlay_begin = &overlay->pixels[y * overlay->width]; auto overlay_end = &overlay->pixels[y * overlay->width + delta_width];
auto overlay_end = &overlay->pixels[y * overlay->width + delta_width];
auto pixels_begin = &pixels[(y + overlay->y)* (img.row_pitch / img.pixel_pitch) + overlay->x]; auto pixels_begin = &pixels[(y + overlay->y)* (img.row_pitch / img.pixel_pitch) + overlay->x];
std::for_each(overlay_begin, overlay_end,[&](long pixel) std::for_each(overlay_begin, overlay_end,[&](long pixel) {
{ int *pixel_p = (int*) &pixel;
int *pixel_p = (int*) &pixel;
auto colors_in = (uint8_t*) pixels_begin; auto colors_in = (uint8_t*) pixels_begin;
auto alpha = (*(uint*) pixel_p) >> 24u; auto alpha = (*(uint*) pixel_p) >> 24u;
if (alpha == 255) if (alpha == 255) {
{ *pixels_begin = *pixel_p;
*pixels_begin = *pixel_p; }
} else {
else auto colors_out = (uint8_t*) pixel_p;
{ colors_in[0] = colors_out[0] + (colors_in[0] * (255 - alpha) +255 /2) / 255;
auto colors_out = (uint8_t*) pixel_p; colors_in[1] = colors_out[1] + (colors_in[1] * (255 - alpha) + 255 / 2) / 255;
colors_in[0] = colors_out[0] + (colors_in[0] * (255 - alpha) +255 /2) / 255; colors_in[2] = colors_out[2] + (colors_in[2] * (255 - alpha) + 255 / 2) / 255;
colors_in[1] = colors_out[1] + (colors_in[1] * (255 - alpha) + 255 / 2) / 255; }
colors_in[2] = colors_out[2] + (colors_in[2] * (255 - alpha) + 255 / 2) / 255; ++pixels_begin;
} });
++pixels_begin; }
});
}
} }
struct x11_attr_t: public display_t struct x11_attr_t: public display_t
{ {
xdisplay_t xdisplay; xdisplay_t xdisplay;
Window xwindow; Window xwindow;
XWindowAttributes xattr; XWindowAttributes xattr;
Display* displayDisplay; Display* displayDisplay;
/* /*
* Remember last X (NOT the streamed monitor!) size. This way we can trigger reinitialization if the dimensions changed while streaming * Remember last X (NOT the streamed monitor!) size. This way we can trigger reinitialization if the dimensions changed while streaming
*/ */
int lastWidth,lastHeight; int lastWidth,lastHeight;
/* /*
* Offsets for when streaming a specific monitor. By default, they are 0. * Offsets for when streaming a specific monitor. By default, they are 0.
*/ */
int displayOffsetX,displayOffsetY; int displayOffsetX,displayOffsetY;
x11_attr_t() : xdisplay { displayDisplay = XOpenDisplay(nullptr) }, xwindow { }, xattr { } x11_attr_t() : xdisplay { displayDisplay = XOpenDisplay(nullptr) }, xwindow { }, xattr { }
{ {
XInitThreads(); XInitThreads();
if (!xdisplay) if (!xdisplay) {
{ BOOST_LOG(fatal) << "Could not open x11 display"sv;
BOOST_LOG(fatal) log_flush();
<< "Could not open x11 display"sv; std::abort();
log_flush(); }
std::abort();
}
xwindow = DefaultRootWindow(xdisplay.get()); xwindow = DefaultRootWindow(xdisplay.get());
refresh(); refresh();
int streamedMonitor = config::video.linux_monitor_id; int streamedMonitor = -1;
if(!config::video.output_name.empty()) {
streamedMonitor = (int)util::from_view(config::video.output_name);
}
if (streamedMonitor != -1) //If the value has been set at all //If the value has been set at all
{ if (streamedMonitor != -1) {
BOOST_LOG(info) << "Configuring selected monitor ("<< streamedMonitor<<") to stream. If it fails here, you may need Xrandr >= 1.5"sv; BOOST_LOG(info) << "Configuring selected monitor ("<< streamedMonitor<<") to stream. If it fails here, you may need Xrandr >= 1.5"sv;
XRRScreenResources *screenr = XRRGetScreenResources(displayDisplay, xwindow); XRRScreenResources *screenr = XRRGetScreenResources(displayDisplay, xwindow);
// This is the key right here. Use XRRScreenResources::noutput // This is the key right here. Use XRRScreenResources::noutput
int output = screenr->noutput; int output = screenr->noutput;
if (streamedMonitor >= output) if (streamedMonitor >= output) {
{ BOOST_LOG(error)<< "Could not stream selected display number because there aren't so many."sv;
BOOST_LOG(error)<< "Could not stream selected display number because there aren't so many."sv; }
} else {
else XRROutputInfo* out_info = XRRGetOutputInfo(displayDisplay, screenr, screenr->outputs[streamedMonitor]);
{ if (NULL == out_info || out_info->connection != RR_Connected)
XRROutputInfo* out_info = XRRGetOutputInfo(displayDisplay, screenr, screenr->outputs[streamedMonitor]); {
if (NULL == out_info || out_info->connection != RR_Connected) BOOST_LOG(error)<< "Could not stream selected display because it doesn't seem to be connected"sv;
{ }
BOOST_LOG(error)<< "Could not stream selected display because it doesn't seem to be connected"sv; else
} {
else XRRCrtcInfo* crt_info = XRRGetCrtcInfo(displayDisplay, screenr, out_info->crtc);
{ BOOST_LOG(info)<<"Streaming display: "<<out_info->name<<" with res "<<crt_info->width<<" x "<<crt_info->height<<+" offset by "<<crt_info->x<<" x "<<crt_info->y<<"."sv;
XRRCrtcInfo* crt_info = XRRGetCrtcInfo(displayDisplay, screenr, out_info->crtc);
BOOST_LOG(info)<<"Streaming display: "<<out_info->name<<" with res "<<crt_info->width<<" x "<<crt_info->height<<+" offset by "<<crt_info->x<<" x "<<crt_info->y<<"."sv;
width = crt_info -> width; width = crt_info -> width;
height = crt_info -> height; height = crt_info -> height;
displayOffsetX = crt_info -> x; displayOffsetX = crt_info -> x;
displayOffsetY = crt_info -> y; displayOffsetY = crt_info -> y;
XRRFreeCrtcInfo(crt_info); XRRFreeCrtcInfo(crt_info);
} }
XRRFreeOutputInfo(out_info); XRRFreeOutputInfo(out_info);
} }
XRRFreeScreenResources(screenr); XRRFreeScreenResources(screenr);
} }
else else {
{ width = xattr.width;
width = xattr.width; height = xattr.height;
height = xattr.height; }
}
lastWidth = xattr.width; lastWidth = xattr.width;
lastHeight = xattr.height; lastHeight = xattr.height;
} }
/** /**
* Called when the display attributes should change. * Called when the display attributes should change.
*/ */
void refresh() void refresh()
{ {
XGetWindowAttributes(xdisplay.get(), xwindow, &xattr); //Update xattr's XGetWindowAttributes(xdisplay.get(), xwindow, &xattr); //Update xattr's
} }
capture_e snapshot(img_t *img_out_base, std::chrono::milliseconds timeout, bool cursor) override capture_e snapshot(img_t *img_out_base, std::chrono::milliseconds timeout, bool cursor) override {
{ refresh();
refresh();
capture_e toReturn;
if (xattr.width != lastWidth || xattr.height != lastHeight) //The whole X server changed, so we gotta reinit everything
{
BOOST_LOG(warning)<< "X dimensions changed in non-SHM mode, request reinit"sv;
toReturn = capture_e::reinit;
}
else
{
XImage *img { XGetImage(xdisplay.get(), xwindow, displayOffsetX, displayOffsetY, width,height, AllPlanes, ZPixmap) };
auto img_out = (x11_img_t*) img_out_base; //The whole X server changed, so we gotta reinit everything
img_out->width = img->width; if (xattr.width != lastWidth || xattr.height != lastHeight) {
img_out->height = img->height; BOOST_LOG(warning)<< "X dimensions changed in non-SHM mode, request reinit"sv;
img_out->data = (uint8_t*) img->data; return capture_e::reinit;
img_out->row_pitch = img->bytes_per_line; }
img_out->pixel_pitch = img->bits_per_pixel / 8; XImage *img { XGetImage(xdisplay.get(), xwindow, displayOffsetX, displayOffsetY, width,height, AllPlanes, ZPixmap) };
img_out->img.reset(img);
if (cursor) auto img_out = (x11_img_t*) img_out_base;
{ img_out->width = img->width;
blend_cursor(xdisplay.get(), *img_out_base,displayOffsetX,displayOffsetY); img_out->height = img->height;
} img_out->data = (uint8_t*) img->data;
toReturn = capture_e::ok; img_out->row_pitch = img->bytes_per_line;
} img_out->pixel_pitch = img->bits_per_pixel / 8;
return toReturn; img_out->img.reset(img);
}
std::shared_ptr<img_t> alloc_img() override if (cursor) {
{ blend_cursor(xdisplay.get(), *img_out_base,displayOffsetX,displayOffsetY);
return std::make_shared<x11_img_t>(); }
}
int dummy_img(img_t *img) override return capture_e::ok;
{ }
snapshot(img, 0s, true);
return 0; std::shared_ptr<img_t> alloc_img() override {
} return std::make_shared<x11_img_t>();
}
int dummy_img(img_t *img) override {
snapshot(img, 0s, true);
return 0;
}
}; };
struct shm_attr_t: public x11_attr_t struct shm_attr_t: public x11_attr_t {
{ xdisplay_t shm_xdisplay; // Prevent race condition with x11_attr_t::xdisplay
xdisplay_t shm_xdisplay; // Prevent race condition with x11_attr_t::xdisplay xcb_connect_t xcb;
xcb_connect_t xcb; xcb_screen_t *display;
xcb_screen_t *display; std::uint32_t seg;
std::uint32_t seg;
shm_id_t shm_id; shm_id_t shm_id;
shm_data_t data; shm_data_t data;
util::TaskPool::task_id_t refresh_task_id; util::TaskPool::task_id_t refresh_task_id;
void delayed_refresh()
{
refresh();
refresh_task_id = task_pool.pushDelayed(&shm_attr_t::delayed_refresh,2s, this).task_id; void delayed_refresh() {
} refresh();
shm_attr_t() : x11_attr_t(), shm_xdisplay { XOpenDisplay(nullptr) } refresh_task_id = task_pool.pushDelayed(&shm_attr_t::delayed_refresh,2s, this).task_id;
{ }
refresh_task_id = task_pool.pushDelayed(&shm_attr_t::delayed_refresh,2s, this).task_id;
}
~shm_attr_t() override shm_attr_t() : x11_attr_t(), shm_xdisplay { XOpenDisplay(nullptr) } {
{ refresh_task_id = task_pool.pushDelayed(&shm_attr_t::delayed_refresh,2s, this).task_id;
while (!task_pool.cancel(refresh_task_id)); }
}
capture_e snapshot(img_t *img, std::chrono::milliseconds timeout, bool cursor) override ~shm_attr_t() override {
{ while (!task_pool.cancel(refresh_task_id));
capture_e toReturn; }
if (xattr.width != lastWidth || xattr.height != lastHeight) //The whole X server changed, so we gotta reinit everything
{
BOOST_LOG(warning)<< "X dimensions changed in SHM mode, request reinit"sv;
toReturn = capture_e::reinit;
}
else
{
auto img_cookie = xcb_shm_get_image_unchecked(xcb.get(), display->root,displayOffsetX, displayOffsetY, width, height, ~0, XCB_IMAGE_FORMAT_Z_PIXMAP, seg, 0);
xcb_img_t img_reply { xcb_shm_get_image_reply(xcb.get(), img_cookie,nullptr) }; capture_e snapshot(img_t *img, std::chrono::milliseconds timeout, bool cursor) override {
if (!img_reply) //The whole X server changed, so we gotta reinit everything
{ if (xattr.width != lastWidth || xattr.height != lastHeight) {
BOOST_LOG(error) BOOST_LOG(warning)<< "X dimensions changed in SHM mode, request reinit"sv;
<< "Could not get image reply"sv; return capture_e::reinit;
return capture_e::reinit; }
} else {
auto img_cookie = xcb_shm_get_image_unchecked(xcb.get(), display->root,displayOffsetX, displayOffsetY, width, height, ~0, XCB_IMAGE_FORMAT_Z_PIXMAP, seg, 0);
std::copy_n((std::uint8_t*) data.data, frame_size(), img->data); xcb_img_t img_reply { xcb_shm_get_image_reply(xcb.get(), img_cookie,nullptr) };
if (!img_reply) {
BOOST_LOG(error)
<< "Could not get image reply"sv;
return capture_e::reinit;
}
if (cursor) std::copy_n((std::uint8_t*) data.data, frame_size(), img->data);
{
blend_cursor(shm_xdisplay.get(), *img,displayOffsetX,displayOffsetY);
}
toReturn = capture_e::ok; if (cursor) {
} blend_cursor(shm_xdisplay.get(), *img,displayOffsetX,displayOffsetY);
return toReturn; }
}
std::shared_ptr<img_t> alloc_img() override return capture_e::ok;
{ }
auto img = std::make_shared<shm_img_t>(); }
img->width = width;
img->height = height;
img->pixel_pitch = 4;
img->row_pitch = img->pixel_pitch * width;
img->data = new std::uint8_t[height * img->row_pitch];
return img; std::shared_ptr<img_t> alloc_img() override {
} auto img = std::make_shared<shm_img_t>();
img->width = width;
img->height = height;
img->pixel_pitch = 4;
img->row_pitch = img->pixel_pitch * width;
img->data = new std::uint8_t[height * img->row_pitch];
int dummy_img(platf::img_t *img) override return img;
{ }
return 0;
}
int init() int dummy_img(platf::img_t *img) override {
{ return 0;
shm_xdisplay.reset(XOpenDisplay(nullptr)); }
xcb.reset(xcb_connect(nullptr, nullptr));
if (xcb_connection_has_error(xcb.get()))
{
return -1;
}
if (!xcb_get_extension_data(xcb.get(), &xcb_shm_id)->present) int init() {
{ shm_xdisplay.reset(XOpenDisplay(nullptr));
BOOST_LOG(error) xcb.reset(xcb_connect(nullptr, nullptr));
<< "Missing SHM extension"sv; if (xcb_connection_has_error(xcb.get()))
{
return -1;
}
return -1; if (!xcb_get_extension_data(xcb.get(), &xcb_shm_id)->present)
} {
BOOST_LOG(error)
<< "Missing SHM extension"sv;
auto iter = xcb_setup_roots_iterator(xcb_get_setup(xcb.get())); return -1;
display = iter.data; }
seg = xcb_generate_id(xcb.get());
shm_id.id = shmget(IPC_PRIVATE, frame_size(), IPC_CREAT | 0777); auto iter = xcb_setup_roots_iterator(xcb_get_setup(xcb.get()));
if (shm_id.id == -1) display = iter.data;
{ seg = xcb_generate_id(xcb.get());
BOOST_LOG(error)
<< "shmget failed"sv;
return -1;
}
xcb_shm_attach(xcb.get(), seg, shm_id.id, false); shm_id.id = shmget(IPC_PRIVATE, frame_size(), IPC_CREAT | 0777);
data.data = shmat(shm_id.id, nullptr, 0); if (shm_id.id == -1)
{
BOOST_LOG(error)
<< "shmget failed"sv;
return -1;
}
if ((uintptr_t) data.data == -1) xcb_shm_attach(xcb.get(), seg, shm_id.id, false);
{ data.data = shmat(shm_id.id, nullptr, 0);
BOOST_LOG(error)
<< "shmat failed"sv;
return -1; if ((uintptr_t) data.data == -1)
} {
BOOST_LOG(error)
<< "shmat failed"sv;
/* return -1;
* Commented out resetting of the sizes when intializing X in SHM mode. It might be wrong. Expect issues. This is the default mode, so poisoning those variables is not desired }
*/
// width = display->width_in_pixels;
// height = display->height_in_pixels;
return 0; /*
} * Commented out resetting of the sizes when intializing X in SHM mode. It might be wrong. Expect issues. This is the default mode, so poisoning those variables is not desired
*/
// width = display->width_in_pixels;
// height = display->height_in_pixels;
std::uint32_t frame_size() return 0;
{ }
return width * height * 4;
} std::uint32_t frame_size() {
return width * height * 4;
}
}; };
struct mic_attr_t: public mic_t struct mic_attr_t: public mic_t {
{ pa_sample_spec ss;
pa_sample_spec ss; util::safe_ptr<pa_simple, pa_simple_free> mic;
util::safe_ptr<pa_simple, pa_simple_free> mic;
explicit mic_attr_t(pa_sample_format format, std::uint32_t sample_rate, explicit mic_attr_t(pa_sample_format format, std::uint32_t sample_rate,
std::uint8_t channels) : ss { format, sample_rate, channels }, mic { } std::uint8_t channels) : ss { format, sample_rate, channels }, mic { } { }
{
}
capture_e sample(std::vector<std::int16_t> &sample_buf) override
{
auto sample_size = sample_buf.size();
auto buf = sample_buf.data(); capture_e sample(std::vector<std::int16_t> &sample_buf) override {
int status; auto sample_size = sample_buf.size();
if (pa_simple_read(mic.get(), buf, sample_size * 2, &status))
{
BOOST_LOG(error)
<< "pa_simple_read() failed: "sv << pa_strerror(status);
return capture_e::error; auto buf = sample_buf.data();
} int status;
if (pa_simple_read(mic.get(), buf, sample_size * 2, &status))
{
BOOST_LOG(error)
<< "pa_simple_read() failed: "sv << pa_strerror(status);
return capture_e::ok; return capture_e::error;
} }
return capture_e::ok;
}
}; };
std::shared_ptr<display_t> shm_display() std::shared_ptr<display_t> shm_display() {
{ auto shm = std::make_shared<shm_attr_t>();
auto shm = std::make_shared<shm_attr_t>();
if (shm->init()) if (shm->init()) {
{ return nullptr;
return nullptr; }
}
return shm; return shm;
} }
std::shared_ptr<display_t> display(platf::dev_type_e hwdevice_type) std::shared_ptr<display_t> display(platf::dev_type_e hwdevice_type) {
{ if (hwdevice_type != platf::dev_type_e::none) {
if (hwdevice_type != platf::dev_type_e::none) BOOST_LOG(error)<< "Could not initialize display with the given hw device type."sv;
{ return nullptr;
BOOST_LOG(error)<< "Could not initialize display with the given hw device type."sv; }
return nullptr;
}
auto shm_disp = shm_display(); //Attempt to use shared memory X11 to avoid copying the frame auto shm_disp = shm_display(); //Attempt to use shared memory X11 to avoid copying the frame
if (!shm_disp) if (!shm_disp) {
{ return std::make_shared<x11_attr_t>(); //Fallback to standard way if else
return std::make_shared<x11_attr_t>(); //Fallback to standard way if else }
}
return shm_disp; return shm_disp;
} }
std::unique_ptr<mic_t> microphone(std::uint32_t sample_rate) std::unique_ptr<mic_t> microphone(std::uint32_t sample_rate) {
{ auto mic = std::make_unique<mic_attr_t>(PA_SAMPLE_S16LE, sample_rate, 2);
auto mic = std::make_unique<mic_attr_t>(PA_SAMPLE_S16LE, sample_rate, 2);
int status; int status;
const char *audio_sink = "@DEFAULT_MONITOR@"; const char *audio_sink = "@DEFAULT_MONITOR@";
if (!config::audio.sink.empty()) if (!config::audio.sink.empty()) {
{ audio_sink = config::audio.sink.c_str();
audio_sink = config::audio.sink.c_str(); }
}
mic->mic.reset( mic->mic.reset(
pa_simple_new(nullptr, "sunshine", pa_simple_new(nullptr, "sunshine",
pa_stream_direction_t::PA_STREAM_RECORD, audio_sink, pa_stream_direction_t::PA_STREAM_RECORD, audio_sink,
"sunshine-record", &mic->ss, nullptr, nullptr, &status)); "sunshine-record", &mic->ss, nullptr, nullptr, &status));
if (!mic->mic) if (!mic->mic) {
{ auto err_str = pa_strerror(status);
auto err_str = pa_strerror(status); BOOST_LOG(error) << "pa_simple_new() failed: "sv << err_str;
BOOST_LOG(error)
<< "pa_simple_new() failed: "sv << err_str;
log_flush(); log_flush();
std::abort(); std::abort();
} }
return mic; return mic;
} }
ifaddr_t get_ifaddrs() ifaddr_t get_ifaddrs() {
{ ifaddrs *p { nullptr };
ifaddrs *p { nullptr };
getifaddrs(&p); 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];
char data[INET6_ADDRSTRLEN];
auto family = ip_addr->sa_family; auto family = ip_addr->sa_family;
if (family == AF_INET6) if (family == AF_INET6) {
{ inet_ntop(AF_INET6, &((sockaddr_in6*) ip_addr)->sin6_addr, data,
inet_ntop(AF_INET6, &((sockaddr_in6*) ip_addr)->sin6_addr, data, INET6_ADDRSTRLEN);
INET6_ADDRSTRLEN); }
}
if (family == AF_INET) if (family == AF_INET) {
{ inet_ntop(AF_INET, &((sockaddr_in*) ip_addr)->sin_addr, data,
inet_ntop(AF_INET, &((sockaddr_in*) ip_addr)->sin_addr, data, INET_ADDRSTRLEN);
INET_ADDRSTRLEN); }
}
return std::string { data }; return std::string { data };
} }
std::pair<std::uint16_t, std::string> from_sockaddr_ex( std::pair<std::uint16_t, std::string> from_sockaddr_ex(const sockaddr *const ip_addr) {
const sockaddr *const ip_addr) char data[INET6_ADDRSTRLEN];
{
char data[INET6_ADDRSTRLEN];
auto family = ip_addr->sa_family; auto family = ip_addr->sa_family;
std::uint16_t port; std::uint16_t port;
if (family == AF_INET6) if (family == AF_INET6) {
{ inet_ntop(AF_INET6, &((sockaddr_in6*) ip_addr)->sin6_addr, data,
inet_ntop(AF_INET6, &((sockaddr_in6*) ip_addr)->sin6_addr, data, INET6_ADDRSTRLEN);
INET6_ADDRSTRLEN); port = ((sockaddr_in6*) ip_addr)->sin6_port;
port = ((sockaddr_in6*) ip_addr)->sin6_port; }
}
if (family == AF_INET) if (family == AF_INET) {
{ inet_ntop(AF_INET, &((sockaddr_in*) ip_addr)->sin_addr, data,
inet_ntop(AF_INET, &((sockaddr_in*) ip_addr)->sin_addr, data, INET_ADDRSTRLEN);
INET_ADDRSTRLEN); port = ((sockaddr_in*) ip_addr)->sin_port;
port = ((sockaddr_in*) ip_addr)->sin_port; }
}
return return
{ port, std::string { port, std::string {data}};
{ 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();
auto ifaddrs = get_ifaddrs(); for (auto pos = ifaddrs.get(); pos != nullptr; pos = pos->ifa_next) {
for (auto pos = ifaddrs.get(); pos != nullptr; pos = pos->ifa_next) if (pos->ifa_addr && address == from_sockaddr(pos->ifa_addr)) {
{ std::ifstream mac_file("/sys/class/net/"s + pos->ifa_name + "/address");
if (pos->ifa_addr && address == from_sockaddr(pos->ifa_addr)) if (mac_file.good()) {
{ std::string mac_address;
std::ifstream mac_file( std::getline(mac_file, mac_address);
"/sys/class/net/"s + pos->ifa_name + "/address"); return mac_address;
if (mac_file.good()) }
{ }
std::string mac_address; }
std::getline(mac_file, mac_address); BOOST_LOG(warning)
return mac_address; << "Unable to find MAC address for "sv << address;
} return "00:00:00:00:00:00"s;
}
}
BOOST_LOG(warning)
<< "Unable to find MAC address for "sv << address;
return "00:00:00:00:00:00"s;
} }
void freeImage(XImage *p) void freeImage(XImage *p) {
{ XDestroyImage(p);
XDestroyImage(p);
} }
void freeX(XFixesCursorImage *p) void freeX(XFixesCursorImage *p) {
{ XFree(p);
XFree(p);
} }
} }