2 * Copyright (C) 2004 Zaheer Abbas Merali <zaheerabbas at merali dot org>
3 * Copyright (C) 2007 Pioneers of the Inevitable <songbird@songbirdnest.com>
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.
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.
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.
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
25 /* inspiration gained from looking at source of osx video out of xine and vlc
26 * and is reflected in the code
30 #include <Cocoa/Cocoa.h>
32 #import "cocoawindow.h"
33 #import "osxvideosink.h"
35 #include <OpenGL/OpenGL.h>
36 #include <OpenGL/gl.h>
37 #include <OpenGL/glext.h>
39 /* Debugging category */
40 #include <gst/gstinfo.h>
42 @ implementation GstOSXVideoSinkWindow
44 /* The object has to be released */
45 - (id) initWithContentRect: (NSRect) rect
46 styleMask: (unsigned int) styleMask
47 backing: (NSBackingStoreType) bufferingType
49 screen:(NSScreen *) aScreen
51 self = [super initWithContentRect: rect
53 backing: bufferingType
57 GST_DEBUG ("Initializing GstOSXvideoSinkWindow");
59 gstview = [[GstGLView alloc] initWithFrame:rect];
62 [self setContentView:gstview];
63 [self setTitle:@"GStreamer Video Output"];
68 - (void) setContentSize:(NSSize) size {
72 [gstview setVideoSize: (int) width:(int) height];
74 [super setContentSize:size];
77 - (GstGLView *) gstView {
81 - (void) awakeFromNib {
82 [self setAcceptsMouseMovedEvents:YES];
85 - (void) sendEvent:(NSEvent *) event {
88 GST_DEBUG ("event %p type:%d", event,(gint)[event type]);
90 if ([event type] == NSKeyDown) {
92 /*taken = [gstview keyDown:event]; */
95 [super sendEvent:event];
104 // OpenGL implementation
107 @ implementation GstGLView
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,
122 fmt = [[NSOpenGLPixelFormat alloc]
123 initWithAttributes:attribs];
126 GST_WARNING ("Cannot create NSOpenGLPixelFormat");
130 self = [super initWithFrame: frame pixelFormat:fmt];
133 actualContext = [self openGLContext];
134 [actualContext makeCurrentContext];
135 [actualContext update];
137 /* Black background */
138 glClearColor (0.0, 0.0, 0.0, 0.0);
142 width = frame.size.width;
143 height = frame.size.height;
145 GST_LOG ("Width: %d Height: %d", width, height);
154 GST_LOG ("reshaping");
160 [actualContext makeCurrentContext];
162 bounds = [self bounds];
164 glViewport (0, 0, (GLint) bounds.size.width, (GLint) bounds.size.height);
168 - (void) initTextures {
170 [actualContext makeCurrentContext];
172 /* Free previous texture if any */
174 glDeleteTextures (1, (GLuint *)&pi_texture);
178 data = g_realloc (data, width * height * sizeof(short)); // short or 3byte?
180 data = g_malloc0(width * height * sizeof(short));
182 /* Create textures */
183 glGenTextures (1, (GLuint *)&pi_texture);
185 glEnable (GL_TEXTURE_RECTANGLE_EXT);
186 glEnable (GL_UNPACK_CLIENT_STORAGE_APPLE);
188 glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
189 glPixelStorei (GL_UNPACK_ROW_LENGTH, width);
191 glBindTexture (GL_TEXTURE_RECTANGLE_EXT, pi_texture);
193 /* Use VRAM texturing */
194 glTexParameteri (GL_TEXTURE_RECTANGLE_EXT,
195 GL_TEXTURE_STORAGE_HINT_APPLE, GL_STORAGE_CACHED_APPLE);
197 /* Tell the driver not to make a copy of the texture but to use
199 glPixelStorei (GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
201 /* Linear interpolation */
202 glTexParameteri (GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
203 glTexParameteri (GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
205 /* I have no idea what this exactly does, but it seems to be
206 necessary for scaling */
207 glTexParameteri (GL_TEXTURE_RECTANGLE_EXT,
208 GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
209 glTexParameteri (GL_TEXTURE_RECTANGLE_EXT,
210 GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
211 // glPixelStorei (GL_UNPACK_ROW_LENGTH, 0); WHY ??
213 glTexImage2D (GL_TEXTURE_RECTANGLE_EXT, 0, GL_RGBA,
215 GL_YCBCR_422_APPLE, GL_UNSIGNED_SHORT_8_8_APPLE, data);
221 - (void) reloadTexture {
226 GST_LOG ("Reloading Texture");
228 [actualContext makeCurrentContext];
230 glBindTexture (GL_TEXTURE_RECTANGLE_EXT, pi_texture);
231 glPixelStorei (GL_UNPACK_ROW_LENGTH, width);
233 /* glTexSubImage2D is faster than glTexImage2D
234 http://developer.apple.com/samplecode/Sample_Code/Graphics_3D/
235 TextureRange/MainOpenGLView.m.htm */
236 glTexSubImage2D (GL_TEXTURE_RECTANGLE_EXT, 0, 0, 0,
238 GL_YCBCR_422_APPLE, GL_UNSIGNED_SHORT_8_8_APPLE, data); //FIXME
251 glTexCoord2f (0.0, 0.0);
252 glVertex2f (-f_x, f_y);
254 glTexCoord2f (0.0, (float) height);
255 glVertex2f (-f_x, -f_y);
257 glTexCoord2f ((float) width, (float) height);
258 glVertex2f (f_x, -f_y);
260 glTexCoord2f ((float) width, 0.0);
261 glVertex2f (f_x, f_y);
265 - (void) drawRect:(NSRect) rect {
266 GLint params[] = { 1 };
268 [actualContext makeCurrentContext];
270 CGLSetParameter (CGLGetCurrentContext (), kCGLCPSwapInterval, params);
272 /* Black background */
273 glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
276 [actualContext flushBuffer];
281 glBindTexture (GL_TEXTURE_RECTANGLE_EXT, pi_texture); // FIXME
284 [actualContext flushBuffer];
287 - (void) displayTexture {
288 if ([self lockFocusIfCanDraw]) {
290 [self drawRect:[self bounds]];
291 [self reloadTexture];
299 - (char *) getTextureBuffer {
303 - (void) setFullScreen:(BOOL) flag {
304 if (!fullscreen && flag) {
306 /* Create the new pixel format */
307 NSOpenGLPixelFormat *fmt;
308 NSOpenGLPixelFormatAttribute attribs[] = {
309 NSOpenGLPFAAccelerated,
310 NSOpenGLPFANoRecovery,
311 NSOpenGLPFADoubleBuffer,
312 NSOpenGLPFAColorSize, 24,
313 NSOpenGLPFAAlphaSize, 8,
314 NSOpenGLPFADepthSize, 24,
315 NSOpenGLPFAFullScreen,
316 NSOpenGLPFAScreenMask,
317 CGDisplayIDToOpenGLDisplayMask (kCGDirectMainDisplay),
321 fmt = [[NSOpenGLPixelFormat alloc]
322 initWithAttributes:attribs];
325 GST_WARNING ("Cannot create NSOpenGLPixelFormat");
329 /* Create the new OpenGL context */
330 fullScreenContext = [[NSOpenGLContext alloc]
331 initWithFormat: fmt shareContext:nil];
332 if (!fullScreenContext) {
333 GST_WARNING ("Failed to create new NSOpenGLContext");
337 actualContext = fullScreenContext;
339 /* Capture display, switch to fullscreen */
340 if (CGCaptureAllDisplays () != CGDisplayNoErr) {
341 GST_WARNING ("CGCaptureAllDisplays() failed");
344 [fullScreenContext setFullScreen];
345 [fullScreenContext makeCurrentContext];
350 [self setNeedsDisplay:YES];
352 } else if (fullscreen && !flag) {
353 // fullscreen now and needs to go back to normal
356 actualContext = [self openGLContext];
358 [NSOpenGLContext clearCurrentContext];
359 [fullScreenContext clearDrawable];
360 [fullScreenContext release];
361 fullScreenContext = nil;
363 CGReleaseAllDisplays ();
368 [self setNeedsDisplay:YES];
375 - (void) setVideoSize: (int) w:(int) h {
376 GST_LOG ("width:%d, height:%d", w, h);
381 // if (data) g_free(data);
383 // data = g_malloc0 (2 * w * h);
387 - (void) haveSuperviewReal:(NSMutableArray *)closure {
388 BOOL haveSuperview = [self superview] != nil;
389 [closure addObject:[NSNumber numberWithBool:haveSuperview]];
392 - (BOOL) haveSuperview {
393 NSMutableArray *closure = [NSMutableArray arrayWithCapacity:1];
394 [self performSelectorOnMainThread:@selector(haveSuperviewReal:)
395 withObject:(id)closure waitUntilDone:YES];
397 return [[closure objectAtIndex:0] boolValue];
400 - (void) addToSuperviewReal:(NSView *)superview {
402 [superview addSubview:self];
403 bounds = [superview bounds];
404 [self setFrame:bounds];
405 [self setAutoresizingMask:NSViewWidthSizable|NSViewHeightSizable];
408 - (void) addToSuperview: (NSView *)superview {
409 [self performSelectorOnMainThread:@selector(addToSuperviewReal:)
410 withObject:superview waitUntilDone:YES];
413 - (void) removeFromSuperview: (id)unused
415 [self removeFromSuperview];
419 GST_LOG ("dealloc called");
420 if (data) g_free(data);
422 if (fullScreenContext) {
423 [NSOpenGLContext clearCurrentContext];
424 [fullScreenContext clearDrawable];
425 [fullScreenContext release];
426 if (actualContext == fullScreenContext) actualContext = nil;
427 fullScreenContext = nil;