From 3b2a54c4c464b8f4a064d87941a1561044618ad2 Mon Sep 17 00:00:00 2001 From: Josh Bernfeld Date: Mon, 2 Apr 2018 18:31:29 -0700 Subject: [PATCH 1/3] [MovieInput/MovieOutput/SpeakerOutput] Audio support, synchronized encoding, internal movie recording dispatch queue context support, looping, pausing, encoding progress, real-time threads (optional), AVVideoComposition support, resolve silently dropped frames, etc. --- README.md | 100 ++- .../project.pbxproj | 467 ++++++++++++++ .../SimpleMovieEncoding/AppDelegate.swift | 46 ++ .../AppIcon.appiconset/Contents.json | 93 +++ .../Base.lproj/LaunchScreen.storyboard | 25 + .../Base.lproj/Main.storyboard | 44 ++ .../SimpleMovieEncoding/Info.plist | 45 ++ .../SimpleMovieEncoding/ViewController.swift | 131 ++++ .../lookup_miss_etikate.png | Bin 0 -> 202596 bytes .../project.pbxproj | 26 +- .../SimpleMovieFilter/AppDelegate.swift | 0 .../Base.lproj/LaunchScreen.storyboard | 0 .../Base.lproj/Main.storyboard | 65 +- .../SimpleMovieFilter/Info.plist | 0 .../SimpleMovieFilter/ViewController.swift | 31 +- .../project.pbxproj | 6 +- .../project.pbxproj | 6 +- .../SimpleVideoRecorder/AppDelegate.swift | 0 .../Base.lproj/LaunchScreen.storyboard | 0 .../SimpleVideoRecorder/Info.plist | 0 .../SimpleVideoRecorder/ViewController.swift | 24 +- framework/GPUImage.xcodeproj/project.pbxproj | 46 +- framework/Source/GPUImage-Bridging-Header.h | 15 + framework/Source/NSObject+Exception.h | 14 + framework/Source/NSObject+Exception.m | 24 + framework/Source/OpenGLContext_Shared.swift | 2 +- framework/Source/OpenGLRendering.swift | 4 +- framework/Source/SerialDispatch.swift | 3 +- framework/Source/ShaderProgram.swift | 2 +- framework/Source/TPCircularBuffer.h | 243 +++++++ framework/Source/TPCircularBuffer.m | 149 +++++ framework/Source/iOS/Camera.swift | 62 +- framework/Source/iOS/MovieInput.swift | 598 ++++++++++++++---- framework/Source/iOS/MovieOutput.swift | 395 ++++++++---- framework/Source/iOS/OpenGLContext.swift | 9 +- framework/Source/iOS/SpeakerOutput.swift | 328 ++++++++++ 36 files changed, 2713 insertions(+), 290 deletions(-) create mode 100755 examples/iOS/SimpleMovieEncoding/SimpleMovieEncoding.xcodeproj/project.pbxproj create mode 100755 examples/iOS/SimpleMovieEncoding/SimpleMovieEncoding/AppDelegate.swift create mode 100755 examples/iOS/SimpleMovieEncoding/SimpleMovieEncoding/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100755 examples/iOS/SimpleMovieEncoding/SimpleMovieEncoding/Base.lproj/LaunchScreen.storyboard create mode 100755 examples/iOS/SimpleMovieEncoding/SimpleMovieEncoding/Base.lproj/Main.storyboard create mode 100755 examples/iOS/SimpleMovieEncoding/SimpleMovieEncoding/Info.plist create mode 100755 examples/iOS/SimpleMovieEncoding/SimpleMovieEncoding/ViewController.swift create mode 100755 examples/iOS/SimpleMovieEncoding/SimpleMovieEncoding/lookup_miss_etikate.png mode change 100644 => 100755 examples/iOS/SimpleMovieFilter/SimpleMovieFilter.xcodeproj/project.pbxproj mode change 100644 => 100755 examples/iOS/SimpleMovieFilter/SimpleMovieFilter/AppDelegate.swift mode change 100644 => 100755 examples/iOS/SimpleMovieFilter/SimpleMovieFilter/Base.lproj/LaunchScreen.storyboard mode change 100644 => 100755 examples/iOS/SimpleMovieFilter/SimpleMovieFilter/Base.lproj/Main.storyboard mode change 100644 => 100755 examples/iOS/SimpleMovieFilter/SimpleMovieFilter/Info.plist mode change 100644 => 100755 examples/iOS/SimpleMovieFilter/SimpleMovieFilter/ViewController.swift mode change 100644 => 100755 examples/iOS/SimpleVideoRecorder/SimpleVideoRecorder.xcodeproj/project.pbxproj mode change 100644 => 100755 examples/iOS/SimpleVideoRecorder/SimpleVideoRecorder/AppDelegate.swift mode change 100644 => 100755 examples/iOS/SimpleVideoRecorder/SimpleVideoRecorder/Base.lproj/LaunchScreen.storyboard mode change 100644 => 100755 examples/iOS/SimpleVideoRecorder/SimpleVideoRecorder/Info.plist mode change 100644 => 100755 examples/iOS/SimpleVideoRecorder/SimpleVideoRecorder/ViewController.swift create mode 100755 framework/Source/GPUImage-Bridging-Header.h create mode 100755 framework/Source/NSObject+Exception.h create mode 100755 framework/Source/NSObject+Exception.m create mode 100755 framework/Source/TPCircularBuffer.h create mode 100755 framework/Source/TPCircularBuffer.m mode change 100644 => 100755 framework/Source/iOS/MovieInput.swift mode change 100644 => 100755 framework/Source/iOS/MovieOutput.swift create mode 100755 framework/Source/iOS/SpeakerOutput.swift diff --git a/README.md b/README.md index 89c2d95d..b8763eb1 100755 --- a/README.md +++ b/README.md @@ -186,24 +186,114 @@ In the above, the imageAvailableCallback will be triggered right at the processI ### Filtering and re-encoding a movie ### -To filter an existing movie file, you can write code like the following: +To filter and playback an existing movie file, you can write code like the following: ```swift do { - let bundleURL = Bundle.main.resourceURL! - let movieURL = URL(string:"sample_iPod.m4v", relativeTo:bundleURL)! - movie = try MovieInput(url:movieURL, playAtActualSpeed:true) + let bundleURL = Bundle.main.resourceURL! + let movieURL = URL(string:"sample_iPod.m4v", relativeTo:bundleURL)! + + let audioDecodeSettings = [AVFormatIDKey:kAudioFormatLinearPCM] + + movie = try MovieInput(url:movieURL, playAtActualSpeed:true, loop:true, audioSettings:audioDecodeSettings) + speaker = SpeakerOutput() + movie.audioEncodingTarget = speaker + filter = SaturationAdjustment() movie --> filter --> renderView + movie.start() + speaker.start() } catch { - fatalError("Could not initialize rendering pipeline: \(error)") + print("Couldn't process movie with error: \(error)") } ``` where renderView is an instance of RenderView that you've placed somewhere in your view hierarchy. The above loads a movie named "sample_iPod.m4v" from the application's bundle, creates a saturation filter, and directs movie frames to be processed through the saturation filter on their way to the screen. start() initiates the movie playback. +To filter an existing movie file and save the result to a new movie file you can write code like the following: + + +```swift +let bundleURL = Bundle.main.resourceURL! +// The movie you want to reencode +let movieURL = URL(string:"sample_iPod.m4v", relativeTo:bundleURL)! + +let documentsDir = FileManager().urls(for:.documentDirectory, in:.userDomainMask).first! +// The location you want to save the new video +let exportedURL = URL(string:"test.mp4", relativeTo:documentsDir)! + +let asset = AVURLAsset(url:movieURL, options:[AVURLAssetPreferPreciseDurationAndTimingKey:NSNumber(value:true)]) + +guard let videoTrack = asset.tracks(withMediaType:AVMediaType.video).first else { return } +let audioTrack = asset.tracks(withMediaType:AVMediaType.audio).first + +// If you would like passthrough audio instead, set both audioDecodingSettings and audioEncodingSettings to nil +let audioDecodingSettings:[String:Any] = [AVFormatIDKey:kAudioFormatLinearPCM] // Noncompressed audio samples + +do { + movieInput = try MovieInput(asset:asset, videoComposition:nil, playAtActualSpeed:false, loop:false, audioSettings:audioDecodingSettings) +} +catch { + print("ERROR: Unable to setup MovieInput with error: \(error)") + return +} + +try? FileManager().removeItem(at: exportedURL) + +let videoEncodingSettings:[String:Any] = [ + AVVideoCompressionPropertiesKey: [ + AVVideoExpectedSourceFrameRateKey:videoTrack.nominalFrameRate, + AVVideoAverageBitRateKey:videoTrack.estimatedDataRate, + AVVideoProfileLevelKey:AVVideoProfileLevelH264HighAutoLevel, + AVVideoH264EntropyModeKey:AVVideoH264EntropyModeCABAC, + AVVideoAllowFrameReorderingKey:videoTrack.requiresFrameReordering], + AVVideoCodecKey:AVVideoCodecH264] + +var acl = AudioChannelLayout() +memset(&acl, 0, MemoryLayout.size) +acl.mChannelLayoutTag = kAudioChannelLayoutTag_Stereo +let audioEncodingSettings:[String:Any] = [ + AVFormatIDKey:kAudioFormatMPEG4AAC, + AVNumberOfChannelsKey:2, + AVSampleRateKey:AVAudioSession.sharedInstance().sampleRate, + AVChannelLayoutKey:NSData(bytes:&acl, length:MemoryLayout.size), + AVEncoderBitRateKey:96000 +] + +do { + movieOutput = try MovieOutput(URL:exportedURL, size:Size(width:Float(videoTrack.naturalSize.width), height:Float(videoTrack.naturalSize.height)), fileType:AVFileType.mp4.rawValue, liveVideo:false, videoSettings:videoEncodingSettings, videoNaturalTimeScale:videoTrack.naturalTimeScale, audioSettings:audioEncodingSettings) +} +catch { + print("ERROR: Unable to setup MovieOutput with error: \(error)") + return +} + +filter = SaturationAdjustment() + +if(audioTrack != nil) { movieInput.audioEncodingTarget = movieOutput } +movieInput.synchronizedMovieOutput = movieOutput +movieInput --> filter --> movieOutput + +movieInput.completion = { + self.movieOutput.finishRecording { + print("Encoding finished") + } +} + +movieOutput.startRecording() { started, error in + if(!started) { + print("ERROR: MovieOutput unable to start writing with error: \(String(describing: error))") + return + } + self.movieInput.start() + print("Encoding started") +} +``` + + The above loads a movie named "sample_iPod.m4v" from the application's bundle, creates a saturation filter, and directs movie frames to be processed through the saturation filter on their way to the new file. In addition it writes the audio in AAC format to the new file. + ### Writing a custom image processing operation ### The framework uses a series of protocols to define types that can output images to be processed, take in an image for processing, or do both. These are the ImageSource, ImageConsumer, and ImageProcessingOperation protocols, respectively. Any type can comply to these, but typically classes are used. diff --git a/examples/iOS/SimpleMovieEncoding/SimpleMovieEncoding.xcodeproj/project.pbxproj b/examples/iOS/SimpleMovieEncoding/SimpleMovieEncoding.xcodeproj/project.pbxproj new file mode 100755 index 00000000..6aae7e00 --- /dev/null +++ b/examples/iOS/SimpleMovieEncoding/SimpleMovieEncoding.xcodeproj/project.pbxproj @@ -0,0 +1,467 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 48; + objects = { + +/* Begin PBXBuildFile section */ + 1F2393442071C12C001886DD /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F2393432071C12C001886DD /* AppDelegate.swift */; }; + 1F2393462071C12C001886DD /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F2393452071C12C001886DD /* ViewController.swift */; }; + 1F2393492071C12C001886DD /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 1F2393472071C12C001886DD /* Main.storyboard */; }; + 1F23934B2071C12C001886DD /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 1F23934A2071C12C001886DD /* Assets.xcassets */; }; + 1F23934E2071C12C001886DD /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 1F23934C2071C12C001886DD /* LaunchScreen.storyboard */; }; + 1F2393662071C169001886DD /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1F2393652071C169001886DD /* AVFoundation.framework */; }; + 1F2393682071C16D001886DD /* CoreAudio.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1F2393672071C16D001886DD /* CoreAudio.framework */; }; + 1F23936D2071C2DB001886DD /* sample_iPod.m4v in Resources */ = {isa = PBXBuildFile; fileRef = 1F23936C2071C2DB001886DD /* sample_iPod.m4v */; }; + 1F2393772071F51C001886DD /* GPUImage.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 1F2393612071C155001886DD /* GPUImage.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 1F2393792071FCB1001886DD /* Assets-iOS.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 1F2393782071FCB1001886DD /* Assets-iOS.xcassets */; }; + 1F23937B2071FCDB001886DD /* lookup_miss_etikate.png in Resources */ = {isa = PBXBuildFile; fileRef = 1F23937A2071FCDA001886DD /* lookup_miss_etikate.png */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 1F23935C2071C155001886DD /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 1F2393552071C155001886DD /* GPUImage.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = BC6E7CAB1C39A9D8006DF678; + remoteInfo = GPUImage_macOS; + }; + 1F23935E2071C155001886DD /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 1F2393552071C155001886DD /* GPUImage.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = BC6E7CB51C39A9D8006DF678; + remoteInfo = GPUImageTests_macOS; + }; + 1F2393602071C155001886DD /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 1F2393552071C155001886DD /* GPUImage.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = BC9E34E91E524A2200B8604F; + remoteInfo = GPUImage_iOS; + }; + 1F2393622071C155001886DD /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 1F2393552071C155001886DD /* GPUImage.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = BC9E34F11E524A2200B8604F; + remoteInfo = GPUImageTests_iOS; + }; + 1F23936A2071C29D001886DD /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 1F2393552071C155001886DD /* GPUImage.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = BC9E34E81E524A2200B8604F; + remoteInfo = GPUImage_iOS; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 1F2393762071F506001886DD /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + 1F2393772071F51C001886DD /* GPUImage.framework in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 1F2393402071C12C001886DD /* SimpleMovieEncoding.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SimpleMovieEncoding.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 1F2393432071C12C001886DD /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 1F2393452071C12C001886DD /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; + 1F2393482071C12C001886DD /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 1F23934A2071C12C001886DD /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 1F23934D2071C12C001886DD /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 1F23934F2071C12C001886DD /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 1F2393552071C155001886DD /* GPUImage.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = GPUImage.xcodeproj; path = ../../../../framework/GPUImage.xcodeproj; sourceTree = ""; }; + 1F2393652071C169001886DD /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; }; + 1F2393672071C16D001886DD /* CoreAudio.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreAudio.framework; path = System/Library/Frameworks/CoreAudio.framework; sourceTree = SDKROOT; }; + 1F23936C2071C2DB001886DD /* sample_iPod.m4v */ = {isa = PBXFileReference; lastKnownFileType = file; name = sample_iPod.m4v; path = ../../../SharedAssets/sample_iPod.m4v; sourceTree = ""; }; + 1F2393782071FCB1001886DD /* Assets-iOS.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = "Assets-iOS.xcassets"; path = "../../../SharedAssets/Assets-iOS.xcassets"; sourceTree = ""; }; + 1F23937A2071FCDA001886DD /* lookup_miss_etikate.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = lookup_miss_etikate.png; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 1F23933D2071C12C001886DD /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 1F2393682071C16D001886DD /* CoreAudio.framework in Frameworks */, + 1F2393662071C169001886DD /* AVFoundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 1F2393372071C12C001886DD = { + isa = PBXGroup; + children = ( + 1F2393422071C12C001886DD /* SimpleMovieEncoding */, + 1F2393412071C12C001886DD /* Products */, + 1F2393642071C169001886DD /* Frameworks */, + ); + sourceTree = ""; + }; + 1F2393412071C12C001886DD /* Products */ = { + isa = PBXGroup; + children = ( + 1F2393402071C12C001886DD /* SimpleMovieEncoding.app */, + ); + name = Products; + sourceTree = ""; + }; + 1F2393422071C12C001886DD /* SimpleMovieEncoding */ = { + isa = PBXGroup; + children = ( + 1F2393432071C12C001886DD /* AppDelegate.swift */, + 1F2393452071C12C001886DD /* ViewController.swift */, + 1F2393472071C12C001886DD /* Main.storyboard */, + 1F2393552071C155001886DD /* GPUImage.xcodeproj */, + 1F23934A2071C12C001886DD /* Assets.xcassets */, + 1F2393782071FCB1001886DD /* Assets-iOS.xcassets */, + 1F23934C2071C12C001886DD /* LaunchScreen.storyboard */, + 1F23937A2071FCDA001886DD /* lookup_miss_etikate.png */, + 1F23936C2071C2DB001886DD /* sample_iPod.m4v */, + 1F23934F2071C12C001886DD /* Info.plist */, + ); + path = SimpleMovieEncoding; + sourceTree = ""; + }; + 1F2393562071C155001886DD /* Products */ = { + isa = PBXGroup; + children = ( + 1F23935D2071C155001886DD /* GPUImage.framework */, + 1F23935F2071C155001886DD /* GPUImageTests_macOS.xctest */, + 1F2393612071C155001886DD /* GPUImage.framework */, + 1F2393632071C155001886DD /* GPUImageTests_iOS.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 1F2393642071C169001886DD /* Frameworks */ = { + isa = PBXGroup; + children = ( + 1F2393672071C16D001886DD /* CoreAudio.framework */, + 1F2393652071C169001886DD /* AVFoundation.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 1F23933F2071C12C001886DD /* SimpleMovieEncoding */ = { + isa = PBXNativeTarget; + buildConfigurationList = 1F2393522071C12C001886DD /* Build configuration list for PBXNativeTarget "SimpleMovieEncoding" */; + buildPhases = ( + 1F23933C2071C12C001886DD /* Sources */, + 1F23933D2071C12C001886DD /* Frameworks */, + 1F23933E2071C12C001886DD /* Resources */, + 1F2393762071F506001886DD /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + 1F23936B2071C29D001886DD /* PBXTargetDependency */, + ); + name = SimpleMovieEncoding; + productName = SimpleMovieEncoding; + productReference = 1F2393402071C12C001886DD /* SimpleMovieEncoding.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 1F2393382071C12C001886DD /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0920; + LastUpgradeCheck = 0920; + ORGANIZATIONNAME = "Sunset Lake Software LLC"; + TargetAttributes = { + 1F23933F2071C12C001886DD = { + CreatedOnToolsVersion = 9.2; + ProvisioningStyle = Automatic; + }; + }; + }; + buildConfigurationList = 1F23933B2071C12C001886DD /* Build configuration list for PBXProject "SimpleMovieEncoding" */; + compatibilityVersion = "Xcode 8.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 1F2393372071C12C001886DD; + productRefGroup = 1F2393412071C12C001886DD /* Products */; + projectDirPath = ""; + projectReferences = ( + { + ProductGroup = 1F2393562071C155001886DD /* Products */; + ProjectRef = 1F2393552071C155001886DD /* GPUImage.xcodeproj */; + }, + ); + projectRoot = ""; + targets = ( + 1F23933F2071C12C001886DD /* SimpleMovieEncoding */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXReferenceProxy section */ + 1F23935D2071C155001886DD /* GPUImage.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = GPUImage.framework; + remoteRef = 1F23935C2071C155001886DD /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 1F23935F2071C155001886DD /* GPUImageTests_macOS.xctest */ = { + isa = PBXReferenceProxy; + fileType = wrapper.cfbundle; + path = GPUImageTests_macOS.xctest; + remoteRef = 1F23935E2071C155001886DD /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 1F2393612071C155001886DD /* GPUImage.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = GPUImage.framework; + remoteRef = 1F2393602071C155001886DD /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 1F2393632071C155001886DD /* GPUImageTests_iOS.xctest */ = { + isa = PBXReferenceProxy; + fileType = wrapper.cfbundle; + path = GPUImageTests_iOS.xctest; + remoteRef = 1F2393622071C155001886DD /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; +/* End PBXReferenceProxy section */ + +/* Begin PBXResourcesBuildPhase section */ + 1F23933E2071C12C001886DD /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 1F23934E2071C12C001886DD /* LaunchScreen.storyboard in Resources */, + 1F23936D2071C2DB001886DD /* sample_iPod.m4v in Resources */, + 1F23934B2071C12C001886DD /* Assets.xcassets in Resources */, + 1F2393492071C12C001886DD /* Main.storyboard in Resources */, + 1F2393792071FCB1001886DD /* Assets-iOS.xcassets in Resources */, + 1F23937B2071FCDB001886DD /* lookup_miss_etikate.png in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 1F23933C2071C12C001886DD /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 1F2393462071C12C001886DD /* ViewController.swift in Sources */, + 1F2393442071C12C001886DD /* AppDelegate.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 1F23936B2071C29D001886DD /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = GPUImage_iOS; + targetProxy = 1F23936A2071C29D001886DD /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 1F2393472071C12C001886DD /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 1F2393482071C12C001886DD /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 1F23934C2071C12C001886DD /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 1F23934D2071C12C001886DD /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 1F2393502071C12C001886DD /* 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_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = 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_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; + CODE_SIGN_IDENTITY = "iPhone Developer"; + 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; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 1F2393512071C12C001886DD /* 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_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = 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_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; + CODE_SIGN_IDENTITY = "iPhone Developer"; + 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; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 1F2393532071C12C001886DD /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = ""; + INFOPLIST_FILE = SimpleMovieEncoding/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.sunsetlakesoftware.SimpleMovieEncoding; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 1F2393542071C12C001886DD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = ""; + INFOPLIST_FILE = SimpleMovieEncoding/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.sunsetlakesoftware.SimpleMovieEncoding; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 1F23933B2071C12C001886DD /* Build configuration list for PBXProject "SimpleMovieEncoding" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1F2393502071C12C001886DD /* Debug */, + 1F2393512071C12C001886DD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 1F2393522071C12C001886DD /* Build configuration list for PBXNativeTarget "SimpleMovieEncoding" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1F2393532071C12C001886DD /* Debug */, + 1F2393542071C12C001886DD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 1F2393382071C12C001886DD /* Project object */; +} diff --git a/examples/iOS/SimpleMovieEncoding/SimpleMovieEncoding/AppDelegate.swift b/examples/iOS/SimpleMovieEncoding/SimpleMovieEncoding/AppDelegate.swift new file mode 100755 index 00000000..14a4337e --- /dev/null +++ b/examples/iOS/SimpleMovieEncoding/SimpleMovieEncoding/AppDelegate.swift @@ -0,0 +1,46 @@ +// +// AppDelegate.swift +// SimpleMovieEncoding +// +// Created by Josh Bernfeld on 4/1/18. +// Copyright © 2018 Sunset Lake Software LLC. All rights reserved. +// + +import UIKit + +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate { + + var window: UIWindow? + + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { + // Override point for customization after application launch. + return true + } + + func applicationWillResignActive(_ application: UIApplication) { + // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. + // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. + } + + func applicationDidEnterBackground(_ application: UIApplication) { + // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. + // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. + } + + func applicationWillEnterForeground(_ application: UIApplication) { + // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. + } + + func applicationDidBecomeActive(_ application: UIApplication) { + // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. + } + + func applicationWillTerminate(_ application: UIApplication) { + // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. + } + + +} + diff --git a/examples/iOS/SimpleMovieEncoding/SimpleMovieEncoding/Assets.xcassets/AppIcon.appiconset/Contents.json b/examples/iOS/SimpleMovieEncoding/SimpleMovieEncoding/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100755 index 00000000..1d060ed2 --- /dev/null +++ b/examples/iOS/SimpleMovieEncoding/SimpleMovieEncoding/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,93 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "83.5x83.5", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/examples/iOS/SimpleMovieEncoding/SimpleMovieEncoding/Base.lproj/LaunchScreen.storyboard b/examples/iOS/SimpleMovieEncoding/SimpleMovieEncoding/Base.lproj/LaunchScreen.storyboard new file mode 100755 index 00000000..f83f6fd5 --- /dev/null +++ b/examples/iOS/SimpleMovieEncoding/SimpleMovieEncoding/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/iOS/SimpleMovieEncoding/SimpleMovieEncoding/Base.lproj/Main.storyboard b/examples/iOS/SimpleMovieEncoding/SimpleMovieEncoding/Base.lproj/Main.storyboard new file mode 100755 index 00000000..e411a78f --- /dev/null +++ b/examples/iOS/SimpleMovieEncoding/SimpleMovieEncoding/Base.lproj/Main.storyboard @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/iOS/SimpleMovieEncoding/SimpleMovieEncoding/Info.plist b/examples/iOS/SimpleMovieEncoding/SimpleMovieEncoding/Info.plist new file mode 100755 index 00000000..16be3b68 --- /dev/null +++ b/examples/iOS/SimpleMovieEncoding/SimpleMovieEncoding/Info.plist @@ -0,0 +1,45 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/examples/iOS/SimpleMovieEncoding/SimpleMovieEncoding/ViewController.swift b/examples/iOS/SimpleMovieEncoding/SimpleMovieEncoding/ViewController.swift new file mode 100755 index 00000000..3ddf2d5a --- /dev/null +++ b/examples/iOS/SimpleMovieEncoding/SimpleMovieEncoding/ViewController.swift @@ -0,0 +1,131 @@ +// +// ViewController.swift +// SimpleMovieEncoding +// +// Created by Josh Bernfeld on 4/1/18. +// Copyright © 2018 Sunset Lake Software LLC. All rights reserved. +// + +import UIKit +import GPUImage +import CoreAudio +import AVFoundation + +class ViewController: UIViewController { + + @IBOutlet var progressView:UIProgressView! + + var movieInput:MovieInput! + var movieOutput:MovieOutput! + var filter:MissEtikateFilter! + + override func viewDidLoad() { + super.viewDidLoad() + // Do any additional setup after loading the view, typically from a nib. + + let bundleURL = Bundle.main.resourceURL! + // The movie you want to reencode + let movieURL = URL(string:"sample_iPod.m4v", relativeTo:bundleURL)! + + let documentsDir = FileManager().urls(for:.documentDirectory, in:.userDomainMask).first! + // The location you want to save the new video + let exportedURL = URL(string:"test.mp4", relativeTo:documentsDir)! + + let inputOptions = [AVURLAssetPreferPreciseDurationAndTimingKey:NSNumber(value:true)] + let asset = AVURLAsset(url:movieURL, options:inputOptions) + + guard let videoTrack = asset.tracks(withMediaType:AVMediaType.video).first else { return } + let audioTrack = asset.tracks(withMediaType:AVMediaType.audio).first + + let audioDecodingSettings:[String:Any]? + let audioEncodingSettings:[String:Any]? + var audioSourceFormatHint:CMFormatDescription? = nil + + let shouldPassthroughAudio = false + if(shouldPassthroughAudio) { + audioDecodingSettings = nil + audioEncodingSettings = nil + // A format hint is required when writing to certain file types with passthrough audio + // A conditional downcast would not work here for some reason + if let description = audioTrack?.formatDescriptions.first { audioSourceFormatHint = (description as! CMFormatDescription) } + } + else { + audioDecodingSettings = [AVFormatIDKey:kAudioFormatLinearPCM] // Noncompressed audio samples + var acl = AudioChannelLayout() + memset(&acl, 0, MemoryLayout.size) + acl.mChannelLayoutTag = kAudioChannelLayoutTag_Stereo + audioEncodingSettings = [ + AVFormatIDKey:kAudioFormatMPEG4AAC, + AVNumberOfChannelsKey:2, + AVSampleRateKey:AVAudioSession.sharedInstance().sampleRate, + AVChannelLayoutKey:NSData(bytes:&acl, length:MemoryLayout.size), + AVEncoderBitRateKey:96000 + ] + audioSourceFormatHint = nil + } + + do { + movieInput = try MovieInput(asset:asset, videoComposition:nil, playAtActualSpeed:false, loop:false, audioSettings:audioDecodingSettings) + } + catch { + print("ERROR: Unable to setup MovieInput with error: \(error)") + return + } + + try? FileManager().removeItem(at: exportedURL) + + let videoEncodingSettings:[String:Any] = [ + AVVideoCompressionPropertiesKey: [ + AVVideoExpectedSourceFrameRateKey:videoTrack.nominalFrameRate, + AVVideoAverageBitRateKey:videoTrack.estimatedDataRate, + AVVideoProfileLevelKey:AVVideoProfileLevelH264HighAutoLevel, + AVVideoH264EntropyModeKey:AVVideoH264EntropyModeCABAC, + AVVideoAllowFrameReorderingKey:videoTrack.requiresFrameReordering], + AVVideoCodecKey:AVVideoCodecH264] + + do { + movieOutput = try MovieOutput(URL: exportedURL, size:Size(width:Float(videoTrack.naturalSize.width), height:Float(videoTrack.naturalSize.height)), fileType:AVFileType.mp4.rawValue, liveVideo:false, videoSettings:videoEncodingSettings, videoNaturalTimeScale:videoTrack.naturalTimeScale, audioSettings:audioEncodingSettings, audioSourceFormatHint:audioSourceFormatHint) + } + catch { + print("ERROR: Unable to setup MovieOutput with error: \(error)") + return + } + + filter = MissEtikateFilter() + + if(audioTrack != nil) { movieInput.audioEncodingTarget = movieOutput } + movieInput.synchronizedMovieOutput = movieOutput + //movieInput.synchronizedEncodingDebug = true + movieInput --> filter --> movieOutput + + movieInput.completion = { + self.movieOutput.finishRecording { + DispatchQueue.main.async { + print("Encoding finished") + } + } + } + movieInput.progress = { progressVal in + DispatchQueue.main.async { + self.progressView.progress = Float(progressVal) + } + } + + movieOutput.startRecording() { started, error in + if(!started) { + print("ERROR: MovieOutput unable to start writing with error: \(String(describing: error))") + return + } + self.movieInput.start() + print("Encoding started") + } + } + + override func didReceiveMemoryWarning() { + super.didReceiveMemoryWarning() + // Dispose of any resources that can be recreated. + } + + +} + diff --git a/examples/iOS/SimpleMovieEncoding/SimpleMovieEncoding/lookup_miss_etikate.png b/examples/iOS/SimpleMovieEncoding/SimpleMovieEncoding/lookup_miss_etikate.png new file mode 100755 index 0000000000000000000000000000000000000000..e1317d7819b051663709c484c7b8c773e95ed6d4 GIT binary patch literal 202596 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 z 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

