]> Gitweb @ Texas Instruments - Open Source Git Repositories - git.TI.com/gitweb - glsdk/gst-plugins-ugly0-10.git/blob - gst/dvdlpcmdec/gstdvdlpcmdec.c
Update for alloc_buffer changes.
[glsdk/gst-plugins-ugly0-10.git] / gst / dvdlpcmdec / gstdvdlpcmdec.c
1 /* GStreamer
2  * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3  * Copyright (C) <2005> Jan Schmidt <jan@noraisin.net>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20 /* Element-Checklist-Version: TODO */
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25 #include <stdlib.h>
26 #include <string.h>
28 #include "gstdvdlpcmdec.h"
30 GST_DEBUG_CATEGORY_STATIC (dvdlpcm_debug);
31 #define GST_CAT_DEFAULT dvdlpcm_debug
33 /* elementfactory information */
34 static GstElementDetails gst_dvdlpcmdec_details =
35 GST_ELEMENT_DETAILS ("DVD LPCM Audio decoder",
36     "Codec/Demuxer/Audio",
37     "Decode DVD LPCM frames into standard PCM audio",
38     "Jan Schmidt <jan@noraisin.net>\n" "Michael Smith <msmith@fluendo.com>");
40 static GstStaticPadTemplate gst_dvdlpcmdec_sink_template =
41     GST_STATIC_PAD_TEMPLATE ("sink",
42     GST_PAD_SINK,
43     GST_PAD_ALWAYS,
44     GST_STATIC_CAPS ("audio/x-private1-lpcm; "
45         "audio/x-lpcm, "
46         "width = (int) { 16, 20, 24 }, "
47         "rate = (int) { 48000, 96000 }, "
48         "channels = (int) [ 1, 8 ], "
49         "dynamic_range = (int) [ 0, 255 ], "
50         "emphasis = (boolean) { TRUE, FALSE }, "
51         "mute = (boolean) { TRUE, FALSE } ")
52     );
54 static GstStaticPadTemplate gst_dvdlpcmdec_src_template =
55 GST_STATIC_PAD_TEMPLATE ("src",
56     GST_PAD_SRC,
57     GST_PAD_ALWAYS,
58     GST_STATIC_CAPS ("audio/x-raw-int, "
59         "width = (int) { 16, 24 }, "
60         "rate = (int) { 48000, 96000 }, "
61         "channels = (int) [ 1, 8 ], "
62         "endianness = (int) { BIG_ENDIAN }, "
63         "depth = (int) { 16, 24 }, " "signed = (boolean) { true }")
64     );
66 /* DvdLpcmDec signals and args */
67 enum
68 {
69   /* FILL ME */
70   LAST_SIGNAL
71 };
73 enum
74 {
75   ARG_0
76       /* FILL ME */
77 };
79 static void gst_dvdlpcmdec_base_init (gpointer g_class);
80 static void gst_dvdlpcmdec_class_init (GstDvdLpcmDecClass * klass);
81 static void gst_dvdlpcmdec_init (GstDvdLpcmDec * dvdlpcmdec);
83 static GstFlowReturn gst_dvdlpcmdec_chain_raw (GstPad * pad,
84     GstBuffer * buffer);
85 static GstFlowReturn gst_dvdlpcmdec_chain_dvd (GstPad * pad,
86     GstBuffer * buffer);
87 static gboolean gst_dvdlpcmdec_setcaps (GstPad * pad, GstCaps * caps);
89 static GstStateChangeReturn gst_dvdlpcmdec_change_state (GstElement * element,
90     GstStateChange transition);
92 static GstElementClass *parent_class = NULL;
94 GType
95 gst_dvdlpcmdec_get_type (void)
96 {
97   static GType dvdlpcmdec_type = 0;
99   if (!dvdlpcmdec_type) {
100     static const GTypeInfo dvdlpcmdec_info = {
101       sizeof (GstDvdLpcmDecClass),
102       gst_dvdlpcmdec_base_init,
103       NULL,
104       (GClassInitFunc) gst_dvdlpcmdec_class_init,
105       NULL,
106       NULL,
107       sizeof (GstDvdLpcmDec),
108       0,
109       (GInstanceInitFunc) gst_dvdlpcmdec_init,
110     };
112     dvdlpcmdec_type =
113         g_type_register_static (GST_TYPE_ELEMENT, "GstDvdLpcmDec",
114         &dvdlpcmdec_info, 0);
115   }
116   return dvdlpcmdec_type;
119 static void
120 gst_dvdlpcmdec_base_init (gpointer g_class)
122   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
124   gst_element_class_add_pad_template (element_class,
125       gst_static_pad_template_get (&gst_dvdlpcmdec_sink_template));
126   gst_element_class_add_pad_template (element_class,
127       gst_static_pad_template_get (&gst_dvdlpcmdec_src_template));
128   gst_element_class_set_details (element_class, &gst_dvdlpcmdec_details);
131 static void
132 gst_dvdlpcmdec_class_init (GstDvdLpcmDecClass * klass)
134   GstElementClass *gstelement_class;
136   gstelement_class = (GstElementClass *) klass;
138   parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
140   gstelement_class->change_state = gst_dvdlpcmdec_change_state;
143 static void
144 gst_dvdlpcm_reset (GstDvdLpcmDec * dvdlpcmdec)
146   dvdlpcmdec->rate = 0;
147   dvdlpcmdec->channels = 0;
148   dvdlpcmdec->width = 0;
149   dvdlpcmdec->out_width = 0;
150   dvdlpcmdec->dynamic_range = 0;
151   dvdlpcmdec->emphasis = FALSE;
152   dvdlpcmdec->mute = FALSE;
153   dvdlpcmdec->timestamp = 0;
155   dvdlpcmdec->header = 0;
157   GST_LOG_OBJECT (dvdlpcmdec, "Setting NULL caps on src pad");
158   gst_pad_set_caps (dvdlpcmdec->srcpad, NULL);
161 static void
162 gst_dvdlpcmdec_init (GstDvdLpcmDec * dvdlpcmdec)
164   dvdlpcmdec->sinkpad =
165       gst_pad_new_from_template (gst_static_pad_template_get
166       (&gst_dvdlpcmdec_sink_template), "sink");
167   gst_pad_set_setcaps_function (dvdlpcmdec->sinkpad, gst_dvdlpcmdec_setcaps);
168   gst_element_add_pad (GST_ELEMENT (dvdlpcmdec), dvdlpcmdec->sinkpad);
170   dvdlpcmdec->srcpad =
171       gst_pad_new_from_template (gst_static_pad_template_get
172       (&gst_dvdlpcmdec_src_template), "src");
173   gst_pad_use_fixed_caps (dvdlpcmdec->srcpad);
174   gst_element_add_pad (GST_ELEMENT (dvdlpcmdec), dvdlpcmdec->srcpad);
176   gst_dvdlpcm_reset (dvdlpcmdec);
179 static gboolean
180 gst_dvdlpcmdec_setcaps (GstPad * pad, GstCaps * caps)
182   GstStructure *structure;
183   gboolean res = TRUE;
184   GstDvdLpcmDec *dvdlpcmdec;
185   GstCaps *src_caps;
187   g_return_val_if_fail (caps != NULL, FALSE);
188   g_return_val_if_fail (pad != NULL, FALSE);
190   dvdlpcmdec = GST_DVDLPCMDEC (gst_pad_get_parent (pad));
192   structure = gst_caps_get_structure (caps, 0);
194   /* If we have the DVD structured LPCM (including header) */
195   if (gst_structure_has_name (structure, "audio/x-private1-lpcm")) {
196     gst_pad_set_chain_function (dvdlpcmdec->sinkpad, gst_dvdlpcmdec_chain_dvd);
197     goto done;
198   }
200   gst_pad_set_chain_function (dvdlpcmdec->sinkpad, gst_dvdlpcmdec_chain_raw);
202   res &= gst_structure_get_int (structure, "rate", &dvdlpcmdec->rate);
203   res &= gst_structure_get_int (structure, "channels", &dvdlpcmdec->channels);
204   res &= gst_structure_get_int (structure, "width", &dvdlpcmdec->width);
205   res &= gst_structure_get_int (structure, "dynamic_range",
206       &dvdlpcmdec->dynamic_range);
207   res &= gst_structure_get_boolean (structure, "emphasis",
208       &dvdlpcmdec->emphasis);
209   res &= gst_structure_get_boolean (structure, "mute", &dvdlpcmdec->mute);
211   if (!res)
212     goto caps_parse_error;
214   /* Output width is the input width rounded up to the nearest byte */
215   if (dvdlpcmdec->width == 20)
216     dvdlpcmdec->out_width = 24;
217   else
218     dvdlpcmdec->out_width = dvdlpcmdec->width;
220   /* Build caps to set on the src pad, which we know from the incoming caps */
221   src_caps = gst_caps_new_simple ("audio/x-raw-int",
222       "rate", G_TYPE_INT, dvdlpcmdec->rate,
223       "channels", G_TYPE_INT, dvdlpcmdec->channels,
224       "endianness", G_TYPE_INT, G_BIG_ENDIAN,
225       "depth", G_TYPE_INT, dvdlpcmdec->out_width,
226       "width", G_TYPE_INT, dvdlpcmdec->out_width,
227       "signed", G_TYPE_BOOLEAN, TRUE, NULL);
229   GST_DEBUG_OBJECT (dvdlpcmdec, "Set rate %d, channels %d, width %d (out %d)",
230       dvdlpcmdec->rate, dvdlpcmdec->channels, dvdlpcmdec->width,
231       dvdlpcmdec->out_width);
233   if (!gst_pad_set_caps (dvdlpcmdec->srcpad, src_caps)) {
234     GST_DEBUG_OBJECT (dvdlpcmdec, "Failed to set caps!");
235     res = FALSE;
236   } else {
237     GST_DEBUG_OBJECT (dvdlpcmdec, "Successfully set caps: %" GST_PTR_FORMAT,
238         caps);
239   }
241   gst_caps_unref (src_caps);
243 done:
244   gst_object_unref (dvdlpcmdec);
246   return res;
248   /* ERRORS */
249 caps_parse_error:
250   {
251     GST_DEBUG_OBJECT (dvdlpcmdec, "Couldn't get parameters; missing caps?");
252     res = FALSE;
253     goto done;
254   }
257 static void
258 update_timestamps (GstDvdLpcmDec * dvdlpcmdec, GstBuffer * buf, int samples)
260   GST_BUFFER_DURATION (buf) = samples * GST_SECOND / dvdlpcmdec->rate;
261   if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
262     /* Then leave it as-is, and save this timestamp */
263     dvdlpcmdec->timestamp = GST_BUFFER_TIMESTAMP (buf);
264   } else {
265     dvdlpcmdec->timestamp += GST_BUFFER_DURATION (buf);
266     GST_BUFFER_TIMESTAMP (buf) = dvdlpcmdec->timestamp;
267   }
269   GST_LOG_OBJECT (dvdlpcmdec, "Updated timestamp to %" GST_TIME_FORMAT,
270       GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
273 static void
274 parse_header (GstDvdLpcmDec * dec, guint32 header)
276   /* We don't actually use 'dynamic range', 'mute', or 'emphasis' currently, 
277    * but parse them out */
278   dec->dynamic_range = header & 0xff;
280   dec->mute = (header & 0x400000) != 0;
281   dec->emphasis = (header & 0x800000) != 0;
283   /* These two bits tell us the bit depth */
284   switch (header & 0xC000) {
285     case 0x8000:
286       dec->width = 24;
287       dec->out_width = 24;
288       break;
289     case 0x4000:
290       dec->width = 20;
291       dec->out_width = 24;
292       break;
293     default:
294       dec->width = 16;
295       dec->out_width = 16;
296       break;
297   }
299   /* Only two sample rates supported */
300   if (header & 0x1000)
301     dec->rate = 96000;
302   else
303     dec->rate = 48000;
305   /* And, of course, the number of channels (up to 8) */
306   dec->channels = ((header >> 8) & 0x7) + 1;
309 static GstFlowReturn
310 gst_dvdlpcmdec_chain_dvd (GstPad * pad, GstBuffer * buf)
312   GstDvdLpcmDec *dvdlpcmdec;
313   guint8 *data;
314   guint size;
315   guint first_access;
316   guint32 header;
317   GstBuffer *subbuf;
318   GstFlowReturn ret;
319   gint off, len;
321   dvdlpcmdec = GST_DVDLPCMDEC (gst_pad_get_parent (pad));
323   size = GST_BUFFER_SIZE (buf);
324   data = GST_BUFFER_DATA (buf);
326   /* We have a 5 byte header, now.
327    * The first two bytes are a (big endian) 16 bit offset into our buffer.
328    * The buffer timestamp refers to this offset.
329    *
330    * The other three bytes are a (big endian) number in which the header is
331    * encoded.
332    */
333   first_access = (data[0] << 8) | data[1];
334   header = (data[2] << 16) | (data[3] << 8) | data[4];
336   /* see if we have a new header */
337   if (header != dvdlpcmdec->header) {
338     GstCaps *src_caps;
340     parse_header (dvdlpcmdec, header);
342     /* Build caps to set on the src pad from what we've just parsed */
343     src_caps = gst_caps_new_simple ("audio/x-raw-int",
344         "rate", G_TYPE_INT, dvdlpcmdec->rate,
345         "channels", G_TYPE_INT, dvdlpcmdec->channels,
346         "endianness", G_TYPE_INT, G_BIG_ENDIAN,
347         "depth", G_TYPE_INT, dvdlpcmdec->out_width,
348         "width", G_TYPE_INT, dvdlpcmdec->out_width,
349         "signed", G_TYPE_BOOLEAN, TRUE, NULL);
351     GST_DEBUG_OBJECT (dvdlpcmdec, "Set rate %d, channels %d, width %d",
352         dvdlpcmdec->rate, dvdlpcmdec->channels, dvdlpcmdec->width);
354     if (!gst_pad_set_caps (dvdlpcmdec->srcpad, src_caps))
355       goto negotiation_failed;
357     gst_caps_unref (src_caps);
359     dvdlpcmdec->header = header;
360   }
362   GST_LOG_OBJECT (dvdlpcmdec, "first_access %d, buffer length %d", first_access,
363       size);
365   /* After first_access, we have an additional 3 bytes of data we've parsed and
366    * don't want to handle; this is included within the value of first_access.
367    * So a first_access value of between 1 and 3 is just broken, we treat that
368    * the same as zero. first_access == 4 means we only need to create a single
369    * sub-buffer, greater than that we need to create two. */
371   /* skip access unit bytes and info */
372   off = 5;
374   if (first_access > 4) {
375     /* length of first blength before access unit */
376     len = first_access - 4;
378     GST_LOG_OBJECT (dvdlpcmdec, "Creating first sub-buffer off %d, len %d", off,
379         len);
381     /* see if we need a subbuffer without timestamp */
382     if (len > 0) {
383       subbuf = gst_buffer_create_sub (buf, off, len);
384       GST_BUFFER_TIMESTAMP (subbuf) = GST_CLOCK_TIME_NONE;
385       ret = gst_dvdlpcmdec_chain_raw (pad, subbuf);
386       if (ret != GST_FLOW_OK)
387         goto done;
388     }
390     /* then the buffer with new timestamp */
391     off += len;
392     len = size - off;
394     GST_LOG_OBJECT (dvdlpcmdec, "Creating next sub-buffer off %d, len %d", off,
395         len);
397     subbuf = gst_buffer_create_sub (buf, off, len);
398     GST_BUFFER_TIMESTAMP (subbuf) = GST_BUFFER_TIMESTAMP (buf);
400     ret = gst_dvdlpcmdec_chain_raw (pad, subbuf);
401   } else {
402     GST_LOG_OBJECT (dvdlpcmdec, "Creating single sub-buffer off %d, len %d",
403         off, size - off);
404     /* We don't have a valid timestamp at all */
405     subbuf = gst_buffer_create_sub (buf, off, size - off);
406     GST_BUFFER_TIMESTAMP (subbuf) = GST_CLOCK_TIME_NONE;
407     ret = gst_dvdlpcmdec_chain_raw (pad, subbuf);
408   }
410 done:
411   gst_buffer_unref (buf);
412   gst_object_unref (dvdlpcmdec);
414   return ret;
416   /* ERRORS */
417 negotiation_failed:
418   {
419     GST_DEBUG_OBJECT (dvdlpcmdec, "Failed to set caps!");
420     GST_DEBUG_OBJECT (dvdlpcmdec, "Couldn't negotiate caps on src pad");
421     ret = GST_FLOW_ERROR;
422     goto done;
423   }
426 static GstFlowReturn
427 gst_dvdlpcmdec_chain_raw (GstPad * pad, GstBuffer * buf)
429   GstDvdLpcmDec *dvdlpcmdec;
430   guchar *data;
431   guint size;
432   GstFlowReturn ret;
433   guint samples = 0;
435   dvdlpcmdec = GST_DVDLPCMDEC (gst_pad_get_parent (pad));
437   size = GST_BUFFER_SIZE (buf);
438   data = GST_BUFFER_DATA (buf);
440   GST_LOG_OBJECT (dvdlpcmdec, "got buffer %p of size %d", buf, size);
442   if (dvdlpcmdec->rate == 0)
443     goto not_negotiated;
445   /* We don't currently do anything at all regarding emphasis, mute or 
446    * dynamic_range - I'm not sure what they're for */
447   switch (dvdlpcmdec->width) {
448     case 16:
449     {
450       /* We can just pass 16-bits straight through intact, once we set 
451        * appropriate things on the buffer */
452       samples = size / dvdlpcmdec->channels / 2;
453       buf = gst_buffer_make_writable (buf);
454       break;
455     }
456     case 20:
457     {
458       /* Allocate a new buffer and copy 20-bit width to 24-bit */
459       gint64 samples = size * 8 / 20;
460       gint64 count = size / 10;
461       gint64 i;
462       guchar *src;
463       guchar *dest;
464       GstBuffer *outbuf;
465       GstCaps *bufcaps = GST_PAD_CAPS (dvdlpcmdec->srcpad);
467       ret = gst_pad_alloc_buffer_and_set_caps (dvdlpcmdec->srcpad, 0,
468           samples * 3, bufcaps, &outbuf);
470       if (ret != GST_FLOW_OK)
471         goto buffer_alloc_failed;
473       gst_buffer_stamp (outbuf, buf);
475       /* adjust samples so we can calc the new timestamp */
476       samples = samples / dvdlpcmdec->channels;
478       src = data;
479       dest = GST_BUFFER_DATA (outbuf);
481       /* Copy 20-bit LPCM format to 24-bit buffers, with 0x00 in the lowest 
482        * nibble. Note that the first 2 bytes are already correct */
483       for (i = 0; i < count; i++) {
484         dest[0] = src[0];
485         dest[1] = src[1];
486         dest[2] = src[8] & 0xf0;
487         dest[3] = src[2];
488         dest[4] = src[3];
489         dest[5] = (src[8] & 0x0f) << 4;
490         dest[6] = src[4];
491         dest[7] = src[5];
492         dest[8] = src[9] & 0x0f;
493         dest[9] = src[6];
494         dest[10] = src[7];
495         dest[11] = (src[9] & 0x0f) << 4;
497         src += 10;
498         dest += 12;
499       }
501       gst_buffer_unref (buf);
502       buf = outbuf;
503       break;
504     }
505     case 24:
506     {
507       /* Rearrange 24-bit LPCM format in-place. Note that the first 2
508        * and last byte are already correct */
509       guint count = size / 12;
510       gint i;
511       guint8 *src;
513       samples = size / dvdlpcmdec->channels / 3;
515       /* Ensure our output buffer is writable */
516       buf = gst_buffer_make_writable (buf);
518       src = GST_BUFFER_DATA (buf);
519       for (i = 0; i < count; i++) {
520         guchar temp[9];
522         temp[0] = src[8];
523         temp[1] = src[2];
524         temp[2] = src[3];
525         temp[3] = src[9];
526         temp[4] = src[4];
527         temp[5] = src[5];
528         temp[6] = src[10];
529         temp[7] = src[6];
530         temp[8] = src[7];
532         memcpy (src + 2, temp, 9);
533         src += 12;
534       }
535       break;
536     }
537     default:
538       goto invalid_width;
539   }
541   /* Set appropriate caps on it to pass downstream */
542   gst_buffer_set_caps (buf, GST_PAD_CAPS (dvdlpcmdec->srcpad));
543   update_timestamps (dvdlpcmdec, buf, samples);
545   ret = gst_pad_push (dvdlpcmdec->srcpad, buf);
547 done:
548   gst_object_unref (dvdlpcmdec);
550   return ret;
552   /* ERRORS */
553 not_negotiated:
554   {
555     GST_ELEMENT_ERROR (dvdlpcmdec, STREAM, FORMAT, (NULL),
556         ("Buffer pushed before negotiation"));
557     gst_buffer_unref (buf);
558     ret = GST_FLOW_NOT_NEGOTIATED;
559     goto done;
560   }
561 buffer_alloc_failed:
562   {
563     GST_ELEMENT_ERROR (dvdlpcmdec, RESOURCE, FAILED, (NULL),
564         ("Buffer allocation failed"));
565     gst_buffer_unref (buf);
566     goto done;
567   }
568 invalid_width:
569   {
570     GST_ELEMENT_ERROR (dvdlpcmdec, STREAM, WRONG_TYPE, (NULL),
571         ("Invalid sample width configured"));
572     gst_buffer_unref (buf);
573     ret = GST_FLOW_NOT_NEGOTIATED;
574     goto done;
575   }
578 static GstStateChangeReturn
579 gst_dvdlpcmdec_change_state (GstElement * element, GstStateChange transition)
581   GstDvdLpcmDec *dvdlpcmdec = GST_DVDLPCMDEC (element);
582   GstStateChangeReturn res;
584   switch (transition) {
585     case GST_STATE_CHANGE_READY_TO_PAUSED:
586       gst_dvdlpcm_reset (dvdlpcmdec);
587       break;
588     default:
589       break;
590   }
592   res = parent_class->change_state (element, transition);
594   switch (transition) {
595     default:
596       break;
597   }
599   return res;
602 static gboolean
603 plugin_init (GstPlugin * plugin)
605   GST_DEBUG_CATEGORY_INIT (dvdlpcm_debug, "dvdlpcmdec", 0, "DVD LPCM Decoder");
607   if (!gst_element_register (plugin, "dvdlpcmdec", GST_RANK_PRIMARY,
608           GST_TYPE_DVDLPCMDEC)) {
609     return FALSE;
610   }
612   return TRUE;
615 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
616     GST_VERSION_MINOR,
617     "dvdlpcmdec",
618     "Decode DVD LPCM frames into standard PCM",
619     plugin_init, VERSION, "LGPL", GST_PACKAGE, GST_ORIGIN)