]> git.sesse.net Git - ffmpeg/blobdiff - libavcodec/libopenh264enc.c
avformat/avio: Add Metacube support
[ffmpeg] / libavcodec / libopenh264enc.c
index ae6d17c6d2c19e904423e054fc8f0011a4c0e38e..54babc3e975d8bc2ebbbb065ac44040cbcd33a46 100644 (file)
 #define SM_SIZELIMITED_SLICE SM_DYN_SLICE
 #endif
 
+#define TARGET_BITRATE_DEFAULT 2*1000*1000
+
 typedef struct SVCContext {
     const AVClass *av_class;
     ISVCEncoder *encoder;
     int slice_mode;
     int loopfilter;
-    char *profile;
+    int profile;
     int max_nal_size;
     int skip_frames;
     int skipped;
-    int cabac;
+#if FF_API_OPENH264_CABAC
+    int cabac;                      // deprecated
+#endif
+    int coder;
+
+    // rate control mode
+    int rc_mode;
 } SVCContext;
 
 #define OFFSET(x) offsetof(SVCContext, x)
 #define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM
+#define DEPRECATED AV_OPT_FLAG_DEPRECATED
 static const AVOption options[] = {
+#if FF_API_OPENH264_SLICE_MODE
 #if OPENH264_VER_AT_LEAST(1, 6)
-    { "slice_mode", "set slice mode", OFFSET(slice_mode), AV_OPT_TYPE_INT, { .i64 = SM_FIXEDSLCNUM_SLICE }, SM_SINGLE_SLICE, SM_RESERVED, VE, "slice_mode" },
+    { "slice_mode", "set slice mode, use slices/max_nal_size", OFFSET(slice_mode), AV_OPT_TYPE_INT, { .i64 = SM_FIXEDSLCNUM_SLICE }, SM_SINGLE_SLICE, SM_RESERVED, VE|DEPRECATED, "slice_mode" },
 #else
-    { "slice_mode", "set slice mode", OFFSET(slice_mode), AV_OPT_TYPE_INT, { .i64 = SM_AUTO_SLICE }, SM_SINGLE_SLICE, SM_RESERVED, VE, "slice_mode" },
+    { "slice_mode", "set slice mode, use slices/max_nal_size", OFFSET(slice_mode), AV_OPT_TYPE_INT, { .i64 = SM_AUTO_SLICE }, SM_SINGLE_SLICE, SM_RESERVED, VE|DEPRECATED, "slice_mode" },
 #endif
         { "fixed", "a fixed number of slices", 0, AV_OPT_TYPE_CONST, { .i64 = SM_FIXEDSLCNUM_SLICE }, 0, 0, VE, "slice_mode" },
 #if OPENH264_VER_AT_LEAST(1, 6)
@@ -65,12 +75,36 @@ static const AVOption options[] = {
         { "rowmb", "one slice per row of macroblocks", 0, AV_OPT_TYPE_CONST, { .i64 = SM_ROWMB_SLICE }, 0, 0, VE, "slice_mode" },
         { "auto", "automatic number of slices according to number of threads", 0, AV_OPT_TYPE_CONST, { .i64 = SM_AUTO_SLICE }, 0, 0, VE, "slice_mode" },
         { "dyn", "Dynamic slicing", 0, AV_OPT_TYPE_CONST, { .i64 = SM_DYN_SLICE }, 0, 0, VE, "slice_mode" },
+#endif
 #endif
     { "loopfilter", "enable loop filter", OFFSET(loopfilter), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, 1, VE },
-    { "profile", "set profile restrictions", OFFSET(profile), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, VE },
+    { "profile", "set profile restrictions", OFFSET(profile), AV_OPT_TYPE_INT, { .i64 = FF_PROFILE_UNKNOWN }, FF_PROFILE_UNKNOWN, 0xffff, VE, "profile" },
+#define PROFILE(name, value)  name, NULL, 0, AV_OPT_TYPE_CONST, { .i64 = value }, 0, 0, VE, "profile"
+        { PROFILE("constrained_baseline", FF_PROFILE_H264_CONSTRAINED_BASELINE) },
+        { PROFILE("main",                 FF_PROFILE_H264_MAIN) },
+        { PROFILE("high",                 FF_PROFILE_H264_HIGH) },
+#undef PROFILE
     { "max_nal_size", "set maximum NAL size in bytes", OFFSET(max_nal_size), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, VE },
     { "allow_skip_frames", "allow skipping frames to hit the target bitrate", OFFSET(skip_frames), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE },
-    { "cabac", "Enable cabac", OFFSET(cabac), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, VE },
+#if FF_API_OPENH264_CABAC
+    { "cabac", "Enable cabac(deprecated, use coder)", OFFSET(cabac), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, VE|DEPRECATED },
+#endif
+    { "coder", "Coder type",  OFFSET(coder), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 1, VE, "coder" },
+        { "default",          NULL, 0, AV_OPT_TYPE_CONST, { .i64 = -1 }, INT_MIN, INT_MAX, VE, "coder" },
+        { "cavlc",            NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 0 },  INT_MIN, INT_MAX, VE, "coder" },
+        { "cabac",            NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 1 },  INT_MIN, INT_MAX, VE, "coder" },
+        { "vlc",              NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 0 },  INT_MIN, INT_MAX, VE, "coder" },
+        { "ac",               NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 1 },  INT_MIN, INT_MAX, VE, "coder" },
+
+    { "rc_mode", "Select rate control mode", OFFSET(rc_mode), AV_OPT_TYPE_INT, { .i64 = RC_QUALITY_MODE }, RC_OFF_MODE, RC_TIMESTAMP_MODE, VE, "rc_mode" },
+        { "off",       "bit rate control off",                                                 0, AV_OPT_TYPE_CONST, { .i64 = RC_OFF_MODE },         0, 0, VE, "rc_mode" },
+        { "quality",   "quality mode",                                                         0, AV_OPT_TYPE_CONST, { .i64 = RC_QUALITY_MODE },     0, 0, VE, "rc_mode" },
+        { "bitrate",   "bitrate mode",                                                         0, AV_OPT_TYPE_CONST, { .i64 = RC_BITRATE_MODE },     0, 0, VE, "rc_mode" },
+        { "buffer",    "using buffer status to adjust the video quality (no bitrate control)", 0, AV_OPT_TYPE_CONST, { .i64 = RC_BUFFERBASED_MODE }, 0, 0, VE, "rc_mode" },
+#if OPENH264_VER_AT_LEAST(1, 4)
+        { "timestamp", "bit rate control based on timestamp",                                  0, AV_OPT_TYPE_CONST, { .i64 = RC_TIMESTAMP_MODE },   0, 0, VE, "rc_mode" },
+#endif
+
     { NULL }
 };
 
