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; }
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"); } }
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; }
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; }
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; }
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; }
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; }
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; }
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())); }
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; }
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(¶mPortDefinitionInput, 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(¶mPortDefinitionInput, 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 }
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]); }
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); }
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; }
/** 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; }
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; }
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; }
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; }