gstsplitmuxsink.c 128 KB
Newer Older
1
/* GStreamer Muxer bin that splits output stream by size/time
2
 * Copyright (C) <2014-2019> Jan Schmidt <jan@centricular.com>
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 */

/**
 * SECTION:element-splitmuxsink
22
 * @title: splitmuxsink
23
24
25
26
27
 * @short_description: Muxer wrapper for splitting output stream by size or time
 *
 * This element wraps a muxer and a sink, and starts a new file when the mux
 * contents are about to cross a threshold of maximum size of maximum time,
 * splitting at video keyframe boundaries. Exactly one input video stream
28
 * can be muxed, with as many accompanying audio and subtitle streams as
29
30
31
32
33
34
35
36
 * desired.
 *
 * By default, it uses mp4mux and filesink, but they can be changed via
 * the 'muxer' and 'sink' properties.
 *
 * The minimum file size is 1 GOP, however - so limits may be overrun if the
 * distance between any 2 keyframes is larger than the limits.
 *
37
38
39
40
 * If a video stream is available, the splitting process is driven by the video
 * stream contents, and the video stream must contain closed GOPs for the output
 * file parts to be played individually correctly. In the absence of a video
 * stream, the first available stream is used as reference for synchronization.
41
 *
42
43
44
45
46
47
48
 * In the async-finalize mode, when the threshold is crossed, the old muxer
 * and sink is disconnected from the pipeline and left to finish the file
 * asynchronously, and a new muxer and sink is created to continue with the
 * next fragment. For that reason, instead of muxer and sink objects, the
 * muxer-factory and sink-factory properties are used to construct the new
 * objects, together with muxer-properties and sink-properties.
 *
49
 * ## Example pipelines
50
 * |[
51
 * gst-launch-1.0 -e v4l2src num-buffers=500 ! video/x-raw,width=320,height=240 ! videoconvert ! queue ! timeoverlay ! x264enc key-int-max=10 ! h264parse ! splitmuxsink location=video%02d.mov max-size-time=10000000000 max-size-bytes=1000000
52
53
54
55
 * ]|
 * Records a video stream captured from a v4l2 device and muxes it into
 * ISO mp4 files, splitting as needed to limit size/duration to 10 seconds
 * and 1MB maximum size.
56
57
58
59
60
61
62
 *
 * |[
 * gst-launch-1.0 -e v4l2src num-buffers=500 ! video/x-raw,width=320,height=240 ! videoconvert ! queue ! timeoverlay ! x264enc key-int-max=10 ! h264parse ! splitmuxsink location=video%02d.mkv max-size-time=10000000000 muxer-factory=matroskamux muxer-properties="properties,streamable=true"
 * ]|
 * Records a video stream captured from a v4l2 device and muxer it into
 * streamable Matroska files, splitting as needed to limit size/duration to 10
 * seconds. Each file will finalize asynchronously.
63
64
65
66
67
68
 *
 * |[
 * gst-launch-1.0 videotestsrc num-buffers=10 ! jpegenc ! .video splitmuxsink muxer=qtmux muxer-pad-map=x-pad-map,video=video_1 location=test%05d.mp4 -v
 * ]|
 * Records 10 frames to an mp4 file, using a muxer-pad-map to make explicit mappings between the splitmuxsink sink pad and the corresponding muxer pad
 * it will deliver to.
69
70
71
72
73
74
75
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <string.h>
76
#include <glib/gstdio.h>
77
#include <gst/video/video.h>
78
79
80
81
82
#include "gstsplitmuxsink.h"

GST_DEBUG_CATEGORY_STATIC (splitmux_debug);
#define GST_CAT_DEFAULT splitmux_debug

83
84
85
#define GST_SPLITMUX_STATE_LOCK(s) g_mutex_lock(&(s)->state_lock)
#define GST_SPLITMUX_STATE_UNLOCK(s) g_mutex_unlock(&(s)->state_lock)

86
87
#define GST_SPLITMUX_LOCK(s) g_mutex_lock(&(s)->lock)
#define GST_SPLITMUX_UNLOCK(s) g_mutex_unlock(&(s)->lock)
88
89
90
91
92
#define GST_SPLITMUX_WAIT_INPUT(s) g_cond_wait (&(s)->input_cond, &(s)->lock)
#define GST_SPLITMUX_BROADCAST_INPUT(s) g_cond_broadcast (&(s)->input_cond)

#define GST_SPLITMUX_WAIT_OUTPUT(s) g_cond_wait (&(s)->output_cond, &(s)->lock)
#define GST_SPLITMUX_BROADCAST_OUTPUT(s) g_cond_broadcast (&(s)->output_cond)
93

94
static void split_now (GstSplitMuxSink * splitmux);
95
static void split_after (GstSplitMuxSink * splitmux);
96
97
static void split_at_running_time (GstSplitMuxSink * splitmux,
    GstClockTime split_time);
98

99
100
101
102
enum
{
  PROP_0,
  PROP_LOCATION,
103
  PROP_START_INDEX,
104
105
  PROP_MAX_SIZE_TIME,
  PROP_MAX_SIZE_BYTES,
106
  PROP_MAX_SIZE_TIMECODE,
107
  PROP_SEND_KEYFRAME_REQUESTS,
108
  PROP_MAX_FILES,
109
  PROP_MUXER_OVERHEAD,
110
  PROP_USE_ROBUST_MUXING,
111
  PROP_ALIGNMENT_THRESHOLD,
112
  PROP_MUXER,
113
114
  PROP_SINK,
  PROP_RESET_MUXER,
115
116
  PROP_ASYNC_FINALIZE,
  PROP_MUXER_FACTORY,
117
  PROP_MUXER_PRESET,
118
119
  PROP_MUXER_PROPERTIES,
  PROP_SINK_FACTORY,
120
  PROP_SINK_PRESET,
121
122
  PROP_SINK_PROPERTIES,
  PROP_MUXERPAD_MAP
123
124
125
126
};

#define DEFAULT_MAX_SIZE_TIME       0
#define DEFAULT_MAX_SIZE_BYTES      0
127
#define DEFAULT_MAX_FILES           0
128
#define DEFAULT_MUXER_OVERHEAD      0.02
129
#define DEFAULT_SEND_KEYFRAME_REQUESTS FALSE
130
#define DEFAULT_ALIGNMENT_THRESHOLD 0
131
132
#define DEFAULT_MUXER "mp4mux"
#define DEFAULT_SINK "filesink"
133
#define DEFAULT_USE_ROBUST_MUXING FALSE
134
#define DEFAULT_RESET_MUXER TRUE
135
#define DEFAULT_ASYNC_FINALIZE FALSE
136
#define DEFAULT_START_INDEX 0
137
138
139
140
141
142

typedef struct _AsyncEosHelper
{
  MqStreamCtx *ctx;
  GstPad *pad;
} AsyncEosHelper;
143

144
145
146
enum
{
  SIGNAL_FORMAT_LOCATION,
147
  SIGNAL_FORMAT_LOCATION_FULL,
148
  SIGNAL_SPLIT_NOW,
149
  SIGNAL_SPLIT_AFTER,
150
  SIGNAL_SPLIT_AT_RUNNING_TIME,
151
152
  SIGNAL_MUXER_ADDED,
  SIGNAL_SINK_ADDED,
153
154
155
156
157
  SIGNAL_LAST
};

static guint signals[SIGNAL_LAST];

158
159
160
161
162
static GstStaticPadTemplate video_sink_template =
GST_STATIC_PAD_TEMPLATE ("video",
    GST_PAD_SINK,
    GST_PAD_REQUEST,
    GST_STATIC_CAPS_ANY);
163
164
165
166
167
static GstStaticPadTemplate video_aux_sink_template =
GST_STATIC_PAD_TEMPLATE ("video_aux_%u",
    GST_PAD_SINK,
    GST_PAD_REQUEST,
    GST_STATIC_CAPS_ANY);
168
169
170
171
172
173
174
175
176
177
static GstStaticPadTemplate audio_sink_template =
GST_STATIC_PAD_TEMPLATE ("audio_%u",
    GST_PAD_SINK,
    GST_PAD_REQUEST,
    GST_STATIC_CAPS_ANY);
static GstStaticPadTemplate subtitle_sink_template =
GST_STATIC_PAD_TEMPLATE ("subtitle_%u",
    GST_PAD_SINK,
    GST_PAD_REQUEST,
    GST_STATIC_CAPS_ANY);
178
179
180
181
182
static GstStaticPadTemplate caption_sink_template =
GST_STATIC_PAD_TEMPLATE ("caption_%u",
    GST_PAD_SINK,
    GST_PAD_REQUEST,
    GST_STATIC_CAPS_ANY);
183
184

static GQuark PAD_CONTEXT;
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
static GQuark EOS_FROM_US;
static GQuark RUNNING_TIME;
/* EOS_FROM_US is only valid in async-finalize mode. We need to know whether
 * to forward an incoming EOS message, but we cannot rely on the state of the
 * splitmux anymore, so we set this qdata on the sink instead.
 * The muxer and sink must be destroyed after both of these things have
 * finished:
 * 1) The EOS message has been sent when the fragment is ending
 * 2) The muxer has been unlinked and relinked
 * Therefore, EOS_FROM_US can have these two values:
 * 0: EOS was not requested from us. Forward the message. The muxer and the
 * sink will be destroyed together with the rest of the bin.
 * 1: EOS was requested from us, but the other of the two tasks hasn't
 * finished. Set EOS_FROM_US to 2 and do your stuff.
 * 2: EOS was requested from us and the other of the two tasks has finished.
 * Now we can destroy the muxer and the sink.
 */
