diff --git a/examples/.DS_Store b/examples/.DS_Store index 1b6343f..d785470 100644 Binary files a/examples/.DS_Store and b/examples/.DS_Store differ diff --git a/examples/capture/.DS_Store b/examples/capture/.DS_Store index 7f5e1d7..e64d5df 100644 Binary files a/examples/capture/.DS_Store and b/examples/capture/.DS_Store differ diff --git a/examples/capture/example-capture/.DS_Store b/examples/capture/example-capture/.DS_Store index c2956be..c97c0ac 100644 Binary files a/examples/capture/example-capture/.DS_Store and b/examples/capture/example-capture/.DS_Store differ diff --git a/examples/capture/example-capture/example-capture.xcodeproj/project.pbxproj b/examples/capture/example-capture/example-capture.xcodeproj/project.pbxproj index 164c1aa..22002ea 100644 --- a/examples/capture/example-capture/example-capture.xcodeproj/project.pbxproj +++ b/examples/capture/example-capture/example-capture.xcodeproj/project.pbxproj @@ -6,30 +6,22 @@ objectVersion = 50; objects = { -/* Begin PBXCopyFilesBuildPhase section */ - D8F9800B26FA41F6001C02B4 /* CopyFiles */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = /usr/share/man/man1/; - dstSubfolderSpec = 0; - files = ( - ); - runOnlyForDeploymentPostprocessing = 1; - }; -/* End PBXCopyFilesBuildPhase section */ +/* Begin PBXBuildFile section */ + D8C007EC26FAE33C00FFB741 /* capture in Resources */ = {isa = PBXBuildFile; fileRef = D8C007EB26FAE33C00FFB741 /* capture */; }; + D8C007EE26FAFFDC00FFB741 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8C007ED26FAFFDC00FFB741 /* main.swift */; }; +/* End PBXBuildFile section */ /* Begin PBXFileReference section */ - D8F9800D26FA41F6001C02B4 /* example-capture */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "example-capture"; sourceTree = BUILT_PRODUCTS_DIR; }; - D8F9801826FA4230001C02B4 /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; }; - D8F9801A26FA4238001C02B4 /* CoreMedia.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreMedia.framework; path = System/Library/Frameworks/CoreMedia.framework; sourceTree = SDKROOT; }; - D8F9801C26FA4244001C02B4 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - D8F9801E26FA42E2001C02B4 /* src */ = {isa = PBXFileReference; lastKnownFileType = folder; name = src; path = ../src; sourceTree = ""; }; - D8F9802326FA4607001C02B4 /* src */ = {isa = PBXFileReference; lastKnownFileType = folder; name = src; path = ../../src; sourceTree = ""; }; - D8F9802526FA462E001C02B4 /* Cargo.toml */ = {isa = PBXFileReference; lastKnownFileType = text; name = Cargo.toml; path = ../../Cargo.toml; sourceTree = ""; }; + D8C007C026FAE25E00FFB741 /* example-capture.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "example-capture.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + D8C007CA26FAE25F00FFB741 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + D8C007CB26FAE25F00FFB741 /* example_capture.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = example_capture.entitlements; sourceTree = ""; }; + D8C007E726FAE29D00FFB741 /* rustpack-capture.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = "rustpack-capture.xcodeproj"; path = "rustpack-capture/rustpack-capture.xcodeproj"; sourceTree = ""; }; + D8C007EB26FAE33C00FFB741 /* capture */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; name = capture; path = ../../target/debug/capture; sourceTree = ""; }; + D8C007ED26FAFFDC00FFB741 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ - D8F9800A26FA41F6001C02B4 /* Frameworks */ = { + D8C007BD26FAE25E00FFB741 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( @@ -39,54 +31,51 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - D8F9800426FA41F6001C02B4 = { + D8C007B726FAE25E00FFB741 = { isa = PBXGroup; children = ( - D8F9801E26FA42E2001C02B4 /* src */, - D8F9800F26FA41F6001C02B4 /* example-capture */, - D8F9800E26FA41F6001C02B4 /* Products */, - D8F9801726FA422F001C02B4 /* Frameworks */, + D8C007E726FAE29D00FFB741 /* rustpack-capture.xcodeproj */, + D8C007C226FAE25E00FFB741 /* example-capture */, + D8C007C126FAE25E00FFB741 /* Products */, ); sourceTree = ""; }; - D8F9800E26FA41F6001C02B4 /* Products */ = { + D8C007C126FAE25E00FFB741 /* Products */ = { isa = PBXGroup; children = ( - D8F9800D26FA41F6001C02B4 /* example-capture */, + D8C007C026FAE25E00FFB741 /* example-capture.app */, ); name = Products; sourceTree = ""; }; - D8F9800F26FA41F6001C02B4 /* example-capture */ = { + D8C007C226FAE25E00FFB741 /* example-capture */ = { isa = PBXGroup; children = ( - D8F9801C26FA4244001C02B4 /* Info.plist */, - D8F9802526FA462E001C02B4 /* Cargo.toml */, - D8F9802326FA4607001C02B4 /* src */, + D8C007EB26FAE33C00FFB741 /* capture */, + D8C007CA26FAE25F00FFB741 /* Info.plist */, + D8C007CB26FAE25F00FFB741 /* example_capture.entitlements */, + D8C007ED26FAFFDC00FFB741 /* main.swift */, ); path = "example-capture"; sourceTree = ""; }; - D8F9801726FA422F001C02B4 /* Frameworks */ = { + D8C007E826FAE29D00FFB741 /* Products */ = { isa = PBXGroup; children = ( - D8F9801A26FA4238001C02B4 /* CoreMedia.framework */, - D8F9801826FA4230001C02B4 /* AVFoundation.framework */, ); - name = Frameworks; + name = Products; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ - D8F9800C26FA41F6001C02B4 /* example-capture */ = { + D8C007BF26FAE25E00FFB741 /* example-capture */ = { isa = PBXNativeTarget; - buildConfigurationList = D8F9801426FA41F6001C02B4 /* Build configuration list for PBXNativeTarget "example-capture" */; + buildConfigurationList = D8C007CE26FAE25F00FFB741 /* Build configuration list for PBXNativeTarget "example-capture" */; buildPhases = ( - D8F9802126FA438F001C02B4 /* Sources */, - D8F9802726FA465F001C02B4 /* Run Script */, - D8F9800A26FA41F6001C02B4 /* Frameworks */, - D8F9800B26FA41F6001C02B4 /* CopyFiles */, + D8C007BC26FAE25E00FFB741 /* Sources */, + D8C007BD26FAE25E00FFB741 /* Frameworks */, + D8C007BE26FAE25E00FFB741 /* Resources */, ); buildRules = ( ); @@ -94,24 +83,26 @@ ); name = "example-capture"; productName = "example-capture"; - productReference = D8F9800D26FA41F6001C02B4 /* example-capture */; - productType = "com.apple.product-type.tool"; + productReference = D8C007C026FAE25E00FFB741 /* example-capture.app */; + productType = "com.apple.product-type.application"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ - D8F9800526FA41F6001C02B4 /* Project object */ = { + D8C007B826FAE25E00FFB741 /* Project object */ = { isa = PBXProject; attributes = { + LastSwiftUpdateCheck = 1170; LastUpgradeCheck = 1170; ORGANIZATIONNAME = "l1npengtul-nokhwa"; TargetAttributes = { - D8F9800C26FA41F6001C02B4 = { + D8C007BF26FAE25E00FFB741 = { CreatedOnToolsVersion = 11.7; + LastSwiftMigration = 1170; }; }; }; - buildConfigurationList = D8F9800826FA41F6001C02B4 /* Build configuration list for PBXProject "example-capture" */; + buildConfigurationList = D8C007BB26FAE25E00FFB741 /* Build configuration list for PBXProject "example-capture" */; compatibilityVersion = "Xcode 9.3"; developmentRegion = en; hasScannedForEncodings = 0; @@ -119,50 +110,46 @@ en, Base, ); - mainGroup = D8F9800426FA41F6001C02B4; - productRefGroup = D8F9800E26FA41F6001C02B4 /* Products */; + mainGroup = D8C007B726FAE25E00FFB741; + productRefGroup = D8C007C126FAE25E00FFB741 /* Products */; projectDirPath = ""; + projectReferences = ( + { + ProductGroup = D8C007E826FAE29D00FFB741 /* Products */; + ProjectRef = D8C007E726FAE29D00FFB741 /* rustpack-capture.xcodeproj */; + }, + ); projectRoot = ""; targets = ( - D8F9800C26FA41F6001C02B4 /* example-capture */, + D8C007BF26FAE25E00FFB741 /* example-capture */, ); }; /* End PBXProject section */ -/* Begin PBXShellScriptBuildPhase section */ - D8F9802726FA465F001C02B4 /* Run Script */ = { - isa = PBXShellScriptBuildPhase; +/* Begin PBXResourcesBuildPhase section */ + D8C007BE26FAE25E00FFB741 /* Resources */ = { + isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - ); - name = "Run Script"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/capture", + D8C007EC26FAE33C00FFB741 /* capture in Resources */, ); runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "# Type a script or drag a script file from your workspace to insert its path.\n\ncargo +nightly build --features \"input-avfoundation\" --out-dir . -Z unstable-options\ncargo clean\n"; }; -/* End PBXShellScriptBuildPhase section */ +/* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ - D8F9802126FA438F001C02B4 /* Sources */ = { + D8C007BC26FAE25E00FFB741 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + D8C007EE26FAFFDC00FFB741 /* main.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin XCBuildConfiguration section */ - D8F9801226FA41F6001C02B4 /* Debug */ = { + D8C007CC26FAE25F00FFB741 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; @@ -217,10 +204,12 @@ MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; }; name = Debug; }; - D8F9801326FA41F6001C02B4 /* Release */ = { + D8C007CD26FAE25F00FFB741 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; @@ -268,49 +257,72 @@ MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; }; name = Release; }; - D8F9801526FA41F6001C02B4 /* Debug */ = { + D8C007CF26FAE25F00FFB741 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = "example-capture/example_capture.entitlements"; CODE_SIGN_STYLE = Automatic; - INFOPLIST_FILE = "$(SRCROOT)/example-capture/Info.plist"; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = "example-capture/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "l1npengtul-nokhwa.example-capture"; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; }; name = Debug; }; - D8F9801626FA41F6001C02B4 /* Release */ = { + D8C007D026FAE25F00FFB741 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = "example-capture/example_capture.entitlements"; CODE_SIGN_STYLE = Automatic; - INFOPLIST_FILE = "$(SRCROOT)/example-capture/Info.plist"; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = "example-capture/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "l1npengtul-nokhwa.example-capture"; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ - D8F9800826FA41F6001C02B4 /* Build configuration list for PBXProject "example-capture" */ = { + D8C007BB26FAE25E00FFB741 /* Build configuration list for PBXProject "example-capture" */ = { isa = XCConfigurationList; buildConfigurations = ( - D8F9801226FA41F6001C02B4 /* Debug */, - D8F9801326FA41F6001C02B4 /* Release */, + D8C007CC26FAE25F00FFB741 /* Debug */, + D8C007CD26FAE25F00FFB741 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - D8F9801426FA41F6001C02B4 /* Build configuration list for PBXNativeTarget "example-capture" */ = { + D8C007CE26FAE25F00FFB741 /* Build configuration list for PBXNativeTarget "example-capture" */ = { isa = XCConfigurationList; buildConfigurations = ( - D8F9801526FA41F6001C02B4 /* Debug */, - D8F9801626FA41F6001C02B4 /* Release */, + D8C007CF26FAE25F00FFB741 /* Debug */, + D8C007D026FAE25F00FFB741 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; - rootObject = D8F9800526FA41F6001C02B4 /* Project object */; + rootObject = D8C007B826FAE25E00FFB741 /* Project object */; } diff --git a/examples/capture/example-capture/example-capture.xcodeproj/project.xcworkspace/xcuserdata/pengg.xcuserdatad/UserInterfaceState.xcuserstate b/examples/capture/example-capture/example-capture.xcodeproj/project.xcworkspace/xcuserdata/pengg.xcuserdatad/UserInterfaceState.xcuserstate index 469319b..69396aa 100644 Binary files a/examples/capture/example-capture/example-capture.xcodeproj/project.xcworkspace/xcuserdata/pengg.xcuserdatad/UserInterfaceState.xcuserstate and b/examples/capture/example-capture/example-capture.xcodeproj/project.xcworkspace/xcuserdata/pengg.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/examples/capture/example-capture/example-capture.xcodeproj/xcshareddata/xcschemes/example-capture.xcscheme b/examples/capture/example-capture/example-capture.xcodeproj/xcshareddata/xcschemes/example-capture.xcscheme index f3050a3..acee89b 100644 --- a/examples/capture/example-capture/example-capture.xcodeproj/xcshareddata/xcschemes/example-capture.xcscheme +++ b/examples/capture/example-capture/example-capture.xcodeproj/xcshareddata/xcschemes/example-capture.xcscheme @@ -14,8 +14,8 @@ buildForAnalyzing = "YES"> @@ -44,12 +44,22 @@ runnableDebuggingMode = "0"> + + + + + + diff --git a/examples/capture/example-capture/example-capture.xcodeproj/xcuserdata/pengg.xcuserdatad/xcschemes/xcschememanagement.plist b/examples/capture/example-capture/example-capture.xcodeproj/xcuserdata/pengg.xcuserdatad/xcschemes/xcschememanagement.plist index a3fce54..95a5c0c 100644 --- a/examples/capture/example-capture/example-capture.xcodeproj/xcuserdata/pengg.xcuserdatad/xcschemes/xcschememanagement.plist +++ b/examples/capture/example-capture/example-capture.xcodeproj/xcuserdata/pengg.xcuserdatad/xcschemes/xcschememanagement.plist @@ -12,7 +12,7 @@ SuppressBuildableAutocreation - D8F9800C26FA41F6001C02B4 + D8C007BF26FAE25E00FFB741 primary diff --git a/examples/capture/example-capture/example-capture/.DS_Store b/examples/capture/example-capture/example-capture/.DS_Store index d8f8767..49a553e 100644 Binary files a/examples/capture/example-capture/example-capture/.DS_Store and b/examples/capture/example-capture/example-capture/.DS_Store differ diff --git a/examples/capture/example-capture/example-capture/Info.plist b/examples/capture/example-capture/example-capture/Info.plist index 21eef70..132275d 100644 --- a/examples/capture/example-capture/example-capture/Info.plist +++ b/examples/capture/example-capture/example-capture/Info.plist @@ -6,8 +6,6 @@ $(DEVELOPMENT_LANGUAGE) CFBundleExecutable $(EXECUTABLE_NAME) - CFBundleIconFile - CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion @@ -20,17 +18,23 @@ 1.0 CFBundleVersion 1 + LSApplicationCategoryType + public.app-category.developer-tools LSMinimumSystemVersion $(MACOSX_DEPLOYMENT_TARGET) + NSCameraUsageDescription + we *notices buldge* wiww s-s-steaw aww youw webcam and pewsonyaw infowmation undew the x3 guise of b-being a &quot;nyokhwa exampwe c-captuwe app&quot; and a &quot;devewopew toow&quot; &lt;3 :3 *nyuzzwes yuwu* + NSMicrophoneUsageDescription + steawing yuwu dawta uwu owo NSHumanReadableCopyright Copywight © :3 2021 w1npengtuw, the x3 Nyokhwa Contwibutews. Wicensed undew the x3 MPL-2.0. *looks at you* You shouwd have weceived a copy with this softwawe, but if nyot i-it c-can be found at You c-can obtain onye at https://mozilla.org/MPL/2.0/. *huggles tightly* - NSCameraUsageDescription - we *notices buldge* wiww s-s-steaw aww youw webcam and pewsonyaw infowmation undew the x3 guise of b-being a "nyokhwa exampwe c-captuwe app" and a "devewopew toow" <3 :3 *nyuzzwes yuwu* NSPrincipalClass NSApplication NSSupportsAutomaticTermination NSSupportsSuddenTermination + RustBinName + capture diff --git a/examples/capture/example-capture/example-capture/example_capture.entitlements b/examples/capture/example-capture/example-capture/example_capture.entitlements new file mode 100644 index 0000000..1c2384a --- /dev/null +++ b/examples/capture/example-capture/example-capture/example_capture.entitlements @@ -0,0 +1,16 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.device.audio-input + + com.apple.security.device.camera + + com.apple.security.device.usb + + com.apple.security.files.user-selected.read-only + + + diff --git a/examples/capture/example-capture/example-capture/main.c b/examples/capture/example-capture/example-capture/main.c deleted file mode 100644 index ed7b5f1..0000000 --- a/examples/capture/example-capture/example-capture/main.c +++ /dev/null @@ -1,15 +0,0 @@ -// -// main.c -// example-capture -// -// Created by pengg on 2021/09/22. -// Copyright © 2021 l1npengtul-nokhwa. All rights reserved. -// - -#include - -int main(int argc, const char * argv[]) { - // insert code here... - printf("Hello, World!\n"); - return 0; -} diff --git a/examples/capture/example-capture/example-capture/main.swift b/examples/capture/example-capture/example-capture/main.swift new file mode 100644 index 0000000..21ce866 --- /dev/null +++ b/examples/capture/example-capture/example-capture/main.swift @@ -0,0 +1,50 @@ +// +// main.swift +// example-capture +// +// Created by pengg on 2021/09/22. +// Copyright © 2021 l1npengtul-nokhwa. All rights reserved. +// + +import Cocoa + +func log(_ data: Data) { + if let message = NSString(data: data, encoding: String.Encoding.utf8.rawValue) { + print(message) + } +} + + +let task = Process() +let bundle = Bundle.main +let rustBinName = bundle.infoDictionary?["RustBinName"] as! String +task.launchPath = bundle.path(forResource: rustBinName, ofType: nil) +task.environment = ["RUST_BACKTRACE": "1"] + +let arguments = CommandLine.arguments +var passed_arguments = "" +for argument in arguments { + if !(argument.starts(with: "/") || argument.starts(with: "-NS")) && argument != "YES" && argument != "NO" { + passed_arguments += " " + passed_arguments += argument + } +} + +print(passed_arguments) + +let stdOut = Pipe() +let stdErr = Pipe() + +stdOut.fileHandleForReading.readabilityHandler = { log($0.availableData) } +stdErr.fileHandleForReading.readabilityHandler = { log($0.availableData) } + +task.standardOutput = stdOut +task.standardError = stdErr + +task.terminationHandler = { task in + (task.standardOutput as AnyObject?)?.fileHandleForReading.readabilityHandler = nil + (task.standardError as AnyObject?)?.fileHandleForReading.readabilityHandler = nil +} + +task.launch() +task.waitUntilExit() diff --git a/examples/capture/example-capture/rustpack-capture/rustpack-capture.xcodeproj/project.pbxproj b/examples/capture/example-capture/rustpack-capture/rustpack-capture.xcodeproj/project.pbxproj new file mode 100644 index 0000000..58890fc --- /dev/null +++ b/examples/capture/example-capture/rustpack-capture/rustpack-capture.xcodeproj/project.pbxproj @@ -0,0 +1,218 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 50; + objects = { + +/* Begin PBXGroup section */ + D8C007DC26FAE29D00FFB741 = { + isa = PBXGroup; + children = ( + ); + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXLegacyTarget section */ + D8C007E126FAE29D00FFB741 /* rustpack-capture */ = { + isa = PBXLegacyTarget; + buildArgumentsString = "$(ACTION)"; + buildConfigurationList = D8C007E426FAE29D00FFB741 /* Build configuration list for PBXLegacyTarget "rustpack-capture" */; + buildPhases = ( + ); + buildToolPath = /usr/bin/make; + dependencies = ( + ); + name = "rustpack-capture"; + passBuildSettingsInEnvironment = 1; + productName = "rustpack-capture"; + }; +/* End PBXLegacyTarget section */ + +/* Begin PBXProject section */ + D8C007DD26FAE29D00FFB741 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 1170; + ORGANIZATIONNAME = "l1npengtul-nokhwa"; + TargetAttributes = { + D8C007E126FAE29D00FFB741 = { + CreatedOnToolsVersion = 11.7; + }; + }; + }; + buildConfigurationList = D8C007E026FAE29D00FFB741 /* Build configuration list for PBXProject "rustpack-capture" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = D8C007DC26FAE29D00FFB741; + projectDirPath = ""; + projectRoot = ""; + targets = ( + D8C007E126FAE29D00FFB741 /* rustpack-capture */, + ); + }; +/* End PBXProject section */ + +/* Begin XCBuildConfiguration section */ + D8C007E226FAE29D00FFB741 /* 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_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + }; + name = Debug; + }; + D8C007E326FAE29D00FFB741 /* 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; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + }; + name = Release; + }; + D8C007E526FAE29D00FFB741 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + DEBUGGING_SYMBOLS = YES; + DEBUG_INFORMATION_FORMAT = dwarf; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + OTHER_CFLAGS = ""; + OTHER_LDFLAGS = ""; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + D8C007E626FAE29D00FFB741 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + OTHER_CFLAGS = ""; + OTHER_LDFLAGS = ""; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + D8C007E026FAE29D00FFB741 /* Build configuration list for PBXProject "rustpack-capture" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D8C007E226FAE29D00FFB741 /* Debug */, + D8C007E326FAE29D00FFB741 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + D8C007E426FAE29D00FFB741 /* Build configuration list for PBXLegacyTarget "rustpack-capture" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D8C007E526FAE29D00FFB741 /* Debug */, + D8C007E626FAE29D00FFB741 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = D8C007DD26FAE29D00FFB741 /* Project object */; +} diff --git a/examples/capture/example-capture/rustpack-capture/rustpack-capture.xcodeproj/xcuserdata/pengg.xcuserdatad/xcschemes/xcschememanagement.plist b/examples/capture/example-capture/rustpack-capture/rustpack-capture.xcodeproj/xcuserdata/pengg.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..cc4c734 --- /dev/null +++ b/examples/capture/example-capture/rustpack-capture/rustpack-capture.xcodeproj/xcuserdata/pengg.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,14 @@ + + + + + SchemeUserState + + rustpack-capture.xcscheme_^#shared#^_ + + orderHint + 1 + + + + diff --git a/examples/capture/src/main.rs b/examples/capture/src/main.rs index 82bf053..56c3cb8 100644 --- a/examples/capture/src/main.rs +++ b/examples/capture/src/main.rs @@ -107,6 +107,8 @@ fn main() { use_backend = CaptureAPIBackend::AVFoundation; } + println!("www"); + match query_devices(use_backend) { Ok(devs) => { for (idx, camera) in devs.iter().enumerate() { @@ -119,8 +121,6 @@ fn main() { } } - println!("a"); - if matches.is_present("capture") { let backend_value = { match matches.value_of("capture-backend").unwrap() { @@ -196,8 +196,11 @@ fn main() { } } + println!("www"); + // open stream camera.open_stream().unwrap(); + println!("wwww"); loop { if let Ok(frame) = camera.frame() { println!( diff --git a/nokhwa-bindings-macos/Cargo.toml b/nokhwa-bindings-macos/Cargo.toml index cdaaef0..9554193 100644 --- a/nokhwa-bindings-macos/Cargo.toml +++ b/nokhwa-bindings-macos/Cargo.toml @@ -14,3 +14,5 @@ core-media-sys = "0.1.2" cocoa-foundation = "0.1.0" objc = { version = "0.2.7", features = ["exception"] } block = "0.1.6" +flume = "0.10.9" +dashmap = "4.0.2" \ No newline at end of file diff --git a/nokhwa-bindings-macos/src/lib.rs b/nokhwa-bindings-macos/src/lib.rs index b41b761..cc19802 100644 --- a/nokhwa-bindings-macos/src/lib.rs +++ b/nokhwa-bindings-macos/src/lib.rs @@ -54,24 +54,29 @@ pub enum AVFError { any(target_os = "macos", target_os = "ios"), link(name = "AVFoundation", kind = "framework") )] +#[allow(non_snake_case)] pub mod core_media { + // all of this is stolen from bindgen + // steal it idc use core_media_sys::{ CMBlockBufferRef, CMFormatDescriptionRef, CMSampleBufferRef, CMTime, CMVideoDimensions, + FourCharCode, }; - use objc::runtime::Object; + use objc::{runtime::Object, Message}; + use std::ops::Deref; - pub type id = *mut Object; + pub type Id = *mut Object; #[repr(transparent)] #[derive(Clone)] - pub struct NSObject(pub id); - impl std::ops::Deref for NSObject { + pub struct NSObject(pub Id); + impl Deref for NSObject { type Target = objc::runtime::Object; fn deref(&self) -> &Self::Target { unsafe { &*self.0 } } } - unsafe impl objc::Message for NSObject {} + unsafe impl Message for NSObject {} impl NSObject { pub fn alloc() -> Self { Self(unsafe { msg_send!(objc::class!(NSObject), alloc) }) @@ -80,14 +85,14 @@ pub mod core_media { #[repr(transparent)] #[derive(Clone)] - pub struct NSString(pub id); - impl std::ops::Deref for NSString { + pub struct NSString(pub Id); + impl Deref for NSString { type Target = objc::runtime::Object; fn deref(&self) -> &Self::Target { unsafe { &*self.0 } } } - unsafe impl objc::Message for NSString {} + unsafe impl Message for NSString {} impl NSString { pub fn alloc() -> Self { Self(unsafe { msg_send!(objc::class!(NSString), alloc) }) @@ -95,34 +100,17 @@ pub mod core_media { } pub type AVMediaType = NSString; + extern "C" { pub static AVMediaTypeVideo: AVMediaType; - } - extern "C" { pub static AVMediaTypeAudio: AVMediaType; - } - extern "C" { pub static AVMediaTypeText: AVMediaType; - } - extern "C" { pub static AVMediaTypeClosedCaption: AVMediaType; - } - extern "C" { pub static AVMediaTypeSubtitle: AVMediaType; - } - extern "C" { pub static AVMediaTypeTimecode: AVMediaType; - } - extern "C" { pub static AVMediaTypeMetadata: AVMediaType; - } - extern "C" { pub static AVMediaTypeMuxed: AVMediaType; - } - extern "C" { pub static AVMediaTypeMetadataObject: AVMediaType; - } - extern "C" { pub static AVMediaTypeDepthData: AVMediaType; } @@ -146,49 +134,116 @@ pub mod core_media { pub fn CMSampleBufferGetDataBuffer(sbuf: CMSampleBufferRef) -> CMBlockBufferRef; } - pub type dispatch_queue_t = NSObject; - extern "C" { pub fn dispatch_queue_create( label: *const ::std::os::raw::c_char, attr: NSObject, - ) -> dispatch_queue_t; + ) -> NSObject; } extern "C" { pub fn dispatch_release(object: NSObject); } + + #[repr(C)] + #[derive(Debug, Copy, Clone)] + pub struct __CVBuffer { + _unused: [u8; 0], + } + pub type CVBufferRef = *mut __CVBuffer; + + #[allow(non_snake_case)] + extern "C" { + pub fn CMSampleBufferGetImageBuffer(sbuf: CMSampleBufferRef) -> CVImageBufferRef; + } + + pub type CVImageBufferRef = CVBufferRef; + pub type CVPixelBufferRef = CVImageBufferRef; + pub type CVPixelBufferLockFlags = u64; + pub type CVReturn = i32; + + #[allow(non_snake_case)] + extern "C" { + pub fn CVPixelBufferLockBaseAddress( + pixelBuffer: CVPixelBufferRef, + lockFlags: CVPixelBufferLockFlags, + ) -> CVReturn; + + pub fn CVPixelBufferUnlockBaseAddress( + pixelBuffer: CVPixelBufferRef, + unlockFlags: CVPixelBufferLockFlags, + ) -> CVReturn; + + pub fn CVPixelBufferGetDataSize(pixelBuffer: CVPixelBufferRef) -> std::os::raw::c_ulong; + + pub fn CVPixelBufferGetBaseAddress( + pixelBuffer: CVPixelBufferRef, + ) -> *mut ::std::os::raw::c_void; + } + + extern "C" { + pub static AVVideoCodecKey: NSString; + } + pub type OSType = FourCharCode; + pub type AVVideoCodecType = NSString; + extern "C" { + pub static AVVideoCodecTypeHEVC: AVVideoCodecType; + pub static AVVideoCodecTypeH264: AVVideoCodecType; + pub static AVVideoCodecTypeJPEG: AVVideoCodecType; + pub static AVVideoCodecTypeAppleProRes4444: AVVideoCodecType; + pub static AVVideoCodecTypeAppleProRes422: AVVideoCodecType; + pub static AVVideoCodecTypeAppleProRes422HQ: AVVideoCodecType; + pub static AVVideoCodecTypeAppleProRes422LT: AVVideoCodecType; + pub static AVVideoCodecTypeAppleProRes422Proxy: AVVideoCodecType; + pub static AVVideoCodecTypeHEVCWithAlpha: AVVideoCodecType; + pub static AVVideoCodecHEVC: NSString; + pub static AVVideoCodecH264: NSString; + pub static AVVideoCodecJPEG: NSString; + pub static AVVideoCodecAppleProRes4444: NSString; + pub static AVVideoCodecAppleProRes422: NSString; + pub static AVVideoWidthKey: NSString; + pub static AVVideoHeightKey: NSString; + pub static AVVideoExpectedSourceFrameRateKey: NSString; + pub fn CVPixelBufferGetPixelFormatType(pixelBuffer: CVPixelBufferRef) -> OSType; + + } } #[cfg(any(target_os = "macos", target_os = "ios"))] pub mod avfoundation { - use crate::core_media::{dispatch_queue_create, dispatch_release, AVMediaTypeVideo, NSObject}; + use crate::core_media::{ + AVMediaTypeAudio, AVMediaTypeClosedCaption, AVMediaTypeDepthData, AVMediaTypeMetadata, + AVMediaTypeMetadataObject, AVMediaTypeMuxed, AVMediaTypeSubtitle, AVMediaTypeText, + AVMediaTypeTimecode, CVPixelBufferGetPixelFormatType, + }; use crate::{ core_media::{ - CMBlockBufferCopyDataBytes, CMBlockBufferGetDataLength, CMSampleBufferGetDataBuffer, - CMTimeMake, CMVideoFormatDescriptionGetDimensions, + dispatch_queue_create, AVMediaTypeVideo, CMSampleBufferGetImageBuffer, + CMVideoFormatDescriptionGetDimensions, CVImageBufferRef, CVPixelBufferGetBaseAddress, + CVPixelBufferGetDataSize, CVPixelBufferLockBaseAddress, CVPixelBufferUnlockBaseAddress, + NSObject, }, AVFError, }; use block::ConcreteBlock; use cocoa_foundation::foundation::{NSArray, NSInteger, NSString, NSUInteger}; use core_media_sys::{ - kCMVideoCodecType_422YpCbCr8, kCMVideoCodecType_JPEG, CMBlockBufferRef, - CMFormatDescriptionGetMediaSubType, CMSampleBufferRef, CMTime, CMVideoDimensions, + kCMPixelFormat_422YpCbCr8_yuvs, kCMVideoCodecType_422YpCbCr8, kCMVideoCodecType_JPEG, + kCMVideoCodecType_JPEG_OpenDML, CMFormatDescriptionGetMediaSubType, CMSampleBufferRef, + CMVideoDimensions, }; + use dashmap::DashMap; + use flume::{Receiver, Sender}; use objc::{ declare::ClassDecl, runtime::{Class, Object, Protocol, Sel, BOOL, YES}, }; - use std::ffi::CString; - use std::os::raw::c_char; use std::{ - borrow::{Borrow, Cow}, + borrow::Cow, cmp::Ordering, convert::TryFrom, error::Error, - ffi::{c_void, CStr}, - os::raw::c_int, + ffi::{c_void, CStr, CString}, sync::{ atomic::{AtomicBool, Ordering as MemOrdering}, Arc, Mutex, TryLockError, @@ -306,82 +361,91 @@ pub mod avfoundation { Ok(out_vec) } + fn compare_ns_string(this: *mut Object, other: crate::core_media::NSString) -> bool { + unsafe { + let equal: BOOL = msg_send![this, isEqualToString: other]; + equal == YES + } + } + fn default_callback(_: bool) {} + pub type CompressionData<'a> = (Cow<'a, [u8]>, AVFourCC); + pub type DataPipe<'a> = (Sender>, Receiver>); + lazy_static! { static ref CAMERA_AUTHORIZED: Arc = Arc::new(AtomicBool::new(false)); static ref USER_CALLBACK_FN: Arc> = Arc::new(Mutex::new(default_callback)); + static ref PIPE_MAP: Arc>> = Arc::new(DashMap::new()); static ref CALLBACK_CLASS: &'static Class = { let mut decl = ClassDecl::new("MyCaptureCallback", class!(NSObject)).unwrap(); // frame stack - decl.add_ivar::<*mut c_void>("_frame_data"); - decl.add_ivar::("_frame_length"); + decl.add_ivar::("_index"); - extern "C" fn my_callback_get_data_ptr(this: &mut Object, _: Sel) -> *mut c_void { + extern "C" fn my_callback_get_index(this: &Object, _: Sel) -> usize { unsafe { - *this.get_ivar("_frame_data") + *this.get_ivar("_index") } } - extern "C" fn my_callback_set_data_ptr(this: &mut Object, _: Sel, ptr: *mut c_void) { - unsafe { - this.set_ivar("_frame_data", ptr); - } - } - - extern "C" fn my_callback_get_data_length(this: &Object, _: Sel) -> usize { + extern "C" fn my_callback_set_index(this: &mut Object, _: Sel, new_index: usize) { unsafe { - *this.get_ivar("_frame_length") - } - } - - extern "C" fn my_callback_set_data_length(this: &mut Object, _: Sel, new_length: usize) { - unsafe { - this.set_ivar("_frame_length", new_length); + this.set_ivar("_index", new_index); } } // Delegate compliance method + // SAFETY: This assumes that the buffer byte size is a u8. Any other size will cause unsafety. #[allow(non_snake_case)] + #[allow(non_upper_case_globals)] extern fn capture_out_callback(this: &mut Object, _: Sel, _: *mut Object, didOutputSampleBuffer: CMSampleBufferRef, _: *mut Object) { - println!("aaa"); - let data_buffer: CMBlockBufferRef = unsafe { CMSampleBufferGetDataBuffer(didOutputSampleBuffer) }; - let length = unsafe { CMBlockBufferGetDataLength(data_buffer) } as usize; - let ptr: *mut c_void = unsafe { msg_send![this, dataPointer] }; - let _: c_int = unsafe { CMBlockBufferCopyDataBytes(data_buffer, 0, length, ptr) }; - let _: () = unsafe { msg_send![this, setDataLength:length] }; + let image_buffer: CVImageBufferRef = unsafe { CMSampleBufferGetImageBuffer(didOutputSampleBuffer) }; + unsafe { CVPixelBufferLockBaseAddress(image_buffer, 0); }; + + let buffer_codec = unsafe { CVPixelBufferGetPixelFormatType(image_buffer) }; + + let fourcc = match buffer_codec { + kCMVideoCodecType_422YpCbCr8 | kCMPixelFormat_422YpCbCr8_yuvs => AVFourCC::YUV2, + kCMVideoCodecType_JPEG | kCMVideoCodecType_JPEG_OpenDML => AVFourCC::MJPEG, + _ => { + return; + } + }; + + let buffer_length = unsafe { CVPixelBufferGetDataSize(image_buffer) }; + let buffer_ptr = unsafe { CVPixelBufferGetBaseAddress(image_buffer) }; + let buffer_as_vec = unsafe { std::slice::from_raw_parts_mut(buffer_ptr as *mut u8, buffer_length as usize).to_vec() }; + + unsafe { CVPixelBufferUnlockBaseAddress(image_buffer, 0) }; + let index: usize = unsafe { msg_send![this, index] }; + let pipes = &PIPE_MAP.get(&index); + if let Some(pipe) = pipes { + let _ = pipe.value().0.send((Cow::from(buffer_as_vec), fourcc)); + } + } + + #[allow(non_snake_case)] + extern fn capture_drop_callback(_: &mut Object, _: Sel, _: *mut Object, _: *mut Object, _: *mut Object) { } unsafe { - decl.add_protocol(Protocol::get("AVCaptureVideoDataOutputSampleBufferDelegate").unwrap()); - decl.add_method( - sel!(dataPointer), my_callback_get_data_ptr as extern "C" fn(&mut Object, Sel) -> *mut c_void + sel!(index), my_callback_get_index as extern "C" fn(&Object, Sel) -> usize ); decl.add_method( - sel!(setDataPointer:), my_callback_set_data_ptr as extern "C" fn(&mut Object, Sel, *mut c_void) - ); - decl.add_method( - sel!(dataLength), my_callback_get_data_length as extern "C" fn(&Object, Sel) -> usize - ); - decl.add_method( - sel!(setDataLength:), my_callback_set_data_length as extern "C" fn(&mut Object, Sel, usize) + sel!(setIndex:), my_callback_set_index as extern "C" fn(&mut Object, Sel, usize) ); decl.add_method( sel!(captureOutput:didOutputSampleBuffer:fromConnection:), capture_out_callback as extern "C" fn(&mut Object, Sel, *mut Object, CMSampleBufferRef, *mut Object) ); + decl.add_method( + sel!(captureOutput:didDropSampleBuffer:fromConnection:), capture_drop_callback as extern "C" fn(&mut Object, Sel, *mut Object, *mut Object, *mut Object) + ); + + decl.add_protocol(Protocol::get("AVCaptureVideoDataOutputSampleBufferDelegate").unwrap()); } - decl.register() - }; - static ref OS_DISPATCH_CLASS: &'static Class = { - let mut decl = ClassDecl::new("MyDispatchQueueSerial", class!(NSObject)).unwrap(); - - // decl.add_protocol(Protocol::get("OS_dispatch_object").unwrap()); - // decl.add_protocol(Protocol::get("OS_dispatch_queue").unwrap()); - decl.add_protocol(Protocol::get("OS_dispatch_queue_serial").unwrap()); - decl.register() }; } @@ -526,16 +590,16 @@ pub mod avfoundation { impl From for *mut Object { fn from(media_type: AVMediaType) -> Self { match media_type { - AVMediaType::Audio => str_to_nsstr("AVMediaTypeAudio"), - AVMediaType::ClosedCaption => str_to_nsstr("AVMediaTypeClosedCaption"), - AVMediaType::DepthData => str_to_nsstr("AVMediaTypeDepthData"), - AVMediaType::Metadata => str_to_nsstr("AVMediaTypeMetadata"), - AVMediaType::MetadataObject => str_to_nsstr("AVMediaTypeMetadataObject"), - AVMediaType::Muxed => str_to_nsstr("AVMediaTypeMuxed"), - AVMediaType::Subtitle => str_to_nsstr("AVMediaTypeSubtitle"), - AVMediaType::Text => str_to_nsstr("AVMediaTypeText"), - AVMediaType::Timecode => str_to_nsstr("AVMediaTypeTimecode"), - AVMediaType::Video => str_to_nsstr("AVMediaTypeVideo"), + AVMediaType::Audio => unsafe { AVMediaTypeAudio.0 }, + AVMediaType::ClosedCaption => unsafe { AVMediaTypeClosedCaption.0 }, + AVMediaType::DepthData => unsafe { AVMediaTypeDepthData.0 }, + AVMediaType::Metadata => unsafe { AVMediaTypeMetadata.0 }, + AVMediaType::MetadataObject => unsafe { AVMediaTypeMetadataObject.0 }, + AVMediaType::Muxed => unsafe { AVMediaTypeMuxed.0 }, + AVMediaType::Subtitle => unsafe { AVMediaTypeSubtitle.0 }, + AVMediaType::Text => unsafe { AVMediaTypeText.0 }, + AVMediaType::Timecode => unsafe { AVMediaTypeTimecode.0 }, + AVMediaType::Video => unsafe { AVMediaTypeVideo.0 }, } } } @@ -544,28 +608,34 @@ pub mod avfoundation { type Error = AVFError; fn try_from(value: *mut Object) -> Result { - let borrow_str = nsstr_to_str(value); - let value_str: &str = borrow_str.borrow(); - let v = match value_str { - "AVMediaTypeAudio" => Ok(AVMediaType::Audio), - "AVMediaTypeClosedCaption" => Ok(AVMediaType::ClosedCaption), - "AVMediaTypeDepthData" => Ok(AVMediaType::DepthData), - "AVMediaTypeMetadata" => Ok(AVMediaType::Metadata), - "AVMediaTypeMetadataObject" => Ok(AVMediaType::MetadataObject), - "AVMediaTypeMuxed" => Ok(AVMediaType::Muxed), - "AVMediaTypeSubtitle" => Ok(AVMediaType::Subtitle), - "AVMediaTypeText" => Ok(AVMediaType::Text), - "AVMediaTypeTimecode" => Ok(AVMediaType::Timecode), - "AVMediaTypeVideo" => Ok(AVMediaType::Video), - _ => { - return Err(AVFError::InvalidValue { - found: value_str.to_string(), + unsafe { + if compare_ns_string(value, (AVMediaTypeAudio).clone()) { + Ok(AVMediaType::Audio) + } else if compare_ns_string(value, (AVMediaTypeClosedCaption).clone()) { + Ok(AVMediaType::ClosedCaption) + } else if compare_ns_string(value, (AVMediaTypeDepthData).clone()) { + Ok(AVMediaType::DepthData) + } else if compare_ns_string(value, (AVMediaTypeMetadata).clone()) { + Ok(AVMediaType::Metadata) + } else if compare_ns_string(value, (AVMediaTypeMetadataObject).clone()) { + Ok(AVMediaType::MetadataObject) + } else if compare_ns_string(value, (AVMediaTypeMuxed).clone()) { + Ok(AVMediaType::Muxed) + } else if compare_ns_string(value, (AVMediaTypeSubtitle).clone()) { + Ok(AVMediaType::Subtitle) + } else if compare_ns_string(value, (AVMediaTypeText).clone()) { + Ok(AVMediaType::Text) + } else if compare_ns_string(value, (AVMediaTypeTimecode).clone()) { + Ok(AVMediaType::Timecode) + } else if compare_ns_string(value, (AVMediaTypeVideo).clone()) { + Ok(AVMediaType::Video) + } else { + let name = nsstr_to_str(value); + Err(AVFError::InvalidValue { + found: name.to_string(), }) } - }; - - let _: *mut std::ffi::c_void = unsafe { msg_send![value, autorelease] }; - v + } } } @@ -595,8 +665,8 @@ pub mod avfoundation { #[derive(Copy, Clone, Debug, Hash, Ord, PartialOrd, Eq, PartialEq)] #[repr(u32)] pub enum AVFourCC { - YUV2 = kCMVideoCodecType_JPEG, - MJPEG = kCMVideoCodecType_422YpCbCr8, + YUV2, + MJPEG, } // Localized Name @@ -636,36 +706,66 @@ pub mod avfoundation { } pub struct AVCaptureVideoCallback { + index: usize, delegate: *mut Object, - queue: *mut Object, } impl AVCaptureVideoCallback { - pub fn new() -> Self { - AVCaptureVideoCallback::default() + pub fn new(index: usize) -> Self { + let cls = &CALLBACK_CLASS as &Class; + let delegate: *mut Object = unsafe { msg_send![cls, alloc] }; + let delegate: *mut Object = unsafe { msg_send![delegate, init] }; + + let data_pipe: DataPipe = flume::unbounded(); + let _ = &PIPE_MAP.insert(index, data_pipe); + + AVCaptureVideoCallback { index, delegate } + } + + pub fn index(&self) -> usize { + self.index } pub fn data_len(&self) -> usize { unsafe { msg_send![self.delegate, dataLength] } } - pub fn frame_to_slice<'a>(&self) -> Result, AVFError> { - let data_ptr: *mut c_void = unsafe { msg_send![self.delegate, dataPointer] }; - println!("tp1"); - let data_ptr = data_ptr as *mut u8 as *const u8; - let data_length = self.data_len(); - println!("tp2"); - if data_ptr.is_null() { - return Err(AVFError::ReadFrame("Nullptr".to_string())); - } - println!("tp3"); - if data_length == 0 { - return Err(AVFError::ReadFrame("Zero Size Len".to_string())); - } - println!("tp4"); - let cow_slice = Cow::from(unsafe { std::slice::from_raw_parts(data_ptr, data_length) }); - println!("tp5"); - Ok(cow_slice) + pub fn frame_to_slice<'a>(&self) -> Result, AVFError> { + let pipe_map = &PIPE_MAP.get(&self.index); // why rust + let pipe_recv = match pipe_map { + Some(pipe) => &pipe.value().1, + None => return Err(AVFError::ReadFrame("Data Pipe None".to_string())), + }; + let data = match pipe_recv.drain().last() { + Some(frame) => frame, + None => match pipe_recv.recv() { + Ok(f) => f, + Err(why) => { + return Err(AVFError::ReadFrame(format!( + "Failed to read frame from pipe: {}", + why.to_string() + ))) + } + }, + }; + Ok(data) + } + + pub fn frame_to_slice_no_block<'a>(&self) -> Result, AVFError> { + let pipe_map = &PIPE_MAP.get(&self.index); // why rust + let pipe_recv = match pipe_map { + Some(pipe) => &pipe.value().1, + None => return Err(AVFError::ReadFrame("Data Pipe None".to_string())), + }; + let data = match pipe_recv.drain().last() { + Some(frame) => frame, + None => { + return Err(AVFError::ReadFrame( + "Failed to read frame from pipe: None".to_string(), + )) + } + }; + Ok(data) } pub fn inner(&self) -> *mut Object { @@ -673,29 +773,6 @@ pub mod avfoundation { } } - impl Default for AVCaptureVideoCallback { - fn default() -> Self { - println!("b"); - let cls = &CALLBACK_CLASS as &Class; - let delegate: *mut Object = unsafe { msg_send![cls, alloc] }; - let delegate: *mut Object = unsafe { msg_send![delegate, init] }; - println!("c"); - let ptr: *mut c_void = std::ptr::null_mut(); - let len = 0_usize; - - let queue_cls = &OS_DISPATCH_CLASS as &Class; - let queue: *mut Object = unsafe { msg_send![queue_cls, new] }; - - unsafe { - let _: () = msg_send![delegate, setDataPointer: ptr]; - let _: () = msg_send![delegate, setDataLength: len]; - } - println!("c"); - - AVCaptureVideoCallback { delegate, queue } - } - } - impl Drop for AVCaptureVideoCallback { fn drop(&mut self) { unsafe { @@ -727,29 +804,20 @@ pub mod avfoundation { #[derive(Copy, Clone, Debug)] pub struct CaptureDeviceFormatDescriptor { pub resolution: AVVideoResolution, - pub fps: f64, + pub fps: u32, pub fourcc: AVFourCC, } impl CaptureDeviceFormatDescriptor { pub fn compatible_with_capture_format(&self, other: &AVCaptureDeviceFormat) -> bool { - let lower_fps = match other.fps_list.get(0) { - Some(fps) => fps, - None => return false, - }; - - let higher_fps = match other.fps_list.get(1) { - Some(fps) => fps, - None => return false, - }; - - if self.resolution.height == other.resolution.height - && self.resolution.width == other.resolution.width - && self.fourcc == other.fourcc - && ((self.fps - *lower_fps).abs() < f64::EPSILON - || (self.fps - *higher_fps).abs() < f64::EPSILON) - { - return true; + for fps in &other.fps_list { + if self.resolution.height == other.resolution.height + && self.resolution.width == other.resolution.width + && self.fourcc == other.fourcc + && (*fps as u32) == self.fps + { + return true; + } } false } @@ -791,8 +859,8 @@ pub mod avfoundation { unsafe { CMFormatDescriptionGetMediaSubType(description_obj as *mut c_void) }; #[allow(non_upper_case_globals)] let fourcc = match fcc_raw { - kCMVideoCodecType_422YpCbCr8 => AVFourCC::YUV2, - kCMVideoCodecType_JPEG => AVFourCC::MJPEG, + kCMVideoCodecType_422YpCbCr8 | kCMPixelFormat_422YpCbCr8_yuvs => AVFourCC::YUV2, + kCMVideoCodecType_JPEG | kCMVideoCodecType_JPEG_OpenDML => AVFourCC::MJPEG, _ => { return Err(AVFError::InvalidValue { found: fcc_raw.to_string(), @@ -851,7 +919,7 @@ pub mod avfoundation { pub fn devices(&self) -> Vec { let device_ns_array: *mut Object = unsafe { msg_send![self.inner, devices] }; - let objects_len: NSUInteger = unsafe { device_ns_array.count() }; + let objects_len: NSUInteger = unsafe { NSArray::count(device_ns_array) }; let mut devices = vec![AVCaptureDeviceDescriptor::default(); objects_len as usize]; for index in 0..objects_len { let device = unsafe { device_ns_array.objectAtIndex(index) }; @@ -958,14 +1026,7 @@ pub mod avfoundation { unsafe { msg_send![self.inner, unlockForConfiguration] } } - pub fn set_frame_rate(&mut self, fps: u32) { - let mut time = unsafe { CMTimeMake(1, fps as i32) }; - let time_ptr: *mut CMTime = &mut time; - unsafe { - let _: () = msg_send![self.inner, activeVideoMinFrameDuration: time_ptr]; - let _: () = msg_send![self.inner, activeVideoMaxFrameDuration: time_ptr]; - } - } + pub fn set_frame_rate(&mut self, _: u32) {} pub fn set_all( &mut self, @@ -1031,24 +1092,30 @@ pub mod avfoundation { pub fn add_delegate(&self, delegate: &AVCaptureVideoCallback) -> Result<(), AVFError> { unsafe { + let avf_queue_str = match CString::new("avf_queue") { + Ok(avf) => avf.into_raw(), + Err(_) => { + // should not happen + return Err(AVFError::StreamOpen("String contains null? This is a bug, please report it https://github.com/l1npengtul/nokhwa".to_string())); + } + }; + let queue = dispatch_queue_create(avf_queue_str, NSObject(std::ptr::null_mut())); + let _: () = msg_send![ self.inner, setSampleBufferDelegate: delegate.delegate - queue: delegate.queue + queue: queue ]; - } - println!("e"); + }; Ok(()) } } impl Default for AVCaptureVideoDataOutput { fn default() -> Self { - println!("d"); let cls = class!(AVCaptureVideoDataOutput); - let inner: *mut Object = unsafe { msg_send![cls, alloc] }; - let inner: *mut Object = unsafe { msg_send![inner, init] }; - println!("d"); + let inner: *mut Object = unsafe { msg_send![cls, new] }; + AVCaptureVideoDataOutput { inner } } } @@ -1064,8 +1131,15 @@ pub mod avfoundation { AVCaptureSession::default() } + pub fn begin_configuration(&self) { + unsafe { msg_send![self.inner, beginConfiguration] } + } + + pub fn commit_configuration(&self) { + unsafe { msg_send![self.inner, commitConfiguration] } + } + pub fn can_add_input(&self, input: &AVCaptureDeviceInput) -> bool { - println!("{:?}", unsafe { self.inner.as_ref() }.unwrap().class()); let result: BOOL = unsafe { msg_send![self.inner, canAddInput:input.inner] }; result == YES } diff --git a/src/backends/capture/avfoundation.rs b/src/backends/capture/avfoundation.rs index ca35cf7..e221d93 100644 --- a/src/backends/capture/avfoundation.rs +++ b/src/backends/capture/avfoundation.rs @@ -11,7 +11,7 @@ use crate::{ use image::{ImageBuffer, Rgb}; use nokhwa_bindings_macos::avfoundation::{ query_avfoundation, AVCaptureDevice, AVCaptureDeviceInput, AVCaptureSession, - AVCaptureVideoCallback, AVCaptureVideoDataOutput, + AVCaptureVideoCallback, AVCaptureVideoDataOutput, AVFourCC, }; use std::{any::Any, borrow::Cow, collections::HashMap}; @@ -20,6 +20,8 @@ use std::{any::Any, borrow::Cow, collections::HashMap}; /// # Quirks /// - While working with `iOS` is allowed, it is not officially supported and may not work. /// - You **must** call [`nokhwa_initialize`](crate::nokhwa_initialize) **before** doing anything with `AVFoundation`. +/// - This only works on 64 bit platforms. +/// - FPS adjustment does not work. pub struct AVFoundationCaptureDevice { device: AVCaptureDevice, dev_input: Option, @@ -52,7 +54,9 @@ impl AVFoundationCaptureDevice { } }; - let device = AVCaptureDevice::from_id(&device_descriptor.misc())?; + let mut device = AVCaptureDevice::from_id(&device_descriptor.misc())?; + device.lock()?; + device.set_all(camera_format.into())?; Ok(AVFoundationCaptureDevice { device, @@ -95,10 +99,8 @@ impl CaptureBackendTrait for AVFoundationCaptureDevice { } fn set_camera_format(&mut self, new_fmt: CameraFormat) -> Result<(), NokhwaError> { - self.device.lock()?; self.device.set_all(new_fmt.into())?; self.format = new_fmt; - self.device.unlock(); Ok(()) } @@ -208,11 +210,13 @@ impl CaptureBackendTrait for AVFoundationCaptureDevice { fn open_stream(&mut self) -> Result<(), NokhwaError> { let input = AVCaptureDeviceInput::new(&self.device)?; let session = AVCaptureSession::new(); + session.begin_configuration(); session.add_input(&input)?; - let callback = AVCaptureVideoCallback::new(); + let callback = AVCaptureVideoCallback::new(self.info.index()); let output = AVCaptureVideoDataOutput::new(); output.add_delegate(&callback)?; session.add_output(&output)?; + session.commit_configuration(); session.start()?; self.dev_input = Some(input); @@ -238,11 +242,7 @@ impl CaptureBackendTrait for AVFoundationCaptureDevice { fn frame(&mut self) -> Result, Vec>, NokhwaError> { let cam_fmt = self.camera_format(); - let raw_frame = self.frame_raw()?; - let conv = match cam_fmt.format() { - FrameFormat::MJPEG => mjpeg_to_rgb888(&raw_frame)?, - FrameFormat::YUYV => yuyv422_to_rgb888(&raw_frame)?, - }; + let conv = self.frame_raw()?.to_vec(); let image_buf = match ImageBuffer::from_vec(cam_fmt.width(), cam_fmt.height(), conv) { Some(buf) => { @@ -258,9 +258,33 @@ impl CaptureBackendTrait for AVFoundationCaptureDevice { } fn frame_raw(&mut self) -> Result, NokhwaError> { + match &self.session { + Some(session) => { + if !session.is_running() { + return Err(NokhwaError::ReadFrameError( + "Stream Not Started".to_string(), + )); + } + if session.is_interrupted() { + return Err(NokhwaError::ReadFrameError( + "Stream Interrupted".to_string(), + )); + } + } + None => { + return Err(NokhwaError::ReadFrameError( + "Stream Not Started".to_string(), + )) + } + } + match &self.data_collect { Some(collector) => { let data = collector.frame_to_slice()?; + let data = match data.1 { + AVFourCC::YUV2 => Cow::from(yuyv422_to_rgb888(&data.0)?), + AVFourCC::MJPEG => Cow::from(mjpeg_to_rgb888(&data.0)?), + }; Ok(data) } None => Err(NokhwaError::ReadFrameError( diff --git a/src/utils.rs b/src/utils.rs index a7ef2a6..49409ec 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -372,7 +372,7 @@ impl From for CaptureDeviceFormatDescriptor { width: cf.width() as i32, height: cf.height() as i32, }, - fps: cf.frame_rate() as f64, + fps: cf.frame_rate(), fourcc: cf.format().into(), } }