Add -s option to set record area size, fix workspace switch freezing recording on bspwm, make background black after resize

This commit is contained in:
dec05eba 2022-03-22 23:08:39 +01:00
parent 821cacf9ff
commit 36fd4516db
2 changed files with 48 additions and 20 deletions

View File

@ -36,7 +36,6 @@ This gpu-screen-recorder keeps the window image on the GPU and sends it directly
FFMPEG only uses the GPU with CUDA when doing transcoding from an input video to an output video, and not when recording the screen when using x11grab. So FFMPEG has the same fps drop issues that OBS has. FFMPEG only uses the GPU with CUDA when doing transcoding from an input video to an output video, and not when recording the screen when using x11grab. So FFMPEG has the same fps drop issues that OBS has.
# TODO # TODO
* Scale video when the window is rescaled.
* Support AMD and Intel, using VAAPI. cuda and vaapi should be loaded at runtime using dlopen instead of linking to those * Support AMD and Intel, using VAAPI. cuda and vaapi should be loaded at runtime using dlopen instead of linking to those
libraries at compile-time. libraries at compile-time.
* Clean up the code! * Clean up the code!

View File

@ -413,7 +413,7 @@ static AVStream *add_audio_stream(AVFormatContext *av_format_context, AVCodecCon
static AVStream *add_video_stream(AVFormatContext *av_format_context, AVCodecContext **video_codec_context, static AVStream *add_video_stream(AVFormatContext *av_format_context, AVCodecContext **video_codec_context,
VideoQuality video_quality, VideoQuality video_quality,
int texture_width, int texture_height, int record_width, int record_height,
int fps, bool use_hevc) { int fps, bool use_hevc) {
const AVCodec *codec = avcodec_find_encoder_by_name(use_hevc ? "hevc_nvenc" : "h264_nvenc"); const AVCodec *codec = avcodec_find_encoder_by_name(use_hevc ? "hevc_nvenc" : "h264_nvenc");
if (!codec) { if (!codec) {
@ -441,8 +441,8 @@ static AVStream *add_video_stream(AVFormatContext *av_format_context, AVCodecCon
assert(codec->type == AVMEDIA_TYPE_VIDEO); assert(codec->type == AVMEDIA_TYPE_VIDEO);
codec_context->codec_id = codec->id; codec_context->codec_id = codec->id;
fprintf(stderr, "codec id: %d\n", codec->id); fprintf(stderr, "codec id: %d\n", codec->id);
codec_context->width = texture_width & ~1; codec_context->width = record_width & ~1;
codec_context->height = texture_height & ~1; codec_context->height = record_height & ~1;
codec_context->bit_rate = 7500000 + (codec_context->width * codec_context->height) / 2; codec_context->bit_rate = 7500000 + (codec_context->width * codec_context->height) / 2;
// Timebase: This is the fundamental unit of time (in seconds) in terms // Timebase: This is the fundamental unit of time (in seconds) in terms
// of which frame timestamps are represented. For fixed-fps content, // of which frame timestamps are represented. For fixed-fps content,
@ -623,7 +623,7 @@ static void usage() {
fprintf(stderr, "usage: gpu-screen-recorder -w <window_id> -c <container_format> -f <fps> [-a <audio_input>] [-q <quality>] [-r <replay_buffer_size_sec>] [-o <output_file>]\n"); fprintf(stderr, "usage: gpu-screen-recorder -w <window_id> -c <container_format> -f <fps> [-a <audio_input>] [-q <quality>] [-r <replay_buffer_size_sec>] [-o <output_file>]\n");
fprintf(stderr, "OPTIONS:\n"); fprintf(stderr, "OPTIONS:\n");
fprintf(stderr, " -w Window to record or a display or \"screen\". The display is the display name in xrandr and if \"screen\" is selected then all displays are recorded and they are recorded in h265 (aka hevc). Recording a display requires a gpu with NvFBC support.\n"); fprintf(stderr, " -w Window to record or a display or \"screen\". The display is the display name in xrandr and if \"screen\" is selected then all displays are recorded and they are recorded in h265 (aka hevc). Recording a display requires a gpu with NvFBC support.\n");
//fprintf(stderr, " -s The screen region to capture in format WxH+X+Y. This is only applicable when -w is a display or \"screen\". Optional, the entire window/display/screen is recorded by default.\n"); fprintf(stderr, " -s The size (area) to record at in the format WxH, for example 1920x1080. Usually you want to set this to the size of the window. Optional, by default the size of the window, monitor or screen is used (which is passed to -w).\n");
fprintf(stderr, " -c Container format for output file, for example mp4, or flv.\n"); fprintf(stderr, " -c Container format for output file, for example mp4, or flv.\n");
fprintf(stderr, " -f Framerate to record at. Clamped to [1,250].\n"); fprintf(stderr, " -f Framerate to record at. Clamped to [1,250].\n");
fprintf(stderr, " -a Audio device to record from (pulse audio device). Optional, disabled by default.\n"); fprintf(stderr, " -a Audio device to record from (pulse audio device). Optional, disabled by default.\n");
@ -675,6 +675,7 @@ int main(int argc, char **argv) {
//{ "-s", Arg { nullptr, true } }, //{ "-s", Arg { nullptr, true } },
{ "-c", Arg { nullptr, false } }, { "-c", Arg { nullptr, false } },
{ "-f", Arg { nullptr, false } }, { "-f", Arg { nullptr, false } },
{ "-s", Arg { nullptr, true } },
{ "-a", Arg { nullptr, true } }, { "-a", Arg { nullptr, true } },
{ "-q", Arg { nullptr, true } }, { "-q", Arg { nullptr, true } },
{ "-o", Arg { nullptr, true } }, { "-o", Arg { nullptr, true } },
@ -787,13 +788,24 @@ int main(int argc, char **argv) {
if(!nv_fbc_library.create(window_str, fps, &window_width, &window_height, region_x, region_y, region_width, region_height)) if(!nv_fbc_library.create(window_str, fps, &window_width, &window_height, region_x, region_y, region_width, region_height))
return 1; return 1;
} else { } else {
errno = 0;
src_window_id = strtol(window_str, nullptr, 0); src_window_id = strtol(window_str, nullptr, 0);
if(src_window_id == None && errno == EINVAL) { if(src_window_id == None || errno == EINVAL) {
fprintf(stderr, "Invalid window number %s\n", window_str); fprintf(stderr, "Invalid window number %s\n", window_str);
usage(); usage();
} }
} }
int record_width = window_width;
int record_height = window_height;
const char *record_area = args["-s"].value;
if(record_area) {
if(sscanf(record_area, "%dx%d", &record_width, &record_height) != 2) {
fprintf(stderr, "Invalid value for -s '%s', expected a value in format WxH\n", record_area);
return 1;
}
}
const char *filename = args["-o"].value; const char *filename = args["-o"].value;
if(!filename) if(!filename)
filename = "/dev/stdout"; filename = "/dev/stdout";
@ -823,6 +835,9 @@ int main(int argc, char **argv) {
return 1; return 1;
} }
window_width = attr.width;
window_height = attr.height;
XCompositeRedirectWindow(dpy, src_window_id, CompositeRedirectAutomatic); XCompositeRedirectWindow(dpy, src_window_id, CompositeRedirectAutomatic);
// glXMakeContextCurrent(Display *dpy, GLXDrawable draw, GLXDrawable read, // glXMakeContextCurrent(Display *dpy, GLXDrawable draw, GLXDrawable read,
@ -868,6 +883,12 @@ int main(int argc, char **argv) {
src_window_id); src_window_id);
return 1; return 1;
} }
if(!record_area) {
record_width = window_pixmap.texture_width;
record_height = window_pixmap.texture_height;
fprintf(stderr, "Record size: %dx%x\n", record_width, record_height);
}
} else { } else {
window_pixmap.texture_id = 0; window_pixmap.texture_id = 0;
window_pixmap.target_texture_id = 0; window_pixmap.target_texture_id = 0;
@ -896,7 +917,7 @@ int main(int argc, char **argv) {
AVCodecContext *video_codec_context; AVCodecContext *video_codec_context;
AVStream *video_stream = AVStream *video_stream =
add_video_stream(av_format_context, &video_codec_context, quality, window_pixmap.texture_width, window_pixmap.texture_height, fps, strcmp(window_str, "screen") == 0); add_video_stream(av_format_context, &video_codec_context, quality, record_width, record_height, fps, strcmp(window_str, "screen") == 0);
if (!video_stream) { if (!video_stream) {
fprintf(stderr, "Error: Failed to create video stream\n"); fprintf(stderr, "Error: Failed to create video stream\n");
return 1; return 1;
@ -957,7 +978,7 @@ int main(int argc, char **argv) {
// avcodec_close(av_codec_context); // avcodec_close(av_codec_context);
if(dpy) if(dpy)
XSelectInput(dpy, src_window_id, StructureNotifyMask); XSelectInput(dpy, src_window_id, StructureNotifyMask | VisibilityChangeMask);
/* /*
int damage_event; int damage_event;
@ -1013,14 +1034,15 @@ int main(int argc, char **argv) {
exit(1); exit(1);
} }
if(dpy) { if(window_pixmap.texture_width < record_width)
XWindowAttributes xwa; frame->width = window_pixmap.texture_width & ~1;
XGetWindowAttributes(dpy, src_window_id, &xwa); else
window_width = xwa.width; frame->width = record_width & ~1;
window_height = xwa.height;
} if(window_pixmap.texture_height < record_height)
int original_texture_width = window_pixmap.texture_width; frame->height = window_pixmap.texture_height & ~1;
int original_texture_height = window_pixmap.texture_height; else
frame->height = record_height & ~1;
std::mutex write_output_mutex; std::mutex write_output_mutex;
std::thread audio_thread; std::thread audio_thread;
@ -1095,6 +1117,11 @@ int main(int argc, char **argv) {
redraw = true; redraw = true;
if(src_window_id) { if(src_window_id) {
if (XCheckTypedWindowEvent(dpy, src_window_id, VisibilityNotify, &e)) {
window_resize_timer = glfwGetTime();
window_resized = true;
}
if (XCheckTypedWindowEvent(dpy, src_window_id, ConfigureNotify, &e) && e.xconfigure.window == src_window_id) { if (XCheckTypedWindowEvent(dpy, src_window_id, ConfigureNotify, &e) && e.xconfigure.window == src_window_id) {
// Window resize // Window resize
if(e.xconfigure.width != window_width || e.xconfigure.height != window_height) { if(e.xconfigure.width != window_width || e.xconfigure.height != window_height) {
@ -1153,15 +1180,17 @@ int main(int argc, char **argv) {
frame->pts = frame_count; frame->pts = frame_count;
if(window_pixmap.texture_width < original_texture_width) if(window_pixmap.texture_width < record_width)
frame->width = window_pixmap.texture_width & ~1; frame->width = window_pixmap.texture_width & ~1;
else else
frame->width = original_texture_width & ~1; frame->width = record_width & ~1;
if(window_pixmap.texture_height < original_texture_height) if(window_pixmap.texture_height < record_height)
frame->height = window_pixmap.texture_height & ~1; frame->height = window_pixmap.texture_height & ~1;
else else
frame->height = original_texture_height & ~1; frame->height = record_height & ~1;
cuMemsetD8((CUdeviceptr)frame->data[0], 0, record_width * record_height * 4);
} }
} }