202
203
204
205
206

static void
_do_init (void)
{
  PAD_CONTEXT = g_quark_from_static_string ("pad-context");
207
208
  EOS_FROM_US = g_quark_from_static_string ("eos-from-us");
  RUNNING_TIME = g_quark_from_static_string ("running-time");
209
210
  GST_DEBUG_CATEGORY_INIT (splitmux_debug, "splitmuxsink", 0,
      "Split File Muxing Sink");
211
212
213
214
215
}

#define gst_splitmux_sink_parent_class parent_class
G_DEFINE_TYPE_EXTENDED (GstSplitMuxSink, gst_splitmux_sink, GST_TYPE_BIN, 0,
    _do_init ());
216
217
GST_ELEMENT_REGISTER_DEFINE (splitmuxsink, "splitmuxsink", GST_RANK_NONE,
    GST_TYPE_SPLITMUX_SINK);
218

219
static gboolean create_muxer (GstSplitMuxSink * splitmux);
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
static gboolean create_sink (GstSplitMuxSink * splitmux);
static void gst_splitmux_sink_set_property (GObject * object, guint prop_id,
    const GValue * value, GParamSpec * pspec);
static void gst_splitmux_sink_get_property (GObject * object, guint prop_id,
    GValue * value, GParamSpec * pspec);
static void gst_splitmux_sink_dispose (GObject * object);
static void gst_splitmux_sink_finalize (GObject * object);

static GstPad *gst_splitmux_sink_request_new_pad (GstElement * element,
    GstPadTemplate * templ, const gchar * name, const GstCaps * caps);
static void gst_splitmux_sink_release_pad (GstElement * element, GstPad * pad);

static GstStateChangeReturn gst_splitmux_sink_change_state (GstElement *
    element, GstStateChange transition);

static void bus_handler (GstBin * bin, GstMessage * msg);
236
237
static void set_next_filename (GstSplitMuxSink * splitmux, MqStreamCtx * ctx);
static void start_next_fragment (GstSplitMuxSink * splitmux, MqStreamCtx * ctx);
238
static void mq_stream_ctx_free (MqStreamCtx * ctx);
239
static void grow_blocked_queues (GstSplitMuxSink * splitmux);
240

