Commit 4764ebc9 authored by Laurent Aimar's avatar Laurent Aimar

* all : add audio decoding support in ffmpeg module, since they have

added wma1/2 decoder... :))
parent ec8efba6
SOURCES_ffmpeg = modules/codec/ffmpeg/ffmpeg.c
SOURCES_ffmpeg = modules/codec/ffmpeg/ffmpeg.c \
modules/codec/ffmpeg/video.c \
modules/codec/ffmpeg/audio.c
noinst_HEADERS += modules/codec/ffmpeg/ffmpeg.h
noinst_HEADERS += modules/codec/ffmpeg/ffmpeg.h \
modules/codec/ffmpeg/video.h \
modules/codec/ffmpeg/audio.h
/*****************************************************************************
* audio.c: audio decoder using ffmpeg library
*****************************************************************************
* Copyright (C) 1999-2001 VideoLAN
* $Id: audio.c,v 1.1 2002/10/28 06:26:11 fenrir 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 <stdlib.h> /* malloc(), free() */
#include <vlc/vlc.h>
#include <vlc/vout.h>
#include <vlc/aout.h>
#include <vlc/decoder.h>
#include <vlc/input.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h> /* getpid() */
#endif
#include <errno.h>
#include <string.h>
#ifdef HAVE_SYS_TIMES_H
# include <sys/times.h>
#endif
#include "avcodec.h" /* ffmpeg */
#include "postprocessing/postprocessing.h"
#include "ffmpeg.h"
#include "audio.h"
/*
* Local prototypes
*/
int E_( InitThread_Audio ) ( adec_thread_t * );
void E_( EndThread_Audio ) ( adec_thread_t * );
void E_( DecodeThread_Audio ) ( adec_thread_t * );
static int i_channels_maps[6] =
{
0,
AOUT_CHAN_MONO, AOUT_CHAN_STEREO,
AOUT_CHAN_3F, AOUT_CHAN_2F2R,
AOUT_CHAN_3F2R
};
/*****************************************************************************
* locales Functions
*****************************************************************************/
static void ffmpeg_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 );
}
}
/*****************************************************************************
*
* Functions that initialize, decode and end the decoding process
*
* Functions exported for ffmpeg.c
* * E_( InitThread_Audio )
* * E_( DecodeThread_Audio )
* * E_( EndThread_Video_Audio )
*****************************************************************************/
/*****************************************************************************
* InitThread: initialize vdec output thread
*****************************************************************************
* This function is called from decoder_Run and performs the second step
* of the initialization. It returns 0 on success. Note that the thread's
* flag are not modified inside this function.
*
* ffmpeg codec will be open, some memory allocated.
*****************************************************************************/
int E_( InitThread_Audio )( adec_thread_t *p_adec )
{
if( p_adec->p_fifo->p_demux_data )
{
ffmpeg_GetWaveFormatEx( &p_adec->format,
(u8*)p_adec->p_fifo->p_demux_data );
}
else
{
msg_Warn( p_adec->p_fifo, "audio informations missing" );
}
/* ***** Fill p_context with init values ***** */
p_adec->p_context->sample_rate = p_adec->format.i_samplespersec;
p_adec->p_context->channels = p_adec->format.i_channels;
p_adec->p_context->block_align = p_adec->format.i_blockalign;
p_adec->p_context->bit_rate = p_adec->format.i_avgbytespersec * 8;
if( ( p_adec->p_context->extradata_size = p_adec->format.i_size ) > 0 )
{
p_adec->p_context->extradata =
malloc( p_adec->format.i_size );
memcpy( p_adec->p_context->extradata,
p_adec->format.p_data,
p_adec->format.i_size );
}
/* ***** Open the codec ***** */
if (avcodec_open(p_adec->p_context, p_adec->p_codec) < 0)
{
msg_Err( p_adec->p_fifo,
"cannot open codec (%s)",
p_adec->psz_namecodec );
return( -1 );
}
else
{
msg_Dbg( p_adec->p_fifo,
"ffmpeg codec (%s) started",
p_adec->psz_namecodec );
}
p_adec->p_output = malloc( AVCODEC_MAX_AUDIO_FRAME_SIZE );
p_adec->output_format.i_format = AOUT_FMT_S16_NE;
p_adec->output_format.i_rate = p_adec->format.i_samplespersec;
p_adec->output_format.i_channels = p_adec->format.i_channels;
p_adec->p_aout = NULL;
p_adec->p_aout_input = NULL;
return( 0 );
}
/*****************************************************************************
* DecodeThread: Called for decode one frame
*****************************************************************************/
void E_( DecodeThread_Audio )( adec_thread_t *p_adec )
{
pes_packet_t *p_pes;
aout_buffer_t *p_aout_buffer;
int i_samplesperchannel;
int i_output_size;
int i_frame_size;
int i_status;
do
{
input_ExtractPES( p_adec->p_fifo, &p_pes );
if( !p_pes )
{
p_adec->p_fifo->b_error = 1;
return;
}
p_adec->pts = p_pes->i_pts;
i_frame_size = p_pes->i_pes_size;
if( i_frame_size > 0 )
{
if( p_adec->i_buffer < i_frame_size + 16 )
{
FREE( p_adec->p_buffer );
p_adec->p_buffer = malloc( i_frame_size + 16 );
p_adec->i_buffer = i_frame_size + 16;
}
E_( GetPESData )( p_adec->p_buffer, p_adec->i_buffer, p_pes );
}
input_DeletePES( p_adec->p_fifo->p_packets_mgt, p_pes );
} while( i_frame_size <= 0 );
i_status = avcodec_decode_audio( p_adec->p_context,
(s16*)p_adec->p_output,
&i_output_size,
p_adec->p_buffer,
i_frame_size );
if( i_status < 0 )
{
msg_Warn( p_adec->p_fifo,
"cannot decode one frame (%d bytes)",
i_frame_size );
return;
}
if( i_output_size <= 0 )
{
msg_Warn( p_adec->p_fifo,
"decoded %d samples bytes",
i_output_size );
}
if( p_adec->p_context->channels <= 0 ||
p_adec->p_context->channels > 5 )
{
msg_Warn( p_adec->p_fifo,
"invalid channels count",
p_adec->p_context->channels );
}
/* **** Now we can output these samples **** */
i_samplesperchannel = i_output_size / 2 / p_adec->output_format.i_channels;
/* **** First check if we have a valid output **** */
if( ( !p_adec->p_aout_input )||
( p_adec->output_format.i_channels !=
p_adec->p_context->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 =
i_channels_maps[p_adec->p_context->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,
i_samplesperchannel );
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,
i_samplesperchannel );
memcpy( p_aout_buffer->p_buffer,
p_adec->p_output,
p_aout_buffer->i_nb_bytes );
aout_DecPlay( p_adec->p_aout, p_adec->p_aout_input, p_aout_buffer );
return;
}
/*****************************************************************************
* EndThread: thread destruction
*****************************************************************************
* This function is called when the thread ends after a sucessful
* initialization.
*****************************************************************************/
void E_( EndThread_Audio )( adec_thread_t *p_adec )
{
FREE( p_adec->format.p_data );
if( p_adec->p_aout_input )
{
aout_DecDelete( p_adec->p_aout, p_adec->p_aout_input );
}
}
/*****************************************************************************
* audio.h: video decoder using ffmpeg library
*****************************************************************************
* Copyright (C) 1999-2001 VideoLAN
* $Id: audio.h,v 1.1 2002/10/28 06:26:11 fenrir 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.
*****************************************************************************/
/* for an audio stream */
typedef struct waveformatex_s
{
u16 i_formattag;
u16 i_channels;
u32 i_samplespersec;
u32 i_avgbytespersec;
u16 i_blockalign;
u16 i_bitspersample;
u16 i_size; /* the extra size in bytes */
u8 *p_data; /* The extra data */
} waveformatex_t;
typedef struct adec_thread_s
{
DECODER_THREAD_COMMON
waveformatex_t format;
/*
* Output properties
*/
u8 * p_output;
aout_instance_t * p_aout; /* opaque */
aout_input_t * p_aout_input; /* opaque */
audio_sample_format_t output_format;
audio_date_t date;
} adec_thread_t;
/*
* Local prototypes
*/
int E_( InitThread_Audio ) ( adec_thread_t * );
void E_( EndThread_Audio ) ( adec_thread_t * );
void E_( DecodeThread_Audio ) ( adec_thread_t * );
......@@ -2,7 +2,7 @@
* ffmpeg.c: video decoder using ffmpeg library
*****************************************************************************
* Copyright (C) 1999-2001 VideoLAN
* $Id: ffmpeg.c,v 1.11 2002/10/27 16:58:13 gbazin Exp $
* $Id: ffmpeg.c,v 1.12 2002/10/28 06:26:11 fenrir Exp $
*
* Authors: Laurent Aimar <fenrir@via.ecp.fr>
*
......@@ -28,6 +28,7 @@
#include <vlc/vlc.h>
#include <vlc/vout.h>
#include <vlc/aout.h>
#include <vlc/decoder.h>
#include <vlc/input.h>
......@@ -47,19 +48,23 @@
#include "postprocessing/postprocessing.h"
#include "ffmpeg.h"
#include "video.h" // video ffmpeg specific
#include "audio.h" // audio ffmpeg specific
/*
* Local prototypes
*/
static int OpenDecoder ( vlc_object_t * );
static int RunDecoder ( decoder_fifo_t * );
static int InitThread ( videodec_thread_t * );
static void EndThread ( videodec_thread_t * );
static void DecodeThread ( videodec_thread_t * );
static int InitThread ( generic_thread_t * );
static void EndThread ( generic_thread_t * );
static int b_ffmpeginit = 0;
static int ffmpeg_GetFfmpegCodec( vlc_fourcc_t, int *, int *, char ** );
/*****************************************************************************
* Module descriptor
*****************************************************************************/
......@@ -131,7 +136,7 @@ vlc_module_begin();
"force chrominance deringing",
"force chrominance deringing (override other settings)" );
set_description( _("ffmpeg video decoder((MS)MPEG4,SVQ1,H263)") );
set_description( _("ffmpeg audio/video decoder((MS)MPEG4,SVQ1,H263,WMV,WMA)") );
set_capability( "decoder", 70 );
set_callbacks( OpenDecoder, NULL );
vlc_module_end();
......@@ -146,7 +151,7 @@ static int OpenDecoder( vlc_object_t *p_this )
{
decoder_fifo_t *p_fifo = (decoder_fifo_t*) p_this;
if( ffmpeg_GetFfmpegCodec( p_fifo->i_fourcc, NULL, NULL ) )
if( ffmpeg_GetFfmpegCodec( p_fifo->i_fourcc, NULL, NULL, NULL ) )
{
p_fifo->pf_run = RunDecoder;
return VLC_SUCCESS;
......@@ -155,41 +160,59 @@ static int OpenDecoder( vlc_object_t *p_this )
return VLC_EGENERIC;
}
typedef union decoder_thread_u
{
generic_thread_t gen;
adec_thread_t audio;
vdec_thread_t video;
} decoder_thread_t;
/*****************************************************************************
* RunDecoder: this function is called just after the thread is created
*****************************************************************************/
static int RunDecoder( decoder_fifo_t *p_fifo )
{
videodec_thread_t *p_vdec;
generic_thread_t *p_decoder;
int b_error;
if ( !(p_vdec = (videodec_thread_t*)malloc( sizeof(videodec_thread_t))) )
if ( !(p_decoder = malloc( sizeof( decoder_thread_t ) ) ) )
{
msg_Err( p_fifo, "out of memory" );
DecoderError( p_fifo );
return( -1 );
}
memset( p_vdec, 0, sizeof( videodec_thread_t ) );
memset( p_decoder, 0, sizeof( decoder_thread_t ) );
p_vdec->p_fifo = p_fifo;
p_decoder->p_fifo = p_fifo;
if( InitThread( p_vdec ) != 0 )
if( InitThread( p_decoder ) != 0 )
{
msg_Err( p_fifo, "initialization failed" );
DecoderError( p_fifo );
return( -1 );
}
while( (!p_vdec->p_fifo->b_die) && (!p_vdec->p_fifo->b_error) )
while( (!p_decoder->p_fifo->b_die) && (!p_decoder->p_fifo->b_error) )
{
DecodeThread( p_vdec );
switch( p_decoder->i_cat )
{
case VIDEO_ES:
E_( DecodeThread_Video )( (vdec_thread_t*)p_decoder );
break;
case AUDIO_ES:
E_( DecodeThread_Audio )( (adec_thread_t*)p_decoder );
break;
}
}
if( ( b_error = p_vdec->p_fifo->b_error ) )
if( ( b_error = p_decoder->p_fifo->b_error ) )
{
DecoderError( p_vdec->p_fifo );
DecoderError( p_decoder->p_fifo );
}
EndThread( p_vdec );
EndThread( p_decoder );
if( b_error )
{
......@@ -199,355 +222,6 @@ static int RunDecoder( decoder_fifo_t *p_fifo )
return( 0 );
}
/*****************************************************************************
* locales Functions
*****************************************************************************/
#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 ) )
#define FREE( p ) if( p ) free( p ); p = NULL
static void ffmpeg_ParseBitMapInfoHeader( bitmapinfoheader_t *p_bh,
u8 *p_data )
{
p_bh->i_size = GetDWLE( p_data );
p_bh->i_width = GetDWLE( p_data + 4 );
p_bh->i_height = GetDWLE( p_data + 8 );
p_bh->i_planes = GetWLE( p_data + 12 );
p_bh->i_bitcount = GetWLE( p_data + 14 );
p_bh->i_compression = GetDWLE( p_data + 16 );
p_bh->i_sizeimage = GetDWLE( p_data + 20 );
p_bh->i_xpelspermeter = GetDWLE( p_data + 24 );
p_bh->i_ypelspermeter = GetDWLE( p_data + 28 );
p_bh->i_clrused = GetDWLE( p_data + 32 );
p_bh->i_clrimportant = GetDWLE( p_data + 36 );
if( p_bh->i_size > 40 )
{
p_bh->i_data = p_bh->i_size - 40;
if( ( p_bh->p_data = malloc( p_bh->i_data ) ) )
{
memcpy( p_bh->p_data, p_data + 40, p_bh->i_data );
}
else
{
p_bh->i_data = 0;
}
}
else
{
p_bh->i_data = 0;
p_bh->p_data = NULL;
}
}
static void GetPESData( u8 *p_buf, int i_max, pes_packet_t *p_pes )
{
int i_copy;
int i_count;
data_packet_t *p_data;
i_count = 0;
p_data = p_pes->p_first;
while( p_data != NULL && i_count < i_max )
{
i_copy = __MIN( p_data->p_payload_end - p_data->p_payload_start,
i_max - i_count );
if( i_copy > 0 )
{
memcpy( p_buf,
p_data->p_payload_start,
i_copy );
}
p_data = p_data->p_next;
i_count += i_copy;
p_buf += i_copy;
}
if( i_count < i_max )
{
memset( p_buf, 0, i_max - i_count );
}
}
/* Check if we have a Vout with good parameters */
static int ffmpeg_CheckVout( vout_thread_t *p_vout,
int i_width,
int i_height,
int i_aspect,
int i_chroma )
{
if( !p_vout )
{
return( 0 );
}
if( !i_chroma )
{
/* we will try to make conversion */
i_chroma = VLC_FOURCC('I','4','2','0');
}
if( ( p_vout->render.i_width != i_width )||
( p_vout->render.i_height != i_height )||
( p_vout->render.i_chroma != i_chroma )||
( p_vout->render.i_aspect !=
ffmpeg_FfAspect( i_width, i_height, i_aspect ) ) )
{
return( 0 );
}
else
{
return( 1 );
}
}
/* Return a Vout */
static vout_thread_t *ffmpeg_CreateVout( videodec_thread_t *p_vdec,
int i_width,
int i_height,
int i_aspect,
int i_chroma )
{
vout_thread_t *p_vout;
if( (!i_width)||(!i_height) )
{
return( NULL ); /* Can't create a new vout without display size */
}
if( !i_chroma )
{
/* we make conversion if possible*/
i_chroma = VLC_FOURCC('I','4','2','0');
msg_Warn( p_vdec->p_fifo, "Internal chroma conversion (FIXME)");
/* It's mainly for I410 -> I420 conversion that I've made,
it's buggy and very slow */
}
i_aspect = ffmpeg_FfAspect( i_width, i_height, i_aspect );
/* Spawn a video output if there is none. First we look for our children,
* then we look for any other vout that might be available. */
p_vout = vlc_object_find( p_vdec->p_fifo, VLC_OBJECT_VOUT,
FIND_CHILD );
if( !p_vout )
{
p_vout = vlc_object_find( p_vdec->p_fifo, VLC_OBJECT_VOUT,
FIND_ANYWHERE );
}
if( p_vout )
{
if( !ffmpeg_CheckVout( p_vout,
i_width, i_height, i_aspect,i_chroma ) )
{
/* We are not interested in this format, close this vout */
vlc_object_detach( p_vout );
vlc_object_release( p_vout );
vout_DestroyThread( p_vout );
p_vout = NULL;
}
else
{
/* This video output is cool! Hijack it. */
vlc_object_detach( p_vout );
vlc_object_attach( p_vout, p_vdec->p_fifo );
vlc_object_release( p_vout );
}