/**
 * @file vaapi_x11.c
 * @brief VA API using X11 video output module for VLC media player
 */
/*****************************************************************************
 * Copyright © 2011 - 2012, M2X BV
 * Author: Jean-Paul Saman
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This library 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 ****************************************************************************/

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#include <stdlib.h>
#include <assert.h>

#include <vlc_common.h>
#include <vlc_plugin.h>
#include <vlc_es.h>
#include <vlc_picture.h>
#include <vlc_vout_display.h>
#include <vlc_vout_window.h>
#include <vlc_picture_pool.h>

#include <vlc_arrays.h>

#if defined(HAVE_AVCODEC_VAAPI) && defined(HAVE_XCB)

#ifdef HAVE_LIBAVCODEC_AVCODEC_H
#   include <libavcodec/avcodec.h>
#else
#   include <avcodec.h>
#endif

#include "avcodec.h"
#include "copy.h"

#include <xcb/xcb.h>
#include <X11/Xlib-xcb.h>
#include <vlc_xlib.h>
#include <libavcodec/vaapi.h>
#include <va/va.h>
#include <va/va_x11.h>
#include "va.h"
#include "vaapi.h"
#include "vasub.h"

#include <X11/Xutil.h>

#include "../../video_output/xcb/xcb_events_vlc.h"

/* define for extra debugging */
#undef VAAPI_DEBUG

static const vlc_fourcc_t va_subpicture_chromas[] = {
    VLC_CODEC_RGBA,
    0
};

struct vout_display_sys_t
{
    vlc_va_conn_t *conn;

    vout_window_t *embed;

    picture_pool_t *pool;  /* picture pool */
    VAImageFormat img_fmt;
    VAImageFormat sub_fmt; /* Subpicture format VA_FOURCC_RGBA */
    int           sflags;  /* Subtitle format flags */

    struct {
        unsigned int   i_cache;
        mtime_t        i_start;
        mtime_t        i_stop;
        subpicture_t  *prev_subpicture;
    } render;

    vlc_mutex_t   cache_lock;
    vlc_array_t   cache;  /* array of subpicture_subpicture_cache_t */

    Display      *x11display; /* x11 display connection */
    xcb_cursor_t cursor; /* blank cursor */
    xcb_window_t window; /* drawable X window */

    bool visible;  /* whether to draw */
};

static picture_pool_t *Pool(vout_display_t *, unsigned);
static void Render(vout_display_t *, picture_t *, subpicture_t *);
static void DisplayPicture(vout_display_t *, picture_t *, subpicture_t *);
static int Control(vout_display_t *, int, va_list);
static void Manage(vout_display_t *);

/* VA API support functions */
static int FindVAFourCC(vout_display_t *vd)
{
    vout_display_sys_t *sys = (vout_display_sys_t *) vd->sys;

    sys->conn->lock();

    /* Find and create a supported image chroma */
    int i_fmt_count = vaMaxNumImageFormats(sys->conn->p_display);
    VAImageFormat *p_fmt = calloc(i_fmt_count, sizeof(*p_fmt));
    if( !p_fmt )
        goto error;

    if( vaQueryImageFormats( sys->conn->p_display, p_fmt, &i_fmt_count ) )
    {
        free( p_fmt );
        goto error;
    }

    vlc_fourcc_t  i_chroma = 0;
    int32_t i_bits_per_pixel = 0;
    for( int i = 0; i < i_fmt_count; i++ )
    {
        if( p_fmt[i].fourcc == VA_FOURCC( 'Y', 'V', '1', '2' ) ||
            p_fmt[i].fourcc == VA_FOURCC( 'I', '4', '2', '0' ) ||
            p_fmt[i].fourcc == VA_FOURCC( 'N', 'V', '1', '2' ) )
        {
            memcpy((void*)&sys->img_fmt, (void*)&p_fmt[i], sizeof(VAImageFormat));
            i_chroma = VLC_CODEC_YV12;
            i_bits_per_pixel = p_fmt[i].bits_per_pixel;
            break;
        }
    }
    free( p_fmt );
    if( !i_chroma )
        goto error;

    vd->fmt.i_chroma = i_chroma;
    vd->fmt.i_bits_per_pixel = i_bits_per_pixel;
    sys->conn->unlock();
    return VLC_SUCCESS;

error:
    sys->conn->unlock();
    return VLC_EGENERIC;
}

