From a543d81bb31176a1ace95c1ea53d378e569a47db Mon Sep 17 00:00:00 2001 From: doggy Date: Sun, 17 Jun 2018 21:53:48 +0800 Subject: cocoa-rendergl: Convert Cocoa example from opengl-cb to render API --- libmpv/cocoa-openglcb/cocoa-openglcb.m | 304 ------------------------------ libmpv/cocoa-rendergl/cocoa-rendergl.m | 325 +++++++++++++++++++++++++++++++++ 2 files changed, 325 insertions(+), 304 deletions(-) delete mode 100644 libmpv/cocoa-openglcb/cocoa-openglcb.m create mode 100644 libmpv/cocoa-rendergl/cocoa-rendergl.m diff --git a/libmpv/cocoa-openglcb/cocoa-openglcb.m b/libmpv/cocoa-openglcb/cocoa-openglcb.m deleted file mode 100644 index 4a4e8aa..0000000 --- a/libmpv/cocoa-openglcb/cocoa-openglcb.m +++ /dev/null @@ -1,304 +0,0 @@ -// Plays a video from the command line in an opengl view in its own window. - -// Build with: clang -o cocoa-openglcb cocoa-openglcb.m `pkg-config --libs --cflags mpv` -framework Cocoa -framework OpenGL - -#import -#import - -#import -#import -#import - -#import - - -static inline void check_error(int status) -{ - if (status < 0) { - printf("mpv API error: %s\n", mpv_error_string(status)); - exit(1); - } -} - -static void *get_proc_address(void *ctx, const char *name) -{ - CFStringRef symbolName = CFStringCreateWithCString(kCFAllocatorDefault, name, kCFStringEncodingASCII); - void *addr = CFBundleGetFunctionPointerForName(CFBundleGetBundleWithIdentifier(CFSTR("com.apple.opengl")), symbolName); - CFRelease(symbolName); - return addr; -} - -static void glupdate(void *ctx); - -@interface MpvClientOGLView : NSOpenGLView -@property mpv_opengl_cb_context *mpvGL; -- (instancetype)initWithFrame:(NSRect)frame; -- (void)drawRect; -- (void)fillBlack; -@end - -@implementation MpvClientOGLView -- (instancetype)initWithFrame:(NSRect)frame -{ - // make sure the pixel format is double buffered so we can use - // [[self openGLContext] flushBuffer]. - NSOpenGLPixelFormatAttribute attributes[] = { - NSOpenGLPFADoubleBuffer, - 0 - }; - self = [super initWithFrame:frame - pixelFormat:[[NSOpenGLPixelFormat alloc] initWithAttributes:attributes]]; - - if (self) { - [self setAutoresizingMask:NSViewWidthSizable|NSViewHeightSizable]; - // swap on vsyncs - GLint swapInt = 1; - [[self openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval]; - [[self openGLContext] makeCurrentContext]; - self.mpvGL = nil; - } - return self; -} - -- (void)fillBlack -{ - glClearColor(0, 0, 0, 0); - glClear(GL_COLOR_BUFFER_BIT); -} - -- (void)drawRect -{ - if (self.mpvGL) - mpv_opengl_cb_draw(self.mpvGL, 0, self.bounds.size.width, -self.bounds.size.height); - else - [self fillBlack]; - [[self openGLContext] flushBuffer]; -} - -- (void)drawRect:(NSRect)dirtyRect -{ - [self drawRect]; -} -@end - -@interface CocoaWindow : NSWindow -@property(retain, readonly) MpvClientOGLView *glView; -@property(retain, readonly) NSButton *pauseButton; -@end - -@implementation CocoaWindow -- (BOOL)canBecomeMainWindow { return YES; } -- (BOOL)canBecomeKeyWindow { return YES; } -- (void)initOGLView { - NSRect bounds = [[self contentView] bounds]; - // window coordinate origin is bottom left - NSRect glFrame = NSMakeRect(bounds.origin.x, bounds.origin.y + 30, bounds.size.width, bounds.size.height - 30); - _glView = [[MpvClientOGLView alloc] initWithFrame:glFrame]; - [self.contentView addSubview:_glView]; - - NSRect buttonFrame = NSMakeRect(bounds.origin.x, bounds.origin.y, 60, 30); - _pauseButton = [[NSButton alloc] initWithFrame:buttonFrame]; - _pauseButton.buttonType = NSToggleButton; - // button target has to be the delegate (it holds the mpv context - // pointer), so that's set later. - _pauseButton.action = @selector(togglePause:); - _pauseButton.title = @"Pause"; - _pauseButton.alternateTitle = @"Play"; - [self.contentView addSubview:_pauseButton]; -} -@end - -@interface AppDelegate : NSObject -{ - mpv_handle *mpv; - dispatch_queue_t queue; - CocoaWindow *window; -} -@end - -static void wakeup(void *); - -@implementation AppDelegate - -- (void)createWindow { - - int mask = NSTitledWindowMask|NSClosableWindowMask| - NSMiniaturizableWindowMask|NSResizableWindowMask; - - window = [[CocoaWindow alloc] - initWithContentRect:NSMakeRect(0, 0, 1280, 720) - styleMask:mask - backing:NSBackingStoreBuffered - defer:NO]; - - // force a minimum size to stop opengl from exploding. - [window setMinSize:NSMakeSize(200, 200)]; - [window initOGLView]; - [window setTitle:@"cocoa-openglcb example"]; - [window makeMainWindow]; - [window makeKeyAndOrderFront:nil]; - - NSMenu *m = [[NSMenu alloc] initWithTitle:@"AMainMenu"]; - NSMenuItem *item = [m addItemWithTitle:@"Apple" action:nil keyEquivalent:@""]; - NSMenu *sm = [[NSMenu alloc] initWithTitle:@"Apple"]; - [m setSubmenu:sm forItem:item]; - [sm addItemWithTitle: @"quit" action:@selector(terminate:) keyEquivalent:@"q"]; - [NSApp setMenu:m]; - [NSApp activateIgnoringOtherApps:YES]; -} - -- (void) applicationDidFinishLaunching:(NSNotification *)notification { - [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; - atexit_b(^{ - // Because activation policy has just been set to behave like a real - // application, that policy must be reset on exit to prevent, among - // other things, the menubar created here from remaining on screen. - [NSApp setActivationPolicy:NSApplicationActivationPolicyProhibited]; - }); - - // Read filename - NSArray *args = [NSProcessInfo processInfo].arguments; - if (args.count < 2) { - NSLog(@"Expected filename on command line"); - exit(1); - } - NSString *filename = args[1]; - - [self createWindow]; - window.pauseButton.target = self; - - mpv = mpv_create(); - if (!mpv) { - printf("failed creating context\n"); - exit(1); - } - - check_error(mpv_set_option_string(mpv, "input-media-keys", "yes")); - // request important errors - check_error(mpv_request_log_messages(mpv, "warn")); - - check_error(mpv_initialize(mpv)); - check_error(mpv_set_option_string(mpv, "vo", "opengl-cb")); - mpv_opengl_cb_context *mpvGL = mpv_get_sub_api(mpv, MPV_SUB_API_OPENGL_CB); - if (!mpvGL) { - puts("libmpv does not have the opengl-cb sub-API."); - exit(1); - } - // pass the mpvGL context to our view - window.glView.mpvGL = mpvGL; - int r = mpv_opengl_cb_init_gl(mpvGL, NULL, get_proc_address, NULL); - if (r < 0) { - puts("gl init has failed."); - exit(1); - } - mpv_opengl_cb_set_update_callback(mpvGL, glupdate, (__bridge void *)window.glView); - - // Deal with MPV in the background. - queue = dispatch_queue_create("mpv", DISPATCH_QUEUE_SERIAL); - dispatch_async(queue, ^{ - // Register to be woken up whenever mpv generates new events. - mpv_set_wakeup_callback(mpv, wakeup, (__bridge void *)self); - // Load the indicated file - const char *cmd[] = {"loadfile", filename.UTF8String, NULL}; - check_error(mpv_command(mpv, cmd)); - }); -} - -static void glupdate(void *ctx) -{ - MpvClientOGLView *glView = (__bridge MpvClientOGLView *)ctx; - // I'm still not sure what the best way to handle this is, but this - // works. - dispatch_async(dispatch_get_main_queue(), ^{ - [glView drawRect]; - }); -} - -- (void) handleEvent:(mpv_event *)event -{ - switch (event->event_id) { - case MPV_EVENT_SHUTDOWN: { - mpv_detach_destroy(mpv); - mpv_opengl_cb_uninit_gl(window.glView.mpvGL); - mpv = NULL; - printf("event: shutdown\n"); - break; - } - - case MPV_EVENT_LOG_MESSAGE: { - struct mpv_event_log_message *msg = (struct mpv_event_log_message *)event->data; - printf("[%s] %s: %s", msg->prefix, msg->level, msg->text); - } - - default: - printf("event: %s\n", mpv_event_name(event->event_id)); - } -} - -- (void)togglePause:(NSButton *)button -{ - if (mpv) { - switch (button.state) { - case NSOffState: - { - int pause = 0; - mpv_set_property(mpv, "pause", MPV_FORMAT_FLAG, &pause); - } - break; - case NSOnState: - { - int pause = 1; - mpv_set_property(mpv, "pause", MPV_FORMAT_FLAG, &pause); - } - break; - default: - NSLog(@"This should never happen."); - } - } -} - -- (void) readEvents -{ - dispatch_async(queue, ^{ - while (mpv) { - mpv_event *event = mpv_wait_event(mpv, 0); - if (event->event_id == MPV_EVENT_NONE) - break; - [self handleEvent:event]; - } - }); -} - -static void wakeup(void *context) -{ - AppDelegate *a = (__bridge AppDelegate *) context; - [a readEvents]; -} - -// quit when the window is closed. -- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)app -{ - return YES; -} - -- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender -{ - NSLog(@"Terminating."); - const char *args[] = {"quit", NULL}; - mpv_command(mpv, args); - [window.glView clearGLContext]; - return NSTerminateNow; -} - -@end - -// Delete this if you already have a main.m. -int main(int argc, const char * argv[]) { - @autoreleasepool { - NSApplication *app = [NSApplication sharedApplication]; - AppDelegate *delegate = [AppDelegate new]; - app.delegate = delegate; - [app run]; - } - return 0; -} diff --git a/libmpv/cocoa-rendergl/cocoa-rendergl.m b/libmpv/cocoa-rendergl/cocoa-rendergl.m new file mode 100644 index 0000000..d5edf54 --- /dev/null +++ b/libmpv/cocoa-rendergl/cocoa-rendergl.m @@ -0,0 +1,325 @@ +// Plays a video from the command line in an opengl view in its own window. + +// Build with: clang -o cocoa-rendergl cocoa-rendergl.m `pkg-config --libs --cflags mpv` -framework Cocoa -framework OpenGL + +#import +#import + +#import +#import +#import + +#import + + +static inline void check_error(int status) +{ + if (status < 0) { + printf("mpv API error: %s\n", mpv_error_string(status)); + exit(1); + } +} + +static void *get_proc_address(void *ctx, const char *name) +{ + CFStringRef symbolName = CFStringCreateWithCString(kCFAllocatorDefault, name, kCFStringEncodingASCII); + void *addr = CFBundleGetFunctionPointerForName(CFBundleGetBundleWithIdentifier(CFSTR("com.apple.opengl")), symbolName); + CFRelease(symbolName); + return addr; +} + +static void glupdate(void *ctx); + +@interface MpvClientOGLView : NSOpenGLView +@property mpv_render_context *mpvGL; +- (instancetype)initWithFrame:(NSRect)frame; +- (void)drawRect; +- (void)fillBlack; +@end + +@implementation MpvClientOGLView +- (instancetype)initWithFrame:(NSRect)frame +{ + // make sure the pixel format is double buffered so we can use + // [[self openGLContext] flushBuffer]. + NSOpenGLPixelFormatAttribute attributes[] = { + NSOpenGLPFADoubleBuffer, + 0 + }; + self = [super initWithFrame:frame + pixelFormat:[[NSOpenGLPixelFormat alloc] initWithAttributes:attributes]]; + + if (self) { + [self setAutoresizingMask:NSViewWidthSizable|NSViewHeightSizable]; + // swap on vsyncs + GLint swapInt = 1; + [[self openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval]; + [[self openGLContext] makeCurrentContext]; + self.mpvGL = nil; + } + return self; +} + +- (void)fillBlack +{ + glClearColor(0, 0, 0, 0); + glClear(GL_COLOR_BUFFER_BIT); +} + +- (void)drawRect +{ + if (self.mpvGL) { + mpv_render_param params[] = { + // Specify the default framebuffer (0) as target. This will + // render onto the entire screen. If you want to show the video + // in a smaller rectangle or apply fancy transformations, you'll + // need to render into a separate FBO and draw it manually. + {MPV_RENDER_PARAM_OPENGL_FBO, &(mpv_opengl_fbo){ + .fbo = 0, + .w = self.bounds.size.width, + .h = self.bounds.size.height, + }}, + // Flip rendering (needed due to flipped GL coordinate system). + {MPV_RENDER_PARAM_FLIP_Y, &(int){1}}, + {0} + }; + // See render_gl.h on what OpenGL environment mpv expects, and + // other API details. + mpv_render_context_render(self.mpvGL, params); + } else + [self fillBlack]; + [[self openGLContext] flushBuffer]; +} + +- (void)drawRect:(NSRect)dirtyRect +{ + [self drawRect]; +} +@end + +@interface CocoaWindow : NSWindow +@property(retain, readonly) MpvClientOGLView *glView; +@property(retain, readonly) NSButton *pauseButton; +@end + +@implementation CocoaWindow +- (BOOL)canBecomeMainWindow { return YES; } +- (BOOL)canBecomeKeyWindow { return YES; } +- (void)initOGLView { + NSRect bounds = [[self contentView] bounds]; + // window coordinate origin is bottom left + NSRect glFrame = NSMakeRect(bounds.origin.x, bounds.origin.y + 30, bounds.size.width, bounds.size.height - 30); + _glView = [[MpvClientOGLView alloc] initWithFrame:glFrame]; + [self.contentView addSubview:_glView]; + + NSRect buttonFrame = NSMakeRect(bounds.origin.x, bounds.origin.y, 60, 30); + _pauseButton = [[NSButton alloc] initWithFrame:buttonFrame]; + _pauseButton.buttonType = NSToggleButton; + // button target has to be the delegate (it holds the mpv context + // pointer), so that's set later. + _pauseButton.action = @selector(togglePause:); + _pauseButton.title = @"Pause"; + _pauseButton.alternateTitle = @"Play"; + [self.contentView addSubview:_pauseButton]; +} +@end + +@interface AppDelegate : NSObject +{ + mpv_handle *mpv; + dispatch_queue_t queue; + CocoaWindow *window; +} +@end + +static void wakeup(void *); + +@implementation AppDelegate + +- (void)createWindow { + + int mask = NSTitledWindowMask|NSClosableWindowMask| + NSMiniaturizableWindowMask|NSResizableWindowMask; + + window = [[CocoaWindow alloc] + initWithContentRect:NSMakeRect(0, 0, 1280, 720) + styleMask:mask + backing:NSBackingStoreBuffered + defer:NO]; + + // force a minimum size to stop opengl from exploding. + [window setMinSize:NSMakeSize(200, 200)]; + [window initOGLView]; + [window setTitle:@"cocoa-rendergl example"]; + [window makeMainWindow]; + [window makeKeyAndOrderFront:nil]; + + NSMenu *m = [[NSMenu alloc] initWithTitle:@"AMainMenu"]; + NSMenuItem *item = [m addItemWithTitle:@"Apple" action:nil keyEquivalent:@""]; + NSMenu *sm = [[NSMenu alloc] initWithTitle:@"Apple"]; + [m setSubmenu:sm forItem:item]; + [sm addItemWithTitle: @"quit" action:@selector(terminate:) keyEquivalent:@"q"]; + [NSApp setMenu:m]; + [NSApp activateIgnoringOtherApps:YES]; +} + +- (void) applicationDidFinishLaunching:(NSNotification *)notification { + [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; + atexit_b(^{ + // Because activation policy has just been set to behave like a real + // application, that policy must be reset on exit to prevent, among + // other things, the menubar created here from remaining on screen. + [NSApp setActivationPolicy:NSApplicationActivationPolicyProhibited]; + }); + + // Read filename + NSArray *args = [NSProcessInfo processInfo].arguments; + if (args.count < 2) { + NSLog(@"Expected filename on command line"); + exit(1); + } + NSString *filename = args[1]; + + [self createWindow]; + window.pauseButton.target = self; + + mpv = mpv_create(); + if (!mpv) { + printf("failed creating context\n"); + exit(1); + } + + check_error(mpv_set_option_string(mpv, "input-media-keys", "yes")); + // request important errors + check_error(mpv_request_log_messages(mpv, "warn")); + + check_error(mpv_initialize(mpv)); + check_error(mpv_set_option_string(mpv, "vo", "libmpv")); + + mpv_render_param params[] = { + {MPV_RENDER_PARAM_API_TYPE, MPV_RENDER_API_TYPE_OPENGL}, + {MPV_RENDER_PARAM_OPENGL_INIT_PARAMS, &(mpv_opengl_init_params){ + .get_proc_address = get_proc_address, + }}, + {0} + }; + + mpv_render_context *mpvGL; + if (mpv_render_context_create(&mpvGL, mpv, params) < 0) { + puts("failed to initialize mpv GL context"); + exit(1); + } + // pass the mpvGL context to our view + window.glView.mpvGL = mpvGL; + + mpv_render_context_set_update_callback(mpvGL, glupdate, (__bridge void *)window.glView); + + // Deal with MPV in the background. + queue = dispatch_queue_create("mpv", DISPATCH_QUEUE_SERIAL); + dispatch_async(queue, ^{ + // Register to be woken up whenever mpv generates new events. + mpv_set_wakeup_callback(mpv, wakeup, (__bridge void *)self); + // Load the indicated file + const char *cmd[] = {"loadfile", filename.UTF8String, NULL}; + check_error(mpv_command(mpv, cmd)); + }); +} + +static void glupdate(void *ctx) +{ + MpvClientOGLView *glView = (__bridge MpvClientOGLView *)ctx; + // I'm still not sure what the best way to handle this is, but this + // works. + dispatch_async(dispatch_get_main_queue(), ^{ + [glView drawRect]; + }); +} + +- (void) handleEvent:(mpv_event *)event +{ + switch (event->event_id) { + case MPV_EVENT_SHUTDOWN: { + mpv_render_context_free(window.glView.mpvGL); + mpv_terminate_destroy(mpv); + mpv = NULL; + printf("event: shutdown\n"); + break; + } + + case MPV_EVENT_LOG_MESSAGE: { + struct mpv_event_log_message *msg = (struct mpv_event_log_message *)event->data; + printf("[%s] %s: %s", msg->prefix, msg->level, msg->text); + } + + default: + printf("event: %s\n", mpv_event_name(event->event_id)); + } +} + +- (void)togglePause:(NSButton *)button +{ + if (mpv) { + switch (button.state) { + case NSOffState: + { + int pause = 0; + mpv_set_property(mpv, "pause", MPV_FORMAT_FLAG, &pause); + } + break; + case NSOnState: + { + int pause = 1; + mpv_set_property(mpv, "pause", MPV_FORMAT_FLAG, &pause); + } + break; + default: + NSLog(@"This should never happen."); + } + } +} + +- (void) readEvents +{ + dispatch_async(queue, ^{ + while (mpv) { + mpv_event *event = mpv_wait_event(mpv, 0); + if (event->event_id == MPV_EVENT_NONE) + break; + [self handleEvent:event]; + } + }); +} + +static void wakeup(void *context) +{ + AppDelegate *a = (__bridge AppDelegate *) context; + [a readEvents]; +} + +// quit when the window is closed. +- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)app +{ + return YES; +} + +- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender +{ + NSLog(@"Terminating."); + const char *args[] = {"quit", NULL}; + mpv_command(mpv, args); + [window.glView clearGLContext]; + return NSTerminateNow; +} + +@end + +// Delete this if you already have a main.m. +int main(int argc, const char * argv[]) { + @autoreleasepool { + NSApplication *app = [NSApplication sharedApplication]; + AppDelegate *delegate = [AppDelegate new]; + app.delegate = delegate; + [app run]; + } + return 0; +} -- cgit v1.2.3