]> git.sesse.net Git - vlc/blobdiff - modules/codec/ffmpeg/encoder.c
Video bitrate tolerance is advertised as kbits/sec in the help (not bits/sec).
[vlc] / modules / codec / ffmpeg / encoder.c
index 0a1fbf8c5b2578e4e2ecfe838843c21a49c10357..f48d0ab0b34a762d247ebf7dc4a51a7b5fe728ac 100644 (file)
 /*****************************************************************************
  * Preamble
  *****************************************************************************/
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
 #include <vlc/vlc.h>
 #include <vlc_vout.h>
 #include <vlc_aout.h>
@@ -37,7 +41,9 @@
 
 /* ffmpeg header */
 #define HAVE_MMX 1
-#ifdef HAVE_FFMPEG_AVCODEC_H
+#ifdef HAVE_LIBAVCODEC_AVCODEC_H
+#   include <libavcodec/avcodec.h>
+#elif defined(HAVE_FFMPEG_AVCODEC_H)
 #   include <ffmpeg/avcodec.h>
 #else
 #   include <avcodec.h>
@@ -54,8 +60,8 @@
 /*****************************************************************************
  * Local prototypes
  *****************************************************************************/
-int  E_(OpenEncoder) ( vlc_object_t * );
-void E_(CloseEncoder)( vlc_object_t * );
+int  OpenEncoder ( vlc_object_t * );
+void CloseEncoder( vlc_object_t * );
 
 static block_t *EncodeVideo( encoder_t *, picture_t * );
 static block_t *EncodeAudio( encoder_t *, aout_buffer_t * );
@@ -80,7 +86,7 @@ struct thread_context_t
 
     vlc_mutex_t     lock;
     vlc_cond_t      cond;
-    vlc_bool_t      b_work, b_done;
+    bool            b_work, b_done;
 };
 
 /*****************************************************************************
@@ -106,7 +112,7 @@ struct encoder_sys_t
     mtime_t i_last_ref_pts;
     mtime_t i_buggy_pts_detect;
     mtime_t i_last_pts;
-    vlc_bool_t b_inited;
+    bool    b_inited;
 
     /*
      * Audio properties
@@ -122,20 +128,22 @@ struct encoder_sys_t
     int        i_qmin;
     int        i_qmax;
     int        i_hq;
-    vlc_bool_t b_strict_rc;
+    bool       b_strict_rc;
     int        i_rc_buffer_size;
     float      f_rc_buffer_aggressivity;
-    vlc_bool_t b_pre_me;
-    vlc_bool_t b_hurry_up;
-    vlc_bool_t b_interlace, b_interlace_me;
+    bool       b_pre_me;
+    bool       b_hurry_up;
+    bool       b_interlace, b_interlace_me;
     float      f_i_quant_factor;
     int        i_noise_reduction;
-    vlc_bool_t b_mpeg4_matrix;
-    vlc_bool_t b_trellis;
+    bool       b_mpeg4_matrix;
+    bool       b_trellis;
     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];
@@ -147,7 +155,11 @@ static const char *ppsz_enc_options[] = {
     "interlace", "i-quant-factor", "noise-reduction", "mpeg4-matrix",
     "trellis", "qscale", "strict", "lumi-masking", "dark-masking",
     "p-masking", "border-masking", "luma-elim-threshold",
-    "chroma-elim-threshold", NULL
+    "chroma-elim-threshold",
+#if LIBAVCODEC_VERSION_INT >= ((51<<16)+(40<<8)+4)
+     "aac-profile",
+#endif
+     NULL
 };
 
 static const uint16_t mpa_bitrate_tab[2][15] =
@@ -159,7 +171,7 @@ static const uint16_t mpa_bitrate_tab[2][15] =
 static const uint16_t mpa_freq_tab[6] =
 { 44100, 48000, 32000, 22050, 24000, 16000 };
 
-static const int16_t mpeg4_default_intra_matrix[64] = {
+static const uint16_t mpeg4_default_intra_matrix[64] = {
   8, 17, 18, 19, 21, 23, 25, 27,
  17, 18, 19, 21, 23, 25, 27, 28,
  20, 21, 22, 23, 24, 26, 28, 30,
@@ -170,7 +182,7 @@ static const int16_t mpeg4_default_intra_matrix[64] = {
  27, 28, 30, 32, 35, 38, 41, 45,
 };
 
-static const int16_t mpeg4_default_non_intra_matrix[64] = {
+static const uint16_t mpeg4_default_non_intra_matrix[64] = {
  16, 17, 18, 19, 20, 21, 22, 23,
  17, 18, 19, 20, 21, 22, 23, 24,
  18, 19, 20, 21, 22, 23, 24, 25,
@@ -181,12 +193,11 @@ static const int16_t mpeg4_default_non_intra_matrix[64] = {
  23, 24, 25, 27, 28, 30, 31, 33,
 };
 
-
 /*****************************************************************************
  * OpenEncoder: probe the encoder
  *****************************************************************************/
 
