mirror of
https://repo.dec05eba.com/gpu-screen-recorder
synced 2026-04-06 03:26:38 +09:00
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f9ab3ebd65 | ||
|
|
ab5988a2bb | ||
|
|
95c6fc84ea | ||
|
|
a10b882e82 | ||
|
|
92f4bd5f95 | ||
|
|
cc43ca0336 | ||
|
|
a3e1b2a896 | ||
|
|
80f0e483a4 | ||
|
|
739fd9cd72 |
4
TODO
4
TODO
@@ -61,8 +61,6 @@ Remove follow focused option.
|
||||
|
||||
Exit if X11/Wayland killed (if drm plane dead or something?)
|
||||
|
||||
Use SRC_W and SRC_H for screen plane instead of crtc_w and crtc_h.
|
||||
|
||||
Test if p2 state can be worked around by using pure nvenc api and overwriting cuInit/cuCtxCreate* to not do anything. Cuda might be loaded when using nvenc but it might not be used, with certain record options? (such as h264 p5).
|
||||
nvenc uses cuda when using b frames and rgb->yuv conversion, so convert the image ourselves instead.-
|
||||
|
||||
@@ -81,8 +79,6 @@ When vulkan encode is added, mention minimum nvidia driver required. (550.54.14?
|
||||
Investigate if there is a way to do gpu->gpu copy directly without touching system ram to enable video encoding on a different gpu. On nvidia this is possible with cudaMemcpyPeer, but how about from an intel/amd gpu to an nvidia gpu or the other way around or any combination of iGPU and dedicated GPU?
|
||||
Maybe something with clEnqueueMigrateMemObjects? on AMD something with DirectGMA maybe?
|
||||
|
||||
Go back to using pure vaapi without opengl for video encoding? rotation (transpose) can be done if its done after (rgb to yuv) color conversion.
|
||||
|
||||
Use lanczos resampling for better scaling quality. Lanczos resampling can also be used for YUV chroma for better color quality on small text.
|
||||
|
||||
Flac is disabled because the frame sizes are too large which causes big audio/video desync.
|
||||
|
||||
@@ -14,13 +14,17 @@ typedef struct AVContentLightMetadata AVContentLightMetadata;
|
||||
typedef struct gsr_capture gsr_capture;
|
||||
|
||||
typedef struct {
|
||||
int width;
|
||||
int height;
|
||||
// Width and height of the video
|
||||
int video_width;
|
||||
int video_height;
|
||||
// Width and height of the frame at the start of capture, the target size
|
||||
int recording_width;
|
||||
int recording_height;
|
||||
int fps;
|
||||
} gsr_capture_metadata;
|
||||
|
||||
struct gsr_capture {
|
||||
/* These methods should not be called manually. Call gsr_capture_* instead. |capture_metdata->width| and |capture_metadata->height| should be set by this function */
|
||||
/* These methods should not be called manually. Call gsr_capture_* instead. |capture_metadata->video_width| and |capture_metadata->video_height| should be set by this function */
|
||||
int (*start)(gsr_capture *cap, gsr_capture_metadata *capture_metadata);
|
||||
void (*on_event)(gsr_capture *cap, gsr_egl *egl); /* can be NULL */
|
||||
void (*tick)(gsr_capture *cap); /* can be NULL. If there is an event then |on_event| is called before this */
|
||||
|
||||
@@ -10,23 +10,14 @@ typedef enum {
|
||||
GSR_IMAGE_FORMAT_PNG
|
||||
} gsr_image_format;
|
||||
|
||||
typedef enum {
|
||||
GSR_IMAGE_WRITER_SOURCE_OPENGL,
|
||||
GSR_IMAGE_WRITER_SOURCE_MEMORY
|
||||
} gsr_image_writer_source;
|
||||
|
||||
typedef struct {
|
||||
gsr_image_writer_source source;
|
||||
gsr_egl *egl;
|
||||
int width;
|
||||
int height;
|
||||
unsigned int texture;
|
||||
const void *memory; /* Reference */
|
||||
} gsr_image_writer;
|
||||
|
||||
bool gsr_image_writer_init_opengl(gsr_image_writer *self, gsr_egl *egl, int width, int height);
|
||||
/* |memory| is taken as a reference. The data is expected to be in rgba8 format (8 bit rgba) */
|
||||
bool gsr_image_writer_init_memory(gsr_image_writer *self, const void *memory, int width, int height);
|
||||
void gsr_image_writer_deinit(gsr_image_writer *self);
|
||||
|
||||
/* Quality is between 1 and 100 where 100 is the max quality. Quality doesn't apply to lossless formats */
|
||||
|
||||
@@ -59,8 +59,8 @@ struct gsr_kms_response_item {
|
||||
gsr_kms_rotation rotation;
|
||||
int x;
|
||||
int y;
|
||||
int crtc_w;
|
||||
int crtc_h;
|
||||
int src_w;
|
||||
int src_h;
|
||||
struct hdr_output_metadata hdr_metadata;
|
||||
};
|
||||
|
||||
|
||||
@@ -141,21 +141,21 @@ typedef enum {
|
||||
PLANE_PROPERTY_Y = 1 << 1,
|
||||
PLANE_PROPERTY_SRC_X = 1 << 2,
|
||||
PLANE_PROPERTY_SRC_Y = 1 << 3,
|
||||
PLANE_PROPERTY_CRTC_W = 1 << 4,
|
||||
PLANE_PROPERTY_CRTC_H = 1 << 5,
|
||||
PLANE_PROPERTY_SRC_W = 1 << 4,
|
||||
PLANE_PROPERTY_SRC_H = 1 << 5,
|
||||
PLANE_PROPERTY_IS_CURSOR = 1 << 6,
|
||||
PLANE_PROPERTY_IS_PRIMARY = 1 << 7,
|
||||
PLANE_PROPERTY_ROTATION = 1 << 8,
|
||||
} plane_property_mask;
|
||||
|
||||
/* Returns plane_property_mask */
|
||||
static uint32_t plane_get_properties(int drmfd, uint32_t plane_id, int *x, int *y, int *src_x, int *src_y, int *crtc_w, int *crtc_h, gsr_kms_rotation *rotation) {
|
||||
static uint32_t plane_get_properties(int drmfd, uint32_t plane_id, int *x, int *y, int *src_x, int *src_y, int *src_w, int *src_h, gsr_kms_rotation *rotation) {
|
||||
*x = 0;
|
||||
*y = 0;
|
||||
*src_x = 0;
|
||||
*src_y = 0;
|
||||
*crtc_w = 0;
|
||||
*crtc_h = 0;
|
||||
*src_w = 0;
|
||||
*src_h = 0;
|
||||
*rotation = KMS_ROT_0;
|
||||
|
||||
plane_property_mask property_mask = 0;
|
||||
@@ -184,12 +184,12 @@ static uint32_t plane_get_properties(int drmfd, uint32_t plane_id, int *x, int *
|
||||
} else if((type & DRM_MODE_PROP_RANGE) && strcmp(prop->name, "SRC_Y") == 0) {
|
||||
*src_y = (int)(props->prop_values[i] >> 16);
|
||||
property_mask |= PLANE_PROPERTY_SRC_Y;
|
||||
} else if((type & DRM_MODE_PROP_RANGE) && strcmp(prop->name, "CRTC_W") == 0) {
|
||||
*crtc_w = props->prop_values[i];
|
||||
property_mask |= PLANE_PROPERTY_CRTC_W;
|
||||
} else if((type & DRM_MODE_PROP_RANGE) && strcmp(prop->name, "CRTC_H") == 0) {
|
||||
*crtc_h = props->prop_values[i];
|
||||
property_mask |= PLANE_PROPERTY_CRTC_H;
|
||||
} else if((type & DRM_MODE_PROP_RANGE) && strcmp(prop->name, "SRC_W") == 0) {
|
||||
*src_w = (int)(props->prop_values[i] >> 16);
|
||||
property_mask |= PLANE_PROPERTY_SRC_W;
|
||||
} else if((type & DRM_MODE_PROP_RANGE) && strcmp(prop->name, "SRC_H") == 0) {
|
||||
*src_h = (int)(props->prop_values[i] >> 16);
|
||||
property_mask |= PLANE_PROPERTY_SRC_H;
|
||||
} else if((type & DRM_MODE_PROP_ENUM) && strcmp(prop->name, "type") == 0) {
|
||||
const uint64_t current_enum_value = props->prop_values[i];
|
||||
for(int j = 0; j < prop->count_enums; ++j) {
|
||||
@@ -351,9 +351,9 @@ static int kms_get_fb(gsr_drm *drm, gsr_kms_response *response) {
|
||||
// TODO: Check if dimensions have changed by comparing width and height to previous time this was called.
|
||||
// TODO: Support other plane formats than rgb (with multiple planes, such as direct YUV420 on wayland).
|
||||
|
||||
int x = 0, y = 0, src_x = 0, src_y = 0, crtc_w = 0, crtc_h = 0;
|
||||
int x = 0, y = 0, src_x = 0, src_y = 0, src_w = 0, src_h = 0;
|
||||
gsr_kms_rotation rotation = KMS_ROT_0;
|
||||
const uint32_t property_mask = plane_get_properties(drm->drmfd, plane->plane_id, &x, &y, &src_x, &src_y, &crtc_w, &crtc_h, &rotation);
|
||||
const uint32_t property_mask = plane_get_properties(drm->drmfd, plane->plane_id, &x, &y, &src_x, &src_y, &src_w, &src_h, &rotation);
|
||||
if(!(property_mask & PLANE_PROPERTY_IS_PRIMARY) && !(property_mask & PLANE_PROPERTY_IS_CURSOR))
|
||||
continue;
|
||||
|
||||
@@ -392,13 +392,13 @@ static int kms_get_fb(gsr_drm *drm, gsr_kms_response *response) {
|
||||
if(property_mask & PLANE_PROPERTY_IS_CURSOR) {
|
||||
response->items[item_index].x = x;
|
||||
response->items[item_index].y = y;
|
||||
response->items[item_index].crtc_w = 0;
|
||||
response->items[item_index].crtc_h = 0;
|
||||
response->items[item_index].src_w = 0;
|
||||
response->items[item_index].src_h = 0;
|
||||
} else {
|
||||
response->items[item_index].x = src_x;
|
||||
response->items[item_index].y = src_y;
|
||||
response->items[item_index].crtc_w = crtc_w;
|
||||
response->items[item_index].crtc_h = crtc_h;
|
||||
response->items[item_index].src_w = src_w;
|
||||
response->items[item_index].src_h = src_h;
|
||||
}
|
||||
++response->num_items;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
project('gpu-screen-recorder', ['c', 'cpp'], version : '5.8.2', default_options : ['warning_level=2'])
|
||||
project('gpu-screen-recorder', ['c', 'cpp'], version : '5.9.0', 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.8.2"
|
||||
version = "5.9.0"
|
||||
platforms = ["posix"]
|
||||
|
||||
[config]
|
||||
|
||||
@@ -209,14 +209,14 @@ static int gsr_capture_kms_start(gsr_capture *cap, gsr_capture_metadata *capture
|
||||
|
||||
if(self->params.output_resolution.x > 0 && self->params.output_resolution.y > 0) {
|
||||
self->params.output_resolution = scale_keep_aspect_ratio(self->capture_size, self->params.output_resolution);
|
||||
capture_metadata->width = self->params.output_resolution.x;
|
||||
capture_metadata->height = self->params.output_resolution.y;
|
||||
capture_metadata->video_width = self->params.output_resolution.x;
|
||||
capture_metadata->video_height = self->params.output_resolution.y;
|
||||
} else if(self->params.region_size.x > 0 && self->params.region_size.y > 0) {
|
||||
capture_metadata->width = self->params.region_size.x;
|
||||
capture_metadata->height = self->params.region_size.y;
|
||||
capture_metadata->video_width = self->params.region_size.x;
|
||||
capture_metadata->video_height = self->params.region_size.y;
|
||||
} else {
|
||||
capture_metadata->width = self->capture_size.x;
|
||||
capture_metadata->height = self->capture_size.y;
|
||||
capture_metadata->video_width = self->capture_size.x;
|
||||
capture_metadata->video_height = self->capture_size.y;
|
||||
}
|
||||
|
||||
self->last_time_monitor_check = clock_get_monotonic_seconds();
|
||||
@@ -542,7 +542,7 @@ static void render_x11_cursor(gsr_capture_kms *self, gsr_color_conversion *color
|
||||
}
|
||||
|
||||
static void gsr_capture_kms_update_capture_size_change(gsr_capture_kms *self, gsr_color_conversion *color_conversion, vec2i target_pos, const gsr_kms_response_item *drm_fd) {
|
||||
if(target_pos.x != self->prev_target_pos.x || target_pos.y != self->prev_target_pos.y || drm_fd->crtc_w != self->prev_plane_size.x || drm_fd->crtc_h != self->prev_plane_size.y) {
|
||||
if(target_pos.x != self->prev_target_pos.x || target_pos.y != self->prev_target_pos.y || drm_fd->src_w != self->prev_plane_size.x || drm_fd->src_h != self->prev_plane_size.y) {
|
||||
self->prev_target_pos = target_pos;
|
||||
self->prev_plane_size = self->capture_size;
|
||||
gsr_color_conversion_clear(color_conversion);
|
||||
@@ -621,15 +621,12 @@ static int gsr_capture_kms_capture(gsr_capture *cap, gsr_capture_metadata *captu
|
||||
if(drm_fd->has_hdr_metadata && self->params.hdr && hdr_metadata_is_supported_format(&drm_fd->hdr_metadata))
|
||||
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 });
|
||||
self->capture_size = rotate_capture_size_if_rotated(self, (vec2i){ drm_fd->src_w, drm_fd->src_h });
|
||||
if(self->params.region_size.x > 0 && self->params.region_size.y > 0)
|
||||
self->capture_size = self->params.region_size;
|
||||
|
||||
const bool is_scaled = self->params.output_resolution.x > 0 && self->params.output_resolution.y > 0;
|
||||
vec2i output_size = is_scaled ? self->params.output_resolution : self->capture_size;
|
||||
output_size = scale_keep_aspect_ratio(self->capture_size, output_size);
|
||||
|
||||
const vec2i target_pos = { max_int(0, capture_metadata->width / 2 - output_size.x / 2), max_int(0, capture_metadata->height / 2 - output_size.y / 2) };
|
||||
const vec2i output_size = scale_keep_aspect_ratio(self->capture_size, (vec2i){capture_metadata->recording_width, capture_metadata->recording_height});
|
||||
const vec2i target_pos = { max_int(0, capture_metadata->video_width / 2 - output_size.x / 2), max_int(0, capture_metadata->video_height / 2 - output_size.y / 2) };
|
||||
gsr_capture_kms_update_capture_size_change(self, color_conversion, target_pos, drm_fd);
|
||||
|
||||
vec2i capture_pos = self->capture_pos;
|
||||
@@ -649,7 +646,7 @@ static int gsr_capture_kms_capture(gsr_capture *cap, gsr_capture_metadata *captu
|
||||
}
|
||||
|
||||
const gsr_monitor_rotation plane_rotation = kms_rotation_to_gsr_monitor_rotation(drm_fd->rotation);
|
||||
const gsr_monitor_rotation rotation = sub_rotations(self->monitor_rotation, plane_rotation);
|
||||
const gsr_monitor_rotation rotation = capture_is_combined_plane ? GSR_MONITOR_ROT_0 : sub_rotations(self->monitor_rotation, plane_rotation);
|
||||
|
||||
gsr_color_conversion_draw(color_conversion, self->external_texture_fallback ? self->external_input_texture_id : self->input_texture_id,
|
||||
target_pos, output_size,
|
||||
@@ -668,7 +665,7 @@ static int gsr_capture_kms_capture(gsr_capture *cap, gsr_capture_metadata *captu
|
||||
cursor_monitor_offset.y += self->params.region_position.y;
|
||||
render_x11_cursor(self, color_conversion, cursor_monitor_offset, target_pos, output_size);
|
||||
} else if(cursor_drm_fd) {
|
||||
const vec2i framebuffer_size = rotate_capture_size_if_rotated(self, (vec2i){ drm_fd->crtc_w, drm_fd->crtc_h });
|
||||
const vec2i framebuffer_size = rotate_capture_size_if_rotated(self, (vec2i){ drm_fd->src_w, drm_fd->src_h });
|
||||
render_drm_cursor(self, color_conversion, cursor_drm_fd, target_pos, output_size, framebuffer_size);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -283,16 +283,16 @@ static int gsr_capture_nvfbc_start(gsr_capture *cap, gsr_capture_metadata *captu
|
||||
goto error_cleanup;
|
||||
}
|
||||
|
||||
capture_metadata->width = self->tracking_width;
|
||||
capture_metadata->height = self->tracking_height;
|
||||
capture_metadata->video_width = self->tracking_width;
|
||||
capture_metadata->video_height = self->tracking_height;
|
||||
|
||||
if(self->params.output_resolution.x > 0 && self->params.output_resolution.y > 0) {
|
||||
self->params.output_resolution = scale_keep_aspect_ratio((vec2i){capture_metadata->width, capture_metadata->height}, self->params.output_resolution);
|
||||
capture_metadata->width = self->params.output_resolution.x;
|
||||
capture_metadata->height = self->params.output_resolution.y;
|
||||
self->params.output_resolution = scale_keep_aspect_ratio((vec2i){capture_metadata->recording_width, capture_metadata->recording_height}, self->params.output_resolution);
|
||||
capture_metadata->video_width = self->params.output_resolution.x;
|
||||
capture_metadata->video_height = self->params.output_resolution.y;
|
||||
} else if(self->params.region_size.x > 0 && self->params.region_size.y > 0) {
|
||||
capture_metadata->width = self->params.region_size.x;
|
||||
capture_metadata->height = self->params.region_size.y;
|
||||
capture_metadata->video_width = self->params.region_size.x;
|
||||
capture_metadata->video_height = self->params.region_size.y;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -333,11 +333,8 @@ static int gsr_capture_nvfbc_capture(gsr_capture *cap, gsr_capture_metadata *cap
|
||||
if(self->params.region_size.x > 0 && self->params.region_size.y > 0)
|
||||
frame_size = self->params.region_size;
|
||||
|
||||
const bool is_scaled = self->params.output_resolution.x > 0 && self->params.output_resolution.y > 0;
|
||||
vec2i output_size = is_scaled ? self->params.output_resolution : frame_size;
|
||||
output_size = scale_keep_aspect_ratio(frame_size, output_size);
|
||||
|
||||
const vec2i target_pos = { max_int(0, capture_metadata->width / 2 - output_size.x / 2), max_int(0, capture_metadata->height / 2 - output_size.y / 2) };
|
||||
const vec2i output_size = scale_keep_aspect_ratio(frame_size, (vec2i){capture_metadata->recording_width, capture_metadata->recording_height});
|
||||
const vec2i target_pos = { max_int(0, capture_metadata->video_width / 2 - output_size.x / 2), max_int(0, capture_metadata->video_height / 2 - output_size.y / 2) };
|
||||
|
||||
NVFBC_FRAME_GRAB_INFO frame_info;
|
||||
memset(&frame_info, 0, sizeof(frame_info));
|
||||
|
||||
@@ -293,12 +293,12 @@ static int gsr_capture_portal_start(gsr_capture *cap, gsr_capture_metadata *capt
|
||||
}
|
||||
|
||||
if(self->params.output_resolution.x == 0 && self->params.output_resolution.y == 0) {
|
||||
capture_metadata->width = self->capture_size.x;
|
||||
capture_metadata->height = self->capture_size.y;
|
||||
capture_metadata->video_width = self->capture_size.x;
|
||||
capture_metadata->video_height = self->capture_size.y;
|
||||
} else {
|
||||
self->params.output_resolution = scale_keep_aspect_ratio(self->capture_size, self->params.output_resolution);
|
||||
capture_metadata->width = self->params.output_resolution.x;
|
||||
capture_metadata->height = self->params.output_resolution.y;
|
||||
capture_metadata->video_width = self->params.output_resolution.x;
|
||||
capture_metadata->video_height = self->params.output_resolution.y;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -353,11 +353,8 @@ static int gsr_capture_portal_capture(gsr_capture *cap, gsr_capture_metadata *ca
|
||||
}
|
||||
}
|
||||
|
||||
const bool is_scaled = self->params.output_resolution.x > 0 && self->params.output_resolution.y > 0;
|
||||
vec2i output_size = is_scaled ? self->params.output_resolution : self->capture_size;
|
||||
output_size = scale_keep_aspect_ratio(self->capture_size, output_size);
|
||||
|
||||
const vec2i target_pos = { max_int(0, capture_metadata->width / 2 - output_size.x / 2), max_int(0, capture_metadata->height / 2 - output_size.y / 2) };
|
||||
const vec2i output_size = scale_keep_aspect_ratio(self->capture_size, (vec2i){capture_metadata->recording_width, capture_metadata->recording_height});
|
||||
const vec2i target_pos = { max_int(0, capture_metadata->video_width / 2 - output_size.x / 2), max_int(0, capture_metadata->video_height / 2 - output_size.y / 2) };
|
||||
|
||||
const vec2i actual_texture_size = {self->pipewire_data.texture_width, self->pipewire_data.texture_height};
|
||||
|
||||
|
||||
@@ -104,11 +104,11 @@ static int gsr_capture_xcomposite_start(gsr_capture *cap, gsr_capture_metadata *
|
||||
self->texture_size.y = self->window_texture.window_height;
|
||||
|
||||
if(self->params.output_resolution.x == 0 && self->params.output_resolution.y == 0) {
|
||||
capture_metadata->width = self->texture_size.x;
|
||||
capture_metadata->height = self->texture_size.y;
|
||||
capture_metadata->video_width = self->texture_size.x;
|
||||
capture_metadata->video_height = self->texture_size.y;
|
||||
} else {
|
||||
capture_metadata->width = self->params.output_resolution.x;
|
||||
capture_metadata->height = self->params.output_resolution.y;
|
||||
capture_metadata->video_width = self->params.output_resolution.x;
|
||||
capture_metadata->video_height = self->params.output_resolution.y;
|
||||
}
|
||||
|
||||
self->window_resize_timer = clock_get_monotonic_seconds();
|
||||
@@ -224,7 +224,7 @@ static bool gsr_capture_xcomposite_should_stop(gsr_capture *cap, bool *err) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static int gsr_capture_xcomposite_capture(gsr_capture *cap, gsr_capture_metadata *capture_metdata, gsr_color_conversion *color_conversion) {
|
||||
static int gsr_capture_xcomposite_capture(gsr_capture *cap, gsr_capture_metadata *capture_metadata, gsr_color_conversion *color_conversion) {
|
||||
gsr_capture_xcomposite *self = cap->priv;
|
||||
|
||||
if(self->clear_background) {
|
||||
@@ -232,11 +232,8 @@ static int gsr_capture_xcomposite_capture(gsr_capture *cap, gsr_capture_metadata
|
||||
gsr_color_conversion_clear(color_conversion);
|
||||
}
|
||||
|
||||
const bool is_scaled = self->params.output_resolution.x > 0 && self->params.output_resolution.y > 0;
|
||||
vec2i output_size = is_scaled ? self->params.output_resolution : self->texture_size;
|
||||
output_size = scale_keep_aspect_ratio(self->texture_size, output_size);
|
||||
|
||||
const vec2i target_pos = { max_int(0, capture_metdata->width / 2 - output_size.x / 2), max_int(0, capture_metdata->height / 2 - output_size.y / 2) };
|
||||
const vec2i output_size = scale_keep_aspect_ratio(self->texture_size, (vec2i){capture_metadata->recording_width, capture_metadata->recording_height});
|
||||
const vec2i target_pos = { max_int(0, capture_metadata->video_width / 2 - output_size.x / 2), max_int(0, capture_metadata->video_height / 2 - output_size.y / 2) };
|
||||
|
||||
//self->params.egl->glFlush();
|
||||
//self->params.egl->glFinish();
|
||||
|
||||
@@ -59,14 +59,14 @@ static int gsr_capture_ximage_start(gsr_capture *cap, gsr_capture_metadata *capt
|
||||
|
||||
if(self->params.output_resolution.x > 0 && self->params.output_resolution.y > 0) {
|
||||
self->params.output_resolution = scale_keep_aspect_ratio(self->capture_size, self->params.output_resolution);
|
||||
capture_metadata->width = self->params.output_resolution.x;
|
||||
capture_metadata->height = self->params.output_resolution.y;
|
||||
capture_metadata->video_width = self->params.output_resolution.x;
|
||||
capture_metadata->video_height = self->params.output_resolution.y;
|
||||
} else if(self->params.region_size.x > 0 && self->params.region_size.y > 0) {
|
||||
capture_metadata->width = self->params.region_size.x;
|
||||
capture_metadata->height = self->params.region_size.y;
|
||||
capture_metadata->video_width = self->params.region_size.x;
|
||||
capture_metadata->video_height = self->params.region_size.y;
|
||||
} else {
|
||||
capture_metadata->width = self->capture_size.x;
|
||||
capture_metadata->height = self->capture_size.y;
|
||||
capture_metadata->video_width = self->capture_size.x;
|
||||
capture_metadata->video_height = self->capture_size.y;
|
||||
}
|
||||
|
||||
self->texture_id = gl_create_texture(self->params.egl, self->capture_size.x, self->capture_size.y, GL_RGB8, GL_RGB, GL_LINEAR);
|
||||
@@ -147,14 +147,11 @@ static bool gsr_capture_ximage_upload_to_texture(gsr_capture_ximage *self, int x
|
||||
return success;
|
||||
}
|
||||
|
||||
static int gsr_capture_ximage_capture(gsr_capture *cap, gsr_capture_metadata *capture_metdata, gsr_color_conversion *color_conversion) {
|
||||
static int gsr_capture_ximage_capture(gsr_capture *cap, gsr_capture_metadata *capture_metadata, gsr_color_conversion *color_conversion) {
|
||||
gsr_capture_ximage *self = cap->priv;
|
||||
|
||||
const bool is_scaled = self->params.output_resolution.x > 0 && self->params.output_resolution.y > 0;
|
||||
vec2i output_size = is_scaled ? self->params.output_resolution : self->capture_size;
|
||||
output_size = scale_keep_aspect_ratio(self->capture_size, output_size);
|
||||
|
||||
const vec2i target_pos = { max_int(0, capture_metdata->width / 2 - output_size.x / 2), max_int(0, capture_metdata->height / 2 - output_size.y / 2) };
|
||||
const vec2i output_size = scale_keep_aspect_ratio(self->capture_size, (vec2i){capture_metadata->recording_width, capture_metadata->recording_height});
|
||||
const vec2i target_pos = { max_int(0, capture_metadata->video_width / 2 - output_size.x / 2), max_int(0, capture_metadata->video_height / 2 - output_size.y / 2) };
|
||||
gsr_capture_ximage_upload_to_texture(self, self->capture_pos.x + self->params.region_position.x, self->capture_pos.y + self->params.region_position.y, self->capture_size.x, self->capture_size.y);
|
||||
|
||||
gsr_color_conversion_draw(color_conversion, self->texture_id,
|
||||
|
||||
@@ -245,8 +245,8 @@ static bool gsr_video_encoder_vaapi_start(gsr_video_encoder *encoder, AVCodecCon
|
||||
video_codec_context->width = FFALIGN(video_codec_context->width, 2);
|
||||
video_codec_context->height = FFALIGN(video_codec_context->height, 2);
|
||||
} else {
|
||||
video_codec_context->width = FFALIGN(video_codec_context->width, 64);
|
||||
video_codec_context->height = FFALIGN(video_codec_context->height, 16);
|
||||
video_codec_context->width = FFALIGN(video_codec_context->width, 256);
|
||||
video_codec_context->height = FFALIGN(video_codec_context->height, 256);
|
||||
}
|
||||
} else if(self->params.egl->gpu_info.vendor == GSR_GPU_VENDOR_AMD && video_codec_context->codec_id == AV_CODEC_ID_AV1) {
|
||||
// TODO: Dont do this for VCN 5 and forward which should fix this hardware bug
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
/* TODO: Support hdr/10-bit */
|
||||
bool gsr_image_writer_init_opengl(gsr_image_writer *self, gsr_egl *egl, int width, int height) {
|
||||
memset(self, 0, sizeof(*self));
|
||||
self->source = GSR_IMAGE_WRITER_SOURCE_OPENGL;
|
||||
self->egl = egl;
|
||||
self->width = width;
|
||||
self->height = height;
|
||||
@@ -25,15 +24,6 @@ bool gsr_image_writer_init_opengl(gsr_image_writer *self, gsr_egl *egl, int widt
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gsr_image_writer_init_memory(gsr_image_writer *self, const void *memory, int width, int height) {
|
||||
memset(self, 0, sizeof(*self));
|
||||
self->source = GSR_IMAGE_WRITER_SOURCE_OPENGL;
|
||||
self->width = width;
|
||||
self->height = height;
|
||||
self->memory = memory;
|
||||
return true;
|
||||
}
|
||||
|
||||
void gsr_image_writer_deinit(gsr_image_writer *self) {
|
||||
if(self->texture) {
|
||||
self->egl->glDeleteTextures(1, &self->texture);
|
||||
@@ -64,7 +54,6 @@ static bool gsr_image_writer_write_memory_to_file(gsr_image_writer *self, const
|
||||
}
|
||||
|
||||
static bool gsr_image_writer_write_opengl_texture_to_file(gsr_image_writer *self, const char *filepath, gsr_image_format image_format, int quality) {
|
||||
assert(self->source == GSR_IMAGE_WRITER_SOURCE_OPENGL);
|
||||
uint8_t *frame_data = malloc(self->width * self->height * 4);
|
||||
if(!frame_data) {
|
||||
fprintf(stderr, "gsr error: gsr_image_writer_write_to_file: failed to allocate memory for image frame\n");
|
||||
@@ -90,11 +79,5 @@ static bool gsr_image_writer_write_opengl_texture_to_file(gsr_image_writer *self
|
||||
}
|
||||
|
||||
bool gsr_image_writer_write_to_file(gsr_image_writer *self, const char *filepath, gsr_image_format image_format, int quality) {
|
||||
switch(self->source) {
|
||||
case GSR_IMAGE_WRITER_SOURCE_OPENGL:
|
||||
return gsr_image_writer_write_opengl_texture_to_file(self, filepath, image_format, quality);
|
||||
case GSR_IMAGE_WRITER_SOURCE_MEMORY:
|
||||
return gsr_image_writer_write_memory_to_file(self, filepath, image_format, quality, self->memory);
|
||||
}
|
||||
return false;
|
||||
return gsr_image_writer_write_opengl_texture_to_file(self, filepath, image_format, quality);
|
||||
}
|
||||
|
||||
77
src/main.cpp
77
src/main.cpp
@@ -1268,6 +1268,11 @@ static RecordingStartAudio* get_recording_start_item_by_stream_index(RecordingSt
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
struct AudioPtsOffset {
|
||||
int64_t pts_offset = 0;
|
||||
int stream_index = 0;
|
||||
};
|
||||
|
||||
static void save_replay_async(AVCodecContext *video_codec_context, int video_stream_index, const std::vector<AudioTrack> &audio_tracks, gsr_replay_buffer *replay_buffer, std::string output_dir, const char *container_format, const std::string &file_extension, bool date_folders, bool hdr, gsr_capture *capture, int current_save_replay_seconds) {
|
||||
if(save_replay_thread.valid())
|
||||
return;
|
||||
@@ -1279,14 +1284,15 @@ static void save_replay_async(AVCodecContext *video_codec_context, int video_str
|
||||
return;
|
||||
}
|
||||
|
||||
const gsr_replay_buffer_iterator audio_start_iterator = gsr_replay_buffer_find_keyframe(replay_buffer, video_start_iterator, video_stream_index, true);
|
||||
// if(audio_start_index == (size_t)-1) {
|
||||
// fprintf(stderr, "gsr error: failed to save replay: failed to find an audio keyframe. perhaps replay was saved too fast, before anything has been recorded\n");
|
||||
// return;
|
||||
// }
|
||||
|
||||
const int64_t video_pts_offset = gsr_replay_buffer_iterator_get_packet(replay_buffer, video_start_iterator)->pts;
|
||||
const int64_t audio_pts_offset = audio_start_iterator.packet_index == (size_t)-1 ? 0 : gsr_replay_buffer_iterator_get_packet(replay_buffer, audio_start_iterator)->pts;
|
||||
|
||||
std::vector<AudioPtsOffset> audio_pts_offsets;
|
||||
audio_pts_offsets.reserve(audio_tracks.size());
|
||||
for(const AudioTrack &audio_track : audio_tracks) {
|
||||
const gsr_replay_buffer_iterator audio_start_iterator = gsr_replay_buffer_find_keyframe(replay_buffer, video_start_iterator, audio_track.stream_index, false);
|
||||
const int64_t audio_pts_offset = audio_start_iterator.packet_index == (size_t)-1 ? 0 : gsr_replay_buffer_iterator_get_packet(replay_buffer, audio_start_iterator)->pts;
|
||||
audio_pts_offsets.push_back(AudioPtsOffset{audio_pts_offset, audio_track.stream_index});
|
||||
}
|
||||
|
||||
gsr_replay_buffer *cloned_replay_buffer = gsr_replay_buffer_clone(replay_buffer);
|
||||
if(!cloned_replay_buffer) {
|
||||
@@ -1302,7 +1308,7 @@ static void save_replay_async(AVCodecContext *video_codec_context, int video_str
|
||||
|
||||
save_replay_output_filepath = std::move(output_filepath);
|
||||
|
||||
save_replay_thread = std::async(std::launch::async, [video_stream_index, recording_start_result, video_start_iterator, video_pts_offset, audio_pts_offset, video_codec_context, cloned_replay_buffer]() mutable {
|
||||
save_replay_thread = std::async(std::launch::async, [video_stream_index, recording_start_result, video_start_iterator, video_pts_offset, audio_pts_offsets{std::move(audio_pts_offsets)}, video_codec_context, cloned_replay_buffer]() mutable {
|
||||
gsr_replay_buffer_iterator replay_iterator = video_start_iterator;
|
||||
for(;;) {
|
||||
AVPacket *replay_packet = gsr_replay_buffer_iterator_get_packet(cloned_replay_buffer, replay_iterator);
|
||||
@@ -1350,8 +1356,10 @@ static void save_replay_async(AVCodecContext *video_codec_context, int video_str
|
||||
stream = recording_start_audio->stream;
|
||||
codec_context = audio_track->codec_context;
|
||||
|
||||
av_packet.pts -= audio_pts_offset;
|
||||
av_packet.dts -= audio_pts_offset;
|
||||
const AudioPtsOffset &audio_pts_offset = audio_pts_offsets[av_packet.stream_index - 1];
|
||||
assert(audio_pts_offset.stream_index == av_packet.stream_index);
|
||||
av_packet.pts -= audio_pts_offset.pts_offset;
|
||||
av_packet.dts -= audio_pts_offset.pts_offset;
|
||||
}
|
||||
|
||||
//av_packet.stream_index = stream->index;
|
||||
@@ -2302,7 +2310,7 @@ static int video_quality_to_image_quality_value(gsr_video_quality video_quality)
|
||||
case GSR_VIDEO_QUALITY_HIGH:
|
||||
return 85;
|
||||
case GSR_VIDEO_QUALITY_VERY_HIGH:
|
||||
return 90;
|
||||
return 91; // Quality above 90 makes the jpeg image encoder (stb_image_writer) use yuv444 instead of yuv420, which greatly improves small colored text quality on dark background
|
||||
case GSR_VIDEO_QUALITY_ULTRA:
|
||||
return 97;
|
||||
}
|
||||
@@ -2318,8 +2326,10 @@ static void capture_image_to_file(args_parser &arg_parser, gsr_egl *egl, gsr_ima
|
||||
gsr_capture *capture = create_capture_impl(arg_parser, egl, prefer_ximage);
|
||||
|
||||
gsr_capture_metadata capture_metadata;
|
||||
capture_metadata.width = 0;
|
||||
capture_metadata.height = 0;
|
||||
capture_metadata.video_width = 0;
|
||||
capture_metadata.video_height = 0;
|
||||
capture_metadata.recording_width = 0;
|
||||
capture_metadata.recording_height = 0;
|
||||
capture_metadata.fps = fps;
|
||||
|
||||
int capture_result = gsr_capture_start(capture, &capture_metadata);
|
||||
@@ -2328,8 +2338,11 @@ static void capture_image_to_file(args_parser &arg_parser, gsr_egl *egl, gsr_ima
|
||||
_exit(capture_result);
|
||||
}
|
||||
|
||||
capture_metadata.recording_width = capture_metadata.video_width;
|
||||
capture_metadata.recording_height = capture_metadata.video_height;
|
||||
|
||||
gsr_image_writer image_writer;
|
||||
if(!gsr_image_writer_init_opengl(&image_writer, egl, capture_metadata.width, capture_metadata.height)) {
|
||||
if(!gsr_image_writer_init_opengl(&image_writer, egl, capture_metadata.video_width, capture_metadata.video_height)) {
|
||||
fprintf(stderr, "gsr error: capture_image_to_file_wayland: gsr_image_write_gl_init failed\n");
|
||||
_exit(1);
|
||||
}
|
||||
@@ -2341,7 +2354,7 @@ static void capture_image_to_file(args_parser &arg_parser, gsr_egl *egl, gsr_ima
|
||||
color_conversion_params.load_external_image_shader = gsr_capture_uses_external_image(capture);
|
||||
|
||||
color_conversion_params.destination_textures[0] = image_writer.texture;
|
||||
color_conversion_params.destination_textures_size[0] = { capture_metadata.width, capture_metadata.height };
|
||||
color_conversion_params.destination_textures_size[0] = { capture_metadata.video_width, capture_metadata.video_height };
|
||||
color_conversion_params.num_destination_textures = 1;
|
||||
color_conversion_params.destination_color = GSR_DESTINATION_COLOR_RGB8;
|
||||
|
||||
@@ -2818,7 +2831,7 @@ static const AVCodec* pick_video_codec(gsr_egl *egl, args_parser *args_parser, b
|
||||
|
||||
/* Returns -1 if none is available */
|
||||
static gsr_video_codec select_appropriate_video_codec_automatically(gsr_capture_metadata capture_metadata, const gsr_supported_video_codecs *supported_video_codecs) {
|
||||
const vec2i capture_size = {capture_metadata.width, capture_metadata.height};
|
||||
const vec2i capture_size = {capture_metadata.video_width, capture_metadata.video_height};
|
||||
if(supported_video_codecs->h264.supported && codec_supports_resolution(supported_video_codecs->h264.max_resolution, capture_size)) {
|
||||
fprintf(stderr, "gsr info: using h264 encoder because a codec was not specified\n");
|
||||
return GSR_VIDEO_CODEC_H264;
|
||||
@@ -2902,7 +2915,7 @@ static const AVCodec* select_video_codec_with_fallback(gsr_capture_metadata capt
|
||||
const AVCodec *codec = pick_video_codec(egl, args_parser, is_flv, low_power, &supported_video_codecs);
|
||||
|
||||
const vec2i codec_max_resolution = codec_get_max_resolution(args_parser->video_codec, args_parser->video_encoder == GSR_VIDEO_ENCODER_HW_CPU, &supported_video_codecs);
|
||||
const vec2i capture_size = {capture_metadata.width, capture_metadata.height};
|
||||
const vec2i capture_size = {capture_metadata.video_width, capture_metadata.video_height};
|
||||
if(!codec_supports_resolution(codec_max_resolution, capture_size)) {
|
||||
const char *video_codec_name = video_codec_to_string(args_parser->video_codec);
|
||||
fprintf(stderr, "gsr error: The max resolution for video codec %s is %dx%d while you are trying to capture at resolution %dx%d. Change capture resolution or video codec and try again\n",
|
||||
@@ -3105,6 +3118,7 @@ int main(int argc, char **argv) {
|
||||
// Linux nvidia driver 580.105.08 added the environment variable CUDA_DISABLE_PERF_BOOST to disable the p2 power level issue,
|
||||
// where running cuda (which includes nvenc) causes the gpu to be forcefully set to p2 power level which on many nvidia gpus
|
||||
// decreases gpu performance in games. On my GTX 1080 it decreased game performance by 10% for absolutely no reason.
|
||||
// TODO: This only seems to allow the gpu to go to lower power level states, but not higher than p2.
|
||||
setenv("CUDA_DISABLE_PERF_BOOST", "1", true);
|
||||
// Stop nvidia driver from buffering frames
|
||||
setenv("__GL_MaxFramesAllowed", "1", true);
|
||||
@@ -3301,8 +3315,10 @@ int main(int argc, char **argv) {
|
||||
gsr_capture *capture = create_capture_impl(arg_parser, &egl, false);
|
||||
|
||||
gsr_capture_metadata capture_metadata;
|
||||
capture_metadata.width = 0;
|
||||
capture_metadata.height = 0;
|
||||
capture_metadata.video_width = 0;
|
||||
capture_metadata.video_height = 0;
|
||||
capture_metadata.recording_width = 0;
|
||||
capture_metadata.recording_height = 0;
|
||||
capture_metadata.fps = arg_parser.fps;
|
||||
|
||||
int capture_result = gsr_capture_start(capture, &capture_metadata);
|
||||
@@ -3327,7 +3343,7 @@ int main(int argc, char **argv) {
|
||||
const AVCodec *video_codec_f = select_video_codec_with_fallback(capture_metadata, &arg_parser, file_extension.c_str(), &egl, &low_power);
|
||||
|
||||
const enum AVPixelFormat video_pix_fmt = get_pixel_format(arg_parser.video_codec, egl.gpu_info.vendor, arg_parser.video_encoder == GSR_VIDEO_ENCODER_HW_CPU);
|
||||
AVCodecContext *video_codec_context = create_video_codec_context(video_pix_fmt, video_codec_f, egl, arg_parser, capture_metadata.width, capture_metadata.height);
|
||||
AVCodecContext *video_codec_context = create_video_codec_context(video_pix_fmt, video_codec_f, egl, arg_parser, capture_metadata.video_width, capture_metadata.video_height);
|
||||
if(!is_replaying)
|
||||
video_stream = create_stream(av_format_context, video_codec_context);
|
||||
|
||||
@@ -3337,8 +3353,8 @@ int main(int argc, char **argv) {
|
||||
_exit(1);
|
||||
}
|
||||
video_frame->format = video_codec_context->pix_fmt;
|
||||
video_frame->width = capture_metadata.width;
|
||||
video_frame->height = capture_metadata.height;
|
||||
video_frame->width = capture_metadata.video_width;
|
||||
video_frame->height = capture_metadata.video_height;
|
||||
video_frame->color_range = video_codec_context->color_range;
|
||||
video_frame->color_primaries = video_codec_context->color_primaries;
|
||||
video_frame->color_trc = video_codec_context->color_trc;
|
||||
@@ -3363,9 +3379,12 @@ int main(int argc, char **argv) {
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
capture_metadata.recording_width = capture_metadata.video_width;
|
||||
capture_metadata.recording_height = capture_metadata.video_height;
|
||||
|
||||
// TODO: What if this updated resolution is above max resolution?
|
||||
capture_metadata.width = video_codec_context->width;
|
||||
capture_metadata.height = video_codec_context->height;
|
||||
capture_metadata.video_width = video_codec_context->width;
|
||||
capture_metadata.video_height = video_codec_context->height;
|
||||
|
||||
const Arg *plugin_arg = args_parser_get_arg(&arg_parser, "-p");
|
||||
assert(plugin_arg);
|
||||
@@ -3378,8 +3397,8 @@ int main(int argc, char **argv) {
|
||||
assert(color_depth == GSR_COLOR_DEPTH_8_BITS || color_depth == GSR_COLOR_DEPTH_10_BITS);
|
||||
|
||||
const gsr_plugin_init_params plugin_init_params = {
|
||||
(unsigned int)capture_metadata.width,
|
||||
(unsigned int)capture_metadata.height,
|
||||
(unsigned int)capture_metadata.video_width,
|
||||
(unsigned int)capture_metadata.video_height,
|
||||
(unsigned int)arg_parser.fps,
|
||||
color_depth == GSR_COLOR_DEPTH_8_BITS ? GSR_PLUGIN_COLOR_DEPTH_8_BITS : GSR_PLUGIN_COLOR_DEPTH_10_BITS,
|
||||
egl.context_type == GSR_GL_CONTEXT_TYPE_GLX ? GSR_PLUGIN_GRAPHICS_API_GLX : GSR_PLUGIN_GRAPHICS_API_EGL_ES,
|
||||
@@ -3819,9 +3838,9 @@ int main(int argc, char **argv) {
|
||||
if(plugins.num_plugins > 0) {
|
||||
gsr_plugins_draw(&plugins);
|
||||
gsr_color_conversion_draw(&color_conversion, plugins.texture,
|
||||
{0, 0}, {capture_metadata.width, capture_metadata.height},
|
||||
{0, 0}, {capture_metadata.width, capture_metadata.height},
|
||||
{capture_metadata.width, capture_metadata.height}, GSR_ROT_0, GSR_SOURCE_COLOR_RGB, false);
|
||||
{0, 0}, {capture_metadata.video_width, capture_metadata.video_height},
|
||||
{0, 0}, {capture_metadata.video_width, capture_metadata.video_height},
|
||||
{capture_metadata.video_width, capture_metadata.video_height}, GSR_ROT_0, GSR_SOURCE_COLOR_RGB, false);
|
||||
}
|
||||
|
||||
if(capture_has_synchronous_task) {
|
||||
|
||||
Reference in New Issue
Block a user