diff options
Diffstat (limited to 'libavcodec/libx265.c')
-rw-r--r-- | libavcodec/libx265.c | 177 |
1 files changed, 156 insertions, 21 deletions
diff --git a/libavcodec/libx265.c b/libavcodec/libx265.c index 8f1d60b4e5..98415366da 100644 --- a/libavcodec/libx265.c +++ b/libavcodec/libx265.c @@ -3,20 +3,20 @@ * * Copyright (c) 2013-2014 Derek Buitenhuis * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -45,6 +45,7 @@ typedef struct libx265Context { int forced_idr; char *preset; char *tune; + char *profile; char *x265_opts; } libx265Context; @@ -83,14 +84,6 @@ static av_cold int libx265_encode_init(AVCodecContext *avctx) if (!ctx->api) ctx->api = x265_api_get(0); - if (avctx->strict_std_compliance > FF_COMPLIANCE_EXPERIMENTAL && - !av_pix_fmt_desc_get(avctx->pix_fmt)->log2_chroma_w) { - av_log(avctx, AV_LOG_ERROR, - "4:2:2 and 4:4:4 support is not fully defined for HEVC yet. " - "Set -strict experimental to encode anyway.\n"); - return AVERROR(ENOSYS); - } - ctx->params = ctx->api->param_alloc(); if (!ctx->params) { av_log(avctx, AV_LOG_ERROR, "Could not allocate x265 param structure.\n"); @@ -121,6 +114,7 @@ static av_cold int libx265_encode_init(AVCodecContext *avctx) ctx->params->sourceWidth = avctx->width; ctx->params->sourceHeight = avctx->height; ctx->params->bEnablePsnr = !!(avctx->flags & AV_CODEC_FLAG_PSNR); + ctx->params->bOpenGOP = !(avctx->flags & AV_CODEC_FLAG_CLOSED_GOP); /* Tune the CTU size based on input resolution. */ if (ctx->params->sourceWidth < 64 || ctx->params->sourceHeight < 64) @@ -133,11 +127,11 @@ static av_cold int libx265_encode_init(AVCodecContext *avctx) return AVERROR(EINVAL); } - if ((avctx->color_primaries <= AVCOL_PRI_BT2020 && + if ((avctx->color_primaries <= AVCOL_PRI_SMPTE432 && avctx->color_primaries != AVCOL_PRI_UNSPECIFIED) || - (avctx->color_trc <= AVCOL_TRC_BT2020_12 && + (avctx->color_trc <= AVCOL_TRC_ARIB_STD_B67 && avctx->color_trc != AVCOL_TRC_UNSPECIFIED) || - (avctx->colorspace <= AVCOL_SPC_BT2020_CL && + (avctx->colorspace <= AVCOL_SPC_ICTCP && avctx->colorspace != AVCOL_SPC_UNSPECIFIED)) { ctx->params->vui.bEnableVideoSignalTypePresentFlag = 1; @@ -166,16 +160,36 @@ static av_cold int libx265_encode_init(AVCodecContext *avctx) switch (avctx->pix_fmt) { case AV_PIX_FMT_YUV420P: case AV_PIX_FMT_YUV420P10: + case AV_PIX_FMT_YUV420P12: ctx->params->internalCsp = X265_CSP_I420; break; case AV_PIX_FMT_YUV422P: case AV_PIX_FMT_YUV422P10: + case AV_PIX_FMT_YUV422P12: ctx->params->internalCsp = X265_CSP_I422; break; + case AV_PIX_FMT_GBRP: + case AV_PIX_FMT_GBRP10: + case AV_PIX_FMT_GBRP12: + ctx->params->vui.matrixCoeffs = AVCOL_SPC_RGB; + ctx->params->vui.bEnableVideoSignalTypePresentFlag = 1; + ctx->params->vui.bEnableColorDescriptionPresentFlag = 1; case AV_PIX_FMT_YUV444P: case AV_PIX_FMT_YUV444P10: + case AV_PIX_FMT_YUV444P12: ctx->params->internalCsp = X265_CSP_I444; break; + case AV_PIX_FMT_GRAY8: + case AV_PIX_FMT_GRAY10: + case AV_PIX_FMT_GRAY12: + if (ctx->api->api_build_number < 85) { + av_log(avctx, AV_LOG_ERROR, + "libx265 version is %d, must be at least 85 for gray encoding.\n", + ctx->api->api_build_number); + return AVERROR_INVALIDDATA; + } + ctx->params->internalCsp = X265_CSP_I400; + break; } if (ctx->crf >= 0) { @@ -191,6 +205,9 @@ static av_cold int libx265_encode_init(AVCodecContext *avctx) ctx->params->rc.rateControlMode = X265_RC_ABR; } + ctx->params->rc.vbvBufferSize = avctx->rc_buffer_size / 1000; + ctx->params->rc.vbvMaxBitrate = avctx->rc_max_rate / 1000; + if (!(avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER)) ctx->params->bRepeatHeaders = 1; @@ -219,6 +236,23 @@ static av_cold int libx265_encode_init(AVCodecContext *avctx) } } + if (ctx->params->rc.vbvBufferSize && avctx->rc_initial_buffer_occupancy > 1000 && + ctx->params->rc.vbvBufferInit == 0.9) { + ctx->params->rc.vbvBufferInit = (float)avctx->rc_initial_buffer_occupancy / 1000; + } + + if (ctx->profile) { + if (ctx->api->param_apply_profile(ctx->params, ctx->profile) < 0) { + int i; + av_log(avctx, AV_LOG_ERROR, "Invalid or incompatible profile set: %s.\n", ctx->profile); + av_log(avctx, AV_LOG_INFO, "Possible profiles:"); + for (i = 0; x265_profile_names[i]; i++) + av_log(avctx, AV_LOG_INFO, " %s", x265_profile_names[i]); + av_log(avctx, AV_LOG_INFO, "\n"); + return AVERROR(EINVAL); + } + } + ctx->encoder = ctx->api->encoder_open(ctx->params); if (!ctx->encoder) { av_log(avctx, AV_LOG_ERROR, "Cannot open libx265 encoder.\n"); @@ -251,6 +285,65 @@ static av_cold int libx265_encode_init(AVCodecContext *avctx) return 0; } +static av_cold int libx265_encode_set_roi(libx265Context *ctx, const AVFrame *frame, x265_picture* pic) +{ + 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"); + } 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 nb_rois; + AVRegionOfInterest *roi; + float *qoffsets; /* will be freed after encode is called. */ + 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); + 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); + } + + if (roi->qoffset.den == 0) { + av_free(qoffsets); + av_log(ctx, AV_LOG_ERROR, "AVRegionOfInterest.qoffset.den must not be zero.\n"); + 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; + + 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; + } + } + return 0; +} + static int libx265_encode_frame(AVCodecContext *avctx, AVPacket *pkt, const AVFrame *pic, int *got_packet) { @@ -280,12 +373,19 @@ static int libx265_encode_frame(AVCodecContext *avctx, AVPacket *pkt, pic->pict_type == AV_PICTURE_TYPE_P ? X265_TYPE_P : pic->pict_type == AV_PICTURE_TYPE_B ? X265_TYPE_B : X265_TYPE_AUTO; + + ret = libx265_encode_set_roi(ctx, pic, &x265pic); + if (ret < 0) + return ret; } ret = ctx->api->encoder_encode(ctx->encoder, &nal, &nnal, pic ? &x265pic : NULL, &x265pic_out); + + av_freep(&x265pic.quantOffsets); + if (ret < 0) - return AVERROR_UNKNOWN; + return AVERROR_EXTERNAL; if (!nnal) return 0; @@ -293,7 +393,7 @@ static int libx265_encode_frame(AVCodecContext *avctx, AVPacket *pkt, for (i = 0; i < nnal; i++) payload += nal[i].sizeBytes; - ret = ff_alloc_packet(pkt, payload); + ret = ff_alloc_packet2(avctx, pkt, payload, payload); if (ret < 0) { av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n"); return ret; @@ -328,6 +428,13 @@ FF_DISABLE_DEPRECATION_WARNINGS FF_ENABLE_DEPRECATION_WARNINGS #endif +#if X265_BUILD >= 130 + if (x265pic_out.sliceType == X265_TYPE_B) +#else + if (x265pic_out.frameData.sliceType == 'b') +#endif + pkt->flags |= AV_PKT_FLAG_DISPOSABLE; + *got_packet = 1; return 0; } @@ -336,6 +443,22 @@ static const enum AVPixelFormat x265_csp_eight[] = { AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV444P, + AV_PIX_FMT_GBRP, + AV_PIX_FMT_GRAY8, + AV_PIX_FMT_NONE +}; + +static const enum AVPixelFormat x265_csp_ten[] = { + AV_PIX_FMT_YUV420P, + AV_PIX_FMT_YUV422P, + AV_PIX_FMT_YUV444P, + AV_PIX_FMT_GBRP, + AV_PIX_FMT_YUV420P10, + AV_PIX_FMT_YUV422P10, + AV_PIX_FMT_YUV444P10, + AV_PIX_FMT_GBRP10, + AV_PIX_FMT_GRAY8, + AV_PIX_FMT_GRAY10, AV_PIX_FMT_NONE }; @@ -343,27 +466,39 @@ static const enum AVPixelFormat x265_csp_twelve[] = { AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV444P, + AV_PIX_FMT_GBRP, AV_PIX_FMT_YUV420P10, AV_PIX_FMT_YUV422P10, AV_PIX_FMT_YUV444P10, + AV_PIX_FMT_GBRP10, + AV_PIX_FMT_YUV420P12, + AV_PIX_FMT_YUV422P12, + AV_PIX_FMT_YUV444P12, + AV_PIX_FMT_GBRP12, + AV_PIX_FMT_GRAY8, + AV_PIX_FMT_GRAY10, + AV_PIX_FMT_GRAY12, AV_PIX_FMT_NONE }; static av_cold void libx265_encode_init_csp(AVCodec *codec) { - if (x265_max_bit_depth == 8) - codec->pix_fmts = x265_csp_eight; - else if (x265_max_bit_depth == 12) + if (x265_api_get(12)) codec->pix_fmts = x265_csp_twelve; + else if (x265_api_get(10)) + codec->pix_fmts = x265_csp_ten; + else if (x265_api_get(8)) + codec->pix_fmts = x265_csp_eight; } #define OFFSET(x) offsetof(libx265Context, x) #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 }, - { "forced-idr", "if forcing keyframes, force them as IDR frames", OFFSET(forced_idr),AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, 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 }, { NULL } }; |