From 5f93114819aeec605baedd635b5a32b2995e24e5 Mon Sep 17 00:00:00 2001 From: Nolan Brown Date: Tue, 11 Aug 2020 17:39:09 -0700 Subject: [PATCH] Initial commit --- .gitignore | 97 ++ Frameworks/.gitkeep | 0 Imago.xcodeproj/project.pbxproj | 1176 +++++++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/xcschemes/Imago.xcscheme | 98 ++ Imago/AppDelegate.swift | 64 + .../AppIcon.appiconset/Asset 9@1024.png | Bin 0 -> 118351 bytes .../AppIcon.appiconset/Asset 9@128.png | Bin 0 -> 11687 bytes .../AppIcon.appiconset/Asset 9@16.png | Bin 0 -> 541 bytes .../AppIcon.appiconset/Asset 9@256-1.png | Bin 0 -> 24558 bytes .../AppIcon.appiconset/Asset 9@256.png | Bin 0 -> 24558 bytes .../AppIcon.appiconset/Asset 9@32-1.png | Bin 0 -> 950 bytes .../AppIcon.appiconset/Asset 9@32.png | Bin 0 -> 950 bytes .../AppIcon.appiconset/Asset 9@512-1.png | Bin 0 -> 56873 bytes .../AppIcon.appiconset/Asset 9@512.png | Bin 0 -> 56873 bytes .../AppIcon.appiconset/Asset 9@64.png | Bin 0 -> 5709 bytes .../AppIcon.appiconset/Contents.json | 68 + .../CameraIcon.imageset/Asset 7.png | Bin 0 -> 1335 bytes .../CameraIcon.imageset/Asset 7@2x.png | Bin 0 -> 2596 bytes .../CameraIcon.imageset/Asset 7@3x.png | Bin 0 -> 3884 bytes .../CameraIcon.imageset/Contents.json | 23 + Imago/Assets/Assets.xcassets/Contents.json | 6 + .../ReloadIcon.imageset/Bold-L.png | Bin 0 -> 1938 bytes .../ReloadIcon.imageset/Bold-L@2x.png | Bin 0 -> 4114 bytes .../ReloadIcon.imageset/Bold-L@3x.png | Bin 0 -> 6281 bytes .../ReloadIcon.imageset/Contents.json | 23 + Imago/Assets/Base.lproj/Main.storyboard | 683 ++++++++++ Imago/Assets/Imago.entitlements | 5 + Imago/Camera/Camera.swift | 83 ++ Imago/Camera/CameraFilter.swift | 77 ++ Imago/Camera/Canon/CanonCamera.swift | 556 ++++++++ Imago/Camera/Canon/CanonCommandErrors.swift | 146 ++ Imago/Camera/Canon/CanonCommandProperty.swift | 176 +++ Imago/Client.swift | 141 ++ Imago/Conductor.swift | 348 +++++ Imago/Extensions/CIImage+Filter.swift | 62 + Imago/Extensions/Frame+Decompress.swift | 65 + Imago/Headers/Imago-Bridging-Header.h | 23 + Imago/Headers/Imago-PrefixHeader.pch | 17 + Imago/Info.plist | 38 + .../Preview Assets.xcassets/Contents.json | 6 + Imago/Server.swift | 184 +++ Imago/Stream.swift | 138 ++ Imago/Tests/ImagoTests/ImagoTests.swift | 34 + Imago/Tests/ImagoTests/Info.plist | 22 + Imago/Tests/ImagoUITests/ImagoUITests.swift | 43 + Imago/Tests/ImagoUITests/Info.plist | 22 + Imago/ViewController.swift | 179 +++ Imago/Views/CameraFilterMenu.swift | 65 + Imago/Views/CameraRow.swift | 61 + Imago/Views/ContentView.swift | 30 + Imago/Views/LivePreview.swift | 45 + Imago/Views/TitlebarAccesory.swift | 27 + LICENSE | 47 + Plugin/Device.swift | 45 + Plugin/Headers/ImagoPlugin-Bridging-Header.h | 14 + Plugin/Headers/PrefixHeader.pch | 22 + Plugin/Info.plist | 42 + Plugin/LICENSE | 22 + Plugin/Main.swift | 14 + Plugin/Object.swift | 55 + Plugin/Plugin.swift | 18 + Plugin/PluginInterface.swift | 279 ++++ Plugin/Property.swift | 155 +++ Plugin/Stream.swift | 179 +++ README.md | 120 ++ Shared/Const.swift | 22 + Shared/DLog.swift | 25 + Shared/Extensions/CIImage+Imago.swift | 49 + Shared/Extensions/CVPixelBuffer+Data.swift | 129 ++ .../Extensions/CVPixelBufferPool+Imago.swift | 25 + Shared/Frame.swift | 286 ++++ Shared/IPC/CallerID.swift | 148 +++ Shared/IPC/PubSub.swift | 560 ++++++++ Shared/ImagoStream.swift | 90 ++ Shared/RepeatingTimer.swift | 76 ++ setup.sh | 165 +++ 78 files changed, 7433 insertions(+) create mode 100644 .gitignore create mode 100644 Frameworks/.gitkeep create mode 100644 Imago.xcodeproj/project.pbxproj create mode 100644 Imago.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 Imago.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 Imago.xcodeproj/xcshareddata/xcschemes/Imago.xcscheme create mode 100644 Imago/AppDelegate.swift create mode 100644 Imago/Assets/Assets.xcassets/AppIcon.appiconset/Asset 9@1024.png create mode 100644 Imago/Assets/Assets.xcassets/AppIcon.appiconset/Asset 9@128.png create mode 100644 Imago/Assets/Assets.xcassets/AppIcon.appiconset/Asset 9@16.png create mode 100644 Imago/Assets/Assets.xcassets/AppIcon.appiconset/Asset 9@256-1.png create mode 100644 Imago/Assets/Assets.xcassets/AppIcon.appiconset/Asset 9@256.png create mode 100644 Imago/Assets/Assets.xcassets/AppIcon.appiconset/Asset 9@32-1.png create mode 100644 Imago/Assets/Assets.xcassets/AppIcon.appiconset/Asset 9@32.png create mode 100644 Imago/Assets/Assets.xcassets/AppIcon.appiconset/Asset 9@512-1.png create mode 100644 Imago/Assets/Assets.xcassets/AppIcon.appiconset/Asset 9@512.png create mode 100644 Imago/Assets/Assets.xcassets/AppIcon.appiconset/Asset 9@64.png create mode 100644 Imago/Assets/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 Imago/Assets/Assets.xcassets/CameraIcon.imageset/Asset 7.png create mode 100644 Imago/Assets/Assets.xcassets/CameraIcon.imageset/Asset 7@2x.png create mode 100644 Imago/Assets/Assets.xcassets/CameraIcon.imageset/Asset 7@3x.png create mode 100644 Imago/Assets/Assets.xcassets/CameraIcon.imageset/Contents.json create mode 100644 Imago/Assets/Assets.xcassets/Contents.json create mode 100644 Imago/Assets/Assets.xcassets/ReloadIcon.imageset/Bold-L.png create mode 100644 Imago/Assets/Assets.xcassets/ReloadIcon.imageset/Bold-L@2x.png create mode 100644 Imago/Assets/Assets.xcassets/ReloadIcon.imageset/Bold-L@3x.png create mode 100644 Imago/Assets/Assets.xcassets/ReloadIcon.imageset/Contents.json create mode 100644 Imago/Assets/Base.lproj/Main.storyboard create mode 100644 Imago/Assets/Imago.entitlements create mode 100644 Imago/Camera/Camera.swift create mode 100644 Imago/Camera/CameraFilter.swift create mode 100644 Imago/Camera/Canon/CanonCamera.swift create mode 100644 Imago/Camera/Canon/CanonCommandErrors.swift create mode 100644 Imago/Camera/Canon/CanonCommandProperty.swift create mode 100644 Imago/Client.swift create mode 100644 Imago/Conductor.swift create mode 100644 Imago/Extensions/CIImage+Filter.swift create mode 100644 Imago/Extensions/Frame+Decompress.swift create mode 100644 Imago/Headers/Imago-Bridging-Header.h create mode 100644 Imago/Headers/Imago-PrefixHeader.pch create mode 100644 Imago/Info.plist create mode 100644 Imago/Preview Content/Preview Assets.xcassets/Contents.json create mode 100644 Imago/Server.swift create mode 100644 Imago/Stream.swift create mode 100644 Imago/Tests/ImagoTests/ImagoTests.swift create mode 100644 Imago/Tests/ImagoTests/Info.plist create mode 100644 Imago/Tests/ImagoUITests/ImagoUITests.swift create mode 100644 Imago/Tests/ImagoUITests/Info.plist create mode 100644 Imago/ViewController.swift create mode 100644 Imago/Views/CameraFilterMenu.swift create mode 100644 Imago/Views/CameraRow.swift create mode 100644 Imago/Views/ContentView.swift create mode 100644 Imago/Views/LivePreview.swift create mode 100644 Imago/Views/TitlebarAccesory.swift create mode 100644 LICENSE create mode 100644 Plugin/Device.swift create mode 100644 Plugin/Headers/ImagoPlugin-Bridging-Header.h create mode 100644 Plugin/Headers/PrefixHeader.pch create mode 100644 Plugin/Info.plist create mode 100644 Plugin/LICENSE create mode 100644 Plugin/Main.swift create mode 100644 Plugin/Object.swift create mode 100644 Plugin/Plugin.swift create mode 100644 Plugin/PluginInterface.swift create mode 100644 Plugin/Property.swift create mode 100644 Plugin/Stream.swift create mode 100644 README.md create mode 100644 Shared/Const.swift create mode 100644 Shared/DLog.swift create mode 100644 Shared/Extensions/CIImage+Imago.swift create mode 100644 Shared/Extensions/CVPixelBuffer+Data.swift create mode 100644 Shared/Extensions/CVPixelBufferPool+Imago.swift create mode 100644 Shared/Frame.swift create mode 100644 Shared/IPC/CallerID.swift create mode 100644 Shared/IPC/PubSub.swift create mode 100644 Shared/ImagoStream.swift create mode 100644 Shared/RepeatingTimer.swift create mode 100755 setup.sh diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3709ecf --- /dev/null +++ b/.gitignore @@ -0,0 +1,97 @@ +.DS_Store +_local/* +Build/* +release.sh +Frameworks/* +!Frameworks/.gitkeep + +# Xcode +# +# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore + +## User settings +xcuserdata/ + +## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) +*.xcscmblueprint +*.xccheckout + +## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) +build/ +DerivedData/ +*.moved-aside +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 + +## Obj-C/Swift specific +*.hmap + +## App packaging +*.ipa +*.dSYM.zip +*.dSYM + +## Playgrounds +timeline.xctimeline +playground.xcworkspace + +# Swift Package Manager +# +# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. +# Packages/ +# Package.pins +# Package.resolved +# *.xcodeproj +# +# Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata +# hence it is not needed unless you have added a package configuration file to your project +# .swiftpm + +.build/ + +# CocoaPods +# +# We recommend against adding the Pods directory to your .gitignore. However +# you should judge for yourself, the pros and cons are mentioned at: +# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control +# +# Pods/ +# +# Add this line if you want to avoid checking in source code from the Xcode workspace +# *.xcworkspace + +# Carthage +# +# Add this line if you want to avoid checking in source code from Carthage dependencies. +# Carthage/Checkouts + +Carthage/Build/ + +# Accio dependency management +Dependencies/ +.accio/ + +# fastlane +# +# It is recommended to not store the screenshots in the git repo. +# Instead, use fastlane to re-generate the screenshots whenever they are needed. +# For more information about the recommended setup visit: +# https://docs.fastlane.tools/best-practices/source-control/#source-control + +fastlane/report.xml +fastlane/Preview.html +fastlane/screenshots/**/*.png +fastlane/test_output + +# Code Injection +# +# After new code Injection tools there's a generated folder /iOSInjectionProject +# https://github.com/johnno1962/injectionforxcode + +iOSInjectionProject/ diff --git a/Frameworks/.gitkeep b/Frameworks/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/Imago.xcodeproj/project.pbxproj b/Imago.xcodeproj/project.pbxproj new file mode 100644 index 0000000..0dd6a21 --- /dev/null +++ b/Imago.xcodeproj/project.pbxproj @@ -0,0 +1,1176 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 50; + objects = { + +/* Begin PBXBuildFile section */ + B111517624BD621D009CA476 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B1C6F8A124B3F3D5009D98C0 /* Cocoa.framework */; }; + B111517724BD621D009CA476 /* CoreMedia.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B177492524B0FAE4007103FB /* CoreMedia.framework */; }; + B111517824BD621D009CA476 /* CoreImage.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B10F711B24BD323C008276A7 /* CoreImage.framework */; }; + B111517924BD621D009CA476 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B177492324B0FAE4007103FB /* CoreGraphics.framework */; }; + B111517A24BD621D009CA476 /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B177492124B0FAD1007103FB /* AVFoundation.framework */; }; + B111517B24BD621D009CA476 /* CoreVideo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B177492424B0FAE4007103FB /* CoreVideo.framework */; }; + B111517C24BD621D009CA476 /* CoreMediaIO.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B177492624B0FAE5007103FB /* CoreMediaIO.framework */; }; + B111519524BD6759009CA476 /* Device.swift in Sources */ = {isa = PBXBuildFile; fileRef = B111518B24BD6758009CA476 /* Device.swift */; }; + B111519624BD6759009CA476 /* Stream.swift in Sources */ = {isa = PBXBuildFile; fileRef = B111518D24BD6758009CA476 /* Stream.swift */; }; + B111519724BD6759009CA476 /* Main.swift in Sources */ = {isa = PBXBuildFile; fileRef = B111518E24BD6758009CA476 /* Main.swift */; }; + B111519924BD6759009CA476 /* Plugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = B111519024BD6758009CA476 /* Plugin.swift */; }; + B111519A24BD6759009CA476 /* PluginInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = B111519124BD6758009CA476 /* PluginInterface.swift */; }; + B111519B24BD6759009CA476 /* Property.swift in Sources */ = {isa = PBXBuildFile; fileRef = B111519224BD6758009CA476 /* Property.swift */; }; + B111519D24BD6759009CA476 /* Object.swift in Sources */ = {isa = PBXBuildFile; fileRef = B111519424BD6758009CA476 /* Object.swift */; }; + B1499C7724D1D9B300FC3557 /* Stream.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1499C7624D1D9B300FC3557 /* Stream.swift */; }; + B15B113624CB61B6002703FF /* PubSub.swift in Sources */ = {isa = PBXBuildFile; fileRef = B15B113524CB61B6002703FF /* PubSub.swift */; }; + B15B113724CB61B6002703FF /* PubSub.swift in Sources */ = {isa = PBXBuildFile; fileRef = B15B113524CB61B6002703FF /* PubSub.swift */; }; + B17748B524B03497007103FB /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B17748B424B03497007103FB /* AppDelegate.swift */; }; + B17748B724B03497007103FB /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B17748B624B03497007103FB /* ContentView.swift */; }; + B17748B924B03498007103FB /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B17748B824B03498007103FB /* Assets.xcassets */; }; + B17748BC24B03498007103FB /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B17748BB24B03498007103FB /* Preview Assets.xcassets */; }; + B17748CB24B03498007103FB /* ImagoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B17748CA24B03498007103FB /* ImagoTests.swift */; }; + B17748D624B03498007103FB /* ImagoUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B17748D524B03498007103FB /* ImagoUITests.swift */; }; + B17748E724B03542007103FB /* Camera.swift in Sources */ = {isa = PBXBuildFile; fileRef = B17748E424B03542007103FB /* Camera.swift */; }; + B17748F224B039B2007103FB /* EDSDK.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B17748F024B0390D007103FB /* EDSDK.framework */; }; + B17748F324B039B2007103FB /* EDSDK.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = B17748F024B0390D007103FB /* EDSDK.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + B18149F224B61F86004CD8A8 /* CanonCamera.swift in Sources */ = {isa = PBXBuildFile; fileRef = B18149F124B61F86004CD8A8 /* CanonCamera.swift */; }; + B18149F924B67E6E004CD8A8 /* CanonCommandErrors.swift in Sources */ = {isa = PBXBuildFile; fileRef = B18149F824B67E6D004CD8A8 /* CanonCommandErrors.swift */; }; + B18149FB24B68094004CD8A8 /* CanonCommandProperty.swift in Sources */ = {isa = PBXBuildFile; fileRef = B18149FA24B68094004CD8A8 /* CanonCommandProperty.swift */; }; + B183883424DDC18E007981BB /* CameraFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B183883324DDC18E007981BB /* CameraFilter.swift */; }; + B183884624E1A829007981BB /* CVPixelBufferPool+Imago.swift in Sources */ = {isa = PBXBuildFile; fileRef = B183884524E1A829007981BB /* CVPixelBufferPool+Imago.swift */; }; + B183884724E1A82A007981BB /* CVPixelBufferPool+Imago.swift in Sources */ = {isa = PBXBuildFile; fileRef = B183884524E1A829007981BB /* CVPixelBufferPool+Imago.swift */; }; + B183884924E1B806007981BB /* TitlebarAccesory.swift in Sources */ = {isa = PBXBuildFile; fileRef = B183884824E1B806007981BB /* TitlebarAccesory.swift */; }; + B183884C24E1B853007981BB /* CameraFilterMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = B183884B24E1B853007981BB /* CameraFilterMenu.swift */; }; + B183884E24E1B8FB007981BB /* LivePreview.swift in Sources */ = {isa = PBXBuildFile; fileRef = B183884D24E1B8FB007981BB /* LivePreview.swift */; }; + B183885324E1BC07007981BB /* CIImage+Filter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B183885224E1BC07007981BB /* CIImage+Filter.swift */; }; + B183885524E1BD63007981BB /* CIImage+Imago.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1D0889224D1F75A0001B8E9 /* CIImage+Imago.swift */; }; + B183885724E1C340007981BB /* CameraRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = B183885624E1C340007981BB /* CameraRow.swift */; }; + B183885F24E30615007981BB /* libturbojpeg.0.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = B183885E24E30614007981BB /* libturbojpeg.0.dylib */; }; + B183886024E30615007981BB /* libturbojpeg.0.dylib in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = B183885E24E30614007981BB /* libturbojpeg.0.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; + B1B8AD0824C61953009470CA /* Frame+Decompress.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1C5577724C53AE7009355F1 /* Frame+Decompress.swift */; }; + B1B8AD0B24C61E9E009470CA /* Const.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1B8AD0924C61E8F009470CA /* Const.swift */; }; + B1B8AD0F24C620C6009470CA /* DLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1B8AD0E24C620C6009470CA /* DLog.swift */; }; + B1B8AD1024C620C6009470CA /* DLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1B8AD0E24C620C6009470CA /* DLog.swift */; }; + B1B8AD1324C621A4009470CA /* ImagoStream.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1B8AD1124C621A4009470CA /* ImagoStream.swift */; }; + B1B8AD1424C64416009470CA /* Const.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1B8AD0924C61E8F009470CA /* Const.swift */; }; + B1B8AD4B24C756AE009470CA /* CallerID.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1B8AD4A24C756AE009470CA /* CallerID.swift */; }; + B1B8AD4C24C756AE009470CA /* CallerID.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1B8AD4A24C756AE009470CA /* CallerID.swift */; }; + B1C297A724CF870B0089C84D /* RepeatingTimer.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1C297A624CF870B0089C84D /* RepeatingTimer.swift */; }; + B1C297A824CF870B0089C84D /* RepeatingTimer.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1C297A624CF870B0089C84D /* RepeatingTimer.swift */; }; + B1C5576F24C0B59A009355F1 /* Frame.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1C5576E24C0B59A009355F1 /* Frame.swift */; }; + B1C5577024C0B59A009355F1 /* Frame.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1C5576E24C0B59A009355F1 /* Frame.swift */; }; + B1D0889024D1F1060001B8E9 /* CVPixelBuffer+Data.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1D0888F24D1F1060001B8E9 /* CVPixelBuffer+Data.swift */; }; + B1D0889124D1F1060001B8E9 /* CVPixelBuffer+Data.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1D0888F24D1F1060001B8E9 /* CVPixelBuffer+Data.swift */; }; + B1D0889324D1F75A0001B8E9 /* CIImage+Imago.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1D0889224D1F75A0001B8E9 /* CIImage+Imago.swift */; }; + B1D0889624D221160001B8E9 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1D0889524D221160001B8E9 /* ViewController.swift */; }; + B1DF8B7024BD7FDB007ED34C /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B1DF8B6E24BD7FDB007ED34C /* Main.storyboard */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + B17748C724B03498007103FB /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = B17748A924B03497007103FB /* Project object */; + proxyType = 1; + remoteGlobalIDString = B17748B024B03497007103FB; + remoteInfo = Imago; + }; + B17748D224B03498007103FB /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = B17748A924B03497007103FB /* Project object */; + proxyType = 1; + remoteGlobalIDString = B17748B024B03497007103FB; + remoteInfo = Imago; + }; + B183883624DF377A007981BB /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = B17748A924B03497007103FB /* Project object */; + proxyType = 1; + remoteGlobalIDString = B111516B24BD621D009CA476; + remoteInfo = ImagoPlugin; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + B17748EF24B035B5007103FB /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + B17748F324B039B2007103FB /* EDSDK.framework in Embed Frameworks */, + B183886024E30615007981BB /* libturbojpeg.0.dylib in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + B10F711B24BD323C008276A7 /* CoreImage.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreImage.framework; path = System/Library/Frameworks/CoreImage.framework; sourceTree = SDKROOT; }; + B111518324BD621D009CA476 /* ImagoPlugin.plugin */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ImagoPlugin.plugin; sourceTree = BUILT_PRODUCTS_DIR; }; + B111518924BD630D009CA476 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + B111518B24BD6758009CA476 /* Device.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Device.swift; path = ../Device.swift; sourceTree = ""; }; + B111518D24BD6758009CA476 /* Stream.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Stream.swift; path = ../Stream.swift; sourceTree = ""; }; + B111518E24BD6758009CA476 /* Main.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Main.swift; path = ../Main.swift; sourceTree = ""; }; + B111519024BD6758009CA476 /* Plugin.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Plugin.swift; path = ../Plugin.swift; sourceTree = ""; }; + B111519124BD6758009CA476 /* PluginInterface.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = PluginInterface.swift; path = ../PluginInterface.swift; sourceTree = ""; }; + B111519224BD6758009CA476 /* Property.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Property.swift; path = ../Property.swift; sourceTree = ""; }; + B111519424BD6758009CA476 /* Object.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Object.swift; path = ../Object.swift; sourceTree = ""; }; + B111519F24BD6B13009CA476 /* PrefixHeader.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PrefixHeader.pch; sourceTree = ""; }; + B11151A024BD6B13009CA476 /* ImagoPlugin-Bridging-Header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ImagoPlugin-Bridging-Header.h"; sourceTree = ""; }; + B142810F24B8C0CF00F50FCD /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; + B1499C7624D1D9B300FC3557 /* Stream.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Stream.swift; sourceTree = ""; }; + B15B113524CB61B6002703FF /* PubSub.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PubSub.swift; sourceTree = ""; }; + B17748B124B03497007103FB /* Imago.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Imago.app; sourceTree = BUILT_PRODUCTS_DIR; }; + B17748B424B03497007103FB /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + B17748B624B03497007103FB /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; + B17748B824B03498007103FB /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + B17748BB24B03498007103FB /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + B17748C024B03498007103FB /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + B17748C124B03498007103FB /* Imago.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Imago.entitlements; sourceTree = ""; }; + B17748C624B03498007103FB /* ImagoTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ImagoTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + B17748CA24B03498007103FB /* ImagoTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImagoTests.swift; sourceTree = ""; }; + B17748CC24B03498007103FB /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + B17748D124B03498007103FB /* ImagoUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ImagoUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + B17748D524B03498007103FB /* ImagoUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImagoUITests.swift; sourceTree = ""; }; + B17748D724B03498007103FB /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + B17748E424B03542007103FB /* Camera.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Camera.swift; sourceTree = ""; }; + B17748E924B0358F007103FB /* Imago-Bridging-Header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "Imago-Bridging-Header.h"; sourceTree = ""; }; + B17748EA24B03590007103FB /* Imago-PrefixHeader.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "Imago-PrefixHeader.pch"; sourceTree = ""; }; + B17748F024B0390D007103FB /* EDSDK.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = EDSDK.framework; path = Frameworks/EDSDK/EDSDK.framework; sourceTree = ""; }; + B177491E24B0F9AF007103FB /* libturbojpeg.0.2.0.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libturbojpeg.0.2.0.dylib; path = Frameworks/libturbojpeg.0.2.0.dylib; sourceTree = ""; }; + B177492124B0FAD1007103FB /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; }; + B177492324B0FAE4007103FB /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; + B177492424B0FAE4007103FB /* CoreVideo.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreVideo.framework; path = System/Library/Frameworks/CoreVideo.framework; sourceTree = SDKROOT; }; + B177492524B0FAE4007103FB /* CoreMedia.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreMedia.framework; path = System/Library/Frameworks/CoreMedia.framework; sourceTree = SDKROOT; }; + B177492624B0FAE5007103FB /* CoreMediaIO.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreMediaIO.framework; path = System/Library/Frameworks/CoreMediaIO.framework; sourceTree = SDKROOT; }; + B18149F124B61F86004CD8A8 /* CanonCamera.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CanonCamera.swift; sourceTree = ""; }; + B18149F824B67E6D004CD8A8 /* CanonCommandErrors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CanonCommandErrors.swift; sourceTree = ""; }; + B18149FA24B68094004CD8A8 /* CanonCommandProperty.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CanonCommandProperty.swift; sourceTree = ""; }; + B183883324DDC18E007981BB /* CameraFilter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CameraFilter.swift; sourceTree = ""; }; + B183884324E19A2B007981BB /* LICENSE */ = {isa = PBXFileReference; lastKnownFileType = text; path = LICENSE; sourceTree = ""; }; + B183884424E1A544007981BB /* LICENSE */ = {isa = PBXFileReference; lastKnownFileType = text; path = LICENSE; sourceTree = ""; }; + B183884524E1A829007981BB /* CVPixelBufferPool+Imago.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CVPixelBufferPool+Imago.swift"; sourceTree = ""; }; + B183884824E1B806007981BB /* TitlebarAccesory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TitlebarAccesory.swift; sourceTree = ""; }; + B183884B24E1B853007981BB /* CameraFilterMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CameraFilterMenu.swift; sourceTree = ""; }; + B183884D24E1B8FB007981BB /* LivePreview.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LivePreview.swift; sourceTree = ""; }; + B183885224E1BC07007981BB /* CIImage+Filter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CIImage+Filter.swift"; sourceTree = ""; }; + B183885624E1C340007981BB /* CameraRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CameraRow.swift; sourceTree = ""; }; + B183885E24E30614007981BB /* libturbojpeg.0.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libturbojpeg.0.dylib; path = Frameworks/libturbojpeg.0.dylib; sourceTree = ""; }; + B1B8AD0924C61E8F009470CA /* Const.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Const.swift; sourceTree = ""; }; + B1B8AD0E24C620C6009470CA /* DLog.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DLog.swift; sourceTree = ""; }; + B1B8AD1124C621A4009470CA /* ImagoStream.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = ImagoStream.swift; path = ../Shared/ImagoStream.swift; sourceTree = ""; }; + B1B8AD4A24C756AE009470CA /* CallerID.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallerID.swift; sourceTree = ""; }; + B1C297A624CF870B0089C84D /* RepeatingTimer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RepeatingTimer.swift; sourceTree = ""; }; + B1C5576E24C0B59A009355F1 /* Frame.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Frame.swift; sourceTree = ""; }; + B1C5577724C53AE7009355F1 /* Frame+Decompress.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Frame+Decompress.swift"; sourceTree = ""; }; + B1C6F8A124B3F3D5009D98C0 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; + B1D0888F24D1F1060001B8E9 /* CVPixelBuffer+Data.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CVPixelBuffer+Data.swift"; sourceTree = ""; }; + B1D0889224D1F75A0001B8E9 /* CIImage+Imago.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CIImage+Imago.swift"; sourceTree = ""; }; + B1D0889524D221160001B8E9 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; + B1DF8B6F24BD7FDB007ED34C /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + B111517524BD621D009CA476 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + B111517624BD621D009CA476 /* Cocoa.framework in Frameworks */, + B111517724BD621D009CA476 /* CoreMedia.framework in Frameworks */, + B111517824BD621D009CA476 /* CoreImage.framework in Frameworks */, + B111517924BD621D009CA476 /* CoreGraphics.framework in Frameworks */, + B111517A24BD621D009CA476 /* AVFoundation.framework in Frameworks */, + B111517B24BD621D009CA476 /* CoreVideo.framework in Frameworks */, + B111517C24BD621D009CA476 /* CoreMediaIO.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + B17748AE24B03497007103FB /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + B17748F224B039B2007103FB /* EDSDK.framework in Frameworks */, + B183885F24E30615007981BB /* libturbojpeg.0.dylib in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + B17748C324B03498007103FB /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + B17748CE24B03498007103FB /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + B111518524BD6240009CA476 /* Tests */ = { + isa = PBXGroup; + children = ( + B17748D424B03498007103FB /* ImagoUITests */, + B17748C924B03498007103FB /* ImagoTests */, + ); + path = Tests; + sourceTree = ""; + }; + B111518624BD6259009CA476 /* Plugin */ = { + isa = PBXGroup; + children = ( + B111519E24BD6B13009CA476 /* Headers */, + B111518924BD630D009CA476 /* Info.plist */, + B1BD496124E34951000BD5C1 /* Base */, + B1B8AD1124C621A4009470CA /* ImagoStream.swift */, + B183884424E1A544007981BB /* LICENSE */, + ); + path = Plugin; + sourceTree = ""; + }; + B111519E24BD6B13009CA476 /* Headers */ = { + isa = PBXGroup; + children = ( + B111519F24BD6B13009CA476 /* PrefixHeader.pch */, + B11151A024BD6B13009CA476 /* ImagoPlugin-Bridging-Header.h */, + ); + path = Headers; + sourceTree = ""; + }; + B17748A824B03497007103FB = { + isa = PBXGroup; + children = ( + B142810F24B8C0CF00F50FCD /* README.md */, + B183884324E19A2B007981BB /* LICENSE */, + B1B8AD0D24C620C6009470CA /* Shared */, + B17748B324B03497007103FB /* Imago */, + B111518624BD6259009CA476 /* Plugin */, + B17748B224B03497007103FB /* Products */, + B17748EB24B035B5007103FB /* Frameworks */, + ); + sourceTree = ""; + }; + B17748B224B03497007103FB /* Products */ = { + isa = PBXGroup; + children = ( + B17748B124B03497007103FB /* Imago.app */, + B17748C624B03498007103FB /* ImagoTests.xctest */, + B17748D124B03498007103FB /* ImagoUITests.xctest */, + B111518324BD621D009CA476 /* ImagoPlugin.plugin */, + ); + name = Products; + sourceTree = ""; + }; + B17748B324B03497007103FB /* Imago */ = { + isa = PBXGroup; + children = ( + B17748C024B03498007103FB /* Info.plist */, + B1F0B10524B919FF00D9C1C1 /* Views */, + B17748B424B03497007103FB /* AppDelegate.swift */, + B1D0889524D221160001B8E9 /* ViewController.swift */, + B1499C7624D1D9B300FC3557 /* Stream.swift */, + B183885424E1BC1B007981BB /* Extensions */, + B183884F24E1BB10007981BB /* Camera */, + B1DF8B6C24BD7C29007ED34C /* Headers */, + B1DF8B6D24BD7C40007ED34C /* Assets */, + B17748BA24B03498007103FB /* Preview Content */, + B111518524BD6240009CA476 /* Tests */, + ); + path = Imago; + sourceTree = ""; + }; + B17748BA24B03498007103FB /* Preview Content */ = { + isa = PBXGroup; + children = ( + B17748BB24B03498007103FB /* Preview Assets.xcassets */, + ); + path = "Preview Content"; + sourceTree = ""; + }; + B17748C924B03498007103FB /* ImagoTests */ = { + isa = PBXGroup; + children = ( + B17748CA24B03498007103FB /* ImagoTests.swift */, + B17748CC24B03498007103FB /* Info.plist */, + ); + path = ImagoTests; + sourceTree = ""; + }; + B17748D424B03498007103FB /* ImagoUITests */ = { + isa = PBXGroup; + children = ( + B17748D524B03498007103FB /* ImagoUITests.swift */, + B17748D724B03498007103FB /* Info.plist */, + ); + path = ImagoUITests; + sourceTree = ""; + }; + B17748EB24B035B5007103FB /* Frameworks */ = { + isa = PBXGroup; + children = ( + B183885E24E30614007981BB /* libturbojpeg.0.dylib */, + B10F711B24BD323C008276A7 /* CoreImage.framework */, + B1C6F8A124B3F3D5009D98C0 /* Cocoa.framework */, + B177492324B0FAE4007103FB /* CoreGraphics.framework */, + B177492524B0FAE4007103FB /* CoreMedia.framework */, + B177492624B0FAE5007103FB /* CoreMediaIO.framework */, + B177492424B0FAE4007103FB /* CoreVideo.framework */, + B177492124B0FAD1007103FB /* AVFoundation.framework */, + B177491E24B0F9AF007103FB /* libturbojpeg.0.2.0.dylib */, + B17748F024B0390D007103FB /* EDSDK.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + B18149F524B62003004CD8A8 /* Canon */ = { + isa = PBXGroup; + children = ( + B18149F124B61F86004CD8A8 /* CanonCamera.swift */, + B18149F824B67E6D004CD8A8 /* CanonCommandErrors.swift */, + B18149FA24B68094004CD8A8 /* CanonCommandProperty.swift */, + ); + path = Canon; + sourceTree = ""; + }; + B183884F24E1BB10007981BB /* Camera */ = { + isa = PBXGroup; + children = ( + B17748E424B03542007103FB /* Camera.swift */, + B183883324DDC18E007981BB /* CameraFilter.swift */, + B18149F524B62003004CD8A8 /* Canon */, + ); + path = Camera; + sourceTree = ""; + }; + B183885024E1BB67007981BB /* Extensions */ = { + isa = PBXGroup; + children = ( + B1D0888F24D1F1060001B8E9 /* CVPixelBuffer+Data.swift */, + B183884524E1A829007981BB /* CVPixelBufferPool+Imago.swift */, + B1D0889224D1F75A0001B8E9 /* CIImage+Imago.swift */, + ); + path = Extensions; + sourceTree = ""; + }; + B183885124E1BB80007981BB /* IPC */ = { + isa = PBXGroup; + children = ( + B1B8AD4A24C756AE009470CA /* CallerID.swift */, + B15B113524CB61B6002703FF /* PubSub.swift */, + ); + path = IPC; + sourceTree = ""; + }; + B183885424E1BC1B007981BB /* Extensions */ = { + isa = PBXGroup; + children = ( + B1C5577724C53AE7009355F1 /* Frame+Decompress.swift */, + B183885224E1BC07007981BB /* CIImage+Filter.swift */, + ); + path = Extensions; + sourceTree = ""; + }; + B1B8AD0D24C620C6009470CA /* Shared */ = { + isa = PBXGroup; + children = ( + B1B8AD0924C61E8F009470CA /* Const.swift */, + B1B8AD0E24C620C6009470CA /* DLog.swift */, + B1C5576E24C0B59A009355F1 /* Frame.swift */, + B1C297A624CF870B0089C84D /* RepeatingTimer.swift */, + B183885124E1BB80007981BB /* IPC */, + B183885024E1BB67007981BB /* Extensions */, + ); + path = Shared; + sourceTree = ""; + }; + B1BD496124E34951000BD5C1 /* Base */ = { + isa = PBXGroup; + children = ( + B111518E24BD6758009CA476 /* Main.swift */, + B111519024BD6758009CA476 /* Plugin.swift */, + B111519124BD6758009CA476 /* PluginInterface.swift */, + B111518B24BD6758009CA476 /* Device.swift */, + B111519424BD6758009CA476 /* Object.swift */, + B111519224BD6758009CA476 /* Property.swift */, + B111518D24BD6758009CA476 /* Stream.swift */, + ); + path = Base; + sourceTree = ""; + }; + B1DF8B6C24BD7C29007ED34C /* Headers */ = { + isa = PBXGroup; + children = ( + B17748E924B0358F007103FB /* Imago-Bridging-Header.h */, + B17748EA24B03590007103FB /* Imago-PrefixHeader.pch */, + ); + path = Headers; + sourceTree = ""; + }; + B1DF8B6D24BD7C40007ED34C /* Assets */ = { + isa = PBXGroup; + children = ( + B1DF8B6E24BD7FDB007ED34C /* Main.storyboard */, + B17748C124B03498007103FB /* Imago.entitlements */, + B17748B824B03498007103FB /* Assets.xcassets */, + ); + path = Assets; + sourceTree = ""; + }; + B1F0B10524B919FF00D9C1C1 /* Views */ = { + isa = PBXGroup; + children = ( + B17748B624B03497007103FB /* ContentView.swift */, + B183885624E1C340007981BB /* CameraRow.swift */, + B183884B24E1B853007981BB /* CameraFilterMenu.swift */, + B183884824E1B806007981BB /* TitlebarAccesory.swift */, + B183884D24E1B8FB007981BB /* LivePreview.swift */, + ); + path = Views; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + B111516B24BD621D009CA476 /* ImagoPlugin */ = { + isa = PBXNativeTarget; + buildConfigurationList = B111518024BD621D009CA476 /* Build configuration list for PBXNativeTarget "ImagoPlugin" */; + buildPhases = ( + B111516C24BD621D009CA476 /* Sources */, + B111517524BD621D009CA476 /* Frameworks */, + B1DF8B6B24BD6E06007ED34C /* Copy Plugin to System */, + B145826924E31D010035723E /* Update Build Version */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = ImagoPlugin; + productName = "Imago DAL"; + productReference = B111518324BD621D009CA476 /* ImagoPlugin.plugin */; + productType = "com.apple.product-type.bundle"; + }; + B17748B024B03497007103FB /* Imago */ = { + isa = PBXNativeTarget; + buildConfigurationList = B17748DA24B03498007103FB /* Build configuration list for PBXNativeTarget "Imago" */; + buildPhases = ( + B145826824E315360035723E /* Code Sign Nested Bundles in EDSDK.framework */, + B17748AD24B03497007103FB /* Sources */, + B17748AE24B03497007103FB /* Frameworks */, + B17748AF24B03497007103FB /* Resources */, + B17748EF24B035B5007103FB /* Embed Frameworks */, + B145826A24E31E180035723E /* Update Build Version */, + ); + buildRules = ( + ); + dependencies = ( + B183883724DF377A007981BB /* PBXTargetDependency */, + ); + name = Imago; + productName = Imago; + productReference = B17748B124B03497007103FB /* Imago.app */; + productType = "com.apple.product-type.application"; + }; + B17748C524B03498007103FB /* ImagoTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = B17748DD24B03498007103FB /* Build configuration list for PBXNativeTarget "ImagoTests" */; + buildPhases = ( + B17748C224B03498007103FB /* Sources */, + B17748C324B03498007103FB /* Frameworks */, + B17748C424B03498007103FB /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + B17748C824B03498007103FB /* PBXTargetDependency */, + ); + name = ImagoTests; + productName = ImagoTests; + productReference = B17748C624B03498007103FB /* ImagoTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + B17748D024B03498007103FB /* ImagoUITests */ = { + isa = PBXNativeTarget; + buildConfigurationList = B17748E024B03498007103FB /* Build configuration list for PBXNativeTarget "ImagoUITests" */; + buildPhases = ( + B17748CD24B03498007103FB /* Sources */, + B17748CE24B03498007103FB /* Frameworks */, + B17748CF24B03498007103FB /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + B17748D324B03498007103FB /* PBXTargetDependency */, + ); + name = ImagoUITests; + productName = ImagoUITests; + productReference = B17748D124B03498007103FB /* ImagoUITests.xctest */; + productType = "com.apple.product-type.bundle.ui-testing"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + B17748A924B03497007103FB /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 1130; + LastUpgradeCheck = 1160; + ORGANIZATIONNAME = "Nolan Brown"; + TargetAttributes = { + B17748B024B03497007103FB = { + CreatedOnToolsVersion = 11.3; + }; + B17748C524B03498007103FB = { + CreatedOnToolsVersion = 11.3; + TestTargetID = B17748B024B03497007103FB; + }; + B17748D024B03498007103FB = { + CreatedOnToolsVersion = 11.3; + TestTargetID = B17748B024B03497007103FB; + }; + }; + }; + buildConfigurationList = B17748AC24B03497007103FB /* Build configuration list for PBXProject "Imago" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = B17748A824B03497007103FB; + productRefGroup = B17748B224B03497007103FB /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + B17748B024B03497007103FB /* Imago */, + B111516B24BD621D009CA476 /* ImagoPlugin */, + B17748C524B03498007103FB /* ImagoTests */, + B17748D024B03498007103FB /* ImagoUITests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + B17748AF24B03497007103FB /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + B17748BC24B03498007103FB /* Preview Assets.xcassets in Resources */, + B17748B924B03498007103FB /* Assets.xcassets in Resources */, + B1DF8B7024BD7FDB007ED34C /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + B17748C424B03498007103FB /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + B17748CF24B03498007103FB /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + B145826824E315360035723E /* Code Sign Nested Bundles in EDSDK.framework */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "Code Sign Nested Bundles in EDSDK.framework"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "# Type a script or drag a script file from your workspace to insert its path.\nEDSDK_FRAMEWORK_PATH=$SRCROOT/Frameworks/EDSDK/EDSDK.framework\nEDSDK_CURRENT_VERSION_PATH=$EDSDK_FRAMEWORK_PATH/Versions/A\ncodesign -fs \"$EXPANDED_CODE_SIGN_IDENTITY_NAME\" $EDSDK_CURRENT_VERSION_PATH/CHHLLite.bundle\ncodesign -fs \"$EXPANDED_CODE_SIGN_IDENTITY_NAME\" $EDSDK_CURRENT_VERSION_PATH/EdsImage.bundle\n#codesign -fs \"$EXPANDED_CODE_SIGN_IDENTITY_NAME\" $EDSDK_CURRENT_VERSION_PATH/EDSDK\n#codesign -fs \"$EXPANDED_CODE_SIGN_IDENTITY_NAME\" $EDSDK_FRAMEWORK_PATH\n"; + }; + B145826924E31D010035723E /* Update Build Version */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "Update Build Version"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "# Keep build version synced between App and Plugin\n\nAPP_PLIST=\"${PROJECT_DIR}/Imago/Info.plist\"\n\nSHORT_VERSION=$($PLB -c \"Print CFBundleShortVersionString\" \"$APP_PLIST\")\nBUILD_NUMBER=$($PLB -c \"Print CFBundleVersion\" \"$APP_PLIST\")\n\nPLUGIN_PLIST=\"${PROJECT_DIR}/${INFOPLIST_FILE}\"\nPLB=/usr/libexec/PlistBuddy\n\n$PLB -c \"Set :CFBundleShortVersionString $SHORT_VERSION\" \"$PLUGIN_PLIST\"\n$PLB -c \"Set :CFBundleVersion $BUILD_NUMBER\" \"$PLUGIN_PLIST\"\n"; + }; + B145826A24E31E180035723E /* Update Build Version */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "Update Build Version"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\nAPP_PLIST=\"${PROJECT_DIR}/${INFOPLIST_FILE}\"\n\nPLB=/usr/libexec/PlistBuddy\n\nLAST_NUMBER=$($PLB -c \"Print CFBundleVersion\" \"$APP_PLIST\")\nNEW_VERSION=$(($LAST_NUMBER + 1))\n$PLB -c \"Set :CFBundleVersion $NEW_VERSION\" \"$APP_PLIST\"\n\n"; + }; + B1DF8B6B24BD6E06007ED34C /* Copy Plugin to System */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "Copy Plugin to System"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "# Type a script or drag a script file from your workspace to insert its path.\nif [ $CONFIGURATION == \"Debug\" ];then\necho $(security find-generic-password -ga \"ImagoBuild\" -w) | sudo -S rm -R /Library/CoreMediaIO/Plug-Ins/DAL/$PRODUCT_NAME.plugin; \nsudo cp -R $SYMROOT/$CONFIGURATION/$PRODUCT_NAME.plugin /Library/CoreMediaIO/Plug-Ins/DAL/$PRODUCT_NAME.plugin\nfi\n"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + B111516C24BD621D009CA476 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + B183885524E1BD63007981BB /* CIImage+Imago.swift in Sources */, + B111519B24BD6759009CA476 /* Property.swift in Sources */, + B111519A24BD6759009CA476 /* PluginInterface.swift in Sources */, + B111519724BD6759009CA476 /* Main.swift in Sources */, + B111519524BD6759009CA476 /* Device.swift in Sources */, + B1C297A824CF870B0089C84D /* RepeatingTimer.swift in Sources */, + B1B8AD1024C620C6009470CA /* DLog.swift in Sources */, + B111519924BD6759009CA476 /* Plugin.swift in Sources */, + B1B8AD1324C621A4009470CA /* ImagoStream.swift in Sources */, + B1B8AD4C24C756AE009470CA /* CallerID.swift in Sources */, + B183884724E1A82A007981BB /* CVPixelBufferPool+Imago.swift in Sources */, + B1B8AD0B24C61E9E009470CA /* Const.swift in Sources */, + B111519D24BD6759009CA476 /* Object.swift in Sources */, + B1C5577024C0B59A009355F1 /* Frame.swift in Sources */, + B15B113724CB61B6002703FF /* PubSub.swift in Sources */, + B1D0889124D1F1060001B8E9 /* CVPixelBuffer+Data.swift in Sources */, + B111519624BD6759009CA476 /* Stream.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + B17748AD24B03497007103FB /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + B1C5576F24C0B59A009355F1 /* Frame.swift in Sources */, + B1C297A724CF870B0089C84D /* RepeatingTimer.swift in Sources */, + B17748E724B03542007103FB /* Camera.swift in Sources */, + B183885724E1C340007981BB /* CameraRow.swift in Sources */, + B15B113624CB61B6002703FF /* PubSub.swift in Sources */, + B1D0889024D1F1060001B8E9 /* CVPixelBuffer+Data.swift in Sources */, + B1D0889624D221160001B8E9 /* ViewController.swift in Sources */, + B18149F924B67E6E004CD8A8 /* CanonCommandErrors.swift in Sources */, + B17748B724B03497007103FB /* ContentView.swift in Sources */, + B1B8AD1424C64416009470CA /* Const.swift in Sources */, + B183884C24E1B853007981BB /* CameraFilterMenu.swift in Sources */, + B183884924E1B806007981BB /* TitlebarAccesory.swift in Sources */, + B1B8AD0824C61953009470CA /* Frame+Decompress.swift in Sources */, + B1B8AD4B24C756AE009470CA /* CallerID.swift in Sources */, + B183883424DDC18E007981BB /* CameraFilter.swift in Sources */, + B18149F224B61F86004CD8A8 /* CanonCamera.swift in Sources */, + B1B8AD0F24C620C6009470CA /* DLog.swift in Sources */, + B17748B524B03497007103FB /* AppDelegate.swift in Sources */, + B18149FB24B68094004CD8A8 /* CanonCommandProperty.swift in Sources */, + B1499C7724D1D9B300FC3557 /* Stream.swift in Sources */, + B183884E24E1B8FB007981BB /* LivePreview.swift in Sources */, + B1D0889324D1F75A0001B8E9 /* CIImage+Imago.swift in Sources */, + B183884624E1A829007981BB /* CVPixelBufferPool+Imago.swift in Sources */, + B183885324E1BC07007981BB /* CIImage+Filter.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + B17748C224B03498007103FB /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + B17748CB24B03498007103FB /* ImagoTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + B17748CD24B03498007103FB /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + B17748D624B03498007103FB /* ImagoUITests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + B17748C824B03498007103FB /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = B17748B024B03497007103FB /* Imago */; + targetProxy = B17748C724B03498007103FB /* PBXContainerItemProxy */; + }; + B17748D324B03498007103FB /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = B17748B024B03497007103FB /* Imago */; + targetProxy = B17748D224B03498007103FB /* PBXContainerItemProxy */; + }; + B183883724DF377A007981BB /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = B111516B24BD621D009CA476 /* ImagoPlugin */; + targetProxy = B183883624DF377A007981BB /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + B1DF8B6E24BD7FDB007ED34C /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + B1DF8B6F24BD7FDB007ED34C /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + B111518124BD621D009CA476 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_INJECT_BASE_ENTITLEMENTS = YES; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 9DCD34JNP6; + ENABLE_HARDENED_RUNTIME = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Frameworks/EDSDK", + ); + GCC_OPTIMIZATION_LEVEL = s; + GCC_PREFIX_HEADER = Plugin/Headers/PrefixHeader.pch; + HEADER_SEARCH_PATHS = ( + "$(PROJECT_DIR)/Frameworks/EDSDK/Header", + "/opt/libjpeg-turbo/include", + ); + INFOPLIST_FILE = Plugin/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/../Frameworks", + ); + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.nolanbrown.Imago.Plugin; + PRODUCT_MODULE_NAME = "$(PRODUCT_NAME:c99extidentifier)"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Plugin/Headers/ImagoPlugin-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + WRAPPER_EXTENSION = plugin; + }; + name = Debug; + }; + B111518224BD621D009CA476 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = "Developer ID Application"; + CODE_SIGN_INJECT_BASE_ENTITLEMENTS = NO; + CODE_SIGN_STYLE = Manual; + COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 9DCD34JNP6; + ENABLE_HARDENED_RUNTIME = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Frameworks/EDSDK", + ); + GCC_PREFIX_HEADER = Plugin/Headers/PrefixHeader.pch; + HEADER_SEARCH_PATHS = ( + "$(PROJECT_DIR)/Frameworks/EDSDK/Header", + "/opt/libjpeg-turbo/include", + ); + INFOPLIST_FILE = Plugin/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/../Frameworks", + ); + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Frameworks", + ); + OTHER_CODE_SIGN_FLAGS = "--timestamp"; + PRODUCT_BUNDLE_IDENTIFIER = com.nolanbrown.Imago.Plugin; + PRODUCT_MODULE_NAME = "$(PRODUCT_NAME:c99extidentifier)"; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_OBJC_BRIDGING_HEADER = "Plugin/Headers/ImagoPlugin-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + WRAPPER_EXTENSION = plugin; + }; + name = Release; + }; + B17748D824B03498007103FB /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + 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.15; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OBJC_BRIDGING_HEADER = ""; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + B17748D924B03498007103FB /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + 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.15; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OBJC_BRIDGING_HEADER = ""; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Release; + }; + B17748DB24B03498007103FB /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_ENTITLEMENTS = Imago/Assets/Imago.entitlements; + CODE_SIGN_INJECT_BASE_ENTITLEMENTS = YES; + COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_ASSET_PATHS = "\"Imago/Preview Content\""; + DEVELOPMENT_TEAM = 9DCD34JNP6; + ENABLE_HARDENED_RUNTIME = YES; + ENABLE_PREVIEWS = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Frameworks/EDSDK", + ); + GCC_PREPROCESSOR_DEFINITIONS = ( + "IMAGO_APP=1", + "DEBUG=1", + ); + HEADER_SEARCH_PATHS = ( + "$(PROJECT_DIR)/Frameworks/EDSDK/Header", + "/opt/libjpeg-turbo/include", + ); + INFOPLIST_FILE = Imago/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = 10.15; + PRODUCT_BUNDLE_IDENTIFIER = com.nolanbrown.Imago; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_OBJC_BRIDGING_HEADER = "Imago/Headers/Imago-Bridging-Header.h"; + SWIFT_PRECOMPILE_BRIDGING_HEADER = NO; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + B17748DC24B03498007103FB /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_ENTITLEMENTS = Imago/Assets/Imago.entitlements; + CODE_SIGN_IDENTITY = "Developer ID Application"; + CODE_SIGN_INJECT_BASE_ENTITLEMENTS = NO; + CODE_SIGN_STYLE = Manual; + COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_ASSET_PATHS = "\"Imago/Preview Content\""; + DEVELOPMENT_TEAM = 9DCD34JNP6; + ENABLE_HARDENED_RUNTIME = YES; + ENABLE_PREVIEWS = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Frameworks/EDSDK", + ); + GCC_PREPROCESSOR_DEFINITIONS = "IMAGO_APP=1"; + HEADER_SEARCH_PATHS = ( + "$(PROJECT_DIR)/Frameworks/EDSDK/Header", + "/opt/libjpeg-turbo/include", + ); + INFOPLIST_FILE = Imago/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = 10.15; + OTHER_CODE_SIGN_FLAGS = "--timestamp"; + PRODUCT_BUNDLE_IDENTIFIER = com.nolanbrown.Imago; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = "Imago Mac App Provisioning Profile"; + SWIFT_OBJC_BRIDGING_HEADER = "Imago/Headers/Imago-Bridging-Header.h"; + SWIFT_PRECOMPILE_BRIDGING_HEADER = NO; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; + B17748DE24B03498007103FB /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 9DCD34JNP6; + INFOPLIST_FILE = ImagoTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/../Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = 10.15; + PRODUCT_BUNDLE_IDENTIFIER = com.nolanbrown.ImagoTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Imago.app/Contents/MacOS/Imago"; + }; + name = Debug; + }; + B17748DF24B03498007103FB /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 9DCD34JNP6; + INFOPLIST_FILE = ImagoTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/../Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = 10.15; + PRODUCT_BUNDLE_IDENTIFIER = com.nolanbrown.ImagoTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Imago.app/Contents/MacOS/Imago"; + }; + name = Release; + }; + B17748E124B03498007103FB /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 9DCD34JNP6; + INFOPLIST_FILE = ImagoUITests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/../Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.nolanbrown.ImagoUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_TARGET_NAME = Imago; + }; + name = Debug; + }; + B17748E224B03498007103FB /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 9DCD34JNP6; + INFOPLIST_FILE = ImagoUITests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/../Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.nolanbrown.ImagoUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_TARGET_NAME = Imago; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + B111518024BD621D009CA476 /* Build configuration list for PBXNativeTarget "ImagoPlugin" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + B111518124BD621D009CA476 /* Debug */, + B111518224BD621D009CA476 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + B17748AC24B03497007103FB /* Build configuration list for PBXProject "Imago" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + B17748D824B03498007103FB /* Debug */, + B17748D924B03498007103FB /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + B17748DA24B03498007103FB /* Build configuration list for PBXNativeTarget "Imago" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + B17748DB24B03498007103FB /* Debug */, + B17748DC24B03498007103FB /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + B17748DD24B03498007103FB /* Build configuration list for PBXNativeTarget "ImagoTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + B17748DE24B03498007103FB /* Debug */, + B17748DF24B03498007103FB /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + B17748E024B03498007103FB /* Build configuration list for PBXNativeTarget "ImagoUITests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + B17748E124B03498007103FB /* Debug */, + B17748E224B03498007103FB /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = B17748A924B03497007103FB /* Project object */; +} diff --git a/Imago.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Imago.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/Imago.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/Imago.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Imago.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/Imago.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Imago.xcodeproj/xcshareddata/xcschemes/Imago.xcscheme b/Imago.xcodeproj/xcshareddata/xcschemes/Imago.xcscheme new file mode 100644 index 0000000..e5f8552 --- /dev/null +++ b/Imago.xcodeproj/xcshareddata/xcschemes/Imago.xcscheme @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Imago/AppDelegate.swift b/Imago/AppDelegate.swift new file mode 100644 index 0000000..5bd6a6a --- /dev/null +++ b/Imago/AppDelegate.swift @@ -0,0 +1,64 @@ +// +// AppDelegate.swift +// Imago +// +// Created by Nolan Brown on 7/3/20. +// Copyright © 2020 Nolan Brown. All rights reserved. +// + +import Cocoa +import SwiftUI + + +@NSApplicationMain +class AppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate, NSToolbarDelegate { + + var window: NSWindow! + var viewController: ViewController! + + func applicationDidFinishLaunching(_ aNotification: Notification) { + // Create the SwiftUI view that provides the window contents. + viewController = ViewController() + + // Create the titlebar accessory + let titlebarAccessoryView = TitlebarAccessory().environmentObject(viewController).frame(minWidth: 480, minHeight: 40, alignment: .topLeading).padding([.top, .leading, .trailing], -15.0).padding([.bottom, .leading],15.0) + let accessoryHostingView = NSHostingView(rootView:titlebarAccessoryView) + accessoryHostingView.frame.size = accessoryHostingView.fittingSize + let titlebarAccessory = NSTitlebarAccessoryViewController() + titlebarAccessory.view = accessoryHostingView + + + // Create the window and set the content view. + window = NSWindow( + contentRect: NSRect(x: 0, y: 0, width: 480, height: 300), + styleMask: [.titled, .closable, .miniaturizable, .fullSizeContentView], + backing: .buffered, defer: false) + window.center() + window.title = "Imago" + window.setFrameAutosaveName("Main Window") + + window.delegate = self + window.addTitlebarAccessoryViewController(titlebarAccessory) + + // Create the SwiftUI view that provides the window contents. + let contentView = ContentView().environmentObject(viewController) + + window.contentView = NSHostingView(rootView: contentView) + window.makeKeyAndOrderFront(nil) + + } + func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { + print("applicationShouldTerminateAfterLastWindowClosed \(sender)") + return true + } + + + func applicationWillTerminate(_ aNotification: Notification) { + print("applicationWillTerminate") + viewController.teardown() + } + +} + + + diff --git a/Imago/Assets/Assets.xcassets/AppIcon.appiconset/Asset 9@1024.png b/Imago/Assets/Assets.xcassets/AppIcon.appiconset/Asset 9@1024.png new file mode 100644 index 0000000000000000000000000000000000000000..2c1acdef42c6a3ea7758b29b6b65363aaa1afac4 GIT binary patch literal 118351 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

