/* * 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, ¶ms, ¶m_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_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_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->input_available && !pic->encode_issued); for (i = 0; i < pic->nb_refs; i++) { av_assert0(pic->refs[i]); // If we are serialised then the references must have already // completed. If not, they must have been issued but need not // have completed yet. if (ctx->issue_mode == ISSUE_MODE_SERIALISE_EVERYTHING) av_assert0(pic->refs[i]->encode_complete); else 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_param_buffer(avctx, pic, VAEncMiscParameterBufferType, (char*)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; } } vas = vaBeginPicture(ctx->hwctx->display, ctx->va_context, pic->input_surface); if (vas != VA_STATUS_SUCCESS) { av_log(avctx, AV_LOG_ERROR, "Failed to begin picture encode issue: " "%d (%s).\n", vas, vaErrorStr(vas)); err = AVERROR(EIO); goto fail_with_picture; } vas = vaRenderPicture(ctx->hwctx->display, ctx->va_context, pic->param_buffers, pic->nb_param_buffers); if (vas != VA_STATUS_SUCCESS) { av_log(avctx, AV_LOG_ERROR, "Failed to upload encode parameters: " "%d (%s).\n", vas, vaErrorStr(vas)); err = AVERROR(EIO); goto fail_with_picture; } vas = vaEndPicture(ctx->hwctx->display, ctx->va_context); if (vas != VA_STATUS_SUCCESS) { av_log(avctx, AV_LOG_ERROR, "Failed to end picture encode issue: " "%d (%s).\n", vas, vaErrorStr(vas)); err = AVERROR(EIO); // vaRenderPicture() has been called here, so we should not destroy // the parameter buffers unless separate destruction is required. if (CONFIG_VAAPI_1 || ctx->hwctx->driver_quirks & AV_VAAPI_DRIVER_QUIRK_RENDER_PARAM_BUFFERS) goto fail; else goto fail_at_end; } if (CONFIG_VAAPI_1 || ctx->hwctx->driver_quirks & AV_VAAPI_DRIVER_QUIRK_RENDER_PARAM_BUFFERS) { for (i = 0; i < pic->nb_param_buffers; i++) { vas = vaDestroyBuffer(ctx->hwctx->display, pic->param_buffers[i]); if (vas != VA_STATUS_SUCCESS) { av_log(avctx, AV_LOG_ERROR, "Failed to destroy " "param buffer %#x: %d (%s).\n", pic->param_buffers[i], vas, vaErrorStr(vas)); // And ignore. } } } pic->encode_issued = 1; if (ctx->issue_mode == ISSUE_MODE_SERIALISE_EVERYTHING) return vaapi_encode_wait(avctx, pic); else return 0; fail_with_picture: vaEndPicture(ctx->hwctx->display, ctx->va_context);fail: for(i = 0; i < pic->nb_param_buffers; i++) vaDestroyBuffer(ctx->hwctx->display, pic->param_buffers[i]); for (i = 0; i < pic->nb_slices; i++) { if (pic->slices) { av_freep(&pic->slices[i].priv_data); av_freep(&pic->slices[i].codec_slice_params); } }fail_at_end: av_freep(&pic->codec_picture_params); av_freep(&pic->param_buffers); av_freep(&pic->slices); av_frame_free(&pic->recon_image); av_buffer_unref(&pic->output_buffer_ref); pic->output_buffer = VA_INVALID_ID; return err;} static int vaapi_encode_output(AVCodecContext *avctx, VAAPIEncodePicture *pic, AVPacket *pkt){ VAAPIEncodeContext *ctx = avctx->priv_data; VACodedBufferSegment *buf_list, *buf; VAStatus vas;Lines 1–500 of 2033. Load more
/* * 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, ¶ms, ¶m_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_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_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->input_available && !pic->encode_issued); for (i = 0; i < pic->nb_refs; i++) { av_assert0(pic->refs[i]); // If we are serialised then the references must have already // completed. If not, they must have been issued but need not // have completed yet. if (ctx->issue_mode == ISSUE_MODE_SERIALISE_EVERYTHING) av_assert0(pic->refs[i]->encode_complete); else 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_param_buffer(avctx, pic, VAEncMiscParameterBufferType, (char*)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; } } vas = vaBeginPicture(ctx->hwctx->display, ctx->va_context, pic->input_surface); if (vas != VA_STATUS_SUCCESS) { av_log(avctx, AV_LOG_ERROR, "Failed to begin picture encode issue: " "%d (%s).\n", vas, vaErrorStr(vas)); err = AVERROR(EIO); goto fail_with_picture; } vas = vaRenderPicture(ctx->hwctx->display, ctx->va_context, pic->param_buffers, pic->nb_param_buffers); if (vas != VA_STATUS_SUCCESS) { av_log(avctx, AV_LOG_ERROR, "Failed to upload encode parameters: " "%d (%s).\n", vas, vaErrorStr(vas)); err = AVERROR(EIO); goto fail_with_picture; } vas = vaEndPicture(ctx->hwctx->display, ctx->va_context); if (vas != VA_STATUS_SUCCESS) { av_log(avctx, AV_LOG_ERROR, "Failed to end picture encode issue: " "%d (%s).\n", vas, vaErrorStr(vas)); err = AVERROR(EIO); // vaRenderPicture() has been called here, so we should not destroy // the parameter buffers unless separate destruction is required. if (CONFIG_VAAPI_1 || ctx->hwctx->driver_quirks & AV_VAAPI_DRIVER_QUIRK_RENDER_PARAM_BUFFERS) goto fail; else goto fail_at_end; } if (CONFIG_VAAPI_1 || ctx->hwctx->driver_quirks & AV_VAAPI_DRIVER_QUIRK_RENDER_PARAM_BUFFERS) { for (i = 0; i < pic->nb_param_buffers; i++) { vas = vaDestroyBuffer(ctx->hwctx->display, pic->param_buffers[i]); if (vas != VA_STATUS_SUCCESS) { av_log(avctx, AV_LOG_ERROR, "Failed to destroy " "param buffer %#x: %d (%s).\n", pic->param_buffers[i], vas, vaErrorStr(vas)); // And ignore. } } } pic->encode_issued = 1; if (ctx->issue_mode == ISSUE_MODE_SERIALISE_EVERYTHING) return vaapi_encode_wait(avctx, pic); else return 0; fail_with_picture: vaEndPicture(ctx->hwctx->display, ctx->va_context); fail: for(i = 0; i < pic->nb_param_buffers; i++) vaDestroyBuffer(ctx->hwctx->display, pic->param_buffers[i]); for (i = 0; i < pic->nb_slices; i++) { if (pic->slices) { av_freep(&pic->slices[i].priv_data); av_freep(&pic->slices[i].codec_slice_params); } } fail_at_end: av_freep(&pic->codec_picture_params); av_freep(&pic->param_buffers); av_freep(&pic->slices); av_frame_free(&pic->recon_image); av_buffer_unref(&pic->output_buffer_ref); pic->output_buffer = VA_INVALID_ID; return err; } static int vaapi_encode_output(AVCodecContext *avctx, VAAPIEncodePicture *pic, AVPacket *pkt) { VAAPIEncodeContext *ctx = avctx->priv_data; VACodedBufferSegment *buf_list, *buf; VAStatus vas;