upload tizen1.0 source
[framework/multimedia/gst-plugins-good0.10.git] / sys / osxvideo / cocoawindow.m
1 /* GStreamer
2  * Copyright (C) 2004 Zaheer Abbas Merali <zaheerabbas at merali dot org>
3  * Copyright (C) 2007 Pioneers of the Inevitable <songbird@songbirdnest.com>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  * 
20  * The development of this code was made possible due to the involvement of Pioneers 
21  * of the Inevitable, the creators of the Songbird Music player
22  *
23  */
24
25 /* inspiration gained from looking at source of osx video out of xine and vlc
26  * and is reflected in the code
27  */
28
29
30 #include <Cocoa/Cocoa.h>
31 #include <gst/gst.h>
32 #import "cocoawindow.h"
33 #import "osxvideosink.h"
34
35 #include <OpenGL/OpenGL.h>
36 #include <OpenGL/gl.h>
37 #include <OpenGL/glext.h>
38
39 /* Debugging category */
40 #include <gst/gstinfo.h>
41
42 @ implementation GstOSXVideoSinkWindow
43
44 /* The object has to be released */
45 - (id) initWithContentRect: (NSRect) rect
46                  styleMask: (unsigned int) styleMask
47                    backing: (NSBackingStoreType) bufferingType 
48                      defer: (BOOL) flag
49                     screen:(NSScreen *) aScreen
50 {
51   self = [super initWithContentRect: rect
52                 styleMask: styleMask
53                 backing: bufferingType 
54                 defer: flag 
55                 screen:aScreen];
56
57   GST_DEBUG ("Initializing GstOSXvideoSinkWindow");
58
59   gstview = [[GstGLView alloc] initWithFrame:rect];
60   
61   if (gstview)
62     [self setContentView:gstview];
63   [self setTitle:@"GStreamer Video Output"];
64
65   return self;
66 }
67
68 - (void) setContentSize:(NSSize) size {
69   width = size.width;
70   height = size.height;
71
72   [gstview setVideoSize: (int) width:(int) height];
73
74   [super setContentSize:size];
75 }
76
77 - (GstGLView *) gstView {
78   return gstview;
79 }
80
81 - (void) awakeFromNib {
82   [self setAcceptsMouseMovedEvents:YES];
83 }
84
85 - (void) sendEvent:(NSEvent *) event {
86   BOOL taken = NO;
87
88   GST_DEBUG ("event %p type:%d", event,(gint)[event type]);
89
90   if ([event type] == NSKeyDown) {
91   }
92   /*taken = [gstview keyDown:event]; */
93
94   if (!taken) {
95     [super sendEvent:event];
96   }
97 }
98
99
100 @end
101
102
103 //
104 // OpenGL implementation
105 //
106
107 @ implementation GstGLView
108
109 - (id) initWithFrame:(NSRect) frame {
110   NSOpenGLPixelFormat *fmt;
111   NSOpenGLPixelFormatAttribute attribs[] = {
112     NSOpenGLPFAAccelerated,
113     NSOpenGLPFANoRecovery,
114     NSOpenGLPFADoubleBuffer,
115     NSOpenGLPFAColorSize, 24,
116     NSOpenGLPFAAlphaSize, 8,
117     NSOpenGLPFADepthSize, 24,
118     NSOpenGLPFAWindow,
119     0
120   };
121
122   fmt = [[NSOpenGLPixelFormat alloc]
123           initWithAttributes:attribs];
124
125   if (!fmt) {
126     GST_WARNING ("Cannot create NSOpenGLPixelFormat");
127     return nil;
128   }
129
130   self = [super initWithFrame: frame pixelFormat:fmt];
131
132    actualContext = [self openGLContext];
133    [actualContext makeCurrentContext];
134    [actualContext update];
135
136   /* Black background */
137   glClearColor (0.0, 0.0, 0.0, 0.0);
138
139   pi_texture = 0;
140   data = nil;
141   width = frame.size.width;
142   height = frame.size.height;
143
144   GST_LOG ("Width: %d Height: %d", width, height);
145
146   [self initTextures];
147   return self;
148 }
149
150 - (void) reshape {
151   NSRect bounds;
152
153   GST_LOG ("reshaping");
154
155   if (!initDone) {
156     return;
157   }
158
159   [actualContext makeCurrentContext];
160
161   bounds = [self bounds];
162
163   glViewport (0, 0, (GLint) bounds.size.width, (GLint) bounds.size.height);
164
165 }
166
167 - (void) initTextures {
168
169   [actualContext makeCurrentContext];
170
171   /* Free previous texture if any */
172   if (pi_texture) {
173     glDeleteTextures (1, (GLuint *)&pi_texture);
174   }
175
176   if (data) {
177     data = g_realloc (data, width * height * sizeof(short)); // short or 3byte?
178   } else {
179     data = g_malloc0(width * height * sizeof(short));
180   }
181   /* Create textures */
182   glGenTextures (1, (GLuint *)&pi_texture);
183
184   glEnable (GL_TEXTURE_RECTANGLE_EXT);
185   glEnable (GL_UNPACK_CLIENT_STORAGE_APPLE);
186
187   glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
188   glPixelStorei (GL_UNPACK_ROW_LENGTH, width);
189   
190   glBindTexture (GL_TEXTURE_RECTANGLE_EXT, pi_texture);
191
192   /* Use VRAM texturing */
193   glTexParameteri (GL_TEXTURE_RECTANGLE_EXT,
194                    GL_TEXTURE_STORAGE_HINT_APPLE, GL_STORAGE_CACHED_APPLE);
195
196   /* Tell the driver not to make a copy of the texture but to use
197      our buffer */
198   glPixelStorei (GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
199
200   /* Linear interpolation */
201   glTexParameteri (GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
202   glTexParameteri (GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
203
204   /* I have no idea what this exactly does, but it seems to be
205      necessary for scaling */
206   glTexParameteri (GL_TEXTURE_RECTANGLE_EXT,
207                    GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
208   glTexParameteri (GL_TEXTURE_RECTANGLE_EXT,
209                    GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
210   // glPixelStorei (GL_UNPACK_ROW_LENGTH, 0); WHY ??
211
212   glTexImage2D (GL_TEXTURE_RECTANGLE_EXT, 0, GL_RGBA,
213                 width, height, 0, 
214                 GL_YCBCR_422_APPLE, GL_UNSIGNED_SHORT_8_8_APPLE, data);
215
216
217   initDone = 1;
218 }
219
220 - (void) reloadTexture {
221   if (!initDone) {
222     return;
223   }
224
225   GST_LOG ("Reloading Texture");
226
227   [actualContext makeCurrentContext];
228
229   glBindTexture (GL_TEXTURE_RECTANGLE_EXT, pi_texture);
230   glPixelStorei (GL_UNPACK_ROW_LENGTH, width);
231
232   /* glTexSubImage2D is faster than glTexImage2D
233      http://developer.apple.com/samplecode/Sample_Code/Graphics_3D/
234      TextureRange/MainOpenGLView.m.htm */
235   glTexSubImage2D (GL_TEXTURE_RECTANGLE_EXT, 0, 0, 0,
236                    width, height,
237                    GL_YCBCR_422_APPLE, GL_UNSIGNED_SHORT_8_8_APPLE, data);    //FIXME
238 }
239
240 - (void) cleanUp {
241   initDone = 0;
242 }
243
244 - (void) drawQuad {
245   f_x = 1.0;
246   f_y = 1.0;
247
248   glBegin (GL_QUADS);
249   /* Top left */
250   glTexCoord2f (0.0, 0.0);
251   glVertex2f (-f_x, f_y);
252   /* Bottom left */
253   glTexCoord2f (0.0, (float) height);
254   glVertex2f (-f_x, -f_y);
255   /* Bottom right */
256   glTexCoord2f ((float) width, (float) height);
257   glVertex2f (f_x, -f_y);
258   /* Top right */
259   glTexCoord2f ((float) width, 0.0);
260   glVertex2f (f_x, f_y);
261   glEnd ();
262 }
263
264 - (void) drawRect:(NSRect) rect {
265   GLint params[] = { 1 };
266
267   [actualContext makeCurrentContext];
268
269   CGLSetParameter (CGLGetCurrentContext (), kCGLCPSwapInterval, params);
270
271   /* Black background */
272   glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
273
274   if (!initDone) {
275     [actualContext flushBuffer];
276     return;
277   }
278
279   /* Draw */
280   glBindTexture (GL_TEXTURE_RECTANGLE_EXT, pi_texture); // FIXME
281   [self drawQuad];
282   /* Draw */
283   [actualContext flushBuffer];
284 }
285
286 - (void) displayTexture {
287   if ([self lockFocusIfCanDraw]) {
288
289     [self drawRect:[self bounds]];
290     [self reloadTexture];
291
292     [self unlockFocus];
293
294   }
295
296 }
297
298 - (char *) getTextureBuffer {
299   return data;
300 }
301
302 - (void) setFullScreen:(BOOL) flag {
303   if (!fullscreen && flag) {
304     // go to full screen
305     /* Create the new pixel format */
306     NSOpenGLPixelFormat *fmt;
307     NSOpenGLPixelFormatAttribute attribs[] = {
308       NSOpenGLPFAAccelerated,
309       NSOpenGLPFANoRecovery,
310       NSOpenGLPFADoubleBuffer,
311       NSOpenGLPFAColorSize, 24,
312       NSOpenGLPFAAlphaSize, 8,
313       NSOpenGLPFADepthSize, 24,
314       NSOpenGLPFAFullScreen,
315       NSOpenGLPFAScreenMask,
316       CGDisplayIDToOpenGLDisplayMask (kCGDirectMainDisplay),
317       0
318     };
319
320     fmt = [[NSOpenGLPixelFormat alloc]
321             initWithAttributes:attribs];
322
323     if (!fmt) {
324       GST_WARNING ("Cannot create NSOpenGLPixelFormat");
325       return;
326     }
327
328     /* Create the new OpenGL context */
329     fullScreenContext = [[NSOpenGLContext alloc]
330                           initWithFormat: fmt shareContext:nil];
331     if (!fullScreenContext) {
332       GST_WARNING ("Failed to create new NSOpenGLContext");
333       return;
334     }
335
336     actualContext = fullScreenContext;
337
338     /* Capture display, switch to fullscreen */
339     if (CGCaptureAllDisplays () != CGDisplayNoErr) {
340       GST_WARNING ("CGCaptureAllDisplays() failed");
341       return;
342     }
343     [fullScreenContext setFullScreen];
344     [fullScreenContext makeCurrentContext];
345
346     fullscreen = YES;
347
348     [self initTextures];
349     [self setNeedsDisplay:YES];
350
351   } else if (fullscreen && !flag) {
352     // fullscreen now and needs to go back to normal
353     initDone = NO;
354     
355     actualContext = [self openGLContext];
356
357     [NSOpenGLContext clearCurrentContext];
358     [fullScreenContext clearDrawable];
359     [fullScreenContext release];
360     fullScreenContext = nil;
361
362     CGReleaseAllDisplays ();
363
364     [self reshape];
365     [self initTextures];
366
367     [self setNeedsDisplay:YES];
368
369     fullscreen = NO;
370     initDone = YES;
371   }
372 }
373
374 - (void) setVideoSize: (int) w:(int) h {
375   GST_LOG ("width:%d, height:%d", w, h);
376
377   width = w;
378   height = h;
379
380 //  if (data) g_free(data);
381
382 //  data = g_malloc0 (2 * w * h);
383   [self initTextures];
384 }
385
386 - (void) haveSuperviewReal:(NSMutableArray *)closure {
387         BOOL haveSuperview = [self superview] != nil;
388         [closure addObject:[NSNumber numberWithBool:haveSuperview]];
389 }
390
391 - (BOOL) haveSuperview {
392         NSMutableArray *closure = [NSMutableArray arrayWithCapacity:1];
393         [self performSelectorOnMainThread:@selector(haveSuperviewReal:)
394                         withObject:(id)closure waitUntilDone:YES];
395
396         return [[closure objectAtIndex:0] boolValue];
397 }
398
399 - (void) addToSuperviewReal:(NSView *)superview {
400         NSRect bounds;
401         [superview addSubview:self];
402         bounds = [superview bounds];
403         [self setFrame:bounds];
404         [self setAutoresizingMask:NSViewWidthSizable|NSViewHeightSizable];
405 }
406
407 - (void) addToSuperview: (NSView *)superview {
408         [self performSelectorOnMainThread:@selector(addToSuperviewReal:)
409                         withObject:superview waitUntilDone:YES];
410 }
411
412 - (void) removeFromSuperview: (id)unused
413 {
414         [self removeFromSuperview];
415 }
416
417 - (void) dealloc {
418   GST_LOG ("dealloc called");
419   if (data) g_free(data);
420
421   if (fullScreenContext) {
422     [NSOpenGLContext clearCurrentContext];
423     [fullScreenContext clearDrawable];
424     [fullScreenContext release];
425     if (actualContext == fullScreenContext) actualContext = nil;
426     fullScreenContext = nil;
427   }
428
429   [super dealloc];
430 }
431 @end