add test for amrnbenc, enable test infrastructure, and fix a leak
[glsdk/gst-plugins-ugly0-10.git] / ext / amrnb / amrnbparse.c
1 /* GStreamer Adaptive Multi-Rate Narrow-Band (AMR-NB) plugin
2  * Copyright (C) 2004 Ronald Bultje <rbultje@ronald.bitfreak.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., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
24 #include <string.h>
26 #include "amrnbparse.h"
28 GST_DEBUG_CATEGORY_STATIC (amrnbparse_debug);
29 #define GST_CAT_DEFAULT amrnbparse_debug
31 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
32     GST_PAD_SRC,
33     GST_PAD_ALWAYS,
34     GST_STATIC_CAPS ("audio/AMR, " "rate = (int) 8000, " "channels = (int) 1")
35     );
37 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
38     GST_PAD_SINK,
39     GST_PAD_ALWAYS,
40     GST_STATIC_CAPS ("audio/x-amr-nb-sh")
41     );
43 static const gint block_size[16] = { 12, 13, 15, 17, 19, 20, 26, 31, 5,
44   0, 0, 0, 0, 0, 0, 0
45 };
47 static void gst_amrnbparse_base_init (GstAmrnbParseClass * klass);
48 static void gst_amrnbparse_class_init (GstAmrnbParseClass * klass);
49 static void gst_amrnbparse_init (GstAmrnbParse * amrnbparse);
51 //static const GstFormat *gst_amrnbparse_formats (GstPad * pad);
52 static const GstQueryType *gst_amrnbparse_querytypes (GstPad * pad);
53 static gboolean gst_amrnbparse_query (GstPad * pad, GstQuery * query);
55 static gboolean gst_amrnbparse_event (GstPad * pad, GstEvent * event);
56 static GstFlowReturn gst_amrnbparse_chain (GstPad * pad, GstBuffer * buffer);
57 static void gst_amrnbparse_loop (GstPad * pad);
58 static gboolean gst_amrnbparse_sink_activate (GstPad * sinkpad);
59 static GstStateChangeReturn gst_amrnbparse_state_change (GstElement * element,
60     GstStateChange transition);
62 static GstElementClass *parent_class = NULL;
64 GType
65 gst_amrnbparse_get_type (void)
66 {
67   static GType amrnbparse_type = 0;
69   if (!amrnbparse_type) {
70     static const GTypeInfo amrnbparse_info = {
71       sizeof (GstAmrnbParseClass),
72       (GBaseInitFunc) gst_amrnbparse_base_init,
73       NULL,
74       (GClassInitFunc) gst_amrnbparse_class_init,
75       NULL,
76       NULL,
77       sizeof (GstAmrnbParse),
78       0,
79       (GInstanceInitFunc) gst_amrnbparse_init,
80     };
82     amrnbparse_type = g_type_register_static (GST_TYPE_ELEMENT,
83         "GstAmrnbParse", &amrnbparse_info, 0);
84   }
86   return amrnbparse_type;
87 }
89 static void
90 gst_amrnbparse_base_init (GstAmrnbParseClass * klass)
91 {
92   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
93   GstElementDetails gst_amrnbparse_details = {
94     "AMR-NB parser",
95     "Codec/Parser/Audio",
96     "Adaptive Multi-Rate Narrow-Band audio parser",
97     "Ronald Bultje <rbultje@ronald.bitfreak.net>"
98   };
100   gst_element_class_add_pad_template (element_class,
101       gst_static_pad_template_get (&sink_template));
102   gst_element_class_add_pad_template (element_class,
103       gst_static_pad_template_get (&src_template));
105   gst_element_class_set_details (element_class, &gst_amrnbparse_details);
108 static void
109 gst_amrnbparse_class_init (GstAmrnbParseClass * klass)
111   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
113   parent_class = g_type_class_peek_parent (klass);
115   element_class->change_state = gst_amrnbparse_state_change;
117   GST_DEBUG_CATEGORY_INIT (amrnbparse_debug,
118       "amrnbparse", 0, "AMR-NB stream parsing");
121 static void
122 gst_amrnbparse_init (GstAmrnbParse * amrnbparse)
124   /* create the sink pad */
125   amrnbparse->sinkpad =
126       gst_pad_new_from_template (gst_static_pad_template_get (&sink_template),
127       "sink");
128   gst_pad_set_chain_function (amrnbparse->sinkpad,
129       GST_DEBUG_FUNCPTR (gst_amrnbparse_chain));
130   gst_pad_set_event_function (amrnbparse->sinkpad,
131       GST_DEBUG_FUNCPTR (gst_amrnbparse_event));
132   gst_pad_set_activate_function (amrnbparse->sinkpad,
133       gst_amrnbparse_sink_activate);
134   gst_element_add_pad (GST_ELEMENT (amrnbparse), amrnbparse->sinkpad);
136   /* create the src pad */
137   amrnbparse->srcpad =
138       gst_pad_new_from_template (gst_static_pad_template_get (&src_template),
139       "src");
140   gst_pad_set_query_function (amrnbparse->srcpad,
141       GST_DEBUG_FUNCPTR (gst_amrnbparse_query));
142   gst_pad_set_query_type_function (amrnbparse->srcpad,
143       GST_DEBUG_FUNCPTR (gst_amrnbparse_querytypes));
144   gst_element_add_pad (GST_ELEMENT (amrnbparse), amrnbparse->srcpad);
146   amrnbparse->adapter = gst_adapter_new ();
148   /* init rest */
149   amrnbparse->ts = 0;
152 /*
153  * Position querying.
154  */
156 #if 0
157 static const GstFormat *
158 gst_amrnbparse_formats (GstPad * pad)
160   static const GstFormat list[] = {
161     GST_FORMAT_TIME,
162     0
163   };
165   return list;
167 #endif
169 static const GstQueryType *
170 gst_amrnbparse_querytypes (GstPad * pad)
172   static const GstQueryType list[] = {
173     GST_QUERY_POSITION,
174     0
175   };
177   return list;
180 static gboolean
181 gst_amrnbparse_query (GstPad * pad, GstQuery * query)
183   GstAmrnbParse *amrnbparse;
184   gboolean res = TRUE;
186   amrnbparse = GST_AMRNBPARSE (GST_PAD_PARENT (pad));
188   switch (GST_QUERY_TYPE (query)) {
189     case GST_QUERY_POSITION:
190     {
191       GstFormat format;
192       gint64 cur;
194       gst_query_parse_position (query, &format, NULL);
196       if (format != GST_FORMAT_TIME)
197         return FALSE;
199       cur = amrnbparse->ts;
201       gst_query_set_position (query, GST_FORMAT_TIME, cur);
202       res = TRUE;
203       break;
204     }
205     case GST_QUERY_DURATION:
206     {
207       GstFormat format;
208       gint64 tot;
209       GstPad *peer;
211       gst_query_parse_duration (query, &format, NULL);
213       if (format != GST_FORMAT_TIME)
214         return FALSE;
216       tot = -1;
218       peer = gst_pad_get_peer (amrnbparse->sinkpad);
219       if (peer) {
220         GstFormat pformat;
221         gint64 pcur, ptot;
223         pformat = GST_FORMAT_BYTES;
224         res = gst_pad_query_position (peer, &pformat, &pcur);
225         res &= gst_pad_query_duration (peer, &pformat, &ptot);
226         gst_object_unref (GST_OBJECT (peer));
227         if (res) {
228           tot = amrnbparse->ts * ((gdouble) ptot / pcur);
229         }
230       }
231       gst_query_set_duration (query, GST_FORMAT_TIME, tot);
232       res = TRUE;
233       break;
234     }
235     default:
236       res = gst_pad_query_default (pad, query);
237       break;
238   }
239   return res;
243 /*
244  * Data reading.
245  */
246 static gboolean
247 gst_amrnbparse_event (GstPad * pad, GstEvent * event)
249   GstAmrnbParse *amrnbparse;
250   gboolean res;
252   amrnbparse = GST_AMRNBPARSE (GST_PAD_PARENT (pad));
254   GST_LOG ("handling event %d", GST_EVENT_TYPE (event));
256   switch (GST_EVENT_TYPE (event)) {
257     case GST_EVENT_EOS:
258     default:
259       break;
260   }
262   res = gst_pad_event_default (amrnbparse->sinkpad, event);
264   return res;
267 /* streaming mode */
268 static GstFlowReturn
269 gst_amrnbparse_chain (GstPad * pad, GstBuffer * buffer)
271   GstAmrnbParse *amrnbparse;
272   GstFlowReturn res;
273   gint block, mode;
274   const guint8 *data;
275   GstBuffer *out;
277   amrnbparse = GST_AMRNBPARSE (GST_PAD_PARENT (pad));
279   gst_adapter_push (amrnbparse->adapter, buffer);
281   res = GST_FLOW_OK;
283   /* init */
284   if (amrnbparse->need_header) {
286     if (gst_adapter_available (amrnbparse->adapter) < 6)
287       goto done;
289     data = gst_adapter_peek (amrnbparse->adapter, 6);
290     if (memcmp (data, "#!AMR\n", 6) != 0)
291       goto done;
293     gst_adapter_flush (amrnbparse->adapter, 6);
295     amrnbparse->need_header = FALSE;
296   }
298   while (TRUE) {
299     if (gst_adapter_available (amrnbparse->adapter) < 1)
300       break;
301     data = gst_adapter_peek (amrnbparse->adapter, 1);
303     /* get size */
304     mode = (data[0] >> 3) & 0x0F;
305     block = block_size[mode] + 1;       /* add one for the mode */
307     if (gst_adapter_available (amrnbparse->adapter) < block)
308       break;
310     out = gst_buffer_new_and_alloc (block);
312     data = gst_adapter_peek (amrnbparse->adapter, block);
313     memcpy (GST_BUFFER_DATA (out), data, block);
315     /* output */
316     GST_BUFFER_DURATION (out) = GST_SECOND * 160 / 8000;
317     GST_BUFFER_TIMESTAMP (out) = amrnbparse->ts;
318     amrnbparse->ts += GST_BUFFER_DURATION (out);
319     gst_buffer_set_caps (out,
320         (GstCaps *) gst_pad_get_pad_template_caps (amrnbparse->srcpad));
322     GST_DEBUG ("Pushing %d bytes of data", block);
323     res = gst_pad_push (amrnbparse->srcpad, out);
325     gst_adapter_flush (amrnbparse->adapter, block);
326   }
327 done:
329   return res;
332 static gboolean
333 gst_amrnbparse_read_header (GstAmrnbParse * amrnbparse)
335   GstBuffer *buffer;
336   GstFlowReturn ret;
337   guint8 *data;
338   gint size;
340   ret =
341       gst_pad_pull_range (amrnbparse->sinkpad, amrnbparse->offset, 6, &buffer);
342   if (ret != GST_FLOW_OK)
343     return FALSE;
345   data = GST_BUFFER_DATA (buffer);
346   size = GST_BUFFER_SIZE (buffer);
347   if (size < 6)
348     goto not_enough;
350   if (memcmp (data, "#!AMR\n", 6))
351     goto no_header;
353   amrnbparse->offset += 6;
355   return TRUE;
357 not_enough:
358   {
359     gst_buffer_unref (buffer);
360     return FALSE;
361   }
362 no_header:
363   {
364     gst_buffer_unref (buffer);
365     return FALSE;
366   }
369 /* random access mode, could just read a fixed size buffer and push it to
370  * the chain function but we don't... */
371 static void
372 gst_amrnbparse_loop (GstPad * pad)
374   GstAmrnbParse *amrnbparse;
375   GstBuffer *buffer;
376   guint8 *data;
377   gint size;
378   gint block, mode;
379   GstFlowReturn ret;
381   amrnbparse = GST_AMRNBPARSE (GST_PAD_PARENT (pad));
383   /* init */
384   if (amrnbparse->need_header) {
385     gboolean got_header;
387     got_header = gst_amrnbparse_read_header (amrnbparse);
388     if (!got_header) {
389       GST_LOG_OBJECT (amrnbparse, "could not read header");
390       goto need_pause;
391     }
392     amrnbparse->need_header = FALSE;
393   }
395   ret =
396       gst_pad_pull_range (amrnbparse->sinkpad, amrnbparse->offset, 1, &buffer);
397   if (ret != GST_FLOW_OK)
398     goto need_pause;
400   data = GST_BUFFER_DATA (buffer);
401   size = GST_BUFFER_SIZE (buffer);
403   /* EOS */
404   if (size < 1)
405     goto eos;
407   /* get size */
408   mode = (data[0] >> 3) & 0x0F;
409   block = block_size[mode] + 1; /* add one for the mode */
411   gst_buffer_unref (buffer);
413   ret =
414       gst_pad_pull_range (amrnbparse->sinkpad, amrnbparse->offset, block,
415       &buffer);
416   if (ret != GST_FLOW_OK)
417     goto need_pause;
419   amrnbparse->offset += block;
421   /* output */
422   GST_BUFFER_DURATION (buffer) = GST_SECOND * 160 / 8000;
423   GST_BUFFER_TIMESTAMP (buffer) = amrnbparse->ts;
424   amrnbparse->ts += GST_BUFFER_DURATION (buffer);
425   gst_buffer_set_caps (buffer,
426       (GstCaps *) gst_pad_get_pad_template_caps (amrnbparse->srcpad));
428   GST_DEBUG ("Pushing %d bytes of data", block);
429   ret = gst_pad_push (amrnbparse->srcpad, buffer);
430   if (ret != GST_FLOW_OK)
431     goto need_pause;
433   return;
435 need_pause:
436   {
437     GST_LOG_OBJECT (amrnbparse, "pausing task");
438     gst_pad_pause_task (pad);
439     return;
440   }
441 eos:
442   {
443     GST_LOG_OBJECT (amrnbparse, "pausing task");
444     gst_pad_push_event (amrnbparse->srcpad, gst_event_new_eos ());
445     gst_pad_pause_task (pad);
446     return;
447   }
450 static gboolean
451 gst_amrnbparse_sink_activate (GstPad * sinkpad)
453   gboolean result = FALSE;
454   GstAmrnbParse *amrnbparse;
455   GstActivateMode mode;
457   amrnbparse = GST_AMRNBPARSE (GST_OBJECT_PARENT (sinkpad));
459   GST_OBJECT_LOCK (sinkpad);
460   mode = GST_PAD_ACTIVATE_MODE (sinkpad);
461   GST_OBJECT_UNLOCK (sinkpad);
463   switch (mode) {
464     case GST_ACTIVATE_PUSH:
465       amrnbparse->seekable = FALSE;
466       result = TRUE;
467       break;
468     case GST_ACTIVATE_PULL:
469       /*gst_pad_peer_set_active (sinkpad, mode); */
471       amrnbparse->need_header = TRUE;
472       amrnbparse->seekable = TRUE;
473       amrnbparse->ts = 0;
475       result = gst_pad_start_task (sinkpad,
476           (GstTaskFunction) gst_amrnbparse_loop, sinkpad);
477       break;
478     case GST_ACTIVATE_NONE:
479       /* step 1, unblock clock sync (if any) */
481       /* step 2, make sure streaming finishes */
482       result = gst_pad_stop_task (sinkpad);
483       break;
484   }
485   return result;
488 static GstStateChangeReturn
489 gst_amrnbparse_state_change (GstElement * element, GstStateChange transition)
491   GstAmrnbParse *amrnbparse;
492   GstStateChangeReturn ret;
494   amrnbparse = GST_AMRNBPARSE (element);
496   switch (transition) {
497     case GST_STATE_CHANGE_NULL_TO_READY:
498       break;
499     case GST_STATE_CHANGE_READY_TO_PAUSED:
500       break;
501     default:
502       break;
503   }
505   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
507   switch (transition) {
508     case GST_STATE_CHANGE_PAUSED_TO_READY:
509       break;
510     case GST_STATE_CHANGE_READY_TO_NULL:
511       break;
512     default:
513       break;
514   }
516   return ret;