Support multichannel raw audio in MKV
authorDenis Charmet <typx@dinauz.org>
Sat, 16 Nov 2013 21:08:11 +0000 (22:08 +0100)
committerDenis Charmet <typx@dinauz.org>
Sun, 17 Nov 2013 14:47:10 +0000 (15:47 +0100)
Fix #9866

modules/demux/mkv/matroska_segment_parse.cpp
modules/demux/mkv/mkv.cpp
modules/demux/mkv/mkv.hpp
modules/demux/wav.c
modules/demux/windows_audio_commons.h [new file with mode: 0644]

index 7b4c359..8845823 100644 (file)
@@ -31,6 +31,7 @@
 extern "C" {
 #include "../vobsub.h"
 #include "../xiph.h"
+#include "../windows_audio_commons.h"
 }
 
 #include <vlc_codecs.h>
@@ -1429,18 +1430,7 @@ int32_t matroska_segment_c::TrackInit( mkv_track_t * p_tk )
         else
         {
             WAVEFORMATEX *p_wf = (WAVEFORMATEX*)p_tk->p_extra_data;
-            if( p_wf->wFormatTag == WAVE_FORMAT_EXTENSIBLE && 
-                p_tk->i_extra_data >= sizeof(WAVEFORMATEXTENSIBLE) )
-            {
-                WAVEFORMATEXTENSIBLE * p_wext = (WAVEFORMATEXTENSIBLE*) p_wf;
-                sf_tag_to_fourcc( &p_wext->SubFormat,  &p_tk->fmt.i_codec, NULL);
-                /* FIXME should we use Samples and dwChannelMask?*/
-            }
-            else
-                wf_tag_to_fourcc( GetWLE( &p_wf->wFormatTag ), &p_tk->fmt.i_codec, NULL );
 
-            if( p_tk->fmt.i_codec == VLC_FOURCC( 'u', 'n', 'd', 'f' ) )
-                msg_Err( &sys.demuxer, "Unrecognized wf tag: 0x%x", GetWLE( &p_wf->wFormatTag ) );
             p_tk->fmt.audio.i_channels   = GetWLE( &p_wf->nChannels );
             p_tk->fmt.audio.i_rate = GetDWLE( &p_wf->nSamplesPerSec );
             p_tk->fmt.i_bitrate    = GetDWLE( &p_wf->nAvgBytesPerSec ) * 8;
@@ -1451,8 +1441,46 @@ int32_t matroska_segment_c::TrackInit( mkv_track_t * p_tk )
             if( p_tk->fmt.i_extra > 0 )
             {
                 p_tk->fmt.p_extra = xmalloc( p_tk->fmt.i_extra );
-                memcpy( p_tk->fmt.p_extra, &p_wf[1], p_tk->fmt.i_extra );
+                if( p_tk->fmt.p_extra )
+                    memcpy( p_tk->fmt.p_extra, &p_wf[1], p_tk->fmt.i_extra );
+                else
+                    p_tk->fmt.i_extra = 0;
             }
+
+            if( p_wf->wFormatTag == WAVE_FORMAT_EXTENSIBLE && 
+                p_tk->i_extra_data >= sizeof(WAVEFORMATEXTENSIBLE) )
+            {
+                WAVEFORMATEXTENSIBLE * p_wext = (WAVEFORMATEXTENSIBLE*) p_wf;
+                sf_tag_to_fourcc( &p_wext->SubFormat,  &p_tk->fmt.i_codec, NULL);
+                /* FIXME should we use Samples */
+
+                if( p_tk->fmt.audio.i_channels > 2 &&
+                    ( p_tk->fmt.i_codec != VLC_FOURCC( 'u', 'n', 'd', 'f' ) ) ) 
+                {
+                    uint32_t wfextcm = GetDWLE( &p_wext->dwChannelMask );
+                    int match;
+                    unsigned i_channel_mask = getChannelMask( &wfextcm,
+                                                              p_tk->fmt.audio.i_channels,
+                                                              &match );
+                    p_tk->fmt.i_codec = vlc_fourcc_GetCodecAudio( p_tk->fmt.i_codec,
+                                                                  p_tk->fmt.audio.i_bitspersample );
+                    if( i_channel_mask )
+                    {
+                        p_tk->i_chans_to_reorder = aout_CheckChannelReorder(
+                            pi_channels_aout, NULL,
+                            i_channel_mask,
+                            p_tk->pi_chan_table );
+
+                        p_tk->fmt.audio.i_physical_channels =
+                        p_tk->fmt.audio.i_original_channels = i_channel_mask;
+                    }
+                }
+            }
+            else
+                wf_tag_to_fourcc( GetWLE( &p_wf->wFormatTag ), &p_tk->fmt.i_codec, NULL );
+
+            if( p_tk->fmt.i_codec == VLC_FOURCC( 'u', 'n', 'd', 'f' ) )
+                msg_Err( &sys.demuxer, "Unrecognized wf tag: 0x%x", GetWLE( &p_wf->wFormatTag ) );
         }
     }
     else if( !strcmp( p_tk->psz_codec, "A_MPEG/L3" ) ||
index 7d42d83..42f3334 100644 (file)
@@ -622,22 +622,32 @@ void BlockDecode( demux_t *p_demux, KaxBlock *block, KaxSimpleBlock *simpleblock
                 p_block->i_length = -1 * i_duration * tk->fmt.audio.i_rate
                     / CLOCK_FREQ;
             }
+            break;
         }
 
