summaryrefslogtreecommitdiffstats
path: root/video/out/vo_corevideo.c
diff options
context:
space:
mode:
Diffstat (limited to 'video/out/vo_corevideo.c')
-rw-r--r--video/out/vo_corevideo.c414
1 files changed, 309 insertions, 105 deletions
diff --git a/video/out/vo_corevideo.c b/video/out/vo_corevideo.c
index bad282436f..cd225aa17f 100644
--- a/video/out/vo_corevideo.c
+++ b/video/out/vo_corevideo.c
@@ -1,6 +1,7 @@
/*
* CoreVideo video output driver
* Copyright (c) 2005 Nicolas Plourde <nicolasplourde@gmail.com>
+ * Copyright (c) 2012-2013 Stefano Pigozzi <stefano.pigozzi@gmail.com>
*
* This file is part of MPlayer.
*
@@ -19,7 +20,13 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
+#include "config.h"
+
#include <QuartzCore/QuartzCore.h>
+#if CONFIG_VDA
+#include <IOSurface/IOSurface.h>
+#endif
+
#include <assert.h>
#include "talloc.h"
@@ -42,9 +49,32 @@ struct quad {
GLfloat upperLeft[2];
};
+struct cv_priv {
+ CVPixelBufferRef pbuf;
+ CVOpenGLTextureCacheRef texture_cache;
+ CVOpenGLTextureRef texture;
+ OSType pixfmt;
+};
+
+struct dr_priv {
+ CVPixelBufferRef pbuf;
+ bool texture_allocated;
+ GLuint texture;
+ GLuint texture_target;
+};
+
+struct cv_functions {
+ void (*init)(struct vo *vo);
+ void (*uninit)(struct vo *vo);
+ void (*prepare_texture)(struct vo *vo, struct mp_image *mpi);
+ void (*bind_texture)(struct vo *vo);
+ void (*unbind_texture)(struct vo *vo);
+ mp_image_t *(*get_screenshot)(struct vo *vo);
+ int (*set_colormatrix)(struct vo *vo, struct mp_csp_details *csp);
+};
+
struct priv {
MPGLContext *mpglctx;
- OSType pixelFormat;
unsigned int image_width;
unsigned int image_height;
struct mp_csp_details colorspace;
@@ -52,12 +82,21 @@ struct priv {
struct mp_rect dst_rect;
struct mp_osd_res osd_res;
- CVPixelBufferRef pixelBuffer;
- CVOpenGLTextureCacheRef textureCache;
- CVOpenGLTextureRef texture;
- struct quad *quad;
+ // state for normal CoreVideo rendering path: uploads mp_image data as
+ // OpenGL textures.
+ struct cv_priv cv;
+
+ // state for IOSurface based direct rendering path: accesses the IOSurface
+ // wrapped by the CVPixelBuffer returned by VDADecoder and directly
+ // renders it to the screen.
+ struct dr_priv dr;
+ struct quad *quad;
struct mpgl_osd *osd;
+
+ // functions to to deal with the the OpenGL texture for containing the
+ // video frame (behaviour changes depending on the rendering path).
+ struct cv_functions fns;
};
static void resize(struct vo *vo)
@@ -98,17 +137,8 @@ static int init_gl(struct vo *vo, uint32_t d_width, uint32_t d_height)
gl->Clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
if (gl->SwapInterval)
gl->SwapInterval(1);
- return 1;
-}
-static void release_cv_entities(struct vo *vo) {
- struct priv *p = vo->priv;
- CVPixelBufferRelease(p->pixelBuffer);
- p->pixelBuffer = NULL;
- CVOpenGLTextureRelease(p->texture);
- p->texture = NULL;
- CVOpenGLTextureCacheRelease(p->textureCache);
- p->textureCache = NULL;
+ return 1;
}
static int config(struct vo *vo, uint32_t width, uint32_t height,
@@ -116,7 +146,8 @@ static int config(struct vo *vo, uint32_t width, uint32_t height,
uint32_t format)
{
struct priv *p = vo->priv;
- release_cv_entities(vo);
+ p->fns.uninit(vo);
+
p->image_width = width;
p->image_height = height;
@@ -125,26 +156,11 @@ static int config(struct vo *vo, uint32_t width, uint32_t height,
return -1;
init_gl(vo, vo->dwidth, vo->dheight);
+ p->fns.init(vo);
return 0;
}
-static void prepare_texture(struct vo *vo)
-{
- struct priv *p = vo->priv;
- struct quad *q = p->quad;
- CVReturn error;
-
- CVOpenGLTextureRelease(p->texture);
- error = CVOpenGLTextureCacheCreateTextureFromImage(NULL,
- p->textureCache, p->pixelBuffer, 0, &p->texture);
- if(error != kCVReturnSuccess)
- MP_ERR(vo, "Failed to create OpenGL texture(%d)\n", error);
-
- CVOpenGLTextureGetCleanTexCoords(p->texture, q->lowerLeft, q->lowerRight,
- q->upperRight, q->upperLeft);
-}
-
// map x/y (in range 0..1) to the video texture, and emit OpenGL vertexes
static void video_vertex(struct vo *vo, float x, float y)
{
@@ -172,12 +188,8 @@ static void do_render(struct vo *vo)
{
struct priv *p = vo->priv;
GL *gl = p->mpglctx->gl;
- prepare_texture(vo);
- gl->Enable(CVOpenGLTextureGetTarget(p->texture));
- gl->BindTexture(
- CVOpenGLTextureGetTarget(p->texture),
- CVOpenGLTextureGetName(p->texture));
+ p->fns.bind_texture(vo);
gl->Begin(GL_QUADS);
video_vertex(vo, 0, 0);
@@ -186,7 +198,7 @@ static void do_render(struct vo *vo)
video_vertex(vo, 1, 0);
gl->End();
- gl->Disable(CVOpenGLTextureGetTarget(p->texture));
+ p->fns.unbind_texture(vo);
}
static void flip_page(struct vo *vo)
@@ -196,61 +208,19 @@ static void flip_page(struct vo *vo)
p->mpglctx->gl->Clear(GL_COLOR_BUFFER_BIT);
}
-static void draw_image(struct vo *vo, mp_image_t *mpi)
+static void draw_image(struct vo *vo, struct mp_image *mpi)
{
struct priv *p = vo->priv;
- CVReturn error;
-
- if (!p->textureCache || !p->pixelBuffer) {
- error = CVOpenGLTextureCacheCreate(NULL, 0, vo_cocoa_cgl_context(vo),
- vo_cocoa_cgl_pixel_format(vo), 0, &p->textureCache);
- if(error != kCVReturnSuccess)
- MP_ERR(vo, "Failed to create OpenGL texture Cache(%d)\n", error);
-
- error = CVPixelBufferCreateWithBytes(NULL, mpi->w, mpi->h,
- p->pixelFormat, mpi->planes[0], mpi->stride[0],
- NULL, NULL, NULL, &p->pixelBuffer);
- if(error != kCVReturnSuccess)
- MP_ERR(vo, "Failed to create PixelBuffer(%d)\n", error);
- }
-
+ p->fns.prepare_texture(vo, mpi);
do_render(vo);
}
-static int query_format(struct vo *vo, uint32_t format)
-{
- struct priv *p = vo->priv;
- const int flags = VFCAP_CSP_SUPPORTED | VFCAP_CSP_SUPPORTED_BY_HW;
- switch (format) {
- case IMGFMT_YUYV:
- p->pixelFormat = kYUVSPixelFormat;
- return flags;
-
- case IMGFMT_UYVY:
- p->pixelFormat = k2vuyPixelFormat;
- return flags;
-
- case IMGFMT_RGB24:
- p->pixelFormat = k24RGBPixelFormat;
- return flags;
-
- case IMGFMT_ARGB:
- p->pixelFormat = k32ARGBPixelFormat;
- return flags;
-
- case IMGFMT_BGRA:
- p->pixelFormat = k32BGRAPixelFormat;
- return flags;
- }
- return 0;
-}
-
static void uninit(struct vo *vo)
{
struct priv *p = vo->priv;
if (p->osd)
mpgl_osd_destroy(p->osd);
- release_cv_entities(vo);
+ p->fns.uninit(vo);
mpgl_uninit(p->mpglctx);
}
@@ -287,23 +257,38 @@ static CFStringRef get_cv_csp_matrix(struct vo *vo)
case MP_CSP_SMPTE_240M:
return kCVImageBufferYCbCrMatrix_SMPTE_240M_1995;
default:
- return kCVImageBufferYCbCrMatrix_ITU_R_601_4;
+ return NULL;
}
}
-static void set_yuv_colorspace(struct vo *vo)
+static int set_yuv_colorspace(struct vo *vo, CVPixelBufferRef pbuf,
+ struct mp_csp_details *csp)
{
struct priv *p = vo->priv;
- CVBufferSetAttachment(p->pixelBuffer,
- kCVImageBufferYCbCrMatrixKey, get_cv_csp_matrix(vo),
- kCVAttachmentMode_ShouldPropagate);
- vo->want_redraw = true;
+ p->colorspace = *csp;
+ CFStringRef cv_csp = get_cv_csp_matrix(vo);
+
+ if (cv_csp) {
+ CVBufferSetAttachment(p->cv.pbuf, kCVImageBufferYCbCrMatrixKey, cv_csp,
+ kCVAttachmentMode_ShouldNotPropagate);
+ vo->want_redraw = true;
+ return VO_TRUE;
+ } else {
+ return VO_NOTIMPL;
+ }
}
-static int get_image_fmt(struct vo *vo)
+static int get_yuv_colorspace(struct vo *vo, struct mp_csp_details *csp)
{
struct priv *p = vo->priv;
- switch (p->pixelFormat) {
+ *csp = p->colorspace;
+ return VO_TRUE;
+}
+
+static int get_image_fmt(struct vo *vo, CVPixelBufferRef pbuf)
+{
+ OSType pixfmt = CVPixelBufferGetPixelFormatType(pbuf);
+ switch (pixfmt) {
case kYUVSPixelFormat: return IMGFMT_YUYV;
case k2vuyPixelFormat: return IMGFMT_UYVY;
case k24RGBPixelFormat: return IMGFMT_RGB24;
@@ -311,21 +296,21 @@ static int get_image_fmt(struct vo *vo)
case k32BGRAPixelFormat: return IMGFMT_BGRA;
}
MP_ERR(vo, "Failed to convert pixel format. Please contact the "
- "developers. PixelFormat: %d\n", p->pixelFormat);
+ "developers. PixelFormat: %d\n", pixfmt);
return -1;
}
-static mp_image_t *get_screenshot(struct vo *vo)
+static mp_image_t *get_screenshot(struct vo *vo, CVPixelBufferRef pbuf)
{
- int img_fmt = get_image_fmt(vo);
+ int img_fmt = get_image_fmt(vo, pbuf);
if (img_fmt < 0) return NULL;
struct priv *p = vo->priv;
- void *base = CVPixelBufferGetBaseAddress(p->pixelBuffer);
-
- size_t width = CVPixelBufferGetWidth(p->pixelBuffer);
- size_t height = CVPixelBufferGetHeight(p->pixelBuffer);
- size_t stride = CVPixelBufferGetBytesPerRow(p->pixelBuffer);
+ CVPixelBufferLockBaseAddress(pbuf, 0);
+ void *base = CVPixelBufferGetBaseAddress(pbuf);
+ size_t width = CVPixelBufferGetWidth(pbuf);
+ size_t height = CVPixelBufferGetHeight(pbuf);
+ size_t stride = CVPixelBufferGetBytesPerRow(pbuf);
struct mp_image img = {0};
mp_image_setfmt(&img, img_fmt);
@@ -336,6 +321,7 @@ static mp_image_t *get_screenshot(struct vo *vo)
struct mp_image *image = mp_image_new_copy(&img);
mp_image_set_display_size(image, vo->aspdat.prew, vo->aspdat.preh);
mp_image_set_colorspace_details(image, &p->colorspace);
+ CVPixelBufferUnlockBaseAddress(pbuf, 0);
return image;
}
@@ -353,18 +339,15 @@ static int control(struct vo *vo, uint32_t request, void *data)
do_render(vo);
return VO_TRUE;
case VOCTRL_SET_YUV_COLORSPACE:
- p->colorspace.format = ((struct mp_csp_details *)data)->format;
- set_yuv_colorspace(vo);
- return VO_TRUE;
+ return p->fns.set_colormatrix(vo, data);
case VOCTRL_GET_YUV_COLORSPACE:
- *(struct mp_csp_details *)data = p->colorspace;
- return VO_TRUE;
+ return get_yuv_colorspace(vo, data);
case VOCTRL_SCREENSHOT: {
struct voctrl_screenshot_args *args = data;
if (args->full_window)
args->out_image = glGetWindowScreenshot(p->mpglctx->gl);
else
- args->out_image = get_screenshot(vo);
+ args->out_image = p->fns.get_screenshot(vo);
return VO_TRUE;
}
}
@@ -377,6 +360,227 @@ static int control(struct vo *vo, uint32_t request, void *data)
return r;
}
+static void dummy_cb(struct vo *vo) { }
+
+static void cv_uninit(struct vo *vo)
+{
+ struct priv *p = vo->priv;
+ CVPixelBufferRelease(p->cv.pbuf);
+ p->cv.pbuf = NULL;
+ CVOpenGLTextureRelease(p->cv.texture);
+ p->cv.texture = NULL;
+ CVOpenGLTextureCacheRelease(p->cv.texture_cache);
+ p->cv.texture_cache = NULL;
+}
+
+static void cv_bind_texture(struct vo *vo)
+{
+ struct priv *p = vo->priv;
+ GL *gl = p->mpglctx->gl;
+
+ gl->Enable(CVOpenGLTextureGetTarget(p->cv.texture));
+ gl->BindTexture(CVOpenGLTextureGetTarget(p->cv.texture),
+ CVOpenGLTextureGetName(p->cv.texture));
+
+}
+
+static void cv_unbind_texture(struct vo *vo)
+{
+ struct priv *p = vo->priv;
+ GL *gl = p->mpglctx->gl;
+
+ gl->Disable(CVOpenGLTextureGetTarget(p->cv.texture));
+}
+
+static void upload_opengl_texture(struct vo *vo, struct mp_image *mpi)
+{
+ struct priv *p = vo->priv;
+ if (!p->cv.texture_cache || !p->cv.pbuf) {
+ CVReturn error;
+ error = CVOpenGLTextureCacheCreate(NULL, 0, vo_cocoa_cgl_context(vo),
+ vo_cocoa_cgl_pixel_format(vo), 0, &p->cv.texture_cache);
+ if(error != kCVReturnSuccess)
+ MP_ERR(vo, "Failed to create OpenGL texture Cache(%d)\n", error);
+
+ error = CVPixelBufferCreateWithBytes(NULL, mpi->w, mpi->h,
+ p->cv.pixfmt, mpi->planes[0], mpi->stride[0],
+ NULL, NULL, NULL, &p->cv.pbuf);
+ if(error != kCVReturnSuccess)
+ MP_ERR(vo, "Failed to create PixelBuffer(%d)\n", error);
+ }
+
+ struct quad *q = p->quad;
+ CVReturn error;
+
+ CVOpenGLTextureRelease(p->cv.texture);
+ error = CVOpenGLTextureCacheCreateTextureFromImage(NULL,
+ p->cv.texture_cache, p->cv.pbuf, 0, &p->cv.texture);
+ if(error != kCVReturnSuccess)
+ MP_ERR(vo, "Failed to create OpenGL texture(%d)\n", error);
+
+ CVOpenGLTextureGetCleanTexCoords(p->cv.texture,
+ q->lowerLeft, q->lowerRight, q->upperRight, q->upperLeft);
+}
+
+static mp_image_t *cv_get_screenshot(struct vo *vo)
+{
+ struct priv *p = vo->priv;
+ return get_screenshot(vo, p->cv.pbuf);
+}
+
+static int cv_set_colormatrix(struct vo *vo, struct mp_csp_details *csp)
+{
+ struct priv *p = vo->priv;
+ return set_yuv_colorspace(vo, p->cv.pbuf, csp);
+}
+
+static struct cv_functions cv_functions = {
+ .init = dummy_cb,
+ .uninit = cv_uninit,
+ .bind_texture = cv_bind_texture,
+ .unbind_texture = cv_unbind_texture,
+ .prepare_texture = upload_opengl_texture,
+ .get_screenshot = cv_get_screenshot,
+ .set_colormatrix = cv_set_colormatrix,
+};
+
+#if CONFIG_VDA
+static void iosurface_init(struct vo *vo)
+{
+ struct priv *p = vo->priv;
+ GL *gl = p->mpglctx->gl;
+
+ p->dr.texture_target = GL_TEXTURE_RECTANGLE_ARB;
+ p->fns.bind_texture(vo);
+ gl->GenTextures(1, &p->dr.texture);
+ p->fns.unbind_texture(vo);
+
+ p->dr.texture_allocated = true;
+}
+
+static void iosurface_uninit(struct vo *vo)
+{
+ struct priv *p = vo->priv;
+ GL *gl = p->mpglctx->gl;
+ if (p->dr.texture_allocated) {
+ gl->DeleteTextures(1, &p->dr.texture);
+ p->dr.texture_allocated = false;
+ }
+}
+
+static void iosurface_bind_texture(struct vo *vo)
+{
+ struct priv *p = vo->priv;
+ GL *gl = p->mpglctx->gl;
+
+ gl->Enable(p->dr.texture_target);
+ gl->BindTexture(p->dr.texture_target, p->dr.texture);
+ gl->MatrixMode(GL_TEXTURE);
+ gl->LoadIdentity();
+ gl->TexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+}
+
+static void iosurface_unbind_texture(struct vo *vo)
+{
+ struct priv *p = vo->priv;
+ GL *gl = p->mpglctx->gl;
+
+ gl->BindTexture(p->dr.texture_target, 0);
+ gl->Disable(p->dr.texture_target);
+}
+
+static void extract_texture_from_iosurface(struct vo *vo, struct mp_image *mpi)
+{
+ struct priv *p = vo->priv;
+ CVPixelBufferRelease(p->dr.pbuf);
+ p->dr.pbuf = (CVPixelBufferRef)mpi->planes[3];
+ CVPixelBufferRetain(p->dr.pbuf);
+ IOSurfaceRef surface = CVPixelBufferGetIOSurface(p->dr.pbuf);
+ MP_DBG(vo, "iosurface id: %d\n", IOSurfaceGetID(surface));
+
+ p->fns.bind_texture(vo);
+
+ CGLError err = CGLTexImageIOSurface2D(
+ vo_cocoa_cgl_context(vo), p->dr.texture_target, GL_RGB8,
+ p->image_width, p->image_height,
+ GL_YCBCR_422_APPLE, GL_UNSIGNED_SHORT_8_8_APPLE, surface, 0);
+
+ if (err != kCGLNoError)
+ MP_ERR(vo, "error creating IOSurface texture: %s (%x)\n",
+ CGLErrorString(err), glGetError());
+
+ p->fns.unbind_texture(vo);
+
+ // video_vertex flips the coordinates.. so feed in a flipped quad
+ *p->quad = (struct quad) {
+ .lowerRight = { p->image_width, p->image_height },
+ .upperLeft = { 0.0, 0.0 },
+ };
+}
+
+static mp_image_t *iosurface_get_screenshot(struct vo *vo)
+{
+ struct priv *p = vo->priv;
+ return get_screenshot(vo, p->dr.pbuf);
+}
+
+static int iosurface_set_colormatrix(struct vo *vo, struct mp_csp_details *csp)
+{
+ struct priv *p = vo->priv;
+ return set_yuv_colorspace(vo, p->dr.pbuf, csp);
+}
+
+static struct cv_functions iosurface_functions = {
+ .init = iosurface_init,
+ .uninit = iosurface_uninit,
+ .bind_texture = iosurface_bind_texture,
+ .unbind_texture = iosurface_unbind_texture,
+ .prepare_texture = extract_texture_from_iosurface,
+ .get_screenshot = iosurface_get_screenshot,
+ .set_colormatrix = iosurface_set_colormatrix,
+};
+#endif /* CONFIG_VDA */
+
+static int query_format(struct vo *vo, uint32_t format)
+{
+ struct priv *p = vo->priv;
+ const int flags = VFCAP_CSP_SUPPORTED | VFCAP_CSP_SUPPORTED_BY_HW;
+
+ switch (format) {
+#if CONFIG_VDA
+ case IMGFMT_VDA:
+ p->fns = iosurface_functions;
+ return flags;
+#endif
+
+ case IMGFMT_YUYV:
+ p->fns = cv_functions;
+ p->cv.pixfmt = kYUVSPixelFormat;
+ return flags;
+
+ case IMGFMT_UYVY:
+ p->fns = cv_functions;
+ p->cv.pixfmt = k2vuyPixelFormat;
+ return flags;
+
+ case IMGFMT_RGB24:
+ p->fns = cv_functions;
+ p->cv.pixfmt = k24RGBPixelFormat;
+ return flags;
+
+ case IMGFMT_ARGB:
+ p->fns = cv_functions;
+ p->cv.pixfmt = k32ARGBPixelFormat;
+ return flags;
+
+ case IMGFMT_BGRA:
+ p->fns = cv_functions;
+ p->cv.pixfmt = k32BGRAPixelFormat;
+ return flags;
+ }
+ return 0;
+}
+
const struct vo_driver video_out_corevideo = {
.info = &(const vo_info_t) {
"Mac OS X Core Video",