@@ -122,19 +156,25 @@ static av_cold int svc_encode_init(AVCodecContext *avctx)
 
     (*s->encoder)->GetDefaultParams(s->encoder, &param);
 
-#if FF_API_CODER_TYPE
-FF_DISABLE_DEPRECATION_WARNINGS
-    if (!s->cabac)
-        s->cabac = avctx->coder_type == FF_CODER_TYPE_AC;
-FF_ENABLE_DEPRECATION_WARNINGS
-#endif
-
-    param.fMaxFrameRate              = 1/av_q2d(avctx->time_base);
+    if (avctx->framerate.num > 0 && avctx->framerate.den > 0) {
+        param.fMaxFrameRate = av_q2d(avctx->framerate);
+    } else {
+        if (avctx->ticks_per_frame > INT_MAX / avctx->time_base.num) {
+            av_log(avctx, AV_LOG_ERROR,
+                   "Could not set framerate for libopenh264enc: integer overflow\n");
+            return AVERROR(EINVAL);
+        }
+        param.fMaxFrameRate = 1.0 / av_q2d(avctx->time_base) / FFMAX(avctx->ticks_per_frame, 1);
+    }
     param.iPicWidth                  = avctx->width;
     param.iPicHeight                 = avctx->height;
-    param.iTargetBitrate             = avctx->bit_rate;
+    param.iTargetBitrate             = avctx->bit_rate > 0 ? avctx->bit_rate : TARGET_BITRATE_DEFAULT;
     param.iMaxBitrate                = FFMAX(avctx->rc_max_rate, avctx->bit_rate);
-    param.iRCMode                    = RC_QUALITY_MODE;
+    param.iRCMode                    = s->rc_mode;
+    if (avctx->qmax >= 0)
+        param.iMaxQp                 = av_clip(avctx->qmax, 1, 51);
+    if (avctx->qmin >= 0)
+        param.iMinQp                 = av_clip(avctx->qmin, 1, param.iMaxQp);
     param.iTemporalLayerNum          = 1;
     param.iSpatialLayerNum           = 1;
     param.bEnableDenoise             = 0;
@@ -143,7 +183,8 @@ FF_ENABLE_DEPRECATION_WARNINGS
     param.bEnableFrameSkip           = s->skip_frames;
     param.bEnableLongTermReference   = 0;
     param.iLtrMarkPeriod             = 30;
-    param.uiIntraPeriod              = avctx->gop_size;
+    if (avctx->gop_size >= 0)
+        param.uiIntraPeriod          = avctx->gop_size;
 #if OPENH264_VER_AT_LEAST(1, 4)
     param.eSpsPpsIdStrategy          = CONSTANT_ID;
 #else
@@ -153,10 +194,56 @@ FF_ENABLE_DEPRECATION_WARNINGS
     param.iLoopFilterDisableIdc      = !s->loopfilter;
     param.iEntropyCodingModeFlag     = 0;
     param.iMultipleThreadIdc         = avctx->thread_count;