static vout_window_t *MakeWindow(vout_display_t *vd)
{
    vout_window_cfg_t wnd_cfg;

    memset (&wnd_cfg, 0, sizeof (wnd_cfg));
    wnd_cfg.type = VOUT_WINDOW_TYPE_XID;
    wnd_cfg.x = var_InheritInteger(vd, "video-x");
    wnd_cfg.y = var_InheritInteger(vd, "video-y");
    wnd_cfg.width  = vd->cfg->display.width;
    wnd_cfg.height = vd->cfg->display.height;

    vout_window_t *wnd = vout_display_NewWindow(vd, &wnd_cfg);
    if (wnd == NULL)
        msg_Err(vd, "parent window not available");
    return wnd;
}

static const xcb_screen_t *FindWindow(vout_display_t *vd, xcb_connection_t *conn,
           unsigned *restrict pnum, uint8_t *restrict pdepth,
           uint16_t *restrict pwidth, uint16_t *restrict pheight)
{
    vout_display_sys_t *sys = vd->sys;

    xcb_get_geometry_reply_t *geo =
        xcb_get_geometry_reply(conn,
            xcb_get_geometry(conn, sys->embed->handle.xid), NULL);
    if (geo == NULL)
    {
        msg_Err (vd, "parent window not valid");
        return NULL;
    }

    xcb_window_t root = geo->root;
    *pdepth = geo->depth;
    *pwidth = geo->width;
    *pheight = geo->height;
    free (geo);

    /* Find the selected screen */
    const xcb_setup_t *setup = xcb_get_setup(conn);
    const xcb_screen_t *screen = NULL;
    unsigned num = 0;

    for (xcb_screen_iterator_t i = xcb_setup_roots_iterator(setup);
         i.rem > 0;
         xcb_screen_next (&i))
    {
        if (i.data->root == root)
        {
            screen = i.data;
            break;
        }
        num++;
    }

    if (screen == NULL)
    {
        msg_Err(vd, "parent window screen not found");
        return NULL;
    }
    msg_Dbg(vd, "using screen 0x%"PRIx32 " (number: %u)", root, num);
    *pnum = num;
    return screen;
}

static int CreateWindow(vout_display_t *vd, xcb_connection_t *conn,
                        uint_fast8_t depth, xcb_visualid_t vid,
                        uint_fast16_t width, uint_fast16_t height)
{
    vout_display_sys_t *sys = vd->sys;
    const uint32_t mask = XCB_CW_EVENT_MASK;
    const uint32_t values[] = {
        /* XCB_CW_EVENT_MASK */
        XCB_EVENT_MASK_VISIBILITY_CHANGE,
    };
    xcb_void_cookie_t cc, cm;

    cc = xcb_create_window_checked(conn, depth, sys->window,
                                   sys->embed->handle.xid, 0, 0,
                                   width, height, 0,
                                   XCB_WINDOW_CLASS_INPUT_OUTPUT,
                                   vid, mask, values);
    cm = xcb_map_window_checked(conn, sys->window);
    if (CheckError(vd, conn, "cannot create X11 window", cc) ||
        CheckError(vd, conn, "cannot map X11 window", cm))
        return VLC_EGENERIC;

    msg_Dbg (vd, "using VAAPI X11 window %08"PRIx32, sys->window);
    xcb_flush(XGetXCBConnection(sys->x11display));
    return VLC_SUCCESS;
}

/**
 * Probe the X server.
 */
