mirror of
https://repo.dec05eba.com/gpu-screen-recorder
synced 2026-04-04 18:46:37 +09:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c11dd77c44 | ||
|
|
cb8104d107 | ||
|
|
7b64f10eaf | ||
|
|
037b215b44 | ||
|
|
8cf1a98c33 |
1
TODO
1
TODO
@@ -330,7 +330,6 @@ Set top level window argument for portal capture. Same for gpu-screen-recorder-g
|
||||
Remove unix domain socket code from kms-client/server and use socketpair directly. To make this possible always execute the kms server permission setup in flatpak, before starting recording (in gpu-screen-recorder-gtk).
|
||||
|
||||
Application audio capture isn't good enough. It creates a sink that for some automatically gets selected as the default output device and it's visible as an output device.
|
||||
When shutting down gpu screen recorder it will also cause audio applications to pause.
|
||||
Fix some of these issues by setting gsr-app-sink media class to "Stream/Input/Audio" and node.virtual=true.
|
||||
However that causes pulseaudio to be unable to record from gsr-app-sink, and it ends up being stuck in pa_sound_device_handle_reconnect in the loop with pa_mainloop_iterate.
|
||||
|
||||
|
||||
@@ -97,6 +97,8 @@ typedef struct {
|
||||
struct pw_proxy **virtual_sink_proxies;
|
||||
size_t num_virtual_sink_proxies;
|
||||
size_t virtual_sink_proxies_capacity_items;
|
||||
|
||||
bool running;
|
||||
} gsr_pipewire_audio;
|
||||
|
||||
bool gsr_pipewire_audio_init(gsr_pipewire_audio *self);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
project('gpu-screen-recorder', ['c', 'cpp'], version : '5.7.0', default_options : ['warning_level=2'])
|
||||
project('gpu-screen-recorder', ['c', 'cpp'], version : '5.7.3', default_options : ['warning_level=2'])
|
||||
|
||||
add_project_arguments('-Wshadow', language : ['c', 'cpp'])
|
||||
if get_option('buildtype') == 'debug'
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "gpu-screen-recorder"
|
||||
type = "executable"
|
||||
version = "5.7.0"
|
||||
version = "5.7.3"
|
||||
platforms = ["posix"]
|
||||
|
||||
[config]
|
||||
|
||||
@@ -622,7 +622,6 @@ static int gsr_capture_kms_capture(gsr_capture *cap, gsr_capture_metadata *captu
|
||||
gsr_kms_set_hdr_metadata(self, drm_fd);
|
||||
|
||||
self->capture_size = rotate_capture_size_if_rotated(self, (vec2i){ drm_fd->crtc_w, drm_fd->crtc_h });
|
||||
const vec2i original_frame_size = self->capture_size;
|
||||
if(self->params.region_size.x > 0 && self->params.region_size.y > 0)
|
||||
self->capture_size = self->params.region_size;
|
||||
|
||||
@@ -654,7 +653,7 @@ static int gsr_capture_kms_capture(gsr_capture *cap, gsr_capture_metadata *captu
|
||||
|
||||
gsr_color_conversion_draw(color_conversion, self->external_texture_fallback ? self->external_input_texture_id : self->input_texture_id,
|
||||
target_pos, output_size,
|
||||
capture_pos, self->capture_size, original_frame_size,
|
||||
capture_pos, self->capture_size, (vec2i){ drm_fd->width, drm_fd->height },
|
||||
gsr_monitor_rotation_to_rotation(rotation), GSR_SOURCE_COLOR_RGB, self->external_texture_fallback, false);
|
||||
|
||||
if(self->params.record_cursor) {
|
||||
|
||||
@@ -87,7 +87,7 @@ static vec2i profile_entrypoint_get_max_resolution(VADisplay va_dpy, VAProfile p
|
||||
.type = VAConfigAttribMaxPictureHeight,
|
||||
}
|
||||
};
|
||||
if(vaGetConfigAttributes(va_dpy, profile, entrypoint, attribs, 2) != VA_STATUS_SUCCESS)
|
||||
if(vaGetConfigAttributes(va_dpy, profile, entrypoint, attribs, 2) != VA_STATUS_SUCCESS || (attribs[0].value & VA_ATTRIB_NOT_SUPPORTED) || (attribs[1].value & VA_ATTRIB_NOT_SUPPORTED))
|
||||
return (vec2i){0, 0};
|
||||
|
||||
return (vec2i){ attribs[0].value, attribs[1].value };
|
||||
|
||||
@@ -476,7 +476,10 @@ static void gsr_color_conversion_swizzle_reset(gsr_color_conversion *self, gsr_s
|
||||
}
|
||||
}
|
||||
|
||||
static void gsr_color_conversion_draw_graphics(gsr_color_conversion *self, unsigned int texture_id, bool external_texture, float rotation_matrix[2][2], vec2i source_position, vec2i source_size, vec2i destination_pos, vec2i texture_size, vec2f scale, gsr_source_color source_color) {
|
||||
static void gsr_color_conversion_draw_graphics(gsr_color_conversion *self, unsigned int texture_id, bool external_texture, gsr_rotation rotation, float rotation_matrix[2][2], vec2i source_position, vec2i source_size, vec2i destination_pos, vec2i texture_size, vec2f scale, gsr_source_color source_color) {
|
||||
if(source_size.x == 0 || source_size.y == 0)
|
||||
return;
|
||||
|
||||
const vec2i dest_texture_size = self->params.destination_textures_size[0];
|
||||
const int texture_target = external_texture ? GL_TEXTURE_EXTERNAL_OES : GL_TEXTURE_2D;
|
||||
|
||||
@@ -498,11 +501,18 @@ static void gsr_color_conversion_draw_graphics(gsr_color_conversion *self, unsig
|
||||
(float)source_position.y / (texture_size.y == 0 ? 1.0f : (float)texture_size.y),
|
||||
};
|
||||
|
||||
const vec2f texture_size_norm = {
|
||||
vec2f texture_size_norm = {
|
||||
(float)source_size.x / (texture_size.x == 0 ? 1.0f : (float)texture_size.x),
|
||||
(float)source_size.y / (texture_size.y == 0 ? 1.0f : (float)texture_size.y),
|
||||
};
|
||||
|
||||
if(rotation == GSR_ROT_90 || rotation == GSR_ROT_270) {
|
||||
const float ratio_x = (double)source_size.x / (double)source_size.y;
|
||||
const float ratio_y = (double)source_size.y / (double)source_size.x;
|
||||
texture_size_norm.x *= ratio_y;
|
||||
texture_size_norm.y *= ratio_x;
|
||||
}
|
||||
|
||||
const float vertices[] = {
|
||||
-1.0f + 0.0f, -1.0f + 0.0f + size_norm.y, texture_pos_norm.x, texture_pos_norm.y + texture_size_norm.y,
|
||||
-1.0f + 0.0f, -1.0f + 0.0f, texture_pos_norm.x, texture_pos_norm.y,
|
||||
@@ -585,7 +595,7 @@ void gsr_color_conversion_draw(gsr_color_conversion *self, unsigned int texture_
|
||||
|
||||
source_position.x += source_pos.x;
|
||||
source_position.y += source_pos.y;
|
||||
gsr_color_conversion_draw_graphics(self, texture_id, external_texture, rotation_matrix, source_position, source_size, destination_pos, texture_size, scale, source_color);
|
||||
gsr_color_conversion_draw_graphics(self, texture_id, external_texture, rotation, rotation_matrix, source_position, source_size, destination_pos, texture_size, scale, source_color);
|
||||
|
||||
self->params.egl->glFlush();
|
||||
// TODO: Use the minimal barrier required
|
||||
|
||||
@@ -2701,6 +2701,8 @@ static vec2i codec_get_max_resolution(gsr_video_codec video_codec, bool use_soft
|
||||
}
|
||||
|
||||
static bool codec_supports_resolution(vec2i codec_max_resolution, vec2i capture_resolution) {
|
||||
if(codec_max_resolution.x == 0 || codec_max_resolution.y == 0)
|
||||
return true;
|
||||
return codec_max_resolution.x >= capture_resolution.x && codec_max_resolution.y >= capture_resolution.y;
|
||||
}
|
||||
|
||||
|
||||
@@ -392,12 +392,12 @@ static void registry_event_global(void *data, uint32_t id, uint32_t permissions,
|
||||
const struct spa_dict *props)
|
||||
{
|
||||
//fprintf(stderr, "add: id: %d, type: %s\n", (int)id, type);
|
||||
if(!props || !type)
|
||||
gsr_pipewire_audio *self = (gsr_pipewire_audio*)data;
|
||||
if(!props || !type || !self->running)
|
||||
return;
|
||||
|
||||
//pw_properties_new_dict(props);
|
||||
|
||||
gsr_pipewire_audio *self = (gsr_pipewire_audio*)data;
|
||||
if(strcmp(type, PW_TYPE_INTERFACE_Node) == 0) {
|
||||
const char *node_name = spa_dict_lookup(props, PW_KEY_NODE_NAME);
|
||||
const char *media_class = spa_dict_lookup(props, PW_KEY_MEDIA_CLASS);
|
||||
@@ -547,6 +547,7 @@ static const struct pw_registry_events registry_events = {
|
||||
|
||||
bool gsr_pipewire_audio_init(gsr_pipewire_audio *self) {
|
||||
memset(self, 0, sizeof(*self));
|
||||
self->running = true;
|
||||
|
||||
pw_init(NULL, NULL);
|
||||
|
||||
@@ -594,8 +595,49 @@ bool gsr_pipewire_audio_init(gsr_pipewire_audio *self) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static gsr_pipewire_audio_link* gsr_pipewire_audio_get_first_link_to_node(gsr_pipewire_audio *self, uint32_t node_id) {
|
||||
for(size_t i = 0; i < self->num_links; ++i) {
|
||||
if(self->links[i].input_node_id == node_id)
|
||||
return &self->links[i];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void gsr_pipewire_audio_destroy_requested_links(gsr_pipewire_audio *self) {
|
||||
pw_thread_loop_lock(self->thread_loop);
|
||||
|
||||
self->server_version_sync = pw_core_sync(self->core, PW_ID_CORE, self->server_version_sync);
|
||||
pw_thread_loop_wait(self->thread_loop);
|
||||
|
||||
for(size_t requested_link_index = 0; requested_link_index < self->num_requested_links; ++requested_link_index) {
|
||||
const gsr_pipewire_audio_node_type requested_link_node_type = self->requested_links[requested_link_index].input_type == GSR_PIPEWIRE_AUDIO_LINK_INPUT_TYPE_STREAM ? GSR_PIPEWIRE_AUDIO_NODE_TYPE_STREAM_INPUT : GSR_PIPEWIRE_AUDIO_NODE_TYPE_SINK_OR_SOURCE;
|
||||
const gsr_pipewire_audio_node *stream_input_node = gsr_pipewire_audio_get_node_by_name_case_insensitive(self, self->requested_links[requested_link_index].input_name, requested_link_node_type);
|
||||
if(!stream_input_node)
|
||||
continue;
|
||||
|
||||
for(;;) {
|
||||
gsr_pipewire_audio_link *link = gsr_pipewire_audio_get_first_link_to_node(self, stream_input_node->id);
|
||||
if(!link)
|
||||
break;
|
||||
|
||||
pw_registry_destroy(self->registry, link->id);
|
||||
|
||||
self->server_version_sync = pw_core_sync(self->core, PW_ID_CORE, self->server_version_sync);
|
||||
pw_thread_loop_wait(self->thread_loop);
|
||||
|
||||
usleep(10 * 1000);
|
||||
}
|
||||
}
|
||||
|
||||
pw_thread_loop_unlock(self->thread_loop);
|
||||
}
|
||||
|
||||
void gsr_pipewire_audio_deinit(gsr_pipewire_audio *self) {
|
||||
self->running = false;
|
||||
|
||||
if(self->thread_loop) {
|
||||
/* We need to manually destroy links first, otherwise the linked audio sources will be paused when closing the program */
|
||||
gsr_pipewire_audio_destroy_requested_links(self);
|
||||
//pw_thread_loop_wait(self->thread_loop);
|
||||
pw_thread_loop_stop(self->thread_loop);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user