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.
521 lines
16 KiB
521 lines
16 KiB
/*****************************************************************************
|
|
* decoder.c: AAC decoder using libfaad2
|
|
*****************************************************************************
|
|
* Copyright (C) 2001, 2002 VideoLAN
|
|
* $Id: decoder.c,v 1.4 2002/09/28 13:05:16 massiot Exp $
|
|
*
|
|
* Authors: Laurent Aimar <fenrir@via.ecp.fr>
|
|
*
|
|
* 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 <vlc/vlc.h>
|
|
#include <vlc/aout.h>
|
|
#include <vlc/decoder.h>
|
|
#include <vlc/input.h>
|
|
|
|
#include <stdlib.h> /* malloc(), free() */
|
|
#include <string.h> /* strdup() */
|
|
|
|
#include <faad.h>
|
|
|
|
#include "decoder.h"
|
|
|
|
|
|
/*****************************************************************************
|
|
* Local prototypes
|
|
*****************************************************************************/
|
|
static int OpenDecoder ( vlc_object_t * );
|
|
|
|
static int RunDecoder ( decoder_fifo_t * );
|
|
static int InitThread ( adec_thread_t * );
|
|
static void DecodeThread ( adec_thread_t * );
|
|
static void EndThread ( adec_thread_t * );
|
|
|
|
/*****************************************************************************
|
|
* Module descriptor
|
|
*****************************************************************************/
|
|
|
|
vlc_module_begin();
|
|
set_description( _("AAC decoder module (libfaad2)") );
|
|
set_capability( "decoder", 60 );
|
|
set_callbacks( OpenDecoder, NULL );
|
|
vlc_module_end();
|
|
|
|
/*****************************************************************************
|
|
* OpenDecoder: probe the decoder and return score
|
|
*****************************************************************************
|
|
* Tries to launch a decoder and return score so that the interface is able
|
|
* to choose.
|
|
*****************************************************************************/
|
|
static int OpenDecoder( vlc_object_t *p_this )
|
|
{
|
|
decoder_fifo_t *p_fifo = (decoder_fifo_t*) p_this;
|
|
|
|
if( p_fifo->i_fourcc != VLC_FOURCC('m','p','4','a') )
|
|
{
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
p_fifo->pf_run = RunDecoder;
|
|
return VLC_SUCCESS;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* RunDecoder: this function is called just after the thread is created
|
|
*****************************************************************************/
|
|
static int RunDecoder( decoder_fifo_t *p_fifo )
|
|
{
|
|
adec_thread_t *p_adec;
|
|
int b_error;
|
|
|
|
if( !( p_adec = malloc( sizeof( adec_thread_t ) ) ) )
|
|
{
|
|
msg_Err( p_fifo, "out of memory" );
|
|
DecoderError( p_fifo );
|
|
return( -1 );
|
|
}
|
|
memset( p_adec, 0, sizeof( adec_thread_t ) );
|
|
|
|
p_adec->p_fifo = p_fifo;
|
|
|
|
if( InitThread( p_adec ) != 0 )
|
|
{
|
|
DecoderError( p_fifo );
|
|
return( -1 );
|
|
}
|
|
|
|
while( ( !p_adec->p_fifo->b_die )&&( !p_adec->p_fifo->b_error ) )
|
|
{
|
|
DecodeThread( p_adec );
|
|
}
|
|
|
|
|
|
if( ( b_error = p_adec->p_fifo->b_error ) )
|
|
{
|
|
DecoderError( p_adec->p_fifo );
|
|
}
|
|
|
|
EndThread( p_adec );
|
|
if( b_error )
|
|
{
|
|
return( -1 );
|
|
}
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
|
|
#define FREE( p ) if( p ) free( p ); p = NULL
|
|
#define GetWLE( p ) \
|
|
( *(u8*)(p) + ( *((u8*)(p)+1) << 8 ) )
|
|
|
|
#define GetDWLE( p ) \
|
|
( *(u8*)(p) + ( *((u8*)(p)+1) << 8 ) + \
|
|
( *((u8*)(p)+2) << 16 ) + ( *((u8*)(p)+3) << 24 ) )
|
|
|
|
static void faac_GetWaveFormatEx( waveformatex_t *p_wh,
|
|
u8 *p_data )
|
|
{
|
|
|
|
p_wh->i_formattag = GetWLE( p_data );
|
|
p_wh->i_channels = GetWLE( p_data + 2 );
|
|
p_wh->i_samplespersec = GetDWLE( p_data + 4 );
|
|
p_wh->i_avgbytespersec= GetDWLE( p_data + 8 );
|
|
p_wh->i_blockalign = GetWLE( p_data + 12 );
|
|
p_wh->i_bitspersample = GetWLE( p_data + 14 );
|
|
p_wh->i_size = GetWLE( p_data + 16 );
|
|
|
|
if( p_wh->i_size )
|
|
{
|
|
p_wh->p_data = malloc( p_wh->i_size );
|
|
memcpy( p_wh->p_data, p_data + 18, p_wh->i_size );
|
|
}
|
|
}
|
|
|
|
/* get the first pes from fifo */
|
|
static pes_packet_t *__PES_GET( decoder_fifo_t *p_fifo )
|
|
{
|
|
pes_packet_t *p_pes;
|
|
|
|
vlc_mutex_lock( &p_fifo->data_lock );
|
|
|
|
/* if fifo is emty wait */
|
|
while( !p_fifo->p_first )
|
|
{
|
|
if( p_fifo->b_die )
|
|
{
|
|
vlc_mutex_unlock( &p_fifo->data_lock );
|
|
return( NULL );
|
|
}
|
|
vlc_cond_wait( &p_fifo->data_wait, &p_fifo->data_lock );
|
|
}
|
|
p_pes = p_fifo->p_first;
|
|
|
|
vlc_mutex_unlock( &p_fifo->data_lock );
|
|
|
|
return( p_pes );
|
|
}
|
|
|
|
/* free the first pes and go to next */
|
|
static void __PES_NEXT( decoder_fifo_t *p_fifo )
|
|
{
|
|
pes_packet_t *p_next;
|
|
|
|
vlc_mutex_lock( &p_fifo->data_lock );
|
|
|
|
p_next = p_fifo->p_first->p_next;
|
|
p_fifo->p_first->p_next = NULL;
|
|
input_DeletePES( p_fifo->p_packets_mgt, p_fifo->p_first );
|
|
p_fifo->p_first = p_next;
|
|
p_fifo->i_depth--;
|
|
|
|
if( !p_fifo->p_first )
|
|
{
|
|
/* No PES in the fifo */
|
|
/* pp_last no longer valid */
|
|
p_fifo->pp_last = &p_fifo->p_first;
|
|
while( !p_fifo->p_first )
|
|
{
|
|
vlc_cond_signal( &p_fifo->data_wait );
|
|
vlc_cond_wait( &p_fifo->data_wait, &p_fifo->data_lock );
|
|
}
|
|
}
|
|
vlc_mutex_unlock( &p_fifo->data_lock );
|
|
}
|
|
|
|
static inline void __GetFrame( adec_thread_t *p_adec )
|
|
{
|
|
pes_packet_t *p_pes;
|
|
data_packet_t *p_data;
|
|
byte_t *p_buffer;
|
|
|
|
p_pes = __PES_GET( p_adec->p_fifo );
|
|
if( p_pes->i_pts )
|
|
{
|
|
p_adec->pts = p_pes->i_pts;
|
|
}
|
|
|
|
while( ( !p_pes->i_nb_data )||( !p_pes->i_pes_size ) )
|
|
{
|
|
__PES_NEXT( p_adec->p_fifo );
|
|
p_pes = __PES_GET( p_adec->p_fifo );
|
|
}
|
|
p_adec->i_framesize = p_pes->i_pes_size;
|
|
if( p_pes->i_nb_data == 1 )
|
|
{
|
|
p_adec->p_framedata = p_pes->p_first->p_payload_start;
|
|
return;
|
|
}
|
|
/* get a buffer and gather all data packet */
|
|
if( p_adec->i_buffer_size < p_pes->i_pes_size )
|
|
{
|
|
p_adec->i_buffer_size = 3 * p_pes->i_pes_size / 2;
|
|
if( p_adec->p_buffer )
|
|
{
|
|
p_adec->p_buffer = realloc( p_adec->p_buffer,
|
|
p_adec->i_buffer_size );
|
|
}
|
|
else
|
|
{
|
|
p_adec->p_buffer = malloc( p_adec->i_buffer_size );
|
|
}
|
|
}
|
|
|
|
p_buffer = p_adec->p_framedata = p_adec->p_buffer;
|
|
p_data = p_pes->p_first;
|
|
do
|
|
{
|
|
p_adec->p_fifo->p_vlc->pf_memcpy( p_buffer, p_data->p_payload_start,
|
|
p_data->p_payload_end - p_data->p_payload_start );
|
|
p_buffer += p_data->p_payload_end - p_data->p_payload_start;
|
|
p_data = p_data->p_next;
|
|
} while( p_data );
|
|
}
|
|
|
|
static inline void __NextFrame( adec_thread_t *p_adec )
|
|
{
|
|
__PES_NEXT( p_adec->p_fifo );
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* InitThread: initialize data before entering main loop
|
|
*****************************************************************************/
|
|
static int InitThread( adec_thread_t * p_adec )
|
|
{
|
|
int i_status;
|
|
unsigned long i_rate;
|
|
unsigned char i_channels;
|
|
|
|
faacDecConfiguration *p_faad_config;
|
|
|
|
if( !p_adec->p_fifo->p_demux_data )
|
|
{
|
|
msg_Warn( p_adec->p_fifo,
|
|
"cannot load stream informations" );
|
|
}
|
|
else
|
|
{
|
|
faac_GetWaveFormatEx( &p_adec->format,
|
|
(u8*)p_adec->p_fifo->p_demux_data );
|
|
}
|
|
|
|
if( !( p_adec->p_handle = faacDecOpen() ) )
|
|
{
|
|
msg_Err( p_adec->p_fifo,
|
|
"cannot initialize faad" );
|
|
FREE( p_adec->format.p_data );
|
|
return( -1 );
|
|
}
|
|
|
|
if( !p_adec->format.p_data )
|
|
{
|
|
|
|
msg_Warn( p_adec->p_fifo,
|
|
"DecoderSpecificInfo missing, trying with first frame" );
|
|
__GetFrame( p_adec );
|
|
|
|
i_status = faacDecInit( p_adec->p_handle,
|
|
p_adec->p_framedata,
|
|
&i_rate,
|
|
&i_channels );
|
|
// __NextFrame( p_adec );
|
|
}
|
|
else
|
|
{
|
|
i_status = faacDecInit2( p_adec->p_handle,
|
|
p_adec->format.p_data,
|
|
p_adec->format.i_size,
|
|
&i_rate,
|
|
&i_channels );
|
|
}
|
|
|
|
if( i_status < 0 )
|
|
{
|
|
msg_Err( p_adec->p_fifo,
|
|
"failed to initialize faad" );
|
|
faacDecClose( p_adec->p_handle );
|
|
return( -1 );
|
|
}
|
|
msg_Dbg( p_adec->p_fifo,
|
|
"faad intitialized, samplerate:%dHz channels:%d",
|
|
i_rate,
|
|
i_channels );
|
|
|
|
|
|
/* set default configuration */
|
|
p_faad_config = faacDecGetCurrentConfiguration( p_adec->p_handle );
|
|
p_faad_config->outputFormat = FAAD_FMT_FLOAT;
|
|
faacDecSetConfiguration( p_adec->p_handle, p_faad_config );
|
|
|
|
|
|
/* Initialize the thread properties */
|
|
p_adec->output_format.i_format = AOUT_FMT_FLOAT32;
|
|
p_adec->output_format.i_rate = i_rate;
|
|
p_adec->output_format.i_channels = i_channels;
|
|
p_adec->p_aout = NULL;
|
|
p_adec->p_aout_input = NULL;
|
|
|
|
#if 0
|
|
if( !p_adec->format.p_data )
|
|
{
|
|
/* Init the BitStream */
|
|
InitBitstream( &p_adec->bit_stream, p_adec->p_fifo,
|
|
NULL, NULL );
|
|
p_adec->p_framedata = malloc( AAC_MAXCHANNELS * FAAD_MIN_STREAMSIZE );
|
|
p_adec->i_framesize = 0;
|
|
}
|
|
#endif
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* DecodeThread: decodes a frame
|
|
*****************************************************************************
|
|
* XXX it will work only for frame based streams like from mp4 file
|
|
* but it's the only way to use libfaad2 without having segfault
|
|
* after 1 or 2 frames
|
|
*****************************************************************************/
|
|
static void DecodeThread( adec_thread_t *p_adec )
|
|
{
|
|
aout_buffer_t *p_aout_buffer;
|
|
|
|
void *p_faad_buffer;
|
|
faacDecFrameInfo faad_frame;
|
|
|
|
/* **** Get a new frames from streams **** */
|
|
__GetFrame( p_adec );
|
|
|
|
/* **** decode this frame **** */
|
|
p_faad_buffer = faacDecDecode( p_adec->p_handle,
|
|
&faad_frame,
|
|
p_adec->p_framedata );
|
|
/* **** switch to the next frame **** */
|
|
__NextFrame( p_adec );
|
|
|
|
#if 0
|
|
/*
|
|
* XXX don't work ! XXX
|
|
*
|
|
* first even without format.p_data stream can be frame based
|
|
* and it make libfaad segfault
|
|
*
|
|
*/
|
|
if( p_adec->format.p_data )
|
|
{
|
|
__GetFrame( p_adec );
|
|
/* Now decode data */
|
|
p_faad_buffer = faacDecDecode( p_adec->p_handle,
|
|
&faad_frame,
|
|
p_adec->p_framedata );
|
|
__NextFrame( p_adec );
|
|
|
|
}
|
|
else
|
|
{
|
|
int i_count;
|
|
/* fill buffer */
|
|
i_count = __MAX( 1, p_adec->output_format.i_channels ) * FAAD_MIN_STREAMSIZE -
|
|
p_adec->i_framesize;
|
|
|
|
GetChunk( &p_adec->bit_stream,
|
|
p_adec->p_framedata + p_adec->i_framesize,
|
|
i_count );
|
|
p_adec->i_framesize += i_count;
|
|
p_faad_buffer = faacDecDecode( p_adec->p_handle,
|
|
&faad_frame,
|
|
p_adec->p_framedata );
|
|
/* update data buffer pos */
|
|
i_count = __MIN( p_adec->i_framesize,
|
|
__MAX( 1, faad_frame.bytesconsumed ) );
|
|
memcpy( p_adec->p_framedata,
|
|
p_adec->p_framedata + i_count,
|
|
p_adec->i_framesize - i_count );
|
|
p_adec->i_framesize -= i_count;
|
|
}
|
|
#endif
|
|
|
|
/* **** some sanity checks to see if we have samples to out **** */
|
|
if( faad_frame.error > 0 )
|
|
{
|
|
msg_Warn( p_adec->p_fifo, "%s",
|
|
faacDecGetErrorMessage(faad_frame.error) );
|
|
return;
|
|
}
|
|
if( ( faad_frame.channels <= 0 )||
|
|
( faad_frame.channels > AAC_MAXCHANNELS) )
|
|
{
|
|
msg_Warn( p_adec->p_fifo,
|
|
"invalid channels count(%d)", faad_frame.channels );
|
|
return;
|
|
}
|
|
if( faad_frame.samples <= 0 )
|
|
{
|
|
msg_Warn( p_adec->p_fifo, "decoded zero sample !" );
|
|
return;
|
|
}
|
|
|
|
#if 0
|
|
msg_Dbg( p_adec->p_fifo,
|
|
"decoded frame samples:%d, channels:%d, consumed:%d",
|
|
faad_frame.samples,
|
|
faad_frame.channels,
|
|
faad_frame.bytesconsumed );
|
|
#endif
|
|
|
|
/* **** Now we can output these samples **** */
|
|
|
|
/* **** First check if we have a valid output **** */
|
|
if( ( !p_adec->p_aout_input )||
|
|
( p_adec->output_format.i_channels != faad_frame.channels ) )
|
|
{
|
|
if( p_adec->p_aout_input )
|
|
{
|
|
/* **** Delete the old **** */
|
|
aout_DecDelete( p_adec->p_aout, p_adec->p_aout_input );
|
|
}
|
|
|
|
/* **** Create a new audio output **** */
|
|
p_adec->output_format.i_channels = faad_frame.channels;
|
|
aout_DateInit( &p_adec->date, p_adec->output_format.i_rate );
|
|
p_adec->p_aout_input = aout_DecNew( p_adec->p_fifo,
|
|
&p_adec->p_aout,
|
|
&p_adec->output_format );
|
|
}
|
|
|
|
if( !p_adec->p_aout_input )
|
|
{
|
|
msg_Err( p_adec->p_fifo, "cannot create aout" );
|
|
return;
|
|
}
|
|
|
|
if( p_adec->pts != 0 && p_adec->pts != aout_DateGet( &p_adec->date ) )
|
|
{
|
|
aout_DateSet( &p_adec->date, p_adec->pts );
|
|
}
|
|
else if( !aout_DateGet( &p_adec->date ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
p_aout_buffer = aout_DecNewBuffer( p_adec->p_aout,
|
|
p_adec->p_aout_input,
|
|
faad_frame.samples / faad_frame.channels );
|
|
if( !p_aout_buffer )
|
|
{
|
|
msg_Err( p_adec->p_fifo, "cannot get aout buffer" );
|
|
p_adec->p_fifo->b_error = 1;
|
|
return;
|
|
}
|
|
p_aout_buffer->start_date = aout_DateGet( &p_adec->date );
|
|
p_aout_buffer->end_date = aout_DateIncrement( &p_adec->date,
|
|
faad_frame.samples /
|
|
faad_frame.channels );
|
|
memcpy( p_aout_buffer->p_buffer,
|
|
p_faad_buffer,
|
|
p_aout_buffer->i_nb_bytes );
|
|
|
|
aout_DecPlay( p_adec->p_aout, p_adec->p_aout_input, p_aout_buffer );
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* EndThread : faad decoder thread destruction
|
|
*****************************************************************************/
|
|
static void EndThread (adec_thread_t *p_adec)
|
|
{
|
|
if( p_adec->p_aout_input )
|
|
{
|
|
aout_DecDelete( p_adec->p_aout, p_adec->p_aout_input );
|
|
}
|
|
|
|
if( p_adec->p_handle )
|
|
{
|
|
faacDecClose( p_adec->p_handle );
|
|
}
|
|
|
|
FREE( p_adec->format.p_data );
|
|
FREE( p_adec->p_buffer );
|
|
|
|
msg_Dbg( p_adec->p_fifo, "faad decoder closed" );
|
|
|
|
free( p_adec );
|
|
}
|
|
|
|
|