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>
42 #include <gst/video/video.h>
44 #include "osxvideosink.h"
46 #import "cocoawindow.h"
48 GST_DEBUG_CATEGORY (gst_debug_osx_video_sink);
49 #define GST_CAT_DEFAULT gst_debug_osx_video_sink
52 extern void _CFRunLoopSetCurrent (CFRunLoopRef rl);
53 extern pthread_t _CFMainPThread;
57 static GstStaticPadTemplate gst_osx_video_sink_sink_template_factory =
58 GST_STATIC_PAD_TEMPLATE ("sink",
61 GST_STATIC_CAPS ("video/x-raw, "
62 "framerate = (fraction) [ 0, MAX ], "
63 "width = (int) [ 1, MAX ], "
64 "height = (int) [ 1, MAX ], "
65 #if G_BYTE_ORDER == G_BIG_ENDIAN
66 "format = (string) YUY2")
68 "format = (string) UYVY")
79 static void gst_osx_video_sink_osxwindow_destroy (GstOSXVideoSink * osxvideosink);
80 static GMutex _run_loop_check_mutex;
81 static GMutex _run_loop_mutex;
82 static GCond _run_loop_cond;
83 static GstOSXVideoSinkClass *sink_class = NULL;
84 static GstVideoSinkClass *parent_class = NULL;
86 /* Helper to trigger calls from the main thread */
88 gst_osx_video_sink_call_from_main_thread(GstOSXVideoSink *osxvideosink,
89 NSObject * object, SEL function, NSObject *data, BOOL waitUntilDone)
93 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
95 if (sink_class->ns_app_thread == NULL){
96 thread = [NSThread mainThread];
98 thread = sink_class->ns_app_thread;
101 [object performSelector:function onThread:thread
102 withObject:data waitUntilDone:waitUntilDone];
106 /* Poll for cocoa events */
108 run_ns_app_loop (void) {
110 NSAutoreleasePool *pool =[[NSAutoreleasePool alloc] init];
111 NSDate *pollTime = nil;
113 /* when running the loop in a thread we want to sleep as long as possible */
114 pollTime = [NSDate distantFuture];
117 event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:pollTime
118 inMode:NSDefaultRunLoopMode dequeue:YES];
119 [NSApp sendEvent:event];
121 while (event != nil);
126 gst_osx_videosink_check_main_run_loop (GstOSXVideoSink *sink)
128 /* check if the main run loop is running */
132 is_running = [[NSRunLoop mainRunLoop] currentMode] != nil;
136 /* the previous check doesn't always work with main loops that run
137 * cocoa's main run loop manually, like the gdk one, giving false
138 * negatives. This check defers a call to the main thread and waits to
139 * be awaken by this function. */
140 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
141 GstOSXVideoSinkObject * object = (GstOSXVideoSinkObject *) sink->osxvideosinkobject;
144 g_mutex_lock (&_run_loop_mutex);
145 [object performSelectorOnMainThread:
146 @selector(checkMainRunLoop)
147 withObject:nil waitUntilDone:NO];
149 abstime = g_get_monotonic_time () + 100 * 1000;
150 is_running = g_cond_wait_until (&_run_loop_cond,
151 &_run_loop_mutex, abstime);
152 g_mutex_unlock (&_run_loop_mutex);
159 GST_DEBUG_OBJECT(sink, "The main runloop %s is running",
160 is_running ? "" : " not ");
162 sink_class->run_loop_state = GST_OSX_VIDEO_SINK_RUN_LOOP_STATE_RUNNING;
163 sink_class->ns_app_thread = [NSThread mainThread];
165 sink_class->run_loop_state = GST_OSX_VIDEO_SINK_RUN_LOOP_STATE_NOT_RUNNING;
171 gst_osx_video_sink_run_cocoa_loop (GstOSXVideoSink * sink )
173 /* Cocoa applications require a main runloop running to dispatch UI
174 * events and process deferred calls to the main thread through
175 * perfermSelectorOnMainThread.
176 * Since the sink needs to create it's own Cocoa window when no
177 * external NSView is passed to the sink through the GstVideoOverlay API,
178 * we need to run the cocoa mainloop somehow.
179 * This run loop can only be started once, by the first sink needing it
182 g_mutex_lock (&_run_loop_check_mutex);
184 if (sink_class->run_loop_state == GST_OSX_VIDEO_SINK_RUN_LOOP_STATE_UNKNOWN) {
185 gst_osx_videosink_check_main_run_loop (sink);
188 if (sink_class->run_loop_state == GST_OSX_VIDEO_SINK_RUN_LOOP_STATE_RUNNING) {
189 g_mutex_unlock (&_run_loop_check_mutex);
193 if (sink_class->ns_app_thread == NULL) {
194 /* run the main runloop in a separate thread */
196 /* override [NSThread isMainThread] with our own implementation so that we can
197 * make it believe our dedicated thread is the main thread
199 Method origIsMainThread = class_getClassMethod([NSThread class],
200 NSSelectorFromString(@"isMainThread"));
201 Method ourIsMainThread = class_getClassMethod([GstOSXVideoSinkObject class],
202 NSSelectorFromString(@"isMainThread"));
204 method_exchangeImplementations(origIsMainThread, ourIsMainThread);
206 sink_class->ns_app_thread = [[NSThread alloc]
207 initWithTarget:sink->osxvideosinkobject
208 selector:@selector(nsAppThread) object:nil];
209 [sink_class->ns_app_thread start];
211 g_mutex_lock (&_run_loop_mutex);
212 g_cond_wait (&_run_loop_cond, &_run_loop_mutex);
213 g_mutex_unlock (&_run_loop_mutex);
216 g_mutex_unlock (&_run_loop_check_mutex);
220 gst_osx_video_sink_stop_cocoa_loop (GstOSXVideoSink * osxvideosink)
224 /* This function handles osx window creation */
226 gst_osx_video_sink_osxwindow_create (GstOSXVideoSink * osxvideosink, gint width,
230 GstOSXWindow *osxwindow = NULL;
234 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
236 g_return_val_if_fail (GST_IS_OSX_VIDEO_SINK (osxvideosink), FALSE);
238 GST_DEBUG_OBJECT (osxvideosink, "Creating new OSX window");
240 osxvideosink->osxwindow = osxwindow = g_new0 (GstOSXWindow, 1);
242 osxwindow->width = width;
243 osxwindow->height = height;
244 osxwindow->closed = FALSE;
245 osxwindow->internal = FALSE;
247 /* Allocate our GstGLView for the window, and then tell the application
248 * about it (hopefully it's listening...) */
251 rect.size.width = (float) osxwindow->width;
252 rect.size.height = (float) osxwindow->height;
253 osxwindow->gstview =[[GstGLView alloc] initWithFrame:rect];
255 s = gst_structure_new ("have-ns-view",
256 "nsview", G_TYPE_POINTER, osxwindow->gstview,
259 msg = gst_message_new_element (GST_OBJECT (osxvideosink), s);
260 gst_element_post_message (GST_ELEMENT (osxvideosink), msg);
262 GST_INFO_OBJECT (osxvideosink, "'have-ns-view' message sent");
264 gst_osx_video_sink_run_cocoa_loop (osxvideosink);
265 [osxwindow->gstview setMainThread:sink_class->ns_app_thread];
267 /* check if have-ns-view was handled and osxwindow->gstview was added to a
270 if ([osxwindow->gstview haveSuperview] == NO) {
271 /* have-ns-view wasn't handled, post prepare-xwindow-id */
272 if (osxvideosink->superview == NULL) {
273 GST_INFO_OBJECT (osxvideosink, "emitting prepare-xwindow-id");
274 gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (osxvideosink));
277 if (osxvideosink->superview != NULL) {
278 /* prepare-xwindow-id was handled, we have the superview in
279 * osxvideosink->superview. We now add osxwindow->gstview to the superview
280 * from the main thread
282 GST_INFO_OBJECT (osxvideosink, "we have a superview, adding our view to it");
283 gst_osx_video_sink_call_from_main_thread(osxvideosink, osxwindow->gstview,
284 @selector(addToSuperview:), osxvideosink->superview, NO);
287 if (osxvideosink->embed) {
288 /* the view wasn't added to a superview. It's possible that the
289 * application handled have-ns-view, stored our view internally and is
290 * going to add it to a superview later (webkit does that now).
292 GST_INFO_OBJECT (osxvideosink, "no superview");
294 gst_osx_video_sink_call_from_main_thread(osxvideosink,
295 osxvideosink->osxvideosinkobject,
296 @selector(createInternalWindow), nil, YES);
297 GST_INFO_OBJECT (osxvideosink, "No superview, creating an internal window.");
301 [osxwindow->gstview setNavigation: GST_NAVIGATION(osxvideosink)];
302 [osxvideosink->osxwindow->gstview setKeepAspectRatio: osxvideosink->keep_par];
310 gst_osx_video_sink_osxwindow_destroy (GstOSXVideoSink * osxvideosink)
312 NSAutoreleasePool *pool;
314 g_return_if_fail (GST_IS_OSX_VIDEO_SINK (osxvideosink));
315 pool = [[NSAutoreleasePool alloc] init];
317 GST_OBJECT_LOCK (osxvideosink);
318 gst_osx_video_sink_call_from_main_thread(osxvideosink,
319 osxvideosink->osxvideosinkobject,
320 @selector(destroy), (id) nil, YES);
321 GST_OBJECT_UNLOCK (osxvideosink);
322 gst_osx_video_sink_stop_cocoa_loop (osxvideosink);
326 /* This function resizes a GstXWindow */
328 gst_osx_video_sink_osxwindow_resize (GstOSXVideoSink * osxvideosink,
329 GstOSXWindow * osxwindow, guint width, guint height)
331 GstOSXVideoSinkObject *object = osxvideosink->osxvideosinkobject;
333 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
334 g_return_if_fail (osxwindow != NULL);
335 g_return_if_fail (GST_IS_OSX_VIDEO_SINK (osxvideosink));
337 osxwindow->width = width;
338 osxwindow->height = height;
340 GST_DEBUG_OBJECT (osxvideosink, "Resizing window to (%d,%d)", width, height);
342 /* Directly resize the underlying view */
343 GST_DEBUG_OBJECT (osxvideosink, "Calling setVideoSize on %p", osxwindow->gstview);
344 gst_osx_video_sink_call_from_main_thread (osxvideosink, object,
345 @selector(resize), (id)nil, YES);
351 gst_osx_video_sink_setcaps (GstBaseSink * bsink, GstCaps * caps)
353 GstOSXVideoSink *osxvideosink;
354 GstStructure *structure;
355 gboolean res, result = FALSE;
356 gint video_width, video_height;
358 osxvideosink = GST_OSX_VIDEO_SINK (bsink);
360 GST_DEBUG_OBJECT (osxvideosink, "caps: %" GST_PTR_FORMAT, caps);
362 structure = gst_caps_get_structure (caps, 0);
363 res = gst_structure_get_int (structure, "width", &video_width);
364 res &= gst_structure_get_int (structure, "height", &video_height);
370 GST_DEBUG_OBJECT (osxvideosink, "our format is: %dx%d video",
371 video_width, video_height);
373 GST_VIDEO_SINK_WIDTH (osxvideosink) = video_width;
374 GST_VIDEO_SINK_HEIGHT (osxvideosink) = video_height;
376 gst_osx_video_sink_osxwindow_resize (osxvideosink, osxvideosink->osxwindow,
377 video_width, video_height);
385 static GstStateChangeReturn
386 gst_osx_video_sink_change_state (GstElement * element,
387 GstStateChange transition)
389 GstOSXVideoSink *osxvideosink;
390 GstStateChangeReturn ret;
392 osxvideosink = GST_OSX_VIDEO_SINK (element);
394 GST_DEBUG_OBJECT (osxvideosink, "%s => %s",
395 gst_element_state_get_name(GST_STATE_TRANSITION_CURRENT (transition)),
396 gst_element_state_get_name(GST_STATE_TRANSITION_NEXT (transition)));
398 switch (transition) {
399 case GST_STATE_CHANGE_NULL_TO_READY:
401 case GST_STATE_CHANGE_READY_TO_PAUSED:
402 /* Creating our window and our image */
403 GST_VIDEO_SINK_WIDTH (osxvideosink) = 320;
404 GST_VIDEO_SINK_HEIGHT (osxvideosink) = 240;
405 if (!gst_osx_video_sink_osxwindow_create (osxvideosink,
406 GST_VIDEO_SINK_WIDTH (osxvideosink),
407 GST_VIDEO_SINK_HEIGHT (osxvideosink))) {
408 ret = GST_STATE_CHANGE_FAILURE;
416 ret = (GST_ELEMENT_CLASS (parent_class))->change_state (element, transition);
418 switch (transition) {
419 case GST_STATE_CHANGE_PAUSED_TO_READY:
420 GST_VIDEO_SINK_WIDTH (osxvideosink) = 0;
421 GST_VIDEO_SINK_HEIGHT (osxvideosink) = 0;
422 gst_osx_video_sink_osxwindow_destroy (osxvideosink);
424 case GST_STATE_CHANGE_READY_TO_NULL:
435 gst_osx_video_sink_show_frame (GstBaseSink * bsink, GstBuffer * buf)
437 GstOSXVideoSink *osxvideosink;
438 GstBufferObject* bufferobject;
439 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
441 osxvideosink = GST_OSX_VIDEO_SINK (bsink);
443 GST_DEBUG ("show_frame");
444 bufferobject = [[GstBufferObject alloc] initWithBuffer:buf];
445 gst_osx_video_sink_call_from_main_thread(osxvideosink,
446 osxvideosink->osxvideosinkobject,
447 @selector(showFrame:), bufferobject, NO);
452 /* Buffer management */
456 /* =========================================== */
458 /* Init & Class init */
460 /* =========================================== */
463 gst_osx_video_sink_set_property (GObject * object, guint prop_id,
464 const GValue * value, GParamSpec * pspec)
466 GstOSXVideoSink *osxvideosink;
468 g_return_if_fail (GST_IS_OSX_VIDEO_SINK (object));
470 osxvideosink = GST_OSX_VIDEO_SINK (object);
474 osxvideosink->embed = g_value_get_boolean(value);
475 g_warning ("The \"embed\" property of osxvideosink is deprecated and "
476 "will be removed in the near future. Use the GstVideoOverlay "
480 osxvideosink->keep_par = g_value_get_boolean(value);
481 if (osxvideosink->osxwindow)
482 [osxvideosink->osxwindow->gstview
483 setKeepAspectRatio: osxvideosink->keep_par];
486 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
492 gst_osx_video_sink_get_property (GObject * object, guint prop_id,
493 GValue * value, GParamSpec * pspec)
495 GstOSXVideoSink *osxvideosink;
497 g_return_if_fail (GST_IS_OSX_VIDEO_SINK (object));
499 osxvideosink = GST_OSX_VIDEO_SINK (object);
503 g_value_set_boolean (value, osxvideosink->embed);
506 g_value_set_boolean (value, osxvideosink->keep_par);
509 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
516 gst_osx_video_sink_init (GstOSXVideoSink * sink)
518 sink->osxwindow = NULL;
519 sink->superview = NULL;
520 sink->osxvideosinkobject = [[GstOSXVideoSinkObject alloc] initWithSink:sink];
521 sink->keep_par = FALSE;
525 gst_osx_video_sink_base_init (gpointer g_class)
527 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
529 gst_element_class_set_static_metadata (element_class, "OSX Video sink",
530 "Sink/Video", "OSX native videosink",
531 "Zaheer Abbas Merali <zaheerabbas at merali dot org>");
533 gst_element_class_add_pad_template (element_class,
534 gst_static_pad_template_get (&gst_osx_video_sink_sink_template_factory));
538 gst_osx_video_sink_finalize (GObject *object)
540 GstOSXVideoSink *osxvideosink = GST_OSX_VIDEO_SINK (object);
542 if (osxvideosink->superview)
543 [osxvideosink->superview release];
545 if (osxvideosink->osxvideosinkobject)
546 [(GstOSXVideoSinkObject*)(osxvideosink->osxvideosinkobject) release];
548 G_OBJECT_CLASS (parent_class)->finalize (object);
552 gst_osx_video_sink_class_init (GstOSXVideoSinkClass * klass)
554 GObjectClass *gobject_class;
555 GstElementClass *gstelement_class;
556 GstBaseSinkClass *gstbasesink_class;
558 gobject_class = (GObjectClass *) klass;
559 gstelement_class = (GstElementClass *) klass;
560 gstbasesink_class = (GstBaseSinkClass *) klass;
562 parent_class = g_type_class_ref (GST_TYPE_VIDEO_SINK);
565 klass->run_loop_state = GST_OSX_VIDEO_SINK_RUN_LOOP_STATE_UNKNOWN;
566 klass->ns_app_thread = NULL;
568 gobject_class->set_property = gst_osx_video_sink_set_property;
569 gobject_class->get_property = gst_osx_video_sink_get_property;
570 gobject_class->finalize = gst_osx_video_sink_finalize;
572 gstbasesink_class->set_caps = gst_osx_video_sink_setcaps;
573 gstbasesink_class->preroll = gst_osx_video_sink_show_frame;
574 gstbasesink_class->render = gst_osx_video_sink_show_frame;
575 gstelement_class->change_state = gst_osx_video_sink_change_state;
578 * GstOSXVideoSink:embed
580 * Set to #TRUE if you are embedding the video window in an application.
584 g_object_class_install_property (gobject_class, ARG_EMBED,
585 g_param_spec_boolean ("embed", "embed", "For ABI compatiblity only, do not use",
586 FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
589 * GstOSXVideoSink:force-aspect-ratio
591 * When enabled, scaling will respect original aspect ratio.
595 g_object_class_install_property (gobject_class, ARG_FORCE_PAR,
596 g_param_spec_boolean ("force-aspect-ratio", "force aspect ration",
597 "When enabled, scaling will respect original aspect ration",
598 TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
602 gst_osx_video_sink_navigation_send_event (GstNavigation * navigation,
603 GstStructure * structure)
605 GstOSXVideoSink *osxvideosink = GST_OSX_VIDEO_SINK (navigation);
608 GstVideoRectangle src, dst, result;
610 gdouble x, y, xscale = 1.0, yscale = 1.0;
612 peer = gst_pad_get_peer (GST_VIDEO_SINK_PAD (osxvideosink));
614 if (!peer || !osxvideosink->osxwindow)
617 event = gst_event_new_navigation (structure);
619 bounds = [osxvideosink->osxwindow->gstview getDrawingBounds];
621 if (osxvideosink->keep_par) {
622 /* We get the frame position using the calculated geometry from _setcaps
623 that respect pixel aspect ratios */
624 src.w = GST_VIDEO_SINK_WIDTH (osxvideosink);
625 src.h = GST_VIDEO_SINK_HEIGHT (osxvideosink);
626 dst.w = bounds.size.width;
627 dst.h = bounds.size.height;
629 gst_video_sink_center_rect (src, dst, &result, TRUE);
630 result.x += bounds.origin.x;
631 result.y += bounds.origin.y;
633 result.x = bounds.origin.x;
634 result.y = bounds.origin.y;
635 result.w = bounds.size.width;
636 result.h = bounds.size.height;
639 /* We calculate scaling using the original video frames geometry to include
640 pixel aspect ratio scaling. */
641 xscale = (gdouble) osxvideosink->osxwindow->width / result.w;
642 yscale = (gdouble) osxvideosink->osxwindow->height / result.h;
644 /* Converting pointer coordinates to the non scaled geometry */
645 if (gst_structure_get_double (structure, "pointer_x", &x)) {
646 x = MIN (x, result.x + result.w);
647 x = MAX (x - result.x, 0);
648 gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE,
649 (gdouble) x * xscale, NULL);
651 if (gst_structure_get_double (structure, "pointer_y", &y)) {
652 y = MIN (y, result.y + result.h);
653 y = MAX (y - result.y, 0);
654 gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE,
655 (gdouble) y * yscale, NULL);
658 gst_pad_send_event (peer, event);
659 gst_object_unref (peer);
663 gst_osx_video_sink_navigation_init (GstNavigationInterface * iface)
665 iface->send_event = gst_osx_video_sink_navigation_send_event;
669 gst_osx_video_sink_set_window_handle (GstVideoOverlay * overlay, guintptr handle_id)
671 GstOSXVideoSink *osxvideosink = GST_OSX_VIDEO_SINK (overlay);
672 NSView *view = (NSView *) handle_id;
674 gst_osx_video_sink_call_from_main_thread(osxvideosink,
675 osxvideosink->osxvideosinkobject,
676 @selector(setView:), view, YES);
680 gst_osx_video_sink_xoverlay_init (GstVideoOverlayInterface * iface)
682 iface->set_window_handle = gst_osx_video_sink_set_window_handle;
683 iface->expose = NULL;
684 iface->handle_events = NULL;
687 /* ============================================================= */
691 /* ============================================================= */
693 /* =========================================== */
695 /* Object typing & Creation */
697 /* =========================================== */
700 gst_osx_video_sink_get_type (void)
702 static GType osxvideosink_type = 0;
704 if (!osxvideosink_type) {
705 static const GTypeInfo osxvideosink_info = {
706 sizeof (GstOSXVideoSinkClass),
707 gst_osx_video_sink_base_init,
709 (GClassInitFunc) gst_osx_video_sink_class_init,
712 sizeof (GstOSXVideoSink),
714 (GInstanceInitFunc) gst_osx_video_sink_init,
717 static const GInterfaceInfo overlay_info = {
718 (GInterfaceInitFunc) gst_osx_video_sink_xoverlay_init,
723 static const GInterfaceInfo navigation_info = {
724 (GInterfaceInitFunc) gst_osx_video_sink_navigation_init,
728 osxvideosink_type = g_type_register_static (GST_TYPE_VIDEO_SINK,
729 "GstOSXVideoSink", &osxvideosink_info, 0);
731 g_type_add_interface_static (osxvideosink_type, GST_TYPE_VIDEO_OVERLAY,
733 g_type_add_interface_static (osxvideosink_type, GST_TYPE_NAVIGATION,
737 return osxvideosink_type;
740 @implementation GstWindowDelegate
741 - (id) initWithSink: (GstOSXVideoSink *) sink
744 self->osxvideosink = sink;
748 - (void)windowWillClose:(NSNotification *)notification {
749 /* Only handle close events if the window was closed manually by the user
750 * and not becuase of a state change state to READY */
751 if (osxvideosink->osxwindow == NULL) {
754 if (!osxvideosink->osxwindow->closed) {
755 osxvideosink->osxwindow->closed = TRUE;
756 GST_ELEMENT_ERROR (osxvideosink, RESOURCE, NOT_FOUND, ("Output window was closed"), (NULL));
757 gst_osx_video_sink_osxwindow_destroy(osxvideosink);
763 @ implementation GstOSXVideoSinkObject
765 -(id) initWithSink: (GstOSXVideoSink*) sink
768 self->osxvideosink = gst_object_ref (sink);
773 gst_object_unref (osxvideosink);
777 -(void) createInternalWindow
779 GstOSXWindow *osxwindow = osxvideosink->osxwindow;
783 osxwindow->internal = TRUE;
785 mask = NSTitledWindowMask |
786 NSClosableWindowMask |
787 NSResizableWindowMask |
788 NSTexturedBackgroundWindowMask |
789 NSMiniaturizableWindowMask;
791 rect.origin.x = 100.0;
792 rect.origin.y = 100.0;
793 rect.size.width = (float) osxwindow->width;
794 rect.size.height = (float) osxwindow->height;
796 osxwindow->win =[[[GstOSXVideoSinkWindow alloc]
797 initWithContentNSRect: rect
799 backing: NSBackingStoreBuffered
801 screen: nil] retain];
802 GST_DEBUG("VideoSinkWindow created, %p", osxwindow->win);
803 [osxwindow->win orderFrontRegardless];
804 osxwindow->gstview =[osxwindow->win gstView];
805 [osxwindow->win setDelegate:[[GstWindowDelegate alloc]
806 initWithSink:osxvideosink]];
810 + (BOOL) isMainThread
812 /* FIXME: ideally we should return YES only for ->ns_app_thread here */
816 - (void) setView: (NSView*)view
818 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
820 if (osxvideosink->superview) {
821 GST_INFO_OBJECT (osxvideosink, "old xwindow id %p", osxvideosink->superview);
822 if (osxvideosink->osxwindow) {
823 [osxvideosink->osxwindow->gstview removeFromSuperview];
825 [osxvideosink->superview release];
827 if (osxvideosink->osxwindow != NULL && view != NULL) {
828 if (osxvideosink->osxwindow->internal) {
829 GST_INFO_OBJECT (osxvideosink, "closing internal window");
830 osxvideosink->osxwindow->closed = TRUE;
831 [osxvideosink->osxwindow->win close];
832 [osxvideosink->osxwindow->win release];
836 GST_INFO_OBJECT (osxvideosink, "set xwindow id %p", view);
837 osxvideosink->superview = [view retain];
838 if (osxvideosink->osxwindow) {
839 [osxvideosink->osxwindow->gstview addToSuperview: osxvideosink->superview];
841 osxvideosink->osxwindow->internal = FALSE;
850 GstOSXWindow *osxwindow = osxvideosink->osxwindow;
852 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
854 GST_INFO_OBJECT (osxvideosink, "resizing");
855 NSSize size = {osxwindow->width, osxwindow->height};
856 if (osxwindow->internal) {
857 [osxwindow->win setContentSize:size];
859 if (osxwindow->gstview) {
860 [osxwindow->gstview setVideoSize :(int)osxwindow->width :(int)osxwindow->height];
862 GST_INFO_OBJECT (osxvideosink, "done");
867 - (void) showFrame: (GstBufferObject *) object
871 guint8 *data, *readp, *writep;
872 gint i, active_width, stride;
873 guint8 *texture_buffer;
874 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
875 GstBuffer *buf = object->buf;
877 GST_OBJECT_LOCK (osxvideosink);
878 if (osxvideosink->osxwindow == NULL)
881 texture_buffer = (guint8 *) [osxvideosink->osxwindow->gstview getTextureBuffer];
882 if (G_UNLIKELY (texture_buffer == NULL))
883 goto no_texture_buffer;
885 vmeta = (GstVideoMeta *) gst_buffer_get_meta (buf, GST_VIDEO_META_API_TYPE);
886 gst_video_meta_map (vmeta, 0, &info, (gpointer *) &data, &stride, GST_MAP_READ);
888 writep = texture_buffer;
889 active_width = GST_VIDEO_SINK_WIDTH (osxvideosink) * sizeof (short);
890 for (i = 0; i < GST_VIDEO_SINK_HEIGHT (osxvideosink); i++) {
891 memcpy (writep, readp, active_width);
892 writep += active_width;
895 [osxvideosink->osxwindow->gstview displayTexture];
897 gst_video_meta_unmap (vmeta, 0, &info);
900 GST_OBJECT_UNLOCK (osxvideosink);
907 GST_WARNING_OBJECT (osxvideosink, "not showing frame since we have no window (!?)");
911 GST_ELEMENT_ERROR (osxvideosink, RESOURCE, WRITE, (NULL),
912 ("the texture buffer is NULL"));
918 NSAutoreleasePool *pool;
919 GstOSXWindow *osxwindow;
921 pool = [[NSAutoreleasePool alloc] init];
923 osxwindow = osxvideosink->osxwindow;
924 osxvideosink->osxwindow = NULL;
927 if (osxvideosink->superview) {
928 [osxwindow->gstview removeFromSuperview];
930 [osxwindow->gstview release];
931 if (osxwindow->internal) {
932 if (!osxwindow->closed) {
933 osxwindow->closed = TRUE;
934 [osxwindow->win close];
935 [osxwindow->win release];
945 NSAutoreleasePool *pool;
947 /* set the main runloop as the runloop for the current thread. This has the
948 * effect that calling NSApp nextEventMatchingMask:untilDate:inMode:dequeue
949 * runs the main runloop.
951 _CFRunLoopSetCurrent(CFRunLoopGetMain());
953 /* this is needed to make IsMainThread checks in core foundation work from the
956 _CFMainPThread = pthread_self();
958 pool = [[NSAutoreleasePool alloc] init];
960 [NSApplication sharedApplication];
961 [NSApp finishLaunching];
963 g_mutex_lock (&_run_loop_mutex);
964 g_cond_signal (&_run_loop_cond);
965 g_mutex_unlock (&_run_loop_mutex);
973 -(void) checkMainRunLoop
975 g_mutex_lock (&_run_loop_mutex);
976 g_cond_signal (&_run_loop_cond);
977 g_mutex_unlock (&_run_loop_mutex);
982 @ implementation GstBufferObject
983 -(id) initWithBuffer: (GstBuffer*) buffer
986 gst_buffer_ref(buffer);
992 gst_buffer_unref(buf);
998 plugin_init (GstPlugin * plugin)
1001 if (!gst_element_register (plugin, "osxvideosink",
1002 GST_RANK_PRIMARY, GST_TYPE_OSX_VIDEO_SINK))
1005 GST_DEBUG_CATEGORY_INIT (gst_debug_osx_video_sink, "osxvideosink", 0,
1006 "osxvideosink element");
1011 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
1014 "OSX native video output plugin",
1015 plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)