int OpenVaapiX11(vlc_object_t *obj)
{
    vout_display_t *vd = (vout_display_t *) obj;

    if (!vlc_xlib_init(obj))
        return VLC_EGENERIC;

    vout_display_sys_t *sys = calloc(1, sizeof(*sys));
    if (unlikely(sys == NULL))
        return VLC_ENOMEM;

    vd->sys = sys;

    sys->conn = NULL;
    sys->pool = NULL;

    vlc_array_init(&sys->cache);
    vlc_mutex_init(&sys->cache_lock);

    sys->render.prev_subpicture = NULL;
    sys->render.i_cache = 0;

    sys->embed = MakeWindow(vd);
    if (unlikely(sys->embed == NULL))
        goto error;

    /* X11 display */
    sys->x11display = XOpenDisplay(sys->embed->display.x11);
    if (!sys->x11display)
        goto error;

    /* Create a VA display */
    sys->conn = vlc_va_Initialize(sys->x11display);
    if (!sys->conn)
        goto error;

    if (FindVAFourCC(vd) != VLC_SUCCESS)
        goto error;

    XSetEventQueueOwner(sys->x11display, XCBOwnsEventQueue);

    xcb_connection_t *conn = XGetXCBConnection(sys->x11display);
    assert(conn);

    RegisterMouseEvents(obj, conn, sys->embed->handle.xid);

    /* Find window parameters */
    unsigned snum;
    uint8_t depth;
    uint16_t width, height;
    const xcb_screen_t *scr = FindWindow(vd, conn, &snum, &depth,
                                         &width, &height);
    if (scr == NULL)
        goto error;

    sys->window = xcb_generate_id(conn);

    if (CreateWindow(vd, conn, depth, 0 /* ??? */, width, height) != VLC_SUCCESS)
    {
        msg_Err (vd, "cannot create VAAPI-X11 window");
        goto error;
    }

    sys->visible = false;
    sys->cursor = CreateBlankCursor(conn, scr);

    msg_Info(vd, "using VAAPI XCB video output (libva version %d.%d)",
            sys->conn->i_version_major, sys->conn->i_version_minor);

    /* */
    vout_display_info_t info = vd->info;
    info.is_slow = false;
    info.has_pictures_invalid = false;
    info.has_event_thread = true;
    info.has_hide_mouse = false;

    /* Setup vout_display_t once everything is fine */
    if (VASubtitleFourCC(sys->conn, VA_FOURCC_RGBA, &sys->sub_fmt, &sys->sflags) == VLC_SUCCESS)
    {
        info.subpicture_chromas = va_subpicture_chromas;
        vd->prepare = Render;
    }
    else
    {
        msg_Warn(vd, "VAAP-XCB does not support fourcc RGBA subtitles");

        vd->prepare = NULL;
        info.subpicture_chromas = NULL;
    }

    vd->pool = Pool;
    vd->display = DisplayPicture;
    vd->control = Control;
    vd->manage = Manage;
    vd->info = info;

    /* */
    if (!(sys->sflags & VA_SUBPICTURE_GLOBAL_ALPHA) &&
        !(sys->sflags & VA_SUBPICTURE_CHROMA_KEYING))
        msg_Warn(vd, "Display server does not support alpha or chroma for subtitles.");

    /* */
    bool is_fullscreen = vd->cfg->is_fullscreen;
    if (is_fullscreen && vout_window_SetFullScreen(sys->embed, true))
        is_fullscreen = false;
    vout_display_SendEventFullscreen(vd, is_fullscreen);
    vout_display_SendEventDisplaySize(vd, width, height, is_fullscreen);
    return VLC_SUCCESS;

error:
    CloseVaapiX11(obj);
    return VLC_EGENERIC;
}

void CloseVaapiX11(vlc_object_t *obj)
{
    vout_display_t *vd = (vout_display_t *) obj;
    vout_display_sys_t *sys = (vout_display_sys_t *) vd->sys;

    sys->visible = false;

    /* Cleanup cache */
    vlc_mutex_destroy(&sys->cache_lock);
    const int count = vlc_array_count(&sys->cache);
    for (int i = count - 1; i >= 0; i--)
    {
        subpicture_cache_t *cache;
        cache = (subpicture_cache_t*) vlc_array_item_at_index(&sys->cache, i);
        if (!cache)
            continue;

        vlc_array_remove(&sys->cache, i);

        cache_SubpictureRelease(obj, sys->conn, &sys->cache, cache_GetCacheID(cache));
        cache_SubpictureDestroy(obj, sys->conn, cache, true);
    }
    vlc_array_clear(&sys->cache);

    if (sys->x11display)
    {
        /* show the default cursor */
        xcb_change_window_attributes(XGetXCBConnection(sys->x11display),
                                     sys->embed->handle.xid, XCB_CW_CURSOR,
                                     &(uint32_t) { XCB_CURSOR_NONE });
        xcb_flush(XGetXCBConnection(sys->x11display));
    }

    if (sys->embed)
        vout_display_DeleteWindow (vd, sys->embed);

    /* */
    if (sys->pool)
    {
        picture_pool_Delete(sys->pool);
        sys->pool = NULL;
    }

    if (sys->conn)
    {
        sys->conn->destroy_surfaces(sys->conn);
        vlc_va_Terminate(sys->conn);
    }

    if (sys->x11display)
        XCloseDisplay(sys->x11display);

    free(vd->sys);
}

static void Manage(vout_display_t *vd)
{
    int canc = vlc_savecancel();
    vout_display_sys_t *sys = vd->sys;
    xcb_connection_t *conn = XGetXCBConnection(sys->x11display);
    ManageEvent(vd, conn, &sys->visible);
    vlc_restorecancel(canc);
}

static int Control (vout_display_t *vd, int query, va_list ap)
{
    vout_display_sys_t *sys = vd->sys;

    switch (query)
    {
    case VOUT_DISPLAY_CHANGE_FULLSCREEN:
    {
        const vout_display_cfg_t *c = va_arg(ap, const vout_display_cfg_t *);
        return vout_window_SetFullScreen(sys->embed, c->is_fullscreen);
    }

    case VOUT_DISPLAY_CHANGE_WINDOW_STATE:
    {
        unsigned state = va_arg(ap, unsigned);
        return vout_window_SetState(sys->embed, state);
    }

    case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE:
    case VOUT_DISPLAY_CHANGE_ZOOM:
    case VOUT_DISPLAY_CHANGE_DISPLAY_FILLED:
    case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT:
    case VOUT_DISPLAY_CHANGE_SOURCE_CROP:
    {
        xcb_connection_t *conn = XGetXCBConnection(sys->x11display);

        const vout_display_cfg_t *cfg;
        const video_format_t *source;
        bool is_forced = false;

        if (query == VOUT_DISPLAY_CHANGE_SOURCE_ASPECT
         || query == VOUT_DISPLAY_CHANGE_SOURCE_CROP)
        {
            source = (const video_format_t *)va_arg(ap, const video_format_t *);
            cfg = vd->cfg;
        }
        else
        {
            source = &vd->source;
            cfg = (const vout_display_cfg_t*)va_arg(ap, const vout_display_cfg_t *);
            if (query == VOUT_DISPLAY_CHANGE_DISPLAY_SIZE)
                is_forced = (bool)va_arg(ap, int);
        }

        /* */
        if (query == VOUT_DISPLAY_CHANGE_DISPLAY_SIZE
         && is_forced
         && (cfg->display.width  != vd->cfg->display.width
           ||cfg->display.height != vd->cfg->display.height)
         && vout_window_SetSize(sys->embed,
                                cfg->display.width, cfg->display.height))
            return VLC_EGENERIC;

        vout_display_place_t place;
        vout_display_PlacePicture(&place, source, cfg, false);

        /* Move the picture within the window */
        const uint32_t values[] = { place.x, place.y,
                                    place.width, place.height, };
        xcb_void_cookie_t ck =
            xcb_configure_window_checked(conn, sys->window,
                            XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y
                          | XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT,
                              values);
        if (CheckError(vd, conn, "cannot resize X11 window", ck))
            return VLC_EGENERIC;

        return VLC_SUCCESS;
    }
    case VOUT_DISPLAY_RESET_PICTURES:
    {
        assert(0);
        return VLC_SUCCESS;
    }

    /* Hide the mouse. It will be send when
     * vout_display_t::info.b_hide_mouse is false */
    case VOUT_DISPLAY_HIDE_MOUSE:
    {
        xcb_connection_t *conn = XGetXCBConnection(sys->x11display);
        xcb_change_window_attributes(conn, sys->embed->handle.xid,
                                     XCB_CW_CURSOR, &(uint32_t){ sys->cursor });
        xcb_flush(conn);
        return VLC_SUCCESS;
    }

    default:
        msg_Err (vd, "Unknown request in VAAPI-X11 vout display");
        return VLC_EGENERIC;
    }
}

static void Render(vout_display_t *vd, picture_t *picture, subpicture_t *subpicture)
{
    vout_display_sys_t *sys = vd->sys;

    if (!subpicture)
        return;

    assert(!subpicture->b_ephemer);
    assert(picture->format.i_chroma == VLC_CODEC_VAAPI_SURFACE);

    if (!picture->p_sys)
        return;

    /* count RGBA subpicture regions */
    int i_region = 0;

    subpicture_region_t *region = NULL;
    for (region = subpicture->p_region; region != NULL; region = region->p_next)
    {
        /* Skip non-RGBA regions for now */
        if (region->fmt.i_chroma == VLC_CODEC_RGBA)
            i_region++;
    }

    if (i_region == 0)
        return;

    /* */
    vlc_mutex_lock(&sys->cache_lock);

    /* same as previous subpicture? */
    if ((sys->render.prev_subpicture == subpicture) &&
        (sys->render.i_cache > 0))
    {
#ifdef VAAPI_DEBUG
        msg_Dbg(vd, "reuse subpicture %p prev %p cache %d surface %d",
                subpicture, sys->render.prev_subpicture,
                sys->render.i_cache, picture->p_sys->i_id);
#endif

        int ret = RenderCachedSubpictures(VLC_OBJECT(vd), sys->conn, &sys->cache,
                                          sys->render.i_cache, picture, subpicture);
        if (ret == VLC_SUCCESS)
            goto out;
    }

    /* Release exra reference */
    if (sys->render.i_cache > 0)
        cache_SubpictureRelease(VLC_OBJECT(vd), sys->conn, &sys->cache, sys->render.i_cache);

    /* render new subpicture */
    subpicture_cache_t *cache = RenderDirectSubpicture(VLC_OBJECT(vd), sys->conn, &sys->sub_fmt,
                                                       picture, subpicture, sys->sflags);
    if (cache == NULL)
        goto out;

#ifdef VAAPI_DEBUG
    msg_Dbg(vd, "new subpicture %p prev %p cache %d surface %d",
            subpicture, sys->render.prev_subpicture, cache_GetCacheID(cache), surface->i_id);
#endif
    /* remember what was created, so it can be released in DisplayPicture() */
    vlc_array_append(&sys->cache, (void *)cache);

    sys->render.i_cache = cache_GetCacheID(cache);
    sys->render.prev_subpicture = subpicture;
    sys->render.i_start = subpicture->i_start;
    sys->render.i_stop  = subpicture->i_stop;

    /* Hold extra reference */
    cache_SubpictureHold(VLC_OBJECT(vd), &sys->cache, cache_GetCacheID(cache));

out:
    vlc_mutex_unlock(&sys->cache_lock);
}

static void DisplayVASurface(vout_display_t *vd, picture_t *picture)
{
    vout_display_sys_t *sys = vd->sys;
    picture_sys_t *surface = picture->p_sys;
    assert(surface);

    sys->conn->lock();

    int canc = vlc_savecancel();

    vout_display_place_t place;
    vout_display_PlacePicture(&place, &vd->source, vd->cfg, false);

    VAStatus status;
    status = vaPutSurface(sys->conn->p_display, surface->i_id, sys->window,
                          vd->source.i_x_offset, vd->source.i_y_offset,
                          vd->source.i_visible_width, vd->source.i_visible_height,
                          place.x, place.y, place.width, place.height,
                          NULL, 0, /* clipping */
                          /* NOTE: vaCreateContext() specifies VA_PROGRESSIVE */
                          picture->b_progressive ? VA_FRAME_PICTURE :
                               (picture->b_top_field_first ? VA_TOP_FIELD : VA_BOTTOM_FIELD));
    if (status != VA_STATUS_SUCCESS)
        msg_Err(vd, "failed displaying picture: %d (surface id=%d): %p",
                status, surface->i_id, picture);

    vlc_restorecancel(canc);

    sys->conn->unlock();
}

static void DisplayPicture(vout_display_t *vd, picture_t *picture, subpicture_t *subpicture)
{
    vout_display_sys_t *sys = vd->sys;
    int ret = VLC_EGENERIC;
    int canc = vlc_savecancel();

    if (!sys->visible)
        goto out;

    assert(picture->format.i_chroma == VLC_CODEC_VAAPI_SURFACE);

    if ((picture->p_sys == NULL) ||
        (picture->p_sys->i_id == VA_INVALID_SURFACE))
    {
        msg_Err(vd, "discarding picture without picture_sys_t information");
        goto out;
    }

    if (subpicture)
    {
        vlc_mutex_lock(&sys->cache_lock);
        ret = SubpictureRegionsLink(VLC_OBJECT(vd), sys->conn, &sys->cache, picture, sys->sflags);
        if (ret == VLC_EGENERIC)
            msg_Err(vd, "Failed linking subpicture to surface");
        vlc_mutex_unlock(&sys->cache_lock);
    }
    DisplayVASurface(vd, picture);

out:
    if (subpicture)
    {
        vlc_mutex_lock(&sys->cache_lock);
        if (ret == VLC_SUCCESS)
        {
            if (SubpictureRegionsUnlink(VLC_OBJECT(vd), sys->conn, &sys->cache, picture) == VLC_EGENERIC)
                msg_Err(vd, "Could not find subpicture in cache");
        }
        if (picture->p_sys->i_cache > 0)
            cache_SubpictureRelease(VLC_OBJECT(vd), sys->conn, &sys->cache, picture->p_sys->i_cache);
        picture->p_sys->i_cache = 0;
        vlc_mutex_unlock(&sys->cache_lock);

        subpicture_Delete(subpicture);
    }

    picture_Release(picture);
    vlc_restorecancel(canc);
}

/**
 * PictureLock and PictureUnlock will be called by picture_pool_*
 * when the picture is held and released. Both are called only
 * once. PictureLock when the picture is retrieved from the pool
 * and PictureUnlock when the picture is returned to the pool.
 */
static int PictureLock(picture_t *picture)
{
    picture_sys_t *surface = picture->p_sys;
    if (surface)
        surface->i_refcount++;
    return VLC_SUCCESS;
}

static void PictureUnlock(picture_t *picture)
{
    assert(picture->p_sys);

    picture_sys_t *surface = picture->p_sys;
    if (surface == NULL)
        return;

    if (surface->i_refcount > 0)
        surface->i_refcount--;
}

/* PictureRelease is called when the picture pool is destroyed. */
static void PictureRelease(picture_t *picture)
{
    if (picture->i_refcount > 0)
        picture->i_refcount--;

    free(picture->p_sys);
    picture->p_sys = NULL;
}

/**
 * Return a direct buffer
 */
static picture_pool_t *Pool (vout_display_t *vd, unsigned requested_count)
{
    vout_display_sys_t *sys = vd->sys;

    assert(requested_count > 0);
    if (sys->pool)
        return sys->pool;

    picture_t **pic_array = (picture_t **) calloc(requested_count, sizeof(picture_t *));
    if (!pic_array)
        return NULL;

    unsigned int count;
    for (count = 0; count < requested_count; count++)
    {
        picture_t *pic = (picture_t*) picture_NewFromFormat(&vd->fmt);
        if (!pic)
            break;
        assert(pic->p_sys == NULL);
        picture_sys_t *pic_sys = (picture_sys_t *) malloc(sizeof(picture_sys_t));
        if (!pic_sys)
        {
            picture_Release(pic);
            break;
        }
        pic->format.i_chroma = VLC_CODEC_VAAPI_SURFACE;

        pic_sys->i_id = VA_INVALID_SURFACE;
        pic_sys->i_refcount = 0;
        pic_sys->i_cache = 0;

        pic_array[count] = pic;
        pic_array[count]->b_progressive = true;
        pic_array[count]->p_sys = pic_sys;
        pic_array[count]->pf_release = PictureRelease;
    }

    int i_surface_count = count;
    if (count == 0)
        goto error;

    /* Create surfaces */
    sys->conn->lock();

    VASurfaceID *pi_surface_id;
    if (sys->conn->i_surface_count < i_surface_count)
    {
        pi_surface_id = sys->conn->create_surfaces(sys->conn,
                                vd->fmt.i_visible_width, vd->fmt.i_visible_height,
                                VA_RT_FORMAT_YUV420, i_surface_count);
    }
    else pi_surface_id = sys->conn->p_surface_ids;

    if (pi_surface_id == NULL)
    {
        sys->conn->unlock();
        goto error;
    }

    for (int i = 0; i < i_surface_count; i++)
    {
        picture_sys_t *pic_sys = pic_array[i]->p_sys;
        assert(pic_sys);

        pic_sys->i_id = pi_surface_id[i];
    }
    sys->conn->unlock();

    /* Register pool with video output core */
    picture_pool_configuration_t cfg;

    memset(&cfg, 0, sizeof(cfg));
    cfg.picture_count = count;
    cfg.picture       = pic_array;
    cfg.lock          = PictureLock;
    cfg.unlock        = PictureUnlock;

    sys->pool = picture_pool_NewExtended(&cfg);
    if (!sys->pool)
        goto error;

    free(pic_array);
    return sys->pool;

error:
    /* Cleanup on error */
    for (unsigned int i = 0; i < count; i++)
    {
        picture_t *pic = pic_array[i];
        if (pic)
            picture_Release(pic);
    }
    free(pic_array);
    return NULL;
}

#endif /* HAVE_AVCODEC_VAAPI */
