Commit 00dcbffa authored by Kateryna Kostiuk's avatar Kateryna Kostiuk

video: change rendering

- use metal framework for video view and preview rendering
- use avframe to get video data

Change-Id: I8faa6f2e8e529e875af207876aa7445f9712d630
parent 76744e40
......@@ -37,6 +37,8 @@ FIND_PACKAGE(OpenGL REQUIRED)
EXECUTE_PROCESS(COMMAND git submodule update --init
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
get_filename_component(PARENT_DIR ${CMAKE_SOURCE_DIR} PATH)
INCLUDE(ExternalProject)
ExternalProject_Add(libqrencode
GIT_SUBMODULES libqrencode
......@@ -52,6 +54,17 @@ ExternalProject_Add(libqrencode
INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/libqrencode/include)
LINK_DIRECTORIES(${CMAKE_SOURCE_DIR}/libqrencode/lib)
INCLUDE_DIRECTORIES(${PARENT_DIR}/daemon/contrib/native/ffmpeg)
set(SHADERS_FILE "Shader.metallib")
add_custom_command (OUTPUT ${CMAKE_SOURCE_DIR}/Shader.metallib
COMMAND ${CMAKE_SOURCE_DIR}/generateShaderLib.sh
COMMENT "Creating Shader.metallib")
add_custom_target(
shader ALL
DEPENDS ${CMAKE_SOURCE_DIR}/Shader.metallib
)
IF(NOT (${ENABLE_SPARKLE} MATCHES false))
MESSAGE("Sparkle auto-update enabled")
......@@ -212,6 +225,8 @@ SET(ringclient_VIEWS
src/views/HoverButton.mm
src/views/CenteredClipView.h
src/views/CenteredClipView.mm
src/views/CallMTKView.h
src/views/CallMTKView.mm
)
SET(ringclient_OTHERS
......@@ -229,8 +244,9 @@ SET(ringclient_OTHERS
src/NSString+Extensions.h
src/NSString+Extensions.mm
src/RingMainWindow.h
src/RingMainWindow.mm)
src/RingMainWindow.mm
src/Shader.metal
)
SET(ringclient_XIBS
MainMenu
......@@ -323,6 +339,8 @@ SET_SOURCE_FILES_PROPERTIES(${ring_ICONS} PROPERTIES
MACOSX_PACKAGE_LOCATION Resources)
SET_SOURCE_FILES_PROPERTIES(Credits.rtf PROPERTIES
MACOSX_PACKAGE_LOCATION Resources)
SET_SOURCE_FILES_PROPERTIES(Shader.metallib PROPERTIES
MACOSX_PACKAGE_LOCATION Resources)
# package ringtones
IF(NOT IS_DIRECTORY ${RINGTONE_DIR})
......@@ -405,6 +423,7 @@ SET(TO_ADD
${LOCALIZABLE_FILES}
${myApp_ICON}
Credits.rtf
Shader.metallib
${ring_ICONS}
${ring_RINGTONES})
......@@ -438,6 +457,8 @@ TARGET_LINK_LIBRARIES( ${PROJ_NAME}
-lqrencode
)
target_link_libraries(${PROJ_NAME} ${PARENT_DIR}/daemon/contrib/x86_64-apple-darwin${CMAKE_SYSTEM_VERSION}/lib/libavutil.a)
IF(ENABLE_SPARKLE)
TARGET_LINK_LIBRARIES(${PROJ_NAME} ${SPARKLE_FRAMEWORK})
ENDIF(ENABLE_SPARKLE)
......@@ -448,6 +469,8 @@ SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -framework Quartz")
SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -framework AVFoundation")
SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -framework AddressBook")
SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -framework SystemConfiguration")
SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -framework MetalKit")
SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -framework Metal")
# These variables are specific to our plist and are NOT standard CMake variables
SET(MACOSX_BUNDLE_NSMAIN_NIB_FILE "MainMenu")
......
xcrun -sdk macosx metal -c ../src/Shader.metal -o ../Shader.air
xcrun -sdk macosx metallib ../Shader.air -o ../Shader.metallib
This diff is collapsed.
......@@ -37,6 +37,7 @@
#import <api/contact.h>
#import <api/datatransfermodel.h>
#import <media/recordingmodel.h>
#import <api/avmodel.h>
// Ring
#import "AppDelegate.h"
......@@ -222,6 +223,7 @@ typedef NS_ENUM(NSInteger, ViewState) {
NSResponder * viewNextResponder = [self nextResponder];
[self setNextResponder: [conversationVC getMessagesView]];
[[conversationVC getMessagesView] setNextResponder: viewNextResponder];
self.avModel->useAVFrame(YES);
}
- (void) connect
......
/*
* Copyright (C) 2019 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.
*/
#include <metal_stdlib>
#include <simd/simd.h>
using namespace metal;
typedef enum VertexAttributes {
kVertexAttributePosition = 0,
kVertexAttributeTexcoord = 1,
} VertexAttributes;
typedef enum TextureIndices {
kTextureIndexColor = 0,
kTextureIndexY = 1,
kTextureIndexCbCr = 2
} TextureIndices;
typedef struct {
float2 position [[attribute(kVertexAttributePosition)]];
float2 texCoord [[attribute(kVertexAttributeTexcoord)]];
} ImageVertex;
typedef struct {
float4 position [[position]];
float2 texCoord;
} ImageColorInOut;
struct Uniforms {
float4x4 projectionMatrix;
float4x4 rotationMatrix;
};
vertex ImageColorInOut imageVertex(ImageVertex in [[stage_in]],
constant Uniforms &uniforms [[buffer(1)]]) {
ImageColorInOut out;
out.position = uniforms.rotationMatrix * uniforms.projectionMatrix * float4(in.position, 1.0);
out.texCoord = in.texCoord;
return out;
}
fragment float4 imageFragment(ImageColorInOut in [[stage_in]],
texture2d<float, access::sample> capturedImageTextureY [[ texture(kTextureIndexY) ]],
texture2d<float, access::sample> capturedImageTextureCbCr [[ texture(kTextureIndexCbCr) ]]) {
constexpr sampler colorSampler(mip_filter::linear, mag_filter::linear, min_filter::linear);
const float4x4 ycbcrToRGBTransform = float4x4(float4(+1.0000f, +1.0000f, +1.0000f, +0.0000f),
float4(+0.0000f, -0.3441f, +1.7720f, +0.0000f),
float4(+1.4020f, -0.7141f, +0.0000f, +0.0000f),
float4(-0.7010f, +0.5291f, -0.8860f, +1.0000f));
// Sample Y and CbCr textures to get the YCbCr color at the given texture coordinate
float4 ycbcr = float4(capturedImageTextureY.sample(colorSampler, in.texCoord).r,
capturedImageTextureCbCr.sample(colorSampler, in.texCoord).rg, 1.0);
return ycbcrToRGBTransform * ycbcr;
}
/*
* Copyright (C) 2019 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 <MetalKit/MetalKit.h>
@interface CallMTKView: MTKView
-(void)renderWithPixelBuffer:(CVPixelBufferRef)buffer size:(CGSize)size rotation: (float)rotation fillFrame: (bool)fill;
-(void)fillWithBlack;
@property bool stopRendering;
@end
/*
* Copyright (C) 2019 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 "CallMTKView.h"
@implementation CallMTKView {
id <MTLBuffer> vertexBuffer;
id <MTLDepthStencilState> depthState;
id<MTLCommandQueue> commandQueue;
id<MTLRenderPipelineState> pipeline;
CVMetalTextureCacheRef textureCache;
}
// Vertex data for an image plane
static const float kImagePlaneVertexData[16] = {
-1.0, -1.0, 0.0, 1.0,
1.0, -1.0, 1.0, 1.0,
-1.0, 1.0, 0.0, 0.0,
1.0, 1.0, 1.0, 0.0,
};
typedef enum BufferIndices {
kBufferIndexMeshPositions = 0,
} BufferIndices;
typedef enum VertexAttributes {
kVertexAttributePosition = 0,
kVertexAttributeTexcoord = 1,
} VertexAttributes;
struct Uniforms {
simd::float4x4 projectionMatrix;
simd::float4x4 rotationMatrix;
};
- (instancetype)initWithFrame:(NSRect)frame
{
self = [super initWithFrame:frame];
if (self) {
id<MTLDevice> device = MTLCreateSystemDefaultDevice();
self.device = device;
commandQueue = [device newCommandQueue];
self.colorPixelFormat = MTLPixelFormatBGRA8Unorm;
commandQueue = [device newCommandQueue];
CVReturn err = CVMetalTextureCacheCreate(kCFAllocatorDefault,
NULL,
self.device,
NULL,
&textureCache);
vertexBuffer = [device newBufferWithBytes:&kImagePlaneVertexData
length:sizeof(kImagePlaneVertexData)
options:MTLResourceCPUCacheModeDefaultCache];
NSString *resourcePath = [[NSBundle mainBundle] resourcePath];
NSString *libraryPath = [resourcePath stringByAppendingPathComponent:@"Shader.metallib"];
id <MTLLibrary> library = [device newLibraryWithFile:libraryPath error:nil];
id<MTLFunction> vertexFunc = [library newFunctionWithName:@"imageVertex"];
id<MTLFunction> fragmentFunc = [library newFunctionWithName:@"imageFragment"];
// Create a vertex descriptor for our image plane vertex buffer
MTLVertexDescriptor *imagePlaneVertexDescriptor = [[MTLVertexDescriptor alloc] init];
// Positions.
imagePlaneVertexDescriptor.attributes[kVertexAttributePosition].format = MTLVertexFormatFloat2;
imagePlaneVertexDescriptor.attributes[kVertexAttributePosition].offset = 0;
imagePlaneVertexDescriptor.attributes[kVertexAttributePosition].bufferIndex = kBufferIndexMeshPositions;
// Texture coordinates.
imagePlaneVertexDescriptor.attributes[kVertexAttributeTexcoord].format = MTLVertexFormatFloat2;
imagePlaneVertexDescriptor.attributes[kVertexAttributeTexcoord].offset = 8;
imagePlaneVertexDescriptor.attributes[kVertexAttributeTexcoord].bufferIndex = kBufferIndexMeshPositions;
// Position Buffer Layout
imagePlaneVertexDescriptor.layouts[kBufferIndexMeshPositions].stride = 16;
imagePlaneVertexDescriptor.layouts[kBufferIndexMeshPositions].stepRate = 1;
imagePlaneVertexDescriptor.layouts[kBufferIndexMeshPositions].stepFunction = MTLVertexStepFunctionPerVertex;
MTLRenderPipelineDescriptor *pipelineDescriptor = [MTLRenderPipelineDescriptor new];
pipelineDescriptor.vertexFunction = vertexFunc;
pipelineDescriptor.fragmentFunction = fragmentFunc;
pipelineDescriptor.colorAttachments[0].pixelFormat = MTLPixelFormatBGRA8Unorm;
pipelineDescriptor.vertexDescriptor = imagePlaneVertexDescriptor;
pipeline = [device newRenderPipelineStateWithDescriptor:pipelineDescriptor error:NULL];
MTLDepthStencilDescriptor *depthStateDescriptor = [[MTLDepthStencilDescriptor alloc] init];
depthStateDescriptor.depthCompareFunction = MTLCompareFunctionAlways;
depthStateDescriptor.depthWriteEnabled = NO;
depthState = [device newDepthStencilStateWithDescriptor:depthStateDescriptor];
self.preferredFramesPerSecond = 30;
}
return self;
}
- (void)fillWithBlack {
NSUInteger width = self.frame.size.width;
NSUInteger height = self.frame.size.height;
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
uint8_t *rawData = (uint8_t *)calloc(height * width * 4, sizeof(uint8_t));
NSUInteger bytesPerPixel = 4;
NSUInteger bytesPerRow = bytesPerPixel * width;
NSUInteger bitsPerComponent = 8;
MTLTextureDescriptor *textureDescriptor =
[MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatRGBA8Unorm
width:width
height:height
mipmapped:YES];
textureDescriptor.usage = MTLTextureUsageRenderTarget;
id<MTLTexture> texture = [self.device newTextureWithDescriptor:textureDescriptor];
MTLRegion region = MTLRegionMake2D(0, 0, width, height);
[texture replaceRegion:region mipmapLevel:0 withBytes:rawData bytesPerRow:bytesPerRow];
id<MTLCommandBuffer> commandBuffer = [commandQueue commandBuffer];
MTLRenderPassDescriptor *renderPass = self.currentRenderPassDescriptor;
id<MTLRenderCommandEncoder> commandEncoder = [commandBuffer renderCommandEncoderWithDescriptor:renderPass];
[commandEncoder setFragmentTexture:texture atIndex:0];
[commandEncoder endEncoding];
[commandBuffer presentDrawable:self.currentDrawable];
[commandBuffer commit];
}
bool frameDisplayed = false;
- (void)renderWithPixelBuffer:(CVPixelBufferRef)buffer
size:(CGSize)size
rotation: (float)rotation
fillFrame: (bool)fill {
if(frameDisplayed) {
return;
}
if(_stopRendering) {
self.releaseDrawables;
return;
}
if (buffer == nil) return;
frameDisplayed = true;
CFRetain(buffer);
CVPixelBufferLockBaseAddress(buffer, 0);
id<MTLTexture> textureY = [self getTexture:buffer pixelFormat:MTLPixelFormatR8Unorm planeIndex:0];
id<MTLTexture> textureCbCr = [self getTexture:buffer pixelFormat:MTLPixelFormatRG8Unorm planeIndex:1];
CVPixelBufferUnlockBaseAddress(buffer, 0);
if(textureY == NULL || textureCbCr == NULL) {
frameDisplayed = false;
CVPixelBufferRelease(buffer);
return;
}
id<CAMetalDrawable> drawable = self.currentDrawable;
if (!drawable.texture) {
frameDisplayed = false;
CVPixelBufferRelease(buffer);
return;
}
NSSize frameSize = self.frame.size;
float viewRatio = (rotation == 90 || rotation == -90 || rotation == 180 || rotation == -180) ?
frameSize.height/frameSize.width : frameSize.width/frameSize.height;
float frameRatio = ((float)size.width)/((float)size.height);
simd::float4x4 projectionMatrix;
float ratio = viewRatio * (1/frameRatio);
if((viewRatio >= 1 && frameRatio >= 1) ||
(viewRatio < 1 && frameRatio < 1) ||
(ratio > 0.5 && ratio < 1.5) ) {
if (ratio <= 1.0 && ratio >= 0.5)
projectionMatrix = [self getScalingMatrix: 1/ratio axis: 'x'];
else if (ratio < 0.5)
projectionMatrix = [self getScalingMatrix: ratio axis: 'y'];
else if (ratio > 1 && ratio < 2)
projectionMatrix = [self getScalingMatrix: ratio axis: 'y'];
else
projectionMatrix = [self getScalingMatrix: 1/ratio axis: 'x'];
} else {
if (ratio < 1.0 && !fill || fill && ratio > 1.0)
projectionMatrix = [self getScalingMatrix: ratio axis: 'y'];
else
projectionMatrix = [self getScalingMatrix: 1/ratio axis: 'x'];
}
float radians = (-rotation * M_PI) / 180;
simd::float4x4 rotationMatrix = [self getRotationMatrix:radians];
Uniforms bytes = Uniforms{projectionMatrix: projectionMatrix, rotationMatrix: rotationMatrix};
id<MTLCommandBuffer> commandBuffer = [commandQueue commandBuffer];
[commandBuffer addCompletedHandler:^(id<MTLCommandBuffer> cbuffer) {
frameDisplayed = false;
CVPixelBufferRelease(buffer);
}];
MTLRenderPassDescriptor *renderPass = self.currentRenderPassDescriptor;
renderPass.colorAttachments[0].texture = drawable.texture;
id<MTLRenderCommandEncoder> commandEncoder = [commandBuffer renderCommandEncoderWithDescriptor:renderPass];
[commandEncoder setRenderPipelineState: pipeline];
[commandEncoder setDepthStencilState:depthState];
[commandEncoder setVertexBytes: &bytes length:sizeof(bytes) atIndex:1];
[commandEncoder setVertexBuffer:vertexBuffer offset:0 atIndex:kBufferIndexMeshPositions];
[commandEncoder setFragmentTexture:textureY atIndex: 1];
[commandEncoder setFragmentTexture:textureCbCr atIndex:2];
[commandEncoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4];
[commandEncoder endEncoding];
[commandBuffer presentDrawable:drawable];
[commandBuffer commit];
}
-(simd::float4x4) getScalingMatrix:(CGFloat) ratio axis:(char) axis {
simd::float4x4 N = 0.0;
simd::float4 v[4] = {0.0, 0.0, 0.0, 0.0};
float xMultyplier = axis == 'x' ? ratio: 1;
float yMultyplier = axis == 'y' ? ratio: 1;
v[0] = { xMultyplier, 0, 0, 0 };
v[1] = { 0, yMultyplier, 0, 0 };
v[2] = { 0, 0, 1, 0 };
v[3] = { 0, 0, 0, 1 };
N = matrix_from_rows(v[0], v[1], v[2], v[3]);
return N;
}
-(simd::float4x4) getRotationMatrix:(float) rotation {
simd::float4x4 N = 0.0;
simd::float4 v[4] = {0.0, 0.0, 0.0, 0.0};
v[0] = { cos(rotation), sin(rotation), 0, 0 };
v[1] = { -sin(rotation), cos(rotation), 0, 0 };
v[2] = { 0, 0, 1, 0 };
v[3] = { 0, 0, 0, 1 };
N = matrix_from_rows(v[0], v[1], v[2], v[3]);
return N;
}
- (id<MTLTexture>)getTexture:(CVPixelBufferRef)image pixelFormat:(MTLPixelFormat)pixelFormat planeIndex:(int)planeIndex {
id<MTLTexture> texture;
size_t width, height;
if (planeIndex == -1)
{
width = CVPixelBufferGetWidth(image);
height = CVPixelBufferGetHeight(image);
planeIndex = 0;
}
else
{
width = CVPixelBufferGetWidthOfPlane(image, planeIndex);
height = CVPixelBufferGetHeightOfPlane(image, planeIndex);
}
auto format = CVPixelBufferGetPixelFormatType(image);
CVMetalTextureRef textureRef = NULL;
CVReturn status = CVMetalTextureCacheCreateTextureFromImage(NULL, textureCache, image, NULL, pixelFormat, width, height, planeIndex, &textureRef);
if(status == kCVReturnSuccess)
{
texture = CVMetalTextureGetTexture(textureRef);
CFRelease(textureRef);
}
else
{
NSLog(@"CVMetalTextureCacheCreateTextureFromImage failed with return stats %d", status);
return NULL;
}
return texture;
}
@end
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14460.31" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14460.31"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14490.70"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
......@@ -45,6 +45,7 @@
<outlet property="stateLabel" destination="kFD-FB-vig" id="SSO-14-q2t"/>
<outlet property="timeSpentLabel" destination="cIU-M7-xpN" id="9Rl-t3-gjY"/>
<outlet property="transferButton" destination="aHZ-qL-mYf" id="9id-Nt-M7i"/>
<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>
......@@ -73,6 +74,9 @@
</view>
<color key="fillColor" name="labelColor" catalog="System" colorSpace="catalog"/>
</box>
<customView translatesAutoresizingMaskIntoConstraints="NO" id="gQE-fN-JhY" customClass="CallMTKView">
<rect key="frame" x="0.0" y="0.0" width="746" height="62"/>
</customView>
<customView translatesAutoresizingMaskIntoConstraints="NO" id="d0X-cW-Xgz">
<rect key="frame" x="0.0" y="-10" width="746" height="72"/>
<subviews>
......@@ -120,6 +124,10 @@
<color key="backgroundColor" name="highlightColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<customView fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="L83-P5-9ao">
<rect key="frame" x="291" y="-12" width="163" height="96"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
</customView>
</subviews>
<constraints>
<constraint firstItem="kFD-FB-vig" firstAttribute="leading" secondItem="bg3-hB-nE8" secondAttribute="leading" id="LXG-QI-oPf"/>
......@@ -485,7 +493,7 @@
<constraint firstItem="anb-Y8-JQi" firstAttribute="centerY" secondItem="Kjq-iM-NBL" secondAttribute="centerY" id="zA4-c4-mEX"/>
</constraints>
</customView>
<customView translatesAutoresizingMaskIntoConstraints="NO" id="6y6-RH-qOp" userLabel="Preview">
<customView translatesAutoresizingMaskIntoConstraints="NO" id="6y6-RH-qOp" userLabel="Preview" customClass="CallMTKView">
<rect key="frame" x="551" y="20" width="175" height="120"/>
<constraints>
<constraint firstAttribute="height" constant="120" id="BvU-kV-0uD"/>
......@@ -669,6 +677,7 @@
<constraints>
<constraint firstAttribute="bottom" secondItem="Usy-W5-TGp" secondAttribute="bottom" id="5mb-Be-9o1"/>
<constraint firstItem="Usy-W5-TGp" firstAttribute="top" secondItem="2wf-Py-l6B" secondAttribute="top" id="9ZC-hX-N5k"/>
<constraint firstItem="gQE-fN-JhY" firstAttribute="trailing" secondItem="Usy-W5-TGp" secondAttribute="trailing" id="9cl-8g-a1Q"/>
<constraint firstAttribute="bottom" secondItem="Eoi-B8-iL6" secondAttribute="bottom" constant="20" id="9j2-HZ-hNX"/>
<constraint firstItem="W4l-Be-bhM" firstAttribute="centerY" secondItem="2wf-Py-l6B" secondAttribute="centerY" id="De3-8O-mXx"/>
<constraint firstAttribute="trailing" secondItem="d0X-cW-Xgz" secondAttribute="trailing" id="G79-Jv-EYw"/>
......@@ -676,6 +685,7 @@
<constraint firstAttribute="trailing" secondItem="6y6-RH-qOp" secondAttribute="trailing" constant="20" id="KTx-SN-RUg"/>
<constraint firstItem="d0X-cW-Xgz" firstAttribute="top" secondItem="2wf-Py-l6B" secondAttribute="top" id="MKB-zm-C75"/>
<constraint firstItem="CDQ-nt-oe4" firstAttribute="leading" secondItem="Usy-W5-TGp" secondAttribute="leading" id="N2u-0C-Y3z"/>
<constraint firstItem="gQE-fN-JhY" firstAttribute="leading" secondItem="Usy-W5-TGp" secondAttribute="leading" id="N5g-Mw-ag2"/>
<constraint firstItem="se7-PJ-iwD" firstAttribute="width" secondItem="2wf-Py-l6B" secondAttribute="width" id="O1b-nk-1Y1"/>
<constraint firstItem="peV-wm-HQm" firstAttribute="height" secondItem="2wf-Py-l6B" secondAttribute="height" id="O2u-BE-VPd"/>
<constraint firstAttribute="trailing" secondItem="Usy-W5-TGp" secondAttribute="trailing" id="Pj0-Ck-gtP"/>
......@@ -683,11 +693,13 @@
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="575" id="aB1-HF-No8"/>
<constraint firstItem="CDQ-nt-oe4" firstAttribute="bottom" secondItem="Usy-W5-TGp" secondAttribute="bottom" id="agL-I1-x42"/>
<constraint firstItem="d0X-cW-Xgz" firstAttribute="leading" secondItem="2wf-Py-l6B" secondAttribute="leading" id="efy-70-qsJ"/>
<constraint firstItem="gQE-fN-JhY" firstAttribute="bottom" secondItem="Usy-W5-TGp" secondAttribute="bottom" id="fwu-m8-3Pt"/>
<constraint firstItem="se7-PJ-iwD" firstAttribute="centerX" secondItem="2wf-Py-l6B" secondAttribute="centerX" id="hts-ke-nkj"/>
<constraint firstItem="se7-PJ-iwD" firstAttribute="centerY" secondItem="2wf-Py-l6B" secondAttribute="centerY" id="kpo-pf-qt5"/>
<constraint firstItem="W4l-Be-bhM" firstAttribute="centerX" secondItem="2wf-Py-l6B" secondAttribute="centerX" id="lvd-la-SAZ"/>
<constraint firstItem="CDQ-nt-oe4" firstAttribute="top" secondItem="Usy-W5-TGp" secondAttribute="top" id="mS7-0s-mzr"/>
<constraint firstItem="se7-PJ-iwD" firstAttribute="height" secondItem="2wf-Py-l6B" secondAttribute="height" id="nkk-DO-Hod"/>
<constraint firstItem="gQE-fN-JhY" firstAttribute="top" secondItem="Usy-W5-TGp" secondAttribute="top" id="pVh-ja-gEo"/>
<constraint firstItem="Eoi-B8-iL6" firstAttribute="leading" secondItem="2wf-Py-l6B" secondAttribute="leading" constant="20" id="sHw-xg-QAo"/>
<constraint firstItem="peV-wm-HQm" firstAttribute="centerY" secondItem="2wf-Py-l6B" secondAttribute="centerY" id="siE-fo-i7E"/>
<constraint firstItem="peV-wm-HQm" firstAttribute="centerX" secondItem="2wf-Py-l6B" secondAttribute="centerX" id="txG-gM-vYq"/>
......@@ -1062,7 +1074,7 @@
<userDefaultsController representsSharedInstance="YES" id="cb1-cg-dMu"/>
</objects>
<resources>
<image name="NSUser" width="128" height="128"/>
<image name="NSUser" width="32" height="32"/>
<image name="ic_action_accept" width="72" height="72"/>
<image name="ic_action_add_participant" width="72" height="72"/>
<image name="ic_action_audio" width="36" height="36"/>
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment