Skip to content

Commit

Permalink
Add frameRate option to the Gifski wrapper
Browse files Browse the repository at this point in the history
In anticipation for #4

And some cleanup.
  • Loading branch information
sindresorhus committed Feb 23, 2018
1 parent 0cc99c2 commit 2dcc1ee
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 15 deletions.
4 changes: 1 addition & 3 deletions Gifski/AppDelegate.swift
@@ -1,8 +1,6 @@
import Cocoa
import ProgressKit

private let defaults = UserDefaults.standard

extension NSColor {
static let appTheme = NSColor(named: NSColor.Name("Theme"))!
}
Expand Down Expand Up @@ -175,7 +173,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate {

gifski.convertFile(
inputUrl,
outputFile: outputUrl,
outputUrl: outputUrl,
quality: defaults["outputQuality"] as! Double,
dimensions: choosenDimensions
)
Expand Down
23 changes: 14 additions & 9 deletions Gifski/Gifski.swift
Expand Up @@ -15,11 +15,16 @@ final class Gifski {
private(set) var progress: Double = 0
var onProgress: ((_ progress: Double) -> Void)?

/**
- parameters:
- frameRate: Clamped to 5...30. Uses the frame rate of `inputUrl` if not specified.
*/
func convertFile(
_ inputFile: URL,
outputFile: URL,
_ inputUrl: URL,
outputUrl: URL,
quality: Double = 1,
dimensions: CGSize? = nil
dimensions: CGSize? = nil,
frameRate: Int? = nil
) {
guard !isRunning else {
return
Expand Down Expand Up @@ -57,17 +62,17 @@ final class Gifski {
}, context)

DispatchQueue.global(qos: .utility).async {
let asset = AVURLAsset(url: inputFile, options: nil)
let asset = AVURLAsset(url: inputUrl, options: nil)
let generator = AVAssetImageGenerator(asset: asset)
generator.requestedTimeToleranceAfter = kCMTimeZero
generator.requestedTimeToleranceBefore = kCMTimeZero

let FPS = 24
self.frameCount = Int(asset.duration.seconds) * FPS
let fps = (frameRate.map { Double($0) } ?? asset.videoMetadata!.frameRate).clamped(to: 5...30)
self.frameCount = Int(asset.duration.seconds * fps)

var frameForTimes = [CMTime]()
for i in 0..<self.frameCount {
frameForTimes.append(CMTimeMake(Int64(i), Int32(FPS)))
frameForTimes.append(CMTimeMake(Int64(i), Int32(fps)))
}

var frameIndex = 0
Expand All @@ -85,7 +90,7 @@ final class Gifski {
UInt32(image.bytesPerRow),
UInt32(image.height),
buffer,
UInt16(100 / FPS)
UInt16(100 / fps)
)
precondition(result == GIFSKI_OK, String(describing: result))

Expand All @@ -96,7 +101,7 @@ final class Gifski {
}
}

gifski_write(g, outputFile.path)
gifski_write(g, outputUrl.path)
gifski_drop(g)
}
}
Expand Down
4 changes: 3 additions & 1 deletion Gifski/SavePanelAccessoryViewController.swift
Expand Up @@ -38,10 +38,12 @@ final class SavePanelAccessoryViewController: NSViewController {
}

qualitySlider.onAction = { _ in
UserDefaults.standard["outputQuality"] = self.qualitySlider.doubleValue
defaults["outputQuality"] = self.qualitySlider.doubleValue
estimateFileSize()
}

// Set initial defaults
dimensionsSlider.triggerAction()
qualitySlider.doubleValue = defaults["outputQuality"] as! Double
}
}
54 changes: 52 additions & 2 deletions Gifski/util.swift
Expand Up @@ -2,6 +2,9 @@ import Cocoa
import AVFoundation


let defaults = UserDefaults.standard


struct Meta {
static func openSubmitFeedbackPage() {
let body =
Expand Down Expand Up @@ -52,6 +55,53 @@ class SSView: NSView {
}


extension Comparable {
/// Note: It's not possible to implement `Range` or `PartialRangeUpTo` here as we can't know what `1.1..<1.53` would be. They only work with Stridable in our case.

/// Example: 20.5.clamped(from: 10.3, to: 15)
func clamped(from lowerBound: Self, to upperBound: Self) -> Self {
return min(max(self, lowerBound), upperBound)
}

/// Example: 20.5.clamped(to: 10.3...15)
func clamped(to range: ClosedRange<Self>) -> Self {
return clamped(from: range.lowerBound, to: range.upperBound)
}

/// Example: 20.5.clamped(to: ...10.3)
/// => 10.3
func clamped(to range: PartialRangeThrough<Self>) -> Self {
return min(self, range.upperBound)
}

/// Example: 5.5.clamped(to: 10.3...)
/// => 10.3
func clamped(to range: PartialRangeFrom<Self>) -> Self {
return max(self, range.lowerBound)
}
}

extension Strideable where Stride: SignedInteger {
/// Example: 20.clamped(to: 5..<10)
/// => 9
func clamped(to range: CountableRange<Self>) -> Self {
return clamped(from: range.lowerBound, to: range.upperBound.advanced(by: -1))
}

/// Example: 20.clamped(to: 5...10)
/// => 10
func clamped(to range: CountableClosedRange<Self>) -> Self {
return clamped(from: range.lowerBound, to: range.upperBound)
}

/// Example: 20.clamped(to: ..<10)
/// => 9
func clamped(to range: PartialRangeUpTo<Self>) -> Self {
return min(self, range.upperBound.advanced(by: -1))
}
}


extension NSViewController {
var appDelegate: AppDelegate {
return NSApp.delegate as! AppDelegate
Expand All @@ -64,7 +114,7 @@ extension AVURLAsset {
struct VideoMetadata {
let dimensions: CGSize
let duration: Double
let frameCount: Double
let frameRate: Double
let fileSize: Int
}

Expand All @@ -78,7 +128,7 @@ extension AVURLAsset {
return VideoMetadata(
dimensions: CGSize(width: fabs(dimensions.width), height: fabs(dimensions.height)),
duration: duration.seconds,
frameCount: Double(track.nominalFrameRate),
frameRate: Double(track.nominalFrameRate),
fileSize: url.fileSize
)
}
Expand Down

0 comments on commit 2dcc1ee

Please sign in to comment.