Initialize Tizen 2.3
[framework/multimedia/gst-plugins-ext0.10.git] / audiotp / src / gstaudiotp.c
1 /*
2  * audiotp
3  *
4  * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved.
5  *
6  * Contact: JongHyuk Choi <jhchoi.choi@samsung.com>
7  *
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)
11  * any later version.
12  *
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.
17  *
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
21  *
22  */
23
24
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #endif
28
29 #include "gstaudiotp.h"
30
31 /* Plugin Detaills for gstreamer */
32 static const GstElementDetails gst_audiotp_plugin_details = GST_ELEMENT_DETAILS (
33  "Audio timestamp reversal plugin",
34  "Utility/Audio",
35  "Reverses audio timestamps for reverse playback",
36  "Samsung Electronics <www.samsung.com>"
37  );
38
39 /*** GSTREAMER PROTOTYPES *****************************************************/
40
41 #define STATIC_CAPS \
42 GST_STATIC_CAPS ( \
43   "audio/x-raw-float, " \
44     "rate = (int) [ 1, MAX ], " \
45     "channels = (int) [ 1, MAX ], " \
46     "endianness = (int) { LITTLE_ENDIAN, BIG_ENDIAN }, " \
47     "width = (int) 64;" \
48   "audio/x-raw-float, " \
49     "rate = (int) [ 1, MAX ], " \
50     "channels = (int) [ 1, MAX ], " \
51     "endianness = (int) { LITTLE_ENDIAN, BIG_ENDIAN }, " \
52     "width = (int) 32;" \
53   "audio/x-raw-int, " \
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 }; " \
60   "audio/x-raw-int, "   \
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 }; "  \
66   "audio/x-raw-int, " \
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 }; " \
73   "audio/x-raw-int, " \
74     "rate = (int) [ 1, MAX ], " \
75     "channels = (int) [ 1, MAX ], " \
76     "endianness = (int) { LITTLE_ENDIAN, BIG_ENDIAN }, " \
77     "width = (int) 8, " \
78     "depth = (int) [ 1, 8 ], " \
79     "signed = (boolean) { true, false } " \
80 )
81
82 /* Element sink pad template */
83 static GstStaticPadTemplate gst_audiotp_sink_template = GST_STATIC_PAD_TEMPLATE (
84  "sink",
85  GST_PAD_SINK,
86  GST_PAD_ALWAYS,
87  STATIC_CAPS);
88
89 /* Element Source Pad template */
90 static GstStaticPadTemplate gst_audiotp_src_template = GST_STATIC_PAD_TEMPLATE (
91  "src",
92  GST_PAD_SRC,
93  GST_PAD_ALWAYS,
94  STATIC_CAPS);
95
96
97 ////////////////////////////////////////////////////////
98 //        Gstreamer Base Prototype                    //
99 ////////////////////////////////////////////////////////
100
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");
106
107 GST_BOILERPLATE_FULL(Gstaudiotp, gst_audiotp, GstElement, GST_TYPE_ELEMENT, _do_init);
108
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);
117
118
119
120 ////////////////////////////////////////////////////////
121 //        Gstreamer Base Functions                    //
122 ////////////////////////////////////////////////////////
123
124 /**
125  **
126  **  Description: The element details and pad templates are registered with the plugin
127  **  In Params    : @ gclass instance of Element class
128  **  return    : None
129  **  Comments    : 1. Adding templates of source and sink pad to element
130  **           2. Setting element class deatils to element
131  **
132  */
133 static void
134 gst_audiotp_base_init(gpointer klass)
135 {
136   GstElementClass *element_class = GST_ELEMENT_CLASS(klass);
137
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);
141 }
142
143
144 /**
145  **
146  **  Description: Initialization of the Element Class
147  **  In Param    : @ gclass instance of Element class
148  **  return    : None
149  **  Comments    : 1. Overwriting base class virtual functions
150  **                2. Installing the properties of the element
151  **
152  */
153 static void
154 gst_audiotp_class_init(GstaudiotpClass *klass)
155 {
156   GstElementClass *gstelement_class = GST_ELEMENT_CLASS(klass);
157   GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
158
159   parent_class = g_type_class_peek_parent(klass);
160
161   gobject_class->finalize = gst_audiotp_finalize;
162   gstelement_class->change_state = GST_DEBUG_FUNCPTR(gst_audiotp_change_state);
163 }
164
165
166 /**
167  **
168  **  Description: Initialization of the Element instance
169  **  In Params    : @ audio tp element instance
170  **           @ gclass instance of Element class
171  **  return    : None
172  **  Comments    : 1. Creating new source & sink pads using templates
173  **           2. Setting the callback functions to the pads
174  **          3. Local data initialization.
175  **
176  */
177 static void
178 gst_audiotp_init(Gstaudiotp *audiotp, GstaudiotpClass *klass)
179 {
180
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");
183
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));
186
187   gst_pad_use_fixed_caps(audiotp->srcpad);
188
189   gst_element_add_pad(GST_ELEMENT(audiotp), audiotp->sinkpad);
190   gst_element_add_pad(GST_ELEMENT(audiotp), audiotp->srcpad);
191
192   audiotp->reverse = g_queue_new ();
193   audiotp->head_prev = GST_CLOCK_TIME_NONE;
194   audiotp->tail_prev = GST_CLOCK_TIME_NONE;
195
196 }
197
198
199 /**
200  **
201  **  Description: Finalization of the Element instance (object)
202  **  In Params    : @ audiotp element instance in the form of GObject
203  **  return    : None
204  **  Comments    : 1. Local data Deinitialization.
205  **
206  **
207  */
208 static void
209 gst_audiotp_finalize(GObject *object)
210 {
211   Gstaudiotp *audiotp = GST_AUDIOTP(object);
212
213   while (!g_queue_is_empty (audiotp->reverse)) {
214     GstMiniObject *data = g_queue_pop_head (audiotp->reverse);
215     gst_mini_object_unref (data);
216   }
217   /* freeing dealy queue */
218   g_queue_free(audiotp->reverse);
219   audiotp->reverse = NULL;
220
221   G_OBJECT_CLASS(parent_class)->finalize(object);
222 }
223
224
225 /**
226  **
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
231  **  Comments    :
232  **
233  **
234  */
235 static GstStateChangeReturn
236 gst_audiotp_change_state(GstElement *element, GstStateChange transition)
237 {
238   GstStateChangeReturn res = GST_FLOW_ERROR;
239
240   switch (transition) {
241     case GST_STATE_CHANGE_NULL_TO_READY:
242       break;
243     case GST_STATE_CHANGE_READY_TO_PAUSED:
244       break;
245     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
246       break;
247     default:
248       break;
249   }
250
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;
255   }
256
257   switch (transition) {
258     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
259       break;
260     case GST_STATE_CHANGE_PAUSED_TO_READY:
261       break;
262     case GST_STATE_CHANGE_READY_TO_NULL:
263       break;
264     default:
265       break;
266   }
267
268   return res;
269 }
270
271
272 /**
273  **
274  **  Description: Callback function when sinkpad gets an event
275  **  In Params    : @ Sinkpad on which the event arrives
276  **          @ event type
277  **  return    : TRUE/FALSE on success/failure of the event processing.
278  **  Comments    : 1. Process the event and push it to the source pad.
279  **
280  **
281  */
282 static gboolean
283 gst_audiotp_sink_event (GstPad *pad, GstEvent *event)
284 {
285   Gstaudiotp *audiotp = NULL;
286   gboolean res = FALSE;
287
288   audiotp = GST_AUDIOTP(GST_PAD_PARENT(pad));
289
290   switch (GST_EVENT_TYPE(event)) {
291     /* Arrives whenever there is a jump in the normal playback. Ex:SEEK */
292     case GST_EVENT_NEWSEGMENT: {
293       GstFormat format;
294       gdouble rate, arate;
295       gint64 start, stop, time;
296       gboolean update;
297
298       GST_INFO_OBJECT (audiotp, "GST_EVENT_NEWSEGMENT");
299       gst_event_parse_new_segment_full(event, &update, &rate, &arate, &format, &start, &stop, &time);
300
301       if (format != GST_FORMAT_TIME) {
302         GST_ERROR("Format is not supported\n");
303         res = gst_pad_push_event(audiotp->srcpad, event);
304         goto done;
305       }
306
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));
311
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);
318                 else
319                   MetaDataBuf = g_queue_pop_tail (audiotp->reverse);
320         ret = gst_audiotp_push_silent_frame (audiotp, MetaDataBuf);
321         if (GST_FLOW_OK != ret)
322         {
323            GST_WARNING_OBJECT (audiotp, "pad_push returned = %s", gst_flow_get_name (ret));
324         }
325       }
326       gst_segment_set_newsegment_full(&audiotp->segment, update, rate, arate, format, start, stop, time);
327       res = gst_pad_push_event(audiotp->srcpad, event);
328       break;
329     }
330
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);
339                 else
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));
344         }
345       }
346
347       res = gst_pad_push_event(audiotp->srcpad, event);
348       break;
349     }
350
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);
355       break;
356     }
357
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));
365       }
366
367       res = gst_pad_push_event(audiotp->srcpad, event);
368       break;
369     }
370
371     default: {
372       res = gst_pad_push_event(audiotp->srcpad, event);
373       break;
374     }
375   }
376
377   done:
378     return res;
379 }
380
381
382 /**
383  **
384  **  Description: Callback function when sinkpad gets a buffer (from the previous element)
385  **  In Params    : @ Sinkpad on which the buffer arrives
386  **          @ input buffer
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.
390  **
391  **
392  */
393 static GstFlowReturn
394 gst_audiotp_chain(GstPad *pad, GstBuffer *buf)
395 {
396   Gstaudiotp *audiotp = GST_AUDIOTP(GST_PAD_PARENT(pad));
397   GstFlowReturn ret = GST_FLOW_OK;
398
399   if(buf == NULL) {
400     ret = GST_FLOW_ERROR;
401         goto error_exit;
402   }
403
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" :"");
408
409   if (audiotp->segment.rate < 0.0) {
410     goto send_reverse;
411   }
412
413
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));
418    buf = NULL;
419    goto error_exit;
420   }
421   return ret;
422
423 send_reverse:
424   {
425     GstBuffer *MetaDataBuf = NULL;
426     GstClockTime headbuf_ts = GST_CLOCK_TIME_NONE;
427         GstClockTime tailbuf_ts = GST_CLOCK_TIME_NONE;
428
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);
434
435         headbuf_ts = GST_BUFFER_TIMESTAMP(headbuf);
436                 tailbuf_ts = GST_BUFFER_TIMESTAMP(tailbuf);
437
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)));
441
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;
446         } else {
447           GST_INFO_OBJECT (audiotp, "Buffers arrived in forward order, audiotp NEED to reverse...");
448           audiotp->is_reversed = FALSE;
449         }
450       }
451
452       while (!g_queue_is_empty (audiotp->reverse)) {
453
454         if(audiotp->is_reversed)
455           MetaDataBuf = g_queue_pop_head (audiotp->reverse);
456         else
457           MetaDataBuf = g_queue_pop_tail (audiotp->reverse);
458
459         if (NULL == MetaDataBuf) {
460           GST_ERROR_OBJECT (audiotp, "NULL pointer...");
461           ret = GST_FLOW_ERROR;
462           goto error_exit;
463         }
464
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);
472           if (MetaDataBuf) {
473             gst_buffer_unref (MetaDataBuf);
474             MetaDataBuf = NULL;
475           }
476
477           if (GST_FLOW_OK != ret) {
478             GST_WARNING_OBJECT (audiotp, "pad_push returned = %s", gst_flow_get_name (ret));
479             if (buf) {
480               gst_buffer_unref (buf);
481               buf = NULL;
482             }
483             return ret;
484           }
485         } else {
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)));
488           if (MetaDataBuf) {
489             gst_buffer_unref (MetaDataBuf);
490             MetaDataBuf = NULL;
491           }
492         }
493       }
494
495       audiotp->head_prev = headbuf_ts;
496           audiotp->tail_prev = tailbuf_ts;
497     }
498
499     MetaDataBuf = gst_buffer_new ();
500     if (NULL == MetaDataBuf) {
501       GST_ERROR_OBJECT (audiotp, "Failed to create memory...");
502       ret = GST_FLOW_ERROR;
503       goto error_exit;
504     }
505
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));
510
511     /* queue all buffer timestamps till we receive next discontinuity */
512     g_queue_push_tail (audiotp->reverse, MetaDataBuf);
513     if (buf) {
514       gst_buffer_unref (buf);
515       buf = NULL;
516     }
517     return GST_FLOW_OK;
518   }
519
520 #if 0
521 send_dummy:
522   {
523
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));
527
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));
531      buf = NULL;
532      goto error_exit;
533     }
534     return GST_FLOW_OK;
535   }
536 #endif
537
538 error_exit:
539
540   GST_WARNING_OBJECT(audiotp, "Returning from audiotp's chain with reason - %s", gst_flow_get_name (ret));
541   if (buf) {
542    gst_buffer_unref (buf);
543    buf = NULL;
544   }
545   return ret;
546 }
547
548
549 static GstFlowReturn
550 gst_audiotp_push_silent_frame (Gstaudiotp *audiotp, GstBuffer *MetaDataBuf)
551 {
552
553   GstBuffer *out = NULL;
554   GstFlowReturn ret = GST_FLOW_OK;
555
556   out = gst_buffer_new_and_alloc(GST_BUFFER_SIZE(MetaDataBuf));
557   if(out == NULL) {
558     GST_ERROR_OBJECT (audiotp, "Failed to allocate memory...");
559     return GST_FLOW_ERROR;
560   }
561
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));
564
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);
570
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));
575
576   gst_buffer_set_caps(out, GST_PAD_CAPS(audiotp->srcpad));
577
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));
581     out = NULL;
582   }
583
584   return ret;
585 }
586
587 static gboolean
588 gst_audiotp_plugin_init (GstPlugin *plugin)
589 {
590   if (!gst_element_register (plugin, "audiotp", GST_RANK_PRIMARY, gst_audiotp_get_type())) {
591     return FALSE;
592   }
593   return TRUE;
594 }
595
596 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
597                    GST_VERSION_MINOR,
598                    "audiotp",
599                    "Audio trickplay plugin",
600                    gst_audiotp_plugin_init,
601                    VERSION,
602                    "LGPL",
603                    "Samsung Electronics Co",
604                    "http://www.samsung.com")