a52tospdif: add DTS support
authorRafaël Carré <funman@videolan.org>
Mon, 13 Jun 2016 14:01:38 +0000 (16:01 +0200)
committerThomas Guillem <thomas@gllm.fr>
Tue, 14 Jun 2016 11:08:50 +0000 (13:08 +0200)
Modified-By: Thomas Guillem <thomas@gllm.fr>
Signed-off-by: Thomas Guillem <thomas@gllm.fr>
modules/audio_filter/converter/a52tospdif.c

index 2e4a820..bd4175c 100644 (file)
@@ -1,11 +1,14 @@
 /*****************************************************************************
- * a52tospdif.c : encapsulates A/52 frames into S/PDIF packets
+ * a52tospdif.c : encapsulates A/52 and DTS frames into S/PDIF packets
  *****************************************************************************
- * Copyright (C) 2002, 2006 VLC authors and VideoLAN
+ * Copyright (C) 2002, 2006-2016 VLC authors and VideoLAN
  * $Id$
  *
  * Authors: Christophe Massiot <massiot@via.ecp.fr>
  *          Stéphane Borel <stef@via.ecp.fr>
+ *          Rémi Denis-Courmont
+ *          Rafaël Carré
+ *          Thomas Guillem
  *
  * 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
@@ -30,6 +33,8 @@
 # include "config.h"
 #endif
 
+#include <assert.h>
+
 #include <vlc_common.h>
 #include <vlc_plugin.h>
 
@@ -48,7 +53,7 @@ static block_t *DoWork( filter_t *, block_t * );
 vlc_module_begin ()
     set_category( CAT_AUDIO )
     set_subcategory( SUBCAT_AUDIO_MISC )
-    set_description( N_("Audio filter for A/52->S/PDIF encapsulation") )
+    set_description( N_("Audio filter for A/52/DTS->S/PDIF encapsulation") )
     set_capability( "audio converter", 10 )
     set_callbacks( Create, NULL )
 vlc_module_end ()
@@ -60,54 +65,112 @@ static int Create( vlc_object_t *p_this )
 {
     filter_t * p_filter = (filter_t *)p_this;
 
-    if ( p_filter->fmt_in.audio.i_format != VLC_CODEC_A52 ||
-         ( p_filter->fmt_out.audio.i_format != VLC_CODEC_SPDIFB &&
-           p_filter->fmt_out.audio.i_format != VLC_CODEC_SPDIFL ) )
-    {
+    if( ( p_filter->fmt_in.audio.i_format != VLC_CODEC_DTS &&
+          p_filter->fmt_in.audio.i_format != VLC_CODEC_A52 ) ||
+        ( p_filter->fmt_out.audio.i_format != VLC_CODEC_SPDIFL &&
+          p_filter->fmt_out.audio.i_format != VLC_CODEC_SPDIFB ) )
         return VLC_EGENERIC;
-    }
 
     p_filter->pf_audio_filter = DoWork;
 
     return VLC_SUCCESS;
 }
 
+
+static uint16_t get_data_type( filter_t *p_filter, block_t *p_in )
+{
+#define IEC61937_AC3 0x01
+#define IEC61937_DTS1 0x0B
+#define IEC61937_DTS2 0x0C
+#define IEC61937_DTS3 0x0D
+
+    switch( p_filter->fmt_in.audio.i_format )
+    {
+        case VLC_CODEC_A52:
+            if( unlikely( p_in->i_buffer < 6 ) )
+                return 0;
+            return ( (p_in->p_buffer[5] & 0x7) << 8 ) /* bsmod */ | IEC61937_AC3;
+        case VLC_CODEC_DTS:
+            if( unlikely( p_in->i_buffer < 1 ) )
+                return 0;
+            switch( p_in->i_nb_samples )
+            {
+            case  512: return IEC61937_DTS1;
+            case 1024: return IEC61937_DTS2;
+            case 2048: return IEC61937_DTS3;
+            default:
+                msg_Err( p_filter, "Frame size %d not supported",
+                         p_in->i_nb_samples );
+                return 0;
+            }
+        default:
+            vlc_assert_unreachable();
+    }
+    return 0;
+}
+
+static bool is_big_endian( filter_t *p_filter, block_t *p_in )
+{
+    switch( p_filter->fmt_in.audio.i_format )
+    {
+        case VLC_CODEC_A52:
+            return true;
+        case VLC_CODEC_DTS:
+            return p_in->p_buffer[0] == 0x1F || p_in->p_buffer[0] == 0x7F;
+        default:
+            vlc_assert_unreachable();
+    }
+    return 0;
+}
+
 /*****************************************************************************
  * DoWork: convert a buffer
  *****************************************************************************/
 static block_t *DoWork( filter_t * p_filter, block_t *p_in_buf )
 {
-    /* AC3 is natively big endian. Most SPDIF devices have the native
-     * endianness of the computer system.
-     * On Mac OS X however, little endian devices are also common.
-     */
-    static const uint8_t p_sync_le[6] = { 0x72, 0xF8, 0x1F, 0x4E, 0x01, 0x00 };
-    static const uint8_t p_sync_be[6] = { 0xF8, 0x72, 0x4E, 0x1F, 0x00, 0x01 };
-    uint16_t i_frame_size = p_in_buf->i_buffer / 2;
+    uint16_t i_length = p_in_buf->i_buffer;
     uint8_t * p_in = p_in_buf->p_buffer;
+    block_t *p_out_buf = NULL;
+
+    uint16_t i_data_type = get_data_type( p_filter, p_in_buf );
+    if( i_data_type == 0 || ( i_length + 8 ) > AOUT_SPDIF_SIZE )
+        goto out;
 
-    block_t *p_out_buf = block_Alloc( AOUT_SPDIF_SIZE );
+    p_out_buf = block_Alloc( AOUT_SPDIF_SIZE );
     if( !p_out_buf )
         goto out;
-    uint8_t * p_out = p_out_buf->p_buffer;
+    uint8_t *p_out = p_out_buf->p_buffer;
 
     /* Copy the S/PDIF headers. */
-    if( p_filter->fmt_out.audio.i_format == VLC_CODEC_SPDIFB )
-    {
-        memcpy( p_out, p_sync_be, 6 );
-        p_out[4] = p_in[5] & 0x7; /* bsmod */
-        SetWBE( p_out + 6, i_frame_size << 4 );
-        memcpy( &p_out[8], p_in, i_frame_size * 2 );
-    }
-    else
+    void (*write16)(void *, uint16_t) =
+        ( p_filter->fmt_out.audio.i_format == VLC_CODEC_SPDIFB )
+        ? SetWBE : SetWLE;
+
+    write16( &p_out[0], 0xf872 ); /* syncword 1 */
+    write16( &p_out[2], 0x4e1f ); /* syncword 2 */
+    write16( &p_out[4], i_data_type ); /* data type */
+    write16( &p_out[6], i_length * 8 ); /* length in bits */
+
+    bool b_input_big_endian = is_big_endian( p_filter, p_in_buf );
+    bool b_output_big_endian =
+        p_filter->fmt_out.audio.i_format == VLC_CODEC_SPDIFB;
+
+    if( b_input_big_endian != b_output_big_endian )
     {
-        memcpy( p_out, p_sync_le, 6 );
-        p_out[5] = p_in[5] & 0x7; /* bsmod */
-        SetWLE( p_out + 6, i_frame_size << 4 );
-        swab( p_in, &p_out[8], i_frame_size * 2 );
-    }
-    memset( p_out + 8 + i_frame_size * 2, 0,
-                AOUT_SPDIF_SIZE - i_frame_size * 2 - 8 );
+        swab( p_in, p_out + 8, i_length & ~1 );
+
+        /* If i_length is odd, we have to adjust swapping a bit... */
+        if( i_length & 1 && ( i_length + 9 ) <= AOUT_SPDIF_SIZE )
+        {
+            p_out[8 + i_length - 1] = 0;
+            p_out[8 + i_length] = p_in[i_length-1];
+            i_length++;
+        }
+    } else
+        memcpy( p_out + 8, p_in, i_length );
+
+    if( 8 + i_length < AOUT_SPDIF_SIZE ) /* padding */
+        memset( p_out + 8 + i_length, 0, AOUT_SPDIF_SIZE - i_length - 8 );
 
     p_out_buf->i_dts = p_in_buf->i_dts;
     p_out_buf->i_pts = p_in_buf->i_pts;