diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 00000000..6db1a2af Binary files /dev/null and b/.DS_Store differ diff --git a/CSFFMpegCapturePlugin/CSFFMpegCapturePlugin.xcodeproj/project.pbxproj b/CSFFMpegCapturePlugin/CSFFMpegCapturePlugin.xcodeproj/project.pbxproj new file mode 100644 index 00000000..7b33ee82 --- /dev/null +++ b/CSFFMpegCapturePlugin/CSFFMpegCapturePlugin.xcodeproj/project.pbxproj @@ -0,0 +1,340 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 343C79EC1D0C76C700B36EEC /* CSFFMpegCapture.m in Sources */ = {isa = PBXBuildFile; fileRef = 343C79EB1D0C76C700B36EEC /* CSFFMpegCapture.m */; }; + 343C79F01D0CF3C100B36EEC /* CSFFMpegInput.m in Sources */ = {isa = PBXBuildFile; fileRef = 343C79EF1D0CF3C100B36EEC /* CSFFMpegInput.m */; }; + 34BDCD651D10F4E100F51996 /* CSFFMpegCaptureViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 34BDCD631D10F4E100F51996 /* CSFFMpegCaptureViewController.m */; }; + 34BDCD661D10F4E100F51996 /* CSFFMpegCaptureViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 34BDCD641D10F4E100F51996 /* CSFFMpegCaptureViewController.xib */; }; + 34BDCD7D1D14178800F51996 /* CSFFMpegPlayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 34BDCD7C1D14178800F51996 /* CSFFMpegPlayer.m */; }; + 34EA820A1D2AB65300928A06 /* Media.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 34BDCD7E1D1FE4A700F51996 /* Media.xcassets */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 343C79BA1D0C763A00B36EEC /* CSFFMpegCapturePlugin.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CSFFMpegCapturePlugin.bundle; sourceTree = BUILT_PRODUCTS_DIR; }; + 343C79BD1D0C763A00B36EEC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 343C79DC1D0C769100B36EEC /* CSPluginServices.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSPluginServices.h; path = PluginHeaders/CSPluginServices.h; sourceTree = ""; }; + 343C79DD1D0C769100B36EEC /* CSIOSurfaceLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSIOSurfaceLayer.h; path = PluginHeaders/CSIOSurfaceLayer.h; sourceTree = ""; }; + 343C79DE1D0C769100B36EEC /* CSPcmPlayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSPcmPlayer.h; path = PluginHeaders/CSPcmPlayer.h; sourceTree = ""; }; + 343C79DF1D0C769100B36EEC /* CAMultiAudioPCM.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CAMultiAudioPCM.h; path = PluginHeaders/CAMultiAudioPCM.h; sourceTree = ""; }; + 343C79E01D0C769100B36EEC /* CSAbstractCaptureDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSAbstractCaptureDevice.h; path = PluginHeaders/CSAbstractCaptureDevice.h; sourceTree = ""; }; + 343C79E11D0C769100B36EEC /* CSTextCaptureBase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSTextCaptureBase.h; path = PluginHeaders/CSTextCaptureBase.h; sourceTree = ""; }; + 343C79E21D0C769100B36EEC /* CSTextCaptureViewControllerBase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSTextCaptureViewControllerBase.h; path = PluginHeaders/CSTextCaptureViewControllerBase.h; sourceTree = ""; }; + 343C79E31D0C769100B36EEC /* CSCaptureBase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSCaptureBase.h; path = PluginHeaders/CSCaptureBase.h; sourceTree = ""; }; + 343C79E41D0C769100B36EEC /* CSCaptureSourceProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSCaptureSourceProtocol.h; path = PluginHeaders/CSCaptureSourceProtocol.h; sourceTree = ""; }; + 343C79E51D0C769100B36EEC /* CSStreamServiceProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSStreamServiceProtocol.h; path = PluginHeaders/CSStreamServiceProtocol.h; sourceTree = ""; }; + 343C79E61D0C769100B36EEC /* CSPluginFactoryProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSPluginFactoryProtocol.h; path = PluginHeaders/CSPluginFactoryProtocol.h; sourceTree = ""; }; + 343C79E71D0C769100B36EEC /* CSExtraPluginProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSExtraPluginProtocol.h; path = PluginHeaders/CSExtraPluginProtocol.h; sourceTree = ""; }; + 343C79E81D0C769100B36EEC /* CSNotifications.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSNotifications.h; path = PluginHeaders/CSNotifications.h; sourceTree = ""; }; + 343C79EA1D0C76C700B36EEC /* CSFFMpegCapture.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSFFMpegCapture.h; sourceTree = ""; }; + 343C79EB1D0C76C700B36EEC /* CSFFMpegCapture.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSFFMpegCapture.m; sourceTree = ""; }; + 343C79EE1D0CF3C100B36EEC /* CSFFMpegInput.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSFFMpegInput.h; sourceTree = ""; }; + 343C79EF1D0CF3C100B36EEC /* CSFFMpegInput.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSFFMpegInput.m; sourceTree = ""; }; + 34BDCD621D10F4E100F51996 /* CSFFMpegCaptureViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSFFMpegCaptureViewController.h; sourceTree = ""; }; + 34BDCD631D10F4E100F51996 /* CSFFMpegCaptureViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSFFMpegCaptureViewController.m; sourceTree = ""; }; + 34BDCD641D10F4E100F51996 /* CSFFMpegCaptureViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = CSFFMpegCaptureViewController.xib; sourceTree = ""; }; + 34BDCD7B1D14178800F51996 /* CSFFMpegPlayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSFFMpegPlayer.h; sourceTree = ""; }; + 34BDCD7C1D14178800F51996 /* CSFFMpegPlayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSFFMpegPlayer.m; sourceTree = ""; }; + 34BDCD7E1D1FE4A700F51996 /* Media.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Media.xcassets; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 343C79B71D0C763A00B36EEC /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 343C79B11D0C763A00B36EEC = { + isa = PBXGroup; + children = ( + 34BDCD7E1D1FE4A700F51996 /* Media.xcassets */, + 343C79E91D0C769100B36EEC /* PluginHeaders */, + 343C79BC1D0C763A00B36EEC /* CSFFMpegCapturePlugin */, + 343C79BB1D0C763A00B36EEC /* Products */, + ); + sourceTree = ""; + }; + 343C79BB1D0C763A00B36EEC /* Products */ = { + isa = PBXGroup; + children = ( + 343C79BA1D0C763A00B36EEC /* CSFFMpegCapturePlugin.bundle */, + ); + name = Products; + sourceTree = ""; + }; + 343C79BC1D0C763A00B36EEC /* CSFFMpegCapturePlugin */ = { + isa = PBXGroup; + children = ( + 343C79BD1D0C763A00B36EEC /* Info.plist */, + 343C79EA1D0C76C700B36EEC /* CSFFMpegCapture.h */, + 343C79EB1D0C76C700B36EEC /* CSFFMpegCapture.m */, + 343C79EE1D0CF3C100B36EEC /* CSFFMpegInput.h */, + 343C79EF1D0CF3C100B36EEC /* CSFFMpegInput.m */, + 34BDCD621D10F4E100F51996 /* CSFFMpegCaptureViewController.h */, + 34BDCD631D10F4E100F51996 /* CSFFMpegCaptureViewController.m */, + 34BDCD641D10F4E100F51996 /* CSFFMpegCaptureViewController.xib */, + 34BDCD7B1D14178800F51996 /* CSFFMpegPlayer.h */, + 34BDCD7C1D14178800F51996 /* CSFFMpegPlayer.m */, + ); + path = CSFFMpegCapturePlugin; + sourceTree = ""; + }; + 343C79E91D0C769100B36EEC /* PluginHeaders */ = { + isa = PBXGroup; + children = ( + 343C79DC1D0C769100B36EEC /* CSPluginServices.h */, + 343C79DD1D0C769100B36EEC /* CSIOSurfaceLayer.h */, + 343C79DE1D0C769100B36EEC /* CSPcmPlayer.h */, + 343C79DF1D0C769100B36EEC /* CAMultiAudioPCM.h */, + 343C79E01D0C769100B36EEC /* CSAbstractCaptureDevice.h */, + 343C79E11D0C769100B36EEC /* CSTextCaptureBase.h */, + 343C79E21D0C769100B36EEC /* CSTextCaptureViewControllerBase.h */, + 343C79E31D0C769100B36EEC /* CSCaptureBase.h */, + 343C79E41D0C769100B36EEC /* CSCaptureSourceProtocol.h */, + 343C79E51D0C769100B36EEC /* CSStreamServiceProtocol.h */, + 343C79E61D0C769100B36EEC /* CSPluginFactoryProtocol.h */, + 343C79E71D0C769100B36EEC /* CSExtraPluginProtocol.h */, + 343C79E81D0C769100B36EEC /* CSNotifications.h */, + ); + name = PluginHeaders; + path = ../CocoaSplit; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 343C79B91D0C763A00B36EEC /* CSFFMpegCapturePlugin */ = { + isa = PBXNativeTarget; + buildConfigurationList = 343C79C01D0C763A00B36EEC /* Build configuration list for PBXNativeTarget "CSFFMpegCapturePlugin" */; + buildPhases = ( + 343C79B61D0C763A00B36EEC /* Sources */, + 343C79B71D0C763A00B36EEC /* Frameworks */, + 343C79B81D0C763A00B36EEC /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = CSFFMpegCapturePlugin; + productName = CSFFMpegCapturePlugin; + productReference = 343C79BA1D0C763A00B36EEC /* CSFFMpegCapturePlugin.bundle */; + productType = "com.apple.product-type.bundle"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 343C79B21D0C763A00B36EEC /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0730; + ORGANIZATIONNAME = Zakk; + TargetAttributes = { + 343C79B91D0C763A00B36EEC = { + CreatedOnToolsVersion = 7.3.1; + }; + }; + }; + buildConfigurationList = 343C79B51D0C763A00B36EEC /* Build configuration list for PBXProject "CSFFMpegCapturePlugin" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 343C79B11D0C763A00B36EEC; + productRefGroup = 343C79BB1D0C763A00B36EEC /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 343C79B91D0C763A00B36EEC /* CSFFMpegCapturePlugin */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 343C79B81D0C763A00B36EEC /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 34EA820A1D2AB65300928A06 /* Media.xcassets in Resources */, + 34BDCD661D10F4E100F51996 /* CSFFMpegCaptureViewController.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 343C79B61D0C763A00B36EEC /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 34BDCD651D10F4E100F51996 /* CSFFMpegCaptureViewController.m in Sources */, + 343C79EC1D0C76C700B36EEC /* CSFFMpegCapture.m in Sources */, + 343C79F01D0CF3C100B36EEC /* CSFFMpegInput.m in Sources */, + 34BDCD7D1D14178800F51996 /* CSFFMpegPlayer.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 343C79BE1D0C763A00B36EEC /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + }; + name = Debug; + }; + 343C79BF1D0C763A00B36EEC /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + }; + name = Release; + }; + 343C79C11D0C763A00B36EEC /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_WARN_OBJC_EXPLICIT_OWNERSHIP_TYPE = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + COMBINE_HIDPI_IMAGES = YES; + HEADER_SEARCH_PATHS = /usr/local/include; + INFOPLIST_FILE = CSFFMpegCapturePlugin/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles"; + MACOSX_DEPLOYMENT_TARGET = 10.8; + OTHER_LDFLAGS = ( + "-undefined", + suppress, + "-flat_namespace", + ); + PRODUCT_BUNDLE_IDENTIFIER = zakk.lol.CSFFMpegCapturePlugin; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + WRAPPER_EXTENSION = bundle; + }; + name = Debug; + }; + 343C79C21D0C763A00B36EEC /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_WARN_OBJC_EXPLICIT_OWNERSHIP_TYPE = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + COMBINE_HIDPI_IMAGES = YES; + HEADER_SEARCH_PATHS = /usr/local/include; + INFOPLIST_FILE = CSFFMpegCapturePlugin/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles"; + MACOSX_DEPLOYMENT_TARGET = 10.8; + OTHER_LDFLAGS = ( + "-undefined", + suppress, + "-flat_namespace", + ); + PRODUCT_BUNDLE_IDENTIFIER = zakk.lol.CSFFMpegCapturePlugin; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + WRAPPER_EXTENSION = bundle; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 343C79B51D0C763A00B36EEC /* Build configuration list for PBXProject "CSFFMpegCapturePlugin" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 343C79BE1D0C763A00B36EEC /* Debug */, + 343C79BF1D0C763A00B36EEC /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 343C79C01D0C763A00B36EEC /* Build configuration list for PBXNativeTarget "CSFFMpegCapturePlugin" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 343C79C11D0C763A00B36EEC /* Debug */, + 343C79C21D0C763A00B36EEC /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 343C79B21D0C763A00B36EEC /* Project object */; +} diff --git a/CSFFMpegCapturePlugin/CSFFMpegCapturePlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/CSFFMpegCapturePlugin.xcscheme b/CSFFMpegCapturePlugin/CSFFMpegCapturePlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/CSFFMpegCapturePlugin.xcscheme new file mode 100644 index 00000000..e3ff0b15 --- /dev/null +++ b/CSFFMpegCapturePlugin/CSFFMpegCapturePlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/CSFFMpegCapturePlugin.xcscheme @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CSFFMpegCapturePlugin/CSFFMpegCapturePlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/xcschememanagement.plist b/CSFFMpegCapturePlugin/CSFFMpegCapturePlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 00000000..6336e593 --- /dev/null +++ b/CSFFMpegCapturePlugin/CSFFMpegCapturePlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,22 @@ + + + + + SchemeUserState + + CSFFMpegCapturePlugin.xcscheme + + orderHint + 24 + + + SuppressBuildableAutocreation + + 343C79B91D0C763A00B36EEC + + primary + + + + + diff --git a/CSFFMpegCapturePlugin/CSFFMpegCapturePlugin/CSFFMpegCapture.h b/CSFFMpegCapturePlugin/CSFFMpegCapturePlugin/CSFFMpegCapture.h new file mode 100644 index 00000000..ec1d6284 --- /dev/null +++ b/CSFFMpegCapturePlugin/CSFFMpegCapturePlugin/CSFFMpegCapture.h @@ -0,0 +1,71 @@ +// +// CSFFMpegCapture.h +// CSFFMpegCapturePlugin +// +// Created by Zakk on 6/11/16. +// Copyright © 2016 Zakk. All rights reserved. +// + +#import +#import "CSCaptureBase.h" +#import "CSCaptureSourceProtocol.h" +#import "CSFFMpegInput.h" +#import "CSIOSurfaceLayer.h" +#import "CAMultiAudioPCM.h" +#import "CSPcmPlayer.h" +#import "CSPluginServices.h" +#import "CSFFMpegPlayer.h" + + + + + +#import "libavformat/avformat.h" +#import "libavcodec/avcodec.h" +#import "libavutil/threadmessage.h" +#import "libavutil/pixfmt.h" +#import "libavutil/pixdesc.h" + + +@interface CSFFMpegCapture : CSCaptureBase +{ + AVFormatContext *_avFmtCtx; + AVThreadMessageQueue *_video_msg_queue; + AVThreadMessageQueue *_audio_msg_queue; + + + dispatch_queue_t _video_decoder_queue; + dispatch_queue_t _media_reader_queue; + CAMultiAudioPCM *_bufferPCM; + AudioStreamBasicDescription _asbd; + CFTimeInterval _lastTimeUpdate; + double _savedTime; + + + +} + + +@property (strong) CSPcmPlayer *pcmPlayer; +@property (strong) CSFFMpegPlayer *player; + +@property (strong) NSString *currentTimeString; +@property (strong) NSString *durationString; +@property (assign) double currentMovieTime; +@property (assign) double currentMovieDuration; +@property (assign) bool playWhenLive; +@property (assign) bool useCurrentPosition; + + + + +-(void)queuePath:(NSString *)path; + +-(void)pause; +-(void)play; +-(void)mute; +-(void)next; +-(void)back; + + +@end diff --git a/CSFFMpegCapturePlugin/CSFFMpegCapturePlugin/CSFFMpegCapture.m b/CSFFMpegCapturePlugin/CSFFMpegCapturePlugin/CSFFMpegCapture.m new file mode 100644 index 00000000..228718f1 --- /dev/null +++ b/CSFFMpegCapturePlugin/CSFFMpegCapturePlugin/CSFFMpegCapture.m @@ -0,0 +1,363 @@ +// +// CSFFMpegCapture.m +// CSFFMpegCapturePlugin +// +// Created by Zakk on 6/11/16. +// Copyright © 2016 Zakk. All rights reserved. +// + +#import "CSFFMpegCapture.h" + +@implementation CSFFMpegCapture + +@synthesize currentMovieTime = _currentMovieTime; + +-(instancetype) init +{ + if (self = [super init]) + { + av_register_all(); + avformat_network_init(); + + + self.needsSourceSelection = NO; + + //Inputs resample to floating point non-interleaved 48k for now. + + _asbd.mSampleRate = 48000; + _asbd.mFormatID = kAudioFormatLinearPCM; + _asbd.mFormatFlags = kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsFloat | kAudioFormatFlagIsNonInterleaved; + _asbd.mChannelsPerFrame = 2; + _asbd.mBitsPerChannel = 32; + _asbd.mBytesPerFrame = 4; + _asbd.mBytesPerPacket = 4; + _asbd.mFramesPerPacket = 1; + + _player = [[CSFFMpegPlayer alloc] init]; + + _player.asbd = &_asbd; + + __weak __typeof__(self) weakSelf = self; + + _player.itemStarted = ^(CSFFMpegInput *item) { [weakSelf itemStarted:item]; }; + _player.queueStateChanged = ^() { [weakSelf queueChanged]; }; + + self.activeVideoDevice = [[CSAbstractCaptureDevice alloc] init]; + + + + + + } + return self; +} + +-(void)encodeWithCoder:(NSCoder *)aCoder +{ + NSMutableArray *queuePaths = [[NSMutableArray alloc] init]; + + for (CSFFMpegInput *inp in self.player.inputQueue) + { + [queuePaths addObject:inp.mediaPath]; + } + + [aCoder encodeObject:queuePaths forKey:@"queuePaths"]; + + CSFFMpegInput *nowPlaying = self.player.currentlyPlaying; + + NSString *nPath = nil; + + if (nowPlaying) + { + nPath = nowPlaying.mediaPath; + } + + [aCoder encodeObject:nPath forKey:@"nowPlayingPath"]; + [aCoder encodeBool:self.playWhenLive forKey:@"playWhenLive"]; + [aCoder encodeBool:self.useCurrentPosition forKey:@"useCurrentPosition"]; + [aCoder encodeDouble:_currentMovieTime forKey:@"savedTime"]; + +} + + +-(instancetype)initWithCoder:(NSCoder *)aDecoder +{ + if (self = [self init]) + { + + CSFFMpegInput *nowPlayingInput = nil; + NSString *nowPlayingPath = [aDecoder decodeObjectForKey:@"nowPlayingPath"]; + + NSArray *paths = [aDecoder decodeObjectForKey:@"queuePaths"]; + for (NSString *mPath in paths) + { + CSFFMpegInput *newInput = [[CSFFMpegInput alloc] initWithMediaPath:mPath]; + [self.player enqueueItem:newInput]; + if (nowPlayingPath && [newInput.mediaPath isEqualToString:nowPlayingPath]) + { + nowPlayingInput = newInput; + } + } + + if (nowPlayingInput) + { + self.player.currentlyPlaying = nowPlayingInput; + } + + _savedTime = [aDecoder decodeDoubleForKey:@"savedTime"]; + self.useCurrentPosition = [aDecoder decodeBoolForKey:@"useCurrentPosition"]; + + self.playWhenLive = [aDecoder decodeBoolForKey:@"playWhenLive"]; + } + + return self; +} + + ++(NSString *)label +{ + return @"Movie"; +} + +-(void) generateUniqueID +{ + NSMutableString *uID = [NSMutableString string]; + + + for(CSFFMpegInput *item in self.player.inputQueue) + { + NSString *itemStr = item.mediaPath; + [uID appendString:itemStr]; + } + + if (_pcmPlayer) + { + _pcmPlayer.nodeUID = uID; + } + + + self.activeVideoDevice.uniqueID = uID; +} + + + +-(double)currentMovieTime +{ + return _currentMovieTime; +} + +-(void)setCurrentMovieTime:(double)currentMovieTime +{ + if (self.player) + { + [self.player seek:currentMovieTime]; + } +} + + +-(void)queueChanged +{ + dispatch_async(dispatch_get_main_queue(), ^{ + [self generateUniqueID]; + }); +} + + +-(void)itemStarted:(CSFFMpegInput *)item +{ + + NSString *timeString = [self timeToString:item.duration]; + dispatch_async(dispatch_get_main_queue(), ^{ + self.durationString = [self timeToString:item.duration]; + self.currentMovieDuration = item.duration; + [self generateUniqueID]; + self.captureName = item.shortName; + if (self.pcmPlayer) + { + self.pcmPlayer.name = item.shortName; + } + }); + +} + + +-(void)queuePath:(NSString *)path +{ + if (!self.player.pcmPlayer && self.pcmPlayer) + { + self.player.pcmPlayer = self.pcmPlayer; + } + + CSFFMpegInput *newItem = [[CSFFMpegInput alloc] initWithMediaPath:path]; + + [self.player enqueueItem:newItem ]; + [self generateUniqueID]; +} + +-(void)pause +{ + if (self.player) + { + [self.player pause]; + } +} + + +-(void)play +{ + if (self.player) + { + [self.player play]; + } +} + + +-(void)mute +{ + if (self.player) + { + self.player.muted = !self.player.muted; + } +} + +-(void)next +{ + if (self.player) + { + [self.player next]; + } +} + +-(void)back +{ + if (self.player) + { + [self.player back]; + } +} + + +-(CALayer *)createNewLayer +{ + + CSIOSurfaceLayer *newLayer = [CSIOSurfaceLayer layer]; + + + return newLayer; +} + + +-(NSString *) timeToString:(double)convertTime +{ + + UInt64 minutes = convertTime/60; + UInt64 seconds = (int)convertTime % 60; + return [NSString stringWithFormat:@"%02lld:%02lld", minutes, seconds]; +} + + + +-(void)frameTick +{ + CFTimeInterval cTime = CACurrentMediaTime(); + CVPixelBufferRef use_buf = [self.player frameForMediaTime:cTime]; + + if (use_buf) + { + + if (cTime - _lastTimeUpdate > 0.5) + { + dispatch_async(dispatch_get_main_queue(), ^{ + self.currentTimeString = [self timeToString:self.player.lastVideoTime]; + [self willChangeValueForKey:@"currentMovieTime"]; + _currentMovieTime = self.player.lastVideoTime;; + + [self didChangeValueForKey:@"currentMovieTime"]; + }); + } + [self updateLayersWithBlock:^(CALayer *layer) { + + ((CSIOSurfaceLayer *)layer).imageBuffer = use_buf; + }]; + + CVPixelBufferRelease(use_buf); + + } +} + +-(void)setIsLive:(bool)isLive +{ + + bool oldLive = super.isLive; + super.isLive = isLive; + + if (isLive == oldLive) + { + return; + } + + if (isLive) + { + [self registerPCMOutput:1024 audioFormat:&_asbd]; + if (self.playWhenLive) + { + [self.player play]; + if (self.useCurrentPosition) + { + [self.player seek:_savedTime]; + } + } + + } else { + [self deregisterPCMOutput]; + } +} + + +-(void)registerPCMOutput:(CMItemCount)frameCount audioFormat:(const AudioStreamBasicDescription *)audioFormat +{ + + if (self.pcmPlayer) + { + //looks like we already have one? + return; + } + + + self.pcmPlayer = [[CSPluginServices sharedPluginServices] createPCMInput:@"BLAHBLAH" withFormat:audioFormat]; + if (self.player) + { + self.player.asbd = &_asbd; + self.player.pcmPlayer = self.pcmPlayer; + self.pcmPlayer.name = self.player.currentlyPlaying.shortName; + } + +} + +-(void)deregisterPCMOutput +{ + + + if (self.pcmPlayer) + { + + [[CSPluginServices sharedPluginServices] removePCMInput:self.pcmPlayer]; + } + + self.pcmPlayer = nil; + self.player.pcmPlayer = nil; + +} + +-(void)dealloc +{ + if (self.pcmPlayer) + { + [self deregisterPCMOutput]; + } +} + + + + +@end diff --git a/CSFFMpegCapturePlugin/CSFFMpegCapturePlugin/CSFFMpegCaptureViewController.h b/CSFFMpegCapturePlugin/CSFFMpegCapturePlugin/CSFFMpegCaptureViewController.h new file mode 100644 index 00000000..dcd0aa89 --- /dev/null +++ b/CSFFMpegCapturePlugin/CSFFMpegCapturePlugin/CSFFMpegCaptureViewController.h @@ -0,0 +1,30 @@ +// +// CSFFMpegCaptureViewController.h +// CSFFMpegCapturePlugin +// +// Created by Zakk on 6/14/16. +// Copyright © 2016 Zakk. All rights reserved. +// + +#import +#import "CSFFMpegCapture.h" + +@interface CSFFMpegCaptureViewController : NSViewController + +@property (weak) CSFFMpegCapture *captureObj; +@property (weak) IBOutlet NSSegmentedControl *playlistControl; +@property (strong) IBOutlet NSArrayController *queueArrayController; +@property (strong) NSString *stringItem; + + +- (IBAction)queueTableDoubleClick:(NSTableView *)sender; + +- (IBAction)chooseFile:(id)sender; +- (IBAction)nextAction:(id)sender; +- (IBAction)sliderValueChanged:(id)sender; +- (IBAction)pauseAction:(id)sender; + +- (IBAction)tableControlAction:(NSSegmentedControl *)sender; +- (IBAction)manualAddItem:(id)sender; + +@end diff --git a/CSFFMpegCapturePlugin/CSFFMpegCapturePlugin/CSFFMpegCaptureViewController.m b/CSFFMpegCapturePlugin/CSFFMpegCapturePlugin/CSFFMpegCaptureViewController.m new file mode 100644 index 00000000..0e549347 --- /dev/null +++ b/CSFFMpegCapturePlugin/CSFFMpegCapturePlugin/CSFFMpegCaptureViewController.m @@ -0,0 +1,138 @@ +// +// CSFFMpegCaptureViewController.m +// CSFFMpegCapturePlugin +// +// Created by Zakk on 6/14/16. +// Copyright © 2016 Zakk. All rights reserved. +// + +#import "CSFFMpegCaptureViewController.h" + +@interface CSFFMpegCaptureViewController () + +@end + +@implementation CSFFMpegCaptureViewController + +- (void)viewDidLoad { + [super viewDidLoad]; + // Do view setup here. + if (self.captureObj && self.captureObj.player) + { + self.captureObj.player.pauseStateChanged = ^{ + [self pauseStateChanged]; + }; + } +} + + +- (IBAction)queueTableDoubleClick:(NSTableView *)sender +{ + CSFFMpegInput *inp = [self.queueArrayController.arrangedObjects objectAtIndex:sender.clickedRow]; + if (inp) + { + [self.captureObj.player playAndAddItem:inp]; + } +} + + + +- (IBAction)chooseFile:(id)sender +{ + + NSOpenPanel *panel = [NSOpenPanel openPanel]; + panel.canChooseDirectories = NO; + panel.canCreateDirectories = NO; + panel.canChooseFiles = YES; + panel.allowsMultipleSelection = YES; + + [panel beginWithCompletionHandler:^(NSInteger result) { + if (result == NSFileHandlingPanelOKButton) + { + for (NSURL *openURL in panel.URLs) + { + [self.captureObj queuePath:openURL.path]; + } + } + + }]; + +} + +- (IBAction)tableControlAction:(NSSegmentedControl *)sender +{ + NSUInteger clicked = sender.selectedSegment; + + switch (clicked) + { + case 0: + [self.queueArrayController removeObjectsAtArrangedObjectIndexes:self.queueArrayController.selectionIndexes]; + break; + case 1: + [self.captureObj back]; + break; + case 2: + if (!self.captureObj.player.playing || self.captureObj.player.paused) + { + [self.captureObj play]; + } else { + [self.captureObj pause]; + } + break; + case 3: + [self.captureObj next]; + break; + } +} + +- (IBAction)manualAddItem:(id)sender +{ + if (self.stringItem) + { + [self.captureObj queuePath:self.stringItem]; + self.stringItem = nil; + } +} + + + + +-(void)pauseStateChanged +{ + dispatch_async(dispatch_get_main_queue(), ^{ + if (self.captureObj.player.paused) + { + NSImage *playImage = [[NSBundle bundleForClass:[self class]] imageForResource:@"play"]; + + [self.playlistControl setImage:playImage forSegment:2]; + + } else { + NSImage *pauseImage = [[NSBundle bundleForClass:[self class]] imageForResource:@"pause"]; + + [self.playlistControl setImage:pauseImage forSegment:2]; + + } + }); + + +} +- (IBAction)sliderValueChanged:(id)sender +{ + NSEvent *event = [[NSApplication sharedApplication] currentEvent]; + BOOL startingDrag = event.type == NSLeftMouseDown; + BOOL endingDrag = event.type == NSLeftMouseUp; + BOOL dragging = event.type == NSLeftMouseDragged; + + + if (startingDrag) { + [self.captureObj mute]; + } + + + if (endingDrag) { + [self.captureObj mute]; + } +} + + +@end diff --git a/CSFFMpegCapturePlugin/CSFFMpegCapturePlugin/CSFFMpegCaptureViewController.xib b/CSFFMpegCapturePlugin/CSFFMpegCapturePlugin/CSFFMpegCaptureViewController.xib new file mode 100644 index 00000000..8a5a9980 --- /dev/null +++ b/CSFFMpegCapturePlugin/CSFFMpegCapturePlugin/CSFFMpegCaptureViewController.xib @@ -0,0 +1,242 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CSFFMpegCapturePlugin/CSFFMpegCapturePlugin/CSFFMpegInput.h b/CSFFMpegCapturePlugin/CSFFMpegCapturePlugin/CSFFMpegInput.h new file mode 100644 index 00000000..ed356147 --- /dev/null +++ b/CSFFMpegCapturePlugin/CSFFMpegCapturePlugin/CSFFMpegInput.h @@ -0,0 +1,109 @@ +// +// CSFFMpegInput.h +// CSFFMpegCapturePlugin +// +// Created by Zakk on 6/11/16. +// Copyright © 2016 Zakk. All rights reserved. +// + +#import +#import + +#import "libavformat/avformat.h" +#import "libavcodec/avcodec.h" +#import "libavutil/threadmessage.h" +#import "libavutil/fifo.h" + +#include "libswscale/swscale.h" +#include "libswresample/swresample.h" +#include "libavutil/samplefmt.h" + +#import "CAMultiAudioPCM.h" +#import "CSPcmPlayer.h" + + + + + +struct frame_message { + + AVFrame *frame; + int notused; +}; + + +@interface CSFFMpegInput : NSObject +{ + int _video_stream_idx; + int _audio_stream_idx; + struct SwsContext *_sws_ctx; + struct SwrContext *_swr_ctx; + bool _video_done; + bool _audio_done; + uint64_t _seek_time; + bool _seek_request; + bool _stop_request; + + dispatch_queue_t _seek_queue; + + + +} + + +-(instancetype) initWithMediaPath:(NSString *)mediaPath; +-(bool)openMedia:(int)bufferVideoFrames; +-(AVFrame *)consumeFrame; +-(AVFrame *)consumeFrameWithRefill; +-(void)readAndDecodeVideoFrames:(int)frameCnt; +-(void)stop; + + + + + + +@property (strong) NSString *mediaPath; +@property (assign) bool is_ready; +@property (assign) bool is_draining; +@property (assign) AVRational videoTimeBase; +@property (assign) AVRational audioTimeBase; +@property (strong) CSPcmPlayer *pcmPlayer; +@property (assign) bool stopped; +@property (assign) bool paused; +@property (assign) AVThreadMessageQueue *video_message_queue; +@property (assign) AVThreadMessageQueue *audio_message_queue; +@property (assign) AVFormatContext *format_ctx; + +@property (assign) AVCodecContext *video_codec_ctx; +@property (assign) AVCodec *video_codec; + +@property (assign) AVCodecContext *audio_codec_ctx; +@property (assign) AVCodec *audio_codec; + + +@property (nonatomic, copy) void (^completionCallback)(void); +@property (assign) int64_t first_video_pts; +@property (assign) int64_t first_audio_pts; + + +@property (assign) NSSize dimensions; +@property (assign) double duration; + +@property (strong) NSString *shortName; + + +-(AVFrame *)consumeFrame:(int *)error_out; +-(CAMultiAudioPCM *)consumeAudioFrame:(AudioStreamBasicDescription *)asbd error_out:(int *)error_out; +-(void) closeMedia; +-(void) seek:(double)time; + + + + + + + + + +@end diff --git a/CSFFMpegCapturePlugin/CSFFMpegCapturePlugin/CSFFMpegInput.m b/CSFFMpegCapturePlugin/CSFFMpegCapturePlugin/CSFFMpegInput.m new file mode 100644 index 00000000..8b54b53d --- /dev/null +++ b/CSFFMpegCapturePlugin/CSFFMpegCapturePlugin/CSFFMpegInput.m @@ -0,0 +1,733 @@ +// +// CSFFMpegInput.m +// CSFFMpegCapturePlugin +// +// Created by Zakk on 6/11/16. +// Copyright © 2016 Zakk. All rights reserved. +// + +#import "CSFFMpegInput.h" + +@implementation CSFFMpegInput + + +-(instancetype) init +{ + if (self = [super init]) + { + _video_codec = NULL; + _video_codec_ctx = NULL; + _video_stream_idx = -1; + _audio_stream_idx = -1; + _is_ready = NO; + _is_draining = NO; + + _first_video_pts = 0; + _first_audio_pts = 0; + _duration = 0.0f; + _seek_request = NO; + _seek_time = 0; + + _seek_queue = dispatch_queue_create("SEEK QUEUE", DISPATCH_QUEUE_SERIAL); + + } + return self; +} + +-(instancetype) initWithMediaPath:(NSString *)mediaPath +{ + if (self = [self init]) + { + self.mediaPath = mediaPath; + self.shortName = [mediaPath lastPathComponent]; + } + + return self; +} + +-(bool)openMedia:(int)bufferVideoFrames +{ + + + if (!self.mediaPath) + { + return NO; + } + + + if (!_video_message_queue) + { + av_thread_message_queue_alloc(&_video_message_queue, 300, sizeof(struct frame_message)); + } + + if (!_audio_message_queue) + { + av_thread_message_queue_alloc(&_audio_message_queue, 4096 , sizeof(struct frame_message)); + } + + av_thread_message_queue_set_err_recv(_video_message_queue, 0); + av_thread_message_queue_set_err_recv(_audio_message_queue, 0); + av_thread_message_queue_set_err_send(_video_message_queue, 0); + av_thread_message_queue_set_err_send(_audio_message_queue, 0); + + + + + + + + AVCodecContext *v_codec_ctx_orig = NULL; + AVCodecContext *a_codec_ctx_orig = NULL; + int open_ret = avformat_open_input(&_format_ctx, self.mediaPath.UTF8String, NULL, NULL); + if (open_ret < 0) + { + return NO; + } + + + avformat_find_stream_info(_format_ctx, NULL); + //av_dump_format(_format_ctx, 0, self.mediaPath.UTF8String, 0); + for (int i=0; i < _format_ctx->nb_streams; i++) + { + if (_format_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO && _video_stream_idx == -1) + { + _video_stream_idx = i; + } + + if (_format_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO && _audio_stream_idx == -1) + { + _audio_stream_idx = i; + } + + if (_audio_stream_idx > -1 && _video_stream_idx > -1) + { + break; + } + } + if (_video_stream_idx > -1) + { + self.videoTimeBase = _format_ctx->streams[_video_stream_idx]->time_base; + v_codec_ctx_orig = _format_ctx->streams[_video_stream_idx]->codec; + _video_codec = avcodec_find_decoder(v_codec_ctx_orig->codec_id); + _video_codec_ctx = avcodec_alloc_context3(_video_codec); + avcodec_copy_context(_video_codec_ctx, v_codec_ctx_orig); + avcodec_open2(_video_codec_ctx, _video_codec, NULL); + self.dimensions = NSMakeSize(_video_codec_ctx->width, _video_codec_ctx->height); + _sws_ctx = sws_alloc_context(); + av_set_int(_sws_ctx, "srcw", _video_codec_ctx->width); + av_set_int(_sws_ctx, "srch", _video_codec_ctx->height); + av_set_int(_sws_ctx, "src_format", _video_codec_ctx->pix_fmt); + av_set_int(_sws_ctx, "dstw", _video_codec_ctx->width); + av_set_int(_sws_ctx, "dsth", _video_codec_ctx->height); + av_set_int(_sws_ctx, "dst_format", AV_PIX_FMT_NV12); + sws_init_context(_sws_ctx, NULL, NULL); + } + if (_audio_stream_idx > -1) + { + self.audioTimeBase = _format_ctx->streams[_audio_stream_idx]->time_base; + + a_codec_ctx_orig = _format_ctx->streams[_audio_stream_idx]->codec; + _audio_codec = avcodec_find_decoder(a_codec_ctx_orig->codec_id); + _audio_codec_ctx = avcodec_alloc_context3(_audio_codec); + avcodec_copy_context(_audio_codec_ctx, a_codec_ctx_orig); + avcodec_open2(_audio_codec_ctx, _audio_codec, NULL); + } + + + self.is_ready = NO; + _stop_request = NO; + self.is_draining = NO; + _video_done = NO; + _audio_done = NO; + + + self.duration = _format_ctx->duration / (double)AV_TIME_BASE; + + + + + + [self readAndDecodeVideoFrames:bufferVideoFrames]; + + return YES; +} + + + + + + + + +-(CAMultiAudioPCM *)consumeAudioFrame:(AudioStreamBasicDescription *)asbd error_out:(int *)error_out +{ + struct frame_message msg; + + AudioBufferList *sampleABL = NULL; + int av_ret = 0; + + if (_audio_message_queue && ((*error_out = av_thread_message_queue_recv(_audio_message_queue, &msg, AV_THREAD_MESSAGE_NONBLOCK)) >= 0)) + { + + AVFrame *recv_frame; + + recv_frame = msg.frame; + if (!recv_frame) + { + return NULL; + } + + + if (recv_frame->width == 999) + { + //flush PCM player + av_frame_free(&recv_frame); + CAMultiAudioPCM *flushPCM = [[CAMultiAudioPCM alloc] init]; + flushPCM.frameCount = -1; + flushPCM.bufferCount = -1; + return flushPCM; + } + + uint8_t **dst_data = NULL; + int dst_linesize; + + if (!_swr_ctx) + { + uint64_t channel_layout = _audio_codec_ctx->channel_layout; + if (!channel_layout) + { + channel_layout = av_get_default_channel_layout(_audio_codec_ctx->channels); + } + _swr_ctx = swr_alloc_set_opts(NULL, AV_CH_LAYOUT_STEREO, AV_SAMPLE_FMT_FLTP, asbd->mSampleRate, channel_layout, _audio_codec_ctx->sample_fmt, _audio_codec_ctx->sample_rate, 0, NULL); + swr_init(_swr_ctx); + } + + + int dst_nb_samples = av_rescale_rnd(recv_frame->nb_samples, asbd->mSampleRate, _audio_codec_ctx->sample_rate, AV_ROUND_UP); + av_samples_alloc_array_and_samples(&dst_data, &dst_linesize, 2, dst_nb_samples, AV_SAMPLE_FMT_FLTP, 1); + int conv_samples = swr_convert(_swr_ctx, dst_data, dst_nb_samples, recv_frame->extended_data, recv_frame->nb_samples); + + + + + + int bufferCnt = asbd->mFormatFlags & kAudioFormatFlagIsNonInterleaved ? asbd->mChannelsPerFrame : 1; + + int channelCnt = _audio_codec_ctx->channels; + + long byteCnt = asbd->mBytesPerFrame * dst_nb_samples; + + sampleABL = malloc(sizeof(AudioBufferList) + (bufferCnt-1)*sizeof(AudioBuffer)); + + + sampleABL->mNumberBuffers = bufferCnt; + + for (int i=0; imBuffers[i].mData = dst_data[i]; + + sampleABL->mBuffers[i].mDataByteSize = (UInt32)dst_linesize; + sampleABL->mBuffers[i].mNumberChannels = 1; + } + + + + av_frame_free(&recv_frame); + + CAMultiAudioPCM *retPCM = [[CAMultiAudioPCM alloc] initWithAudioBufferList:sampleABL streamFormat:asbd]; + return retPCM; + + } else { + return NULL; + } +} + + + +-(void)videoFlush:(bool)withEOF +{ + + struct frame_message msg; + avcodec_flush_buffers(_video_codec_ctx); + int flush_cnt = 0; + if (_video_message_queue) + { + while (av_thread_message_queue_recv(_video_message_queue, &msg, AV_THREAD_MESSAGE_NONBLOCK) >= 0) + { + if (msg.frame) + { + av_frame_free(&msg.frame); + } + } + if (withEOF) + { + av_thread_message_queue_set_err_recv(_video_message_queue, AVERROR_EOF); + } + } + + +} + + +-(void)audioFlush +{ + + struct frame_message msg; + int flush_cnt = 0; + + avcodec_flush_buffers(_audio_codec_ctx); + if (_audio_message_queue) + { + while (av_thread_message_queue_recv(_audio_message_queue, &msg, AV_THREAD_MESSAGE_NONBLOCK) >= 0) + { + + if (msg.frame) + { + av_frame_free(&msg.frame); + } + } + + AVFrame *flushFrame = av_frame_alloc(); + flushFrame->width = 999; + msg.frame = flushFrame; + + av_thread_message_queue_send(_audio_message_queue, &msg, AV_THREAD_MESSAGE_NONBLOCK); + + } +} + + +-(void)internal_seek:(int64_t)time +{ + if (_format_ctx) + { + + int seek_ret = av_seek_frame(_format_ctx, -1, time, AVSEEK_FLAG_BACKWARD); + + AVFifoBuffer *seek_buffer = av_fifo_alloc(sizeof(AVPacket) * 600); + + + //int seek_ret = avformat_seek_file(_format_ctx, _video_stream_idx, time-10, time, time+10, AVSEEK_FLAG_BACKWARD); + [self videoFlush:NO]; + [self audioFlush]; + + AVPacket buf_pkt; + int64_t video_pts = AV_NOPTS_VALUE; + + while (av_read_frame(_format_ctx, &buf_pkt) >= 0) + { + if (buf_pkt.stream_index == _video_stream_idx) + { + video_pts = buf_pkt.pts; + [self decodeVideoPacket:&buf_pkt]; + av_free_packet(&buf_pkt); + break; + } else if (buf_pkt.stream_index == _audio_stream_idx){ + av_fifo_generic_write(seek_buffer, &buf_pkt, sizeof(AVPacket), NULL); + } + } + + while (av_fifo_size(seek_buffer) >= sizeof(AVPacket)) + { + AVPacket a_pkt; + av_fifo_generic_read(seek_buffer, &a_pkt, sizeof(AVPacket), NULL); + if (av_compare_ts(a_pkt.pts, self.audioTimeBase, video_pts, self.videoTimeBase) >= 0) + { + [self decodeAudioPacket:&a_pkt]; + } + av_free_packet(&a_pkt); + } + av_fifo_free(seek_buffer); + + _first_video_pts = 0; + _seek_request = NO; + } +} + +-(void)seek:(double)time +{ + if (_seek_request) return; + + if (_format_ctx) + { + int64_t seek_pts = time / av_q2d(AV_TIME_BASE_Q); + + + _seek_time = seek_pts; + _seek_request = YES; + av_thread_message_queue_set_err_send(_video_message_queue, AVERROR_EXTERNAL); + + //[self audioFlush]; + } + +} + +-(void)stop +{ + _stop_request = YES; +} + +-(AVFrame *)consumeFrame:(int *)error_out +{ + + if (!_video_message_queue) + { + return NULL; + } + + + if (_video_done) + { + return NULL; + } + *error_out = 0; + + struct frame_message msg; + AVFrame *recv_frame; + if ((*error_out = av_thread_message_queue_recv(_video_message_queue, &msg, AV_THREAD_MESSAGE_NONBLOCK)) >= 0) + { + recv_frame = msg.frame; + } else { + if (*error_out == AVERROR_EOF) + { + _video_done = YES; + } + + if (self.is_draining) + { + self.is_ready = NO; + } + recv_frame = NULL; + } + return recv_frame; +} + + +//You should run this is a gcd queue/block + +-(void)readAndDecodeVideoFrames:(int)frameCnt +{ + + int read_frames = 0; + AVFrame *output_frame = NULL; + AVPacket av_packet; + bool do_audio = YES; + bool do_video = YES; + int64_t seek_pts; + struct frame_message msg; + + + + if (!_format_ctx) + { + return; + } + + + int pkt_cnt = 0; + + while (do_audio || do_video) + { + if (_stop_request) + { + [self closeMedia]; + _stop_request = NO; + return; + } + + if (_seek_request) + { + [self internal_seek:_seek_time]; + av_thread_message_queue_set_err_send(_video_message_queue, 0); + + } + + + if (frameCnt == 0 && !self.is_ready) + { + + continue; + } + + output_frame = av_frame_alloc(); + int got_decoded_frame; + int read_ret = 0; + + if (!self.is_draining) + { + read_ret = av_read_frame(_format_ctx, &av_packet); + } else { + + if (do_video) + { + av_init_packet(&av_packet); + av_packet.stream_index = _video_stream_idx; + av_packet.size = 0; + av_packet.data = NULL; + + } else if (do_audio) { + av_init_packet(&av_packet); + av_packet.stream_index = _audio_stream_idx; + av_packet.size = 0; + av_packet.data = NULL; + } + } + + if (read_ret < 0) + { + self.is_draining = YES; + continue; + } + + + + if (do_video && (av_packet.stream_index == _video_stream_idx)) + { + + if (_first_video_pts == 0 && av_packet.pts != AV_NOPTS_VALUE) + { + _first_video_pts = av_packet.pts; + } + + bool got_frame = [self decodeVideoPacket:&av_packet]; + + + + if (!got_frame) + { + if (self.is_draining) + { + av_thread_message_queue_set_err_recv(_video_message_queue, AVERROR_EOF); + do_video = NO; + } + } else { + read_frames++; + } + } else if (do_audio && (av_packet.stream_index == _audio_stream_idx)) { + + if (_first_audio_pts == 0) + { + _first_audio_pts = av_packet.pts; + } + + bool got_frame = [self decodeAudioPacket:&av_packet]; + if (!got_frame && self.is_draining) + { + do_audio = NO; + av_thread_message_queue_set_err_recv(_audio_message_queue, AVERROR_EOF); + } + } + av_free_packet(&av_packet); + + av_frame_free(&output_frame); + if (frameCnt > 0 && read_frames >= frameCnt && !self.is_ready) + { + self.is_ready = YES; + return; + } + + } +} + + +-(bool)decodeAudioPacket:(AVPacket *)av_packet +{ + + AVFrame *output_frame = NULL; + void *orig_data; + size_t orig_size; + orig_size = av_packet->size; + orig_data = av_packet->data; + bool ret = NO; + int got_decoded_frame = 0; + output_frame = av_frame_alloc(); + struct frame_message msg; + + + int read_len; + while (av_packet->size > 0 || av_packet->data == NULL) + { + + read_len = avcodec_decode_audio4(_audio_codec_ctx, output_frame, &got_decoded_frame, av_packet); + if (got_decoded_frame) + { + if (!output_frame->channel_layout) + { + output_frame->channel_layout = av_get_default_channel_layout(_audio_codec_ctx->channels); + } + AVFrame *cloned_frame = av_frame_clone(output_frame); + + msg.frame = cloned_frame; + msg.notused = 0; + av_thread_message_queue_send(_audio_message_queue, &msg, 0); + ret = YES; + + } else { + if (self.is_draining) + { + ret = NO; + break; + } + } + if (!self.is_draining) + { + if (read_len < 0) + { + av_packet->data = orig_data; + av_packet->size = orig_size; + break; + } else { + av_packet->data += read_len; + av_packet->size -= read_len; + } + } + } + av_frame_free(&output_frame); + return ret; +} + + +-(bool)decodeVideoPacket:(AVPacket *)av_packet +{ + AVFrame *output_frame = NULL; + struct frame_message msg; + int got_decoded_frame = 0; + bool ret = NO; + + + + + output_frame = av_frame_alloc(); + + avcodec_decode_video2(_video_codec_ctx, output_frame, &got_decoded_frame, av_packet); + if (got_decoded_frame) + { + + int width = output_frame->width; + int height = output_frame->height; + + + + AVFrame* conv_frame = avcodec_alloc_frame(); + conv_frame->width = width; + conv_frame->height = height; + conv_frame->format = AV_PIX_FMT_NV12; + + int num_bytes = avpicture_get_size(AV_PIX_FMT_NV12, width, height); + uint8_t* conv_buffer = (uint8_t *)av_malloc(num_bytes); + avpicture_fill((AVPicture*)conv_frame, conv_buffer, AV_PIX_FMT_NV12, width, height); + int scale_ret = sws_scale(_sws_ctx, output_frame->data, output_frame->linesize, 0, height, conv_frame->data, conv_frame->linesize); + conv_frame->pts = output_frame->pts; + conv_frame->pkt_dts = output_frame->pkt_dts; + if (output_frame->pkt_pts != AV_NOPTS_VALUE) + { + + conv_frame->pkt_pts = output_frame->pkt_pts; + } else { + conv_frame->pkt_pts = output_frame->pkt_dts; //I guess + } + + + msg.frame = conv_frame; + msg.notused = 0; + av_thread_message_queue_send(_video_message_queue, &msg, 0); + ret = YES; + } else { + ret = NO; + } + av_frame_free(&output_frame); + + return ret; +} + + +-(void) closeMedia +{ + + struct frame_message msg; + + if (_video_message_queue) + { + while (av_thread_message_queue_recv(_video_message_queue, &msg, AV_THREAD_MESSAGE_NONBLOCK) >= 0) + { + if (msg.frame) + { + av_frame_free(&msg.frame); + } + } + + av_thread_message_queue_set_err_recv(_video_message_queue, AVERROR_EOF); + + } + + if (_audio_message_queue) + { + av_thread_message_queue_set_err_recv(_audio_message_queue, AVERROR_EOF); + + + while (av_thread_message_queue_recv(_audio_message_queue, &msg, AV_THREAD_MESSAGE_NONBLOCK) >= 0) + { + if (msg.frame) + { + av_frame_free(&msg.frame); + } + } + + } + + if (_video_codec_ctx) + { + avcodec_close(_video_codec_ctx); + } + + if (_audio_codec_ctx) + { + avcodec_close(_audio_codec_ctx); + } + + + if (_format_ctx) + { + avformat_close_input(&_format_ctx); + } + + _video_codec_ctx = NULL; + _audio_codec_ctx = NULL; + + _format_ctx = NULL; + if (_sws_ctx) + { + sws_freeContext(_sws_ctx); + _sws_ctx = NULL; + } + + if (_swr_ctx) + { + swr_free(&_swr_ctx); + } + + + self.duration = 0.0f; + + +} + +-(void)dealloc +{ + NSLog(@"DEALLOC?"); + [self closeMedia]; + if (_video_message_queue) + { + av_thread_message_queue_free(&_video_message_queue); + _video_message_queue = NULL; + } + + if (_audio_message_queue) + { + av_thread_message_queue_free(&_audio_message_queue); + _audio_message_queue = NULL; + + } +} + +@end diff --git a/CSFFMpegCapturePlugin/CSFFMpegCapturePlugin/CSFFMpegPlayer.h b/CSFFMpegCapturePlugin/CSFFMpegCapturePlugin/CSFFMpegPlayer.h new file mode 100644 index 00000000..4f64c050 --- /dev/null +++ b/CSFFMpegCapturePlugin/CSFFMpegCapturePlugin/CSFFMpegPlayer.h @@ -0,0 +1,85 @@ +// +// CSFFMpegPlayer.h +// CSFFMpegCapturePlugin +// +// Created by Zakk on 6/17/16. +// Copyright © 2016 Zakk. All rights reserved. +// + +#import +#import + +#import "CSFFMpegInput.h" +#import "CSPcmPlayer.h" + +@interface CSFFMpegPlayer : NSObject +{ + CFTimeInterval _first_frame_host_time; + AVFrame *_peek_frame; + CVPixelBufferRef _last_buf; + dispatch_queue_t _input_read_queue; + dispatch_queue_t _audio_queue; + bool _audio_done; + bool _video_done; + CVPixelBufferPoolRef *_cvpool; + NSSize _currentSize; + bool _nextFlag; + int64_t _first_video_pts; + bool _flushAudio; + int _doneDirection; + CSFFMpegInput *_forceNextInput; + bool _seekRequest; + double _seekRequestTime; + + + + +} + + + +@property (strong) NSMutableArray *inputQueue; +@property (strong) CSPcmPlayer *pcmPlayer; +@property (assign) bool paused; +@property (assign) bool playing; + +@property (strong) CSFFMpegInput *currentlyPlaying; +@property (assign) AudioStreamBasicDescription *asbd; + +@property (copy, nonatomic) void (^itemStarted)(CSFFMpegInput *); +@property (copy, nonatomic) void (^pauseStateChanged)(); +@property (copy, nonatomic) void (^queueStateChanged)(); + + +@property (assign) double lastVideoTime; +@property (assign) double videoDuration; +@property (assign) bool muted; +@property (assign) bool seeking; +@property (assign) bool audio_needs_restart; + + + + + +-(void)nextItem; +-(void)previousItem; +-(void)enqueueItem:(CSFFMpegInput *)item; +-(void)play; +-(void)stop; +-(void)next; +-(void)pause; +-(void)back; +-(void)playAndAddItem:(CSFFMpegInput *)item; + + + +-(void)seek:(double)toTime; +-(void)startAudio; + + + +-(CVPixelBufferRef)frameForMediaTime:(CFTimeInterval)mediaTime; + + + +@end diff --git a/CSFFMpegCapturePlugin/CSFFMpegCapturePlugin/CSFFMpegPlayer.m b/CSFFMpegCapturePlugin/CSFFMpegCapturePlugin/CSFFMpegPlayer.m new file mode 100644 index 00000000..c3ba4923 --- /dev/null +++ b/CSFFMpegCapturePlugin/CSFFMpegCapturePlugin/CSFFMpegPlayer.m @@ -0,0 +1,660 @@ +// +// CSFFMpegPlayer.m +// CSFFMpegCapturePlugin +// +// Created by Zakk on 6/17/16. +// Copyright © 2016 Zakk. All rights reserved. +// + +#import "CSFFMpegPlayer.h" + +@implementation CSFFMpegPlayer + +@synthesize muted = _muted; + + +-(instancetype) init +{ + if (self = [super init]) + { + _input_read_queue = dispatch_queue_create("FFMPEG PLAYER INPUT", DISPATCH_QUEUE_SERIAL); + _audio_queue = dispatch_queue_create("FFMPEG PLAYER AUDIO", DISPATCH_QUEUE_SERIAL); + _currentSize = NSZeroSize; + _inputQueue = [[NSMutableArray alloc] init]; + _nextFlag = NO; + _muted = NO; + _doneDirection = 1; + + } + + return self; +} + + +-(void)removeObjectFromInputQueueAtIndex:(NSUInteger)index +{ + [_inputQueue removeObjectAtIndex:index]; + if (self.queueStateChanged) + { + self.queueStateChanged(); + } +} + +-(void)insertObject:(NSObject *)object inInputQueueAtIndex:(NSUInteger)index +{ + [_inputQueue insertObject:object atIndex:index]; + if (self.queueStateChanged) + { + self.queueStateChanged(); + } + +} + + + +-(void)setMuted:(bool)muted +{ + _muted = muted; + if (self.pcmPlayer) + { + + self.pcmPlayer.muted = muted; + } +} + +-(bool)muted +{ + return _muted; +} + + +-(void)readThread +{ + if (self.currentlyPlaying) + { + if (_seekRequest) + { + [self seek:_seekRequestTime]; + } + + [self.currentlyPlaying readAndDecodeVideoFrames:0]; + } +} + + +-(CSFFMpegInput *)preChangeItem +{ + CSFFMpegInput *useItem; + _nextFlag = NO; + @synchronized (self) { + useItem = self.currentlyPlaying; + self.currentlyPlaying = nil; + _audio_done = NO; + _video_done = NO; + _flushAudio = NO; + _first_frame_host_time = 0; + } + + if (useItem) + { + if (self.pcmPlayer) + { + [self.pcmPlayer flush]; + } + } + + if (useItem) + { + [useItem closeMedia]; + } + + + return useItem; + +} + + +-(void)previousItem +{ + CSFFMpegInput *useItem = [self preChangeItem]; + + if (!self.playing) + { + return; + } + + NSInteger currentIdx = 0; + + currentIdx = [_inputQueue indexOfObject:useItem]; + + currentIdx--; + + if (currentIdx < 0) + { + currentIdx = _inputQueue.count-1; + } + + + + CSFFMpegInput *nextItem = nil; + + + if (currentIdx >=0 && (currentIdx < _inputQueue.count)) + { + nextItem = [_inputQueue objectAtIndex:currentIdx]; + + } + if (nextItem) + { + [self playItem:nextItem]; + //[self removeObjectFromInputQueueAtIndex:0]; + + } + +} + + +-(void)nextItem +{ + CSFFMpegInput *useItem = [self preChangeItem]; + + if (!self.playing) + { + return; + } + + NSUInteger currentIdx = 0; + + currentIdx = [_inputQueue indexOfObject:useItem]; + + currentIdx++; + + if (currentIdx >= _inputQueue.count) + { + currentIdx = 0; + } + + CSFFMpegInput *nextItem = nil; + + + if (currentIdx >=0 && (currentIdx < _inputQueue.count)) + { + nextItem = [_inputQueue objectAtIndex:currentIdx]; + + } + if (nextItem) + { + [self playItem:nextItem]; + //[self removeObjectFromInputQueueAtIndex:0]; + + } + +} + + +-(void)playAndAddItem:(CSFFMpegInput *)item; +{ + if ([_inputQueue indexOfObject:item] == NSNotFound) + { + [self enqueueItem:item]; + } + + if (!self.playing) + { + self.currentlyPlaying = item; + [self playItem:item]; + } else { + _forceNextInput = item; + [self.currentlyPlaying stop]; + } +} + + +-(void)seek:(double)toTime +{ + + if (_seekRequest) + { + [self.currentlyPlaying seek:toTime]; + _first_frame_host_time = 0; + _peek_frame = NULL; + _first_video_pts = 0; + _seekRequest = NO; + _seekRequestTime = 0.0f; + + } else { + _seekRequest = YES; + _seekRequestTime = toTime; + + } + +} + + +-(void)playItem:(CSFFMpegInput *)item +{ + dispatch_async(_input_read_queue, ^{ + + [item openMedia:15]; + + if (self.itemStarted) + { + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ self.itemStarted(item);}); + } + + self.lastVideoTime = 0.0f; + self.videoDuration = item.duration; + + + @synchronized (self) { + self.currentlyPlaying = item; + self.playing = YES; + } + [self readThread]; + }); +} + + +-(void)enqueueItem:(CSFFMpegInput *)item +{ + [self insertObject:item inInputQueueAtIndex:self.inputQueue.count]; + +} + + +-(void)play +{ + self.playing = YES; + if (!self.currentlyPlaying) + { + + [self nextItem]; + } else { + [self playItem:self.currentlyPlaying]; + } + + if (self.paused) + { + [self pause]; + } else { + + if (self.pauseStateChanged) + { + self.pauseStateChanged(); + } + } +} + +-(void)next +{ + _flushAudio = YES; + _doneDirection = 1; + [self.currentlyPlaying stop]; +} + +-(void)back +{ + _flushAudio = YES; + if (self.lastVideoTime >= 1.5) + { + [self seek:0.0]; + } else { + _doneDirection = -1; + [self.currentlyPlaying stop]; + } +} + + +-(void)stop +{ + self.playing = NO; + [self.currentlyPlaying closeMedia]; +} + +-(void)startAudio +{ + dispatch_async(_audio_queue, ^{ + + [self.pcmPlayer play]; + [self audioThread]; + }); +} + + + +-(void)audioThread +{ + + + int av_error = 0; + CAMultiAudioPCM *audioPCM = NULL; + CSFFMpegInput *useItem; + bool good_audio = NO; + + while (self.playing) + { + if (self.paused) + { + [self.pcmPlayer pause]; + return; + } + + audioPCM = [self.currentlyPlaying consumeAudioFrame:self.asbd error_out:&av_error]; + if (!self.playing) break; + if (av_error == AVERROR_EOF) + { + if (_flushAudio) + { + [self.pcmPlayer flush]; + } + break; + } + + if (audioPCM) + { + if (audioPCM.bufferCount == -1 && audioPCM.frameCount == -1) + { + if (good_audio) + { + //input needs us to flush the player, probably due to seek + [self.pcmPlayer flush]; + [self.pcmPlayer play]; + continue; + } else { + continue; + } + } + + good_audio = YES; + + + } + + + if (!self.playing) break; + + if (self.pcmPlayer.pendingFrames > 60 || av_error == AVERROR(EAGAIN)) + { + usleep(10000); + } + + if (!self.playing) break; + if (audioPCM) + { + [self.pcmPlayer playPcmBuffer:audioPCM]; + } + if (self.paused) + { + [self.pcmPlayer pause]; + return; + } + + if (!self.playing) break; + } + + _audio_done = YES; + [self inputDone]; +} + + +-(void)pause +{ + if (self.paused) + { + _first_video_pts = self.lastVideoTime / av_q2d(self.currentlyPlaying.videoTimeBase); + _first_frame_host_time = CACurrentMediaTime(); + self.paused = NO; + [self startAudio]; + } else { + self.paused = YES; + } + + if (self.pauseStateChanged) + { + self.pauseStateChanged(); + } +} + + + +-(CVPixelBufferRef)frameForMediaTime:(CFTimeInterval)mediaTime +{ + CSFFMpegInput *_useInput; + + @synchronized (self) { + _useInput = self.currentlyPlaying; + } + + if (!_useInput) + { + return nil; + } + + if (_seekRequest) + { + [self seek:_seekRequestTime]; + } + + + AVFrame *use_frame = NULL; + CVPixelBufferRef ret = nil; + int64_t audio_pts = 0; + bool play_audio = YES; + + + int av_error = 0; + + if (_first_frame_host_time == 0) + { + + play_audio = NO; + + use_frame = [_useInput consumeFrame:&av_error]; + if (use_frame) + { + + _first_frame_host_time = mediaTime; + _peek_frame = NULL; + _last_buf = nil; + audio_pts = use_frame->pkt_pts; + _first_video_pts = 0; + [self startAudio]; + } + } else { + if (!self.paused) + { + CFTimeInterval host_delta = mediaTime - _first_frame_host_time; + int64_t target_pts = host_delta / av_q2d(self.currentlyPlaying.videoTimeBase); + + if (_first_video_pts) + { + target_pts += _first_video_pts; + } else { + target_pts += _useInput.first_video_pts; + } + + audio_pts = target_pts; + + + + int consumed = 0; + use_frame = NULL; + bool do_consume = YES; + + if (_last_buf && _peek_frame) + { + + if (_peek_frame->pkt_pts > target_pts) + { + do_consume = NO; + } else { + use_frame = _peek_frame; + do_consume = YES; + } + } + + + while (do_consume && (_peek_frame = [_useInput consumeFrame:&av_error]) && _peek_frame->pkt_pts < target_pts) + { + + if (use_frame) + { + av_frame_free(&use_frame); + use_frame = _peek_frame; + } + consumed++; + } + if (av_error == AVERROR_EOF) + { + _video_done = YES; + } + + consumed++; + } + } + + if (use_frame && !_video_done) + { + + + /* + if (self.audio_needs_restart) + { + [self.pcmPlayer flush]; + [self.pcmPlayer play]; + self.audio_needs_restart = NO; + }*/ + + + self.lastVideoTime = use_frame->pkt_pts * av_q2d(_useInput.videoTimeBase); + + ret = [self convertFrameToPixelBuffer:use_frame]; + av_frame_free(&use_frame); + CVPixelBufferRetain(ret); + if (_last_buf) + { + CVPixelBufferRelease(_last_buf); + } + _last_buf = ret; + } else { + CVPixelBufferRetain(_last_buf); + ret = _last_buf; + } + + [self inputDone]; + return ret; +} + +-(void)inputDone +{ + if (_audio_done && _video_done) + { + [self.currentlyPlaying stop]; + + if (_forceNextInput) + { + [self preChangeItem]; + [self playItem:_forceNextInput]; + _forceNextInput = nil; + } else if (_doneDirection > 0) { + [self nextItem]; + } else if (_doneDirection < 0) { + [self previousItem]; + } + + } +} + + +-(bool) createPixelBufferPoolForSize:(NSSize) size +{ + + NSMutableDictionary *attributes = [NSMutableDictionary dictionary]; + [attributes setValue:[NSNumber numberWithInt:size.width] forKey:(NSString *)kCVPixelBufferWidthKey]; + [attributes setValue:[NSNumber numberWithInt:size.height] forKey:(NSString *)kCVPixelBufferHeightKey]; + [attributes setValue:@{(NSString *)kIOSurfaceIsGlobal: @NO} forKey:(NSString *)kCVPixelBufferIOSurfacePropertiesKey]; + [attributes setValue:[NSNumber numberWithUnsignedInt:kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange] forKey:(NSString *)kCVPixelBufferPixelFormatTypeKey]; + + + + if (_cvpool) + { + CVPixelBufferPoolRelease(_cvpool); + } + + + + CVReturn result = CVPixelBufferPoolCreate(NULL, NULL, (__bridge CFDictionaryRef)(attributes), &_cvpool); + + if (result != kCVReturnSuccess) + { + return NO; + } + + return YES; + + +} + +-(CVPixelBufferRef) convertFrameToPixelBuffer:(AVFrame *)av_frame +{ + if (!av_frame || !av_frame->data[0]) + { + return nil; + } + + + NSSize frameSize = NSMakeSize(av_frame->width, av_frame->height); + if (!NSEqualSizes(_currentSize, frameSize)) + { + _currentSize = frameSize; + [self createPixelBufferPoolForSize:frameSize]; + } + + + CVPixelBufferRef buf; + CVPixelBufferPoolCreatePixelBuffer(NULL, _cvpool, &buf); + int pbcnt = CVPixelBufferGetPlaneCount(buf); + + CVPixelBufferLockBaseAddress(buf, 0); + + for (int i = 0; i < pbcnt; i++) + { + uint8_t *src_addr; + uint8_t *dst_addr; + int dst_stride, src_stride; + int rows; + + dst_addr = CVPixelBufferGetBaseAddressOfPlane(buf, i); + src_addr = av_frame->data[i]; + dst_stride = CVPixelBufferGetBytesPerRowOfPlane(buf, i); + src_stride = av_frame->linesize[i]; + rows = CVPixelBufferGetHeightOfPlane(buf, i); + + if (dst_stride == src_stride) + { + memcpy(dst_addr, src_addr, src_stride * rows); + } else { + int copy_bytes = dst_stride < src_stride ? dst_stride : src_stride; + for (int j = 0; j < rows; j++) + { + memcpy(dst_addr + j * dst_stride, src_addr + j * src_stride, copy_bytes); + } + } + } + CVPixelBufferUnlockBaseAddress(buf, 0); + + return buf; +} + + +-(void)removeInputQueueAtIndexes:(NSIndexSet *)indexes +{ + [self.inputQueue removeObjectsAtIndexes:indexes]; + if (self.queueStateChanged) + { + self.queueStateChanged(); + } +} + +@end diff --git a/CSFFMpegCapturePlugin/CSFFMpegCapturePlugin/Info.plist b/CSFFMpegCapturePlugin/CSFFMpegCapturePlugin/Info.plist new file mode 100644 index 00000000..43fde119 --- /dev/null +++ b/CSFFMpegCapturePlugin/CSFFMpegCapturePlugin/Info.plist @@ -0,0 +1,28 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + NSHumanReadableCopyright + Copyright © 2016 Zakk. All rights reserved. + NSPrincipalClass + CSFFMpegCapture + + diff --git a/CSFFMpegCapturePlugin/Media.xcassets/Contents.json b/CSFFMpegCapturePlugin/Media.xcassets/Contents.json new file mode 100644 index 00000000..da4a164c --- /dev/null +++ b/CSFFMpegCapturePlugin/Media.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/CSFFMpegCapturePlugin/Media.xcassets/fastforward.imageset/Contents.json b/CSFFMpegCapturePlugin/Media.xcassets/fastforward.imageset/Contents.json new file mode 100644 index 00000000..3c0493aa --- /dev/null +++ b/CSFFMpegCapturePlugin/Media.xcassets/fastforward.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "fastforward.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/CSFFMpegCapturePlugin/Media.xcassets/fastforward.imageset/fastforward.png b/CSFFMpegCapturePlugin/Media.xcassets/fastforward.imageset/fastforward.png new file mode 100644 index 00000000..a64f2fa0 Binary files /dev/null and b/CSFFMpegCapturePlugin/Media.xcassets/fastforward.imageset/fastforward.png differ diff --git a/CSFFMpegCapturePlugin/Media.xcassets/pause.imageset/Contents.json b/CSFFMpegCapturePlugin/Media.xcassets/pause.imageset/Contents.json new file mode 100644 index 00000000..6aa82cf0 --- /dev/null +++ b/CSFFMpegCapturePlugin/Media.xcassets/pause.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "pause.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/CSFFMpegCapturePlugin/Media.xcassets/pause.imageset/pause.png b/CSFFMpegCapturePlugin/Media.xcassets/pause.imageset/pause.png new file mode 100644 index 00000000..35fc06a5 Binary files /dev/null and b/CSFFMpegCapturePlugin/Media.xcassets/pause.imageset/pause.png differ diff --git a/CSFFMpegCapturePlugin/Media.xcassets/play.imageset/Contents.json b/CSFFMpegCapturePlugin/Media.xcassets/play.imageset/Contents.json new file mode 100644 index 00000000..5f85e051 --- /dev/null +++ b/CSFFMpegCapturePlugin/Media.xcassets/play.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "play.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/CSFFMpegCapturePlugin/Media.xcassets/play.imageset/play.png b/CSFFMpegCapturePlugin/Media.xcassets/play.imageset/play.png new file mode 100644 index 00000000..3e22aaa8 Binary files /dev/null and b/CSFFMpegCapturePlugin/Media.xcassets/play.imageset/play.png differ diff --git a/CSFFMpegCapturePlugin/Media.xcassets/rewind.imageset/Contents.json b/CSFFMpegCapturePlugin/Media.xcassets/rewind.imageset/Contents.json new file mode 100644 index 00000000..436dce70 --- /dev/null +++ b/CSFFMpegCapturePlugin/Media.xcassets/rewind.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "rewind.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/CSFFMpegCapturePlugin/Media.xcassets/rewind.imageset/rewind.png b/CSFFMpegCapturePlugin/Media.xcassets/rewind.imageset/rewind.png new file mode 100644 index 00000000..401ffde0 Binary files /dev/null and b/CSFFMpegCapturePlugin/Media.xcassets/rewind.imageset/rewind.png differ diff --git a/CSNotifications.m b/CSNotifications.m index a298a2d5..1abb116d 100644 --- a/CSNotifications.m +++ b/CSNotifications.m @@ -9,7 +9,8 @@ NSString *const CSNotificationLayoutAdded = @"CSNotificationLayoutAdded"; NSString *const CSNotificationLayoutDeleted = @"CSNotificationLayoutDeleted"; - +NSString *const CSNotificationLayoutCanvasChanged = @"CSNotificationLayoutCanvasChanged"; +NSString *const CSNotificationLayoutFramerateChanged = @"CSNotificationLayoutFramerateChanged"; @@ -22,7 +23,9 @@ NSString *const CSNotificationOutputDeleted = @"CSNotificationOutputDeleted"; NSString *const CSNotificationCompressorAdded = @"CSNotificationCompressorAdded"; - NSString *const CSNotificationCompressorDeleted = @"CSNotificationCompressorDeleted"; +NSString *const CSNotificationCompressorDeleted = @"CSNotificationCompressorDeleted"; +NSString *const CSNotificationCompressorRenamed = @"CSNotificationCompressorRenamed"; +NSString *const CSNotificationCompressorReconfigured = @"CSNotificationCompressorReconfigured"; NSString *const CSNotificationInputAdded = @"CSNotificationInputAdded"; @@ -30,5 +33,9 @@ NSString *const CSNotificationInputDeleted = @"CSNotificationInputDeleted"; NSString *const CSNotificationInputSelected = @"CSNotificationInputSelected"; +NSString *const CSNotificationInputAttached = @"CSNotificationInputAttached"; +NSString *const CSNotificationInputDetached = @"CSNotificationInputDetached"; + +NSString *const CSNotificationLayoutModeChanged = @"CSNotificationLayoutModeChanged"; diff --git a/CapturePlugins/CSAVFCapturePlugin/CSAVFCapturePlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/CSAVFCapturePlugin.xcscheme b/CapturePlugins/CSAVFCapturePlugin/CSAVFCapturePlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/CSAVFCapturePlugin.xcscheme index 756650d1..e770f7d2 100644 --- a/CapturePlugins/CSAVFCapturePlugin/CSAVFCapturePlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/CSAVFCapturePlugin.xcscheme +++ b/CapturePlugins/CSAVFCapturePlugin/CSAVFCapturePlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/CSAVFCapturePlugin.xcscheme @@ -1,6 +1,6 @@ +>>>>>> 2.0UI + version = "1.3"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CapturePlugins/CSAVFCapturePlugin/CSAVFCapturePlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/CSAVFCapturePlugin_BACKUP_26694.xcscheme b/CapturePlugins/CSAVFCapturePlugin/CSAVFCapturePlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/CSAVFCapturePlugin_BACKUP_26694.xcscheme new file mode 100644 index 00000000..fe99fd2b --- /dev/null +++ b/CapturePlugins/CSAVFCapturePlugin/CSAVFCapturePlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/CSAVFCapturePlugin_BACKUP_26694.xcscheme @@ -0,0 +1,75 @@ + +>>>>>> 2.0UI + version = "1.3"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CapturePlugins/CSAVFCapturePlugin/CSAVFCapturePlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/CSAVFCapturePlugin_BASE_26129.xcscheme b/CapturePlugins/CSAVFCapturePlugin/CSAVFCapturePlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/CSAVFCapturePlugin_BASE_26129.xcscheme new file mode 100644 index 00000000..e74808be --- /dev/null +++ b/CapturePlugins/CSAVFCapturePlugin/CSAVFCapturePlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/CSAVFCapturePlugin_BASE_26129.xcscheme @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CapturePlugins/CSAVFCapturePlugin/CSAVFCapturePlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/CSAVFCapturePlugin_BASE_26694.xcscheme b/CapturePlugins/CSAVFCapturePlugin/CSAVFCapturePlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/CSAVFCapturePlugin_BASE_26694.xcscheme new file mode 100644 index 00000000..e74808be --- /dev/null +++ b/CapturePlugins/CSAVFCapturePlugin/CSAVFCapturePlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/CSAVFCapturePlugin_BASE_26694.xcscheme @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CapturePlugins/CSAVFCapturePlugin/CSAVFCapturePlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/CSAVFCapturePlugin_LOCAL_26129.xcscheme b/CapturePlugins/CSAVFCapturePlugin/CSAVFCapturePlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/CSAVFCapturePlugin_LOCAL_26129.xcscheme new file mode 100644 index 00000000..756650d1 --- /dev/null +++ b/CapturePlugins/CSAVFCapturePlugin/CSAVFCapturePlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/CSAVFCapturePlugin_LOCAL_26129.xcscheme @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CapturePlugins/CSAVFCapturePlugin/CSAVFCapturePlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/CSAVFCapturePlugin_LOCAL_26694.xcscheme b/CapturePlugins/CSAVFCapturePlugin/CSAVFCapturePlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/CSAVFCapturePlugin_LOCAL_26694.xcscheme new file mode 100644 index 00000000..756650d1 --- /dev/null +++ b/CapturePlugins/CSAVFCapturePlugin/CSAVFCapturePlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/CSAVFCapturePlugin_LOCAL_26694.xcscheme @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CapturePlugins/CSAVFCapturePlugin/CSAVFCapturePlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/CSAVFCapturePlugin_REMOTE_26129.xcscheme b/CapturePlugins/CSAVFCapturePlugin/CSAVFCapturePlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/CSAVFCapturePlugin_REMOTE_26129.xcscheme new file mode 100644 index 00000000..e770f7d2 --- /dev/null +++ b/CapturePlugins/CSAVFCapturePlugin/CSAVFCapturePlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/CSAVFCapturePlugin_REMOTE_26129.xcscheme @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CapturePlugins/CSAVFCapturePlugin/CSAVFCapturePlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/CSAVFCapturePlugin_REMOTE_26694.xcscheme b/CapturePlugins/CSAVFCapturePlugin/CSAVFCapturePlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/CSAVFCapturePlugin_REMOTE_26694.xcscheme new file mode 100644 index 00000000..e770f7d2 --- /dev/null +++ b/CapturePlugins/CSAVFCapturePlugin/CSAVFCapturePlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/CSAVFCapturePlugin_REMOTE_26694.xcscheme @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CapturePlugins/CSAVFCapturePlugin/CSAVFCapturePlugin/AVFCapture.m b/CapturePlugins/CSAVFCapturePlugin/CSAVFCapturePlugin/AVFCapture.m index 118cdf7a..3c8f9f02 100644 --- a/CapturePlugins/CSAVFCapturePlugin/CSAVFCapturePlugin/AVFCapture.m +++ b/CapturePlugins/CSAVFCapturePlugin/CSAVFCapturePlugin/AVFCapture.m @@ -233,6 +233,7 @@ [_capture_session registerOutput:self]; + self.captureName = newDev.captureName; self.videoFormats = _selectedVideoCaptureDevice.formats; diff --git a/CapturePlugins/CSDeckLinkCapturePlugin/CSDeckLinkCapturePlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/CSDeckLinkCapturePlugin.xcscheme b/CapturePlugins/CSDeckLinkCapturePlugin/CSDeckLinkCapturePlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/CSDeckLinkCapturePlugin.xcscheme new file mode 100644 index 00000000..8c8adacf --- /dev/null +++ b/CapturePlugins/CSDeckLinkCapturePlugin/CSDeckLinkCapturePlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/CSDeckLinkCapturePlugin.xcscheme @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CapturePlugins/CSDeckLinkCapturePlugin/CSDeckLinkCapturePlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/xcschememanagement.plist b/CapturePlugins/CSDeckLinkCapturePlugin/CSDeckLinkCapturePlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 00000000..e9670f11 --- /dev/null +++ b/CapturePlugins/CSDeckLinkCapturePlugin/CSDeckLinkCapturePlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,22 @@ + + + + + SchemeUserState + + CSDeckLinkCapturePlugin.xcscheme + + orderHint + 21 + + + SuppressBuildableAutocreation + + 34F7C54F1B2C908700345230 + + primary + + + + + diff --git a/CapturePlugins/CSDeckLinkCapturePlugin/CSDeckLinkCapturePlugin/CSDeckLinkCapture.mm b/CapturePlugins/CSDeckLinkCapturePlugin/CSDeckLinkCapturePlugin/CSDeckLinkCapture.mm index d9411edf..ec9c0eac 100644 --- a/CapturePlugins/CSDeckLinkCapturePlugin/CSDeckLinkCapturePlugin/CSDeckLinkCapture.mm +++ b/CapturePlugins/CSDeckLinkCapturePlugin/CSDeckLinkCapturePlugin/CSDeckLinkCapture.mm @@ -134,9 +134,10 @@ { super.activeVideoDevice = activeVideoDevice; - + if (activeVideoDevice) { + CSDeckLinkWrapper *devWrapper = activeVideoDevice.captureDevice; IDeckLink *deckLink = devWrapper.deckLink; diff --git a/CapturePlugins/CSDeckLinkCapturePlugin/CSDeckLinkCapturePlugin/CSDeckLinkCaptureViewController.xib b/CapturePlugins/CSDeckLinkCapturePlugin/CSDeckLinkCapturePlugin/CSDeckLinkCaptureViewController.xib index 6a137fa9..a728cebd 100644 --- a/CapturePlugins/CSDeckLinkCapturePlugin/CSDeckLinkCapturePlugin/CSDeckLinkCaptureViewController.xib +++ b/CapturePlugins/CSDeckLinkCapturePlugin/CSDeckLinkCapturePlugin/CSDeckLinkCaptureViewController.xib @@ -1,7 +1,7 @@ - + - + diff --git a/CapturePlugins/CSDeckLinkCapturePlugin/CSDeckLinkCapturePlugin/CSDeckLinkLayer.h b/CapturePlugins/CSDeckLinkCapturePlugin/CSDeckLinkCapturePlugin/CSDeckLinkLayer.h index 03ad5076..bae4420a 100644 --- a/CapturePlugins/CSDeckLinkCapturePlugin/CSDeckLinkCapturePlugin/CSDeckLinkLayer.h +++ b/CapturePlugins/CSDeckLinkCapturePlugin/CSDeckLinkCapturePlugin/CSDeckLinkLayer.h @@ -9,6 +9,7 @@ #import #import #import + #import "DeckLinkBridge.h" @@ -16,7 +17,7 @@ { CGLContextObj _myCGLContext; CGRect _lastBounds; - CGSize _lastImageSize; + NSRect _lastImageSize; CGRect _privateCropRect; CGRect _lastCrop; CGRect _calculatedCrop; diff --git a/CapturePlugins/CSDeckLinkCapturePlugin/CSDeckLinkCapturePlugin/CSDeckLinkLayer.mm b/CapturePlugins/CSDeckLinkCapturePlugin/CSDeckLinkCapturePlugin/CSDeckLinkLayer.mm index be2a625f..523c2ef0 100644 --- a/CapturePlugins/CSDeckLinkCapturePlugin/CSDeckLinkCapturePlugin/CSDeckLinkLayer.mm +++ b/CapturePlugins/CSDeckLinkCapturePlugin/CSDeckLinkCapturePlugin/CSDeckLinkLayer.mm @@ -25,20 +25,15 @@ } -/* -(void)setContentsRect:(CGRect)contentsRect { _privateCropRect = contentsRect; - [self calculateCrop:_lastImageSize]; - _needsRedraw = YES; - [self setNeedsDisplay]; } -(CGRect)contentsRect { return _privateCropRect; } -*/ -(CGLContextObj)copyCGLContextForPixelFormat:(CGLPixelFormatObj)pf @@ -86,16 +81,23 @@ NSSize useSize = self.bounds.size; + CGRect newCrop; + + newCrop.origin.x = _deckLinkFrameSize.width * _privateCropRect.origin.x; + newCrop.origin.y = _deckLinkFrameSize.height * _privateCropRect.origin.y; + newCrop.size.width = _deckLinkFrameSize.width * _privateCropRect.size.width; + newCrop.size.height = _deckLinkFrameSize.height * _privateCropRect.size.height; if ([self.contentsGravity isEqualToString:kCAGravityResizeAspect]) { - float wr = _deckLinkFrameSize.width / self.bounds.size.width; - float hr = _deckLinkFrameSize.height / self.bounds.size.height; + float wr = newCrop.size.width / self.bounds.size.width; + float hr = newCrop.size.height / self.bounds.size.height; float ratio = (hr < wr ? wr : hr); - useSize = NSMakeSize(_deckLinkFrameSize.width / ratio, _deckLinkFrameSize.height / ratio); + useSize = NSMakeSize(newCrop.size.width / ratio, newCrop.size.height / ratio); } + GLint vpx = (self.bounds.size.width - useSize.width)/2; GLint vpy = (self.bounds.size.height - useSize.height)/2; @@ -103,20 +105,30 @@ glMatrixMode(GL_PROJECTION); glLoadIdentity(); + glMatrixMode(GL_MODELVIEW); glLoadIdentity(); + NSRect cropRect; + glMatrixMode(GL_TEXTURE); + glLoadIdentity(); + + + GLfloat yt = 1.0 - (_privateCropRect.origin.y + _privateCropRect.size.height); + + + glScalef(_privateCropRect.size.width, _privateCropRect.size.height, 1); + glTranslatef(_privateCropRect.origin.x * _deckLinkFrameSize.width, yt * _deckLinkFrameSize.height, 0); + + if (_deckLinkOGL) { _deckLinkOGL->PaintGL(); } - /* - glTranslated(self.bounds.size.width * 0.5, self.bounds.size.height * 0.5, 0.0); - */ [super drawInCGLContext:ctx pixelFormat:pf forLayerTime:t displayTime:ts]; diff --git a/CapturePlugins/CSDesktopCapturePlugin/CSDesktopCapturePlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/CSDesktopCapturePlugin.xcscheme b/CapturePlugins/CSDesktopCapturePlugin/CSDesktopCapturePlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/CSDesktopCapturePlugin.xcscheme index 3f9a5ade..48ea46af 100644 --- a/CapturePlugins/CSDesktopCapturePlugin/CSDesktopCapturePlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/CSDesktopCapturePlugin.xcscheme +++ b/CapturePlugins/CSDesktopCapturePlugin/CSDesktopCapturePlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/CSDesktopCapturePlugin.xcscheme @@ -1,6 +1,6 @@ - + - + @@ -71,12 +71,12 @@ - - + + - + @@ -108,9 +108,11 @@ @@ -162,8 +164,8 @@ - + @@ -201,8 +203,8 @@ - + diff --git a/CapturePlugins/CSNowPlayingPlugin/CSNowPlayingPlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/CSNowPlayingPlugin.xcscheme b/CapturePlugins/CSNowPlayingPlugin/CSNowPlayingPlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/CSNowPlayingPlugin.xcscheme new file mode 100644 index 00000000..d95dd2a2 --- /dev/null +++ b/CapturePlugins/CSNowPlayingPlugin/CSNowPlayingPlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/CSNowPlayingPlugin.xcscheme @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CapturePlugins/CSNowPlayingPlugin/CSNowPlayingPlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/xcschememanagement.plist b/CapturePlugins/CSNowPlayingPlugin/CSNowPlayingPlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 00000000..9403df5f --- /dev/null +++ b/CapturePlugins/CSNowPlayingPlugin/CSNowPlayingPlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,22 @@ + + + + + SchemeUserState + + CSNowPlayingPlugin.xcscheme + + orderHint + 23 + + + SuppressBuildableAutocreation + + 34D2D58E1A54857C001004E5 + + primary + + + + + diff --git a/CapturePlugins/CSQTCapturePlugin/CSQTCapturePlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/CSQTCapturePlugin.xcscheme b/CapturePlugins/CSQTCapturePlugin/CSQTCapturePlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/CSQTCapturePlugin.xcscheme index e752c9b1..522abb78 100644 --- a/CapturePlugins/CSQTCapturePlugin/CSQTCapturePlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/CSQTCapturePlugin.xcscheme +++ b/CapturePlugins/CSQTCapturePlugin/CSQTCapturePlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/CSQTCapturePlugin.xcscheme @@ -1,6 +1,6 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CapturePlugins/CSShapeCapturePlugin/CSShapeCapturePlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/xcschememanagement.plist b/CapturePlugins/CSShapeCapturePlugin/CSShapeCapturePlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 00000000..7175dcab --- /dev/null +++ b/CapturePlugins/CSShapeCapturePlugin/CSShapeCapturePlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,22 @@ + + + + + SchemeUserState + + CSShapeCapturePlugin.xcscheme + + orderHint + 20 + + + SuppressBuildableAutocreation + + 343692101B62364E007EC2D9 + + primary + + + + + diff --git a/CapturePlugins/CSShapeCapturePlugin/CSShapeCapturePlugin/CSShapePathPlugin/CSShapePathLoader.py b/CapturePlugins/CSShapeCapturePlugin/CSShapeCapturePlugin/CSShapePathPlugin/CSShapePathLoader.py index 92e95f56..2528cf8b 100644 --- a/CapturePlugins/CSShapeCapturePlugin/CSShapeCapturePlugin/CSShapePathPlugin/CSShapePathLoader.py +++ b/CapturePlugins/CSShapeCapturePlugin/CSShapeCapturePlugin/CSShapePathPlugin/CSShapePathLoader.py @@ -16,7 +16,7 @@ plugin_base = PluginBase(package='shapeplugins') library_dirs = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSAllDomainsMask - NSSystemDomainMask, YES) plugin_dirs = map(lambda x: x + "/Application Support/CocoaSplit/Plugins/Paths", library_dirs) -plugin_dirs.append(NSBundle.bundleForClass_(objc.lookUpClass("CSShapeCapture").class__()).builtInPlugInsPath() + "/Paths") +plugin_dirs.append(NSBundle.bundleForClass_(objc.lookUpClass("CSShapeCapture").class__()).resourcePath() + "/Paths") plugin_source = plugin_base.make_plugin_source(searchpath=plugin_dirs) diff --git a/CapturePlugins/CSShapeCapturePlugin/CSShapeCapturePlugin/ShapePaths/rectangle.py b/CapturePlugins/CSShapeCapturePlugin/CSShapeCapturePlugin/ShapePaths/rectangle.py new file mode 100644 index 00000000..5059ee3b --- /dev/null +++ b/CapturePlugins/CSShapeCapturePlugin/CSShapeCapturePlugin/ShapePaths/rectangle.py @@ -0,0 +1,8 @@ +from Quartz import CGPathCreateMutable, CGPathAddRect + +name = "Rectangle" + +def create_cgpath(frame): + newpath = CGPathCreateMutable() + CGPathAddRect(newpath, None, frame) + return newpath diff --git a/CapturePlugins/CSSyphonCapturePlugin/CSSyphonCapturePlugin.xcodeproj/project.pbxproj b/CapturePlugins/CSSyphonCapturePlugin/CSSyphonCapturePlugin.xcodeproj/project.pbxproj index 27a3f77f..a187dbb4 100644 --- a/CapturePlugins/CSSyphonCapturePlugin/CSSyphonCapturePlugin.xcodeproj/project.pbxproj +++ b/CapturePlugins/CSSyphonCapturePlugin/CSSyphonCapturePlugin.xcodeproj/project.pbxproj @@ -9,13 +9,13 @@ /* Begin PBXBuildFile section */ 3429A0491A41063900EE702C /* CSSyphonInjectCaptureViewNotInstalled.xib in Resources */ = {isa = PBXBuildFile; fileRef = 3429A0481A41063900EE702C /* CSSyphonInjectCaptureViewNotInstalled.xib */; }; 3479A99A1A91C5A500A524F0 /* CSSyphonCaptureLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 3479A9991A91C5A500A524F0 /* CSSyphonCaptureLayer.m */; }; - 349CA6A71BC04D1B0010678D /* Syphon.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 34C67B4A1A2A4D870012DC1B /* Syphon.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 34AFC1D719B04EB90007C07B /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 34AFC1D619B04EB90007C07B /* Cocoa.framework */; }; 34AFC1E119B04EB90007C07B /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 34AFC1DF19B04EB90007C07B /* InfoPlist.strings */; }; 34AFC1F319B04F0B0007C07B /* SyphonCaptureViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 34AFC1EF19B04F0B0007C07B /* SyphonCaptureViewController.m */; }; 34AFC1F419B04F0B0007C07B /* SyphonCaptureViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 34AFC1F019B04F0B0007C07B /* SyphonCaptureViewController.xib */; }; 34AFC1F519B04F0B0007C07B /* SyphonCapture.m in Sources */ = {isa = PBXBuildFile; fileRef = 34AFC1F219B04F0B0007C07B /* SyphonCapture.m */; }; - 34C67B4F1A2A56A20012DC1B /* Syphon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 34C67B4A1A2A4D870012DC1B /* Syphon.framework */; }; + 34C67B4E1A2A4ECB0012DC1B /* Syphon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 34C67B4A1A2A4D870012DC1B /* Syphon.framework */; }; + 34C67B4F1A2A56A20012DC1B /* Syphon.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 34C67B4A1A2A4D870012DC1B /* Syphon.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 34C7B8C91A344DB20033AC71 /* CSSyphonInjectCapture.m in Sources */ = {isa = PBXBuildFile; fileRef = 34C7B8C81A344DB20033AC71 /* CSSyphonInjectCapture.m */; }; 34C7B8CC1A34621C0033AC71 /* CSSyphonCaptureFactory.m in Sources */ = {isa = PBXBuildFile; fileRef = 34C7B8CB1A34621C0033AC71 /* CSSyphonCaptureFactory.m */; }; 34C7B8DB1A3465010033AC71 /* CSSyphonInjectCaptureViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 34C7B8D91A3465010033AC71 /* CSSyphonInjectCaptureViewController.m */; }; @@ -47,7 +47,7 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( - 349CA6A71BC04D1B0010678D /* Syphon.framework in CopyFiles */, + 34C67B4F1A2A56A20012DC1B /* Syphon.framework in CopyFiles */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -101,7 +101,7 @@ files = ( 34C7B8DE1A3562400033AC71 /* ScriptingBridge.framework in Frameworks */, 34AFC1D719B04EB90007C07B /* Cocoa.framework in Frameworks */, - 34C67B4F1A2A56A20012DC1B /* Syphon.framework in Frameworks */, + 34C67B4E1A2A4ECB0012DC1B /* Syphon.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/CapturePlugins/CSSyphonCapturePlugin/CSSyphonCapturePlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/CSSyphonCapturePlugin.xcscheme b/CapturePlugins/CSSyphonCapturePlugin/CSSyphonCapturePlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/CSSyphonCapturePlugin.xcscheme index 022ba4fb..9f81cedd 100644 --- a/CapturePlugins/CSSyphonCapturePlugin/CSSyphonCapturePlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/CSSyphonCapturePlugin.xcscheme +++ b/CapturePlugins/CSSyphonCapturePlugin/CSSyphonCapturePlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/CSSyphonCapturePlugin.xcscheme @@ -1,6 +1,6 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CapturePlugins/CSTimeCapturePlugin/CSTimeCapturePlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/xcschememanagement.plist b/CapturePlugins/CSTimeCapturePlugin/CSTimeCapturePlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 00000000..08e057fc --- /dev/null +++ b/CapturePlugins/CSTimeCapturePlugin/CSTimeCapturePlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,22 @@ + + + + + SchemeUserState + + CSTimeCapturePlugin.xcscheme + + orderHint + 22 + + + SuppressBuildableAutocreation + + 34FDA7FC1A84BF9300E7F65E + + primary + + + + + diff --git a/CapturePlugins/CSTimeCapturePlugin/CSTimeIntervalBase.m b/CapturePlugins/CSTimeCapturePlugin/CSTimeIntervalBase.m index 233ebce9..9b82a958 100644 --- a/CapturePlugins/CSTimeCapturePlugin/CSTimeIntervalBase.m +++ b/CapturePlugins/CSTimeCapturePlugin/CSTimeIntervalBase.m @@ -58,6 +58,18 @@ } +-(NSImage *)libraryImage +{ + NSString *calPath = [[NSWorkspace sharedWorkspace] absolutePathForAppBundleWithIdentifier:@"com.apple.iCal"]; + + if (calPath) + { + return [[NSWorkspace sharedWorkspace] iconForFile:calPath]; + } + return nil; +} + + -(void)setFormat:(NSString *)format { _format = format; diff --git a/CapturePlugins/CSWindowCapturePlugin/CSWindowCapturePlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/CSWindowCapturePlugin.xcscheme b/CapturePlugins/CSWindowCapturePlugin/CSWindowCapturePlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/CSWindowCapturePlugin.xcscheme index 25ca9814..c4ad11fd 100644 --- a/CapturePlugins/CSWindowCapturePlugin/CSWindowCapturePlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/CSWindowCapturePlugin.xcscheme +++ b/CapturePlugins/CSWindowCapturePlugin/CSWindowCapturePlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/CSWindowCapturePlugin.xcscheme @@ -1,6 +1,6 @@ - + - + @@ -18,6 +18,7 @@ + This capture method sucks. The API for grabbing window images really isn't made for video framerates and depending on what window you are capturing it can't keep up. Set the framerate for the source low if you can get away with it. Otherwise expect to drop lots of frames. @@ -27,6 +28,7 @@ + @@ -35,6 +37,7 @@ + @@ -45,6 +48,7 @@ + diff --git a/CocoaSplit.xcodeproj/project.pbxproj b/CocoaSplit.xcodeproj/project.pbxproj index a95ae826..69756f9a 100644 --- a/CocoaSplit.xcodeproj/project.pbxproj +++ b/CocoaSplit.xcodeproj/project.pbxproj @@ -8,8 +8,15 @@ /* Begin PBXBuildFile section */ 3400777016E6FE5D00097E8F /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 347B7FC916907AFA00B5F4B3 /* libz.dylib */; }; + 3400A0621BD44DF0003E1828 /* CSInputLibraryItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 3400A0611BD44DF0003E1828 /* CSInputLibraryItem.m */; }; + 3400A0791BD4533A003E1828 /* CSInputLibraryWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3400A0771BD4533A003E1828 /* CSInputLibraryWindowController.m */; }; + 3400A07A1BD4533A003E1828 /* CSInputLibraryWindowController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 3400A0781BD4533A003E1828 /* CSInputLibraryWindowController.xib */; }; + 3400A07E1BD49406003E1828 /* CSInputLibraryItemView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 3400A07D1BD49406003E1828 /* CSInputLibraryItemView.xib */; }; + 3400A0841BD522A5003E1828 /* CSLibraryInputItemViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3400A0831BD522A5003E1828 /* CSLibraryInputItemViewController.m */; }; 3408235519BC677A00CD1F5F /* CSNotifications.m in Sources */ = {isa = PBXBuildFile; fileRef = 3408235419BC530300CD1F5F /* CSNotifications.m */; }; 3408235619BC677C00CD1F5F /* CSNotifications.m in Sources */ = {isa = PBXBuildFile; fileRef = 3408235419BC530300CD1F5F /* CSNotifications.m */; }; + 340C7FE91D43D22000B2FD7D /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 340C7FE81D43D22000B2FD7D /* Security.framework */; }; + 340C802D1D450A5100B2FD7D /* CSYoutubeStreamServicePlugin.bundle in Copy Files */ = {isa = PBXBuildFile; fileRef = 340C80151D44722100B2FD7D /* CSYoutubeStreamServicePlugin.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 340D8C971959C7C100BE5144 /* CompressionSettingsPanel.xib in Resources */ = {isa = PBXBuildFile; fileRef = 340D8C961959C7C100BE5144 /* CompressionSettingsPanel.xib */; }; 340DC734197CF6E9003A0BB3 /* CSCaptureBase.m in Sources */ = {isa = PBXBuildFile; fileRef = 340DC733197CF6E9003A0BB3 /* CSCaptureBase.m */; }; 340DC735197CF6E9003A0BB3 /* CSCaptureBase.m in Sources */ = {isa = PBXBuildFile; fileRef = 340DC733197CF6E9003A0BB3 /* CSCaptureBase.m */; }; @@ -22,7 +29,11 @@ 340FE4DA15F346D900E4CE4E /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 340FE4D815F346CC00E4CE4E /* AVFoundation.framework */; }; 340FE4E115F3581200E4CE4E /* QTKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 340FE4E015F3581200E4CE4E /* QTKit.framework */; }; 340FE50815F444AA00E4CE4E /* CaptureController.m in Sources */ = {isa = PBXBuildFile; fileRef = 340FE50715F444AA00E4CE4E /* CaptureController.m */; }; + 3414047D1D76EA3200505A5D /* CSSyphonCapturePlugin.bundle in Copy Files */ = {isa = PBXBuildFile; fileRef = 34AFC26819B05B7C0007C07B /* CSSyphonCapturePlugin.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 3414C6AE1CBB9EBF00107C69 /* CSIRCompressor.m in Sources */ = {isa = PBXBuildFile; fileRef = 3414C6AD1CBB9EBF00107C69 /* CSIRCompressor.m */; }; 3415CED31AE257DA002F11F5 /* CSTextCaptureBaseView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 3415CED21AE257DA002F11F5 /* CSTextCaptureBaseView.xib */; }; + 341A2D5C1C7AB04700FF7566 /* Sparkle.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 341A2D5B1C7AB04700FF7566 /* Sparkle.framework */; }; + 341A2D5D1C7AB0D100FF7566 /* Sparkle.framework in Copy Files */ = {isa = PBXBuildFile; fileRef = 341A2D5B1C7AB04700FF7566 /* Sparkle.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 34210B3E1606001200362FC8 /* OutputDestination.m in Sources */ = {isa = PBXBuildFile; fileRef = 34210B3D1606001100362FC8 /* OutputDestination.m */; }; 34213E1A196680880054E238 /* CompressorBase.m in Sources */ = {isa = PBXBuildFile; fileRef = 34213E19196680880054E238 /* CompressorBase.m */; }; 34213E1B196680880054E238 /* CompressorBase.m in Sources */ = {isa = PBXBuildFile; fileRef = 34213E19196680880054E238 /* CompressorBase.m */; }; @@ -33,29 +44,40 @@ 342346BE15F6103D00C8C77E /* FFMpegTask.m in Sources */ = {isa = PBXBuildFile; fileRef = 342346BD15F6103D00C8C77E /* FFMpegTask.m */; }; 342346CF15F9F07E00C8C77E /* CSAbstractCaptureDevice.m in Sources */ = {isa = PBXBuildFile; fileRef = 342346CE15F9F07E00C8C77E /* CSAbstractCaptureDevice.m */; }; 342B33B3198073EC00492CB7 /* line.fgsh in Resources */ = {isa = PBXBuildFile; fileRef = 342B33B2198073EC00492CB7 /* line.fgsh */; }; - 342B33B4198075F200492CB7 /* line.fgsh in CopyFiles */ = {isa = PBXBuildFile; fileRef = 342B33B2198073EC00492CB7 /* line.fgsh */; }; + 342B33B4198075F200492CB7 /* line.fgsh in CopyFiles */ = {isa = PBXBuildFile; fileRef = 342B33B2198073EC00492CB7 /* line.fgsh */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 342B33B61980771200492CB7 /* line.vtsh in Resources */ = {isa = PBXBuildFile; fileRef = 342B33B51980771200492CB7 /* line.vtsh */; }; - 342B33B71980774000492CB7 /* line.vtsh in CopyFiles */ = {isa = PBXBuildFile; fileRef = 342B33B51980771200492CB7 /* line.vtsh */; }; + 342B33B71980774000492CB7 /* line.vtsh in CopyFiles */ = {isa = PBXBuildFile; fileRef = 342B33B51980771200492CB7 /* line.vtsh */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 3431FFE119786502000965FE /* InputSource.m in Sources */ = {isa = PBXBuildFile; fileRef = 3431FFE019786502000965FE /* InputSource.m */; }; 3431FFE219786502000965FE /* InputSource.m in Sources */ = {isa = PBXBuildFile; fileRef = 3431FFE019786502000965FE /* InputSource.m */; }; 34348C2A19BDBDC000A122C2 /* PluginManagerWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 34348C2819BDBDC000A122C2 /* PluginManagerWindowController.m */; }; 34348C2B19BDBDC000A122C2 /* PluginManagerWindowController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 34348C2919BDBDC000A122C2 /* PluginManagerWindowController.xib */; }; 34348C3919BDC0D800A122C2 /* PluginManagerWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 34348C2819BDBDC000A122C2 /* PluginManagerWindowController.m */; }; + 3434CB281CA8615A001B3DF9 /* AppleProResCompressor.m in Sources */ = {isa = PBXBuildFile; fileRef = 3434CB271CA8615A001B3DF9 /* AppleProResCompressor.m */; }; + 3434CB2D1CA90D28001B3DF9 /* CSx264CompressorViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3434CB2B1CA90D28001B3DF9 /* CSx264CompressorViewController.m */; }; + 3434CB2E1CA90D28001B3DF9 /* CSx264CompressorViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 3434CB2C1CA90D28001B3DF9 /* CSx264CompressorViewController.xib */; }; + 3434CB321CA91D99001B3DF9 /* CSAppleH264CompressorViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3434CB301CA91D99001B3DF9 /* CSAppleH264CompressorViewController.m */; }; + 3434CB331CA91D99001B3DF9 /* CSAppleH264CompressorViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 3434CB311CA91D99001B3DF9 /* CSAppleH264CompressorViewController.xib */; }; + 3434CB381CA93BCD001B3DF9 /* CSAppleProResCompressorViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3434CB361CA93BCD001B3DF9 /* CSAppleProResCompressorViewController.m */; }; + 3434CB391CA93BCD001B3DF9 /* CSAppleProResCompressorViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 3434CB371CA93BCD001B3DF9 /* CSAppleProResCompressorViewController.xib */; }; + 3434CB3C1CB09BC4001B3DF9 /* CSTimedOutputBuffer.m in Sources */ = {isa = PBXBuildFile; fileRef = 3434CB3B1CB09BC4001B3DF9 /* CSTimedOutputBuffer.m */; }; 3435E396188B902C0015CC01 /* 420v.fgsh in Resources */ = {isa = PBXBuildFile; fileRef = 3435E395188B902C0015CC01 /* 420v.fgsh */; }; 3435E39B188B93150015CC01 /* passthrough.vtsh in Resources */ = {isa = PBXBuildFile; fileRef = 3435E39A188B93150015CC01 /* passthrough.vtsh */; }; - 3435E39D188BD8250015CC01 /* passthrough.fgsh in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3435E399188B90900015CC01 /* passthrough.fgsh */; }; - 3435E39E188BD82F0015CC01 /* passthrough.vtsh in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3435E39A188B93150015CC01 /* passthrough.vtsh */; }; - 3435E39F188CD8EE0015CC01 /* 420v.fgsh in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3435E395188B902C0015CC01 /* 420v.fgsh */; }; - 343644061B78F01D0069B8F2 /* CSAnimationRunner.py in CopyFiles */ = {isa = PBXBuildFile; fileRef = 349461591AB4213B00F28883 /* CSAnimationRunner.py */; }; - 343644071B78F01D0069B8F2 /* CSAnimationBlock.py in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3494616A1ABD1B5900F28883 /* CSAnimationBlock.py */; }; - 343644081B78F01D0069B8F2 /* CSAnimation.py in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3494616C1ABD399B00F28883 /* CSAnimation.py */; }; - 343644091B78F01D0069B8F2 /* pluginbase.py in CopyFiles */ = {isa = PBXBuildFile; fileRef = 34E983841B78B19D00F26F1E /* pluginbase.py */; }; - 343692501B6243D4007EC2D9 /* CSShapeCapturePlugin.bundle in Copy Files */ = {isa = PBXBuildFile; fileRef = 3436922B1B62364E007EC2D9 /* CSShapeCapturePlugin.bundle */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + 3435E39D188BD8250015CC01 /* passthrough.fgsh in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3435E399188B90900015CC01 /* passthrough.fgsh */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; + 3435E39E188BD82F0015CC01 /* passthrough.vtsh in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3435E39A188B93150015CC01 /* passthrough.vtsh */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; + 3435E39F188CD8EE0015CC01 /* 420v.fgsh in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3435E395188B902C0015CC01 /* 420v.fgsh */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; + 343644061B78F01D0069B8F2 /* CSAnimationRunner.py in CopyFiles */ = {isa = PBXBuildFile; fileRef = 349461591AB4213B00F28883 /* CSAnimationRunner.py */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; + 343644071B78F01D0069B8F2 /* CSAnimationBlock.py in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3494616A1ABD1B5900F28883 /* CSAnimationBlock.py */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; + 343644081B78F01D0069B8F2 /* CSAnimation.py in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3494616C1ABD399B00F28883 /* CSAnimation.py */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; + 343644091B78F01D0069B8F2 /* pluginbase.py in CopyFiles */ = {isa = PBXBuildFile; fileRef = 34E983841B78B19D00F26F1E /* pluginbase.py */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; + 343692501B6243D4007EC2D9 /* CSShapeCapturePlugin.bundle in Copy Files */ = {isa = PBXBuildFile; fileRef = 3436922B1B62364E007EC2D9 /* CSShapeCapturePlugin.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 343C79ED1D0C796E00B36EEC /* CSFFMpegCapturePlugin.bundle in Copy Files */ = {isa = PBXBuildFile; fileRef = 343C79CC1D0C763A00B36EEC /* CSFFMpegCapturePlugin.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 343ED9421BCAC1C80061FC4B /* EditBuiltinLayoutView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 343ED9411BCAC1C80061FC4B /* EditBuiltinLayoutView.xib */; }; 3444D8E819A0CDE8000DC46B /* Quartz.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3444D8E719A0CDE8000DC46B /* Quartz.framework */; }; 344825D21B2536EA00AF9EAC /* CAMultiAudioMatrixView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 344825D11B2536EA00AF9EAC /* CAMultiAudioMatrixView.xib */; }; 344825E61B253D6900AF9EAC /* CAMultiAudioMatrixCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 344825E51B253D6900AF9EAC /* CAMultiAudioMatrixCell.m */; }; 344825E71B253D6900AF9EAC /* CAMultiAudioMatrixCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 344825E51B253D6900AF9EAC /* CAMultiAudioMatrixCell.m */; }; 344988131893CF0500044259 /* LogWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 344988121893CF0500044259 /* LogWindow.xib */; }; + 3450239D1C60B3EF00B461B5 /* CSLayoutCollectionItemView.m in Sources */ = {isa = PBXBuildFile; fileRef = 3450239C1C60B3EF00B461B5 /* CSLayoutCollectionItemView.m */; }; 3451A1C417111BD900DF6A8B /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 34AE3C2F164E3E020052C95E /* Foundation.framework */; }; 3451A1C717111BD900DF6A8B /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 3451A1C617111BD900DF6A8B /* main.m */; }; 3451A1D31712C41000DF6A8B /* CaptureController.m in Sources */ = {isa = PBXBuildFile; fileRef = 340FE50715F444AA00E4CE4E /* CaptureController.m */; }; @@ -112,12 +134,25 @@ 345F8B7A1A183D1D009A81E3 /* CAMultiAudioEngine.m in Sources */ = {isa = PBXBuildFile; fileRef = 345F8B781A183D1D009A81E3 /* CAMultiAudioEngine.m */; }; 3460E58A1AC766260080358E /* rotate.py in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3460E5871AC764500080358E /* rotate.py */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 3463F99F1AD1594300F3B5C8 /* movement.py in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3463F99D1AD0DC7100F3B5C8 /* movement.py */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; + 346865471CC353E2002BAB86 /* CSInstantRecorderCompressorViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 346865451CC353E2002BAB86 /* CSInstantRecorderCompressorViewController.m */; }; + 346865481CC353E2002BAB86 /* CSInstantRecorderCompressorViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 346865461CC353E2002BAB86 /* CSInstantRecorderCompressorViewController.xib */; }; 346CF44C1A5C10C2008E5BFF /* CSInputLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 346CF44B1A5C10C2008E5BFF /* CSInputLayer.m */; }; 346CF44D1A5C10C2008E5BFF /* CSInputLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 346CF44B1A5C10C2008E5BFF /* CSInputLayer.m */; }; - 3470F41419C84989000A81C4 /* Sparkle.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3470F41319C84989000A81C4 /* Sparkle.framework */; settings = {ATTRIBUTES = (Required, ); }; }; - 3470F41619C849E7000A81C4 /* Sparkle.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3470F41319C84989000A81C4 /* Sparkle.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; + 3470CD0F1D57065B006404D9 /* CSAdvancedAudioCollectionViewItem.xib in Resources */ = {isa = PBXBuildFile; fileRef = 3470CD0E1D57065B006404D9 /* CSAdvancedAudioCollectionViewItem.xib */; }; + 3470CD291D57A088006404D9 /* CSAnimationWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3470CD271D57A088006404D9 /* CSAnimationWindowController.m */; }; + 3470CD2A1D57A088006404D9 /* CSAnimationWindowController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 3470CD281D57A088006404D9 /* CSAnimationWindowController.xib */; }; + 3470CD2E1D57D2AA006404D9 /* CSStreamOutputWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3470CD2C1D57D2AA006404D9 /* CSStreamOutputWindowController.m */; }; + 3470CD2F1D57D2AA006404D9 /* CSStreamOutputWindowController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 3470CD2D1D57D2AA006404D9 /* CSStreamOutputWindowController.xib */; }; + 3470CD3A1D5EED3A006404D9 /* CSNobarSliderCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 3470CD391D5EED3A006404D9 /* CSNobarSliderCell.m */; }; 347277FD1A5A2758008801A9 /* CSIOSurfaceLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 347277FC1A5A2758008801A9 /* CSIOSurfaceLayer.m */; }; 347277FE1A5A2758008801A9 /* CSIOSurfaceLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 347277FC1A5A2758008801A9 /* CSIOSurfaceLayer.m */; }; + 3473D2871D4C359700842EEE /* CSVaughnliveStreamServicePlugin.bundle in Copy Files */ = {isa = PBXBuildFile; fileRef = 3473D27E1D4C349700842EEE /* CSVaughnliveStreamServicePlugin.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 3473D29A1D4C3D0E00842EEE /* CSAddOutputPopupViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3473D2981D4C3D0E00842EEE /* CSAddOutputPopupViewController.m */; }; + 3473D29B1D4C3D0E00842EEE /* CSAddOutputPopupViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 3473D2991D4C3D0E00842EEE /* CSAddOutputPopupViewController.xib */; }; + 3473D2A61D56391700842EEE /* CSAudioLevelView.m in Sources */ = {isa = PBXBuildFile; fileRef = 3473D2A51D56391700842EEE /* CSAudioLevelView.m */; }; + 3473D2A81D5685DA00842EEE /* Media.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 3473D2A71D5685DA00842EEE /* Media.xcassets */; }; + 3473D2AE1D56920B00842EEE /* CSAdvancedAudioWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3473D2AC1D56920B00842EEE /* CSAdvancedAudioWindowController.m */; }; + 3473D2AF1D56920B00842EEE /* CSAdvancedAudioWindowController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 3473D2AD1D56920B00842EEE /* CSAdvancedAudioWindowController.xib */; }; 34792AF616104AA70065A859 /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 34792AF516104AA70065A859 /* IOKit.framework */; }; 34792AFB1611A6B90065A859 /* IOSurface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 34792AFA1611A6B90065A859 /* IOSurface.framework */; }; 3479A9971A9007DD00A524F0 /* CocoaSplit.sdef in Resources */ = {isa = PBXBuildFile; fileRef = 3479A9961A9007DD00A524F0 /* CocoaSplit.sdef */; }; @@ -140,11 +175,13 @@ 349461751AC49BB300F28883 /* CSAnimationChooserViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 349461731AC49BB300F28883 /* CSAnimationChooserViewController.m */; }; 349461761AC49BB300F28883 /* CSAnimationChooserViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 349461741AC49BB300F28883 /* CSAnimationChooserViewController.xib */; }; 349461771AC4CCD000F28883 /* CSAnimationChooserViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 349461731AC49BB300F28883 /* CSAnimationChooserViewController.m */; }; + 3494DF381CCD2DB000E921BF /* TPCircularBuffer.c in Sources */ = {isa = PBXBuildFile; fileRef = 3494DF341CCD2DB000E921BF /* TPCircularBuffer.c */; }; + 3494DF391CCD2DB000E921BF /* TPCircularBuffer+AudioBufferList.c in Sources */ = {isa = PBXBuildFile; fileRef = 3494DF361CCD2DB000E921BF /* TPCircularBuffer+AudioBufferList.c */; }; 3498D94A1A94685000906532 /* CSAudioSwitchCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 3498D9491A94685000906532 /* CSAudioSwitchCell.m */; }; 3498D94F1A9472BD00906532 /* CSAudioSwitchControl.m in Sources */ = {isa = PBXBuildFile; fileRef = 3498D94E1A9472BD00906532 /* CSAudioSwitchControl.m */; }; 34A64A2F165EFE4C00A68428 /* PreviewView.m in Sources */ = {isa = PBXBuildFile; fileRef = 34A64A2E165EFE4B00A68428 /* PreviewView.m */; }; - 34A64A35165F047900A68428 /* CapturePreview.xib in Resources */ = {isa = PBXBuildFile; fileRef = 34A64A34165F047900A68428 /* CapturePreview.xib */; }; 34A64A37165F208800A68428 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 34A64A36165F208800A68428 /* QuartzCore.framework */; }; + 34A7ADB81C01EF1400685044 /* CompressionSettingsPanelController.m in Sources */ = {isa = PBXBuildFile; fileRef = 34A7ADB71C01EF1400685044 /* CompressionSettingsPanelController.m */; }; 34A7C1AD19B9A57600BC6882 /* CSLayoutSwitcherExtraPlugin.bundle in Copy Files */ = {isa = PBXBuildFile; fileRef = 34A7C1A119B9A41A00BC6882 /* CSLayoutSwitcherExtraPlugin.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 34AB1C311B944F48003F2C03 /* CSPreviewOverlayView.m in Sources */ = {isa = PBXBuildFile; fileRef = 34AB1C301B944F48003F2C03 /* CSPreviewOverlayView.m */; }; 34ABEF3C17C0C2CB00AA5E62 /* advancedPrefPanel.xib in Resources */ = {isa = PBXBuildFile; fileRef = 34ABEF3B17C0C2CB00AA5E62 /* advancedPrefPanel.xib */; }; @@ -162,7 +199,6 @@ 34AFC27C19B05C4E0007C07B /* CSWindowCapturePlugin.bundle in Copy Files */ = {isa = PBXBuildFile; fileRef = 34AFC25919B05B470007C07B /* CSWindowCapturePlugin.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 34AFC27D19B05C4E0007C07B /* CSImageCapturePlugin.bundle in Copy Files */ = {isa = PBXBuildFile; fileRef = 34AFC24F19B05B0A0007C07B /* CSImageCapturePlugin.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 34AFC27E19B05C4E0007C07B /* CSTextCapturePlugin.bundle in Copy Files */ = {isa = PBXBuildFile; fileRef = 34AFC26319B05B6B0007C07B /* CSTextCapturePlugin.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; - 34AFC27F19B05C4E0007C07B /* CSSyphonCapturePlugin.bundle in Copy Files */ = {isa = PBXBuildFile; fileRef = 34AFC26819B05B7C0007C07B /* CSSyphonCapturePlugin.bundle */; }; 34AFC28019B05C4E0007C07B /* CSAVFCapturePlugin.bundle in Copy Files */ = {isa = PBXBuildFile; fileRef = 34AFC26D19B05B930007C07B /* CSAVFCapturePlugin.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 34AFC33319B19CD00007C07B /* SourceCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 34AFC33219B19CD00007C07B /* SourceCache.m */; }; 34AFC33419B19CD00007C07B /* SourceCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 34AFC33219B19CD00007C07B /* SourceCache.m */; }; @@ -179,9 +215,7 @@ 34C19053189EE60400AB2430 /* CapturedFrameData.m in Sources */ = {isa = PBXBuildFile; fileRef = 34C19052189EE60400AB2430 /* CapturedFrameData.m */; }; 34C19054189EE60400AB2430 /* CapturedFrameData.m in Sources */ = {isa = PBXBuildFile; fileRef = 34C19052189EE60400AB2430 /* CapturedFrameData.m */; }; 34C67B8E1A2E69F60012DC1B /* CSHitboxStreamServicePlugin.bundle in Copy Files */ = {isa = PBXBuildFile; fileRef = 34C67B781A2D7B6A0012DC1B /* CSHitboxStreamServicePlugin.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 34C680A119DD06E800413468 /* CSSyphonInjectExtraPlugin.bundle in Copy Files */ = {isa = PBXBuildFile; fileRef = 34C6808C19DD01C400413468 /* CSSyphonInjectExtraPlugin.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 34C6C3BF15FB5B440018A18B /* OpenGL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 34C6C3BE15FB5B440018A18B /* OpenGL.framework */; }; - 34CFE49D18F1459600092C6A /* AudioMixer.xib in Resources */ = {isa = PBXBuildFile; fileRef = 34CFE49C18F1459600092C6A /* AudioMixer.xib */; }; 34CFE4A018F154DD00092C6A /* AVFChannelManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 34CFE49F18F154DD00092C6A /* AVFChannelManager.m */; }; 34CFE4A118F154DD00092C6A /* AVFChannelManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 34CFE49F18F154DD00092C6A /* AVFChannelManager.m */; }; 34CFE4A418F1992A00092C6A /* AVFAudioChannel.m in Sources */ = {isa = PBXBuildFile; fileRef = 34CFE4A318F1992A00092C6A /* AVFAudioChannel.m */; }; @@ -191,16 +225,19 @@ 34D2D5771A547C35001004E5 /* CSTextCaptureViewControllerBase.m in Sources */ = {isa = PBXBuildFile; fileRef = 34D2D5761A547C35001004E5 /* CSTextCaptureViewControllerBase.m */; }; 34D2D5781A547C35001004E5 /* CSTextCaptureViewControllerBase.m in Sources */ = {isa = PBXBuildFile; fileRef = 34D2D5761A547C35001004E5 /* CSTextCaptureViewControllerBase.m */; }; 34D2D5B41A548B68001004E5 /* CSNowPlayingPlugin.bundle in Copy Files */ = {isa = PBXBuildFile; fileRef = 34D2D59E1A54857D001004E5 /* CSNowPlayingPlugin.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 34D4351B198BE3B700266169 /* NewLayoutPanel.xib in Resources */ = {isa = PBXBuildFile; fileRef = 34D4351A198BE3B700266169 /* NewLayoutPanel.xib */; }; 34D6580219B342E00012E32B /* CSTwitchStreamServicePlugin.bundle in Copy Files */ = {isa = PBXBuildFile; fileRef = 34AFC32B19B0AD360007C07B /* CSTwitchStreamServicePlugin.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 34D6580319B342E70012E32B /* CSFileStreamServicePlugin.bundle in Copy Files */ = {isa = PBXBuildFile; fileRef = 34AFC33019B0AD500007C07B /* CSFileStreamServicePlugin.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 34D6581819B387DE0012E32B /* AppDelegate+AppDelegate_ScriptingAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 34D6581719B387DE0012E32B /* AppDelegate+AppDelegate_ScriptingAdditions.m */; }; 34D696FC1871B8F700B50EB6 /* Defaults.plist in Resources */ = {isa = PBXBuildFile; fileRef = 34D696FB1871B8F700B50EB6 /* Defaults.plist */; }; + 34DA1D3E1BF823E700132486 /* CSNewOutputWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 34DA1D3C1BF823E700132486 /* CSNewOutputWindowController.m */; }; + 34DA1D3F1BF823E700132486 /* CSNewOutputWindowController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 34DA1D3D1BF823E700132486 /* CSNewOutputWindowController.xib */; }; 34DC2FB01B512362008F12A2 /* CSCaptureBase+TimerDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 34DC2FAF1B512362008F12A2 /* CSCaptureBase+TimerDelegate.m */; }; 34DC2FB11B512362008F12A2 /* CSCaptureBase+TimerDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 34DC2FAF1B512362008F12A2 /* CSCaptureBase+TimerDelegate.m */; }; 34DF75581AA272F100DA9FDE /* LayoutRenderer.m in Sources */ = {isa = PBXBuildFile; fileRef = 34DF75571AA272F100DA9FDE /* LayoutRenderer.m */; }; 34DF75591AA272F100DA9FDE /* LayoutRenderer.m in Sources */ = {isa = PBXBuildFile; fileRef = 34DF75571AA272F100DA9FDE /* LayoutRenderer.m */; }; 34E983701B78AE9100F26F1E /* Python.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 34E9836F1B78AE9100F26F1E /* Python.framework */; }; + 34EA822E1D3C568400928A06 /* CSOauth2Authenticator.m in Sources */ = {isa = PBXBuildFile; fileRef = 34EA822D1D3C568400928A06 /* CSOauth2Authenticator.m */; }; + 34EA824E1D3CBFCA00928A06 /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 34EA824D1D3CBFCA00928A06 /* WebKit.framework */; }; 34EAFB6F1A1A137600E12FBD /* CSMeterCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 34EAFB6E1A1A137600E12FBD /* CSMeterCell.m */; }; 34EAFB801A1BA86800E12FBD /* CoreMediaIO.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 34EAFB7F1A1BA86800E12FBD /* CoreMediaIO.framework */; }; 34EAFB811A1BA87000E12FBD /* CoreMediaIO.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 34EAFB7F1A1BA86800E12FBD /* CoreMediaIO.framework */; }; @@ -315,8 +352,16 @@ 34ED8CB91B073C1B002C0674 /* CSMidiManagerWindowController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 34ED8CB61B073C1B002C0674 /* CSMidiManagerWindowController.xib */; }; 34ED8CBC1B07763F002C0674 /* CSMidiWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = 34ED8CBB1B07763F002C0674 /* CSMidiWrapper.m */; }; 34ED8CBD1B07763F002C0674 /* CSMidiWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = 34ED8CBB1B07763F002C0674 /* CSMidiWrapper.m */; }; + 34F04D9F1CE0381D0054663A /* CSAddInputViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 34F04D9D1CE0381D0054663A /* CSAddInputViewController.m */; }; + 34F04DA01CE0381D0054663A /* CSAddInputViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 34F04D9E1CE0381D0054663A /* CSAddInputViewController.xib */; }; + 34F04DAB1CE0BB770054663A /* NSView+NSLayoutConstraintFilter.m in Sources */ = {isa = PBXBuildFile; fileRef = 34F04DAA1CE0BB770054663A /* NSView+NSLayoutConstraintFilter.m */; }; + 34F1EB921BCCA41F00B38E6C /* CSLayoutEditWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 34F1EB901BCCA41F00B38E6C /* CSLayoutEditWindowController.m */; }; + 34F1EB931BCCA41F00B38E6C /* CSLayoutEditWindowController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 34F1EB911BCCA41F00B38E6C /* CSLayoutEditWindowController.xib */; }; 34F5141216FD6E4F00BA894D /* x264Compressor.m in Sources */ = {isa = PBXBuildFile; fileRef = 34F5140F16FD6E4F00BA894D /* x264Compressor.m */; }; 34F5141316FD6E4F00BA894D /* AppleVTCompressor.m in Sources */ = {isa = PBXBuildFile; fileRef = 34F5141116FD6E4F00BA894D /* AppleVTCompressor.m */; }; + 34F52A271BC1CE2700662911 /* CSLayoutCollectionItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 34F52A251BC1CE2700662911 /* CSLayoutCollectionItem.m */; }; + 34F52A281BC1CE2700662911 /* CSLayoutCollectionItem.xib in Resources */ = {isa = PBXBuildFile; fileRef = 34F52A261BC1CE2700662911 /* CSLayoutCollectionItem.xib */; }; + 34F52A2B1BC1E0F900662911 /* CSLayoutButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 34F52A2A1BC1E0F800662911 /* CSLayoutButton.m */; }; 34F7C5871B2C9A5300345230 /* CSDeckLinkCapturePlugin.bundle in Copy Files */ = {isa = PBXBuildFile; fileRef = 34F7C5611B2C908700345230 /* CSDeckLinkCapturePlugin.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 34FDA83E1A84C46500E7F65E /* CSTimeCapturePlugin.bundle in Copy Files */ = {isa = PBXBuildFile; fileRef = 34FDA8191A84BF9400E7F65E /* CSTimeCapturePlugin.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 34FDD6F21A215268009A7413 /* CSPluginServices.m in Sources */ = {isa = PBXBuildFile; fileRef = 34FDD6F11A215268009A7413 /* CSPluginServices.m */; }; @@ -324,6 +369,13 @@ /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ + 340C80141D44722100B2FD7D /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 340C80101D44722000B2FD7D /* CSYoutubeStreamServicePlugin.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 340C80071D44722000B2FD7D; + remoteInfo = CSYoutubeStreamServicePlugin; + }; 3436922A1B62364E007EC2D9 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 3436921B1B62364E007EC2D9 /* CSShapeCapturePlugin.xcodeproj */; @@ -331,6 +383,20 @@ remoteGlobalIDString = 343692111B62364E007EC2D9; remoteInfo = CSShapeCapturePlugin; }; + 343C79CB1D0C763A00B36EEC /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 343C79C31D0C763A00B36EEC /* CSFFMpegCapturePlugin.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 343C79BA1D0C763A00B36EEC; + remoteInfo = CSFFMpegCapturePlugin; + }; + 3473D27D1D4C349700842EEE /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 3473D2791D4C349700842EEE /* CSVaughnliveStreamServicePlugin.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 34BD1E121B1B970C0015CBEC; + remoteInfo = CSVaughnliveStreamServicePlugin; + }; 34A7C1A019B9A41A00BC6882 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 34A7C19C19B9A41900BC6882 /* CSLayoutSwitcherExtraPlugin.xcodeproj */; @@ -338,13 +404,6 @@ remoteGlobalIDString = 34A7C18719B9A41900BC6882; remoteInfo = CSLayoutSwitcherExtraPlugin; }; - 34AE3C40164E3FAD0052C95E /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 340FE49415F3417E00E4CE4E /* Project object */; - proxyType = 1; - remoteGlobalIDString = 34AE3C2D164E3E020052C95E; - remoteInfo = QTCaptureHelper; - }; 34AFC24919B05ADA0007C07B /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 34AFC24519B05ADA0007C07B /* CSDesktopCapturePlugin.xcodeproj */; @@ -507,21 +566,22 @@ isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = Animations; - dstSubfolderSpec = 13; + dstSubfolderSpec = 7; files = ( 3463F99F1AD1594300F3B5C8 /* movement.py in CopyFiles */, 3460E58A1AC766260080358E /* rotate.py in CopyFiles */, ); runOnlyForDeploymentPostprocessing = 0; }; - 3470F41519C849D9000A81C4 /* CopyFiles */ = { + 3470F41519C849D9000A81C4 /* Copy Files */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; dstSubfolderSpec = 10; files = ( - 3470F41619C849E7000A81C4 /* Sparkle.framework in CopyFiles */, + 341A2D5D1C7AB0D100FF7566 /* Sparkle.framework in Copy Files */, ); + name = "Copy Files"; runOnlyForDeploymentPostprocessing = 0; }; 34D43556198DD00F00266169 /* Copy Files */ = { @@ -530,12 +590,15 @@ dstPath = ""; dstSubfolderSpec = 13; files = ( + 3414047D1D76EA3200505A5D /* CSSyphonCapturePlugin.bundle in Copy Files */, + 3473D2871D4C359700842EEE /* CSVaughnliveStreamServicePlugin.bundle in Copy Files */, + 340C802D1D450A5100B2FD7D /* CSYoutubeStreamServicePlugin.bundle in Copy Files */, + 343C79ED1D0C796E00B36EEC /* CSFFMpegCapturePlugin.bundle in Copy Files */, 343692501B6243D4007EC2D9 /* CSShapeCapturePlugin.bundle in Copy Files */, 34F7C5871B2C9A5300345230 /* CSDeckLinkCapturePlugin.bundle in Copy Files */, 34FDA83E1A84C46500E7F65E /* CSTimeCapturePlugin.bundle in Copy Files */, 34D2D5B41A548B68001004E5 /* CSNowPlayingPlugin.bundle in Copy Files */, 34C67B8E1A2E69F60012DC1B /* CSHitboxStreamServicePlugin.bundle in Copy Files */, - 34C680A119DD06E800413468 /* CSSyphonInjectExtraPlugin.bundle in Copy Files */, 34A7C1AD19B9A57600BC6882 /* CSLayoutSwitcherExtraPlugin.bundle in Copy Files */, 34D6580319B342E70012E32B /* CSFileStreamServicePlugin.bundle in Copy Files */, 34D6580219B342E00012E32B /* CSTwitchStreamServicePlugin.bundle in Copy Files */, @@ -546,7 +609,6 @@ 34AFC27C19B05C4E0007C07B /* CSWindowCapturePlugin.bundle in Copy Files */, 34AFC27D19B05C4E0007C07B /* CSImageCapturePlugin.bundle in Copy Files */, 34AFC27E19B05C4E0007C07B /* CSTextCapturePlugin.bundle in Copy Files */, - 34AFC27F19B05C4E0007C07B /* CSSyphonCapturePlugin.bundle in Copy Files */, 34AFC28019B05C4E0007C07B /* CSAVFCapturePlugin.bundle in Copy Files */, ); name = "Copy Files"; @@ -555,8 +617,18 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 3400A0601BD44DF0003E1828 /* CSInputLibraryItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSInputLibraryItem.h; sourceTree = ""; }; + 3400A0611BD44DF0003E1828 /* CSInputLibraryItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSInputLibraryItem.m; sourceTree = ""; }; + 3400A0761BD4533A003E1828 /* CSInputLibraryWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSInputLibraryWindowController.h; path = Interface/CSInputLibraryWindowController.h; sourceTree = ""; }; + 3400A0771BD4533A003E1828 /* CSInputLibraryWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CSInputLibraryWindowController.m; path = Interface/CSInputLibraryWindowController.m; sourceTree = ""; }; + 3400A0781BD4533A003E1828 /* CSInputLibraryWindowController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = CSInputLibraryWindowController.xib; path = Interface/CSInputLibraryWindowController.xib; sourceTree = ""; }; + 3400A07D1BD49406003E1828 /* CSInputLibraryItemView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = CSInputLibraryItemView.xib; path = Interface/CSInputLibraryItemView.xib; sourceTree = ""; }; + 3400A0821BD522A5003E1828 /* CSLibraryInputItemViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSLibraryInputItemViewController.h; path = Interface/CSLibraryInputItemViewController.h; sourceTree = ""; }; + 3400A0831BD522A5003E1828 /* CSLibraryInputItemViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CSLibraryInputItemViewController.m; path = Interface/CSLibraryInputItemViewController.m; sourceTree = ""; }; 3408234619BC50AD00CD1F5F /* CSNotifications.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSNotifications.h; path = PluginHeaders/CSNotifications.h; sourceTree = ""; }; 3408235419BC530300CD1F5F /* CSNotifications.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CSNotifications.m; path = ../CSNotifications.m; sourceTree = ""; }; + 340C7FE81D43D22000B2FD7D /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; }; + 340C80101D44722000B2FD7D /* CSYoutubeStreamServicePlugin.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = CSYoutubeStreamServicePlugin.xcodeproj; path = StreamServicePlugins/CSYoutubeStreamServicePlugin/CSYoutubeStreamServicePlugin.xcodeproj; sourceTree = ""; }; 340D8C961959C7C100BE5144 /* CompressionSettingsPanel.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = CompressionSettingsPanel.xib; path = Interface/CompressionSettingsPanel.xib; sourceTree = ""; }; 340DC732197CF6E9003A0BB3 /* CSCaptureBase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSCaptureBase.h; path = PluginHeaders/CSCaptureBase.h; sourceTree = ""; }; 340DC733197CF6E9003A0BB3 /* CSCaptureBase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSCaptureBase.m; sourceTree = ""; }; @@ -582,7 +654,10 @@ 340FE4E015F3581200E4CE4E /* QTKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QTKit.framework; path = System/Library/Frameworks/QTKit.framework; sourceTree = SDKROOT; }; 340FE50615F444AA00E4CE4E /* CaptureController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CaptureController.h; sourceTree = ""; }; 340FE50715F444AA00E4CE4E /* CaptureController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CaptureController.m; sourceTree = ""; }; + 3414C6AC1CBB9EBF00107C69 /* CSIRCompressor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSIRCompressor.h; path = Compressor/CSIRCompressor.h; sourceTree = ""; }; + 3414C6AD1CBB9EBF00107C69 /* CSIRCompressor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CSIRCompressor.m; path = Compressor/CSIRCompressor.m; sourceTree = ""; }; 3415CED21AE257DA002F11F5 /* CSTextCaptureBaseView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = CSTextCaptureBaseView.xib; path = Interface/CSTextCaptureBaseView.xib; sourceTree = ""; }; + 341A2D5B1C7AB04700FF7566 /* Sparkle.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Sparkle.framework; sourceTree = ""; }; 34210B3C1606001100362FC8 /* OutputDestination.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OutputDestination.h; sourceTree = ""; }; 34210B3D1606001100362FC8 /* OutputDestination.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OutputDestination.m; sourceTree = ""; }; 34213E18196680880054E238 /* CompressorBase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CompressorBase.h; path = Compressor/CompressorBase.h; sourceTree = ""; }; @@ -605,19 +680,37 @@ 34348C2719BDBDC000A122C2 /* PluginManagerWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PluginManagerWindowController.h; sourceTree = ""; }; 34348C2819BDBDC000A122C2 /* PluginManagerWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PluginManagerWindowController.m; sourceTree = ""; }; 34348C2919BDBDC000A122C2 /* PluginManagerWindowController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = PluginManagerWindowController.xib; path = Interface/PluginManagerWindowController.xib; sourceTree = ""; }; + 3434CB261CA8615A001B3DF9 /* AppleProResCompressor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppleProResCompressor.h; path = Compressor/AppleProResCompressor.h; sourceTree = ""; }; + 3434CB271CA8615A001B3DF9 /* AppleProResCompressor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppleProResCompressor.m; path = Compressor/AppleProResCompressor.m; sourceTree = ""; }; + 3434CB2A1CA90D28001B3DF9 /* CSx264CompressorViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSx264CompressorViewController.h; path = Interface/CSx264CompressorViewController.h; sourceTree = ""; }; + 3434CB2B1CA90D28001B3DF9 /* CSx264CompressorViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CSx264CompressorViewController.m; path = Interface/CSx264CompressorViewController.m; sourceTree = ""; }; + 3434CB2C1CA90D28001B3DF9 /* CSx264CompressorViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = CSx264CompressorViewController.xib; path = Interface/CSx264CompressorViewController.xib; sourceTree = ""; }; + 3434CB2F1CA91D99001B3DF9 /* CSAppleH264CompressorViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSAppleH264CompressorViewController.h; path = Interface/CSAppleH264CompressorViewController.h; sourceTree = ""; }; + 3434CB301CA91D99001B3DF9 /* CSAppleH264CompressorViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CSAppleH264CompressorViewController.m; path = Interface/CSAppleH264CompressorViewController.m; sourceTree = ""; }; + 3434CB311CA91D99001B3DF9 /* CSAppleH264CompressorViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = CSAppleH264CompressorViewController.xib; path = Interface/CSAppleH264CompressorViewController.xib; sourceTree = ""; }; + 3434CB341CA933B3001B3DF9 /* CSCompressorViewControllerProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSCompressorViewControllerProtocol.h; path = Interface/CSCompressorViewControllerProtocol.h; sourceTree = ""; }; + 3434CB351CA93BCD001B3DF9 /* CSAppleProResCompressorViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSAppleProResCompressorViewController.h; path = Interface/CSAppleProResCompressorViewController.h; sourceTree = ""; }; + 3434CB361CA93BCD001B3DF9 /* CSAppleProResCompressorViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CSAppleProResCompressorViewController.m; path = Interface/CSAppleProResCompressorViewController.m; sourceTree = ""; }; + 3434CB371CA93BCD001B3DF9 /* CSAppleProResCompressorViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = CSAppleProResCompressorViewController.xib; path = Interface/CSAppleProResCompressorViewController.xib; sourceTree = ""; }; + 3434CB3A1CB09BC4001B3DF9 /* CSTimedOutputBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSTimedOutputBuffer.h; sourceTree = ""; }; + 3434CB3B1CB09BC4001B3DF9 /* CSTimedOutputBuffer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSTimedOutputBuffer.m; sourceTree = ""; }; 3435E395188B902C0015CC01 /* 420v.fgsh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = 420v.fgsh; path = Shaders/420v.fgsh; sourceTree = ""; }; 3435E399188B90900015CC01 /* passthrough.fgsh */ = {isa = PBXFileReference; lastKnownFileType = text; name = passthrough.fgsh; path = Shaders/passthrough.fgsh; sourceTree = ""; }; 3435E39A188B93150015CC01 /* passthrough.vtsh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = passthrough.vtsh; path = Shaders/passthrough.vtsh; sourceTree = ""; }; 3436921B1B62364E007EC2D9 /* CSShapeCapturePlugin.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = CSShapeCapturePlugin.xcodeproj; path = CapturePlugins/CSShapeCapturePlugin/CSShapeCapturePlugin.xcodeproj; sourceTree = ""; }; + 343C79C31D0C763A00B36EEC /* CSFFMpegCapturePlugin.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = CSFFMpegCapturePlugin.xcodeproj; path = CSFFMpegCapturePlugin/CSFFMpegCapturePlugin.xcodeproj; sourceTree = ""; }; 343DD65919AAD3060017232A /* ApplicationServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ApplicationServices.framework; path = System/Library/Frameworks/ApplicationServices.framework; sourceTree = SDKROOT; }; 343DD65C19AAD3060017232A /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; 343DD65E19AAD3060017232A /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; }; 343DD6AB19AAD3660017232A /* CSChromaKey.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = CSChromaKey.xcodeproj; path = ImageUnitPlugins/CSChromaKey/CSChromaKey.xcodeproj; sourceTree = ""; }; + 343ED9411BCAC1C80061FC4B /* EditBuiltinLayoutView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = EditBuiltinLayoutView.xib; path = Interface/EditBuiltinLayoutView.xib; sourceTree = ""; }; 3444D8E719A0CDE8000DC46B /* Quartz.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Quartz.framework; path = System/Library/Frameworks/Quartz.framework; sourceTree = SDKROOT; }; 344825D11B2536EA00AF9EAC /* CAMultiAudioMatrixView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = CAMultiAudioMatrixView.xib; sourceTree = ""; }; 344825E41B253D6900AF9EAC /* CAMultiAudioMatrixCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CAMultiAudioMatrixCell.h; path = Interface/CAMultiAudioMatrixCell.h; sourceTree = ""; }; 344825E51B253D6900AF9EAC /* CAMultiAudioMatrixCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CAMultiAudioMatrixCell.m; path = Interface/CAMultiAudioMatrixCell.m; sourceTree = ""; }; 344988121893CF0500044259 /* LogWindow.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = LogWindow.xib; path = Interface/LogWindow.xib; sourceTree = ""; }; + 3450239B1C60B3EF00B461B5 /* CSLayoutCollectionItemView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSLayoutCollectionItemView.h; path = Interface/CSLayoutCollectionItemView.h; sourceTree = ""; }; + 3450239C1C60B3EF00B461B5 /* CSLayoutCollectionItemView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CSLayoutCollectionItemView.m; path = Interface/CSLayoutCollectionItemView.m; sourceTree = ""; }; 3451A1C317111BD900DF6A8B /* CocoaSplitCmd */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = CocoaSplitCmd; sourceTree = BUILT_PRODUCTS_DIR; }; 3451A1C617111BD900DF6A8B /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 3451A1C917111BD900DF6A8B /* CocoaSplitCmd-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "CocoaSplitCmd-Prefix.pch"; sourceTree = ""; }; @@ -659,12 +752,33 @@ 345F8B7B1A184FBC009A81E3 /* CAMultiAudioMixingProtocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CAMultiAudioMixingProtocol.h; sourceTree = ""; }; 3460E5871AC764500080358E /* rotate.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = rotate.py; sourceTree = ""; }; 3463F99D1AD0DC7100F3B5C8 /* movement.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = movement.py; sourceTree = ""; }; + 346865441CC353E2002BAB86 /* CSInstantRecorderCompressorViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSInstantRecorderCompressorViewController.h; path = Compressor/CSInstantRecorderCompressorViewController.h; sourceTree = ""; }; + 346865451CC353E2002BAB86 /* CSInstantRecorderCompressorViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CSInstantRecorderCompressorViewController.m; path = Compressor/CSInstantRecorderCompressorViewController.m; sourceTree = ""; }; + 346865461CC353E2002BAB86 /* CSInstantRecorderCompressorViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = CSInstantRecorderCompressorViewController.xib; path = Compressor/CSInstantRecorderCompressorViewController.xib; sourceTree = ""; }; 346CF44A1A5C10C2008E5BFF /* CSInputLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSInputLayer.h; sourceTree = ""; }; 346CF44B1A5C10C2008E5BFF /* CSInputLayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSInputLayer.m; sourceTree = ""; }; 346EAAF6197A7B28000892CF /* Capture.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Capture.h; sourceTree = ""; }; - 3470F41319C84989000A81C4 /* Sparkle.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Sparkle.framework; sourceTree = ""; }; + 3470CD0E1D57065B006404D9 /* CSAdvancedAudioCollectionViewItem.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = CSAdvancedAudioCollectionViewItem.xib; path = Interface/CSAdvancedAudioCollectionViewItem.xib; sourceTree = ""; }; + 3470CD261D57A088006404D9 /* CSAnimationWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSAnimationWindowController.h; path = Interface/CSAnimationWindowController.h; sourceTree = ""; }; + 3470CD271D57A088006404D9 /* CSAnimationWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CSAnimationWindowController.m; path = Interface/CSAnimationWindowController.m; sourceTree = ""; }; + 3470CD281D57A088006404D9 /* CSAnimationWindowController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = CSAnimationWindowController.xib; path = Interface/CSAnimationWindowController.xib; sourceTree = ""; }; + 3470CD2B1D57D2AA006404D9 /* CSStreamOutputWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSStreamOutputWindowController.h; path = Interface/CSStreamOutputWindowController.h; sourceTree = ""; }; + 3470CD2C1D57D2AA006404D9 /* CSStreamOutputWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CSStreamOutputWindowController.m; path = Interface/CSStreamOutputWindowController.m; sourceTree = ""; }; + 3470CD2D1D57D2AA006404D9 /* CSStreamOutputWindowController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = CSStreamOutputWindowController.xib; path = Interface/CSStreamOutputWindowController.xib; sourceTree = ""; }; + 3470CD381D5EED3A006404D9 /* CSNobarSliderCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSNobarSliderCell.h; path = Interface/CSNobarSliderCell.h; sourceTree = ""; }; + 3470CD391D5EED3A006404D9 /* CSNobarSliderCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CSNobarSliderCell.m; path = Interface/CSNobarSliderCell.m; sourceTree = ""; }; 347277FB1A5A2758008801A9 /* CSIOSurfaceLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSIOSurfaceLayer.h; path = PluginHeaders/CSIOSurfaceLayer.h; sourceTree = ""; }; 347277FC1A5A2758008801A9 /* CSIOSurfaceLayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSIOSurfaceLayer.m; sourceTree = ""; }; + 3473D2791D4C349700842EEE /* CSVaughnliveStreamServicePlugin.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = CSVaughnliveStreamServicePlugin.xcodeproj; path = StreamServicePlugins/CSVaughnliveStreamServicePlugin/CSVaughnliveStreamServicePlugin.xcodeproj; sourceTree = ""; }; + 3473D2971D4C3D0E00842EEE /* CSAddOutputPopupViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSAddOutputPopupViewController.h; path = Interface/CSAddOutputPopupViewController.h; sourceTree = ""; }; + 3473D2981D4C3D0E00842EEE /* CSAddOutputPopupViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CSAddOutputPopupViewController.m; path = Interface/CSAddOutputPopupViewController.m; sourceTree = ""; }; + 3473D2991D4C3D0E00842EEE /* CSAddOutputPopupViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = CSAddOutputPopupViewController.xib; path = Interface/CSAddOutputPopupViewController.xib; sourceTree = ""; }; + 3473D2A41D56391700842EEE /* CSAudioLevelView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSAudioLevelView.h; path = Interface/CSAudioLevelView.h; sourceTree = ""; }; + 3473D2A51D56391700842EEE /* CSAudioLevelView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CSAudioLevelView.m; path = Interface/CSAudioLevelView.m; sourceTree = ""; }; + 3473D2A71D5685DA00842EEE /* Media.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Media.xcassets; sourceTree = ""; }; + 3473D2AB1D56920B00842EEE /* CSAdvancedAudioWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSAdvancedAudioWindowController.h; path = Interface/CSAdvancedAudioWindowController.h; sourceTree = ""; }; + 3473D2AC1D56920B00842EEE /* CSAdvancedAudioWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CSAdvancedAudioWindowController.m; path = Interface/CSAdvancedAudioWindowController.m; sourceTree = ""; }; + 3473D2AD1D56920B00842EEE /* CSAdvancedAudioWindowController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = CSAdvancedAudioWindowController.xib; path = Interface/CSAdvancedAudioWindowController.xib; sourceTree = ""; }; 34792AF516104AA70065A859 /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = System/Library/Frameworks/IOKit.framework; sourceTree = SDKROOT; }; 34792AFA1611A6B90065A859 /* IOSurface.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOSurface.framework; path = System/Library/Frameworks/IOSurface.framework; sourceTree = SDKROOT; }; 3479A9961A9007DD00A524F0 /* CocoaSplit.sdef */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = CocoaSplit.sdef; sourceTree = ""; }; @@ -693,14 +807,19 @@ 349461721AC49BB300F28883 /* CSAnimationChooserViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSAnimationChooserViewController.h; sourceTree = ""; }; 349461731AC49BB300F28883 /* CSAnimationChooserViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSAnimationChooserViewController.m; sourceTree = ""; }; 349461741AC49BB300F28883 /* CSAnimationChooserViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = CSAnimationChooserViewController.xib; sourceTree = ""; }; + 3494DF341CCD2DB000E921BF /* TPCircularBuffer.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = TPCircularBuffer.c; sourceTree = ""; }; + 3494DF351CCD2DB000E921BF /* TPCircularBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TPCircularBuffer.h; sourceTree = ""; }; + 3494DF361CCD2DB000E921BF /* TPCircularBuffer+AudioBufferList.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "TPCircularBuffer+AudioBufferList.c"; sourceTree = ""; }; + 3494DF371CCD2DB000E921BF /* TPCircularBuffer+AudioBufferList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "TPCircularBuffer+AudioBufferList.h"; sourceTree = ""; }; 3498D9481A94685000906532 /* CSAudioSwitchCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSAudioSwitchCell.h; sourceTree = ""; }; 3498D9491A94685000906532 /* CSAudioSwitchCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSAudioSwitchCell.m; sourceTree = ""; }; 3498D94D1A9472BD00906532 /* CSAudioSwitchControl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSAudioSwitchControl.h; path = Interface/CSAudioSwitchControl.h; sourceTree = ""; }; 3498D94E1A9472BD00906532 /* CSAudioSwitchControl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CSAudioSwitchControl.m; path = Interface/CSAudioSwitchControl.m; sourceTree = ""; }; 34A64A2D165EFE4B00A68428 /* PreviewView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PreviewView.h; sourceTree = ""; }; 34A64A2E165EFE4B00A68428 /* PreviewView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PreviewView.m; sourceTree = ""; }; - 34A64A34165F047900A68428 /* CapturePreview.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = CapturePreview.xib; path = Interface/CapturePreview.xib; sourceTree = ""; }; 34A64A36165F208800A68428 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; + 34A7ADB61C01EF1400685044 /* CompressionSettingsPanelController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CompressionSettingsPanelController.h; path = Interface/CompressionSettingsPanelController.h; sourceTree = ""; }; + 34A7ADB71C01EF1400685044 /* CompressionSettingsPanelController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CompressionSettingsPanelController.m; path = Interface/CompressionSettingsPanelController.m; sourceTree = ""; }; 34A7C17A19B98EA200BC6882 /* CSExtraPluginProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSExtraPluginProtocol.h; path = PluginHeaders/CSExtraPluginProtocol.h; sourceTree = ""; }; 34A7C19C19B9A41900BC6882 /* CSLayoutSwitcherExtraPlugin.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = CSLayoutSwitcherExtraPlugin.xcodeproj; path = ExtraPlugins/CSLayoutSwitcherExtraPlugin/CSLayoutSwitcherExtraPlugin.xcodeproj; sourceTree = ""; }; 34AB1C2F1B944F48003F2C03 /* CSPreviewOverlayView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSPreviewOverlayView.h; sourceTree = ""; }; @@ -745,7 +864,6 @@ 34C67B731A2D7B690012DC1B /* CSHitboxStreamServicePlugin.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = CSHitboxStreamServicePlugin.xcodeproj; path = StreamServicePlugins/CSHitboxStreamServicePlugin/CSHitboxStreamServicePlugin.xcodeproj; sourceTree = ""; }; 34C6807E19DD01C300413468 /* CSSyphonInjectExtraPlugin.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = CSSyphonInjectExtraPlugin.xcodeproj; path = ExtraPlugins/CSSyphonInjectExtraPlugin/CSSyphonInjectExtraPlugin.xcodeproj; sourceTree = ""; }; 34C6C3BE15FB5B440018A18B /* OpenGL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGL.framework; path = System/Library/Frameworks/OpenGL.framework; sourceTree = SDKROOT; }; - 34CFE49C18F1459600092C6A /* AudioMixer.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = AudioMixer.xib; path = Interface/AudioMixer.xib; sourceTree = ""; }; 34CFE49E18F154DD00092C6A /* AVFChannelManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AVFChannelManager.h; sourceTree = ""; }; 34CFE49F18F154DD00092C6A /* AVFChannelManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AVFChannelManager.m; sourceTree = ""; }; 34CFE4A218F1992A00092C6A /* AVFAudioChannel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AVFAudioChannel.h; sourceTree = ""; }; @@ -755,12 +873,14 @@ 34D2D5751A547C35001004E5 /* CSTextCaptureViewControllerBase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSTextCaptureViewControllerBase.h; path = PluginHeaders/CSTextCaptureViewControllerBase.h; sourceTree = ""; }; 34D2D5761A547C35001004E5 /* CSTextCaptureViewControllerBase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSTextCaptureViewControllerBase.m; sourceTree = ""; }; 34D2D5991A54857C001004E5 /* CSNowPlayingPlugin.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = CSNowPlayingPlugin.xcodeproj; path = CapturePlugins/CSNowPlayingPlugin/CSNowPlayingPlugin.xcodeproj; sourceTree = ""; }; - 34D4351A198BE3B700266169 /* NewLayoutPanel.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = NewLayoutPanel.xib; path = Interface/NewLayoutPanel.xib; sourceTree = ""; }; 34D43550198DCE3800266169 /* TextureWrapPlugin.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = TextureWrapPlugin.xcodeproj; path = ImageUnitPlugins/TextureWrapPlugin/TextureWrapPlugin.xcodeproj; sourceTree = ""; }; 34D657F519B33DCF0012E32B /* CSPluginFactoryProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSPluginFactoryProtocol.h; path = PluginHeaders/CSPluginFactoryProtocol.h; sourceTree = ""; }; 34D6581619B387DE0012E32B /* AppDelegate+AppDelegate_ScriptingAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "AppDelegate+AppDelegate_ScriptingAdditions.h"; path = "ScriptingAddditions/AppDelegate+AppDelegate_ScriptingAdditions.h"; sourceTree = ""; }; 34D6581719B387DE0012E32B /* AppDelegate+AppDelegate_ScriptingAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "AppDelegate+AppDelegate_ScriptingAdditions.m"; path = "ScriptingAddditions/AppDelegate+AppDelegate_ScriptingAdditions.m"; sourceTree = ""; }; 34D696FB1871B8F700B50EB6 /* Defaults.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Defaults.plist; sourceTree = ""; }; + 34DA1D3B1BF823E700132486 /* CSNewOutputWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSNewOutputWindowController.h; path = Interface/CSNewOutputWindowController.h; sourceTree = ""; }; + 34DA1D3C1BF823E700132486 /* CSNewOutputWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CSNewOutputWindowController.m; path = Interface/CSNewOutputWindowController.m; sourceTree = ""; }; + 34DA1D3D1BF823E700132486 /* CSNewOutputWindowController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = CSNewOutputWindowController.xib; path = Interface/CSNewOutputWindowController.xib; sourceTree = ""; }; 34DC2F9B1B50FBCD008F12A2 /* CSTimerSourceProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSTimerSourceProtocol.h; sourceTree = ""; }; 34DC2FAE1B512362008F12A2 /* CSCaptureBase+TimerDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "CSCaptureBase+TimerDelegate.h"; sourceTree = ""; }; 34DC2FAF1B512362008F12A2 /* CSCaptureBase+TimerDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "CSCaptureBase+TimerDelegate.m"; sourceTree = ""; }; @@ -768,6 +888,9 @@ 34DF75571AA272F100DA9FDE /* LayoutRenderer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LayoutRenderer.m; sourceTree = ""; }; 34E9836F1B78AE9100F26F1E /* Python.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Python.framework; path = System/Library/Frameworks/Python.framework; sourceTree = SDKROOT; }; 34E983841B78B19D00F26F1E /* pluginbase.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = pluginbase.py; sourceTree = ""; }; + 34EA822C1D3C568400928A06 /* CSOauth2Authenticator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSOauth2Authenticator.h; path = PluginHeaders/CSOauth2Authenticator.h; sourceTree = ""; }; + 34EA822D1D3C568400928A06 /* CSOauth2Authenticator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSOauth2Authenticator.m; sourceTree = ""; }; + 34EA824D1D3CBFCA00928A06 /* WebKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebKit.framework; path = System/Library/Frameworks/WebKit.framework; sourceTree = SDKROOT; }; 34EAFB6D1A1A137600E12FBD /* CSMeterCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSMeterCell.h; path = Interface/CSMeterCell.h; sourceTree = ""; }; 34EAFB6E1A1A137600E12FBD /* CSMeterCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CSMeterCell.m; path = Interface/CSMeterCell.m; sourceTree = ""; }; 34EAFB7F1A1BA86800E12FBD /* CoreMediaIO.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreMediaIO.framework; path = System/Library/Frameworks/CoreMediaIO.framework; sourceTree = SDKROOT; }; @@ -888,11 +1011,24 @@ 34ED8CB61B073C1B002C0674 /* CSMidiManagerWindowController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = CSMidiManagerWindowController.xib; path = Interface/CSMidiManagerWindowController.xib; sourceTree = ""; }; 34ED8CBA1B07763F002C0674 /* CSMidiWrapper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSMidiWrapper.h; sourceTree = ""; }; 34ED8CBB1B07763F002C0674 /* CSMidiWrapper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSMidiWrapper.m; sourceTree = ""; }; + 34F04D9C1CE0381D0054663A /* CSAddInputViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSAddInputViewController.h; path = Interface/CSAddInputViewController.h; sourceTree = ""; }; + 34F04D9D1CE0381D0054663A /* CSAddInputViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CSAddInputViewController.m; path = Interface/CSAddInputViewController.m; sourceTree = ""; }; + 34F04D9E1CE0381D0054663A /* CSAddInputViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = CSAddInputViewController.xib; path = Interface/CSAddInputViewController.xib; sourceTree = ""; }; + 34F04DA91CE0BB770054663A /* NSView+NSLayoutConstraintFilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSView+NSLayoutConstraintFilter.h"; sourceTree = ""; }; + 34F04DAA1CE0BB770054663A /* NSView+NSLayoutConstraintFilter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSView+NSLayoutConstraintFilter.m"; sourceTree = ""; }; + 34F1EB8F1BCCA41F00B38E6C /* CSLayoutEditWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSLayoutEditWindowController.h; path = Interface/CSLayoutEditWindowController.h; sourceTree = ""; }; + 34F1EB901BCCA41F00B38E6C /* CSLayoutEditWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CSLayoutEditWindowController.m; path = Interface/CSLayoutEditWindowController.m; sourceTree = ""; }; + 34F1EB911BCCA41F00B38E6C /* CSLayoutEditWindowController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = CSLayoutEditWindowController.xib; path = Interface/CSLayoutEditWindowController.xib; sourceTree = ""; }; 34F5140E16FD6E4F00BA894D /* x264Compressor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = x264Compressor.h; path = Compressor/x264Compressor.h; sourceTree = ""; }; 34F5140F16FD6E4F00BA894D /* x264Compressor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = x264Compressor.m; path = Compressor/x264Compressor.m; sourceTree = ""; }; 34F5141016FD6E4F00BA894D /* AppleVTCompressor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppleVTCompressor.h; path = Compressor/AppleVTCompressor.h; sourceTree = ""; }; 34F5141116FD6E4F00BA894D /* AppleVTCompressor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppleVTCompressor.m; path = Compressor/AppleVTCompressor.m; sourceTree = ""; }; - 34F5141416FD6E5D00BA894D /* h264Compressor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = h264Compressor.h; path = Compressor/h264Compressor.h; sourceTree = ""; }; + 34F5141416FD6E5D00BA894D /* VideoCompressor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = VideoCompressor.h; path = Compressor/VideoCompressor.h; sourceTree = ""; }; + 34F52A241BC1CE2700662911 /* CSLayoutCollectionItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSLayoutCollectionItem.h; path = Interface/CSLayoutCollectionItem.h; sourceTree = ""; }; + 34F52A251BC1CE2700662911 /* CSLayoutCollectionItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CSLayoutCollectionItem.m; path = Interface/CSLayoutCollectionItem.m; sourceTree = ""; }; + 34F52A261BC1CE2700662911 /* CSLayoutCollectionItem.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = CSLayoutCollectionItem.xib; path = Interface/CSLayoutCollectionItem.xib; sourceTree = ""; }; + 34F52A291BC1E0F800662911 /* CSLayoutButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSLayoutButton.h; sourceTree = ""; }; + 34F52A2A1BC1E0F800662911 /* CSLayoutButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSLayoutButton.m; sourceTree = ""; }; 34F7C55A1B2C908700345230 /* CSDeckLinkCapturePlugin.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = CSDeckLinkCapturePlugin.xcodeproj; path = CapturePlugins/CSDeckLinkCapturePlugin/CSDeckLinkCapturePlugin.xcodeproj; sourceTree = ""; }; 34FDA8071A84BF9300E7F65E /* CSTimeCapturePlugin.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = CSTimeCapturePlugin.xcodeproj; path = CapturePlugins/CSTimeCapturePlugin/CSTimeCapturePlugin.xcodeproj; sourceTree = ""; }; 34FDD6DC1A204CE4009A7413 /* CAMultiAudioEngine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CAMultiAudioEngine.h; path = CAMultiAudio/CAMultiAudioEngine.h; sourceTree = ""; }; @@ -905,6 +1041,8 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 340C7FE91D43D22000B2FD7D /* Security.framework in Frameworks */, + 34EA824E1D3CBFCA00928A06 /* WebKit.framework in Frameworks */, 34E983701B78AE9100F26F1E /* Python.framework in Frameworks */, 34ED8CB31B0737AC002C0674 /* CoreMIDI.framework in Frameworks */, 34EAFB801A1BA86800E12FBD /* CoreMediaIO.framework in Frameworks */, @@ -915,10 +1053,10 @@ 3400777016E6FE5D00097E8F /* libz.dylib in Frameworks */, 347B7FC816907AE700B5F4B3 /* VideoDecodeAcceleration.framework in Frameworks */, 347B7FC616907A1700B5F4B3 /* libbz2.dylib in Frameworks */, + 341A2D5C1C7AB04700FF7566 /* Sparkle.framework in Frameworks */, 34A64A37165F208800A68428 /* QuartzCore.framework in Frameworks */, 34792AFB1611A6B90065A859 /* IOSurface.framework in Frameworks */, 34792AF616104AA70065A859 /* IOKit.framework in Frameworks */, - 3470F41419C84989000A81C4 /* Sparkle.framework in Frameworks */, 34C6C3BF15FB5B440018A18B /* OpenGL.framework in Frameworks */, 342346BB15F5FBD700C8C77E /* CoreMedia.framework in Frameworks */, 342346B915F5FBD200C8C77E /* CoreVideo.framework in Frameworks */, @@ -970,6 +1108,14 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 340C80111D44722000B2FD7D /* Products */ = { + isa = PBXGroup; + children = ( + 340C80151D44722100B2FD7D /* CSYoutubeStreamServicePlugin.bundle */, + ); + name = Products; + sourceTree = ""; + }; 340FE49215F3417E00E4CE4E = { isa = PBXGroup; children = ( @@ -1002,13 +1148,15 @@ 340FE4A015F3417E00E4CE4E /* Frameworks */ = { isa = PBXGroup; children = ( + 340C7FE81D43D22000B2FD7D /* Security.framework */, + 34EA824D1D3CBFCA00928A06 /* WebKit.framework */, + 341A2D5B1C7AB04700FF7566 /* Sparkle.framework */, 34E9836F1B78AE9100F26F1E /* Python.framework */, 34ED8CB11B0737A3002C0674 /* CoreMIDI.framework */, 34EAFB7F1A1BA86800E12FBD /* CoreMediaIO.framework */, 345F8B341A157433009A81E3 /* CoreAudio.framework */, 345F8B1C1A12AC2C009A81E3 /* AudioUnit.framework */, 345F8B191A1218DE009A81E3 /* AudioToolbox.framework */, - 3470F41319C84989000A81C4 /* Sparkle.framework */, 34A64A36165F208800A68428 /* QuartzCore.framework */, 34AE3C6C164F04C90052C95E /* CoreVideo.framework */, 34AE3C6A164F04B30052C95E /* IOSurface.framework */, @@ -1047,6 +1195,10 @@ 340FE4A715F3417E00E4CE4E /* CocoaSplit */ = { isa = PBXGroup; children = ( + 3494DF341CCD2DB000E921BF /* TPCircularBuffer.c */, + 3494DF351CCD2DB000E921BF /* TPCircularBuffer.h */, + 3494DF361CCD2DB000E921BF /* TPCircularBuffer+AudioBufferList.c */, + 3494DF371CCD2DB000E921BF /* TPCircularBuffer+AudioBufferList.h */, 3460E5861AC764360080358E /* AnimationSamples */, 340FE4B315F3417E00E4CE4E /* AppDelegate.h */, 340FE4B415F3417E00E4CE4E /* AppDelegate.m */, @@ -1113,6 +1265,12 @@ 34BB29801B7652EA00F201C2 /* CSPreviewGLLayer.m */, 34AB1C2F1B944F48003F2C03 /* CSPreviewOverlayView.h */, 34AB1C301B944F48003F2C03 /* CSPreviewOverlayView.m */, + 3400A0601BD44DF0003E1828 /* CSInputLibraryItem.h */, + 3400A0611BD44DF0003E1828 /* CSInputLibraryItem.m */, + 3434CB3A1CB09BC4001B3DF9 /* CSTimedOutputBuffer.h */, + 3434CB3B1CB09BC4001B3DF9 /* CSTimedOutputBuffer.m */, + 34EA822D1D3C568400928A06 /* CSOauth2Authenticator.m */, + 3473D2A71D5685DA00842EEE /* Media.xcassets */, ); path = CocoaSplit; sourceTree = ""; @@ -1150,6 +1308,26 @@ name = "Supporting Files"; sourceTree = ""; }; + 3434CB291CA90C3D001B3DF9 /* Compressor */ = { + isa = PBXGroup; + children = ( + 3434CB2A1CA90D28001B3DF9 /* CSx264CompressorViewController.h */, + 3434CB2B1CA90D28001B3DF9 /* CSx264CompressorViewController.m */, + 3434CB2C1CA90D28001B3DF9 /* CSx264CompressorViewController.xib */, + 3434CB2F1CA91D99001B3DF9 /* CSAppleH264CompressorViewController.h */, + 3434CB301CA91D99001B3DF9 /* CSAppleH264CompressorViewController.m */, + 3434CB311CA91D99001B3DF9 /* CSAppleH264CompressorViewController.xib */, + 3434CB341CA933B3001B3DF9 /* CSCompressorViewControllerProtocol.h */, + 3434CB351CA93BCD001B3DF9 /* CSAppleProResCompressorViewController.h */, + 3434CB361CA93BCD001B3DF9 /* CSAppleProResCompressorViewController.m */, + 3434CB371CA93BCD001B3DF9 /* CSAppleProResCompressorViewController.xib */, + 346865441CC353E2002BAB86 /* CSInstantRecorderCompressorViewController.h */, + 346865451CC353E2002BAB86 /* CSInstantRecorderCompressorViewController.m */, + 346865461CC353E2002BAB86 /* CSInstantRecorderCompressorViewController.xib */, + ); + name = Compressor; + sourceTree = ""; + }; 3435E398188B90610015CC01 /* Shaders */ = { isa = PBXGroup; children = ( @@ -1170,16 +1348,28 @@ name = Products; sourceTree = ""; }; + 343C79C41D0C763A00B36EEC /* Products */ = { + isa = PBXGroup; + children = ( + 343C79CC1D0C763A00B36EEC /* CSFFMpegCapturePlugin.bundle */, + ); + name = Products; + sourceTree = ""; + }; 3441338C18667F280059B686 /* Compressor */ = { isa = PBXGroup; children = ( - 34F5141416FD6E5D00BA894D /* h264Compressor.h */, + 34F5141416FD6E5D00BA894D /* VideoCompressor.h */, 34F5140E16FD6E4F00BA894D /* x264Compressor.h */, 34F5140F16FD6E4F00BA894D /* x264Compressor.m */, 34213E18196680880054E238 /* CompressorBase.h */, 34213E19196680880054E238 /* CompressorBase.m */, 34F5141016FD6E4F00BA894D /* AppleVTCompressor.h */, 34F5141116FD6E4F00BA894D /* AppleVTCompressor.m */, + 3434CB261CA8615A001B3DF9 /* AppleProResCompressor.h */, + 3434CB271CA8615A001B3DF9 /* AppleProResCompressor.m */, + 3414C6AC1CBB9EBF00107C69 /* CSIRCompressor.h */, + 3414C6AD1CBB9EBF00107C69 /* CSIRCompressor.m */, ); name = Compressor; sourceTree = ""; @@ -1196,15 +1386,16 @@ 3441338E18667FE40059B686 /* Interface */ = { isa = PBXGroup; children = ( + 34F04DA91CE0BB770054663A /* NSView+NSLayoutConstraintFilter.h */, + 34F04DAA1CE0BB770054663A /* NSView+NSLayoutConstraintFilter.m */, + 3434CB291CA90C3D001B3DF9 /* Compressor */, 34ABEF3B17C0C2CB00AA5E62 /* advancedPrefPanel.xib */, - 34CFE49C18F1459600092C6A /* AudioMixer.xib */, 344825E41B253D6900AF9EAC /* CAMultiAudioMatrixCell.h */, 344825E51B253D6900AF9EAC /* CAMultiAudioMatrixCell.m */, 34BD1E601B2481C20015CBEC /* CAMultiAudioMatrixMixerWindowController.h */, 34BD1E611B2481C20015CBEC /* CAMultiAudioMatrixMixerWindowController.m */, 34BD1E621B2481C20015CBEC /* CAMultiAudioMatrixMixerWindowController.xib */, 344825D11B2536EA00AF9EAC /* CAMultiAudioMatrixView.xib */, - 34A64A34165F047900A68428 /* CapturePreview.xib */, 340D8C961959C7C100BE5144 /* CompressionSettingsPanel.xib */, 349461721AC49BB300F28883 /* CSAnimationChooserViewController.h */, 349461731AC49BB300F28883 /* CSAnimationChooserViewController.m */, @@ -1225,9 +1416,50 @@ 347B14DB198492D900DC7DF0 /* InputPopupControllerViewController.xib */, 344988121893CF0500044259 /* LogWindow.xib */, 340FE4B615F3417E00E4CE4E /* MainMenu.xib */, - 34D4351A198BE3B700266169 /* NewLayoutPanel.xib */, 34348C2919BDBDC000A122C2 /* PluginManagerWindowController.xib */, 349461441AAD73BC00F28883 /* TestView.xib */, + 34F52A241BC1CE2700662911 /* CSLayoutCollectionItem.h */, + 3450239B1C60B3EF00B461B5 /* CSLayoutCollectionItemView.h */, + 3450239C1C60B3EF00B461B5 /* CSLayoutCollectionItemView.m */, + 34F52A251BC1CE2700662911 /* CSLayoutCollectionItem.m */, + 34F52A261BC1CE2700662911 /* CSLayoutCollectionItem.xib */, + 34F52A291BC1E0F800662911 /* CSLayoutButton.h */, + 34F52A2A1BC1E0F800662911 /* CSLayoutButton.m */, + 343ED9411BCAC1C80061FC4B /* EditBuiltinLayoutView.xib */, + 34F1EB8F1BCCA41F00B38E6C /* CSLayoutEditWindowController.h */, + 34F1EB901BCCA41F00B38E6C /* CSLayoutEditWindowController.m */, + 34F1EB911BCCA41F00B38E6C /* CSLayoutEditWindowController.xib */, + 3400A0761BD4533A003E1828 /* CSInputLibraryWindowController.h */, + 3400A0771BD4533A003E1828 /* CSInputLibraryWindowController.m */, + 3400A0781BD4533A003E1828 /* CSInputLibraryWindowController.xib */, + 3400A07D1BD49406003E1828 /* CSInputLibraryItemView.xib */, + 3400A0821BD522A5003E1828 /* CSLibraryInputItemViewController.h */, + 3400A0831BD522A5003E1828 /* CSLibraryInputItemViewController.m */, + 34DA1D3B1BF823E700132486 /* CSNewOutputWindowController.h */, + 34DA1D3C1BF823E700132486 /* CSNewOutputWindowController.m */, + 34DA1D3D1BF823E700132486 /* CSNewOutputWindowController.xib */, + 34A7ADB61C01EF1400685044 /* CompressionSettingsPanelController.h */, + 34A7ADB71C01EF1400685044 /* CompressionSettingsPanelController.m */, + 34F04D9C1CE0381D0054663A /* CSAddInputViewController.h */, + 34F04D9D1CE0381D0054663A /* CSAddInputViewController.m */, + 34F04D9E1CE0381D0054663A /* CSAddInputViewController.xib */, + 3473D2971D4C3D0E00842EEE /* CSAddOutputPopupViewController.h */, + 3473D2981D4C3D0E00842EEE /* CSAddOutputPopupViewController.m */, + 3473D2991D4C3D0E00842EEE /* CSAddOutputPopupViewController.xib */, + 3473D2A41D56391700842EEE /* CSAudioLevelView.h */, + 3473D2A51D56391700842EEE /* CSAudioLevelView.m */, + 3473D2AB1D56920B00842EEE /* CSAdvancedAudioWindowController.h */, + 3473D2AC1D56920B00842EEE /* CSAdvancedAudioWindowController.m */, + 3473D2AD1D56920B00842EEE /* CSAdvancedAudioWindowController.xib */, + 3470CD0E1D57065B006404D9 /* CSAdvancedAudioCollectionViewItem.xib */, + 3470CD261D57A088006404D9 /* CSAnimationWindowController.h */, + 3470CD271D57A088006404D9 /* CSAnimationWindowController.m */, + 3470CD281D57A088006404D9 /* CSAnimationWindowController.xib */, + 3470CD2B1D57D2AA006404D9 /* CSStreamOutputWindowController.h */, + 3470CD2C1D57D2AA006404D9 /* CSStreamOutputWindowController.m */, + 3470CD2D1D57D2AA006404D9 /* CSStreamOutputWindowController.xib */, + 3470CD381D5EED3A006404D9 /* CSNobarSliderCell.h */, + 3470CD391D5EED3A006404D9 /* CSNobarSliderCell.m */, ); name = Interface; sourceTree = ""; @@ -1267,6 +1499,7 @@ 34AFC2C119B08AF30007C07B /* CSStreamServiceProtocol.h */, 34D657F519B33DCF0012E32B /* CSPluginFactoryProtocol.h */, 34A7C17A19B98EA200BC6882 /* CSExtraPluginProtocol.h */, + 34EA822C1D3C568400928A06 /* CSOauth2Authenticator.h */, 3408234619BC50AD00CD1F5F /* CSNotifications.h */, ); name = PluginHeaders; @@ -1313,6 +1546,14 @@ path = AnimationSamples; sourceTree = ""; }; + 3473D27A1D4C349700842EEE /* Products */ = { + isa = PBXGroup; + children = ( + 3473D27E1D4C349700842EEE /* CSVaughnliveStreamServicePlugin.bundle */, + ); + name = Products; + sourceTree = ""; + }; 349461581AB420E600F28883 /* CSAnimationRunner */ = { isa = PBXGroup; children = ( @@ -1374,6 +1615,7 @@ 34AFC24319B058800007C07B /* CapturePlugins */ = { isa = PBXGroup; children = ( + 343C79C31D0C763A00B36EEC /* CSFFMpegCapturePlugin.xcodeproj */, 3436921B1B62364E007EC2D9 /* CSShapeCapturePlugin.xcodeproj */, 34F7C55A1B2C908700345230 /* CSDeckLinkCapturePlugin.xcodeproj */, 34FDA8071A84BF9300E7F65E /* CSTimeCapturePlugin.xcodeproj */, @@ -1482,6 +1724,8 @@ 34AFC2E619B08C1E0007C07B /* StreamServicePlugins */ = { isa = PBXGroup; children = ( + 3473D2791D4C349700842EEE /* CSVaughnliveStreamServicePlugin.xcodeproj */, + 340C80101D44722000B2FD7D /* CSYoutubeStreamServicePlugin.xcodeproj */, 34C67B731A2D7B690012DC1B /* CSHitboxStreamServicePlugin.xcodeproj */, 34AFC31319B0A5800007C07B /* CSTwitchStreamServicePlugin.xcodeproj */, 34AFC2E019B08C050007C07B /* CSFileStreamServicePlugin.xcodeproj */, @@ -1699,15 +1943,14 @@ 340FE49B15F3417E00E4CE4E /* Resources */, 3435E39C188BD80A0015CC01 /* CopyFiles */, 34D43556198DD00F00266169 /* Copy Files */, - 3470F41519C849D9000A81C4 /* CopyFiles */, + 3470F41519C849D9000A81C4 /* Copy Files */, 3460E5891AC766120080358E /* CopyFiles */, 343644051B78EFF90069B8F2 /* CopyFiles */, - 344492211BC040D000BE9603 /* ShellScript */, + 34BAAD3B1C13DF8F00A48C76 /* ShellScript */, ); buildRules = ( ); dependencies = ( - 34AE3C41164E3FAD0052C95E /* PBXTargetDependency */, ); name = CocoaSplit; productName = CocoaSplit; @@ -1801,6 +2044,10 @@ ProductGroup = 34AFC24619B05ADA0007C07B /* Products */; ProjectRef = 34AFC24519B05ADA0007C07B /* CSDesktopCapturePlugin.xcodeproj */; }, + { + ProductGroup = 343C79C41D0C763A00B36EEC /* Products */; + ProjectRef = 343C79C31D0C763A00B36EEC /* CSFFMpegCapturePlugin.xcodeproj */; + }, { ProductGroup = 34AFC32C19B0AD500007C07B /* Products */; ProjectRef = 34AFC2E019B08C050007C07B /* CSFileStreamServicePlugin.xcodeproj */; @@ -1853,10 +2100,18 @@ ProductGroup = 34AFC32719B0AD360007C07B /* Products */; ProjectRef = 34AFC31319B0A5800007C07B /* CSTwitchStreamServicePlugin.xcodeproj */; }, + { + ProductGroup = 3473D27A1D4C349700842EEE /* Products */; + ProjectRef = 3473D2791D4C349700842EEE /* CSVaughnliveStreamServicePlugin.xcodeproj */; + }, { ProductGroup = 34AFC25519B05B470007C07B /* Products */; ProjectRef = 34576C9819AFE6F7007BAD90 /* CSWindowCapturePlugin.xcodeproj */; }, + { + ProductGroup = 340C80111D44722000B2FD7D /* Products */; + ProjectRef = 340C80101D44722000B2FD7D /* CSYoutubeStreamServicePlugin.xcodeproj */; + }, { ProductGroup = 34AFC27319B05BF50007C07B /* Products */; ProjectRef = 34D43550198DCE3800266169 /* TextureWrapPlugin.xcodeproj */; @@ -1872,6 +2127,13 @@ /* End PBXProject section */ /* Begin PBXReferenceProxy section */ + 340C80151D44722100B2FD7D /* CSYoutubeStreamServicePlugin.bundle */ = { + isa = PBXReferenceProxy; + fileType = wrapper.cfbundle; + path = CSYoutubeStreamServicePlugin.bundle; + remoteRef = 340C80141D44722100B2FD7D /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; 3436922B1B62364E007EC2D9 /* CSShapeCapturePlugin.bundle */ = { isa = PBXReferenceProxy; fileType = wrapper.cfbundle; @@ -1879,6 +2141,20 @@ remoteRef = 3436922A1B62364E007EC2D9 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; + 343C79CC1D0C763A00B36EEC /* CSFFMpegCapturePlugin.bundle */ = { + isa = PBXReferenceProxy; + fileType = wrapper.cfbundle; + path = CSFFMpegCapturePlugin.bundle; + remoteRef = 343C79CB1D0C763A00B36EEC /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 3473D27E1D4C349700842EEE /* CSVaughnliveStreamServicePlugin.bundle */ = { + isa = PBXReferenceProxy; + fileType = wrapper.cfbundle; + path = CSVaughnliveStreamServicePlugin.bundle; + remoteRef = 3473D27D1D4C349700842EEE /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; 34A7C1A119B9A41A00BC6882 /* CSLayoutSwitcherExtraPlugin.bundle */ = { isa = PBXReferenceProxy; fileType = wrapper.cfbundle; @@ -2012,31 +2288,45 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 34F52A281BC1CE2700662911 /* CSLayoutCollectionItem.xib in Resources */, 347B14DD198492D900DC7DF0 /* InputPopupControllerViewController.xib in Resources */, + 3470CD2A1D57A088006404D9 /* CSAnimationWindowController.xib in Resources */, + 3400A07A1BD4533A003E1828 /* CSInputLibraryWindowController.xib in Resources */, 34ED8CB91B073C1B002C0674 /* CSMidiManagerWindowController.xib in Resources */, + 3470CD0F1D57065B006404D9 /* CSAdvancedAudioCollectionViewItem.xib in Resources */, 340FE4AC15F3417E00E4CE4E /* InfoPlist.strings in Resources */, 34BD1E651B2481C20015CBEC /* CAMultiAudioMatrixMixerWindowController.xib in Resources */, + 3400A07E1BD49406003E1828 /* CSInputLibraryItemView.xib in Resources */, 340FE4B215F3417E00E4CE4E /* Credits.rtf in Resources */, 340FE4B815F3417E00E4CE4E /* MainMenu.xib in Resources */, - 34D4351B198BE3B700266169 /* NewLayoutPanel.xib in Resources */, + 34DA1D3F1BF823E700132486 /* CSNewOutputWindowController.xib in Resources */, + 34F1EB931BCCA41F00B38E6C /* CSLayoutEditWindowController.xib in Resources */, 342B33B61980771200492CB7 /* line.vtsh in Resources */, 34938EEF1AE4D24200F3B1CF /* CSFilterChooserWindowController.xib in Resources */, + 3434CB2E1CA90D28001B3DF9 /* CSx264CompressorViewController.xib in Resources */, 3435E39B188B93150015CC01 /* passthrough.vtsh in Resources */, 34B5FCE119BF1C3F00F67D19 /* CreateLayoutViewController.xib in Resources */, 3479A9971A9007DD00A524F0 /* CocoaSplit.sdef in Resources */, 340D8C971959C7C100BE5144 /* CompressionSettingsPanel.xib in Resources */, - 34CFE49D18F1459600092C6A /* AudioMixer.xib in Resources */, + 3434CB331CA91D99001B3DF9 /* CSAppleH264CompressorViewController.xib in Resources */, 344988131893CF0500044259 /* LogWindow.xib in Resources */, - 34A64A35165F047900A68428 /* CapturePreview.xib in Resources */, 34D696FC1871B8F700B50EB6 /* Defaults.plist in Resources */, 34348C2B19BDBDC000A122C2 /* PluginManagerWindowController.xib in Resources */, 34ABEF3C17C0C2CB00AA5E62 /* advancedPrefPanel.xib in Resources */, + 346865481CC353E2002BAB86 /* CSInstantRecorderCompressorViewController.xib in Resources */, + 3434CB391CA93BCD001B3DF9 /* CSAppleProResCompressorViewController.xib in Resources */, 3435E396188B902C0015CC01 /* 420v.fgsh in Resources */, + 3473D2AF1D56920B00842EEE /* CSAdvancedAudioWindowController.xib in Resources */, + 343ED9421BCAC1C80061FC4B /* EditBuiltinLayoutView.xib in Resources */, 3415CED31AE257DA002F11F5 /* CSTextCaptureBaseView.xib in Resources */, 342B33B3198073EC00492CB7 /* line.fgsh in Resources */, + 3470CD2F1D57D2AA006404D9 /* CSStreamOutputWindowController.xib in Resources */, 344825D21B2536EA00AF9EAC /* CAMultiAudioMatrixView.xib in Resources */, + 3473D2A81D5685DA00842EEE /* Media.xcassets in Resources */, + 3473D29B1D4C3D0E00842EEE /* CSAddOutputPopupViewController.xib in Resources */, 349461761AC49BB300F28883 /* CSAnimationChooserViewController.xib in Resources */, 349461451AAD73BC00F28883 /* TestView.xib in Resources */, + 34F04DA01CE0381D0054663A /* CSAddInputViewController.xib in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2051,7 +2341,7 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 344492211BC040D000BE9603 /* ShellScript */ = { + 34BAAD3B1C13DF8F00A48C76 /* ShellScript */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -2062,7 +2352,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "LOCATION=\"${BUILT_PRODUCTS_DIR}\"/\"${CONTENTS_FOLDER_PATH}\"\nIDENTITY=\"Mac Developer: Zachary Girouard (5ZF5EX2CV8)\"\ncodesign --verbose --force --sign \"$IDENTITY\" \"$LOCATION/Frameworks/Sparkle.framework\"\ncodesign --verbose --force --sign \"$IDENTITY\" \"$LOCATION/MacOS/CocoaSplitCmd\"\ncodesign --verbose --force --sign \"$IDENTITY\" \"$LOCATION/PlugIns/CSAVFCapturePlugin.bundle\"\ncodesign --verbose --force --sign \"$IDENTITY\" \"$LOCATION/PlugIns/CSLayoutSwitcherExtraPlugin.bundle\"\ncodesign --verbose --force --sign \"$IDENTITY\" \"$LOCATION/PlugIns/CSFileStreamServicePlugin.bundle\"\ncodesign --verbose --force --sign \"$IDENTITY\" \"$LOCATION/PlugIns/CSTwitchStreamServicePlugin.bundle\"\ncodesign --verbose --force --sign \"$IDENTITY\" \"$LOCATION/PlugIns/CSChromaKey.plugin\"\ncodesign --verbose --force --sign \"$IDENTITY\" \"$LOCATION/PlugIns/TextureWrapPlugin.plugin\"\ncodesign --verbose --force --sign \"$IDENTITY\" \"$LOCATION/PlugIns/CSDesktopCapturePlugin.bundle\"\ncodesign --verbose --force --sign \"$IDENTITY\" \"$LOCATION/PlugIns/CSMovieCapturePlugin.bundle\"\ncodesign --verbose --force --sign \"$IDENTITY\" \"$LOCATION/PlugIns/CSWindowCapturePlugin.bundle\"\ncodesign --verbose --force --sign \"$IDENTITY\" \"$LOCATION/PlugIns/CSImageCapturePlugin.bundle\"\ncodesign --verbose --force --sign \"$IDENTITY\" \"$LOCATION/PlugIns/CSTextCapturePlugin.bundle\"\ncodesign --verbose --force --sign \"$IDENTITY\" \"$LOCATION/PlugIns/CSSyphonCapturePlugin.bundle/Contents/Frameworks/Syphon.framework\"\ncodesign --verbose --force --sign \"$IDENTITY\" \"$LOCATION/PlugIns/CSSyphonCapturePlugin.bundle\"\ncodesign --verbose --force --sign \"$IDENTITY\" \"$LOCATION/XPCServices/zakk.lol.QTCaptureHelper.xpc\"\ncodesign --verbose --force --sign \"$IDENTITY\" \"$LOCATION/PlugIns/CSSyphonInjectExtraPlugin.bundle\"\ncodesign --verbose --deep --force --sign \"$IDENTITY\" \"$LOCATION/Resources/CSAnimationRunner.plugin\"\ncodesign --verbose --deep --force --sign \"$IDENTITY\" \"$LOCATION/PlugIns/CSDeckLinkCapturePlugin.bundle\"\ncodesign --verbose --deep --force --sign \"$IDENTITY\" \"$LOCATION/PlugIns/CSNowPlayingPlugin.bundle\"\ncodesign --verbose --deep --force --sign \"$IDENTITY\" \"$LOCATION/PlugIns/CSShapeCapturePlugin.bundle\"\n"; + shellScript = "#LOCATION=\"${BUILT_PRODUCTS_DIR}\"/\"${CONTENTS_FOLDER_PATH}\"\n#IDENTITY=\"Developer ID Application: Zachary Girouard (L3JLUY4S5E)\"\n#codesign --verbose --force --sign \"$IDENTITY\" \"$LOCATION/Frameworks/Sparkle.framework\"\n#codesign --verbose --force --sign \"$IDENTITY\" \"$LOCATION/MacOS/CocoaSplitCmd\"\n#codesign --verbose --force --sign \"$IDENTITY\" \"$LOCATION/PlugIns/CSAVFCapturePlugin.bundle\"\n#codesign --verbose --force --sign \"$IDENTITY\" \"$LOCATION/PlugIns/CSLayoutSwitcherExtraPlugin.bundle\"\n#codesign --verbose --force --sign \"$IDENTITY\" \"$LOCATION/PlugIns/CSFileStreamServicePlugin.bundle\"\n#codesign --verbose --force --sign \"$IDENTITY\" \"$LOCATION/PlugIns/CSTwitchStreamServicePlugin.bundle\"\n#codesign --verbose --force --sign \"$IDENTITY\" \"$LOCATION/PlugIns/CSChromaKey.plugin\"\n#codesign --verbose --force --sign \"$IDENTITY\" \"$LOCATION/PlugIns/TextureWrapPlugin.plugin\"\n#codesign --verbose --force --sign \"$IDENTITY\" \"$LOCATION/PlugIns/CSDesktopCapturePlugin.bundle\"\n#codesign --verbose --force --sign \"$IDENTITY\" \"$LOCATION/PlugIns/CSMovieCapturePlugin.bundle\"\n#codesign --verbose --force --sign \"$IDENTITY\" \"$LOCATION/PlugIns/CSWindowCapturePlugin.bundle\"\n#codesign --verbose --force --sign \"$IDENTITY\" \"$LOCATION/PlugIns/CSImageCapturePlugin.bundle\"\n#codesign --verbose --force --sign \"$IDENTITY\" \"$LOCATION/PlugIns/CSTextCapturePlugin.bundle\"\n#codesign --verbose --force --sign \"$IDENTITY\" \"$LOCATION/PlugIns/CSSyphonCapturePlugin.bundle/Contents/Frameworks/Syphon.framework\"\n#codesign --verbose --force --sign \"$IDENTITY\" \"$LOCATION/PlugIns/CSSyphonCapturePlugin.bundle\"\n#codesign --verbose --force --sign \"$IDENTITY\" \"$LOCATION/XPCServices/zakk.lol.QTCaptureHelper.xpc\"\n#codesign --verbose --force --sign \"$IDENTITY\" \"$LOCATION/PlugIns/CSSyphonInjectExtraPlugin.bundle\"\n#codesign --verbose --deep --force --sign \"$IDENTITY\" \"$LOCATION/Resources/CSAnimationRunner.plugin\"\n#codesign --verbose --deep --force --sign \"$IDENTITY\" \"$LOCATION/PlugIns/CSDeckLinkCapturePlugin.bundle\"\n#codesign --verbose --deep --force --sign \"$IDENTITY\" \"$LOCATION/PlugIns/CSNowPlayingPlugin.bundle\"\n#codesign --verbose --deep --force --sign \"$IDENTITY\" \"$LOCATION/PlugIns/CSShapeCapturePlugin.bundle\"\n#codesign --verbose --deep --force --sign \"$IDENTITY\" \"$LOCATION/Resources/Animations/movement.py\"\n#codesign --verbose --deep --force --sign \"$IDENTITY\" \"$LOCATION/Resources/Animations/rotate.py\"\n"; }; /* End PBXShellScriptBuildPhase section */ @@ -2076,18 +2366,22 @@ 345F8B791A183D1D009A81E3 /* CAMultiAudioEngine.m in Sources */, 340FE4AE15F3417E00E4CE4E /* main.m in Sources */, 34ED8C891B07371C002C0674 /* MIKMIDIMetaTrackSequenceNameEvent.m in Sources */, + 3470CD3A1D5EED3A006404D9 /* CSNobarSliderCell.m in Sources */, 34ED8C931B07371C002C0674 /* MIKMIDIObject.m in Sources */, 346CF44C1A5C10C2008E5BFF /* CSInputLayer.m in Sources */, 345F8B711A177C1F009A81E3 /* CAMultiAudioConverter.m in Sources */, 34C19053189EE60400AB2430 /* CapturedFrameData.m in Sources */, 34ED8C4B1B07371C002C0674 /* MIKMIDIClientDestinationEndpoint.m in Sources */, 34ED8C871B07371C002C0674 /* MIKMIDIMetaTimeSignatureEvent.m in Sources */, + 34F04DAB1CE0BB770054663A /* NSView+NSLayoutConstraintFilter.m in Sources */, 34ED8C7B1B07371C002C0674 /* MIKMIDIMetaInstrumentNameEvent.m in Sources */, 34ED8C8B1B07371C002C0674 /* MIKMIDIMetronome.m in Sources */, + 3470CD291D57A088006404D9 /* CSAnimationWindowController.m in Sources */, 345F8B6D1A176A5D009A81E3 /* CAMultiAudioUnit.m in Sources */, 34ED8C771B07371C002C0674 /* MIKMIDIMetaCuePointEvent.m in Sources */, 34D2D5731A547628001004E5 /* CSTextCaptureBase.m in Sources */, 340FE4B515F3417E00E4CE4E /* AppDelegate.m in Sources */, + 34F04D9F1CE0381D0054663A /* CSAddInputViewController.m in Sources */, 345F8B4F1A15F7DA009A81E3 /* CAMultiAudioPCMPlayer.m in Sources */, 34ED8C5B1B07371C002C0674 /* MIKMIDIDeviceManager.m in Sources */, 34AFC33319B19CD00007C07B /* SourceCache.m in Sources */, @@ -2099,6 +2393,9 @@ 34ED8C9D1B07371C002C0674 /* MIKMIDIProgramChangeCommand.m in Sources */, 34ED8C731B07371C002C0674 /* MIKMIDIMappingXMLParser.m in Sources */, 34BD1E631B2481C20015CBEC /* CAMultiAudioMatrixMixerWindowController.m in Sources */, + 3434CB381CA93BCD001B3DF9 /* CSAppleProResCompressorViewController.m in Sources */, + 3434CB321CA91D99001B3DF9 /* CSAppleH264CompressorViewController.m in Sources */, + 34F52A271BC1CE2700662911 /* CSLayoutCollectionItem.m in Sources */, 347277FD1A5A2758008801A9 /* CSIOSurfaceLayer.m in Sources */, 344825E61B253D6900AF9EAC /* CAMultiAudioMatrixCell.m in Sources */, 34BD1E4D1B1F1A2D0015CBEC /* CAMultiAudioDownmixer.m in Sources */, @@ -2112,7 +2409,9 @@ 34ED8C9F1B07371C002C0674 /* MIKMIDISequence.m in Sources */, 34ED8C671B07371C002C0674 /* MIKMIDIEventIterator.m in Sources */, 34ED8CBC1B07763F002C0674 /* CSMidiWrapper.m in Sources */, + 34DA1D3E1BF823E700132486 /* CSNewOutputWindowController.m in Sources */, 34ED8C691B07371C002C0674 /* MIKMIDIInputPort.m in Sources */, + 3434CB281CA8615A001B3DF9 /* AppleProResCompressor.m in Sources */, 34ED8C531B07371C002C0674 /* MIKMIDICommandThrottler.m in Sources */, 345F8B611A161552009A81E3 /* CAMultiAudioAVCapturePlayer.m in Sources */, 34ED8CAD1B07371C002C0674 /* MIKMIDIUtilities.m in Sources */, @@ -2130,10 +2429,12 @@ 34ED8C9B1B07371C002C0674 /* MIKMIDIPrivateUtilities.m in Sources */, 345F8B391A15D850009A81E3 /* CAMultiAudioDefaultOutput.m in Sources */, 34ED8CA51B07371C002C0674 /* MIKMIDISystemExclusiveCommand.m in Sources */, + 3473D29A1D4C3D0E00842EEE /* CSAddOutputPopupViewController.m in Sources */, 34ED8C5D1B07371C002C0674 /* MIKMIDIEndpoint.m in Sources */, 34ED8C5F1B07371C002C0674 /* MIKMIDIEndpointSynthesizer.m in Sources */, 34ED8CA11B07371C002C0674 /* MIKMIDISequencer.m in Sources */, 34ED8C751B07371C002C0674 /* MIKMIDIMetaCopyrightEvent.m in Sources */, + 3434CB3C1CB09BC4001B3DF9 /* CSTimedOutputBuffer.m in Sources */, 34ED8CB71B073C1B002C0674 /* CSMidiManagerWindowController.m in Sources */, 3408235519BC677A00CD1F5F /* CSNotifications.m in Sources */, 347B14DC198492D900DC7DF0 /* InputPopupControllerViewController.m in Sources */, @@ -2144,23 +2445,37 @@ 34A64A2F165EFE4C00A68428 /* PreviewView.m in Sources */, 34ED8CAB1B07371C002C0674 /* MIKMIDITrack.m in Sources */, 34ED8CA91B07371C002C0674 /* MIKMIDITempoEvent.m in Sources */, + 3450239D1C60B3EF00B461B5 /* CSLayoutCollectionItemView.m in Sources */, + 3400A0841BD522A5003E1828 /* CSLibraryInputItemViewController.m in Sources */, 34ED8C631B07371C002C0674 /* MIKMIDIErrors.m in Sources */, 34ED8C551B07371C002C0674 /* MIKMIDIControlChangeCommand.m in Sources */, + 3494DF381CCD2DB000E921BF /* TPCircularBuffer.c in Sources */, 349461681ABC57C100F28883 /* CSAnimationItem.m in Sources */, 34ED8C971B07371C002C0674 /* MIKMIDIPlayer.m in Sources */, 34ED8C591B07371C002C0674 /* MIKMIDIDevice.m in Sources */, + 3400A0791BD4533A003E1828 /* CSInputLibraryWindowController.m in Sources */, 345F8B691A16C348009A81E3 /* CAMultiAudioGraph.m in Sources */, 3431FFE119786502000965FE /* InputSource.m in Sources */, + 3414C6AE1CBB9EBF00107C69 /* CSIRCompressor.m in Sources */, 34ED8C911B07371C002C0674 /* MIKMIDINoteOnCommand.m in Sources */, + 346865471CC353E2002BAB86 /* CSInstantRecorderCompressorViewController.m in Sources */, + 3494DF391CCD2DB000E921BF /* TPCircularBuffer+AudioBufferList.c in Sources */, + 3473D2A61D56391700842EEE /* CSAudioLevelView.m in Sources */, 3498D94F1A9472BD00906532 /* CSAudioSwitchControl.m in Sources */, + 3470CD2E1D57D2AA006404D9 /* CSStreamOutputWindowController.m in Sources */, + 3400A0621BD44DF0003E1828 /* CSInputLibraryItem.m in Sources */, 34ED8C831B07371C002C0674 /* MIKMIDIMetaSequenceEvent.m in Sources */, + 3434CB2D1CA90D28001B3DF9 /* CSx264CompressorViewController.m in Sources */, 34ED8C6D1B07371C002C0674 /* MIKMIDIMapping.m in Sources */, + 34F1EB921BCCA41F00B38E6C /* CSLayoutEditWindowController.m in Sources */, 34ED8CA31B07371C002C0674 /* MIKMIDISourceEndpoint.m in Sources */, 34ED8C4D1B07371C002C0674 /* MIKMIDIClientSourceEndpoint.m in Sources */, 34ED8C811B07371C002C0674 /* MIKMIDIMetaMarkerTextEvent.m in Sources */, 34F5141216FD6E4F00BA894D /* x264Compressor.m in Sources */, + 34A7ADB81C01EF1400685044 /* CompressionSettingsPanelController.m in Sources */, 34DC2FB01B512362008F12A2 /* CSCaptureBase+TimerDelegate.m in Sources */, 34ED8C791B07371C002C0674 /* MIKMIDIMetaEvent.m in Sources */, + 34F52A2B1BC1E0F900662911 /* CSLayoutButton.m in Sources */, 34576C7819AFCA1B007BAD90 /* CSPluginLoader.m in Sources */, 34FDD6F21A215268009A7413 /* CSPluginServices.m in Sources */, 34ED8C8F1B07371C002C0674 /* MIKMIDINoteOffCommand.m in Sources */, @@ -2171,7 +2486,9 @@ 34DF75581AA272F100DA9FDE /* LayoutRenderer.m in Sources */, 34ED8C651B07371C002C0674 /* MIKMIDIEvent.m in Sources */, 34ED8C7D1B07371C002C0674 /* MIKMIDIMetaKeySignatureEvent.m in Sources */, + 3473D2AE1D56920B00842EEE /* CSAdvancedAudioWindowController.m in Sources */, 348AC06B19B406910064F02D /* SourceLayout.m in Sources */, + 34EA822E1D3C568400928A06 /* CSOauth2Authenticator.m in Sources */, 345F8B751A17A785009A81E3 /* CAMultiAudioMixer.m in Sources */, 34ED8C951B07371C002C0674 /* MIKMIDIOutputPort.m in Sources */, 34ED8CA71B07371C002C0674 /* MIKMIDISystemMessageCommand.m in Sources */, @@ -2303,14 +2620,6 @@ }; /* End PBXSourcesBuildPhase section */ -/* Begin PBXTargetDependency section */ - 34AE3C41164E3FAD0052C95E /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 34AE3C2D164E3E020052C95E /* QTCaptureHelper */; - targetProxy = 34AE3C40164E3FAD0052C95E /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - /* Begin PBXVariantGroup section */ 340FE4AA15F3417E00E4CE4E /* InfoPlist.strings */ = { isa = PBXVariantGroup; @@ -2363,7 +2672,7 @@ CLANG_CXX_LANGUAGE_STANDARD = "compiler-default"; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - CODE_SIGN_IDENTITY = "Mac Developer"; + CODE_SIGN_IDENTITY = "Developer ID Application: Zachary Girouard (L3JLUY4S5E)"; COPY_PHASE_STRIP = NO; GCC_C_LANGUAGE_STANDARD = "compiler-default"; GCC_DYNAMIC_NO_PIC = NO; @@ -2393,7 +2702,7 @@ CLANG_CXX_LANGUAGE_STANDARD = "compiler-default"; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - CODE_SIGN_IDENTITY = "Mac Developer"; + CODE_SIGN_IDENTITY = "Developer ID Application: Zachary Girouard (L3JLUY4S5E)"; COPY_PHASE_STRIP = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; GCC_C_LANGUAGE_STANDARD = "compiler-default"; @@ -2414,11 +2723,13 @@ buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; ARCHS = "$(ARCHS_STANDARD_64_BIT)"; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CODE_SIGN_ENTITLEMENTS = ""; - CODE_SIGN_IDENTITY = "Developer ID Application"; - "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Developer ID Application"; + CODE_SIGN_IDENTITY = "Mac Developer"; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Mac Developer"; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = L3JLUY4S5E; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(SRCROOT)", @@ -2430,19 +2741,22 @@ GCC_WARN_UNUSED_PARAMETER = NO; HEADER_SEARCH_PATHS = /usr/local/include; INFOPLIST_FILE = "CocoaSplit/CocoaSplit-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "@loader_path/../Frameworks"; "LIBRARY_SEARCH_PATHS[arch=*]" = ""; - MACOSX_DEPLOYMENT_TARGET = 10.9; + MACOSX_DEPLOYMENT_TARGET = 10.8; "OTHER_LDFLAGS[arch=*]" = ( + /usr/local/lib/libswresample.a, /usr/local/lib/libavutil.a, /usr/local/lib/libavformat.a, /usr/local/lib/libavcodec.a, /usr/local/lib/libmp3lame.a, /usr/local/lib/libswscale.a, /usr/local/lib/libx264.a, + "-all_load", ); PRODUCT_NAME = CocoaSplit; PROVISIONING_PROFILE = ""; - SDKROOT = macosx10.11; + SDKROOT = macosx; STRIP_STYLE = debugging; WRAPPER_EXTENSION = app; }; @@ -2453,12 +2767,14 @@ buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; ARCHS = "$(ARCHS_STANDARD_64_BIT)"; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CODE_SIGN_ENTITLEMENTS = ""; - CODE_SIGN_IDENTITY = "Developer ID Application"; - "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Developer ID Application"; + CODE_SIGN_IDENTITY = "Mac Developer"; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Mac Developer"; COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = NO; + DEVELOPMENT_TEAM = L3JLUY4S5E; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(SRCROOT)", @@ -2471,8 +2787,10 @@ GCC_WARN_UNUSED_PARAMETER = NO; HEADER_SEARCH_PATHS = /usr/local/include; INFOPLIST_FILE = "CocoaSplit/CocoaSplit-Info.plist"; - MACOSX_DEPLOYMENT_TARGET = 10.9; + LD_RUNPATH_SEARCH_PATHS = "@loader_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.8; "OTHER_LDFLAGS[arch=*]" = ( + /usr/local/lib/libswresample.a, /usr/local/lib/libavformat.a, /usr/local/lib/libavutil.a, /usr/local/lib/libavcodec.a, @@ -2480,10 +2798,11 @@ /usr/local/lib/libswscale.a, /usr/local/lib/libx264.a, "-flat_namespace", + "-all_load", ); PRODUCT_NAME = CocoaSplit; PROVISIONING_PROFILE = ""; - SDKROOT = macosx10.11; + SDKROOT = macosx; STRIP_STYLE = debugging; WRAPPER_EXTENSION = app; }; @@ -2517,7 +2836,7 @@ ); PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE = ""; - SDKROOT = macosx10.11; + SDKROOT = macosx10.10; SKIP_INSTALL = YES; }; name = Debug; @@ -2549,7 +2868,7 @@ ); PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE = ""; - SDKROOT = macosx10.11; + SDKROOT = macosx10.10; SKIP_INSTALL = YES; }; name = Release; @@ -2570,7 +2889,7 @@ MACOSX_DEPLOYMENT_TARGET = 10.8; ONLY_ACTIVE_ARCH = YES; PRODUCT_NAME = "zakk.lol.$(TARGET_NAME:rfc1034identifier)"; - SDKROOT = macosx10.11; + SDKROOT = macosx10.9; VALID_ARCHS = "i386 x86_64"; WRAPPER_EXTENSION = xpc; }; @@ -2591,7 +2910,7 @@ MACH_O_TYPE = mh_execute; MACOSX_DEPLOYMENT_TARGET = 10.8; PRODUCT_NAME = "zakk.lol.$(TARGET_NAME:rfc1034identifier)"; - SDKROOT = macosx10.11; + SDKROOT = macosx10.9; VALID_ARCHS = "i386 x86_64"; WRAPPER_EXTENSION = xpc; }; diff --git a/CocoaSplit.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/CocoaSplit.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..919434a6 --- /dev/null +++ b/CocoaSplit.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/CocoaSplit.xcodeproj/project.xcworkspace/xcshareddata/CocoaSplit.xcscmblueprint b/CocoaSplit.xcodeproj/project.xcworkspace/xcshareddata/CocoaSplit.xcscmblueprint new file mode 100644 index 00000000..7632881c --- /dev/null +++ b/CocoaSplit.xcodeproj/project.xcworkspace/xcshareddata/CocoaSplit.xcscmblueprint @@ -0,0 +1,30 @@ +{ + "DVTSourceControlWorkspaceBlueprintPrimaryRemoteRepositoryKey" : "2F75B324D6E95041E8A2E2A8BC21974740F08BF7", + "DVTSourceControlWorkspaceBlueprintWorkingCopyRepositoryLocationsKey" : { + + }, + "DVTSourceControlWorkspaceBlueprintWorkingCopyStatesKey" : { + "2F75B324D6E95041E8A2E2A8BC21974740F08BF7" : 0, + "7360F7D45D6592BBC0CCBB41E9FC383DA90D4985" : 0 + }, + "DVTSourceControlWorkspaceBlueprintIdentifierKey" : "E6022B3D-C90B-4603-869E-23206BED5F1F", + "DVTSourceControlWorkspaceBlueprintWorkingCopyPathsKey" : { + "2F75B324D6E95041E8A2E2A8BC21974740F08BF7" : "CocoaSplit\/", + "7360F7D45D6592BBC0CCBB41E9FC383DA90D4985" : "CocoaSplit\/CapturePlugins\/CSSyphonCapturePlugin\/Syphon-Framework\/" + }, + "DVTSourceControlWorkspaceBlueprintNameKey" : "CocoaSplit", + "DVTSourceControlWorkspaceBlueprintVersion" : 204, + "DVTSourceControlWorkspaceBlueprintRelativePathToProjectKey" : "CocoaSplit.xcodeproj", + "DVTSourceControlWorkspaceBlueprintRemoteRepositoriesKey" : [ + { + "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "github.com:zakk4223\/CocoaSplit.git", + "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git", + "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "2F75B324D6E95041E8A2E2A8BC21974740F08BF7" + }, + { + "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/palana\/Syphon-Framework.git", + "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git", + "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "7360F7D45D6592BBC0CCBB41E9FC383DA90D4985" + } + ] +} \ No newline at end of file diff --git a/CocoaSplit.xcodeproj/project.xcworkspace/xcuserdata/zakk.xcuserdatad/UserInterfaceState.xcuserstate b/CocoaSplit.xcodeproj/project.xcworkspace/xcuserdata/zakk.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 00000000..feee06d7 Binary files /dev/null and b/CocoaSplit.xcodeproj/project.xcworkspace/xcuserdata/zakk.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/CocoaSplit.xcodeproj/xcuserdata/zakk.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/CocoaSplit.xcodeproj/xcuserdata/zakk.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist new file mode 100644 index 00000000..59ec3458 --- /dev/null +++ b/CocoaSplit.xcodeproj/xcuserdata/zakk.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -0,0 +1,17 @@ + + + + + + + + + diff --git a/CocoaSplit.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/CocoaSplit.xcscheme b/CocoaSplit.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/CocoaSplit.xcscheme new file mode 100644 index 00000000..dde10cfb --- /dev/null +++ b/CocoaSplit.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/CocoaSplit.xcscheme @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CocoaSplit.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/CocoaSplitCmd.xcscheme b/CocoaSplit.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/CocoaSplitCmd.xcscheme new file mode 100644 index 00000000..cda2e326 --- /dev/null +++ b/CocoaSplit.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/CocoaSplitCmd.xcscheme @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CocoaSplit.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/QTCaptureHelper.xcscheme b/CocoaSplit.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/QTCaptureHelper.xcscheme new file mode 100644 index 00000000..d764b814 --- /dev/null +++ b/CocoaSplit.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/QTCaptureHelper.xcscheme @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CocoaSplit.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/xcschememanagement.plist b/CocoaSplit.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 00000000..abb092ed --- /dev/null +++ b/CocoaSplit.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,42 @@ + + + + + SchemeUserState + + CocoaSplit.xcscheme + + orderHint + 14 + + CocoaSplitCmd.xcscheme + + orderHint + 16 + + QTCaptureHelper.xcscheme + + orderHint + 15 + + + SuppressBuildableAutocreation + + 340FE49C15F3417E00E4CE4E + + primary + + + 3451A1C217111BD900DF6A8B + + primary + + + 34AE3C2D164E3E020052C95E + + primary + + + + + diff --git a/CocoaSplit/.DS_Store b/CocoaSplit/.DS_Store new file mode 100644 index 00000000..254532ad Binary files /dev/null and b/CocoaSplit/.DS_Store differ diff --git a/CocoaSplit/AVFAudioCapture.h b/CocoaSplit/AVFAudioCapture.h index 6d07ee6a..adc2536a 100644 --- a/CocoaSplit/AVFAudioCapture.h +++ b/CocoaSplit/AVFAudioCapture.h @@ -14,7 +14,6 @@ @class CAMultiAudioPCMPlayer; -@class CaptureController; @interface AVFAudioCapture : CSCaptureBase { @@ -29,7 +28,6 @@ @property (strong) AVFChannelManager *audioChannelManager; -@property (strong) CaptureController *audioDelegate; @property (assign) int audioBitrate; @property (assign) float audioSamplerate; @property (strong) AVCaptureDevice *activeAudioDevice; diff --git a/CocoaSplit/AVFAudioCapture.m b/CocoaSplit/AVFAudioCapture.m index e2ff5703..3b9370ec 100644 --- a/CocoaSplit/AVFAudioCapture.m +++ b/CocoaSplit/AVFAudioCapture.m @@ -8,7 +8,6 @@ #import "AVFAudioCapture.h" #import "CAMultiAudioPCMPlayer.h" -#import "CaptureController.h" @implementation AVFAudioCapture @@ -281,21 +280,14 @@ if (connection.output == _audio_capture_output) { - if (!self.useAudioEngine && self.audioDelegate) + + if (self.multiInput) { - [_audioDelegate captureOutputAudio:self didOutputSampleBuffer:sampleBuffer]; - } else { - - if (self.multiInput) - { - - [self.multiInput scheduleBuffer:sampleBuffer]; - - } - + [self.multiInput scheduleBuffer:sampleBuffer]; } + } } diff --git a/CocoaSplit/AnimationSamples/rotate.py b/CocoaSplit/AnimationSamples/rotate.py index c9e072b1..f1ba123d 100644 --- a/CocoaSplit/AnimationSamples/rotate.py +++ b/CocoaSplit/AnimationSamples/rotate.py @@ -1,3 +1,5 @@ +from Foundation import NSLog + animation_name = "RotateDemo" animation_description = "Rotate demo" @@ -7,6 +9,7 @@ animation_params = ["degrees"] def do_animation(inputs, duration): + NSLog("RUNNING ANIMATION") source1 = inputs['source1'] source2 = inputs['source2'] diff --git a/CocoaSplit/AppDelegate.h b/CocoaSplit/AppDelegate.h index da1aa8ec..e7fbe5c0 100644 --- a/CocoaSplit/AppDelegate.h +++ b/CocoaSplit/AppDelegate.h @@ -7,7 +7,6 @@ // #import -#import #import "CaptureController.h" diff --git a/CocoaSplit/CAMultiAudio/CAMultiAudioAVCapturePlayer.h b/CocoaSplit/CAMultiAudio/CAMultiAudioAVCapturePlayer.h index e0e5920e..68a4c575 100644 --- a/CocoaSplit/CAMultiAudio/CAMultiAudioAVCapturePlayer.h +++ b/CocoaSplit/CAMultiAudio/CAMultiAudioAVCapturePlayer.h @@ -20,7 +20,6 @@ -(instancetype)initWithDevice:(AVCaptureDevice *)avDevice withFormat:(AudioStreamBasicDescription *)withFormat; -(void)resetFormat:(AudioStreamBasicDescription *)format; --(const AudioStreamBasicDescription *)deviceAudioDescription; @end diff --git a/CocoaSplit/CAMultiAudio/CAMultiAudioDownmixer.m b/CocoaSplit/CAMultiAudio/CAMultiAudioDownmixer.m index 872d09d2..275a758d 100644 --- a/CocoaSplit/CAMultiAudio/CAMultiAudioDownmixer.m +++ b/CocoaSplit/CAMultiAudio/CAMultiAudioDownmixer.m @@ -56,7 +56,8 @@ { UInt32 enableVal = 1; OSStatus err; - err = AudioUnitSetProperty(self.audioUnit, kAudioUnitProperty_MeteringMode, kAudioUnitScope_Input, bus, &enableVal, sizeof(enableVal)); + //err = AudioUnitSetProperty(self.audioUnit, kAudioUnitProperty_MeteringMode, kAudioUnitScope_Input, bus, &enableVal, sizeof(enableVal)); + err = AudioUnitSetProperty(self.audioUnit, kAudioUnitProperty_MeteringMode, kAudioUnitScope_Output, 0, &enableVal, sizeof(enableVal)); if (err) { @@ -71,7 +72,9 @@ Float32 result = 0; OSStatus err; - err = AudioUnitGetParameter(self.audioUnit, kStereoMixerParam_PostAveragePower, kAudioUnitScope_Input, bus, &result); + + //err = AudioUnitGetParameter(self.audioUnit, kStereoMixerParam_PostAveragePower, kAudioUnitScope_Input, bus, &result); + err = AudioUnitGetParameter(self.audioUnit, kStereoMixerParam_PostAveragePower, kAudioUnitScope_Output, 0, &result); if (err) @@ -83,6 +86,24 @@ } +-(Float32)outputPower +{ + Float32 result = 0; + OSStatus err; + + + err = AudioUnitGetParameter(self.audioUnit, kStereoMixerParam_PostAveragePower, kAudioUnitScope_Output, 0, &result); + + + if (err) + { + NSLog(@"GET POWER ERROR %d", err); + } + + return result; + +} + -(void)setVolumeForScope:(AudioUnitScope)scope onBus:(AudioUnitElement)onBus volume:(float)volume diff --git a/CocoaSplit/CAMultiAudio/CAMultiAudioEngine.h b/CocoaSplit/CAMultiAudio/CAMultiAudioEngine.h index 4723328d..640fcafd 100644 --- a/CocoaSplit/CAMultiAudio/CAMultiAudioEngine.h +++ b/CocoaSplit/CAMultiAudio/CAMultiAudioEngine.h @@ -42,7 +42,8 @@ @property (strong) CAMultiAudioDevice *outputNode; @property (strong) CAMultiAudioDevice *graphOutputNode; @property (strong) NSArray *validSamplerates; - +@property (assign) Float32 streamAudioPowerLevel; +@property (assign) Float32 previewAudioPowerLevel; diff --git a/CocoaSplit/CAMultiAudio/CAMultiAudioEngine.m b/CocoaSplit/CAMultiAudio/CAMultiAudioEngine.m index 832bcab4..bd34de87 100644 --- a/CocoaSplit/CAMultiAudio/CAMultiAudioEngine.m +++ b/CocoaSplit/CAMultiAudio/CAMultiAudioEngine.m @@ -150,11 +150,27 @@ OSStatus encoderRenderCallback( void *inRefCon, AudioUnitRenderActionFlags *ioAc -(void)updateStatistics { - /* - for(CAMultiAudioNode *node in self.audioInputs) - { - [node updatePowerlevel]; - }*/ + dispatch_async(dispatch_get_main_queue(), ^{ + + for(CAMultiAudioNode *node in self.audioInputs) + { + [node updatePowerlevel]; + } + }); + + + float rawPreview = [self.previewMixer outputPower]; + float rawStream = [self.encodeMixer outputPower]; + + dispatch_async(dispatch_get_main_queue(), ^{ + self.previewAudioPowerLevel = pow(10.0f, rawPreview/20.0f); + self.streamAudioPowerLevel = pow(10.0f, rawStream/20.0f); + }); + + + + + } @@ -440,6 +456,7 @@ OSStatus encoderRenderCallback( void *inRefCon, AudioUnitRenderActionFlags *ioAc [self.graph addNode:input]; [self attachInput:newConverter]; + [self.graph connectNode:input toNode:newConverter sampleRate:input.inputFormat->mSampleRate]; } @@ -570,6 +587,8 @@ OSStatus encoderRenderCallback( void *inRefCon, AudioUnitRenderActionFlags *ioAc if (selfPtr.encoder) { + + [selfPtr.encoder enqueuePCM:ioData atTime:inTimeStamp]; } diff --git a/CocoaSplit/CAMultiAudio/CAMultiAudioMixer.m b/CocoaSplit/CAMultiAudio/CAMultiAudioMixer.m index 3680d8c1..7e7bcb99 100644 --- a/CocoaSplit/CAMultiAudio/CAMultiAudioMixer.m +++ b/CocoaSplit/CAMultiAudio/CAMultiAudioMixer.m @@ -69,6 +69,25 @@ } +-(Float32)outputPower +{ + Float32 result = 0; + OSStatus err; + + + err = AudioUnitGetParameter(self.audioUnit, kStereoMixerParam_PostAveragePower, kAudioUnitScope_Output, 0, &result); + + + if (err) + { + NSLog(@"GET POWER ERROR %d", err); + } + + return result; + +} + + -(Float32)powerForInputBus:(UInt32)bus { Float32 result = 0; diff --git a/CocoaSplit/CAMultiAudio/CAMultiAudioNode.m b/CocoaSplit/CAMultiAudio/CAMultiAudioNode.m index c99afe32..59aee715 100644 --- a/CocoaSplit/CAMultiAudio/CAMultiAudioNode.m +++ b/CocoaSplit/CAMultiAudio/CAMultiAudioNode.m @@ -130,15 +130,18 @@ return; } + + -(void)updatePowerlevel { - [self.connectedTo updatePowerlevel]; if ([self.connectedTo.class conformsToProtocol:@protocol(CAMultiAudioMixingProtocol)]) { idmixerNode = (id)self.connectedTo; - self.powerLevel = [mixerNode powerForInputBus:self.connectedToBus]; + float rawPower = [mixerNode powerForInputBus:self.connectedToBus]; + + self.powerLevel = pow(10.0f, rawPower/20.0f); } } @@ -164,8 +167,11 @@ { self.mixerWindow = [[CAMultiAudioMatrixMixerWindowController alloc] initWithAudioMixer:self.downMixer]; [self.mixerWindow showWindow:nil]; + self.mixerWindow.window.title = self.name; } } + + -(void)nodeConnected:(CAMultiAudioNode *)toNode onBus:(UInt32)onBus { self.connectedTo = toNode; @@ -189,9 +195,12 @@ return; } + //if we're muting, save the current player volume if (muted == YES) { + NSLog(@"NODE MUTE %d", muted); + _saved_volume = self.volume; self.volume = 0.0f; } else { diff --git a/CocoaSplit/CAMultiAudio/CAMultiAudioPCM.m b/CocoaSplit/CAMultiAudio/CAMultiAudioPCM.m index 1ced36d9..934cc3df 100644 --- a/CocoaSplit/CAMultiAudio/CAMultiAudioPCM.m +++ b/CocoaSplit/CAMultiAudio/CAMultiAudioPCM.m @@ -35,6 +35,7 @@ self.frameCount = _audioSlice->mNumberFrames; self.bufferCount = streamFormat->mChannelsPerFrame; + _alloced_buffers = NO; } @@ -42,6 +43,7 @@ } + -(void)copyFromAudioBufferList:(AudioBufferList *)copyFrom { //Just copy the data, we already allocated the List. @@ -91,6 +93,7 @@ } _audioSlice->mBufferList = _pcmData; memcpy(&_pcmFormat, streamFormat, sizeof(AudioStreamBasicDescription)); + _alloced_buffers = YES; } @@ -101,13 +104,14 @@ -(void)dealloc { - //You better not have freed this before - for (int i=0; i < self.bufferCount; i++) + if (_alloced_buffers || self.handleFreeBuffer) { - free(_audioSlice->mBufferList->mBuffers[i].mData); + for (int i=0; i < self.bufferCount; i++) + { + free(_audioSlice->mBufferList->mBuffers[i].mData); + } + free(_audioSlice->mBufferList); } - - free(_audioSlice->mBufferList); free(_audioSlice); } diff --git a/CocoaSplit/CAMultiAudio/CAMultiAudioPCMPlayer.h b/CocoaSplit/CAMultiAudio/CAMultiAudioPCMPlayer.h index adc82968..42dda383 100644 --- a/CocoaSplit/CAMultiAudio/CAMultiAudioPCMPlayer.h +++ b/CocoaSplit/CAMultiAudio/CAMultiAudioPCMPlayer.h @@ -18,12 +18,17 @@ bool _playing; int _bufcnt; + } @property (strong) NSString *inputUID; @property (weak) id converterNode; @property (assign) Float64 latestScheduledTime; @property (assign) AudioStreamBasicDescription *inputFormat; +@property (readonly) NSUInteger pendingFrames; +@property (nonatomic, copy) void (^completedBlock)(CAMultiAudioPCM *pcmBuffer); +@property (strong) NSMutableArray *pauseBuffer; +@property (assign) bool save_buffer; -(void)releasePCM:(CAMultiAudioPCM *)buffer; -(void)scheduleBuffer:(CMSampleBufferRef)sampleBuffer; @@ -32,6 +37,8 @@ -(void)play; +-(void)pause; +-(void)flush; diff --git a/CocoaSplit/CAMultiAudio/CAMultiAudioPCMPlayer.m b/CocoaSplit/CAMultiAudio/CAMultiAudioPCMPlayer.m index fc4bc506..ab21d3d2 100644 --- a/CocoaSplit/CAMultiAudio/CAMultiAudioPCMPlayer.m +++ b/CocoaSplit/CAMultiAudio/CAMultiAudioPCMPlayer.m @@ -27,6 +27,7 @@ void BufferCompletedPlaying(void *userData, ScheduledAudioSlice *bufferList); _bufcnt = 0; _inputFormat = NULL; self.latestScheduledTime = 0; + _pauseBuffer = [[NSMutableArray alloc] init]; } return self; @@ -41,6 +42,10 @@ void BufferCompletedPlaying(void *userData, ScheduledAudioSlice *bufferList); [self playPcmBuffer:pcmBuffer]; } +-(NSUInteger)pendingFrames +{ + return _pendingBuffers.count; +} -(bool)playPcmBuffer:(CAMultiAudioPCM *)pcmBuffer { @@ -102,9 +107,10 @@ void BufferCompletedPlaying(void *userData, ScheduledAudioSlice *bufferList); + dispatch_async(_pendingQueue, ^{ - [_pendingBuffers addObject:pcmBuffer]; + [self->_pendingBuffers addObject:pcmBuffer]; }); @@ -115,6 +121,21 @@ void BufferCompletedPlaying(void *userData, ScheduledAudioSlice *bufferList); return YES; } + + + +-(void)setVolume:(float)volume +{ + super.volume = volume; + + + if (self.converterNode) + { + + [(CAMultiAudioNode *)self.converterNode setVolumeOnConnectedNode]; + } +} + -(void)scheduleBuffer:(CMSampleBufferRef)sampleBuffer { @@ -155,8 +176,11 @@ void BufferCompletedPlaying(void *userData, ScheduledAudioSlice *bufferList); CMSampleBufferCopyPCMDataIntoAudioBufferList(sampleBuffer, 0, (int32_t)numSamples, sampleABL); CAMultiAudioPCM *pcmBuffer = [[CAMultiAudioPCM alloc] initWithAudioBufferList:sampleABL streamFormat:asbd]; + pcmBuffer.handleFreeBuffer = YES; + + [self playPcmBuffer:pcmBuffer]; } @@ -217,6 +241,22 @@ void BufferCompletedPlaying(void *userData, ScheduledAudioSlice *bufferList); } } +-(void)pause +{ + + self.save_buffer = YES; + [self flush]; +} + + +-(void)flush +{ + if (self.audioUnit) + { + AudioUnitReset(self.audioUnit, kAudioUnitScope_Global, 0); + } +} + -(void)play { @@ -225,12 +265,21 @@ void BufferCompletedPlaying(void *userData, ScheduledAudioSlice *bufferList); OSStatus err; + ts.mFlags = kAudioTimeStampSampleTimeValid; ts.mSampleTime = -1; err = AudioUnitSetProperty(self.audioUnit, kAudioUnitProperty_ScheduleStartTimeStamp, kAudioUnitScope_Global, 0, &ts, sizeof(ts)); + _save_buffer = NO; + for (CAMultiAudioPCM *buffer in self.pauseBuffer) + { + [self playPcmBuffer:buffer]; + } + + [self.pauseBuffer removeAllObjects]; } + -(void)dealloc { if (_inputFormat) @@ -248,11 +297,23 @@ void BufferCompletedPlaying(void *userData, ScheduledAudioSlice *bufferList) CAMultiAudioPCM *pcmObj = (__bridge CAMultiAudioPCM *)(userData); //maybe put this on a dedicated queue? //why a queue? don't want to do any sort of memory/managed object operations in an audio callback. - dispatch_async(dispatch_get_main_queue(), ^{ + //dispatch_async(dispatch_get_main_queue(), ^{ CAMultiAudioPCMPlayer *pplayer = pcmObj.player; //pplayer.latestScheduledTime = pcmObj.audioSlice->mTimeStamp.mSampleTime + pcmObj.audioSlice->mNumberFrames; + if (pplayer.completedBlock) + { + pplayer.completedBlock(pcmObj); + } + + if (pplayer.save_buffer) + { + [pplayer.pauseBuffer addObject:pcmObj]; + } else { [pplayer releasePCM:pcmObj]; - }); + } + + + //}); } \ No newline at end of file diff --git a/CocoaSplit/CAMultiAudioMatrixView.xib b/CocoaSplit/CAMultiAudioMatrixView.xib index 34ce3684..886219d1 100644 --- a/CocoaSplit/CAMultiAudioMatrixView.xib +++ b/CocoaSplit/CAMultiAudioMatrixView.xib @@ -1,15 +1,15 @@ - + - + - + diff --git a/CocoaSplit/CAMultiAudioMixingProtocol.h b/CocoaSplit/CAMultiAudioMixingProtocol.h index 66c03ddf..1836c41c 100644 --- a/CocoaSplit/CAMultiAudioMixingProtocol.h +++ b/CocoaSplit/CAMultiAudioMixingProtocol.h @@ -21,6 +21,8 @@ -(void)setVolumeOnOutput:(float)volume; -(void)enableMeteringOnInputBus:(UInt32)bus; -(Float32)powerForInputBus:(UInt32)bus; +-(Float32)outputPower; + @end diff --git a/CocoaSplit/CSAacEncoder.h b/CocoaSplit/CSAacEncoder.h index 7860aff9..bd72a257 100644 --- a/CocoaSplit/CSAacEncoder.h +++ b/CocoaSplit/CSAacEncoder.h @@ -9,6 +9,9 @@ #import #import #import +#import "TPCircularBuffer.h" +#import "TPCircularBuffer+AudioBufferList.h" + @class CaptureController; @@ -24,6 +27,14 @@ long outputSampleCount; CMAudioFormatDescriptionRef cmFormat; void *_pcmData; + u_int64_t _last_sample_time; + int _last_write_sample_cnt; + TPCircularBuffer _inputBuffer; + TPCircularBuffer _scratchBuffer; + dispatch_source_t _dispatch_timer; + dispatch_semaphore_t _aSemaphore; + + } @property (assign) bool encoderStarted; @@ -31,8 +42,11 @@ @property (assign) int sampleRate; @property (assign) int bitRate; @property (assign) int preallocatedBuffersize; +@property (assign) AudioStreamBasicDescription *inputASBD; -(void) enqueuePCM:(AudioBufferList *)pcmBuffer atTime:(const AudioTimeStamp *)atTime; +-(void) setupEncoderBuffer; + -(void) stopEncoder; diff --git a/CocoaSplit/CSAacEncoder.m b/CocoaSplit/CSAacEncoder.m index 72a058f4..9a36c13d 100644 --- a/CocoaSplit/CSAacEncoder.m +++ b/CocoaSplit/CSAacEncoder.m @@ -19,38 +19,33 @@ if (self = [super init]) { encoderQueue = dispatch_queue_create("CSAACEncoderQueue", NULL); - _pcmData = NULL; + _aSemaphore = dispatch_semaphore_create(0); + + } return self; } --(void)preallocateBufferList:(AudioBufferList *)bufferList +-(void)setupEncoderBuffer { - //To avoid doing mallocs every cycle, the AU callback asks us to preallocate memory based on the size of the buffers it receives - //If the size changes, we only re-allocate if it is bigger than our preallocated size. If it's smaller we - //just "waste" the memory and leave it be. - - int bufferSize = bufferList->mBuffers[0].mDataByteSize; - - if (bufferSize > self.preallocatedBuffersize) - { - _pcmData = malloc(bufferSize*2); //Assuming deinterleaved 2-ch, so allocate enough space for both channels - self.preallocatedBuffersize = bufferSize; - } - -} + TPCircularBufferInit(&_inputBuffer, self.inputASBD->mBytesPerFrame * 4096); + TPCircularBufferInit(&_scratchBuffer, self.inputASBD->mBytesPerFrame * 4096); + dispatch_async(encoderQueue, ^{[self encodeAudio];}); +} -(void) enqueuePCM:(AudioBufferList *)pcmBuffer atTime:(const AudioTimeStamp *)atTime { - - - - [self preallocateBufferList:pcmBuffer]; - + TPCircularBufferCopyAudioBufferList(&_inputBuffer, pcmBuffer, atTime, kTPCircularBufferCopyAll, NULL); + dispatch_semaphore_signal(_aSemaphore); +} + +-(void)encodeAudio +{ + if (!self.encoderStarted) { @@ -58,154 +53,136 @@ self.encoderStarted = YES; } - - - - //for now assume Float32, 2 channel, non-interleaved. We have to interleave it outselves here. - - AudioBuffer buffer0 = pcmBuffer->mBuffers[0]; - AudioBuffer buffer1 = pcmBuffer->mBuffers[1]; - Float32 *data0 = buffer0.mData; - Float32 *data1 = buffer1.mData; - - Float32 *writebuf = _pcmData; - int channel_size = buffer0.mDataByteSize/sizeof(Float32); - int i, u; - for(i=u=0; i < channel_size; i++,u+=2) + while (1) { - writebuf[u] = data0[i]; - writebuf[u+1] = data1[i]; - } - - - - //Do the actual compression on another thread so as not to block AudioUnit callbacks - - - - - - - - - dispatch_async(encoderQueue, ^{ - - - - - UInt32 wrote_bytes = 0; - UInt32 num_packets = 1; - UInt32 outstatus = 0; - Float32 *readbuf = _pcmData; - - + dispatch_semaphore_wait(_aSemaphore, DISPATCH_TIME_FOREVER); - - - UInt32 bufsize = self.preallocatedBuffersize*2; //This should be equal to 2x pcmBuffer->mBuffers[0].mDataByteSize - - UInt32 orig_size = bufsize; - - - - UInt32 buffer_size = maxOutputSize; - - while (true) + while (TPCircularBufferPeek(&_inputBuffer, NULL, self.inputASBD) >= 1024) { + AudioBufferList *inBuffer = TPCircularBufferPrepareEmptyAudioBufferListWithAudioFormat(&_scratchBuffer, self.inputASBD, 1024, NULL); + UInt32 inFrameCnt = 1024; + AudioTimeStamp atTime; - void *aacBuffer = malloc(maxOutputSize); - - OSStatus err; + TPCircularBufferDequeueBufferListFrames(&_inputBuffer, &inFrameCnt, inBuffer, &atTime, self.inputASBD); - err = AudioCodecAppendInputData(aacCodec, readbuf, &bufsize, NULL, NULL); - - - wrote_bytes += bufsize; - - readbuf += bufsize/sizeof(Float32); - //reset bufsize for next loop - bufsize = orig_size - wrote_bytes; - - AudioStreamPacketDescription packetDesc; - - - - err = AudioCodecProduceOutputPackets(aacCodec, aacBuffer, &buffer_size, &num_packets, &packetDesc, &outstatus); - - if (err != 0) + Float32 *writebuf = malloc(inBuffer->mBuffers[0].mDataByteSize*2); + AudioBuffer buffer0 = inBuffer->mBuffers[0]; + AudioBuffer buffer1 = inBuffer->mBuffers[1]; + Float32 *data0 = buffer0.mData; + Float32 *data1 = buffer1.mData; + int channel_size = buffer0.mDataByteSize/sizeof(Float32); + int i, u; + for(i=u=0; i < channel_size; i++,u+=2) { - NSLog(@"CODEC PRODUCE OUTPUT ERROR IS %@", [[NSError errorWithDomain:NSOSStatusErrorDomain code:err userInfo:nil] description]); - + writebuf[u] = data0[i]; + writebuf[u+1] = data1[i]; } - - if (outstatus == kAudioCodecProduceOutputPacketNeedsMoreInputData) - { - free(aacBuffer); - break; + @autoreleasepool { + + UInt32 num_packets = 1; + UInt32 outstatus = 0; + + + + + + + UInt32 bufsize = inBuffer->mBuffers[0].mDataByteSize*2;//This should be equal to 2x pcmBuffer->mBuffers[0].mDataByteSize + + + + + + UInt32 buffer_size = maxOutputSize; + + void *aacBuffer = malloc(maxOutputSize); + + + OSStatus err; + + err = AudioCodecAppendInputData(aacCodec, writebuf, &bufsize, NULL, NULL); + + free(writebuf); + AudioStreamPacketDescription packetDesc; + + + + err = AudioCodecProduceOutputPackets(aacCodec, aacBuffer, &buffer_size, &num_packets, &packetDesc, &outstatus); + + if (err != 0) + { + NSLog(@"CODEC PRODUCE OUTPUT ERROR IS %@", [[NSError errorWithDomain:NSOSStatusErrorDomain code:err userInfo:nil] description]); + + } + + + if (outstatus == kAudioCodecProduceOutputPacketNeedsMoreInputData) + { + NSLog(@"NEED MORE INPUT DATA"); + free(aacBuffer); + break; + } + + + if (self.encodedReceiver && buffer_size) + { + + + CMTime duration = CMTimeMake(1024, self.sampleRate); + uint64_t mach_now = atTime.mHostTime; + + double abs_pts = (double)mach_now/NSEC_PER_SEC; + + CMTime ptsTime = CMTimeMake(abs_pts*1000, 1000); + + CMSampleTimingInfo timeInfo; + + timeInfo.duration = duration; + timeInfo.presentationTimeStamp = ptsTime; + timeInfo.decodeTimeStamp = kCMTimeInvalid; + + CMSampleBufferRef newSampleBuf; + CMSampleBufferRef timingSampleBuf; + CMBlockBufferRef bufferRef; + + + CMBlockBufferCreateWithMemoryBlock(NULL, aacBuffer, buffer_size, kCFAllocatorMalloc, NULL, 0, buffer_size, 0, &bufferRef); + + + CMAudioSampleBufferCreateWithPacketDescriptions(kCFAllocatorDefault, bufferRef, YES, NULL, NULL, cmFormat, 1, ptsTime, &packetDesc, &newSampleBuf); + + + CMSampleBufferCreateCopyWithNewTiming(kCFAllocatorDefault, newSampleBuf, 1, &timeInfo, &timingSampleBuf); + + CFRelease(newSampleBuf); + //The sample buffer retains the block buffer when it is handed over to it, we can release ours. + CFRelease(bufferRef); + + + [self.encodedReceiver captureOutputAudio:nil didOutputSampleBuffer:timingSampleBuf]; + + //Individual video compressors retain the buffer until they push it to their output, we can release it now. + CFRelease(timingSampleBuf); + + } else { + free(aacBuffer); + } + + + + buffer_size = maxOutputSize; + num_packets = 1; + + outputSampleCount += 1024; } - - - if (self.encodedReceiver && buffer_size) - { - CMTime ptsTime = CMTimeMake(outputSampleCount, self.sampleRate); - CMTime duration = CMTimeMake(1024, self.sampleRate); - - CMSampleTimingInfo timeInfo; - - timeInfo.duration = duration; - timeInfo.presentationTimeStamp = ptsTime; - timeInfo.decodeTimeStamp = kCMTimeInvalid; - - CMSampleBufferRef newSampleBuf; - CMSampleBufferRef timingSampleBuf; - CMBlockBufferRef bufferRef; - - - CMBlockBufferCreateWithMemoryBlock(NULL, aacBuffer, buffer_size, kCFAllocatorMalloc, NULL, 0, buffer_size, 0, &bufferRef); - - - CMAudioSampleBufferCreateWithPacketDescriptions(kCFAllocatorDefault, bufferRef, YES, NULL, NULL, cmFormat, 1, ptsTime, &packetDesc, &newSampleBuf); - - - //CMAudioSampleBufferCreateReadyWithPacketDescriptions(kCFAllocatorDefault, bufferRef, cmFormat, 1, ptsTime, &packetDesc, &newSampleBuf); - - CMSampleBufferCreateCopyWithNewTiming(kCFAllocatorDefault, newSampleBuf, 1, &timeInfo, &timingSampleBuf); - CFRelease(newSampleBuf); - //The sample buffer retains the block buffer when it is handed over to it, we can release ours. - CFRelease(bufferRef); - - - [self.encodedReceiver captureOutputAudio:nil didOutputSampleBuffer:timingSampleBuf]; - //Individual video compressors retain the buffer until they push it to their output, we can release it now. - CFRelease(timingSampleBuf); - - } else { - free(aacBuffer); - } - - - - buffer_size = maxOutputSize; - num_packets = 1; - - outputSampleCount += 1024; - if (wrote_bytes >= orig_size) - { - break; - } - } - - - }); - + } } - -(void) setupEncoder { //create the input format. diff --git a/CocoaSplit/CSAnimationChooserViewController.h b/CocoaSplit/CSAnimationChooserViewController.h index 38980882..09ab86e6 100644 --- a/CocoaSplit/CSAnimationChooserViewController.h +++ b/CocoaSplit/CSAnimationChooserViewController.h @@ -8,12 +8,10 @@ #import #import "SourceLayout.h" -#import "CaptureController.h" @interface CSAnimationChooserViewController : NSViewController -@property (weak) CaptureController *controller; @property (weak) NSPopover *popover; @property (strong) SourceLayout *sourceLayout; diff --git a/CocoaSplit/CSAnimationChooserViewController.m b/CocoaSplit/CSAnimationChooserViewController.m index 98ff3858..32b3e517 100644 --- a/CocoaSplit/CSAnimationChooserViewController.m +++ b/CocoaSplit/CSAnimationChooserViewController.m @@ -7,6 +7,8 @@ // #import "CSAnimationChooserViewController.h" +#import "CaptureController.h" + @interface CSAnimationChooserViewController () diff --git a/CocoaSplit/CSAnimationChooserViewController.xib b/CocoaSplit/CSAnimationChooserViewController.xib index caa876e2..e0a15845 100644 --- a/CocoaSplit/CSAnimationChooserViewController.xib +++ b/CocoaSplit/CSAnimationChooserViewController.xib @@ -1,8 +1,8 @@ - + - + @@ -28,15 +28,15 @@ - - + + - + - + @@ -65,11 +65,11 @@ diff --git a/CocoaSplit/CSAnimationItem.h b/CocoaSplit/CSAnimationItem.h index 3a020b14..bf4ffef3 100644 --- a/CocoaSplit/CSAnimationItem.h +++ b/CocoaSplit/CSAnimationItem.h @@ -16,6 +16,10 @@ @property (strong) NSString *module_name; @property (strong) NSString *name; @property (readonly) bool onLive; +@property (strong) NSString *uuid; +@property (assign) NSInteger refCount; + + //label -> 'whatever' //input -> InputSource diff --git a/CocoaSplit/CSAnimationItem.m b/CocoaSplit/CSAnimationItem.m index e3e358ba..57ea826e 100644 --- a/CocoaSplit/CSAnimationItem.m +++ b/CocoaSplit/CSAnimationItem.m @@ -19,7 +19,8 @@ { [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sourceWasDeleted:) name:CSNotificationInputDeleted object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sourceWasAdded:) name:CSNotificationInputAdded object:nil]; - + [self createUUID]; + self.refCount = 0; } return self; @@ -38,7 +39,24 @@ [aCoder encodeObject:self.name forKey:@"name"]; [aCoder encodeObject:self.module_name forKey:@"module_name"]; + for (NSMutableDictionary *item in self.inputs) + { + if ([item[@"type"] isEqualToString:@"input"]) + { + if (item[@"value"] && ![item[@"value"] isEqualTo:[NSNull null]]) + { + InputSource *inp = item[@"value"]; + item[@"savedUUID"] = inp.uuid; + //item[@"value"] = [NSNull null]; + } + } + } [aCoder encodeObject:self.inputs forKey:@"inputs"]; + + if (self.uuid) + { + [aCoder encodeObject:self.uuid forKey:@"uuid"]; + } } -(instancetype)initWithCoder:(NSCoder *)aDecoder @@ -48,12 +66,24 @@ self.name = [aDecoder decodeObjectForKey:@"name"]; self.module_name = [aDecoder decodeObjectForKey:@"module_name"]; self.inputs = [aDecoder decodeObjectForKey:@"inputs"]; + if ([aDecoder containsValueForKey:@"uuid"]) + { + self.uuid = [aDecoder decodeObjectForKey:@"uuid"]; + } } return self; } +-(void)createUUID +{ + CFUUIDRef tmpUUID = CFUUIDCreate(NULL); + self.uuid = (__bridge_transfer NSString *)CFUUIDCreateString(NULL, tmpUUID); + CFRelease(tmpUUID); + +} + -(instancetype)copyWithZone:(NSZone *)zone { CSAnimationItem *newItem = [[CSAnimationItem allocWithZone:zone] init]; @@ -97,7 +127,7 @@ if ([inputType isEqualToString:@"input"]) { InputSource *inpsrc = inp[@"value"]; - if (inpsrc == srcDel) + if (inpsrc == srcDel && inpsrc.sourceLayout == srcDel.sourceLayout) { inp[@"deletedUUID"] = srcDel.uuid; inp[@"value"] = [NSNull null]; diff --git a/CocoaSplit/CSAnimationRunner/CSAnimationRunner.py b/CocoaSplit/CSAnimationRunner/CSAnimationRunner.py index 1a83c47f..7f58a69b 100644 --- a/CocoaSplit/CSAnimationRunner/CSAnimationRunner.py +++ b/CocoaSplit/CSAnimationRunner/CSAnimationRunner.py @@ -19,7 +19,7 @@ plugin_base = PluginBase(package='animationplugins') library_dirs = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSAllDomainsMask - NSSystemDomainMask, YES) plugin_dirs = map(lambda x: x + "/Application Support/CocoaSplit/Plugins/Animations", library_dirs) -plugin_dirs.append(NSBundle.mainBundle().builtInPlugInsPath() + "/Animations") +plugin_dirs.append(NSBundle.mainBundle().resourcePath() + "/Animations") plugin_source = plugin_base.make_plugin_source(searchpath=plugin_dirs) diff --git a/CocoaSplit/CSCaptureBase+TimerDelegate.m b/CocoaSplit/CSCaptureBase+TimerDelegate.m index 5a09a146..3cb60480 100644 --- a/CocoaSplit/CSCaptureBase+TimerDelegate.m +++ b/CocoaSplit/CSCaptureBase+TimerDelegate.m @@ -9,5 +9,7 @@ #import "CSCaptureBase+TimerDelegate.h" @implementation CSCaptureBase (TimerDelegate) +@dynamic timerDelegateCtx; +@dynamic timerDelegate; @end diff --git a/CocoaSplit/CSCaptureBase.m b/CocoaSplit/CSCaptureBase.m index 05faa56a..d5a6ae4f 100644 --- a/CocoaSplit/CSCaptureBase.m +++ b/CocoaSplit/CSCaptureBase.m @@ -38,6 +38,11 @@ return NSStringFromClass(self); } +-(NSString *)label +{ + return [self.class label]; +} + -(instancetype) init { @@ -76,6 +81,13 @@ return self; } + +-(NSImage *)libraryImage +{ + return nil; +} + + -(NSString *) configurationViewClassName { return [NSString stringWithFormat:@"%@ViewController", self.className]; @@ -219,7 +231,9 @@ -(CALayer *)createNewLayer { - return [CALayer layer]; + CALayer *newLayer = [CALayer layer]; + return newLayer; + } diff --git a/CocoaSplit/CSInputLayer.m b/CocoaSplit/CSInputLayer.m index 8a2f6076..0d113ac3 100644 --- a/CocoaSplit/CSInputLayer.m +++ b/CocoaSplit/CSInputLayer.m @@ -281,7 +281,7 @@ _allowResize = YES; - _sourceLayer = [CALayer layer]; + self.sourceLayer = [CALayer layer]; _sourceLayer.anchorPoint = CGPointMake(0.0, 0.0); _sourceLayer.contentsGravity = kCAGravityResizeAspect; _sourceLayer.frame = CGRectMake(0, 0, 1, 1); @@ -466,11 +466,11 @@ } - +/* -(void)setHidden:(BOOL)hidden { _yLayer.hidden = hidden; -} +}*/ -(void)transitionToLayer:(CALayer *)toLayer fromLayer:(CALayer *)fromLayer withTransition:(CATransition *)transition @@ -509,9 +509,14 @@ -(void)setSourceLayer:(CALayer *)sourceLayer { [CATransaction begin]; - [self copySourceSettings:sourceLayer]; - [_sourceLayer.superlayer replaceSublayer:_sourceLayer with:sourceLayer]; + if (_sourceLayer) + { + [self copySourceSettings:sourceLayer]; + + + [_sourceLayer.superlayer replaceSublayer:_sourceLayer with:sourceLayer]; + } _sourceLayer = sourceLayer; diff --git a/CocoaSplit/CSInputLibraryItem.h b/CocoaSplit/CSInputLibraryItem.h new file mode 100644 index 00000000..4bcf04a4 --- /dev/null +++ b/CocoaSplit/CSInputLibraryItem.h @@ -0,0 +1,24 @@ +// +// CSInputLibraryItem.h +// CocoaSplit +// +// Created by Zakk on 10/18/15. +// Copyright © 2015 Zakk. All rights reserved. +// + +#import +#import "InputSource.h" + + +@interface CSInputLibraryItem : NSObject + +@property (strong) NSString *name; +@property (strong) NSData *inputData; +@property (strong) NSImage *inputImage; +@property (strong) InputSource *editInput; + +-(instancetype) initWithInput:(InputSource *)input; +-(void)makeDataFromInput:(InputSource *)input; +-(InputSource *)makeInput; + +@end diff --git a/CocoaSplit/CSInputLibraryItem.m b/CocoaSplit/CSInputLibraryItem.m new file mode 100644 index 00000000..573112a9 --- /dev/null +++ b/CocoaSplit/CSInputLibraryItem.m @@ -0,0 +1,138 @@ +// +// CSInputLibraryItem.m +// CocoaSplit +// +// Created by Zakk on 10/18/15. +// Copyright © 2015 Zakk. All rights reserved. +// + +#import "CSInputLibraryItem.h" + +@implementation CSInputLibraryItem + + +-(instancetype) initWithInput:(InputSource *)input +{ + if (self = [super init]) + { + NSString *inputType = @"None"; + NSString *inputName = @"No Name"; + + if (input.videoInput) + { + inputType = [input.videoInput.class label]; + NSImage *img = [input.videoInput libraryImage]; + if (img) + { + NSImage *thumb = [[NSImage alloc] initWithSize:NSMakeSize(32, 32)]; + NSRect fromRect = NSMakeRect(0, 0, img.size.width, img.size.height); + [thumb lockFocus]; + [img drawInRect:NSMakeRect(0, 0, 32, 32) fromRect:fromRect operation:NSCompositeCopy fraction:1.0f]; + [thumb unlockFocus]; + self.inputImage = thumb; + } + } + + if (input.name) + { + inputName = input.name; + } + + self.name = [NSString stringWithFormat:@"%@ %@", inputType, inputName]; + NSMutableData *saveData = [NSMutableData data]; + NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:saveData]; + [archiver encodeObject:input forKey:@"root"]; + [archiver finishEncoding]; + self.inputData = saveData; + if (!self.inputImage) + { + self.inputImage = [NSImage imageNamed:NSImageNameUser]; + } + } + + return self; +} + + +-(instancetype) init +{ + if (self = [super init]) + { + self.inputImage = [NSImage imageNamed:NSImageNameUser]; + } + + return self; +} + +-(void)encodeWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeObject:self.name forKey:@"name"]; + [aCoder encodeObject:self.inputImage forKey:@"inputImage"]; + [aCoder encodeObject:self.inputData forKey:@"inputData"]; +} + +-(instancetype)initWithCoder:(NSCoder *)aDecoder +{ + if (self = [super init]) + { + _name = [aDecoder decodeObjectForKey:@"name"]; + _inputData = [aDecoder decodeObjectForKey:@"inputData"]; + _inputImage = [aDecoder decodeObjectForKey:@"inputImage"]; + } + + return self; +} + + +-(InputSource *)makeInput +{ + if (!self.inputData) + { + return nil; + } + + NSData *iData = self.inputData; + + NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:iData]; + + + InputSource *iSrc = [unarchiver decodeObjectForKey:@"root"]; + [unarchiver finishDecoding]; + return iSrc; +} + +-(void)makeDataFromInput:(InputSource *)input +{ + NSMutableData *saveData = [NSMutableData data]; + NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:saveData]; + [archiver encodeObject:input forKey:@"root"]; + [archiver finishEncoding]; + self.inputData = saveData; +} +-(id)initWithPasteboardPropertyList:(id)propertyList ofType:(NSString *)type +{ + return [NSKeyedUnarchiver unarchiveObjectWithData:propertyList]; +} + ++(NSArray *)readableTypesForPasteboard:(NSPasteboard *)pasteboard +{ + return @[@"cocoasplit.library.item"]; +} + +- (NSArray *)writableTypesForPasteboard:(NSPasteboard *)pasteboard +{ + return @[@"cocoasplit.library.item"]; +} + +-(id)pasteboardPropertyListForType:(NSString *)type +{ + if (![type isEqualToString:@"cocoasplit.library.item"]) + { + return nil; + } + + return [NSKeyedArchiver archivedDataWithRootObject:self]; +} + + +@end diff --git a/CocoaSplit/CSLayoutButton.h b/CocoaSplit/CSLayoutButton.h new file mode 100644 index 00000000..44eac089 --- /dev/null +++ b/CocoaSplit/CSLayoutButton.h @@ -0,0 +1,23 @@ +// +// CSLayoutButton.h +// CocoaSplit +// +// Created by Zakk on 10/4/15. +// Copyright © 2015 Zakk. All rights reserved. +// + +#import + +@class CSLayoutCollectionItem; + + +@interface CSLayoutButton : NSButton +{ + NSEvent *_savedMouseDown; +} + + +@property (weak) IBOutlet CSLayoutCollectionItem *viewController; + + +@end diff --git a/CocoaSplit/CSLayoutButton.m b/CocoaSplit/CSLayoutButton.m new file mode 100644 index 00000000..f671639c --- /dev/null +++ b/CocoaSplit/CSLayoutButton.m @@ -0,0 +1,95 @@ +// +// CSLayoutButton.m +// CocoaSplit +// +// Created by Zakk on 10/4/15. +// Copyright © 2015 Zakk. All rights reserved. +// + +#import "CSLayoutButton.h" +#import "CSLayoutCollectionItem.h" + +@implementation CSLayoutButton + + + + + + +- (void)drawRect:(NSRect)dirtyRect { + [super drawRect:dirtyRect]; + NSColor *redColor = [NSColor colorWithRed:1.0 green:0.0 blue:0.0 alpha:0.3]; + NSColor *greenColor = [NSColor colorWithRed:0.0 green:1.0 blue:0.0 alpha:0.3]; + + NSRect mybounds = self.bounds; + + SourceLayout *myLayout = self.viewController.representedObject; + + if (myLayout.in_staging) + { + [greenColor set]; + + NSRect fillBox = mybounds; + + fillBox.size.width = fillBox.size.width/2; + + NSRectFillUsingOperation(fillBox, NSCompositeSourceOver); + } + + if (myLayout.in_live) + { + [redColor set]; + + NSRect fillBox = mybounds; + + fillBox.size.width = fillBox.size.width/2; + fillBox.origin.x = fillBox.origin.x + fillBox.size.width; + + NSRectFillUsingOperation(fillBox, NSCompositeSourceOver); + } + + + + // Drawing code here. +} + + +-(void)mouseDown:(NSEvent *)theEvent +{ + [self highlight:YES]; + //[self.nextResponder mouseDown:theEvent];' + _savedMouseDown = theEvent; + + return; +} + +-(void)mouseDragged:(NSEvent *)theEvent +{ + [self highlight:NO]; + [self.nextResponder mouseDown:_savedMouseDown]; + [self.nextResponder mouseDragged:theEvent]; +} + +-(void)mouseUp:(NSEvent *)theEvent +{ + [self performClick:self]; + [self.nextResponder mouseUp:theEvent]; +} + + + +-(void)rightMouseDown:(NSEvent *)theEvent +{ + + [self.viewController showLayoutMenu:theEvent]; +} + + +-(void)dealloc +{ + [self.viewController removeObserver:self forKeyPath:@"representedObject.in_live"]; + [self.viewController removeObserver:self forKeyPath:@"representedObject.in_staging"]; + + +} +@end diff --git a/CocoaSplit/CSMidiWrapper.h b/CocoaSplit/CSMidiWrapper.h index 789e98dd..61504a6f 100644 --- a/CocoaSplit/CSMidiWrapper.h +++ b/CocoaSplit/CSMidiWrapper.h @@ -17,6 +17,7 @@ @property (strong) MIKMIDIDevice *device; @property (strong) MIKMIDIMapping *deviceMapping; +@property (nonatomic, copy) id(^redirectResponderBlock)(MIKMIDICommand *command, MIKMIDIMappingItem *item); diff --git a/CocoaSplit/CSMidiWrapper.m b/CocoaSplit/CSMidiWrapper.m index 94c3f5d4..bb7b7fde 100644 --- a/CocoaSplit/CSMidiWrapper.m +++ b/CocoaSplit/CSMidiWrapper.m @@ -74,25 +74,40 @@ for (MIKMIDIMappingItem *item in items) { - id responder = [NSApp MIDIResponderWithIdentifier:item.MIDIResponderIdentifier]; - if ([responder respondsToMIDICommand:command]) + + id responder = nil; + + if (self.redirectResponderBlock) { + responder = self.redirectResponderBlock(command, item); + } + + if (!responder) + { + responder = [NSApp MIDIResponderWithIdentifier:item.MIDIResponderIdentifier]; + } - NSString *dynMethod = [NSString stringWithFormat:@"handleMIDICommand%@:", item.commandIdentifier]; - - SEL dynSelector = NSSelectorFromString(dynMethod); - - if ([responder respondsToSelector:dynSelector]) + if (responder) + { + if ([responder respondsToMIDICommand:command]) { - NSMethodSignature *dynsig = [[responder class] instanceMethodSignatureForSelector:dynSelector]; - NSInvocation *dyninvoke = [NSInvocation invocationWithMethodSignature:dynsig]; - dyninvoke.target = responder; - dyninvoke.selector = dynSelector; - [dyninvoke setArgument:&command atIndex:2]; - [dyninvoke retainArguments]; - [dyninvoke invoke]; - } else { - [responder handleMIDICommand:command forIdentifier:item.commandIdentifier]; + + NSString *dynMethod = [NSString stringWithFormat:@"handleMIDICommand%@:", item.commandIdentifier]; + + SEL dynSelector = NSSelectorFromString(dynMethod); + + if ([responder respondsToSelector:dynSelector]) + { + NSMethodSignature *dynsig = [[responder class] instanceMethodSignatureForSelector:dynSelector]; + NSInvocation *dyninvoke = [NSInvocation invocationWithMethodSignature:dynsig]; + dyninvoke.target = responder; + dyninvoke.selector = dynSelector; + [dyninvoke setArgument:&command atIndex:2]; + [dyninvoke retainArguments]; + [dyninvoke invoke]; + } else { + [responder handleMIDICommand:command forIdentifier:item.commandIdentifier]; + } } } } diff --git a/CocoaSplit/CSOauth2Authenticator.m b/CocoaSplit/CSOauth2Authenticator.m new file mode 100644 index 00000000..e08c3e80 --- /dev/null +++ b/CocoaSplit/CSOauth2Authenticator.m @@ -0,0 +1,613 @@ +// +// CSOauth2Authenticator.m +// CocoaSplit +// +// Created by Zakk on 7/17/16. +// Copyright © 2016 Zakk. All rights reserved. +// + +#import "CSOauth2Authenticator.h" + +NSString *const kCSOauth2ConfigRedirectURL = @"CSOauth2ConfigRedirectURL"; +NSString *const kCSOauth2ConfigScopes = @"CSOauth2ConfigScopes"; +NSString *const kCSOauth2ConfigAuthURL = @"CSOauth2ConfigAuthURL"; +NSString *const kCSOauth2CodeFlow = @"CSOauth2CodeFlow"; +NSString *const kCSOauth2ImplicitGrantFlow = @"CSOauth2ImplicitGrantFlow"; +NSString *const kCSOauth2ExtraAuthParams = @"CSOauth2ExtraAuthParams"; +NSString *const kCSOauth2AccessTokenRequestURL = @"CSOauth2AccessTokenRequestURL"; +NSString *const kCSOauth2AccessRefreshURL = @"CSOauth2AccessRefreshURL"; +NSString *const kCSOauth2ClientSecret = @"CSOauth2ClientSecret"; + + + + + + +@interface CSOauth2Authenticator() +{ + NSMutableDictionary *_config_dict; +} + +@property (strong) NSString *refreshToken; +@property (strong) NSDate *expireDate; + +@end + + +@implementation CSOauth2Authenticator + +-(instancetype) initWithServiceName:(NSString *)serviceName clientID:(NSString *)client_id flowType:(NSString *)flow_type config:(NSDictionary *)config_dict +{ + if (self = [self init]) + { + self.serviceName = serviceName; + self.clientID = client_id; + self.forceVerify = NO; + self.useKeychain = YES; + self.flowType = flow_type; + + if (config_dict) + { + _config_dict = [config_dict mutableCopy]; + } else { + _config_dict = [[NSMutableDictionary alloc] init]; + } + } + + return self; +} + + + + +-(void)configurationVariableSet:(id)val forName:(NSString *)forName +{ + [_config_dict setObject:val forKey:forName]; +} + +-(id)configurationVariableGet:(NSString *)forName +{ + return [_config_dict objectForKey:forName]; +} + +-(id)configurationVariableRemove:(NSString *)forName +{ + id ret = [_config_dict objectForKey:forName]; + [_config_dict removeObjectForKey:forName]; + return ret; +} + + +-(NSMutableDictionary *)buildKeychainQuery +{ + NSString *useServiceName = [NSString stringWithFormat:@"CocoaSplit-%@", self.serviceName]; + NSMutableDictionary *keyChainAttrs = [[NSMutableDictionary alloc] init]; + [keyChainAttrs setObject:(__bridge NSString *)kSecClassGenericPassword forKey:(__bridge NSString *)kSecClass]; + [keyChainAttrs setObject:useServiceName forKey:(__bridge NSString *)kSecAttrService]; + [keyChainAttrs setObject:self.accountName forKey:(__bridge NSString *)kSecAttrAccount]; + [keyChainAttrs setObject:(__bridge id)kSecAttrAccessibleAfterFirstUnlock forKey:(__bridge NSString *)kSecAttrAccessible]; + return keyChainAttrs; +} + + +-(void)loadFromKeychain +{ + + if (!self.useKeychain || !self.accountName || !self.serviceName) + { + return; + } + + NSMutableDictionary *keyChainAttrs = [self buildKeychainQuery]; + + [keyChainAttrs setObject:(id)kCFBooleanTrue forKey:(__bridge NSString *)kSecReturnData]; + [keyChainAttrs setObject:(__bridge id)kSecMatchLimitOne forKey:(__bridge NSString *)kSecMatchLimit]; + + + CFDataRef keyData = NULL; + + if (!SecItemCopyMatching((__bridge CFDictionaryRef)keyChainAttrs, (CFTypeRef *)&keyData)) + { + if (keyData) + { + NSDictionary *storedData = [NSKeyedUnarchiver unarchiveObjectWithData:(__bridge NSData *)keyData]; + + if (storedData) + { + self.accessToken = storedData[@"accessToken"]; + self.refreshToken = storedData[@"refreshToken"]; + self.expireDate = storedData[@"expireDate"]; + + } + CFRelease(keyData); + } + + } +} + + +-(void)saveToKeychain:(NSString *)accountName +{ + self.accountName = accountName; + [self saveToKeychain]; +} + + +-(void)saveToKeychain +{ + if (!self.useKeychain || !self.accountName) + { + return; + } + + NSMutableDictionary *keyChainAttrs = [self buildKeychainQuery]; + + NSMutableDictionary *keyDataDict = [[NSMutableDictionary alloc] init]; + [keyDataDict setObject:self.accessToken forKey:@"accessToken"]; + if (self.refreshToken) + { + [keyDataDict setObject:self.refreshToken forKey:@"refreshToken"]; + + } + + if (self.expireDate) + { + [keyDataDict setObject:self.expireDate forKey:@"expireDate"]; + + } + + NSData *keyData = [NSKeyedArchiver archivedDataWithRootObject:keyDataDict]; + [keyChainAttrs setObject:keyData forKey:(__bridge NSString *)kSecValueData]; + + + OSStatus addRes = SecItemAdd((__bridge CFDictionaryRef)keyChainAttrs, NULL); + + if (addRes == errSecDuplicateItem) + { + SecItemDelete((__bridge CFDictionaryRef)keyChainAttrs); + SecItemAdd((__bridge CFDictionaryRef)keyChainAttrs, NULL); + + } +} + + + +-(NSString *)buildQueryString:(NSDictionary *)params +{ + NSMutableArray *paramParts = [[NSMutableArray alloc] init]; + + for (NSString *pname in params) + { + NSString *pval = params[pname]; + NSString *equalString = [NSString stringWithFormat:@"%@=%@", pname, [pval stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]; + [paramParts addObject:equalString]; + } + + return [paramParts componentsJoinedByString:@"&"]; +} + + +-(void)buildAuthURL +{ + //NSQueryItems is 10.10+, so we can't use it :/ + + NSString *authLocation = [self configurationVariableGet:kCSOauth2ConfigAuthURL]; + + if (!authLocation) + { + return; + } + + NSURLComponents *urlComponent = [NSURLComponents componentsWithString:authLocation]; + + NSMutableArray *paramParts = [[NSMutableArray alloc] init]; + + NSString *response_type = nil; + + if ([self.flowType isEqualToString:kCSOauth2CodeFlow]) + { + response_type = @"code"; + } else if ([self.flowType isEqualToString:kCSOauth2ImplicitGrantFlow]) { + response_type = @"token"; + } + + + [paramParts addObject:[NSString stringWithFormat:@"response_type=%@", response_type]]; + + [paramParts addObject:[NSString stringWithFormat:@"client_id=%@", self.clientID]]; + + NSString *redirectURL = [self configurationVariableGet:kCSOauth2ConfigRedirectURL]; + + if (redirectURL) + { + [paramParts addObject:[NSString stringWithFormat:@"redirect_uri=%@", redirectURL]]; + } + + + NSArray *authScopes = [self configurationVariableGet:kCSOauth2ConfigScopes]; + + if (authScopes) + { + NSString *scopeValue = [authScopes componentsJoinedByString:@" "]; + [paramParts addObject:[NSString stringWithFormat:@"scope=%@", scopeValue]]; + } + + NSDictionary *extraAuthParams = [self configurationVariableGet:kCSOauth2ExtraAuthParams]; + + if (extraAuthParams) + { + for(NSString *key in extraAuthParams) + { + NSString *val = extraAuthParams[key]; + [paramParts addObject:[NSString stringWithFormat:@"%@=%@", key,val]]; + } + } + + + NSString *paramString = [paramParts componentsJoinedByString:@"&"]; + + urlComponent.query = paramString; + + + self.authURL = urlComponent.URL; +} + + +-(bool)doesTokenNeedRefresh +{ + + + if (!self.expireDate) + { + return NO; + } + + NSDate *nowDate = [NSDate date]; + + + if ([nowDate compare:self.expireDate] == NSOrderedDescending) + { + return YES; + } + + return NO; +} + + +-(void)authorize:(void (^)(bool success))authCallback +{ + + bool doAuth = NO; + + if (!self.forceVerify) + { + [self loadFromKeychain]; + if ([self doesTokenNeedRefresh]) + { + _authorizeCallback = authCallback; + [self refreshAccessToken]; + return; + } + + if (self.accessToken) + { + doAuth = NO; + } else { + doAuth = YES; + } + } else { + doAuth = YES; + } + + if (doAuth) + { + _authorizeCallback = authCallback; + + [self buildAuthURL]; + + + NSRect winFrame = NSMakeRect(0, 0, 1000, 1000); + _authWebView = [[WebView alloc] initWithFrame:winFrame frameName:nil groupName:nil]; + _authWebView.policyDelegate = self; + + _authWindow = [[NSWindow alloc] initWithContentRect:winFrame styleMask:NSTitledWindowMask|NSClosableWindowMask|NSMiniaturizableWindowMask|NSResizableWindowMask backing:NSBackingStoreBuffered defer:NO]; + + [_authWindow center]; + [_authWindow setContentView:_authWebView]; + [_authWindow makeKeyAndOrderFront:NSApp]; + [[_authWebView mainFrame] loadRequest:[NSURLRequest requestWithURL:self.authURL]]; + } else { + if (authCallback) + { + authCallback(!!self.accessToken); + } + } + +} + + +-(void)closeAuthWindow +{ + _authWindow = nil; +} + +-(void)refreshAccessToken +{ + NSString *refreshLocation = [self configurationVariableGet:kCSOauth2AccessRefreshURL]; + NSString *clientSecret = [self configurationVariableGet:kCSOauth2ClientSecret]; + NSString *redirectURL = [self configurationVariableGet:kCSOauth2ConfigRedirectURL]; + + + if (!refreshLocation || !clientSecret || !redirectURL || !self.refreshToken) + { + return; + } + + + NSURL *locationURL = [NSURL URLWithString:refreshLocation]; + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:locationURL]; + request.HTTPMethod = @"POST"; + + NSDictionary *queryDict = @{@"grant_type": @"refresh_token", + @"client_id": self.clientID, + @"refresh_token": self.refreshToken, + @"client_secret": clientSecret }; + + NSString *queryString = [self buildQueryString:queryDict]; + request.HTTPBody = [queryString dataUsingEncoding:NSUTF8StringEncoding]; + + NSURLSession *urlSession = [NSURLSession sharedSession]; + + NSURLSessionDataTask *dataTask = [urlSession dataTaskWithRequest:request + completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { + NSError *jsonError; + NSDictionary *tokenData = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&jsonError]; + + self.accessToken = tokenData[@"access_token"]; + NSNumber *expire_seconds = tokenData[@"expires_in"]; + self.expireDate = [NSDate dateWithTimeIntervalSinceNow:expire_seconds.integerValue]; + if (_authorizeCallback) + { + _authorizeCallback(!!self.accessToken); + } + + + }]; + + [dataTask resume]; + + +} +-(void)requestAccessToken:(NSString *)forCode +{ + + + NSString *tokenLocation = [self configurationVariableGet:kCSOauth2AccessTokenRequestURL]; + NSString *clientSecret = [self configurationVariableGet:kCSOauth2ClientSecret]; + NSString *redirectURL = [self configurationVariableGet:kCSOauth2ConfigRedirectURL]; + + + if (!tokenLocation || !clientSecret || !redirectURL) + { + return; + } + + + NSURL *locationURL = [NSURL URLWithString:tokenLocation]; + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:locationURL]; + request.HTTPMethod = @"POST"; + + NSDictionary *queryDict = @{@"grant_type": @"authorization_code", + @"code": forCode, + @"redirect_uri": redirectURL, + @"client_id": self.clientID, + @"client_secret": clientSecret }; + + NSString *queryString = [self buildQueryString:queryDict]; + request.HTTPBody = [queryString dataUsingEncoding:NSUTF8StringEncoding]; + + NSURLSession *urlSession = [NSURLSession sharedSession]; + + + NSURLSessionDataTask *dataTask = [urlSession dataTaskWithRequest:request + completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { + NSError *jsonError; + NSDictionary *tokenData = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&jsonError]; + + + self.accessToken = tokenData[@"access_token"]; + self.refreshToken = tokenData[@"refresh_token"]; + NSNumber *expire_seconds = tokenData[@"expires_in"]; + self.expireDate = [NSDate dateWithTimeIntervalSinceNow:expire_seconds.integerValue]; + if (_authorizeCallback) + { + _authorizeCallback(!!self.accessToken); + if (self.accountNameFetcher && self.useKeychain) + { + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ + + self.accountNameFetcher(self); + + }); + } + } + + + }]; + + [dataTask resume]; + + + + + +} +-(NSString *)extractCodeFromURL:(NSURL *)url +{ + NSString *query = url.query; + + for (NSString *param in [query componentsSeparatedByString:@"&"]) + { + NSArray *pparts = [param componentsSeparatedByString:@"="]; + if (pparts.count < 2) + { + //what? + continue; + } + + NSString *pname = pparts.firstObject; + NSString *pvalue = pparts.lastObject; + + if ([pname isEqualToString:@"code"]) + { + return pvalue; + } + } + return nil; +} + + + +-(NSString *)extractAccessTokenFromURL:(NSURL *)url +{ + NSURLComponents *urlComp = [NSURLComponents componentsWithURL:url resolvingAgainstBaseURL:NO]; + NSString *urlFragment = urlComp.percentEncodedFragment; + + NSURLComponents *fakeComponents = [NSURLComponents componentsWithString:[NSString stringWithFormat:@"http://localhost/blah?%@", urlFragment]]; + + NSArray *queryVars = fakeComponents.queryItems; + + NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name=%@", @"access_token"]; + NSURLQueryItem *tokenItem = [queryVars filteredArrayUsingPredicate:predicate].firstObject; + + return tokenItem.value; +} + + +-(bool)isURLRedirect:(NSURL *)testurl +{ + + + NSString *redirectURL = [self configurationVariableGet:kCSOauth2ConfigRedirectURL]; + + if (!redirectURL) + { + return NO; + } + + NSURLComponents *testComp = [NSURLComponents componentsWithURL:testurl resolvingAgainstBaseURL:NO]; + NSURLComponents *redirectComp = [NSURLComponents componentsWithString:redirectURL]; + + if (![testComp.scheme isEqualToString:redirectComp.scheme]) + { + return NO; + } + + if (![testComp.host isEqualToString:redirectComp.host]) + { + return NO; + } + + NSString *testPath = testComp.path; + NSString *rePath = redirectComp.path; + + if ([testPath isEqualToString:rePath]) + { + return YES; + } + + return NO; +} + + +-(void)webView:(WebView *)webView decidePolicyForNavigationAction:(NSDictionary *)actionInformation request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id)listener +{ + + NSURL *reqUrl = request.URL; + NSString *redirectURL = [self configurationVariableGet:kCSOauth2ConfigRedirectURL]; + + if (reqUrl && redirectURL) + { + + + + if ([self isURLRedirect:reqUrl]) + { + [listener ignore]; + + if ([self.flowType isEqualToString:kCSOauth2ImplicitGrantFlow]) + { + self.accessToken = [self extractAccessTokenFromURL:reqUrl]; + if (_authorizeCallback) + { + _authorizeCallback(!!self.accessToken); + if (self.accountNameFetcher && self.useKeychain) + { + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ + + self.accountNameFetcher(self); + + }); + } + } + } else if ([self.flowType isEqualToString:kCSOauth2CodeFlow]) { + NSString *flowCode = [self extractCodeFromURL:reqUrl]; + [self requestAccessToken:flowCode]; + } + [self closeAuthWindow]; + } + } + + [listener use]; +} + + + + + +-(void)authorizedJsonRequest:(NSMutableURLRequest *)request completionHandler:(void (^)(id decodedData))handler +{ + //Set OAuth Bearer header + + NSString *authType; + + if ([self.flowType isEqualToString:kCSOauth2ImplicitGrantFlow]) + { + authType = @"OAuth"; + } else if ([self.flowType isEqualToString:kCSOauth2CodeFlow]) { + authType = @"Bearer"; + } + + + + [request setValue:[NSString stringWithFormat:@"%@ %@", authType, self.accessToken] forHTTPHeaderField:@"Authorization"]; + + NSURLSession *urlSession = [NSURLSession sharedSession]; + + + NSURLSessionDataTask *dataTask = [urlSession dataTaskWithRequest:request + completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { + NSError *jsonError; + id json_object = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&jsonError]; + if (handler) + { + handler(json_object); + } + }]; + + [dataTask resume]; +} + +-(void)jsonRequest:(NSMutableURLRequest *)request completionHandler:(void (^)(id decodedData))handler +{ + [self authorize:^(bool success) { + if (success) + { + [self authorizedJsonRequest:request completionHandler:handler]; + } + + }]; +} + + +@end diff --git a/CocoaSplit/CSPluginLoader.m b/CocoaSplit/CSPluginLoader.m index 08e4c138..7983ed56 100644 --- a/CocoaSplit/CSPluginLoader.m +++ b/CocoaSplit/CSPluginLoader.m @@ -102,9 +102,11 @@ NSURL *filterURL = [NSURL fileURLWithPath:IUPath]; [CIPlugIn loadPlugIn:filterURL allowExecutableCode:YES]; } + } + -(bool)validateAndRegisterPluginClass:(Class)toLoad { if (!toLoad) diff --git a/CocoaSplit/CSPluginServices.m b/CocoaSplit/CSPluginServices.m index 3b2d7189..a53d788d 100644 --- a/CocoaSplit/CSPluginServices.m +++ b/CocoaSplit/CSPluginServices.m @@ -10,11 +10,12 @@ #import "CAMultiAudioPCMPlayer.h" #import "CSPcmPlayer.h" #import "AppDelegate.h" +#import "PreviewView.h" @implementation CSPluginServices -+(id) sharedPluginServices ++(CSPluginServices *) sharedPluginServices { static CSPluginServices *sharedCSPluginServices = nil; static dispatch_once_t onceToken; @@ -26,6 +27,63 @@ return sharedCSPluginServices; } + + + + + + +-(NSArray *)accountNamesForService:(NSString *)serviceName +{ + + NSString *useServiceName = [NSString stringWithFormat:@"CocoaSplit-%@", serviceName]; + NSMutableDictionary *keyChainAttrs = [[NSMutableDictionary alloc] init]; + + [keyChainAttrs setObject:(__bridge NSString *)kSecClassGenericPassword forKey:(__bridge NSString *)kSecClass]; + [keyChainAttrs setObject:useServiceName forKey:(__bridge NSString *)kSecAttrService]; + [keyChainAttrs setObject:(id)kCFBooleanTrue forKey:(__bridge NSString *)kSecReturnAttributes]; + [keyChainAttrs setObject:(__bridge id)kSecMatchLimitAll forKey:(__bridge NSString *)kSecMatchLimit]; + + CFArrayRef resultArray = NULL; + + NSMutableArray *retArr = [[NSMutableArray alloc] init]; + + SecItemCopyMatching((CFDictionaryRef)keyChainAttrs, (CFTypeRef *)&resultArray); + + + for (NSDictionary *itemDict in (__bridge NSArray *)resultArray) + { + NSString *accountName = itemDict[(__bridge NSString *)kSecAttrAccount]; + [retArr addObject:accountName]; + } + + return retArr; +} + + +-(double) currentFPS +{ + AppDelegate *myAppDelegate = [[NSApplication sharedApplication] delegate]; + if (myAppDelegate.captureController) + { + return myAppDelegate.captureController.captureFPS; + } + return 0.0; +} + + +-(int) audioSampleRate +{ + AppDelegate *myAppDelegate = [[NSApplication sharedApplication] delegate]; + if (myAppDelegate.captureController) + { + return myAppDelegate.captureController.audioSamplerate; + } + return 0; +} + + + -(void)loadPythonClass:(NSString *)pyClass fromFile:(NSString *)fromFile withBlock:(void (^)(__unsafe_unretained Class))withBlock { [CaptureController loadPythonClass:pyClass fromFile:fromFile withBlock:withBlock]; @@ -73,4 +131,11 @@ } +-(CSOauth2Authenticator *) createOAuth2Authenticator:(NSString *)serviceName clientID:(NSString *)client_id flowType:(NSString *)flow_type config:(NSDictionary *)config_dict; +{ + + return [[CSOauth2Authenticator alloc] initWithServiceName:serviceName clientID:client_id flowType:flow_type config:config_dict]; +} + + @end diff --git a/CocoaSplit/CSPreviewGLLayer.h b/CocoaSplit/CSPreviewGLLayer.h index e860bb5d..9b86711c 100644 --- a/CocoaSplit/CSPreviewGLLayer.h +++ b/CocoaSplit/CSPreviewGLLayer.h @@ -20,6 +20,7 @@ NSSize _lastSurfaceSize; bool _resizeDirty; CIContext *_cictx; + bool _resetClearColor; GLint _viewport[4]; @@ -36,6 +37,9 @@ @property (assign) float snap_y; @property (assign) float snap_x; +@property (assign) bool doRender; +@property (assign) bool midiActive; +@property (assign) bool resizeDirty; -(NSPoint)realPointforWindowPoint:(NSPoint)winPoint; @@ -43,4 +47,5 @@ + @end diff --git a/CocoaSplit/CSPreviewGLLayer.m b/CocoaSplit/CSPreviewGLLayer.m index 56cce2ec..8be0b262 100644 --- a/CocoaSplit/CSPreviewGLLayer.m +++ b/CocoaSplit/CSPreviewGLLayer.m @@ -9,6 +9,7 @@ #import "CSPreviewGLLayer.h" @implementation CSPreviewGLLayer +@synthesize midiActive = _midiActive; -(instancetype)init { @@ -30,22 +31,64 @@ } +-(void)setMidiActive:(bool)midiActive +{ + _midiActive = midiActive; + _resetClearColor = YES; +} + +-(bool)midiActive +{ + return _midiActive; +} + + -(void)drawInCGLContext:(CGLContextObj)ctx pixelFormat:(CGLPixelFormatObj)pf forLayerTime:(CFTimeInterval)t displayTime:(const CVTimeStamp *)ts { + CGLSetCurrentContext(ctx); if (!_initDone) { glGenTextures(1, &_renderTexture); - glClearColor(0.184314f, 0.309804f, 0.309804f, 0); - _initDone = YES; } + + if (_resetClearColor) + { + if (self.midiActive) + { + glClearColor(0.309804f, 0.184314f, 0.309804f, 0); + } else { + glClearColor(0.184314f, 0.309804f, 0.309804f, 0); + } + + _resetClearColor = NO; + } + + glClear(GL_COLOR_BUFFER_BIT); + if (!self.renderer) + { + return; + } - CVPixelBufferRef toDraw = [self.renderer currentFrame]; + + CVPixelBufferRef toDraw; + if (self.doRender) + { + toDraw = [self.renderer currentImg]; + CGLSetCurrentContext(ctx); + + if (toDraw) + { + CVPixelBufferRetain(toDraw); + } + } else { + toDraw = [self.renderer currentFrame]; + } if (!toDraw) { @@ -103,6 +146,7 @@ glMatrixMode(GL_PROJECTION); glLoadIdentity(); + glOrtho(0.0, self.bounds.size.width, 0.0, self.bounds.size.height, 0, 1); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); @@ -149,7 +193,6 @@ - GLfloat outline_verts[8]; GLfloat snapx_verts[4]; GLfloat snapy_verts[4]; @@ -248,8 +291,7 @@ glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0); glDisable(GL_TEXTURE_RECTANGLE_ARB); - - + [super drawInCGLContext:ctx pixelFormat:pf forLayerTime:t displayTime:ts]; @@ -300,11 +342,15 @@ -(void)setBounds:(CGRect)bounds { - _resizeDirty = YES; + dispatch_async(dispatch_get_main_queue(), ^{ + self.resizeDirty = YES; + + }); [super setBounds:bounds]; } + -(BOOL)isAsynchronous { return YES; diff --git a/CocoaSplit/CSPreviewOverlayView.h b/CocoaSplit/CSPreviewOverlayView.h index 6c2287d5..8e528192 100644 --- a/CocoaSplit/CSPreviewOverlayView.h +++ b/CocoaSplit/CSPreviewOverlayView.h @@ -19,6 +19,8 @@ @property (weak) InputSource *parentSource; @property (weak) PreviewView *previewView; +@property (assign) bool renderControls; + -(void)updatePosition; @end diff --git a/CocoaSplit/CSPreviewOverlayView.m b/CocoaSplit/CSPreviewOverlayView.m index 5f99f8aa..e710e329 100644 --- a/CocoaSplit/CSPreviewOverlayView.m +++ b/CocoaSplit/CSPreviewOverlayView.m @@ -27,6 +27,18 @@ #define RESIZE_HANDLE_SIZE 6.0f + +-(instancetype) init +{ + if (self = [super init]) + { + self.renderControls = YES; + } + + return self; +} + + -(BOOL)wantsDefaultClipping { return NO; @@ -96,7 +108,7 @@ CGContextSetStrokeColorWithColor(currentContext, [[NSColor blueColor] CGColor]); CGContextStrokeRect(currentContext, [self insetSelectionRect]); - if (self.parentSource && self.previewView) + if (self.renderControls && self.parentSource && self.previewView) { CGContextSetFillColorWithColor(currentContext, [NSColor knobColor].CGColor); CGContextFillRect(currentContext, [self bottomLeftResizeRect]); @@ -191,6 +203,7 @@ } + -(InputSource *)parentSource { return _parentSource; @@ -216,6 +229,7 @@ NSLog(@"MOUSE ENTERED!!!"); } + @end diff --git a/CocoaSplit/CSTextCaptureBase.m b/CocoaSplit/CSTextCaptureBase.m index c6e13a91..96d33480 100644 --- a/CocoaSplit/CSTextCaptureBase.m +++ b/CocoaSplit/CSTextCaptureBase.m @@ -101,6 +101,12 @@ } +-(NSImage *)libraryImage +{ + return [NSImage imageNamed:NSImageNameFontPanel]; +} + + -(void) buildString { diff --git a/CocoaSplit/CSTimedOutputBuffer.h b/CocoaSplit/CSTimedOutputBuffer.h new file mode 100644 index 00000000..083d947b --- /dev/null +++ b/CocoaSplit/CSTimedOutputBuffer.h @@ -0,0 +1,33 @@ +// +// CSTimedOutputBuffer.h +// CocoaSplit +// +// Created by Zakk on 4/2/16. +// Copyright © 2016 Zakk. All rights reserved. +// + +#import +#import "VideoCompressor.h" +#import "FFMpegTask.h" +#import "CSIRCompressor.h" + + +@interface CSTimedOutputBuffer : NSObject +{ + NSMutableArray *_frameBuffer; + FFMpegTask *_outFFMpeg; + float _currentBufferDuration; + +} + + + +@property (assign) float bufferDuration; +@property (strong) NSString *name; +@property (strong) CSIRCompressor *compressor; + +-(void) writeCurrentBuffer:(NSString *)toFile; +-(instancetype) initWithCompressor:(id)compressor; + + +@end diff --git a/CocoaSplit/CSTimedOutputBuffer.m b/CocoaSplit/CSTimedOutputBuffer.m new file mode 100644 index 00000000..f3302887 --- /dev/null +++ b/CocoaSplit/CSTimedOutputBuffer.m @@ -0,0 +1,124 @@ +// +// CSTimedOutputBuffer.m +// CocoaSplit +// +// Created by Zakk on 4/2/16. +// Copyright © 2016 Zakk. All rights reserved. +// + +#import "CSTimedOutputBuffer.h" +#import "AppDelegate.h" + +@implementation CSTimedOutputBuffer + + + +-(instancetype) init +{ + if (self = [super init]) + { + _frameBuffer = [[NSMutableArray alloc] init]; + _name = @"Instant Recording"; + } + return self; +} + +-(instancetype) initWithCompressor:(id)compressor +{ + if (self = [self init]) + { + _compressor = compressor; + [_compressor addOutput:self]; + } + + return self; +} + +-(void) writeCurrentBuffer:(NSString *)toFile +{ + AppDelegate *appD = NSApp.delegate; + CaptureController *controller = appD.captureController; + + + FFMpegTask *newout = [[FFMpegTask alloc] init]; + + newout.video_codec_id = self.compressor.codec_id; + newout.framerate = controller.captureFPS; + newout.stream_output = [toFile stringByStandardizingPath]; + newout.settingsController = controller; + newout.samplerate = controller.audioSamplerate; + newout.audio_bitrate = controller.audioBitrate; + + NSMutableArray *fCopy; + @synchronized(self) { + fCopy = _frameBuffer.copy; + } + + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + for (CapturedFrameData *cData in fCopy) + { + [newout writeEncodedData:cData]; + } + [newout stopProcess]; + }); + +} + + +-(void) writeEncodedData:(CapturedFrameData *)frameData +{ + + + float frameDuration = CMTimeGetSeconds(frameData.videoDuration); + + @synchronized(self) { + [_frameBuffer addObject:frameData]; + } + _currentBufferDuration += frameDuration; + + //Drain the buffer if we need to + //Try to always have a keyframe at the head of the buffer, even if it means we have to fudge the duration a bit + + while (_currentBufferDuration > self.bufferDuration) + { + + float deleteDuration = 0.0f; + + //gobble until first keyFrame + + int delcnt = 0; + NSMutableArray *fCopy; + @synchronized(self) { + fCopy = _frameBuffer.copy; + } + + for (CapturedFrameData *cFrame in fCopy) + { + deleteDuration += CMTimeGetSeconds(cFrame.videoDuration); + delcnt++; + + if (cFrame.isKeyFrame) + { + break; + } + } + + if ((_currentBufferDuration - deleteDuration) < self.bufferDuration) + { + //We'd have to delete too much, just leave it for now, we'll get it next time. + break; + } + + if (delcnt > 0) + { + @synchronized(self) { + [_frameBuffer removeObjectsInRange:NSMakeRange(0, delcnt)]; + } + _currentBufferDuration -= deleteDuration; + } + } + + +} + +@end diff --git a/CocoaSplit/CaptureController.h b/CocoaSplit/CaptureController.h index fe303f9c..3aa35f7a 100644 --- a/CocoaSplit/CaptureController.h +++ b/CocoaSplit/CaptureController.h @@ -9,7 +9,6 @@ #import #import #import -#import "AVFAudioCapture.h" #import #import "CSCaptureSourceProtocol.h" #import "CSAbstractCaptureDevice.h" @@ -22,22 +21,32 @@ #import "CSNotifications.h" #import "PluginManagerWindowController.h" #import "CreateLayoutViewController.h" +#import "CSAddInputViewController.h" #import "CAMultiAudioEngine.h" #import "CSAnimationRunnerObj.h" #import "CSAnimationChooserViewController.h" #import "CSMidiManagerWindowController.h" #import "MIKMIDI.h" #import "CSTimerSourceProtocol.h" - +#import "CSInputLibraryWindowController.h" +#import "CSInputLibraryItem.h" +#import "CSNewOutputWindowController.h" +#import "CompressionSettingsPanelController.h" +#import "AppleProResCompressor.h" +#import "CSAddOutputPopupViewController.h" +#import "CSAnimationWindowController.h" +#import "CSStreamOutputWindowController.h" @class FFMpegTask; -@protocol h264Compressor; +@protocol VideoCompressor; @class OutputDestination; @class InputSource; @class SourceLayout; @class LayoutPreviewWindowController; - +@class CSLayoutEditWindowController; +@class CSTimedOutputBuffer; +@class CSAdvancedAudioWindowController; void VideoCompressorReceiveFrame(void *, void *, OSStatus , VTEncodeInfoFlags , CMSampleBufferRef ); @@ -46,32 +55,14 @@ void VideoCompressorReceiveFrame(void *, void *, OSStatus , VTEncodeInfoFlags , @class PreviewView; -@interface CaptureController : NSObject { +@interface CaptureController : NSObject { - bool _stagingHidden; NSArray *_inputIdentifiers; - - CIFilter *_compositeFilter; - CIImage *_backgroundImage; - - CVDisplayLinkRef _displayLink; - NSRect _stagingFrame; NSRect _liveFrame; - - id _audio_capture_session; - - CFAbsoluteTime _lastcvtime; - - NSThread *mainThread; - - NSTimer *_captureTimer; - NSTimer *_idleTimer; - BOOL _cmdLineInfo; - NSScreen *_fullscreenOn; @@ -81,9 +72,15 @@ void VideoCompressorReceiveFrame(void *, void *, OSStatus , VTEncodeInfoFlags , id _activity_token; long long _frameCount; - CFAbsoluteTime _firstFrameTime; + long long _streamFrameStart; + CFAbsoluteTime _lastFrameTime; + CFAbsoluteTime _firstFrameTime; + CMTime _firstAudioTime; + CMTime _previousAudioTime; + + dispatch_queue_t _main_capture_queue; dispatch_queue_t _preview_queue; dispatch_source_t _dispatch_timer; @@ -98,41 +95,55 @@ void VideoCompressorReceiveFrame(void *, void *, OSStatus , VTEncodeInfoFlags , double _frame_time; double _start_time; - CMSampleBufferRef audioRingBuffer[512]; - size_t audioWritePosition; - size_t audioLastReadPosition; - NSMutableArray *audioBuffer; NSMutableArray *videoBuffer; + dispatch_source_t _log_source; - int _saved_stderr; bool _last_running_value; - CIFilter *_cifilter; - NSOpenGLContext *_ogl_ctx; - CGLContextObj _cgl_ctx; - float _min_render_time; float _max_render_time; float _avg_render_time; float _render_time_total; int _renderedFrames; - NSTask *_renderTask; - NSConnection *_remoteConn; - id _renderServer; + NSPopover *_addInputpopOver; + + NSPopover *_addOutputpopOver; + NSPopover *_layoutpopOver; NSPopover *_animatepopOver; NSMutableArray *_screensCache; + NSMutableArray *_layoutWindows; + NSMutableArray *_outputWindows; - - + bool _needsIRReset; + NSMutableArray *_audioBuffer; + CSAdvancedAudioWindowController *_audioWindowController; + CSAnimationWindowController *_animationWindowController; + CSStreamOutputWindowController *_streamOutputWindowController; } +@property (assign) bool useInstantRecord; +@property (assign) int instantRecordBufferDuration; +@property (strong) NSString *instantRecordCompressor; +@property (strong) NSString *instantRecordDirectory; + + +@property (assign) bool instantRecordActive; + +@property (strong) CSTimedOutputBuffer *instantRecorder; + + +@property (weak) IBOutlet NSCollectionView *layoutCollectionView; + +@property (assign) bool stagingHidden; + +@property (strong) NSMutableArray *inputLibrary; @property (weak) IBOutlet NSMenu *stagingFullScreenMenu; @property (weak) IBOutlet NSMenu *liveFullScreenMenu; @@ -160,30 +171,22 @@ void VideoCompressorReceiveFrame(void *, void *, OSStatus , VTEncodeInfoFlags , @property (strong) CAMultiAudioEngine *multiAudioEngine; -@property (assign) bool renderOnIntegratedGPU; - - -@property (strong) LayoutPreviewWindowController *layoutPreviewController; @property (strong) PluginManagerWindowController *pluginManagerController; @property (strong) CSMidiManagerWindowController *midiManagerController; @property (strong) NSString *renderStatsString; +@property (strong) NSString *outputStatsString; -@property (strong) NSString *layoutPanelName; @property (strong) NSMutableArray *sourceLayouts; @property (strong) SourceLayout *selectedLayout; @property (strong) SourceLayout *stagingLayout; -@property (weak) IBOutlet NSView *stagingControls; -@property (weak) IBOutlet NSView *goLiveControls; @property (weak) IBOutlet NSSplitView *canvasSplitView; -@property (strong) id videoCompressor; -@property (strong) AVFAudioCapture *audioCaptureSession; @property (assign) double captureFPS; @@ -191,15 +194,8 @@ void VideoCompressorReceiveFrame(void *, void *, OSStatus , VTEncodeInfoFlags , @property (assign) int audioSamplerate; -@property (assign) double min_delay; -@property (assign) double max_delay; -@property (assign) double avg_delay; -@property (assign) long long compressedFrameCount; -@property (assign) NSMutableArray *streamPanelDestinations; -@property (strong) NSUserDefaults *cmdLineArgs; @property (assign) double audio_adjust; -@property (assign) CFAbsoluteTime last_dl_time; @property (weak) IBOutlet NSPopover *editorPopover; @@ -207,9 +203,6 @@ void VideoCompressorReceiveFrame(void *, void *, OSStatus , VTEncodeInfoFlags , @property (weak) IBOutlet PreviewView *stagingCtx; @property (unsafe_unretained) IBOutlet NSObjectController *objectController; -@property (strong) IBOutlet NSObjectController *compressSettingsController; -@property (strong) IBOutlet NSObjectController *outputPanelController; -@property (strong) IBOutlet NSObjectController *layoutPanelController; @property (readonly) NSArray *layoutSortDescriptors; @@ -222,9 +215,17 @@ void VideoCompressorReceiveFrame(void *, void *, OSStatus , VTEncodeInfoFlags , @property (strong) NSString *transitionName; @property (strong) NSString *transitionDirection; @property (strong) CIFilter *transitionFilter; +@property (assign) bool transitionFullScene; +@property (assign) NSInteger active_output_count; +@property (assign) NSInteger total_dropped_frames; +@property (assign) NSInteger pendingAnimations; +@property (strong) NSString *pendingAnimationString; + + @property (strong) NSWindow *transitionFilterWindow; +- (IBAction)doInstantRecord:(id)sender; -(IBAction)openTransitionFilterPanel:(NSButton *)sender; @@ -237,6 +238,7 @@ void VideoCompressorReceiveFrame(void *, void *, OSStatus , VTEncodeInfoFlags , - (IBAction)openMidiManager:(id)sender; +-(IBAction) swapStagingAndLive:(id)sender; - (IBAction)stagingGoLive:(id)sender; - (IBAction)stagingSave:(id)sender; - (IBAction)stagingRevert:(id)sender; @@ -246,116 +248,62 @@ void VideoCompressorReceiveFrame(void *, void *, OSStatus , VTEncodeInfoFlags , - (IBAction)unlockStagingFPS:(id)sender; - (IBAction)unlockLiveFPS:(id)sender; -- (void)deleteLayout:(NSInteger)deleteIdx; +- (bool)deleteLayout:(SourceLayout *)toDelete; -- (IBAction)addStreamingService:(id)sender; - - (IBAction)streamButtonPushed:(id)sender; - (IBAction)closeAdvancedPrefPanel:(id)sender; - (IBAction)openAdvancedPrefPanel:(id)sender; - (IBAction)openCreateSheet:(id)sender; -- (IBAction)openVideoAdvanced:(id)sender; -- (IBAction)closeVideoAdvanced:(id)sender; -- (void)openCompressPanel:(bool)doEdit; -- (IBAction)newCompressPanel; -- (IBAction)editCompressPanel; --(IBAction)deleteCompressorPanel; - -- (IBAction)closeCompressPanel; - -- (IBAction)addInputSource:(id)sender; - -- (IBAction)openAudioMixerPanel:(id)sender; -- (IBAction)closeAudioMixerPanel:(id)sender; - -- (IBAction)closeCreateSheet:(id)sender; -- (IBAction)openLayoutPanel:(id)sender; -@property (strong) NSString *compressTabLabel; - -@property (weak) IBOutlet NSDictionaryController *compressController; +- (IBAction)chooseInstantRecordDirectory:(id)sender; + + + -@property (strong) id editingCompressor; -@property (strong) NSString *editingCompressorKey; @property (strong) NSMutableDictionary *compressors; -@property (strong) id selectedCompressor; @property (weak) NSString *selectedVideoType; @property (strong) NSString *selectedCompressorType; -@property (strong) NSArray *videoTypes; -@property (strong) NSArray *compressorTypes; -@property (strong) NSMutableArray *ffmpeg_objects; -@property (weak) NSString *streamingServiceServer; -@property (weak) NSString *streamingServiceKey; - -@property (weak) NSString *streamingDestination; -@property (weak) NSString *selectedDestinationType; -@property (weak) IBOutlet NSTabView *compressTabs; -@property (strong) IBOutlet NSWindow *createSheet; -@property (strong) IBOutlet NSWindow *advancedVideoPanel; -@property (strong) IBOutlet NSWindow *compressPanel; + @property (strong) IBOutlet NSWindow *advancedPrefPanel; @property (strong) IBOutlet NSWindow *logWindow; -@property (strong) IBOutlet NSWindow *audioMixerPanel; -@property (strong) IBOutlet NSWindow *outputEditPanel; -@property (strong) IBOutlet NSWindow *layoutPanel; -@property (weak) IBOutlet NSView *streamServiceAddView; +@property (strong) CSNewOutputWindowController *addOutputWindowController; +@property (strong) CompressionSettingsPanelController *compressionEditPanelController; +@property (weak) IBOutlet NSWindow *mainWindow; + -@property (unsafe_unretained) IBOutlet NSWindow *streamServiceConfWindow; -@property (strong) NSViewController *streamServicePluginViewController; -@property (strong) NSObject*streamServiceObject; - (IBAction)openLogWindow:(id)sender; -@property (readonly) NSArray *destinationTypes; @property (strong) NSMutableArray *captureDestinations; @property (weak) NSIndexSet *selectedCaptureDestinations; @property (assign) int selectedTabIndex; -@property (assign) BOOL showPreview; -@property (assign) int captureVideoAverageBitrate; -@property (assign) int captureVideoMaxBitrate; -@property (assign) int captureVideoMaxKeyframeInterval; -@property (strong) NSString *x264tune; -@property (strong) NSString *x264preset; -@property (strong) NSString *x264profile; -@property (assign) int x264crf; -@property (strong) NSMutableArray *x264tunes; -@property (strong) NSMutableArray *x264presets; -@property (strong) NSMutableArray *x264profiles; -@property (strong) NSString *vtcompressor_profile; -@property (assign) BOOL videoCBR; @property (assign) int maxOutputPending; @property (assign) int maxOutputDropped; @property (assign) BOOL captureRunning; -@property (strong) NSArray *arOptions; @property (strong) NSString *resolutionOption; -@property (strong) OutputDestination *editDestination; - - - - @property (assign) int captureHeight; @property (assign) int captureWidth; @@ -369,18 +317,12 @@ void VideoCompressorReceiveFrame(void *, void *, OSStatus , VTEncodeInfoFlags , @property (weak) NSArray *audioCaptureDevices; -@property (strong) FFMpegTask *ffmpeg_obj; -@property (strong) AVAssetWriterInput *video_writer; -@property (strong) AVAssetWriterInput *audio_writer; -@property (weak) CSAbstractCaptureDevice *selectedVideoCapture; -@property (readonly) AVCaptureDevice *selectedAudioCapture; -@property (weak) NSString *ffmpeg_path; +@property (weak) IBOutlet NSOutlineView *inputOutlineView; -@property NSString *imageDirectory; @property (strong) NSDictionary *extraSaveData; @@ -398,6 +340,17 @@ void VideoCompressorReceiveFrame(void *, void *, OSStatus , VTEncodeInfoFlags , @property (strong) NSMutableDictionary *extraPluginsSaveData; @property (strong) CSPluginLoader *sharedPluginLoader; +@property (strong) PreviewView *activePreviewView; + +@property (strong) CSInputLibraryWindowController *inputLibraryController; + +@property (strong) NSIndexSet *inputOutlineSelectionIndexes; + + +@property (weak) IBOutlet NSArrayController *activeInputsArrayController; + +@property (assign) bool inLayoutTransition; + - (IBAction)openAnimatePopover:(NSButton *)sender; @@ -406,7 +359,6 @@ void VideoCompressorReceiveFrame(void *, void *, OSStatus , VTEncodeInfoFlags , - (void)loadSettings; - (bool) startStream; - (void) stopStream; -- (void) loadCmdlineSettings:(NSUserDefaults *)cmdargs; -(void)setExtraData:(id)saveData forKey:(NSString *)forKey; -(id)getExtraData:(NSString *)forkey; -(CVPixelBufferRef)currentFrame; @@ -422,19 +374,20 @@ void VideoCompressorReceiveFrame(void *, void *, OSStatus , VTEncodeInfoFlags , -(SourceLayout *)addLayoutFromBase:(SourceLayout *)baseLayout; -(SourceLayout *)getLayoutForName:(NSString *)name; -- (IBAction)layoutTableSelected:(NSTableView *)sender; +-(void)openAddInputPopover:(NSButton *)sender; + - (IBAction)openLayoutPopover:(NSButton *)sender; -- (IBAction)mainDeleteLayoutClicked:(id)sender; -- (IBAction)mainCopyLayoutClicked:(id)sender; --(IBAction)stagingDeleteLayoutClicked:(id)sender; --(IBAction)stagingCopyLayoutClicked:(id)sender; +-(void)openLayoutPopover:(NSButton *)sender forLayout:(SourceLayout *)layout; +-(void)openBuiltinLayoutPopover:(NSView *)sender spawnRect:(NSRect)spawnRect forLayout:(SourceLayout *)layout; -- (IBAction)stagingAnimationSelected:(id)sender; +@property (weak) IBOutlet NSTableView *inputTableView; + +@property (weak) IBOutlet NSTableView *outputTableView; +- (IBAction)outputEditClicked:(OutputDestination *)sender; @property (weak) IBOutlet NSArrayController *sourceLayoutsArrayController; -@property (weak) IBOutlet NSTableView *mainSourceLayoutTableView; -@property (weak) IBOutlet NSTableView *stagingSourceLayoutTableView; +@property (weak) IBOutlet NSTreeController *inputTreeController; -(void)setupLogging; +(CSAnimationRunnerObj *) sharedAnimationObj; @@ -451,10 +404,33 @@ void VideoCompressorReceiveFrame(void *, void *, OSStatus , VTEncodeInfoFlags , -(void)layoutLeftFullscreen; +(void)loadPythonClass:(NSString *)pyClass fromFile:(NSString *)fromFile withBlock:(void(^)(Class))withBlock; +(Class)loadPythonClass:(NSString *)pyClass fromFile:(NSString *)fromFile; +-(void)toggleLayout:(SourceLayout *)layout; +-(void)saveToLayout:(SourceLayout *)layout; +-(void)switchToLayout:(SourceLayout *)layout; +-(CSLayoutEditWindowController *)openLayoutWindow:(SourceLayout *)layout; +-(void)layoutWindowWillClose:(CSLayoutEditWindowController *)windowController; + +-(void)addInputToLibrary:(InputSource *)source; +- (IBAction)openLibraryWindow:(id) sender; +-(void)updateFrameIntervals; + +- (IBAction)configureIRCompressor:(id)sender; +- (IBAction)inputTableControlClick:(NSButton *)sender; +-(void) resetInputTableHighlights; +- (IBAction)removePendingAnimations:(id)sender; +- (IBAction)outputSegmentedAction:(NSButton *)sender; + +- (IBAction)openAdvancedAudio:(id)sender; +- (IBAction)openAnimationWindow:(id)sender; +- (IBAction)openStreamOutputWindow:(id)sender; +-(void) removeObjectFromCaptureDestinationsAtIndex:(NSUInteger)index; +-(void)openAddOutputPopover:(id)sender sourceRect:(NSRect)sourceRect; + +- (IBAction)previewAnimations:(id)sender; @end diff --git a/CocoaSplit/CaptureController.m b/CocoaSplit/CaptureController.m index 4c3cfdf6..9752c133 100644 --- a/CocoaSplit/CaptureController.m +++ b/CocoaSplit/CaptureController.m @@ -26,58 +26,30 @@ #import "MIKMIDI.h" #import "CSMidiWrapper.h" #import "CSCaptureBase+TimerDelegate.h" -#import +#import "CSLayoutEditWindowController.h" +#import "CSTimedOutputBuffer.h" +#import "CSAdvancedAudioWindowController.h" +#import + @implementation CaptureController -@synthesize selectedCompressorType = _selectedCompressorType; @synthesize selectedLayout = _selectedLayout; @synthesize stagingLayout = _stagingLayout; @synthesize audioSamplerate = _audioSamplerate; @synthesize transitionName = _transitionName; +@synthesize useInstantRecord = _useInstantRecord; +@synthesize instantRecordBufferDuration = _instantRecordBufferDuration; --(IBAction)mainDeleteLayoutClicked:(id)sender -{ - - NSInteger selectedIdx = self.mainSourceLayoutTableView.selectedRow; - if (selectedIdx != -1) - { - [self deleteLayout:selectedIdx]; - [self layoutTableSelected:self.mainSourceLayoutTableView]; - } -} - --(IBAction)stagingDeleteLayoutClicked:(id)sender -{ - - NSInteger selectedIdx = self.stagingSourceLayoutTableView.selectedRow; - if (selectedIdx != -1) - { - [self deleteLayout:selectedIdx]; - [self layoutTableSelected:self.stagingSourceLayoutTableView]; - } -} - --(IBAction)stagingCopyLayoutClicked:(id)sender -{ - [self cloneSelectedSourceLayout:self.stagingSourceLayoutTableView]; -} - -- (IBAction)stagingAnimationSelected:(id)sender { -} --(IBAction)mainCopyLayoutClicked:(id)sender -{ - [self cloneSelectedSourceLayout:self.mainSourceLayoutTableView]; -} -(void) cloneSelectedSourceLayout:(NSTableView *)fromTable @@ -93,9 +65,142 @@ } -- (IBAction)openLayoutPopover:(NSButton *)sender +-(void)openBuiltinLayoutPopover:(NSView *)sender spawnRect:(NSRect)spawnRect forLayout:(SourceLayout *)layout +{ + CreateLayoutViewController *vc; + if (!_layoutpopOver) + { + _layoutpopOver = [[NSPopover alloc] init]; + + _layoutpopOver.animates = YES; + _layoutpopOver.behavior = NSPopoverBehaviorTransient; + } + + if (!_layoutpopOver.contentViewController) + { + vc = [[CreateLayoutViewController alloc] initForBuiltin]; + + + _layoutpopOver.contentViewController = vc; + _layoutpopOver.delegate = vc; + vc.popover = _layoutpopOver; + + } + + SourceLayout *useLayout = layout; + if (!useLayout) + { + vc.createDialog = YES; + useLayout = [[SourceLayout alloc] init]; + } + vc.sourceLayout = useLayout; + + + [_layoutpopOver showRelativeToRect:spawnRect ofView:sender preferredEdge:NSMinYEdge]; +} + + +-(void)openAddOutputPopover:(id)sender sourceRect:(NSRect)sourceRect +{ + CSAddOutputPopupViewController *vc; + if (!_addOutputpopOver) + { + _addOutputpopOver = [[NSPopover alloc] init]; + _addOutputpopOver.animates = YES; + _addOutputpopOver.behavior = NSPopoverBehaviorTransient; + } + + //if (!_addInputpopOver.contentViewController) + { + vc = [[CSAddOutputPopupViewController alloc] init]; + vc.addOutput = ^void(Class outputClass) { + [self outputPopupButtonAction:outputClass]; + }; + + _addOutputpopOver.contentViewController = vc; + vc.popover = _addOutputpopOver; + //_addInputpopOver.delegate = vc; + } + + [_addOutputpopOver showRelativeToRect:sourceRect ofView:sender preferredEdge:NSMaxXEdge]; +} + +- (IBAction)previewAnimations:(id)sender { + if (self.stagingHidden) + { + return; + } + + //save and copy both live and staging + //set staging layout to copy of live + //apply transition settings to staging + //replace staging with copy of 'old' staging + //when done, delay 1.5 seconds and then restore old staging + + [self.selectedLayout saveSourceList]; + [self.stagingLayout saveSourceList]; + + SourceLayout *stagingSave = self.activePreviewView.sourceLayout; + + SourceLayout *liveCopy = [self.selectedLayout copy]; + SourceLayout *stagingCopy = [self.stagingLayout copy]; + + + [liveCopy restoreSourceList:liveCopy.savedSourceListData]; + + [stagingCopy restoreSourceList:stagingCopy.savedSourceListData]; + + self.activePreviewView.sourceLayout = liveCopy; + liveCopy.in_staging = NO; + + [self applyTransitionSettings:liveCopy]; + dispatch_time_t delay_dispatch = dispatch_time(DISPATCH_TIME_NOW, 1.0 * NSEC_PER_SEC); + dispatch_after(delay_dispatch, dispatch_get_main_queue(), ^{ + [liveCopy replaceWithSourceLayout:stagingCopy withCompletionBlock:^{ + + self.activePreviewView.sourceLayout.in_staging = YES; + dispatch_time_t inner_dispatch = dispatch_time(DISPATCH_TIME_NOW, 1.5 * NSEC_PER_SEC); + dispatch_after(inner_dispatch, dispatch_get_main_queue(), ^{ + self.activePreviewView.sourceLayout = stagingSave; + }); + + }]; + + }); + + +} + + + + +-(void)openAddInputPopover:(id)sender sourceRect:(NSRect)sourceRect +{ + CSAddInputViewController *vc; + if (!_addInputpopOver) + { + _addInputpopOver = [[NSPopover alloc] init]; + _addInputpopOver.animates = YES; + _addInputpopOver.behavior = NSPopoverBehaviorTransient; + } + + //if (!_addInputpopOver.contentViewController) + { + vc = [[CSAddInputViewController alloc] init]; + _addInputpopOver.contentViewController = vc; + vc.popover = _addInputpopOver; + vc.previewView = self.activePreviewView; + //_addInputpopOver.delegate = vc; + } + + [_addInputpopOver showRelativeToRect:sourceRect ofView:sender preferredEdge:NSMaxXEdge]; +} + + +-(void)openLayoutPopover:(NSButton *)sender forLayout:(SourceLayout *)layout +{ CreateLayoutViewController *vc; if (!_layoutpopOver) { @@ -109,7 +214,6 @@ { vc = [[CreateLayoutViewController alloc] init]; - vc.controller = self; _layoutpopOver.contentViewController = vc; _layoutpopOver.delegate = vc; @@ -117,16 +221,86 @@ } - vc.sourceLayout = [[SourceLayout alloc] init]; - + SourceLayout *useLayout = layout; + if (!useLayout) + { + vc.createDialog = YES; + useLayout = [[SourceLayout alloc] init]; + } + vc.sourceLayout = useLayout; [_layoutpopOver showRelativeToRect:sender.bounds ofView:sender preferredEdge:NSMinYEdge]; +} + + +-(void)layoutWindowWillClose:(CSLayoutEditWindowController *)windowController +{ + + if ([_layoutWindows containsObject:windowController]) + { + [_layoutWindows removeObject:windowController]; + } +} + + +- (IBAction)openLibraryWindow:(id) sender +{ + CSInputLibraryWindowController *newController = [[CSInputLibraryWindowController alloc] init]; + + [newController showWindow:nil]; + + newController.controller = self; + + self.inputLibraryController = newController; +} + +-(void)addInputToLibrary:(InputSource *)source +{ + CSInputLibraryItem *newItem = [[CSInputLibraryItem alloc] initWithInput:source]; + + NSUInteger cIdx = self.inputLibrary.count; + + [self insertObject:newItem inInputLibraryAtIndex:cIdx]; } -- (void)deleteLayout:(NSInteger)deleteIdx +-(CSLayoutEditWindowController *)openLayoutWindow:(SourceLayout *)layout +{ + CSLayoutEditWindowController *newController = [[CSLayoutEditWindowController alloc] init]; + + [newController showWindow:nil]; + + newController.previewView.isEditWindow = YES; + + LayoutRenderer *wRenderer = [[LayoutRenderer alloc] init]; + + newController.previewView.layoutRenderer = wRenderer; + + newController.previewView.controller = self; + newController.previewView.sourceLayout = layout; + [newController.previewView.sourceLayout restoreSourceList:nil]; + newController.delegate = self; + + + [_layoutWindows addObject:newController]; + return newController; +} + + + + + +- (IBAction)openLayoutPopover:(NSButton *)sender +{ + + + [self openLayoutPopover:sender forLayout:nil]; + +} + + +- (bool)deleteLayout:(SourceLayout *)toDelete { - SourceLayout *toDelete = [self.sourceLayoutsArrayController.arrangedObjects objectAtIndex:deleteIdx]; if (toDelete) { @@ -135,15 +309,11 @@ toDelete.isActive = NO; - - [self.sourceLayoutsArrayController removeObjectAtArrangedObjectIndex:deleteIdx]; - - if (self.selectedLayout == toDelete) - { - self.selectedLayout = nil; - } + [self.sourceLayoutsArrayController removeObject:toDelete]; + return YES; } } + return NO; } @@ -191,6 +361,7 @@ newLayout.canvas_height = self.captureHeight; } + [self insertObject:newLayout inSourceLayoutsAtIndex:self.sourceLayouts.count]; @@ -231,9 +402,15 @@ SourceLayout *layout; + if (tableView == self.inputTableView) + { + return [tableView makeViewWithIdentifier:@"inputTableCellView" owner:tableView]; + } + + if (tableView.tag == 0) { - layout = self.stagingPreviewView.sourceLayout; + layout = self.activePreviewView.sourceLayout; } else { layout = self.livePreviewView.sourceLayout; } @@ -253,6 +430,7 @@ retView = [tableView makeViewWithIdentifier:@"LabelCellView" owner:self]; } else if ([tableColumn.identifier isEqualToString:@"value"]) { + if ([inputmap[@"type"] isEqualToString:@"param"]) { retView = [tableView makeViewWithIdentifier:@"InputParamView" owner:self]; @@ -283,7 +461,6 @@ { vc = [[CSAnimationChooserViewController alloc] init]; - vc.controller = self; _animatepopOver.contentViewController = vc; _animatepopOver.delegate = vc; @@ -291,12 +468,7 @@ } - if (sender.tag == 1) - { - vc.sourceLayout = self.stagingPreviewView.sourceLayout; - } else { - vc.sourceLayout = self.livePreviewView.sourceLayout; - } + vc.sourceLayout = self.activePreviewView.sourceLayout; [_animatepopOver showRelativeToRect:sender.bounds ofView:sender preferredEdge:NSMinYEdge]; @@ -312,422 +484,95 @@ } --(NSString *)selectedCompressorType +-(void)outputPopupButtonAction:(Class)outputClass { - - return _selectedCompressorType; + + OutputDestination *newDest = [[OutputDestination alloc] initWithType:[outputClass label]]; + id serviceObj = [[outputClass alloc] init]; + newDest.streamServiceObject = serviceObj; + + [self openOutputSheet:newDest]; } --(void)setSelectedCompressorType:(NSString *)selectedCompressorType +-(void)openOutputSheet:(OutputDestination *)toEdit { - _selectedCompressorType = selectedCompressorType; - self.compressTabLabel = selectedCompressorType; - - if (!self.editingCompressor || self.editingCompressor.isNew) + + CSNewOutputWindowController *newController = nil; + + if (!_outputWindows) { - if ([selectedCompressorType isEqualToString:@"x264"]) + _outputWindows = [[NSMutableArray alloc] init]; + } + + + + newController = [[CSNewOutputWindowController alloc] init]; + newController.compressors = self.compressors; + if (toEdit) + { + newController.outputDestination = toEdit; + } + + + newController.windowDone = ^void(NSModalResponse returnCode, CSNewOutputWindowController *window) { + + if (returnCode == NSModalResponseOK) { - self.editingCompressor = [[x264Compressor alloc] init]; - } else if ([selectedCompressorType isEqualToString:@"AppleVTCompressor"]) { - self.editingCompressor = [[AppleVTCompressor alloc] init]; - } else { - self.editingCompressor = nil; - } - if (self.editingCompressor) - { - self.editingCompressor.isNew = YES; - } - - } - - -} - - - - --(IBAction)newCompressPanel -{ - [self openCompressPanel:NO]; -} - --(IBAction)editCompressPanel -{ - [self openCompressPanel:YES]; -} - - --(IBAction)deleteCompressorPanel -{ - - if (self.editingCompressor) - { - NSString *deleteKey = self.editingCompressor.name; - - if (deleteKey) - { - self.selectedCompressor = nil; - self.editingCompressor = nil; - - [self deleteCompressorForName:deleteKey]; - } - } - - [self closeCompressPanel]; -} - - --(void)deleteCompressorForName:(NSString *)name -{ - id to_delete = self.compressors[name]; - - [self willChangeValueForKey:@"compressors"]; - [self.compressors removeObjectForKey:name]; - [self didChangeValueForKey:@"compressors"]; - [[NSNotificationCenter defaultCenter] postNotificationName:CSNotificationCompressorDeleted object:to_delete userInfo:nil]; -} - - --(IBAction)openCompressPanel:(bool)doEdit -{ - self.selectedCompressor = nil; - - if (self.compressController.selectedObjects.count > 0) - { - id tmpCompressor; - tmpCompressor = [[self.compressController.selectedObjects objectAtIndex:0] valueForKey:@"value"]; - - - self.selectedCompressor = self.compressors[tmpCompressor.name]; - } - - - if (doEdit) - { - self.editingCompressor = self.selectedCompressor; - self.editingCompressorKey = self.selectedCompressor.name; - - - if (self.editingCompressor) - { - self.selectedCompressorType = self.editingCompressor.compressorType; - self.compressTabLabel = self.editingCompressor.compressorType; - } - } else { - self.selectedCompressorType = self.selectedCompressorType; - } - - - - if (!self.compressPanel) - { - NSString *panelName; - - panelName = @"CompressionSettingsPanel"; - - [[NSBundle mainBundle] loadNibNamed:panelName owner:self topLevelObjects:nil]; - - - - [NSApp beginSheet:self.compressPanel modalForWindow:[NSApplication sharedApplication].mainWindow modalDelegate:self didEndSelector:NULL contextInfo:NULL]; - - } - -} - - --(NSString *)addCompressor:(id )newCompressor -{ - NSMutableString *baseName = newCompressor.name; - - NSMutableString *newName = baseName; - int name_try = 1; - - while (self.compressors[newName]) { - newName = [NSMutableString stringWithFormat:@"%@#%d", baseName, name_try]; - name_try++; - } - - newCompressor.name = newName; - [self willChangeValueForKey:@"compressors"]; - [self.compressors setObject:newCompressor forKey:newName]; - [self didChangeValueForKey:@"compressors"]; - - [[NSNotificationCenter defaultCenter] postNotificationName:CSNotificationCompressorAdded object:newCompressor userInfo:nil]; - - return newName; - -} - - - --(void) setCompressSelection:(NSString *)forName -{ - - for (id tmpval in self.compressController.arrangedObjects) - { - if ([[tmpval valueForKey:@"key"] isEqualToString:forName] ) - { - [self.compressController setSelectedObjects:@[tmpval]]; - break; - } - } -} - - - --(IBAction)saveCompressPanel -{ - - NSError *compressError; - - - if (self.editingCompressor) - { - - if (![self.editingCompressor validate:&compressError]) - { - if (compressError) + + OutputDestination *newDest = window.outputDestination; + if (newDest) { - [NSApp presentError:compressError]; + newDest.settingsController = self; + + NSInteger idx = NSNotFound; + + if (toEdit) + { + idx = [self.captureDestinations indexOfObject:toEdit]; + } + if (idx != NSNotFound) + { + [self replaceObjectInCaptureDestinationsAtIndex:idx withObject:newDest]; + } else { + [self insertObject:newDest inCaptureDestinationsAtIndex:self.captureDestinations.count]; + } } - return; } - - - - if (self.editingCompressor.isNew) - { - - self.editingCompressor.isNew = NO; - NSString *newName = [self addCompressor:self.editingCompressor]; - - [self setCompressSelection:newName]; - - - - } else if (![self.editingCompressorKey isEqualToString:self.editingCompressor.name]) { - //the name was changed in the edit dialog, so create a new key entry and delete the old one - NSString *newName = [self addCompressor:self.editingCompressor]; - - - - - [self willChangeValueForKey:@"compressors"]; - [self.compressors removeObjectForKey:self.editingCompressorKey]; - [self didChangeValueForKey:@"compressors"]; - [self setCompressSelection:newName]; - - - - } else { - [self.compressors setObject:self.editingCompressor forKey:self.editingCompressor.name]; - } - } - - [self closeCompressPanel]; + [_outputWindows removeObject:window]; + [window close]; + }; + [_outputWindows addObject:newController]; + [newController showWindow:nil]; } --(IBAction)closeCompressPanel -{ - - [NSApp endSheet:self.compressPanel]; - [self.compressPanel close]; - self.compressPanel = nil; - self.editingCompressor = nil; - self.editingCompressorKey = nil; -} - -- (IBAction)addInputSource:(id)sender -{ - if (self.selectedLayout) - { - - - InputSource *newSource = [[InputSource alloc] init]; - [self.selectedLayout addSource:newSource]; - [self.previewCtx spawnInputSettings:newSource atRect:NSZeroRect]; - } -} - - - -- (IBAction)openAudioMixerPanel:(id)sender { - - if (!self.audioMixerPanel) - { - [[NSBundle mainBundle] loadNibNamed:@"AudioMixer" owner:self topLevelObjects:nil]; - [NSApp beginSheet:self.audioMixerPanel modalForWindow:[NSApplication sharedApplication].mainWindow modalDelegate:self didEndSelector:NULL contextInfo:NULL]; - } -} - - -- (IBAction)closeAudioMixerPanel:(id)sender { - - [NSApp endSheet:self.audioMixerPanel]; - [self.audioMixerPanel close]; - self.audioMixerPanel = nil; -} - - - --(IBAction)openVideoAdvanced:(id)sender -{ - - - NSString *panelName; - - if (!self.advancedVideoPanel) - { - - - panelName = [NSString stringWithFormat:@"%@AdvancedPanel", self.selectedVideoType]; - - - [[NSBundle mainBundle] loadNibNamed:panelName owner:self topLevelObjects:nil]; - - [NSApp beginSheet:self.advancedVideoPanel modalForWindow:[NSApplication sharedApplication].mainWindow modalDelegate:self didEndSelector:NULL contextInfo:NULL]; - - } - -} - --(IBAction)closeVideoAdvanced:(id)sender -{ - [NSApp endSheet:self.advancedVideoPanel]; - [self.advancedVideoPanel close]; - self.advancedVideoPanel = nil; -} - -(IBAction)openCreateSheet:(id)sender { - - - self.streamServiceObject = nil; - - NSMutableDictionary *servicePlugins = [[CSPluginLoader sharedPluginLoader] streamServicePlugins]; - - - Class serviceClass = servicePlugins[self.selectedDestinationType];; - - - if (serviceClass) - { - self.streamServiceObject = [[serviceClass alloc] init]; - } - - - - - - if (self.streamServiceObject) - { - NSViewController *serviceConfigView = [self.streamServiceObject getConfigurationView]; - self.streamServiceAddView.frame = serviceConfigView.view.frame; - - [self.streamServiceAddView addSubview:serviceConfigView.view]; - self.streamServicePluginViewController = serviceConfigView; - [NSApp beginSheet:self.streamServiceConfWindow modalForWindow:[NSApplication sharedApplication].mainWindow modalDelegate:self didEndSelector:NULL contextInfo:NULL]; - } - + [self openOutputSheet:nil]; } - --(IBAction)closeCreateSheet:(id)sender +- (IBAction)chooseInstantRecordDirectory:(id)sender { + + NSOpenPanel *panel = [NSOpenPanel openPanel]; + panel.canChooseDirectories = YES; + panel.canCreateDirectories = YES; + panel.canChooseFiles = NO; + panel.allowsMultipleSelection = NO; - - [self.streamServicePluginViewController.view removeFromSuperview]; - - - [NSApp endSheet:self.streamServiceConfWindow]; - [self.streamServiceConfWindow close]; - self.streamServicePluginViewController = nil; - self.streamServiceObject = nil; + [panel beginWithCompletionHandler:^(NSInteger result) { + if (result == NSFileHandlingPanelOKButton) + { + self.instantRecordDirectory = panel.URL.path; + } + + }]; } -- (IBAction)openLayoutPanel:(id)sender -{ - if (!self.layoutPanel) - { - - [[NSBundle mainBundle] loadNibNamed:@"NewLayoutPanel" owner:self topLevelObjects:nil]; - - } - [NSApp beginSheet:self.layoutPanel modalForWindow:[NSApp mainWindow] modalDelegate:self didEndSelector:NULL contextInfo:NULL]; -} - - - - --(AVCaptureDevice *)selectedAudioCapture -{ - if (self.audioCaptureSession) - { - return self.audioCaptureSession.activeAudioDevice; - } - - return nil; -} - - --(void) selectedAudioCaptureFromID:(NSString *)uniqueID -{ - if (uniqueID) - { - self.audioCaptureSession.activeAudioDevice = [AVCaptureDevice deviceWithUniqueID:uniqueID]; - } - -} - - --(void) createCGLContext -{ - NSOpenGLPixelFormatAttribute glAttributes[] = { - - NSOpenGLPFANoRecovery, - NSOpenGLPFAAccelerated, - //NSOpenGLPFAAllowOfflineRenderers, - NSOpenGLPFADepthSize, 32, - (NSOpenGLPixelFormatAttribute) 0,0, - (NSOpenGLPixelFormatAttribute) 0 - - }; - if (self.renderOnIntegratedGPU) - { - NSLog(@"RENDERING ON INTELHD!"); - - glAttributes[5] = NSOpenGLPFARendererID; - glAttributes[6] = kCGLRendererIntelHDID; - } - - - NSOpenGLPixelFormat *pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:glAttributes]; - - if (!pixelFormat) - { - return; - } - - _ogl_ctx = [[NSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:nil]; - - if (!_ogl_ctx) - { - return; - } - - _cgl_ctx = [_ogl_ctx CGLContextObj]; - - -} - - - -(void)buildScreensInfo:(NSNotification *)notification { @@ -925,6 +770,7 @@ } + -(void)exportLayout:(SourceLayout *)layout { NSSavePanel *panel = [NSSavePanel savePanel]; @@ -938,16 +784,19 @@ if (layout == self.selectedLayout) { useLayout = self.livePreviewView.sourceLayout; + [useLayout saveSourceList]; + [useLayout saveAnimationSource]; } else if (layout == self.stagingLayout) { useLayout = self.stagingPreviewView.sourceLayout; + [useLayout saveSourceList]; + [useLayout saveAnimationSource]; + } [panel beginWithCompletionHandler:^(NSInteger result) { if (result == NSFileHandlingPanelOKButton) { NSURL *saveFile = [panel URL]; - [useLayout saveSourceList]; - [useLayout saveAnimationSource]; [NSKeyedArchiver archiveRootObject:useLayout toFile:saveFile.path]; @@ -963,8 +812,14 @@ if (self = [super init]) { + + _layoutWindows = [NSMutableArray array]; + self.transitionDirections = @[kCATransitionFromTop, kCATransitionFromRight, kCATransitionFromBottom, kCATransitionFromLeft]; - + self.useInstantRecord = YES; + self.instantRecordActive = YES; + self.instantRecordBufferDuration = 60; + NSArray *caTransitionNames = @[kCATransitionFade, kCATransitionPush, kCATransitionMoveIn, kCATransitionReveal, @"cube", @"alignedCube", @"flip", @"alignedFlip"]; NSArray *ciTransitionNames = [CIFilter filterNamesInCategory:kCICategoryTransition]; @@ -996,11 +851,10 @@ - audioLastReadPosition = 0; - audioWritePosition = 0; - audioBuffer = [[NSMutableArray alloc] init]; videoBuffer = [[NSMutableArray alloc] init]; + _audioBuffer = [[NSMutableArray alloc] init]; + @@ -1012,19 +866,24 @@ self.useStatusColors = YES; + + dispatch_source_t sigsrc = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, SIGPIPE, 0, dispatch_get_global_queue(0, 0)); dispatch_source_set_event_handler(sigsrc, ^{ return;}); dispatch_resume(sigsrc); - _main_capture_queue = dispatch_queue_create("CocoaSplit.main.queue", NULL); + /* + dispatch_queue_attr_t attr = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_DEFAULT, 0); + _main_capture_queue = dispatch_queue_create("CocoaSplit.main.queue", attr); _preview_queue = dispatch_queue_create("CocoaSplit.preview.queue", NULL); + */ + + _main_capture_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0); + _preview_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0); + - self.showPreview = YES; - self.videoTypes = @[@"Desktop", @"AVFoundation", @"QTCapture", @"Syphon", @"Image", @"Text"]; - self.compressorTypes = @[@"x264", @"AppleVTCompressor", @"None"]; - self.arOptions = @[@"None", @"Use Source", @"Preserve AR"]; mach_timebase_info(&_mach_timebase); @@ -1037,22 +896,15 @@ dispatch_strict_flag = 0; } - /* - _dispatch_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, dispatch_strict_flag, _main_capture_queue); - - dispatch_source_set_timer(_dispatch_timer, DISPATCH_TIME_NOW, _frame_interval, 0); - - dispatch_source_set_event_handler(_dispatch_timer, ^{[self newFrameDispatched];}); - - dispatch_resume(_dispatch_timer); - */ - _audio_statistics_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue()); dispatch_source_set_timer(_audio_statistics_timer, DISPATCH_TIME_NOW, 0.10*NSEC_PER_SEC, 0); dispatch_source_set_event_handler(_audio_statistics_timer, ^{ - [self.multiAudioEngine updateStatistics]; + if (self.multiAudioEngine) + { + [self.multiAudioEngine updateStatistics]; + } }); dispatch_resume(_audio_statistics_timer); @@ -1063,23 +915,43 @@ dispatch_source_set_timer(_statistics_timer, DISPATCH_TIME_NOW, 1*NSEC_PER_SEC, 0); dispatch_source_set_event_handler(_statistics_timer, ^{ + int total_outputs = 0; + int errored_outputs = 0; + int dropped_frame_cnt = 0; + for (OutputDestination *outdest in _captureDestinations) { + if (outdest.active) + { + total_outputs++; + if (outdest.errored) + { + errored_outputs++; + } + + dropped_frame_cnt += outdest.dropped_frame_count; + } [outdest updateStatistics]; } - self.renderStatsString = [NSString stringWithFormat:@"Render min/max/avg: %f/%f/%f", _min_render_time, _max_render_time, _render_time_total / _renderedFrames]; + dispatch_async(dispatch_get_main_queue(), ^{ + self.outputStatsString = [NSString stringWithFormat:@"Active Outputs: %d Errored %d Frames dropped %d", total_outputs, errored_outputs,dropped_frame_cnt]; + self.renderStatsString = [NSString stringWithFormat:@"Render min/max/avg: %f/%f/%f", _min_render_time, _max_render_time, _render_time_total / _renderedFrames]; + self.active_output_count = total_outputs; + self.total_dropped_frames = dropped_frame_cnt; + + }); _renderedFrames = 0; _render_time_total = 0.0f; }); - + + dispatch_resume(_statistics_timer); - self.extraSaveData = [[NSMutableDictionary alloc] init]; //load all filters, then load our custom filter(s) @@ -1108,8 +980,16 @@ @"ChangeInterval", @"EffectDuration", @"MultiTransition", @"PositionX", @"PositionY"]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(buildScreensInfo:) name:NSApplicationDidChangeScreenParametersNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(layoutCanvasChanged:) name:CSNotificationLayoutCanvasChanged object:nil]; + + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(layoutFramerateChanged:) name:CSNotificationLayoutFramerateChanged object:nil]; + + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(compressorReconfigured:) name:CSNotificationCompressorReconfigured object:nil]; + + } @@ -1119,6 +999,44 @@ } +-(void)compressorReconfigured:(NSNotification *)notification +{ + + + id compressor = [notification object]; + if (self.instantRecorder && [compressor isEqual:self.instantRecorder.compressor]) + { + [self resetInstantRecorder]; + } +} + + +-(void)layoutFramerateChanged:(NSNotification *)notification +{ + SourceLayout *layout = [notification object]; + if (layout == self.livePreviewView.sourceLayout || layout == self.stagingPreviewView.sourceLayout) + { + [self updateFrameIntervals]; + } + + if (layout == self.livePreviewView.sourceLayout) + { + [self resetInstantRecorder]; + } +} + + +-(void)layoutCanvasChanged:(NSNotification *)notification +{ + SourceLayout *layout = [notification object]; + + if ([layout isEqual:self.livePreviewView.sourceLayout]) + { + + [self resetInstantRecorder]; + } +} + -(NSData *)archiveLayout:(SourceLayout *)layout { @@ -1276,14 +1194,6 @@ } --(NSArray *)destinationTypes -{ - - NSMutableDictionary *servicePlugins = [[CSPluginLoader sharedPluginLoader] streamServicePlugins]; - - return servicePlugins.allKeys; -} - -(void)setAudioSamplerate:(int)audioSamplerate { @@ -1328,7 +1238,15 @@ [fileManager createDirectoryAtPath:saveFolder withIntermediateDirectories:NO attributes:nil error:nil]; } - NSString *saveFile = [saveFolder stringByAppendingPathComponent:@"CocoaSplit-CA.settings"]; + NSString *saveFile = [saveFolder stringByAppendingPathComponent:@"CocoaSplit-2.settings"]; + + if ([fileManager fileExistsAtPath:saveFile]) + { + return saveFile; + } + + + saveFile = [saveFolder stringByAppendingPathComponent:@"CocoaSplit-CA.settings"]; if ([fileManager fileExistsAtPath:saveFile]) { @@ -1355,7 +1273,7 @@ [fileManager createDirectoryAtPath:saveFolder withIntermediateDirectories:NO attributes:nil error:nil]; } - NSString *saveFile = @"CocoaSplit-CA.settings"; + NSString *saveFile = @"CocoaSplit-2.settings"; return [saveFolder stringByAppendingPathComponent:saveFile]; } @@ -1435,57 +1353,94 @@ dispatch_resume(_log_source); } + + +-(void) resetInstantRecorder +{ + + + if (self.instantRecorder && self.instantRecorder.compressor) + { + id irCompressor = self.instantRecorder.compressor; + if ([irCompressor outputCount] > 1 && !_needsIRReset) + { + _needsIRReset = YES; + } else { + [irCompressor reset]; + } + + } +} + + +-(void) setupInstantRecorder +{ + id irCompressor = self.compressors[@"InstantRecorder"]; + + if (irCompressor) + { + self.instantRecorder = [[CSTimedOutputBuffer alloc] initWithCompressor:irCompressor]; + self.instantRecorder.bufferDuration = self.instantRecordBufferDuration; + } +} + + -(void) migrateDefaultCompressor:(NSMutableDictionary *)saveRoot { - if (self.compressors[@"default"]) + + id defaultCompressor = self.compressors[@"default"]; + if (defaultCompressor) { - //We already migrated, or the user did it for us? - return; + [self.compressors removeObjectForKey:@"default"]; + defaultCompressor.name = defaultCompressor.compressorType.mutableCopy; + [self.compressors setObject:defaultCompressor forKey:@"x264"]; + NSDictionary *notifyMsg = [NSDictionary dictionaryWithObjectsAndKeys:@"default", @"oldName", defaultCompressor, @"compressor", nil]; + [[NSNotificationCenter defaultCenter] postNotificationName:CSNotificationCompressorRenamed object:notifyMsg]; } - - id newCompressor; - if ([self.selectedCompressorType isEqualToString:@"x264"]) + + + if (!self.compressors[@"x264"]) { + x264Compressor *newCompressor; - x264Compressor *tmpCompressor; + newCompressor = [[x264Compressor alloc] init]; + newCompressor.name = @"x264".mutableCopy; + newCompressor.vbv_buffer = 1000; + newCompressor.vbv_maxrate = 1000; + newCompressor.keyframe_interval = 2; - tmpCompressor = [[x264Compressor alloc] init]; - tmpCompressor.tune = [saveRoot valueForKey:@"x264tune"]; - tmpCompressor.profile = [saveRoot valueForKey:@"x264profile"]; - tmpCompressor.preset = [saveRoot valueForKey:@"x264preset"]; - tmpCompressor.use_cbr = [[saveRoot valueForKey:@"videoCBR"] boolValue]; - tmpCompressor.crf = [[saveRoot valueForKey:@"x264crf"] intValue]; - tmpCompressor.vbv_maxrate = [[saveRoot valueForKey:@"captureVideoAverageBitrate"] intValue]; - tmpCompressor.vbv_buffer = [[saveRoot valueForKey:@"captureVideoMaxBitrate"] intValue]; - tmpCompressor.keyframe_interval = [[saveRoot valueForKey:@"captureVideoMaxKeyframeInterval"] intValue]; - newCompressor = tmpCompressor; - } else if ([self.selectedCompressorType isEqualToString:@"AppleVTCompressor"]) { - AppleVTCompressor *tmpCompressor; - tmpCompressor = [[AppleVTCompressor alloc] init]; - tmpCompressor.average_bitrate = [[saveRoot valueForKey:@"captureVideoAverageBitrate"] intValue]; - tmpCompressor.max_bitrate = [[saveRoot valueForKey:@"captureVideoMaxBitrate"] intValue]; - tmpCompressor.keyframe_interval = [[saveRoot valueForKey:@"captureVideoMaxKeyframeInterval"] intValue]; - newCompressor = tmpCompressor; - } else { - newCompressor = nil; + self.compressors[@"x264"] = newCompressor; + [[NSNotificationCenter defaultCenter] postNotificationName:CSNotificationCompressorAdded object:newCompressor]; } - - if (newCompressor) + + if (!self.compressors[@"AppleVT"]) { - - newCompressor.width = [[saveRoot valueForKey:@"captureWidth"] intValue]; - newCompressor.height = [[saveRoot valueForKey:@"captureHeight"] intValue]; - if ([saveRoot valueForKey:@"resolutionOption"]) - { - newCompressor.resolutionOption = [saveRoot valueForKey:@"resolutionOption"]; - } - - - newCompressor.name = [@"default" mutableCopy]; - [self addCompressor:newCompressor]; + AppleVTCompressor *newCompressor = [[AppleVTCompressor alloc] init]; + newCompressor.name = @"AppleVT".mutableCopy; + newCompressor.average_bitrate = 1000; + newCompressor.max_bitrate = 1000; + newCompressor.keyframe_interval = 2; + self.compressors[@"AppleVT"] = newCompressor; + [[NSNotificationCenter defaultCenter] postNotificationName:CSNotificationCompressorAdded object:newCompressor]; + } + + if (!self.compressors[@"AppleProRes"]) + { + AppleProResCompressor *newCompressor = [[AppleProResCompressor alloc] init]; + newCompressor.name = @"AppleProRes".mutableCopy; + self.compressors[@"AppleProRes"] = newCompressor; + [[NSNotificationCenter defaultCenter] postNotificationName:CSNotificationCompressorAdded object:newCompressor]; + } + + if (!self.compressors[@"InstantRecorder"]) + { + CSIRCompressor *newCompressor = [[CSIRCompressor alloc] init]; + newCompressor.name = @"InstantRecorder".mutableCopy; + self.compressors[@"InstantRecorder"] = newCompressor; + [[NSNotificationCenter defaultCenter] postNotificationName:CSNotificationCompressorAdded object:newCompressor]; } } @@ -1510,27 +1465,22 @@ [saveRoot setValue: [NSNumber numberWithInt:self.audioBitrate] forKey:@"audioBitrate"]; [saveRoot setValue: [NSNumber numberWithInt:self.audioSamplerate] forKey:@"audioSamplerate"]; [saveRoot setValue: self.selectedVideoType forKey:@"selectedVideoType"]; - [saveRoot setValue: self.selectedAudioCapture.uniqueID forKey:@"audioCaptureID"]; [saveRoot setValue: self.captureDestinations forKey:@"captureDestinations"]; - [saveRoot setValue: self.selectedCompressorType forKey:@"selectedCompressorType"]; - [saveRoot setValue:[NSNumber numberWithFloat:self.audioCaptureSession.previewVolume] forKey:@"previewVolume"]; [saveRoot setValue:[NSNumber numberWithInt:self.maxOutputDropped] forKey:@"maxOutputDropped"]; [saveRoot setValue:[NSNumber numberWithInt:self.maxOutputPending] forKey:@"maxOutputPending"]; - [saveRoot setValue:self.resolutionOption forKey:@"resolutionOption"]; [saveRoot setValue:[NSNumber numberWithDouble:self.audio_adjust] forKey:@"audioAdjust"]; [saveRoot setValue: [NSNumber numberWithBool:self.useStatusColors] forKey:@"useStatusColors"]; [saveRoot setValue:self.compressors forKey:@"compressors"]; [saveRoot setValue:self.extraSaveData forKey:@"extraSaveData"]; - - [saveRoot setValue:[NSNumber numberWithBool:self.renderOnIntegratedGPU] forKey:@"renderOnIntegratedGPU"]; + [saveRoot setValue: [NSNumber numberWithBool:self.useInstantRecord] forKey:@"useInstantRecord"]; + + [saveRoot setValue:[NSNumber numberWithInt:self.instantRecordBufferDuration] forKey:@"instantRecordBufferDuration"]; + [saveRoot setValue:self.instantRecordDirectory forKey:@"instantRecordDirectory"]; - NSUInteger compressoridx = [self.compressController selectionIndex]; - [saveRoot setValue:[NSNumber numberWithUnsignedInteger:compressoridx] forKey:@"selectedCompressor"];\ - //[saveRoot setValue:self.sourceList forKeyPath:@"sourceList"]; [saveRoot setValue:self.selectedLayout forKey:@"selectedLayout"]; @@ -1565,6 +1515,7 @@ [self saveMIDI]; + [saveRoot setValue:self.inputLibrary forKey:@"inputLibrary"]; [NSKeyedArchiver archiveRootObject:saveRoot toFile:path]; } @@ -1574,6 +1525,9 @@ { //all color panels allow opacity + self.activePreviewView = self.stagingPreviewView; + [self.layoutCollectionView registerForDraggedTypes:@[@"CS_LAYOUT_DRAG"]]; + [NSColorPanel sharedColorPanel].showsAlpha = YES; [NSColor setIgnoresAlpha:NO]; @@ -1590,6 +1544,21 @@ [saveRoot addEntriesFromDictionary:savedValues]; + if (saveRoot[@"useInstantRecord"]) + { + self.useInstantRecord = [[saveRoot valueForKey:@"useInstantRecord"] boolValue]; + } + + self.instantRecordActive = YES; + + if (saveRoot[@"instantRecordBufferDuration"]) + { + self.instantRecordBufferDuration = [[saveRoot valueForKey:@"instantRecordBufferDuration"] intValue]; + } + + self.instantRecordDirectory = [saveRoot valueForKey:@"instantRecordDirectory"]; + + self.transitionName = [saveRoot valueForKey:@"transitionName"]; self.transitionDirection = [saveRoot valueForKey:@"transitionDirection"]; @@ -1611,14 +1580,8 @@ } - NSUInteger selectedCompressoridx = [[saveRoot valueForKey:@"selectedCompressor"] unsignedIntegerValue]; - if (self.compressors.count > 0) - { - [self.compressController setSelectionIndex:selectedCompressoridx]; - } - self.captureDestinations = [saveRoot valueForKey:@"captureDestinations"]; @@ -1647,14 +1610,9 @@ self.selectedVideoType = [saveRoot valueForKey:@"selectedVideoType"]; - self.selectedCompressorType = [saveRoot valueForKey:@"selectedCompressorType"]; - NSString *audioID = [saveRoot valueForKey:@"audioCaptureID"]; - - [self selectedAudioCaptureFromID:audioID]; - self.audioCaptureSession.previewVolume = [[saveRoot valueForKey:@"previewVolume"] floatValue]; self.captureFPS = [[saveRoot valueForKey:@"captureFPS"] doubleValue]; self.maxOutputDropped = [[saveRoot valueForKey:@"maxOutputDropped"] intValue]; @@ -1662,20 +1620,7 @@ self.audio_adjust = [[saveRoot valueForKey:@"audioAdjust"] doubleValue]; - self.resolutionOption = [saveRoot valueForKey:@"resolutionOption"]; - if (!self.resolutionOption) - { - self.resolutionOption = @"None"; - } - - self.renderOnIntegratedGPU = [[saveRoot valueForKey:@"renderOnIntegratedGPU"] boolValue]; - - [self createCGLContext]; - //mainThread = [[NSThread alloc] initWithTarget:self selector:@selector(newFrameTimed) object:nil]; - //[mainThread start]; - - self.stagingPreviewView.controller = self; self.livePreviewView.controller = self; LayoutRenderer *stagingRender = [[LayoutRenderer alloc] init]; @@ -1688,7 +1633,9 @@ self.livePreviewView.viewOnly = YES; - + self.selectedLayout = [[SourceLayout alloc] init]; + self.stagingLayout = [[SourceLayout alloc] init]; + self.extraPluginsSaveData = [saveRoot valueForKey:@"extraPluginsSaveData"]; [self migrateDefaultCompressor:saveRoot]; @@ -1710,38 +1657,122 @@ { self.multiAudioEngine = [[CAMultiAudioEngine alloc] init]; } + self.extraPluginsSaveData = nil; self.sourceLayouts = [saveRoot valueForKey:@"sourceLayouts"]; + + if (!self.sourceLayouts) + { + self.sourceLayouts = [[NSMutableArray alloc] init]; + } + + SourceLayout *tmpLayout = [saveRoot valueForKey:@"selectedLayout"]; + if (tmpLayout) + { + if (tmpLayout == self.stagingLayout || [self.sourceLayouts containsObject:tmpLayout]) + { + SourceLayout *tmpCopy = [tmpLayout copy]; + self.selectedLayout = tmpCopy; + } else { + self.selectedLayout = tmpLayout; + } + //[self.selectedLayout mergeSourceLayout:tmpLayout withLayer:nil]; + } + + tmpLayout = [saveRoot valueForKey:@"stagingLayout"]; + if (tmpLayout) + { + if (tmpLayout == self.selectedLayout || [self.sourceLayouts containsObject:tmpLayout]) + { + SourceLayout *tmpCopy = [tmpLayout copy]; + self.stagingLayout = tmpCopy; + } else { + self.stagingLayout = tmpLayout; + } + + //[self.stagingLayout mergeSourceLayout:tmpLayout withLayer:nil]; + } + + [self changePendingAnimations]; + self.inputLibrary = [saveRoot valueForKey:@"inputLibrary"]; + if (!self.inputLibrary) + { + self.inputLibrary = [NSMutableArray array]; + } + + _firstAudioTime = kCMTimeZero; + _previousAudioTime = kCMTimeZero; + + + + + + CSAacEncoder *audioEnc = [[CSAacEncoder alloc] init]; + audioEnc.encodedReceiver = self; + audioEnc.sampleRate = self.audioSamplerate; + audioEnc.bitRate = self.audioBitrate*1000; + + audioEnc.inputASBD = self.multiAudioEngine.graph.graphAsbd; + [audioEnc setupEncoderBuffer]; + self.multiAudioEngine.encoder = audioEnc; + + if (self.useInstantRecord) + { + [self setupInstantRecorder]; + } dispatch_async(_main_capture_queue, ^{[self newFrameTimed];}); dispatch_async(_preview_queue, ^{ [self newStagingFrameTimed]; }); - if (!self.sourceLayouts) - { - self.sourceLayouts = [[NSMutableArray alloc] init]; - SourceLayout *newLayout = [[SourceLayout alloc] init]; - newLayout.name = @"default"; - [[self mutableArrayValueForKey:@"sourceLayouts" ] addObject:newLayout]; - self.selectedLayout = newLayout; - self.stagingLayout = newLayout; - newLayout.isActive = YES; - - } else { - - self.selectedLayout = [saveRoot valueForKey:@"selectedLayout"]; - self.stagingLayout = [saveRoot valueForKey:@"stagingLayout"]; - } + + +} + +-(void)setInstantRecordBufferDuration:(int)instantRecordBufferDuration +{ + _instantRecordBufferDuration = instantRecordBufferDuration; + + if (_instantRecordBufferDuration <= 0) + { + self.instantRecorder = nil; + } else { + if (self.instantRecorder) + { + self.instantRecorder.bufferDuration = _instantRecordBufferDuration; + } + } +} + +-(int)instantRecordBufferDuration +{ + return _instantRecordBufferDuration; } +-(void) setUseInstantRecord:(bool)useInstantRecord +{ + _useInstantRecord = useInstantRecord; + + if (useInstantRecord) + { + [self setupInstantRecorder]; + self.instantRecordActive = YES; + } else { + self.instantRecorder = nil; + self.instantRecordActive = NO; + } +} - +-(bool)useInstantRecord +{ + return _useInstantRecord; +} -(void)controlTextDidEndEditing:(NSNotification *)obj @@ -1764,50 +1795,43 @@ -(void)setStagingLayout:(SourceLayout *)stagingLayout { - [self.objectController commitEditing]; - - - self.stagingCtx.layoutRenderer.transitionName = self.transitionName; - self.stagingCtx.layoutRenderer.transitionDirection = self.transitionDirection; - self.stagingCtx.layoutRenderer.transitionDuration = self.transitionDuration; - self.stagingCtx.layoutRenderer.transitionFilter = self.transitionFilter; - [self stagingSave:self]; - - - SourceLayout *previewCopy = stagingLayout.copy; - - [previewCopy restoreSourceList:nil]; - self.stagingPreviewView.sourceLayout = previewCopy; + [stagingLayout restoreSourceList:nil]; + [stagingLayout setupMIDI]; + self.stagingPreviewView.sourceLayout = stagingLayout; + self.stagingPreviewView.midiActive = YES; - - - if (self.sourceLayoutsArrayController) - { - NSUInteger sidx = [self.sourceLayoutsArrayController.arrangedObjects indexOfObject:stagingLayout]; - if ((sidx != NSNotFound) && self.stagingSourceLayoutTableView) - { - [self.stagingSourceLayoutTableView selectRowIndexes:[NSIndexSet indexSetWithIndex:sidx] byExtendingSelection:NO]; - - - } - } + [stagingLayout setAddLayoutBlock:^(SourceLayout *layout) { - float framerate = stagingLayout.frameRate; + layout.in_staging = YES; - if (framerate && framerate > 0) - { - _staging_frame_interval = (1.0/framerate); - } else { - _staging_frame_interval = 1.0/60.0; - } - - self.currentMidiInputStagingIdx = 0; - - _stagingLayout = stagingLayout; + }]; + + [stagingLayout setRemoveLayoutBlock:^(SourceLayout *layout) { + + layout.in_staging = NO; + + }]; + + + [stagingLayout applyAddBlock]; + + float framerate = stagingLayout.frameRate; + + if (framerate && framerate > 0) + { + _staging_frame_interval = (1.0/framerate); + } else { + _staging_frame_interval = 1.0/60.0; + } + + self.currentMidiInputStagingIdx = 0; + + _stagingLayout = stagingLayout; + stagingLayout.doSaveSourceList = YES; + - } @@ -1821,45 +1845,41 @@ -(void)setSelectedLayout:(SourceLayout *)selectedLayout { - if (selectedLayout == _selectedLayout) - { - return; - } + + [selectedLayout setAddLayoutBlock:^(SourceLayout *layout) { + + layout.in_live = YES; + + }]; + [selectedLayout setRemoveLayoutBlock:^(SourceLayout *layout) { + + layout.in_live = NO; + + }]; + + [selectedLayout applyAddBlock]; + [self.objectController commitEditing]; - - - self.previewCtx.layoutRenderer.transitionName = self.transitionName; - self.previewCtx.layoutRenderer.transitionDirection = self.transitionDirection; - self.previewCtx.layoutRenderer.transitionDuration = self.transitionDuration; - self.previewCtx.layoutRenderer.transitionFilter = self.transitionFilter; - - - - selectedLayout.isActive = YES; - - [self setupFrameTimer:selectedLayout.frameRate]; - self.previewCtx.sourceLayout = selectedLayout; - - - if (self.sourceLayoutsArrayController) - { - NSUInteger sidx = [self.sourceLayoutsArrayController.arrangedObjects indexOfObject:selectedLayout]; - if ((sidx != NSNotFound) && self.mainSourceLayoutTableView) - { - [self.mainSourceLayoutTableView selectRowIndexes:[NSIndexSet indexSetWithIndex:sidx] byExtendingSelection:NO]; - - - } - } - - - self.currentMidiInputLiveIdx = 0; - _selectedLayout = selectedLayout; - + + selectedLayout.isActive = YES; + [selectedLayout restoreSourceList:nil]; + + [selectedLayout setupMIDI]; + + [self setupFrameTimer:selectedLayout.frameRate]; + self.livePreviewView.sourceLayout = selectedLayout; + self.livePreviewView.midiActive = NO; + + + + self.currentMidiInputLiveIdx = 0; + _selectedLayout = selectedLayout; + selectedLayout.doSaveSourceList = YES; + } @@ -1871,6 +1891,8 @@ -(void) setTransitionName:(NSString *)transitionName { + + _transitionName = transitionName; if ([transitionName hasPrefix:@"CI"]) { @@ -1896,30 +1918,6 @@ } - -- (IBAction)addStreamingService:(id)sender { - - - OutputDestination *newDest; - - [self.streamServicePluginViewController commitEditing]; - - NSString *destination = [self.streamServiceObject getServiceDestination]; - - newDest = [[OutputDestination alloc] initWithType:[self.streamServiceObject.class label]]; - newDest.destination = destination; - newDest.settingsController = self; - - [self insertObject:newDest inCaptureDestinationsAtIndex:self.captureDestinations.count]; - [self closeCreateSheet:nil]; - - -} - - - - - - (void) outputEncodedData:(CapturedFrameData *)newFrameData { @@ -1948,55 +1946,12 @@ { - id tmpCompressor; - - for (id cKey in self.compressors) - { - id tmpcomp = self.compressors[cKey]; - tmpcomp.settingsController = self; - } - - - if (!self.selectedCompressor) - { - - if (self.compressController.selectedObjects.count > 0) - { - tmpCompressor = [[self.compressController.selectedObjects objectAtIndex:0] valueForKey:@"value"]; - if (tmpCompressor) - { - self.selectedCompressor = self.compressors[tmpCompressor.name]; - } - - } - } - - - if (self.selectedCompressor) - { - - self.selectedCompressor.settingsController = self; - } for (OutputDestination *outdest in _captureDestinations) { //make the outputs pick up the default selected compressor [outdest setupCompressor]; } - - - - [self.audioCaptureSession setupAudioCompression]; - - _frameCount = 0; - _firstAudioTime = kCMTimeZero; - _firstFrameTime = 0; - - _compressedFrameCount = 0; - _min_delay = _max_delay = _avg_delay = 0; - - //self.videoCompressor = self.selectedCompressor; - return YES; @@ -2007,21 +1962,9 @@ { - if (_cmdLineInfo) - { - printf("%s", [[self buildCmdLineInfo] UTF8String]); - [NSApp performSelector:@selector(terminate:) withObject:nil afterDelay:0.0]; - return YES; - } - - _frameCount = 0; - _firstAudioTime = kCMTimeZero; - _firstFrameTime = 0; - - _compressedFrameCount = 0; - _min_delay = _max_delay = _avg_delay = 0; - - + //_frameCount = 0; + //_firstAudioTime = kCMTimeZero; + //_firstFrameTime = [self mach_time_seconds]; if (floor(NSAppKitVersionNumber) <= NSAppKitVersionNumber10_8) { @@ -2038,12 +1981,6 @@ } - CSAacEncoder *audioEnc = [[CSAacEncoder alloc] init]; - audioEnc.encodedReceiver = self; - audioEnc.sampleRate = self.audioSamplerate; - audioEnc.bitRate = self.audioBitrate*1000; - - self.multiAudioEngine.encoder = audioEnc; self.captureRunning = YES; @@ -2056,6 +1993,7 @@ + -(void) setupFrameTimer:(double)framerate { if (self.captureRunning) @@ -2078,189 +2016,21 @@ --(NSString *) buildCmdLineInfo -{ - - NSMutableDictionary *infoDict = [[NSMutableDictionary alloc] init]; - NSMutableArray *audioArray = [[NSMutableArray alloc] init]; - - - for (AVCaptureDevice *audioDev in self.audioCaptureDevices) - { - [audioArray addObject:@{@"name": audioDev.localizedName, @"uniqueID": audioDev.uniqueID}]; - } - - - - [infoDict setValue:audioArray forKey:@"audioDevices"]; - - - NSMutableDictionary *x264dict = [[NSMutableDictionary alloc] init]; - - [x264dict setValue:self.x264presets forKey:@"presets"]; - [x264dict setValue:self.x264tunes forKey:@"tunes"]; - [x264dict setValue:self.x264profiles forKey:@"profiles"]; - - - - [infoDict setValue:x264dict forKey:@"x264"]; - - - - NSData *jsonData = [NSJSONSerialization dataWithJSONObject:infoDict options:0 error:nil]; - - return [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; - -} - - --(id ) buildCmdlineCompressor:(NSUserDefaults *)cmdargs -{ - - id newCompressor; - if ([self.selectedCompressorType isEqualToString:@"x264"]) - { - - x264Compressor *tmpCompressor; - - tmpCompressor = [[x264Compressor alloc] init]; - tmpCompressor.tune = [cmdargs stringForKey:@"x264tune"]; - tmpCompressor.profile = [cmdargs stringForKey:@"x264profile"]; - tmpCompressor.preset = [cmdargs stringForKey:@"x264preset"]; - tmpCompressor.use_cbr = [cmdargs boolForKey:@"videoCBR"]; - tmpCompressor.crf = (int)[cmdargs integerForKey:@"x264crf"]; - tmpCompressor.vbv_maxrate = (int)[cmdargs integerForKey:@"captureVideoAverageBitrate"]; - tmpCompressor.vbv_buffer = (int)[cmdargs integerForKey:@"captureVideoMaxBitrate"]; - tmpCompressor.keyframe_interval = (int)[cmdargs integerForKey:@"captureVideoMaxKeyframeInterval"]; - newCompressor = tmpCompressor; - } else if ([self.selectedCompressorType isEqualToString:@"AppleVTCompressor"]) { - AppleVTCompressor *tmpCompressor; - tmpCompressor = [[AppleVTCompressor alloc] init]; - tmpCompressor.average_bitrate = (int)[cmdargs integerForKey:@"captureVideoAverageBitrate"]; - tmpCompressor.max_bitrate = (int)[cmdargs integerForKey:@"captureVideoMaxBitrate"]; - tmpCompressor.keyframe_interval = (int)[cmdargs integerForKey:@"captureVideoMaxKeyframeInterval"]; - newCompressor = tmpCompressor; - } else { - newCompressor = nil; - } - - return newCompressor; -} - --(void) loadCmdlineSettings:(NSUserDefaults *)cmdargs -{ - - - if ([cmdargs objectForKey:@"dumpInfo"]) - { - _cmdLineInfo = YES; - } else { - _cmdLineInfo = NO; - } - - - if ([cmdargs objectForKey:@"captureWidth"]) - { - self.captureWidth = (int)[cmdargs integerForKey:@"captureWidth"]; - } - - if ([cmdargs objectForKey:@"captureHeight"]) - { - self.captureHeight = (int)[cmdargs integerForKey:@"captureHeight"]; - } - - if ([cmdargs objectForKey:@"audioBitrate"]) - { - self.audioBitrate = (int)[cmdargs integerForKey:@"audioBitrate"]; - } - - if ([cmdargs objectForKey:@"audioSamplerate"]) - { - self.audioSamplerate = (int)[cmdargs integerForKey:@"audioSamplerate"]; - } - - if ([cmdargs objectForKey:@"selectedVideoType"]) - { - self.selectedVideoType = [cmdargs stringForKey:@"selectedVideoType"]; - } - - if ([cmdargs objectForKey:@"selectedCompressorType"]) - { - self.selectedCompressorType = [cmdargs stringForKey:@"selectedCompressorType"]; - } - - - /* - if ([cmdargs objectForKey:@"videoCaptureID"]) - { - NSString *videoID = [cmdargs stringForKey:@"videoCaptureID"]; - [self selectedVideoCaptureFromID:videoID]; - } - - */ - - if ([cmdargs objectForKey:@"audioCaptureID"]) - { - NSString *audioID = [cmdargs stringForKey:@"audioCaptureID"]; - [self selectedAudioCaptureFromID:audioID]; - } - - if ([cmdargs objectForKey:@"captureFPS"]) - { - self.captureFPS = [cmdargs doubleForKey:@"captureFPS"]; - } - - if ([cmdargs objectForKey:@"outputDestinations"]) - { - - if (!self.captureDestinations) - { - self.captureDestinations = [[NSMutableArray alloc] init]; - } - - NSArray *outputs = [cmdargs arrayForKey:@"outputDestinations"]; - for (NSString *outstr in outputs) - { - OutputDestination *newDest = [[OutputDestination alloc] initWithType:@"file"]; - - newDest.active = YES; - newDest.destination = outstr; - newDest.settingsController = self; - [[self mutableArrayValueForKey:@"captureDestinations"] addObject:newDest]; - } - - } - - if ([cmdargs objectForKey:@"compressor"]) - { - NSString *forName = [cmdargs stringForKey:@"compressor"]; - - for (id tmpval in self.compressController.arrangedObjects) - { - if ([[tmpval valueForKey:@"key"] isEqualToString:forName] ) - { - self.selectedCompressor = tmpval; - break; - } - } - - } else { - self.selectedCompressor = [self buildCmdlineCompressor:cmdargs]; - } -} - - (void)stopStream { - self.videoCompressor = nil; - self.selectedCompressor = nil; self.captureRunning = NO; for (id cKey in self.compressors) { - id ctmp = self.compressors[cKey]; + id ctmp = self.compressors[cKey]; + if (ctmp && self.instantRecorder && [self.instantRecorder.compressor isEqual:ctmp]) + { + continue; + } + if (ctmp) { [ctmp reset]; @@ -2287,8 +2057,14 @@ [[NSProcessInfo processInfo] endActivity:_activity_token]; } - //[self.audioCaptureSession stopAudioCompression]; - self.multiAudioEngine.encoder = nil; + if (_needsIRReset) + { + [self resetInstantRecorder]; + _needsIRReset = NO; + } + + + //self.multiAudioEngine.encoder = nil; [[NSNotificationCenter defaultCenter] postNotificationName:CSNotificationStreamStopped object:self userInfo:nil]; @@ -2311,31 +2087,69 @@ - if ([self startStream] == YES) + if ([self startStream] != YES) { - self.selectedTabIndex = 4; - } else { [sender setNextState]; } } else { - self.selectedTabIndex = 0; [self stopStream]; } } +-(void) addAudioData:(CMSampleBufferRef)audioData +{ + + @synchronized(self) + { + + [_audioBuffer addObject:(__bridge id)audioData]; + } +} + + +-(void) setAudioData:(NSMutableArray *)audioDestination videoPTS:(CMTime)videoPTS +{ + + NSUInteger audioConsumed = 0; + @synchronized(self) + { + NSUInteger audioBufferSize = [_audioBuffer count]; + + for (int i = 0; i < audioBufferSize; i++) + { + CMSampleBufferRef audioData = (__bridge CMSampleBufferRef)[_audioBuffer objectAtIndex:i]; + + CMTime audioTime = CMSampleBufferGetOutputPresentationTimeStamp(audioData); + + + + + if (CMTIME_COMPARE_INLINE(audioTime, <=, videoPTS)) + { + + audioConsumed++; + [audioDestination addObject:(__bridge id)audioData]; + } else { + break; + } + } + + if (audioConsumed > 0) + { + [_audioBuffer removeObjectsInRange:NSMakeRange(0, audioConsumed)]; + } + + } +} + - (void)captureOutputAudio:(id)fromDevice didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer { - if (!self.captureRunning) - { - return; - } - CMTime orig_pts = CMSampleBufferGetPresentationTimeStamp(sampleBuffer); @@ -2343,26 +2157,26 @@ if (CMTIME_COMPARE_INLINE(_firstAudioTime, ==, kCMTimeZero)) { + + //NSLog(@"FIRST AUDIO AT %f", CFAbsoluteTimeGetCurrent()); _firstAudioTime = orig_pts; return; } + CMTime real_pts = CMTimeSubtract(orig_pts, _firstAudioTime); CMTime adjust_pts = CMTimeMakeWithSeconds(self.audio_adjust, orig_pts.timescale); CMTime pts = CMTimeAdd(real_pts, adjust_pts); - + CMSampleBufferSetOutputPresentationTimeStamp(sampleBuffer, pts); - for(id cKey in self.compressors) + if (CMTIME_COMPARE_INLINE(pts, >, _previousAudioTime)) { - - id compressor; - compressor = self.compressors[cKey]; - [compressor addAudioData:sampleBuffer]; - + [self addAudioData:sampleBuffer]; + _previousAudioTime = pts; } } @@ -2372,7 +2186,6 @@ -(double)mach_time_seconds { double retval; - uint64_t mach_now = mach_absolute_time(); retval = (double)((mach_now * _mach_timebase.numer / _mach_timebase.denom))/NSEC_PER_SEC; return retval; @@ -2398,21 +2211,6 @@ } } - /* - double mach_duration = target_time - mach_now; - double mach_wait_time = mach_now + mach_duration/2.0; - - mach_wait_until(mach_wait_time*NSEC_PER_SEC); - - - while ([self mach_time_seconds] < target_time) - { - usleep(500); - - //wheeeeeeeeeeeee - } - - */ return YES; } @@ -2506,16 +2304,12 @@ -(void)newStagingFrame { - if (_stagingHidden) + if (self.stagingHidden) { return; } [self.stagingCtx.layoutRenderer currentImg]; - /* - dispatch_async(dispatch_get_main_queue(), ^{ - [self.stagingCtx setNeedsDisplay:YES]; - });*/ } @@ -2534,7 +2328,7 @@ while (1) { - if (_stagingHidden) + if (self.stagingHidden) { return; } @@ -2559,17 +2353,252 @@ } } +-(void)updateFrameIntervals +{ + _staging_frame_interval = 1.0/self.stagingPreviewView.sourceLayout.frameRate; + [self setupFrameTimer:self.livePreviewView.sourceLayout.frameRate]; +} + +- (IBAction)configureIRCompressor:(id)sender { + + + CompressionSettingsPanelController *cPanel = [[CompressionSettingsPanelController alloc] init]; + CSIRCompressor *compressor = self.compressors[@"InstantRecorder"]; + + cPanel.compressor = compressor; + + + [self.advancedPrefPanel beginSheet:cPanel.window completionHandler:^(NSModalResponse returnCode) { + switch (returnCode) { + case NSModalResponseStop: + if (cPanel.compressor.active) + { + return; + } + [self willChangeValueForKey:@"compressors"]; + [self.compressors removeObjectForKey:cPanel.compressor.name]; + [self didChangeValueForKey:@"compressors"]; + [[NSNotificationCenter defaultCenter] postNotificationName:CSNotificationCompressorDeleted object:cPanel.compressor userInfo:nil]; + + break; + case NSModalResponseOK: + { + + + if (!cPanel.compressor.active) + { + if (![compressor.name isEqualToString:cPanel.compressor.name]) + { + [self.compressors removeObjectForKey:compressor.name]; + NSDictionary *notifyMsg = [NSDictionary dictionaryWithObjectsAndKeys:compressor.name, @"oldName", cPanel.compressor, @"compressor", nil]; + [[NSNotificationCenter defaultCenter] postNotificationName:CSNotificationCompressorRenamed object:notifyMsg]; + + } + self.compressors[cPanel.compressor.name] = cPanel.compressor; + } + [[NSNotificationCenter defaultCenter] postNotificationName:CSNotificationCompressorReconfigured object:cPanel.compressor]; + + break; + } + case 4242: + if (cPanel.saveProfileName) + { + cPanel.compressor.name = cPanel.saveProfileName.mutableCopy; + [self willChangeValueForKey:@"compressors"]; + self.compressors[cPanel.compressor.name] = cPanel.compressor; + [self didChangeValueForKey:@"compressors"]; + [[NSNotificationCenter defaultCenter] postNotificationName:CSNotificationCompressorAdded object:cPanel.compressor userInfo:nil]; + + } + default: + break; + } + + + }]; +} + +-(void) resetInputTableHighlights +{ + [self.activePreviewView stopHighlightingAllSources]; + if (self.inputOutlineView && self.inputOutlineView.selectedRowIndexes) + { + [self.inputOutlineView.selectedRowIndexes enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL * _Nonnull stop) { + NSTreeNode *node = [self.inputOutlineView itemAtRow:idx]; + InputSource *src = node.representedObject; + + if (src) + { + [self.activePreviewView highlightSource:src]; + } + }]; + } +} + +- (IBAction)removePendingAnimations:(id)sender +{ + if (!self.stagingHidden) + { + [self.activePreviewView.sourceLayout clearAnimations]; + [self changePendingAnimations]; + } +} + + + +- (IBAction)outputSegmentedAction:(NSButton *)sender +{ + NSUInteger clicked = sender.tag; + + switch (clicked) + { + case 0: + [self openAddOutputPopover:sender sourceRect:sender.bounds]; + break; + case 1: + [self removeDestination:sender]; + break; + default: + break; + } +} + + +- (IBAction)openStreamOutputWindow:(id)sender +{ + if (!_streamOutputWindowController) + { + _streamOutputWindowController = [[CSStreamOutputWindowController alloc] init]; + } + + _streamOutputWindowController.controller = self; + + [_streamOutputWindowController showWindow:nil]; +} + + + +- (IBAction)openAnimationWindow:(id)sender +{ + if (!_animationWindowController) + { + _animationWindowController = [[CSAnimationWindowController alloc] init]; + } + + [_animationWindowController showWindow:nil]; +} + + +- (IBAction)openAdvancedAudio:(id)sender +{ + if (!_audioWindowController) + { + _audioWindowController = [[CSAdvancedAudioWindowController alloc] init]; + } + + _audioWindowController.controller = self; + [_audioWindowController showWindow:nil]; + +} + + + +-(void)outlineView:(NSOutlineView *)outlineView didAddRowView:(NSTableRowView *)rowView forRow:(NSInteger)row +{ + if (outlineView == self.inputOutlineView) + { + + NSTreeNode *node = [outlineView itemAtRow:row]; + InputSource *src = node.representedObject; + if (!src.parentInput) + { + dispatch_async(dispatch_get_main_queue(), ^{ + [outlineView expandItem:nil expandChildren:YES]; + }); + } + } +} + + +-(void) outlineViewSelectionDidChange:(NSNotification *)notification +{ + NSOutlineView *outline = notification.object; + + if (outline == self.inputOutlineView) + { + [self resetInputTableHighlights]; + + } + + +} + + +- (IBAction)inputTableControlClick:(NSButton *)sender +{ + NSInteger clicked = sender.tag; + + NSArray *selectedInputs; + NSRect sbounds; + switch (clicked) { + case 0: + sbounds = sender.bounds; + //[self.activePreviewView addInputSource:sender]; + //sbounds.origin.x = NSMaxX(sender.frame) - [sender widthForSegment:0]; + //sbounds.origin.x -= 333; + [self openAddInputPopover:sender sourceRect:sbounds]; + break; + case 1: + if (self.inputOutlineView && self.inputOutlineView.selectedRowIndexes) + { + [self.inputOutlineView.selectedRowIndexes enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL * _Nonnull stop) { + NSTreeNode *node = [self.inputOutlineView itemAtRow:idx]; + InputSource *src = node.representedObject; + + if (src) + { + NSString *uuid = src.uuid; + InputSource *realInput = [self.activePreviewView.sourceLayout inputForUUID:uuid]; + [self.activePreviewView deleteInput:realInput]; + } + + }]; + + + } + break; + case 2: + if (self.inputOutlineView && self.inputOutlineView.selectedRowIndexes) + { + + [self.inputOutlineView.selectedRowIndexes enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL * _Nonnull stop) { + NSTreeNode *node = [self.inputOutlineView itemAtRow:idx]; + InputSource *src = node.representedObject; + + if (src) + { + [self.activePreviewView openInputConfigWindow:src.uuid]; + } + + }]; + } + break; + default: + break; + } +} + + -(void) newFrameTimed { - double startTime; startTime = [self mach_time_seconds]; - double lastLoopTime = startTime; _frame_time = startTime; + _firstFrameTime = startTime; [self newFrame]; //[self setFrameThreadPriority]; @@ -2591,12 +2620,11 @@ //_frame_time = nowTime;//startTime; - double nowTime = [self mach_time_seconds]; - lastLoopTime = nowTime; if (![self sleepUntil:(startTime += _frame_interval)]) { + //NSLog(@"MISSED FRAME!"); continue; } @@ -2621,15 +2649,6 @@ } -/* --(NSArray *)sourceListOrdered -{ - NSArray *listCopy = [self.selectedLayout.sourceList sortedArrayUsingDescriptors:@[_sourceDepthSorter, _sourceUUIDSorter]]; - return listCopy; -} - */ - - -(InputSource *)findSource:(NSPoint)forPoint { @@ -2646,119 +2665,134 @@ -(void) newFrame { - - CVPixelBufferRef newFrame; - //if (self.videoCaptureSession) + CVPixelBufferRef newFrame; + + + double nfstart = [self mach_time_seconds]; + + newFrame = [self.previewCtx.layoutRenderer currentImg]; + + + double nfdone = [self mach_time_seconds]; + double nftime = nfdone - nfstart; + _renderedFrames++; + + _render_time_total += nftime; + if (nftime < _min_render_time || _min_render_time == 0.0f) + { + _min_render_time = nftime; + } + + if (nftime > _max_render_time) + { + _max_render_time = nftime; + } + + + + if (newFrame) + { + _frameCount++; + CVPixelBufferRetain(newFrame); + NSMutableArray *frameAudio = [[NSMutableArray alloc] init]; + [self setAudioData:frameAudio videoPTS:CMTimeMake((_frame_time - _firstFrameTime)*1000, 1000)]; + CapturedFrameData *newData = [self createFrameData]; + newData.audioSamples = frameAudio; + newData.videoFrame = newFrame; + + [self sendFrameToReplay:newData]; + if (self.captureRunning) { - - double nfstart = [self mach_time_seconds]; - - //[CATransaction begin]; - newFrame = [self.previewCtx.layoutRenderer currentImg]; - //[CATransaction commit]; - //newFrame = [self currentFrame]; - - - double nfdone = [self mach_time_seconds]; - double nftime = nfdone - nfstart; - _renderedFrames++; - - _render_time_total += nftime; - if (nftime < _min_render_time || _min_render_time == 0.0f) + if (self.captureRunning != _last_running_value) { - _min_render_time = nftime; + [self setupCompressors]; } - if (nftime > _max_render_time) - { - _max_render_time = nftime; - } - + + [self processVideoFrame:newData]; - if (newFrame) + } else { + + for (OutputDestination *outdest in _captureDestinations) { - CVPixelBufferRetain(newFrame); - if (self.captureRunning) - { - if (self.captureRunning != _last_running_value) - { - [self setupCompressors]; - } - - - [self processVideoFrame:newFrame]; - - - } else { - - for (OutputDestination *outdest in _captureDestinations) - { - [outdest writeEncodedData:nil]; - } - - } - - _last_running_value = self.captureRunning; - - CVPixelBufferRelease(newFrame); - - + [outdest writeEncodedData:nil]; } + } + + _last_running_value = self.captureRunning; + + CVPixelBufferRelease(newFrame); + + + } } - --(void)processVideoFrame:(CVPixelBufferRef)videoFrame +-(CapturedFrameData *)createFrameData { - - //CVImageBufferRef imageBuffer = frameData.videoFrame; - - if (!self.captureRunning) - { - //CVPixelBufferRelease(imageBuffer); + CMTime pts = CMTimeMake((_frame_time - _firstFrameTime)*1000, 1000); + CMTime duration = CMTimeMake(1, self.captureFPS); - return; - } + CapturedFrameData *newFrameData = [[CapturedFrameData alloc] init]; + newFrameData.videoPTS = pts; + newFrameData.videoDuration = duration; + newFrameData.frameNumber = _frameCount; + newFrameData.frameTime = _frame_time; + return newFrameData; +} + + +-(void)sendFrameToReplay:(CapturedFrameData *)frameData +{ CMTime pts; CMTime duration; + pts = CMTimeMake((_frame_time - _firstFrameTime)*1000, 1000); + + duration = CMTimeMake(1, self.captureFPS); - if (_firstFrameTime == 0) + + if (self.instantRecorder && self.instantRecorder.compressor && !self.instantRecorder.compressor.errored) { - _firstFrameTime = _frame_time; - + CapturedFrameData *newFrameData = frameData.copy; + [self.instantRecorder.compressor compressFrame:newFrameData]; + if (self.instantRecorder.compressor.errored) + { + self.instantRecordActive = NO; + } } - - CFAbsoluteTime ptsTime = _frame_time - _firstFrameTime; - - - - - _frameCount++; - _lastFrameTime = _frame_time; - - - pts = CMTimeMake(ptsTime*1000000, 1000000); +} - duration = CMTimeMake(1000, self.captureFPS*1000); + +-(void)processVideoFrame:(CapturedFrameData *)frameData +{ + + + + if (!self.captureRunning) + { + + return; + } for(id cKey in self.compressors) { - CapturedFrameData *newFrameData = [[CapturedFrameData alloc] init]; - newFrameData.videoPTS = pts; - newFrameData.videoDuration = duration; - newFrameData.frameNumber = _frameCount; - newFrameData.frameTime = _frame_time; - newFrameData.videoFrame = videoFrame; - - id compressor; + id compressor; compressor = self.compressors[cKey]; + + if (self.instantRecorder && [self.instantRecorder.compressor isEqual:compressor]) + { + continue; + } + + CapturedFrameData *newFrameData = frameData.copy; + [compressor compressFrame:newFrameData]; } @@ -2843,7 +2877,7 @@ { NSUInteger key_idx = [@[@"captureWidth", @"captureHeight", @"captureFPS", - @"captureVideoAverageBitrate", @"audioBitrate", @"audioSamplerate", @"captureVideoMaxBitrate", @"captureVideoMaxKeyframeInterval"] indexOfObject:key]; + @"audioBitrate", @"audioSamplerate"] indexOfObject:key]; if (key_idx != NSNotFound) { @@ -2871,6 +2905,12 @@ [[NSNotificationCenter defaultCenter] postNotificationName:CSNotificationOutputDeleted object:to_delete userInfo:nil]; } +-(void)replaceObjectInCaptureDestinationsAtIndex:(NSUInteger)index withObject:(id)object +{ + [self.captureDestinations replaceObjectAtIndex:index withObject:object]; +} + + -(void)insertObject:(OutputDestination *)object inCaptureDestinationsAtIndex:(NSUInteger)index { [self.captureDestinations insertObject:object atIndex:index]; @@ -2894,6 +2934,17 @@ } +-(void) insertObject:(CSInputLibraryItem *)item inInputLibraryAtIndex:(NSUInteger)index +{ + [self.inputLibrary insertObject:item atIndex:index]; +} + +-(void)removeObjectFromInputLibraryAtIndex:(NSUInteger)index +{ + [self.inputLibrary removeObjectAtIndex:index]; +} + + -(NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender { @@ -2922,6 +2973,84 @@ +-(NSDragOperation)collectionView:(NSCollectionView *)collectionView validateDrop:(id)draggingInfo proposedIndex:(NSInteger *)proposedDropIndex dropOperation:(NSCollectionViewDropOperation *)proposedDropOperation +{ + + NSPasteboard *pBoard = [draggingInfo draggingPasteboard]; + NSData *indexSave = [pBoard dataForType:@"CS_LAYOUT_DRAG"]; + NSIndexSet *indexes = [NSKeyedUnarchiver unarchiveObjectWithData:indexSave]; + NSInteger draggedItemIdx = [indexes firstIndex]; + + NSInteger useIdx = *proposedDropIndex; + + if (*proposedDropIndex > draggedItemIdx) + { + useIdx--; + } + + + if (useIdx < 0) + { + useIdx = 0; + } + + + + if (*proposedDropIndex == -1 || labs(draggedItemIdx - useIdx) < 1) + { + return NSDragOperationNone; + } + + return NSDragOperationMove; +} + + +-(BOOL)collectionView:(NSCollectionView *)collectionView writeItemsAtIndexes:(NSIndexSet *)indexes toPasteboard:(NSPasteboard *)pasteboard +{ + NSData *indexSave = [NSKeyedArchiver archivedDataWithRootObject:indexes]; + [pasteboard declareTypes:@[@"CS_LAYOUT_DRAG"] owner:nil]; + [pasteboard setData:indexSave forType:@"CS_LAYOUT_DRAG"]; + return YES; +} + + +-(BOOL)collectionView:(NSCollectionView *)collectionView acceptDrop:(id)draggingInfo index:(NSInteger)index dropOperation:(NSCollectionViewDropOperation)dropOperation +{ + NSPasteboard *pBoard = [draggingInfo draggingPasteboard]; + NSData *indexSave = [pBoard dataForType:@"CS_LAYOUT_DRAG"]; + NSIndexSet *indexes = [NSKeyedUnarchiver unarchiveObjectWithData:indexSave]; + NSInteger draggedItemIdx = [indexes firstIndex]; + + + [self willChangeValueForKey:@"sourceLayouts"]; + SourceLayout *draggedItem = [self.sourceLayouts objectAtIndex:draggedItemIdx]; + NSInteger useIdx = index; + + if (index > draggedItemIdx) + { + useIdx--; + } + + + if (useIdx < 0) + { + useIdx = 0; + } + + [self.sourceLayouts removeObjectAtIndex:draggedItemIdx]; + [self.sourceLayouts insertObject:draggedItem atIndex:useIdx]; + [self didChangeValueForKey:@"sourceLayouts"]; + + return YES; +} + + +-(BOOL)collectionView:(NSCollectionView *)collectionView canDragItemsAtIndexes:(NSIndexSet *)indexes withEvent:(NSEvent *)event +{ + return YES; +} + + -(SourceLayout *)getLayoutForName:(NSString *)name { NSUInteger selectedIdx = [self.sourceLayouts indexOfObjectPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) { @@ -2941,47 +3070,63 @@ } -- (IBAction)layoutTableSelected:(NSTableView *)sender +-(void)switchToLayout:(SourceLayout *)layout { + SourceLayout *activeLayout = self.activePreviewView.sourceLayout; + [activeLayout replaceWithSourceLayout:layout]; +} + + +-(void)changePendingAnimations +{ + NSInteger finalCount = 0; - NSInteger selectedRow = [sender selectedRow]; - - NSInteger selectedCount = sender.numberOfSelectedRows; - - if (selectedRow != -1) + if (self.stagingHidden) { - SourceLayout *selected = [self.sourceLayoutsArrayController.arrangedObjects objectAtIndex:selectedRow]; - if (selected) + finalCount = 0; + } else { + + for (CSAnimationItem *anim in self.stagingLayout.animationList) { - if (sender == self.mainSourceLayoutTableView) + if (![self.selectedLayout animationForUUID:anim.uuid] && anim.onLive) { - if (selectedCount > 1) - { - if (!self.livePreviewView.viewOnly) - { - [self.selectedLayout mergeSourceListData:selected.savedSourceListData withLayer:nil]; - } - } else { - self.selectedLayout = selected; - } - } else { - if (selectedCount > 1) - { - [self.stagingPreviewView.sourceLayout mergeSourceListData:selected.savedSourceListData withLayer:nil]; - } else { - self.stagingLayout = selected; - } - } - if (selectedCount > 1) - { - [sender deselectRow:selectedRow]; + finalCount++; } } + } + self.pendingAnimations = finalCount; + self.pendingAnimationString = [NSString stringWithFormat:@"%ld pending animations", (long)self.pendingAnimations]; + } +-(void)toggleLayout:(SourceLayout *)layout +{ + SourceLayout *activeLayout = self.activePreviewView.sourceLayout; + [self applyTransitionSettings:activeLayout]; + + if ([activeLayout containsLayout:layout]) + { + [activeLayout removeSourceLayout:layout withLayer:nil]; + } else { + [activeLayout mergeSourceLayout:layout withLayer:nil]; + } + + [self changePendingAnimations]; +} + + +-(void)saveToLayout:(SourceLayout *)layout +{ + [self.activePreviewView.sourceLayout saveSourceList]; + layout.savedSourceListData = self.activePreviewView.sourceLayout.savedSourceListData; +} + + + + -(void)clearLearnedMidiForCommand:(NSString *)command withResponder:(id)responder { for(CSMidiWrapper *cwrap in self.midiMapGenerators) @@ -3035,16 +3180,20 @@ - (NSArray *)commandIdentifiers { - NSArray *baseIdentifiers = @[@"GoLive", @"StagingRevert", @"LiveRevert", @"InputNext", @"InputPrevious", @"ActivateLive", @"ActivateStaging", @"ActivateToggle"]; + NSArray *baseIdentifiers = @[@"GoLive", @"InputNext", @"InputPrevious", @"ActivateLive", @"ActivateStaging", @"ActivateToggle", @"InstantRecord"]; NSMutableArray *layoutIdentifiers = [NSMutableArray array]; for (SourceLayout *layout in self.sourceLayouts) { - [layoutIdentifiers addObject:[NSString stringWithFormat:@"SwitchStaging:%@", layout.name]]; - [layoutIdentifiers addObject:[NSString stringWithFormat:@"SwitchLive:%@", layout.name]]; + [layoutIdentifiers addObject:[NSString stringWithFormat:@"ToggleLayout:%@", layout.name]]; } + for (SourceLayout *layout in self.sourceLayouts) + { + [layoutIdentifiers addObject:[NSString stringWithFormat:@"SwitchToLayout:%@", layout.name]]; + } + baseIdentifiers = [baseIdentifiers arrayByAddingObjectsFromArray:layoutIdentifiers]; baseIdentifiers = [baseIdentifiers arrayByAddingObjectsFromArray:_inputIdentifiers]; return baseIdentifiers; @@ -3091,6 +3240,13 @@ -(SourceLayout *)currentMIDILayout { + + if (self.stagingHidden) + { + return self.activePreviewView.sourceLayout; + } + + if (self.currentMidiLayoutLive) { return self.livePreviewView.sourceLayout; @@ -3140,19 +3296,35 @@ -(void)handleMIDICommandActivateLive:(MIKMIDICommand *)command { - self.currentMidiLayoutLive = YES; + if (!self.stagingHidden) + { + self.currentMidiLayoutLive = YES; + self.stagingPreviewView.midiActive = NO; + self.livePreviewView.midiActive = YES; + } } -(void)handleMIDICommandActivateStaging:(MIKMIDICommand *)command { - self.currentMidiLayoutLive = NO; + if (!self.stagingHidden) + { + self.currentMidiLayoutLive = NO; + self.livePreviewView.midiActive = NO; + self.stagingPreviewView.midiActive = YES; + } } -(void)handleMIDICommandActivateToggle:(MIKMIDICommand *)command { - self.currentMidiLayoutLive = !self.currentMidiLayoutLive; + if (!self.stagingHidden) + { + self.currentMidiLayoutLive = !self.currentMidiLayoutLive; + self.stagingPreviewView.midiActive = !self.stagingPreviewView.midiActive; + self.livePreviewView.midiActive = !self.livePreviewView.midiActive; + } } + -(void)handleMIDICommandInputNext:(MIKMIDIChannelVoiceCommand *)command { @@ -3213,11 +3385,41 @@ self.currentMidiInputStagingIdx = cVal; } } + + +-(id)dispatchMIDI:(MIKMIDICommand *)command forItem:(MIKMIDIMappingItem *)item +{ + + id ret = nil; + + SourceLayout *currLayout = [self currentMIDILayout]; + NSString *responderName = item.MIDIResponderIdentifier; + + if ([responderName hasPrefix:@"Layout:"]) + { + ret = currLayout; + } else if ([responderName hasPrefix:@"Input:"]) { + NSString *uuid = [responderName substringFromIndex:6]; + InputSource *input = [currLayout inputForUUID:uuid]; + if (input) + { + ret = input; + } + } + + return ret; +} + + + + + -(void)handleMIDICommand:(MIKMIDICommand *)command forIdentifier:(NSString *)identifier { __weak CaptureController *weakSelf = self; + if ([_inputIdentifiers containsObject:identifier]) { InputSource *currInput = [self currentMIDIInput:command]; @@ -3239,9 +3441,11 @@ } - if ([identifier hasPrefix:@"SwitchStaging:"]) + if ([identifier hasPrefix:@"ToggleLayout:"]) { - NSString *layoutName = [identifier substringFromIndex:14]; + + + NSString *layoutName = [identifier substringFromIndex:13]; NSUInteger indexOfLayout = [self.sourceLayouts indexOfObjectPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) { SourceLayout *testLayout = obj; if ([testLayout.name isEqualToString:layoutName]) @@ -3256,32 +3460,7 @@ { SourceLayout *layout = [self.sourceLayouts objectAtIndex:indexOfLayout]; dispatch_async(dispatch_get_main_queue(), ^{ - weakSelf.stagingLayout = layout; - }); - - } - return; - } - - - if ([identifier hasPrefix:@"SwitchLive:"]) - { - NSString *layoutName = [identifier substringFromIndex:11]; - NSUInteger indexOfLayout = [self.sourceLayouts indexOfObjectPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) { - SourceLayout *testLayout = obj; - if ([testLayout.name isEqualToString:layoutName]) - { - *stop = YES; - return YES; - } - return NO; - }]; - - if (indexOfLayout != NSNotFound) - { - SourceLayout *layout = [self.sourceLayouts objectAtIndex:indexOfLayout]; - dispatch_async(dispatch_get_main_queue(), ^{ - weakSelf.selectedLayout = layout; + [weakSelf toggleLayout:layout]; }); } @@ -3289,20 +3468,50 @@ } + if ([identifier hasPrefix:@"SwitchToLayout:"]) + { + + + NSString *layoutName = [identifier substringFromIndex:15]; + NSUInteger indexOfLayout = [self.sourceLayouts indexOfObjectPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) { + SourceLayout *testLayout = obj; + if ([testLayout.name isEqualToString:layoutName]) + { + *stop = YES; + return YES; + } + return NO; + }]; + + if (indexOfLayout != NSNotFound) + { + SourceLayout *layout = [self.sourceLayouts objectAtIndex:indexOfLayout]; + dispatch_async(dispatch_get_main_queue(), ^{ + [weakSelf switchToLayout:layout]; + }); + + } + return; + } + if ([identifier isEqualToString:@"GoLive"]) { dispatch_async(dispatch_get_main_queue(), ^{ [weakSelf stagingGoLive:nil]; }); - } else if ([identifier isEqualToString:@"StagingRevert"]) { + } + + if ([identifier isEqualToString:@"InstantRecord"]) + { dispatch_async(dispatch_get_main_queue(), ^{ - [weakSelf stagingRevert:nil]; + [weakSelf doInstantRecord:nil]; }); } } + -(void)saveMIDI { MIKMIDIMappingManager *manager = [MIKMIDIMappingManager sharedManager]; @@ -3344,6 +3553,10 @@ for (CSMidiWrapper *wrap in self.midiMapGenerators) { + wrap.redirectResponderBlock = ^id(MIKMIDICommand *command, MIKMIDIMappingItem *item) { + return [self dispatchMIDI:command forItem:item]; + }; + self.midiDeviceMappings[wrap.device.name] = wrap; [wrap connect]; @@ -3355,6 +3568,36 @@ +- (IBAction)doInstantRecord:(id)sender +{ + if (self.instantRecordActive && self.instantRecorder) + { + + NSString *directory = self.instantRecordDirectory; + + if (!directory) + { + NSArray *mPaths = NSSearchPathForDirectoriesInDomains(NSMoviesDirectory, NSUserDomainMask, YES); + directory = mPaths.firstObject; + } + + if (directory) + { + NSDateFormatter *dFormat = [[NSDateFormatter alloc] init]; + dFormat.dateStyle = NSDateFormatterMediumStyle; + dFormat.timeStyle = NSDateFormatterMediumStyle; + NSString *dateStr = [dFormat stringFromDate:[NSDate date]]; + NSString *useFilename = [NSString stringWithFormat:@"CS_instant_record-%@.mov", dateStr]; + + NSString *savePath = [NSString pathWithComponents:@[directory, useFilename]]; + + [self.instantRecorder writeCurrentBuffer:savePath]; + } + } +} + + + -(IBAction)openTransitionFilterPanel:(NSButton *)sender { @@ -3400,50 +3643,86 @@ [self.pluginManagerController showWindow:nil]; } + + +-(void)applyTransitionSettings:(SourceLayout *)layout +{ + [self.objectController commitEditing]; + layout.transitionName = self.transitionName; + layout.transitionDirection = self.transitionDirection; + layout.transitionDuration = self.transitionDuration; + layout.transitionFilter = self.transitionFilter; + layout.transitionFullScene = self.transitionFullScene; +} + +-(void)clearTransitionSettings:(SourceLayout *)layout +{ + layout.transitionName = nil; + layout.transitionDirection = nil; + layout.transitionDuration = 0; + layout.transitionFilter = nil; + layout.transitionFullScene = nil; + +} +-(IBAction) swapStagingAndLive:(id)sender +{ + + //Save the current live layout to a temporary layout, do a normal staging->live and then restore old live into current staging + + [self.livePreviewView.sourceLayout saveSourceList]; + + SourceLayout *tmpLive = [self.livePreviewView.sourceLayout copy]; + + [self stagingGoLive:self]; + + [self applyTransitionSettings:self.activePreviewView.sourceLayout]; + [self switchToLayout:tmpLive]; + [self clearTransitionSettings:self.activePreviewView.sourceLayout]; +} + + + - (IBAction)stagingGoLive:(id)sender { - [self.objectController commitEditing]; + [self applyTransitionSettings:self.livePreviewView.sourceLayout]; - self.previewCtx.layoutRenderer.transitionName = self.transitionName; - self.previewCtx.layoutRenderer.transitionDirection = self.transitionDirection; - self.previewCtx.layoutRenderer.transitionDuration = self.transitionDuration; - self.previewCtx.layoutRenderer.transitionFilter = self.transitionFilter; + /* + InputSource *src1 = [[InputSource alloc] init]; + InputSource *src2 = src1.copy; + src2.uuid = src1.uuid; - - - if (self.stagingLayout && self.stagingCtx.sourceLayout) + + bool diffInp = [src1 isDifferentInput:src2]; + + + NSLog(@"DIFFERENT INPUT %d", diffInp); + + */ + + + if (self.stagingLayout) { [self stagingSave:sender]; + + self.inLayoutTransition = YES; + [self.selectedLayout replaceWithSourceLayout:self.stagingLayout withCompletionBlock:^{ + dispatch_async(dispatch_get_main_queue(), ^{ + self.inLayoutTransition = NO; + }); + + }]; - if (self.selectedLayout != self.stagingLayout) - { - self.selectedLayout = self.stagingLayout; - } else { - SourceLayout *previewCopy = self.stagingLayout.copy; - [previewCopy restoreSourceList:nil]; - NSUInteger layoutIdx = [self.sourceLayoutsArrayController.arrangedObjects indexOfObject:self.selectedLayout]; - [self.sourceLayoutsArrayController removeObjectAtArrangedObjectIndex:layoutIdx]; - [self.sourceLayoutsArrayController insertObject:previewCopy atArrangedObjectIndex:layoutIdx]; - - self.selectedLayout = previewCopy; - _stagingLayout = previewCopy; - - } + + [self changePendingAnimations]; + } } + -(IBAction)stagingSave:(id)sender { - if (self.stagingLayout && self.stagingCtx.sourceLayout) - { - [self.stagingCtx.sourceLayout saveSourceList]; - self.stagingLayout.savedSourceListData = self.stagingCtx.sourceLayout.savedSourceListData; - - self.stagingLayout.frameRate = self.stagingCtx.sourceLayout.frameRate; - self.stagingLayout.canvas_width = self.stagingCtx.sourceLayout.canvas_width; - self.stagingLayout.canvas_height = self.stagingCtx.sourceLayout.canvas_height; - } + [self.stagingLayout saveSourceList]; } -(IBAction)stagingRevert:(id)sender @@ -3492,11 +3771,12 @@ [self.canvasSplitView display]; - self.stagingControls.hidden = YES; - self.goLiveControls.hidden = YES; self.livePreviewView.viewOnly = NO; - _stagingHidden = YES; - + self.livePreviewView.midiActive = NO; + self.activePreviewView = self.livePreviewView; + self.stagingHidden = YES; + [[NSNotificationCenter defaultCenter] postNotificationName:CSNotificationLayoutModeChanged object:self]; + } -(void) showStagingView @@ -3530,10 +3810,17 @@ [self.canvasSplitView adjustSubviews]; [self.canvasSplitView display]; - self.stagingControls.hidden = NO; - self.goLiveControls.hidden = NO; self.livePreviewView.viewOnly = YES; - _stagingHidden = NO; + self.stagingHidden = NO; + self.activePreviewView = self.stagingPreviewView; + if (self.currentMidiLayoutLive) + { + self.livePreviewView.midiActive = YES; + self.stagingPreviewView.midiActive = NO; + } else { + self.livePreviewView.midiActive = YES; + self.stagingPreviewView.midiActive = NO; + } dispatch_async(_preview_queue, ^{ [self newStagingFrameTimed]; }); @@ -3584,4 +3871,9 @@ } +- (IBAction)outputEditClicked:(OutputDestination *)toEdit +{ + [self openOutputSheet:toEdit]; +} + @end diff --git a/CocoaSplit/CapturedFrameData.h b/CocoaSplit/CapturedFrameData.h index cb14c7e9..0a345dd4 100644 --- a/CocoaSplit/CapturedFrameData.h +++ b/CocoaSplit/CapturedFrameData.h @@ -12,7 +12,7 @@ #import "libavcodec/avcodec.h" -@interface CapturedFrameData : NSObject +@interface CapturedFrameData : NSObject @@ -21,6 +21,7 @@ @property CVImageBufferRef videoFrame; @property (assign) void *encoderData; +@property (assign) BOOL isKeyFrame; //Array of CMSampleBuffers from audio capture. @@ -29,9 +30,12 @@ @property CMTime videoPTS; @property CMTime videoDuration; -@property CMSampleBufferRef encodedSampleBuffer; +@property (assign) CMSampleBufferRef encodedSampleBuffer; @property (assign) AVPacket *avcodec_pkt; @property AVCodecContext *avcodec_ctx; + +-(NSInteger) encodedDataLength; + @end \ No newline at end of file diff --git a/CocoaSplit/CapturedFrameData.m b/CocoaSplit/CapturedFrameData.m index 9b89121f..0fa05825 100644 --- a/CocoaSplit/CapturedFrameData.m +++ b/CocoaSplit/CapturedFrameData.m @@ -18,13 +18,33 @@ @synthesize avcodec_pkt = _avcodec_pkt; + +-(instancetype) copyWithZone:(NSZone *)zone +{ + + CapturedFrameData *copy = [[[self class] allocWithZone:zone] init]; + copy.frameNumber = self.frameNumber; + copy.frameTime = self.frameTime; + copy.videoFrame = self.videoFrame; + copy.encoderData = self.encoderData; + copy.isKeyFrame = self.isKeyFrame; + copy.audioSamples = self.audioSamples; + copy.videoPTS = self.videoPTS; + copy.videoDuration = self.videoDuration; + copy.encodedSampleBuffer = self.encodedSampleBuffer; + copy.avcodec_pkt = self.avcodec_pkt; + copy.avcodec_ctx = self.avcodec_ctx; + return copy; +} + + -(id)init { if (self = [super init]) { _videoFrame = nil; self.frameNumber = 0; - self.audioSamples = [[NSMutableArray alloc] init]; + //self.audioSamples = [[NSMutableArray alloc] init]; } return self; @@ -52,10 +72,25 @@ av_free(_avcodec_pkt); } - + + self.audioSamples = nil; } +-(NSInteger) encodedDataLength +{ + NSInteger ret = 0; + + if (self.avcodec_pkt) + { + ret = self.avcodec_pkt->size; + } else if (self.encodedSampleBuffer) { + ret = CMSampleBufferGetTotalSampleSize(self.encodedSampleBuffer); + } + + return ret; +} + -(CMSampleBufferRef)encodedSampleBuffer { @@ -68,8 +103,11 @@ if (_encodedSampleBuffer) { CFRelease(_encodedSampleBuffer); + } + + if (encodedSampleBuffer) + { CFRetain(encodedSampleBuffer); - } _encodedSampleBuffer = encodedSampleBuffer; diff --git a/CocoaSplit/CocoaSplit-Info.plist b/CocoaSplit/CocoaSplit-Info.plist index 12ff12db..223bc001 100644 --- a/CocoaSplit/CocoaSplit-Info.plist +++ b/CocoaSplit/CocoaSplit-Info.plist @@ -17,11 +17,11 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.9.12 + 2.0.13 CFBundleSignature ???? CFBundleVersion - 1.9.12 + 2.0.13 LSApplicationCategoryType public.app-category.video LSMinimumSystemVersion @@ -37,6 +37,6 @@ OSAScriptingDefinition CocoaSplit.sdef SUFeedURL - http://www.cocoasplit.com/cocoasplitcast.xml + https://www.cocoasplit.com/cocoasplitcast2.0.xml diff --git a/CocoaSplit/Compressor/AppleProResCompressor.h b/CocoaSplit/Compressor/AppleProResCompressor.h new file mode 100644 index 00000000..5f82dbad --- /dev/null +++ b/CocoaSplit/Compressor/AppleProResCompressor.h @@ -0,0 +1,32 @@ +// +// AppleProResCompressor.h +// CocoaSplit +// +// Created by Zakk on 3/27/16. +// Copyright © 2016 Zakk. All rights reserved. +// + +#import +#import "CaptureController.h" +#import "CompressorBase.h" + +#import +#import + +@interface AppleProResCompressor : CompressorBase +{ + + VTCompressionSessionRef _compression_session; + VTPixelTransferSessionRef _vtpt_ref; + +} + + +//@property (strong) id outputDelegate; + +@property (strong) NSNumber *proResType; + + +-(bool)compressFrame:(CapturedFrameData *)frameData; + +@end diff --git a/CocoaSplit/Compressor/AppleProResCompressor.m b/CocoaSplit/Compressor/AppleProResCompressor.m new file mode 100644 index 00000000..d26900db --- /dev/null +++ b/CocoaSplit/Compressor/AppleProResCompressor.m @@ -0,0 +1,311 @@ +// +// AppleProResCompressor.m +// CocoaSplit +// +// Created by Zakk on 3/27/16. +// Copyright © 2016 Zakk. All rights reserved. +// + +#import "AppleProResCompressor.h" +#import "OutputDestination.h" +#import "CSAppleProResCompressorViewController.h" + +@implementation AppleProResCompressor + +- (id)copyWithZone:(NSZone *)zone +{ + AppleProResCompressor *copy = [[[self class] allocWithZone:zone] init]; + + + copy.isNew = self.isNew; + + copy.name = self.name; + + copy.compressorType = self.compressorType; + + copy.width = self.width; + copy.height = self.height; + copy.working_width = self.width; + copy.working_height = self.height; + + copy.resolutionOption = self.resolutionOption; + + copy.proResType = self.proResType; + + return copy; +} + + +-(void) encodeWithCoder:(NSCoder *)aCoder +{ + + + [aCoder encodeObject:self.name forKey:@"name"]; + [aCoder encodeInteger:self.width forKey:@"videoWidth"]; + [aCoder encodeInteger:self.height forKey:@"videoHeight"]; + + [aCoder encodeObject:self.resolutionOption forKey:@"resolutionOption"]; + [aCoder encodeObject:self.proResType forKey:@"proResType"]; + +} + +-(id) initWithCoder:(NSCoder *)aDecoder +{ + if (self = [self init]) + { + self.name = [aDecoder decodeObjectForKey:@"name"]; + self.width = (int)[aDecoder decodeIntegerForKey:@"videoWidth"]; + self.height = (int)[aDecoder decodeIntegerForKey:@"videoHeight"]; + if ([aDecoder containsValueForKey:@"resolutionOption"]) + { + self.resolutionOption = [aDecoder decodeObjectForKey:@"resolutionOption"]; + } + + self.proResType = [aDecoder decodeObjectForKey:@"proResType"]; + + if (!self.proResType) + { + self.proResType = @(kCMVideoCodecType_AppleProRes422); + + } + + } + + return self; +} + + +-(id)init +{ + if (self = [super init]) + { + + + + self.compressorType = @"AppleProResCompressor"; + self.codec_id = AV_CODEC_ID_PRORES; + self.proResType = @(kCMVideoCodecType_AppleProRes422); + } + + return self; +} + + +-(void) reset +{ + + + self.errored = NO; + VTCompressionSessionInvalidate(_compression_session); + if (_compression_session) + { + CFRelease(_compression_session); + } + + _compression_session = nil; + +} + + + +- (void) dealloc +{ + [self reset]; +} + + + +-(NSString *)description +{ + + return @"Apple ProRes Compressor"; + +} + + +void __ProResPixelBufferRelease( void *releaseRefCon, const void *baseAddress ) +{ + free((int *)baseAddress); +} + + + +-(bool)compressFrame:(CapturedFrameData *)frameData +{ + + + if (![self hasOutputs]) + { + return NO; + } + + if (!_compression_session) + { + + if (![self setupCompressor:frameData.videoFrame]) + { + return NO; + } + return NO; + } + + + + CFMutableDictionaryRef frameProperties; + + /* + if (isKeyFrame) + { + + frameProperties = CFDictionaryCreateMutable(NULL, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + CFDictionaryAddValue(frameProperties, kVTEncodeFrameOptionKey_ForceKeyFrame, kCFBooleanTrue); + } else { + */ + frameProperties = NULL; + //} + + if (!_vtpt_ref) + { + VTPixelTransferSessionCreate(kCFAllocatorDefault, &_vtpt_ref); + VTSessionSetProperty(_vtpt_ref, kVTPixelTransferPropertyKey_ScalingMode, kVTScalingMode_Letterbox); + } + CVPixelBufferRef converted_frame; + + CVImageBufferRef imageBuffer = frameData.videoFrame; + CVPixelBufferRetain(imageBuffer); + + CVPixelBufferCreate(kCFAllocatorDefault, self.working_width, self.working_height, kCVPixelFormatType_420YpCbCr8Planar, 0, &converted_frame); + + VTPixelTransferSessionTransferImage(_vtpt_ref, imageBuffer, converted_frame); + + //set it to nil since this is our private copy and this will force the frameData instance to release the video data + + frameData.videoFrame = nil; + frameData.encoderData = converted_frame; + + + CVPixelBufferRelease(imageBuffer); + + + VTCompressionSessionEncodeFrame(_compression_session, converted_frame, frameData.videoPTS, frameData.videoDuration, frameProperties, (__bridge_retained void *)(frameData), NULL); + + if (frameProperties) + { + CFRelease(frameProperties); + } + + + return YES; +} + + + + +- (bool)setupCompressor:(CVPixelBufferRef)videoFrame +{ + OSStatus status; + + + [self setupResolution:videoFrame]; + + if (!self.working_height || !self.working_width) + { + self.errored = YES; + return NO; + } + + NSDictionary *encoderSpec = @{ + }; + + _compression_session = NULL; + + status = VTCompressionSessionCreate(NULL, self.working_width, self.working_height, self.proResType.intValue, (__bridge CFDictionaryRef)encoderSpec, NULL, NULL, __ProResVideoCompressorReceiveFrame, (__bridge void *)self, &_compression_session); + + //CFDictionaryRef props; + //VTCompressionSessionCopySupportedPropertyDictionary(_compression_session, &props); + + if (status != noErr || !_compression_session) + { + NSLog(@"COMPRESSOR SETUP ERROR"); + self.errored = YES; + return NO; + } + + int real_keyframe_interval = 2; + VTSessionSetProperty(_compression_session, kVTCompressionPropertyKey_MaxKeyFrameIntervalDuration, (__bridge CFTypeRef)@(real_keyframe_interval)); + + _audioBuffer = [[NSMutableArray alloc] init]; + return YES; + +} + + +void __ProResVideoCompressorReceiveFrame(void *VTref, void *VTFrameRef, OSStatus status, VTEncodeInfoFlags infoFlags, CMSampleBufferRef sampleBuffer) +{ + + /* + if (VTFrameRef) + { + CVPixelBufferRelease(VTFrameRef); + } + */ + + + //@autoreleasepool { + + + + if(!sampleBuffer) + return; + + + + CFRetain(sampleBuffer); + + CapturedFrameData *frameData; + + + frameData = (__bridge_transfer CapturedFrameData *)(VTFrameRef); + + + if (!frameData) + { + //What? + return; + } + + + CVPixelBufferRelease(frameData.encoderData); + + + //frameData.videoFrame = nil; + frameData.encodedSampleBuffer = sampleBuffer; + frameData.isKeyFrame = YES; + + AppleVTCompressor *selfobj = (__bridge AppleVTCompressor *)VTref; + + + + + for (id dKey in selfobj.outputs) + { + + OutputDestination *dest = selfobj.outputs[dKey]; + + [dest writeEncodedData:frameData]; + + + } + + + CFRelease(sampleBuffer); + //} +} + +-(id )getConfigurationView +{ + return [[CSAppleProResCompressorViewController alloc] init]; +} + + +@end diff --git a/CocoaSplit/Compressor/AppleVTCompressor.h b/CocoaSplit/Compressor/AppleVTCompressor.h index 0a213882..6e6c7232 100644 --- a/CocoaSplit/Compressor/AppleVTCompressor.h +++ b/CocoaSplit/Compressor/AppleVTCompressor.h @@ -13,16 +13,19 @@ #import #import -@interface AppleVTCompressor : CompressorBase +@interface AppleVTCompressor : CompressorBase { VTCompressionSessionRef _compression_session; VTPixelTransferSessionRef _vtpt_ref; + bool _resetPending; + dispatch_queue_t _compressor_queue; + + } -//@property (strong) id settingsController; //@property (strong) id outputDelegate; @property (assign) int average_bitrate; @@ -32,8 +35,12 @@ @property (assign) BOOL use_cbr; @property (strong) NSArray *profiles; +@property (assign) bool noHardware; +@property (assign) bool forceHardware; + -(bool)compressFrame:(CapturedFrameData *)frameData; ++(bool)intelQSVAvailable; @end diff --git a/CocoaSplit/Compressor/AppleVTCompressor.m b/CocoaSplit/Compressor/AppleVTCompressor.m index f4c5b400..5b01ab4d 100644 --- a/CocoaSplit/Compressor/AppleVTCompressor.m +++ b/CocoaSplit/Compressor/AppleVTCompressor.m @@ -8,6 +8,9 @@ #import "AppleVTCompressor.h" #import "OutputDestination.h" +#import "CSAppleH264CompressorViewController.h" +#import "CSPluginServices.h" + OSStatus VTCompressionSessionCopySupportedPropertyDictionary(VTCompressionSessionRef, CFDictionaryRef *); @@ -20,7 +23,6 @@ OSStatus VTCompressionSessionCopySupportedPropertyDictionary(VTCompressionSessio { AppleVTCompressor *copy = [[[self class] allocWithZone:zone] init]; - copy.settingsController = self.settingsController; copy.isNew = self.isNew; @@ -61,6 +63,9 @@ OSStatus VTCompressionSessionCopySupportedPropertyDictionary(VTCompressionSessio [aCoder encodeObject:self.resolutionOption forKey:@"resolutionOption"]; + [aCoder encodeBool:self.noHardware forKey:@"noHardware"]; + [aCoder encodeBool:self.forceHardware forKey:@"forceHardware"]; + } -(id) initWithCoder:(NSCoder *)aDecoder @@ -75,6 +80,9 @@ OSStatus VTCompressionSessionCopySupportedPropertyDictionary(VTCompressionSessio self.use_cbr = [aDecoder decodeBoolForKey:@"use_cbr"]; self.width = (int)[aDecoder decodeIntegerForKey:@"videoWidth"]; self.height = (int)[aDecoder decodeIntegerForKey:@"videoHeight"]; + self.noHardware = [aDecoder decodeBoolForKey:@"noHardware"]; + self.forceHardware = [aDecoder decodeBoolForKey:@"forceHardware"]; + if ([aDecoder containsValueForKey:@"resolutionOption"]) { self.resolutionOption = [aDecoder decodeObjectForKey:@"resolutionOption"]; @@ -97,6 +105,7 @@ OSStatus VTCompressionSessionCopySupportedPropertyDictionary(VTCompressionSessio self.compressorType = @"AppleVTCompressor"; self.profiles = @[[NSNull null], @"Baseline", @"Main", @"High"]; + _compressor_queue = dispatch_queue_create("Apple VT Compressor Queue", 0); } return self; @@ -106,16 +115,17 @@ OSStatus VTCompressionSessionCopySupportedPropertyDictionary(VTCompressionSessio -(void) reset { - + _resetPending = YES; self.errored = NO; + VTCompressionSessionCompleteFrames(_compression_session, CMTimeMake(0, 0)); VTCompressionSessionInvalidate(_compression_session); if (_compression_session) { CFRelease(_compression_session); } - _compression_session = nil; - + _compression_session = NULL; + _resetPending = NO; } @@ -143,12 +153,20 @@ void PixelBufferRelease( void *releaseRefCon, const void *baseAddress ) -(bool)compressFrame:(CapturedFrameData *)frameData { + if (_resetPending) + { + return NO; + } + if (![self hasOutputs]) { return NO; } + + + if (!_compression_session) { @@ -159,7 +177,7 @@ void PixelBufferRelease( void *releaseRefCon, const void *baseAddress ) return NO; } - + CFMutableDictionaryRef frameProperties; @@ -195,8 +213,6 @@ void PixelBufferRelease( void *releaseRefCon, const void *baseAddress ) CVPixelBufferRelease(imageBuffer); - - [self setAudioData:frameData syncObj:self]; VTCompressionSessionEncodeFrame(_compression_session, converted_frame, frameData.videoPTS, frameData.videoDuration, frameProperties, (__bridge_retained void *)(frameData), NULL); @@ -204,22 +220,44 @@ void PixelBufferRelease( void *releaseRefCon, const void *baseAddress ) { CFRelease(frameProperties); } - + return YES; } ++(bool)intelQSVAvailable +{ + NSMutableDictionary *encoderSpec = [[NSMutableDictionary alloc] init]; + encoderSpec[(__bridge NSString *)kVTVideoEncoderSpecification_RequireHardwareAcceleratedVideoEncoder] = @YES; + + + VTCompressionSessionRef testSession = NULL; + OSStatus status; + + status = VTCompressionSessionCreate(NULL, 1920, 1080, kCMVideoCodecType_H264, (__bridge CFDictionaryRef)encoderSpec, NULL, NULL, NULL, (__bridge void *)self, &testSession); + + bool ret; + if (status != noErr || !testSession) + { + ret = NO; + } else { + VTCompressionSessionInvalidate(testSession); + if (testSession) + { + CFRelease(testSession); + } + ret = YES; + } + + return ret; +} - (bool)setupCompressor:(CVPixelBufferRef)videoFrame { OSStatus status; - if (!self.settingsController) - { - return NO; - } [self setupResolution:videoFrame]; @@ -229,10 +267,21 @@ void PixelBufferRelease( void *releaseRefCon, const void *baseAddress ) return NO; } - NSDictionary *encoderSpec = @{ - @"RequireHardwareAcceleratedVideoEncoder": @YES, - }; - + + + NSMutableDictionary *encoderSpec = [[NSMutableDictionary alloc] init]; + + bool enableVal = !self.noHardware; + + encoderSpec[(__bridge NSString *)kVTVideoEncoderSpecification_EnableHardwareAcceleratedVideoEncoder] = @(enableVal); + + + if (self.forceHardware) + { + encoderSpec[(__bridge NSString *)kVTVideoEncoderSpecification_RequireHardwareAcceleratedVideoEncoder] = @YES; + } + + _compression_session = NULL; status = VTCompressionSessionCreate(NULL, self.working_width, self.working_height, kCMVideoCodecType_H264, (__bridge CFDictionaryRef)encoderSpec, NULL, NULL, VideoCompressorReceiveFrame, (__bridge void *)self, &_compression_session); @@ -265,7 +314,13 @@ void PixelBufferRelease( void *releaseRefCon, const void *baseAddress ) + double captureFPS = [CSPluginServices sharedPluginServices].currentFPS; + if (captureFPS > 0) + { + VTSessionSetProperty(_compression_session, kVTCompressionPropertyKey_ExpectedFrameRate, (__bridge CFTypeRef)(@(captureFPS))); + } + int real_keyframe_interval = 2; if (self.keyframe_interval && self.keyframe_interval > 0) @@ -308,18 +363,6 @@ void PixelBufferRelease( void *releaseRefCon, const void *baseAddress ) - /* - - if (self.settingsController.captureFPS && self.settingsController.captureFPS > 0) - { - - - - VTSessionSetProperty(_compression_session, kVTCompressionPropertyKey_ExpectedFrameRate, (__bridge CFTypeRef)(@(self.settingsController.captureFPS))); - - } - - */ //This doesn't appear to work at all (2012 rMBP, 10.8.4). Even if you set DataRateLimits, you don't get anything back if you @@ -421,8 +464,19 @@ void VideoCompressorReceiveFrame(void *VTref, void *VTFrameRef, OSStatus status, //frameData.videoFrame = nil; frameData.encodedSampleBuffer = sampleBuffer; + CFArrayRef sample_attachments; + sample_attachments = CMSampleBufferGetSampleAttachmentsArray(sampleBuffer, NO); + if (sample_attachments) + { + CFDictionaryRef attach; + CFBooleanRef depends_on_others; + + attach = CFArrayGetValueAtIndex(sample_attachments, 0); + depends_on_others = CFDictionaryGetValue(attach, kCMSampleAttachmentKey_DependsOnOthers); + frameData.isKeyFrame = depends_on_others; + } - + AppleVTCompressor *selfobj = (__bridge AppleVTCompressor *)VTref; @@ -443,5 +497,10 @@ void VideoCompressorReceiveFrame(void *VTref, void *VTFrameRef, OSStatus status, //} } +-(id )getConfigurationView +{ + return [[CSAppleH264CompressorViewController alloc] init]; +} + @end diff --git a/CocoaSplit/Compressor/CSIRCompressor.h b/CocoaSplit/Compressor/CSIRCompressor.h new file mode 100644 index 00000000..7e0fddda --- /dev/null +++ b/CocoaSplit/Compressor/CSIRCompressor.h @@ -0,0 +1,30 @@ +// +// CSIRCompressor.h +// CocoaSplit +// +// Created by Zakk on 4/11/16. +// Copyright © 2016 Zakk. All rights reserved. +// + +#import "CompressorBase.h" +#import "AppleVTCompressor.h" +@interface CSIRCompressor : CompressorBase +{ + + id _compressor; + +} + + +@property (assign) bool tryAppleHardware; +@property (assign) bool useAppleH264; +@property (assign) bool useAppleProRes; +@property (assign) bool usex264; +@property (assign) bool useNone; +@property (assign) int selectedCompressor; + + + +-(bool)compressFrame:(CapturedFrameData *)frameData; + +@end diff --git a/CocoaSplit/Compressor/CSIRCompressor.m b/CocoaSplit/Compressor/CSIRCompressor.m new file mode 100644 index 00000000..80ac4356 --- /dev/null +++ b/CocoaSplit/Compressor/CSIRCompressor.m @@ -0,0 +1,254 @@ +// +// CSIRCompressor.m +// CocoaSplit +// +// Created by Zakk on 4/11/16. +// Copyright © 2016 Zakk. All rights reserved. +// + +#import "CSIRCompressor.h" +#import "OutputDestination.h" +#import "CSInstantRecorderCompressorViewController.h" +#import "x264Compressor.h" + + + +@implementation CSIRCompressor + +- (id)copyWithZone:(NSZone *)zone +{ + CSIRCompressor *copy = [[[self class] allocWithZone:zone] init]; + + + copy.isNew = self.isNew; + + copy.name = self.name; + + copy.compressorType = self.compressorType; + + copy.width = self.width; + copy.height = self.height; + copy.working_width = self.width; + copy.working_height = self.height; + + copy.resolutionOption = self.resolutionOption; + copy.tryAppleHardware = self.tryAppleHardware; + copy.useAppleH264 = self.useAppleH264; + copy.useAppleProRes = self.useAppleProRes; + copy.usex264 = self.usex264; + copy.useNone = self.useNone; + + + return copy; +} + + +-(void) encodeWithCoder:(NSCoder *)aCoder +{ + + + [aCoder encodeObject:self.name forKey:@"name"]; + [aCoder encodeInteger:self.width forKey:@"videoWidth"]; + [aCoder encodeInteger:self.height forKey:@"videoHeight"]; + + [aCoder encodeObject:self.resolutionOption forKey:@"resolutionOption"]; + [aCoder encodeBool:self.tryAppleHardware forKey:@"tryAppleHardware"]; + [aCoder encodeBool:self.useAppleH264 forKey:@"useAppleH264"]; + [aCoder encodeBool:self.useAppleProRes forKey:@"useAppleProRes"]; + [aCoder encodeBool:self.usex264 forKey:@"usex264"]; + [aCoder encodeBool:self.useNone forKey:@"useNone"]; + +} + +-(id) initWithCoder:(NSCoder *)aDecoder +{ + if (self = [self init]) + { + self.name = [aDecoder decodeObjectForKey:@"name"]; + self.width = (int)[aDecoder decodeIntegerForKey:@"videoWidth"]; + self.height = (int)[aDecoder decodeIntegerForKey:@"videoHeight"]; + + if ([aDecoder containsValueForKey:@"resolutionOption"]) + { + self.resolutionOption = [aDecoder decodeObjectForKey:@"resolutionOption"]; + } + + if ([aDecoder containsValueForKey:@"tryAppleHardware"]) + { + self.tryAppleHardware = [aDecoder decodeBoolForKey:@"tryAppleHardware"]; + } + + if ([aDecoder containsValueForKey:@"useAppleH264"]) + { + self.useAppleH264 = [aDecoder decodeBoolForKey:@"useAppleH264"]; + } + + if ([aDecoder containsValueForKey:@"useAppleProRes"]) + { + self.useAppleProRes = [aDecoder decodeBoolForKey:@"useAppleProRes"]; + } + + if ([aDecoder containsValueForKey:@"usex264"]) + { + self.usex264 = [aDecoder decodeBoolForKey:@"usex264"]; + } + + if ([aDecoder containsValueForKey:@"useNone"]) + { + self.useNone = [aDecoder decodeBoolForKey:@"useNone"]; + } + } + + return self; +} + + +-(id)init +{ + if (self = [super init]) + { + + + + self.compressorType = @"Instant Replay Compressor"; + self.tryAppleHardware = YES; + self.useNone = YES; + } + + return self; +} + + +-(void) reset +{ + if (_compressor) + { + [_compressor reset]; + } + + _compressor = nil; + self.errored = NO; + + + + +} + + +- (void) dealloc +{ + [self reset]; +} + + + +-(bool)compressFrame:(CapturedFrameData *)frameData +{ + + + if (![self hasOutputs]) + { + return NO; + } + + + + if (!_compressor) + { + bool compressor_status; + + compressor_status = [self setupCompressor:frameData.videoFrame]; + + + if (!compressor_status) + { + self.errored = YES; + return NO; + } else { + self.codec_id = _compressor.codec_id; + + [_compressor addOutput:self]; + } + } + + bool ret; + ret = [_compressor compressFrame:frameData]; + return ret; +} + + +-(void) writeEncodedData:(CapturedFrameData *)frameData +{ + for (id dKey in self.outputs) + { + + OutputDestination *dest = self.outputs[dKey]; + + [dest writeEncodedData:frameData]; + + + } + +} + + +- (bool)setupCompressor:(CVPixelBufferRef)videoFrame +{ + + + if (self.tryAppleHardware && [AppleVTCompressor intelQSVAvailable]) + { + + AppleVTCompressor *acomp = [[AppleVTCompressor alloc] init]; + acomp.average_bitrate = 9000; + acomp.max_bitrate = 15000; + acomp.keyframe_interval = 2; + acomp.forceHardware = YES; + _compressor = acomp; + return YES; + } + + if (self.useNone) + { + return NO; + } + + if (self.useAppleH264) + { + AppleVTCompressor *acomp = [[AppleVTCompressor alloc] init]; + acomp.average_bitrate = 9000; + acomp.max_bitrate = 15000; + acomp.keyframe_interval = 2; + acomp.forceHardware = NO; + acomp.noHardware = YES; + _compressor = acomp; + return YES; + } + + if (self.useAppleProRes) + { + AppleProResCompressor *acomp = [[AppleProResCompressor alloc] init]; + _compressor = acomp; + return YES; + } + + if (self.usex264) + { + x264Compressor *xcomp = [[x264Compressor alloc] init]; + xcomp.use_cbr = NO; + xcomp.crf = 10; + _compressor = xcomp; + return YES; + } + + return NO; //??? +} + + + +-(id )getConfigurationView +{ + return [[CSInstantRecorderCompressorViewController alloc] init]; +} + +@end diff --git a/CocoaSplit/Compressor/CSInstantRecorderCompressorViewController.h b/CocoaSplit/Compressor/CSInstantRecorderCompressorViewController.h new file mode 100644 index 00000000..0ed8cf7f --- /dev/null +++ b/CocoaSplit/Compressor/CSInstantRecorderCompressorViewController.h @@ -0,0 +1,24 @@ +// +// CSInstantRecorderCompressorViewController.h +// CocoaSplit +// +// Created by Zakk on 4/17/16. +// Copyright © 2016 Zakk. All rights reserved. +// + +#import +#import "CSCompressorViewControllerProtocol.h" +#import "CSIRCompressor.h" + + +@interface CSInstantRecorderCompressorViewController : NSViewController +@property (strong) CSIRCompressor *compressor; +@property (strong) NSObjectController *compressorController; +@property (strong) NSString *encoderName; + + +- (IBAction)selectCompressorType:(id)sender; + +@end + + diff --git a/CocoaSplit/Compressor/CSInstantRecorderCompressorViewController.m b/CocoaSplit/Compressor/CSInstantRecorderCompressorViewController.m new file mode 100644 index 00000000..fda6f00d --- /dev/null +++ b/CocoaSplit/Compressor/CSInstantRecorderCompressorViewController.m @@ -0,0 +1,57 @@ +// +// CSInstantRecorderCompressorViewController.m +// CocoaSplit +// +// Created by Zakk on 4/17/16. +// Copyright © 2016 Zakk. All rights reserved. +// + +#import "CSInstantRecorderCompressorViewController.h" + +@interface CSInstantRecorderCompressorViewController () + +@end + +@implementation CSInstantRecorderCompressorViewController + + +-(instancetype)init +{ + return [self initWithNibName:@"CSInstantRecorderCompressorViewController" bundle:nil]; +} + + +- (void)viewDidLoad { + [super viewDidLoad]; + + // Do view setup here. +} + +- (IBAction)selectCompressorType:(NSButton *)sender { + + CSIRCompressor *tcomp = self.compressor; + tcomp.usex264 = NO; + tcomp.useAppleH264 = NO; + tcomp.useAppleProRes = NO; + tcomp.useNone = NO; + + switch (sender.tag) + { + case 0: + //useNone + tcomp.useNone = YES; + break; + case 1: + tcomp.useAppleH264 = YES; + break; + case 2: + tcomp.useAppleProRes = YES; + break; + case 3: + tcomp.usex264 = YES; + break; + default: + break; + } +} +@end diff --git a/CocoaSplit/Compressor/CSInstantRecorderCompressorViewController.xib b/CocoaSplit/Compressor/CSInstantRecorderCompressorViewController.xib new file mode 100644 index 00000000..ffcc8b39 --- /dev/null +++ b/CocoaSplit/Compressor/CSInstantRecorderCompressorViewController.xib @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CocoaSplit/Compressor/CompressorBase.h b/CocoaSplit/Compressor/CompressorBase.h index fb52a9fa..0440e240 100644 --- a/CocoaSplit/Compressor/CompressorBase.h +++ b/CocoaSplit/Compressor/CompressorBase.h @@ -7,19 +7,19 @@ // #import -#import "h264Compressor.h" +#import "VideoCompressor.h" +#import "CSCompressorViewControllerProtocol.h" @class captureController; -@interface CompressorBase : NSObject +@interface CompressorBase : NSObject { NSMutableArray *_audioBuffer; } -@property (strong) CaptureController *settingsController; @property (strong) NSMutableDictionary *outputs; @@ -37,14 +37,15 @@ @property (strong) NSString *compressorType; @property (strong) NSMutableString *name; @property (assign) bool errored; +@property (assign) bool active; +@property (assign) enum AVCodecID codec_id; -(void) reset; -(BOOL) setupResolution:(CVImageBufferRef)withFrame; --(void) addAudioData:(CMSampleBufferRef)audioData; --(void) setAudioData:(CapturedFrameData *)forFrame syncObj:(id)syncObj; +-(id )getConfigurationView; diff --git a/CocoaSplit/Compressor/CompressorBase.m b/CocoaSplit/Compressor/CompressorBase.m index 310dce1e..cecbea79 100644 --- a/CocoaSplit/Compressor/CompressorBase.m +++ b/CocoaSplit/Compressor/CompressorBase.m @@ -26,6 +26,8 @@ self.resolutionOption = @"Use Source"; + self.codec_id = AV_CODEC_ID_H264; + self.outputs = [[NSMutableDictionary alloc] init]; _audioBuffer = [[NSMutableArray alloc] init]; @@ -34,6 +36,11 @@ return self; } +-(instancetype)copyWithZone:(NSZone *)zone +{ + return [[self.class allocWithZone:zone] init]; +} + -(bool) validate:(NSError **)therror { @@ -65,53 +72,6 @@ --(void) addAudioData:(CMSampleBufferRef)audioData -{ - if ([self hasOutputs] && audioData && _audioBuffer) - { - @synchronized(self) - { - [_audioBuffer addObject:(__bridge id)audioData]; - } - } -} - - --(void) setAudioData:(CapturedFrameData *)forFrame syncObj:(id)syncObj -{ - - NSUInteger audioConsumed = 0; - @synchronized(syncObj) - { - NSUInteger audioBufferSize = [_audioBuffer count]; - - for (int i = 0; i < audioBufferSize; i++) - { - CMSampleBufferRef audioData = (__bridge CMSampleBufferRef)[_audioBuffer objectAtIndex:i]; - - CMTime audioTime = CMSampleBufferGetOutputPresentationTimeStamp(audioData); - - - - - if (CMTIME_COMPARE_INLINE(audioTime, <=, forFrame.videoPTS)) - { - - audioConsumed++; - [forFrame.audioSamples addObject:(__bridge id)audioData]; - } else { - break; - } - } - - if (audioConsumed > 0) - { - [_audioBuffer removeObjectsInRange:NSMakeRange(0, audioConsumed)]; - } - - } - -} -(void) reset @@ -127,11 +87,13 @@ -(id) initWithCoder:(NSCoder *)aDecoder { + self.codec_id = AV_CODEC_ID_H264; return self; } + -(bool) compressFrame:(CapturedFrameData *)imageBuffer { return YES; @@ -147,16 +109,26 @@ -(void) addOutput:(OutputDestination *)destination { - [self.outputs setObject:destination forKey:destination.name]; + + + [self.outputs setObject:destination forKey:[NSValue valueWithPointer:(__bridge const void * _Nullable)(destination)]]; + self.active = YES; } +-(NSInteger)outputCount +{ + return self.outputs.count; +} + + -(void) removeOutput:(OutputDestination *)destination { - [self.outputs removeObjectForKey:destination.name]; + [self.outputs removeObjectForKey:[NSValue valueWithPointer:(__bridge const void * _Nullable)(destination)]]; if (self.outputs.count == 0) { [self reset]; + self.active = NO; } } @@ -172,6 +144,7 @@ self.working_height = self.height; self.working_width = self.width; + if (!self.resolutionOption || [self.resolutionOption isEqualToString:@"None"]) { if (!(self.working_height > 0) || !(self.working_width > 0)) @@ -187,6 +160,7 @@ { self.working_height = (int)CVPixelBufferGetHeight(withFrame); self.working_width = (int)CVPixelBufferGetWidth(withFrame); + } else if ([self.resolutionOption isEqualToString:@"Preserve AR"]) { float inputAR = (float)CVPixelBufferGetWidth(withFrame) / (float)CVPixelBufferGetHeight(withFrame); int newWidth; @@ -210,6 +184,10 @@ return YES; } +-(id )getConfigurationView +{ + return nil; +} @end diff --git a/CocoaSplit/Compressor/h264Compressor.h b/CocoaSplit/Compressor/VideoCompressor.h similarity index 76% rename from CocoaSplit/Compressor/h264Compressor.h rename to CocoaSplit/Compressor/VideoCompressor.h index c4b71287..85015424 100644 --- a/CocoaSplit/Compressor/h264Compressor.h +++ b/CocoaSplit/Compressor/VideoCompressor.h @@ -1,5 +1,5 @@ // -// h264Compressor.h +// VideoCompressor.h // streamOutput // // Created by Zakk on 3/17/13. @@ -9,6 +9,8 @@ #import #import #import "CapturedFrameData.h" +#import "libavformat/avformat.h" +#import "CSCompressorViewControllerProtocol.h" //#import "OutputDestination.h" @@ -18,7 +20,7 @@ @protocol ControllerProtocol; -@protocol h264Compressor +@protocol VideoCompressor //compressFrame is expected to be non-blocking. Create a serial dispatch queue if the underlying compressor //is blocking @@ -31,7 +33,6 @@ -@property (strong) CaptureController *settingsController; @property (assign) bool isNew; @property (strong) NSMutableString *name; @property (strong) NSString *compressorType; @@ -39,14 +40,17 @@ @property (assign) int height; @property (strong) NSString *resolutionOption; @property (assign) bool errored; - +@property (assign) bool active; +@property (assign) enum AVCodecID codec_id; -(void) addOutput:(id)destination; -(void) removeOutput:(id)destination; -(bool) hasOutputs; +-(NSInteger) outputCount; -(void) reset; -(bool) validate:(NSError **)therror; -(void) addAudioData:(CMSampleBufferRef)audioData; +-(id )getConfigurationView; diff --git a/CocoaSplit/Compressor/x264Compressor.h b/CocoaSplit/Compressor/x264Compressor.h index e9e04fc4..8d4efaae 100644 --- a/CocoaSplit/Compressor/x264Compressor.h +++ b/CocoaSplit/Compressor/x264Compressor.h @@ -11,11 +11,12 @@ #import "libavcodec/avcodec.h" #import "libswscale/swscale.h" #import -#import "h264Compressor.h" +#import "VideoCompressor.h" #import "CaptureController.h" #import #import "CapturedFrameData.h" #import "CompressorBase.h" +#import "CSPluginServices.h" #import "x264.h" @@ -44,7 +45,6 @@ @property (strong) NSMutableArray *x264profiles; -@property (strong) CaptureController *settingsController; @property (strong) NSString *preset; diff --git a/CocoaSplit/Compressor/x264Compressor.m b/CocoaSplit/Compressor/x264Compressor.m index 8bea20f7..bdd733f3 100644 --- a/CocoaSplit/Compressor/x264Compressor.m +++ b/CocoaSplit/Compressor/x264Compressor.m @@ -9,6 +9,7 @@ #import "x264Compressor.h" #import "OutputDestination.h" #import +#import "CSx264CompressorViewController.h" @@ -25,7 +26,6 @@ copy.x264presets = self.x264presets; copy.x264profiles = self.x264profiles; - copy.settingsController = self.settingsController; copy.isNew = self.isNew; copy.name = self.name; @@ -185,11 +185,6 @@ return NO; } - if (!self.settingsController) - { - return NO; - } - if (!_av_codec && !self.errored) { @@ -215,18 +210,20 @@ CVPixelBufferRetain(frameData.videoFrame); } - [self setAudioData:frameData syncObj:self]; dispatch_async(_compressor_queue, ^{ - - if (frameData.frameNumber == 1) + @autoreleasepool { + + + if (_next_keyframe_time == 0.0f) { _next_keyframe_time = frameData.frameTime; } BOOL isKeyFrame = NO; + if (frameData.frameTime >= _next_keyframe_time) { isKeyFrame = YES; @@ -253,7 +250,7 @@ VTSessionSetProperty(_vtpt_ref, kVTPixelTransferPropertyKey_ScalingMode, kVTScalingMode_Letterbox); } - int64_t usePts = av_rescale_q(pts.value, (AVRational){1,1000000}, _av_codec_ctx->time_base); + int64_t usePts = av_rescale_q(pts.value, (AVRational){1,1000}, _av_codec_ctx->time_base); if (_last_pts > 0 && usePts <= _last_pts) { @@ -348,7 +345,7 @@ frameData.avcodec_ctx = _av_codec_ctx; frameData.avcodec_pkt = pkt; - + frameData.isKeyFrame = pkt->flags & AV_PKT_FLAG_KEY; for (id dKey in self.outputs) { @@ -367,7 +364,7 @@ //av_free_packet(pkt); //av_free(pkt); - + } }); return YES; @@ -413,13 +410,6 @@ - NSLog(@"IN COMPRESSOR SETUP"); - if (!self.settingsController) - { - return NO; - } - - NSString *useAdvancedSettings = self.advancedSettings.copy; @@ -436,6 +426,8 @@ return NO; } + double captureFPS = [CSPluginServices sharedPluginServices].currentFPS; + _next_keyframe_time = 0.0f; _av_codec_ctx = avcodec_alloc_context3(_av_codec); @@ -444,18 +436,23 @@ //_av_codec_ctx->max_b_frames = 0; _av_codec_ctx->width = self.working_width; _av_codec_ctx->height = self.working_height; - _av_codec_ctx->time_base.num = 1000000; + _av_codec_ctx->time_base.num = 1; + _av_codec_ctx->time_base.den = 1000; + + - _av_codec_ctx->time_base.den = self.settingsController.captureFPS*1000000; _av_codec_ctx->pix_fmt = PIX_FMT_YUV420P; int real_keyframe_interval = 0; + + + if (!self.keyframe_interval) { - real_keyframe_interval = self.settingsController.captureFPS*2; + real_keyframe_interval = captureFPS*2; } else { - real_keyframe_interval = self.settingsController.captureFPS*self.keyframe_interval; + real_keyframe_interval = captureFPS*self.keyframe_interval; } @@ -479,8 +476,7 @@ } else { - //what did we learn today? Don't believe shit you read in forum posts... - //_av_codec_ctx->rc_buffer_size = ((1/self.settingsController.captureFPS)*self.settingsController.captureVideoAverageBitrate)*1000; + _av_codec_ctx->bit_rate = self.vbv_maxrate*1000; @@ -490,7 +486,6 @@ } else { useAdvancedSettings = [useAdvancedSettings stringByAppendingString:@":filler=1"]; } - //av_dict_set(&opts, "nal-hrd", "cbr", 0); } _av_codec_ctx->flags |= CODEC_FLAG_GLOBAL_HEADER; @@ -542,6 +537,7 @@ } + _sws_ctx = NULL; _audioBuffer = [[NSMutableArray alloc] init]; @@ -563,6 +559,10 @@ [super setNilValueForKey:key]; } +-(id )getConfigurationView +{ + return [[CSx264CompressorViewController alloc] init]; +} @end diff --git a/CocoaSplit/ControllerProtocol.h b/CocoaSplit/ControllerProtocol.h index 61a2242b..cf7244fb 100644 --- a/CocoaSplit/ControllerProtocol.h +++ b/CocoaSplit/ControllerProtocol.h @@ -10,7 +10,7 @@ #import "libavformat/avformat.h" #import "CapturedFrameData.h" #import -#import "h264Compressor.h" +#import "VideoCompressor.h" #import "InputSource.h" @@ -24,19 +24,9 @@ @property (readonly) int audioSamplerate; @property (assign) BOOL captureRunning; -@property int captureVideoMaxKeyframeInterval; -@property int captureVideoMaxBitrate; -@property int captureVideoAverageBitrate; -@property NSString *x264preset; -@property NSString *x264profile; -@property NSString *x264tune; -@property NSString *vtcompressor_profile; -@property int x264crf; -@property BOOL videoCBR; @property (assign) int maxOutputPending; @property (assign) int maxOutputDropped; -@property NSString *imageDirectory; -@property (strong) id selectedCompressor; +@property (strong) id selectedCompressor; @property (strong) NSMutableDictionary *compressors; diff --git a/CocoaSplit/CreateLayoutViewController.h b/CocoaSplit/CreateLayoutViewController.h index 8df6b771..7632f1fa 100644 --- a/CocoaSplit/CreateLayoutViewController.h +++ b/CocoaSplit/CreateLayoutViewController.h @@ -8,15 +8,22 @@ #import #import "SourceLayout.h" -#import "CaptureController.h" + + @interface CreateLayoutViewController : NSViewController -@property (weak) CaptureController *controller; @property (weak) NSPopover *popover; @property (strong) SourceLayout *sourceLayout; +@property (assign) bool createDialog; + + +@property (assign) int canvas_width; +@property (assign) int canvas_height; - (IBAction)createButtonClicked:(id)sender; +-(instancetype) initForBuiltin; + @end diff --git a/CocoaSplit/CreateLayoutViewController.m b/CocoaSplit/CreateLayoutViewController.m index 31a275f4..2ceabcb7 100644 --- a/CocoaSplit/CreateLayoutViewController.m +++ b/CocoaSplit/CreateLayoutViewController.m @@ -7,12 +7,15 @@ // #import "CreateLayoutViewController.h" +#import "CaptureController.h" +#import "AppDelegate.h" @interface CreateLayoutViewController () @end @implementation CreateLayoutViewController +@synthesize sourceLayout = _sourceLayout; -(instancetype) init @@ -21,10 +24,18 @@ } +-(instancetype) initForBuiltin +{ + return [self initWithNibName:@"EditBuiltinLayoutView" bundle:nil]; +} + + - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; if (self) { + _createDialog = NO; + // Initialization code here. } return self; @@ -38,7 +49,17 @@ if (self.sourceLayout) { - [self.controller addLayoutFromBase:self.sourceLayout]; + [self.sourceLayout updateCanvasWidth:self.canvas_width height:self.canvas_height]; + } + + + if (self.sourceLayout && self.createDialog) + { + AppDelegate *appDel = NSApp.delegate; + + CaptureController *controller = appDel.captureController; + + [controller addLayoutFromBase:self.sourceLayout]; } [self.popover close]; @@ -47,9 +68,32 @@ -(void)popoverDidClose:(NSNotification *)notification { + + AppDelegate *appDel = NSApp.delegate; + + CaptureController *controller = appDel.captureController; + self.popover.contentViewController = nil; + //This is only relevant if we're a custom edit popup for staging/live, but just do it unconditionally because reasons/lazy + [controller updateFrameIntervals]; } +-(SourceLayout *)sourceLayout +{ + return _sourceLayout; +} + + +-(void) setSourceLayout:(SourceLayout *)sourceLayout +{ + _sourceLayout = sourceLayout; + if (_sourceLayout) + { + self.canvas_width = sourceLayout.canvas_width; + self.canvas_height = sourceLayout.canvas_height; + } +} + @end diff --git a/CocoaSplit/CreateLayoutViewController.xib b/CocoaSplit/CreateLayoutViewController.xib index 54ead47a..7c216821 100644 --- a/CocoaSplit/CreateLayoutViewController.xib +++ b/CocoaSplit/CreateLayoutViewController.xib @@ -1,8 +1,8 @@ - + - + @@ -13,69 +13,30 @@ - + - + - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -83,7 +44,7 @@ - + @@ -93,8 +54,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - + diff --git a/CocoaSplit/FFMpegTask.h b/CocoaSplit/FFMpegTask.h index 4ebdc660..cefded1c 100644 --- a/CocoaSplit/FFMpegTask.h +++ b/CocoaSplit/FFMpegTask.h @@ -11,18 +11,17 @@ #import "libavformat/avformat.h" #import "CapturedFrameData.h" #import "CaptureController.h" +#import "libavutil/opt.h" + #define AUDIO_BUFFER_SIZE 1000 + + + @interface FFMpegTask : NSObject { - - - - - dispatch_queue_t _stream_dispatch; - AVFormatContext *_av_fmt_ctx; AVStream *_av_video_stream; AVStream *_av_audio_stream; @@ -30,8 +29,6 @@ char *_audio_extradata; size_t _audio_extradata_size; - CFAbsoluteTime _input_frame_timestamp; - CFAbsoluteTime _output_frame_timestamp; int _input_framecnt; int _output_framecnt; int _output_bytes; @@ -43,10 +40,10 @@ } --(void) writeVideoSampleBuffer:(CapturedFrameData *)frameData; --(void) writeAudioSampleBuffer:(CMSampleBufferRef)theBuffer presentationTimeStamp:(CMTime)pts; --(void) writeAVPacket:(CapturedFrameData *)frameData; --(void) writeEncodedData:(CapturedFrameData *)frameData; +-(BOOL) writeVideoSampleBuffer:(CapturedFrameData *)frameData; +-(BOOL) writeAudioSampleBuffer:(CMSampleBufferRef)theBuffer presentationTimeStamp:(CMTime)pts; +-(BOOL) writeAVPacket:(CapturedFrameData *)frameData; +-(BOOL) writeEncodedData:(CapturedFrameData *)frameData; -(void) updateOutputStats; -(void) updateInputStats; @@ -60,7 +57,6 @@ @property (assign) BOOL init_done; -@property (assign) BOOL active; @property (strong) NSString *stream_output; @property (strong) NSString *stream_format; @property (assign) int framerate; @@ -75,6 +71,8 @@ @property (assign) double output_bitrate; @property (assign) int dropped_frame_count; @property (assign) BOOL errored; +@property (assign) enum AVCodecID video_codec_id; + @property (strong) CaptureController *settingsController; diff --git a/CocoaSplit/FFMpegTask.m b/CocoaSplit/FFMpegTask.m index 782e0b04..0b108d3a 100644 --- a/CocoaSplit/FFMpegTask.m +++ b/CocoaSplit/FFMpegTask.m @@ -121,6 +121,7 @@ void getAudioExtradata(char *cookie, char **buffer, size_t *size) { CMAudioFormatDescriptionRef audio_fmt; + audio_fmt = CMSampleBufferGetFormatDescription(theBuffer); if (!audio_fmt) { @@ -137,18 +138,12 @@ void getAudioExtradata(char *cookie, char **buffer, size_t *size) } --(void) writeEncodedData:(CapturedFrameData *)frameDataIn +-(BOOL) writeEncodedData:(CapturedFrameData *)frameDataIn { + CapturedFrameData *frameData = frameDataIn; - - if (!self.active) - { - return; - } - - if (!_audio_extradata && [frameData.audioSamples count] > 0) { @@ -157,12 +152,6 @@ void getAudioExtradata(char *cookie, char **buffer, size_t *size) [self extractAudioCookie:audioSample]; } - if (!_stream_dispatch) - { - _stream_dispatch = dispatch_queue_create("FFMpeg Stream Dispatch", NULL); - _pending_frame_count = 0; - } - if (!_av_video_stream && _audio_extradata) { @@ -170,58 +159,52 @@ void getAudioExtradata(char *cookie, char **buffer, size_t *size) { [self initStatsValues]; } else { - return; + return NO; } } if (!_av_video_stream || !_av_audio_stream) { - //!? - return; + //This is a lie. We probably have only received video frames and are waiting for audio. Just pretend we did something. + return YES; } //If we made it here, we have all the metadata and av* stuff created, so start sending data. - for (id object in frameData.audioSamples) { CMSampleBufferRef audioSample = (__bridge CMSampleBufferRef)object; + [self writeAudioSampleBuffer:audioSample presentationTimeStamp:CMSampleBufferGetOutputPresentationTimeStamp(audioSample)]; //CFRelease(audioSample); } + BOOL ret_status = YES; + if (frameData.encodedSampleBuffer) { - [self writeVideoSampleBuffer:frameData]; + ret_status = [self writeVideoSampleBuffer:frameData]; } else if (frameData.avcodec_pkt) { - [self writeAVPacket:frameData]; + ret_status = [self writeAVPacket:frameData]; } + return ret_status; } --(void) writeAudioSampleBuffer:(CMSampleBufferRef)theBuffer presentationTimeStamp:(CMTime)pts +-(BOOL) writeAudioSampleBuffer:(CMSampleBufferRef)theBuffer presentationTimeStamp:(CMTime)pts { - if ([self shouldDropFrame]) - { - return; - } - CFRetain(theBuffer); + BOOL ret_val = YES; if (_av_audio_stream && (self.init_done == YES)) { - dispatch_async(_stream_dispatch, ^{ - if (!self.active) - { - return; - } CMBlockBufferRef blockBufferRef = CMSampleBufferGetDataBuffer(theBuffer); size_t buffer_length; @@ -249,32 +232,40 @@ void getAudioExtradata(char *cookie, char **buffer, size_t *size) - if (av_interleaved_write_frame(_av_fmt_ctx, &pkt) < 0) { - NSLog(@"AV WRITE AUDIO failed for %@", self.stream_output); + ret_val = NO; [self stopProcess]; } - //CMSampleBufferInvalidate(theBuffer); - CFRelease(theBuffer); - }); - /*} else if (!_audio_extradata) { - - CMAudioFormatDescriptionRef audio_fmt; - audio_fmt = CMSampleBufferGetFormatDescription(theBuffer); - void *audio_tmp; - if (!audio_fmt) - return; - - - - audio_tmp = (char *)CMAudioFormatDescriptionGetMagicCookie(audio_fmt, &_audio_extradata_size); - - if (audio_tmp) - { - getAudioExtradata(audio_tmp, &_audio_extradata, &_audio_extradata_size); - } - */ + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ + CFRelease(theBuffer); + }); + } + + return ret_val; +} + +-(void) setVideoFormatOptions:(AVFormatContext *)ctx +{ + + + ctx->avoid_negative_ts = AVFMT_AVOID_NEG_TS_MAKE_ZERO; + + + AVOutputFormat *ofmt = ctx->oformat; + + if (!ofmt) + { + return; + } + + const char *fmt_name = ofmt->name; + + if (!strcasecmp(fmt_name, "mmmmmov")) + { + av_opt_set_int(ctx->priv_data, "frag_duration", 10000000, 0); + } else if (!strcasecmp(fmt_name, "segment")) { + av_opt_set(ctx->priv_data, "reset_timestamps", "1", AV_OPT_SEARCH_CHILDREN); } } @@ -294,35 +285,43 @@ void getAudioExtradata(char *cookie, char **buffer, size_t *size) if (!_av_fmt_ctx) { - NSLog(@"No av_fmt_ctx"); return NO; } + [self setVideoFormatOptions:_av_fmt_ctx]; + av_out_fmt = _av_fmt_ctx->oformat; + _av_video_stream = avformat_new_stream(_av_fmt_ctx, 0); if (!_av_video_stream) { - NSLog(@"No av_video_stream"); return NO; } AVCodecContext *c_ctx = _av_video_stream->codec; - c_ctx->codec_type = AVMEDIA_TYPE_VIDEO; - c_ctx->codec_id = AV_CODEC_ID_H264; + + //c_ctx->codec_type = AVMEDIA_TYPE_VIDEO; + //c_ctx->codec_id = self.video_codec_id; + /* _av_video_stream->time_base.num = 1000000; _av_video_stream->time_base.den = self.framerate*1000000; + */ + //_av_video_stream->time_base.num = 1; + //_av_video_stream->time_base.den = 1000; + + + _av_audio_stream = avformat_new_stream(_av_fmt_ctx, 0); if (!_av_audio_stream) { - NSLog(@"No av_audio_stream"); return NO; } @@ -332,8 +331,13 @@ void getAudioExtradata(char *cookie, char **buffer, size_t *size) a_ctx->codec_type = AVMEDIA_TYPE_AUDIO; a_ctx->codec_id = AV_CODEC_ID_AAC; - _av_audio_stream->time_base.num = 1000000; - _av_audio_stream->time_base.den = self.framerate*1000000; + /*_av_audio_stream->time_base.num = 100000; + _av_audio_stream->time_base.den = self.framerate*100000; + */ + + _av_audio_stream->time_base.num = 1; + _av_audio_stream->time_base.den = _samplerate; + a_ctx->sample_rate = _samplerate; a_ctx->bit_rate = _audio_bitrate; a_ctx->channels = 2; @@ -360,15 +364,25 @@ void getAudioExtradata(char *cookie, char **buffer, size_t *size) atoms = CMFormatDescriptionGetExtension(fmt, kCMFormatDescriptionExtension_SampleDescriptionExtensionAtoms); avccKey = CFSTR("avcC"); - NSLog(@"ATOMS %@", atoms); - avcc_data = CFDictionaryGetValue(atoms, avccKey); - avcc_size = CFDataGetLength(avcc_data); - c_ctx->extradata = malloc(avcc_size); + if (atoms) + { + avcc_data = CFDictionaryGetValue(atoms, avccKey); + avcc_size = CFDataGetLength(avcc_data); + c_ctx->extradata = malloc(avcc_size); - CFDataGetBytes(avcc_data, CFRangeMake(0,avcc_size), c_ctx->extradata); + CFDataGetBytes(avcc_data, CFRangeMake(0,avcc_size), c_ctx->extradata); - c_ctx->extradata_size = (int)avcc_size; + c_ctx->extradata_size = (int)avcc_size; + } + c_ctx->codec_type = AVMEDIA_TYPE_VIDEO; + c_ctx->codec_id = self.video_codec_id; + } else if (codec_ctx) { + + avcodec_copy_context(_av_video_stream->codec, codec_ctx); + _av_video_stream->time_base = av_add_q(codec_ctx->time_base, (AVRational){0,1}); + _av_video_stream->codec->codec = codec_ctx->codec; + self.width = codec_ctx->width; self.height = codec_ctx->height; @@ -419,217 +433,84 @@ void getAudioExtradata(char *cookie, char **buffer, size_t *size) -(void) updateInputStats { - CFAbsoluteTime time_now = CFAbsoluteTimeGetCurrent(); - double calculated_input_framerate = _input_framecnt / (time_now - _input_frame_timestamp); - _input_framecnt = 0; - _input_frame_timestamp = time_now; - self.input_framerate = calculated_input_framerate; - self.buffered_frame_count = _pending_frame_count; - self.buffered_frame_size = _pending_frame_size; self.dropped_frame_count = _dropped_frames; - } -(void) updateOutputStats { - CFAbsoluteTime time_now = CFAbsoluteTimeGetCurrent(); - double calculated_output_framerate = _output_framecnt / (time_now - _output_frame_timestamp); - double calculated_output_bitrate = (_output_bytes / (time_now - _output_frame_timestamp))*8; _output_framecnt = 0; _output_bytes = 0; - _output_frame_timestamp = time_now; - - - self.output_framerate = calculated_output_framerate; - self.output_bitrate = calculated_output_bitrate; - } -(void) initStatsValues { - CFAbsoluteTime time_now = CFAbsoluteTimeGetCurrent(); _input_framecnt = 0; - _input_frame_timestamp = time_now; _output_framecnt = 0; _output_bytes = 0; - _output_frame_timestamp = time_now; } -//(AVPacket *)pkt codec_ctx:(AVCodecContext *)codec_ctx --(void) writeAVPacket:(CapturedFrameData *)frameData +-(BOOL) writeAVPacket:(CapturedFrameData *)frameData { AVPacket *pkt = frameData.avcodec_pkt; - if (!_stream_dispatch) - { - _stream_dispatch = dispatch_queue_create("FFMpeg Stream Dispatch", NULL); - _pending_frame_count = 0; - } - - - _input_framecnt++; - - - @synchronized(self) - { - _pending_frame_count++; - _pending_frame_size += pkt->size; - } - - if ([self shouldDropFrame]) - { - _dropped_frames++; - _consecutive_dropped_frames++; - return; - } else { - _consecutive_dropped_frames = 0; - } - - if ([self resetOutputIfNeeded]) - { - return; - } - - - - - - AVPacket *p = av_malloc(sizeof (AVPacket)); av_init_packet(p); av_packet_ref(p, pkt); - dispatch_async(_stream_dispatch, ^{ + av_packet_rescale_ts(p, frameData.avcodec_ctx->time_base, _av_video_stream->time_base); + + /* + if (p->pts != AV_NOPTS_VALUE) + { - if (!self.active) - { - return; - } - - if (p->pts != AV_NOPTS_VALUE) - { - p->pts = av_rescale_q(p->pts, frameData.avcodec_ctx->time_base, _av_video_stream->time_base); - } - - if (p->dts != AV_NOPTS_VALUE) - { - p->dts = av_rescale_q(p->dts, frameData.avcodec_ctx->time_base, _av_video_stream->time_base); - } - - - - - p->stream_index = _av_video_stream->index; - - int packet_size = p->size; - /* Write the compressed frame to the media file. */ - if (av_interleaved_write_frame(_av_fmt_ctx, p) < 0) - { - NSLog(@"INTERLEAVED WRITE FRAME FAILED FOR %@ frame number %lld", self.stream_output, frameData.frameNumber); - } - - - //av_free_packet(p); - //av_free(p); - _output_framecnt++; - _output_bytes += packet_size; - @synchronized(self) - { - _pending_frame_count--; - _pending_frame_size -= packet_size; - } - }); + p->pts = av_rescale_q(p->pts, frameData.avcodec_ctx->time_base, _av_video_stream->time_base); + } + + if (p->dts != AV_NOPTS_VALUE) + { + p->dts = av_rescale_q(p->dts, frameData.avcodec_ctx->time_base, _av_video_stream->time_base); + }*/ + + + p->stream_index = _av_video_stream->index; + + /* Write the compressed frame to the media file. */ + BOOL write_status = YES; + if (av_interleaved_write_frame(_av_fmt_ctx, p) < 0) + { + NSLog(@"INTERLEAVED WRITE FRAME FAILED FOR %@ frame number %lld", self.stream_output, frameData.frameNumber); + write_status = NO; + } + + return write_status; } --(void) writeVideoSampleBuffer:(CapturedFrameData *)frameData + +-(BOOL) writeVideoSampleBuffer:(CapturedFrameData *)frameData { - if (!frameData || !frameData.encodedSampleBuffer) { - return; + return NO; } - /* - if (!_stream_dispatch) - { - _stream_dispatch = dispatch_queue_create("FFMpeg Stream Dispatch", NULL); - _pending_frame_count = 0; - } - - */ - - - - _input_framecnt++; - - if ([self shouldDropFrame]) - { - _dropped_frames++; - _consecutive_dropped_frames++; - return; - } else { - _consecutive_dropped_frames = 0; - } - - if ([self resetOutputIfNeeded]) - { - return; - } - - CFRetain(frameData.encodedSampleBuffer); - CMBlockBufferRef tmp_sample_data = CMSampleBufferGetDataBuffer(frameData.encodedSampleBuffer); - size_t data_length = CMBlockBufferGetDataLength(tmp_sample_data); - - - @synchronized(self) - { - _pending_frame_count++; - _pending_frame_size += data_length; - } - - dispatch_async(_stream_dispatch, ^{ - CMSampleBufferRef theBuffer = frameData.encodedSampleBuffer; - if (!self.active) - { - return; - } - - - /* - if (!_av_video_stream) - { - if (_audio_extradata) - { - if (![self createAVFormatOut:theBuffer codec_ctx:nil]) - { - return; - } - [self initStatsValues]; - - } else { - @synchronized(self) { _pending_frame_count--; _pending_frame_size -= data_length;} - return; - } - } - */ CMBlockBufferRef my_buffer; char *sampledata; size_t offset_length; @@ -652,70 +533,35 @@ void getAudioExtradata(char *cookie, char **buffer, size_t *size) pkt.size = (int)buffer_length; - pkt.dts = av_rescale_q(CMSampleBufferGetDecodeTimeStamp(theBuffer).value, (AVRational) {1.0, CMSampleBufferGetDecodeTimeStamp(theBuffer).timescale}, _av_video_stream->time_base); + + pkt.dts = av_rescale_q(CMSampleBufferGetDecodeTimeStamp(theBuffer).value, (AVRational) {1.0, CMSampleBufferGetDecodeTimeStamp(theBuffer). + timescale}, _av_video_stream->time_base); pkt.pts = av_rescale_q(CMSampleBufferGetPresentationTimeStamp(theBuffer).value, (AVRational) {1.0, CMSampleBufferGetPresentationTimeStamp(theBuffer).timescale}, _av_video_stream->time_base); - - - - - - //kt.pts = CMSampleBufferGetPresentationTimeStamp(theBuffer).value; - - - - - if ([self isBufferKeyframe:theBuffer]) + if (frameData.isKeyFrame) { pkt.flags |= AV_PKT_FLAG_KEY; } - + + BOOL send_status = YES; + if (av_interleaved_write_frame(_av_fmt_ctx, &pkt) < 0) { NSLog(@"VIDEO WRITE FRAME failed for %@", self.stream_output); + send_status = NO; //[self stopProcess]; } - - _output_framecnt++; - _output_bytes += pkt.size; - //CMSampleBufferInvalidate(theBuffer); + CFRelease(theBuffer); - @synchronized(self) - { - _pending_frame_count--; - _pending_frame_size -= pkt.size; - } - }); - - return; + return send_status; } --(BOOL) isBufferKeyframe:(CMSampleBufferRef)theBuffer -{ - - CFArrayRef sample_attachments; - BOOL result = NO; - - sample_attachments = CMSampleBufferGetSampleAttachmentsArray(theBuffer, NO); - if (sample_attachments) - { - CFDictionaryRef attach; - CFBooleanRef depends_on_others; - - attach = CFArrayGetValueAtIndex(sample_attachments, 0); - depends_on_others = CFDictionaryGetValue(attach, kCMSampleAttachmentKey_DependsOnOthers); - result = depends_on_others == kCFBooleanFalse; - } - - return result; - -} @@ -724,16 +570,10 @@ void getAudioExtradata(char *cookie, char **buffer, size_t *size) self = [super init]; self.init_done = NO; - self.active = NO; self.errored = NO; av_register_all(); avformat_network_init(); - - - _stream_dispatch = dispatch_queue_create("FFMpeg Stream Dispatch", NULL); - - return self; } @@ -742,18 +582,7 @@ void getAudioExtradata(char *cookie, char **buffer, size_t *size) -(bool)stopProcess { - if (!self.active) - { - return NO; - } - - - self.active = NO; - - dispatch_async(_stream_dispatch, ^{ - [self _internal_stopProcess]; - }); - + [self _internal_stopProcess]; return YES; } @@ -774,12 +603,6 @@ void getAudioExtradata(char *cookie, char **buffer, size_t *size) } - /* - if (_av_video_stream) - av_free(_av_video_stream); - if (_av_audio_stream) - av_free(_av_audio_stream); -*/ _av_fmt_ctx = NULL; _av_video_stream = NULL; _av_audio_stream = NULL; @@ -789,10 +612,6 @@ void getAudioExtradata(char *cookie, char **buffer, size_t *size) //free(_audio_extradata); _audio_extradata = NULL; } - - - _stream_dispatch = nil; - NSLog(@"Stopped FFMPEG"); return YES; diff --git a/CocoaSplit/InputSource.h b/CocoaSplit/InputSource.h index 40d4299e..18a160e9 100644 --- a/CocoaSplit/InputSource.h +++ b/CocoaSplit/InputSource.h @@ -76,6 +76,10 @@ typedef enum resize_style_t { NSDictionary *_constraintAttributeMap; NSArray *_constraintObserveKeys; NSMutableDictionary *_restoredConstraintMap; + NSString *_editedName; + NSDictionary *_undoActionMap; + + } @@ -194,6 +198,12 @@ typedef enum resize_style_t { @property (assign) CGFloat gradientStopX; @property (assign) CGFloat gradientStopY; +@property (assign) CGFloat topLevelWidth; +@property (assign) CGFloat topLevelHeight; +@property (assign) bool needsAdjustment; +@property (assign) bool needsAdjustPosition; +@property (assign) NSInteger refCount; + -(void) updateOrigin:(CGFloat)x y:(CGFloat)y; -(void) positionOrigin:(CGFloat)x y:(CGFloat)y; @@ -222,6 +232,12 @@ typedef enum resize_style_t { -(void)deleteBackgroundFilter:(NSString *)filteruuid; -(void)buildLayerConstraints; -(void)restoreConstraints; +-(void)createUUID; +-(void)updateRotationTransform; +-(void) directSize:(CGFloat)width height:(CGFloat)height; +-(bool)isDifferentInput:(InputSource *)from; +-(void)addedToLayout; + diff --git a/CocoaSplit/InputSource.m b/CocoaSplit/InputSource.m index 2401a082..b5e26830 100644 --- a/CocoaSplit/InputSource.m +++ b/CocoaSplit/InputSource.m @@ -56,7 +56,8 @@ static NSArray *_sourceTypes = nil; { [CATransaction begin]; InputSource *newSource = [[InputSource allocWithZone:zone] init]; - + newSource.name = _editedName; + newSource.videoInput = self.videoInput; [newSource registerVideoInput:self.videoInput]; newSource->_currentLayer = [self.videoInput layerForInput:newSource]; @@ -66,7 +67,7 @@ static NSArray *_sourceTypes = nil; newSource.rotationAngleY = self.rotationAngleY; newSource.rotationAngleX = self.rotationAngleX; newSource.opacity = self.opacity; - newSource.name = self.name; + newSource.depth = self.depth; newSource.crop_top = self.crop_top; newSource.crop_bottom = self.crop_bottom; @@ -116,6 +117,7 @@ static NSArray *_sourceTypes = nil; + -(void) encodeWithCoder:(NSCoder *)aCoder { @@ -124,12 +126,14 @@ static NSArray *_sourceTypes = nil; [aCoder encodeFloat:self.rotationAngleY forKey:@"rotationAngleY"]; [aCoder encodeFloat:self.opacity forKey:@"opacity"]; - [aCoder encodeObject:self.name forKey:@"name"]; + + [aCoder encodeObject:_editedName forKey:@"name"]; [aCoder encodeFloat:self.depth forKey:@"CAdepth"]; [aCoder encodeFloat:self.crop_top forKey:@"CAcrop_top"]; [aCoder encodeFloat:self.crop_bottom forKey:@"CAcrop_bottom"]; [aCoder encodeFloat:self.crop_left forKey:@"CAcrop_left"]; [aCoder encodeFloat:self.crop_right forKey:@"CAcrop_right"]; + [aCoder encodeObject:self.selectedVideoType forKey:@"selectedVideoType"]; [aCoder encodeObject:self.uuid forKey:@"uuid"]; @@ -137,12 +141,21 @@ static NSArray *_sourceTypes = nil; [aCoder encodeFloat:self.scrollYSpeed forKey:@"scrollYSpeed"]; [aCoder encodeInt:self.rotateStyle forKey:@"rotateStyle"]; - if (self.videoInput) { [aCoder encodeObject:self.videoInput forKey:@"videoInput"]; } + if (self.sourceLayout) + { + [aCoder encodeFloat:self.canvas_width forKey:@"topLevelWidth"]; + [aCoder encodeFloat:self.canvas_height forKey:@"topLevelHeight"]; + } else { + [aCoder encodeFloat:_topLevelWidth forKey:@"topLevelWidth"]; + [aCoder encodeFloat:_topLevelHeight forKey:@"topLevelHeight"]; + } + + [aCoder encodeBool:self.doChromaKey forKey:@"doChromaKey"]; [aCoder encodeObject:self.chromaKeyColor forKey:@"chromaKeyColor"]; [aCoder encodeFloat:self.chromaKeyThreshold forKey:@"chromaKeyThreshold"]; @@ -150,13 +163,13 @@ static NSArray *_sourceTypes = nil; [aCoder encodeObject:self.videoSources forKey:@"videoSources"]; [aCoder encodeObject:self.currentEffects forKey:@"currentEffects"]; [aCoder encodeFloat:self.changeInterval forKey:@"changeInterval"]; - [aCoder encodeFloat:self.layer.position.x forKey:@"CAx_pos"]; [aCoder encodeFloat:self.layer.position.y forKey:@"CAy_pos"]; [aCoder encodeFloat:self.layer.bounds.size.width forKey:@"CAdisplay_width"]; [aCoder encodeFloat:self.layer.bounds.size.height forKey:@"CAdisplay_height"]; + [aCoder encodeFloat:self.borderWidth forKey:@"borderWidth"]; [aCoder encodeObject:self.borderColor forKey:@"borderColor"]; [aCoder encodeFloat:self.cornerRadius forKey:@"cornerRadius"]; @@ -166,13 +179,16 @@ static NSArray *_sourceTypes = nil; [aCoder encodeFloat:self.transitionDuration forKey:@"transitionDuration"]; [aCoder encodeObject:self.advancedTransition forKey:@"advancedTransition"]; - [aCoder encodeObject:self.parentInput forKey:@"parentInput"]; + //if we directly encode constraintMap the resulting NSData is not equal to an 'equal' InputSource, so double encode? - [aCoder encodeObject:self.constraintMap forKey:@"constraintMap"]; + NSData *constraintData = [NSKeyedArchiver archivedDataWithRootObject:self.constraintMap]; + + [aCoder encodeObject:constraintData forKey:@"constraintMapData"]; + //[aCoder encodeObject:self.constraintMap forKey:@"constraintMap"]; [aCoder encodeObject:self.startColor forKey:@"gradientStartColor"]; [aCoder encodeObject:self.stopColor forKey:@"gradientStopColor"]; @@ -184,7 +200,6 @@ static NSArray *_sourceTypes = nil; [aCoder encodeObject:self.layer.filters forKey:@"layerFilters"]; - if (_userBackground) { [aCoder encodeObject:self.backgroundColor forKey:@"backgroundColor"]; @@ -231,6 +246,7 @@ static NSArray *_sourceTypes = nil; if (!_userBackground) { self.backgroundColor = nil; + _userBackground = NO; } } @@ -281,15 +297,13 @@ static NSArray *_sourceTypes = nil; if (constraintData) { self.constraintMap = constraintData; + } else if ((constraintData = [aDecoder decodeObjectForKey:@"constraintMapData"])) { + self.constraintMap = [NSKeyedUnarchiver unarchiveObjectWithData:constraintData]; } - - - - _rotationAngle = [aDecoder decodeFloatForKey:@"rotationAngle"]; _rotationAngleX = [aDecoder decodeFloatForKey:@"rotationAngleX"]; _rotationAngleY = [aDecoder decodeFloatForKey:@"rotationAngleY"]; @@ -397,16 +411,6 @@ static NSArray *_sourceTypes = nil; self.cornerRadius = [aDecoder decodeFloatForKey:@"cornerRadius"]; self.layoutPosition = self.layer.frame; - InputSource *parentInput = [aDecoder decodeObjectForKey:@"parentInput"]; - if (parentInput) - { - - [parentInput.layer addSublayer:self.layer]; - [parentInput.attachedInputs addObject:self]; - self.parentInput = parentInput; - - - } self.startColor = [aDecoder decodeObjectForKey:@"gradientStartColor"]; @@ -420,7 +424,15 @@ static NSArray *_sourceTypes = nil; self.gradientStopY = [aDecoder decodeFloatForKey:@"gradientEndPointY"]; - + if ([aDecoder containsValueForKey:@"topLevelWidth"]) + { + _topLevelWidth = [aDecoder decodeFloatForKey:@"topLevelWidth"]; + } + + if ([aDecoder containsValueForKey:@"topLevelHeight"]) + { + _topLevelHeight = [aDecoder decodeFloatForKey:@"topLevelHeight"]; + } self.layer.filters = [aDecoder decodeObjectForKey:@"layerFilters"]; @@ -435,13 +447,36 @@ static NSArray *_sourceTypes = nil; self.transitionEnabled = [aDecoder decodeBoolForKey:@"transitionEnabled"]; } - [CATransaction commit]; + + InputSource *parentInput = [aDecoder decodeObjectForKey:@"parentInput"]; + self.parentInput = parentInput; + + if (self.parentInput) + { + + [self.parentInput.layer addSublayer:self.layer]; + [self.parentInput.attachedInputs addObject:self]; + } + + } return self; } + + +-(void)addedToLayout +{ + if (self.parentInput) + { + + [self.parentInput.layer addSublayer:self.layer]; + [self.parentInput.attachedInputs addObject:self]; + } + +} -(CGRect)globalLayoutPosition { return [self.sourceLayout.rootLayer convertRect:self.layoutPosition fromLayer:self.layer.superlayer]; @@ -451,6 +486,7 @@ static NSArray *_sourceTypes = nil; { forInput.inputSource = self; forInput.isLive = self.is_live; + [forInput addObserver:self forKeyPath:@"captureName" options:NSKeyValueChangeNewKey context:NULL]; [forInput createNewLayerForInput:self]; } @@ -464,6 +500,7 @@ static NSArray *_sourceTypes = nil; forInput.isLive = NO; [forInput removeLayerForInput:self]; + [forInput removeObserver:self forKeyPath:@"captureName"]; } @@ -495,8 +532,14 @@ static NSArray *_sourceTypes = nil; { [CATransaction begin]; + self.name = nil; + _nextImageTime = 0.0f; _currentSourceIdx = 0; + _needsAdjustment = NO; + _needsAdjustPosition = NO; + _topLevelHeight = 0; + _topLevelWidth = 0; self.changeInterval = 20.0f; @@ -516,9 +559,11 @@ static NSArray *_sourceTypes = nil; self.crop_left = 0; self.crop_right = 0; self.videoSources = [[NSMutableArray alloc] init]; + _refCount = 0; self.constraintMap = [NSMutableDictionary dictionary]; + _constraintAttributeMap = @{@"LeftEdge": @(kCAConstraintMinX), @"RightEdge": @(kCAConstraintMaxX), @"TopEdge": @(kCAConstraintMaxY), @@ -579,10 +624,7 @@ static NSArray *_sourceTypes = nil; _multiTransition.removedOnCompletion = YES; - - CFUUIDRef tmpUUID = CFUUIDCreate(NULL); - self.uuid = (__bridge_transfer NSString *)CFUUIDCreateString(NULL, tmpUUID); - CFRelease(tmpUUID); + [self createUUID]; self.layoutPosition = self.layer.frame; @@ -604,13 +646,56 @@ static NSArray *_sourceTypes = nil; [self observeConstraintKeys]; [CATransaction commit]; + _undoActionMap = @{@"name": @"Set Name", + @"crop_top": @"Crop Top", + @"changeInterval": @"Change Interval", + @"gradientStartX": @"Gradient Start Color X", + @"gradientStartY": @"Gradient Start Color Y", + @"gradientStopX": @"Gradient Stop Color X", + @"gradientStopY": @"Gradient Stop Color Y", + @"startColor": @"Gradient Start Color", + @"stopColor": @"Gradient Stop Color", + @"backgroundColor": @"Background Color", + @"borderColor": @"Border Color", + @"cornerRadius": @"Border Corner Radius", + @"borderWidth": @"Border Width", + @"compositingFilterName": @"Composition Filter", + @"rotationAngle": @"Rotation", + @"rotationAngleX": @"X Rotation", + @"rotationAngleY": @"Y Rotation", + @"transitionDuration": @"Effect Duration", + @"transitionDirection": @"Transition Direction", + @"transitionEnabled": @"Enable Transitions", + @"alwaysDisplay": @"Always Show", + @"transitionFilterName": @"Transition Effect", + @"width": @"Width", + @"height": @"Height", + @"opacity": @"Opacity", + @"scrollXSpeed": @"Horizontal Scroll Speed", + @"scrollYSpeed": @"Vertical Scroll Speed", + @"crop_left": @"Crop Left", + @"crop_right": @"Crop Right", + @"crop_bottom": @"Crop Bottom", + @"chromaKeyColor": @"Chroma Key Color", + @"chromaKeySmoothing": @"CK Smoothing", + @"chromaKeyThreshold": @"CK Threshold", + @"rotateStyle": @"Order", + }; + } +-(void)createUUID +{ + CFUUIDRef tmpUUID = CFUUIDCreate(NULL); + self.uuid = (__bridge_transfer NSString *)CFUUIDCreateString(NULL, tmpUUID); + CFRelease(tmpUUID); + +} + -(void)setRotateStyle:(input_rotate_style)rotateStyle { - [self registerUndoForProperty:@"rotateStyle" withAction:@"Order"]; _rotateStyle = rotateStyle; } @@ -623,7 +708,6 @@ static NSArray *_sourceTypes = nil; -(void)setChangeInterval:(float)changeInterval { - [self registerUndoForProperty:@"changeInterval" withAction:@"Change Interval"]; _changeInterval = changeInterval; } @@ -640,7 +724,10 @@ static NSArray *_sourceTypes = nil; for (NSString *base in baseKeys) { - [dict setObject:[NSMutableDictionary dictionaryWithDictionary:@{@"attr": [NSNull null], @"offset": @0}] forKey:base]; + NSMutableDictionary *valDict = [[NSMutableDictionary alloc] init]; + [valDict setObject:[NSNumber numberWithInt:0] forKey:@"attr"]; + [valDict setObject:[NSNumber numberWithInt:0] forKey:@"offset"]; + [dict setObject:valDict forKey:base]; } } @@ -672,7 +759,6 @@ static NSArray *_sourceTypes = nil; -(void)setGradientStartX:(CGFloat)gradientStartX { - [self registerUndoForProperty:@"gradientStartX" withAction:@"Gradient Start Color X"]; self.layer.gradientStartX = gradientStartX; } @@ -683,7 +769,6 @@ static NSArray *_sourceTypes = nil; -(void)setGradientStartY:(CGFloat)gradientStartY { - [self registerUndoForProperty:@"gradientStartY" withAction:@"Gradient Start Color Y"]; self.layer.gradientStartY = gradientStartY; } @@ -694,7 +779,6 @@ static NSArray *_sourceTypes = nil; -(void)setGradientStopX:(CGFloat)gradientStopX { - [self registerUndoForProperty:@"gradientStopX" withAction:@"Gradient Stop Color X"]; self.layer.gradientStopX = gradientStopX; } @@ -705,7 +789,6 @@ static NSArray *_sourceTypes = nil; -(void)setGradientStopY:(CGFloat)gradientStopY { - [self registerUndoForProperty:@"gradientStopY" withAction:@"Gradient Stop Color Y"]; self.layer.gradientStopY = gradientStopY; } @@ -722,7 +805,6 @@ static NSArray *_sourceTypes = nil; -(void)setStartColor:(NSColor *)startColor { - [self registerUndoForProperty:@"startColor" withAction:@"Gradient Start Color"]; self.layer.startColor = startColor; } @@ -735,7 +817,6 @@ static NSArray *_sourceTypes = nil; -(void)setStopColor:(NSColor *)stopColor { - [self registerUndoForProperty:@"stopColor" withAction:@"Gradient Stop Color"]; self.layer.stopColor = stopColor; } @@ -751,7 +832,6 @@ static NSArray *_sourceTypes = nil; -(void)setBackgroundColor:(NSColor *)backgroundColor { - [self registerUndoForProperty:@"backgroundColor" withAction:@"Background Color"]; [CATransaction begin]; _userBackground = YES; @@ -778,7 +858,6 @@ static NSArray *_sourceTypes = nil; -(void)setBorderColor:(NSColor *)borderColor { - [self registerUndoForProperty:@"borderColor" withAction:@"Border Color"]; [CATransaction begin]; self.layer.borderColor = [borderColor CGColor]; @@ -797,7 +876,6 @@ static NSArray *_sourceTypes = nil; -(void)setCornerRadius:(CGFloat)cornerRadius { - [self registerUndoForProperty:@"cornerRadius" withAction:@"Border Corner Radius"]; [CATransaction begin]; self.layer.cornerRadius = cornerRadius; @@ -813,7 +891,6 @@ static NSArray *_sourceTypes = nil; -(void)setBorderWidth:(CGFloat)borderWidth { - [self registerUndoForProperty:@"borderWidth" withAction:@"Border Width"]; [CATransaction begin]; @@ -836,7 +913,6 @@ static NSArray *_sourceTypes = nil; -(void)setCompositingFilterName:(NSString *)compositingFilterName { - [self registerUndoForProperty:@"compositingFilterName" withAction:@"Composition Filter"]; CIFilter *newFilter = nil; if (compositingFilterName) { @@ -852,7 +928,6 @@ static NSArray *_sourceTypes = nil; -(void)setIsMaskLayer:(bool)isMaskLayer { - [self registerUndoForProperty:@"isMaskLayer" withAction:isMaskLayer ? @"Set As Mask" : @"Unset As Mask"]; [self.sourceLayout.undoManager disableUndoRegistration]; if (isMaskLayer) @@ -1255,32 +1330,46 @@ static NSArray *_sourceTypes = nil; } +/* -(NSString *)description { return [NSString stringWithFormat:@"Name: %@ Depth %f", self.name, self.depth]; } + */ -(void)setName:(NSString *)name { - [self registerUndoForProperty:@"name" withAction:@"Set Name"]; + _name = name; + + + _editedName = name; + + if (!_name) + { + if (self.videoInput) + { + _name = self.videoInput.captureName; + _editedName = nil; + } + } + + if (!_name) + { + _name = @"No Name"; + _editedName = nil; + } [CATransaction begin]; self.layer.name = name; [CATransaction commit]; - _name = name; } -(NSString *)name { - if (!_name && self.videoInput) - { - return self.videoInput.captureName; - } - return _name; } @@ -1324,7 +1413,6 @@ static NSArray *_sourceTypes = nil; -(void)setRotationAngle:(float)rotationAngle { - [self registerUndoForProperty:@"rotationAngle" withAction:@"Rotation"]; _rotationAngle = rotationAngle; @@ -1340,7 +1428,6 @@ static NSArray *_sourceTypes = nil; -(void)setRotationAngleX:(float)rotationAngleX { - [self registerUndoForProperty:@"rotationAngleX" withAction:@"X Rotation"]; _rotationAngleX = rotationAngleX; @@ -1355,7 +1442,6 @@ static NSArray *_sourceTypes = nil; -(void)setRotationAngleY:(float)rotationAngleY { - [self registerUndoForProperty:@"rotationAngleY" withAction:@"Y Rotation"]; _rotationAngleY = rotationAngleY; @@ -1388,7 +1474,6 @@ static NSArray *_sourceTypes = nil; -(void)setTransitionDuration:(float)transitionDuration { - [self registerUndoForProperty:@"transitionDuration" withAction:@"Effect Duration"]; _transitionDuration = transitionDuration; } @@ -1399,7 +1484,6 @@ static NSArray *_sourceTypes = nil; } -(void)setTransitionDirection:(NSString *)transitionDirection { - [self registerUndoForProperty:@"transitionDirection" withAction:@"Transition Direction"]; _transitionDirection = transitionDirection; } @@ -1410,7 +1494,6 @@ static NSArray *_sourceTypes = nil; -(void)setTransitionEnabled:(bool)transitionEnabled { - [self registerUndoForProperty:@"transitionEnabled" withAction:@"Enable Transitions"]; [CATransaction begin]; _transitionEnabled = transitionEnabled; @@ -1442,7 +1525,6 @@ static NSArray *_sourceTypes = nil; { - [self registerUndoForProperty:@"alwaysDisplay" withAction:@"Always Show"]; if (alwaysDisplay) { [CATransaction begin]; @@ -1482,7 +1564,6 @@ static NSArray *_sourceTypes = nil; -(void) setTransitionFilterName:(NSString *)transitionFilterName { - [self registerUndoForProperty:@"transitionFilterName" withAction:@"Transition Effect"]; _transitionFilterName = transitionFilterName; if ([transitionFilterName hasPrefix:@"CI"]) { @@ -1594,9 +1675,8 @@ static NSArray *_sourceTypes = nil; -(void)setWidth:(float)width { - [self registerUndoForProperty:@"width" withAction:@"Width"]; _width = width; - [self updateSize:_width height:_height]; + [self directSize:_width height:_height]; } -(float)width @@ -1606,9 +1686,8 @@ static NSArray *_sourceTypes = nil; -(void)setHeight:(float)height { - [self registerUndoForProperty:@"height" withAction:@"Height"]; _height = height; - [self updateSize:_width height:_height]; + [self directSize:_width height:_height]; } -(float)height @@ -1619,7 +1698,7 @@ static NSArray *_sourceTypes = nil; -(void)setX_pos:(float)x_pos { - [self registerUndoForProperty:@"x_pos" withAction:@"Position X"]; + //[self registerUndoForProperty:@"x_pos" withAction:@"Position X"]; if (x_pos > 0 && x_pos <= 1.0) { CALayer *sLayer = self.layer.superlayer; @@ -1638,7 +1717,7 @@ static NSArray *_sourceTypes = nil; -(void)setY_pos:(float)y_pos { - [self registerUndoForProperty:@"y_pos" withAction:@"Position Y"]; + //[self registerUndoForProperty:@"y_pos" withAction:@"Position Y"]; if (y_pos > 0 && y_pos <= 1.0) { CALayer *sLayer = self.layer.superlayer; @@ -1658,6 +1737,31 @@ static NSArray *_sourceTypes = nil; } + +-(void) adjustInputSize: (bool)doPosition +{ + + if (self.topLevelHeight > 0 && self.topLevelWidth > 0) + { + float wRatio = self.canvas_width/self.topLevelWidth; + float hRatio = self.canvas_height/self.topLevelHeight; + float old_x = self.x_pos; + float old_y = self.y_pos; + float new_width = self.layer.bounds.size.width * wRatio; + float new_height = self.layer.bounds.size.height * hRatio; + [self directSize:new_width height:new_height]; + if (doPosition) + { + self.x_pos = old_x*wRatio; + self.y_pos = old_y*hRatio; + } + } + + self.topLevelWidth = self.canvas_width; + self.topLevelHeight = self.canvas_height; + +} + -(void)frameTick { @@ -1686,14 +1790,16 @@ static NSArray *_sourceTypes = nil; } self.layer.allowResize = self.videoInput.allowScaling; - self.layer.sourceLayer = _currentLayer; } [self.videoInput frameTick]; [self.layer frameTick]; - - + if (self.needsAdjustment) + { + [self adjustInputSize:self.needsAdjustPosition]; + self.needsAdjustment = NO; + } } @@ -1741,6 +1847,17 @@ static NSArray *_sourceTypes = nil; +-(void) directSize:(CGFloat)width height:(CGFloat)height +{ + NSRect newLayout = self.layoutPosition; + + newLayout.size.width = width; + newLayout.size.height = height; + + NSRect iRect = NSIntegralRect(newLayout); + + self.layer.bounds = iRect; +} -(void) updateSize:(CGFloat)width height:(CGFloat)height { @@ -1789,7 +1906,15 @@ static NSArray *_sourceTypes = nil; newLayout.size.height = height; self.layer.allowResize = tmpResize; + NSRect iRect = NSIntegralRect(newLayout); + NSRect cFrame = oldLayout; + cFrame.origin = iRect.origin; + + //self.layer.frame = cFrame; + //self.layer.bounds = iRect; + self.layer.frame = NSIntegralRect(newLayout); + self.layer.allowResize = oldResize; } @@ -1837,7 +1962,9 @@ static NSArray *_sourceTypes = nil; toDetach.layer.position = newPosition; [CATransaction commit]; - [self.attachedInputs removeObject:toDetach]; + [[self mutableArrayValueForKey:@"attachedInputs"] removeObject:toDetach]; + [[NSNotificationCenter defaultCenter] postNotificationName:CSNotificationInputDetached object:toDetach userInfo:nil]; + } @@ -1845,6 +1972,7 @@ static NSArray *_sourceTypes = nil; -(void)attachInput:(InputSource *)toAttach { + if (toAttach.parentInput) { if (toAttach.parentInput == self) @@ -1856,8 +1984,10 @@ static NSArray *_sourceTypes = nil; } [toAttach makeSublayerOfLayer:self.layer]; - [self.attachedInputs addObject:toAttach]; + [[self mutableArrayValueForKey:@"attachedInputs"] addObject:toAttach]; toAttach.parentInput = self; + [[NSNotificationCenter defaultCenter] postNotificationName:CSNotificationInputAttached object:toAttach userInfo:nil]; + } @@ -1880,10 +2010,13 @@ static NSArray *_sourceTypes = nil; return ret; } + + -(void)makeSublayerOfLayer:(CALayer *)parentLayer { [CATransaction begin]; + [parentLayer addSublayer:self.layer]; //translate the position to the new sublayers coordinates @@ -1893,13 +2026,11 @@ static NSArray *_sourceTypes = nil; for (CALayer *curr in [layers reverseObjectEnumerator]) { //We start at the layer just before the canvas layer and the point we are converting is in canvas coordinate space - newPosition = [curr convertPoint:newPosition fromLayer:curr.superlayer]; } NSRect oldFrame = self.layer.frame; oldFrame.origin = newPosition; - self.layer.frame = oldFrame; [CATransaction commit]; @@ -1912,12 +2043,15 @@ static NSArray *_sourceTypes = nil; if (self.layer) { + /* NSRect newFrame = self.layer.frame; newFrame.origin.x = x; newFrame.origin.y = y; [CATransaction begin]; - self.layer.frame = NSIntegralRect(newFrame); + self.layer.frame = newFrame; [CATransaction commit]; + */ + [self updateOrigin:x-self.layer.frame.origin.x y:y-self.layer.frame.origin.y]; } @@ -1974,7 +2108,6 @@ static NSArray *_sourceTypes = nil; -(void)setOpacity:(float)opacity { - [self registerUndoForProperty:@"opacity" withAction:@"Opacity"]; _opacity = opacity; [CATransaction begin]; @@ -2013,8 +2146,39 @@ static NSArray *_sourceTypes = nil; _selectedVideoType = selectedVideoType; + self.name = _editedName; + } +-(NSData *)saveData +{ + NSMutableData *saveData = [NSMutableData data]; + NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:saveData]; + + [archiver encodeObject:self forKey:@"root"]; + [archiver finishEncoding]; + return saveData; +} + +-(bool)isDifferentInput:(InputSource *)from +{ + if (![from.uuid isEqualToString:self.uuid]) + { + return YES; + } + + NSData *myData = [self saveData]; + NSData *fromData = [from saveData]; + + + if ([myData isEqualToData:fromData]) + { + return NO; + } + + return YES; +} + -(NSViewController *)sourceConfigurationView { @@ -2043,7 +2207,6 @@ static NSArray *_sourceTypes = nil; -(void)setScrollXSpeed:(float)scrollXSpeed { - [self registerUndoForProperty:@"scrollXSpeed" withAction:@"Horizontal Scroll Speed"]; [CATransaction begin]; self.layer.scrollXSpeed = scrollXSpeed; @@ -2059,7 +2222,6 @@ static NSArray *_sourceTypes = nil; -(void)setScrollYSpeed:(float)scrollYSpeed { - [self registerUndoForProperty:@"scrollYSpeed" withAction:@"Vertical Scroll Speed"]; [CATransaction begin]; self.layer.scrollYSpeed = scrollYSpeed; @@ -2085,27 +2247,13 @@ static NSArray *_sourceTypes = nil; -(NSString *)MIDIShortIdentifier { - if (self.is_live) - { - return @"Live"; - } - - return @"Staging"; + return nil; } -(NSString *)MIDIIdentifier { - NSString *liveStr; - if (self.is_live) - { - liveStr = @"Live"; - } else { - liveStr = @"Staging"; - } - - - return [NSString stringWithFormat:@"%@:%@", liveStr, self.uuid]; + return [NSString stringWithFormat:@"Input:%@", self.uuid]; } -(NSArray *)commandIdentifiers @@ -2445,7 +2593,6 @@ static NSArray *_sourceTypes = nil; -(void) setActive:(bool)active { - [self registerUndoForProperty:@"active" withAction:active ? @"Set Active" : @"Unset Active"]; _active = active; if (self.videoInput) { @@ -2510,7 +2657,6 @@ static NSArray *_sourceTypes = nil; -(void) setCrop_left:(float)crop_left { - [self registerUndoForProperty:@"crop_left" withAction:@"Crop Left"]; if (crop_left < 0) { _crop_left = 0; @@ -2531,7 +2677,6 @@ static NSArray *_sourceTypes = nil; -(void) setCrop_right:(float)crop_right { - [self registerUndoForProperty:@"crop_right" withAction:@"Crop Right"]; if (crop_right < 0) { _crop_right = 0; @@ -2550,7 +2695,6 @@ static NSArray *_sourceTypes = nil; -(void) setCrop_top:(float)crop_top { - [self registerUndoForProperty:@"crop_top" withAction:@"Crop Top"]; if (crop_top < 0) { _crop_top = 0; @@ -2567,9 +2711,30 @@ static NSArray *_sourceTypes = nil; } +-(void)setValue:(id)value forKeyPath:(NSString *)keyPath +{ + + NSString *actionName = _undoActionMap[keyPath]; + if (!actionName) + { + if ([keyPath isEqualToString:@"isMaskLayer"]) + { + actionName = value ? @"Set As Mask" : @"Unset As Mask"; + } else if ([keyPath isEqualToString: @"active"]) { + actionName = value ? @"Set Active" : @"Unset Active"; + } else if ([keyPath isEqualToString:@"doChromaKey"]) { + actionName = value ? @"Set Chroma Key" : @"Unset Chroma Key"; + } else { + actionName = [NSString stringWithFormat:@"Change %@", keyPath]; + } + } + + [self registerUndoForProperty:keyPath withAction:actionName]; + + [super setValue:value forKeyPath:keyPath]; +} -(void) setCrop_bottom:(float)crop_bottom { - [self registerUndoForProperty:@"crop_bottom" withAction:@"Crop Bottom"]; if (crop_bottom < 0) { _crop_bottom = 0; @@ -2597,7 +2762,6 @@ static NSArray *_sourceTypes = nil; -(void)setChromaKeyColor:(NSColor *)chromaKeyColor { - [self registerUndoForProperty:@"chromaKeyColor" withAction:@"Chroma Key Color"]; _chromaKeyColor = chromaKeyColor; [CATransaction begin]; @@ -2612,7 +2776,6 @@ static NSArray *_sourceTypes = nil; -(void)setChromaKeySmoothing:(float)chromaKeySmoothing { - [self registerUndoForProperty:@"chromaKeySmoothing" withAction:@"CK Smoothing"]; _chromaKeySmoothing = chromaKeySmoothing; [CATransaction begin]; @@ -2627,7 +2790,6 @@ static NSArray *_sourceTypes = nil; -(void)setChromaKeyThreshold:(float)chromaKeyThreshold { - [self registerUndoForProperty:@"chromaKeyThreshold" withAction:@"CK Threshold"]; _chromaKeyThreshold = chromaKeyThreshold; [CATransaction begin]; [self.layer.sourceLayer setValue:@(chromaKeyThreshold) forKeyPath:@"filters.Chromakey.inputThreshold"]; @@ -2644,7 +2806,6 @@ static NSArray *_sourceTypes = nil; -(void)setDoChromaKey:(bool)doChromaKey { - [self registerUndoForProperty:@"doChromaKey" withAction:doChromaKey ? @"Set Chroma Key" : @"Unset Chroma Key"]; _doChromaKey = doChromaKey; [CATransaction begin]; @@ -2695,7 +2856,6 @@ static NSArray *_sourceTypes = nil; -(void) buildLayerConstraints { - NSMutableArray *constraints = [NSMutableArray array]; for (NSString *key in self.constraintMap) @@ -2732,6 +2892,10 @@ static NSArray *_sourceTypes = nil; } CAConstraintAttribute parentAttrib = [parentVal intValue]; + if (!parentAttrib) + { + continue; + } [constraints addObject:[CAConstraint constraintWithAttribute:toConstrain relativeTo:@"superlayer" attribute:parentAttrib scale:1 offset:offsetFloat]]; } @@ -2763,10 +2927,13 @@ static NSArray *_sourceTypes = nil; [self.sourceLayout.undoManager setActionName:@"Constraint Change"]; [self buildLayerConstraints]; + } else if ([keyPath isEqualToString:@"captureName"]) { + self.name = _editedName; } } + -(void)setClonedFromInput:(InputSource *)clonedFromInput { NSObject *fromInput = clonedFromInput.videoInput; diff --git a/CocoaSplit/Interface/AudioMixer.xib b/CocoaSplit/Interface/AudioMixer.xib deleted file mode 100644 index 3a6f9ac3..00000000 --- a/CocoaSplit/Interface/AudioMixer.xib +++ /dev/null @@ -1,114 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - NSNegateBoolean - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/CocoaSplit/Interface/CAMultiAudioMatrixMixerWindowController.xib b/CocoaSplit/Interface/CAMultiAudioMatrixMixerWindowController.xib index c933173f..bcf597ba 100644 --- a/CocoaSplit/Interface/CAMultiAudioMatrixMixerWindowController.xib +++ b/CocoaSplit/Interface/CAMultiAudioMatrixMixerWindowController.xib @@ -1,8 +1,8 @@ - + - + @@ -25,11 +25,11 @@ - + - + @@ -89,7 +89,7 @@ - + diff --git a/CocoaSplit/Interface/CSAddInputViewController.h b/CocoaSplit/Interface/CSAddInputViewController.h new file mode 100644 index 00000000..08cb1df2 --- /dev/null +++ b/CocoaSplit/Interface/CSAddInputViewController.h @@ -0,0 +1,43 @@ +// +// CSAddInputViewController.h +// CocoaSplit +// +// Created by Zakk on 5/8/16. +// Copyright © 2016 Zakk. All rights reserved. +// + +#import +#import "CSPluginLoader.h" +#import "InputSource.h" + +@class PreviewView; + +@interface CSAddInputViewController : NSViewController +{ + NSView *_typeListView; + NSArray *_sourceTypeList; +} +@property (strong) IBOutlet NSView *initialView; + +@property (weak) NSPopover *popover; + +@property (strong) IBOutlet NSView *inputListView; + +@property (weak) IBOutlet NSTableView *initialTable; + +@property (strong) NSObject *selectedInput; + +@property (weak) IBOutlet NSTableView *deviceTable; + +@property (readonly) NSArray *sourceTypes; +@property (weak) IBOutlet NSView *headerView; +@property (strong) IBOutlet NSArrayController *sourceTypesController; + +@property (weak) PreviewView *previewView; + +- (IBAction)nextViewButton:(id)sender; +- (IBAction)previousViewButton:(id)sender; +- (IBAction)initalTableButtonClicked:(id)sender; +- (IBAction)inputTableButtonClicked:(id)sender; + +@end diff --git a/CocoaSplit/Interface/CSAddInputViewController.m b/CocoaSplit/Interface/CSAddInputViewController.m new file mode 100644 index 00000000..f462fc10 --- /dev/null +++ b/CocoaSplit/Interface/CSAddInputViewController.m @@ -0,0 +1,273 @@ +// +// CSAddInputViewController.m +// CocoaSplit +// +// Created by Zakk on 5/8/16. +// Copyright © 2016 Zakk. All rights reserved. +// + +#import "CSAddInputViewController.h" +#import "NSView+NSLayoutConstraintFilter.h" +#import "CSCaptureSourceProtocol.h" +#import "CSPluginServices.h" +#import "AppDelegate.h" +#import "PreviewView.h" + +@interface CSAddInputViewController () + +@end + +@implementation CSAddInputViewController + +@synthesize popover = _popover; + + +-(instancetype)init +{ + return [self initWithNibName:@"CSAddInputViewController" bundle:nil]; +} + + +-(void)loadView +{ + + [super loadView]; + [self switchToInitialView]; +} + + +-(NSPopover *)popover +{ + return _popover; +} + +-(void)setPopover:(NSPopover *)popover +{ + [self willChangeValueForKey:@"sourceTypes"]; + _sourceTypeList = nil; + [self didChangeValueForKey:@"sourceTypes"]; + + _popover = popover; + _popover.delegate = self; +} + + +-(void)popoverWillClose:(NSNotification *)notification +{ + [self willChangeValueForKey:@"sourceTypes"]; + _sourceTypeList = @[]; + [self didChangeValueForKey:@"sourceTypes"]; + self.selectedInput = nil; + +} + + +-(void)switchToInputListView +{ + + NSSize __block newSize; + + CATransition *transition = [CATransition animation]; + transition.type = kCATransitionPush; + transition.subtype = kCATransitionFromRight; + self.view.animations = @{@"subviews": transition}; + + [NSAnimationContext runAnimationGroup:^(NSAnimationContext * _Nonnull context) { + if (self.initialView && self.initialView.superview) + { + [self.initialView.animator removeFromSuperview]; + } + NSRect lRect = [self.deviceTable rectOfRow:self.deviceTable.numberOfRows - 1]; + + + NSRect vRect = self.inputListView.frame; + newSize = NSMakeSize(vRect.size.width, lRect.origin.y+lRect.size.height+2+self.headerView.frame.size.height); + + vRect.size = newSize; + vRect.origin.y = self.view.frame.size.height/2 - newSize.height/2; + + [self.inputListView setFrame:vRect]; + + [self.view.animator addSubview:self.inputListView]; + + + } completionHandler:^{ + + self.popover.contentSize = newSize; + [self.inputListView setFrameOrigin:NSMakePoint(0,0)]; + }]; +} + + +-(void)switchToInitialView +{ + NSSize __block newSize; + + CATransition *transition = [CATransition animation]; + transition.type = kCATransitionPush; + transition.subtype = kCATransitionFromLeft; + self.view.animations = @{@"subviews": transition}; + + [NSAnimationContext runAnimationGroup:^(NSAnimationContext * _Nonnull context) { + if (self.inputListView && self.inputListView.superview) + { + [self.inputListView.animator removeFromSuperview]; + } + NSRect lRect = [self.initialTable rectOfRow:self.initialTable.numberOfRows - 1]; + + + NSRect vRect = self.initialView.frame; + newSize = NSMakeSize(vRect.size.width, lRect.origin.y+lRect.size.height+2); + + vRect.size = newSize; + vRect.origin.y = self.view.frame.size.height/2 - newSize.height/2; + [self.initialView setFrame:vRect]; + [self.view.animator addSubview:self.initialView]; + + + } completionHandler:^{ + self.popover.contentSize = newSize; + [self.initialView setFrameOrigin:NSMakePoint(0,0)]; + }]; +} + + + +-(NSInteger)adjustTableHeight:(NSTableView *)table +{ + NSInteger height = 0; + for (int i = 0; i < table.numberOfRows; i++) + { + NSView *view = [table viewAtColumn:0 row:i makeIfNecessary:YES]; + height += view.frame.size.height; + } + + height += 4; + + + NSScrollView *tSview = (NSScrollView *)table.superview.superview; + + NSLayoutConstraint *constraint = [tSview constraintForAttribute:NSLayoutAttributeHeight]; + [constraint setConstant:height]; + + return height; +} + + +- (IBAction)nextViewButton:(id)sender +{ + [self.initialView removeFromSuperview]; + self.popover.contentSize = self.inputListView.frame.size; + [self.view addSubview:self.inputListView]; +} + +- (IBAction)previousViewButton:(id)sender +{ + [self switchToInitialView]; + self.selectedInput = nil; +} + +- (IBAction)initalTableButtonClicked:(id)sender +{ + + NSObject *clickedCapture; + + clickedCapture = [ self.sourceTypes objectAtIndex:[self.initialTable rowForView:sender]]; + + if (!clickedCapture.availableVideoDevices || clickedCapture.availableVideoDevices.count == 0) + { + InputSource *newSrc = [[InputSource alloc] init]; + newSrc.selectedVideoType = clickedCapture.label; + [self addInput:newSrc]; + } else { + self.selectedInput = clickedCapture; + [self switchToInputListView]; + } +} + +- (IBAction)inputTableButtonClicked:(id)sender +{ + CSAbstractCaptureDevice *clickedDevice; + clickedDevice = [self.selectedInput.availableVideoDevices objectAtIndex:[self.deviceTable rowForView:sender]]; + if (clickedDevice) + { + InputSource *newSrc = [[InputSource alloc] init]; + newSrc.selectedVideoType = self.selectedInput.label; + newSrc.videoInput.activeVideoDevice = clickedDevice; + [self addInput:newSrc]; + } + +} + +-(void)addInput:(id)toAdd +{ + + + if (self.previewView) + { + [self.previewView addInputSourceWithInput:toAdd]; + } + + } + + +-(BOOL)tableView:(NSTableView *)tableView shouldSelectRow:(NSInteger)row +{ + return NO; +} + + +-(NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row +{ + + if (tableView == self.deviceTable) + { + return [tableView makeViewWithIdentifier:@"deviceTableView" owner:self]; + } else if (tableView == self.initialTable) { + + NSObject *item = [self.sourceTypesController.arrangedObjects objectAtIndex:row]; + if (item.availableVideoDevices && item.availableVideoDevices.count > 0) + { + return [tableView makeViewWithIdentifier:@"initialInputView" owner:self]; + } else { + return [tableView makeViewWithIdentifier:@"initialInputViewNoArrow" owner:self]; + } + } + + return nil; +} + + +-(NSArray *)sourceTypes +{ + + if (_sourceTypeList) + { + return _sourceTypeList; + } + + + + NSMutableDictionary *pluginMap = [[CSPluginLoader sharedPluginLoader] sourcePlugins]; + + NSArray *sortedKeys = [pluginMap.allKeys sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)]; + + + NSMutableArray *ret = [[NSMutableArray alloc] init]; + for (NSString *key in sortedKeys) + { + Class captureClass = pluginMap[key]; + NSObject *newCaptureSession; + + newCaptureSession = [[captureClass alloc] init]; + + [ret addObject:newCaptureSession]; + } + + + _sourceTypeList = ret; + return ret; +} + + +@end diff --git a/CocoaSplit/Interface/CSAddInputViewController.xib b/CocoaSplit/Interface/CSAddInputViewController.xib new file mode 100644 index 00000000..478e5165 --- /dev/null +++ b/CocoaSplit/Interface/CSAddInputViewController.xib @@ -0,0 +1,244 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CocoaSplit/Interface/CSAddOutputPopupViewController.h b/CocoaSplit/Interface/CSAddOutputPopupViewController.h new file mode 100644 index 00000000..ea0d660d --- /dev/null +++ b/CocoaSplit/Interface/CSAddOutputPopupViewController.h @@ -0,0 +1,26 @@ +// +// CSAddOutputPopupViewController.h +// CocoaSplit +// +// Created by Zakk on 7/29/16. +// Copyright © 2016 Zakk. All rights reserved. +// + +#import +#import "CSPluginLoader.h" + + +@interface CSAddOutputPopupViewController : NSViewController +{ + NSArray *_outputTypes; + +} +@property (weak) IBOutlet NSTableView *outputTypesTableView; +@property (readonly) NSArray *outputTypes; +@property (weak) NSPopover *popover; +@property (nonatomic,copy) void (^addOutput)(Class outputClass); + + +- (IBAction)inputTableButtonClicked:(id)sender; + +@end diff --git a/CocoaSplit/Interface/CSAddOutputPopupViewController.m b/CocoaSplit/Interface/CSAddOutputPopupViewController.m new file mode 100644 index 00000000..03b0f370 --- /dev/null +++ b/CocoaSplit/Interface/CSAddOutputPopupViewController.m @@ -0,0 +1,117 @@ +// +// CSAddOutputPopupViewController.m +// CocoaSplit +// +// Created by Zakk on 7/29/16. +// Copyright © 2016 Zakk. All rights reserved. +// + +#import "CSAddOutputPopupViewController.h" +#import "CSStreamServiceProtocol.h" +#import "NSView+NSLayoutConstraintFilter.h" + + + +@interface CSAddOutputPopupViewController () + +@end + +@implementation CSAddOutputPopupViewController + + +-(instancetype)init +{ + return [self initWithNibName:@"CSAddOutputPopupViewController" bundle:nil]; +} + + +- (void)viewDidLoad { + [super viewDidLoad]; + [self adjustTableHeight:self.outputTypesTableView]; + // Do view setup here. +} + + + +/* +-(void)switchToInitialView +{ + + NSRect lRect = [self.outputTypesTableView rectOfRow:self.outputTypesTableView.numberOfRows - 1]; + + + NSRect vRect = self.outputTypesTableView.frame; + NSSize newSize = NSMakeSize(vRect.size.width, lRect.origin.y+lRect.size.height+2); + + vRect.size = newSize; + vRect.origin.y = self.view.frame.size.height/2 - newSize.height/2; + [self.outputTypesTableView setFrame:vRect]; + [self.view.animator addSubview:self.initialView]; + self.popover.contentSize = newSize; + [self.initialView setFrameOrigin:NSMakePoint(0,0)]; + + +} +*/ + + +-(void)adjustTableHeight:(NSTableView *)table +{ + + + NSSize vSize = self.view.frame.size; + + vSize.height = table.numberOfRows * table.rowHeight + 3; + self.preferredContentSize = vSize; + + +} + + +-(BOOL)tableView:(NSTableView *)tableView shouldSelectRow:(NSInteger)row +{ + return NO; +} + + +- (IBAction)inputTableButtonClicked:(id)sender +{ + + Class clickedOutputclass; + clickedOutputclass = [_outputTypes objectAtIndex:[self.outputTypesTableView rowForView:sender]]; + if (clickedOutputclass && self.addOutput) + { + self.addOutput(clickedOutputclass); + } + +} + + +-(NSArray *)outputTypes +{ + + if (_outputTypes) + { + return _outputTypes; + } + + + NSMutableDictionary *pluginMap = [[CSPluginLoader sharedPluginLoader] streamServicePlugins]; + + NSArray *sortedKeys = [pluginMap.allKeys sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)]; + + + + NSMutableArray *ret = [[NSMutableArray alloc] init]; + for (NSString *key in sortedKeys) + { + Class streamClass = pluginMap[key]; + [ret addObject:streamClass]; + } + + _outputTypes = ret; + return ret; +} + + +@end diff --git a/CocoaSplit/Interface/CSAddOutputPopupViewController.xib b/CocoaSplit/Interface/CSAddOutputPopupViewController.xib new file mode 100644 index 00000000..703d20aa --- /dev/null +++ b/CocoaSplit/Interface/CSAddOutputPopupViewController.xib @@ -0,0 +1,114 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CocoaSplit/Interface/CSAdvancedAudioCollectionViewItem.xib b/CocoaSplit/Interface/CSAdvancedAudioCollectionViewItem.xib new file mode 100644 index 00000000..46e042da --- /dev/null +++ b/CocoaSplit/Interface/CSAdvancedAudioCollectionViewItem.xib @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CocoaSplit/Interface/CSAdvancedAudioWindowController.h b/CocoaSplit/Interface/CSAdvancedAudioWindowController.h new file mode 100644 index 00000000..97f13c40 --- /dev/null +++ b/CocoaSplit/Interface/CSAdvancedAudioWindowController.h @@ -0,0 +1,17 @@ +// +// CSAdvancedAudioWindowController.h +// CocoaSplit +// +// Created by Zakk on 8/6/16. +// Copyright © 2016 Zakk. All rights reserved. +// + +#import +#import "CaptureController.h" +@interface CSAdvancedAudioWindowController : NSWindowController + + +@property (weak) CaptureController *controller; + + +@end diff --git a/CocoaSplit/Interface/CSAdvancedAudioWindowController.m b/CocoaSplit/Interface/CSAdvancedAudioWindowController.m new file mode 100644 index 00000000..be7399c8 --- /dev/null +++ b/CocoaSplit/Interface/CSAdvancedAudioWindowController.m @@ -0,0 +1,31 @@ +// +// CSAdvancedAudioWindowController.m +// CocoaSplit +// +// Created by Zakk on 8/6/16. +// Copyright © 2016 Zakk. All rights reserved. +// + +#import "CSAdvancedAudioWindowController.h" + +@interface CSAdvancedAudioWindowController () + +@end + +@implementation CSAdvancedAudioWindowController + + + +-(instancetype) init +{ + return [self initWithWindowNibName:@"CSAdvancedAudioWindowController"]; +} + + +- (void)windowDidLoad { + [super windowDidLoad]; + + // Implement this method to handle any initialization after your window controller's window has been loaded from its nib file. +} + +@end diff --git a/CocoaSplit/Interface/CSAdvancedAudioWindowController.xib b/CocoaSplit/Interface/CSAdvancedAudioWindowController.xib new file mode 100644 index 00000000..a01e18f9 --- /dev/null +++ b/CocoaSplit/Interface/CSAdvancedAudioWindowController.xib @@ -0,0 +1,327 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NSNegateBoolean + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CocoaSplit/Interface/CSAnimationWindowController.h b/CocoaSplit/Interface/CSAnimationWindowController.h new file mode 100644 index 00000000..2fe2b564 --- /dev/null +++ b/CocoaSplit/Interface/CSAnimationWindowController.h @@ -0,0 +1,26 @@ +// +// CSAnimationWindowController.h +// CocoaSplit +// +// Created by Zakk on 8/7/16. +// Copyright © 2016 Zakk. All rights reserved. +// + +#import + +@class PreviewView; + + +@interface CSAnimationWindowController : NSWindowController +{ + NSPopover *_animatepopOver; + +} +@property (strong) PreviewView *activePreviewView; +@property (strong) PreviewView *livePreviewView; +@property (assign) bool stagingHidden; + + +- (IBAction)openAnimatePopover:(NSButton *)sender; + +@end diff --git a/CocoaSplit/Interface/CSAnimationWindowController.m b/CocoaSplit/Interface/CSAnimationWindowController.m new file mode 100644 index 00000000..2e28f1cb --- /dev/null +++ b/CocoaSplit/Interface/CSAnimationWindowController.m @@ -0,0 +1,123 @@ +// +// CSAnimationWindowController.m +// CocoaSplit +// +// Created by Zakk on 8/7/16. +// Copyright © 2016 Zakk. All rights reserved. +// + +#import "CSAnimationWindowController.h" +#import "CaptureController.h" +#import "AppDelegate.h" +#import "PreviewView.h" + + + +@interface CSAnimationWindowController () + +@end + +@implementation CSAnimationWindowController + +-(instancetype) init +{ + return [self initWithWindowNibName:@"CSAnimationWindowController"]; +} + + +- (void)windowDidLoad { + [super windowDidLoad]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(setupLayoutViews:) name:CSNotificationLayoutModeChanged object:nil]; + + // Implement this method to handle any initialization after your window controller's window has been loaded from its nib file. +} + + + +-(void)showWindow:(id)sender +{ + [super showWindow:sender]; + [self setupLayoutViews:nil]; + +} + +-(void)setupLayoutViews:(id)sender +{ + AppDelegate *appDel = [NSApp delegate]; + + CaptureController *ccont = appDel.captureController; + + self.activePreviewView = ccont.activePreviewView; + self.livePreviewView = ccont.livePreviewView; + self.stagingHidden = ccont.stagingHidden; +} + + + + +- (IBAction)openAnimatePopover:(NSButton *)sender +{ + + CSAnimationChooserViewController *vc; + if (!_animatepopOver) + { + _animatepopOver = [[NSPopover alloc] init]; + + _animatepopOver.animates = YES; + _animatepopOver.behavior = NSPopoverBehaviorTransient; + } + + if (!_animatepopOver.contentViewController) + { + vc = [[CSAnimationChooserViewController alloc] init]; + + + _animatepopOver.contentViewController = vc; + _animatepopOver.delegate = vc; + vc.popover = _animatepopOver; + + } + + vc.sourceLayout = self.activePreviewView.sourceLayout; + [_animatepopOver showRelativeToRect:sender.bounds ofView:sender preferredEdge:NSMinYEdge]; + +} + +- (id)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row +{ + + NSView *retView = nil; + + + CSAnimationItem *animation = self.activePreviewView.sourceLayout.selectedAnimation; + + NSArray *inputs = animation.inputs; + + NSDictionary *inputmap = nil; + + if (row > -1 && row < inputs.count) + { + inputmap = [inputs objectAtIndex:row]; + } + + if ([tableColumn.identifier isEqualToString:@"label"]) + { + + retView = [tableView makeViewWithIdentifier:@"LabelCellView" owner:self]; + } else if ([tableColumn.identifier isEqualToString:@"value"]) { + if ([inputmap[@"type"] isEqualToString:@"param"]) + { + retView = [tableView makeViewWithIdentifier:@"InputParamView" owner:self]; + } else if ([inputmap[@"type"] isEqualToString:@"bool"]) { + retView = [tableView makeViewWithIdentifier:@"InputBoolView" owner:self]; + } else { + retView = [tableView makeViewWithIdentifier:@"InputSourceView" owner:self]; + } + } + + return retView; +} + + + +@end diff --git a/CocoaSplit/Interface/CSAnimationWindowController.xib b/CocoaSplit/Interface/CSAnimationWindowController.xib new file mode 100644 index 00000000..789928f8 --- /dev/null +++ b/CocoaSplit/Interface/CSAnimationWindowController.xib @@ -0,0 +1,344 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CocoaSplit/Interface/CSAppleH264CompressorViewController.h b/CocoaSplit/Interface/CSAppleH264CompressorViewController.h new file mode 100644 index 00000000..05f632bb --- /dev/null +++ b/CocoaSplit/Interface/CSAppleH264CompressorViewController.h @@ -0,0 +1,19 @@ +// +// CSAppleH264CompressorViewController.h +// CocoaSplit +// +// Created by Zakk on 3/28/16. +// Copyright © 2016 Zakk. All rights reserved. +// + +#import +#import "AppleVTCompressor.h" +#import "CSCompressorViewControllerProtocol.h" + +@interface CSAppleH264CompressorViewController : NSViewController + +@property (strong) AppleVTCompressor *compressor; +@property (strong) NSObjectController *compressorController; +@property (strong) NSArray *profiles; + +@end diff --git a/CocoaSplit/Interface/CSAppleH264CompressorViewController.m b/CocoaSplit/Interface/CSAppleH264CompressorViewController.m new file mode 100644 index 00000000..19b673a6 --- /dev/null +++ b/CocoaSplit/Interface/CSAppleH264CompressorViewController.m @@ -0,0 +1,36 @@ +// +// CSAppleH264CompressorViewController.m +// CocoaSplit +// +// Created by Zakk on 3/28/16. +// Copyright © 2016 Zakk. All rights reserved. +// + +#import "CSAppleH264CompressorViewController.h" + +@interface CSAppleH264CompressorViewController () + +@end + +@implementation CSAppleH264CompressorViewController + +- (void)viewDidLoad { + [super viewDidLoad]; + // Do view setup here. +} + + +-(instancetype)init +{ + return [self initWithNibName:@"CSAppleH264CompressorViewController" bundle:nil]; +} + +-(void)loadView +{ + [super loadView]; + self.profiles = @[[NSNull null], @"Baseline", @"Main", @"High"]; + +} + + +@end diff --git a/CocoaSplit/Interface/CSAppleH264CompressorViewController.xib b/CocoaSplit/Interface/CSAppleH264CompressorViewController.xib new file mode 100644 index 00000000..55876d7b --- /dev/null +++ b/CocoaSplit/Interface/CSAppleH264CompressorViewController.xib @@ -0,0 +1,168 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CocoaSplit/Interface/CSAppleProResCompressorViewController.h b/CocoaSplit/Interface/CSAppleProResCompressorViewController.h new file mode 100644 index 00000000..53166cce --- /dev/null +++ b/CocoaSplit/Interface/CSAppleProResCompressorViewController.h @@ -0,0 +1,21 @@ +// +// CSAppleProResCompressorViewController.h +// CocoaSplit +// +// Created by Zakk on 3/28/16. +// Copyright © 2016 Zakk. All rights reserved. +// + +#import +#import "AppleProResCompressor.h" +#import "CSCompressorViewControllerProtocol.h" + +@interface CSAppleProResCompressorViewController : NSViewController + +@property (strong) AppleProResCompressor *compressor; + +@property (strong) NSObjectController *compressorController; + +@property (strong) NSDictionary *compressorTypes; + +@end diff --git a/CocoaSplit/Interface/CSAppleProResCompressorViewController.m b/CocoaSplit/Interface/CSAppleProResCompressorViewController.m new file mode 100644 index 00000000..79e007ef --- /dev/null +++ b/CocoaSplit/Interface/CSAppleProResCompressorViewController.m @@ -0,0 +1,45 @@ +// +// CSAppleProResCompressorViewController.m +// CocoaSplit +// +// Created by Zakk on 3/28/16. +// Copyright © 2016 Zakk. All rights reserved. +// + +#import "CSAppleProResCompressorViewController.h" + +@interface CSAppleProResCompressorViewController () + +@end + +@implementation CSAppleProResCompressorViewController + + +-(instancetype)init +{ + return [self initWithNibName:@"CSAppleProResCompressorViewController" bundle:nil]; +} + +-(void)loadView +{ + [super loadView]; + self.compressorTypes = @{@"ProRes 4444": @(kCMVideoCodecType_AppleProRes4444), + @"ProRes 422": @(kCMVideoCodecType_AppleProRes422), + @"ProRes 422HQ": @(kCMVideoCodecType_AppleProRes422HQ), + @"ProRes 422LT": @(kCMVideoCodecType_AppleProRes422LT), + @"ProRes 422Proxy": @(kCMVideoCodecType_AppleProRes422Proxy), + }; + +} + +- (void)viewDidLoad { + [super viewDidLoad]; + // Do view setup here. + +} + + + + + +@end diff --git a/CocoaSplit/Interface/CSAppleProResCompressorViewController.xib b/CocoaSplit/Interface/CSAppleProResCompressorViewController.xib new file mode 100644 index 00000000..5fffbebf --- /dev/null +++ b/CocoaSplit/Interface/CSAppleProResCompressorViewController.xib @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CocoaSplit/Interface/CSAudioLevelView.h b/CocoaSplit/Interface/CSAudioLevelView.h new file mode 100644 index 00000000..bee958bd --- /dev/null +++ b/CocoaSplit/Interface/CSAudioLevelView.h @@ -0,0 +1,26 @@ +// +// CSAudioLevelView.h +// CocoaSplit +// +// Created by Zakk on 8/6/16. +// Copyright © 2016 Zakk. All rights reserved. +// + +#import +#import + +@interface CSAudioLevelView : NSView +{ + bool _isVertical; + CALayer *_maskLayer; +} + + +@property (assign) float level; +@property (assign) IBInspectable float startValue; +@property (assign) IBInspectable float endValue; +@property (strong) IBInspectable NSColor *backgroundColor; +@property (assign) IBInspectable float backgroundSize; + + +@end diff --git a/CocoaSplit/Interface/CSAudioLevelView.m b/CocoaSplit/Interface/CSAudioLevelView.m new file mode 100644 index 00000000..a2d0c21b --- /dev/null +++ b/CocoaSplit/Interface/CSAudioLevelView.m @@ -0,0 +1,132 @@ +// +// CSAudioLevelView.m +// CocoaSplit +// +// Created by Zakk on 8/6/16. +// Copyright © 2016 Zakk. All rights reserved. +// + +#import "CSAudioLevelView.h" + +@implementation CSAudioLevelView +@synthesize level = _level; + +- (void)drawRect:(NSRect)dirtyRect { + [super drawRect:dirtyRect]; + + // Drawing code here. +} + + + +-(float)level +{ + return _level; +} + +-(void)setLevel:(float)level +{ + + float myRange = self.endValue - self.startValue; + CALayer *mLayer = _maskLayer; + + + + CGRect cFrame = self.frame; + CGRect nFrame = CGRectMake(0,0,cFrame.size.width, cFrame.size.height); + if (_isVertical) + { + nFrame.size.height = (level/myRange) * cFrame.size.height; + } else { + nFrame.size.width = (level/myRange) * cFrame.size.width; + } + + [CATransaction begin]; + [CATransaction setDisableActions:YES]; + [mLayer setFrame:nFrame]; + [CATransaction commit]; +} + +/* +-(instancetype)initWithFrame:(NSRect)frameRect +{ + if (self = [super initWithFrame:frameRect]) + { + [self setupLayers]; + } + + return self; +} + + */ + + + +-(void)awakeFromNib +{ + [super awakeFromNib]; + if (!self.backgroundColor) + { + self.backgroundColor = [NSColor disabledControlTextColor]; + } + [self setupLayers]; +} + +-(void)setupLayers +{ + if (_maskLayer) + { + return; + } + + _isVertical = NO; + + if (self.frame.size.height > self.frame.size.width) + { + _isVertical = YES; + } + + CALayer *outerLayer = [CALayer layer]; + outerLayer.borderWidth = self.backgroundSize; + outerLayer.backgroundColor = self.backgroundColor.CGColor; + + CAGradientLayer *gLayer = [CAGradientLayer layer]; + NSArray *colors = [NSArray arrayWithObjects:(__bridge id)([NSColor greenColor].CGColor), (__bridge id)[NSColor yellowColor].CGColor, (__bridge id)[NSColor yellowColor].CGColor, (__bridge id)[NSColor redColor].CGColor, nil]; + gLayer.colors = colors; + + if (_isVertical) + { + gLayer.startPoint = CGPointMake(0.5, 0.0); + gLayer.endPoint = CGPointMake(0.5, 1.0); + + } else { + + gLayer.startPoint = CGPointMake(0.0, 0.5); + gLayer.endPoint = CGPointMake(1.0, 0.5); + } + + self.layer = outerLayer; + self.wantsLayer = YES; + + [outerLayer addSublayer:gLayer]; + + gLayer.frame = CGRectMake(0.0, 0.0, self.frame.size.width, self.frame.size.height); + _maskLayer = [CALayer layer]; + + + _maskLayer.backgroundColor = [NSColor blackColor].CGColor; + + + CGRect maskFrame = CGRectMake(0.0, 0.0, self.frame.size.width, self.frame.size.height); + if (_isVertical) + { + maskFrame.size.height = 0.0; + } else { + maskFrame.size.width = 0.0; + } + _maskLayer.frame = maskFrame; + gLayer.mask = _maskLayer; + +} + +@end diff --git a/CocoaSplit/Interface/CSCompressorViewControllerProtocol.h b/CocoaSplit/Interface/CSCompressorViewControllerProtocol.h new file mode 100644 index 00000000..7caf7683 --- /dev/null +++ b/CocoaSplit/Interface/CSCompressorViewControllerProtocol.h @@ -0,0 +1,18 @@ +// +// CSCompressorViewControllerProtocol.h +// CocoaSplit +// +// Created by Zakk on 3/28/16. +// Copyright © 2016 Zakk. All rights reserved. +// + +#import +#import + +@protocol CSCompressorViewControllerProtocol + +@property (strong) NSObjectController *compressorController; +@property (strong) id compressor; + + +@end diff --git a/CocoaSplit/Interface/CSFilterChooserWindowController.xib b/CocoaSplit/Interface/CSFilterChooserWindowController.xib index e07021ac..652b673d 100644 --- a/CocoaSplit/Interface/CSFilterChooserWindowController.xib +++ b/CocoaSplit/Interface/CSFilterChooserWindowController.xib @@ -1,8 +1,8 @@ - + - + @@ -24,15 +24,15 @@ - - + + - + - + @@ -58,11 +58,11 @@ - + @@ -94,15 +94,15 @@ Gw - - + + - + - + @@ -128,11 +128,11 @@ Gw diff --git a/CocoaSplit/Interface/CSInputLibraryItemView.xib b/CocoaSplit/Interface/CSInputLibraryItemView.xib new file mode 100644 index 00000000..37c3ca41 --- /dev/null +++ b/CocoaSplit/Interface/CSInputLibraryItemView.xib @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CocoaSplit/Interface/CSInputLibraryWindowController.h b/CocoaSplit/Interface/CSInputLibraryWindowController.h new file mode 100644 index 00000000..1f414668 --- /dev/null +++ b/CocoaSplit/Interface/CSInputLibraryWindowController.h @@ -0,0 +1,41 @@ +// +// CSInputLibraryWindowController.h +// CocoaSplit +// +// Created by Zakk on 10/18/15. +// Copyright © 2015 Zakk. All rights reserved. +// + +#import +#import "InputPopupControllerViewController.h" +#import "CSInputLibraryItem.h" + +@class CaptureController; +@class CSLayoutEditWindowController; + +@interface CSInputLibraryWindowController : NSWindowController +{ + NSRange _dragRange; + NSArray *_draggingObjects; +} + + +@property (strong) CaptureController *controller; +@property (weak) IBOutlet NSTableView *tableView; +@property (strong) NSMutableArray *tableControllers; +@property (strong) IBOutlet NSArrayController *itemArrayController; + +@property (strong) InputPopupControllerViewController *activePopupController; +@property (strong) CSInputLibraryItem *activePopupItem; + +@property (strong) SourceLayout *editLayout; + +@property (strong) CSLayoutEditWindowController *editWindowController; + +- (IBAction)deleteItem:(id)sender; + + +- (IBAction)doDeleteFromMenu:(id)sender; +-(IBAction)doEditFromMenu:(id)sender; + +@end diff --git a/CocoaSplit/Interface/CSInputLibraryWindowController.m b/CocoaSplit/Interface/CSInputLibraryWindowController.m new file mode 100644 index 00000000..f1d0a34e --- /dev/null +++ b/CocoaSplit/Interface/CSInputLibraryWindowController.m @@ -0,0 +1,213 @@ +// +// CSInputLibraryWindowController.m +// CocoaSplit +// +// Created by Zakk on 10/18/15. +// Copyright © 2015 Zakk. All rights reserved. +// + +#import "CSInputLibraryWindowController.h" +#import "CaptureController.h" +#import "CSLibraryInputItemViewController.h" +#import "CSLayoutEditWindowController.h" + + +@interface CSInputLibraryWindowController () + +@end + +@implementation CSInputLibraryWindowController + + +-(instancetype) init +{ + return [self initWithWindowNibName:@"CSInputLibraryWindowController"]; +} + + + +- (void)windowDidLoad { + [super windowDidLoad]; + + // Implement this method to handle any initialization after your window controller's window has been loaded from its nib file. + [self.tableView registerForDraggedTypes:@[@"cocoasplit.library.item"]]; +} + + +//Table view delegate + +-(void)tableView:(NSTableView *)tableView draggingSession:(NSDraggingSession *)session willBeginAtPoint:(NSPoint)screenPoint forRowIndexes:(NSIndexSet *)rowIndexes +{ + NSUInteger dlen = (rowIndexes.lastIndex+1) - rowIndexes.firstIndex; + _dragRange = NSMakeRange(rowIndexes.firstIndex, dlen); + _draggingObjects = [self.itemArrayController.arrangedObjects objectsAtIndexes:rowIndexes]; +} + +-(void)tableView:(NSTableView *)tableView draggingSession:(NSDraggingSession *)session endedAtPoint:(NSPoint)screenPoint operation:(NSDragOperation)operation +{ + _dragRange = NSMakeRange(0, 0); + _draggingObjects = nil; +} + +-(NSDragOperation)tableView:(NSTableView *)tableView validateDrop:(id)info proposedRow:(NSInteger)row proposedDropOperation:(NSTableViewDropOperation)dropOperation +{ + if (dropOperation == NSTableViewDropAbove && (_dragRange.location > row || _dragRange.location+_dragRange.length < row)) + { + if ([info draggingSource] == self.tableView) + { + return NSDragOperationMove; + } + } + return NSDragOperationNone; +} + + +-(BOOL)tableView:(NSTableView *)tableView acceptDrop:(id)info row:(NSInteger)row dropOperation:(NSTableViewDropOperation)dropOperation +{ + [tableView beginUpdates]; + NSArray *classes = @[[CSInputLibraryItem class]]; + + [info enumerateDraggingItemsWithOptions:0 forView:tableView classes:classes searchOptions:@{} usingBlock:^(NSDraggingItem * _Nonnull draggingItem, NSInteger idx, BOOL * _Nonnull stop) { + NSInteger newIdx = row+idx; + CSInputLibraryItem *dragItem = _draggingObjects[idx]; + NSInteger oldIdx = [self.itemArrayController.arrangedObjects indexOfObject:dragItem]; + if (oldIdx < newIdx) + { + newIdx -= idx+1; + } + + [self.controller.inputLibrary removeObjectAtIndex:oldIdx]; + [self.controller.inputLibrary insertObject:dragItem atIndex:newIdx]; + [self.tableView moveRowAtIndex:oldIdx toIndex:newIdx]; + }]; + [tableView endUpdates]; + return YES; +} + + +-(BOOL)tableView:(NSTableView *)tableView writeRowsWithIndexes:(NSIndexSet *)rowIndexes toPasteboard:(NSPasteboard *)pboard +{ + NSArray *draggedItems = [self.itemArrayController.arrangedObjects objectsAtIndexes:rowIndexes]; + + [pboard clearContents]; + [pboard writeObjects:draggedItems]; + + return YES; +} + +-(NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row +{ + if (!self.tableControllers) + { + self.tableControllers = [NSMutableArray array]; + } + + CSInputLibraryItem *cItem = [self.itemArrayController.arrangedObjects objectAtIndex:row]; + if (cItem) + { + CSLibraryInputItemViewController *vCont = [[CSLibraryInputItemViewController alloc] init]; + vCont.item = cItem; + [self.tableControllers addObject:vCont]; + return vCont.view; + } + return nil; +} + +- (IBAction)deleteItem:(id)sender +{ + [self.tableView beginUpdates]; + [self.itemArrayController removeObjectsAtArrangedObjectIndexes:self.tableView.selectedRowIndexes]; +// [self.tableView removeRowsAtIndexes:self.tableView.selectedRowIndexes withAnimation:NSTableViewAnimationEffectFade]; + [self.tableView endUpdates]; +} + + +-(void)popoverWillClose:(NSNotification *)notification +{ + if (self.activePopupController && self.activePopupItem) + { + InputSource *editInput = self.activePopupController.inputSource; + + //[editInput frameTick]; + + [self.activePopupItem makeDataFromInput:editInput]; + } + + self.activePopupItem = nil; +} + +-(void)popoverDidClose:(NSNotification *)notification +{ + self.activePopupItem = nil; + self.activePopupController = nil; + self.editLayout = nil; +} + + +-(void)layoutWindowWillClose:(id)controller +{ + [self.activePopupItem makeDataFromInput:self.activePopupItem.editInput]; + self.activePopupItem.editInput = nil; + [self.controller layoutWindowWillClose:controller]; +} + + +- (IBAction)doDeleteFromMenu:(id)sender +{ + if (self.tableView.selectedRowIndexes.count == 0) + { + NSIndexSet *toSelect = [NSIndexSet indexSetWithIndex:self.tableView.clickedRow]; + [self.tableView selectRowIndexes:toSelect byExtendingSelection:NO]; + } + [self deleteItem:sender]; +} + +-(IBAction)doEditFromMenu:(id)sender +{ + + CSInputLibraryItem *cItem = [self.itemArrayController.arrangedObjects objectAtIndex:self.tableView.clickedRow]; + + + + + + InputSource *iSrc = [cItem makeInput]; + + cItem.editInput = iSrc; + + //self.editLayout = [[SourceLayout alloc] init]; + + CGFloat parent_width = iSrc.topLevelWidth; + CGFloat parent_height = iSrc.topLevelHeight; + + //self.editLayout.canvas_width = parent_width; + //self.editLayout.canvas_height = parent_height; + NSLog(@"PARENT WIDTH %f HEIGHT %f", parent_width, parent_height); + + + //[self.editLayout addSource:iSrc]; + + /* + self.editWindowController = [[CSLayoutEditWindowController alloc] init]; + self.editWindowController.previewView.sourceLayout = self.editLayout; + [self.editWindowController showWindow:nil]; + */ + //self.editWindowController = [self.controller openLayoutWindow:self.editLayout]; + //self.editWindowController.delegate = self; + + InputPopupControllerViewController *popupController = [[InputPopupControllerViewController alloc] init]; + + NSPopover *popover = [[NSPopover alloc] init]; + popover.contentViewController = popupController; + popover.animates = YES; + popover.delegate = self; + popover.behavior = NSPopoverBehaviorTransient; + [popover showRelativeToRect:self.tableView.frame ofView:self.tableView preferredEdge:NSMaxXEdge]; + + //[iSrc frameTick]; + popupController.inputSource = iSrc; + self.activePopupItem = cItem; + self.activePopupController = popupController; +} + +@end diff --git a/CocoaSplit/Interface/CSInputLibraryWindowController.xib b/CocoaSplit/Interface/CSInputLibraryWindowController.xib new file mode 100644 index 00000000..d8d6a526 --- /dev/null +++ b/CocoaSplit/Interface/CSInputLibraryWindowController.xib @@ -0,0 +1,163 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + predicate + self.name CONTAINS[cd] $value + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CocoaSplit/Interface/CSLayoutCollectionItem.h b/CocoaSplit/Interface/CSLayoutCollectionItem.h new file mode 100644 index 00000000..150ff489 --- /dev/null +++ b/CocoaSplit/Interface/CSLayoutCollectionItem.h @@ -0,0 +1,27 @@ +// +// CSLayoutCollectionItem.h +// CocoaSplit +// +// Created by Zakk on 10/4/15. +// Copyright © 2015 Zakk. All rights reserved. +// + +#import +#import "CaptureController.h" +#import "CSLayoutButton.h" + + +@interface CSLayoutCollectionItem : NSCollectionViewItem + + +@property (weak) IBOutlet CaptureController *captureController; +@property (strong) NSMenu *layoutMenu; + +- (IBAction)layoutButtonPushed:(id)sender; + +-(void)buildLayoutMenu; +-(void)showLayoutMenu:(NSEvent *)clickEvent; + +@property (weak) IBOutlet CSLayoutButton *layoutButton; + +@end diff --git a/CocoaSplit/Interface/CSLayoutCollectionItem.m b/CocoaSplit/Interface/CSLayoutCollectionItem.m new file mode 100644 index 00000000..82263d43 --- /dev/null +++ b/CocoaSplit/Interface/CSLayoutCollectionItem.m @@ -0,0 +1,118 @@ +// +// CSLayoutCollectionItem.m +// CocoaSplit +// +// Created by Zakk on 10/4/15. +// Copyright © 2015 Zakk. All rights reserved. +// + +#import "CSLayoutCollectionItem.h" +#import "AppDelegate.h" +@interface CSLayoutCollectionItem () + +@end + +@implementation CSLayoutCollectionItem + + + +-(void)setRepresentedObject:(id)representedObject +{ + [super setRepresentedObject:representedObject]; + [self.representedObject addObserver:self forKeyPath:@"in_live" options:NSKeyValueObservingOptionNew context:NULL]; + [self.representedObject addObserver:self forKeyPath:@"in_staging" options:NSKeyValueObservingOptionNew context:NULL]; +} + + +-(void) awakeFromNib +{ + [super awakeFromNib]; + AppDelegate *appDel = [NSApp delegate]; + + self.captureController = appDel.captureController; + + +} + +-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context +{ + [self.layoutButton setNeedsDisplay]; +} + + +- (void)viewDidLoad { + [super viewDidLoad]; + + //self.view.layer.backgroundColor = [[NSColor blackColor] CGColor]; + + // Do view setup here. +} + +- (IBAction)layoutButtonPushed:(id)sender +{ + if ([NSEvent modifierFlags] & NSCommandKeyMask) + { + [self.captureController switchToLayout:self.representedObject]; + } else { + [self.captureController toggleLayout:self.representedObject]; + } +} + +-(void)mouseDragged:(NSEvent *)theEvent +{ + [self.nextResponder mouseDragged:theEvent]; +} + + +-(void)saveToLayout:(id) sender +{ + [self.captureController saveToLayout:self.representedObject]; +} + + +-(void)editLayout:(id) sender +{ + SourceLayout *toEdit = self.representedObject; + [self.captureController openLayoutWindow:toEdit]; + + //[self.captureController openLayoutPopover:self.layoutButton forLayout:toEdit]; +} + + +-(void)deleteLayout:(id) sender +{ + SourceLayout *toDelete = self.representedObject; + + if ([self.captureController deleteLayout:self.representedObject]) + { + [toDelete removeObserver:self forKeyPath:@"in_live"]; + [toDelete removeObserver:self forKeyPath:@"in_staging"]; + + } +} + + +-(void)buildLayoutMenu +{ + + NSInteger idx = 0; + + NSMenuItem *tmp; + self.layoutMenu = [[NSMenu allocWithZone:[NSMenu menuZone]] init]; + tmp = [self.layoutMenu insertItemWithTitle:@"Save To" action:@selector(saveToLayout:) keyEquivalent:@"" atIndex:idx++]; + tmp.target = self; + tmp = [self.layoutMenu insertItemWithTitle:@"Edit" action:@selector(editLayout:) keyEquivalent:@"" atIndex:idx++]; + tmp.target = self; + + tmp = [self.layoutMenu insertItemWithTitle:@"Delete" action:@selector(deleteLayout:) keyEquivalent:@"" atIndex:idx++]; + tmp.target = self; +} + +-(void)showLayoutMenu:(NSEvent *)clickEvent +{ + NSPoint tmp = [self.view convertPoint:clickEvent.locationInWindow fromView:nil]; + [self buildLayoutMenu]; + [self.layoutMenu popUpMenuPositioningItem:self.layoutMenu.itemArray.firstObject atLocation:tmp inView:self.view]; +} + +@end diff --git a/CocoaSplit/Interface/CSLayoutCollectionItem.xib b/CocoaSplit/Interface/CSLayoutCollectionItem.xib new file mode 100644 index 00000000..e8d4643b --- /dev/null +++ b/CocoaSplit/Interface/CSLayoutCollectionItem.xib @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CocoaSplit/Interface/CSLayoutCollectionItemView.h b/CocoaSplit/Interface/CSLayoutCollectionItemView.h new file mode 100644 index 00000000..215efdcd --- /dev/null +++ b/CocoaSplit/Interface/CSLayoutCollectionItemView.h @@ -0,0 +1,13 @@ +// +// CSLayoutCollectionItemView.h +// CocoaSplit +// +// Created by Zakk on 2/2/16. +// Copyright © 2016 Zakk. All rights reserved. +// + +#import + +@interface CSLayoutCollectionItemView : NSView + +@end diff --git a/CocoaSplit/Interface/CSLayoutCollectionItemView.m b/CocoaSplit/Interface/CSLayoutCollectionItemView.m new file mode 100644 index 00000000..f2a9fccd --- /dev/null +++ b/CocoaSplit/Interface/CSLayoutCollectionItemView.m @@ -0,0 +1,15 @@ +// +// CSLayoutCollectionItemView.m +// CocoaSplit +// +// Created by Zakk on 2/2/16. +// Copyright © 2016 Zakk. All rights reserved. +// + +#import "CSLayoutCollectionItemView.h" +#import + +@implementation CSLayoutCollectionItemView + + +@end diff --git a/CocoaSplit/Interface/CSLayoutEditWindowController.h b/CocoaSplit/Interface/CSLayoutEditWindowController.h new file mode 100644 index 00000000..a9b960b7 --- /dev/null +++ b/CocoaSplit/Interface/CSLayoutEditWindowController.h @@ -0,0 +1,39 @@ +// +// CSLayoutEditWindowController.h +// CocoaSplit +// +// Created by Zakk on 10/12/15. +// Copyright © 2015 Zakk. All rights reserved. +// + +#import +#import "SourceLayout.h" +#import "PreviewView.h" + + +@interface CSLayoutEditWindowController : NSWindowController +{ + float _frame_interval; + NSPopover *_animatepopOver; + NSPopover *_addInputpopOver; + + + +} + + +@property (weak) id delegate; + +@property (weak) IBOutlet PreviewView *previewView; + +@property (strong) IBOutlet NSObjectController *layoutController; +- (IBAction)cancelEdit:(id)sender; +- (IBAction)editOK:(id)sender; +- (IBAction)newSource:(id)sender; +- (IBAction)openAnimatePopover:(NSButton *)sender; +- (IBAction)inputTableControlClick:(NSButton *)sender; + +@property (weak) IBOutlet NSOutlineView *inputOutlineView; +@property (assign) bool previewOnly; + +@end diff --git a/CocoaSplit/Interface/CSLayoutEditWindowController.m b/CocoaSplit/Interface/CSLayoutEditWindowController.m new file mode 100644 index 00000000..86a91677 --- /dev/null +++ b/CocoaSplit/Interface/CSLayoutEditWindowController.m @@ -0,0 +1,260 @@ +// +// CSLayoutEditWindowController.m +// CocoaSplit +// +// Created by Zakk on 10/12/15. +// Copyright © 2015 Zakk. All rights reserved. +// + +#import "CSLayoutEditWindowController.h" + +@interface CSLayoutEditWindowController () + +@end + +@implementation CSLayoutEditWindowController + + +-(instancetype) init +{ + return [self initWithWindowNibName:@"CSLayoutEditWindowController"]; +} + + +- (void)windowDidLoad { + [super windowDidLoad]; + self.window.delegate = self; + // Implement this method to handle any initialization after your window controller's window has been loaded from its nib file. +} + + +-(void)windowWillClose:(NSNotification *)notification +{ + if (self.delegate) + { + [self.delegate layoutWindowWillClose:self]; + } +} + + + +- (IBAction)cancelEdit:(id)sender +{ + if (self.layoutController) + { + [self.layoutController discardEditing]; + } + + if (self.previewView.sourceLayout) + { + [self.previewView.sourceLayout clearSourceList]; + } + + [self close]; +} + + + +- (IBAction)editOK:(id)sender +{ + if (self.layoutController) + { + [self.layoutController commitEditing]; + } + + if (self.previewView.sourceLayout) + { + [self.previewView.sourceLayout saveSourceList]; + [self.previewView.sourceLayout clearSourceList]; + } + [self close]; +} + +- (IBAction)newSource:(id)sender +{ + [self.previewView addInputSource:self]; +} + + +- (IBAction)openAnimatePopover:(NSButton *)sender +{ + + CSAnimationChooserViewController *vc; + if (!_animatepopOver) + { + _animatepopOver = [[NSPopover alloc] init]; + + _animatepopOver.animates = YES; + _animatepopOver.behavior = NSPopoverBehaviorTransient; + } + + if (!_animatepopOver.contentViewController) + { + vc = [[CSAnimationChooserViewController alloc] init]; + + + _animatepopOver.contentViewController = vc; + _animatepopOver.delegate = vc; + vc.popover = _animatepopOver; + + } + + vc.sourceLayout = self.previewView.sourceLayout; + [_animatepopOver showRelativeToRect:sender.bounds ofView:sender preferredEdge:NSMinYEdge]; + +} + +- (id)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row +{ + + NSView *retView = nil; + + + CSAnimationItem *animation = self.previewView.sourceLayout.selectedAnimation; + + NSArray *inputs = animation.inputs; + + NSDictionary *inputmap = nil; + + if (row > -1 && row < inputs.count) + { + inputmap = [inputs objectAtIndex:row]; + } + + if ([tableColumn.identifier isEqualToString:@"label"]) + { + + retView = [tableView makeViewWithIdentifier:@"LabelCellView" owner:self]; + } else if ([tableColumn.identifier isEqualToString:@"value"]) { + if ([inputmap[@"type"] isEqualToString:@"param"]) + { + retView = [tableView makeViewWithIdentifier:@"InputParamView" owner:self]; + } else if ([inputmap[@"type"] isEqualToString:@"bool"]) { + retView = [tableView makeViewWithIdentifier:@"InputBoolView" owner:self]; + } else { + retView = [tableView makeViewWithIdentifier:@"InputSourceView" owner:self]; + } + } + + return retView; +} + + +-(void) resetInputTableHighlights +{ + [self.previewView stopHighlightingAllSources]; + if (self.inputOutlineView && self.inputOutlineView.selectedRowIndexes) + { + [self.inputOutlineView.selectedRowIndexes enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL * _Nonnull stop) { + NSTreeNode *node = [self.inputOutlineView itemAtRow:idx]; + InputSource *src = node.representedObject; + + if (src) + { + [self.previewView highlightSource:src]; + } + }]; + } +} + + +-(void)outlineView:(NSOutlineView *)outlineView didAddRowView:(NSTableRowView *)rowView forRow:(NSInteger)row +{ + NSTreeNode *node = [outlineView itemAtRow:row]; + InputSource *src = node.representedObject; + if (!src.parentInput) + { + dispatch_async(dispatch_get_main_queue(), ^{ + [outlineView expandItem:nil expandChildren:YES]; + }); + } +} + + +-(void) outlineViewSelectionDidChange:(NSNotification *)notification +{ + + [self resetInputTableHighlights]; +} + + +-(void)openAddInputPopover:(id)sender sourceRect:(NSRect)sourceRect +{ + CSAddInputViewController *vc; + if (!_addInputpopOver) + { + _addInputpopOver = [[NSPopover alloc] init]; + _addInputpopOver.animates = YES; + _addInputpopOver.behavior = NSPopoverBehaviorTransient; + } + + //if (!_addInputpopOver.contentViewController) + { + vc = [[CSAddInputViewController alloc] init]; + _addInputpopOver.contentViewController = vc; + vc.popover = _addInputpopOver; + vc.previewView = self.previewView; + + //_addInputpopOver.delegate = vc; + } + + [_addInputpopOver showRelativeToRect:sourceRect ofView:sender preferredEdge:NSMaxXEdge]; +} + + +- (IBAction)inputTableControlClick:(NSButton *)sender +{ + NSInteger clicked = sender.tag; + + NSArray *selectedInputs; + NSRect sbounds; + switch (clicked) { + case 0: + sbounds = sender.bounds; + //[self.activePreviewView addInputSource:sender]; + //sbounds.origin.x = NSMaxX(sender.frame) - [sender widthForSegment:0]; + //sbounds.origin.x -= 333; + [self openAddInputPopover:sender sourceRect:sbounds]; + break; + case 1: + if (self.inputOutlineView && self.inputOutlineView.selectedRowIndexes) + { + [self.inputOutlineView.selectedRowIndexes enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL * _Nonnull stop) { + NSTreeNode *node = [self.inputOutlineView itemAtRow:idx]; + InputSource *src = node.representedObject; + + if (src) + { + NSString *uuid = src.uuid; + InputSource *realInput = [self.previewView.sourceLayout inputForUUID:uuid]; + [self.previewView deleteInput:realInput]; + } + + }]; + + } + break; + case 2: + if (self.inputOutlineView && self.inputOutlineView.selectedRowIndexes) + { + + [self.inputOutlineView.selectedRowIndexes enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL * _Nonnull stop) { + NSTreeNode *node = [self.inputOutlineView itemAtRow:idx]; + InputSource *src = node.representedObject; + + if (src) + { + [self.previewView openInputConfigWindow:src.uuid]; + } + + }]; + } + break; + default: + break; + } +} + + + +@end diff --git a/CocoaSplit/Interface/CSLayoutEditWindowController.xib b/CocoaSplit/Interface/CSLayoutEditWindowController.xib new file mode 100644 index 00000000..fd53de7e --- /dev/null +++ b/CocoaSplit/Interface/CSLayoutEditWindowController.xib @@ -0,0 +1,717 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NSNegateBoolean + + + + + + + + + + + + + + + + + + + + + + + + + + + NSNegateBoolean + + + + + + + + + + + + + + + + + + + + + + + + + + + NSNegateBoolean + + + + + + + + + + + + + + + + NSNegateBoolean + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CocoaSplit/Interface/CSLibraryInputItemViewController.h b/CocoaSplit/Interface/CSLibraryInputItemViewController.h new file mode 100644 index 00000000..aa800cad --- /dev/null +++ b/CocoaSplit/Interface/CSLibraryInputItemViewController.h @@ -0,0 +1,17 @@ +// +// CSLibraryInputItemViewController.h +// CocoaSplit +// +// Created by Zakk on 10/19/15. +// Copyright © 2015 Zakk. All rights reserved. +// + +#import +#import "CSInputLibraryItem.h" + +@interface CSLibraryInputItemViewController : NSViewController + +@property (strong) CSInputLibraryItem *item; + + +@end diff --git a/CocoaSplit/Interface/CSLibraryInputItemViewController.m b/CocoaSplit/Interface/CSLibraryInputItemViewController.m new file mode 100644 index 00000000..ada10409 --- /dev/null +++ b/CocoaSplit/Interface/CSLibraryInputItemViewController.m @@ -0,0 +1,33 @@ +// +// CSLibraryInputItemViewController.m +// CocoaSplit +// +// Created by Zakk on 10/19/15. +// Copyright © 2015 Zakk. All rights reserved. +// + +#import "CSLibraryInputItemViewController.h" + +@interface CSLibraryInputItemViewController () + +@end + +@implementation CSLibraryInputItemViewController + +-(instancetype) init +{ + if (self = [super initWithNibName:@"CSInputLibraryItemView" bundle:nil]) + { + + } + + return self; +} + + +- (void)viewDidLoad { + [super viewDidLoad]; + // Do view setup here. +} + +@end diff --git a/CocoaSplit/Interface/CSMidiManagerWindowController.h b/CocoaSplit/Interface/CSMidiManagerWindowController.h index 85b2a7c7..6c9997cb 100644 --- a/CocoaSplit/Interface/CSMidiManagerWindowController.h +++ b/CocoaSplit/Interface/CSMidiManagerWindowController.h @@ -13,7 +13,7 @@ @class CaptureController; -@interface CSMidiManagerWindowController : NSWindowController +@interface CSMidiManagerWindowController : NSWindowController @property (weak) CaptureController *captureController; diff --git a/CocoaSplit/Interface/CSMidiManagerWindowController.m b/CocoaSplit/Interface/CSMidiManagerWindowController.m index 56f7b24a..1b50e08c 100644 --- a/CocoaSplit/Interface/CSMidiManagerWindowController.m +++ b/CocoaSplit/Interface/CSMidiManagerWindowController.m @@ -18,6 +18,7 @@ - (void)windowDidLoad { [super windowDidLoad]; + self.window.delegate = self; // Implement this method to handle any initialization after your window controller's window has been loaded from its nib file. @@ -26,6 +27,13 @@ } +-(void)windowWillClose:(NSNotification *)notification +{ + self.responderList = nil; + self.commandIdentfiers = nil; +} + + -(void)buildIdentifiers { NSMutableArray *identList = [NSMutableArray array]; @@ -58,7 +66,16 @@ [midiMappings unionSet:commandMaps]; } } - [identList addObject:@{@"command":ident, @"responder":responder, @"count":@(midiMappings.count), @"display":[NSString stringWithFormat:@"%@-%@", responderName, ident]}]; + + NSString *displayName; + + if (responderName) + { + displayName = [NSString stringWithFormat:@"%@-%@", responderName, ident]; + } else { + displayName = [NSString stringWithFormat:@"%@", ident]; + } + [identList addObject:@{@"command":ident, @"responder":responder, @"count":@(midiMappings.count), @"display":displayName}]; } } @@ -158,4 +175,5 @@ } + @end diff --git a/CocoaSplit/Interface/CSMidiManagerWindowController.xib b/CocoaSplit/Interface/CSMidiManagerWindowController.xib index cf19ba9f..ebef07e6 100644 --- a/CocoaSplit/Interface/CSMidiManagerWindowController.xib +++ b/CocoaSplit/Interface/CSMidiManagerWindowController.xib @@ -1,8 +1,8 @@ - + - + @@ -33,11 +33,11 @@ - + - + @@ -123,7 +123,7 @@ - + diff --git a/CocoaSplit/Interface/CSNewOutputWindowController.h b/CocoaSplit/Interface/CSNewOutputWindowController.h new file mode 100644 index 00000000..82bc212b --- /dev/null +++ b/CocoaSplit/Interface/CSNewOutputWindowController.h @@ -0,0 +1,38 @@ +// +// CSNewOutputWindowController.h +// CocoaSplit +// +// Created by Zakk on 11/14/15. +// Copyright © 2015 Zakk. All rights reserved. +// + +#import +#import "CSStreamServiceProtocol.h" +#import "CompressionSettingsPanelController.h" + +@class OutputDestination; + +@interface CSNewOutputWindowController : NSWindowController + +- (IBAction)cancelButtonAction:(id)sender; +- (IBAction)addButtonAction:(id)sender; +- (IBAction)openCompressorEdit:(id)sender; + +@property (strong) IBOutlet NSObjectController *outputObjectController; + +@property (strong) OutputDestination *outputDestination; + +@property (strong) NSString *selectedOutputType; +@property (strong) NSObject*streamServiceObject; +@property (strong) NSArray *outputTypes; +@property (strong) IBOutlet NSView *serviceConfigView; +@property (strong) NSViewController *pluginViewController; +@property (strong) NSMutableDictionary *compressors; +@property (strong) CompressionSettingsPanelController *compressionPanelController; +@property (strong) NSString *buttonLabel; + +@property (nonatomic, copy) void (^windowDone)(NSModalResponse response, CSNewOutputWindowController *windowController); + + + +@end diff --git a/CocoaSplit/Interface/CSNewOutputWindowController.m b/CocoaSplit/Interface/CSNewOutputWindowController.m new file mode 100644 index 00000000..788a081f --- /dev/null +++ b/CocoaSplit/Interface/CSNewOutputWindowController.m @@ -0,0 +1,260 @@ +// +// CSNewOutputWindowController.m +// CocoaSplit +// +// Created by Zakk on 11/14/15. +// Copyright © 2015 Zakk. All rights reserved. +// + +#import "CSNewOutputWindowController.h" +#import "CSPluginLoader.h" +#import "OutputDestination.h" + + +@implementation CSNewOutputWindowController + +@synthesize selectedOutputType = _selectedOutputType; +@synthesize outputDestination = _outputDestination; + + +-(instancetype) init +{ + if (self = [self initWithWindowNibName:@"CSNewOutputWindowController"]) + { + NSMutableDictionary *servicePlugins = [[CSPluginLoader sharedPluginLoader] streamServicePlugins]; + + self.outputTypes = servicePlugins.allKeys; + _outputDestination = [[OutputDestination alloc] init]; + self.buttonLabel = @"Add"; + } + + return self; +} + + +-(void)setOutputDestination:(OutputDestination *)outputDestination +{ + _outputDestination = outputDestination; + self.buttonLabel = @"Save"; + + NSString *windowTitlePart = nil; + + if (outputDestination.name) + { + windowTitlePart = outputDestination.name; + } + + if (outputDestination.streamServiceObject) + { + self.streamServiceObject = outputDestination.streamServiceObject; + Class serviceClass = self.streamServiceObject.class; + self.selectedOutputType = [serviceClass label]; + if (!windowTitlePart) + { + windowTitlePart = [self.streamServiceObject.class label]; + } + } + + if (windowTitlePart) + { + self.window.title = [NSString stringWithFormat:@"Output Configuration - %@", windowTitlePart]; + } + + + if (self.outputDestination.compressor_name) + { + id oCompressor = self.compressors[self.outputDestination.compressor_name]; + if (!oCompressor) + { + self.outputDestination.compressor_name = nil; + } + } +} + + +-(OutputDestination *)outputDestination +{ + return _outputDestination; +} + + +-(void)setupServiceView +{ + + if (!self.serviceConfigView) + { + return; + } + + + if (!self.streamServiceObject) + { + if (self.pluginViewController) + { + [self.pluginViewController.view removeFromSuperview]; + self.pluginViewController = nil; + } + } else { + NSViewController *serviceConfigView = [self.streamServiceObject getConfigurationView]; + [self.serviceConfigView addSubview:serviceConfigView.view]; + + [serviceConfigView.view setFrameOrigin:NSMakePoint(0, self.serviceConfigView.frame.size.height - serviceConfigView.view.frame.size.height)]; + self.pluginViewController = serviceConfigView; + } +} + +-(NSString *)selectedOutputType +{ + return _selectedOutputType; +} + + + +-(void)setSelectedOutputType:(NSString *)selectedOutputType +{ + + _selectedOutputType = selectedOutputType; + NSMutableDictionary *servicePlugins = [[CSPluginLoader sharedPluginLoader] streamServicePlugins]; + Class serviceClass = servicePlugins[selectedOutputType]; + + + if (self.streamServiceObject && [self.streamServiceObject isKindOfClass:serviceClass]) + { + [self setupServiceView]; + return; + } + + NSObject*serviceObj; + + if (serviceClass) + { + serviceObj = [[serviceClass alloc] init]; + } + + if (serviceObj) + { + + if (self.pluginViewController) + { + [self.pluginViewController.view removeFromSuperview]; + } + self.pluginViewController = nil; + + self.outputDestination.type_name = [serviceObj.class label]; + self.streamServiceObject = serviceObj; + [self setupServiceView]; + } +} + + +- (void)windowDidLoad { + [super windowDidLoad]; + dispatch_async(dispatch_get_main_queue(), ^{ + [self setupServiceView]; + + + }); + // Implement this method to handle any initialization after your window controller's window has been loaded from its nib file. +} + +- (IBAction)cancelButtonAction:(id)sender +{ + [self.outputObjectController discardEditing]; + [self.pluginViewController discardEditing]; + + if (self.windowDone) + { + self.windowDone(NSModalResponseCancel, self); + } +} + +- (IBAction)addButtonAction:(id)sender +{ + [self.outputObjectController commitEditing]; + [self.pluginViewController commitEditing]; + self.outputDestination.streamServiceObject = self.streamServiceObject; + if (self.windowDone) + { + self.windowDone(NSModalResponseOK, self); + } +} + + +- (IBAction)openCompressorEdit:(id)sender +{ + + NSObject *editCompressor; + + if (!self.outputDestination.compressor_name) + { + return; + } + + editCompressor = self.compressors[self.outputDestination.compressor_name]; + + if (!editCompressor) + { + return; + } + + + self.compressionPanelController = [[CompressionSettingsPanelController alloc] init]; + + if (editCompressor.active) + { + self.compressionPanelController.compressor = editCompressor; + } else { + self.compressionPanelController.compressor = editCompressor.copy; + } + + + [self.window beginSheet:self.compressionPanelController.window completionHandler:^(NSModalResponse returnCode) { + switch (returnCode) { + case NSModalResponseStop: + if (self.compressionPanelController.compressor.active) + { + return; + } + [self willChangeValueForKey:@"compressors"]; + [self.compressors removeObjectForKey:self.compressionPanelController.compressor.name]; + [self didChangeValueForKey:@"compressors"]; + [[NSNotificationCenter defaultCenter] postNotificationName:CSNotificationCompressorDeleted object:self.compressionPanelController.compressor userInfo:nil]; + + break; + case NSModalResponseOK: + { + + + if (!self.compressionPanelController.compressor.active) + { + if (![editCompressor.name isEqualToString:self.compressionPanelController.compressor.name]) + { + [self.compressors removeObjectForKey:editCompressor.name]; + NSDictionary *notifyMsg = [NSDictionary dictionaryWithObjectsAndKeys:editCompressor.name, @"oldName", self.compressionPanelController.compressor, @"compressor", nil]; + [[NSNotificationCenter defaultCenter] postNotificationName:CSNotificationCompressorRenamed object:notifyMsg]; + + } + self.compressors[self.compressionPanelController.compressor.name] = self.compressionPanelController.compressor; + } + [[NSNotificationCenter defaultCenter] postNotificationName:CSNotificationCompressorReconfigured object:self.compressionPanelController.compressor]; + + break; + } + case 4242: + if (self.compressionPanelController.saveProfileName) + { + self.compressionPanelController.compressor.name = self.compressionPanelController.saveProfileName.mutableCopy; + [self willChangeValueForKey:@"compressors"]; + self.compressors[self.compressionPanelController.compressor.name] = self.compressionPanelController.compressor; + [self didChangeValueForKey:@"compressors"]; + [[NSNotificationCenter defaultCenter] postNotificationName:CSNotificationCompressorAdded object:self.compressionPanelController.compressor userInfo:nil]; + + } + default: + break; + } + }]; +} + + +@end diff --git a/CocoaSplit/Interface/CSNewOutputWindowController.xib b/CocoaSplit/Interface/CSNewOutputWindowController.xib new file mode 100644 index 00000000..95761957 --- /dev/null +++ b/CocoaSplit/Interface/CSNewOutputWindowController.xib @@ -0,0 +1,184 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CocoaSplit/Interface/CSNobarSliderCell.h b/CocoaSplit/Interface/CSNobarSliderCell.h new file mode 100644 index 00000000..b93df382 --- /dev/null +++ b/CocoaSplit/Interface/CSNobarSliderCell.h @@ -0,0 +1,13 @@ +// +// CSNobarSliderCell.h +// CocoaSplit +// +// Created by Zakk on 8/13/16. +// Copyright © 2016 Zakk. All rights reserved. +// + +#import + +@interface CSNobarSliderCell : NSSliderCell + +@end diff --git a/CocoaSplit/Interface/CSNobarSliderCell.m b/CocoaSplit/Interface/CSNobarSliderCell.m new file mode 100644 index 00000000..683710ce --- /dev/null +++ b/CocoaSplit/Interface/CSNobarSliderCell.m @@ -0,0 +1,20 @@ +// +// CSNobarSliderCell.m +// CocoaSplit +// +// Created by Zakk on 8/13/16. +// Copyright © 2016 Zakk. All rights reserved. +// + +#import "CSNobarSliderCell.h" + +@implementation CSNobarSliderCell + +-(void)drawBarInside:(NSRect)aRect flipped:(BOOL)flipped +{ + + return; +} + + +@end diff --git a/CocoaSplit/Interface/CSStreamOutputWindowController.h b/CocoaSplit/Interface/CSStreamOutputWindowController.h new file mode 100644 index 00000000..7a7fee2b --- /dev/null +++ b/CocoaSplit/Interface/CSStreamOutputWindowController.h @@ -0,0 +1,27 @@ +// +// CSStreamOutputWindowController.h +// CocoaSplit +// +// Created by Zakk on 8/7/16. +// Copyright © 2016 Zakk. All rights reserved. +// + +#import + + +@class CaptureController; + +@interface CSStreamOutputWindowController : NSWindowController + + +@property (weak) CaptureController *controller; + +@property (weak) IBOutlet NSTableView *outputTableView; +@property (strong) NSIndexSet *selectedCaptureDestinations; + + +- (IBAction)outputEditClicked:(id)sender; +- (IBAction)outputSegmentedAction:(NSButton *)sender; + + +@end diff --git a/CocoaSplit/Interface/CSStreamOutputWindowController.m b/CocoaSplit/Interface/CSStreamOutputWindowController.m new file mode 100644 index 00000000..764c22b9 --- /dev/null +++ b/CocoaSplit/Interface/CSStreamOutputWindowController.m @@ -0,0 +1,67 @@ +// +// CSStreamOutputWindowController.m +// CocoaSplit +// +// Created by Zakk on 8/7/16. +// Copyright © 2016 Zakk. All rights reserved. +// + +#import "CSStreamOutputWindowController.h" +#import "CaptureController.h" + + +@interface CSStreamOutputWindowController () + +@end + +@implementation CSStreamOutputWindowController + + +-(instancetype) init +{ + return [self initWithWindowNibName:@"CSStreamOutputWindowController"]; +} + + + +- (void)windowDidLoad { + [super windowDidLoad]; + + // Implement this method to handle any initialization after your window controller's window has been loaded from its nib file. +} + + +- (IBAction)outputEditClicked:(id)sender +{ + //hax + OutputDestination *toEdit = [self.controller.captureDestinations objectAtIndex:self.outputTableView.clickedRow]; + + [self.controller outputEditClicked:toEdit]; +} + + +- (IBAction)outputSegmentedAction:(NSButton *)sender +{ + NSUInteger clicked = sender.tag; + + switch (clicked) + { + case 0: + { + [self.controller openAddOutputPopover:sender sourceRect:sender.bounds]; + break; + } + case 1: + { + [self.selectedCaptureDestinations enumerateIndexesWithOptions:0 usingBlock:^(NSUInteger idx, BOOL *stop) { + [self.controller removeObjectFromCaptureDestinationsAtIndex:idx]; + }]; + break; + } + default: + break; + } +} + + +@end diff --git a/CocoaSplit/Interface/CSStreamOutputWindowController.xib b/CocoaSplit/Interface/CSStreamOutputWindowController.xib new file mode 100644 index 00000000..3a95d723 --- /dev/null +++ b/CocoaSplit/Interface/CSStreamOutputWindowController.xib @@ -0,0 +1,292 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CocoaSplit/Interface/CSTextCaptureBaseView.xib b/CocoaSplit/Interface/CSTextCaptureBaseView.xib index d2fbbbce..a7f29a60 100644 --- a/CocoaSplit/Interface/CSTextCaptureBaseView.xib +++ b/CocoaSplit/Interface/CSTextCaptureBaseView.xib @@ -1,8 +1,8 @@ - + - + diff --git a/CocoaSplit/Interface/CSx264CompressorViewController.h b/CocoaSplit/Interface/CSx264CompressorViewController.h new file mode 100644 index 00000000..7d29a1bb --- /dev/null +++ b/CocoaSplit/Interface/CSx264CompressorViewController.h @@ -0,0 +1,19 @@ +// +// CSx264CompressorViewController.h +// CocoaSplit +// +// Created by Zakk on 3/28/16. +// Copyright © 2016 Zakk. All rights reserved. +// + +#import +#import "x264Compressor.h" +#import "CSCompressorViewControllerProtocol.h" + +@interface CSx264CompressorViewController : NSViewController + + +@property (strong) x264Compressor *compressor; +@property (strong) NSObjectController *compressorController; + +@end diff --git a/CocoaSplit/Interface/CSx264CompressorViewController.m b/CocoaSplit/Interface/CSx264CompressorViewController.m new file mode 100644 index 00000000..56ac6c1c --- /dev/null +++ b/CocoaSplit/Interface/CSx264CompressorViewController.m @@ -0,0 +1,30 @@ +// +// CSx264CompressorViewController.m +// CocoaSplit +// +// Created by Zakk on 3/28/16. +// Copyright © 2016 Zakk. All rights reserved. +// + +#import "CSx264CompressorViewController.h" + +@interface CSx264CompressorViewController () + +@end + +@implementation CSx264CompressorViewController + + +-(instancetype)init +{ + return [self initWithNibName:@"CSx264CompressorViewController" bundle:nil]; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + // Do view setup here. +} + + + +@end diff --git a/CocoaSplit/Interface/CSx264CompressorViewController.xib b/CocoaSplit/Interface/CSx264CompressorViewController.xib new file mode 100644 index 00000000..27671231 --- /dev/null +++ b/CocoaSplit/Interface/CSx264CompressorViewController.xib @@ -0,0 +1,253 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NSNegateBoolean + + + + + + + + + + + + + + + + + + NSNegateBoolean + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NSNegateBoolean + + + + + + + + + + + + + + + + + + + + + + + NSNegateBoolean + + + + + + + + + + + + + + + + + + + + + + + NSNegateBoolean + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CocoaSplit/Interface/CapturePreview.xib b/CocoaSplit/Interface/CapturePreview.xib deleted file mode 100644 index 098dc4fa..00000000 --- a/CocoaSplit/Interface/CapturePreview.xib +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/CocoaSplit/Interface/CompressionSettingsPanel.xib b/CocoaSplit/Interface/CompressionSettingsPanel.xib index b06fc54a..09f433a9 100644 --- a/CocoaSplit/Interface/CompressionSettingsPanel.xib +++ b/CocoaSplit/Interface/CompressionSettingsPanel.xib @@ -1,21 +1,22 @@ - + - + - + - - - + + + + - + @@ -25,37 +26,14 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - + @@ -160,490 +143,74 @@ Gw - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - NSNegateBoolean - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - No Value - - - - - - - - - - - - - - - - - - - - - - - - - - - No Value - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + - + - + + + + + + + + + + + + + + + + NSNegateBoolean + + + + + + + + + + + + + + + + + + + + + + + + + + + - + @@ -682,6 +249,11 @@ Gw + + + + + diff --git a/CocoaSplit/Interface/CompressionSettingsPanelController.h b/CocoaSplit/Interface/CompressionSettingsPanelController.h new file mode 100644 index 00000000..5148e041 --- /dev/null +++ b/CocoaSplit/Interface/CompressionSettingsPanelController.h @@ -0,0 +1,34 @@ +// +// CompressionSettingsPanelController.h +// CocoaSplit +// +// Created by Zakk on 11/22/15. +// Copyright © 2015 Zakk. All rights reserved. +// + +#import +#import "VideoCompressor.h" +#import "CSCompressorViewControllerProtocol.h" + + +@interface CompressionSettingsPanelController : NSWindowController +@property (weak) IBOutlet NSView *compressorSettingsView; +@property (strong) NSViewController *compressorViewController; + + +@property (strong) IBOutlet NSObjectController *compressorObjectController; + +@property (strong) id compressor; +@property (strong) NSArray *compressorTypes; +@property (strong) NSString *selectedCompressorType; +@property (strong) NSString *saveProfileName; +@property (strong) IBOutlet NSObjectController *baseObjectController; + +-(void)saveCompressPanel; +-(void)deleteCompressPanel; +-(void)closeCompressPanel; +- (IBAction)saveCompressPanel:(id)sender; + + + +@end diff --git a/CocoaSplit/Interface/CompressionSettingsPanelController.m b/CocoaSplit/Interface/CompressionSettingsPanelController.m new file mode 100644 index 00000000..3f07aa68 --- /dev/null +++ b/CocoaSplit/Interface/CompressionSettingsPanelController.m @@ -0,0 +1,97 @@ +// +// CompressionSettingsPanelController.m +// CocoaSplit +// +// Created by Zakk on 11/22/15. +// Copyright © 2015 Zakk. All rights reserved. +// + +#import "CompressionSettingsPanelController.h" + +@interface CompressionSettingsPanelController () + +@end + +@implementation CompressionSettingsPanelController + +-(instancetype) init +{ + if (self = [self initWithWindowNibName:@"CompressionSettingsPanel"]) + { + self.compressorTypes = @[@"x264", @"AppleVTCompressor", @"AppleProResCompressor", @"None"]; + } + + return self; +} + +- (void)windowDidLoad { + [super windowDidLoad]; + [self setupCompressorView]; + + // Implement this method to handle any initialization after your window controller's window has been loaded from its nib file. +} + + +-(void)setupCompressorView +{ + + if (!self.compressorSettingsView) + { + return; + } + + + if (!self.compressor) + { + if (self.compressorViewController) + { + [self.compressorViewController.view removeFromSuperview]; + self.compressorViewController = nil; + } + } else { + id compressorConfigView = [self.compressor getConfigurationView]; + if (compressorConfigView) + { + compressorConfigView.compressorController = self.compressorObjectController; + compressorConfigView.compressor = self.compressor; + + + + [self.compressorSettingsView addSubview:((NSViewController *)compressorConfigView).view]; + + [((NSViewController *)compressorConfigView).view setFrameOrigin:NSMakePoint(0, self.compressorSettingsView.frame.size.height - ((NSViewController *)compressorConfigView).view.frame.size.height)]; + self.compressorViewController = (NSViewController *)compressorConfigView; + } + } +} + + +-(void)saveCompressPanel +{ + [self.compressorObjectController commitEditing]; + [self.window.sheetParent endSheet:self.window returnCode:NSModalResponseOK]; + +} + +-(void)deleteCompressPanel +{ + + [self.window.sheetParent endSheet:self.window returnCode:NSModalResponseStop]; +} + +-(void)closeCompressPanel +{ + [self.compressorObjectController discardEditing]; + [self.window.sheetParent endSheet:self.window returnCode:NSModalResponseCancel]; +} + +- (IBAction)saveCompressPanel:(id)sender +{ + [self.compressorObjectController commitEditing]; + [self.baseObjectController commitEditing]; + [self.window.sheetParent endSheet:self.window returnCode:4242]; + +} + + +@end diff --git a/CocoaSplit/Interface/EditBuiltinLayoutView.xib b/CocoaSplit/Interface/EditBuiltinLayoutView.xib new file mode 100644 index 00000000..17d9503d --- /dev/null +++ b/CocoaSplit/Interface/EditBuiltinLayoutView.xib @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CocoaSplit/Interface/InputPopupControllerViewController.xib b/CocoaSplit/Interface/InputPopupControllerViewController.xib index 5954c30d..e36b66a2 100644 --- a/CocoaSplit/Interface/InputPopupControllerViewController.xib +++ b/CocoaSplit/Interface/InputPopupControllerViewController.xib @@ -1,8 +1,8 @@ - + - + @@ -36,7 +36,7 @@ - + @@ -59,7 +59,7 @@ - + @@ -71,15 +71,15 @@ - + - - + @@ -114,7 +100,7 @@ - + @@ -126,14 +112,14 @@ - + - + @@ -141,7 +127,7 @@ - + @@ -153,14 +139,14 @@ - + - + @@ -168,7 +154,7 @@ - + @@ -180,233 +166,14 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -414,14 +181,14 @@ - + - + @@ -429,7 +196,7 @@ - + @@ -440,7 +207,7 @@ - + @@ -448,7 +215,7 @@ - + @@ -458,60 +225,8 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -519,14 +234,14 @@ - + - - - - - - - - - + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + - + - - - + + + - + - - - + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - @@ -787,6 +492,247 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -958,12 +904,12 @@ - - + + - + @@ -1028,13 +974,15 @@ + - + @@ -1722,12 +1670,12 @@ - - + + - + @@ -1758,35 +1706,14 @@ - - @@ -1797,12 +1724,12 @@ - - + + - + @@ -1834,25 +1761,14 @@ - @@ -1863,12 +1779,12 @@ - - + + - + @@ -1899,25 +1815,14 @@ - @@ -1926,59 +1831,6 @@ - - - - - @@ -2009,6 +1861,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -2070,6 +1979,7 @@ + diff --git a/CocoaSplit/Interface/LogWindow.xib b/CocoaSplit/Interface/LogWindow.xib index 7f9b073a..e4c9cce2 100644 --- a/CocoaSplit/Interface/LogWindow.xib +++ b/CocoaSplit/Interface/LogWindow.xib @@ -1,8 +1,8 @@ - + - - + + @@ -12,19 +12,18 @@ - + - + - + - @@ -34,10 +33,10 @@ - + - + diff --git a/CocoaSplit/Interface/NewLayoutPanel.xib b/CocoaSplit/Interface/NewLayoutPanel.xib deleted file mode 100644 index d1e19354..00000000 --- a/CocoaSplit/Interface/NewLayoutPanel.xib +++ /dev/null @@ -1,77 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/CocoaSplit/Interface/PluginManagerWindowController.xib b/CocoaSplit/Interface/PluginManagerWindowController.xib index 49481fa0..3ea36972 100644 --- a/CocoaSplit/Interface/PluginManagerWindowController.xib +++ b/CocoaSplit/Interface/PluginManagerWindowController.xib @@ -1,8 +1,8 @@ - + - + @@ -23,18 +23,18 @@ - - + + - + - + - + @@ -59,13 +59,15 @@ - + diff --git a/CocoaSplit/Interface/advancedPrefPanel.xib b/CocoaSplit/Interface/advancedPrefPanel.xib index fada8bc7..c2b6c8ef 100644 --- a/CocoaSplit/Interface/advancedPrefPanel.xib +++ b/CocoaSplit/Interface/advancedPrefPanel.xib @@ -1,8 +1,8 @@ - + - + @@ -12,30 +12,17 @@ - - + + - + - + - - + @@ -53,31 +40,31 @@ DQ - - + + - - + + - - - + + - + @@ -112,7 +89,7 @@ DQ - + @@ -123,7 +100,7 @@ DQ - + @@ -134,15 +111,15 @@ DQ - - + + - + @@ -153,8 +130,8 @@ DQ - - + + - + @@ -181,16 +158,97 @@ DQ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + diff --git a/CocoaSplit/LayoutRenderer.m b/CocoaSplit/LayoutRenderer.m index 794fe27a..7729065d 100644 --- a/CocoaSplit/LayoutRenderer.m +++ b/CocoaSplit/LayoutRenderer.m @@ -112,6 +112,7 @@ glLoadIdentity(); glOrtho(0, _cvpool_size.width, 0,_cvpool_size.height, -1, 1); + NSLog(@"CVPOOL %@", NSStringFromSize(_cvpool_size)); glMatrixMode(GL_MODELVIEW); @@ -128,7 +129,7 @@ CGLSetCurrentContext(self.cglCtx); - + if (!self.rootLayer) { self.rootLayer = [CALayer layer]; @@ -136,58 +137,27 @@ } - - _transitionLayout = _currentLayout; - if (self.transitionName && _transitionLayout) + + [CATransaction begin]; + _currentLayout.inTransition = NO; + + if (_transitionLayout) { - [CATransaction begin]; - [CATransaction setDisableActions:YES]; - _layoutTransition = [CATransition animation]; - _layoutTransition.type = self.transitionName ; - _layoutTransition.subtype = self.transitionDirection; - _layoutTransition.duration = self.transitionDuration; - _layoutTransition.filter = self.transitionFilter; - _layoutTransition.removedOnCompletion = YES; - [CATransaction setCompletionBlock:^{ - [CATransaction begin]; - if (_transitionLayout) - { - [_transitionLayout.rootLayer removeFromSuperlayer]; - _transitionLayout.isActive = NO; - _transitionLayout = nil; - } - - - [CATransaction commit]; - }]; - [self.rootLayer addAnimation:_layoutTransition forKey:nil]; - [self.rootLayer addSublayer:self.layout.rootLayer]; - [CATransaction commit]; - - } else { - [CATransaction begin]; - _currentLayout.inTransition = NO; - - if (_transitionLayout) - { - - [_transitionLayout.rootLayer removeFromSuperlayer]; - _transitionLayout.isActive = NO; - _transitionLayout = nil; - } - - [self.renderer.layer addSublayer:self.layout.rootLayer]; - [CATransaction commit]; + [_transitionLayout.rootLayer removeFromSuperlayer]; + _transitionLayout.isActive = NO; + _transitionLayout = nil; } - - + [self.renderer.layer addSublayer:self.layout.rootLayer]; + [CATransaction commit]; + + + _currentLayout = self.layout; [_currentLayout didBecomeVisible]; - } -(void)renderToSurface:(IOSurfaceRef)ioSurface @@ -218,6 +188,7 @@ glDepthMask(GL_FALSE); + CGLTexImageIOSurface2D(self.cglCtx, GL_TEXTURE_RECTANGLE_ARB, GL_RGBA, (int)IOSurfaceGetWidth(ioSurface), (int)IOSurfaceGetHeight(ioSurface), GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, ioSurface, 0); GLenum fboStatus; @@ -248,6 +219,10 @@ -(CVPixelBufferRef)currentImg { + if (self.cglCtx) + { + CGLSetCurrentContext(self.cglCtx); + } CVPixelBufferRef destFrame = NULL; CGFloat frameWidth, frameHeight; @@ -284,7 +259,7 @@ } - CVPixelBufferPoolCreatePixelBuffer(kCVReturnSuccess, _cvpool, &destFrame); + CVPixelBufferPoolCreatePixelBuffer(kCFAllocatorDefault, _cvpool, &destFrame); [self renderToSurface:CVPixelBufferGetIOSurface(destFrame)]; @@ -317,7 +292,10 @@ @synchronized(self) { - CVPixelBufferRetain(_currentPB); + if (_currentPB) + { + CVPixelBufferRetain(_currentPB); + } return _currentPB; } } diff --git a/CocoaSplit/Media.xcassets/Contents.json b/CocoaSplit/Media.xcassets/Contents.json new file mode 100644 index 00000000..da4a164c --- /dev/null +++ b/CocoaSplit/Media.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/CocoaSplit/Media.xcassets/Mute_Icon.imageset/Contents.json b/CocoaSplit/Media.xcassets/Mute_Icon.imageset/Contents.json new file mode 100644 index 00000000..2347ca6e --- /dev/null +++ b/CocoaSplit/Media.xcassets/Mute_Icon.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "Mute_Icon.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/CocoaSplit/Media.xcassets/Mute_Icon.imageset/Mute_Icon.png b/CocoaSplit/Media.xcassets/Mute_Icon.imageset/Mute_Icon.png new file mode 100644 index 00000000..b440ae36 Binary files /dev/null and b/CocoaSplit/Media.xcassets/Mute_Icon.imageset/Mute_Icon.png differ diff --git a/CocoaSplit/Media.xcassets/Speaker_Icon.imageset/Contents.json b/CocoaSplit/Media.xcassets/Speaker_Icon.imageset/Contents.json new file mode 100644 index 00000000..d99359f3 --- /dev/null +++ b/CocoaSplit/Media.xcassets/Speaker_Icon.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "Speaker_Icon.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/CocoaSplit/Media.xcassets/Speaker_Icon.imageset/Speaker_Icon.png b/CocoaSplit/Media.xcassets/Speaker_Icon.imageset/Speaker_Icon.png new file mode 100644 index 00000000..8ae1407e Binary files /dev/null and b/CocoaSplit/Media.xcassets/Speaker_Icon.imageset/Speaker_Icon.png differ diff --git a/CocoaSplit/NSView+NSLayoutConstraintFilter.h b/CocoaSplit/NSView+NSLayoutConstraintFilter.h new file mode 100755 index 00000000..ed078168 --- /dev/null +++ b/CocoaSplit/NSView+NSLayoutConstraintFilter.h @@ -0,0 +1,37 @@ +// +// NSView+NSLayoutConstraintFilter.h +// Expanding Search +// +// Created by Ilija Tovilo on 12/13/12. +// Copyright (c) 2012 Ilija Tovilo. All rights reserved. +// + +// Copyright (c) 2012, Ilija Tovilo +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation and/or +// other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#import + +@interface NSView (NSLayoutConstraintFilter) +- (NSLayoutConstraint *)constraintForAttribute:(NSLayoutAttribute)attribute; +- (NSArray *)constaintsForAttribute:(NSLayoutAttribute)attribute; +@end \ No newline at end of file diff --git a/CocoaSplit/NSView+NSLayoutConstraintFilter.m b/CocoaSplit/NSView+NSLayoutConstraintFilter.m new file mode 100755 index 00000000..2c082bc3 --- /dev/null +++ b/CocoaSplit/NSView+NSLayoutConstraintFilter.m @@ -0,0 +1,53 @@ +// +// NSView+NSLayoutConstraintFilter.m +// Expanding Search +// +// Created by Ilija Tovilo on 12/13/12. +// Copyright (c) 2012 Ilija Tovilo. All rights reserved. +// + +// Copyright (c) 2012, Ilija Tovilo +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation and/or +// other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#import "NSView+NSLayoutConstraintFilter.h" + +@implementation NSView (NSLayoutConstraintFilter) + +- (NSArray *)constaintsForAttribute:(NSLayoutAttribute)attribute { + NSPredicate *predicate = [NSPredicate predicateWithFormat:@"firstAttribute = %d", attribute]; + NSArray *filteredArray = [[self constraints] filteredArrayUsingPredicate:predicate]; + + return filteredArray; +} + +- (NSLayoutConstraint *)constraintForAttribute:(NSLayoutAttribute)attribute { + NSArray *constraints = [self constaintsForAttribute:attribute]; + + if (constraints.count) { + return constraints[0]; + } + + return nil; +} + +@end diff --git a/CocoaSplit/OutputDestination.h b/CocoaSplit/OutputDestination.h index d52ca76f..2f7c0ac9 100644 --- a/CocoaSplit/OutputDestination.h +++ b/CocoaSplit/OutputDestination.h @@ -8,31 +8,46 @@ #import #import -#import "h264Compressor.h" +#import "VideoCompressor.h" #import "CaptureController.h" @class FFMpegTask; -@interface OutputDestination : NSObject +@interface OutputDestination : NSObject { - NSString *_destination; NSString *_name; BOOL _active; double _output_start_time; NSMutableArray *_delayBuffer; BOOL _stopped; + NSString *_destination; + dispatch_queue_t _output_queue; + int _p_buffered_frame_count; + int _p_buffered_frame_size; + int _p_dropped_frame_count; + int _p_input_framecnt; + int _p_output_framecnt; + int _p_output_bytes; + int _pending_frame_count; + int _consecutive_dropped_frames; + bool _output_prepared; + CFAbsoluteTime _input_frame_timestamp; + CFAbsoluteTime _output_frame_timestamp; } + +@property (assign) BOOL errored; @property (strong) NSString *server_name; @property (strong) NSString *type_name; -@property (strong) NSString *destination; +@property (strong) NSString *type_class_name; +@property (readonly) NSString *destination; @property (strong) NSString *output_format; @property (strong) NSString *stream_key; @property (assign) int stream_delay; @@ -42,6 +57,7 @@ @property (assign) NSUInteger delay_buffer_frames; @property (assign) BOOL buffer_draining; @property (strong) NSString *name; +@property (strong) NSObject*streamServiceObject; //stats, mostly we just interrogate the ffmpeg_out object for these, but bouncing //through this class allows us to be a bit smarter about the UI status updates @@ -52,7 +68,7 @@ @property (assign) int dropped_frame_count; @property (assign) double output_framerate; @property (assign) double output_bitrate; -@property (strong) NSObject *compressor; +@property (strong) NSObject *compressor; @property (strong) NSString *compressor_name; diff --git a/CocoaSplit/OutputDestination.m b/CocoaSplit/OutputDestination.m index 9e5774c8..506cd949 100644 --- a/CocoaSplit/OutputDestination.m +++ b/CocoaSplit/OutputDestination.m @@ -14,42 +14,58 @@ @synthesize name = _name; +@synthesize output_format = _output_format; + + + +-(instancetype)copyWithZone:(NSZone *)zone +{ + OutputDestination *newCopy = [[OutputDestination alloc] init]; + newCopy.name = self.name; + newCopy.type_name = self.type_name; + newCopy.type_class_name = self.type_class_name; + newCopy.active = self.active; + newCopy.stream_delay = self.stream_delay; + newCopy.compressor_name = self.compressor_name; + newCopy.streamServiceObject = self.streamServiceObject; + newCopy->_destination = _destination; + return newCopy; +} -(void) encodeWithCoder:(NSCoder *)aCoder { - [aCoder encodeObject:self.destination forKey:@"destination"]; [aCoder encodeObject:self.name forKey:@"name"]; [aCoder encodeObject:self.type_name forKey:@"type_name"]; - [aCoder encodeObject:self.output_format forKey:@"output_format"]; + [aCoder encodeObject:self.type_class_name forKey:@"type_class_name"]; [aCoder encodeBool:self.active forKey:@"active"]; [aCoder encodeInteger:self.stream_delay forKey:@"stream_delay"]; [aCoder encodeObject:self.compressor_name forKey:@"compressor_name"]; - + [aCoder encodeObject:self.streamServiceObject forKey:@"streamServiceObject"]; + [aCoder encodeObject:_destination forKey:@"destination"]; } + -(id) initWithCoder:(NSCoder *)aDecoder { if (self = [self init]) { - - self.destination = [aDecoder decodeObjectForKey:@"destination"]; + _destination = [aDecoder decodeObjectForKey:@"destination"]; self.name = [aDecoder decodeObjectForKey:@"name"]; self.type_name = [aDecoder decodeObjectForKey:@"type_name"]; - self.output_format = [aDecoder decodeObjectForKey:@"output_format"]; self.active = [aDecoder decodeBoolForKey:@"active"]; self.stream_delay = (int)[aDecoder decodeIntegerForKey:@"stream_delay"]; self.compressor_name = [aDecoder decodeObjectForKey:@"compressor_name"]; + self.streamServiceObject = [aDecoder decodeObjectForKey:@"streamServiceObject"]; + self.type_class_name = [aDecoder decodeObjectForKey:@"type_class_name"]; } - - - return self; } + -(id)init { @@ -71,19 +87,15 @@ { if (_destination) - return _destination; - - - if ([_type_name isEqualToString:@"rtmp"]) { - _destination = [NSString stringWithFormat:@"%@/%@", self.server_name, self.stream_key]; - + return _destination; } - return _destination; - + return [self.streamServiceObject getServiceDestination]; } + + -(NSString *)name { if (_name) @@ -91,7 +103,7 @@ return _name; } - return _destination; + return self.destination; } @@ -106,6 +118,13 @@ -(void) setActive:(BOOL)is_active { + if (!_output_queue) + { + NSString *queue_name = [NSString stringWithFormat:@"Output Queue %@", self.name]; + _output_queue = dispatch_queue_create(queue_name.UTF8String, NULL); + } + + if (is_active != _active) { _active = is_active; @@ -114,6 +133,7 @@ [self stopCompressor]; [self reset]; } else { + [self initStatsValues]; [self setupCompressor]; } @@ -128,31 +148,38 @@ --(void)setDestination:(NSString *)destination -{ - - if ([destination hasPrefix:@"rtmp://"] || [destination hasPrefix:@"udp:"]) - { - self.output_format = @"FLV"; - } - _destination = destination; - -} - -(void) reset { + _output_prepared = NO; + self.buffer_draining = NO; [_delayBuffer removeAllObjects]; - + [self initStatsValues]; if (self.ffmpeg_out) { - [self.ffmpeg_out stopProcess]; - [self.ffmpeg_out removeObserver:self forKeyPath:@"errored"]; - [self.ffmpeg_out removeObserver:self forKeyPath:@"active"]; + if (self.errored) + { + //errors jump the queue and just kill the ffmpeg output + [self.ffmpeg_out stopProcess]; + self.ffmpeg_out = nil; + dispatch_async(dispatch_get_main_queue(), ^{ + self.textColor = [NSColor redColor]; + }); + } else { + dispatch_async(_output_queue, ^{ + @autoreleasepool { + + [self.ffmpeg_out stopProcess]; + self.ffmpeg_out = nil; + } + dispatch_async(dispatch_get_main_queue(), ^{ + self.textColor = [NSColor blackColor]; + }); + + }); + } - - self.ffmpeg_out = nil; } _output_start_time = 0.0f; @@ -177,6 +204,7 @@ if (self.active) { [self reset]; + [self stopCompressor]; } } @@ -193,13 +221,69 @@ _delayBuffer = [[NSMutableArray alloc] init]; self.delay_buffer_frames = 0; _stopped = YES; - + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(compressorDeleted:) name:CSNotificationCompressorDeleted object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(compressorRenamed:) name:CSNotificationCompressorRenamed object:nil]; + + } return self; } +-(void)setOutput_format:(NSString *)output_format +{ + _output_format = output_format; +} + + +-(NSString *)output_format +{ + if (_output_format) + { + return _output_format; + } + + + if (self.streamServiceObject) + { + if ([self.streamServiceObject respondsToSelector:@selector(getServiceFormat)]) + { + return [self.streamServiceObject getServiceFormat]; + } + } + + return nil; +} + + +-(void)compressorDeleted:(NSNotification *)notification +{ + id compressor = notification.object; + + if (self.compressor_name && [self.compressor_name isEqualToString:compressor.name]) + { + self.compressor_name = nil; + self.compressor = nil; + } +} + + +-(void)compressorRenamed:(NSNotification *)notification +{ + + NSDictionary *infoDict = notification.object; + + NSString *oldName = infoDict[@"oldName"]; + id compressor = infoDict[@"compressor"]; + + if (self.compressor_name && [self.compressor_name isEqualToString:oldName]) + { + self.compressor_name = compressor.name; + self.compressor = compressor; + } +} + -(void) attachOutput { @@ -216,6 +300,19 @@ newout = self.ffmpeg_out; } + if (!_output_prepared) + { + [self.streamServiceObject prepareForStreamStart]; + _output_prepared = YES; + } + + NSString *destination = self.destination; + + if (!destination) + { + return; + } + /* if (self.stream_delay > 0) @@ -224,9 +321,9 @@ } */ - + newout.video_codec_id = self.compressor.codec_id; newout.framerate = self.settingsController.captureFPS; - newout.stream_output = [self.destination stringByStandardizingPath]; + newout.stream_output = [destination stringByStandardizingPath]; newout.stream_format = self.output_format; newout.settingsController = self.settingsController; newout.samplerate = self.settingsController.audioSamplerate; @@ -236,22 +333,47 @@ self.ffmpeg_out = newout; - [self.ffmpeg_out addObserver:self forKeyPath:@"errored" options:NSKeyValueObservingOptionNew context:NULL]; - [self.ffmpeg_out addObserver:self forKeyPath:@"active" options:NSKeyValueObservingOptionNew context:NULL]; - self.ffmpeg_out.active = self.active; +} + + +- (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context +{ + + NSColor *newColor = nil; + + if ([keyPath isEqualToString:@"errored"]) { + + BOOL errVal = [[change objectForKey:NSKeyValueChangeNewKey] boolValue]; + + if (errVal == YES) + { + self.errored = YES; + newColor = [NSColor redColor]; + } + + } + + + if (newColor) + { + dispatch_async(dispatch_get_main_queue(), ^{ + self.textColor = newColor; + }); + } + } -(void) setupCompressor { - if (!self.active) + if (!self.active || !self.settingsController.captureRunning) { return; } - NSObject *old_compressor = self.compressor; + NSObject *old_compressor = self.compressor; if (self.compressor_name) { @@ -261,9 +383,7 @@ if (!self.compressor) { - NSLog(@"NO COMPRESSOR, SETTING TO %@", self.settingsController.selectedCompressor); - - self.compressor = self.settingsController.selectedCompressor; + NSLog(@"NO COMPRESSOR"); } @@ -282,42 +402,35 @@ } } -- (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context + + +-(bool) resetOutputIfNeeded { - - NSColor *newColor = nil; - - if ([keyPath isEqualToString:@"active"]) + if (self.settingsController.maxOutputDropped) { - BOOL activeVal = [[change objectForKey:NSKeyValueChangeNewKey] boolValue]; - if (activeVal == YES) + if (_consecutive_dropped_frames >= self.settingsController.maxOutputDropped) { - newColor = [NSColor greenColor]; - } else { - newColor = [NSColor blackColor]; + return YES; } - - } else if ([keyPath isEqualToString:@"errored"]) { - - BOOL errVal = [[change objectForKey:NSKeyValueChangeNewKey] boolValue]; - - if (errVal == YES) - { - newColor = [NSColor redColor]; - } - } - - - if (newColor) - { - dispatch_async(dispatch_get_main_queue(), ^{ - self.textColor = newColor; - }); - } - + return NO; } + +-(bool) shouldDropFrame +{ + if (self.settingsController.maxOutputPending) + { + if (_pending_frame_count >= self.settingsController.maxOutputPending) + { + return YES; + } + } + + return NO; +} + + -(void) writeEncodedData:(CapturedFrameData *)frameData { @@ -334,32 +447,90 @@ } - if (frameData) + if (frameData && self.stream_delay > 0) { [_delayBuffer addObject:frameData]; } + BOOL start_stream = NO; - - if ((current_time >= _output_start_time) && ([_delayBuffer count] > 0)) + if (self.settingsController.captureRunning && !self.ffmpeg_out) { - - if (self.settingsController.captureRunning && !self.ffmpeg_out) + if (self.stream_delay == 0) { - [self attachOutput]; + start_stream = YES; } + if ((current_time >= _output_start_time) && ([_delayBuffer count] > 0)) + { + + start_stream = YES; + } + } + + if (start_stream) + { - sendData = [_delayBuffer objectAtIndex:0]; - [_delayBuffer removeObjectAtIndex:0]; + [self attachOutput]; + dispatch_async(dispatch_get_main_queue(), ^{ + self.textColor = [NSColor greenColor]; + }); + } + + if (frameData) + { + _p_input_framecnt++; } + if (self.ffmpeg_out) + { + if (_delayBuffer.count > 0) + { + sendData = [_delayBuffer objectAtIndex:0]; + [_delayBuffer removeObjectAtIndex:0]; + } else { + sendData = frameData; + } + } + if (sendData && self.ffmpeg_out) { - [self.ffmpeg_out writeEncodedData:sendData]; + NSInteger f_size = [sendData encodedDataLength]; + + if ([self resetOutputIfNeeded]) + { + self.errored = YES; + self.active = NO; + return; + } + if ([self shouldDropFrame]) + { + _dropped_frame_count++; + _consecutive_dropped_frames++; + } else { + _consecutive_dropped_frames = 0; + + dispatch_async(_output_queue, ^{ + @autoreleasepool { + _p_buffered_frame_size += f_size; + _p_buffered_frame_count++; + + BOOL write_ret = [self.ffmpeg_out writeEncodedData:sendData]; + + if (write_ret) + { + _p_output_framecnt++; + _p_buffered_frame_count--; + _p_buffered_frame_size -= f_size; + _p_output_bytes += f_size; + } + } + + }); + } } if (self.buffer_draining) @@ -373,22 +544,43 @@ } +-(void) initStatsValues +{ + CFAbsoluteTime time_now = CFAbsoluteTimeGetCurrent(); + _input_frame_timestamp = time_now; + _output_frame_timestamp = time_now; + _p_input_framecnt = 0; + _p_buffered_frame_count = 0; + _p_buffered_frame_size = 0; + _p_dropped_frame_count = 0; + _p_output_framecnt = 0; + _p_output_bytes = 0; +} + -(void) updateStatistics { - if (self.ffmpeg_out) - { - [self.ffmpeg_out updateInputStats]; - [self.ffmpeg_out updateOutputStats]; - self.output_framerate = self.ffmpeg_out.output_framerate; - self.output_bitrate = self.ffmpeg_out.output_bitrate; - self.input_framerate = self.ffmpeg_out.input_framerate; - self.dropped_frame_count = self.ffmpeg_out.dropped_frame_count; - self.buffered_frame_count = self.ffmpeg_out.buffered_frame_count; - self.buffered_frame_size = self.ffmpeg_out.buffered_frame_size; - } + CFAbsoluteTime time_now = CFAbsoluteTimeGetCurrent(); + double calculated_input_framerate = _p_input_framecnt / (time_now - _input_frame_timestamp); + double calculated_output_framerate = _p_output_framecnt / (time_now - _output_frame_timestamp); + double calculated_output_bitrate = (_p_output_bytes / (time_now - _output_frame_timestamp)) * 8; + + _p_input_framecnt = 0; + _p_output_framecnt = 0; + _output_frame_timestamp = time_now; + _input_frame_timestamp = time_now; + _p_output_bytes = 0; + + + self.output_framerate = calculated_output_framerate; + self.input_framerate = calculated_input_framerate; + self.output_bitrate = calculated_output_bitrate; + self.buffered_frame_count = _p_buffered_frame_count; + self.buffered_frame_size = _p_buffered_frame_size; + //TODO + self.dropped_frame_count = 0; self.delay_buffer_frames = [_delayBuffer count]; } @@ -409,13 +601,9 @@ if (self.ffmpeg_out) { - [self.ffmpeg_out removeObserver:self forKeyPath:@"errored" context:NULL]; - [self.ffmpeg_out removeObserver:self forKeyPath:@"active" context:NULL]; [self.compressor removeObserver:self forKeyPath:@"errored" context:NULL]; - - } - + [[NSNotificationCenter defaultCenter] removeObserver:self]; } diff --git a/CocoaSplit/PluginHeaders/.DS_Store b/CocoaSplit/PluginHeaders/.DS_Store new file mode 100644 index 00000000..2d19ef16 Binary files /dev/null and b/CocoaSplit/PluginHeaders/.DS_Store differ diff --git a/CocoaSplit/PluginHeaders/CAMultiAudioPCM.h b/CocoaSplit/PluginHeaders/CAMultiAudioPCM.h index d01b92c3..71c1b47e 100644 --- a/CocoaSplit/PluginHeaders/CAMultiAudioPCM.h +++ b/CocoaSplit/PluginHeaders/CAMultiAudioPCM.h @@ -18,7 +18,7 @@ { size_t _audioBufferListSize; size_t _audioBufferDataSize; - AudioBufferList *_pcmData; + bool _alloced_buffers; } @property (assign) ScheduledAudioSlice *audioSlice; @@ -26,6 +26,10 @@ @property (assign) int frameCount; @property (weak) id player; @property (assign) AudioStreamBasicDescription pcmFormat; +@property (assign) AudioBufferList *pcmData; +@property (assign) bool handleFreeBuffer; + + -(instancetype)initWithAudioBufferList:(AudioBufferList *)bufferList streamFormat:(const AudioStreamBasicDescription *)streamFormat; -(instancetype)initWithDescription:(const AudioStreamBasicDescription *)streamFormat forFrameCount:(int)forFrameCount; diff --git a/CocoaSplit/PluginHeaders/CSCaptureBase.h b/CocoaSplit/PluginHeaders/CSCaptureBase.h index b1c4afe3..f72ee2b2 100644 --- a/CocoaSplit/PluginHeaders/CSCaptureBase.h +++ b/CocoaSplit/PluginHeaders/CSCaptureBase.h @@ -104,6 +104,8 @@ typedef enum frame_render_behavior_t { -(void)setDeviceForUniqueID:(NSString *)uniqueID; -(NSView *)configurationView; +(NSString *) label; +-(NSString *)label; + //Class method to run code that messes with the CALayer(s). It has to be on the main thread even if it isn't in a view :( //All this method does is dispatch_sync to the main thread OR run the block immediately if we're already on the main thread +(void) layoutModification:(void (^)())modBlock; @@ -115,4 +117,6 @@ typedef enum frame_render_behavior_t { -(void)frameArrived; +-(NSImage *)libraryImage; + @end diff --git a/CocoaSplit/PluginHeaders/CSCaptureSourceProtocol.h b/CocoaSplit/PluginHeaders/CSCaptureSourceProtocol.h index a6e11eac..66940bad 100644 --- a/CocoaSplit/PluginHeaders/CSCaptureSourceProtocol.h +++ b/CocoaSplit/PluginHeaders/CSCaptureSourceProtocol.h @@ -118,9 +118,13 @@ If your source is 'shared' between inputSources each new one will call this func -(NSViewController *)configurationView; +(NSString *) label; +-(NSString *)label; + -(void)frameArrived; +-(NSImage *)libraryImage; + diff --git a/CocoaSplit/PluginHeaders/CSNotifications.h b/CocoaSplit/PluginHeaders/CSNotifications.h index 89cfbff5..b6cde066 100644 --- a/CocoaSplit/PluginHeaders/CSNotifications.h +++ b/CocoaSplit/PluginHeaders/CSNotifications.h @@ -11,6 +11,8 @@ extern NSString *const CSNotificationLayoutAdded; extern NSString *const CSNotificationLayoutDeleted; +extern NSString *const CSNotificationLayoutCanvasChanged; +extern NSString *const CSNotificationLayoutFramerateChanged; @@ -24,6 +26,9 @@ extern NSString *const CSNotificationOutputDeleted; extern NSString *const CSNotificationCompressorAdded; extern NSString *const CSNotificationCompressorDeleted; +extern NSString *const CSNotificationCompressorRenamed; +extern NSString *const CSNotificationCompressorReconfigured; + extern NSString *const CSNotificationInputAdded; @@ -31,6 +36,10 @@ extern NSString *const CSNotificationInputDeleted; extern NSString *const CSNotificationInputSelected; +extern NSString *const CSNotificationInputAttached; +extern NSString *const CSNotificationInputDetached; + +extern NSString *const CSNotificationLayoutModeChanged; diff --git a/CocoaSplit/PluginHeaders/CSOauth2Authenticator.h b/CocoaSplit/PluginHeaders/CSOauth2Authenticator.h new file mode 100644 index 00000000..432b1c4f --- /dev/null +++ b/CocoaSplit/PluginHeaders/CSOauth2Authenticator.h @@ -0,0 +1,63 @@ +// +// CSOauth2Authenticator.h +// CocoaSplit +// +// Created by Zakk on 7/17/16. +// Copyright © 2016 Zakk. All rights reserved. +// + +#import +#import + +extern NSString *const kCSOauth2ConfigRedirectURL; +extern NSString *const kCSOauth2ConfigScopes; +extern NSString *const kCSOauth2ConfigAuthURL; +extern NSString *const kCSOauth2AccessTokenRequestURL; +extern NSString *const kCSOauth2AccessRefreshURL; +extern NSString *const kCSOauth2CodeFlow; +extern NSString *const kCSOauth2ImplicitGrantFlow; +extern NSString *const kCSOauth2ExtraAuthParams; +extern NSString *const kCSOauth2ClientSecret; + + + + + + +@interface CSOauth2Authenticator : NSObject +{ + NSWindow *_authWindow; + WebView *_authWebView; + void (^_authorizeCallback)(bool success); +} + + +@property (strong) NSString *clientID; +@property (assign) bool forceVerify; +@property (strong) NSURL *authURL; +@property (strong) NSString *serviceName; +@property (strong) NSString *accessToken; +@property (strong) NSString *accountName; +@property (assign) bool useKeychain; +@property (strong) NSString *flowType; + +@property (nonatomic, copy) void(^accountNameFetcher)(CSOauth2Authenticator *authenticator); +@property (strong) NSDictionary *extraAuthParams; + + + + +-(instancetype) initWithServiceName:(NSString *)serviceName clientID:(NSString *)client_id flowType:(NSString *)flow_type config:(NSDictionary *)config_dict; + + +-(void)jsonRequest:(NSMutableURLRequest *)request completionHandler:(void (^)(id decodedData))handler; + +-(void)saveToKeychain:(NSString *)accountName; +-(void)authorize:(void (^)(bool success))authCallback; +-(void)configurationVariableSet:(id)val forName:(NSString *)forName; +-(id)configurationVariableGet:(NSString *)forName; +-(id)configurationVariableRemove:(NSString *)forName; + + + +@end diff --git a/CocoaSplit/PluginHeaders/CSPcmPlayer.h b/CocoaSplit/PluginHeaders/CSPcmPlayer.h index 26ad7b6d..1ce512dd 100644 --- a/CocoaSplit/PluginHeaders/CSPcmPlayer.h +++ b/CocoaSplit/PluginHeaders/CSPcmPlayer.h @@ -18,9 +18,15 @@ @property (strong) NSString *name; @property (strong) NSString *nodeUID; +@property (readonly) NSUInteger pendingFrames; +@property (nonatomic, copy) void (^completedBlock)(CAMultiAudioPCM *pcmBuffer); +@property (assign) bool muted; -(void)scheduleBuffer:(CMSampleBufferRef)sampleBuffer; -(bool)playPcmBuffer:(CAMultiAudioPCM *)pcmBuffer; +-(void)play; +-(void)pause; +-(void)flush; @end diff --git a/CocoaSplit/PluginHeaders/CSPluginServices.h b/CocoaSplit/PluginHeaders/CSPluginServices.h index 994dac1c..e800f5f9 100644 --- a/CocoaSplit/PluginHeaders/CSPluginServices.h +++ b/CocoaSplit/PluginHeaders/CSPluginServices.h @@ -8,17 +8,26 @@ #import #import "CSPcmPlayer.h" +#import "CSOauth2Authenticator.h" + @interface CSPluginServices : NSObject -+(id)sharedPluginServices; ++(CSPluginServices *)sharedPluginServices; -(CSPcmPlayer *)createPCMInput:(NSString *)forUID withFormat:(const AudioStreamBasicDescription *)withFormat; -(void)removePCMInput:(CSPcmPlayer *)toRemove; -(void)loadPythonClass:(NSString *)pyClass fromFile:(NSString *)fromFile withBlock:(void(^)(Class))withBlock; -(Class)loadPythonClass:(NSString *)pyClass fromFile:(NSString *)fromFile; +-(CSOauth2Authenticator *) createOAuth2Authenticator:(NSString *)serviceName clientID:(NSString *)client_id flowType:(NSString *)flow_type config:(NSDictionary *)config_dict; +-(NSArray *)accountNamesForService:(NSString *)serviceName; + + +@property (readonly) double currentFPS; +@property (readonly) int audioSampleRate; + @end diff --git a/CocoaSplit/PluginHeaders/CSStreamServiceProtocol.h b/CocoaSplit/PluginHeaders/CSStreamServiceProtocol.h index 10d2b4cb..bd1d02a6 100644 --- a/CocoaSplit/PluginHeaders/CSStreamServiceProtocol.h +++ b/CocoaSplit/PluginHeaders/CSStreamServiceProtocol.h @@ -9,14 +9,19 @@ #import #import -@protocol CSStreamServiceProtocol +@protocol CSStreamServiceProtocol @property bool isReady; -(NSViewController *)getConfigurationView; -(NSString *)getServiceDestination; +-(NSString *)getServiceFormat; +(NSString *)label; +(NSString *)serviceDescription; ++(NSImage *)serviceImage; + +-(void)prepareForStreamStart; + @end diff --git a/CocoaSplit/PreviewView.h b/CocoaSplit/PreviewView.h index af69c372..096e280d 100644 --- a/CocoaSplit/PreviewView.h +++ b/CocoaSplit/PreviewView.h @@ -23,10 +23,10 @@ +#define LAYOUT_RESOLUTIONS @[@"1280x720@60", @"1280x720@30", @"1920x1080@60", @"1920x1080@30", @"Custom"] - -@interface PreviewView : NSView +@interface PreviewView : NSView { @@ -53,7 +53,10 @@ NSPopover *_layoutpopOver; CSPreviewOverlayView *_overlayView; bool _inDrag; + NSMutableDictionary *_highlightedSourceMap; + NSPoint _configWindowCascadePoint; + @@ -84,6 +87,8 @@ @property (strong) NSMenu *sourceSettingsMenu; +@property (assign) bool midiActive; + @@ -101,10 +106,20 @@ @property (strong) NSViewController *activePopupController; @property (strong) NSMutableDictionary *activeConfigWindows; @property (strong) NSMutableDictionary *activeConfigControllers; +@property (assign) bool isEditWindow; -(void)needsUpdate; -(NSRect)windowRectforWorldRect:(NSRect)worldRect; -(NSArray *)resizeRectsForSource:(InputSource *)inputSource withExtra:(float)withExtra; +-(void)addInputSourceWithInput:(InputSource *)source; +-(void) highlightSource:(InputSource *)source; +-(void)stopHighlightingSource:(InputSource *)source; +-(void)stopHighlightingAllSources; +-(void)openInputConfigWindow:(NSString *)uuid; + + + + diff --git a/CocoaSplit/PreviewView.m b/CocoaSplit/PreviewView.m index ff4e87c1..8ed129a8 100644 --- a/CocoaSplit/PreviewView.m +++ b/CocoaSplit/PreviewView.m @@ -37,6 +37,17 @@ @synthesize selectedSource = _selectedSource; +-(void)setMidiActive:(bool)midiActive +{ + _glLayer.midiActive = midiActive; +} + + +-(bool)midiActive +{ + return _glLayer.midiActive; +} + -(void)cursorUpdate:(NSEvent *)event @@ -87,9 +98,10 @@ if (_glLayer) { _glLayer.renderer = layoutRenderer; + _glLayer.doRender = self.isEditWindow; } - _layoutRenderer = layoutRenderer; + _layoutRenderer = layoutRenderer; } @@ -98,6 +110,8 @@ { return _layoutRenderer; } + + -(SourceLayout *)sourceLayout { return _sourceLayout; @@ -106,7 +120,7 @@ -(void) setSourceLayout:(SourceLayout *)sourceLayout { - if (_sourceLayout) + if (_sourceLayout && !self.isEditWindow) { [NSApp unregisterMIDIResponder:_sourceLayout]; @@ -115,7 +129,10 @@ [self.undoManager removeAllActions]; sourceLayout.undoManager = self.undoManager; - [NSApp registerMIDIResponder:sourceLayout]; + if (!self.isEditWindow) + { + [NSApp registerMIDIResponder:sourceLayout]; + } if (self.layoutRenderer) { @@ -189,16 +206,38 @@ if (self.selectedSource) { - InputSource *mapCopy = self.selectedSource.copy; - mapCopy.uuid = self.selectedSource.uuid; - - mapCopy.is_live = !self.selectedSource.is_live; - - [self.controller openMidiLearnerForResponders:@[self.selectedSource, mapCopy]]; + [self.controller openMidiLearnerForResponders:@[self.selectedSource]]; } } +- (IBAction)addInputToLibrary:(id)sender +{ + + InputSource *toAdd = nil; + + if (sender) + { + if ([sender isKindOfClass:[NSMenuItem class]]) + { + NSMenuItem *item = (NSMenuItem *)sender; + toAdd = (InputSource *)item.representedObject; + } else if ([sender isKindOfClass:[InputSource class]]) { + toAdd = (InputSource *)sender; + } + } + + if (!toAdd) + { + toAdd = self.selectedSource; + } + + if (toAdd) + { + [self.controller addInputToLibrary:toAdd]; + } +} + -(void) buildSettingsMenu { @@ -210,15 +249,14 @@ tmp.target = self; tmp = [self.sourceSettingsMenu insertItemWithTitle:@"Move Down" action:@selector(moveInputDown:) keyEquivalent:@"" atIndex:idx++]; tmp.target = self; - tmp = [self.sourceSettingsMenu insertItemWithTitle:@"Auto Fit" action:@selector(autoFitInput:) keyEquivalent:@"" atIndex:idx++]; - tmp.target = self; tmp = [self.sourceSettingsMenu insertItemWithTitle:@"Settings" action:@selector(showInputSettings:) keyEquivalent:@"" atIndex:idx++]; tmp.target = self; - tmp = [self.sourceSettingsMenu insertItemWithTitle:@"Delete" action:@selector(deleteInput:) keyEquivalent:@"" atIndex:idx++]; - tmp.target = self; tmp = [self.sourceSettingsMenu insertItemWithTitle:@"Clone" action:@selector(cloneInputSource:) keyEquivalent:@"" atIndex:idx++]; tmp.target = self; + tmp = [self.sourceSettingsMenu insertItemWithTitle:@"Add to Library" action:@selector(addInputToLibrary:) keyEquivalent:@"" atIndex:idx++]; + tmp.target = self; + tmp = [self.sourceSettingsMenu insertItemWithTitle:@"Midi Mapping" action:@selector(midiMapSource:) keyEquivalent:@"" atIndex:idx++]; tmp.target = self; @@ -256,6 +294,46 @@ } +-(void)menu:(NSMenu *)menu willHighlightItem:(nullable NSMenuItem *)item +{ + if (item.representedObject) + { + InputSource *hInput = (InputSource *)item.representedObject; + if (_overlayView) + { + _overlayView.parentSource = hInput; + } + } +} + + + +-(void)resolutionMenuAction:(NSMenuItem *)sender +{ + NSInteger tag = sender.tag; + + if (!self.sourceLayout) + { + return; + } + + if (tag < 2) + { + [self.sourceLayout updateCanvasWidth:1280 height:720]; + } else if (tag < 4) { + [self.sourceLayout updateCanvasWidth:1920 height:1080]; + } + + if ((tag % 2) == 0) + { + self.sourceLayout.frameRate = 60.0f; + } else { + self.sourceLayout.frameRate = 30.0f; + } +} + + + -(NSMenu *) buildSourceMenu { @@ -263,16 +341,51 @@ NSArray *sourceList = [self.sourceLayout sourceListOrdered]; NSMenu *sourceListMenu = [[NSMenu allocWithZone:[NSMenu menuZone]] init]; + sourceListMenu.delegate = self; + NSString *resTitle = [NSString stringWithFormat:@"%dx%d@%.2f", self.sourceLayout.canvas_width, self.sourceLayout.canvas_height, self.sourceLayout.frameRate]; + NSMenuItem *resItem = [[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:resTitle action:nil keyEquivalent:@""]; + + NSMenu *resSubmenu = [[NSMenu allocWithZone:[NSMenu menuZone]] init]; + + [LAYOUT_RESOLUTIONS enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { + NSString *resOpt = obj; + SEL menuAction = @selector(resolutionMenuAction:); + + if ([resOpt isEqualToString:@"Custom"]) + { + menuAction = @selector(showLayoutSettings:); + } + + + NSMenuItem *item = [resSubmenu addItemWithTitle:resOpt action:menuAction keyEquivalent:@""]; + item.target = self; + item.enabled = YES; + item.tag = idx; + + }]; + [resItem setSubmenu:resSubmenu]; + + + + [sourceListMenu insertItem:resItem atIndex:[sourceListMenu.itemArray count]]; + + if (self.viewOnly) + { + return sourceListMenu; + } + NSMenuItem *midiItem = [[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:@"Midi Mapping" action:@selector(doLayoutMidi:) keyEquivalent:@""]; [midiItem setTarget:self]; [midiItem setEnabled:YES]; [sourceListMenu insertItem:midiItem atIndex:[sourceListMenu.itemArray count]]; + [sourceListMenu insertItem:[NSMenuItem separatorItem] atIndex:[sourceListMenu.itemArray count]]; + for (InputSource *src in sourceList) { NSString *srcName = src.name; @@ -295,6 +408,13 @@ [delItem setRepresentedObject:src]; [delItem setTarget:self]; [submenu addItem:delItem]; + + NSMenuItem *libraryItem = [[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:@"Add to Library" action:@selector(addInputToLibrary:) keyEquivalent:@""]; + [libraryItem setEnabled:YES]; + [libraryItem setRepresentedObject:src]; + [libraryItem setTarget:self]; + [submenu addItem:libraryItem]; + NSMenuItem *cloneItem = [[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:@"Clone" action:@selector(cloneInputSource:) keyEquivalent:@""]; [cloneItem setEnabled:YES]; [cloneItem setRepresentedObject:src]; @@ -302,7 +422,8 @@ [submenu addItem:cloneItem]; [srcItem setSubmenu:submenu]; - + [srcItem setRepresentedObject:src]; + [sourceListMenu insertItem:srcItem atIndex:[sourceListMenu.itemArray count]]; } @@ -386,9 +507,17 @@ -(void)rightMouseDown:(NSEvent *)theEvent { + NSPoint tmp; + + tmp = [self convertPoint:theEvent.locationInWindow fromView:nil]; + + if (self.viewOnly) { - return; + NSMenu *srcListMenu = [self buildSourceMenu]; + + [srcListMenu popUpMenuPositioningItem:srcListMenu.itemArray.firstObject atLocation:tmp inView:self]; + } bool doDeep = YES; @@ -397,9 +526,6 @@ { doDeep = NO; } - NSPoint tmp; - - tmp = [self convertPoint:theEvent.locationInWindow fromView:nil]; NSPoint worldPoint = [self realPointforWindowPoint:tmp]; self.selectedSource = [self.sourceLayout findSource:worldPoint deepParent:doDeep]; @@ -704,6 +830,14 @@ NSPoint c_rt_snap; NSPoint c_center_snap; + NSPoint *c_snaps; + int c_snap_size = 0; + + if (!self.selectedSource) + { + return; + } + if (superInput) { NSRect super_rect = superInput.globalLayoutPosition; @@ -711,11 +845,38 @@ c_lb_snap = super_rect.origin; c_rt_snap = NSMakePoint(NSMaxX(super_rect), NSMaxY(super_rect)); c_center_snap = NSMakePoint(NSMidX(super_rect), NSMidY(super_rect)); + c_snaps = malloc(sizeof(NSPoint) * 3); + c_snaps[0] = c_lb_snap; + c_snaps[1] = c_rt_snap; + c_snaps[2] = c_center_snap; + c_snap_size = 3; } else { //define snap points. basically edges and the center of the canvas c_lb_snap = NSMakePoint(0, 0); c_rt_snap = NSMakePoint(self.sourceLayout.canvas_width, self.sourceLayout.canvas_height); c_center_snap = NSMakePoint(self.sourceLayout.canvas_width/2, self.sourceLayout.canvas_height/2); + c_snap_size = 3; + + //NSArray *srcs = self.sourceLayout.topLevelSourceList; + + NSArray *srcs = @[]; + + c_snap_size += srcs.count*3; + + c_snaps = malloc(sizeof(NSPoint) * c_snap_size); + c_snaps[0] = c_lb_snap; + c_snaps[1] = c_rt_snap; + c_snaps[2] = c_center_snap; + + int snap_idx = 3; + for (InputSource *src in srcs) + { + NSRect srect = src.globalLayoutPosition; + c_snaps[snap_idx++] = srect.origin; + c_snaps[snap_idx++] = NSMakePoint(NSMaxX(srect), NSMaxY(srect)); + c_snaps[snap_idx++] = NSMakePoint(NSMidX(srect), NSMidY(srect)); + + } } @@ -723,10 +884,6 @@ //selected source snap points. edges, and center - if (!self.selectedSource) - { - return; - } NSRect src_rect = self.selectedSource.globalLayoutPosition; @@ -738,7 +895,6 @@ NSPoint dist; NSPoint s_snaps[3] = {s_lb_snap, s_rt_snap, s_center_snap}; - NSPoint c_snaps[3] = {c_lb_snap, c_rt_snap, c_center_snap}; bool did_snap_x = NO; bool did_snap_y = NO; @@ -778,7 +934,7 @@ for(int i=0; i < sizeof(s_snaps)/sizeof(NSPoint); i++) { NSPoint s_snap = s_snaps[i]; - for(int j=0; j < sizeof(c_snaps)/sizeof(NSPoint); j++) + for(int j=0; j < c_snap_size; j++) { NSPoint c_snap = c_snaps[j]; @@ -814,6 +970,11 @@ _glLayer.snap_x = _snap_x; _glLayer.snap_y = _snap_y; } + + if (c_snaps) + { + free(c_snaps); + } } @@ -854,15 +1015,75 @@ } _overlayView.parentSource = self.mousedSource; + + if (self.mousedSource) + { + [self stopHighlightingSource:self.mousedSource]; + } else { + [self.controller resetInputTableHighlights]; + } } - - - } +-(void) highlightSource:(InputSource *)source +{ + if (!_highlightedSourceMap) + { + _highlightedSourceMap = [[NSMutableDictionary alloc] init]; + } + + + NSString *srcUUID = source.uuid; + + InputSource *realSrc = [self.sourceLayout inputForUUID:srcUUID]; + if (!_highlightedSourceMap[srcUUID] && realSrc) + { + CSPreviewOverlayView *oview = [[CSPreviewOverlayView alloc] init]; + oview.renderControls = NO; + oview.previewView = self; + oview.parentSource = realSrc; + _highlightedSourceMap[srcUUID] = oview; + } +} + + +-(void)stopHighlightingSource:(InputSource *)source +{ + if (!_highlightedSourceMap) + { + _highlightedSourceMap = [[NSMutableDictionary alloc] init]; + } + + NSString *srcUUID = source.uuid; + + if (_highlightedSourceMap[srcUUID]) + { + CSPreviewOverlayView *oview = _highlightedSourceMap[srcUUID]; + [oview removeFromSuperview]; + [_highlightedSourceMap removeObjectForKey:srcUUID]; + } +} + +-(void)stopHighlightingAllSources +{ + if (!_highlightedSourceMap) + { + _highlightedSourceMap = [[NSMutableDictionary alloc] init]; + } + for (NSString *key in _highlightedSourceMap) + { + CSPreviewOverlayView *oview = _highlightedSourceMap[key]; + if (oview) + { + [oview removeFromSuperview]; + } + } + [_highlightedSourceMap removeAllObjects]; +} + - (IBAction)moveInputUp:(id)sender { @@ -1071,6 +1292,26 @@ } +-(void)undoAddInput:(NSString *)uuid +{ + InputSource *toDelete = [self.sourceLayout inputForUUID:uuid]; + if (toDelete) + { + [self deleteInput:toDelete]; + } +} + + +-(void)addInputSourceWithInput:(InputSource *)source +{ + if (self.sourceLayout) + { + + [self.sourceLayout addSource:source]; + [[self.undoManager prepareWithInvocationTarget:self] undoAddInput:source.uuid]; + } +} + - (IBAction)addInputSource:(id)sender { @@ -1080,7 +1321,7 @@ InputSource *newSource = [[InputSource alloc] init]; [self.sourceLayout addSource:newSource]; - [[self.undoManager prepareWithInvocationTarget:self] deleteInput:newSource]; + [[self.undoManager prepareWithInvocationTarget:self] undoAddInput:newSource.uuid]; [self spawnInputSettings:newSource atRect:NSZeroRect]; } } @@ -1240,6 +1481,24 @@ } +- (void)showLayoutSettings:(id)sender +{ + + + NSPoint tmp = [self convertPoint:[self.window mouseLocationOutsideOfEventStream] fromView:nil]; + + NSRect spawnRect = NSMakeRect(tmp.x, tmp.y, 1.0f, 1.0f); + + if (!NSPointInRect(NSMakePoint(tmp.x, 0), self.bounds)) + { + spawnRect = NSMakeRect(self.bounds.size.width-5, tmp.y, 1.0f, 1.0f); + } else if (!NSPointInRect(NSMakePoint(0, tmp.y), self.bounds)) { + spawnRect = NSMakeRect(tmp.x, 5.0f, 1.0f, 1.0f); + } + + [self.controller openBuiltinLayoutPopover:self spawnRect:spawnRect forLayout:self.sourceLayout]; +} + - (IBAction)showInputSettings:(id)sender { @@ -1258,18 +1517,7 @@ } - NSPoint tmp = [self convertPoint:[self.window mouseLocationOutsideOfEventStream] fromView:nil]; - - NSRect spawnRect = NSMakeRect(tmp.x, tmp.y, 1.0f, 1.0f); - - if (!NSPointInRect(NSMakePoint(tmp.x, 0), self.bounds)) - { - spawnRect = NSMakeRect(self.bounds.size.width-5, tmp.y, 1.0f, 1.0f); - } else if (!NSPointInRect(NSMakePoint(0, tmp.y), self.bounds)) { - spawnRect = NSMakeRect(tmp.x, 5.0f, 1.0f, 1.0f); - } - - [self spawnInputSettings:configSource atRect:spawnRect]; + [self openInputConfigWindow:configSource.uuid]; } @@ -1324,6 +1572,8 @@ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sourceWasDeleted:) name:CSNotificationInputDeleted object:nil]; + + _configWindowCascadePoint = NSZeroPoint; _snap_x = _snap_y = -1; @@ -1338,13 +1588,77 @@ [self setWantsLayer:YES]; self.layer.backgroundColor = CGColorCreateGenericRGB(0.184314f, 0.309804f, 0.309804f, 1); + [self registerForDraggedTypes:@[@"cocoasplit.library.item"]]; } +-(NSDragOperation)draggingEntered:(id)sender +{ + NSPasteboard *pboard; + pboard = [sender draggingPasteboard]; + if ([pboard.types containsObject:@"cocoasplit.library.item"] && !self.viewOnly) + { + return NSDragOperationGeneric; + } + return NSDragOperationNone; +} + +-(BOOL)performDragOperation:(id)sender +{ + NSPasteboard *pboard; + + pboard = [sender draggingPasteboard]; + + + if ([pboard canReadItemWithDataConformingToTypes:@[@"cocoasplit.library.item"]]) + { + + NSArray *classes = @[[CSInputLibraryItem class]]; + NSArray *draggedObjects = [pboard readObjectsForClasses:classes options:@{}]; + + for (CSInputLibraryItem *item in draggedObjects) + { + NSData *iData = item.inputData; + + NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:iData]; + + + InputSource *iSrc = [unarchiver decodeObjectForKey:@"root"]; + [unarchiver finishDecoding]; + + NSPoint mouseLoc = [NSEvent mouseLocation]; + + NSRect rect = NSRectFromCGRect((CGRect){mouseLoc, CGSizeZero}); + + mouseLoc = [self.window convertRectFromScreen:rect].origin; + mouseLoc = [self convertPoint:mouseLoc fromView:nil]; + + if (![self mouse:mouseLoc inRect:self.bounds]) + { + return NO; + } + + + NSPoint worldPoint = [self realPointforWindowPoint:mouseLoc]; + + + [iSrc createUUID]; + + [self.sourceLayout addSource:iSrc]; + //[iSrc positionOrigin:worldPoint.x y:worldPoint.y]; + iSrc.x_pos = worldPoint.x; + iSrc.y_pos = worldPoint.y; + } + return YES; + } + return NO; +} + + -(CALayer *)makeBackingLayer { _glLayer = [CSPreviewGLLayer layer]; - + _glLayer.doRender = self.isEditWindow; return _glLayer; } @@ -1361,6 +1675,8 @@ { NSString *uuid = src.uuid; + [self stopHighlightingSource:src]; + NSWindow *cWindow = [self.activeConfigWindows objectForKey:uuid]; InputPopupControllerViewController *cController = [self.activeConfigControllers objectForKey:uuid]; @@ -1390,7 +1706,78 @@ return self.undoManager; } +-(void)openInputConfigWindows:(NSArray *)uuids +{ + _configWindowCascadePoint = NSZeroPoint; + for (NSString *uuid in uuids) + { + [self openInputConfigWindow:uuid]; + } +} + +-(void)openInputConfigWindow:(NSString *)uuid +{ + + + InputSource *configSrc = [self.sourceLayout inputForUUID:uuid]; + + if (!configSrc) + { + return; + } + + InputPopupControllerViewController *newViewController = [[InputPopupControllerViewController alloc] init]; + + newViewController.inputSource = configSrc; + + NSWindow *configWindow = [[NSWindow alloc] init]; + + NSRect newFrame = [configWindow frameRectForContentRect:NSMakeRect(0.0f, 0.0f, newViewController.view.frame.size.width, newViewController.view.frame.size.height)]; + + + + [configWindow setFrame:newFrame display:NO]; + if (NSEqualPoints(_configWindowCascadePoint, NSZeroPoint)) + { + [configWindow center]; + + _configWindowCascadePoint = NSMakePoint(NSMinX(configWindow.frame), NSMaxY(configWindow.frame)); + } else { + _configWindowCascadePoint = [configWindow cascadeTopLeftFromPoint:_configWindowCascadePoint]; + } + + [configWindow setReleasedWhenClosed:NO]; + + + [configWindow.contentView addSubview:newViewController.view]; + configWindow.title = [NSString stringWithFormat:@"CocoaSplit Input (%@)", newViewController.inputSource.name]; + configWindow.delegate = self; + + configWindow.styleMask = NSTitledWindowMask|NSClosableWindowMask|NSMiniaturizableWindowMask; + + NSWindow *cWindow = [self.activeConfigWindows objectForKey:uuid]; + InputPopupControllerViewController *cController = [self.activeConfigControllers objectForKey:uuid]; + + if (cController) + { + cController.inputSource = nil; + [self.activeConfigControllers removeObjectForKey:uuid]; + } + + if (cWindow) + { + [self.activeConfigWindows removeObjectForKey:uuid]; + } + + + [self.activeConfigWindows setObject:configWindow forKey:uuid]; + [self.activeConfigControllers setObject:newViewController forKey:uuid]; + + [configWindow makeKeyAndOrderFront:nil]; + + +} -(NSWindow *)detachableWindowForPopover:(NSPopover *)popover { @@ -1448,8 +1835,6 @@ - (void)popoverDidClose:(NSNotification *)notification { - - NSString *closeReason = [[notification userInfo] valueForKey:NSPopoverCloseReasonKey]; NSPopover *popover = notification.object; if (closeReason && closeReason == NSPopoverCloseReasonStandard) diff --git a/CocoaSplit/SourceCache.m b/CocoaSplit/SourceCache.m index ffe03e3c..1c81ece0 100644 --- a/CocoaSplit/SourceCache.m +++ b/CocoaSplit/SourceCache.m @@ -28,6 +28,7 @@ -(instancetype) init { + if (self = [super init]) { self.cacheMap = [NSMapTable strongToWeakObjectsMapTable]; diff --git a/CocoaSplit/SourceLayout.h b/CocoaSplit/SourceLayout.h index 6fad77a5..bb572e69 100644 --- a/CocoaSplit/SourceLayout.h +++ b/CocoaSplit/SourceLayout.h @@ -21,18 +21,25 @@ NSSortDescriptor *_sourceDepthSorter; NSSortDescriptor *_sourceUUIDSorter; - CVPixelBufferPoolRef _cvpool; - CVPixelBufferRef _currentPB; NSSize _rootSize; GLuint _fboTexture; GLuint _rFbo; dispatch_queue_t _animationQueue; NSMutableDictionary *_uuidMap; + bool _noSceneTransactions; + NSMutableArray *_topLevelSourceArray; + bool _skipRefCounting; + + + } + +@property (assign) bool doSaveSourceList; + @property (assign) bool inTransition; @property (strong) NSMutableArray *animationList; @property (strong) NSIndexSet *animationIndexes; @@ -43,6 +50,8 @@ @property (strong) NSMutableArray *sourceList; +@property (readonly) NSArray *topLevelSourceList; + @property (strong) NSData *savedSourceListData; @property (assign) bool isActive; @@ -65,6 +74,25 @@ @property (weak) InputSource *layoutTimingSource; @property (strong) NSUndoManager *undoManager; +@property (strong) NSMutableArray *containedLayouts; + + +@property (assign) bool in_live; +@property (assign) bool in_staging; + +@property (nonatomic, copy) void (^addLayoutBlock)(SourceLayout *layout); +@property (nonatomic, copy) void (^removeLayoutBlock)(SourceLayout *layout); + +@property (strong) NSString *transitionName; +@property (strong) NSString *transitionDirection; +@property (strong) CIFilter *transitionFilter; +@property (assign) float transitionDuration; +@property (assign) bool transitionFullScene; + + + + + -(void)deleteSource:(InputSource *)delSource; -(void)addSource:(InputSource *)newSource; @@ -78,7 +106,7 @@ -(InputSource *)inputForUUID:(NSString *)uuid; -(void)frameTick; --(NSObject *)mergeSourceListData:(NSData *)mergeData withLayer:(CALayer *)withLayer; +-(NSObject *)mergeSourceListData:(NSData *)mergeData onlyAdd:(bool)onlyAdd; -(IBAction)runAnimations:(id)sender; -(void)addAnimation:(NSDictionary *)animation; -(InputSource *)sourceUnder:(InputSource *)source; @@ -86,6 +114,22 @@ -(bool)containsInput:(InputSource *)cSource; -(void)modifyUUID:(NSString *)uuid withBlock:(void (^)(InputSource *input))withBlock; +-(void)mergeSourceLayout:(SourceLayout *)toMerge withLayer:(CALayer *)withLayer; +-(void)removeSourceLayout:(SourceLayout *)toRemove withLayer:(CALayer *)withLayer; +-(bool)containsLayout:(SourceLayout *)layout; +-(void)applyAddBlock; +-(void)replaceWithSourceLayout:(SourceLayout *)layout; +-(void)clearSourceList; +-(void)setupMIDI; +-(void)updateCanvasWidth:(int)width height:(int)height; +-(CSAnimationItem *)animationForUUID:(NSString *)uuid; +-(void)clearAnimations; +-(void)runSingleAnimation:(CSAnimationItem *)animation withCompletionBlock:(void (^)(void))completionBlock; +-(void)replaceWithSourceLayout:(SourceLayout *)layout withCompletionBlock:(void (^)(void))completionBlock; + + + + diff --git a/CocoaSplit/SourceLayout.m b/CocoaSplit/SourceLayout.m index 67a6bac9..ce58b9e1 100644 --- a/CocoaSplit/SourceLayout.m +++ b/CocoaSplit/SourceLayout.m @@ -16,6 +16,7 @@ @synthesize isActive = _isActive; @synthesize animationIndexes = _animationIndexes; +@synthesize frameRate = _frameRate; -(instancetype) init { @@ -31,16 +32,21 @@ _rFbo = 0; _uuidMap = [NSMutableDictionary dictionary]; + _animationQueue = dispatch_queue_create("CSAnimationQueue", NULL); - - + _containedLayouts = [[NSMutableArray alloc] init]; + _noSceneTransactions = NO; + _topLevelSourceArray = [[NSMutableArray alloc] init]; self.rootLayer = [self newRootLayer]; self.animationList = [NSMutableArray array]; - //self.rootLayer.geometryFlipped = YES; _rootSize = NSMakeSize(_canvas_width, _canvas_height); self.sourceList = [NSMutableArray array]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(inputAttachEvent:) name:CSNotificationInputAttached object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(inputAttachEvent:) name:CSNotificationInputDetached object:nil]; + + } @@ -48,6 +54,18 @@ } +-(void)inputAttachEvent:(NSNotification *)notification +{ + InputSource *src = notification.object; + if (src.sourceLayout == self) + { + [self willChangeValueForKey:@"topLevelSourceList"]; + [self generateTopLevelSourceList]; + [self didChangeValueForKey:@"topLevelSourceList"]; + } +} + + -(CALayer *)newRootLayer { CALayer *newRoot = [CALayer layer]; @@ -68,13 +86,7 @@ -(NSString *)MIDIIdentifier { - NSString *liveStr = @"Staging"; - if (self.isActive) - { - liveStr = @"Live"; - } - - return [NSString stringWithFormat:@"%@Layout:%@", liveStr, self.name]; + return [NSString stringWithFormat:@"Layout:%@", self.name]; } @@ -142,6 +154,7 @@ { _animationIndexes = animationIndexes; NSUInteger firstIndex = animationIndexes.firstIndex; + if (firstIndex < self.animationList.count) { self.selectedAnimation = [self.animationList objectAtIndex:firstIndex]; @@ -173,7 +186,14 @@ } + -(void)runSingleAnimation:(CSAnimationItem *)animation +{ + [self runSingleAnimation:animation withCompletionBlock:nil]; +} + + +-(void)runSingleAnimation:(CSAnimationItem *)animation withCompletionBlock:(void (^)(void))completionBlock { if (!animation) { @@ -181,20 +201,47 @@ } NSMutableDictionary *inputMap = [NSMutableDictionary dictionary]; + for (NSDictionary *item in animation.inputs) { + if (item[@"value"]) { - inputMap[item[@"label"]] = item[@"value"]; + if ([item[@"type"] isEqualToString:@"input"]) + { + InputSource *nSrc = item[@"value"]; + + if ([nSrc isEqualTo:[NSNull null]]) + { + nSrc = nil; + } + + NSString *suuid = item[@"savedUUID"]; + if (!nSrc && suuid && ![suuid isEqualTo:[NSNull null]]) + { + nSrc = [self inputForUUID:suuid]; + } + + if (nSrc) + { + inputMap[item[@"label"]] = nSrc; + } + } else { + inputMap[item[@"label"]] = item[@"value"]; + } } else { inputMap[item[@"label"]] = [NSNull null]; } } - NSDictionary *animMap = @{@"moduleName": animation.module_name, @"inputs": inputMap, @"rootLayer": self.rootLayer}; + NSMutableDictionary *animMap = @{@"moduleName": animation.module_name, @"inputs": inputMap, @"rootLayer": self.rootLayer}.mutableCopy; + if (completionBlock) + { + [animMap setObject:completionBlock forKey:@"completionBlock"]; + } //[self doAnimation:animMap]; - + NSThread *runThread = [[NSThread alloc] initWithTarget:self selector:@selector(doAnimation:) object:animMap]; [runThread start]; @@ -242,17 +289,28 @@ NSString *modName = threadDict[@"moduleName"]; NSDictionary *inpMap = threadDict[@"inputs"]; CALayer *rootLayer = threadDict[@"rootLayer"]; + void (^completionBlock)(void) = [threadDict objectForKey:@"completionBlock"]; @try { - [runner runAnimation:modName forInput:inpMap withSuperlayer:rootLayer]; + if (completionBlock) + { + [CATransaction begin]; + [CATransaction setCompletionBlock:completionBlock]; + } + [runner runAnimation:modName forInput:inpMap withSuperlayer:rootLayer]; } @catch (NSException *exception) { NSLog(@"Animation module %@ failed with exception: %@: %@", modName, [exception name], [exception reason]); } @finally { + if (completionBlock) + { + [CATransaction commit]; + } + [CATransaction flush]; } } @@ -268,6 +326,9 @@ { CSAnimationItem *newItem = [[CSAnimationItem alloc] initWithDictionary:animation moduleName:animation[@"module"]]; [[self mutableArrayValueForKey:@"animationList"] addObject:newItem]; + + + } @@ -284,6 +345,7 @@ newLayout.canvas_width = self.canvas_width; newLayout.frameRate = self.frameRate; newLayout.isActive = NO; + newLayout.containedLayouts = self.containedLayouts.mutableCopy; return newLayout; } @@ -295,12 +357,13 @@ { [aCoder encodeObject:self.name forKey:@"name"]; - if (self.isActive) + if (self.doSaveSourceList) { [self saveSourceList]; } + [aCoder encodeObject:self.savedSourceListData forKey:@"savedSourceData"]; [aCoder encodeInt:self.canvas_width forKey:@"canvas_width"]; [aCoder encodeInt:self.canvas_height forKey:@"canvas_height"]; @@ -309,11 +372,18 @@ { [aCoder encodeObject:self.animationSaveData forKey:@"animationSaveData"]; } + + if (self.containedLayouts) + { + [aCoder encodeObject:self.containedLayouts forKey:@"containedLayouts"]; + } + } + -(id) initWithCoder:(NSCoder *)aDecoder { if (self = [self init]) @@ -339,6 +409,12 @@ { self.animationSaveData = [aDecoder decodeObjectForKey:@"animationSaveData"]; } + + if ([aDecoder containsValueForKey:@"containedLayouts"]) + { + self.containedLayouts = [[aDecoder decodeObjectForKey:@"containedLayouts"] mutableCopy]; + //set live/staging status for each layout + } } @@ -346,6 +422,59 @@ } + +-(float)frameRate +{ + return _frameRate; +} + + +-(void)setFrameRate:(float)frameRate +{ + float oldframerate = _frameRate; + + _frameRate = frameRate; + + if (_frameRate != oldframerate) + { + [[NSNotificationCenter defaultCenter] postNotificationName:CSNotificationLayoutFramerateChanged object:self userInfo:nil]; + } +} + + +-(void)applyAddBlock +{ + if (self.addLayoutBlock) + { + for (SourceLayout *layout in self.containedLayouts) + { + self.addLayoutBlock(layout); + } + } +} + + +-(void)generateTopLevelSourceList +{ + [_topLevelSourceArray removeAllObjects]; + for (InputSource *src in self.sourceListOrdered) + { + if (!src.parentInput) + { + [_topLevelSourceArray addObject:src]; + } + } +} + + +-(NSArray *)topLevelSourceList +{ + + return _topLevelSourceArray; + +} + + -(NSArray *)sourceListOrdered { NSArray *mylist; @@ -387,6 +516,66 @@ return retInput; } + + +-(void) resetAllRefCounts +{ + for (InputSource *src in self.sourceList) + { + src.refCount = 1; + } + + for (CSAnimationItem *item in self.animationList) + { + item.refCount = 1; + } +} + + + +-(NSInteger) incrementAnimationRef:(CSAnimationItem *)anim +{ + anim.refCount++; + return anim.refCount; + +} + +-(NSInteger)decrementAnimationRef:(CSAnimationItem *)anim +{ + + anim.refCount--; + + if (anim.refCount < 0) + { + anim.refCount = 0; + } + + return anim.refCount; +} + + +-(NSInteger)incrementInputRef:(InputSource *)input +{ + + input.refCount++; + + return input.refCount; +} + +-(NSInteger)decrementInputRef:(InputSource *)input +{ + + input.refCount--; + + if (input.refCount < 0) + { + input.refCount = 0; + } + + return input.refCount; +} + + -(InputSource *)findSource:(NSPoint)forPoint deepParent:(bool)deepParent { @@ -435,8 +624,491 @@ } --(NSObject *)mergeSourceListData:(NSData *)mergeData withLayer:(CALayer *)withLayer +-(void)replaceWithSourceLayout:(SourceLayout *)layout { + [self replaceWithSourceLayout:layout withCompletionBlock:nil]; +} + + +-(void)replaceWithSourceLayout:(SourceLayout *)layout withCompletionBlock:(void (^)(void))completionBlock +{ + + NSInteger __block pendingCount = 0; + void (^internalCompletionBlock)(void) = ^{ + @synchronized (self) + { + pendingCount--; + if (pendingCount <= 0 && completionBlock) + { + completionBlock(); + } + } + }; + + //_noSceneTransactions = YES; + CATransition *rTrans = nil; + + [CATransaction begin]; + if (completionBlock) + { + @synchronized (self) + { + pendingCount++; + } + [CATransaction setCompletionBlock:^{ + internalCompletionBlock(); + }]; + } + + if (self.transitionFullScene) + { + if (self.transitionName || self.transitionFilter) + { + rTrans = [CATransition animation]; + rTrans.type = self.transitionName; + rTrans.duration = self.transitionDuration; + rTrans.removedOnCompletion = YES; + rTrans.subtype = self.transitionDirection; + if (self.transitionFilter) + { + rTrans.filter = self.transitionFilter; + } + } + + } + + + for (SourceLayout *cLayout in self.containedLayouts.copy) + { + if (self.removeLayoutBlock) + { + self.removeLayoutBlock(cLayout); + } + + [self.containedLayouts removeObject:cLayout]; + } + //Only run animations that aren't already in the layout + + NSMutableArray *runAnimations = [[NSMutableArray alloc] init]; + + if (!self.in_staging) + { + for (CSAnimationItem *anim in layout.animationList) + { + + if (![self animationForUUID:anim.uuid] && anim.onLive) + { + [runAnimations addObject:anim.uuid]; + + } + } + + if (completionBlock) + { + @synchronized (self) + { + pendingCount += [runAnimations count]; + } + } + } + + [self.animationList removeAllObjects]; + //If an input exists in both lists, only remove it if the new one is different/changed + + NSMutableArray *rList = [[NSMutableArray alloc] init]; + for (InputSource *src in self.sourceList) + { + InputSource *nSrc = [layout inputForUUID:src.uuid]; + if (nSrc) + { + if ([nSrc isDifferentInput:src]) + { + [rList addObject:src]; + } + } else { + [rList addObject:src]; + + } + } + + + [self removeSourceInputs:rList withLayer:nil]; + + + if (self.addLayoutBlock) + { + self.addLayoutBlock(layout); + } + + [self.containedLayouts addObject:layout]; + + for (SourceLayout *cLayout in layout.containedLayouts.copy) + { + if (self.addLayoutBlock) + { + self.addLayoutBlock(cLayout); + } + + [self.containedLayouts addObject:cLayout]; + } + + [self mergeSourceListData:layout.savedSourceListData onlyAdd:YES]; + + + if (self.transitionFullScene) + { + if (rTrans) + { + //[self.rootLayer addAnimation:rTrans forKey:kCATransition]; + } + + } + + [CATransaction commit]; + + for (NSString *anim in runAnimations) + { + CSAnimationItem *eItem = [self animationForUUID:anim]; + if (eItem) + { + [self runSingleAnimation:eItem withCompletionBlock:^{ + if (completionBlock) + { + internalCompletionBlock(); + } + }]; + } + } + + + _noSceneTransactions = NO; + [self updateCanvasWidth:layout.canvas_width height:layout.canvas_height]; + self.frameRate = layout.frameRate; + [self resetAllRefCounts]; + +} + + + +-(bool)containsLayout:(SourceLayout *)layout +{ + return [self.containedLayouts containsObject:layout]; +} + + +-(void)clearAnimations +{ + [self.animationList removeAllObjects]; +} + + +-(void)mergeSourceLayout:(SourceLayout *)toMerge withLayer:(CALayer *)withLayer +{ + + if ([self.containedLayouts containsObject:toMerge]) + { + return; + } + + NSArray *mergedAnim = nil; + + NSObject *dictOrObj = [self mergeSourceListData:toMerge.savedSourceListData]; + + if ([dictOrObj isKindOfClass:[NSDictionary class]]) + { + NSDictionary *dict = (NSDictionary *)dictOrObj; + mergedAnim = [dict valueForKey:@"animationList"]; + } + + [self adjustAllInputs]; + [self.containedLayouts addObject:toMerge]; + if (self.addLayoutBlock) + { + self.addLayoutBlock(toMerge); + } + + if (mergedAnim && !self.in_staging) + { + for (CSAnimationItem *anim in mergedAnim) + { + if (anim.onLive) + { + CSAnimationItem *eItem = [self animationForUUID:anim.uuid]; + if (eItem && eItem.refCount == 1) + { + [self runSingleAnimation:eItem]; + } + } + } + } +} + + +-(void)removeSourceLayout:(SourceLayout *)toRemove withLayer:(CALayer *)withLayer +{ + + if (![self.containedLayouts containsObject:toRemove]) + { + return; + } + + [self removeSourceListData:toRemove.savedSourceListData withLayer:withLayer]; + + [self.containedLayouts removeObject:toRemove]; + if (self.removeLayoutBlock) + { + self.removeLayoutBlock(toRemove); + } +} + + + + +-(NSArray *)mergeSourceInputsScene:(NSArray *)inputs onlyAdd:(bool)onlyAdd +{ + + NSMutableArray *undoSources = [NSMutableArray array]; + + + CATransition *rTrans = nil; + if (!_noSceneTransactions && (self.transitionName || self.transitionFilter)) + { + rTrans = [CATransition animation]; + rTrans.type = self.transitionName; + rTrans.duration = self.transitionDuration; + rTrans.removedOnCompletion = YES; + rTrans.subtype = self.transitionDirection; + if (self.transitionFilter) + { + rTrans.filter = self.transitionFilter; + } + } + + if (!_noSceneTransactions) + { + [CATransaction begin]; + } + + for(InputSource *src in inputs) + { + src.sourceLayout = self; + src.is_live = self.isActive; + InputSource *eSrc = [self inputForUUID:src.uuid]; + bool isDifferent = YES; + + if (eSrc) + { + + isDifferent = [eSrc isDifferentInput:src]; + if (!isDifferent) + { + [self incrementInputRef:eSrc]; + + continue; + } + } + if (eSrc && !onlyAdd) + { + if (!src.layer.superlayer) + { + [eSrc.layer.superlayer addSublayer:src.layer]; + } + eSrc.layer.hidden = YES; + [undoSources addObject:eSrc]; + eSrc.refCount = 0; + } else { + if (!src.layer.superlayer) + { + [self.rootLayer addSublayer:src.layer]; + } + } + [NSApp registerMIDIResponder:src]; + [self incrementInputRef:src]; + + + [self willChangeValueForKey:@"topLevelSourceList"]; + [[self mutableArrayValueForKey:@"sourceList" ] addObject:src]; + [self generateTopLevelSourceList]; + [self didChangeValueForKey:@"topLevelSourceList"]; + [_uuidMap setObject:src forKey:src.uuid]; + + } + + __weak SourceLayout *weakSelf = self; + + if (undoSources.count > 0) + { + [CATransaction setCompletionBlock:^{ + for (InputSource *dInput in undoSources) + { + [weakSelf deleteSource:dInput]; + } + }]; + } + + if (rTrans) + { + [self.rootLayer addAnimation:rTrans forKey:nil]; + } + if (!_noSceneTransactions) + { + [CATransaction commit]; + } + + return undoSources; +} + + +-(NSArray *)mergeSourceInputsIndividual:(NSArray *)inputs onlyAdd:(bool)onlyAdd +{ + + NSMutableArray *undoSources = [NSMutableArray array]; + CATransition *rTrans = nil; + NSInteger origRefCnt = 0; + + if (self.transitionName || self.transitionFilter) + { + rTrans = [CATransition animation]; + rTrans.type = self.transitionName; + rTrans.duration = self.transitionDuration; + rTrans.removedOnCompletion = YES; + rTrans.subtype = self.transitionDirection; + if (self.transitionFilter) + { + rTrans.filter = self.transitionFilter; + } + } + + for(InputSource *src in inputs) + { + src.sourceLayout = self; + src.is_live = self.isActive; + InputSource *eSrc = [self inputForUUID:src.uuid]; + + bool isDifferent = NO; + + if (eSrc) + { + isDifferent = [eSrc isDifferentInput:src]; + } + + if (eSrc && !onlyAdd) + { + if (!isDifferent) + { + [self incrementInputRef:eSrc]; + continue; + } + + src.layer.hidden = YES; + if (!src.layer.superlayer) + { + [eSrc.layer.superlayer addSublayer:src.layer]; + } + [CATransaction flush]; + + [CATransaction begin]; + __weak SourceLayout *weakSelf = self; + + [CATransaction setCompletionBlock:^{ + [weakSelf deleteSource:eSrc]; + }]; + + + + if (rTrans) + { + [eSrc.layer addAnimation:rTrans forKey:nil]; + [src.layer addAnimation:rTrans forKey:nil]; + } + + origRefCnt = eSrc.refCount; + eSrc.refCount = 0; + eSrc.layer.hidden = YES; + src.layer.hidden = NO; + [CATransaction commit]; + [undoSources addObject:eSrc]; + } else { + + if (eSrc && !isDifferent) + { + [self incrementInputRef:eSrc]; + + continue; + } + + [CATransaction begin]; + + if (!src.layer.superlayer) + { + [self.rootLayer addSublayer:src.layer]; + } + + + src.layer.hidden = YES; + + [CATransaction commit]; + + //worst hack ever. add a dummy animation of same length so the outer transaction doesn't complete until we do + + [CATransaction begin]; + CABasicAnimation *hackAnim = [CABasicAnimation animationWithKeyPath:@"dummyKeyPath"]; + hackAnim.duration = rTrans.duration; + hackAnim.fromValue = @0; + hackAnim.toValue = @100; + hackAnim.removedOnCompletion = YES; + + [src.layer addAnimation:hackAnim forKey:@"dummyKey"]; + [CATransaction commit]; + + + dispatch_async(dispatch_get_main_queue(), ^{ + [CATransaction begin]; + + if (rTrans) + { + + [src.layer addAnimation:rTrans forKey:nil]; + } + + + src.layer.hidden = NO; + + + + + [CATransaction commit]; + }); + + + } + + [NSApp registerMIDIResponder:src]; + + + src.refCount = origRefCnt+1; + + [self willChangeValueForKey:@"topLevelSourceList"]; + [[self mutableArrayValueForKey:@"sourceList" ] addObject:src]; + [self generateTopLevelSourceList]; + [self didChangeValueForKey:@"topLevelSourceList"]; + [_uuidMap setObject:src forKey:src.uuid]; + + } + + return undoSources; +} + + +-(NSObject *)mergeSourceListData:(NSData *)mergeData +{ + return [self mergeSourceListData:mergeData onlyAdd:NO]; +} + + +-(NSObject *)mergeSourceListData:(NSData *)mergeData onlyAdd:(bool)onlyAdd +{ + if (!self.sourceList) @@ -449,13 +1121,6 @@ return nil; } - if (!withLayer) - { - withLayer = self.rootLayer; - } - - NSMutableArray *undoSources = [NSMutableArray array]; - NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:mergeData]; [unarchiver setDelegate:self]; @@ -464,10 +1129,13 @@ [unarchiver finishDecoding]; NSArray *mergeList; + NSArray *mergeAnimationList = nil; + if ([mergeObj isKindOfClass:[NSDictionary class]]) { mergeList = [((NSDictionary *)mergeObj) objectForKey:@"sourcelist"]; + mergeAnimationList = [((NSDictionary *)mergeObj) objectForKey:@"animationList"]; } else { mergeList = (NSArray *)mergeObj; } @@ -476,21 +1144,150 @@ { [self.undoManager beginUndoGrouping]; } - for(InputSource *src in mergeList) + + + if (mergeAnimationList) + { + for (CSAnimationItem *aItem in mergeAnimationList) + { + CSAnimationItem *eItem = [self animationForUUID:aItem.uuid]; + if (eItem) + { + [self incrementAnimationRef:eItem]; + } else { + [[self mutableArrayValueForKey:@"animationList"] addObject:aItem]; + [self incrementAnimationRef:aItem]; + } + } + } + + + NSArray *undoSources; + + if (self.transitionFullScene) + { + undoSources = [self mergeSourceInputsScene:mergeList onlyAdd:onlyAdd]; + } else { + undoSources = [self mergeSourceInputsIndividual:mergeList onlyAdd:onlyAdd]; + } + + + if (undoSources.count > 0) + { + NSData *undoData = [NSKeyedArchiver archivedDataWithRootObject:undoSources]; + [[self.undoManager prepareWithInvocationTarget:self] mergeSourceListData:undoData]; + } else { + [[self.undoManager prepareWithInvocationTarget:self] removeSourceListData:mergeData withLayer:nil]; + } + + if (self.undoManager) + { + [self.undoManager endUndoGrouping]; + } + + return mergeObj; +} + + +-(NSArray *)removeSourceInputsScene:(NSArray *)inputs +{ + NSMutableArray *undoSources = [NSMutableArray array]; + + CATransition *rTrans = nil; + + if (!_noSceneTransactions && (self.transitionName || self.transitionFilter)) + { + rTrans = [CATransition animation]; + rTrans.type = self.transitionName; + rTrans.duration = self.transitionDuration; + rTrans.subtype = self.transitionDirection; + + if (self.transitionFilter) + { + rTrans.filter = self.transitionFilter; + } + rTrans.removedOnCompletion = YES; + } + + if (!_noSceneTransactions) + { + [CATransaction begin]; + + } + __weak SourceLayout *weakSelf = self; + + [CATransaction setCompletionBlock:^{ + for (InputSource *dInput in undoSources) + { + [weakSelf deleteSource:dInput]; + } + }]; + if (rTrans) + { + [self.rootLayer addAnimation:rTrans forKey:nil]; + } + + for(InputSource *src in inputs) + { + src.sourceLayout = self; + InputSource *eSrc = [self inputForUUID:src.uuid]; + + + + if (eSrc) + { + NSInteger refCnt = [self decrementInputRef:eSrc]; + + + if (refCnt != 0) + { + continue; + } + + eSrc.layer.hidden = YES; + [undoSources addObject:eSrc]; + } + } + + if (!_noSceneTransactions) + { + [CATransaction commit]; + } + + return undoSources; +} + + +-(NSArray *)removeSourceInputsIndividual:(NSArray *)inputs +{ + + NSMutableArray *undoSources = [NSMutableArray array]; + + CATransition *rTrans = nil; + if (self.transitionName || self.transitionFilter) + { + rTrans = [CATransition animation]; + rTrans.type = self.transitionName; + rTrans.duration = self.transitionDuration; + rTrans.subtype = self.transitionDirection; + if (self.transitionFilter) + { + rTrans.filter = self.transitionFilter; + } + rTrans.removedOnCompletion = YES; + } + + for(InputSource *src in inputs) { src.sourceLayout = self; - src.is_live = self.isActive; InputSource *eSrc = [self inputForUUID:src.uuid]; if (eSrc) { - CATransition *rTrans = [CATransition animation]; - rTrans.type = @"flip"; - rTrans.duration = 2.5; - rTrans.removedOnCompletion = YES; - [CATransaction begin]; - [eSrc.layer.superlayer addSublayer:src.layer]; - src.layer.hidden = YES; - [CATransaction commit]; + NSInteger refCnt = [self decrementInputRef:eSrc]; + if (refCnt != 0) + { + continue; + } [CATransaction begin]; __weak SourceLayout *weakSelf = self; @@ -500,48 +1297,55 @@ }]; - - //[eSrc.layer addAnimation:rTrans forKey:nil]; - // [src.layer addAnimation:rTrans forKey:nil]; + if (rTrans) + { + [eSrc.layer addAnimation:rTrans forKey:nil]; + } eSrc.layer.hidden = YES; - src.layer.hidden = NO; - [CATransaction commit]; [undoSources addObject:eSrc]; } - if (undoSources.count > 0) - { - NSData *undoData = [NSKeyedArchiver archivedDataWithRootObject:undoSources]; - [[self.undoManager prepareWithInvocationTarget:self] mergeSourceListData:undoData withLayer:nil]; - } else { - [[self.undoManager prepareWithInvocationTarget:self] removeSourceListData:mergeData withLayer:nil]; - } - - - [NSApp registerMIDIResponder:src]; - - if (!src.layer.superlayer) - { - [CATransaction begin]; - [withLayer addSublayer:src.layer]; - [CATransaction commit]; - - } - - - [[self mutableArrayValueForKey:@"sourceList" ] addObject:src]; - [_uuidMap setObject:src forKey:src.uuid]; - } + + return undoSources; +} + + + +-(void)removeSourceInputs:(NSArray *)inputs withLayer:(CALayer *)withLayer +{ + + + if (self.undoManager) + { + [self.undoManager beginUndoGrouping]; + } + + NSArray *undoSources; + + if (self.transitionFullScene) + { + undoSources = [self removeSourceInputsScene:inputs]; + } else { + undoSources = [self removeSourceInputsIndividual:inputs]; + } + + + if (undoSources.count > 0) + { + NSData *undoData = [NSKeyedArchiver archivedDataWithRootObject:undoSources]; + [[self.undoManager prepareWithInvocationTarget:self] mergeSourceListData:undoData]; + } + if (self.undoManager) { [self.undoManager endUndoGrouping]; } + - return mergeObj; } @@ -564,7 +1368,6 @@ withLayer = self.rootLayer; } - NSMutableArray *undoSources = [NSMutableArray array]; NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:mergeData]; @@ -574,59 +1377,35 @@ [unarchiver finishDecoding]; NSArray *mergeList; + NSArray *mergeAnim; if ([mergeObj isKindOfClass:[NSDictionary class]]) { mergeList = [((NSDictionary *)mergeObj) objectForKey:@"sourcelist"]; + mergeAnim = [((NSDictionary *)mergeObj) objectForKey:@"animationList"]; + } else { mergeList = (NSArray *)mergeObj; } - if (self.undoManager) - { - [self.undoManager beginUndoGrouping]; - } - for(InputSource *src in mergeList) - { - src.sourceLayout = self; - InputSource *eSrc = [self inputForUUID:src.uuid]; - if (eSrc) - { - CATransition *rTrans = [CATransition animation]; - rTrans.type = @"flip"; - rTrans.duration = 2.5; - rTrans.removedOnCompletion = YES; - [CATransaction begin]; - __weak SourceLayout *weakSelf = self; - - [CATransaction setCompletionBlock:^{ - [weakSelf deleteSource:eSrc]; - }]; - - - - [eSrc.layer addAnimation:rTrans forKey:nil]; - // [src.layer addAnimation:rTrans forKey:nil]; - - eSrc.layer.hidden = YES; - - [CATransaction commit]; - [undoSources addObject:eSrc]; - } - - - if (undoSources.count > 0) - { - NSData *undoData = [NSKeyedArchiver archivedDataWithRootObject:undoSources]; - [[self.undoManager prepareWithInvocationTarget:self] mergeSourceListData:undoData withLayer:nil]; - } - } - if (self.undoManager) - { - [self.undoManager endUndoGrouping]; - } + [self removeSourceInputs:mergeList withLayer:withLayer]; + if (mergeAnim) + { + for (CSAnimationItem *aItem in mergeAnim) + { + CSAnimationItem *eItem = [self animationForUUID:aItem.uuid]; + if (eItem) + { + NSInteger eCnt = [self decrementAnimationRef:eItem]; + if (eCnt <= 0) + { + [[self mutableArrayValueForKey:@"animationList"] removeObject:eItem]; + } + } + } + } return mergeObj; } @@ -634,10 +1413,17 @@ -(void)restoreSourceList:(NSData *)withData { + CALayer *oldSuperLayer = nil; + CALayer *newRoot = nil; + if (self.rootLayer) + { + oldSuperLayer = self.rootLayer.superlayer; + } if (self.savedSourceListData) { - CALayer *newRoot = [self newRootLayer]; + + newRoot = [self newRootLayer]; [CATransaction begin]; newRoot.sublayers = [NSArray array]; @@ -653,7 +1439,7 @@ { withData = self.savedSourceListData; } - NSObject *restData = [self mergeSourceListData:withData withLayer:newRoot]; + NSObject *restData = [self mergeSourceListData:withData]; if (restData && [restData isKindOfClass:[NSDictionary class]]) @@ -676,8 +1462,10 @@ self.layoutTimingSource = ((InputSource *)timerSrc); } - [self.rootLayer.superlayer replaceSublayer:self.rootLayer with:newRoot]; - self.rootLayer = newRoot; + if (oldSuperLayer) + { + [oldSuperLayer replaceSublayer:self.rootLayer with:newRoot]; + } for(InputSource *src in oldSourceList) { @@ -709,12 +1497,17 @@ } + -(void)deleteSource:(InputSource *)delSource { [delSource willDelete]; + [self willChangeValueForKey:@"topLevelSourceList"]; [[self mutableArrayValueForKey:@"sourceList" ] removeObject:delSource]; + [self generateTopLevelSourceList]; + [self didChangeValueForKey:@"topLevelSourceList"]; + InputSource *uSrc; uSrc = _uuidMap[delSource.uuid]; @@ -739,24 +1532,77 @@ +-(void)setupMIDI +{ + [NSApp registerMIDIResponder:self]; + for (InputSource *src in self.sourceList) + { + [NSApp registerMIDIResponder:src]; + + } +} + + +-(void) adjustAllInputs +{ + + NSArray *copiedInputs = [self sourceListOrdered]; + + for (InputSource *src in copiedInputs) + { + src.needsAdjustPosition = YES; + src.needsAdjustment = YES; + } +} + + + + -(void) addSource:(InputSource *)newSource { newSource.sourceLayout = self; newSource.is_live = self.isActive; - + [self willChangeValueForKey:@"topLevelSourceList"]; [[self mutableArrayValueForKey:@"sourceList" ] addObject:newSource]; - + [self generateTopLevelSourceList]; + [self didChangeValueForKey:@"topLevelSourceList"]; + [self.rootLayer addSublayer:newSource.layer]; + + + newSource.needsAdjustPosition = NO; + newSource.needsAdjustment = YES; + [_uuidMap setObject:newSource forKey:newSource.uuid]; + [NSApp registerMIDIResponder:newSource]; [[NSNotificationCenter defaultCenter] postNotificationName:CSNotificationInputAdded object:newSource userInfo:nil]; } +-(void)clearSourceList +{ + self.rootLayer.sublayers = [NSArray array]; + @synchronized(self) + { + [self willChangeValueForKey:@"topLevelSourceList"]; + [self.sourceList removeAllObjects]; + [self generateTopLevelSourceList]; + [self didChangeValueForKey:@"topLevelSourceList"]; + + + } + [self.animationList removeAllObjects]; + [_uuidMap removeAllObjects]; + self.selectedAnimation = nil; +} + + +/* -(void) setIsActive:(bool)isActive { @@ -811,6 +1657,8 @@ return _isActive; } +*/ + @@ -860,11 +1708,25 @@ } +-(void)updateCanvasWidth:(int)width height:(int)height +{ + int old_height = self.canvas_height; + int old_width = self.canvas_width; + + self.canvas_height = height; + self.canvas_width = width; + + if ((old_height != height) || (old_width != width)) + { + [[NSNotificationCenter defaultCenter] postNotificationName:CSNotificationLayoutCanvasChanged object:self userInfo:nil]; + } +} + -(void)frameTick { - + bool needsResize = NO; NSSize curSize = NSMakeSize(self.canvas_width, self.canvas_height); if (!NSEqualSizes(curSize, _rootSize)) @@ -873,6 +1735,7 @@ self.rootLayer.bounds = CGRectMake(0, 0, self.canvas_width, self.canvas_height); _rootSize = curSize; + needsResize = YES; } NSArray *listCopy = [self sourceListOrdered]; @@ -880,6 +1743,11 @@ for (InputSource *isource in listCopy) { + if (needsResize) + { + isource.needsAdjustPosition = YES; + isource.needsAdjustment = YES; + } if (isource.active) { @@ -893,6 +1761,11 @@ -(void)didBecomeVisible { + if (self.in_staging) + { + return; + } + for (CSAnimationItem *anim in self.animationList) { if (anim.onLive) @@ -914,6 +1787,18 @@ } +-(CSAnimationItem *)animationForUUID:(NSString *)uuid +{ + for (CSAnimationItem *item in self.animationList) + { + if ([item.uuid isEqualToString:uuid]) + { + return item; + } + } + return nil; +} + -(InputSource *)inputForUUID:(NSString *)uuid { @@ -922,5 +1807,10 @@ } +-(void)dealloc +{ + [[NSNotificationCenter defaultCenter] removeObserver:self]; + +} @end diff --git a/CocoaSplit/TPCircularBuffer+AudioBufferList.c b/CocoaSplit/TPCircularBuffer+AudioBufferList.c new file mode 100644 index 00000000..7b913dc7 --- /dev/null +++ b/CocoaSplit/TPCircularBuffer+AudioBufferList.c @@ -0,0 +1,319 @@ +// +// TPCircularBuffer+AudioBufferList.c +// Circular/Ring buffer implementation +// +// https://github.com/michaeltyson/TPCircularBuffer +// +// Created by Michael Tyson on 20/03/2012. +// +// Copyright (C) 2012-2013 A Tasty Pixel +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source distribution. +// + +#include "TPCircularBuffer+AudioBufferList.h" +#import + +static double __secondsToHostTicks = 0.0; + +static inline long align16byte(long val) { + if ( val & (16-1) ) { + return val + (16 - (val & (16-1))); + } + return val; +} + +static inline long min(long a, long b) { + return a > b ? b : a; +} + +AudioBufferList *TPCircularBufferPrepareEmptyAudioBufferList(TPCircularBuffer *buffer, int numberOfBuffers, int bytesPerBuffer, const AudioTimeStamp *inTimestamp) { + int32_t availableBytes; + TPCircularBufferABLBlockHeader *block = (TPCircularBufferABLBlockHeader*)TPCircularBufferHead(buffer, &availableBytes); + if ( !block || availableBytes < sizeof(TPCircularBufferABLBlockHeader)+((numberOfBuffers-1)*sizeof(AudioBuffer))+(numberOfBuffers*bytesPerBuffer) ) return NULL; + + #ifdef DEBUG + assert(!((unsigned long)block & 0xF) /* Beware unaligned accesses */); + #endif + + if ( inTimestamp ) { + memcpy(&block->timestamp, inTimestamp, sizeof(AudioTimeStamp)); + } else { + memset(&block->timestamp, 0, sizeof(AudioTimeStamp)); + } + + memset(&block->bufferList, 0, sizeof(AudioBufferList)+((numberOfBuffers-1)*sizeof(AudioBuffer))); + block->bufferList.mNumberBuffers = numberOfBuffers; + + char *dataPtr = (char*)&block->bufferList + sizeof(AudioBufferList)+((numberOfBuffers-1)*sizeof(AudioBuffer)); + for ( int i=0; i availableBytes ) { + return NULL; + } + + block->bufferList.mBuffers[i].mData = dataPtr; + block->bufferList.mBuffers[i].mDataByteSize = bytesPerBuffer; + block->bufferList.mBuffers[i].mNumberChannels = 1; + + dataPtr += bytesPerBuffer; + } + + // Make sure whole buffer (including timestamp and length value) is 16-byte aligned in length + block->totalLength = (UInt32)align16byte(dataPtr - (char*)block); + if ( block->totalLength > availableBytes ) { + return NULL; + } + + return &block->bufferList; +} + +AudioBufferList *TPCircularBufferPrepareEmptyAudioBufferListWithAudioFormat(TPCircularBuffer *buffer, const AudioStreamBasicDescription *audioFormat, UInt32 frameCount, const AudioTimeStamp *timestamp) { + return TPCircularBufferPrepareEmptyAudioBufferList(buffer, + (audioFormat->mFormatFlags & kAudioFormatFlagIsNonInterleaved) ? audioFormat->mChannelsPerFrame : 1, + audioFormat->mBytesPerFrame * frameCount, + timestamp); +} + +void TPCircularBufferProduceAudioBufferList(TPCircularBuffer *buffer, const AudioTimeStamp *inTimestamp) { + int32_t availableBytes; + TPCircularBufferABLBlockHeader *block = (TPCircularBufferABLBlockHeader*)TPCircularBufferHead(buffer, &availableBytes); + + assert(block); + + #ifdef DEBUG + assert(!((unsigned long)block & 0xF) /* Beware unaligned accesses */); + #endif + + assert(block->bufferList.mBuffers[0].mDataByteSize > 0); + + if ( inTimestamp ) { + memcpy(&block->timestamp, inTimestamp, sizeof(AudioTimeStamp)); + } + + UInt32 calculatedLength = (UInt32)(((char*)block->bufferList.mBuffers[block->bufferList.mNumberBuffers-1].mData + block->bufferList.mBuffers[block->bufferList.mNumberBuffers-1].mDataByteSize) - (char*)block); + + // Make sure whole buffer (including timestamp and length value) is 16-byte aligned in length + calculatedLength = (UInt32)align16byte(calculatedLength); + + assert(calculatedLength <= block->totalLength && calculatedLength <= availableBytes); + + block->totalLength = calculatedLength; + + TPCircularBufferProduce(buffer, block->totalLength); +} + +bool TPCircularBufferCopyAudioBufferList(TPCircularBuffer *buffer, const AudioBufferList *inBufferList, const AudioTimeStamp *inTimestamp, UInt32 frames, const AudioStreamBasicDescription *audioDescription) { + if ( frames == 0 ) return true; + + int byteCount = inBufferList->mBuffers[0].mDataByteSize; + if ( frames != kTPCircularBufferCopyAll ) { + byteCount = frames * audioDescription->mBytesPerFrame; + assert(byteCount <= inBufferList->mBuffers[0].mDataByteSize); + } + + if ( byteCount == 0 ) return true; + + AudioBufferList *bufferList = TPCircularBufferPrepareEmptyAudioBufferList(buffer, inBufferList->mNumberBuffers, byteCount, inTimestamp); + if ( !bufferList ) return false; + + for ( int i=0; imNumberBuffers; i++ ) { + memcpy(bufferList->mBuffers[i].mData, inBufferList->mBuffers[i].mData, byteCount); + } + + TPCircularBufferProduceAudioBufferList(buffer, NULL); + + return true; +} + +AudioBufferList *TPCircularBufferNextBufferListAfter(TPCircularBuffer *buffer, const AudioBufferList *bufferList, AudioTimeStamp *outTimestamp) { + int32_t availableBytes; + void *tail = TPCircularBufferTail(buffer, &availableBytes); + void *end = (char*)tail + availableBytes; + assert((void*)bufferList > (void*)tail && (void*)bufferList < end); + + TPCircularBufferABLBlockHeader *originalBlock = (TPCircularBufferABLBlockHeader*)((char*)bufferList - offsetof(TPCircularBufferABLBlockHeader, bufferList)); + + #ifdef DEBUG + assert(!((unsigned long)originalBlock & 0xF) /* Beware unaligned accesses */); + #endif + + TPCircularBufferABLBlockHeader *nextBlock = (TPCircularBufferABLBlockHeader*)((char*)originalBlock + originalBlock->totalLength); + if ( (void*)nextBlock >= end ) return NULL; + + #ifdef DEBUG + assert(!((unsigned long)nextBlock & 0xF) /* Beware unaligned accesses */); + #endif + + if ( outTimestamp ) { + memcpy(outTimestamp, &nextBlock->timestamp, sizeof(AudioTimeStamp)); + } + + return &nextBlock->bufferList; +} + +void TPCircularBufferConsumeNextBufferListPartial(TPCircularBuffer *buffer, int framesToConsume, const AudioStreamBasicDescription *audioFormat) { + assert(framesToConsume >= 0); + + int32_t dontcare; + TPCircularBufferABLBlockHeader *block = (TPCircularBufferABLBlockHeader*)TPCircularBufferTail(buffer, &dontcare); + if ( !block ) return; + + #ifdef DEBUG + assert(!((unsigned long)block & 0xF)); // Beware unaligned accesses + #endif + + int bytesToConsume = (int)min(framesToConsume * audioFormat->mBytesPerFrame, block->bufferList.mBuffers[0].mDataByteSize); + + if ( bytesToConsume == block->bufferList.mBuffers[0].mDataByteSize ) { + TPCircularBufferConsumeNextBufferList(buffer); + return; + } + + for ( int i=0; ibufferList.mNumberBuffers; i++ ) { + assert(bytesToConsume <= block->bufferList.mBuffers[i].mDataByteSize); + + block->bufferList.mBuffers[i].mData = (char*)block->bufferList.mBuffers[i].mData + bytesToConsume; + block->bufferList.mBuffers[i].mDataByteSize -= bytesToConsume; + } + + if ( block->timestamp.mFlags & kAudioTimeStampSampleTimeValid ) { + block->timestamp.mSampleTime += framesToConsume; + } + if ( block->timestamp.mFlags & kAudioTimeStampHostTimeValid ) { + if ( !__secondsToHostTicks ) { + mach_timebase_info_data_t tinfo; + mach_timebase_info(&tinfo); + __secondsToHostTicks = 1.0 / (((double)tinfo.numer / tinfo.denom) * 1.0e-9); + } + + block->timestamp.mHostTime += ((double)framesToConsume / audioFormat->mSampleRate) * __secondsToHostTicks; + } + + // Reposition block forward, just before the audio data, ensuring 16-byte alignment + TPCircularBufferABLBlockHeader *newBlock = (TPCircularBufferABLBlockHeader*)(((unsigned long)block + bytesToConsume) & ~0xFul); + memmove(newBlock, block, sizeof(TPCircularBufferABLBlockHeader) + (block->bufferList.mNumberBuffers-1)*sizeof(AudioBuffer)); + intptr_t bytesFreed = (intptr_t)newBlock - (intptr_t)block; + newBlock->totalLength -= bytesFreed; + TPCircularBufferConsume(buffer, (int32_t)bytesFreed); +} + +void TPCircularBufferDequeueBufferListFrames(TPCircularBuffer *buffer, UInt32 *ioLengthInFrames, const AudioBufferList *outputBufferList, AudioTimeStamp *outTimestamp, const AudioStreamBasicDescription *audioFormat) { + bool hasTimestamp = false; + UInt32 bytesToGo = *ioLengthInFrames * audioFormat->mBytesPerFrame; + UInt32 bytesCopied = 0; + while ( bytesToGo > 0 ) { + AudioBufferList *bufferList = TPCircularBufferNextBufferList(buffer, !hasTimestamp ? outTimestamp : NULL); + if ( !bufferList ) break; + + hasTimestamp = true; + long bytesToCopy = min(bytesToGo, bufferList->mBuffers[0].mDataByteSize); + + if ( outputBufferList ) { + for ( int i=0; imNumberBuffers; i++ ) { + assert(bytesCopied + bytesToCopy <= outputBufferList->mBuffers[i].mDataByteSize); + memcpy((char*)outputBufferList->mBuffers[i].mData + bytesCopied, bufferList->mBuffers[i].mData, bytesToCopy); + } + } + + TPCircularBufferConsumeNextBufferListPartial(buffer, (int)bytesToCopy/audioFormat->mBytesPerFrame, audioFormat); + + bytesToGo -= bytesToCopy; + bytesCopied += bytesToCopy; + } + + *ioLengthInFrames -= bytesToGo / audioFormat->mBytesPerFrame; +} + +UInt32 TPCircularBufferPeekContiguousWrapped(TPCircularBuffer *buffer, AudioTimeStamp *outTimestamp, const AudioStreamBasicDescription *audioFormat, UInt32 contiguousToleranceSampleTime, UInt32 wrapPoint) { + int32_t availableBytes; + TPCircularBufferABLBlockHeader *block = (TPCircularBufferABLBlockHeader*)TPCircularBufferTail(buffer, &availableBytes); + if ( !block ) return 0; + + #ifdef DEBUG + assert(!((unsigned long)block & 0xF) /* Beware unaligned accesses */); + #endif + + if ( outTimestamp ) { + memcpy(outTimestamp, &block->timestamp, sizeof(AudioTimeStamp)); + } + + void *end = (char*)block + availableBytes; + + UInt32 byteCount = 0; + + while ( 1 ) { + byteCount += block->bufferList.mBuffers[0].mDataByteSize; + TPCircularBufferABLBlockHeader *nextBlock = (TPCircularBufferABLBlockHeader*)((char*)block + block->totalLength); + if ( (void*)nextBlock >= end ) { + break; + } + + if ( contiguousToleranceSampleTime != UINT32_MAX ) { + UInt32 frames = block->bufferList.mBuffers[0].mDataByteSize / audioFormat->mBytesPerFrame; + Float64 nextTime = block->timestamp.mSampleTime + frames; + if ( wrapPoint && nextTime > wrapPoint ) nextTime = fmod(nextTime, wrapPoint); + Float64 diff = fabs(nextBlock->timestamp.mSampleTime - nextTime); + if ( diff > contiguousToleranceSampleTime && (!wrapPoint || fabs(diff-wrapPoint) > contiguousToleranceSampleTime) ) { + break; + } + } + + #ifdef DEBUG + assert(!((unsigned long)nextBlock & 0xF) /* Beware unaligned accesses */); + #endif + + block = nextBlock; + } + + return byteCount / audioFormat->mBytesPerFrame; +} + +UInt32 TPCircularBufferPeek(TPCircularBuffer *buffer, AudioTimeStamp *outTimestamp, const AudioStreamBasicDescription *audioFormat) { + return TPCircularBufferPeekContiguousWrapped(buffer, outTimestamp, audioFormat, UINT32_MAX, 0); +} + +UInt32 TPCircularBufferPeekContiguous(TPCircularBuffer *buffer, AudioTimeStamp *outTimestamp, const AudioStreamBasicDescription *audioFormat, UInt32 contiguousToleranceSampleTime) { + return TPCircularBufferPeekContiguousWrapped(buffer, outTimestamp, audioFormat, contiguousToleranceSampleTime, 0); +} + +UInt32 TPCircularBufferGetAvailableSpace(TPCircularBuffer *buffer, const AudioStreamBasicDescription *audioFormat) { + // Look at buffer head; make sure there's space for the block metadata + int32_t availableBytes; + TPCircularBufferABLBlockHeader *block = (TPCircularBufferABLBlockHeader*)TPCircularBufferHead(buffer, &availableBytes); + if ( !block ) return 0; + + #ifdef DEBUG + assert(!((unsigned long)block & 0xF) /* Beware unaligned accesses */); + #endif + + // Now find out how much 16-byte aligned audio we can store in the space available + int numberOfBuffers = audioFormat->mFormatFlags & kAudioFormatFlagIsNonInterleaved ? audioFormat->mChannelsPerFrame : 1; + char * endOfBuffer = (char*)block + availableBytes; + char * dataPtr = (char*)align16byte((long)(&block->bufferList + sizeof(AudioBufferList)+((numberOfBuffers-1)*sizeof(AudioBuffer)))); + if ( dataPtr >= endOfBuffer ) return 0; + int32_t availableAudioBytes = (int)(endOfBuffer - dataPtr); + + int32_t availableAudioBytesPerBuffer = availableAudioBytes / numberOfBuffers; + availableAudioBytesPerBuffer -= (availableAudioBytesPerBuffer % (16-1)); + + return availableAudioBytesPerBuffer > 0 ? availableAudioBytesPerBuffer / audioFormat->mBytesPerFrame : 0; +} diff --git a/CocoaSplit/TPCircularBuffer+AudioBufferList.h b/CocoaSplit/TPCircularBuffer+AudioBufferList.h new file mode 100644 index 00000000..6384c46a --- /dev/null +++ b/CocoaSplit/TPCircularBuffer+AudioBufferList.h @@ -0,0 +1,232 @@ +// +// TPCircularBuffer+AudioBufferList.h +// Circular/Ring buffer implementation +// +// https://github.com/michaeltyson/TPCircularBuffer +// +// Created by Michael Tyson on 20/03/2012. +// +// Copyright (C) 2012-2013 A Tasty Pixel +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source distribution. +// + +#ifndef TPCircularBuffer_AudioBufferList_h +#define TPCircularBuffer_AudioBufferList_h + +#ifdef __cplusplus +extern "C" { +#endif + +#include "TPCircularBuffer.h" +#include + +#define kTPCircularBufferCopyAll UINT32_MAX + +typedef struct { + AudioTimeStamp timestamp; + UInt32 totalLength; + AudioBufferList bufferList; +} TPCircularBufferABLBlockHeader; + + +/*! + * Prepare an empty buffer list, stored on the circular buffer + * + * @param buffer Circular buffer + * @param numberOfBuffers The number of buffers to be contained within the buffer list + * @param bytesPerBuffer The number of bytes to store for each buffer + * @param timestamp The timestamp associated with the buffer, or NULL. Note that you can also pass a timestamp into TPCircularBufferProduceAudioBufferList, to set it there instead. + * @return The empty buffer list, or NULL if circular buffer has insufficient space + */ +AudioBufferList *TPCircularBufferPrepareEmptyAudioBufferList(TPCircularBuffer *buffer, int numberOfBuffers, int bytesPerBuffer, const AudioTimeStamp *timestamp); + +/*! + * Prepare an empty buffer list, stored on the circular buffer, using an audio description to automatically configure buffer + * + * @param buffer Circular buffer + * @param audioFormat The kind of audio that will be stored + * @param frameCount The number of frames that will be stored + * @param timestamp The timestamp associated with the buffer, or NULL. Note that you can also pass a timestamp into TPCircularBufferProduceAudioBufferList, to set it there instead. + * @return The empty buffer list, or NULL if circular buffer has insufficient space + */ +AudioBufferList *TPCircularBufferPrepareEmptyAudioBufferListWithAudioFormat(TPCircularBuffer *buffer, const AudioStreamBasicDescription *audioFormat, UInt32 frameCount, const AudioTimeStamp *timestamp); + +/*! + * Mark next audio buffer list as ready for reading + * + * This marks the audio buffer list prepared using TPCircularBufferPrepareEmptyAudioBufferList + * as ready for reading. You must not call this function without first calling + * TPCircularBufferPrepareEmptyAudioBufferList. + * + * @param buffer Circular buffer + * @param timestamp The timestamp associated with the buffer, or NULL to leave as-is. Note that you can also pass a timestamp into TPCircularBufferPrepareEmptyAudioBufferList, to set it there instead. + */ +void TPCircularBufferProduceAudioBufferList(TPCircularBuffer *buffer, const AudioTimeStamp *inTimestamp); + +/*! + * Copy the audio buffer list onto the buffer + * + * @param buffer Circular buffer + * @param bufferList Buffer list containing audio to copy to buffer + * @param timestamp The timestamp associated with the buffer, or NULL + * @param frames Length of audio in frames. Specify kTPCircularBufferCopyAll to copy the whole buffer (audioFormat can be NULL, in this case) + * @param audioFormat The AudioStreamBasicDescription describing the audio, or NULL if you specify kTPCircularBufferCopyAll to the `frames` argument + * @return YES if buffer list was successfully copied; NO if there was insufficient space + */ +bool TPCircularBufferCopyAudioBufferList(TPCircularBuffer *buffer, const AudioBufferList *bufferList, const AudioTimeStamp *timestamp, UInt32 frames, const AudioStreamBasicDescription *audioFormat); + +/*! + * Get a pointer to the next stored buffer list + * + * @param buffer Circular buffer + * @param outTimestamp On output, if not NULL, the timestamp corresponding to the buffer + * @return Pointer to the next buffer list in the buffer + */ +static __inline__ __attribute__((always_inline)) AudioBufferList *TPCircularBufferNextBufferList(TPCircularBuffer *buffer, AudioTimeStamp *outTimestamp) { + int32_t dontcare; // Length of segment is contained within buffer list, so we can ignore this + TPCircularBufferABLBlockHeader *block = (TPCircularBufferABLBlockHeader*)TPCircularBufferTail(buffer, &dontcare); + if ( !block ) { + if ( outTimestamp ) { + memset(outTimestamp, 0, sizeof(AudioTimeStamp)); + } + return NULL; + } + if ( outTimestamp ) { + memcpy(outTimestamp, &block->timestamp, sizeof(AudioTimeStamp)); + } + return &block->bufferList; +} + +/*! + * Get a pointer to the next stored buffer list after the given one + * + * @param buffer Circular buffer + * @param bufferList Preceding buffer list + * @param outTimestamp On output, if not NULL, the timestamp corresponding to the buffer + * @return Pointer to the next buffer list in the buffer, or NULL + */ +AudioBufferList *TPCircularBufferNextBufferListAfter(TPCircularBuffer *buffer, const AudioBufferList *bufferList, AudioTimeStamp *outTimestamp); + +/*! + * Consume the next buffer list + * + * @param buffer Circular buffer + */ +static __inline__ __attribute__((always_inline)) void TPCircularBufferConsumeNextBufferList(TPCircularBuffer *buffer) { + int32_t dontcare; + TPCircularBufferABLBlockHeader *block = (TPCircularBufferABLBlockHeader*)TPCircularBufferTail(buffer, &dontcare); + if ( !block ) return; + TPCircularBufferConsume(buffer, block->totalLength); +} + +/*! + * Consume a portion of the next buffer list + * + * This will also increment the sample time and host time portions of the timestamp of + * the buffer list, if present. + * + * @param buffer Circular buffer + * @param framesToConsume The number of frames to consume from the buffer list + * @param audioFormat The AudioStreamBasicDescription describing the audio + */ +void TPCircularBufferConsumeNextBufferListPartial(TPCircularBuffer *buffer, int framesToConsume, const AudioStreamBasicDescription *audioFormat); + +/*! + * Consume a certain number of frames from the buffer, possibly from multiple queued buffer lists + * + * Copies the given number of frames from the buffer into outputBufferList, of the + * given audio description, then consumes the audio buffers. If an audio buffer has + * not been entirely consumed, then updates the queued buffer list structure to point + * to the unconsumed data only. + * + * @param buffer Circular buffer + * @param ioLengthInFrames On input, the number of frames in the given audio format to consume; on output, the number of frames provided + * @param outputBufferList The buffer list to copy audio to, or NULL to discard audio. If not NULL, the structure must be initialised properly, and the mData pointers must not be NULL. + * @param outTimestamp On output, if not NULL, the timestamp corresponding to the first audio frame returned + * @param audioFormat The format of the audio stored in the buffer + */ +void TPCircularBufferDequeueBufferListFrames(TPCircularBuffer *buffer, UInt32 *ioLengthInFrames, const AudioBufferList *outputBufferList, AudioTimeStamp *outTimestamp, const AudioStreamBasicDescription *audioFormat); + +/*! + * Determine how many frames of audio are buffered + * + * Given the provided audio format, determines the frame count of all queued buffers + * + * Note: This function should only be used on the consumer thread, not the producer thread. + * + * @param buffer Circular buffer + * @param outTimestamp On output, if not NULL, the timestamp corresponding to the first audio frame + * @param audioFormat The format of the audio stored in the buffer + * @return The number of frames in the given audio format that are in the buffer + */ +UInt32 TPCircularBufferPeek(TPCircularBuffer *buffer, AudioTimeStamp *outTimestamp, const AudioStreamBasicDescription *audioFormat); + +/*! + * Determine how many contiguous frames of audio are buffered + * + * Given the provided audio format, determines the frame count of all queued buffers that are contiguous, + * given their corresponding timestamps (sample time). + * + * Note: This function should only be used on the consumer thread, not the producer thread. + * + * @param buffer Circular buffer + * @param outTimestamp On output, if not NULL, the timestamp corresponding to the first audio frame + * @param audioFormat The format of the audio stored in the buffer + * @param contiguousToleranceSampleTime The number of samples of discrepancy to tolerate + * @return The number of frames in the given audio format that are in the buffer + */ +UInt32 TPCircularBufferPeekContiguous(TPCircularBuffer *buffer, AudioTimeStamp *outTimestamp, const AudioStreamBasicDescription *audioFormat, UInt32 contiguousToleranceSampleTime); + +/*! + * Determine how many contiguous frames of audio are buffered, with wrap around + * + * Like TPCircularBufferPeekContiguous, determines how many contiguous frames are buffered, + * but considers audio that wraps around a region of a given length as also contiguous. This + * is good for audio that loops. + * + * Note: This function should only be used on the consumer thread, not the producer thread. + * + * @param buffer Circular buffer + * @param outTimestamp On output, if not NULL, the timestamp corresponding to the first audio frame + * @param audioFormat The format of the audio stored in the buffer + * @param contiguousToleranceSampleTime The number of samples of discrepancy to tolerate + * @param wrapPoint The point around which the audio may wrap and still be considered contiguous, or 0 to disable + * @return The number of frames in the given audio format that are in the buffer + */ +UInt32 TPCircularBufferPeekContiguousWrapped(TPCircularBuffer *buffer, AudioTimeStamp *outTimestamp, const AudioStreamBasicDescription *audioFormat, UInt32 contiguousToleranceSampleTime, UInt32 wrapPoint); + +/*! + * Determine how many much space there is in the buffer + * + * Given the provided audio format, determines the number of frames of audio that can be buffered. + * + * Note: This function should only be used on the producer thread, not the consumer thread. + * + * @param buffer Circular buffer + * @param audioFormat The format of the audio stored in the buffer + * @return The number of frames in the given audio format that can be stored in the buffer + */ +UInt32 TPCircularBufferGetAvailableSpace(TPCircularBuffer *buffer, const AudioStreamBasicDescription *audioFormat); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/CocoaSplit/TPCircularBuffer.c b/CocoaSplit/TPCircularBuffer.c new file mode 100644 index 00000000..1d85d6c9 --- /dev/null +++ b/CocoaSplit/TPCircularBuffer.c @@ -0,0 +1,149 @@ +// +// TPCircularBuffer.c +// Circular/Ring buffer implementation +// +// https://github.com/michaeltyson/TPCircularBuffer +// +// Created by Michael Tyson on 10/12/2011. +// +// Copyright (C) 2012-2013 A Tasty Pixel +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source distribution. +// + +#include "TPCircularBuffer.h" +#include +#include +#include + +#define reportResult(result,operation) (_reportResult((result),(operation),strrchr(__FILE__, '/')+1,__LINE__)) +static inline bool _reportResult(kern_return_t result, const char *operation, const char* file, int line) { + if ( result != ERR_SUCCESS ) { + printf("%s:%d: %s: %s\n", file, line, operation, mach_error_string(result)); + return false; + } + return true; +} + +bool _TPCircularBufferInit(TPCircularBuffer *buffer, int32_t length, size_t structSize) { + + assert(length > 0); + + if ( structSize != sizeof(TPCircularBuffer) ) { + fprintf(stderr, "TPCircularBuffer: Header version mismatch. Check for old versions of TPCircularBuffer in your project\n"); + abort(); + } + + // Keep trying until we get our buffer, needed to handle race conditions + int retries = 3; + while ( true ) { + + buffer->length = (int32_t)round_page(length); // We need whole page sizes + + // Temporarily allocate twice the length, so we have the contiguous address space to + // support a second instance of the buffer directly after + vm_address_t bufferAddress; + kern_return_t result = vm_allocate(mach_task_self(), + &bufferAddress, + buffer->length * 2, + VM_FLAGS_ANYWHERE); // allocate anywhere it'll fit + if ( result != ERR_SUCCESS ) { + if ( retries-- == 0 ) { + reportResult(result, "Buffer allocation"); + return false; + } + // Try again if we fail + continue; + } + + // Now replace the second half of the allocation with a virtual copy of the first half. Deallocate the second half... + result = vm_deallocate(mach_task_self(), + bufferAddress + buffer->length, + buffer->length); + if ( result != ERR_SUCCESS ) { + if ( retries-- == 0 ) { + reportResult(result, "Buffer deallocation"); + return false; + } + // If this fails somehow, deallocate the whole region and try again + vm_deallocate(mach_task_self(), bufferAddress, buffer->length); + continue; + } + + // Re-map the buffer to the address space immediately after the buffer + vm_address_t virtualAddress = bufferAddress + buffer->length; + vm_prot_t cur_prot, max_prot; + result = vm_remap(mach_task_self(), + &virtualAddress, // mirror target + buffer->length, // size of mirror + 0, // auto alignment + 0, // force remapping to virtualAddress + mach_task_self(), // same task + bufferAddress, // mirror source + 0, // MAP READ-WRITE, NOT COPY + &cur_prot, // unused protection struct + &max_prot, // unused protection struct + VM_INHERIT_DEFAULT); + if ( result != ERR_SUCCESS ) { + if ( retries-- == 0 ) { + reportResult(result, "Remap buffer memory"); + return false; + } + // If this remap failed, we hit a race condition, so deallocate and try again + vm_deallocate(mach_task_self(), bufferAddress, buffer->length); + continue; + } + + if ( virtualAddress != bufferAddress+buffer->length ) { + // If the memory is not contiguous, clean up both allocated buffers and try again + if ( retries-- == 0 ) { + printf("Couldn't map buffer memory to end of buffer\n"); + return false; + } + + vm_deallocate(mach_task_self(), virtualAddress, buffer->length); + vm_deallocate(mach_task_self(), bufferAddress, buffer->length); + continue; + } + + buffer->buffer = (void*)bufferAddress; + buffer->fillCount = 0; + buffer->head = buffer->tail = 0; + buffer->atomic = true; + + return true; + } + return false; +} + +void TPCircularBufferCleanup(TPCircularBuffer *buffer) { + vm_deallocate(mach_task_self(), (vm_address_t)buffer->buffer, buffer->length * 2); + memset(buffer, 0, sizeof(TPCircularBuffer)); +} + +void TPCircularBufferClear(TPCircularBuffer *buffer) { + int32_t fillCount; + if ( TPCircularBufferTail(buffer, &fillCount) ) { + TPCircularBufferConsume(buffer, fillCount); + } +} + +void TPCircularBufferSetAtomic(TPCircularBuffer *buffer, bool atomic) { + buffer->atomic = atomic; +} diff --git a/CocoaSplit/TPCircularBuffer.h b/CocoaSplit/TPCircularBuffer.h new file mode 100644 index 00000000..88eb7220 --- /dev/null +++ b/CocoaSplit/TPCircularBuffer.h @@ -0,0 +1,231 @@ +// +// TPCircularBuffer.h +// Circular/Ring buffer implementation +// +// https://github.com/michaeltyson/TPCircularBuffer +// +// Created by Michael Tyson on 10/12/2011. +// +// +// This implementation makes use of a virtual memory mapping technique that inserts a virtual copy +// of the buffer memory directly after the buffer's end, negating the need for any buffer wrap-around +// logic. Clients can simply use the returned memory address as if it were contiguous space. +// +// The implementation is thread-safe in the case of a single producer and single consumer. +// +// Virtual memory technique originally proposed by Philip Howard (http://vrb.slashusr.org/), and +// adapted to Darwin by Kurt Revis (http://www.snoize.com, +// http://www.snoize.com/Code/PlayBufferedSoundFile.tar.gz) +// +// +// Copyright (C) 2012-2013 A Tasty Pixel +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source distribution. +// + +#ifndef TPCircularBuffer_h +#define TPCircularBuffer_h + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + void *buffer; + int32_t length; + int32_t tail; + int32_t head; + volatile int32_t fillCount; + bool atomic; +} TPCircularBuffer; + +/*! + * Initialise buffer + * + * Note that the length is advisory only: Because of the way the + * memory mirroring technique works, the true buffer length will + * be multiples of the device page size (e.g. 4096 bytes) + * + * If you intend to use the AudioBufferList utilities, you should + * always allocate a bit more space than you need for pure audio + * data, so there's room for the metadata. How much extra is required + * depends on how many AudioBufferList structures are used, which is + * a function of how many audio frames each buffer holds. A good rule + * of thumb is to add 15%, or at least another 2048 bytes or so. + * + * @param buffer Circular buffer + * @param length Length of buffer + */ +#define TPCircularBufferInit(buffer, length) \ + _TPCircularBufferInit(buffer, length, sizeof(*buffer)) +bool _TPCircularBufferInit(TPCircularBuffer *buffer, int32_t length, size_t structSize); + +/*! + * Cleanup buffer + * + * Releases buffer resources. + */ +void TPCircularBufferCleanup(TPCircularBuffer *buffer); + +/*! + * Clear buffer + * + * Resets buffer to original, empty state. + * + * This is safe for use by consumer while producer is accessing + * buffer. + */ +void TPCircularBufferClear(TPCircularBuffer *buffer); + +/*! + * Set the atomicity + * + * If you set the atomiticy to false using this method, the buffer will + * not use atomic operations. This can be used to give the compiler a little + * more optimisation opportunities when the buffer is only used on one thread. + * + * Important note: Only set this to false if you know what you're doing! + * + * The default value is true (the buffer will use atomic operations) + * + * @param buffer Circular buffer + * @param atomic Whether the buffer is atomic (default true) + */ +void TPCircularBufferSetAtomic(TPCircularBuffer *buffer, bool atomic); + +// Reading (consuming) + +/*! + * Access end of buffer + * + * This gives you a pointer to the end of the buffer, ready + * for reading, and the number of available bytes to read. + * + * @param buffer Circular buffer + * @param availableBytes On output, the number of bytes ready for reading + * @return Pointer to the first bytes ready for reading, or NULL if buffer is empty + */ +static __inline__ __attribute__((always_inline)) void* TPCircularBufferTail(TPCircularBuffer *buffer, int32_t* availableBytes) { + *availableBytes = buffer->fillCount; + if ( *availableBytes == 0 ) return NULL; + return (void*)((char*)buffer->buffer + buffer->tail); +} + +/*! + * Consume bytes in buffer + * + * This frees up the just-read bytes, ready for writing again. + * + * @param buffer Circular buffer + * @param amount Number of bytes to consume + */ +static __inline__ __attribute__((always_inline)) void TPCircularBufferConsume(TPCircularBuffer *buffer, int32_t amount) { + buffer->tail = (buffer->tail + amount) % buffer->length; + if ( buffer->atomic ) { + OSAtomicAdd32Barrier(-amount, &buffer->fillCount); + } else { + buffer->fillCount -= amount; + } + assert(buffer->fillCount >= 0); +} + +/*! + * Access front of buffer + * + * This gives you a pointer to the front of the buffer, ready + * for writing, and the number of available bytes to write. + * + * @param buffer Circular buffer + * @param availableBytes On output, the number of bytes ready for writing + * @return Pointer to the first bytes ready for writing, or NULL if buffer is full + */ +static __inline__ __attribute__((always_inline)) void* TPCircularBufferHead(TPCircularBuffer *buffer, int32_t* availableBytes) { + *availableBytes = (buffer->length - buffer->fillCount); + if ( *availableBytes == 0 ) return NULL; + return (void*)((char*)buffer->buffer + buffer->head); +} + +// Writing (producing) + +/*! + * Produce bytes in buffer + * + * This marks the given section of the buffer ready for reading. + * + * @param buffer Circular buffer + * @param amount Number of bytes to produce + */ +static __inline__ __attribute__((always_inline)) void TPCircularBufferProduce(TPCircularBuffer *buffer, int32_t amount) { + buffer->head = (buffer->head + amount) % buffer->length; + if ( buffer->atomic ) { + OSAtomicAdd32Barrier(amount, &buffer->fillCount); + } else { + buffer->fillCount += amount; + } + assert(buffer->fillCount <= buffer->length); +} + +/*! + * Helper routine to copy bytes to buffer + * + * This copies the given bytes to the buffer, and marks them ready for reading. + * + * @param buffer Circular buffer + * @param src Source buffer + * @param len Number of bytes in source buffer + * @return true if bytes copied, false if there was insufficient space + */ +static __inline__ __attribute__((always_inline)) bool TPCircularBufferProduceBytes(TPCircularBuffer *buffer, const void* src, int32_t len) { + int32_t space; + void *ptr = TPCircularBufferHead(buffer, &space); + if ( space < len ) return false; + memcpy(ptr, src, len); + TPCircularBufferProduce(buffer, len); + return true; +} + +/*! + * Deprecated method + */ +static __inline__ __attribute__((always_inline)) __deprecated_msg("use TPCircularBufferSetAtomic(false) and TPCircularBufferConsume instead") +void TPCircularBufferConsumeNoBarrier(TPCircularBuffer *buffer, int32_t amount) { + buffer->tail = (buffer->tail + amount) % buffer->length; + buffer->fillCount -= amount; + assert(buffer->fillCount >= 0); +} + +/*! + * Deprecated method + */ +static __inline__ __attribute__((always_inline)) __deprecated_msg("use TPCircularBufferSetAtomic(false) and TPCircularBufferProduce instead") +void TPCircularBufferProduceNoBarrier(TPCircularBuffer *buffer, int32_t amount) { + buffer->head = (buffer->head + amount) % buffer->length; + buffer->fillCount += amount; + assert(buffer->fillCount <= buffer->length); +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/CocoaSplit/TestView.xib b/CocoaSplit/TestView.xib index c214e989..b2786a5c 100644 --- a/CocoaSplit/TestView.xib +++ b/CocoaSplit/TestView.xib @@ -1,8 +1,8 @@ - + - + diff --git a/CocoaSplit/en.lproj/.DS_Store b/CocoaSplit/en.lproj/.DS_Store new file mode 100644 index 00000000..fd9a74ce Binary files /dev/null and b/CocoaSplit/en.lproj/.DS_Store differ diff --git a/CocoaSplit/en.lproj/MainMenu.xib b/CocoaSplit/en.lproj/MainMenu.xib index 3ab88814..922f1be5 100644 --- a/CocoaSplit/en.lproj/MainMenu.xib +++ b/CocoaSplit/en.lproj/MainMenu.xib @@ -1,8 +1,9 @@ - - + + - + + @@ -19,26 +20,22 @@ - + - - + + - + - - - - @@ -182,6 +179,12 @@ + + + + + + @@ -297,31 +300,31 @@ - + - - + + - + - + - + - + - + - + @@ -332,28 +335,28 @@ - - - - + + + + - - - - + + + + - + - + - + @@ -361,13 +364,13 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - NSIsNotNil - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - NSIsNotNil - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - NSNegateBoolean - - - - - - - - - - - - - - - - - - - - - - - - NSNegateBoolean - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - NSIsNil - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - NSIsNil - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NSIsNotNil + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NSIsNotNil + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NSNegateBoolean + + + + + + - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -2316,25 +1143,19 @@ - - - - + + + + - + - - - - - - @@ -2342,16 +1163,10 @@ - - - - - - + - - + @@ -2362,6 +1177,16 @@ + + + + + + + + + + @@ -2369,25 +1194,6 @@ - - - - - - - - - - - - - - - - - - - name @@ -2400,11 +1206,6 @@ - - - - - @@ -2421,162 +1222,14 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -YnBsaXN0MDDUAQIDBAUGJCVYJHZlcnNpb25YJG9iamVjdHNZJGFyY2hpdmVyVCR0b3ASAAGGoKkHCBES -EwsKGR9VJG51bGzUCQoLDA0ODxBWJGNsYXNzVHR5cGVXc3VidHlwZV8QEl9fQ0FDb2RpbmdDb250ZW50 -c4AIgAOAAoAEWWZyb21SaWdodFRwdXNo0hQJFRhaTlMub2JqZWN0c6IWF4AFgAaAB9IaGxwdWiRjbGFz -c25hbWVYJGNsYXNzZXNXTlNBcnJheaIcHlhOU09iamVjdNIaGyAhXENBVHJhbnNpdGlvbqMiIx5cQ0FU -cmFuc2l0aW9uW0NBQW5pbWF0aW9uXxAPTlNLZXllZEFyY2hpdmVy0SYnVHJvb3SAAQAIABEAGgAjAC0A -MgA3AEEARwBQAFcAXABkAHkAewB9AH8AgQCLAJAAlQCgAKMApQCnAKkArgC5AMIAygDNANYA2wDoAOwA -+QEFARcBGgEfAAAAAAAAAgEAAAAAAAAAKAAAAAAAAAAAAAAAAAAAASE - - - - - - - - - - - - - - - - - - - - - - - - - - + + - + + + diff --git a/ExtraPlugins/CSLayoutSwitcherExtraPlugin/CSLayoutSwitcherExtraPlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/CSLayoutSwitcherExtraPlugin.xcscheme b/ExtraPlugins/CSLayoutSwitcherExtraPlugin/CSLayoutSwitcherExtraPlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/CSLayoutSwitcherExtraPlugin.xcscheme new file mode 100644 index 00000000..dd290807 --- /dev/null +++ b/ExtraPlugins/CSLayoutSwitcherExtraPlugin/CSLayoutSwitcherExtraPlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/CSLayoutSwitcherExtraPlugin.xcscheme @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ExtraPlugins/CSLayoutSwitcherExtraPlugin/CSLayoutSwitcherExtraPlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/xcschememanagement.plist b/ExtraPlugins/CSLayoutSwitcherExtraPlugin/CSLayoutSwitcherExtraPlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 00000000..3f61487c --- /dev/null +++ b/ExtraPlugins/CSLayoutSwitcherExtraPlugin/CSLayoutSwitcherExtraPlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,22 @@ + + + + + SchemeUserState + + CSLayoutSwitcherExtraPlugin.xcscheme + + orderHint + 18 + + + SuppressBuildableAutocreation + + 34A7C18619B9A41900BC6882 + + primary + + + + + diff --git a/ExtraPlugins/CSSyphonInjectExtraPlugin/CSSyphonInjectExtraPlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/CSSyphonInjectExtraPlugin.xcscheme b/ExtraPlugins/CSSyphonInjectExtraPlugin/CSSyphonInjectExtraPlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/CSSyphonInjectExtraPlugin.xcscheme new file mode 100644 index 00000000..3b30bbcb --- /dev/null +++ b/ExtraPlugins/CSSyphonInjectExtraPlugin/CSSyphonInjectExtraPlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/CSSyphonInjectExtraPlugin.xcscheme @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ExtraPlugins/CSSyphonInjectExtraPlugin/CSSyphonInjectExtraPlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/xcschememanagement.plist b/ExtraPlugins/CSSyphonInjectExtraPlugin/CSSyphonInjectExtraPlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 00000000..ccdaedce --- /dev/null +++ b/ExtraPlugins/CSSyphonInjectExtraPlugin/CSSyphonInjectExtraPlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,22 @@ + + + + + SchemeUserState + + CSSyphonInjectExtraPlugin.xcscheme + + orderHint + 17 + + + SuppressBuildableAutocreation + + 34C6806819DD01C300413468 + + primary + + + + + diff --git a/ImageUnitPlugins/CSChromaKey/CSChromaKey.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/CSChromaKey.xcscheme b/ImageUnitPlugins/CSChromaKey/CSChromaKey.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/CSChromaKey.xcscheme index b3c513ad..d2bd08f2 100644 --- a/ImageUnitPlugins/CSChromaKey/CSChromaKey.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/CSChromaKey.xcscheme +++ b/ImageUnitPlugins/CSChromaKey/CSChromaKey.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/CSChromaKey.xcscheme @@ -1,6 +1,6 @@ +#import "SUExport.h" @class SUAppcastItem; -@interface SUAppcast : NSObject +SU_EXPORT @interface SUAppcast : NSObject -@property (weak) id delegate; @property (copy) NSString *userAgentString; +@property (copy) NSDictionary *httpHeaders; -- (void)fetchAppcastFromURL:(NSURL *)url; +- (void)fetchAppcastFromURL:(NSURL *)url completionBlock:(void (^)(NSError *))err; @property (readonly, copy) NSArray *items; @end -@protocol SUAppcastDelegate -- (void)appcastDidFinishLoading:(SUAppcast *)appcast; -- (void)appcast:(SUAppcast *)appcast failedToLoadWithError:(NSError *)error; -@end - #endif diff --git a/Sparkle.framework/Versions/A/Headers/SUAppcastItem.h b/Sparkle.framework/Versions/A/Headers/SUAppcastItem.h index 3334a1af..86843bfb 100644 --- a/Sparkle.framework/Versions/A/Headers/SUAppcastItem.h +++ b/Sparkle.framework/Versions/A/Headers/SUAppcastItem.h @@ -9,7 +9,10 @@ #ifndef SUAPPCASTITEM_H #define SUAPPCASTITEM_H -@interface SUAppcastItem : NSObject +#import +#import "SUExport.h" + +SU_EXPORT @interface SUAppcastItem : NSObject @property (copy, readonly) NSString *title; @property (copy, readonly) NSDate *date; @property (copy, readonly) NSString *itemDescription; @@ -29,6 +32,7 @@ @property (getter=isDeltaUpdate, readonly) BOOL deltaUpdate; @property (getter=isCriticalUpdate, readonly) BOOL criticalUpdate; +@property (getter=isInformationOnlyUpdate, readonly) BOOL informationOnlyUpdate; // Returns the dictionary provided in initWithDictionary; this might be useful later for extensions. @property (readonly, copy) NSDictionary *propertiesDictionary; diff --git a/Sparkle.framework/Versions/A/Headers/SUErrors.h b/Sparkle.framework/Versions/A/Headers/SUErrors.h new file mode 100644 index 00000000..d73aadba --- /dev/null +++ b/Sparkle.framework/Versions/A/Headers/SUErrors.h @@ -0,0 +1,47 @@ +// +// SUErrors.h +// Sparkle +// +// Created by C.W. Betts on 10/13/14. +// Copyright (c) 2014 Sparkle Project. All rights reserved. +// + +#ifndef SUERRORS_H +#define SUERRORS_H + +#import +#import "SUExport.h" + +/** + * Error domain used by Sparkle + */ +SU_EXPORT extern NSString *const SUSparkleErrorDomain; + +typedef NS_ENUM(OSStatus, SUError) { + // Appcast phase errors. + SUAppcastParseError = 1000, + SUNoUpdateError = 1001, + SUAppcastError = 1002, + SURunningFromDiskImageError = 1003, + + // Downlaod phase errors. + SUTemporaryDirectoryError = 2000, + + // Extraction phase errors. + SUUnarchivingError = 3000, + SUSignatureError = 3001, + + // Installation phase errors. + SUFileCopyFailure = 4000, + SUAuthenticationFailure = 4001, + SUMissingUpdateError = 4002, + SUMissingInstallerToolError = 4003, + SURelaunchError = 4004, + SUInstallationError = 4005, + SUDowngradeError = 4006, + + // System phase errors + SUSystemPowerOffError = 5000 +}; + +#endif diff --git a/Sparkle.framework/Versions/A/Headers/SUExport.h b/Sparkle.framework/Versions/A/Headers/SUExport.h new file mode 100644 index 00000000..3e3f8a16 --- /dev/null +++ b/Sparkle.framework/Versions/A/Headers/SUExport.h @@ -0,0 +1,18 @@ +// +// SUExport.h +// Sparkle +// +// Created by Jake Petroules on 2014-08-23. +// Copyright (c) 2014 Sparkle Project. All rights reserved. +// + +#ifndef SUEXPORT_H +#define SUEXPORT_H + +#ifdef BUILDING_SPARKLE +#define SU_EXPORT __attribute__((visibility("default"))) +#else +#define SU_EXPORT +#endif + +#endif diff --git a/Sparkle.framework/Versions/A/Headers/SUStandardVersionComparator.h b/Sparkle.framework/Versions/A/Headers/SUStandardVersionComparator.h index f40d5715..d7f2a48c 100644 --- a/Sparkle.framework/Versions/A/Headers/SUStandardVersionComparator.h +++ b/Sparkle.framework/Versions/A/Headers/SUStandardVersionComparator.h @@ -9,7 +9,8 @@ #ifndef SUSTANDARDVERSIONCOMPARATOR_H #define SUSTANDARDVERSIONCOMPARATOR_H - +#import +#import "SUExport.h" #import "SUVersionComparisonProtocol.h" /*! @@ -19,7 +20,7 @@ It's "dumb" in that it does essentially string comparison, in components split by character type. */ -@interface SUStandardVersionComparator : NSObject +SU_EXPORT @interface SUStandardVersionComparator : NSObject /*! Returns a singleton instance of the comparator. diff --git a/Sparkle.framework/Versions/A/Headers/SUUpdater.h b/Sparkle.framework/Versions/A/Headers/SUUpdater.h index 4c838571..ed0072af 100644 --- a/Sparkle.framework/Versions/A/Headers/SUUpdater.h +++ b/Sparkle.framework/Versions/A/Headers/SUUpdater.h @@ -9,6 +9,8 @@ #ifndef SUUPDATER_H #define SUUPDATER_H +#import +#import "SUExport.h" #import "SUVersionComparisonProtocol.h" #import "SUVersionDisplayProtocol.h" @@ -22,15 +24,16 @@ This class is used to configure the update paramters as well as manually and automatically schedule and control checks for updates. */ -@interface SUUpdater : NSObject +SU_EXPORT @interface SUUpdater : NSObject -@property (weak) IBOutlet id delegate; +@property (unsafe_unretained) IBOutlet id delegate; + (SUUpdater *)sharedUpdater; + (SUUpdater *)updaterForBundle:(NSBundle *)bundle; - (instancetype)initForBundle:(NSBundle *)bundle; @property (readonly, strong) NSBundle *hostBundle; +@property (strong, readonly) NSBundle *sparkleBundle; @property BOOL automaticallyChecksForUpdates; @@ -45,6 +48,8 @@ @property (nonatomic, copy) NSString *userAgentString; +@property (copy) NSDictionary *httpHeaders; + @property BOOL sendsSystemProfile; @property BOOL automaticallyDownloadsUpdates; @@ -72,6 +77,16 @@ */ - (void)checkForUpdatesInBackground; +/*! + Checks for updates and, if available, immediately downloads and installs them. + A progress dialog is shown but the user will never be prompted to read the + release notes. + + You may want to respond to the userDidCancelDownload delegate method in case + the user clicks the "Cancel" button while the update is downloading. + */ +- (void)installUpdatesIfAvailable; + /*! Returns the date of last update check. @@ -107,17 +122,17 @@ // SUUpdater Notifications for events that might be interesting to more than just the delegate // The updater will be the notification object // ----------------------------------------------------------------------------- -extern NSString *const SUUpdaterDidFinishLoadingAppCastNotification; -extern NSString *const SUUpdaterDidFindValidUpdateNotification; -extern NSString *const SUUpdaterDidNotFindUpdateNotification; -extern NSString *const SUUpdaterWillRestartNotification; +SU_EXPORT extern NSString *const SUUpdaterDidFinishLoadingAppCastNotification; +SU_EXPORT extern NSString *const SUUpdaterDidFindValidUpdateNotification; +SU_EXPORT extern NSString *const SUUpdaterDidNotFindUpdateNotification; +SU_EXPORT extern NSString *const SUUpdaterWillRestartNotification; #define SUUpdaterWillRelaunchApplicationNotification SUUpdaterWillRestartNotification; #define SUUpdaterWillInstallUpdateNotification SUUpdaterWillRestartNotification; // Key for the SUAppcastItem object in the SUUpdaterDidFindValidUpdateNotification userInfo -extern NSString *const SUUpdaterAppcastItemNotificationKey; +SU_EXPORT extern NSString *const SUUpdaterAppcastItemNotificationKey; // Key for the SUAppcast object in the SUUpdaterDidFinishLoadingAppCastNotification userInfo -extern NSString *const SUUpdaterAppcastNotificationKey; +SU_EXPORT extern NSString *const SUUpdaterAppcastNotificationKey; // ----------------------------------------------------------------------------- // SUUpdater Delegate: @@ -205,6 +220,31 @@ extern NSString *const SUUpdaterAppcastNotificationKey; */ - (void)updaterDidNotFindUpdate:(SUUpdater *)updater; +/*! + Called immediately before downloading the specified update. + + \param updater The SUUpdater instance. + \param item The appcast item corresponding to the update that is proposed to be downloaded. + \param request The mutable URL request that will be used to download the update. + */ +- (void)updater:(SUUpdater *)updater willDownloadUpdate:(SUAppcastItem *)item withRequest:(NSMutableURLRequest *)request; + +/*! + Called after the specified update failed to download. + + \param updater The SUUpdater instance. + \param item The appcast item corresponding to the update that failed to download. + \param error The error generated by the failed download. + */ +- (void)updater:(SUUpdater *)updater failedToDownloadUpdate:(SUAppcastItem *)item error:(NSError *)error; + +/*! + Called when the user clicks the cancel button while and update is being downloaded. + + \param updater The SUUpdater instance. + */ +- (void)userDidCancelDownload:(SUUpdater *)updater; + /*! Called immediately before installing the specified update. @@ -311,24 +351,14 @@ extern NSString *const SUUpdaterAppcastNotificationKey; */ - (void)updater:(SUUpdater *)updater didCancelInstallUpdateOnQuit:(SUAppcastItem *)item; +/*! + Called after an update is aborted due to an error. + + \param updater The SUUpdater instance. + \param error The error that caused the abort + */ +- (void)updater:(SUUpdater *)updater didAbortWithError:(NSError *)error; + @end - -// ----------------------------------------------------------------------------- -// Constants: -// ----------------------------------------------------------------------------- - -// Define some minimum intervals to avoid DOS-like checking attacks. These are in seconds. -#if defined(DEBUG) && DEBUG && 0 -#define SU_MIN_CHECK_INTERVAL 60 -#else -#define SU_MIN_CHECK_INTERVAL 60 * 60 -#endif - -#if defined(DEBUG) && DEBUG && 0 -#define SU_DEFAULT_CHECK_INTERVAL 60 -#else -#define SU_DEFAULT_CHECK_INTERVAL 60 * 60 * 24 -#endif - #endif diff --git a/Sparkle.framework/Versions/A/Headers/SUVersionComparisonProtocol.h b/Sparkle.framework/Versions/A/Headers/SUVersionComparisonProtocol.h index d3fb3d2b..10c42669 100644 --- a/Sparkle.framework/Versions/A/Headers/SUVersionComparisonProtocol.h +++ b/Sparkle.framework/Versions/A/Headers/SUVersionComparisonProtocol.h @@ -10,6 +10,7 @@ #define SUVERSIONCOMPARISONPROTOCOL_H #import +#import "SUExport.h" /*! Provides version comparison facilities for Sparkle. diff --git a/Sparkle.framework/Versions/A/Headers/SUVersionDisplayProtocol.h b/Sparkle.framework/Versions/A/Headers/SUVersionDisplayProtocol.h index 697f1a87..97fae4c9 100644 --- a/Sparkle.framework/Versions/A/Headers/SUVersionDisplayProtocol.h +++ b/Sparkle.framework/Versions/A/Headers/SUVersionDisplayProtocol.h @@ -7,7 +7,7 @@ // #import - +#import "SUExport.h" /*! Applies special display formatting to version numbers. diff --git a/Sparkle.framework/Versions/A/Headers/Sparkle.h b/Sparkle.framework/Versions/A/Headers/Sparkle.h index 954ca519..20ed6979 100644 --- a/Sparkle.framework/Versions/A/Headers/Sparkle.h +++ b/Sparkle.framework/Versions/A/Headers/Sparkle.h @@ -9,14 +9,17 @@ #ifndef SPARKLE_H #define SPARKLE_H +#import + // This list should include the shared headers. It doesn't matter if some of them aren't shared (unless // there are name-space collisions) so we can list all of them to start with: -#import - -#import -#import -#import -#import +#import "SUAppcast.h" +#import "SUAppcastItem.h" +#import "SUStandardVersionComparator.h" +#import "SUUpdater.h" +#import "SUVersionComparisonProtocol.h" +#import "SUVersionDisplayProtocol.h" +#import "SUErrors.h" #endif diff --git a/Sparkle.framework/Versions/A/Modules/module.modulemap b/Sparkle.framework/Versions/A/Modules/module.modulemap new file mode 100644 index 00000000..af3fe6d0 --- /dev/null +++ b/Sparkle.framework/Versions/A/Modules/module.modulemap @@ -0,0 +1,6 @@ +framework module Sparkle { + umbrella header "Sparkle.h" + + export * + module * { export * } +} diff --git a/Sparkle.framework/Versions/A/PrivateHeaders/SUUnarchiver.h b/Sparkle.framework/Versions/A/PrivateHeaders/SUUnarchiver.h new file mode 100644 index 00000000..6397fe71 --- /dev/null +++ b/Sparkle.framework/Versions/A/PrivateHeaders/SUUnarchiver.h @@ -0,0 +1,35 @@ +// +// SUUnarchiver.h +// Sparkle +// +// Created by Andy Matuschak on 3/16/06. +// Copyright 2006 Andy Matuschak. All rights reserved. +// + +#ifndef SUUNARCHIVER_H +#define SUUNARCHIVER_H + +#import + +@class SUHost; +@protocol SUUnarchiverDelegate; + +@interface SUUnarchiver : NSObject + +@property (copy, readonly) NSString *archivePath; +@property (copy, readonly) NSString *updateHostBundlePath; +@property (weak) id delegate; + ++ (SUUnarchiver *)unarchiverForPath:(NSString *)path updatingHostBundlePath:(NSString *)host; + +- (void)start; +@end + +@protocol SUUnarchiverDelegate +- (void)unarchiverDidFinish:(SUUnarchiver *)unarchiver; +- (void)unarchiverDidFail:(SUUnarchiver *)unarchiver; +@optional +- (void)unarchiver:(SUUnarchiver *)unarchiver extractedProgress:(double)progress; +@end + +#endif diff --git a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Info.plist b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Info.plist index e5578182..26a02122 100644 --- a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Info.plist +++ b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Info.plist @@ -3,7 +3,7 @@ BuildMachineOSBuild - 14A298i + 15E27e CFBundleDevelopmentRegion English CFBundleExecutable @@ -17,25 +17,29 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.8.0 + 1.13.1 git-2afc553 CFBundleSignature ???? + CFBundleSupportedPlatforms + + MacOSX + CFBundleVersion - 1.8.0 + 1.13.1 DTCompiler com.apple.compilers.llvm.clang.1_0 DTPlatformBuild - 6A254o + 7C68 DTPlatformVersion GM DTSDKBuild - 14A283h + 15C43 DTSDKName - macosx10.10 + macosx10.11 DTXcode - 0600 + 0720 DTXcodeBuild - 6A254o + 7C68 LSBackgroundOnly 1 LSMinimumSystemVersion diff --git a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/MacOS/Autoupdate b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/MacOS/Autoupdate index a3740cd5..7caea3d5 100755 Binary files a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/MacOS/Autoupdate and b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/MacOS/Autoupdate differ diff --git a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/AppIcon.icns b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/AppIcon.icns index b6fa7347..75c7c379 100644 Binary files a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/AppIcon.icns and b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/AppIcon.icns differ diff --git a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/SUStatus.nib b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/SUStatus.nib index b16eb221..30f3c2c4 100644 Binary files a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/SUStatus.nib and b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/SUStatus.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ar.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ar.lproj/Sparkle.strings index 28c2c217..057e2f82 100644 Binary files a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ar.lproj/Sparkle.strings and b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ar.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ca.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ca.lproj/Sparkle.strings new file mode 100644 index 00000000..cc238f68 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ca.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/cs.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/cs.lproj/Sparkle.strings index a50d6bf2..6ca360d1 100644 Binary files a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/cs.lproj/Sparkle.strings and b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/cs.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/da.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/da.lproj/Sparkle.strings index 8c349bd2..266c0693 100644 Binary files a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/da.lproj/Sparkle.strings and b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/da.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/de.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/de.lproj/Sparkle.strings index fe50db5c..f99c8c0e 100644 Binary files a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/de.lproj/Sparkle.strings and b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/de.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/el.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/el.lproj/Sparkle.strings index 015c213e..394c159c 100644 Binary files a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/el.lproj/Sparkle.strings and b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/el.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/en.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/en.lproj/Sparkle.strings index d2e5e5ce..f427ad69 100644 Binary files a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/en.lproj/Sparkle.strings and b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/en.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/es.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/es.lproj/Sparkle.strings index ee694079..8922b321 100644 Binary files a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/es.lproj/Sparkle.strings and b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/es.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/fi.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/fi.lproj/Sparkle.strings new file mode 100644 index 00000000..32d3107f Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/fi.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/fr.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/fr.lproj/Sparkle.strings index c7b221b1..6577569f 100644 Binary files a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/fr.lproj/Sparkle.strings and b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/fr.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/he.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/he.lproj/Sparkle.strings new file mode 100644 index 00000000..99124ccc Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/he.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/is.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/is.lproj/Sparkle.strings index 68ae6e0a..74ae7280 100644 Binary files a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/is.lproj/Sparkle.strings and b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/is.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/it.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/it.lproj/Sparkle.strings index 99955afc..f7fb9358 100644 Binary files a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/it.lproj/Sparkle.strings and b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/it.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ja.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ja.lproj/Sparkle.strings index faa546c8..1925ba49 100644 Binary files a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ja.lproj/Sparkle.strings and b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ja.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ko.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ko.lproj/Sparkle.strings index d5df79da..c6ecfbac 100644 Binary files a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ko.lproj/Sparkle.strings and b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ko.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/nb.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/nb.lproj/Sparkle.strings index 05a426d1..25e20793 100644 Binary files a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/nb.lproj/Sparkle.strings and b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/nb.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/nl.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/nl.lproj/Sparkle.strings index 95923ee0..de389122 100644 Binary files a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/nl.lproj/Sparkle.strings and b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/nl.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/pl.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/pl.lproj/Sparkle.strings index d10801f5..e366e3bf 100644 Binary files a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/pl.lproj/Sparkle.strings and b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/pl.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/pt_BR.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/pt_BR.lproj/Sparkle.strings index 0f2ef4da..300fc86a 100644 Binary files a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/pt_BR.lproj/Sparkle.strings and b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/pt_BR.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/pt_PT.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/pt_PT.lproj/Sparkle.strings index 69853a3f..d3eddf75 100644 Binary files a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/pt_PT.lproj/Sparkle.strings and b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/pt_PT.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ro.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ro.lproj/Sparkle.strings index 3cd694d5..28a407bc 100644 Binary files a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ro.lproj/Sparkle.strings and b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ro.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ru.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ru.lproj/Sparkle.strings index 349f17fe..d5cb6073 100644 Binary files a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ru.lproj/Sparkle.strings and b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ru.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/sk.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/sk.lproj/Sparkle.strings index 38bb70dd..949fb16e 100644 Binary files a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/sk.lproj/Sparkle.strings and b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/sk.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/sl.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/sl.lproj/Sparkle.strings index 889a6583..c1ce5a04 100644 Binary files a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/sl.lproj/Sparkle.strings and b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/sl.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/sv.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/sv.lproj/Sparkle.strings index fbebf966..e65ac559 100644 Binary files a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/sv.lproj/Sparkle.strings and b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/sv.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/th.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/th.lproj/Sparkle.strings index 323bb70f..fc728fd5 100644 Binary files a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/th.lproj/Sparkle.strings and b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/th.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/tr.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/tr.lproj/Sparkle.strings index ccc268fd..c41e3dba 100644 Binary files a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/tr.lproj/Sparkle.strings and b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/tr.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/uk.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/uk.lproj/Sparkle.strings index c3f3d9e0..521656d3 100644 Binary files a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/uk.lproj/Sparkle.strings and b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/uk.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/zh_CN.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/zh_CN.lproj/Sparkle.strings index 49b68ce1..0e91210e 100644 Binary files a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/zh_CN.lproj/Sparkle.strings and b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/zh_CN.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/zh_TW.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/zh_TW.lproj/Sparkle.strings index 17fcca0a..ea8c82f9 100644 Binary files a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/zh_TW.lproj/Sparkle.strings and b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/zh_TW.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/Info.plist b/Sparkle.framework/Versions/A/Resources/Info.plist index fd7868be..aba6f813 100644 --- a/Sparkle.framework/Versions/A/Resources/Info.plist +++ b/Sparkle.framework/Versions/A/Resources/Info.plist @@ -3,13 +3,13 @@ BuildMachineOSBuild - 14A298i + 15E27e CFBundleDevelopmentRegion en CFBundleExecutable Sparkle CFBundleIdentifier - org.andymatuschak.Sparkle + org.sparkle-project.Sparkle CFBundleInfoDictionaryVersion 6.0 CFBundleName @@ -17,24 +17,28 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 1.8.0 + 1.13.1 git-2afc553 CFBundleSignature ???? + CFBundleSupportedPlatforms + + MacOSX + CFBundleVersion - 0b186dc + 1.13.1 DTCompiler com.apple.compilers.llvm.clang.1_0 DTPlatformBuild - 6A254o + 7C68 DTPlatformVersion GM DTSDKBuild - 14A283h + 15C43 DTSDKName - macosx10.10 + macosx10.11 DTXcode - 0600 + 0720 DTXcodeBuild - 6A254o + 7C68 diff --git a/Sparkle.framework/Versions/A/Resources/SUModelTranslation.plist b/Sparkle.framework/Versions/A/Resources/SUModelTranslation.plist index 74db0ff0..1f75b248 100644 --- a/Sparkle.framework/Versions/A/Resources/SUModelTranslation.plist +++ b/Sparkle.framework/Versions/A/Resources/SUModelTranslation.plist @@ -16,6 +16,8 @@ iMac (Core 2 Duo, 17 inch, Combo Drive) iMac6,1 iMac (Core 2 Duo, 24 inch, SuperDrive) + iMac7,1 + iMac Intel Core 2 Duo (aluminum enclosure) iMac8,1 iMac (Core 2 Duo, 20 or 24 inch, Early 2008 ) iMac9,1 @@ -25,7 +27,7 @@ iMac11,1 iMac (Core i5 or i7, 27 inch Late 2009) iMac11,2 - iMac (Core i3 or i5, 27 inch Mid 2010) + 21.5" iMac (mid 2010) iMac11,3 iMac (Core i5 or i7, 27 inch Mid 2010) iMac12,1 @@ -42,6 +44,16 @@ iMac (Core i5 or i7, 27 inch Late 2013) iMac14,3 iMac (Core i5 or i7, 21.5 inch Late 2013) + iMac14,4 + iMac (Core i5, 21.5 inch Mid 2014) + iMac15,1 + iMac (Retina 5K Core i5 or i7, 27 inch Late 2014 or Mid 2015) + iMac16,1 + iMac (Core i5, 21,5 inch Late 2015) + iMac16,2 + iMac (Retina 4K Core i5 or i7, 21.5 inch Late 2015) + iMac17,1 + iMac (Retina 5K Core i5 or i7, 27 inch Late 2015) MacBook1,1 MacBook (Core Duo) MacBook2,1 @@ -56,6 +68,8 @@ MacBook (Core 2 Duo, Late 2009, Unibody) MacBook7,1 MacBook (Core 2 Duo, Mid 2010, White) + MacBook8,1 + MacBook (Core M, 12 inch, Early 2015) MacBookAir1,1 MacBook Air (Core 2 Duo, 13 inch, Early 2008) MacBookAir2,1 @@ -76,6 +90,10 @@ MacBook Air (Core i5 or i7, 11 inch, Mid 2013 or Early 2014) MacBookAir6,2 MacBook Air (Core i5 or i7, 13 inch, Mid 2013 or Early 2014) + MacBookAir7,1 + MacBook Air (Core i5 or i7, 11 inch, Early 2015) + MacBookAir7,2 + MacBook Air (Core i5 or i7, 13 inch, Early 2015) MacBookPro1,1 MacBook Pro Core Duo (15-inch) MacBookPro1,2 @@ -90,8 +108,70 @@ MacBook Pro Core 2 Duo (17-inch HD, Core 2 Duo) MacBookPro4,1 MacBook Pro (Core 2 Duo Feb 2008) + MacBookPro5,1 + MacBook Pro Intel Core 2 Duo (aluminum unibody) + MacBookPro5,2 + MacBook Pro Intel Core 2 Duo (aluminum unibody) + MacBookPro5,3 + MacBook Pro Intel Core 2 Duo (aluminum unibody) + MacBookPro5,4 + MacBook Pro Intel Core 2 Duo (aluminum unibody) + MacBookPro5,5 + MacBook Pro Intel Core 2 Duo (aluminum unibody) + MacBookPro6,1 + MacBook Pro Intel Core i5, Intel Core i7 (mid 2010) + MacBookPro6,2 + MacBook Pro Intel Core i5, Intel Core i7 (mid 2010) + MacBookPro7,1 + MacBook Pro Intel Core 2 Duo (mid 2010) + MacBookPro8,1 + MacBook Pro Intel Core i5, Intel Core i7, 13" (early 2011) + MacBookPro8,2 + MacBook Pro Intel Core i7, 15" (early 2011) + MacBookPro8,3 + MacBook Pro Intel Core i7, 17" (early 2011) + MacBookPro9,1 + MacBook Pro (15-inch, Mid 2012) + MacBookPro9,2 + MacBook Pro (13-inch, Mid 2012) + MacBookPro10,1 + MacBook Pro (Retina, Mid 2012) + MacBookPro10,2 + MacBook Pro (Retina, 13-inch, Late 2012) + MacBookPro11,1 + MacBook Pro (Retina, 13-inch, Late 2013) + MacBookPro11,2 + MacBook Pro (Retina, 15-inch, Late 2013) + MacBookPro11,3 + MacBook Pro (Retina, 15-inch, Late 2013) + MacbookPro11,4 + MacBook Pro (Retina, 15-inch, Mid 2015) + MacbookPro11,5 + MacBook Pro (Retina, 15-inch, Mid 2015) + MacbookPro12,1  + MacBook Pro (Retina, 13-inch, Early 2015) Macmini1,1 Mac Mini (Core Solo/Duo) + Macmini2,1 + Mac mini Intel Core + Macmini3,1 + Mac mini Intel Core + Macmini4,1 + Mac mini Intel Core (Mid 2010) + Macmini5,1 + Mac mini (Core i5, Mid 2011) + Macmini5,2 + Mac mini (Core i5 or Core i7, Mid 2011) + Macmini5,3 + Mac mini (Core i7, Server, Mid 2011) + Macmini6,1 + Mac mini (Core i5, Late 2012) + Macmini6,2 + Mac mini (Core i7, Normal or Server, Late 2012) + Macmini7,1 + Mac mini (Core i5 or Core i7, Late 2014) + MacPro1,1,Quad + Mac Pro MacPro1,1 Mac Pro (four-core) MacPro2,1 @@ -101,7 +181,9 @@ MacPro4,1 Mac Pro (March 2009) MacPro5,1 - Mac Pro (August 2010) + Mac Pro (2010 or 2012) + MacPro6,1 + Mac Pro (Late 2013) PowerBook1,1 PowerBook G3 PowerBook2,1 @@ -164,14 +246,6 @@ Power Macintosh G3 (Blue & White) PowerMac1,2 Power Macintosh G4 (PCI Graphics) - PowerMac10,1 - Mac Mini G4 - PowerMac10,2 - Mac Mini (Late 2005) - PowerMac11,2 - Power Macintosh G5 (Late 2005) - PowerMac12,1 - iMac G5 (iSight) PowerMac2,1 iMac G3 (Slot-loading CD-ROM) PowerMac2,2 @@ -198,6 +272,8 @@ iMac G4 (17-inch Flat Panel) PowerMac5,1 Power Macintosh G4 Cube + PowerMac5,2 + Power Mac G4 Cube PowerMac6,1 iMac G4 (USB 2.0) PowerMac6,3 @@ -214,6 +290,14 @@ iMac G5 (Ambient Light Sensor) PowerMac9,1 Power Macintosh G5 (Late 2005) + PowerMac10,1 + Mac Mini G4 + PowerMac10,2 + Mac Mini (Late 2005) + PowerMac11,2 + Power Macintosh G5 (Late 2005) + PowerMac12,1 + iMac G5 (iSight) RackMac1,1 Xserve G4 RackMac1,2 @@ -224,5 +308,7 @@ Xserve (Intel Xeon) Xserve2,1 Xserve (January 2008 quad-core) + Xserve3,1 + Xserve (early 2009) diff --git a/Sparkle.framework/Versions/A/Resources/SUStatus.nib b/Sparkle.framework/Versions/A/Resources/SUStatus.nib index b16eb221..30f3c2c4 100644 Binary files a/Sparkle.framework/Versions/A/Resources/SUStatus.nib and b/Sparkle.framework/Versions/A/Resources/SUStatus.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/ar.lproj/SUAutomaticUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/ar.lproj/SUAutomaticUpdateAlert.nib index bdbae6e2..46d10f10 100644 Binary files a/Sparkle.framework/Versions/A/Resources/ar.lproj/SUAutomaticUpdateAlert.nib and b/Sparkle.framework/Versions/A/Resources/ar.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/ar.lproj/SUUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/ar.lproj/SUUpdateAlert.nib index e58bbdaf..1fafb033 100644 Binary files a/Sparkle.framework/Versions/A/Resources/ar.lproj/SUUpdateAlert.nib and b/Sparkle.framework/Versions/A/Resources/ar.lproj/SUUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/ar.lproj/SUUpdatePermissionPrompt.nib b/Sparkle.framework/Versions/A/Resources/ar.lproj/SUUpdatePermissionPrompt.nib index 80c21a86..f93d74b0 100644 Binary files a/Sparkle.framework/Versions/A/Resources/ar.lproj/SUUpdatePermissionPrompt.nib and b/Sparkle.framework/Versions/A/Resources/ar.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/ar.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/ar.lproj/Sparkle.strings index 28c2c217..057e2f82 100644 Binary files a/Sparkle.framework/Versions/A/Resources/ar.lproj/Sparkle.strings and b/Sparkle.framework/Versions/A/Resources/ar.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/ca.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/ca.lproj/Sparkle.strings new file mode 100644 index 00000000..cc238f68 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/ca.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/cs.lproj/SUAutomaticUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/cs.lproj/SUAutomaticUpdateAlert.nib index d6155cc3..4b32c516 100644 Binary files a/Sparkle.framework/Versions/A/Resources/cs.lproj/SUAutomaticUpdateAlert.nib and b/Sparkle.framework/Versions/A/Resources/cs.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/cs.lproj/SUUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/cs.lproj/SUUpdateAlert.nib index 57d4e99f..122c0e03 100644 Binary files a/Sparkle.framework/Versions/A/Resources/cs.lproj/SUUpdateAlert.nib and b/Sparkle.framework/Versions/A/Resources/cs.lproj/SUUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/cs.lproj/SUUpdatePermissionPrompt.nib b/Sparkle.framework/Versions/A/Resources/cs.lproj/SUUpdatePermissionPrompt.nib index 813a273f..26ec0254 100644 Binary files a/Sparkle.framework/Versions/A/Resources/cs.lproj/SUUpdatePermissionPrompt.nib and b/Sparkle.framework/Versions/A/Resources/cs.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/cs.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/cs.lproj/Sparkle.strings index a50d6bf2..6ca360d1 100644 Binary files a/Sparkle.framework/Versions/A/Resources/cs.lproj/Sparkle.strings and b/Sparkle.framework/Versions/A/Resources/cs.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/da.lproj/SUAutomaticUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/da.lproj/SUAutomaticUpdateAlert.nib index 1731d8fe..a1fbef90 100644 Binary files a/Sparkle.framework/Versions/A/Resources/da.lproj/SUAutomaticUpdateAlert.nib and b/Sparkle.framework/Versions/A/Resources/da.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/da.lproj/SUUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/da.lproj/SUUpdateAlert.nib index 487672c8..05bf2cb3 100644 Binary files a/Sparkle.framework/Versions/A/Resources/da.lproj/SUUpdateAlert.nib and b/Sparkle.framework/Versions/A/Resources/da.lproj/SUUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/da.lproj/SUUpdatePermissionPrompt.nib b/Sparkle.framework/Versions/A/Resources/da.lproj/SUUpdatePermissionPrompt.nib index 21280e17..21fca7b9 100644 Binary files a/Sparkle.framework/Versions/A/Resources/da.lproj/SUUpdatePermissionPrompt.nib and b/Sparkle.framework/Versions/A/Resources/da.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/da.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/da.lproj/Sparkle.strings index 8c349bd2..266c0693 100644 Binary files a/Sparkle.framework/Versions/A/Resources/da.lproj/Sparkle.strings and b/Sparkle.framework/Versions/A/Resources/da.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/de.lproj/SUAutomaticUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/de.lproj/SUAutomaticUpdateAlert.nib index 95f6ba70..66360626 100644 Binary files a/Sparkle.framework/Versions/A/Resources/de.lproj/SUAutomaticUpdateAlert.nib and b/Sparkle.framework/Versions/A/Resources/de.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/de.lproj/SUUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/de.lproj/SUUpdateAlert.nib index b1c45c53..a457c2f6 100644 Binary files a/Sparkle.framework/Versions/A/Resources/de.lproj/SUUpdateAlert.nib and b/Sparkle.framework/Versions/A/Resources/de.lproj/SUUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/de.lproj/SUUpdatePermissionPrompt.nib b/Sparkle.framework/Versions/A/Resources/de.lproj/SUUpdatePermissionPrompt.nib index ea967a11..92499cbb 100644 Binary files a/Sparkle.framework/Versions/A/Resources/de.lproj/SUUpdatePermissionPrompt.nib and b/Sparkle.framework/Versions/A/Resources/de.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/de.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/de.lproj/Sparkle.strings index fe50db5c..f99c8c0e 100644 Binary files a/Sparkle.framework/Versions/A/Resources/de.lproj/Sparkle.strings and b/Sparkle.framework/Versions/A/Resources/de.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/el.lproj/SUAutomaticUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/el.lproj/SUAutomaticUpdateAlert.nib index 4cb03b9f..a3db760f 100644 Binary files a/Sparkle.framework/Versions/A/Resources/el.lproj/SUAutomaticUpdateAlert.nib and b/Sparkle.framework/Versions/A/Resources/el.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/el.lproj/SUUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/el.lproj/SUUpdateAlert.nib index 4baedf49..20302af0 100644 Binary files a/Sparkle.framework/Versions/A/Resources/el.lproj/SUUpdateAlert.nib and b/Sparkle.framework/Versions/A/Resources/el.lproj/SUUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/el.lproj/SUUpdatePermissionPrompt.nib b/Sparkle.framework/Versions/A/Resources/el.lproj/SUUpdatePermissionPrompt.nib index 390bfdc9..157168bb 100644 Binary files a/Sparkle.framework/Versions/A/Resources/el.lproj/SUUpdatePermissionPrompt.nib and b/Sparkle.framework/Versions/A/Resources/el.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/el.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/el.lproj/Sparkle.strings index 015c213e..394c159c 100644 Binary files a/Sparkle.framework/Versions/A/Resources/el.lproj/Sparkle.strings and b/Sparkle.framework/Versions/A/Resources/el.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/en.lproj/SUAutomaticUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/en.lproj/SUAutomaticUpdateAlert.nib index 10b3948c..df090b49 100644 Binary files a/Sparkle.framework/Versions/A/Resources/en.lproj/SUAutomaticUpdateAlert.nib and b/Sparkle.framework/Versions/A/Resources/en.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdateAlert.nib index 2f73d6e5..7ed26479 100644 Binary files a/Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdateAlert.nib and b/Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdatePermissionPrompt.nib b/Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdatePermissionPrompt.nib index e962ae2f..9c0c887c 100644 Binary files a/Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdatePermissionPrompt.nib and b/Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/en.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/en.lproj/Sparkle.strings index d2e5e5ce..f427ad69 100644 Binary files a/Sparkle.framework/Versions/A/Resources/en.lproj/Sparkle.strings and b/Sparkle.framework/Versions/A/Resources/en.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/es.lproj/SUAutomaticUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/es.lproj/SUAutomaticUpdateAlert.nib index 06e1a62b..4fc4bbc4 100644 Binary files a/Sparkle.framework/Versions/A/Resources/es.lproj/SUAutomaticUpdateAlert.nib and b/Sparkle.framework/Versions/A/Resources/es.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/es.lproj/SUUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/es.lproj/SUUpdateAlert.nib index a59b1aff..5ae5adef 100644 Binary files a/Sparkle.framework/Versions/A/Resources/es.lproj/SUUpdateAlert.nib and b/Sparkle.framework/Versions/A/Resources/es.lproj/SUUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/es.lproj/SUUpdatePermissionPrompt.nib b/Sparkle.framework/Versions/A/Resources/es.lproj/SUUpdatePermissionPrompt.nib index f3c227fd..8006f904 100644 Binary files a/Sparkle.framework/Versions/A/Resources/es.lproj/SUUpdatePermissionPrompt.nib and b/Sparkle.framework/Versions/A/Resources/es.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/es.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/es.lproj/Sparkle.strings index ee694079..8922b321 100644 Binary files a/Sparkle.framework/Versions/A/Resources/es.lproj/Sparkle.strings and b/Sparkle.framework/Versions/A/Resources/es.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/fi.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/fi.lproj/Sparkle.strings new file mode 100644 index 00000000..32d3107f Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/fi.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/fr.lproj/SUAutomaticUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/fr.lproj/SUAutomaticUpdateAlert.nib index 7bb50b35..bc13d0c0 100644 Binary files a/Sparkle.framework/Versions/A/Resources/fr.lproj/SUAutomaticUpdateAlert.nib and b/Sparkle.framework/Versions/A/Resources/fr.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/fr.lproj/SUUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/fr.lproj/SUUpdateAlert.nib index 46e6ee95..f8f1f1c3 100644 Binary files a/Sparkle.framework/Versions/A/Resources/fr.lproj/SUUpdateAlert.nib and b/Sparkle.framework/Versions/A/Resources/fr.lproj/SUUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/fr.lproj/SUUpdatePermissionPrompt.nib b/Sparkle.framework/Versions/A/Resources/fr.lproj/SUUpdatePermissionPrompt.nib index eef7c808..7b2b40e6 100644 Binary files a/Sparkle.framework/Versions/A/Resources/fr.lproj/SUUpdatePermissionPrompt.nib and b/Sparkle.framework/Versions/A/Resources/fr.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/fr.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/fr.lproj/Sparkle.strings index c7b221b1..6577569f 100644 Binary files a/Sparkle.framework/Versions/A/Resources/fr.lproj/Sparkle.strings and b/Sparkle.framework/Versions/A/Resources/fr.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/he.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/he.lproj/Sparkle.strings new file mode 100644 index 00000000..99124ccc Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/he.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/is.lproj/SUAutomaticUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/is.lproj/SUAutomaticUpdateAlert.nib index 27a8e8d7..3550df4c 100644 Binary files a/Sparkle.framework/Versions/A/Resources/is.lproj/SUAutomaticUpdateAlert.nib and b/Sparkle.framework/Versions/A/Resources/is.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/is.lproj/SUUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/is.lproj/SUUpdateAlert.nib index 55c5b059..683ad629 100644 Binary files a/Sparkle.framework/Versions/A/Resources/is.lproj/SUUpdateAlert.nib and b/Sparkle.framework/Versions/A/Resources/is.lproj/SUUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/is.lproj/SUUpdatePermissionPrompt.nib b/Sparkle.framework/Versions/A/Resources/is.lproj/SUUpdatePermissionPrompt.nib index 64506f52..6551540d 100644 Binary files a/Sparkle.framework/Versions/A/Resources/is.lproj/SUUpdatePermissionPrompt.nib and b/Sparkle.framework/Versions/A/Resources/is.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/is.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/is.lproj/Sparkle.strings index 68ae6e0a..74ae7280 100644 Binary files a/Sparkle.framework/Versions/A/Resources/is.lproj/Sparkle.strings and b/Sparkle.framework/Versions/A/Resources/is.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/it.lproj/SUAutomaticUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/it.lproj/SUAutomaticUpdateAlert.nib index d8177c67..74eb0267 100644 Binary files a/Sparkle.framework/Versions/A/Resources/it.lproj/SUAutomaticUpdateAlert.nib and b/Sparkle.framework/Versions/A/Resources/it.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/it.lproj/SUUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/it.lproj/SUUpdateAlert.nib index e439f2cb..a7bc37b4 100644 Binary files a/Sparkle.framework/Versions/A/Resources/it.lproj/SUUpdateAlert.nib and b/Sparkle.framework/Versions/A/Resources/it.lproj/SUUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/it.lproj/SUUpdatePermissionPrompt.nib b/Sparkle.framework/Versions/A/Resources/it.lproj/SUUpdatePermissionPrompt.nib index 6d40161b..7581873f 100644 Binary files a/Sparkle.framework/Versions/A/Resources/it.lproj/SUUpdatePermissionPrompt.nib and b/Sparkle.framework/Versions/A/Resources/it.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/it.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/it.lproj/Sparkle.strings index 99955afc..f7fb9358 100644 Binary files a/Sparkle.framework/Versions/A/Resources/it.lproj/Sparkle.strings and b/Sparkle.framework/Versions/A/Resources/it.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/ja.lproj/SUAutomaticUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/ja.lproj/SUAutomaticUpdateAlert.nib index 69444f08..7207576b 100644 Binary files a/Sparkle.framework/Versions/A/Resources/ja.lproj/SUAutomaticUpdateAlert.nib and b/Sparkle.framework/Versions/A/Resources/ja.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/ja.lproj/SUUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/ja.lproj/SUUpdateAlert.nib index e56ef40b..5479c403 100644 Binary files a/Sparkle.framework/Versions/A/Resources/ja.lproj/SUUpdateAlert.nib and b/Sparkle.framework/Versions/A/Resources/ja.lproj/SUUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/ja.lproj/SUUpdatePermissionPrompt.nib b/Sparkle.framework/Versions/A/Resources/ja.lproj/SUUpdatePermissionPrompt.nib index 70fda1a1..67c837f1 100644 Binary files a/Sparkle.framework/Versions/A/Resources/ja.lproj/SUUpdatePermissionPrompt.nib and b/Sparkle.framework/Versions/A/Resources/ja.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/ja.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/ja.lproj/Sparkle.strings index faa546c8..1925ba49 100644 Binary files a/Sparkle.framework/Versions/A/Resources/ja.lproj/Sparkle.strings and b/Sparkle.framework/Versions/A/Resources/ja.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/ko.lproj/SUAutomaticUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/ko.lproj/SUAutomaticUpdateAlert.nib index 6babcd5f..95105afd 100644 Binary files a/Sparkle.framework/Versions/A/Resources/ko.lproj/SUAutomaticUpdateAlert.nib and b/Sparkle.framework/Versions/A/Resources/ko.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/ko.lproj/SUUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/ko.lproj/SUUpdateAlert.nib index 23fc9a86..5a815712 100644 Binary files a/Sparkle.framework/Versions/A/Resources/ko.lproj/SUUpdateAlert.nib and b/Sparkle.framework/Versions/A/Resources/ko.lproj/SUUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/ko.lproj/SUUpdatePermissionPrompt.nib b/Sparkle.framework/Versions/A/Resources/ko.lproj/SUUpdatePermissionPrompt.nib index 98d29a93..8cecd70a 100644 Binary files a/Sparkle.framework/Versions/A/Resources/ko.lproj/SUUpdatePermissionPrompt.nib and b/Sparkle.framework/Versions/A/Resources/ko.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/ko.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/ko.lproj/Sparkle.strings index d5df79da..c6ecfbac 100644 Binary files a/Sparkle.framework/Versions/A/Resources/ko.lproj/Sparkle.strings and b/Sparkle.framework/Versions/A/Resources/ko.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/nb.lproj/SUAutomaticUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/nb.lproj/SUAutomaticUpdateAlert.nib index 78424ebc..ab9491fb 100644 Binary files a/Sparkle.framework/Versions/A/Resources/nb.lproj/SUAutomaticUpdateAlert.nib and b/Sparkle.framework/Versions/A/Resources/nb.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/nb.lproj/SUUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/nb.lproj/SUUpdateAlert.nib index ab23fb74..14bcaf72 100644 Binary files a/Sparkle.framework/Versions/A/Resources/nb.lproj/SUUpdateAlert.nib and b/Sparkle.framework/Versions/A/Resources/nb.lproj/SUUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/nb.lproj/SUUpdatePermissionPrompt.nib b/Sparkle.framework/Versions/A/Resources/nb.lproj/SUUpdatePermissionPrompt.nib index 397da758..54e248f9 100644 Binary files a/Sparkle.framework/Versions/A/Resources/nb.lproj/SUUpdatePermissionPrompt.nib and b/Sparkle.framework/Versions/A/Resources/nb.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/nb.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/nb.lproj/Sparkle.strings index 05a426d1..25e20793 100644 Binary files a/Sparkle.framework/Versions/A/Resources/nb.lproj/Sparkle.strings and b/Sparkle.framework/Versions/A/Resources/nb.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/nl.lproj/SUAutomaticUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/nl.lproj/SUAutomaticUpdateAlert.nib index ac9a4873..f60fb1d0 100644 Binary files a/Sparkle.framework/Versions/A/Resources/nl.lproj/SUAutomaticUpdateAlert.nib and b/Sparkle.framework/Versions/A/Resources/nl.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/nl.lproj/SUUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/nl.lproj/SUUpdateAlert.nib index cd37445f..7da34c23 100644 Binary files a/Sparkle.framework/Versions/A/Resources/nl.lproj/SUUpdateAlert.nib and b/Sparkle.framework/Versions/A/Resources/nl.lproj/SUUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/nl.lproj/SUUpdatePermissionPrompt.nib b/Sparkle.framework/Versions/A/Resources/nl.lproj/SUUpdatePermissionPrompt.nib index de7f9b26..516751a6 100644 Binary files a/Sparkle.framework/Versions/A/Resources/nl.lproj/SUUpdatePermissionPrompt.nib and b/Sparkle.framework/Versions/A/Resources/nl.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/nl.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/nl.lproj/Sparkle.strings index 95923ee0..de389122 100644 Binary files a/Sparkle.framework/Versions/A/Resources/nl.lproj/Sparkle.strings and b/Sparkle.framework/Versions/A/Resources/nl.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/pl.lproj/SUAutomaticUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/pl.lproj/SUAutomaticUpdateAlert.nib index 080a9dbe..a7ae9831 100644 Binary files a/Sparkle.framework/Versions/A/Resources/pl.lproj/SUAutomaticUpdateAlert.nib and b/Sparkle.framework/Versions/A/Resources/pl.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/pl.lproj/SUUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/pl.lproj/SUUpdateAlert.nib index 6fe229f5..d7a2f0f9 100644 Binary files a/Sparkle.framework/Versions/A/Resources/pl.lproj/SUUpdateAlert.nib and b/Sparkle.framework/Versions/A/Resources/pl.lproj/SUUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/pl.lproj/SUUpdatePermissionPrompt.nib b/Sparkle.framework/Versions/A/Resources/pl.lproj/SUUpdatePermissionPrompt.nib index c1ddfd06..616cf6a0 100644 Binary files a/Sparkle.framework/Versions/A/Resources/pl.lproj/SUUpdatePermissionPrompt.nib and b/Sparkle.framework/Versions/A/Resources/pl.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/pl.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/pl.lproj/Sparkle.strings index d10801f5..e366e3bf 100644 Binary files a/Sparkle.framework/Versions/A/Resources/pl.lproj/Sparkle.strings and b/Sparkle.framework/Versions/A/Resources/pl.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/SUAutomaticUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/SUAutomaticUpdateAlert.nib index b6e5e77a..c04684b2 100644 Binary files a/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/SUAutomaticUpdateAlert.nib and b/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/SUUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/SUUpdateAlert.nib index e36ca283..c0831eed 100644 Binary files a/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/SUUpdateAlert.nib and b/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/SUUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/SUUpdatePermissionPrompt.nib b/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/SUUpdatePermissionPrompt.nib index 95c042b7..da41ed28 100644 Binary files a/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/SUUpdatePermissionPrompt.nib and b/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/Sparkle.strings index 0f2ef4da..300fc86a 100644 Binary files a/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/Sparkle.strings and b/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/SUAutomaticUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/SUAutomaticUpdateAlert.nib index 84a7e45d..a7c83d71 100644 Binary files a/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/SUAutomaticUpdateAlert.nib and b/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/SUUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/SUUpdateAlert.nib index 067cf68e..7ae53223 100644 Binary files a/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/SUUpdateAlert.nib and b/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/SUUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/SUUpdatePermissionPrompt.nib b/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/SUUpdatePermissionPrompt.nib index fc8e4d5a..9864c7aa 100644 Binary files a/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/SUUpdatePermissionPrompt.nib and b/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/Sparkle.strings index 69853a3f..d3eddf75 100644 Binary files a/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/Sparkle.strings and b/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/ro.lproj/SUAutomaticUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/ro.lproj/SUAutomaticUpdateAlert.nib index f87479e6..eace82c8 100644 Binary files a/Sparkle.framework/Versions/A/Resources/ro.lproj/SUAutomaticUpdateAlert.nib and b/Sparkle.framework/Versions/A/Resources/ro.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/ro.lproj/SUUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/ro.lproj/SUUpdateAlert.nib index 607da620..e22df981 100644 Binary files a/Sparkle.framework/Versions/A/Resources/ro.lproj/SUUpdateAlert.nib and b/Sparkle.framework/Versions/A/Resources/ro.lproj/SUUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/ro.lproj/SUUpdatePermissionPrompt.nib b/Sparkle.framework/Versions/A/Resources/ro.lproj/SUUpdatePermissionPrompt.nib index d6f50899..fbb2a4bf 100644 Binary files a/Sparkle.framework/Versions/A/Resources/ro.lproj/SUUpdatePermissionPrompt.nib and b/Sparkle.framework/Versions/A/Resources/ro.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/ro.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/ro.lproj/Sparkle.strings index 3cd694d5..28a407bc 100644 Binary files a/Sparkle.framework/Versions/A/Resources/ro.lproj/Sparkle.strings and b/Sparkle.framework/Versions/A/Resources/ro.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/ru.lproj/SUAutomaticUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/ru.lproj/SUAutomaticUpdateAlert.nib index 422bce25..df2f8172 100644 Binary files a/Sparkle.framework/Versions/A/Resources/ru.lproj/SUAutomaticUpdateAlert.nib and b/Sparkle.framework/Versions/A/Resources/ru.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/ru.lproj/SUUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/ru.lproj/SUUpdateAlert.nib index a6b25376..1e69dbe4 100644 Binary files a/Sparkle.framework/Versions/A/Resources/ru.lproj/SUUpdateAlert.nib and b/Sparkle.framework/Versions/A/Resources/ru.lproj/SUUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/ru.lproj/SUUpdatePermissionPrompt.nib b/Sparkle.framework/Versions/A/Resources/ru.lproj/SUUpdatePermissionPrompt.nib index fdd831fe..b85d0617 100644 Binary files a/Sparkle.framework/Versions/A/Resources/ru.lproj/SUUpdatePermissionPrompt.nib and b/Sparkle.framework/Versions/A/Resources/ru.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/ru.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/ru.lproj/Sparkle.strings index 349f17fe..d5cb6073 100644 Binary files a/Sparkle.framework/Versions/A/Resources/ru.lproj/Sparkle.strings and b/Sparkle.framework/Versions/A/Resources/ru.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/sk.lproj/SUAutomaticUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/sk.lproj/SUAutomaticUpdateAlert.nib index 94488daf..c6aa9450 100644 Binary files a/Sparkle.framework/Versions/A/Resources/sk.lproj/SUAutomaticUpdateAlert.nib and b/Sparkle.framework/Versions/A/Resources/sk.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/sk.lproj/SUUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/sk.lproj/SUUpdateAlert.nib index ab314151..5ce8b5ff 100644 Binary files a/Sparkle.framework/Versions/A/Resources/sk.lproj/SUUpdateAlert.nib and b/Sparkle.framework/Versions/A/Resources/sk.lproj/SUUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/sk.lproj/SUUpdatePermissionPrompt.nib b/Sparkle.framework/Versions/A/Resources/sk.lproj/SUUpdatePermissionPrompt.nib index 03d5c26b..fc3a83ce 100644 Binary files a/Sparkle.framework/Versions/A/Resources/sk.lproj/SUUpdatePermissionPrompt.nib and b/Sparkle.framework/Versions/A/Resources/sk.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/sk.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/sk.lproj/Sparkle.strings index 38bb70dd..949fb16e 100644 Binary files a/Sparkle.framework/Versions/A/Resources/sk.lproj/Sparkle.strings and b/Sparkle.framework/Versions/A/Resources/sk.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/sl.lproj/SUAutomaticUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/sl.lproj/SUAutomaticUpdateAlert.nib index 6c2a9a6c..58d1b279 100644 Binary files a/Sparkle.framework/Versions/A/Resources/sl.lproj/SUAutomaticUpdateAlert.nib and b/Sparkle.framework/Versions/A/Resources/sl.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/sl.lproj/SUUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/sl.lproj/SUUpdateAlert.nib index 8efac437..b3ff8180 100644 Binary files a/Sparkle.framework/Versions/A/Resources/sl.lproj/SUUpdateAlert.nib and b/Sparkle.framework/Versions/A/Resources/sl.lproj/SUUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/sl.lproj/SUUpdatePermissionPrompt.nib b/Sparkle.framework/Versions/A/Resources/sl.lproj/SUUpdatePermissionPrompt.nib index bf9eccde..02738220 100644 Binary files a/Sparkle.framework/Versions/A/Resources/sl.lproj/SUUpdatePermissionPrompt.nib and b/Sparkle.framework/Versions/A/Resources/sl.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/sl.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/sl.lproj/Sparkle.strings index 889a6583..c1ce5a04 100644 Binary files a/Sparkle.framework/Versions/A/Resources/sl.lproj/Sparkle.strings and b/Sparkle.framework/Versions/A/Resources/sl.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/sv.lproj/SUAutomaticUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/sv.lproj/SUAutomaticUpdateAlert.nib index d3e0f38e..84a4996a 100644 Binary files a/Sparkle.framework/Versions/A/Resources/sv.lproj/SUAutomaticUpdateAlert.nib and b/Sparkle.framework/Versions/A/Resources/sv.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/sv.lproj/SUUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/sv.lproj/SUUpdateAlert.nib index e42a612c..a89378cb 100644 Binary files a/Sparkle.framework/Versions/A/Resources/sv.lproj/SUUpdateAlert.nib and b/Sparkle.framework/Versions/A/Resources/sv.lproj/SUUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/sv.lproj/SUUpdatePermissionPrompt.nib b/Sparkle.framework/Versions/A/Resources/sv.lproj/SUUpdatePermissionPrompt.nib index c5e0a9b7..d2abca1e 100644 Binary files a/Sparkle.framework/Versions/A/Resources/sv.lproj/SUUpdatePermissionPrompt.nib and b/Sparkle.framework/Versions/A/Resources/sv.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/sv.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/sv.lproj/Sparkle.strings index fbebf966..e65ac559 100644 Binary files a/Sparkle.framework/Versions/A/Resources/sv.lproj/Sparkle.strings and b/Sparkle.framework/Versions/A/Resources/sv.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/th.lproj/SUAutomaticUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/th.lproj/SUAutomaticUpdateAlert.nib index f8be0bad..f16caf04 100644 Binary files a/Sparkle.framework/Versions/A/Resources/th.lproj/SUAutomaticUpdateAlert.nib and b/Sparkle.framework/Versions/A/Resources/th.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/th.lproj/SUUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/th.lproj/SUUpdateAlert.nib index 51b92de6..31295fc3 100644 Binary files a/Sparkle.framework/Versions/A/Resources/th.lproj/SUUpdateAlert.nib and b/Sparkle.framework/Versions/A/Resources/th.lproj/SUUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/th.lproj/SUUpdatePermissionPrompt.nib b/Sparkle.framework/Versions/A/Resources/th.lproj/SUUpdatePermissionPrompt.nib index a02033da..6f575498 100644 Binary files a/Sparkle.framework/Versions/A/Resources/th.lproj/SUUpdatePermissionPrompt.nib and b/Sparkle.framework/Versions/A/Resources/th.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/th.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/th.lproj/Sparkle.strings index 323bb70f..fc728fd5 100644 Binary files a/Sparkle.framework/Versions/A/Resources/th.lproj/Sparkle.strings and b/Sparkle.framework/Versions/A/Resources/th.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/tr.lproj/SUAutomaticUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/tr.lproj/SUAutomaticUpdateAlert.nib index 8ef9fcf4..08c15cbe 100644 Binary files a/Sparkle.framework/Versions/A/Resources/tr.lproj/SUAutomaticUpdateAlert.nib and b/Sparkle.framework/Versions/A/Resources/tr.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/tr.lproj/SUUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/tr.lproj/SUUpdateAlert.nib index 6d4963c2..cc72ff88 100644 Binary files a/Sparkle.framework/Versions/A/Resources/tr.lproj/SUUpdateAlert.nib and b/Sparkle.framework/Versions/A/Resources/tr.lproj/SUUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/tr.lproj/SUUpdatePermissionPrompt.nib b/Sparkle.framework/Versions/A/Resources/tr.lproj/SUUpdatePermissionPrompt.nib index 79dabd93..aa2c54de 100644 Binary files a/Sparkle.framework/Versions/A/Resources/tr.lproj/SUUpdatePermissionPrompt.nib and b/Sparkle.framework/Versions/A/Resources/tr.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/tr.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/tr.lproj/Sparkle.strings index ccc268fd..c41e3dba 100644 Binary files a/Sparkle.framework/Versions/A/Resources/tr.lproj/Sparkle.strings and b/Sparkle.framework/Versions/A/Resources/tr.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/uk.lproj/SUAutomaticUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/uk.lproj/SUAutomaticUpdateAlert.nib index b75a0849..987d9150 100644 Binary files a/Sparkle.framework/Versions/A/Resources/uk.lproj/SUAutomaticUpdateAlert.nib and b/Sparkle.framework/Versions/A/Resources/uk.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/uk.lproj/SUUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/uk.lproj/SUUpdateAlert.nib index e122413b..1a77ccfc 100644 Binary files a/Sparkle.framework/Versions/A/Resources/uk.lproj/SUUpdateAlert.nib and b/Sparkle.framework/Versions/A/Resources/uk.lproj/SUUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/uk.lproj/SUUpdatePermissionPrompt.nib b/Sparkle.framework/Versions/A/Resources/uk.lproj/SUUpdatePermissionPrompt.nib index f569f0d5..bdce4621 100644 Binary files a/Sparkle.framework/Versions/A/Resources/uk.lproj/SUUpdatePermissionPrompt.nib and b/Sparkle.framework/Versions/A/Resources/uk.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/uk.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/uk.lproj/Sparkle.strings index c3f3d9e0..521656d3 100644 Binary files a/Sparkle.framework/Versions/A/Resources/uk.lproj/Sparkle.strings and b/Sparkle.framework/Versions/A/Resources/uk.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/SUAutomaticUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/SUAutomaticUpdateAlert.nib index 43c91131..d4e07287 100644 Binary files a/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/SUAutomaticUpdateAlert.nib and b/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/SUUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/SUUpdateAlert.nib index f0216d0a..0bf62863 100644 Binary files a/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/SUUpdateAlert.nib and b/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/SUUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/SUUpdatePermissionPrompt.nib b/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/SUUpdatePermissionPrompt.nib index d2aed6dd..b371b0df 100644 Binary files a/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/SUUpdatePermissionPrompt.nib and b/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/Sparkle.strings index 49b68ce1..0e91210e 100644 Binary files a/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/Sparkle.strings and b/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/SUAutomaticUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/SUAutomaticUpdateAlert.nib index 74dd51e6..e204c1a4 100644 Binary files a/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/SUAutomaticUpdateAlert.nib and b/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/SUUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/SUUpdateAlert.nib index aa182995..5f242053 100644 Binary files a/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/SUUpdateAlert.nib and b/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/SUUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/SUUpdatePermissionPrompt.nib b/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/SUUpdatePermissionPrompt.nib index 475bf56a..fb32ddc9 100644 Binary files a/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/SUUpdatePermissionPrompt.nib and b/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/Sparkle.strings index 17fcca0a..ea8c82f9 100644 Binary files a/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/Sparkle.strings and b/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Sparkle b/Sparkle.framework/Versions/A/Sparkle index 5398212b..158a2592 100755 Binary files a/Sparkle.framework/Versions/A/Sparkle and b/Sparkle.framework/Versions/A/Sparkle differ diff --git a/StreamServicePlugins/.DS_Store b/StreamServicePlugins/.DS_Store new file mode 100644 index 00000000..7aaf2a0b Binary files /dev/null and b/StreamServicePlugins/.DS_Store differ diff --git a/StreamServicePlugins/CSFileStreamServicePlugin/CSFileStreamServicePlugin.xcodeproj/project.pbxproj b/StreamServicePlugins/CSFileStreamServicePlugin/CSFileStreamServicePlugin.xcodeproj/project.pbxproj index cb048096..c8ea719e 100644 --- a/StreamServicePlugins/CSFileStreamServicePlugin/CSFileStreamServicePlugin.xcodeproj/project.pbxproj +++ b/StreamServicePlugins/CSFileStreamServicePlugin/CSFileStreamServicePlugin.xcodeproj/project.pbxproj @@ -12,6 +12,10 @@ 34AFC2E919B08C810007C07B /* FileStreamService.m in Sources */ = {isa = PBXBuildFile; fileRef = 34AFC2E819B08C810007C07B /* FileStreamService.m */; }; 34AFC2ED19B08CA80007C07B /* FileStreamServiceViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 34AFC2EB19B08CA80007C07B /* FileStreamServiceViewController.m */; }; 34AFC2EE19B08CA80007C07B /* FileStreamServiceViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 34AFC2EC19B08CA80007C07B /* FileStreamServiceViewController.xib */; }; + 34EA82211D3A3B6900928A06 /* CSFileStreamServiceFactory.m in Sources */ = {isa = PBXBuildFile; fileRef = 34EA82201D3A3B6900928A06 /* CSFileStreamServiceFactory.m */; }; + 34EA82241D3A3C4E00928A06 /* CSFileStreamRTMPService.m in Sources */ = {isa = PBXBuildFile; fileRef = 34EA82231D3A3C4E00928A06 /* CSFileStreamRTMPService.m */; }; + 34EA82281D3A3FF600928A06 /* CSFileStreamRTMPServiceViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 34EA82261D3A3FF600928A06 /* CSFileStreamRTMPServiceViewController.m */; }; + 34EA82291D3A3FF600928A06 /* CSFileStreamRTMPServiceViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 34EA82271D3A3FF600928A06 /* CSFileStreamRTMPServiceViewController.xib */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -33,6 +37,13 @@ 34D6580C19B343290012E32B /* CSCaptureSourceProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSCaptureSourceProtocol.h; path = PluginHeaders/CSCaptureSourceProtocol.h; sourceTree = ""; }; 34D6580D19B343290012E32B /* CSStreamServiceProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSStreamServiceProtocol.h; path = PluginHeaders/CSStreamServiceProtocol.h; sourceTree = ""; }; 34D6580E19B343290012E32B /* CSPluginFactoryProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSPluginFactoryProtocol.h; path = PluginHeaders/CSPluginFactoryProtocol.h; sourceTree = ""; }; + 34EA821F1D3A3B6900928A06 /* CSFileStreamServiceFactory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSFileStreamServiceFactory.h; sourceTree = ""; }; + 34EA82201D3A3B6900928A06 /* CSFileStreamServiceFactory.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSFileStreamServiceFactory.m; sourceTree = ""; }; + 34EA82221D3A3C4E00928A06 /* CSFileStreamRTMPService.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSFileStreamRTMPService.h; sourceTree = ""; }; + 34EA82231D3A3C4E00928A06 /* CSFileStreamRTMPService.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSFileStreamRTMPService.m; sourceTree = ""; }; + 34EA82251D3A3FF600928A06 /* CSFileStreamRTMPServiceViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSFileStreamRTMPServiceViewController.h; sourceTree = ""; }; + 34EA82261D3A3FF600928A06 /* CSFileStreamRTMPServiceViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSFileStreamRTMPServiceViewController.m; sourceTree = ""; }; + 34EA82271D3A3FF600928A06 /* CSFileStreamRTMPServiceViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = CSFileStreamRTMPServiceViewController.xib; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -93,6 +104,13 @@ 34AFC2EA19B08CA80007C07B /* FileStreamServiceViewController.h */, 34AFC2EB19B08CA80007C07B /* FileStreamServiceViewController.m */, 34AFC2EC19B08CA80007C07B /* FileStreamServiceViewController.xib */, + 34EA821F1D3A3B6900928A06 /* CSFileStreamServiceFactory.h */, + 34EA82201D3A3B6900928A06 /* CSFileStreamServiceFactory.m */, + 34EA82221D3A3C4E00928A06 /* CSFileStreamRTMPService.h */, + 34EA82231D3A3C4E00928A06 /* CSFileStreamRTMPService.m */, + 34EA82251D3A3FF600928A06 /* CSFileStreamRTMPServiceViewController.h */, + 34EA82261D3A3FF600928A06 /* CSFileStreamRTMPServiceViewController.m */, + 34EA82271D3A3FF600928A06 /* CSFileStreamRTMPServiceViewController.xib */, ); path = CSFileStreamServicePlugin; sourceTree = ""; @@ -171,6 +189,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 34EA82291D3A3FF600928A06 /* CSFileStreamRTMPServiceViewController.xib in Resources */, 34AFC2EE19B08CA80007C07B /* FileStreamServiceViewController.xib in Resources */, 34AFC2D919B08C050007C07B /* InfoPlist.strings in Resources */, ); @@ -184,7 +203,10 @@ buildActionMask = 2147483647; files = ( 34AFC2ED19B08CA80007C07B /* FileStreamServiceViewController.m in Sources */, + 34EA82281D3A3FF600928A06 /* CSFileStreamRTMPServiceViewController.m in Sources */, 34AFC2E919B08C810007C07B /* FileStreamService.m in Sources */, + 34EA82241D3A3C4E00928A06 /* CSFileStreamRTMPService.m in Sources */, + 34EA82211D3A3B6900928A06 /* CSFileStreamServiceFactory.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/StreamServicePlugins/CSFileStreamServicePlugin/CSFileStreamServicePlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/CSFileStreamServicePlugin.xcscheme b/StreamServicePlugins/CSFileStreamServicePlugin/CSFileStreamServicePlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/CSFileStreamServicePlugin.xcscheme index d8c37a05..9b5db013 100644 --- a/StreamServicePlugins/CSFileStreamServicePlugin/CSFileStreamServicePlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/CSFileStreamServicePlugin.xcscheme +++ b/StreamServicePlugins/CSFileStreamServicePlugin/CSFileStreamServicePlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/CSFileStreamServicePlugin.xcscheme @@ -1,6 +1,6 @@ +#import "CSStreamServiceProtocol.h" + +@interface CSFileStreamRTMPService : NSObject + + +@property bool isReady; +@property (strong) NSString *destinationURI; + + +-(NSViewController *)getConfigurationView; +-(NSString *)getServiceDestination; ++(NSString *)label; ++(NSString *)serviceDescription; ++(NSImage *)serviceImage; + + + +@end diff --git a/StreamServicePlugins/CSFileStreamServicePlugin/CSFileStreamServicePlugin/CSFileStreamRTMPService.m b/StreamServicePlugins/CSFileStreamServicePlugin/CSFileStreamServicePlugin/CSFileStreamRTMPService.m new file mode 100644 index 00000000..e1763131 --- /dev/null +++ b/StreamServicePlugins/CSFileStreamServicePlugin/CSFileStreamServicePlugin/CSFileStreamRTMPService.m @@ -0,0 +1,92 @@ +// +// CSFileStreamRTMPService.m +// CSFileStreamServicePlugin +// +// Created by Zakk on 7/16/16. +// Copyright © 2016 Zakk. All rights reserved. +// + +#import "CSFileStreamRTMPService.h" +#import "CSFileStreamRTMPServiceViewController.h" + + +@implementation CSFileStreamRTMPService + +-(instancetype) init +{ + if(self = [super init]) + { + self.isReady = YES; + } + return self; +} + + +-(void)encodeWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeObject:self.destinationURI forKey:@"destinationURI"]; +} + + +-(instancetype)initWithCoder:(NSCoder *)aDecoder +{ + if (self = [self init]) + { + self.destinationURI = [aDecoder decodeObjectForKey:@"destinationURI"]; + } + + return self; +} + + +-(NSViewController *)getConfigurationView +{ + + CSFileStreamRTMPServiceViewController *configViewController; + + configViewController = [[CSFileStreamRTMPServiceViewController alloc] initWithNibName:@"CSFileStreamRTMPServiceViewController" bundle:[NSBundle bundleForClass:self.class]]; + configViewController.serviceObj = self; + return configViewController; +} + + + +-(NSString *)getServiceFormat +{ + return @"FLV"; +} + + + +-(NSString *)getServiceDestination +{ + + return self.destinationURI; +} + + + ++(NSString *)label +{ + return @"RTMP/Network"; +} + + ++(NSString *)serviceDescription +{ + return @"RTMP/Network"; +} + ++(NSImage *)serviceImage +{ + return [NSImage imageNamed:NSImageNameNetwork]; +} + + +-(void)prepareForStreamStart +{ + return; +} + + +@end diff --git a/StreamServicePlugins/CSFileStreamServicePlugin/CSFileStreamServicePlugin/CSFileStreamRTMPServiceViewController.h b/StreamServicePlugins/CSFileStreamServicePlugin/CSFileStreamServicePlugin/CSFileStreamRTMPServiceViewController.h new file mode 100644 index 00000000..9979cd48 --- /dev/null +++ b/StreamServicePlugins/CSFileStreamServicePlugin/CSFileStreamServicePlugin/CSFileStreamRTMPServiceViewController.h @@ -0,0 +1,18 @@ +// +// CSFileStreamRTMPServiceViewController.h +// CSFileStreamServicePlugin +// +// Created by Zakk on 7/16/16. +// Copyright © 2016 Zakk. All rights reserved. +// + +#import +#import "CSFileStreamRTMPService.h" + +@interface CSFileStreamRTMPServiceViewController : NSViewController + +@property (weak) CSFileStreamRTMPService *serviceObj; +@property (strong) IBOutlet NSObjectController *fileStreamRTMPServiceController; + + +@end diff --git a/StreamServicePlugins/CSFileStreamServicePlugin/CSFileStreamServicePlugin/CSFileStreamRTMPServiceViewController.m b/StreamServicePlugins/CSFileStreamServicePlugin/CSFileStreamServicePlugin/CSFileStreamRTMPServiceViewController.m new file mode 100644 index 00000000..80d4654e --- /dev/null +++ b/StreamServicePlugins/CSFileStreamServicePlugin/CSFileStreamServicePlugin/CSFileStreamRTMPServiceViewController.m @@ -0,0 +1,22 @@ +// +// CSFileStreamRTMPServiceViewController.m +// CSFileStreamServicePlugin +// +// Created by Zakk on 7/16/16. +// Copyright © 2016 Zakk. All rights reserved. +// + +#import "CSFileStreamRTMPServiceViewController.h" + +@interface CSFileStreamRTMPServiceViewController () + +@end + +@implementation CSFileStreamRTMPServiceViewController + +- (void)viewDidLoad { + [super viewDidLoad]; + // Do view setup here. +} + +@end diff --git a/StreamServicePlugins/CSFileStreamServicePlugin/CSFileStreamServicePlugin/CSFileStreamRTMPServiceViewController.xib b/StreamServicePlugins/CSFileStreamServicePlugin/CSFileStreamServicePlugin/CSFileStreamRTMPServiceViewController.xib new file mode 100644 index 00000000..db9bb091 --- /dev/null +++ b/StreamServicePlugins/CSFileStreamServicePlugin/CSFileStreamServicePlugin/CSFileStreamRTMPServiceViewController.xib @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/StreamServicePlugins/CSFileStreamServicePlugin/CSFileStreamServicePlugin/CSFileStreamServiceFactory.h b/StreamServicePlugins/CSFileStreamServicePlugin/CSFileStreamServicePlugin/CSFileStreamServiceFactory.h new file mode 100644 index 00000000..0b5b9f54 --- /dev/null +++ b/StreamServicePlugins/CSFileStreamServicePlugin/CSFileStreamServicePlugin/CSFileStreamServiceFactory.h @@ -0,0 +1,20 @@ +// +// CSFileStreamServiceFactory.h +// CSFileStreamServicePlugin +// +// Created by Zakk on 7/16/16. +// Copyright © 2016 Zakk. All rights reserved. +// + +#import +#import "CSPluginFactoryProtocol.h" +#import "FileStreamService.h" +#import "CSFileStreamRTMPService.h" + + +@interface CSFileStreamServiceFactory : NSObject ++(NSArray *)captureSourceClasses; ++(NSArray *)streamServiceClasses; ++(NSArray *)extraPluginClasses; + +@end diff --git a/StreamServicePlugins/CSFileStreamServicePlugin/CSFileStreamServicePlugin/CSFileStreamServiceFactory.m b/StreamServicePlugins/CSFileStreamServicePlugin/CSFileStreamServicePlugin/CSFileStreamServiceFactory.m new file mode 100644 index 00000000..9377d20b --- /dev/null +++ b/StreamServicePlugins/CSFileStreamServicePlugin/CSFileStreamServicePlugin/CSFileStreamServiceFactory.m @@ -0,0 +1,29 @@ +// +// CSFileStreamServiceFactory.m +// CSFileStreamServicePlugin +// +// Created by Zakk on 7/16/16. +// Copyright © 2016 Zakk. All rights reserved. +// + +#import "CSFileStreamServiceFactory.h" + + +@implementation CSFileStreamServiceFactory ++(NSArray *)captureSourceClasses +{ + return @[[FileStreamService class], [CSFileStreamRTMPService class]];} + + ++(NSArray *)streamServiceClasses +{ + return nil; +} + ++(NSArray *)extraPluginClasses +{ + return nil; +} + + +@end diff --git a/StreamServicePlugins/CSFileStreamServicePlugin/CSFileStreamServicePlugin/CSFileStreamServicePlugin-Info.plist b/StreamServicePlugins/CSFileStreamServicePlugin/CSFileStreamServicePlugin/CSFileStreamServicePlugin-Info.plist index bceb9c0e..85ee6a3e 100644 --- a/StreamServicePlugins/CSFileStreamServicePlugin/CSFileStreamServicePlugin/CSFileStreamServicePlugin-Info.plist +++ b/StreamServicePlugins/CSFileStreamServicePlugin/CSFileStreamServicePlugin/CSFileStreamServicePlugin-Info.plist @@ -25,6 +25,6 @@ NSHumanReadableCopyright Copyright © 2014 Zakk. All rights reserved. NSPrincipalClass - FileStreamService + CSFileStreamServiceFactory diff --git a/StreamServicePlugins/CSFileStreamServicePlugin/CSFileStreamServicePlugin/FileStreamService.h b/StreamServicePlugins/CSFileStreamServicePlugin/CSFileStreamServicePlugin/FileStreamService.h index 6d412587..c397b97d 100644 --- a/StreamServicePlugins/CSFileStreamServicePlugin/CSFileStreamServicePlugin/FileStreamService.h +++ b/StreamServicePlugins/CSFileStreamServicePlugin/CSFileStreamServicePlugin/FileStreamService.h @@ -13,7 +13,10 @@ @property bool isReady; -@property (assign) NSString *fileName; +@property (strong) NSString *fileName; + +@property (assign) BOOL useTimestamp; +@property (assign) BOOL noClobber; @@ -21,6 +24,8 @@ -(NSString *)getServiceDestination; +(NSString *)label; +(NSString *)serviceDescription; ++(NSImage *)serviceImage; + @end diff --git a/StreamServicePlugins/CSFileStreamServicePlugin/CSFileStreamServicePlugin/FileStreamService.m b/StreamServicePlugins/CSFileStreamServicePlugin/CSFileStreamServicePlugin/FileStreamService.m index 5f5fe494..500e9eef 100644 --- a/StreamServicePlugins/CSFileStreamServicePlugin/CSFileStreamServicePlugin/FileStreamService.m +++ b/StreamServicePlugins/CSFileStreamServicePlugin/CSFileStreamServicePlugin/FileStreamService.m @@ -24,6 +24,27 @@ } +-(void)encodeWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeObject:self.fileName forKey:@"fileName"]; + [aCoder encodeBool:self.useTimestamp forKey:@"useTimestamp"]; + [aCoder encodeBool:self.noClobber forKey:@"noClobber"]; +} + + +-(instancetype)initWithCoder:(NSCoder *)aDecoder +{ + if (self = [self init]) + { + self.fileName = [aDecoder decodeObjectForKey:@"fileName"]; + self.noClobber = [aDecoder decodeBoolForKey:@"noClobber"]; + self.useTimestamp = [aDecoder decodeBoolForKey:@"useTimestamp"]; + } + + return self; +} + + -(NSViewController *)getConfigurationView { @@ -36,24 +57,65 @@ +-(NSString *)getServiceFormat +{ + return nil; +} + + -(NSString *)getServiceDestination { + NSString *useFilename = self.fileName; + NSString *pathExt = [self.fileName pathExtension]; + NSString *noExt = [useFilename stringByDeletingPathExtension]; + + if (self.useTimestamp) + { + NSDateFormatter *dFormat = [[NSDateFormatter alloc] init]; + dFormat.dateFormat = @"yyyyMMddHHmmss"; + NSString *dateStr = [dFormat stringFromDate:[NSDate date]]; + useFilename = [NSString stringWithFormat:@"%@-%@.%@", noExt, dateStr, pathExt]; + } - return self.fileName; + if (self.noClobber) + { + noExt = [useFilename stringByDeletingPathExtension]; + + NSFileManager *fManager = [[NSFileManager alloc] init]; + NSString *noExt = [useFilename stringByDeletingPathExtension]; + int fidx = 1; + while ([fManager fileExistsAtPath:useFilename]) + { + useFilename = [NSString stringWithFormat:@"%@-%d.%@", noExt, fidx, pathExt]; + fidx++; + } + } + return useFilename; } +(NSString *)label { - return @"File/RTMP"; + return @"File"; } +(NSString *)serviceDescription { - return @"File/RTMP"; + return @"File"; +} + ++(NSImage *)serviceImage +{ + return [NSImage imageNamed:NSImageNameFolder]; +} + + +-(void)prepareForStreamStart +{ + return; } @end diff --git a/StreamServicePlugins/CSFileStreamServicePlugin/CSFileStreamServicePlugin/FileStreamServiceViewController.h b/StreamServicePlugins/CSFileStreamServicePlugin/CSFileStreamServicePlugin/FileStreamServiceViewController.h index bf42a0c7..467553e4 100644 --- a/StreamServicePlugins/CSFileStreamServicePlugin/CSFileStreamServicePlugin/FileStreamServiceViewController.h +++ b/StreamServicePlugins/CSFileStreamServicePlugin/CSFileStreamServicePlugin/FileStreamServiceViewController.h @@ -14,4 +14,6 @@ @property (weak) FileStreamService *serviceObj; @property (strong) IBOutlet NSObjectController *fileStreamServiceController; +- (IBAction)chooseDestination:(id)sender; + @end diff --git a/StreamServicePlugins/CSFileStreamServicePlugin/CSFileStreamServicePlugin/FileStreamServiceViewController.m b/StreamServicePlugins/CSFileStreamServicePlugin/CSFileStreamServicePlugin/FileStreamServiceViewController.m index 26169866..5ceac3b2 100644 --- a/StreamServicePlugins/CSFileStreamServicePlugin/CSFileStreamServicePlugin/FileStreamServiceViewController.m +++ b/StreamServicePlugins/CSFileStreamServicePlugin/CSFileStreamServicePlugin/FileStreamServiceViewController.m @@ -23,4 +23,24 @@ return self; } + +- (IBAction)chooseDestination:(id)sender +{ + + NSOpenPanel *panel = [NSOpenPanel openPanel]; + panel.canChooseDirectories = YES; + panel.canCreateDirectories = YES; + panel.canChooseFiles = NO; + panel.allowsMultipleSelection = NO; + + [panel beginWithCompletionHandler:^(NSInteger result) { + if (result == NSFileHandlingPanelOKButton) + { + self.serviceObj.fileName = panel.URL.path; + } + + }]; + +} + @end diff --git a/StreamServicePlugins/CSFileStreamServicePlugin/CSFileStreamServicePlugin/FileStreamServiceViewController.xib b/StreamServicePlugins/CSFileStreamServicePlugin/CSFileStreamServicePlugin/FileStreamServiceViewController.xib index 4dacea37..cc9af50c 100644 --- a/StreamServicePlugins/CSFileStreamServicePlugin/CSFileStreamServicePlugin/FileStreamServiceViewController.xib +++ b/StreamServicePlugins/CSFileStreamServicePlugin/CSFileStreamServicePlugin/FileStreamServiceViewController.xib @@ -1,7 +1,8 @@ - + - + + @@ -11,23 +12,51 @@ - + - + + + - - + diff --git a/StreamServicePlugins/CSHitboxStreamServicePlugin/CSHitboxStreamServicePlugin.xcodeproj/project.pbxproj b/StreamServicePlugins/CSHitboxStreamServicePlugin/CSHitboxStreamServicePlugin.xcodeproj/project.pbxproj index 60dece40..074967d8 100644 --- a/StreamServicePlugins/CSHitboxStreamServicePlugin/CSHitboxStreamServicePlugin.xcodeproj/project.pbxproj +++ b/StreamServicePlugins/CSHitboxStreamServicePlugin/CSHitboxStreamServicePlugin.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 3473D29F1D4E51EA00842EEE /* Media.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 3473D29E1D4E51EA00842EEE /* Media.xcassets */; }; 34C67B861A2D7C490012DC1B /* HitboxStreamService.m in Sources */ = {isa = PBXBuildFile; fileRef = 34C67B851A2D7C490012DC1B /* HitboxStreamService.m */; }; 34C67B881A2D7DDB0012DC1B /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 34C67B871A2D7DDB0012DC1B /* Cocoa.framework */; }; 34C67B8C1A2D82250012DC1B /* HitboxStreamServiceViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 34C67B8A1A2D82250012DC1B /* HitboxStreamServiceViewController.m */; }; @@ -14,6 +15,7 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 3473D29E1D4E51EA00842EEE /* Media.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Media.xcassets; path = CSHitboxStreamServicePlugin/Media.xcassets; sourceTree = ""; }; 34C67B691A2D7B690012DC1B /* CSHitboxStreamServicePlugin.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CSHitboxStreamServicePlugin.bundle; sourceTree = BUILT_PRODUCTS_DIR; }; 34C67B6D1A2D7B690012DC1B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 34C67B791A2D7B760012DC1B /* CSPluginServices.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSPluginServices.h; sourceTree = ""; }; @@ -49,6 +51,7 @@ 34C67B601A2D7B690012DC1B = { isa = PBXGroup; children = ( + 3473D29E1D4E51EA00842EEE /* Media.xcassets */, 34C67B871A2D7DDB0012DC1B /* Cocoa.framework */, 34C67B831A2D7B760012DC1B /* PluginHeaders */, 34C67B6B1A2D7B690012DC1B /* CSHitboxStreamServicePlugin */, @@ -159,6 +162,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 3473D29F1D4E51EA00842EEE /* Media.xcassets in Resources */, 34C67B8D1A2D82250012DC1B /* HitboxStreamServiceViewController.xib in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -295,6 +299,7 @@ 34C67B721A2D7B690012DC1B /* Release */, ); defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; diff --git a/StreamServicePlugins/CSHitboxStreamServicePlugin/CSHitboxStreamServicePlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/CSHitboxStreamServicePlugin.xcscheme b/StreamServicePlugins/CSHitboxStreamServicePlugin/CSHitboxStreamServicePlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/CSHitboxStreamServicePlugin.xcscheme new file mode 100644 index 00000000..ae318348 --- /dev/null +++ b/StreamServicePlugins/CSHitboxStreamServicePlugin/CSHitboxStreamServicePlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/CSHitboxStreamServicePlugin.xcscheme @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/StreamServicePlugins/CSHitboxStreamServicePlugin/CSHitboxStreamServicePlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/xcschememanagement.plist b/StreamServicePlugins/CSHitboxStreamServicePlugin/CSHitboxStreamServicePlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 00000000..e255bc8d --- /dev/null +++ b/StreamServicePlugins/CSHitboxStreamServicePlugin/CSHitboxStreamServicePlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,22 @@ + + + + + SchemeUserState + + CSHitboxStreamServicePlugin.xcscheme + + orderHint + 19 + + + SuppressBuildableAutocreation + + 34C67B681A2D7B690012DC1B + + primary + + + + + diff --git a/StreamServicePlugins/CSHitboxStreamServicePlugin/CSHitboxStreamServicePlugin/HitboxStreamService.h b/StreamServicePlugins/CSHitboxStreamServicePlugin/CSHitboxStreamServicePlugin/HitboxStreamService.h index 887ae949..04ad33fc 100644 --- a/StreamServicePlugins/CSHitboxStreamServicePlugin/CSHitboxStreamServicePlugin/HitboxStreamService.h +++ b/StreamServicePlugins/CSHitboxStreamServicePlugin/CSHitboxStreamServicePlugin/HitboxStreamService.h @@ -30,6 +30,8 @@ +(NSString *)serviceDescription; -(void)authenticate:(NSString *)username password:(NSString *)password onComplete:(void(^)(void))callback; -(void)fetchIngestServers:(void(^)(void))callback; ++(NSImage *)serviceImage; + diff --git a/StreamServicePlugins/CSHitboxStreamServicePlugin/CSHitboxStreamServicePlugin/HitboxStreamService.m b/StreamServicePlugins/CSHitboxStreamServicePlugin/CSHitboxStreamServicePlugin/HitboxStreamService.m index 9e53543d..4a8fef45 100644 --- a/StreamServicePlugins/CSHitboxStreamServicePlugin/CSHitboxStreamServicePlugin/HitboxStreamService.m +++ b/StreamServicePlugins/CSHitboxStreamServicePlugin/CSHitboxStreamServicePlugin/HitboxStreamService.m @@ -32,6 +32,49 @@ return @"Hitbox"; } ++(NSImage *)serviceImage +{ + return [[NSBundle bundleForClass:[self class]] imageForResource:@"hitbox-icon-green"];; +} + + +/* +@property (strong) NSString *authKey; +@property (strong) NSArray *ingestServers; +@property (strong) NSString *authUsername; +@property (strong) NSString *streamKey; +@property (strong) NSString *streamPath; +@property (strong) NSString *selectedServer; +*/ + + +-(NSString *)getServiceFormat +{ + return @"FLV"; +} + + +-(void)encodeWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeObject:self.authUsername forKey:@"authUsername"]; + [aCoder encodeObject:self.streamKey forKey:@"streamKey"]; + [aCoder encodeObject:self.streamPath forKey:@"streamPath"]; + [aCoder encodeObject:self.selectedServer forKey:@"selectedServer"]; +} + + +-(instancetype)initWithCoder:(NSCoder *)aDecoder +{ + if (self = [self init]) + { + self.authUsername = [aDecoder decodeObjectForKey:@"authUsername"]; + self.streamKey = [aDecoder decodeObjectForKey:@"streamKey"]; + self.streamPath = [aDecoder decodeObjectForKey:@"streamPath"]; + self.selectedServer = [aDecoder decodeObjectForKey:@"selectedServer"]; + } + return self; +} + -(NSString *)getServiceDestination { @@ -112,5 +155,10 @@ }]; } +-(void)prepareForStreamStart +{ + return; +} + @end diff --git a/StreamServicePlugins/CSHitboxStreamServicePlugin/CSHitboxStreamServicePlugin/HitboxStreamServiceViewController.xib b/StreamServicePlugins/CSHitboxStreamServicePlugin/CSHitboxStreamServicePlugin/HitboxStreamServiceViewController.xib index 2b22314f..f94d5a0f 100644 --- a/StreamServicePlugins/CSHitboxStreamServicePlugin/CSHitboxStreamServicePlugin/HitboxStreamServiceViewController.xib +++ b/StreamServicePlugins/CSHitboxStreamServicePlugin/CSHitboxStreamServicePlugin/HitboxStreamServiceViewController.xib @@ -1,7 +1,7 @@ - + - + @@ -14,11 +14,11 @@ - + - + @@ -26,7 +26,7 @@ - + @@ -34,7 +34,7 @@ - + @@ -45,7 +45,7 @@ - + @@ -59,7 +59,7 @@ - + @@ -74,7 +74,7 @@ - - + + - + @@ -109,8 +109,8 @@ - + NSIsNil @@ -120,6 +120,7 @@ + diff --git a/StreamServicePlugins/CSHitboxStreamServicePlugin/CSHitboxStreamServicePlugin/Media.xcassets/Contents.json b/StreamServicePlugins/CSHitboxStreamServicePlugin/CSHitboxStreamServicePlugin/Media.xcassets/Contents.json new file mode 100644 index 00000000..da4a164c --- /dev/null +++ b/StreamServicePlugins/CSHitboxStreamServicePlugin/CSHitboxStreamServicePlugin/Media.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/StreamServicePlugins/CSHitboxStreamServicePlugin/CSHitboxStreamServicePlugin/Media.xcassets/hitbox-icon-green.imageset/Contents.json b/StreamServicePlugins/CSHitboxStreamServicePlugin/CSHitboxStreamServicePlugin/Media.xcassets/hitbox-icon-green.imageset/Contents.json new file mode 100644 index 00000000..c35596a6 --- /dev/null +++ b/StreamServicePlugins/CSHitboxStreamServicePlugin/CSHitboxStreamServicePlugin/Media.xcassets/hitbox-icon-green.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "hitbox-icon-green.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/StreamServicePlugins/CSHitboxStreamServicePlugin/CSHitboxStreamServicePlugin/Media.xcassets/hitbox-icon-green.imageset/hitbox-icon-green.png b/StreamServicePlugins/CSHitboxStreamServicePlugin/CSHitboxStreamServicePlugin/Media.xcassets/hitbox-icon-green.imageset/hitbox-icon-green.png new file mode 100644 index 00000000..8b9459d7 Binary files /dev/null and b/StreamServicePlugins/CSHitboxStreamServicePlugin/CSHitboxStreamServicePlugin/Media.xcassets/hitbox-icon-green.imageset/hitbox-icon-green.png differ diff --git a/StreamServicePlugins/CSTwitchStreamServicePlugin/.DS_Store b/StreamServicePlugins/CSTwitchStreamServicePlugin/.DS_Store new file mode 100644 index 00000000..eae99a04 Binary files /dev/null and b/StreamServicePlugins/CSTwitchStreamServicePlugin/.DS_Store differ diff --git a/StreamServicePlugins/CSTwitchStreamServicePlugin/CSTwitchStreamServicePlugin.xcodeproj/project.pbxproj b/StreamServicePlugins/CSTwitchStreamServicePlugin/CSTwitchStreamServicePlugin.xcodeproj/project.pbxproj index 678c1bf7..d0976775 100644 --- a/StreamServicePlugins/CSTwitchStreamServicePlugin/CSTwitchStreamServicePlugin.xcodeproj/project.pbxproj +++ b/StreamServicePlugins/CSTwitchStreamServicePlugin/CSTwitchStreamServicePlugin.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 3473D29D1D4E4B8C00842EEE /* Media.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 3473D29C1D4E4B8C00842EEE /* Media.xcassets */; }; 34AFC30219B0A5800007C07B /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 34AFC30119B0A5800007C07B /* Cocoa.framework */; }; 34AFC30C19B0A5800007C07B /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 34AFC30A19B0A5800007C07B /* InfoPlist.strings */; }; 34AFC32019B0A5DC0007C07B /* TwitchStreamService.m in Sources */ = {isa = PBXBuildFile; fileRef = 34AFC31F19B0A5DC0007C07B /* TwitchStreamService.m */; }; @@ -15,6 +16,7 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 3473D29C1D4E4B8C00842EEE /* Media.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Media.xcassets; path = CSTwitchStreamServicePlugin/Media.xcassets; sourceTree = ""; }; 34AFC2FE19B0A5800007C07B /* CSTwitchStreamServicePlugin.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CSTwitchStreamServicePlugin.bundle; sourceTree = BUILT_PRODUCTS_DIR; }; 34AFC30119B0A5800007C07B /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; 34AFC30419B0A5800007C07B /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; @@ -23,16 +25,26 @@ 34AFC30919B0A5800007C07B /* CSTwitchStreamServicePlugin-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "CSTwitchStreamServicePlugin-Info.plist"; sourceTree = ""; }; 34AFC30B19B0A5800007C07B /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 34AFC30D19B0A5800007C07B /* CSTwitchStreamServicePlugin-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "CSTwitchStreamServicePlugin-Prefix.pch"; sourceTree = ""; }; - 34AFC31E19B0A5DC0007C07B /* TwitchStreamService.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TwitchStreamService.h; sourceTree = ""; }; - 34AFC31F19B0A5DC0007C07B /* TwitchStreamService.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TwitchStreamService.m; sourceTree = ""; }; - 34AFC32119B0A5FD0007C07B /* TwitchStreamServiceViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TwitchStreamServiceViewController.h; sourceTree = ""; }; - 34AFC32219B0A5FD0007C07B /* TwitchStreamServiceViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TwitchStreamServiceViewController.m; sourceTree = ""; }; - 34AFC32319B0A5FD0007C07B /* TwitchStreamServiceViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = TwitchStreamServiceViewController.xib; sourceTree = ""; }; - 34D6580419B343210012E32B /* CSAbstractCaptureDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSAbstractCaptureDevice.h; path = PluginHeaders/CSAbstractCaptureDevice.h; sourceTree = ""; }; - 34D6580519B343210012E32B /* CSCaptureBase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSCaptureBase.h; path = PluginHeaders/CSCaptureBase.h; sourceTree = ""; }; - 34D6580619B343210012E32B /* CSCaptureSourceProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSCaptureSourceProtocol.h; path = PluginHeaders/CSCaptureSourceProtocol.h; sourceTree = ""; }; - 34D6580719B343210012E32B /* CSStreamServiceProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSStreamServiceProtocol.h; path = PluginHeaders/CSStreamServiceProtocol.h; sourceTree = ""; }; - 34D6580819B343210012E32B /* CSPluginFactoryProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSPluginFactoryProtocol.h; path = PluginHeaders/CSPluginFactoryProtocol.h; sourceTree = ""; }; + 34AFC31E19B0A5DC0007C07B /* TwitchStreamService.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TwitchStreamService.h; path = ../TwitchStreamService.h; sourceTree = ""; }; + 34AFC31F19B0A5DC0007C07B /* TwitchStreamService.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TwitchStreamService.m; path = ../TwitchStreamService.m; sourceTree = ""; }; + 34AFC32119B0A5FD0007C07B /* TwitchStreamServiceViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TwitchStreamServiceViewController.h; path = ../TwitchStreamServiceViewController.h; sourceTree = ""; }; + 34AFC32219B0A5FD0007C07B /* TwitchStreamServiceViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TwitchStreamServiceViewController.m; path = ../TwitchStreamServiceViewController.m; sourceTree = ""; }; + 34AFC32319B0A5FD0007C07B /* TwitchStreamServiceViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = TwitchStreamServiceViewController.xib; path = ../TwitchStreamServiceViewController.xib; sourceTree = ""; }; + 34EA822A1D3AC49600928A06 /* WebKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebKit.framework; path = System/Library/Frameworks/WebKit.framework; sourceTree = SDKROOT; }; + 34EA823E1D3CBD3600928A06 /* CSPluginServices.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSPluginServices.h; path = PluginHeaders/CSPluginServices.h; sourceTree = ""; }; + 34EA823F1D3CBD3600928A06 /* CSIOSurfaceLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSIOSurfaceLayer.h; path = PluginHeaders/CSIOSurfaceLayer.h; sourceTree = ""; }; + 34EA82401D3CBD3600928A06 /* CSPcmPlayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSPcmPlayer.h; path = PluginHeaders/CSPcmPlayer.h; sourceTree = ""; }; + 34EA82411D3CBD3600928A06 /* CAMultiAudioPCM.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CAMultiAudioPCM.h; path = PluginHeaders/CAMultiAudioPCM.h; sourceTree = ""; }; + 34EA82421D3CBD3600928A06 /* CSAbstractCaptureDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSAbstractCaptureDevice.h; path = PluginHeaders/CSAbstractCaptureDevice.h; sourceTree = ""; }; + 34EA82431D3CBD3600928A06 /* CSTextCaptureBase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSTextCaptureBase.h; path = PluginHeaders/CSTextCaptureBase.h; sourceTree = ""; }; + 34EA82441D3CBD3600928A06 /* CSTextCaptureViewControllerBase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSTextCaptureViewControllerBase.h; path = PluginHeaders/CSTextCaptureViewControllerBase.h; sourceTree = ""; }; + 34EA82451D3CBD3600928A06 /* CSCaptureBase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSCaptureBase.h; path = PluginHeaders/CSCaptureBase.h; sourceTree = ""; }; + 34EA82461D3CBD3600928A06 /* CSCaptureSourceProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSCaptureSourceProtocol.h; path = PluginHeaders/CSCaptureSourceProtocol.h; sourceTree = ""; }; + 34EA82471D3CBD3600928A06 /* CSStreamServiceProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSStreamServiceProtocol.h; path = PluginHeaders/CSStreamServiceProtocol.h; sourceTree = ""; }; + 34EA82481D3CBD3600928A06 /* CSPluginFactoryProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSPluginFactoryProtocol.h; path = PluginHeaders/CSPluginFactoryProtocol.h; sourceTree = ""; }; + 34EA82491D3CBD3600928A06 /* CSExtraPluginProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSExtraPluginProtocol.h; path = PluginHeaders/CSExtraPluginProtocol.h; sourceTree = ""; }; + 34EA824A1D3CBD3600928A06 /* CSOauth2Authenticator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSOauth2Authenticator.h; path = PluginHeaders/CSOauth2Authenticator.h; sourceTree = ""; }; + 34EA824B1D3CBD3600928A06 /* CSNotifications.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSNotifications.h; path = PluginHeaders/CSNotifications.h; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -50,12 +62,8 @@ 34AFC2F519B0A5800007C07B = { isa = PBXGroup; children = ( - 34D6580919B343210012E32B /* PluginHeaders */, - 34AFC32119B0A5FD0007C07B /* TwitchStreamServiceViewController.h */, - 34AFC32219B0A5FD0007C07B /* TwitchStreamServiceViewController.m */, - 34AFC32319B0A5FD0007C07B /* TwitchStreamServiceViewController.xib */, - 34AFC31E19B0A5DC0007C07B /* TwitchStreamService.h */, - 34AFC31F19B0A5DC0007C07B /* TwitchStreamService.m */, + 3473D29C1D4E4B8C00842EEE /* Media.xcassets */, + 34EA824C1D3CBD3600928A06 /* PluginHeaders */, 34AFC30719B0A5800007C07B /* CSTwitchStreamServicePlugin */, 34AFC30019B0A5800007C07B /* Frameworks */, 34AFC2FF19B0A5800007C07B /* Products */, @@ -73,6 +81,7 @@ 34AFC30019B0A5800007C07B /* Frameworks */ = { isa = PBXGroup; children = ( + 34EA822A1D3AC49600928A06 /* WebKit.framework */, 34AFC30119B0A5800007C07B /* Cocoa.framework */, 34AFC30319B0A5800007C07B /* Other Frameworks */, ); @@ -92,6 +101,11 @@ 34AFC30719B0A5800007C07B /* CSTwitchStreamServicePlugin */ = { isa = PBXGroup; children = ( + 34AFC32119B0A5FD0007C07B /* TwitchStreamServiceViewController.h */, + 34AFC32219B0A5FD0007C07B /* TwitchStreamServiceViewController.m */, + 34AFC32319B0A5FD0007C07B /* TwitchStreamServiceViewController.xib */, + 34AFC31E19B0A5DC0007C07B /* TwitchStreamService.h */, + 34AFC31F19B0A5DC0007C07B /* TwitchStreamService.m */, 34AFC30819B0A5800007C07B /* Supporting Files */, ); path = CSTwitchStreamServicePlugin; @@ -107,14 +121,23 @@ name = "Supporting Files"; sourceTree = ""; }; - 34D6580919B343210012E32B /* PluginHeaders */ = { + 34EA824C1D3CBD3600928A06 /* PluginHeaders */ = { isa = PBXGroup; children = ( - 34D6580419B343210012E32B /* CSAbstractCaptureDevice.h */, - 34D6580519B343210012E32B /* CSCaptureBase.h */, - 34D6580619B343210012E32B /* CSCaptureSourceProtocol.h */, - 34D6580719B343210012E32B /* CSStreamServiceProtocol.h */, - 34D6580819B343210012E32B /* CSPluginFactoryProtocol.h */, + 34EA823E1D3CBD3600928A06 /* CSPluginServices.h */, + 34EA823F1D3CBD3600928A06 /* CSIOSurfaceLayer.h */, + 34EA82401D3CBD3600928A06 /* CSPcmPlayer.h */, + 34EA82411D3CBD3600928A06 /* CAMultiAudioPCM.h */, + 34EA82421D3CBD3600928A06 /* CSAbstractCaptureDevice.h */, + 34EA82431D3CBD3600928A06 /* CSTextCaptureBase.h */, + 34EA82441D3CBD3600928A06 /* CSTextCaptureViewControllerBase.h */, + 34EA82451D3CBD3600928A06 /* CSCaptureBase.h */, + 34EA82461D3CBD3600928A06 /* CSCaptureSourceProtocol.h */, + 34EA82471D3CBD3600928A06 /* CSStreamServiceProtocol.h */, + 34EA82481D3CBD3600928A06 /* CSPluginFactoryProtocol.h */, + 34EA82491D3CBD3600928A06 /* CSExtraPluginProtocol.h */, + 34EA824A1D3CBD3600928A06 /* CSOauth2Authenticator.h */, + 34EA824B1D3CBD3600928A06 /* CSNotifications.h */, ); name = PluginHeaders; path = ../../CocoaSplit; @@ -172,6 +195,7 @@ buildActionMask = 2147483647; files = ( 34AFC30C19B0A5800007C07B /* InfoPlist.strings in Resources */, + 3473D29D1D4E4B8C00842EEE /* Media.xcassets in Resources */, 34AFC32519B0A5FD0007C07B /* TwitchStreamServiceViewController.xib in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -280,6 +304,11 @@ GCC_PREFIX_HEADER = "CSTwitchStreamServicePlugin/CSTwitchStreamServicePlugin-Prefix.pch"; INFOPLIST_FILE = "CSTwitchStreamServicePlugin/CSTwitchStreamServicePlugin-Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles"; + OTHER_LDFLAGS = ( + "-undefined", + suppress, + "-flat_namespace", + ); PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; WRAPPER_EXTENSION = bundle; @@ -294,6 +323,11 @@ GCC_PREFIX_HEADER = "CSTwitchStreamServicePlugin/CSTwitchStreamServicePlugin-Prefix.pch"; INFOPLIST_FILE = "CSTwitchStreamServicePlugin/CSTwitchStreamServicePlugin-Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles"; + OTHER_LDFLAGS = ( + "-undefined", + suppress, + "-flat_namespace", + ); PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; WRAPPER_EXTENSION = bundle; diff --git a/StreamServicePlugins/CSTwitchStreamServicePlugin/CSTwitchStreamServicePlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/CSTwitchStreamServicePlugin.xcscheme b/StreamServicePlugins/CSTwitchStreamServicePlugin/CSTwitchStreamServicePlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/CSTwitchStreamServicePlugin.xcscheme index fc98ec3a..b0d8e707 100644 --- a/StreamServicePlugins/CSTwitchStreamServicePlugin/CSTwitchStreamServicePlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/CSTwitchStreamServicePlugin.xcscheme +++ b/StreamServicePlugins/CSTwitchStreamServicePlugin/CSTwitchStreamServicePlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/CSTwitchStreamServicePlugin.xcscheme @@ -1,6 +1,6 @@ Copyright © 2014 Zakk. All rights reserved. NSPrincipalClass TwitchStreamService + TwitchAPIClientID + p2onxyxk17dlngdgtj43kl9gaj2yb2a diff --git a/StreamServicePlugins/CSTwitchStreamServicePlugin/CSTwitchStreamServicePlugin/Media.xcassets/Contents.json b/StreamServicePlugins/CSTwitchStreamServicePlugin/CSTwitchStreamServicePlugin/Media.xcassets/Contents.json new file mode 100644 index 00000000..da4a164c --- /dev/null +++ b/StreamServicePlugins/CSTwitchStreamServicePlugin/CSTwitchStreamServicePlugin/Media.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/StreamServicePlugins/CSTwitchStreamServicePlugin/CSTwitchStreamServicePlugin/Media.xcassets/GlitchIcon_PurpleonWhite.imageset/Contents.json b/StreamServicePlugins/CSTwitchStreamServicePlugin/CSTwitchStreamServicePlugin/Media.xcassets/GlitchIcon_PurpleonWhite.imageset/Contents.json new file mode 100644 index 00000000..2d523421 --- /dev/null +++ b/StreamServicePlugins/CSTwitchStreamServicePlugin/CSTwitchStreamServicePlugin/Media.xcassets/GlitchIcon_PurpleonWhite.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "GlitchIcon_PurpleonWhite.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/StreamServicePlugins/CSTwitchStreamServicePlugin/CSTwitchStreamServicePlugin/Media.xcassets/GlitchIcon_PurpleonWhite.imageset/GlitchIcon_PurpleonWhite.png b/StreamServicePlugins/CSTwitchStreamServicePlugin/CSTwitchStreamServicePlugin/Media.xcassets/GlitchIcon_PurpleonWhite.imageset/GlitchIcon_PurpleonWhite.png new file mode 100644 index 00000000..e21c2427 Binary files /dev/null and b/StreamServicePlugins/CSTwitchStreamServicePlugin/CSTwitchStreamServicePlugin/Media.xcassets/GlitchIcon_PurpleonWhite.imageset/GlitchIcon_PurpleonWhite.png differ diff --git a/StreamServicePlugins/CSTwitchStreamServicePlugin/TwitchStreamService.h b/StreamServicePlugins/CSTwitchStreamServicePlugin/TwitchStreamService.h index 2fd62ca5..2a06fe8e 100644 --- a/StreamServicePlugins/CSTwitchStreamServicePlugin/TwitchStreamService.h +++ b/StreamServicePlugins/CSTwitchStreamServicePlugin/TwitchStreamService.h @@ -8,9 +8,15 @@ #import #import "CSStreamServiceProtocol.h" +#import "CSPluginServices.h" +#import "CSOauth2Authenticator.h" @interface TwitchStreamService : NSObject +{ + NSString *_oauth_client_id; + bool _key_fetch_pending; +} @property bool isReady; @@ -18,13 +24,26 @@ @property (strong) NSArray *twitchServers; @property (strong) NSString *streamKey; @property (strong) NSString *selectedServer; +@property (strong) NSString *oAuthKey; +@property (strong) CSOauth2Authenticator *oauthObject; +@property (strong) NSString *accountName; +@property (assign) bool alwaysFetchKey; +@property (strong) NSArray *knownAccounts; + + + -(NSViewController *)getConfigurationView; -(NSString *)getServiceDestination; +-(void)fetchTwitchStreamKey; +(NSString *)label; +(NSString *)serviceDescription; +-(void)authenticateUser; ++(NSImage *)serviceImage; + + @end diff --git a/StreamServicePlugins/CSTwitchStreamServicePlugin/TwitchStreamService.m b/StreamServicePlugins/CSTwitchStreamServicePlugin/TwitchStreamService.m index e1297c8a..e950680c 100644 --- a/StreamServicePlugins/CSTwitchStreamServicePlugin/TwitchStreamService.m +++ b/StreamServicePlugins/CSTwitchStreamServicePlugin/TwitchStreamService.m @@ -12,11 +12,38 @@ @implementation TwitchStreamService +@synthesize accountName = _accountName; + -(instancetype) init { if(self = [super init]) { self.isReady = YES; + self.knownAccounts = [[CSPluginServices sharedPluginServices] accountNamesForService:@"twitch"]; + } + + + return self; +} + + +-(void)encodeWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeObject:self.selectedServer forKey:@"selectedServer"]; + [aCoder encodeObject:self.streamKey forKey:@"streamKey"]; + [aCoder encodeObject:self.accountName forKey:@"accountName"]; + [aCoder encodeBool:self.alwaysFetchKey forKey:@"alwaysFetchKey"]; + +} + +-(instancetype)initWithCoder:(NSCoder *)aDecoder +{ + if (self = [self init]) + { + self.selectedServer = [aDecoder decodeObjectForKey:@"selectedServer"]; + self.streamKey = [aDecoder decodeObjectForKey:@"streamKey"]; + self.accountName = [aDecoder decodeObjectForKey:@"accountName"]; + self.alwaysFetchKey = [aDecoder decodeBoolForKey:@"alwaysFetchKey"]; } return self; } @@ -35,11 +62,47 @@ +-(NSString *)getServiceFormat +{ + return @"FLV"; +} + + +-(NSString *)accountName +{ + return _accountName; +} + +-(void)setAccountName:(NSString *)accountName +{ + _accountName = accountName; + self.oauthObject = nil; +} + + +-(void)prepareForStreamStart +{ + if (self.alwaysFetchKey && !_key_fetch_pending) + { + @synchronized (self) { + self.streamKey = nil; + _key_fetch_pending = YES; + } + + [self fetchTwitchStreamKey]; + + } +} + -(NSString *)getServiceDestination { - + + if (!self.streamKey) + { + return nil; + } if (self.selectedServer) { @@ -54,15 +117,132 @@ +(NSString *)label { - return @"TwitchTV"; + return @"Twitch"; } +(NSString *)serviceDescription { - return @"TwitchTV"; + return @"Twitch"; } + ++(NSImage *)serviceImage +{ + NSImage *ret = [[NSBundle bundleForClass:[self class]] imageForResource:@"GlitchIcon_PurpleonWhite"]; + + return ret; +} + + + + +-(void)createAuthenticator +{ + if (!self.oauthObject) + { + + if (!_oauth_client_id) + { + _oauth_client_id = [[NSBundle bundleForClass:[self class]] objectForInfoDictionaryKey:@"TwitchAPIClientID"]; + } + + NSDictionary *oconfig = @{kCSOauth2ConfigAuthURL:@"https://api.twitch.tv/kraken/oauth2/authorize", kCSOauth2ConfigRedirectURL:@"cocoasplit-twitch://cocoasplit.com/oauth/redirect", kCSOauth2ConfigScopes:@[@"channel_read", @"user_read"]}; + + self.oauthObject = [[CSPluginServices sharedPluginServices] createOAuth2Authenticator:@"twitch" clientID:_oauth_client_id flowType:kCSOauth2ImplicitGrantFlow config:oconfig]; + + + self.oauthObject.accountName = self.accountName; + self.oauthObject.accountNameFetcher = ^void(CSOauth2Authenticator *authenticator) { + [self fetchAccountname:authenticator]; + }; + } +} + +-(void)authenticateUser +{ + self.accountName = nil; + + [self createAuthenticator]; + self.oauthObject.forceVerify = YES; + [self.oauthObject configurationVariableSet:@{@"force_verify": @"true"} forName:kCSOauth2ExtraAuthParams]; + + [self fetchTwitchStreamKey]; + self.oauthObject.forceVerify = NO; + [self.oauthObject configurationVariableRemove:kCSOauth2ExtraAuthParams]; + +} + + + + +-(void)fetchAccountname:(CSOauth2Authenticator *)authenticator +{ + if (self.accountName) + { + [authenticator saveToKeychain:self.accountName]; + + dispatch_async(dispatch_get_main_queue(), ^{ + + self.knownAccounts = [[CSPluginServices sharedPluginServices] accountNamesForService:@"twitch"]; + }); + + + return; + } + + if (self.oauthObject && self.oauthObject.accessToken) + { + NSString *apiString = @"https://api.twitch.tv/kraken/channel"; + + NSURL *apiURL = [NSURL URLWithString:apiString]; + + + + NSMutableURLRequest *apiRequest = [NSMutableURLRequest requestWithURL:apiURL]; + + [self.oauthObject jsonRequest:apiRequest completionHandler:^(id decodedData) { + + NSDictionary *user_response = (NSDictionary *)decodedData; + NSString *account_name = [user_response objectForKey:@"name"]; + [authenticator saveToKeychain:account_name]; + + dispatch_async(dispatch_get_main_queue(), ^{ + + self.accountName = account_name; + self.knownAccounts = [[CSPluginServices sharedPluginServices] accountNamesForService:@"twitch"]; + }); + }]; + } +} + + + +-(void)fetchTwitchStreamKey +{ + + [self createAuthenticator]; + + NSString *apiString = @"https://api.twitch.tv/kraken/channel"; + + NSURL *apiURL = [NSURL URLWithString:apiString]; + + + + NSMutableURLRequest *apiRequest = [NSMutableURLRequest requestWithURL:apiURL]; + + + //[apiRequest setValue:[NSString stringWithFormat:@"OAuth %@", self.oAuthKey] forHTTPHeaderField:@"Authorization"]; + + [self.oauthObject jsonRequest:apiRequest completionHandler:^(id decodedData) { + + NSDictionary *channel_response = (NSDictionary *)decodedData; + NSString *stream_key = [channel_response objectForKey:@"stream_key"]; + dispatch_async(dispatch_get_main_queue(), ^{self.streamKey = stream_key; _key_fetch_pending = NO;}); + }]; +} + + -(void)loadTwitchIngest { @@ -72,6 +252,12 @@ NSMutableURLRequest *apiRequest = [NSMutableURLRequest requestWithURL:apiURL]; + if (!_oauth_client_id) + { + _oauth_client_id = [[NSBundle bundleForClass:[self class]] objectForInfoDictionaryKey:@"TwitchAPIClientID"]; + } + + [apiRequest setValue:_oauth_client_id forHTTPHeaderField:@"Client-ID"]; [NSURLConnection sendAsynchronousRequest:apiRequest queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *err) { NSError *jsonError; diff --git a/StreamServicePlugins/CSTwitchStreamServicePlugin/TwitchStreamServiceViewController.h b/StreamServicePlugins/CSTwitchStreamServicePlugin/TwitchStreamServiceViewController.h index 93759496..34b8570d 100644 --- a/StreamServicePlugins/CSTwitchStreamServicePlugin/TwitchStreamServiceViewController.h +++ b/StreamServicePlugins/CSTwitchStreamServicePlugin/TwitchStreamServiceViewController.h @@ -9,10 +9,16 @@ #import #import "TwitchStreamService.h" +#import -@interface TwitchStreamServiceViewController : NSViewController +@interface TwitchStreamServiceViewController : NSViewController @property (weak) TwitchStreamService *serviceObj; +@property (strong) NSWindow *authWindow; +@property (strong) WebView *authWebView; +@property (strong) NSArray *serverSortDescriptors; +- (IBAction)doTwitchAuth:(id)sender; +- (IBAction)doTwitchstreamkey:(id)sender; @end diff --git a/StreamServicePlugins/CSTwitchStreamServicePlugin/TwitchStreamServiceViewController.m b/StreamServicePlugins/CSTwitchStreamServicePlugin/TwitchStreamServiceViewController.m index 4ede11ae..23392132 100644 --- a/StreamServicePlugins/CSTwitchStreamServicePlugin/TwitchStreamServiceViewController.m +++ b/StreamServicePlugins/CSTwitchStreamServicePlugin/TwitchStreamServiceViewController.m @@ -8,6 +8,7 @@ #import "TwitchStreamServiceViewController.h" + @interface TwitchStreamServiceViewController () @end @@ -19,8 +20,74 @@ self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; if (self) { // Initialization code here. + self.serverSortDescriptors = @[[[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES]]; + } return self; } + + +-(NSString *)extractAccessTokenFromURL:(NSURL *)url +{ + NSURLComponents *urlComp = [NSURLComponents componentsWithURL:url resolvingAgainstBaseURL:NO]; + NSString *urlFragment = urlComp.percentEncodedFragment; + + NSURLComponents *fakeComponents = [NSURLComponents componentsWithString:[NSString stringWithFormat:@"http://localhost/blah?%@", urlFragment]]; + + NSArray *queryVars = fakeComponents.queryItems; + + NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name=%@", @"access_token"]; + NSURLQueryItem *tokenItem = [queryVars filteredArrayUsingPredicate:predicate].firstObject; + + return tokenItem.value; +} + + +- (IBAction)doTwitchAuth:(id)sender +{ + + [self.serviceObj authenticateUser]; + +} + +- (IBAction)doTwitchstreamkey:(id)sender +{ + [self.serviceObj fetchTwitchStreamKey]; + +} + + +-(void)closeAuthWindow +{ + self.authWindow = nil; +} + + +-(void)receivedOAuth:(NSString *)oToken +{ + self.serviceObj.oAuthKey = oToken; + [self.serviceObj fetchTwitchStreamKey]; + +} + + +-(void)webView:(WebView *)webView decidePolicyForNavigationAction:(NSDictionary *)actionInformation request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id)listener +{ + NSURL *reqUrl = request.URL; + if (reqUrl) + { + if ([reqUrl.scheme isEqualToString:@"cocoasplit-twitch"]) + { + [listener ignore]; + NSString *accessToken = [self extractAccessTokenFromURL:reqUrl]; + [self receivedOAuth:accessToken]; + [self closeAuthWindow]; + + } + } + + [listener use]; +} + @end diff --git a/StreamServicePlugins/CSTwitchStreamServicePlugin/TwitchStreamServiceViewController.xib b/StreamServicePlugins/CSTwitchStreamServicePlugin/TwitchStreamServiceViewController.xib index 3b4a30dc..75ca7967 100644 --- a/StreamServicePlugins/CSTwitchStreamServicePlugin/TwitchStreamServiceViewController.xib +++ b/StreamServicePlugins/CSTwitchStreamServicePlugin/TwitchStreamServiceViewController.xib @@ -1,8 +1,8 @@ - + - + @@ -13,30 +13,30 @@ - + - - - + + + - - - + + + - - + + - + @@ -53,26 +53,84 @@ - - - + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + diff --git a/StreamServicePlugins/CSVaughnliveStreamServicePlugin/.DS_Store b/StreamServicePlugins/CSVaughnliveStreamServicePlugin/.DS_Store new file mode 100644 index 00000000..f24b294f Binary files /dev/null and b/StreamServicePlugins/CSVaughnliveStreamServicePlugin/.DS_Store differ diff --git a/StreamServicePlugins/CSVaughnliveStreamServicePlugin/CSVaughnliveStreamServicePlugin.xcodeproj/project.pbxproj b/StreamServicePlugins/CSVaughnliveStreamServicePlugin/CSVaughnliveStreamServicePlugin.xcodeproj/project.pbxproj index cc52e9e5..ffe1e163 100644 --- a/StreamServicePlugins/CSVaughnliveStreamServicePlugin/CSVaughnliveStreamServicePlugin.xcodeproj/project.pbxproj +++ b/StreamServicePlugins/CSVaughnliveStreamServicePlugin/CSVaughnliveStreamServicePlugin.xcodeproj/project.pbxproj @@ -6,7 +6,34 @@ objectVersion = 46; objects = { +/* Begin PBXBuildFile section */ + 3473D2841D4C34B500842EEE /* CSVaughnliveStreamService.m in Sources */ = {isa = PBXBuildFile; fileRef = 3473D2801D4C34B500842EEE /* CSVaughnliveStreamService.m */; }; + 3473D2851D4C34B500842EEE /* CSVaughnliveStreamServiceViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3473D2821D4C34B500842EEE /* CSVaughnliveStreamServiceViewController.m */; }; + 3473D2861D4C34B500842EEE /* CSVaughnliveStreamServiceViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 3473D2831D4C34B500842EEE /* CSVaughnliveStreamServiceViewController.xib */; }; + 3473D2A31D4E5C3900842EEE /* Media.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 3473D2A21D4E5C3900842EEE /* Media.xcassets */; }; +/* End PBXBuildFile section */ + /* Begin PBXFileReference section */ + 3473D27F1D4C34B500842EEE /* CSVaughnliveStreamService.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSVaughnliveStreamService.h; sourceTree = ""; }; + 3473D2801D4C34B500842EEE /* CSVaughnliveStreamService.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSVaughnliveStreamService.m; sourceTree = ""; }; + 3473D2811D4C34B500842EEE /* CSVaughnliveStreamServiceViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSVaughnliveStreamServiceViewController.h; sourceTree = ""; }; + 3473D2821D4C34B500842EEE /* CSVaughnliveStreamServiceViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSVaughnliveStreamServiceViewController.m; sourceTree = ""; }; + 3473D2831D4C34B500842EEE /* CSVaughnliveStreamServiceViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = CSVaughnliveStreamServiceViewController.xib; sourceTree = ""; }; + 3473D2881D4C35B900842EEE /* CSPluginServices.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSPluginServices.h; path = PluginHeaders/CSPluginServices.h; sourceTree = ""; }; + 3473D2891D4C35B900842EEE /* CSIOSurfaceLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSIOSurfaceLayer.h; path = PluginHeaders/CSIOSurfaceLayer.h; sourceTree = ""; }; + 3473D28A1D4C35B900842EEE /* CSPcmPlayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSPcmPlayer.h; path = PluginHeaders/CSPcmPlayer.h; sourceTree = ""; }; + 3473D28B1D4C35B900842EEE /* CAMultiAudioPCM.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CAMultiAudioPCM.h; path = PluginHeaders/CAMultiAudioPCM.h; sourceTree = ""; }; + 3473D28C1D4C35B900842EEE /* CSAbstractCaptureDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSAbstractCaptureDevice.h; path = PluginHeaders/CSAbstractCaptureDevice.h; sourceTree = ""; }; + 3473D28D1D4C35B900842EEE /* CSTextCaptureBase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSTextCaptureBase.h; path = PluginHeaders/CSTextCaptureBase.h; sourceTree = ""; }; + 3473D28E1D4C35B900842EEE /* CSTextCaptureViewControllerBase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSTextCaptureViewControllerBase.h; path = PluginHeaders/CSTextCaptureViewControllerBase.h; sourceTree = ""; }; + 3473D28F1D4C35B900842EEE /* CSCaptureBase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSCaptureBase.h; path = PluginHeaders/CSCaptureBase.h; sourceTree = ""; }; + 3473D2901D4C35B900842EEE /* CSCaptureSourceProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSCaptureSourceProtocol.h; path = PluginHeaders/CSCaptureSourceProtocol.h; sourceTree = ""; }; + 3473D2911D4C35B900842EEE /* CSStreamServiceProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSStreamServiceProtocol.h; path = PluginHeaders/CSStreamServiceProtocol.h; sourceTree = ""; }; + 3473D2921D4C35B900842EEE /* CSPluginFactoryProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSPluginFactoryProtocol.h; path = PluginHeaders/CSPluginFactoryProtocol.h; sourceTree = ""; }; + 3473D2931D4C35B900842EEE /* CSExtraPluginProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSExtraPluginProtocol.h; path = PluginHeaders/CSExtraPluginProtocol.h; sourceTree = ""; }; + 3473D2941D4C35B900842EEE /* CSOauth2Authenticator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSOauth2Authenticator.h; path = PluginHeaders/CSOauth2Authenticator.h; sourceTree = ""; }; + 3473D2951D4C35B900842EEE /* CSNotifications.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSNotifications.h; path = PluginHeaders/CSNotifications.h; sourceTree = ""; }; + 3473D2A21D4E5C3900842EEE /* Media.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Media.xcassets; path = CSVaughnliveStreamServicePlugin/Media.xcassets; sourceTree = ""; }; 34BD1E121B1B970C0015CBEC /* CSVaughnliveStreamServicePlugin.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CSVaughnliveStreamServicePlugin.bundle; sourceTree = BUILT_PRODUCTS_DIR; }; 34BD1E161B1B970C0015CBEC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; /* End PBXFileReference section */ @@ -22,9 +49,32 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 3473D2961D4C35B900842EEE /* PluginHeaders */ = { + isa = PBXGroup; + children = ( + 3473D2881D4C35B900842EEE /* CSPluginServices.h */, + 3473D2891D4C35B900842EEE /* CSIOSurfaceLayer.h */, + 3473D28A1D4C35B900842EEE /* CSPcmPlayer.h */, + 3473D28B1D4C35B900842EEE /* CAMultiAudioPCM.h */, + 3473D28C1D4C35B900842EEE /* CSAbstractCaptureDevice.h */, + 3473D28D1D4C35B900842EEE /* CSTextCaptureBase.h */, + 3473D28E1D4C35B900842EEE /* CSTextCaptureViewControllerBase.h */, + 3473D28F1D4C35B900842EEE /* CSCaptureBase.h */, + 3473D2901D4C35B900842EEE /* CSCaptureSourceProtocol.h */, + 3473D2911D4C35B900842EEE /* CSStreamServiceProtocol.h */, + 3473D2921D4C35B900842EEE /* CSPluginFactoryProtocol.h */, + 3473D2931D4C35B900842EEE /* CSExtraPluginProtocol.h */, + 3473D2941D4C35B900842EEE /* CSOauth2Authenticator.h */, + 3473D2951D4C35B900842EEE /* CSNotifications.h */, + ); + name = PluginHeaders; + path = ../../../CocoaSplit; + sourceTree = ""; + }; 34BD1E091B1B970C0015CBEC = { isa = PBXGroup; children = ( + 3473D2A21D4E5C3900842EEE /* Media.xcassets */, 34BD1E141B1B970C0015CBEC /* CSVaughnliveStreamServicePlugin */, 34BD1E131B1B970C0015CBEC /* Products */, ); @@ -41,6 +91,12 @@ 34BD1E141B1B970C0015CBEC /* CSVaughnliveStreamServicePlugin */ = { isa = PBXGroup; children = ( + 3473D2961D4C35B900842EEE /* PluginHeaders */, + 3473D27F1D4C34B500842EEE /* CSVaughnliveStreamService.h */, + 3473D2801D4C34B500842EEE /* CSVaughnliveStreamService.m */, + 3473D2811D4C34B500842EEE /* CSVaughnliveStreamServiceViewController.h */, + 3473D2821D4C34B500842EEE /* CSVaughnliveStreamServiceViewController.m */, + 3473D2831D4C34B500842EEE /* CSVaughnliveStreamServiceViewController.xib */, 34BD1E151B1B970C0015CBEC /* Supporting Files */, ); path = CSVaughnliveStreamServicePlugin; @@ -110,6 +166,8 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 3473D2A31D4E5C3900842EEE /* Media.xcassets in Resources */, + 3473D2861D4C34B500842EEE /* CSVaughnliveStreamServiceViewController.xib in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -120,6 +178,8 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 3473D2841D4C34B500842EEE /* CSVaughnliveStreamService.m in Sources */, + 3473D2851D4C34B500842EEE /* CSVaughnliveStreamServiceViewController.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -246,6 +306,7 @@ 34BD1E1B1B1B970C0015CBEC /* Release */, ); defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; diff --git a/StreamServicePlugins/CSVaughnliveStreamServicePlugin/CSVaughnliveStreamServicePlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/CSVaughnliveStreamServicePlugin.xcscheme b/StreamServicePlugins/CSVaughnliveStreamServicePlugin/CSVaughnliveStreamServicePlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/CSVaughnliveStreamServicePlugin.xcscheme new file mode 100644 index 00000000..b508c0b3 --- /dev/null +++ b/StreamServicePlugins/CSVaughnliveStreamServicePlugin/CSVaughnliveStreamServicePlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/CSVaughnliveStreamServicePlugin.xcscheme @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/StreamServicePlugins/CSVaughnliveStreamServicePlugin/CSVaughnliveStreamServicePlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/xcschememanagement.plist b/StreamServicePlugins/CSVaughnliveStreamServicePlugin/CSVaughnliveStreamServicePlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 00000000..1a7b4a02 --- /dev/null +++ b/StreamServicePlugins/CSVaughnliveStreamServicePlugin/CSVaughnliveStreamServicePlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,22 @@ + + + + + SchemeUserState + + CSVaughnliveStreamServicePlugin.xcscheme + + orderHint + 23 + + + SuppressBuildableAutocreation + + 34BD1E111B1B970C0015CBEC + + primary + + + + + diff --git a/StreamServicePlugins/CSVaughnliveStreamServicePlugin/CSVaughnliveStreamServicePlugin/.DS_Store b/StreamServicePlugins/CSVaughnliveStreamServicePlugin/CSVaughnliveStreamServicePlugin/.DS_Store new file mode 100644 index 00000000..c61982b2 Binary files /dev/null and b/StreamServicePlugins/CSVaughnliveStreamServicePlugin/CSVaughnliveStreamServicePlugin/.DS_Store differ diff --git a/StreamServicePlugins/CSVaughnliveStreamServicePlugin/CSVaughnliveStreamServicePlugin/CSVaughnliveStreamService.h b/StreamServicePlugins/CSVaughnliveStreamServicePlugin/CSVaughnliveStreamServicePlugin/CSVaughnliveStreamService.h index e04eec43..d661f108 100644 --- a/StreamServicePlugins/CSVaughnliveStreamServicePlugin/CSVaughnliveStreamServicePlugin/CSVaughnliveStreamService.h +++ b/StreamServicePlugins/CSVaughnliveStreamServicePlugin/CSVaughnliveStreamServicePlugin/CSVaughnliveStreamService.h @@ -27,6 +27,8 @@ -(NSString *)getServiceDestination; +(NSString *)label; +(NSString *)serviceDescription; ++(NSImage *)serviceImage; + diff --git a/StreamServicePlugins/CSVaughnliveStreamServicePlugin/CSVaughnliveStreamServicePlugin/CSVaughnliveStreamService.m b/StreamServicePlugins/CSVaughnliveStreamServicePlugin/CSVaughnliveStreamServicePlugin/CSVaughnliveStreamService.m index 365e19fd..f9fb36b8 100644 --- a/StreamServicePlugins/CSVaughnliveStreamServicePlugin/CSVaughnliveStreamServicePlugin/CSVaughnliveStreamService.m +++ b/StreamServicePlugins/CSVaughnliveStreamServicePlugin/CSVaughnliveStreamServicePlugin/CSVaughnliveStreamService.m @@ -45,6 +45,10 @@ return destination; } ++(NSImage *)serviceImage +{ + return [[NSBundle bundleForClass:[self class]] imageForResource:@"vaughnlive_icon"]; +} +(NSString *)label diff --git a/StreamServicePlugins/CSVaughnliveStreamServicePlugin/CSVaughnliveStreamServicePlugin/CSVaughnliveStreamServiceViewController.xib b/StreamServicePlugins/CSVaughnliveStreamServicePlugin/CSVaughnliveStreamServicePlugin/CSVaughnliveStreamServiceViewController.xib index 9a22ce36..dddc3b70 100644 --- a/StreamServicePlugins/CSVaughnliveStreamServicePlugin/CSVaughnliveStreamServicePlugin/CSVaughnliveStreamServiceViewController.xib +++ b/StreamServicePlugins/CSVaughnliveStreamServicePlugin/CSVaughnliveStreamServicePlugin/CSVaughnliveStreamServiceViewController.xib @@ -1,7 +1,7 @@ - + - + diff --git a/StreamServicePlugins/CSVaughnliveStreamServicePlugin/CSVaughnliveStreamServicePlugin/Media.xcassets/Contents.json b/StreamServicePlugins/CSVaughnliveStreamServicePlugin/CSVaughnliveStreamServicePlugin/Media.xcassets/Contents.json new file mode 100644 index 00000000..da4a164c --- /dev/null +++ b/StreamServicePlugins/CSVaughnliveStreamServicePlugin/CSVaughnliveStreamServicePlugin/Media.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/StreamServicePlugins/CSVaughnliveStreamServicePlugin/CSVaughnliveStreamServicePlugin/Media.xcassets/vaughnlive_icon.imageset/Contents.json b/StreamServicePlugins/CSVaughnliveStreamServicePlugin/CSVaughnliveStreamServicePlugin/Media.xcassets/vaughnlive_icon.imageset/Contents.json new file mode 100644 index 00000000..6a3fb411 --- /dev/null +++ b/StreamServicePlugins/CSVaughnliveStreamServicePlugin/CSVaughnliveStreamServicePlugin/Media.xcassets/vaughnlive_icon.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "vaughnlive_icon.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/StreamServicePlugins/CSVaughnliveStreamServicePlugin/CSVaughnliveStreamServicePlugin/Media.xcassets/vaughnlive_icon.imageset/vaughnlive_icon.png b/StreamServicePlugins/CSVaughnliveStreamServicePlugin/CSVaughnliveStreamServicePlugin/Media.xcassets/vaughnlive_icon.imageset/vaughnlive_icon.png new file mode 100644 index 00000000..3ef03923 Binary files /dev/null and b/StreamServicePlugins/CSVaughnliveStreamServicePlugin/CSVaughnliveStreamServicePlugin/Media.xcassets/vaughnlive_icon.imageset/vaughnlive_icon.png differ diff --git a/StreamServicePlugins/CSYoutubeStreamServicePlugin/CSYoutubeStreamServicePlugin.xcodeproj/project.pbxproj b/StreamServicePlugins/CSYoutubeStreamServicePlugin/CSYoutubeStreamServicePlugin.xcodeproj/project.pbxproj new file mode 100644 index 00000000..0f217f72 --- /dev/null +++ b/StreamServicePlugins/CSYoutubeStreamServicePlugin/CSYoutubeStreamServicePlugin.xcodeproj/project.pbxproj @@ -0,0 +1,322 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 340C80271D44F5AD00B2FD7D /* CSYoutubeStreamService.m in Sources */ = {isa = PBXBuildFile; fileRef = 340C80261D44F5AD00B2FD7D /* CSYoutubeStreamService.m */; }; + 340C802B1D44F6DB00B2FD7D /* CSYoutubeStreamServiceViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 340C80291D44F6DB00B2FD7D /* CSYoutubeStreamServiceViewController.m */; }; + 340C802C1D44F6DB00B2FD7D /* CSYoutubeStreamServiceViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 340C802A1D44F6DB00B2FD7D /* CSYoutubeStreamServiceViewController.xib */; }; + 3473D2A11D4E562400842EEE /* Media.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 3473D2A01D4E562400842EEE /* Media.xcassets */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 340C80071D44722000B2FD7D /* CSYoutubeStreamServicePlugin.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CSYoutubeStreamServicePlugin.bundle; sourceTree = BUILT_PRODUCTS_DIR; }; + 340C800A1D44722000B2FD7D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 340C80161D44742900B2FD7D /* CSPluginServices.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSPluginServices.h; path = PluginHeaders/CSPluginServices.h; sourceTree = ""; }; + 340C80171D44742900B2FD7D /* CSIOSurfaceLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSIOSurfaceLayer.h; path = PluginHeaders/CSIOSurfaceLayer.h; sourceTree = ""; }; + 340C80181D44742900B2FD7D /* CSPcmPlayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSPcmPlayer.h; path = PluginHeaders/CSPcmPlayer.h; sourceTree = ""; }; + 340C80191D44742900B2FD7D /* CAMultiAudioPCM.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CAMultiAudioPCM.h; path = PluginHeaders/CAMultiAudioPCM.h; sourceTree = ""; }; + 340C801A1D44742900B2FD7D /* CSAbstractCaptureDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSAbstractCaptureDevice.h; path = PluginHeaders/CSAbstractCaptureDevice.h; sourceTree = ""; }; + 340C801B1D44742900B2FD7D /* CSTextCaptureBase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSTextCaptureBase.h; path = PluginHeaders/CSTextCaptureBase.h; sourceTree = ""; }; + 340C801C1D44742900B2FD7D /* CSTextCaptureViewControllerBase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSTextCaptureViewControllerBase.h; path = PluginHeaders/CSTextCaptureViewControllerBase.h; sourceTree = ""; }; + 340C801D1D44742900B2FD7D /* CSCaptureBase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSCaptureBase.h; path = PluginHeaders/CSCaptureBase.h; sourceTree = ""; }; + 340C801E1D44742900B2FD7D /* CSCaptureSourceProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSCaptureSourceProtocol.h; path = PluginHeaders/CSCaptureSourceProtocol.h; sourceTree = ""; }; + 340C801F1D44742900B2FD7D /* CSStreamServiceProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSStreamServiceProtocol.h; path = PluginHeaders/CSStreamServiceProtocol.h; sourceTree = ""; }; + 340C80201D44742900B2FD7D /* CSPluginFactoryProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSPluginFactoryProtocol.h; path = PluginHeaders/CSPluginFactoryProtocol.h; sourceTree = ""; }; + 340C80211D44742900B2FD7D /* CSExtraPluginProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSExtraPluginProtocol.h; path = PluginHeaders/CSExtraPluginProtocol.h; sourceTree = ""; }; + 340C80221D44742900B2FD7D /* CSOauth2Authenticator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSOauth2Authenticator.h; path = PluginHeaders/CSOauth2Authenticator.h; sourceTree = ""; }; + 340C80231D44742900B2FD7D /* CSNotifications.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSNotifications.h; path = PluginHeaders/CSNotifications.h; sourceTree = ""; }; + 340C80251D44F5AD00B2FD7D /* CSYoutubeStreamService.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSYoutubeStreamService.h; sourceTree = ""; }; + 340C80261D44F5AD00B2FD7D /* CSYoutubeStreamService.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSYoutubeStreamService.m; sourceTree = ""; }; + 340C80281D44F6DB00B2FD7D /* CSYoutubeStreamServiceViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSYoutubeStreamServiceViewController.h; sourceTree = ""; }; + 340C80291D44F6DB00B2FD7D /* CSYoutubeStreamServiceViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSYoutubeStreamServiceViewController.m; sourceTree = ""; }; + 340C802A1D44F6DB00B2FD7D /* CSYoutubeStreamServiceViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = CSYoutubeStreamServiceViewController.xib; sourceTree = ""; }; + 3473D2A01D4E562400842EEE /* Media.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Media.xcassets; path = CSYoutubeStreamServicePlugin/Media.xcassets; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 340C80041D44722000B2FD7D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 340C7FFE1D44722000B2FD7D = { + isa = PBXGroup; + children = ( + 3473D2A01D4E562400842EEE /* Media.xcassets */, + 340C80241D44742900B2FD7D /* PluginHeaders */, + 340C80091D44722000B2FD7D /* CSYoutubeStreamServicePlugin */, + 340C80081D44722000B2FD7D /* Products */, + ); + sourceTree = ""; + }; + 340C80081D44722000B2FD7D /* Products */ = { + isa = PBXGroup; + children = ( + 340C80071D44722000B2FD7D /* CSYoutubeStreamServicePlugin.bundle */, + ); + name = Products; + sourceTree = ""; + }; + 340C80091D44722000B2FD7D /* CSYoutubeStreamServicePlugin */ = { + isa = PBXGroup; + children = ( + 340C800A1D44722000B2FD7D /* Info.plist */, + 340C80251D44F5AD00B2FD7D /* CSYoutubeStreamService.h */, + 340C80261D44F5AD00B2FD7D /* CSYoutubeStreamService.m */, + 340C80281D44F6DB00B2FD7D /* CSYoutubeStreamServiceViewController.h */, + 340C80291D44F6DB00B2FD7D /* CSYoutubeStreamServiceViewController.m */, + 340C802A1D44F6DB00B2FD7D /* CSYoutubeStreamServiceViewController.xib */, + ); + path = CSYoutubeStreamServicePlugin; + sourceTree = ""; + }; + 340C80241D44742900B2FD7D /* PluginHeaders */ = { + isa = PBXGroup; + children = ( + 340C80161D44742900B2FD7D /* CSPluginServices.h */, + 340C80171D44742900B2FD7D /* CSIOSurfaceLayer.h */, + 340C80181D44742900B2FD7D /* CSPcmPlayer.h */, + 340C80191D44742900B2FD7D /* CAMultiAudioPCM.h */, + 340C801A1D44742900B2FD7D /* CSAbstractCaptureDevice.h */, + 340C801B1D44742900B2FD7D /* CSTextCaptureBase.h */, + 340C801C1D44742900B2FD7D /* CSTextCaptureViewControllerBase.h */, + 340C801D1D44742900B2FD7D /* CSCaptureBase.h */, + 340C801E1D44742900B2FD7D /* CSCaptureSourceProtocol.h */, + 340C801F1D44742900B2FD7D /* CSStreamServiceProtocol.h */, + 340C80201D44742900B2FD7D /* CSPluginFactoryProtocol.h */, + 340C80211D44742900B2FD7D /* CSExtraPluginProtocol.h */, + 340C80221D44742900B2FD7D /* CSOauth2Authenticator.h */, + 340C80231D44742900B2FD7D /* CSNotifications.h */, + ); + name = PluginHeaders; + path = ../../CocoaSplit; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 340C80061D44722000B2FD7D /* CSYoutubeStreamServicePlugin */ = { + isa = PBXNativeTarget; + buildConfigurationList = 340C800D1D44722000B2FD7D /* Build configuration list for PBXNativeTarget "CSYoutubeStreamServicePlugin" */; + buildPhases = ( + 340C80031D44722000B2FD7D /* Sources */, + 340C80041D44722000B2FD7D /* Frameworks */, + 340C80051D44722000B2FD7D /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = CSYoutubeStreamServicePlugin; + productName = CSYoutubeStreamServicePlugin; + productReference = 340C80071D44722000B2FD7D /* CSYoutubeStreamServicePlugin.bundle */; + productType = "com.apple.product-type.bundle"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 340C7FFF1D44722000B2FD7D /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0730; + ORGANIZATIONNAME = Zakk; + TargetAttributes = { + 340C80061D44722000B2FD7D = { + CreatedOnToolsVersion = 7.3.1; + }; + }; + }; + buildConfigurationList = 340C80021D44722000B2FD7D /* Build configuration list for PBXProject "CSYoutubeStreamServicePlugin" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 340C7FFE1D44722000B2FD7D; + productRefGroup = 340C80081D44722000B2FD7D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 340C80061D44722000B2FD7D /* CSYoutubeStreamServicePlugin */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 340C80051D44722000B2FD7D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 3473D2A11D4E562400842EEE /* Media.xcassets in Resources */, + 340C802C1D44F6DB00B2FD7D /* CSYoutubeStreamServiceViewController.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 340C80031D44722000B2FD7D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 340C802B1D44F6DB00B2FD7D /* CSYoutubeStreamServiceViewController.m in Sources */, + 340C80271D44F5AD00B2FD7D /* CSYoutubeStreamService.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 340C800B1D44722000B2FD7D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + }; + name = Debug; + }; + 340C800C1D44722000B2FD7D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + }; + name = Release; + }; + 340C800E1D44722000B2FD7D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = CSYoutubeStreamServicePlugin/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles"; + OTHER_LDFLAGS = ( + "-undefined", + suppress, + "-flat_namespace", + ); + PRODUCT_BUNDLE_IDENTIFIER = zakk.lol.CSYoutubeStreamServicePlugin; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + WRAPPER_EXTENSION = bundle; + }; + name = Debug; + }; + 340C800F1D44722000B2FD7D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = CSYoutubeStreamServicePlugin/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles"; + OTHER_LDFLAGS = ( + "-undefined", + suppress, + "-flat_namespace", + ); + PRODUCT_BUNDLE_IDENTIFIER = zakk.lol.CSYoutubeStreamServicePlugin; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + WRAPPER_EXTENSION = bundle; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 340C80021D44722000B2FD7D /* Build configuration list for PBXProject "CSYoutubeStreamServicePlugin" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 340C800B1D44722000B2FD7D /* Debug */, + 340C800C1D44722000B2FD7D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 340C800D1D44722000B2FD7D /* Build configuration list for PBXNativeTarget "CSYoutubeStreamServicePlugin" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 340C800E1D44722000B2FD7D /* Debug */, + 340C800F1D44722000B2FD7D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 340C7FFF1D44722000B2FD7D /* Project object */; +} diff --git a/StreamServicePlugins/CSYoutubeStreamServicePlugin/CSYoutubeStreamServicePlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/CSYoutubeStreamServicePlugin.xcscheme b/StreamServicePlugins/CSYoutubeStreamServicePlugin/CSYoutubeStreamServicePlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/CSYoutubeStreamServicePlugin.xcscheme new file mode 100644 index 00000000..ff138dbe --- /dev/null +++ b/StreamServicePlugins/CSYoutubeStreamServicePlugin/CSYoutubeStreamServicePlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/CSYoutubeStreamServicePlugin.xcscheme @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/StreamServicePlugins/CSYoutubeStreamServicePlugin/CSYoutubeStreamServicePlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/xcschememanagement.plist b/StreamServicePlugins/CSYoutubeStreamServicePlugin/CSYoutubeStreamServicePlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 00000000..688c6ff8 --- /dev/null +++ b/StreamServicePlugins/CSYoutubeStreamServicePlugin/CSYoutubeStreamServicePlugin.xcodeproj/xcuserdata/zakk.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,22 @@ + + + + + SchemeUserState + + CSYoutubeStreamServicePlugin.xcscheme + + orderHint + 25 + + + SuppressBuildableAutocreation + + 340C80061D44722000B2FD7D + + primary + + + + + diff --git a/StreamServicePlugins/CSYoutubeStreamServicePlugin/CSYoutubeStreamServicePlugin/CSYoutubeStreamService.h b/StreamServicePlugins/CSYoutubeStreamServicePlugin/CSYoutubeStreamServicePlugin/CSYoutubeStreamService.h new file mode 100644 index 00000000..0eccb309 --- /dev/null +++ b/StreamServicePlugins/CSYoutubeStreamServicePlugin/CSYoutubeStreamServicePlugin/CSYoutubeStreamService.h @@ -0,0 +1,41 @@ +// +// CSYoutubeStreamService.h +// CSYoutubeStreamServicePlugin +// +// Created by Zakk on 7/24/16. +// Copyright © 2016 Zakk. All rights reserved. +// + +#import +#import "CSStreamServiceProtocol.h" +#import "CSPluginServices.h" +#import "CSOauth2Authenticator.h" + +@interface CSYoutubeStreamService : NSObject +{ + NSString *_currentStreamDest; + bool _destination_fetch_pending; + NSString *_saved_selected_streamID; + NSString *_oauth_client_id; + NSString *_oauth_client_secret; + +} + + + +-(NSViewController *)getConfigurationView; +-(NSString *)getServiceDestination; ++(NSString *)label; ++(NSString *)serviceDescription; +-(void)authenticateUser; ++(NSImage *)serviceImage; + + + +@property (strong) NSString *accountName; +@property (strong) CSOauth2Authenticator *oauthObject; +@property (strong) NSArray *liveStreams; +@property (strong) NSDictionary *selectedLiveStream; +@property (strong) NSArray *knownAccounts; + +@end diff --git a/StreamServicePlugins/CSYoutubeStreamServicePlugin/CSYoutubeStreamServicePlugin/CSYoutubeStreamService.m b/StreamServicePlugins/CSYoutubeStreamServicePlugin/CSYoutubeStreamServicePlugin/CSYoutubeStreamService.m new file mode 100644 index 00000000..4045f11c --- /dev/null +++ b/StreamServicePlugins/CSYoutubeStreamServicePlugin/CSYoutubeStreamServicePlugin/CSYoutubeStreamService.m @@ -0,0 +1,349 @@ +// +// CSYoutubeStreamService.m +// CSYoutubeStreamServicePlugin +// +// Created by Zakk on 7/24/16. +// Copyright © 2016 Zakk. All rights reserved. +// + +#import "CSYoutubeStreamService.h" +#import "CSYoutubeStreamServiceViewController.h" + +@implementation CSYoutubeStreamService + +@synthesize accountName = _accountName; + + +-(instancetype) init +{ + if(self = [super init]) + { + self.knownAccounts = [[CSPluginServices sharedPluginServices] accountNamesForService:@"youtube"]; + if (self.knownAccounts.count == 1) + { + self.accountName = self.knownAccounts.firstObject; + } + } + + + return self; +} + + +-(void)encodeWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeObject:self.accountName forKey:@"accountName"]; + if (self.selectedLiveStream) + { + NSString *streamID = self.selectedLiveStream[@"id"]; + [aCoder encodeObject:streamID forKey:@"selectedStreamID"]; + } +} + +-(instancetype)initWithCoder:(NSCoder *)aDecoder +{ + if (self = [self init]) + { + self.accountName = [aDecoder decodeObjectForKey:@"accountName"]; + _saved_selected_streamID = [aDecoder decodeObjectForKey:@"selectedStreamID"]; + + if (self.accountName) + { + [self fetchliveStreams]; + } + } + return self; +} + + + +-(NSString *)accountName +{ + return _accountName; +} + +-(void)setAccountName:(NSString *)accountName +{ + _accountName = accountName; + self.oauthObject = nil; + [self fetchliveStreams]; +} + + ++(NSString *)label +{ + return @"Youtube"; +} + + ++(NSString *)serviceDescription +{ + return @"Youtube"; +} + ++(NSImage *)serviceImage +{ + + + return [[NSBundle bundleForClass:[self class]] imageForResource:@"YouTube-icon-full_color"]; +} + + + + +-(void)prepareForStreamStart +{ + if (!_destination_fetch_pending) + { + @synchronized (self) { + _currentStreamDest = nil; + _destination_fetch_pending = YES; + } + + [self fetchStreamDestination]; + + } +} + +-(NSString *)getServiceDestination +{ + return _currentStreamDest; +} + +-(void)fetchStreamDestination +{ + if (!self.selectedLiveStream) + { + return; + } + + [self createAuthenticator]; + + if (!self.oauthObject) + { + return; + } + + + NSString *boundStreamID = self.selectedLiveStream[@"contentDetails"][@"boundStreamId"]; + + if (!boundStreamID) + { + return; + } + + NSString *apiString = [NSString stringWithFormat:@"https://www.googleapis.com/youtube/v3/liveStreams?part=cdn&id=%@&fields=items", boundStreamID]; + NSURL *apiURL = [NSURL URLWithString:apiString]; + + NSMutableURLRequest *apiRequest = [NSMutableURLRequest requestWithURL:apiURL]; + + [self.oauthObject jsonRequest:apiRequest completionHandler:^(id decodedData) { + + NSDictionary *streamResponse = (NSDictionary *)decodedData; + NSArray *items = streamResponse[@"items"]; + if (!items || items.count < 1) + { + return; + } + + NSDictionary *streamObj = items.firstObject; + + NSDictionary *cdnMap = streamObj[@"cdn"]; + + if (!cdnMap) + { + return; + } + + NSDictionary *ingestion = cdnMap[@"ingestionInfo"]; + + NSString *streamKey = ingestion[@"streamName"]; + NSString *streamAddress = ingestion[@"ingestionAddress"]; + + + if (streamKey && streamAddress) + { + _currentStreamDest = [NSString stringWithFormat:@"%@/%@", streamAddress, streamKey]; + _destination_fetch_pending = NO; + } + + + //dispatch_async(dispatch_get_main_queue(), ^{self.streamKey = stream_key; _key_fetch_pending = NO;}); + }]; + +} + +-(NSString *)getServiceFormat +{ + return @"FLV"; +} + +-(void)selectStream +{ + NSString *useID = _saved_selected_streamID; + NSDictionary *selectedStream = nil; + + _saved_selected_streamID = nil; + + if (self.selectedLiveStream) + { + useID = self.selectedLiveStream[@"id"]; + } + + for(NSDictionary *stream in self.liveStreams) + { + if (useID) + { + if ([stream[@"id"] isEqualToString:useID]) + { + selectedStream = stream; + break; + } + } + + NSNumber *defaultBroadcast = stream[@"snippet"][@"isDefaultBroadcast"]; + + if (defaultBroadcast.boolValue) + { + selectedStream = stream; + } + } + + self.selectedLiveStream = selectedStream; +} + + +-(void)fetchliveStreams +{ + [self createAuthenticator]; + + if (!self.oauthObject) + { + return; + } + + NSString *apiString = @"https://www.googleapis.com/youtube/v3/liveBroadcasts?part=id%2Csnippet%2CcontentDetails%2Cstatus&broadcastStatus=upcoming&broadcastType=all&fields=items(contentDetails%2FboundStreamId%2Cid%2Csnippet(description%2CisDefaultBroadcast%2Ctitle)%2Cstatus%2FlifeCycleStatus)"; + + NSURL *apiURL = [NSURL URLWithString:apiString]; + + + NSMutableURLRequest *apiRequest = [NSMutableURLRequest requestWithURL:apiURL]; + + + + + [self.oauthObject jsonRequest:apiRequest completionHandler:^(id decodedData) { + + NSDictionary *livestream_response = (NSDictionary *)decodedData; + + dispatch_async(dispatch_get_main_queue(), ^{ + self.liveStreams = livestream_response[@"items"]; + [self selectStream]; + }); + + //dispatch_async(dispatch_get_main_queue(), ^{self.streamKey = stream_key; _key_fetch_pending = NO;}); + }]; +} + + +-(void)fetchAccountname:(CSOauth2Authenticator *)authenticator +{ + if (self.accountName) + { + [authenticator saveToKeychain:self.accountName]; + dispatch_async(dispatch_get_main_queue(), ^{ + + self.knownAccounts = [[CSPluginServices sharedPluginServices] accountNamesForService:@"twitch"]; + [self fetchliveStreams]; + }); + + return; + } + + if (self.oauthObject && self.oauthObject.accessToken) + { + NSString *apiString = @"https://www.googleapis.com/oauth2/v2/userinfo"; + + NSURL *apiURL = [NSURL URLWithString:apiString]; + + + + NSMutableURLRequest *apiRequest = [NSMutableURLRequest requestWithURL:apiURL]; + + [self.oauthObject jsonRequest:apiRequest completionHandler:^(id decodedData) { + + NSDictionary *user_response = (NSDictionary *)decodedData; + NSString *account_name = [user_response objectForKey:@"email"]; + [authenticator saveToKeychain:account_name]; + + dispatch_async(dispatch_get_main_queue(), ^{ + + self.accountName = account_name; + self.knownAccounts = [[CSPluginServices sharedPluginServices] accountNamesForService:@"twitch"]; + //[self fetchliveStreams]; + }); + }]; + } +} + + +-(void)authenticateUser +{ + [self createAuthenticator]; + + self.oauthObject.forceVerify = YES; + + [self fetchliveStreams]; + + self.oauthObject.forceVerify = NO; + + + +} + + +-(void)createAuthenticator +{ + if (!self.oauthObject) + { + + if (!_oauth_client_id) + { + _oauth_client_id = [[NSBundle bundleForClass:[self class]] objectForInfoDictionaryKey:@"YoutubeClientID"]; + } + + if (!_oauth_client_secret) + { + _oauth_client_secret = [[NSBundle bundleForClass:[self class]] objectForInfoDictionaryKey:@"YoutubeClientSecret"]; + } + + + + + + NSDictionary *oconfig = @{kCSOauth2ConfigAuthURL:@"https://accounts.google.com/o/oauth2/v2/auth", kCSOauth2ConfigRedirectURL:@"https://localhost:5555/", kCSOauth2ConfigScopes:@[@"profile", @"https://www.googleapis.com/auth/youtube.readonly", @"https://www.googleapis.com/auth/userinfo.email"], kCSOauth2AccessTokenRequestURL: @"https://www.googleapis.com/oauth2/v4/token", kCSOauth2AccessRefreshURL: @"https://www.googleapis.com/oauth2/v4/token", kCSOauth2ClientSecret: _oauth_client_secret}; + + self.oauthObject = [[CSPluginServices sharedPluginServices] createOAuth2Authenticator:@"youtube" clientID:_oauth_client_id flowType:kCSOauth2CodeFlow config:oconfig]; + + + self.oauthObject.accountName = self.accountName; + self.oauthObject.accountNameFetcher = ^void(CSOauth2Authenticator *authenticator) { + [self fetchAccountname:authenticator]; + }; + } +} + + +-(NSViewController *)getConfigurationView +{ + + CSYoutubeStreamServiceViewController *configViewController; + + + configViewController = [[CSYoutubeStreamServiceViewController alloc] initWithNibName:@"CSYoutubeStreamServiceViewController" bundle:[NSBundle bundleForClass:self.class]]; + + configViewController.serviceObj = self; + return configViewController; +} + +@end diff --git a/StreamServicePlugins/CSYoutubeStreamServicePlugin/CSYoutubeStreamServicePlugin/CSYoutubeStreamServiceViewController.h b/StreamServicePlugins/CSYoutubeStreamServicePlugin/CSYoutubeStreamServicePlugin/CSYoutubeStreamServiceViewController.h new file mode 100644 index 00000000..20854e41 --- /dev/null +++ b/StreamServicePlugins/CSYoutubeStreamServicePlugin/CSYoutubeStreamServicePlugin/CSYoutubeStreamServiceViewController.h @@ -0,0 +1,17 @@ +// +// CSYoutubeStreamServiceViewController.h +// CSYoutubeStreamServicePlugin +// +// Created by Zakk on 7/24/16. +// Copyright © 2016 Zakk. All rights reserved. +// + +#import +#import "CSYoutubeStreamService.h" + +@interface CSYoutubeStreamServiceViewController : NSViewController + +@property (weak) CSYoutubeStreamService *serviceObj; +- (IBAction)authenticateUser:(id)sender; + +@end diff --git a/StreamServicePlugins/CSYoutubeStreamServicePlugin/CSYoutubeStreamServicePlugin/CSYoutubeStreamServiceViewController.m b/StreamServicePlugins/CSYoutubeStreamServicePlugin/CSYoutubeStreamServicePlugin/CSYoutubeStreamServiceViewController.m new file mode 100644 index 00000000..1bb23fed --- /dev/null +++ b/StreamServicePlugins/CSYoutubeStreamServicePlugin/CSYoutubeStreamServicePlugin/CSYoutubeStreamServiceViewController.m @@ -0,0 +1,41 @@ +// +// CSYoutubeStreamServiceViewController.m +// CSYoutubeStreamServicePlugin +// +// Created by Zakk on 7/24/16. +// Copyright © 2016 Zakk. All rights reserved. +// + +#import "CSYoutubeStreamServiceViewController.h" +#import "CSPluginServices.h" + +@interface CSYoutubeStreamServiceViewController () + +@end + +@implementation CSYoutubeStreamServiceViewController + + +- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil +{ + self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; + if (self) { + + // Initialization code here. + } + return self; +} + + +- (void)viewDidLoad { + [super viewDidLoad]; + // Do view setup here. +} + +- (IBAction)authenticateUser:(id)sender +{ + [self.serviceObj authenticateUser]; +} + + +@end diff --git a/StreamServicePlugins/CSYoutubeStreamServicePlugin/CSYoutubeStreamServicePlugin/CSYoutubeStreamServiceViewController.xib b/StreamServicePlugins/CSYoutubeStreamServicePlugin/CSYoutubeStreamServicePlugin/CSYoutubeStreamServiceViewController.xib new file mode 100644 index 00000000..1d8c77a5 --- /dev/null +++ b/StreamServicePlugins/CSYoutubeStreamServicePlugin/CSYoutubeStreamServicePlugin/CSYoutubeStreamServiceViewController.xib @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/StreamServicePlugins/CSYoutubeStreamServicePlugin/CSYoutubeStreamServicePlugin/Info.plist b/StreamServicePlugins/CSYoutubeStreamServicePlugin/CSYoutubeStreamServicePlugin/Info.plist new file mode 100644 index 00000000..615726dc --- /dev/null +++ b/StreamServicePlugins/CSYoutubeStreamServicePlugin/CSYoutubeStreamServicePlugin/Info.plist @@ -0,0 +1,32 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + NSHumanReadableCopyright + Copyright © 2016 Zakk. All rights reserved. + NSPrincipalClass + CSYoutubeStreamService + YoutubeClientID + 997237227687-ubgm910gsivg6g0v00qgm0sgngq1o0oe.apps.googleusercontent.com + YoutubeClientSecret + xvgH6PQpQkVDCfxNGHvkJiim + + diff --git a/StreamServicePlugins/CSYoutubeStreamServicePlugin/CSYoutubeStreamServicePlugin/Media.xcassets/Contents.json b/StreamServicePlugins/CSYoutubeStreamServicePlugin/CSYoutubeStreamServicePlugin/Media.xcassets/Contents.json new file mode 100644 index 00000000..da4a164c --- /dev/null +++ b/StreamServicePlugins/CSYoutubeStreamServicePlugin/CSYoutubeStreamServicePlugin/Media.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/StreamServicePlugins/CSYoutubeStreamServicePlugin/CSYoutubeStreamServicePlugin/Media.xcassets/YouTube-icon-full_color.imageset/Contents.json b/StreamServicePlugins/CSYoutubeStreamServicePlugin/CSYoutubeStreamServicePlugin/Media.xcassets/YouTube-icon-full_color.imageset/Contents.json new file mode 100644 index 00000000..c7d8267c --- /dev/null +++ b/StreamServicePlugins/CSYoutubeStreamServicePlugin/CSYoutubeStreamServicePlugin/Media.xcassets/YouTube-icon-full_color.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "YouTube-icon-full_color.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/StreamServicePlugins/CSYoutubeStreamServicePlugin/CSYoutubeStreamServicePlugin/Media.xcassets/YouTube-icon-full_color.imageset/YouTube-icon-full_color.png b/StreamServicePlugins/CSYoutubeStreamServicePlugin/CSYoutubeStreamServicePlugin/Media.xcassets/YouTube-icon-full_color.imageset/YouTube-icon-full_color.png new file mode 100644 index 00000000..5322e65a Binary files /dev/null and b/StreamServicePlugins/CSYoutubeStreamServicePlugin/CSYoutubeStreamServicePlugin/Media.xcassets/YouTube-icon-full_color.imageset/YouTube-icon-full_color.png differ