Merging gst-plugins-ugly
[platform/upstream/gstreamer.git] / subprojects / gst-plugins-ugly / 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., 51 Franklin St, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20 /* Element-Checklist-Version: TODO */
21
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25 #include <stdlib.h>
26 #include <string.h>
27
28 #include "gstdvdlpcmdec.h"
29 #include <gst/audio/audio.h>
30
31 GST_DEBUG_CATEGORY_STATIC (dvdlpcm_debug);
32 #define GST_CAT_DEFAULT dvdlpcm_debug
33
34 static GstStaticPadTemplate gst_dvdlpcmdec_sink_template =
35     GST_STATIC_PAD_TEMPLATE ("sink",
36     GST_PAD_SINK,
37     GST_PAD_ALWAYS,
38     GST_STATIC_CAPS ("audio/x-private1-lpcm; "
39         "audio/x-private2-lpcm; "
40         "audio/x-private-ts-lpcm; "
41         "audio/x-lpcm, "
42         "width = (int) { 16, 20, 24 }, "
43         "rate = (int) { 32000, 44100, 48000, 96000 }, "
44         "channels = (int) [ 1, 8 ], "
45         "dynamic_range = (int) [ 0, 255 ], "
46         "emphasis = (boolean) { TRUE, FALSE }, "
47         "mute = (boolean) { TRUE, FALSE } ")
48     );
49
50 static GstStaticPadTemplate gst_dvdlpcmdec_src_template =
51 GST_STATIC_PAD_TEMPLATE ("src",
52     GST_PAD_SRC,
53     GST_PAD_ALWAYS,
54     GST_STATIC_CAPS ("audio/x-raw, "
55         "format = (string) { S16BE, S24BE }, "
56         "layout = (string) interleaved, "
57         "rate = (int) { 32000, 44100, 48000, 96000 }, "
58         "channels = (int) [ 1, 8 ]")
59     );
60
61 #define gst_dvdlpcmdec_parent_class parent_class
62 G_DEFINE_TYPE (GstDvdLpcmDec, gst_dvdlpcmdec, GST_TYPE_AUDIO_DECODER);
63 GST_ELEMENT_REGISTER_DEFINE (dvdlpcmdec, "dvdlpcmdec", GST_RANK_PRIMARY,
64     GST_TYPE_DVDLPCMDEC);
65
66 static gboolean gst_dvdlpcmdec_set_format (GstAudioDecoder * bdec,
67     GstCaps * caps);
68 static GstFlowReturn gst_dvdlpcmdec_parse (GstAudioDecoder * bdec,
69     GstAdapter * adapter, gint * offset, gint * len);
70 static GstFlowReturn gst_dvdlpcmdec_handle_frame (GstAudioDecoder * bdec,
71     GstBuffer * buffer);
72 static GstFlowReturn gst_dvdlpcmdec_chain (GstPad * pad, GstObject * parent,
73     GstBuffer * buffer);
74
75
76 static void
77 gst_dvdlpcmdec_class_init (GstDvdLpcmDecClass * klass)
78 {
79   GstElementClass *element_class;
80   GstAudioDecoderClass *gstbase_class;
81
82   element_class = (GstElementClass *) klass;
83   gstbase_class = (GstAudioDecoderClass *) klass;
84
85   gstbase_class->set_format = GST_DEBUG_FUNCPTR (gst_dvdlpcmdec_set_format);
86   gstbase_class->parse = GST_DEBUG_FUNCPTR (gst_dvdlpcmdec_parse);
87   gstbase_class->handle_frame = GST_DEBUG_FUNCPTR (gst_dvdlpcmdec_handle_frame);
88
89   gst_element_class_add_static_pad_template (element_class,
90       &gst_dvdlpcmdec_sink_template);
91   gst_element_class_add_static_pad_template (element_class,
92       &gst_dvdlpcmdec_src_template);
93   gst_element_class_set_static_metadata (element_class,
94       "DVD LPCM Audio decoder", "Codec/Decoder/Audio",
95       "Decode DVD LPCM frames into standard PCM audio",
96       "Jan Schmidt <jan@noraisin.net>, Michael Smith <msmith@fluendo.com>");
97
98   GST_DEBUG_CATEGORY_INIT (dvdlpcm_debug, "dvdlpcmdec", 0, "DVD LPCM Decoder");
99 }
100
101 static void
102 gst_dvdlpcm_reset (GstDvdLpcmDec * dvdlpcmdec)
103 {
104   gst_audio_info_init (&dvdlpcmdec->info);
105   dvdlpcmdec->dynamic_range = 0;
106   dvdlpcmdec->emphasis = FALSE;
107   dvdlpcmdec->mute = FALSE;
108
109   dvdlpcmdec->header = 0;
110
111   dvdlpcmdec->mode = GST_LPCM_UNKNOWN;
112 }
113
114 static void
115 gst_dvdlpcmdec_init (GstDvdLpcmDec * dvdlpcmdec)
116 {
117   gst_dvdlpcm_reset (dvdlpcmdec);
118
119   gst_audio_decoder_set_use_default_pad_acceptcaps (GST_AUDIO_DECODER_CAST
120       (dvdlpcmdec), TRUE);
121   GST_PAD_SET_ACCEPT_TEMPLATE (GST_AUDIO_DECODER_SINK_PAD (dvdlpcmdec));
122
123   /* retrieve and intercept base class chain.
124    * Quite HACKish, but that's dvd specs/caps for you,
125    * since one buffer needs to be split into 2 frames */
126   dvdlpcmdec->base_chain =
127       GST_PAD_CHAINFUNC (GST_AUDIO_DECODER_SINK_PAD (dvdlpcmdec));
128   gst_pad_set_chain_function (GST_AUDIO_DECODER_SINK_PAD (dvdlpcmdec),
129       GST_DEBUG_FUNCPTR (gst_dvdlpcmdec_chain));
130 }
131
132 static const GstAudioChannelPosition channel_positions[][8] = {
133   {GST_AUDIO_CHANNEL_POSITION_MONO},
134   {GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
135       GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT},
136   {GST_AUDIO_CHANNEL_POSITION_INVALID},
137   {GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
138         GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
139         GST_AUDIO_CHANNEL_POSITION_REAR_LEFT,
140       GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT},
141   {GST_AUDIO_CHANNEL_POSITION_INVALID},
142   {GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
143         GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
144         GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER,
145         GST_AUDIO_CHANNEL_POSITION_LFE1, GST_AUDIO_CHANNEL_POSITION_REAR_LEFT,
146       GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT},
147   {GST_AUDIO_CHANNEL_POSITION_INVALID},
148   {GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
149         GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
150         GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER,
151         GST_AUDIO_CHANNEL_POSITION_LFE1, GST_AUDIO_CHANNEL_POSITION_REAR_LEFT,
152         GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT,
153         GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT,
154       GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT},
155   {GST_AUDIO_CHANNEL_POSITION_INVALID}
156 };
157
158 static const GstAudioChannelPosition bluray_channel_positions[][8] = {
159   /* invalid */
160   {GST_AUDIO_CHANNEL_POSITION_INVALID},
161   /* mono */
162   {GST_AUDIO_CHANNEL_POSITION_MONO},
163   /* invalid */
164   {GST_AUDIO_CHANNEL_POSITION_INVALID},
165   /* stereo */
166   {GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
167       GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT},
168   /* surround */
169   {GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
170         GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
171       GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER},
172   /* 2.1 */
173   {GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
174         GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
175       GST_AUDIO_CHANNEL_POSITION_REAR_CENTER},
176   /* 4.0 */
177   {GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
178         GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
179         GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER,
180       GST_AUDIO_CHANNEL_POSITION_REAR_CENTER},
181   /* 2.2 */
182   {GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
183         GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
184         GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT,
185       GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT},
186   /* 5.0 */
187   {GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
188         GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
189         GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER,
190         GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT,
191       GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT},
192   /* 5.1 */
193   {GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
194         GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
195         GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER,
196         GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT,
197         GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT,
198       GST_AUDIO_CHANNEL_POSITION_LFE1},
199   /* 7.0 */
200   {GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
201         GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
202         GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER,
203         GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT,
204         GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT,
205         GST_AUDIO_CHANNEL_POSITION_REAR_LEFT,
206       GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT},
207   /* 7.1 */
208   {GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
209         GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
210         GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER,
211         GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT,
212         GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT,
213         GST_AUDIO_CHANNEL_POSITION_REAR_LEFT,
214         GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT,
215       GST_AUDIO_CHANNEL_POSITION_LFE1},
216   /* invalid */
217   {GST_AUDIO_CHANNEL_POSITION_INVALID},
218   /* invalid */
219   {GST_AUDIO_CHANNEL_POSITION_INVALID},
220   /* invalid */
221   {GST_AUDIO_CHANNEL_POSITION_INVALID},
222   /* invalid */
223   {GST_AUDIO_CHANNEL_POSITION_INVALID}
224 };
225
226 static void
227 gst_dvdlpcmdec_send_tags (GstDvdLpcmDec * dvdlpcmdec)
228 {
229   GstTagList *taglist;
230   guint bitrate;
231   gint bpf, rate;
232
233   bpf = GST_AUDIO_INFO_BPF (&dvdlpcmdec->info);
234   rate = GST_AUDIO_INFO_RATE (&dvdlpcmdec->info);
235
236   bitrate = bpf * 8 * rate;
237
238   taglist = gst_tag_list_new (GST_TAG_AUDIO_CODEC, "LPCM Audio",
239       GST_TAG_BITRATE, bitrate, NULL);
240
241   gst_audio_decoder_merge_tags (GST_AUDIO_DECODER (dvdlpcmdec), taglist,
242       GST_TAG_MERGE_REPLACE);
243   gst_tag_list_unref (taglist);
244 }
245
246 static gboolean
247 gst_dvdlpcmdec_set_output_format (GstDvdLpcmDec * dvdlpcmdec)
248 {
249   gboolean res = TRUE;
250
251   res = gst_audio_decoder_set_output_format (GST_AUDIO_DECODER (dvdlpcmdec),
252       &dvdlpcmdec->info);
253   if (res) {
254     GST_DEBUG_OBJECT (dvdlpcmdec, "Successfully set output format");
255
256     gst_dvdlpcmdec_send_tags (dvdlpcmdec);
257   } else {
258     GST_DEBUG_OBJECT (dvdlpcmdec, "Failed to set output format");
259   }
260
261   return res;
262 }
263
264 static void
265 gst_dvdlpcmdec_update_audio_formats (GstDvdLpcmDec * dec, gint channels,
266     gint rate, GstAudioFormat format, guint8 channel_indicator,
267     const GstAudioChannelPosition positions[][8])
268 {
269   GST_DEBUG_OBJECT (dec, "got channels = %d, rate = %d, format = %d", channels,
270       rate, format);
271
272   /* Reorder the channel positions and set the default into for the audio */
273   if (channels < 9
274       && positions[channel_indicator][0] !=
275       GST_AUDIO_CHANNEL_POSITION_INVALID) {
276     const GstAudioChannelPosition *position;
277     GstAudioChannelPosition sorted_position[8];
278     guint c;
279
280     position = positions[channel_indicator];
281     for (c = 0; c < channels; ++c)
282       sorted_position[c] = position[c];
283     gst_audio_channel_positions_to_valid_order (sorted_position, channels);
284     gst_audio_info_set_format (&dec->info, format, rate, channels,
285         sorted_position);
286     if (memcmp (position, sorted_position,
287             channels * sizeof (position[0])) != 0)
288       dec->lpcm_layout = position;
289     else
290       dec->lpcm_layout = NULL;
291   } else {
292     gst_audio_info_set_format (&dec->info, format, rate, channels, NULL);
293   }
294 }
295
296 static gboolean
297 gst_dvdlpcmdec_set_format (GstAudioDecoder * bdec, GstCaps * caps)
298 {
299   GstDvdLpcmDec *dvdlpcmdec = GST_DVDLPCMDEC (bdec);
300   GstStructure *structure;
301   gboolean res = TRUE;
302   GstAudioFormat format;
303   gint rate, channels, width;
304
305   gst_dvdlpcm_reset (dvdlpcmdec);
306
307   structure = gst_caps_get_structure (caps, 0);
308
309   /* If we have the DVD structured LPCM (including header) then we wait
310    * for incoming data before creating the output pad caps */
311   if (gst_structure_has_name (structure, "audio/x-private1-lpcm")) {
312     dvdlpcmdec->mode = GST_LPCM_DVD;
313     goto done;
314   }
315   if (gst_structure_has_name (structure, "audio/x-private2-lpcm")) {
316     dvdlpcmdec->mode = GST_LPCM_1394;
317     goto done;
318   }
319   if (gst_structure_has_name (structure, "audio/x-private-ts-lpcm")) {
320     dvdlpcmdec->mode = GST_LPCM_BLURAY;
321     goto done;
322   }
323
324   dvdlpcmdec->mode = GST_LPCM_RAW;
325
326   res &= gst_structure_get_int (structure, "rate", &rate);
327   res &= gst_structure_get_int (structure, "channels", &channels);
328   res &= gst_structure_get_int (structure, "width", &width);
329   res &= gst_structure_get_int (structure, "dynamic_range",
330       &dvdlpcmdec->dynamic_range);
331   res &= gst_structure_get_boolean (structure, "emphasis",
332       &dvdlpcmdec->emphasis);
333   res &= gst_structure_get_boolean (structure, "mute", &dvdlpcmdec->mute);
334
335   if (!res)
336     goto caps_parse_error;
337
338   switch (width) {
339     case 24:
340     case 20:
341       format = GST_AUDIO_FORMAT_S24BE;
342       break;
343     case 16:
344       format = GST_AUDIO_FORMAT_S16BE;
345       break;
346     default:
347       format = GST_AUDIO_FORMAT_UNKNOWN;
348       break;
349   }
350
351   gst_dvdlpcmdec_update_audio_formats (dvdlpcmdec, channels, rate, format,
352       channels - 1, channel_positions);
353
354   dvdlpcmdec->width = width;
355
356   res = gst_dvdlpcmdec_set_output_format (dvdlpcmdec);
357
358 done:
359   return res;
360
361   /* ERRORS */
362 caps_parse_error:
363   {
364     GST_DEBUG_OBJECT (dvdlpcmdec, "Couldn't get parameters; missing caps?");
365     return FALSE;
366   }
367 }
368
369 static void
370 parse_header (GstDvdLpcmDec * dec, guint32 header)
371 {
372   GstAudioFormat format;
373   gint rate, channels, width;
374
375   /* We don't actually use 'dynamic range', 'mute', or 'emphasis' currently,
376    * but parse them out */
377   dec->dynamic_range = header & 0xff;
378
379   dec->mute = (header & 0x400000) != 0;
380   dec->emphasis = (header & 0x800000) != 0;
381
382   /* These two bits tell us the bit depth */
383   switch (header & 0xC000) {
384     case 0x8000:
385       /* 24 bits in 3 bytes */
386       format = GST_AUDIO_FORMAT_S24BE;
387       width = 24;
388       break;
389     case 0x4000:
390       /* 20 bits in 3 bytes */
391       format = GST_AUDIO_FORMAT_S24BE;
392       width = 20;
393       break;
394     default:
395       format = GST_AUDIO_FORMAT_S16BE;
396       width = 16;
397       break;
398   }
399
400   dec->width = width;
401
402   /* Only four sample rates supported */
403   switch (header & 0x3000) {
404     case 0x0000:
405       rate = 48000;
406       break;
407     case 0x1000:
408       rate = 96000;
409       break;
410     case 0x2000:
411       rate = 44100;
412       break;
413     case 0x3000:
414       rate = 32000;
415       break;
416     default:
417       rate = 0;
418       break;
419   }
420
421   /* And, of course, the number of channels (up to 8) */
422   channels = ((header >> 8) & 0x7) + 1;
423
424   gst_dvdlpcmdec_update_audio_formats (dec, channels, rate, format,
425       channels - 1, channel_positions);
426 }
427
428 static GstFlowReturn
429 gst_dvdlpcmdec_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
430 {
431   GstDvdLpcmDec *dvdlpcmdec = GST_DVDLPCMDEC (parent);
432   guint8 data[2];
433   gsize size;
434   guint first_access;
435   GstBuffer *subbuf;
436   GstFlowReturn ret = GST_FLOW_OK;
437   gint off, len;
438
439   if (dvdlpcmdec->mode != GST_LPCM_DVD)
440     return dvdlpcmdec->base_chain (pad, parent, buf);
441
442   size = gst_buffer_get_size (buf);
443   if (size < 5)
444     goto too_small;
445
446   gst_buffer_extract (buf, 0, data, 2);
447   first_access = (data[0] << 8) | data[1];
448   if (first_access > size)
449     goto invalid_data;
450
451   /* After first_access, we have an additional 3 bytes of header data; this
452    * is included within the value of first_access.
453    * So a first_access value of between 1 and 3 is just broken, we treat that
454    * the same as zero. first_access == 4 means we only need to create a single
455    * sub-buffer, greater than that we need to create two. */
456
457   /* skip access unit bytes */
458   off = 2;
459
460   if (first_access > 4) {
461     /* length of first buffer */
462     len = first_access - 1;
463
464     GST_LOG_OBJECT (dvdlpcmdec, "Creating first sub-buffer off %d, len %d",
465         off, len);
466
467     /* see if we need a subbuffer without timestamp */
468     if (off + len > size)
469       goto bad_first_access;
470
471     subbuf = gst_buffer_copy_region (buf, GST_BUFFER_COPY_ALL, off, len);
472     GST_BUFFER_TIMESTAMP (subbuf) = GST_CLOCK_TIME_NONE;
473     ret = dvdlpcmdec->base_chain (pad, parent, subbuf);
474     if (ret != GST_FLOW_OK)
475       goto done;
476
477     /* then the buffer with new timestamp */
478     off += len;
479     len = size - off;
480
481     GST_LOG_OBJECT (dvdlpcmdec, "Creating next sub-buffer off %d, len %d", off,
482         len);
483
484     if (len > 0) {
485       GstMemory *header, *tmp;
486       subbuf = gst_buffer_copy_region (buf, GST_BUFFER_COPY_ALL, off, len);
487       tmp = gst_buffer_peek_memory (buf, 0);
488       header = gst_memory_copy (tmp, 2, 3);
489       gst_buffer_prepend_memory (subbuf, header);
490       GST_BUFFER_TIMESTAMP (subbuf) = GST_BUFFER_TIMESTAMP (buf);
491
492       ret = dvdlpcmdec->base_chain (pad, parent, subbuf);
493     }
494   } else {
495     GST_LOG_OBJECT (dvdlpcmdec,
496         "Creating single sub-buffer off %d, len %" G_GSIZE_FORMAT, off,
497         size - off);
498     subbuf = gst_buffer_copy_region (buf, GST_BUFFER_COPY_ALL, off, size - off);
499     GST_BUFFER_TIMESTAMP (subbuf) = GST_BUFFER_TIMESTAMP (buf);
500     ret = dvdlpcmdec->base_chain (pad, parent, subbuf);
501   }
502
503 done:
504   gst_buffer_unref (buf);
505
506   return ret;
507
508   /* ERRORS */
509 too_small:
510   {
511     /* Buffer is too small */
512     GST_ELEMENT_WARNING (dvdlpcmdec, STREAM, DECODE,
513         ("Invalid data found parsing LPCM packet"),
514         ("LPCM packet was too small. Dropping"));
515     ret = GST_FLOW_OK;
516     goto done;
517   }
518 invalid_data:
519   {
520     GST_ELEMENT_WARNING (dvdlpcmdec, STREAM, DECODE,
521         ("Invalid data found parsing LPCM packet"),
522         ("LPCM packet contained invalid first access. Dropping"));
523     ret = GST_FLOW_OK;
524     goto done;
525   }
526 bad_first_access:
527   {
528     GST_WARNING_OBJECT (pad, "Bad first_access parameter in buffer");
529     GST_ELEMENT_ERROR (dvdlpcmdec, STREAM, DECODE,
530         (NULL),
531         ("first_access parameter out of range: bad buffer from demuxer"));
532     ret = GST_FLOW_ERROR;
533     goto done;
534   }
535 }
536
537 static GstFlowReturn
538 gst_dvdlpcmdec_parse_dvd (GstDvdLpcmDec * dvdlpcmdec, GstAdapter * adapter,
539     gint * offset, gint * len)
540 {
541   guint32 header;
542   const guint8 *data;
543
544   data = (const guint8 *) gst_adapter_map (adapter, 3);
545   if (!data)
546     goto too_small;
547
548   /* Don't keep the 'frame number' low 5 bits of the first byte */
549   header = ((data[0] & 0xC0) << 16) | (data[1] << 8) | data[2];
550
551   gst_adapter_unmap (adapter);
552
553   /* see if we have a new header */
554   if (header != dvdlpcmdec->header) {
555     parse_header (dvdlpcmdec, header);
556
557     if (!gst_dvdlpcmdec_set_output_format (dvdlpcmdec))
558       goto negotiation_failed;
559
560     dvdlpcmdec->header = header;
561   }
562
563   *offset = 3;
564   *len = gst_adapter_available (adapter) - 3;
565
566   return GST_FLOW_OK;
567
568   /* ERRORS */
569 too_small:
570   {
571     /* Buffer is too small */
572     GST_ELEMENT_WARNING (dvdlpcmdec, STREAM, DECODE,
573         ("Invalid data found parsing LPCM packet"),
574         ("LPCM packet was too small. Dropping"));
575     *offset = gst_adapter_available (adapter);
576     return GST_FLOW_EOS;
577   }
578 negotiation_failed:
579   {
580     GST_ELEMENT_ERROR (dvdlpcmdec, STREAM, FORMAT, (NULL),
581         ("Failed to configure output format"));
582     return GST_FLOW_NOT_NEGOTIATED;
583   }
584 }
585
586 static GstFlowReturn
587 gst_dvdlpcmdec_parse_bluray (GstDvdLpcmDec * dvdlpcmdec, GstAdapter * adapter,
588     gint * offset, gint * len)
589 {
590   guint32 header;
591   const guint8 *data;
592   guint8 channel_indicator;
593
594   data = (const guint8 *) gst_adapter_map (adapter, 4);
595   if (!data)
596     goto too_small;
597
598   header = GST_READ_UINT32_BE (data);
599
600   gst_adapter_unmap (adapter);
601
602   /* see if we have a new header */
603   if (header != dvdlpcmdec->header) {
604     GstAudioFormat format;
605     gint rate, channels;
606
607     switch ((header >> 6) & 0x3) {
608       case 0x1:
609         format = GST_AUDIO_FORMAT_S16BE;
610         dvdlpcmdec->width = 16;
611         break;
612       case 0x2:
613         format = GST_AUDIO_FORMAT_S24BE;
614         dvdlpcmdec->width = 20;
615         break;
616       case 0x3:
617         format = GST_AUDIO_FORMAT_S24BE;
618         dvdlpcmdec->width = 24;
619         break;
620       default:
621         format = GST_AUDIO_FORMAT_UNKNOWN;
622         dvdlpcmdec->width = 0;
623         GST_WARNING ("Invalid sample depth!");
624         break;
625     }
626
627     switch ((header >> 8) & 0xf) {
628       case 0x1:
629         rate = 48000;
630         break;
631       case 0x4:
632         rate = 96000;
633         break;
634       case 0x5:
635         rate = 192000;
636         break;
637       default:
638         rate = 0;
639         GST_WARNING ("Invalid audio sampling frequency!");
640         break;
641     }
642     channel_indicator = (header >> 12) & 0xf;
643     switch (channel_indicator) {
644       case 0x1:
645         channels = 1;
646         break;
647       case 0x3:
648         channels = 2;
649         break;
650       case 0x4:
651       case 0x5:
652         channels = 3;
653         break;
654       case 0x6:
655       case 0x7:
656         channels = 4;
657         break;
658       case 0x8:
659         channels = 5;
660         break;
661       case 0x9:
662         channels = 6;
663         break;
664       case 0xa:
665         channels = 7;
666         break;
667       case 0xb:
668         channels = 8;
669         break;
670       default:
671         channels = 0;
672         GST_WARNING ("Invalid number of audio channels!");
673         goto negotiation_failed;
674     }
675     GST_DEBUG_OBJECT (dvdlpcmdec, "got channels %d rate %d format %s",
676         channels, rate, gst_audio_format_to_string (format));
677
678     gst_dvdlpcmdec_update_audio_formats (dvdlpcmdec, channels, rate, format,
679         channel_indicator, bluray_channel_positions);
680
681     if (!gst_dvdlpcmdec_set_output_format (dvdlpcmdec))
682       goto negotiation_failed;
683
684     dvdlpcmdec->header = header;
685   }
686
687   *offset = 4;
688   *len = gst_adapter_available (adapter) - 4;
689
690   return GST_FLOW_OK;
691
692   /* ERRORS */
693 too_small:
694   {
695     /* Buffer is too small */
696     GST_ELEMENT_WARNING (dvdlpcmdec, STREAM, DECODE,
697         ("Invalid data found parsing LPCM packet"),
698         ("LPCM packet was too small. Dropping"));
699     *offset = gst_adapter_available (adapter);
700     return GST_FLOW_EOS;
701   }
702 negotiation_failed:
703   {
704     GST_ELEMENT_ERROR (dvdlpcmdec, STREAM, FORMAT, (NULL),
705         ("Failed to configure output format"));
706     return GST_FLOW_NOT_NEGOTIATED;
707   }
708
709 }
710
711 static GstFlowReturn
712 gst_dvdlpcmdec_parse_1394 (GstDvdLpcmDec * dvdlpcmdec, GstAdapter * adapter,
713     gint * offset, gint * len)
714 {
715   guint32 header;
716   const guint8 *data;
717
718   data = (const guint8 *) gst_adapter_map (adapter, 4);
719   if (!data)
720     goto too_small;
721
722   header = GST_READ_UINT32_BE (data);
723
724   gst_adapter_unmap (adapter);
725
726   /* see if we have a new header */
727   if (header != dvdlpcmdec->header) {
728     GstAudioFormat format;
729     gint rate, channels;
730
731     if (header >> 24 != 0xa0)
732       goto invalid_data;
733
734     switch ((header >> 6) & 0x3) {
735       case 0x0:
736         format = GST_AUDIO_FORMAT_S16BE;
737         dvdlpcmdec->width = 16;
738         break;
739       default:
740         format = GST_AUDIO_FORMAT_UNKNOWN;
741         dvdlpcmdec->width = 0;
742         GST_WARNING ("Invalid quantization word length!");
743         break;
744     }
745
746     switch ((header >> 3) & 0x7) {
747       case 0x1:
748         rate = 44100;
749         break;
750       case 0x2:
751         rate = 48000;
752         break;
753       default:
754         rate = 0;
755         GST_WARNING ("Invalid audio sampling frequency!");
756         break;
757     }
758     switch (header & 0x7) {
759       case 0x0:                /* 2 channels dual-mono */
760       case 0x1:                /* 2 channels stereo */
761         channels = 2;
762         break;
763       default:
764         channels = 0;
765         GST_WARNING ("Invalid number of audio channels!");
766         goto negotiation_failed;
767     }
768
769     gst_dvdlpcmdec_update_audio_formats (dvdlpcmdec, channels, rate, format,
770         channels - 1, channel_positions);
771
772     if (!gst_dvdlpcmdec_set_output_format (dvdlpcmdec))
773       goto negotiation_failed;
774
775     dvdlpcmdec->header = header;
776   }
777
778   *offset = 4;
779   *len = gst_adapter_available (adapter) - 4;
780
781   return GST_FLOW_OK;
782
783   /* ERRORS */
784 too_small:
785   {
786     /* Buffer is too small */
787     GST_ELEMENT_WARNING (dvdlpcmdec, STREAM, DECODE,
788         ("Invalid data found parsing LPCM packet"),
789         ("LPCM packet was too small. Dropping"));
790     *offset = gst_adapter_available (adapter);
791     return GST_FLOW_EOS;
792   }
793 invalid_data:
794   {
795     GST_ELEMENT_WARNING (dvdlpcmdec, STREAM, DECODE,
796         ("Invalid data found parsing LPCM packet"),
797         ("LPCM packet contains invalid sub_stream_id."));
798     return GST_FLOW_ERROR;
799   }
800 negotiation_failed:
801   {
802     GST_ELEMENT_ERROR (dvdlpcmdec, STREAM, FORMAT, (NULL),
803         ("Failed to configure output format"));
804     return GST_FLOW_NOT_NEGOTIATED;
805   }
806 }
807
808 static GstFlowReturn
809 gst_dvdlpcmdec_parse (GstAudioDecoder * bdec, GstAdapter * adapter,
810     gint * offset, gint * len)
811 {
812   GstDvdLpcmDec *dvdlpcmdec = GST_DVDLPCMDEC (bdec);
813
814   switch (dvdlpcmdec->mode) {
815     case GST_LPCM_UNKNOWN:
816       return GST_FLOW_NOT_NEGOTIATED;
817
818     case GST_LPCM_RAW:
819       *offset = 0;
820       *len = gst_adapter_available (adapter);
821       return GST_FLOW_OK;
822
823     case GST_LPCM_DVD:
824       return gst_dvdlpcmdec_parse_dvd (dvdlpcmdec, adapter, offset, len);
825
826     case GST_LPCM_1394:
827       return gst_dvdlpcmdec_parse_1394 (dvdlpcmdec, adapter, offset, len);
828
829     case GST_LPCM_BLURAY:
830       return gst_dvdlpcmdec_parse_bluray (dvdlpcmdec, adapter, offset, len);
831   }
832   return GST_FLOW_ERROR;
833 }
834
835 static GstFlowReturn
836 gst_dvdlpcmdec_handle_frame (GstAudioDecoder * bdec, GstBuffer * buf)
837 {
838   GstDvdLpcmDec *dvdlpcmdec = GST_DVDLPCMDEC (bdec);
839   gsize size;
840   GstFlowReturn ret;
841   guint samples = 0;
842   gint rate, channels;
843
844   /* no fancy draining */
845   if (G_UNLIKELY (!buf))
846     return GST_FLOW_OK;
847
848   size = gst_buffer_get_size (buf);
849
850   GST_LOG_OBJECT (dvdlpcmdec,
851       "got buffer %p of size %" G_GSIZE_FORMAT " with ts %" GST_TIME_FORMAT,
852       buf, size, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
853
854   rate = GST_AUDIO_INFO_RATE (&dvdlpcmdec->info);
855   channels = GST_AUDIO_INFO_CHANNELS (&dvdlpcmdec->info);
856
857   if (rate == 0)
858     goto not_negotiated;
859
860   /* We don't currently do anything at all regarding emphasis, mute or
861    * dynamic_range - I'm not sure what they're for */
862   switch (dvdlpcmdec->width) {
863     case 16:
864     {
865       /* We can just pass 16-bits straight through intact, once we set
866        * appropriate things on the buffer */
867       samples = size / channels / 2;
868       if (samples < 1)
869         goto drop;
870
871       gst_buffer_ref (buf);
872       break;
873     }
874     case 20:
875     {
876       /* Allocate a new buffer and copy 20-bit width to 24-bit */
877       gint64 samples = size * 8 / 20;
878       gint64 count = size / 10;
879       gint64 i;
880       GstMapInfo srcmap, destmap;
881       guint8 *src;
882       guint8 *dest;
883       GstBuffer *outbuf;
884
885       if (samples < 1)
886         goto drop;
887
888       outbuf = gst_buffer_new_allocate (NULL, samples * 3, NULL);
889       gst_buffer_copy_into (outbuf, buf, GST_BUFFER_COPY_TIMESTAMPS, 0, -1);
890
891       /* adjust samples so we can calc the new timestamp */
892       samples = samples / channels;
893
894       gst_buffer_map (buf, &srcmap, GST_MAP_READ);
895       gst_buffer_map (outbuf, &destmap, GST_MAP_WRITE);
896       src = srcmap.data;
897       dest = destmap.data;
898
899       /* Copy 20-bit LPCM format to 24-bit buffers, with 0x00 in the lowest
900        * nibble. Note that the first 2 bytes are already correct */
901       for (i = 0; i < count; i++) {
902         dest[0] = src[0];
903         dest[1] = src[1];
904         dest[2] = src[8] & 0xf0;
905         dest[3] = src[2];
906         dest[4] = src[3];
907         dest[5] = (src[8] & 0x0f) << 4;
908         dest[6] = src[4];
909         dest[7] = src[5];
910         dest[8] = src[9] & 0x0f;
911         dest[9] = src[6];
912         dest[10] = src[7];
913         dest[11] = (src[9] & 0x0f) << 4;
914
915         src += 10;
916         dest += 12;
917       }
918       gst_buffer_unmap (outbuf, &destmap);
919       gst_buffer_unmap (buf, &srcmap);
920       buf = outbuf;
921       break;
922     }
923     case 24:
924     {
925       /* Rearrange 24-bit LPCM format in-place. Note that the first 2
926        * and last byte are already correct */
927       guint count = size / 12;
928       gint i;
929       GstMapInfo srcmap, destmap;
930       guint8 *src, *dest;
931       GstBuffer *outbuf;
932
933       samples = size / channels / 3;
934
935       if (samples < 1)
936         goto drop;
937
938       outbuf = gst_buffer_new_allocate (NULL, size, NULL);
939       gst_buffer_copy_into (outbuf, buf, GST_BUFFER_COPY_TIMESTAMPS, 0, -1);
940
941       gst_buffer_map (buf, &srcmap, GST_MAP_READ);
942       gst_buffer_map (outbuf, &destmap, GST_MAP_READWRITE);
943       src = srcmap.data;
944       dest = destmap.data;
945
946       for (i = 0; i < count; i++) {
947         dest[0] = src[0];
948         dest[1] = src[1];
949         dest[11] = src[11];
950         dest[10] = src[7];
951         dest[7] = src[5];
952         dest[5] = src[9];
953         dest[9] = src[6];
954         dest[6] = src[4];
955         dest[4] = src[3];
956         dest[3] = src[2];
957         dest[2] = src[8];
958         dest[8] = src[10];
959
960         src += 12;
961         dest += 12;
962       }
963       gst_buffer_unmap (outbuf, &destmap);
964       gst_buffer_unmap (buf, &srcmap);
965       buf = outbuf;
966       break;
967     }
968     default:
969       goto invalid_width;
970   }
971
972   if (dvdlpcmdec->lpcm_layout) {
973     buf = gst_buffer_make_writable (buf);
974     gst_audio_buffer_reorder_channels (buf, dvdlpcmdec->info.finfo->format,
975         dvdlpcmdec->info.channels, dvdlpcmdec->lpcm_layout,
976         dvdlpcmdec->info.position);
977   }
978
979   ret = gst_audio_decoder_finish_frame (bdec, buf, 1);
980
981 done:
982   return ret;
983
984   /* ERRORS */
985 drop:
986   {
987     GST_DEBUG_OBJECT (dvdlpcmdec,
988         "Buffer of size %" G_GSIZE_FORMAT " is too small. Dropping", size);
989     ret = GST_FLOW_OK;
990     goto done;
991   }
992 not_negotiated:
993   {
994     GST_ELEMENT_ERROR (dvdlpcmdec, STREAM, FORMAT, (NULL),
995         ("Buffer pushed before negotiation"));
996     ret = GST_FLOW_NOT_NEGOTIATED;
997     goto done;
998   }
999 invalid_width:
1000   {
1001     GST_ELEMENT_ERROR (dvdlpcmdec, STREAM, WRONG_TYPE, (NULL),
1002         ("Invalid sample width configured"));
1003     ret = GST_FLOW_NOT_NEGOTIATED;
1004     goto done;
1005   }
1006 }
1007
1008 static gboolean
1009 plugin_init (GstPlugin * plugin)
1010 {
1011   return GST_ELEMENT_REGISTER (dvdlpcmdec, plugin);
1012 }
1013
1014 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
1015     GST_VERSION_MINOR,
1016     dvdlpcmdec,
1017     "Decode DVD LPCM frames into standard PCM",
1018     plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN);