2398 lines · 81.8 KB · C
RawDownload
/* * This file is part of FFmpeg. * * 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. * * 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 FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include <inttypes.h>#include <string.h> #include "libavutil/avassert.h"#include "libavutil/common.h"#include "libavutil/log.h"#include "libavutil/pixdesc.h" #include "vaapi_encode.h"#include "avcodec.h" static const char * const picture_type_name[] = { "IDR", "I", "P", "B" }; static int vaapi_encode_make_packed_header(AVCodecContext *avctx,                                           VAAPIEncodePicture *pic,                                           int type, char *data, size_t bit_len){    VAAPIEncodeContext *ctx = avctx->priv_data;    VAStatus vas;    VABufferID param_buffer, data_buffer;    VABufferID *tmp;    VAEncPackedHeaderParameterBuffer params = {        .type = type,        .bit_length = bit_len,        .has_emulation_bytes = 1,    };     tmp = av_realloc_array(pic->param_buffers, sizeof(*tmp), pic->nb_param_buffers + 2);    if (!tmp)        return AVERROR(ENOMEM);    pic->param_buffers = tmp;     vas = vaCreateBuffer(ctx->hwctx->display, ctx->va_context,                         VAEncPackedHeaderParameterBufferType,                         sizeof(params), 1, &params, &param_buffer);    if (vas != VA_STATUS_SUCCESS) {        av_log(avctx, AV_LOG_ERROR, "Failed to create parameter buffer "               "for packed header (type %d): %d (%s).\n",               type, vas, vaErrorStr(vas));        return AVERROR(EIO);    }    pic->param_buffers[pic->nb_param_buffers++] = param_buffer;     vas = vaCreateBuffer(ctx->hwctx->display, ctx->va_context,                         VAEncPackedHeaderDataBufferType,                         (bit_len + 7) / 8, 1, data, &data_buffer);    if (vas != VA_STATUS_SUCCESS) {        av_log(avctx, AV_LOG_ERROR, "Failed to create data buffer "               "for packed header (type %d): %d (%s).\n",               type, vas, vaErrorStr(vas));        return AVERROR(EIO);    }    pic->param_buffers[pic->nb_param_buffers++] = data_buffer;     av_log(avctx, AV_LOG_DEBUG, "Packed header buffer (%d) is %#x/%#x "           "(%zu bits).\n", type, param_buffer, data_buffer, bit_len);    return 0;} static int vaapi_encode_make_param_buffer(AVCodecContext *avctx,                                          VAAPIEncodePicture *pic,                                          int type, char *data, size_t len){    VAAPIEncodeContext *ctx = avctx->priv_data;    VAStatus vas;    VABufferID *tmp;    VABufferID buffer;     tmp = av_realloc_array(pic->param_buffers, sizeof(*tmp), pic->nb_param_buffers + 1);    if (!tmp)        return AVERROR(ENOMEM);    pic->param_buffers = tmp;     vas = vaCreateBuffer(ctx->hwctx->display, ctx->va_context,                         type, len, 1, data, &buffer);    if (vas != VA_STATUS_SUCCESS) {        av_log(avctx, AV_LOG_ERROR, "Failed to create parameter buffer "               "(type %d): %d (%s).\n", type, vas, vaErrorStr(vas));        return AVERROR(EIO);    }    pic->param_buffers[pic->nb_param_buffers++] = buffer;     av_log(avctx, AV_LOG_DEBUG, "Param buffer (%d) is %#x.\n",           type, buffer);    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){    VAAPIEncodeContext *ctx = avctx->priv_data;    VAStatus vas;     av_assert0(pic->encode_issued);     if (pic->encode_complete) {        // Already waited for this picture.        return 0;    }     av_log(avctx, AV_LOG_DEBUG, "Sync to pic %"PRId64"/%"PRId64" "           "(input surface %#x).\n", pic->display_order,           pic->encode_order, pic->input_surface);     vas = vaSyncSurface(ctx->hwctx->display, pic->input_surface);    if (vas != VA_STATUS_SUCCESS) {        av_log(avctx, AV_LOG_ERROR, "Failed to sync to picture completion: "               "%d (%s).\n", vas, vaErrorStr(vas));        return AVERROR(EIO);    }     // Input is definitely finished with now.    av_frame_free(&pic->input_image);     pic->encode_complete = 1;    return 0;} static int vaapi_encode_issue(AVCodecContext *avctx,                              VAAPIEncodePicture *pic){    VAAPIEncodeContext *ctx = avctx->priv_data;    VAAPIEncodeSlice *slice;    VAStatus vas;    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,           picture_type_name[pic->type]);    if (pic->nb_refs == 0) {        av_log(avctx, AV_LOG_DEBUG, "No reference pictures.\n");    } else {        av_log(avctx, AV_LOG_DEBUG, "Refers to:");        for (i = 0; i < pic->nb_refs; i++) {            av_log(avctx, AV_LOG_DEBUG, " %"PRId64"/%"PRId64,                   pic->refs[i]->display_order, pic->refs[i]->encode_order);        }        av_log(avctx, AV_LOG_DEBUG, ".\n");    }     av_assert0(!pic->encode_issued);    for (i = 0; i < pic->nb_refs; i++) {        av_assert0(pic->refs[i]);        av_assert0(pic->refs[i]->encode_issued);    }     av_log(avctx, AV_LOG_DEBUG, "Input surface is %#x.\n", pic->input_surface);     pic->recon_image = av_frame_alloc();    if (!pic->recon_image) {        err = AVERROR(ENOMEM);        goto fail;    }     err = av_hwframe_get_buffer(ctx->recon_frames_ref, pic->recon_image, 0);    if (err < 0) {        err = AVERROR(ENOMEM);        goto fail;    }    pic->recon_surface = (VASurfaceID)(uintptr_t)pic->recon_image->data[3];    av_log(avctx, AV_LOG_DEBUG, "Recon surface is %#x.\n", pic->recon_surface);     pic->output_buffer_ref = av_buffer_pool_get(ctx->output_buffer_pool);    if (!pic->output_buffer_ref) {        err = AVERROR(ENOMEM);        goto fail;    }    pic->output_buffer = (VABufferID)(uintptr_t)pic->output_buffer_ref->data;    av_log(avctx, AV_LOG_DEBUG, "Output buffer is %#x.\n",           pic->output_buffer);     if (ctx->codec->picture_params_size > 0) {        pic->codec_picture_params = av_malloc(ctx->codec->picture_params_size);        if (!pic->codec_picture_params)            goto fail;        memcpy(pic->codec_picture_params, ctx->codec_picture_params,               ctx->codec->picture_params_size);    } else {        av_assert0(!ctx->codec_picture_params);    }     pic->nb_param_buffers = 0;     if (pic->type == PICTURE_TYPE_IDR && ctx->codec->init_sequence_params) {        err = vaapi_encode_make_param_buffer(avctx, pic,                                             VAEncSequenceParameterBufferType,                                             ctx->codec_sequence_params,                                             ctx->codec->sequence_params_size);        if (err < 0)            goto fail;    }     if (pic->type == PICTURE_TYPE_IDR) {        for (i = 0; i < ctx->nb_global_params; 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;        }    }     if (ctx->codec->init_picture_params) {        err = ctx->codec->init_picture_params(avctx, pic);        if (err < 0) {            av_log(avctx, AV_LOG_ERROR, "Failed to initialise picture "                   "parameters: %d.\n", err);            goto fail;        }        err = vaapi_encode_make_param_buffer(avctx, pic,                                             VAEncPictureParameterBufferType,                                             pic->codec_picture_params,                                             ctx->codec->picture_params_size);        if (err < 0)            goto fail;    }     if (pic->type == PICTURE_TYPE_IDR) {        if (ctx->va_packed_headers & VA_ENC_PACKED_HEADER_SEQUENCE &&            ctx->codec->write_sequence_header) {            bit_len = 8 * sizeof(data);            err = ctx->codec->write_sequence_header(avctx, data, &bit_len);            if (err < 0) {                av_log(avctx, AV_LOG_ERROR, "Failed to write per-sequence "                       "header: %d.\n", err);                goto fail;            }            err = vaapi_encode_make_packed_header(avctx, pic,                                                  ctx->codec->sequence_header_type,                                                  data, bit_len);            if (err < 0)                goto fail;        }    }     if (ctx->va_packed_headers & VA_ENC_PACKED_HEADER_PICTURE &&        ctx->codec->write_picture_header) {        bit_len = 8 * sizeof(data);        err = ctx->codec->write_picture_header(avctx, pic, data, &bit_len);        if (err < 0) {            av_log(avctx, AV_LOG_ERROR, "Failed to write per-picture "                   "header: %d.\n", err);            goto fail;        }        err = vaapi_encode_make_packed_header(avctx, pic,                                              ctx->codec->picture_header_type,                                              data, bit_len);        if (err < 0)            goto fail;    }     if (ctx->codec->write_extra_buffer) {        for (i = 0;; i++) {            size_t len = sizeof(data);            int type;            err = ctx->codec->write_extra_buffer(avctx, pic, i, &type,                                                 data, &len);            if (err == AVERROR_EOF)                break;            if (err < 0) {                av_log(avctx, AV_LOG_ERROR, "Failed to write extra "                       "buffer %d: %d.\n", i, err);                goto fail;            }             err = vaapi_encode_make_param_buffer(avctx, pic, type,                                                 data, len);            if (err < 0)                goto fail;        }    }     if (ctx->va_packed_headers & VA_ENC_PACKED_HEADER_MISC &&        ctx->codec->write_extra_header) {        for (i = 0;; i++) {            int type;            bit_len = 8 * sizeof(data);            err = ctx->codec->write_extra_header(avctx, pic, i, &type,                                                 data, &bit_len);            if (err == AVERROR_EOF)                break;            if (err < 0) {                av_log(avctx, AV_LOG_ERROR, "Failed to write extra "                       "header %d: %d.\n", i, err);                goto fail;            }             err = vaapi_encode_make_packed_header(avctx, pic, type,                                                  data, bit_len);            if (err < 0)                goto fail;        }    }     if (pic->nb_slices == 0)        pic->nb_slices = ctx->nb_slices;    if (pic->nb_slices > 0) {        int rounding;         pic->slices = av_mallocz_array(pic->nb_slices, sizeof(*pic->slices));        if (!pic->slices) {            err = AVERROR(ENOMEM);            goto fail;        }         for (i = 0; i < pic->nb_slices; i++)            pic->slices[i].row_size = ctx->slice_size;         rounding = ctx->slice_block_rows - ctx->nb_slices * ctx->slice_size;        if (rounding > 0) {            // Place rounding error at top and bottom of frame.            av_assert0(rounding < pic->nb_slices);            // Some Intel drivers contain a bug where the encoder will fail            // if the last slice is smaller than the one before it.  Since            // that's straightforward to avoid here, just do so.            if (rounding <= 2) {                for (i = 0; i < rounding; i++)                    ++pic->slices[i].row_size;            } else {                for (i = 0; i < (rounding + 1) / 2; i++)                    ++pic->slices[pic->nb_slices - i - 1].row_size;                for (i = 0; i < rounding / 2; i++)                    ++pic->slices[i].row_size;            }        } else if (rounding < 0) {            // Remove rounding error from last slice only.            av_assert0(rounding < ctx->slice_size);            pic->slices[pic->nb_slices - 1].row_size += rounding;        }    }    for (i = 0; i < pic->nb_slices; i++) {        slice = &pic->slices[i];        slice->index = i;        if (i == 0) {            slice->row_start   = 0;            slice->block_start = 0;        } else {            const VAAPIEncodeSlice *prev = &pic->slices[i - 1];            slice->row_start   = prev->row_start   + prev->row_size;            slice->block_start = prev->block_start + prev->block_size;        }        slice->block_size  = slice->row_size * ctx->slice_block_cols;         av_log(avctx, AV_LOG_DEBUG, "Slice %d: %d-%d (%d rows), "               "%d-%d (%d blocks).\n", i, slice->row_start,               slice->row_start + slice->row_size - 1, slice->row_size,               slice->block_start, slice->block_start + slice->block_size - 1,               slice->block_size);         if (ctx->codec->slice_params_size > 0) {            slice->codec_slice_params = av_mallocz(ctx->codec->slice_params_size);            if (!slice->codec_slice_params) {                err = AVERROR(ENOMEM);                goto fail;            }        }         if (ctx->codec->init_slice_params) {            err = ctx->codec->init_slice_params(avctx, pic, slice);            if (err < 0) {                av_log(avctx, AV_LOG_ERROR, "Failed to initialise slice "                       "parameters: %d.\n", err);                goto fail;            }        }         if (ctx->va_packed_headers & VA_ENC_PACKED_HEADER_SLICE &&            ctx->codec->write_slice_header) {            bit_len = 8 * sizeof(data);            err = ctx->codec->write_slice_header(avctx, pic, slice,                                                 data, &bit_len);            if (err < 0) {                av_log(avctx, AV_LOG_ERROR, "Failed to write per-slice "                       "header: %d.\n", err);                goto fail;            }            err = vaapi_encode_make_packed_header(avctx, pic,                                                  ctx->codec->slice_header_type,                                                  data, bit_len);            if (err < 0)                goto fail;        }         if (ctx->codec->init_slice_params) {            err = vaapi_encode_make_param_buffer(avctx, pic,                                                 VAEncSliceParameterBufferType,                                                 slice->codec_slice_params,                                                 ctx->codec->slice_params_size);            if (err < 0)                goto fail;        }    } #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;

Lines 1–500 of 2398.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t