-int E_(OpenEncoder)( vlc_object_t *p_this )
+int OpenEncoder( vlc_object_t *p_this )
 {
     encoder_t *p_enc = (encoder_t *)p_this;
     encoder_sys_t *p_sys = p_enc->p_sys;
@@ -195,15 +206,11 @@ int E_(OpenEncoder)( vlc_object_t *p_this )
     int i_codec_id, i_cat;
     const char *psz_namecodec;
     vlc_value_t val;
-    vlc_value_t lockval;
 
-    var_Create( p_enc->p_libvlc_global, "avcodec", VLC_VAR_MUTEX );
-    var_Get( p_enc->p_libvlc_global, "avcodec", &lockval );
-
-    if( !E_(GetFfmpegCodec)( p_enc->fmt_out.i_codec, &i_cat, &i_codec_id,
+    if( !GetFfmpegCodec( p_enc->fmt_out.i_codec, &i_cat, &i_codec_id,
                              &psz_namecodec ) )
     {
-        if( E_(GetFfmpegChroma)( p_enc->fmt_out.i_codec ) < 0 )
+        if( GetFfmpegChroma( p_enc->fmt_out.i_codec ) < 0 )
         {
             /* handed chroma output */
             return VLC_EGENERIC;
@@ -216,7 +223,7 @@ int E_(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, VLC_FALSE, _("Streaming / Transcoding failed"),
+        intf_UserFatal( p_enc, false, _("Streaming / Transcoding failed"),
                         _("\"%s\" is no video encoder."), psz_namecodec );
         return VLC_EGENERIC;
     }
@@ -224,19 +231,19 @@ int E_(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, VLC_FALSE, _("Streaming / Transcoding failed"),
+        intf_UserFatal( p_enc, false, _("Streaming / Transcoding failed"),
                         _("\"%s\" is no audio encoder."), psz_namecodec );
         return VLC_EGENERIC;
     }
 
     /* Initialization must be done before avcodec_find_encoder() */
-    E_(InitLibavcodec)(p_this);
+    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, VLC_FALSE, _("Streaming / Transcoding failed"),
+        intf_UserFatal( p_enc, false, _("Streaming / Transcoding failed"),
                         _("VLC could not find encoder \"%s\"."), psz_namecodec );
         return VLC_EGENERIC;
     }
@@ -291,7 +298,7 @@ int E_(OpenEncoder)( vlc_object_t *p_this )
     p_sys->i_b_frames = val.i_int;
 
     var_Get( p_enc, ENC_CFG_PREFIX "vt", &val );
-    p_sys->i_vtolerance = val.i_int;
+    p_sys->i_vtolerance = val.i_int * 1000;
 
     var_Get( p_enc, ENC_CFG_PREFIX "interlace", &val );
     p_sys->b_interlace = val.b_bool;
@@ -331,6 +338,7 @@ int E_(OpenEncoder)( vlc_object_t *p_this )
     p_sys->i_quality = (int)(FF_QP2LAMBDA * val.f_float + 0.5);
 
     var_Get( p_enc, ENC_CFG_PREFIX "hq", &val );
+    p_sys->i_hq = FF_MB_DECISION_RD;
     if( val.psz_string && *val.psz_string )
     {
         if( !strcmp( val.psz_string, "rd" ) )
@@ -342,7 +350,9 @@ int E_(OpenEncoder)( vlc_object_t *p_this )
         else
             p_sys->i_hq = FF_MB_DECISION_RD;
     }
-    if( val.psz_string ) free( val.psz_string );
+    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;
@@ -368,6 +378,30 @@ int E_(OpenEncoder)( vlc_object_t *p_this )
     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 );
+    p_sys->i_aac_profile = FF_PROFILE_UNKNOWN;
+    if( val.psz_string && *val.psz_string )
+    {
+        if( !strncmp( val.psz_string, "main", 4 ) )
+            p_sys->i_aac_profile = FF_PROFILE_AAC_MAIN;
+        else if( !strncmp( val.psz_string, "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 ) )
+            p_sys->i_aac_profile = FF_PROFILE_AAC_SSR;
+#endif
+        else  if( !strncmp( val.psz_string, "ltp", 3 ) )
+            p_sys->i_aac_profile = FF_PROFILE_AAC_LTP;
+        else
+        {
+            msg_Warn( p_enc, "unknown AAC profile requested" );
+            p_sys->i_aac_profile = FF_PROFILE_UNKNOWN;
+        }
+    }
+    free( val.psz_string );
+#endif
+
     if( p_enc->fmt_in.i_cat == VIDEO_ES )
     {
         int i_aspect_num, i_aspect_den;
@@ -425,7 +459,7 @@ int E_(OpenEncoder)( vlc_object_t *p_this )
         p_sys->p_buffer_out = malloc( p_context->height * p_context->width * 3 );
 
         p_enc->fmt_in.i_codec = VLC_FOURCC('I','4','2','0');
-        p_context->pix_fmt = E_(GetFfmpegChroma)( p_enc->fmt_in.i_codec );
+        p_context->pix_fmt = GetFfmpegChroma( p_enc->fmt_in.i_codec );
         if( p_codec->pix_fmts )
         {
             const enum PixelFormat *p = p_codec->pix_fmts;
@@ -434,12 +468,14 @@ int E_(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 = E_(GetVlcChroma)( p_context->pix_fmt );
+            p_enc->fmt_in.i_codec = GetVlcChroma( p_context->pix_fmt );
         }
 
         if ( p_sys->b_strict_rc )
         {
+            p_context->rc_qsquish = 1.0;
             p_context->rc_max_rate = p_enc->fmt_out.i_bitrate;
+            p_context->rc_min_rate = p_enc->fmt_out.i_bitrate;
             p_context->rc_buffer_size = p_sys->i_rc_buffer_size;
             /* This is from ffmpeg's ffmpeg.c : */
             p_context->rc_initial_buffer_occupancy
@@ -493,10 +529,25 @@ int E_(OpenEncoder)( vlc_object_t *p_this )
         if( p_sys->i_vtolerance > 0 )
             p_context->bit_rate_tolerance = p_sys->i_vtolerance;
 
+        /* usually if someone sets bitrate, he likes more to get that bitrate
+         * over quality should help 'normal' user to get asked bitrate
+         */
+        if( p_enc->fmt_out.i_bitrate > 0 && p_sys->i_qmax == 0 && p_sys->i_qmin == 0 )
+        {
+            p_sys->i_qmax = 51;
+            p_sys->i_qmin = 3;
+        }
+
         if( p_sys->i_qmin > 0 )
+        {
             p_context->mb_qmin = p_context->qmin = p_sys->i_qmin;
+            p_context->mb_lmin = p_context->lmin = p_sys->i_qmin * FF_QP2LAMBDA;
+        }
         if( p_sys->i_qmax > 0 )
+        {
             p_context->mb_qmax = p_context->qmax = p_sys->i_qmax;
+            p_context->mb_lmax = p_context->lmax = p_sys->i_qmax * FF_QP2LAMBDA;
+        }
         p_context->max_qdiff = 3;
 
         p_context->mb_decision = p_sys->i_hq;
@@ -514,8 +565,20 @@ int E_(OpenEncoder)( vlc_object_t *p_this )
             p_enc->fmt_in.audio.i_channels = 2;
 
         p_enc->fmt_in.i_codec  = AOUT_FMT_S16_NE;
-        p_context->sample_rate = p_enc->fmt_in.audio.i_rate;
-        p_context->channels    = p_enc->fmt_in.audio.i_channels;
+        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') )
+        {
+            /* 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 )
+            p_context->profile = p_sys->i_aac_profile;
+#endif
+        }
     }
 
     /* Misc parameters */
@@ -525,7 +588,7 @@ int E_(OpenEncoder)( vlc_object_t *p_this )
     {
         /* 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 = E_(GetFfmpegChroma)( p_enc->fmt_in.i_codec );
+        p_context->pix_fmt = GetFfmpegChroma( p_enc->fmt_in.i_codec );
     }
 
     /* Make sure we get extradata filled by the encoder */
@@ -533,10 +596,11 @@ int E_(OpenEncoder)( vlc_object_t *p_this )
     p_context->extradata = NULL;
     p_context->flags |= CODEC_FLAG_GLOBAL_HEADER;
 
-    vlc_mutex_lock( lockval.p_address );
+    vlc_mutex_t *lock = var_AcquireMutex( "avcodec" );
+
     if( avcodec_open( p_context, p_codec ) )
     {
-        vlc_mutex_unlock( lockval.p_address );
+        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) )
@@ -586,12 +650,13 @@ int E_(OpenEncoder)( vlc_object_t *p_this )
             }
 
             p_context->codec = NULL;
-            vlc_mutex_lock( lockval.p_address );
+            vlc_mutex_lock( lock );
             if( avcodec_open( p_context, p_codec ) )
             {
-                vlc_mutex_unlock( lockval.p_address );
+                vlc_mutex_unlock( lock );
                 msg_Err( p_enc, "cannot open encoder" );
-                intf_UserFatal( p_enc, VLC_FALSE, _("Streaming / Transcoding failed"),
+                intf_UserFatal( p_enc, false,
+                                _("Streaming / Transcoding failed"),
                                 _("VLC could not open the encoder.") );
                 free( p_sys );
                 return VLC_EGENERIC;
@@ -600,13 +665,13 @@ int E_(OpenEncoder)( vlc_object_t *p_this )
         else
         {
             msg_Err( p_enc, "cannot open encoder" );
-            intf_UserFatal( p_enc, VLC_FALSE, _("Streaming / Transcoding failed"),
+            intf_UserFatal( p_enc, false, _("Streaming / Transcoding failed"),
                             _("VLC could not open the encoder.") );
             free( p_sys );
             return VLC_EGENERIC;
         }
     }
-    vlc_mutex_unlock( lockval.p_address );
+    vlc_mutex_unlock( lock);
 
     p_enc->fmt_out.i_extra = p_context->extradata_size;
     if( p_enc->fmt_out.i_extra )
@@ -622,6 +687,7 @@ int E_(OpenEncoder)( vlc_object_t *p_this )
         p_sys->p_buffer_out = malloc( 2 * AVCODEC_MAX_AUDIO_FRAME_SIZE );
         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 );
@@ -725,12 +791,12 @@ static block_t *EncodeVideo( encoder_t *p_enc, picture_t *p_pict )
             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_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, VLC_FALSE ) )
+                                    VLC_THREAD_PRIORITY_VIDEO, false ) )
             {
                 msg_Err( p_enc, "cannot spawn encoder thread, expect to die soon" );
                 return NULL;
@@ -755,9 +821,7 @@ static block_t *EncodeVideo( encoder_t *p_enc, picture_t *p_pict )
     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', 'g', 'v' ) ||
-        p_enc->fmt_out.i_codec == VLC_FOURCC( 'm', 'p', '1', 'v' ) ||
-        p_enc->fmt_out.i_codec == VLC_FOURCC( 'm', 'p', '2', 'v' ) )
+    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;
 
@@ -809,13 +873,13 @@ static block_t *EncodeVideo( encoder_t *p_enc, picture_t *p_pict )
         if ( p_sys->i_last_pts == frame.pts )
         {
             msg_Warn( p_enc, "almost fed libavcodec with two frames with the "
-                      "same PTS (" I64Fd ")", frame.pts );
+                      "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: " I64Fd ", last: "I64Fd")",
+                      "past (current: %"PRId64 ", last: %"PRId64")",
                       frame.pts, p_sys->i_last_pts );
             return NULL;
         }
@@ -845,7 +909,7 @@ static block_t *EncodeVideo( encoder_t *p_enc, picture_t *p_pict )
         memcpy( p_block->p_buffer, p_sys->p_buffer_out, i_out );
 
         /* FIXME, 3-2 pulldown is not handled correctly */
-        p_block->i_length = I64C(1000000) *
+        p_block->i_length = INT64_C(1000000) *
             p_enc->fmt_in.video.i_frame_rate_base /
                 p_enc->fmt_in.video.i_frame_rate;
 
@@ -958,7 +1022,8 @@ static block_t *EncodeAudio( encoder_t *p_enc, aout_buffer_t *p_aout_buf )
             p_samples = (int16_t *)p_buffer;
         }
 
-        i_out = avcodec_encode_audio( p_sys->p_context, (uint8_t *)p_sys->p_buffer_out,
+        i_out = avcodec_encode_audio( p_sys->p_context,
+                                      (uint8_t *)p_sys->p_buffer_out,
                                       2 * AVCODEC_MAX_AUDIO_FRAME_SIZE,
                                       p_samples );
 
@@ -1001,13 +1066,10 @@ static block_t *EncodeAudio( encoder_t *p_enc, aout_buffer_t *p_aout_buf )
 /*****************************************************************************
  * CloseEncoder: ffmpeg encoder destruction
  *****************************************************************************/
-void E_(CloseEncoder)( vlc_object_t *p_this )
+void CloseEncoder( vlc_object_t *p_this )
 {
     encoder_t *p_enc = (encoder_t *)p_this;
     encoder_sys_t *p_sys = p_enc->p_sys;
-    vlc_value_t lockval;
-
-    var_Get( p_enc->p_libvlc_global, "avcodec", &lockval );
 
     if ( p_sys->b_inited && p_enc->i_threads >= 1 )
     {
@@ -1021,19 +1083,19 @@ void E_(CloseEncoder)( vlc_object_t *p_this )
             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] );
+            vlc_object_release( pp_contexts[i] );
         }
 
         free( pp_contexts );
     }
 
-    vlc_mutex_lock( lockval.p_address );
+    vlc_mutex_t *lock = var_AcquireMutex( "avcodec" );
     avcodec_close( p_sys->p_context );
-    vlc_mutex_unlock( lockval.p_address );
+    vlc_mutex_unlock( lock );
     av_free( p_sys->p_context );
 
-    if( p_sys->p_buffer ) free( p_sys->p_buffer );
-    if( p_sys->p_buffer_out ) free( p_sys->p_buffer_out );
+    free( p_sys->p_buffer );
+    free( p_sys->p_buffer_out );
 
     free( p_sys );
 }