/* videoTools - R package; display raw frame buffer on Mac OS X (C)Copyright 2007 Simon Urbanek Parts based on vo_macosx MPlayer Mac OSX video out module by Nicolas Plourde Copyright (c) 2005 License: GPL v2 */ #include #include #include #import "VideoView.h" #define IMGFMT_BGR32 0x52474220 #define IMGFMT_RGB32 0x42475220 #define IMGFMT_YUY2 0x32595559 #define IMGFMT_YV12 0x32315659 #define IMGFMT_IYUV 0x56555949 #define IMGFMT_RGB24 0x42475218 #define IMGFMT_BGR24 0x52474218 @implementation VideoView - (void) awakeFromNib { NSRect frameRect = [self frame]; NSLog(@"ViewInit.awakeFromNib (%f x %f)", frameRect.size.width, frameRect.size.height); [self configureWidth:frameRect.size.width height:frameRect.size.height format:IMGFMT_RGB32]; } - (int) configureWidth: (uint32_t) width height: (uint32_t) height format: (uint32_t) format { //init screen NSArray *screen_array = [NSScreen screens]; screen_handle = [screen_array objectAtIndex:0]; screen_frame = [screen_handle frame]; image_format = format; image_width = width; image_height = height; switch (image_format) { case IMGFMT_BGR32: case IMGFMT_RGB32: image_depth = 32; pixelFormat = k32ARGBPixelFormat; break; case IMGFMT_YUY2: image_depth = 16; pixelFormat = kYUVSPixelFormat; break; } image_bytes = (image_depth + 7) / 8; image_data = malloc(image_width*image_height*image_bytes); NSLog(@"image_bytes=%d, pixelFormat=%x [%x], image_depth=%d, %d x %d\n", image_bytes, pixelFormat, format, image_depth, image_width, image_height); isFullscreen = 0; winSizeMult = 1; monitor_aspect = 1; movie_aspect = 1; isRootwin = 0; vo_keepaspect = 0; [self config]; return 0; } - (id) config { GLint swapInterval = 1; CVReturn rerror = kCVReturnSuccess; //create OpenGL Context glContext = [[NSOpenGLContext alloc] initWithFormat:[NSOpenGLView defaultPixelFormat] shareContext:nil]; [self setOpenGLContext:glContext]; [glContext setValues:&swapInterval forParameter:NSOpenGLCPSwapInterval]; [glContext setView:self]; [glContext makeCurrentContext]; rerror = CVPixelBufferCreateWithBytes( NULL, image_width, image_height, pixelFormat, image_data, image_width*image_bytes, NULL, NULL, NULL, ¤tFrameBuffer); if(rerror != kCVReturnSuccess) { NSLog(@"Failed to create Pixel Buffer(%d)", rerror); return nil; } rerror = CVOpenGLTextureCacheCreate(NULL, 0, [glContext CGLContextObj], [[self pixelFormat] CGLPixelFormatObj], 0, &textureCache); if(rerror != kCVReturnSuccess) { NSLog(@"Failed to create OpenGL texture Cache(%d)", rerror); return nil; } rerror = CVOpenGLTextureCacheCreateTextureFromImage( NULL, textureCache, currentFrameBuffer, 0, &texture); if(rerror != kCVReturnSuccess) { NSLog(@"Failed to create OpenGL texture(%d)", rerror); return nil; } return self; } /* Setup OpenGL */ - (void)prepareOpenGL { glEnable(GL_BLEND); glDisable(GL_DEPTH_TEST); glDepthMask(GL_FALSE); glDisable(GL_CULL_FACE); [self reshape]; } /* reshape OpenGL viewport */ - (void)reshape { uint32_t d_width = image_width; uint32_t d_height = image_height; float aspectX=1.0; float aspectY=1.0; int padding = 0; NSRect frame = [self frame]; frame.origin.x = frame.origin.y = 0; glViewport(0, 0, frame.size.width, frame.size.height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(0, frame.size.width, frame.size.height, 0, -1.0, 1.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); //set texture frame if(vo_keepaspect) { d_height = ((float)d_width/movie_aspect); aspectX = (float)((float)frame.size.width/(float)d_width); aspectY = (float)((float)(frame.size.height)/(float)d_height); if((d_height*aspectX)>(frame.size.height)) { padding = (frame.size.width - d_width*aspectY)/2; textureFrame = NSMakeRect(padding, 0, d_width*aspectY+padding, d_height*aspectY); } else { padding = ((frame.size.height) - d_height*aspectX)/2; textureFrame = NSMakeRect(0, padding, d_width*aspectX, d_height*aspectX+padding); } } else { textureFrame = frame; } } /* Render frame */ - (void) render { int curTime; static int lastTime; glClear(GL_COLOR_BUFFER_BIT); glEnable(CVOpenGLTextureGetTarget(texture)); glBindTexture(CVOpenGLTextureGetTarget(texture), CVOpenGLTextureGetName(texture)); glColor3f(1,1,1); glBegin(GL_QUADS); glTexCoord2f(upperLeft[0], upperLeft[1]); glVertex2i( textureFrame.origin.x, textureFrame.origin.y); glTexCoord2f(lowerLeft[0], lowerLeft[1]); glVertex2i( textureFrame.origin.x, textureFrame.size.height); glTexCoord2f(lowerRight[0], lowerRight[1]); glVertex2i( textureFrame.size.width, textureFrame.size.height); glTexCoord2f(upperRight[0], upperRight[1]); glVertex2i( textureFrame.size.width, textureFrame.origin.y); glEnd(); glDisable(CVOpenGLTextureGetTarget(texture)); //render resize box if(!isFullscreen) { NSRect frame = [self frame]; glBegin(GL_LINES); glColor4f(0.2, 0.2, 0.2, 0.5); glVertex2i(frame.size.width-1, frame.size.height-1); glVertex2i(frame.size.width-1, frame.size.height-1); glVertex2i(frame.size.width-1, frame.size.height-5); glVertex2i(frame.size.width-5, frame.size.height-1); glVertex2i(frame.size.width-1, frame.size.height-9); glVertex2i(frame.size.width-9, frame.size.height-1); glColor4f(0.4, 0.4, 0.4, 0.5); glVertex2i(frame.size.width-1, frame.size.height-2); glVertex2i(frame.size.width-2, frame.size.height-1); glVertex2i(frame.size.width-1, frame.size.height-6); glVertex2i(frame.size.width-6, frame.size.height-1); glVertex2i(frame.size.width-1, frame.size.height-10); glVertex2i(frame.size.width-10, frame.size.height-1); glColor4f(0.6, 0.6, 0.6, 0.5); glVertex2i(frame.size.width-1, frame.size.height-3); glVertex2i(frame.size.width-3, frame.size.height-1); glVertex2i(frame.size.width-1, frame.size.height-7); glVertex2i(frame.size.width-7, frame.size.height-1); glVertex2i(frame.size.width-1, frame.size.height-11); glVertex2i(frame.size.width-11, frame.size.height-1); glEnd(); } glFlush(); //auto hide mouse cursor and futur on-screen control? if(isFullscreen && !mouseHide && !isRootwin) { int curTime = TickCount()/60; static int lastTime = 0; if( ((curTime - lastTime) >= 5) || (lastTime == 0) ) { CGDisplayHideCursor(kCGDirectMainDisplay); mouseHide = YES; lastTime = curTime; } } //update activity every 30 seconds to prevent //screensaver from starting up. curTime = TickCount()/60; lastTime = 0; if( ((curTime - lastTime) >= 30) || (lastTime == 0) ) { UpdateSystemActivity(UsrActivity); lastTime = curTime; } } /* Create OpenGL texture from current frame & set texco */ - (void) setCurrentTexture { CVReturn rerror = kCVReturnSuccess; rerror = CVOpenGLTextureCacheCreateTextureFromImage (NULL, textureCache, currentFrameBuffer, 0, &texture); if (rerror != kCVReturnSuccess) { NSLog(@"Failed to create OpenGL texture(%d)", rerror); return; } CVOpenGLTextureGetCleanTexCoords(texture, lowerLeft, lowerRight, upperRight, upperLeft); } /* redraw win rect */ - (void) drawRect: (NSRect *) bounds { [self render]; } /* Toggle Fullscreen */ - (void) fullscreen: (BOOL) vo_fs { static NSRect old_frame; static NSRect old_view_frame; BOOL animate = YES; NSWindow *window = [self window]; screen_frame = [[window screen] frame]; //go fullscreen if(vo_fs) { if(!isRootwin) { SetSystemUIMode( kUIModeAllHidden, kUIOptionAutoShowMenuBar); CGDisplayHideCursor(kCGDirectMainDisplay); mouseHide = YES; } old_frame = [window frame]; //save main window size & position [window setFrame:screen_frame display:YES animate:animate]; //zoom-in window with nice useless sfx old_view_frame = [self bounds]; //fix origin for multi screen setup screen_frame.origin.x = 0; screen_frame.origin.y = 0; [self setFrame:screen_frame]; [self setNeedsDisplay:YES]; [window setHasShadow:NO]; isFullscreen = 1; } else { SetSystemUIMode( kUIModeNormal, 0); isFullscreen = 0; CGDisplayShowCursor(kCGDirectMainDisplay); mouseHide = NO; //revert window to previous setting [self setFrame:old_view_frame]; [self setNeedsDisplay:YES]; [window setHasShadow:YES]; [window setFrame:old_frame display:YES animate:animate];//zoom-out window with nice useless sfx } } - (unsigned int) width { return image_width; } - (unsigned int) height { return image_height; } - (unsigned char*) imageBuffer { return image_data; } - (unsigned int) format { return (image_format==IMGFMT_YUY2)?1:0; } /* Toggle ontop */ - (void) ontop: (BOOL) vo_ontop { if(vo_ontop) { [[self window] setLevel:NSScreenSaverWindowLevel]; isOntop = YES; } else { [[self window] setLevel:NSNormalWindowLevel]; isOntop = NO; } } /* Toggle rootwin */ - (void) rootwin: (BOOL) vo_rootwin { if(vo_rootwin) { [[self window] setLevel:CGWindowLevelForKey(kCGDesktopWindowLevelKey)]; [[self window] orderBack:self]; isRootwin = YES; } else { [[self window] setLevel:NSNormalWindowLevel]; isRootwin = NO; } } /* Check event for new event */ - (void) check_events { event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:[NSDate dateWithTimeIntervalSinceNow:0.0001] inMode:NSEventTrackingRunLoopMode dequeue:YES]; [NSApp sendEvent:event]; } /* From NSView, respond to key equivalents. */ - (BOOL)performKeyEquivalent:(NSEvent *)theEvent { /* switch([theEvent keyCode]) { case 0x21: [window setAlphaValue: winAlpha-=0.05]; return YES; case 0x1e: [window setAlphaValue: winAlpha+=0.05]; return YES; } */ return NO; } // Process key event - (void) keyDown: (NSEvent *) theEvent { //unsigned int key; switch([theEvent keyCode]) { /* case 0x34: case 0x24: key = KEY_ENTER; break; */ // case 0x35: running = 0; break; /* case 0x33: key = KEY_BACKSPACE; break; case 0x3A: key = KEY_BACKSPACE; break; case 0x3B: key = KEY_BACKSPACE; break; case 0x38: key = KEY_BACKSPACE; break; case 0x7A: key = KEY_F+1; break; case 0x78: key = KEY_F+2; break; case 0x63: key = KEY_F+3; break; case 0x76: key = KEY_F+4; break; case 0x60: key = KEY_F+5; break; case 0x61: key = KEY_F+6; break; case 0x62: key = KEY_F+7; break; case 0x64: key = KEY_F+8; break; case 0x65: key = KEY_F+9; break; case 0x6D: key = KEY_F+10; break; case 0x67: key = KEY_F+11; break; case 0x6F: key = KEY_F+12; break; case 0x72: key = KEY_INSERT; break; case 0x75: key = KEY_DELETE; break; case 0x73: key = KEY_HOME; break; case 0x77: key = KEY_END; break; case 0x45: key = '+'; break; case 0x4E: key = '-'; break; case 0x30: key = KEY_TAB; break; case 0x74: key = KEY_PAGE_UP; break; case 0x79: key = KEY_PAGE_DOWN; break; case 0x7B: key = KEY_LEFT; break; case 0x7C: key = KEY_RIGHT; break; case 0x7D: key = KEY_DOWN; break; case 0x7E: key = KEY_UP; break; case 0x43: key = '*'; break; case 0x4B: key = '/'; break; case 0x4C: key = KEY_BACKSPACE; break; case 0x41: key = KEY_KPDEC; break; case 0x52: key = KEY_KP0; break; case 0x53: key = KEY_KP1; break; case 0x54: key = KEY_KP2; break; case 0x55: key = KEY_KP3; break; case 0x56: key = KEY_KP4; break; case 0x57: key = KEY_KP5; break; case 0x58: key = KEY_KP6; break; case 0x59: key = KEY_KP7; break; case 0x5B: key = KEY_KP8; break; case 0x5C: key = KEY_KP9; break; default: key = *[[theEvent characters] UTF8String]; break; */ } //mplayer_put_key(key); } /* Process mouse button event */ - (void) mouseMoved: (NSEvent *) theEvent { if(isFullscreen && !isRootwin) { CGDisplayShowCursor(kCGDirectMainDisplay); mouseHide = NO; } } - (void) mouseDown: (NSEvent *) theEvent { [self mouseEvent: theEvent]; } - (void) rightMouseDown: (NSEvent *) theEvent { [self mouseEvent: theEvent]; } - (void) otherMouseDown: (NSEvent *) theEvent { [self mouseEvent: theEvent]; } - (void) scrollWheel: (NSEvent *) theEvent { /* if([theEvent deltaY] > 0) mplayer_put_key(MOUSE_BTN3); else mplayer_put_key(MOUSE_BTN4); */ } - (void) mouseEvent: (NSEvent *) theEvent { /* switch( [theEvent buttonNumber] ) { case 0: mplayer_put_key(MOUSE_BTN0);break; case 1: mplayer_put_key(MOUSE_BTN1);break; case 2: mplayer_put_key(MOUSE_BTN2);break; }*/ } /* NSResponder */ - (BOOL) acceptsFirstResponder { return YES; } - (BOOL) becomeFirstResponder { return YES; } - (BOOL) resignFirstResponder { return YES; } - (void)windowWillClose:(NSNotification *)aNotification { } - (void) updateOverlay /* overlay or rather change the pixels to visualize bubbleArea */ { unsigned char *c = (unsigned char*) image_data; unsigned char *d = c; unsigned char *e = c + image_width * image_height * 4; int mode = 0; int m1pos = bubbleArea.y * image_width; int m2pos = (bubbleArea.y + bubbleArea.height) * image_width; while (c < e) { if (c[1] == c[2] && c[2] == c[3]) { /* update only b/w pixels */ int p = (c - d) / 4; if (mode == 0) { if (p >= m1pos) mode = 1; else { c[1] = 0; } } if (mode == 1) { if (p >= m2pos) mode = 2; else { int x = p % image_width; if (x < bubbleArea.x || x > bubbleArea.x + bubbleArea.width) { c[3] = c[2] = c[1] >> 1; } } } if (mode == 2) { c[1] = 0; } } c+=4; } } - (void) setBubbleArea: (bubarea_t) anArea; { /* reset the whole picture to b/w */ unsigned char *c = (unsigned char*) image_data; unsigned char *e = c + image_width * image_height * 4; bubbleArea = anArea; while (c < e) { if (c[1] != c[2] || c[2] != c[3]) { unsigned char v = c[1]; if (c[2] > v) v = c[2]; if (c[3] > v) v = c[3]; c[1] = c[2] = c[3] = v; } c+=4; } [self updateOverlay]; } - (void) updateMono: (const char*) data { [self updateMono:data offset:0 length:image_width*image_height]; } - (void) updateMono: (const char*) data offset: (int) offset length: (int) len { const unsigned char *d = (const unsigned char*) data, *ed = d + len; unsigned int *c = (unsigned int*) image_data + offset; while (d < ed) { unsigned int v = *(d++); #ifdef __LITTLE_ENDIAN__ *(c++) = (v<<8) | (v<<16) | (v<<24); #else *(c++) = v | (v<<8) | (v<<16); #endif } #ifndef FAST [self updateOverlay]; #endif [self setCurrentTexture]; [self render]; } - (void) updateMonoWithData: (NSData*) data atOffset: (int) offset { const unsigned char *d = (const unsigned char*) [data bytes]; const unsigned char *ed = d + [data length]; unsigned int *c = (unsigned int*) image_data + offset; while (d < ed) { unsigned int v = *(d++); #ifdef __LITTLE_ENDIAN__ *(c++) = (v<<8) | (v<<16) | (v<<24); #else *(c++) = v | (v<<8) | (v<<16); #endif } [self updateOverlay]; [self setCurrentTexture]; [self render]; } - (void) updateMonoWithData: (NSData*) data { [self updateMonoWithData:data atOffset:0]; } @end