bool VAApiWriter::getNV12Image( VAImageFormat *img_fmt, VASurfaceID surfaceID, void *dest, ImgScaler *yv12ToRGB32 ) const { VAImage image; quint8 *data = getImage( image, surfaceID, img_fmt ); if ( data ) { QByteArray yv12; yv12.resize( outW * outH * 3 << 1 ); memcpy( yv12.data(), data + image.offsets[ 0 ], outW * outH ); quint8 *yv12data_cb = ( quint8 * )yv12.data() + outW * outH; quint8 *yv12data_cr = yv12data_cb + outW/2 * outH/2; const unsigned second_plane_size = outW * outH / 2; data += image.offsets[ 1 ]; for ( unsigned i = 0 ; i < second_plane_size ; i += 2 ) { *( yv12data_cr++ ) = *( data++ ); *( yv12data_cb++ ) = *( data++ ); } vaUnmapBuffer( VADisp, image.buf ); yv12ToRGB32->scale( yv12.data(), dest ); vaDestroyImage( VADisp, image.image_id ); return true; } return false; }
static void DestroySurfaces( vlc_va_vaapi_t *p_va ) { if( p_va->image.image_id != VA_INVALID_ID ) { CopyCleanCache( &p_va->image_cache ); vaDestroyImage( p_va->p_display, p_va->image.image_id ); } else if(p_va->b_supports_derive) { CopyCleanCache( &p_va->image_cache ); } if( p_va->i_context_id != VA_INVALID_ID ) vaDestroyContext( p_va->p_display, p_va->i_context_id ); for( int i = 0; i < p_va->i_surface_count && p_va->p_surface; i++ ) { vlc_va_surface_t *p_surface = &p_va->p_surface[i]; if( p_surface->i_id != VA_INVALID_SURFACE ) vaDestroySurfaces( p_va->p_display, &p_surface->i_id, 1 ); } free( p_va->p_surface ); /* */ p_va->image.image_id = VA_INVALID_ID; p_va->i_context_id = VA_INVALID_ID; p_va->p_surface = NULL; p_va->i_surface_width = 0; p_va->i_surface_height = 0; }
static void unref_image(struct gl_hwdec *hw) { struct priv *p = hw->priv; VAStatus status; for (int n = 0; n < 4; n++) { if (p->images[n]) p->DestroyImageKHR(eglGetCurrentDisplay(), p->images[n]); p->images[n] = 0; } va_lock(p->ctx); if (p->buffer_acquired) { status = vaReleaseBufferHandle(p->display, p->current_image.buf); CHECK_VA_STATUS(p, "vaReleaseBufferHandle()"); p->buffer_acquired = false; } if (p->current_image.image_id != VA_INVALID_ID) { status = vaDestroyImage(p->display, p->current_image.image_id); CHECK_VA_STATUS(p, "vaDestroyImage()"); p->current_image.image_id = VA_INVALID_ID; } mp_image_unrefp(&p->current_ref); va_unlock(p->ctx); }
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"); } }
mfxStatus vaapiFrameAllocator::UnlockFrame(mfxMemId mid, mfxFrameData *ptr) { vaapiMemId* vaapi_mid = (vaapiMemId*)mid; if (!vaapi_mid || !(vaapi_mid->m_surface)) return MFX_ERR_INVALID_HANDLE; if (MFX_FOURCC_P8 == vaapi_mid->m_fourcc) // bitstream processing { vaUnmapBuffer(m_dpy, *(vaapi_mid->m_surface)); } else // Image processing { vaUnmapBuffer(m_dpy, vaapi_mid->m_image.buf); vaDestroyImage(m_dpy, vaapi_mid->m_image.image_id); if (NULL != ptr) { ptr->Pitch = 0; ptr->Y = NULL; ptr->U = NULL; ptr->V = NULL; ptr->A = NULL; } } return MFX_ERR_NONE; }
void* SurfaceInteropVAAPI::mapToHost(const VideoFormat &format, void *handle, int plane) { Q_UNUSED(plane); int nb_fmts = vaMaxNumImageFormats(m_surface->vadisplay()); //av_mallocz_array VAImageFormat *p_fmt = (VAImageFormat*)calloc(nb_fmts, sizeof(*p_fmt)); if (!p_fmt) { return NULL; } if (vaQueryImageFormats(m_surface->vadisplay(), p_fmt, &nb_fmts)) { free(p_fmt); return NULL; } VAImage image; for (int i = 0; i < nb_fmts; i++) { if (p_fmt[i].fourcc == VA_FOURCC_YV12 || p_fmt[i].fourcc == VA_FOURCC_IYUV || p_fmt[i].fourcc == VA_FOURCC_NV12) { qDebug("vaCreateImage: %c%c%c%c", p_fmt[i].fourcc<<24>>24, p_fmt[i].fourcc<<16>>24, p_fmt[i].fourcc<<8>>24, p_fmt[i].fourcc>>24); if (vaCreateImage(m_surface->vadisplay(), &p_fmt[i], m_surface->width(), m_surface->height(), &image) != VA_STATUS_SUCCESS) { image.image_id = VA_INVALID_ID; qDebug("vaCreateImage error: %c%c%c%c", p_fmt[i].fourcc<<24>>24, p_fmt[i].fourcc<<16>>24, p_fmt[i].fourcc<<8>>24, p_fmt[i].fourcc>>24); continue; } /* Validate that vaGetImage works with this format */ if (vaGetImage(m_surface->vadisplay(), m_surface->get(), 0, 0, m_surface->width(), m_surface->height(), image.image_id) != VA_STATUS_SUCCESS) { vaDestroyImage(m_surface->vadisplay(), image.image_id); qDebug("vaGetImage error: %c%c%c%c", p_fmt[i].fourcc<<24>>24, p_fmt[i].fourcc<<16>>24, p_fmt[i].fourcc<<8>>24, p_fmt[i].fourcc>>24); image.image_id = VA_INVALID_ID; continue; }
void VideoDecoderVAAPIPrivate::destroySurfaces() { if (image.image_id != VA_INVALID_ID) { //CopyCleanCache(&sys->image_cache); vaDestroyImage(display, image.image_id); } else if (supports_derive) { //CopyCleanCache(&sys->image_cache); } if (context_id != VA_INVALID_ID) vaDestroyContext(display, context_id); for (int i = 0; i < nb_surfaces && surfaces; i++) { va_surface_t *surface = &surfaces[i]; if (surface->i_id != VA_INVALID_SURFACE) vaDestroySurfaces(display, &surface->i_id, 1); } //qDeleteAll(surfaces); //surfaces.clear(); free(surfaces); surfaces = 0; /* */ image.image_id = VA_INVALID_ID; context_id = VA_INVALID_ID; surface_width = 0; surface_height = 0; //vlc_mutex_destroy(&sys->lock); }
/** * 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; }
EglVaapiImage::~EglVaapiImage() { if (m_inited) { if (m_acquired) vaReleaseBufferHandle(m_display, m_image.buf); vaDestroyImage(m_display, m_image.image_id); } }
VaapiImage::~VaapiImage() { VAStatus status; status = vaDestroyImage(m_display->getID(), m_image->image_id); if (!checkVaapiStatus(status, "vaDestoryImage()")) return; }
bool VAApiWriter::getRGB32Image( VAImageFormat *img_fmt, VASurfaceID surfaceID, void *dest ) const { VAImage image; quint8 *data = getImage( image, surfaceID, img_fmt ); if ( data ) { memcpy( dest, data + image.offsets[ 0 ], outW * outH << 2 ); vaUnmapBuffer( VADisp, image.buf ); vaDestroyImage( VADisp, image.image_id ); return true; } return false; }
VAAPIContext::~VAAPIContext() { delete [] m_pictureAttributes; ClearGLXSurfaces(); if (m_display) { m_display->m_x_disp->Lock(); INIT_ST; if (m_image.image_id != VA_INVALID_ID) { va_status = vaDestroyImage(m_ctx.display, m_image.image_id); CHECK_ST; } if (m_ctx.context_id) { va_status = vaDestroyContext(m_ctx.display, m_ctx.context_id); CHECK_ST; } if (m_ctx.config_id) { va_status = vaDestroyConfig(m_ctx.display, m_ctx.config_id); CHECK_ST; } if (m_surfaces) { va_status = vaDestroySurfaces(m_ctx.display, m_surfaces, m_numSurfaces); CHECK_ST; } } if (m_surfaces) delete [] m_surfaces; if (m_surfaceData) delete [] m_surfaceData; if (m_display) { m_display->m_x_disp->Unlock(); m_display->DecrRef(); } delete m_copy; LOG(VB_PLAYBACK, LOG_INFO, LOC + "Deleted context"); }
void* SurfaceInteropVAAPI::mapToHost(const VideoFormat &format, void *handle, int plane) { Q_UNUSED(plane); VAImage image; static const unsigned int fcc[] = { VA_FOURCC_NV12, VA_FOURCC_YV12, VA_FOURCC_IYUV, 0}; va_new_image(m_surface->vadisplay(), fcc, &image, m_surface->width(), m_surface->height()); if (image.image_id == VA_INVALID_ID) return NULL; void *p_base; VA_ENSURE(vaGetImage(m_surface->vadisplay(), m_surface->get(), 0, 0, m_surface->width(), m_surface->height(), image.image_id), NULL); VA_ENSURE(vaMapBuffer(m_surface->vadisplay(), image.buf, &p_base), NULL); //TODO: destroy image before return VideoFormat::PixelFormat pixfmt = pixelFormatFromVA(image.format.fourcc); bool swap_uv = image.format.fourcc != VA_FOURCC_NV12; if (pixfmt == VideoFormat::Format_Invalid) { qWarning("unsupported vaapi pixel format: %#x", image.format.fourcc); VA_ENSURE(vaDestroyImage(m_surface->vadisplay(), image.image_id), NULL); return NULL; } 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 + image.offsets[i]; pitch[i] = image.pitches[i]; } VideoFrame frame = VideoFrame::fromGPU(fmt, frame_width, frame_height, m_surface->height(), src, pitch, true, swap_uv); if (format != fmt) frame = frame.to(format); VAWARN(vaUnmapBuffer(m_surface->vadisplay(), image.buf)); VAWARN(vaDestroyImage(m_surface->vadisplay(), image.image_id)); image.image_id = VA_INVALID_ID; VideoFrame *f = reinterpret_cast<VideoFrame*>(handle); frame.setTimestamp(f->timestamp()); *f = frame; return f; }
quint8 *VAApiWriter::getImage( VAImage &image, VASurfaceID surfaceID, VAImageFormat *img_fmt ) const { if ( vaCreateImage( VADisp, img_fmt, outW, outH, &image ) == VA_STATUS_SUCCESS ) { quint8 *data; if ( vaSyncSurface( VADisp, surfaceID ) == VA_STATUS_SUCCESS && vaGetImage( VADisp, surfaceID, 0, 0, outW, outH, image.image_id ) == VA_STATUS_SUCCESS && vaMapBuffer( VADisp, image.buf, ( void ** )&data ) == VA_STATUS_SUCCESS ) return data; vaDestroyImage( VADisp, image.image_id ); } return NULL; }
bool VAApiWriter::getYV12Image( VAImageFormat *img_fmt, VASurfaceID surfaceID, void *dest, ImgScaler *yv12ToRGB32 ) const { VAImage image; quint8 *data = getImage( image, surfaceID, img_fmt ); if ( data ) { QByteArray yv12; yv12.resize( outW * outH * 3 << 1 ); memcpy( yv12.data(), data + image.offsets[ 0 ], outW * outH ); memcpy( yv12.data() + outW * outH, data + image.offsets[ 1 ], outW/2 * outH/2 ); memcpy( yv12.data() + outW * outH + outW/2 * outH/2, data + image.offsets[ 2 ], outW/2 * outH/2 ); vaUnmapBuffer( VADisp, image.buf ); yv12ToRGB32->scale( yv12.data(), dest ); vaDestroyImage( VADisp, image.image_id ); return true; } return false; }
static void gst_vaapi_image_destroy(GstVaapiImage *image) { GstVaapiDisplay * const display = GST_VAAPI_OBJECT_DISPLAY(image); VAImageID image_id; VAStatus status; _gst_vaapi_image_unmap(image); image_id = GST_VAAPI_OBJECT_ID(image); GST_DEBUG("image %" GST_VAAPI_ID_FORMAT, GST_VAAPI_ID_ARGS(image_id)); if (image_id != VA_INVALID_ID) { GST_VAAPI_DISPLAY_LOCK(display); status = vaDestroyImage(GST_VAAPI_DISPLAY_VADISPLAY(display), image_id); GST_VAAPI_DISPLAY_UNLOCK(display); if (!vaapi_check_status(status, "vaDestroyImage()")) g_warning("failed to destroy image %" GST_VAAPI_ID_FORMAT, GST_VAAPI_ID_ARGS(image_id)); GST_VAAPI_OBJECT_ID(image) = VA_INVALID_ID; } }
static void vaapi_unmap_frame(void *opaque, uint8_t *data) { AVHWFramesContext *hwfc = opaque; AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx; VAAPISurfaceMap *map = (VAAPISurfaceMap*)data; const AVFrame *src; VASurfaceID surface_id; VAStatus vas; src = map->source; surface_id = (VASurfaceID)(uintptr_t)src->data[3]; av_log(hwfc, AV_LOG_DEBUG, "Unmap surface %#x.\n", surface_id); vas = vaUnmapBuffer(hwctx->display, map->image.buf); if (vas != VA_STATUS_SUCCESS) { av_log(hwfc, AV_LOG_ERROR, "Failed to unmap image from surface " "%#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas)); } if ((map->flags & VAAPI_MAP_WRITE) && !(map->flags & VAAPI_MAP_DIRECT)) { vas = vaPutImage(hwctx->display, surface_id, map->image.image_id, 0, 0, hwfc->width, hwfc->height, 0, 0, hwfc->width, hwfc->height); if (vas != VA_STATUS_SUCCESS) { av_log(hwfc, AV_LOG_ERROR, "Failed to write image to surface " "%#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas)); } } vas = vaDestroyImage(hwctx->display, map->image.image_id); if (vas != VA_STATUS_SUCCESS) { av_log(hwfc, AV_LOG_ERROR, "Failed to destroy image from surface " "%#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas)); } av_free(map); }
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; }
int vaapi_exit(void) { VAAPIContext * const vaapi = vaapi_get_context(); unsigned int i; if (!vaapi) return 0; #if USE_GLX if (display_type() == DISPLAY_GLX) vaapi_glx_destroy_surface(); #endif destroy_buffers(vaapi->display, &vaapi->pic_param_buf_id, 1); destroy_buffers(vaapi->display, &vaapi->iq_matrix_buf_id, 1); destroy_buffers(vaapi->display, &vaapi->bitplane_buf_id, 1); destroy_buffers(vaapi->display, vaapi->slice_buf_ids, vaapi->n_slice_buf_ids); if (vaapi->subpic_flags) { free(vaapi->subpic_flags); vaapi->subpic_flags = NULL; } if (vaapi->subpic_formats) { free(vaapi->subpic_formats); vaapi->subpic_formats = NULL; vaapi->n_subpic_formats = 0; } if (vaapi->image_formats) { free(vaapi->image_formats); vaapi->image_formats = NULL; vaapi->n_image_formats = 0; } if (vaapi->entrypoints) { free(vaapi->entrypoints); vaapi->entrypoints = NULL; vaapi->n_entrypoints = 0; } if (vaapi->profiles) { free(vaapi->profiles); vaapi->profiles = NULL; vaapi->n_profiles = 0; } if (vaapi->slice_params) { free(vaapi->slice_params); vaapi->slice_params = NULL; vaapi->slice_params_alloc = 0; vaapi->n_slice_params = 0; } if (vaapi->slice_buf_ids) { free(vaapi->slice_buf_ids); vaapi->slice_buf_ids = NULL; vaapi->n_slice_buf_ids = 0; } if (vaapi->subpic_image.image_id != VA_INVALID_ID) { vaDestroyImage(vaapi->display, vaapi->subpic_image.image_id); vaapi->subpic_image.image_id = VA_INVALID_ID; } for (i = 0; i < ARRAY_ELEMS(vaapi->subpic_ids); i++) { if (vaapi->subpic_ids[i] != VA_INVALID_ID) { vaDestroySubpicture(vaapi->display, vaapi->subpic_ids[i]); vaapi->subpic_ids[i] = VA_INVALID_ID; } } if (vaapi->surface_id) { vaDestroySurfaces(vaapi->display, &vaapi->surface_id, 1); vaapi->surface_id = 0; } if (vaapi->context_id) { vaDestroyContext(vaapi->display, vaapi->context_id); vaapi->context_id = 0; } if (vaapi->config_id) { vaDestroyConfig(vaapi->display, vaapi->config_id); vaapi->config_id = 0; } if (vaapi->display) { vaTerminate(vaapi->display); vaapi->display = NULL; } free(vaapi_context); return 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; }
/** 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 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; }
int main(int argc,char **argv) { int major_ver, minor_ver; VAStatus va_status; pthread_t thread1; int ret; char c; int i; char str_src_fmt[5], str_dst_fmt[5]; static struct option long_options[] = { {"fmt1", required_argument, NULL, '1'}, {"fmt2", required_argument, NULL, '2'}, {0, 0, 0, 0} }; while ((c =getopt_long(argc,argv,"w:h:g:r:d:f:tcep?n:1:2:v", long_options, NULL)) != EOF) { switch (c) { case '?': printf("putsurface <options>\n"); printf(" -g <widthxheight+x_location+y_location> window geometry\n"); printf(" -w/-h resolution of surface\n"); printf(" -r <framerate>\n"); printf(" -d the dimension of black/write square box, default is 32\n"); printf(" -t multi-threads\n"); printf(" -c test clipbox\n"); printf(" -f <1/2> top field, or bottom field\n"); printf(" -1 source format (fourcc) for color conversion test\n"); printf(" -2 dest format (fourcc) for color conversion test\n"); printf(" --fmt1 same to -1\n"); printf(" --fmt2 same to -2\n"); printf(" -v verbose output\n"); exit(0); break; case 'g': ret = sscanf(optarg, "%dx%d+%d+%d", &win_width, &win_height, &win_x, &win_y); if (ret != 4) { printf("invalid window geometry, must be widthxheight+x_location+y_location\n"); exit(0); } else printf("Create window at (%d, %d), width = %d, height = %d\n", win_x, win_y, win_width, win_height); break; case 'r': frame_rate = atoi(optarg); break; case 'w': surface_width = atoi(optarg); break; case 'h': surface_height = atoi(optarg); break; case 'n': frame_num_total = atoi(optarg); break; case 'd': box_width = atoi(optarg); break; case 't': multi_thread = 1; printf("Two threads to do vaPutSurface\n"); break; case 'e': check_event = 0; break; case 'p': put_pixmap = 1; break; case 'c': test_clip = 1; break; case 'f': if (atoi(optarg) == 1) { printf("Display TOP field\n"); display_field = VA_TOP_FIELD; } else if (atoi(optarg) == 2) { printf("Display BOTTOM field\n"); display_field = VA_BOTTOM_FIELD; } else printf("The validate input for -f is: 1(top field)/2(bottom field)\n"); break; case '1': sscanf(optarg, "%s", str_src_fmt); csc_src_fourcc = map_str_to_vafourcc (str_src_fmt); if (!csc_src_fourcc) { printf("invalid fmt1: %s\n", str_src_fmt ); exit(0); } break; case '2': sscanf(optarg, "%s", str_dst_fmt); csc_dst_fourcc = map_str_to_vafourcc (str_dst_fmt); if (!csc_dst_fourcc) { printf("invalid fmt1: %s\n", str_dst_fmt ); exit(0); } break; case 'v': verbose = 1; printf("Enable verbose output\n"); break; } } if (csc_src_fourcc && csc_dst_fourcc) { test_color_conversion = 1; } win_display = (void *)open_display(); if (win_display == NULL) { fprintf(stderr, "Can't open the connection of display!\n"); exit(-1); } create_window(win_display, win_x, win_y, win_width, win_height); va_dpy = vaGetDisplay(win_display); va_status = vaInitialize(va_dpy, &major_ver, &minor_ver); CHECK_VASTATUS(va_status, "vaInitialize"); if (test_color_conversion) { ret = csc_preparation(); } if (!test_color_conversion || !ret ) { va_status = vaCreateSurfaces( va_dpy, VA_RT_FORMAT_YUV420, surface_width, surface_height, &surface_id[0], SURFACE_NUM, NULL, 0 ); } CHECK_VASTATUS(va_status, "vaCreateSurfaces"); if (multi_thread == 0) /* upload the content for all surfaces */ upload_source_YUV_once_for_all(); if (check_event) pthread_mutex_init(&gmutex, NULL); for(i = 0; i< SURFACE_NUM; i++) pthread_mutex_init(&surface_mutex[i], NULL); if (multi_thread == 1) ret = pthread_create(&thread1, NULL, putsurface_thread, (void*)drawable_thread1); putsurface_thread((void *)drawable_thread0); if (multi_thread == 1) pthread_join(thread1, (void **)&ret); printf("thread1 is free\n"); if (test_color_conversion) { // destroy temp surface/image va_status = vaDestroySurfaces(va_dpy, &csc_render_surface, 1); CHECK_VASTATUS(va_status,"vaDestroySurfaces"); va_status = vaDestroyImage(va_dpy, csc_dst_fourcc_image.image_id); CHECK_VASTATUS(va_status,"vaDestroyImage"); } if (vpp_config_id != VA_INVALID_ID) { vaDestroyConfig (va_dpy, vpp_config_id); vpp_config_id = VA_INVALID_ID; } vaDestroySurfaces(va_dpy,&surface_id[0],SURFACE_NUM); vaTerminate(va_dpy); free(va_image_formats); free(va_surface_attribs); close_display(win_display); return 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(¶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 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; }
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; }
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())); }
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)); }
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); }