Ge*sy!)0#8F6iQyHeUurgv~3iO?zI@v{c2T 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

`j^m_N!_j|`TY>GwYoRbG!X>FWY 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 Yw{)>utZN62AmES6O?8DLIpZh)56(BtI{*Lx literal 0 HcmV?d00001 diff --git a/Imago/Assets/Assets.xcassets/AppIcon.appiconset/Asset 9@128.png b/Imago/Assets/Assets.xcassets/AppIcon.appiconset/Asset 9@128.png new file mode 100644 index 0000000000000000000000000000000000000000..bf400731cb81c31b8a28307eb8bcb057167edeb0 GIT binary patch literal 11687 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 pKMM)39<_SyypV|_ALPbu`B?9-2HQhjs&v9 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 Y2h=e}7z6utbpQYW07*qoM6N<$f>Dg0g#Z8m literal 0 HcmV?d00001 diff --git a/Imago/Assets/Assets.xcassets/AppIcon.appiconset/Asset 9@32.png b/Imago/Assets/Assets.xcassets/AppIcon.appiconset/Asset 9@32.png new file mode 100644 index 0000000000000000000000000000000000000000..b47b223b60ba2d57c63fc692aab850a7446f6bcc GIT binary patch literal 950 zcmV;n14;aeP)(Zoc=^&piGF)SjYKZ-`VY-+aouX|6NZHk+_^WJUx zgKcp4JLmg3zjN+6-$N*+*viPO_LdR>R!~|(C{AwMDsAm)Zz-X$5};(w(Zi` z@EZ!x0c1UKA@MC;j1pCO2^8BSp7o9YkCUk>gE9wmp`YD1m+*(5E9&LC%j; z6n+r!*t6=S3V4&z95Vobkj5vJKwlog3q3(mTI5FKOF;b?#nJZyp8WQMRKx=iaSmA( zdJP0U27>$)agc^lien=}IXjMH1&C@Kiqn9h{RHh=0^Rn=ITK6%pU&+1g;8cmF zstjP{5W!uY2*}(w_RY?V&fVwcUCfdt6ksK)G6C0N4z2ny5Y^tbX{@S)xwSqgGo>2= zyp#*UqB4`h>ZsVoB5!Ra;t7lRH+6eN;0 z=YkuFSVO5{`_+=d9FPG(qn!RYh26m{lQjHuP&J_d5dt4j3PRBsayjKqMn->4B0!76?yyKn)S7)7Fr!2L zFlteNAOtHP0}-pnGdAZ6yTd7`R4jBRFRO@+2sk4O5C#+e>bQbyCa%y|dJUkof>3n* Y2h=e}7z6utbpQYW07*qoM6N<$f>Dg0g#Z8m literal 0 HcmV?d00001 diff --git a/Imago/Assets/Assets.xcassets/AppIcon.appiconset/Asset 9@512-1.png b/Imago/Assets/Assets.xcassets/AppIcon.appiconset/Asset 9@512-1.png new file mode 100644 index 0000000000000000000000000000000000000000..9391c14f48768a1fcc7b374c0ed64e952229be7c GIT binary patch literal 56873 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 RpBn%G002ovPDHLkV1hd5x|{$2 literal 0 HcmV?d00001 diff --git a/Imago/Assets/Assets.xcassets/AppIcon.appiconset/Asset 9@512.png b/Imago/Assets/Assets.xcassets/AppIcon.appiconset/Asset 9@512.png new file mode 100644 index 0000000000000000000000000000000000000000..9391c14f48768a1fcc7b374c0ed64e952229be7c GIT binary patch literal 56873 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 RpBn%G002ovPDHLkV1hd5x|{$2 literal 0 HcmV?d00001 diff --git a/Imago/Assets/Assets.xcassets/AppIcon.appiconset/Asset 9@64.png b/Imago/Assets/Assets.xcassets/AppIcon.appiconset/Asset 9@64.png new file mode 100644 index 0000000000000000000000000000000000000000..a889ec7712e2f3a206af7b427092fd5d92e5acce GIT binary patch literal 5709 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 Is^5tFABPG7wg3PC literal 0 HcmV?d00001 diff --git a/Imago/Assets/Assets.xcassets/AppIcon.appiconset/Contents.json b/Imago/Assets/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..b90134b --- /dev/null +++ b/Imago/Assets/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,68 @@ +{ + "images" : [ + { + "filename" : "Asset 9@16.png", + "idiom" : "mac", + "scale" : "1x", + "size" : "16x16" + }, + { + "filename" : "Asset 9@32.png", + "idiom" : "mac", + "scale" : "2x", + "size" : "16x16" + }, + { + "filename" : "Asset 9@32-1.png", + "idiom" : "mac", + "scale" : "1x", + "size" : "32x32" + }, + { + "filename" : "Asset 9@64.png", + "idiom" : "mac", + "scale" : "2x", + "size" : "32x32" + }, + { + "filename" : "Asset 9@128.png", + "idiom" : "mac", + "scale" : "1x", + "size" : "128x128" + }, + { + "filename" : "Asset 9@256-1.png", + "idiom" : "mac", + "scale" : "2x", + "size" : "128x128" + }, + { + "filename" : "Asset 9@256.png", + "idiom" : "mac", + "scale" : "1x", + "size" : "256x256" + }, + { + "filename" : "Asset 9@512-1.png", + "idiom" : "mac", + "scale" : "2x", + "size" : "256x256" + }, + { + "filename" : "Asset 9@512.png", + "idiom" : "mac", + "scale" : "1x", + "size" : "512x512" + }, + { + "filename" : "Asset 9@1024.png", + "idiom" : "mac", + "scale" : "2x", + "size" : "512x512" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Imago/Assets/Assets.xcassets/CameraIcon.imageset/Asset 7.png b/Imago/Assets/Assets.xcassets/CameraIcon.imageset/Asset 7.png new file mode 100644 index 0000000000000000000000000000000000000000..803a80427379606d8c67d24242189249efd04d51 GIT binary patch literal 1335 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 WD1J!$w#Lqrl_Pri5*qLk^nU=~FK-wC literal 0 HcmV?d00001 diff --git a/Imago/Assets/Assets.xcassets/ReloadIcon.imageset/Bold-L@2x.png b/Imago/Assets/Assets.xcassets/ReloadIcon.imageset/Bold-L@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..8ad75acaeeb2cab183764cd410a3d5796a59cf13 GIT binary patch literal 4114 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 literal 0 HcmV?d00001 diff --git a/Imago/Assets/Assets.xcassets/ReloadIcon.imageset/Bold-L@3x.png b/Imago/Assets/Assets.xcassets/ReloadIcon.imageset/Bold-L@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..7a989c7661e37a9cb77452e8e2174da55106e2c5 GIT binary patch literal 6281 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 QZvk}e>P=*sp5v4M0qP6cV*mgE literal 0 HcmV?d00001 diff --git a/Imago/Assets/Assets.xcassets/ReloadIcon.imageset/Contents.json b/Imago/Assets/Assets.xcassets/ReloadIcon.imageset/Contents.json new file mode 100644 index 0000000..d774646 --- /dev/null +++ b/Imago/Assets/Assets.xcassets/ReloadIcon.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "Bold-L.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Bold-L@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Bold-L@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Imago/Assets/Base.lproj/Main.storyboard b/Imago/Assets/Base.lproj/Main.storyboard new file mode 100644 index 0000000..230cdf5 --- /dev/null +++ b/Imago/Assets/Base.lproj/Main.storyboard @@ -0,0 +1,683 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Default + + + + + + + Left to Right + + + + + + + Right to Left + + + + + + + + + + + Default + + + + + + + Left to Right + + + + + + + Right to Left + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Imago/Assets/Imago.entitlements b/Imago/Assets/Imago.entitlements new file mode 100644 index 0000000..0c67376 --- /dev/null +++ b/Imago/Assets/Imago.entitlements @@ -0,0 +1,5 @@ + + + + + diff --git a/Imago/Camera/Camera.swift b/Imago/Camera/Camera.swift new file mode 100644 index 0000000..b91eed5 --- /dev/null +++ b/Imago/Camera/Camera.swift @@ -0,0 +1,83 @@ +// +// Camera.swift +// Imago +// +// Created by Nolan Brown on 7/3/20. +// + +import SwiftUI + +import Foundation +import Cocoa +/* + + The Camera class is expected to be subclassed to interface with a particular brand or type of camera and return frames + */ + +extension Notification.Name { + static let didLoadCameras = Notification.Name("didLoadCameras") + static let cameraAddedEvent = Notification.Name("cameraAddedEvent") +} + +class Camera : Hashable, Identifiable, ObservableObject, CustomDebugStringConvertible { + var currentFrame: Frame? + + // For furture use when we can save previously used Cameras + // Stores if a camera is connected to the applications + @Published var connected: Bool = true + + // Is the camera sending frames? + @Published var streaming: Bool = false + + var name: String = "Camera" + var model: String = "Model" + var identifier: String = "Serial Number" + + var isActive: Bool = false + + var filter: CameraFilter = CameraFilter.None + + + var debugDescription: String { + return "\(String(describing: type(of: self)))(id:\(identifier) name:'\(name)')" + } + + var didReceiveFrame: ((Frame, Camera)->Void)? + + + //var model: String + static func == (lhs: Camera, rhs: Camera) -> Bool { + return lhs.identifier == rhs.identifier + } + public func hash(into hasher: inout Hasher) { + return hasher.combine(ObjectIdentifier(self).hashValue) + } + + + func setActive(active: Bool) { + if active == true { + streaming = true + } + else if active == false { + streaming = false + } + isActive = active + } + + + func setFrame(_ frame: Frame) { + if self.isActive { + + DispatchQueue.main.async { [unowned self, frame] in + self.currentFrame = frame + //self.previewImage = image + + self.didReceiveFrame?(frame, self) + } + + } + } + +} + + diff --git a/Imago/Camera/CameraFilter.swift b/Imago/Camera/CameraFilter.swift new file mode 100644 index 0000000..dcf0bec --- /dev/null +++ b/Imago/Camera/CameraFilter.swift @@ -0,0 +1,77 @@ +// +// CameraFilter.swift +// Imago +// +// Created by Nolan Brown on 8/7/20. +// Copyright © 2020 Nolan Brown. All rights reserved. +// + +import Foundation +import CoreImage + +struct CameraFilter: OptionSet, Hashable, CustomStringConvertible { + static let Options: [CameraFilter] = [.Mirror, .Pixellate, .Thermalize] + + static let Mirror = CameraFilter(rawValue: 1 << 0) + static let Pixellate = CameraFilter(rawValue: 1 << 1) + static let Thermalize = CameraFilter(rawValue: 1 << 2) + + static let None = CameraFilter([]) + + let rawValue: Int + + + var hashValue: Int { + return self.rawValue + } + + + + static var debugDescriptions: [CameraFilter:String] = { + var descriptions = [CameraFilter:String]() + descriptions[.None] = "--" + descriptions[.Mirror] = "Mirror" + descriptions[.Pixellate] = "Pixellate" + descriptions[.Thermalize] = "Thermalize" + return descriptions + }() + + public var keyDescriptions: String { + var result = [String]() + for key in CameraFilter.debugDescriptions.keys { + guard self.contains(key), + let description = CameraFilter.debugDescriptions[key] + else { continue } + result.append(description) + } + return "\(result)" + } + + public var description: String { + return "CameraFilter(rawValue: \(self.rawValue)) \(self.keyDescriptions)" + } + + public func processImage(_ image: CIImage) -> CIImage { + var processedImage: CIImage = image + + if self.contains(.Pixellate) { + if let newImage = processedImage.pixellate(scale: 15) { + processedImage = newImage + } + } + if self.contains(.Thermalize) { + if let newImage = processedImage.thermalize() { + processedImage = newImage + } + } + if self.contains(.Mirror) { + if let newImage = processedImage.mirror() { + processedImage = newImage + } + + } + return processedImage + } + + +} diff --git a/Imago/Camera/Canon/CanonCamera.swift b/Imago/Camera/Canon/CanonCamera.swift new file mode 100644 index 0000000..97d8987 --- /dev/null +++ b/Imago/Camera/Canon/CanonCamera.swift @@ -0,0 +1,556 @@ +// +// CanonCamera.swift +// Imago +// +// Created by Nolan Brown on 7/8/20. +// Copyright © 2020 Nolan Brown. All rights reserved. +// + +import Foundation + +struct CommandResult : CustomDebugStringConvertible { + let rawResult: EdsError + let property: CanonProperty? + + init() { + self.init(EdsError(EDS_ERR_OK), nil) + } + + init(_ rawResult: EdsError) { + self.init(rawResult, nil) + } + + init(_ rawResult: EdsError, _ property: CanonProperty?) { + self.rawResult = rawResult + self.property = property + + } + init(_ property: CanonProperty) { + self.init(EdsError(EDS_ERR_OK), property) + } + + func withProperty(_ property: CanonProperty) -> CommandResult { + return CommandResult(self.rawResult, property) + } + + + func isOK() -> Bool { + let isOK = (rawResult == EdsError(EDS_ERR_OK)) + return isOK + } + + // For readability in Command.perform() functions + func isError() -> Bool { + return !isOK() + } + + func getResultDescription() -> String { + return DescriptionForEdsError(self.rawResult) + } + + var debugDescription : String { + return self.description + } + + var description: String { + if self.property != nil { + return "CommandResult(result:\(self.getResultDescription()) property:\(self.property!))" + } + + return "CommandResult(\(self.getResultDescription()))" + } + +} + +class CanonCamera : Camera { + fileprivate var _deviceInfo: EdsDeviceInfo? = nil + fileprivate var _cameraRef: EdsCameraRef? = nil + fileprivate var _serialNumber: String? = nil + + fileprivate var _deviceDescription: String + fileprivate var _rawPort: String + + fileprivate var _fps: Int = 30 + fileprivate var _sequence: UInt64 = 0 + + fileprivate var _commandQueue: DispatchQueue = DispatchQueue(label: "com.nolanbrown.imago.CanonCamera.commands") //, qos: .userInteractive) + + private lazy var _signal: DispatchSourceUserDataAdd = { + let signal = DispatchSource.makeUserDataAddSource(queue: self._commandQueue) + signal.setEventHandler{ + // set frame here + + } + return signal + }() + + private lazy var _newFrameTimer: DispatchSourceTimer = { + let interval: DispatchTimeInterval = DispatchTimeInterval.nanoseconds(Int(NSEC_PER_SEC / UInt64(Double(self._fps)))) + let timer = DispatchSource.makeTimerSource(queue: self._commandQueue) + timer.setEventHandler(handler: self.newFrameTimerHandler) + timer.setCancelHandler(handler: self.timerCancelledHandler) + timer.schedule(deadline: .now(), repeating: interval) + return timer + }() + + private lazy var _stayAliveTimer: DispatchSourceTimer = { + let interval: DispatchTimeInterval = DispatchTimeInterval.seconds(60) + let timer = DispatchSource.makeTimerSource(queue: self._commandQueue) + timer.setEventHandler(handler: self.stayAliveTimerHandler) + timer.schedule(deadline: .now() + interval , repeating: interval) + return timer + }() + + + fileprivate var _id: String? + deinit { + print("deinit CanonCamera") + EdsRelease(_cameraRef) + } + +// required init(from decoder: Decoder) throws { +// super.init(from: decoder) +// let values = try decoder.container(keyedBy: CodingKeys.self) +// name = try values.decode(String.self, forKey: .name) +// model = try values.decode(String.self, forKey: .model) +// identifier = try values.decode(String.self, forKey: .identifier) +// +// } + + init(cameraRef:EdsCameraRef, deviceInfo: EdsDeviceInfo){ + _cameraRef = cameraRef + _deviceInfo = deviceInfo + + EdsRetain(_cameraRef) + + _deviceDescription = withUnsafePointer(to: _deviceInfo!.szDeviceDescription) { + String(cString: UnsafeRawPointer($0).assumingMemoryBound(to: CChar.self)) + } + _rawPort = withUnsafePointer(to: _deviceInfo!.szPortName) { + String(cString: UnsafeRawPointer($0).assumingMemoryBound(to: CChar.self)) + } + super.init() + + name = _deviceDescription + identifier = _rawPort + if let serial = self.serialNumber() { + identifier = serial + } + + } + + internal func getCameraRef()->EdsCameraRef?{ + return _cameraRef + } + + func serialNumber() -> String? { + if _serialNumber != nil { + return _serialNumber + } + if isActive { + let prop = CanonProperty.forID(EdsPropertyID(kEdsPropID_BodyIDEx)) + let propValue = getEdsPropertyValue(prop) + _serialNumber = propValue?.stringValue + return _serialNumber + } + return nil + } + +// override func identifier() -> String { +// return _rawPort +// } +// +// override func name() -> String { +// return _deviceDescription +// +// } + + + + override func setActive(active: Bool) { + var becameActive = active + + if active { + _commandQueue.sync { [unowned self] in + if self.registerEdsEventHandlers() { + if self.openSession() { + dlog("open session") + + if self.setEvfMode() && self.setOutputDevice(toPC: true) { + self._newFrameTimer.resume() + self._stayAliveTimer.resume() + becameActive = true + return + + } + } + } + becameActive = false + } + } + else { + _commandQueue.sync { [unowned self] in + self._newFrameTimer.suspend() + self._stayAliveTimer.suspend() + + self.removeEdsEventHandlers() + if self.setOutputDevice(toPC: false) { + if self.closeSession() { + dlog("Closed Session") + } + else { + dlog("Session didn't close") + } + } + else { + dlog("Output device wasn't reset") + } + } + } + + super.setActive(active: becameActive) + + } + + + func stayAliveTimerHandler() { + if self.setOutputDevice(toPC: true) { + dlog("Stay alive timer set output device") + } + else { + dlog("Stay alive timer failed to output device") + } + } + + func newFrameTimerHandler() { + guard let frame = loadFrame() else { + return + } + self.setFrame(frame) + flog(frame, "set frame for next request") + + } + + func timerCancelledHandler() { + dlog("Timer Cancelled") + } + + + +} + +extension CanonCamera { + private func openSession() -> Bool { + let result = CommandResult(EdsOpenSession(_cameraRef)) + if result.isError() { + if result.rawResult == EDS_ERR_SESSION_ALREADY_OPEN { + return true + } + return false + } + return true + } + + private func closeSession() -> Bool { + let result = CommandResult(EdsCloseSession(_cameraRef)) + if result.isError() { + if result.rawResult == EDS_ERR_SESSION_NOT_OPEN { + return true + } + return false + } + return true + } + + private func setEvfMode() -> Bool { + var evfMode: EdsUInt32 = 0 + + let result = CommandResult(EdsGetPropertyData(_cameraRef, EdsPropertyID(kEdsPropID_Evf_Mode), 0, EdsUInt32(MemoryLayout.size), &evfMode)) + if result.isError() { + return false + } + + if(evfMode == 0) + { + evfMode = 1 + // Set to the camera. + let setResult = CommandResult(EdsSetPropertyData(_cameraRef, EdsPropertyID(kEdsPropID_Evf_Mode), 0, EdsUInt32(MemoryLayout.size), &evfMode)) + if setResult.isError() { + return false + } + } + return true + } + + private func setOutputDevice(toPC: Bool = true) -> Bool { + var device: EdsUInt32 = 0 + + // Get Output Device + let outputDevicePropertyID = EdsPropertyID(kEdsPropID_Evf_OutputDevice) + let result = CommandResult(EdsGetPropertyData(_cameraRef, outputDevicePropertyID, 0, EdsUInt32(MemoryLayout.size), &device)) + if result.isError() { + dlog("Get Output Device Failed: \(result)") + + return false + } + + // Set the current output device. + if toPC { + device |= kEdsEvfOutputDevice_PC.rawValue + } + else { + device &= ~kEdsEvfOutputDevice_PC.rawValue + } + + let setResult = CommandResult(EdsSetPropertyData(_cameraRef, outputDevicePropertyID, 0, EdsUInt32(MemoryLayout.size), &device)) + if setResult.isError() { + dlog("Set Output Device Failed: \(setResult)") + return false + } + else { + dlog("Set Output Device == \(device)") + } + return true + } + + func loadFrame() -> Frame? { + let starts = mach_absolute_time() + + var streamRef: EdsStreamRef? = nil + var imageRef: EdsEvfImageRef? = nil + let bufferSize:EdsUInt64 = 0 + + // Create memory stream. + let createMemoryStreamResult = CommandResult(EdsCreateMemoryStream(bufferSize, &streamRef)) + if createMemoryStreamResult.isError() { + return nil + } + + // Create EvfImageRef. + let createImageRefResult = CommandResult(EdsCreateEvfImageRef(streamRef, &imageRef)) + if createImageRefResult.isError() { + return nil + } + + let downloadImageResult = CommandResult(EdsDownloadEvfImage(_cameraRef, imageRef)) + if downloadImageResult.isError() { + return nil + } + + // Get Image Details + var imageSize: EdsUInt64 = 0 + var pImage: UnsafeMutableRawPointer? = nil + + EdsGetPointer(streamRef, &pImage) + EdsGetLength(streamRef, &imageSize) + + if(imageRef != nil) + { + EdsRelease(imageRef) + } + + if(streamRef != nil) + { + EdsRelease(streamRef) + } + + let endget = mach_absolute_time() + + if imageSize > 0 { + let startdcom = mach_absolute_time() + if var imgData = Frame.decompress(UnsafeMutableRawPointer(pImage)!, length: UInt(imageSize)) { + let endcom = mach_absolute_time() + //imgData.id = UUID().uuidString + imgData.timestamp = starts + imgData.sequence = _sequence + + _sequence += 1 + + flog(imgData, "received sdk response", timestamp: endget) + flog(imgData, "decompress start", timestamp: startdcom) + flog(imgData, "decompress end", timestamp: endcom) + return imgData + } + + } + + + return nil + } + + + @discardableResult + private func registerEdsEventHandlers() -> Bool { + let camera: UnsafeMutableRawPointer = unsafeBitCast(self, to: UnsafeMutableRawPointer.self) + let result = CommandResult(EdsSetCameraStateEventHandler(_cameraRef, EdsStateEvent(kEdsStateEvent_All), edsHandleStateEvent, camera)) + if result.isError() { + return false + } + return true + } + + @discardableResult + private func removeEdsEventHandlers() -> Bool { + let result = CommandResult(EdsSetCameraStateEventHandler(_cameraRef, EdsStateEvent(kEdsStateEvent_All), nil, nil)) + if result.isError() { + return false + } + return true + } + + + +} + + +let edsHandleStateEvent : EdsStateEventHandler = { ( inEvent, inParam, inContext)->EdsError in + + var error = EdsError(EDS_ERR_OK) + + dlog("handleStateEvent \(inEvent)") + switch(inEvent) + { + case EdsStateEvent(kEdsStateEvent_Shutdown): + dlog("kEdsStateEvent_Shutdown") + break; + + case EdsStateEvent(kEdsStateEvent_WillSoonShutDown): + dlog("kEdsStateEvent_WillSoonShutDown") + break; + + case EdsStateEvent(kEdsStateEvent_ShutDownTimerUpdate): + dlog("kEdsStateEvent_ShutDownTimerUpdate") + break; + + default: + break; + } + return error; +} + +extension CanonCamera { + private func getEdsPropertyValue(_ property: CanonProperty) -> CanonProperty? { + guard let cameraRef = _cameraRef else { + return nil + } + var propertyWithValue: CanonProperty? + let propertyID = property.id + + var propType: EdsDataType = kEdsDataType_Unknown + var propSize: EdsUInt32 = 0 + + let propertySizeResult = CommandResult(EdsGetPropertySize(cameraRef, propertyID, 0, &propType, &propSize)) + if propertySizeResult.isOK() { + if(propType == kEdsDataType_UInt32 || propType == kEdsDataType_Int32 || propertyID == EdsPropertyID(kEdsPropID_Evf_OutputDevice)) + { + var uintData: EdsUInt32 = 0 + let getPropertyResult = CommandResult(EdsGetPropertyData(cameraRef, propertyID, 0, EdsUInt32(MemoryLayout.size), &uintData)) + + if getPropertyResult.isOK() { + propertyWithValue = property.withValue(uintData) + } + } + else if(propType == kEdsDataType_String) + { + var stringData:[EdsChar] = Array(repeating: 0, count: 256) + + let getPropertyResult = CommandResult(EdsGetPropertyData(cameraRef, propertyID, 0, EdsUInt32(MemoryLayout.size*256), &stringData)) + if getPropertyResult.isOK() { + let propertyStringValue : String = String(cString: stringData,encoding: String.Encoding.ascii)! + propertyWithValue = property.withValue(propertyStringValue) + } + } + } + return propertyWithValue + } +} + +extension CanonCamera { + static func setup() { + var result = CommandResult(EdsInitializeSDK()) + if result.isError() { + dlog("Error creating SDK connection: \(result)") + } + + result = CommandResult(EdsSetCameraAddedHandler(edsHandleCameraAddEvent, nil)) + if result.isError() { + dlog("Error adding Camera Added Handler: \(result)") + } + } + static func teardown() { + + var result = CommandResult(EdsSetCameraAddedHandler(nil, nil)) + if result.isError() { + dlog("Error removing Camera Added Handler: \(result)") + } + + result = CommandResult(EdsTerminateSDK()) + if result.isError() { + dlog("Error terminating SDK connection: \(result)") + } + } + + static func loadCameras() { + DispatchQueue.main.async { + var cameraList = nil as EdsCameraListRef? + var result = CommandResult() + result = CommandResult(EdsGetCameraList(&cameraList)) + if result.isError() { + return //nil + } + var cameraCount = 0 as EdsUInt32 + result = CommandResult(EdsGetChildCount(cameraList, &cameraCount)) + if result.isError() { + if cameraList != nil { + EdsRelease(cameraList) + } + return //nil + } + + var availableCameras: [CanonCamera] = [] + for i in 0.. EdsError in + + var error = EdsError(EDS_ERR_OK) + + dlog("Camera added..") + Thread.sleep(forTimeInterval:0.1) + CanonCamera.loadCameras() + + return error; +} + diff --git a/Imago/Camera/Canon/CanonCommandErrors.swift b/Imago/Camera/Canon/CanonCommandErrors.swift new file mode 100644 index 0000000..49e64a9 --- /dev/null +++ b/Imago/Camera/Canon/CanonCommandErrors.swift @@ -0,0 +1,146 @@ +// +// CanonCommandErrors.swift +// Imago +// +// Created by Nolan Brown on 7/8/20. +// Copyright © 2020 Nolan Brown. All rights reserved. +// + +import Foundation + +func DescriptionForEdsError(_ error: EdsError) -> String { + switch error { + case EdsError(EDS_ERR_OK): return "EDS_ERR_OK" + case EdsError(EDS_ERR_UNIMPLEMENTED): return "EDS_ERR_UNIMPLEMENTED" + case EdsError(EDS_ERR_INTERNAL_ERROR): return "EDS_ERR_INTERNAL_ERROR" + case EdsError(EDS_ERR_MEM_ALLOC_FAILED): return "EDS_ERR_MEM_ALLOC_FAILED" + case EdsError(EDS_ERR_MEM_FREE_FAILED): return "EDS_ERR_MEM_FREE_FAILED" + case EdsError(EDS_ERR_OPERATION_CANCELLED): return "EDS_ERR_OPERATION_CANCELLED" + case EdsError(EDS_ERR_INCOMPATIBLE_VERSION): return "EDS_ERR_INCOMPATIBLE_VERSION" + case EdsError(EDS_ERR_NOT_SUPPORTED): return "EDS_ERR_NOT_SUPPORTED" + case EdsError(EDS_ERR_UNEXPECTED_EXCEPTION): return "EDS_ERR_UNEXPECTED_EXCEPTION" + case EdsError(EDS_ERR_PROTECTION_VIOLATION): return "EDS_ERR_PROTECTION_VIOLATION" + case EdsError(EDS_ERR_MISSING_SUBCOMPONENT): return "EDS_ERR_MISSING_SUBCOMPONENT" + case EdsError(EDS_ERR_SELECTION_UNAVAILABLE): return "EDS_ERR_SELECTION_UNAVAILABLE" + case EdsError(EDS_ERR_FILE_IO_ERROR): return "EDS_ERR_FILE_IO_ERROR" + case EdsError(EDS_ERR_FILE_TOO_MANY_OPEN): return "EDS_ERR_FILE_TOO_MANY_OPEN" + case EdsError(EDS_ERR_FILE_NOT_FOUND): return "EDS_ERR_FILE_NOT_FOUND" + case EdsError(EDS_ERR_FILE_OPEN_ERROR): return "EDS_ERR_FILE_OPEN_ERROR" + case EdsError(EDS_ERR_FILE_CLOSE_ERROR): return "EDS_ERR_FILE_CLOSE_ERROR" + case EdsError(EDS_ERR_FILE_SEEK_ERROR): return "EDS_ERR_FILE_SEEK_ERROR" + case EdsError(EDS_ERR_FILE_TELL_ERROR): return "EDS_ERR_FILE_TELL_ERROR" + case EdsError(EDS_ERR_FILE_READ_ERROR): return "EDS_ERR_FILE_READ_ERROR" + case EdsError(EDS_ERR_FILE_WRITE_ERROR): return "EDS_ERR_FILE_WRITE_ERROR" + case EdsError(EDS_ERR_FILE_PERMISSION_ERROR): return "EDS_ERR_FILE_PERMISSION_ERROR" + case EdsError(EDS_ERR_FILE_DISK_FULL_ERROR): return "EDS_ERR_FILE_DISK_FULL_ERROR" + case EdsError(EDS_ERR_FILE_ALREADY_EXISTS): return "EDS_ERR_FILE_ALREADY_EXISTS" + case EdsError(EDS_ERR_FILE_FORMAT_UNRECOGNIZED): return "EDS_ERR_FILE_FORMAT_UNRECOGNIZED" + case EdsError(EDS_ERR_FILE_DATA_CORRUPT): return "EDS_ERR_FILE_DATA_CORRUPT" + case EdsError(EDS_ERR_FILE_NAMING_NA): return "EDS_ERR_FILE_NAMING_NA" + case EdsError(EDS_ERR_DIR_NOT_FOUND): return "EDS_ERR_DIR_NOT_FOUND" + case EdsError(EDS_ERR_DIR_IO_ERROR): return "EDS_ERR_DIR_IO_ERROR" + case EdsError(EDS_ERR_DIR_ENTRY_NOT_FOUND): return "EDS_ERR_DIR_ENTRY_NOT_FOUND" + case EdsError(EDS_ERR_DIR_ENTRY_EXISTS): return "EDS_ERR_DIR_ENTRY_EXISTS" + case EdsError(EDS_ERR_DIR_NOT_EMPTY): return "EDS_ERR_DIR_NOT_EMPTY" + case EdsError(EDS_ERR_PROPERTIES_UNAVAILABLE): return "EDS_ERR_PROPERTIES_UNAVAILABLE" + case EdsError(EDS_ERR_PROPERTIES_MISMATCH): return "EDS_ERR_PROPERTIES_MISMATCH" + case EdsError(EDS_ERR_PROPERTIES_NOT_LOADED): return "EDS_ERR_PROPERTIES_NOT_LOADED" + case EdsError(EDS_ERR_INVALID_PARAMETER): return "EDS_ERR_INVALID_PARAMETER" + case EdsError(EDS_ERR_INVALID_HANDLE): return "EDS_ERR_INVALID_HANDLE" + case EdsError(EDS_ERR_INVALID_POINTER): return "EDS_ERR_INVALID_POINTER" + case EdsError(EDS_ERR_INVALID_INDEX): return "EDS_ERR_INVALID_INDEX" + case EdsError(EDS_ERR_INVALID_LENGTH): return "EDS_ERR_INVALID_LENGTH" + case EdsError(EDS_ERR_INVALID_FN_POINTER): return "EDS_ERR_INVALID_FN_POINTER" + case EdsError(EDS_ERR_INVALID_SORT_FN): return "EDS_ERR_INVALID_SORT_FN" + case EdsError(EDS_ERR_DEVICE_NOT_FOUND): return "EDS_ERR_DEVICE_NOT_FOUND" + case EdsError(EDS_ERR_DEVICE_BUSY): return "EDS_ERR_DEVICE_BUSY" + case EdsError(EDS_ERR_DEVICE_INVALID): return "EDS_ERR_DEVICE_INVALID" + case EdsError(EDS_ERR_DEVICE_EMERGENCY): return "EDS_ERR_DEVICE_EMERGENCY" + case EdsError(EDS_ERR_DEVICE_MEMORY_FULL): return "EDS_ERR_DEVICE_MEMORY_FULL" + case EdsError(EDS_ERR_DEVICE_INTERNAL_ERROR): return "EDS_ERR_DEVICE_INTERNAL_ERROR" + case EdsError(EDS_ERR_DEVICE_INVALID_PARAMETER): return "EDS_ERR_DEVICE_INVALID_PARAMETER" + case EdsError(EDS_ERR_DEVICE_NO_DISK): return "EDS_ERR_DEVICE_NO_DISK" + case EdsError(EDS_ERR_DEVICE_DISK_ERROR): return "EDS_ERR_DEVICE_DISK_ERROR" + case EdsError(EDS_ERR_DEVICE_CF_GATE_CHANGED): return "EDS_ERR_DEVICE_CF_GATE_CHANGED" + case EdsError(EDS_ERR_DEVICE_DIAL_CHANGED): return "EDS_ERR_DEVICE_DIAL_CHANGED" + case EdsError(EDS_ERR_DEVICE_NOT_INSTALLED): return "EDS_ERR_DEVICE_NOT_INSTALLED" + case EdsError(EDS_ERR_DEVICE_STAY_AWAKE): return "EDS_ERR_DEVICE_STAY_AWAKE" + case EdsError(EDS_ERR_DEVICE_NOT_RELEASED): return "EDS_ERR_DEVICE_NOT_RELEASED" + case EdsError(EDS_ERR_STREAM_IO_ERROR): return "EDS_ERR_STREAM_IO_ERROR" + case EdsError(EDS_ERR_STREAM_NOT_OPEN): return "EDS_ERR_STREAM_NOT_OPEN" + case EdsError(EDS_ERR_STREAM_ALREADY_OPEN): return "EDS_ERR_STREAM_ALREADY_OPEN" + case EdsError(EDS_ERR_STREAM_OPEN_ERROR): return "EDS_ERR_STREAM_OPEN_ERROR" + case EdsError(EDS_ERR_STREAM_CLOSE_ERROR): return "EDS_ERR_STREAM_CLOSE_ERROR" + case EdsError(EDS_ERR_STREAM_SEEK_ERROR): return "EDS_ERR_STREAM_SEEK_ERROR" + case EdsError(EDS_ERR_STREAM_TELL_ERROR): return "EDS_ERR_STREAM_TELL_ERROR" + case EdsError(EDS_ERR_STREAM_READ_ERROR): return "EDS_ERR_STREAM_READ_ERROR" + case EdsError(EDS_ERR_STREAM_WRITE_ERROR): return "EDS_ERR_STREAM_WRITE_ERROR" + case EdsError(EDS_ERR_STREAM_PERMISSION_ERROR): return "EDS_ERR_STREAM_PERMISSION_ERROR" + case EdsError(EDS_ERR_STREAM_COULDNT_BEGIN_THREAD): return "EDS_ERR_STREAM_COULDNT_BEGIN_THREAD" + case EdsError(EDS_ERR_STREAM_BAD_OPTIONS): return "EDS_ERR_STREAM_BAD_OPTIONS" + case EdsError(EDS_ERR_STREAM_END_OF_STREAM): return "EDS_ERR_STREAM_END_OF_STREAM" + case EdsError(EDS_ERR_COMM_PORT_IS_IN_USE): return "EDS_ERR_COMM_PORT_IS_IN_USE" + case EdsError(EDS_ERR_COMM_DISCONNECTED): return "EDS_ERR_COMM_DISCONNECTED" + case EdsError(EDS_ERR_COMM_DEVICE_INCOMPATIBLE): return "EDS_ERR_COMM_DEVICE_INCOMPATIBLE" + case EdsError(EDS_ERR_COMM_BUFFER_FULL): return "EDS_ERR_COMM_BUFFER_FULL" + case EdsError(EDS_ERR_COMM_USB_BUS_ERR): return "EDS_ERR_COMM_USB_BUS_ERR" + case EdsError(EDS_ERR_USB_DEVICE_LOCK_ERROR): return "EDS_ERR_USB_DEVICE_LOCK_ERROR" + case EdsError(EDS_ERR_USB_DEVICE_UNLOCK_ERROR): return "EDS_ERR_USB_DEVICE_UNLOCK_ERROR" + case EdsError(EDS_ERR_STI_UNKNOWN_ERROR): return "EDS_ERR_STI_UNKNOWN_ERROR" + case EdsError(EDS_ERR_STI_INTERNAL_ERROR): return "EDS_ERR_STI_INTERNAL_ERROR" + case EdsError(EDS_ERR_STI_DEVICE_CREATE_ERROR): return "EDS_ERR_STI_DEVICE_CREATE_ERROR" + case EdsError(EDS_ERR_STI_DEVICE_RELEASE_ERROR): return "EDS_ERR_STI_DEVICE_RELEASE_ERROR" + case EdsError(EDS_ERR_DEVICE_NOT_LAUNCHED): return "EDS_ERR_DEVICE_NOT_LAUNCHED" + case EdsError(EDS_ERR_ENUM_NA): return "EDS_ERR_ENUM_NA" + case EdsError(EDS_ERR_INVALID_FN_CALL): return "EDS_ERR_INVALID_FN_CALL" + case EdsError(EDS_ERR_HANDLE_NOT_FOUND): return "EDS_ERR_HANDLE_NOT_FOUND" + case EdsError(EDS_ERR_INVALID_ID): return "EDS_ERR_INVALID_ID" + case EdsError(EDS_ERR_WAIT_TIMEOUT_ERROR): return "EDS_ERR_WAIT_TIMEOUT_ERROR" + case EdsError(EDS_ERR_SESSION_NOT_OPEN): return "EDS_ERR_SESSION_NOT_OPEN" + case EdsError(EDS_ERR_INVALID_TRANSACTIONID): return "EDS_ERR_INVALID_TRANSACTIONID" + case EdsError(EDS_ERR_INCOMPLETE_TRANSFER): return "EDS_ERR_INCOMPLETE_TRANSFER" + case EdsError(EDS_ERR_INVALID_STRAGEID): return "EDS_ERR_INVALID_STRAGEID" + case EdsError(EDS_ERR_DEVICEPROP_NOT_SUPPORTED): return "EDS_ERR_DEVICEPROP_NOT_SUPPORTED" + case EdsError(EDS_ERR_INVALID_OBJECTFORMATCODE): return "EDS_ERR_INVALID_OBJECTFORMATCODE" + case EdsError(EDS_ERR_SELF_TEST_FAILED): return "EDS_ERR_SELF_TEST_FAILED" + case EdsError(EDS_ERR_PARTIAL_DELETION): return "EDS_ERR_PARTIAL_DELETION" + case EdsError(EDS_ERR_SPECIFICATION_BY_FORMAT_UNSUPPORTED): return "EDS_ERR_SPECIFICATION_BY_FORMAT_UNSUPPORTED" + case EdsError(EDS_ERR_NO_VALID_OBJECTINFO): return "EDS_ERR_NO_VALID_OBJECTINFO" + case EdsError(EDS_ERR_INVALID_CODE_FORMAT): return "EDS_ERR_INVALID_CODE_FORMAT" + case EdsError(EDS_ERR_UNKNOWN_VENDOR_CODE): return "EDS_ERR_UNKNOWN_VENDOR_CODE" + case EdsError(EDS_ERR_CAPTURE_ALREADY_TERMINATED): return "EDS_ERR_CAPTURE_ALREADY_TERMINATED" + case EdsError(EDS_ERR_PTP_DEVICE_BUSY): return "EDS_ERR_PTP_DEVICE_BUSY" + case EdsError(EDS_ERR_INVALID_PARENTOBJECT): return "EDS_ERR_INVALID_PARENTOBJECT" + case EdsError(EDS_ERR_INVALID_DEVICEPROP_FORMAT): return "EDS_ERR_INVALID_DEVICEPROP_FORMAT" + case EdsError(EDS_ERR_INVALID_DEVICEPROP_VALUE): return "EDS_ERR_INVALID_DEVICEPROP_VALUE" + case EdsError(EDS_ERR_SESSION_ALREADY_OPEN): return "EDS_ERR_SESSION_ALREADY_OPEN" + case EdsError(EDS_ERR_TRANSACTION_CANCELLED): return "EDS_ERR_TRANSACTION_CANCELLED" + case EdsError(EDS_ERR_SPECIFICATION_OF_DESTINATION_UNSUPPORTED): return "EDS_ERR_SPECIFICATION_OF_DESTINATION_UNSUPPORTED" + case EdsError(EDS_ERR_NOT_CAMERA_SUPPORT_SDK_VERSION): return "EDS_ERR_NOT_CAMERA_SUPPORT_SDK_VERSION" + case EdsError(EDS_ERR_UNKNOWN_COMMAND): return "EDS_ERR_UNKNOWN_COMMAND" + case EdsError(EDS_ERR_OPERATION_REFUSED): return "EDS_ERR_OPERATION_REFUSED" + case EdsError(EDS_ERR_LENS_COVER_CLOSE): return "EDS_ERR_LENS_COVER_CLOSE" + case EdsError(EDS_ERR_LOW_BATTERY): return "EDS_ERR_LOW_BATTERY" + case EdsError(EDS_ERR_OBJECT_NOTREADY): return "EDS_ERR_OBJECT_NOTREADY" + case EdsError(EDS_ERR_CANNOT_MAKE_OBJECT): return "EDS_ERR_CANNOT_MAKE_OBJECT" + case EdsError(EDS_ERR_MEMORYSTATUS_NOTREADY): return "EDS_ERR_MEMORYSTATUS_NOTREADY" + case EdsError(EDS_ERR_TAKE_PICTURE_AF_NG): return "EDS_ERR_TAKE_PICTURE_AF_NG" + case EdsError(EDS_ERR_TAKE_PICTURE_RESERVED): return "EDS_ERR_TAKE_PICTURE_RESERVED" + case EdsError(EDS_ERR_TAKE_PICTURE_MIRROR_UP_NG): return "EDS_ERR_TAKE_PICTURE_MIRROR_UP_NG" + case EdsError(EDS_ERR_TAKE_PICTURE_SENSOR_CLEANING_NG): return "EDS_ERR_TAKE_PICTURE_SENSOR_CLEANING_NG" + case EdsError(EDS_ERR_TAKE_PICTURE_SILENCE_NG): return "EDS_ERR_TAKE_PICTURE_SILENCE_NG" + case EdsError(EDS_ERR_TAKE_PICTURE_NO_CARD_NG): return "EDS_ERR_TAKE_PICTURE_NO_CARD_NG" + case EdsError(EDS_ERR_TAKE_PICTURE_CARD_NG): return "EDS_ERR_TAKE_PICTURE_CARD_NG" + case EdsError(EDS_ERR_TAKE_PICTURE_CARD_PROTECT_NG): return "EDS_ERR_TAKE_PICTURE_CARD_PROTECT_NG" + case EdsError(EDS_ERR_TAKE_PICTURE_MOVIE_CROP_NG): return "EDS_ERR_TAKE_PICTURE_MOVIE_CROP_NG" + case EdsError(EDS_ERR_TAKE_PICTURE_STROBO_CHARGE_NG): return "EDS_ERR_TAKE_PICTURE_STROBO_CHARGE_NG" + case EdsError(EDS_ERR_TAKE_PICTURE_NO_LENS_NG): return "EDS_ERR_TAKE_PICTURE_NO_LENS_NG" + case EdsError(EDS_ERR_TAKE_PICTURE_SPECIAL_MOVIE_MODE_NG): return "EDS_ERR_TAKE_PICTURE_SPECIAL_MOVIE_MODE_NG" + case EdsError(EDS_ERR_TAKE_PICTURE_LV_REL_PROHIBIT_MODE_NG): return "EDS_ERR_TAKE_PICTURE_LV_REL_PROHIBIT_MODE_NG" + case EdsError(EDS_ERR_LAST_GENERIC_ERROR_PLUS_ONE): return "EDS_ERR_LAST_GENERIC_ERROR_PLUS_ONE" + + default: + return "UNKNOWN" + + } +} diff --git a/Imago/Camera/Canon/CanonCommandProperty.swift b/Imago/Camera/Canon/CanonCommandProperty.swift new file mode 100644 index 0000000..51e8518 --- /dev/null +++ b/Imago/Camera/Canon/CanonCommandProperty.swift @@ -0,0 +1,176 @@ +// +// CanonCommandProperty.swift +// Imago +// +// Created by Nolan Brown on 7/8/20. +// Copyright © 2020 Nolan Brown. All rights reserved. +// + +import Foundation + +// The majority of these aren't used but are available here in case they're set as part of the EDSDK Property Change Handler +struct CanonProperty : Hashable, Identifiable, CustomDebugStringConvertible { + var id: EdsPropertyID + var name: String + var stringValue: String? + var dataValue: Data? + var dataType: EdsDataType? + var intValue: UInt32? + + init(_ name: String, _ edsPropertyID: EdsPropertyID) { + self.name = name + self.id = edsPropertyID + } + + init(id:EdsPropertyID, name: String, stringValue: String?, dataValue: Data?, dataType: EdsDataType?, intValue: UInt32?) { + self.id = id + self.name = name + self.stringValue = stringValue + self.dataValue = dataValue + self.dataType = dataType + self.intValue = intValue + + } + + static func forID(_ propertyID: EdsPropertyID) -> CanonProperty { + return GetProperyForEdsPropertyID(propertyID) + } + + func withValue(_ data: Data, _ type: EdsDataType) -> CanonProperty { + return CanonProperty(id: self.id, name: self.name, stringValue: nil, dataValue: data, dataType: type, intValue: nil) + } + func withValue(_ str: String) -> CanonProperty { + return CanonProperty(id: self.id, name: self.name, stringValue: str, dataValue: nil, dataType: nil, intValue: nil) + } + func withValue(_ i: UInt32) -> CanonProperty { + return CanonProperty(id: self.id, name: self.name, stringValue: nil, dataValue: nil, dataType: nil, intValue: i) + } + + static func == (lhs: CanonProperty, rhs: CanonProperty) -> Bool { + return lhs.id == rhs.id + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(id) + hasher.combine(stringValue) + hasher.combine(dataValue) + hasher.combine(intValue) + } + var debugDescription: String { + if self.stringValue != nil { + return "CanonProperty(id: \(id) name: \(name) value: \(self.stringValue!))" + } + else if self.dataValue != nil { + return "CanonProperty(id: \(id) name: \(name) value: \(self.dataValue!))" + } + else if self.intValue != nil { + return "CanonProperty(id: \(id) name: \(name) value: \(self.intValue!))" + } + return "CanonProperty(id: \(id) name: \(name) value: nil)" + } +} + +func GetProperyForEdsPropertyID(_ propertyID: EdsPropertyID) -> CanonProperty { + switch propertyID { + case EdsPropertyID(kEdsPropID_Unknown): return CanonProperty("Unknown", EdsPropertyID(kEdsPropID_Unknown)) + case EdsPropertyID(kEdsPropID_ProductName): return CanonProperty("ProductName", EdsPropertyID(kEdsPropID_ProductName)) + case EdsPropertyID(kEdsPropID_OwnerName): return CanonProperty("OwnerName", EdsPropertyID(kEdsPropID_OwnerName)) + case EdsPropertyID(kEdsPropID_MakerName): return CanonProperty("MakerName", EdsPropertyID(kEdsPropID_MakerName)) + case EdsPropertyID(kEdsPropID_DateTime): return CanonProperty("DateTime", EdsPropertyID(kEdsPropID_DateTime)) + case EdsPropertyID(kEdsPropID_FirmwareVersion): return CanonProperty("FirmwareVersion", EdsPropertyID(kEdsPropID_FirmwareVersion)) + case EdsPropertyID(kEdsPropID_BatteryLevel): return CanonProperty("BatteryLevel", EdsPropertyID(kEdsPropID_BatteryLevel)) + case EdsPropertyID(kEdsPropID_CFn): return CanonProperty("CFn", EdsPropertyID(kEdsPropID_CFn)) + case EdsPropertyID(kEdsPropID_SaveTo): return CanonProperty("SaveTo", EdsPropertyID(kEdsPropID_SaveTo)) + case EdsPropertyID(kEdsPropID_CurrentStorage): return CanonProperty("CurrentStorage", EdsPropertyID(kEdsPropID_CurrentStorage)) + case EdsPropertyID(kEdsPropID_CurrentFolder): return CanonProperty("CurrentFolder", EdsPropertyID(kEdsPropID_CurrentFolder)) + case EdsPropertyID(kEdsPropID_BatteryQuality): return CanonProperty("BatteryQuality", EdsPropertyID(kEdsPropID_BatteryQuality)) + case EdsPropertyID(kEdsPropID_BodyIDEx): return CanonProperty("BodyIDEx", EdsPropertyID(kEdsPropID_BodyIDEx)) + case EdsPropertyID(kEdsPropID_HDDirectoryStructure): return CanonProperty("HDDirectoryStructure", EdsPropertyID(kEdsPropID_HDDirectoryStructure)) + case EdsPropertyID(kEdsPropID_ImageQuality): return CanonProperty("ImageQuality", EdsPropertyID(kEdsPropID_ImageQuality)) + case EdsPropertyID(kEdsPropID_Orientation): return CanonProperty("Orientation", EdsPropertyID(kEdsPropID_Orientation)) + case EdsPropertyID(kEdsPropID_ICCProfile): return CanonProperty("ICCProfile", EdsPropertyID(kEdsPropID_ICCProfile)) + case EdsPropertyID(kEdsPropID_FocusInfo): return CanonProperty("FocusInfo", EdsPropertyID(kEdsPropID_FocusInfo)) + case EdsPropertyID(kEdsPropID_WhiteBalance): return CanonProperty("WhiteBalance", EdsPropertyID(kEdsPropID_WhiteBalance)) + case EdsPropertyID(kEdsPropID_ColorTemperature): return CanonProperty("ColorTemperature", EdsPropertyID(kEdsPropID_ColorTemperature)) + case EdsPropertyID(kEdsPropID_WhiteBalanceShift): return CanonProperty("WhiteBalanceShift", EdsPropertyID(kEdsPropID_WhiteBalanceShift)) + case EdsPropertyID(kEdsPropID_ColorSpace): return CanonProperty("ColorSpace", EdsPropertyID(kEdsPropID_ColorSpace)) + case EdsPropertyID(kEdsPropID_PictureStyle): return CanonProperty("PictureStyle", EdsPropertyID(kEdsPropID_PictureStyle)) + case EdsPropertyID(kEdsPropID_PictureStyleDesc): return CanonProperty("PictureStyleDesc", EdsPropertyID(kEdsPropID_PictureStyleDesc)) + case EdsPropertyID(kEdsPropID_PictureStyleCaption): return CanonProperty("PictureStyleCaption", EdsPropertyID(kEdsPropID_PictureStyleCaption)) + case EdsPropertyID(kEdsPropID_GPSVersionID): return CanonProperty("GPSVersionID", EdsPropertyID(kEdsPropID_GPSVersionID)) + case EdsPropertyID(kEdsPropID_GPSLatitudeRef): return CanonProperty("GPSLatitudeRef", EdsPropertyID(kEdsPropID_GPSLatitudeRef)) + case EdsPropertyID(kEdsPropID_GPSLatitude): return CanonProperty("GPSLatitude", EdsPropertyID(kEdsPropID_GPSLatitude)) + case EdsPropertyID(kEdsPropID_GPSLongitudeRef): return CanonProperty("GPSLongitudeRef", EdsPropertyID(kEdsPropID_GPSLongitudeRef)) + case EdsPropertyID(kEdsPropID_GPSLongitude): return CanonProperty("GPSLongitude", EdsPropertyID(kEdsPropID_GPSLongitude)) + case EdsPropertyID(kEdsPropID_GPSAltitudeRef): return CanonProperty("GPSAltitudeRef", EdsPropertyID(kEdsPropID_GPSAltitudeRef)) + case EdsPropertyID(kEdsPropID_GPSAltitude): return CanonProperty("GPSAltitude", EdsPropertyID(kEdsPropID_GPSAltitude)) + case EdsPropertyID(kEdsPropID_GPSTimeStamp): return CanonProperty("GPSTimeStamp", EdsPropertyID(kEdsPropID_GPSTimeStamp)) + case EdsPropertyID(kEdsPropID_GPSSatellites): return CanonProperty("GPSSatellites", EdsPropertyID(kEdsPropID_GPSSatellites)) + case EdsPropertyID(kEdsPropID_GPSStatus): return CanonProperty("GPSStatus", EdsPropertyID(kEdsPropID_GPSStatus)) + case EdsPropertyID(kEdsPropID_GPSMapDatum): return CanonProperty("GPSMapDatum", EdsPropertyID(kEdsPropID_GPSMapDatum)) + case EdsPropertyID(kEdsPropID_GPSDateStamp): return CanonProperty("GPSDateStamp", EdsPropertyID(kEdsPropID_GPSDateStamp)) + case EdsPropertyID(kEdsPropID_AEMode): return CanonProperty("AEMode", EdsPropertyID(kEdsPropID_AEMode)) + case EdsPropertyID(kEdsPropID_DriveMode): return CanonProperty("DriveMode", EdsPropertyID(kEdsPropID_DriveMode)) + case EdsPropertyID(kEdsPropID_ISOSpeed): return CanonProperty("ISOSpeed", EdsPropertyID(kEdsPropID_ISOSpeed)) + case EdsPropertyID(kEdsPropID_MeteringMode): return CanonProperty("MeteringMode", EdsPropertyID(kEdsPropID_MeteringMode)) + case EdsPropertyID(kEdsPropID_AFMode): return CanonProperty("AFMode", EdsPropertyID(kEdsPropID_AFMode)) + case EdsPropertyID(kEdsPropID_Av): return CanonProperty("Av", EdsPropertyID(kEdsPropID_Av)) + case EdsPropertyID(kEdsPropID_Tv): return CanonProperty("Tv", EdsPropertyID(kEdsPropID_Tv)) + case EdsPropertyID(kEdsPropID_ExposureCompensation): return CanonProperty("ExposureCompensation", EdsPropertyID(kEdsPropID_ExposureCompensation)) + case EdsPropertyID(kEdsPropID_FocalLength): return CanonProperty("FocalLength", EdsPropertyID(kEdsPropID_FocalLength)) + case EdsPropertyID(kEdsPropID_AvailableShots): return CanonProperty("AvailableShots", EdsPropertyID(kEdsPropID_AvailableShots)) + case EdsPropertyID(kEdsPropID_Bracket): return CanonProperty("Bracket", EdsPropertyID(kEdsPropID_Bracket)) + case EdsPropertyID(kEdsPropID_WhiteBalanceBracket): return CanonProperty("WhiteBalanceBracket", EdsPropertyID(kEdsPropID_WhiteBalanceBracket)) + case EdsPropertyID(kEdsPropID_LensName): return CanonProperty("LensName", EdsPropertyID(kEdsPropID_LensName)) + case EdsPropertyID(kEdsPropID_AEBracket): return CanonProperty("AEBracket", EdsPropertyID(kEdsPropID_AEBracket)) + case EdsPropertyID(kEdsPropID_FEBracket): return CanonProperty("FEBracket", EdsPropertyID(kEdsPropID_FEBracket)) + case EdsPropertyID(kEdsPropID_ISOBracket): return CanonProperty("ISOBracket", EdsPropertyID(kEdsPropID_ISOBracket)) + case EdsPropertyID(kEdsPropID_NoiseReduction): return CanonProperty("NoiseReduction", EdsPropertyID(kEdsPropID_NoiseReduction)) + case EdsPropertyID(kEdsPropID_FlashOn): return CanonProperty("FlashOn", EdsPropertyID(kEdsPropID_FlashOn)) + case EdsPropertyID(kEdsPropID_RedEye): return CanonProperty("RedEye", EdsPropertyID(kEdsPropID_RedEye)) + case EdsPropertyID(kEdsPropID_FlashMode): return CanonProperty("FlashMode", EdsPropertyID(kEdsPropID_FlashMode)) + case EdsPropertyID(kEdsPropID_LensStatus): return CanonProperty("LensStatus", EdsPropertyID(kEdsPropID_LensStatus)) + case EdsPropertyID(kEdsPropID_Artist): return CanonProperty("Artist", EdsPropertyID(kEdsPropID_Artist)) + case EdsPropertyID(kEdsPropID_Copyright): return CanonProperty("Copyright", EdsPropertyID(kEdsPropID_Copyright)) + case EdsPropertyID(kEdsPropID_AEModeSelect): return CanonProperty("AEModeSelect", EdsPropertyID(kEdsPropID_AEModeSelect)) + case EdsPropertyID(kEdsPropID_PowerZoom_Speed): return CanonProperty("PowerZoom_Speed", EdsPropertyID(kEdsPropID_PowerZoom_Speed)) + case EdsPropertyID(kEdsPropID_Evf_OutputDevice): return CanonProperty("Evf_OutputDevice", EdsPropertyID(kEdsPropID_Evf_OutputDevice)) + case EdsPropertyID(kEdsPropID_Evf_Mode): return CanonProperty("Evf_Mode", EdsPropertyID(kEdsPropID_Evf_Mode)) + case EdsPropertyID(kEdsPropID_Evf_WhiteBalance): return CanonProperty("Evf_WhiteBalance", EdsPropertyID(kEdsPropID_Evf_WhiteBalance)) + case EdsPropertyID(kEdsPropID_Evf_ColorTemperature): return CanonProperty("Evf_ColorTemperature", EdsPropertyID(kEdsPropID_Evf_ColorTemperature)) + case EdsPropertyID(kEdsPropID_Evf_DepthOfFieldPreview): return CanonProperty("Evf_DepthOfFieldPreview", EdsPropertyID(kEdsPropID_Evf_DepthOfFieldPreview)) + case EdsPropertyID(kEdsPropID_Evf_Zoom): return CanonProperty("Evf_Zoom", EdsPropertyID(kEdsPropID_Evf_Zoom)) + case EdsPropertyID(kEdsPropID_Evf_ZoomPosition): return CanonProperty("Evf_ZoomPosition", EdsPropertyID(kEdsPropID_Evf_ZoomPosition)) + case EdsPropertyID(kEdsPropID_Evf_Histogram): return CanonProperty("Evf_Histogram", EdsPropertyID(kEdsPropID_Evf_Histogram)) + case EdsPropertyID(kEdsPropID_Evf_ImagePosition): return CanonProperty("Evf_ImagePosition", EdsPropertyID(kEdsPropID_Evf_ImagePosition)) + case EdsPropertyID(kEdsPropID_Evf_HistogramStatus): return CanonProperty("Evf_HistogramStatus", EdsPropertyID(kEdsPropID_Evf_HistogramStatus)) + case EdsPropertyID(kEdsPropID_Evf_AFMode): return CanonProperty("Evf_AFMode", EdsPropertyID(kEdsPropID_Evf_AFMode)) + case EdsPropertyID(kEdsPropID_Record): return CanonProperty("Record", EdsPropertyID(kEdsPropID_Record)) + case EdsPropertyID(kEdsPropID_Evf_HistogramY): return CanonProperty("Evf_HistogramY", EdsPropertyID(kEdsPropID_Evf_HistogramY)) + case EdsPropertyID(kEdsPropID_Evf_HistogramR): return CanonProperty("Evf_HistogramR", EdsPropertyID(kEdsPropID_Evf_HistogramR)) + case EdsPropertyID(kEdsPropID_Evf_HistogramG): return CanonProperty("Evf_HistogramG", EdsPropertyID(kEdsPropID_Evf_HistogramG)) + case EdsPropertyID(kEdsPropID_Evf_HistogramB): return CanonProperty("Evf_HistogramB", EdsPropertyID(kEdsPropID_Evf_HistogramB)) + case EdsPropertyID(kEdsPropID_Evf_CoordinateSystem): return CanonProperty("Evf_CoordinateSystem", EdsPropertyID(kEdsPropID_Evf_CoordinateSystem)) + case EdsPropertyID(kEdsPropID_Evf_ZoomRect): return CanonProperty("Evf_ZoomRect", EdsPropertyID(kEdsPropID_Evf_ZoomRect)) + case EdsPropertyID(kEdsPropID_Evf_ImageClipRect): return CanonProperty("Evf_ImageClipRect", EdsPropertyID(kEdsPropID_Evf_ImageClipRect)) + case EdsPropertyID(kEdsPropID_Evf_PowerZoom_CurPosition): return CanonProperty("Evf_PowerZoom_CurPosition", EdsPropertyID(kEdsPropID_Evf_PowerZoom_CurPosition)) + case EdsPropertyID(kEdsPropID_Evf_PowerZoom_MaxPosition): return CanonProperty("Evf_PowerZoom_MaxPosition", EdsPropertyID(kEdsPropID_Evf_PowerZoom_MaxPosition)) + case EdsPropertyID(kEdsPropID_Evf_PowerZoom_MinPosition): return CanonProperty("Evf_PowerZoom_MinPosition", EdsPropertyID(kEdsPropID_Evf_PowerZoom_MinPosition)) + case EdsPropertyID(kEdsPropID_TempStatus): return CanonProperty("TempStatus", EdsPropertyID(kEdsPropID_TempStatus)) + case EdsPropertyID(kEdsPropID_EVF_RollingPitching): return CanonProperty("EVF_RollingPitching", EdsPropertyID(kEdsPropID_EVF_RollingPitching)) + case EdsPropertyID(kEdsPropID_FixedMovie): return CanonProperty("FixedMovie", EdsPropertyID(kEdsPropID_FixedMovie)) + case EdsPropertyID(kEdsPropID_MovieParam): return CanonProperty("MovieParam", EdsPropertyID(kEdsPropID_MovieParam)) + case EdsPropertyID(kEdsPropID_Evf_ClickWBCoeffs): return CanonProperty("Evf_ClickWBCoeffs", EdsPropertyID(kEdsPropID_Evf_ClickWBCoeffs)) + case EdsPropertyID(kEdsPropID_ManualWhiteBalanceData): return CanonProperty("ManualWhiteBalanceData", EdsPropertyID(kEdsPropID_ManualWhiteBalanceData)) + case EdsPropertyID(kEdsPropID_MirrorUpSetting): return CanonProperty("MirrorUpSetting", EdsPropertyID(kEdsPropID_MirrorUpSetting)) + case EdsPropertyID(kEdsPropID_MirrorLockUpState): return CanonProperty("MirrorLockUpState", EdsPropertyID(kEdsPropID_MirrorLockUpState)) + case EdsPropertyID(kEdsPropID_UTCTime): return CanonProperty("UTCTime", EdsPropertyID(kEdsPropID_UTCTime)) + case EdsPropertyID(kEdsPropID_TimeZone): return CanonProperty("TimeZone", EdsPropertyID(kEdsPropID_TimeZone)) + case EdsPropertyID(kEdsPropID_SummerTimeSetting): return CanonProperty("SummerTimeSetting", EdsPropertyID(kEdsPropID_SummerTimeSetting)) + case EdsPropertyID(kEdsPropID_DC_Zoom): return CanonProperty("DC_Zoom", EdsPropertyID(kEdsPropID_DC_Zoom)) + case EdsPropertyID(kEdsPropID_DC_Strobe): return CanonProperty("DC_Strobe", EdsPropertyID(kEdsPropID_DC_Strobe)) + case EdsPropertyID(kEdsPropID_LensBarrelStatus): return CanonProperty("LensBarrelStatus", EdsPropertyID(kEdsPropID_LensBarrelStatus)) + default: + return CanonProperty("Unknown", EdsPropertyID(kEdsPropID_Unknown)) + } +} diff --git a/Imago/Client.swift b/Imago/Client.swift new file mode 100644 index 0000000..9e49405 --- /dev/null +++ b/Imago/Client.swift @@ -0,0 +1,141 @@ +// +// Client.swift +// Imago +// +// Created by Nolan Brown on 7/21/20. +// Copyright © 2020 Nolan Brown. All rights reserved. +// + +import Foundation + +class Client : Conductor2 { + + private lazy var _registerRetryTimer: DispatchSourceTimer = { + let interval: DispatchTimeInterval = DispatchTimeInterval.seconds(1) + let timer = DispatchSource.makeTimerSource() + timer.setEventHandler(handler: register) + timer.schedule(deadline: .now() + interval, repeating: interval) + return timer + }() + private var _retryingRegistration: Bool = false + private var _messagePort: CFMessagePort? + var _identifier: String + var _server: Server + var onRegistration : ((Client) -> Void)? = nil + var receivedNewFrame : ((Frame) -> Void)? = nil + + + override init(serverName: String? = nil) { + _identifier = UUID().uuidString + _server = Server(serverName: "\(serverName!).\(_identifier)") + super.init(serverName: serverName) + _server.receivedNewFrameData = receivedNewFrameDataHandler + _server.start() + } + + override func isClient() -> Bool { + return true + } + + func invalidate() { + _registerRetryTimer.suspend() + _messagePort = nil + } + + public func register() { + if _messagePort == nil { + if let port = CFMessagePortCreateRemote(nil, serverName() as CFString) { + dlog("CONNECTED \(port)") + _messagePort = port + _registerRetryTimer.suspend() + _retryingRegistration = false + } + else { + dlog("NOT CONNECTED \(self)") + + // We couldn't connect to the remote port. Maybe it hasn't been opened yet?? + if _retryingRegistration == false { + _retryingRegistration = true + _registerRetryTimer.resume() + } + + return + } + + } + + let request = Request(port: _messagePort!, callerID: .Register, str: _identifier) + let response = sendRequest(request) + if response.isSuccess() { + let id = response.asString()! + didRegister(id) + } + else { + dlog("Error during registration: \(response)") + // We should retry here.. + } + } + + private func didRegister(_ identifier: String) { + if identifier != _identifier { + dlog("Invalid registration identifier found \(identifier) != \(_identifier)") + } + else if onRegistration != nil { + DispatchQueue.main.async { [unowned self] in + self.onRegistration!(self) + } + } + } + + func receivedNewFrameDataHandler(_ data: Data) { + let pre_deserailize = mach_absolute_time() + + let frame = Frame.deserialize(fromData: data) + let post_deserailize = mach_absolute_time() + flog(frame!, "deserailize began", timestamp: pre_deserailize) + flog(frame!, "deserailize ended", timestamp: post_deserailize) + + if receivedNewFrame != nil { + receivedNewFrame!(frame!) + } + } + + func getFrame() -> Frame? { + guard let _messagePort = _messagePort else { + dlog("_messagePort is nil") + self.register() + return nil + } + let prerequest = mach_absolute_time() + let request = Request(port: _messagePort, callerID: .GetFrame, str: _identifier) + let response = sendRequest(request) + let postrequest = mach_absolute_time() + + if response.isSuccess() { + let data = response.data + if data!.count > 0 { + let pre_deserailize = mach_absolute_time() + + let frame = Frame.deserialize(fromData: data!) + let post_deserailize = mach_absolute_time() + + flog(frame!, "request began", timestamp: prerequest) + flog(frame!, "request completed", timestamp: postrequest) + flog(frame!, "deserailize began", timestamp: pre_deserailize) + flog(frame!, "deserailize ended", timestamp: post_deserailize) + + return frame + } + } + else { + dlog("Server error response: \(response) ") + + } + return nil + } + + func _registerRetryHandler() { + self.register() + } + +} diff --git a/Imago/Conductor.swift b/Imago/Conductor.swift new file mode 100644 index 0000000..a85239d --- /dev/null +++ b/Imago/Conductor.swift @@ -0,0 +1,348 @@ +// +// Conductor.swift +// Imago +// +// Created by Nolan Brown on 7/3/20. +// + + + +class Conductor { + private var _serverName: String + + private let _timeout: Double = 10.0 + + internal let _runLoopMode: CFRunLoopMode = CFRunLoopMode.defaultMode //CFRunLoopMode.commonModes + + private lazy var _registerRetryTimer: DispatchSourceTimer = { + let interval: DispatchTimeInterval = DispatchTimeInterval.seconds(1) + let timer = DispatchSource.makeTimerSource() + timer.setEventHandler(handler: register) + timer.schedule(deadline: .now() + interval, repeating: interval) + return timer + }() + + private var _serverPort: CFMessagePort? + private var _registeredClients: Set = [] + + private var _retryingRegistration: Bool = false + + var _identifier: String + + var onRegistration : ((Client) -> Void)? = nil + var receivedNewFrame : ((Frame) -> Void)? = nil + + public init(serverName: String? = nil) { + + if serverName == nil { + _serverName = Bundle.main.bundleIdentifier! + } + else { + _serverName = serverName! + } + dlog("Using \(_serverName) for Conductor Server") + + } + + func serverName() -> String { + return _serverName + } + + + + func sendRequest(_ request: Request) -> Response { + let messageID = request.messageID() + let port = request.port + + let requestData = request.asCFData() + + + + var responseRawData: Unmanaged? + + let status = CFMessagePortSendRequest(port, + messageID, + requestData, + _timeout, + _timeout, + _runLoopMode.rawValue, + &responseRawData) + + var responseData: Data? + if responseRawData != nil { + responseData = responseRawData!.takeRetainedValue() as Data + } + + return Response(callerID: request.callerID, status: status, data: responseData) + } + + // PUBLIC METHODS + func start() { + // Don't try to start the publisher again + if _messagePort != nil { + return + } + + let (createdPort, createdRunLoopSource) = createLocalPort(serverName()) + _messagePort = createdPort + _runLoopSource = createdRunLoopSource + dlog("Started Server at \(serverName()): \(_messagePort)") + } + + func stop() { + if _messagePort != nil { + CFMessagePortInvalidate(_messagePort) + _messagePort = nil + } + if _runLoopSource != nil { + CFRunLoopRemoveSource(CFRunLoopGetCurrent(), _runLoopSource, _runLoopMode) + _runLoopSource = nil + } + } + func sendFrameToClients(_ frame: Frame) { + guard let data = frame.serialize() else { + print("no frame data \(frame)") + return + } + for client in _registeredClients { + let request = Request(port: client, callerID: .ReceiveFrame, data: data) + let response = sendRequest(request) + if response.isSuccess() { + print("response \(response)") + } + } + } + + private func addClient(_ identifier: String) { + _registeredClientIDs.insert(identifier) + + let clientName = "\(serverName()).\(identifier)" + + if let port = CFMessagePortCreateRemote(nil, clientName as CFString) { + if !_registeredClients.contains(port) { + _registeredClients.insert(port) + } + } + } + + fileprivate func receivedRequest(_ data: Data?, _ callerID: CallerID) -> Data? { + + // let receivedData: Data? + // var identifier: String? + // // We will always expected publisher data to be an indentifier + // if data != nil && CFDataGetLength(data!) > 0 { + // let receivedData = data! as Data + // identifier = String(data: receivedData, encoding: .utf8)! + // } + + switch callerID { + case .Register: + guard let id_data = data else { + return nil + } + let identifier = String(data: id_data, encoding: .utf8)! + + dlog("Recieved \(callerID) request from client \(identifier)") + + addClient(identifier) + return identifier.data(using: .utf8) + + case .GetFrame: + + if getFrameData != nil { + return getFrameData!() + } + case .ReceiveFrame: + guard let frame_data = data else { + return nil + } + receivedNewFrameData?(frame_data) + return "OK".data(using: .utf8) + default: + break + } + return nil + } + + private func createLocalPort(_ portName: String) -> (CFMessagePort?, CFRunLoopSource?) { + var context = CFMessagePortContext(version: 0, info: Unmanaged.passUnretained(self).toOpaque(), retain: nil, release: nil, copyDescription: nil) + if let port = CFMessagePortCreateLocal(nil, portName as CFString, PortRequestCallback, &context, nil) { + //CFMessagePortSetInvalidationCallBack(port, PortInvalidationCallback) + + let runLoopSource = CFMessagePortCreateRunLoopSource(nil, port, 0); + + CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, _runLoopMode); + + CFMessagePortSetDispatchQueue(port, _queue) + + return (port, runLoopSource) + } + return (nil, nil) + } + + + + func invalidate() { + _registerRetryTimer.suspend() + _messagePort = nil + } + + // + + public func register() { + if _messagePort == nil { + if let port = CFMessagePortCreateRemote(nil, serverName() as CFString) { + dlog("CONNECTED \(port)") + _messagePort = port + _registerRetryTimer.suspend() + _retryingRegistration = false + } + else { + dlog("NOT CONNECTED \(self)") + + // We couldn't connect to the remote port. Maybe it hasn't been opened yet?? + if _retryingRegistration == false { + _retryingRegistration = true + _registerRetryTimer.resume() + } + + return + } + + } + + let request = Request(port: _messagePort!, callerID: .Register, str: _identifier) + let response = sendRequest(request) + if response.isSuccess() { + let id = response.asString()! + didRegister(id) + } + else { + dlog("Error during registration: \(response)") + // We should retry here.. + } + } + + private func didRegister(_ identifier: String) { + if identifier != _identifier { + dlog("Invalid registration identifier found \(identifier) != \(_identifier)") + } + else if onRegistration != nil { + DispatchQueue.main.async { [unowned self] in + self.onRegistration!(self) + } + } + } + + func receivedNewFrameDataHandler(_ data: Data) { + let pre_deserailize = mach_absolute_time() + + let frame = Frame.deserialize(fromData: data) + let post_deserailize = mach_absolute_time() + flog(frame!, "deserailize began", timestamp: pre_deserailize) + flog(frame!, "deserailize ended", timestamp: post_deserailize) + + if receivedNewFrame != nil { + receivedNewFrame!(frame!) + } + } + + func getFrame() -> Frame? { + guard let _messagePort = _messagePort else { + dlog("_messagePort is nil") + self.register() + return nil + } + let prerequest = mach_absolute_time() + let request = Request(port: _messagePort, callerID: .GetFrame, str: _identifier) + let response = sendRequest(request) + let postrequest = mach_absolute_time() + + if response.isSuccess() { + let data = response.data + if data!.count > 0 { + let pre_deserailize = mach_absolute_time() + + let frame = Frame.deserialize(fromData: data!) + let post_deserailize = mach_absolute_time() + + flog(frame!, "request began", timestamp: prerequest) + flog(frame!, "request completed", timestamp: postrequest) + flog(frame!, "deserailize began", timestamp: pre_deserailize) + flog(frame!, "deserailize ended", timestamp: post_deserailize) + + return frame + } + } + else { + dlog("Server error response: \(response) ") + + } + return nil + } + + func _registerRetryHandler() { + self.register() + } +} + + +class Conductor2 { + private var _serverName: String + + private let _timeout: Double = 10.0 + + internal let _runLoopMode: CFRunLoopMode = CFRunLoopMode.defaultMode //CFRunLoopMode.commonModes + + public init(serverName: String? = nil) { + + if serverName == nil { + _serverName = Bundle.main.bundleIdentifier! + } + else { + _serverName = serverName! + } + dlog("Using \(_serverName) for Conductor Server") + + } + + func serverName() -> String { + return _serverName + } + + func isServer() -> Bool { + return false + } + + func isClient() -> Bool { + return !isServer() + } + + + func sendRequest(_ request: Request) -> Response { + let messageID = request.messageID() + let port = request.port + + let requestData = request.asCFData() + + + + var responseRawData: Unmanaged? + + let status = CFMessagePortSendRequest(port, + messageID, + requestData, + _timeout, + _timeout, + _runLoopMode.rawValue, + &responseRawData) + + var responseData: Data? + if responseRawData != nil { + responseData = responseRawData!.takeRetainedValue() as Data + } + + return Response(callerID: request.callerID, status: status, data: responseData) + } + +} diff --git a/Imago/Extensions/CIImage+Filter.swift b/Imago/Extensions/CIImage+Filter.swift new file mode 100644 index 0000000..d0f43c7 --- /dev/null +++ b/Imago/Extensions/CIImage+Filter.swift @@ -0,0 +1,62 @@ +// +// CIImage+Filter.swift +// Imago +// +// Created by Nolan Brown on 8/10/20. +// Copyright © 2020 Nolan Brown. All rights reserved. +// + +import Foundation +import CoreImage + +// Filter conviences +extension CIImage { + func mirror() -> CIImage? { + return self.oriented(.upMirrored) + } + + func pixellate(scale: Int = 15 ) -> CIImage? { + if let currentFilter = CIFilter(name: "CIPixellate") { + currentFilter.setValue(self, forKey: kCIInputImageKey) + currentFilter.setValue(scale, forKey: kCIInputScaleKey) + + let v = self.centerVector() + currentFilter.setValue(v, forKey: kCIInputCenterKey) + + return currentFilter.outputImage + } + return nil + } + + func thermalize() -> CIImage? { + if let currentFilter = CIFilter(name: "CIThermal") { + currentFilter.setValue(self, forKey: kCIInputImageKey) + return currentFilter.outputImage + } + return nil + } + + + func findFaceRects(_ context: CIContext, padding: Float = 30 ) -> [CGRect] { + let detector: CIDetector! = CIDetector(ofType: CIDetectorTypeFace, + context: context, + options: [CIDetectorAccuracy: CIDetectorAccuracyHigh]) + let features = detector.features(in: self, options: nil) + + var faces : [CGRect] = [] + for faceFeature in features { + var faceRect = faceFeature.bounds + faceRect = faceRect.insetBy(dx: CGFloat(padding) , dy: CGFloat(padding)) + faces.append(faceRect) + + } + + return faces + } + + func centerVector() -> CIVector { + return CIVector(cgPoint: CGPoint(x: self.extent.width/2, y: self.extent.height / 2) ) + + } +} + diff --git a/Imago/Extensions/Frame+Decompress.swift b/Imago/Extensions/Frame+Decompress.swift new file mode 100644 index 0000000..d47e694 --- /dev/null +++ b/Imago/Extensions/Frame+Decompress.swift @@ -0,0 +1,65 @@ +// +// Frame+Decompress.swift +// Imago +// +// Created by Nolan Brown on 7/19/20. +// Copyright © 2020 Nolan Brown. All rights reserved. +// + +import Foundation + +func TJPAD(_ width: Int32) -> Int32 { + return (((width) + 3) & (~3)) +} + +extension Frame { + + static func decompress(_ bytes: UnsafeMutableRawPointer?, length: UInt) -> Frame? { + let _decompressor: tjhandle = tjInitDecompress() + var width: Int32 = 0 + var height: Int32 = 0 + + let rawBytes: UnsafeMutablePointer = bytes!.assumingMemoryBound(to: UInt8.self) + let result = tjDecompressHeader(_decompressor, + rawBytes, + length, + &width, &height) + if result == 0 { + + let encoding = TJPF_BGRA.rawValue + + // We can't access tjPixelSize directly because it lacks a type in turbojpeg and becomes a tuple in Swift + var tjPixelSizes = tjPixelSize + let pixelSize = withUnsafeBytes(of: &tjPixelSizes) { (rawPtr) -> Int32 in + let ptr = rawPtr.baseAddress!.assumingMemoryBound(to: Int32.self) + return ptr[Int(encoding)] + } + let wanted_bpr = TJPAD(pixelSize * width) + + let bufferSize = Int(wanted_bpr * height) + let buffer = UnsafeMutablePointer.allocate(capacity: bufferSize) + let result = tjDecompress2(_decompressor, rawBytes, length, buffer, width, wanted_bpr, height, encoding, 0); + + // Use TJFLAG_BOTTOMUP when rendering in PreviewMetalView + //let result = tjDecompress2(_decompressor, rawBytes, length, buffer, width, wanted_bpr, height, TJPF_BGRA.rawValue, TJFLAG_BOTTOMUP); + + if (result == 0) + { + let data = Data(bytes: buffer, count: bufferSize) + + let imgData = Frame(data:data, + height: Int(height), + width: Int(width), + bytesPerRow: Int(wanted_bpr), + pixelSize: Int(pixelSize)) + + free(buffer) + tjDestroy(_decompressor) + return imgData + } + } + tjDestroy(_decompressor) + + return nil + } +} diff --git a/Imago/Headers/Imago-Bridging-Header.h b/Imago/Headers/Imago-Bridging-Header.h new file mode 100644 index 0000000..3aa27bb --- /dev/null +++ b/Imago/Headers/Imago-Bridging-Header.h @@ -0,0 +1,23 @@ +// +// Imago-Bridging-Header.h +// Imago +// +// Created by Nolan Brown on 7/3/20. +// + +#ifndef Imago_Bridging_Header_h +#define Imago_Bridging_Header_h + +#ifndef __MACOS__ +#define __MACOS__ +#endif + +#import "EDSDK.h" +#import + +#import +#import +#import + + +#endif /* Imago_Bridging_Header_h */ diff --git a/Imago/Headers/Imago-PrefixHeader.pch b/Imago/Headers/Imago-PrefixHeader.pch new file mode 100644 index 0000000..dd750d7 --- /dev/null +++ b/Imago/Headers/Imago-PrefixHeader.pch @@ -0,0 +1,17 @@ +// +// PrefixHeader.pch +// Imago +// +// Created by Nolan Brown on 7/2/20. +// + +#ifndef PrefixHeader_pch +#define PrefixHeader_pch + +// Include any system framework and library headers here that should be included in all compilation units. +// You will also need to set the Prefix Header build setting of one or more of your targets to reference this file. +#ifndef __MACOS__ +#define __MACOS__ +#endif + +#endif /* PrefixHeader_pch */ diff --git a/Imago/Info.plist b/Imago/Info.plist new file mode 100644 index 0000000..d6c4c14 --- /dev/null +++ b/Imago/Info.plist @@ -0,0 +1,38 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIconFile + + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 0.1 + CFBundleSignature + ???? + CFBundleVersion + 385 + LSMinimumSystemVersion + $(MACOSX_DEPLOYMENT_TARGET) + NSHumanReadableCopyright + Copyright © 2020 Nolan Brown. All rights reserved. + NSMainStoryboardFile + Main + NSPrincipalClass + NSApplication + NSSupportsAutomaticTermination + + NSSupportsSuddenTermination + + + diff --git a/Imago/Preview Content/Preview Assets.xcassets/Contents.json b/Imago/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 0000000..da4a164 --- /dev/null +++ b/Imago/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Imago/Server.swift b/Imago/Server.swift new file mode 100644 index 0000000..7929e7c --- /dev/null +++ b/Imago/Server.swift @@ -0,0 +1,184 @@ +// +// Publisher.swift +// Imago +// +// Created by Nolan Brown on 7/16/20. +// Copyright © 2020 Nolan Brown. All rights reserved. +// + +import Foundation + +class Server : Conductor2 { + + fileprivate var _messagePort: CFMessagePort? + fileprivate var _runLoopSource: CFRunLoopSource? + fileprivate var _queue: DispatchQueue = DispatchQueue(label: "com.nolanbrown.Imago.Server", qos: .userInteractive) + + //@objc weak var delegate: ConductorProtocol? + fileprivate var _registeredClientIDs: Set = [] + fileprivate var _registeredClients: Set = [] + + var subscribedHandler : (() -> Void)? = nil + var getFrameData : (() -> Data?)? = nil + + var receivedNewFrameData : ((Data) -> Void)? = nil + + deinit { + stop() + } + + override func isServer() -> Bool { + return true + } + + + // PUBLIC METHODS + func start() { + // Don't try to start the publisher again + if _messagePort != nil { + return + } + + let (createdPort, createdRunLoopSource) = createLocalPort(serverName()) + _messagePort = createdPort + _runLoopSource = createdRunLoopSource + dlog("Started Server at \(serverName()): \(_messagePort)") + } + + func stop() { + if _messagePort != nil { + CFMessagePortInvalidate(_messagePort) + _messagePort = nil + } + if _runLoopSource != nil { + CFRunLoopRemoveSource(CFRunLoopGetCurrent(), _runLoopSource, _runLoopMode) + _runLoopSource = nil + } + } + func sendFrameToClients(_ frame: Frame) { + guard let data = frame.serialize() else { + print("no frame data \(frame)") + return + } + for client in _registeredClients { + let request = Request(port: client, callerID: .ReceiveFrame, data: data) + let response = sendRequest(request) + if response.isSuccess() { + print("response \(response)") + } + } + } + + private func addClient(_ identifier: String) { + _registeredClientIDs.insert(identifier) + + let clientName = "\(serverName()).\(identifier)" + + if let port = CFMessagePortCreateRemote(nil, clientName as CFString) { + if !_registeredClients.contains(port) { + _registeredClients.insert(port) + } + } + } + + fileprivate func receivedRequest(_ data: Data?, _ callerID: CallerID) -> Data? { + +// let receivedData: Data? +// var identifier: String? +// // We will always expected publisher data to be an indentifier +// if data != nil && CFDataGetLength(data!) > 0 { +// let receivedData = data! as Data +// identifier = String(data: receivedData, encoding: .utf8)! +// } + + switch callerID { + case .Register: + guard let id_data = data else { + return nil + } + let identifier = String(data: id_data, encoding: .utf8)! + + dlog("Recieved \(callerID) request from client \(identifier)") + + addClient(identifier) + return identifier.data(using: .utf8) + + case .GetFrame: + + if getFrameData != nil { + return getFrameData!() + } + case .ReceiveFrame: + guard let frame_data = data else { + return nil + } + receivedNewFrameData?(frame_data) + return "OK".data(using: .utf8) + default: + break + } + return nil + } + + private func createLocalPort(_ portName: String) -> (CFMessagePort?, CFRunLoopSource?) { + var context = CFMessagePortContext(version: 0, info: Unmanaged.passUnretained(self).toOpaque(), retain: nil, release: nil, copyDescription: nil) + if let port = CFMessagePortCreateLocal(nil, portName as CFString, PortRequestCallback, &context, nil) { + //CFMessagePortSetInvalidationCallBack(port, PortInvalidationCallback) + + let runLoopSource = CFMessagePortCreateRunLoopSource(nil, port, 0); + + CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, _runLoopMode); + + CFMessagePortSetDispatchQueue(port, _queue) + + return (port, runLoopSource) + } + return (nil, nil) + } +} + +let PortInvalidationCallback : CFMessagePortInvalidationCallBack = { + (port: CFMessagePort?, info: UnsafeMutableRawPointer?) -> Void in + print("PortInvalidationCallback \(port)") +} + +/* + CFMessagePortCallBack + + All CFMessagePortCreateLocal methods are provided PortRequestCallback as the callback function. It's here we do: + 1. Determine what type the request is (ie CallerID) + 2. Load the related initialized Conductor object + 3. Process the request + + */ +let PortRequestCallback : CFMessagePortCallBack = { + (port: CFMessagePort?, messageID: Int32, data: CFData?, info: UnsafeMutableRawPointer?) -> Unmanaged? in + + let callbackID = CallerID(rawValue: messageID) + + var conductorInstance : Server? + if info != nil { + conductorInstance = unsafeBitCast(info, to: Server.self) + //conductorInstance = info!.load(as: Conductor.self) + } + if conductorInstance == nil { + print("ERROR: Conductor Instance Unavailable") + return nil + } + + var receivedData: Data? + if data != nil && CFDataGetLength(data!) > 0 { + receivedData = data! as Data + } + + var portName = conductorInstance!.serverName() + + guard let callerID = callbackID else { + return nil + } + + guard let responseData = conductorInstance!.receivedRequest(receivedData, callerID) else { + return nil + } + return Unmanaged.passRetained(responseData as CFData) +} diff --git a/Imago/Stream.swift b/Imago/Stream.swift new file mode 100644 index 0000000..367a28d --- /dev/null +++ b/Imago/Stream.swift @@ -0,0 +1,138 @@ +// +// Stream.swift +// Imago +// +// Created by Nolan Brown on 7/29/20. +// Copyright © 2020 Nolan Brown. All rights reserved. +// + +import Foundation +import CoreImage +import AppKit + +enum StreamSetting { + case Pixellate + case Thermalize + +} + +// Coordinates frames to a registered plugin and any rendering that's required +class Stream : Hashable, Identifiable, ObservableObject { + + + var name: String? + var id: String = UUID().uuidString + var settings: [StreamSetting] = [] + + + private var _pubsub: PubSub = PubSub(publisherPortName:IMAGO_SERVER_NAME) + + private var _activeCamera: Camera? + + private var _context: CIContext = CIContext() + private var _bufferPool: CVPixelBufferPool? + + private var _fps: Int = 30 + + private var _frameQueue: [Frame] = [] + + + fileprivate var _processingQueue: DispatchQueue = DispatchQueue(label: "com.Imago.Camera", qos: .userInteractive) + + + static func == (lhs: Stream, rhs: Stream) -> Bool { + return lhs.id == rhs.id + } + public func hash(into hasher: inout Hasher) { + return hasher.combine(ObjectIdentifier(self).hashValue) + } + + + func getPubSubMetrics() -> Dictionary { + + var metrics: [String: Any] = _pubsub.getSubscriberMetrics() + metrics["IsRunning"] = _pubsub.isRunning() + return metrics + + } + + func start() { + _pubsub.start() + } + + func stop() { + _pubsub.stop() + if _bufferPool != nil { + CVPixelBufferPoolFlush(_bufferPool!, []) + } + } + + // Use frames from this camera to publish + func makeCameraActive(_ camera: Camera?) { + if camera != nil { + _activeCamera?.setActive(active: false) + camera!.didReceiveFrame = self.didReceiveFrame + camera!.setActive(active: true) + _activeCamera = camera + } + else { + _activeCamera?.setActive(active: false) + _activeCamera = nil + } + + } + + /* + Callback from the active Camera containing the latest Frame + We process the frame before publishing to any registered subscribers + */ + func didReceiveFrame(_ frame: Frame, _ camera: Camera) { + _processingQueue.async { [unowned self, frame, camera] in + if let newFrame = self.prepareFrameForPublication(frame, camera) { + if let data = newFrame.serialize() { + self._pubsub.publishFrameData(data) + } + } + } + } + + func getCachedPixelBufferPool(_ frame: Frame) -> CVPixelBufferPool? { + if _bufferPool != nil { + return _bufferPool + } + _bufferPool = CVPixelBufferPool.create(size: frame.getSize()) + return _bufferPool + } + + + /* + We will perform any neccessary processing to prepare the frame for publication to subscribers + Frame's provided as input haven't had their data converted to a CVPixelBuffer yet + + */ + func prepareFrameForPublication(_ frame: Frame, _ camera: Camera) -> Frame? { + var createdFrame: Frame? + let pixelBufferPool = getCachedPixelBufferPool(frame) + + autoreleasepool { + if let ciImage = frame.buildCIImage() { + + let processedImage = camera.filter.processImage(ciImage) + + + if let pixelBuffer = processedImage.toPixelBuffer(withContext: _context, fromPool: pixelBufferPool) { + + if let data = pixelBuffer.toData() { + var newFrame = frame.copy() + newFrame.data = data + newFrame.isDataPixelBuffer = true + createdFrame = newFrame + } + } + } + + } + return createdFrame + } + +} diff --git a/Imago/Tests/ImagoTests/ImagoTests.swift b/Imago/Tests/ImagoTests/ImagoTests.swift new file mode 100644 index 0000000..058881e --- /dev/null +++ b/Imago/Tests/ImagoTests/ImagoTests.swift @@ -0,0 +1,34 @@ +// +// ImagoTests.swift +// ImagoTests +// +// Created by Nolan Brown on 7/3/20. +// Copyright © 2020 Nolan Brown. All rights reserved. +// + +import XCTest +@testable import Imago + +class ImagoTests: XCTestCase { + + override func setUp() { + // Put setup code here. This method is called before the invocation of each test method in the class. + } + + override func tearDown() { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + func testExample() { + // This is an example of a functional test case. + // Use XCTAssert and related functions to verify your tests produce the correct results. + } + + func testPerformanceExample() { + // This is an example of a performance test case. + self.measure { + // Put the code you want to measure the time of here. + } + } + +} diff --git a/Imago/Tests/ImagoTests/Info.plist b/Imago/Tests/ImagoTests/Info.plist new file mode 100644 index 0000000..64d65ca --- /dev/null +++ b/Imago/Tests/ImagoTests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/Imago/Tests/ImagoUITests/ImagoUITests.swift b/Imago/Tests/ImagoUITests/ImagoUITests.swift new file mode 100644 index 0000000..a48893d --- /dev/null +++ b/Imago/Tests/ImagoUITests/ImagoUITests.swift @@ -0,0 +1,43 @@ +// +// ImagoUITests.swift +// ImagoUITests +// +// Created by Nolan Brown on 7/3/20. +// Copyright © 2020 Nolan Brown. All rights reserved. +// + +import XCTest + +class ImagoUITests: XCTestCase { + + override func setUp() { + // Put setup code here. This method is called before the invocation of each test method in the class. + + // In UI tests it is usually best to stop immediately when a failure occurs. + continueAfterFailure = false + + // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. + } + + override func tearDown() { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + func testExample() { + // UI tests must launch the application that they test. + let app = XCUIApplication() + app.launch() + + // Use recording to get started writing UI tests. + // Use XCTAssert and related functions to verify your tests produce the correct results. + } + + func testLaunchPerformance() { + if #available(macOS 10.15, iOS 13.0, tvOS 13.0, *) { + // This measures how long it takes to launch your application. + measure(metrics: [XCTOSSignpostMetric.applicationLaunch]) { + XCUIApplication().launch() + } + } + } +} diff --git a/Imago/Tests/ImagoUITests/Info.plist b/Imago/Tests/ImagoUITests/Info.plist new file mode 100644 index 0000000..64d65ca --- /dev/null +++ b/Imago/Tests/ImagoUITests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/Imago/ViewController.swift b/Imago/ViewController.swift new file mode 100644 index 0000000..8e715bd --- /dev/null +++ b/Imago/ViewController.swift @@ -0,0 +1,179 @@ +// +// ViewController.swift +// Imago +// +// Created by Nolan Brown on 7/29/20. +// Copyright © 2020 Nolan Brown. All rights reserved. +// + +import Foundation +import Combine +import SwiftUI +import IOKit.pwr_mgt + +/* +Manages available cameras and links them to a Stream. +*/ +class ViewController: NSObject, ObservableObject, NSWindowDelegate { + @Published var cameras: [Camera] = [] + + private var _activeStream: Stream + + var newFrameHandler : ((Frame) -> Void)? = nil + + override init() { + _activeStream = Stream() + super.init() + + _activeStream.start() + + NotificationCenter.default.addObserver(self, selector: #selector(onCameraAddedEvent(_:)), name: .cameraAddedEvent, object: nil) + _disableSleep() + } + + + deinit { + self.teardown() + } + + /* + Action for UI + */ + func loadCameras() -> Void { + CanonCamera.loadCameras() + } + + + /* + Called from the UI onAppear to do initial setup + */ + func setup() { + CanonCamera.setup() + Thread.sleep(forTimeInterval: 0.5) + self.loadCameras() + } + + /* + Called when closing the application + */ + func teardown() { + setActiveCamera(nil) + CanonCamera.teardown() + } + + func setActiveCamera(_ camera: Camera?) { + if camera != nil { + _activeStream.makeCameraActive(camera!) + } + else { + _activeStream.makeCameraActive(nil) + } + } + + + private func _addNewCameras(_ newCameras: [Camera]) { + var cameraSet = Set(self.cameras) + for cam in newCameras { + if !cameraSet.contains(cam) { + cameraSet.insert(cam) + } + } + self.cameras = Array(cameraSet) + self.cameras.sort { (c1, c2) -> Bool in + if c1.name > c2.name { + return true + } + return false + } + + } + + + private func _disableSleep() { + var noSleepAssertion: IOPMAssertionID = IOPMAssertionID(0) + if (noSleepAssertion == 0) + { + let reasonForActivity = "Imago maintaining connection to camera" as CFString + + _ = IOPMAssertionCreateWithName(kIOPMAssertionTypePreventUserIdleSystemSleep as CFString?, + IOPMAssertionLevel(kIOPMAssertionLevelOn), + reasonForActivity, &noSleepAssertion) + } + + } + + @objc func onCameraAddedEvent(_ notification: Notification) + { + if let cameras = notification.userInfo?["Cameras"] as? [Camera] { + self._addNewCameras(cameras) + } + } +} + +/* +extension ViewController { + + fileprivate var _isLiveViewActive = false + fileprivate var _liveViewWindow : NSWindow? + + func showLiveViewWindow(_ camera: Camera) { + + if self.activeCamera != nil && self._liveViewWindow == nil { + + self._liveViewWindow = NSWindow( + contentRect: NSRect(x: 300, y: 300, width: 300, height: 300), + styleMask: [.titled, .closable, .miniaturizable], // .resizable, .fullSizeContentView + backing: .buffered, defer: false) + let view = LiveImageView(containerWindow: _liveViewWindow, camera:self.activeCamera!) + let hostingView = NSHostingView(rootView: view) + self._liveViewWindow!.center() + self._liveViewWindow!.contentView = hostingView + self._liveViewWindow!.delegate = self + self._liveViewWindow!.makeKeyAndOrderFront(nil) + self._isLiveViewActive = true + self.startLiveStream() + } + } + func windowWillClose(_ notification: Notification) { + self._isLiveViewActive = false + //self.activeCamera!.stopLiveStream() + } +} +*/ + +/* + For future use to manage application settings + */ +struct Settings : Codable { + var showPreviewFrame: Bool + var autoConnectToLastUsedCamera: Bool + + private enum CodingKeys: String, CodingKey { + case showPreviewFrame + case autoConnectToLastUsedCamera + } + + init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: CodingKeys.self) + showPreviewFrame = try values.decode(Bool.self, forKey: .showPreviewFrame) + autoConnectToLastUsedCamera = try values.decode(Bool.self, forKey: .autoConnectToLastUsedCamera) + } + + func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(showPreviewFrame, forKey: .showPreviewFrame) + try container.encode(autoConnectToLastUsedCamera, forKey: .autoConnectToLastUsedCamera) + } + +// func save() { +// let encoder = PropertyListEncoder() +// encoder.outputFormat = .xml +// do { +// let data = try encoder.encode(someSettings) +// try data.write(to: settingsURL) +// } catch { +// // Handle error +// print(error) +// } +// } +} diff --git a/Imago/Views/CameraFilterMenu.swift b/Imago/Views/CameraFilterMenu.swift new file mode 100644 index 0000000..c6bd0fc --- /dev/null +++ b/Imago/Views/CameraFilterMenu.swift @@ -0,0 +1,65 @@ +// +// CameraFilterMenu.swift +// Imago +// +// Created by Nolan Brown on 7/3/20. +// Copyright © 2020 Nolan Brown. All rights reserved. +// + +import Foundation +import SwiftUI + +struct CameraFilterMenu: View { + var camera: Camera + @State var filter: CameraFilter = .None + + func toggleFilter(_ newFilter: CameraFilter) { + if newFilter == .None { + self.filter = .None + } + else { + if self.filter.contains(newFilter) { + self.filter.remove(newFilter) + } + else { + self.filter.insert(newFilter) + } + } + + self.camera.filter = self.filter + } + + func filterName(_ filter: CameraFilter) -> String { + var filterName = CameraFilter.debugDescriptions[filter] ?? "--" + let checkbox = "✓" + if self.filter.contains(filter) { + filterName = "\(checkbox) \(filterName)" + } + return filterName + } + + + var body: some View { + MenuButton("Filter") { + ForEach(CameraFilter.Options, id: \.hashValue) { f in + Button(self.filterName(f)) { self.toggleFilter(f) } + } + }.buttonStyle(DetectHover()) + } +} + +/* + From https://stackoverflow.com/questions/59837991/how-to-get-accentcolor-background-for-menu-items-in-swiftui-with-reduced-transpa + */ +struct DetectHover: ButtonStyle { + @State private var hovering: Bool = false + + public func makeBody(configuration: DetectHover.Configuration) -> some View { + configuration.label + .foregroundColor(self.hovering ? Color.white : Color.primary) + .background(self.hovering ? Color.blue : Color.clear) + .onHover { hover in + self.hovering = hover + } + } +} diff --git a/Imago/Views/CameraRow.swift b/Imago/Views/CameraRow.swift new file mode 100644 index 0000000..a8c65af --- /dev/null +++ b/Imago/Views/CameraRow.swift @@ -0,0 +1,61 @@ +// +// CameraRow.swift +// Imago +// +// Created by Nolan Brown on 7/3/20. +// Copyright © 2020 Nolan Brown. All rights reserved. +// + +import Foundation +import SwiftUI + +struct CameraRow: View { + @EnvironmentObject var viewController: ViewController + + @ObservedObject var camera: Camera + @State var filter: CameraFilter = .None + + var body: some View { + VStack { + HStack(alignment: .center, spacing: 6) { + Circle() + .fill(camera.streaming ? Color.green : Color.red) + .frame(width: 9, height: 9).padding([.leading], 6.0) + Spacer() + VStack { + + Text(camera.name) + + HStack { + if self.camera.streaming { + CameraFilterMenu(camera: camera) + } else { + Button(action: { + if self.camera.streaming { + self.viewController.setActiveCamera(nil) + } + else { + self.viewController.setActiveCamera(self.camera) + } + }) { + Text(camera.streaming ? "Stop" : "Start") + } + } + } + + } + Spacer() + Image("CameraIcon").frame(width: 50, height: 50).padding([.trailing], 6.0) + + }.frame(maxWidth: .infinity).padding([.all], 6.0) + + }.background(RoundedRectangle(cornerRadius: 5, style: .continuous).fill(Color.white)).padding([.all], 6.0).onTapGesture { + if self.camera.streaming { + self.viewController.setActiveCamera(nil) + } + else { + self.viewController.setActiveCamera(self.camera) + } + } + } +} diff --git a/Imago/Views/ContentView.swift b/Imago/Views/ContentView.swift new file mode 100644 index 0000000..ab8301d --- /dev/null +++ b/Imago/Views/ContentView.swift @@ -0,0 +1,30 @@ +// +// ContentView.swift +// Imago +// +// Created by Nolan Brown on 7/3/20. +// Copyright © 2020 Nolan Brown. All rights reserved. +// + +import SwiftUI + +struct ContentView: View { + @EnvironmentObject var viewController: ViewController + + var body: some View { + GeometryReader { geometry in + NavigationView { + ScrollView() { + ForEach(self.viewController.cameras, id: \.identifier) { camera in + CameraRow(camera: camera).tag(camera.identifier).padding([.all], 6.0) + } + }.frame(width:geometry.size.width) + + }.onAppear { + self.viewController.setup() + } + } + } +} + + diff --git a/Imago/Views/LivePreview.swift b/Imago/Views/LivePreview.swift new file mode 100644 index 0000000..d1b5405 --- /dev/null +++ b/Imago/Views/LivePreview.swift @@ -0,0 +1,45 @@ +// +// LivePreview.swift +// Imago +// +// Created by Nolan Brown on 8/10/20. +// Copyright © 2020 Nolan Brown. All rights reserved. +// + +import Foundation +import SwiftUI + +struct LivePreview: View { + @ObservedObject var camera: Camera + @State var expanded: Bool = false + @State var size: NSSize = NSSize(width: 150, height: 100) + + var isPreviewAvailable: Bool { + return false + //return camera.streaming && camera.previewPixelBuffer != nil + } + + func getPreviewImage() -> NSImage? { +// if isPreviewAvailable { +// return NSImage(cgImage: camera.previewPixelBuffer!.toImage()!, size: camera.previewPixelBuffer!.size) +// } + return nil + } + + var body: some View { + VStack { + if isPreviewAvailable { + Image(nsImage: self.getPreviewImage()!) + .resizable() + .aspectRatio(contentMode: .fit) + .frame(width:size.width, height:size.height) + .scaledToFit() + .padding([.trailing], 6.0) + } + else { + Image("CameraIcon").frame(width: 50, height: 50).padding([.trailing], 6.0) + } + } + } +} + diff --git a/Imago/Views/TitlebarAccesory.swift b/Imago/Views/TitlebarAccesory.swift new file mode 100644 index 0000000..78bc0cb --- /dev/null +++ b/Imago/Views/TitlebarAccesory.swift @@ -0,0 +1,27 @@ +// +// TitlebarAccesory.swift +// Imago +// +// Created by Nolan Brown on 8/10/20. +// Copyright © 2020 Nolan Brown. All rights reserved. +// + +import Foundation +import SwiftUI + +struct TitlebarAccessory: View { + @EnvironmentObject private var viewController: ViewController + + var body: some View { + HStack(alignment: .top) { + Button(action: { + self.viewController.loadCameras() + }) { + Text("Load Cameras") + } + + + }.frame(minHeight: 30, maxHeight: 30) + + } +} diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..2e41e00 --- /dev/null +++ b/LICENSE @@ -0,0 +1,47 @@ +MIT License + +Copyright (c) 2020 Nolan Brown + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + + +# SimpleDALPlugin +MIT License + +Copyright (c) 2020 John Boiles +Copyright (c) 2020 Ryohei Ikegami + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Plugin/Device.swift b/Plugin/Device.swift new file mode 100644 index 0000000..c6d157c --- /dev/null +++ b/Plugin/Device.swift @@ -0,0 +1,45 @@ +// +// Device.swift +// SimpleDALPlugin +// +// Created by 池上涼平 on 2020/04/25. +// Copyright © 2020 com.seanchas116. All rights reserved. +// + +import Foundation +import IOKit + +class Device: Object { + var objectID: CMIOObjectID = 0 + var streamID: CMIOStreamID = 0 + let name = PLUGIN_DEVICE_NAME + let manufacturer = PLUGIN_DEVICE_MANUFACTURER + let deviceUID = PLUGIN_DEVICE_UID + let modelUID = PLUGIN_DEVICE_MODEL_UID + var excludeNonDALAccess: Bool = false + var deviceMaster: Int32 = -1 + + lazy var properties: [Int : Property] = [ + kCMIOObjectPropertyName: Property(name), + kCMIOObjectPropertyManufacturer: Property(manufacturer), + kCMIODevicePropertyDeviceUID: Property(deviceUID), + kCMIODevicePropertyModelUID: Property(modelUID), + kCMIODevicePropertyTransportType: Property(UInt32(kIOAudioDeviceTransportTypeBuiltIn)), + kCMIODevicePropertyDeviceIsAlive: Property(UInt32(1)), + kCMIODevicePropertyDeviceIsRunning: Property(UInt32(1)), + kCMIODevicePropertyDeviceIsRunningSomewhere: Property(UInt32(1)), + kCMIODevicePropertyDeviceCanBeDefaultDevice: Property(UInt32(1)), + kCMIODevicePropertyCanProcessAVCCommand: Property(UInt32(0)), + kCMIODevicePropertyCanProcessRS422Command: Property(UInt32(0)), + kCMIODevicePropertyHogMode: Property(Int32(-1)), + kCMIODevicePropertyStreams: Property { [unowned self] in self.streamID }, + kCMIODevicePropertyExcludeNonDALAccess: Property( + getter: { [unowned self] () -> UInt32 in self.excludeNonDALAccess ? 1 : 0 }, + setter: { [unowned self] (value: UInt32) -> Void in self.excludeNonDALAccess = value != 0 } + ), + kCMIODevicePropertyDeviceMaster: Property( + getter: { [unowned self] () -> Int32 in self.deviceMaster }, + setter: { [unowned self] (value: Int32) -> Void in self.deviceMaster = value } + ), + ] +} diff --git a/Plugin/Headers/ImagoPlugin-Bridging-Header.h b/Plugin/Headers/ImagoPlugin-Bridging-Header.h new file mode 100644 index 0000000..80bfa17 --- /dev/null +++ b/Plugin/Headers/ImagoPlugin-Bridging-Header.h @@ -0,0 +1,14 @@ +// +// Use this file to import your target's public headers that you would like to expose to Swift. +// + + +#ifndef Imago_Plugin_Bridging_Header_h +#define Imago_Plugin_Bridging_Header_h + +#import +#import +#import + + +#endif diff --git a/Plugin/Headers/PrefixHeader.pch b/Plugin/Headers/PrefixHeader.pch new file mode 100644 index 0000000..cf54a07 --- /dev/null +++ b/Plugin/Headers/PrefixHeader.pch @@ -0,0 +1,22 @@ +// +// PrefixHeader.pch +// Imago +// +// Created by Nolan Brown on 7/2/20. +// + +#ifndef PrefixHeader_pch +#define PrefixHeader_pch + +// Include any system framework and library headers here that should be included in all compilation units. +// You will also need to set the Prefix Header build setting of one or more of your targets to reference this file. +#ifndef __MACOS__ +#define __MACOS__ +#endif + +#ifdef __OBJC__ + #import +#endif +//#include "bootstrap_priv.h" + +#endif /* PrefixHeader_pch */ diff --git a/Plugin/Info.plist b/Plugin/Info.plist new file mode 100644 index 0000000..4e24119 --- /dev/null +++ b/Plugin/Info.plist @@ -0,0 +1,42 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + + CFBundleVersion + + CFPlugInFactories + + 1E367E04-F5D5-40AE-AB2F-39E96ADDAEF8 + ImagoPluginMain + + CFPlugInTypes + + 30010C1C-93BF-11D8-8B5B-000A95AF9C6A + + 1E367E04-F5D5-40AE-AB2F-39E96ADDAEF8 + + + CMIOHardwareAssistantServiceNames + + com.nolanbrown.Imago + + NSHumanReadableCopyright + Copyright © 2020 Nolan Brown. All rights reserved. + NSPrincipalClass + + + diff --git a/Plugin/LICENSE b/Plugin/LICENSE new file mode 100644 index 0000000..8191a47 --- /dev/null +++ b/Plugin/LICENSE @@ -0,0 +1,22 @@ +MIT License + +Copyright (c) 2020 John Boiles +Copyright (c) 2020 Ryohei Ikegami + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Plugin/Main.swift b/Plugin/Main.swift new file mode 100644 index 0000000..7b4fbba --- /dev/null +++ b/Plugin/Main.swift @@ -0,0 +1,14 @@ +// +// Main.swift +// Imago +// +// Created by Nolan Brown on 7/3/20. +// + +import Foundation +import CoreMediaIO + +@_cdecl("ImagoPluginMain") +public func ImagoPluginMain(allocator: CFAllocator, requestedTypeUUID: CFUUID) -> CMIOHardwarePlugInRef { + return pluginRef +} diff --git a/Plugin/Object.swift b/Plugin/Object.swift new file mode 100644 index 0000000..af531c5 --- /dev/null +++ b/Plugin/Object.swift @@ -0,0 +1,55 @@ +// +// Object.swift +// SimpleDALPlugin +// +// Created by 池上涼平 on 2020/04/25. +// Copyright © 2020 com.seanchas116. All rights reserved. +// + +import Foundation + +protocol Object: class { + var objectID: CMIOObjectID { get } + var properties: [Int: Property] { get } +} + +extension Object { + func hasProperty(address: CMIOObjectPropertyAddress) -> Bool { + return properties[Int(address.mSelector)] != nil + } + + func isPropertySettable(address: CMIOObjectPropertyAddress) -> Bool { + guard let property = properties[Int(address.mSelector)] else { + return false + } + return property.isSettable + } + + func getPropertyDataSize(address: CMIOObjectPropertyAddress) -> UInt32 { + guard let property = properties[Int(address.mSelector)] else { + return 0 + } + return property.dataSize + } + + func getPropertyData(address: CMIOObjectPropertyAddress, dataSize: inout UInt32, data: UnsafeMutableRawPointer) { + guard let property = properties[Int(address.mSelector)] else { + return + } + dataSize = property.dataSize + property.getData(data: data) + } + + func setPropertyData(address: CMIOObjectPropertyAddress, data: UnsafeRawPointer) { + guard let property = properties[Int(address.mSelector)] else { + return + } + property.setData(data: data) + } +} + +var objects = [CMIOObjectID: Object]() + +func addObject(object: Object) { + objects[object.objectID] = object +} diff --git a/Plugin/Plugin.swift b/Plugin/Plugin.swift new file mode 100644 index 0000000..64aafb9 --- /dev/null +++ b/Plugin/Plugin.swift @@ -0,0 +1,18 @@ +// +// Plugin.swift +// SimpleDALPlugin +// +// Created by 池上涼平 on 2020/04/25. +// Copyright © 2020 com.seanchas116. All rights reserved. +// + +import Foundation + +class Plugin: Object { + var objectID: CMIOObjectID = 0 + let name = PLUGIN_NAME + + lazy var properties: [Int : Property] = [ + kCMIOObjectPropertyName: Property(name), + ] +} diff --git a/Plugin/PluginInterface.swift b/Plugin/PluginInterface.swift new file mode 100644 index 0000000..8658846 --- /dev/null +++ b/Plugin/PluginInterface.swift @@ -0,0 +1,279 @@ +// +// PluginInterface.swift +// SimpleDALPlugin +// +// Created by 池上涼平 on 2020/04/25. +// Copyright © 2020 com.seanchas116. All rights reserved. +// + +import Foundation + +private func QueryInterface(plugin: UnsafeMutableRawPointer?, uuid: REFIID, interface: UnsafeMutablePointer?) -> HRESULT { + //dlog() + let pluginRefPtr = UnsafeMutablePointer(OpaquePointer(interface)) + pluginRefPtr?.pointee = pluginRef + return HRESULT(noErr) +} + +private func AddRef(plugin: UnsafeMutableRawPointer?) -> ULONG { + //dlog() + return 0 +} + +private func Release(plugin: UnsafeMutableRawPointer?) -> ULONG { + //dlog() + return 0 +} + +private func Initialize(plugin: CMIOHardwarePlugInRef?) -> OSStatus { + //dlog() + return OSStatus(kCMIOHardwareIllegalOperationError) +} + +private func InitializeWithObjectID(plugin: CMIOHardwarePlugInRef?, objectID: CMIOObjectID) -> OSStatus { + //dlog() + guard let plugin = plugin else { + return OSStatus(kCMIOHardwareIllegalOperationError) + } + + var error = noErr + + let pluginObject = Plugin() + pluginObject.objectID = objectID + addObject(object: pluginObject) + + let device = Device() + error = CMIOObjectCreate(plugin, CMIOObjectID(kCMIOObjectSystemObject), CMIOClassID(kCMIODeviceClassID), &device.objectID) + guard error == noErr else { + dlog("error: \(error)") + return error + } + addObject(object: device) + + let stream = Stream() + error = CMIOObjectCreate(plugin, device.objectID, CMIOClassID(kCMIOStreamClassID), &stream.objectID) + guard error == noErr else { + dlog("error: \(error)") + return error + } + addObject(object: stream) + + device.streamID = stream.objectID + + error = CMIOObjectsPublishedAndDied(plugin, CMIOObjectID(kCMIOObjectSystemObject), 1, &device.objectID, 0, nil) + guard error == noErr else { + dlog("error: \(error)") + return error + } + + error = CMIOObjectsPublishedAndDied(plugin, device.objectID, 1, &stream.objectID, 0, nil) + guard error == noErr else { + dlog("error: \(error)") + return error + } + + return noErr +} +private func Teardown(plugin: CMIOHardwarePlugInRef?) -> OSStatus { + //dlog() + return noErr +} +private func ObjectShow(plugin: CMIOHardwarePlugInRef?, objectID: CMIOObjectID) { + //dlog() +} + +private func ObjectHasProperty(plugin: CMIOHardwarePlugInRef?, objectID: CMIOObjectID, address: UnsafePointer?) -> DarwinBoolean { + //dlog(address?.pointee.mSelector) + guard let address = address?.pointee else { + dlog("Address is nil") + return false + } + guard let object = objects[objectID] else { + dlog("Object not found") + return false + } + return DarwinBoolean(object.hasProperty(address: address)) +} + +private func ObjectIsPropertySettable(plugin: CMIOHardwarePlugInRef?, objectID: CMIOObjectID, address: UnsafePointer?, isSettable: UnsafeMutablePointer?) -> OSStatus { + //dlog(address?.pointee.mSelector) + guard let address = address?.pointee else { + dlog("Address is nil") + return OSStatus(kCMIOHardwareBadObjectError) + } + guard let object = objects[objectID] else { + dlog("Object not found") + return OSStatus(kCMIOHardwareBadObjectError) + } + let settable = object.isPropertySettable(address: address) + isSettable?.pointee = DarwinBoolean(settable) + return noErr +} + +private func ObjectGetPropertyDataSize(plugin: CMIOHardwarePlugInRef?, objectID: CMIOObjectID, address: UnsafePointer?, qualifiedDataSize: UInt32, qualifiedData: UnsafeRawPointer?, dataSize: UnsafeMutablePointer?) -> OSStatus { + //dlog(address?.pointee.mSelector) + guard let address = address?.pointee else { + dlog("Address is nil") + return OSStatus(kCMIOHardwareBadObjectError) + } + guard let object = objects[objectID] else { + dlog("Object not found") + return OSStatus(kCMIOHardwareBadObjectError) + } + dataSize?.pointee = object.getPropertyDataSize(address: address) + return noErr +} + +private func ObjectGetPropertyData(plugin: CMIOHardwarePlugInRef?, objectID: CMIOObjectID, address: UnsafePointer?, qualifiedDataSize: UInt32, qualifiedData: UnsafeRawPointer?, dataSize: UInt32, dataUsed: UnsafeMutablePointer?, data: UnsafeMutableRawPointer?) -> OSStatus { + //dlog(address?.pointee.mSelector) + guard let address = address?.pointee else { + dlog("Address is nil") + return OSStatus(kCMIOHardwareBadObjectError) + } + guard let object = objects[objectID] else { + dlog("Object not found") + return OSStatus(kCMIOHardwareBadObjectError) + } + guard let data = data else { + dlog("data is nil") + return OSStatus(kCMIOHardwareBadObjectError) + } + var dataUsed_: UInt32 = 0 + object.getPropertyData(address: address, dataSize: &dataUsed_, data: data) + dataUsed?.pointee = dataUsed_ + return noErr +} + +private func ObjectSetPropertyData(plugin: CMIOHardwarePlugInRef?, objectID: CMIOObjectID, address: UnsafePointer?, qualifiedDataSize: UInt32, qualifiedData: UnsafeRawPointer?, dataSize: UInt32, data: UnsafeRawPointer?) -> OSStatus { + //dlog() + + guard let address = address?.pointee else { + dlog("Address is nil") + return OSStatus(kCMIOHardwareBadObjectError) + } + guard let object = objects[objectID] else { + dlog("Object not found") + return OSStatus(kCMIOHardwareBadObjectError) + } + guard let data = data else { + dlog("data is nil") + return OSStatus(kCMIOHardwareBadObjectError) + } + object.setPropertyData(address: address, data: data) + return noErr +} + +private func DeviceSuspend(plugin: CMIOHardwarePlugInRef?, deviceID: CMIODeviceID) -> OSStatus { + //dlog() + return noErr +} + +private func DeviceResume(plugin: CMIOHardwarePlugInRef?, deviceID: CMIODeviceID) -> OSStatus { + //dlog() + return noErr +} + +private func DeviceStartStream(plugin: CMIOHardwarePlugInRef?, deviceID: CMIODeviceID, streamID: CMIOStreamID) -> OSStatus { + //dlog() + guard let stream = objects[streamID] as? Stream else { + dlog("no stream") + return OSStatus(kCMIOHardwareBadObjectError) + } + stream.start() + return noErr +} + +private func DeviceStopStream(plugin: CMIOHardwarePlugInRef?, deviceID: CMIODeviceID, streamID: CMIOStreamID) -> OSStatus { + //dlog() + guard let stream = objects[streamID] as? Stream else { + dlog("no stream") + return OSStatus(kCMIOHardwareBadObjectError) + } + stream.stop() + return noErr +} + +private func DeviceProcessAVCCommand(plugin: CMIOHardwarePlugInRef?, deviceID: CMIODeviceID, avcCommand: UnsafeMutablePointer?) -> OSStatus { + //dlog() + return OSStatus(kCMIOHardwareIllegalOperationError) +} + +private func DeviceProcessRS422Command(plugin: CMIOHardwarePlugInRef?, deviceID: CMIODeviceID, rs422Command: UnsafeMutablePointer?) -> OSStatus { + //dlog() + return OSStatus(kCMIOHardwareIllegalOperationError) +} + +private func StreamCopyBufferQueue(plugin: CMIOHardwarePlugInRef?, streamID: CMIOStreamID, queueAlteredProc: CMIODeviceStreamQueueAlteredProc?, queueAlteredRefCon: UnsafeMutableRawPointer?, queueOut: UnsafeMutablePointer?>?) -> OSStatus { + //dlog() + guard let queueOut = queueOut else { + dlog("no queueOut") + return OSStatus(kCMIOHardwareBadObjectError) + } + guard let stream = objects[streamID] as? Stream else { + dlog("no stream") + return OSStatus(kCMIOHardwareBadObjectError) + } + guard let queue = stream.copyBufferQueue(queueAlteredProc: queueAlteredProc, queueAlteredRefCon: queueAlteredRefCon) else { + dlog("no queue") + return OSStatus(kCMIOHardwareBadObjectError) + } + queueOut.pointee = Unmanaged.passRetained(queue) + return noErr +} + +private func StreamDeckPlay(plugin: CMIOHardwarePlugInRef?, streamID: CMIOStreamID) -> OSStatus { + //dlog() + return OSStatus(kCMIOHardwareIllegalOperationError) +} + +private func StreamDeckStop(plugin: CMIOHardwarePlugInRef?, streamID: CMIOStreamID) -> OSStatus { + //dlog() + return OSStatus(kCMIOHardwareIllegalOperationError) +} + +private func StreamDeckJog(plugin: CMIOHardwarePlugInRef?, streamID: CMIOStreamID, speed: Int32) -> OSStatus { + //dlog() + return OSStatus(kCMIOHardwareIllegalOperationError) +} + +private func StreamDeckCueTo(plugin: CMIOHardwarePlugInRef?, streamID: CMIOStreamID, requestedTimecode: Float64, playOnCue: DarwinBoolean) -> OSStatus { + //dlog() + return OSStatus(kCMIOHardwareIllegalOperationError) +} + +private func createPluginInterface() -> CMIOHardwarePlugInInterface { + return CMIOHardwarePlugInInterface( + _reserved: nil, + QueryInterface: QueryInterface, + AddRef: AddRef, + Release: Release, + Initialize: Initialize, + InitializeWithObjectID: InitializeWithObjectID, + Teardown: Teardown, + ObjectShow: ObjectShow, + ObjectHasProperty: ObjectHasProperty, + ObjectIsPropertySettable: ObjectIsPropertySettable, + ObjectGetPropertyDataSize: ObjectGetPropertyDataSize, + ObjectGetPropertyData: ObjectGetPropertyData, + ObjectSetPropertyData: ObjectSetPropertyData, + DeviceSuspend: DeviceSuspend, + DeviceResume: DeviceResume, + DeviceStartStream: DeviceStartStream, + DeviceStopStream: DeviceStopStream, + DeviceProcessAVCCommand: DeviceProcessAVCCommand, + DeviceProcessRS422Command: DeviceProcessRS422Command, + StreamCopyBufferQueue: StreamCopyBufferQueue, + StreamDeckPlay: StreamDeckPlay, + StreamDeckStop: StreamDeckStop, + StreamDeckJog: StreamDeckJog, + StreamDeckCueTo: StreamDeckCueTo) +} + +let pluginRef: CMIOHardwarePlugInRef = { + let interfacePtr = UnsafeMutablePointer.allocate(capacity: 1) + interfacePtr.pointee = createPluginInterface() + + let pluginRef = CMIOHardwarePlugInRef.allocate(capacity: 1) + pluginRef.pointee = interfacePtr + return pluginRef +}() diff --git a/Plugin/Property.swift b/Plugin/Property.swift new file mode 100644 index 0000000..1401df6 --- /dev/null +++ b/Plugin/Property.swift @@ -0,0 +1,155 @@ +// +// Property.swift +// SimpleDALPlugin +// +// Created by 池上涼平 on 2020/04/25. +// Copyright © 2020 com.seanchas116. All rights reserved. +// + +import Foundation + +protocol PropertyValue { + var dataSize: UInt32 { get } + func toData(data: UnsafeMutableRawPointer) + static func fromData(data: UnsafeRawPointer) -> Self +} + +extension String: PropertyValue { + var dataSize: UInt32 { + return UInt32(MemoryLayout.size) + } + func toData(data: UnsafeMutableRawPointer) { + let cfString = self as CFString + let unmanagedCFString = Unmanaged.passRetained(cfString) + UnsafeMutablePointer>(OpaquePointer(data)).pointee = unmanagedCFString + } + static func fromData(data: UnsafeRawPointer) -> Self { + fatalError("not implemented") + } +} + +extension CMFormatDescription: PropertyValue { + var dataSize: UInt32 { + return UInt32(MemoryLayout.size) + } + func toData(data: UnsafeMutableRawPointer) { + let unmanaged = Unmanaged.passRetained(self as! Self) + UnsafeMutablePointer>(OpaquePointer(data)).pointee = unmanaged + } + static func fromData(data: UnsafeRawPointer) -> Self { + fatalError("not implemented") + } +} + +extension CFArray: PropertyValue { + var dataSize: UInt32 { + return UInt32(MemoryLayout.size) + } + func toData(data: UnsafeMutableRawPointer) { + let unmanaged = Unmanaged.passRetained(self as! Self) + UnsafeMutablePointer>(OpaquePointer(data)).pointee = unmanaged + } + static func fromData(data: UnsafeRawPointer) -> Self { + fatalError("not implemented") + } +} + +struct CFTypeRefWrapper { + let ref: CFTypeRef +} + +extension CFTypeRefWrapper: PropertyValue { + var dataSize: UInt32 { + return UInt32(MemoryLayout.size) + } + func toData(data: UnsafeMutableRawPointer) { + let unmanaged = Unmanaged.passRetained(ref) + UnsafeMutablePointer>(OpaquePointer(data)).pointee = unmanaged + } + static func fromData(data: UnsafeRawPointer) -> Self { + fatalError("not implemented") + } +} + +extension UInt32: PropertyValue { + var dataSize: UInt32 { + return UInt32(MemoryLayout.size) + } + func toData(data: UnsafeMutableRawPointer) { + UnsafeMutablePointer(OpaquePointer(data)).pointee = self + } + static func fromData(data: UnsafeRawPointer) -> Self { + return UnsafePointer(OpaquePointer(data)).pointee + } +} + +extension Int32: PropertyValue { + var dataSize: UInt32 { + return UInt32(MemoryLayout.size) + } + func toData(data: UnsafeMutableRawPointer) { + UnsafeMutablePointer(OpaquePointer(data)).pointee = self + } + static func fromData(data: UnsafeRawPointer) -> Self { + return UnsafePointer(OpaquePointer(data)).pointee + } +} + +extension Float64: PropertyValue { + var dataSize: UInt32 { + return UInt32(MemoryLayout.size) + } + func toData(data: UnsafeMutableRawPointer) { + UnsafeMutablePointer(OpaquePointer(data)).pointee = self + } + static func fromData(data: UnsafeRawPointer) -> Self { + return UnsafePointer(OpaquePointer(data)).pointee + } +} + +extension AudioValueRange: PropertyValue { + var dataSize: UInt32 { + return UInt32(MemoryLayout.size) + } + func toData(data: UnsafeMutableRawPointer) { + UnsafeMutablePointer(OpaquePointer(data)).pointee = self + } + static func fromData(data: UnsafeRawPointer) -> Self { + return UnsafePointer(OpaquePointer(data)).pointee + } +} + +class Property { + let getter: () -> PropertyValue + let setter: ((UnsafeRawPointer) -> Void)? + + var isSettable: Bool { + return setter != nil + } + + var dataSize: UInt32 { + getter().dataSize + } + + convenience init(_ value: Element) { + self.init(getter: { value }) + } + + convenience init(getter: @escaping () -> Element) { + self.init(getter: getter, setter: nil) + } + + init(getter: @escaping () -> Element, setter: ((Element) -> Void)?) { + self.getter = getter + self.setter = (setter != nil) ? { data in setter?(Element.fromData(data: data)) } : nil + } + + func getData(data: UnsafeMutableRawPointer) { + let value = getter() + value.toData(data: data) + } + + func setData(data: UnsafeRawPointer) { + setter?(data) + } +} diff --git a/Plugin/Stream.swift b/Plugin/Stream.swift new file mode 100644 index 0000000..0f09560 --- /dev/null +++ b/Plugin/Stream.swift @@ -0,0 +1,179 @@ +// +// Stream.swift +// SimpleDALPlugin +// +// Created by 池上涼平 on 2020/04/25. +// Copyright © 2020 com.seanchas116. All rights reserved. +// + +import Foundation +import CoreImage + + +class Stream: Object { + var objectID: CMIOObjectID = 0 + let name = PLUGIN_STREAM_NAME + let width = 1280 + let height = 720 + private var _sequenceNumber: UInt64 = 0 + + private var frameRate = 30 + + private var queueAlteredProc: CMIODeviceStreamQueueAlteredProc? + private var queueAlteredRefCon: UnsafeMutableRawPointer? + + private var _imagoStream: ImagoStream + private lazy var _context: CIContext = { + return CIContext() + }() + + private lazy var formatDescription: CMVideoFormatDescription? = { + var formatDescription: CMVideoFormatDescription? + let error = CMVideoFormatDescriptionCreate( + allocator: kCFAllocatorDefault, + codecType: kCVPixelFormatType_32ARGB, + width: Int32(width), height: Int32(height), + extensions: nil, + formatDescriptionOut: &formatDescription) + guard error == noErr else { + dlog("CMVideoFormatDescriptionCreate Error: \(error)") + return nil + } + return formatDescription + }() + + private lazy var clock: CFTypeRef? = { + var clock: Unmanaged? = nil + + let error = CMIOStreamClockCreate( + kCFAllocatorDefault, + "\(name) clock" as CFString, + Unmanaged.passUnretained(self).toOpaque(), + CMTimeMake(value: 1, timescale: 10), + 100, 10, + &clock); + guard error == noErr else { + dlog("CMIOStreamClockCreate Error: \(error)") + return nil + } + return clock?.takeUnretainedValue() + }() + + private lazy var queue: CMSimpleQueue? = { + var queue: CMSimpleQueue? + let error = CMSimpleQueueCreate( + allocator: kCFAllocatorDefault, + capacity: 30, + queueOut: &queue) + guard error == noErr else { + dlog("CMSimpleQueueCreate Error: \(error)") + return nil + } + return queue + }() + + lazy var properties: [Int : Property] = [ + kCMIOObjectPropertyName: Property(name), + kCMIOStreamPropertyFormatDescription: Property(formatDescription!), + kCMIOStreamPropertyFormatDescriptions: Property([formatDescription!] as CFArray), + kCMIOStreamPropertyDirection: Property(UInt32(0)), + kCMIOStreamPropertyFrameRate: Property(Float64(frameRate)), + kCMIOStreamPropertyFrameRates: Property(Float64(frameRate)), + kCMIOStreamPropertyMinimumFrameRate: Property(Float64(frameRate)), + kCMIOStreamPropertyFrameRateRanges: Property(AudioValueRange(mMinimum: Float64(frameRate), mMaximum: Float64(frameRate))), + kCMIOStreamPropertyClock: Property(CFTypeRefWrapper(ref: clock!)), + ] + + init() { + _imagoStream = ImagoStream() + frameRate = _imagoStream.framesPerSecond() + } + + func start() { + dlog("START STREAM") + + _imagoStream.receivedFrame = self.enqueueFrame + _imagoStream.start() + + } + + func stop() { + dlog("STOP STREAM") + _imagoStream.stop() + _imagoStream.receivedFrame = nil + } + + func enqueueFrame(frame: Frame) { + guard let queue = queue else { + dlog("queue is nil") + return + } + + guard CMSimpleQueueGetCount(queue) < CMSimpleQueueGetCapacity(queue) else { + dlog("queue is full") + return + } + + guard let pixelBuffer: CVPixelBuffer = frame.buildPixelBuffer(image: nil, inContext: _context) else { + dlog("pixel buffer couldn't be created") + return + } + + let fps = frameRate + let sequence = _sequenceNumber + let scale = UInt64(fps) * 100 + let duration = CMTime(value: CMTimeValue(scale / UInt64(fps)), timescale: CMTimeScale(scale)) + let timestamp = CMTime(value: duration.value * CMTimeValue(sequence), timescale: CMTimeScale(scale)) + + let timing: CMSampleTimingInfo = CMSampleTimingInfo( + duration: duration, + presentationTimeStamp: timestamp, + decodeTimeStamp: timestamp + ) + + + var error = noErr + + var formatDescription: CMFormatDescription? + error = CMVideoFormatDescriptionCreateForImageBuffer( + allocator: kCFAllocatorDefault, + imageBuffer: pixelBuffer, + formatDescriptionOut: &formatDescription) + guard error == noErr else { + dlog("CMVideoFormatDescriptionCreateForImageBuffer Error: \(error)") + return + } + + error = CMIOStreamClockPostTimingEvent(timing.decodeTimeStamp, mach_absolute_time(), true, clock) + guard error == noErr else { + dlog("CMSimpleQueueCreate Error: \(error)") + return + } + var frameTiming = timing + var sampleBufferUnmanaged: Unmanaged? = nil + error = CMIOSampleBufferCreateForImageBuffer( + kCFAllocatorDefault, + pixelBuffer, + formatDescription, + &frameTiming, + sequence, + UInt32(kCMIOSampleBufferNoDiscontinuities), + &sampleBufferUnmanaged + ) + guard error == noErr else { + dlog("CMIOSampleBufferCreateForImageBuffer Error: \(error)") + return + } + + CMSimpleQueueEnqueue(queue, element: sampleBufferUnmanaged!.toOpaque()) + queueAlteredProc?(objectID, sampleBufferUnmanaged!.toOpaque(), queueAlteredRefCon) + _sequenceNumber += 1 + } + + func copyBufferQueue(queueAlteredProc: CMIODeviceStreamQueueAlteredProc?, queueAlteredRefCon: UnsafeMutableRawPointer?) -> CMSimpleQueue? { + self.queueAlteredProc = queueAlteredProc + self.queueAlteredRefCon = queueAlteredRefCon + return self.queue + } + +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..e4aa2c5 --- /dev/null +++ b/README.md @@ -0,0 +1,120 @@ +# Imago + +## Introduction + +This project is named after the [Imago Camera](https://en.wikipedia.org/wiki/Imago_camera) created by Werner Kraus, which was created to capture images at the life size of a subject. + +I created Imago to replace multiple apps that allowed me to use my [Canon M50](https://www.amazon.com/Canon-Mirrorless-Camera-EF-M15-45mm-Video/dp/B079Y45KTJ/) as a Virtual Camera for apps like Zoom. I was using the wonderful [Camera Live](https://github.com/v002/v002-Camera-Live) by [Tom Butterworth (@bangnoise)](https://github.com/bangnoise) and [CamTwist](http://camtwiststudio.com/), both of which have worked extraordinarily well. I encourage you to take a look at both to decide which is a better solution for you. + +The DAL plugin is based off of [SimpleDALPlugin](https://github.com/seanchas116/SimpleDALPlugin) by [Ryohei Ikegami (@seanchas116)](https://github.com/seanchas116), who migrated [CoreMediaIO Device Abstraction Layer Minimal Example](https://github.com/johnboiles/coremediaio-dal-minimal-example) by [John Boiles (@johnboiles)](https://github.com/johnboiles) into Swift. + +The UI is written in SwiftUI for macOS 10.15, so it is missing some of the nicities that were added for macOS 11. + +## System Requirements + +- macOS 10.15 +- Compatible Canon Camera + +## Architecture + +**Caveat:** _This is my first Swift app, so if there are better ways to do something please let me know._ + +The project is split into two Targets, _Imago_ is the macOS app and _ImagoPlugin_ is the Virtual Camera plugin (aka Device Abstraction Layer). + +Beyond the base implementation of the CMIO Plugin, _ImagoPlugin_ is only responsible for communicating with _Imago_ and reconstituing the raw data back into a `CVPixelBuffer`. This is accomplished by setting up dedicated inter-process communications using [CFMessagePort](https://developer.apple.com/documentation/corefoundation/cfmessageport-rs2). + +The _Imago_ app is responsible for all communications and data management between the Camera (via the EDSDK.framework), image decompression, and coordinates all the inter-process communications to active instances of _ImagoPlugin_. + +### Camera Communications +Communications between _Imago_ and the EDSDK can be summarized as: +1. Initialize EDSDK +2. Get the available list of Cameras +3. Connect to a selected Camera +4. Get required properties about the Camera +5. Set the Camera Output mode +5. Get Image Data + +All EDSDK calls are in the `CanonCamera` subclass of `Camera` where they're coordinated using a dedicated DispatchQueue. + +### Imago <=> Imago Plugin Communcations + +For a primer on inter-process communications, check out this [NSHipster post](https://nshipster.com/inter-process-communication/) by [@Mattt](https://nshipster.com/authors/mattt/). + +_Imago_ uses CFMessagePort, which in turn uses Mach Ports under the hood. These provide fast, dedicated communication channels between processes that allow _Imago_ to push image data directly into _ImagoPlugin_. The main limitation of them is they only provide only 1:1 communications. Given that there can be multiple instances of an _ImagoPlugin_ running if a user has multiple client programs running (for example Zoom and Google Meet in Chrome), _Imago_ implements a Publisher/Subscriber architecture to allow for dedicated 1:1 communication between multiple processes. There are other ways of acheving the same result, either by using lower level Mach Ports or higher level XPC, and passing an IOSurface object between processess; however both those ways are more complicated then using CFMessagePort. + + +1. Start Publisher at known port address (ie. `com.nolanbrown.Imago.conductor.publisher`) + - If a Publisher isn't available, the Subscriber will periodically attempt to connect until it's able to establish a connection +2. When a Subscriber is ready to connect to the Publisher, it will create a UUID and open a local port address using that ID (ie `com.nolanbrown.Imago.conductor.e02d59bd-16fa-41a0-bf70-70adfc02e877`) +3. When Publisher receives a `Register` message from a subscriber process , it will open a remote port the Subcriber at that ID +4. Publisher sends data to all confirmed Subscribers +5. A Subscriber will periodically `Register` with the Publisher to confirm that both connections + + + +## Building + + +To get started building _Imago_, first the required libraries must be downloaded. To simplify this process, you only need to run `setup.sh` that's in the root directory of this project. The script will install and setup `libturbo-jpeg` and the `Canon EOS SDK`, once that's completed you can build the project. + +_ImagoPlugin_ is set as a dependecy for _Imago_, so the plugin will be automatically built every time _Imago_ is. + +### Imago Plugin Installation +To use the _ImagoPlugin_, it needs to be installed in `/Library/CoreMediaIO/Plug-Ins/DAL/`. It can be tedious to manually copy the plugin so there is a Run Script build phase that will automatically move the newly built `ImagoPlugin.plugin` to that directory. + +The Run Script requires a sudoer password to be provided which is done via a query to the Keychain. To set this up for yourself, create a new entry in Keychain Access with both Item Name and Account Name set as `ImagoBuild` and enter a password for a Sudo user, or create a new Keychain Item from the command line with the command `security add-generic-password -a ImagoBuild -s ImagoBuild -w` and enter a password for Sudo user. + +Below is the executed Run Script to automatically install the built plugin. +``` +if [ $CONFIGURATION == "Debug" ];then + echo $(security find-generic-password -ga "ImagoBuild" -w) | sudo -S rm -R /Library/CoreMediaIO/Plug-Ins/DAL/$PRODUCT_NAME.plugin; + sudo cp -R $SYMROOT/$CONFIGURATION/$PRODUCT_NAME.plugin /Library/CoreMediaIO/Plug-Ins/DAL/$PRODUCT_NAME.plugin +fi +``` + +### Required Libaries + +#### EDSDK (Canon EOS SDK) +To download the EDSDK.framework, register for a developer account with Canon. For US developers, you can do that [here](https://developercommunity.usa.canon.com/canon) and then [download the SDK](https://developercommunity.usa.canon.com/canon?id=sdk_download). + +Once downloaded, unzip the file and open `Macintosh.dmg`. From the mounted disk image `Macintosh` copy `EDSDK/Framework/EDSDK.framework` to `/Frameworks/EDSDK/EDSDK.framework` and the `Header` directory to `/Frameworks/EDSDK/Header/` + + + +#### libjpeg-turbo + +You can download the latest version of the libjpeg-turbo binary [here](https://sourceforge.net/projects/libjpeg-turbo/files/) or visit their [website](https://libjpeg-turbo.org/Documentation/OfficialBinaries). + + + +## Debugging + +Use [Cameo](https://github.com/lvsti/Cameo) to load and test the _ImagoPlugin_ or use any application that allow for Virtual Cameras. + + +### Notes +The error `"The file “ic_hevcdec.framework” couldn’t be opened because there is no such file."` is part of EDSDK and can be ignored. + + +## Troubleshooting +- Connect your camera via USB directly to your computer, not through a USB hub or Dock +- Try a different USB cable +- Turn off any other apps that are communicating with the Camera (EOS Utility, Camera Live, etc) +- Turn off your camera and turn it back on again + + +## Credits + +### [Icon By KDesign](https://dribbble.com/shots/7485922-Abstract-icons) + +## TODO + +- [ ] Improve Plugin re-connection +- [ ] Test with more Canon Cameras +- [ ] Implement an iOS App to act as remote camera +- [ ] Add more filter options and ability to configure +- [ ] Add a Live Preview mode +- [ ] Add preferences pane +- [ ] Add support for other DSLR manufacturers +- [ ] Calculate and Display frame rate +- [ ] Fix UI bugs on macOS 11 diff --git a/Shared/Const.swift b/Shared/Const.swift new file mode 100644 index 0000000..74e11a2 --- /dev/null +++ b/Shared/Const.swift @@ -0,0 +1,22 @@ +// +// Const.swift +// Imago +// +// Created by Nolan Brown on 7/20/20. +// Copyright © 2020 Nolan Brown. All rights reserved. +// + +import Foundation + +let PLUGIN_NAME = "Imago" + +let PLUGIN_DEVICE_NAME = PLUGIN_NAME +let PLUGIN_DEVICE_MANUFACTURER = "Nolan Brown" +let PLUGIN_DEVICE_UID = "Imago Plugin Device" +let PLUGIN_DEVICE_MODEL_UID = "Imago Plugin Model" + +let PLUGIN_STREAM_NAME = PLUGIN_NAME + +let IMAGO_SERVER_NAME = "com.nolanbrown.Imago.conductor" + +let DEFAULT_PIXEL_FORMAT = kCVPixelFormatType_32ARGB diff --git a/Shared/DLog.swift b/Shared/DLog.swift new file mode 100644 index 0000000..ec80990 --- /dev/null +++ b/Shared/DLog.swift @@ -0,0 +1,25 @@ +// +// DLog.swift +// Imago +// +// Created by Nolan Brown on 7/19/20. +// Copyright © 2020 Nolan Brown. All rights reserved. +// + +import Foundation + +func dlog(_ message: Any = "", function: String = #function) { + NSLog("Imago: \(function): \(message)") +} + +func flog(_ frame: Frame, _ message: Any = "", timestamp: UInt64 = mach_absolute_time()) { + //let diff = TimeInterval(timestamp - UInt64(frame.timestamp)) / TimeInterval(NSEC_PER_SEC) + //NSLog("Frame \(frame.id): \(message) - \(diff)s") +} + +func printTimeElapsedWhenRunningCode(title:String, operation:()->()) { + let startTime = CFAbsoluteTimeGetCurrent() + operation() + let timeElapsed = CFAbsoluteTimeGetCurrent() - startTime + print("Time elapsed for \(title): \(timeElapsed) s.") +} diff --git a/Shared/Extensions/CIImage+Imago.swift b/Shared/Extensions/CIImage+Imago.swift new file mode 100644 index 0000000..4d903f2 --- /dev/null +++ b/Shared/Extensions/CIImage+Imago.swift @@ -0,0 +1,49 @@ +// +// CIImage+Imago.swift +// Imago +// +// Created by Nolan Brown on 7/29/20. +// Copyright © 2020 Nolan Brown. All rights reserved. +// + +import Foundation +import CoreImage + +extension CIImage { + + func toCGImage(withContext context: CIContext) -> CGImage? { + return context.createCGImage(self, from: self.extent) + } + + func toPixelBuffer(withContext context: CIContext, fromPool pool: CVPixelBufferPool? = nil) -> CVPixelBuffer? { + let imageRect = self.extent + let size = imageRect.size + + + guard let pixelBuffer = CVPixelBuffer.create(withSize: size, fromPool: pool) else { + return nil + } + + let rect = NSMakeRect(0, 0, size.width, size.height); + let rgbColorSpace = CGColorSpaceCreateDeviceRGB() + + CVPixelBufferLockBaseAddress(pixelBuffer, []) + + context.render(self, to:pixelBuffer, bounds: rect, colorSpace: rgbColorSpace) + + CVPixelBufferUnlockBaseAddress(pixelBuffer, []) + + return pixelBuffer + } + + func toJpegData(_ context: CIContext? = nil) -> Data? { + let colorSpace = CGColorSpaceCreateDeviceRGB() + + let ctx: CIContext = context ?? CIContext() + + let options = [kCGImageDestinationLossyCompressionQuality as CIImageRepresentationOption: 1.0] + + let data = ctx.jpegRepresentation(of: self, colorSpace: colorSpace, options: options) + return data + } +} diff --git a/Shared/Extensions/CVPixelBuffer+Data.swift b/Shared/Extensions/CVPixelBuffer+Data.swift new file mode 100644 index 0000000..4d8ee10 --- /dev/null +++ b/Shared/Extensions/CVPixelBuffer+Data.swift @@ -0,0 +1,129 @@ +// +// CVPixelBuffer+Data.swift +// Imago +// +// Created by Nolan Brown on 7/29/20. +// Copyright © 2020 Nolan Brown. All rights reserved. +// + +import Foundation +import VideoToolbox + + +extension CVPixelBuffer { + + var width: Int { + return CVPixelBufferGetWidth(self) + } + var height: Int { + return CVPixelBufferGetHeight(self) + } + + var size: NSSize { + return NSSize(width: self.width, height: self.height) + } + + func toImage() -> CGImage? { + var cgImage: CGImage? + VTCreateCGImageFromCVPixelBuffer(self, options: nil, imageOut: &cgImage) + + return cgImage + } + + func toData() -> Data? { + CVPixelBufferLockBaseAddress(self,.readOnly) + + let bufferSize = CVPixelBufferGetDataSize(self) + + // UnsafeMutableRawPointer + guard let baseAddress = CVPixelBufferGetBaseAddress(self) else { + return nil + } + + //let data = Data(bytesNoCopy: baseAddress, count: bufferSize, deallocator: .free) + let data = Data(bytes: baseAddress, count: bufferSize) + + CVPixelBufferUnlockBaseAddress(self, .readOnly) + return data + } + + static func fromData(_ data: Data, height: Int, width: Int) -> CVPixelBuffer? { + + // Create an empty pixel buffer + var _pixelBuffer: CVPixelBuffer? + var err = CVPixelBufferCreate(kCFAllocatorDefault, width, height, kCVPixelFormatType_32ARGB, nil, &_pixelBuffer) + + guard let pixelBuffer = _pixelBuffer else {return nil} + + // Generate the video format description from that pixel buffer + var format: CMFormatDescription? + err = CMVideoFormatDescriptionCreateForImageBuffer(allocator: nil, imageBuffer: pixelBuffer, formatDescriptionOut: &format) + if (err != noErr) { + dlog("CMVideoFormatDescriptionCreateForImageBuffer err \(err)") + return nil + } + + // Copy memory into the pixel buffer + CVPixelBufferLockBaseAddress(pixelBuffer, []) + let baseAddress = CVPixelBufferGetBaseAddress(pixelBuffer) +// let bytesPerRow = CVPixelBufferGetBytesPerRow(pixelBuffer) +// let srcBytesPerRow = width * 2 + + data.withUnsafeBytes { rawBufferPointer in + let rawPtr = rawBufferPointer.baseAddress! + + memcpy(baseAddress, rawPtr, data.count) + + } + + CVPixelBufferUnlockBaseAddress(pixelBuffer, []); + return pixelBuffer + } + + + static func create(withSize size:NSSize, fromPool pool: CVPixelBufferPool? = nil, attributes: [String:Any]? = nil, pixelFormat: OSType = DEFAULT_PIXEL_FORMAT) -> CVPixelBuffer? { + + var attrs = attributes + if attrs == nil { + attrs = CVPixelBuffer.defaultAttributes(additionalAttributes: attributes, size: size, pixelFormat: pixelFormat) + + } + + var pixelBuffer : CVPixelBuffer? + var status: CVReturn + + if pool != nil { + status = CVPixelBufferPoolCreatePixelBuffer(kCFAllocatorDefault, pool!, &pixelBuffer) + } + else { + status = CVPixelBufferCreate(kCFAllocatorDefault, Int(size.width), Int(size.height), pixelFormat, attrs! as CFDictionary, &pixelBuffer) + } + + if status != kCVReturnSuccess { + dlog("Failed to create CVPixelBuffer: \(status)") + } + + return pixelBuffer + } + + + static func defaultAttributes(additionalAttributes: [String:Any]? = nil, size: NSSize? = nil, pixelFormat: OSType = DEFAULT_PIXEL_FORMAT) -> Dictionary { + var attributes: [String: Any] = [ + kCVPixelBufferPixelFormatTypeKey as String: pixelFormat, + kCVPixelBufferCGImageCompatibilityKey as String: kCFBooleanTrue!, + kCVPixelBufferCGBitmapContextCompatibilityKey as String: kCFBooleanTrue!, + //kCVPixelBufferMetalCompatibilityKey as String: kCFBooleanTrue! + ] + + if size != nil { + attributes[kCVPixelBufferWidthKey as String] = Int(size!.width) + attributes[kCVPixelBufferHeightKey as String] = Int(size!.height) + } + if additionalAttributes != nil { + attributes.merge(additionalAttributes!) { (_, new) in new } + } + + return attributes + } + +} diff --git a/Shared/Extensions/CVPixelBufferPool+Imago.swift b/Shared/Extensions/CVPixelBufferPool+Imago.swift new file mode 100644 index 0000000..e3d42be --- /dev/null +++ b/Shared/Extensions/CVPixelBufferPool+Imago.swift @@ -0,0 +1,25 @@ +// +// CVPixelBufferPool+Imago.swift +// Imago +// +// Created by Nolan Brown on 8/10/20. +// Copyright © 2020 Nolan Brown. All rights reserved. +// + +import Foundation + +extension CVPixelBufferPool { + static func create(size: NSSize) -> CVPixelBufferPool? { + + let outputPixelBufferAttributes = CVPixelBuffer.defaultAttributes(additionalAttributes: nil, size: size, pixelFormat: DEFAULT_PIXEL_FORMAT) + + let poolAttributes = [kCVPixelBufferPoolMinimumBufferCountKey as String: 5] + var cvPixelBufferPool: CVPixelBufferPool? + // Create a pixel buffer pool with the same pixel attributes as the input format description + CVPixelBufferPoolCreate(kCFAllocatorDefault, + poolAttributes as NSDictionary?, + outputPixelBufferAttributes as NSDictionary?, + &cvPixelBufferPool) + return cvPixelBufferPool + } +} diff --git a/Shared/Frame.swift b/Shared/Frame.swift new file mode 100644 index 0000000..ca334e0 --- /dev/null +++ b/Shared/Frame.swift @@ -0,0 +1,286 @@ +// +// Frame.swift +// Imago +// +// Created by Nolan Brown on 7/16/20. +// Copyright © 2020 Nolan Brown. All rights reserved. +// + +import Foundation +import SwiftUI + + +struct Frame { + let id: String + let height: Int + let width: Int + let bytesPerRow: Int + let pixelSize: Int + var data: Data + + var bitmapInfo = CGImageAlphaInfo.premultipliedLast.rawValue | CGBitmapInfo.byteOrder32Little.rawValue + var timestamp: UInt64 = 0 + var fps: Int = 30 + var sequence: UInt64 = 0 + var isDataPixelBuffer: Bool = false + + + init(data: Data, height: Int, width: Int, bytesPerRow: Int, pixelSize: Int, id: String? = nil) { + self.id = id ?? UUID().uuidString + self.data = data + self.height = height + self.width = width + self.bytesPerRow = bytesPerRow + self.pixelSize = pixelSize + } + func getSize() -> CGSize { + return CGSize(width: self.width, height: self.height) + } + + func copy() -> Frame { + var frame = Frame(data: data, height: height, width: width, bytesPerRow: bytesPerRow, pixelSize: pixelSize) + frame.isDataPixelBuffer = isDataPixelBuffer + frame.bitmapInfo = bitmapInfo + frame.timestamp = timestamp + frame.fps = fps + frame.sequence = UInt64(sequence) + return frame + } + + func elapsedSeconds(_ ts: UInt64 = mach_absolute_time()) -> Double { + let current_ts = self.timestamp + var recent_ts = ts + var past_ts = current_ts + if recent_ts < past_ts { + recent_ts = current_ts + past_ts = ts + } + + let diff = TimeInterval(recent_ts - past_ts) / TimeInterval(NSEC_PER_SEC) + let fpx = Double(fps) + let remainer = (diff * fpx).truncatingRemainder(dividingBy: 1) + let calcedFPS = fpx - (fpx * remainer) + let fpsString = String(format:"%.2f",calcedFPS) + //dlog("\(self) elapsedSeconds \(diff) \(fpsString)fps") + return diff + } + + func getTimingInfo() -> CMSampleTimingInfo { + let scale = UInt64(fps) * 100 + let duration = CMTime(value: CMTimeValue(scale / UInt64(fps)), timescale: CMTimeScale(scale)) + let timestamp = CMTime(value: duration.value * CMTimeValue(sequence), timescale: CMTimeScale(scale)) + + return CMSampleTimingInfo( + duration: duration, + presentationTimeStamp: timestamp, + decodeTimeStamp: timestamp + ) + } + + func getVideoFormatDescription() -> CMVideoFormatDescription? { + var formatDescription: CMVideoFormatDescription? + let error = CMVideoFormatDescriptionCreate( + allocator: kCFAllocatorDefault, + codecType: kCVPixelFormatType_32ARGB, + width: Int32(width), height: Int32(height), + extensions: nil, + formatDescriptionOut: &formatDescription) + if error != noErr { + dlog("Error creating CMVideoFormatDescription \(error)") + } + return formatDescription + } + + func hash() -> Int { + return data.hashValue + } +} + + +/* + +Serialization +CFPropertyListCreateData + +Time elapsed for serialize: 0.00884699821472168 s. +Time elapsed for create: 0.005692005157470703 s. +___________ + +JSONEncoder +Time elapsed for jsonserialize: 0.0524829626083374 s. +Time elapsed for jsoncreate: 0.049543023109436035 s. + + + We need to serialize Frame information into a Data object to send to the plugin + +*/ + +extension Frame { + func serialize() -> Data? { + let obj = [ + "id": id, + "height": height, + "width": width, + "bytesPerRow": bytesPerRow, + "pixelSize": pixelSize, + "bitmapInfo": bitmapInfo, + "timestamp": timestamp, + "fps": fps, + "sequence": sequence, + "data": data, + "isDataPixelBuffer": isDataPixelBuffer, + ] as [String : Any] + let data: Unmanaged = CFPropertyListCreateData(kCFAllocatorDefault, obj as CFPropertyList, CFPropertyListFormat.binaryFormat_v1_0, 0, nil) + return data.takeRetainedValue() as Data + } + + static func deserialize(fromData data: Data) -> Frame? { + var error: Unmanaged? + var inputFormat = CFPropertyListFormat.binaryFormat_v1_0 + let options: CFOptionFlags = 0 + guard let plist: CFPropertyList = CFPropertyListCreateWithData(kCFAllocatorDefault, data as CFData, options, &inputFormat, &error)?.takeRetainedValue() + else { + return nil + } + + let id = plist["id"] as! String + let w = plist["width"] as! Int + let h = plist["height"] as! Int + let bpr = plist["bytesPerRow"] as! Int + let ps = plist["pixelSize"] as! Int + let bi = plist["bitmapInfo"] as! UInt32 + let ts = plist["timestamp"] as! UInt64 + let fps = plist["fps"] as! Int + let sequence = plist["sequence"] as! UInt64 + let d = plist["data"] as! Data + let isDataPixelBuffer = plist["isDataPixelBuffer"] as! Bool + + var frame = Frame(data: d, height: h, width: w, bytesPerRow: bpr, pixelSize: ps, id: id) + frame.isDataPixelBuffer = isDataPixelBuffer + frame.bitmapInfo = UInt32(bi) + frame.timestamp = UInt64(ts) + frame.fps = fps + frame.sequence = UInt64(sequence) + return frame + + } + +} + + +extension Frame { + func buildCIImage() -> CIImage? { + let colorSpace = CGColorSpaceCreateDeviceRGB() + if self.bytesPerRow == 0 { + let bitmapImg = NSBitmapImageRep(data: data) + if bitmapImg != nil { + return CIImage(bitmapImageRep: bitmapImg!) + } + + } + else { + return CIImage(bitmapData: data, + bytesPerRow: bytesPerRow, + size: getSize(), + format: CIFormat.BGRA8, + colorSpace: colorSpace) + + } + return nil + } + + func findFaces(_ cicontext: CIContext?, _ ciimage: CIImage?) -> CIImage? { + + var ctx = cicontext + if ctx == nil { + ctx = CIContext() + } + + var ciimg = ciimage + if ciimg == nil { + ciimg = buildCIImage() + } + + // create a face detector - since speed is not an issue we'll use a high accuracy + // detector + let detector: CIDetector! = CIDetector(ofType: CIDetectorTypeFace, + context: ctx, + options: [CIDetectorAccuracy: CIDetectorAccuracyHigh]) + let features = detector.features(in: ciimage!, options: nil) + for faceFeature in features { + var faceRect = faceFeature.bounds + faceRect = CGRect(x: faceRect.minX - 30, y: faceRect.minY - 30, width: faceRect.width + 60, height: faceRect.height + 60) + + ciimg = ciimg!.cropped(to: faceRect) + + print("faceFeature \(faceFeature)") + return ciimg + + } + + return ciimg + } + + func buildCGContext() -> CGContext? { + let rawBytes: UnsafeMutableRawPointer = unsafeBitCast(self.data, to: UnsafeMutableRawPointer.self) + let colorSpace = CGColorSpaceCreateDeviceRGB() + + guard let imageContext = CGContext(data: rawBytes, + width: self.width, + height: self.height, + bitsPerComponent: 8, + bytesPerRow: self.bytesPerRow, + space: colorSpace, + bitmapInfo: self.bitmapInfo, + releaseCallback: nil, releaseInfo: nil) else {return nil} + + return imageContext + + //guard let cgImage = imageContext.makeImage() else {return nil} + } + + /* +https://developer.apple.com/library/archive/documentation/GraphicsImaging/Conceptual/CoreImaging/ci_performance/ci_performance.html#//apple_ref/doc/uid/TP30001185-CH10-SW1 + https://docs.huihoo.com/apple/wwdc/2011/session_422__using_core_image_on_ios_and_mac_os_x.pdf + */ + func image(_ ciContext: CIContext? = nil) -> NSImage? { + guard let img = buildCIImage() else { + return nil + } + let ctx = ciContext ?? CIContext() + + if let cgImg = ctx.createCGImage(img, from: img.extent) { + return NSImage(cgImage: cgImg, size: self.getSize()) + } + return nil + } + + func cgimage(_ ciContext: CIContext? = nil) -> CGImage? { + guard let img = buildCIImage() else { + return nil + } + let ctx = ciContext ?? CIContext() + + if let cgImg = ctx.createCGImage(img, from: img.extent) { + return cgImg + } + return nil + } + + + func buildPixelBuffer(image: CIImage? = nil, inContext ciContext: CIContext? = nil) -> CVPixelBuffer? { + + if isDataPixelBuffer { + return CVPixelBuffer.fromData(data, height: height, width: width) + } + + guard let ciImage = image ?? buildCIImage() else { + return nil + } + + let ctx = ciContext ?? CIContext() + return ciImage.toPixelBuffer(withContext: ctx) + } + +} + diff --git a/Shared/IPC/CallerID.swift b/Shared/IPC/CallerID.swift new file mode 100644 index 0000000..5533567 --- /dev/null +++ b/Shared/IPC/CallerID.swift @@ -0,0 +1,148 @@ +// +// CallerID.swift +// Imago +// +// Created by Nolan Brown on 7/21/20. +// Copyright © 2020 Nolan Brown. All rights reserved. +// + +import Foundation + +enum CallerID: Int32 { + case Register = 0x1 + case Deregister = 0x2 + case RegistrationConfirmation = 0x3 + case GetFrame = 0x4 + case ReceiveFrame = 0x5 + case Ping = 0x6 + case ReceiveData = 0x7 + case KillServer = 0x99 +} + +enum ResponseCode: Int8 { + case OK = 0x1 + case Success = 0x2 + case NotAvailable = 0x3 + case ConnectionClosed = 0x4 + + func data() -> Data { + var theData : Int8 = self.rawValue + return Data(bytes: &theData, count: 1) + } +} + + + +struct Response { + enum Code: UInt8 { + case OK = 1 + case Success = 2 + case NotAvailable = 3 + case ConnectionClosed = 4 + case Unknown = 99 + + func data() -> Data { + var theData : UInt8 = self.rawValue + return Data(bytes: &theData, count: 1) + } + } + + var code: Code = .Unknown + + let callerID: CallerID + let status: Int32 + let data: Data? + + init(callerID: CallerID, status: Int32, data: Data?) { + self.callerID = callerID + self.status = status + self.data = data + + if self.data?.count == 1 { + self.code = Code(rawValue: self.data![0]) ?? Code.Unknown + } + + } + + func asString() -> String? { + if data != nil { + return String(data: data!, encoding: .utf8) + } + return nil + } + func statusString() -> String { + switch status { + case kCFMessagePortSuccess: + return "CFMessagePortSuccess" + case kCFMessagePortSendTimeout: + return "CFMessagePortSendTimeout" + case kCFMessagePortReceiveTimeout: + return "CFMessagePortReceiveTimeout" + case kCFMessagePortIsInvalid: + return "CFMessagePortIsInvalid" + case kCFMessagePortTransportError: + return "CFMessagePortTransportError" + case kCFMessagePortBecameInvalidError: + return "CFMessagePortBecameInvalidError" + default: + return "Unknown Status" + } + } + + func isSuccess() -> Bool { + switch status { + case kCFMessagePortSuccess: + return true + default: + return false + } + } + + func isTimeoutError() -> Bool { + switch status { + case kCFMessagePortSendTimeout, kCFMessagePortReceiveTimeout: + return true + default: + return false + } + } + + func isInvalidPortError() -> Bool { + switch status { + case kCFMessagePortIsInvalid, kCFMessagePortBecameInvalidError: + return true + default: + return false + } + } +} + +struct Request { + let port: CFMessagePort + let callerID: CallerID + let data: Data? + let str: String? + + init(port: CFMessagePort, callerID: CallerID, data: Data? = nil, str: String? = nil) { + self.port = port + self.callerID = callerID + self.data = data + self.str = str + } + + func messageID() -> Int32 { + return callerID.rawValue + } + + func asCFData() -> CFData? { + var requestData: CFData? = nil + if str != nil { + requestData = str!.data(using: .utf8)! as CFData + + } + else if data != nil { + requestData = data! as CFData + } + return requestData + } +} diff --git a/Shared/IPC/PubSub.swift b/Shared/IPC/PubSub.swift new file mode 100644 index 0000000..1803187 --- /dev/null +++ b/Shared/IPC/PubSub.swift @@ -0,0 +1,560 @@ +// +// PubSub.swift +// Imago +// +// Created by Nolan Brown on 7/24/20. +// Copyright © 2020 Nolan Brown. All rights reserved. +// + +import Foundation +import CoreFoundation + +let PortInvalidationCallback : CFMessagePortInvalidationCallBack = { + (port: CFMessagePort?, info: UnsafeMutableRawPointer?) -> Void in + print("PortInvalidationCallback \(String(describing: port))") +} + +/* + CFMessagePortCallBack + + All CFMessagePortCreateLocal methods are provided PortRequestCallback as the callback function. It's here we do: + 1. Determine what type the request is (ie CallerID) + 2. Load the related initialized Conductor object + 3. Process the request + + */ +let ServerPortCallback : CFMessagePortCallBack = { + (port: CFMessagePort?, messageID: Int32, data: CFData?, info: UnsafeMutableRawPointer?) -> Unmanaged? in + + let callbackID = CallerID(rawValue: messageID) + + var serverInstance : PubSub? + if info != nil { + serverInstance = unsafeBitCast(info, to: PubSub.self) + } + if serverInstance == nil { + print("ERROR: PubSub Instance Unavailable") + return nil + } + + var requestData: Data? + if data != nil && CFDataGetLength(data!) > 0 { + requestData = data! as Data + } + + guard let callerID = callbackID else { + return nil + } + + guard let responseData = serverInstance!.receivedRequest(requestData, callerID) else { + return nil + } + return Unmanaged.passRetained(responseData as CFData) +} + + +enum PortConnectionStatus { + case Ready + case Connecting + case Connected + case Disconnected // A connect was made invalid + case Closed // A connection was closed +} + + +typealias PortConnectionStatusChangedCallback = (PortConnection, PortConnectionStatus)->Void + +class PortConnection : Hashable, Identifiable, CustomDebugStringConvertible { + var automaticallyReconnect: Bool = true + var maxConnectionAttempts: Int? = nil + var retryConnectionInterval: Int = 1 + var timeout: Double = 10.0 + var lastResponseTime: Date? = nil + + var _queue: DispatchQueue = DispatchQueue(label: "PortConnection", qos: .userInteractive) + + private lazy var _retryConnectionTimer: RepeatingTimer = { + let timer = RepeatingTimer(secondsInterval: retryConnectionInterval) //, queue: _queue) + timer.eventHandler = _connect + return timer + }() + + private var _numConnectionAttempts: Int = 0 + + internal let _runLoopMode: CFRunLoopMode = CFRunLoopMode.defaultMode //CFRunLoopMode.commonModes + + private var _remotePort: CFMessagePort? + private let _remotePortName: String + + private var _retryingConnection: Bool = false + + public private(set) var status: PortConnectionStatus { + didSet { + self.onStatusChanged?(self, self.status) + } + } + + var onStatusChanged: PortConnectionStatusChangedCallback? + + private var _onConnect: PortConnectionStatusChangedCallback? + + deinit { + self.close() + //_retryConnectionTimer.cancel() + } + + public init(_ portName: String, queue: DispatchQueue? = nil) { + if queue != nil { + _queue = queue! + } + _remotePortName = portName + status = .Ready + + dlog("Using \(_remotePortName) for Connection") + } + + var debugDescription: String { + return "\(String(describing: type(of: self)))(port:\(portName()) status:'\(String(describing: type(of: status)))')" + } + + //var model: String + static func == (lhs: PortConnection, rhs: PortConnection) -> Bool { + return lhs.portName() == rhs.portName() + } + public func hash(into hasher: inout Hasher) { + hasher.combine(_remotePortName) + } + + func isConnected() -> Bool { + if _remotePort == nil { + return false + } + return true + } + + + func portName() -> String { + return _remotePortName + } + + + func connect(_ onConnect: PortConnectionStatusChangedCallback? = nil) { + // Don't attempt to connect if we are trying or are already connected + if status == .Connecting || status == .Connected { + if onConnect != nil && status == .Connected { + onConnect!(self, status) + } + return + } + if onConnect != nil { + _onConnect = onConnect + } + status = .Connecting + //_connect() + if _remotePort == nil { + _retryConnectionTimer.resume() + } + } + + func close() { + _invalidatePort() + _onConnect = nil + _retryConnectionTimer.suspend() + self.status = .Closed + } + + func sendRequest(_ callerID: CallerID, data: Data? = nil, str: String? = nil, onCompletion: @escaping((Response)->Void)) { + DispatchQueue.main.async { [unowned self] in + let messageID = callerID.rawValue + + var requestData: CFData? = nil + if str != nil { + requestData = str!.data(using: .utf8)! as CFData + + } + else if data != nil { + requestData = data! as CFData + } + + + var responseRawData: Unmanaged? + + let status = CFMessagePortSendRequest(self._remotePort, + messageID, + requestData, + self.timeout, + self.timeout, + self._runLoopMode.rawValue, + &responseRawData) + + var responseData: Data? + if responseRawData != nil { + responseData = responseRawData!.takeRetainedValue() as Data + if responseData?.count == 0 { + responseData = nil + } + } + + self.lastResponseTime = Date() + let response = Response(callerID: callerID, status: status, data: responseData) + onCompletion(response) + } + + } + func sendRequest(_ callerID: CallerID, data: Data? = nil, str: String? = nil) -> Response { + if status != .Connected { + return Response(callerID: callerID, status: kCFMessagePortIsInvalid, data: nil) + } + + let messageID = callerID.rawValue + + var requestData: CFData? = nil + if str != nil { + requestData = str!.data(using: .utf8)! as CFData + + } + else if data != nil { + requestData = data! as CFData + } + + + var responseRawData: Unmanaged? + + let status = CFMessagePortSendRequest(_remotePort, + messageID, + requestData, + timeout, + timeout, + _runLoopMode.rawValue, + &responseRawData) + + var responseData: Data? + if responseRawData != nil { + responseData = responseRawData!.takeRetainedValue() as Data + if responseData?.count == 0 { + responseData = nil + } + } + self.lastResponseTime = Date() + + let response = Response(callerID: callerID, status: status, data: responseData) + return response + } + + + private func _invalidatePort() { + if _remotePort == nil { + CFMessagePortInvalidate(_remotePort) + _remotePort = nil + } + } + + private func _resetConnection() { + _invalidatePort() + _numConnectionAttempts = 0 + _retryConnectionTimer.resume() + status = .Connecting + + } + + private func _didConnect() { + if status == .Connected { + return + } + status = .Connected + DispatchQueue.main.async { [unowned self] in + self._onConnect?(self, self.status) + + self._onConnect = nil + } + } + + // _retryConnectionTimer handler + private func _connect() { + _retryingConnection = true + dlog("CONNECTING ... \(String(describing: _remotePort))") + if _remotePort == nil { + // Close the connection after reaching the max number of attempts + if maxConnectionAttempts != nil { + if _numConnectionAttempts >= maxConnectionAttempts! { + self.close() + return + } + } + + + if let port = CFMessagePortCreateRemote(nil, _remotePortName as CFString) { + dlog("CONNECTED \(port)") + _remotePort = port + } + else { + // We couldn't connect to the remote port. Maybe it hasn't been opened yet?? + _numConnectionAttempts += 1 + return + } + } + + _numConnectionAttempts = 0 + _retryConnectionTimer.suspend() + _retryingConnection = false + + _didConnect() + } +} + + + +class Subscriber: PubSub { + let identifier: String = UUID().uuidString + + + private lazy var _periodicRegistrationTimer: RepeatingTimer = { + let timer = RepeatingTimer(secondsInterval: 5) //, queue: _queue) + timer.eventHandler = _periodicRegistration + return timer + }() + + private var _publisherConnection: PortConnection + private var _portName: String + private var _port: CFMessagePort? + + fileprivate var _runLoopSource: CFRunLoopSource? + + var receivedNewFrame : ((Frame) -> Void)? = nil + var receivedNewFrameData : ((Data) -> Void)? = nil + + override init(publisherPortName: String) { + _portName = "\(publisherPortName).\(identifier)" + + // Connect to publisher and register so we can start receiving data + _publisherConnection = PortConnection(publisherPortName) + + super.init(publisherPortName: publisherPortName) + + // Create port for Publisher to call + let (createdPort, createdRunLoopSource) = createLocalPort(_portName) + + _port = createdPort + _runLoopSource = createdRunLoopSource + dlog("Started Subscriber at \(_portName)") + + _publisherConnection.onStatusChanged = self.onPublisherConnectionStatusChange + + } + + func teardown() { + stopLocalPort(port: _port, runLoopSource: _runLoopSource) + _port = nil + _runLoopSource = nil + } + + override func start() { + _publisherConnection.connect { [weak self] (conn, status) in + let response = conn.sendRequest(.Register, str: self?.identifier) + dlog("received publisher register response \(response)") + if response.isSuccess() { + + } + } + _periodicRegistrationTimer.resume() + + } + override func stop() { + _periodicRegistrationTimer.suspend() + // we don't want to get any more frames + + } + + deinit { + _periodicRegistrationTimer.suspend() + _publisherConnection.close() + stopLocalPort(port: _port, runLoopSource: _runLoopSource) + _port = nil + _runLoopSource = nil + } + + // can we make this run on a concucrent queue + fileprivate override func receivedRequest(_ data: Data?, _ callerID: CallerID) -> Data? { + //dlog("Subscriber receivedRequest \(callerID) \(data) \(Thread.current) \(Thread.isMainThread)") + + switch callerID { + case .ReceiveFrame: + guard let frame_data = data else { + return nil + } + + if receivedNewFrameData != nil { + receivedNewFrameData?(frame_data) + + return Response.Code.OK.data() + + } + return Response.Code.NotAvailable.data() + default: + break + } + return nil + } + + private func onPublisherConnectionStatusChange(connection: PortConnection, status: PortConnectionStatus) { + dlog("onPublisherConnectionStatusChange connection \(connection) \(status)") + if status == .Connected { + dlog("connected to publisher \(connection) \(status)") + } + + } + + private func _periodicRegistration() { + let response = _publisherConnection.sendRequest(.Register, str: self.identifier) + if response.isSuccess() { + + } + else { + dlog("not connected to publisher") + _publisherConnection.close() + _publisherConnection = PortConnection(self._publisherPortName) + _publisherConnection.onStatusChanged = self.onPublisherConnectionStatusChange + _publisherConnection.connect() + + } + } + +} + +// Handles connection management and setting up local connections +class PubSub { + fileprivate var _publisherPortName: String + private var _publisherPort: CFMessagePort? + private var _publisherRunLoopSource: CFRunLoopSource? + + internal let _runLoopMode: CFRunLoopMode = CFRunLoopMode.defaultMode //CFRunLoopMode.commonModes + + private var _connections: Set = [] + + fileprivate var _queue: DispatchQueue = DispatchQueue(label: "PubSub", qos: .userInteractive) + + public init(publisherPortName: String) { + dlog("PubSub INIT \(publisherPortName)") + _publisherPortName = publisherPortName + } + + func publisherName() -> String { + return _publisherPortName + } + + + func getSubscriberMetrics() -> Dictionary { + var metrics: [String: Any] = ["NumSubscribers": _connections.count] + + var subscribers: [[String:Any?]] = [] + for conn in _connections { + let subscriber: [String : Any?] = ["LastResponseTime":conn.lastResponseTime, "ID": conn.portName(), "Connected": conn.isConnected()] + subscribers.append(subscriber) + } + metrics["Subscribers"] = subscribers + return metrics + } + + func isRunning() -> Bool { + if _publisherPort != nil && _publisherRunLoopSource != nil { + return true + } + return false + } + + func start() { + if _publisherPort != nil { + return + } + // Create port for Publisher to call + let (createdPort, createdRunLoopSource) = createLocalPort(_publisherPortName) + + _publisherPort = createdPort + _publisherRunLoopSource = createdRunLoopSource + + } + func stop() { + stopLocalPort(port: _publisherPort, runLoopSource: _publisherRunLoopSource) + _publisherPort = nil + _publisherRunLoopSource = nil + } + + + func publishFrameData(_ frameData: Data) { + for conn in _connections { + let response = conn.sendRequest(.ReceiveFrame, data: frameData) + if response.isSuccess() { + //print("response \(response)") + } + } + } + + private func onConnectionStatusChange(connection: PortConnection, status: PortConnectionStatus) { + dlog("onConnectionStatusChange connection \(connection) \(status)") + if status == .Connected { +// dlog("sending ping to \(connection)") +// let response = connection.sendRequest(.Ping) +// print("ping response \(response)") + } + } + + private func addConnectionByIdentifier(_ identifier: String) -> Bool { + let clientName = "\(publisherName()).\(identifier)" + let conn = PortConnection(clientName) + if !_connections.contains(conn) { + _connections.insert(conn) + conn.onStatusChanged = self.onConnectionStatusChange + conn.connect() + return true + } + return false + } + + fileprivate func receivedRequest(_ data: Data?, _ callerID: CallerID) -> Data? { + + switch callerID { + case .Register: + guard let id_data = data else { + return nil + } + let identifier = String(data: id_data, encoding: .utf8)! + + dlog("Recieved \(callerID) request from client \(identifier)") + + if addConnectionByIdentifier(identifier) { // we added the connection + return Response.Code.Success.data() + } + return Response.Code.OK.data() + + + default: + break + } + return nil + } + + fileprivate func createLocalPort(_ portName: String) -> (CFMessagePort?, CFRunLoopSource?) { + //return DispatchQueue.main.sync { + var context = CFMessagePortContext(version: 0, info: Unmanaged.passUnretained(self).toOpaque(), retain: nil, release: nil, copyDescription: nil) + if let port = CFMessagePortCreateLocal(nil, portName as CFString, ServerPortCallback, &context, nil) { + CFMessagePortSetInvalidationCallBack(port, PortInvalidationCallback) + + CFMessagePortSetDispatchQueue(port, _queue) + + return (port, nil) + } + return (nil, nil) + //} + + } + + fileprivate func stopLocalPort(port: CFMessagePort?, runLoopSource: CFRunLoopSource? ) { + if port != nil { + CFMessagePortInvalidate(port) + } + if runLoopSource != nil { + CFRunLoopRemoveSource(CFRunLoopGetCurrent(), runLoopSource, _runLoopMode) + } + } + +} diff --git a/Shared/ImagoStream.swift b/Shared/ImagoStream.swift new file mode 100644 index 0000000..fd8acad --- /dev/null +++ b/Shared/ImagoStream.swift @@ -0,0 +1,90 @@ +// +// ImagoStream.swift +// Imago +// +// Created by Nolan Brown on 7/20/20. +// Copyright © 2020 Nolan Brown. All rights reserved. +// + +import Foundation +import CoreImage + +class ImagoStream { + private var _subscriber: Subscriber? + private var _fps: Int = 30 + private var _startReceivingFrames: Bool = true + private var _isRegistered: Bool = false + private var _isTimerRunning: Bool = false + private var _queue: DispatchQueue = DispatchQueue(label: "com.Imago.ImagoStream") + private var _currentFrame: Frame? + private var _pubsub: PubSub = PubSub(publisherPortName:IMAGO_SERVER_NAME) + + private var _renderedFrames: [UInt64] = [] + private var _startTS: UInt64 = 0 + + private var _sequenceNumber: UInt64 = 0 + + var isRunning: Bool = false + + var receivedFrame : ((Frame) -> Void)? = nil + + init() { + print("ImagoStream INIT") + } + + func framesPerSecond() -> Int { + return _fps + } + + func start() { + if !isRunning { + _startReceivingFrames = true + isRunning = true + if _subscriber == nil { + _subscriber = Subscriber(publisherPortName: IMAGO_SERVER_NAME) + _subscriber!.start() + _subscriber!.receivedNewFrameData = setFrameData + } + } + _subscriber!.start() + _subscriber!.receivedNewFrameData = setFrameData + + } + + func stop() { + _subscriber!.stop() + _startReceivingFrames = false + isRunning = false + + } + + + func setFrameData(_ frameData: Data) { + + guard let frame: Frame = Frame.deserialize(fromData:frameData) else { + return + } + + if _currentFrame != nil && _currentFrame?.id == frame.id { + return + } + if _currentFrame == nil { // first frame + _startTS = mach_absolute_time() + } + + var newFrame = frame + if receivedFrame != nil { + + receivedFrame!(newFrame) + + if _currentFrame?.timestamp != nil { + let _ = newFrame.elapsedSeconds(_currentFrame!.timestamp) + } + } + _currentFrame = newFrame + + _sequenceNumber += 1 + + } + +} diff --git a/Shared/RepeatingTimer.swift b/Shared/RepeatingTimer.swift new file mode 100644 index 0000000..aafd8cf --- /dev/null +++ b/Shared/RepeatingTimer.swift @@ -0,0 +1,76 @@ +// +// RepeatingTimer.swift +// Imago +// +// Created by Nolan Brown on 7/27/20. +// Copyright © 2020 Nolan Brown. All rights reserved. +// + +/* + Code by danielgalasko + https://medium.com/over-engineering/a-background-repeating-timer-in-swift-412cecfd2ef9 + https://gist.github.com/danielgalasko/1da90276f23ea24cb3467c33d2c05768#file-repeatingtimer-swift + */ + +import Foundation + +class RepeatingTimer { + + let interval: DispatchTimeInterval + let queue: DispatchQueue? + + init(secondsInterval: Int, queue: DispatchQueue? = nil) { + self.queue = queue + self.interval = DispatchTimeInterval.seconds(secondsInterval) + } + + init(timeInterval: DispatchTimeInterval, queue: DispatchQueue? = nil) { + self.queue = queue + self.interval = timeInterval + } + + private lazy var timer: DispatchSourceTimer = { + let t = DispatchSource.makeTimerSource()//(queue: self.queue) + t.schedule(deadline: .now() + self.interval, repeating: self.interval) + t.setEventHandler(handler: { [weak self] in + self?.eventHandler?() + }) + return t + }() + + var eventHandler: (() -> Void)? + + private enum State { + case suspended + case resumed + } + + private var state: State = .suspended + + deinit { + timer.setEventHandler {} + timer.cancel() + /* + If the timer is suspended, calling cancel without resuming + triggers a crash. This is documented here https://forums.developer.apple.com/thread/15902 + */ + resume() + eventHandler = nil + } + + func resume() { + if state == .resumed { + return + } + state = .resumed + timer.resume() + } + + func suspend() { + if state == .suspended { + return + } + state = .suspended + timer.suspend() + } +} diff --git a/setup.sh b/setup.sh new file mode 100755 index 0000000..12ba489 --- /dev/null +++ b/setup.sh @@ -0,0 +1,165 @@ +#!/bin/bash + +PROJECTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" + +TURBOJPEG_LIB_NAME=libturbojpeg.0.dylib +TURBOJPEG_LIB_PATH=/opt/libjpeg-turbo/lib/$TURBOJPEG_LIB_NAME + +TURBOJPEG_RELEASE_URL=https://sourceforge.net/projects/libjpeg-turbo/files/ + +FRAMEWORKS_DIR=Frameworks + + +EDSDK_ZIP_URL=https://downloads.canon.com/sdk/EDSDK_v131231_Macintosh.zip + +if [ ! -d "Imago.xcodeproj" ]; then + echo "This script must be executed in the base directory of the Imago project" + exit 1 +fi + +installTurboJpeg() { + if [ -f "$TURBOJPEG_LIB_PATH" ]; then + return 0 + else + if isBrewInstalled; then + while true; do + read -p "Do you wish to install TurboJPEG?" yn + case $yn in + [Yy]* ) brew install jpeg-turbo; return installTurboJpeg;; + [Nn]* ) askUserToInstallTurboJpeg ;; + * ) echo "Please answer yes or no.";; + esac + done + else + askUserToInstallTurboJpeg + fi + fi +} + +askUserToInstallTurboJpeg() { + echo "TurboJPEG not found. Please install and re-run this script" + open $TURBOJPEG_RELEASE_URL + exit +} + +isBrewInstalled() { + which -s brew + if [[ $? != 0 ]] ; then + return 0 + else + return 1 + fi +} + +isTurboJpegSetup() { + if [ -f "$FRAMEWORKS_DIR/$TURBOJPEG_LIB_NAME" ]; then + return 0 + else + return 1 + fi +} + +setupTurboJpegAsFramework() { + if installTurboJpeg; then + local newLibPath=$FRAMEWORKS_DIR/$TURBOJPEG_LIB_NAME + + if [ -f "$TURBOJPEG_LIB_PATH" ]; then + echo "Copying $TURBOJPEG_LIB_NAME to $newLibPath" + cp $TURBOJPEG_LIB_PATH $newLibPath + + echo "Updating $TURBOJPEG_LIB_NAME location to look in app bundle" + install_name_tool -id @executable_path/../Frameworks/$TURBOJPEG_LIB_NAME $newLibPath + + echo "Updating $TURBOJPEG_LIB_NAME use system libgcc" + install_name_tool -change /opt/local/lib/libgcc/libgcc_s.1.dylib /usr/lib/libgcc_s.1.dylib $newLibPath + + echo "Removing uncessary architectures from $TURBOJPEG_LIB_NAME" + lipo -thin x86_64 $newLibPath -o $newLibPath + fi + else + echo "TurboJPEG could not be installed." + fi + +} + +isEDSDKSetup() { + if [ -d "$PROJECTDIR/$FRAMEWORKS_DIR/EDSDK/EDSDK.framework" ]; then + return 0 + else + return 1 + fi +} + +setupEDSDKAsFramework() { + local tmpDir=`mktemp -d` + local zipName=$(basename $EDSDK_ZIP_URL) + + local diskImageName=Macintosh.dmg + local mountedImagePath=/Volumes/Macintosh + local edsdkDirectory="$mountedImagePath/EDSDK" + + local frameworkDirectory="$PROJECTDIR/$FRAMEWORKS_DIR/EDSDK" + + local frameworkName=EDSDK.framework + + + if [ ! -d "$frameworkDirectory/$frameworkName" ]; then + + cd $tmpDir + + echo "Downloading EDSDK.framework..." + curl -O $EDSDK_ZIP_URL &> /dev/null + + if [ ! -f $zipName ]; then + echo "Download of $EDSDK_ZIP_URL failed" + cd $PROJECTDIR + return 1 + fi + + echo "Unziping EDSDK" + unzip -a $zipName &> /dev/null + unzip -a "$diskImageName.zip" &> /dev/null + + if [ ! -f $diskImageName ]; then + echo "EDSDK disk image not found. Can not complete setup" + cd $PROJECTDIR + return 1 + fi + + echo "Attaching EDSDK disk image" + hdiutil attach $diskImageName &> /dev/null + + echo "Creating required directory structure" + mkdir -p $frameworkDirectory/Header + + echo "Copying $frameworkName and Headers" + cp -R $edsdkDirectory/Framework/$frameworkName $frameworkDirectory/$frameworkName + cp -R $edsdkDirectory/Header/* $frameworkDirectory/Header/ + + echo "Cleaning up..." + hdiutil detach $mountedImagePath &> /dev/null + + cd $PROJECTDIR + + echo "Done" + fi +} + + +if isTurboJpegSetup; then + echo "TurboJPEG is setup for Imago" +else + setupTurboJpegAsFramework +fi + + +if isEDSDKSetup; then + echo "EDSDK is setup for Imago" +else + setupEDSDKAsFramework +fi + + + + +