1/** 2 * @file opengl.m Video driver for OpenGL on MacOSX 3 * 4 * Copyright (C) 2010 - 2015 Creytiv.com 5 */ 6#include <Cocoa/Cocoa.h> 7#include <OpenGL/gl.h> 8#include <OpenGL/glext.h> 9#include <re.h> 10#include <rem.h> 11#include <baresip.h> 12 13 14/** 15 * @defgroup opengl opengl 16 * 17 * Video display module for OpenGL on MacOSX 18 */ 19 20 21#if (MAC_OS_X_VERSION_MAX_ALLOWED >= 101200) 22#define NSTitledWindowMask NSWindowStyleMaskTitled 23#define NSClosableWindowMask NSWindowStyleMaskClosable 24#define NSMiniaturizableWindowMask NSWindowStyleMaskMiniaturizable 25#endif 26 27 28struct vidisp_st { 29 const struct vidisp *vd; /**< Inheritance (1st) */ 30 struct vidsz size; /**< Current size */ 31 NSOpenGLContext *ctx; 32 NSWindow *win; 33 GLhandleARB PHandle; 34 char *prog; 35}; 36 37 38static struct vidisp *vid; /**< OPENGL Video-display */ 39 40 41static const char *FProgram= 42 "uniform sampler2DRect Ytex;\n" 43 "uniform sampler2DRect Utex,Vtex;\n" 44 "void main(void) {\n" 45 " float nx,ny,r,g,b,y,u,v;\n" 46 " vec4 txl,ux,vx;" 47 " nx=gl_TexCoord[0].x;\n" 48 " ny=%d.0-gl_TexCoord[0].y;\n" 49 " y=texture2DRect(Ytex,vec2(nx,ny)).r;\n" 50 " u=texture2DRect(Utex,vec2(nx/2.0,ny/2.0)).r;\n" 51 " v=texture2DRect(Vtex,vec2(nx/2.0,ny/2.0)).r;\n" 52 53 " y=1.1643*(y-0.0625);\n" 54 " u=u-0.5;\n" 55 " v=v-0.5;\n" 56 57 " r=y+1.5958*v;\n" 58 " g=y-0.39173*u-0.81290*v;\n" 59 " b=y+2.017*u;\n" 60 61 " gl_FragColor=vec4(r,g,b,1.0);\n" 62 "}\n"; 63 64 65static void destructor(void *arg) 66{ 67 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 68 struct vidisp_st *st = arg; 69 70 if (st->ctx) { 71 [st->ctx clearDrawable]; 72 [st->ctx release]; 73 } 74 75 [st->win close]; 76 77 if (st->PHandle) { 78 glUseProgramObjectARB(0); 79 glDeleteObjectARB(st->PHandle); 80 } 81 82 mem_deref(st->prog); 83 84 [pool release]; 85} 86 87 88static int create_window(struct vidisp_st *st) 89{ 90 NSRect rect = NSMakeRect(0, 0, 100, 100); 91 NSUInteger style; 92 93 if (st->win) 94 return 0; 95 96 style = NSTitledWindowMask | 97 NSClosableWindowMask | 98 NSMiniaturizableWindowMask; 99 100 st->win = [[NSWindow alloc] initWithContentRect:rect 101 styleMask:style 102 backing:NSBackingStoreBuffered 103 defer:FALSE]; 104 if (!st->win) { 105 warning("opengl: could not create NSWindow\n"); 106 return ENOMEM; 107 } 108 109 [st->win setLevel:NSFloatingWindowLevel]; 110 111 return 0; 112} 113 114 115static void opengl_reset(struct vidisp_st *st, const struct vidsz *sz) 116{ 117 if (st->PHandle) { 118 glUseProgramObjectARB(0); 119 glDeleteObjectARB(st->PHandle); 120 st->PHandle = 0; 121 st->prog = mem_deref(st->prog); 122 } 123 124 st->size = *sz; 125} 126 127 128static int setup_shader(struct vidisp_st *st, int width, int height) 129{ 130 GLhandleARB FSHandle, PHandle; 131 const char *progv[1]; 132 char buf[1024]; 133 int err, i; 134 135 if (st->PHandle) 136 return 0; 137 138 err = re_sdprintf(&st->prog, FProgram, height); 139 if (err) 140 return err; 141 142 glMatrixMode(GL_PROJECTION); 143 glLoadIdentity(); 144 glOrtho(0, width, 0, height, -1, 1); 145 glViewport(0, 0, width, height); 146 glClearColor(0, 0, 0, 0); 147 glColor3f(1.0f, 0.84f, 0.0f); 148 glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST); 149 150 /* Set up program objects. */ 151 PHandle = glCreateProgramObjectARB(); 152 FSHandle = glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB); 153 154 /* Compile the shader. */ 155 progv[0] = st->prog; 156 glShaderSourceARB(FSHandle, 1, progv, NULL); 157 glCompileShaderARB(FSHandle); 158 159 /* Print the compilation log. */ 160 glGetObjectParameterivARB(FSHandle, GL_OBJECT_COMPILE_STATUS_ARB, &i); 161 if (i != 1) { 162 warning("opengl: shader compile failed\n"); 163 return ENOSYS; 164 } 165 166 glGetInfoLogARB(FSHandle, sizeof(buf), NULL, buf); 167 168 /* Create a complete program object. */ 169 glAttachObjectARB(PHandle, FSHandle); 170 glLinkProgramARB(PHandle); 171 172 /* And print the link log. */ 173 glGetInfoLogARB(PHandle, sizeof(buf), NULL, buf); 174 175 /* Finally, use the program. */ 176 glUseProgramObjectARB(PHandle); 177 178 st->PHandle = PHandle; 179 180 return 0; 181} 182 183 184static int alloc(struct vidisp_st **stp, const struct vidisp *vd, 185 struct vidisp_prm *prm, const char *dev, 186 vidisp_resize_h *resizeh, void *arg) 187{ 188 NSOpenGLPixelFormatAttribute attr[] = { 189 NSOpenGLPFAColorSize, 32, 190 NSOpenGLPFADepthSize, 16, 191 NSOpenGLPFADoubleBuffer, 192 0 193 }; 194 NSOpenGLPixelFormat *fmt; 195 NSAutoreleasePool *pool; 196 struct vidisp_st *st; 197 GLint vsync = 1; 198 int err = 0; 199 200 (void)dev; 201 (void)resizeh; 202 (void)arg; 203 204 pool = [[NSAutoreleasePool alloc] init]; 205 if (!pool) 206 return ENOMEM; 207 208 st = mem_zalloc(sizeof(*st), destructor); 209 if (!st) 210 return ENOMEM; 211 212 st->vd = vd; 213 214 fmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:attr]; 215 if (!fmt) { 216 err = ENOMEM; 217 warning("opengl: Failed creating OpenGL format\n"); 218 goto out; 219 } 220 221 st->ctx = [[NSOpenGLContext alloc] initWithFormat:fmt 222 shareContext:nil]; 223 224 [fmt release]; 225 226 if (!st->ctx) { 227 err = ENOMEM; 228 warning("opengl: Failed creating OpenGL context\n"); 229 goto out; 230 } 231 232 /* Use provided view, or create our own */ 233 if (prm && prm->view) { 234 [st->ctx setView:prm->view]; 235 } 236 else { 237 err = create_window(st); 238 if (err) 239 goto out; 240 241 if (prm) 242 prm->view = [st->win contentView]; 243 } 244 245 /* Enable vertical sync */ 246 [st->ctx setValues:&vsync forParameter:NSOpenGLCPSwapInterval]; 247 248 out: 249 if (err) 250 mem_deref(st); 251 else 252 *stp = st; 253 254 [pool release]; 255 256 return err; 257} 258 259 260static inline void draw_yuv(GLhandleARB PHandle, int height, 261 const uint8_t *Ytex, int linesizeY, 262 const uint8_t *Utex, int linesizeU, 263 const uint8_t *Vtex, int linesizeV) 264{ 265 int i; 266 267 /* This might not be required, but should not hurt. */ 268 glEnable(GL_TEXTURE_2D); 269 270 /* Select texture unit 1 as the active unit and bind the U texture. */ 271 glActiveTexture(GL_TEXTURE1); 272 i = glGetUniformLocationARB(PHandle, "Utex"); 273 glUniform1iARB(i,1); /* Bind Utex to texture unit 1 */ 274 glBindTexture(GL_TEXTURE_RECTANGLE_EXT,1); 275 276 glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, 277 GL_TEXTURE_MAG_FILTER,GL_LINEAR); 278 glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, 279 GL_TEXTURE_MIN_FILTER,GL_LINEAR); 280 glTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_DECAL); 281 glTexImage2D(GL_TEXTURE_RECTANGLE_EXT,0,GL_LUMINANCE, 282 linesizeU, height/2, 0, 283 GL_LUMINANCE,GL_UNSIGNED_BYTE,Utex); 284 285 /* Select texture unit 2 as the active unit and bind the V texture. */ 286 glActiveTexture(GL_TEXTURE2); 287 i = glGetUniformLocationARB(PHandle, "Vtex"); 288 glBindTexture(GL_TEXTURE_RECTANGLE_EXT,2); 289 glUniform1iARB(i,2); /* Bind Vtext to texture unit 2 */ 290 291 glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, 292 GL_TEXTURE_MAG_FILTER,GL_LINEAR); 293 glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, 294 GL_TEXTURE_MIN_FILTER,GL_LINEAR); 295 296 glTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_DECAL); 297 glTexImage2D(GL_TEXTURE_RECTANGLE_EXT,0,GL_LUMINANCE, 298 linesizeV, height/2, 0, 299 GL_LUMINANCE,GL_UNSIGNED_BYTE,Vtex); 300 301 /* Select texture unit 0 as the active unit and bind the Y texture. */ 302 glActiveTexture(GL_TEXTURE0); 303 i = glGetUniformLocationARB(PHandle,"Ytex"); 304 glUniform1iARB(i,0); /* Bind Ytex to texture unit 0 */ 305 glBindTexture(GL_TEXTURE_RECTANGLE_EXT,3); 306 307 glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, 308 GL_TEXTURE_MAG_FILTER,GL_LINEAR); 309 glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, 310 GL_TEXTURE_MIN_FILTER,GL_LINEAR); 311 glTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_DECAL); 312 313 glTexImage2D(GL_TEXTURE_RECTANGLE_EXT, 0, GL_LUMINANCE, 314 linesizeY, height, 0, 315 GL_LUMINANCE, GL_UNSIGNED_BYTE, Ytex); 316} 317 318 319static inline void draw_blit(int width, int height) 320{ 321 glClear(GL_COLOR_BUFFER_BIT); 322 323 /* Draw image */ 324 325 glBegin(GL_QUADS); 326 { 327 glTexCoord2i(0, 0); 328 glVertex2i(0, 0); 329 glTexCoord2i(width, 0); 330 glVertex2i(width, 0); 331 glTexCoord2i(width, height); 332 glVertex2i(width, height); 333 glTexCoord2i(0, height); 334 glVertex2i(0, height); 335 } 336 glEnd(); 337} 338 339 340static inline void draw_rgb(const uint8_t *pic, int w, int h) 341{ 342 glEnable(GL_TEXTURE_RECTANGLE_EXT); 343 glBindTexture(GL_TEXTURE_RECTANGLE_EXT, 1); 344 345 glTextureRangeAPPLE(GL_TEXTURE_RECTANGLE_EXT, w * h * 2, pic); 346 347 glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, 348 GL_TEXTURE_STORAGE_HINT_APPLE, 349 GL_STORAGE_SHARED_APPLE); 350 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE); 351 352 glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MIN_FILTER, 353 GL_NEAREST); 354 glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MAG_FILTER, 355 GL_NEAREST); 356 glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_WRAP_S, 357 GL_CLAMP_TO_EDGE); 358 glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_WRAP_T, 359 GL_CLAMP_TO_EDGE); 360 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); 361 362 glTexImage2D(GL_TEXTURE_RECTANGLE_EXT, 0, GL_RGBA, w, h, 0, 363 GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, pic); 364 365 /* draw */ 366 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 367 glEnable(GL_TEXTURE_2D); 368 369 glViewport(0, 0, w, h); 370 371 glMatrixMode(GL_PROJECTION); 372 glLoadIdentity(); 373 374 glOrtho( (GLfloat)0, (GLfloat)w, (GLfloat)0, (GLfloat)h, -1.0, 1.0); 375 376 glBindTexture(GL_TEXTURE_RECTANGLE_EXT, 1); 377 378 glMatrixMode(GL_TEXTURE); 379 glLoadIdentity(); 380 381 glBegin(GL_QUADS); 382 { 383 glTexCoord2f(0.0f, 0.0f); 384 glVertex2f(0.0f, h); 385 glTexCoord2f(0.0f, h); 386 glVertex2f(0.0f, 0.0f); 387 glTexCoord2f(w, h); 388 glVertex2f(w, 0.0f); 389 glTexCoord2f(w, 0.0f); 390 glVertex2f(w, h); 391 } 392 glEnd(); 393} 394 395 396static int display(struct vidisp_st *st, const char *title, 397 const struct vidframe *frame) 398{ 399 NSAutoreleasePool *pool; 400 bool upd = false; 401 int err = 0; 402 403 pool = [[NSAutoreleasePool alloc] init]; 404 if (!pool) 405 return ENOMEM; 406 407 if (!vidsz_cmp(&st->size, &frame->size)) { 408 if (st->size.w && st->size.h) { 409 info("opengl: reset: %u x %u ---> %u x %u\n", 410 st->size.w, st->size.h, 411 frame->size.w, frame->size.h); 412 } 413 414 opengl_reset(st, &frame->size); 415 416 upd = true; 417 } 418 419 if (upd && st->win) { 420 421 const NSSize size = {frame->size.w, frame->size.h}; 422 char capt[256]; 423 424 [st->win setContentSize:size]; 425 426 if (title) { 427 re_snprintf(capt, sizeof(capt), "%s - %u x %u", 428 title, frame->size.w, frame->size.h); 429 } 430 else { 431 re_snprintf(capt, sizeof(capt), "%u x %u", 432 frame->size.w, frame->size.h); 433 } 434 435 [st->win setTitle:[NSString stringWithUTF8String:capt]]; 436 437 [st->win makeKeyAndOrderFront:nil]; 438 [st->win display]; 439 [st->win center]; 440 441 [st->ctx clearDrawable]; 442 [st->ctx setView:[st->win contentView]]; 443 } 444 445 [st->ctx makeCurrentContext]; 446 447 if (frame->fmt == VID_FMT_YUV420P) { 448 449 if (!st->PHandle) { 450 451 debug("opengl: using Vertex shader with YUV420P\n"); 452 453 err = setup_shader(st, frame->size.w, frame->size.h); 454 if (err) 455 goto out; 456 } 457 458 draw_yuv(st->PHandle, frame->size.h, 459 frame->data[0], frame->linesize[0], 460 frame->data[1], frame->linesize[1], 461 frame->data[2], frame->linesize[2]); 462 draw_blit(frame->size.w, frame->size.h); 463 } 464 else if (frame->fmt == VID_FMT_RGB32) { 465 466 glClearColor(0, 0, 0, 0); 467 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 468 469 glViewport(0, 0, frame->size.w, frame->size.h); 470 471 glMatrixMode(GL_PROJECTION); 472 glLoadIdentity(); 473 474 glMatrixMode(GL_MODELVIEW); 475 glLoadIdentity(); 476 477 draw_rgb(frame->data[0], frame->size.w, frame->size.h); 478 } 479 else { 480 warning("opengl: unknown pixel format %s\n", 481 vidfmt_name(frame->fmt)); 482 err = EINVAL; 483 } 484 485 [st->ctx flushBuffer]; 486 487 out: 488 [pool release]; 489 490 return err; 491} 492 493 494static void hide(struct vidisp_st *st) 495{ 496 if (!st) 497 return; 498 499 [st->win orderOut:nil]; 500} 501 502 503static int module_init(void) 504{ 505 NSApplication *app; 506 int err; 507 508 app = [NSApplication sharedApplication]; 509 if (!app) 510 return ENOSYS; 511 512 err = vidisp_register(&vid, baresip_vidispl(), 513 "opengl", alloc, NULL, display, hide); 514 if (err) 515 return err; 516 517 return 0; 518} 519 520 521static int module_close(void) 522{ 523 vid = mem_deref(vid); 524 525 return 0; 526} 527 528 529EXPORT_SYM const struct mod_export DECL_EXPORTS(opengl) = { 530 "opengl", 531 "vidisp", 532 module_init, 533 module_close, 534}; 535