#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)
{ "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 }
};
static const AVClass class = {
- .class_name = "libvo_amrwbenc",
+ .class_name = "libopenh264enc",
.item_name = av_default_item_name,
.option = options,
.version = LIBAVUTIL_VERSION_INT,
(*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;
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
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;
param.sSpatialLayers[0].iSpatialBitrate = param.iTargetBitrate;
param.sSpatialLayers[0].iMaxSpatialBitrate = param.iMaxBitrate;
+#if OPENH264_VER_AT_LEAST(1, 7)
+ if (avctx->sample_aspect_ratio.num && avctx->sample_aspect_ratio.den) {
+ // Table E-1.
+ static const AVRational sar_idc[] = {
+ { 0, 0 }, // Unspecified (never written here).
+ { 1, 1 }, { 12, 11 }, { 10, 11 }, { 16, 11 },
+ { 40, 33 }, { 24, 11 }, { 20, 11 }, { 32, 11 },
+ { 80, 33 }, { 18, 11 }, { 15, 11 }, { 64, 33 },
+ { 160, 99 }, // Last 3 are unknown to openh264: { 4, 3 }, { 3, 2 }, { 2, 1 },
+ };
+ static const ESampleAspectRatio asp_idc[] = {
+ ASP_UNSPECIFIED,
+ ASP_1x1, ASP_12x11, ASP_10x11, ASP_16x11,
+ ASP_40x33, ASP_24x11, ASP_20x11, ASP_32x11,
+ ASP_80x33, ASP_18x11, ASP_15x11, ASP_64x33,
+ ASP_160x99,
+ };
+ int num, den, i;
+
+ av_reduce(&num, &den, avctx->sample_aspect_ratio.num,
+ avctx->sample_aspect_ratio.den, 65535);
+
+ for (i = 1; i < FF_ARRAY_ELEMS(sar_idc); i++) {
+ if (num == sar_idc[i].num &&
+ den == sar_idc[i].den)
+ break;
+ }
+ if (i == FF_ARRAY_ELEMS(sar_idc)) {
+ param.sSpatialLayers[0].eAspectRatio = ASP_EXT_SAR;
+ param.sSpatialLayers[0].sAspectRatioExtWidth = num;
+ param.sSpatialLayers[0].sAspectRatioExtHeight = den;
+ } else {
+ param.sSpatialLayers[0].eAspectRatio = asp_idc[i];
+ }
+ param.sSpatialLayers[0].bAspectRatioPresent = true;
+ } else {
+ param.sSpatialLayers[0].bAspectRatioPresent = false;
+ }
+#endif
+
if ((avctx->slices > 1) && (s->max_nal_size)) {
av_log(avctx, AV_LOG_ERROR,
"Invalid combination -slices %d and -max_nal_size %d.\n",
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;
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,
.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",
};