X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavcodec%2Flibx265.c;h=a1bd205201d3c18d50658597089ec64a03d77818;hb=11bc79089378a5ec00547d0f85bc152afdf30dfa;hp=07bca81aeff9ae74ca0d89b7ec73adf3b0020019;hpb=68bac50604a52760ccb9d9dd43f100436fd66b71;p=ffmpeg diff --git a/libavcodec/libx265.c b/libavcodec/libx265.c index 07bca81aeff..a1bd205201d 100644 --- a/libavcodec/libx265.c +++ b/libavcodec/libx265.c @@ -33,6 +33,7 @@ #include "libavutil/pixdesc.h" #include "avcodec.h" #include "internal.h" +#include "packet_internal.h" typedef struct libx265Context { const AVClass *class; @@ -42,11 +43,18 @@ typedef struct libx265Context { const x265_api *api; float crf; + int cqp; int forced_idr; char *preset; char *tune; char *profile; - char *x265_opts; + AVDictionary *x265_opts; + + /** + * If the encoder does not support ROI then warn the first time we + * encounter a frame with ROI side data. + */ + int roi_warned; } libx265Context; static int is_keyframe(NalUnitType naltype) @@ -76,10 +84,41 @@ static av_cold int libx265_encode_close(AVCodecContext *avctx) return 0; } +static av_cold int libx265_param_parse_float(AVCodecContext *avctx, + const char *key, float value) +{ + libx265Context *ctx = avctx->priv_data; + char buf[256]; + + snprintf(buf, sizeof(buf), "%2.2f", value); + if (ctx->api->param_parse(ctx->params, key, buf) == X265_PARAM_BAD_VALUE) { + av_log(avctx, AV_LOG_ERROR, "Invalid value %2.2f for param \"%s\".\n", value, key); + return AVERROR(EINVAL); + } + + return 0; +} + +static av_cold int libx265_param_parse_int(AVCodecContext *avctx, + const char *key, int value) +{ + libx265Context *ctx = avctx->priv_data; + char buf[256]; + + snprintf(buf, sizeof(buf), "%d", value); + if (ctx->api->param_parse(ctx->params, key, buf) == X265_PARAM_BAD_VALUE) { + av_log(avctx, AV_LOG_ERROR, "Invalid value %d for param \"%s\".\n", value, key); + return AVERROR(EINVAL); + } + + return 0; +} + static av_cold int libx265_encode_init(AVCodecContext *avctx) { libx265Context *ctx = avctx->priv_data; AVCPBProperties *cpb_props = NULL; + int ret; ctx->api = x265_api_get(av_pix_fmt_desc_get(avctx->pix_fmt)->comp[0].depth); if (!ctx->api) @@ -133,6 +172,14 @@ static av_cold int libx265_encode_init(AVCodecContext *avctx) return AVERROR(EINVAL); } + + ctx->params->vui.bEnableVideoSignalTypePresentFlag = 1; + + ctx->params->vui.bEnableVideoFullRangeFlag = avctx->pix_fmt == AV_PIX_FMT_YUVJ420P || + avctx->pix_fmt == AV_PIX_FMT_YUVJ422P || + avctx->pix_fmt == AV_PIX_FMT_YUVJ444P || + avctx->color_range == AVCOL_RANGE_JPEG; + if ((avctx->color_primaries <= AVCOL_PRI_SMPTE432 && avctx->color_primaries != AVCOL_PRI_UNSPECIFIED) || (avctx->color_trc <= AVCOL_TRC_ARIB_STD_B67 && @@ -140,12 +187,15 @@ static av_cold int libx265_encode_init(AVCodecContext *avctx) (avctx->colorspace <= AVCOL_SPC_ICTCP && avctx->colorspace != AVCOL_SPC_UNSPECIFIED)) { - ctx->params->vui.bEnableVideoSignalTypePresentFlag = 1; ctx->params->vui.bEnableColorDescriptionPresentFlag = 1; // x265 validates the parameters internally ctx->params->vui.colorPrimaries = avctx->color_primaries; ctx->params->vui.transferCharacteristics = avctx->color_trc; +#if X265_BUILD >= 159 + if (avctx->color_trc == AVCOL_TRC_ARIB_STD_B67) + ctx->params->preferredTransferCharacteristics = ctx->params->vui.transferCharacteristics; +#endif ctx->params->vui.matrixCoeffs = avctx->colorspace; } @@ -209,6 +259,48 @@ static av_cold int libx265_encode_init(AVCodecContext *avctx) } else if (avctx->bit_rate > 0) { ctx->params->rc.bitrate = avctx->bit_rate / 1000; ctx->params->rc.rateControlMode = X265_RC_ABR; + } else if (ctx->cqp >= 0) { + ret = libx265_param_parse_int(avctx, "qp", ctx->cqp); + if (ret < 0) + return ret; + } + +#if X265_BUILD >= 89 + if (avctx->qmin >= 0) { + ret = libx265_param_parse_int(avctx, "qpmin", avctx->qmin); + if (ret < 0) + return ret; + } + if (avctx->qmax >= 0) { + ret = libx265_param_parse_int(avctx, "qpmax", avctx->qmax); + if (ret < 0) + return ret; + } +#endif + if (avctx->max_qdiff >= 0) { + ret = libx265_param_parse_int(avctx, "qpstep", avctx->max_qdiff); + if (ret < 0) + return ret; + } + if (avctx->qblur >= 0) { + ret = libx265_param_parse_float(avctx, "qblur", avctx->qblur); + if (ret < 0) + return ret; + } + if (avctx->qcompress >= 0) { + ret = libx265_param_parse_float(avctx, "qcomp", avctx->qcompress); + if (ret < 0) + return ret; + } + if (avctx->i_quant_factor >= 0) { + ret = libx265_param_parse_float(avctx, "ipratio", avctx->i_quant_factor); + if (ret < 0) + return ret; + } + if (avctx->b_quant_factor >= 0) { + ret = libx265_param_parse_float(avctx, "pbratio", avctx->b_quant_factor); + if (ret < 0) + return ret; } ctx->params->rc.vbvBufferSize = avctx->rc_buffer_size / 1000; @@ -218,34 +310,50 @@ static av_cold int libx265_encode_init(AVCodecContext *avctx) if (!cpb_props) return AVERROR(ENOMEM); cpb_props->buffer_size = ctx->params->rc.vbvBufferSize * 1000; - cpb_props->max_bitrate = ctx->params->rc.vbvMaxBitrate * 1000; - cpb_props->avg_bitrate = ctx->params->rc.bitrate * 1000; + cpb_props->max_bitrate = ctx->params->rc.vbvMaxBitrate * 1000LL; + cpb_props->avg_bitrate = ctx->params->rc.bitrate * 1000LL; if (!(avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER)) ctx->params->bRepeatHeaders = 1; - if (ctx->x265_opts) { - AVDictionary *dict = NULL; - AVDictionaryEntry *en = NULL; + if (avctx->gop_size >= 0) { + ret = libx265_param_parse_int(avctx, "keyint", avctx->gop_size); + if (ret < 0) + return ret; + } + if (avctx->keyint_min > 0) { + ret = libx265_param_parse_int(avctx, "min-keyint", avctx->keyint_min); + if (ret < 0) + return ret; + } + if (avctx->max_b_frames >= 0) { + ret = libx265_param_parse_int(avctx, "bframes", avctx->max_b_frames); + if (ret < 0) + return ret; + } + if (avctx->refs >= 0) { + ret = libx265_param_parse_int(avctx, "ref", avctx->refs); + if (ret < 0) + return ret; + } - if (!av_dict_parse_string(&dict, ctx->x265_opts, "=", ":", 0)) { - while ((en = av_dict_get(dict, "", en, AV_DICT_IGNORE_SUFFIX))) { - int parse_ret = ctx->api->param_parse(ctx->params, en->key, en->value); - - switch (parse_ret) { - case X265_PARAM_BAD_NAME: - av_log(avctx, AV_LOG_WARNING, - "Unknown option: %s.\n", en->key); - break; - case X265_PARAM_BAD_VALUE: - av_log(avctx, AV_LOG_WARNING, - "Invalid value for %s: %s.\n", en->key, en->value); - break; - default: - break; - } + { + AVDictionaryEntry *en = NULL; + while ((en = av_dict_get(ctx->x265_opts, "", en, AV_DICT_IGNORE_SUFFIX))) { + int parse_ret = ctx->api->param_parse(ctx->params, en->key, en->value); + + switch (parse_ret) { + case X265_PARAM_BAD_NAME: + av_log(avctx, AV_LOG_WARNING, + "Unknown option: %s.\n", en->key); + break; + case X265_PARAM_BAD_VALUE: + av_log(avctx, AV_LOG_WARNING, + "Invalid value for %s: %s.\n", en->key, en->value); + break; + default: + break; } - av_dict_free(&dict); } } @@ -293,6 +401,7 @@ static av_cold int libx265_encode_init(AVCodecContext *avctx) } memcpy(avctx->extradata, nal[0].payload, avctx->extradata_size); + memset(avctx->extradata + avctx->extradata_size, 0, AV_INPUT_BUFFER_PADDING_SIZE); } return 0; @@ -303,33 +412,45 @@ static av_cold int libx265_encode_set_roi(libx265Context *ctx, const AVFrame *fr AVFrameSideData *sd = av_frame_get_side_data(frame, AV_FRAME_DATA_REGIONS_OF_INTEREST); if (sd) { if (ctx->params->rc.aqMode == X265_AQ_NONE) { - av_log(ctx, AV_LOG_WARNING, "Adaptive quantization must be enabled to use ROI encoding, skipping ROI.\n"); + if (!ctx->roi_warned) { + ctx->roi_warned = 1; + av_log(ctx, AV_LOG_WARNING, "Adaptive quantization must be enabled to use ROI encoding, skipping ROI.\n"); + } } else { /* 8x8 block when qg-size is 8, 16*16 block otherwise. */ int mb_size = (ctx->params->rc.qgSize == 8) ? 8 : 16; int mbx = (frame->width + mb_size - 1) / mb_size; int mby = (frame->height + mb_size - 1) / mb_size; + int qp_range = 51 + 6 * (pic->bitDepth - 8); int nb_rois; - AVRegionOfInterest *roi; + const AVRegionOfInterest *roi; + uint32_t roi_size; float *qoffsets; /* will be freed after encode is called. */ + + roi = (const AVRegionOfInterest*)sd->data; + roi_size = roi->self_size; + if (!roi_size || sd->size % roi_size != 0) { + av_log(ctx, AV_LOG_ERROR, "Invalid AVRegionOfInterest.self_size.\n"); + return AVERROR(EINVAL); + } + nb_rois = sd->size / roi_size; + qoffsets = av_mallocz_array(mbx * mby, sizeof(*qoffsets)); if (!qoffsets) return AVERROR(ENOMEM); - nb_rois = sd->size / sizeof(AVRegionOfInterest); - roi = (AVRegionOfInterest*)sd->data; - for (int count = 0; count < nb_rois; count++) { - int starty = FFMIN(mby, roi->top / mb_size); - int endy = FFMIN(mby, (roi->bottom + mb_size - 1)/ mb_size); - int startx = FFMIN(mbx, roi->left / mb_size); - int endx = FFMIN(mbx, (roi->right + mb_size - 1)/ mb_size); + // This list must be iterated in reverse because the first + // region in the list applies when regions overlap. + for (int i = nb_rois - 1; i >= 0; i--) { + int startx, endx, starty, endy; float qoffset; - if (roi->self_size == 0) { - av_free(qoffsets); - av_log(ctx, AV_LOG_ERROR, "AVRegionOfInterest.self_size must be set to sizeof(AVRegionOfInterest).\n"); - return AVERROR(EINVAL); - } + roi = (const AVRegionOfInterest*)(sd->data + roi_size * i); + + starty = FFMIN(mby, roi->top / mb_size); + endy = FFMIN(mby, (roi->bottom + mb_size - 1)/ mb_size); + startx = FFMIN(mbx, roi->left / mb_size); + endx = FFMIN(mbx, (roi->right + mb_size - 1)/ mb_size); if (roi->qoffset.den == 0) { av_free(qoffsets); @@ -337,18 +458,11 @@ static av_cold int libx265_encode_set_roi(libx265Context *ctx, const AVFrame *fr return AVERROR(EINVAL); } qoffset = roi->qoffset.num * 1.0f / roi->qoffset.den; - qoffset = av_clipf(qoffset, -1.0f, 1.0f); - - /* qp range of x265 is from 0 to 51, just choose 25 as the scale value, - * so the range of final qoffset is [-25.0, 25.0]. - */ - qoffset = qoffset * 25; + qoffset = av_clipf(qoffset * qp_range, -qp_range, +qp_range); for (int y = starty; y < endy; y++) for (int x = startx; x < endx; x++) qoffsets[x + y*mbx] = qoffset; - - roi = (AVRegionOfInterest*)((char*)roi + roi->self_size); } pic->quantOffsets = qoffsets; @@ -365,6 +479,7 @@ static int libx265_encode_frame(AVCodecContext *avctx, AVPacket *pkt, x265_picture x265pic_out = { 0 }; x265_nal *nal; uint8_t *dst; + int pict_type; int payload = 0; int nnal; int ret; @@ -390,6 +505,16 @@ static int libx265_encode_frame(AVCodecContext *avctx, AVPacket *pkt, ret = libx265_encode_set_roi(ctx, pic, &x265pic); if (ret < 0) return ret; + + if (pic->reordered_opaque) { + x265pic.userData = av_malloc(sizeof(pic->reordered_opaque)); + if (!x265pic.userData) { + av_freep(&x265pic.quantOffsets); + return AVERROR(ENOMEM); + } + + memcpy(x265pic.userData, &pic->reordered_opaque, sizeof(pic->reordered_opaque)); + } } ret = ctx->api->encoder_encode(ctx->encoder, &nal, &nnal, @@ -424,22 +549,22 @@ static int libx265_encode_frame(AVCodecContext *avctx, AVPacket *pkt, pkt->pts = x265pic_out.pts; pkt->dts = x265pic_out.dts; -#if FF_API_CODED_FRAME -FF_DISABLE_DEPRECATION_WARNINGS switch (x265pic_out.sliceType) { case X265_TYPE_IDR: case X265_TYPE_I: - avctx->coded_frame->pict_type = AV_PICTURE_TYPE_I; + pict_type = AV_PICTURE_TYPE_I; break; case X265_TYPE_P: - avctx->coded_frame->pict_type = AV_PICTURE_TYPE_P; + pict_type = AV_PICTURE_TYPE_P; break; case X265_TYPE_B: - avctx->coded_frame->pict_type = AV_PICTURE_TYPE_B; + case X265_TYPE_BREF: + pict_type = AV_PICTURE_TYPE_B; break; + default: + av_log(avctx, AV_LOG_ERROR, "Unknown picture type encountered.\n"); + return AVERROR_EXTERNAL; } -FF_ENABLE_DEPRECATION_WARNINGS -#endif #if X265_BUILD >= 130 if (x265pic_out.sliceType == X265_TYPE_B) @@ -448,14 +573,25 @@ FF_ENABLE_DEPRECATION_WARNINGS #endif pkt->flags |= AV_PKT_FLAG_DISPOSABLE; + ff_side_data_set_encoder_stats(pkt, x265pic_out.frameData.qp * FF_QP2LAMBDA, NULL, 0, pict_type); + + if (x265pic_out.userData) { + memcpy(&avctx->reordered_opaque, x265pic_out.userData, sizeof(avctx->reordered_opaque)); + av_freep(&x265pic_out.userData); + } else + avctx->reordered_opaque = 0; + *got_packet = 1; return 0; } static const enum AVPixelFormat x265_csp_eight[] = { AV_PIX_FMT_YUV420P, + AV_PIX_FMT_YUVJ420P, AV_PIX_FMT_YUV422P, + AV_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUV444P, + AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_GBRP, AV_PIX_FMT_GRAY8, AV_PIX_FMT_NONE @@ -463,8 +599,11 @@ static const enum AVPixelFormat x265_csp_eight[] = { static const enum AVPixelFormat x265_csp_ten[] = { AV_PIX_FMT_YUV420P, + AV_PIX_FMT_YUVJ420P, AV_PIX_FMT_YUV422P, + AV_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUV444P, + AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_GBRP, AV_PIX_FMT_YUV420P10, AV_PIX_FMT_YUV422P10, @@ -477,8 +616,11 @@ static const enum AVPixelFormat x265_csp_ten[] = { static const enum AVPixelFormat x265_csp_twelve[] = { AV_PIX_FMT_YUV420P, + AV_PIX_FMT_YUVJ420P, AV_PIX_FMT_YUV422P, + AV_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUV444P, + AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_GBRP, AV_PIX_FMT_YUV420P10, AV_PIX_FMT_YUV422P10, @@ -508,11 +650,12 @@ static av_cold void libx265_encode_init_csp(AVCodec *codec) #define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM static const AVOption options[] = { { "crf", "set the x265 crf", OFFSET(crf), AV_OPT_TYPE_FLOAT, { .dbl = -1 }, -1, FLT_MAX, VE }, + { "qp", "set the x265 qp", OFFSET(cqp), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, VE }, { "forced-idr", "if forcing keyframes, force them as IDR frames", OFFSET(forced_idr),AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE }, { "preset", "set the x265 preset", OFFSET(preset), AV_OPT_TYPE_STRING, { 0 }, 0, 0, VE }, { "tune", "set the x265 tune parameter", OFFSET(tune), AV_OPT_TYPE_STRING, { 0 }, 0, 0, VE }, { "profile", "set the x265 profile", OFFSET(profile), AV_OPT_TYPE_STRING, { 0 }, 0, 0, VE }, - { "x265-params", "set the x265 configuration using a :-separated list of key=value parameters", OFFSET(x265_opts), AV_OPT_TYPE_STRING, { 0 }, 0, 0, VE }, + { "x265-params", "set the x265 configuration using a :-separated list of key=value parameters", OFFSET(x265_opts), AV_OPT_TYPE_DICT, { 0 }, 0, 0, VE }, { NULL } }; @@ -525,6 +668,17 @@ static const AVClass class = { static const AVCodecDefault x265_defaults[] = { { "b", "0" }, + { "bf", "-1" }, + { "g", "-1" }, + { "keyint_min", "-1" }, + { "refs", "-1" }, + { "qmin", "-1" }, + { "qmax", "-1" }, + { "qdiff", "-1" }, + { "qblur", "-1" }, + { "qcomp", "-1" }, + { "i_qfactor", "-1" }, + { "b_qfactor", "-1" }, { NULL }, }; @@ -540,6 +694,8 @@ AVCodec ff_libx265_encoder = { .priv_data_size = sizeof(libx265Context), .priv_class = &class, .defaults = x265_defaults, - .capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_AUTO_THREADS, + .capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_OTHER_THREADS | + AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, + .caps_internal = FF_CODEC_CAP_AUTO_THREADS, .wrapper_name = "libx265", };