/*****************************************************************************
 * Copyright © 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_subpicture.h>

#include <vlc_arrays.h>

#ifdef HAVE_AVCODEC_VAAPI

#include <xcb/xcb.h>
#include <X11/Xlib-xcb.h>

#include <va/va.h>

#include "vaapi.h"
#include "vasub.h"

/* subpicture region cache management */
static unsigned int cache_counter = 0;

struct vasubpicture_cache_t
{
  VASubpictureID i_id;
  VAImage        image;
  struct {
    int i_x, i_y;
    int i_width, i_height;
  } rect;
};

struct subpicture_cache_t
{
    unsigned int  i_id;
    unsigned int  i_refcount;  /* refcount usage */
    mtime_t       i_start;     /* start display date */
    mtime_t       i_stop;      /* stop display date */
    vlc_array_t   subpictures; /* subpicture regions */
};

/* */
static subpicture_cache_t *cache_new(void)
{
    subpicture_cache_t *cache = calloc(1, sizeof(subpicture_cache_t));
    if (cache == NULL)
        return NULL;

    cache->i_id = ++cache_counter;
    assert(cache_counter > 0);
    vlc_array_init(&cache->subpictures);
    return cache;
}

static void cache_destroy(subpicture_cache_t *cache)
{
    assert(cache);
    assert(cache->i_refcount == 0);
    assert(vlc_array_count(&cache->subpictures) == 0);

    vlc_array_clear(&cache->subpictures);
    free(cache);
}

static subpicture_cache_t *cache_find(vlc_array_t *cache, const unsigned int id)
{
    const int count = vlc_array_count(cache);
    for (int i = 0; i < count; i++)
    {
        subpicture_cache_t *c = (subpicture_cache_t *) vlc_array_item_at_index(cache, i);
        if (c)
        {
            if (c->i_id == id)
                return c;
        }
    }
    return NULL;
}

static inline void cache_ref(subpicture_cache_t *cache)
{
    assert(cache);
    cache->i_refcount++;
}

static inline void cache_unref(subpicture_cache_t *cache)
{
    assert(cache);
    assert(cache->i_refcount > 0);
    if (cache->i_refcount > 0)
        cache->i_refcount--;
}

static vasubpicture_cache_t *subpicture_cache_new( void )
{
    vasubpicture_cache_t *cache = calloc(1, sizeof(vasubpicture_cache_t));
    if (cache == NULL)
        return NULL;

    cache->i_id = VA_INVALID_ID;
    cache->image.buf = VA_INVALID_ID;
    cache->image.image_id = VA_INVALID_ID;
    return cache;
}

static void subpicture_cache_destroy(vasubpicture_cache_t *cache)
{
    assert(cache);
    assert(cache->i_id == VA_INVALID_ID);
    assert(cache->image.buf == VA_INVALID_ID);
    assert(cache->image.image_id == VA_INVALID_ID);

    free(cache);
}

unsigned int cache_GetCacheID(const subpicture_cache_t *cache)
{
    assert(cache);
    return cache->i_id;
}
/* ------------------------ */
/* VA API support functions */
/* ------------------------ */
int VASubtitleFourCC(vlc_va_conn_t *vaconn, const unsigned int vafourcc, VAImageFormat *sub_fmt, int *flags)
{
    vaconn->lock();

    int num = vaMaxNumSubpictureFormats(vaconn->p_display);
    if (num <= 0)
        goto out_warning;

    VAImageFormat *p_fmt = calloc(num, sizeof(*p_fmt));
    unsigned int  *vaflags = calloc(num, sizeof(*vaflags));
    if (!p_fmt || !vaflags)
        goto out;

    unsigned int num_fmt = 0;
    VAStatus status = vaQuerySubpictureFormats(vaconn->p_display,
                                               p_fmt, vaflags, &num_fmt);
    if (status != VA_STATUS_SUCCESS)
        goto out;

    for (unsigned int i = 0; i < num_fmt; i++)
    {
        VAImageFormat format = p_fmt[i];
        if (format.fourcc == vafourcc)
        {
            *flags = vaflags[i];
            memcpy((void*)sub_fmt, (void*)&format, sizeof(VAImageFormat));
            free(p_fmt);
            free(vaflags);
            vaconn->unlock();
            return VLC_SUCCESS;
        }
    }

out:
    free(p_fmt);
    free(vaflags);

out_warning:
    vaconn->unlock();
    return VLC_EGENERIC;
}

static inline void vaapi_fixup_alpha(uint8_t *p_pixel, size_t i_size)
{
    for (size_t p = 0; p < i_size; p += 4)
    {
        /* RGBA */
        uint8_t *R = &p_pixel[p];
        uint8_t *G = &p_pixel[p+1];
        uint8_t *B = &p_pixel[p+2];
        uint8_t *A = &p_pixel[p+3];

        if (*A != 255) /* alpha transparancy */
        {
            *A = 0; /* 0 = transparent, 255 = opaque */
            *R = 0;
            *G = 0;
            *B = 0;
        }
    }
 }

static int CopyPictureToVAImage(vlc_object_t *obj, vlc_va_conn_t *vaconn,
                                picture_t *pic, VAImage *image, VAImageFormat *fmt,
                                const bool fixup_alpha)
{
    VAStatus status;
    status = vaCreateImage(vaconn->p_display, fmt,
                           pic->format.i_width, pic->format.i_height, image);
    if (status != VA_STATUS_SUCCESS)
        return VLC_EGENERIC;

    /* Sanity checks */
    if ((image->image_id == VA_INVALID_ID) ||
        (image->format.fourcc != VA_FOURCC_RGBA) ||
        (image->num_planes != (unsigned int) pic->i_planes) ||
        (image->height != pic->format.i_height) ||
        (image->width != pic->format.i_width) ||
        (image->format.bits_per_pixel != pic->format.i_bits_per_pixel))
    {
        msg_Err(obj, "subpicture region: planes %d/%d, depth %d, width %d/%d, height %d/%d, bpp %d/%d",
                 image->num_planes, pic->i_planes,
                 image->format.depth,
                 image->width, pic->format.i_width,
                 image->height, pic->format.i_height,
                 image->format.bits_per_pixel, pic->format.i_bits_per_pixel);
        goto out_va_free;
    }
    assert(image->image_id != VA_INVALID_ID);
    assert(image->buf != VA_INVALID_ID);

    void *p_base = NULL;
    status = vaMapBuffer(vaconn->p_display, image->buf, &p_base);
    if (status != VA_STATUS_SUCCESS)
    {
        msg_Err(obj, "failed mapping image for copying (%d)", status);
        msg_Err(obj, "subpicture region: planes %d/%d, depth %d, width %d/%d, height %d/%d, bpp %d/%d",
                 image->num_planes, pic->i_planes,
                 image->format.depth,
                 image->width, pic->format.i_width,
                 image->height, pic->format.i_height,
                 image->format.bits_per_pixel, pic->format.i_bits_per_pixel);
        goto out_va_free;
    }
    assert(p_base); /* should not happen */

    for (unsigned int i_plane = 0; i_plane < image->num_planes; i_plane++)
    {
        /* Does the pitch match? Then just copy it directly */
        if (image->pitches[i_plane] == (unsigned int)pic->p[i_plane].i_pitch)
        {
            vlc_memcpy((uint8_t*)(((uint8_t*)p_base) + image->offsets[i_plane]),
                       pic->p[i_plane].p_pixels,
                       pic->p[i_plane].i_pitch * pic->p[i_plane].i_visible_lines);
        }
        else
        {
            /* proceed line by line */
            uint8_t *p_in  = pic->p[i_plane].p_pixels;
            uint8_t *p_out = (uint8_t *)(((uint8_t*)p_base) + image->offsets[i_plane]);

            int i_line = pic->format.i_y_offset * pic->p[i_plane].i_visible_pitch;
            for (; i_line < pic->p[i_plane].i_visible_lines; i_line++)
            {
                /* NOTE: image->pitch[i_plane] includes image->offset[i_plane], thus
                 * visible_pitch = image->pitch[i_plane] - image->offset[i_plane] */
                size_t i_size = __MIN(image->pitches[i_plane],
                                      (unsigned int)pic->p[i_plane].i_pitch);
                vlc_memcpy(&p_out[i_line * image->pitches[i_plane]], &p_in[i_line * pic->p[i_plane].i_pitch], i_size);

                /* FIXME: Remove this code when VDPAU and XvBA backends for
                 * VAAPI properly support alpha or chroma keying in subpictures. */
                if (fixup_alpha)
                {
                    uint8_t *p_pixel = p_out + (i_line * image->pitches[i_plane]);
                    vaapi_fixup_alpha(p_pixel, i_size);
                }
            }
        }
    }

    status = vaUnmapBuffer(vaconn->p_display, image->buf);
    if (status != VA_STATUS_SUCCESS)
    {
        msg_Err(obj, "failed unmapping image for copying (%d)", status);
        goto out_va_free;
    }

    return VLC_SUCCESS;

out_va_free:
    msg_Err(obj, "failed to copy region into vaImage id=%d", image->image_id);
    vaDestroyImage(vaconn->p_display, image->image_id);
    image->image_id = VA_INVALID_ID;
    image->buf = VA_INVALID_ID;
    return VLC_EGENERIC;
}

/* Create subpicture cache for this subpicture and
 * allocate needed VA API resources. Call this
 * function when holding cache->lock */
vasubpicture_cache_t *cache_SubpictureCreate(vlc_object_t *obj, vlc_va_conn_t *vaconn, VAImageFormat *fmt,
            const subpicture_t *subpicture, const subpicture_region_t *region, const int flags)
{
    int canc = vlc_savecancel();

    vaconn->lock();

    /* Copy pixels from region into VAImage */
    vasubpicture_cache_t *vasub_cache = subpicture_cache_new();
    if (!vasub_cache)
        goto error;

    VAImage *image = &vasub_cache->image;
    image->image_id = VA_INVALID_ID;
    image->buf = VA_INVALID_ID;

    int err = CopyPictureToVAImage(obj, vaconn, region->p_picture, image, fmt,
                                       (flags == 0x0) ? true : false);
    if (err != VLC_SUCCESS)
    {
        subpicture_cache_destroy(vasub_cache);
        goto error;
    }

    VAStatus status;
    status = vaCreateSubpicture(vaconn->p_display, image->image_id, &vasub_cache->i_id);
    if (status != VA_STATUS_SUCCESS)
        goto cleanup;

    bool b_alpha  = (flags & VA_SUBPICTURE_GLOBAL_ALPHA);
    bool b_chroma = (flags & VA_SUBPICTURE_CHROMA_KEYING);

    if (b_alpha)
    {
        assert(!b_chroma);

        float global_alpha = (float)(subpicture->i_alpha * region->i_alpha / 255) / 255;
        status = vaSetSubpictureGlobalAlpha(vaconn->p_display, vasub_cache->i_id, global_alpha);
        if (status != VA_STATUS_SUCCESS)
            msg_Err(obj, "failed applying alpha value to subpicture");
    }

    if (b_chroma)
    {
        assert(!b_alpha);

        /* chromakey values */
        const unsigned int min = 0x0; /* ? */
        const unsigned int max = 0x0000ff00; /* ? */
        const unsigned int mask = (region->p_picture->format.i_rmask << 24) |
                                  (region->p_picture->format.i_gmask << 16) |
                                  (region->p_picture->format.i_bmask <<  8) | 0;

        status = vaSetSubpictureChromakey(vaconn->p_display, vasub_cache->i_id,
                                          min, max, mask);
        if (status != VA_STATUS_SUCCESS)
            msg_Err(obj, "failed applying chroma valuesto subpicture");
    }

    vasub_cache->rect.i_x = region->i_x;
    vasub_cache->rect.i_y = region->i_y;
    vasub_cache->rect.i_width = region->fmt.i_visible_width;
    vasub_cache->rect.i_height = region->fmt.i_visible_height;

    vaconn->unlock();
    vlc_restorecancel(canc);
    return vasub_cache;

cleanup:
    vaDestroyImage(vaconn->p_display, image->image_id);
    vasub_cache->i_id = VA_INVALID_ID;
    image->image_id = VA_INVALID_ID;
    image->buf = VA_INVALID_ID;
    subpicture_cache_destroy(vasub_cache);
    vasub_cache = NULL;

error:
    vaconn->unlock();
    vlc_restorecancel(canc);
    return NULL;
}

/* Destroy subpicture cache for this subpicture and
 * release allocated VA API resources. Call this
 * function when holding cache->lock */
void cache_SubpictureDestroy(vlc_object_t *obj, vlc_va_conn_t *vaconn, subpicture_cache_t *cache, const bool b_force)
{
    VLC_UNUSED(obj);

#ifdef VAAPI_DEBUG
    msg_Dbg(obj, "cache_SubpictureDestroy cache %d", cache->i_id);
#endif
    int canc = vlc_savecancel();

    vaconn->lock();

    const int num = vlc_array_count(&cache->subpictures);
    for (int i = num - 1; i >= 0; i--)
    {
        vasubpicture_cache_t *subpic;
        subpic = (vasubpicture_cache_t *)vlc_array_item_at_index(&cache->subpictures, i);
        if (!subpic)
            continue;

        vlc_array_remove(&cache->subpictures, i);
#ifdef VAAPI_DEBUG
        msg_Dbg(obj, "- %d: sub_id %d, image_id %d",
                 i, subpic->i_id, subpic->image.image_id);
#endif
        assert(subpic->i_id != VA_INVALID_ID);
        assert(subpic->image.image_id != VA_INVALID_ID);

        vaDestroySubpicture(vaconn->p_display, subpic->i_id);
        vaDestroyImage(vaconn->p_display, subpic->image.image_id);

        subpic->i_id = VA_INVALID_ID;
        subpic->image.buf = VA_INVALID_ID;
        subpic->image.image_id = VA_INVALID_ID;

        subpicture_cache_destroy(subpic);
        subpic = NULL;
    }
    if (b_force)
        cache->i_refcount = 0;
    cache_destroy(cache);
    cache = NULL;

    vaconn->unlock();

    vlc_restorecancel(canc);
}

int SubpictureRegionsLink(vlc_object_t *obj, vlc_va_conn_t *vaconn, vlc_array_t *cache_array,
                          const picture_t *picture, const int flags)
{
    assert(cache_array);
    assert(picture->p_sys);

    picture_sys_t *surface = picture->p_sys;
    assert(surface->i_id != VA_INVALID_SURFACE);

    subpicture_cache_t *cache = (subpicture_cache_t *) cache_find(cache_array, surface->i_cache);
    if (cache == NULL)
        goto error;

    assert(cache->i_id > 0);

    const int count = vlc_array_count(&cache->subpictures);
#ifdef VAAPI_DEBUG
    msg_Dbg(obj, "  link surface %d cache %d refcount %d",
                surface->i_id, cache->i_id, cache->i_refcount);
#endif

    int i_sub = 0;
    for (int i = 0; i < count; i++)
    {
        vasubpicture_cache_t *vasubpic;
        vasubpic = (vasubpicture_cache_t *) vlc_array_item_at_index(&cache->subpictures, i);
        if (!vasubpic)
            goto cleanup;

#ifdef VAAPI_DEBUG
        msg_Dbg(obj, "+ %d: subid %d", i_sub, vasubpic->i_id);
#endif
        /* NOTE: The video output core renders subpictures to picture size.
         * in case of VAAPI SURFACES this is always the original picture size.
         * Subpictures must be placed on the VAAPI SURFACE and vaPutSurface()
         * does the final scaling.
         */

        /* subpicture positioning */
        unsigned int src_x = 0;
        unsigned int src_w = src_x + vasubpic->rect.i_width;
        unsigned int src_y = 0;
        unsigned int src_h = src_y + vasubpic->rect.i_height;

        vaconn->lock();

        /* Associate subpicture with surface */
        VAStatus status;
        status = vaAssociateSubpicture(vaconn->p_display, vasubpic->i_id,
                                       &surface->i_id, 1, /* array of surfaces */
                                       /* src rectangle */
                                       src_x, src_y, src_w, src_h,
                                       /* dest rectangle */
                                       vasubpic->rect.i_x, vasubpic->rect.i_y,
                                       vasubpic->rect.i_width,
                                       vasubpic->rect.i_height,
                                       flags);

        vaconn->unlock();
        if (status != VA_STATUS_SUCCESS)
            goto cleanup;

        vasubpic = NULL;
        i_sub++;
    }
    return VLC_SUCCESS;

cleanup:
    /* Cleanup linked subpictures */
#ifdef VAAPI_DEBUG
    msg_Err(obj, "unlink surface %d cache %d", surface->i_id, cache->i_id);
#endif
    for (int i = 0; i < i_sub; i++)
    {
        vasubpicture_cache_t *sub = vlc_array_item_at_index(&cache->subpictures, i);
        assert(sub);

        if (sub->i_id != VA_INVALID_ID)
        {
            VAStatus status = VA_STATUS_ERROR_UNKNOWN;
            vaconn->lock();
            status = vaDeassociateSubpicture(vaconn->p_display,
                                             sub->i_id, &surface->i_id, 1);
            vaconn->unlock();
            if (status != VA_STATUS_SUCCESS)
                msg_Err(obj, "failed unlinking subid: %d (error %d)",
                        sub->i_id, status);
        }
    }

error:
    return VLC_EGENERIC;
}

int SubpictureRegionsUnlink(vlc_object_t *obj, vlc_va_conn_t *vaconn,
                            vlc_array_t *cache_array, const picture_t *picture)
{
    assert(cache_array);
    picture_sys_t *surface = picture->p_sys;

    assert(surface);
    assert(surface->i_id != VA_INVALID_SURFACE);
    assert(surface->i_cache > 0);

    subpicture_cache_t *cache = cache_find(cache_array, surface->i_cache);
    if (cache == NULL)
        return VLC_EGENERIC;

    assert(cache->i_id > 0);

#ifdef VAAPI_DEBUG
        msg_Dbg(obj, "unlink surface %d cache %d size %d",
                surface->i_id, surface->i_cache, count);
#endif

    const int count = vlc_array_count(&cache->subpictures);
    for (int i = 0; i < count; i++)
    {
        vasubpicture_cache_t *sub = vlc_array_item_at_index(&cache->subpictures, i);
        assert(sub);
#ifdef VAAPI_DEBUG
        msg_Dbg(vd, "- %u: subid %d", i, sub->i_id);
#endif
        VAStatus status = VA_STATUS_ERROR_UNKNOWN;
        if (sub->i_id != VA_INVALID_ID)
        {
            vaconn->lock();
            status = vaDeassociateSubpicture(vaconn->p_display,
                                             sub->i_id, &surface->i_id, 1);
            vaconn->unlock();
            if (status != VA_STATUS_SUCCESS)
                msg_Err(obj, "failed unlinking subid: %d (error %d)",
                        sub->i_id, status);
        }
    }
    return VLC_SUCCESS;
}

/* NOTE: call with sys->cache_lock held */
void cache_SubpictureRelease(vlc_object_t *obj, vlc_va_conn_t *vaconn, vlc_array_t *cache_array, const int cache_id)
{
    assert(cache_array);

    subpicture_cache_t *cache = cache_find(cache_array, cache_id);
    if (cache)
    {
#ifdef VAAPI_DEBUG
        msg_Info(obj, "Release cache %d count %d refcount %d",
                 cache->i_id, vlc_array_count(cache_array), cache->i_refcount);
#endif
        cache_unref(cache);

        /* Do we still need need this subpicture cache ? */
        if (cache->i_refcount == 0)
        {
#ifdef VAAPI_DEBUG
            msg_Info(obj, "Deleting subpicture cache %d refcount %d",
                     cache->i_id, cache->i_refcount);
#endif
            int index = vlc_array_index_of_item(cache_array, (void*)cache);
            vlc_array_remove(cache_array, index);
            cache_SubpictureDestroy(obj, vaconn, cache, false);
        }
#ifdef VAAPI_DEBUG
        msg_Info(obj, "cache id %d refcount %d", cache_id, cache->i_refcount);
#endif
    }
}

/* NOTE: call with sys->cache_lock held */
void cache_SubpictureHold(vlc_object_t *obj, vlc_array_t *cache_array, const int cache_id)
{
    VLC_UNUSED(obj);

    subpicture_cache_t *cache = cache_find(cache_array, cache_id);
    if (cache)
    {
#ifdef VAAPI_DEBUG
        msg_Info(obj, "Subpicture cache %d refcount %d",
                       cache->i_id, cache->i_refcount);
#endif
        cache_ref(cache);
    }
}

subpicture_cache_t *RenderDirectSubpicture(vlc_object_t *obj, vlc_va_conn_t *vaconn, VAImageFormat *fmt,
                                           picture_t *picture, subpicture_t *subpicture, int flags)
{
    picture_sys_t *surface = picture->p_sys;

    subpicture_cache_t *cache = cache_new();
    if (!cache)
        return NULL;

    cache->i_start = subpicture->i_start;
    cache->i_stop  = subpicture->i_stop;

    int i_sub = 0;
    subpicture_region_t *region;
    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)
            continue;

        assert(region->p_picture->format.i_width > 0);
        assert(region->p_picture->format.i_height > 0);

        /* Create Subpicture */
        vasubpicture_cache_t *vasub_cache;
        vasub_cache = cache_SubpictureCreate(obj, vaconn, fmt, subpicture, region, flags);
        if (vasub_cache == NULL)
            goto cleanup;

#ifdef VAAPI_DEBUG
        msg_Dbg(obj, "+ %d: subid %d", i_sub, vasub_cache->i_id);
#endif

        vlc_array_append(&cache->subpictures, (void *)vasub_cache);
        vasub_cache = NULL;

        i_sub++;
    }

    /* remember what was created, so it can be released in DisplayPicture() */
    cache_ref(cache);
    surface->i_cache = cache->i_id;
    return cache;

cleanup:
    msg_Err(obj, "failed creating subpicture cache %d for surface %d",
            cache->i_id, surface->i_id);
    cache_SubpictureDestroy(obj, vaconn, cache, false);
    return NULL;
}

int RenderCachedSubpictures(vlc_object_t *obj, vlc_va_conn_t *vaconn, vlc_array_t *cache_array, const int cache_id,
                            picture_t *picture, subpicture_t *subpicture)
{
    VLC_UNUSED(vaconn);

    assert(picture);
    assert(subpicture);

    picture_sys_t *surface = picture->p_sys;

    subpicture_cache_t *cache = (subpicture_cache_t *) cache_find(cache_array, cache_id);
    if (cache == NULL)
        goto cleanup;

    assert(cache->i_id > 0);
#if 0
    if ((cache->i_start != subpicture->i_start) &&
        (cache->i_stop  != subpicture->i_stop))
        goto cleanup;
#endif
    /* Check if all subpictures region pictures match */
    const int count = vlc_array_count(&cache->subpictures);
    int i_sub = 0;
    subpicture_region_t *region;
    for (region = subpicture->p_region; region != NULL; region = region->p_next)
    {
        /* Skip non-RGBA regions */
        if (region->fmt.i_chroma != VLC_CODEC_RGBA)
            continue;

        /* Ooh: big booboo */
        if (i_sub >= count)
            abort();

        vasubpicture_cache_t *vasubpic;
        vasubpic = (vasubpicture_cache_t *) vlc_array_item_at_index(&cache->subpictures, i_sub);
        if (!vasubpic)
            goto cleanup;

        if ((vasubpic->image.width  != region->fmt.i_width) ||
            (vasubpic->image.height != region->fmt.i_height))
            goto cleanup;

#ifdef VAAPI_DEBUG
        msg_Dbg(obj, "+ %d: subid %d", i_sub, vasubpic->i_id);
#endif
        vasubpic = NULL;
        i_sub++;
    }

    /* FIXME: should not happen */
    if (i_sub < count)
        goto cleanup;

    picture->p_sys->i_cache = cache->i_id;
    cache_ref(cache);
    return VLC_SUCCESS;

cleanup:
    /* Cannot reuse this cached subpicture */
    msg_Warn(obj, "cannot reuse cached subpicture %d for surface %d -- resetting",
             cache->i_id, surface->i_id);
    return VLC_EGENERIC;
}

#endif
