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"
29 #import "sub/font_load.h"
31 #import "subopt-helper.h"
34 #import "libmpcodecs/vfcap.h"
35 #import "libmpcodecs/mp_image.h"
39 #import "cocoa_common.h"
43 GLfloat lowerRight[2];
44 GLfloat upperRight[2];
48 #define CV_VERTICES_PER_QUAD 6
49 #define CV_MAX_OSD_PARTS 20
52 GLuint tex[CV_MAX_OSD_PARTS];
53 NSRect tex_rect[CV_MAX_OSD_PARTS];
60 unsigned int image_width;
61 unsigned int image_height;
62 struct mp_csp_details colorspace;
64 CVPixelBufferRef pixelBuffer;
65 CVOpenGLTextureCacheRef textureCache;
66 CVOpenGLTextureRef texture;
72 static struct priv *p;
74 static void resize(struct vo *vo, int width, int height)
76 GL *gl = p->mpglctx->gl;
77 p->image_width = width;
78 p->image_height = height;
80 mp_msg(MSGT_VO, MSGL_V, "[vo_corevideo] New OpenGL Viewport (0, 0, %d, "
81 "%d)\n", p->image_width, p->image_height);
83 gl->Viewport(0, 0, p->image_width, p->image_height);
84 gl->MatrixMode(GL_PROJECTION);
87 if (aspect_scaling()) {
89 GLdouble scale_x, scale_y;
91 aspect(vo, &new_w, &new_h, A_WINZOOM);
92 panscan_calc_windowed(vo);
93 new_w += vo->panscan_x;
94 new_h += vo->panscan_y;
95 scale_x = (GLdouble)new_w / (GLdouble)p->image_width;
96 scale_y = (GLdouble)new_h / (GLdouble)p->image_height;
97 gl->Scaled(scale_x, scale_y, 1);
100 gl->Ortho(0, p->image_width, p->image_height, 0, -1.0, 1.0);
101 gl->MatrixMode(GL_MODELVIEW);
105 vo_osd_changed(OSDTYPE_OSD);
107 gl->Clear(GL_COLOR_BUFFER_BIT);
108 vo->want_redraw = true;
111 static int init_gl(struct vo *vo, uint32_t d_width, uint32_t d_height)
113 GL *gl = p->mpglctx->gl;
115 const char *vendor = gl->GetString(GL_VENDOR);
116 const char *version = gl->GetString(GL_VERSION);
117 const char *renderer = gl->GetString(GL_RENDERER);
119 mp_msg(MSGT_VO, MSGL_V, "[vo_corevideo] Running on OpenGL '%s' by '%s',"
120 " version '%s'\n", renderer, vendor, version);
122 gl->Disable(GL_BLEND);
123 gl->Disable(GL_DEPTH_TEST);
124 gl->DepthMask(GL_FALSE);
125 gl->Disable(GL_CULL_FACE);
126 gl->Enable(GL_TEXTURE_2D);
127 gl->DrawBuffer(GL_BACK);
128 gl->TexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
130 resize(vo, d_width, d_height);
132 gl->ClearColor(0.0f, 0.0f, 0.0f, 0.0f);
133 gl->Clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
134 if (gl->SwapInterval)
139 static void release_cv_entities(void) {
140 CVPixelBufferRelease(p->pixelBuffer);
141 p->pixelBuffer = NULL;
142 CVOpenGLTextureRelease(p->texture);
144 CVOpenGLTextureCacheRelease(p->textureCache);
145 p->textureCache = NULL;
149 static int config(struct vo *vo, uint32_t width, uint32_t height,
150 uint32_t d_width, uint32_t d_height, uint32_t flags,
153 release_cv_entities();
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 int e = p->mpglctx->check_events(vo);
170 if (e & VO_EVENT_RESIZE)
171 resize(vo, vo->dwidth, vo->dheight);
174 static void create_osd_texture(void *ctx, int x0, int y0, int w, int h,
175 unsigned char *src, unsigned char *srca,
178 struct osd_p *osd = p->osd;
179 GL *gl = p->mpglctx->gl;
181 if (w <= 0 || h <= 0 || stride < w) {
182 mp_msg(MSGT_VO, MSGL_V, "Invalid dimensions OSD for part!\n");
186 if (osd->tex_cnt >= CV_MAX_OSD_PARTS) {
187 mp_msg(MSGT_VO, MSGL_ERR, "Too many OSD parts, contact the"
192 gl->GenTextures(1, &osd->tex[osd->tex_cnt]);
193 gl->BindTexture(GL_TEXTURE_2D, osd->tex[osd->tex_cnt]);
194 glCreateClearTex(gl, GL_TEXTURE_2D, GL_LUMINANCE_ALPHA, GL_LUMINANCE_ALPHA,
195 GL_UNSIGNED_BYTE, GL_LINEAR, w, h, 0);
198 unsigned char *tmp = malloc(stride * h * 2);
199 // convert alpha from weird MPlayer scale.
200 for (i = 0; i < h * stride; i++) {
202 tmp[i*2+1] = -srca[i];
204 glUploadTex(gl, GL_TEXTURE_2D, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE,
205 tmp, stride * 2, 0, 0, w, h, 0);
209 osd->tex_rect[osd->tex_cnt] = NSMakeRect(x0, y0, w, h);
211 gl->BindTexture(GL_TEXTURE_2D, 0);
215 static void clearOSD(struct vo *vo)
217 struct osd_p *osd = p->osd;
218 GL *gl = p->mpglctx->gl;
222 gl->DeleteTextures(osd->tex_cnt, osd->tex);
226 static void draw_osd(struct vo *vo, struct osd_state *osd_s)
228 struct osd_p *osd = p->osd;
229 GL *gl = p->mpglctx->gl;
231 if (vo_osd_changed(0)) {
233 osd_draw_text_ext(osd_s, vo->dwidth, vo->dheight, 0, 0, 0, 0,
234 p->image_width, p->image_height, create_osd_texture,
238 if (osd->tex_cnt > 0) {
239 gl->Enable(GL_BLEND);
240 gl->BlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
242 for (int n = 0; n < osd->tex_cnt; n++) {
243 NSRect tr = osd->tex_rect[n];
244 gl->BindTexture(GL_TEXTURE_2D, osd->tex[n]);
245 glDrawTex(gl, tr.origin.x, tr.origin.y,
246 tr.size.width, tr.size.height,
247 0, 0, 1.0, 1.0, 1, 1, 0, 0, 0);
250 gl->Disable(GL_BLEND);
251 gl->BindTexture(GL_TEXTURE_2D, 0);
255 static void prepare_texture(void)
258 struct quad *q = p->quad;
260 CVOpenGLTextureRelease(p->texture);
261 error = CVOpenGLTextureCacheCreateTextureFromImage(NULL,
262 p->textureCache, p->pixelBuffer, 0, &p->texture);
263 if(error != kCVReturnSuccess)
264 mp_msg(MSGT_VO, MSGL_ERR,"[vo_corevideo] Failed to create OpenGL"
265 " texture(%d)\n", error);
267 CVOpenGLTextureGetCleanTexCoords(p->texture, q->lowerLeft, q->lowerRight,
268 q->upperRight, q->upperLeft);
271 static void do_render(struct vo *vo)
273 struct quad *q = p->quad;
274 GL *gl = p->mpglctx->gl;
279 float w = p->image_width;
280 float h = p->image_height;
282 // vertically flips the image
289 gl->Enable(CVOpenGLTextureGetTarget(p->texture));
291 CVOpenGLTextureGetTarget(p->texture),
292 CVOpenGLTextureGetName(p->texture));
295 gl->TexCoord2fv(q->lowerLeft); gl->Vertex2f(x0, y0);
296 gl->TexCoord2fv(q->upperLeft); gl->Vertex2f(x0, ym);
297 gl->TexCoord2fv(q->upperRight); gl->Vertex2f(xm, ym);
298 gl->TexCoord2fv(q->lowerRight); gl->Vertex2f(xm, y0);
301 gl->Disable(CVOpenGLTextureGetTarget(p->texture));
304 static void flip_page(struct vo *vo)
306 p->mpglctx->swapGlBuffers(p->mpglctx);
307 p->mpglctx->gl->Clear(GL_COLOR_BUFFER_BIT);
310 static uint32_t draw_image(struct vo *vo, mp_image_t *mpi)
314 if (!p->textureCache || !p->pixelBuffer) {
315 error = CVOpenGLTextureCacheCreate(NULL, 0, vo_cocoa_cgl_context(),
316 vo_cocoa_cgl_pixel_format(), 0, &p->textureCache);
317 if(error != kCVReturnSuccess)
318 mp_msg(MSGT_VO, MSGL_ERR,"[vo_corevideo] Failed to create OpenGL"
319 " texture Cache(%d)\n", error);
321 error = CVPixelBufferCreateWithBytes(NULL, mpi->width, mpi->height,
322 p->pixelFormat, mpi->planes[0], mpi->width * mpi->bpp / 8,
323 NULL, NULL, NULL, &p->pixelBuffer);
324 if(error != kCVReturnSuccess)
325 mp_msg(MSGT_VO, MSGL_ERR,"[vo_corevideo] Failed to create Pixel"
326 "Buffer(%d)\n", error);
333 static int query_format(uint32_t format)
335 const int flags = VFCAP_CSP_SUPPORTED | VFCAP_CSP_SUPPORTED_BY_HW |
336 VFCAP_OSD | VFCAP_HWSCALE_UP | VFCAP_HWSCALE_DOWN |
340 p->pixelFormat = kYUVSPixelFormat;
344 p->pixelFormat = k24RGBPixelFormat;
348 p->pixelFormat = k32ARGBPixelFormat;
352 p->pixelFormat = k32BGRAPixelFormat;
358 static void uninit(struct vo *vo)
360 uninit_mpglcontext(p->mpglctx);
361 release_cv_entities();
366 static int preinit(struct vo *vo, const char *arg)
368 const opt_t subopts[] = {
372 if (subopt_parse(arg, subopts) != 0) {
373 mp_msg(MSGT_VO, MSGL_FATAL,
374 "\n-vo corevideo command line help:\n"
375 "Example: mplayer -vo corevideo\n"
380 p = talloc_ptrtype(NULL, p);
382 .mpglctx = init_mpglcontext(GLTYPE_COCOA, vo),
383 .colorspace = MP_CSP_DETAILS_DEFAULTS,
384 .quad = talloc_ptrtype(p, p->quad),
385 .osd = talloc_ptrtype(p, p->osd),
388 *p->osd = (struct osd_p) {
395 static CFStringRef get_cv_csp_matrix(void)
397 switch (p->colorspace.format) {
399 return kCVImageBufferYCbCrMatrix_ITU_R_601_4;
401 return kCVImageBufferYCbCrMatrix_ITU_R_709_2;
402 case MP_CSP_SMPTE_240M:
403 return kCVImageBufferYCbCrMatrix_SMPTE_240M_1995;
405 return kCVImageBufferYCbCrMatrix_ITU_R_601_4;
408 static void set_yuv_colorspace(struct vo *vo)
410 CVBufferSetAttachment(p->pixelBuffer,
411 kCVImageBufferYCbCrMatrixKey, get_cv_csp_matrix(),
412 kCVAttachmentMode_ShouldPropagate);
413 vo->want_redraw = true;
416 static int control(struct vo *vo, uint32_t request, void *data)
419 case VOCTRL_DRAW_IMAGE:
420 return draw_image(vo, data);
421 case VOCTRL_QUERY_FORMAT:
422 return query_format(*(uint32_t*)data);
424 p->mpglctx->ontop(vo);
426 case VOCTRL_FULLSCREEN:
427 p->mpglctx->fullscreen(vo);
428 resize(vo, vo->dwidth, vo->dheight);
430 case VOCTRL_GET_PANSCAN:
432 case VOCTRL_SET_PANSCAN:
433 resize(vo, vo->dwidth, vo->dheight);
435 case VOCTRL_UPDATE_SCREENINFO:
436 p->mpglctx->update_xinerama_info(vo);
438 case VOCTRL_REDRAW_FRAME:
441 case VOCTRL_SET_YUV_COLORSPACE:
442 p->colorspace.format = ((struct mp_csp_details *)data)->format;
443 set_yuv_colorspace(vo);
445 case VOCTRL_GET_YUV_COLORSPACE:
446 *(struct mp_csp_details *)data = p->colorspace;
452 const struct vo_driver video_out_corevideo = {
454 .info = &(const vo_info_t) {
455 "Mac OS X Core Video",
457 "Nicolas Plourde <nicolas.plourde@gmail.com> and others",
463 .draw_osd = draw_osd,
464 .flip_page = flip_page,
465 .check_events = check_events,