~ubuntu-branches/ubuntu/natty/gst-entrans/natty

« back to all changes in this revision

Viewing changes to gst/entrans/gstdam.c

  • Committer: Bazaar Package Importer
  • Author(s): Jonas Smedegaard
  • Date: 2010-09-13 19:49:29 UTC
  • Revision ID: james.westby@ubuntu.com-20100913194929-qz90a14xyxln9yfz
Tags: upstream-0.10.2
ImportĀ upstreamĀ versionĀ 0.10.2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* GStreamer Element
 
2
 * Copyright (C) 2006 Mark Nauwelaerts <mnauw@users.sourceforge.net>
 
3
 *
 
4
 * This library is free software; you can redistribute it and/or
 
5
 * modify it under the terms of the GNU Library General Public
 
6
 * License as published by the Free Software Foundation; either
 
7
 * version 2 of the License, or (at your option) any later version.
 
8
 *
 
9
 * This library is distributed in the hope that it will be useful,
 
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
12
 * Library General Public License for more details.
 
13
 *
 
14
 * You should have received a copy of the GNU Library General Public
 
15
 * License along with this library; if not, write to the
 
16
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 
17
 * Boston, MA 02110-1307, USA.
 
18
 */
 
19
 
 
20
/**
 
21
 * SECTION:element-dam
 
22
 *
 
23
 * <refsect2>
 
24
 * <para>
 
25
 * Special-purpose element that enables a "cutting" muxing pipeline.
 
26
 * This is used for transcoding purposes where it is not desired to mux
 
27
 * the entire original stream into a new stream, but only selected parts of it.
 
28
 * The desired parts in such a scenario are selected depending on
 
29
 * <link linkend="GstDam--segment-mode">segment-mode</link> property:
 
30
 * <itemizedlist>
 
31
 * <listitem>
 
32
 * <para>
 
33
 * using regular seek-events (segment-mode or seek-mode);
 
34
 * an initial flushing seek followed by a number of segmenting seeks and
 
35
 * finally concluded with a normal seek.
 
36
 * The element then drops buffers that are out of the selected segment
 
37
 * range, to compensate for some elements not doing so (yes, there are such ...).
 
38
 * The usual end-of-segment messages (or final EOS) allow the 'application'
 
39
 * to pass from one segment to the next.
 
40
 * </para>
 
41
 * </listitem>
 
42
 * <listitem>
 
43
 * <para>
 
44
 * cut-mode: allowing only a certain range of buffers to pass where the range is
 
45
 * specified by gst time (starting from 0).  When using
 
46
 * <link linkend="GstDam--use-count">counts</link>, such times are then
 
47
 * converted to either a frame count or a sample count and compared with
 
48
 * the corresponding count of the incoming data (using the format info
 
49
 * supplied by the caps) (so buffer timestamps are disregarded).
 
50
 * Alternatively, buffer timestamps can be used to decide on clipping.
 
51
 * </para>
 
52
 * </listitem>
 
53
 * </itemizedlist>
 
54
 * In addition, audio data can optionally be clipped to sample
 
55
 * <link linkend="GstDam--precision">precision</link> when
 
56
 * deciding based on buffer timestamps (it is always done so
 
57
 * when clipping based on counts).
 
58
 * </para>
 
59
 * <para>
 
60
 * The element can also be requested to send an
 
61
 * <link linkend="GstDam--force-eos">eos</link> event, so that streaming
 
62
 * can be cleanly completed, and e.g. muxers can complete EOS-actions.
 
63
 * </para>
 
64
 * <title>Usage</title>
 
65
 * <para>
 
66
 * This element is (likely) essentially in a muxing pipeline
 
67
 * whenever only a part of the input is selected, even when using segments.
 
68
 * The functionality to drop out-of-segment data is typically present in (base)sinks,
 
69
 * so this usually happens transparently in a playing pipeline.
 
70
 * However, in a muxing pipeline, a filesink can and should clearly have no impact
 
71
 * on this; so it is up to the rest of pipeline to perform this dropping.
 
72
 * Failing to do so would not only result in too much and unwanted data,
 
73
 * but will probably even block the muxing pipeline (in a collectpads instance),
 
74
 * due to (typically) an imbalance (in volume and timestamps)
 
75
 * in video and audio data.
 
76
 * </para>
 
77
 * <para>
 
78
 * In case of a demuxer element involved in providing input, it should be separated
 
79
 * from it by means of queue (or other thread boundary).
 
80
 * </para>
 
81
 * <para>
 
82
 * The sequence of events/operation is roughly as follows.
 
83
 * <itemizedlist>
 
84
 * <listitem>
 
85
 * <para>
 
86
 * The element starts up and announces its existence by posting a message on the bus.
 
87
 * This should be detected (synchronously) by the application using it,
 
88
 * which should block pads to prevent data flowing through prior to setup
 
89
 * being completed.
 
90
 * </para>
 
91
 * </listitem>
 
92
 * <listitem>
 
93
 * <para>
 
94
 * This setup (typically happening when all relevant pads have blocked) consists
 
95
 * of setting the proper "mode", and providing section info in case of cut-mode.
 
96
 * After this, streaming can be continued by (optionally) performing seek and
 
97
 * (typically) unblocking pads.  The element then performs as described above,
 
98
 * depending on mode, and either the last normal seek will give rise to EOS,
 
99
 * or the element generates EOS when having passed all data for the last section.
 
100
 * </para>
 
101
 * </listitem>
 
102
 * </itemizedlist>
 
103
 * </para>
 
104
 * </refsect2>
 
105
 *
 
106
 */
 
107
 
 
108
#ifdef HAVE_CONFIG_H
 
109
#include "config.h"
 
110
#endif
 
111
 
 
112
#include "plugin-entrans.h"
 
113
 
 
114
#define GST_TYPE_DAM \
 
