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;
56 static GstStaticPadTemplate gst_osx_video_sink_sink_template_factory =
57 GST_STATIC_PAD_TEMPLATE ("sink",
60 GST_STATIC_CAPS ("video/x-raw, "
61 "framerate = (fraction) [ 0, MAX ], "
62 "width = (int) [ 1, MAX ], "
63 "height = (int) [ 1, MAX ], "
64 #if G_BYTE_ORDER == G_BIG_ENDIAN
65 "format = (string) YUY2")
67 "format = (string) UYVY")
78 static void gst_osx_video_sink_osxwindow_destroy (GstOSXVideoSink * osxvideosink);
80 static GstVideoSinkClass *parent_class = NULL;
82 /* Helper to trigger calls from the main thread */
84 gst_osx_video_sink_call_from_main_thread(GstOSXVideoSink *osxvideosink,
85 NSObject * object, SEL function, NSObject *data, BOOL waitUntilDone)
88 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
90 [object performSelector:function onThread:osxvideosink->ns_app_thread
91 withObject:data waitUntilDone:waitUntilDone];
95 /* Poll for cocoa events */
97 run_ns_app_loop (void) {
99 NSAutoreleasePool *pool =[[NSAutoreleasePool alloc] init];
100 NSDate *pollTime = nil;
102 #ifdef RUN_NS_APP_THREAD
103 /* when running the loop in a thread we want to sleep as long as possible */
104 pollTime = [NSDate distantFuture];
106 pollTime = [NSDate distantPast];
110 event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:pollTime
111 inMode:NSDefaultRunLoopMode dequeue:YES];
112 [NSApp sendEvent:event];
114 while (event != nil);
119 gst_osx_videosink_check_main_run_loop (GstOSXVideoSink *sink)
121 /* check if the main run loop is running */
124 if (sink->mrl_check_done) {
128 is_running = [[NSRunLoop mainRunLoop] currentMode] != nil;
132 /* the previous check doesn't always work with main loops that run
133 * cocoa's main run loop manually, like the gdk one, giving false
134 * negatives. This check defers a call to the main thread and waits to
135 * be awaken by this function. */
136 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
137 GstOSXVideoSinkObject * object = (GstOSXVideoSinkObject *) sink->osxvideosinkobject;
140 g_mutex_lock (&sink->mrl_check_lock);
141 [object performSelectorOnMainThread:
142 @selector(checkMainRunLoop)
143 withObject:nil waitUntilDone:NO];
145 abstime = g_get_monotonic_time () + 100 * 1000;
146 is_running = g_cond_wait_until (&sink->mrl_check_cond,
147 &sink->mrl_check_lock, abstime);
148 g_mutex_unlock (&sink->mrl_check_lock);
155 GST_DEBUG_OBJECT(sink, "The main runloop %s is running",
156 is_running ? "" : " not ");
157 sink->main_run_loop_running = is_running;
158 sink->mrl_check_done = TRUE;
163 gst_osx_video_sink_run_cocoa_loop (GstOSXVideoSink * sink )
165 /* Cocoa applications require a main runloop running to dispatch UI
166 * events and process deferred calls to the main thread through
167 * perfermSelectorOnMainThread.
168 * Since the sink needs to create it's own Cocoa window when no
169 * external NSView is passed to the sink through the GstVideoOverlay API,
170 * we need to run the cocoa mainloop somehow.
172 if (!sink->main_run_loop_running) {
173 #ifdef RUN_NS_APP_THREAD
174 /* run the main runloop in a separate thread */
176 /* override [NSThread isMainThread] with our own implementation so that we can
177 * make it believe our dedicated thread is the main thread
179 Method origIsMainThread = class_getClassMethod([NSThread class],
180 NSSelectorFromString(@"isMainThread"));
181 Method ourIsMainThread = class_getClassMethod([GstOSXVideoSinkObject class],
182 NSSelectorFromString(@"isMainThread"));
184 method_exchangeImplementations(origIsMainThread, ourIsMainThread);
186 sink->ns_app_thread = [[NSThread alloc]
187 initWithTarget:sink->osxvideosinkobject
188 selector:@selector(nsAppThread) object:nil];
189 [sink->ns_app_thread start];
191 g_mutex_lock (&sink->loop_thread_lock);
192 while (!sink->app_started)
193 g_cond_wait (&sink->loop_thread_cond, &sink->loop_thread_lock);
194 g_mutex_unlock (&sink->loop_thread_lock);
196 /* assume that there is a GMainLoop and iterate the main runloop from there
198 sink->cocoa_timeout = g_timeout_add (10,
199 (GSourceFunc) run_ns_app_loop, NULL);
205 gst_osx_video_sink_stop_cocoa_loop (GstOSXVideoSink * osxvideosink)
207 #ifndef RUN_NS_APP_THREAD
208 if (osxvideosink->cocoa_timeout)
209 g_source_remove(osxvideosink->cocoa_timeout);
213 /* This function handles osx window creation */
215 gst_osx_video_sink_osxwindow_create (GstOSXVideoSink * osxvideosink, gint width,
219 GstOSXWindow *osxwindow = NULL;
223 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
225 g_return_val_if_fail (GST_IS_OSX_VIDEO_SINK (osxvideosink), FALSE);
227 GST_DEBUG_OBJECT (osxvideosink, "Creating new OSX window");
229 osxvideosink->osxwindow = osxwindow = g_new0 (GstOSXWindow, 1);
231 osxwindow->width = width;
232 osxwindow->height = height;
233 osxwindow->closed = FALSE;
234 osxwindow->internal = FALSE;
236 /* Allocate our GstGLView for the window, and then tell the application
237 * about it (hopefully it's listening...) */
240 rect.size.width = (float) osxwindow->width;
241 rect.size.height = (float) osxwindow->height;
242 osxwindow->gstview =[[GstGLView alloc] initWithFrame:rect];
244 s = gst_structure_new ("have-ns-view",
245 "nsview", G_TYPE_POINTER, osxwindow->gstview,
248 msg = gst_message_new_element (GST_OBJECT (osxvideosink), s);
249 gst_element_post_message (GST_ELEMENT (osxvideosink), msg);
251 GST_INFO_OBJECT (osxvideosink, "'have-ns-view' message sent");
253 osxvideosink->ns_app_thread = [NSThread mainThread];
254 gst_osx_videosink_check_main_run_loop (osxvideosink);
255 gst_osx_video_sink_run_cocoa_loop (osxvideosink);
256 [osxwindow->gstview setMainThread:osxvideosink->ns_app_thread];
258 /* check if have-ns-view was handled and osxwindow->gstview was added to a
261 if ([osxwindow->gstview haveSuperview] == NO) {
262 /* have-ns-view wasn't handled, post prepare-xwindow-id */
263 if (osxvideosink->superview == NULL) {
264 GST_INFO_OBJECT (osxvideosink, "emitting prepare-xwindow-id");
265 gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (osxvideosink));
268 if (osxvideosink->superview != NULL) {
269 /* prepare-xwindow-id was handled, we have the superview in
270 * osxvideosink->superview. We now add osxwindow->gstview to the superview
271 * from the main thread
273 GST_INFO_OBJECT (osxvideosink, "we have a superview, adding our view to it");
274 gst_osx_video_sink_call_from_main_thread(osxvideosink, osxwindow->gstview,
275 @selector(addToSuperview:), osxvideosink->superview, NO);
278 if (osxvideosink->embed) {
279 /* the view wasn't added to a superview. It's possible that the
280 * application handled have-ns-view, stored our view internally and is
281 * going to add it to a superview later (webkit does that now).
283 GST_INFO_OBJECT (osxvideosink, "no superview");
285 gst_osx_video_sink_call_from_main_thread(osxvideosink,
286 osxvideosink->osxvideosinkobject,
287 @selector(createInternalWindow), nil, YES);
288 GST_INFO_OBJECT (osxvideosink, "No superview, creating an internal window.");
292 [osxwindow->gstview setNavigation: GST_NAVIGATION(osxvideosink)];
293 [osxvideosink->osxwindow->gstview setKeepAspectRatio: osxvideosink->keep_par];
301 gst_osx_video_sink_osxwindow_destroy (GstOSXVideoSink * osxvideosink)
303 NSAutoreleasePool *pool;
305 g_return_if_fail (GST_IS_OSX_VIDEO_SINK (osxvideosink));
306 pool = [[NSAutoreleasePool alloc] init];
308 gst_osx_video_sink_call_from_main_thread(osxvideosink,
309 osxvideosink->osxvideosinkobject,
310 @selector(destroy), (id) nil, YES);
311 gst_osx_video_sink_stop_cocoa_loop (osxvideosink);
315 /* This function resizes a GstXWindow */
317 gst_osx_video_sink_osxwindow_resize (GstOSXVideoSink * osxvideosink,
318 GstOSXWindow * osxwindow, guint width, guint height)
320 GstOSXVideoSinkObject *object = osxvideosink->osxvideosinkobject;
322 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
323 g_return_if_fail (osxwindow != NULL);
324 g_return_if_fail (GST_IS_OSX_VIDEO_SINK (osxvideosink));
326 osxwindow->width = width;
327 osxwindow->height = height;
329 GST_DEBUG_OBJECT (osxvideosink, "Resizing window to (%d,%d)", width, height);
331 /* Directly resize the underlying view */
332 GST_DEBUG_OBJECT (osxvideosink, "Calling setVideoSize on %p", osxwindow->gstview);
333 gst_osx_video_sink_call_from_main_thread(osxvideosink, object,
334 @selector(resize), (id)nil, YES);
340 gst_osx_video_sink_setcaps (GstBaseSink * bsink, GstCaps * caps)
342 GstOSXVideoSink *osxvideosink;
343 GstStructure *structure;
344 gboolean res, result = FALSE;
345 gint video_width, video_height;
347 osxvideosink = GST_OSX_VIDEO_SINK (bsink);
349 GST_DEBUG_OBJECT (osxvideosink, "caps: %" GST_PTR_FORMAT, caps);
351 structure = gst_caps_get_structure (caps, 0);
352 res = gst_structure_get_int (structure, "width", &video_width);
353 res &= gst_structure_get_int (structure, "height", &video_height);
359 GST_DEBUG_OBJECT (osxvideosink, "our format is: %dx%d video",
360 video_width, video_height);
362 GST_VIDEO_SINK_WIDTH (osxvideosink) = video_width;
363 GST_VIDEO_SINK_HEIGHT (osxvideosink) = video_height;
365 gst_osx_video_sink_osxwindow_resize (osxvideosink, osxvideosink->osxwindow,
366 video_width, video_height);
374 static GstStateChangeReturn
375 gst_osx_video_sink_change_state (GstElement * element,
376 GstStateChange transition)
378 GstOSXVideoSink *osxvideosink;
379 GstStateChangeReturn ret;
381 osxvideosink = GST_OSX_VIDEO_SINK (element);
383 GST_DEBUG_OBJECT (osxvideosink, "%s => %s",
384 gst_element_state_get_name(GST_STATE_TRANSITION_CURRENT (transition)),
385 gst_element_state_get_name(GST_STATE_TRANSITION_NEXT (transition)));
387 switch (transition) {
388 case GST_STATE_CHANGE_NULL_TO_READY:
390 case GST_STATE_CHANGE_READY_TO_PAUSED:
391 /* Creating our window and our image */
392 GST_VIDEO_SINK_WIDTH (osxvideosink) = 320;
393 GST_VIDEO_SINK_HEIGHT (osxvideosink) = 240;
394 if (!gst_osx_video_sink_osxwindow_create (osxvideosink,
395 GST_VIDEO_SINK_WIDTH (osxvideosink),
396 GST_VIDEO_SINK_HEIGHT (osxvideosink))) {
397 ret = GST_STATE_CHANGE_FAILURE;
405 ret = (GST_ELEMENT_CLASS (parent_class))->change_state (element, transition);
407 switch (transition) {
408 case GST_STATE_CHANGE_PAUSED_TO_READY:
409 GST_VIDEO_SINK_WIDTH (osxvideosink) = 0;
410 GST_VIDEO_SINK_HEIGHT (osxvideosink) = 0;
411 osxvideosink->app_started = FALSE;
412 gst_osx_video_sink_osxwindow_destroy (osxvideosink);
414 case GST_STATE_CHANGE_READY_TO_NULL:
425 gst_osx_video_sink_show_frame (GstBaseSink * bsink, GstBuffer * buf)
427 GstOSXVideoSink *osxvideosink;
428 GstBufferObject* bufferobject;
429 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
431 osxvideosink = GST_OSX_VIDEO_SINK (bsink);
433 GST_DEBUG ("show_frame");
434 bufferobject = [[GstBufferObject alloc] initWithBuffer:buf];
435 gst_osx_video_sink_call_from_main_thread(osxvideosink,
436 osxvideosink->osxvideosinkobject,
437 @selector(showFrame:), bufferobject, NO);
442 /* Buffer management */
446 /* =========================================== */
448 /* Init & Class init */
450 /* =========================================== */
453 gst_osx_video_sink_set_property (GObject * object, guint prop_id,
454 const GValue * value, GParamSpec * pspec)
456 GstOSXVideoSink *osxvideosink;
458 g_return_if_fail (GST_IS_OSX_VIDEO_SINK (object));
460 osxvideosink = GST_OSX_VIDEO_SINK (object);
464 osxvideosink->embed = g_value_get_boolean(value);
467 osxvideosink->keep_par = g_value_get_boolean(value);
468 if (osxvideosink->osxwindow)
469 [osxvideosink->osxwindow->gstview
470 setKeepAspectRatio: osxvideosink->keep_par];
473 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
479 gst_osx_video_sink_get_property (GObject * object, guint prop_id,
480 GValue * value, GParamSpec * pspec)
482 GstOSXVideoSink *osxvideosink;
484 g_return_if_fail (GST_IS_OSX_VIDEO_SINK (object));
486 osxvideosink = GST_OSX_VIDEO_SINK (object);
490 g_value_set_boolean (value, osxvideosink->embed);
493 g_value_set_boolean (value, osxvideosink->keep_par);
496 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
503 gst_osx_video_sink_init (GstOSXVideoSink * sink)
505 sink->osxwindow = NULL;
506 sink->superview = NULL;
507 sink->osxvideosinkobject = [[GstOSXVideoSinkObject alloc] initWithSink:sink];
508 #ifdef RUN_NS_APP_THREAD
509 g_mutex_init (&sink->loop_thread_lock);
510 g_cond_init (&sink->loop_thread_cond);
512 g_mutex_init (&sink->mrl_check_lock);
513 g_cond_init (&sink->mrl_check_cond);
514 sink->mrl_check_done = FALSE;
515 sink->main_run_loop_running = FALSE;
516 sink->app_started = FALSE;
517 sink->keep_par = FALSE;
521 gst_osx_video_sink_base_init (gpointer g_class)
523 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
525 gst_element_class_set_static_metadata (element_class, "OSX Video sink",
526 "Sink/Video", "OSX native videosink",
527 "Zaheer Abbas Merali <zaheerabbas at merali dot org>");
529 gst_element_class_add_pad_template (element_class,
530 gst_static_pad_template_get (&gst_osx_video_sink_sink_template_factory));
534 gst_osx_video_sink_finalize (GObject *object)
536 GstOSXVideoSink *osxvideosink = GST_OSX_VIDEO_SINK (object);
538 if (osxvideosink->superview)
539 [osxvideosink->superview release];
541 if (osxvideosink->osxvideosinkobject)
542 [(GstOSXVideoSinkObject*)(osxvideosink->osxvideosinkobject) release];
544 g_mutex_clear (&osxvideosink->mrl_check_lock);
545 g_cond_clear (&osxvideosink->mrl_check_cond);
547 G_OBJECT_CLASS (parent_class)->finalize (object);
551 gst_osx_video_sink_class_init (GstOSXVideoSinkClass * klass)
553 GObjectClass *gobject_class;
554 GstElementClass *gstelement_class;
555 GstBaseSinkClass *gstbasesink_class;
557 gobject_class = (GObjectClass *) klass;
558 gstelement_class = (GstElementClass *) klass;
559 gstbasesink_class = (GstBaseSinkClass *) klass;
562 parent_class = g_type_class_ref (GST_TYPE_VIDEO_SINK);
564 gobject_class->set_property = gst_osx_video_sink_set_property;
565 gobject_class->get_property = gst_osx_video_sink_get_property;
566 gobject_class->finalize = gst_osx_video_sink_finalize;
568 gstbasesink_class->set_caps = gst_osx_video_sink_setcaps;
569 gstbasesink_class->preroll = gst_osx_video_sink_show_frame;
570 gstbasesink_class->render = gst_osx_video_sink_show_frame;
571 gstelement_class->change_state = gst_osx_video_sink_change_state;
574 * GstOSXVideoSink:embed
576 * Set to #TRUE if you are embedding the video window in an application.
580 g_object_class_install_property (gobject_class, ARG_EMBED,
581 g_param_spec_boolean ("embed", "embed", "For ABI compatiblity only, do not use",
582 FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
585 * GstOSXVideoSink:force-aspect-ratio
587 * When enabled, scaling will respect original aspect ratio.
591 g_object_class_install_property (gobject_class, ARG_FORCE_PAR,
592 g_param_spec_boolean ("force-aspect-ratio", "force aspect ration",
593 "When enabled, scaling will respect original aspect ration",
594 TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
598 gst_osx_video_sink_navigation_send_event (GstNavigation * navigation,
599 GstStructure * structure)
601 GstOSXVideoSink *osxvideosink = GST_OSX_VIDEO_SINK (navigation);
604 GstVideoRectangle src, dst, result;
606 gdouble x, y, xscale = 1.0, yscale = 1.0;
608 peer = gst_pad_get_peer (GST_VIDEO_SINK_PAD (osxvideosink));
610 if (!peer || !osxvideosink->osxwindow)
613 event = gst_event_new_navigation (structure);
615 bounds = [osxvideosink->osxwindow->gstview getDrawingBounds];
617 if (osxvideosink->keep_par) {
618 /* We get the frame position using the calculated geometry from _setcaps
619 that respect pixel aspect ratios */
620 src.w = GST_VIDEO_SINK_WIDTH (osxvideosink);
621 src.h = GST_VIDEO_SINK_HEIGHT (osxvideosink);
622 dst.w = bounds.size.width;
623 dst.h = bounds.size.height;
625 gst_video_sink_center_rect (src, dst, &result, TRUE);
626 result.x += bounds.origin.x;
627 result.y += bounds.origin.y;
629 result.x = bounds.origin.x;
630 result.y = bounds.origin.y;
631 result.w = bounds.size.width;
632 result.h = bounds.size.height;
635 /* We calculate scaling using the original video frames geometry to include
636 pixel aspect ratio scaling. */
637 xscale = (gdouble) osxvideosink->osxwindow->width / result.w;
638 yscale = (gdouble) osxvideosink->osxwindow->height / result.h;
640 /* Converting pointer coordinates to the non scaled geometry */
641 if (gst_structure_get_double (structure, "pointer_x", &x)) {
642 x = MIN (x, result.x + result.w);
643 x = MAX (x - result.x, 0);
644 gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE,
645 (gdouble) x * xscale, NULL);
647 if (gst_structure_get_double (structure, "pointer_y", &y)) {
648 y = MIN (y, result.y + result.h);
649 y = MAX (y - result.y, 0);
650 gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE,
651 (gdouble) y * yscale, NULL);
654 gst_pad_send_event (peer, event);
655 gst_object_unref (peer);
659 gst_osx_video_sink_navigation_init (GstNavigationInterface * iface)
661 iface->send_event = gst_osx_video_sink_navigation_send_event;
665 gst_osx_video_sink_set_window_handle (GstVideoOverlay * overlay, guintptr handle_id)
667 GstOSXVideoSink *osxvideosink = GST_OSX_VIDEO_SINK (overlay);
668 gulong window_id = (gulong) handle_id;
670 if (osxvideosink->superview) {
671 GST_INFO_OBJECT (osxvideosink, "old xwindow id %p", osxvideosink->superview);
672 if (osxvideosink->osxwindow) {
673 gst_osx_video_sink_call_from_main_thread(osxvideosink,
674 osxvideosink->osxwindow->gstview,
675 @selector(removeFromSuperview:), (id)nil, YES);
677 [osxvideosink->superview release];
681 GST_INFO_OBJECT (osxvideosink, "set xwindow id 0x%lx", window_id);
682 osxvideosink->superview = [((NSView *) window_id) retain];
683 if (osxvideosink->osxwindow) {
684 gst_osx_video_sink_call_from_main_thread(osxvideosink,
685 osxvideosink->osxwindow->gstview,
686 @selector(addToSuperview:), osxvideosink->superview, YES);
688 osxvideosink->osxwindow->internal = FALSE;
695 gst_osx_video_sink_xoverlay_init (GstVideoOverlayInterface * iface)
697 iface->set_window_handle = gst_osx_video_sink_set_window_handle;
698 iface->expose = NULL;
699 iface->handle_events = NULL;
702 /* ============================================================= */
706 /* ============================================================= */
708 /* =========================================== */
710 /* Object typing & Creation */
712 /* =========================================== */
715 gst_osx_video_sink_get_type (void)
717 static GType osxvideosink_type = 0;
719 if (!osxvideosink_type) {
720 static const GTypeInfo osxvideosink_info = {
721 sizeof (GstOSXVideoSinkClass),
722 gst_osx_video_sink_base_init,
724 (GClassInitFunc) gst_osx_video_sink_class_init,
727 sizeof (GstOSXVideoSink),
729 (GInstanceInitFunc) gst_osx_video_sink_init,
732 static const GInterfaceInfo overlay_info = {
733 (GInterfaceInitFunc) gst_osx_video_sink_xoverlay_init,
738 static const GInterfaceInfo navigation_info = {
739 (GInterfaceInitFunc) gst_osx_video_sink_navigation_init,
743 osxvideosink_type = g_type_register_static (GST_TYPE_VIDEO_SINK,
744 "GstOSXVideoSink", &osxvideosink_info, 0);
746 g_type_add_interface_static (osxvideosink_type, GST_TYPE_VIDEO_OVERLAY,
748 g_type_add_interface_static (osxvideosink_type, GST_TYPE_NAVIGATION,
752 return osxvideosink_type;
755 @implementation GstWindowDelegate
756 - (id) initWithSink: (GstOSXVideoSink *) sink
759 self->osxvideosink = sink;
763 - (void)windowWillClose:(NSNotification *)notification {
764 /* Only handle close events if the window was closed manually by the user
765 * and not becuase of a state change state to READY */
766 if (!osxvideosink->osxwindow->closed) {
767 osxvideosink->osxwindow->closed = TRUE;
768 GST_ELEMENT_ERROR (osxvideosink, RESOURCE, NOT_FOUND, ("Output window was closed"), (NULL));
769 gst_osx_video_sink_osxwindow_destroy(osxvideosink);
775 @ implementation GstOSXVideoSinkObject
777 -(id) initWithSink: (GstOSXVideoSink*) sink
780 self->osxvideosink = sink;
784 -(void) createInternalWindow
786 GstOSXWindow *osxwindow = osxvideosink->osxwindow;
787 ProcessSerialNumber psn;
791 osxwindow->internal = TRUE;
793 mask = NSTitledWindowMask |
794 NSClosableWindowMask |
795 NSResizableWindowMask |
796 NSTexturedBackgroundWindowMask |
797 NSMiniaturizableWindowMask;
799 rect.origin.x = 100.0;
800 rect.origin.y = 100.0;
801 rect.size.width = (float) osxwindow->width;
802 rect.size.height = (float) osxwindow->height;
804 #ifndef RUN_NS_APP_THREAD
805 if (!osxvideosink->app_started) {
806 [NSApplication sharedApplication];
807 [NSApp finishLaunching];
808 osxvideosink->app_started = TRUE;
812 if (!GetCurrentProcess(&psn)) {
813 TransformProcessType(&psn, kProcessTransformToForegroundApplication);
814 SetFrontProcess(&psn);
817 osxwindow->win =[[GstOSXVideoSinkWindow alloc]
818 initWithContentNSRect: rect
820 backing: NSBackingStoreBuffered
823 GST_DEBUG("VideoSinkWindow created, %p", osxwindow->win);
824 [osxwindow->win makeKeyAndOrderFront:NSApp];
825 osxwindow->gstview =[osxwindow->win gstView];
826 [osxwindow->win setDelegate:[[GstWindowDelegate alloc]
827 initWithSink:osxvideosink]];
831 #ifdef RUN_NS_APP_THREAD
832 + (BOOL) isMainThread
834 /* FIXME: ideally we should return YES only for ->ns_app_thread here */
841 GstOSXWindow *osxwindow = osxvideosink->osxwindow;
843 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
845 GST_INFO_OBJECT (osxvideosink, "resizing");
846 NSSize size = {osxwindow->width, osxwindow->height};
847 if (osxwindow->internal) {
848 [osxwindow->win setContentSize:size];
850 if (osxwindow->gstview) {
851 [osxwindow->gstview setVideoSize :(int)osxwindow->width :(int)osxwindow->height];
853 GST_INFO_OBJECT (osxvideosink, "done");
858 - (void) showFrame: (GstBufferObject *) object
862 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
863 GstBuffer *buf = object->buf;
865 if (osxvideosink->osxwindow != NULL)
867 gst_buffer_map (buf, &info, GST_MAP_READ);
868 viewdata = (guint8 *) [osxvideosink->osxwindow->gstview getTextureBuffer];
870 memcpy (viewdata, info.data, info.size);
871 [osxvideosink->osxwindow->gstview displayTexture];
872 gst_buffer_unmap (buf, &info);
882 NSAutoreleasePool *pool;
883 GstOSXWindow *osxwindow;
885 pool = [[NSAutoreleasePool alloc] init];
887 osxwindow = osxvideosink->osxwindow;
888 osxvideosink->osxwindow = NULL;
891 if (osxvideosink->superview) {
892 [osxwindow->gstview removeFromSuperview];
894 [osxwindow->gstview release];
895 if (osxwindow->internal) {
896 if (!osxwindow->closed) {
897 osxwindow->closed = TRUE;
898 [osxwindow->win release];
907 #ifdef RUN_NS_APP_THREAD
910 NSAutoreleasePool *pool;
911 GstOSXVideoSink *sink = osxvideosink;
913 /* set the main runloop as the runloop for the current thread. This has the
914 * effect that calling NSApp nextEventMatchingMask:untilDate:inMode:dequeue
915 * runs the main runloop.
917 _CFRunLoopSetCurrent(CFRunLoopGetMain());
919 /* this is needed to make IsMainThread checks in core foundation work from the
922 _CFMainPThread = pthread_self();
924 pool = [[NSAutoreleasePool alloc] init];
926 [NSApplication sharedApplication];
927 [NSApp finishLaunching];
929 g_mutex_lock (&sink->loop_thread_lock);
930 sink->app_started = TRUE;
931 g_cond_signal (&sink->loop_thread_cond);
932 g_mutex_unlock (&sink->loop_thread_lock);
941 -(void) checkMainRunLoop
943 g_mutex_lock (&osxvideosink->mrl_check_lock);
944 g_cond_signal (&osxvideosink->mrl_check_cond);
945 g_mutex_unlock (&osxvideosink->mrl_check_lock);
950 @ implementation GstBufferObject
951 -(id) initWithBuffer: (GstBuffer*) buffer
954 gst_buffer_ref(buffer);
960 gst_buffer_unref(buf);
966 plugin_init (GstPlugin * plugin)
969 if (!gst_element_register (plugin, "osxvideosink",
970 GST_RANK_PRIMARY, GST_TYPE_OSX_VIDEO_SINK))
973 GST_DEBUG_CATEGORY_INIT (gst_debug_osx_video_sink, "osxvideosink", 0,
974 "osxvideosink element");
979 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
982 "OSX native video output plugin",
983 plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)