Make audio recording optional
This commit is contained in:
parent
bf9df281d2
commit
78ab032cf5
@ -11,11 +11,12 @@ When recording a 4k game, fps drops from 30 to 7 when using OBS Studio, however
|
|||||||
the fps remains at 30.
|
the fps remains at 30.
|
||||||
|
|
||||||
# Installation
|
# Installation
|
||||||
gpu screen recorder can be built using [sibs](https://github.com/DEC05EBA/sibs) or if you are running Arch Linux, then you can find it on aur under the name gpu-screen-recorder-git (`yay -S gpu-screen-recorder-git`).
|
gpu screen recorder can be built using [sibs](https://git.dec05eba.com/sibs) or if you are running Arch Linux, then you can find it on aur under the name gpu-screen-recorder-git (`yay -S gpu-screen-recorder-git`).
|
||||||
|
|
||||||
# How to use
|
# How to use
|
||||||
Run `interactive.sh` or run gpu-screen-recorder directly, for example: `gpu-screen-recorder -w 0x1c00001 -c mp4 -f 60 -a bluez_sink.00_18_09_8A_07_93.a2dp_sink.monitor > test_video.mp4`\
|
Run `interactive.sh` or run gpu-screen-recorder directly, for example: `gpu-screen-recorder -w 0x1c00001 -c mp4 -f 60 -a bluez_sink.00_18_09_8A_07_93.a2dp_sink.monitor > test_video.mp4`\
|
||||||
Then stop the screen recorder with Ctrl+C.
|
Then stop the screen recorder with Ctrl+C.\
|
||||||
|
There is also a gui for the gpu-screen-recorder, called [gpu-screen-recorder-gtk](https://git.dec05eba.com/gpu-screen-recorder-gtk/).
|
||||||
|
|
||||||
# Demo
|
# Demo
|
||||||
[![Click here to watch a demo video on youtube](https://img.youtube.com/vi/n5tm0g01n6A/0.jpg)](https://www.youtube.com/watch?v=n5tm0g01n6A)
|
[![Click here to watch a demo video on youtube](https://img.youtube.com/vi/n5tm0g01n6A/0.jpg)](https://www.youtube.com/watch?v=n5tm0g01n6A)
|
||||||
|
172
src/main.cpp
172
src/main.cpp
@ -565,44 +565,42 @@ static void int_handler(int dummy) {
|
|||||||
running = 0;
|
running = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct Arg {
|
||||||
|
const char *value;
|
||||||
|
bool optional;
|
||||||
|
};
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(int argc, char **argv) {
|
||||||
signal(SIGINT, int_handler);
|
signal(SIGINT, int_handler);
|
||||||
|
|
||||||
std::map<std::string, std::string> args = {
|
std::map<std::string, Arg> args = {
|
||||||
{ "-w", "" },
|
{ "-w", Arg { nullptr, false } },
|
||||||
{ "-c", "" },
|
{ "-c", Arg { nullptr, false } },
|
||||||
{ "-f", "" },
|
{ "-f", Arg { nullptr, false } },
|
||||||
{ "-a", "" },
|
{ "-a", Arg { nullptr, true } }
|
||||||
};
|
};
|
||||||
|
|
||||||
for(int i = 1; i < argc; i += 2) {
|
for(int i = 1; i < argc - 1; i += 2) {
|
||||||
bool valid_arg = false;
|
auto it = args.find(argv[i]);
|
||||||
for(auto &it : args) {
|
if(it == args.end()) {
|
||||||
if(strcmp(argv[i], it.first.c_str()) == 0) {
|
|
||||||
it.second = argv[i + 1];
|
|
||||||
valid_arg = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!valid_arg) {
|
|
||||||
fprintf(stderr, "Invalid argument '%s'\n", argv[i]);
|
fprintf(stderr, "Invalid argument '%s'\n", argv[i]);
|
||||||
usage();
|
usage();
|
||||||
}
|
}
|
||||||
|
it->second.value = argv[i + 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
for(auto &it : args) {
|
for(auto &it : args) {
|
||||||
if(it.second.empty()) {
|
if(!it.second.optional && !it.second.value) {
|
||||||
fprintf(stderr, "Missing argument '%s'\n", it.first.c_str());
|
fprintf(stderr, "Missing argument '%s'\n", it.first.c_str());
|
||||||
usage();
|
usage();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Window src_window_id = strtol(args["-w"].c_str(), nullptr, 0);
|
Window src_window_id = strtol(args["-w"].value, nullptr, 0);
|
||||||
const char *container_format = args["-c"].c_str();
|
const char *container_format = args["-c"].value;
|
||||||
int fps = atoi(args["-f"].c_str());
|
int fps = atoi(args["-f"].value);
|
||||||
if(fps <= 0 || fps > 255) {
|
if(fps <= 0 || fps > 255) {
|
||||||
fprintf(stderr, "invalid fps argument: %s\n", args["-f"].c_str());
|
fprintf(stderr, "invalid fps argument: %s\n", args["-f"].value);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -807,68 +805,75 @@ int main(int argc, char **argv) {
|
|||||||
int window_width = xwa.width;
|
int window_width = xwa.width;
|
||||||
int window_height = xwa.height;
|
int window_height = xwa.height;
|
||||||
|
|
||||||
|
std::mutex write_output_mutex;
|
||||||
|
std::thread audio_thread;
|
||||||
|
|
||||||
SoundDevice sound_device;
|
SoundDevice sound_device;
|
||||||
if(sound_device_get_by_name(&sound_device, args["-a"].c_str(), audio_stream->codec->channels, audio_stream->codec->frame_size) != 0) {
|
Arg &audio_input_arg = args["-a"];
|
||||||
fprintf(stderr, "failed to get 'pulse' sound device\n");
|
if(audio_input_arg.value) {
|
||||||
exit(1);
|
if(sound_device_get_by_name(&sound_device, audio_input_arg.value, audio_stream->codec->channels, audio_stream->codec->frame_size) != 0) {
|
||||||
|
fprintf(stderr, "failed to get 'pulse' sound device\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int audio_buffer_size = av_samples_get_buffer_size(NULL, audio_stream->codec->channels, audio_stream->codec->frame_size, audio_stream->codec->sample_fmt, 1);
|
||||||
|
uint8_t *audio_frame_buf = (uint8_t *)av_malloc(audio_buffer_size);
|
||||||
|
avcodec_fill_audio_frame(audio_frame, audio_stream->codec->channels, audio_stream->codec->sample_fmt, (const uint8_t*)audio_frame_buf, audio_buffer_size, 1);
|
||||||
|
|
||||||
|
audio_thread = std::thread([audio_buffer_size](AVFormatContext *av_format_context, AVStream *audio_stream, uint8_t *audio_frame_buf, SoundDevice *sound_device, AVFrame *audio_frame, std::mutex *write_output_mutex) mutable {
|
||||||
|
AVPacket audio_packet;
|
||||||
|
if(av_new_packet(&audio_packet, audio_buffer_size) != 0) {
|
||||||
|
fprintf(stderr, "Failed to create audio packet\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
SwrContext *swr = swr_alloc();
|
||||||
|
if(!swr) {
|
||||||
|
fprintf(stderr, "Failed to create SwrContext\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
av_opt_set_int(swr, "in_channel_layout", audio_stream->codec->channel_layout, 0);
|
||||||
|
av_opt_set_int(swr, "out_channel_layout", audio_stream->codec->channel_layout, 0);
|
||||||
|
av_opt_set_int(swr, "in_sample_rate", audio_stream->codec->sample_rate, 0);
|
||||||
|
av_opt_set_int(swr, "out_sample_rate", audio_stream->codec->sample_rate, 0);
|
||||||
|
av_opt_set_sample_fmt(swr, "in_sample_fmt", AV_SAMPLE_FMT_S16, 0);
|
||||||
|
av_opt_set_sample_fmt(swr, "out_sample_fmt", AV_SAMPLE_FMT_FLTP, 0);
|
||||||
|
swr_init(swr);
|
||||||
|
|
||||||
|
while(running) {
|
||||||
|
void *sound_buffer;
|
||||||
|
int sound_buffer_size = sound_device_read_next_chunk(sound_device, &sound_buffer);
|
||||||
|
if(sound_buffer_size >= 0) {
|
||||||
|
// TODO: Instead of converting audio, get float audio from alsa. Or does alsa do conversion internally to get this format?
|
||||||
|
swr_convert(swr, &audio_frame_buf, audio_frame->nb_samples, (const uint8_t**)&sound_buffer, sound_buffer_size);
|
||||||
|
audio_frame->extended_data = &audio_frame_buf;
|
||||||
|
// TODO: Fix this. Warning from ffmpeg:
|
||||||
|
// Timestamps are unset in a packet for stream 1. This is deprecated and will stop working in the future. Fix your code to set the timestamps properly
|
||||||
|
//audio_frame->pts=audio_frame_index*100;
|
||||||
|
//++audio_frame_index;
|
||||||
|
|
||||||
|
int got_frame = 0;
|
||||||
|
int ret = avcodec_encode_audio2(audio_stream->codec, &audio_packet, audio_frame, &got_frame);
|
||||||
|
if(ret < 0){
|
||||||
|
printf("Failed to encode!\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (got_frame==1){
|
||||||
|
//printf("Succeed to encode 1 frame! \tsize:%5d\n",pkt.size);
|
||||||
|
audio_packet.stream_index = audio_stream->index;
|
||||||
|
std::lock_guard<std::mutex> lock(*write_output_mutex);
|
||||||
|
ret = av_write_frame(av_format_context, &audio_packet);
|
||||||
|
av_free_packet(&audio_packet);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "failed to read sound from device, error: %d\n", sound_buffer_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
swr_free(&swr);
|
||||||
|
}, av_format_context, audio_stream, audio_frame_buf, &sound_device, audio_frame, &write_output_mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
int audio_buffer_size = av_samples_get_buffer_size(NULL, audio_stream->codec->channels, audio_stream->codec->frame_size, audio_stream->codec->sample_fmt, 1);
|
|
||||||
uint8_t *audio_frame_buf = (uint8_t *)av_malloc(audio_buffer_size);
|
|
||||||
avcodec_fill_audio_frame(audio_frame, audio_stream->codec->channels, audio_stream->codec->sample_fmt, (const uint8_t*)audio_frame_buf, audio_buffer_size, 1);
|
|
||||||
|
|
||||||
AVPacket audio_packet;
|
|
||||||
av_new_packet(&audio_packet, audio_buffer_size);
|
|
||||||
|
|
||||||
std::mutex write_output_mutex;
|
|
||||||
|
|
||||||
std::thread audio_thread([](AVFormatContext *av_format_context, AVStream *audio_stream, AVPacket *audio_packet, uint8_t *audio_frame_buf, SoundDevice *sound_device, AVFrame *audio_frame, std::mutex *write_output_mutex) mutable {
|
|
||||||
SwrContext *swr = swr_alloc();
|
|
||||||
if(!swr) {
|
|
||||||
fprintf(stderr, "Failed to create SwrContext\n");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
av_opt_set_int(swr, "in_channel_layout", audio_stream->codec->channel_layout, 0);
|
|
||||||
av_opt_set_int(swr, "out_channel_layout", audio_stream->codec->channel_layout, 0);
|
|
||||||
av_opt_set_int(swr, "in_sample_rate", audio_stream->codec->sample_rate, 0);
|
|
||||||
av_opt_set_int(swr, "out_sample_rate", audio_stream->codec->sample_rate, 0);
|
|
||||||
av_opt_set_sample_fmt(swr, "in_sample_fmt", AV_SAMPLE_FMT_S16, 0);
|
|
||||||
av_opt_set_sample_fmt(swr, "out_sample_fmt", AV_SAMPLE_FMT_FLTP, 0);
|
|
||||||
swr_init(swr);
|
|
||||||
|
|
||||||
while(running) {
|
|
||||||
void *sound_buffer;
|
|
||||||
int sound_buffer_size = sound_device_read_next_chunk(sound_device, &sound_buffer);
|
|
||||||
if(sound_buffer_size >= 0) {
|
|
||||||
// TODO: Instead of converting audio, get float audio from alsa. Or does alsa do conversion internally to get this format?
|
|
||||||
swr_convert(swr, &audio_frame_buf, audio_frame->nb_samples, (const uint8_t**)&sound_buffer, sound_buffer_size);
|
|
||||||
audio_frame->extended_data = &audio_frame_buf;
|
|
||||||
// TODO: Fix this. Warning from ffmpeg:
|
|
||||||
// Timestamps are unset in a packet for stream 1. This is deprecated and will stop working in the future. Fix your code to set the timestamps properly
|
|
||||||
//audio_frame->pts=audio_frame_index*100;
|
|
||||||
//++audio_frame_index;
|
|
||||||
|
|
||||||
int got_frame = 0;
|
|
||||||
int ret = avcodec_encode_audio2(audio_stream->codec, audio_packet, audio_frame, &got_frame);
|
|
||||||
if(ret < 0){
|
|
||||||
printf("Failed to encode!\n");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (got_frame==1){
|
|
||||||
//printf("Succeed to encode 1 frame! \tsize:%5d\n",pkt.size);
|
|
||||||
audio_packet->stream_index = audio_stream->index;
|
|
||||||
std::lock_guard<std::mutex> lock(*write_output_mutex);
|
|
||||||
ret = av_write_frame(av_format_context, audio_packet);
|
|
||||||
av_free_packet(audio_packet);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
fprintf(stderr, "failed to read sound from device, error: %d\n", sound_buffer_size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
swr_free(&swr);
|
|
||||||
}, av_format_context, audio_stream, &audio_packet, audio_frame_buf, &sound_device, audio_frame, &write_output_mutex);
|
|
||||||
|
|
||||||
bool redraw = true;
|
bool redraw = true;
|
||||||
XEvent e;
|
XEvent e;
|
||||||
while (running) {
|
while (running) {
|
||||||
@ -1000,9 +1005,10 @@ int main(int argc, char **argv) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
running = 0;
|
running = 0;
|
||||||
audio_thread.join();
|
if(audio_input_arg.value) {
|
||||||
|
audio_thread.join();
|
||||||
sound_device_close(&sound_device);
|
sound_device_close(&sound_device);
|
||||||
|
}
|
||||||
|
|
||||||
//Flush Encoder
|
//Flush Encoder
|
||||||
#if 0
|
#if 0
|
||||||
|
Loading…
Reference in New Issue
Block a user