mirror of https://github.com/oxen-io/session-ios
				
				
				
			
			You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
	
	
		
			131 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			Swift
		
	
		
		
			
		
	
	
			131 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			Swift
		
	
| 
											5 years ago
										 | 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 { | ||
| 
											5 years ago
										 |         SNLog("thumbnailing image: \(path)") | ||
| 
											5 years ago
										 | 
 | ||
|  |         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 { | ||
| 
											5 years ago
										 |         SNLog("thumbnailing video: \(path)") | ||
| 
											5 years ago
										 | 
 | ||
|  |         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 { | ||
| 
											5 years ago
										 |             SNLog("Media file has missing or invalid length.") | ||
| 
											5 years ago
										 |             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 { | ||
| 
											5 years ago
										 |             SNLog("Media file missing.") | ||
| 
											5 years ago
										 |             return false | ||
|  |         } | ||
|  |         let fileExtension = URL(fileURLWithPath: path).pathExtension | ||
|  |         guard let contentType = MIMETypeUtil.mimeType(forFileExtension: fileExtension) else { | ||
| 
											5 years ago
										 |             SNLog("Media file has unknown content type.") | ||
| 
											5 years ago
										 |             return false | ||
|  |         } | ||
|  |         guard MIMETypeUtil.isSupportedVideoMIMEType(contentType) else { | ||
| 
											5 years ago
										 |             SNLog("Media file has invalid content type.") | ||
| 
											5 years ago
										 |             return false | ||
|  |         } | ||
|  | 
 | ||
|  |         guard let fileSize = OWSFileSystem.fileSize(ofPath: path) else { | ||
| 
											5 years ago
										 |             SNLog("Media file has unknown length.") | ||
| 
											5 years ago
										 |             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 { | ||
| 
											5 years ago
										 |             SNLog("Invalid video size: \(maxTrackSize)") | ||
| 
											5 years ago
										 |             return false | ||
|  |         } | ||
|  |         if maxTrackSize.width > kMaxVideoDimensions || maxTrackSize.height > kMaxVideoDimensions { | ||
| 
											5 years ago
										 |             SNLog("Invalid video dimensions: \(maxTrackSize)") | ||
| 
											5 years ago
										 |             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 | ||
| 
											5 years ago
										 |     public static var kMaxFileSizeAnimatedImage: UInt { SNUtilitiesKitConfiguration.shared.maxFileSize } | ||
| 
											5 years ago
										 |     @objc | ||
| 
											5 years ago
										 |     public static var kMaxFileSizeImage: UInt { SNUtilitiesKitConfiguration.shared.maxFileSize } | ||
| 
											5 years ago
										 |     @objc | ||
| 
											5 years ago
										 |     public static var kMaxFileSizeVideo: UInt { SNUtilitiesKitConfiguration.shared.maxFileSize } | ||
| 
											5 years ago
										 |     @objc | ||
| 
											5 years ago
										 |     public static var kMaxFileSizeAudio: UInt { SNUtilitiesKitConfiguration.shared.maxFileSize } | ||
| 
											5 years ago
										 |     @objc | ||
| 
											5 years ago
										 |     public static var kMaxFileSizeGeneric: UInt { SNUtilitiesKitConfiguration.shared.maxFileSize } | ||
| 
											5 years ago
										 | 
 | ||
|  |     @objc | ||
|  |     public static let kMaxVideoDimensions: CGFloat = 3 * 1024 | ||
|  |     @objc | ||
|  |     public static let kMaxAnimatedImageDimensions: UInt = 1 * 1024 | ||
|  |     @objc | ||
|  |     public static let kMaxStillImageDimensions: UInt = 8 * 1024 | ||
|  | } |