diff options
Diffstat (limited to 'sys/osxvideo')
-rw-r--r-- | sys/osxvideo/Makefile.am | 17 | ||||
-rw-r--r-- | sys/osxvideo/cocoawindow.h | 79 | ||||
-rw-r--r-- | sys/osxvideo/cocoawindow.m | 475 | ||||
-rw-r--r-- | sys/osxvideo/osxvideosink.h | 111 | ||||
-rw-r--r-- | sys/osxvideo/osxvideosink.m | 474 |
5 files changed, 1156 insertions, 0 deletions
diff --git a/sys/osxvideo/Makefile.am b/sys/osxvideo/Makefile.am new file mode 100644 index 000000000..99bb04899 --- /dev/null +++ b/sys/osxvideo/Makefile.am @@ -0,0 +1,17 @@ +# FIXME: clean up this crap +OBJC=gcc + +plugin_LTLIBRARIES = libgstosxvideosink.la + +libgstosxvideosink_la_SOURCES = osxvideosink.m cocoawindow.m +libgstosxvideosink_la_CFLAGS = $(GST_CFLAGS) $(GST_BASE_CFLAGS) \ + $(GST_PLUGINS_BASE_CFLAGS) +libgstosxvideosink_la_LIBADD = \ + $(GST_BASE_LIBS) $(GST_PLUGINS_BASE_LIBS) -lgstvideo-$(GST_MAJORMINOR) \ + -lgstinterfaces-$(GST_MAJORMINOR) + +libgstosxvideosink_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) -Wl,-framework -Wl,Cocoa -Wl,-framework -Wl,QuickTime -Wl,-framework -Wl,OpenGL + +AM_OBJCFLAGS=$(CFLAGS) $(GST_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) + +noinst_HEADERS = osxvideosink.h cocoawindow.h diff --git a/sys/osxvideo/cocoawindow.h b/sys/osxvideo/cocoawindow.h new file mode 100644 index 000000000..c4a774d17 --- /dev/null +++ b/sys/osxvideo/cocoawindow.h @@ -0,0 +1,79 @@ +/* GStreamer + * Copyright (C) 2004 Zaheer Abbas Merali <zaheerabbas at merali dot org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* inspiration gained from looking at source of osx video out of xine and vlc + * and is reflected in the code + */ + +#import <Cocoa/Cocoa.h> +#import <QuickTime/QuickTime.h> +#import <glib.h> + +struct _GstOSXImage; + +@interface GstGLView : NSOpenGLView +{ + int i_effect; + unsigned long pi_texture; + float f_x; + float f_y; + int initDone; + char* data; + int width, height; + BOOL fullscreen; + NSOpenGLContext* fullScreenContext; +} +- (void) drawQuad; +- (void) drawRect: (NSRect) rect; +- (id) initWithFrame: (NSRect) frame; +- (void) initTextures; +- (void) reloadTexture; +- (void) cleanUp; +- (void) displayTexture; +- (char*) getTextureBuffer; +- (void) setFullScreen: (BOOL) flag; +- (void) reshape; +- (void) setVideoSize: (int) w: (int) h; + +@end + +@interface GstView : NSQuickDrawView { + int width, height; + gboolean isPortSet; + void* port; +/* Quicktime Sequence */ + ImageSequence qtseqid; + ImageDescriptionHandle imgdesc; + struct _GstOSXImage* curimg; +} +- (void) drawRect: (NSRect) rect; +- (id) initWithFrame: (NSRect) frame; +- (void) setVideoSize: (int) w: (int) h; +- (void) setVideoImage: (GstBuffer*) img; +@end + +@interface GstOSXVideoSinkWindow: NSWindow { + int width, height; + GstGLView *gstview; +} + +- (void) setContentSize: (NSSize) size; +- (GstGLView *) gstView; +- (id)initWithContentRect:(NSRect)contentRect styleMask:(unsigned int)styleMask backing:(NSBackingStoreType)bufferingType defer:(BOOL)flag screen:(NSScreen *)aScreen; +@end diff --git a/sys/osxvideo/cocoawindow.m b/sys/osxvideo/cocoawindow.m new file mode 100644 index 000000000..e5f7694a2 --- /dev/null +++ b/sys/osxvideo/cocoawindow.m @@ -0,0 +1,475 @@ +/* GStreamer + * Copyright (C) 2004 Zaheer Abbas Merali <zaheerabbas at merali dot org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* inspiration gained from looking at source of osx video out of xine and vlc + * and is reflected in the code + */ + + +#include <Cocoa/Cocoa.h> +#include <gst/gst.h> +#import "cocoawindow.h" +#import "osxvideosink.h" + +#include <OpenGL/OpenGL.h> +#include <OpenGL/gl.h> +#include <OpenGL/glext.h> + +/* Debugging category */ +#include <gst/gstinfo.h> + +@ implementation GstOSXVideoSinkWindow + +- (id) initWithContentRect: (NSRect) rect + styleMask: (unsigned int) styleMask + backing: (NSBackingStoreType) bufferingType + defer: (BOOL) flag + screen:(NSScreen *) aScreen +{ + self = [super initWithContentRect: rect + styleMask: styleMask + backing: bufferingType + defer: flag + screen:aScreen]; + + GST_DEBUG ("Initializing GstOSXvideoSinkWindow"); + + gstview = [[GstGLView alloc] initWithFrame:rect]; + + if (gstview) + [self setContentView:gstview]; + [self setTitle:@"GStreamer Video Output"]; + + return self; +} + +- (void) setContentSize:(NSSize) size { + width = size.width; + height = size.height; + + [gstview setVideoSize: (int) width:(int) height]; + + [super setContentSize:size]; +} + +- (GstGLView *) gstView { + return gstview; +} + +- (void) awakeFromNib { + [self setAcceptsMouseMovedEvents:YES]; +} + +- (void) sendEvent:(NSEvent *) event { + BOOL taken = NO; + + GST_LOG ("event %p type:%d", event,[event type]); + + if ([event type] == NSKeyDown) { + } + /*taken = [gstview keyDown:event]; */ + + if (!taken) { + [super sendEvent:event]; + } +} + + +@end + +// +// GstView +// Deprecated QuickDraw implementation +// + +@ implementation GstView + +- (void) drawRect:(NSRect) rect { + /*NSRect bounds = [self bounds]; + [[NSColor greenColor] set]; + [NSBezierPath fillRect:bounds]; */ + [[NSColor blackColor] set]; + NSRectFill (rect); + [super drawRect:rect]; +} + +- (id) initWithFrame:(NSRect) frame { + NSRect bounds =[self bounds]; + + [[NSColor greenColor] set]; + + self =[super initWithFrame:frame]; + isPortSet = FALSE; + [NSBezierPath fillRect:bounds]; + return self; +} + +- (void) setVideoSize: (int) w:(int) h { + GST_LOG ("width:%d height:%d", w, h); + + width = w; + height = h; +} + +- (void) setVideoImage:(GstBuffer *) img { + if (isPortSet == FALSE) { + // first image + //GWorldPtr imgGWorld; + //Rect coords; + OSErr err; + ImageDescriptionPtr pimgdesc; + + err = EnterMovies (); + + if (err != noErr) + GST_ERROR ("EnterMovies error: %d", err); + /*SetRect(&coords,0,0,width,height); + NewGWorldFromPtr (&imgGWorld, kYUV420CodecType, &coords, 0, 0, 0, GST_BUFFER_DATA(img), width * 4); + MakeImageDescriptionForPixMap (GetGWorldPixMap(imgGWorld), &imgdesc); + DisposeGWorld(imgGWorld); */ + imgdesc = + (ImageDescriptionHandle) NewHandleClear (sizeof (ImageDescription)); + pimgdesc = *imgdesc; + pimgdesc->idSize = sizeof (ImageDescription); + pimgdesc->cType = kYUV420CodecType; + pimgdesc->version = 1; + pimgdesc->revisionLevel = 0; + pimgdesc->vendor = 'appl'; + pimgdesc->width = width; + pimgdesc->height = height; + pimgdesc->hRes = Long2Fix (72); + pimgdesc->vRes = Long2Fix (72); + pimgdesc->spatialQuality = codecLosslessQuality; + pimgdesc->frameCount = 1; + pimgdesc->clutID = -1; + pimgdesc->dataSize = 0; + pimgdesc->depth = 12; + + [self lockFocus]; + port =[self qdPort]; + g_warning ("port = 0x%x", (int) port); + err = DecompressSequenceBeginS (&qtseqid, imgdesc, NULL, 0, port, NULL, NULL, NULL, 0, // srcCopy + NULL, codecFlagUseImageBuffer, codecLosslessQuality, bestSpeedCodec); + if (err != noErr) { + GST_DEBUG ("DecompressSequenceBeginS error: %d", err); + } + [self unlockFocus]; + isPortSet = TRUE; + } + + OSErr err; + CodecFlags flags; + + GST_DEBUG ("qtseqid: %d img data: %p size: %d", (int) qtseqid, + GST_BUFFER_DATA (img), GST_BUFFER_SIZE (img)); + err = + DecompressSequenceFrameS (qtseqid, (char *) GST_BUFFER_DATA (img), + GST_BUFFER_SIZE (img), codecFlagUseImageBuffer, &flags, NULL); + if (err != noErr) { + GST_DEBUG ("DecompressSequenceS erro: %d", err); + } else { + //QDFlushPortBuffer (port, nil); + } + +} + +@end + +// +// OpenGL implementation +// + +@ implementation GstGLView + +- (id) initWithFrame:(NSRect) frame { + NSOpenGLPixelFormat *fmt; + NSOpenGLPixelFormatAttribute attribs[] = { + NSOpenGLPFAAccelerated, + NSOpenGLPFANoRecovery, + NSOpenGLPFADoubleBuffer, + NSOpenGLPFAColorSize, 24, + NSOpenGLPFAAlphaSize, 8, + NSOpenGLPFADepthSize, 24, + NSOpenGLPFAWindow, + 0 + }; + + fmt = [[NSOpenGLPixelFormat alloc] + initWithAttributes:attribs]; + + if (!fmt) { + GST_WARNING ("Cannot create NSOpenGLPixelFormat"); + return nil; + } + + self = [super initWithFrame: frame pixelFormat:fmt]; + + [[self openGLContext] makeCurrentContext]; + [[self openGLContext] update]; + + /* Black background */ + glClearColor (0.0, 0.0, 0.0, 0.0); + + pi_texture = 0; + data = g_malloc (2 * 320 * 240); + width = frame.size.width; + height = frame.size.height; + + GST_LOG ("Width: %d Height: %d", width, height); + + [self initTextures]; + return self; +} + +- (void) reshape { + NSRect bounds; + + GST_LOG ("reshaping"); + + if (!initDone) { + return; + } + + [[self openGLContext] makeCurrentContext]; + + bounds = [self bounds]; + + glViewport (0, 0, (GLint) bounds.size.width, (GLint) bounds.size.height); + +} + +- (void) initTextures { + NSOpenGLContext *currentContext; + + if (fullscreen) + currentContext = fullScreenContext; + else + currentContext =[self openGLContext]; + + [currentContext makeCurrentContext]; + + /* Free previous texture if any */ + if (initDone) { + glDeleteTextures (1, &pi_texture); + } + /* Create textures */ + glGenTextures (1, &pi_texture); + + glEnable (GL_TEXTURE_RECTANGLE_EXT); + glEnable (GL_UNPACK_CLIENT_STORAGE_APPLE); + + glBindTexture (GL_TEXTURE_RECTANGLE_EXT, pi_texture); + + /* Use VRAM texturing */ + glTexParameteri (GL_TEXTURE_RECTANGLE_EXT, + GL_TEXTURE_STORAGE_HINT_APPLE, GL_STORAGE_CACHED_APPLE); + + /* Tell the driver not to make a copy of the texture but to use + our buffer */ + glPixelStorei (GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE); + + + /* Linear interpolation */ + glTexParameteri (GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri (GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + /* I have no idea what this exactly does, but it seems to be + necessary for scaling */ + glTexParameteri (GL_TEXTURE_RECTANGLE_EXT, + GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri (GL_TEXTURE_RECTANGLE_EXT, + GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glPixelStorei (GL_UNPACK_ROW_LENGTH, 0); + + glTexImage2D (GL_TEXTURE_RECTANGLE_EXT, 0, GL_RGBA, + width, height, 0, GL_YCBCR_422_APPLE, GL_UNSIGNED_SHORT_8_8_APPLE, data); + + + initDone = 1; +} + +- (void) reloadTexture { + NSOpenGLContext *currentContext; + + if (!initDone) { + return; + } + + GST_LOG ("Reloading Texture"); + + if (fullscreen) + currentContext = fullScreenContext; + else + currentContext =[self openGLContext]; + [currentContext makeCurrentContext]; + + glBindTexture (GL_TEXTURE_RECTANGLE_EXT, pi_texture); + + /* glTexSubImage2D is faster than glTexImage2D + http://developer.apple.com/samplecode/Sample_Code/Graphics_3D/ + TextureRange/MainOpenGLView.m.htm */ + glTexSubImage2D (GL_TEXTURE_RECTANGLE_EXT, 0, 0, 0, width, height, GL_YCBCR_422_APPLE, GL_UNSIGNED_SHORT_8_8_APPLE, data); //FIXME +} + +- (void) cleanUp { + initDone = 0; +} + +- (void) drawQuad { + f_x = 1.0; + f_y = 1.0; + + glBegin (GL_QUADS); + /* Top left */ + glTexCoord2f (0.0, 0.0); + glVertex2f (-f_x, f_y); + /* Bottom left */ + glTexCoord2f (0.0, (float) height); + glVertex2f (-f_x, -f_y); + /* Bottom right */ + glTexCoord2f ((float) width, (float) height); + glVertex2f (f_x, -f_y); + /* Top right */ + glTexCoord2f ((float) width, 0.0); + glVertex2f (f_x, f_y); + glEnd (); +} + +- (void) drawRect:(NSRect) rect { + NSOpenGLContext *currentContext; + long params[] = { 1 }; + + if (fullscreen) { + currentContext = fullScreenContext; + } else { + currentContext =[self openGLContext]; + } + [currentContext makeCurrentContext]; + + CGLSetParameter (CGLGetCurrentContext (), kCGLCPSwapInterval, params); + + /* Black background */ + glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + if (!initDone) { + [[self openGLContext] flushBuffer]; + return; + } + + /* Draw */ + glBindTexture (GL_TEXTURE_RECTANGLE_EXT, pi_texture); // FIXME + [self drawQuad]; + /* Draw */ + [currentContext flushBuffer]; +} + +- (void) displayTexture { + if ([self lockFocusIfCanDraw]) { + + [self drawRect:[self bounds]]; + [self reloadTexture]; + + [self unlockFocus]; + + } + +} + +- (char *) getTextureBuffer { + return data; +} + +- (void) setFullScreen:(BOOL) flag { + if (!fullscreen && flag) { + // go to full screen + /* Create the new pixel format */ + NSOpenGLPixelFormat *fmt; + NSOpenGLPixelFormatAttribute attribs[] = { + NSOpenGLPFAAccelerated, + NSOpenGLPFANoRecovery, + NSOpenGLPFADoubleBuffer, + NSOpenGLPFAColorSize, 24, + NSOpenGLPFAAlphaSize, 8, + NSOpenGLPFADepthSize, 24, + NSOpenGLPFAFullScreen, + NSOpenGLPFAScreenMask, + CGDisplayIDToOpenGLDisplayMask (kCGDirectMainDisplay), + 0 + }; + + fmt = [[NSOpenGLPixelFormat alloc] + initWithAttributes:attribs]; + + if (!fmt) { + GST_WARNING ("Cannot create NSOpenGLPixelFormat"); + return; + } + + /* Create the new OpenGL context */ + fullScreenContext = [[NSOpenGLContext alloc] + initWithFormat: fmt shareContext:nil]; + if (!fullScreenContext) { + GST_WARNING ("Failed to create new NSOpenGLContext"); + return; + } + + /* Capture display, switch to fullscreen */ + if (CGCaptureAllDisplays () != CGDisplayNoErr) { + GST_WARNING ("CGCaptureAllDisplays() failed"); + return; + } + [fullScreenContext setFullScreen]; + [fullScreenContext makeCurrentContext]; + + fullscreen = YES; + + [self initTextures]; + [self setNeedsDisplay:YES]; + + } else if (fullscreen && !flag) { + // fullscreen now and needs to go back to normal + initDone = NO; + [NSOpenGLContext clearCurrentContext]; + + CGReleaseAllDisplays (); + + [self reshape]; + [self initTextures]; + + [self setNeedsDisplay:YES]; + + fullscreen = NO; + initDone = YES; + } +} + +- (void) setVideoSize: (int) w:(int) h { + GST_LOG ("width:%d, height:%d", w, h); + + width = w; + height = h; + + // FIXME : so, do we free, or don't we ? + //if (data) g_free(data); + + data = g_malloc0 (2 * w * h); + [self reloadTexture]; +} + +@end diff --git a/sys/osxvideo/osxvideosink.h b/sys/osxvideo/osxvideosink.h new file mode 100644 index 000000000..b2577512f --- /dev/null +++ b/sys/osxvideo/osxvideosink.h @@ -0,0 +1,111 @@ +/* GStreamer + * Copyright (C) 2004-6 Zaheer Abbas Merali <zaheerabbas at merali dot org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GST_OSX_VIDEO_SINK_H__ +#define __GST_OSX_VIDEO_SINK_H__ + +#include <gst/video/gstvideosink.h> + +#include <string.h> +#include <math.h> +#include <Cocoa/Cocoa.h> + +#include <QuickTime/QuickTime.h> +#import "cocoawindow.h" + +GST_DEBUG_CATEGORY_EXTERN (gst_debug_osx_video_sink); +#define GST_CAT_DEFAULT gst_debug_osx_video_sink + +G_BEGIN_DECLS + +#define GST_TYPE_OSX_VIDEO_SINK \ + (gst_osx_video_sink_get_type()) +#define GST_OSX_VIDEO_SINK(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_OSX_VIDEO_SINK, GstOSXVideoSink)) +#define GST_OSX_VIDEO_SINK_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_OSX_VIDEO_SINK, GstOSXVideoSinkClass)) +#define GST_IS_OSX_VIDEO_SINK(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_OSX_VIDEO_SINK)) +#define GST_IS_OSX_VIDEO_SINK_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_OSX_VIDEO_SINK)) + +typedef struct _GstOSXWindow GstOSXWindow; + +typedef struct _GstOSXVideoSink GstOSXVideoSink; +typedef struct _GstOSXVideoSinkClass GstOSXVideoSinkClass; + +#define GST_TYPE_OSXVIDEOBUFFER (gst_osxvideobuffer_get_type()) + +#define GST_IS_OSXVIDEOBUFFER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_OSXVIDEOBUFFER)) +#define GST_OSXVIDEOBUFFER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_OSXVIDEOBUFFER, GstOSXVideoBuffer)) + +typedef struct _GstOSXVideoBuffer GstOSXVideoBuffer; + +struct _GstOSXVideoBuffer { + GstBuffer buffer; /* We extend GstBuffer */ + + CVOpenGLTextureRef texture; + + gint width; + gint height; + + gboolean locked; + + GstOSXVideoSink *osxvideosink; +}; + +/* OSXWindow stuff */ +struct _GstOSXWindow { + gint width, height; + gboolean internal; + GstOSXVideoSinkWindow* win; + GstGLView* gstview; +}; + +struct _GstOSXVideoSink { + /* Our element stuff */ + GstVideoSink videosink; + + GMutex *pool_lock; + GSList *buffer_pool; + + GstOSXWindow *osxwindow; + + gint fps_n; + gint fps_d; + + /* Unused */ + gint pixel_width, pixel_height; + + GstClockTime time; + + gboolean embed; + gboolean fullscreen; + gboolean sw_scaling_failed; +}; + +struct _GstOSXVideoSinkClass { + GstVideoSinkClass parent_class; +}; + +GType gst_osx_video_sink_get_type(void); + +G_END_DECLS + +#endif /* __GST_OSX_VIDEO_SINK_H__ */ diff --git a/sys/osxvideo/osxvideosink.m b/sys/osxvideo/osxvideosink.m new file mode 100644 index 000000000..f3f099754 --- /dev/null +++ b/sys/osxvideo/osxvideosink.m @@ -0,0 +1,474 @@ +/* GStreamer + * OSX video sink + * Copyright (C) 2004-6 Zaheer Abbas Merali <zaheerabbas at merali dot org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +/* Object header */ +#include "osxvideosink.h" + +#import "cocoawindow.h" + +/* Debugging category */ +GST_DEBUG_CATEGORY (gst_debug_osx_video_sink); +#define GST_CAT_DEFAULT gst_debug_osx_video_sink + +/* ElementFactory information */ +static const GstElementDetails gst_osx_video_sink_details = +GST_ELEMENT_DETAILS ("OSX Video sink", + "Sink/Video", + "OSX native videosink", + "Zaheer Abbas Merali <zaheerabbas at merali dot org>"); + +/* Default template - initiated with class struct to allow gst-register to work + without X running */ +static GstStaticPadTemplate gst_osx_video_sink_sink_template_factory = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("video/x-raw-yuv, " + "framerate = (fraction) [ 0, MAX ], " + "width = (int) [ 1, MAX ], " + "height = (int) [ 1, MAX ], " + "format = (fourcc) UYVY") + ); + +enum +{ + ARG_0, + ARG_EMBED, + ARG_FULLSCREEN + /* FILL ME */ +}; + +static GstVideoSinkClass *parent_class = NULL; + +/* cocoa event loop - needed if not run in own app */ +gpointer +cocoa_event_loop (GstOSXVideoSink * vsink) +{ + GST_DEBUG_OBJECT (vsink, "About to start cocoa event loop"); + + [NSApp run]; + + GST_DEBUG_OBJECT (vsink, "Cocoa event loop ended"); + + return NULL; +} + +/* This function handles osx window creation */ +static GstOSXWindow * +gst_osx_video_sink_osxwindow_new (GstOSXVideoSink * osxvideosink, gint width, + gint height) +{ + NSRect rect; + GstOSXWindow *osxwindow = NULL; + + g_return_val_if_fail (GST_IS_OSX_VIDEO_SINK (osxvideosink), NULL); + + osxwindow = g_new0 (GstOSXWindow, 1); + + osxwindow->width = width; + osxwindow->height = height; + osxwindow->internal = TRUE; + + if (osxvideosink->embed == FALSE) { + NSAutoreleasePool *pool; + + rect.origin.x = 100.0; + rect.origin.y = 100.0; + rect.size.width = (float) osxwindow->width; + rect.size.height = (float) osxwindow->height; + + pool =[[NSAutoreleasePool alloc] init]; + + [NSApplication sharedApplication]; + osxwindow->win =[[GstOSXVideoSinkWindow alloc] initWithContentRect: rect styleMask: NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask backing: NSBackingStoreBuffered defer: NO screen:nil]; + [osxwindow->win makeKeyAndOrderFront:NSApp]; + osxwindow->gstview =[osxwindow->win gstView]; + if (osxvideosink->fullscreen) + [osxwindow->gstview setFullScreen:YES]; + + [pool release]; + + /* Start Cocoa event loop */ + g_thread_create ((GThreadFunc) cocoa_event_loop, osxvideosink, FALSE, NULL); + } else { + /* Needs to be embedded */ + + rect.origin.x = 0.0; + rect.origin.y = 0.0; + rect.size.width = (float) osxwindow->width; + rect.size.height = (float) osxwindow->height; + osxwindow->gstview =[[GstGLView alloc] initWithFrame:rect]; + /* send signal + FIXME: need to send a bus message */ + /*g_signal_emit (G_OBJECT(osxvideosink), + gst_osx_video_sink_signals[SIGNAL_VIEW_CREATED], 0, + osxwindow->gstview); */ + } + return osxwindow; +} + +/* This function destroys a GstXWindow */ +static void +gst_osx_video_sink_osxwindow_destroy (GstOSXVideoSink * osxvideosink, + GstOSXWindow * osxwindow) +{ + g_return_if_fail (osxwindow != NULL); + g_return_if_fail (GST_IS_OSX_VIDEO_SINK (osxvideosink)); + + g_free (osxwindow); +} + +/* This function resizes a GstXWindow */ +static void +gst_osx_video_sink_osxwindow_resize (GstOSXVideoSink * osxvideosink, + GstOSXWindow * osxwindow, guint width, guint height) +{ + NSSize size; + + g_return_if_fail (osxwindow != NULL); + g_return_if_fail (GST_IS_OSX_VIDEO_SINK (osxvideosink)); + + //SizeWindow (osxwindow->win, width, height, 1); + osxwindow->width = width; + osxwindow->height = height; + + size.width = width; + size.height = height; + /* Call relevant cocoa function to resize window */ +[osxwindow->win setContentSize:size]; +} + +static void +gst_osx_video_sink_osxwindow_clear (GstOSXVideoSink * osxvideosink, + GstOSXWindow * osxwindow) +{ + + g_return_if_fail (osxwindow != NULL); + g_return_if_fail (GST_IS_OSX_VIDEO_SINK (osxvideosink)); + +} + + +/* Element stuff */ +static gboolean +gst_osx_video_sink_setcaps (GstBaseSink * bsink, GstCaps * caps) +{ + GstOSXVideoSink *osxvideosink; + GstStructure *structure; + gboolean res, result = FALSE; + gint video_width, video_height; + const GValue *framerate; + + osxvideosink = GST_OSX_VIDEO_SINK (bsink); + + GST_DEBUG_OBJECT (osxvideosink, "caps: %" GST_PTR_FORMAT, caps); + + structure = gst_caps_get_structure (caps, 0); + res = gst_structure_get_int (structure, "width", &video_width); + res &= gst_structure_get_int (structure, "height", &video_height); + framerate = gst_structure_get_value (structure, "framerate"); + res &= (framerate != NULL); + + if (!res) { + goto beach; + } + + osxvideosink->fps_n = gst_value_get_fraction_numerator (framerate); + osxvideosink->fps_d = gst_value_get_fraction_denominator (framerate); + + GST_DEBUG_OBJECT (osxvideosink, "our format is: %dx%d video at %d/%d fps", + video_width, video_height, osxvideosink->fps_n, osxvideosink->fps_d); + + GST_VIDEO_SINK_WIDTH (osxvideosink) = video_width; + GST_VIDEO_SINK_HEIGHT (osxvideosink) = video_height; + + gst_osx_video_sink_osxwindow_resize (osxvideosink, osxvideosink->osxwindow, + video_width, video_height); + result = TRUE; + +beach: + return result; + +} + +static GstStateChangeReturn +gst_osx_video_sink_change_state (GstElement * element, + GstStateChange transition) +{ + GstOSXVideoSink *osxvideosink; + + osxvideosink = GST_OSX_VIDEO_SINK (element); + + switch (transition) { + case GST_STATE_CHANGE_NULL_TO_READY: + /* Creating our window and our image */ + if (!osxvideosink->osxwindow) { + GST_VIDEO_SINK_WIDTH (osxvideosink) = 320; + GST_VIDEO_SINK_HEIGHT (osxvideosink) = 240; + osxvideosink->osxwindow = + gst_osx_video_sink_osxwindow_new (osxvideosink, + GST_VIDEO_SINK_WIDTH (osxvideosink), + GST_VIDEO_SINK_HEIGHT (osxvideosink)); + gst_osx_video_sink_osxwindow_clear (osxvideosink, + osxvideosink->osxwindow); + } else { + if (osxvideosink->osxwindow->internal) + gst_osx_video_sink_osxwindow_resize (osxvideosink, + osxvideosink->osxwindow, GST_VIDEO_SINK_WIDTH (osxvideosink), + GST_VIDEO_SINK_HEIGHT (osxvideosink)); + } + break; + case GST_STATE_CHANGE_READY_TO_PAUSED: + GST_DEBUG ("ready to paused"); + if (osxvideosink->osxwindow) + gst_osx_video_sink_osxwindow_clear (osxvideosink, + osxvideosink->osxwindow); + osxvideosink->time = 0; + break; + case GST_STATE_CHANGE_PAUSED_TO_PLAYING: + break; + case GST_STATE_CHANGE_PLAYING_TO_PAUSED: + break; + case GST_STATE_CHANGE_PAUSED_TO_READY: + osxvideosink->fps_n = 0; + osxvideosink->fps_d = 0; + osxvideosink->sw_scaling_failed = FALSE; + GST_VIDEO_SINK_WIDTH (osxvideosink) = 0; + GST_VIDEO_SINK_HEIGHT (osxvideosink) = 0; + break; + case GST_STATE_CHANGE_READY_TO_NULL: + + if (osxvideosink->osxwindow) { + gst_osx_video_sink_osxwindow_destroy (osxvideosink, + osxvideosink->osxwindow); + osxvideosink->osxwindow = NULL; + } + break; + } + + return (GST_ELEMENT_CLASS (parent_class))->change_state (element, transition); + +} + +static GstFlowReturn +gst_osx_video_sink_show_frame (GstBaseSink * bsink, GstBuffer * buf) +{ + GstOSXVideoSink *osxvideosink; + + osxvideosink = GST_OSX_VIDEO_SINK (bsink); + + char *viewdata =[osxvideosink->osxwindow->gstview getTextureBuffer]; + + GST_DEBUG ("show_frame"); + memcpy (viewdata, GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf)); + [osxvideosink->osxwindow->gstview displayTexture]; + + return GST_FLOW_OK; +} + +/* Buffer management */ + + + +/* =========================================== */ +/* */ +/* Init & Class init */ +/* */ +/* =========================================== */ + +static void +gst_osx_video_sink_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstOSXVideoSink *osxvideosink; + + g_return_if_fail (GST_IS_OSX_VIDEO_SINK (object)); + + osxvideosink = GST_OSX_VIDEO_SINK (object); + + switch (prop_id) { + case ARG_EMBED: + osxvideosink->embed = g_value_get_boolean (value); + break; + case ARG_FULLSCREEN: + osxvideosink->fullscreen = g_value_get_boolean (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_osx_video_sink_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstOSXVideoSink *osxvideosink; + + g_return_if_fail (GST_IS_OSX_VIDEO_SINK (object)); + + osxvideosink = GST_OSX_VIDEO_SINK (object); + + switch (prop_id) { + case ARG_EMBED: + g_value_set_boolean (value, osxvideosink->embed); + break; + case ARG_FULLSCREEN: + g_value_set_boolean (value, osxvideosink->fullscreen); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_osx_video_sink_dispose (GObject * object) +{ + GstOSXVideoSink *osxvideosink; + + osxvideosink = GST_OSX_VIDEO_SINK (object); + + g_mutex_free (osxvideosink->pool_lock); + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +gst_osx_video_sink_init (GstOSXVideoSink * osxvideosink) +{ + + + osxvideosink->osxwindow = NULL; + + + osxvideosink->fps_n = 0; + osxvideosink->fps_d = 0; + + osxvideosink->pixel_width = osxvideosink->pixel_height = 1; + osxvideosink->sw_scaling_failed = FALSE; + osxvideosink->embed = FALSE; + osxvideosink->fullscreen = FALSE; + +} + +static void +gst_osx_video_sink_base_init (gpointer g_class) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); + + gst_element_class_set_details (element_class, &gst_osx_video_sink_details); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&gst_osx_video_sink_sink_template_factory)); +} + +static void +gst_osx_video_sink_class_init (GstOSXVideoSinkClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + GstBaseSinkClass *gstbasesink_class; + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + gstbasesink_class = (GstBaseSinkClass *) klass; + + + parent_class = g_type_class_ref (GST_TYPE_VIDEO_SINK); + + gobject_class->dispose = gst_osx_video_sink_dispose; + gobject_class->set_property = gst_osx_video_sink_set_property; + gobject_class->get_property = gst_osx_video_sink_get_property; + + //gstbasesink_class->get_times = gst_osx_video_sink_get_times; + gstbasesink_class->set_caps = gst_osx_video_sink_setcaps; + gstbasesink_class->preroll = gst_osx_video_sink_show_frame; + gstbasesink_class->render = gst_osx_video_sink_show_frame; + gstelement_class->change_state = gst_osx_video_sink_change_state; + + g_object_class_install_property (gobject_class, ARG_EMBED, + g_param_spec_boolean ("embed", "embed", "When enabled, it " + "can be embedded", FALSE, G_PARAM_READWRITE)); + g_object_class_install_property (gobject_class, ARG_FULLSCREEN, + g_param_spec_boolean ("fullscreen", "fullscreen", + "When enabled, the view " "is fullscreen", FALSE, + G_PARAM_READWRITE)); +} + +/* ============================================================= */ +/* */ +/* Public Methods */ +/* */ +/* ============================================================= */ + +/* =========================================== */ +/* */ +/* Object typing & Creation */ +/* */ +/* =========================================== */ + +GType +gst_osx_video_sink_get_type (void) +{ + static GType osxvideosink_type = 0; + + if (!osxvideosink_type) { + static const GTypeInfo osxvideosink_info = { + sizeof (GstOSXVideoSinkClass), + gst_osx_video_sink_base_init, + NULL, + (GClassInitFunc) gst_osx_video_sink_class_init, + NULL, + NULL, + sizeof (GstOSXVideoSink), + 0, + (GInstanceInitFunc) gst_osx_video_sink_init, + }; + + osxvideosink_type = g_type_register_static (GST_TYPE_VIDEO_SINK, + "GstOSXVideoSink", &osxvideosink_info, 0); + + } + + return osxvideosink_type; +} + +static gboolean +plugin_init (GstPlugin * plugin) +{ + + if (!gst_element_register (plugin, "osxvideosink", + GST_RANK_PRIMARY, GST_TYPE_OSX_VIDEO_SINK)) + return FALSE; + + GST_DEBUG_CATEGORY_INIT (gst_debug_osx_video_sink, "osxvideosink", 0, + "osxvideosink element"); + + return TRUE; +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "osxvideo", + "OSX native video output plugin", + plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN) |