session-ios/SessionUtilitiesKit/Media/OWSMediaUtils.swift
Morgan Pretty 32304ae5dd Cleared out some of the legacy serialisation logic, further UI binding
Refactored the SignalApp class to Swift
Fixed a horizontal alignment issue in the ConversationTitleView
Fixed an issue where expiration timer update messages weren't migrated or rendering correctly
Fixed an issue where expiring messages weren't migrated correctly
Fixed an issue where closed groups which had been left were causing migration failures (due to data incorrectly being assumed to be required)
Shifted the Legacy Attachment types into the 'SMKLegacy' namespace
Moved all of the NSCoding logic for the TSMessage
2022-05-03 17:14:56 +10:00

133 lines
5.1 KiB
Swift

// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import Foundation
import AVFoundation
public enum OWSMediaError: Error {
case failure(description: String)
}
@objc public class OWSMediaUtils: NSObject {
@available(*, unavailable, message:"do not instantiate this class.")
private override init() {
}
@objc public class func thumbnail(forImageAtPath path: String, maxDimension: CGFloat) throws -> UIImage {
SNLog("thumbnailing image: \(path)")
guard FileManager.default.fileExists(atPath: path) else {
throw OWSMediaError.failure(description: "Media file missing.")
}
guard NSData.ows_isValidImage(atPath: path) else {
throw OWSMediaError.failure(description: "Invalid image.")
}
guard let originalImage = UIImage(contentsOfFile: path) else {
throw OWSMediaError.failure(description: "Could not load original image.")
}
guard let thumbnailImage = originalImage.resized(withMaxDimensionPoints: maxDimension) else {
throw OWSMediaError.failure(description: "Could not thumbnail image.")
}
return thumbnailImage
}
@objc public class func thumbnail(forVideoAtPath path: String, maxDimension: CGFloat) throws -> UIImage {
SNLog("thumbnailing video: \(path)")
guard isVideoOfValidContentTypeAndSize(path: path) else {
throw OWSMediaError.failure(description: "Media file has missing or invalid length.")
}
let maxSize = CGSize(width: maxDimension, height: maxDimension)
let url = URL(fileURLWithPath: path)
let asset = AVURLAsset(url: url, options: nil)
guard isValidVideo(asset: asset) else {
throw OWSMediaError.failure(description: "Invalid video.")
}
let generator = AVAssetImageGenerator(asset: asset)
generator.maximumSize = maxSize
generator.appliesPreferredTrackTransform = true
let time: CMTime = CMTimeMake(value: 1, timescale: 60)
let cgImage = try generator.copyCGImage(at: time, actualTime: nil)
let image = UIImage(cgImage: cgImage)
return image
}
@objc public class func isValidVideo(path: String) -> Bool {
guard isVideoOfValidContentTypeAndSize(path: path) else {
SNLog("Media file has missing or invalid length.")
return false
}
let url = URL(fileURLWithPath: path)
let asset = AVURLAsset(url: url, options: nil)
return isValidVideo(asset: asset)
}
private class func isVideoOfValidContentTypeAndSize(path: String) -> Bool {
guard FileManager.default.fileExists(atPath: path) else {
SNLog("Media file missing.")
return false
}
let fileExtension = URL(fileURLWithPath: path).pathExtension
guard let contentType = MIMETypeUtil.mimeType(forFileExtension: fileExtension) else {
SNLog("Media file has unknown content type.")
return false
}
guard MIMETypeUtil.isSupportedVideoMIMEType(contentType) else {
SNLog("Media file has invalid content type.")
return false
}
guard let fileSize = OWSFileSystem.fileSize(ofPath: path) else {
SNLog("Media file has unknown length.")
return false
}
return fileSize.uintValue <= kMaxFileSizeVideo
}
private class func isValidVideo(asset: AVURLAsset) -> Bool {
var maxTrackSize = CGSize.zero
for track: AVAssetTrack in asset.tracks(withMediaType: .video) {
let trackSize: CGSize = track.naturalSize
maxTrackSize.width = max(maxTrackSize.width, trackSize.width)
maxTrackSize.height = max(maxTrackSize.height, trackSize.height)
}
if maxTrackSize.width < 1.0 || maxTrackSize.height < 1.0 {
SNLog("Invalid video size: \(maxTrackSize)")
return false
}
if maxTrackSize.width > kMaxVideoDimensions || maxTrackSize.height > kMaxVideoDimensions {
SNLog("Invalid video dimensions: \(maxTrackSize)")
return false
}
return true
}
// MARK: Constants
/**
* Media Size constraints from Signal-Android
*
* https://github.com/signalapp/Signal-Android/blob/master/src/org/thoughtcrime/securesms/mms/PushMediaConstraints.java
*/
@objc
public static var kMaxFileSizeAnimatedImage: UInt { SNUtilitiesKitConfiguration.shared.maxFileSize }
@objc
public static var kMaxFileSizeImage: UInt { SNUtilitiesKitConfiguration.shared.maxFileSize }
@objc
public static var kMaxFileSizeVideo: UInt { SNUtilitiesKitConfiguration.shared.maxFileSize }
@objc
public static var kMaxFileSizeAudio: UInt { SNUtilitiesKitConfiguration.shared.maxFileSize }
@objc
public static var kMaxFileSizeGeneric: UInt { SNUtilitiesKitConfiguration.shared.maxFileSize }
@objc
public static let kMaxVideoDimensions: CGFloat = 3 * 1024
@objc
public static let kMaxAnimatedImageDimensions: UInt = 1 * 1024
@objc
public static let kMaxStillImageDimensions: UInt = 8 * 1024
}