123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689 |
- /*
- * 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 "config.h"
- #if HAVE_VAAPI_X11
- # include <va/va_x11.h>
- #endif
- #if HAVE_VAAPI_DRM
- # include <va/va_drm.h>
- #endif
- #if CONFIG_LIBDRM
- # include <va/va_drmcommon.h>
- # include <xf86drm.h>
- # include <drm_fourcc.h>
- # ifndef DRM_FORMAT_MOD_INVALID
- # define DRM_FORMAT_MOD_INVALID ((1ULL << 56) - 1)
- # endif
- #endif
- #include <fcntl.h>
- #if HAVE_UNISTD_H
- # include <unistd.h>
- #endif
- #include "avassert.h"
- #include "buffer.h"
- #include "common.h"
- #include "hwcontext.h"
- #include "hwcontext_drm.h"
- #include "hwcontext_internal.h"
- #include "hwcontext_vaapi.h"
- #include "mem.h"
- #include "pixdesc.h"
- #include "pixfmt.h"
- typedef struct VAAPIDevicePriv {
- #if HAVE_VAAPI_X11
- Display *x11_display;
- #endif
- int drm_fd;
- } VAAPIDevicePriv;
- typedef struct VAAPISurfaceFormat {
- enum AVPixelFormat pix_fmt;
- VAImageFormat image_format;
- } VAAPISurfaceFormat;
- typedef struct VAAPIDeviceContext {
- // Surface formats which can be used with this device.
- VAAPISurfaceFormat *formats;
- int nb_formats;
- } VAAPIDeviceContext;
- typedef struct VAAPIFramesContext {
- // Surface attributes set at create time.
- VASurfaceAttrib *attributes;
- int nb_attributes;
- // RT format of the underlying surface (Intel driver ignores this anyway).
- unsigned int rt_format;
- // Whether vaDeriveImage works.
- int derive_works;
- } VAAPIFramesContext;
- typedef struct VAAPIMapping {
- // Handle to the derived or copied image which is mapped.
- VAImage image;
- // The mapping flags actually used.
- int flags;
- } VAAPIMapping;
- typedef struct VAAPIFormat {
- unsigned int fourcc;
- unsigned int rt_format;
- enum AVPixelFormat pix_fmt;
- int chroma_planes_swapped;
- } VAAPIFormatDescriptor;
- #define MAP(va, rt, av, swap_uv) { \
- VA_FOURCC_ ## va, \
- VA_RT_FORMAT_ ## rt, \
- AV_PIX_FMT_ ## av, \
- swap_uv, \
- }
- // The map fourcc <-> pix_fmt isn't bijective because of the annoying U/V
- // plane swap cases. The frame handling below tries to hide these.
- static const VAAPIFormatDescriptor vaapi_format_map[] = {
- MAP(NV12, YUV420, NV12, 0),
- #ifdef VA_FOURCC_I420
- MAP(I420, YUV420, YUV420P, 0),
- #endif
- MAP(YV12, YUV420, YUV420P, 1),
- MAP(IYUV, YUV420, YUV420P, 0),
- MAP(422H, YUV422, YUV422P, 0),
- #ifdef VA_FOURCC_YV16
- MAP(YV16, YUV422, YUV422P, 1),
- #endif
- MAP(UYVY, YUV422, UYVY422, 0),
- MAP(YUY2, YUV422, YUYV422, 0),
- MAP(411P, YUV411, YUV411P, 0),
- MAP(422V, YUV422, YUV440P, 0),
- MAP(444P, YUV444, YUV444P, 0),
- MAP(Y800, YUV400, GRAY8, 0),
- #ifdef VA_FOURCC_P010
- MAP(P010, YUV420_10BPP, P010, 0),
- #endif
- MAP(BGRA, RGB32, BGRA, 0),
- MAP(BGRX, RGB32, BGR0, 0),
- MAP(RGBA, RGB32, RGBA, 0),
- MAP(RGBX, RGB32, RGB0, 0),
- #ifdef VA_FOURCC_ABGR
- MAP(ABGR, RGB32, ABGR, 0),
- MAP(XBGR, RGB32, 0BGR, 0),
- #endif
- MAP(ARGB, RGB32, ARGB, 0),
- MAP(XRGB, RGB32, 0RGB, 0),
- };
- #undef MAP
- static const VAAPIFormatDescriptor *
- vaapi_format_from_fourcc(unsigned int fourcc)
- {
- int i;
- for (i = 0; i < FF_ARRAY_ELEMS(vaapi_format_map); i++)
- if (vaapi_format_map[i].fourcc == fourcc)
- return &vaapi_format_map[i];
- return NULL;
- }
- static const VAAPIFormatDescriptor *
- vaapi_format_from_pix_fmt(enum AVPixelFormat pix_fmt)
- {
- int i;
- for (i = 0; i < FF_ARRAY_ELEMS(vaapi_format_map); i++)
- if (vaapi_format_map[i].pix_fmt == pix_fmt)
- return &vaapi_format_map[i];
- return NULL;
- }
- static enum AVPixelFormat vaapi_pix_fmt_from_fourcc(unsigned int fourcc)
- {
- const VAAPIFormatDescriptor *desc;
- desc = vaapi_format_from_fourcc(fourcc);
- if (desc)
- return desc->pix_fmt;
- else
- return AV_PIX_FMT_NONE;
- }
- static int vaapi_get_image_format(AVHWDeviceContext *hwdev,
- enum AVPixelFormat pix_fmt,
- VAImageFormat **image_format)
- {
- VAAPIDeviceContext *ctx = hwdev->internal->priv;
- int i;
- for (i = 0; i < ctx->nb_formats; i++) {
- if (ctx->formats[i].pix_fmt == pix_fmt) {
- if (image_format)
- *image_format = &ctx->formats[i].image_format;
- return 0;
- }
- }
- return AVERROR(EINVAL);
- }
- static int vaapi_frames_get_constraints(AVHWDeviceContext *hwdev,
- const void *hwconfig,
- AVHWFramesConstraints *constraints)
- {
- AVVAAPIDeviceContext *hwctx = hwdev->hwctx;
- const AVVAAPIHWConfig *config = hwconfig;
- VAAPIDeviceContext *ctx = hwdev->internal->priv;
- VASurfaceAttrib *attr_list = NULL;
- VAStatus vas;
- enum AVPixelFormat pix_fmt;
- unsigned int fourcc;
- int err, i, j, attr_count, pix_fmt_count;
- if (config &&
- !(hwctx->driver_quirks & AV_VAAPI_DRIVER_QUIRK_SURFACE_ATTRIBUTES)) {
- attr_count = 0;
- vas = vaQuerySurfaceAttributes(hwctx->display, config->config_id,
- 0, &attr_count);
- if (vas != VA_STATUS_SUCCESS) {
- av_log(hwdev, AV_LOG_ERROR, "Failed to query surface attributes: "
- "%d (%s).\n", vas, vaErrorStr(vas));
- err = AVERROR(ENOSYS);
- goto fail;
- }
- attr_list = av_malloc(attr_count * sizeof(*attr_list));
- if (!attr_list) {
- err = AVERROR(ENOMEM);
- goto fail;
- }
- vas = vaQuerySurfaceAttributes(hwctx->display, config->config_id,
- attr_list, &attr_count);
- if (vas != VA_STATUS_SUCCESS) {
- av_log(hwdev, AV_LOG_ERROR, "Failed to query surface attributes: "
- "%d (%s).\n", vas, vaErrorStr(vas));
- err = AVERROR(ENOSYS);
- goto fail;
- }
- pix_fmt_count = 0;
- for (i = 0; i < attr_count; i++) {
- switch (attr_list[i].type) {
- case VASurfaceAttribPixelFormat:
- fourcc = attr_list[i].value.value.i;
- pix_fmt = vaapi_pix_fmt_from_fourcc(fourcc);
- if (pix_fmt != AV_PIX_FMT_NONE) {
- ++pix_fmt_count;
- } else {
- // Something unsupported - ignore.
- }
- break;
- case VASurfaceAttribMinWidth:
- constraints->min_width = attr_list[i].value.value.i;
- break;
- case VASurfaceAttribMinHeight:
- constraints->min_height = attr_list[i].value.value.i;
- break;
- case VASurfaceAttribMaxWidth:
- constraints->max_width = attr_list[i].value.value.i;
- break;
- case VASurfaceAttribMaxHeight:
- constraints->max_height = attr_list[i].value.value.i;
- break;
- }
- }
- if (pix_fmt_count == 0) {
- // Nothing usable found. Presumably there exists something which
- // works, so leave the set null to indicate unknown.
- constraints->valid_sw_formats = NULL;
- } else {
- constraints->valid_sw_formats = av_malloc_array(pix_fmt_count + 1,
- sizeof(pix_fmt));
- if (!constraints->valid_sw_formats) {
- err = AVERROR(ENOMEM);
- goto fail;
- }
- for (i = j = 0; i < attr_count; i++) {
- if (attr_list[i].type != VASurfaceAttribPixelFormat)
- continue;
- fourcc = attr_list[i].value.value.i;
- pix_fmt = vaapi_pix_fmt_from_fourcc(fourcc);
- if (pix_fmt != AV_PIX_FMT_NONE)
- constraints->valid_sw_formats[j++] = pix_fmt;
- }
- av_assert0(j == pix_fmt_count);
- constraints->valid_sw_formats[j] = AV_PIX_FMT_NONE;
- }
- } else {
- // No configuration supplied.
- // Return the full set of image formats known by the implementation.
- constraints->valid_sw_formats = av_malloc_array(ctx->nb_formats + 1,
- sizeof(pix_fmt));
- if (!constraints->valid_sw_formats) {
- err = AVERROR(ENOMEM);
- goto fail;
- }
- for (i = 0; i < ctx->nb_formats; i++)
- constraints->valid_sw_formats[i] = ctx->formats[i].pix_fmt;
- constraints->valid_sw_formats[i] = AV_PIX_FMT_NONE;
- }
- constraints->valid_hw_formats = av_malloc_array(2, sizeof(pix_fmt));
- if (!constraints->valid_hw_formats) {
- err = AVERROR(ENOMEM);
- goto fail;
- }
- constraints->valid_hw_formats[0] = AV_PIX_FMT_VAAPI;
- constraints->valid_hw_formats[1] = AV_PIX_FMT_NONE;
- err = 0;
- fail:
- av_freep(&attr_list);
- return err;
- }
- static const struct {
- const char *friendly_name;
- const char *match_string;
- unsigned int quirks;
- } vaapi_driver_quirks_table[] = {
- #if !VA_CHECK_VERSION(1, 0, 0)
- // The i965 driver did not conform before version 2.0.
- {
- "Intel i965 (Quick Sync)",
- "i965",
- AV_VAAPI_DRIVER_QUIRK_RENDER_PARAM_BUFFERS,
- },
- #endif
- {
- "Intel iHD",
- "ubit",
- AV_VAAPI_DRIVER_QUIRK_ATTRIB_MEMTYPE,
- },
- {
- "VDPAU wrapper",
- "Splitted-Desktop Systems VDPAU backend for VA-API",
- AV_VAAPI_DRIVER_QUIRK_SURFACE_ATTRIBUTES,
- },
- };
- static int vaapi_device_init(AVHWDeviceContext *hwdev)
- {
- VAAPIDeviceContext *ctx = hwdev->internal->priv;
- AVVAAPIDeviceContext *hwctx = hwdev->hwctx;
- VAImageFormat *image_list = NULL;
- VAStatus vas;
- const char *vendor_string;
- int err, i, image_count;
- enum AVPixelFormat pix_fmt;
- unsigned int fourcc;
- image_count = vaMaxNumImageFormats(hwctx->display);
- if (image_count <= 0) {
- err = AVERROR(EIO);
- goto fail;
- }
- image_list = av_malloc(image_count * sizeof(*image_list));
- if (!image_list) {
- err = AVERROR(ENOMEM);
- goto fail;
- }
- vas = vaQueryImageFormats(hwctx->display, image_list, &image_count);
- if (vas != VA_STATUS_SUCCESS) {
- err = AVERROR(EIO);
- goto fail;
- }
- ctx->formats = av_malloc(image_count * sizeof(*ctx->formats));
- if (!ctx->formats) {
- err = AVERROR(ENOMEM);
- goto fail;
- }
- ctx->nb_formats = 0;
- for (i = 0; i < image_count; i++) {
- fourcc = image_list[i].fourcc;
- pix_fmt = vaapi_pix_fmt_from_fourcc(fourcc);
- if (pix_fmt == AV_PIX_FMT_NONE) {
- av_log(hwdev, AV_LOG_DEBUG, "Format %#x -> unknown.\n",
- fourcc);
- } else {
- av_log(hwdev, AV_LOG_DEBUG, "Format %#x -> %s.\n",
- fourcc, av_get_pix_fmt_name(pix_fmt));
- ctx->formats[ctx->nb_formats].pix_fmt = pix_fmt;
- ctx->formats[ctx->nb_formats].image_format = image_list[i];
- ++ctx->nb_formats;
- }
- }
- vendor_string = vaQueryVendorString(hwctx->display);
- if (vendor_string)
- av_log(hwdev, AV_LOG_VERBOSE, "VAAPI driver: %s.\n", vendor_string);
- if (hwctx->driver_quirks & AV_VAAPI_DRIVER_QUIRK_USER_SET) {
- av_log(hwdev, AV_LOG_VERBOSE, "Using quirks set by user (%#x).\n",
- hwctx->driver_quirks);
- } else {
- // Detect the driver in use and set quirk flags if necessary.
- hwctx->driver_quirks = 0;
- if (vendor_string) {
- for (i = 0; i < FF_ARRAY_ELEMS(vaapi_driver_quirks_table); i++) {
- if (strstr(vendor_string,
- vaapi_driver_quirks_table[i].match_string)) {
- av_log(hwdev, AV_LOG_VERBOSE, "Matched driver string "
- "as known nonstandard driver \"%s\", setting "
- "quirks (%#x).\n",
- vaapi_driver_quirks_table[i].friendly_name,
- vaapi_driver_quirks_table[i].quirks);
- hwctx->driver_quirks |=
- vaapi_driver_quirks_table[i].quirks;
- break;
- }
- }
- if (!(i < FF_ARRAY_ELEMS(vaapi_driver_quirks_table))) {
- av_log(hwdev, AV_LOG_VERBOSE, "Driver not found in known "
- "nonstandard list, using standard behaviour.\n");
- }
- } else {
- av_log(hwdev, AV_LOG_VERBOSE, "Driver has no vendor string, "
- "assuming standard behaviour.\n");
- }
- }
- av_free(image_list);
- return 0;
- fail:
- av_freep(&ctx->formats);
- av_free(image_list);
- return err;
- }
- static void vaapi_device_uninit(AVHWDeviceContext *hwdev)
- {
- VAAPIDeviceContext *ctx = hwdev->internal->priv;
- av_freep(&ctx->formats);
- }
- static void vaapi_buffer_free(void *opaque, uint8_t *data)
- {
- AVHWFramesContext *hwfc = opaque;
- AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
- VASurfaceID surface_id;
- VAStatus vas;
- surface_id = (VASurfaceID)(uintptr_t)data;
- vas = vaDestroySurfaces(hwctx->display, &surface_id, 1);
- if (vas != VA_STATUS_SUCCESS) {
- av_log(hwfc, AV_LOG_ERROR, "Failed to destroy surface %#x: "
- "%d (%s).\n", surface_id, vas, vaErrorStr(vas));
- }
- }
- static AVBufferRef *vaapi_pool_alloc(void *opaque, int size)
- {
- AVHWFramesContext *hwfc = opaque;
- VAAPIFramesContext *ctx = hwfc->internal->priv;
- AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
- AVVAAPIFramesContext *avfc = hwfc->hwctx;
- VASurfaceID surface_id;
- VAStatus vas;
- AVBufferRef *ref;
- if (hwfc->initial_pool_size > 0 &&
- avfc->nb_surfaces >= hwfc->initial_pool_size)
- return NULL;
- vas = vaCreateSurfaces(hwctx->display, ctx->rt_format,
- hwfc->width, hwfc->height,
- &surface_id, 1,
- ctx->attributes, ctx->nb_attributes);
- if (vas != VA_STATUS_SUCCESS) {
- av_log(hwfc, AV_LOG_ERROR, "Failed to create surface: "
- "%d (%s).\n", vas, vaErrorStr(vas));
- return NULL;
- }
- av_log(hwfc, AV_LOG_DEBUG, "Created surface %#x.\n", surface_id);
- ref = av_buffer_create((uint8_t*)(uintptr_t)surface_id,
- sizeof(surface_id), &vaapi_buffer_free,
- hwfc, AV_BUFFER_FLAG_READONLY);
- if (!ref) {
- vaDestroySurfaces(hwctx->display, &surface_id, 1);
- return NULL;
- }
- if (hwfc->initial_pool_size > 0) {
- // This is a fixed-size pool, so we must still be in the initial
- // allocation sequence.
- av_assert0(avfc->nb_surfaces < hwfc->initial_pool_size);
- avfc->surface_ids[avfc->nb_surfaces] = surface_id;
- ++avfc->nb_surfaces;
- }
- return ref;
- }
- static int vaapi_frames_init(AVHWFramesContext *hwfc)
- {
- AVVAAPIFramesContext *avfc = hwfc->hwctx;
- VAAPIFramesContext *ctx = hwfc->internal->priv;
- AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
- const VAAPIFormatDescriptor *desc;
- VAImageFormat *expected_format;
- AVBufferRef *test_surface = NULL;
- VASurfaceID test_surface_id;
- VAImage test_image;
- VAStatus vas;
- int err, i;
- desc = vaapi_format_from_pix_fmt(hwfc->sw_format);
- if (!desc) {
- av_log(hwfc, AV_LOG_ERROR, "Unsupported format: %s.\n",
- av_get_pix_fmt_name(hwfc->sw_format));
- return AVERROR(EINVAL);
- }
- if (!hwfc->pool) {
- if (!(hwctx->driver_quirks & AV_VAAPI_DRIVER_QUIRK_SURFACE_ATTRIBUTES)) {
- int need_memory_type = !(hwctx->driver_quirks & AV_VAAPI_DRIVER_QUIRK_ATTRIB_MEMTYPE);
- int need_pixel_format = 1;
- for (i = 0; i < avfc->nb_attributes; i++) {
- if (avfc->attributes[i].type == VASurfaceAttribMemoryType)
- need_memory_type = 0;
- if (avfc->attributes[i].type == VASurfaceAttribPixelFormat)
- need_pixel_format = 0;
- }
- ctx->nb_attributes =
- avfc->nb_attributes + need_memory_type + need_pixel_format;
- ctx->attributes = av_malloc(ctx->nb_attributes *
- sizeof(*ctx->attributes));
- if (!ctx->attributes) {
- err = AVERROR(ENOMEM);
- goto fail;
- }
- for (i = 0; i < avfc->nb_attributes; i++)
- ctx->attributes[i] = avfc->attributes[i];
- if (need_memory_type) {
- ctx->attributes[i++] = (VASurfaceAttrib) {
- .type = VASurfaceAttribMemoryType,
- .flags = VA_SURFACE_ATTRIB_SETTABLE,
- .value.type = VAGenericValueTypeInteger,
- .value.value.i = VA_SURFACE_ATTRIB_MEM_TYPE_VA,
- };
- }
- if (need_pixel_format) {
- ctx->attributes[i++] = (VASurfaceAttrib) {
- .type = VASurfaceAttribPixelFormat,
- .flags = VA_SURFACE_ATTRIB_SETTABLE,
- .value.type = VAGenericValueTypeInteger,
- .value.value.i = desc->fourcc,
- };
- }
- av_assert0(i == ctx->nb_attributes);
- } else {
- ctx->attributes = NULL;
- ctx->nb_attributes = 0;
- }
- ctx->rt_format = desc->rt_format;
- if (hwfc->initial_pool_size > 0) {
- // This pool will be usable as a render target, so we need to store
- // all of the surface IDs somewhere that vaCreateContext() calls
- // will be able to access them.
- avfc->nb_surfaces = 0;
- avfc->surface_ids = av_malloc(hwfc->initial_pool_size *
- sizeof(*avfc->surface_ids));
- if (!avfc->surface_ids) {
- err = AVERROR(ENOMEM);
- goto fail;
- }
- } else {
- // This pool allows dynamic sizing, and will not be usable as a
- // render target.
- avfc->nb_surfaces = 0;
- avfc->surface_ids = NULL;
- }
- hwfc->internal->pool_internal =
- av_buffer_pool_init2(sizeof(VASurfaceID), hwfc,
- &vaapi_pool_alloc, NULL);
- if (!hwfc->internal->pool_internal) {
- av_log(hwfc, AV_LOG_ERROR, "Failed to create VAAPI surface pool.\n");
- err = AVERROR(ENOMEM);
- goto fail;
- }
- }
- // Allocate a single surface to test whether vaDeriveImage() is going
- // to work for the specific configuration.
- if (hwfc->pool) {
- test_surface = av_buffer_pool_get(hwfc->pool);
- if (!test_surface) {
- av_log(hwfc, AV_LOG_ERROR, "Unable to allocate a surface from "
- "user-configured buffer pool.\n");
- err = AVERROR(ENOMEM);
- goto fail;
- }
- } else {
- test_surface = av_buffer_pool_get(hwfc->internal->pool_internal);
- if (!test_surface) {
- av_log(hwfc, AV_LOG_ERROR, "Unable to allocate a surface from "
- "internal buffer pool.\n");
- err = AVERROR(ENOMEM);
- goto fail;
- }
- }
- test_surface_id = (VASurfaceID)(uintptr_t)test_surface->data;
- ctx->derive_works = 0;
- err = vaapi_get_image_format(hwfc->device_ctx,
- hwfc->sw_format, &expected_format);
- if (err == 0) {
- vas = vaDeriveImage(hwctx->display, test_surface_id, &test_image);
- if (vas == VA_STATUS_SUCCESS) {
- if (expected_format->fourcc == test_image.format.fourcc) {
- av_log(hwfc, AV_LOG_DEBUG, "Direct mapping possible.\n");
- ctx->derive_works = 1;
- } else {
- av_log(hwfc, AV_LOG_DEBUG, "Direct mapping disabled: "
- "derived image format %08x does not match "
- "expected format %08x.\n",
- expected_format->fourcc, test_image.format.fourcc);
- }
- vaDestroyImage(hwctx->display, test_image.image_id);
- } else {
- av_log(hwfc, AV_LOG_DEBUG, "Direct mapping disabled: "
- "deriving image does not work: "
- "%d (%s).\n", vas, vaErrorStr(vas));
- }
- } else {
- av_log(hwfc, AV_LOG_DEBUG, "Direct mapping disabled: "
- "image format is not supported.\n");
- }
- av_buffer_unref(&test_surface);
- return 0;
- fail:
- av_buffer_unref(&test_surface);
- av_freep(&avfc->surface_ids);
- av_freep(&ctx->attributes);
- return err;
- }
- static void vaapi_frames_uninit(AVHWFramesContext *hwfc)
- {
- AVVAAPIFramesContext *avfc = hwfc->hwctx;
- VAAPIFramesContext *ctx = hwfc->internal->priv;
- av_freep(&avfc->surface_ids);
- av_freep(&ctx->attributes);
- }
- static int vaapi_get_buffer(AVHWFramesContext *hwfc, AVFrame *frame)
- {
- frame->buf[0] = av_buffer_pool_get(hwfc->pool);
- if (!frame->buf[0])
- return AVERROR(ENOMEM);
- frame->data[3] = frame->buf[0]->data;
- frame->format = AV_PIX_FMT_VAAPI;
- frame->width = hwfc->width;
- frame->height = hwfc->height;
- return 0;
- }
- static int vaapi_transfer_get_formats(AVHWFramesContext *hwfc,
- enum AVHWFrameTransferDirection dir,
- enum AVPixelFormat **formats)
- {
- VAAPIDeviceContext *ctx = hwfc->device_ctx->internal->priv;
- enum AVPixelFormat *pix_fmts;
- int i, k, sw_format_available;
- sw_format_available = 0;
- for (i = 0; i < ctx->nb_formats; i++) {
- if (ctx->formats[i].pix_fmt == hwfc->sw_format)
- sw_format_available = 1;
- }
- pix_fmts = av_malloc((ctx->nb_formats + 1) * sizeof(*pix_fmts));
- if (!pix_fmts)
- return AVERROR(ENOMEM);
- if (sw_format_available) {
- pix_fmts[0] = hwfc->sw_format;
- k = 1;
- } else {
- k = 0;
- }
- for (i = 0; i < ctx->nb_formats; i++) {
- if (ctx->formats[i].pix_fmt == hwfc->sw_format)
- continue;
- av_assert0(k < ctx->nb_formats);
- pix_fmts[k++] = ctx->formats[i].pix_fmt;
- }
- pix_fmts[k] = AV_PIX_FMT_NONE;
- *formats = pix_fmts;
- return 0;
- }
- static void vaapi_unmap_frame(AVHWFramesContext *hwfc,
- HWMapDescriptor *hwmap)
- {
- AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
- VAAPIMapping *map = hwmap->priv;
- VASurfaceID surface_id;
- VAStatus vas;
- surface_id = (VASurfaceID)(uintptr_t)hwmap->source->data[3];
- av_log(hwfc, AV_LOG_DEBUG, "Unmap surface %#x.\n", surface_id);
- vas = vaUnmapBuffer(hwctx->display, map->image.buf);
- if (vas != VA_STATUS_SUCCESS) {
- av_log(hwfc, AV_LOG_ERROR, "Failed to unmap image from surface "
- "%#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas));
- }
- if ((map->flags & AV_HWFRAME_MAP_WRITE) &&
- !(map->flags & AV_HWFRAME_MAP_DIRECT)) {
- vas = vaPutImage(hwctx->display, surface_id, map->image.image_id,
- 0, 0, hwfc->width, hwfc->height,
- 0, 0, hwfc->width, hwfc->height);
- if (vas != VA_STATUS_SUCCESS) {
- av_log(hwfc, AV_LOG_ERROR, "Failed to write image to surface "
- "%#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas));
- }
- }
- vas = vaDestroyImage(hwctx->display, map->image.image_id);
- if (vas != VA_STATUS_SUCCESS) {
- av_log(hwfc, AV_LOG_ERROR, "Failed to destroy image from surface "
- "%#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas));
- }
- av_free(map);
- }
- static int vaapi_map_frame(AVHWFramesContext *hwfc,
- AVFrame *dst, const AVFrame *src, int flags)
- {
- AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
- VAAPIFramesContext *ctx = hwfc->internal->priv;
- VASurfaceID surface_id;
- const VAAPIFormatDescriptor *desc;
- VAImageFormat *image_format;
- VAAPIMapping *map;
- VAStatus vas;
- void *address = NULL;
- int err, i;
- surface_id = (VASurfaceID)(uintptr_t)src->data[3];
- av_log(hwfc, AV_LOG_DEBUG, "Map surface %#x.\n", surface_id);
- if (!ctx->derive_works && (flags & AV_HWFRAME_MAP_DIRECT)) {
- // Requested direct mapping but it is not possible.
- return AVERROR(EINVAL);
- }
- if (dst->format == AV_PIX_FMT_NONE)
- dst->format = hwfc->sw_format;
- if (dst->format != hwfc->sw_format && (flags & AV_HWFRAME_MAP_DIRECT)) {
- // Requested direct mapping but the formats do not match.
- return AVERROR(EINVAL);
- }
- err = vaapi_get_image_format(hwfc->device_ctx, dst->format, &image_format);
- if (err < 0) {
- // Requested format is not a valid output format.
- return AVERROR(EINVAL);
- }
- map = av_malloc(sizeof(*map));
- if (!map)
- return AVERROR(ENOMEM);
- map->flags = flags;
- map->image.image_id = VA_INVALID_ID;
- vas = vaSyncSurface(hwctx->display, surface_id);
- if (vas != VA_STATUS_SUCCESS) {
- av_log(hwfc, AV_LOG_ERROR, "Failed to sync surface "
- "%#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas));
- err = AVERROR(EIO);
- goto fail;
- }
- // The memory which we map using derive need not be connected to the CPU
- // in a way conducive to fast access. On Gen7-Gen9 Intel graphics, the
- // memory is mappable but not cached, so normal memcpy()-like access is
- // very slow to read it (but writing is ok). It is possible to read much
- // faster with a copy routine which is aware of the limitation, but we
- // assume for now that the user is not aware of that and would therefore
- // prefer not to be given direct-mapped memory if they request read access.
- if (ctx->derive_works && dst->format == hwfc->sw_format &&
- ((flags & AV_HWFRAME_MAP_DIRECT) || !(flags & AV_HWFRAME_MAP_READ))) {
- vas = vaDeriveImage(hwctx->display, surface_id, &map->image);
- if (vas != VA_STATUS_SUCCESS) {
- av_log(hwfc, AV_LOG_ERROR, "Failed to derive image from "
- "surface %#x: %d (%s).\n",
- surface_id, vas, vaErrorStr(vas));
- err = AVERROR(EIO);
- goto fail;
- }
- if (map->image.format.fourcc != image_format->fourcc) {
- av_log(hwfc, AV_LOG_ERROR, "Derive image of surface %#x "
- "is in wrong format: expected %#08x, got %#08x.\n",
- surface_id, image_format->fourcc, map->image.format.fourcc);
- err = AVERROR(EIO);
- goto fail;
- }
- map->flags |= AV_HWFRAME_MAP_DIRECT;
- } else {
- vas = vaCreateImage(hwctx->display, image_format,
- hwfc->width, hwfc->height, &map->image);
- if (vas != VA_STATUS_SUCCESS) {
- av_log(hwfc, AV_LOG_ERROR, "Failed to create image for "
- "surface %#x: %d (%s).\n",
- surface_id, vas, vaErrorStr(vas));
- err = AVERROR(EIO);
- goto fail;
- }
- if (!(flags & AV_HWFRAME_MAP_OVERWRITE)) {
- vas = vaGetImage(hwctx->display, surface_id, 0, 0,
- hwfc->width, hwfc->height, map->image.image_id);
- if (vas != VA_STATUS_SUCCESS) {
- av_log(hwfc, AV_LOG_ERROR, "Failed to read image from "
- "surface %#x: %d (%s).\n",
- surface_id, vas, vaErrorStr(vas));
- err = AVERROR(EIO);
- goto fail;
- }
- }
- }
- vas = vaMapBuffer(hwctx->display, map->image.buf, &address);
- if (vas != VA_STATUS_SUCCESS) {
- av_log(hwfc, AV_LOG_ERROR, "Failed to map image from surface "
- "%#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas));
- err = AVERROR(EIO);
- goto fail;
- }
- err = ff_hwframe_map_create(src->hw_frames_ctx,
- dst, src, &vaapi_unmap_frame, map);
- if (err < 0)
- goto fail;
- dst->width = src->width;
- dst->height = src->height;
- for (i = 0; i < map->image.num_planes; i++) {
- dst->data[i] = (uint8_t*)address + map->image.offsets[i];
- dst->linesize[i] = map->image.pitches[i];
- }
- desc = vaapi_format_from_fourcc(map->image.format.fourcc);
- if (desc && desc->chroma_planes_swapped) {
- // Chroma planes are YVU rather than YUV, so swap them.
- FFSWAP(uint8_t*, dst->data[1], dst->data[2]);
- }
- return 0;
- fail:
- if (map) {
- if (address)
- vaUnmapBuffer(hwctx->display, map->image.buf);
- if (map->image.image_id != VA_INVALID_ID)
- vaDestroyImage(hwctx->display, map->image.image_id);
- av_free(map);
- }
- return err;
- }
- static int vaapi_transfer_data_from(AVHWFramesContext *hwfc,
- AVFrame *dst, const AVFrame *src)
- {
- AVFrame *map;
- int err;
- if (dst->width > hwfc->width || dst->height > hwfc->height)
- return AVERROR(EINVAL);
- map = av_frame_alloc();
- if (!map)
- return AVERROR(ENOMEM);
- map->format = dst->format;
- err = vaapi_map_frame(hwfc, map, src, AV_HWFRAME_MAP_READ);
- if (err)
- goto fail;
- map->width = dst->width;
- map->height = dst->height;
- err = av_frame_copy(dst, map);
- if (err)
- goto fail;
- err = 0;
- fail:
- av_frame_free(&map);
- return err;
- }
- static int vaapi_transfer_data_to(AVHWFramesContext *hwfc,
- AVFrame *dst, const AVFrame *src)
- {
- AVFrame *map;
- int err;
- if (src->width > hwfc->width || src->height > hwfc->height)
- return AVERROR(EINVAL);
- map = av_frame_alloc();
- if (!map)
- return AVERROR(ENOMEM);
- map->format = src->format;
- err = vaapi_map_frame(hwfc, map, dst, AV_HWFRAME_MAP_WRITE | AV_HWFRAME_MAP_OVERWRITE);
- if (err)
- goto fail;
- map->width = src->width;
- map->height = src->height;
- err = av_frame_copy(map, src);
- if (err)
- goto fail;
- err = 0;
- fail:
- av_frame_free(&map);
- return err;
- }
- static int vaapi_map_to_memory(AVHWFramesContext *hwfc, AVFrame *dst,
- const AVFrame *src, int flags)
- {
- int err;
- if (dst->format != AV_PIX_FMT_NONE) {
- err = vaapi_get_image_format(hwfc->device_ctx, dst->format, NULL);
- if (err < 0)
- return AVERROR(ENOSYS);
- }
- err = vaapi_map_frame(hwfc, dst, src, flags);
- if (err)
- return err;
- err = av_frame_copy_props(dst, src);
- if (err)
- return err;
- return 0;
- }
- #if CONFIG_LIBDRM
- #define DRM_MAP(va, layers, ...) { \
- VA_FOURCC_ ## va, \
- layers, \
- { __VA_ARGS__ } \
- }
- static const struct {
- uint32_t va_fourcc;
- int nb_layer_formats;
- uint32_t layer_formats[AV_DRM_MAX_PLANES];
- } vaapi_drm_format_map[] = {
- #ifdef DRM_FORMAT_R8
- DRM_MAP(NV12, 2, DRM_FORMAT_R8, DRM_FORMAT_RG88),
- #endif
- DRM_MAP(NV12, 1, DRM_FORMAT_NV12),
- #if defined(VA_FOURCC_P010) && defined(DRM_FORMAT_R16)
- DRM_MAP(P010, 2, DRM_FORMAT_R16, DRM_FORMAT_RG1616),
- #endif
- DRM_MAP(BGRA, 1, DRM_FORMAT_ARGB8888),
- DRM_MAP(BGRX, 1, DRM_FORMAT_XRGB8888),
- DRM_MAP(RGBA, 1, DRM_FORMAT_ABGR8888),
- DRM_MAP(RGBX, 1, DRM_FORMAT_XBGR8888),
- #ifdef VA_FOURCC_ABGR
- DRM_MAP(ABGR, 1, DRM_FORMAT_RGBA8888),
- DRM_MAP(XBGR, 1, DRM_FORMAT_RGBX8888),
- #endif
- DRM_MAP(ARGB, 1, DRM_FORMAT_BGRA8888),
- DRM_MAP(XRGB, 1, DRM_FORMAT_BGRX8888),
- };
- #undef DRM_MAP
- static void vaapi_unmap_from_drm(AVHWFramesContext *dst_fc,
- HWMapDescriptor *hwmap)
- {
- AVVAAPIDeviceContext *dst_dev = dst_fc->device_ctx->hwctx;
- VASurfaceID surface_id = (VASurfaceID)(uintptr_t)hwmap->priv;
- av_log(dst_fc, AV_LOG_DEBUG, "Destroy surface %#x.\n", surface_id);
- vaDestroySurfaces(dst_dev->display, &surface_id, 1);
- }
- static int vaapi_map_from_drm(AVHWFramesContext *src_fc, AVFrame *dst,
- const AVFrame *src, int flags)
- {
- AVHWFramesContext *dst_fc =
- (AVHWFramesContext*)dst->hw_frames_ctx->data;
- AVVAAPIDeviceContext *dst_dev = dst_fc->device_ctx->hwctx;
- const AVDRMFrameDescriptor *desc;
- const VAAPIFormatDescriptor *format_desc;
- VASurfaceID surface_id;
- VAStatus vas;
- uint32_t va_fourcc;
- int err, i, j, k;
- unsigned long buffer_handle;
- VASurfaceAttribExternalBuffers buffer_desc;
- VASurfaceAttrib attrs[2] = {
- {
- .type = VASurfaceAttribMemoryType,
- .flags = VA_SURFACE_ATTRIB_SETTABLE,
- .value.type = VAGenericValueTypeInteger,
- .value.value.i = VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME,
- },
- {
- .type = VASurfaceAttribExternalBufferDescriptor,
- .flags = VA_SURFACE_ATTRIB_SETTABLE,
- .value.type = VAGenericValueTypePointer,
- .value.value.p = &buffer_desc,
- }
- };
- desc = (AVDRMFrameDescriptor*)src->data[0];
- if (desc->nb_objects != 1) {
- av_log(dst_fc, AV_LOG_ERROR, "VAAPI can only map frames "
- "made from a single DRM object.\n");
- return AVERROR(EINVAL);
- }
- va_fourcc = 0;
- for (i = 0; i < FF_ARRAY_ELEMS(vaapi_drm_format_map); i++) {
- if (desc->nb_layers != vaapi_drm_format_map[i].nb_layer_formats)
- continue;
- for (j = 0; j < desc->nb_layers; j++) {
- if (desc->layers[j].format !=
- vaapi_drm_format_map[i].layer_formats[j])
- break;
- }
- if (j != desc->nb_layers)
- continue;
- va_fourcc = vaapi_drm_format_map[i].va_fourcc;
- break;
- }
- if (!va_fourcc) {
- av_log(dst_fc, AV_LOG_ERROR, "DRM format not supported "
- "by VAAPI.\n");
- return AVERROR(EINVAL);
- }
- av_log(dst_fc, AV_LOG_DEBUG, "Map DRM object %d to VAAPI as "
- "%08x.\n", desc->objects[0].fd, va_fourcc);
- format_desc = vaapi_format_from_fourcc(va_fourcc);
- av_assert0(format_desc);
- buffer_handle = desc->objects[0].fd;
- buffer_desc.pixel_format = va_fourcc;
- buffer_desc.width = src_fc->width;
- buffer_desc.height = src_fc->height;
- buffer_desc.data_size = desc->objects[0].size;
- buffer_desc.buffers = &buffer_handle;
- buffer_desc.num_buffers = 1;
- buffer_desc.flags = 0;
- k = 0;
- for (i = 0; i < desc->nb_layers; i++) {
- for (j = 0; j < desc->layers[i].nb_planes; j++) {
- buffer_desc.pitches[k] = desc->layers[i].planes[j].pitch;
- buffer_desc.offsets[k] = desc->layers[i].planes[j].offset;
- ++k;
- }
- }
- buffer_desc.num_planes = k;
- if (format_desc->chroma_planes_swapped &&
- buffer_desc.num_planes == 3) {
- FFSWAP(uint32_t, buffer_desc.pitches[1], buffer_desc.pitches[2]);
- FFSWAP(uint32_t, buffer_desc.offsets[1], buffer_desc.offsets[2]);
- }
- vas = vaCreateSurfaces(dst_dev->display, format_desc->rt_format,
- src->width, src->height,
- &surface_id, 1,
- attrs, FF_ARRAY_ELEMS(attrs));
- if (vas != VA_STATUS_SUCCESS) {
- av_log(dst_fc, AV_LOG_ERROR, "Failed to create surface from DRM "
- "object: %d (%s).\n", vas, vaErrorStr(vas));
- return AVERROR(EIO);
- }
- av_log(dst_fc, AV_LOG_DEBUG, "Create surface %#x.\n", surface_id);
- err = ff_hwframe_map_create(dst->hw_frames_ctx, dst, src,
- &vaapi_unmap_from_drm,
- (void*)(uintptr_t)surface_id);
- if (err < 0)
- return err;
- dst->width = src->width;
- dst->height = src->height;
- dst->data[3] = (uint8_t*)(uintptr_t)surface_id;
- av_log(dst_fc, AV_LOG_DEBUG, "Mapped DRM object %d to "
- "surface %#x.\n", desc->objects[0].fd, surface_id);
- return 0;
- }
- #if VA_CHECK_VERSION(1, 1, 0)
- static void vaapi_unmap_to_drm_esh(AVHWFramesContext *hwfc,
- HWMapDescriptor *hwmap)
- {
- AVDRMFrameDescriptor *drm_desc = hwmap->priv;
- int i;
- for (i = 0; i < drm_desc->nb_objects; i++)
- close(drm_desc->objects[i].fd);
- av_freep(&drm_desc);
- }
- static int vaapi_map_to_drm_esh(AVHWFramesContext *hwfc, AVFrame *dst,
- const AVFrame *src, int flags)
- {
- AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
- VASurfaceID surface_id;
- VAStatus vas;
- VADRMPRIMESurfaceDescriptor va_desc;
- AVDRMFrameDescriptor *drm_desc = NULL;
- uint32_t export_flags;
- int err, i, j;
- surface_id = (VASurfaceID)(uintptr_t)src->data[3];
- export_flags = VA_EXPORT_SURFACE_SEPARATE_LAYERS;
- if (flags & AV_HWFRAME_MAP_READ)
- export_flags |= VA_EXPORT_SURFACE_READ_ONLY;
- if (flags & AV_HWFRAME_MAP_WRITE)
- export_flags |= VA_EXPORT_SURFACE_WRITE_ONLY;
- vas = vaExportSurfaceHandle(hwctx->display, surface_id,
- VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2,
- export_flags, &va_desc);
- if (vas != VA_STATUS_SUCCESS) {
- if (vas == VA_STATUS_ERROR_UNIMPLEMENTED)
- return AVERROR(ENOSYS);
- av_log(hwfc, AV_LOG_ERROR, "Failed to export surface %#x: "
- "%d (%s).\n", surface_id, vas, vaErrorStr(vas));
- return AVERROR(EIO);
- }
- drm_desc = av_mallocz(sizeof(*drm_desc));
- if (!drm_desc) {
- err = AVERROR(ENOMEM);
- goto fail;
- }
- // By some bizarre coincidence, these structures are very similar...
- drm_desc->nb_objects = va_desc.num_objects;
- for (i = 0; i < va_desc.num_objects; i++) {
- drm_desc->objects[i].fd = va_desc.objects[i].fd;
- drm_desc->objects[i].size = va_desc.objects[i].size;
- drm_desc->objects[i].format_modifier =
- va_desc.objects[i].drm_format_modifier;
- }
- drm_desc->nb_layers = va_desc.num_layers;
- for (i = 0; i < va_desc.num_layers; i++) {
- drm_desc->layers[i].format = va_desc.layers[i].drm_format;
- drm_desc->layers[i].nb_planes = va_desc.layers[i].num_planes;
- for (j = 0; j < va_desc.layers[i].num_planes; j++) {
- drm_desc->layers[i].planes[j].object_index =
- va_desc.layers[i].object_index[j];
- drm_desc->layers[i].planes[j].offset =
- va_desc.layers[i].offset[j];
- drm_desc->layers[i].planes[j].pitch =
- va_desc.layers[i].pitch[j];
- }
- }
- err = ff_hwframe_map_create(src->hw_frames_ctx, dst, src,
- &vaapi_unmap_to_drm_esh, drm_desc);
- if (err < 0)
- goto fail;
- dst->width = src->width;
- dst->height = src->height;
- dst->data[0] = (uint8_t*)drm_desc;
- return 0;
- fail:
- for (i = 0; i < va_desc.num_objects; i++)
- close(va_desc.objects[i].fd);
- av_freep(&drm_desc);
- return err;
- }
- #endif
- #if VA_CHECK_VERSION(0, 36, 0)
- typedef struct VAAPIDRMImageBufferMapping {
- VAImage image;
- VABufferInfo buffer_info;
- AVDRMFrameDescriptor drm_desc;
- } VAAPIDRMImageBufferMapping;
- static void vaapi_unmap_to_drm_abh(AVHWFramesContext *hwfc,
- HWMapDescriptor *hwmap)
- {
- AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
- VAAPIDRMImageBufferMapping *mapping = hwmap->priv;
- VASurfaceID surface_id;
- VAStatus vas;
- surface_id = (VASurfaceID)(uintptr_t)hwmap->source->data[3];
- av_log(hwfc, AV_LOG_DEBUG, "Unmap VAAPI surface %#x from DRM.\n",
- surface_id);
- // DRM PRIME file descriptors are closed by vaReleaseBufferHandle(),
- // so we shouldn't close them separately.
- vas = vaReleaseBufferHandle(hwctx->display, mapping->image.buf);
- if (vas != VA_STATUS_SUCCESS) {
- av_log(hwfc, AV_LOG_ERROR, "Failed to release buffer "
- "handle of image %#x (derived from surface %#x): "
- "%d (%s).\n", mapping->image.buf, surface_id,
- vas, vaErrorStr(vas));
- }
- vas = vaDestroyImage(hwctx->display, mapping->image.image_id);
- if (vas != VA_STATUS_SUCCESS) {
- av_log(hwfc, AV_LOG_ERROR, "Failed to destroy image "
- "derived from surface %#x: %d (%s).\n",
- surface_id, vas, vaErrorStr(vas));
- }
- av_free(mapping);
- }
- static int vaapi_map_to_drm_abh(AVHWFramesContext *hwfc, AVFrame *dst,
- const AVFrame *src, int flags)
- {
- AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
- VAAPIDRMImageBufferMapping *mapping = NULL;
- VASurfaceID surface_id;
- VAStatus vas;
- int err, i, p;
- surface_id = (VASurfaceID)(uintptr_t)src->data[3];
- av_log(hwfc, AV_LOG_DEBUG, "Map VAAPI surface %#x to DRM.\n",
- surface_id);
- mapping = av_mallocz(sizeof(*mapping));
- if (!mapping)
- return AVERROR(ENOMEM);
- vas = vaDeriveImage(hwctx->display, surface_id,
- &mapping->image);
- if (vas != VA_STATUS_SUCCESS) {
- av_log(hwfc, AV_LOG_ERROR, "Failed to derive image from "
- "surface %#x: %d (%s).\n",
- surface_id, vas, vaErrorStr(vas));
- err = AVERROR(EIO);
- goto fail;
- }
- for (i = 0; i < FF_ARRAY_ELEMS(vaapi_drm_format_map); i++) {
- if (vaapi_drm_format_map[i].va_fourcc ==
- mapping->image.format.fourcc)
- break;
- }
- if (i >= FF_ARRAY_ELEMS(vaapi_drm_format_map)) {
- av_log(hwfc, AV_LOG_ERROR, "No matching DRM format for "
- "VAAPI format %#x.\n", mapping->image.format.fourcc);
- err = AVERROR(EINVAL);
- goto fail_derived;
- }
- mapping->buffer_info.mem_type =
- VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME;
- mapping->drm_desc.nb_layers =
- vaapi_drm_format_map[i].nb_layer_formats;
- if (mapping->drm_desc.nb_layers > 1) {
- if (mapping->drm_desc.nb_layers != mapping->image.num_planes) {
- av_log(hwfc, AV_LOG_ERROR, "Image properties do not match "
- "expected format: got %d planes, but expected %d.\n",
- mapping->image.num_planes, mapping->drm_desc.nb_layers);
- err = AVERROR(EINVAL);
- goto fail_derived;
- }
- for(p = 0; p < mapping->drm_desc.nb_layers; p++) {
- mapping->drm_desc.layers[p] = (AVDRMLayerDescriptor) {
- .format = vaapi_drm_format_map[i].layer_formats[p],
- .nb_planes = 1,
- .planes[0] = {
- .object_index = 0,
- .offset = mapping->image.offsets[p],
- .pitch = mapping->image.pitches[p],
- },
- };
- }
- } else {
- mapping->drm_desc.layers[0].format =
- vaapi_drm_format_map[i].layer_formats[0];
- mapping->drm_desc.layers[0].nb_planes = mapping->image.num_planes;
- for (p = 0; p < mapping->image.num_planes; p++) {
- mapping->drm_desc.layers[0].planes[p] = (AVDRMPlaneDescriptor) {
- .object_index = 0,
- .offset = mapping->image.offsets[p],
- .pitch = mapping->image.pitches[p],
- };
- }
- }
- vas = vaAcquireBufferHandle(hwctx->display, mapping->image.buf,
- &mapping->buffer_info);
- if (vas != VA_STATUS_SUCCESS) {
- av_log(hwfc, AV_LOG_ERROR, "Failed to get buffer "
- "handle from image %#x (derived from surface %#x): "
- "%d (%s).\n", mapping->image.buf, surface_id,
- vas, vaErrorStr(vas));
- err = AVERROR(EIO);
- goto fail_derived;
- }
- av_log(hwfc, AV_LOG_DEBUG, "DRM PRIME fd is %ld.\n",
- mapping->buffer_info.handle);
- mapping->drm_desc.nb_objects = 1;
- mapping->drm_desc.objects[0] = (AVDRMObjectDescriptor) {
- .fd = mapping->buffer_info.handle,
- .size = mapping->image.data_size,
- // There is no way to get the format modifier with this API.
- .format_modifier = DRM_FORMAT_MOD_INVALID,
- };
- err = ff_hwframe_map_create(src->hw_frames_ctx,
- dst, src, &vaapi_unmap_to_drm_abh,
- mapping);
- if (err < 0)
- goto fail_mapped;
- dst->data[0] = (uint8_t*)&mapping->drm_desc;
- dst->width = src->width;
- dst->height = src->height;
- return 0;
- fail_mapped:
- vaReleaseBufferHandle(hwctx->display, mapping->image.buf);
- fail_derived:
- vaDestroyImage(hwctx->display, mapping->image.image_id);
- fail:
- av_freep(&mapping);
- return err;
- }
- #endif
- static int vaapi_map_to_drm(AVHWFramesContext *hwfc, AVFrame *dst,
- const AVFrame *src, int flags)
- {
- #if VA_CHECK_VERSION(1, 1, 0)
- int err;
- err = vaapi_map_to_drm_esh(hwfc, dst, src, flags);
- if (err != AVERROR(ENOSYS))
- return err;
- #endif
- #if VA_CHECK_VERSION(0, 36, 0)
- return vaapi_map_to_drm_abh(hwfc, dst, src, flags);
- #endif
- return AVERROR(ENOSYS);
- }
- #endif /* CONFIG_LIBDRM */
- static int vaapi_map_to(AVHWFramesContext *hwfc, AVFrame *dst,
- const AVFrame *src, int flags)
- {
- switch (src->format) {
- #if CONFIG_LIBDRM
- case AV_PIX_FMT_DRM_PRIME:
- return vaapi_map_from_drm(hwfc, dst, src, flags);
- #endif
- default:
- return AVERROR(ENOSYS);
- }
- }
- static int vaapi_map_from(AVHWFramesContext *hwfc, AVFrame *dst,
- const AVFrame *src, int flags)
- {
- switch (dst->format) {
- #if CONFIG_LIBDRM
- case AV_PIX_FMT_DRM_PRIME:
- return vaapi_map_to_drm(hwfc, dst, src, flags);
- #endif
- default:
- return vaapi_map_to_memory(hwfc, dst, src, flags);
- }
- }
- static void vaapi_device_free(AVHWDeviceContext *ctx)
- {
- AVVAAPIDeviceContext *hwctx = ctx->hwctx;
- VAAPIDevicePriv *priv = ctx->user_opaque;
- if (hwctx->display)
- vaTerminate(hwctx->display);
- #if HAVE_VAAPI_X11
- if (priv->x11_display)
- XCloseDisplay(priv->x11_display);
- #endif
- if (priv->drm_fd >= 0)
- close(priv->drm_fd);
- av_freep(&priv);
- }
- #if CONFIG_VAAPI_1
- static void vaapi_device_log_error(void *context, const char *message)
- {
- AVHWDeviceContext *ctx = context;
- av_log(ctx, AV_LOG_ERROR, "libva: %s", message);
- }
- static void vaapi_device_log_info(void *context, const char *message)
- {
- AVHWDeviceContext *ctx = context;
- av_log(ctx, AV_LOG_VERBOSE, "libva: %s", message);
- }
- #endif
- static int vaapi_device_connect(AVHWDeviceContext *ctx,
- VADisplay display)
- {
- AVVAAPIDeviceContext *hwctx = ctx->hwctx;
- int major, minor;
- VAStatus vas;
- #if CONFIG_VAAPI_1
- vaSetErrorCallback(display, &vaapi_device_log_error, ctx);
- vaSetInfoCallback (display, &vaapi_device_log_info, ctx);
- #endif
- hwctx->display = display;
- vas = vaInitialize(display, &major, &minor);
- if (vas != VA_STATUS_SUCCESS) {
- av_log(ctx, AV_LOG_ERROR, "Failed to initialise VAAPI "
- "connection: %d (%s).\n", vas, vaErrorStr(vas));
- return AVERROR(EIO);
- }
- av_log(ctx, AV_LOG_VERBOSE, "Initialised VAAPI connection: "
- "version %d.%d\n", major, minor);
- return 0;
- }
- static int vaapi_device_create(AVHWDeviceContext *ctx, const char *device,
- AVDictionary *opts, int flags)
- {
- VAAPIDevicePriv *priv;
- VADisplay display = NULL;
- const AVDictionaryEntry *ent;
- int try_drm, try_x11, try_all;
- priv = av_mallocz(sizeof(*priv));
- if (!priv)
- return AVERROR(ENOMEM);
- priv->drm_fd = -1;
- ctx->user_opaque = priv;
- ctx->free = vaapi_device_free;
- ent = av_dict_get(opts, "connection_type", NULL, 0);
- if (ent) {
- try_all = try_drm = try_x11 = 0;
- if (!strcmp(ent->value, "drm")) {
- try_drm = 1;
- } else if (!strcmp(ent->value, "x11")) {
- try_x11 = 1;
- } else {
- av_log(ctx, AV_LOG_ERROR, "Invalid connection type %s.\n",
- ent->value);
- return AVERROR(EINVAL);
- }
- } else {
- try_all = 1;
- try_drm = HAVE_VAAPI_DRM;
- try_x11 = HAVE_VAAPI_X11;
- }
- #if HAVE_VAAPI_DRM
- while (!display && try_drm) {
- // If the device is specified, try to open it as a DRM device node.
- // If not, look for a usable render node, possibly restricted to those
- // using a specified kernel driver.
- int loglevel = try_all ? AV_LOG_VERBOSE : AV_LOG_ERROR;
- if (device) {
- priv->drm_fd = open(device, O_RDWR);
- if (priv->drm_fd < 0) {
- av_log(ctx, loglevel, "Failed to open %s as "
- "DRM device node.\n", device);
- break;
- }
- } else {
- char path[64];
- int n, max_devices = 8;
- #if CONFIG_LIBDRM
- const AVDictionaryEntry *kernel_driver;
- kernel_driver = av_dict_get(opts, "kernel_driver", NULL, 0);
- #endif
- for (n = 0; n < max_devices; n++) {
- snprintf(path, sizeof(path),
- "/dev/dri/renderD%d", 128 + n);
- priv->drm_fd = open(path, O_RDWR);
- if (priv->drm_fd < 0) {
- av_log(ctx, AV_LOG_VERBOSE, "Cannot open "
- "DRM render node for device %d.\n", n);
- break;
- }
- #if CONFIG_LIBDRM
- if (kernel_driver) {
- drmVersion *info;
- info = drmGetVersion(priv->drm_fd);
- if (strcmp(kernel_driver->value, info->name)) {
- av_log(ctx, AV_LOG_VERBOSE, "Ignoring device %d "
- "with non-matching kernel driver (%s).\n",
- n, info->name);
- drmFreeVersion(info);
- close(priv->drm_fd);
- priv->drm_fd = -1;
- continue;
- }
- av_log(ctx, AV_LOG_VERBOSE, "Trying to use "
- "DRM render node for device %d, "
- "with matching kernel driver (%s).\n",
- n, info->name);
- drmFreeVersion(info);
- } else
- #endif
- {
- av_log(ctx, AV_LOG_VERBOSE, "Trying to use "
- "DRM render node for device %d.\n", n);
- }
- break;
- }
- if (n >= max_devices)
- break;
- }
- display = vaGetDisplayDRM(priv->drm_fd);
- if (!display) {
- av_log(ctx, AV_LOG_VERBOSE, "Cannot open a VA display "
- "from DRM device %s.\n", device);
- return AVERROR_EXTERNAL;
- }
- break;
- }
- #endif
- #if HAVE_VAAPI_X11
- if (!display && try_x11) {
- // Try to open the device as an X11 display.
- priv->x11_display = XOpenDisplay(device);
- if (!priv->x11_display) {
- av_log(ctx, AV_LOG_VERBOSE, "Cannot open X11 display "
- "%s.\n", XDisplayName(device));
- } else {
- display = vaGetDisplay(priv->x11_display);
- if (!display) {
- av_log(ctx, AV_LOG_ERROR, "Cannot open a VA display "
- "from X11 display %s.\n", XDisplayName(device));
- return AVERROR_UNKNOWN;
- }
- av_log(ctx, AV_LOG_VERBOSE, "Opened VA display via "
- "X11 display %s.\n", XDisplayName(device));
- }
- }
- #endif
- if (!display) {
- if (device)
- av_log(ctx, AV_LOG_ERROR, "No VA display found for "
- "device %s.\n", device);
- else
- av_log(ctx, AV_LOG_ERROR, "No VA display found for "
- "any default device.\n");
- return AVERROR(EINVAL);
- }
- ent = av_dict_get(opts, "driver", NULL, 0);
- if (ent) {
- #if VA_CHECK_VERSION(0, 38, 0)
- VAStatus vas;
- vas = vaSetDriverName(display, ent->value);
- if (vas != VA_STATUS_SUCCESS) {
- av_log(ctx, AV_LOG_ERROR, "Failed to set driver name to "
- "%s: %d (%s).\n", ent->value, vas, vaErrorStr(vas));
- vaTerminate(display);
- return AVERROR_EXTERNAL;
- }
- #else
- av_log(ctx, AV_LOG_WARNING, "Driver name setting is not "
- "supported with this VAAPI version.\n");
- #endif
- }
- return vaapi_device_connect(ctx, display);
- }
- static int vaapi_device_derive(AVHWDeviceContext *ctx,
- AVHWDeviceContext *src_ctx, int flags)
- {
- #if HAVE_VAAPI_DRM
- if (src_ctx->type == AV_HWDEVICE_TYPE_DRM) {
- AVDRMDeviceContext *src_hwctx = src_ctx->hwctx;
- VADisplay *display;
- VAAPIDevicePriv *priv;
- if (src_hwctx->fd < 0) {
- av_log(ctx, AV_LOG_ERROR, "DRM instance requires an associated "
- "device to derive a VA display from.\n");
- return AVERROR(EINVAL);
- }
- priv = av_mallocz(sizeof(*priv));
- if (!priv)
- return AVERROR(ENOMEM);
- // Inherits the fd from the source context, which will close it.
- priv->drm_fd = -1;
- ctx->user_opaque = priv;
- ctx->free = &vaapi_device_free;
- display = vaGetDisplayDRM(src_hwctx->fd);
- if (!display) {
- av_log(ctx, AV_LOG_ERROR, "Failed to open a VA display from "
- "DRM device.\n");
- return AVERROR(EIO);
- }
- return vaapi_device_connect(ctx, display);
- }
- #endif
- return AVERROR(ENOSYS);
- }
- const HWContextType ff_hwcontext_type_vaapi = {
- .type = AV_HWDEVICE_TYPE_VAAPI,
- .name = "VAAPI",
- .device_hwctx_size = sizeof(AVVAAPIDeviceContext),
- .device_priv_size = sizeof(VAAPIDeviceContext),
- .device_hwconfig_size = sizeof(AVVAAPIHWConfig),
- .frames_hwctx_size = sizeof(AVVAAPIFramesContext),
- .frames_priv_size = sizeof(VAAPIFramesContext),
- .device_create = &vaapi_device_create,
- .device_derive = &vaapi_device_derive,
- .device_init = &vaapi_device_init,
- .device_uninit = &vaapi_device_uninit,
- .frames_get_constraints = &vaapi_frames_get_constraints,
- .frames_init = &vaapi_frames_init,
- .frames_uninit = &vaapi_frames_uninit,
- .frames_get_buffer = &vaapi_get_buffer,
- .transfer_get_formats = &vaapi_transfer_get_formats,
- .transfer_data_to = &vaapi_transfer_data_to,
- .transfer_data_from = &vaapi_transfer_data_from,
- .map_to = &vaapi_map_to,
- .map_from = &vaapi_map_from,
- .pix_fmts = (const enum AVPixelFormat[]) {
- AV_PIX_FMT_VAAPI,
- AV_PIX_FMT_NONE
- },
- };
|