diff options
-rw-r--r-- | README.md | 3 | ||||
-rw-r--r-- | include/encoder/encoder.h | 44 | ||||
-rw-r--r-- | include/encoder/video/video.h | 28 | ||||
-rw-r--r-- | meson.build | 1 | ||||
-rw-r--r-- | src/encoder/encoder.c | 152 | ||||
-rw-r--r-- | src/encoder/video/video.c | 151 | ||||
-rw-r--r-- | src/main.cpp | 36 |
7 files changed, 226 insertions, 189 deletions
@@ -135,6 +135,9 @@ The file path to the saved replay is output to stdout. All other output from GPU You can also use the `-sc` option to specify a script that should be run (asynchronously) when the video has been saved and the script will have access to the location of the saved file as its first argument. This can be used for example to show a notification when a replay has been saved, to rename the video with a title that matches the game played (see `scripts/record-save-application-name.sh` as an example on how to do this on X11) or to re-encode the video.\ The replay buffer is stored in ram (as encoded video), so don't use a too large replay time and/or video quality unless you have enough ram to store it. +## Recording while using replay/streaming +You can record a regular video while using replay/streaming by launching GPU Screen Recorder with the `-ro` option to specify a directory where to save the recording.\ +To start/stop (and save) recording use the SIGRTMIN signal, for example `pkill -SIGRTMIN -f gpu-screen-recorder`. The name of the video will be displayed in stdout when saving the video. ## Controlling GPU Screen Recorder remotely To save a video in replay mode, you need to send signal SIGUSR1 to gpu screen recorder. You can do this by running `pkill -SIGUSR1 -f gpu-screen-recorder`.\ To stop recording send SIGINT to gpu screen recorder. You can do this by running `pkill -SIGINT -f gpu-screen-recorder` or pressing `Ctrl-C` in the terminal that runs gpu screen recorder. When recording a regular non-replay video this will also save the video.\ diff --git a/include/encoder/encoder.h b/include/encoder/encoder.h new file mode 100644 index 0000000..8f03149 --- /dev/null +++ b/include/encoder/encoder.h @@ -0,0 +1,44 @@ +#ifndef GSR_ENCODER_H +#define GSR_ENCODER_H + +#include "../replay_buffer.h" +#include <stdbool.h> +#include <stdint.h> +#include <stddef.h> +#include <pthread.h> + +#define GSR_MAX_RECORDING_DESTINATIONS 128 + +typedef struct AVCodecContext AVCodecContext; +typedef struct AVFormatContext AVFormatContext; +typedef struct AVStream AVStream; + +typedef struct { + size_t id; + AVCodecContext *codec_context; + AVFormatContext *format_context; + AVStream *stream; + int64_t start_pts; + bool has_received_keyframe; +} gsr_encoder_recording_destination; + +typedef struct { + gsr_replay_buffer replay_buffer; + bool has_replay_buffer; + pthread_mutex_t file_write_mutex; + bool mutex_created; + + gsr_encoder_recording_destination recording_destinations[GSR_MAX_RECORDING_DESTINATIONS]; + size_t num_recording_destinations; + size_t recording_destination_id_counter; +} gsr_encoder; + +bool gsr_encoder_init(gsr_encoder *self, size_t replay_buffer_num_packets); +void gsr_encoder_deinit(gsr_encoder *self); + +void gsr_encoder_receive_packets(gsr_encoder *self, AVCodecContext *codec_context, int64_t pts, int stream_index); +/* 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); + +#endif /* GSR_ENCODER_H */ diff --git a/include/encoder/video/video.h b/include/encoder/video/video.h index 97f63e8..7a706b5 100644 --- a/include/encoder/video/video.h +++ b/include/encoder/video/video.h @@ -2,27 +2,13 @@ #define GSR_ENCODER_VIDEO_H #include "../../color_conversion.h" -#include "../../replay_buffer.h" #include <stdbool.h> -#include <stdint.h> -#include <pthread.h> #define GSR_MAX_RECORDING_DESTINATIONS 128 typedef struct gsr_video_encoder gsr_video_encoder; typedef struct AVCodecContext AVCodecContext; -typedef struct AVFormatContext AVFormatContext; typedef struct AVFrame AVFrame; -typedef struct AVStream AVStream; - -typedef struct { - size_t id; - AVCodecContext *codec_context; - AVFormatContext *format_context; - AVStream *stream; - int64_t start_pts; - bool has_received_keyframe; -} gsr_video_encoder_recording_destination; struct gsr_video_encoder { bool (*start)(gsr_video_encoder *encoder, AVCodecContext *video_codec_context, AVFrame *frame); @@ -33,24 +19,12 @@ struct gsr_video_encoder { void *priv; bool started; - gsr_replay_buffer replay_buffer; - bool has_replay_buffer; - pthread_mutex_t file_write_mutex; - - gsr_video_encoder_recording_destination recording_destinations[GSR_MAX_RECORDING_DESTINATIONS]; - size_t num_recording_destinations; - size_t recording_destination_id; }; /* Set |replay_buffer_time_seconds| and |fps| to 0 to disable replay buffer */ -bool gsr_video_encoder_start(gsr_video_encoder *encoder, AVCodecContext *video_codec_context, AVFrame *frame, size_t replay_buffer_num_packets); +bool gsr_video_encoder_start(gsr_video_encoder *encoder, AVCodecContext *video_codec_context, AVFrame *frame); void gsr_video_encoder_destroy(gsr_video_encoder *encoder, AVCodecContext *video_codec_context); void gsr_video_encoder_copy_textures_to_frame(gsr_video_encoder *encoder, AVFrame *frame, gsr_color_conversion *color_conversion); void gsr_video_encoder_get_textures(gsr_video_encoder *encoder, unsigned int *textures, int *num_textures, gsr_destination_color *destination_color); -void gsr_video_encoder_receive_packets(gsr_video_encoder *encoder, AVCodecContext *codec_context, int64_t pts, int stream_index); -/* Returns the id to the recording destination, or -1 on error */ -size_t gsr_video_encoder_add_recording_destination(gsr_video_encoder *encoder, AVCodecContext *codec_context, AVFormatContext *format_context, AVStream *stream, int64_t start_pts); -bool gsr_video_encoder_remove_recording_destination(gsr_video_encoder *encoder, size_t id); - #endif /* GSR_ENCODER_VIDEO_H */ diff --git a/meson.build b/meson.build index e47359e..1d386da 100644 --- a/meson.build +++ b/meson.build @@ -14,6 +14,7 @@ src = [ 'src/capture/xcomposite.c', 'src/capture/ximage.c', 'src/capture/kms.c', + 'src/encoder/encoder.c', 'src/encoder/video/video.c', 'src/encoder/video/nvenc.c', 'src/encoder/video/vaapi.c', diff --git a/src/encoder/encoder.c b/src/encoder/encoder.c new file mode 100644 index 0000000..d35cbbe --- /dev/null +++ b/src/encoder/encoder.c @@ -0,0 +1,152 @@ +#include "../../include/encoder/encoder.h" +#include "../../include/utils.h" + +#include <string.h> +#include <stdio.h> + +#include <libavcodec/avcodec.h> +#include <libavformat/avformat.h> + +bool gsr_encoder_init(gsr_encoder *self, size_t replay_buffer_num_packets) { + memset(self, 0, sizeof(*self)); + self->num_recording_destinations = 0; + self->recording_destination_id_counter = 0; + + if(pthread_mutex_init(&self->file_write_mutex, NULL) != 0) { + fprintf(stderr, "gsr error: gsr_encoder_init: failed to create mutex\n"); + return false; + } + self->mutex_created = true; + + if(replay_buffer_num_packets > 0) { + if(!gsr_replay_buffer_init(&self->replay_buffer, replay_buffer_num_packets)) { + fprintf(stderr, "gsr error: gsr_encoder_init: failed to create replay buffer\n"); + gsr_encoder_deinit(self); + return false; + } + self->has_replay_buffer = true; + } + + return true; +} + +void gsr_encoder_deinit(gsr_encoder *self) { + if(self->mutex_created) { + self->mutex_created = false; + pthread_mutex_destroy(&self->file_write_mutex); + } + + gsr_replay_buffer_deinit(&self->replay_buffer); + self->has_replay_buffer = false; + self->num_recording_destinations = 0; + self->recording_destination_id_counter = 0; +} + +void gsr_encoder_receive_packets(gsr_encoder *self, AVCodecContext *codec_context, int64_t pts, int stream_index) { + for(;;) { + AVPacket *av_packet = av_packet_alloc(); + if(!av_packet) + break; + + av_packet->data = NULL; + av_packet->size = 0; + int res = avcodec_receive_packet(codec_context, av_packet); + if(res == 0) { // we have a packet, send the packet to the muxer + av_packet->stream_index = stream_index; + av_packet->pts = pts; + av_packet->dts = pts; + + if(self->has_replay_buffer) { + const double time_now = clock_get_monotonic_seconds(); + if(!gsr_replay_buffer_append(&self->replay_buffer, av_packet, time_now)) + fprintf(stderr, "gsr error: gsr_encoder_receive_packets: failed to add replay buffer data\n"); + } + + pthread_mutex_lock(&self->file_write_mutex); + const bool is_keyframe = av_packet->flags & AV_PKT_FLAG_KEY; + for(size_t i = 0; i < self->num_recording_destinations; ++i) { + gsr_encoder_recording_destination *recording_destination = &self->recording_destinations[i]; + if(recording_destination->codec_context != codec_context) + continue; + + if(is_keyframe) + recording_destination->has_received_keyframe = true; + else if(!recording_destination->has_received_keyframe) + continue; + + av_packet->pts = pts - recording_destination->start_pts; + av_packet->dts = pts - recording_destination->start_pts; + + av_packet_rescale_ts(av_packet, codec_context->time_base, recording_destination->stream->time_base); + // TODO: Is av_interleaved_write_frame needed?. Answer: might be needed for mkv but dont use it! it causes frames to be inconsistent, skipping frames and duplicating frames. + // TODO: av_interleaved_write_frame might be needed for cfr, or always for flv + const int ret = av_write_frame(recording_destination->format_context, av_packet); + if(ret < 0) { + char error_buffer[AV_ERROR_MAX_STRING_SIZE]; + if(av_strerror(ret, error_buffer, sizeof(error_buffer)) < 0) + snprintf(error_buffer, sizeof(error_buffer), "Unknown error"); + fprintf(stderr, "gsr error: gsr_encoder_receive_packets: failed to write frame index %d to muxer, reason: %s (%d)\n", av_packet->stream_index, error_buffer, ret); + } + } + pthread_mutex_unlock(&self->file_write_mutex); + + av_packet_free(&av_packet); + } else if (res == AVERROR(EAGAIN)) { // we have no packet + // fprintf(stderr, "No packet!\n"); + av_packet_free(&av_packet); + break; + } else if (res == AVERROR_EOF) { // this is the end of the stream + av_packet_free(&av_packet); + fprintf(stderr, "End of stream!\n"); + break; + } else { + av_packet_free(&av_packet); + fprintf(stderr, "Unexpected error: %d\n", res); + break; + } + } +} + +size_t gsr_encoder_add_recording_destination(gsr_encoder *self, AVCodecContext *codec_context, AVFormatContext *format_context, AVStream *stream, int64_t start_pts) { + if(self->num_recording_destinations >= GSR_MAX_RECORDING_DESTINATIONS) { + fprintf(stderr, "gsr error: gsr_encoder_add_recording_destination: failed to add destination, reached the max amount of recording destinations (%d)\n", GSR_MAX_RECORDING_DESTINATIONS); + return (size_t)-1; + } + + for(size_t i = 0; i < self->num_recording_destinations; ++i) { + if(self->recording_destinations[i].stream == stream) { + fprintf(stderr, "gsr error: gsr_encoder_add_recording_destination: failed to add destination, the stream %p already exists as an output\n", (void*)stream); + return (size_t)-1; + } + } + + pthread_mutex_lock(&self->file_write_mutex); + gsr_encoder_recording_destination *recording_destination = &self->recording_destinations[self->num_recording_destinations]; + recording_destination->id = self->recording_destination_id_counter; + recording_destination->codec_context = codec_context; + recording_destination->format_context = format_context; + recording_destination->stream = stream; + recording_destination->start_pts = start_pts; + recording_destination->has_received_keyframe = false; + + ++self->recording_destination_id_counter; + ++self->num_recording_destinations; + pthread_mutex_unlock(&self->file_write_mutex); + + return recording_destination->id; +} + +bool gsr_encoder_remove_recording_destination(gsr_encoder *self, size_t id) { + 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) { + self->recording_destinations[i] = self->recording_destinations[self->num_recording_destinations - 1]; + --self->num_recording_destinations; + found = true; + break; + } + } + pthread_mutex_unlock(&self->file_write_mutex); + return found; +} diff --git a/src/encoder/video/video.c b/src/encoder/video/video.c index 82711ce..ce3b61b 100644 --- a/src/encoder/video/video.c +++ b/src/encoder/video/video.c @@ -1,54 +1,18 @@ #include "../../../include/encoder/video/video.h" -#include "../../../include/utils.h" -#include <string.h> -#include <stdio.h> #include <assert.h> -#include <libavcodec/avcodec.h> -#include <libavformat/avformat.h> - -bool gsr_video_encoder_start(gsr_video_encoder *encoder, AVCodecContext *video_codec_context, AVFrame *frame, size_t replay_buffer_num_packets) { +bool gsr_video_encoder_start(gsr_video_encoder *encoder, AVCodecContext *video_codec_context, AVFrame *frame) { assert(!encoder->started); - encoder->num_recording_destinations = 0; - encoder->recording_destination_id = 0; - - if(pthread_mutex_init(&encoder->file_write_mutex, NULL) != 0) { - fprintf(stderr, "gsr error: gsr_video_encoder_start: failed to create mutex\n"); - return false; - } - - memset(&encoder->replay_buffer, 0, sizeof(encoder->replay_buffer)); - if(replay_buffer_num_packets > 0) { - if(!gsr_replay_buffer_init(&encoder->replay_buffer, replay_buffer_num_packets)) { - fprintf(stderr, "gsr error: gsr_video_encoder_start: failed to create replay buffer\n"); - goto error; - } - encoder->has_replay_buffer = true; - } - bool res = encoder->start(encoder, video_codec_context, frame); - if(res) { + if(res) encoder->started = true; - return true; - } else { - goto error; - } - - error: - pthread_mutex_destroy(&encoder->file_write_mutex); - gsr_replay_buffer_deinit(&encoder->replay_buffer); - return false; + return res; } void gsr_video_encoder_destroy(gsr_video_encoder *encoder, AVCodecContext *video_codec_context) { assert(encoder->started); encoder->started = false; - pthread_mutex_destroy(&encoder->file_write_mutex); - gsr_replay_buffer_deinit(&encoder->replay_buffer); - encoder->has_replay_buffer = false; - encoder->num_recording_destinations = 0; - encoder->recording_destination_id = 0; encoder->destroy(encoder, video_codec_context); } @@ -62,112 +26,3 @@ void gsr_video_encoder_get_textures(gsr_video_encoder *encoder, unsigned int *te assert(encoder->started); encoder->get_textures(encoder, textures, num_textures, destination_color); } - -void gsr_video_encoder_receive_packets(gsr_video_encoder *encoder, AVCodecContext *codec_context, int64_t pts, int stream_index) { - for (;;) { - AVPacket *av_packet = av_packet_alloc(); - if(!av_packet) - break; - - av_packet->data = NULL; - av_packet->size = 0; - int res = avcodec_receive_packet(codec_context, av_packet); - if(res == 0) { // we have a packet, send the packet to the muxer - av_packet->stream_index = stream_index; - av_packet->pts = pts; - av_packet->dts = pts; - - if(encoder->has_replay_buffer) { - const double time_now = clock_get_monotonic_seconds(); - if(!gsr_replay_buffer_append(&encoder->replay_buffer, av_packet, time_now)) - fprintf(stderr, "gsr error: gsr_video_encoder_receive_packets: failed to add replay buffer data\n"); - } - - pthread_mutex_lock(&encoder->file_write_mutex); - const bool is_keyframe = av_packet->flags & AV_PKT_FLAG_KEY; - for(size_t i = 0; i < encoder->num_recording_destinations; ++i) { - gsr_video_encoder_recording_destination *recording_destination = &encoder->recording_destinations[i]; - if(recording_destination->codec_context != codec_context) - continue; - - if(is_keyframe) - recording_destination->has_received_keyframe = true; - else if(!recording_destination->has_received_keyframe) - continue; - - av_packet->pts = pts - recording_destination->start_pts; - av_packet->dts = pts - recording_destination->start_pts; - - av_packet_rescale_ts(av_packet, codec_context->time_base, recording_destination->stream->time_base); - // TODO: Is av_interleaved_write_frame needed?. Answer: might be needed for mkv but dont use it! it causes frames to be inconsistent, skipping frames and duplicating frames. - // TODO: av_interleaved_write_frame might be needed for cfr, or always for flv - const int ret = av_write_frame(recording_destination->format_context, av_packet); - if(ret < 0) { - char error_buffer[AV_ERROR_MAX_STRING_SIZE]; - if(av_strerror(ret, error_buffer, sizeof(error_buffer)) < 0) - snprintf(error_buffer, sizeof(error_buffer), "Unknown error"); - fprintf(stderr, "Error: Failed to write frame index %d to muxer, reason: %s (%d)\n", av_packet->stream_index, error_buffer, ret); - } - } - pthread_mutex_unlock(&encoder->file_write_mutex); - - av_packet_free(&av_packet); - } else if (res == AVERROR(EAGAIN)) { // we have no packet - // fprintf(stderr, "No packet!\n"); - av_packet_free(&av_packet); - break; - } else if (res == AVERROR_EOF) { // this is the end of the stream - av_packet_free(&av_packet); - fprintf(stderr, "End of stream!\n"); - break; - } else { - av_packet_free(&av_packet); - fprintf(stderr, "Unexpected error: %d\n", res); - break; - } - } -} - -size_t gsr_video_encoder_add_recording_destination(gsr_video_encoder *encoder, AVCodecContext *codec_context, AVFormatContext *format_context, AVStream *stream, int64_t start_pts) { - if(encoder->num_recording_destinations >= GSR_MAX_RECORDING_DESTINATIONS) { - fprintf(stderr, "gsr error: gsr_video_encoder_add_recording_destination: failed to add destination, reached the max amount of recording destinations (%d)\n", GSR_MAX_RECORDING_DESTINATIONS); - return (size_t)-1; - } - - for(size_t i = 0; i < encoder->num_recording_destinations; ++i) { - if(encoder->recording_destinations[i].stream == stream) { - fprintf(stderr, "gsr error: gsr_video_encoder_add_recording_destination: failed to add destination, the stream %p already exists as an output\n", (void*)stream); - return (size_t)-1; - } - } - - pthread_mutex_lock(&encoder->file_write_mutex); - gsr_video_encoder_recording_destination *recording_destination = &encoder->recording_destinations[encoder->num_recording_destinations]; - recording_destination->id = encoder->recording_destination_id; - recording_destination->codec_context = codec_context; - recording_destination->format_context = format_context; - recording_destination->stream = stream; - recording_destination->start_pts = start_pts; - recording_destination->has_received_keyframe = false; - - ++encoder->recording_destination_id; - ++encoder->num_recording_destinations; - pthread_mutex_unlock(&encoder->file_write_mutex); - - return recording_destination->id; -} - -bool gsr_video_encoder_remove_recording_destination(gsr_video_encoder *encoder, size_t id) { - bool found = false; - pthread_mutex_lock(&encoder->file_write_mutex); - for(size_t i = 0; i < encoder->num_recording_destinations; ++i) { - if(encoder->recording_destinations[i].id == id) { - encoder->recording_destinations[i] = encoder->recording_destinations[encoder->num_recording_destinations - 1]; - --encoder->num_recording_destinations; - found = true; - break; - } - } - pthread_mutex_unlock(&encoder->file_write_mutex); - return found; -} diff --git a/src/main.cpp b/src/main.cpp index eca8db1..477d8f5 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -10,6 +10,7 @@ extern "C" { #ifdef GSR_APP_AUDIO #include "../include/pipewire_audio.h" #endif +#include "../include/encoder/encoder.h" #include "../include/encoder/video/nvenc.h" #include "../include/encoder/video/vaapi.h" #include "../include/encoder/video/vulkan.h" @@ -3143,14 +3144,20 @@ int main(int argc, char **argv) { video_frame->width = capture_metadata.width; video_frame->height = capture_metadata.height; + const size_t estimated_replay_buffer_packets = calculate_estimated_replay_buffer_packets(arg_parser.replay_buffer_size_secs, arg_parser.fps, arg_parser.audio_codec, requested_audio_inputs); + gsr_encoder encoder; + if(!gsr_encoder_init(&encoder, estimated_replay_buffer_packets)) { + fprintf(stderr, "Error: failed to create encoder\n"); + _exit(1); + } + gsr_video_encoder *video_encoder = create_video_encoder(&egl, arg_parser); if(!video_encoder) { fprintf(stderr, "Error: failed to create video encoder\n"); _exit(1); } - const size_t estimated_replay_buffer_packets = calculate_estimated_replay_buffer_packets(arg_parser.replay_buffer_size_secs, arg_parser.fps, arg_parser.audio_codec, requested_audio_inputs); - if(!gsr_video_encoder_start(video_encoder, video_codec_context, video_frame, estimated_replay_buffer_packets)) { + if(!gsr_video_encoder_start(video_encoder, video_codec_context, video_frame)) { fprintf(stderr, "Error: failed to start video encoder\n"); _exit(1); } @@ -3181,7 +3188,7 @@ int main(int argc, char **argv) { if(video_stream) { avcodec_parameters_from_context(video_stream->codecpar, video_codec_context); - gsr_video_encoder_add_recording_destination(video_encoder, video_codec_context, av_format_context, video_stream, 0); + gsr_encoder_add_recording_destination(&encoder, video_codec_context, av_format_context, video_stream, 0); } int audio_max_frame_size = 1024; @@ -3193,7 +3200,7 @@ int main(int argc, char **argv) { AVStream *audio_stream = nullptr; if(!is_replaying) { audio_stream = create_stream(av_format_context, audio_codec_context); - if(gsr_video_encoder_add_recording_destination(video_encoder, audio_codec_context, av_format_context, audio_stream, 0) == (size_t)-1) + if(gsr_encoder_add_recording_destination(&encoder, audio_codec_context, av_format_context, audio_stream, 0) == (size_t)-1) fprintf(stderr, "gsr error: added too many audio sources\n"); } @@ -3396,7 +3403,7 @@ int main(int argc, char **argv) { ret = avcodec_send_frame(audio_track.codec_context, audio_device.frame); if(ret >= 0) { // TODO: Move to separate thread because this could write to network (for example when livestreaming) - gsr_video_encoder_receive_packets(video_encoder, audio_track.codec_context, audio_device.frame->pts, audio_track.stream_index); + gsr_encoder_receive_packets(&encoder, audio_track.codec_context, audio_device.frame->pts, audio_track.stream_index); } else { fprintf(stderr, "Failed to encode audio!\n"); } @@ -3430,7 +3437,7 @@ int main(int argc, char **argv) { ret = avcodec_send_frame(audio_track.codec_context, audio_device.frame); if(ret >= 0) { // TODO: Move to separate thread because this could write to network (for example when livestreaming) - gsr_video_encoder_receive_packets(video_encoder, audio_track.codec_context, audio_device.frame->pts, audio_track.stream_index); + gsr_encoder_receive_packets(&encoder, audio_track.codec_context, audio_device.frame->pts, audio_track.stream_index); } else { fprintf(stderr, "Failed to encode audio!\n"); } @@ -3465,7 +3472,7 @@ int main(int argc, char **argv) { err = avcodec_send_frame(audio_track.codec_context, aframe); if(err >= 0){ // TODO: Move to separate thread because this could write to network (for example when livestreaming) - gsr_video_encoder_receive_packets(video_encoder, audio_track.codec_context, aframe->pts, audio_track.stream_index); + gsr_encoder_receive_packets(&encoder, audio_track.codec_context, aframe->pts, audio_track.stream_index); } else { fprintf(stderr, "Failed to encode audio!\n"); } @@ -3616,7 +3623,7 @@ int main(int argc, char **argv) { int ret = avcodec_send_frame(video_codec_context, video_frame); if(ret == 0) { // TODO: Move to separate thread because this could write to network (for example when livestreaming) - gsr_video_encoder_receive_packets(video_encoder, video_codec_context, video_frame->pts, VIDEO_STREAM_INDEX); + gsr_encoder_receive_packets(&encoder, video_codec_context, video_frame->pts, VIDEO_STREAM_INDEX); } else { fprintf(stderr, "Error: avcodec_send_frame failed, error: %s\n", av_error_to_string(ret)); } @@ -3659,12 +3666,12 @@ int main(int argc, char **argv) { replay_recording_filepath = create_new_recording_filepath_from_timestamp(arg_parser.replay_recording_directory, "Recording", file_extension, arg_parser.date_folders); replay_recording_start_result = start_recording_create_streams(replay_recording_filepath.c_str(), arg_parser.container_format, video_codec_context, audio_tracks, hdr, capture); if(replay_recording_start_result.av_format_context) { - const size_t video_recording_destination_id = gsr_video_encoder_add_recording_destination(video_encoder, video_codec_context, replay_recording_start_result.av_format_context, replay_recording_start_result.video_stream, video_frame->pts); + 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(video_recording_destination_id != (size_t)-1) replay_recording_items.push_back(video_recording_destination_id); for(const auto &audio_input : replay_recording_start_result.audio_inputs) { - const size_t audio_recording_destination_id = gsr_video_encoder_add_recording_destination(video_encoder, audio_input.audio_track->codec_context, replay_recording_start_result.av_format_context, audio_input.stream, audio_input.audio_track->pts); + const size_t audio_recording_destination_id = gsr_encoder_add_recording_destination(&encoder, audio_input.audio_track->codec_context, replay_recording_start_result.av_format_context, audio_input.stream, audio_input.audio_track->pts); if(audio_recording_destination_id != (size_t)-1) replay_recording_items.push_back(audio_recording_destination_id); } @@ -3678,7 +3685,7 @@ int main(int argc, char **argv) { } } else if(replay_recording_start_result.av_format_context) { for(size_t id : replay_recording_items) { - gsr_video_encoder_remove_recording_destination(video_encoder, id); + gsr_encoder_remove_recording_destination(&encoder, id); } replay_recording_items.clear(); @@ -3718,10 +3725,10 @@ int main(int argc, char **argv) { save_replay_seconds = 0; save_replay_output_filepath.clear(); - save_replay_async(video_codec_context, VIDEO_STREAM_INDEX, audio_tracks, &video_encoder->replay_buffer, arg_parser.filename, arg_parser.container_format, file_extension, arg_parser.date_folders, hdr, capture, current_save_replay_seconds); + save_replay_async(video_codec_context, VIDEO_STREAM_INDEX, audio_tracks, &encoder.replay_buffer, arg_parser.filename, arg_parser.container_format, file_extension, arg_parser.date_folders, hdr, capture, current_save_replay_seconds); if(arg_parser.restart_replay_on_save && current_save_replay_seconds == save_replay_seconds_full) { - gsr_replay_buffer_clear(&video_encoder->replay_buffer); + gsr_replay_buffer_clear(&encoder.replay_buffer); replay_start_time = clock_get_monotonic_seconds() - paused_time_offset; } } @@ -3768,7 +3775,7 @@ int main(int argc, char **argv) { if(replay_recording_start_result.av_format_context) { for(size_t id : replay_recording_items) { - gsr_video_encoder_remove_recording_destination(video_encoder, id); + gsr_encoder_remove_recording_destination(&encoder, id); } replay_recording_items.clear(); @@ -3807,6 +3814,7 @@ int main(int argc, char **argv) { gsr_damage_deinit(&damage); gsr_color_conversion_deinit(&color_conversion); gsr_video_encoder_destroy(video_encoder, video_codec_context); + gsr_encoder_deinit(&encoder); gsr_capture_destroy(capture); #ifdef GSR_APP_AUDIO gsr_pipewire_audio_deinit(&pipewire_audio); |