@@ -70,7 +70,7 @@ class VideoCompressor: RCTEventEmitter, URLSessionTaskDelegate {
7070
7171 @objc ( compress: withOptions: withResolver: withRejecter: )
7272 func compress( fileUrl: String , options: [ String : Any ] , resolve: @escaping RCTPromiseResolveBlock , reject: @escaping RCTPromiseRejectBlock ) -> Void {
73- compressVideo ( url: URL ( string: fileUrl) !, bitRate : options [ " bitrate " ] as! Float ? ,
73+ compressVideo ( url: URL ( string: fileUrl) !, options : options,
7474 onProgress: { progress in
7575 print ( " Progress " , progress)
7676 if ( self . hasListener) {
@@ -176,101 +176,275 @@ func makeValidUri(filePath: String) -> String {
176176 override func startObserving( ) -> Void {
177177 hasListener = true
178178 }
179+
180+ func getfileSize( forURL url: Any ) -> Double {
181+ var fileURL : URL ?
182+ var fileSize : Double = 0.0
183+ if ( url is URL ) || ( url is String )
184+ {
185+ if ( url is URL ) {
186+ fileURL = url as? URL
187+ }
188+ else {
189+ fileURL = URL ( fileURLWithPath: url as! String )
190+ }
191+ var fileSizeValue = 0.0
192+ try ? fileSizeValue = ( fileURL? . resourceValues ( forKeys: [ URLResourceKey . fileSizeKey] ) . allValues. first? . value as! Double ? ) !
193+ if fileSizeValue > 0.0 {
194+ fileSize = ( Double ( fileSizeValue) / ( 1024 * 1024 ) )
195+ }
196+ }
197+ return fileSize
198+ }
179199
180200
181- func compressVideo( url: URL , bitRate: Float ? , onProgress: @escaping ( Float ) -> Void , onCompletion: @escaping ( URL ) -> Void , onFailure: @escaping ( Error ) -> Void ) {
182- var tmpURL = URL ( fileURLWithPath: NSTemporaryDirectory ( ) , isDirectory: true )
183- . appendingPathComponent ( ProcessInfo ( ) . globallyUniqueString)
184- . appendingPathExtension ( " mp4 " )
185- tmpURL = URL ( string: makeValidUri ( filePath: tmpURL. absoluteString) ) !
201+ func compressVideo( url: URL , options: [ String : Any ] , onProgress: @escaping ( Float ) -> Void , onCompletion: @escaping ( URL ) -> Void , onFailure: @escaping ( Error ) -> Void ) {
186202
187- var _bitRate = bitRate;
188- let asset = AVAsset ( url: url)
189- guard asset. tracks. count >= 1 else {
190- let error = CompressionError ( message: " Invalid video URL, no track found " )
191- onFailure ( error)
192- return
193- }
194- var videoTrackIndex : Int = 0 ;
195- let trackLength = asset. tracks. count;
196- if ( trackLength== 2 )
203+ let fileSize = self . getfileSize ( forURL: url) ;
204+
205+ if ( fileSize> 16 )
197206 {
198- if ( asset . tracks [ 0 ] . mediaType . rawValue == " soun " )
207+ if ( options [ " compressionMethod " ] as! String == " auto " )
199208 {
200- videoTrackIndex= 1 ;
209+ autoCompressionHelper ( url: url, options: options) { progress in
210+ onProgress ( progress)
211+ } onCompletion: { outputURL in
212+ onCompletion ( outputURL)
213+ } onFailure: { error in
214+ onFailure ( error)
215+ }
201216 }
202- }
203- let track = asset. tracks [ videoTrackIndex] ;
204- let exporter = NextLevelSessionExporter ( withAsset: asset)
205- exporter. outputURL = tmpURL
206- exporter. outputFileType = AVFileType . mp4
207-
208- let videoSize = track. naturalSize. applying ( track. preferredTransform) ;
209- var width = Float ( abs ( videoSize. width) )
210- var height = Float ( abs ( videoSize. height) )
211- let isPortrait = height > width
212- let maxSize = Float ( 1920 ) ;
213- if ( isPortrait && height > maxSize) {
214- width = ( maxSize/ height) * width
215- height = maxSize
216- } else if ( width > maxSize) {
217- height = ( maxSize/ width) * height
218- width = maxSize
217+ else
218+ {
219+ manualCompressionHelper ( url: url, bitRate: options [ " bitrate " ] as! Float ? ) { progress in
220+ onProgress ( progress)
221+ } onCompletion: { outputURL in
222+ onCompletion ( outputURL)
223+ } onFailure: { error in
224+ onFailure ( error)
225+ }
226+ }
227+
228+
229+
219230 }
220231 else
221232 {
222- _bitRate = bitRate ?? Float ( abs ( track . estimatedDataRate ) ) * 0.8
233+ onCompletion ( url )
223234 }
224-
225- let videoBitRate = _bitRate ?? height*width*1. 5
226-
227- let compressionDict : [ String : Any ] = [
228- AVVideoAverageBitRateKey: videoBitRate,
229- AVVideoProfileLevelKey: AVVideoProfileLevelH264HighAutoLevel,
230- ]
231- exporter. optimizeForNetworkUse = true ;
232- exporter. videoOutputConfiguration = [
233- AVVideoCodecKey: AVVideoCodecType . h264,
234- AVVideoWidthKey: width,
235- AVVideoHeightKey: height,
236- AVVideoScalingModeKey: AVVideoScalingModeResizeAspectFill,
237- AVVideoCompressionPropertiesKey: compressionDict
238- ]
239- exporter. audioOutputConfiguration = [
240- AVFormatIDKey: kAudioFormatMPEG4AAC,
241- AVEncoderBitRateKey: NSNumber ( integerLiteral: 128000 ) ,
242- AVNumberOfChannelsKey: NSNumber ( integerLiteral: 2 ) ,
243- AVSampleRateKey: NSNumber ( value: Float ( 44100 ) )
244- ]
245235
236+
237+
238+ }
239+
240+
241+ func makeVideoBitrate( originalHeight: Int , originalWidth: Int , originalBitrate: Int , height: Int , width: Int ) -> Int {
242+ let compressFactor : Float = 0.8
243+ let minCompressFactor : Float = 0.8
244+ let maxBitrate : Int = 1669000
246245
247- exporter. export ( progressHandler: { ( progress) in
248- let _progress : Float = progress*100;
249- if ( Int ( _progress) == self . videoCompressionCounter)
246+ var remeasuredBitrate : Int = originalBitrate / ( min ( originalHeight/ height, originalWidth/ width) )
247+ remeasuredBitrate = remeasuredBitrate*Int ( compressFactor)
248+ let minBitrate : Int = self . getVideoBitrateWithFactor ( f: minCompressFactor) / ( 1280 * 720 / ( width * height) )
249+ if ( originalBitrate < minBitrate) {
250+ return remeasuredBitrate;
251+ }
252+ if ( remeasuredBitrate > maxBitrate) {
253+ return maxBitrate;
254+ }
255+ return max ( remeasuredBitrate, minBitrate) ;
256+ }
257+ func getVideoBitrateWithFactor( f: Float ) -> Int {
258+ return Int ( f * 2000 * 1000 * 1.13 ) ;
259+ }
260+
261+ func autoCompressionHelper( url: URL , options: [ String : Any ] , onProgress: @escaping ( Float ) -> Void , onCompletion: @escaping ( URL ) -> Void , onFailure: @escaping ( Error ) -> Void ) {
262+ var bitRate = options [ " bitrate " ] as! Float ? ;
263+ var tmpURL = URL ( fileURLWithPath: NSTemporaryDirectory ( ) , isDirectory: true )
264+ . appendingPathComponent ( ProcessInfo ( ) . globallyUniqueString)
265+ . appendingPathExtension ( " mp4 " )
266+ tmpURL = URL ( string: makeValidUri ( filePath: tmpURL. absoluteString) ) !
267+
268+ let maxSize = 640.0 ;
269+ var _bitRate = bitRate;
270+ let asset = AVAsset ( url: url)
271+ guard asset. tracks. count >= 1 else {
272+ let error = CompressionError ( message: " Invalid video URL, no track found " )
273+ onFailure ( error)
274+ return
275+ }
276+ var videoTrackIndex : Int = 0 ;
277+ let trackLength = asset. tracks. count;
278+ if ( trackLength== 2 )
250279 {
251- self . videoCompressionCounter= Int ( _progress) + self . videoCompressionThreshold
252- onProgress ( progress)
280+ if ( asset. tracks [ 0 ] . mediaType. rawValue== " soun " )
281+ {
282+ videoTrackIndex= 1 ;
283+ }
253284 }
285+ let track = asset. tracks [ videoTrackIndex] ;
286+ let exporter = NextLevelSessionExporter ( withAsset: asset)
287+ exporter. outputURL = tmpURL
288+ exporter. outputFileType = AVFileType . mp4
254289
255- } , completionHandler: { result in
256- self . videoCompressionCounter= 0 ;
257- switch result {
258- case . success( let status) :
259- switch status {
260- case . completed:
261- onCompletion ( exporter. outputURL!)
262- break
263- default :
264- let error = CompressionError ( message: " Compression didn't complete " )
290+ let videoSize = track. naturalSize. applying ( track. preferredTransform) ;
291+ var actualWidth = Float ( abs ( videoSize. width) )
292+ var actualHeight = Float ( abs ( videoSize. height) )
293+
294+ let bitrate = Float ( abs ( track. estimatedDataRate) ) ;
295+ let scale : Float = actualWidth > actualHeight ? ( Float ( maxSize) / actualWidth) : ( Float ( maxSize) / actualHeight) ;
296+ let resultWidth : Float = round ( actualWidth * scale / 2 ) * 2 ;
297+ let resultHeight : Float = round ( actualHeight * scale / 2 ) * 2 ;
298+
299+ let videoBitRate : Int = self . makeVideoBitrate (
300+ originalHeight: Int ( actualHeight) , originalWidth: Int ( actualWidth) ,
301+ originalBitrate: Int ( bitrate) ,
302+ height: Int ( resultHeight) , width: Int ( resultWidth)
303+ ) ;
304+
305+
306+ let compressionDict : [ String : Any ] = [
307+ AVVideoAverageBitRateKey: videoBitRate,
308+ AVVideoProfileLevelKey: AVVideoProfileLevelH264HighAutoLevel,
309+ ]
310+ exporter. optimizeForNetworkUse = true ;
311+ exporter. videoOutputConfiguration = [
312+ AVVideoCodecKey: AVVideoCodecType . h264,
313+ AVVideoWidthKey: resultWidth,
314+ AVVideoHeightKey: resultHeight,
315+ AVVideoScalingModeKey: AVVideoScalingModeResizeAspectFill,
316+ AVVideoCompressionPropertiesKey: compressionDict
317+ ]
318+ exporter. audioOutputConfiguration = [
319+ AVFormatIDKey: kAudioFormatMPEG4AAC,
320+ AVEncoderBitRateKey: NSNumber ( integerLiteral: 128000 ) ,
321+ AVNumberOfChannelsKey: NSNumber ( integerLiteral: 2 ) ,
322+ AVSampleRateKey: NSNumber ( value: Float ( 44100 ) )
323+ ]
324+
325+
326+ exporter. export ( progressHandler: { ( progress) in
327+ let _progress : Float = progress*100;
328+ if ( Int ( _progress) == self . videoCompressionCounter)
329+ {
330+ self . videoCompressionCounter= Int ( _progress) + self . videoCompressionThreshold
331+ onProgress ( progress)
332+ }
333+
334+ } , completionHandler: { result in
335+ self . videoCompressionCounter= 0 ;
336+ switch result {
337+ case . success( let status) :
338+ switch status {
339+ case . completed:
340+ onCompletion ( exporter. outputURL!)
341+ break
342+ default :
343+ let error = CompressionError ( message: " Compression didn't complete " )
344+ onFailure ( error)
345+ break
346+ }
347+ break
348+ case . failure( let error) :
349+ onFailure ( error)
350+ break
351+ }
352+ } )
353+ }
354+
355+
356+ func manualCompressionHelper( url: URL , bitRate: Float ? , onProgress: @escaping ( Float ) -> Void , onCompletion: @escaping ( URL ) -> Void , onFailure: @escaping ( Error ) -> Void ) {
357+ var tmpURL = URL ( fileURLWithPath: NSTemporaryDirectory ( ) , isDirectory: true )
358+ . appendingPathComponent ( ProcessInfo ( ) . globallyUniqueString)
359+ . appendingPathExtension ( " mp4 " )
360+ tmpURL = URL ( string: makeValidUri ( filePath: tmpURL. absoluteString) ) !
361+
362+ var _bitRate = bitRate;
363+ let asset = AVAsset ( url: url)
364+ guard asset. tracks. count >= 1 else {
365+ let error = CompressionError ( message: " Invalid video URL, no track found " )
265366 onFailure ( error)
266- break
367+ return
368+ }
369+ var videoTrackIndex : Int = 0 ;
370+ let trackLength = asset. tracks. count;
371+ if ( trackLength== 2 )
372+ {
373+ if ( asset. tracks [ 0 ] . mediaType. rawValue== " soun " )
374+ {
375+ videoTrackIndex= 1 ;
376+ }
267377 }
268- break
269- case . failure( let error) :
270- onFailure ( error)
271- break
378+ let track = asset. tracks [ videoTrackIndex] ;
379+ let exporter = NextLevelSessionExporter ( withAsset: asset)
380+ exporter. outputURL = tmpURL
381+ exporter. outputFileType = AVFileType . mp4
382+
383+ let videoSize = track. naturalSize. applying ( track. preferredTransform) ;
384+ var width = Float ( abs ( videoSize. width) )
385+ var height = Float ( abs ( videoSize. height) )
386+ let isPortrait = height > width
387+ let maxSize = Float ( 1920 ) ;
388+ if ( isPortrait && height > maxSize) {
389+ width = ( maxSize/ height) * width
390+ height = maxSize
391+ } else if ( width > maxSize) {
392+ height = ( maxSize/ width) * height
393+ width = maxSize
394+ }
395+ else
396+ {
397+ _bitRate= bitRate ?? Float ( abs ( track. estimatedDataRate) ) * 0.8
398+ }
399+
400+ let videoBitRate = _bitRate ?? height*width*1. 5
401+
402+ let compressionDict : [ String : Any ] = [
403+ AVVideoAverageBitRateKey: videoBitRate,
404+ AVVideoProfileLevelKey: AVVideoProfileLevelH264HighAutoLevel,
405+ ]
406+ exporter. optimizeForNetworkUse = true ;
407+ exporter. videoOutputConfiguration = [
408+ AVVideoCodecKey: AVVideoCodecType . h264,
409+ AVVideoWidthKey: width,
410+ AVVideoHeightKey: height,
411+ AVVideoScalingModeKey: AVVideoScalingModeResizeAspectFill,
412+ AVVideoCompressionPropertiesKey: compressionDict
413+ ]
414+ exporter. audioOutputConfiguration = [
415+ AVFormatIDKey: kAudioFormatMPEG4AAC,
416+ AVEncoderBitRateKey: NSNumber ( integerLiteral: 128000 ) ,
417+ AVNumberOfChannelsKey: NSNumber ( integerLiteral: 2 ) ,
418+ AVSampleRateKey: NSNumber ( value: Float ( 44100 ) )
419+ ]
420+
421+
422+ exporter. export ( progressHandler: { ( progress) in
423+ let _progress : Float = progress*100;
424+ if ( Int ( _progress) == self . videoCompressionCounter)
425+ {
426+ self . videoCompressionCounter= Int ( _progress) + self . videoCompressionThreshold
427+ onProgress ( progress)
428+ }
429+
430+ } , completionHandler: { result in
431+ self . videoCompressionCounter= 0 ;
432+ switch result {
433+ case . success( let status) :
434+ switch status {
435+ case . completed:
436+ onCompletion ( exporter. outputURL!)
437+ break
438+ default :
439+ let error = CompressionError ( message: " Compression didn't complete " )
440+ onFailure ( error)
441+ break
442+ }
443+ break
444+ case . failure( let error) :
445+ onFailure ( error)
446+ break
447+ }
448+ } )
272449 }
273- } )
274- }
275-
276- }
450+ }
0 commit comments