From patchwork Sun Jan 8 18:11:05 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mark Thompson X-Patchwork-Id: 2109 Delivered-To: ffmpegpatchwork@gmail.com Received: by 10.103.89.21 with SMTP id n21csp6753350vsb; Sun, 8 Jan 2017 10:11:15 -0800 (PST) X-Received: by 10.223.173.183 with SMTP id w52mr9247516wrc.164.1483899075740; Sun, 08 Jan 2017 10:11:15 -0800 (PST) Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id 65si6128231wro.182.2017.01.08.10.11.15; Sun, 08 Jan 2017 10:11:15 -0800 (PST) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@jkqxz-net.20150623.gappssmtp.com; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 83FBD68A52E; Sun, 8 Jan 2017 20:11:06 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-wm0-f46.google.com (mail-wm0-f46.google.com [74.125.82.46]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 9357668A3BA for ; Sun, 8 Jan 2017 20:11:00 +0200 (EET) Received: by mail-wm0-f46.google.com with SMTP id a197so78000630wmd.0 for ; Sun, 08 Jan 2017 10:11:07 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=jkqxz-net.20150623.gappssmtp.com; s=20150623; h=subject:to:references:from:message-id:date:user-agent:mime-version :in-reply-to:content-transfer-encoding; bh=XRn7hXCJ5m2Ifu+VBkLO/BUtLv1+pUY1iRPRTDmGJVo=; b=z2AP0hwlue1Ivvj8AA5bNNgIrQ2uxjtpt9M7pyHxQMvgR3OvlrxabdOoS5e0qAlaim WH6V/c5Ae35LX74XZd0oJTj1e0kahh/SOVn/MLzXLNSYr7+MwrAtQ6Fe4Uo0pfaLfE+Y OkdJ7hICD7Rc6gze6p/Wl/+4vewGngXomZ339nP/wBWi7SJOzoJcX3daDxuj9EVWEnwI E0epQYxK715OWXlslYjsD90J7U3BknghK6Y4VfPkAiSgj8DDnsER+OHisfs9SGvbIflX 0RB2U4svqe2g2vLya+iFyaGBolKxe7XuEiZgWrVOgYvFvwQIQ5UDqnxpnWNWK7BlpQYD Pa0A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:subject:to:references:from:message-id:date :user-agent:mime-version:in-reply-to:content-transfer-encoding; bh=XRn7hXCJ5m2Ifu+VBkLO/BUtLv1+pUY1iRPRTDmGJVo=; b=Gsr5SVUIWc6lrF1kqG7QdcYBivxpsvxk7X4aEZ78UL/8XYG9JGMax6J62foc1SseqA LRnh3Ir3SUlwYUyyd4AijULOhd1Er+OSM6UZKMDOrbl84p/o+bZGMqIchulN4Aplkkl2 hh/Bm47GB1dpJyPqEKH+LR7eFnWc6Mmn8BmJK0mQpYRHfbwvy24UnAC+PpZ6HKPdojne 96CZ91EjybWMLGw819y3QTSd8Ajg5jpQCV883UEwTAtRe1aiCHF3Y8ejdcNNL3O8JtAy J5+tmUVrrGX2NjsbptALr85moBqXCFHg+Byn9oEwv2+IdlbyGv5tp9FMqgLIECdYT19i 7kkw== X-Gm-Message-State: AIkVDXJc4Hutq08XcfDxXA74ZkNiVPM6ZLeI0iNg5Yr34gndjaogk7t0eE/lKjW7ijbnoA== X-Received: by 10.223.162.132 with SMTP id s4mr9105176wra.44.1483899066513; Sun, 08 Jan 2017 10:11:06 -0800 (PST) Received: from [192.168.0.8] (cpc91242-cmbg18-2-0-cust650.5-4.cable.virginm.net. [82.8.130.139]) by smtp.gmail.com with ESMTPSA id o132sm14537759wmo.17.2017.01.08.10.11.05 for (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Sun, 08 Jan 2017 10:11:05 -0800 (PST) To: FFmpeg development discussions and patches References: <320e2652-f3d9-5509-11ac-fbd84e22db6a@jkqxz.net> From: Mark Thompson Message-ID: <9c5b71da-c884-b83b-8124-591b2a3fe862@jkqxz.net> Date: Sun, 8 Jan 2017 18:11:05 +0000 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101 Icedove/45.4.0 MIME-Version: 1.0 In-Reply-To: <320e2652-f3d9-5509-11ac-fbd84e22db6a@jkqxz.net> Subject: [FFmpeg-devel] [PATCH 05/16] lavc: Rewrite VAAPI decode infrastructure X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" Moves much of the setup logic for VAAPI decoding into lavc; the user now need only provide the hw_frames_ctx. (cherry picked from commit 123ccd07c55ccf075cc5daf5581237fbccb86bdb) (cherry picked from commit 5e879b54a3a46817ea6c8a95a9aecab1176418b9) (cherry picked from commit 0aec37e625821040c103641eec9c1e7a1efa2952) (cherry picked from commit cfa4eb4fba782f3f37a33be997b27a91a07053c9) --- libavcodec/Makefile | 5 +- libavcodec/vaapi_decode.c | 536 ++++++++++++++++++++++++++++++++++++++++++++++ libavcodec/vaapi_decode.h | 90 ++++++++ 3 files changed, 629 insertions(+), 2 deletions(-) create mode 100644 libavcodec/vaapi_decode.c create mode 100644 libavcodec/vaapi_decode.h diff --git a/libavcodec/Makefile b/libavcodec/Makefile index 44e416e6cd..bd4b2315da 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -769,7 +769,7 @@ OBJS-$(CONFIG_ADPCM_YAMAHA_ENCODER) += adpcmenc.o adpcm_data.o # hardware accelerators OBJS-$(CONFIG_D3D11VA) += dxva2.o OBJS-$(CONFIG_DXVA2) += dxva2.o -OBJS-$(CONFIG_VAAPI) += vaapi.o +OBJS-$(CONFIG_VAAPI) += vaapi.o vaapi_decode.o OBJS-$(CONFIG_VDA) += vda.o videotoolbox.o OBJS-$(CONFIG_VIDEOTOOLBOX) += videotoolbox.o OBJS-$(CONFIG_VDPAU) += vdpau.o @@ -1000,7 +1000,8 @@ SKIPHEADERS-$(CONFIG_QSV) += qsv.h qsv_internal.h SKIPHEADERS-$(CONFIG_QSVDEC) += qsvdec.h SKIPHEADERS-$(CONFIG_QSVENC) += qsvenc.h SKIPHEADERS-$(CONFIG_XVMC) += xvmc.h -SKIPHEADERS-$(CONFIG_VAAPI) += vaapi_encode.h vaapi_internal.h +SKIPHEADERS-$(CONFIG_VAAPI) += vaapi_decode.h vaapi_encode.h \ + vaapi_internal.h SKIPHEADERS-$(CONFIG_VDA) += vda.h vda_vt_internal.h SKIPHEADERS-$(CONFIG_VDPAU) += vdpau.h vdpau_internal.h SKIPHEADERS-$(CONFIG_VIDEOTOOLBOX) += videotoolbox.h vda_vt_internal.h diff --git a/libavcodec/vaapi_decode.c b/libavcodec/vaapi_decode.c new file mode 100644 index 0000000000..878e8ed3d1 --- /dev/null +++ b/libavcodec/vaapi_decode.c @@ -0,0 +1,536 @@ +/* + * 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 "libavutil/avassert.h" +#include "libavutil/common.h" + +#include "avcodec.h" +#include "internal.h" +#include "vaapi_decode.h" + + +int ff_vaapi_decode_make_param_buffer(AVCodecContext *avctx, + VAAPIDecodePicture *pic, + int type, + const void *data, + size_t size) +{ + VAAPIDecodeContext *ctx = avctx->internal->hwaccel_priv_data; + VAStatus vas; + VABufferID buffer; + + av_assert0(pic->nb_param_buffers + 1 <= MAX_PARAM_BUFFERS); + + vas = vaCreateBuffer(ctx->hwctx->display, ctx->va_context, + type, size, 1, (void*)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 (type %d, %zu bytes) " + "is %#x.\n", type, size, buffer); + return 0; +} + + +int ff_vaapi_decode_make_slice_buffer(AVCodecContext *avctx, + VAAPIDecodePicture *pic, + const void *params_data, + size_t params_size, + const void *slice_data, + size_t slice_size) +{ + VAAPIDecodeContext *ctx = avctx->internal->hwaccel_priv_data; + VAStatus vas; + int index; + + av_assert0(pic->nb_slices <= pic->slices_allocated); + if (pic->nb_slices == pic->slices_allocated) { + if (pic->slices_allocated > 0) + pic->slices_allocated *= 2; + else + pic->slices_allocated = 64; + + pic->slice_buffers = + av_realloc_array(pic->slice_buffers, + pic->slices_allocated, + 2 * sizeof(*pic->slice_buffers)); + if (!pic->slice_buffers) + return AVERROR(ENOMEM); + } + av_assert0(pic->nb_slices + 1 <= pic->slices_allocated); + + index = 2 * pic->nb_slices; + + vas = vaCreateBuffer(ctx->hwctx->display, ctx->va_context, + VASliceParameterBufferType, + params_size, 1, (void*)params_data, + &pic->slice_buffers[index]); + if (vas != VA_STATUS_SUCCESS) { + av_log(avctx, AV_LOG_ERROR, "Failed to create slice " + "parameter buffer: %d (%s).\n", vas, vaErrorStr(vas)); + return AVERROR(EIO); + } + + av_log(avctx, AV_LOG_DEBUG, "Slice %d param buffer (%zu bytes) " + "is %#x.\n", pic->nb_slices, params_size, + pic->slice_buffers[index]); + + vas = vaCreateBuffer(ctx->hwctx->display, ctx->va_context, + VASliceDataBufferType, + slice_size, 1, (void*)slice_data, + &pic->slice_buffers[index + 1]); + if (vas != VA_STATUS_SUCCESS) { + av_log(avctx, AV_LOG_ERROR, "Failed to create slice " + "data buffer (size %zu): %d (%s).\n", + slice_size, vas, vaErrorStr(vas)); + vaDestroyBuffer(ctx->hwctx->display, + pic->slice_buffers[index]); + return AVERROR(EIO); + } + + av_log(avctx, AV_LOG_DEBUG, "Slice %d data buffer (%zu bytes) " + "is %#x.\n", pic->nb_slices, slice_size, + pic->slice_buffers[index + 1]); + + ++pic->nb_slices; + return 0; +} + +static void ff_vaapi_decode_destroy_buffers(AVCodecContext *avctx, + VAAPIDecodePicture *pic) +{ + VAAPIDecodeContext *ctx = avctx->internal->hwaccel_priv_data; + VAStatus vas; + int i; + + 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 " + "parameter buffer %#x: %d (%s).\n", + pic->param_buffers[i], vas, vaErrorStr(vas)); + } + } + + for (i = 0; i < 2 * pic->nb_slices; i++) { + vas = vaDestroyBuffer(ctx->hwctx->display, + pic->slice_buffers[i]); + if (vas != VA_STATUS_SUCCESS) { + av_log(avctx, AV_LOG_ERROR, "Failed to destroy slice " + "slice buffer %#x: %d (%s).\n", + pic->slice_buffers[i], vas, vaErrorStr(vas)); + } + } +} + +int ff_vaapi_decode_issue(AVCodecContext *avctx, + VAAPIDecodePicture *pic) +{ + VAAPIDecodeContext *ctx = avctx->internal->hwaccel_priv_data; + VAStatus vas; + int err; + + av_log(avctx, AV_LOG_DEBUG, "Decode to surface %#x.\n", + pic->output_surface); + + vas = vaBeginPicture(ctx->hwctx->display, ctx->va_context, + pic->output_surface); + if (vas != VA_STATUS_SUCCESS) { + av_log(avctx, AV_LOG_ERROR, "Failed to begin picture decode " + "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 decode " + "parameters: %d (%s).\n", vas, vaErrorStr(vas)); + err = AVERROR(EIO); + goto fail_with_picture; + } + + vas = vaRenderPicture(ctx->hwctx->display, ctx->va_context, + pic->slice_buffers, 2 * pic->nb_slices); + if (vas != VA_STATUS_SUCCESS) { + av_log(avctx, AV_LOG_ERROR, "Failed to upload slices: " + "%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 decode " + "issue: %d (%s).\n", vas, vaErrorStr(vas)); + err = AVERROR(EIO); + if (ctx->hwctx->driver_quirks & + AV_VAAPI_DRIVER_QUIRK_RENDER_PARAM_BUFFERS) + goto fail; + else + goto fail_at_end; + } + + if (ctx->hwctx->driver_quirks & + AV_VAAPI_DRIVER_QUIRK_RENDER_PARAM_BUFFERS) + ff_vaapi_decode_destroy_buffers(avctx, pic); + + pic->nb_param_buffers = 0; + pic->nb_slices = 0; + pic->slices_allocated = 0; + av_freep(&pic->slice_buffers); + + return 0; + +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 decode " + "after error: %d (%s).\n", vas, vaErrorStr(vas)); + } +fail: + ff_vaapi_decode_destroy_buffers(avctx, pic); +fail_at_end: + return err; +} + +int ff_vaapi_decode_cancel(AVCodecContext *avctx, + VAAPIDecodePicture *pic) +{ + ff_vaapi_decode_destroy_buffers(avctx, pic); + + pic->nb_param_buffers = 0; + pic->nb_slices = 0; + pic->slices_allocated = 0; + av_freep(&pic->slice_buffers); + + return 0; +} + +static const struct { + enum AVCodecID codec_id; + int codec_profile; + VAProfile va_profile; +} vaapi_profile_map[] = { +#define MAP(c, p, v) { AV_CODEC_ID_ ## c, FF_PROFILE_ ## p, VAProfile ## v } + MAP(MPEG2VIDEO, MPEG2_SIMPLE, MPEG2Simple ), + MAP(MPEG2VIDEO, MPEG2_MAIN, MPEG2Main ), + MAP(H263, UNKNOWN, H263Baseline), + MAP(MPEG4, MPEG4_SIMPLE, MPEG4Simple ), + MAP(MPEG4, MPEG4_ADVANCED_SIMPLE, + MPEG4AdvancedSimple), + MAP(MPEG4, MPEG4_MAIN, MPEG4Main ), + MAP(H264, H264_CONSTRAINED_BASELINE, + H264ConstrainedBaseline), + MAP(H264, H264_BASELINE, H264Baseline), + MAP(H264, H264_MAIN, H264Main ), + MAP(H264, H264_HIGH, H264High ), +#if VA_CHECK_VERSION(0, 37, 0) + MAP(HEVC, HEVC_MAIN, HEVCMain ), + MAP(HEVC, HEVC_MAIN_10, HEVCMain10 ), +#endif + MAP(WMV3, VC1_SIMPLE, VC1Simple ), + MAP(WMV3, VC1_MAIN, VC1Main ), + MAP(WMV3, VC1_COMPLEX, VC1Advanced ), + MAP(WMV3, VC1_ADVANCED, VC1Advanced ), + MAP(VC1, VC1_SIMPLE, VC1Simple ), + MAP(VC1, VC1_MAIN, VC1Main ), + MAP(VC1, VC1_COMPLEX, VC1Advanced ), + MAP(VC1, VC1_ADVANCED, VC1Advanced ), +#if VA_CHECK_VERSION(0, 35, 0) + MAP(VP8, UNKNOWN, VP8Version0_3 ), +#endif +#if VA_CHECK_VERSION(0, 38, 0) + MAP(VP9, VP9_0, VP9Profile0 ), +#endif +#if VA_CHECK_VERSION(0, 39, 0) + MAP(VP9, VP9_2, VP9Profile2 ), +#endif +#undef MAP +}; + +static int vaapi_decode_make_config(AVCodecContext *avctx) +{ + VAAPIDecodeContext *ctx = avctx->internal->hwaccel_priv_data; + + AVVAAPIHWConfig *hwconfig = NULL; + AVHWFramesConstraints *constraints = NULL; + VAStatus vas; + int err, i, j; + const AVCodecDescriptor *codec_desc; + VAProfile profile, *profile_list = NULL; + int profile_count, exact_match, alt_profile; + + // Allowing a profile mismatch can be useful because streams may + // over-declare their required capabilities - in particular, many + // H.264 baseline profile streams (notably some of those in FATE) + // only use the feature set of constrained baseline. This flag + // would have to be be set by some external means in order to + // actually be useful. (AV_HWACCEL_FLAG_IGNORE_PROFILE?) + int allow_profile_mismatch = 0; + + codec_desc = avcodec_descriptor_get(avctx->codec_id); + if (!codec_desc) { + err = AVERROR(EINVAL); + goto fail; + } + + profile_count = vaMaxNumProfiles(ctx->hwctx->display); + profile_list = av_malloc_array(profile_count, + sizeof(VAProfile)); + if (!profile_list) { + err = AVERROR(ENOMEM); + goto fail; + } + + vas = vaQueryConfigProfiles(ctx->hwctx->display, + profile_list, &profile_count); + if (vas != VA_STATUS_SUCCESS) { + av_log(avctx, AV_LOG_ERROR, "Failed to query profiles: " + "%d (%s).\n", vas, vaErrorStr(vas)); + err = AVERROR(ENOSYS); + goto fail; + } + + profile = VAProfileNone; + exact_match = 0; + + for (i = 0; i < FF_ARRAY_ELEMS(vaapi_profile_map); i++) { + int profile_match = 0; + if (avctx->codec_id != vaapi_profile_map[i].codec_id) + continue; + if (avctx->profile == vaapi_profile_map[i].codec_profile) + profile_match = 1; + profile = vaapi_profile_map[i].va_profile; + for (j = 0; j < profile_count; j++) { + if (profile == profile_list[j]) { + exact_match = profile_match; + break; + } + } + if (j < profile_count) { + if (exact_match) + break; + alt_profile = vaapi_profile_map[i].codec_profile; + } + } + av_freep(&profile_list); + + if (profile == VAProfileNone) { + av_log(avctx, AV_LOG_ERROR, "No support for codec %s " + "profile %d.\n", codec_desc->name, avctx->profile); + err = AVERROR(ENOSYS); + goto fail; + } + if (!exact_match) { + if (allow_profile_mismatch) { + av_log(avctx, AV_LOG_VERBOSE, "Codec %s profile %d not " + "supported for hardware decode.\n", + codec_desc->name, avctx->profile); + av_log(avctx, AV_LOG_WARNING, "Using possibly-" + "incompatible profile %d instead.\n", + alt_profile); + } else { + av_log(avctx, AV_LOG_VERBOSE, "Codec %s profile %d not " + "supported for hardware decode.\n", + codec_desc->name, avctx->profile); + err = AVERROR(EINVAL); + goto fail; + } + } + + ctx->va_profile = profile; + ctx->va_entrypoint = VAEntrypointVLD; + + vas = vaCreateConfig(ctx->hwctx->display, ctx->va_profile, + ctx->va_entrypoint, NULL, 0, + &ctx->va_config); + if (vas != VA_STATUS_SUCCESS) { + av_log(avctx, AV_LOG_ERROR, "Failed to create decode " + "configuration: %d (%s).\n", vas, vaErrorStr(vas)); + err = AVERROR(EIO); + goto fail; + } + + hwconfig = av_hwdevice_hwconfig_alloc(ctx->frames->device_ref); + if (!hwconfig) { + err = AVERROR(ENOMEM); + goto fail; + } + hwconfig->config_id = ctx->va_config; + + constraints = + av_hwdevice_get_hwframe_constraints(ctx->frames->device_ref, + hwconfig); + if (!constraints) { + // Ignore. + } else { + if (avctx->coded_width < constraints->min_width || + avctx->coded_height < constraints->min_height || + avctx->coded_width > constraints->max_width || + avctx->coded_height > constraints->max_height) { + av_log(avctx, AV_LOG_ERROR, "Hardware does not support image " + "size %dx%d (constraints: width %d-%d height %d-%d).\n", + avctx->coded_width, avctx->coded_height, + constraints->min_width, constraints->max_width, + constraints->min_height, constraints->max_height); + err = AVERROR(EINVAL); + goto fail; + } + } + + av_hwframe_constraints_free(&constraints); + av_freep(&hwconfig); + + return 0; + +fail: + av_hwframe_constraints_free(&constraints); + av_freep(&hwconfig); + if (ctx->va_config != VA_INVALID_ID) { + vaDestroyConfig(ctx->hwctx->display, ctx->va_config); + ctx->va_config = VA_INVALID_ID; + } + av_freep(&profile_list); + return err; +} + +int ff_vaapi_decode_init(AVCodecContext *avctx) +{ + VAAPIDecodeContext *ctx = avctx->internal->hwaccel_priv_data; + VAStatus vas; + int err; + + ctx->va_config = VA_INVALID_ID; + ctx->va_context = VA_INVALID_ID; + + if (avctx->hwaccel_context) { + av_log(avctx, AV_LOG_WARNING, "Using deprecated struct " + "vaapi_context in decode.\n"); + + ctx->have_old_context = 1; + ctx->old_context = avctx->hwaccel_context; + + // Really we only want the VAAPI device context, but this + // allocates a whole generic device context because we don't + // have any other way to determine how big it should be. + ctx->device_ref = + av_hwdevice_ctx_alloc(AV_HWDEVICE_TYPE_VAAPI); + if (!ctx->device_ref) { + err = AVERROR(ENOMEM); + goto fail; + } + ctx->device = (AVHWDeviceContext*)ctx->device_ref->data; + ctx->hwctx = ctx->device->hwctx; + + ctx->hwctx->display = ctx->old_context->display; + + // The old VAAPI decode setup assumed this quirk was always + // present, so set it here to avoid the behaviour changing. + ctx->hwctx->driver_quirks = + AV_VAAPI_DRIVER_QUIRK_RENDER_PARAM_BUFFERS; + + } else if (avctx->hw_frames_ctx) { + // This structure has a shorter lifetime than the enclosing + // AVCodecContext, so we inherit the references from there + // and do not need to make separate ones. + + ctx->frames = (AVHWFramesContext*)avctx->hw_frames_ctx->data; + ctx->hwfc = ctx->frames->hwctx; + + ctx->device = ctx->frames->device_ctx; + ctx->hwctx = ctx->device->hwctx; + + } else { + av_log(avctx, AV_LOG_ERROR, "A hardware frames context is " + "required for VAAPI decoding.\n"); + err = AVERROR(EINVAL); + goto fail; + } + + if (ctx->have_old_context) { + ctx->va_config = ctx->old_context->config_id; + ctx->va_context = ctx->old_context->context_id; + + av_log(avctx, AV_LOG_DEBUG, "Using user-supplied decoder " + "context: %#x/%#x.\n", ctx->va_config, ctx->va_context); + } else { + err = vaapi_decode_make_config(avctx); + if (err) + goto fail; + + vas = vaCreateContext(ctx->hwctx->display, ctx->va_config, + avctx->coded_width, avctx->coded_height, + VA_PROGRESSIVE, + ctx->hwfc->surface_ids, + ctx->hwfc->nb_surfaces, + &ctx->va_context); + if (vas != VA_STATUS_SUCCESS) { + av_log(avctx, AV_LOG_ERROR, "Failed to create decode " + "context: %d (%s).\n", vas, vaErrorStr(vas)); + err = AVERROR(EIO); + goto fail; + } + + av_log(avctx, AV_LOG_DEBUG, "Decode context initialised: " + "%#x/%#x.\n", ctx->va_config, ctx->va_context); + } + + return 0; + +fail: + ff_vaapi_decode_uninit(avctx); + return err; +} + +int ff_vaapi_decode_uninit(AVCodecContext *avctx) +{ + VAAPIDecodeContext *ctx = avctx->internal->hwaccel_priv_data; + VAStatus vas; + + if (ctx->have_old_context) { + av_buffer_unref(&ctx->device_ref); + } else { + if (ctx->va_context != VA_INVALID_ID) { + vas = vaDestroyContext(ctx->hwctx->display, ctx->va_context); + if (vas != VA_STATUS_SUCCESS) { + av_log(avctx, AV_LOG_ERROR, "Failed to destroy decode " + "context %#x: %d (%s).\n", + ctx->va_context, vas, vaErrorStr(vas)); + } + } + if (ctx->va_config != VA_INVALID_ID) { + vas = vaDestroyConfig(ctx->hwctx->display, ctx->va_config); + if (vas != VA_STATUS_SUCCESS) { + av_log(avctx, AV_LOG_ERROR, "Failed to destroy decode " + "configuration %#x: %d (%s).\n", + ctx->va_config, vas, vaErrorStr(vas)); + } + } + } + + return 0; +} diff --git a/libavcodec/vaapi_decode.h b/libavcodec/vaapi_decode.h new file mode 100644 index 0000000000..f1caa54eca --- /dev/null +++ b/libavcodec/vaapi_decode.h @@ -0,0 +1,90 @@ +/* + * 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 + */ + +#ifndef AVCODEC_VAAPI_DECODE_H +#define AVCODEC_VAAPI_DECODE_H + +#include + +#include "libavutil/frame.h" +#include "libavutil/hwcontext.h" +#include "libavutil/hwcontext_vaapi.h" + +#include "avcodec.h" +#include "vaapi.h" + +static inline VASurfaceID ff_vaapi_get_surface_id(AVFrame *pic) +{ + return (uintptr_t)pic->data[3]; +} + +enum { + MAX_PARAM_BUFFERS = 16, +}; + +typedef struct VAAPIDecodePicture { + VASurfaceID output_surface; + + int nb_param_buffers; + VABufferID param_buffers[MAX_PARAM_BUFFERS]; + + int nb_slices; + VABufferID *slice_buffers; + int slices_allocated; +} VAAPIDecodePicture; + +typedef struct VAAPIDecodeContext { + VAProfile va_profile; + VAEntrypoint va_entrypoint; + VAConfigID va_config; + VAContextID va_context; + + int have_old_context; + struct vaapi_context *old_context; + AVBufferRef *device_ref; + + AVHWDeviceContext *device; + AVVAAPIDeviceContext *hwctx; + + AVHWFramesContext *frames; + AVVAAPIFramesContext *hwfc; +} VAAPIDecodeContext; + + +int ff_vaapi_decode_make_param_buffer(AVCodecContext *avctx, + VAAPIDecodePicture *pic, + int type, + const void *data, + size_t size); + +int ff_vaapi_decode_make_slice_buffer(AVCodecContext *avctx, + VAAPIDecodePicture *pic, + const void *params_data, + size_t params_size, + const void *slice_data, + size_t slice_size); + +int ff_vaapi_decode_issue(AVCodecContext *avctx, + VAAPIDecodePicture *pic); +int ff_vaapi_decode_cancel(AVCodecContext *avctx, + VAAPIDecodePicture *pic); + +int ff_vaapi_decode_init(AVCodecContext *avctx); +int ff_vaapi_decode_uninit(AVCodecContext *avctx); + +#endif /* AVCODEC_VAAPI_DECODE_H */