|
|
|
@ -2,7 +2,7 @@ |
|
|
|
* ffmpeg.c: video decoder using ffmpeg library |
|
|
|
***************************************************************************** |
|
|
|
* Copyright (C) 1999-2001 VideoLAN |
|
|
|
* $Id: ffmpeg.c,v 1.7 2002/05/10 02:04:17 fenrir Exp $ |
|
|
|
* $Id: ffmpeg.c,v 1.8 2002/05/12 06:51:08 fenrir Exp $ |
|
|
|
* |
|
|
|
* Authors: Laurent Aimar <fenrir@via.ecp.fr> |
|
|
|
* |
|
|
|
@ -81,8 +81,8 @@ MODULE_CONFIG_START |
|
|
|
MODULE_CONFIG_STOP |
|
|
|
|
|
|
|
MODULE_INIT_START |
|
|
|
SET_DESCRIPTION( "ffmpeg video decoder module (MSMPEG4,MPEG4)" ) |
|
|
|
ADD_CAPABILITY( DECODER, 50 ) |
|
|
|
SET_DESCRIPTION( "ffmpeg video decoder (MSMPEG4v3,MPEG4)" ) |
|
|
|
ADD_CAPABILITY( DECODER, 70 ) |
|
|
|
ADD_SHORTCUT( "ffmpeg" ) |
|
|
|
MODULE_INIT_STOP |
|
|
|
|
|
|
|
@ -119,8 +119,6 @@ static int decoder_Probe( u8 *pi_type ) |
|
|
|
{ |
|
|
|
switch( *pi_type ) |
|
|
|
{ |
|
|
|
/* case( MPEG1_VIDEO_ES ): marche pas pr le moment
|
|
|
|
case( MPEG2_VIDEO_ES ): */ |
|
|
|
case( MSMPEG4_VIDEO_ES ): |
|
|
|
case( MPEG4_VIDEO_ES ): |
|
|
|
return( 0 ); |
|
|
|
@ -167,7 +165,7 @@ static pes_packet_t *__PES_GET( decoder_fifo_t *p_fifo ) |
|
|
|
p_pes = p_fifo->p_first; |
|
|
|
|
|
|
|
vlc_mutex_unlock( &p_fifo->data_lock ); |
|
|
|
|
|
|
|
|
|
|
|
return( p_pes ); |
|
|
|
} |
|
|
|
/* free the first pes and go to next */ |
|
|
|
@ -197,99 +195,53 @@ static void __PES_NEXT( decoder_fifo_t *p_fifo ) |
|
|
|
vlc_mutex_unlock( &p_fifo->data_lock ); |
|
|
|
} |
|
|
|
|
|
|
|
static void __PACKET_REINIT( videodec_thread_t *p_vdec ) |
|
|
|
static __inline__ void __GetFrame( videodec_thread_t *p_vdec ) |
|
|
|
{ |
|
|
|
pes_packet_t *p_pes; |
|
|
|
pes_packet_t *p_pes; |
|
|
|
data_packet_t *p_data; |
|
|
|
byte_t *p_buffer; |
|
|
|
|
|
|
|
p_pes = __PES_GET( p_vdec->p_fifo ); |
|
|
|
if( p_vdec->p_fifo->b_die ) |
|
|
|
p_vdec->i_pts = p_pes->i_pts; |
|
|
|
|
|
|
|
while( ( !p_pes->i_nb_data )||( !p_pes->i_pes_size ) ) |
|
|
|
{ |
|
|
|
return; |
|
|
|
__PES_NEXT( p_vdec->p_fifo ); |
|
|
|
p_pes = __PES_GET( p_vdec->p_fifo ); |
|
|
|
} |
|
|
|
p_vdec->p_data = p_pes->p_first; |
|
|
|
p_vdec->p_buff = p_vdec->p_data->p_payload_start; |
|
|
|
p_vdec->i_data_size = p_vdec->p_data->p_payload_end - |
|
|
|
p_vdec->p_data->p_payload_start; |
|
|
|
} |
|
|
|
|
|
|
|
static void __PACKET_NEXT( videodec_thread_t *p_vdec ) |
|
|
|
{ |
|
|
|
p_vdec->i_framesize = p_pes->i_pes_size; |
|
|
|
if( p_pes->i_nb_data == 1 ) |
|
|
|
{ |
|
|
|
p_vdec->p_framedata = p_pes->p_first->p_payload_start; |
|
|
|
return; |
|
|
|
} |
|
|
|
/* get a buffer and gather all data packet */ |
|
|
|
p_vdec->p_framedata = p_buffer = malloc( p_pes->i_pes_size ); |
|
|
|
p_data = p_pes->p_first; |
|
|
|
do |
|
|
|
{ |
|
|
|
p_vdec->p_data = p_vdec->p_data->p_next; |
|
|
|
if( !p_vdec->p_data ) |
|
|
|
{ |
|
|
|
__PES_NEXT( p_vdec->p_fifo ); |
|
|
|
if( p_vdec->p_fifo->b_die ) |
|
|
|
{ |
|
|
|
return; |
|
|
|
} |
|
|
|
__PACKET_REINIT( p_vdec ); |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
p_vdec->p_buff = p_vdec->p_data->p_payload_start; |
|
|
|
p_vdec->i_data_size = p_vdec->p_data->p_payload_end - |
|
|
|
p_vdec->p_data->p_payload_start; |
|
|
|
} |
|
|
|
|
|
|
|
} while( ( p_vdec->i_data_size <= 0 ) |
|
|
|
||( p_vdec->p_data->b_discard_payload ) ); |
|
|
|
FAST_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 void __PACKET_FILL( videodec_thread_t *p_vdec ) |
|
|
|
{ |
|
|
|
if( p_vdec->i_data_size <= 0 ) |
|
|
|
{ |
|
|
|
__PACKET_NEXT( p_vdec ); |
|
|
|
} |
|
|
|
} |
|
|
|
/* call only two times so inline for faster */ |
|
|
|
static __inline__ void __ConvertAVPictureToPicture( AVPicture *p_avpicture, |
|
|
|
picture_t *p_picture ) |
|
|
|
static __inline__ void __NextFrame( videodec_thread_t *p_vdec ) |
|
|
|
{ |
|
|
|
int i_plane, i_line, i_inc; |
|
|
|
u8 *p_dest,*p_src; |
|
|
|
|
|
|
|
for( i_plane = 0; i_plane < __MIN(p_picture->i_planes, 3); i_plane++ ) |
|
|
|
pes_packet_t *p_pes; |
|
|
|
|
|
|
|
p_pes = __PES_GET( p_vdec->p_fifo ); |
|
|
|
if( p_pes->i_nb_data != 1 ) |
|
|
|
{ |
|
|
|
p_dest = p_picture->p[i_plane].p_pixels; |
|
|
|
p_src = p_avpicture->data[i_plane]; |
|
|
|
if( ( !p_dest )||( !p_src )) |
|
|
|
{ |
|
|
|
return; |
|
|
|
} |
|
|
|
i_inc = __MIN( p_picture->p[i_plane].i_pitch, |
|
|
|
p_avpicture->linesize[i_plane] ); |
|
|
|
for( i_line = 0; i_line < p_picture->p[i_plane].i_lines; i_line++ ) |
|
|
|
{ |
|
|
|
FAST_MEMCPY( p_dest, |
|
|
|
p_src, |
|
|
|
i_inc ); |
|
|
|
p_dest += p_picture->p[i_plane].i_pitch; |
|
|
|
p_src += p_avpicture->linesize[i_plane]; |
|
|
|
} |
|
|
|
free( p_vdec->p_framedata ); /* FIXME keep this buffer */ |
|
|
|
} |
|
|
|
__PES_NEXT( p_vdec->p_fifo ); |
|
|
|
} |
|
|
|
|
|
|
|
static __inline__ u32 __FfmpegChromaToFourCC( int i_ffmpegchroma ) |
|
|
|
{ |
|
|
|
switch( i_ffmpegchroma ) |
|
|
|
{ |
|
|
|
case( PIX_FMT_YUV420P ): |
|
|
|
case( PIX_FMT_YUV422 ): |
|
|
|
return FOURCC_I420; |
|
|
|
case( PIX_FMT_RGB24 ): |
|
|
|
return FOURCC_RV24; |
|
|
|
case( PIX_FMT_YUV422P ): |
|
|
|
return FOURCC_Y422; |
|
|
|
case( PIX_FMT_YUV444P ): |
|
|
|
case( PIX_FMT_BGR24 ): |
|
|
|
default: |
|
|
|
return( 0 ); |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
* decoder_Run: this function is called just after the thread is created |
|
|
|
*****************************************************************************/ |
|
|
|
@ -355,14 +307,11 @@ static int InitThread( videodec_thread_t *p_vdec ) |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
memset( &p_vdec->format, 0, sizeof( bitmapinfoheader_t ) ); |
|
|
|
intf_ErrMsg( "vdec error: cannot get informations" ); |
|
|
|
return( -1 ); |
|
|
|
} |
|
|
|
/* some codec need to have height and width initialized (msmepg4,mpeg4) */ |
|
|
|
/* we cannot create vout because we don't know what chroma */ |
|
|
|
|
|
|
|
/*init ffmpeg */ |
|
|
|
/* TODO: add a global variable to know if init was already done
|
|
|
|
in case we use it also for audio */ |
|
|
|
if( !b_ffmpeginit ) |
|
|
|
{ |
|
|
|
avcodec_init(); |
|
|
|
@ -377,11 +326,6 @@ static int InitThread( videodec_thread_t *p_vdec ) |
|
|
|
|
|
|
|
switch( p_vdec->p_config->i_type) |
|
|
|
{ |
|
|
|
case( MPEG1_VIDEO_ES ): /* marche pas pr le moment */ |
|
|
|
case( MPEG2_VIDEO_ES ): |
|
|
|
p_vdec->p_codec = avcodec_find_decoder( CODEC_ID_MPEG1VIDEO ); |
|
|
|
p_vdec->psz_namecodec = "MPEG-1"; |
|
|
|
break; |
|
|
|
case( MSMPEG4_VIDEO_ES): |
|
|
|
p_vdec->p_codec = avcodec_find_decoder( CODEC_ID_MSMPEG4 ); |
|
|
|
p_vdec->psz_namecodec = "MS MPEG-4/divx"; |
|
|
|
@ -407,7 +351,7 @@ static int InitThread( videodec_thread_t *p_vdec ) |
|
|
|
|
|
|
|
p_vdec->p_context->width = p_vdec->format.i_width; |
|
|
|
p_vdec->p_context->height = p_vdec->format.i_height; |
|
|
|
p_vdec->p_context->pix_fmt = PIX_FMT_YUV420P; |
|
|
|
p_vdec->p_context->pix_fmt = PIX_FMT_YUV420P; /* I420 */ |
|
|
|
|
|
|
|
if (avcodec_open(p_vdec->p_context, p_vdec->p_codec) < 0) |
|
|
|
{ |
|
|
|
@ -420,18 +364,37 @@ static int InitThread( videodec_thread_t *p_vdec ) |
|
|
|
intf_WarnMsg( 1, "vdec info: ffmpeg codec (%s) started", |
|
|
|
p_vdec->psz_namecodec ); |
|
|
|
} |
|
|
|
/* destroy each p_vout */ |
|
|
|
/* create vout */ |
|
|
|
|
|
|
|
p_vdec->p_vout = vout_CreateThread( |
|
|
|
NULL, |
|
|
|
p_vdec->format.i_width, |
|
|
|
p_vdec->format.i_height, |
|
|
|
FOURCC_I420, |
|
|
|
VOUT_ASPECT_FACTOR * p_vdec->format.i_width / |
|
|
|
p_vdec->format.i_height ); |
|
|
|
|
|
|
|
if( !p_vdec->p_vout ) |
|
|
|
{ |
|
|
|
intf_ErrMsg( "vdec error: can't open vout, aborting" ); |
|
|
|
avcodec_close( p_vdec->p_context ); |
|
|
|
intf_WarnMsg(1, "vdec info: ffmpeg codec (%s) stopped", |
|
|
|
p_vdec->psz_namecodec); |
|
|
|
return( -1 ); |
|
|
|
} |
|
|
|
|
|
|
|
vlc_mutex_lock( &p_vout_bank->lock ); |
|
|
|
if( p_vout_bank->i_count != 0 ) |
|
|
|
{ |
|
|
|
vlc_mutex_unlock( &p_vout_bank->lock ); |
|
|
|
vout_DestroyThread( p_vout_bank->pp_vout[ 0 ], NULL ); |
|
|
|
vlc_mutex_lock( &p_vout_bank->lock ); |
|
|
|
p_vout_bank->i_count--; |
|
|
|
p_vout_bank->pp_vout[ 0 ] = NULL; |
|
|
|
p_vout_bank->i_count--; |
|
|
|
} |
|
|
|
p_vout_bank->i_count++; |
|
|
|
p_vout_bank->pp_vout[0] = p_vdec->p_vout; |
|
|
|
vlc_mutex_unlock( &p_vout_bank->lock ); |
|
|
|
__PACKET_REINIT( p_vdec ); |
|
|
|
|
|
|
|
return( 0 ); |
|
|
|
} |
|
|
|
|
|
|
|
@ -472,98 +435,38 @@ static void EndThread( videodec_thread_t *p_vdec ) |
|
|
|
|
|
|
|
static void DecodeThread( videodec_thread_t *p_vdec ) |
|
|
|
{ |
|
|
|
int i_len; |
|
|
|
int i_plane; |
|
|
|
int i_status; |
|
|
|
int b_gotpicture; |
|
|
|
int b_convert; |
|
|
|
mtime_t i_pts; |
|
|
|
AVPicture avpicture; /* ffmpeg picture */ |
|
|
|
u32 i_chroma; |
|
|
|
picture_t *p_picture; /* videolan picture */ |
|
|
|
picture_t *p_pic; /* videolan picture */ |
|
|
|
/* we have to get a frame stored in a pes
|
|
|
|
give it to ffmpeg decoder |
|
|
|
and send the image to the output */ |
|
|
|
/* when we have the first image we create the video output */ |
|
|
|
|
|
|
|
i_pts = 0 ; |
|
|
|
do |
|
|
|
{ |
|
|
|
__PACKET_FILL( p_vdec ); |
|
|
|
if( (p_vdec->p_fifo->b_die)||(p_vdec->p_fifo->b_error) ) |
|
|
|
{ |
|
|
|
return; |
|
|
|
} |
|
|
|
/* save pts */ |
|
|
|
if( !i_pts ) {i_pts = __PES_GET( p_vdec->p_fifo )->i_pts;} |
|
|
|
|
|
|
|
i_len = avcodec_decode_video( p_vdec->p_context, |
|
|
|
&avpicture, |
|
|
|
&b_gotpicture, |
|
|
|
p_vdec->p_buff, |
|
|
|
p_vdec->i_data_size); |
|
|
|
|
|
|
|
if( i_len < 0 ) |
|
|
|
{ |
|
|
|
intf_WarnMsg( 3, "vdec error: cannot decode one frame (%d bytes)", |
|
|
|
p_vdec->i_data_size ); |
|
|
|
__PES_NEXT( p_vdec->p_fifo ); |
|
|
|
__PACKET_REINIT( p_vdec ); |
|
|
|
return; |
|
|
|
} |
|
|
|
p_vdec->i_data_size -= i_len; |
|
|
|
p_vdec->p_buff += i_len; |
|
|
|
} while( !b_gotpicture ); |
|
|
|
__GetFrame( p_vdec ); |
|
|
|
|
|
|
|
if( !(i_chroma =__FfmpegChromaToFourCC( p_vdec->p_context->pix_fmt ) ) ) |
|
|
|
i_status = avcodec_decode_video( p_vdec->p_context, |
|
|
|
&avpicture, |
|
|
|
&b_gotpicture, |
|
|
|
p_vdec->p_framedata, |
|
|
|
p_vdec->i_framesize); |
|
|
|
__NextFrame( p_vdec ); |
|
|
|
|
|
|
|
if( i_status < 0 ) |
|
|
|
{ |
|
|
|
b_convert = 1; |
|
|
|
i_chroma = FOURCC_I420; |
|
|
|
intf_WarnMsg( 2, "vdec error: cannot decode one frame (%d bytes)", |
|
|
|
p_vdec->i_framesize ); |
|
|
|
return; |
|
|
|
} |
|
|
|
else |
|
|
|
if( !b_gotpicture ) |
|
|
|
{ |
|
|
|
b_convert = 0; |
|
|
|
} |
|
|
|
|
|
|
|
/* Send decoded frame to vout */ |
|
|
|
if( !p_vdec->p_vout ) |
|
|
|
{ |
|
|
|
/* create vout */ |
|
|
|
|
|
|
|
/* ffmpeg set it for us with some codec */ |
|
|
|
if( (!p_vdec->format.i_width )||(!p_vdec->format.i_height) ) |
|
|
|
{ |
|
|
|
p_vdec->format.i_width = p_vdec->p_context->width; |
|
|
|
p_vdec->format.i_height = p_vdec->p_context->height; |
|
|
|
} |
|
|
|
/* calculate i_aspect */ |
|
|
|
p_vdec->i_aspect = VOUT_ASPECT_FACTOR * p_vdec->format.i_width / |
|
|
|
p_vdec->format.i_height; |
|
|
|
p_vdec->i_chroma = i_chroma; |
|
|
|
|
|
|
|
p_vdec->p_vout = vout_CreateThread( |
|
|
|
NULL, |
|
|
|
p_vdec->format.i_width, |
|
|
|
p_vdec->format.i_height, |
|
|
|
p_vdec->i_chroma, |
|
|
|
p_vdec->i_aspect ); |
|
|
|
|
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
if( !p_vdec->p_vout ) |
|
|
|
{ |
|
|
|
intf_ErrMsg( "vdec error: can't open vout, aborting" ); |
|
|
|
p_vdec->p_fifo->b_error = 1; |
|
|
|
return; |
|
|
|
} |
|
|
|
vlc_mutex_lock( &p_vout_bank->lock ); |
|
|
|
p_vout_bank->pp_vout[ 0 ] = p_vdec->p_vout; |
|
|
|
p_vout_bank->i_count++; |
|
|
|
vlc_mutex_unlock( &p_vout_bank->lock ); |
|
|
|
} |
|
|
|
/* Send decoded frame to vout */ |
|
|
|
|
|
|
|
while( (p_picture = vout_CreatePicture( p_vdec->p_vout, |
|
|
|
0, /* ??? */ |
|
|
|
0, /* ??? */ |
|
|
|
0) ) /* ??? */ |
|
|
|
== NULL ) |
|
|
|
while( !(p_pic = vout_CreatePicture( p_vdec->p_vout, 0, 0, 0 ) ) ) |
|
|
|
{ |
|
|
|
if( p_vdec->p_fifo->b_die || p_vdec->p_fifo->b_error ) |
|
|
|
{ |
|
|
|
@ -572,43 +475,28 @@ static void DecodeThread( videodec_thread_t *p_vdec ) |
|
|
|
msleep( VOUT_OUTMEM_SLEEP ); |
|
|
|
} |
|
|
|
|
|
|
|
if( b_convert ) |
|
|
|
{ |
|
|
|
/* we convert in a supported format */ |
|
|
|
int i_status; |
|
|
|
u8 *p_buff; |
|
|
|
AVPicture avpicture_tmp; |
|
|
|
|
|
|
|
p_buff = malloc( avpicture_get_size( PIX_FMT_YUV420P, |
|
|
|
p_vdec->p_context->width, |
|
|
|
p_vdec->p_context->height) ); |
|
|
|
avpicture_fill( &avpicture_tmp, |
|
|
|
p_buff, |
|
|
|
PIX_FMT_YUV420P, |
|
|
|
p_vdec->p_context->width, |
|
|
|
p_vdec->p_context->height ); |
|
|
|
|
|
|
|
i_status = img_convert( &avpicture_tmp, |
|
|
|
PIX_FMT_YUV420P, |
|
|
|
&avpicture, |
|
|
|
p_vdec->p_context->pix_fmt, |
|
|
|
p_vdec->p_context->width, |
|
|
|
p_vdec->p_context->height ); |
|
|
|
if( i_status < 0 ) |
|
|
|
{ |
|
|
|
intf_ErrMsg( "vdec error: cannot convert picture in known chroma" ); |
|
|
|
return; |
|
|
|
} |
|
|
|
__ConvertAVPictureToPicture( &avpicture_tmp, p_picture ); |
|
|
|
free( p_buff ); /* FIXME try to alloc only one time */ |
|
|
|
} |
|
|
|
else |
|
|
|
for( i_plane = 0; i_plane < p_pic->i_planes; i_plane++ ) |
|
|
|
{ |
|
|
|
__ConvertAVPictureToPicture( &avpicture, p_picture ); |
|
|
|
int i_size; |
|
|
|
int i_line; |
|
|
|
byte_t *p_dest = p_pic->p[i_plane].p_pixels; |
|
|
|
byte_t *p_src = avpicture.data[i_plane]; |
|
|
|
if( ( !p_dest )||( !p_src )) |
|
|
|
{ |
|
|
|
break; |
|
|
|
} |
|
|
|
i_size = __MIN( p_pic->p[i_plane].i_pitch, |
|
|
|
avpicture.linesize[i_plane] ); |
|
|
|
for( i_line = 0; i_line < p_pic->p[i_plane].i_lines; i_line++ ) |
|
|
|
{ |
|
|
|
FAST_MEMCPY( p_dest, p_src, i_size ); |
|
|
|
p_dest += p_pic->p[i_plane].i_pitch; |
|
|
|
p_src += avpicture.linesize[i_plane]; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
vout_DatePicture( p_vdec->p_vout, p_picture, i_pts ); |
|
|
|
vout_DisplayPicture( p_vdec->p_vout, p_picture ); |
|
|
|
vout_DatePicture( p_vdec->p_vout, p_pic, p_vdec->i_pts); |
|
|
|
vout_DisplayPicture( p_vdec->p_vout, p_pic ); |
|
|
|
|
|
|
|
return; |
|
|
|
} |
|
|
|
|