4 * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved.
6 * Contact: JongHyuk Choi <jhchoi.choi@samsung.com>
8 * This library is free software; you can redistribute it and/or modify it under
9 * the terms of the GNU Lesser General Public License as published by the
10 * Free Software Foundation; either version 2.1 of the License, or (at your option)
13 * This library is distributed in the hope that it will be useful, but WITHOUT ANY
14 * WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
16 * License for more details.
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this library; if not, write to the Free Software Foundation, Inc., 51
20 * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
29 #include "gstaudiotp.h"
31 /* Plugin Detaills for gstreamer */
32 static const GstElementDetails gst_audiotp_plugin_details = GST_ELEMENT_DETAILS (
33 "Audio timestamp reversal plugin",
35 "Reverses audio timestamps for reverse playback",
36 "Samsung Electronics <www.samsung.com>"
39 /*** GSTREAMER PROTOTYPES *****************************************************/
43 "audio/x-raw-float, " \
44 "rate = (int) [ 1, MAX ], " \
45 "channels = (int) [ 1, MAX ], " \
46 "endianness = (int) { LITTLE_ENDIAN, BIG_ENDIAN }, " \
48 "audio/x-raw-float, " \
49 "rate = (int) [ 1, MAX ], " \
50 "channels = (int) [ 1, MAX ], " \
51 "endianness = (int) { LITTLE_ENDIAN, BIG_ENDIAN }, " \
54 "rate = (int) [ 1, MAX ], " \
55 "channels = (int) [ 1, MAX ], " \
56 "endianness = (int) { LITTLE_ENDIAN, BIG_ENDIAN }, " \
57 "width = (int) 32, " \
58 "depth = (int) [ 1, 32 ], " \
59 "signed = (boolean) { true, false }; " \
61 "rate = (int) [ 1, MAX ], " \
62 "channels = (int) [ 1, MAX ], " \
63 "endianness = (int) { LITTLE_ENDIAN, BIG_ENDIAN }, " \
64 "width = (int) 24, " \
65 "depth = (int) [ 1, 24 ], " "signed = (boolean) { true, false }; " \
67 "rate = (int) [ 1, MAX ], " \
68 "channels = (int) [ 1, MAX ], " \
69 "endianness = (int) { LITTLE_ENDIAN, BIG_ENDIAN }, " \
70 "width = (int) 16, " \
71 "depth = (int) [ 1, 16 ], " \
72 "signed = (boolean) { true, false }; " \
74 "rate = (int) [ 1, MAX ], " \
75 "channels = (int) [ 1, MAX ], " \
76 "endianness = (int) { LITTLE_ENDIAN, BIG_ENDIAN }, " \
78 "depth = (int) [ 1, 8 ], " \
79 "signed = (boolean) { true, false } " \
82 /* Element sink pad template */
83 static GstStaticPadTemplate gst_audiotp_sink_template = GST_STATIC_PAD_TEMPLATE (
89 /* Element Source Pad template */
90 static GstStaticPadTemplate gst_audiotp_src_template = GST_STATIC_PAD_TEMPLATE (
97 ////////////////////////////////////////////////////////
98 // Gstreamer Base Prototype //
99 ////////////////////////////////////////////////////////
101 GST_DEBUG_CATEGORY_STATIC(gst_audiotp_debug);
102 #define GST_CAT_DEFAULT gst_audiotp_debug
103 #define _do_init(bla) \
104 GST_DEBUG_CATEGORY_INIT(GST_CAT_DEFAULT, "audiotp", 0, "Audio trickplay plugin"); \
105 GST_DEBUG("audiotp is registered");
107 GST_BOILERPLATE_FULL(Gstaudiotp, gst_audiotp, GstElement, GST_TYPE_ELEMENT, _do_init);
109 static void gst_audiotp_base_init(gpointer klass);
110 static void gst_audiotp_class_init(GstaudiotpClass *klass);
111 static void gst_audiotp_init(Gstaudiotp *dec, GstaudiotpClass *klass);
112 static GstFlowReturn gst_audiotp_chain(GstPad *pad, GstBuffer *buf);
113 static GstStateChangeReturn gst_audiotp_change_state(GstElement *element, GstStateChange transition);
114 static void gst_audiotp_finalize(GObject *object);
115 static gboolean gst_audiotp_sink_event (GstPad *pad, GstEvent *event);
116 static GstFlowReturn gst_audiotp_push_silent_frame (Gstaudiotp *audiotp, GstBuffer *MetaDataBuf);
120 ////////////////////////////////////////////////////////
121 // Gstreamer Base Functions //
122 ////////////////////////////////////////////////////////
126 ** Description: The element details and pad templates are registered with the plugin
127 ** In Params : @ gclass instance of Element class
129 ** Comments : 1. Adding templates of source and sink pad to element
130 ** 2. Setting element class deatils to element
134 gst_audiotp_base_init(gpointer klass)
136 GstElementClass *element_class = GST_ELEMENT_CLASS(klass);
138 gst_element_class_add_pad_template(element_class, gst_static_pad_template_get(&gst_audiotp_sink_template));
139 gst_element_class_add_pad_template(element_class, gst_static_pad_template_get(&gst_audiotp_src_template));
140 gst_element_class_set_details(element_class, &gst_audiotp_plugin_details);
146 ** Description: Initialization of the Element Class
147 ** In Param : @ gclass instance of Element class
149 ** Comments : 1. Overwriting base class virtual functions
150 ** 2. Installing the properties of the element
154 gst_audiotp_class_init(GstaudiotpClass *klass)
156 GstElementClass *gstelement_class = GST_ELEMENT_CLASS(klass);
157 GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
159 parent_class = g_type_class_peek_parent(klass);
161 gobject_class->finalize = gst_audiotp_finalize;
162 gstelement_class->change_state = GST_DEBUG_FUNCPTR(gst_audiotp_change_state);
168 ** Description: Initialization of the Element instance
169 ** In Params : @ audio tp element instance
170 ** @ gclass instance of Element class
172 ** Comments : 1. Creating new source & sink pads using templates
173 ** 2. Setting the callback functions to the pads
174 ** 3. Local data initialization.
178 gst_audiotp_init(Gstaudiotp *audiotp, GstaudiotpClass *klass)
181 audiotp->sinkpad = gst_pad_new_from_static_template(&gst_audiotp_sink_template, "sink");
182 audiotp->srcpad = gst_pad_new_from_static_template(&gst_audiotp_src_template, "src");
184 gst_pad_set_chain_function (audiotp->sinkpad, GST_DEBUG_FUNCPTR(gst_audiotp_chain));
185 gst_pad_set_event_function (audiotp->sinkpad, GST_DEBUG_FUNCPTR(gst_audiotp_sink_event));
187 gst_pad_use_fixed_caps(audiotp->srcpad);
189 gst_element_add_pad(GST_ELEMENT(audiotp), audiotp->sinkpad);
190 gst_element_add_pad(GST_ELEMENT(audiotp), audiotp->srcpad);
192 audiotp->reverse = g_queue_new ();
193 audiotp->head_prev = GST_CLOCK_TIME_NONE;
194 audiotp->tail_prev = GST_CLOCK_TIME_NONE;
201 ** Description: Finalization of the Element instance (object)
202 ** In Params : @ audiotp element instance in the form of GObject
204 ** Comments : 1. Local data Deinitialization.
209 gst_audiotp_finalize(GObject *object)
211 Gstaudiotp *audiotp = GST_AUDIOTP(object);
213 while (!g_queue_is_empty (audiotp->reverse)) {
214 GstMiniObject *data = g_queue_pop_head (audiotp->reverse);
215 gst_mini_object_unref (data);
217 /* freeing dealy queue */
218 g_queue_free(audiotp->reverse);
219 audiotp->reverse = NULL;
221 G_OBJECT_CLASS(parent_class)->finalize(object);
227 ** Description: Callback function when the element's state gets changed
228 ** In Params : @ audiotp plugin element
229 ** @ type of state change
230 ** return : status of the state change processing
235 static GstStateChangeReturn
236 gst_audiotp_change_state(GstElement *element, GstStateChange transition)
238 GstStateChangeReturn res = GST_FLOW_ERROR;
240 switch (transition) {
241 case GST_STATE_CHANGE_NULL_TO_READY:
243 case GST_STATE_CHANGE_READY_TO_PAUSED:
245 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
251 res = parent_class->change_state(element, transition);
252 if ( res != GST_STATE_CHANGE_SUCCESS ) {
253 GST_ERROR ("change state error in parent class\n");
254 return GST_STATE_CHANGE_FAILURE;
257 switch (transition) {
258 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
260 case GST_STATE_CHANGE_PAUSED_TO_READY:
262 case GST_STATE_CHANGE_READY_TO_NULL:
274 ** Description: Callback function when sinkpad gets an event
275 ** In Params : @ Sinkpad on which the event arrives
277 ** return : TRUE/FALSE on success/failure of the event processing.
278 ** Comments : 1. Process the event and push it to the source pad.
283 gst_audiotp_sink_event (GstPad *pad, GstEvent *event)
285 Gstaudiotp *audiotp = NULL;
286 gboolean res = FALSE;
288 audiotp = GST_AUDIOTP(GST_PAD_PARENT(pad));
290 switch (GST_EVENT_TYPE(event)) {
291 /* Arrives whenever there is a jump in the normal playback. Ex:SEEK */
292 case GST_EVENT_NEWSEGMENT: {
295 gint64 start, stop, time;
298 GST_INFO_OBJECT (audiotp, "GST_EVENT_NEWSEGMENT");
299 gst_event_parse_new_segment_full(event, &update, &rate, &arate, &format, &start, &stop, &time);
301 if (format != GST_FORMAT_TIME) {
302 GST_ERROR("Format is not supported\n");
303 res = gst_pad_push_event(audiotp->srcpad, event);
307 GST_INFO_OBJECT (audiotp, "update: %d, rate: %0.3f, arate: %0.3f\n", update, rate, arate);
308 GST_INFO_OBJECT (audiotp, "start : %" GST_TIME_FORMAT, GST_TIME_ARGS(start));
309 GST_INFO_OBJECT (audiotp, "stop : %" GST_TIME_FORMAT, GST_TIME_ARGS(stop));
310 GST_INFO_OBJECT (audiotp, "time : %" GST_TIME_FORMAT, GST_TIME_ARGS(time));
312 /* If we receive new_segment without FLUSH events, then we will push all the frame in queue */
313 while (!g_queue_is_empty (audiotp->reverse)) {
314 GstBuffer *MetaDataBuf;
315 GstFlowReturn ret = GST_FLOW_OK;
316 if(audiotp->is_reversed)
317 MetaDataBuf = g_queue_pop_head (audiotp->reverse);
319 MetaDataBuf = g_queue_pop_tail (audiotp->reverse);
320 ret = gst_audiotp_push_silent_frame (audiotp, MetaDataBuf);
321 if (GST_FLOW_OK != ret)
323 GST_WARNING_OBJECT (audiotp, "pad_push returned = %s", gst_flow_get_name (ret));
326 gst_segment_set_newsegment_full(&audiotp->segment, update, rate, arate, format, start, stop, time);
327 res = gst_pad_push_event(audiotp->srcpad, event);
331 /* Indication of the end of the stream */
332 case GST_EVENT_EOS: {
333 /* queue all buffer timestamps till we receive next discontinuity */
334 while (!g_queue_is_empty (audiotp->reverse)) {
335 GstBuffer *MetaDataBuf;
336 GstFlowReturn ret = GST_FLOW_OK;
337 if(audiotp->is_reversed)
338 MetaDataBuf = g_queue_pop_head (audiotp->reverse);
340 MetaDataBuf = g_queue_pop_tail (audiotp->reverse);
341 ret = gst_audiotp_push_silent_frame (audiotp, MetaDataBuf);
342 if (GST_FLOW_OK != ret) {
343 GST_WARNING_OBJECT (audiotp, "pad_push returned = %s", gst_flow_get_name (ret));
347 res = gst_pad_push_event(audiotp->srcpad, event);
351 /* Indication of the SEEK operation start */
352 case GST_EVENT_FLUSH_START: {
353 GST_INFO_OBJECT (audiotp, "GST_EVENT_FLUSH_START");
354 res = gst_pad_push_event(audiotp->srcpad, event);
358 /* Indication of the SEEK operation stop */
359 case GST_EVENT_FLUSH_STOP: {
360 GST_INFO_OBJECT (audiotp, "GST_EVENT_FLUSH_STOP");
361 /* make sure that we empty the queue */
362 while (!g_queue_is_empty (audiotp->reverse)) {
363 GST_DEBUG_OBJECT (audiotp, "Flushing buffers in reverse queue....");
364 gst_buffer_unref(g_queue_pop_head (audiotp->reverse));
367 res = gst_pad_push_event(audiotp->srcpad, event);
372 res = gst_pad_push_event(audiotp->srcpad, event);
384 ** Description: Callback function when sinkpad gets a buffer (from the previous element)
385 ** In Params : @ Sinkpad on which the buffer arrives
387 ** return : status of the buffer processing.
388 ** Comments : 1. Handle the buffer discontinuity ( in terms of tmestamp)
389 ** 2. Push or pop buffer based on discontinuity.
394 gst_audiotp_chain(GstPad *pad, GstBuffer *buf)
396 Gstaudiotp *audiotp = GST_AUDIOTP(GST_PAD_PARENT(pad));
397 GstFlowReturn ret = GST_FLOW_OK;
400 ret = GST_FLOW_ERROR;
404 GST_LOG_OBJECT (audiotp, "Input buffer : ts =%" GST_TIME_FORMAT ", dur=%" GST_TIME_FORMAT ", size=%d",
405 GST_TIME_ARGS(GST_BUFFER_TIMESTAMP(buf)),
406 GST_TIME_ARGS(GST_BUFFER_DURATION(buf)),
407 GST_BUFFER_SIZE(buf), GST_BUFFER_IS_DISCONT (buf) ? " - discont" :"");
409 if (audiotp->segment.rate < 0.0) {
414 /* Push the input data to the next element */
415 ret = gst_pad_push(audiotp->srcpad, buf);
416 if (ret != GST_FLOW_OK ) {
417 GST_WARNING("failed to push buffer %p. reason: %s", buf, gst_flow_get_name (ret));
425 GstBuffer *MetaDataBuf = NULL;
426 GstClockTime headbuf_ts = GST_CLOCK_TIME_NONE;
427 GstClockTime tailbuf_ts = GST_CLOCK_TIME_NONE;
429 /* Discont buffers is mostly due to seek, when buffers of seeked timestamp gets pushed */
430 if (GST_BUFFER_IS_DISCONT(buf)) {
431 if(!g_queue_is_empty (audiotp->reverse)) {
432 GstBuffer *headbuf = (GstBuffer*) (audiotp->reverse->head->data);
433 GstBuffer *tailbuf = (GstBuffer*) (audiotp->reverse->tail->data);
435 headbuf_ts = GST_BUFFER_TIMESTAMP(headbuf);
436 tailbuf_ts = GST_BUFFER_TIMESTAMP(tailbuf);
438 GST_DEBUG_OBJECT(audiotp,"Headbuf ts =%" GST_TIME_FORMAT ", TailBuf ts =%" GST_TIME_FORMAT "",
439 GST_TIME_ARGS(GST_BUFFER_TIMESTAMP(headbuf)),
440 GST_TIME_ARGS(GST_BUFFER_TIMESTAMP(tailbuf)));
442 /* Check if the decoder is already having the reversal logic */
443 if(GST_BUFFER_TIMESTAMP(headbuf) > GST_BUFFER_TIMESTAMP(tailbuf)) {
444 GST_INFO_OBJECT (audiotp, "Buffers arrived in reverse order, audiotp NO NEED to reverse...");
445 audiotp->is_reversed = TRUE;
447 GST_INFO_OBJECT (audiotp, "Buffers arrived in forward order, audiotp NEED to reverse...");
448 audiotp->is_reversed = FALSE;
452 while (!g_queue_is_empty (audiotp->reverse)) {
454 if(audiotp->is_reversed)
455 MetaDataBuf = g_queue_pop_head (audiotp->reverse);
457 MetaDataBuf = g_queue_pop_tail (audiotp->reverse);
459 if (NULL == MetaDataBuf) {
460 GST_ERROR_OBJECT (audiotp, "NULL pointer...");
461 ret = GST_FLOW_ERROR;
465 /* If buffers arrive in forward order, compare the MetaDatabuf with
466 * previous head buffer timestamp.
467 * If buffers arrive in reverse order, compare the MetaDataBuf with
468 * previous tail buffer timestamp */
469 if((GST_BUFFER_TIMESTAMP(MetaDataBuf) < audiotp->head_prev && !audiotp->is_reversed)
470 || (GST_BUFFER_TIMESTAMP(MetaDataBuf) < audiotp->tail_prev && audiotp->is_reversed)) {
471 ret = gst_audiotp_push_silent_frame (audiotp, MetaDataBuf);
473 gst_buffer_unref (MetaDataBuf);
477 if (GST_FLOW_OK != ret) {
478 GST_WARNING_OBJECT (audiotp, "pad_push returned = %s", gst_flow_get_name (ret));
480 gst_buffer_unref (buf);
486 GST_DEBUG_OBJECT(audiotp, "Dropping the buffer out of segment with time-stamp %"GST_TIME_FORMAT,
487 GST_TIME_ARGS(GST_BUFFER_TIMESTAMP(MetaDataBuf)));
489 gst_buffer_unref (MetaDataBuf);
495 audiotp->head_prev = headbuf_ts;
496 audiotp->tail_prev = tailbuf_ts;
499 MetaDataBuf = gst_buffer_new ();
500 if (NULL == MetaDataBuf) {
501 GST_ERROR_OBJECT (audiotp, "Failed to create memory...");
502 ret = GST_FLOW_ERROR;
506 /* copy buffer timestamps & FLAGS to metadata buffer */
507 gst_buffer_copy_metadata (MetaDataBuf, buf, GST_BUFFER_COPY_TIMESTAMPS | GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_CAPS);
508 GST_BUFFER_SIZE(MetaDataBuf) = GST_BUFFER_SIZE(buf);
509 GST_DEBUG_OBJECT (audiotp, "Pushing into reverse queue data of size: %d", GST_BUFFER_SIZE(MetaDataBuf));
511 /* queue all buffer timestamps till we receive next discontinuity */
512 g_queue_push_tail (audiotp->reverse, MetaDataBuf);
514 gst_buffer_unref (buf);
524 /* Resetting the buffer data to zero */
525 memset(GST_BUFFER_DATA(buf), 0, GST_BUFFER_SIZE(buf));
526 gst_buffer_set_caps(buf, GST_PAD_CAPS(audiotp->srcpad));
528 ret = gst_pad_push(audiotp->srcpad, buf);
529 if (ret != GST_FLOW_OK) {
530 GST_ERROR("Failed to push buffer. reason: %s\n", gst_flow_get_name(ret));
540 GST_WARNING_OBJECT(audiotp, "Returning from audiotp's chain with reason - %s", gst_flow_get_name (ret));
542 gst_buffer_unref (buf);
550 gst_audiotp_push_silent_frame (Gstaudiotp *audiotp, GstBuffer *MetaDataBuf)
553 GstBuffer *out = NULL;
554 GstFlowReturn ret = GST_FLOW_OK;
556 out = gst_buffer_new_and_alloc(GST_BUFFER_SIZE(MetaDataBuf));
558 GST_ERROR_OBJECT (audiotp, "Failed to allocate memory...");
559 return GST_FLOW_ERROR;
562 /* Memset the data of the out buffer so that silent frame is sent */
563 memset(GST_BUFFER_DATA(out), 0, GST_BUFFER_SIZE(out));
565 gst_buffer_copy_metadata (out, MetaDataBuf, GST_BUFFER_COPY_FLAGS);
566 GST_BUFFER_OFFSET (out) = GST_BUFFER_OFFSET_END (out) = 0;
567 GST_BUFFER_SIZE(out) = GST_BUFFER_SIZE(MetaDataBuf);
568 GST_BUFFER_TIMESTAMP(out) = GST_BUFFER_TIMESTAMP(MetaDataBuf);
569 GST_BUFFER_DURATION(out) = GST_BUFFER_DURATION(MetaDataBuf);
571 GST_LOG_OBJECT(audiotp, "Out buffer ts =%" GST_TIME_FORMAT ", dur=%" GST_TIME_FORMAT ", size=%d",
572 GST_TIME_ARGS(GST_BUFFER_TIMESTAMP(out)),
573 GST_TIME_ARGS(GST_BUFFER_DURATION(out)),
574 GST_BUFFER_SIZE(out));
576 gst_buffer_set_caps(out, GST_PAD_CAPS(audiotp->srcpad));
578 ret = gst_pad_push(audiotp->srcpad, out);
579 if (ret != GST_FLOW_OK) {
580 GST_ERROR_OBJECT (audiotp, "Failed to push buffer. reason: %s\n", gst_flow_get_name(ret));
588 gst_audiotp_plugin_init (GstPlugin *plugin)
590 if (!gst_element_register (plugin, "audiotp", GST_RANK_PRIMARY, gst_audiotp_get_type())) {
596 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
599 "Audio trickplay plugin",
600 gst_audiotp_plugin_init,
603 "Samsung Electronics Co",
604 "http://www.samsung.com")