static int reinit(struct gl_hwdec *hw, struct mp_image_params *params) { struct priv *p = hw->priv; GL *gl = hw->gl; struct vdp_functions *vdp = &p->ctx->vdp; VdpStatus vdp_st; destroy_objects(hw); assert(params->imgfmt == hw->driver->imgfmt); p->image_params = *params; if (mp_vdpau_handle_preemption(p->ctx, &p->preemption_counter) < 0) return -1; gl->VDPAUInitNV(BRAINDEATH(p->ctx->vdp_device), p->ctx->get_proc_address); p->vdpgl_initialized = true; vdp_st = vdp->output_surface_create(p->ctx->vdp_device, VDP_RGBA_FORMAT_B8G8R8A8, params->w, params->h, &p->vdp_surface); CHECK_VDP_ERROR(p, "Error when calling vdp_output_surface_create"); gl->GenTextures(1, &p->gl_texture); gl->BindTexture(GL_TEXTURE_2D, p->gl_texture); gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); gl->BindTexture(GL_TEXTURE_2D, 0); p->vdpgl_surface = gl->VDPAURegisterOutputSurfaceNV(BRAINDEATH(p->vdp_surface), GL_TEXTURE_2D, 1, &p->gl_texture); if (!p->vdpgl_surface) return -1; gl->VDPAUSurfaceAccessNV(p->vdpgl_surface, GL_READ_ONLY); glCheckError(gl, hw->log, "After initializing vdpau OpenGL interop"); return 0; }
static int reinit(struct gl_hwdec *hw, int w, int h) { struct priv *p = hw->priv; GL *gl = hw->mpgl->gl; VAStatus status; destroy_texture(hw); gl->GenTextures(1, &p->gl_texture); gl->BindTexture(GL_TEXTURE_2D, p->gl_texture); gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); gl->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); gl->BindTexture(GL_TEXTURE_2D, 0); status = vaCreateSurfaceGLX(p->display, GL_TEXTURE_2D, p->gl_texture, &p->vaglx_surface); return check_va_status(status, "vaCreateSurfaceGLX()") ? 0 : -1; }
static int reinit(struct gl_hwdec *hw, struct mp_image_params *params) { struct priv *p = hw->priv; GL *gl = hw->gl; // Recreate them to get rid of all previous image data (possibly). destroy_textures(hw); assert(params->imgfmt == hw->driver->imgfmt); gl->GenTextures(4, p->gl_textures); for (int n = 0; n < 4; n++) { gl->BindTexture(GL_TEXTURE_2D, p->gl_textures[n]); gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); } gl->BindTexture(GL_TEXTURE_2D, 0); return 0; }
static int reinit(struct gl_hwdec *hw, struct mp_image_params *params) { struct priv *p = hw->priv; GL *gl = hw->gl; destroy_texture(hw); params->imgfmt = hw->driver->imgfmt; gl->GenTextures(1, &p->gl_texture); gl->BindTexture(GL_TEXTURE_2D, p->gl_texture); gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); gl->BindTexture(GL_TEXTURE_2D, 0); p->pixmap = XCreatePixmap(p->xdisplay, RootWindow(p->xdisplay, DefaultScreen(p->xdisplay)), params->w, params->h, 24); if (!p->pixmap) { MP_FATAL(hw, "could not create pixmap\n"); return -1; } int attribs[] = { GLX_TEXTURE_TARGET_EXT, GLX_TEXTURE_2D_EXT, GLX_TEXTURE_FORMAT_EXT, GLX_TEXTURE_FORMAT_RGB_EXT, GLX_MIPMAP_TEXTURE_EXT, False, None, }; p->glxpixmap = glXCreatePixmap(p->xdisplay, p->fbc, p->pixmap, attribs); gl->BindTexture(GL_TEXTURE_2D, p->gl_texture); p->glXBindTexImage(p->xdisplay, p->glxpixmap, GLX_FRONT_EXT, NULL); gl->BindTexture(GL_TEXTURE_2D, 0); return 0; }
static int reinit(struct gl_hwdec *hw, const struct mp_image_params *params) { struct priv *p = hw->priv; GL *gl = hw->mpgl->gl; VAStatus status; destroy_texture(hw); gl->GenTextures(1, &p->gl_texture); gl->BindTexture(GL_TEXTURE_2D, p->gl_texture); gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); gl->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, params->w, params->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); gl->BindTexture(GL_TEXTURE_2D, 0); va_lock(p->ctx); status = vaCreateSurfaceGLX(p->display, GL_TEXTURE_2D, p->gl_texture, &p->vaglx_surface); va_unlock(p->ctx); return CHECK_VA_STATUS(p, "vaCreateSurfaceGLX()") ? 0 : -1; }
static int init(struct ra_hwdec *hw) { struct priv_owner *p = hw->priv; if (!ra_is_gl(hw->ra)) return -1; GL *gl = ra_gl_get(hw->ra); if (!(gl->mpgl_caps & MPGL_CAP_DXINTEROP)) return -1; // AMD drivers won't open multiple dxinterop HANDLES on the same D3D device, // so we request the one already in use by context_dxinterop p->device_h = mpgl_get_native_display(gl, "dxinterop_device_HANDLE"); if (!p->device_h) return -1; // But we also still need the actual D3D device p->device = mpgl_get_native_display(gl, "IDirect3DDevice9Ex"); if (!p->device) return -1; IDirect3DDevice9Ex_AddRef(p->device); p->hwctx = (struct mp_hwdec_ctx){ .driver_name = hw->driver->name, .av_device_ref = d3d9_wrap_device_ref((IDirect3DDevice9 *)p->device), }; hwdec_devices_add(hw->devs, &p->hwctx); return 0; } static void mapper_uninit(struct ra_hwdec_mapper *mapper) { struct priv *p = mapper->priv; GL *gl = ra_gl_get(mapper->ra); if (p->rtarget_h && p->device_h) { if (!gl->DXUnlockObjectsNV(p->device_h, 1, &p->rtarget_h)) { MP_ERR(mapper, "Failed unlocking texture for access by OpenGL: %s\n", mp_LastError_to_str()); } } if (p->rtarget_h) { if (!gl->DXUnregisterObjectNV(p->device_h, p->rtarget_h)) { MP_ERR(mapper, "Failed to unregister Direct3D surface with OpenGL: %s\n", mp_LastError_to_str()); } else { p->rtarget_h = 0; } } gl->DeleteTextures(1, &p->texture); p->texture = 0; if (p->rtarget) { IDirect3DSurface9_Release(p->rtarget); p->rtarget = NULL; } ra_tex_free(mapper->ra, &mapper->tex[0]); } static int mapper_init(struct ra_hwdec_mapper *mapper) { struct priv_owner *p_owner = mapper->owner->priv; struct priv *p = mapper->priv; GL *gl = ra_gl_get(mapper->ra); HRESULT hr; p->device = p_owner->device; p->device_h = p_owner->device_h; HANDLE share_handle = NULL; hr = IDirect3DDevice9Ex_CreateRenderTarget( p->device, mapper->src_params.w, mapper->src_params.h, SHARED_SURFACE_D3DFMT, D3DMULTISAMPLE_NONE, 0, FALSE, &p->rtarget, &share_handle); if (FAILED(hr)) { MP_ERR(mapper, "Failed creating offscreen Direct3D surface: %s\n", mp_HRESULT_to_str(hr)); return -1; } if (share_handle && !gl->DXSetResourceShareHandleNV(p->rtarget, share_handle)) { MP_ERR(mapper, "Failed setting Direct3D/OpenGL share handle for surface: %s\n", mp_LastError_to_str()); return -1; } gl->GenTextures(1, &p->texture); gl->BindTexture(GL_TEXTURE_2D, p->texture); gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); gl->BindTexture(GL_TEXTURE_2D, 0); p->rtarget_h = gl->DXRegisterObjectNV(p->device_h, p->rtarget, p->texture, GL_TEXTURE_2D, WGL_ACCESS_READ_ONLY_NV); if (!p->rtarget_h) { MP_ERR(mapper, "Failed to register Direct3D surface with OpenGL: %s\n", mp_LastError_to_str()); return -1; } if (!gl->DXLockObjectsNV(p->device_h, 1, &p->rtarget_h)) { MP_ERR(mapper, "Failed locking texture for access by OpenGL %s\n", mp_LastError_to_str()); return -1; } struct ra_tex_params params = { .dimensions = 2, .w = mapper->src_params.w, .h = mapper->src_params.h, .d = 1, .format = ra_find_unorm_format(mapper->ra, 1, 4), .render_src = true, .src_linear = true, }; if (!params.format) return -1; mapper->tex[0] = ra_create_wrapped_tex(mapper->ra, ¶ms, p->texture); if (!mapper->tex[0]) return -1; mapper->dst_params = mapper->src_params; mapper->dst_params.imgfmt = IMGFMT_RGB0; mapper->dst_params.hw_subfmt = 0; return 0; } static int mapper_map(struct ra_hwdec_mapper *mapper) { struct priv *p = mapper->priv; GL *gl = ra_gl_get(mapper->ra); HRESULT hr; if (!gl->DXUnlockObjectsNV(p->device_h, 1, &p->rtarget_h)) { MP_ERR(mapper, "Failed unlocking texture for access by OpenGL: %s\n", mp_LastError_to_str()); return -1; } IDirect3DSurface9* hw_surface = (IDirect3DSurface9 *)mapper->src->planes[3]; RECT rc = {0, 0, mapper->src->w, mapper->src->h}; hr = IDirect3DDevice9Ex_StretchRect(p->device, hw_surface, &rc, p->rtarget, &rc, D3DTEXF_NONE); if (FAILED(hr)) { MP_ERR(mapper, "Direct3D RGB conversion failed: %s", mp_HRESULT_to_str(hr)); return -1; } if (!gl->DXLockObjectsNV(p->device_h, 1, &p->rtarget_h)) { MP_ERR(mapper, "Failed locking texture for access by OpenGL: %s\n", mp_LastError_to_str()); return -1; } return 0; } const struct ra_hwdec_driver ra_hwdec_dxva2gldx = { .name = "dxva2-dxinterop", .priv_size = sizeof(struct priv_owner), .imgfmts = {IMGFMT_DXVA2, 0}, .init = init, .uninit = uninit, .mapper = &(const struct ra_hwdec_mapper_driver){ .priv_size = sizeof(struct priv), .init = mapper_init, .uninit = mapper_uninit, .map = mapper_map, }, };
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; }
static int create(struct gl_hwdec *hw) { if (!check_hwdec(hw)) return -1; struct priv *p = talloc_zero(hw, struct priv); hw->priv = p; hw->gl->GenTextures(MP_MAX_PLANES, p->gl_planes); p->hwctx = (struct mp_hwdec_ctx){ .type = HWDEC_VIDEOTOOLBOX, .download_image = mp_vt_download_image, .ctx = &p->hwctx, }; hwdec_devices_add(hw->devs, &p->hwctx); return 0; } static int reinit(struct gl_hwdec *hw, struct mp_image_params *params) { struct priv *p = hw->priv; assert(params->imgfmt == hw->driver->imgfmt); if (!params->hw_subfmt) { MP_ERR(hw, "Unsupported CVPixelBuffer format.\n"); return -1; } if (!gl_get_imgfmt_desc(hw->gl, params->hw_subfmt, &p->desc)) { MP_ERR(hw, "Unsupported texture format.\n"); return -1; } params->imgfmt = params->hw_subfmt; params->hw_subfmt = 0; return 0; } static int map_frame(struct gl_hwdec *hw, struct mp_image *hw_image, struct gl_hwdec_frame *out_frame) { struct priv *p = hw->priv; GL *gl = hw->gl; CVPixelBufferRelease(p->pbuf); p->pbuf = (CVPixelBufferRef)hw_image->planes[3]; CVPixelBufferRetain(p->pbuf); IOSurfaceRef surface = CVPixelBufferGetIOSurface(p->pbuf); if (!surface) { MP_ERR(hw, "CVPixelBuffer has no IOSurface\n"); return -1; } const bool planar = CVPixelBufferIsPlanar(p->pbuf); const int planes = CVPixelBufferGetPlaneCount(p->pbuf); assert((planar && planes == p->desc.num_planes) || p->desc.num_planes == 1); GLenum gl_target = GL_TEXTURE_RECTANGLE; for (int i = 0; i < p->desc.num_planes; i++) { const struct gl_format *fmt = p->desc.planes[i]; gl->BindTexture(gl_target, p->gl_planes[i]); CGLError err = CGLTexImageIOSurface2D( CGLGetCurrentContext(), gl_target, fmt->internal_format, IOSurfaceGetWidthOfPlane(surface, i), IOSurfaceGetHeightOfPlane(surface, i), fmt->format, fmt->type, surface, i); if (err != kCGLNoError) MP_ERR(hw, "error creating IOSurface texture for plane %d: %s (%x)\n", i, CGLErrorString(err), gl->GetError()); gl->BindTexture(gl_target, 0); out_frame->planes[i] = (struct gl_hwdec_plane){ .gl_texture = p->gl_planes[i], .gl_target = gl_target, .tex_w = IOSurfaceGetWidthOfPlane(surface, i), .tex_h = IOSurfaceGetHeightOfPlane(surface, i), }; } snprintf(out_frame->swizzle, sizeof(out_frame->swizzle), "%s", p->desc.swizzle); return 0; } static void destroy(struct gl_hwdec *hw) { struct priv *p = hw->priv; GL *gl = hw->gl; CVPixelBufferRelease(p->pbuf); gl->DeleteTextures(MP_MAX_PLANES, p->gl_planes); hwdec_devices_remove(hw->devs, &p->hwctx); } const struct gl_hwdec_driver gl_hwdec_videotoolbox = { .name = "videotoolbox", .api = HWDEC_VIDEOTOOLBOX, .imgfmt = IMGFMT_VIDEOTOOLBOX, .create = create, .reinit = reinit, .map_frame = map_frame, .destroy = destroy, };