2 * CoreVideo video output driver
3 * Copyright (c) 2005 Nicolas Plourde <nicolasplourde@gmail.com>
5 * This file is part of MPlayer.
7 * MPlayer is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * MPlayer is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License along
18 * with MPlayer; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 #import "vo_corevideo.h"
25 #import "fastmemcpy.h"
30 #import "subopt-helper.h"
33 #import "libmpcodecs/vfcap.h"
34 #import "libmpcodecs/mp_image.h"
38 #import "cocoa_common.h"
42 GLfloat lowerRight[2];
43 GLfloat upperRight[2];
47 #define CV_VERTICES_PER_QUAD 6
48 #define CV_MAX_OSD_PARTS 20
51 GLuint tex[CV_MAX_OSD_PARTS];
52 NSRect tex_rect[CV_MAX_OSD_PARTS];
59 unsigned int image_width;
60 unsigned int image_height;
61 struct mp_csp_details colorspace;
63 CVPixelBufferRef pixelBuffer;
64 CVOpenGLTextureCacheRef textureCache;
65 CVOpenGLTextureRef texture;
71 static void resize(struct vo *vo, int width, int height)
73 struct priv *p = vo->priv;
74 GL *gl = p->mpglctx->gl;
75 p->image_width = width;
76 p->image_height = height;
78 mp_msg(MSGT_VO, MSGL_V, "[vo_corevideo] New OpenGL Viewport (0, 0, %d, "
79 "%d)\n", p->image_width, p->image_height);
81 gl->Viewport(0, 0, p->image_width, p->image_height);
82 gl->MatrixMode(GL_PROJECTION);
85 if (aspect_scaling()) {
87 GLdouble scale_x, scale_y;
89 aspect(vo, &new_w, &new_h, A_WINZOOM);
90 panscan_calc_windowed(vo);
91 new_w += vo->panscan_x;
92 new_h += vo->panscan_y;
93 scale_x = (GLdouble)new_w / (GLdouble)p->image_width;
94 scale_y = (GLdouble)new_h / (GLdouble)p->image_height;
95 gl->Scaled(scale_x, scale_y, 1);
98 gl->Ortho(0, p->image_width, p->image_height, 0, -1.0, 1.0);
99 gl->MatrixMode(GL_MODELVIEW);
102 vo_osd_changed(OSDTYPE_OSD);
104 gl->Clear(GL_COLOR_BUFFER_BIT);
105 vo->want_redraw = true;
108 static int init_gl(struct vo *vo, uint32_t d_width, uint32_t d_height)
110 struct priv *p = vo->priv;
111 GL *gl = p->mpglctx->gl;
113 const char *vendor = gl->GetString(GL_VENDOR);
114 const char *version = gl->GetString(GL_VERSION);
115 const char *renderer = gl->GetString(GL_RENDERER);
117 mp_msg(MSGT_VO, MSGL_V, "[vo_corevideo] Running on OpenGL '%s' by '%s',"
118 " version '%s'\n", renderer, vendor, version);
120 gl->Disable(GL_BLEND);
121 gl->Disable(GL_DEPTH_TEST);
122 gl->DepthMask(GL_FALSE);
123 gl->Disable(GL_CULL_FACE);
124 gl->Enable(GL_TEXTURE_2D);
125 gl->DrawBuffer(GL_BACK);
126 gl->TexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
128 resize(vo, d_width, d_height);
130 gl->ClearColor(0.0f, 0.0f, 0.0f, 0.0f);
131 gl->Clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
132 if (gl->SwapInterval)
137 static void release_cv_entities(struct vo *vo) {
138 struct priv *p = vo->priv;
139 CVPixelBufferRelease(p->pixelBuffer);
140 p->pixelBuffer = NULL;
141 CVOpenGLTextureRelease(p->texture);
143 CVOpenGLTextureCacheRelease(p->textureCache);
144 p->textureCache = NULL;
148 static int config(struct vo *vo, uint32_t width, uint32_t height,
149 uint32_t d_width, uint32_t d_height, uint32_t flags,
152 struct priv *p = vo->priv;
153 release_cv_entities(vo);
154 p->image_width = width;
155 p->image_height = height;
157 if (p->mpglctx->create_window(p->mpglctx, d_width, d_height, flags) < 0)
159 if (p->mpglctx->setGlWindow(p->mpglctx) == SET_WINDOW_FAILED)
162 init_gl(vo, vo->dwidth, vo->dheight);
167 static void check_events(struct vo *vo)
169 struct priv *p = vo->priv;
170 int e = p->mpglctx->check_events(vo);
171 if (e & VO_EVENT_RESIZE)
172 resize(vo, vo->dwidth, vo->dheight);
175 static void create_osd_texture(void *ctx, int x0, int y0, int w, int h,
176 unsigned char *src, unsigned char *srca,
179 struct priv *p = ((struct vo *) ctx)->priv;
180 struct osd_p *osd = p->osd;
181 GL *gl = p->mpglctx->gl;
183 if (w <= 0 || h <= 0 || stride < w) {
184 mp_msg(MSGT_VO, MSGL_V, "Invalid dimensions OSD for part!\n");
188 if (osd->tex_cnt >= CV_MAX_OSD_PARTS) {
189 mp_msg(MSGT_VO, MSGL_ERR, "Too many OSD parts, contact the"
194 gl->GenTextures(1, &osd->tex[osd->tex_cnt]);
195 gl->BindTexture(GL_TEXTURE_2D, osd->tex[osd->tex_cnt]);
196 glCreateClearTex(gl, GL_TEXTURE_2D, GL_LUMINANCE_ALPHA, GL_LUMINANCE_ALPHA,
197 GL_UNSIGNED_BYTE, GL_LINEAR, w, h, 0);
200 unsigned char *tmp = malloc(stride * h * 2);
201 // convert alpha from weird MPlayer scale.
202 for (i = 0; i < h * stride; i++) {
204 tmp[i*2+1] = -srca[i];
206 glUploadTex(gl, GL_TEXTURE_2D, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE,
207 tmp, stride * 2, 0, 0, w, h, 0);
211 osd->tex_rect[osd->tex_cnt] = NSMakeRect(x0, y0, w, h);
213 gl->BindTexture(GL_TEXTURE_2D, 0);
217 static void clearOSD(struct vo *vo)
219 struct priv *p = vo->priv;
220 struct osd_p *osd = p->osd;
221 GL *gl = p->mpglctx->gl;
225 gl->DeleteTextures(osd->tex_cnt, osd->tex);
229 static void draw_osd(struct vo *vo, struct osd_state *osd_s)
231 struct priv *p = vo->priv;
232 struct osd_p *osd = p->osd;
233 GL *gl = p->mpglctx->gl;
235 if (vo_osd_changed(0)) {
237 osd_draw_text_ext(osd_s, vo->dwidth, vo->dheight, 0, 0, 0, 0,
238 p->image_width, p->image_height, create_osd_texture,
242 if (osd->tex_cnt > 0) {
243 gl->Enable(GL_BLEND);
244 gl->BlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
246 for (int n = 0; n < osd->tex_cnt; n++) {
247 NSRect tr = osd->tex_rect[n];
248 gl->BindTexture(GL_TEXTURE_2D, osd->tex[n]);
249 glDrawTex(gl, tr.origin.x, tr.origin.y,
250 tr.size.width, tr.size.height,
251 0, 0, 1.0, 1.0, 1, 1, 0, 0, 0);
254 gl->Disable(GL_BLEND);
255 gl->BindTexture(GL_TEXTURE_2D, 0);
259 static void prepare_texture(struct vo *vo)
261 struct priv *p = vo->priv;
262 struct quad *q = p->quad;
265 CVOpenGLTextureRelease(p->texture);
266 error = CVOpenGLTextureCacheCreateTextureFromImage(NULL,
267 p->textureCache, p->pixelBuffer, 0, &p->texture);
268 if(error != kCVReturnSuccess)
269 mp_msg(MSGT_VO, MSGL_ERR,"[vo_corevideo] Failed to create OpenGL"
270 " texture(%d)\n", error);
272 CVOpenGLTextureGetCleanTexCoords(p->texture, q->lowerLeft, q->lowerRight,
273 q->upperRight, q->upperLeft);
276 static void do_render(struct vo *vo)
278 struct priv *p = vo->priv;
279 struct quad *q = p->quad;
280 GL *gl = p->mpglctx->gl;
285 float w = p->image_width;
286 float h = p->image_height;
288 // vertically flips the image
295 gl->Enable(CVOpenGLTextureGetTarget(p->texture));
297 CVOpenGLTextureGetTarget(p->texture),
298 CVOpenGLTextureGetName(p->texture));
301 gl->TexCoord2fv(q->lowerLeft); gl->Vertex2f(x0, y0);
302 gl->TexCoord2fv(q->upperLeft); gl->Vertex2f(x0, ym);
303 gl->TexCoord2fv(q->upperRight); gl->Vertex2f(xm, ym);
304 gl->TexCoord2fv(q->lowerRight); gl->Vertex2f(xm, y0);
307 gl->Disable(CVOpenGLTextureGetTarget(p->texture));
310 static void flip_page(struct vo *vo)
312 struct priv *p = vo->priv;
313 p->mpglctx->swapGlBuffers(p->mpglctx);
314 p->mpglctx->gl->Clear(GL_COLOR_BUFFER_BIT);
317 static uint32_t draw_image(struct vo *vo, mp_image_t *mpi)
319 struct priv *p = vo->priv;
322 if (!p->textureCache || !p->pixelBuffer) {
323 error = CVOpenGLTextureCacheCreate(NULL, 0, vo_cocoa_cgl_context(vo),
324 vo_cocoa_cgl_pixel_format(vo), 0, &p->textureCache);
325 if(error != kCVReturnSuccess)
326 mp_msg(MSGT_VO, MSGL_ERR,"[vo_corevideo] Failed to create OpenGL"
327 " texture Cache(%d)\n", error);
329 error = CVPixelBufferCreateWithBytes(NULL, mpi->width, mpi->height,
330 p->pixelFormat, mpi->planes[0], mpi->width * mpi->bpp / 8,
331 NULL, NULL, NULL, &p->pixelBuffer);
332 if(error != kCVReturnSuccess)
333 mp_msg(MSGT_VO, MSGL_ERR,"[vo_corevideo] Failed to create Pixel"
334 "Buffer(%d)\n", error);
341 static int query_format(struct vo *vo, uint32_t format)
343 struct priv *p = vo->priv;
344 const int flags = VFCAP_CSP_SUPPORTED | VFCAP_CSP_SUPPORTED_BY_HW |
345 VFCAP_OSD | VFCAP_HWSCALE_UP | VFCAP_HWSCALE_DOWN |
349 p->pixelFormat = kYUVSPixelFormat;
353 p->pixelFormat = k24RGBPixelFormat;
357 p->pixelFormat = k32ARGBPixelFormat;
361 p->pixelFormat = k32BGRAPixelFormat;
367 static void uninit(struct vo *vo)
369 struct priv *p = vo->priv;
370 uninit_mpglcontext(p->mpglctx);
371 release_cv_entities(vo);
375 static int preinit(struct vo *vo, const char *arg)
377 struct priv *p = vo->priv;
380 .mpglctx = init_mpglcontext(GLTYPE_COCOA, vo),
381 .colorspace = MP_CSP_DETAILS_DEFAULTS,
382 .quad = talloc_ptrtype(p, p->quad),
383 .osd = talloc_ptrtype(p, p->osd),
386 *p->osd = (struct osd_p) {
393 static CFStringRef get_cv_csp_matrix(struct vo *vo)
395 struct priv *p = vo->priv;
396 switch (p->colorspace.format) {
398 return kCVImageBufferYCbCrMatrix_ITU_R_601_4;
400 return kCVImageBufferYCbCrMatrix_ITU_R_709_2;
401 case MP_CSP_SMPTE_240M:
402 return kCVImageBufferYCbCrMatrix_SMPTE_240M_1995;
404 return kCVImageBufferYCbCrMatrix_ITU_R_601_4;
407 static void set_yuv_colorspace(struct vo *vo)
409 struct priv *p = vo->priv;
410 CVBufferSetAttachment(p->pixelBuffer,
411 kCVImageBufferYCbCrMatrixKey, get_cv_csp_matrix(vo),
412 kCVAttachmentMode_ShouldPropagate);
413 vo->want_redraw = true;
416 static int control(struct vo *vo, uint32_t request, void *data)
418 struct priv *p = vo->priv;
420 case VOCTRL_DRAW_IMAGE:
421 return draw_image(vo, data);
422 case VOCTRL_QUERY_FORMAT:
423 return query_format(vo, *(uint32_t*)data);
425 p->mpglctx->ontop(vo);
428 if (!p->mpglctx->pause)
430 p->mpglctx->pause(vo);
433 if (!p->mpglctx->resume)
435 p->mpglctx->resume(vo);
437 case VOCTRL_FULLSCREEN:
438 p->mpglctx->fullscreen(vo);
439 resize(vo, vo->dwidth, vo->dheight);
441 case VOCTRL_GET_PANSCAN:
443 case VOCTRL_SET_PANSCAN:
444 resize(vo, vo->dwidth, vo->dheight);
446 case VOCTRL_UPDATE_SCREENINFO:
447 p->mpglctx->update_xinerama_info(vo);
449 case VOCTRL_REDRAW_FRAME:
452 case VOCTRL_SET_YUV_COLORSPACE:
453 p->colorspace.format = ((struct mp_csp_details *)data)->format;
454 set_yuv_colorspace(vo);
456 case VOCTRL_GET_YUV_COLORSPACE:
457 *(struct mp_csp_details *)data = p->colorspace;
463 const struct vo_driver video_out_corevideo = {
465 .info = &(const vo_info_t) {
466 "Mac OS X Core Video",
468 "Nicolas Plourde <nicolas.plourde@gmail.com> and others",
474 .draw_osd = draw_osd,
475 .flip_page = flip_page,
476 .check_events = check_events,
478 .privsize = sizeof(struct priv),