mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-20 23:36:38 +00:00
5d004d2c32
Original commit message from CVS: * configure.ac: Check for an Objective C compiler * sys/Makefile.am: * sys/osxvideo/Makefile.am: * sys/osxvideo/cocoawindow.h: * sys/osxvideo/cocoawindow.m: * sys/osxvideo/osxvideosink.h: * sys/osxvideo/osxvideosink.m: Port of osxvideo plugin to 0.10. Do NOT consider 100% stable ! Fixes #402470
475 lines
11 KiB
Objective-C
475 lines
11 KiB
Objective-C
/* GStreamer
|
|
* Copyright (C) 2004 Zaheer Abbas Merali <zaheerabbas at merali dot org>
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Library General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Library General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Library General Public
|
|
* License along with this library; if not, write to the
|
|
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
* Boston, MA 02111-1307, USA.
|
|
*/
|
|
|
|
/* inspiration gained from looking at source of osx video out of xine and vlc
|
|
* and is reflected in the code
|
|
*/
|
|
|
|
|
|
#include <Cocoa/Cocoa.h>
|
|
#include <gst/gst.h>
|
|
#import "cocoawindow.h"
|
|
#import "osxvideosink.h"
|
|
|
|
#include <OpenGL/OpenGL.h>
|
|
#include <OpenGL/gl.h>
|
|
#include <OpenGL/glext.h>
|
|
|
|
/* Debugging category */
|
|
#include <gst/gstinfo.h>
|
|
|
|
@ implementation GstOSXVideoSinkWindow
|
|
|
|
- (id) initWithContentRect: (NSRect) rect
|
|
styleMask: (unsigned int) styleMask
|
|
backing: (NSBackingStoreType) bufferingType
|
|
defer: (BOOL) flag
|
|
screen:(NSScreen *) aScreen
|
|
{
|
|
self = [super initWithContentRect: rect
|
|
styleMask: styleMask
|
|
backing: bufferingType
|
|
defer: flag
|
|
screen:aScreen];
|
|
|
|
GST_DEBUG ("Initializing GstOSXvideoSinkWindow");
|
|
|
|
gstview = [[GstGLView alloc] initWithFrame:rect];
|
|
|
|
if (gstview)
|
|
[self setContentView:gstview];
|
|
[self setTitle:@"GStreamer Video Output"];
|
|
|
|
return self;
|
|
}
|
|
|
|
- (void) setContentSize:(NSSize) size {
|
|
width = size.width;
|
|
height = size.height;
|
|
|
|
[gstview setVideoSize: (int) width:(int) height];
|
|
|
|
[super setContentSize:size];
|
|
}
|
|
|
|
- (GstGLView *) gstView {
|
|
return gstview;
|
|
}
|
|
|
|
- (void) awakeFromNib {
|
|
[self setAcceptsMouseMovedEvents:YES];
|
|
}
|
|
|
|
- (void) sendEvent:(NSEvent *) event {
|
|
BOOL taken = NO;
|
|
|
|
GST_LOG ("event %p type:%d", event,[event type]);
|
|
|
|
if ([event type] == NSKeyDown) {
|
|
}
|
|
/*taken = [gstview keyDown:event]; */
|
|
|
|
if (!taken) {
|
|
[super sendEvent:event];
|
|
}
|
|
}
|
|
|
|
|
|
@end
|
|
|
|
//
|
|
// GstView
|
|
// Deprecated QuickDraw implementation
|
|
//
|
|
|
|
@ implementation GstView
|
|
|
|
- (void) drawRect:(NSRect) rect {
|
|
/*NSRect bounds = [self bounds];
|
|
[[NSColor greenColor] set];
|
|
[NSBezierPath fillRect:bounds]; */
|
|
[[NSColor blackColor] set];
|
|
NSRectFill (rect);
|
|
[super drawRect:rect];
|
|
}
|
|
|
|
- (id) initWithFrame:(NSRect) frame {
|
|
NSRect bounds =[self bounds];
|
|
|
|
[[NSColor greenColor] set];
|
|
|
|
self =[super initWithFrame:frame];
|
|
isPortSet = FALSE;
|
|
[NSBezierPath fillRect:bounds];
|
|
return self;
|
|
}
|
|
|
|
- (void) setVideoSize: (int) w:(int) h {
|
|
GST_LOG ("width:%d height:%d", w, h);
|
|
|
|
width = w;
|
|
height = h;
|
|
}
|
|
|
|
- (void) setVideoImage:(GstBuffer *) img {
|
|
if (isPortSet == FALSE) {
|
|
// first image
|
|
//GWorldPtr imgGWorld;
|
|
//Rect coords;
|
|
OSErr err;
|
|
ImageDescriptionPtr pimgdesc;
|
|
|
|
err = EnterMovies ();
|
|
|
|
if (err != noErr)
|
|
GST_ERROR ("EnterMovies error: %d", err);
|
|
/*SetRect(&coords,0,0,width,height);
|
|
NewGWorldFromPtr (&imgGWorld, kYUV420CodecType, &coords, 0, 0, 0, GST_BUFFER_DATA(img), width * 4);
|
|
MakeImageDescriptionForPixMap (GetGWorldPixMap(imgGWorld), &imgdesc);
|
|
DisposeGWorld(imgGWorld); */
|
|
imgdesc =
|
|
(ImageDescriptionHandle) NewHandleClear (sizeof (ImageDescription));
|
|
pimgdesc = *imgdesc;
|
|
pimgdesc->idSize = sizeof (ImageDescription);
|
|
pimgdesc->cType = kYUV420CodecType;
|
|
pimgdesc->version = 1;
|
|
pimgdesc->revisionLevel = 0;
|
|
pimgdesc->vendor = 'appl';
|
|
pimgdesc->width = width;
|
|
pimgdesc->height = height;
|
|
pimgdesc->hRes = Long2Fix (72);
|
|
pimgdesc->vRes = Long2Fix (72);
|
|
pimgdesc->spatialQuality = codecLosslessQuality;
|
|
pimgdesc->frameCount = 1;
|
|
pimgdesc->clutID = -1;
|
|
pimgdesc->dataSize = 0;
|
|
pimgdesc->depth = 12;
|
|
|
|
[self lockFocus];
|
|
port =[self qdPort];
|
|
g_warning ("port = 0x%x", (int) port);
|
|
err = DecompressSequenceBeginS (&qtseqid, imgdesc, NULL, 0, port, NULL, NULL, NULL, 0, // srcCopy
|
|
NULL, codecFlagUseImageBuffer, codecLosslessQuality, bestSpeedCodec);
|
|
if (err != noErr) {
|
|
GST_DEBUG ("DecompressSequenceBeginS error: %d", err);
|
|
}
|
|
[self unlockFocus];
|
|
isPortSet = TRUE;
|
|
}
|
|
|
|
OSErr err;
|
|
CodecFlags flags;
|
|
|
|
GST_DEBUG ("qtseqid: %d img data: %p size: %d", (int) qtseqid,
|
|
GST_BUFFER_DATA (img), GST_BUFFER_SIZE (img));
|
|
err =
|
|
DecompressSequenceFrameS (qtseqid, (char *) GST_BUFFER_DATA (img),
|
|
GST_BUFFER_SIZE (img), codecFlagUseImageBuffer, &flags, NULL);
|
|
if (err != noErr) {
|
|
GST_DEBUG ("DecompressSequenceS erro: %d", err);
|
|
} else {
|
|
//QDFlushPortBuffer (port, nil);
|
|
}
|
|
|
|
}
|
|
|
|
@end
|
|
|
|
//
|
|
// OpenGL implementation
|
|
//
|
|
|
|
@ implementation GstGLView
|
|
|
|
- (id) initWithFrame:(NSRect) frame {
|
|
NSOpenGLPixelFormat *fmt;
|
|
NSOpenGLPixelFormatAttribute attribs[] = {
|
|
NSOpenGLPFAAccelerated,
|
|
NSOpenGLPFANoRecovery,
|
|
NSOpenGLPFADoubleBuffer,
|
|
NSOpenGLPFAColorSize, 24,
|
|
NSOpenGLPFAAlphaSize, 8,
|
|
NSOpenGLPFADepthSize, 24,
|
|
NSOpenGLPFAWindow,
|
|
0
|
|
};
|
|
|
|
fmt = [[NSOpenGLPixelFormat alloc]
|
|
initWithAttributes:attribs];
|
|
|
|
if (!fmt) {
|
|
GST_WARNING ("Cannot create NSOpenGLPixelFormat");
|
|
return nil;
|
|
}
|
|
|
|
self = [super initWithFrame: frame pixelFormat:fmt];
|
|
|
|
[[self openGLContext] makeCurrentContext];
|
|
[[self openGLContext] update];
|
|
|
|
/* Black background */
|
|
glClearColor (0.0, 0.0, 0.0, 0.0);
|
|
|
|
pi_texture = 0;
|
|
data = g_malloc (2 * 320 * 240);
|
|
width = frame.size.width;
|
|
height = frame.size.height;
|
|
|
|
GST_LOG ("Width: %d Height: %d", width, height);
|
|
|
|
[self initTextures];
|
|
return self;
|
|
}
|
|
|
|
- (void) reshape {
|
|
NSRect bounds;
|
|
|
|
GST_LOG ("reshaping");
|
|
|
|
if (!initDone) {
|
|
return;
|
|
}
|
|
|
|
[[self openGLContext] makeCurrentContext];
|
|
|
|
bounds = [self bounds];
|
|
|
|
glViewport (0, 0, (GLint) bounds.size.width, (GLint) bounds.size.height);
|
|
|
|
}
|
|
|
|
- (void) initTextures {
|
|
NSOpenGLContext *currentContext;
|
|
|
|
if (fullscreen)
|
|
currentContext = fullScreenContext;
|
|
else
|
|
currentContext =[self openGLContext];
|
|
|
|
[currentContext makeCurrentContext];
|
|
|
|
/* Free previous texture if any */
|
|
if (initDone) {
|
|
glDeleteTextures (1, &pi_texture);
|
|
}
|
|
/* Create textures */
|
|
glGenTextures (1, &pi_texture);
|
|
|
|
glEnable (GL_TEXTURE_RECTANGLE_EXT);
|
|
glEnable (GL_UNPACK_CLIENT_STORAGE_APPLE);
|
|
|
|
glBindTexture (GL_TEXTURE_RECTANGLE_EXT, pi_texture);
|
|
|
|
/* Use VRAM texturing */
|
|
glTexParameteri (GL_TEXTURE_RECTANGLE_EXT,
|
|
GL_TEXTURE_STORAGE_HINT_APPLE, GL_STORAGE_CACHED_APPLE);
|
|
|
|
/* Tell the driver not to make a copy of the texture but to use
|
|
our buffer */
|
|
glPixelStorei (GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
|
|
|
|
|
|
/* Linear interpolation */
|
|
glTexParameteri (GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
glTexParameteri (GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
|
|
/* I have no idea what this exactly does, but it seems to be
|
|
necessary for scaling */
|
|
glTexParameteri (GL_TEXTURE_RECTANGLE_EXT,
|
|
GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
glTexParameteri (GL_TEXTURE_RECTANGLE_EXT,
|
|
GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
glPixelStorei (GL_UNPACK_ROW_LENGTH, 0);
|
|
|
|
glTexImage2D (GL_TEXTURE_RECTANGLE_EXT, 0, GL_RGBA,
|
|
width, height, 0, GL_YCBCR_422_APPLE, GL_UNSIGNED_SHORT_8_8_APPLE, data);
|
|
|
|
|
|
initDone = 1;
|
|
}
|
|
|
|
- (void) reloadTexture {
|
|
NSOpenGLContext *currentContext;
|
|
|
|
if (!initDone) {
|
|
return;
|
|
}
|
|
|
|
GST_LOG ("Reloading Texture");
|
|
|
|
if (fullscreen)
|
|
currentContext = fullScreenContext;
|
|
else
|
|
currentContext =[self openGLContext];
|
|
[currentContext makeCurrentContext];
|
|
|
|
glBindTexture (GL_TEXTURE_RECTANGLE_EXT, pi_texture);
|
|
|
|
/* glTexSubImage2D is faster than glTexImage2D
|
|
http://developer.apple.com/samplecode/Sample_Code/Graphics_3D/
|
|
TextureRange/MainOpenGLView.m.htm */
|
|
glTexSubImage2D (GL_TEXTURE_RECTANGLE_EXT, 0, 0, 0, width, height, GL_YCBCR_422_APPLE, GL_UNSIGNED_SHORT_8_8_APPLE, data); //FIXME
|
|
}
|
|
|
|
- (void) cleanUp {
|
|
initDone = 0;
|
|
}
|
|
|
|
- (void) drawQuad {
|
|
f_x = 1.0;
|
|
f_y = 1.0;
|
|
|
|
glBegin (GL_QUADS);
|
|
/* Top left */
|
|
glTexCoord2f (0.0, 0.0);
|
|
glVertex2f (-f_x, f_y);
|
|
/* Bottom left */
|
|
glTexCoord2f (0.0, (float) height);
|
|
glVertex2f (-f_x, -f_y);
|
|
/* Bottom right */
|
|
glTexCoord2f ((float) width, (float) height);
|
|
glVertex2f (f_x, -f_y);
|
|
/* Top right */
|
|
glTexCoord2f ((float) width, 0.0);
|
|
glVertex2f (f_x, f_y);
|
|
glEnd ();
|
|
}
|
|
|
|
- (void) drawRect:(NSRect) rect {
|
|
NSOpenGLContext *currentContext;
|
|
long params[] = { 1 };
|
|
|
|
if (fullscreen) {
|
|
currentContext = fullScreenContext;
|
|
} else {
|
|
currentContext =[self openGLContext];
|
|
}
|
|
[currentContext makeCurrentContext];
|
|
|
|
CGLSetParameter (CGLGetCurrentContext (), kCGLCPSwapInterval, params);
|
|
|
|
/* Black background */
|
|
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
|
|
if (!initDone) {
|
|
[[self openGLContext] flushBuffer];
|
|
return;
|
|
}
|
|
|
|
/* Draw */
|
|
glBindTexture (GL_TEXTURE_RECTANGLE_EXT, pi_texture); // FIXME
|
|
[self drawQuad];
|
|
/* Draw */
|
|
[currentContext flushBuffer];
|
|
}
|
|
|
|
- (void) displayTexture {
|
|
if ([self lockFocusIfCanDraw]) {
|
|
|
|
[self drawRect:[self bounds]];
|
|
[self reloadTexture];
|
|
|
|
[self unlockFocus];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
- (char *) getTextureBuffer {
|
|
return data;
|
|
}
|
|
|
|
- (void) setFullScreen:(BOOL) flag {
|
|
if (!fullscreen && flag) {
|
|
// go to full screen
|
|
/* Create the new pixel format */
|
|
NSOpenGLPixelFormat *fmt;
|
|
NSOpenGLPixelFormatAttribute attribs[] = {
|
|
NSOpenGLPFAAccelerated,
|
|
NSOpenGLPFANoRecovery,
|
|
NSOpenGLPFADoubleBuffer,
|
|
NSOpenGLPFAColorSize, 24,
|
|
NSOpenGLPFAAlphaSize, 8,
|
|
NSOpenGLPFADepthSize, 24,
|
|
NSOpenGLPFAFullScreen,
|
|
NSOpenGLPFAScreenMask,
|
|
CGDisplayIDToOpenGLDisplayMask (kCGDirectMainDisplay),
|
|
0
|
|
};
|
|
|
|
fmt = [[NSOpenGLPixelFormat alloc]
|
|
initWithAttributes:attribs];
|
|
|
|
if (!fmt) {
|
|
GST_WARNING ("Cannot create NSOpenGLPixelFormat");
|
|
return;
|
|
}
|
|
|
|
/* Create the new OpenGL context */
|
|
fullScreenContext = [[NSOpenGLContext alloc]
|
|
initWithFormat: fmt shareContext:nil];
|
|
if (!fullScreenContext) {
|
|
GST_WARNING ("Failed to create new NSOpenGLContext");
|
|
return;
|
|
}
|
|
|
|
/* Capture display, switch to fullscreen */
|
|
if (CGCaptureAllDisplays () != CGDisplayNoErr) {
|
|
GST_WARNING ("CGCaptureAllDisplays() failed");
|
|
return;
|
|
}
|
|
[fullScreenContext setFullScreen];
|
|
[fullScreenContext makeCurrentContext];
|
|
|
|
fullscreen = YES;
|
|
|
|
[self initTextures];
|
|
[self setNeedsDisplay:YES];
|
|
|
|
} else if (fullscreen && !flag) {
|
|
// fullscreen now and needs to go back to normal
|
|
initDone = NO;
|
|
[NSOpenGLContext clearCurrentContext];
|
|
|
|
CGReleaseAllDisplays ();
|
|
|
|
[self reshape];
|
|
[self initTextures];
|
|
|
|
[self setNeedsDisplay:YES];
|
|
|
|
fullscreen = NO;
|
|
initDone = YES;
|
|
}
|
|
}
|
|
|
|
- (void) setVideoSize: (int) w:(int) h {
|
|
GST_LOG ("width:%d, height:%d", w, h);
|
|
|
|
width = w;
|
|
height = h;
|
|
|
|
// FIXME : so, do we free, or don't we ?
|
|
//if (data) g_free(data);
|
|
|
|
data = g_malloc0 (2 * w * h);
|
|
[self reloadTexture];
|
|
}
|
|
|
|
@end
|