mirror of
https://repo.dec05eba.com/gpu-screen-recorder
synced 2026-04-05 19:16:36 +09:00
Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0de75e5b7e | ||
|
|
c79fb1e5c9 | ||
|
|
4a4af85b6d | ||
|
|
8f7608e7ee | ||
|
|
f3235ed1bf | ||
|
|
3666bba518 | ||
|
|
5d8d14eeaf | ||
|
|
57caf13d65 | ||
|
|
bdf1950ca2 | ||
|
|
144b481526 | ||
|
|
f4ee71a094 | ||
|
|
2dce92d82f | ||
|
|
933911bdde | ||
|
|
01d0df500c | ||
|
|
95415f7ac7 | ||
|
|
a39dad1c02 |
@@ -29,14 +29,14 @@ Supported image formats:
|
||||
This software works on X11 and Wayland on AMD, Intel and NVIDIA.
|
||||
|
||||
# Installation
|
||||
If you are running an Arch Linux based distro then you can find gpu screen recorder on aur under the name gpu-screen-recorder (`yay -S gpu-screen-recorder`).\
|
||||
If you are running an Arch Linux based distro then you can find gpu screen recorder in the official repositories under the name gpu-screen-recorder (`sudo pacman -S gpu-screen-recorder`).\
|
||||
If you are running another distro then you can run `sudo ./install.sh`, but you need to manually install the dependencies, as described below.\
|
||||
You can also install gpu screen recorder ([the ui version](https://git.dec05eba.com/gpu-screen-recorder-gtk/)) from [flathub](https://flathub.org/apps/details/com.dec05eba.gpu_screen_recorder), which is the easiest method
|
||||
to install GPU Screen Recorder on non-arch based distros.\
|
||||
If you install GPU Screen Recorder flatpak, which is the gtk gui version then you can still run GPU Screen Recorder command line by using the flatpak command option, for example `flatpak run --command=gpu-screen-recorder com.dec05eba.gpu_screen_recorder -w screen -f 60 -o video.mp4`. Note that if you want to record your monitor on AMD/Intel then you need to install the flatpak system-wide (like so: `flatpak install --system com.dec05eba.gpu_screen_recorder`).
|
||||
|
||||
## Unofficial install methods
|
||||
The only official ways to install GPU Screen Recorder is either from source, AUR or flathub. Other sources may be out of date and missing features or may not work correctly.\
|
||||
The only official ways to install GPU Screen Recorder is either from source, arch linux extra repository or flathub. Other sources may be out of date and missing features or may not work correctly.\
|
||||
If you install GPU Screen Recorder from somewhere else and have an issue then try installing it from one of the official sources before reporting it as an issue.\
|
||||
If you still prefer to install GPU Screen Recorder with a package manager instead of from source or as a flatpak then you may be able to find a package for your distro.\
|
||||
Here are some known unofficial packages:
|
||||
@@ -261,3 +261,5 @@ If the root user is disabled on your system then you can instead record with `-w
|
||||
## GPU usage is high on my laptop
|
||||
GPU usage on battery powered devices is misleading. For example Intel iGPUs has multiple performance levels and the GPU usage reported on the system is the GPU usage at the current performance level.
|
||||
The performance level changes depending on the GPU load, so it may say that GPU usage is 80%, but the actual GPU usage may be 5%.
|
||||
## The video is too dark when capturing full-range video or 10-bit video
|
||||
This is an issue in some broken video players such as vlc. Play the video with a video player such as mpv (or a mpv frontend such as celluloid) or a browser instead.
|
||||
|
||||
10
TODO
10
TODO
@@ -312,8 +312,6 @@ Check if region capture works properly with fractional scaling on wayland.
|
||||
Add option to specify medium/high/very high/ultra for -bm cbr as well, which should automatically pick bitrate based on resolution and framerate.
|
||||
This should also be reflected in gsr ui.
|
||||
|
||||
Create a manpage and move --help text there and mention the manpage command to view it (and make it work in flatpak, maybe with man <link-to-manpage-file>).
|
||||
|
||||
Implement webcam support by using mjpeg with v4l2 and use ffmpeg mjpeg decoder.
|
||||
|
||||
After adding rpc, making recording while in replay/streaming work differently. Start recording should take audio as an argument, to optionally specify different audio for recording than replay/stream.
|
||||
@@ -398,4 +396,10 @@ Return the max resolution of each codec in --info to display an error in the UI
|
||||
|
||||
Should -low-power option also use vaapi/vulkan low power, if available?
|
||||
|
||||
Should capture option x=bla;y=bla be scaled by -s (output resolution scale)? width and height is.
|
||||
Should capture option x=bla;y=bla be scaled by -s (output resolution scale)? width and height is.
|
||||
|
||||
Certain webcam resolutions yuyv resolutions dont work (on amd at least), such as 800x600. Maybe it's because of alignment issue, 600 isn't divisible by 16.
|
||||
|
||||
Add option to capture all monitors automatically.
|
||||
|
||||
Make -w optional, to only capture audio.
|
||||
|
||||
@@ -14,13 +14,17 @@ gpu-screen-recorder \- The fastest screen recording tool for Linux
|
||||
|
|
||||
.B \-\-version
|
||||
|
|
||||
.B \-\-info
|
||||
|
|
||||
.B \-\-list\-capture\-options
|
||||
|
|
||||
.B \-\-list\-monitors
|
||||
|
|
||||
.B \-\-list\-v4l2\-devices
|
||||
|
|
||||
.B \-\-list\-audio\-devices
|
||||
|
|
||||
.B \-\-list\-application\-audio
|
||||
|
|
||||
.B \-\-info
|
||||
.SH DESCRIPTION
|
||||
.B gpu-screen-recorder
|
||||
is the fastest screen recording tool for Linux. It uses the GPU
|
||||
@@ -92,10 +96,6 @@ Run
|
||||
.B \-\-list\-capture\-options
|
||||
to list available capture sources.
|
||||
.PP
|
||||
Run
|
||||
.B \-\-list\-v4l2\-devices
|
||||
to list available camera devices (V4L2).
|
||||
.PP
|
||||
Additional options can be passed to each capture source by splitting capture source with
|
||||
.B ;
|
||||
for example
|
||||
@@ -105,18 +105,18 @@ These are the available options for all capture sources (optional):
|
||||
.RS
|
||||
.IP \(bu 3
|
||||
.B x
|
||||
- The X position in pixels. If the number ends with % and is a number between 0 and 100 then it's a position relative to the video size
|
||||
- The X position in pixels. If the number ends with % then this sets the X position relative to the video width (integer percentage where 100 = 100%)
|
||||
.IP \(bu 3
|
||||
.B y
|
||||
- The Y position in pixels. If the number ends with % and is a number between 0 and 100 then it's a position relative to the video size
|
||||
- The Y position in pixels. If the number ends with % then this sets the Y position relative to the video height (integer percentage where 100 = 100%)
|
||||
.IP \(bu 3
|
||||
.B width
|
||||
- The width in pixels. If the number ends with % and is a number between 0 and 100 then it's a size relative to the video size.
|
||||
- The width in pixels. If the number ends with % then this sets the width relative to the video width (integer percentage where 100 = 100%).
|
||||
|
||||
A value of 0 means to not scale the capture source and instead use the original width.
|
||||
.IP \(bu 3
|
||||
.B height
|
||||
- The height in pixels. If the number ends with % and is a number between 0 and 100 then it's a size relative to the video size
|
||||
- The height in pixels. If the number ends with % then this sets the height relative to the video height (integer percentage where 100 = 100%).
|
||||
|
||||
A value of 0 means to not scale the capture source and instead use the original height.
|
||||
.IP \(bu 3
|
||||
@@ -283,10 +283,12 @@ Video codec:
|
||||
Quality preset (medium, high, very_high, ultra) for QP/VBR mode, or bitrate (kbps) for CBR mode (default: very_high).
|
||||
.TP
|
||||
.BI \-bm " auto|qp|vbr|cbr"
|
||||
Bitrate mode (default: auto → qp). CBR recommended for replay buffer.
|
||||
Bitrate mode (default: auto → qp). CBR recommended for replay buffer and live streaming.
|
||||
|
||||
QP means to capture with constant quality, even in motion, while VBR and CBR means to capture with constant size.
|
||||
.TP
|
||||
.BI \-fm " cfr|vfr|content"
|
||||
Frame rate mode: constant, variable, or match content (default: vfr). Content mode only on X11 or portal.
|
||||
Frame rate mode: cfr (constant), vfr (variable), or content (match content) (default: vfr). Content mode is only available on X11 or portal.
|
||||
|
||||
Content mode syncs video to the captured content and is recommended for smoothest video when the game is running
|
||||
at the same frame rate or lower than what you are trying to record at.
|
||||
@@ -372,6 +374,14 @@ It's recommended to also use the option
|
||||
when this is set to
|
||||
.B yes
|
||||
to only encode frames when the screen content updates to lower GPU and video encoding usage when the system is idle.
|
||||
.TP
|
||||
.BI \-write\-first\-frame\-ts " yes|no"
|
||||
When enabled, writes a timestamp file with extra extension \fI.ts\fR next to the output video containing:
|
||||
.nf
|
||||
monotonic_microsec realtime_microsec
|
||||
<monotonic_microsec> <realtime_microsec>
|
||||
.fi
|
||||
(default: no). Ignored for live streaming and when output is piped.
|
||||
.SS Output Options
|
||||
.TP
|
||||
.BI \-o " output"
|
||||
@@ -393,6 +403,9 @@ Show system info (codecs, capture options).
|
||||
.B \-\-list\-capture\-options
|
||||
List available capture sources (window, monitors, portal, v4l2 device path).
|
||||
.TP
|
||||
.B \-\-list\-monitors
|
||||
List available monitors.
|
||||
.TP
|
||||
.B \-\-list\-v4l2\-devices
|
||||
List available cameras devices (V4L2).
|
||||
.TP
|
||||
@@ -467,7 +480,7 @@ Instant replay (last 60 seconds):
|
||||
.PP
|
||||
.nf
|
||||
.RS
|
||||
gpu-screen-recorder -w screen -f 60 -c mkv -r 60 -o ~/Videos
|
||||
gpu-screen-recorder -w screen -c mkv -r 60 -o ~/Videos
|
||||
.RE
|
||||
.fi
|
||||
.PP
|
||||
@@ -491,15 +504,15 @@ Instant replay and launch a script when saving replay:
|
||||
.PP
|
||||
.nf
|
||||
.RS
|
||||
gpu-screen-recorder -w screen -f 60 -c mkv -r 60 -sc ./script.sh -o ~/Videos
|
||||
gpu-screen-recorder -w screen -c mkv -r 60 -sc ./script.sh -o ~/Videos
|
||||
.RE
|
||||
.fi
|
||||
.PP
|
||||
Stream to Twitch:
|
||||
Stream to Twitch with constant bitrate mode:
|
||||
.PP
|
||||
.nf
|
||||
.RS
|
||||
gpu-screen-recorder -w screen -f 60 -a default_output -o "rtmp://live.twitch.tv/app/stream_key"
|
||||
gpu-screen-recorder -w screen -c flv -a default_output -bm cbr -q 8000 -o "rtmp://live.twitch.tv/app/stream_key"
|
||||
.RE
|
||||
.fi
|
||||
.PP
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
typedef struct gsr_egl gsr_egl;
|
||||
|
||||
#define NUM_ARGS 36
|
||||
#define NUM_ARGS 37
|
||||
|
||||
typedef enum {
|
||||
GSR_CAPTURE_SOURCE_TYPE_WINDOW,
|
||||
@@ -63,6 +63,7 @@ typedef struct {
|
||||
void (*list_application_audio)(void *userdata);
|
||||
void (*list_v4l2_devices)(void *userdata);
|
||||
void (*list_capture_options)(const char *card_path, void *userdata);
|
||||
void (*list_monitors)(void *userdata);
|
||||
} args_handlers;
|
||||
|
||||
typedef struct {
|
||||
@@ -97,6 +98,7 @@ typedef struct {
|
||||
bool restore_portal_session;
|
||||
bool restart_replay_on_save;
|
||||
bool overclock;
|
||||
bool write_first_frame_ts;
|
||||
bool is_livestream;
|
||||
bool is_output_piped;
|
||||
bool low_latency_recording;
|
||||
|
||||
@@ -20,6 +20,8 @@ typedef struct {
|
||||
AVStream *stream;
|
||||
int64_t start_pts;
|
||||
bool has_received_keyframe;
|
||||
char *first_frame_ts_filepath;
|
||||
bool first_frame_ts_written;
|
||||
} gsr_encoder_recording_destination;
|
||||
|
||||
typedef struct {
|
||||
@@ -43,5 +45,6 @@ void gsr_encoder_receive_packets(gsr_encoder *self, AVCodecContext *codec_contex
|
||||
/* Returns the id to the recording destination, or -1 on error */
|
||||
size_t gsr_encoder_add_recording_destination(gsr_encoder *self, AVCodecContext *codec_context, AVFormatContext *format_context, AVStream *stream, int64_t start_pts);
|
||||
bool gsr_encoder_remove_recording_destination(gsr_encoder *self, size_t id);
|
||||
bool gsr_encoder_set_recording_destination_first_frame_ts_filepath(gsr_encoder *self, size_t id, const char *filepath);
|
||||
|
||||
#endif /* GSR_ENCODER_H */
|
||||
|
||||
@@ -106,6 +106,7 @@ typedef struct {
|
||||
|
||||
bool no_modifiers_fallback;
|
||||
bool external_texture_fallback;
|
||||
uint64_t renegotiated_modifier;
|
||||
|
||||
uint64_t modifiers[GSR_PIPEWIRE_VIDEO_MAX_MODIFIERS];
|
||||
size_t num_modifiers;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
project('gpu-screen-recorder', ['c', 'cpp'], version : '5.12.2', default_options : ['warning_level=2'])
|
||||
project('gpu-screen-recorder', ['c', 'cpp'], version : '5.12.4', default_options : ['warning_level=2'])
|
||||
|
||||
add_project_arguments('-Wshadow', language : ['c', 'cpp'])
|
||||
if get_option('buildtype') == 'debug'
|
||||
@@ -117,6 +117,7 @@ executable('gpu-screen-recorder', src, dependencies : dep, install : true)
|
||||
|
||||
install_headers('plugin/plugin.h', install_dir : 'include/gsr')
|
||||
install_man('gpu-screen-recorder.1', 'gsr-kms-server.1')
|
||||
install_subdir('scripts', install_dir: 'share/gpu-screen-recorder')
|
||||
|
||||
if get_option('systemd') == true
|
||||
install_data(files('extra/gpu-screen-recorder.service'), install_dir : 'lib/systemd/user')
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "gpu-screen-recorder"
|
||||
type = "executable"
|
||||
version = "5.12.2"
|
||||
version = "5.12.4"
|
||||
platforms = ["posix"]
|
||||
|
||||
[config]
|
||||
|
||||
@@ -4,4 +4,4 @@
|
||||
|
||||
[ "$#" -ne 4 ] && echo "usage: twitch-stream-local-copy.sh <window_id> <fps> <livestream_key> <local_file>" && exit 1
|
||||
active_sink=default_output
|
||||
gpu-screen-recorder -w "$1" -c flv -f "$2" -q high -a "$active_sink" | tee -- "$4" | ffmpeg -i pipe:0 -c copy -f flv -- "rtmp://live.twitch.tv/app/$3"
|
||||
gpu-screen-recorder -w "$1" -c flv -f "$2" -bm cbr -q 8000 -a "$active_sink" | tee -- "$4" | ffmpeg -i pipe:0 -c copy -f flv -- "rtmp://live.twitch.tv/app/$3"
|
||||
|
||||
@@ -2,4 +2,4 @@
|
||||
|
||||
[ "$#" -ne 3 ] && echo "usage: twitch-stream.sh <window_id> <fps> <livestream_key>" && exit 1
|
||||
active_sink=default_output
|
||||
gpu-screen-recorder -w "$1" -c flv -f "$2" -q high -a "$active_sink" -o "rtmp://live.twitch.tv/app/$3"
|
||||
gpu-screen-recorder -w "$1" -c flv -f "$2" -bm cbr -q 8000 -a "$active_sink" -o "rtmp://live.twitch.tv/app/$3"
|
||||
|
||||
@@ -2,4 +2,4 @@
|
||||
|
||||
[ "$#" -ne 3 ] && echo "usage: youtube-hls-stream.sh <window_id> <fps> <livestream_key>" && exit 1
|
||||
active_sink=default_output
|
||||
gpu-screen-recorder -w "$1" -c hls -f "$2" -q high -a "$active_sink" -ac aac -o "https://a.upload.youtube.com/http_upload_hls?cid=$3©=0&file=stream.m3u8"
|
||||
gpu-screen-recorder -w "$1" -c hls -f "$2" -bm cbr -q 8000 -a "$active_sink" -ac aac -o "https://a.upload.youtube.com/http_upload_hls?cid=$3©=0&file=stream.m3u8"
|
||||
@@ -196,7 +196,8 @@ static void usage_header(void) {
|
||||
"[-bm auto|qp|vbr|cbr] [-cr limited|full] [-tune performance|quality] [-df yes|no] [-sc <script_path>] [-p <plugin_path>] "
|
||||
"[-cursor yes|no] [-keyint <value>] [-restore-portal-session yes|no] [-portal-session-token-filepath filepath] [-encoder gpu|cpu] "
|
||||
"[-fallback-cpu-encoding yes|no] [-o <output_file>] [-ro <output_directory>] [-ffmpeg-opts <options>] [--list-capture-options [card_path]] "
|
||||
"[--list-audio-devices] [--list-application-audio] [--list-v4l2-devices] [-low-power yes|no] [-v yes|no] [-gl-debug yes|no] [--version] [-h|--help]\n", program_name);
|
||||
"[--list-monitors] [--list-audio-devices] [--list-application-audio] [--list-v4l2-devices] [-write-first-frame-ts yes|no] [-low-power yes|no] "
|
||||
"[-v yes|no] [-gl-debug yes|no] [--version] [-h|--help]\n", program_name);
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
@@ -255,6 +256,7 @@ static bool args_parser_set_values(args_parser *self) {
|
||||
self->restart_replay_on_save = args_get_boolean_by_key(self->args, NUM_ARGS, "-restart-replay-on-save", false);
|
||||
self->overclock = args_get_boolean_by_key(self->args, NUM_ARGS, "-oc", false);
|
||||
self->fallback_cpu_encoding = args_get_boolean_by_key(self->args, NUM_ARGS, "-fallback-cpu-encoding", false);
|
||||
self->write_first_frame_ts = args_get_boolean_by_key(self->args, NUM_ARGS, "-write-first-frame-ts", false);
|
||||
self->low_power = args_get_boolean_by_key(self->args, NUM_ARGS, "-low-power", false);
|
||||
|
||||
self->audio_bitrate = args_get_i64_by_key(self->args, NUM_ARGS, "-ab", 0);
|
||||
@@ -432,6 +434,10 @@ static bool args_parser_set_values(args_parser *self) {
|
||||
|
||||
self->is_output_piped = strcmp(self->filename, "/dev/stdout") == 0;
|
||||
self->low_latency_recording = self->is_livestream || self->is_output_piped;
|
||||
if(self->write_first_frame_ts && (self->is_livestream || self->is_output_piped)) {
|
||||
fprintf(stderr, "gsr warning: -write-first-frame-ts is ignored for livestreaming or when output is piped\n");
|
||||
self->write_first_frame_ts = false;
|
||||
}
|
||||
|
||||
self->replay_recording_directory = args_get_value_by_key(self->args, NUM_ARGS, "-ro");
|
||||
|
||||
@@ -495,6 +501,11 @@ bool args_parser_parse(args_parser *self, int argc, char **argv, const args_hand
|
||||
}
|
||||
}
|
||||
|
||||
if(strcmp(argv[1], "--list-monitors") == 0) {
|
||||
arg_handlers->list_monitors(userdata);
|
||||
return true;
|
||||
}
|
||||
|
||||
if(argc == 2 && strcmp(argv[1], "--version") == 0) {
|
||||
arg_handlers->version(userdata);
|
||||
return true;
|
||||
@@ -536,6 +547,7 @@ bool args_parser_parse(args_parser *self, int argc, char **argv, const args_hand
|
||||
self->args[arg_index++] = (Arg){ .key = "-ffmpeg-opts", .optional = true, .list = false, .type = ARG_TYPE_STRING };
|
||||
self->args[arg_index++] = (Arg){ .key = "-ffmpeg-video-opts", .optional = true, .list = false, .type = ARG_TYPE_STRING };
|
||||
self->args[arg_index++] = (Arg){ .key = "-ffmpeg-audio-opts", .optional = true, .list = false, .type = ARG_TYPE_STRING };
|
||||
self->args[arg_index++] = (Arg){ .key = "-write-first-frame-ts", .optional = true, .list = false, .type = ARG_TYPE_BOOLEAN };
|
||||
self->args[arg_index++] = (Arg){ .key = "-low-power", .optional = true, .list = false, .type = ARG_TYPE_BOOLEAN };
|
||||
assert(arg_index == NUM_ARGS);
|
||||
|
||||
|
||||
16
src/dbus.c
16
src/dbus.c
@@ -78,14 +78,6 @@ bool gsr_dbus_init(gsr_dbus *self, const char *screencast_restore_token) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* TODO: Check the name */
|
||||
const int ret = dbus_bus_request_name(self->con, "com.dec05eba.gpu_screen_recorder", DBUS_NAME_FLAG_REPLACE_EXISTING, &self->err);
|
||||
if(dbus_error_is_set(&self->err)) {
|
||||
fprintf(stderr, "gsr error: gsr_dbus_init: dbus_bus_request_name failed with error: %s\n", self->err.message);
|
||||
gsr_dbus_deinit(self);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(screencast_restore_token) {
|
||||
self->screencast_restore_token = strdup(screencast_restore_token);
|
||||
if(!self->screencast_restore_token) {
|
||||
@@ -95,12 +87,6 @@ bool gsr_dbus_init(gsr_dbus *self, const char *screencast_restore_token) {
|
||||
}
|
||||
}
|
||||
|
||||
(void)ret;
|
||||
// if(ret != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
|
||||
// fprintf(stderr, "gsr error: gsr_capture_portal_setup_dbus: dbus_bus_request_name failed to get primary owner\n");
|
||||
// return false;
|
||||
// }
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -119,8 +105,6 @@ void gsr_dbus_deinit(gsr_dbus *self) {
|
||||
if(self->con) {
|
||||
dbus_error_free(&self->err);
|
||||
|
||||
dbus_bus_release_name(self->con, "com.dec05eba.gpu_screen_recorder", NULL);
|
||||
|
||||
// Apparently shouldn't be used when a connection is setup by using dbus_bus_get
|
||||
//dbus_connection_close(self->con);
|
||||
dbus_connection_unref(self->con);
|
||||
|
||||
@@ -3,10 +3,37 @@
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <inttypes.h>
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <libavcodec/avcodec.h>
|
||||
#include <libavformat/avformat.h>
|
||||
|
||||
static uint64_t clock_gettime_microseconds(clockid_t clock_id) {
|
||||
struct timespec ts;
|
||||
ts.tv_sec = 0;
|
||||
ts.tv_nsec = 0;
|
||||
clock_gettime(clock_id, &ts);
|
||||
return (uint64_t)ts.tv_sec * 1000000ULL + (uint64_t)ts.tv_nsec / 1000ULL;
|
||||
}
|
||||
|
||||
static void gsr_write_first_frame_timestamp_file(const char *filepath) {
|
||||
const uint64_t evdev_compatible_ts = clock_gettime_microseconds(CLOCK_MONOTONIC);
|
||||
const uint64_t unix_time_microsec = clock_gettime_microseconds(CLOCK_REALTIME);
|
||||
|
||||
FILE *file = fopen(filepath, "w");
|
||||
if(!file) {
|
||||
fprintf(stderr, "gsr warning: failed to open timestamp file '%s': %s\n", filepath, strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
fputs("monotonic_microsec\trealtime_microsec\n", file);
|
||||
fprintf(file, "%" PRIu64 "\t%" PRIu64 "\n", evdev_compatible_ts, unix_time_microsec);
|
||||
fclose(file);
|
||||
}
|
||||
|
||||
bool gsr_encoder_init(gsr_encoder *self, gsr_replay_storage replay_storage, size_t replay_buffer_num_packets, double replay_buffer_time, const char *replay_directory) {
|
||||
memset(self, 0, sizeof(*self));
|
||||
self->num_recording_destinations = 0;
|
||||
@@ -39,6 +66,16 @@ bool gsr_encoder_init(gsr_encoder *self, gsr_replay_storage replay_storage, size
|
||||
}
|
||||
|
||||
void gsr_encoder_deinit(gsr_encoder *self) {
|
||||
if(self->file_write_mutex_created)
|
||||
pthread_mutex_lock(&self->file_write_mutex);
|
||||
for(size_t i = 0; i < self->num_recording_destinations; ++i) {
|
||||
free(self->recording_destinations[i].first_frame_ts_filepath);
|
||||
self->recording_destinations[i].first_frame_ts_filepath = NULL;
|
||||
self->recording_destinations[i].first_frame_ts_written = false;
|
||||
}
|
||||
if(self->file_write_mutex_created)
|
||||
pthread_mutex_unlock(&self->file_write_mutex);
|
||||
|
||||
if(self->replay_buffer) {
|
||||
pthread_mutex_lock(&self->replay_mutex);
|
||||
gsr_replay_buffer_destroy(self->replay_buffer);
|
||||
@@ -94,6 +131,11 @@ void gsr_encoder_receive_packets(gsr_encoder *self, AVCodecContext *codec_contex
|
||||
else if(!recording_destination->has_received_keyframe)
|
||||
continue;
|
||||
|
||||
if(recording_destination->first_frame_ts_filepath && !recording_destination->first_frame_ts_written) {
|
||||
gsr_write_first_frame_timestamp_file(recording_destination->first_frame_ts_filepath);
|
||||
recording_destination->first_frame_ts_written = true;
|
||||
}
|
||||
|
||||
av_packet->pts = pts - recording_destination->start_pts;
|
||||
av_packet->dts = pts - recording_destination->start_pts;
|
||||
|
||||
@@ -148,6 +190,8 @@ size_t gsr_encoder_add_recording_destination(gsr_encoder *self, AVCodecContext *
|
||||
recording_destination->stream = stream;
|
||||
recording_destination->start_pts = start_pts;
|
||||
recording_destination->has_received_keyframe = false;
|
||||
recording_destination->first_frame_ts_filepath = NULL;
|
||||
recording_destination->first_frame_ts_written = false;
|
||||
|
||||
++self->recording_destination_id_counter;
|
||||
++self->num_recording_destinations;
|
||||
@@ -161,6 +205,9 @@ bool gsr_encoder_remove_recording_destination(gsr_encoder *self, size_t id) {
|
||||
pthread_mutex_lock(&self->file_write_mutex);
|
||||
for(size_t i = 0; i < self->num_recording_destinations; ++i) {
|
||||
if(self->recording_destinations[i].id == id) {
|
||||
free(self->recording_destinations[i].first_frame_ts_filepath);
|
||||
self->recording_destinations[i].first_frame_ts_filepath = NULL;
|
||||
self->recording_destinations[i].first_frame_ts_written = false;
|
||||
self->recording_destinations[i] = self->recording_destinations[self->num_recording_destinations - 1];
|
||||
--self->num_recording_destinations;
|
||||
found = true;
|
||||
@@ -170,3 +217,26 @@ bool gsr_encoder_remove_recording_destination(gsr_encoder *self, size_t id) {
|
||||
pthread_mutex_unlock(&self->file_write_mutex);
|
||||
return found;
|
||||
}
|
||||
|
||||
bool gsr_encoder_set_recording_destination_first_frame_ts_filepath(gsr_encoder *self, size_t id, const char *filepath) {
|
||||
if(!filepath)
|
||||
return false;
|
||||
|
||||
bool found = false;
|
||||
pthread_mutex_lock(&self->file_write_mutex);
|
||||
for(size_t i = 0; i < self->num_recording_destinations; ++i) {
|
||||
if(self->recording_destinations[i].id == id) {
|
||||
char *filepath_copy = strdup(filepath);
|
||||
if(!filepath_copy)
|
||||
break;
|
||||
|
||||
free(self->recording_destinations[i].first_frame_ts_filepath);
|
||||
self->recording_destinations[i].first_frame_ts_filepath = filepath_copy;
|
||||
self->recording_destinations[i].first_frame_ts_written = false;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&self->file_write_mutex);
|
||||
return found;
|
||||
}
|
||||
|
||||
185
src/main.cpp
185
src/main.cpp
@@ -1888,23 +1888,31 @@ static void camera_query_callback(const char *path, const gsr_capture_v4l2_suppo
|
||||
printf("%s|%ux%u@%uhz|%s\n", path, setup->resolution.width, setup->resolution.height, gsr_capture_v4l2_framerate_to_number(setup->framerate), gsr_capture_v4l2_pixfmt_to_string(setup->pixfmt));
|
||||
}
|
||||
|
||||
static void list_supported_capture_options(const gsr_window *window, const char *card_path, bool list_monitors) {
|
||||
// Returns the number of monitors found
|
||||
static int list_monitors(const gsr_window *window, const char *card_path) {
|
||||
capture_options_callback options;
|
||||
options.window = window;
|
||||
options.num_monitors = 0;
|
||||
|
||||
const bool is_x11 = gsr_window_get_display_server(window) == GSR_DISPLAY_SERVER_X11;
|
||||
const gsr_connection_type connection_type = is_x11 ? GSR_CONNECTION_X11 : GSR_CONNECTION_DRM;
|
||||
for_each_active_monitor_output(window, card_path, connection_type, output_monitor_info, &options);
|
||||
|
||||
return options.num_monitors;
|
||||
}
|
||||
|
||||
static void list_supported_capture_options(const gsr_window *window, const char *card_path, bool do_list_monitors) {
|
||||
const bool wayland = gsr_window_get_display_server(window) == GSR_DISPLAY_SERVER_WAYLAND;
|
||||
if(!wayland) {
|
||||
puts("window");
|
||||
puts("focused");
|
||||
}
|
||||
|
||||
capture_options_callback options;
|
||||
options.window = window;
|
||||
options.num_monitors = 0;
|
||||
if(list_monitors) {
|
||||
const bool is_x11 = gsr_window_get_display_server(window) == GSR_DISPLAY_SERVER_X11;
|
||||
const gsr_connection_type connection_type = is_x11 ? GSR_CONNECTION_X11 : GSR_CONNECTION_DRM;
|
||||
for_each_active_monitor_output(window, card_path, connection_type, output_monitor_info, &options);
|
||||
}
|
||||
int num_monitors = 0;
|
||||
if(do_list_monitors)
|
||||
num_monitors = list_monitors(window, card_path);
|
||||
|
||||
if(options.num_monitors > 0)
|
||||
if(num_monitors > 0)
|
||||
puts("region");
|
||||
|
||||
gsr_capture_v4l2_list_devices(camera_query_callback, NULL);
|
||||
@@ -1933,11 +1941,20 @@ static void version_command(void *userdata) {
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
static void info_command(void *userdata) {
|
||||
(void)userdata;
|
||||
struct WindowingSetup {
|
||||
Display *dpy;
|
||||
gsr_window *window;
|
||||
gsr_egl egl;
|
||||
bool list_monitors;
|
||||
};
|
||||
|
||||
static WindowingSetup setup_windowing(bool setup_egl) {
|
||||
WindowingSetup setup;
|
||||
memset(&setup, 0, sizeof(setup));
|
||||
|
||||
bool wayland = false;
|
||||
Display *dpy = XOpenDisplay(nullptr);
|
||||
if (!dpy) {
|
||||
setup.dpy = XOpenDisplay(nullptr);
|
||||
if (!setup.dpy) {
|
||||
wayland = true;
|
||||
fprintf(stderr, "gsr warning: failed to connect to the X server. Assuming wayland is running without Xwayland\n");
|
||||
}
|
||||
@@ -1946,7 +1963,7 @@ static void info_command(void *userdata) {
|
||||
XSetIOErrorHandler(x11_io_error_handler);
|
||||
|
||||
if(!wayland)
|
||||
wayland = is_xwayland(dpy);
|
||||
wayland = is_xwayland(setup.dpy);
|
||||
|
||||
if(!wayland && is_using_prime_run()) {
|
||||
// Disable prime-run and similar options as it doesn't work, the monitor to capture has to be run on the same device.
|
||||
@@ -1956,46 +1973,56 @@ static void info_command(void *userdata) {
|
||||
disable_prime_run();
|
||||
}
|
||||
|
||||
gsr_window *window = gsr_window_create(dpy, wayland);
|
||||
if(!window) {
|
||||
setup.window = gsr_window_create(setup.dpy, wayland);
|
||||
if(!setup.window) {
|
||||
fprintf(stderr, "gsr error: failed to create window\n");
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
gsr_egl egl;
|
||||
if(!gsr_egl_load(&egl, window, false, false)) {
|
||||
fprintf(stderr, "gsr error: failed to load opengl\n");
|
||||
_exit(22);
|
||||
}
|
||||
setup.list_monitors = true;
|
||||
|
||||
bool list_monitors = true;
|
||||
egl.card_path[0] = '\0';
|
||||
if(monitor_capture_use_drm(window, egl.gpu_info.vendor)) {
|
||||
// TODO: Allow specifying another card, and in other places
|
||||
if(!gsr_get_valid_card_path(&egl, egl.card_path, true)) {
|
||||
fprintf(stderr, "gsr error: no /dev/dri/cardX device found. Make sure that you have at least one monitor connected\n");
|
||||
list_monitors = false;
|
||||
if(setup_egl) {
|
||||
if(!gsr_egl_load(&setup.egl, setup.window, false, false)) {
|
||||
fprintf(stderr, "gsr error: failed to load opengl\n");
|
||||
_exit(22);
|
||||
}
|
||||
|
||||
setup.egl.card_path[0] = '\0';
|
||||
if(monitor_capture_use_drm(setup.window, setup.egl.gpu_info.vendor)) {
|
||||
// TODO: Allow specifying another card, and in other places
|
||||
if(!gsr_get_valid_card_path(&setup.egl, setup.egl.card_path, true)) {
|
||||
fprintf(stderr, "gsr error: no /dev/dri/cardX device found. Make sure that you have at least one monitor connected\n");
|
||||
setup.list_monitors = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return setup;
|
||||
}
|
||||
|
||||
static void info_command(void *userdata) {
|
||||
(void)userdata;
|
||||
WindowingSetup windowing_setup = setup_windowing(true);
|
||||
const bool wayland = gsr_window_get_display_server(windowing_setup.window) == GSR_DISPLAY_SERVER_WAYLAND;
|
||||
|
||||
av_log_set_level(AV_LOG_FATAL);
|
||||
|
||||
puts("section=system_info");
|
||||
list_system_info(wayland);
|
||||
if(egl.gpu_info.is_steam_deck)
|
||||
if(windowing_setup.egl.gpu_info.is_steam_deck)
|
||||
puts("is_steam_deck|yes");
|
||||
else
|
||||
puts("is_steam_deck|no");
|
||||
printf("gsr_version|%s\n", GSR_VERSION);
|
||||
puts("section=gpu_info");
|
||||
list_gpu_info(&egl);
|
||||
list_gpu_info(&windowing_setup.egl);
|
||||
puts("section=video_codecs");
|
||||
list_supported_video_codecs(&egl, wayland);
|
||||
list_supported_video_codecs(&windowing_setup.egl, wayland);
|
||||
puts("section=image_formats");
|
||||
puts("jpeg");
|
||||
puts("png");
|
||||
puts("section=capture_options");
|
||||
list_supported_capture_options(window, egl.card_path, list_monitors);
|
||||
list_supported_capture_options(windowing_setup.window, windowing_setup.egl.card_path, windowing_setup.list_monitors);
|
||||
|
||||
fflush(stdout);
|
||||
|
||||
@@ -2058,53 +2085,30 @@ static void list_v4l2_devices(void *userdata) {
|
||||
// |card_path| can be NULL. If not NULL then |vendor| has to be valid
|
||||
static void list_capture_options_command(const char *card_path, void *userdata) {
|
||||
(void)userdata;
|
||||
bool wayland = false;
|
||||
Display *dpy = XOpenDisplay(nullptr);
|
||||
if (!dpy) {
|
||||
wayland = true;
|
||||
fprintf(stderr, "gsr warning: failed to connect to the X server. Assuming wayland is running without Xwayland\n");
|
||||
}
|
||||
WindowingSetup windowing_setup = setup_windowing(card_path != nullptr);
|
||||
|
||||
XSetErrorHandler(x11_error_handler);
|
||||
XSetIOErrorHandler(x11_io_error_handler);
|
||||
if(card_path)
|
||||
list_supported_capture_options(windowing_setup.window, card_path, true);
|
||||
else
|
||||
list_supported_capture_options(windowing_setup.window, windowing_setup.egl.card_path, windowing_setup.list_monitors);
|
||||
|
||||
if(!wayland)
|
||||
wayland = is_xwayland(dpy);
|
||||
fflush(stdout);
|
||||
|
||||
if(!wayland && is_using_prime_run()) {
|
||||
// Disable prime-run and similar options as it doesn't work, the monitor to capture has to be run on the same device.
|
||||
// This is fine on wayland since nvidia uses drm interface there and the monitor query checks the monitors connected
|
||||
// to the drm device.
|
||||
fprintf(stderr, "gsr warning: use of prime-run on X11 is not supported. Disabling prime-run\n");
|
||||
disable_prime_run();
|
||||
}
|
||||
// Not needed as this will just slow down shutdown
|
||||
//gsr_egl_unload(&egl);
|
||||
//gsr_window_destroy(&window);
|
||||
//if(dpy)
|
||||
// XCloseDisplay(dpy);
|
||||
|
||||
gsr_window *window = gsr_window_create(dpy, wayland);
|
||||
if(!window) {
|
||||
fprintf(stderr, "gsr error: failed to create window\n");
|
||||
_exit(1);
|
||||
}
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
if(card_path) {
|
||||
list_supported_capture_options(window, card_path, true);
|
||||
} else {
|
||||
gsr_egl egl;
|
||||
if(!gsr_egl_load(&egl, window, false, false)) {
|
||||
fprintf(stderr, "gsr error: failed to load opengl\n");
|
||||
_exit(1);
|
||||
}
|
||||
static void list_monitors_command(void *userdata) {
|
||||
(void)userdata;
|
||||
WindowingSetup windowing_setup = setup_windowing(true);
|
||||
|
||||
bool list_monitors = true;
|
||||
egl.card_path[0] = '\0';
|
||||
if(monitor_capture_use_drm(window, egl.gpu_info.vendor)) {
|
||||
// TODO: Allow specifying another card, and in other places
|
||||
if(!gsr_get_valid_card_path(&egl, egl.card_path, true)) {
|
||||
fprintf(stderr, "gsr error: no /dev/dri/cardX device found. Make sure that you have at least one monitor connected\n");
|
||||
list_monitors = false;
|
||||
}
|
||||
}
|
||||
list_supported_capture_options(window, egl.card_path, list_monitors);
|
||||
}
|
||||
if(windowing_setup.list_monitors)
|
||||
list_monitors(windowing_setup.window, windowing_setup.egl.card_path);
|
||||
|
||||
fflush(stdout);
|
||||
|
||||
@@ -2269,6 +2273,7 @@ static std::string region_get_data(gsr_egl *egl, vec2i *region_size, vec2i *regi
|
||||
} else {
|
||||
region_position->x -= monitor_pos.x;
|
||||
region_position->y -= monitor_pos.y;
|
||||
// Match drm plane coordinate space (1x scaling) to wayland coordinate space (which may have scaling set by user)
|
||||
region_position->x *= monitor_scale_inverted;
|
||||
region_position->y *= monitor_scale_inverted;
|
||||
|
||||
@@ -2799,15 +2804,6 @@ static bool string_to_bool(const char *str, size_t len, bool *value) {
|
||||
}
|
||||
}
|
||||
|
||||
static int clamp_scalar(int value) {
|
||||
if(value < -100)
|
||||
return -100;
|
||||
else if(value > 100)
|
||||
return 100;
|
||||
else
|
||||
return value;
|
||||
}
|
||||
|
||||
static void parse_capture_source_options(const std::string &capture_source_str, CaptureSource &capture_source) {
|
||||
bool is_first_column = true;
|
||||
|
||||
@@ -2829,9 +2825,6 @@ static void parse_capture_source_options(const std::string &capture_source_str,
|
||||
fprintf(stderr, "gsr error: invalid capture target value for option x: \"%.*s\", expected a number\n", (int)size, sub);
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
if(capture_source.pos.x_type == VVEC2I_TYPE_SCALAR)
|
||||
capture_source.pos.x = clamp_scalar(capture_source.pos.x);
|
||||
} else if(string_starts_with(sub, size, "y=")) {
|
||||
capture_source.pos.y_type = sub[size - 1] == '%' ? VVEC2I_TYPE_SCALAR : VVEC2I_TYPE_PIXELS;
|
||||
sub += 2;
|
||||
@@ -2841,8 +2834,6 @@ static void parse_capture_source_options(const std::string &capture_source_str,
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
if(capture_source.pos.y_type == VVEC2I_TYPE_SCALAR)
|
||||
capture_source.pos.y = clamp_scalar(capture_source.pos.y);
|
||||
} else if(string_starts_with(sub, size, "width=")) {
|
||||
capture_source.size.x_type = sub[size - 1] == '%' ? VVEC2I_TYPE_SCALAR : VVEC2I_TYPE_PIXELS;
|
||||
sub += 6;
|
||||
@@ -2851,9 +2842,6 @@ static void parse_capture_source_options(const std::string &capture_source_str,
|
||||
fprintf(stderr, "gsr error: invalid capture target value for option width: \"%.*s\", expected a number\n", (int)size, sub);
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
if(capture_source.size.x_type == VVEC2I_TYPE_SCALAR)
|
||||
capture_source.size.x = clamp_scalar(capture_source.size.x);
|
||||
} else if(string_starts_with(sub, size, "height=")) {
|
||||
capture_source.size.y_type = sub[size - 1] == '%' ? VVEC2I_TYPE_SCALAR : VVEC2I_TYPE_PIXELS;
|
||||
sub += 7;
|
||||
@@ -2862,9 +2850,6 @@ static void parse_capture_source_options(const std::string &capture_source_str,
|
||||
fprintf(stderr, "gsr error: invalid capture target value for option height: \"%.*s\", expected a number\n", (int)size, sub);
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
if(capture_source.size.y_type == VVEC2I_TYPE_SCALAR)
|
||||
capture_source.size.y = clamp_scalar(capture_source.size.y);
|
||||
} else if(string_starts_with(sub, size, "halign=")) {
|
||||
sub += 7;
|
||||
size -= 7;
|
||||
@@ -3701,6 +3686,7 @@ int main(int argc, char **argv) {
|
||||
arg_handlers.list_application_audio = list_application_audio_command;
|
||||
arg_handlers.list_v4l2_devices = list_v4l2_devices;
|
||||
arg_handlers.list_capture_options = list_capture_options_command;
|
||||
arg_handlers.list_monitors = list_monitors_command;
|
||||
|
||||
args_parser arg_parser;
|
||||
if(!args_parser_parse(&arg_parser, argc, argv, &arg_handlers, NULL))
|
||||
@@ -4007,7 +3993,11 @@ int main(int argc, char **argv) {
|
||||
|
||||
if(video_stream) {
|
||||
avcodec_parameters_from_context(video_stream->codecpar, video_codec_context);
|
||||
gsr_encoder_add_recording_destination(&encoder, video_codec_context, av_format_context, video_stream, 0);
|
||||
const size_t video_destination_id = gsr_encoder_add_recording_destination(&encoder, video_codec_context, av_format_context, video_stream, 0);
|
||||
if(arg_parser.write_first_frame_ts && video_destination_id != (size_t)-1) {
|
||||
std::string ts_filepath = std::string(arg_parser.filename) + ".ts";
|
||||
gsr_encoder_set_recording_destination_first_frame_ts_filepath(&encoder, video_destination_id, ts_filepath.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
int audio_max_frame_size = 1024;
|
||||
@@ -4546,6 +4536,11 @@ int main(int argc, char **argv) {
|
||||
replay_recording_start_result = start_recording_create_streams(replay_recording_filepath.c_str(), arg_parser, video_codec_context, audio_tracks, hdr, video_sources);
|
||||
if(replay_recording_start_result.av_format_context) {
|
||||
const size_t video_recording_destination_id = gsr_encoder_add_recording_destination(&encoder, video_codec_context, replay_recording_start_result.av_format_context, replay_recording_start_result.video_stream, video_frame->pts);
|
||||
if(arg_parser.write_first_frame_ts && video_recording_destination_id != (size_t)-1) {
|
||||
std::string ts_filepath = replay_recording_filepath + ".ts";
|
||||
gsr_encoder_set_recording_destination_first_frame_ts_filepath(&encoder, video_recording_destination_id, ts_filepath.c_str());
|
||||
}
|
||||
|
||||
if(video_recording_destination_id != (size_t)-1)
|
||||
replay_recording_items.push_back(video_recording_destination_id);
|
||||
|
||||
|
||||
@@ -532,31 +532,36 @@ static bool spa_video_format_get_modifiers(gsr_pipewire_video *self, const enum
|
||||
static void gsr_pipewire_video_init_modifiers(gsr_pipewire_video *self) {
|
||||
for(size_t i = 0; i < GSR_PIPEWIRE_VIDEO_NUM_VIDEO_FORMATS; i++) {
|
||||
self->supported_video_formats[i].format = video_formats[i];
|
||||
int32_t num_modifiers = 0;
|
||||
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);
|
||||
int32_t num_modifiers_video_format = 0;
|
||||
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_video_format);
|
||||
self->supported_video_formats[i].modifiers_index = self->num_modifiers;
|
||||
self->supported_video_formats[i].modifiers_size = num_modifiers;
|
||||
self->num_modifiers += num_modifiers;
|
||||
self->supported_video_formats[i].modifiers_size = num_modifiers_video_format;
|
||||
self->num_modifiers += num_modifiers_video_format;
|
||||
}
|
||||
}
|
||||
|
||||
static void gsr_pipewire_video_format_remove_modifier(gsr_pipewire_video *self, gsr_video_format *video_format, uint64_t modifier) {
|
||||
/* Returns the number of modifiers */
|
||||
static size_t gsr_pipewire_video_format_remove_modifier(gsr_pipewire_video *self, gsr_video_format *video_format, uint64_t modifier) {
|
||||
for(size_t i = 0; i < video_format->modifiers_size; ++i) {
|
||||
if(self->modifiers[video_format->modifiers_index + i] != modifier)
|
||||
continue;
|
||||
if(self->modifiers[video_format->modifiers_index + i] == modifier) {
|
||||
for(size_t j = i + 1; j < video_format->modifiers_size; ++j) {
|
||||
self->modifiers[video_format->modifiers_index + j - 1] = self->modifiers[video_format->modifiers_index + j];
|
||||
}
|
||||
|
||||
for(size_t j = i + 1; j < video_format->modifiers_size; ++j) {
|
||||
self->modifiers[j - 1] = self->modifiers[j];
|
||||
--video_format->modifiers_size;
|
||||
break;
|
||||
}
|
||||
--video_format->modifiers_size;
|
||||
return;
|
||||
}
|
||||
return video_format->modifiers_size;
|
||||
}
|
||||
|
||||
static void gsr_pipewire_video_remove_modifier(gsr_pipewire_video *self, uint64_t modifier) {
|
||||
self->num_modifiers = 0;
|
||||
for(size_t i = 0; i < GSR_PIPEWIRE_VIDEO_NUM_VIDEO_FORMATS; i++) {
|
||||
gsr_video_format *video_format = &self->supported_video_formats[i];
|
||||
gsr_pipewire_video_format_remove_modifier(self, video_format, modifier);
|
||||
const size_t num_modifiers_video_format = gsr_pipewire_video_format_remove_modifier(self, video_format, modifier);
|
||||
video_format->modifiers_index = self->num_modifiers;
|
||||
self->num_modifiers += num_modifiers_video_format;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -687,6 +692,7 @@ bool gsr_pipewire_video_init(gsr_pipewire_video *self, int pipewire_fd, uint32_t
|
||||
self->video_info.fps_num = fps;
|
||||
self->video_info.fps_den = 1;
|
||||
self->cursor.visible = capture_cursor;
|
||||
self->renegotiated_modifier = DRM_FORMAT_MOD_INVALID;
|
||||
|
||||
if(!gsr_pipewire_video_setup_stream(self)) {
|
||||
gsr_pipewire_video_deinit(self);
|
||||
@@ -792,8 +798,14 @@ static EGLImage gsr_pipewire_video_create_egl_image_with_fallback(gsr_pipewire_v
|
||||
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;
|
||||
image = gsr_pipewire_video_create_egl_image(self, fds, offsets, pitches, modifiers, false);
|
||||
} else if(self->renegotiated_modifier == self->format.info.raw.modifier) {
|
||||
fprintf(stderr, "gsr error: gsr_pipewire_video_create_egl_image_with_fallback: modifier 0x%" PRIx64 " failed again after renegotiation, trying without modifiers as last resort\n", self->format.info.raw.modifier);
|
||||
image = gsr_pipewire_video_create_egl_image(self, fds, offsets, pitches, modifiers, false);
|
||||
if(image)
|
||||
self->no_modifiers_fallback = true;
|
||||
} else {
|
||||
fprintf(stderr, "gsr error: gsr_pipewire_video_create_egl_image_with_fallback: failed to create egl image with modifiers, renegotiating with a different modifier\n");
|
||||
fprintf(stderr, "gsr error: gsr_pipewire_video_create_egl_image_with_fallback: failed to create egl image with modifier 0x%" PRIx64 ", renegotiating with a different modifier\n", self->format.info.raw.modifier);
|
||||
self->renegotiated_modifier = self->format.info.raw.modifier;
|
||||
self->negotiated = false;
|
||||
pw_thread_loop_lock(self->thread_loop);
|
||||
gsr_pipewire_video_remove_modifier(self, self->format.info.raw.modifier);
|
||||
|
||||
Reference in New Issue
Block a user