You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
328 lines
11 KiB
328 lines
11 KiB
/*****************************************************************************
|
|
* ac3_spdif.c: ac3 pass-through to external decoder with enabled soundcard
|
|
*****************************************************************************
|
|
* Copyright (C) 2001 VideoLAN
|
|
* $Id: ac3_spdif.c,v 1.14 2002/02/19 00:50:19 sam Exp $
|
|
*
|
|
* Authors: Stéphane Borel <stef@via.ecp.fr>
|
|
* Juha Yrjola <jyrjola@cc.hut.fi>
|
|
* German Gomez Garcia <german@piraos.com>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 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 General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
|
|
*****************************************************************************/
|
|
|
|
/*****************************************************************************
|
|
* Preamble
|
|
*****************************************************************************/
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h> /* memcpy() */
|
|
#include <fcntl.h>
|
|
|
|
#include <videolan/vlc.h>
|
|
|
|
#ifdef HAVE_UNISTD_H
|
|
# include <unistd.h>
|
|
#endif
|
|
|
|
#include "audio_output.h"
|
|
|
|
#include "stream_control.h"
|
|
#include "input_ext-dec.h"
|
|
|
|
#include "ac3_spdif.h"
|
|
#include "ac3_iec958.h"
|
|
|
|
#define FRAME_NB 8
|
|
|
|
/****************************************************************************
|
|
* Local Prototypes
|
|
****************************************************************************/
|
|
static int decoder_Probe ( u8 * );
|
|
static int decoder_Run ( decoder_config_t * );
|
|
static int InitThread ( ac3_spdif_thread_t * );
|
|
static void EndThread ( ac3_spdif_thread_t * );
|
|
static void BitstreamCallback ( bit_stream_t *, boolean_t );
|
|
|
|
/*****************************************************************************
|
|
* Capabilities
|
|
*****************************************************************************/
|
|
void _M( adec_getfunctions )( function_list_t * p_function_list )
|
|
{
|
|
p_function_list->functions.dec.pf_probe = decoder_Probe;
|
|
p_function_list->functions.dec.pf_run = decoder_Run;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* Build configuration tree.
|
|
*****************************************************************************/
|
|
MODULE_CONFIG_START
|
|
MODULE_CONFIG_STOP
|
|
|
|
MODULE_INIT_START
|
|
SET_DESCRIPTION( "SPDIF pass-through AC3 decoder" )
|
|
ADD_CAPABILITY( DECODER, 100 )
|
|
MODULE_INIT_STOP
|
|
|
|
MODULE_ACTIVATE_START
|
|
_M( adec_getfunctions )( &p_module->p_functions->dec );
|
|
MODULE_ACTIVATE_STOP
|
|
|
|
MODULE_DEACTIVATE_START
|
|
MODULE_DEACTIVATE_STOP
|
|
|
|
/*****************************************************************************
|
|
* decoder_Probe: probe the decoder and return score
|
|
*****************************************************************************
|
|
* Tries to launch a decoder and return score so that the interface is able
|
|
* to chose.
|
|
*****************************************************************************/
|
|
static int decoder_Probe( u8 *pi_type )
|
|
{
|
|
return( ( main_GetIntVariable( AOUT_SPDIF_VAR, 0 )
|
|
&& *pi_type == AC3_AUDIO_ES ) ? 0 : -1 );
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
* decoder_Run: the whole thing
|
|
****************************************************************************
|
|
* This function is called just after the thread is launched.
|
|
****************************************************************************/
|
|
static int decoder_Run( decoder_config_t * p_config )
|
|
{
|
|
ac3_spdif_thread_t * p_spdif;
|
|
mtime_t i_frame_time;
|
|
boolean_t b_sync;
|
|
/* PTS of the current frame */
|
|
mtime_t i_current_pts = 0;
|
|
|
|
/* Allocate the memory needed to store the thread's structure */
|
|
p_spdif = malloc( sizeof(ac3_spdif_thread_t) );
|
|
|
|
if( p_spdif == NULL )
|
|
{
|
|
intf_ErrMsg ( "spdif error: not enough memory "
|
|
"for spdif_CreateThread() to create the new thread");
|
|
DecoderError( p_config->p_decoder_fifo );
|
|
return( -1 );
|
|
}
|
|
|
|
p_spdif->p_config = p_config;
|
|
|
|
if (InitThread( p_spdif ) )
|
|
{
|
|
intf_ErrMsg( "spdif error: could not initialize thread" );
|
|
DecoderError( p_config->p_decoder_fifo );
|
|
free( p_spdif );
|
|
return( -1 );
|
|
}
|
|
|
|
/* Compute the theorical duration of an ac3 frame */
|
|
i_frame_time = 1000000 * AC3_FRAME_SIZE /
|
|
p_spdif->ac3_info.i_sample_rate;
|
|
|
|
while( !p_spdif->p_fifo->b_die && !p_spdif->p_fifo->b_error )
|
|
{
|
|
/* Handle the dates */
|
|
if( p_spdif->i_real_pts )
|
|
{
|
|
mtime_t i_delta = p_spdif->i_real_pts - i_current_pts -
|
|
i_frame_time;
|
|
if( i_delta > i_frame_time || i_delta < -i_frame_time )
|
|
{
|
|
intf_WarnMsg( 3, "spdif warning: date discontinuity (%d)",
|
|
i_delta );
|
|
}
|
|
i_current_pts = p_spdif->i_real_pts;
|
|
p_spdif->i_real_pts = 0;
|
|
}
|
|
else
|
|
{
|
|
i_current_pts += i_frame_time;
|
|
}
|
|
|
|
/* if we're late here the output won't have to play the frame */
|
|
if( i_current_pts > mdate() )
|
|
{
|
|
p_spdif->p_aout_fifo->date[p_spdif->p_aout_fifo->l_end_frame] =
|
|
i_current_pts;
|
|
|
|
/* Write in the first free packet of aout fifo */
|
|
p_spdif->p_iec = ((u8*)(p_spdif->p_aout_fifo->buffer) +
|
|
(p_spdif->p_aout_fifo->l_end_frame * SPDIF_FRAME_SIZE ));
|
|
|
|
/* Build burst to be sent to hardware decoder */
|
|
ac3_iec958_build_burst( p_spdif );
|
|
|
|
vlc_mutex_lock (&p_spdif->p_aout_fifo->data_lock);
|
|
p_spdif->p_aout_fifo->l_end_frame =
|
|
(p_spdif->p_aout_fifo->l_end_frame + 1 ) & AOUT_FIFO_SIZE;
|
|
vlc_mutex_unlock (&p_spdif->p_aout_fifo->data_lock);
|
|
}
|
|
|
|
/* Find syncword again in case of stream discontinuity */
|
|
/* Here we have p_spdif->i_pts == 0
|
|
* Therefore a non-zero value after a call to GetBits() means the PES
|
|
* has changed. */
|
|
b_sync = 0;
|
|
while( !b_sync )
|
|
{
|
|
while( GetBits( &p_spdif->bit_stream, 8 ) != 0x0b );
|
|
p_spdif->i_real_pts = p_spdif->i_pts;
|
|
p_spdif->i_pts = 0;
|
|
b_sync = ( ShowBits( &p_spdif->bit_stream, 8 ) == 0x77 );
|
|
}
|
|
RemoveBits( &p_spdif->bit_stream, 8 );
|
|
|
|
/* Read data from bitstream */
|
|
GetChunk( &p_spdif->bit_stream, p_spdif->p_ac3 + 2,
|
|
p_spdif->ac3_info.i_frame_size - 2 );
|
|
}
|
|
|
|
/* If b_error is set, the ac3 spdif thread enters the error loop */
|
|
if( p_spdif->p_fifo->b_error )
|
|
{
|
|
DecoderError( p_spdif->p_fifo );
|
|
}
|
|
|
|
/* End of the ac3 decoder thread */
|
|
EndThread( p_spdif );
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
/****************************************************************************
|
|
* InitThread: initialize thread data and create output fifo
|
|
****************************************************************************/
|
|
static int InitThread( ac3_spdif_thread_t * p_spdif )
|
|
{
|
|
boolean_t b_sync = 0;
|
|
|
|
/* Temporary buffer to store ac3 frames to be transformed */
|
|
p_spdif->p_ac3 = malloc( SPDIF_FRAME_SIZE );
|
|
|
|
if( p_spdif->p_ac3 == NULL )
|
|
{
|
|
free( p_spdif->p_ac3 );
|
|
return( -1 );
|
|
}
|
|
|
|
/*
|
|
* Initialize the thread properties
|
|
*/
|
|
p_spdif->p_fifo = p_spdif->p_config->p_decoder_fifo;
|
|
|
|
InitBitstream( &p_spdif->bit_stream, p_spdif->p_config->p_decoder_fifo,
|
|
BitstreamCallback, (void*)p_spdif );
|
|
|
|
/* Sync word */
|
|
p_spdif->p_ac3[0] = 0x0b;
|
|
p_spdif->p_ac3[1] = 0x77;
|
|
|
|
/* Find syncword */
|
|
while( !b_sync )
|
|
{
|
|
while( GetBits( &p_spdif->bit_stream, 8 ) != 0x0b );
|
|
p_spdif->i_real_pts = p_spdif->i_pts;
|
|
p_spdif->i_pts = 0;
|
|
b_sync = ( ShowBits( &p_spdif->bit_stream, 8 ) == 0x77 );
|
|
}
|
|
RemoveBits( &p_spdif->bit_stream, 8 );
|
|
|
|
/* Check stream properties */
|
|
if( ac3_iec958_parse_syncinfo( p_spdif ) < 0 )
|
|
{
|
|
intf_ErrMsg( "spdif error: stream not valid");
|
|
|
|
aout_DestroyFifo( p_spdif->p_aout_fifo );
|
|
return( -1 );
|
|
}
|
|
|
|
/* Check that we can handle the rate
|
|
* FIXME: we should check that we have the same rate for all fifos
|
|
* but all rates should be supported by the decoder (32, 44.1, 48) */
|
|
if( p_spdif->ac3_info.i_sample_rate != 48000 )
|
|
{
|
|
intf_ErrMsg( "spdif error: Only 48000 Hz streams tested"
|
|
"expect weird things !" );
|
|
//intf_ErrMsg( "spdif error: Only 48000 Hz streams supported");
|
|
|
|
//aout_DestroyFifo( p_spdif->p_aout_fifo );
|
|
//return -1;
|
|
}
|
|
|
|
/* Creating the audio output fifo */
|
|
p_spdif->p_aout_fifo = aout_CreateFifo( AOUT_ADEC_SPDIF_FIFO, 1,
|
|
p_spdif->ac3_info.i_sample_rate,
|
|
0, SPDIF_FRAME_SIZE, NULL );
|
|
|
|
if( p_spdif->p_aout_fifo == NULL )
|
|
{
|
|
return( -1 );
|
|
}
|
|
|
|
intf_WarnMsg( 3, "spdif: aout fifo #%d created",
|
|
p_spdif->p_aout_fifo->i_fifo );
|
|
|
|
GetChunk( &p_spdif->bit_stream, p_spdif->p_ac3 + sizeof(sync_frame_t),
|
|
p_spdif->ac3_info.i_frame_size - sizeof(sync_frame_t) );
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* EndThread : ac3 spdif thread destruction
|
|
*****************************************************************************/
|
|
static void EndThread( ac3_spdif_thread_t * p_spdif )
|
|
{
|
|
/* If the audio output fifo was created, we destroy it */
|
|
if( p_spdif->p_aout_fifo != NULL )
|
|
{
|
|
aout_DestroyFifo( p_spdif->p_aout_fifo );
|
|
|
|
/* Make sure the output thread leaves the NextFrame() function */
|
|
vlc_mutex_lock( &(p_spdif->p_aout_fifo->data_lock ) );
|
|
vlc_cond_signal( &(p_spdif->p_aout_fifo->data_wait ) );
|
|
vlc_mutex_unlock( &(p_spdif->p_aout_fifo->data_lock ) );
|
|
|
|
}
|
|
|
|
/* Destroy descriptor */
|
|
free( p_spdif->p_ac3 );
|
|
free( p_spdif );
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* BitstreamCallback: Import parameters from the new data/PES packet
|
|
*****************************************************************************
|
|
* This function is called by input's NextDataPacket.
|
|
*****************************************************************************/
|
|
static void BitstreamCallback ( bit_stream_t * p_bit_stream,
|
|
boolean_t b_new_pes)
|
|
{
|
|
ac3_spdif_thread_t * p_spdif;
|
|
|
|
if( b_new_pes )
|
|
{
|
|
p_spdif = (ac3_spdif_thread_t *)p_bit_stream->p_callback_arg;
|
|
|
|
p_bit_stream->p_byte += 3;
|
|
|
|
p_spdif->i_pts =
|
|
p_bit_stream->p_decoder_fifo->p_first->i_pts;
|
|
p_bit_stream->p_decoder_fifo->p_first->i_pts = 0;
|
|
}
|
|
}
|
|
|