From cd59f39c2c62cab1b2d23830cb108a271812a3c5 Mon Sep 17 00:00:00 2001 From: Vivia Nikolaidou Date: Wed, 5 Jul 2017 18:58:35 +0300 Subject: [PATCH] dvdlpcmdec: Added support for Blu-Ray audio https://bugzilla.gnome.org/show_bug.cgi?id=784552 --- gst/dvdlpcmdec/gstdvdlpcmdec.c | 219 +++++++++++++++++++++++++++++++-- gst/dvdlpcmdec/gstdvdlpcmdec.h | 3 +- 2 files changed, 214 insertions(+), 8 deletions(-) diff --git a/gst/dvdlpcmdec/gstdvdlpcmdec.c b/gst/dvdlpcmdec/gstdvdlpcmdec.c index a4020075fb..8ac6abb195 100644 --- a/gst/dvdlpcmdec/gstdvdlpcmdec.c +++ b/gst/dvdlpcmdec/gstdvdlpcmdec.c @@ -37,6 +37,7 @@ static GstStaticPadTemplate gst_dvdlpcmdec_sink_template = GST_PAD_ALWAYS, GST_STATIC_CAPS ("audio/x-private1-lpcm; " "audio/x-private2-lpcm; " + "audio/x-private-ts-lpcm; " "audio/x-lpcm, " "width = (int) { 16, 20, 24 }, " "rate = (int) { 32000, 44100, 48000, 96000 }, " @@ -152,6 +153,74 @@ static const GstAudioChannelPosition channel_positions[][8] = { {GST_AUDIO_CHANNEL_POSITION_INVALID} }; +static const GstAudioChannelPosition bluray_channel_positions[][8] = { + /* invalid */ + {GST_AUDIO_CHANNEL_POSITION_INVALID}, + /* mono */ + {GST_AUDIO_CHANNEL_POSITION_MONO}, + /* invalid */ + {GST_AUDIO_CHANNEL_POSITION_INVALID}, + /* stereo */ + {GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT}, + /* surround */ + {GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, + GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER}, + /* 2.1 */ + {GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, + GST_AUDIO_CHANNEL_POSITION_REAR_CENTER}, + /* 4.0 */ + {GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, + GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER, + GST_AUDIO_CHANNEL_POSITION_REAR_CENTER}, + /* 2.2 */ + {GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, + GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT, + GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT}, + /* 5.0 */ + {GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, + GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER, + GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT, + GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT}, + /* 5.1 */ + {GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, + GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER, + GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT, + GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT, + GST_AUDIO_CHANNEL_POSITION_LFE1}, + /* 7.0 */ + {GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, + GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER, + GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT, + GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT, + GST_AUDIO_CHANNEL_POSITION_REAR_LEFT, + GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT}, + /* 7.1 */ + {GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, + GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER, + GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT, + GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT, + GST_AUDIO_CHANNEL_POSITION_REAR_LEFT, + GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT, + GST_AUDIO_CHANNEL_POSITION_LFE1}, + /* invalid */ + {GST_AUDIO_CHANNEL_POSITION_INVALID}, + /* invalid */ + {GST_AUDIO_CHANNEL_POSITION_INVALID}, + /* invalid */ + {GST_AUDIO_CHANNEL_POSITION_INVALID}, + /* invalid */ + {GST_AUDIO_CHANNEL_POSITION_INVALID} +}; + static void gst_dvdlpcmdec_send_tags (GstDvdLpcmDec * dvdlpcmdec) { @@ -192,20 +261,21 @@ gst_dvdlpcmdec_set_output_format (GstDvdLpcmDec * dvdlpcmdec) static void gst_dvdlpcmdec_update_audio_formats (GstDvdLpcmDec * dec, gint channels, - gint rate, GstAudioFormat format) + gint rate, GstAudioFormat format, guint8 channel_indicator, + const GstAudioChannelPosition positions[][8]) { - GST_DEBUG_OBJECT (dec, "got channles = %d, rate = %d, format = %d", channels, + GST_DEBUG_OBJECT (dec, "got channels = %d, rate = %d, format = %d", channels, rate, format); /* Reorder the channel positions and set the default into for the audio */ if (channels < 9 - && channel_positions[channels - 1][0] != + && positions[channel_indicator][0] != GST_AUDIO_CHANNEL_POSITION_INVALID) { const GstAudioChannelPosition *position; GstAudioChannelPosition sorted_position[8]; guint c; - position = channel_positions[channels - 1]; + position = positions[channel_indicator]; for (c = 0; c < channels; ++c) sorted_position[c] = position[c]; gst_audio_channel_positions_to_valid_order (sorted_position, channels); @@ -244,6 +314,10 @@ gst_dvdlpcmdec_set_format (GstAudioDecoder * bdec, GstCaps * caps) dvdlpcmdec->mode = GST_LPCM_1394; goto done; } + if (gst_structure_has_name (structure, "audio/x-private-ts-lpcm")) { + dvdlpcmdec->mode = GST_LPCM_BLURAY; + goto done; + } dvdlpcmdec->mode = GST_LPCM_RAW; @@ -272,7 +346,8 @@ gst_dvdlpcmdec_set_format (GstAudioDecoder * bdec, GstCaps * caps) break; } - gst_dvdlpcmdec_update_audio_formats (dvdlpcmdec, channels, rate, format); + gst_dvdlpcmdec_update_audio_formats (dvdlpcmdec, channels, rate, format, + channels - 1, channel_positions); dvdlpcmdec->width = width; @@ -344,7 +419,8 @@ parse_header (GstDvdLpcmDec * dec, guint32 header) /* And, of course, the number of channels (up to 8) */ channels = ((header >> 8) & 0x7) + 1; - gst_dvdlpcmdec_update_audio_formats (dec, channels, rate, format); + gst_dvdlpcmdec_update_audio_formats (dec, channels, rate, format, + channels - 1, channel_positions); } static GstFlowReturn @@ -505,6 +581,131 @@ negotiation_failed: } } +static GstFlowReturn +gst_dvdlpcmdec_parse_bluray (GstDvdLpcmDec * dvdlpcmdec, GstAdapter * adapter, + gint * offset, gint * len) +{ + guint32 header; + const guint8 *data; + guint8 channel_indicator; + + data = (const guint8 *) gst_adapter_map (adapter, 4); + if (!data) + goto too_small; + + header = GST_READ_UINT32_BE (data); + + gst_adapter_unmap (adapter); + + /* see if we have a new header */ + if (header != dvdlpcmdec->header) { + GstAudioFormat format; + gint rate, channels; + + switch ((header >> 6) & 0x3) { + case 0x1: + format = GST_AUDIO_FORMAT_S16BE; + dvdlpcmdec->width = 16; + break; + case 0x2: + format = GST_AUDIO_FORMAT_S24BE; + dvdlpcmdec->width = 20; + break; + case 0x3: + format = GST_AUDIO_FORMAT_S24BE; + dvdlpcmdec->width = 24; + break; + default: + format = GST_AUDIO_FORMAT_UNKNOWN; + dvdlpcmdec->width = 0; + GST_WARNING ("Invalid sample depth!"); + break; + } + + switch ((header >> 8) & 0xf) { + case 0x1: + rate = 48000; + break; + case 0x4: + rate = 96000; + break; + case 0x5: + rate = 192000; + break; + default: + rate = 0; + GST_WARNING ("Invalid audio sampling frequency!"); + break; + } + channel_indicator = (header >> 12) & 0xf; + switch (channel_indicator) { + case 0x1: + channels = 1; + break; + case 0x3: + channels = 2; + break; + case 0x4: + case 0x5: + channels = 3; + break; + case 0x6: + case 0x7: + channels = 4; + break; + case 0x8: + channels = 5; + break; + case 0x9: + channels = 6; + break; + case 0xa: + channels = 7; + break; + case 0xb: + channels = 8; + break; + default: + channels = 0; + GST_WARNING ("Invalid number of audio channels!"); + break; + } + GST_DEBUG_OBJECT (dvdlpcmdec, "got channels %d rate %d format %s", + channels, rate, gst_audio_format_to_string (format)); + + gst_dvdlpcmdec_update_audio_formats (dvdlpcmdec, channels, rate, format, + channel_indicator, bluray_channel_positions); + + if (!gst_dvdlpcmdec_set_output_format (dvdlpcmdec)) + goto negotiation_failed; + + dvdlpcmdec->header = header; + } + + *offset = 4; + *len = gst_adapter_available (adapter) - 4; + + return GST_FLOW_OK; + + /* ERRORS */ +too_small: + { + /* Buffer is too small */ + GST_ELEMENT_WARNING (dvdlpcmdec, STREAM, DECODE, + ("Invalid data found parsing LPCM packet"), + ("LPCM packet was too small. Dropping")); + *offset = gst_adapter_available (adapter); + return GST_FLOW_EOS; + } +negotiation_failed: + { + GST_ELEMENT_ERROR (dvdlpcmdec, STREAM, FORMAT, (NULL), + ("Failed to configure output format")); + return GST_FLOW_NOT_NEGOTIATED; + } + +} + static GstFlowReturn gst_dvdlpcmdec_parse_1394 (GstDvdLpcmDec * dvdlpcmdec, GstAdapter * adapter, gint * offset, gint * len) @@ -563,7 +764,8 @@ gst_dvdlpcmdec_parse_1394 (GstDvdLpcmDec * dvdlpcmdec, GstAdapter * adapter, break; } - gst_dvdlpcmdec_update_audio_formats (dvdlpcmdec, channels, rate, format); + gst_dvdlpcmdec_update_audio_formats (dvdlpcmdec, channels, rate, format, + channels - 1, channel_positions); if (!gst_dvdlpcmdec_set_output_format (dvdlpcmdec)) goto negotiation_failed; @@ -621,6 +823,9 @@ gst_dvdlpcmdec_parse (GstAudioDecoder * bdec, GstAdapter * adapter, case GST_LPCM_1394: return gst_dvdlpcmdec_parse_1394 (dvdlpcmdec, adapter, offset, len); + + case GST_LPCM_BLURAY: + return gst_dvdlpcmdec_parse_bluray (dvdlpcmdec, adapter, offset, len); } return GST_FLOW_ERROR; } diff --git a/gst/dvdlpcmdec/gstdvdlpcmdec.h b/gst/dvdlpcmdec/gstdvdlpcmdec.h index 7f3d0c9c82..3bdce3f0e9 100644 --- a/gst/dvdlpcmdec/gstdvdlpcmdec.h +++ b/gst/dvdlpcmdec/gstdvdlpcmdec.h @@ -45,7 +45,8 @@ typedef enum { GST_LPCM_UNKNOWN, GST_LPCM_RAW, GST_LPCM_DVD, - GST_LPCM_1394 + GST_LPCM_1394, + GST_LPCM_BLURAY } GstDvdLpcmMode; struct _GstDvdLpcmDec {