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];
132 actualContext = [self openGLContext];
133 [actualContext makeCurrentContext];
134 [actualContext update];
136 /* Black background */
137 glClearColor (0.0, 0.0, 0.0, 0.0);
141 width = frame.size.width;
142 height = frame.size.height;
144 GST_LOG ("Width: %d Height: %d", width, height);
153 GST_LOG ("reshaping");
159 [actualContext makeCurrentContext];
161 bounds = [self bounds];
163 glViewport (0, 0, (GLint) bounds.size.width, (GLint) bounds.size.height);
167 - (void) initTextures {
169 [actualContext makeCurrentContext];
171 /* Free previous texture if any */
173 glDeleteTextures (1, (GLuint *)&pi_texture);
177 data = g_realloc (data, width * height * sizeof(short)); // short or 3byte?
179 data = g_malloc0(width * height * sizeof(short));
181 /* Create textures */
182 glGenTextures (1, (GLuint *)&pi_texture);
184 glEnable (GL_TEXTURE_RECTANGLE_EXT);
185 glEnable (GL_UNPACK_CLIENT_STORAGE_APPLE);
187 glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
188 glPixelStorei (GL_UNPACK_ROW_LENGTH, width);
190 glBindTexture (GL_TEXTURE_RECTANGLE_EXT, pi_texture);
192 /* Use VRAM texturing */
193 glTexParameteri (GL_TEXTURE_RECTANGLE_EXT,
194 GL_TEXTURE_STORAGE_HINT_APPLE, GL_STORAGE_CACHED_APPLE);
196 /* Tell the driver not to make a copy of the texture but to use
198 glPixelStorei (GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
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);
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 ??
212 glTexImage2D (GL_TEXTURE_RECTANGLE_EXT, 0, GL_RGBA,
214 GL_YCBCR_422_APPLE, GL_UNSIGNED_SHORT_8_8_APPLE, data);
220 - (void) reloadTexture {
225 GST_LOG ("Reloading Texture");
227 [actualContext makeCurrentContext];
229 glBindTexture (GL_TEXTURE_RECTANGLE_EXT, pi_texture);
230 glPixelStorei (GL_UNPACK_ROW_LENGTH, width);
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,
237 GL_YCBCR_422_APPLE, GL_UNSIGNED_SHORT_8_8_APPLE, data); //FIXME
250 glTexCoord2f (0.0, 0.0);
251 glVertex2f (-f_x, f_y);
253 glTexCoord2f (0.0, (float) height);
254 glVertex2f (-f_x, -f_y);
256 glTexCoord2f ((float) width, (float) height);
257 glVertex2f (f_x, -f_y);
259 glTexCoord2f ((float) width, 0.0);
260 glVertex2f (f_x, f_y);
264 - (void) drawRect:(NSRect) rect {
265 GLint params[] = { 1 };
267 [actualContext makeCurrentContext];
269 CGLSetParameter (CGLGetCurrentContext (), kCGLCPSwapInterval, params);
271 /* Black background */
272 glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
275 [actualContext flushBuffer];
280 glBindTexture (GL_TEXTURE_RECTANGLE_EXT, pi_texture); // FIXME
283 [actualContext flushBuffer];
286 - (void) displayTexture {
287 if ([self lockFocusIfCanDraw]) {
289 [self drawRect:[self bounds]];
290 [self reloadTexture];
298 - (char *) getTextureBuffer {
302 - (void) setFullScreen:(BOOL) flag {
303 if (!fullscreen && flag) {
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),
320 fmt = [[NSOpenGLPixelFormat alloc]
321 initWithAttributes:attribs];
324 GST_WARNING ("Cannot create NSOpenGLPixelFormat");
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");
336 actualContext = fullScreenContext;
338 /* Capture display, switch to fullscreen */
339 if (CGCaptureAllDisplays () != CGDisplayNoErr) {
340 GST_WARNING ("CGCaptureAllDisplays() failed");
343 [fullScreenContext setFullScreen];
344 [fullScreenContext makeCurrentContext];
349 [self setNeedsDisplay:YES];
351 } else if (fullscreen && !flag) {
352 // fullscreen now and needs to go back to normal
355 actualContext = [self openGLContext];
357 [NSOpenGLContext clearCurrentContext];
358 [fullScreenContext clearDrawable];
359 [fullScreenContext release];
360 fullScreenContext = nil;
362 CGReleaseAllDisplays ();
367 [self setNeedsDisplay:YES];
374 - (void) setVideoSize: (int) w:(int) h {
375 GST_LOG ("width:%d, height:%d", w, h);
380 // if (data) g_free(data);
382 // data = g_malloc0 (2 * w * h);
386 - (void) haveSuperviewReal:(NSMutableArray *)closure {
387 BOOL haveSuperview = [self superview] != nil;
388 [closure addObject:[NSNumber numberWithBool:haveSuperview]];
391 - (BOOL) haveSuperview {
392 NSMutableArray *closure = [NSMutableArray arrayWithCapacity:1];
393 [self performSelectorOnMainThread:@selector(haveSuperviewReal:)
394 withObject:(id)closure waitUntilDone:YES];
396 return [[closure objectAtIndex:0] boolValue];
399 - (void) addToSuperviewReal:(NSView *)superview {
401 [superview addSubview:self];
402 bounds = [superview bounds];
403 [self setFrame:bounds];
404 [self setAutoresizingMask:NSViewWidthSizable|NSViewHeightSizable];
407 - (void) addToSuperview: (NSView *)superview {
408 [self performSelectorOnMainThread:@selector(addToSuperviewReal:)
409 withObject:superview waitUntilDone:YES];
412 - (void) removeFromSuperview: (id)unused
414 [self removeFromSuperview];
418 GST_LOG ("dealloc called");
419 if (data) g_free(data);
421 if (fullScreenContext) {
422 [NSOpenGLContext clearCurrentContext];
423 [fullScreenContext clearDrawable];
424 [fullScreenContext release];
425 if (actualContext == fullScreenContext) actualContext = nil;
426 fullScreenContext = nil;