diff options
Diffstat (limited to 'video/out/vo_corevideo.m')
-rw-r--r-- | video/out/vo_corevideo.m | 457 |
1 files changed, 457 insertions, 0 deletions
diff --git a/video/out/vo_corevideo.m b/video/out/vo_corevideo.m new file mode 100644 index 0000000000..5116ab653c --- /dev/null +++ b/video/out/vo_corevideo.m @@ -0,0 +1,457 @@ +/* + * CoreVideo video output driver + * Copyright (c) 2005 Nicolas Plourde <nicolasplourde@gmail.com> + * + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <assert.h> + +#import "vo_corevideo.h" + +// mplayer includes +#import "fastmemcpy.h" +#import "talloc.h" +#import "video_out.h" +#import "aspect.h" +#import "sub/sub.h" +#import "subopt-helper.h" + +#import "csputils.h" +#import "libmpcodecs/vfcap.h" +#import "libmpcodecs/mp_image.h" + +#import "gl_common.h" +#import "gl_osd.h" +#import "cocoa_common.h" + +struct quad { + GLfloat lowerLeft[2]; + GLfloat lowerRight[2]; + GLfloat upperRight[2]; + GLfloat upperLeft[2]; +}; + +struct priv { + MPGLContext *mpglctx; + OSType pixelFormat; + unsigned int image_width; + unsigned int image_height; + struct mp_csp_details colorspace; + int ass_border_x, ass_border_y; + + CVPixelBufferRef pixelBuffer; + CVOpenGLTextureCacheRef textureCache; + CVOpenGLTextureRef texture; + struct quad *quad; + + struct mpgl_osd *osd; +}; + +static void resize(struct vo *vo, int width, int height) +{ + struct priv *p = vo->priv; + GL *gl = p->mpglctx->gl; + + gl->Viewport(0, 0, width, height); + gl->MatrixMode(GL_PROJECTION); + gl->LoadIdentity(); + p->ass_border_x = p->ass_border_y = 0; + if (aspect_scaling()) { + int new_w, new_h; + GLdouble scale_x, scale_y; + + aspect(vo, &new_w, &new_h, A_WINZOOM); + panscan_calc_windowed(vo); + new_w += vo->panscan_x; + new_h += vo->panscan_y; + scale_x = (GLdouble)new_w / (GLdouble)width; + scale_y = (GLdouble)new_h / (GLdouble)height; + gl->Scaled(scale_x, scale_y, 1); + p->ass_border_x = (vo->dwidth - new_w) / 2; + p->ass_border_y = (vo->dheight - new_h) / 2; + } + + gl->Ortho(0, p->image_width, p->image_height, 0, -1.0, 1.0); + gl->MatrixMode(GL_MODELVIEW); + gl->LoadIdentity(); + + gl->Clear(GL_COLOR_BUFFER_BIT); + vo->want_redraw = true; +} + +static int init_gl(struct vo *vo, uint32_t d_width, uint32_t d_height) +{ + struct priv *p = vo->priv; + GL *gl = p->mpglctx->gl; + + const char *vendor = gl->GetString(GL_VENDOR); + const char *version = gl->GetString(GL_VERSION); + const char *renderer = gl->GetString(GL_RENDERER); + + mp_msg(MSGT_VO, MSGL_V, "[vo_corevideo] Running on OpenGL '%s' by '%s'," + " version '%s'\n", renderer, vendor, version); + + gl->Disable(GL_BLEND); + gl->Disable(GL_DEPTH_TEST); + gl->DepthMask(GL_FALSE); + gl->Disable(GL_CULL_FACE); + gl->Enable(GL_TEXTURE_2D); + gl->DrawBuffer(GL_BACK); + gl->TexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + + if (!p->osd) + p->osd = mpgl_osd_init(gl, true); + + resize(vo, d_width, d_height); + + gl->ClearColor(0.0f, 0.0f, 0.0f, 0.0f); + 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; +} + +static int config(struct vo *vo, uint32_t width, uint32_t height, + uint32_t d_width, uint32_t d_height, uint32_t flags, + uint32_t format) +{ + struct priv *p = vo->priv; + release_cv_entities(vo); + p->image_width = width; + p->image_height = height; + + int mpgl_caps = MPGL_CAP_GL_LEGACY; + if (!mpgl_create_window(p->mpglctx, mpgl_caps, d_width, d_height, flags)) + return -1; + + init_gl(vo, vo->dwidth, vo->dheight); + + return 0; +} + +static void check_events(struct vo *vo) +{ + struct priv *p = vo->priv; + int e = p->mpglctx->check_events(vo); + if (e & VO_EVENT_RESIZE) + resize(vo, vo->dwidth, vo->dheight); +} + +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_msg(MSGT_VO, MSGL_ERR,"[vo_corevideo] Failed to create OpenGL" + " texture(%d)\n", error); + + CVOpenGLTextureGetCleanTexCoords(p->texture, q->lowerLeft, q->lowerRight, + q->upperRight, q->upperLeft); +} + +static void do_render(struct vo *vo) +{ + struct priv *p = vo->priv; + struct quad *q = p->quad; + GL *gl = p->mpglctx->gl; + prepare_texture(vo); + + float x0 = 0; + float y0 = 0; + float w = p->image_width; + float h = p->image_height; + + // vertically flips the image + y0 += h; + h = -h; + + float xm = x0 + w; + float ym = y0 + h; + + gl->Enable(CVOpenGLTextureGetTarget(p->texture)); + gl->BindTexture( + CVOpenGLTextureGetTarget(p->texture), + CVOpenGLTextureGetName(p->texture)); + + gl->Begin(GL_QUADS); + gl->TexCoord2fv(q->lowerLeft); gl->Vertex2f(x0, y0); + gl->TexCoord2fv(q->upperLeft); gl->Vertex2f(x0, ym); + gl->TexCoord2fv(q->upperRight); gl->Vertex2f(xm, ym); + gl->TexCoord2fv(q->lowerRight); gl->Vertex2f(xm, y0); + gl->End(); + + gl->Disable(CVOpenGLTextureGetTarget(p->texture)); +} + +static void flip_page(struct vo *vo) +{ + struct priv *p = vo->priv; + p->mpglctx->swapGlBuffers(p->mpglctx); + p->mpglctx->gl->Clear(GL_COLOR_BUFFER_BIT); +} + +static uint32_t draw_image(struct vo *vo, mp_image_t *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_msg(MSGT_VO, MSGL_ERR,"[vo_corevideo] Failed to create OpenGL" + " texture Cache(%d)\n", error); + + error = CVPixelBufferCreateWithBytes(NULL, mpi->width, mpi->height, + p->pixelFormat, mpi->planes[0], mpi->width * mpi->bpp / 8, + NULL, NULL, NULL, &p->pixelBuffer); + if(error != kCVReturnSuccess) + mp_msg(MSGT_VO, MSGL_ERR,"[vo_corevideo] Failed to create Pixel" + "Buffer(%d)\n", error); + } + + do_render(vo); + return VO_TRUE; +} + +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 | + VFCAP_OSD | VFCAP_HWSCALE_UP | VFCAP_HWSCALE_DOWN | + VOCAP_NOSLICES; + switch (format) { + case IMGFMT_YUY2: + p->pixelFormat = kYUVSPixelFormat; + 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); + mpgl_uninit(p->mpglctx); +} + + +static int preinit(struct vo *vo, const char *arg) +{ + struct priv *p = vo->priv; + + *p = (struct priv) { + .mpglctx = mpgl_init(GLTYPE_COCOA, vo), + .colorspace = MP_CSP_DETAILS_DEFAULTS, + .quad = talloc_ptrtype(p, p->quad), + }; + + return 0; +} + +static void draw_osd(struct vo *vo, struct osd_state *osd) +{ + struct priv *p = vo->priv; + GL *gl = p->mpglctx->gl; + assert(p->osd); + + gl->MatrixMode(GL_PROJECTION); + gl->PushMatrix(); + gl->LoadIdentity(); + gl->Ortho(0, vo->dwidth, vo->dheight, 0, -1, 1); + + struct mp_osd_res res = { + .w = vo->dwidth, + .h = vo->dheight, + .display_par = vo->monitor_par, + .video_par = vo->aspdat.par, + }; + + if (aspect_scaling()) { + res.ml = res.mr = p->ass_border_x; + res.mt = res.mb = p->ass_border_y; + } + + mpgl_osd_draw_legacy(p->osd, osd, res); + + gl->PopMatrix(); +} + +static CFStringRef get_cv_csp_matrix(struct vo *vo) +{ + struct priv *p = vo->priv; + switch (p->colorspace.format) { + case MP_CSP_BT_601: + return kCVImageBufferYCbCrMatrix_ITU_R_601_4; + case MP_CSP_BT_709: + return kCVImageBufferYCbCrMatrix_ITU_R_709_2; + case MP_CSP_SMPTE_240M: + return kCVImageBufferYCbCrMatrix_SMPTE_240M_1995; + } + return kCVImageBufferYCbCrMatrix_ITU_R_601_4; +} + +static void set_yuv_colorspace(struct vo *vo) +{ + struct priv *p = vo->priv; + CVBufferSetAttachment(p->pixelBuffer, + kCVImageBufferYCbCrMatrixKey, get_cv_csp_matrix(vo), + kCVAttachmentMode_ShouldPropagate); + vo->want_redraw = true; +} + +static int get_image_fmt(struct vo *vo) +{ + struct priv *p = vo->priv; + switch (p->pixelFormat) { + case kYUVSPixelFormat: return IMGFMT_YUY2; + case k24RGBPixelFormat: return IMGFMT_RGB24; + case k32ARGBPixelFormat: return IMGFMT_ARGB; + case k32BGRAPixelFormat: return IMGFMT_BGRA; + } + mp_msg(MSGT_VO, MSGL_ERR, "[vo_corevideo] Failed to convert pixel format. " + "Please contact the developers. PixelFormat: %d\n", p->pixelFormat); + return -1; +} + +static mp_image_t *get_screenshot(struct vo *vo) +{ + int img_fmt = get_image_fmt(vo); + 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); + size_t image_size = stride * height; + + mp_image_t *image = alloc_mpi(width, height, img_fmt); + memcpy(image->planes[0], base, image_size); + image->stride[0] = stride; + + image->display_w = vo->aspdat.prew; + image->display_h = vo->aspdat.preh; + + mp_image_set_colorspace_details(image, &p->colorspace); + + return image; +} + +static int control(struct vo *vo, uint32_t request, void *data) +{ + struct priv *p = vo->priv; + switch (request) { + case VOCTRL_DRAW_IMAGE: + return draw_image(vo, data); + case VOCTRL_QUERY_FORMAT: + return query_format(vo, *(uint32_t*)data); + case VOCTRL_ONTOP: + p->mpglctx->ontop(vo); + return VO_TRUE; + case VOCTRL_PAUSE: + if (!p->mpglctx->pause) + break; + p->mpglctx->pause(vo); + return VO_TRUE; + case VOCTRL_RESUME: + if (!p->mpglctx->resume) + break; + p->mpglctx->resume(vo); + return VO_TRUE; + case VOCTRL_FULLSCREEN: + p->mpglctx->fullscreen(vo); + resize(vo, vo->dwidth, vo->dheight); + return VO_TRUE; + case VOCTRL_GET_PANSCAN: + return VO_TRUE; + case VOCTRL_SET_PANSCAN: + resize(vo, vo->dwidth, vo->dheight); + return VO_TRUE; + case VOCTRL_UPDATE_SCREENINFO: + p->mpglctx->update_xinerama_info(vo); + return VO_TRUE; + case VOCTRL_REDRAW_FRAME: + 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; + case VOCTRL_GET_YUV_COLORSPACE: + *(struct mp_csp_details *)data = p->colorspace; + return VO_TRUE; + 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); + return VO_TRUE; + } + } + return VO_NOTIMPL; +} + +const struct vo_driver video_out_corevideo = { + .is_new = true, + .info = &(const vo_info_t) { + "Mac OS X Core Video", + "corevideo", + "Nicolas Plourde <nicolas.plourde@gmail.com> and others", + "" + }, + .preinit = preinit, + .config = config, + .control = control, + .draw_osd = draw_osd, + .flip_page = flip_page, + .check_events = check_events, + .uninit = uninit, + .priv_size = sizeof(struct priv), +}; |