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 const 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);
88 static gboolean dvdlpcmdec_sink_event (GstPad * pad, GstEvent * event);
90 static GstStateChangeReturn gst_dvdlpcmdec_change_state (GstElement * element,
91 GstStateChange transition);
93 static GstElementClass *parent_class = NULL;
95 GType
96 gst_dvdlpcmdec_get_type (void)
97 {
98 static GType dvdlpcmdec_type = 0;
100 if (!dvdlpcmdec_type) {
101 static const GTypeInfo dvdlpcmdec_info = {
102 sizeof (GstDvdLpcmDecClass),
103 gst_dvdlpcmdec_base_init,
104 NULL,
105 (GClassInitFunc) gst_dvdlpcmdec_class_init,
106 NULL,
107 NULL,
108 sizeof (GstDvdLpcmDec),
109 0,
110 (GInstanceInitFunc) gst_dvdlpcmdec_init,
111 };
113 dvdlpcmdec_type =
114 g_type_register_static (GST_TYPE_ELEMENT, "GstDvdLpcmDec",
115 &dvdlpcmdec_info, 0);
116 }
117 return dvdlpcmdec_type;
118 }
120 static void
121 gst_dvdlpcmdec_base_init (gpointer g_class)
122 {
123 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
125 gst_element_class_add_pad_template (element_class,
126 gst_static_pad_template_get (&gst_dvdlpcmdec_sink_template));
127 gst_element_class_add_pad_template (element_class,
128 gst_static_pad_template_get (&gst_dvdlpcmdec_src_template));
129 gst_element_class_set_details (element_class, &gst_dvdlpcmdec_details);
130 }
132 static void
133 gst_dvdlpcmdec_class_init (GstDvdLpcmDecClass * klass)
134 {
135 GstElementClass *gstelement_class;
137 gstelement_class = (GstElementClass *) klass;
139 parent_class = g_type_class_peek_parent (klass);
141 gstelement_class->change_state = gst_dvdlpcmdec_change_state;
142 }
144 static void
145 gst_dvdlpcm_reset (GstDvdLpcmDec * dvdlpcmdec)
146 {
147 dvdlpcmdec->rate = 0;
148 dvdlpcmdec->channels = 0;
149 dvdlpcmdec->width = 0;
150 dvdlpcmdec->out_width = 0;
151 dvdlpcmdec->dynamic_range = 0;
152 dvdlpcmdec->emphasis = FALSE;
153 dvdlpcmdec->mute = FALSE;
154 dvdlpcmdec->timestamp = GST_CLOCK_TIME_NONE;
156 dvdlpcmdec->header = 0;
158 gst_segment_init (&dvdlpcmdec->segment, GST_FORMAT_UNDEFINED);
159 }
161 static void
162 gst_dvdlpcmdec_init (GstDvdLpcmDec * dvdlpcmdec)
163 {
164 dvdlpcmdec->sinkpad =
165 gst_pad_new_from_static_template (&gst_dvdlpcmdec_sink_template, "sink");
166 gst_pad_set_setcaps_function (dvdlpcmdec->sinkpad, gst_dvdlpcmdec_setcaps);
167 gst_pad_set_event_function (dvdlpcmdec->sinkpad, dvdlpcmdec_sink_event);
168 gst_element_add_pad (GST_ELEMENT (dvdlpcmdec), dvdlpcmdec->sinkpad);
170 dvdlpcmdec->srcpad =
171 gst_pad_new_from_static_template (&gst_dvdlpcmdec_src_template, "src");
172 gst_pad_use_fixed_caps (dvdlpcmdec->srcpad);
173 gst_element_add_pad (GST_ELEMENT (dvdlpcmdec), dvdlpcmdec->srcpad);
175 gst_dvdlpcm_reset (dvdlpcmdec);
176 }
178 static gboolean
179 gst_dvdlpcmdec_setcaps (GstPad * pad, GstCaps * caps)
180 {
181 GstStructure *structure;
182 gboolean res = TRUE;
183 GstDvdLpcmDec *dvdlpcmdec;
184 GstCaps *src_caps;
186 g_return_val_if_fail (caps != NULL, FALSE);
187 g_return_val_if_fail (pad != NULL, FALSE);
189 dvdlpcmdec = GST_DVDLPCMDEC (gst_pad_get_parent (pad));
191 structure = gst_caps_get_structure (caps, 0);
193 /* If we have the DVD structured LPCM (including header) */
194 if (gst_structure_has_name (structure, "audio/x-private1-lpcm")) {
195 gst_pad_set_chain_function (dvdlpcmdec->sinkpad, gst_dvdlpcmdec_chain_dvd);
196 goto done;
197 }
199 gst_pad_set_chain_function (dvdlpcmdec->sinkpad, gst_dvdlpcmdec_chain_raw);
201 res &= gst_structure_get_int (structure, "rate", &dvdlpcmdec->rate);
202 res &= gst_structure_get_int (structure, "channels", &dvdlpcmdec->channels);
203 res &= gst_structure_get_int (structure, "width", &dvdlpcmdec->width);
204 res &= gst_structure_get_int (structure, "dynamic_range",
205 &dvdlpcmdec->dynamic_range);
206 res &= gst_structure_get_boolean (structure, "emphasis",
207 &dvdlpcmdec->emphasis);
208 res &= gst_structure_get_boolean (structure, "mute", &dvdlpcmdec->mute);
210 if (!res)
211 goto caps_parse_error;
213 /* Output width is the input width rounded up to the nearest byte */
214 if (dvdlpcmdec->width == 20)
215 dvdlpcmdec->out_width = 24;
216 else
217 dvdlpcmdec->out_width = dvdlpcmdec->width;
219 /* Build caps to set on the src pad, which we know from the incoming caps */
220 src_caps = gst_caps_new_simple ("audio/x-raw-int",
221 "rate", G_TYPE_INT, dvdlpcmdec->rate,
222 "channels", G_TYPE_INT, dvdlpcmdec->channels,
223 "endianness", G_TYPE_INT, G_BIG_ENDIAN,
224 "depth", G_TYPE_INT, dvdlpcmdec->out_width,
225 "width", G_TYPE_INT, dvdlpcmdec->out_width,
226 "signed", G_TYPE_BOOLEAN, TRUE, NULL);
228 GST_DEBUG_OBJECT (dvdlpcmdec, "Set rate %d, channels %d, width %d (out %d)",
229 dvdlpcmdec->rate, dvdlpcmdec->channels, dvdlpcmdec->width,
230 dvdlpcmdec->out_width);
232 if (!gst_pad_set_caps (dvdlpcmdec->srcpad, src_caps)) {
233 GST_DEBUG_OBJECT (dvdlpcmdec, "Failed to set caps!");
234 res = FALSE;
235 } else {
236 GST_DEBUG_OBJECT (dvdlpcmdec, "Successfully set caps: %" GST_PTR_FORMAT,
237 caps);
238 }
240 gst_caps_unref (src_caps);
242 done:
243 gst_object_unref (dvdlpcmdec);
245 return res;
247 /* ERRORS */
248 caps_parse_error:
249 {
250 GST_DEBUG_OBJECT (dvdlpcmdec, "Couldn't get parameters; missing caps?");
251 res = FALSE;
252 goto done;
253 }
254 }
256 static void
257 update_timestamps (GstDvdLpcmDec * dvdlpcmdec, GstBuffer * buf, int samples)
258 {
259 gboolean take_buf_ts = FALSE;
261 GST_BUFFER_DURATION (buf) =
262 gst_util_uint64_scale (samples, GST_SECOND, dvdlpcmdec->rate);
264 if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
265 if (GST_CLOCK_TIME_IS_VALID (dvdlpcmdec->timestamp)) {
266 GstClockTimeDiff one_sample = GST_SECOND / dvdlpcmdec->rate;
267 GstClockTimeDiff diff = GST_CLOCK_DIFF (GST_BUFFER_TIMESTAMP (buf),
268 dvdlpcmdec->timestamp);
270 if (diff > one_sample || diff < -one_sample)
271 take_buf_ts = TRUE;
272 } else {
273 take_buf_ts = TRUE;
274 }
275 } else if (!GST_CLOCK_TIME_IS_VALID (dvdlpcmdec->timestamp)) {
276 dvdlpcmdec->timestamp = 0;
277 }
279 if (take_buf_ts) {
280 /* Take buffer timestamp */
281 dvdlpcmdec->timestamp = GST_BUFFER_TIMESTAMP (buf);
282 } else {
283 GST_BUFFER_TIMESTAMP (buf) = dvdlpcmdec->timestamp;
284 }
286 dvdlpcmdec->timestamp += GST_BUFFER_DURATION (buf);
288 GST_LOG_OBJECT (dvdlpcmdec, "Updated timestamp to %" GST_TIME_FORMAT,
289 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
290 }
292 static void
293 parse_header (GstDvdLpcmDec * dec, guint32 header)
294 {
295 /* We don't actually use 'dynamic range', 'mute', or 'emphasis' currently,
296 * but parse them out */
297 dec->dynamic_range = header & 0xff;
299 dec->mute = (header & 0x400000) != 0;
300 dec->emphasis = (header & 0x800000) != 0;
302 /* These two bits tell us the bit depth */
303 switch (header & 0xC000) {
304 case 0x8000:
305 dec->width = 24;
306 dec->out_width = 24;
307 break;
308 case 0x4000:
309 dec->width = 20;
310 dec->out_width = 24;
311 break;
312 default:
313 dec->width = 16;
314 dec->out_width = 16;
315 break;
316 }
318 /* Only four sample rates supported */
319 switch (header & 0x3000) {
320 case 0x0000:
321 dec->rate = 48000;
322 break;
323 case 0x1000:
324 dec->rate = 96000;
325 break;
326 case 0x2000:
327 dec->rate = 44100;
328 break;
329 case 0x3000:
330 dec->rate = 32000;
331 break;
332 }
334 /* And, of course, the number of channels (up to 8) */
335 dec->channels = ((header >> 8) & 0x7) + 1;
336 }
338 static GstFlowReturn
339 gst_dvdlpcmdec_chain_dvd (GstPad * pad, GstBuffer * buf)
340 {
341 GstDvdLpcmDec *dvdlpcmdec;
342 guint8 *data;
343 guint size;
344 guint first_access;
345 guint32 header;
346 GstBuffer *subbuf;
347 GstFlowReturn ret = GST_FLOW_OK;
348 gint off, len;
350 dvdlpcmdec = GST_DVDLPCMDEC (gst_pad_get_parent (pad));
352 size = GST_BUFFER_SIZE (buf);
353 data = GST_BUFFER_DATA (buf);
355 if (size < 5) {
356 /* Buffer is too small */
357 GST_ELEMENT_WARNING (dvdlpcmdec, STREAM, DECODE,
358 ("Invalid data found parsing LPCM packet"),
359 ("LPCM packet was too small. Dropping"));
360 ret = GST_FLOW_OK;
361 goto done;
362 }
364 /* We have a 5 byte header, now.
365 * The first two bytes are a (big endian) 16 bit offset into our buffer.
366 * The buffer timestamp refers to this offset.
367 *
368 * The other three bytes are a (big endian) number in which the header is
369 * encoded.
370 */
371 first_access = (data[0] << 8) | data[1];
372 if (first_access > size) {
373 GST_ELEMENT_WARNING (dvdlpcmdec, STREAM, DECODE,
374 ("Invalid data found parsing LPCM packet"),
375 ("LPCM packet contained invalid first access. Dropping"));
376 ret = GST_FLOW_OK;
377 goto done;
378 }
380 /* Don't keep the 'frame number' low 5 bits of the first byte */
381 header = ((data[2] & 0xC0) << 16) | (data[3] << 8) | data[4];
383 /* see if we have a new header */
384 if (header != dvdlpcmdec->header) {
385 GstCaps *src_caps;
387 parse_header (dvdlpcmdec, header);
389 /* Build caps to set on the src pad from what we've just parsed */
390 src_caps = gst_caps_new_simple ("audio/x-raw-int",
391 "rate", G_TYPE_INT, dvdlpcmdec->rate,
392 "channels", G_TYPE_INT, dvdlpcmdec->channels,
393 "endianness", G_TYPE_INT, G_BIG_ENDIAN,
394 "depth", G_TYPE_INT, dvdlpcmdec->out_width,
395 "width", G_TYPE_INT, dvdlpcmdec->out_width,
396 "signed", G_TYPE_BOOLEAN, TRUE, NULL);
398 GST_DEBUG_OBJECT (dvdlpcmdec, "Set rate %d, channels %d, width %d",
399 dvdlpcmdec->rate, dvdlpcmdec->channels, dvdlpcmdec->width);
401 if (!gst_pad_set_caps (dvdlpcmdec->srcpad, src_caps))
402 goto negotiation_failed;
404 gst_caps_unref (src_caps);
406 dvdlpcmdec->header = header;
407 }
409 GST_LOG_OBJECT (dvdlpcmdec, "first_access %d, buffer length %d", first_access,
410 size);
412 /* After first_access, we have an additional 3 bytes of data we've parsed and
413 * don't want to handle; this is included within the value of first_access.
414 * So a first_access value of between 1 and 3 is just broken, we treat that
415 * the same as zero. first_access == 4 means we only need to create a single
416 * sub-buffer, greater than that we need to create two. */
418 /* skip access unit bytes and info */
419 off = 5;
421 if (first_access > 4) {
422 guint samples = 0;
423 GstClockTime ts;
425 /* length of first buffer */
426 len = first_access - 4;
428 GST_LOG_OBJECT (dvdlpcmdec, "Creating first sub-buffer off %d, len %d",
429 off, len);
431 /* see if we need a subbuffer without timestamp */
432 if (off + len > size) {
433 GST_WARNING_OBJECT (pad, "Bad first_access parameter in buffer");
434 GST_ELEMENT_ERROR (dvdlpcmdec, STREAM, DECODE,
435 (NULL),
436 ("first_access parameter out of range: bad buffer from demuxer"));
437 ret = GST_FLOW_ERROR;
438 goto done;
439 }
441 subbuf = gst_buffer_create_sub (buf, off, len);
443 /* If we don't have a stored timestamp from the last packet,
444 * (it's straight after a new-segment, but we have one on the
445 * first access buffer, then calculate the timestamp to align
446 * this buffer to just before the first_access buffer */
447 if (!GST_CLOCK_TIME_IS_VALID (dvdlpcmdec->timestamp) &&
448 GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
449 switch (dvdlpcmdec->width) {
450 case 16:
451 samples = len / dvdlpcmdec->channels / 2;
452 break;
453 case 20:
454 samples = (len / dvdlpcmdec->channels) * 2 / 5;
455 break;
456 case 24:
457 samples = len / dvdlpcmdec->channels / 3;
458 break;
459 }
460 }
461 if (samples != 0) {
462 ts = gst_util_uint64_scale (samples, GST_SECOND, dvdlpcmdec->rate);
463 if (ts < GST_BUFFER_TIMESTAMP (buf))
464 GST_BUFFER_TIMESTAMP (subbuf) = GST_BUFFER_TIMESTAMP (buf) - ts;
465 else
466 GST_BUFFER_TIMESTAMP (subbuf) = 0;
467 } else {
468 GST_BUFFER_TIMESTAMP (subbuf) = GST_CLOCK_TIME_NONE;
469 }
471 ret = gst_dvdlpcmdec_chain_raw (pad, subbuf);
472 if (ret != GST_FLOW_OK)
473 goto done;
475 /* then the buffer with new timestamp */
476 off += len;
477 len = size - off;
479 GST_LOG_OBJECT (dvdlpcmdec, "Creating next sub-buffer off %d, len %d", off,
480 len);
482 if (len > 0) {
483 subbuf = gst_buffer_create_sub (buf, off, len);
484 GST_BUFFER_TIMESTAMP (subbuf) = GST_BUFFER_TIMESTAMP (buf);
486 ret = gst_dvdlpcmdec_chain_raw (pad, subbuf);
487 }
488 } else {
489 GST_LOG_OBJECT (dvdlpcmdec, "Creating single sub-buffer off %d, len %d",
490 off, size - off);
491 subbuf = gst_buffer_create_sub (buf, off, size - off);
492 GST_BUFFER_TIMESTAMP (subbuf) = GST_BUFFER_TIMESTAMP (buf);
493 ret = gst_dvdlpcmdec_chain_raw (pad, subbuf);
494 }
496 done:
497 gst_buffer_unref (buf);
498 gst_object_unref (dvdlpcmdec);
500 return ret;
502 /* ERRORS */
503 negotiation_failed:
504 {
505 GST_DEBUG_OBJECT (dvdlpcmdec, "Failed to set caps!");
506 ret = GST_FLOW_NOT_NEGOTIATED;
507 goto done;
508 }
509 }
511 static GstFlowReturn
512 gst_dvdlpcmdec_chain_raw (GstPad * pad, GstBuffer * buf)
513 {
514 GstDvdLpcmDec *dvdlpcmdec;
515 guint8 *data;
516 guint size;
517 GstFlowReturn ret;
518 guint samples = 0;
520 dvdlpcmdec = GST_DVDLPCMDEC (gst_pad_get_parent (pad));
522 size = GST_BUFFER_SIZE (buf);
523 data = GST_BUFFER_DATA (buf);
525 GST_LOG_OBJECT (dvdlpcmdec,
526 "got buffer %p of size %d with ts %" GST_TIME_FORMAT,
527 buf, size, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
529 if (dvdlpcmdec->rate == 0)
530 goto not_negotiated;
532 if (GST_BUFFER_TIMESTAMP_IS_VALID (buf))
533 dvdlpcmdec->timestamp = GST_BUFFER_TIMESTAMP (buf);
535 /* We don't currently do anything at all regarding emphasis, mute or
536 * dynamic_range - I'm not sure what they're for */
537 switch (dvdlpcmdec->width) {
538 case 16:
539 {
540 /* We can just pass 16-bits straight through intact, once we set
541 * appropriate things on the buffer */
542 samples = size / dvdlpcmdec->channels / 2;
543 buf = gst_buffer_make_metadata_writable (buf);
544 break;
545 }
546 case 20:
547 {
548 /* Allocate a new buffer and copy 20-bit width to 24-bit */
549 gint64 samples = size * 8 / 20;
550 gint64 count = size / 10;
551 gint64 i;
552 guint8 *src;
553 guint8 *dest;
554 GstBuffer *outbuf;
555 GstCaps *bufcaps = GST_PAD_CAPS (dvdlpcmdec->srcpad);
557 ret = gst_pad_alloc_buffer_and_set_caps (dvdlpcmdec->srcpad, 0,
558 samples * 3, bufcaps, &outbuf);
560 if (ret != GST_FLOW_OK)
561 goto buffer_alloc_failed;
563 gst_buffer_stamp (outbuf, buf);
565 /* adjust samples so we can calc the new timestamp */
566 samples = samples / dvdlpcmdec->channels;
568 src = data;
569 dest = GST_BUFFER_DATA (outbuf);
571 /* Copy 20-bit LPCM format to 24-bit buffers, with 0x00 in the lowest
572 * nibble. Note that the first 2 bytes are already correct */
573 for (i = 0; i < count; i++) {
574 dest[0] = src[0];
575 dest[1] = src[1];
576 dest[2] = src[8] & 0xf0;
577 dest[3] = src[2];
578 dest[4] = src[3];
579 dest[5] = (src[8] & 0x0f) << 4;
580 dest[6] = src[4];
581 dest[7] = src[5];
582 dest[8] = src[9] & 0x0f;
583 dest[9] = src[6];
584 dest[10] = src[7];
585 dest[11] = (src[9] & 0x0f) << 4;
587 src += 10;
588 dest += 12;
589 }
591 gst_buffer_unref (buf);
592 buf = outbuf;
593 break;
594 }
595 case 24:
596 {
597 /* Rearrange 24-bit LPCM format in-place. Note that the first 2
598 * and last byte are already correct */
599 guint count = size / 12;
600 gint i;
601 guint8 *src;
603 samples = size / dvdlpcmdec->channels / 3;
605 /* Ensure our output buffer is writable */
606 buf = gst_buffer_make_writable (buf);
608 src = GST_BUFFER_DATA (buf);
609 for (i = 0; i < count; i++) {
610 guint8 tmp;
612 tmp = src[10];
613 src[10] = src[7];
614 src[7] = src[5];
615 src[5] = src[9];
616 src[9] = src[6];
617 src[6] = src[4];
618 src[4] = src[3];
619 src[3] = src[2];
620 src[2] = src[8];
621 src[8] = tmp;
623 src += 12;
624 }
625 break;
626 }
627 default:
628 goto invalid_width;
629 }
631 /* Set appropriate caps on it to pass downstream */
632 gst_buffer_set_caps (buf, GST_PAD_CAPS (dvdlpcmdec->srcpad));
633 update_timestamps (dvdlpcmdec, buf, samples);
635 ret = gst_pad_push (dvdlpcmdec->srcpad, buf);
637 done:
638 gst_object_unref (dvdlpcmdec);
640 return ret;
642 /* ERRORS */
643 not_negotiated:
644 {
645 GST_ELEMENT_ERROR (dvdlpcmdec, STREAM, FORMAT, (NULL),
646 ("Buffer pushed before negotiation"));
647 gst_buffer_unref (buf);
648 ret = GST_FLOW_NOT_NEGOTIATED;
649 goto done;
650 }
651 buffer_alloc_failed:
652 {
653 GST_ELEMENT_ERROR (dvdlpcmdec, RESOURCE, FAILED, (NULL),
654 ("Buffer allocation failed"));
655 gst_buffer_unref (buf);
656 goto done;
657 }
658 invalid_width:
659 {
660 GST_ELEMENT_ERROR (dvdlpcmdec, STREAM, WRONG_TYPE, (NULL),
661 ("Invalid sample width configured"));
662 gst_buffer_unref (buf);
663 ret = GST_FLOW_NOT_NEGOTIATED;
664 goto done;
665 }
666 }
668 static gboolean
669 dvdlpcmdec_sink_event (GstPad * pad, GstEvent * event)
670 {
671 GstDvdLpcmDec *dvdlpcmdec;
672 gboolean res;
674 dvdlpcmdec = GST_DVDLPCMDEC (GST_PAD_PARENT (pad));
676 switch (GST_EVENT_TYPE (event)) {
677 case GST_EVENT_NEWSEGMENT:
678 {
679 gdouble rate, arate;
680 GstFormat format;
681 gboolean update;
682 gint64 start, stop, pos;
684 gst_event_parse_new_segment_full (event, &update, &rate, &arate,
685 &format, &start, &stop, &pos);
687 GST_DEBUG_OBJECT (dvdlpcmdec,
688 "new segment, format=%d, start = %" G_GINT64_FORMAT
689 ", stop = %" G_GINT64_FORMAT ", position %" G_GINT64_FORMAT,
690 format, start, stop, pos);
692 gst_segment_set_newsegment_full (&dvdlpcmdec->segment, update,
693 rate, arate, format, start, stop, pos);
695 if (format == GST_FORMAT_TIME) {
696 dvdlpcmdec->timestamp = GST_CLOCK_TIME_NONE;
697 } else {
698 dvdlpcmdec->timestamp = 0;
699 }
700 res = gst_pad_push_event (dvdlpcmdec->srcpad, event);
701 break;
702 }
703 case GST_EVENT_FLUSH_STOP:
704 gst_segment_init (&dvdlpcmdec->segment, GST_FORMAT_UNDEFINED);
705 res = gst_pad_push_event (dvdlpcmdec->srcpad, event);
706 break;
707 default:
708 res = gst_pad_push_event (dvdlpcmdec->srcpad, event);
709 break;
710 }
712 return res;
713 }
715 static GstStateChangeReturn
716 gst_dvdlpcmdec_change_state (GstElement * element, GstStateChange transition)
717 {
718 GstDvdLpcmDec *dvdlpcmdec = GST_DVDLPCMDEC (element);
719 GstStateChangeReturn res;
721 switch (transition) {
722 case GST_STATE_CHANGE_READY_TO_PAUSED:
723 gst_dvdlpcm_reset (dvdlpcmdec);
724 break;
725 default:
726 break;
727 }
729 res = parent_class->change_state (element, transition);
731 switch (transition) {
732 default:
733 break;
734 }
736 return res;
737 }
739 static gboolean
740 plugin_init (GstPlugin * plugin)
741 {
742 GST_DEBUG_CATEGORY_INIT (dvdlpcm_debug, "dvdlpcmdec", 0, "DVD LPCM Decoder");
744 if (!gst_element_register (plugin, "dvdlpcmdec", GST_RANK_PRIMARY,
745 GST_TYPE_DVDLPCMDEC)) {
746 return FALSE;
747 }
749 return TRUE;
750 }
752 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
753 GST_VERSION_MINOR,
754 "dvdlpcmdec",
755 "Decode DVD LPCM frames into standard PCM",
756 plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN);