void test()
{
    VAEntrypoint entrypoints[5];
    int num_entrypoints,slice_entrypoint;
    VAConfigAttrib attrib[2];
    VAConfigID config_id;
    unsigned int fourcc, luma_stride, chroma_u_stride, chroma_v_stride, luma_offset, chroma_u_offset;
    unsigned int chroma_v_offset, buffer_name;
    unsigned int *p_buffer;
    int i, ret;
    char c;
    int frame_width=640,  frame_height=480;
    VASurfaceID surface_id;
    VAImage image_id;
    unsigned char *usrbuf;

    usrbuf = (unsigned char*)malloc(frame_width * frame_height * 2);
    ASSERT ( usrbuf != NULL);

    VASurfaceAttribExternalBuffers  vaSurfaceExternBuf;
    VASurfaceAttrib attrib_list[2];

    va_status = vaQueryConfigEntrypoints(va_dpy, VAProfileH264Baseline, entrypoints, &num_entrypoints);
    ASSERT( VA_STATUS_SUCCESS == va_status );

    for	(slice_entrypoint = 0; slice_entrypoint < num_entrypoints; slice_entrypoint++) {
        if (entrypoints[slice_entrypoint] == VAEntrypointEncSlice)
            break;
    }
    if (slice_entrypoint == num_entrypoints) {
        /* not find Slice entry point */
        status("VAEntrypointEncSlice doesn't support, exit.\n");
        ASSERT(0);
    }

    /* find out the format for the render target, and rate control mode */
    attrib[0].type = VAConfigAttribRTFormat;
    attrib[1].type = VAConfigAttribRateControl;
    va_status = vaGetConfigAttributes(va_dpy, VAProfileH264Baseline, VAEntrypointEncSlice, &attrib[0], 2);
    ASSERT( VA_STATUS_SUCCESS == va_status );

    if ((attrib[0].value & VA_RT_FORMAT_YUV420) == 0) {
        /* not find desired YUV420 RT format */
        status("VA_RT_FORMAT_YUV420 doesn't support, exit\n");
        ASSERT(0);
    }
    if ((attrib[1].value & VA_RC_VBR) == 0) {
        /* Can't find matched RC mode */
        status("VBR mode doesn't found, exit\n");
        ASSERT(0);
    }

    attrib[0].value = VA_RT_FORMAT_YUV420; /* set to desired RT format */
    attrib[1].value = VA_RC_VBR; /* set to desired RC mode */

    va_status = vaCreateConfig(va_dpy, VAProfileH264Baseline, VAEntrypointEncSlice, &attrib[0], 2, &config_id);
    ASSERT( VA_STATUS_SUCCESS == va_status );

    attrib_list[1].type = (VASurfaceAttribType)VASurfaceAttribExternalBufferDescriptor;
    attrib_list[0].type = (VASurfaceAttribType)VASurfaceAttribMemoryType;
    va_status = vaGetSurfaceAttributes(va_dpy, config_id, attrib_list, 2);
    ASSERT( VA_STATUS_SUCCESS == va_status );

    if (attrib_list[0].flags != VA_SURFACE_ATTRIB_NOT_SUPPORTED) {
        status("supported memory type:\n");
        if (attrib_list[0].value.value.i & VA_SURFACE_ATTRIB_MEM_TYPE_VA)
            status("\tVA_SURFACE_ATTRIB_MEM_TYPE_VA\n");
        if (attrib_list[0].value.value.i & VA_SURFACE_ATTRIB_MEM_TYPE_V4L2)
            status("\tVA_SURFACE_ATTRIB_MEM_TYPE_V4L2\n");
        if (attrib_list[0].value.value.i & VA_SURFACE_ATTRIB_MEM_TYPE_USER_PTR)
            status("\tVA_SURFACE_ATTRIB_MEM_TYPE_USER_PTR\n");
        if (attrib_list[0].value.value.i & VA_SURFACE_ATTRIB_MEM_TYPE_ANDROID_GRALLOC)
           status("\tVA_SURFACE_ATTRIB_MEM_TYPE_ANDROID_GRALLOC\n");
        if (attrib_list[0].value.value.i &  VA_SURFACE_ATTRIB_MEM_TYPE_ANDROID_ION)
            status("\tVA_SURFACE_ATTRIB_MEM_TYPE_ANDROID_ION\n");
        if (attrib_list[0].value.value.i & VA_SURFACE_ATTRIB_MEM_TYPE_KERNEL_DRM)
            status("\tVA_SURFACE_ATTRIB_MEM_TYPE_KERNEL_DRM\n");
    }

    if ((attrib_list[1].flags != VA_SURFACE_ATTRIB_NOT_SUPPORTED) &&
            (attrib_list[0].value.value.i & VA_SURFACE_ATTRIB_MEM_TYPE_USER_PTR)) {
        status("vaCreateSurfaces from external usr pointer\n");

        vaSurfaceExternBuf.buffers = (unsigned long*)malloc(sizeof(unsigned int));
        vaSurfaceExternBuf.buffers[0] = usrbuf;
        vaSurfaceExternBuf.num_buffers = 1;
        vaSurfaceExternBuf.width = frame_width;
        vaSurfaceExternBuf.height = frame_height;
        vaSurfaceExternBuf.pitches[0] = vaSurfaceExternBuf.pitches[1] = vaSurfaceExternBuf.pitches[2] = frame_width;
        //vaSurfaceExternBuf.flags = VA_SURFACE_ATTRIB_MEM_TYPE_ANDROID_GRALLOC;
        vaSurfaceExternBuf.pixel_format = VA_FOURCC_NV12;
        //vaSurfaceExternBuf.pitches[0] = attribute_tpi->luma_stride;

        attrib_list[1].flags = VA_SURFACE_ATTRIB_SETTABLE;
        attrib_list[1].value.type = VAGenericValueTypePointer;
        attrib_list[1].value.value.p = (void *)&vaSurfaceExternBuf;

        attrib_list[0].flags = VA_SURFACE_ATTRIB_SETTABLE;
        attrib_list[0].value.type = VAGenericValueTypeInteger;
        attrib_list[0].value.value.i = VA_SURFACE_ATTRIB_MEM_TYPE_USER_PTR;

        va_status = vaCreateSurfaces(va_dpy, VA_RT_FORMAT_YUV420, frame_width, frame_height, &surface_id, 1, attrib_list, 2);
        ASSERT( VA_STATUS_SUCCESS == va_status );

        va_status = vaDeriveImage(va_dpy, surface_id, &image_id);
        ASSERT( VA_STATUS_SUCCESS == va_status );

        va_status = vaMapBuffer(va_dpy, image_id.buf, (void**)&p_buffer);
        ASSERT( VA_STATUS_SUCCESS == va_status );

        memset(p_buffer, 0x80, image_id.width * image_id.height);

        va_status = vaUnmapBuffer(va_dpy, image_id.buf);
        ASSERT( VA_STATUS_SUCCESS == va_status );

        va_status = vaDestroyImage(va_dpy, image_id.image_id);
        ASSERT( VA_STATUS_SUCCESS == va_status );

        va_status = vaDestroySurfaces(va_dpy, &surface_id, 1);
        ASSERT( VA_STATUS_SUCCESS == va_status );

    }

    va_status = vaDestroyConfig(va_dpy, config_id);
    ASSERT( VA_STATUS_SUCCESS == va_status );

    free(usrbuf);

}
/**
 * gst_vaapi_surface_derive_image:
 * @surface: a #GstVaapiSurface
 *
 * Derives a #GstVaapiImage from the @surface. This image buffer can
 * then be mapped/unmapped for direct CPU access. This operation is
 * only possible if the underlying implementation supports direct
 * rendering capabilities and internal surface formats that can be
 * represented with a #GstVaapiImage.
 *
 * When the operation is not possible, the function returns %NULL and
 * the user should then fallback to using gst_vaapi_surface_get_image()
 * or gst_vaapi_surface_put_image() to accomplish the same task in an
 * indirect manner (additional copy).
 *
 * An image created with gst_vaapi_surface_derive_image() should be
 * unreferenced when it's no longer needed. The image and image buffer
 * data structures will be destroyed. However, the surface contents
 * will remain unchanged until destroyed through the last call to
 * gst_vaapi_object_unref().
 *
 * Return value: the newly allocated #GstVaapiImage object, or %NULL
 *   on failure
 */
GstVaapiImage *
gst_vaapi_surface_derive_image (GstVaapiSurface * surface)
{
  GstVaapiDisplay *display;
  VAImage va_image;
  VAStatus status;
  GstVaapiImage *image;

  g_return_val_if_fail (surface != NULL, NULL);

  display = GST_VAAPI_OBJECT_DISPLAY (surface);
  va_image.image_id = VA_INVALID_ID;
  va_image.buf = VA_INVALID_ID;

  GST_VAAPI_DISPLAY_LOCK (display);
  status = vaDeriveImage (GST_VAAPI_DISPLAY_VADISPLAY (display),
      GST_VAAPI_OBJECT_ID (surface), &va_image);
  GST_VAAPI_DISPLAY_UNLOCK (display);
  if (!vaapi_check_status (status, "vaDeriveImage()"))
    return NULL;
  if (va_image.image_id == VA_INVALID_ID || va_image.buf == VA_INVALID_ID)
    return NULL;

  image = gst_vaapi_image_new_with_image (display, &va_image);
  if (!image)
    vaDestroyImage (GST_VAAPI_DISPLAY_VADISPLAY (display), va_image.image_id);
  return image;
}
Пример #3
0
static void put_updated_rectangle(rfbClient *client, int x, int y, int width, int height, int f_width, int f_height, int first_for_frame)
{
    if (curr_surface == VA_INVALID_ID) {
        rfbClientErr("%s: called, but current surface is invalid\n", __FUNCTION__);
        return;
    }

    VAStatus va_status;

    if (client->outputWindow) {
        /* use efficient vaPutSurface() method of putting the framebuffer on the screen */
        if (first_for_frame) {
            /* vaPutSurface() clears window contents outside the given destination rectangle => always update full screen. */
            va_status = vaPutSurface(va_dpy, curr_surface, client->outputWindow, 0, 0, f_width, f_height, 0, 0, f_width, f_height, NULL, 0, VA_FRAME_PICTURE);
            CHECK_VASTATUS(va_status, "vaPutSurface");
        }
    }
    else if (client->frameBuffer) {
        /* ... or copy the changed framebuffer region manually as a fallback */
        VAImage decoded_image;
        decoded_image.image_id = VA_INVALID_ID;
        decoded_image.buf      = VA_INVALID_ID;
        va_status = vaDeriveImage(va_dpy, curr_surface, &decoded_image);
        CHECK_VASTATUS(va_status, "vaDeriveImage");

        if ((decoded_image.image_id == VA_INVALID_ID) || (decoded_image.buf == VA_INVALID_ID)) {
            rfbClientErr("%s: vaDeriveImage() returned success but VA image is invalid (id: %d, buf: %d)\n", __FUNCTION__, decoded_image.image_id, decoded_image.buf);
        }

        nv12_to_rgba(decoded_image, client, x, y, width, height);

        va_status = vaDestroyImage(va_dpy, decoded_image.image_id);
        CHECK_VASTATUS(va_status, "vaDestroyImage");
    }
}
Пример #4
0
ImagePtr VaapiImage::derive(const SurfacePtr& surface)
{
    ImagePtr image;
    if (!surface)
        return image;

    DisplayPtr display = surface->getDisplay();
    VAImagePtr vaImage(new VAImage);

    VAStatus status = vaDeriveImage(display->getID(), surface->getID(), vaImage.get());
    if (!checkVaapiStatus(status, "vaDeriveImage()")) {
        return image;
    }
    image.reset(new VaapiImage(display, surface, vaImage));
    return image;

}
Пример #5
0
VaapiImage *VaapiSurface::getDerivedImage()
{
    VAImage va_image;
    VAStatus status;

    if (m_derivedImage)
        return m_derivedImage;

    va_image.image_id = VA_INVALID_ID;
    va_image.buf = VA_INVALID_ID;

    status = vaDeriveImage(m_display->getID(), m_ID, &va_image);
    if (!checkVaapiStatus(status, "vaDeriveImage()"))
        return NULL;

    m_derivedImage = new VaapiImage(m_display->getID(), &va_image);

    return m_derivedImage;
}
Пример #6
0
mfxStatus vaapiFrameAllocator::LockFrame(mfxMemId mid, mfxFrameData *ptr)
{
    mfxStatus mfx_res = MFX_ERR_NONE;
    VAStatus  va_res  = VA_STATUS_SUCCESS;
    vaapiMemId* vaapi_mid = (vaapiMemId*)mid;
    mfxU8* pBuffer = 0;

    if (!vaapi_mid || !(vaapi_mid->m_surface)) return MFX_ERR_INVALID_HANDLE;

    if (MFX_FOURCC_P8 == vaapi_mid->m_fourcc)   // bitstream processing
    {
        VACodedBufferSegment *coded_buffer_segment;
        va_res =  vaMapBuffer(m_dpy, *(vaapi_mid->m_surface), (void **)(&coded_buffer_segment));
        mfx_res = va_to_mfx_status(va_res);
        ptr->Y = (mfxU8*)coded_buffer_segment->buf; // !!! bug
    }
    else   // Image processing
    {
        va_res = vaDeriveImage(m_dpy, *(vaapi_mid->m_surface), &(vaapi_mid->m_image));
        mfx_res = va_to_mfx_status(va_res);

        if (MFX_ERR_NONE == mfx_res)
        {
            va_res = vaMapBuffer(m_dpy, vaapi_mid->m_image.buf, (void **) &pBuffer);
            mfx_res = va_to_mfx_status(va_res);
        }
        if (MFX_ERR_NONE == mfx_res)
        {
            switch (vaapi_mid->m_image.format.fourcc)
            {
            case VA_FOURCC_NV12:
                if (vaapi_mid->m_fourcc == MFX_FOURCC_NV12)
                {
                    ptr->Pitch = (mfxU16)vaapi_mid->m_image.pitches[0];
                    ptr->Y = pBuffer + vaapi_mid->m_image.offsets[0];
                    ptr->U = pBuffer + vaapi_mid->m_image.offsets[1];
                    ptr->V = ptr->U + 1;
                }
                else mfx_res = MFX_ERR_LOCK_MEMORY;
                break;
            case VA_FOURCC_YV12:
                if (vaapi_mid->m_fourcc == MFX_FOURCC_YV12)
                {
                    ptr->Pitch = (mfxU16)vaapi_mid->m_image.pitches[0];
                    ptr->Y = pBuffer + vaapi_mid->m_image.offsets[0];
                    ptr->V = pBuffer + vaapi_mid->m_image.offsets[1];
                    ptr->U = pBuffer + vaapi_mid->m_image.offsets[2];
                }
                else mfx_res = MFX_ERR_LOCK_MEMORY;
                break;
            case VA_FOURCC_YUY2:
                if (vaapi_mid->m_fourcc == MFX_FOURCC_YUY2)
                {
                    ptr->Pitch = (mfxU16)vaapi_mid->m_image.pitches[0];
                    ptr->Y = pBuffer + vaapi_mid->m_image.offsets[0];
                    ptr->U = ptr->Y + 1;
                    ptr->V = ptr->Y + 3;
                }
                else mfx_res = MFX_ERR_LOCK_MEMORY;
                break;
            case VA_FOURCC_ARGB:
                if (vaapi_mid->m_fourcc == MFX_FOURCC_RGB4)
                {
                    ptr->Pitch = (mfxU16)vaapi_mid->m_image.pitches[0];
                    ptr->B = pBuffer + vaapi_mid->m_image.offsets[0];
                    ptr->G = ptr->B + 1;
                    ptr->R = ptr->B + 2;
                    ptr->A = ptr->B + 3;
                }
                else mfx_res = MFX_ERR_LOCK_MEMORY;
                break;
            default:
                mfx_res = MFX_ERR_LOCK_MEMORY;
                break;
            }
        }
    }
    return mfx_res;
}
Пример #7
0
bool VAAPIContext::CopySurfaceToFrame(VideoFrame *frame, const void *buf)
{
    MythXLocker locker(m_display->m_x_disp);

    if (!m_deriveSupport && m_image.image_id == VA_INVALID_ID)
        InitImage(buf);

    if (!frame || !buf || (m_dispType != kVADisplayX11) ||
        (!m_deriveSupport && m_image.image_id == VA_INVALID_ID))
        return false;

    const vaapi_surface *surf = (vaapi_surface*)buf;

    INIT_ST;
    va_status = vaSyncSurface(m_ctx.display, surf->m_id);
    CHECK_ST;

    if (m_deriveSupport)
    {
        va_status = vaDeriveImage(m_ctx.display, surf->m_id, &m_image);
    }
    else
    {
        va_status = vaGetImage(m_ctx.display, surf->m_id, 0, 0,
                               m_size.width(), m_size.height(),
                               m_image.image_id);
    }
    CHECK_ST;

    if (ok)
    {
        VideoFrame src;
        void* source = NULL;

        if (vaMapBuffer(m_ctx.display, m_image.buf, &source))
            return false;

        if (m_image.format.fourcc == VA_FOURCC_NV12)
        {
            init(&src, FMT_NV12, (unsigned char*)source, m_image.width,
                 m_image.height, m_image.data_size, NULL,
                 NULL, frame->aspect, frame->frame_rate);
            for (int i = 0; i < 2; i++)
            {
                src.pitches[i] = m_image.pitches[i];
                src.offsets[i] = m_image.offsets[i];
            }
        }
        else
        {
            // Our VideoFrame YV12 format, is really YUV420P/IYUV
            bool swap = m_image.format.fourcc == VA_FOURCC_YV12;
            init(&src, FMT_YV12, (unsigned char*)source, m_image.width,
                 m_image.height, m_image.data_size, NULL,
                 NULL, frame->aspect, frame->frame_rate);
            src.pitches[0] = m_image.pitches[0];
            src.pitches[1] = m_image.pitches[swap ? 2 : 1];
            src.pitches[2] = m_image.pitches[swap ? 1 : 2];
            src.offsets[0] = m_image.offsets[0];
            src.offsets[1] = m_image.offsets[swap ? 2 : 1];
            src.offsets[2] = m_image.offsets[swap ? 1 : 2];
        }
        m_copy->copy(frame, &src);

        if (vaUnmapBuffer(m_ctx.display, m_image.buf))
            return false;

        if (m_deriveSupport)
        {
            vaDestroyImage(m_ctx.display, m_image.image_id );
            m_image.image_id = VA_INVALID_ID;
        }
        return true;
    }

    LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to get image");
    return false;
}
Пример #8
0
bool VAAPIContext::InitImage(const void *buf)
{
    if (!buf)
        return false;
    if (!m_dispType == kVADisplayX11)
        return true;

    int num_formats = 0;
    int max_formats = vaMaxNumImageFormats(m_ctx.display);
    VAImageFormat *formats = new VAImageFormat[max_formats];

    INIT_ST;
    va_status = vaQueryImageFormats(m_ctx.display, formats, &num_formats);
    CHECK_ST;

    const vaapi_surface *surf = (vaapi_surface*)buf;
    unsigned int deriveImageFormat = 0;

    if (vaDeriveImage(m_ctx.display, surf->m_id, &m_image) == VA_STATUS_SUCCESS)
    {
        m_deriveSupport = true;
        deriveImageFormat = m_image.format.fourcc;
        vaDestroyImage(m_ctx.display, m_image.image_id);
    }

    int nv12support = -1;

    for (int i = 0; i < num_formats; i++)
    {
        if (formats[i].fourcc == VA_FOURCC_YV12 ||
            formats[i].fourcc == VA_FOURCC_IYUV ||
            formats[i].fourcc == VA_FOURCC_NV12)
        {
            if (vaCreateImage(m_ctx.display, &formats[i],
                              m_size.width(), m_size.height(), &m_image))
            {
                m_image.image_id = VA_INVALID_ID;
                continue;
            }

            if (vaGetImage(m_ctx.display, surf->m_id, 0, 0,
                           m_size.width(), m_size.height(), m_image.image_id))
            {
                vaDestroyImage(m_ctx.display, m_image.image_id);
                m_image.image_id = VA_INVALID_ID;
                continue;
            }

            if (formats[i].fourcc == VA_FOURCC_NV12)
            {
                // mark as NV12 as supported, but favor other formats first
                nv12support = i;
                vaDestroyImage(m_ctx.display, m_image.image_id);
                m_image.image_id = VA_INVALID_ID;
                continue;
            }
            break;
        }
    }

    if (m_image.image_id == VA_INVALID_ID && nv12support >= 0)
    {
        // only nv12 is supported, use that format
        if (vaCreateImage(m_ctx.display, &formats[nv12support],
                          m_size.width(), m_size.height(), &m_image))
        {
            m_image.image_id = VA_INVALID_ID;
        }
    }
    else if (m_deriveSupport && deriveImageFormat != m_image.format.fourcc)
    {
        // only use vaDerive if it's giving us a format we can handle natively
        m_deriveSupport = false;
    }

    delete [] formats;

    if (m_image.image_id == VA_INVALID_ID)
    {
        LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to create software image.");
        return false;
    }

    LOG(VB_GENERAL, LOG_DEBUG,
        LOC + QString("InitImage: id %1, width %2 height %3 "
                      "format %4 vaDeriveSupport:%5")
        .arg(m_image.image_id).arg(m_image.width).arg(m_image.height)
        .arg(m_image.format.fourcc).arg(m_deriveSupport));

    if (m_deriveSupport)
    {
        vaDestroyImage(m_ctx.display, m_image.image_id );
        m_image.image_id = VA_INVALID_ID;
    }

    return true;
}
Пример #9
0
static int map_image(struct gl_hwdec *hw, struct mp_image *hw_image,
                     GLuint *out_textures)
{
    struct priv *p = hw->priv;
    GL *gl = hw->gl;
    VAStatus status;
    VAImage *va_image = &p->current_image;

    unref_image(hw);

    mp_image_setrefp(&p->current_ref, hw_image);

    va_lock(p->ctx);

    status = vaDeriveImage(p->display, va_surface_id(hw_image), va_image);
    if (!CHECK_VA_STATUS(p, "vaDeriveImage()"))
        goto err;

    int mpfmt = va_fourcc_to_imgfmt(va_image->format.fourcc);
    if (mpfmt != IMGFMT_NV12 && mpfmt != IMGFMT_420P) {
        MP_FATAL(p, "unsupported VA image format %s\n",
                 VA_STR_FOURCC(va_image->format.fourcc));
        goto err;
    }

    if (!hw->converted_imgfmt) {
        MP_VERBOSE(p, "format: %s %s\n", VA_STR_FOURCC(va_image->format.fourcc),
                   mp_imgfmt_to_name(mpfmt));
        hw->converted_imgfmt = mpfmt;
    }

    if (hw->converted_imgfmt != mpfmt) {
        MP_FATAL(p, "mid-stream hwdec format change (%s -> %s) not supported\n",
                 mp_imgfmt_to_name(hw->converted_imgfmt), mp_imgfmt_to_name(mpfmt));
        goto err;
    }

    VABufferInfo buffer_info = {.mem_type = VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME};
    status = vaAcquireBufferHandle(p->display, va_image->buf, &buffer_info);
    if (!CHECK_VA_STATUS(p, "vaAcquireBufferHandle()"))
        goto err;
    p->buffer_acquired = true;

    struct mp_image layout = {0};
    mp_image_set_params(&layout, &hw_image->params);
    mp_image_setfmt(&layout, mpfmt);

    // (it would be nice if we could use EGL_IMAGE_INTERNAL_FORMAT_EXT)
    int drm_fmts[4] = {MP_FOURCC('R', '8', ' ', ' '),   // DRM_FORMAT_R8
                       MP_FOURCC('G', 'R', '8', '8'),   // DRM_FORMAT_GR88
                       MP_FOURCC('R', 'G', '2', '4'),   // DRM_FORMAT_RGB888
                       MP_FOURCC('R', 'A', '2', '4')};  // DRM_FORMAT_RGBA8888

    for (int n = 0; n < layout.num_planes; n++) {
        int attribs[20] = {EGL_NONE};
        int num_attribs = 0;

        ADD_ATTRIB(EGL_LINUX_DRM_FOURCC_EXT, drm_fmts[layout.fmt.bytes[n] - 1]);
        ADD_ATTRIB(EGL_WIDTH, mp_image_plane_w(&layout, n));
        ADD_ATTRIB(EGL_HEIGHT, mp_image_plane_h(&layout, n));
        ADD_ATTRIB(EGL_DMA_BUF_PLANE0_FD_EXT, buffer_info.handle);
        ADD_ATTRIB(EGL_DMA_BUF_PLANE0_OFFSET_EXT, va_image->offsets[n]);
        ADD_ATTRIB(EGL_DMA_BUF_PLANE0_PITCH_EXT, va_image->pitches[n]);

        p->images[n] = p->CreateImageKHR(eglGetCurrentDisplay(),
            EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, NULL, attribs);
        if (!p->images[n])
            goto err;

        gl->BindTexture(GL_TEXTURE_2D, p->gl_textures[n]);
        p->EGLImageTargetTexture2DOES(GL_TEXTURE_2D, p->images[n]);

        out_textures[n] = p->gl_textures[n];
    }
    gl->BindTexture(GL_TEXTURE_2D, 0);

    if (va_image->format.fourcc == VA_FOURCC_YV12)
        MPSWAP(GLuint, out_textures[1], out_textures[2]);

    va_unlock(p->ctx);
    return 0;

err:
    va_unlock(p->ctx);
    MP_FATAL(p, "mapping VAAPI EGL image failed\n");
    unref_image(hw);
    return -1;
}
Пример #10
0
VideoFrame VideoDecoderVAAPI::frame()
{
    DPTR_D(VideoDecoderVAAPI);
    if (!d.frame->opaque || !d.frame->data[0])
        return VideoFrame();
    VASurfaceID surface_id = (VASurfaceID)(uintptr_t)d.frame->data[3];
    VAStatus status = VA_STATUS_SUCCESS;
    if (display() == GLX || (copyMode() == ZeroCopy && display() == X11)) {
        surface_ptr p;
        std::list<surface_ptr>::iterator it = d.surfaces_used.begin();
        for (; it != d.surfaces_used.end() && !p; ++it) {
            if((*it)->get() == surface_id) {
                p = *it;
                break;
            }
        }
        if (!p) {
            for (it = d.surfaces_free.begin(); it != d.surfaces_free.end() && !p; ++it) {
                if((*it)->get() == surface_id) {
                    p = *it;
                    break;
                }
            }
        }
        if (!p) {
            qWarning("VAAPI - Unable to find surface");
            return VideoFrame();
        }
        ((SurfaceInteropVAAPI*)d.surface_interop.data())->setSurface(p);

        VideoFrame f(d.width, d.height, VideoFormat::Format_RGB32); //p->width()
        f.setBytesPerLine(d.width*4); //used by gl to compute texture size
        f.setMetaData("surface_interop", QVariant::fromValue(d.surface_interop));
        f.setTimestamp(double(d.frame->pkt_pts)/1000.0);
        return f;
    }
#if VA_CHECK_VERSION(0,31,0)
    if ((status = vaSyncSurface(d.display->get(), surface_id)) != VA_STATUS_SUCCESS) {
        qWarning("vaSyncSurface(VADisplay:%p, VASurfaceID:%#x) == %#x", d.display->get(), surface_id, status);
#else
    if (vaSyncSurface(d.display->get(), d.context_id, surface_id)) {
        qWarning("vaSyncSurface(VADisplay:%#x, VAContextID:%#x, VASurfaceID:%#x) == %#x", d.display, d.context_id, surface_id, status);
#endif
        return VideoFrame();
    }

    if (!d.disable_derive && d.supports_derive) {
        /*
         * http://web.archiveorange.com/archive/v/OAywENyq88L319OcRnHI
         * vaDeriveImage is faster than vaGetImage. But VAImage is uncached memory and copying from it would be terribly slow
         * TODO: copy from USWC, see vlc and https://github.com/OpenELEC/OpenELEC.tv/pull/2937.diff
         * https://software.intel.com/en-us/articles/increasing-memory-throughput-with-intel-streaming-simd-extensions-4-intel-sse4-streaming-load
         */
        VA_ENSURE_TRUE(vaDeriveImage(d.display->get(), surface_id, &d.image), VideoFrame());
    } else {
        VA_ENSURE_TRUE(vaGetImage(d.display->get(), surface_id, 0, 0, d.width, d.height, d.image.image_id), VideoFrame());
    }

    void *p_base;
    VA_ENSURE_TRUE(vaMapBuffer(d.display->get(), d.image.buf, &p_base), VideoFrame());

    VideoFormat::PixelFormat pixfmt = VideoFormat::Format_Invalid;
    bool swap_uv = false;
    switch (d.image.format.fourcc) {
    case VA_FOURCC_YV12:
        swap_uv |= d.disable_derive || !d.supports_derive;
        pixfmt = VideoFormat::Format_YUV420P;
        break;
    case VA_FOURCC_IYUV:
        swap_uv = true;
        pixfmt = VideoFormat::Format_YUV420P;
        break;
    case VA_FOURCC_NV12:
        pixfmt = VideoFormat::Format_NV12;
        break;
    default:
        break;
    }
    if (pixfmt == VideoFormat::Format_Invalid) {
        qWarning("unsupported vaapi pixel format: %#x", d.image.format.fourcc);
        return VideoFrame();
    }
    const VideoFormat fmt(pixfmt);
    uint8_t *src[3];
    int pitch[3];
    for (int i = 0; i < fmt.planeCount(); ++i) {
        src[i] = (uint8_t*)p_base + d.image.offsets[i];
        pitch[i] = d.image.pitches[i];
    }
    VideoFrame frame(copyToFrame(fmt, d.surface_height, src, pitch, swap_uv));
    VAWARN(vaUnmapBuffer(d.display->get(), d.image.buf));
    if (!d.disable_derive && d.supports_derive) {
        vaDestroyImage(d.display->get(), d.image.image_id);
        d.image.image_id = VA_INVALID_ID;
    }
    return frame;
}

void VideoDecoderVAAPI::setDisplayPriority(const QStringList &priority)
{
    DPTR_D(VideoDecoderVAAPI);
    d.display_priority.clear();
    int idx = staticMetaObject.indexOfEnumerator("DisplayType");
    const QMetaEnum me = staticMetaObject.enumerator(idx);
    foreach (const QString& disp, priority) {
        d.display_priority.push_back((DisplayType)me.keyToValue(disp.toUtf8().constData()));
    }
Пример #11
0
int get_image(VASurfaceID surface, Image *dst_img)
{
    VAAPIContext * const vaapi = vaapi_get_context();
    VAImage image;
    VAImageFormat *image_format = NULL;
    VAStatus status;
    Image bound_image;
    int i, is_bound_image = 0, is_derived_image = 0, error = -1;

    image.image_id = VA_INVALID_ID;
    image.buf      = VA_INVALID_ID;

    if (!image_format) {
        status = vaDeriveImage(vaapi->display, surface, &image);
        if (vaapi_check_status(status, "vaDeriveImage()")) {
            if (image.image_id != VA_INVALID_ID && image.buf != VA_INVALID_ID) {
                D(bug("using vaDeriveImage()\n"));
                is_derived_image = 1;
                image_format = &image.format;
            }
            else {
                D(bug("vaDeriveImage() returned success but VA image is invalid. Trying vaGetImage()\n"));
            }
        }
    }

    if (!image_format) {
        for (i = 0; image_formats[i] != 0; i++) {
            if (get_image_format(vaapi, image_formats[i], &image_format))
                break;
        }
    }

    if (!image_format)
        goto end;
    D(bug("selected %s image format for getimage\n",
          string_of_VAImageFormat(image_format)));

    if (!is_derived_image) {
        status = vaCreateImage(vaapi->display, image_format,
                               vaapi->picture_width, vaapi->picture_height,
                               &image);
        if (!vaapi_check_status(status, "vaCreateImage()"))
            goto end;
        D(bug("created image with id 0x%08x and buffer id 0x%08x\n",
              image.image_id, image.buf));

        VARectangle src_rect;

        src_rect.x      = 0;
        src_rect.y      = 0;
        src_rect.width  = vaapi->picture_width;
        src_rect.height = vaapi->picture_height;

        D(bug("src rect (%d,%d):%ux%u\n",
              src_rect.x, src_rect.y, src_rect.width, src_rect.height));

        status = vaGetImage(
            vaapi->display, vaapi->surface_id,
            src_rect.x, src_rect.y, src_rect.width, src_rect.height,
            image.image_id
        );
        if (!vaapi_check_status(status, "vaGetImage()")) {
            vaDestroyImage(vaapi->display, image.image_id);
            goto end;
        }
    }

    if (bind_image(&image, &bound_image) < 0)
        goto end;
    is_bound_image = 1;

    if (image_convert(dst_img, &bound_image) < 0)
        goto end;

    error = 0;
end:
    if (is_bound_image) {
        if (release_image(&image) < 0)
            error = -1;
    }

    if (image.image_id != VA_INVALID_ID) {
        status = vaDestroyImage(vaapi->display, image.image_id);
        if (!vaapi_check_status(status, "vaDestroyImage()"))
            error = -1;
    }
    return error;
}
Пример #12
0
OMX_ERRORTYPE OMXVideoDecoderVP9HWR::ProcessorInit(void)
{
    unsigned int i = 0;

    for (i = 0; i < MAX_NATIVE_BUFFER_COUNT; i++) {
        extMIDs[i] = (vaapiMemId*)malloc(sizeof(vaapiMemId));
        extMIDs[i]->m_usrAddr = NULL;
        extMIDs[i]->m_surface = new VASurfaceID;
    }

    initDecoder();

    if (RAWDATA_MODE == mWorkingMode) {
        OMX_PARAM_PORTDEFINITIONTYPE paramPortDefinitionInput;

        memcpy(&paramPortDefinitionInput,
               this->ports[INPORT_INDEX]->GetPortDefinition(),
               sizeof(paramPortDefinitionInput));

        extNativeBufferSize = INTERNAL_MAX_FRAME_WIDTH *
                              INTERNAL_MAX_FRAME_HEIGHT * 1.5;
        extActualBufferStride = INTERNAL_MAX_FRAME_WIDTH;
        extActualBufferHeightStride = INTERNAL_MAX_FRAME_HEIGHT;

        for (i = 0; i < OUTPORT_ACTUAL_BUFFER_COUNT; i++ ) {
            extMIDs[i]->m_usrAddr = (unsigned char*)malloc(sizeof(unsigned char) *
                                    extNativeBufferSize);
            extMIDs[i]->m_render_done = true;
            extMIDs[i]->m_released = true;
        }
        extMappedNativeBufferCount = OUTPORT_ACTUAL_BUFFER_COUNT;
        return OMX_ErrorNone;
    }

#ifdef DECODE_WITH_GRALLOC_BUFFER
    if (mOMXBufferHeaderTypePtrNum > MAX_NATIVE_BUFFER_COUNT) {
        LOGE("Actual OMX outport buffer header type num (%d) > MAX_NATIVE_BUFFER_COUNT (%d)",
              mOMXBufferHeaderTypePtrNum, MAX_NATIVE_BUFFER_COUNT);
        return OMX_ErrorOverflow;
    }

    OMX_PARAM_PORTDEFINITIONTYPE paramPortDefinitionInput;

    memcpy(&paramPortDefinitionInput,
        this->ports[INPORT_INDEX]->GetPortDefinition(),
        sizeof(paramPortDefinitionInput));

    int surfaceWidth = mGraphicBufferParam.graphicBufferWidth;
    int surfaceHeight = mGraphicBufferParam.graphicBufferHeight;
    int surfaceStride = mGraphicBufferParam.graphicBufferStride;
    extNativeBufferSize = mGraphicBufferParam.graphicBufferStride *
                          mGraphicBufferParam.graphicBufferHeight * 1.5;
    extActualBufferStride = surfaceStride;
    extActualBufferHeightStride = surfaceHeight;

    for (i = 0; i < mOMXBufferHeaderTypePtrNum; i++) {
        OMX_BUFFERHEADERTYPE *buf_hdr = mOMXBufferHeaderTypePtrArray[i];

        extMIDs[i]->m_key = (unsigned int)(buf_hdr->pBuffer);
        extMIDs[i]->m_render_done = false;
        extMIDs[i]->m_released = true;

        VAStatus va_res;
        unsigned int buffer;
        VASurfaceAttrib attribs[2];
        VASurfaceAttribExternalBuffers* surfExtBuf = new VASurfaceAttribExternalBuffers;
        int32_t format = VA_RT_FORMAT_YUV420;

        surfExtBuf->buffers= (unsigned long *)&buffer;
        surfExtBuf->num_buffers = 1;
        surfExtBuf->pixel_format = VA_FOURCC_NV12;
        surfExtBuf->width = surfaceWidth;
        surfExtBuf->height = surfaceHeight;
        surfExtBuf->data_size = surfaceStride * surfaceHeight * 1.5;
        surfExtBuf->num_planes = 2;
        surfExtBuf->pitches[0] = surfaceStride;
        surfExtBuf->pitches[1] = surfaceStride;
        surfExtBuf->pitches[2] = 0;
        surfExtBuf->pitches[3] = 0;
        surfExtBuf->offsets[0] = 0;
        surfExtBuf->offsets[1] = surfaceStride * surfaceHeight;
        surfExtBuf->offsets[2] = 0;
        surfExtBuf->offsets[3] = 0;
        surfExtBuf->flags = VA_SURFACE_ATTRIB_MEM_TYPE_ANDROID_GRALLOC;

        surfExtBuf->buffers[0] = (unsigned int)buf_hdr->pBuffer;

        attribs[0].type = (VASurfaceAttribType)VASurfaceAttribMemoryType;
        attribs[0].flags = VA_SURFACE_ATTRIB_SETTABLE;
        attribs[0].value.type = VAGenericValueTypeInteger;
        attribs[0].value.value.i = VA_SURFACE_ATTRIB_MEM_TYPE_ANDROID_GRALLOC;

        attribs[1].type = (VASurfaceAttribType)VASurfaceAttribExternalBufferDescriptor;
        attribs[1].flags = VA_SURFACE_ATTRIB_SETTABLE;
        attribs[1].value.type = VAGenericValueTypePointer;
        attribs[1].value.value.p = (void *)surfExtBuf;

        va_res = vaCreateSurfaces(mVADisplay,
                                  format,
                                  surfaceWidth,
                                  surfaceHeight,
                                  extMIDs[i]->m_surface,
                                  1,
                                  attribs,
                                  2);

        if (va_res != VA_STATUS_SUCCESS) {
            LOGE("Failed to create vaSurface!");
            return OMX_ErrorUndefined;
        }

        delete surfExtBuf;

        VAImage image;
        unsigned char* usrptr;

        va_res = vaDeriveImage(mVADisplay, *(extMIDs[i]->m_surface), &image);
        if (VA_STATUS_SUCCESS == va_res) {
            va_res = vaMapBuffer(mVADisplay, image.buf, (void **) &usrptr);
            if (VA_STATUS_SUCCESS == va_res) {
                extMIDs[i]->m_usrAddr = usrptr;
                vaUnmapBuffer(mVADisplay, image.buf);
            }
            vaDestroyImage(mVADisplay, image.image_id);
        }
        extMappedNativeBufferCount++;
    }
    return OMX_ErrorNone;
#endif
}
Пример #13
0
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;
    VAImageFormat *image_format;
    VAAPISurfaceMap *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 & VAAPI_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 & VAAPI_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(VAAPISurfaceMap));
    if (!map)
        return AVERROR(ENOMEM);

    map->source         = src;
    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 &&
        ((flags & VAAPI_MAP_DIRECT) || !(flags & VAAPI_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 |= VAAPI_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 & VAAPI_MAP_READ) {
            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;
    }

    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];
    }
    if (
#ifdef VA_FOURCC_YV16
        map->image.format.fourcc == VA_FOURCC_YV16 ||
#endif
        map->image.format.fourcc == VA_FOURCC_YV12) {
        // Chroma planes are YVU rather than YUV, so swap them.
        FFSWAP(uint8_t*, dst->data[1], dst->data[2]);
    }
