From 0668168a110e37555ee69d3a79b157862529ba7f Mon Sep 17 00:00:00 2001 From: Kateryna Kostiuk <kateryna.kostiuk@savoirfairelinux.com> Date: Thu, 7 May 2020 20:50:56 -0400 Subject: [PATCH] video: use openGL when metal is not available Change-Id: Ic2d19d19da72b71f73f12d3efab75c9bb384d2fb --- CMakeLists.txt | 3 + src/CurrentCallVC.mm | 84 ++++++++++---------- src/RecordFileVC.mm | 8 +- src/VideoCommon.mm | 25 +++++- src/VideoPrefsVC.mm | 16 ++-- src/views/CallLayer.h | 6 +- src/views/CallLayer.mm | 140 ++++++++++++++++++++++++++------- src/views/CallMTKView.h | 7 +- src/views/CallMTKView.mm | 3 +- src/views/RenderingView.h | 26 ++++++ src/views/RenderingView.mm | 89 +++++++++++++++++++++ src/views/VideoRendering.h | 29 +++++++ ui/Base.lproj/CurrentCall.xib | 38 ++++----- ui/Base.lproj/RecordFileVC.xib | 13 +-- ui/Base.lproj/VideoPrefs.xib | 12 +-- 15 files changed, 374 insertions(+), 125 deletions(-) create mode 100644 src/views/RenderingView.h create mode 100644 src/views/RenderingView.mm create mode 100644 src/views/VideoRendering.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 4033aa0b..1e4ae52f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -234,6 +234,9 @@ SET(ringclient_VIEWS src/views/GradientView.mm src/views/MovableView.h src/views/MovableView.mm + src/views/RenderingView.h + src/views/RenderingView.mm + src/views/VideoRendering.h ) SET(ringclient_OTHERS diff --git a/src/CurrentCallVC.mm b/src/CurrentCallVC.mm index c51af7ac..1316ea86 100644 --- a/src/CurrentCallVC.mm +++ b/src/CurrentCallVC.mm @@ -51,6 +51,7 @@ extern "C" { #import "VideoCommon.h" #import "views/GradientView.h" #import "views/MovableView.h" +#import "views/RenderingView.h" @interface CurrentCallVC () <NSPopoverDelegate> { QString convUid_; @@ -96,12 +97,12 @@ extern "C" { // Video @property (unsafe_unretained) IBOutlet CallView *videoView; -@property (unsafe_unretained) IBOutlet CallMTKView *previewView; +@property (unsafe_unretained) IBOutlet RenderingView *previewView; @property (unsafe_unretained) IBOutlet MovableView *movableBaseForView; @property (unsafe_unretained) IBOutlet NSView* hidePreviewBackground; @property (unsafe_unretained) IBOutlet NSButton* hidePreviewButton; -@property (unsafe_unretained) IBOutlet CallMTKView *videoMTKView; +@property (unsafe_unretained) IBOutlet RenderingView *distantView; @property RendererConnectionsHolder* renderConnections; @property QMetaObject::Connection videoStarted; @@ -373,13 +374,13 @@ CVPixelBufferRef pixelBufferPreview; [headerGradientView setHidden:YES]; [controlsPanel setHidden:YES]; [controlsStackView setHidden:YES]; - [self.videoMTKView fillWithBlack]; + [self.distantView fillWithBlack]; [self.previewView fillWithBlack]; [hidePreviewBackground setHidden:YES]; [self.previewView setHidden: YES]; - [self.videoMTKView setHidden: YES]; - self.previewView.stopRendering = true; - self.videoMTKView.stopRendering = true; + [self.distantView setHidden: YES]; + self.previewView.videoRunning = NO; + self.distantView.videoRunning = NO; [backgroundImage setHidden:NO]; [bluerBackgroundEffect setHidden:NO]; break; @@ -392,13 +393,13 @@ CVPixelBufferRef pixelBufferPreview; [backgroundImage setHidden:NO]; [bluerBackgroundEffect setHidden:NO]; if(!currentCall.isAudioOnly) { - [self.videoMTKView fillWithBlack]; + [self.distantView fillWithBlack]; [self.previewView fillWithBlack]; [hidePreviewBackground setHidden:YES]; [self.previewView setHidden: YES]; - [self.videoMTKView setHidden: YES]; - self.previewView.stopRendering = true; - self.videoMTKView.stopRendering = true; + [self.distantView setHidden: YES]; + self.previewView.videoRunning = NO; + self.distantView.videoRunning = NO; } break; case Status::INACTIVE: @@ -436,19 +437,19 @@ CVPixelBufferRef pixelBufferPreview; -(void) setUpVideoCallView { [previewView fillWithBlack]; - [self.videoMTKView fillWithBlack]; + [self.distantView fillWithBlack]; [previewView setHidden: NO]; - [self.videoMTKView setHidden:NO]; - [hidePreviewBackground setHidden: self.previewView.stopRendering]; + [self.distantView setHidden:NO]; + [hidePreviewBackground setHidden: !self.previewView.videoRunning]; [bluerBackgroundEffect setHidden:YES]; [backgroundImage setHidden:YES]; - self.previewView.stopRendering = false; - self.videoMTKView.stopRendering = false; + self.previewView.videoRunning = true; + self.distantView.videoRunning = true; } -(void) setUpAudioOnlyView { [self.previewView setHidden: YES]; - [self.videoMTKView setHidden: YES]; + [self.distantView setHidden: YES]; [hidePreviewBackground setHidden: YES]; [bluerBackgroundEffect setHidden:NO]; [backgroundImage setHidden:NO]; @@ -536,11 +537,11 @@ CVPixelBufferRef pixelBufferPreview; if (id == lrc::api::video::PREVIEW_RENDERER_ID) { [self.previewView setHidden:NO]; [hidePreviewBackground setHidden: NO]; - self.previewView.stopRendering = false; + self.previewView.videoRunning = true; } else if ([self isCurrentCall: id]) { [self mouseIsMoving: NO]; - self.videoMTKView.stopRendering = false; - [self.videoMTKView setHidden:NO]; + self.distantView.videoRunning = true; + [self.distantView setHidden:NO]; [bluerBackgroundEffect setHidden:YES]; [backgroundImage setHidden:YES]; } @@ -551,11 +552,11 @@ CVPixelBufferRef pixelBufferPreview; [=](const QString& id) { if (id == lrc::api::video::PREVIEW_RENDERER_ID) { [self.previewView setHidden:YES]; - self.previewView.stopRendering = true; + self.previewView.videoRunning = false; } else if ([self isCurrentCall: id]) { [self mouseIsMoving: YES]; - self.videoMTKView.stopRendering = true; - [self.videoMTKView setHidden:YES]; + self.distantView.videoRunning = false; + [self.distantView setHidden:YES]; [bluerBackgroundEffect setHidden:NO]; [backgroundImage setHidden:NO]; } @@ -569,46 +570,47 @@ CVPixelBufferRef pixelBufferPreview; if(!renderer->isRendering()) { return; } - [hidePreviewBackground setHidden: self.previewView.stopRendering]; - [self renderer: renderer renderFrameForPreviewView:previewView]; + [hidePreviewBackground setHidden: !self.previewView.videoRunning]; + [self rendererPreview: renderer]; } else if ([self isCurrentCall: id]) { auto renderer = &mediaModel->getRenderer(id); if(!renderer->isRendering()) { return; } - [self renderer:renderer renderFrameForDistantView: self.videoMTKView]; + [self rendererDistantView: renderer]; } }); } --(void) renderer: (const lrc::api::video::Renderer*)renderer renderFrameForPreviewView:(CallMTKView*) view -{ +-(void) rendererDistantView: (const lrc::api::video::Renderer*)renderer { @autoreleasepool { auto framePtr = renderer->currentAVFrame(); auto frame = framePtr.get(); - if(!frame || !frame->width || !frame->height) { + if(!frame || !frame->width || !frame->height) { return; } auto frameSize = CGSizeMake(frame->width, frame->height); auto rotation = 0; + if (auto matrix = av_frame_get_side_data(frame, AV_FRAME_DATA_DISPLAYMATRIX)) { + const int32_t* data = reinterpret_cast<int32_t*>(matrix->data); + rotation = av_display_rotation_get(data); + } if (frame->data[3] != NULL && (CVPixelBufferRef)frame->data[3]) { - [view renderWithPixelBuffer:(CVPixelBufferRef)frame->data[3] + [self.distantView renderWithPixelBuffer: (CVPixelBufferRef)frame->data[3] size: frameSize rotation: rotation - fillFrame: true]; - return; + fillFrame: false]; } - else if (CVPixelBufferRef pixelBuffer = [self getBufferForPreviewFromFrame:frame]) { - [view renderWithPixelBuffer: pixelBuffer + if (CVPixelBufferRef pixelBuffer = [self getBufferForDistantViewFromFrame:frame]) { + [self.distantView renderWithPixelBuffer: pixelBuffer size: frameSize rotation: rotation - fillFrame: true]; + fillFrame: false]; } } } --(void) renderer: (const lrc::api::video::Renderer*)renderer renderFrameForDistantView:(CallMTKView*) view -{ +-(void) rendererPreview: (const lrc::api::video::Renderer*)renderer { @autoreleasepool { auto framePtr = renderer->currentAVFrame(); auto frame = framePtr.get(); @@ -622,16 +624,16 @@ CVPixelBufferRef pixelBufferPreview; rotation = av_display_rotation_get(data); } if (frame->data[3] != NULL && (CVPixelBufferRef)frame->data[3]) { - [view renderWithPixelBuffer: (CVPixelBufferRef)frame->data[3] + [self.previewView renderWithPixelBuffer: (CVPixelBufferRef)frame->data[3] size: frameSize rotation: rotation fillFrame: false]; } - if (CVPixelBufferRef pixelBuffer = [self getBufferForDistantViewFromFrame:frame]) { - [view renderWithPixelBuffer: pixelBuffer + if (CVPixelBufferRef pixelBuffer = [self getBufferForPreviewFromFrame:frame]) { + [self.previewView renderWithPixelBuffer: pixelBuffer size: frameSize rotation: rotation - fillFrame: false]; + fillFrame: true]; } } } @@ -700,7 +702,7 @@ CVPixelBufferRef pixelBufferPreview; [backgroundImage setHidden:NO]; backgroundImage.layer.contents = nil; [self.previewView setHidden:YES]; - [self.videoMTKView setHidden:YES]; + [self.distantView setHidden:YES]; contactNameLabel.textColor = [NSColor highlightColor]; contactNameLabel.textColor = [NSColor highlightColor]; diff --git a/src/RecordFileVC.mm b/src/RecordFileVC.mm index 9a09b6d1..edd1ce6c 100644 --- a/src/RecordFileVC.mm +++ b/src/RecordFileVC.mm @@ -21,8 +21,8 @@ #import "AppDelegate.h" #import "VideoCommon.h" #import "views/HoverButton.h" -#import "views/CallMTKView.h" #import "views/NSColor+RingTheme.h" +#import "views/RenderingView.h" #import "NSString+Extensions.h" //lrc @@ -32,7 +32,7 @@ #import <AVFoundation/AVFoundation.h> @interface RecordFileVC () -@property (unsafe_unretained) IBOutlet CallMTKView* previewView; +@property (unsafe_unretained) IBOutlet RenderingView* previewView; @property (unsafe_unretained) IBOutlet NSTextField* timeLabel; @property (unsafe_unretained) IBOutlet NSTextField* infoLabel; @@ -106,7 +106,7 @@ previewView, timeLabel, recordOnOffButton, sendButton, fileImage, infoLabel, tim #pragma mark - dispaly --(void) renderer: (const lrc::api::video::Renderer*)renderer renderFrameForView:(CallMTKView*) view +-(void) renderer: (const lrc::api::video::Renderer*)renderer renderFrameForView:(RenderingView*) view { @autoreleasepool { const CGSize frameSize = [VideoCommon fillPixelBuffr:&pixBuf @@ -270,7 +270,7 @@ previewView, timeLabel, recordOnOffButton, sendButton, fileImage, infoLabel, tim } [previewView fillWithBlack]; - self.previewView.stopRendering = false; + self.previewView.videoRunning = true; [self connectPreviewSignals]; avModel->stopPreview(); avModel->startPreview(); diff --git a/src/VideoCommon.mm b/src/VideoCommon.mm index 5e3be633..70ff004a 100644 --- a/src/VideoCommon.mm +++ b/src/VideoCommon.mm @@ -122,10 +122,27 @@ extern "C" { return; } base = static_cast<uint8_t*>(CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 1)); - for(size_t i = 0; i < frame->height / 2 * bytesPerRowUV / 2; i++ ){ - *base++ = frame->data[1][i]; - *base++ = frame->data[2][i]; - } + if (bytesPerRowUV == frame->linesize[1] * 2) { + for(size_t i = 0; i < frame->height / 2 * bytesPerRowUV / 2; i++ ) { + *base++ = frame->data[1][i]; + *base++ = frame->data[2][i]; + } + } else { + uint32_t size = frame->linesize[1] * frame->height / 2; + uint8_t* dstData = new uint8_t[2 * size]; + for (int i = 0; i < 2 * size; i++){ + if (i % 2 == 0){ + dstData[i] = frame->data[1][i/2]; + }else { + dstData[i] = frame->data[2][i/2]; + } + } + [VideoCommon copyLineByLineSrc: dstData + toDest: base + srcLinesize: frame->linesize[1] * 2 + destLinesize: bytesPerRowUV + height: frame->height/2]; + } CVPixelBufferUnlockBaseAddress(pixelBuffer, 0); } diff --git a/src/VideoPrefsVC.mm b/src/VideoPrefsVC.mm index ff0680de..ed9739a0 100644 --- a/src/VideoPrefsVC.mm +++ b/src/VideoPrefsVC.mm @@ -18,7 +18,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #import "VideoPrefsVC.h" -#import "views/CallMTKView.h" +#import "views/RenderingView.h" #import "AppDelegate.h" #import "VideoCommon.h" @@ -36,7 +36,7 @@ extern "C" { @interface VideoPrefsVC () -@property IBOutlet CallMTKView* previewView; +@property IBOutlet RenderingView* previewView; @property (assign) IBOutlet NSPopUpButton* videoDevicesList; @property (assign) IBOutlet NSPopUpButton* sizesList; @property (assign) IBOutlet NSPopUpButton* ratesList; @@ -85,7 +85,7 @@ QString currentVideoDevice; QObject::disconnect(deviceEvent); AppDelegate* appDelegate = (AppDelegate *)[[NSApplication sharedApplication] delegate]; if (![appDelegate getActiveCalls].size()) { - self.previewView.stopRendering = true; + self.previewView.videoRunning = false; avModel->stopPreview(); [previewView fillWithBlack]; } @@ -162,7 +162,7 @@ QString currentVideoDevice; if (id != lrc::api::video::PREVIEW_RENDERER_ID) { return; } - self.previewView.stopRendering = false; + self.previewView.videoRunning = true; QObject::disconnect(frameUpdated); QObject::disconnect(previewStarted); QObject::disconnect(previewStopped); @@ -187,7 +187,7 @@ QString currentVideoDevice; ::PREVIEW_RENDERER_ID) { return; } - self.previewView.stopRendering = true; + self.previewView.videoRunning = false; QObject::disconnect(previewStopped); QObject::disconnect(frameUpdated); }); @@ -203,7 +203,7 @@ QString currentVideoDevice; bool updatePreview = avModel->getRenderer(lrc::api ::video::PREVIEW_RENDERER_ID).isRendering() && (defaultDevice != currentVideoDevice); if (updatePreview) { [previewView fillWithBlack]; - self.previewView.stopRendering = true; + self.previewView.videoRunning = false; [self startPreview]; } [self addDevices]; @@ -212,7 +212,7 @@ QString currentVideoDevice; #pragma mark - dispaly --(void) renderer: (const lrc::api::video::Renderer*)renderer renderFrameForView:(CallMTKView*) view +-(void) renderer: (const lrc::api::video::Renderer*)renderer renderFrameForView:(RenderingView*) view { @autoreleasepool { auto framePtr = renderer->currentAVFrame(); @@ -272,7 +272,7 @@ QString currentVideoDevice; AppDelegate* appDelegate = (AppDelegate *)[[NSApplication sharedApplication] delegate]; auto calls = [appDelegate getActiveCalls]; if (calls.empty()) { - self.previewView.stopRendering = false; + self.previewView.videoRunning = true; [self connectPreviewSignals]; avModel->stopPreview(); avModel->startPreview(); diff --git a/src/views/CallLayer.h b/src/views/CallLayer.h index 86d21c79..f604e29e 100644 --- a/src/views/CallLayer.h +++ b/src/views/CallLayer.h @@ -20,11 +20,9 @@ #import <Cocoa/Cocoa.h> #import <QSize> #import <video/renderer.h> +#import "VideoRendering.h" -@interface CallLayer : NSOpenGLLayer +@interface CallLayer : NSOpenGLLayer <VideoRendering> -@property BOOL videoRunning; - -- (void) setCurrentFrame:(Video::Frame)framePtr; @end diff --git a/src/views/CallLayer.mm b/src/views/CallLayer.mm index 3dd5fd32..4e03fa9d 100644 --- a/src/views/CallLayer.mm +++ b/src/views/CallLayer.mm @@ -26,13 +26,14 @@ static const GLchar* vShaderSrc = R"glsl( in vec2 in_Pos; in vec2 in_TexCoord; uniform vec2 in_Scaling; +uniform mat4 in_rotationMatrix; out vec2 texCoord; void main() { texCoord = in_TexCoord; - gl_Position = vec4(in_Pos.x*in_Scaling.x, in_Pos.y*in_Scaling.y, 0.0, 1.0); + gl_Position = in_rotationMatrix * vec4(in_Pos.x*in_Scaling.x, in_Pos.y*in_Scaling.y, 0.0, 1.0); } )glsl"; @@ -42,23 +43,37 @@ static const GLchar* fShaderSrc = R"glsl( out vec4 fragColor; in vec2 texCoord; -uniform sampler2D tex; +uniform sampler2D tex_y, tex_uv; void main() { - fragColor = texture(tex, texCoord); + mediump vec3 yuv, rgb; + yuv.x = (texture(tex_y, texCoord).r); + yuv.yz = (texture(tex_uv, texCoord).rg - vec2(0.5, 0.5)); + rgb = mat3( 1, 1, 1, + 0, -0.3441, 1.7720, + 1.4020, -0.7141, 0) * yuv; + fragColor = vec4(rgb, 1); } )glsl"; +@interface CallLayer() + +@property BOOL currentFrameDisplayed; +@property NSLock* currentFrameLk; +@property CGFloat currentWidth; +@property CGFloat currentHeight; +@property CGFloat currentAngle; +@property CVPixelBufferRef currentFrame; + +@end + @implementation CallLayer // OpenGL handlers -GLuint tex, vbo, vShader, fShader, sProg, vao; +GLuint textureY, textureUV, textureUniformY, textureUniformUV, vbo, vShader, fShader, sProg, vao; -// Last frame data and attributes -Video::Frame currentFrame; -BOOL currentFrameDisplayed; -NSLock* currentFrameLk; +@synthesize currentAngle, currentFrameDisplayed, currentFrameLk, currentWidth, currentHeight, currentFrame, videoRunning; - (id) init { @@ -113,6 +128,9 @@ NSLock* currentFrameLk; glLinkProgram(sProg); glUseProgram(sProg); + textureUniformY = glGetUniformLocation(sProg, "tex_y"); + textureUniformUV = glGetUniformLocation(sProg, "tex_uv"); + // Vertices position attrib GLuint inPosAttrib = glGetAttribLocation(sProg, "in_Pos"); glEnableVertexAttribArray(inPosAttrib); @@ -122,10 +140,19 @@ NSLock* currentFrameLk; GLuint inTexCoordAttrib = glGetAttribLocation(sProg, "in_TexCoord"); glEnableVertexAttribArray(inTexCoordAttrib); glVertexAttribPointer(inTexCoordAttrib, 2, GL_FLOAT, GL_FALSE, 4*sizeof(GLfloat), (void*)(2*sizeof(GLfloat))); + // TextureY + glActiveTexture(GL_TEXTURE0); + glGenTextures(1, &textureY); + glBindTexture(GL_TEXTURE_2D, textureY); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); - // Texture - glGenTextures(1, &tex); - glBindTexture(GL_TEXTURE_2D, tex); + // TextureUV + glActiveTexture(GL_TEXTURE1); + glGenTextures(1, &textureUV); + glBindTexture(GL_TEXTURE_2D, textureUV); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); @@ -161,25 +188,35 @@ NSLock* currentFrameLk; - (void)drawInOpenGLContext:(NSOpenGLContext *)context pixelFormat:(NSOpenGLPixelFormat *)pixelFormat forLayerTime:(CFTimeInterval)t displayTime:(const CVTimeStamp *)ts { GLenum errEnum; - glBindTexture(GL_TEXTURE_2D, tex); - [currentFrameLk lock]; - if(!currentFrameDisplayed) { - if(currentFrame.ptr) { - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, currentFrame.width, currentFrame.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, currentFrame.ptr); - } + if(!currentFrameDisplayed && currentFrame) { + CVPixelBufferLockBaseAddress(currentFrame, 0); + [self configureTexture:textureY + index:0 + uniform:textureUniformY + activeTexture:GL_TEXTURE0 + format:GL_RED + fullPlane: YES]; + [self configureTexture:textureUV + index:1 + uniform:textureUniformUV + activeTexture:GL_TEXTURE1 + format:GL_RG + fullPlane: NO]; + CVPixelBufferUnlockBaseAddress(currentFrame, 0); + CVPixelBufferRelease(currentFrame); currentFrameDisplayed = YES; } // To ensure that we will not divide by zero - if (currentFrame.ptr && currentFrame.width && currentFrame.height) { + if (currentFrame && currentWidth && currentHeight) { // Compute scaling factor to keep the original aspect ratio of the video CGSize viewSize = self.frame.size; - float viewRatio = viewSize.width/viewSize.height; - float frameRatio = ((float)currentFrame.width)/((float)currentFrame.height); + float viewRatio = (currentAngle == 90 || currentAngle == -90) ? + viewSize.height/viewSize.width : viewSize.width/viewSize.height; + float frameRatio = ((float)currentWidth)/((float)currentHeight); float ratio = viewRatio * (1/frameRatio); GLint inScalingUniform = glGetUniformLocation(sProg, "in_Scaling"); - float multiplier = MAX(frameRatio, ratio); if((viewRatio >= 1 && frameRatio >= 1) || (viewRatio < 1 && frameRatio < 1) || @@ -193,24 +230,73 @@ NSLock* currentFrameLk; glUniform2f(inScalingUniform, 1.0, 1.0 * ratio); else glUniform2f(inScalingUniform, 1.0/ratio, 1.0); - } } - [currentFrameLk unlock]; - glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + // apply rotation + float radians = (currentAngle * M_PI) / 180; + float rotation[16] = + { + cos(radians), -sin(radians), 0.0f, 0.0f, + sin(radians), cos(radians), 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f + }; + GLint location = glGetUniformLocation(sProg, "in_rotationMatrix"); + glUniformMatrix4fv(location, 1, GL_FALSE, rotation); + + [currentFrameLk unlock]; + glClearColor(0.0f, 0.0f, 0.0f, 0.1f); glClear(GL_COLOR_BUFFER_BIT); if([self videoRunning]) glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); } -- (void) setCurrentFrame:(Video::Frame)framePtr -{ +-(void)configureTexture:(GLuint)texture index:(int)index + uniform:(GLuint)uniform + activeTexture:(GLenum)activeTexture + format:(GLint)format + fullPlane:(BOOL)fullPlane { + auto plane = CVPixelBufferGetBaseAddressOfPlane(currentFrame, index); + auto width = CVPixelBufferGetWidthOfPlane(currentFrame, index); + auto height = CVPixelBufferGetHeightOfPlane(currentFrame, index); + auto strideWidth = CVPixelBufferGetBytesPerRowOfPlane(currentFrame, index); + strideWidth = fullPlane ? strideWidth : strideWidth * 0.5; + if(strideWidth > width) { + glPixelStorei(GL_UNPACK_ROW_LENGTH, strideWidth); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + } else { + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + glPixelStorei(GL_UNPACK_ALIGNMENT, 0); + } + glActiveTexture(activeTexture); + glBindTexture(GL_TEXTURE_2D, texture); + glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, plane); + glUniform1i(uniform, index); +} + +-(void)fillWithBlack { [currentFrameLk lock]; - currentFrame = std::move(framePtr); + if(currentFrame) { + currentFrame = nullptr; + currentFrameDisplayed = YES; + } + [currentFrameLk unlock]; +} + +-(void)renderWithPixelBuffer:(CVPixelBufferRef)buffer size:(CGSize)size rotation: (float)rotation fillFrame: (bool)fill { + [currentFrameLk lock]; + currentFrame = buffer; + CFRetain(currentFrame); + currentWidth = size.width; + currentHeight = size.height; + currentAngle = rotation; currentFrameDisplayed = NO; + videoRunning = YES; [currentFrameLk unlock]; } +-(void)setupView { +} @end diff --git a/src/views/CallMTKView.h b/src/views/CallMTKView.h index edf09d57..ab432a43 100644 --- a/src/views/CallMTKView.h +++ b/src/views/CallMTKView.h @@ -19,10 +19,7 @@ #import <Cocoa/Cocoa.h> #import <MetalKit/MetalKit.h> +#import "VideoRendering.h" -@interface CallMTKView: MTKView --(void)renderWithPixelBuffer:(CVPixelBufferRef)buffer size:(CGSize)size rotation: (float)rotation fillFrame: (bool)fill; --(void)fillWithBlack; --(void)setupView; -@property bool stopRendering; +@interface CallMTKView: MTKView <VideoRendering> @end diff --git a/src/views/CallMTKView.mm b/src/views/CallMTKView.mm index b49dd466..98d0afca 100644 --- a/src/views/CallMTKView.mm +++ b/src/views/CallMTKView.mm @@ -48,6 +48,7 @@ struct Uniforms { simd::float4x4 projectionMatrix; simd::float4x4 rotationMatrix; }; +@synthesize videoRunning; - (instancetype)initWithFrame:(NSRect)frame { @@ -151,7 +152,7 @@ bool frameDisplayed = false; if(frameDisplayed) { return; } - if(_stopRendering) { + if(!self.videoRunning) { self.releaseDrawables; return; } diff --git a/src/views/RenderingView.h b/src/views/RenderingView.h new file mode 100644 index 00000000..1be755f2 --- /dev/null +++ b/src/views/RenderingView.h @@ -0,0 +1,26 @@ +/* +* Copyright (C) 2020 Savoir-faire Linux Inc. +* Author: Kateryna Kostiuk <kateryna.kostiuk@savoirfairelinux.com> +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 3 of the License, or +* (at your option) any later version. +* +* This program 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 General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#import <Cocoa/Cocoa.h> +#import "VideoRendering.h" + +@interface RenderingView : NSView <VideoRendering> + +@end + diff --git a/src/views/RenderingView.mm b/src/views/RenderingView.mm new file mode 100644 index 00000000..24d80064 --- /dev/null +++ b/src/views/RenderingView.mm @@ -0,0 +1,89 @@ +/* +* Copyright (C) 2020 Savoir-faire Linux Inc. +* Author: Kateryna Kostiuk <kateryna.kostiuk@savoirfairelinux.com> +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 3 of the License, or +* (at your option) any later version. +* +* This program 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 General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ +#import "RenderingView.h" +#import "src/utils.h" +#import "CallMTKView.h" +#import "CallLayer.h" + +@interface RenderingView() + +@property id <VideoRendering> renderer; + +@end + +@implementation RenderingView +@synthesize videoRunning, renderer; + +- (instancetype)initWithFrame:(NSRect)frame { + self = [super initWithFrame:frame]; + [self commonInit]; + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)coder { + self = [super initWithCoder:coder]; + [self commonInit]; + return self; +} + +-(void)commonInit { + if ([self metalSupported]) { + renderer = [[CallMTKView alloc] initWithFrame:self.frame]; + NSView* renderView = (NSView*)renderer; + [self addSubview: renderView]; + renderView.translatesAutoresizingMaskIntoConstraints = true; + [renderView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; + [renderView.topAnchor constraintEqualToAnchor:self.topAnchor constant:0].active = YES; + [renderView.bottomAnchor constraintEqualToAnchor:self.bottomAnchor constant:0].active = YES; + [renderView.leadingAnchor constraintEqualToAnchor:self.leadingAnchor constant:0].active = YES; + [renderView.trailingAnchor constraintEqualToAnchor:self.trailingAnchor constant:0].active = YES; + } else { + [self setLayer:[[CallLayer alloc] init]]; + [self setWantsLayer:true]; + CallLayer* callLayer = (CallLayer*)self.layer; + renderer = callLayer; + } +} + +-(void)renderWithPixelBuffer:(CVPixelBufferRef)buffer size:(CGSize)size rotation: (float)rotation fillFrame: (bool)fill { + [renderer renderWithPixelBuffer:buffer size:size rotation:rotation fillFrame:fill]; +} + +-(void)fillWithBlack { + [renderer fillWithBlack]; + [self.layer setBackgroundColor:NSColor.blackColor.CGColor]; +} + +-(void)setupView { + [renderer setupView]; +} + +-(void)setVideoRunning:(BOOL)running { + // for opengl video running set when new frame received + if ([self metalSupported] || !running) { + renderer.videoRunning = running; + } + videoRunning = running; +} + +-(BOOL)metalSupported { + return MTLCreateSystemDefaultDevice() != nil; +} + +@end diff --git a/src/views/VideoRendering.h b/src/views/VideoRendering.h new file mode 100644 index 00000000..cef76e7a --- /dev/null +++ b/src/views/VideoRendering.h @@ -0,0 +1,29 @@ +/* +* Copyright (C) 2020 Savoir-faire Linux Inc. +* Author: Kateryna Kostiuk <kateryna.kostiuk@savoirfairelinux.com> +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 3 of the License, or +* (at your option) any later version. +* +* This program 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 General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#import <Foundation/Foundation.h> +#import <video/renderer.h> +@protocol VideoRendering +-(void)renderWithPixelBuffer:(CVPixelBufferRef)buffer size:(CGSize)size rotation: (float)rotation fillFrame: (bool)fill; +-(void)fillWithBlack; +-(void)setupView; +@property BOOL videoRunning; + +@end + diff --git a/ui/Base.lproj/CurrentCall.xib b/ui/Base.lproj/CurrentCall.xib index 92e85a5f..df09647e 100644 --- a/ui/Base.lproj/CurrentCall.xib +++ b/ui/Base.lproj/CurrentCall.xib @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8"?> -<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES"> +<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="15705" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES"> <dependencies> - <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14490.70"/> + <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="15705"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> </dependencies> <objects> @@ -19,6 +19,7 @@ <outlet property="contactPhoto" destination="z3E-kv-Uwb" id="otV-zW-LCQ"/> <outlet property="controlsPanel" destination="wDi-X6-dgL" id="zs8-jw-wQ3"/> <outlet property="controlsStackView" destination="Djs-L8-8Pe" id="Nsi-pe-3uh"/> + <outlet property="distantView" destination="gQE-fN-JhY" id="ARl-xd-Cfc"/> <outlet property="hangUpButton" destination="Kjq-iM-NBL" id="Puz-4L-Okl"/> <outlet property="headerContainer" destination="DfH-di-xs7" id="gAV-d7-fNh"/> <outlet property="headerGradientView" destination="d0X-cW-Xgz" id="5l9-Sl-TSk"/> @@ -31,11 +32,10 @@ <outlet property="muteAudioButton" destination="tQl-cT-0Lb" id="qV4-Ef-UTx"/> <outlet property="muteVideoButton" destination="LVS-yZ-98V" id="qQs-zP-wQ4"/> <outlet property="pickUpButton" destination="qgD-3D-nD5" id="eu2-aU-twv"/> - <outlet property="previewView" destination="6y6-RH-qOp" id="1PY-sd-mh4"/> + <outlet property="previewView" destination="6y6-RH-qOp" id="odr-bq-Wdg"/> <outlet property="recordOnOffButton" destination="QYT-0n-4sw" id="LpC-8i-BGz"/> <outlet property="splitView" destination="GIJ-gB-FZo" id="PM0-az-Q8X"/> <outlet property="timeSpentLabel" destination="dl1-Gt-oz5" id="jdk-Ky-BG6"/> - <outlet property="videoMTKView" destination="gQE-fN-JhY" id="qca-wq-idt"/> <outlet property="videoView" destination="2wf-Py-l6B" id="dEF-Gx-w6x"/> <outlet property="view" destination="Hz6-mo-xeY" id="VKn-lN-ijP"/> </connections> @@ -65,10 +65,10 @@ <color key="fillColor" red="0.120510533452034" green="0.12050692737102509" blue="0.12050899863243103" alpha="1" colorSpace="calibratedRGB"/> </box> <stackView distribution="fill" orientation="vertical" alignment="centerX" spacing="10" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" translatesAutoresizingMaskIntoConstraints="NO" id="aLB-LA-yn0"> - <rect key="frame" x="302" y="249" width="142" height="308"/> + <rect key="frame" x="302" y="250" width="142" height="305"/> <subviews> <customView translatesAutoresizingMaskIntoConstraints="NO" id="s4L-Ke-9Jm"> - <rect key="frame" x="7" y="180" width="128" height="128"/> + <rect key="frame" x="7" y="177" width="128" height="128"/> <subviews> <imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="z3E-kv-Uwb" userLabel="contactPhoto"> <rect key="frame" x="4" y="4" width="120" height="120"/> @@ -96,7 +96,7 @@ </constraints> </customView> <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Q9v-1a-PP9" userLabel="contactName"> - <rect key="frame" x="12" y="148" width="118" height="22"/> + <rect key="frame" x="12" y="146" width="118" height="21"/> <constraints> <constraint firstAttribute="width" relation="greaterThanOrEqual" constant="81" id="tvn-e9-GfK"/> </constraints> @@ -107,7 +107,7 @@ </textFieldCell> </textField> <textField verticalHuggingPriority="750" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Qq2-Et-gep" userLabel="contactID"> - <rect key="frame" x="39" y="121" width="65" height="17"/> + <rect key="frame" x="39" y="120" width="65" height="16"/> <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" state="on" alignment="center" title="person Id" id="hKi-Ay-ex5"> <font key="font" metaFont="systemLight" size="13"/> <color key="textColor" name="selectedMenuItemTextColor" catalog="System" colorSpace="catalog"/> @@ -115,14 +115,14 @@ </textFieldCell> </textField> <customView translatesAutoresizingMaskIntoConstraints="NO" id="Wps-oy-aXf"> - <rect key="frame" x="69" y="106" width="5" height="5"/> + <rect key="frame" x="69" y="105" width="5" height="5"/> <constraints> <constraint firstAttribute="height" constant="5" id="2gw-nj-4KM"/> <constraint firstAttribute="width" constant="5" id="BNy-PR-qtu"/> </constraints> </customView> <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Ff6-F4-zbn" userLabel="callState"> - <rect key="frame" x="50" y="78" width="42" height="18"/> + <rect key="frame" x="50" y="78" width="42" height="17"/> <constraints> <constraint firstAttribute="width" relation="greaterThanOrEqual" constant="29" id="tka-qK-aZu"/> </constraints> @@ -222,14 +222,14 @@ <real value="3.4028234663852886e+38"/> </customSpacing> </stackView> - <customView translatesAutoresizingMaskIntoConstraints="NO" id="gQE-fN-JhY" customClass="CallMTKView"> + <customView translatesAutoresizingMaskIntoConstraints="NO" id="gQE-fN-JhY" customClass="RenderingView"> <rect key="frame" x="0.0" y="0.0" width="746" height="805"/> </customView> <customView translatesAutoresizingMaskIntoConstraints="NO" id="d0X-cW-Xgz" customClass="GradientView"> - <rect key="frame" x="0.0" y="733" width="746" height="72"/> + <rect key="frame" x="0.0" y="734" width="746" height="71"/> <subviews> <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="cIU-M7-xpN"> - <rect key="frame" x="624" y="24" width="104" height="24"/> + <rect key="frame" x="624" y="24" width="104" height="23"/> <constraints> <constraint firstAttribute="width" constant="100" id="9vz-kb-6L6"/> </constraints> @@ -285,7 +285,7 @@ <rect key="frame" x="550" y="20" width="175" height="120"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <subviews> - <customView autoresizesSubviews="NO" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="6y6-RH-qOp" userLabel="Preview" customClass="CallMTKView"> + <customView autoresizesSubviews="NO" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="6y6-RH-qOp" userLabel="Preview" customClass="RenderingView"> <rect key="frame" x="0.0" y="0.0" width="175" height="120"/> <autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" heightSizable="YES" flexibleMaxY="YES"/> <subviews> @@ -321,10 +321,10 @@ </subviews> </customView> <customView translatesAutoresizingMaskIntoConstraints="NO" id="DfH-di-xs7" customClass="GradientView"> - <rect key="frame" x="0.0" y="733" width="746" height="72"/> + <rect key="frame" x="0.0" y="734" width="746" height="71"/> <subviews> <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="dl1-Gt-oz5"> - <rect key="frame" x="624" y="24" width="104" height="24"/> + <rect key="frame" x="624" y="24" width="104" height="23"/> <constraints> <constraint firstAttribute="width" constant="100" id="zSV-0S-PdC"/> </constraints> @@ -683,7 +683,7 @@ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <subviews> <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="IJm-DU-cs6"> - <rect key="frame" x="160" y="9" width="8" height="17"/> + <rect key="frame" x="160" y="9" width="8" height="16"/> <constraints> <constraint firstAttribute="width" priority="250" constant="20" id="HNl-gc-YBr"/> </constraints> @@ -750,7 +750,7 @@ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <subviews> <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="xkH-rw-k6w"> - <rect key="frame" x="160" y="9" width="8" height="17"/> + <rect key="frame" x="160" y="9" width="8" height="16"/> <constraints> <constraint firstAttribute="width" priority="250" constant="20" id="IIf-Z3-Q3T"/> </constraints> @@ -806,7 +806,7 @@ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <subviews> <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" tag="200" translatesAutoresizingMaskIntoConstraints="NO" id="SMB-Vk-E06"> - <rect key="frame" x="160" y="7" width="8" height="17"/> + <rect key="frame" x="160" y="7" width="8" height="16"/> <constraints> <constraint firstAttribute="width" priority="250" constant="20" id="Wod-ib-NVj"/> </constraints> diff --git a/ui/Base.lproj/RecordFileVC.xib b/ui/Base.lproj/RecordFileVC.xib index 82938aa8..39bcf0fb 100644 --- a/ui/Base.lproj/RecordFileVC.xib +++ b/ui/Base.lproj/RecordFileVC.xib @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8"?> -<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct"> +<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="15705" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct"> <dependencies> - <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14490.70"/> + <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="15705"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> </dependencies> <objects> @@ -26,11 +26,11 @@ <rect key="frame" x="0.0" y="0.0" width="480" height="272"/> <autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" heightSizable="YES" flexibleMaxY="YES"/> <subviews> - <customView translatesAutoresizingMaskIntoConstraints="NO" id="rRf-Sm-dXA" customClass="CallMTKView"> + <customView translatesAutoresizingMaskIntoConstraints="NO" id="rRf-Sm-dXA" customClass="RenderingView"> <rect key="frame" x="0.0" y="0.0" width="480" height="272"/> </customView> <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="MXV-85-UvC"> - <rect key="frame" x="165" y="73" width="151" height="17"/> + <rect key="frame" x="165" y="73" width="151" height="16"/> <textFieldCell key="cell" lineBreakMode="clipping" alignment="center" title="Press to start recording" id="Dp1-8c-of5"> <font key="font" usesAppearanceFont="YES"/> <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/> @@ -54,7 +54,7 @@ </userDefinedRuntimeAttributes> </button> <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="ZjD-ni-kiA"> - <rect key="frame" x="238" y="124" width="4" height="24"/> + <rect key="frame" x="238" y="125" width="4" height="23"/> <textFieldCell key="cell" lineBreakMode="clipping" id="VXB-jT-wv2"> <font key="font" metaFont="system" size="20"/> <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/> @@ -93,7 +93,7 @@ </connections> </button> <button hidden="YES" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="6Nv-0Z-3Lv"> - <rect key="frame" x="412" y="17" width="48" height="24"/> + <rect key="frame" x="412" y="18" width="48" height="23"/> <buttonCell key="cell" type="square" title="Send" bezelStyle="shadowlessSquare" alignment="center" inset="2" id="UXO-l1-POf"> <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/> <font key="font" metaFont="system" size="20"/> @@ -157,6 +157,7 @@ <constraint firstAttribute="trailing" secondItem="rRf-Sm-dXA" secondAttribute="trailing" id="r9d-DV-Kle"/> <constraint firstItem="rRf-Sm-dXA" firstAttribute="top" secondItem="Hz6-mo-xeY" secondAttribute="top" id="vl5-VY-oXf"/> </constraints> + <point key="canvasLocation" x="139" y="154"/> </customView> </objects> <resources> diff --git a/ui/Base.lproj/VideoPrefs.xib b/ui/Base.lproj/VideoPrefs.xib index fe370b8d..5f8e44e8 100644 --- a/ui/Base.lproj/VideoPrefs.xib +++ b/ui/Base.lproj/VideoPrefs.xib @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8"?> -<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="15505" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct"> +<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="15705" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct"> <dependencies> - <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="15505"/> + <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="15705"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> </dependencies> <objects> @@ -48,7 +48,7 @@ </constraints> <popUpButtonCell key="cell" type="push" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" id="6Vf-hb-26C"> <behavior key="behavior" lightByBackground="YES" lightByGray="YES"/> - <font key="font" metaFont="system"/> + <font key="font" metaFont="menu"/> <menu key="menu" id="zJ9-1a-cpr"> <connections> <outlet property="delegate" destination="-2" id="mF2-Zc-wNP"/> @@ -87,7 +87,7 @@ </constraints> <popUpButtonCell key="cell" type="push" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" id="6ly-dX-MT4"> <behavior key="behavior" lightByBackground="YES" lightByGray="YES"/> - <font key="font" metaFont="system"/> + <font key="font" metaFont="menu"/> <menu key="menu" id="ykd-50-las"> <connections> <outlet property="delegate" destination="-2" id="5cI-wH-0AT"/> @@ -126,7 +126,7 @@ </constraints> <popUpButtonCell key="cell" type="push" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" id="pDt-c4-Fhs"> <behavior key="behavior" lightByBackground="YES" lightByGray="YES"/> - <font key="font" metaFont="system"/> + <font key="font" metaFont="menu"/> <menu key="menu" id="aKL-76-vSP"> <connections> <outlet property="delegate" destination="-2" id="5Gu-bg-y3C"/> @@ -190,7 +190,7 @@ <real value="3.4028234663852886e+38"/> </customSpacing> </stackView> - <customView wantsLayer="YES" canDrawConcurrently="YES" translatesAutoresizingMaskIntoConstraints="NO" id="qAJ-w8-a6Q" userLabel="Preview" customClass="CallMTKView"> + <customView wantsLayer="YES" canDrawConcurrently="YES" translatesAutoresizingMaskIntoConstraints="NO" id="qAJ-w8-a6Q" userLabel="Preview" customClass="RenderingView"> <rect key="frame" x="0.0" y="0.0" width="470" height="256"/> <constraints> <constraint firstAttribute="width" constant="470" id="ipm-Al-OAC"/> -- GitLab