X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavcodec%2Flibopenh264enc.c;h=54babc3e975d8bc2ebbbb065ac44040cbcd33a46;hb=bd96c54fe4819b3ca9a975e2083d67f4443c559b;hp=ae6d17c6d2c19e904423e054fc8f0011a4c0e38e;hpb=0c126431f9b290f5651ec62f45627632d94c51ea;p=ffmpeg diff --git a/libavcodec/libopenh264enc.c b/libavcodec/libopenh264enc.c index ae6d17c6d2c..54babc3e975 100644 --- a/libavcodec/libopenh264enc.c +++ b/libavcodec/libopenh264enc.c @@ -37,25 +37,35 @@ #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, ¶m); -#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", };