aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2026-08-05 14:56:59 +0200
committerdec05eba <dec05eba@protonmail.com>2026-08-05 14:56:59 +0200
commite6c1cf0ec1ce220912a6ef6b6fac5ba9b2e98276 (patch)
tree5e5636b9bc485a0efc37af6b03fccc99401d671b /src
parentfbc31a9561d16418226c0efa65e2a73923516dbd (diff)
workaround ffmpeg hybrid_fragmented bug when no packets have been recorded yet
Diffstat (limited to 'src')
-rw-r--r--src/encoder/encoder.c3
-rw-r--r--src/ffmpeg_utils.c28
-rw-r--r--src/recorder/muxer.c2
-rw-r--r--src/recorder/recorder.c2
-rw-r--r--src/recorder/replay_save.c4
5 files changed, 36 insertions, 3 deletions
diff --git a/src/encoder/encoder.c b/src/encoder/encoder.c
index 2df4666..3aebb7a 100644
--- a/src/encoder/encoder.c
+++ b/src/encoder/encoder.c
@@ -1,6 +1,7 @@
#include "../../include/encoder/encoder.h"
#include "../../include/log.h"
#include "../../include/utils.h"
+#include "../../include/ffmpeg_utils.h"
#include <string.h>
#include <stdio.h>
@@ -152,6 +153,8 @@ void gsr_encoder_receive_packets(gsr_encoder *self, AVCodecContext *codec_contex
// 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)
+ gsr_av_format_context_mark_packet_written(recording_destination->format_context);
if(ret < 0) {
char error_buffer[AV_ERROR_MAX_STRING_SIZE];
if(av_strerror(ret, error_buffer, sizeof(error_buffer)) < 0)
diff --git a/src/ffmpeg_utils.c b/src/ffmpeg_utils.c
index ce239a1..fc4e4ed 100644
--- a/src/ffmpeg_utils.c
+++ b/src/ffmpeg_utils.c
@@ -1,7 +1,10 @@
#include "../include/ffmpeg_utils.h"
+#include "../include/log.h"
#include <string.h>
#include <libavutil/error.h>
+#include <libavutil/opt.h>
+#include <libavformat/avformat.h>
static _Thread_local char av_error_buffer[AV_ERROR_MAX_STRING_SIZE];
@@ -10,3 +13,28 @@ const char* gsr_av_error_to_string(int err) {
strcpy(av_error_buffer, "Unknown error");
return av_error_buffer;
}
+
+void gsr_av_format_context_mark_packet_written(AVFormatContext *av_format_context) {
+ av_format_context->opaque = (void*)1;
+}
+
+static bool av_format_context_uses_hybrid_fragmented(AVFormatContext *av_format_context) {
+ if(LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(62, 6, 101))
+ return false;
+
+ const AVOption *opt = av_opt_find(av_format_context->priv_data, "movflags", NULL, 0, 0);
+ if(!opt || !opt->unit)
+ return false;
+
+ return av_opt_find(av_format_context->priv_data, "hybrid_fragmented", opt->unit, 0, 0) != NULL;
+}
+
+int gsr_av_format_context_write_trailer(AVFormatContext *av_format_context) {
+ const bool packet_written = av_format_context->opaque != NULL;
+ if(!packet_written && av_format_context_uses_hybrid_fragmented(av_format_context)) {
+ gsr_log(GSR_LOG_LEVEL_WARNING, "not finalizing the video file because it has no video/audio data");
+ return 0;
+ }
+
+ return av_write_trailer(av_format_context);
+}
diff --git a/src/recorder/muxer.c b/src/recorder/muxer.c
index ff4ec06..806921d 100644
--- a/src/recorder/muxer.c
+++ b/src/recorder/muxer.c
@@ -197,7 +197,7 @@ bool gsr_recording_output_start(gsr_recording_output *self, const char *filename
bool gsr_recording_output_stop(gsr_recording_output *self) {
bool trailer_written = true;
- if(av_write_trailer(self->av_format_context) != 0) {
+ if(gsr_av_format_context_write_trailer(self->av_format_context) != 0) {
//trailer_written = false;
}
diff --git a/src/recorder/recorder.c b/src/recorder/recorder.c
index 4bffb8e..fc0ccf0 100644
--- a/src/recorder/recorder.c
+++ b/src/recorder/recorder.c
@@ -886,7 +886,7 @@ static void gsr_recorder_stop_recording(gsr_recorder *self) {
gsr_audio_capture_join_threads(&self->audio_capture);
// TODO: Replace this with start_recording_create_steams
- if(!self->settings.is_replaying && av_write_trailer(self->av_format_context) != 0) {
+ if(!self->settings.is_replaying && gsr_av_format_context_write_trailer(self->av_format_context) != 0) {
//fprintf(stderr, "Failed to write trailer\n");
}
diff --git a/src/recorder/replay_save.c b/src/recorder/replay_save.c
index c3a1bea..3251616 100644
--- a/src/recorder/replay_save.c
+++ b/src/recorder/replay_save.c
@@ -96,7 +96,9 @@ static void* replay_save_thread(void *userdata) {
av_packet_rescale_ts(&av_packet, codec_context->time_base, stream->time_base);
const int ret = av_write_frame(self->recording_output.av_format_context, &av_packet);
- if(ret < 0)
+ if(ret >= 0)
+ gsr_av_format_context_mark_packet_written(self->recording_output.av_format_context);
+ else
gsr_log(GSR_LOG_LEVEL_ERROR, "Failed to write frame index %d to muxer, reason: %s (%d)", av_packet.stream_index, gsr_av_error_to_string(ret), ret);
free(replay_packet_data);