115
  (gst_dam_get_type())
 
116
#define GST_DAM(obj) \
 
117
  (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_DAM,GstDam))
 
118
#define GST_DAM_CLASS(klass) \
 
119
  (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_DAM,GstDamClass))
 
120
#define GST_IS_DAM(obj) \
 
121
  (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_DAM))
 
122
#define GST_IS_DAM_CLASS(klass) \
 
123
  (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_DAM))
 
124
 
 
125
 
 
126
typedef struct _SectionInfo SectionInfo;
 
127
typedef struct _GstDam GstDam;
 
128
typedef struct _GstDamClass GstDamClass;
 
129
 
 
130
struct _SectionInfo
 
131
{
 
132
  guint64 begin_count, end_count;
 
133
  GstClockTime begin_time, end_time;
 
134
};
 
135
 
 
136
struct _GstDam
 
137
{
 
138
  GstBaseTransform basetransform;
 
139
 
 
140
  /* what mode we operate in */
 
141
  gboolean segment_mode;
 
142
  /* if cutting, use count or time */
 
143
  gboolean use_count;
 
144
  /* if audio and time-based, do precision slicing */
 
145
  gboolean precision;
 
146
  /* what we are actually doing in the drop area */
 
147
  gboolean dropping;
 
148
  /* count based dropping/cutting */
 
149
  /* eos has been requested for clean-up of stream */
 
150
  gboolean eos;
 
151
  /* do we respond to position query or pass along */
 
152
  gboolean handle_query;
 
153
  /* section management */
 
154
  gint section;
 
155
  GList *sections;
 
156
  GMemChunk *memchunk;
 
157
  SectionInfo *current;
 
158
  /* type of stream */
 
159
  gboolean type;
 
160
  /* (frame) position in stream */
 
161
  glong cut_position;
 
162
  /* audio position = byte position in stream */
 
163
  guint64 audio_count;
 
164
  /* stream audio info */
 
165
  gint sample, rate;
 
166
  /* stream video info */
 
167
  gint fps_num, fps_denom;
 
168
  /* segment info for current section */
 
169
  GstSegment segment;
 
170
  /* newsegment event we should sent for section */
 
171
  GstEvent *newsegment;
 
172
  /* segment-event sent for current section ?*/
 
173
  gboolean sent_segment;
 
174
  /* last timestamp seen */
 
175
  GstClockTime last_stamp;
 
176
 
 
177
  /* the parent's setcaps function */
 
178
  GstPadSetCapsFunction btrans_setcaps;
 
179
 
 
180
};
 
181
 
 
182
enum
 
183
{
 
184
  TYPE_VIDEO,
 
185
  TYPE_AUDIO,
 
186
  TYPE_OTHER
 
187
};
 
188
 
 
189
struct _GstDamClass
 
190
{
 
191
  GstBaseTransformClass parent_class;
 
192
};
 
193
 
 
194
GST_DEBUG_CATEGORY_STATIC (dam_debug);
 
195
#define GST_CAT_DEFAULT dam_debug
 
196
 
 
197
/* GstDam signals and args */
 
198
enum
 
199
{
 
200
  /* FILL ME */
 
201
  LAST_SIGNAL
 
202
};
 
203
 
 
204
enum
 
205
{
 
206
  PROP_0,
 
207
  PROP_SEGMENT_MODE,
 
208
  PROP_USE_COUNT,
 
209
  PROP_PRECISION,
 
210
  PROP_QUERY,
 
211
  PROP_SECTION,
 
212
  PROP_BEGIN_COUNT,
 
213
  PROP_END_COUNT,
 
214
  PROP_BEGIN_TIME,
 
215
  PROP_END_TIME,
 
216
  PROP_SAVE,
 
217
  PROP_EOS,
 
218
  PROP_SAMPLERATE,
 
219
  PROP_SAMPLEWIDTH,
 
220
  PROP_FRAMERATE
 
221
      /* FILL ME */
 
222
};
 
223
 
 
224
#define  DEFAULT_SEGMENT_MODE   TRUE
 
225
#define  DEFAULT_QUERY          FALSE
 
226
#define  DEFAULT_USE_COUNT      TRUE
 
227
#define  DEFAULT_PRECISION      FALSE
 
228
 
 
229
static GstElementDetails dam_details =
 
230
GST_ELEMENT_DETAILS ("Dam",
 
231
    "Generic",
 
232
    "Block and/or filter stream",
 
233
    "Mark Nauwelaerts <mnauw@users.sourceforge.net>");
 
234
 
 
235
static GstStaticPadTemplate gst_dam_src_template =
 
236
GST_STATIC_PAD_TEMPLATE (GST_BASE_TRANSFORM_SRC_NAME,
 
237
    GST_PAD_SRC,
 
238
    GST_PAD_ALWAYS,
 
239
    GST_STATIC_CAPS_ANY
 
240
    );
 
241
 
 
242
static GstStaticPadTemplate gst_dam_sink_template =
 
243
GST_STATIC_PAD_TEMPLATE (GST_BASE_TRANSFORM_SINK_NAME,
 
244
    GST_PAD_SINK,
 
245
    GST_PAD_ALWAYS,
 
246
    GST_STATIC_CAPS_ANY
 
247
    );
 
248
 
 
249
static gboolean gst_dam_start (GstBaseTransform * trans);
 
250
static gboolean gst_dam_stop (GstBaseTransform * trans);
 
251
static gboolean gst_dam_setcaps (GstPad * pad, GstCaps * caps);
 
252
static gboolean gst_dam_src_query (GstPad * pad, GstQuery * query);
 
253
    static gboolean gst_dam_event (GstBaseTransform * trans, GstEvent * event);
 
254
static GstFlowReturn gst_dam_chain (GstPad * pad, GstBuffer * in);
 
255
 
 
256
static void gst_dam_set_property (GObject * object, guint prop_id,
 
257
    const GValue * value, GParamSpec * pspec);
 
258
static void gst_dam_get_property (GObject * object, guint prop_id,
 
259
    GValue * value, GParamSpec * pspec);
 
260
 
 
261
GST_BOILERPLATE (GstDam, gst_dam, GstBaseTransform, GST_TYPE_BASE_TRANSFORM);
 
262
 
 
263
static void
 
264
gst_dam_base_init (gpointer g_class)
 
265
{
 
266
  GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
 
267
 
 
268
  gst_element_class_set_details (element_class, &dam_details);
 
269
 
 
270
  gst_element_class_add_pad_template (element_class,
 
271
      gst_static_pad_template_get (&gst_dam_sink_template));
 
272
  gst_element_class_add_pad_template (element_class,
 
273
      gst_static_pad_template_get (&gst_dam_src_template));
 
274
}
 
275
 
 
276
static void
 
277
gst_dam_class_init (GstDamClass * g_class)
 
278
{
 
279
  GObjectClass *gobject_class;
 
280
  GstBaseTransformClass *trans_class;
 
281
 
 
282
  gobject_class = G_OBJECT_CLASS (g_class);
 
283
  trans_class = GST_BASE_TRANSFORM_CLASS (g_class);
 
284
 
 
285
  GST_DEBUG_CATEGORY_INIT (dam_debug, "dam", 0, "dam");
 
286
 
 
287
  gobject_class->set_property = gst_dam_set_property;
 
288
  gobject_class->get_property = gst_dam_get_property;
 
289
 
 
290
  g_object_class_install_property (gobject_class, PROP_SEGMENT_MODE,
 
291
      g_param_spec_boolean ("segment-mode", "Segment-Based Dam",
 
292
          "Control and filter flow based on segments and seeks",
 
293
          DEFAULT_SEGMENT_MODE, G_PARAM_READWRITE));
 
294
 
 
295
  g_object_class_install_property (gobject_class, PROP_USE_COUNT,
 
296
      g_param_spec_boolean ("use-count", "Count-Based Filter",
 
297
          "Filter flow based on byte or frame count",
 
298
          DEFAULT_USE_COUNT, G_PARAM_READWRITE));
 
299
 
 
300
  g_object_class_install_property (gobject_class, PROP_PRECISION,
 
301
      g_param_spec_boolean ("precision", "Segment-Based Dam",
 
302
          "Precision filter, slicing (audio) buffers if needed",
 
303
          DEFAULT_PRECISION, G_PARAM_READWRITE));
 
304
 
 
305
  g_object_class_install_property (gobject_class, PROP_QUERY,
 
306
      g_param_spec_boolean ("handle-query", "Handle Query",
 
307
          "Respond to position query",
 
308
          DEFAULT_QUERY, G_PARAM_READWRITE));
 
309
 
 
310
  g_object_class_install_property (gobject_class, PROP_SECTION,
 
311
      g_param_spec_int ("section", "Section",
 
312
          "Current filtered section",
 
313
          -1, G_MAXINT, -1, G_PARAM_READABLE));
 
314
 
 
315
  g_object_class_install_property (gobject_class, PROP_BEGIN_COUNT,
 
316
      g_param_spec_long ("begin-count", "Begin Section",
 
317
          "Begin of section in frames",
 
318
          -1, G_MAXLONG, -1, G_PARAM_WRITABLE));
 
319
 
 
320
  g_object_class_install_property (gobject_class, PROP_END_COUNT,
 
321
      g_param_spec_long ("end-count", "End Section",
 
322
          "End of section in frames",
 
323
          -1, G_MAXLONG, -1, G_PARAM_WRITABLE));
 
324
 
 
325
  g_object_class_install_property (gobject_class, PROP_BEGIN_TIME,
 
326
      g_param_spec_uint64 ("begin-time", "Begin Section",
 
327
          "Begin of section in time",
 
328
          0, G_MAXUINT64, 0, G_PARAM_WRITABLE));
 
329
 
 
330
  g_object_class_install_property (gobject_class, PROP_END_TIME,
 
331
      g_param_spec_uint64 ("end-time", "End Section",
 
332
          "End of section in time",
 
333
          0, G_MAXUINT64, 0, G_PARAM_WRITABLE));
 
334
 
 
335
  g_object_class_install_property (gobject_class, PROP_SAVE,
 
336
      g_param_spec_boolean ("save-section", "Save Section",
 
337
          "Commit current section info for processing",
 
338
          TRUE, G_PARAM_WRITABLE));
 
339
 
 
340
  g_object_class_install_property (gobject_class, PROP_EOS,
 
341
      g_param_spec_boolean ("force-eos", "Force EOS",
 
342
          "Force End-Of-Stream",
 
343
          TRUE, G_PARAM_WRITABLE));
 
344
 
 
345
  g_object_class_install_property (gobject_class, PROP_SAMPLERATE,
 
346
      g_param_spec_int ("samplerate", "samplerate",
 
347
          "Samplerate discovered in stream and used for cutting and stamping",
 
348
          0, G_MAXINT, 0, G_PARAM_READABLE));
 
349
 
 
350
  g_object_class_install_property (gobject_class, PROP_SAMPLEWIDTH,
 
351
      g_param_spec_int ("samplewidth", "samplewidth",
 
352
          "Width of a sample as deduced from stream and used for cutting and stamping",
 
353
          0, G_MAXINT, 0, G_PARAM_READABLE));
 
354
 
 
355
  g_object_class_install_property (gobject_class, PROP_FRAMERATE,
 
356
      g_param_spec_string ("framerate", "framerate",
 
357
          "Framerate discovered in stream and used for cutting and stamping",
 
358
          "1/1", G_PARAM_READABLE));
 
359
 
 
360
  trans_class->start = GST_DEBUG_FUNCPTR (gst_dam_start);
 
361
  trans_class->stop = GST_DEBUG_FUNCPTR (gst_dam_stop);
 
362
  trans_class->event = GST_DEBUG_FUNCPTR (gst_dam_event);
 
363
}
 
