bool VdaMixer::upload(const VideoFrame &frame, bool /*deint*/) { Q_ASSERT(frame.format().imgfmt() == IMGFMT_VDA); CGLError error = kCGLNoError; for (auto &texture : m_textures) { const auto cgl = CGLGetCurrentContext(); const auto surface = CVPixelBufferGetIOSurface((CVPixelBufferRef)frame.data(3)); texture.bind(); const auto w = IOSurfaceGetWidthOfPlane(surface, texture.plane()); const auto h = IOSurfaceGetHeightOfPlane(surface, texture.plane()); if (_Change(error, CGLTexImageIOSurface2D(cgl, texture.target(), texture.format(), w, h, texture.transfer().format, texture.transfer().type, surface, texture.plane()))) { _Error("CGLError: %%(0x%%)", CGLErrorString(error), _N(error, 16)); return false; } } return true; }
bool CRendererVTB::UploadTexture(int index) { YUVBUFFER &buf = m_buffers[index]; YUVPLANE (&planes)[YuvImage::MAX_PLANES] = m_buffers[index].fields[0]; VTB::CVideoBufferVTB *vb = dynamic_cast<VTB::CVideoBufferVTB*>(buf.videoBuffer); if (!vb) { return false; } CVImageBufferRef cvBufferRef = vb->GetPB(); glEnable(m_textureTarget); // It is the fastest way to render a CVPixelBuffer backed // with an IOSurface as there is no CPU -> GPU upload. CGLContextObj cgl_ctx = (CGLContextObj)g_Windowing.GetCGLContextObj(); IOSurfaceRef surface = CVPixelBufferGetIOSurface(cvBufferRef); OSType format_type = IOSurfaceGetPixelFormat(surface); if (format_type != kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange) { return false; } GLsizei surfplanes = IOSurfaceGetPlaneCount(surface); if (surfplanes != 2) { return false; } GLsizei widthY = IOSurfaceGetWidthOfPlane(surface, 0); GLsizei widthUV = IOSurfaceGetWidthOfPlane(surface, 1); GLsizei heightY = IOSurfaceGetHeightOfPlane(surface, 0); GLsizei heightUV = IOSurfaceGetHeightOfPlane(surface, 1); glBindTexture(m_textureTarget, planes[0].id); CGLTexImageIOSurface2D(cgl_ctx, m_textureTarget, GL_LUMINANCE, widthY, heightY, GL_LUMINANCE, GL_UNSIGNED_BYTE, surface, 0); glTexParameteri(m_textureTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(m_textureTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(m_textureTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(m_textureTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glBindTexture(m_textureTarget, planes[1].id); CGLTexImageIOSurface2D(cgl_ctx, m_textureTarget, GL_LUMINANCE_ALPHA, widthUV, heightUV, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, surface, 1); glTexParameteri(m_textureTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(m_textureTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(m_textureTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(m_textureTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glBindTexture(m_textureTarget, 0); glDisable(m_textureTarget); CalculateTextureSourceRects(index, 3); return true; }
EGLSurface Display::createPBufferSurface(EGLConfig config, const EGLint *attribList, EGLClientBuffer clientBuffer) { EGLint width = -1, height = -1, ioSurfacePlane = -1; EGLenum textureFormat = EGL_NO_TEXTURE; EGLenum textureTarget = EGL_NO_TEXTURE; EGLenum clientBufferFormat = EGL_NO_TEXTURE; EGLenum clientBufferType = EGL_NO_TEXTURE; EGLBoolean largestPBuffer = EGL_FALSE; const Config *configuration = mConfigSet.get(config); if(attribList) { while(*attribList != EGL_NONE) { switch(attribList[0]) { case EGL_WIDTH: width = attribList[1]; break; case EGL_HEIGHT: height = attribList[1]; break; case EGL_LARGEST_PBUFFER: largestPBuffer = attribList[1]; break; case EGL_TEXTURE_FORMAT: switch(attribList[1]) { case EGL_NO_TEXTURE: case EGL_TEXTURE_RGB: case EGL_TEXTURE_RGBA: textureFormat = attribList[1]; break; default: return error(EGL_BAD_ATTRIBUTE, EGL_NO_SURFACE); } break; case EGL_TEXTURE_INTERNAL_FORMAT_ANGLE: switch(attribList[1]) { case GL_RED: case GL_R16UI: case GL_RG: case GL_BGRA_EXT: case GL_RGBA: clientBufferFormat = attribList[1]; break; default: return error(EGL_BAD_ATTRIBUTE, EGL_NO_SURFACE); } break; case EGL_TEXTURE_TYPE_ANGLE: switch(attribList[1]) { case GL_UNSIGNED_BYTE: case GL_UNSIGNED_SHORT: case GL_HALF_FLOAT_OES: case GL_HALF_FLOAT: clientBufferType = attribList[1]; break; default: return error(EGL_BAD_ATTRIBUTE, EGL_NO_SURFACE); } break; case EGL_IOSURFACE_PLANE_ANGLE: if(attribList[1] < 0) { return error(EGL_BAD_ATTRIBUTE, EGL_NO_SURFACE); } ioSurfacePlane = attribList[1]; break; case EGL_TEXTURE_TARGET: switch(attribList[1]) { case EGL_NO_TEXTURE: case EGL_TEXTURE_2D: case EGL_TEXTURE_RECTANGLE_ANGLE: textureTarget = attribList[1]; break; default: return error(EGL_BAD_ATTRIBUTE, EGL_NO_SURFACE); } break; case EGL_MIPMAP_TEXTURE: if(attribList[1] != EGL_FALSE) { UNIMPLEMENTED(); return error(EGL_BAD_MATCH, EGL_NO_SURFACE); } break; case EGL_VG_COLORSPACE: return error(EGL_BAD_MATCH, EGL_NO_SURFACE); case EGL_VG_ALPHA_FORMAT: return error(EGL_BAD_MATCH, EGL_NO_SURFACE); default: return error(EGL_BAD_ATTRIBUTE, EGL_NO_SURFACE); } attribList += 2; } } if(width < 0 || height < 0) { return error(EGL_BAD_PARAMETER, EGL_NO_SURFACE); } if(width == 0 || height == 0) { return error(EGL_BAD_ATTRIBUTE, EGL_NO_SURFACE); } if((textureFormat != EGL_NO_TEXTURE && textureTarget == EGL_NO_TEXTURE) || (textureFormat == EGL_NO_TEXTURE && textureTarget != EGL_NO_TEXTURE)) { return error(EGL_BAD_MATCH, EGL_NO_SURFACE); } if(!(configuration->mSurfaceType & EGL_PBUFFER_BIT)) { return error(EGL_BAD_MATCH, EGL_NO_SURFACE); } if(clientBuffer) { switch(clientBufferType) { case GL_UNSIGNED_BYTE: switch(clientBufferFormat) { case GL_RED: case GL_RG: case GL_BGRA_EXT: break; case GL_R16UI: case GL_RGBA: return error(EGL_BAD_ATTRIBUTE, EGL_NO_SURFACE); default: return error(EGL_BAD_PARAMETER, EGL_NO_SURFACE); } break; case GL_UNSIGNED_SHORT: switch(clientBufferFormat) { case GL_R16UI: break; case GL_RED: case GL_RG: case GL_BGRA_EXT: case GL_RGBA: return error(EGL_BAD_ATTRIBUTE, EGL_NO_SURFACE); default: return error(EGL_BAD_PARAMETER, EGL_NO_SURFACE); } break; case GL_HALF_FLOAT_OES: case GL_HALF_FLOAT: switch(clientBufferFormat) { case GL_RGBA: break; case GL_RED: case GL_R16UI: case GL_RG: case GL_BGRA_EXT: return error(EGL_BAD_ATTRIBUTE, EGL_NO_SURFACE); default: return error(EGL_BAD_PARAMETER, EGL_NO_SURFACE); } break; default: return error(EGL_BAD_PARAMETER, EGL_NO_SURFACE); } if(ioSurfacePlane < 0) { return error(EGL_BAD_PARAMETER, EGL_NO_SURFACE); } if(textureFormat != EGL_TEXTURE_RGBA) { return error(EGL_BAD_ATTRIBUTE, EGL_NO_SURFACE); } if(textureTarget != EGL_TEXTURE_RECTANGLE_ANGLE) { return error(EGL_BAD_ATTRIBUTE, EGL_NO_SURFACE); } #if defined(__APPLE__) IOSurfaceRef ioSurface = reinterpret_cast<IOSurfaceRef>(clientBuffer); size_t planeCount = IOSurfaceGetPlaneCount(ioSurface); if((static_cast<size_t>(width) > IOSurfaceGetWidthOfPlane(ioSurface, ioSurfacePlane)) || (static_cast<size_t>(height) > IOSurfaceGetHeightOfPlane(ioSurface, ioSurfacePlane)) || ((planeCount != 0) && static_cast<size_t>(ioSurfacePlane) >= planeCount)) { return error(EGL_BAD_ATTRIBUTE, EGL_NO_SURFACE); } #endif } else { if((textureFormat == EGL_TEXTURE_RGB && configuration->mBindToTextureRGB != EGL_TRUE) || ((textureFormat == EGL_TEXTURE_RGBA && configuration->mBindToTextureRGBA != EGL_TRUE))) { return error(EGL_BAD_ATTRIBUTE, EGL_NO_SURFACE); } } Surface *surface = new PBufferSurface(this, configuration, width, height, textureFormat, textureTarget, clientBufferFormat, clientBufferType, largestPBuffer, clientBuffer, ioSurfacePlane); if(!surface->initialize()) { surface->release(); return EGL_NO_SURFACE; } surface->addRef(); mSurfaceSet.insert(surface); return success(surface); }
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, };