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);
106 }
108 static void
109 gst_amrnbparse_class_init (GstAmrnbParseClass * klass)
110 {
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");
119 }
121 static void
122 gst_amrnbparse_init (GstAmrnbParse * amrnbparse)
123 {
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;
150 }
152 /*
153 * Position querying.
154 */
156 #if 0
157 static const GstFormat *
158 gst_amrnbparse_formats (GstPad * pad)
159 {
160 static const GstFormat list[] = {
161 GST_FORMAT_TIME,
162 0
163 };
165 return list;
166 }
167 #endif
169 static const GstQueryType *
170 gst_amrnbparse_querytypes (GstPad * pad)
171 {
172 static const GstQueryType list[] = {
173 GST_QUERY_POSITION,
174 0
175 };
177 return list;
178 }
180 static gboolean
181 gst_amrnbparse_query (GstPad * pad, GstQuery * query)
182 {
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;
240 }
243 /*
244 * Data reading.
245 */
246 static gboolean
247 gst_amrnbparse_event (GstPad * pad, GstEvent * event)
248 {
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;
265 }
267 /* streaming mode */
268 static GstFlowReturn
269 gst_amrnbparse_chain (GstPad * pad, GstBuffer * buffer)
270 {
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;
330 }
332 static gboolean
333 gst_amrnbparse_read_header (GstAmrnbParse * amrnbparse)
334 {
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 }
367 }
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)
373 {
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 }
448 }
450 static gboolean
451 gst_amrnbparse_sink_activate (GstPad * sinkpad)
452 {
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;
486 }
488 static GstStateChangeReturn
489 gst_amrnbparse_state_change (GstElement * element, GstStateChange transition)
490 {
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;
517 }