241
static void gst_splitmux_sink_ensure_max_files (GstSplitMuxSink * splitmux);
242
243
static GstElement *create_element (GstSplitMuxSink * splitmux,
    const gchar * factory, const gchar * name, gboolean locked);
244

245
static void do_async_done (GstSplitMuxSink * splitmux);
246
static void gst_splitmux_reset_timecode (GstSplitMuxSink * splitmux);
247

248
249
250
251
252
253
254
255
256
257
258
259
static MqStreamBuf *
mq_stream_buf_new (void)
{
  return g_slice_new0 (MqStreamBuf);
}

static void
mq_stream_buf_free (MqStreamBuf * data)
{
  g_slice_free (MqStreamBuf, data);
}

260
261
262
263
264
265
266
267
268
269
270
271
static SplitMuxOutputCommand *
out_cmd_buf_new (void)
{
  return g_slice_new0 (SplitMuxOutputCommand);
}

static void
out_cmd_buf_free (SplitMuxOutputCommand * data)
{
  g_slice_free (SplitMuxOutputCommand, data);
}

272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
static void
gst_splitmux_sink_class_init (GstSplitMuxSinkClass * klass)
{
  GObjectClass *gobject_class = (GObjectClass *) klass;
  GstElementClass *gstelement_class = (GstElementClass *) klass;
  GstBinClass *gstbin_class = (GstBinClass *) klass;

  gobject_class->set_property = gst_splitmux_sink_set_property;
  gobject_class->get_property = gst_splitmux_sink_get_property;
  gobject_class->dispose = gst_splitmux_sink_dispose;
  gobject_class->finalize = gst_splitmux_sink_finalize;

  gst_element_class_set_static_metadata (gstelement_class,
      "Split Muxing Bin", "Generic/Bin/Muxer",
      "Convenience bin that muxes incoming streams into multiple time/size limited files",
      "Jan Schmidt <jan@centricular.com>");

289
290
  gst_element_class_add_static_pad_template (gstelement_class,
      &video_sink_template);
291
292
  gst_element_class_add_static_pad_template (gstelement_class,
      &video_aux_sink_template);
293
294
295
296
  gst_element_class_add_static_pad_template (gstelement_class,
      &audio_sink_template);
  gst_element_class_add_static_pad_template (gstelement_class,
      &subtitle_sink_template);
297
298
  gst_element_class_add_static_pad_template (gstelement_class,
      &caption_sink_template);
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321

  gstelement_class->change_state =
      GST_DEBUG_FUNCPTR (gst_splitmux_sink_change_state);
  gstelement_class->request_new_pad =
      GST_DEBUG_FUNCPTR (gst_splitmux_sink_request_new_pad);
  gstelement_class->release_pad =
      GST_DEBUG_FUNCPTR (gst_splitmux_sink_release_pad);

  gstbin_class->handle_message = bus_handler;

  g_object_class_install_property (gobject_class, PROP_LOCATION,
      g_param_spec_string ("location", "File Output Pattern",
          "Format string pattern for the location of the files to write (e.g. video%05d.mp4)",
          NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
  g_object_class_install_property (gobject_class, PROP_MUXER_OVERHEAD,
      g_param_spec_double ("mux-overhead", "Muxing Overhead",
          "Extra size overhead of muxing (0.02 = 2%)", 0.0, 1.0,
          DEFAULT_MUXER_OVERHEAD,
          G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));

  g_object_class_install_property (gobject_class, PROP_MAX_SIZE_TIME,
      g_param_spec_uint64 ("max-size-time", "Max. size (ns)",
          "Max. amount of time per file (in ns, 0=disable)", 0, G_MAXUINT64,
322
323
324
          DEFAULT_MAX_SIZE_TIME,
          G_PARAM_READWRITE | GST_PARAM_MUTABLE_READY |
          G_PARAM_STATIC_STRINGS));
325
326
327
  g_object_class_install_property (gobject_class, PROP_MAX_SIZE_BYTES,
      g_param_spec_uint64 ("max-size-bytes", "Max. size bytes",
          "Max. amount of data per file (in bytes, 0=disable)", 0, G_MAXUINT64,
328
329
330
          DEFAULT_MAX_SIZE_BYTES,
          G_PARAM_READWRITE | GST_PARAM_MUTABLE_READY |
          G_PARAM_STATIC_STRINGS));
331
332
333
334
  g_object_class_install_property (gobject_class, PROP_MAX_SIZE_TIMECODE,
      g_param_spec_string ("max-size-timecode", "Maximum timecode difference",
          "Maximum difference in timecode between first and last frame. "
          "Separator is assumed to be \":\" everywhere (e.g. 01:00:00:00). "
335
336
337
          "Will only be effective if a timecode track is present.", NULL,
          G_PARAM_READWRITE | GST_PARAM_MUTABLE_READY |
          G_PARAM_STATIC_STRINGS));
338
339
340
341
342
343
  g_object_class_install_property (gobject_class, PROP_SEND_KEYFRAME_REQUESTS,
      g_param_spec_boolean ("send-keyframe-requests",
          "Request keyframes at max-size-time",
          "Request a keyframe every max-size-time ns to try splitting at that point. "
          "Needs max-size-bytes to be 0 in order to be effective.",
          DEFAULT_SEND_KEYFRAME_REQUESTS,
344
345
          G_PARAM_READWRITE | GST_PARAM_MUTABLE_READY |
          G_PARAM_STATIC_STRINGS));
346
347
348
  g_object_class_install_property (gobject_class, PROP_MAX_FILES,
      g_param_spec_uint ("max-files", "Max files",
          "Maximum number of files to keep on disk. Once the maximum is reached,"
349
350
          "old files start to be deleted to make room for new ones.", 0,
          G_MAXUINT, DEFAULT_MAX_FILES,
351
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
352
353
354
  g_object_class_install_property (gobject_class, PROP_ALIGNMENT_THRESHOLD,
      g_param_spec_uint64 ("alignment-threshold", "Alignment threshold (ns)",
          "Allow non-reference streams to be that many ns before the reference"
355
356
357
          " stream", 0, G_MAXUINT64, DEFAULT_ALIGNMENT_THRESHOLD,
          G_PARAM_READWRITE | GST_PARAM_MUTABLE_READY |
          G_PARAM_STATIC_STRINGS));
358
359
360

  g_object_class_install_property (gobject_class, PROP_MUXER,
      g_param_spec_object ("muxer", "Muxer",
361
362
          "The muxer element to use (NULL = default mp4mux). "
          "Valid only for async-finalize = FALSE",
363
364
365
          GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
  g_object_class_install_property (gobject_class, PROP_SINK,
      g_param_spec_object ("sink", "Sink",
366
367
          "The sink element (or element chain) to use (NULL = default filesink). "
          "Valid only for async-finalize = FALSE",
368
          GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
369

370
371
372
373
374
375
376
  g_object_class_install_property (gobject_class, PROP_USE_ROBUST_MUXING,
      g_param_spec_boolean ("use-robust-muxing",
          "Support robust-muxing mode of some muxers",
          "Check if muxers support robust muxing via the reserved-max-duration and "
          "reserved-duration-remaining properties and use them if so. "
          "(Only present on qtmux and mp4mux for now). splitmuxsink may then also "
          " create new fragments if the reserved header space is about to overflow. "
377
378
          "Note that for mp4mux and qtmux, reserved-moov-update-period must be set "
          "manually by the app to a non-zero value for robust muxing to have an effect.",
379
380
381
          DEFAULT_USE_ROBUST_MUXING,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));

382
383
384
385
386
387
  g_object_class_install_property (gobject_class, PROP_RESET_MUXER,
      g_param_spec_boolean ("reset-muxer",
          "Reset Muxer",
          "Reset the muxer after each segment. Disabling this will not work for most muxers.",
          DEFAULT_RESET_MUXER, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));

388
389
390
391
392
393
394
395
396
397
  g_object_class_install_property (gobject_class, PROP_ASYNC_FINALIZE,
      g_param_spec_boolean ("async-finalize",
          "Finalize fragments asynchronously",
          "Finalize each fragment asynchronously and start a new one",
          DEFAULT_ASYNC_FINALIZE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
  g_object_class_install_property (gobject_class, PROP_MUXER_FACTORY,
      g_param_spec_string ("muxer-factory", "Muxer factory",
          "The muxer element factory to use (default = mp4mux). "
          "Valid only for async-finalize = TRUE",
          "mp4mux", G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
398
399
400
401
402
403
404
405
406
407
408
409
410
  /**
   * GstSplitMuxSink:muxer-preset
   *
   * An optional #GstPreset name to use for the muxer. This only has an effect
   * in `async-finalize=TRUE` mode.
   *
   * Since: 1.18
   */
  g_object_class_install_property (gobject_class, PROP_MUXER_PRESET,
      g_param_spec_string ("muxer-preset", "Muxer preset",
          "The muxer preset to use. "
          "Valid only for async-finalize = TRUE",
          NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
411
412
413
414
415
416
417
418
419
420
421
  g_object_class_install_property (gobject_class, PROP_MUXER_PROPERTIES,
      g_param_spec_boxed ("muxer-properties", "Muxer properties",
          "The muxer element properties to use. "
          "Example: {properties,boolean-prop=true,string-prop=\"hi\"}. "
          "Valid only for async-finalize = TRUE",
          GST_TYPE_STRUCTURE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
  g_object_class_install_property (gobject_class, PROP_SINK_FACTORY,
      g_param_spec_string ("sink-factory", "Sink factory",
          "The sink element factory to use (default = filesink). "
          "Valid only for async-finalize = TRUE",
          "filesink", G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
422
423
424
425
426
427
428
429
430
431
432
433
434
  /**
   * GstSplitMuxSink:sink-preset
   *
   * An optional #GstPreset name to use for the sink. This only has an effect
   * in `async-finalize=TRUE` mode.
   *
   * Since: 1.18
   */
  g_object_class_install_property (gobject_class, PROP_SINK_PRESET,
      g_param_spec_string ("sink-preset", "Sink preset",
          "The sink preset to use. "
          "Valid only for async-finalize = TRUE",
          NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
435
436
437
438
439
440
  g_object_class_install_property (gobject_class, PROP_SINK_PROPERTIES,
      g_param_spec_boxed ("sink-properties", "Sink properties",
          "The sink element properties to use. "
          "Example: {properties,boolean-prop=true,string-prop=\"hi\"}. "
          "Valid only for async-finalize = TRUE",
          GST_TYPE_STRUCTURE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
441
442
443
444
445
  g_object_class_install_property (gobject_class, PROP_START_INDEX,
      g_param_spec_int ("start-index", "Start Index",
          "Start value of fragment index.",
          0, G_MAXINT, DEFAULT_START_INDEX,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
446

447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
  /**
   * GstSplitMuxSink::muxer-pad-map
   *
   * An optional GstStructure that provides a map from splitmuxsink sinkpad
   * names to muxer pad names they should feed. Splitmuxsink has some default
   * mapping behaviour to link video to video pads and audio to audio pads
   * that usually works fine. This property is useful if you need to ensure
   * a particular mapping to muxed streams.
   *
   * The GstStructure contains string fields like so:
   *   splitmuxsink muxer-pad-map=x-pad-map,video=video_1
   *
   * Since: 1.18
   */
  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_MUXERPAD_MAP,
      g_param_spec_boxed ("muxer-pad-map", "Muxer pad map",
          "A GstStructure specifies the mapping from splitmuxsink sink pads to muxer pads",
          GST_TYPE_STRUCTURE,
          (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));

467
468
469
470
471
  /**
   * GstSplitMuxSink::format-location:
   * @splitmux: the #GstSplitMuxSink
   * @fragment_id: the sequence number of the file to be created
   *
472
473
474
475
   * Returns: the location to be used for the next output file. This must be
   *    a newly-allocated string which will be freed with g_free() by the
   *    splitmuxsink element when it no longer needs it, so use g_strdup() or
   *    g_strdup_printf() or similar functions to allocate it.
476
477
478
479
   */
  signals[SIGNAL_FORMAT_LOCATION] =
      g_signal_new ("format-location", G_TYPE_FROM_CLASS (klass),
      G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_STRING, 1, G_TYPE_UINT);
480
481
482
483
484
485
486
487

  /**
   * GstSplitMuxSink::format-location-full:
   * @splitmux: the #GstSplitMuxSink
   * @fragment_id: the sequence number of the file to be created
   * @first_sample: A #GstSample containing the first buffer
   *   from the reference stream in the new file
   *
488
489
490
491
492
493
   * Returns: the location to be used for the next output file. This must be
   *    a newly-allocated string which will be freed with g_free() by the
   *    splitmuxsink element when it no longer needs it, so use g_strdup() or
   *    g_strdup_printf() or similar functions to allocate it.
   *
   * Since: 1.12
494
495
496
497
498
   */
  signals[SIGNAL_FORMAT_LOCATION_FULL] =
      g_signal_new ("format-location-full", G_TYPE_FROM_CLASS (klass),
      G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_STRING, 2, G_TYPE_UINT,
      GST_TYPE_SAMPLE);
499
500
501
502
503
504

  /**
   * GstSplitMuxSink::split-now:
   * @splitmux: the #GstSplitMuxSink
   *
   * When called by the user, this action signal splits the video file (and begins a new one) immediately.
505
   * The current GOP will be output to the new file.
506
507
508
509
510
   *
   * Since: 1.14
   */
  signals[SIGNAL_SPLIT_NOW] =
      g_signal_new ("split-now", G_TYPE_FROM_CLASS (klass),
511
512
513
      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
      G_STRUCT_OFFSET (GstSplitMuxSinkClass, split_now), NULL, NULL, NULL,
      G_TYPE_NONE, 0);
514

515
516
517
518
519
  /**
   * GstSplitMuxSink::split-after:
   * @splitmux: the #GstSplitMuxSink
   *
   * When called by the user, this action signal splits the video file (and begins a new one) immediately.
520
   * Unlike the 'split-now' signal, with 'split-after', the current GOP will be output to the old file.
521
522
523
524
525
   *
   * Since: 1.16
   */
  signals[SIGNAL_SPLIT_AFTER] =
      g_signal_new ("split-after", G_TYPE_FROM_CLASS (klass),
526
527
528
      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
      G_STRUCT_OFFSET (GstSplitMuxSinkClass, split_after), NULL, NULL, NULL,
      G_TYPE_NONE, 0);
529

530
  /**
531
   * GstSplitMuxSink::split-at-running-time:
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
   * @splitmux: the #GstSplitMuxSink
   *
   * When called by the user, this action signal splits the video file (and
   * begins a new one) as soon as the given running time is reached. If this
   * action signal is called multiple times, running times are queued up and
   * processed in the order they were given.
   *
   * Note that this is prone to race conditions, where said running time is
   * reached and surpassed before we had a chance to split. The file will
   * still split immediately, but in order to make sure that the split doesn't
   * happen too late, it is recommended to call this action signal from
   * something that will prevent further buffers from flowing into
   * splitmuxsink before the split is completed, such as a pad probe before
   * splitmuxsink.
   *
   *
   * Since: 1.16
   */
  signals[SIGNAL_SPLIT_AT_RUNNING_TIME] =
      g_signal_new ("split-at-running-time", G_TYPE_FROM_CLASS (klass),
552
553
554
      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
      G_STRUCT_OFFSET (GstSplitMuxSinkClass, split_at_running_time), NULL, NULL,
      NULL, G_TYPE_NONE, 1, G_TYPE_UINT64);
555

556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
  /**
   * GstSplitMuxSink::muxer-added:
   * @splitmux: the #GstSplitMuxSink
   * @muxer: the newly added muxer element
   *
   * Since: 1.14
   */
  signals[SIGNAL_MUXER_ADDED] =
      g_signal_new ("muxer-added", G_TYPE_FROM_CLASS (klass),
      G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, GST_TYPE_ELEMENT);

  /**
   * GstSplitMuxSink::sink-added:
   * @splitmux: the #GstSplitMuxSink
   * @sink: the newly added sink element
   *
   * Since: 1.14
   */
  signals[SIGNAL_SINK_ADDED] =
      g_signal_new ("sink-added", G_TYPE_FROM_CLASS (klass),
      G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, GST_TYPE_ELEMENT);

578
  klass->split_now = split_now;
579
  klass->split_after = split_after;
580
  klass->split_at_running_time = split_at_running_time;
581
582
583
584
585
586
}

static void
gst_splitmux_sink_init (GstSplitMuxSink * splitmux)
{
  g_mutex_init (&splitmux->lock);
587
  g_mutex_init (&splitmux->state_lock);
588
589
590
  g_cond_init (&splitmux->input_cond);
  g_cond_init (&splitmux->output_cond);
  g_queue_init (&splitmux->out_cmd_q);
591
592
593
594

  splitmux->mux_overhead = DEFAULT_MUXER_OVERHEAD;
  splitmux->threshold_time = DEFAULT_MAX_SIZE_TIME;
  splitmux->threshold_bytes = DEFAULT_MAX_SIZE_BYTES;
595
  splitmux->max_files = DEFAULT_MAX_FILES;
596
  splitmux->send_keyframe_requests = DEFAULT_SEND_KEYFRAME_REQUESTS;
597
  splitmux->alignment_threshold = DEFAULT_ALIGNMENT_THRESHOLD;
598
  splitmux->use_robust_muxing = DEFAULT_USE_ROBUST_MUXING;
599
  splitmux->reset_muxer = DEFAULT_RESET_MUXER;
600
601

  splitmux->threshold_timecode_str = NULL;
602
  gst_splitmux_reset_timecode (splitmux);
603

604
605
606
607
608
609
  splitmux->async_finalize = DEFAULT_ASYNC_FINALIZE;
  splitmux->muxer_factory = g_strdup (DEFAULT_MUXER);
  splitmux->muxer_properties = NULL;
  splitmux->sink_factory = g_strdup (DEFAULT_SINK);
  splitmux->sink_properties = NULL;

610
  GST_OBJECT_FLAG_SET (splitmux, GST_ELEMENT_FLAG_SINK);
611
612
  splitmux->split_requested = FALSE;
  splitmux->do_split_next_gop = FALSE;
613
  splitmux->times_to_split = gst_queue_array_new_for_struct (8, 8);
614
  splitmux->next_fku_time = GST_CLOCK_TIME_NONE;
615
616
617
}

static void
618
gst_splitmux_reset_elements (GstSplitMuxSink * splitmux)
619
{
620
621
622
  if (splitmux->muxer) {
    gst_element_set_locked_state (splitmux->muxer, TRUE);
    gst_element_set_state (splitmux->muxer, GST_STATE_NULL);
623
    gst_bin_remove (GST_BIN (splitmux), splitmux->muxer);
624
625
626
627
  }
  if (splitmux->active_sink) {
    gst_element_set_locked_state (splitmux->active_sink, TRUE);
    gst_element_set_state (splitmux->active_sink, GST_STATE_NULL);
628
    gst_bin_remove (GST_BIN (splitmux), splitmux->active_sink);
629
  }
630

631
  splitmux->sink = splitmux->active_sink = splitmux->muxer = NULL;
632
633
}

634
635
636
637
638
639
640
641
642
static void
gst_splitmux_reset_timecode (GstSplitMuxSink * splitmux)
{
  g_clear_pointer (&splitmux->in_tc, gst_video_time_code_free);
  g_clear_pointer (&splitmux->fragment_start_tc, gst_video_time_code_free);
  g_clear_pointer (&splitmux->gop_start_tc, gst_video_time_code_free);
  splitmux->next_fragment_start_tc_time = GST_CLOCK_TIME_NONE;
}

643
644
645
646
647
648
static void
gst_splitmux_sink_dispose (GObject * object)
{
  GstSplitMuxSink *splitmux = GST_SPLITMUX_SINK (object);

  /* Calling parent dispose invalidates all child pointers */
649
  splitmux->sink = splitmux->active_sink = splitmux->muxer = NULL;
650
651

  G_OBJECT_CLASS (parent_class)->dispose (object);
652
653
654
655
656
657
}

static void
gst_splitmux_sink_finalize (GObject * object)
{
  GstSplitMuxSink *splitmux = GST_SPLITMUX_SINK (object);
658
659
  g_cond_clear (&splitmux->input_cond);
  g_cond_clear (&splitmux->output_cond);
660
  g_mutex_clear (&splitmux->lock);
661
  g_mutex_clear (&splitmux->state_lock);
662
663
664
  g_queue_foreach (&splitmux->out_cmd_q, (GFunc) out_cmd_buf_free, NULL);
  g_queue_clear (&splitmux->out_cmd_q);

665
666
667
  if (splitmux->muxerpad_map)
    gst_structure_free (splitmux->muxerpad_map);

668
669
  if (splitmux->provided_sink)
    gst_object_unref (splitmux->provided_sink);
670
671
  if (splitmux->provided_muxer)
    gst_object_unref (splitmux->provided_muxer);
672

673
674
  if (splitmux->muxer_factory)
    g_free (splitmux->muxer_factory);
675
676
  if (splitmux->muxer_preset)
    g_free (splitmux->muxer_preset);
677
678
679
680
  if (splitmux->muxer_properties)
    gst_structure_free (splitmux->muxer_properties);
  if (splitmux->sink_factory)
    g_free (splitmux->sink_factory);
681
682
  if (splitmux->sink_preset)
    g_free (splitmux->sink_preset);
683
684
685
  if (splitmux->sink_properties)
    gst_structure_free (splitmux->sink_properties);

686
687
  if (splitmux->threshold_timecode_str)
    g_free (splitmux->threshold_timecode_str);
688
689
  if (splitmux->tc_interval)
    gst_video_time_code_interval_free (splitmux->tc_interval);
690

691
692
693
  if (splitmux->times_to_split)
    gst_queue_array_free (splitmux->times_to_split);

694
695
  g_free (splitmux->location);

696
697
698
  /* Make sure to free any un-released contexts. There should not be any,
   * because the dispose will have freed all request pads though */
  g_list_foreach (splitmux->contexts, (GFunc) mq_stream_ctx_free, NULL);
699
  g_list_free (splitmux->contexts);
700
  gst_splitmux_reset_timecode (splitmux);
701

702
703
704
  G_OBJECT_CLASS (parent_class)->finalize (object);
}

705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
/*
 * Set any time threshold to the muxer, if it has
 * reserved-max-duration and reserved-duration-remaining
 * properties. Called when creating/claiming the muxer
 * in create_elements() */
static void
update_muxer_properties (GstSplitMuxSink * sink)
{
  GObjectClass *klass;
  GstClockTime threshold_time;

  sink->muxer_has_reserved_props = FALSE;
  if (sink->muxer == NULL)
    return;
  klass = G_OBJECT_GET_CLASS (sink->muxer);
  if (g_object_class_find_property (klass, "reserved-max-duration") == NULL)
    return;
  if (g_object_class_find_property (klass,
          "reserved-duration-remaining") == NULL)
    return;
  sink->muxer_has_reserved_props = TRUE;

  GST_LOG_OBJECT (sink, "Setting muxer reserved time to %" GST_TIME_FORMAT,
      GST_TIME_ARGS (sink->threshold_time));
  GST_OBJECT_LOCK (sink);
  threshold_time = sink->threshold_time;
  GST_OBJECT_UNLOCK (sink);

  if (threshold_time > 0) {
    /* Tell the muxer how much space to reserve */
    GstClockTime muxer_threshold = threshold_time;
    g_object_set (sink->muxer, "reserved-max-duration", muxer_threshold, NULL);
  }
}

740
741
742
743
744
745
746
747
748
749
750
751
752
753
static void
gst_splitmux_sink_set_property (GObject * object, guint prop_id,
    const GValue * value, GParamSpec * pspec)
{
  GstSplitMuxSink *splitmux = GST_SPLITMUX_SINK (object);

  switch (prop_id) {
    case PROP_LOCATION:{
      GST_OBJECT_LOCK (splitmux);
      g_free (splitmux->location);
      splitmux->location = g_value_dup_string (value);
      GST_OBJECT_UNLOCK (splitmux);
      break;
    }
754
755
756
757
758
    case PROP_START_INDEX:
      GST_OBJECT_LOCK (splitmux);
      splitmux->start_index = g_value_get_int (value);
      GST_OBJECT_UNLOCK (splitmux);
      break;
759
760
761
762
763
764
765
766
767
768
    case PROP_MAX_SIZE_BYTES:
      GST_OBJECT_LOCK (splitmux);
      splitmux->threshold_bytes = g_value_get_uint64 (value);
      GST_OBJECT_UNLOCK (splitmux);
      break;
    case PROP_MAX_SIZE_TIME:
      GST_OBJECT_LOCK (splitmux);
      splitmux->threshold_time = g_value_get_uint64 (value);
      GST_OBJECT_UNLOCK (splitmux);
      break;
769
770
    case PROP_MAX_SIZE_TIMECODE:
      GST_OBJECT_LOCK (splitmux);
771
772
773
774
      g_free (splitmux->threshold_timecode_str);
      /* will be calculated later */
      g_clear_pointer (&splitmux->tc_interval,
          gst_video_time_code_interval_free);
775
      gst_splitmux_reset_timecode (splitmux);
776

777
      splitmux->threshold_timecode_str = g_value_dup_string (value);
778
779
780
781
782
783
784
785
786
787
788
      if (splitmux->threshold_timecode_str) {
        splitmux->tc_interval =
            gst_video_time_code_interval_new_from_string
            (splitmux->threshold_timecode_str);
        if (!splitmux->tc_interval) {
          g_warning ("Wrong timecode string %s",
              splitmux->threshold_timecode_str);
          g_free (splitmux->threshold_timecode_str);
          splitmux->threshold_timecode_str = NULL;
        }
      }
789
790
      GST_OBJECT_UNLOCK (splitmux);
      break;
791
792
793
794
795
    case PROP_SEND_KEYFRAME_REQUESTS:
      GST_OBJECT_LOCK (splitmux);
      splitmux->send_keyframe_requests = g_value_get_boolean (value);
      GST_OBJECT_UNLOCK (splitmux);
      break;
796
797
798
799
800
    case PROP_MAX_FILES:
      GST_OBJECT_LOCK (splitmux);
      splitmux->max_files = g_value_get_uint (value);
      GST_OBJECT_UNLOCK (splitmux);
      break;
801
802
803
804
805
    case PROP_MUXER_OVERHEAD:
      GST_OBJECT_LOCK (splitmux);
      splitmux->mux_overhead = g_value_get_double (value);
      GST_OBJECT_UNLOCK (splitmux);
      break;
806
807
808
809
810
811
812
    case PROP_USE_ROBUST_MUXING:
      GST_OBJECT_LOCK (splitmux);
      splitmux->use_robust_muxing = g_value_get_boolean (value);
      GST_OBJECT_UNLOCK (splitmux);
      if (splitmux->use_robust_muxing)
        update_muxer_properties (splitmux);
      break;
813
814
815
816
817
    case PROP_ALIGNMENT_THRESHOLD:
      GST_OBJECT_LOCK (splitmux);
      splitmux->alignment_threshold = g_value_get_uint64 (value);
      GST_OBJECT_UNLOCK (splitmux);
      break;
818
    case PROP_SINK:
819
      GST_OBJECT_LOCK (splitmux);
820
      gst_clear_object (&splitmux->provided_sink);
821
      splitmux->provided_sink = g_value_get_object (value);
822
823
      if (splitmux->provided_sink)
        gst_object_ref_sink (splitmux->provided_sink);
824
      GST_OBJECT_UNLOCK (splitmux);
825
826
      break;
    case PROP_MUXER:
827
      GST_OBJECT_LOCK (splitmux);
828
      gst_clear_object (&splitmux->provided_muxer);
829
      splitmux->provided_muxer = g_value_get_object (value);
830
831
      if (splitmux->provided_muxer)
        gst_object_ref_sink (splitmux->provided_muxer);
832
      GST_OBJECT_UNLOCK (splitmux);
833
      break;
834
835
836
837
838
    case PROP_RESET_MUXER:
      GST_OBJECT_LOCK (splitmux);
      splitmux->reset_muxer = g_value_get_boolean (value);
      GST_OBJECT_UNLOCK (splitmux);
      break;
839
840
841
842
843
844
845
846
847
848
849
850
    case PROP_ASYNC_FINALIZE:
      GST_OBJECT_LOCK (splitmux);
      splitmux->async_finalize = g_value_get_boolean (value);
      GST_OBJECT_UNLOCK (splitmux);
      break;
    case PROP_MUXER_FACTORY:
      GST_OBJECT_LOCK (splitmux);
      if (splitmux->muxer_factory)
        g_free (splitmux->muxer_factory);
      splitmux->muxer_factory = g_value_dup_string (value);
      GST_OBJECT_UNLOCK (splitmux);
      break;
851
852
853
854
855
856
857
    case PROP_MUXER_PRESET:
      GST_OBJECT_LOCK (splitmux);
      if (splitmux->muxer_preset)
        g_free (splitmux->muxer_preset);
      splitmux->muxer_preset = g_value_dup_string (value);
      GST_OBJECT_UNLOCK (splitmux);
      break;
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
    case PROP_MUXER_PROPERTIES:
      GST_OBJECT_LOCK (splitmux);
      if (splitmux->muxer_properties)
        gst_structure_free (splitmux->muxer_properties);
      if (gst_value_get_structure (value))
        splitmux->muxer_properties =
            gst_structure_copy (gst_value_get_structure (value));
      else
        splitmux->muxer_properties = NULL;
      GST_OBJECT_UNLOCK (splitmux);
      break;
    case PROP_SINK_FACTORY:
      GST_OBJECT_LOCK (splitmux);
      if (splitmux->sink_factory)
        g_free (splitmux->sink_factory);
      splitmux->sink_factory = g_value_dup_string (value);
      GST_OBJECT_UNLOCK (splitmux);
      break;
876
877
878
879
880
881
882
    case PROP_SINK_PRESET:
      GST_OBJECT_LOCK (splitmux);
      if (splitmux->sink_preset)
        g_free (splitmux->sink_preset);
      splitmux->sink_preset = g_value_dup_string (value);
      GST_OBJECT_UNLOCK (splitmux);
      break;
883
884
885
886
887
888
889
890
891
892
893
    case PROP_SINK_PROPERTIES:
      GST_OBJECT_LOCK (splitmux);
      if (splitmux->sink_properties)
        gst_structure_free (splitmux->sink_properties);
      if (gst_value_get_structure (value))
        splitmux->sink_properties =
            gst_structure_copy (gst_value_get_structure (value));
      else
        splitmux->sink_properties = NULL;
      GST_OBJECT_UNLOCK (splitmux);
      break;
894
895
896
897
898
899
900
901
902
903
904
905
906
907
    case PROP_MUXERPAD_MAP:
    {
      const GstStructure *s = gst_value_get_structure (value);
      GST_SPLITMUX_LOCK (splitmux);
      if (splitmux->muxerpad_map) {
        gst_structure_free (splitmux->muxerpad_map);
      }
      if (s)
        splitmux->muxerpad_map = gst_structure_copy (s);
      else
        splitmux->muxerpad_map = NULL;
      GST_SPLITMUX_UNLOCK (splitmux);
      break;
    }
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

static void
gst_splitmux_sink_get_property (GObject * object, guint prop_id,
    GValue * value, GParamSpec * pspec)
{
  GstSplitMuxSink *splitmux = GST_SPLITMUX_SINK (object);

  switch (prop_id) {
    case PROP_LOCATION:
      GST_OBJECT_LOCK (splitmux);
      g_value_set_string (value, splitmux->location);
      GST_OBJECT_UNLOCK (splitmux);
      break;
926
927
928
929
930
    case PROP_START_INDEX:
      GST_OBJECT_LOCK (splitmux);
      g_value_set_int (value, splitmux->start_index);
      GST_OBJECT_UNLOCK (splitmux);
      break;
931
932
933
934
935
936
937
938
939
940
    case PROP_MAX_SIZE_BYTES:
      GST_OBJECT_LOCK (splitmux);
      g_value_set_uint64 (value, splitmux->threshold_bytes);
      GST_OBJECT_UNLOCK (splitmux);
      break;
    case PROP_MAX_SIZE_TIME:
      GST_OBJECT_LOCK (splitmux);
      g_value_set_uint64 (value, splitmux->threshold_time);
      GST_OBJECT_UNLOCK (splitmux);
      break;
941
942
943
944
945
    case PROP_MAX_SIZE_TIMECODE:
      GST_OBJECT_LOCK (splitmux);
      g_value_set_string (value, splitmux->threshold_timecode_str);
      GST_OBJECT_UNLOCK (splitmux);
      break;
946
947
948
949
950
    case PROP_SEND_KEYFRAME_REQUESTS:
      GST_OBJECT_LOCK (splitmux);
      g_value_set_boolean (value, splitmux->send_keyframe_requests);
      GST_OBJECT_UNLOCK (splitmux);
      break;
951
952
953
954
955
    case PROP_MAX_FILES:
      GST_OBJECT_LOCK (splitmux);
      g_value_set_uint (value, splitmux->max_files);
      GST_OBJECT_UNLOCK (splitmux);
      break;
956
957
958
959
960
    case PROP_MUXER_OVERHEAD:
      GST_OBJECT_LOCK (splitmux);
      g_value_set_double (value, splitmux->mux_overhead);
      GST_OBJECT_UNLOCK (splitmux);
      break;
961
962
963
964
965
    case PROP_USE_ROBUST_MUXING:
      GST_OBJECT_LOCK (splitmux);
      g_value_set_boolean (value, splitmux->use_robust_muxing);
      GST_OBJECT_UNLOCK (splitmux);
      break;
966
967
968
969
970
    case PROP_ALIGNMENT_THRESHOLD:
      GST_OBJECT_LOCK (splitmux);
      g_value_set_uint64 (value, splitmux->alignment_threshold);
      GST_OBJECT_UNLOCK (splitmux);
      break;
971
    case PROP_SINK:
972
      GST_OBJECT_LOCK (splitmux);
973
      g_value_set_object (value, splitmux->provided_sink);
974
      GST_OBJECT_UNLOCK (splitmux);
975
976
      break;
    case PROP_MUXER:
977
      GST_OBJECT_LOCK (splitmux);
978
      g_value_set_object (value, splitmux->provided_muxer);
979
      GST_OBJECT_UNLOCK (splitmux);
980
      break;
981
982
983
984
985
    case PROP_RESET_MUXER:
      GST_OBJECT_LOCK (splitmux);
      g_value_set_boolean (value, splitmux->reset_muxer);
      GST_OBJECT_UNLOCK (splitmux);
      break;
986
987
988
989
990
991
992
993
994
995
    case PROP_ASYNC_FINALIZE:
      GST_OBJECT_LOCK (splitmux);
      g_value_set_boolean (value, splitmux->async_finalize);
      GST_OBJECT_UNLOCK (splitmux);
      break;
    case PROP_MUXER_FACTORY:
      GST_OBJECT_LOCK (splitmux);
      g_value_set_string (value, splitmux->muxer_factory);
      GST_OBJECT_UNLOCK (splitmux);
      break;
996
997
998
999
1000
    case PROP_MUXER_PRESET:
      GST_OBJECT_LOCK (splitmux);
      g_value_set_string (value, splitmux->muxer_preset);
      GST_OBJECT_UNLOCK (splitmux);
      break;