3 * Copyright (C) 2004-6 Zaheer Abbas Merali <zaheerabbas at merali dot org>
4 * Copyright (C) 2007,2008,2009 Pioneers of the Inevitable <songbird@songbirdnest.com>
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
16 * You should have received a copy of the GNU Library General Public
17 * License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
21 * The development of this code was made possible due to the involvement of
22 * Pioneers of the Inevitable, the creators of the Songbird Music player.
27 * SECTION:element-osxvideosink
29 * The OSXVideoSink renders video frames to a MacOSX window. The video output
30 * must be directed to a window embedded in an existing NSApp.
32 * When the NSView to be embedded is created an element #GstMessage with a
33 * name of 'have-ns-view' will be created and posted on the bus.
34 * The pointer to the NSView to embed will be in the 'nsview' field of that
35 * message. The application MUST handle this message and embed the view
40 #include <gst/video/videooverlay.h>
41 #include <gst/video/navigation.h>
43 #include "osxvideosink.h"
45 #import "cocoawindow.h"
47 GST_DEBUG_CATEGORY (gst_debug_osx_video_sink);
48 #define GST_CAT_DEFAULT gst_debug_osx_video_sink
50 #ifdef RUN_NS_APP_THREAD
52 extern void _CFRunLoopSetCurrent(CFRunLoopRef rl);
53 extern pthread_t _CFMainPThread;
58 static GstStaticPadTemplate gst_osx_video_sink_sink_template_factory =
59 GST_STATIC_PAD_TEMPLATE ("sink",
62 GST_STATIC_CAPS ("video/x-raw, "
63 "framerate = (fraction) [ 0, MAX ], "
64 "width = (int) [ 1, MAX ], "
65 "height = (int) [ 1, MAX ], "
66 #if G_BYTE_ORDER == G_BIG_ENDIAN
67 "format = (string) YUY2")
69 "format = (string) UYVY")
80 static void gst_osx_video_sink_osxwindow_destroy (GstOSXVideoSink * osxvideosink);
81 static GMutex _run_loop_check_mutex;
82 static GMutex _run_loop_mutex;
83 static GCond _run_loop_cond;
84 static GstOSXVideoSinkClass *sink_class = NULL;
85 static GstVideoSinkClass *parent_class = NULL;
87 /* Helper to trigger calls from the main thread */
89 gst_osx_video_sink_call_from_main_thread(GstOSXVideoSink *osxvideosink,
90 NSObject * object, SEL function, NSObject *data, BOOL waitUntilDone)
94 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
96 if (sink_class->ns_app_thread == NULL){
97 thread = [NSThread mainThread];
99 thread = sink_class->ns_app_thread;
102 [object performSelector:function onThread:thread
103 withObject:data waitUntilDone:waitUntilDone];
107 /* Poll for cocoa events */
109 run_ns_app_loop (void) {
111 NSAutoreleasePool *pool =[[NSAutoreleasePool alloc] init];
112 NSDate *pollTime = nil;
114 #ifdef RUN_NS_APP_THREAD
115 /* when running the loop in a thread we want to sleep as long as possible */
116 pollTime = [NSDate distantFuture];
118 pollTime = [NSDate distantPast];
122 event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:pollTime
123 inMode:NSDefaultRunLoopMode dequeue:YES];
124 [NSApp sendEvent:event];
126 while (event != nil);
131 gst_osx_videosink_check_main_run_loop (GstOSXVideoSink *sink)
133 /* check if the main run loop is running */
137 is_running = [[NSRunLoop mainRunLoop] currentMode] != nil;
141 /* the previous check doesn't always work with main loops that run
142 * cocoa's main run loop manually, like the gdk one, giving false
143 * negatives. This check defers a call to the main thread and waits to
144 * be awaken by this function. */
145 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
146 GstOSXVideoSinkObject * object = (GstOSXVideoSinkObject *) sink->osxvideosinkobject;
149 g_mutex_lock (&_run_loop_mutex);
150 [object performSelectorOnMainThread:
151 @selector(checkMainRunLoop)
152 withObject:nil waitUntilDone:NO];
154 abstime = g_get_monotonic_time () + 100 * 1000;
155 is_running = g_cond_wait_until (&_run_loop_cond,
156 &_run_loop_mutex, abstime);
157 g_mutex_unlock (&_run_loop_mutex);
164 GST_DEBUG_OBJECT(sink, "The main runloop %s is running",
165 is_running ? "" : " not ");
167 sink_class->run_loop_state = GST_OSX_VIDEO_SINK_RUN_LOOP_STATE_RUNNING;
168 sink_class->ns_app_thread = [NSThread mainThread];
170 sink_class->run_loop_state = GST_OSX_VIDEO_SINK_RUN_LOOP_STATE_NOT_RUNNING;
176 gst_osx_video_sink_run_cocoa_loop (GstOSXVideoSink * sink )
178 /* Cocoa applications require a main runloop running to dispatch UI
179 * events and process deferred calls to the main thread through
180 * perfermSelectorOnMainThread.
181 * Since the sink needs to create it's own Cocoa window when no
182 * external NSView is passed to the sink through the GstVideoOverlay API,
183 * we need to run the cocoa mainloop somehow.
184 * This run loop can only be started once, by the first sink needing it
187 g_mutex_lock (&_run_loop_check_mutex);
189 if (sink_class->run_loop_state == GST_OSX_VIDEO_SINK_RUN_LOOP_STATE_UNKNOWN) {
190 gst_osx_videosink_check_main_run_loop (sink);
193 if (sink_class->run_loop_state == GST_OSX_VIDEO_SINK_RUN_LOOP_STATE_RUNNING) {
194 g_mutex_unlock (&_run_loop_check_mutex);
198 if (sink_class->ns_app_thread == NULL) {
199 #ifdef RUN_NS_APP_THREAD
200 /* run the main runloop in a separate thread */
202 /* override [NSThread isMainThread] with our own implementation so that we can
203 * make it believe our dedicated thread is the main thread
205 Method origIsMainThread = class_getClassMethod([NSThread class],
206 NSSelectorFromString(@"isMainThread"));
207 Method ourIsMainThread = class_getClassMethod([GstOSXVideoSinkObject class],
208 NSSelectorFromString(@"isMainThread"));
210 method_exchangeImplementations(origIsMainThread, ourIsMainThread);
212 sink_class->ns_app_thread = [[NSThread alloc]
213 initWithTarget:sink->osxvideosinkobject
214 selector:@selector(nsAppThread) object:nil];
215 [sink_class->ns_app_thread start];
217 g_mutex_lock (&_run_loop_mutex);
218 g_cond_wait (&_run_loop_cond, &_run_loop_mutex);
219 g_mutex_unlock (&_run_loop_mutex);
221 /* assume that there is a GMainLoop and iterate the main runloop from there
223 sink_class->cocoa_timeout = g_timeout_add (10,
224 (GSourceFunc) run_ns_app_loop, NULL);
228 g_mutex_unlock (&_run_loop_check_mutex);
232 gst_osx_video_sink_stop_cocoa_loop (GstOSXVideoSink * osxvideosink)
234 #ifndef RUN_NS_APP_THREAD
235 if (sink_class->cocoa_timeout)
236 g_source_remove(sink_klass->cocoa_timeout);
240 /* This function handles osx window creation */
242 gst_osx_video_sink_osxwindow_create (GstOSXVideoSink * osxvideosink, gint width,
246 GstOSXWindow *osxwindow = NULL;
250 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
252 g_return_val_if_fail (GST_IS_OSX_VIDEO_SINK (osxvideosink), FALSE);
254 GST_DEBUG_OBJECT (osxvideosink, "Creating new OSX window");
256 osxvideosink->osxwindow = osxwindow = g_new0 (GstOSXWindow, 1);
258 osxwindow->width = width;
259 osxwindow->height = height;
260 osxwindow->closed = FALSE;
261 osxwindow->internal = FALSE;
263 /* Allocate our GstGLView for the window, and then tell the application
264 * about it (hopefully it's listening...) */
267 rect.size.width = (float) osxwindow->width;
268 rect.size.height = (float) osxwindow->height;
269 osxwindow->gstview =[[GstGLView alloc] initWithFrame:rect];
271 s = gst_structure_new ("have-ns-view",
272 "nsview", G_TYPE_POINTER, osxwindow->gstview,
275 msg = gst_message_new_element (GST_OBJECT (osxvideosink), s);
276 gst_element_post_message (GST_ELEMENT (osxvideosink), msg);
278 GST_INFO_OBJECT (osxvideosink, "'have-ns-view' message sent");
280 gst_osx_video_sink_run_cocoa_loop (osxvideosink);
281 [osxwindow->gstview setMainThread:sink_class->ns_app_thread];
283 /* check if have-ns-view was handled and osxwindow->gstview was added to a
286 if ([osxwindow->gstview haveSuperview] == NO) {
287 /* have-ns-view wasn't handled, post prepare-xwindow-id */
288 if (osxvideosink->superview == NULL) {
289 GST_INFO_OBJECT (osxvideosink, "emitting prepare-xwindow-id");
290 gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (osxvideosink));
293 if (osxvideosink->superview != NULL) {
294 /* prepare-xwindow-id was handled, we have the superview in
295 * osxvideosink->superview. We now add osxwindow->gstview to the superview
296 * from the main thread
298 GST_INFO_OBJECT (osxvideosink, "we have a superview, adding our view to it");
299 gst_osx_video_sink_call_from_main_thread(osxvideosink, osxwindow->gstview,
300 @selector(addToSuperview:), osxvideosink->superview, NO);
303 if (osxvideosink->embed) {
304 /* the view wasn't added to a superview. It's possible that the
305 * application handled have-ns-view, stored our view internally and is
306 * going to add it to a superview later (webkit does that now).
308 GST_INFO_OBJECT (osxvideosink, "no superview");
310 gst_osx_video_sink_call_from_main_thread(osxvideosink,
311 osxvideosink->osxvideosinkobject,
312 @selector(createInternalWindow), nil, YES);
313 GST_INFO_OBJECT (osxvideosink, "No superview, creating an internal window.");
317 [osxwindow->gstview setNavigation: GST_NAVIGATION(osxvideosink)];
318 [osxvideosink->osxwindow->gstview setKeepAspectRatio: osxvideosink->keep_par];
326 gst_osx_video_sink_osxwindow_destroy (GstOSXVideoSink * osxvideosink)
328 NSAutoreleasePool *pool;
330 g_return_if_fail (GST_IS_OSX_VIDEO_SINK (osxvideosink));
331 pool = [[NSAutoreleasePool alloc] init];
333 gst_osx_video_sink_call_from_main_thread(osxvideosink,
334 osxvideosink->osxvideosinkobject,
335 @selector(destroy), (id) nil, YES);
336 gst_osx_video_sink_stop_cocoa_loop (osxvideosink);
340 /* This function resizes a GstXWindow */
342 gst_osx_video_sink_osxwindow_resize (GstOSXVideoSink * osxvideosink,
343 GstOSXWindow * osxwindow, guint width, guint height)
345 GstOSXVideoSinkObject *object = osxvideosink->osxvideosinkobject;
347 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
348 g_return_if_fail (osxwindow != NULL);
349 g_return_if_fail (GST_IS_OSX_VIDEO_SINK (osxvideosink));
351 osxwindow->width = width;
352 osxwindow->height = height;
354 GST_DEBUG_OBJECT (osxvideosink, "Resizing window to (%d,%d)", width, height);
356 /* Directly resize the underlying view */
357 GST_DEBUG_OBJECT (osxvideosink, "Calling setVideoSize on %p", osxwindow->gstview);
358 gst_osx_video_sink_call_from_main_thread(osxvideosink, object,
359 @selector(resize), (id)nil, YES);
365 gst_osx_video_sink_setcaps (GstBaseSink * bsink, GstCaps * caps)
367 GstOSXVideoSink *osxvideosink;
368 GstStructure *structure;
369 gboolean res, result = FALSE;
370 gint video_width, video_height;
372 osxvideosink = GST_OSX_VIDEO_SINK (bsink);
374 GST_DEBUG_OBJECT (osxvideosink, "caps: %" GST_PTR_FORMAT, caps);
376 structure = gst_caps_get_structure (caps, 0);
377 res = gst_structure_get_int (structure, "width", &video_width);
378 res &= gst_structure_get_int (structure, "height", &video_height);
384 GST_DEBUG_OBJECT (osxvideosink, "our format is: %dx%d video",
385 video_width, video_height);
387 GST_VIDEO_SINK_WIDTH (osxvideosink) = video_width;
388 GST_VIDEO_SINK_HEIGHT (osxvideosink) = video_height;
390 gst_osx_video_sink_osxwindow_resize (osxvideosink, osxvideosink->osxwindow,
391 video_width, video_height);
399 static GstStateChangeReturn
400 gst_osx_video_sink_change_state (GstElement * element,
401 GstStateChange transition)
403 GstOSXVideoSink *osxvideosink;
404 GstStateChangeReturn ret;
406 osxvideosink = GST_OSX_VIDEO_SINK (element);
408 GST_DEBUG_OBJECT (osxvideosink, "%s => %s",
409 gst_element_state_get_name(GST_STATE_TRANSITION_CURRENT (transition)),
410 gst_element_state_get_name(GST_STATE_TRANSITION_NEXT (transition)));
412 switch (transition) {
413 case GST_STATE_CHANGE_NULL_TO_READY:
415 case GST_STATE_CHANGE_READY_TO_PAUSED:
416 /* Creating our window and our image */
417 GST_VIDEO_SINK_WIDTH (osxvideosink) = 320;
418 GST_VIDEO_SINK_HEIGHT (osxvideosink) = 240;
419 if (!gst_osx_video_sink_osxwindow_create (osxvideosink,
420 GST_VIDEO_SINK_WIDTH (osxvideosink),
421 GST_VIDEO_SINK_HEIGHT (osxvideosink))) {
422 ret = GST_STATE_CHANGE_FAILURE;
430 ret = (GST_ELEMENT_CLASS (parent_class))->change_state (element, transition);
432 switch (transition) {
433 case GST_STATE_CHANGE_PAUSED_TO_READY:
434 GST_VIDEO_SINK_WIDTH (osxvideosink) = 0;
435 GST_VIDEO_SINK_HEIGHT (osxvideosink) = 0;
436 gst_osx_video_sink_osxwindow_destroy (osxvideosink);
438 case GST_STATE_CHANGE_READY_TO_NULL:
449 gst_osx_video_sink_show_frame (GstBaseSink * bsink, GstBuffer * buf)
451 GstOSXVideoSink *osxvideosink;
452 GstBufferObject* bufferobject;
453 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
455 osxvideosink = GST_OSX_VIDEO_SINK (bsink);
457 GST_DEBUG ("show_frame");
458 bufferobject = [[GstBufferObject alloc] initWithBuffer:buf];
459 gst_osx_video_sink_call_from_main_thread(osxvideosink,
460 osxvideosink->osxvideosinkobject,
461 @selector(showFrame:), bufferobject, NO);
466 /* Buffer management */
470 /* =========================================== */
472 /* Init & Class init */
474 /* =========================================== */
477 gst_osx_video_sink_set_property (GObject * object, guint prop_id,
478 const GValue * value, GParamSpec * pspec)
480 GstOSXVideoSink *osxvideosink;
482 g_return_if_fail (GST_IS_OSX_VIDEO_SINK (object));
484 osxvideosink = GST_OSX_VIDEO_SINK (object);
488 osxvideosink->embed = g_value_get_boolean(value);
491 osxvideosink->keep_par = g_value_get_boolean(value);
492 if (osxvideosink->osxwindow)
493 [osxvideosink->osxwindow->gstview
494 setKeepAspectRatio: osxvideosink->keep_par];
497 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
503 gst_osx_video_sink_get_property (GObject * object, guint prop_id,
504 GValue * value, GParamSpec * pspec)
506 GstOSXVideoSink *osxvideosink;
508 g_return_if_fail (GST_IS_OSX_VIDEO_SINK (object));
510 osxvideosink = GST_OSX_VIDEO_SINK (object);
514 g_value_set_boolean (value, osxvideosink->embed);
517 g_value_set_boolean (value, osxvideosink->keep_par);
520 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
527 gst_osx_video_sink_init (GstOSXVideoSink * sink)
529 sink->osxwindow = NULL;
530 sink->superview = NULL;
531 sink->osxvideosinkobject = [[GstOSXVideoSinkObject alloc] initWithSink:sink];
532 sink->keep_par = FALSE;
536 gst_osx_video_sink_base_init (gpointer g_class)
538 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
540 gst_element_class_set_static_metadata (element_class, "OSX Video sink",
541 "Sink/Video", "OSX native videosink",
542 "Zaheer Abbas Merali <zaheerabbas at merali dot org>");
544 gst_element_class_add_pad_template (element_class,
545 gst_static_pad_template_get (&gst_osx_video_sink_sink_template_factory));
549 gst_osx_video_sink_finalize (GObject *object)
551 GstOSXVideoSink *osxvideosink = GST_OSX_VIDEO_SINK (object);
553 if (osxvideosink->superview)
554 [osxvideosink->superview release];
556 if (osxvideosink->osxvideosinkobject)
557 [(GstOSXVideoSinkObject*)(osxvideosink->osxvideosinkobject) release];
559 G_OBJECT_CLASS (parent_class)->finalize (object);
563 gst_osx_video_sink_class_init (GstOSXVideoSinkClass * klass)
565 GObjectClass *gobject_class;
566 GstElementClass *gstelement_class;
567 GstBaseSinkClass *gstbasesink_class;
569 gobject_class = (GObjectClass *) klass;
570 gstelement_class = (GstElementClass *) klass;
571 gstbasesink_class = (GstBaseSinkClass *) klass;
573 parent_class = g_type_class_ref (GST_TYPE_VIDEO_SINK);
576 klass->run_loop_state = GST_OSX_VIDEO_SINK_RUN_LOOP_STATE_UNKNOWN;
577 klass->ns_app_thread = NULL;
579 gobject_class->set_property = gst_osx_video_sink_set_property;
580 gobject_class->get_property = gst_osx_video_sink_get_property;
581 gobject_class->finalize = gst_osx_video_sink_finalize;
583 gstbasesink_class->set_caps = gst_osx_video_sink_setcaps;
584 gstbasesink_class->preroll = gst_osx_video_sink_show_frame;
585 gstbasesink_class->render = gst_osx_video_sink_show_frame;
586 gstelement_class->change_state = gst_osx_video_sink_change_state;
589 * GstOSXVideoSink:embed
591 * Set to #TRUE if you are embedding the video window in an application.
595 g_object_class_install_property (gobject_class, ARG_EMBED,
596 g_param_spec_boolean ("embed", "embed", "For ABI compatiblity only, do not use",
597 FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
600 * GstOSXVideoSink:force-aspect-ratio
602 * When enabled, scaling will respect original aspect ratio.
606 g_object_class_install_property (gobject_class, ARG_FORCE_PAR,
607 g_param_spec_boolean ("force-aspect-ratio", "force aspect ration",
608 "When enabled, scaling will respect original aspect ration",
609 TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
613 gst_osx_video_sink_navigation_send_event (GstNavigation * navigation,
614 GstStructure * structure)
616 GstOSXVideoSink *osxvideosink = GST_OSX_VIDEO_SINK (navigation);
619 GstVideoRectangle src, dst, result;
621 gdouble x, y, xscale = 1.0, yscale = 1.0;
623 peer = gst_pad_get_peer (GST_VIDEO_SINK_PAD (osxvideosink));
625 if (!peer || !osxvideosink->osxwindow)
628 event = gst_event_new_navigation (structure);
630 bounds = [osxvideosink->osxwindow->gstview getDrawingBounds];
632 if (osxvideosink->keep_par) {
633 /* We get the frame position using the calculated geometry from _setcaps
634 that respect pixel aspect ratios */
635 src.w = GST_VIDEO_SINK_WIDTH (osxvideosink);
636 src.h = GST_VIDEO_SINK_HEIGHT (osxvideosink);
637 dst.w = bounds.size.width;
638 dst.h = bounds.size.height;
640 gst_video_sink_center_rect (src, dst, &result, TRUE);
641 result.x += bounds.origin.x;
642 result.y += bounds.origin.y;
644 result.x = bounds.origin.x;
645 result.y = bounds.origin.y;
646 result.w = bounds.size.width;
647 result.h = bounds.size.height;
650 /* We calculate scaling using the original video frames geometry to include
651 pixel aspect ratio scaling. */
652 xscale = (gdouble) osxvideosink->osxwindow->width / result.w;
653 yscale = (gdouble) osxvideosink->osxwindow->height / result.h;
655 /* Converting pointer coordinates to the non scaled geometry */
656 if (gst_structure_get_double (structure, "pointer_x", &x)) {
657 x = MIN (x, result.x + result.w);
658 x = MAX (x - result.x, 0);
659 gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE,
660 (gdouble) x * xscale, NULL);
662 if (gst_structure_get_double (structure, "pointer_y", &y)) {
663 y = MIN (y, result.y + result.h);
664 y = MAX (y - result.y, 0);
665 gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE,
666 (gdouble) y * yscale, NULL);
669 gst_pad_send_event (peer, event);
670 gst_object_unref (peer);
674 gst_osx_video_sink_navigation_init (GstNavigationInterface * iface)
676 iface->send_event = gst_osx_video_sink_navigation_send_event;
680 gst_osx_video_sink_set_window_handle (GstVideoOverlay * overlay, guintptr handle_id)
682 GstOSXVideoSink *osxvideosink = GST_OSX_VIDEO_SINK (overlay);
683 gulong window_id = (gulong) handle_id;
684 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
686 if (osxvideosink->superview) {
687 GST_INFO_OBJECT (osxvideosink, "old xwindow id %p", osxvideosink->superview);
688 if (osxvideosink->osxwindow) {
689 gst_osx_video_sink_call_from_main_thread(osxvideosink,
690 osxvideosink->osxwindow->gstview,
691 @selector(removeFromSuperview:), (id)nil, YES);
693 [osxvideosink->superview release];
695 if (osxvideosink->osxwindow != NULL && window_id != 0) {
696 if (osxvideosink->osxwindow->internal) {
697 GST_INFO_OBJECT (osxvideosink, "closing internal window");
698 osxvideosink->osxwindow->closed = TRUE;
699 [osxvideosink->osxwindow->win close];
700 [osxvideosink->osxwindow->win release];
704 GST_INFO_OBJECT (osxvideosink, "set xwindow id 0x%lx", window_id);
705 osxvideosink->superview = [((NSView *) window_id) retain];
706 if (osxvideosink->osxwindow) {
707 gst_osx_video_sink_call_from_main_thread(osxvideosink,
708 osxvideosink->osxwindow->gstview,
709 @selector(addToSuperview:), osxvideosink->superview, YES);
711 osxvideosink->osxwindow->internal = FALSE;
719 gst_osx_video_sink_xoverlay_init (GstVideoOverlayInterface * iface)
721 iface->set_window_handle = gst_osx_video_sink_set_window_handle;
722 iface->expose = NULL;
723 iface->handle_events = NULL;
726 /* ============================================================= */
730 /* ============================================================= */
732 /* =========================================== */
734 /* Object typing & Creation */
736 /* =========================================== */
739 gst_osx_video_sink_get_type (void)
741 static GType osxvideosink_type = 0;
743 if (!osxvideosink_type) {
744 static const GTypeInfo osxvideosink_info = {
745 sizeof (GstOSXVideoSinkClass),
746 gst_osx_video_sink_base_init,
748 (GClassInitFunc) gst_osx_video_sink_class_init,
751 sizeof (GstOSXVideoSink),
753 (GInstanceInitFunc) gst_osx_video_sink_init,
756 static const GInterfaceInfo overlay_info = {
757 (GInterfaceInitFunc) gst_osx_video_sink_xoverlay_init,
762 static const GInterfaceInfo navigation_info = {
763 (GInterfaceInitFunc) gst_osx_video_sink_navigation_init,
767 osxvideosink_type = g_type_register_static (GST_TYPE_VIDEO_SINK,
768 "GstOSXVideoSink", &osxvideosink_info, 0);
770 g_type_add_interface_static (osxvideosink_type, GST_TYPE_VIDEO_OVERLAY,
772 g_type_add_interface_static (osxvideosink_type, GST_TYPE_NAVIGATION,
776 return osxvideosink_type;
779 @implementation GstWindowDelegate
780 - (id) initWithSink: (GstOSXVideoSink *) sink
783 self->osxvideosink = sink;
787 - (void)windowWillClose:(NSNotification *)notification {
788 /* Only handle close events if the window was closed manually by the user
789 * and not becuase of a state change state to READY */
790 if (osxvideosink->osxwindow == NULL) {
793 if (!osxvideosink->osxwindow->closed) {
794 osxvideosink->osxwindow->closed = TRUE;
795 GST_ELEMENT_ERROR (osxvideosink, RESOURCE, NOT_FOUND, ("Output window was closed"), (NULL));
796 gst_osx_video_sink_osxwindow_destroy(osxvideosink);
802 @ implementation GstOSXVideoSinkObject
804 -(id) initWithSink: (GstOSXVideoSink*) sink
807 self->osxvideosink = sink;
811 -(void) createInternalWindow
813 GstOSXWindow *osxwindow = osxvideosink->osxwindow;
814 ProcessSerialNumber psn;
818 osxwindow->internal = TRUE;
820 mask = NSTitledWindowMask |
821 NSClosableWindowMask |
822 NSResizableWindowMask |
823 NSTexturedBackgroundWindowMask |
824 NSMiniaturizableWindowMask;
826 rect.origin.x = 100.0;
827 rect.origin.y = 100.0;
828 rect.size.width = (float) osxwindow->width;
829 rect.size.height = (float) osxwindow->height;
831 #ifndef RUN_NS_APP_THREAD
832 if (!osxvideosink->app_started) {
833 [NSApplication sharedApplication];
834 [NSApp finishLaunching];
835 osxvideosink->app_started = TRUE;
839 if (!GetCurrentProcess(&psn)) {
840 TransformProcessType(&psn, kProcessTransformToForegroundApplication);
841 SetFrontProcess(&psn);
844 osxwindow->win =[[[GstOSXVideoSinkWindow alloc]
845 initWithContentNSRect: rect
847 backing: NSBackingStoreBuffered
849 screen: nil] retain];
850 GST_DEBUG("VideoSinkWindow created, %p", osxwindow->win);
851 [osxwindow->win makeKeyAndOrderFront:NSApp];
852 osxwindow->gstview =[osxwindow->win gstView];
853 [osxwindow->win setDelegate:[[GstWindowDelegate alloc]
854 initWithSink:osxvideosink]];
858 #ifdef RUN_NS_APP_THREAD
859 + (BOOL) isMainThread
861 /* FIXME: ideally we should return YES only for ->ns_app_thread here */
868 GstOSXWindow *osxwindow = osxvideosink->osxwindow;
870 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
872 GST_INFO_OBJECT (osxvideosink, "resizing");
873 NSSize size = {osxwindow->width, osxwindow->height};
874 if (osxwindow->internal) {
875 [osxwindow->win setContentSize:size];
877 if (osxwindow->gstview) {
878 [osxwindow->gstview setVideoSize :(int)osxwindow->width :(int)osxwindow->height];
880 GST_INFO_OBJECT (osxvideosink, "done");
885 - (void) showFrame: (GstBufferObject *) object
889 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
890 GstBuffer *buf = object->buf;
892 if (osxvideosink->osxwindow != NULL)
894 gst_buffer_map (buf, &info, GST_MAP_READ);
895 viewdata = (guint8 *) [osxvideosink->osxwindow->gstview getTextureBuffer];
897 if (G_UNLIKELY (viewdata == NULL)) {
898 GST_ELEMENT_ERROR (osxvideosink, RESOURCE, WRITE,
899 ("Could not get a texture buffer"), (NULL));
901 memcpy (viewdata, info.data, info.size);
902 [osxvideosink->osxwindow->gstview displayTexture];
903 gst_buffer_unmap (buf, &info);
914 NSAutoreleasePool *pool;
915 GstOSXWindow *osxwindow;
917 pool = [[NSAutoreleasePool alloc] init];
919 osxwindow = osxvideosink->osxwindow;
920 osxvideosink->osxwindow = NULL;
923 if (osxvideosink->superview) {
924 [osxwindow->gstview removeFromSuperview];
926 [osxwindow->gstview release];
927 if (osxwindow->internal) {
928 if (!osxwindow->closed) {
929 osxwindow->closed = TRUE;
930 [osxwindow->win close];
931 [osxwindow->win release];
939 #ifdef RUN_NS_APP_THREAD
942 NSAutoreleasePool *pool;
944 /* set the main runloop as the runloop for the current thread. This has the
945 * effect that calling NSApp nextEventMatchingMask:untilDate:inMode:dequeue
946 * runs the main runloop.
948 _CFRunLoopSetCurrent(CFRunLoopGetMain());
950 /* this is needed to make IsMainThread checks in core foundation work from the
953 _CFMainPThread = pthread_self();
955 pool = [[NSAutoreleasePool alloc] init];
957 [NSApplication sharedApplication];
958 [NSApp finishLaunching];
960 g_mutex_lock (&_run_loop_mutex);
961 g_cond_signal (&_run_loop_cond);
962 g_mutex_unlock (&_run_loop_mutex);
971 -(void) checkMainRunLoop
973 g_mutex_lock (&_run_loop_mutex);
974 g_cond_signal (&_run_loop_cond);
975 g_mutex_unlock (&_run_loop_mutex);
980 @ implementation GstBufferObject
981 -(id) initWithBuffer: (GstBuffer*) buffer
984 gst_buffer_ref(buffer);
990 gst_buffer_unref(buf);
996 plugin_init (GstPlugin * plugin)
999 if (!gst_element_register (plugin, "osxvideosink",
1000 GST_RANK_PRIMARY, GST_TYPE_OSX_VIDEO_SINK))
1003 GST_DEBUG_CATEGORY_INIT (gst_debug_osx_video_sink, "osxvideosink", 0,
1004 "osxvideosink element");
1009 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
1012 "OSX native video output plugin",
1013 plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)