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/x-amr-nb, "
35 "rate = (int) 8000, " "channels = (int) 1")
36 );
38 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
39 GST_PAD_SINK,
40 GST_PAD_ALWAYS,
41 GST_STATIC_CAPS ("audio/x-amr-nb-sh")
42 );
44 static const gint block_size[16] = { 12, 13, 15, 17, 19, 20, 26, 31, 5,
45 0, 0, 0, 0, 0, 0, 0
46 };
48 static void gst_amrnbparse_base_init (GstAmrnbParseClass * klass);
49 static void gst_amrnbparse_class_init (GstAmrnbParseClass * klass);
50 static void gst_amrnbparse_init (GstAmrnbParse * amrnbparse);
52 //static const GstFormat *gst_amrnbparse_formats (GstPad * pad);
53 static const GstQueryType *gst_amrnbparse_querytypes (GstPad * pad);
54 static gboolean gst_amrnbparse_query (GstPad * pad, GstQuery * query);
56 static gboolean gst_amrnbparse_event (GstPad * pad, GstEvent * event);
57 static GstFlowReturn gst_amrnbparse_chain (GstPad * pad, GstBuffer * buffer);
58 static void gst_amrnbparse_loop (GstPad * pad);
59 static gboolean gst_amrnbparse_sink_activate (GstPad * sinkpad);
60 static GstElementStateReturn gst_amrnbparse_state_change (GstElement * element);
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_ref (GST_TYPE_ELEMENT);
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_loop_function (amrnbparse->sinkpad,
133 GST_DEBUG_FUNCPTR (gst_amrnbparse_loop));
134 */
135 gst_pad_set_activate_function (amrnbparse->sinkpad,
136 gst_amrnbparse_sink_activate);
137 gst_element_add_pad (GST_ELEMENT (amrnbparse), amrnbparse->sinkpad);
139 /* create the src pad */
140 amrnbparse->srcpad =
141 gst_pad_new_from_template (gst_static_pad_template_get (&src_template),
142 "src");
143 gst_pad_set_query_function (amrnbparse->srcpad,
144 GST_DEBUG_FUNCPTR (gst_amrnbparse_query));
145 gst_pad_set_query_type_function (amrnbparse->srcpad,
146 GST_DEBUG_FUNCPTR (gst_amrnbparse_querytypes));
147 gst_element_add_pad (GST_ELEMENT (amrnbparse), amrnbparse->srcpad);
149 amrnbparse->adapter = gst_adapter_new ();
151 /* init rest */
152 amrnbparse->ts = 0;
153 }
155 /*
156 * Position querying.
157 */
159 #if 0
160 static const GstFormat *
161 gst_amrnbparse_formats (GstPad * pad)
162 {
163 static const GstFormat list[] = {
164 GST_FORMAT_TIME,
165 0
166 };
168 return list;
169 }
170 #endif
172 static const GstQueryType *
173 gst_amrnbparse_querytypes (GstPad * pad)
174 {
175 static const GstQueryType list[] = {
176 GST_QUERY_POSITION,
177 0
178 };
180 return list;
181 }
183 static gboolean
184 gst_amrnbparse_query (GstPad * pad, GstQuery * query)
185 {
186 GstAmrnbParse *amrnbparse;
187 gboolean res = TRUE;
189 amrnbparse = GST_AMRNBPARSE (GST_PAD_PARENT (pad));
191 switch (GST_QUERY_TYPE (query)) {
192 case GST_QUERY_POSITION:
193 {
194 GstFormat format;
195 gint64 cur, tot;
196 GstPad *peer;
198 gst_query_parse_position (query, &format, NULL, NULL);
200 if (format != GST_FORMAT_TIME)
201 return FALSE;
203 tot = -1;
205 peer = gst_pad_get_peer (amrnbparse->sinkpad);
206 if (peer) {
207 GstFormat pformat;
208 gint64 pcur, ptot;
210 pformat = GST_FORMAT_BYTES;
211 res = gst_pad_query_position (peer, &pformat, &pcur, &ptot);
212 gst_object_unref (GST_OBJECT (peer));
213 if (res) {
214 tot = amrnbparse->ts * ((gdouble) ptot / pcur);
215 }
216 }
217 cur = amrnbparse->ts;
219 gst_query_set_position (query, GST_FORMAT_TIME, cur, tot);
220 res = TRUE;
221 break;
222 }
223 default:
224 res = FALSE;
225 break;
226 }
227 return res;
228 }
231 /*
232 * Data reading.
233 */
234 static gboolean
235 gst_amrnbparse_event (GstPad * pad, GstEvent * event)
236 {
237 GstAmrnbParse *amrnbparse;
238 gboolean res;
240 amrnbparse = GST_AMRNBPARSE (GST_PAD_PARENT (pad));
242 GST_LOG ("handling event %d", GST_EVENT_TYPE (event));
244 switch (GST_EVENT_TYPE (event)) {
245 case GST_EVENT_EOS:
246 case GST_EVENT_DISCONTINUOUS:
247 default:
248 break;
249 }
251 res = gst_pad_event_default (amrnbparse->sinkpad, event);
253 return res;
254 }
256 /* streaming mode */
257 static GstFlowReturn
258 gst_amrnbparse_chain (GstPad * pad, GstBuffer * buffer)
259 {
260 GstAmrnbParse *amrnbparse;
261 GstFlowReturn res;
262 gint block, mode;
263 const guint8 *data;
264 GstBuffer *out;
266 amrnbparse = GST_AMRNBPARSE (GST_PAD_PARENT (pad));
268 gst_adapter_push (amrnbparse->adapter, buffer);
270 res = GST_FLOW_OK;
272 /* init */
273 if (amrnbparse->need_header) {
275 if (gst_adapter_available (amrnbparse->adapter) < 6)
276 goto done;
278 data = gst_adapter_peek (amrnbparse->adapter, 6);
279 if (memcmp (data, "#!AMR\n", 6) != 0)
280 goto done;
282 gst_adapter_flush (amrnbparse->adapter, 6);
284 amrnbparse->need_header = FALSE;
285 }
287 while (TRUE) {
288 if (gst_adapter_available (amrnbparse->adapter) < 1)
289 break;
290 data = gst_adapter_peek (amrnbparse->adapter, 1);
292 /* get size */
293 mode = (data[0] >> 3) & 0x0F;
294 block = block_size[mode] + 1; /* add one for the mode */
296 if (gst_adapter_available (amrnbparse->adapter) < block)
297 break;
299 out = gst_buffer_new_and_alloc (block);
301 data = gst_adapter_peek (amrnbparse->adapter, block);
302 memcpy (GST_BUFFER_DATA (out), data, block);
304 /* output */
305 GST_BUFFER_DURATION (out) = GST_SECOND * 160 / 8000;
306 GST_BUFFER_TIMESTAMP (out) = amrnbparse->ts;
307 amrnbparse->ts += GST_BUFFER_DURATION (out);
308 gst_buffer_set_caps (out,
309 (GstCaps *) gst_pad_get_pad_template_caps (amrnbparse->srcpad));
311 GST_DEBUG ("Pushing %d bytes of data", block);
312 res = gst_pad_push (amrnbparse->srcpad, out);
314 gst_adapter_flush (amrnbparse->adapter, block);
315 }
316 done:
318 return res;
319 }
321 static gboolean
322 gst_amrnbparse_read_header (GstAmrnbParse * amrnbparse)
323 {
324 GstBuffer *buffer;
325 GstFlowReturn ret;
326 gint8 *data;
327 gint size;
329 ret =
330 gst_pad_pull_range (amrnbparse->sinkpad, amrnbparse->offset, 6, &buffer);
331 if (ret != GST_FLOW_OK)
332 return FALSE;
334 data = GST_BUFFER_DATA (buffer);
335 size = GST_BUFFER_SIZE (buffer);
336 if (size < 6)
337 goto not_enough;
339 if (memcmp (data, "#!AMR\n", 6))
340 goto no_header;
342 amrnbparse->offset += 6;
344 return TRUE;
346 not_enough:
347 {
348 gst_buffer_unref (buffer);
349 return FALSE;
350 }
351 no_header:
352 {
353 gst_buffer_unref (buffer);
354 return FALSE;
355 }
356 }
358 /* random access mode, could just read a fixed size buffer and push it to
359 * the chain function but we don't... */
360 static void
361 gst_amrnbparse_loop (GstPad * pad)
362 {
363 GstAmrnbParse *amrnbparse;
364 GstBuffer *buffer;
365 guint8 *data;
366 gint size;
367 gint block, mode;
368 GstFlowReturn ret;
370 amrnbparse = GST_AMRNBPARSE (GST_PAD_PARENT (pad));
372 /* init */
373 if (amrnbparse->need_header) {
374 gboolean got_header;
376 got_header = gst_amrnbparse_read_header (amrnbparse);
377 if (!got_header) {
378 GST_LOG_OBJECT (amrnbparse, "could not read header");
379 goto need_pause;
380 }
381 amrnbparse->need_header = FALSE;
382 }
384 ret =
385 gst_pad_pull_range (amrnbparse->sinkpad, amrnbparse->offset, 1, &buffer);
386 if (ret != GST_FLOW_OK)
387 goto need_pause;
389 data = GST_BUFFER_DATA (buffer);
390 size = GST_BUFFER_SIZE (buffer);
392 /* EOS */
393 if (size < 1)
394 goto eos;
396 /* get size */
397 mode = (data[0] >> 3) & 0x0F;
398 block = block_size[mode] + 1; /* add one for the mode */
400 gst_buffer_unref (buffer);
402 ret =
403 gst_pad_pull_range (amrnbparse->sinkpad, amrnbparse->offset, block,
404 &buffer);
405 if (ret != GST_FLOW_OK)
406 goto need_pause;
408 amrnbparse->offset += block;
410 /* output */
411 GST_BUFFER_DURATION (buffer) = GST_SECOND * 160 / 8000;
412 GST_BUFFER_TIMESTAMP (buffer) = amrnbparse->ts;
413 amrnbparse->ts += GST_BUFFER_DURATION (buffer);
414 gst_buffer_set_caps (buffer,
415 (GstCaps *) gst_pad_get_pad_template_caps (amrnbparse->srcpad));
417 GST_DEBUG ("Pushing %d bytes of data", block);
418 ret = gst_pad_push (amrnbparse->srcpad, buffer);
419 if (ret != GST_FLOW_OK)
420 goto need_pause;
422 return;
424 need_pause:
425 {
426 GST_LOG_OBJECT (amrnbparse, "pausing task");
427 gst_pad_pause_task (pad);
428 return;
429 }
430 eos:
431 {
432 GST_LOG_OBJECT (amrnbparse, "pausing task");
433 gst_pad_push_event (amrnbparse->srcpad, gst_event_new (GST_EVENT_EOS));
434 gst_pad_pause_task (pad);
435 return;
436 }
437 }
439 static gboolean
440 gst_amrnbparse_sink_activate (GstPad * sinkpad)
441 {
442 gboolean result = FALSE;
443 GstAmrnbParse *amrnbparse;
444 GstActivateMode mode;
446 amrnbparse = GST_AMRNBPARSE (GST_OBJECT_PARENT (sinkpad));
448 GST_LOCK (sinkpad);
449 mode = GST_PAD_ACTIVATE_MODE (sinkpad);
450 GST_UNLOCK (sinkpad);
452 switch (mode) {
453 case GST_ACTIVATE_PUSH:
454 amrnbparse->seekable = FALSE;
455 result = TRUE;
456 break;
457 case GST_ACTIVATE_PULL:
458 /*gst_pad_peer_set_active (sinkpad, mode); */
460 amrnbparse->need_header = TRUE;
461 amrnbparse->seekable = TRUE;
462 amrnbparse->ts = 0;
464 result = gst_pad_start_task (sinkpad,
465 (GstTaskFunction) gst_amrnbparse_loop, sinkpad);
466 break;
467 case GST_ACTIVATE_NONE:
468 /* step 1, unblock clock sync (if any) */
470 /* step 2, make sure streaming finishes */
471 result = gst_pad_stop_task (sinkpad);
472 break;
473 }
474 return result;
475 }
477 static GstElementStateReturn
478 gst_amrnbparse_state_change (GstElement * element)
479 {
480 GstAmrnbParse *amrnbparse;
481 gint transition;
482 GstElementStateReturn ret;
484 amrnbparse = GST_AMRNBPARSE (element);
485 transition = GST_STATE_TRANSITION (element);
487 switch (transition) {
488 case GST_STATE_NULL_TO_READY:
489 break;
490 case GST_STATE_READY_TO_PAUSED:
491 break;
492 default:
493 break;
494 }
496 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element);
498 switch (transition) {
499 case GST_STATE_PAUSED_TO_READY:
500 break;
501 case GST_STATE_READY_TO_NULL:
502 break;
503 default:
504 break;
505 }
507 return ret;
508 }