/* 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); dragX = -1; [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) cleanDrag { unsigned int *c = (unsigned int*) image_data; unsigned int *e = c + image_width*image_height; while (c < e) { #ifdef __LITTLE_ENDIAN__ unsigned int v = (*c & 0xff00) >> 8; *c = (v>>8) | (v>>16) | (v>>24); #else unsigned int v = (*c & 0xff0000) >> 16; *c = v | (v>>8) | (v>>16); #endif c++; } } #ifdef __LITTLE_ENDIAN__ #define DRAG_SIG 0x00ff0000 #else #define DRAG_SIG 0x0000ff00 #endif - (void) mouseDragged: (NSEvent *) theEvent { if (dragX < 0) return; // ignore move if not dragging NSPoint eventLocation = [theEvent locationInWindow]; NSPoint localPoint = [self convertPoint:eventLocation fromView:nil]; //NSLog(@"drag %d:%d - %d:%d", dragX, dragY, (int)localPoint.x, (int)localPoint.y); dx = (int)localPoint.x - dragX; dy = (int)localPoint.y - dragY; if (allowDrag) { [self updateOverlay]; [self setCurrentTexture]; [self render]; } } - (void) mouseDown: (NSEvent *) theEvent { NSPoint eventLocation = [theEvent locationInWindow]; NSPoint localPoint = [self convertPoint:eventLocation fromView:nil]; dragX = localPoint.x; dragY = localPoint.y; //NSLog(@" - init drag: %d:%d", dragX, dragY); } - (void) mouseUp: (NSEvent *) theEvent { //NSLog(@" - end drag"); dragX = -1; //[self cleanDrag]; if (allowDrag) { [self updateOverlay]; [self setCurrentTexture]; [self render]; } } /* - (void) mouseEvent: (NSEvent *) theEvent { } */ /* NSResponder */ - (BOOL) acceptsFirstResponder { return YES; } - (BOOL) becomeFirstResponder { return YES; } - (BOOL) resignFirstResponder { return YES; } - (void)windowWillClose:(NSNotification *)aNotification { } const char dig[10][5] = { {7,5,5,5,7}, {2,2,2,2,2}, {7,1,7,4,7}, {7,1,3,1,7}, {5,5,7,1,1}, {7,4,7,1,7}, {6,4,7,5,7}, {7,1,2,2,2}, {7,5,7,5,7}, {7,5,7,1,3} }; static void drawDig(unsigned int *c, int num, int w) { const char *d = dig[num%10]; int y = 0; while (y < 5) { if (d[y] & 4) { c[0]|=DRAG_SIG; c[1]|=DRAG_SIG; c[w]|=DRAG_SIG; c[w+1]|=DRAG_SIG; } c+=2; if (d[y] & 2) { c[0]|=DRAG_SIG; c[1]|=DRAG_SIG; c[w]|=DRAG_SIG; c[w+1]|=DRAG_SIG; } c+=2; if (d[y] & 1) { c[0]|=DRAG_SIG; c[1]|=DRAG_SIG; c[w]|=DRAG_SIG; c[w+1]|=DRAG_SIG; } c+=(w*2)-4; y++; } } static void drawNum(unsigned int *c, int d, int w) { drawDig(c, d, w); d/=10; while (d) { c -= 8; drawDig(c, d, w); d/=10; } } - (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; } if (dragX > 0 && allowDrag) { int _dx = dx, _dy = -dy; unsigned int *c = (unsigned int*) image_data; int i = dragX + (image_height-dragY)*image_width; int s = (_dx<0)?-1:1; while (_dx != 0) { c[i]|=DRAG_SIG; i+=s; _dx-=s; } s = (_dy<0)?-1:1; int ish = s*((int)image_width); while (_dy != 0) { c[i]|=DRAG_SIG; i+=ish; _dy-=s; } int nx = dragX + (dx/2) + (image_height-dragY - 12)*image_width; int ny = dragX + dx + (image_height-dragY-dy/2)*image_width; if (dx < 20 && dx > -20) nx = dragX + dx + (image_height-dragY-dy)*image_width - 2 - image_width*12; if (dy < 20 && dy > -20) ny = dragX + dx + (image_height-dragY-dy)*image_width + 5*8 - image_width*12; drawNum(c + nx, (dx<0)?-dx:dx, image_width); drawNum(c + ny, (dy<0)?-dy:dy, image_width); } } - (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]; } - (void) setAllowDrag: (BOOL) drag { allowDrag = drag; } @end