Permalink
Browse files

Use NSGIF

  • Loading branch information...
1 parent 613f642 commit 456e13d24036d6688b25efb6990a491c8de66272 @onmyway133 committed Mar 6, 2017
Showing with 2,256 additions and 13 deletions.
  1. +1 โˆ’1 .gitignore
  2. +2 โˆ’6 GifCapture/Camera/Saver.swift
  3. +1 โˆ’1 Podfile
  4. +4 โˆ’4 Podfile.lock
  5. +12 โˆ’0 Pods/Manifest.lock
  6. +22 โˆ’0 Pods/NSGIF/LICENSE
  7. +26 โˆ’0 Pods/NSGIF/NSGIF/NSGIF.h
  8. +235 โˆ’0 Pods/NSGIF/NSGIF/NSGIF.m
  9. +71 โˆ’0 Pods/NSGIF/README.md
  10. +862 โˆ’0 Pods/Pods.xcodeproj/project.pbxproj
  11. +26 โˆ’0 Pods/Target Support Files/NSGIF/Info.plist
  12. +5 โˆ’0 Pods/Target Support Files/NSGIF/NSGIF-dummy.m
  13. +12 โˆ’0 Pods/Target Support Files/NSGIF/NSGIF-prefix.pch
  14. +17 โˆ’0 Pods/Target Support Files/NSGIF/NSGIF-umbrella.h
  15. +6 โˆ’0 Pods/Target Support Files/NSGIF/NSGIF.modulemap
  16. +10 โˆ’0 Pods/Target Support Files/NSGIF/NSGIF.xcconfig
  17. +26 โˆ’0 Pods/Target Support Files/Pods-GifCapture/Info.plist
  18. +29 โˆ’0 Pods/Target Support Files/Pods-GifCapture/Pods-GifCapture-acknowledgements.markdown
  19. +61 โˆ’0 Pods/Target Support Files/Pods-GifCapture/Pods-GifCapture-acknowledgements.plist
  20. +5 โˆ’0 Pods/Target Support Files/Pods-GifCapture/Pods-GifCapture-dummy.m
  21. +99 โˆ’0 Pods/Target Support Files/Pods-GifCapture/Pods-GifCapture-frameworks.sh
  22. +99 โˆ’0 Pods/Target Support Files/Pods-GifCapture/Pods-GifCapture-resources.sh
  23. +16 โˆ’0 Pods/Target Support Files/Pods-GifCapture/Pods-GifCapture-umbrella.h
  24. +9 โˆ’0 Pods/Target Support Files/Pods-GifCapture/Pods-GifCapture.debug.xcconfig
  25. +6 โˆ’0 Pods/Target Support Files/Pods-GifCapture/Pods-GifCapture.modulemap
  26. +9 โˆ’0 Pods/Target Support Files/Pods-GifCapture/Pods-GifCapture.release.xcconfig
  27. +26 โˆ’0 Pods/Target Support Files/Pods-GifCaptureTests/Info.plist
  28. +3 โˆ’0 Pods/Target Support Files/Pods-GifCaptureTests/Pods-GifCaptureTests-acknowledgements.markdown
  29. +29 โˆ’0 Pods/Target Support Files/Pods-GifCaptureTests/Pods-GifCaptureTests-acknowledgements.plist
  30. +5 โˆ’0 Pods/Target Support Files/Pods-GifCaptureTests/Pods-GifCaptureTests-dummy.m
  31. +92 โˆ’0 Pods/Target Support Files/Pods-GifCaptureTests/Pods-GifCaptureTests-frameworks.sh
  32. +99 โˆ’0 Pods/Target Support Files/Pods-GifCaptureTests/Pods-GifCaptureTests-resources.sh
  33. +16 โˆ’0 Pods/Target Support Files/Pods-GifCaptureTests/Pods-GifCaptureTests-umbrella.h
  34. +8 โˆ’0 Pods/Target Support Files/Pods-GifCaptureTests/Pods-GifCaptureTests.debug.xcconfig
  35. +6 โˆ’0 Pods/Target Support Files/Pods-GifCaptureTests/Pods-GifCaptureTests.modulemap
  36. +8 โˆ’0 Pods/Target Support Files/Pods-GifCaptureTests/Pods-GifCaptureTests.release.xcconfig
  37. +26 โˆ’0 Pods/Target Support Files/Pods-GifCaptureUITests/Info.plist
  38. +3 โˆ’0 Pods/Target Support Files/Pods-GifCaptureUITests/Pods-GifCaptureUITests-acknowledgements.markdown
  39. +29 โˆ’0 Pods/Target Support Files/Pods-GifCaptureUITests/Pods-GifCaptureUITests-acknowledgements.plist
  40. +5 โˆ’0 Pods/Target Support Files/Pods-GifCaptureUITests/Pods-GifCaptureUITests-dummy.m
  41. +92 โˆ’0 Pods/Target Support Files/Pods-GifCaptureUITests/Pods-GifCaptureUITests-frameworks.sh
  42. +99 โˆ’0 Pods/Target Support Files/Pods-GifCaptureUITests/Pods-GifCaptureUITests-resources.sh
  43. +16 โˆ’0 Pods/Target Support Files/Pods-GifCaptureUITests/Pods-GifCaptureUITests-umbrella.h
  44. +8 โˆ’0 Pods/Target Support Files/Pods-GifCaptureUITests/Pods-GifCaptureUITests.debug.xcconfig
  45. +6 โˆ’0 Pods/Target Support Files/Pods-GifCaptureUITests/Pods-GifCaptureUITests.modulemap
  46. +8 โˆ’0 Pods/Target Support Files/Pods-GifCaptureUITests/Pods-GifCaptureUITests.release.xcconfig
  47. +1 โˆ’1 README.md
