123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170 |
- // _____ _
- // |_ _| |_ _ _ ___ ___ _ __ __ _
- // | | | ' \| '_/ -_) -_) ' \/ _` |_
- // |_| |_||_|_| \___\___|_|_|_\__,_(_)
- //
- // Threema iOS Client
- // Copyright (c) 2020 Threema GmbH
- //
- // This program is free software: you can redistribute it and/or modify
- // it under the terms of the GNU Affero General Public License, version 3,
- // as published by the Free Software Foundation.
- //
- // This program is distributed in the hope that it will be useful,
- // but WITHOUT ANY WARRANTY; without even the implied warranty of
- // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- // GNU Affero General Public License for more details.
- //
- // You should have received a copy of the GNU Affero General Public License
- // along with this program. If not, see <https://www.gnu.org/licenses/>.
- import Foundation
- import CocoaLumberjackSwift
- @objc public class VideoConversionHelper : NSObject {
-
- private static let fileOverhead : Int64 = 48 * 1024
-
- private struct MovieRate {
- var videoRate : Int32
- var videoSize : Int32
- var audioRate : Int32
- var audioChannels : Int32
- }
-
- private static let movieRateHigh = MovieRate(videoRate: kVideoBitrateHigh,
- videoSize: kMaxVideoSizeHigh,
- audioRate: kAudioBitrateHigh,
- audioChannels: kAudioChannelsHigh)
-
- private static let movieRateMedium = MovieRate(videoRate: kVideoBitrateMedium,
- videoSize: kMaxVideoSizeHigh,
- audioRate: kAudioBitrateMedium,
- audioChannels: kAudioChannelsHigh)
-
- private static let movieRateLow = MovieRate(videoRate: kVideoBitrateLow,
- videoSize: kMaxVideoSizeLow,
- audioRate: kAudioBitrateLow,
- audioChannels: kAudioChannelsLow)
-
- private static let rates = [movieRateHigh, movieRateMedium, movieRateLow]
-
- private static func getHighestPossibleBitrate(duration : Int, audioBitrate : Int, videoBitrate : Int) -> MovieRate? {
-
- if UserSettings.shared()?.videoQuality == "low" {
- return rates.last!
- }
-
- for rate in rates {
- let totAudioRate = (rate.audioRate * rate.audioChannels) / 8
- let totVideoRate = rate.videoRate / 8
- let fileSize = Int((totVideoRate + totAudioRate)) * duration
- DDLogInfo("Video File Size is \(fileSize)")
- if fileSize <= kMaxFileSize {
- return rate
- }
- }
- return nil
- }
-
-
- @objc public static func videoHasAllowedSize(at url : URL) -> Bool {
- let asset = AVURLAsset(url: url)
-
- return assetHasAllowedSize(asset: asset)
- }
-
- @objc public static func assetHasAllowedSize(asset : AVAsset) -> Bool {
- let duration : Int = Int(asset.duration.seconds)
-
- guard let videoTrack = asset.tracks(withMediaType: .video).first else {
- return false
- }
-
- let audioTrack = asset.tracks(withMediaType: .audio).first
-
- let videoRate : Int = Int(videoTrack.estimatedDataRate)
-
- var audioRate : Int = 0
-
- if audioTrack != nil {
- audioRate = Int(audioTrack!.estimatedDataRate)
- }
-
-
- let bitRate = VideoConversionHelper.getHighestPossibleBitrate(duration: duration,
- audioBitrate: audioRate,
- videoBitrate: videoRate)
-
- return bitRate != nil
- }
-
- @objc public static func getMaxdurationFor(videoBitrate : Int64, audioBitrate : Int64) -> Int64 {
- return Int64(kMaxFileSize) / (videoBitrate + audioBitrate + fileOverhead)
- }
-
- @objc public static func getAVAssetExportSession(from asset : AVAsset, outputURL : URL) -> SDAVAssetExportSession? {
-
- guard let videoTrack = asset.tracks(withMediaType: .video).first else {
- return nil
- }
- let audioTrackRate = asset.tracks(withMediaType: .audio).first?.estimatedDataRate ?? 0
-
- var srcVideoSize = __CGSizeApplyAffineTransform(videoTrack.naturalSize, videoTrack.preferredTransform)
-
- if srcVideoSize.width < 0 {
- srcVideoSize.width = -srcVideoSize.width
- }
-
- if srcVideoSize.height < 0 {
- srcVideoSize.height = -srcVideoSize.height
- }
-
- let duration : Int = Int(asset.duration.seconds)
- guard let rate = VideoConversionHelper.getHighestPossibleBitrate(duration: duration,
- audioBitrate: Int(audioTrackRate),
- videoBitrate: Int(videoTrack.estimatedDataRate)) else {
- return nil
- }
-
- var targetVideoSize : CGSize = srcVideoSize;
- if (targetVideoSize.width > CGFloat(rate.videoSize)) {
- targetVideoSize.height *= (CGFloat(rate.videoSize) / targetVideoSize.width);
- targetVideoSize.width = CGFloat(rate.videoSize);
- }
- if (targetVideoSize.height > CGFloat(rate.videoSize)) {
- targetVideoSize.width *= (CGFloat(rate.videoSize) / targetVideoSize.height);
- targetVideoSize.height = CGFloat(rate.videoSize);
- }
-
- let videoWidth = roundf(Float(targetVideoSize.width / 16.0)) * 16
- let videoHeight = roundf(Float(targetVideoSize.height / 16.0)) * 16
-
- guard let exportSession = SDAVAssetExportSession(asset: asset) else {
- return nil
- }
-
- exportSession.outputURL = outputURL
- exportSession.shouldOptimizeForNetworkUse = true
- exportSession.outputFileType = AVFileType.mp4.rawValue
-
- exportSession.videoSettings = [
- AVVideoCodecKey : AVVideoCodecH264,
- AVVideoWidthKey : NSNumber(value: videoWidth),
- AVVideoHeightKey : NSNumber(value: videoHeight),
- AVVideoCompressionPropertiesKey : [
- AVVideoAverageBitRateKey : rate.videoRate,
- AVVideoProfileLevelKey : AVVideoProfileLevelH264Baseline31
- ],
- ]
-
- exportSession.audioSettings = [
- AVFormatIDKey : kAudioFormatMPEG4AAC,
- AVNumberOfChannelsKey : rate.audioChannels,
- AVSampleRateKey : 44100,
- AVEncoderBitRateKey : rate.audioRate
- ]
-
- return exportSession
- }
- }
|