]> git.sesse.net Git - ffmpeg/blobdiff - libavcodec/libx265.c
avcodec: Remove deprecated AVCodecContext.coded_frame
[ffmpeg] / libavcodec / libx265.c
index 07bca81aeff9ae74ca0d89b7ec73adf3b0020019..a1bd205201d3c18d50658597089ec64a03d77818 100644 (file)
@@ -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",
 };