X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Fcodec%2Favcodec%2Fencoder.c;h=90e76e65868982f0021f05a4da18d7476864134b;hb=c5010ad330c808122ca16cdcbbd1b20d330b1d4b;hp=acd092ff7fdcaf0a4dc8aaf557604dcda329112b;hpb=a48fa0327705ef15ba4356f48f375eee0fb3dca9;p=vlc diff --git a/modules/codec/avcodec/encoder.c b/modules/codec/avcodec/encoder.c index acd092ff7f..90e76e6586 100644 --- a/modules/codec/avcodec/encoder.c +++ b/modules/codec/avcodec/encoder.c @@ -33,11 +33,12 @@ #endif #include -#include #include #include #include -#include +#include +#include +#include /* ffmpeg header */ #define HAVE_MMX 1 @@ -50,8 +51,6 @@ #endif #include "avcodec.h" -#include "chroma.h" -#include "fourcc.h" #define HURRY_UP_GUARD1 (450000) #define HURRY_UP_GUARD2 (300000) @@ -59,6 +58,8 @@ #define MAX_FRAME_DELAY (FF_MAX_B_FRAMES + 2) +#define RAW_AUDIO_FRAME_SIZE (2048) + /***************************************************************************** * Local prototypes *****************************************************************************/ @@ -69,10 +70,6 @@ static block_t *EncodeVideo( encoder_t *, picture_t * ); static block_t *EncodeAudio( encoder_t *, aout_buffer_t * ); struct thread_context_t; -static void* FfmpegThread( vlc_object_t *p_this ); -static int FfmpegExecute( AVCodecContext *s, - int (*pf_func)(AVCodecContext *c2, void *arg2), - void **arg, int *ret, int count ); /***************************************************************************** * thread_context_t : for multithreaded encoding @@ -120,6 +117,7 @@ struct encoder_sys_t /* * Audio properties */ + int i_sample_bytes; int i_frame_size; int i_samples_delay; mtime_t i_pts; @@ -143,9 +141,7 @@ struct encoder_sys_t int i_quality; /* for VBR */ float f_lumi_masking, f_dark_masking, f_p_masking, f_border_masking; int i_luma_elim, i_chroma_elim; -#if LIBAVCODEC_VERSION_INT >= ((51<<16)+(40<<8)+4) int i_aac_profile; /* AAC profile to use.*/ -#endif /* Used to work around stupid timestamping behaviour in libavcodec */ uint64_t i_framenum; mtime_t pi_delay_pts[MAX_FRAME_DELAY]; @@ -154,13 +150,11 @@ struct encoder_sys_t static const char *const ppsz_enc_options[] = { "keyint", "bframes", "vt", "qmin", "qmax", "hq", "rc-buffer-size", "rc-buffer-aggressivity", "pre-me", "hurry-up", - "interlace", "i-quant-factor", "noise-reduction", "mpeg4-matrix", + "interlace", "interlace-me", "i-quant-factor", "noise-reduction", "mpeg4-matrix", "trellis", "qscale", "strict", "lumi-masking", "dark-masking", "p-masking", "border-masking", "luma-elim-threshold", "chroma-elim-threshold", -#if LIBAVCODEC_VERSION_INT >= ((51<<16)+(40<<8)+4) "aac-profile", -#endif NULL }; @@ -202,17 +196,19 @@ static const uint16_t mpeg4_default_non_intra_matrix[64] = { int OpenEncoder( vlc_object_t *p_this ) { encoder_t *p_enc = (encoder_t *)p_this; - encoder_sys_t *p_sys = p_enc->p_sys; + encoder_sys_t *p_sys; AVCodecContext *p_context; AVCodec *p_codec; int i_codec_id, i_cat; const char *psz_namecodec; - vlc_value_t val; + float f_val; + char *psz_val; + int i_val; if( !GetFfmpegCodec( p_enc->fmt_out.i_codec, &i_cat, &i_codec_id, &psz_namecodec ) ) { - if( GetFfmpegChroma( p_enc->fmt_out.i_codec ) < 0 ) + if( TestFfmpegChroma( -1, p_enc->fmt_out.i_codec ) != VLC_SUCCESS ) { /* handed chroma output */ return VLC_EGENERIC; @@ -225,7 +221,7 @@ int OpenEncoder( vlc_object_t *p_this ) if( p_enc->fmt_out.i_cat == VIDEO_ES && i_cat != VIDEO_ES ) { msg_Err( p_enc, "\"%s\" is not a video encoder", psz_namecodec ); - intf_UserFatal( p_enc, false, _("Streaming / Transcoding failed"), + dialog_Fatal( p_enc, _("Streaming / Transcoding failed"), _("\"%s\" is no video encoder."), psz_namecodec ); return VLC_EGENERIC; } @@ -233,27 +229,46 @@ int OpenEncoder( vlc_object_t *p_this ) if( p_enc->fmt_out.i_cat == AUDIO_ES && i_cat != AUDIO_ES ) { msg_Err( p_enc, "\"%s\" is not an audio encoder", psz_namecodec ); - intf_UserFatal( p_enc, false, _("Streaming / Transcoding failed"), + dialog_Fatal( p_enc, _("Streaming / Transcoding failed"), _("\"%s\" is no audio encoder."), psz_namecodec ); return VLC_EGENERIC; } + if( p_enc->fmt_out.i_cat == SPU_ES ) + { + /* We don't support subtitle encoding */ + return VLC_EGENERIC; + } + /* Initialization must be done before avcodec_find_encoder() */ InitLibavcodec( p_this ); p_codec = avcodec_find_encoder( i_codec_id ); if( !p_codec ) { - msg_Err( p_enc, "cannot find encoder %s", psz_namecodec ); - intf_UserFatal( p_enc, false, _("Streaming / Transcoding failed"), - _("VLC could not find encoder \"%s\"."), psz_namecodec ); + msg_Err( p_enc, "cannot find encoder %s\n" +"*** Your FFMPEG installation is crippled. ***\n" +"*** Please check with your FFMPEG packager. ***\n" +"*** This is NOT a VLC media player issue. ***", psz_namecodec ); + + dialog_Fatal( p_enc, _("Streaming / Transcoding failed"), _( +/* I have had enough of all these MPEG-3 transcoding bug reports. + * Downstream packager, you had better not patch this out, or I will be really + * annoyed. Think about it - you don't want to fork the VLC translation files, + * do you? -- Courmisch, 2008-10-22 */ +"It seems your FFMPEG (libavcodec) installation lacks the following encoder:\n" +"%s.\n" +"If you don't know how to fix this, ask for support from your distribution.\n" +"\n" +"This is not an error inside VLC media player.\n" +"Do not contact the VideoLAN project about this issue.\n"), + psz_namecodec ); return VLC_EGENERIC; } /* Allocate the memory needed to store the encoder's structure */ - if( ( p_sys = (encoder_sys_t *)malloc(sizeof(encoder_sys_t)) ) == NULL ) + if( ( p_sys = calloc( 1, sizeof(encoder_sys_t) ) ) == NULL ) return VLC_ENOMEM; - memset( p_sys, 0, sizeof(encoder_sys_t) ); p_enc->p_sys = p_sys; p_sys->p_codec = p_codec; @@ -265,7 +280,8 @@ int OpenEncoder( vlc_object_t *p_this ) p_sys->i_buffer_out = 0; p_sys->p_context = p_context = avcodec_alloc_context(); - p_context->debug = config_GetInt( p_enc, "ffmpeg-debug" ); + p_sys->p_context->codec_id = p_sys->p_codec->id; + p_context->debug = var_InheritInteger( p_enc, "ffmpeg-debug" ); p_context->opaque = (void *)p_this; /* Set CPU capabilities */ @@ -291,105 +307,77 @@ int OpenEncoder( vlc_object_t *p_this ) config_ChainParse( p_enc, ENC_CFG_PREFIX, ppsz_enc_options, p_enc->p_cfg ); - var_Get( p_enc, ENC_CFG_PREFIX "keyint", &val ); - p_sys->i_key_int = val.i_int; - - var_Get( p_enc, ENC_CFG_PREFIX "bframes", &val ); - p_sys->i_b_frames = val.i_int; + p_sys->i_key_int = var_GetInteger( p_enc, ENC_CFG_PREFIX "keyint" ); + p_sys->i_b_frames = var_GetInteger( p_enc, ENC_CFG_PREFIX "bframes" ); + p_sys->i_vtolerance = var_GetInteger( p_enc, ENC_CFG_PREFIX "vt" ) * 1000; + p_sys->b_interlace = var_GetBool( p_enc, ENC_CFG_PREFIX "interlace" ); + p_sys->b_interlace_me = var_GetBool( p_enc, ENC_CFG_PREFIX "interlace-me" ); + p_sys->b_pre_me = var_GetBool( p_enc, ENC_CFG_PREFIX "pre-me" ); + p_sys->b_hurry_up = var_GetBool( p_enc, ENC_CFG_PREFIX "hurry-up" ); - var_Get( p_enc, ENC_CFG_PREFIX "vt", &val ); - p_sys->i_vtolerance = val.i_int * 1000; - - var_Get( p_enc, ENC_CFG_PREFIX "interlace", &val ); - p_sys->b_interlace = val.b_bool; - - var_Get( p_enc, ENC_CFG_PREFIX "interlace-me", &val ); - p_sys->b_interlace_me = val.b_bool; - - var_Get( p_enc, ENC_CFG_PREFIX "pre-me", &val ); - p_sys->b_pre_me = val.b_bool; - - var_Get( p_enc, ENC_CFG_PREFIX "hurry-up", &val ); - p_sys->b_hurry_up = val.b_bool; if( p_sys->b_hurry_up ) { /* hurry up mode needs noise reduction, even small */ p_sys->i_noise_reduction = 1; } - var_Get( p_enc, ENC_CFG_PREFIX "rc-buffer-size", &val ); - p_sys->i_rc_buffer_size = val.i_int; - var_Get( p_enc, ENC_CFG_PREFIX "rc-buffer-aggressivity", &val ); - p_sys->f_rc_buffer_aggressivity = val.f_float; + p_sys->i_rc_buffer_size = var_GetInteger( p_enc, ENC_CFG_PREFIX "rc-buffer-size" ); + p_sys->f_rc_buffer_aggressivity = var_GetFloat( p_enc, ENC_CFG_PREFIX "rc-buffer-aggressivity" ); + p_sys->f_i_quant_factor = var_GetFloat( p_enc, ENC_CFG_PREFIX "i-quant-factor" ); + p_sys->i_noise_reduction = var_GetInteger( p_enc, ENC_CFG_PREFIX "noise-reduction" ); + p_sys->b_mpeg4_matrix = var_GetBool( p_enc, ENC_CFG_PREFIX "mpeg4-matrix" ); - var_Get( p_enc, ENC_CFG_PREFIX "i-quant-factor", &val ); - p_sys->f_i_quant_factor = val.f_float; + f_val = var_GetFloat( p_enc, ENC_CFG_PREFIX "qscale" ); + if( f_val < 0.01 || f_val > 255.0 ) f_val = 0; + p_sys->i_quality = (int)(FF_QP2LAMBDA * f_val + 0.5); - var_Get( p_enc, ENC_CFG_PREFIX "noise-reduction", &val ); - p_sys->i_noise_reduction = val.i_int; - - var_Get( p_enc, ENC_CFG_PREFIX "mpeg4-matrix", &val ); - p_sys->b_mpeg4_matrix = val.b_bool; - - var_Get( p_enc, ENC_CFG_PREFIX "qscale", &val ); - if( val.f_float < 0.01 || val.f_float > 255.0 ) val.f_float = 0; - p_sys->i_quality = (int)(FF_QP2LAMBDA * val.f_float + 0.5); - - var_Get( p_enc, ENC_CFG_PREFIX "hq", &val ); + psz_val = var_GetString( p_enc, ENC_CFG_PREFIX "hq" ); p_sys->i_hq = FF_MB_DECISION_RD; - if( val.psz_string && *val.psz_string ) + if( psz_val && *psz_val ) { - if( !strcmp( val.psz_string, "rd" ) ) + if( !strcmp( psz_val, "rd" ) ) p_sys->i_hq = FF_MB_DECISION_RD; - else if( !strcmp( val.psz_string, "bits" ) ) + else if( !strcmp( psz_val, "bits" ) ) p_sys->i_hq = FF_MB_DECISION_BITS; - else if( !strcmp( val.psz_string, "simple" ) ) + else if( !strcmp( psz_val, "simple" ) ) p_sys->i_hq = FF_MB_DECISION_SIMPLE; else p_sys->i_hq = FF_MB_DECISION_RD; } else p_sys->i_hq = FF_MB_DECISION_RD; - free( val.psz_string ); - - var_Get( p_enc, ENC_CFG_PREFIX "qmin", &val ); - p_sys->i_qmin = val.i_int; - var_Get( p_enc, ENC_CFG_PREFIX "qmax", &val ); - p_sys->i_qmax = val.i_int; - var_Get( p_enc, ENC_CFG_PREFIX "trellis", &val ); - p_sys->b_trellis = val.b_bool; - - var_Get( p_enc, ENC_CFG_PREFIX "strict", &val ); - if( val.i_int < - 1 || val.i_int > 1 ) val.i_int = 0; - p_context->strict_std_compliance = val.i_int; - - var_Get( p_enc, ENC_CFG_PREFIX "lumi-masking", &val ); - p_sys->f_lumi_masking = val.f_float; - var_Get( p_enc, ENC_CFG_PREFIX "dark-masking", &val ); - p_sys->f_dark_masking = val.f_float; - var_Get( p_enc, ENC_CFG_PREFIX "p-masking", &val ); - p_sys->f_p_masking = val.f_float; - var_Get( p_enc, ENC_CFG_PREFIX "border-masking", &val ); - p_sys->f_border_masking = val.f_float; - var_Get( p_enc, ENC_CFG_PREFIX "luma-elim-threshold", &val ); - p_sys->i_luma_elim = val.i_int; - var_Get( p_enc, ENC_CFG_PREFIX "chroma-elim-threshold", &val ); - p_sys->i_chroma_elim = val.i_int; - -#if LIBAVCODEC_VERSION_INT >= ((51<<16)+(40<<8)+4) - var_Get( p_enc, ENC_CFG_PREFIX "aac-profile", &val ); + free( psz_val ); + + p_sys->i_qmin = var_GetInteger( p_enc, ENC_CFG_PREFIX "qmin" ); + p_sys->i_qmax = var_GetInteger( p_enc, ENC_CFG_PREFIX "qmax" ); + p_sys->b_trellis = var_GetBool( p_enc, ENC_CFG_PREFIX "trellis" ); + + i_val = var_GetInteger( p_enc, ENC_CFG_PREFIX "strict" ); + if( i_val < - 1 || i_val > 1 ) i_val = 0; + p_context->strict_std_compliance = i_val; + + p_sys->f_lumi_masking = var_GetFloat( p_enc, ENC_CFG_PREFIX "lumi-masking" ); + p_sys->f_dark_masking = var_GetFloat( p_enc, ENC_CFG_PREFIX "dark-masking" ); + p_sys->f_p_masking = var_GetFloat( p_enc, ENC_CFG_PREFIX "p-masking" ); + p_sys->f_border_masking = var_GetFloat( p_enc, ENC_CFG_PREFIX "border-masking" ); + p_sys->i_luma_elim = var_GetInteger( p_enc, ENC_CFG_PREFIX "luma-elim-threshold" ); + p_sys->i_chroma_elim = var_GetInteger( p_enc, ENC_CFG_PREFIX "chroma-elim-threshold" ); + + psz_val = var_GetString( p_enc, ENC_CFG_PREFIX "aac-profile" ); + /* ffmpeg uses faac encoder atm, and it has issues with + * other than low-complexity profile, so default to that */ p_sys->i_aac_profile = FF_PROFILE_AAC_LOW; - if( val.psz_string && *val.psz_string ) + if( psz_val && *psz_val ) { - if( !strncmp( val.psz_string, "main", 4 ) ) + if( !strncmp( psz_val, "main", 4 ) ) p_sys->i_aac_profile = FF_PROFILE_AAC_MAIN; - else if( !strncmp( val.psz_string, "low", 3 ) ) + else if( !strncmp( psz_val, "low", 3 ) ) p_sys->i_aac_profile = FF_PROFILE_AAC_LOW; #if 0 /* Not supported by FAAC encoder */ - else if( !strncmp( val.psz_string, "ssr", 3 ) ) + else if( !strncmp( psz_val, "ssr", 3 ) ) p_sys->i_aac_profile = FF_PROFILE_AAC_SSR; #endif - else if( !strncmp( val.psz_string, "ltp", 3 ) ) + else if( !strncmp( psz_val, "ltp", 3 ) ) p_sys->i_aac_profile = FF_PROFILE_AAC_LTP; else { @@ -397,13 +385,10 @@ int OpenEncoder( vlc_object_t *p_this ) p_sys->i_aac_profile = FF_PROFILE_AAC_LOW; } } - free( val.psz_string ); -#endif + free( psz_val ); if( p_enc->fmt_in.i_cat == VIDEO_ES ) { - int i_aspect_num, i_aspect_den; - if( !p_enc->fmt_in.video.i_width || !p_enc->fmt_in.video.i_height ) { msg_Warn( p_enc, "invalid size %ix%i", p_enc->fmt_in.video.i_width, @@ -412,11 +397,10 @@ int OpenEncoder( vlc_object_t *p_this ) return VLC_EGENERIC; } + p_context->codec_type = CODEC_TYPE_VIDEO; + p_context->width = p_enc->fmt_in.video.i_width; p_context->height = p_enc->fmt_in.video.i_height; - if( p_enc->fmt_out.i_codec == VLC_FOURCC('m', 'p', '2', 'v') - && (p_context->width > 720 || p_context->height > 576) ) - p_context->level = 4; /* High level */ p_context->time_base.num = p_enc->fmt_in.video.i_frame_rate_base; p_context->time_base.den = p_enc->fmt_in.video.i_frame_rate; @@ -442,25 +426,28 @@ int OpenEncoder( vlc_object_t *p_this ) __MAX( __MIN( p_sys->i_b_frames, FF_MAX_B_FRAMES ), 0 ); p_context->b_frame_strategy = 0; if( !p_context->max_b_frames && - ( p_enc->fmt_out.i_codec == VLC_FOURCC('m', 'p', '2', 'v') || - p_enc->fmt_out.i_codec == VLC_FOURCC('m', 'p', '1', 'v') ) ) + ( p_enc->fmt_out.i_codec == VLC_CODEC_MPGV || + p_enc->fmt_out.i_codec == VLC_CODEC_MP2V || + p_enc->fmt_out.i_codec == VLC_CODEC_MP1V ) ) p_context->flags |= CODEC_FLAG_LOW_DELAY; - av_reduce( &i_aspect_num, &i_aspect_den, - p_enc->fmt_in.video.i_aspect, - VOUT_ASPECT_FACTOR, 1 << 30 /* something big */ ); av_reduce( &p_context->sample_aspect_ratio.num, &p_context->sample_aspect_ratio.den, - i_aspect_num * (int64_t)p_context->height, - i_aspect_den * (int64_t)p_context->width, 1 << 30 ); + p_enc->fmt_in.video.i_sar_num, + p_enc->fmt_in.video.i_sar_den, 1 << 30 ); + + p_sys->i_buffer_out = p_context->height * p_context->width + * 3 /* Assume 24bpp maximum */ + + 200; /* some room for potential headers (such as BMP) */ - p_sys->i_buffer_out = p_context->height * p_context->width * 3; if( p_sys->i_buffer_out < FF_MIN_BUFFER_SIZE ) p_sys->i_buffer_out = FF_MIN_BUFFER_SIZE; p_sys->p_buffer_out = malloc( p_sys->i_buffer_out ); - p_enc->fmt_in.i_codec = VLC_FOURCC('I','4','2','0'); - p_context->pix_fmt = GetFfmpegChroma( p_enc->fmt_in.i_codec ); + p_enc->fmt_in.i_codec = VLC_CODEC_I420; + p_enc->fmt_in.video.i_chroma = p_enc->fmt_in.i_codec; + GetFfmpegChroma( &p_context->pix_fmt, p_enc->fmt_in.video ); + if( p_codec->pix_fmts ) { const enum PixelFormat *p = p_codec->pix_fmts; @@ -469,7 +456,8 @@ int OpenEncoder( vlc_object_t *p_this ) if( *p == p_context->pix_fmt ) break; } if( *p == -1 ) p_context->pix_fmt = p_codec->pix_fmts[0]; - p_enc->fmt_in.i_codec = GetVlcChroma( p_context->pix_fmt ); + GetVlcChroma( &p_enc->fmt_in.video, p_context->pix_fmt ); + p_enc->fmt_in.i_codec = p_enc->fmt_in.video.i_chroma; } @@ -507,14 +495,34 @@ int OpenEncoder( vlc_object_t *p_this ) } } - if ( p_sys->b_trellis ) - p_context->flags |= CODEC_FLAG_TRELLIS_QUANT; + p_context->trellis = p_sys->b_trellis; if ( p_sys->i_qmin > 0 && p_sys->i_qmin == p_sys->i_qmax ) p_context->flags |= CODEC_FLAG_QSCALE; + /* These codecs cause libavcodec to exit if thread_count is > 1. + See libavcodec/mpegvideo_enc.c:MPV_encode_init and + libavcodec/svq3.c , WMV2 calls MPV_encode_init also. + */ + if ( i_codec_id == CODEC_ID_FLV1 || + i_codec_id == CODEC_ID_H261 || + i_codec_id == CODEC_ID_LJPEG || + i_codec_id == CODEC_ID_MJPEG || + i_codec_id == CODEC_ID_H263 || + i_codec_id == CODEC_ID_H263P || + i_codec_id == CODEC_ID_MSMPEG4V1 || + i_codec_id == CODEC_ID_MSMPEG4V2 || + i_codec_id == CODEC_ID_MSMPEG4V3 || + i_codec_id == CODEC_ID_WMV1 || + i_codec_id == CODEC_ID_WMV2 || + i_codec_id == CODEC_ID_RV10 || + i_codec_id == CODEC_ID_RV20 || + i_codec_id == CODEC_ID_SVQ3 ) + p_enc->i_threads = 1; if ( p_enc->i_threads >= 1 ) - p_context->thread_count = p_enc->i_threads; + avcodec_thread_init( p_context, p_enc->i_threads ); + else + avcodec_thread_init( p_context, vlc_GetCPUCount() ); if( p_sys->i_vtolerance > 0 ) p_context->bit_rate_tolerance = p_sys->i_vtolerance; @@ -565,31 +573,84 @@ int OpenEncoder( vlc_object_t *p_this ) if( i_codec_id == CODEC_ID_MP3 && p_enc->fmt_in.audio.i_channels > 2 ) p_enc->fmt_in.audio.i_channels = 2; - p_enc->fmt_in.i_codec = AOUT_FMT_S16_NE; + p_context->codec_type = CODEC_TYPE_AUDIO; + p_context->sample_fmt = p_codec->sample_fmts ? + p_codec->sample_fmts[0] : + SAMPLE_FMT_S16; + p_enc->fmt_in.i_codec = VLC_CODEC_S16N; p_context->sample_rate = p_enc->fmt_out.audio.i_rate; p_context->channels = p_enc->fmt_out.audio.i_channels; - if ( p_enc->fmt_out.i_codec == VLC_FOURCC('m','p','4','a') ) + if ( p_enc->fmt_out.i_codec == VLC_CODEC_MP4A ) { /* XXX: FAAC does resample only when setting the INPUT samplerate * to the desired value (-R option of the faac frontend) p_enc->fmt_in.audio.i_rate = p_context->sample_rate;*/ -#if LIBAVCODEC_VERSION_INT >= ((51<<16)+(40<<8)+4) - /* Ignore FF_PROFILE_UNKNOWN */ - if( p_sys->i_aac_profile >= FF_PROFILE_AAC_MAIN ) + /* vlc should default to low-complexity profile, faac encoder + * has bug and aac audio has issues otherwise atm */ p_context->profile = p_sys->i_aac_profile; -#endif } } /* Misc parameters */ p_context->bit_rate = p_enc->fmt_out.i_bitrate; +#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT( 52, 69, 2 ) + /* Set reasonable defaults to VP8, based on + libvpx-720p preset from libvpx ffmpeg-patch */ + if( i_codec_id == CODEC_ID_VP8 ) + { + /* Lets give bitrate tolerance */ + p_context->bit_rate_tolerance = __MAX(2 * p_enc->fmt_out.i_bitrate, p_sys->i_vtolerance ); + /* default to 120 frames between keyframe */ + if( !var_GetInteger( p_enc, ENC_CFG_PREFIX "keyint" ) ) + p_context->gop_size = 120; + /* seems that ffmpeg presets have 720p as divider for buffers */ + if( p_enc->fmt_out.video.i_width >= 720 ) + { + /* Check that we don't overrun users qmin/qmax values */ + if( !var_GetInteger( p_enc, ENC_CFG_PREFIX "qmin" ) ) + { + p_context->mb_qmin = p_context->qmin = 10; + p_context->mb_lmin = p_context->lmin = 10 * FF_QP2LAMBDA; + } + + if( !var_GetInteger( p_enc, ENC_CFG_PREFIX "qmax" ) ) + { + p_context->mb_qmax = p_context->qmax = 42; + p_context->mb_lmax = p_context->lmax = 42 * FF_QP2LAMBDA; + } + + p_context->rc_max_rate = 24 * 1000 * 1000; //24M + p_context->rc_min_rate = 100 * 1000; // 100k + } else { + if( !var_GetInteger( p_enc, ENC_CFG_PREFIX "qmin" ) ) + { + p_context->mb_qmin = p_context->qmin = FF_QP2LAMBDA; + p_context->mb_lmin = p_context->lmin = FF_QP2LAMBDA; + } + + p_context->rc_max_rate = 1.5 * 1000 * 1000; //1.5M + p_context->rc_min_rate = 40 * 1000; // 40k + } + + +#if 0 /* enable when/if vp8 encoder is accepted in libavcodec */ + p_context->lag = 16; + p_context->level = 216; + p_context->profile = 0; + p_context->rc_buffer_aggressivity = 0.95; + p_context->token_partitions = 4; + p_context->mb_static_threshold = 0; +#endif + } +#endif + if( i_codec_id == CODEC_ID_RAWVIDEO ) { /* XXX: hack: Force same codec (will be handled by transcode) */ - p_enc->fmt_in.i_codec = p_enc->fmt_out.i_codec; - p_context->pix_fmt = GetFfmpegChroma( p_enc->fmt_in.i_codec ); + p_enc->fmt_in.video.i_chroma = p_enc->fmt_in.i_codec = p_enc->fmt_out.i_codec; + GetFfmpegChroma( &p_context->pix_fmt, p_enc->fmt_in.video ); } /* Make sure we get extradata filled by the encoder */ @@ -597,11 +658,12 @@ int OpenEncoder( vlc_object_t *p_this ) p_context->extradata = NULL; p_context->flags |= CODEC_FLAG_GLOBAL_HEADER; - vlc_mutex_t *lock = var_AcquireMutex( "avcodec" ); - - if( avcodec_open( p_context, p_codec ) ) + int ret; + vlc_avcodec_lock(); + ret = avcodec_open( p_context, p_codec ); + vlc_avcodec_unlock(); + if( ret ) { - vlc_mutex_unlock( lock ); if( p_enc->fmt_in.i_cat == AUDIO_ES && (p_context->channels > 2 || i_codec_id == CODEC_ID_MP2 || i_codec_id == CODEC_ID_MP3) ) @@ -651,14 +713,15 @@ int OpenEncoder( vlc_object_t *p_this ) } p_context->codec = NULL; - vlc_mutex_lock( lock ); - if( avcodec_open( p_context, p_codec ) ) + vlc_avcodec_lock(); + ret = avcodec_open( p_context, p_codec ); + vlc_avcodec_unlock(); + if( ret ) { - vlc_mutex_unlock( lock ); msg_Err( p_enc, "cannot open encoder" ); - intf_UserFatal( p_enc, false, + dialog_Fatal( p_enc, _("Streaming / Transcoding failed"), - _("VLC could not open the encoder.") ); + "%s", _("VLC could not open the encoder.") ); free( p_sys ); return VLC_EGENERIC; } @@ -666,108 +729,72 @@ int OpenEncoder( vlc_object_t *p_this ) else { msg_Err( p_enc, "cannot open encoder" ); - intf_UserFatal( p_enc, false, _("Streaming / Transcoding failed"), - _("VLC could not open the encoder.") ); + dialog_Fatal( p_enc, _("Streaming / Transcoding failed"), + "%s", _("VLC could not open the encoder.") ); free( p_sys ); return VLC_EGENERIC; } } - vlc_mutex_unlock( lock); - p_enc->fmt_out.i_extra = p_context->extradata_size; - if( p_enc->fmt_out.i_extra ) + if( i_codec_id == CODEC_ID_FLAC ) { + p_enc->fmt_out.i_extra = 4 + 1 + 3 + p_context->extradata_size; p_enc->fmt_out.p_extra = malloc( p_enc->fmt_out.i_extra ); - memcpy( p_enc->fmt_out.p_extra, p_context->extradata, - p_enc->fmt_out.i_extra ); - } - p_context->flags &= ~CODEC_FLAG_GLOBAL_HEADER; - - if( p_enc->fmt_in.i_cat == AUDIO_ES ) - { - p_sys->i_buffer_out = 2 * AVCODEC_MAX_AUDIO_FRAME_SIZE; - p_sys->p_buffer_out = malloc( p_sys->i_buffer_out ); - p_sys->i_frame_size = p_context->frame_size * 2 * p_context->channels; - p_sys->p_buffer = malloc( p_sys->i_frame_size ); - p_enc->fmt_out.audio.i_blockalign = p_context->block_align; - } - - msg_Dbg( p_enc, "found encoder %s", psz_namecodec ); - - return VLC_SUCCESS; -} - -/**************************************************************************** - * Ffmpeg threading system - ****************************************************************************/ -static void* FfmpegThread( vlc_object_t *p_this ) -{ - struct thread_context_t *p_context = (struct thread_context_t *)p_this; - while ( vlc_object_alive (p_context) && !p_context->b_error ) - { - vlc_mutex_lock( &p_context->lock ); - while ( !p_context->b_work && vlc_object_alive (p_context) && !p_context->b_error ) + if( p_enc->fmt_out.p_extra ) { - vlc_cond_wait( &p_context->cond, &p_context->lock ); + uint8_t *p = p_enc->fmt_out.p_extra; + p[0] = 0x66; /* f */ + p[1] = 0x4C; /* L */ + p[2] = 0x61; /* a */ + p[3] = 0x43; /* C */ + p[4] = 0x80; /* streaminfo block, last block before audio */ + p[5] = ( p_context->extradata_size >> 16 ) & 0xff; + p[6] = ( p_context->extradata_size >> 8 ) & 0xff; + p[7] = ( p_context->extradata_size ) & 0xff; + memcpy( &p[8], p_context->extradata, p_context->extradata_size ); } - p_context->b_work = 0; - vlc_mutex_unlock( &p_context->lock ); - if ( !vlc_object_alive (p_context) || p_context->b_error ) - break; - - if ( p_context->pf_func ) + else { - p_context->i_ret = p_context->pf_func( p_context->p_context, - p_context->arg ); + p_enc->fmt_out.i_extra = 0; } - - vlc_mutex_lock( &p_context->lock ); - p_context->b_done = 1; - vlc_cond_signal( &p_context->cond ); - vlc_mutex_unlock( &p_context->lock ); } - - return NULL; -} - -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++ ) + else { - vlc_mutex_lock( &pp_contexts[i]->lock ); - while ( !pp_contexts[i]->b_done ) + p_enc->fmt_out.i_extra = p_context->extradata_size; + if( p_enc->fmt_out.i_extra ) { - vlc_cond_wait( &pp_contexts[i]->cond, &pp_contexts[i]->lock ); + p_enc->fmt_out.p_extra = malloc( p_enc->fmt_out.i_extra ); + memcpy( p_enc->fmt_out.p_extra, p_context->extradata, + p_enc->fmt_out.i_extra ); } - 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; - } + p_context->flags &= ~CODEC_FLAG_GLOBAL_HEADER; + + if( p_enc->fmt_in.i_cat == AUDIO_ES ) + { + GetVlcAudioFormat( &p_enc->fmt_in.i_codec, + &p_enc->fmt_in.audio.i_bitspersample, + p_sys->p_context->sample_fmt ); + p_sys->i_sample_bytes = (p_enc->fmt_in.audio.i_bitspersample / 8) * + p_context->channels; + p_sys->i_frame_size = p_context->frame_size > 1 ? + p_context->frame_size : + RAW_AUDIO_FRAME_SIZE; + p_sys->p_buffer = malloc( p_sys->i_frame_size * p_sys->i_sample_bytes ); + p_enc->fmt_out.audio.i_blockalign = p_context->block_align; + p_enc->fmt_out.audio.i_bitspersample = aout_BitsPerSample( vlc_fourcc_GetCodec( AUDIO_ES, p_enc->fmt_out.i_codec ) ); + + if( p_context->frame_size > 1 ) + p_sys->i_buffer_out = 8 * AVCODEC_MAX_AUDIO_FRAME_SIZE; + else + p_sys->i_buffer_out = p_sys->i_frame_size * p_sys->i_sample_bytes; + p_sys->p_buffer_out = malloc( p_sys->i_buffer_out ); } - return 0; + msg_Dbg( p_enc, "found encoder %s", psz_namecodec ); + + return VLC_SUCCESS; } /**************************************************************************** @@ -779,133 +806,110 @@ static block_t *EncodeVideo( encoder_t *p_enc, picture_t *p_pict ) AVFrame frame; int i_out, i_plane; - 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( &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, false ) ) - { - msg_Err( p_enc, "cannot spawn encoder thread, expect to die soon" ); - return NULL; - } - } - - p_sys->p_context->execute = FfmpegExecute; - } memset( &frame, 0, sizeof( AVFrame ) ); - for( i_plane = 0; i_plane < p_pict->i_planes; i_plane++ ) - { - frame.data[i_plane] = p_pict->p[i_plane].p_pixels; - frame.linesize[i_plane] = p_pict->p[i_plane].i_pitch; - } - - /* Let ffmpeg select the frame type */ - frame.pict_type = 0; - - frame.repeat_pict = p_pict->i_nb_fields - 2; - frame.interlaced_frame = !p_pict->b_progressive; - frame.top_field_first = !!p_pict->b_top_field_first; - - /* Set the pts of the frame being encoded (segfaults with mpeg4!)*/ - if( p_enc->fmt_out.i_codec != VLC_FOURCC( 'm', 'p', '4', 'v' ) ) - { - frame.pts = p_pict->date ? p_pict->date : (int64_t)AV_NOPTS_VALUE; - - if ( p_sys->b_hurry_up && frame.pts != (int64_t)AV_NOPTS_VALUE ) - { - mtime_t current_date = mdate(); - - if ( 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; - msg_Dbg( p_enc, "hurry up mode 3" ); - } - else - { - p_sys->p_context->mb_decision = p_sys->i_hq; - - if ( current_date + HURRY_UP_GUARD2 > frame.pts ) - { - p_sys->p_context->flags &= ~CODEC_FLAG_TRELLIS_QUANT; - p_sys->p_context->noise_reduction = p_sys->i_noise_reduction - + (HURRY_UP_GUARD2 + current_date - frame.pts) / 500; - msg_Dbg( p_enc, "hurry up mode 2" ); - } - else - { - if ( p_sys->b_trellis ) - p_sys->p_context->flags |= CODEC_FLAG_TRELLIS_QUANT; - - p_sys->p_context->noise_reduction = - p_sys->i_noise_reduction; - } - } - - if ( current_date + HURRY_UP_GUARD1 > frame.pts ) - { - frame.pict_type = FF_P_TYPE; - /* msg_Dbg( p_enc, "hurry up mode 1 %lld", current_date + HURRY_UP_GUARD1 - frame.pts ); */ - } - } + if( likely(p_pict) ) { + for( i_plane = 0; i_plane < p_pict->i_planes; i_plane++ ) + { + frame.data[i_plane] = p_pict->p[i_plane].p_pixels; + frame.linesize[i_plane] = p_pict->p[i_plane].i_pitch; + } + + /* Let ffmpeg select the frame type */ + frame.pict_type = 0; + + frame.repeat_pict = p_pict->i_nb_fields - 2; + frame.interlaced_frame = !p_pict->b_progressive; + frame.top_field_first = !!p_pict->b_top_field_first; + + /* Set the pts of the frame being encoded (segfaults with mpeg4!)*/ + if( p_enc->fmt_out.i_codec != VLC_CODEC_MP4V ) + { + frame.pts = p_pict->date ? p_pict->date : (int64_t)AV_NOPTS_VALUE; + + if ( p_sys->b_hurry_up && frame.pts != (int64_t)AV_NOPTS_VALUE ) + { + mtime_t current_date = mdate(); + + if ( current_date + HURRY_UP_GUARD3 > frame.pts ) + { + p_sys->p_context->mb_decision = FF_MB_DECISION_SIMPLE; + p_sys->p_context->trellis = 0; + msg_Dbg( p_enc, "hurry up mode 3" ); + } + else + { + p_sys->p_context->mb_decision = p_sys->i_hq; + + if ( current_date + HURRY_UP_GUARD2 > frame.pts ) + { + p_sys->p_context->trellis = 0; + p_sys->p_context->noise_reduction = p_sys->i_noise_reduction + + (HURRY_UP_GUARD2 + current_date - frame.pts) / 500; + msg_Dbg( p_enc, "hurry up mode 2" ); + } + else + { + p_sys->p_context->trellis = p_sys->b_trellis; + + p_sys->p_context->noise_reduction = + p_sys->i_noise_reduction; + } + } + + if ( current_date + HURRY_UP_GUARD1 > frame.pts ) + { + frame.pict_type = FF_P_TYPE; + /* msg_Dbg( p_enc, "hurry up mode 1 %lld", current_date + HURRY_UP_GUARD1 - frame.pts ); */ + } + } + } + else + { + frame.pts = (int64_t)AV_NOPTS_VALUE; + } + + if ( frame.pts != (int64_t)AV_NOPTS_VALUE && frame.pts != 0 ) + { + if ( p_sys->i_last_pts == frame.pts ) + { + msg_Warn( p_enc, "almost fed libavcodec with two frames with the " + "same PTS (%"PRId64 ")", frame.pts ); + return NULL; + } + else if ( p_sys->i_last_pts > frame.pts ) + { + msg_Warn( p_enc, "almost fed libavcodec with a frame in the " + "past (current: %"PRId64 ", last: %"PRId64")", + frame.pts, p_sys->i_last_pts ); + return NULL; + } + else + { + p_sys->i_last_pts = frame.pts; + } + } + + frame.quality = p_sys->i_quality; + + /* Ugly work-around for stupid libavcodec behaviour */ + p_sys->i_framenum++; + p_sys->pi_delay_pts[p_sys->i_framenum % MAX_FRAME_DELAY] = frame.pts; + frame.pts = p_sys->i_framenum * AV_TIME_BASE * + p_enc->fmt_in.video.i_frame_rate_base; + frame.pts += p_enc->fmt_in.video.i_frame_rate - 1; + frame.pts /= p_enc->fmt_in.video.i_frame_rate; + /* End work-around */ + + i_out = avcodec_encode_video( p_sys->p_context, p_sys->p_buffer_out, + p_sys->i_buffer_out, &frame ); } else { - frame.pts = (int64_t)AV_NOPTS_VALUE; + i_out = avcodec_encode_video( p_sys->p_context, p_sys->p_buffer_out, + p_sys->i_buffer_out, NULL); } - if ( frame.pts != (int64_t)AV_NOPTS_VALUE && frame.pts != 0 ) - { - if ( p_sys->i_last_pts == frame.pts ) - { - msg_Warn( p_enc, "almost fed libavcodec with two frames with the " - "same PTS (%"PRId64 ")", frame.pts ); - return NULL; - } - else if ( p_sys->i_last_pts > frame.pts ) - { - msg_Warn( p_enc, "almost fed libavcodec with a frame in the " - "past (current: %"PRId64 ", last: %"PRId64")", - frame.pts, p_sys->i_last_pts ); - return NULL; - } - else - { - p_sys->i_last_pts = frame.pts; - } - } - - frame.quality = p_sys->i_quality; - - /* Ugly work-around for stupid libavcodec behaviour */ - p_sys->i_framenum++; - p_sys->pi_delay_pts[p_sys->i_framenum % MAX_FRAME_DELAY] = frame.pts; - frame.pts = p_sys->i_framenum * AV_TIME_BASE * - p_enc->fmt_in.video.i_frame_rate_base; - frame.pts += p_enc->fmt_in.video.i_frame_rate - 1; - frame.pts /= p_enc->fmt_in.video.i_frame_rate; - /* End work-around */ - - i_out = avcodec_encode_video( p_sys->p_context, p_sys->p_buffer_out, - p_sys->i_buffer_out, &frame ); - if( i_out > 0 ) { block_t *p_block = block_New( p_enc, i_out ); @@ -919,7 +923,9 @@ static block_t *EncodeVideo( encoder_t *p_enc, picture_t *p_pict ) if( !p_sys->p_context->max_b_frames || !p_sys->p_context->delay ) { /* No delay -> output pts == input pts */ - p_block->i_pts = p_block->i_dts = p_pict->date; + if( p_pict ) + p_block->i_dts = p_pict->date; + p_block->i_pts = p_block->i_dts; } else if( p_sys->p_context->coded_frame->pts != (int64_t)AV_NOPTS_VALUE && p_sys->p_context->coded_frame->pts != 0 && @@ -990,39 +996,42 @@ static block_t *EncodeVideo( encoder_t *p_enc, picture_t *p_pict ) static block_t *EncodeAudio( encoder_t *p_enc, aout_buffer_t *p_aout_buf ) { encoder_sys_t *p_sys = p_enc->p_sys; + block_t *p_block, *p_chain = NULL; uint8_t *p_buffer = p_aout_buf->p_buffer; int i_samples = p_aout_buf->i_nb_samples; int i_samples_delay = p_sys->i_samples_delay; - p_sys->i_pts = p_aout_buf->start_date - + p_sys->i_pts = p_aout_buf->i_pts - (mtime_t)1000000 * (mtime_t)p_sys->i_samples_delay / (mtime_t)p_enc->fmt_in.audio.i_rate; p_sys->i_samples_delay += i_samples; - while( p_sys->i_samples_delay >= p_sys->p_context->frame_size ) + while( p_sys->i_samples_delay >= p_sys->i_frame_size ) { - int16_t *p_samples; + void *p_samples; int i_out; if( i_samples_delay ) { /* Take care of the left-over from last time */ - int i_delay_size = i_samples_delay * 2 * - p_sys->p_context->channels; - int i_size = p_sys->i_frame_size - i_delay_size; + int i_delay_size = i_samples_delay; + int i_size = (p_sys->i_frame_size - i_delay_size) * + p_sys->i_sample_bytes; - p_samples = (int16_t *)p_sys->p_buffer; - memcpy( p_sys->p_buffer + i_delay_size, p_buffer, i_size ); - p_buffer -= i_delay_size; + memcpy( p_sys->p_buffer + i_delay_size * p_sys->i_sample_bytes, + p_buffer, i_size ); + p_buffer -= i_delay_size * p_sys->i_sample_bytes; i_samples += i_samples_delay; i_samples_delay = 0; + + p_samples = p_sys->p_buffer; } else { - p_samples = (int16_t *)p_buffer; + p_samples = p_buffer; } i_out = avcodec_encode_audio( p_sys->p_context, p_sys->p_buffer_out, @@ -1031,19 +1040,18 @@ static block_t *EncodeAudio( encoder_t *p_enc, aout_buffer_t *p_aout_buf ) #if 0 msg_Warn( p_enc, "avcodec_encode_audio: %d", i_out ); #endif - if( i_out < 0 ) break; - - p_buffer += p_sys->i_frame_size; - p_sys->i_samples_delay -= p_sys->p_context->frame_size; - i_samples -= p_sys->p_context->frame_size; + p_buffer += p_sys->i_frame_size * p_sys->i_sample_bytes; + p_sys->i_samples_delay -= p_sys->i_frame_size; + i_samples -= p_sys->i_frame_size; - if( i_out == 0 ) continue; + if( i_out <= 0 ) + continue; p_block = block_New( p_enc, i_out ); memcpy( p_block->p_buffer, p_sys->p_buffer_out, i_out ); p_block->i_length = (mtime_t)1000000 * - (mtime_t)p_sys->p_context->frame_size / + (mtime_t)p_sys->i_frame_size / (mtime_t)p_sys->p_context->sample_rate; p_block->i_dts = p_block->i_pts = p_sys->i_pts; @@ -1056,9 +1064,9 @@ static block_t *EncodeAudio( encoder_t *p_enc, aout_buffer_t *p_aout_buf ) /* Backup the remaining raw samples */ if( i_samples ) { - memcpy( p_sys->p_buffer + i_samples_delay * 2 * - p_sys->p_context->channels, p_buffer, - i_samples * 2 * p_sys->p_context->channels ); + memcpy( &p_sys->p_buffer[i_samples_delay * p_sys->i_sample_bytes], + p_buffer, + i_samples * p_sys->i_sample_bytes ); } return p_chain; @@ -1072,27 +1080,9 @@ void CloseEncoder( vlc_object_t *p_this ) encoder_t *p_enc = (encoder_t *)p_this; encoder_sys_t *p_sys = p_enc->p_sys; - 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++ ) - { - vlc_object_kill( pp_contexts[i] ); - 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_release( pp_contexts[i] ); - } - - free( pp_contexts ); - } - - vlc_mutex_t *lock = var_AcquireMutex( "avcodec" ); + vlc_avcodec_lock(); avcodec_close( p_sys->p_context ); - vlc_mutex_unlock( lock ); + vlc_avcodec_unlock(); av_free( p_sys->p_context ); free( p_sys->p_buffer );