364
 
 
365
static void
 
366
gst_dam_init (GstDam * dam, GstDamClass * g_class)
 
367
{
 
368
  GstBaseTransform *trans = GST_BASE_TRANSFORM (dam);
 
369
 
 
370
  gst_base_transform_set_passthrough (trans, TRUE);
 
371
 
 
372
  /* HACK to override chain function so we can drop buffers */
 
373
  if (trans->sinkpad)
 
374
    gst_pad_set_chain_function (trans->sinkpad,
 
375
        GST_DEBUG_FUNCPTR (gst_dam_chain));
 
376
 
 
377
  /* HACK need some caps info */
 
378
  if (trans->sinkpad) {
 
379
    dam->btrans_setcaps = GST_PAD_SETCAPSFUNC (trans->sinkpad);
 
380
    gst_pad_set_setcaps_function (trans->sinkpad,
 
381
        GST_DEBUG_FUNCPTR (gst_dam_setcaps));
 
382
  }
 
383
 
 
384
  /* may have to handle query */
 
385
  if (trans->srcpad)
 
386
    gst_pad_set_query_function (trans->srcpad,
 
387
        GST_DEBUG_FUNCPTR (gst_dam_src_query));
 
388
 
 
389
  dam->section = -1;
 
390
  dam->segment_mode = DEFAULT_SEGMENT_MODE;
 
391
  dam->use_count = DEFAULT_USE_COUNT;
 
392
  dam->precision = DEFAULT_PRECISION;
 
393
  dam->handle_query = DEFAULT_QUERY;
 
394
  /* no div by zero anywhere */
 
395
  dam->fps_denom = 1;
 
396
}
 
397
 
 
398
static GstMessage *
 
399
gst_dam_new_message (GstDam * dam, const gchar * msg)
 
400
{
 
401
  GstStructure *s;
 
402
 
 
403
  s = gst_structure_new ("dam", msg, G_TYPE_BOOLEAN, TRUE, NULL);
 
404
  return gst_message_new_element (GST_OBJECT (dam), s);
 
405
}
 
406
 
 
407
static void
 
408
gst_dam_calc_counts (GstDam * dam)
 
409
{
 
410
  GList *node;
 
411
 
 
412
  node = g_list_first (dam->sections);
 
413
  while (node) {
 
414
    SectionInfo *info = (SectionInfo *) node->data;
 
415
 
 
416
    node = g_list_next (node);
 
417
 
 
418
    if (dam->type == TYPE_AUDIO) {
 
419
      info->begin_count =
 
420
          gst_util_uint64_scale (info->begin_time, dam->rate, GST_SECOND)
 
421
          * dam->sample;
 
422
      info->end_count =
 
423
          gst_util_uint64_scale (info->end_time, dam->rate, GST_SECOND)
 
424
          * dam->sample;
 
425
    } else if (dam->type == TYPE_VIDEO) {
 
426
      info->begin_count =
 
427
          gst_util_uint64_scale (info->begin_time, dam->fps_num,
 
428
              GST_SECOND * dam->fps_denom);
 
429
      info->end_count =
 
430
          gst_util_uint64_scale (info->end_time, dam->fps_num,
 
431
              GST_SECOND * dam->fps_denom);
 
432
    }
 
433
 
 
434
    if (! GST_CLOCK_TIME_IS_VALID (info->end_time))
 
435
      info->end_count = G_MAXUINT64;
 
436
  }
 
437
}
 
438
 
 
439
static gboolean
 
440
gst_dam_setcaps (GstPad * pad, GstCaps * caps)
 
441
{
 
442
  GstDam *dam = GST_DAM (GST_PAD_PARENT (pad));
 
443
  GstStructure *structure;
 
444
  const gchar *mime;
 
445
  gint width, channels;
 
446
  gboolean ret = TRUE;
 
447
 
 
448
  structure = gst_caps_get_structure (caps, 0);
 
449
  mime = gst_structure_get_name (structure);
 
450
  if (g_strrstr (mime, "audio")) {
 
451
    dam->type = TYPE_AUDIO;
 
452
    if (!gst_structure_get_int (structure, "rate", &dam->rate))
 
453
      goto not_found;
 
454
    if (!gst_structure_get_int (structure, "width", &width))
 
455
      goto not_found;
 
456
    if (!gst_structure_get_int (structure, "channels", &channels))
 
457
      goto not_found;
 
458
    dam->sample = channels * width / 8;
 
459
    g_object_freeze_notify (G_OBJECT (dam));
 
460
    g_object_notify (G_OBJECT (dam), "samplerate");
 
461
    g_object_notify (G_OBJECT (dam), "samplewidth");
 
462
    g_object_thaw_notify (G_OBJECT (dam));
 
463
  } else if (g_strrstr (mime, "video")) {
 
464
    const GValue *fps;
 
465
 
 
466
    dam->type = TYPE_VIDEO;
 
467
    fps = gst_structure_get_value (structure, "framerate");
 
468
    if (fps) {
 
469
      g_return_val_if_fail (GST_VALUE_HOLDS_FRACTION (fps), FALSE);
 
470
      dam->fps_num = gst_value_get_fraction_numerator (fps);
 
471
      dam->fps_denom = gst_value_get_fraction_denominator (fps);
 
472
    }
 
473
    g_object_notify (G_OBJECT (dam), "framerate");
 
474
  } else { /* some other type, subtitle, etc */
 
475
    dam->type = TYPE_OTHER;
 
476
    /* force time based, non precision cutting */
 
477
    if (!dam->segment_mode) {
 
478
      dam->use_count = FALSE;
 
479
      dam->precision = FALSE;
 
480
    }
 
481
  }
 
482
  gst_dam_calc_counts (dam);
 
483
 
 
484
exit:
 
485
  {
 
486
   if (ret && dam->btrans_setcaps)
 
487
      ret = dam->btrans_setcaps (pad, caps);
 
488
    return ret;
 
489
  }
 
490
 
 
491
not_found:
 
492
  {
 
493
    if ((dam->segment_mode || !dam->use_count) && !dam->precision)
 
494
      ret = TRUE;
 
495
    else
 
496
      ret = FALSE;
 
497
    goto exit;
 
498
  }
 
499
}
 