03m0W9-p;;K2 zs*=rRiJj5t)z6&;Hh8d93CnW6S{ns`B?bY%3l@P>$sn)xy=V8x4GR2ME$}#+xrh80 z#1R^V(?>rh1$$iRpBYGe%~Ws$wOm~INGdgrY;llHk`i>GGUDAP`cJyYln?R9a|nkz zyzCS=E;KD*0&q+{@)d@qD?7m--;;2<6eG!CaHl;0#mi48$nYy)l=N?p8~s0Ls=nwa zD5-q2lK3h>3iwQ|=y^#PJ{k)Ro1smys92q;;00At+4Tog;J$3&YHnEJuD(H0Whnm@ zn#V2HN818KnDr;qzt^>|y%U;zXh#?1p-cGC0sCTGGx3ojF1!*X#}+59;ZC^ibv3$D zEQu1tY!b4v4B=0L;ZGK;fs+>`VUp*%#IA{^C?d0bvKwAqk1>W#~d!w4bG` zr9&{Nidf=hkR{~Q{seKm2(t(FJsrJ-B2?SCQni?*YnyWArUmM&a#`&A;-&X1h5r6M z1`dS7R&tRx)B)c0uUxY3KARzz5Fte#>TlpjRWGrb3}FYx`p8w2>r{5@@tX<7hC@he z82+9`@KieT6}GE#902d18zTfKJ`D|?!QhtsY_pzl9X312u>;GxkS6RwV)^-600u;t z?i#2mFn=5VRVT!CNd!PBAIYrI`iyA9e8(Pql(ort~3Y;zbNf(jXnw^`Fg ze9_yNKv7vR06@j5$skB;u52%0w#rdEOJ77C!Q3)8XLXLN1dLu==;-i^7 zAd~h5i}ReWlfle93OlXm9k${av<-hZp~`TC{Jd#T`{%{Ak ziMcv}(?B&N!2F<-6#n~en#(;1QNMr7t~jSR$lq>tbLa+i?3FOPmW5n2jt-P=8Oj^1 zWv|x^0OzAfze)593Jgyv8sc`PkeaUS88I&{e1M=g~LBQ42aH(@UI*X zRK1PnRs}7eMM8^O3gj`@dWB!x2{Le!Y$#l2v~dB~pL;dLCd3w@PaF4>(J7LHmqH41 z%O-XEGme_%KcppOi3>zLg1?F9S{IbJe@~`rL=SK>6`1=RKz9I{EMtFE^8had_EmEk zHS4FAK+pF8v;h&!&`kHYyaw&r!UIoa!eRuhJq7ko6tI<`wvCHbq!&caDu`b%kmDvL zMYper7Zl20938mQAP!gAc!%>iGJzKm=j|<$0z;fL*|kkqzCoVVAcFd0cjBa{!1Tl} zs0_&d8qD;Ml85OJV8SY~4`B7s7eOl4u{B5w`rFez1qMzYx!>kgTSp+<0HMiGn78i3 z`m9VEsr~Ipk;d{O2$o9HXLX!6y>t#pXGn$E)yrC1QbQ#g^`C#^BT^O%!3_<_n09XKdc%c*fYe@3eXnK33Kz~E&&DOE?0CQ^L)DRa$ zR$WC?P=7IhuMmD`0D&8enmBd{5*8>xE_d&&zqKHCGnKpOT(89Sj*B*Ke`Nt<{KbiQ zTN-89ZWi$G3Q=Jj4X~g~p|duby`ukaO!=Kaj^H&vt&FSEzzhB&PcuL)@S0JuzcZu5 z714rDldH;VZD|HzhC5F8O?Pu6v^UICI-dX z$&N!z2Ahw(@D^DMaqso^Cb_<^uoJRM|IKJx0NL-;9i+Rm=V22EL}~iEWRSrKo@0cQIs`Ph zt`vHA;`c((K}Y!lD?(Y#0Km4zbxd42A_nRaH0XKF{-BTs`ElkQN53VwCYlzbcRhF& z+|ldduM_@iofkyZZ4L{(G^F}d1v2{EjlPgUemR6*iu4IEcEC +6EY7dr56a7qsy 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 WV2kfaZkx{l0000 - + + + + + - + + + @@ -14,18 +19,61 @@ - + - - - + + + + + + + + + - + + + + + + + + @@ -34,6 +82,7 @@ + diff --git a/examples/iOS/SimpleMovieFilter/SimpleMovieFilter/Info.plist b/examples/iOS/SimpleMovieFilter/SimpleMovieFilter/Info.plist old mode 100644 new mode 100755 diff --git a/examples/iOS/SimpleMovieFilter/SimpleMovieFilter/ViewController.swift b/examples/iOS/SimpleMovieFilter/SimpleMovieFilter/ViewController.swift old mode 100644 new mode 100755 index 173b6b8a..ff9431f2 --- a/examples/iOS/SimpleMovieFilter/SimpleMovieFilter/ViewController.swift +++ b/examples/iOS/SimpleMovieFilter/SimpleMovieFilter/ViewController.swift @@ -1,5 +1,7 @@ import UIKit import GPUImage +import CoreAudio +import AVFoundation class ViewController: UIViewController { @@ -7,19 +9,27 @@ class ViewController: UIViewController { var movie:MovieInput! var filter:Pixellate! + var speaker:SpeakerOutput! - override func viewDidLayoutSubviews() { - super.viewDidLayoutSubviews() + override func viewDidLoad() { + super.viewDidLoad() let bundleURL = Bundle.main.resourceURL! let movieURL = URL(string:"sample_iPod.m4v", relativeTo:bundleURL)! do { - movie = try MovieInput(url:movieURL, playAtActualSpeed:true) + let audioDecodeSettings = [AVFormatIDKey:kAudioFormatLinearPCM] + + movie = try MovieInput(url:movieURL, playAtActualSpeed:true, loop:true, audioSettings:audioDecodeSettings) + speaker = SpeakerOutput() + movie.audioEncodingTarget = speaker + filter = Pixellate() movie --> filter --> renderView movie.runBenchmark = true + movie.start() + speaker.start() } catch { print("Couldn't process movie with error: \(error)") } @@ -28,5 +38,20 @@ class ViewController: UIViewController { // let fileURL = NSURL(string:"test.png", relativeToURL:documentsDir)! // try pngImage.writeToURL(fileURL, options:.DataWritingAtomic) } + + @IBAction func pause() { + movie.pause() + speaker.cancel() + } + + @IBAction func cancel() { + movie.cancel() + speaker.cancel() + } + + @IBAction func play() { + movie.start() + speaker.start() + } } diff --git a/examples/iOS/SimpleVideoFilter/SimpleVideoFilter.xcodeproj/project.pbxproj b/examples/iOS/SimpleVideoFilter/SimpleVideoFilter.xcodeproj/project.pbxproj index 757f18c9..1f49e1f5 100755 --- a/examples/iOS/SimpleVideoFilter/SimpleVideoFilter.xcodeproj/project.pbxproj +++ b/examples/iOS/SimpleVideoFilter/SimpleVideoFilter.xcodeproj/project.pbxproj @@ -92,7 +92,7 @@ isa = PBXGroup; children = ( BC9E36861E525C2A00B8604F /* GPUImage.framework */, - BC9E36881E525C2A00B8604F /* GPUImage.xctest */, + BC9E36881E525C2A00B8604F /* GPUImageTests_macOS.xctest */, BC9E368A1E525C2A00B8604F /* GPUImage.framework */, BC9E368C1E525C2A00B8604F /* GPUImageTests_iOS.xctest */, ); @@ -208,10 +208,10 @@ remoteRef = BC9E36851E525C2A00B8604F /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; - BC9E36881E525C2A00B8604F /* GPUImage.xctest */ = { + BC9E36881E525C2A00B8604F /* GPUImageTests_macOS.xctest */ = { isa = PBXReferenceProxy; fileType = wrapper.cfbundle; - path = GPUImage.xctest; + path = GPUImageTests_macOS.xctest; remoteRef = BC9E36871E525C2A00B8604F /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; diff --git a/examples/iOS/SimpleVideoRecorder/SimpleVideoRecorder.xcodeproj/project.pbxproj b/examples/iOS/SimpleVideoRecorder/SimpleVideoRecorder.xcodeproj/project.pbxproj old mode 100644 new mode 100755 index 937e99bf..c977e0c6 --- a/examples/iOS/SimpleVideoRecorder/SimpleVideoRecorder.xcodeproj/project.pbxproj +++ b/examples/iOS/SimpleVideoRecorder/SimpleVideoRecorder.xcodeproj/project.pbxproj @@ -92,7 +92,7 @@ isa = PBXGroup; children = ( BC9E36991E525C9900B8604F /* GPUImage.framework */, - BC9E369B1E525C9900B8604F /* GPUImage.xctest */, + BC9E369B1E525C9900B8604F /* GPUImageTests_macOS.xctest */, BC9E369D1E525C9900B8604F /* GPUImage.framework */, BC9E369F1E525C9900B8604F /* GPUImageTests_iOS.xctest */, ); @@ -209,10 +209,10 @@ remoteRef = BC9E36981E525C9900B8604F /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; - BC9E369B1E525C9900B8604F /* GPUImage.xctest */ = { + BC9E369B1E525C9900B8604F /* GPUImageTests_macOS.xctest */ = { isa = PBXReferenceProxy; fileType = wrapper.cfbundle; - path = GPUImage.xctest; + path = GPUImageTests_macOS.xctest; remoteRef = BC9E369A1E525C9900B8604F /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; diff --git a/examples/iOS/SimpleVideoRecorder/SimpleVideoRecorder/AppDelegate.swift b/examples/iOS/SimpleVideoRecorder/SimpleVideoRecorder/AppDelegate.swift old mode 100644 new mode 100755 diff --git a/examples/iOS/SimpleVideoRecorder/SimpleVideoRecorder/Base.lproj/LaunchScreen.storyboard b/examples/iOS/SimpleVideoRecorder/SimpleVideoRecorder/Base.lproj/LaunchScreen.storyboard old mode 100644 new mode 100755 diff --git a/examples/iOS/SimpleVideoRecorder/SimpleVideoRecorder/Info.plist b/examples/iOS/SimpleVideoRecorder/SimpleVideoRecorder/Info.plist old mode 100644 new mode 100755 diff --git a/examples/iOS/SimpleVideoRecorder/SimpleVideoRecorder/ViewController.swift b/examples/iOS/SimpleVideoRecorder/SimpleVideoRecorder/ViewController.swift old mode 100644 new mode 100755 index 82ed237e..ad950f26 --- a/examples/iOS/SimpleVideoRecorder/SimpleVideoRecorder/ViewController.swift +++ b/examples/iOS/SimpleVideoRecorder/SimpleVideoRecorder/ViewController.swift @@ -38,10 +38,30 @@ class ViewController: UIViewController { } catch { } - movieOutput = try MovieOutput(URL:fileURL, size:Size(width:480, height:640), liveVideo:true) + // Do this now so we can access the audioOutput recommendedAudioSettings before initializing the MovieOutput + do { + try self.camera.addAudioInputsAndOutputs() + } catch { + fatalError("ERROR: Could not connect audio target with error: \(error)") + } + + let audioSettings = self.camera!.audioOutput?.recommendedAudioSettingsForAssetWriter(withOutputFileType:AVFileTypeMPEG4) as? [String : Any] + var videoSettings:[String : Any]? = nil + if #available(iOS 11.0, *) { + videoSettings = self.camera!.videoOutput.recommendedVideoSettings(forVideoCodecType:.h264, assetWriterOutputFileType:AVFileTypeMPEG4) as? [String : Any] + videoSettings![AVVideoWidthKey] = nil + videoSettings![AVVideoHeightKey] = nil + } + + movieOutput = try MovieOutput(URL:fileURL, size:Size(width:480, height:640), fileType:AVFileTypeMPEG4, liveVideo:true, videoSettings:videoSettings, audioSettings:audioSettings) camera.audioEncodingTarget = movieOutput filter --> movieOutput! - movieOutput!.startRecording() + movieOutput!.startRecording() { started, error in + if(!started) { + self.isRecording = false + fatalError("ERROR: Could not start writing with error: \(String(describing: error))") + } + } DispatchQueue.main.async { // Label not updating on the main thread, for some reason, so dispatching slightly after this (sender as! UIButton).titleLabel!.text = "Stop" diff --git a/framework/GPUImage.xcodeproj/project.pbxproj b/framework/GPUImage.xcodeproj/project.pbxproj index a18a613c..bbc96339 100755 --- a/framework/GPUImage.xcodeproj/project.pbxproj +++ b/framework/GPUImage.xcodeproj/project.pbxproj @@ -7,6 +7,17 @@ objects = { /* Begin PBXBuildFile section */ + 1F499A731FDA0F9F0000E37E /* NSObject+Exception.m in Sources */ = {isa = PBXBuildFile; fileRef = 1F499A711FDA0F9E0000E37E /* NSObject+Exception.m */; }; + 1F499A741FDA0F9F0000E37E /* NSObject+Exception.m in Sources */ = {isa = PBXBuildFile; fileRef = 1F499A711FDA0F9E0000E37E /* NSObject+Exception.m */; }; + 1F499A751FDA0F9F0000E37E /* NSObject+Exception.h in Headers */ = {isa = PBXBuildFile; fileRef = 1F499A721FDA0F9F0000E37E /* NSObject+Exception.h */; }; + 1F499A761FDA0F9F0000E37E /* NSObject+Exception.h in Headers */ = {isa = PBXBuildFile; fileRef = 1F499A721FDA0F9F0000E37E /* NSObject+Exception.h */; }; + 1F6D1CB32048F81D00317B5F /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1F6D1CB22048F81D00317B5F /* AudioToolbox.framework */; }; + 1F6D1CB52048F8DD00317B5F /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1F6D1CB42048F8DD00317B5F /* AVFoundation.framework */; }; + 1F6D1CB82048FB0300317B5F /* TPCircularBuffer.h in Headers */ = {isa = PBXBuildFile; fileRef = 1F6D1CB62048FB0300317B5F /* TPCircularBuffer.h */; }; + 1F6D1CB92048FB0300317B5F /* TPCircularBuffer.h in Headers */ = {isa = PBXBuildFile; fileRef = 1F6D1CB62048FB0300317B5F /* TPCircularBuffer.h */; }; + 1F6D1CBA2048FB0300317B5F /* TPCircularBuffer.m in Sources */ = {isa = PBXBuildFile; fileRef = 1F6D1CB72048FB0300317B5F /* TPCircularBuffer.m */; }; + 1F6D1CBB2048FB0300317B5F /* TPCircularBuffer.m in Sources */ = {isa = PBXBuildFile; fileRef = 1F6D1CB72048FB0300317B5F /* TPCircularBuffer.m */; }; + 1F6D1CC02048FFD900317B5F /* SpeakerOutput.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F6D1CBF2048FFD900317B5F /* SpeakerOutput.swift */; }; BC09239E1C92658200A2ADFA /* ShaderProgram_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC09239D1C92658200A2ADFA /* ShaderProgram_Tests.swift */; }; BC0923A11C92661D00A2ADFA /* Pipeline_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC09239F1C9265A600A2ADFA /* Pipeline_Tests.swift */; }; BC0923A21C92664900A2ADFA /* Framebuffer.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCB279EB1C8D11630013E213 /* Framebuffer.swift */; }; @@ -370,6 +381,14 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 1F499A711FDA0F9E0000E37E /* NSObject+Exception.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSObject+Exception.m"; path = "Source/NSObject+Exception.m"; sourceTree = ""; }; + 1F499A721FDA0F9F0000E37E /* NSObject+Exception.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSObject+Exception.h"; path = "Source/NSObject+Exception.h"; sourceTree = ""; }; + 1F499A771FDA0FE20000E37E /* GPUImage-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "GPUImage-Bridging-Header.h"; path = "Source/GPUImage-Bridging-Header.h"; sourceTree = ""; }; + 1F6D1CB22048F81D00317B5F /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS11.2.sdk/System/Library/Frameworks/AudioToolbox.framework; sourceTree = DEVELOPER_DIR; }; + 1F6D1CB42048F8DD00317B5F /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS11.2.sdk/System/Library/Frameworks/AVFoundation.framework; sourceTree = DEVELOPER_DIR; }; + 1F6D1CB62048FB0300317B5F /* TPCircularBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TPCircularBuffer.h; path = Source/TPCircularBuffer.h; sourceTree = ""; }; + 1F6D1CB72048FB0300317B5F /* TPCircularBuffer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TPCircularBuffer.m; path = Source/TPCircularBuffer.m; sourceTree = ""; }; + 1F6D1CBF2048FFD900317B5F /* SpeakerOutput.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SpeakerOutput.swift; path = Source/iOS/SpeakerOutput.swift; sourceTree = ""; }; BC09239D1C92658200A2ADFA /* ShaderProgram_Tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ShaderProgram_Tests.swift; path = Tests/ShaderProgram_Tests.swift; sourceTree = SOURCE_ROOT; }; BC09239F1C9265A600A2ADFA /* Pipeline_Tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Pipeline_Tests.swift; path = Tests/Pipeline_Tests.swift; sourceTree = SOURCE_ROOT; }; BC1E12F41C9F2FD7008F844F /* ThreeInput.vsh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.glsl; name = ThreeInput.vsh; path = Source/Operations/Shaders/ThreeInput.vsh; sourceTree = ""; }; @@ -710,6 +729,8 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 1F6D1CB52048F8DD00317B5F /* AVFoundation.framework in Frameworks */, + 1F6D1CB32048F81D00317B5F /* AudioToolbox.framework in Frameworks */, BC9E35021E524BE200B8604F /* OpenGLES.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -961,8 +982,13 @@ BC6E7CAD1C39A9D8006DF678 /* Other */ = { isa = PBXGroup; children = ( + 1F6D1CB62048FB0300317B5F /* TPCircularBuffer.h */, + 1F6D1CB72048FB0300317B5F /* TPCircularBuffer.m */, + 1F499A721FDA0F9F0000E37E /* NSObject+Exception.h */, + 1F499A711FDA0F9E0000E37E /* NSObject+Exception.m */, BC4C85ED1C9F042900FD95D8 /* ConvertedShaders_GL.swift */, BC9E35531E52521F00B8604F /* ConvertedShaders_GLES.swift */, + 1F499A771FDA0FE20000E37E /* GPUImage-Bridging-Header.h */, ); name = Other; sourceTree = ""; @@ -1002,6 +1028,8 @@ BC6E7CCB1C39ADDD006DF678 /* Frameworks */ = { isa = PBXGroup; children = ( + 1F6D1CB42048F8DD00317B5F /* AVFoundation.framework */, + 1F6D1CB22048F81D00317B5F /* AudioToolbox.framework */, BC9E35011E524BE200B8604F /* OpenGLES.framework */, BC6E7CC91C39ADCC006DF678 /* OpenGL.framework */, ); @@ -1126,6 +1154,7 @@ BC9E35231E524D4D00B8604F /* RenderView.swift */, BC9E35221E524D4D00B8604F /* PictureOutput.swift */, BC9E35211E524D4D00B8604F /* MovieOutput.swift */, + 1F6D1CBF2048FFD900317B5F /* SpeakerOutput.swift */, ); name = iOS; sourceTree = ""; @@ -1198,6 +1227,8 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( + 1F499A751FDA0F9F0000E37E /* NSObject+Exception.h in Headers */, + 1F6D1CB82048FB0300317B5F /* TPCircularBuffer.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1205,6 +1236,8 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( + 1F499A761FDA0F9F0000E37E /* NSObject+Exception.h in Headers */, + 1F6D1CB92048FB0300317B5F /* TPCircularBuffer.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1295,13 +1328,14 @@ TargetAttributes = { BC6E7CAA1C39A9D8006DF678 = { CreatedOnToolsVersion = 7.2; - LastSwiftMigration = 0800; + LastSwiftMigration = 0910; }; BC6E7CB41C39A9D8006DF678 = { CreatedOnToolsVersion = 7.2; }; BC9E34E81E524A2200B8604F = { CreatedOnToolsVersion = 8.2.1; + LastSwiftMigration = 0910; ProvisioningStyle = Automatic; }; BC9E34F01E524A2200B8604F = { @@ -1447,6 +1481,7 @@ BCFF46C01CB9556B00A0C521 /* WhiteBalance.swift in Sources */, BC7FD14E1CB0BD3900037949 /* ZoomBlur.swift in Sources */, BCFB07921CBF37A1009B2333 /* TextureInput.swift in Sources */, + 1F6D1CBA2048FB0300317B5F /* TPCircularBuffer.m in Sources */, BC6E7CC71C39AD9E006DF678 /* ShaderProgram.swift in Sources */, BCFF46CA1CB96BD700A0C521 /* HighPassFilter.swift in Sources */, BC7FD1321CB0A57F00037949 /* HighlightsAndShadows.swift in Sources */, @@ -1454,6 +1489,7 @@ BC7FD11C1CB0795A00037949 /* NormalBlend.swift in Sources */, BC4EE15E1CB3481F00AD8A65 /* ThresholdSobelEdgeDetection.swift in Sources */, BC7FD1911CB1D2A300037949 /* ImageGenerator.swift in Sources */, + 1F499A731FDA0F9F0000E37E /* NSObject+Exception.m in Sources */, BC7FD1201CB079B200037949 /* SaturationBlend.swift in Sources */, BCA4E2491CC3EF26007B51BA /* ColourFASTFeatureDetection.swift in Sources */, BC7FD0FD1CB06E0000037949 /* Position.swift in Sources */, @@ -1630,6 +1666,7 @@ BC9E35511E52518F00B8604F /* Timestamp.swift in Sources */, BC9E35781E5256EB00B8604F /* ColorMatrixFilter.swift in Sources */, BC9E35D11E52580400B8604F /* ScreenBlend.swift in Sources */, + 1F6D1CBB2048FB0300317B5F /* TPCircularBuffer.m in Sources */, BC9E356A1E5256C200B8604F /* Haze.swift in Sources */, BC9E35D31E52580A00B8604F /* SourceOverBlend.swift in Sources */, BC9E357E1E5256FE00B8604F /* Vibrance.swift in Sources */, @@ -1637,6 +1674,8 @@ BC9E356E1E5256CE00B8604F /* FalseColor.swift in Sources */, BC9E35881E52572000B8604F /* ThresholdSobelEdgeDetection.swift in Sources */, BC9E356F1E5256D000B8604F /* HighlightsAndShadows.swift in Sources */, + 1F6D1CC02048FFD900317B5F /* SpeakerOutput.swift in Sources */, + 1F499A741FDA0F9F0000E37E /* NSObject+Exception.m in Sources */, BC9E35AA1E52578900B8604F /* Halftone.swift in Sources */, BC9E35961E52574A00B8604F /* ImageBuffer.swift in Sources */, BC9E35831E52571100B8604F /* LocalBinaryPattern.swift in Sources */, @@ -1788,6 +1827,7 @@ ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; SKIP_INSTALL = YES; + SWIFT_OBJC_BRIDGING_HEADER = "Source/GPUImage-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 3.0; VERSIONING_SYSTEM = "apple-generic"; @@ -1831,6 +1871,7 @@ MACOSX_DEPLOYMENT_TARGET = 10.9; SDKROOT = macosx; SKIP_INSTALL = YES; + SWIFT_OBJC_BRIDGING_HEADER = "Source/GPUImage-Bridging-Header.h"; SWIFT_VERSION = 3.0; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; @@ -1910,6 +1951,7 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_ANALYZER_NONNULL = YES; + CLANG_ENABLE_MODULES = YES; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; @@ -1930,6 +1972,7 @@ SDKROOT = iphoneos; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 3.0; TARGETED_DEVICE_FAMILY = "1,2"; }; @@ -1939,6 +1982,7 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_ANALYZER_NONNULL = YES; + CLANG_ENABLE_MODULES = YES; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; diff --git a/framework/Source/GPUImage-Bridging-Header.h b/framework/Source/GPUImage-Bridging-Header.h new file mode 100755 index 00000000..379e1ac3 --- /dev/null +++ b/framework/Source/GPUImage-Bridging-Header.h @@ -0,0 +1,15 @@ +// +// GPUImage-Bridging-Header.h +// GPUImage +// +// Created by Josh Bernfeld on 12/7/17. +// Copyright © 2017 Sunset Lake Software LLC. All rights reserved. +// + +#ifndef GPUImage_Bridging_Header_h +#define GPUImage_Bridging_Header_h + +#import "NSObject+Exception.h" +#import "TPCircularBuffer.h" + +#endif /* GPUImage_Bridging_Header_h */ diff --git a/framework/Source/NSObject+Exception.h b/framework/Source/NSObject+Exception.h new file mode 100755 index 00000000..bb0bf010 --- /dev/null +++ b/framework/Source/NSObject+Exception.h @@ -0,0 +1,14 @@ +// +// NSObject+Exception.h +// GPUImage2 +// +// Created by Josh Bernfeld on 11/23/17. +// + +#import + +@interface NSObject (Exception) + ++ (BOOL)catchException:(void(^)(void))tryBlock error:(__autoreleasing NSError **)error; + +@end diff --git a/framework/Source/NSObject+Exception.m b/framework/Source/NSObject+Exception.m new file mode 100755 index 00000000..ed6d3711 --- /dev/null +++ b/framework/Source/NSObject+Exception.m @@ -0,0 +1,24 @@ +// +// NSObject+Exception.m +// GPUImage2 +// +// Created by Josh Bernfeld on 11/23/17. +// +// Source: https://stackoverflow.com/a/36454808/1275014 + +#import "NSObject+Exception.h" + +@implementation NSObject (Exception) + ++ (BOOL)catchException:(void(^)(void))tryBlock error:(__autoreleasing NSError **)error { + @try { + tryBlock(); + return YES; + } + @catch (NSException *exception) { + *error = [[NSError alloc] initWithDomain:exception.name code:0 userInfo:exception.userInfo]; + return NO; + } +} + +@end diff --git a/framework/Source/OpenGLContext_Shared.swift b/framework/Source/OpenGLContext_Shared.swift index 20473a30..6fcbf83d 100755 --- a/framework/Source/OpenGLContext_Shared.swift +++ b/framework/Source/OpenGLContext_Shared.swift @@ -23,7 +23,7 @@ extension OpenGLContext { if let shaderFromCache = shaderCache[lookupKeyForShaderProgram] { return shaderFromCache } else { - return try sharedImageProcessingContext.runOperationSynchronously{ + return try self.runOperationSynchronously{ let program = try ShaderProgram(vertexShader:vertexShader, fragmentShader:fragmentShader) self.shaderCache[lookupKeyForShaderProgram] = program return program diff --git a/framework/Source/OpenGLRendering.swift b/framework/Source/OpenGLRendering.swift index 020b0b12..ba55c790 100755 --- a/framework/Source/OpenGLRendering.swift +++ b/framework/Source/OpenGLRendering.swift @@ -63,7 +63,7 @@ public let standardImageVertices:[GLfloat] = [-1.0, -1.0, 1.0, -1.0, -1.0, 1.0, public let verticallyInvertedImageVertices:[GLfloat] = [-1.0, 1.0, 1.0, 1.0, -1.0, -1.0, 1.0, -1.0] // "position" and "inputTextureCoordinate", "inputTextureCoordinate2" attribute naming follows the convention of the old GPUImage -public func renderQuadWithShader(_ shader:ShaderProgram, uniformSettings:ShaderUniformSettings? = nil, vertices:[GLfloat]? = nil, vertexBufferObject:GLuint? = nil, inputTextures:[InputTextureProperties]) { +public func renderQuadWithShader(_ shader:ShaderProgram, uniformSettings:ShaderUniformSettings? = nil, vertices:[GLfloat]? = nil, vertexBufferObject:GLuint? = nil, inputTextures:[InputTextureProperties], context: OpenGLContext = sharedImageProcessingContext) { switch (vertices, vertexBufferObject) { case (.none, .some): break case (.some, .none): break @@ -71,7 +71,7 @@ public func renderQuadWithShader(_ shader:ShaderProgram, uniformSettings:ShaderU case (.none, .none): fatalError("Can't specify both vertices and a VBO in renderQuadWithShader()") } - sharedImageProcessingContext.makeCurrentContext() + context.makeCurrentContext() shader.use() uniformSettings?.restoreShaderSettings(shader) diff --git a/framework/Source/SerialDispatch.swift b/framework/Source/SerialDispatch.swift index bdf32f1d..f87aec8c 100755 --- a/framework/Source/SerialDispatch.swift +++ b/framework/Source/SerialDispatch.swift @@ -65,6 +65,7 @@ func runOnMainQueue(_ mainThreadOperation:() -> T) -> T { public protocol SerialDispatch { var serialDispatchQueue:DispatchQueue { get } var dispatchQueueKey:DispatchSpecificKey { get } + var dispatchQueueKeyValue:Int { get } func makeCurrentContext() } @@ -78,7 +79,7 @@ public extension SerialDispatch { public func runOperationSynchronously(_ operation:() -> ()) { // TODO: Verify this works as intended - if (DispatchQueue.getSpecific(key:self.dispatchQueueKey) == 81) { + if (DispatchQueue.getSpecific(key:self.dispatchQueueKey) == self.dispatchQueueKeyValue) { operation() } else { self.serialDispatchQueue.sync { diff --git a/framework/Source/ShaderProgram.swift b/framework/Source/ShaderProgram.swift index abf50b63..7e2013bc 100755 --- a/framework/Source/ShaderProgram.swift +++ b/framework/Source/ShaderProgram.swift @@ -59,7 +59,7 @@ public class ShaderProgram { } deinit { - debugPrint("Shader deallocated") + //debugPrint("Shader deallocated") if (vertexShader != nil) { glDeleteShader(vertexShader) diff --git a/framework/Source/TPCircularBuffer.h b/framework/Source/TPCircularBuffer.h new file mode 100755 index 00000000..88129560 --- /dev/null +++ b/framework/Source/TPCircularBuffer.h @@ -0,0 +1,243 @@ +// +// TPCircularBuffer.h +// Circular/Ring buffer implementation +// +// https://github.com/michaeltyson/TPCircularBuffer +// +// Created by Michael Tyson on 10/12/2011. +// +// +// This implementation makes use of a virtual memory mapping technique that inserts a virtual copy +// of the buffer memory directly after the buffer's end, negating the need for any buffer wrap-around +// logic. Clients can simply use the returned memory address as if it were contiguous space. +// +// The implementation is thread-safe in the case of a single producer and single consumer. +// +// Virtual memory technique originally proposed by Philip Howard (http://vrb.slashusr.org/), and +// adapted to Darwin by Kurt Revis (http://www.snoize.com, +// http://www.snoize.com/Code/PlayBufferedSoundFile.tar.gz) +// +// +// Copyright (C) 2012-2013 A Tasty Pixel +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source distribution. +// + +#ifndef TPCircularBuffer_h +#define TPCircularBuffer_h + +#include +#include +#include + +#ifdef __cplusplus + extern "C++" { + #include + typedef std::atomic_int atomicInt; + #define atomicFetchAdd(a,b) std::atomic_fetch_add(a,b) + } +#else + #include + typedef atomic_int atomicInt; + #define atomicFetchAdd(a,b) atomic_fetch_add(a,b) +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + void *buffer; + uint32_t length; + uint32_t tail; + uint32_t head; + volatile atomicInt fillCount; + bool atomic; +} TPCircularBuffer; + +/*! + * Initialise buffer + * + * Note that the length is advisory only: Because of the way the + * memory mirroring technique works, the true buffer length will + * be multiples of the device page size (e.g. 4096 bytes) + * + * If you intend to use the AudioBufferList utilities, you should + * always allocate a bit more space than you need for pure audio + * data, so there's room for the metadata. How much extra is required + * depends on how many AudioBufferList structures are used, which is + * a function of how many audio frames each buffer holds. A good rule + * of thumb is to add 15%, or at least another 2048 bytes or so. + * + * @param buffer Circular buffer + * @param length Length of buffer + */ +#define TPCircularBufferInit(buffer, length) \ + _TPCircularBufferInit(buffer, length, sizeof(*buffer)) +bool _TPCircularBufferInit(TPCircularBuffer *buffer, uint32_t length, size_t structSize); + +/*! + * Cleanup buffer + * + * Releases buffer resources. + */ +void TPCircularBufferCleanup(TPCircularBuffer *buffer); + +/*! + * Clear buffer + * + * Resets buffer to original, empty state. + * + * This is safe for use by consumer while producer is accessing + * buffer. + */ +void TPCircularBufferClear(TPCircularBuffer *buffer); + +/*! + * Set the atomicity + * + * If you set the atomiticy to false using this method, the buffer will + * not use atomic operations. This can be used to give the compiler a little + * more optimisation opportunities when the buffer is only used on one thread. + * + * Important note: Only set this to false if you know what you're doing! + * + * The default value is true (the buffer will use atomic operations) + * + * @param buffer Circular buffer + * @param atomic Whether the buffer is atomic (default true) + */ +void TPCircularBufferSetAtomic(TPCircularBuffer *buffer, bool atomic); + +// Reading (consuming) + +/*! + * Access end of buffer + * + * This gives you a pointer to the end of the buffer, ready + * for reading, and the number of available bytes to read. + * + * @param buffer Circular buffer + * @param availableBytes On output, the number of bytes ready for reading + * @return Pointer to the first bytes ready for reading, or NULL if buffer is empty + */ +static __inline__ __attribute__((always_inline)) void* TPCircularBufferTail(TPCircularBuffer *buffer, uint32_t* availableBytes) { + *availableBytes = buffer->fillCount; + if ( *availableBytes == 0 ) return NULL; + return (void*)((char*)buffer->buffer + buffer->tail); +} + +/*! + * Consume bytes in buffer + * + * This frees up the just-read bytes, ready for writing again. + * + * @param buffer Circular buffer + * @param amount Number of bytes to consume + */ +static __inline__ __attribute__((always_inline)) void TPCircularBufferConsume(TPCircularBuffer *buffer, uint32_t amount) { + buffer->tail = (buffer->tail + amount) % buffer->length; + if ( buffer->atomic ) { + atomicFetchAdd(&buffer->fillCount, -amount); + } else { + buffer->fillCount -= amount; + } + assert(buffer->fillCount >= 0); +} + +/*! + * Access front of buffer + * + * This gives you a pointer to the front of the buffer, ready + * for writing, and the number of available bytes to write. + * + * @param buffer Circular buffer + * @param availableBytes On output, the number of bytes ready for writing + * @return Pointer to the first bytes ready for writing, or NULL if buffer is full + */ +static __inline__ __attribute__((always_inline)) void* TPCircularBufferHead(TPCircularBuffer *buffer, uint32_t* availableBytes) { + *availableBytes = (buffer->length - buffer->fillCount); + if ( *availableBytes == 0 ) return NULL; + return (void*)((char*)buffer->buffer + buffer->head); +} + +// Writing (producing) + +/*! + * Produce bytes in buffer + * + * This marks the given section of the buffer ready for reading. + * + * @param buffer Circular buffer + * @param amount Number of bytes to produce + */ +static __inline__ __attribute__((always_inline)) void TPCircularBufferProduce(TPCircularBuffer *buffer, uint32_t amount) { + buffer->head = (buffer->head + amount) % buffer->length; + if ( buffer->atomic ) { + atomicFetchAdd(&buffer->fillCount, amount); + } else { + buffer->fillCount += amount; + } + assert(buffer->fillCount <= buffer->length); +} + +/*! + * Helper routine to copy bytes to buffer + * + * This copies the given bytes to the buffer, and marks them ready for reading. + * + * @param buffer Circular buffer + * @param src Source buffer + * @param len Number of bytes in source buffer + * @return true if bytes copied, false if there was insufficient space + */ +static __inline__ __attribute__((always_inline)) bool TPCircularBufferProduceBytes(TPCircularBuffer *buffer, const void* src, uint32_t len) { + uint32_t space; + void *ptr = TPCircularBufferHead(buffer, &space); + if ( space < len ) return false; + memcpy(ptr, src, len); + TPCircularBufferProduce(buffer, len); + return true; +} + +/*! + * Deprecated method + */ +static __inline__ __attribute__((always_inline)) __deprecated_msg("use TPCircularBufferSetAtomic(false) and TPCircularBufferConsume instead") +void TPCircularBufferConsumeNoBarrier(TPCircularBuffer *buffer, uint32_t amount) { + buffer->tail = (buffer->tail + amount) % buffer->length; + buffer->fillCount -= amount; + assert(buffer->fillCount >= 0); +} + +/*! + * Deprecated method + */ +static __inline__ __attribute__((always_inline)) __deprecated_msg("use TPCircularBufferSetAtomic(false) and TPCircularBufferProduce instead") +void TPCircularBufferProduceNoBarrier(TPCircularBuffer *buffer, uint32_t amount) { + buffer->head = (buffer->head + amount) % buffer->length; + buffer->fillCount += amount; + assert(buffer->fillCount <= buffer->length); +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/framework/Source/TPCircularBuffer.m b/framework/Source/TPCircularBuffer.m new file mode 100755 index 00000000..a3e6b3c5 --- /dev/null +++ b/framework/Source/TPCircularBuffer.m @@ -0,0 +1,149 @@ +// +// TPCircularBuffer.c +// Circular/Ring buffer implementation +// +// https://github.com/michaeltyson/TPCircularBuffer +// +// Created by Michael Tyson on 10/12/2011. +// +// Copyright (C) 2012-2013 A Tasty Pixel +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source distribution. +// + +#include "TPCircularBuffer.h" +#include +#include +#include + +#define reportResult(result,operation) (_reportResult((result),(operation),strrchr(__FILE__, '/')+1,__LINE__)) +static inline bool _reportResult(kern_return_t result, const char *operation, const char* file, int line) { + if ( result != ERR_SUCCESS ) { + printf("%s:%d: %s: %s\n", file, line, operation, mach_error_string(result)); + return false; + } + return true; +} + +bool _TPCircularBufferInit(TPCircularBuffer *buffer, uint32_t length, size_t structSize) { + + assert(length > 0); + + if ( structSize != sizeof(TPCircularBuffer) ) { + fprintf(stderr, "TPCircularBuffer: Header version mismatch. Check for old versions of TPCircularBuffer in your project\n"); + abort(); + } + + // Keep trying until we get our buffer, needed to handle race conditions + int retries = 3; + while ( true ) { + + buffer->length = (uint32_t)round_page(length); // We need whole page sizes + + // Temporarily allocate twice the length, so we have the contiguous address space to + // support a second instance of the buffer directly after + vm_address_t bufferAddress; + kern_return_t result = vm_allocate(mach_task_self(), + &bufferAddress, + buffer->length * 2, + VM_FLAGS_ANYWHERE); // allocate anywhere it'll fit + if ( result != ERR_SUCCESS ) { + if ( retries-- == 0 ) { + reportResult(result, "Buffer allocation"); + return false; + } + // Try again if we fail + continue; + } + + // Now replace the second half of the allocation with a virtual copy of the first half. Deallocate the second half... + result = vm_deallocate(mach_task_self(), + bufferAddress + buffer->length, + buffer->length); + if ( result != ERR_SUCCESS ) { + if ( retries-- == 0 ) { + reportResult(result, "Buffer deallocation"); + return false; + } + // If this fails somehow, deallocate the whole region and try again + vm_deallocate(mach_task_self(), bufferAddress, buffer->length); + continue; + } + + // Re-map the buffer to the address space immediately after the buffer + vm_address_t virtualAddress = bufferAddress + buffer->length; + vm_prot_t cur_prot, max_prot; + result = vm_remap(mach_task_self(), + &virtualAddress, // mirror target + buffer->length, // size of mirror + 0, // auto alignment + 0, // force remapping to virtualAddress + mach_task_self(), // same task + bufferAddress, // mirror source + 0, // MAP READ-WRITE, NOT COPY + &cur_prot, // unused protection struct + &max_prot, // unused protection struct + VM_INHERIT_DEFAULT); + if ( result != ERR_SUCCESS ) { + if ( retries-- == 0 ) { + reportResult(result, "Remap buffer memory"); + return false; + } + // If this remap failed, we hit a race condition, so deallocate and try again + vm_deallocate(mach_task_self(), bufferAddress, buffer->length); + continue; + } + + if ( virtualAddress != bufferAddress+buffer->length ) { + // If the memory is not contiguous, clean up both allocated buffers and try again + if ( retries-- == 0 ) { + printf("Couldn't map buffer memory to end of buffer\n"); + return false; + } + + vm_deallocate(mach_task_self(), virtualAddress, buffer->length); + vm_deallocate(mach_task_self(), bufferAddress, buffer->length); + continue; + } + + buffer->buffer = (void*)bufferAddress; + buffer->fillCount = 0; + buffer->head = buffer->tail = 0; + buffer->atomic = true; + + return true; + } + return false; +} + +void TPCircularBufferCleanup(TPCircularBuffer *buffer) { + vm_deallocate(mach_task_self(), (vm_address_t)buffer->buffer, buffer->length * 2); + memset(buffer, 0, sizeof(TPCircularBuffer)); +} + +void TPCircularBufferClear(TPCircularBuffer *buffer) { + uint32_t fillCount; + if ( TPCircularBufferTail(buffer, &fillCount) ) { + TPCircularBufferConsume(buffer, fillCount); + } +} + +void TPCircularBufferSetAtomic(TPCircularBuffer *buffer, bool atomic) { + buffer->atomic = atomic; +} diff --git a/framework/Source/iOS/Camera.swift b/framework/Source/iOS/Camera.swift index 0c7f4950..95b9a700 100755 --- a/framework/Source/iOS/Camera.swift +++ b/framework/Source/iOS/Camera.swift @@ -1,18 +1,20 @@ import Foundation import AVFoundation -public protocol CameraDelegate { +public protocol CameraDelegate: class { func didCaptureBuffer(_ sampleBuffer: CMSampleBuffer) } public enum PhysicalCameraLocation { case backFacing case frontFacing + case frontFacingMirrored // Documentation: "The front-facing camera would always deliver buffers in AVCaptureVideoOrientationLandscapeLeft and the back-facing camera would always deliver buffers in AVCaptureVideoOrientationLandscapeRight." func imageOrientation() -> ImageOrientation { switch self { case .backFacing: return .landscapeRight case .frontFacing: return .landscapeLeft + case .frontFacingMirrored: return .landscapeLeft } } @@ -20,6 +22,7 @@ public enum PhysicalCameraLocation { switch self { case .backFacing: return .back case .frontFacing: return .front + case .frontFacingMirrored: return .front } } @@ -51,40 +54,41 @@ public class Camera: NSObject, ImageSource, AVCaptureVideoDataOutputSampleBuffer public var audioEncodingTarget:AudioEncodingTarget? { didSet { guard let audioEncodingTarget = audioEncodingTarget else { - self.removeAudioInputsAndOutputs() return } do { try self.addAudioInputsAndOutputs() audioEncodingTarget.activateAudioTrack() } catch { - fatalError("ERROR: Could not connect audio target with error: \(error)") + print("ERROR: Could not connect audio target with error: \(error)") } } } public let targets = TargetContainer() - public var delegate: CameraDelegate? + public weak var delegate: CameraDelegate? public let captureSession:AVCaptureSession - let inputCamera:AVCaptureDevice! - let videoInput:AVCaptureDeviceInput! - let videoOutput:AVCaptureVideoDataOutput! - var microphone:AVCaptureDevice? - var audioInput:AVCaptureDeviceInput? - var audioOutput:AVCaptureAudioDataOutput? + public let inputCamera:AVCaptureDevice! + public let videoInput:AVCaptureDeviceInput! + public let videoOutput:AVCaptureVideoDataOutput! + public var microphone:AVCaptureDevice? + public var audioInput:AVCaptureDeviceInput? + public var audioOutput:AVCaptureAudioDataOutput? var supportsFullYUVRange:Bool = false let captureAsYUV:Bool let yuvConversionShader:ShaderProgram? let frameRenderingSemaphore = DispatchSemaphore(value:1) - let cameraProcessingQueue = DispatchQueue.global(priority:DispatchQueue.GlobalQueuePriority.default) - let audioProcessingQueue = DispatchQueue.global(priority:DispatchQueue.GlobalQueuePriority.default) + let cameraProcessingQueue = DispatchQueue(label:"com.sunsetlakesoftware.GPUImage.cameraProcessingQueue", qos: .default) + let audioProcessingQueue = DispatchQueue(label:"com.sunsetlakesoftware.GPUImage.audioProcessingQueue", qos: .default) let framesToIgnore = 5 var numberOfFramesCaptured = 0 var totalFrameTimeDuringCapture:Double = 0.0 var framesSinceLastCheck = 0 var lastCheckTime = CFAbsoluteTimeGetCurrent() + + var captureSessionRestartAttempts = 0 public init(sessionPreset:String, cameraDevice:AVCaptureDevice? = nil, location:PhysicalCameraLocation = .backFacing, captureAsYUV:Bool = true) throws { @@ -151,21 +155,47 @@ public class Camera: NSObject, ImageSource, AVCaptureVideoDataOutputSampleBuffer captureSession.addOutput(videoOutput) } captureSession.sessionPreset = sessionPreset + + if let connections = videoOutput.connections as? [AVCaptureConnection] { + for connection in connections { + if(connection.isVideoMirroringSupported) { + connection.isVideoMirrored = (location == .frontFacingMirrored) + } + } + } + captureSession.commitConfiguration() super.init() videoOutput.setSampleBufferDelegate(self, queue:cameraProcessingQueue) + + NotificationCenter.default.addObserver(self, selector: #selector(Camera.captureSessionRuntimeError(note:)), name: NSNotification.Name.AVCaptureSessionRuntimeError, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(Camera.captureSessionDidStartRunning(note:)), name: NSNotification.Name.AVCaptureSessionDidStartRunning, object: nil) } deinit { sharedImageProcessingContext.runOperationSynchronously{ self.stopCapture() - self.videoOutput.setSampleBufferDelegate(nil, queue:nil) + self.videoOutput?.setSampleBufferDelegate(nil, queue:nil) self.audioOutput?.setSampleBufferDelegate(nil, queue:nil) } } + func captureSessionRuntimeError(note: NSNotification) { + print("ERROR: Capture session runtime error: \(String(describing: note.userInfo))") + if(self.captureSessionRestartAttempts < 1) { + DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { + self.startCapture() + } + self.captureSessionRestartAttempts += 1 + } + } + + func captureSessionDidStartRunning(note: NSNotification) { + self.captureSessionRestartAttempts = 0 + } + public func captureOutput(_ captureOutput:AVCaptureOutput!, didOutputSampleBuffer sampleBuffer:CMSampleBuffer!, from connection:AVCaptureConnection!) { guard (captureOutput != audioOutput) else { self.processAudioSampleBuffer(sampleBuffer) @@ -287,7 +317,7 @@ public class Camera: NSObject, ImageSource, AVCaptureVideoDataOutputSampleBuffer // MARK: - // MARK: Audio processing - func addAudioInputsAndOutputs() throws { + public func addAudioInputsAndOutputs() throws { guard (audioOutput == nil) else { return } captureSession.beginConfiguration() @@ -306,7 +336,7 @@ public class Camera: NSObject, ImageSource, AVCaptureVideoDataOutputSampleBuffer audioOutput?.setSampleBufferDelegate(self, queue:audioProcessingQueue) } - func removeAudioInputsAndOutputs() { + public func removeAudioInputsAndOutputs() { guard (audioOutput != nil) else { return } captureSession.beginConfiguration() @@ -319,6 +349,6 @@ public class Camera: NSObject, ImageSource, AVCaptureVideoDataOutputSampleBuffer } func processAudioSampleBuffer(_ sampleBuffer:CMSampleBuffer) { - self.audioEncodingTarget?.processAudioBuffer(sampleBuffer) + self.audioEncodingTarget?.processAudioBuffer(sampleBuffer, shouldInvalidateSampleWhenDone: false) } } diff --git a/framework/Source/iOS/MovieInput.swift b/framework/Source/iOS/MovieInput.swift old mode 100644 new mode 100755 index 8db20754..a10dc4ac --- a/framework/Source/iOS/MovieInput.swift +++ b/framework/Source/iOS/MovieInput.swift @@ -1,140 +1,351 @@ import AVFoundation +public protocol MovieInputDelegate: class { + func didFinishMovie() +} + public class MovieInput: ImageSource { public let targets = TargetContainer() public var runBenchmark = false + public weak var delegate: MovieInputDelegate? + + public var audioEncodingTarget:AudioEncodingTarget? { + didSet { + guard let audioEncodingTarget = audioEncodingTarget else { + return + } + audioEncodingTarget.activateAudioTrack() + + // Call enableSynchronizedEncoding() again if they didn't set the audioEncodingTarget before setting synchronizedMovieOutput. + if(synchronizedMovieOutput != nil) { self.enableSynchronizedEncoding() } + } + } + let yuvConversionShader:ShaderProgram let asset:AVAsset - let assetReader:AVAssetReader - let playAtActualSpeed:Bool - let loop:Bool - var videoEncodingIsFinished = false - var previousFrameTime = kCMTimeZero - var previousActualFrameTime = CFAbsoluteTimeGetCurrent() - - var numberOfFramesCaptured = 0 + let videoComposition:AVVideoComposition? + var playAtActualSpeed:Bool + + // Time in the video where it should start. + var requestedStartTime:CMTime? + // Time in the video where it started. + var startTime:CMTime? + // Time according to device clock when the video started. + var actualStartTime:DispatchTime? + // Last sample time that played. + private(set) public var currentTime:CMTime? + + public var loop:Bool + + // Called after the video finishes. Not called when cancel() or pause() is called. + public var completion: (() -> Void)? + // Progress block of the video with a paramater value of 0-1. + // Can be used to check video encoding progress. Not called from main thread. + public var progress: ((Double) -> Void)? + + public var synchronizedMovieOutput:MovieOutput? { + didSet { + self.enableSynchronizedEncoding() + } + } + public var synchronizedEncodingDebug = false { + didSet { + self.synchronizedMovieOutput?.synchronizedEncodingDebug = self.synchronizedEncodingDebug + } + } + let conditionLock = NSCondition() + var readingShouldWait = false + var videoInputStatusObserver:NSKeyValueObservation? + var audioInputStatusObserver:NSKeyValueObservation? + + public var useRealtimeThreads = false + var timebaseInfo = mach_timebase_info_data_t() + var currentThread:Thread? + + var totalFramesSent = 0 var totalFrameTimeDuringCapture:Double = 0.0 - - // TODO: Add movie reader synchronization + + var audioSettings:[String:Any]? + + var movieFramebuffer:Framebuffer? + // TODO: Someone will have to add back in the AVPlayerItem logic, because I don't know how that works - public init(asset:AVAsset, playAtActualSpeed:Bool = false, loop:Bool = false) throws { + public init(asset:AVAsset, videoComposition: AVVideoComposition?, playAtActualSpeed:Bool = false, loop:Bool = false, audioSettings:[String:Any]? = nil) throws { self.asset = asset + self.videoComposition = videoComposition self.playAtActualSpeed = playAtActualSpeed self.loop = loop self.yuvConversionShader = crashOnShaderCompileFailure("MovieInput"){try sharedImageProcessingContext.programForVertexShader(defaultVertexShaderForInputs(2), fragmentShader:YUVConversionFullRangeFragmentShader)} - - assetReader = try AVAssetReader(asset:self.asset) - - let outputSettings:[String:AnyObject] = [(kCVPixelBufferPixelFormatTypeKey as String):NSNumber(value:Int32(kCVPixelFormatType_420YpCbCr8BiPlanarFullRange))] - let readerVideoTrackOutput = AVAssetReaderTrackOutput(track:self.asset.tracks(withMediaType: AVMediaTypeVideo)[0], outputSettings:outputSettings) - readerVideoTrackOutput.alwaysCopiesSampleData = false - assetReader.add(readerVideoTrackOutput) - // TODO: Audio here + self.audioSettings = audioSettings } - public convenience init(url:URL, playAtActualSpeed:Bool = false, loop:Bool = false) throws { + public convenience init(url:URL, playAtActualSpeed:Bool = false, loop:Bool = false, audioSettings:[String:Any]? = nil) throws { let inputOptions = [AVURLAssetPreferPreciseDurationAndTimingKey:NSNumber(value:true)] let inputAsset = AVURLAsset(url:url, options:inputOptions) - try self.init(asset:inputAsset, playAtActualSpeed:playAtActualSpeed, loop:loop) + try self.init(asset:inputAsset, videoComposition: nil, playAtActualSpeed:playAtActualSpeed, loop:loop, audioSettings:audioSettings) + } + + deinit { + self.movieFramebuffer?.unlock() + self.cancel() + + self.videoInputStatusObserver?.invalidate() + self.audioInputStatusObserver?.invalidate() } // MARK: - // MARK: Playback control - - public func start() { - asset.loadValuesAsynchronously(forKeys:["tracks"], completionHandler:{ - DispatchQueue.global(priority:DispatchQueue.GlobalQueuePriority.default).async(execute: { - guard (self.asset.statusOfValue(forKey: "tracks", error:nil) == .loaded) else { return } - - guard self.assetReader.startReading() else { - print("Couldn't start reading") - return - } - - var readerVideoTrackOutput:AVAssetReaderOutput? = nil; - - for output in self.assetReader.outputs { - if(output.mediaType == AVMediaTypeVideo) { - readerVideoTrackOutput = output; - } - } - - while (self.assetReader.status == .reading) { - self.readNextVideoFrame(from:readerVideoTrackOutput!) - } - - if (self.assetReader.status == .completed) { - self.assetReader.cancelReading() - - if (self.loop) { - // TODO: Restart movie processing - } else { - self.endProcessing() - } - } - }) - }) + + public func start(atTime: CMTime) { + self.requestedStartTime = atTime + self.start() + } + + @objc public func start() { + if let currentThread = self.currentThread, + currentThread.isExecuting, + !currentThread.isCancelled { + // If the current thread is running and has not been cancelled, bail. + return + } + // Cancel the thread just to be safe in the event we somehow get here with the thread still running. + self.currentThread?.cancel() + + self.currentThread = Thread(target: self, selector: #selector(beginReading), object: nil) + self.currentThread?.start() } public func cancel() { - assetReader.cancelReading() - self.endProcessing() + self.currentThread?.cancel() + self.currentThread = nil } - func endProcessing() { - + public func pause() { + self.cancel() + self.requestedStartTime = self.currentTime } // MARK: - // MARK: Internal processing functions - func readNextVideoFrame(from videoTrackOutput:AVAssetReaderOutput) { - if ((assetReader.status == .reading) && !videoEncodingIsFinished) { - if let sampleBuffer = videoTrackOutput.copyNextSampleBuffer() { - if (playAtActualSpeed) { - // Do this outside of the video processing queue to not slow that down while waiting - let currentSampleTime = CMSampleBufferGetOutputPresentationTimeStamp(sampleBuffer) - let differenceFromLastFrame = CMTimeSubtract(currentSampleTime, previousFrameTime) - let currentActualTime = CFAbsoluteTimeGetCurrent() - - let frameTimeDifference = CMTimeGetSeconds(differenceFromLastFrame) - let actualTimeDifference = currentActualTime - previousActualFrameTime - - if (frameTimeDifference > actualTimeDifference) { - usleep(UInt32(round(1000000.0 * (frameTimeDifference - actualTimeDifference)))) - } - - previousFrameTime = currentSampleTime - previousActualFrameTime = CFAbsoluteTimeGetCurrent() + func createReader() -> AVAssetReader? + { + do { + let outputSettings:[String:AnyObject] = + [(kCVPixelBufferPixelFormatTypeKey as String):NSNumber(value:Int32(kCVPixelFormatType_420YpCbCr8BiPlanarFullRange))] + + let assetReader = try AVAssetReader.init(asset: self.asset) + + if(self.videoComposition == nil) { + let readerVideoTrackOutput = AVAssetReaderTrackOutput(track: self.asset.tracks(withMediaType: AVMediaTypeVideo).first!, outputSettings:outputSettings) + readerVideoTrackOutput.alwaysCopiesSampleData = false + assetReader.add(readerVideoTrackOutput) + } + else { + let readerVideoTrackOutput = AVAssetReaderVideoCompositionOutput(videoTracks: self.asset.tracks(withMediaType: AVMediaTypeVideo), videoSettings: outputSettings) + readerVideoTrackOutput.videoComposition = self.videoComposition + readerVideoTrackOutput.alwaysCopiesSampleData = false + assetReader.add(readerVideoTrackOutput) + } + + if let audioTrack = self.asset.tracks(withMediaType: AVMediaTypeAudio).first, + let _ = self.audioEncodingTarget { + let readerAudioTrackOutput = AVAssetReaderTrackOutput(track: audioTrack, outputSettings: audioSettings) + readerAudioTrackOutput.alwaysCopiesSampleData = false + assetReader.add(readerAudioTrackOutput) + } + + self.startTime = self.requestedStartTime + if let requestedStartTime = self.requestedStartTime { + assetReader.timeRange = CMTimeRange(start: requestedStartTime, duration: kCMTimePositiveInfinity) + } + self.requestedStartTime = nil + self.currentTime = nil + self.actualStartTime = nil + + return assetReader + } catch { + print("ERROR: Unable to create asset reader: \(error)") + } + return nil + } + + @objc func beginReading() { + let thread = Thread.current + + mach_timebase_info(&timebaseInfo) + + if(useRealtimeThreads) { + self.configureThread() + } + else if(playAtActualSpeed) { + thread.qualityOfService = .userInitiated + } + else { + // This includes synchronized encoding since the above vars will be disabled for it. + thread.qualityOfService = .default + } + + guard let assetReader = self.createReader() else { + return // A return statement in this frame will end thread execution. + } + + do { + try NSObject.catchException { + guard assetReader.startReading() else { + print("ERROR: Unable to start reading: \(String(describing: assetReader.error))") + return } - - sharedImageProcessingContext.runOperationSynchronously{ - self.process(movieFrame:sampleBuffer) - CMSampleBufferInvalidate(sampleBuffer) + } + } + catch { + print("ERROR: Unable to start reading: \(error)") + return + } + + var readerVideoTrackOutput:AVAssetReaderOutput? = nil + var readerAudioTrackOutput:AVAssetReaderOutput? = nil + + for output in assetReader.outputs { + if(output.mediaType == AVMediaTypeVideo) { + readerVideoTrackOutput = output + } + if(output.mediaType == AVMediaTypeAudio) { + readerAudioTrackOutput = output + } + } + + while(assetReader.status == .reading) { + if(thread.isCancelled) { break } + + if let movieOutput = self.synchronizedMovieOutput { + self.conditionLock.lock() + if(self.readingShouldWait) { + self.synchronizedEncodingDebugPrint("Disable reading") + self.conditionLock.wait() + self.synchronizedEncodingDebugPrint("Enable reading") + } + self.conditionLock.unlock() + + if(movieOutput.assetWriterVideoInput.isReadyForMoreMediaData) { + self.readNextVideoFrame(with: assetReader, from: readerVideoTrackOutput!) } - } else { - if (!loop) { - videoEncodingIsFinished = true - if (videoEncodingIsFinished) { - self.endProcessing() + if(movieOutput.assetWriterAudioInput?.isReadyForMoreMediaData ?? false) { + if let readerAudioTrackOutput = readerAudioTrackOutput { + self.readNextAudioSample(with: assetReader, from: readerAudioTrackOutput) } } } + else { + self.readNextVideoFrame(with: assetReader, from: readerVideoTrackOutput!) + if let readerAudioTrackOutput = readerAudioTrackOutput, + self.audioEncodingTarget?.readyForNextAudioBuffer() ?? true { + self.readNextAudioSample(with: assetReader, from: readerAudioTrackOutput) + } + } + } + + assetReader.cancelReading() + + // Since only the main thread will cancel and create threads jump onto it to prevent + // the current thread from being cancelled in between the below if statement and creating the new thread. + DispatchQueue.main.async { + // Start the video over so long as it wasn't cancelled. + if (self.loop && !thread.isCancelled) { + self.currentThread = Thread(target: self, selector: #selector(self.beginReading), object: nil) + self.currentThread?.start() + } + else { + self.delegate?.didFinishMovie() + self.completion?() + + self.synchronizedEncodingDebugPrint("MovieInput finished reading") + self.synchronizedEncodingDebugPrint("MovieInput total frames sent: \(self.totalFramesSent)") + } } -// else if (synchronizedMovieWriter != nil) { -// if (assetReader.status == .Completed) { -// self.endProcessing() -// } -// } - + } + + func readNextVideoFrame(with assetReader: AVAssetReader, from videoTrackOutput:AVAssetReaderOutput) { + guard let sampleBuffer = videoTrackOutput.copyNextSampleBuffer() else { + if let movieOutput = self.synchronizedMovieOutput { + movieOutput.movieProcessingContext.runOperationAsynchronously { + // Documentation: "Clients that are monitoring each input's readyForMoreMediaData value must call markAsFinished on an input when they are done + // appending buffers to it. This is necessary to prevent other inputs from stalling, as they may otherwise wait forever + // for that input's media data, attempting to complete the ideal interleaving pattern." + movieOutput.videoEncodingIsFinished = true + movieOutput.assetWriterVideoInput.markAsFinished() + } + } + return + } + + self.synchronizedEncodingDebugPrint("Process frame input") + + var currentSampleTime = CMSampleBufferGetOutputPresentationTimeStamp(sampleBuffer) + var duration = self.asset.duration // Only used for the progress block so its acuracy is not critical + + self.currentTime = currentSampleTime + + if let startTime = self.startTime { + // Make sure our samples start at kCMTimeZero if the video was started midway. + currentSampleTime = CMTimeSubtract(currentSampleTime, startTime) + duration = CMTimeSubtract(duration, startTime) + } + + if (self.playAtActualSpeed) { + let currentSampleTimeNanoseconds = Int64(currentSampleTime.seconds * 1_000_000_000) + let currentActualTime = DispatchTime.now() + + if(self.actualStartTime == nil) { self.actualStartTime = currentActualTime } + + // Determine how much time we need to wait in order to display the frame at the right currentActualTime such that it will match the currentSampleTime. + // The reason we subtract the actualStartTime from the currentActualTime is so the actual time starts at zero relative to the video start. + let delay = currentSampleTimeNanoseconds - Int64(currentActualTime.uptimeNanoseconds-self.actualStartTime!.uptimeNanoseconds) + + //print("currentSampleTime: \(currentSampleTimeNanoseconds) currentTime: \((currentActualTime.uptimeNanoseconds-self.actualStartTime!.uptimeNanoseconds)) delay: \(delay)") + + if(delay > 0) { + mach_wait_until(mach_absolute_time()+self.nanosToAbs(UInt64(delay))) + } + else { + // This only happens if we aren't given enough processing time for playback + // but is necessary otherwise the playback will never catch up to its timeline. + // If we weren't adhearing to the sample timline and used the old timing method + // the video would still lag during an event like this. + //print("Dropping frame in order to catch up") + return + } + } + + self.progress?(currentSampleTime.seconds/duration.seconds) + + sharedImageProcessingContext.runOperationSynchronously{ + self.process(movieFrame:sampleBuffer) + CMSampleBufferInvalidate(sampleBuffer) + } + } + + func readNextAudioSample(with assetReader: AVAssetReader, from audioTrackOutput:AVAssetReaderOutput) { + guard let sampleBuffer = audioTrackOutput.copyNextSampleBuffer() else { + if let movieOutput = self.synchronizedMovieOutput { + movieOutput.movieProcessingContext.runOperationAsynchronously { + movieOutput.audioEncodingIsFinished = true + movieOutput.assetWriterAudioInput?.markAsFinished() + } + } + return + } + + self.synchronizedEncodingDebugPrint("Process audio sample input") + + self.audioEncodingTarget?.processAudioBuffer(sampleBuffer, shouldInvalidateSampleWhenDone: true) } func process(movieFrame frame:CMSampleBuffer) { let currentSampleTime = CMSampleBufferGetOutputPresentationTimeStamp(frame) let movieFrame = CMSampleBufferGetImageBuffer(frame)! - -// processingFrameTime = currentSampleTime + self.process(movieFrame:movieFrame, withSampleTime:currentSampleTime) } @@ -142,51 +353,204 @@ public class MovieInput: ImageSource { let bufferHeight = CVPixelBufferGetHeight(movieFrame) let bufferWidth = CVPixelBufferGetWidth(movieFrame) CVPixelBufferLockBaseAddress(movieFrame, CVPixelBufferLockFlags(rawValue:CVOptionFlags(0))) - + let conversionMatrix = colorConversionMatrix601FullRangeDefault // TODO: Get this color query working -// if let colorAttachments = CVBufferGetAttachment(movieFrame, kCVImageBufferYCbCrMatrixKey, nil) { -// if(CFStringCompare(colorAttachments, kCVImageBufferYCbCrMatrix_ITU_R_601_4, 0) == .EqualTo) { -// _preferredConversion = kColorConversion601FullRange -// } else { -// _preferredConversion = kColorConversion709 -// } -// } else { -// _preferredConversion = kColorConversion601FullRange -// } + // if let colorAttachments = CVBufferGetAttachment(movieFrame, kCVImageBufferYCbCrMatrixKey, nil) { + // if(CFStringCompare(colorAttachments, kCVImageBufferYCbCrMatrix_ITU_R_601_4, 0) == .EqualTo) { + // _preferredConversion = kColorConversion601FullRange + // } else { + // _preferredConversion = kColorConversion709 + // } + // } else { + // _preferredConversion = kColorConversion601FullRange + // } let startTime = CFAbsoluteTimeGetCurrent() - - let luminanceFramebuffer = sharedImageProcessingContext.framebufferCache.requestFramebufferWithProperties(orientation:.portrait, size:GLSize(width:GLint(bufferWidth), height:GLint(bufferHeight)), textureOnly:true) - luminanceFramebuffer.lock() + + var luminanceGLTexture: CVOpenGLESTexture? + glActiveTexture(GLenum(GL_TEXTURE0)) - glBindTexture(GLenum(GL_TEXTURE_2D), luminanceFramebuffer.texture) - glTexImage2D(GLenum(GL_TEXTURE_2D), 0, GL_LUMINANCE, GLsizei(bufferWidth), GLsizei(bufferHeight), 0, GLenum(GL_LUMINANCE), GLenum(GL_UNSIGNED_BYTE), CVPixelBufferGetBaseAddressOfPlane(movieFrame, 0)) - let chrominanceFramebuffer = sharedImageProcessingContext.framebufferCache.requestFramebufferWithProperties(orientation:.portrait, size:GLSize(width:GLint(bufferWidth), height:GLint(bufferHeight)), textureOnly:true) - chrominanceFramebuffer.lock() + let luminanceGLTextureResult = CVOpenGLESTextureCacheCreateTextureFromImage(kCFAllocatorDefault, sharedImageProcessingContext.coreVideoTextureCache, movieFrame, nil, GLenum(GL_TEXTURE_2D), GL_LUMINANCE, GLsizei(bufferWidth), GLsizei(bufferHeight), GLenum(GL_LUMINANCE), GLenum(GL_UNSIGNED_BYTE), 0, &luminanceGLTexture) + + if(luminanceGLTextureResult != kCVReturnSuccess || luminanceGLTexture == nil) { + print("Could not create LuminanceGLTexture") + return + } + + let luminanceTexture = CVOpenGLESTextureGetName(luminanceGLTexture!) + + glBindTexture(GLenum(GL_TEXTURE_2D), luminanceTexture) + glTexParameterf(GLenum(GL_TEXTURE_2D), GLenum(GL_TEXTURE_WRAP_S), GLfloat(GL_CLAMP_TO_EDGE)); + glTexParameterf(GLenum(GL_TEXTURE_2D), GLenum(GL_TEXTURE_WRAP_T), GLfloat(GL_CLAMP_TO_EDGE)); + + let luminanceFramebuffer: Framebuffer + do { + luminanceFramebuffer = try Framebuffer(context: sharedImageProcessingContext, orientation: .portrait, size: GLSize(width:GLint(bufferWidth), height:GLint(bufferHeight)), textureOnly: true, overriddenTexture: luminanceTexture) + } catch { + print("Could not create a framebuffer of the size (\(bufferWidth), \(bufferHeight)), error: \(error)") + return + } + + var chrominanceGLTexture: CVOpenGLESTexture? + glActiveTexture(GLenum(GL_TEXTURE1)) - glBindTexture(GLenum(GL_TEXTURE_2D), chrominanceFramebuffer.texture) - glTexImage2D(GLenum(GL_TEXTURE_2D), 0, GL_LUMINANCE_ALPHA, GLsizei(bufferWidth / 2), GLsizei(bufferHeight / 2), 0, GLenum(GL_LUMINANCE_ALPHA), GLenum(GL_UNSIGNED_BYTE), CVPixelBufferGetBaseAddressOfPlane(movieFrame, 1)) + let chrominanceGLTextureResult = CVOpenGLESTextureCacheCreateTextureFromImage(kCFAllocatorDefault, sharedImageProcessingContext.coreVideoTextureCache, movieFrame, nil, GLenum(GL_TEXTURE_2D), GL_LUMINANCE_ALPHA, GLsizei(bufferWidth / 2), GLsizei(bufferHeight / 2), GLenum(GL_LUMINANCE_ALPHA), GLenum(GL_UNSIGNED_BYTE), 1, &chrominanceGLTexture) + + if(chrominanceGLTextureResult != kCVReturnSuccess || chrominanceGLTexture == nil) { + print("Could not create ChrominanceGLTexture") + return + } + + let chrominanceTexture = CVOpenGLESTextureGetName(chrominanceGLTexture!) + + glBindTexture(GLenum(GL_TEXTURE_2D), chrominanceTexture) + glTexParameterf(GLenum(GL_TEXTURE_2D), GLenum(GL_TEXTURE_WRAP_S), GLfloat(GL_CLAMP_TO_EDGE)); + glTexParameterf(GLenum(GL_TEXTURE_2D), GLenum(GL_TEXTURE_WRAP_T), GLfloat(GL_CLAMP_TO_EDGE)); + + let chrominanceFramebuffer: Framebuffer + do { + chrominanceFramebuffer = try Framebuffer(context: sharedImageProcessingContext, orientation: .portrait, size: GLSize(width:GLint(bufferWidth), height:GLint(bufferHeight)), textureOnly: true, overriddenTexture: chrominanceTexture) + } catch { + print("Could not create a framebuffer of the size (\(bufferWidth), \(bufferHeight)), error: \(error)") + return + } + + self.movieFramebuffer?.unlock() let movieFramebuffer = sharedImageProcessingContext.framebufferCache.requestFramebufferWithProperties(orientation:.portrait, size:GLSize(width:GLint(bufferWidth), height:GLint(bufferHeight)), textureOnly:false) + movieFramebuffer.lock() convertYUVToRGB(shader:self.yuvConversionShader, luminanceFramebuffer:luminanceFramebuffer, chrominanceFramebuffer:chrominanceFramebuffer, resultFramebuffer:movieFramebuffer, colorConversionMatrix:conversionMatrix) CVPixelBufferUnlockBaseAddress(movieFrame, CVPixelBufferLockFlags(rawValue:CVOptionFlags(0))) - + movieFramebuffer.timingStyle = .videoFrame(timestamp:Timestamp(withSampleTime)) + self.movieFramebuffer = movieFramebuffer + self.updateTargetsWithFramebuffer(movieFramebuffer) + if(self.runBenchmark || self.synchronizedEncodingDebug) { + self.totalFramesSent += 1 + } + if self.runBenchmark { let currentFrameTime = (CFAbsoluteTimeGetCurrent() - startTime) - self.numberOfFramesCaptured += 1 self.totalFrameTimeDuringCapture += currentFrameTime - print("Average frame time : \(1000.0 * self.totalFrameTimeDuringCapture / Double(self.numberOfFramesCaptured)) ms") + print("Average frame time : \(1000.0 * self.totalFrameTimeDuringCapture / Double(self.totalFramesSent)) ms") print("Current frame time : \(1000.0 * currentFrameTime) ms") } } - + public func transmitPreviousImage(to target:ImageConsumer, atIndex:UInt) { // Not needed for movie inputs } + + public func transmitPreviousFrame() { + sharedImageProcessingContext.runOperationAsynchronously { + if let movieFramebuffer = self.movieFramebuffer { + self.updateTargetsWithFramebuffer(movieFramebuffer) + } + } + } + + // MARK: - + // MARK: Synchronized encoding + + func enableSynchronizedEncoding() { + self.synchronizedMovieOutput?.encodingLiveVideo = false + self.synchronizedMovieOutput?.synchronizedEncodingDebug = self.synchronizedEncodingDebug + self.playAtActualSpeed = false + self.loop = false + + // Subscribe to isReadyForMoreMediaData changes + self.setupObservers() + // Set the intial state of the lock + self.updateLock() + } + + func setupObservers() { + self.videoInputStatusObserver?.invalidate() + self.audioInputStatusObserver?.invalidate() + + guard let movieOutput = self.synchronizedMovieOutput else { return } + + self.videoInputStatusObserver = movieOutput.assetWriterVideoInput.observe(\.isReadyForMoreMediaData, options: [.new, .old]) { [weak self] (assetWriterVideoInput, change) in + guard let weakSelf = self else { return } + weakSelf.updateLock() + } + self.audioInputStatusObserver = movieOutput.assetWriterAudioInput?.observe(\.isReadyForMoreMediaData, options: [.new, .old]) { [weak self] (assetWriterAudioInput, change) in + guard let weakSelf = self else { return } + weakSelf.updateLock() + } + } + + func updateLock() { + guard let movieOutput = self.synchronizedMovieOutput else { return } + + self.conditionLock.lock() + // Allow reading if either input is able to accept data, prevent reading if both inputs are unable to accept data. + if(movieOutput.assetWriterVideoInput.isReadyForMoreMediaData || movieOutput.assetWriterAudioInput?.isReadyForMoreMediaData ?? false) { + self.readingShouldWait = false + self.conditionLock.signal() + } + else { + self.readingShouldWait = true + } + self.conditionLock.unlock() + } + + // MARK: - + // MARK: Thread configuration + + func configureThread() { + let clock2abs = Double(timebaseInfo.denom) / Double(timebaseInfo.numer) * Double(NSEC_PER_MSEC) + + // http://docs.huihoo.com/darwin/kernel-programming-guide/scheduler/chapter_8_section_4.html + // + // To see the impact of adjusting these values, uncomment the print statement above mach_wait_until() in self.readNextVideoFrame() + // + // Setup for 5 ms of work. + // The anticpated frame render duration is in the 1-3 ms range on an iPhone 6 for 1080p without filters and 1-7 ms range with filters + // If the render duration is allowed to exceed 16ms (the duration of a frame in 60fps video) + // the 60fps video will no longer be playing in real time. + let computation = UInt32(5 * clock2abs) + // Tell the scheduler the next 20 ms of work needs to be done as soon as possible. + let period = UInt32(0 * clock2abs) + // According to the above scheduling chapter this constraint only appears relevant + // if preemtible is set to true and the period is not 0. If this is wrong, please let me know. + let constraint = UInt32(5 * clock2abs) + + //print("period: \(period) computation: \(computation) constraint: \(constraint)") + + let THREAD_TIME_CONSTRAINT_POLICY_COUNT = mach_msg_type_number_t(MemoryLayout.size / MemoryLayout.size) + + var policy = thread_time_constraint_policy() + var ret: Int32 + let thread: thread_port_t = pthread_mach_thread_np(pthread_self()) + + policy.period = period + policy.computation = computation + policy.constraint = constraint + policy.preemptible = 0 + + ret = withUnsafeMutablePointer(to: &policy) { + $0.withMemoryRebound(to: integer_t.self, capacity: Int(THREAD_TIME_CONSTRAINT_POLICY_COUNT)) { + thread_policy_set(thread, UInt32(THREAD_TIME_CONSTRAINT_POLICY), $0, THREAD_TIME_CONSTRAINT_POLICY_COUNT) + } + } + + if ret != KERN_SUCCESS { + mach_error("thread_policy_set:", ret) + print("Unable to configure thread") + } + } + + func nanosToAbs(_ nanos: UInt64) -> UInt64 { + return nanos * UInt64(timebaseInfo.denom) / UInt64(timebaseInfo.numer) + } + + func synchronizedEncodingDebugPrint(_ string: String) { + if(synchronizedMovieOutput != nil && synchronizedEncodingDebug) { print(string) } + } } diff --git a/framework/Source/iOS/MovieOutput.swift b/framework/Source/iOS/MovieOutput.swift old mode 100644 new mode 100755 index 03bc9593..bef4aaa4 --- a/framework/Source/iOS/MovieOutput.swift +++ b/framework/Source/iOS/MovieOutput.swift @@ -2,214 +2,365 @@ import AVFoundation public protocol AudioEncodingTarget { func activateAudioTrack() - func processAudioBuffer(_ sampleBuffer:CMSampleBuffer) + func processAudioBuffer(_ sampleBuffer:CMSampleBuffer, shouldInvalidateSampleWhenDone:Bool) + // Note: This is not used for synchronized encoding. + func readyForNextAudioBuffer() -> Bool +} + +public enum MovieOutputError: Error, CustomStringConvertible { + case startWritingError(assetWriterError: Error?) + case pixelBufferPoolNilError + + public var errorDescription: String { + switch self { + case .startWritingError(let assetWriterError): + return "Could not start asset writer: \(String(describing: assetWriterError))" + case .pixelBufferPoolNilError: + return "Asset writer pixel buffer pool was nil. Make sure that your output file doesn't already exist." + } + } + + public var description: String { + return "<\(type(of: self)): errorDescription = \(self.errorDescription)>" + } } public class MovieOutput: ImageConsumer, AudioEncodingTarget { + public let sources = SourceContainer() public let maximumInputs:UInt = 1 let assetWriter:AVAssetWriter let assetWriterVideoInput:AVAssetWriterInput var assetWriterAudioInput:AVAssetWriterInput? - + let assetWriterPixelBufferInput:AVAssetWriterInputPixelBufferAdaptor let size:Size let colorSwizzlingShader:ShaderProgram private var isRecording = false - private var videoEncodingIsFinished = false - private var audioEncodingIsFinished = false - private var startTime:CMTime? - private var previousFrameTime = kCMTimeNegativeInfinity - private var previousAudioTime = kCMTimeNegativeInfinity - private var encodingLiveVideo:Bool + var videoEncodingIsFinished = false + var audioEncodingIsFinished = false + private var previousFrameTime: CMTime? + var encodingLiveVideo:Bool { + didSet { + assetWriterVideoInput.expectsMediaDataInRealTime = encodingLiveVideo + assetWriterAudioInput?.expectsMediaDataInRealTime = encodingLiveVideo + } + } var pixelBuffer:CVPixelBuffer? = nil var renderFramebuffer:Framebuffer! - public init(URL:Foundation.URL, size:Size, fileType:String = AVFileTypeQuickTimeMovie, liveVideo:Bool = false, settings:[String:AnyObject]? = nil) throws { - if sharedImageProcessingContext.supportsTextureCaches() { - self.colorSwizzlingShader = sharedImageProcessingContext.passthroughShader + var audioSettings:[String:Any]? = nil + var audioSourceFormatHint:CMFormatDescription? + + let movieProcessingContext:OpenGLContext + + var synchronizedEncodingDebug = false + var totalFramesAppended:Int = 0 + + public init(URL:Foundation.URL, size:Size, fileType:String = AVFileTypeQuickTimeMovie, liveVideo:Bool = false, videoSettings:[String:Any]? = nil, videoNaturalTimeScale:CMTimeScale? = nil, audioSettings:[String:Any]? = nil, audioSourceFormatHint:CMFormatDescription? = nil) throws { + imageProcessingShareGroup = sharedImageProcessingContext.context.sharegroup + let movieProcessingContext = OpenGLContext() + + if movieProcessingContext.supportsTextureCaches() { + self.colorSwizzlingShader = movieProcessingContext.passthroughShader } else { - self.colorSwizzlingShader = crashOnShaderCompileFailure("MovieOutput"){try sharedImageProcessingContext.programForVertexShader(defaultVertexShaderForInputs(1), fragmentShader:ColorSwizzlingFragmentShader)} + self.colorSwizzlingShader = crashOnShaderCompileFailure("MovieOutput"){try movieProcessingContext.programForVertexShader(defaultVertexShaderForInputs(1), fragmentShader:ColorSwizzlingFragmentShader)} } self.size = size + assetWriter = try AVAssetWriter(url:URL, fileType:fileType) - // Set this to make sure that a functional movie is produced, even if the recording is cut off mid-stream. Only the last second should be lost in that case. - assetWriter.movieFragmentInterval = CMTimeMakeWithSeconds(1.0, 1000) - var localSettings:[String:AnyObject] - if let settings = settings { - localSettings = settings + var localSettings:[String:Any] + if let videoSettings = videoSettings { + localSettings = videoSettings } else { - localSettings = [String:AnyObject]() + localSettings = [String:Any]() } - localSettings[AVVideoWidthKey] = localSettings[AVVideoWidthKey] ?? NSNumber(value:size.width) - localSettings[AVVideoHeightKey] = localSettings[AVVideoHeightKey] ?? NSNumber(value:size.height) - localSettings[AVVideoCodecKey] = localSettings[AVVideoCodecKey] ?? AVVideoCodecH264 as NSString + localSettings[AVVideoWidthKey] = localSettings[AVVideoWidthKey] ?? size.width + localSettings[AVVideoHeightKey] = localSettings[AVVideoHeightKey] ?? size.height + localSettings[AVVideoCodecKey] = localSettings[AVVideoCodecKey] ?? AVVideoCodecH264 assetWriterVideoInput = AVAssetWriterInput(mediaType:AVMediaTypeVideo, outputSettings:localSettings) assetWriterVideoInput.expectsMediaDataInRealTime = liveVideo + + // You should provide a naturalTimeScale if you have one for the current media. + // Otherwise the asset writer will choose one for you and it may result in misaligned frames. + if let naturalTimeScale = videoNaturalTimeScale { + assetWriter.movieTimeScale = naturalTimeScale + assetWriterVideoInput.mediaTimeScale = naturalTimeScale + // This is set to make sure that a functional movie is produced, even if the recording is cut off mid-stream. Only the last second should be lost in that case. + assetWriter.movieFragmentInterval = CMTimeMakeWithSeconds(1, naturalTimeScale) + } + else { + assetWriter.movieFragmentInterval = CMTimeMakeWithSeconds(1, 1000) + } + encodingLiveVideo = liveVideo // You need to use BGRA for the video in order to get realtime encoding. I use a color-swizzling shader to line up glReadPixels' normal RGBA output with the movie input's BGRA. - let sourcePixelBufferAttributesDictionary:[String:AnyObject] = [kCVPixelBufferPixelFormatTypeKey as String:NSNumber(value:Int32(kCVPixelFormatType_32BGRA)), - kCVPixelBufferWidthKey as String:NSNumber(value:size.width), - kCVPixelBufferHeightKey as String:NSNumber(value:size.height)] + let sourcePixelBufferAttributesDictionary:[String:Any] = [kCVPixelBufferPixelFormatTypeKey as String:Int32(kCVPixelFormatType_32BGRA), + kCVPixelBufferWidthKey as String:self.size.width, + kCVPixelBufferHeightKey as String:self.size.height] assetWriterPixelBufferInput = AVAssetWriterInputPixelBufferAdaptor(assetWriterInput:assetWriterVideoInput, sourcePixelBufferAttributes:sourcePixelBufferAttributesDictionary) assetWriter.add(assetWriterVideoInput) + + self.audioSettings = audioSettings + self.audioSourceFormatHint = audioSourceFormatHint + + self.movieProcessingContext = movieProcessingContext + } + + public func startRecording(_ completionCallback:((_ started: Bool, _ error: Error?) -> Void)? = nil) { + // Don't do this work on the movieProcessingContext queue so we don't block it. + // If it does get blocked framebuffers will pile up from live video and after it is no longer blocked (this work has finished) + // we will be able to accept framebuffers but the ones that piled up will come in too quickly resulting in most being dropped. + DispatchQueue.global(qos: .utility).async { + do { + var success = false + try NSObject.catchException { + success = self.assetWriter.startWriting() + } + + if(!success) { + throw MovieOutputError.startWritingError(assetWriterError: self.assetWriter.error) + } + + guard self.assetWriterPixelBufferInput.pixelBufferPool != nil else { + /* + When the pixelBufferPool returns nil, check the following: + 1. the the output file of the AVAssetsWriter doesn't exist. + 2. use the pixelbuffer after calling startSessionAtTime: on the AVAssetsWriter. + 3. the settings of AVAssetWriterInput and AVAssetWriterInputPixelBufferAdaptor are correct. + 4. the present times of appendPixelBuffer uses are not the same. + https://stackoverflow.com/a/20110179/1275014 + */ + throw MovieOutputError.pixelBufferPoolNilError + } + + self.isRecording = true + + self.synchronizedEncodingDebugPrint("MovieOutput started writing") + + completionCallback?(true, nil) + } catch { + self.assetWriter.cancelWriting() + + completionCallback?(false, error) + } + } } - public func startRecording() { - startTime = nil - sharedImageProcessingContext.runOperationSynchronously{ - self.isRecording = self.assetWriter.startWriting() + public func finishRecording(_ completionCallback:(() -> Void)? = nil) { + movieProcessingContext.runOperationAsynchronously{ + guard self.isRecording, + self.assetWriter.status == .writing else { + completionCallback?() + return + } - CVPixelBufferPoolCreatePixelBuffer(nil, self.assetWriterPixelBufferInput.pixelBufferPool!, &self.pixelBuffer) + self.audioEncodingIsFinished = true + self.videoEncodingIsFinished = true - /* AVAssetWriter will use BT.601 conversion matrix for RGB to YCbCr conversion - * regardless of the kCVImageBufferYCbCrMatrixKey value. - * Tagging the resulting video file as BT.601, is the best option right now. - * Creating a proper BT.709 video is not possible at the moment. - */ - CVBufferSetAttachment(self.pixelBuffer!, kCVImageBufferColorPrimariesKey, kCVImageBufferColorPrimaries_ITU_R_709_2, .shouldPropagate) - CVBufferSetAttachment(self.pixelBuffer!, kCVImageBufferYCbCrMatrixKey, kCVImageBufferYCbCrMatrix_ITU_R_601_4, .shouldPropagate) - CVBufferSetAttachment(self.pixelBuffer!, kCVImageBufferTransferFunctionKey, kCVImageBufferTransferFunction_ITU_R_709_2, .shouldPropagate) + self.isRecording = false - let bufferSize = GLSize(self.size) - var cachedTextureRef:CVOpenGLESTexture? = nil - let _ = CVOpenGLESTextureCacheCreateTextureFromImage(kCFAllocatorDefault, sharedImageProcessingContext.coreVideoTextureCache, self.pixelBuffer!, nil, GLenum(GL_TEXTURE_2D), GL_RGBA, bufferSize.width, bufferSize.height, GLenum(GL_BGRA), GLenum(GL_UNSIGNED_BYTE), 0, &cachedTextureRef) - let cachedTexture = CVOpenGLESTextureGetName(cachedTextureRef!) + if let lastFrame = self.previousFrameTime { + // Resolve black frames at the end. Without this the end timestamp of the session's samples could be either video or audio. + // Documentation: "You do not need to call this method; if you call finishWriting without + // calling this method, the session's effective end time will be the latest end timestamp of + // the session's samples (that is, no samples will be edited out at the end)." + self.assetWriter.endSession(atSourceTime: lastFrame) + } - self.renderFramebuffer = try! Framebuffer(context:sharedImageProcessingContext, orientation:.portrait, size:bufferSize, textureOnly:false, overriddenTexture:cachedTexture) + self.assetWriter.finishWriting { + completionCallback?() + } + self.synchronizedEncodingDebugPrint("MovieOutput finished writing") + self.synchronizedEncodingDebugPrint("MovieOutput total frames appended: \(self.totalFramesAppended)") } } - public func finishRecording(_ completionCallback:(() -> Void)? = nil) { - sharedImageProcessingContext.runOperationSynchronously{ - self.isRecording = false + public func newFramebufferAvailable(_ framebuffer:Framebuffer, fromSourceIndex:UInt) { + glFinish(); + + let work = { + guard self.isRecording, + self.assetWriter.status == .writing, + !self.videoEncodingIsFinished else { + self.synchronizedEncodingDebugPrint("Guard fell through, dropping frame") + return + } - if (self.assetWriter.status == .completed || self.assetWriter.status == .cancelled || self.assetWriter.status == .unknown) { - sharedImageProcessingContext.runOperationAsynchronously{ - completionCallback?() - } + // Ignore still images and other non-video updates (do I still need this?) + guard let frameTime = framebuffer.timingStyle.timestamp?.asCMTime else { return } + + // If two consecutive times with the same value are added to the movie, it aborts recording, so I bail on that case. + guard (frameTime != self.previousFrameTime) else { return } + + if (self.previousFrameTime == nil) { + // This resolves black frames at the beginning. Any samples recieved before this time will be edited out. + self.assetWriter.startSession(atSourceTime: frameTime) + } + + self.previousFrameTime = frameTime + + guard (self.assetWriterVideoInput.isReadyForMoreMediaData || !self.encodingLiveVideo) else { + print("Had to drop a frame at time \(frameTime)") return } - if ((self.assetWriter.status == .writing) && (!self.videoEncodingIsFinished)) { - self.videoEncodingIsFinished = true - self.assetWriterVideoInput.markAsFinished() + + while(!self.assetWriterVideoInput.isReadyForMoreMediaData && !self.encodingLiveVideo && !self.videoEncodingIsFinished) { + self.synchronizedEncodingDebugPrint("Video waiting...") + // Better to poll isReadyForMoreMediaData often since when it does become true + // we don't want to risk letting framebuffers pile up in between poll intervals. + usleep(100000) // 0.1 seconds } - if ((self.assetWriter.status == .writing) && (!self.audioEncodingIsFinished)) { - self.audioEncodingIsFinished = true - self.assetWriterAudioInput?.markAsFinished() + + let pixelBufferStatus = CVPixelBufferPoolCreatePixelBuffer(nil, self.assetWriterPixelBufferInput.pixelBufferPool!, &self.pixelBuffer) + guard ((self.pixelBuffer != nil) && (pixelBufferStatus == kCVReturnSuccess)) else { + print("WARNING: Unable to create pixel buffer, dropping frame") + return } - // Why can't I use ?? here for the callback? - if let callback = completionCallback { - self.assetWriter.finishWriting(completionHandler: callback) - } else { - self.assetWriter.finishWriting{} + do { + try self.renderIntoPixelBuffer(self.pixelBuffer!, framebuffer:framebuffer) + + self.synchronizedEncodingDebugPrint("Process frame output") + try NSObject.catchException { + if (!self.assetWriterPixelBufferInput.append(self.pixelBuffer!, withPresentationTime:frameTime)) { + print("WARNING: Trouble appending pixel buffer at time: \(frameTime) \(String(describing: self.assetWriter.error))") + } + } } - } - } - - public func newFramebufferAvailable(_ framebuffer:Framebuffer, fromSourceIndex:UInt) { - defer { - framebuffer.unlock() - } - guard isRecording else { return } - // Ignore still images and other non-video updates (do I still need this?) - guard let frameTime = framebuffer.timingStyle.timestamp?.asCMTime else { return } - // If two consecutive times with the same value are added to the movie, it aborts recording, so I bail on that case - guard (frameTime != previousFrameTime) else { return } - - if (startTime == nil) { - if (assetWriter.status != .writing) { - assetWriter.startWriting() + catch { + print("WARNING: Trouble appending pixel buffer at time: \(frameTime) \(error)") } - assetWriter.startSession(atSourceTime: frameTime) - startTime = frameTime - } - - // TODO: Run the following on an internal movie recording dispatch queue, context - guard (assetWriterVideoInput.isReadyForMoreMediaData || (!encodingLiveVideo)) else { - debugPrint("Had to drop a frame at time \(frameTime)") - return - } - - if !sharedImageProcessingContext.supportsTextureCaches() { - let pixelBufferStatus = CVPixelBufferPoolCreatePixelBuffer(nil, assetWriterPixelBufferInput.pixelBufferPool!, &pixelBuffer) - guard ((pixelBuffer != nil) && (pixelBufferStatus == kCVReturnSuccess)) else { return } + if(self.synchronizedEncodingDebug) { + self.totalFramesAppended += 1 + } + + CVPixelBufferUnlockBaseAddress(self.pixelBuffer!, CVPixelBufferLockFlags(rawValue:CVOptionFlags(0))) + self.pixelBuffer = nil + + sharedImageProcessingContext.runOperationAsynchronously { + framebuffer.unlock() + } } - - renderIntoPixelBuffer(pixelBuffer!, framebuffer:framebuffer) - - if (!assetWriterPixelBufferInput.append(pixelBuffer!, withPresentationTime:frameTime)) { - debugPrint("Problem appending pixel buffer at time: \(frameTime)") + + if(self.encodingLiveVideo) { + // This is done asynchronously to reduce the amount of work done on the sharedImageProcessingContext que + // so we can decrease the risk of frames being dropped by the camera. I believe it is unlikely a backlog of framebuffers will occur + // since the framebuffers come in much slower than during synchronized encoding. + movieProcessingContext.runOperationAsynchronously(work) } - - CVPixelBufferUnlockBaseAddress(pixelBuffer!, CVPixelBufferLockFlags(rawValue:CVOptionFlags(0))) - if !sharedImageProcessingContext.supportsTextureCaches() { - pixelBuffer = nil + else { + // This is done synchronously to prevent framebuffers from piling up during synchronized encoding. + // If we don't force the sharedImageProcessingContext queue to wait for this frame to finish processing it will + // keep sending frames whenever isReadyForMoreMediaData = true but the movieProcessingContext queue would run when the system wants it to. + movieProcessingContext.runOperationSynchronously(work) } } - func renderIntoPixelBuffer(_ pixelBuffer:CVPixelBuffer, framebuffer:Framebuffer) { - if !sharedImageProcessingContext.supportsTextureCaches() { - renderFramebuffer = sharedImageProcessingContext.framebufferCache.requestFramebufferWithProperties(orientation:framebuffer.orientation, size:GLSize(self.size)) - renderFramebuffer.lock() + func renderIntoPixelBuffer(_ pixelBuffer:CVPixelBuffer, framebuffer:Framebuffer) throws { + // Is this the first pixel buffer we have recieved? + if(renderFramebuffer == nil) { + CVBufferSetAttachment(pixelBuffer, kCVImageBufferColorPrimariesKey, kCVImageBufferColorPrimaries_ITU_R_709_2, .shouldPropagate) + CVBufferSetAttachment(pixelBuffer, kCVImageBufferYCbCrMatrixKey, kCVImageBufferYCbCrMatrix_ITU_R_601_4, .shouldPropagate) + CVBufferSetAttachment(pixelBuffer, kCVImageBufferTransferFunctionKey, kCVImageBufferTransferFunction_ITU_R_709_2, .shouldPropagate) } + let bufferSize = GLSize(self.size) + var cachedTextureRef:CVOpenGLESTexture? = nil + let _ = CVOpenGLESTextureCacheCreateTextureFromImage(kCFAllocatorDefault, self.movieProcessingContext.coreVideoTextureCache, pixelBuffer, nil, GLenum(GL_TEXTURE_2D), GL_RGBA, bufferSize.width, bufferSize.height, GLenum(GL_BGRA), GLenum(GL_UNSIGNED_BYTE), 0, &cachedTextureRef) + let cachedTexture = CVOpenGLESTextureGetName(cachedTextureRef!) + + renderFramebuffer = try Framebuffer(context:self.movieProcessingContext, orientation:.portrait, size:bufferSize, textureOnly:false, overriddenTexture:cachedTexture) + renderFramebuffer.activateFramebufferForRendering() clearFramebufferWithColor(Color.black) CVPixelBufferLockBaseAddress(pixelBuffer, CVPixelBufferLockFlags(rawValue:CVOptionFlags(0))) - renderQuadWithShader(colorSwizzlingShader, uniformSettings:ShaderUniformSettings(), vertexBufferObject:sharedImageProcessingContext.standardImageVBO, inputTextures:[framebuffer.texturePropertiesForOutputRotation(.noRotation)]) + renderQuadWithShader(colorSwizzlingShader, uniformSettings:ShaderUniformSettings(), vertexBufferObject:movieProcessingContext.standardImageVBO, inputTextures:[framebuffer.texturePropertiesForOutputRotation(.noRotation)], context: movieProcessingContext) - if sharedImageProcessingContext.supportsTextureCaches() { + if movieProcessingContext.supportsTextureCaches() { glFinish() } else { glReadPixels(0, 0, renderFramebuffer.size.width, renderFramebuffer.size.height, GLenum(GL_RGBA), GLenum(GL_UNSIGNED_BYTE), CVPixelBufferGetBaseAddress(pixelBuffer)) - renderFramebuffer.unlock() } } // MARK: - // MARK: Audio support - + public func activateAudioTrack() { - // TODO: Add ability to set custom output settings - assetWriterAudioInput = AVAssetWriterInput(mediaType:AVMediaTypeAudio, outputSettings:nil) + assetWriterAudioInput = AVAssetWriterInput(mediaType:AVMediaTypeAudio, outputSettings:self.audioSettings, sourceFormatHint:self.audioSourceFormatHint) + assetWriter.add(assetWriterAudioInput!) assetWriterAudioInput?.expectsMediaDataInRealTime = encodingLiveVideo } - public func processAudioBuffer(_ sampleBuffer:CMSampleBuffer) { - guard let assetWriterAudioInput = assetWriterAudioInput else { return } - - sharedImageProcessingContext.runOperationSynchronously{ - let currentSampleTime = CMSampleBufferGetOutputPresentationTimeStamp(sampleBuffer) - if (self.startTime == nil) { - if (self.assetWriter.status != .writing) { - self.assetWriter.startWriting() + public func processAudioBuffer(_ sampleBuffer:CMSampleBuffer, shouldInvalidateSampleWhenDone:Bool) { + let work = { + defer { + if(shouldInvalidateSampleWhenDone) { + CMSampleBufferInvalidate(sampleBuffer) } - - self.assetWriter.startSession(atSourceTime: currentSampleTime) - self.startTime = currentSampleTime } - guard (assetWriterAudioInput.isReadyForMoreMediaData || (!self.encodingLiveVideo)) else { + guard self.isRecording, + self.assetWriter.status == .writing, + !self.audioEncodingIsFinished, + let assetWriterAudioInput = self.assetWriterAudioInput else { + self.synchronizedEncodingDebugPrint("Guard fell through, dropping audio sample") + return + } + + let currentSampleTime = CMSampleBufferGetOutputPresentationTimeStamp(sampleBuffer) + + guard (assetWriterAudioInput.isReadyForMoreMediaData || !self.encodingLiveVideo) else { + print("Had to drop a audio sample at time \(currentSampleTime)") return } - if (!assetWriterAudioInput.append(sampleBuffer)) { - print("Trouble appending audio sample buffer") + while(!assetWriterAudioInput.isReadyForMoreMediaData && !self.encodingLiveVideo && !self.audioEncodingIsFinished) { + self.synchronizedEncodingDebugPrint("Audio waiting...") + usleep(100000) } + + self.synchronizedEncodingDebugPrint("Process audio sample output") + + do { + try NSObject.catchException { + if (!assetWriterAudioInput.append(sampleBuffer)) { + print("WARNING: Trouble appending audio sample buffer: \(String(describing: self.assetWriter.error))") + } + } + } + catch { + print("WARNING: Trouble appending audio sample buffer: \(error)") + } + } + + if(self.encodingLiveVideo) { + movieProcessingContext.runOperationAsynchronously(work) + } + else { + work() } } + + // Note: This is not used for synchronized encoding, only live video. + public func readyForNextAudioBuffer() -> Bool { + return true + } + + func synchronizedEncodingDebugPrint(_ string: String) { + if(synchronizedEncodingDebug && !encodingLiveVideo) { print(string) } + } } diff --git a/framework/Source/iOS/OpenGLContext.swift b/framework/Source/iOS/OpenGLContext.swift index fc5c81d8..dec61982 100755 --- a/framework/Source/iOS/OpenGLContext.swift +++ b/framework/Source/iOS/OpenGLContext.swift @@ -4,6 +4,8 @@ import UIKit // TODO: Find a way to warn people if they set this after the context has been created var imageProcessingShareGroup:EAGLSharegroup? = nil +var dispatchQueKeyValueCounter = 81 + public class OpenGLContext: SerialDispatch { lazy var framebufferCache:FramebufferCache = { return FramebufferCache(context:self) @@ -25,14 +27,17 @@ public class OpenGLContext: SerialDispatch { }() - public let serialDispatchQueue:DispatchQueue = DispatchQueue(label:"com.sunsetlakesoftware.GPUImage.processingQueue", attributes: []) + public let serialDispatchQueue:DispatchQueue = DispatchQueue(label:"com.sunsetlakesoftware.GPUImage.processingQueue", qos: .userInitiated) public let dispatchQueueKey = DispatchSpecificKey() + public let dispatchQueueKeyValue: Int // MARK: - // MARK: Initialization and teardown init() { - serialDispatchQueue.setSpecific(key:dispatchQueueKey, value:81) + dispatchQueueKeyValue = dispatchQueKeyValueCounter + serialDispatchQueue.setSpecific(key:dispatchQueueKey, value:dispatchQueueKeyValue) + dispatchQueKeyValueCounter += 1 let generatedContext:EAGLContext? if let shareGroup = imageProcessingShareGroup { diff --git a/framework/Source/iOS/SpeakerOutput.swift b/framework/Source/iOS/SpeakerOutput.swift new file mode 100755 index 00000000..03e00624 --- /dev/null +++ b/framework/Source/iOS/SpeakerOutput.swift @@ -0,0 +1,328 @@ +// +// SpeakerOutput.swift +// GPUImage +// +// Rewritten by Josh Bernfeld on 3/1/18 +// and originally created by Uzi Refaeli on 3/9/13. +// Copyright (c) 2018 Brad Larson. All rights reserved. +// + +import Foundation +import AudioToolbox +import AVFoundation + +public class SpeakerOutput: AudioEncodingTarget { + + public var changesAudioSession = true + + public private(set) var isPlaying = false + var hasBuffer = false + var isReadyForMoreMediaData = true { + willSet { + guard newValue else { return } + + // When we are ready to begin accepting new data check if we had something + // in the rescue buffer. If we did then move it to the main buffer. + self.copyRescueBufferContentsToCircularBuffer() + } + } + + var processingGraph:AUGraph? + var mixerUnit:AudioUnit? + + var firstBufferReached = false + + let outputBus:AudioUnitElement = 0 + let inputBus:AudioUnitElement = 1 + + let unitSize = UInt32(MemoryLayout.size) + let bufferUnit:UInt32 = 655360 + + var circularBuffer = TPCircularBuffer() + let circularBufferSize:UInt32 + + var rescueBuffer:UnsafeMutableRawPointer? + let rescueBufferSize:Int + var rescueBufferContentsSize:UInt32 = 0 + + + public init() { + circularBufferSize = bufferUnit * unitSize + rescueBufferSize = Int(bufferUnit / 2) + } + + deinit { + if let processingGraph = processingGraph { + DisposeAUGraph(processingGraph) + } + if let rescueBuffer = rescueBuffer { + free(rescueBuffer) + } + TPCircularBufferCleanup(&circularBuffer) + + self.cancel() + } + + // MARK: - + // MARK: Playback control + + public func start() { + if(isPlaying || processingGraph == nil) { return } + + AUGraphStart(processingGraph!) + + isPlaying = true + } + + public func cancel() { + if(!isPlaying || processingGraph == nil) { return } + + AUGraphStop(processingGraph!) + + isPlaying = false + + rescueBufferContentsSize = 0 + TPCircularBufferClear(&circularBuffer) + hasBuffer = false + isReadyForMoreMediaData = true + } + + // MARK: - + // MARK: AudioEncodingTarget protocol + + public func activateAudioTrack() { + if(changesAudioSession) { + do { + try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryAmbient) + try AVAudioSession.sharedInstance().setActive(true) + } + catch { + print("ERROR: Unable to set audio session: \(error)") + } + } + + // Create a new AUGraph + NewAUGraph(&processingGraph) + + // AUNodes represent AudioUnits on the AUGraph and provide an + // easy means for connecting audioUnits together. + var outputNode = AUNode() + var mixerNode = AUNode() + + // Create AudioComponentDescriptions for the AUs we want in the graph mixer component + var mixerDesc = AudioComponentDescription() + mixerDesc.componentType = kAudioUnitType_Mixer + mixerDesc.componentSubType = kAudioUnitSubType_SpatialMixer + mixerDesc.componentFlags = 0 + mixerDesc.componentFlagsMask = 0 + mixerDesc.componentManufacturer = kAudioUnitManufacturer_Apple + + // Output component + var outputDesc = AudioComponentDescription() + outputDesc.componentType = kAudioUnitType_Output + outputDesc.componentSubType = kAudioUnitSubType_RemoteIO + outputDesc.componentFlags = 0 + outputDesc.componentFlagsMask = 0 + outputDesc.componentManufacturer = kAudioUnitManufacturer_Apple + + // Add nodes to the graph to hold our AudioUnits, + // You pass in a reference to the AudioComponentDescription + // and get back an AudioUnit + AUGraphAddNode(processingGraph!, &mixerDesc, &mixerNode) + AUGraphAddNode(processingGraph!, &outputDesc, &outputNode) + + // Now we can manage connections using nodes in the graph. + // Connect the mixer node's output to the output node's input + AUGraphConnectNodeInput(processingGraph!, mixerNode, 0, outputNode, 0) + + // Upon return from this function call, the audio units belonging to the graph are open but not initialized. Specifically, no resource allocation occurs. + AUGraphOpen(processingGraph!) + + // Get a link to the mixer AU so we can talk to it later + AUGraphNodeInfo(processingGraph!, mixerNode, nil, &mixerUnit) + + var elementCount:UInt32 = 1 + AudioUnitSetProperty(mixerUnit!, kAudioUnitProperty_ElementCount, kAudioUnitScope_Input, 0, &elementCount, UInt32(MemoryLayout.size)) + + // Set output callback, this is how audio sample data will be retrieved + var callbackStruct = AURenderCallbackStruct() + callbackStruct.inputProc = playbackCallback + callbackStruct.inputProcRefCon = bridgeObject(self) + AUGraphSetNodeInputCallback(processingGraph!, mixerNode, 0, &callbackStruct) + + // Describe the format, this will get adjusted when the first sample comes in. + var audioFormat = AudioStreamBasicDescription() + audioFormat.mFormatID = kAudioFormatLinearPCM + audioFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked + audioFormat.mSampleRate = 44100.0 + audioFormat.mReserved = 0 + + audioFormat.mBytesPerPacket = 2 + audioFormat.mFramesPerPacket = 1 + audioFormat.mBytesPerFrame = 2 + audioFormat.mChannelsPerFrame = 1 + audioFormat.mBitsPerChannel = 16 + + // Apply the format + AudioUnitSetProperty(mixerUnit!, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, outputBus, &audioFormat, UInt32(MemoryLayout.size)) + + // Initialize the processing graph + AUGraphInitialize(processingGraph!) + + circularBuffer = TPCircularBuffer() + + // Initialize the circular buffer + _TPCircularBufferInit(&circularBuffer, circularBufferSize, MemoryLayout.size) + + hasBuffer = false + } + + public func processAudioBuffer(_ sampleBuffer: CMSampleBuffer, shouldInvalidateSampleWhenDone: Bool) { + defer { + if(shouldInvalidateSampleWhenDone) { + CMSampleBufferInvalidate(sampleBuffer) + } + } + + if(!isReadyForMoreMediaData || !isPlaying) { return } + + if(!firstBufferReached) { + firstBufferReached = true + // Get the format information of the sample + let desc = CMSampleBufferGetFormatDescription(sampleBuffer)! + let basicDesc = CMAudioFormatDescriptionGetStreamBasicDescription(desc)! + + var oSize = UInt32(MemoryLayout.size) + // Retrieve the existing set audio format + var audioFormat = AudioStreamBasicDescription() + AudioUnitGetProperty(mixerUnit!, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, outputBus, &audioFormat, &oSize) + + // Update the audio format with the information we have from the sample + audioFormat.mSampleRate = basicDesc.pointee.mSampleRate + + audioFormat.mBytesPerPacket = basicDesc.pointee.mBytesPerPacket + audioFormat.mFramesPerPacket = basicDesc.pointee.mFramesPerPacket + audioFormat.mBytesPerFrame = basicDesc.pointee.mBytesPerFrame + audioFormat.mChannelsPerFrame = basicDesc.pointee.mChannelsPerFrame + audioFormat.mBitsPerChannel = basicDesc.pointee.mBitsPerChannel + + // Apply the format + AudioUnitSetProperty(mixerUnit!, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, outputBus, &audioFormat, UInt32(MemoryLayout.size)) + AUGraphUpdate(processingGraph!, nil) + } + + // Populate an AudioBufferList with the sample + var audioBufferList = AudioBufferList() + var blockBuffer:CMBlockBuffer? + CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer(sampleBuffer, nil, &audioBufferList, MemoryLayout.size, nil, nil, kCMSampleBufferFlag_AudioBufferList_Assure16ByteAlignment, &blockBuffer) + + // This is actually doing audioBufferList.mBuffers[0] + // Since the struct has an array of length of 1 the compiler is interpreting + // it as a single item array and not letting us use the above line. + // Since the array pointer points to the first item of the c array + // and all we want is the first item this is equally fine. + let audioBuffer = audioBufferList.mBuffers + + // Place the AudioBufferList in the circular buffer + let sampleSize = UInt32(CMSampleBufferGetTotalSampleSize(sampleBuffer)) + let didCopyBytes = TPCircularBufferProduceBytes(&circularBuffer, audioBuffer.mData, sampleSize) + + // The circular buffer has not been proceseed quickly enough and has filled up. + // Disable reading any further samples and save this last buffer so we don't lose it. + if(!didCopyBytes) { + //print("TPCircularBuffer limit reached: \(sampleSize) Bytes") + + isReadyForMoreMediaData = false + + self.writeToRescueBuffer(audioBuffer.mData, sampleSize) + } + else { + hasBuffer = true + } + } + + public func readyForNextAudioBuffer() -> Bool { + return isReadyForMoreMediaData + } + + // MARK: - + // MARK: Rescue buffer + + func writeToRescueBuffer(_ src: UnsafeRawPointer!, _ size: UInt32) { + if(rescueBufferContentsSize > 0) { + print("WARNING: Writing to rescue buffer with contents already inside") + } + + if(size > rescueBufferSize) { + print("WARNING: Unable to allocate enought space for rescue buffer, dropping audio sample") + } + else { + if(rescueBuffer == nil) { + rescueBuffer = malloc(rescueBufferSize) + } + + rescueBufferContentsSize = size + memcpy(rescueBuffer!, src, Int(size)) + } + } + + func copyRescueBufferContentsToCircularBuffer() { + if(rescueBufferContentsSize > 0) { + let didCopyBytes = TPCircularBufferProduceBytes(&circularBuffer, rescueBuffer, rescueBufferContentsSize) + if(!didCopyBytes) { + print("WARNING: Unable to copy rescue buffer into main buffer, dropping audio sample") + } + rescueBufferContentsSize = 0 + } + } +} + +func playbackCallback( + inRefCon:UnsafeMutableRawPointer, + ioActionFlags:UnsafeMutablePointer, + inTimeStamp:UnsafePointer, + inBusNumber:UInt32, + inNumberFrames:UInt32, + ioData:UnsafeMutablePointer?) -> OSStatus { + + let audioBuffer = ioData!.pointee.mBuffers + let numberOfChannels = audioBuffer.mNumberChannels + let outSamples = audioBuffer.mData + + // Zero-out all of the output samples first + memset(outSamples, 0, Int(audioBuffer.mDataByteSize)) + + let p = bridgeRawPointer(inRefCon) as! SpeakerOutput + + if(p.hasBuffer && p.isPlaying) { + var availableBytes:UInt32 = 0 + let bufferTail = TPCircularBufferTail(&p.circularBuffer, &availableBytes) + + let requestedBytesSize = inNumberFrames * p.unitSize * numberOfChannels + + let bytesToRead = min(availableBytes, requestedBytesSize) + // Copy the bytes from the circular buffer into the outSample + memcpy(outSamples, bufferTail, Int(bytesToRead)) + // Clear what we just read out of the circular buffer + TPCircularBufferConsume(&p.circularBuffer, bytesToRead) + + if(availableBytes <= requestedBytesSize*2) { + p.isReadyForMoreMediaData = true + } + + if(availableBytes <= requestedBytesSize) { + p.hasBuffer = false + } + } + + return noErr +} + +func bridgeObject(_ obj : AnyObject) -> UnsafeMutableRawPointer { + return UnsafeMutableRawPointer(Unmanaged.passUnretained(obj).toOpaque()) +} + +func bridgeRawPointer(_ ptr : UnsafeMutableRawPointer) -> AnyObject { + return Unmanaged.fromOpaque(ptr).takeUnretainedValue() +} + From 89edf60346882fb9b4b89f98d78154191c5f5f0b Mon Sep 17 00:00:00 2001 From: Josh Bernfeld Date: Mon, 2 Apr 2018 21:31:16 -0700 Subject: [PATCH 2/3] Fix memory leak --- README.md | 2 ++ .../SimpleMovieEncoding/ViewController.swift | 3 +++ 2 files changed, 5 insertions(+) diff --git a/README.md b/README.md index b8763eb1..0421cc3c 100755 --- a/README.md +++ b/README.md @@ -278,6 +278,8 @@ movieInput --> filter --> movieOutput movieInput.completion = { self.movieOutput.finishRecording { + self.movieInput.audioEncodingTarget = nil + self.movieInput.synchronizedMovieOutput = nil print("Encoding finished") } } diff --git a/examples/iOS/SimpleMovieEncoding/SimpleMovieEncoding/ViewController.swift b/examples/iOS/SimpleMovieEncoding/SimpleMovieEncoding/ViewController.swift index 3ddf2d5a..ce027377 100755 --- a/examples/iOS/SimpleMovieEncoding/SimpleMovieEncoding/ViewController.swift +++ b/examples/iOS/SimpleMovieEncoding/SimpleMovieEncoding/ViewController.swift @@ -100,6 +100,9 @@ class ViewController: UIViewController { movieInput.completion = { self.movieOutput.finishRecording { + self.movieInput.audioEncodingTarget = nil + self.movieInput.synchronizedMovieOutput = nil + DispatchQueue.main.async { print("Encoding finished") } From 88807b09e266188b90b7066eb00bd9d0f5478e99 Mon Sep 17 00:00:00 2001 From: Josh Bernfeld Date: Thu, 10 May 2018 14:00:41 -0700 Subject: [PATCH 3/3] Add mute support to SpeakerOutput --- framework/Source/iOS/SpeakerOutput.swift | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) mode change 100755 => 100644 framework/Source/iOS/SpeakerOutput.swift diff --git a/framework/Source/iOS/SpeakerOutput.swift b/framework/Source/iOS/SpeakerOutput.swift old mode 100755 new mode 100644 index 03e00624..499d4763 --- a/framework/Source/iOS/SpeakerOutput.swift +++ b/framework/Source/iOS/SpeakerOutput.swift @@ -16,6 +16,9 @@ public class SpeakerOutput: AudioEncodingTarget { public var changesAudioSession = true public private(set) var isPlaying = false + + public var isMuted = false + var hasBuffer = false var isReadyForMoreMediaData = true { willSet { @@ -301,8 +304,10 @@ func playbackCallback( let requestedBytesSize = inNumberFrames * p.unitSize * numberOfChannels let bytesToRead = min(availableBytes, requestedBytesSize) - // Copy the bytes from the circular buffer into the outSample - memcpy(outSamples, bufferTail, Int(bytesToRead)) + if(!p.isMuted) { + // Copy the bytes from the circular buffer into the outSample + memcpy(outSamples, bufferTail, Int(bytesToRead)) + } // Clear what we just read out of the circular buffer TPCircularBufferConsume(&p.circularBuffer, bytesToRead)