From: Christophe Massiot Date: Fri, 20 Feb 2004 18:34:28 +0000 (+0000) Subject: * modules/codec/ffmpeg/encoder.c, modules/stream_out/transcode.c : X-Git-Tag: 0.7.1~107 X-Git-Url: https://git.sesse.net/?a=commitdiff_plain;h=8c816feb508b59236416d1b85f96c6b924bcc73c;p=vlc * modules/codec/ffmpeg/encoder.c, modules/stream_out/transcode.c : - new ffmpeg options available from the command-line : interleaved video, noise reduction, rate control parameters, i_quant_factor, trellis quantification, mpeg4 matrix, and thread count - portable functions to use ffmpeg multithreading capabilities on all platforms - hurry up mode now turns off rd and trellis, and also raises the noise reduction parameter (thus noise reduction is mandatory with hurry up) - if threads=1, no ffmpeg thread is launched, but the ffmpeg encoding will be done in a separate thread than the packetizer and decoder - fixed a bug with mp3 decoding and weird ffmpeg return code --- diff --git a/include/vlc_codec.h b/include/vlc_codec.h index a3960ce119..f62e99ce77 100644 --- a/include/vlc_codec.h +++ b/include/vlc_codec.h @@ -2,7 +2,7 @@ * vlc_codec.h: codec related structures ***************************************************************************** * Copyright (C) 1999-2003 VideoLAN - * $Id: vlc_codec.h,v 1.7 2003/11/27 22:44:50 massiot Exp $ + * $Id: vlc_codec.h,v 1.8 2004/02/20 18:34:28 massiot Exp $ * * Authors: Gildas Bazin * @@ -118,6 +118,14 @@ struct encoder_t vlc_bool_t b_strict_rc; vlc_bool_t b_pre_me; vlc_bool_t b_hurry_up; + vlc_bool_t b_interlace; + int i_rc_buffer_size; + float f_rc_buffer_aggressivity; + float f_i_quant_factor; + int i_noise_reduction; + vlc_bool_t b_mpeg4_matrix; + int i_threads; + vlc_bool_t b_trellis; }; /** diff --git a/modules/codec/ffmpeg/encoder.c b/modules/codec/ffmpeg/encoder.c index f4360d340e..1b776021e1 100644 --- a/modules/codec/ffmpeg/encoder.c +++ b/modules/codec/ffmpeg/encoder.c @@ -2,7 +2,7 @@ * encoder.c: video and audio encoder using the ffmpeg library ***************************************************************************** * Copyright (C) 1999-2001 VideoLAN - * $Id: encoder.c,v 1.21 2004/01/04 15:32:13 fenrir Exp $ + * $Id: encoder.c,v 1.22 2004/02/20 18:34:28 massiot Exp $ * * Authors: Laurent Aimar * Gildas Bazin @@ -41,7 +41,9 @@ #include "ffmpeg.h" #define AVCODEC_MAX_VIDEO_FRAME_SIZE (3*1024*1024) -#define HURRY_UP_GUARD (200000) +#define HURRY_UP_GUARD1 (1000000) +#define HURRY_UP_GUARD2 (1000000) +#define HURRY_UP_GUARD3 (200000) /***************************************************************************** * Local prototypes @@ -52,6 +54,31 @@ void E_(CloseEncoder)( vlc_object_t * ); static block_t *EncodeVideo( encoder_t *, picture_t * ); static block_t *EncodeAudio( encoder_t *, aout_buffer_t * ); +struct thread_context_t; +static int FfmpegThread( struct thread_context_t *p_context ); +static int FfmpegExecute( AVCodecContext *s, + int (*pf_func)(AVCodecContext *c2, void *arg2), + void **arg, int *ret, int count ); + +/***************************************************************************** + * thread_context_t : for multithreaded encoding + *****************************************************************************/ +#if LIBAVCODEC_BUILD >= 4702 +struct thread_context_t +{ + VLC_COMMON_MEMBERS + + AVCodecContext *p_context; + int (* pf_func)(AVCodecContext *c, void *arg); + void *arg; + int i_ret; + + vlc_mutex_t lock; + vlc_cond_t cond; + vlc_bool_t b_work, b_done; +}; +#endif + /***************************************************************************** * encoder_sys_t : ffmpeg encoder descriptor *****************************************************************************/ @@ -70,10 +97,11 @@ struct encoder_sys_t char *p_buffer_out; /* - * Videoo properties + * Video properties */ mtime_t i_last_ref_pts; mtime_t i_buggy_pts_detect; + vlc_bool_t b_inited; /* * Audio properties @@ -86,6 +114,9 @@ struct encoder_sys_t /***************************************************************************** * OpenEncoder: probe the encoder *****************************************************************************/ +extern const int16_t ff_mpeg4_default_intra_matrix[]; +extern const int16_t ff_mpeg4_default_non_intra_matrix[]; + int E_(OpenEncoder)( vlc_object_t *p_this ) { encoder_t *p_enc = (encoder_t *)p_this; @@ -145,6 +176,7 @@ int E_(OpenEncoder)( vlc_object_t *p_this ) p_sys->p_buffer_out = NULL; p_sys->p_buffer = NULL; + p_sys->b_inited = 0; p_sys->p_context = p_context = avcodec_alloc_context(); @@ -189,6 +221,19 @@ int E_(OpenEncoder)( vlc_object_t *p_this ) p_context->frame_rate = p_enc->fmt_in.video.i_frame_rate; p_context->frame_rate_base= p_enc->fmt_in.video.i_frame_rate_base; + /* Defaults from ffmpeg.c */ + p_context->qblur = 0.5; + p_context->qcompress = 0.5; + p_context->b_quant_offset = 1.25; + p_context->b_quant_factor = 1.25; + p_context->i_quant_offset = 0.0; + p_context->i_quant_factor = -0.8; + + p_context->gop_size = p_enc->i_key_int > 0 ? p_enc->i_key_int : 50; + p_context->max_b_frames = + __MIN( p_enc->i_b_frames, FF_MAX_B_FRAMES ); + p_context->b_frame_strategy = 0; + #if LIBAVCODEC_BUILD >= 4687 p_context->sample_aspect_ratio = (AVRational){ p_enc->fmt_in.video.i_aspect * @@ -206,8 +251,21 @@ int E_(OpenEncoder)( vlc_object_t *p_this ) if ( p_enc->b_strict_rc ) { p_context->rc_max_rate = p_enc->fmt_out.i_bitrate; - p_context->rc_buffer_size = p_context->bit_rate / 2; - p_context->rc_buffer_aggressivity = 1000.0; /* FIXME */ + p_context->rc_buffer_size = p_enc->i_rc_buffer_size; + p_context->rc_buffer_aggressivity = p_enc->f_rc_buffer_aggressivity; + } + + if ( p_enc->f_i_quant_factor != 0.0 ) + { + p_context->i_quant_factor = p_enc->f_i_quant_factor; + } + + p_context->noise_reduction = p_enc->i_noise_reduction; + + if ( p_enc->b_mpeg4_matrix ) + { + p_context->intra_matrix = ff_mpeg4_default_intra_matrix; + p_context->inter_matrix = ff_mpeg4_default_non_intra_matrix; } if ( p_enc->b_pre_me ) @@ -215,6 +273,35 @@ int E_(OpenEncoder)( vlc_object_t *p_this ) p_context->pre_me = 1; p_context->me_pre_cmp = FF_CMP_CHROMA; } + + if ( p_enc->b_interlace ) + { + p_context->flags |= CODEC_FLAG_INTERLACED_DCT; +#if LIBAVCODEC_BUILD >= 4698 + p_context->flags |= CODEC_FLAG_INTERLACED_ME; +#endif + } + + if ( p_enc->b_trellis ) + { + p_context->flags |= CODEC_FLAG_TRELLIS_QUANT; + } + + if ( p_enc->i_threads >= 1 ) + { + p_context->thread_count = p_enc->i_threads; + } + + if( p_enc->i_vtolerance > 0 ) + { + p_context->bit_rate_tolerance = p_enc->i_vtolerance; + } + + p_context->mb_qmin = p_context->qmin = p_enc->i_qmin; + p_context->mb_qmax = p_context->qmax = p_enc->i_qmax; + p_context->max_qdiff = 3; + + p_context->mb_decision = p_enc->i_hq; } else if( p_enc->fmt_in.i_cat == AUDIO_ES ) { @@ -228,20 +315,6 @@ int E_(OpenEncoder)( vlc_object_t *p_this ) /* Misc parameters */ p_context->bit_rate = p_enc->fmt_out.i_bitrate; - p_context->gop_size = p_enc->i_key_int > 0 ? p_enc->i_key_int : 50; - p_context->max_b_frames = - __MIN( p_enc->i_b_frames, FF_MAX_B_FRAMES ); - p_context->b_frame_strategy = 0; - p_context->b_quant_factor = 2.0; - - if( p_enc->i_vtolerance > 0 ) - { - p_context->bit_rate_tolerance = p_enc->i_vtolerance; - } - p_context->qmin = p_enc->i_qmin; - p_context->qmax = p_enc->i_qmax; - - p_context->mb_decision = p_enc->i_hq; if( i_codec_id == CODEC_ID_RAWVIDEO ) { @@ -297,6 +370,80 @@ int E_(OpenEncoder)( vlc_object_t *p_this ) return VLC_SUCCESS; } +/**************************************************************************** + * Ffmpeg threading system + ****************************************************************************/ +#if LIBAVCODEC_BUILD >= 4702 +static int FfmpegThread( struct thread_context_t *p_context ) +{ + while ( !p_context->b_die && !p_context->b_error ) + { + vlc_mutex_lock( &p_context->lock ); + while ( !p_context->b_work && !p_context->b_die && !p_context->b_error ) + { + vlc_cond_wait( &p_context->cond, &p_context->lock ); + } + p_context->b_work = 0; + vlc_mutex_unlock( &p_context->lock ); + if ( p_context->b_die || p_context->b_error ) + break; + + if ( p_context->pf_func ) + { + p_context->i_ret = p_context->pf_func( p_context->p_context, + p_context->arg ); + } + + vlc_mutex_lock( &p_context->lock ); + p_context->b_done = 1; + vlc_cond_signal( &p_context->cond ); + vlc_mutex_unlock( &p_context->lock ); + } + + return 0; +} + +static int FfmpegExecute( AVCodecContext *s, + int (*pf_func)(AVCodecContext *c2, void *arg2), + void **arg, int *ret, int count ) +{ + struct thread_context_t ** pp_contexts = + (struct thread_context_t **)s->thread_opaque; + int i; + + /* Note, we can be certain that this is not called with the same + * AVCodecContext by different threads at the same time */ + for ( i = 0; i < count; i++ ) + { + vlc_mutex_lock( &pp_contexts[i]->lock ); + pp_contexts[i]->arg = arg[i]; + pp_contexts[i]->pf_func = pf_func; + pp_contexts[i]->i_ret = 12345; + pp_contexts[i]->b_work = 1; + vlc_cond_signal( &pp_contexts[i]->cond ); + vlc_mutex_unlock( &pp_contexts[i]->lock ); + } + for ( i = 0; i < count; i++ ) + { + vlc_mutex_lock( &pp_contexts[i]->lock ); + while ( !pp_contexts[i]->b_done ) + { + vlc_cond_wait( &pp_contexts[i]->cond, &pp_contexts[i]->lock ); + } + pp_contexts[i]->b_done = 0; + pp_contexts[i]->pf_func = NULL; + vlc_mutex_unlock( &pp_contexts[i]->lock ); + + if ( ret ) + { + ret[i] = pp_contexts[i]->i_ret; + } + } + + return 0; +} +#endif + /**************************************************************************** * EncodeVideo: the whole thing ****************************************************************************/ @@ -305,7 +452,38 @@ static block_t *EncodeVideo( encoder_t *p_enc, picture_t *p_pict ) encoder_sys_t *p_sys = p_enc->p_sys; AVFrame frame; int i_out, i_plane; - vlc_bool_t b_hurry_up = 0; + +#if LIBAVCODEC_BUILD >= 4702 + if ( !p_sys->b_inited && p_enc->i_threads >= 1 ) + { + struct thread_context_t ** pp_contexts; + int i; + + p_sys->b_inited = 1; + pp_contexts = malloc( sizeof(struct thread_context_t *) + * p_enc->i_threads ); + p_sys->p_context->thread_opaque = (void *)pp_contexts; + + for ( i = 0; i < p_enc->i_threads; i++ ) + { + pp_contexts[i] = vlc_object_create( p_enc, + sizeof(struct thread_context_t) ); + pp_contexts[i]->p_context = p_sys->p_context; + vlc_mutex_init( p_enc, &pp_contexts[i]->lock ); + vlc_cond_init( p_enc, &pp_contexts[i]->cond ); + pp_contexts[i]->b_work = 0; + pp_contexts[i]->b_done = 0; + if ( vlc_thread_create( pp_contexts[i], "encoder", FfmpegThread, + VLC_THREAD_PRIORITY_VIDEO, VLC_FALSE ) ) + { + msg_Err( p_enc, "cannot spawn encoder thread, expect to die soon" ); + return NULL; + } + } + + p_sys->p_context->execute = FfmpegExecute; + } +#endif memset( &frame, 0, sizeof( AVFrame ) ); for( i_plane = 0; i_plane < p_pict->i_planes; i_plane++ ) @@ -320,12 +498,83 @@ static block_t *EncodeVideo( encoder_t *p_enc, picture_t *p_pict ) p_enc->fmt_out.i_codec == VLC_FOURCC( 'm', 'p', '2', 'v' ) ) { frame.pts = p_pict->date; - if ( frame.pts && mdate() + HURRY_UP_GUARD > frame.pts - && p_enc->b_hurry_up ) + + if ( p_enc->b_hurry_up ) { - msg_Dbg( p_enc, "hurry up mode" ); - p_sys->p_context->mb_decision = FF_MB_DECISION_SIMPLE; - b_hurry_up = 1; + mtime_t current_date = mdate(); +#if LIBAVCODEC_BUILD >= 4702 + struct thread_context_t ** pp_contexts = + (struct thread_context_t **)p_sys->p_context->thread_opaque; +#endif + + if ( frame.pts && current_date + HURRY_UP_GUARD3 > frame.pts ) + { + p_sys->p_context->mb_decision = FF_MB_DECISION_SIMPLE; + p_sys->p_context->flags &= ~CODEC_FLAG_TRELLIS_QUANT; +#if LIBAVCODEC_BUILD >= 4702 + if ( p_enc->i_threads >= 2 ) + { + int i; + + for ( i = 0; i < p_enc->i_threads; i++ ) + { + vlc_thread_set_priority( pp_contexts[i], + VLC_THREAD_PRIORITY_VIDEO + 4 ); + } + } +#endif + msg_Dbg( p_enc, "hurry up mode 3" ); + } + else + { + p_sys->p_context->mb_decision = p_enc->i_hq; + + if ( frame.pts && current_date + HURRY_UP_GUARD2 > frame.pts ) + { + p_sys->p_context->flags &= ~CODEC_FLAG_TRELLIS_QUANT; +#if LIBAVCODEC_BUILD >= 4702 + if ( p_enc->i_threads >= 2 ) + { + int i; + + for ( i = 0; i < p_enc->i_threads; i++ ) + { + vlc_thread_set_priority( pp_contexts[i], + VLC_THREAD_PRIORITY_VIDEO + 2 ); + } + } +#endif + msg_Dbg( p_enc, "hurry up mode 2" ); + } + else + { + if ( p_enc->b_trellis ) + p_sys->p_context->flags |= CODEC_FLAG_TRELLIS_QUANT; + +#if LIBAVCODEC_BUILD >= 4702 + if ( p_enc->i_threads >= 2 ) + { + int i; + + for ( i = 0; i < p_enc->i_threads; i++ ) + { + vlc_thread_set_priority( pp_contexts[i], + VLC_THREAD_PRIORITY_VIDEO ); + } + } +#endif + } + } + + if ( frame.pts && current_date + HURRY_UP_GUARD1 > frame.pts ) + { + p_sys->p_context->noise_reduction = p_enc->i_noise_reduction + + (HURRY_UP_GUARD1 + current_date - frame.pts) / 1500; + } + else + { + p_sys->p_context->noise_reduction = p_enc->i_noise_reduction; + } } } else @@ -335,21 +584,17 @@ static block_t *EncodeVideo( encoder_t *p_enc, picture_t *p_pict ) /* Let ffmpeg select the frame type */ frame.pict_type = 0; + + frame.interlaced_frame = !p_pict->b_progressive; frame.repeat_pict = p_pict->i_nb_fields; #if LIBAVCODEC_BUILD >= 4685 - frame.interlaced_frame = !p_pict->b_progressive; frame.top_field_first = p_pict->b_top_field_first; #endif i_out = avcodec_encode_video( p_sys->p_context, p_sys->p_buffer_out, AVCODEC_MAX_VIDEO_FRAME_SIZE, &frame ); - if ( b_hurry_up ) - { - p_sys->p_context->mb_decision = p_enc->i_hq; - } - if( i_out > 0 ) { block_t *p_block = block_New( p_enc, i_out ); @@ -492,6 +737,26 @@ void E_(CloseEncoder)( vlc_object_t *p_this ) encoder_t *p_enc = (encoder_t *)p_this; encoder_sys_t *p_sys = p_enc->p_sys; +#if LIBAVCODEC_BUILD >= 4702 + if ( p_sys->b_inited && p_enc->i_threads >= 1 ) + { + int i; + struct thread_context_t ** pp_contexts = + (struct thread_context_t **)p_sys->p_context->thread_opaque; + for ( i = 0; i < p_enc->i_threads; i++ ) + { + pp_contexts[i]->b_die = 1; + vlc_cond_signal( &pp_contexts[i]->cond ); + vlc_thread_join( pp_contexts[i] ); + vlc_mutex_destroy( &pp_contexts[i]->lock ); + vlc_cond_destroy( &pp_contexts[i]->cond ); + vlc_object_destroy( pp_contexts[i] ); + } + + free(pp_contexts); + } +#endif + avcodec_close( p_sys->p_context ); free( p_sys->p_context ); diff --git a/modules/stream_out/transcode.c b/modules/stream_out/transcode.c index b5f71c2257..858c1b4a7d 100644 --- a/modules/stream_out/transcode.c +++ b/modules/stream_out/transcode.c @@ -2,7 +2,7 @@ * transcode.c: transcoding stream output module ***************************************************************************** * Copyright (C) 2003-2004 VideoLAN - * $Id: transcode.c,v 1.75 2004/02/18 13:21:33 fenrir Exp $ + * $Id: transcode.c,v 1.76 2004/02/20 18:34:28 massiot Exp $ * * Authors: Laurent Aimar * Gildas Bazin @@ -61,6 +61,8 @@ static int transcode_video_ffmpeg_process( sout_stream_t *, sout_stream_id_t *, static int transcode_video_ffmpeg_getframebuf( struct AVCodecContext *, AVFrame *); +static int EncoderThread( struct sout_stream_sys_t * p_sys ); + static int pi_channels_maps[6] = { 0, @@ -82,9 +84,19 @@ vlc_module_begin(); set_callbacks( Open, Close ); vlc_module_end(); +#define PICTURE_RING_SIZE 64 + struct sout_stream_sys_t { - sout_stream_t *p_out; + VLC_COMMON_MEMBERS + + sout_stream_t * p_out; + sout_stream_id_t * id_video; + sout_buffer_t * p_buffers; + vlc_mutex_t lock_out; + vlc_cond_t cond; + picture_t * pp_pics[PICTURE_RING_SIZE]; + int i_first_pic, i_last_pic; vlc_fourcc_t i_acodec; /* codec audio (0 if not transcode) */ int i_sample_rate; @@ -103,9 +115,17 @@ struct sout_stream_sys_t int i_qmax; vlc_bool_t i_hq; vlc_bool_t b_deinterlace; + vlc_bool_t b_interlace; vlc_bool_t b_strict_rc; vlc_bool_t b_pre_me; vlc_bool_t b_hurry_up; + int i_rc_buffer_size; + float f_rc_buffer_aggressivity; + float f_i_quant_factor; + int i_noise_reduction; + vlc_bool_t b_mpeg4_matrix; + int i_threads; + vlc_bool_t b_trellis; int i_crop_top; int i_crop_bottom; @@ -125,8 +145,7 @@ static int Open( vlc_object_t *p_this ) sout_stream_sys_t *p_sys; char *codec; - p_sys = malloc( sizeof( sout_stream_sys_t ) ); - memset( p_sys, 0, sizeof(struct sout_stream_sys_t) ); + p_sys = vlc_object_create( p_this, sizeof( sout_stream_sys_t ) ); p_sys->p_out = sout_stream_new( p_stream->p_sout, p_stream->psz_next ); p_sys->f_scale = 1; @@ -134,11 +153,16 @@ static int Open( vlc_object_t *p_this ) p_sys->i_key_int = -1; p_sys->i_qmin = 2; p_sys->i_qmax = 31; + p_sys->f_i_quant_factor = 0.0; #if LIBAVCODEC_BUILD >= 4673 p_sys->i_hq = FF_MB_DECISION_SIMPLE; #else p_sys->i_hq = VLC_FALSE; #endif + p_sys->i_rc_buffer_size = 224*1024*8 * 3/2; + p_sys->f_rc_buffer_aggressivity = 0.1; + p_sys->i_threads = 0; + p_sys->b_trellis = 0; if( ( codec = sout_cfg_find_value( p_stream->p_cfg, "acodec" ) ) ) { @@ -168,7 +192,7 @@ static int Open( vlc_object_t *p_this ) msg_Dbg( p_stream, "codec audio=%4.4s %dHz %d channels %dKb/s", fcc, p_sys->i_sample_rate, p_sys->i_channels, - p_sys->i_abitrate / 1024 ); + p_sys->i_abitrate / 1000 ); } if( ( codec = sout_cfg_find_value( p_stream->p_cfg, "vcodec" ) ) ) @@ -208,6 +232,10 @@ static int Open( vlc_object_t *p_this ) { p_sys->b_deinterlace = VLC_TRUE; } + if( sout_cfg_find( p_stream->p_cfg, "interlace" ) ) + { + p_sys->b_interlace = VLC_TRUE; + } if( sout_cfg_find( p_stream->p_cfg, "strict_rc" ) ) { p_sys->b_strict_rc = VLC_TRUE; @@ -219,6 +247,28 @@ static int Open( vlc_object_t *p_this ) if( sout_cfg_find( p_stream->p_cfg, "hurry_up" ) ) { p_sys->b_hurry_up = VLC_TRUE; + /* hurry up mode needs noise reduction, even small */ + p_sys->i_noise_reduction = 1; + } + if( ( val = sout_cfg_find_value( p_stream->p_cfg, "rc_buffer_size" ) ) ) + { + p_sys->i_rc_buffer_size = atoi( val ); + } + if( ( val = sout_cfg_find_value( p_stream->p_cfg, "rc_buffer_aggressivity" ) ) ) + { + p_sys->f_rc_buffer_aggressivity = atof( val ); + } + if( ( val = sout_cfg_find_value( p_stream->p_cfg, "i_quant_factor" ) ) ) + { + p_sys->f_i_quant_factor = atof( val ); + } + if( ( val = sout_cfg_find_value( p_stream->p_cfg, "noise_reduction" ) ) ) + { + p_sys->i_noise_reduction = atoi( val ); + } + if( ( val = sout_cfg_find_value( p_stream->p_cfg, "mpeg4_matrix" ) ) ) + { + p_sys->b_mpeg4_matrix = VLC_TRUE; } /* crop */ if( ( val = sout_cfg_find_value( p_stream->p_cfg, "croptop" ) ) ) @@ -279,10 +329,18 @@ static int Open( vlc_object_t *p_this ) { p_sys->i_qmax = atoi( val ); } + if( ( val = sout_cfg_find_value( p_stream->p_cfg, "threads" ) ) ) + { + p_sys->i_threads = atoi( val ); + } + if( sout_cfg_find( p_stream->p_cfg, "trellis" ) ) + { + p_sys->b_trellis = VLC_TRUE; + } msg_Dbg( p_stream, "codec video=%4.4s %dx%d scaling: %f %dkb/s", fcc, p_sys->i_width, p_sys->i_height, p_sys->f_scale, - p_sys->i_vbitrate / 1024 ); + p_sys->i_vbitrate / 1000 ); } if( !p_sys->p_out ) @@ -315,7 +373,7 @@ static void Close( vlc_object_t * p_this ) sout_stream_sys_t *p_sys = p_stream->p_sys; sout_stream_delete( p_sys->p_out ); - free( p_sys ); + vlc_object_destroy( p_sys ); } struct sout_stream_id_t @@ -798,6 +856,12 @@ static int transcode_audio_ffmpeg_process( sout_stream_t *p_stream, i_buffer -= i_used; p_buffer += i_used; + + if ( id->i_buffer_pos < 0 ) + { + msg_Warn( p_stream, "weird error audio decoding"); + break; + } } else { @@ -1084,6 +1148,14 @@ static int transcode_video_ffmpeg_new( sout_stream_t *p_stream, id->p_encoder->b_strict_rc = p_sys->b_strict_rc; id->p_encoder->b_pre_me = p_sys->b_pre_me; id->p_encoder->b_hurry_up = p_sys->b_hurry_up; + id->p_encoder->b_interlace = p_sys->b_interlace; + id->p_encoder->i_rc_buffer_size = p_sys->i_rc_buffer_size; + id->p_encoder->f_rc_buffer_aggressivity = p_sys->f_rc_buffer_aggressivity; + id->p_encoder->f_i_quant_factor = p_sys->f_i_quant_factor; + id->p_encoder->i_noise_reduction = p_sys->i_noise_reduction; + id->p_encoder->b_mpeg4_matrix = p_sys->b_mpeg4_matrix; + id->p_encoder->i_threads = p_sys->i_threads; + id->p_encoder->b_trellis = p_sys->b_trellis; id->p_ff_pic = avcodec_alloc_frame(); id->p_ff_pic_tmp0 = NULL; @@ -1108,6 +1180,25 @@ static int transcode_video_ffmpeg_new( sout_stream_t *p_stream, id->b_enc_inited = VLC_FALSE; + if ( p_sys->i_threads >= 1 ) + { + p_sys->id_video = id; + vlc_mutex_init( p_stream, &p_sys->lock_out ); + vlc_cond_init( p_stream, &p_sys->cond ); + memset( p_sys->pp_pics, 0, sizeof(p_sys->pp_pics) ); + p_sys->i_first_pic = 0; + p_sys->i_last_pic = 0; + p_sys->p_buffers = NULL; + p_sys->b_die = p_sys->b_error = 0; + if( vlc_thread_create( p_sys, "encoder", EncoderThread, + VLC_THREAD_PRIORITY_VIDEO, VLC_FALSE ) ) + { + vlc_object_destroy( id->p_encoder ); + msg_Err( p_stream, "cannot spawn encoder thread" ); + return VLC_EGENERIC; + } + } + return VLC_SUCCESS; } @@ -1151,6 +1242,13 @@ static void transcode_video_ffmpeg_close ( sout_stream_t *p_stream, { img_resample_close( id->p_vresample ); } + if ( p_stream->p_sys->i_threads >= 1 ) + { + p_stream->p_sys->b_die = 1; + vlc_thread_join( p_stream->p_sys ); + vlc_mutex_destroy( &p_stream->p_sys->lock_out ); + vlc_cond_destroy( &p_stream->p_sys->cond ); + } } static int transcode_video_ffmpeg_process( sout_stream_t *p_stream, @@ -1172,9 +1270,11 @@ static int transcode_video_ffmpeg_process( sout_stream_t *p_stream, for( ;; ) { block_t *p_block; - picture_t pic; + picture_t * p_pic; int i_plane; + p_pic = malloc(sizeof(picture_t)); + /* decode frame */ frame = id->p_ff_pic; p_sys->i_input_pts = in->i_pts; @@ -1336,6 +1436,8 @@ static int transcode_video_ffmpeg_process( sout_stream_t *p_stream, id->ff_dec_c->pix_fmt, id->ff_dec_c->width, id->ff_dec_c->height ); + id->p_ff_pic_tmp0->interlaced_frame = 0; + id->p_ff_pic_tmp0->repeat_pict = frame->repeat_pict; frame = id->p_ff_pic_tmp0; } @@ -1362,6 +1464,11 @@ static int transcode_video_ffmpeg_process( sout_stream_t *p_stream, (AVPicture*)frame, id->ff_dec_c->pix_fmt, id->ff_dec_c->width, id->ff_dec_c->height ); + id->p_ff_pic_tmp1->interlaced_frame = frame->interlaced_frame; + id->p_ff_pic_tmp1->repeat_pict = frame->repeat_pict; +#if LIBAVCODEC_BUILD >= 4684 + id->p_ff_pic_tmp1->top_field_first = frame->top_field_first; +#endif frame = id->p_ff_pic_tmp1; } @@ -1399,29 +1506,42 @@ static int transcode_video_ffmpeg_process( sout_stream_t *p_stream, img_resample( id->p_vresample, (AVPicture*)id->p_ff_pic_tmp2, (AVPicture*)frame ); + id->p_ff_pic_tmp2->interlaced_frame = frame->interlaced_frame; + id->p_ff_pic_tmp2->repeat_pict = frame->repeat_pict; +#if LIBAVCODEC_BUILD >= 4684 + id->p_ff_pic_tmp2->top_field_first = frame->top_field_first; +#endif frame = id->p_ff_pic_tmp2; } /* Encoding */ - vout_InitPicture( VLC_OBJECT(p_stream), &pic, + vout_InitPicture( VLC_OBJECT(p_stream), p_pic, id->p_encoder->fmt_in.i_codec, id->f_dst.video.i_width, id->f_dst.video.i_height, id->f_dst.video.i_width * VOUT_ASPECT_FACTOR / id->f_dst.video.i_height ); - for( i_plane = 0; i_plane < pic.i_planes; i_plane++ ) + for( i_plane = 0; i_plane < p_pic->i_planes; i_plane++ ) { - pic.p[i_plane].p_pixels = frame->data[i_plane]; - pic.p[i_plane].i_pitch = frame->linesize[i_plane]; + p_pic->p[i_plane].i_pitch = frame->linesize[i_plane]; + if ( p_sys->i_threads >= 1 ) + { + p_pic->p[i_plane].p_pixels = malloc(p_pic->p[i_plane].i_lines * p_pic->p[i_plane].i_pitch); + p_stream->p_vlc->pf_memcpy(p_pic->p[i_plane].p_pixels, frame->data[i_plane], p_pic->p[i_plane].i_lines * p_pic->p[i_plane].i_pitch); + } + else + { + p_pic->p[i_plane].p_pixels = frame->data[i_plane]; + } } /* Set the pts of the frame being encoded */ - pic.date = p_sys->i_output_pts; + p_pic->date = p_sys->i_output_pts; - pic.b_progressive = 1; /* ffmpeg doesn't support interlaced encoding */ - pic.i_nb_fields = frame->repeat_pict; + p_pic->b_progressive = !frame->interlaced_frame; + p_pic->i_nb_fields = frame->repeat_pict; #if LIBAVCODEC_BUILD >= 4684 - pic.b_top_field_first = frame->top_field_first; + p_pic->b_top_field_first = frame->top_field_first; #endif /* Interpolate the next PTS @@ -1432,7 +1552,78 @@ static int transcode_video_ffmpeg_process( sout_stream_t *p_stream, id->ff_dec_c->frame_rate_base / (2 * id->ff_dec_c->frame_rate); } - p_block = id->p_encoder->pf_encode_video( id->p_encoder, &pic ); + if ( p_sys->i_threads >= 1 ) + { + vlc_mutex_lock( &p_sys->lock_out ); + p_sys->pp_pics[p_sys->i_last_pic++] = p_pic; + p_sys->i_last_pic %= PICTURE_RING_SIZE; + *out = p_sys->p_buffers; + p_sys->p_buffers = NULL; + vlc_cond_signal( &p_sys->cond ); + vlc_mutex_unlock( &p_sys->lock_out ); + } + else + { + block_t *p_block; + p_block = id->p_encoder->pf_encode_video( id->p_encoder, p_pic ); + while( p_block ) + { + sout_buffer_t *p_out; + block_t *p_prev_block = p_block; + + p_out = sout_BufferNew( p_stream->p_sout, p_block->i_buffer ); + memcpy( p_out->p_buffer, p_block->p_buffer, p_block->i_buffer); + p_out->i_dts = p_block->i_dts; + p_out->i_pts = p_block->i_pts; + p_out->i_length = p_block->i_length; + sout_BufferChain( out, p_out ); + + p_block = p_block->p_next; + block_Release( p_prev_block ); + } + free( p_pic ); + } + + if( i_data <= 0 ) + { + return VLC_SUCCESS; + } + } + + return VLC_SUCCESS; +} + +static int EncoderThread( sout_stream_sys_t * p_sys ) +{ + sout_stream_t * p_stream = p_sys->p_out; + sout_stream_id_t * id = p_sys->id_video; + picture_t * p_pic; + int i_plane; + sout_buffer_t * p_buffer; + + while ( !p_sys->b_die && !p_sys->b_error ) + { + block_t *p_block; + + vlc_mutex_lock( &p_sys->lock_out ); + while ( p_sys->i_last_pic == p_sys->i_first_pic ) + { + vlc_cond_wait( &p_sys->cond, &p_sys->lock_out ); + if ( p_sys->b_die || p_sys->b_error ) + break; + } + if ( p_sys->b_die || p_sys->b_error ) + { + vlc_mutex_unlock( &p_sys->lock_out ); + break; + } + + p_pic = p_sys->pp_pics[p_sys->i_first_pic++]; + p_sys->i_first_pic %= PICTURE_RING_SIZE; + vlc_mutex_unlock( &p_sys->lock_out ); + + p_block = id->p_encoder->pf_encode_video( id->p_encoder, p_pic ); + vlc_mutex_lock( &p_sys->lock_out ); while( p_block ) { sout_buffer_t *p_out; @@ -1443,19 +1634,41 @@ static int transcode_video_ffmpeg_process( sout_stream_t *p_stream, p_out->i_dts = p_block->i_dts; p_out->i_pts = p_block->i_pts; p_out->i_length = p_block->i_length; - sout_BufferChain( out, p_out ); + sout_BufferChain( &p_sys->p_buffers, p_out ); p_block = p_block->p_next; block_Release( p_prev_block ); } + vlc_mutex_unlock( &p_sys->lock_out ); - if( i_data <= 0 ) + for( i_plane = 0; i_plane < p_pic->i_planes; i_plane++ ) { - return VLC_SUCCESS; + free( p_pic->p[i_plane].p_pixels ); } + free( p_pic ); } - return VLC_SUCCESS; + while ( p_sys->i_last_pic != p_sys->i_first_pic ) + { + p_pic = p_sys->pp_pics[p_sys->i_first_pic++]; + p_sys->i_first_pic %= PICTURE_RING_SIZE; + + for( i_plane = 0; i_plane < p_pic->i_planes; i_plane++ ) + { + free( p_pic->p[i_plane].p_pixels ); + } + free( p_pic ); + } + + p_buffer = p_sys->p_buffers; + while ( p_buffer != NULL ) + { + sout_buffer_t * p_next = p_buffer->p_next; + sout_BufferDelete( p_stream->p_sout, p_buffer ); + p_buffer = p_next; + } + + return 0; } /*****************************************************************************