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.
35 #include <gst/video/videooverlay.h>
36 #include <gst/video/navigation.h>
37 #include <gst/video/video.h>
39 #include "osxvideosink.h"
41 #import "cocoawindow.h"
43 GST_DEBUG_CATEGORY (gst_debug_osx_video_sink);
44 #define GST_CAT_DEFAULT gst_debug_osx_video_sink
47 extern void _CFRunLoopSetCurrent (CFRunLoopRef rl);
48 extern pthread_t _CFMainPThread;
52 static GstStaticPadTemplate gst_osx_video_sink_sink_template_factory =
53 GST_STATIC_PAD_TEMPLATE ("sink",
56 GST_STATIC_CAPS ("video/x-raw, "
57 "framerate = (fraction) [ 0, MAX ], "
58 "width = (int) [ 1, MAX ], "
59 "height = (int) [ 1, MAX ], "
60 #if G_BYTE_ORDER == G_BIG_ENDIAN
61 "format = (string) YUY2")
63 "format = (string) UYVY")
74 static void gst_osx_video_sink_osxwindow_destroy (GstOSXVideoSink * osxvideosink);
75 static GMutex _run_loop_check_mutex;
76 static GMutex _run_loop_mutex;
77 static GCond _run_loop_cond;
78 static GstOSXVideoSinkClass *sink_class = NULL;
79 static GstVideoSinkClass *parent_class = NULL;
81 /* Helper to trigger calls from the main thread */
83 gst_osx_video_sink_call_from_main_thread(GstOSXVideoSink *osxvideosink,
84 NSObject * object, SEL function, NSObject *data, BOOL waitUntilDone)
88 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
90 if (sink_class->ns_app_thread == NULL){
91 thread = [NSThread mainThread];
93 thread = sink_class->ns_app_thread;
96 [object performSelector:function onThread:thread
97 withObject:data waitUntilDone:waitUntilDone];
101 /* Poll for cocoa events */
103 run_ns_app_loop (void) {
105 NSAutoreleasePool *pool =[[NSAutoreleasePool alloc] init];
106 NSDate *pollTime = nil;
108 /* when running the loop in a thread we want to sleep as long as possible */
109 pollTime = [NSDate distantFuture];
112 event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:pollTime
113 inMode:NSDefaultRunLoopMode dequeue:YES];
114 [NSApp sendEvent:event];
116 while (event != nil);
121 gst_osx_videosink_check_main_run_loop (GstOSXVideoSink *sink)
123 /* check if the main run loop is running */
127 is_running = [[NSRunLoop mainRunLoop] currentMode] != nil;
131 /* the previous check doesn't always work with main loops that run
132 * cocoa's main run loop manually, like the gdk one, giving false
133 * negatives. This check defers a call to the main thread and waits to
134 * be awaken by this function. */
135 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
136 GstOSXVideoSinkObject * object = (GstOSXVideoSinkObject *) sink->osxvideosinkobject;
139 g_mutex_lock (&_run_loop_mutex);
140 [object performSelectorOnMainThread:
141 @selector(checkMainRunLoop)
142 withObject:nil waitUntilDone:NO];
144 abstime = g_get_monotonic_time () + 100 * 1000;
145 is_running = g_cond_wait_until (&_run_loop_cond,
146 &_run_loop_mutex, abstime);
147 g_mutex_unlock (&_run_loop_mutex);
154 GST_DEBUG_OBJECT(sink, "The main runloop %s is running",
155 is_running ? "" : " not ");
157 sink_class->run_loop_state = GST_OSX_VIDEO_SINK_RUN_LOOP_STATE_RUNNING;
158 sink_class->ns_app_thread = [NSThread mainThread];
160 sink_class->run_loop_state = GST_OSX_VIDEO_SINK_RUN_LOOP_STATE_NOT_RUNNING;
166 gst_osx_video_sink_run_cocoa_loop (GstOSXVideoSink * sink )
168 /* Cocoa applications require a main runloop running to dispatch UI
169 * events and process deferred calls to the main thread through
170 * perfermSelectorOnMainThread.
171 * Since the sink needs to create it's own Cocoa window when no
172 * external NSView is passed to the sink through the GstVideoOverlay API,
173 * we need to run the cocoa mainloop somehow.
174 * This run loop can only be started once, by the first sink needing it
177 g_mutex_lock (&_run_loop_check_mutex);
179 if (sink_class->run_loop_state == GST_OSX_VIDEO_SINK_RUN_LOOP_STATE_UNKNOWN) {
180 gst_osx_videosink_check_main_run_loop (sink);
183 if (sink_class->run_loop_state == GST_OSX_VIDEO_SINK_RUN_LOOP_STATE_RUNNING) {
184 g_mutex_unlock (&_run_loop_check_mutex);
188 if (sink_class->ns_app_thread == NULL) {
189 /* run the main runloop in a separate thread */
191 /* override [NSThread isMainThread] with our own implementation so that we can
192 * make it believe our dedicated thread is the main thread
194 Method origIsMainThread = class_getClassMethod([NSThread class],
195 NSSelectorFromString(@"isMainThread"));
196 Method ourIsMainThread = class_getClassMethod([GstOSXVideoSinkObject class],
197 NSSelectorFromString(@"isMainThread"));
199 method_exchangeImplementations(origIsMainThread, ourIsMainThread);
201 sink_class->ns_app_thread = [[NSThread alloc]
202 initWithTarget:sink->osxvideosinkobject
203 selector:@selector(nsAppThread) object:nil];
204 [sink_class->ns_app_thread start];
206 g_mutex_lock (&_run_loop_mutex);
207 g_cond_wait (&_run_loop_cond, &_run_loop_mutex);
208 g_mutex_unlock (&_run_loop_mutex);
211 g_mutex_unlock (&_run_loop_check_mutex);
215 gst_osx_video_sink_stop_cocoa_loop (GstOSXVideoSink * osxvideosink)
219 /* This function handles osx window creation */
221 gst_osx_video_sink_osxwindow_create (GstOSXVideoSink * osxvideosink, gint width,
225 GstOSXWindow *osxwindow = NULL;
227 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
229 g_return_val_if_fail (GST_IS_OSX_VIDEO_SINK (osxvideosink), FALSE);
231 GST_DEBUG_OBJECT (osxvideosink, "Creating new OSX window");
233 osxvideosink->osxwindow = osxwindow = g_new0 (GstOSXWindow, 1);
235 osxwindow->width = width;
236 osxwindow->height = height;
237 osxwindow->closed = FALSE;
238 osxwindow->internal = FALSE;
240 /* Allocate our GstGLView for the window, and then tell the application
241 * about it (hopefully it's listening...) */
244 rect.size.width = (float) osxwindow->width;
245 rect.size.height = (float) osxwindow->height;
246 osxwindow->gstview =[[GstGLView alloc] initWithFrame:rect];
249 gst_osx_video_sink_run_cocoa_loop (osxvideosink);
250 [osxwindow->gstview setMainThread:sink_class->ns_app_thread];
252 if (osxvideosink->superview == NULL) {
253 GST_INFO_OBJECT (osxvideosink, "emitting prepare-xwindow-id");
254 gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (osxvideosink));
257 if (osxvideosink->superview != NULL) {
258 /* prepare-xwindow-id was handled, we have the superview in
259 * osxvideosink->superview. We now add osxwindow->gstview to the superview
260 * from the main thread
262 GST_INFO_OBJECT (osxvideosink, "we have a superview, adding our view to it");
263 gst_osx_video_sink_call_from_main_thread(osxvideosink, osxwindow->gstview,
264 @selector(addToSuperview:), osxvideosink->superview, NO);
267 gst_osx_video_sink_call_from_main_thread(osxvideosink,
268 osxvideosink->osxvideosinkobject,
269 @selector(createInternalWindow), nil, YES);
270 GST_INFO_OBJECT (osxvideosink, "No superview, creating an internal window.");
272 [osxwindow->gstview setNavigation: GST_NAVIGATION(osxvideosink)];
273 [osxvideosink->osxwindow->gstview setKeepAspectRatio: osxvideosink->keep_par];
281 gst_osx_video_sink_osxwindow_destroy (GstOSXVideoSink * osxvideosink)
283 NSAutoreleasePool *pool;
285 g_return_if_fail (GST_IS_OSX_VIDEO_SINK (osxvideosink));
286 pool = [[NSAutoreleasePool alloc] init];
288 GST_OBJECT_LOCK (osxvideosink);
289 gst_osx_video_sink_call_from_main_thread(osxvideosink,
290 osxvideosink->osxvideosinkobject,
291 @selector(destroy), (id) nil, YES);
292 GST_OBJECT_UNLOCK (osxvideosink);
293 gst_osx_video_sink_stop_cocoa_loop (osxvideosink);
297 /* This function resizes a GstXWindow */
299 gst_osx_video_sink_osxwindow_resize (GstOSXVideoSink * osxvideosink,
300 GstOSXWindow * osxwindow, guint width, guint height)
302 GstOSXVideoSinkObject *object = osxvideosink->osxvideosinkobject;
304 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
305 g_return_if_fail (osxwindow != NULL);
306 g_return_if_fail (GST_IS_OSX_VIDEO_SINK (osxvideosink));
308 osxwindow->width = width;
309 osxwindow->height = height;
311 GST_DEBUG_OBJECT (osxvideosink, "Resizing window to (%d,%d)", width, height);
313 /* Directly resize the underlying view */
314 GST_DEBUG_OBJECT (osxvideosink, "Calling setVideoSize on %p", osxwindow->gstview);
315 gst_osx_video_sink_call_from_main_thread (osxvideosink, object,
316 @selector(resize), (id)nil, YES);
322 gst_osx_video_sink_setcaps (GstBaseSink * bsink, GstCaps * caps)
324 GstOSXVideoSink *osxvideosink;
325 GstStructure *structure;
326 gboolean res, result = FALSE;
327 gint video_width, video_height;
329 osxvideosink = GST_OSX_VIDEO_SINK (bsink);
331 GST_DEBUG_OBJECT (osxvideosink, "caps: %" GST_PTR_FORMAT, caps);
333 structure = gst_caps_get_structure (caps, 0);
334 res = gst_structure_get_int (structure, "width", &video_width);
335 res &= gst_structure_get_int (structure, "height", &video_height);
341 GST_DEBUG_OBJECT (osxvideosink, "our format is: %dx%d video",
342 video_width, video_height);
344 GST_VIDEO_SINK_WIDTH (osxvideosink) = video_width;
345 GST_VIDEO_SINK_HEIGHT (osxvideosink) = video_height;
347 gst_osx_video_sink_osxwindow_resize (osxvideosink, osxvideosink->osxwindow,
348 video_width, video_height);
350 gst_video_info_from_caps (&osxvideosink->info, caps);
359 static GstStateChangeReturn
360 gst_osx_video_sink_change_state (GstElement * element,
361 GstStateChange transition)
363 GstOSXVideoSink *osxvideosink;
364 GstStateChangeReturn ret;
366 osxvideosink = GST_OSX_VIDEO_SINK (element);
368 GST_DEBUG_OBJECT (osxvideosink, "%s => %s",
369 gst_element_state_get_name(GST_STATE_TRANSITION_CURRENT (transition)),
370 gst_element_state_get_name(GST_STATE_TRANSITION_NEXT (transition)));
372 switch (transition) {
373 case GST_STATE_CHANGE_NULL_TO_READY:
375 case GST_STATE_CHANGE_READY_TO_PAUSED:
376 /* Creating our window and our image */
377 GST_VIDEO_SINK_WIDTH (osxvideosink) = 320;
378 GST_VIDEO_SINK_HEIGHT (osxvideosink) = 240;
379 if (!gst_osx_video_sink_osxwindow_create (osxvideosink,
380 GST_VIDEO_SINK_WIDTH (osxvideosink),
381 GST_VIDEO_SINK_HEIGHT (osxvideosink))) {
382 ret = GST_STATE_CHANGE_FAILURE;
390 ret = (GST_ELEMENT_CLASS (parent_class))->change_state (element, transition);
392 switch (transition) {
393 case GST_STATE_CHANGE_PAUSED_TO_READY:
394 GST_VIDEO_SINK_WIDTH (osxvideosink) = 0;
395 GST_VIDEO_SINK_HEIGHT (osxvideosink) = 0;
396 gst_osx_video_sink_osxwindow_destroy (osxvideosink);
398 case GST_STATE_CHANGE_READY_TO_NULL:
409 gst_osx_video_sink_show_frame (GstBaseSink * bsink, GstBuffer * buf)
411 GstOSXVideoSink *osxvideosink;
412 GstBufferObject* bufferobject;
413 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
415 osxvideosink = GST_OSX_VIDEO_SINK (bsink);
417 GST_DEBUG ("show_frame");
418 bufferobject = [[GstBufferObject alloc] initWithBuffer:buf];
419 gst_osx_video_sink_call_from_main_thread(osxvideosink,
420 osxvideosink->osxvideosinkobject,
421 @selector(showFrame:), bufferobject, NO);
426 /* Buffer management */
430 /* =========================================== */
432 /* Init & Class init */
434 /* =========================================== */
437 gst_osx_video_sink_set_property (GObject * object, guint prop_id,
438 const GValue * value, GParamSpec * pspec)
440 GstOSXVideoSink *osxvideosink;
442 g_return_if_fail (GST_IS_OSX_VIDEO_SINK (object));
444 osxvideosink = GST_OSX_VIDEO_SINK (object);
448 g_warning ("The \"embed\" property of osxvideosink is deprecated and "
449 "has no effect anymore. Use the GstVideoOverlay "
453 osxvideosink->keep_par = g_value_get_boolean(value);
454 if (osxvideosink->osxwindow)
455 [osxvideosink->osxwindow->gstview
456 setKeepAspectRatio: osxvideosink->keep_par];
459 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
465 gst_osx_video_sink_get_property (GObject * object, guint prop_id,
466 GValue * value, GParamSpec * pspec)
468 GstOSXVideoSink *osxvideosink;
470 g_return_if_fail (GST_IS_OSX_VIDEO_SINK (object));
472 osxvideosink = GST_OSX_VIDEO_SINK (object);
476 g_value_set_boolean (value, FALSE);
479 g_value_set_boolean (value, osxvideosink->keep_par);
482 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
488 gst_osx_video_sink_propose_allocation (GstBaseSink * base_sink, GstQuery * query)
490 gst_query_add_allocation_meta (query,
491 GST_VIDEO_META_API_TYPE, NULL);
497 gst_osx_video_sink_init (GstOSXVideoSink * sink)
499 sink->osxwindow = NULL;
500 sink->superview = NULL;
501 sink->osxvideosinkobject = [[GstOSXVideoSinkObject alloc] initWithSink:sink];
502 sink->keep_par = FALSE;
506 gst_osx_video_sink_base_init (gpointer g_class)
508 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
510 gst_element_class_set_static_metadata (element_class, "OSX Video sink",
511 "Sink/Video", "OSX native videosink",
512 "Zaheer Abbas Merali <zaheerabbas at merali dot org>");
514 gst_element_class_add_pad_template (element_class,
515 gst_static_pad_template_get (&gst_osx_video_sink_sink_template_factory));
519 gst_osx_video_sink_finalize (GObject *object)
521 GstOSXVideoSink *osxvideosink = GST_OSX_VIDEO_SINK (object);
523 if (osxvideosink->superview)
524 [osxvideosink->superview release];
526 if (osxvideosink->osxvideosinkobject)
527 [(GstOSXVideoSinkObject*)(osxvideosink->osxvideosinkobject) release];
529 G_OBJECT_CLASS (parent_class)->finalize (object);
533 gst_osx_video_sink_class_init (GstOSXVideoSinkClass * klass)
535 GObjectClass *gobject_class;
536 GstElementClass *gstelement_class;
537 GstBaseSinkClass *gstbasesink_class;
539 gobject_class = (GObjectClass *) klass;
540 gstelement_class = (GstElementClass *) klass;
541 gstbasesink_class = (GstBaseSinkClass *) klass;
543 parent_class = g_type_class_ref (GST_TYPE_VIDEO_SINK);
546 klass->run_loop_state = GST_OSX_VIDEO_SINK_RUN_LOOP_STATE_UNKNOWN;
547 klass->ns_app_thread = NULL;
549 gobject_class->set_property = gst_osx_video_sink_set_property;
550 gobject_class->get_property = gst_osx_video_sink_get_property;
551 gobject_class->finalize = gst_osx_video_sink_finalize;
553 gstbasesink_class->set_caps = gst_osx_video_sink_setcaps;
554 gstbasesink_class->preroll = gst_osx_video_sink_show_frame;
555 gstbasesink_class->render = gst_osx_video_sink_show_frame;
556 gstbasesink_class->propose_allocation = gst_osx_video_sink_propose_allocation;
557 gstelement_class->change_state = gst_osx_video_sink_change_state;
560 * GstOSXVideoSink:embed
562 * For ABI comatibility onyl, do not use
566 g_object_class_install_property (gobject_class, ARG_EMBED,
567 g_param_spec_boolean ("embed", "embed", "For ABI compatiblity only, do not use",
568 FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
571 * GstOSXVideoSink:force-aspect-ratio
573 * When enabled, scaling will respect original aspect ratio.
577 g_object_class_install_property (gobject_class, ARG_FORCE_PAR,
578 g_param_spec_boolean ("force-aspect-ratio", "force aspect ration",
579 "When enabled, scaling will respect original aspect ration",
580 TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
584 gst_osx_video_sink_navigation_send_event (GstNavigation * navigation,
585 GstStructure * structure)
587 GstOSXVideoSink *osxvideosink = GST_OSX_VIDEO_SINK (navigation);
590 GstVideoRectangle src = { 0, };
591 GstVideoRectangle dst = { 0, };
592 GstVideoRectangle result;
594 gdouble x, y, xscale = 1.0, yscale = 1.0;
596 peer = gst_pad_get_peer (GST_VIDEO_SINK_PAD (osxvideosink));
598 if (!peer || !osxvideosink->osxwindow)
601 event = gst_event_new_navigation (structure);
603 bounds = [osxvideosink->osxwindow->gstview getDrawingBounds];
605 if (osxvideosink->keep_par) {
606 /* We get the frame position using the calculated geometry from _setcaps
607 that respect pixel aspect ratios */
608 src.w = GST_VIDEO_SINK_WIDTH (osxvideosink);
609 src.h = GST_VIDEO_SINK_HEIGHT (osxvideosink);
610 dst.w = bounds.size.width;
611 dst.h = bounds.size.height;
613 gst_video_sink_center_rect (src, dst, &result, TRUE);
614 result.x += bounds.origin.x;
615 result.y += bounds.origin.y;
617 result.x = bounds.origin.x;
618 result.y = bounds.origin.y;
619 result.w = bounds.size.width;
620 result.h = bounds.size.height;
623 /* We calculate scaling using the original video frames geometry to include
624 pixel aspect ratio scaling. */
625 xscale = (gdouble) osxvideosink->osxwindow->width / result.w;
626 yscale = (gdouble) osxvideosink->osxwindow->height / result.h;
628 /* Converting pointer coordinates to the non scaled geometry */
629 if (gst_structure_get_double (structure, "pointer_x", &x)) {
630 x = MIN (x, result.x + result.w);
631 x = MAX (x - result.x, 0);
632 gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE,
633 (gdouble) x * xscale, NULL);
635 if (gst_structure_get_double (structure, "pointer_y", &y)) {
636 y = MIN (y, result.y + result.h);
637 y = MAX (y - result.y, 0);
638 gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE,
639 (gdouble) y * yscale, NULL);
642 gst_pad_send_event (peer, event);
643 gst_object_unref (peer);
647 gst_osx_video_sink_navigation_init (GstNavigationInterface * iface)
649 iface->send_event = gst_osx_video_sink_navigation_send_event;
653 gst_osx_video_sink_set_window_handle (GstVideoOverlay * overlay, guintptr handle_id)
655 GstOSXVideoSink *osxvideosink = GST_OSX_VIDEO_SINK (overlay);
656 NSView *view = (NSView *) handle_id;
658 gst_osx_video_sink_call_from_main_thread(osxvideosink,
659 osxvideosink->osxvideosinkobject,
660 @selector(setView:), view, YES);
664 gst_osx_video_sink_xoverlay_init (GstVideoOverlayInterface * iface)
666 iface->set_window_handle = gst_osx_video_sink_set_window_handle;
667 iface->expose = NULL;
668 iface->handle_events = NULL;
671 /* ============================================================= */
675 /* ============================================================= */
677 /* =========================================== */
679 /* Object typing & Creation */
681 /* =========================================== */
684 gst_osx_video_sink_get_type (void)
686 static GType osxvideosink_type = 0;
688 if (!osxvideosink_type) {
689 static const GTypeInfo osxvideosink_info = {
690 sizeof (GstOSXVideoSinkClass),
691 gst_osx_video_sink_base_init,
693 (GClassInitFunc) gst_osx_video_sink_class_init,
696 sizeof (GstOSXVideoSink),
698 (GInstanceInitFunc) gst_osx_video_sink_init,
701 static const GInterfaceInfo overlay_info = {
702 (GInterfaceInitFunc) gst_osx_video_sink_xoverlay_init,
707 static const GInterfaceInfo navigation_info = {
708 (GInterfaceInitFunc) gst_osx_video_sink_navigation_init,
712 osxvideosink_type = g_type_register_static (GST_TYPE_VIDEO_SINK,
713 "GstOSXVideoSink", &osxvideosink_info, 0);
715 g_type_add_interface_static (osxvideosink_type, GST_TYPE_VIDEO_OVERLAY,
717 g_type_add_interface_static (osxvideosink_type, GST_TYPE_NAVIGATION,
721 return osxvideosink_type;
724 @implementation GstWindowDelegate
725 - (id) initWithSink: (GstOSXVideoSink *) sink
728 self->osxvideosink = sink;
732 - (void)windowWillClose:(NSNotification *)notification {
733 /* Only handle close events if the window was closed manually by the user
734 * and not becuase of a state change state to READY */
735 if (osxvideosink->osxwindow == NULL) {
738 if (!osxvideosink->osxwindow->closed) {
739 osxvideosink->osxwindow->closed = TRUE;
740 GST_ELEMENT_ERROR (osxvideosink, RESOURCE, NOT_FOUND, ("Output window was closed"), (NULL));
741 gst_osx_video_sink_osxwindow_destroy(osxvideosink);
747 @ implementation GstOSXVideoSinkObject
749 -(id) initWithSink: (GstOSXVideoSink*) sink
752 self->osxvideosink = gst_object_ref (sink);
757 gst_object_unref (osxvideosink);
761 -(void) createInternalWindow
763 GstOSXWindow *osxwindow = osxvideosink->osxwindow;
767 osxwindow->internal = TRUE;
769 mask = NSTitledWindowMask |
770 NSClosableWindowMask |
771 NSResizableWindowMask |
772 NSTexturedBackgroundWindowMask |
773 NSMiniaturizableWindowMask;
775 rect.origin.x = 100.0;
776 rect.origin.y = 100.0;
777 rect.size.width = (float) osxwindow->width;
778 rect.size.height = (float) osxwindow->height;
780 osxwindow->win =[[[GstOSXVideoSinkWindow alloc]
781 initWithContentNSRect: rect
783 backing: NSBackingStoreBuffered
785 screen: nil] retain];
786 GST_DEBUG("VideoSinkWindow created, %p", osxwindow->win);
787 [osxwindow->win orderFrontRegardless];
788 osxwindow->gstview =[osxwindow->win gstView];
789 [osxwindow->win setDelegate:[[GstWindowDelegate alloc]
790 initWithSink:osxvideosink]];
794 + (BOOL) isMainThread
796 /* FIXME: ideally we should return YES only for ->ns_app_thread here */
800 - (void) setView: (NSView*)view
802 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
804 if (osxvideosink->superview) {
805 GST_INFO_OBJECT (osxvideosink, "old xwindow id %p", osxvideosink->superview);
806 if (osxvideosink->osxwindow) {
807 [osxvideosink->osxwindow->gstview removeFromSuperview];
809 [osxvideosink->superview release];
811 if (osxvideosink->osxwindow != NULL && view != NULL) {
812 if (osxvideosink->osxwindow->internal) {
813 GST_INFO_OBJECT (osxvideosink, "closing internal window");
814 osxvideosink->osxwindow->closed = TRUE;
815 [osxvideosink->osxwindow->win close];
816 [osxvideosink->osxwindow->win release];
820 GST_INFO_OBJECT (osxvideosink, "set xwindow id %p", view);
821 osxvideosink->superview = [view retain];
822 if (osxvideosink->osxwindow) {
823 [osxvideosink->osxwindow->gstview addToSuperview: osxvideosink->superview];
825 osxvideosink->osxwindow->internal = FALSE;
834 GstOSXWindow *osxwindow = osxvideosink->osxwindow;
836 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
838 GST_INFO_OBJECT (osxvideosink, "resizing");
839 NSSize size = {osxwindow->width, osxwindow->height};
840 if (osxwindow->internal) {
841 [osxwindow->win setContentSize:size];
843 if (osxwindow->gstview) {
844 [osxwindow->gstview setVideoSize :(int)osxwindow->width :(int)osxwindow->height];
846 GST_INFO_OBJECT (osxvideosink, "done");
851 - (void) showFrame: (GstBufferObject *) object
854 guint8 *data, *readp, *writep;
855 gint i, active_width, stride;
856 guint8 *texture_buffer;
857 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
858 GstBuffer *buf = object->buf;
860 GST_OBJECT_LOCK (osxvideosink);
861 if (osxvideosink->osxwindow == NULL)
864 texture_buffer = (guint8 *) [osxvideosink->osxwindow->gstview getTextureBuffer];
865 if (G_UNLIKELY (texture_buffer == NULL))
866 goto no_texture_buffer;
868 if (!gst_video_frame_map (&frame, &osxvideosink->info, buf, GST_MAP_READ))
871 data = readp = GST_VIDEO_FRAME_PLANE_DATA (&frame, 0);
872 stride = GST_VIDEO_FRAME_PLANE_STRIDE (&frame, 0);
873 writep = texture_buffer;
874 active_width = GST_VIDEO_SINK_WIDTH (osxvideosink) * sizeof (short);
875 for (i = 0; i < GST_VIDEO_SINK_HEIGHT (osxvideosink); i++) {
876 memcpy (writep, readp, active_width);
877 writep += active_width;
880 [osxvideosink->osxwindow->gstview displayTexture];
882 gst_video_frame_unmap (&frame);
885 GST_OBJECT_UNLOCK (osxvideosink);
892 GST_WARNING_OBJECT (osxvideosink, "couldn't map frame");
896 GST_WARNING_OBJECT (osxvideosink, "not showing frame since we have no window (!?)");
900 GST_ELEMENT_ERROR (osxvideosink, RESOURCE, WRITE, (NULL),
901 ("the texture buffer is NULL"));
907 NSAutoreleasePool *pool;
908 GstOSXWindow *osxwindow;
910 pool = [[NSAutoreleasePool alloc] init];
912 osxwindow = osxvideosink->osxwindow;
913 osxvideosink->osxwindow = NULL;
916 if (osxvideosink->superview) {
917 [osxwindow->gstview removeFromSuperview];
919 [osxwindow->gstview release];
920 if (osxwindow->internal) {
921 if (!osxwindow->closed) {
922 osxwindow->closed = TRUE;
923 [osxwindow->win close];
924 [osxwindow->win release];
934 NSAutoreleasePool *pool;
936 /* set the main runloop as the runloop for the current thread. This has the
937 * effect that calling NSApp nextEventMatchingMask:untilDate:inMode:dequeue
938 * runs the main runloop.
940 _CFRunLoopSetCurrent(CFRunLoopGetMain());
942 /* this is needed to make IsMainThread checks in core foundation work from the
945 _CFMainPThread = pthread_self();
947 pool = [[NSAutoreleasePool alloc] init];
949 [NSApplication sharedApplication];
950 [NSApp finishLaunching];
952 g_mutex_lock (&_run_loop_mutex);
953 g_cond_signal (&_run_loop_cond);
954 g_mutex_unlock (&_run_loop_mutex);
962 -(void) checkMainRunLoop
964 g_mutex_lock (&_run_loop_mutex);
965 g_cond_signal (&_run_loop_cond);
966 g_mutex_unlock (&_run_loop_mutex);
971 @ implementation GstBufferObject
972 -(id) initWithBuffer: (GstBuffer*) buffer
975 gst_buffer_ref(buffer);
981 gst_buffer_unref(buf);
987 plugin_init (GstPlugin * plugin)
990 if (!gst_element_register (plugin, "osxvideosink",
991 GST_RANK_MARGINAL, GST_TYPE_OSX_VIDEO_SINK))
994 GST_DEBUG_CATEGORY_INIT (gst_debug_osx_video_sink, "osxvideosink", 0,
995 "osxvideosink element");
1000 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
1003 "OSX native video output plugin",
1004 plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)