-        if ( tk->fmt.i_cat == NAV_ES )
-        {
-            // TODO handle the start/stop times of this packet
-            p_sys->p_ev->SetPci( (const pci_t *)&p_block->p_buffer[1]);
-            block_Release( p_block );
-            return;
-        }
-        // correct timestamping when B frames are used
+        
         if( tk->fmt.i_cat != VIDEO_ES )
         {
+            if ( tk->fmt.i_cat == NAV_ES )
+            {
+                // TODO handle the start/stop times of this packet
+                p_sys->p_ev->SetPci( (const pci_t *)&p_block->p_buffer[1]);
+                block_Release( p_block );
+                return;
+            }
+            else if( tk->fmt.i_cat == AUDIO_ES )
+            {
+                if( tk->i_chans_to_reorder )
+                    aout_ChannelReorder( p_block->p_buffer, p_block->i_buffer,
+                                         tk->fmt.audio.i_channels,
+                                         tk->pi_chan_table, tk->fmt.i_codec );
+
+            }
             p_block->i_dts = p_block->i_pts = i_pts;
         }
         else
         {
+            // correct timestamping when B frames are used
             if( tk->b_dts_only )
             {
                 p_block->i_pts = VLC_TS_INVALID;
index 7cae3e7..a883a46 100644 (file)
@@ -53,6 +53,7 @@
 #include <vlc_charset.h>
 #include <vlc_input.h>
 #include <vlc_demux.h>
+#include <vlc_aout.h> /* For reordering */
 
 #include <iostream>
 #include <cassert>
@@ -211,6 +212,9 @@ struct mkv_track_t
 
     /* audio */
     unsigned int i_original_rate;
+    uint8_t i_chans_to_reorder;            /* do we need channel reordering */
+    uint8_t pi_chan_table[AOUT_CHAN_MAX];
+
 
     /* Private track paramters */
     PrivateTrackData *p_sys;
index 258ba7a..9a1c3d5 100644 (file)
@@ -35,6 +35,8 @@
 #include <vlc_aout.h>
 #include <vlc_codecs.h>
 
+#include "windows_audio_commons.h"
+
 /*****************************************************************************
  * Module descriptor
  *****************************************************************************/
@@ -80,17 +82,6 @@ static int FrameInfo_MS_ADPCM ( unsigned int *, int *, const es_format_t * );
 static int FrameInfo_PCM      ( unsigned int *, int *, const es_format_t * );
 static int FrameInfo_MSGSM    ( unsigned int *, int *, const es_format_t * );
 
-static const uint32_t pi_channels_src[] =
-    { WAVE_SPEAKER_FRONT_LEFT, WAVE_SPEAKER_FRONT_RIGHT,
-      WAVE_SPEAKER_FRONT_CENTER, WAVE_SPEAKER_LOW_FREQUENCY,
-      WAVE_SPEAKER_BACK_LEFT, WAVE_SPEAKER_BACK_RIGHT, WAVE_SPEAKER_BACK_CENTER,
-      WAVE_SPEAKER_SIDE_LEFT, WAVE_SPEAKER_SIDE_RIGHT, 0 };
-static const uint32_t pi_channels_in[] =
-    { AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT,
-      AOUT_CHAN_CENTER, AOUT_CHAN_LFE,
-      AOUT_CHAN_REARLEFT, AOUT_CHAN_REARRIGHT, AOUT_CHAN_REARCENTER,
-      AOUT_CHAN_MIDDLELEFT, AOUT_CHAN_MIDDLERIGHT, 0 };
-
 /*****************************************************************************
  * Open: check file and initializes structures
  *****************************************************************************/
@@ -230,20 +221,7 @@ static int Open( vlc_object_t * p_this )
         if( i_channel_mask )
         {
             int i_match = 0;
-            for( unsigned i = 0; i < sizeof(pi_channels_src)/sizeof(*pi_channels_src); i++ )
-            {
-                if( i_channel_mask & pi_channels_src[i] )
-                {
-                    if( !( p_sys->i_channel_mask & pi_channels_in[i] ) )
-                        i_match++;
-
-                    i_channel_mask &= ~pi_channels_src[i];
-                    p_sys->i_channel_mask |= pi_channels_in[i];
-
-                    if( i_match >= p_sys->fmt.audio.i_channels )
-                        break;
-                }
-            }
+            p_sys->i_channel_mask = getChannelMask( &i_channel_mask, p_sys->fmt.audio.i_channels, &i_match );
             if( i_channel_mask )
                 msg_Warn( p_demux, "Some channels are unrecognized or uselessly specified (0x%x)", i_channel_mask );
             if( i_match < p_sys->fmt.audio.i_channels )
@@ -269,11 +247,11 @@ static int Open( vlc_object_t * p_this )
                     }
                 }
                 /* Well fill up with what we can */
-                for( unsigned i = 0; i < sizeof(pi_channels_in)/sizeof(*pi_channels_in) && i_missing > 0; i++ )
+                for( unsigned i = 0; i < sizeof(pi_channels_aout)/sizeof(*pi_channels_aout) && i_missing > 0; i++ )
                 {
-                    if( !( p_sys->i_channel_mask & pi_channels_in[i] ) )
+                    if( !( p_sys->i_channel_mask & pi_channels_aout[i] ) )
                     {
-                        p_sys->i_channel_mask |= pi_channels_in[i];
+                        p_sys->i_channel_mask |= pi_channels_aout[i];
                         i_missing--;
 
                         if( i_missing <= 0 )
@@ -294,7 +272,7 @@ static int Open( vlc_object_t * p_this )
              p_sys->fmt.audio.i_channels > 2 && p_sys->fmt.audio.i_channels <= 9 )
     {
         for( int i = 0; i < p_sys->fmt.audio.i_channels; i++ )
-            p_sys->i_channel_mask |= pi_channels_in[i];
+            p_sys->i_channel_mask |= pi_channels_aout[i];
     }
 
     if( p_sys->i_channel_mask )
@@ -302,7 +280,7 @@ static int Open( vlc_object_t * p_this )
         if( p_sys->fmt.i_codec == VLC_FOURCC('a','r','a','w') ||
             p_sys->fmt.i_codec == VLC_FOURCC('a','f','l','t') )
             p_sys->i_chans_to_reorder =
-                aout_CheckChannelReorder( pi_channels_in, NULL,
+                aout_CheckChannelReorder( pi_channels_aout, NULL,
                                           p_sys->i_channel_mask,
                                           p_sys->pi_chan_table );
 
diff --git a/modules/demux/windows_audio_commons.h b/modules/demux/windows_audio_commons.h
new file mode 100644 (file)
index 0000000..80aa5cb
--- /dev/null
@@ -0,0 +1,58 @@
+/*****************************************************************************
+ * windows_audio_common.h: Windows Audio common code
+ *****************************************************************************
+ * Copyright (C) 2001-2013 VideoLAN
+ * $Id$
+ *
+ * Authors: Denis Charmet <typx@videolan.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+#ifndef DMX_WINDOWS_AUDIO_COMMONS_H
+#define DMX_WINDOWS_AUDIO_COMMONS_H
+
+#include <vlc_aout.h>
+#include <vlc_codecs.h>
+
+static const uint32_t pi_channels_src[] = { WAVE_SPEAKER_FRONT_LEFT,
+    WAVE_SPEAKER_FRONT_RIGHT, WAVE_SPEAKER_FRONT_CENTER,
+    WAVE_SPEAKER_LOW_FREQUENCY, WAVE_SPEAKER_BACK_LEFT, WAVE_SPEAKER_BACK_RIGHT,
+    WAVE_SPEAKER_BACK_CENTER, WAVE_SPEAKER_SIDE_LEFT, WAVE_SPEAKER_SIDE_RIGHT, 0 };
+
+static const uint32_t pi_channels_aout[] = { AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT,
+    AOUT_CHAN_CENTER, AOUT_CHAN_LFE, AOUT_CHAN_REARLEFT, AOUT_CHAN_REARRIGHT,
+    AOUT_CHAN_REARCENTER, AOUT_CHAN_MIDDLELEFT, AOUT_CHAN_MIDDLERIGHT, 0 };
+
+static inline unsigned getChannelMask( uint32_t * wvfextChannelMask, int i_channels, int * i_match )
+{
+    unsigned i_channel_mask = 0;
+    *i_match = 0;
+    for( unsigned i = 0;
+         i < sizeof(pi_channels_src)/sizeof(*pi_channels_src) &&
+         *i_match < i_channels; i++ )
+    {
+        if( *wvfextChannelMask & pi_channels_src[i] )
+        {
+            if( !( i_channel_mask & pi_channels_aout[i] ) )
+                 *i_match += 1;
+
+            *wvfextChannelMask &= ~pi_channels_src[i];
+            i_channel_mask |= pi_channels_aout[i];
+        }
+    }
+    return i_channel_mask;
+}
+
+#endif /*DMX_WINDOWS_AUDIO_COMMONS_H*/