]> git.sesse.net Git - ffmpeg/blobdiff - libavcodec/vaapi_encode.c
avcodec/option_table: mark venc_params as a video decoder flag opt type
[ffmpeg] / libavcodec / vaapi_encode.c
index 2dda451882614c77262cc29ff440545cb5a805e8..cb05ebd774a1a259b4f2659c0eeba992f4d53957 100644 (file)
 #include "vaapi_encode.h"
 #include "avcodec.h"
 
+const AVCodecHWConfigInternal *ff_vaapi_encode_hw_configs[] = {
+    HW_CONFIG_ENCODER_FRAMES(VAAPI, VAAPI),
+    NULL,
+};
+
 static const char * const picture_type_name[] = { "IDR", "I", "P", "B" };
 
 static int vaapi_encode_make_packed_header(AVCodecContext *avctx,
@@ -103,6 +108,29 @@ static int vaapi_encode_make_param_buffer(AVCodecContext *avctx,
     return 0;
 }
 
+static int vaapi_encode_make_misc_param_buffer(AVCodecContext *avctx,
+                                               VAAPIEncodePicture *pic,
+                                               int type,
+                                               const void *data, size_t len)
+{
+    // Construct the buffer on the stack - 1KB is much larger than any
+    // current misc parameter buffer type (the largest is EncQuality at
+    // 224 bytes).
+    uint8_t buffer[1024];
+    VAEncMiscParameterBuffer header = {
+        .type = type,
+    };
+    size_t buffer_size = sizeof(header) + len;
+    av_assert0(buffer_size <= sizeof(buffer));
+
+    memcpy(buffer, &header, sizeof(header));
+    memcpy(buffer + sizeof(header), data, len);
+
+    return vaapi_encode_make_param_buffer(avctx, pic,
+                                          VAEncMiscParameterBufferType,
+                                          buffer, buffer_size);
+}
+
 static int vaapi_encode_wait(AVCodecContext *avctx,
                              VAAPIEncodePicture *pic)
 {
@@ -143,6 +171,7 @@ static int vaapi_encode_issue(AVCodecContext *avctx,
     int err, i;
     char data[MAX_PARAM_BUFFER_SIZE];
     size_t bit_len;
+    av_unused AVFrameSideData *sd;
 
     av_log(avctx, AV_LOG_DEBUG, "Issuing encode for pic %"PRId64"/%"PRId64" "
            "as type %s.\n", pic->display_order, pic->encode_order,
@@ -212,10 +241,10 @@ static int vaapi_encode_issue(AVCodecContext *avctx,
 
     if (pic->type == PICTURE_TYPE_IDR) {
         for (i = 0; i < ctx->nb_global_params; i++) {
-            err = vaapi_encode_make_param_buffer(avctx, pic,
-                                                 VAEncMiscParameterBufferType,
-                                                 (char*)ctx->global_params[i],
-                                                 ctx->global_params_size[i]);
+            err = vaapi_encode_make_misc_param_buffer(avctx, pic,
+                                                      ctx->global_params_type[i],
+                                                      ctx->global_params[i],
+                                                      ctx->global_params_size[i]);
             if (err < 0)
                 goto fail;
         }
@@ -412,6 +441,71 @@ static int vaapi_encode_issue(AVCodecContext *avctx,
         }
     }
 
+#if VA_CHECK_VERSION(1, 0, 0)
+    sd = av_frame_get_side_data(pic->input_image,
+                                AV_FRAME_DATA_REGIONS_OF_INTEREST);
+    if (sd && ctx->roi_allowed) {
+        const AVRegionOfInterest *roi;
+        uint32_t roi_size;
+        VAEncMiscParameterBufferROI param_roi;
+        int nb_roi, i, v;
+
+        roi = (const AVRegionOfInterest*)sd->data;
+        roi_size = roi->self_size;
+        av_assert0(roi_size && sd->size % roi_size == 0);
+        nb_roi = sd->size / roi_size;
+        if (nb_roi > ctx->roi_max_regions) {
+            if (!ctx->roi_warned) {
+                av_log(avctx, AV_LOG_WARNING, "More ROIs set than "
+                       "supported by driver (%d > %d).\n",
+                       nb_roi, ctx->roi_max_regions);
+                ctx->roi_warned = 1;
+            }
+            nb_roi = ctx->roi_max_regions;
+        }
+
+        pic->roi = av_mallocz_array(nb_roi, sizeof(*pic->roi));
+        if (!pic->roi) {
+            err = AVERROR(ENOMEM);
+            goto fail;
+        }
+        // For overlapping regions, the first in the array takes priority.
+        for (i = 0; i < nb_roi; i++) {
+            roi = (const AVRegionOfInterest*)(sd->data + roi_size * i);
+
+            av_assert0(roi->qoffset.den != 0);
+            v = roi->qoffset.num * ctx->roi_quant_range / roi->qoffset.den;
+            av_log(avctx, AV_LOG_DEBUG, "ROI: (%d,%d)-(%d,%d) -> %+d.\n",
+                   roi->top, roi->left, roi->bottom, roi->right, v);
+
+            pic->roi[i] = (VAEncROI) {
+                .roi_rectangle = {
+                    .x      = roi->left,
+                    .y      = roi->top,
+                    .width  = roi->right  - roi->left,
+                    .height = roi->bottom - roi->top,
+                },
+                .roi_value = av_clip_int8(v),
+            };
+        }
+
+        param_roi = (VAEncMiscParameterBufferROI) {
+            .num_roi      = nb_roi,
+            .max_delta_qp = INT8_MAX,
+            .min_delta_qp = INT8_MIN,
+            .roi          = pic->roi,
+            .roi_flags.bits.roi_value_is_qp_delta = 1,
+        };
+
+        err = vaapi_encode_make_misc_param_buffer(avctx, pic,
+                                                  VAEncMiscParameterTypeROI,
+                                                  &param_roi,
+                                                  sizeof(param_roi));
+        if (err < 0)
+            goto fail;
+    }
+#endif
+
     vas = vaBeginPicture(ctx->hwctx->display, ctx->va_context,
                          pic->input_surface);
     if (vas != VA_STATUS_SUCCESS) {
@@ -477,6 +571,7 @@ fail_at_end:
     av_freep(&pic->codec_picture_params);
     av_freep(&pic->param_buffers);
     av_freep(&pic->slices);
+    av_freep(&pic->roi);
     av_frame_free(&pic->recon_image);
     av_buffer_unref(&pic->output_buffer_ref);
     pic->output_buffer = VA_INVALID_ID;
@@ -489,6 +584,8 @@ static int vaapi_encode_output(AVCodecContext *avctx,
     VAAPIEncodeContext *ctx = avctx->priv_data;
     VACodedBufferSegment *buf_list, *buf;
     VAStatus vas;
+    int total_size = 0;
+    uint8_t *ptr;
     int err;
 
     err = vaapi_encode_wait(avctx, pic);
@@ -505,15 +602,21 @@ static int vaapi_encode_output(AVCodecContext *avctx,
         goto fail;
     }
 
+    for (buf = buf_list; buf; buf = buf->next)
+        total_size += buf->size;
+
+    err = av_new_packet(pkt, total_size);
+    ptr = pkt->data;
+
+    if (err < 0)
+        goto fail_mapped;
+
     for (buf = buf_list; buf; buf = buf->next) {
         av_log(avctx, AV_LOG_DEBUG, "Output buffer: %u bytes "
                "(status %08x).\n", buf->size, buf->status);
 
-        err = av_new_packet(pkt, buf->size);
-        if (err < 0)
-            goto fail_mapped;
-
-        memcpy(pkt->data, buf->buf, buf->size);
+        memcpy(ptr, buf->buf, buf->size);
+        ptr += buf->size;
     }
 
     if (pic->type == PICTURE_TYPE_IDR)
@@ -611,6 +714,7 @@ static int vaapi_encode_free(AVCodecContext *avctx,
 
     av_freep(&pic->priv_data);
     av_freep(&pic->codec_picture_params);
+    av_freep(&pic->roi);
 
     av_free(pic);
 
@@ -913,6 +1017,32 @@ static int vaapi_encode_clear_old(AVCodecContext *avctx)
     return 0;
 }
 
+static int vaapi_encode_check_frame(AVCodecContext *avctx,
+                                    const AVFrame *frame)
+{
+    VAAPIEncodeContext *ctx = avctx->priv_data;
+
+    if ((frame->crop_top  || frame->crop_bottom ||
+         frame->crop_left || frame->crop_right) && !ctx->crop_warned) {
+        av_log(avctx, AV_LOG_WARNING, "Cropping information on input "
+               "frames ignored due to lack of API support.\n");
+        ctx->crop_warned = 1;
+    }
+
+    if (!ctx->roi_allowed) {
+        AVFrameSideData *sd =
+            av_frame_get_side_data(frame, AV_FRAME_DATA_REGIONS_OF_INTEREST);
+
+        if (sd && !ctx->roi_warned) {
+            av_log(avctx, AV_LOG_WARNING, "ROI side data on input "
+                   "frames ignored due to lack of driver support.\n");
+            ctx->roi_warned = 1;
+        }
+    }
+
+    return 0;
+}
+
 int ff_vaapi_encode_send_frame(AVCodecContext *avctx, const AVFrame *frame)
 {
     VAAPIEncodeContext *ctx = avctx->priv_data;
@@ -923,6 +1053,10 @@ int ff_vaapi_encode_send_frame(AVCodecContext *avctx, const AVFrame *frame)
         av_log(avctx, AV_LOG_DEBUG, "Input frame: %ux%u (%"PRId64").\n",
                frame->width, frame->height, frame->pts);
 
+        err = vaapi_encode_check_frame(avctx, frame);
+        if (err < 0)
+            return err;
+
         pic = vaapi_encode_alloc(avctx);
         if (!pic)
             return AVERROR(ENOMEM);
@@ -936,7 +1070,7 @@ int ff_vaapi_encode_send_frame(AVCodecContext *avctx, const AVFrame *frame)
         if (err < 0)
             goto fail;
 
-        if (ctx->input_order == 0)
+        if (ctx->input_order == 0 || frame->pict_type == AV_PICTURE_TYPE_I)
             pic->force_idr = 1;
 
         pic->input_surface = (VASurfaceID)(uintptr_t)frame->data[3];
@@ -972,6 +1106,7 @@ int ff_vaapi_encode_send_frame(AVCodecContext *avctx, const AVFrame *frame)
     return 0;
 
 fail:
+    vaapi_encode_free(avctx, pic);
     return err;
 }
 
@@ -1028,20 +1163,15 @@ int ff_vaapi_encode_receive_packet(AVCodecContext *avctx, AVPacket *pkt)
     return 0;
 }
 
-int ff_vaapi_encode2(AVCodecContext *avctx, AVPacket *pkt,
-                     const AVFrame *input_image, int *got_packet)
-{
-    return AVERROR(ENOSYS);
-}
 
-static av_cold void vaapi_encode_add_global_param(AVCodecContext *avctx,
-                                                  VAEncMiscParameterBuffer *buffer,
-                                                  size_t size)
+static av_cold void vaapi_encode_add_global_param(AVCodecContext *avctx, int type,
+                                                  void *buffer, size_t size)
 {
     VAAPIEncodeContext *ctx = avctx->priv_data;
 
     av_assert0(ctx->nb_global_params < MAX_GLOBAL_PARAMS);
 
+    ctx->global_params_type[ctx->nb_global_params] = type;
     ctx->global_params     [ctx->nb_global_params] = buffer;
     ctx->global_params_size[ctx->nb_global_params] = size;
 
@@ -1575,8 +1705,7 @@ rc_mode_found:
                    rc_bits_per_second, rc_window_size);
         }
 
-        ctx->rc_params.misc.type = VAEncMiscParameterTypeRateControl;
-        ctx->rc_params.rc = (VAEncMiscParameterRateControl) {
+        ctx->rc_params = (VAEncMiscParameterRateControl) {
             .bits_per_second    = rc_bits_per_second,
             .target_percentage  = rc_target_percentage,
             .window_size        = rc_window_size,
@@ -1591,7 +1720,9 @@ rc_mode_found:
             .quality_factor     = rc_quality,
 #endif
         };
-        vaapi_encode_add_global_param(avctx, &ctx->rc_params.misc,
+        vaapi_encode_add_global_param(avctx,
+                                      VAEncMiscParameterTypeRateControl,
+                                      &ctx->rc_params,
                                       sizeof(ctx->rc_params));
     }
 
@@ -1600,12 +1731,13 @@ rc_mode_found:
                "initial fullness %"PRId64" bits.\n",
                hrd_buffer_size, hrd_initial_buffer_fullness);
 
-        ctx->hrd_params.misc.type = VAEncMiscParameterTypeHRD;
-        ctx->hrd_params.hrd = (VAEncMiscParameterHRD) {
+        ctx->hrd_params = (VAEncMiscParameterHRD) {
             .initial_buffer_fullness = hrd_initial_buffer_fullness,
             .buffer_size             = hrd_buffer_size,
         };
-        vaapi_encode_add_global_param(avctx, &ctx->hrd_params.misc,
+        vaapi_encode_add_global_param(avctx,
+                                      VAEncMiscParameterTypeHRD,
+                                      &ctx->hrd_params,
                                       sizeof(ctx->hrd_params));
     }
 
@@ -1619,11 +1751,13 @@ rc_mode_found:
     av_log(avctx, AV_LOG_VERBOSE, "RC framerate: %d/%d (%.2f fps).\n",
            fr_num, fr_den, (double)fr_num / fr_den);
 
-    ctx->fr_params.misc.type = VAEncMiscParameterTypeFrameRate;
-    ctx->fr_params.fr.framerate = (unsigned int)fr_den << 16 | fr_num;
-
+    ctx->fr_params = (VAEncMiscParameterFrameRate) {
+        .framerate = (unsigned int)fr_den << 16 | fr_num,
+    };
 #if VA_CHECK_VERSION(0, 40, 0)
-    vaapi_encode_add_global_param(avctx, &ctx->fr_params.misc,
+    vaapi_encode_add_global_param(avctx,
+                                  VAEncMiscParameterTypeFrameRate,
+                                  &ctx->fr_params,
                                   sizeof(ctx->fr_params));
 #endif
 
@@ -1885,10 +2019,12 @@ static av_cold int vaapi_encode_init_quality(AVCodecContext *avctx)
             quality = attr.value;
         }
 
-        ctx->quality_params.misc.type = VAEncMiscParameterTypeQualityLevel;
-        ctx->quality_params.quality.quality_level = quality;
-
-        vaapi_encode_add_global_param(avctx, &ctx->quality_params.misc,
+        ctx->quality_params = (VAEncMiscParameterBufferQualityLevel) {
+            .quality_level = quality,
+        };
+        vaapi_encode_add_global_param(avctx,
+                                      VAEncMiscParameterTypeQualityLevel,
+                                      &ctx->quality_params,
                                       sizeof(ctx->quality_params));
     }
 #else
@@ -1899,6 +2035,39 @@ static av_cold int vaapi_encode_init_quality(AVCodecContext *avctx)
     return 0;
 }
 
+static av_cold int vaapi_encode_init_roi(AVCodecContext *avctx)
+{
+#if VA_CHECK_VERSION(1, 0, 0)
+    VAAPIEncodeContext *ctx = avctx->priv_data;
+    VAStatus vas;
+    VAConfigAttrib attr = { VAConfigAttribEncROI };
+
+    vas = vaGetConfigAttributes(ctx->hwctx->display,
+                                ctx->va_profile,
+                                ctx->va_entrypoint,
+                                &attr, 1);
+    if (vas != VA_STATUS_SUCCESS) {
+        av_log(avctx, AV_LOG_ERROR, "Failed to query ROI "
+               "config attribute: %d (%s).\n", vas, vaErrorStr(vas));
+        return AVERROR_EXTERNAL;
+    }
+
+    if (attr.value == VA_ATTRIB_NOT_SUPPORTED) {
+        ctx->roi_allowed = 0;
+    } else {
+        VAConfigAttribValEncROI roi = {
+            .value = attr.value,
+        };
+
+        ctx->roi_max_regions = roi.bits.num_roi_regions;
+        ctx->roi_allowed = ctx->roi_max_regions > 0 &&
+            (ctx->va_rc_mode == VA_RC_CQP ||
+             roi.bits.roi_rc_qp_delta_support);
+    }
+#endif
+    return 0;
+}
+
 static void vaapi_encode_free_output_buffer(void *opaque,
                                             uint8_t *data)
 {
@@ -2089,6 +2258,10 @@ av_cold int ff_vaapi_encode_init(AVCodecContext *avctx)
     if (err < 0)
         goto fail;
 
+    err = vaapi_encode_init_roi(avctx);
+    if (err < 0)
+        goto fail;
+
     if (avctx->compression_level >= 0) {
         err = vaapi_encode_init_quality(avctx);
         if (err < 0)
@@ -2193,7 +2366,6 @@ av_cold int ff_vaapi_encode_init(AVCodecContext *avctx)
     return 0;
 
 fail:
-    ff_vaapi_encode_close(avctx);
     return err;
 }