sbGStreamerPlatformOSX.mm
Go to the documentation of this file.
1 /*
2 //
3 // BEGIN SONGBIRD GPL
4 //
5 // This file is part of the Songbird web player.
6 //
7 // Copyright(c) 2005-2008 POTI, Inc.
8 // http://songbirdnest.com
9 //
10 // This file may be licensed under the terms of of the
11 // GNU General Public License Version 2 (the "GPL").
12 //
13 // Software distributed under the License is distributed
14 // on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either
15 // express or implied. See the GPL for the specific language
16 // governing rights and limitations.
17 //
18 // You should have received a copy of the GPL along with this
19 // program. If not, go to http://www.gnu.org/licenses/gpl.html
20 // or write to the Free Software Foundation, Inc.,
21 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 //
23 // END SONGBIRD GPL
24 //
25 */
26 
27 #include "sbGStreamerPlatformOSX.h"
28 #include "sbGStreamerMediacore.h"
29 
30 #include <prlog.h>
31 #include <nsDebug.h>
32 
33 #include <nsCOMPtr.h>
34 #include <nsIRunnable.h>
35 #include <nsThreadUtils.h>
36 
37 #import <AppKit/AppKit.h>
38 
45 #ifdef PR_LOGGING
46 
47 static PRLogModuleInfo* gGStreamerPlatformOSX =
48  PR_NewLogModule("sbGStreamerPlatformOSX");
49 
50 #define LOG(args) \
51  if (gGStreamerPlatformOSX) \
52  PR_LOG(gGStreamerPlatformOSX, PR_LOG_WARNING, args)
53 
54 #define TRACE(args) \
55  if (gGStreamerPlatformOSX) \
56  PR_LOG(gGStreamerPlatformOSX, PR_LOG_DEBUG, args)
57 
58 #else /* PR_LOGGING */
59 
60 #define LOG(args) /* nothing */
61 #define TRACE(args) /* nothing */
62 
63 #endif /* PR_LOGGING */
64 
65 
70 
71 - (void)setFrameAsString:(NSString *)aString;
72 
73 @end
74 
75 @implementation NSView (GStreamerPlatformOSX)
76 
77 - (void)setFrameAsString:(NSString *)aString
78 {
79  NSRect newFrame = NSRectFromString(aString);
80  [self setFrame:newFrame];
81 }
82 
83 @end
84 
85 
87 
88 - (void)setDelegate:(id)aDelegate;
89 
90 @end
91 
92 
96 @interface SBGstGLViewDelgate : NSObject
97 {
98  OSXPlatformInterface *mOwner; // weak, it owns us.
99 }
100 
101 - (id)initWithPlatformInterface:(OSXPlatformInterface *)aOwner;
102 - (void)startListeningToResizeEvents;
103 - (void)stopListeningToResizeEvents;
104 - (void)mouseMoved:(NSEvent *)theEvent;
105 - (void)windowResized:(NSNotification *)aNotice;
106 
107 @end
108 
109 @implementation SBGstGLViewDelgate
110 
111 - (id)initWithPlatformInterface:(OSXPlatformInterface *)aOwner
112 {
113  if ((self = [super init])) {
114  mOwner = aOwner;
115  }
116 
117  return self;
118 }
119 
120 - (void)dealloc
121 {
122  if (mOwner) {
123  mOwner = nsnull;
124  }
125 
126  [[NSNotificationCenter defaultCenter] removeObserver:self];
127 
128  [super dealloc];
129 }
130 
132 {
133  if (!mOwner) {
134  return;
135  }
136 
137  [[NSNotificationCenter defaultCenter] addObserver:self
138  selector:@selector(windowResized:)
139  name:NSWindowDidResizeNotification
140  object:nil];
141 }
142 
144 {
145  if (!mOwner) {
146  return;
147  }
148 
149  [[NSNotificationCenter defaultCenter] removeObserver:self];
150 }
151 
152 - (void)mouseMoved:(NSEvent *)theEvent
153 {
154  if (!mOwner) {
155  return;
156  }
157 
158  mOwner->OnMouseMoved(theEvent);
159 }
160 
161 - (void)windowResized:(NSNotification *)aNotice
162 {
163  if (!mOwner) {
164  return;
165  }
166 
167  if ([[aNotice object] isEqual:[(NSView *)mOwner->GetVideoView() window]]) {
169  }
170 }
171 
172 @end
173 
174 
176  BasePlatformInterface(aCore),
177  mParentView(NULL),
178  mVideoView(NULL),
179  mGstGLViewDelegate(NULL)
180 {
182  (void *)[[SBGstGLViewDelgate alloc] initWithPlatformInterface:this];
183 }
184 
186 {
187  RemoveView();
188 
189  // Clean up the view delegate.
191  [delegate release];
192  mGstGLViewDelegate = NULL;
193 }
194 
195 GstElement *
196 OSXPlatformInterface::SetVideoSink(GstElement *aVideoSink)
197 {
198  if (mVideoSink) {
199  gst_object_unref(mVideoSink);
200  mVideoSink = NULL;
201  }
202 
203  mVideoSink = aVideoSink;
204 
205  if (!mVideoSink) {
206  mVideoSink = gst_element_factory_make("osxvideosink", "video-sink");
207  if (mVideoSink) {
208  g_object_set (mVideoSink, "embed", TRUE, NULL);
209  }
210  }
211 
212  if (!mVideoSink) {
213  // Then hopefully autovideosink will pick something appropriate...
214  mVideoSink = gst_element_factory_make("autovideosink", "video-sink");
215  }
216 
217  // Keep a reference to it.
218  if (mVideoSink)
219  gst_object_ref(mVideoSink);
220 
221  return mVideoSink;
222 }
223 
224 GstElement *
225 OSXPlatformInterface::SetAudioSink(GstElement *aAudioSink)
226 {
227  if (mAudioSink) {
228  gst_object_unref(mAudioSink);
229  mAudioSink = NULL;
230  }
231 
232  mAudioSink = aAudioSink;
233 
234  if (!mAudioSink)
235  mAudioSink = gst_element_factory_make("osxaudiosink", "audio-sink");
236  if (!mAudioSink) {
237  // Then hopefully autoaudiosink will pick something appropriate...
238  mAudioSink = gst_element_factory_make("autoaudiosink", "audio-sink");
239  }
240 
241  // Keep a reference to it.
242  if (mAudioSink)
243  gst_object_ref(mAudioSink);
244 
245  return mAudioSink;
246 }
247 
248 nsresult
249 OSXPlatformInterface::SetVideoBox (nsIBoxObject *aBoxObject, nsIWidget *aWidget)
250 {
251  // First let the superclass do its thing.
252  nsresult rv = BasePlatformInterface::SetVideoBox (aBoxObject, aWidget);
253  NS_ENSURE_SUCCESS(rv, rv);
254 
255  if (aWidget) {
256  mParentView = (void *)(aWidget->GetNativeData(NS_NATIVE_WIDGET));
257  NS_ENSURE_TRUE(mParentView != NULL, NS_ERROR_FAILURE);
258  }
259  else {
260  RemoveView();
261  mParentView = NULL;
262  }
263 
264  return NS_OK;
265 }
266 
267 void
269 {
271 
272  // Firstly, if we don't already have a video view set up, request a video
273  // window, and set up the appropriate parent view.
274  if (!mParentView) {
275  nsCOMPtr<nsIThread> mainThread;
276  nsresult rv = NS_GetMainThread(getter_AddRefs(mainThread));
277  NS_ENSURE_SUCCESS(rv, /* void */);
278 
279  nsCOMPtr<nsIRunnable> runnable =
280  NS_NEW_RUNNABLE_METHOD (sbGStreamerMediacore,
281  mCore,
282  RequestVideoWindow);
283 
284  rv = mainThread->Dispatch(runnable, NS_DISPATCH_SYNC);
285  NS_ENSURE_SUCCESS(rv, /* void */);
286  }
287 
288  // Now we can deal with setting this up...
289 
290  /* This message has an 'nsview' element containing a pointer to
291  the NSView that the video is drawn into. Grab the NSView */
292  NSView *view;
293  const GValue *value = gst_structure_get_value (
294  aMessage->structure, "nsview");
295 
296  if (!value || !G_VALUE_HOLDS_POINTER(value))
297  return;
298 
299  NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
300 
301  RemoveView();
302 
303  NSView *parentView = (NSView *)mParentView;
304  mVideoView = g_value_get_pointer (value);
305  view = (NSView *)mVideoView;
306 
307  // Listen to live resize events since gecko resize events aren't posted on
308  // Mac until the resize has finished. (see bug 20445).
310  [delegate startListeningToResizeEvents];
311 
312  // Now, we want to set this view as a subview of the NSView we have
313  // as our window-for-displaying-video. Don't do this from a non-main
314  // thread, though!
315  [parentView performSelectorOnMainThread:@selector(addSubview:)
316  withObject:view
317  waitUntilDone:YES];
318 
319  // Fail safe, ensure that the gst |NSView| responds to the delegate method
320  // before attempting to set the delegate of the view.
321  if ([view respondsToSelector:@selector(setDelegate:)]) {
322  [view setDelegate:(id)mGstGLViewDelegate];
323  }
324 
325  // Resize the window
326  ResizeToWindow();
327 
328  [pool release];
329 }
330 
331 void*
333 {
334  return mVideoView;
335 }
336 
337 void
339 {
340  NS_ENSURE_TRUE(aCocoaEvent, /* void */);
341  NS_ENSURE_TRUE(mVideoView, /* void */);
342 
343  // Convert the cocoa event to a gecko event
344  nsresult rv;
345  nsCOMPtr<nsIDOMMouseEvent> mouseEvent;
346  rv = CreateDOMMouseEvent(getter_AddRefs(mouseEvent));
347  NS_ENSURE_SUCCESS(rv, /* void */);
348 
349  NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
350 
351  // Get the window and local view coordinates and convert them from cocoa
352  // coords (bottom-left) to gecko coords (top-left).
353  NSEvent *event = (NSEvent *)aCocoaEvent;
354  NSView *eventView = (NSView *)mVideoView;
355 
356  float menuBarHeight = 0.0;
357  NSArray *allScreens = [NSScreen screens];
358  if ([allScreens count]) {
359  menuBarHeight = [[allScreens objectAtIndex:0] frame].size.height;
360  }
361  float windowHeight = [[[eventView window] contentView] frame].size.height;
362 
363  // This will need to be flipped....
364  NSPoint viewPoint = [event locationInWindow];
365  NSPoint screenPoint = [[eventView window] convertBaseToScreen:viewPoint];
366 
367  viewPoint.y = windowHeight - viewPoint.y;
368  screenPoint.y = menuBarHeight - screenPoint.y;
369 
370  rv = mouseEvent->InitMouseEvent(
371  NS_LITERAL_STRING("mousemove"),
372  PR_TRUE,
373  PR_TRUE,
374  nsnull,
375  0,
376  (PRInt32)screenPoint.x,
377  (PRInt32)screenPoint.y,
378  (PRInt32)viewPoint.x,
379  (PRInt32)viewPoint.y,
380  (([event modifierFlags] & NSControlKeyMask) != 0),
381  (([event modifierFlags] & NSAlternateKeyMask) != 0),
382  (([event modifierFlags] & NSShiftKeyMask) != 0),
383  (([event modifierFlags] & NSCommandKeyMask) != 0),
384  0,
385  nsnull);
386  [pool release];
387  NS_ENSURE_SUCCESS(rv, /* void */);
388 
389  nsCOMPtr<nsIDOMEvent> domEvent(do_QueryInterface(mouseEvent));
390  rv = DispatchDOMEvent(domEvent);
391  NS_ENSURE_SUCCESS(rv, /* void */);
392 }
393 
394 void
396 {
397  ResizeToWindow();
398 }
399 
400 void
402 {
403  NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
404 
405  NSView *view = (NSView *)mVideoView;
406  if (view) {
407  NSRect rect;
408  // Remap to OSX's coordinate system, which is from the bottom left.
409  rect.origin.y = [[view superview] frame].size.height - y - height;
410 
411  rect.origin.x = x;
412  rect.size.width = width;
413  rect.size.height = height;
414 
415  // A ObjC object is needed to simply perform a selector on the main thread.
416  // To do this, simply convert the calculated rect to a NSString.
417  NSString *frameStr = NSStringFromRect(rect);
418  [view performSelectorOnMainThread:@selector(setFrameAsString:)
419  withObject:frameStr
420  waitUntilDone:YES];
421  }
422 
423  [pool release];
424 }
425 
427 {
428  if (mVideoView) {
429  NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
430 
431  NSView *view = (NSView *)mVideoView;
432  if (view) {
433  [view performSelectorOnMainThread:@selector(removeFromSuperviewWithoutNeedingDisplay)
434  withObject:nil
435  waitUntilDone:YES];
436  }
437 
439  [delegate stopListeningToResizeEvents];
440 
441  mVideoView = nsnull;
442  [pool release];
443  }
444 }
445 
void OnMouseMoved(void *aCocoaEvent)
sbGStreamerMediacore * mCore
return NS_OK
menuItem id
Definition: FeedWriter.js:971
void MoveVideoWindow(int x, int y, int width, int height)
var event
nsresult DispatchDOMEvent(nsIDOMEvent *aEvent)
OSXPlatformInterface * mOwner
function width(ele) rect(ele).width
virtual nsresult SetVideoBox(nsIBoxObject *aVideoBox, nsIWidget *aWidget)
let window
OSXPlatformInterface(sbGStreamerMediacore *aCore)
_window init
Definition: FeedWriter.js:1144
var count
Definition: test_bug7406.js:32
virtual void PrepareVideoWindow(GstMessage *aMessage)
nsresult CreateDOMMouseEvent(nsIDOMMouseEvent **aMouseEvent)
GstElement * SetVideoSink(GstElement *aVideoSink)
function rect(ele) ele.getBoundingClientRect()
GstElement * SetAudioSink(GstElement *aAudioSink)
_updateDatepicker height
countRef value
Definition: FeedWriter.js:1423
virtual void PrepareVideoWindow(GstMessage *aMessage)
nsresult SetVideoBox(nsIBoxObject *aBoxObject, nsIWidget *aWidget)