+
+static const unsigned p_aob_group1[21][6] = {
+ { AOUT_CHAN_CENTER, 0 },
+ { AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT, 0 },
+ { AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT, 0 },
+ { AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT, 0 },
+ { AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT, 0 },
+ { AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT, 0 },
+ { AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT, 0 },
+ { AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT, 0 },
+ { AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT, 0 },
+ { AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT, 0 },
+ { AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT, 0 },
+ { AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT, 0 },
+ { AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT, 0 },
+ { AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT, AOUT_CHAN_CENTER, 0 },
+ { AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT, AOUT_CHAN_CENTER, 0 },
+ { AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT, AOUT_CHAN_CENTER, 0 },
+ { AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT, AOUT_CHAN_CENTER, 0 },
+ { AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT, AOUT_CHAN_CENTER, 0 },
+ { AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT, AOUT_CHAN_REARLEFT, AOUT_CHAN_REARRIGHT, 0 },
+ { AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT, AOUT_CHAN_REARLEFT, AOUT_CHAN_REARRIGHT, 0 },
+ { AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT, AOUT_CHAN_REARLEFT, AOUT_CHAN_REARRIGHT, 0 },
+};
+static const unsigned p_aob_group2[21][6] = {
+ { 0 },
+ { 0 },
+ { AOUT_CHAN_REARCENTER, 0 },
+ { AOUT_CHAN_REARLEFT, AOUT_CHAN_REARRIGHT, 0 },
+ { AOUT_CHAN_LFE, 0 },
+ { AOUT_CHAN_LFE, AOUT_CHAN_REARCENTER, 0 },
+ { AOUT_CHAN_LFE, AOUT_CHAN_REARLEFT, AOUT_CHAN_REARRIGHT, 0 },
+ { AOUT_CHAN_CENTER, 0 },
+ { AOUT_CHAN_CENTER, AOUT_CHAN_REARCENTER, 0 },
+ { AOUT_CHAN_CENTER, AOUT_CHAN_REARLEFT, AOUT_CHAN_REARRIGHT, 0 },
+ { AOUT_CHAN_CENTER, AOUT_CHAN_LFE, 0 },
+ { AOUT_CHAN_CENTER, AOUT_CHAN_LFE, AOUT_CHAN_REARCENTER, 0 },
+ { AOUT_CHAN_CENTER, AOUT_CHAN_LFE, AOUT_CHAN_REARLEFT, AOUT_CHAN_REARRIGHT, 0 },
+ { AOUT_CHAN_REARCENTER, 0 },
+ { AOUT_CHAN_REARLEFT, AOUT_CHAN_REARRIGHT, 0 },
+ { AOUT_CHAN_LFE, 0 },
+ { AOUT_CHAN_LFE, AOUT_CHAN_REARCENTER, 0 },
+ { AOUT_CHAN_LFE, AOUT_CHAN_REARLEFT, AOUT_CHAN_REARRIGHT, 0 },
+ { AOUT_CHAN_LFE, 0 },
+ { AOUT_CHAN_CENTER, 0 },
+ { AOUT_CHAN_CENTER, AOUT_CHAN_LFE, 0 },
+};
+
+static int AobHeader( unsigned *pi_rate,
+ unsigned *pi_channels, unsigned *pi_layout,
+ unsigned *pi_bits,
+ unsigned *pi_padding,
+ aob_group_t g[2],
+ const uint8_t *p_header )
+{
+ const unsigned i_header_size = GetWBE( &p_header[1] );
+ if( i_header_size + 3 < LPCM_AOB_HEADER_LEN )
+ return VLC_EGENERIC;
+
+ *pi_padding = 3+i_header_size - LPCM_AOB_HEADER_LEN;
+
+ const int i_index_size_g1 = (p_header[6] >> 4) & 0x0f;
+ const int i_index_size_g2 = (p_header[6] ) & 0x0f;
+ const int i_index_rate_g1 = (p_header[7] >> 4) & 0x0f;
+ const int i_index_rate_g2 = (p_header[7] ) & 0x0f;
+ const int i_assignment = p_header[9];
+
+ /* Validate */
+ if( i_index_size_g1 > 0x02 ||
+ ( i_index_size_g2 != 0x0f && i_index_size_g2 > 0x02 ) )
+ return VLC_EGENERIC;
+ if( (i_index_rate_g1 & 0x07) > 0x02 ||
+ ( i_index_rate_g2 != 0x0f && (i_index_rate_g1 & 0x07) > 0x02 ) )
+ return VLC_EGENERIC;
+ if( i_assignment > 20 )
+ return VLC_EGENERIC;
+
+ /* */
+ *pi_bits = 16 + 4 * i_index_size_g1;
+ if( i_index_rate_g1 & 0x08 )
+ *pi_rate = 44100 << (i_index_rate_g1 & 0x07);
+ else
+ *pi_rate = 48000 << (i_index_rate_g1 & 0x07);
+
+
+ /* Group1 */
+ unsigned i_channels1 = 0;
+ unsigned i_layout1 = 0;
+ for( int i = 0; p_aob_group1[i_assignment][i] != 0; i++ )
+ {
+ i_channels1++;
+ i_layout1 |= p_aob_group1[i_assignment][i];
+ }
+ /* Group2 */
+ unsigned i_channels2 = 0;
+ unsigned i_layout2 = 0;
+ if( i_index_size_g2 != 0x0f && i_index_rate_g2 != 0x0f )
+ {
+ for( int i = 0; p_aob_group2[i_assignment][i] != 0; i++ )
+ {
+ i_channels2++;
+ i_layout2 |= p_aob_group2[i_assignment][i];
+ }
+ assert( (i_layout1 & i_layout2) == 0 );
+ }
+ /* It is enabled only when presents and compatible wih group1 */
+ const bool b_group2_used = i_index_size_g1 == i_index_size_g2 &&
+ i_index_rate_g1 == i_index_rate_g2;
+
+ /* */
+ *pi_channels = i_channels1 + ( b_group2_used ? i_channels2 : 0 );
+ *pi_layout = i_layout1 | ( b_group2_used ? i_layout2 : 0 );
+
+ /* */
+ for( unsigned i = 0; i < 2; i++ )
+ {
+ const unsigned *p_aob = i == 0 ? p_aob_group1[i_assignment] :
+ p_aob_group2[i_assignment];
+ g[i].i_channels = i == 0 ? i_channels1 :
+ i_channels2;
+
+ g[i].b_used = i == 0 || b_group2_used;
+ if( !g[i].b_used )
+ continue;
+ for( unsigned j = 0; j < g[i].i_channels; j++ )
+ {
+ g[i].pi_position[j] = 0;
+ for( int k = 0; pi_vlc_chan_order_wg4[k] != 0; k++ )
+ {
+ const unsigned i_channel = pi_vlc_chan_order_wg4[k];
+ if( i_channel == p_aob[j] )
+ break;
+ if( (*pi_layout) & i_channel )
+ g[i].pi_position[j]++;
+ }
+ }
+ }
+ return VLC_SUCCESS;
+}
+
+static int BdHeader( unsigned *pi_rate,
+ unsigned *pi_channels, unsigned *pi_original_channels,
+ unsigned *pi_bits,
+ const uint8_t *p_header )
+{
+ const uint32_t h = GetDWBE( p_header );
+ switch( ( h & 0xf000) >> 12 )
+ {
+ case 1:
+ *pi_channels = 1;
+ *pi_original_channels = AOUT_CHAN_CENTER;
+ break;
+ case 3:
+ *pi_channels = 2;
+ *pi_original_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
+ break;
+ case 4:
+ *pi_channels = 3;
+ *pi_original_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER;
+ break;
+ case 5:
+ *pi_channels = 3;
+ *pi_original_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_REARCENTER;
+ break;
+ case 6:
+ *pi_channels = 4;
+ *pi_original_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER |
+ AOUT_CHAN_REARCENTER;
+ break;
+ case 7:
+ *pi_channels = 4;
+ *pi_original_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT |
+ AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
+ break;
+ case 8:
+ *pi_channels = 5;
+ *pi_original_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER |
+ AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
+ break;
+ case 9:
+ *pi_channels = 6;
+ *pi_original_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER |
+ AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT |
+ AOUT_CHAN_LFE;
+ break;
+ case 10:
+ *pi_channels = 7;
+ *pi_original_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER |
+ AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT |
+ AOUT_CHAN_MIDDLELEFT | AOUT_CHAN_MIDDLERIGHT;
+ break;
+ case 11:
+ *pi_channels = 8;
+ *pi_original_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER |
+ AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT |
+ AOUT_CHAN_MIDDLELEFT | AOUT_CHAN_MIDDLERIGHT |
+ AOUT_CHAN_LFE;
+ break;
+
+ default:
+ return -1;
+ }
+ switch( (h >> 6) & 0x03 )
+ {
+ case 1:
+ *pi_bits = 16;
+ break;
+ case 2: /* 20 bits but samples are stored on 24 bits */
+ case 3: /* 24 bits */
+ *pi_bits = 24;
+ break;
+ default:
+ return -1;
+ }
+ switch( (h >> 8) & 0x0f )
+ {
+ case 1:
+ *pi_rate = 48000;
+ break;
+ case 4:
+ *pi_rate = 96000;
+ break;
+ case 5:
+ *pi_rate = 192000;
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static void VobExtract( aout_buffer_t *p_aout_buffer, block_t *p_block,
+ unsigned i_bits )
+{
+ uint8_t *p_out = p_aout_buffer->p_buffer;
+
+ /* 20/24 bits LPCM use special packing */
+ if( i_bits == 24 )
+ {
+ while( p_block->i_buffer / 12 )
+ {
+ /* Sample 1 */
+ p_out[0] = p_block->p_buffer[0];
+ p_out[1] = p_block->p_buffer[1];
+ p_out[2] = p_block->p_buffer[8];
+ /* Sample 2 */
+ p_out[3] = p_block->p_buffer[2];
+ p_out[4] = p_block->p_buffer[3];
+ p_out[5] = p_block->p_buffer[9];
+ /* Sample 3 */
+ p_out[6] = p_block->p_buffer[4];
+ p_out[7] = p_block->p_buffer[5];
+ p_out[8] = p_block->p_buffer[10];
+ /* Sample 4 */
+ p_out[9] = p_block->p_buffer[6];
+ p_out[10] = p_block->p_buffer[7];
+ p_out[11] = p_block->p_buffer[11];
+
+ p_block->i_buffer -= 12;
+ p_block->p_buffer += 12;
+ p_out += 12;
+ }
+ }
+ else if( i_bits == 20 )
+ {
+ while( p_block->i_buffer / 10 )
+ {
+ /* Sample 1 */
+ p_out[0] = p_block->p_buffer[0];
+ p_out[1] = p_block->p_buffer[1];
+ p_out[2] = p_block->p_buffer[8] & 0xF0;
+ /* Sample 2 */
+ p_out[3] = p_block->p_buffer[2];
+ p_out[4] = p_block->p_buffer[3];
+ p_out[5] = p_block->p_buffer[8] << 4;
+ /* Sample 3 */
+ p_out[6] = p_block->p_buffer[4];
+ p_out[7] = p_block->p_buffer[5];
+ p_out[8] = p_block->p_buffer[9] & 0xF0;
+ /* Sample 4 */
+ p_out[9] = p_block->p_buffer[6];
+ p_out[10] = p_block->p_buffer[7];
+ p_out[11] = p_block->p_buffer[9] << 4;
+
+ p_block->i_buffer -= 10;
+ p_block->p_buffer += 10;
+ p_out += 12;
+ }
+ }
+ else
+ {
+ assert( i_bits == 16 );
+ memcpy( p_out, p_block->p_buffer, p_block->i_buffer );
+ }
+}
+static void AobExtract( aout_buffer_t *p_aout_buffer,
+ block_t *p_block, unsigned i_bits, aob_group_t p_group[2] )
+{
+ const unsigned i_channels = p_group[0].i_channels +
+ ( p_group[1].b_used ? p_group[1].i_channels : 0 );
+ uint8_t *p_out = p_aout_buffer->p_buffer;
+
+ while( p_block->i_buffer > 0 )
+ {
+ for( int i = 0; i < 2; i++ )
+ {
+ const aob_group_t *g = &p_group[1-i];
+ const unsigned int i_group_size = 2 * g->i_channels * i_bits / 8;
+
+ if( p_block->i_buffer < i_group_size )
+ {
+ p_block->i_buffer = 0;
+ break;
+ }
+ for( unsigned n = 0; n < 2; n++ )
+ {
+ for( unsigned j = 0; j < g->i_channels && g->b_used; j++ )
+ {
+ const int i_src = n * g->i_channels + j;
+ const int i_dst = n * i_channels + g->pi_position[j];
+
+ if( i_bits == 24 )
+ {
+ p_out[3*i_dst+0] = p_block->p_buffer[2*i_src+0];
+ p_out[3*i_dst+1] = p_block->p_buffer[2*i_src+1];
+ p_out[3*i_dst+2] = p_block->p_buffer[4*g->i_channels+i_src];
+ }
+ else if( i_bits == 20 )
+ {
+ p_out[3*i_dst+0] = p_block->p_buffer[2*i_src+0];
+ p_out[3*i_dst+1] = p_block->p_buffer[2*i_src+1];
+ if( n == 0 )
+ p_out[3*i_dst+2] = (p_block->p_buffer[4*g->i_channels+i_src] ) & 0xf0;
+ else
+ p_out[3*i_dst+2] = (p_block->p_buffer[4*g->i_channels+i_src] << 4) & 0xf0;
+ }
+ else
+ {
+ assert( i_bits == 16 );
+ p_out[2*i_dst+0] = p_block->p_buffer[2*i_src+0];
+ p_out[2*i_dst+1] = p_block->p_buffer[2*i_src+1];
+ }
+ }
+ }
+
+ p_block->i_buffer -= i_group_size;
+ p_block->p_buffer += i_group_size;
+ }
+ /* */
+ p_out += (i_bits == 16 ? 2 : 3) * i_channels * 2;
+
+ }
+}
+static void BdExtract( aout_buffer_t *p_aout_buffer, block_t *p_block )
+{
+ memcpy( p_aout_buffer->p_buffer, p_block->p_buffer, p_block->i_buffer );
+}
+