mirror of
https://repo.dec05eba.com/gpu-screen-recorder
synced 2026-03-31 09:07:13 +09:00
Prepare for pipewire audio routing
This commit is contained in:
4
include/pipewire_audio.h
Normal file
4
include/pipewire_audio.h
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
#ifndef GSR_PIPEWIRE_AUDIO_H
|
||||||
|
#define GSR_PIPEWIRE_AUDIO_H
|
||||||
|
|
||||||
|
#endif /* GSR_PIPEWIRE_AUDIO_H */
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
#ifndef GSR_PIPEWIRE_H
|
#ifndef GSR_PIPEWIRE_VIDEO_H
|
||||||
#define GSR_PIPEWIRE_H
|
#define GSR_PIPEWIRE_VIDEO_H
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
@@ -8,9 +8,9 @@
|
|||||||
#include <spa/utils/hook.h>
|
#include <spa/utils/hook.h>
|
||||||
#include <spa/param/video/format.h>
|
#include <spa/param/video/format.h>
|
||||||
|
|
||||||
#define GSR_PIPEWIRE_MAX_MODIFIERS 1024
|
#define GSR_PIPEWIRE_VIDEO_MAX_MODIFIERS 1024
|
||||||
#define GSR_PIPEWIRE_NUM_VIDEO_FORMATS 6
|
#define GSR_PIPEWIRE_VIDEO_NUM_VIDEO_FORMATS 6
|
||||||
#define GSR_PIPEWIRE_DMABUF_MAX_PLANES 4
|
#define GSR_PIPEWIRE_VIDEO_DMABUF_MAX_PLANES 4
|
||||||
|
|
||||||
typedef struct gsr_egl gsr_egl;
|
typedef struct gsr_egl gsr_egl;
|
||||||
|
|
||||||
@@ -18,23 +18,23 @@ typedef struct {
|
|||||||
int major;
|
int major;
|
||||||
int minor;
|
int minor;
|
||||||
int micro;
|
int micro;
|
||||||
} gsr_pipewire_data_version;
|
} gsr_pipewire_video_data_version;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint32_t fps_num;
|
uint32_t fps_num;
|
||||||
uint32_t fps_den;
|
uint32_t fps_den;
|
||||||
} gsr_pipewire_video_info;
|
} gsr_pipewire_video_video_info;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int fd;
|
int fd;
|
||||||
uint32_t offset;
|
uint32_t offset;
|
||||||
int32_t stride;
|
int32_t stride;
|
||||||
} gsr_pipewire_dmabuf_data;
|
} gsr_pipewire_video_dmabuf_data;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int x, y;
|
int x, y;
|
||||||
int width, height;
|
int width, height;
|
||||||
} gsr_pipewire_region;
|
} gsr_pipewire_video_region;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
enum spa_video_format format;
|
enum spa_video_format format;
|
||||||
@@ -82,31 +82,31 @@ typedef struct {
|
|||||||
uint32_t width, height;
|
uint32_t width, height;
|
||||||
} crop;
|
} crop;
|
||||||
|
|
||||||
gsr_video_format supported_video_formats[GSR_PIPEWIRE_NUM_VIDEO_FORMATS];
|
gsr_video_format supported_video_formats[GSR_PIPEWIRE_VIDEO_NUM_VIDEO_FORMATS];
|
||||||
|
|
||||||
gsr_pipewire_data_version server_version;
|
gsr_pipewire_video_data_version server_version;
|
||||||
gsr_pipewire_video_info video_info;
|
gsr_pipewire_video_video_info video_info;
|
||||||
gsr_pipewire_dmabuf_data dmabuf_data[GSR_PIPEWIRE_DMABUF_MAX_PLANES];
|
gsr_pipewire_video_dmabuf_data dmabuf_data[GSR_PIPEWIRE_VIDEO_DMABUF_MAX_PLANES];
|
||||||
size_t dmabuf_num_planes;
|
size_t dmabuf_num_planes;
|
||||||
|
|
||||||
bool no_modifiers_fallback;
|
bool no_modifiers_fallback;
|
||||||
bool external_texture_fallback;
|
bool external_texture_fallback;
|
||||||
|
|
||||||
uint64_t modifiers[GSR_PIPEWIRE_MAX_MODIFIERS];
|
uint64_t modifiers[GSR_PIPEWIRE_VIDEO_MAX_MODIFIERS];
|
||||||
size_t num_modifiers;
|
size_t num_modifiers;
|
||||||
} gsr_pipewire;
|
} gsr_pipewire_video;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|capture_cursor| only applies to when capturing a window or region.
|
|capture_cursor| only applies to when capturing a window or region.
|
||||||
In other cases |pipewire_node|'s setup will determine if the cursor is included.
|
In other cases |pipewire_node|'s setup will determine if the cursor is included.
|
||||||
Note that the cursor is not guaranteed to be shown even if set to true, it depends on the wayland compositor.
|
Note that the cursor is not guaranteed to be shown even if set to true, it depends on the wayland compositor.
|
||||||
*/
|
*/
|
||||||
bool gsr_pipewire_init(gsr_pipewire *self, int pipewire_fd, uint32_t pipewire_node, int fps, bool capture_cursor, gsr_egl *egl);
|
bool gsr_pipewire_video_init(gsr_pipewire_video *self, int pipewire_fd, uint32_t pipewire_node, int fps, bool capture_cursor, gsr_egl *egl);
|
||||||
void gsr_pipewire_deinit(gsr_pipewire *self);
|
void gsr_pipewire_video_deinit(gsr_pipewire_video *self);
|
||||||
|
|
||||||
/* |dmabuf_data| should be at least GSR_PIPEWIRE_DMABUF_MAX_PLANES in size */
|
/* |dmabuf_data| should be at least GSR_PIPEWIRE_VIDEO_DMABUF_MAX_PLANES in size */
|
||||||
bool gsr_pipewire_map_texture(gsr_pipewire *self, gsr_texture_map texture_map, gsr_pipewire_region *region, gsr_pipewire_region *cursor_region, gsr_pipewire_dmabuf_data *dmabuf_data, int *num_dmabuf_data, uint32_t *fourcc, uint64_t *modifiers, bool *using_external_image);
|
bool gsr_pipewire_video_map_texture(gsr_pipewire_video *self, gsr_texture_map texture_map, gsr_pipewire_video_region *region, gsr_pipewire_video_region *cursor_region, gsr_pipewire_video_dmabuf_data *dmabuf_data, int *num_dmabuf_data, uint32_t *fourcc, uint64_t *modifiers, bool *using_external_image);
|
||||||
bool gsr_pipewire_is_damaged(gsr_pipewire *self);
|
bool gsr_pipewire_video_is_damaged(gsr_pipewire_video *self);
|
||||||
void gsr_pipewire_clear_damage(gsr_pipewire *self);
|
void gsr_pipewire_video_clear_damage(gsr_pipewire_video *self);
|
||||||
|
|
||||||
#endif /* GSR_PIPEWIRE_H */
|
#endif /* GSR_PIPEWIRE_VIDEO_H */
|
||||||
20
meson.build
20
meson.build
@@ -57,20 +57,32 @@ dep = [
|
|||||||
dependency('wayland-client'),
|
dependency('wayland-client'),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
uses_pipewire = false
|
||||||
|
|
||||||
if get_option('portal') == true
|
if get_option('portal') == true
|
||||||
src += [
|
src += [
|
||||||
'src/capture/portal.c',
|
'src/capture/portal.c',
|
||||||
'src/dbus.c',
|
'src/dbus.c',
|
||||||
'src/pipewire.c',
|
'src/pipewire_video.c',
|
||||||
]
|
]
|
||||||
|
dep += dependency('dbus-1')
|
||||||
|
add_project_arguments('-DGSR_PORTAL', language : ['c', 'cpp'])
|
||||||
|
uses_pipewire = true
|
||||||
|
endif
|
||||||
|
|
||||||
|
if get_option('app_audio') == true
|
||||||
|
src += [
|
||||||
|
'src/pipewire_audio.c',
|
||||||
|
]
|
||||||
|
add_project_arguments('-DGSR_APP_AUDIO', language : ['c', 'cpp'])
|
||||||
|
uses_pipewire = true
|
||||||
|
endif
|
||||||
|
|
||||||
|
if uses_pipewire == true
|
||||||
dep += [
|
dep += [
|
||||||
dependency('dbus-1'),
|
|
||||||
dependency('libpipewire-0.3'),
|
dependency('libpipewire-0.3'),
|
||||||
dependency('libspa-0.2'),
|
dependency('libspa-0.2'),
|
||||||
]
|
]
|
||||||
|
|
||||||
add_project_arguments('-DGSR_PORTAL', language : ['c', 'cpp'])
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
add_project_arguments('-DGSR_VERSION="' + meson.project_version() + '"', language: ['c', 'cpp'])
|
add_project_arguments('-DGSR_VERSION="' + meson.project_version() + '"', language: ['c', 'cpp'])
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
option('systemd', type : 'boolean', value : true, description : 'Install systemd service file')
|
option('systemd', type : 'boolean', value : true, description : 'Install systemd service file')
|
||||||
option('capabilities', type : 'boolean', value : true, description : 'Set binary admin capability to remove password prompt when recording monitor (without desktop portal option) on amd/intel or nvidia wayland')
|
option('capabilities', type : 'boolean', value : true, description : 'Set binary admin capability to remove password prompt when recording monitor (without desktop portal option) on amd/intel or nvidia wayland')
|
||||||
option('nvidia_suspend_fix', type : 'boolean', value : true, description : 'Install nvidia modprobe config file to tell nvidia driver to preserve video memory on suspend. This is a workaround for an nvidia driver bug that breaks cuda (and gpu screen recorder) on suspend')
|
option('nvidia_suspend_fix', type : 'boolean', value : true, description : 'Install nvidia modprobe config file to tell nvidia driver to preserve video memory on suspend. This is a workaround for an nvidia driver bug that breaks cuda (and gpu screen recorder) on suspend')
|
||||||
option('portal', type : 'boolean', value : true, description : 'If GPU Screen Recorder should be built with support for xdg desktop portal ScreenCast capture (wayland only)')
|
option('portal', type : 'boolean', value : true, description : 'Build with support for xdg desktop portal ScreenCast capture (wayland only) (-w portal option)')
|
||||||
|
option('app_audio', type : 'boolean', value : true, description : 'Build with support for recording a single audio source (-aa option). Requires pipewire')
|
||||||
@@ -10,6 +10,7 @@ ignore_dirs = ["kms/server", "build", "debug-build"]
|
|||||||
|
|
||||||
[define]
|
[define]
|
||||||
GSR_PORTAL = "1"
|
GSR_PORTAL = "1"
|
||||||
|
GSR_APP_AUDIO = "1"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
libavcodec = ">=58"
|
libavcodec = ">=58"
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
#include "../../include/egl.h"
|
#include "../../include/egl.h"
|
||||||
#include "../../include/utils.h"
|
#include "../../include/utils.h"
|
||||||
#include "../../include/dbus.h"
|
#include "../../include/dbus.h"
|
||||||
#include "../../include/pipewire.h"
|
#include "../../include/pipewire_video.h"
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@@ -20,9 +20,9 @@ typedef struct {
|
|||||||
gsr_dbus dbus;
|
gsr_dbus dbus;
|
||||||
char *session_handle;
|
char *session_handle;
|
||||||
|
|
||||||
gsr_pipewire pipewire;
|
gsr_pipewire_video pipewire;
|
||||||
vec2i capture_size;
|
vec2i capture_size;
|
||||||
gsr_pipewire_dmabuf_data dmabuf_data[GSR_PIPEWIRE_DMABUF_MAX_PLANES];
|
gsr_pipewire_video_dmabuf_data dmabuf_data[GSR_PIPEWIRE_VIDEO_DMABUF_MAX_PLANES];
|
||||||
int num_dmabuf_data;
|
int num_dmabuf_data;
|
||||||
|
|
||||||
AVCodecContext *video_codec_context;
|
AVCodecContext *video_codec_context;
|
||||||
@@ -57,7 +57,7 @@ static void gsr_capture_portal_stop(gsr_capture_portal *self) {
|
|||||||
|
|
||||||
gsr_capture_portal_cleanup_plane_fds(self);
|
gsr_capture_portal_cleanup_plane_fds(self);
|
||||||
|
|
||||||
gsr_pipewire_deinit(&self->pipewire);
|
gsr_pipewire_video_deinit(&self->pipewire);
|
||||||
|
|
||||||
if(self->session_handle) {
|
if(self->session_handle) {
|
||||||
free(self->session_handle);
|
free(self->session_handle);
|
||||||
@@ -233,8 +233,8 @@ static int gsr_capture_portal_setup_dbus(gsr_capture_portal *self, int *pipewire
|
|||||||
}
|
}
|
||||||
|
|
||||||
static bool gsr_capture_portal_get_frame_dimensions(gsr_capture_portal *self) {
|
static bool gsr_capture_portal_get_frame_dimensions(gsr_capture_portal *self) {
|
||||||
gsr_pipewire_region region = {0, 0, 0, 0};
|
gsr_pipewire_video_region region = {0, 0, 0, 0};
|
||||||
gsr_pipewire_region cursor_region = {0, 0, 0, 0};
|
gsr_pipewire_video_region cursor_region = {0, 0, 0, 0};
|
||||||
fprintf(stderr, "gsr info: gsr_capture_portal_start: waiting for pipewire negotiation\n");
|
fprintf(stderr, "gsr info: gsr_capture_portal_start: waiting for pipewire negotiation\n");
|
||||||
|
|
||||||
const double start_time = clock_get_monotonic_seconds();
|
const double start_time = clock_get_monotonic_seconds();
|
||||||
@@ -242,7 +242,7 @@ static bool gsr_capture_portal_get_frame_dimensions(gsr_capture_portal *self) {
|
|||||||
bool uses_external_image = false;
|
bool uses_external_image = false;
|
||||||
uint32_t fourcc = 0;
|
uint32_t fourcc = 0;
|
||||||
uint64_t modifiers = 0;
|
uint64_t modifiers = 0;
|
||||||
if(gsr_pipewire_map_texture(&self->pipewire, self->texture_map, ®ion, &cursor_region, self->dmabuf_data, &self->num_dmabuf_data, &fourcc, &modifiers, &uses_external_image)) {
|
if(gsr_pipewire_video_map_texture(&self->pipewire, self->texture_map, ®ion, &cursor_region, self->dmabuf_data, &self->num_dmabuf_data, &fourcc, &modifiers, &uses_external_image)) {
|
||||||
gsr_capture_portal_cleanup_plane_fds(self);
|
gsr_capture_portal_cleanup_plane_fds(self);
|
||||||
self->capture_size.x = region.width;
|
self->capture_size.x = region.width;
|
||||||
self->capture_size.y = region.height;
|
self->capture_size.y = region.height;
|
||||||
@@ -285,7 +285,7 @@ static int gsr_capture_portal_start(gsr_capture *cap, AVCodecContext *video_code
|
|||||||
fprintf(stderr, "gsr info: gsr_capture_portal_start: setting up pipewire\n");
|
fprintf(stderr, "gsr info: gsr_capture_portal_start: setting up pipewire\n");
|
||||||
/* TODO: support hdr when pipewire supports it */
|
/* TODO: support hdr when pipewire supports it */
|
||||||
/* gsr_pipewire closes the pipewire fd, even on failure */
|
/* gsr_pipewire closes the pipewire fd, even on failure */
|
||||||
if(!gsr_pipewire_init(&self->pipewire, pipewire_fd, pipewire_node, video_codec_context->framerate.num, self->params.record_cursor, self->params.egl)) {
|
if(!gsr_pipewire_video_init(&self->pipewire, pipewire_fd, pipewire_node, video_codec_context->framerate.num, self->params.record_cursor, self->params.egl)) {
|
||||||
fprintf(stderr, "gsr error: gsr_capture_portal_start: failed to setup pipewire with fd: %d, node: %" PRIu32 "\n", pipewire_fd, pipewire_node);
|
fprintf(stderr, "gsr error: gsr_capture_portal_start: failed to setup pipewire with fd: %d, node: %" PRIu32 "\n", pipewire_fd, pipewire_node);
|
||||||
gsr_capture_portal_stop(self);
|
gsr_capture_portal_stop(self);
|
||||||
return -1;
|
return -1;
|
||||||
@@ -327,12 +327,12 @@ static int gsr_capture_portal_capture(gsr_capture *cap, AVFrame *frame, gsr_colo
|
|||||||
gsr_capture_portal *self = cap->priv;
|
gsr_capture_portal *self = cap->priv;
|
||||||
|
|
||||||
/* TODO: Handle formats other than RGB(a) */
|
/* TODO: Handle formats other than RGB(a) */
|
||||||
gsr_pipewire_region region = {0, 0, 0, 0};
|
gsr_pipewire_video_region region = {0, 0, 0, 0};
|
||||||
gsr_pipewire_region cursor_region = {0, 0, 0, 0};
|
gsr_pipewire_video_region cursor_region = {0, 0, 0, 0};
|
||||||
uint32_t pipewire_fourcc = 0;
|
uint32_t pipewire_fourcc = 0;
|
||||||
uint64_t pipewire_modifiers = 0;
|
uint64_t pipewire_modifiers = 0;
|
||||||
bool using_external_image = false;
|
bool using_external_image = false;
|
||||||
if(gsr_pipewire_map_texture(&self->pipewire, self->texture_map, ®ion, &cursor_region, self->dmabuf_data, &self->num_dmabuf_data, &pipewire_fourcc, &pipewire_modifiers, &using_external_image)) {
|
if(gsr_pipewire_video_map_texture(&self->pipewire, self->texture_map, ®ion, &cursor_region, self->dmabuf_data, &self->num_dmabuf_data, &pipewire_fourcc, &pipewire_modifiers, &using_external_image)) {
|
||||||
if(region.width != self->capture_size.x || region.height != self->capture_size.y) {
|
if(region.width != self->capture_size.x || region.height != self->capture_size.y) {
|
||||||
self->capture_size.x = region.width;
|
self->capture_size.x = region.width;
|
||||||
self->capture_size.y = region.height;
|
self->capture_size.y = region.height;
|
||||||
@@ -420,12 +420,12 @@ static bool gsr_capture_portal_uses_external_image(gsr_capture *cap) {
|
|||||||
|
|
||||||
static bool gsr_capture_portal_is_damaged(gsr_capture *cap) {
|
static bool gsr_capture_portal_is_damaged(gsr_capture *cap) {
|
||||||
gsr_capture_portal *self = cap->priv;
|
gsr_capture_portal *self = cap->priv;
|
||||||
return gsr_pipewire_is_damaged(&self->pipewire);
|
return gsr_pipewire_video_is_damaged(&self->pipewire);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gsr_capture_portal_clear_damage(gsr_capture *cap) {
|
static void gsr_capture_portal_clear_damage(gsr_capture *cap) {
|
||||||
gsr_capture_portal *self = cap->priv;
|
gsr_capture_portal *self = cap->priv;
|
||||||
gsr_pipewire_clear_damage(&self->pipewire);
|
gsr_pipewire_video_clear_damage(&self->pipewire);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gsr_capture_portal_destroy(gsr_capture *cap, AVCodecContext *video_codec_context) {
|
static void gsr_capture_portal_destroy(gsr_capture *cap, AVCodecContext *video_codec_context) {
|
||||||
|
|||||||
19
src/main.cpp
19
src/main.cpp
@@ -990,6 +990,8 @@ static void open_video_hardware(AVCodecContext *codec_context, VideoQuality vide
|
|||||||
|
|
||||||
// TODO: Enable multipass
|
// TODO: Enable multipass
|
||||||
|
|
||||||
|
// TODO: Set "usage" option to "record"/"stream" and "content" option to "rendered" for vulkan encoding
|
||||||
|
|
||||||
if(vendor == GSR_GPU_VENDOR_NVIDIA) {
|
if(vendor == GSR_GPU_VENDOR_NVIDIA) {
|
||||||
// TODO: These dont seem to be necessary
|
// TODO: These dont seem to be necessary
|
||||||
// av_dict_set_int(&options, "zerolatency", 1, 0);
|
// av_dict_set_int(&options, "zerolatency", 1, 0);
|
||||||
@@ -1062,7 +1064,7 @@ static void open_video_hardware(AVCodecContext *codec_context, VideoQuality vide
|
|||||||
static void usage_header() {
|
static void usage_header() {
|
||||||
const bool inside_flatpak = getenv("FLATPAK_ID") != NULL;
|
const bool inside_flatpak = getenv("FLATPAK_ID") != NULL;
|
||||||
const char *program_name = inside_flatpak ? "flatpak run --command=gpu-screen-recorder com.dec05eba.gpu_screen_recorder" : "gpu-screen-recorder";
|
const char *program_name = inside_flatpak ? "flatpak run --command=gpu-screen-recorder com.dec05eba.gpu_screen_recorder" : "gpu-screen-recorder";
|
||||||
fprintf(stderr, "usage: %s -w <window_id|monitor|focused|portal> [-c <container_format>] [-s WxH] -f <fps> [-a <audio_input>] [-q <quality>] [-r <replay_buffer_size_sec>] [-k h264|hevc|av1|vp8|vp9|hevc_hdr|av1_hdr|hevc_10bit|av1_10bit] [-ac aac|opus|flac] [-ab <bitrate>] [-oc yes|no] [-fm cfr|vfr|content] [-bm auto|qp|vbr|cbr] [-cr limited|full] [-df yes|no] [-sc <script_path>] [-cursor yes|no] [-keyint <value>] [-restore-portal-session yes|no] [-portal-session-token-filepath filepath] [-encoder gpu|cpu] [-o <output_file>] [-v yes|no] [--version] [-h|--help]\n", program_name);
|
fprintf(stderr, "usage: %s -w <window_id|monitor|focused|portal> [-c <container_format>] [-s WxH] -f <fps> [-a <audio_input>] [-aa <application_name>] [-q <quality>] [-r <replay_buffer_size_sec>] [-k h264|hevc|av1|vp8|vp9|hevc_hdr|av1_hdr|hevc_10bit|av1_10bit] [-ac aac|opus|flac] [-ab <bitrate>] [-oc yes|no] [-fm cfr|vfr|content] [-bm auto|qp|vbr|cbr] [-cr limited|full] [-df yes|no] [-sc <script_path>] [-cursor yes|no] [-keyint <value>] [-restore-portal-session yes|no] [-portal-session-token-filepath filepath] [-encoder gpu|cpu] [-o <output_file>] [-v yes|no] [--version] [-h|--help]\n", program_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Update with portal info
|
// TODO: Update with portal info
|
||||||
@@ -1100,6 +1102,15 @@ static void usage_full() {
|
|||||||
fprintf(stderr, " If the audio device is an empty string then the audio device is ignored.\n");
|
fprintf(stderr, " If the audio device is an empty string then the audio device is ignored.\n");
|
||||||
fprintf(stderr, " Optional, no audio track is added by default.\n");
|
fprintf(stderr, " Optional, no audio track is added by default.\n");
|
||||||
fprintf(stderr, "\n");
|
fprintf(stderr, "\n");
|
||||||
|
#ifdef GSR_APP_AUDIO
|
||||||
|
fprintf(stderr, " -aa Audio device to record from (pulse audio device). Can be specified multiple times. Each time this is specified a new audio track is added for the specified audio device.\n");
|
||||||
|
fprintf(stderr, " A name can be given to the audio input device by prefixing the audio input with <name>/, for example \"dummy/alsa_output.pci-0000_00_1b.0.analog-stereo.monitor\".\n");
|
||||||
|
fprintf(stderr, " Multiple audio devices can be merged into one audio track by using \"|\" as a separator into one -a argument, for example: -a \"alsa_output1|alsa_output2\".\n");
|
||||||
|
fprintf(stderr, " The audio device can also be \"default_output\" in which case the default output device is used, or \"default_input\" in which case the default input device is used.\n");
|
||||||
|
fprintf(stderr, " If the audio device is an empty string then the audio device is ignored.\n");
|
||||||
|
fprintf(stderr, " Optional, no audio track is added by default.\n");
|
||||||
|
fprintf(stderr, "\n");
|
||||||
|
#endif
|
||||||
fprintf(stderr, " -q Video quality. Should be either 'medium', 'high', 'very_high' or 'ultra' when using '-bm qp' or '-bm vbr' options, and '-bm qp' is the default option used.\n");
|
fprintf(stderr, " -q Video quality. Should be either 'medium', 'high', 'very_high' or 'ultra' when using '-bm qp' or '-bm vbr' options, and '-bm qp' is the default option used.\n");
|
||||||
fprintf(stderr, " 'high' is the recommended option when live streaming or when you have a slower harddrive.\n");
|
fprintf(stderr, " 'high' is the recommended option when live streaming or when you have a slower harddrive.\n");
|
||||||
fprintf(stderr, " When using '-bm cbr' option then this is option is instead used to specify the video bitrate in kbps.\n");
|
fprintf(stderr, " When using '-bm cbr' option then this is option is instead used to specify the video bitrate in kbps.\n");
|
||||||
@@ -2120,7 +2131,7 @@ static gsr_capture* create_capture_impl(std::string &window_str, vec2i output_re
|
|||||||
if(!capture)
|
if(!capture)
|
||||||
_exit(1);
|
_exit(1);
|
||||||
#else
|
#else
|
||||||
fprintf(stderr, "Error: option '-w portal' used but GPU Screen Recorder was compiled without desktop portal support\n");
|
fprintf(stderr, "Error: option '-w portal' used but GPU Screen Recorder was compiled without desktop portal support. Please recompile GPU Screen recorder with the -Dportal=true option\n");
|
||||||
_exit(2);
|
_exit(2);
|
||||||
#endif
|
#endif
|
||||||
} else if(contains_non_hex_number(window_str.c_str())) {
|
} else if(contains_non_hex_number(window_str.c_str())) {
|
||||||
@@ -2307,8 +2318,8 @@ static std::vector<MergedAudioInputs> parse_audio_inputs(const AudioDevices &aud
|
|||||||
fprintf(stderr, " default_output (Default output)\n");
|
fprintf(stderr, " default_output (Default output)\n");
|
||||||
if(!audio_devices.default_input.empty())
|
if(!audio_devices.default_input.empty())
|
||||||
fprintf(stderr, " default_input (Default input)\n");
|
fprintf(stderr, " default_input (Default input)\n");
|
||||||
for(const auto &audio_input : audio_devices.audio_inputs) {
|
for(const auto &audio_device_input : audio_devices.audio_inputs) {
|
||||||
fprintf(stderr, " %s (%s)\n", audio_input.name.c_str(), audio_input.description.c_str());
|
fprintf(stderr, " %s (%s)\n", audio_device_input.name.c_str(), audio_device_input.description.c_str());
|
||||||
}
|
}
|
||||||
_exit(2);
|
_exit(2);
|
||||||
}
|
}
|
||||||
|
|||||||
1
src/pipewire_audio.c
Normal file
1
src/pipewire_audio.c
Normal file
@@ -0,0 +1 @@
|
|||||||
|
#include "../include/pipewire_audio.h"
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
#include "../include/pipewire.h"
|
#include "../include/pipewire_video.h"
|
||||||
#include "../include/egl.h"
|
#include "../include/egl.h"
|
||||||
#include "../include/utils.h"
|
#include "../include/utils.h"
|
||||||
|
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
|
|
||||||
/* This code is partially based on xr-video-player pipewire implementation which is based on obs-studio's pipewire implementation */
|
/* This code is partially based on xr-video-player pipewire implementation which is based on obs-studio's pipewire implementation */
|
||||||
|
|
||||||
/* TODO: Make gsr_pipewire_init asynchronous */
|
/* TODO: Make gsr_pipewire_video_init asynchronous */
|
||||||
/* TODO: Support 10-bit capture (hdr) when pipewire supports it */
|
/* TODO: Support 10-bit capture (hdr) when pipewire supports it */
|
||||||
/* TODO: Test all of the image formats */
|
/* TODO: Test all of the image formats */
|
||||||
|
|
||||||
@@ -25,12 +25,12 @@
|
|||||||
(sizeof(struct spa_meta_cursor) + sizeof(struct spa_meta_bitmap) + \
|
(sizeof(struct spa_meta_cursor) + sizeof(struct spa_meta_bitmap) + \
|
||||||
width * height * 4)
|
width * height * 4)
|
||||||
|
|
||||||
static bool parse_pw_version(gsr_pipewire_data_version *dst, const char *version) {
|
static bool parse_pw_version(gsr_pipewire_video_data_version *dst, const char *version) {
|
||||||
const int n_matches = sscanf(version, "%d.%d.%d", &dst->major, &dst->minor, &dst->micro);
|
const int n_matches = sscanf(version, "%d.%d.%d", &dst->major, &dst->minor, &dst->micro);
|
||||||
return n_matches == 3;
|
return n_matches == 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool check_pw_version(const gsr_pipewire_data_version *pw_version, int major, int minor, int micro) {
|
static bool check_pw_version(const gsr_pipewire_video_data_version *pw_version, int major, int minor, int micro) {
|
||||||
if (pw_version->major != major)
|
if (pw_version->major != major)
|
||||||
return pw_version->major > major;
|
return pw_version->major > major;
|
||||||
if (pw_version->minor != minor)
|
if (pw_version->minor != minor)
|
||||||
@@ -38,7 +38,7 @@ static bool check_pw_version(const gsr_pipewire_data_version *pw_version, int ma
|
|||||||
return pw_version->micro >= micro;
|
return pw_version->micro >= micro;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void update_pw_versions(gsr_pipewire *self, const char *version) {
|
static void update_pw_versions(gsr_pipewire_video *self, const char *version) {
|
||||||
fprintf(stderr, "gsr info: pipewire: server version: %s\n", version);
|
fprintf(stderr, "gsr info: pipewire: server version: %s\n", version);
|
||||||
fprintf(stderr, "gsr info: pipewire: library version: %s\n", pw_get_library_version());
|
fprintf(stderr, "gsr info: pipewire: library version: %s\n", pw_get_library_version());
|
||||||
fprintf(stderr, "gsr info: pipewire: header version: %s\n", pw_get_headers_version());
|
fprintf(stderr, "gsr info: pipewire: header version: %s\n", pw_get_headers_version());
|
||||||
@@ -47,18 +47,18 @@ static void update_pw_versions(gsr_pipewire *self, const char *version) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void on_core_info_cb(void *user_data, const struct pw_core_info *info) {
|
static void on_core_info_cb(void *user_data, const struct pw_core_info *info) {
|
||||||
gsr_pipewire *self = user_data;
|
gsr_pipewire_video *self = user_data;
|
||||||
update_pw_versions(self, info->version);
|
update_pw_versions(self, info->version);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void on_core_error_cb(void *user_data, uint32_t id, int seq, int res, const char *message) {
|
static void on_core_error_cb(void *user_data, uint32_t id, int seq, int res, const char *message) {
|
||||||
gsr_pipewire *self = user_data;
|
gsr_pipewire_video *self = user_data;
|
||||||
fprintf(stderr, "gsr error: pipewire: error id:%u seq:%d res:%d: %s\n", id, seq, res, message);
|
fprintf(stderr, "gsr error: pipewire: error id:%u seq:%d res:%d: %s\n", id, seq, res, message);
|
||||||
pw_thread_loop_signal(self->thread_loop, false);
|
pw_thread_loop_signal(self->thread_loop, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void on_core_done_cb(void *user_data, uint32_t id, int seq) {
|
static void on_core_done_cb(void *user_data, uint32_t id, int seq) {
|
||||||
gsr_pipewire *self = user_data;
|
gsr_pipewire_video *self = user_data;
|
||||||
if (id == PW_ID_CORE && self->server_version_sync == seq)
|
if (id == PW_ID_CORE && self->server_version_sync == seq)
|
||||||
pw_thread_loop_signal(self->thread_loop, false);
|
pw_thread_loop_signal(self->thread_loop, false);
|
||||||
}
|
}
|
||||||
@@ -86,7 +86,7 @@ static const struct pw_core_events core_events = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static void on_process_cb(void *user_data) {
|
static void on_process_cb(void *user_data) {
|
||||||
gsr_pipewire *self = user_data;
|
gsr_pipewire_video *self = user_data;
|
||||||
struct spa_meta_cursor *cursor = NULL;
|
struct spa_meta_cursor *cursor = NULL;
|
||||||
//struct spa_meta *video_damage = NULL;
|
//struct spa_meta *video_damage = NULL;
|
||||||
|
|
||||||
@@ -122,8 +122,8 @@ static void on_process_cb(void *user_data) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self->dmabuf_num_planes = buffer->n_datas;
|
self->dmabuf_num_planes = buffer->n_datas;
|
||||||
if(self->dmabuf_num_planes > GSR_PIPEWIRE_DMABUF_MAX_PLANES)
|
if(self->dmabuf_num_planes > GSR_PIPEWIRE_VIDEO_DMABUF_MAX_PLANES)
|
||||||
self->dmabuf_num_planes = GSR_PIPEWIRE_DMABUF_MAX_PLANES;
|
self->dmabuf_num_planes = GSR_PIPEWIRE_VIDEO_DMABUF_MAX_PLANES;
|
||||||
|
|
||||||
for(size_t i = 0; i < self->dmabuf_num_planes; ++i) {
|
for(size_t i = 0; i < self->dmabuf_num_planes; ++i) {
|
||||||
self->dmabuf_data[i].fd = dup(buffer->datas[i].fd);
|
self->dmabuf_data[i].fd = dup(buffer->datas[i].fd);
|
||||||
@@ -206,7 +206,7 @@ read_metadata:
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void on_param_changed_cb(void *user_data, uint32_t id, const struct spa_pod *param) {
|
static void on_param_changed_cb(void *user_data, uint32_t id, const struct spa_pod *param) {
|
||||||
gsr_pipewire *self = user_data;
|
gsr_pipewire_video *self = user_data;
|
||||||
|
|
||||||
if (!param || id != SPA_PARAM_Format)
|
if (!param || id != SPA_PARAM_Format)
|
||||||
return;
|
return;
|
||||||
@@ -276,7 +276,7 @@ static void on_param_changed_cb(void *user_data, uint32_t id, const struct spa_p
|
|||||||
|
|
||||||
static void on_state_changed_cb(void *user_data, enum pw_stream_state old, enum pw_stream_state state, const char *error) {
|
static void on_state_changed_cb(void *user_data, enum pw_stream_state old, enum pw_stream_state state, const char *error) {
|
||||||
(void)old;
|
(void)old;
|
||||||
gsr_pipewire *self = user_data;
|
gsr_pipewire_video *self = user_data;
|
||||||
|
|
||||||
fprintf(stderr, "gsr info: pipewire: stream %p state: \"%s\" (error: %s)\n",
|
fprintf(stderr, "gsr info: pipewire: stream %p state: \"%s\" (error: %s)\n",
|
||||||
(void*)self->stream, pw_stream_state_as_string(state),
|
(void*)self->stream, pw_stream_state_as_string(state),
|
||||||
@@ -291,7 +291,7 @@ static const struct pw_stream_events stream_events = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static inline struct spa_pod *build_format(struct spa_pod_builder *b,
|
static inline struct spa_pod *build_format(struct spa_pod_builder *b,
|
||||||
const gsr_pipewire_video_info *ovi,
|
const gsr_pipewire_video_video_info *ovi,
|
||||||
uint32_t format, const uint64_t *modifiers,
|
uint32_t format, const uint64_t *modifiers,
|
||||||
size_t modifier_count)
|
size_t modifier_count)
|
||||||
{
|
{
|
||||||
@@ -358,13 +358,13 @@ static const enum spa_video_format video_formats[] = {
|
|||||||
SPA_VIDEO_FORMAT_RGB,
|
SPA_VIDEO_FORMAT_RGB,
|
||||||
};
|
};
|
||||||
|
|
||||||
static bool gsr_pipewire_build_format_params(gsr_pipewire *self, struct spa_pod_builder *pod_builder, struct spa_pod **params, uint32_t *num_params) {
|
static bool gsr_pipewire_video_build_format_params(gsr_pipewire_video *self, struct spa_pod_builder *pod_builder, struct spa_pod **params, uint32_t *num_params) {
|
||||||
*num_params = 0;
|
*num_params = 0;
|
||||||
|
|
||||||
if(!check_pw_version(&self->server_version, 0, 3, 33))
|
if(!check_pw_version(&self->server_version, 0, 3, 33))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
for(size_t i = 0; i < GSR_PIPEWIRE_NUM_VIDEO_FORMATS; i++) {
|
for(size_t i = 0; i < GSR_PIPEWIRE_VIDEO_NUM_VIDEO_FORMATS; i++) {
|
||||||
if(self->supported_video_formats[i].modifiers_size == 0)
|
if(self->supported_video_formats[i].modifiers_size == 0)
|
||||||
continue;
|
continue;
|
||||||
params[i] = build_format(pod_builder, &self->video_info, self->supported_video_formats[i].format, self->modifiers + self->supported_video_formats[i].modifiers_index, self->supported_video_formats[i].modifiers_size);
|
params[i] = build_format(pod_builder, &self->video_info, self->supported_video_formats[i].format, self->modifiers + self->supported_video_formats[i].modifiers_index, self->supported_video_formats[i].modifiers_size);
|
||||||
@@ -376,15 +376,15 @@ static bool gsr_pipewire_build_format_params(gsr_pipewire *self, struct spa_pod_
|
|||||||
|
|
||||||
static void renegotiate_format(void *data, uint64_t expirations) {
|
static void renegotiate_format(void *data, uint64_t expirations) {
|
||||||
(void)expirations;
|
(void)expirations;
|
||||||
gsr_pipewire *self = (gsr_pipewire*)data;
|
gsr_pipewire_video *self = (gsr_pipewire_video*)data;
|
||||||
|
|
||||||
pw_thread_loop_lock(self->thread_loop);
|
pw_thread_loop_lock(self->thread_loop);
|
||||||
|
|
||||||
struct spa_pod *params[GSR_PIPEWIRE_NUM_VIDEO_FORMATS];
|
struct spa_pod *params[GSR_PIPEWIRE_VIDEO_NUM_VIDEO_FORMATS];
|
||||||
uint32_t num_video_formats = 0;
|
uint32_t num_video_formats = 0;
|
||||||
uint8_t params_buffer[2048];
|
uint8_t params_buffer[2048];
|
||||||
struct spa_pod_builder pod_builder = SPA_POD_BUILDER_INIT(params_buffer, sizeof(params_buffer));
|
struct spa_pod_builder pod_builder = SPA_POD_BUILDER_INIT(params_buffer, sizeof(params_buffer));
|
||||||
if (!gsr_pipewire_build_format_params(self, &pod_builder, params, &num_video_formats)) {
|
if (!gsr_pipewire_video_build_format_params(self, &pod_builder, params, &num_video_formats)) {
|
||||||
pw_thread_loop_unlock(self->thread_loop);
|
pw_thread_loop_unlock(self->thread_loop);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -393,7 +393,7 @@ static void renegotiate_format(void *data, uint64_t expirations) {
|
|||||||
pw_thread_loop_unlock(self->thread_loop);
|
pw_thread_loop_unlock(self->thread_loop);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool spa_video_format_get_modifiers(gsr_pipewire *self, const enum spa_video_format format, uint64_t *modifiers, int32_t max_modifiers, int32_t *num_modifiers) {
|
static bool spa_video_format_get_modifiers(gsr_pipewire_video *self, const enum spa_video_format format, uint64_t *modifiers, int32_t max_modifiers, int32_t *num_modifiers) {
|
||||||
*num_modifiers = 0;
|
*num_modifiers = 0;
|
||||||
|
|
||||||
if(max_modifiers == 0) {
|
if(max_modifiers == 0) {
|
||||||
@@ -430,36 +430,36 @@ static bool spa_video_format_get_modifiers(gsr_pipewire *self, const enum spa_vi
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gsr_pipewire_init_modifiers(gsr_pipewire *self) {
|
static void gsr_pipewire_video_init_modifiers(gsr_pipewire_video *self) {
|
||||||
for(size_t i = 0; i < GSR_PIPEWIRE_NUM_VIDEO_FORMATS; i++) {
|
for(size_t i = 0; i < GSR_PIPEWIRE_VIDEO_NUM_VIDEO_FORMATS; i++) {
|
||||||
self->supported_video_formats[i].format = video_formats[i];
|
self->supported_video_formats[i].format = video_formats[i];
|
||||||
int32_t num_modifiers = 0;
|
int32_t num_modifiers = 0;
|
||||||
spa_video_format_get_modifiers(self, self->supported_video_formats[i].format, self->modifiers + self->num_modifiers, GSR_PIPEWIRE_MAX_MODIFIERS - self->num_modifiers, &num_modifiers);
|
spa_video_format_get_modifiers(self, self->supported_video_formats[i].format, self->modifiers + self->num_modifiers, GSR_PIPEWIRE_VIDEO_MAX_MODIFIERS - self->num_modifiers, &num_modifiers);
|
||||||
self->supported_video_formats[i].modifiers_index = self->num_modifiers;
|
self->supported_video_formats[i].modifiers_index = self->num_modifiers;
|
||||||
self->supported_video_formats[i].modifiers_size = num_modifiers;
|
self->supported_video_formats[i].modifiers_size = num_modifiers;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool gsr_pipewire_setup_stream(gsr_pipewire *self) {
|
static bool gsr_pipewire_video_setup_stream(gsr_pipewire_video *self) {
|
||||||
struct spa_pod *params[GSR_PIPEWIRE_NUM_VIDEO_FORMATS];
|
struct spa_pod *params[GSR_PIPEWIRE_VIDEO_NUM_VIDEO_FORMATS];
|
||||||
uint32_t num_video_formats = 0;
|
uint32_t num_video_formats = 0;
|
||||||
uint8_t params_buffer[2048];
|
uint8_t params_buffer[2048];
|
||||||
struct spa_pod_builder pod_builder = SPA_POD_BUILDER_INIT(params_buffer, sizeof(params_buffer));
|
struct spa_pod_builder pod_builder = SPA_POD_BUILDER_INIT(params_buffer, sizeof(params_buffer));
|
||||||
|
|
||||||
self->thread_loop = pw_thread_loop_new("PipeWire thread loop", NULL);
|
self->thread_loop = pw_thread_loop_new("PipeWire thread loop", NULL);
|
||||||
if(!self->thread_loop) {
|
if(!self->thread_loop) {
|
||||||
fprintf(stderr, "gsr error: gsr_pipewire_setup_stream: failed to create pipewire thread\n");
|
fprintf(stderr, "gsr error: gsr_pipewire_video_setup_stream: failed to create pipewire thread\n");
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
self->context = pw_context_new(pw_thread_loop_get_loop(self->thread_loop), NULL, 0);
|
self->context = pw_context_new(pw_thread_loop_get_loop(self->thread_loop), NULL, 0);
|
||||||
if(!self->context) {
|
if(!self->context) {
|
||||||
fprintf(stderr, "gsr error: gsr_pipewire_setup_stream: failed to create pipewire context\n");
|
fprintf(stderr, "gsr error: gsr_pipewire_video_setup_stream: failed to create pipewire context\n");
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(pw_thread_loop_start(self->thread_loop) < 0) {
|
if(pw_thread_loop_start(self->thread_loop) < 0) {
|
||||||
fprintf(stderr, "gsr error: gsr_pipewire_setup_stream: failed to start thread\n");
|
fprintf(stderr, "gsr error: gsr_pipewire_video_setup_stream: failed to start thread\n");
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -469,20 +469,20 @@ static bool gsr_pipewire_setup_stream(gsr_pipewire *self) {
|
|||||||
self->core = pw_context_connect_fd(self->context, fcntl(self->fd, F_DUPFD_CLOEXEC, 5), NULL, 0);
|
self->core = pw_context_connect_fd(self->context, fcntl(self->fd, F_DUPFD_CLOEXEC, 5), NULL, 0);
|
||||||
if(!self->core) {
|
if(!self->core) {
|
||||||
pw_thread_loop_unlock(self->thread_loop);
|
pw_thread_loop_unlock(self->thread_loop);
|
||||||
fprintf(stderr, "gsr error: gsr_pipewire_setup_stream: failed to connect to fd %d\n", self->fd);
|
fprintf(stderr, "gsr error: gsr_pipewire_video_setup_stream: failed to connect to fd %d\n", self->fd);
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Error check
|
// TODO: Error check
|
||||||
pw_core_add_listener(self->core, &self->core_listener, &core_events, self);
|
pw_core_add_listener(self->core, &self->core_listener, &core_events, self);
|
||||||
|
|
||||||
gsr_pipewire_init_modifiers(self);
|
gsr_pipewire_video_init_modifiers(self);
|
||||||
|
|
||||||
// TODO: Cleanup?
|
// TODO: Cleanup?
|
||||||
self->reneg = pw_loop_add_event(pw_thread_loop_get_loop(self->thread_loop), renegotiate_format, self);
|
self->reneg = pw_loop_add_event(pw_thread_loop_get_loop(self->thread_loop), renegotiate_format, self);
|
||||||
if(!self->reneg) {
|
if(!self->reneg) {
|
||||||
pw_thread_loop_unlock(self->thread_loop);
|
pw_thread_loop_unlock(self->thread_loop);
|
||||||
fprintf(stderr, "gsr error: gsr_pipewire_setup_stream: pw_loop_add_event failed\n");
|
fprintf(stderr, "gsr error: gsr_pipewire_video_setup_stream: pw_loop_add_event failed\n");
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -495,14 +495,14 @@ static bool gsr_pipewire_setup_stream(gsr_pipewire *self) {
|
|||||||
PW_KEY_MEDIA_ROLE, "Screen", NULL));
|
PW_KEY_MEDIA_ROLE, "Screen", NULL));
|
||||||
if(!self->stream) {
|
if(!self->stream) {
|
||||||
pw_thread_loop_unlock(self->thread_loop);
|
pw_thread_loop_unlock(self->thread_loop);
|
||||||
fprintf(stderr, "gsr error: gsr_pipewire_setup_stream: failed to create stream\n");
|
fprintf(stderr, "gsr error: gsr_pipewire_video_setup_stream: failed to create stream\n");
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
pw_stream_add_listener(self->stream, &self->stream_listener, &stream_events, self);
|
pw_stream_add_listener(self->stream, &self->stream_listener, &stream_events, self);
|
||||||
|
|
||||||
if(!gsr_pipewire_build_format_params(self, &pod_builder, params, &num_video_formats)) {
|
if(!gsr_pipewire_video_build_format_params(self, &pod_builder, params, &num_video_formats)) {
|
||||||
pw_thread_loop_unlock(self->thread_loop);
|
pw_thread_loop_unlock(self->thread_loop);
|
||||||
fprintf(stderr, "gsr error: gsr_pipewire_setup_stream: failed to build format params\n");
|
fprintf(stderr, "gsr error: gsr_pipewire_video_setup_stream: failed to build format params\n");
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -512,7 +512,7 @@ static bool gsr_pipewire_setup_stream(gsr_pipewire *self) {
|
|||||||
num_video_formats) < 0)
|
num_video_formats) < 0)
|
||||||
{
|
{
|
||||||
pw_thread_loop_unlock(self->thread_loop);
|
pw_thread_loop_unlock(self->thread_loop);
|
||||||
fprintf(stderr, "gsr error: gsr_pipewire_setup_stream: failed to connect stream\n");
|
fprintf(stderr, "gsr error: gsr_pipewire_video_setup_stream: failed to connect stream\n");
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -549,7 +549,7 @@ static bool gsr_pipewire_setup_stream(gsr_pipewire *self) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int pw_init_counter = 0;
|
static int pw_init_counter = 0;
|
||||||
bool gsr_pipewire_init(gsr_pipewire *self, int pipewire_fd, uint32_t pipewire_node, int fps, bool capture_cursor, gsr_egl *egl) {
|
bool gsr_pipewire_video_init(gsr_pipewire_video *self, int pipewire_fd, uint32_t pipewire_node, int fps, bool capture_cursor, gsr_egl *egl) {
|
||||||
if(pw_init_counter == 0)
|
if(pw_init_counter == 0)
|
||||||
pw_init(NULL, NULL);
|
pw_init(NULL, NULL);
|
||||||
++pw_init_counter;
|
++pw_init_counter;
|
||||||
@@ -559,8 +559,8 @@ bool gsr_pipewire_init(gsr_pipewire *self, int pipewire_fd, uint32_t pipewire_no
|
|||||||
self->fd = pipewire_fd;
|
self->fd = pipewire_fd;
|
||||||
self->node = pipewire_node;
|
self->node = pipewire_node;
|
||||||
if(pthread_mutex_init(&self->mutex, NULL) != 0) {
|
if(pthread_mutex_init(&self->mutex, NULL) != 0) {
|
||||||
fprintf(stderr, "gsr error: gsr_pipewire_init: failed to initialize mutex\n");
|
fprintf(stderr, "gsr error: gsr_pipewire_video_init: failed to initialize mutex\n");
|
||||||
gsr_pipewire_deinit(self);
|
gsr_pipewire_video_deinit(self);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
self->mutex_initialized = true;
|
self->mutex_initialized = true;
|
||||||
@@ -568,15 +568,15 @@ bool gsr_pipewire_init(gsr_pipewire *self, int pipewire_fd, uint32_t pipewire_no
|
|||||||
self->video_info.fps_den = 1;
|
self->video_info.fps_den = 1;
|
||||||
self->cursor.visible = capture_cursor;
|
self->cursor.visible = capture_cursor;
|
||||||
|
|
||||||
if(!gsr_pipewire_setup_stream(self)) {
|
if(!gsr_pipewire_video_setup_stream(self)) {
|
||||||
gsr_pipewire_deinit(self);
|
gsr_pipewire_video_deinit(self);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void gsr_pipewire_deinit(gsr_pipewire *self) {
|
void gsr_pipewire_video_deinit(gsr_pipewire_video *self) {
|
||||||
if(self->thread_loop) {
|
if(self->thread_loop) {
|
||||||
//pw_thread_loop_wait(self->thread_loop);
|
//pw_thread_loop_wait(self->thread_loop);
|
||||||
pw_thread_loop_stop(self->thread_loop);
|
pw_thread_loop_stop(self->thread_loop);
|
||||||
@@ -636,7 +636,7 @@ void gsr_pipewire_deinit(gsr_pipewire *self) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static EGLImage gsr_pipewire_create_egl_image(gsr_pipewire *self, const int *fds, const uint32_t *offsets, const uint32_t *pitches, const uint64_t *modifiers, bool use_modifiers) {
|
static EGLImage gsr_pipewire_video_create_egl_image(gsr_pipewire_video *self, const int *fds, const uint32_t *offsets, const uint32_t *pitches, const uint64_t *modifiers, bool use_modifiers) {
|
||||||
intptr_t img_attr[44];
|
intptr_t img_attr[44];
|
||||||
setup_dma_buf_attrs(img_attr, spa_video_format_to_drm_format(self->format.info.raw.format), self->format.info.raw.size.width, self->format.info.raw.size.height,
|
setup_dma_buf_attrs(img_attr, spa_video_format_to_drm_format(self->format.info.raw.format), self->format.info.raw.size.width, self->format.info.raw.size.height,
|
||||||
fds, offsets, pitches, modifiers, self->dmabuf_num_planes, use_modifiers);
|
fds, offsets, pitches, modifiers, self->dmabuf_num_planes, use_modifiers);
|
||||||
@@ -650,11 +650,11 @@ static EGLImage gsr_pipewire_create_egl_image(gsr_pipewire *self, const int *fds
|
|||||||
return image;
|
return image;
|
||||||
}
|
}
|
||||||
|
|
||||||
static EGLImage gsr_pipewire_create_egl_image_with_fallback(gsr_pipewire *self) {
|
static EGLImage gsr_pipewire_video_create_egl_image_with_fallback(gsr_pipewire_video *self) {
|
||||||
int fds[GSR_PIPEWIRE_DMABUF_MAX_PLANES];
|
int fds[GSR_PIPEWIRE_VIDEO_DMABUF_MAX_PLANES];
|
||||||
uint32_t offsets[GSR_PIPEWIRE_DMABUF_MAX_PLANES];
|
uint32_t offsets[GSR_PIPEWIRE_VIDEO_DMABUF_MAX_PLANES];
|
||||||
uint32_t pitches[GSR_PIPEWIRE_DMABUF_MAX_PLANES];
|
uint32_t pitches[GSR_PIPEWIRE_VIDEO_DMABUF_MAX_PLANES];
|
||||||
uint64_t modifiers[GSR_PIPEWIRE_DMABUF_MAX_PLANES];
|
uint64_t modifiers[GSR_PIPEWIRE_VIDEO_DMABUF_MAX_PLANES];
|
||||||
for(size_t i = 0; i < self->dmabuf_num_planes; ++i) {
|
for(size_t i = 0; i < self->dmabuf_num_planes; ++i) {
|
||||||
fds[i] = self->dmabuf_data[i].fd;
|
fds[i] = self->dmabuf_data[i].fd;
|
||||||
offsets[i] = self->dmabuf_data[i].offset;
|
offsets[i] = self->dmabuf_data[i].offset;
|
||||||
@@ -664,19 +664,19 @@ static EGLImage gsr_pipewire_create_egl_image_with_fallback(gsr_pipewire *self)
|
|||||||
|
|
||||||
EGLImage image = NULL;
|
EGLImage image = NULL;
|
||||||
if(self->no_modifiers_fallback) {
|
if(self->no_modifiers_fallback) {
|
||||||
image = gsr_pipewire_create_egl_image(self, fds, offsets, pitches, modifiers, false);
|
image = gsr_pipewire_video_create_egl_image(self, fds, offsets, pitches, modifiers, false);
|
||||||
} else {
|
} else {
|
||||||
image = gsr_pipewire_create_egl_image(self, fds, offsets, pitches, modifiers, true);
|
image = gsr_pipewire_video_create_egl_image(self, fds, offsets, pitches, modifiers, true);
|
||||||
if(!image) {
|
if(!image) {
|
||||||
fprintf(stderr, "gsr error: gsr_pipewire_create_egl_image_with_fallback: failed to create egl image with modifiers, trying without modifiers\n");
|
fprintf(stderr, "gsr error: gsr_pipewire_video_create_egl_image_with_fallback: failed to create egl image with modifiers, trying without modifiers\n");
|
||||||
self->no_modifiers_fallback = true;
|
self->no_modifiers_fallback = true;
|
||||||
image = gsr_pipewire_create_egl_image(self, fds, offsets, pitches, modifiers, false);
|
image = gsr_pipewire_video_create_egl_image(self, fds, offsets, pitches, modifiers, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return image;
|
return image;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool gsr_pipewire_bind_image_to_texture(gsr_pipewire *self, EGLImage image, unsigned int texture_id, bool external_texture) {
|
static bool gsr_pipewire_video_bind_image_to_texture(gsr_pipewire_video *self, EGLImage image, unsigned int texture_id, bool external_texture) {
|
||||||
const int texture_target = external_texture ? GL_TEXTURE_EXTERNAL_OES : GL_TEXTURE_2D;
|
const int texture_target = external_texture ? GL_TEXTURE_EXTERNAL_OES : GL_TEXTURE_2D;
|
||||||
while(self->egl->glGetError() != 0){}
|
while(self->egl->glGetError() != 0){}
|
||||||
self->egl->glBindTexture(texture_target, texture_id);
|
self->egl->glBindTexture(texture_target, texture_id);
|
||||||
@@ -686,19 +686,19 @@ static bool gsr_pipewire_bind_image_to_texture(gsr_pipewire *self, EGLImage imag
|
|||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gsr_pipewire_bind_image_to_texture_with_fallback(gsr_pipewire *self, gsr_texture_map texture_map, EGLImage image) {
|
static void gsr_pipewire_video_bind_image_to_texture_with_fallback(gsr_pipewire_video *self, gsr_texture_map texture_map, EGLImage image) {
|
||||||
if(self->external_texture_fallback) {
|
if(self->external_texture_fallback) {
|
||||||
gsr_pipewire_bind_image_to_texture(self, image, texture_map.external_texture_id, true);
|
gsr_pipewire_video_bind_image_to_texture(self, image, texture_map.external_texture_id, true);
|
||||||
} else {
|
} else {
|
||||||
if(!gsr_pipewire_bind_image_to_texture(self, image, texture_map.texture_id, false)) {
|
if(!gsr_pipewire_video_bind_image_to_texture(self, image, texture_map.texture_id, false)) {
|
||||||
fprintf(stderr, "gsr error: gsr_pipewire_map_texture: failed to bind image to texture, trying with external texture\n");
|
fprintf(stderr, "gsr error: gsr_pipewire_video_map_texture: failed to bind image to texture, trying with external texture\n");
|
||||||
self->external_texture_fallback = true;
|
self->external_texture_fallback = true;
|
||||||
gsr_pipewire_bind_image_to_texture(self, image, texture_map.external_texture_id, true);
|
gsr_pipewire_video_bind_image_to_texture(self, image, texture_map.external_texture_id, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gsr_pipewire_update_cursor_texture(gsr_pipewire *self, gsr_texture_map texture_map) {
|
static void gsr_pipewire_video_update_cursor_texture(gsr_pipewire_video *self, gsr_texture_map texture_map) {
|
||||||
if(!self->cursor.data)
|
if(!self->cursor.data)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -715,9 +715,9 @@ static void gsr_pipewire_update_cursor_texture(gsr_pipewire *self, gsr_texture_m
|
|||||||
self->cursor.data = NULL;
|
self->cursor.data = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool gsr_pipewire_map_texture(gsr_pipewire *self, gsr_texture_map texture_map, gsr_pipewire_region *region, gsr_pipewire_region *cursor_region, gsr_pipewire_dmabuf_data *dmabuf_data, int *num_dmabuf_data, uint32_t *fourcc, uint64_t *modifiers, bool *using_external_image) {
|
bool gsr_pipewire_video_map_texture(gsr_pipewire_video *self, gsr_texture_map texture_map, gsr_pipewire_video_region *region, gsr_pipewire_video_region *cursor_region, gsr_pipewire_video_dmabuf_data *dmabuf_data, int *num_dmabuf_data, uint32_t *fourcc, uint64_t *modifiers, bool *using_external_image) {
|
||||||
for(int i = 0; i < GSR_PIPEWIRE_DMABUF_MAX_PLANES; ++i) {
|
for(int i = 0; i < GSR_PIPEWIRE_VIDEO_DMABUF_MAX_PLANES; ++i) {
|
||||||
memset(&dmabuf_data[i], 0, sizeof(gsr_pipewire_dmabuf_data));
|
memset(&dmabuf_data[i], 0, sizeof(gsr_pipewire_video_dmabuf_data));
|
||||||
}
|
}
|
||||||
*num_dmabuf_data = 0;
|
*num_dmabuf_data = 0;
|
||||||
*using_external_image = self->external_texture_fallback;
|
*using_external_image = self->external_texture_fallback;
|
||||||
@@ -730,14 +730,14 @@ bool gsr_pipewire_map_texture(gsr_pipewire *self, gsr_texture_map texture_map, g
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
EGLImage image = gsr_pipewire_create_egl_image_with_fallback(self);
|
EGLImage image = gsr_pipewire_video_create_egl_image_with_fallback(self);
|
||||||
if(image) {
|
if(image) {
|
||||||
gsr_pipewire_bind_image_to_texture_with_fallback(self, texture_map, image);
|
gsr_pipewire_video_bind_image_to_texture_with_fallback(self, texture_map, image);
|
||||||
*using_external_image = self->external_texture_fallback;
|
*using_external_image = self->external_texture_fallback;
|
||||||
self->egl->eglDestroyImage(self->egl->egl_display, image);
|
self->egl->eglDestroyImage(self->egl->egl_display, image);
|
||||||
}
|
}
|
||||||
|
|
||||||
gsr_pipewire_update_cursor_texture(self, texture_map);
|
gsr_pipewire_video_update_cursor_texture(self, texture_map);
|
||||||
|
|
||||||
region->x = 0;
|
region->x = 0;
|
||||||
region->y = 0;
|
region->y = 0;
|
||||||
@@ -773,7 +773,7 @@ bool gsr_pipewire_map_texture(gsr_pipewire *self, gsr_texture_map texture_map, g
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool gsr_pipewire_is_damaged(gsr_pipewire *self) {
|
bool gsr_pipewire_video_is_damaged(gsr_pipewire_video *self) {
|
||||||
bool damaged = false;
|
bool damaged = false;
|
||||||
pthread_mutex_lock(&self->mutex);
|
pthread_mutex_lock(&self->mutex);
|
||||||
damaged = self->damaged;
|
damaged = self->damaged;
|
||||||
@@ -781,7 +781,7 @@ bool gsr_pipewire_is_damaged(gsr_pipewire *self) {
|
|||||||
return damaged;
|
return damaged;
|
||||||
}
|
}
|
||||||
|
|
||||||
void gsr_pipewire_clear_damage(gsr_pipewire *self) {
|
void gsr_pipewire_video_clear_damage(gsr_pipewire_video *self) {
|
||||||
pthread_mutex_lock(&self->mutex);
|
pthread_mutex_lock(&self->mutex);
|
||||||
self->damaged = false;
|
self->damaged = false;
|
||||||
pthread_mutex_unlock(&self->mutex);
|
pthread_mutex_unlock(&self->mutex);
|
||||||
Reference in New Issue
Block a user