500
 
 
501
static gboolean
 
502
gst_dam_src_query (GstPad * pad, GstQuery * query)
 
503
{
 
504
  GstDam *dam = GST_DAM (GST_PAD_PARENT (pad));
 
505
  GstBaseTransform *trans = GST_BASE_TRANSFORM (dam);
 
506
  GstPad *peer;
 
507
 
 
508
  switch (GST_QUERY_TYPE (query)) {
 
509
    case GST_QUERY_POSITION:
 
510
      if (dam->handle_query) {
 
511
        gst_query_set_position (query, GST_FORMAT_TIME, dam->last_stamp);
 
512
        return TRUE;
 
513
      }
 
514
      break;
 
515
    default:
 
516
      break;
 
517
  }
 
518
 
 
519
  if (trans->sinkpad) {
 
520
    peer = GST_PAD_PEER (trans->sinkpad);
 
521
    if (peer)
 
522
      return gst_pad_query (peer, query);
 
523
  }
 
524
 
 
525
  return FALSE;
 
526
}
 
527
 
 
528
static gboolean
 
529
gst_dam_event (GstBaseTransform * trans, GstEvent * event)
 
530
{
 
531
  GstDam *dam;
 
532
  gboolean ret = TRUE;
 
533
 
 
534
  dam = GST_DAM (trans);
 
535
 
 
536
  GST_BASE_TRANSFORM_CLASS (parent_class)->event (trans, event);
 
537
 
 
538
  if (!dam->segment_mode && (GST_EVENT_TYPE (event) == GST_EVENT_NEWSEGMENT)) {
 
539
    /* eat up segments */
 
540
    ret = FALSE;
 
541
  }
 
542
 
 
543
  return ret;
 
544
}
 
545
 
 
546
 
 
547
/* performs precise cutting of an audio buffer w.r.t. begin and end of segment
 
548
   returns -1 if buffer is before the desired segment,
 
549
            0 if partially (or completely) in
 
550
            1 if buffer is beyond
 
551
  in case of 0, there may be some leftover if it was only partially
 
552
*/
 
553
static inline gint
 
554
gst_dam_cut (GstDam * dam, GstBuffer ** buf, GstBuffer ** leftover)
 
555
{
 
556
  gint rate, sample;
 
557
  GstBuffer *outbuf;
 
558
  gint start_offset, end_offset;
 
559
  guint size;
 
560
  guint64 begin_count, end_count;
 
561
  gint result = 0;
 
562
 
 
563
  /* more convenient writing */
 
564
  sample = dam->sample;
 
565
  rate = dam->rate;
 
566
  size = GST_BUFFER_SIZE (*buf);
 
567
 
 
568
  begin_count = dam->current->begin_count;
 
569
  end_count = dam->current->end_count;
 
570
 
 
571
  /* clean case; all passes */
 
572
  if (dam->audio_count >= begin_count
 
573
      && dam->audio_count + size <= end_count) {
 
574
    GST_DEBUG_OBJECT (dam, "audio buffer within segment");
 
575
    result = start_offset = 0;
 
576
    end_offset = size;
 
577
    goto stamp;
 
578
  }
 
579
 
 
580
  /* buffer not yet in segment */
 
581
  if (dam->audio_count + size <= begin_count) {
 
582
    GST_DEBUG_OBJECT (dam, "audio buffer before segment");
 
583
    result = -1;
 
584
    goto exit;
 
585
  }
 
586
 
 
587
  /* buffer beyond segment */
 
588
  if (dam->audio_count >= end_count) {
 
589
    GST_DEBUG_OBJECT (dam, "audio buffer after segment");
 
590
    result = 1;
 
591
    goto exit;
 
592
  }
 
593
 
 
594
  /* buffer partially in segment */
 
595
  /* find offsets */
 
596
  if (begin_count >= dam->audio_count)
 
597
    start_offset = begin_count - dam->audio_count;
 
598
  else
 
599
    start_offset = 0;
 
600
  GST_DEBUG ("%d", start_offset);
 
601
  end_offset = (MIN (dam->audio_count + size, end_count) - dam->audio_count);
 
602
  GST_DEBUG ("%d", end_offset);
 
603
  /* play safe */
 
604
  start_offset = MIN (start_offset, size);
 
605
  GST_DEBUG ("%d", start_offset);
 
606
  end_offset = MIN (MAX (end_offset, start_offset), size);
 
607
  GST_DEBUG ("%d", end_offset);
 
608
 
 
609
  if (size - end_offset > 0) {
 
610
    /* have some leftover */
 
611
    GST_DEBUG_OBJECT (dam, "creating leftover audio buffer, %d-%d",
 
612
        end_offset, size);
 
613
    *leftover = gst_buffer_create_sub (*buf, end_offset, size - end_offset);
 
614
    *leftover = gst_buffer_make_metadata_writable (*leftover);
 
615
    GST_BUFFER_TIMESTAMP (*leftover) =
 
616
        gst_util_uint64_scale ((dam->audio_count + end_offset) / sample,
 
617
            GST_SECOND, rate);
 
618
    GST_BUFFER_DURATION (*leftover) =
 
619
        gst_util_uint64_scale ((size - end_offset) / sample,
 
620
            GST_SECOND, rate);
 
621
  }
 
622
  else
 
623
    *leftover = NULL;
 
624
 
 
625
  GST_DEBUG_OBJECT (dam, "creating sub audio buffer, %d-%d",
 
626
      start_offset, end_offset);
 
627
  outbuf = gst_buffer_create_sub (*buf, start_offset,
 
628
      end_offset - start_offset);
 
629
  gst_buffer_unref (*buf);
 
630
  *buf = outbuf;
 
631
 
 
632
stamp:
 
633
  *buf = gst_buffer_make_metadata_writable (*buf);
 
634
  gst_buffer_set_caps (*buf, GST_PAD_CAPS (GST_BASE_TRANSFORM (dam)->srcpad));
 
635
  GST_BUFFER_TIMESTAMP (*buf) =
 
636
    gst_util_uint64_scale ((dam->audio_count + start_offset) / sample,
 
637
        GST_SECOND, rate);
 
638
  GST_BUFFER_DURATION (*buf) =
 
639
      gst_util_uint64_scale ((end_offset - start_offset) / sample,
 
640
        GST_SECOND, rate);
 
641
exit:
 
642
  dam->audio_count += size;
 
643
  return result;
 
644
}
 
