Commit 27c91bc8 authored by Edward Hervey's avatar Edward Hervey 🤘 Committed by Edward Hervey

multifilesink: Add a new max-duration file switching mode

This new mode ensures that files will never exceed a certain duration
based on incoming buffer PTS (and duration if present)

Note:
* You need timestamped buffers (duh). If some of the incoming buffers don't
  have PTS, then it will just accept them in the current file
parent f1ceaab0
...@@ -136,6 +136,7 @@ GST_DEBUG_CATEGORY_STATIC (gst_multi_file_sink_debug); ...@@ -136,6 +136,7 @@ GST_DEBUG_CATEGORY_STATIC (gst_multi_file_sink_debug);
#define DEFAULT_NEXT_FILE GST_MULTI_FILE_SINK_NEXT_BUFFER #define DEFAULT_NEXT_FILE GST_MULTI_FILE_SINK_NEXT_BUFFER
#define DEFAULT_MAX_FILES 0 #define DEFAULT_MAX_FILES 0
#define DEFAULT_MAX_FILE_SIZE G_GUINT64_CONSTANT(2*1024*1024*1024) #define DEFAULT_MAX_FILE_SIZE G_GUINT64_CONSTANT(2*1024*1024*1024)
#define DEFAULT_MAX_FILE_DURATION GST_CLOCK_TIME_NONE
#define DEFAULT_AGGREGATE_GOPS FALSE #define DEFAULT_AGGREGATE_GOPS FALSE
enum enum
...@@ -147,6 +148,7 @@ enum ...@@ -147,6 +148,7 @@ enum
PROP_NEXT_FILE, PROP_NEXT_FILE,
PROP_MAX_FILES, PROP_MAX_FILES,
PROP_MAX_FILE_SIZE, PROP_MAX_FILE_SIZE,
PROP_MAX_FILE_DURATION,
PROP_AGGREGATE_GOPS PROP_AGGREGATE_GOPS
}; };
...@@ -190,6 +192,10 @@ gst_multi_file_sink_next_get_type (void) ...@@ -190,6 +192,10 @@ gst_multi_file_sink_next_get_type (void)
{GST_MULTI_FILE_SINK_NEXT_MAX_SIZE, "New file when the configured maximum " {GST_MULTI_FILE_SINK_NEXT_MAX_SIZE, "New file when the configured maximum "
"file size would be exceeded with the next buffer or buffer list", "file size would be exceeded with the next buffer or buffer list",
"max-size"}, "max-size"},
{GST_MULTI_FILE_SINK_NEXT_MAX_DURATION,
"New file when the configured maximum "
"file duration would be exceeded with the next buffer or buffer list",
"max-duration"},
{0, NULL, NULL} {0, NULL, NULL}
}; };
...@@ -270,6 +276,17 @@ gst_multi_file_sink_class_init (GstMultiFileSinkClass * klass) ...@@ -270,6 +276,17 @@ gst_multi_file_sink_class_init (GstMultiFileSinkClass * klass)
0, G_MAXUINT64, DEFAULT_MAX_FILE_SIZE, 0, G_MAXUINT64, DEFAULT_MAX_FILE_SIZE,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
/**
* GstMultiFileSink:max-file-duration:
*
* Maximum file size before starting a new file in max-size mode.
*/
g_object_class_install_property (gobject_class, PROP_MAX_FILE_DURATION,
g_param_spec_uint64 ("max-file-duration", "Maximum File Duration",
"Maximum file duration before starting a new file in max-size mode",
0, G_MAXUINT64, DEFAULT_MAX_FILE_DURATION,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
/** /**
* GstMultiFileSink:aggregate-gops: * GstMultiFileSink:aggregate-gops:
* *
...@@ -317,6 +334,7 @@ gst_multi_file_sink_init (GstMultiFileSink * multifilesink) ...@@ -317,6 +334,7 @@ gst_multi_file_sink_init (GstMultiFileSink * multifilesink)
multifilesink->post_messages = DEFAULT_POST_MESSAGES; multifilesink->post_messages = DEFAULT_POST_MESSAGES;
multifilesink->max_files = DEFAULT_MAX_FILES; multifilesink->max_files = DEFAULT_MAX_FILES;
multifilesink->max_file_size = DEFAULT_MAX_FILE_SIZE; multifilesink->max_file_size = DEFAULT_MAX_FILE_SIZE;
multifilesink->max_file_duration = DEFAULT_MAX_FILE_DURATION;
multifilesink->files = NULL; multifilesink->files = NULL;
multifilesink->n_files = 0; multifilesink->n_files = 0;
...@@ -377,6 +395,9 @@ gst_multi_file_sink_set_property (GObject * object, guint prop_id, ...@@ -377,6 +395,9 @@ gst_multi_file_sink_set_property (GObject * object, guint prop_id,
case PROP_MAX_FILE_SIZE: case PROP_MAX_FILE_SIZE:
sink->max_file_size = g_value_get_uint64 (value); sink->max_file_size = g_value_get_uint64 (value);
break; break;
case PROP_MAX_FILE_DURATION:
sink->max_file_duration = g_value_get_uint64 (value);
break;
case PROP_AGGREGATE_GOPS: case PROP_AGGREGATE_GOPS:
sink->aggregate_gops = g_value_get_boolean (value); sink->aggregate_gops = g_value_get_boolean (value);
break; break;
...@@ -411,6 +432,9 @@ gst_multi_file_sink_get_property (GObject * object, guint prop_id, ...@@ -411,6 +432,9 @@ gst_multi_file_sink_get_property (GObject * object, guint prop_id,
case PROP_MAX_FILE_SIZE: case PROP_MAX_FILE_SIZE:
g_value_set_uint64 (value, sink->max_file_size); g_value_set_uint64 (value, sink->max_file_size);
break; break;
case PROP_MAX_FILE_DURATION:
g_value_set_uint64 (value, sink->max_file_duration);
break;
case PROP_AGGREGATE_GOPS: case PROP_AGGREGATE_GOPS:
g_value_set_boolean (value, sink->aggregate_gops); g_value_set_boolean (value, sink->aggregate_gops);
break; break;
...@@ -428,6 +452,7 @@ gst_multi_file_sink_start (GstBaseSink * bsink) ...@@ -428,6 +452,7 @@ gst_multi_file_sink_start (GstBaseSink * bsink)
if (sink->aggregate_gops) if (sink->aggregate_gops)
sink->gop_adapter = gst_adapter_new (); sink->gop_adapter = gst_adapter_new ();
sink->potential_next_gop = NULL; sink->potential_next_gop = NULL;
sink->file_pts = GST_CLOCK_TIME_NONE;
return TRUE; return TRUE;
} }
...@@ -704,6 +729,46 @@ gst_multi_file_sink_write_buffer (GstMultiFileSink * multifilesink, ...@@ -704,6 +729,46 @@ gst_multi_file_sink_write_buffer (GstMultiFileSink * multifilesink,
multifilesink->cur_file_size += map.size; multifilesink->cur_file_size += map.size;
break; break;
} }
case GST_MULTI_FILE_SINK_NEXT_MAX_DURATION:{
GstClockTime new_duration = 0;
if (GST_BUFFER_PTS_IS_VALID (buffer)
&& GST_CLOCK_TIME_IS_VALID (multifilesink->file_pts)) {
/* The new duration will extend to this new buffer pts ... */
new_duration = GST_BUFFER_PTS (buffer) - multifilesink->file_pts;
/* ... and duration (if it has one) */
if (GST_BUFFER_DURATION_IS_VALID (buffer))
new_duration += GST_BUFFER_DURATION (buffer);
}
if (new_duration > multifilesink->max_file_duration) {
GST_INFO_OBJECT (multifilesink,
"new_duration: %" G_GUINT64_FORMAT ", max. duration %"
G_GUINT64_FORMAT, new_duration, multifilesink->max_file_duration);
if (multifilesink->file != NULL) {
first_file = FALSE;
gst_multi_file_sink_close_file (multifilesink, buffer);
}
}
if (multifilesink->file == NULL) {
if (!gst_multi_file_sink_open_next_file (multifilesink))
goto stdio_write_error;
multifilesink->file_pts = GST_BUFFER_PTS (buffer);
if (!first_file)
gst_multi_file_sink_write_stream_headers (multifilesink);
}
ret = fwrite (map.data, map.size, 1, multifilesink->file);
if (ret != 1)
goto stdio_write_error;
break;
}
default: default:
g_assert_not_reached (); g_assert_not_reached ();
} }
...@@ -753,8 +818,13 @@ gst_multi_file_sink_render (GstBaseSink * bsink, GstBuffer * buffer) ...@@ -753,8 +818,13 @@ gst_multi_file_sink_render (GstBaseSink * bsink, GstBuffer * buffer)
g_list_append (sink->potential_next_gop, gst_buffer_ref (buffer)); g_list_append (sink->potential_next_gop, gst_buffer_ref (buffer));
} else { } else {
if (key_unit && avail > 0) { if (key_unit && avail > 0) {
GstClockTime pts, dts;
GST_LOG_OBJECT (sink, "Grabbing pending completed GOP"); GST_LOG_OBJECT (sink, "Grabbing pending completed GOP");
pts = gst_adapter_prev_pts_at_offset (sink->gop_adapter, 0, NULL);
dts = gst_adapter_prev_dts_at_offset (sink->gop_adapter, 0, NULL);
gop_buffer = gst_adapter_take_buffer (sink->gop_adapter, avail); gop_buffer = gst_adapter_take_buffer (sink->gop_adapter, avail);
GST_BUFFER_PTS (gop_buffer) = pts;
GST_BUFFER_DTS (gop_buffer) = dts;
} }
/* just accumulate the buffer */ /* just accumulate the buffer */
...@@ -775,6 +845,12 @@ gst_multi_file_sink_render (GstBaseSink * bsink, GstBuffer * buffer) ...@@ -775,6 +845,12 @@ gst_multi_file_sink_render (GstBaseSink * bsink, GstBuffer * buffer)
if (gop_buffer != NULL) { if (gop_buffer != NULL) {
GST_DEBUG_OBJECT (sink, "writing out pending GOP, %u bytes", avail); GST_DEBUG_OBJECT (sink, "writing out pending GOP, %u bytes", avail);
GST_DEBUG_OBJECT (sink,
"gop buffer pts:%" GST_TIME_FORMAT " dts:%" GST_TIME_FORMAT
" duration:%" GST_TIME_FORMAT,
GST_TIME_ARGS (GST_BUFFER_PTS (gop_buffer)),
GST_TIME_ARGS (GST_BUFFER_DTS (gop_buffer)),
GST_TIME_ARGS (GST_BUFFER_DURATION (gop_buffer)));
flow = gst_multi_file_sink_write_buffer (sink, gop_buffer); flow = gst_multi_file_sink_write_buffer (sink, gop_buffer);
gst_buffer_unref (gop_buffer); gst_buffer_unref (gop_buffer);
} }
......
...@@ -62,6 +62,8 @@ typedef struct _GstMultiFileSinkClass GstMultiFileSinkClass; ...@@ -62,6 +62,8 @@ typedef struct _GstMultiFileSinkClass GstMultiFileSinkClass;
* event * event
* @GST_MULTI_FILE_SINK_NEXT_MAX_SIZE: New file when the configured maximum file * @GST_MULTI_FILE_SINK_NEXT_MAX_SIZE: New file when the configured maximum file
* size would be exceeded with the next buffer or buffer list * size would be exceeded with the next buffer or buffer list
* @GST_MULTI_FILE_SINK_NEXT_MAX_DURATION: New file when the configured maximum duration
* would be exceeded with the next buffer or buffer list
* *
* File splitting modes. * File splitting modes.
*/ */
...@@ -70,7 +72,8 @@ typedef enum { ...@@ -70,7 +72,8 @@ typedef enum {
GST_MULTI_FILE_SINK_NEXT_DISCONT, GST_MULTI_FILE_SINK_NEXT_DISCONT,
GST_MULTI_FILE_SINK_NEXT_KEY_FRAME, GST_MULTI_FILE_SINK_NEXT_KEY_FRAME,
GST_MULTI_FILE_SINK_NEXT_KEY_UNIT_EVENT, GST_MULTI_FILE_SINK_NEXT_KEY_UNIT_EVENT,
GST_MULTI_FILE_SINK_NEXT_MAX_SIZE GST_MULTI_FILE_SINK_NEXT_MAX_SIZE,
GST_MULTI_FILE_SINK_NEXT_MAX_DURATION
} GstMultiFileSinkNext; } GstMultiFileSinkNext;
struct _GstMultiFileSink struct _GstMultiFileSink
...@@ -95,6 +98,9 @@ struct _GstMultiFileSink ...@@ -95,6 +98,9 @@ struct _GstMultiFileSink
guint64 cur_file_size; guint64 cur_file_size;
guint64 max_file_size; guint64 max_file_size;
GstClockTime file_pts;
GstClockTime max_file_duration;
gboolean aggregate_gops; gboolean aggregate_gops;
GstAdapter *gop_adapter; /* to aggregate GOPs */ GstAdapter *gop_adapter; /* to aggregate GOPs */
GList *potential_next_gop; /* To detect false-positives */ GList *potential_next_gop; /* To detect false-positives */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment