aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--include/recorder/replay_save.h10
-rw-r--r--include/replay_buffer/replay_buffer.h9
-rw-r--r--src/recorder/recorder.c8
-rw-r--r--src/recorder/replay_save.c17
-rw-r--r--src/replay_buffer/replay_buffer.c4
-rw-r--r--src/replay_buffer/replay_buffer_disk.c31
-rw-r--r--src/replay_buffer/replay_buffer_ram.c27
7 files changed, 96 insertions, 10 deletions
diff --git a/include/recorder/replay_save.h b/include/recorder/replay_save.h
index 7f48851..ac4eb98 100644
--- a/include/recorder/replay_save.h
+++ b/include/recorder/replay_save.h
@@ -37,14 +37,20 @@ typedef struct {
gsr_audio_pts_offset *audio_pts_offsets;
size_t num_audio_pts_offsets;
gsr_replay_buffer *cloned_replay_buffer;
+ /* The data that the replay buffer had before it was restarted, free'd on the replay save thread */
+ gsr_replay_buffer *restarted_replay_buffer_data;
gsr_encoder *encoder;
} gsr_replay_save;
void gsr_replay_save_init(gsr_replay_save *self);
bool gsr_replay_save_is_running(const gsr_replay_save *self);
-/* Returns false if the replay failed to start. |current_save_replay_seconds| can be GSR_SAVE_REPLAY_SECONDS_FULL to save the whole replay buffer */
-bool gsr_replay_save_start(gsr_replay_save *self, AVCodecContext *video_codec_context, int video_stream_index, const gsr_audio_capture *audio_capture, gsr_encoder *encoder, const gsr_recorder_settings *settings, const char *file_extension, bool hdr, gsr_video_sources *video_sources, int current_save_replay_seconds);
+/*
+ Returns false if the replay failed to start. |current_save_replay_seconds| can be GSR_SAVE_REPLAY_SECONDS_FULL to save the whole replay buffer.
+ The replay buffer of |encoder| is restarted (emptied) when |restart_replay| is set, which is done without blocking the
+ thread that appends to the replay buffer by free'ing the old data on the replay save thread.
+*/
+bool gsr_replay_save_start(gsr_replay_save *self, AVCodecContext *video_codec_context, int video_stream_index, const gsr_audio_capture *audio_capture, gsr_encoder *encoder, const gsr_recorder_settings *settings, const char *file_extension, bool hdr, gsr_video_sources *video_sources, int current_save_replay_seconds, bool restart_replay);
/* Returns true when the replay finished saving, in which case |success| and |output_filepath| are set. |output_filepath| is empty when nothing was saved */
bool gsr_replay_save_poll(gsr_replay_save *self, bool *success, const char **output_filepath);
/* Waits for an ongoing replay save to finish. Returns the same values as gsr_replay_save_poll */
diff --git a/include/replay_buffer/replay_buffer.h b/include/replay_buffer/replay_buffer.h
index 6e91103..c036b1c 100644
--- a/include/replay_buffer/replay_buffer.h
+++ b/include/replay_buffer/replay_buffer.h
@@ -21,6 +21,8 @@ struct gsr_replay_buffer {
uint8_t* (*iterator_get_packet_data)(gsr_replay_buffer *self, gsr_replay_buffer_iterator iterator);
/* The clone has to be destroyed before the replay buffer it clones is destroyed */
gsr_replay_buffer* (*clone)(gsr_replay_buffer *self);
+ /* Returns NULL on failure, in which case |self| is left untouched */
+ gsr_replay_buffer* (*take_data)(gsr_replay_buffer *self);
/* Returns {0, 0} if replay buffer is empty */
gsr_replay_buffer_iterator (*find_packet_index_by_time_passed)(gsr_replay_buffer *self, int seconds);
/* Returns {-1, 0} if not found */
@@ -38,6 +40,13 @@ AVPacket* gsr_replay_buffer_iterator_get_packet(gsr_replay_buffer *self, gsr_rep
uint8_t* gsr_replay_buffer_iterator_get_packet_data(gsr_replay_buffer *self, gsr_replay_buffer_iterator iterator);
/* The clone has to be destroyed before the replay buffer it clones is destroyed */
gsr_replay_buffer* gsr_replay_buffer_clone(gsr_replay_buffer *self);
+/*
+ Moves the data of |self| into a new replay buffer and leaves |self| empty and ready to be used again.
+ This is a cheap operation, unlike gsr_replay_buffer_clear, because freeing the data is deferred to
+ gsr_replay_buffer_destroy of the returned replay buffer, which can be done without blocking |self|.
+ Returns NULL on failure, in which case |self| is left untouched.
+*/
+gsr_replay_buffer* gsr_replay_buffer_take_data(gsr_replay_buffer *self);
/* Returns {0, 0} if replay buffer is empty */
gsr_replay_buffer_iterator gsr_replay_buffer_find_packet_index_by_time_passed(gsr_replay_buffer *self, int seconds);
/* Returns {-1, 0} if not found */
diff --git a/src/recorder/recorder.c b/src/recorder/recorder.c
index 3f1331f..949ed99 100644
--- a/src/recorder/recorder.c
+++ b/src/recorder/recorder.c
@@ -752,15 +752,9 @@ static void recorder_poll_replay_save(gsr_recorder *self) {
atomic_store(&self->save_replay_seconds, 0);
const int restart_replay_request = atomic_exchange(&self->save_replay_restart_replay, GSR_RESTART_REPLAY_USE_OPTION);
const bool restart_replay = restart_replay_request == GSR_RESTART_REPLAY_USE_OPTION ? self->settings.restart_replay_on_save : restart_replay_request == GSR_RESTART_REPLAY_ENABLE;
- const bool replay_start_result = gsr_replay_save_start(&self->replay_save, self->video_codec_context, GSR_VIDEO_STREAM_INDEX, &self->audio_capture, &self->encoder, &self->settings, self->file_extension, self->hdr, self->video_sources, current_save_replay_seconds);
+ const bool replay_start_result = gsr_replay_save_start(&self->replay_save, self->video_codec_context, GSR_VIDEO_STREAM_INDEX, &self->audio_capture, &self->encoder, &self->settings, self->file_extension, self->hdr, self->video_sources, current_save_replay_seconds, restart_replay && current_save_replay_seconds == GSR_SAVE_REPLAY_SECONDS_FULL);
if(!replay_start_result && self->callbacks.replay_saved)
self->callbacks.replay_saved(NULL, self->callbacks.userdata);
-
- if(restart_replay && current_save_replay_seconds == GSR_SAVE_REPLAY_SECONDS_FULL) {
- pthread_mutex_lock(&self->encoder.replay_mutex);
- gsr_replay_buffer_clear(self->encoder.replay_buffer);
- pthread_mutex_unlock(&self->encoder.replay_mutex);
- }
}
}
diff --git a/src/recorder/replay_save.c b/src/recorder/replay_save.c
index 3251616..55cf35d 100644
--- a/src/recorder/replay_save.c
+++ b/src/recorder/replay_save.c
@@ -23,6 +23,15 @@ static void gsr_replay_save_cleanup(gsr_replay_save *self) {
self->cloned_replay_buffer = NULL;
}
+ /*
+ This is the data that the replay buffer had before the replay was saved and nothing references it anymore,
+ so it can be free'd without holding the replay mutex. This can take a while when the replay buffer is large.
+ */
+ if(self->restarted_replay_buffer_data) {
+ gsr_replay_buffer_destroy(self->restarted_replay_buffer_data);
+ self->restarted_replay_buffer_data = NULL;
+ }
+
if(self->audio_pts_offsets) {
free(self->audio_pts_offsets);
self->audio_pts_offsets = NULL;
@@ -116,7 +125,7 @@ static void* replay_save_thread(void *userdata) {
return NULL;
}
-bool gsr_replay_save_start(gsr_replay_save *self, AVCodecContext *video_codec_context, int video_stream_index, const gsr_audio_capture *audio_capture, gsr_encoder *encoder, const gsr_recorder_settings *settings, const char *file_extension, bool hdr, gsr_video_sources *video_sources, int current_save_replay_seconds) {
+bool gsr_replay_save_start(gsr_replay_save *self, AVCodecContext *video_codec_context, int video_stream_index, const gsr_audio_capture *audio_capture, gsr_encoder *encoder, const gsr_recorder_settings *settings, const char *file_extension, bool hdr, gsr_video_sources *video_sources, int current_save_replay_seconds, bool restart_replay) {
if(self->thread_created)
return true;
@@ -125,10 +134,16 @@ bool gsr_replay_save_start(gsr_replay_save *self, AVCodecContext *video_codec_co
self->video_stream_index = video_stream_index;
self->output_filepath[0] = '\0';
self->success = false;
+ self->restarted_replay_buffer_data = NULL;
atomic_store(&self->finished, 0);
pthread_mutex_lock(&encoder->replay_mutex);
self->cloned_replay_buffer = gsr_replay_buffer_clone(encoder->replay_buffer);
+ if(self->cloned_replay_buffer && restart_replay) {
+ self->restarted_replay_buffer_data = gsr_replay_buffer_take_data(encoder->replay_buffer);
+ if(!self->restarted_replay_buffer_data)
+ gsr_log(GSR_LOG_LEVEL_ERROR, "gsr_replay_save: failed to restart the replay buffer");
+ }
pthread_mutex_unlock(&encoder->replay_mutex);
if(!self->cloned_replay_buffer) {
/* TODO: Return this error to mark the replay as failed */
diff --git a/src/replay_buffer/replay_buffer.c b/src/replay_buffer/replay_buffer.c
index 08ed1f1..458b359 100644
--- a/src/replay_buffer/replay_buffer.c
+++ b/src/replay_buffer/replay_buffer.c
@@ -42,6 +42,10 @@ gsr_replay_buffer* gsr_replay_buffer_clone(gsr_replay_buffer *self) {
return self->clone(self);
}
+gsr_replay_buffer* gsr_replay_buffer_take_data(gsr_replay_buffer *self) {
+ return self->take_data(self);
+}
+
gsr_replay_buffer_iterator gsr_replay_buffer_find_packet_index_by_time_passed(gsr_replay_buffer *self, int seconds) {
return self->find_packet_index_by_time_passed(self, seconds);
}
diff --git a/src/replay_buffer/replay_buffer_disk.c b/src/replay_buffer/replay_buffer_disk.c
index ffc59cc..56244f9 100644
--- a/src/replay_buffer/replay_buffer_disk.c
+++ b/src/replay_buffer/replay_buffer_disk.c
@@ -254,6 +254,36 @@ static uint8_t* gsr_replay_buffer_disk_iterator_get_packet_data(gsr_replay_buffe
return packet_data;
}
+static gsr_replay_buffer* gsr_replay_buffer_disk_take_data(gsr_replay_buffer *replay_buffer) {
+ gsr_replay_buffer_disk *self = (gsr_replay_buffer_disk*)replay_buffer;
+ gsr_replay_buffer_disk *destination = calloc(1, sizeof(gsr_replay_buffer_disk));
+ if(!destination)
+ return NULL;
+
+ gsr_replay_buffer_disk_set_impl_funcs(destination);
+
+ destination->replay_buffer_time = self->replay_buffer_time;
+ destination->storage_counter = self->storage_counter;
+ destination->storage_num_bytes_written = self->storage_num_bytes_written;
+ destination->storage_fd = self->storage_fd;
+
+ for(size_t i = 0; i < self->num_files; ++i) {
+ destination->files[i] = self->files[i];
+ }
+ destination->num_files = self->num_files;
+
+ snprintf(destination->replay_directory, sizeof(destination->replay_directory), "%s", self->replay_directory);
+ /* |self| keeps writing to the same directory, so it's the one that removes it */
+ destination->owns_directory = false;
+
+ self->num_files = 0;
+ self->storage_fd = 0;
+ self->storage_num_bytes_written = 0;
+ /* |storage_counter| is not reset, to not overwrite the files that were moved to |destination| */
+
+ return (gsr_replay_buffer*)destination;
+}
+
static gsr_replay_buffer* gsr_replay_buffer_disk_clone(gsr_replay_buffer *replay_buffer) {
gsr_replay_buffer_disk *self = (gsr_replay_buffer_disk*)replay_buffer;
gsr_replay_buffer_disk *destination = calloc(1, sizeof(gsr_replay_buffer_disk));
@@ -398,6 +428,7 @@ static void gsr_replay_buffer_disk_set_impl_funcs(gsr_replay_buffer_disk *self)
self->replay_buffer.iterator_get_packet = gsr_replay_buffer_disk_iterator_get_packet;
self->replay_buffer.iterator_get_packet_data = gsr_replay_buffer_disk_iterator_get_packet_data;
self->replay_buffer.clone = gsr_replay_buffer_disk_clone;
+ self->replay_buffer.take_data = gsr_replay_buffer_disk_take_data;
self->replay_buffer.find_packet_index_by_time_passed = gsr_replay_buffer_disk_find_file_index_by_time_passed;
self->replay_buffer.find_keyframe = gsr_replay_buffer_disk_find_keyframe;
self->replay_buffer.iterator_next = gsr_replay_buffer_disk_iterator_next;
diff --git a/src/replay_buffer/replay_buffer_ram.c b/src/replay_buffer/replay_buffer_ram.c
index 5508a12..680517d 100644
--- a/src/replay_buffer/replay_buffer_ram.c
+++ b/src/replay_buffer/replay_buffer_ram.c
@@ -125,6 +125,32 @@ static uint8_t* gsr_replay_buffer_ram_iterator_get_packet_data(gsr_replay_buffer
return NULL;
}
+static gsr_replay_buffer* gsr_replay_buffer_ram_take_data(gsr_replay_buffer *replay_buffer) {
+ gsr_replay_buffer_ram *self = (gsr_replay_buffer_ram*)replay_buffer;
+ gsr_av_packet_ram **packets = calloc(self->capacity_num_packets, sizeof(gsr_av_packet_ram*));
+ if(!packets)
+ return NULL;
+
+ gsr_replay_buffer_ram *destination = calloc(1, sizeof(gsr_replay_buffer_ram));
+ if(!destination) {
+ free(packets);
+ return NULL;
+ }
+
+ gsr_replay_buffer_ram_set_impl_funcs(destination);
+
+ destination->packets = self->packets;
+ destination->capacity_num_packets = self->capacity_num_packets;
+ destination->num_packets = self->num_packets;
+ destination->index = self->index;
+
+ self->packets = packets;
+ self->num_packets = 0;
+ self->index = 0;
+
+ return (gsr_replay_buffer*)destination;
+}
+
static gsr_replay_buffer* gsr_replay_buffer_ram_clone(gsr_replay_buffer *replay_buffer) {
gsr_replay_buffer_ram *self = (gsr_replay_buffer_ram*)replay_buffer;
gsr_replay_buffer_ram *destination = calloc(1, sizeof(gsr_replay_buffer_ram));
@@ -210,6 +236,7 @@ static void gsr_replay_buffer_ram_set_impl_funcs(gsr_replay_buffer_ram *self) {
self->replay_buffer.iterator_get_packet = gsr_replay_buffer_ram_iterator_get_packet;
self->replay_buffer.iterator_get_packet_data = gsr_replay_buffer_ram_iterator_get_packet_data;
self->replay_buffer.clone = gsr_replay_buffer_ram_clone;
+ self->replay_buffer.take_data = gsr_replay_buffer_ram_take_data;
self->replay_buffer.find_packet_index_by_time_passed = gsr_replay_buffer_ram_find_packet_index_by_time_passed;
self->replay_buffer.find_keyframe = gsr_replay_buffer_ram_find_keyframe;
self->replay_buffer.iterator_next = gsr_replay_buffer_ram_iterator_next;