-    if (s->profile && !strcmp(s->profile, "main"))
+
+    /* Allow specifying the libopenh264 profile through AVCodecContext. */
+    if (FF_PROFILE_UNKNOWN == s->profile &&
+        FF_PROFILE_UNKNOWN != avctx->profile)
+        switch (avctx->profile) {
+        case FF_PROFILE_H264_HIGH:
+        case FF_PROFILE_H264_MAIN:
+        case FF_PROFILE_H264_CONSTRAINED_BASELINE:
+            s->profile = avctx->profile;
+            break;
+        default:
+            av_log(avctx, AV_LOG_WARNING,
+                   "Unsupported avctx->profile: %d.\n", avctx->profile);
+            break;
+        }
+
+    if (s->profile == FF_PROFILE_UNKNOWN && s->coder >= 0)
+        s->profile = s->coder == 0 ? FF_PROFILE_H264_CONSTRAINED_BASELINE :
+#if OPENH264_VER_AT_LEAST(1, 8)
+                                     FF_PROFILE_H264_HIGH;
+#else
+                                     FF_PROFILE_H264_MAIN;
+#endif
+
+    switch (s->profile) {
+#if OPENH264_VER_AT_LEAST(1, 8)
+    case FF_PROFILE_H264_HIGH:
         param.iEntropyCodingModeFlag = 1;
-    else if (!s->profile && s->cabac)
+        av_log(avctx, AV_LOG_VERBOSE, "Using CABAC, "
+                "select EProfileIdc PRO_HIGH in libopenh264.\n");
+        break;
+#else
+    case FF_PROFILE_H264_MAIN:
         param.iEntropyCodingModeFlag = 1;
+        av_log(avctx, AV_LOG_VERBOSE, "Using CABAC, "
+                "select EProfileIdc PRO_MAIN in libopenh264.\n");
+        break;
+#endif
+    case FF_PROFILE_H264_CONSTRAINED_BASELINE:
+    case FF_PROFILE_UNKNOWN:
+        param.iEntropyCodingModeFlag = 0;
+        av_log(avctx, AV_LOG_VERBOSE, "Using CAVLC, "
+               "select EProfileIdc PRO_BASELINE in libopenh264.\n");
+        break;
+    default:
+        param.iEntropyCodingModeFlag = 0;
+        av_log(avctx, AV_LOG_WARNING, "Unsupported profile, "
+               "select EProfileIdc PRO_BASELINE in libopenh264.\n");
+        break;
+    }
 
     param.sSpatialLayers[0].iVideoWidth         = param.iPicWidth;
     param.sSpatialLayers[0].iVideoHeight        = param.iPicHeight;
@@ -199,8 +286,7 @@ FF_ENABLE_DEPRECATION_WARNINGS
             param.sSpatialLayers[0].eAspectRatio = asp_idc[i];
         }
         param.sSpatialLayers[0].bAspectRatioPresent = true;
-    }
-    else {
+    } else {
         param.sSpatialLayers[0].bAspectRatioPresent = false;
     }
 #endif
@@ -225,9 +311,11 @@ FF_ENABLE_DEPRECATION_WARNINGS
     param.sSpatialLayers[0].sSliceCfg.uiSliceMode               = s->slice_mode;
     param.sSpatialLayers[0].sSliceCfg.sSliceArgument.uiSliceNum = avctx->slices;
 #endif
+    if (avctx->slices == 0 && s->slice_mode == SM_FIXEDSLCNUM_SLICE)
+        av_log(avctx, AV_LOG_WARNING, "Slice count will be set automatically\n");
 
     if (s->slice_mode == SM_SIZELIMITED_SLICE) {
-        if (s->max_nal_size){
+        if (s->max_nal_size) {
             param.uiMaxNalSize = s->max_nal_size;
 #if OPENH264_VER_AT_LEAST(1, 6)
             param.sSpatialLayers[0].sSliceArgument.uiSliceSizeConstraint = s->max_nal_size;
@@ -332,7 +420,15 @@ static int svc_encode_frame(AVCodecContext *avctx, AVPacket *avpkt,
     return 0;
 }
 
-AVCodec ff_libopenh264_encoder = {
+static const AVCodecDefault svc_enc_defaults[] = {
+    { "b",         "0"     },
+    { "g",         "-1"    },
+    { "qmin",      "-1"    },
+    { "qmax",      "-1"    },
+    { NULL },
+};
+
+const AVCodec ff_libopenh264_encoder = {
     .name           = "libopenh264",
     .long_name      = NULL_IF_CONFIG_SMALL("OpenH264 H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10"),
     .type           = AVMEDIA_TYPE_VIDEO,
@@ -341,10 +437,12 @@ AVCodec ff_libopenh264_encoder = {
     .init           = svc_encode_init,
     .encode2        = svc_encode_frame,
     .close          = svc_encode_close,
-    .capabilities   = AV_CODEC_CAP_AUTO_THREADS,
-    .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE | FF_CODEC_CAP_INIT_CLEANUP,
+    .capabilities   = AV_CODEC_CAP_OTHER_THREADS,
+    .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE | FF_CODEC_CAP_INIT_CLEANUP |
+                      FF_CODEC_CAP_AUTO_THREADS,
     .pix_fmts       = (const enum AVPixelFormat[]){ AV_PIX_FMT_YUV420P,
                                                     AV_PIX_FMT_NONE },
+    .defaults       = svc_enc_defaults,
     .priv_class     = &class,
     .wrapper_name   = "libopenh264",
 };