2
* Copyright (C) 2006 Mark Nauwelaerts <mnauw@users.sourceforge.net>
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.
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.
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.
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:
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.
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.
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).
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.
64
* <title>Usage</title>
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.
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).
82
* The sequence of events/operation is roughly as follows.
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
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.
112
#include "plugin-entrans.h"
114
#define GST_TYPE_DAM \
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))
126
typedef struct _SectionInfo SectionInfo;
127
typedef struct _GstDam GstDam;
128
typedef struct _GstDamClass GstDamClass;
132
guint64 begin_count, end_count;
133
GstClockTime begin_time, end_time;
138
GstBaseTransform basetransform;
140
/* what mode we operate in */
141
gboolean segment_mode;
142
/* if cutting, use count or time */
144
/* if audio and time-based, do precision slicing */
146
/* what we are actually doing in the drop area */
148
/* count based dropping/cutting */
149
/* eos has been requested for clean-up of stream */
151
/* do we respond to position query or pass along */
152
gboolean handle_query;
153
/* section management */
157
SectionInfo *current;
160
/* (frame) position in stream */
162
/* audio position = byte position in stream */
164
/* stream audio info */
166
/* stream video info */
167
gint fps_num, fps_denom;
168
/* segment info for current section */
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;
177
/* the parent's setcaps function */
178
GstPadSetCapsFunction btrans_setcaps;
191
GstBaseTransformClass parent_class;
194
GST_DEBUG_CATEGORY_STATIC (dam_debug);
195
#define GST_CAT_DEFAULT dam_debug
197
/* GstDam signals and args */
224
#define DEFAULT_SEGMENT_MODE TRUE
225
#define DEFAULT_QUERY FALSE
226
#define DEFAULT_USE_COUNT TRUE
227
#define DEFAULT_PRECISION FALSE
229
static GstElementDetails dam_details =
230
GST_ELEMENT_DETAILS ("Dam",
232
"Block and/or filter stream",
233
"Mark Nauwelaerts <mnauw@users.sourceforge.net>");
235
static GstStaticPadTemplate gst_dam_src_template =
236
GST_STATIC_PAD_TEMPLATE (GST_BASE_TRANSFORM_SRC_NAME,
242
static GstStaticPadTemplate gst_dam_sink_template =
243
GST_STATIC_PAD_TEMPLATE (GST_BASE_TRANSFORM_SINK_NAME,
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);
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);
261
GST_BOILERPLATE (GstDam, gst_dam, GstBaseTransform, GST_TYPE_BASE_TRANSFORM);
264
gst_dam_base_init (gpointer g_class)
266
GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
268
gst_element_class_set_details (element_class, &dam_details);
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));
277
gst_dam_class_init (GstDamClass * g_class)
279
GObjectClass *gobject_class;
280
GstBaseTransformClass *trans_class;
282
gobject_class = G_OBJECT_CLASS (g_class);
283
trans_class = GST_BASE_TRANSFORM_CLASS (g_class);
285
GST_DEBUG_CATEGORY_INIT (dam_debug, "dam", 0, "dam");
287
gobject_class->set_property = gst_dam_set_property;
288
gobject_class->get_property = gst_dam_get_property;
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));
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));
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));
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));
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));
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));
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));
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));
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));
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));
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));
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));
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));
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));
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);
366
gst_dam_init (GstDam * dam, GstDamClass * g_class)
368
GstBaseTransform *trans = GST_BASE_TRANSFORM (dam);
370
gst_base_transform_set_passthrough (trans, TRUE);
372
/* HACK to override chain function so we can drop buffers */
374
gst_pad_set_chain_function (trans->sinkpad,
375
GST_DEBUG_FUNCPTR (gst_dam_chain));
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));
384
/* may have to handle query */
386
gst_pad_set_query_function (trans->srcpad,
387
GST_DEBUG_FUNCPTR (gst_dam_src_query));
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 */
399
gst_dam_new_message (GstDam * dam, const gchar * msg)
403
s = gst_structure_new ("dam", msg, G_TYPE_BOOLEAN, TRUE, NULL);
404
return gst_message_new_element (GST_OBJECT (dam), s);
408
gst_dam_calc_counts (GstDam * dam)
412
node = g_list_first (dam->sections);
414
SectionInfo *info = (SectionInfo *) node->data;
416
node = g_list_next (node);
418
if (dam->type == TYPE_AUDIO) {
420
gst_util_uint64_scale (info->begin_time, dam->rate, GST_SECOND)
423
gst_util_uint64_scale (info->end_time, dam->rate, GST_SECOND)
425
} else if (dam->type == TYPE_VIDEO) {
427
gst_util_uint64_scale (info->begin_time, dam->fps_num,
428
GST_SECOND * dam->fps_denom);
430
gst_util_uint64_scale (info->end_time, dam->fps_num,
431
GST_SECOND * dam->fps_denom);
434
if (! GST_CLOCK_TIME_IS_VALID (info->end_time))
435
info->end_count = G_MAXUINT64;
440
gst_dam_setcaps (GstPad * pad, GstCaps * caps)
442
GstDam *dam = GST_DAM (GST_PAD_PARENT (pad));
443
GstStructure *structure;
445
gint width, channels;
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))
454
if (!gst_structure_get_int (structure, "width", &width))
456
if (!gst_structure_get_int (structure, "channels", &channels))
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")) {
466
dam->type = TYPE_VIDEO;
467
fps = gst_structure_get_value (structure, "framerate");
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);
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;
482
gst_dam_calc_counts (dam);
486
if (ret && dam->btrans_setcaps)
487
ret = dam->btrans_setcaps (pad, caps);
493
if ((dam->segment_mode || !dam->use_count) && !dam->precision)
502
gst_dam_src_query (GstPad * pad, GstQuery * query)
504
GstDam *dam = GST_DAM (GST_PAD_PARENT (pad));
505
GstBaseTransform *trans = GST_BASE_TRANSFORM (dam);
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);
519
if (trans->sinkpad) {
520
peer = GST_PAD_PEER (trans->sinkpad);
522
return gst_pad_query (peer, query);
529
gst_dam_event (GstBaseTransform * trans, GstEvent * event)
534
dam = GST_DAM (trans);
536
GST_BASE_TRANSFORM_CLASS (parent_class)->event (trans, event);
538
if (!dam->segment_mode && (GST_EVENT_TYPE (event) == GST_EVENT_NEWSEGMENT)) {
539
/* eat up segments */
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
554
gst_dam_cut (GstDam * dam, GstBuffer ** buf, GstBuffer ** leftover)
558
gint start_offset, end_offset;
560
guint64 begin_count, end_count;
563
/* more convenient writing */
564
sample = dam->sample;
566
size = GST_BUFFER_SIZE (*buf);
568
begin_count = dam->current->begin_count;
569
end_count = dam->current->end_count;
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;
580
/* buffer not yet in segment */
581
if (dam->audio_count + size <= begin_count) {
582
GST_DEBUG_OBJECT (dam, "audio buffer before segment");
587
/* buffer beyond segment */
588
if (dam->audio_count >= end_count) {
589
GST_DEBUG_OBJECT (dam, "audio buffer after segment");
594
/* buffer partially in segment */
596
if (begin_count >= dam->audio_count)
597
start_offset = begin_count - dam->audio_count;
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);
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);
609
if (size - end_offset > 0) {
610
/* have some leftover */
611
GST_DEBUG_OBJECT (dam, "creating leftover audio buffer, %d-%d",
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,
618
GST_BUFFER_DURATION (*leftover) =
619
gst_util_uint64_scale ((size - end_offset) / sample,
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);
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,
638
GST_BUFFER_DURATION (*buf) =
639
gst_util_uint64_scale ((end_offset - start_offset) / sample,
642
dam->audio_count += size;
646
/* returns -1 if buffer is before the desired segment,
647
0 if partially (or completely) in
648
1 if buffer is beyond
650
static inline gboolean
651
gst_dam_segment (GstDam * dam, GstSegment *segment, GstBuffer * buf,
652
GstBuffer ** leftover)
654
GstClockTime start_time, stop_time = GST_CLOCK_TIME_NONE, duration;
655
gboolean drop = FALSE;
657
start_time = GST_BUFFER_TIMESTAMP (buf);
658
duration = GST_BUFFER_DURATION (buf);
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;
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)) {
673
GST_DEBUG_OBJECT (dam, "Dropping buffer by segment");
675
/* update the segment with last seen */
676
gst_segment_set_last_stop (segment, GST_FORMAT_TIME, stop_time);
679
if (drop) { /* before or after ? */
680
if (start_time > (GstClockTime) dam->segment.stop)
685
if (dam->type == TYPE_AUDIO && dam->precision) {
686
/* we believe the buffer is right about this */
688
= gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), dam->rate, GST_SECOND)
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");
699
gst_dam_chain (GstPad * pad, GstBuffer * buf)
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;
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));
714
if (dam->dropping || GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_GAP)) {
719
gst_pad_push_event (trans->srcpad, gst_event_new_eos ());
720
result = GST_FLOW_OK;
721
dam->dropping = TRUE;
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)
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)
736
dam->current->end_count = G_MAXUINT64;
737
trans->have_newsegment = FALSE;
739
if (gst_dam_segment (dam, &trans->segment, buf, &leftover))
742
/* never mind any leftover, it will come again if needed */
744
gst_buffer_unref (leftover);
750
/* non-segment based cutting; some ugly stuff here ... */
752
/* make mark that we have started seeing data */
753
if (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);
767
/* do it anyway, only needed if video */
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);
781
/* decide to drop or push */
782
if (dam->cut_position < dam->current->begin_count)
784
if (dam->cut_position < dam->current->end_count)
791
res = gst_dam_cut (dam, &buf, &leftover);
793
res = gst_dam_segment (dam, &dam->segment, buf, &leftover);
801
/* ended section, need next one */
803
g_object_notify (G_OBJECT (dam), "section");
804
dam->sent_segment = FALSE;
805
entry = g_list_nth (dam->sections, dam->section);
807
dam->current = entry->data;
808
/* prepare segment stuff for new section */
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 */
821
dam->dropping = TRUE;
822
gst_pad_push_event (trans->srcpad, gst_event_new_eos ());
824
gst_buffer_unref (leftover);
828
/* common code for either mode */
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;
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 */
852
GST_DEBUG_OBJECT (dam, "dropping buffer");
853
dam->last_stamp = GST_BUFFER_TIMESTAMP (buf);
854
gst_buffer_unref (buf);
860
gst_dam_start (GstBaseTransform * trans)
862
GstDam *dam = GST_DAM (trans);
864
dam->dropping = FALSE;
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;
876
dam->memchunk = g_mem_chunk_create (SectionInfo, 10, G_ALLOC_ONLY);
877
dam->current = g_chunk_new (SectionInfo, dam->memchunk);
879
/* announce existence */
880
gst_element_post_message (GST_ELEMENT (trans),
881
gst_dam_new_message (dam, "announce"));
887
gst_dam_stop (GstBaseTransform * trans)
889
GstDam *dam = GST_DAM (trans);
893
g_list_free (dam->sections);
894
g_mem_chunk_destroy (dam->memchunk);
895
dam->memchunk = NULL;
896
dam->sections = NULL;
899
gst_event_unref (dam->newsegment);
900
dam->newsegment = NULL;
906
gst_dam_set_property (GObject * object, guint prop_id,
907
const GValue * value, GParamSpec * pspec)
911
g_return_if_fail (GST_IS_DAM (object));
912
dam = GST_DAM (object);
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));
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));
928
case PROP_SEGMENT_MODE:
929
dam->segment_mode = g_value_get_boolean (value);
932
dam->use_count = g_value_get_boolean (value);
935
dam->precision = g_value_get_boolean (value);
938
dam->handle_query = g_value_get_boolean (value);
941
dam->section = g_value_get_int (value);
943
case PROP_BEGIN_COUNT:
944
dam->current->begin_count = g_value_get_long (value);
947
dam->current->end_count = g_value_get_long (value);
949
case PROP_BEGIN_TIME:
950
dam->current->begin_time = g_value_get_uint64 (value);
953
dam->current->end_time = g_value_get_uint64 (value);
956
dam->sections = g_list_append (dam->sections, dam->current);
957
dam->current = g_chunk_new (SectionInfo, dam->memchunk);
960
dam->eos = g_value_get_boolean (value);
963
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
969
gst_dam_get_property (GObject * object, guint prop_id, GValue * value,
974
g_return_if_fail (GST_IS_DAM (object));
975
dam = GST_DAM (object);
978
case PROP_SEGMENT_MODE:
979
g_value_set_boolean (value, dam->segment_mode);
982
g_value_set_boolean (value, dam->use_count);
985
g_value_set_boolean (value, dam->precision);
988
g_value_set_boolean (value, dam->handle_query);
991
g_value_set_int (value, dam->section);
993
case PROP_SAMPLERATE:
994
g_value_set_int (value, dam->rate);
996
case PROP_SAMPLEWIDTH:
997
g_value_set_int (value, dam->sample);
1001
GValue fvalue = { 0, };
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);
1009
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);