vo_corevideo: restructure this video output
[mplayer.git] / libvo / vo_corevideo.m
blob57e93a9326e9d4a48641b7999cd0a0264fcb1438
1 /*
2  * CoreVideo video output driver
3  * Copyright (c) 2005 Nicolas Plourde <nicolasplourde@gmail.com>
4  *
5  * This file is part of MPlayer.
6  *
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.
11  *
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.
16  *
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.
20  */
22 #import "vo_corevideo.h"
24 // mplayer includes
25 #import "fastmemcpy.h"
26 #import "talloc.h"
27 #import "video_out.h"
28 #import "aspect.h"
29 #import "sub/font_load.h"
30 #import "sub/sub.h"
31 #import "subopt-helper.h"
33 #import "csputils.h"
34 #import "libmpcodecs/vfcap.h"
35 #import "libmpcodecs/mp_image.h"
36 #import "osd.h"
38 #import "gl_common.h"
39 #import "cocoa_common.h"
41 struct quad {
42     GLfloat lowerLeft[2];
43     GLfloat lowerRight[2];
44     GLfloat upperRight[2];
45     GLfloat upperLeft[2];
48 #define CV_VERTICES_PER_QUAD 6
49 #define CV_MAX_OSD_PARTS 20
51 struct osd_p {
52     GLuint tex[CV_MAX_OSD_PARTS];
53     NSRect tex_rect[CV_MAX_OSD_PARTS];
54     int tex_cnt;
57 struct priv {
58     MPGLContext *mpglctx;
59     OSType pixelFormat;
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;
67     struct quad *quad;
69     struct osd_p *osd;
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);
85     gl->LoadIdentity();
87     if (aspect_scaling()) {
88         int new_w, new_h;
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);
98     }
100     gl->Ortho(0, p->image_width, p->image_height, 0, -1.0, 1.0);
101     gl->MatrixMode(GL_MODELVIEW);
102     gl->LoadIdentity();
104     force_load_font = 1;
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)
135         gl->SwapInterval(1);
136     return 1;
139 static void release_cv_entities(void) {
140     CVPixelBufferRelease(p->pixelBuffer);
141     p->pixelBuffer = NULL;
142     CVOpenGLTextureRelease(p->texture);
143     p->texture = NULL;
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,
151                   uint32_t format)
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)
158         return -1;
159     if (p->mpglctx->setGlWindow(p->mpglctx) == SET_WINDOW_FAILED)
160         return -1;
162     init_gl(vo, vo->dwidth, vo->dheight);
164     return 0;
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,
176                                int stride)
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");
183         return;
184     }
186     if (osd->tex_cnt >= CV_MAX_OSD_PARTS) {
187         mp_msg(MSGT_VO, MSGL_ERR, "Too many OSD parts, contact the"
188                                   " developers!\n");
189         return;
190     }
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);
196     {
197         int i;
198         unsigned char *tmp = malloc(stride * h * 2);
199         // convert alpha from weird MPlayer scale.
200         for (i = 0; i < h * stride; i++) {
201             tmp[i*2+0] = src[i];
202             tmp[i*2+1] = -srca[i];
203         }
204         glUploadTex(gl, GL_TEXTURE_2D, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE,
205                     tmp, stride * 2, 0, 0, w, h, 0);
206         free(tmp);
207     }
209     osd->tex_rect[osd->tex_cnt] = NSMakeRect(x0, y0, w, h);
211     gl->BindTexture(GL_TEXTURE_2D, 0);
212     osd->tex_cnt++;
215 static void clearOSD(struct vo *vo)
217     struct osd_p *osd = p->osd;
218     GL *gl = p->mpglctx->gl;
220     if (!osd->tex_cnt)
221         return;
222     gl->DeleteTextures(osd->tex_cnt, osd->tex);
223     osd->tex_cnt = 0;
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)) {
232         clearOSD(vo);
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,
235                           vo);
236     }
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);
248         }
250         gl->Disable(GL_BLEND);
251         gl->BindTexture(GL_TEXTURE_2D, 0);
252     }
255 static void prepare_texture(void)
257     CVReturn error;
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;
275     prepare_texture();
277     float x0 = 0;
278     float y0 = 0;
279     float  w = p->image_width;
280     float  h = p->image_height;
282     // vertically flips the image
283     y0 += h;
284     h = -h;
286     float xm = x0 + w;
287     float ym = y0 + h;
289     gl->Enable(CVOpenGLTextureGetTarget(p->texture));
290     gl->BindTexture(
291             CVOpenGLTextureGetTarget(p->texture),
292             CVOpenGLTextureGetName(p->texture));
294     gl->Begin(GL_QUADS);
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);
299     gl->End();
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)
312     CVReturn error;
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);
327     }
329     do_render(vo);
330     return VO_TRUE;
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 |
337                       VOCAP_NOSLICES;
338     switch (format) {
339         case IMGFMT_YUY2:
340             p->pixelFormat = kYUVSPixelFormat;
341             return flags;
343         case IMGFMT_RGB24:
344             p->pixelFormat = k24RGBPixelFormat;
345             return flags;
347         case IMGFMT_ARGB:
348             p->pixelFormat = k32ARGBPixelFormat;
349             return flags;
351         case IMGFMT_BGRA:
352             p->pixelFormat = k32BGRAPixelFormat;
353             return flags;
354     }
355     return 0;
358 static void uninit(struct vo *vo)
360     uninit_mpglcontext(p->mpglctx);
361     release_cv_entities();
362     talloc_free(p);
366 static int preinit(struct vo *vo, const char *arg)
368     const opt_t subopts[] = {
369         {NULL}
370     };
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"
376                 "\n" );
377         return -1;
378     }
380     p = talloc_ptrtype(NULL, p);
381     *p = (struct priv) {
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),
386     };
388     *p->osd = (struct osd_p) {
389         .tex_cnt = 0,
390     };
392     return 0;
395 static CFStringRef get_cv_csp_matrix(void)
397     switch (p->colorspace.format) {
398         case MP_CSP_BT_601:
399             return kCVImageBufferYCbCrMatrix_ITU_R_601_4;
400         case MP_CSP_BT_709:
401             return kCVImageBufferYCbCrMatrix_ITU_R_709_2;
402         case MP_CSP_SMPTE_240M:
403             return kCVImageBufferYCbCrMatrix_SMPTE_240M_1995;
404     }
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)
418     switch (request) {
419         case VOCTRL_DRAW_IMAGE:
420             return draw_image(vo, data);
421         case VOCTRL_QUERY_FORMAT:
422             return query_format(*(uint32_t*)data);
423         case VOCTRL_ONTOP:
424             p->mpglctx->ontop(vo);
425             return VO_TRUE;
426         case VOCTRL_FULLSCREEN:
427             p->mpglctx->fullscreen(vo);
428             resize(vo, vo->dwidth, vo->dheight);
429             return VO_TRUE;
430         case VOCTRL_GET_PANSCAN:
431             return VO_TRUE;
432         case VOCTRL_SET_PANSCAN:
433             resize(vo, vo->dwidth, vo->dheight);
434             return VO_TRUE;
435         case VOCTRL_UPDATE_SCREENINFO:
436             p->mpglctx->update_xinerama_info(vo);
437             return VO_TRUE;
438         case VOCTRL_REDRAW_FRAME:
439             do_render(vo);
440             return VO_TRUE;
441         case VOCTRL_SET_YUV_COLORSPACE:
442             p->colorspace.format = ((struct mp_csp_details *)data)->format;
443             set_yuv_colorspace(vo);
444             return VO_TRUE;
445         case VOCTRL_GET_YUV_COLORSPACE:
446             *(struct mp_csp_details *)data = p->colorspace;
447             return VO_TRUE;
448     }
449     return VO_NOTIMPL;
452 const struct vo_driver video_out_corevideo = {
453     .is_new = true,
454     .info = &(const vo_info_t) {
455         "Mac OS X Core Video",
456         "corevideo",
457         "Nicolas Plourde <nicolas.plourde@gmail.com> and others",
458         ""
459     },
460     .preinit = preinit,
461     .config = config,
462     .control = control,
463     .draw_osd = draw_osd,
464     .flip_page = flip_page,
465     .check_events = check_events,
466     .uninit = uninit,