645
 
 
646
/* returns -1 if buffer is before the desired segment,
 
647
            0 if partially (or completely) in
 
648
            1 if buffer is beyond
 
649
*/
 
650
static inline gboolean
 
651
gst_dam_segment (GstDam * dam, GstSegment *segment, GstBuffer * buf,
 
652
    GstBuffer ** leftover)
 
653
{
 
654
  GstClockTime start_time, stop_time = GST_CLOCK_TIME_NONE, duration;
 
655
  gboolean drop = FALSE;
 
656
 
 
657
  start_time = GST_BUFFER_TIMESTAMP (buf);
 
658
  duration = GST_BUFFER_DURATION (buf);
 
659
 
 
660
  /* if invalid start_time or not time based segment - we let it pass */
 
661
  if (GST_CLOCK_TIME_IS_VALID (start_time) && segment != NULL
 
662
      && segment->format == GST_FORMAT_TIME) {
 
663
    if (GST_CLOCK_TIME_IS_VALID (duration))
 
664
      stop_time = start_time + duration;
 
665
    else
 
666
      stop_time = start_time;
 
667
    GST_DEBUG_OBJECT (dam, "Checking with segment start %" GST_TIME_FORMAT ""
 
668
        ", stop %" GST_TIME_FORMAT ", duration %" GST_TIME_FORMAT,
 
669
        GST_TIME_ARGS (segment->start), GST_TIME_ARGS (segment->stop),
 
670
        GST_TIME_ARGS (segment->duration));
 
671
    if (!gst_segment_clip (segment, GST_FORMAT_TIME, start_time, stop_time, NULL, NULL)) {
 
672
      drop = TRUE;
 
673
      GST_DEBUG_OBJECT (dam, "Dropping buffer by segment");
 
674
    }
 
675
    /* update the segment with last seen */
 
676
    gst_segment_set_last_stop (segment, GST_FORMAT_TIME, stop_time);
 
677
  }
 
678
 
 
679
  if (drop) { /* before or after ? */
 
680
    if (start_time > (GstClockTime) dam->segment.stop)
 
681
      return 1;
 
682
    else
 
683
      return -1;
 
684
  } else {
 
685
    if (dam->type == TYPE_AUDIO && dam->precision) {
 
686
      /* we believe the buffer is right about this */
 
687
      dam->audio_count
 
688
          = gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), dam->rate, GST_SECOND)
 
689
          * dam->sample;
 
690
      /* should be agreement on being in segment */
 
691
      if (gst_dam_cut (dam, &buf, leftover))
 
692
        g_warning ("No precision cut for segment-passed buffer");
 
693
    }
 
694
    return 0;
 
695
  }
 
696
}
 
697
 
 
698
static GstFlowReturn
 
699
gst_dam_chain (GstPad * pad, GstBuffer * buf)
 
700
{
 
701
  GstDam *dam;
 
702
  GList *entry;
 
703
  GstFlowReturn result = GST_FLOW_OK;
 
704
  GstBaseTransform *trans = GST_BASE_TRANSFORM (GST_PAD_PARENT (pad));
 
705
  dam = GST_DAM (trans);
 
706
  GstBuffer *leftover = NULL;
 
707
 
 
708
  GST_DEBUG_OBJECT (dam,
 
709
      "Received buffer of time %" GST_TIME_FORMAT ", duration %" GST_TIME_FORMAT ", size %d",
 
710
      GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)), GST_TIME_ARGS (GST_BUFFER_DURATION (buf)),
 
711
      GST_BUFFER_SIZE (buf));
 
712
 
 
713
  /* if we are eos */
 
714
  if (dam->dropping || GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_GAP)) {
 
715
    goto drop;
 
716
  }
 
717
 
 
718
  if (dam->eos) {
 
719
    gst_pad_push_event (trans->srcpad, gst_event_new_eos ());
 
720
    result = GST_FLOW_OK;
 
721
    dam->dropping = TRUE;
 
722
    goto drop;
 
723
  }
 
724
 
 
725
  if (dam->segment_mode) {
 
726
    if (trans->have_newsegment) {
 
727
      /* need this for precision cutting */
 
728
      dam->current->begin_count =
 
729
          gst_util_uint64_scale (trans->segment.start, dam->rate, GST_SECOND)
 
730
          * dam->sample;
 
731
      if (GST_CLOCK_TIME_IS_VALID (trans->segment.stop))
 
732
        dam->current->end_count =
 
733
            gst_util_uint64_scale (trans->segment.stop, dam->rate, GST_SECOND)
 
734
            * dam->sample;
 
735
      else
 
736
        dam->current->end_count = G_MAXUINT64;
 
737
      trans->have_newsegment = FALSE;
 
738
    }
 
739
    if (gst_dam_segment (dam, &trans->segment, buf, &leftover))
 
740
      goto drop;
 
741
    else {
 
742
      /* never mind any leftover, it will come again if needed */
 
743
      if (leftover)
 
744
        gst_buffer_unref (leftover);
 
745
      leftover = NULL;
 
746
      goto push;
 
747
    }
 
748
  }
 
749
 
 
750
  /* non-segment based cutting; some ugly stuff here ... */
 
751
 
 
752
  /* make mark that we have started seeing data */
 
753
  if (dam->section < 0) {
 
754
    dam->section = 0;
 
755
    g_object_notify (G_OBJECT (dam), "section");
 
756
    entry = g_list_first (dam->sections);
 
757
    /* there !must! be sections in this state */
 
758
    g_return_val_if_fail (entry != NULL, GST_FLOW_ERROR);
 
759
    dam->current = entry->data;
 
760
    dam->newsegment = gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_TIME,
 
761
        dam->current->begin_time, dam->current->end_time, dam->current->begin_time);
 
762
    gst_segment_set_newsegment (&dam->segment, FALSE, 1.0,
 
763
        GST_FORMAT_TIME, dam->current->begin_time, dam->current->end_time,
 
764
        dam->current->begin_time);
 
765
  }
 
766
 
 
767
  /* do it anyway, only needed if video */
 
768
  dam->cut_position++;
 
769
 
 
770
check:
 
771
  if (dam->type == TYPE_VIDEO && dam->use_count) {
 
772
    /* timestamp needed either way */
 
773
    if (G_LIKELY (dam->fps_num > 0)) {
 
774
      gst_buffer_make_metadata_writable (buf);
 
775
      GST_BUFFER_TIMESTAMP (buf) =
 
776
          gst_util_uint64_scale (dam->cut_position,
 
777
              dam->fps_denom * GST_SECOND, dam->fps_num);
 
778
      GST_BUFFER_DURATION (buf) =
 
779
          gst_util_uint64_scale (GST_SECOND, dam->fps_denom, dam->fps_num);
 
780
    }
 
781
    /* decide to drop or push */
 
782
    if (dam->cut_position < dam->current->begin_count)
 
783
      goto drop;
 
784
    if (dam->cut_position < dam->current->end_count)
 
785
      goto push;
 
786
  } else {
 
787
    gint res;
 
788
 
 
789
    if (dam->use_count)
 
790
      /* so not video */
 
791
      res = gst_dam_cut (dam, &buf, &leftover);
 
792
    else
 
793
      res = gst_dam_segment (dam, &dam->segment, buf, &leftover);
 
794
 
 
795
    if (!res)
 
796
      goto push;
 
797
    if (res < 0)
 
798
      goto drop;
 
799
  }
 
800
 
 
801
  /* ended section, need next one */
 
802
  dam->section++;
 
803
  g_object_notify (G_OBJECT (dam), "section");
 
804
  dam->sent_segment = FALSE;
 
805
  entry = g_list_nth (dam->sections, dam->section);
 
806
  if (entry) {
 
807
    dam->current = entry->data;
 
808
    /* prepare segment stuff for new section */
 
809
    if (dam->newsegment)
 
810
      gst_event_unref (dam->newsegment);
 
811
    dam->newsegment = gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_TIME,
 
812
        dam->current->begin_time, dam->current->end_time, dam->current->begin_time);
 
813
    gst_segment_set_newsegment (&dam->segment, FALSE, 1.0,
 
814
        GST_FORMAT_TIME, dam->current->begin_time, dam->current->end_time,
 
815
        dam->current->begin_time);
 
816
    /* if sections are close together, buffer may already be in next section */
 
817
    goto check;
 
818
  }
 
819
  else {
 
820
    dam->current = NULL;
 
821
    dam->dropping = TRUE;
 
822
    gst_pad_push_event (trans->srcpad, gst_event_new_eos ());
 
823
    if (leftover)
 
824
      gst_buffer_unref (leftover);
 
825
    goto drop;
 
826
  }
 
827
 
 
828
  /* common code for either mode */
 
829
push:
 
830
  if (!dam->segment_mode && !dam->sent_segment) {
 
831
    /* need to generate a segment event for non-segment cutting */
 
832
    GST_DEBUG_OBJECT (dam, "sending newsegment");
 
833
    gst_pad_push_event (trans->srcpad, dam->newsegment);
 
834
    /* is no longer ours */
 
835
    dam->newsegment = NULL;
 
836
    dam->sent_segment = TRUE;
 
837
  }
 
838
 
 
839
  dam->last_stamp = GST_BUFFER_TIMESTAMP (buf);
 
840
  result = gst_pad_push (trans->srcpad, buf);
 
841
  if (result == GST_FLOW_OK && leftover) {
 
842
    /* have another go with the remainder */
 
843
    buf = leftover;
 
844
    leftover = NULL;
 
845
    goto check;
 
846
  }
 
847
  else
 
848
    return result;
 
849
 
 
850
drop:
 
851
  {
 
852
    GST_DEBUG_OBJECT (dam, "dropping buffer");
 
853
    dam->last_stamp = GST_BUFFER_TIMESTAMP (buf);
 
854
    gst_buffer_unref (buf);
 
855
    return result;
 
856
  }
 
857
}
 
858
 
 
859
static gboolean
 
860
gst_dam_start (GstBaseTransform * trans)
 
861
{
 
862
  GstDam *dam = GST_DAM (trans);
 
863
 
 
864
  dam->dropping = FALSE;
 
865
  dam->eos = FALSE;
 
866
  dam->section = -1;
 
867
  dam->sections = NULL;
 
868
  dam->cut_position = -1;
 
869
  dam->audio_count = 0;
 
870
  dam->sample = dam->rate = 1;
 
871
  dam->fps_num = dam->fps_denom = -1;
 
872
  gst_segment_init (&dam->segment, GST_FORMAT_TIME);
 
873
  dam->newsegment = NULL;
 
874
  dam->sent_segment = FALSE;
 
875
  dam->last_stamp = 0;
 
876
  dam->memchunk = g_mem_chunk_create (SectionInfo, 10, G_ALLOC_ONLY);
 
877
  dam->current = g_chunk_new (SectionInfo, dam->memchunk);
 
878
 
 
879
  /* announce existence */
 
880
  gst_element_post_message (GST_ELEMENT (trans),
 
881
      gst_dam_new_message (dam, "announce"));
 
882
 
 
883
  return TRUE;
 
884
}
 
885
 
 
886
static gboolean
 
887
gst_dam_stop (GstBaseTransform * trans)
 
888
{
 
889
  GstDam *dam = GST_DAM (trans);
 
890
 
 
891
  dam->section = -1;
 
892
  if (dam->sections)
 
893
    g_list_free (dam->sections);
 
894
  g_mem_chunk_destroy (dam->memchunk);
 
895
  dam->memchunk = NULL;
 
896
  dam->sections = NULL;
 
897
  dam->current = NULL;
 
898
  if (dam->newsegment)
 
899
    gst_event_unref (dam->newsegment);
 
900
  dam->newsegment = NULL;
 
901
 
 
902
  return TRUE;
 
903
}
 
904
 
 
905
static void
 
906
gst_dam_set_property (GObject * object, guint prop_id,
 
907
    const GValue * value, GParamSpec * pspec)
 
908
{
 
909
  GstDam *dam;
 
910
 
 
911
  g_return_if_fail (GST_IS_DAM (object));
 
912
  dam = GST_DAM (object);
 
913
 
 
914
  if (dam->section >= 0 && prop_id != PROP_EOS) {
 
915
    g_critical ("Cannot set property on %s once streaming has begun.",
 
916
        GST_OBJECT_NAME (object));
 
917
    return;
 
918
  }
 
919
 
 
920
  if (!dam->current && prop_id != PROP_EOS && prop_id != PROP_QUERY
 
921
     && prop_id != PROP_USE_COUNT && prop_id != PROP_PRECISION) {
 
922
    g_critical ("Cannot set property on %s before streaming has started.",
 
923
        GST_OBJECT_NAME (object));
 
924
    return;
 
925
  }
 
926
 
 
927
  switch (prop_id) {
 
928
    case PROP_SEGMENT_MODE:
 
929
      dam->segment_mode = g_value_get_boolean (value);
 
930
      break;
 
931
    case PROP_USE_COUNT:
 
932
      dam->use_count = g_value_get_boolean (value);
 
933
      break;
 
934
    case PROP_PRECISION:
 
935
      dam->precision = g_value_get_boolean (value);
 
936
      break;
 
937
    case PROP_QUERY:
 
938
      dam->handle_query = g_value_get_boolean (value);
 
939
      break;
 
940
    case PROP_SECTION:
 
941
      dam->section = g_value_get_int (value);
 
942
      break;
 
943
    case PROP_BEGIN_COUNT:
 
944
      dam->current->begin_count = g_value_get_long (value);
 
945
      break;
 
946
    case PROP_END_COUNT:
 
947
      dam->current->end_count = g_value_get_long (value);
 
948
      break;
 
949
    case PROP_BEGIN_TIME:
 
950
      dam->current->begin_time = g_value_get_uint64 (value);
 
951
      break;
 
952
    case PROP_END_TIME:
 
953
      dam->current->end_time = g_value_get_uint64 (value);
 
954
      break;
 
955
    case PROP_SAVE:
 
956
      dam->sections = g_list_append (dam->sections, dam->current);
 
957
      dam->current = g_chunk_new (SectionInfo, dam->memchunk);
 
958
      break;
 
959
    case PROP_EOS:
 
960
      dam->eos = g_value_get_boolean (value);
 
961
      break;
 
962
    default:
 
963
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 
964
      break;
 
965
  }
 
966
}
 
967
 
 
968
static void
 
969
gst_dam_get_property (GObject * object, guint prop_id, GValue * value,
 
970
    GParamSpec * pspec)
 
971
{
 
972
  GstDam *dam;
 
973
 
 
974
  g_return_if_fail (GST_IS_DAM (object));
 
975
  dam = GST_DAM (object);
 
976
 
 
977
  switch (prop_id) {
 
978
    case PROP_SEGMENT_MODE:
 
979
      g_value_set_boolean (value, dam->segment_mode);
 
980
      break;
 
981
    case PROP_USE_COUNT:
 
982
      g_value_set_boolean (value, dam->use_count);
 
983
      break;
 
984
    case PROP_PRECISION:
 
985
      g_value_set_boolean (value, dam->precision);
 
986
      break;
 
987
    case PROP_QUERY:
 
988
      g_value_set_boolean (value, dam->handle_query);
 
989
      break;
 
990
    case PROP_SECTION:
 
991
      g_value_set_int (value, dam->section);
 
992
      break;
 
993
    case PROP_SAMPLERATE:
 
994
      g_value_set_int (value, dam->rate);
 
995
      break;
 
996
    case PROP_SAMPLEWIDTH:
 
997
      g_value_set_int (value, dam->sample);
 
998
      break;
 
999
    case PROP_FRAMERATE:
 
1000
    {
 
1001
      GValue fvalue = { 0, };
 
1002
 
 
1003
      g_value_init (&fvalue, GST_TYPE_FRACTION);
 
1004
      gst_value_set_fraction (&fvalue, dam->fps_num, dam->fps_denom);
 
1005
      g_value_transform (&fvalue, value);
 
1006
      break;
 
1007
    }
 
1008
    default:
 
1009
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 
1010
      break;
 
1011
  }
 
1012
}