From: Tim-Philipp Müller Date: Mon, 18 Oct 2010 23:15:20 +0000 (+0100) Subject: audioparsers: add very basic dts/dca parser X-Git-Tag: RELEASE-0.10.29~318 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=75735c4573b2a72948b98691b7e1716815ddc8f5;p=platform%2Fupstream%2Fgst-plugins-good.git audioparsers: add very basic dts/dca parser Still some issues, e.g. with seekable queries in totem, but also processing already-chunked input (created with matroskademux ! gdppay). --- diff --git a/gst/audioparsers/Makefile.am b/gst/audioparsers/Makefile.am index cd99235..8aca6d9 100644 --- a/gst/audioparsers/Makefile.am +++ b/gst/audioparsers/Makefile.am @@ -1,7 +1,8 @@ plugin_LTLIBRARIES = libgstaudioparsersbad.la libgstaudioparsersbad_la_SOURCES = \ - gstaacparse.c gstamrparse.c gstac3parse.c gstflacparse.c gstmpegaudioparse.c \ + gstaacparse.c gstamrparse.c gstac3parse.c \ + gstdcaparse.c gstflacparse.c gstmpegaudioparse.c \ gstbaseparse.c plugin.c libgstaudioparsersbad_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(GST_CFLAGS) @@ -12,5 +13,6 @@ libgstaudioparsersbad_la_LIBADD = \ libgstaudioparsersbad_la_LDFLAGS = $(PACKAGE_LIBS) $(GST_PLUGIN_LDFLAGS) libgstaudioparsersbad_la_LIBTOOLFLAGS = --tag=disable-static -noinst_HEADERS = gstaacparse.h gstamrparse.h gstac3parse.h gstflacparse.h gstmpegaudioparse.h \ +noinst_HEADERS = gstaacparse.h gstamrparse.h gstac3parse.h \ + gstdcaparse.h gstflacparse.h gstmpegaudioparse.h \ gstbaseparse.h diff --git a/gst/audioparsers/gstdcaparse.c b/gst/audioparsers/gstdcaparse.c new file mode 100644 index 0000000..f46484e --- /dev/null +++ b/gst/audioparsers/gstdcaparse.c @@ -0,0 +1,417 @@ +/* GStreamer DCA parser + * Copyright (C) 2010 Tim-Philipp Müller + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/** + * SECTION:element-dcaparse + * @short_description: DCA (DTS Coherent Acoustics) parser + * @see_also: #GstAmrParse, #GstAACParse, #GstAc3Parse + * + * + * + * This is a DCA (DTS Coherent Acoustics) parser. + * + * Example launch line + * + * + * gst-launch filesrc location=abc.dts ! dcaparse ! dtsdec ! audioresample ! audioconvert ! autoaudiosink + * + * + * + */ + +/* TODO: + * - should accept framed and unframed input (needs decodebin fixes first) + * - seeking in raw .dts files doesn't seem to work, but duration estimate ok + * + * - if frames have 'odd' durations, the frame durations (plus timestamps) + * aren't adjusted up occasionally to make up for rounding error gaps. + * (e.g. if 512 samples per frame @ 48kHz = 10.666666667 ms/frame) + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "gstdcaparse.h" +#include +#include + +GST_DEBUG_CATEGORY_STATIC (dca_parse_debug); +#define GST_CAT_DEFAULT dca_parse_debug + +static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-dts, framed = (boolean) true, " + " channels = (int) [ 1, 8 ], rate = (int) [ 8000, 192000 ]")); + +static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-dts, framed = (boolean) false")); + +static void gst_dca_parse_finalize (GObject * object); + +static gboolean gst_dca_parse_start (GstBaseParse * parse); +static gboolean gst_dca_parse_stop (GstBaseParse * parse); +static gboolean gst_dca_parse_check_valid_frame (GstBaseParse * parse, + GstBuffer * buffer, guint * size, gint * skipsize); +static GstFlowReturn gst_dca_parse_parse_frame (GstBaseParse * parse, + GstBuffer * buf); + +GST_BOILERPLATE (GstDcaParse, gst_dca_parse, GstBaseParse, GST_TYPE_BASE_PARSE); + +static void +gst_dca_parse_base_init (gpointer klass) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (klass); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&sink_template)); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&src_template)); + + gst_element_class_set_details_simple (element_class, + "DTS Coherent Acoustics audio stream parser", "Codec/Parser/Audio", + "DCA parser", "Tim-Philipp Müller "); +} + +static void +gst_dca_parse_class_init (GstDcaParseClass * klass) +{ + GstBaseParseClass *parse_class = GST_BASE_PARSE_CLASS (klass); + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + GST_DEBUG_CATEGORY_INIT (dca_parse_debug, "dcaparse", 0, + "DCA audio stream parser"); + + object_class->finalize = gst_dca_parse_finalize; + + parse_class->start = GST_DEBUG_FUNCPTR (gst_dca_parse_start); + parse_class->stop = GST_DEBUG_FUNCPTR (gst_dca_parse_stop); + parse_class->check_valid_frame = + GST_DEBUG_FUNCPTR (gst_dca_parse_check_valid_frame); + parse_class->parse_frame = GST_DEBUG_FUNCPTR (gst_dca_parse_parse_frame); +} + +static void +gst_dca_parse_reset (GstDcaParse * dcaparse) +{ + dcaparse->channels = -1; + dcaparse->rate = -1; + dcaparse->last_sync = 0; +} + +static void +gst_dca_parse_init (GstDcaParse * dcaparse, GstDcaParseClass * klass) +{ + gst_base_parse_set_min_frame_size (GST_BASE_PARSE (dcaparse), + DCA_MIN_FRAMESIZE); + gst_dca_parse_reset (dcaparse); +} + +static void +gst_dca_parse_finalize (GObject * object) +{ + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static gboolean +gst_dca_parse_start (GstBaseParse * parse) +{ + GstDcaParse *dcaparse = GST_DCA_PARSE (parse); + + GST_DEBUG_OBJECT (parse, "starting"); + + gst_dca_parse_reset (dcaparse); + + return TRUE; +} + +static gboolean +gst_dca_parse_stop (GstBaseParse * parse) +{ + GST_DEBUG_OBJECT (parse, "stopping"); + + return TRUE; +} + +static gboolean +gst_dca_parse_parse_header (GstDcaParse * dcaparse, + const GstByteReader * reader, guint * frame_size, + guint * sample_rate, guint * channels, guint * samples) +{ + static const int sample_rates[16] = { 0, 8000, 16000, 32000, 0, 0, 11025, + 22050, 44100, 0, 0, 12000, 24000, 48000, 96000, 192000 + }; + static const guint8 channels_table[16] = { 1, 2, 2, 2, 2, 3, 3, 4, 4, 5, + 6, 6, 6, 7, 8, 8 + }; + GstByteReader r = *reader; + guint16 hdr[8]; + guint32 marker; + guint num_blocks, samples_per_block, chans, lfe, i; + + if (gst_byte_reader_get_remaining (&r) < (4 + sizeof (hdr))) + return FALSE; + + marker = gst_byte_reader_peek_uint32_be_unchecked (&r); + + /* raw big endian or 14-bit big endian */ + if (marker == 0x7FFE8001 || marker == 0x1FFFE800) { + for (i = 0; i < G_N_ELEMENTS (hdr); ++i) + hdr[i] = gst_byte_reader_get_uint16_be_unchecked (&r); + } else + /* raw little endian or 14-bit little endian */ + if (marker == 0xFE7F0180 || marker == 0xFF1F00E8) { + for (i = 0; i < G_N_ELEMENTS (hdr); ++i) + hdr[i] = gst_byte_reader_get_uint16_le_unchecked (&r); + } else { + return FALSE; + } + + GST_LOG_OBJECT (dcaparse, "dts sync marker 0x%08x at offset %u", marker, + gst_byte_reader_get_pos (reader)); + + /* 14-bit mode */ + if (marker == 0x1FFFE800 || marker == 0xFF1F00E8) { + if ((hdr[2] & 0xFFF0) != 0x07F0) + return FALSE; + /* discard top 2 bits (2 void), shift in 2 */ + hdr[0] = (hdr[0] << 2) | ((hdr[1] >> 12) & 0x0003); + /* discard top 4 bits (2 void, 2 shifted into hdr[0]), shift in 4 etc. */ + hdr[1] = (hdr[1] << 4) | ((hdr[2] >> 10) & 0x000F); + hdr[2] = (hdr[2] << 6) | ((hdr[3] >> 8) & 0x003F); + hdr[3] = (hdr[3] << 8) | ((hdr[4] >> 6) & 0x00FF); + hdr[4] = (hdr[4] << 10) | ((hdr[5] >> 4) & 0x03FF); + hdr[5] = (hdr[5] << 12) | ((hdr[6] >> 2) & 0x0FFF); + hdr[6] = (hdr[6] << 14) | ((hdr[7] >> 0) & 0x3FFF); + g_assert (hdr[0] == 0x7FFE && hdr[1] == 0x8001); + } + + GST_LOG_OBJECT (dcaparse, "frame header: %04x%04x%04x%04x", + hdr[2], hdr[3], hdr[4], hdr[5]); + + samples_per_block = ((hdr[2] >> 10) & 0x1f) + 1; + num_blocks = ((hdr[2] >> 2) & 0x7F) + 1; + *frame_size = (((hdr[2] & 0x03) << 12) | (hdr[3] >> 4)) + 1; + chans = ((hdr[3] & 0x0F) << 2) | (hdr[4] >> 14); + *sample_rate = sample_rates[(hdr[4] >> 10) & 0x0F]; + lfe = (hdr[5] >> 9) & 0x03; + + GST_TRACE_OBJECT (dcaparse, "frame size %u, num_blocks %u, rate %u, " + "samples per block %u", *frame_size, num_blocks, *sample_rate, + samples_per_block); + + if (num_blocks < 6 || *frame_size < 96 || *sample_rate == 0) + return FALSE; + + if (marker == 0x1FFFE800 || marker == 0xFF1F00E8) + *frame_size = (*frame_size * 16) / 14; /* FIXME: round up? */ + + if (chans < G_N_ELEMENTS (channels_table)) + *channels = channels_table[chans] + ((lfe) ? 1 : 0); + else + *channels = 0; + + *samples = num_blocks * samples_per_block; + + GST_TRACE_OBJECT (dcaparse, "frame size %u, channels %u, rate %u, samples %u", + *frame_size, *channels, *sample_rate, *samples); + return TRUE; +} + +static gint +gst_dca_parse_find_sync (GstDcaParse * dcaparse, GstByteReader * reader, + const GstBuffer * buf, guint32 * sync) +{ + guint32 best_sync; + guint best_offset = G_MAXUINT; + gint off; + + /* FIXME: verify syncs via _parse_header() here already */ + + /* Raw big endian */ + off = gst_byte_reader_masked_scan_uint32 (reader, 0xffffffff, 0xfe7f0180, + 0, GST_BUFFER_SIZE (buf)); + if (off >= 0 && off < best_offset) { + best_offset = off; + best_sync = 0xfe7f0180; + } + + /* Raw little endian */ + off = gst_byte_reader_masked_scan_uint32 (reader, 0xffffffff, 0x7ffe8001, + 0, GST_BUFFER_SIZE (buf)); + if (off >= 0 && off < best_offset) { + best_offset = off; + best_sync = 0x7ffe8001; + } + + /* FIXME: check next 2 bytes as well for 14-bit formats (but then don't + * forget to adjust the *skipsize= in _check_valid_frame() */ + + /* 14-bit big endian */ + off = gst_byte_reader_masked_scan_uint32 (reader, 0xffffffff, 0xfe7f0180, + 0, GST_BUFFER_SIZE (buf)); + if (off >= 0 && off < best_offset) { + best_offset = off; + best_sync = 0xfe7f0180; + } + + /* 14-bit little endian */ + off = gst_byte_reader_masked_scan_uint32 (reader, 0xffffffff, 0x1fffe800, + 0, GST_BUFFER_SIZE (buf)); + if (off >= 0 && off < best_offset) { + best_offset = off; + best_sync = 0x1fffe800; + } + + if (best_offset == G_MAXUINT) + return -1; + + *sync = best_sync; + return best_offset; +} + +static gboolean +gst_dca_parse_check_valid_frame (GstBaseParse * parse, GstBuffer * buf, + guint * framesize, gint * skipsize) +{ + GstByteReader r = GST_BYTE_READER_INIT_FROM_BUFFER (buf); + GstDcaParse *dcaparse = GST_DCA_PARSE (parse); + gboolean parser_draining; + gboolean parser_in_sync; + guint32 sync = 0; + guint size, rate, chans, samples; + gint off = -1; + + if (G_UNLIKELY (GST_BUFFER_SIZE (buf) < 16)) + return FALSE; + + parser_in_sync = gst_base_parse_get_sync (parse); + + if (G_LIKELY (parser_in_sync && dcaparse->last_sync != 0)) { + off = gst_byte_reader_masked_scan_uint32 (&r, 0xffffffff, + dcaparse->last_sync, 0, GST_BUFFER_SIZE (buf)); + } + + if (G_UNLIKELY (off < 0)) { + off = gst_dca_parse_find_sync (dcaparse, &r, buf, &sync); + } + + /* didn't find anything that looks like a sync word, skip */ + if (off < 0) { + *skipsize = GST_BUFFER_SIZE (buf) - 3; + return FALSE; + } + + GST_LOG_OBJECT (parse, "possible sync %08x at buffer offset %d", sync, off); + + /* possible frame header, but not at offset 0? skip bytes before sync */ + if (off > 0) { + *skipsize = off; + return FALSE; + } + + /* make sure the values in the frame header look sane */ + if (!gst_dca_parse_parse_header (dcaparse, &r, &size, &rate, &chans, + &samples)) { + *skipsize = 4; + return FALSE; + } + + GST_LOG_OBJECT (parse, "got frame, sync %08x, size %u, rate %d, channels %d", + sync, size, rate, chans); + + *framesize = size; + + dcaparse->last_sync = sync; + + parser_draining = gst_base_parse_get_drain (parse); + + if (!parser_in_sync && !parser_draining) { + /* check for second frame to be sure */ + GST_DEBUG_OBJECT (dcaparse, "resyncing; checking next frame syncword"); + if (GST_BUFFER_SIZE (buf) >= (size + 16)) { + guint s2, r2, c2, n2; + + GST_MEMDUMP ("buf", GST_BUFFER_DATA (buf), size + 16); + gst_byte_reader_init_from_buffer (&r, buf); + gst_byte_reader_skip_unchecked (&r, size); + + if (!gst_dca_parse_parse_header (dcaparse, &r, &s2, &r2, &c2, &n2)) { + GST_DEBUG_OBJECT (dcaparse, "didn't find second syncword"); + *skipsize = 4; + return FALSE; + } + + /* ok, got sync now, let's assume constant frame size */ + gst_base_parse_set_min_frame_size (parse, size); + } else { + /* FIXME: baseparse always seems to hand us buffers of min_frame_size + * bytes, which is unhelpful here */ + GST_LOG_OBJECT (dcaparse, "next sync out of reach (%u < %u)", + GST_BUFFER_SIZE (buf), size + 16); + /* *skipsize = 0; */ + /* return FALSE; */ + } + } + + return TRUE; +} + +static GstFlowReturn +gst_dca_parse_parse_frame (GstBaseParse * parse, GstBuffer * buf) +{ + GstByteReader r = GST_BYTE_READER_INIT_FROM_BUFFER (buf); + GstDcaParse *dcaparse = GST_DCA_PARSE (parse); + guint size, rate, chans, samples; + + if (!gst_dca_parse_parse_header (dcaparse, &r, &size, &rate, &chans, + &samples)) + goto broken_header; + + if (G_UNLIKELY (dcaparse->rate != rate || dcaparse->channels != chans)) { + GstCaps *caps; + + caps = gst_caps_new_simple ("audio/x-dts", + "framed", G_TYPE_BOOLEAN, TRUE, + "rate", G_TYPE_INT, rate, "channels", G_TYPE_INT, chans, NULL); + gst_buffer_set_caps (buf, caps); + gst_pad_set_caps (GST_BASE_PARSE_SRC_PAD (parse), caps); + gst_caps_unref (caps); + + dcaparse->rate = rate; + dcaparse->channels = chans; + + gst_base_parse_set_frame_props (parse, rate, samples, 0, 0); + } + + return GST_FLOW_OK; + +/* ERRORS */ +broken_header: + { + /* this really shouldn't ever happen */ + GST_ELEMENT_ERROR (parse, STREAM, DECODE, (NULL), (NULL)); + return GST_FLOW_ERROR; + } +} diff --git a/gst/audioparsers/gstdcaparse.h b/gst/audioparsers/gstdcaparse.h new file mode 100644 index 0000000..150a191 --- /dev/null +++ b/gst/audioparsers/gstdcaparse.h @@ -0,0 +1,74 @@ +/* GStreamer DCA parser + * Copyright (C) 2010 Tim-Philipp Müller + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GST_DCA_PARSE_H__ +#define __GST_DCA_PARSE_H__ + +#include +#include "gstbaseparse.h" + +G_BEGIN_DECLS + +#define GST_TYPE_DCA_PARSE \ + (gst_dca_parse_get_type()) +#define GST_DCA_PARSE(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_DCA_PARSE, GstDcaParse)) +#define GST_DCA_PARSE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_DCA_PARSE, GstDcaParseClass)) +#define GST_IS_DCA_PARSE(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_DCA_PARSE)) +#define GST_IS_DCA_PARSE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_DCA_PARSE)) + +#define DCA_MIN_FRAMESIZE 96 +#define DCA_MAX_FRAMESIZE 18725 /* 16384*16/14 */ + +typedef struct _GstDcaParse GstDcaParse; +typedef struct _GstDcaParseClass GstDcaParseClass; + +/** + * GstDcaParse: + * + * The opaque GstDcaParse object + */ +struct _GstDcaParse { + GstBaseParse baseparse; + + /*< private >*/ + gint rate; + gint channels; + + guint32 last_sync; +}; + +/** + * GstDcaParseClass: + * @parent_class: Element parent class. + * + * The opaque GstDcaParseClass data structure. + */ +struct _GstDcaParseClass { + GstBaseParseClass baseparse_class; +}; + +GType gst_dca_parse_get_type (void); + +G_END_DECLS + +#endif /* __GST_DCA_PARSE_H__ */ diff --git a/gst/audioparsers/plugin.c b/gst/audioparsers/plugin.c index 789ccd8..5d532d7 100644 --- a/gst/audioparsers/plugin.c +++ b/gst/audioparsers/plugin.c @@ -24,6 +24,7 @@ #include "gstaacparse.h" #include "gstamrparse.h" #include "gstac3parse.h" +#include "gstdcaparse.h" #include "gstflacparse.h" #include "gstmpegaudioparse.h" @@ -38,6 +39,8 @@ plugin_init (GstPlugin * plugin) GST_RANK_PRIMARY + 1, GST_TYPE_AMRPARSE); ret &= gst_element_register (plugin, "ac3parse", GST_RANK_PRIMARY + 1, GST_TYPE_AC3_PARSE); + ret &= gst_element_register (plugin, "dcaparse", + GST_RANK_NONE, GST_TYPE_DCA_PARSE); ret &= gst_element_register (plugin, "flacparse", GST_RANK_PRIMARY + 1, GST_TYPE_FLAC_PARSE); ret &= gst_element_register (plugin, "mpegaudioparse",