Пример #14
0
static int vaapi_frames_init(AVHWFramesContext *hwfc)
{
    AVVAAPIFramesContext  *avfc = hwfc->hwctx;
    VAAPIFramesContext     *ctx = hwfc->internal->priv;
    AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
    VAImageFormat *expected_format;
    AVBufferRef *test_surface = NULL;
    VASurfaceID test_surface_id;
    VAImage test_image;
    VAStatus vas;
    int err, i;
    unsigned int fourcc, rt_format;

    for (i = 0; i < FF_ARRAY_ELEMS(vaapi_format_map); i++) {
        if (vaapi_format_map[i].pix_fmt == hwfc->sw_format) {
            fourcc    = vaapi_format_map[i].fourcc;
            rt_format = vaapi_format_map[i].rt_format;
            break;
        }
    }
    if (i >= FF_ARRAY_ELEMS(vaapi_format_map)) {
        av_log(hwfc, AV_LOG_ERROR, "Unsupported format: %s.\n",
               av_get_pix_fmt_name(hwfc->sw_format));
        return AVERROR(EINVAL);
    }

    if (!hwfc->pool) {
        int need_memory_type = 1, need_pixel_format = 1;
        for (i = 0; i < avfc->nb_attributes; i++) {
            if (ctx->attributes[i].type == VASurfaceAttribMemoryType)
                need_memory_type  = 0;
            if (ctx->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 = fourcc,
            };
        }
        av_assert0(i == ctx->nb_attributes);

        ctx->rt_format = 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);
}
Пример #15
0
static int CreateSurfaces( vlc_va_vaapi_t *p_va, void **pp_hw_ctx, vlc_fourcc_t *pi_chroma,
                           int i_width, int i_height )
{
    assert( i_width > 0 && i_height > 0 );

    /* */
    p_va->p_surface = calloc( p_va->i_surface_count, sizeof(*p_va->p_surface) );
    if( !p_va->p_surface )
        return VLC_EGENERIC;
    p_va->image.image_id = VA_INVALID_ID;
    p_va->i_context_id   = VA_INVALID_ID;

    /* Create surfaces */
    VASurfaceID pi_surface_id[p_va->i_surface_count];
    if( vaCreateSurfaces( p_va->p_display, i_width, i_height, VA_RT_FORMAT_YUV420,
                          p_va->i_surface_count, pi_surface_id ) )
    {
        for( int i = 0; i < p_va->i_surface_count; i++ )
            p_va->p_surface[i].i_id = VA_INVALID_SURFACE;
        goto error;
    }

    for( int i = 0; i < p_va->i_surface_count; i++ )
    {
        vlc_va_surface_t *p_surface = &p_va->p_surface[i];

        p_surface->i_id = pi_surface_id[i];
        p_surface->i_refcount = 0;
        p_surface->i_order = 0;
    }

    /* Create a context */
    if( vaCreateContext( p_va->p_display, p_va->i_config_id,
                         i_width, i_height, VA_PROGRESSIVE,
                         pi_surface_id, p_va->i_surface_count, &p_va->i_context_id ) )
    {
        p_va->i_context_id = VA_INVALID_ID;
        goto error;
    }

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

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

    VAImage testImage;
    if(vaDeriveImage(p_va->p_display, pi_surface_id[0], &testImage) == VA_STATUS_SUCCESS)
    {
        p_va->b_supports_derive = true;
        vaDestroyImage(p_va->p_display, testImage.image_id);
    }

    vlc_fourcc_t  i_chroma = 0;
    VAImageFormat fmt;
    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' ) )
        {
            if( vaCreateImage(  p_va->p_display, &p_fmt[i], i_width, i_height, &p_va->image ) )
            {
                p_va->image.image_id = VA_INVALID_ID;
                continue;
            }
            /* Validate that vaGetImage works with this format */
            if( vaGetImage( p_va->p_display, pi_surface_id[0],
                            0, 0, i_width, i_height,
                            p_va->image.image_id) )
            {
                vaDestroyImage( p_va->p_display, p_va->image.image_id );
                p_va->image.image_id = VA_INVALID_ID;
                continue;
            }

            i_chroma = VLC_CODEC_YV12;
            fmt = p_fmt[i];
            break;
        }
    }
    free( p_fmt );
    if( !i_chroma )
        goto error;
    *pi_chroma = i_chroma;

    if(p_va->b_supports_derive)
    {
        vaDestroyImage( p_va->p_display, p_va->image.image_id );
        p_va->image.image_id = VA_INVALID_ID;
    }

    if( unlikely(CopyInitCache( &p_va->image_cache, i_width )) )
        goto error;

    /* Setup the ffmpeg hardware context */
    *pp_hw_ctx = &p_va->hw_ctx;

    memset( &p_va->hw_ctx, 0, sizeof(p_va->hw_ctx) );
    p_va->hw_ctx.display    = p_va->p_display;
    p_va->hw_ctx.config_id  = p_va->i_config_id;
    p_va->hw_ctx.context_id = p_va->i_context_id;

    /* */
    p_va->i_surface_chroma = i_chroma;
    p_va->i_surface_width = i_width;
    p_va->i_surface_height = i_height;
    return VLC_SUCCESS;

error:
    DestroySurfaces( p_va );
    return VLC_EGENERIC;
}
Пример #16
0
/** Finds a supported image chroma */
static int FindFormat(vlc_va_sys_t *sys)
{
    int count = vaMaxNumImageFormats(sys->hw_ctx.display);

    VAImageFormat *fmts = malloc(count * sizeof (*fmts));
    if (unlikely(fmts == NULL))
        return VLC_ENOMEM;

    if (vaQueryImageFormats(sys->hw_ctx.display, fmts, &count))
    {
        free(fmts);
        return VLC_EGENERIC;
    }

    sys->format.fourcc = 0;

    for (int i = 0; i < count; i++)
    {
        unsigned fourcc = fmts[i].fourcc;

        if (fourcc != VA_FOURCC_YV12 && fourcc != VA_FOURCC_IYUV
         && fourcc != VA_FOURCC_NV12)
            continue;

        VAImage image;

        if (vaCreateImage(sys->hw_ctx.display, &fmts[i], sys->width,
                          sys->height, &image))
            continue;

        /* Validate that vaGetImage works with this format */
        int val = vaGetImage(sys->hw_ctx.display, sys->surfaces[0], 0, 0,
                             sys->width, sys->height, image.image_id);

        vaDestroyImage(sys->hw_ctx.display, image.image_id);

        if (val != VA_STATUS_SUCCESS)
            continue;

        /* Mark NV12 as supported, but favor other formats first */
        sys->format = fmts[i];
        if (fourcc != VA_FOURCC_NV12)
            break;
    }

    free(fmts);

    if (sys->format.fourcc == 0)
        return VLC_EGENERIC; /* None of the formats work */

    VAImage image;

    /* Use vaDerive() iif it supports the best selected format */
    sys->do_derive = false;

    if (vaDeriveImage(sys->hw_ctx.display, sys->surfaces[0],
                      &image) == VA_STATUS_SUCCESS)
    {
        if (image.format.fourcc == sys->format.fourcc)
        {
            sys->do_derive = true;
            sys->format = image.format;
        }
        vaDestroyImage(sys->hw_ctx.display, image.image_id);
    }

    return VLC_SUCCESS;
}
Пример #17
0
static int Extract( vlc_va_t *p_external, picture_t *p_picture, AVFrame *p_ff )
{
    vlc_va_vaapi_t *p_va = vlc_va_vaapi_Get(p_external);

    VASurfaceID i_surface_id = (VASurfaceID)(uintptr_t)p_ff->data[3];

#if VA_CHECK_VERSION(0,31,0)
    if( vaSyncSurface( p_va->p_display, i_surface_id ) )
#else
    if( vaSyncSurface( p_va->p_display, p_va->i_context_id, i_surface_id ) )
#endif
        return VLC_EGENERIC;

    if(p_va->b_supports_derive)
    {
        if(vaDeriveImage(p_va->p_display, i_surface_id, &(p_va->image)) != VA_STATUS_SUCCESS)
            return VLC_EGENERIC;
    }
    else
    {
        if( vaGetImage( p_va->p_display, i_surface_id,
                        0, 0, p_va->i_surface_width, p_va->i_surface_height,
                        p_va->image.image_id) )
            return VLC_EGENERIC;
    }

    void *p_base;
    if( vaMapBuffer( p_va->p_display, p_va->image.buf, &p_base ) )
        return VLC_EGENERIC;

    const uint32_t i_fourcc = p_va->image.format.fourcc;
    if( i_fourcc == VA_FOURCC('Y','V','1','2') ||
        i_fourcc == VA_FOURCC('I','4','2','0') )
    {
        bool b_swap_uv = i_fourcc == VA_FOURCC('I','4','2','0');
        uint8_t *pp_plane[3];
        size_t  pi_pitch[3];

        for( int i = 0; i < 3; i++ )
        {
            const int i_src_plane = (b_swap_uv && i != 0) ?  (3 - i) : i;
            pp_plane[i] = (uint8_t*)p_base + p_va->image.offsets[i_src_plane];
            pi_pitch[i] = p_va->image.pitches[i_src_plane];
        }
        CopyFromYv12( p_picture, pp_plane, pi_pitch,
                      p_va->i_surface_width,
                      p_va->i_surface_height,
                      &p_va->image_cache );
    }
    else
    {
        assert( i_fourcc == VA_FOURCC('N','V','1','2') );
        uint8_t *pp_plane[2];
        size_t  pi_pitch[2];

        for( int i = 0; i < 2; i++ )
        {
            pp_plane[i] = (uint8_t*)p_base + p_va->image.offsets[i];
            pi_pitch[i] = p_va->image.pitches[i];
        }
        CopyFromNv12( p_picture, pp_plane, pi_pitch,
                      p_va->i_surface_width,
                      p_va->i_surface_height,
                      &p_va->image_cache );
    }

    if( vaUnmapBuffer( p_va->p_display, p_va->image.buf ) )
        return VLC_EGENERIC;

    if(p_va->b_supports_derive)
    {
        vaDestroyImage( p_va->p_display, p_va->image.image_id );
        p_va->image.image_id = VA_INVALID_ID;
    }

    return VLC_SUCCESS;
}
Пример #18
0
static int Extract( vlc_va_t *va, picture_t *p_picture, uint8_t *data )
{
    vlc_va_sys_t *sys = va->sys;
    VASurfaceID surface = (VASurfaceID)(uintptr_t)data;
    VAImage image;
    int ret = VLC_EGENERIC;

#if VA_CHECK_VERSION(0,31,0)
    if (vaSyncSurface(sys->hw_ctx.display, surface))
#else
    if (vaSyncSurface(sys->hw_ctx.display, sys->hw_ctx.context_id, surface))
#endif
        return VLC_EGENERIC;

    if (!sys->do_derive || vaDeriveImage(sys->hw_ctx.display, surface, &image))
    {   /* Fallback if image derivation is not supported */
        if (vaCreateImage(sys->hw_ctx.display, &sys->format, sys->width,
                          sys->height, &image))
            return VLC_EGENERIC;
        if (vaGetImage(sys->hw_ctx.display, surface, 0, 0, sys->width,
                       sys->height, image.image_id))
            goto error;
    }

    void *p_base;
    if (vaMapBuffer(sys->hw_ctx.display, image.buf, &p_base))
        goto error;

    const unsigned i_fourcc = sys->format.fourcc;
    if( i_fourcc == VA_FOURCC_YV12 ||
        i_fourcc == VA_FOURCC_IYUV )
    {
        bool b_swap_uv = i_fourcc == VA_FOURCC_IYUV;
        uint8_t *pp_plane[3];
        size_t  pi_pitch[3];

        for( int i = 0; i < 3; i++ )
        {
            const int i_src_plane = (b_swap_uv && i != 0) ?  (3 - i) : i;
            pp_plane[i] = (uint8_t*)p_base + image.offsets[i_src_plane];
            pi_pitch[i] = image.pitches[i_src_plane];
        }
        CopyFromYv12( p_picture, pp_plane, pi_pitch, sys->width, sys->height,
                      &sys->image_cache );
    }
    else
    {
        assert( i_fourcc == VA_FOURCC_NV12 );
        uint8_t *pp_plane[2];
        size_t  pi_pitch[2];

        for( int i = 0; i < 2; i++ )
        {
            pp_plane[i] = (uint8_t*)p_base + image.offsets[i];
            pi_pitch[i] = image.pitches[i];
        }
        CopyFromNv12( p_picture, pp_plane, pi_pitch, sys->width, sys->height,
                      &sys->image_cache );
    }

    vaUnmapBuffer(sys->hw_ctx.display, image.buf);
    ret = VLC_SUCCESS;
error:
    vaDestroyImage(sys->hw_ctx.display, image.image_id);
    return ret;
}
Пример #19
0
VideoFrame VideoDecoderVAAPI::frame()
{
    DPTR_D(VideoDecoderVAAPI);
    if (!d.frame->opaque || !d.frame->data[0])
        return VideoFrame();
    VASurfaceID surface_id = (VASurfaceID)(uintptr_t)d.frame->data[3];
    VAStatus status = VA_STATUS_SUCCESS;
    if (display() == GLX) {
        d.surface_interop->setSurface((va_surface_t*)d.frame->opaque, d.surface_width, d.surface_height);
        VideoFrame f(d.surface_width, d.surface_height, VideoFormat::Format_RGB32);
        f.setBytesPerLine(d.surface_width*4); //used by gl to compute texture size
        f.setSurfaceInterop(d.surface_interop);
        return f;
    }
#if VA_CHECK_VERSION(0,31,0)
    if ((status = vaSyncSurface(d.display, surface_id)) != VA_STATUS_SUCCESS) {
        qWarning("vaSyncSurface(VADisplay:%p, VASurfaceID:%#x) == %#x", d.display, surface_id, status);
#else
    if (vaSyncSurface(d.display, d.context_id, surface_id)) {
        qWarning("vaSyncSurface(VADisplay:%#x, VAContextID:%#x, VASurfaceID:%#x) == %#x", d.display, d.context_id, surface_id, status);
#endif
        return VideoFrame();
    }

    if (!d.disable_derive && d.supports_derive) {
        /*
         * http://web.archiveorange.com/archive/v/OAywENyq88L319OcRnHI
         * vaDeriveImage is faster than vaGetImage. But VAImage is uncached memory and copying from it would be terribly slow
         * TODO: copy from USWC, see vlc and https://github.com/OpenELEC/OpenELEC.tv/pull/2937.diff
         * https://software.intel.com/en-us/articles/increasing-memory-throughput-with-intel-streaming-simd-extensions-4-intel-sse4-streaming-load
         */
        status = vaDeriveImage(d.display, surface_id, &d.image);
        if (status != VA_STATUS_SUCCESS) {
            qWarning("vaDeriveImage(VADisplay:%p, VASurfaceID:%#x, VAImage*:%p) == %#x", d.display, surface_id, &d.image, status);
            return VideoFrame();
        }
    } else {
        status = vaGetImage(d.display, surface_id, 0, 0, d.surface_width, d.surface_height, d.image.image_id);
        if (status != VA_STATUS_SUCCESS) {
            qWarning("vaGetImage(VADisplay:%p, VASurfaceID:%#x, 0,0, %d, %d, VAImageID:%#x) == %#x", d.display, surface_id, d.surface_width, d.surface_height, d.image.image_id, status);
            return VideoFrame();
        }
    }

    void *p_base;
    if ((status = vaMapBuffer(d.display, d.image.buf, &p_base)) != VA_STATUS_SUCCESS) {
        qWarning("vaMapBuffer(VADisplay:%p, VABufferID:%#x, pBuf:%p) == %#x", d.display, d.image.buf, &p_base, status);
        return VideoFrame();
    }

    VideoFormat::PixelFormat pixfmt = VideoFormat::Format_Invalid;
    bool swap_uv = false;
    switch (d.image.format.fourcc) {
    case VA_FOURCC_YV12:
        swap_uv |= d.disable_derive || !d.supports_derive;
        pixfmt = VideoFormat::Format_YUV420P;
        break;
    case VA_FOURCC_IYUV:
        swap_uv = true;
        pixfmt = VideoFormat::Format_YUV420P;
        break;
    case VA_FOURCC_NV12:
        pixfmt = VideoFormat::Format_NV12;
        break;
    default:
        break;
    }
    if (pixfmt == VideoFormat::Format_Invalid) {
        qWarning("unsupported vaapi pixel format: %#x", d.image.format.fourcc);
        return VideoFrame();
    }
    const VideoFormat fmt(pixfmt);
    uint8_t *src[3];
    int pitch[3];
    for (int i = 0; i < fmt.planeCount(); ++i) {
        src[i] = (uint8_t*)p_base + d.image.offsets[i];
        pitch[i] = d.image.pitches[i];
    }
    if (swap_uv) {
        std::swap(src[1], src[2]);
        std::swap(pitch[1], pitch[2]);
    }
    VideoFrame frame;
    if (d.copy_uswc && d.gpu_mem.isReady()) {
        int yuv_size = 0;
        if (pixfmt == VideoFormat::Format_NV12)
            yuv_size = pitch[0]*d.surface_height*3/2;
        else
            yuv_size = pitch[0]*d.surface_height + pitch[1]*d.surface_height/2 + pitch[2]*d.surface_height/2;
        // additional 15 bytes to ensure 16 bytes aligned
        QByteArray buf(15 + yuv_size, 0);
        const int offset_16 = (16 - ((uintptr_t)buf.data() & 0x0f)) & 0x0f;
        // plane 1, 2... is aligned?
        uchar* plane_ptr = (uchar*)buf.data() + offset_16;
        QVector<uchar*> dst(fmt.planeCount(), 0);
        for (int i = 0; i < dst.size(); ++i) {
            dst[i] = plane_ptr;
            // TODO: add VideoFormat::planeWidth/Height() ?
            const int plane_w = pitch[i];//(i == 0 || pixfmt == VideoFormat::Format_NV12) ? d.surface_width : fmt.chromaWidth(d.surface_width);
            const int plane_h = i == 0 ? d.surface_height : fmt.chromaHeight(d.surface_height);
            plane_ptr += pitch[i] * plane_h;
            d.gpu_mem.copyFrame(src[i], dst[i], plane_w, plane_h, pitch[i]);
        }
        frame = VideoFrame(buf, d.width, d.height, fmt);
        frame.setBits(dst);
        frame.setBytesPerLine(pitch);
    } else {
        frame = VideoFrame(d.width, d.height, fmt);
        frame.setBits(src);
        frame.setBytesPerLine(pitch);
        // TODO: why clone is faster()?
        frame = frame.clone();
    }

    if ((status = vaUnmapBuffer(d.display, d.image.buf)) != VA_STATUS_SUCCESS) {
        qWarning("vaUnmapBuffer(VADisplay:%p, VABufferID:%#x) == %#x", d.display, d.image.buf, status);
        return VideoFrame();
    }

    if (!d.disable_derive && d.supports_derive) {
        vaDestroyImage(d.display, d.image.image_id);
        d.image.image_id = VA_INVALID_ID;
    }
    return frame;
}

struct display_names_t {
    VideoDecoderVAAPI::DisplayType display;
    QString name;
};
static const display_names_t display_names[] = {
    { VideoDecoderVAAPI::GLX, "GLX" },
    { VideoDecoderVAAPI::X11, "X11" },
    { VideoDecoderVAAPI::DRM, "DRM" }
};

static VideoDecoderVAAPI::DisplayType displayFromName(QString name) {
    for (unsigned int i = 0; i < sizeof(display_names)/sizeof(display_names[0]); ++i) {
        if (name.toUpper().contains(display_names[i].name.toUpper())) {
            return display_names[i].display;
        }
    }
    return VideoDecoderVAAPI::X11;
}

static QString displayToName(VideoDecoderVAAPI::DisplayType t) {
    for (unsigned int i = 0; i < sizeof(display_names)/sizeof(display_names[0]); ++i) {
        if (t == display_names[i].display) {
            return display_names[i].name;
        }
    }
    return QString();
}

void VideoDecoderVAAPI::setDisplayPriority(const QStringList &priority)
{
    DPTR_D(VideoDecoderVAAPI);
    d.display_priority.clear();
    foreach (QString disp, priority) {
        d.display_priority.push_back(displayFromName(disp));
    }
VdpStatus
vdpVideoSurfaceGetBitsYCbCr(VdpVideoSurface surface, VdpYCbCrFormat destination_ycbcr_format,
                            void *const *destination_data, uint32_t const *destination_pitches)
{
    VdpStatus err_code;
    if (!destination_data || !destination_pitches)
        return VDP_STATUS_INVALID_POINTER;
    VdpVideoSurfaceData *srcSurfData = handle_acquire(surface, HANDLETYPE_VIDEO_SURFACE);
    if (NULL == srcSurfData)
        return VDP_STATUS_INVALID_HANDLE;
    VdpDeviceData *deviceData = srcSurfData->deviceData;
    VADisplay va_dpy = deviceData->va_dpy;

    if (deviceData->va_available) {
        VAImage q;
        vaDeriveImage(va_dpy, srcSurfData->va_surf, &q);
        if (VA_FOURCC('N', 'V', '1', '2') == q.format.fourcc &&
            VDP_YCBCR_FORMAT_NV12 == destination_ycbcr_format)
        {
            uint8_t *img_data;
            vaMapBuffer(va_dpy, q.buf, (void **)&img_data);
            if (destination_pitches[0] == q.pitches[0] &&
                destination_pitches[1] == q.pitches[1])
            {
                const uint32_t sz = (uint32_t)q.width * (uint32_t)q.height;
                memcpy(destination_data[0], img_data + q.offsets[0], sz);
                memcpy(destination_data[1], img_data + q.offsets[1], sz / 2);
            } else {
                uint8_t *src = img_data + q.offsets[0];
                uint8_t *dst = destination_data[0];
                for (unsigned int y = 0; y < q.height; y ++) {  // Y plane
                    memcpy (dst, src, q.width);
                    src += q.pitches[0];
                    dst += destination_pitches[0];
                }
                src = img_data + q.offsets[1];
                dst = destination_data[1];
                for (unsigned int y = 0; y < q.height / 2; y ++) {  // UV plane
                    memcpy(dst, src, q.width);  // q.width/2 samples of U and V each, hence q.width
                    src += q.pitches[1];
                    dst += destination_pitches[1];
                }
            }
            vaUnmapBuffer(va_dpy, q.buf);
        } else if (VA_FOURCC('N', 'V', '1', '2') == q.format.fourcc &&
                   VDP_YCBCR_FORMAT_YV12 == destination_ycbcr_format)
        {
            uint8_t *img_data;
            vaMapBuffer(va_dpy, q.buf, (void **)&img_data);

            // Y plane
            if (destination_pitches[0] == q.pitches[0]) {
                const uint32_t sz = (uint32_t)q.width * (uint32_t)q.height;
                memcpy(destination_data[0], img_data + q.offsets[0], sz);
            } else {
                uint8_t *src = img_data + q.offsets[0];
                uint8_t *dst = destination_data[0];
                for (unsigned int y = 0; y < q.height; y ++) {
                    memcpy (dst, src, q.width);
                    src += q.pitches[0];
                    dst += destination_pitches[0];
                }
            }

            // unpack mixed UV to separate planes
            for (unsigned int y = 0; y < q.height/2; y ++) {
                uint8_t *src = img_data + q.offsets[1] + y * q.pitches[1];
                uint8_t *dst_u = destination_data[1] + y * destination_pitches[1];
                uint8_t *dst_v = destination_data[2] + y * destination_pitches[2];

                for (unsigned int x = 0; x < q.width/2; x++) {
                    *dst_v++ = *src++;
                    *dst_u++ = *src++;
                }
            }

            vaUnmapBuffer(va_dpy, q.buf);
        } else {
            const char *c = (const char *)&q.format.fourcc;
            traceError("error (%s): not implemented conversion VA FOURCC %c%c%c%c -> %s\n",
                       __func__, *c, *(c+1), *(c+2), *(c+3),
                       reverse_ycbcr_format(destination_ycbcr_format));
            vaDestroyImage(va_dpy, q.image_id);
            err_code = VDP_STATUS_INVALID_Y_CB_CR_FORMAT;
            goto quit;
        }
        vaDestroyImage(va_dpy, q.image_id);
    } else {
        // software fallback
        traceError("error (%s): not implemented software fallback\n", __func__);
        err_code = VDP_STATUS_ERROR;
        goto quit;
    }

    GLenum gl_error = glGetError();
    if (GL_NO_ERROR != gl_error) {
        traceError("error (%s): gl error %d\n", __func__, gl_error);
        err_code = VDP_STATUS_ERROR;
        goto quit;
    }

    err_code = VDP_STATUS_OK;
quit:
    handle_release(surface);
    return err_code;
}