View
@@ -27,7 +27,7 @@ DerivedData
*.xcuserstate
# CocoaPods
-Pods
+#Pods
# Carthage
Carthage
@@ -7,17 +7,13 @@
//
import Foundation
-import Regift
+import NSGIF
class Saver {
func save(videoUrl: URL, completion: @escaping (URL?) -> Void) {
- Regift.createGIFFromSource(videoUrl,
- destinationFileURL: gifUrl(),
- frameCount: 16,
- delayTime: 0.2, loopCount: 0)
- { [weak self] (url) in
+ NSGIF.optimalGIFfromURL(videoUrl, loopCount: 0) { [weak self] (url) in
self?.removeFile(at: videoUrl)
completion(url)
}
View
@@ -1,7 +1,7 @@
target 'GifCapture' do
use_frameworks!
- pod 'Regift', '~> 1.3'
+ pod 'NSGIF', '~> 1.2'
target 'GifCaptureTests' do
inherit! :search_paths
View
@@ -1,12 +1,12 @@
PODS:
- - Regift (1.3.0)
+ - NSGIF (1.2)
DEPENDENCIES:
- - Regift (~> 1.3)
+ - NSGIF (~> 1.2)
SPEC CHECKSUMS:
- Regift: 43c5e057949a9e2b16de0bcf0a221a5dbd033211
+ NSGIF: 73b435330174a9d04db9886a232c605a3e8139b5
-PODFILE CHECKSUM: b24bde26b211ae94cf275ae7dd2b538c7f53c11f
+PODFILE CHECKSUM: 0121fb276cd88b274fc9084a6a3072e0798b3bb3
COCOAPODS: 1.2.0
View
@@ -0,0 +1,12 @@
+PODS:
+ - NSGIF (1.2)
+
+DEPENDENCIES:
+ - NSGIF (~> 1.2)
+
+SPEC CHECKSUMS:
+ NSGIF: 73b435330174a9d04db9886a232c605a3e8139b5
+
+PODFILE CHECKSUM: 0121fb276cd88b274fc9084a6a3072e0798b3bb3
+
+COCOAPODS: 1.2.0
View
@@ -0,0 +1,22 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 Sebastian Dobrincu
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
View
@@ -0,0 +1,26 @@
+//
+// NSGIF.h
+//
+// Created by Sebastian Dobrincu
+//
+
+#import <Foundation/Foundation.h>
+#import <CoreGraphics/CoreGraphics.h>
+#import <ImageIO/ImageIO.h>
+#import <AVFoundation/AVFoundation.h>
+
+#if TARGET_OS_IPHONE
+ #import <MobileCoreServices/MobileCoreServices.h>
+ #import <UIKit/UIKit.h>
+#elif TARGET_OS_MAC
+ #import <CoreServices/CoreServices.h>
+ #import <WebKit/WebKit.h>
+#endif
+
+@interface NSGIF : NSObject
+
++ (void)optimalGIFfromURL:(NSURL*)videoURL loopCount:(int)loopCount completion:(void(^)(NSURL *GifURL))completionBlock;
+
++ (void)createGIFfromURL:(NSURL*)videoURL withFrameCount:(int)frameCount delayTime:(int)delayTime loopCount:(int)loopCount completion:(void(^)(NSURL *GifURL))completionBlock;
+
+@end
View
@@ -0,0 +1,235 @@
+//
+// NSGIF.m
+//
+// Created by Sebastian Dobrincu
+//
+
+#import "NSGIF.h"
+
+@implementation NSGIF
+
+// Declare constants
+#define fileName @"NSGIF.gif"
+#define timeInterval @(600)
+#define tolerance @(0.01)
+
+typedef NS_ENUM(NSInteger, GIFSize) {
+ GIFSizeVeryLow = 2,
+ GIFSizeLow = 3,
+ GIFSizeMedium = 5,
+ GIFSizeHigh = 7,
+ GIFSizeOriginal = 10
+};
+
+#pragma mark - Public methods
+
++ (void)optimalGIFfromURL:(NSURL*)videoURL loopCount:(int)loopCount completion:(void(^)(NSURL *GifURL))completionBlock {
+
+ int delayTime = 0.2;
+
+ // Create properties dictionaries
+ NSDictionary *fileProperties = [self filePropertiesWithLoopCount:loopCount];
+ NSDictionary *frameProperties = [self framePropertiesWithDelayTime:delayTime];
+
+ AVURLAsset *asset = [AVURLAsset assetWithURL:videoURL];
+
+ float videoWidth = [[[asset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0] naturalSize].width;
+ float videoHeight = [[[asset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0] naturalSize].height;
+
+ GIFSize optimalSize = GIFSizeMedium;
+ if (videoWidth >= 1200 || videoHeight >= 1200)
+ optimalSize = GIFSizeVeryLow;
+ else if (videoWidth >= 800 || videoHeight >= 800)
+ optimalSize = GIFSizeLow;
+ else if (videoWidth >= 400 || videoHeight >= 400)
+ optimalSize = GIFSizeMedium;
+ else if (videoWidth < 400|| videoHeight < 400)
+ optimalSize = GIFSizeHigh;
+
+ // Get the length of the video in seconds
+ float videoLength = (float)asset.duration.value/asset.duration.timescale;
+ int framesPerSecond = 4;
+ int frameCount = videoLength*framesPerSecond;
+
+ // How far along the video track we want to move, in seconds.
+ float increment = (float)videoLength/frameCount;
+
+ // Add frames to the buffer
+ NSMutableArray *timePoints = [NSMutableArray array];
+ for (int currentFrame = 0; currentFrame<frameCount; ++currentFrame) {
+ float seconds = (float)increment * currentFrame;
+ CMTime time = CMTimeMakeWithSeconds(seconds, [timeInterval intValue]);
+ [timePoints addObject:[NSValue valueWithCMTime:time]];
+ }
+
+ // Prepare group for firing completion block
+ dispatch_group_t gifQueue = dispatch_group_create();
+ dispatch_group_enter(gifQueue);
+
+ __block NSURL *gifURL;
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+
+ gifURL = [self createGIFforTimePoints:timePoints fromURL:videoURL fileProperties:fileProperties frameProperties:frameProperties frameCount:frameCount gifSize:optimalSize];
+
+ dispatch_group_leave(gifQueue);
+ });
+
+ dispatch_group_notify(gifQueue, dispatch_get_main_queue(), ^{
+ // Return GIF URL
+ completionBlock(gifURL);
+ });
+
+}
+
++ (void)createGIFfromURL:(NSURL*)videoURL withFrameCount:(int)frameCount delayTime:(int)delayTime loopCount:(int)loopCount completion:(void(^)(NSURL *GifURL))completionBlock {
+
+ // Convert the video at the given URL to a GIF, and return the GIF's URL if it was created.
+ // The frames are spaced evenly over the video, and each has the same duration.
+ // delayTime is the amount of time for each frame in the GIF.
+ // loopCount is the number of times the GIF will repeat. Defaults to 0, which means repeat infinitely.
+
+ // Create properties dictionaries
+ NSDictionary *fileProperties = [self filePropertiesWithLoopCount:loopCount];
+ NSDictionary *frameProperties = [self framePropertiesWithDelayTime:delayTime];
+
+ AVURLAsset *asset = [AVURLAsset assetWithURL:videoURL];
+
+ // Get the length of the video in seconds
+ float videoLength = (float)asset.duration.value/asset.duration.timescale;
+
+ // How far along the video track we want to move, in seconds.
+ float increment = (float)videoLength/frameCount;
+
+ // Add frames to the buffer
+ NSMutableArray *timePoints = [NSMutableArray array];
+ for (int currentFrame = 0; currentFrame<frameCount; ++currentFrame) {
+ float seconds = (float)increment * currentFrame;
+ CMTime time = CMTimeMakeWithSeconds(seconds, [timeInterval intValue]);
+ [timePoints addObject:[NSValue valueWithCMTime:time]];
+ }
+
+ // Prepare group for firing completion block
+ dispatch_group_t gifQueue = dispatch_group_create();
+ dispatch_group_enter(gifQueue);
+
+ __block NSURL *gifURL;
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+
+ gifURL = [self createGIFforTimePoints:timePoints fromURL:videoURL fileProperties:fileProperties frameProperties:frameProperties frameCount:frameCount gifSize:GIFSizeMedium];
+
+ dispatch_group_leave(gifQueue);
+ });
+
+ dispatch_group_notify(gifQueue, dispatch_get_main_queue(), ^{
+ // Return GIF URL
+ completionBlock(gifURL);
+ });
+
+}
+
+#pragma mark - Base methods
+
++ (NSURL *)createGIFforTimePoints:(NSArray *)timePoints fromURL:(NSURL *)url fileProperties:(NSDictionary *)fileProperties frameProperties:(NSDictionary *)frameProperties frameCount:(int)frameCount gifSize:(GIFSize)gifSize{
+
+ NSString *temporaryFile = [NSTemporaryDirectory() stringByAppendingString:fileName];
+ NSURL *fileURL = [NSURL fileURLWithPath:temporaryFile];
+ CGImageDestinationRef destination = CGImageDestinationCreateWithURL((__bridge CFURLRef)fileURL, kUTTypeGIF , frameCount, NULL);
+
+ if (fileURL == nil)
+ return nil;
+
+ AVURLAsset *asset = [AVURLAsset URLAssetWithURL:url options:nil];
+ AVAssetImageGenerator *generator = [AVAssetImageGenerator assetImageGeneratorWithAsset:asset];
+ generator.appliesPreferredTrackTransform = YES;
+
+ CMTime tol = CMTimeMakeWithSeconds([tolerance floatValue], [timeInterval intValue]);
+ generator.requestedTimeToleranceBefore = tol;
+ generator.requestedTimeToleranceAfter = tol;
+
+ NSError *error = nil;
+ CGImageRef previousImageRefCopy = nil;
+ for (NSValue *time in timePoints) {
+ CGImageRef imageRef;
+
+ #if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR
+ imageRef = (float)gifSize/10 != 1 ? ImageWithScale([generator copyCGImageAtTime:[time CMTimeValue] actualTime:nil error:&error], (float)gifSize/10) : [generator copyCGImageAtTime:[time CMTimeValue] actualTime:nil error:&error];
+ #elif TARGET_OS_MAC
+ imageRef = [generator copyCGImageAtTime:[time CMTimeValue] actualTime:nil error:&error];
+ #endif
+
+ if (error) {
+ NSLog(@"Error copying image: %@", error);
+ }
+ if (imageRef) {
+ CGImageRelease(previousImageRefCopy);
+ previousImageRefCopy = CGImageCreateCopy(imageRef);
+ } else if (previousImageRefCopy) {
+ imageRef = CGImageCreateCopy(previousImageRefCopy);
+ } else {
+ NSLog(@"Error copying image and no previous frames to duplicate");
+ return nil;
+ }
+ CGImageDestinationAddImage(destination, imageRef, (CFDictionaryRef)frameProperties);
+ CGImageRelease(imageRef);
+ }
+ CGImageRelease(previousImageRefCopy);
+
+ CGImageDestinationSetProperties(destination, (CFDictionaryRef)fileProperties);
+ // Finalize the GIF
+ if (!CGImageDestinationFinalize(destination)) {
+ NSLog(@"Failed to finalize GIF destination: %@", error);
+ return nil;
+ }
+ CFRelease(destination);
+
+ return fileURL;
+}
+
+#pragma mark - Helpers
+
+CGImageRef ImageWithScale(CGImageRef imageRef, float scale) {
+
+ #if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR
+ CGSize newSize = CGSizeMake(CGImageGetWidth(imageRef)*scale, CGImageGetHeight(imageRef)*scale);
+ CGRect newRect = CGRectIntegral(CGRectMake(0, 0, newSize.width, newSize.height));
+
+ UIGraphicsBeginImageContextWithOptions(newSize, NO, 0);
+ CGContextRef context = UIGraphicsGetCurrentContext();
+ if (!context) {
+ return nil;
+ }
+
+ // Set the quality level to use when rescaling
+ CGContextSetInterpolationQuality(context, kCGInterpolationHigh);
+ CGAffineTransform flipVertical = CGAffineTransformMake(1, 0, 0, -1, 0, newSize.height);
+
+ CGContextConcatCTM(context, flipVertical);
+ // Draw into the context; this scales the image
+ CGContextDrawImage(context, newRect, imageRef);
+
+ // Get the resized image from the context and a UIImage
+ imageRef = CGBitmapContextCreateImage(context);
+
+ UIGraphicsEndImageContext();
+ #endif
+
+ return imageRef;
+}
+
+#pragma mark - Properties
+
++ (NSDictionary *)filePropertiesWithLoopCount:(int)loopCount {
+ return @{(NSString *)kCGImagePropertyGIFDictionary:
+ @{(NSString *)kCGImagePropertyGIFLoopCount: @(loopCount)}
+ };
+}
+
++ (NSDictionary *)framePropertiesWithDelayTime:(int)delayTime {
+
+ return @{(NSString *)kCGImagePropertyGIFDictionary:
+ @{(NSString *)kCGImagePropertyGIFDelayTime: @(delayTime)},
+ (NSString *)kCGImagePropertyColorModel:(NSString *)kCGImagePropertyColorModelRGB
+ };
+}
+
+@end
Oops, something went wrong.

0 comments on commit 456e13d

Please sign in to comment.