5 Commits

Author SHA1 Message Date
AFathi b05e744dc6 minor performance improvements 2019-01-12 21:32:35 -05:00
AFathi c51fc0db85 iPhone X+ fixes 2018-12-17 12:02:01 -05:00
AFathi 61415aa9aa iPhone X+ fixes 2018-12-17 12:01:35 -05:00
AFathi b89bd27d59 few changes 2018-12-17 11:58:01 -05:00
Ahmed Bekhit 6037aa9619 Update README.md 2018-09-20 12:03:54 -04:00
18 changed files with 94 additions and 181 deletions
Executable
+1
View File
@@ -0,0 +1 @@
echo "4.0" > .swift-version
+1 -1
View File
@@ -5,7 +5,7 @@ Pod::Spec.new do |s|
s.description = "Enabling developers to capture videos 📹, photos 🌄, Live Photos 🎇, and GIFs 🎆 with augmented reality components."
s.homepage = "https://github.com/AFathi/ARVideoKit"
s.screenshots = "http://www.ahmedbekhit.com/SK_PREV.gif", "http://www.ahmedbekhit.com/SCN_PREVIEW.gif"
s.swift_version = '4.2'
s.license = { :type => "Apache 2.0", :file => "LICENSE" }
+2 -14
View File
@@ -7,7 +7,6 @@
objects = {
/* Begin PBXBuildFile section */
B466A8B82279E34C00BD7070 /* WeakProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = B466A8B72279E34C00BD7070 /* WeakProxy.swift */; };
FB2E36891FAE29C00035B8D6 /* LICENSE in Resources */ = {isa = PBXBuildFile; fileRef = FB2E36881FAE29BF0035B8D6 /* LICENSE */; };
FB404FFE20D72A190056EA1D /* JPEG.swift in Sources */ = {isa = PBXBuildFile; fileRef = FB404FFD20D72A190056EA1D /* JPEG.swift */; };
FBD604DF1FA969DD00EC9804 /* ARVideoKit.h in Headers */ = {isa = PBXBuildFile; fileRef = FBD604DD1FA969DD00EC9804 /* ARVideoKit.h */; settings = {ATTRIBUTES = (Public, ); }; };
@@ -34,7 +33,6 @@
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
B466A8B72279E34C00BD7070 /* WeakProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeakProxy.swift; sourceTree = "<group>"; };
FB2E36881FAE29BF0035B8D6 /* LICENSE */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = LICENSE; sourceTree = "<group>"; };
FB404FFD20D72A190056EA1D /* JPEG.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JPEG.swift; sourceTree = "<group>"; };
FBD604DA1FA969DD00EC9804 /* ARVideoKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ARVideoKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -73,14 +71,6 @@
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
B466A8B62279E33800BD7070 /* Utils */ = {
isa = PBXGroup;
children = (
B466A8B72279E34C00BD7070 /* WeakProxy.swift */,
);
path = Utils;
sourceTree = "<group>";
};
FBA0AA0A1FAD9D9D006C481B /* Writer */ = {
isa = PBXGroup;
children = (
@@ -109,7 +99,6 @@
FBD604DC1FA969DD00EC9804 /* ARVideoKit */ = {
isa = PBXGroup;
children = (
B466A8B62279E33800BD7070 /* Utils */,
FBD604E51FA96ACD00EC9804 /* Assets */,
FBD604E61FA96AD400EC9804 /* Enumerations */,
FBD604E71FA96AE500EC9804 /* Extensions */,
@@ -301,7 +290,6 @@
FBD604F51FA96B3300EC9804 /* UIImage+VideoBuffer.swift in Sources */,
FBD605111FA96BA100EC9804 /* ARView.swift in Sources */,
FBD604F61FA96B3300EC9804 /* CGImage+Resize.swift in Sources */,
B466A8B82279E34C00BD7070 /* WeakProxy.swift in Sources */,
FBD604F91FA96B3300EC9804 /* UIViewController+hasType.swift in Sources */,
FBD605141FA96BA100EC9804 /* ViewAR.swift in Sources */,
FBD604EE1FA96B2700EC9804 /* ARVideoOptions.swift in Sources */,
@@ -462,7 +450,7 @@
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
SKIP_INSTALL = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 4.2;
SWIFT_VERSION = 4.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
@@ -487,7 +475,7 @@
PRODUCT_BUNDLE_IDENTIFIER = com.ahmedbekhit.ARVideoKit;
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
SKIP_INSTALL = YES;
SWIFT_VERSION = 4.2;
SWIFT_VERSION = 4.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;
+1 -1
View File
@@ -17,7 +17,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.32</string>
<string>1.31</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSPrincipalClass</key>
@@ -28,25 +28,12 @@ import ARKit
- parameter noError: A boolean that returns true when the recorder ends without errors. Otherwise, it returns false.
*/
func recorder(didEndRecording path: URL, with noError: Bool)
/**
A protocol method that is triggered when a recorder fails recording.
- parameter error: An `Error` object that returns the error value.
- parameter status: A string that returns the reason of the recorder failure in a string literal format.
*/
func recorder(didFailRecording error: Error?, and status: String)
/**
A protocol method that is triggered when a recorder is cancelled.
- parameter status: A string that returns the reason the of recorder cancelation in a string literal format.
*/
@objc optional func recorder(didCancelRecording status: String)
/**
A protocol method that is triggered when a recorder is modified.
- parameter duration: A double that returns the duration of current recording
*/
@objc optional func recorder(didUpdateRecording duration: TimeInterval)
/**
A protocol method that is triggered when the application will resign active.
@@ -26,11 +26,11 @@ class LivePhotoGenerator {
generator.appliesPreferredTrackTransform = true
//retrieves the key photo frame from the middle of the video asset
let time = NSValue(time: CMTimeMultiplyByFloat64(asset.duration, multiplier: 0.5))
let time = NSValue(time: CMTimeMultiplyByFloat64(asset.duration, 0.5))
//generates the key photo CGImage asynchronously
generator.generateCGImagesAsynchronously(forTimes: [time], completionHandler: { _, image, _, _, _ in
if let cgImg = image, let imgData = UIImage(cgImage: cgImg).pngData() {
if let cgImg = image, let imgData = UIImagePNGRepresentation(UIImage(cgImage: cgImg)) {
do {
self.keyPhotoPath = self.newPath(for: true, and: false)
try imgData.write(to: self.keyPhotoPath!, options: [.atomic])
@@ -16,7 +16,7 @@ class QuickTimeMov {
private let kKeyStillImageTime = "com.apple.quicktime.still-image-time"
private let kKeySpaceQuickTimeMetadata = "mdta"
private let path: String
private let dummyTimeRange = CMTimeRange(start: CMTime(value: 0, timescale: 1000), duration: CMTime(value: 200, timescale: 3000))
private let dummyTimeRange = CMTimeRangeMake(CMTimeMake(0, 1000), CMTimeMake(200, 3000))
private lazy var asset: AVURLAsset = {
let url = URL(fileURLWithPath: self.path)
@@ -128,7 +128,7 @@ class QuickTimeMov {
// --------------------------------------------------
writer.startWriting()
reader.startReading()
writer.startSession(atSourceTime: CMTime.zero)
writer.startSession(atSourceTime: kCMTimeZero)
// write metadata track
adapter.append(AVTimedMetadataGroup(items: [metadataForStillImageTime()],
@@ -148,7 +148,7 @@ class QuickTimeMov {
input.markAsFinished()
if reader.status == .completed && aAudioAsset.tracks.count > 1 {
audioReader?.startReading()
writer.startSession(atSourceTime: CMTime.zero)
writer.startSession(atSourceTime: kCMTimeZero)
let media_queue = DispatchQueue(label: "assetAudioWriterQueue", attributes: [])
audioWriterInput?.requestMediaDataWhenReady(on: media_queue) {
while (audioWriterInput?.isReadyForMoreMediaData)! {
@@ -218,9 +218,7 @@ class QuickTimeMov {
"com.apple.metadata.datatype.int8" ]
var desc: CMFormatDescription? = nil
CMMetadataFormatDescriptionCreateWithMetadataSpecifications(allocator: kCFAllocatorDefault, metadataType: kCMMetadataFormatType_Boxed, metadataSpecifications: [spec] as CFArray, formatDescriptionOut: &desc)
// CMFormatDescription.createForMetadata(allocator: kCFAllocatorDefault, metadataType: kCMMetadataFormatType_Boxed, metadataSpecifications: [spec] as CFArray, formatDescriptionOut: &desc)
CMMetadataFormatDescriptionCreateWithMetadataSpecifications(kCFAllocatorDefault, kCMMetadataFormatType_Boxed, [spec] as CFArray, &desc)
let input = AVAssetWriterInput(mediaType: AVMediaType.metadata,
outputSettings: nil, sourceFormatHint: desc)
return AVAssetWriterInputMetadataAdaptor(assetWriterInput: input)
@@ -18,7 +18,7 @@ import Photos
* [Email](mailto:me@ahmedbekhit.com)
*/
@available(iOS 9.1, *)
@objc public class PHLivePhotoPlus: NSObject {
@objc public class PHLivePhotoPlus: PHLivePhoto {
var pairedVideoPath: URL?
var keyPhotoPath: URL?
+5 -2
View File
@@ -9,10 +9,11 @@
import Foundation
import ARKit
private var view: Any?
private var renderEngine: SCNRenderer!
@available(iOS 11.0, *)
struct RenderAR {
private var view: Any?
private var renderEngine: SCNRenderer!
var ARcontentMode: ARFrameMode!
init(_ ARview: Any?, renderer: SCNRenderer, contentMode: ARFrameMode) {
@@ -92,7 +93,9 @@ struct RenderAR {
} else {
renderedFrame = renderEngine.snapshot(atTime: time, with: size, antialiasingMode: .none)
}
guard let buffer = renderedFrame!.buffer else { return nil }
return buffer
} else if view is ARSKView {
guard let size = bufferSize else { return nil }
+39 -32
View File
@@ -25,7 +25,7 @@ class WritAR: NSObject, AVCaptureAudioDataOutputSampleBufferDelegate {
private var isRecording: Bool = false
weak var delegate: RecordARDelegate?
var delegate: RecordARDelegate?
var videoInputOrientation: ARVideoOrientation = .auto
init(output: URL, width: Int, height: Int, adjustForSharing: Bool, audioEnabled: Bool, orientaions:[ARInputViewOrientation], queue: DispatchQueue, allowMix: Bool) {
@@ -33,13 +33,13 @@ class WritAR: NSObject, AVCaptureAudioDataOutputSampleBufferDelegate {
do {
assetWriter = try AVAssetWriter(outputURL: output, fileType: AVFileType.mp4)
} catch {
// FIXME: handle when failed to allocate AVAssetWriter.
return
fatalError("An error occurred while intializing an AVAssetWriter")
}
if audioEnabled {
if allowMix {
let audioOptions: AVAudioSession.CategoryOptions = [.mixWithOthers , .allowBluetooth, .defaultToSpeaker, .interruptSpokenAudioAndMixWithOthers]
try? AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category.playAndRecord, mode: AVAudioSession.Mode.spokenAudio, options: audioOptions)
let audioOptions: AVAudioSessionCategoryOptions = [.mixWithOthers , .allowBluetooth, .defaultToSpeaker, .interruptSpokenAudioAndMixWithOthers]
try? AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayAndRecord, with: audioOptions)
try? AVAudioSession.sharedInstance().setActive(true)
}
AVAudioSession.sharedInstance().requestRecordPermission({ permitted in
@@ -57,15 +57,15 @@ class WritAR: NSObject, AVCaptureAudioDataOutputSampleBufferDelegate {
AVVideoHeightKey: height as AnyObject
]
let attributes: [String: Bool] = [
kCVPixelBufferCGImageCompatibilityKey as String: true,
kCVPixelBufferCGBitmapContextCompatibilityKey as String: true
]
// let attributes: [String: Bool] = [
// kCVPixelBufferCGImageCompatibilityKey as String: true,
// kCVPixelBufferCGBitmapContextCompatibilityKey as String: true
// ]
videoInput = AVAssetWriterInput(mediaType: .video, outputSettings: videoOutputSettings)
videoInput.expectsMediaDataInRealTime = true
pixelBufferInput = AVAssetWriterInputPixelBufferAdaptor(assetWriterInput: videoInput, sourcePixelBufferAttributes: nil)
var angleEnabled: Bool {
for v in orientaions {
if UIDevice.current.orientation.rawValue == v.rawValue {
@@ -141,6 +141,7 @@ class WritAR: NSObject, AVCaptureAudioDataOutputSampleBufferDelegate {
if session.canAddInput(audioDeviceInput!) {
session.addInput(audioDeviceInput!)
}
if session.canAddOutput(audioDataOutput) {
session.addOutput(audioDataOutput)
}
@@ -162,11 +163,34 @@ class WritAR: NSObject, AVCaptureAudioDataOutputSampleBufferDelegate {
var startingVideoTime: CMTime?
var isWritingWithoutError: Bool?
var currentDuration: TimeInterval = 0 // Seconds
func insert(pixel buffer: CVPixelBuffer, with intervals: CFTimeInterval) {
let time: CMTime = CMTime(seconds: intervals, preferredTimescale: 1000000)
insert(pixel: buffer, with: time)
let time: CMTime = CMTimeMakeWithSeconds(intervals, 1000000)
if assetWriter.status == .unknown {
guard startingVideoTime == nil else {
isWritingWithoutError = false
return
}
startingVideoTime = time
if assetWriter.startWriting() {
assetWriter.startSession(atSourceTime: startingVideoTime!)
session.startRunning()
isRecording = true
isWritingWithoutError = true
} else {
delegate?.recorder(didFailRecording: assetWriter.error, and: "An error occurred while starting the video session.")
isWritingWithoutError = false
}
} else if assetWriter.status == .failed {
delegate?.recorder(didFailRecording: assetWriter.error, and: "Video session failed while recording.")
logAR.message("An error occurred while recording the video, status: \(assetWriter.status.rawValue), error: \(assetWriter.error!.localizedDescription)")
isWritingWithoutError = false
return
}
if videoInput.isReadyForMoreMediaData {
append(pixel: buffer, with: time)
isWritingWithoutError = true
}
}
func insert(pixel buffer: CVPixelBuffer, with time: CMTime) {
@@ -178,19 +202,16 @@ class WritAR: NSObject, AVCaptureAudioDataOutputSampleBufferDelegate {
startingVideoTime = time
if assetWriter.startWriting() {
assetWriter.startSession(atSourceTime: startingVideoTime!)
currentDuration = 0
isRecording = true
isWritingWithoutError = true
} else {
delegate?.recorder(didFailRecording: assetWriter.error, and: "An error occurred while starting the video session.")
currentDuration = 0
isRecording = false
isWritingWithoutError = false
}
} else if assetWriter.status == .failed {
delegate?.recorder(didFailRecording: assetWriter.error, and: "Video session failed while recording.")
logAR.message("An error occurred while recording the video, status: \(assetWriter.status.rawValue), error: \(assetWriter.error!.localizedDescription)")
currentDuration = 0
isRecording = false
isWritingWithoutError = false
return
@@ -198,10 +219,8 @@ class WritAR: NSObject, AVCaptureAudioDataOutputSampleBufferDelegate {
if videoInput.isReadyForMoreMediaData {
append(pixel: buffer, with: time)
currentDuration = time.seconds - startingVideoTime!.seconds
isRecording = true
isWritingWithoutError = true
delegate?.recorder?(didUpdateRecording: currentDuration)
}
}
@@ -219,25 +238,13 @@ class WritAR: NSObject, AVCaptureAudioDataOutputSampleBufferDelegate {
isRecording = false
}
func end(writing finished: @escaping () -> Void) {
func end(writing finished: @escaping () -> Void){
if let session = session {
if session.isRunning {
session.stopRunning()
}
}
if assetWriter.status == .writing {
assetWriter.finishWriting(completionHandler: finished)
}
}
func cancel() {
if let session = session {
if session.isRunning {
session.stopRunning()
}
}
assetWriter.cancelWriting()
assetWriter.finishWriting(completionHandler: finished)
}
}
+4 -4
View File
@@ -20,7 +20,7 @@ import ARKit
*/
@available(iOS 11.0, *)
@objc public class ARView: NSObject {
private weak var parentVC: UIViewController?
private var parentVC: UIViewController?
private var recentAngle = 0
private var inputViewOrientation:[ARInputViewOrientation] = []
@@ -52,7 +52,7 @@ import ARKit
@objc init?(ARSceneKit: ARSCNView) {
super.init()
NotificationCenter.default.addObserver(self, selector: #selector(deviceDidRotate), name: UIDevice.orientationDidChangeNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(deviceDidRotate), name: NSNotification.Name.UIDeviceOrientationDidChange, object: nil)
let value = UIInterfaceOrientation.portrait.rawValue
UIDevice.current.setValue(value, forKey: "orientation")
@@ -68,7 +68,7 @@ import ARKit
@objc init?(ARSpriteKit: ARSKView) {
super.init()
NotificationCenter.default.addObserver(self, selector: #selector(deviceDidRotate), name: UIDevice.orientationDidChangeNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(deviceDidRotate), name: NSNotification.Name.UIDeviceOrientationDidChange, object: nil)
let value = UIInterfaceOrientation.portrait.rawValue
UIDevice.current.setValue(value, forKey: "orientation")
@@ -82,7 +82,7 @@ import ARKit
@objc init?(SceneKit: SCNView) {
super.init()
NotificationCenter.default.addObserver(self, selector: #selector(deviceDidRotate), name: UIDevice.orientationDidChangeNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(deviceDidRotate), name: NSNotification.Name.UIDeviceOrientationDidChange, object: nil)
let value = UIInterfaceOrientation.portrait.rawValue
UIDevice.current.setValue(value, forKey: "orientation")
+22 -66
View File
@@ -12,6 +12,13 @@ import ARKit
import Photos
import PhotosUI
private var view: Any?
private var renderEngine: SCNRenderer!
private var gpuLoop: CADisplayLink!
private var isResting = false
private var ARcontentMode: ARFrameMode!
@available(iOS 11.0, *)
private var renderer: RenderAR!
/**
This class renders the `ARSCNView` or `ARSKView` content with the device's camera stream to generate a video 📹, photo 🌄, live photo 🎇 or GIF 🎆.
@@ -27,11 +34,11 @@ import PhotosUI
/**
An object that passes the AR recorder errors and status in the protocol methods.
*/
@objc public var delegate: RecordARDelegate?
@objc public weak var delegate: RecordARDelegate?
/**
An object that passes the AR rendered content in the protocol method.
*/
@objc public var renderAR: RenderARDelegate?
@objc public weak var renderAR: RenderARDelegate?
/**
An object that returns the AR recorder current status.
*/
@@ -148,11 +155,6 @@ import PhotosUI
view = SceneKit
setup()
}
//MARK: - Deinit
deinit {
gpuLoop.invalidate()
}
//MARK: - threads
let writerQueue = DispatchQueue(label:"com.ahmedbekhit.WriterQueue")
@@ -160,13 +162,6 @@ import PhotosUI
let audioSessionQueue = DispatchQueue(label: "com.ahmedbekhit.AudioSessionQueue", attributes: .concurrent)
//MARK: - Objects
private var view: Any?
private var renderEngine: SCNRenderer!
private var gpuLoop: CADisplayLink!
private var isResting = false
private var ARcontentMode: ARFrameMode!
private var renderer: RenderAR!
private var scnView: SCNView!
private var fileCount = 0
@@ -225,11 +220,10 @@ import PhotosUI
}
renderEngine = SCNRenderer(device: mtlDevice, options: nil)
renderEngine.scene = view.scene
gpuLoop = CADisplayLink(target: WeakProxy(target: self),
selector: #selector(renderFrame))
gpuLoop = CADisplayLink(target: self, selector: #selector(renderFrame))
gpuLoop.preferredFramesPerSecond = fps.rawValue
gpuLoop.add(to: .main, forMode: .common)
gpuLoop.add(to: .main, forMode: .commonModes)
status = .readyToRecord
} else if let view = view as? ARSKView {
@@ -249,11 +243,10 @@ import PhotosUI
renderEngine = SCNRenderer(device: mtlDevice, options: nil)
renderEngine.scene = scnView.scene
gpuLoop = CADisplayLink(target: WeakProxy(target: self),
selector: #selector(renderFrame))
gpuLoop = CADisplayLink(target: self, selector: #selector(renderFrame))
gpuLoop.preferredFramesPerSecond = fps.rawValue
gpuLoop.add(to: .main, forMode: .common)
gpuLoop.add(to: .main, forMode: .commonModes)
status = .readyToRecord
} else if let view = view as? SCNView {
@@ -263,11 +256,10 @@ import PhotosUI
}
renderEngine = SCNRenderer(device: mtlDevice, options: nil)
renderEngine.scene = view.scene
gpuLoop = CADisplayLink(target: WeakProxy(target: self),
selector: #selector(renderFrame))
gpuLoop = CADisplayLink(target: self, selector: #selector(renderFrame))
gpuLoop.preferredFramesPerSecond = fps.rawValue
gpuLoop.add(to: .main, forMode: .common)
gpuLoop.add(to: .main, forMode: .commonModes)
status = .readyToRecord
}
@@ -276,7 +268,7 @@ import PhotosUI
renderer = RenderAR(view, renderer: renderEngine, contentMode: contentMode)
NotificationCenter.default.addObserver(self, selector: #selector(appWillEnterBackground), name: UIApplication.willResignActiveNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(appWillEnterBackground), name: Notification.Name.UIApplicationWillResignActive, object: nil)
}
@@ -521,39 +513,6 @@ import PhotosUI
}
}
}
/**
A method that cancels recording a video 📹.
- parameter finished: A block that will be called when the specified `duration` has ended.
*/
@objc public func cancel() {
writerQueue.sync {
isRecording = false
adjustPausedTime = false
backFromPause = false
recordingWithLimit = false
pausedFrameTime = nil
resumeFrameTime = nil
DispatchQueue.main.async {
self.writer?.cancel()
if let path = self.currentVideoPath {
logAR.remove(from: path)
self.delegate?.recorder?(didCancelRecording: "Recording was cancelled manually.")
self.status = .readyToRecord
} else {
self.status = .readyToRecord
self.delegate?.recorder(didFailRecording: errSecDecode as? Error, and: "An error occured while stopping your video.")
}
self.writer = nil
}
}
}
/**
A method that exports a video 📹 file path to the Photo Library 📲💾.
@@ -765,8 +724,7 @@ extension RecordAR {
renderer.ARcontentMode = contentMode
self.writerQueue.sync {
var time: CMTime { return CMTime(seconds: renderer.time, preferredTimescale: 1000000) }
var time: CMTime { return CMTimeMakeWithSeconds(renderer.time, 1000000) }
self.renderAR?.frame(didRender: buffer, with: time, using: rawBuffer)
@@ -815,10 +773,8 @@ extension RecordAR {
self.adjustPausedTime = false
if self.pausedFrameTime != nil && self.resumeFrameTime != nil {
self.pausedFrameTime = self.adjustTime(current: time,
resume: self.resumeFrameTime!,
pause: self.pausedFrameTime!)
if self.pausedFrameTime != nil {
self.pausedFrameTime = self.adjustTime(current: time, resume: self.resumeFrameTime!, pause: self.pausedFrameTime!)
} else {
self.pausedFrameTime = time
}
-26
View File
@@ -1,26 +0,0 @@
//
// SSSS.swift
// ARVideoKit
//
// Created by Saul Moreno Abril on 01/05/2019.
// Copyright © 2019 Ahmed Fathit Bekhit. All rights reserved.
//
import Foundation
class WeakProxy: NSObject {
weak var target: NSObjectProtocol?
init(target: NSObjectProtocol) {
self.target = target
super.init()
}
override func responds(to aSelector: Selector!) -> Bool {
return (target?.responds(to: aSelector) ?? false) || super.responds(to: aSelector)
}
override func forwardingTarget(for aSelector: Selector!) -> Any? {
return target
}
}
@@ -429,7 +429,7 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "com.ahmedbekhit.ARVideoKit-Example";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 4.2;
SWIFT_VERSION = 4.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
@@ -451,7 +451,7 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "com.ahmedbekhit.ARVideoKit-Example";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 4.2;
SWIFT_VERSION = 4.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;
@@ -19,7 +19,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
return ViewAR.orientation
}
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
return true
}
@@ -115,7 +115,7 @@ class SCNViewController: UIViewController, ARSCNViewDelegate, RenderARDelegate,
}else if status == .denied || status == .restricted || status == .notDetermined {
let errorView = UIAlertController(title: "😅", message: "Please allow access to the photo library in order to save this media file.", preferredStyle: .alert)
let settingsBtn = UIAlertAction(title: "Open Settings", style: .cancel) { (_) -> Void in
guard let settingsUrl = URL(string: UIApplication.openSettingsURLString) else {
guard let settingsUrl = URL(string: UIApplicationOpenSettingsURLString) else {
return
}
if UIApplication.shared.canOpenURL(settingsUrl) {
@@ -123,11 +123,11 @@ class SCNViewController: UIViewController, ARSCNViewDelegate, RenderARDelegate,
UIApplication.shared.open(settingsUrl, completionHandler: { (success) in
})
} else {
UIApplication.shared.openURL(URL(string:UIApplication.openSettingsURLString)!)
UIApplication.shared.openURL(URL(string:UIApplicationOpenSettingsURLString)!)
}
}
}
errorView.addAction(UIAlertAction(title: "Later", style: UIAlertAction.Style.default, handler: {
errorView.addAction(UIAlertAction(title: "Later", style: UIAlertActionStyle.default, handler: {
(UIAlertAction)in
}))
errorView.addAction(settingsBtn)
@@ -106,7 +106,7 @@ class SKViewController: UIViewController, ARSKViewDelegate, RenderARDelegate, Re
}else if status == .denied || status == .restricted || status == .notDetermined {
let errorView = UIAlertController(title: "😅", message: "Please allow access to the photo library in order to save this media file.", preferredStyle: .alert)
let settingsBtn = UIAlertAction(title: "Open Settings", style: .cancel) { (_) -> Void in
guard let settingsUrl = URL(string: UIApplication.openSettingsURLString) else {
guard let settingsUrl = URL(string: UIApplicationOpenSettingsURLString) else {
return
}
if UIApplication.shared.canOpenURL(settingsUrl) {
@@ -114,11 +114,11 @@ class SKViewController: UIViewController, ARSKViewDelegate, RenderARDelegate, Re
UIApplication.shared.open(settingsUrl, completionHandler: { (success) in
})
} else {
UIApplication.shared.openURL(URL(string:UIApplication.openSettingsURLString)!)
UIApplication.shared.openURL(URL(string:UIApplicationOpenSettingsURLString)!)
}
}
}
errorView.addAction(UIAlertAction(title: "Later", style: UIAlertAction.Style.default, handler: {
errorView.addAction(UIAlertAction(title: "Later", style: UIAlertActionStyle.default, handler: {
(UIAlertAction)in
}))
errorView.addAction(settingsBtn)
+1 -3
View File
@@ -1,6 +1,4 @@
> Use [swift_5](https://github.com/AFathi/ARVideoKit/tree/swift_5) branch for projects written in Swift 5
> Use [master](https://github.com/AFathi/ARVideoKit/tree/master) branch for projects written in Swift 4.0
> Use [swift_4_2](https://github.com/AFathi/ARVideoKit/tree/swift_4_2) branch for projects written in Swift 4.2
![intro image](http://www.ahmedbekhit.com/projects/repo-header-arvideokit.png)