react-native-compressor 1.8.16 → 1.8.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/README.md +36 -9
  2. package/android/src/main/java/com/reactnativecompressor/Audio/AudioCompressor.kt +58 -27
  3. package/android/src/main/java/com/reactnativecompressor/Audio/AudioHelper.kt +49 -0
  4. package/android/src/main/java/com/reactnativecompressor/Audio/AudioMain.kt +2 -6
  5. package/android/src/main/java/com/reactnativecompressor/Utils/createVideoThumbnail.kt +0 -2
  6. package/ios/Audio/AudioHelper.swift +49 -0
  7. package/ios/Audio/AudioMain.swift +46 -82
  8. package/ios/Audio/AudioOptions.swift +37 -0
  9. package/ios/Audio/FormatConverter/FormatConverter+Compressed.swift +326 -0
  10. package/ios/Audio/FormatConverter/FormatConverter+PCM.swift +239 -0
  11. package/ios/Audio/FormatConverter/FormatConverter+Utilities.swift +103 -0
  12. package/ios/Audio/FormatConverter/FormatConverter.swift +264 -0
  13. package/ios/Compressor.xcodeproj/xcuserdata/apple.xcuserdatad/xcschemes/xcschememanagement.plist +14 -0
  14. package/ios/Video/NextLevelSessionExporter.swift +694 -0
  15. package/ios/Video/VideoMain.swift +1 -2
  16. package/lib/commonjs/Audio/index.js +1 -3
  17. package/lib/commonjs/Audio/index.js.map +1 -1
  18. package/lib/commonjs/utils/index.js +0 -2
  19. package/lib/commonjs/utils/index.js.map +1 -1
  20. package/lib/module/Audio/index.js +1 -3
  21. package/lib/module/Audio/index.js.map +1 -1
  22. package/lib/module/utils/index.js +0 -3
  23. package/lib/module/utils/index.js.map +1 -1
  24. package/lib/typescript/Audio/index.d.ts.map +1 -1
  25. package/lib/typescript/utils/index.d.ts +4 -1
  26. package/lib/typescript/utils/index.d.ts.map +1 -1
  27. package/package.json +1 -1
  28. package/react-native-compressor.podspec +0 -1
  29. package/src/Audio/index.tsx +1 -3
  30. package/src/utils/index.tsx +5 -2
package/README.md CHANGED
@@ -129,9 +129,9 @@ react-native link react-native-compressor
129
129
 
130
130
  #### iOS
131
131
 
132
- 1. In XCode, in the project navigator, right click `Libraries` ➜ `Add Files to [your project's name]`
133
- 2. Go to `node_modules` `react-native-compressor` and add `Compressor.xcodeproj`
134
- 3. In XCode, in the project navigator, select your project. Add `libCompressor.a` to your project's `Build Phases` ➜ `Link Binary With Libraries`
132
+ 1. In XCode, open Podfile
133
+ 2. paste this line `pod 'react-native-compressor', :path => '../node_modules/react-native-compressor'` into `Podfile`
134
+ 3. run this command inside ios folder `pod install`
135
135
  4. Run your project (`Cmd+R`)<
136
136
 
137
137
  #### Android
@@ -270,6 +270,17 @@ const result = await Audio.compress(
270
270
  'file://path_of_file/file_example_MP3_2MG.wav', // recommended wav file but can be use mp3 file
271
271
  { quality: 'medium' }
272
272
  );
273
+
274
+ // OR
275
+
276
+ const result = await Audio.compress(
277
+ 'file://path_of_file/file_example_MP3_2MG.wav', // recommended wav file but can be use mp3 file
278
+ {
279
+ bitrate: 64000,
280
+ samplerate: 44100,
281
+ channels: 1,
282
+ }
283
+ );
273
284
  ```
274
285
 
275
286
  ### Background Upload
@@ -377,8 +388,14 @@ await clearCache(); // this will clear cache of thumbnails cache directory
377
388
  - ###### `compress(url: string, options?: videoCompresssionType , onProgress?: (progress: number)): Promise<string>`
378
389
 
379
390
  - ###### `cancelCompression(cancellationId: string): void`
391
+
380
392
  we can get cancellationId from `getCancellationId` which is the callback method of compress method options
381
393
 
394
+ - ###### `activateBackgroundTask(onExpired?: (data: any) => void): Promise<any>`
395
+ if you wanna compress video while app is in backgroup then you should call this method before compression
396
+ - ###### `deactivateBackgroundTask(): Promise<any>`
397
+ if you call `activateBackgroundTask` method, then after video compression, you should call `deactivateBackgroundTask` for disable background task mode.
398
+
382
399
  ### videoCompresssionType
383
400
 
384
401
  - ###### `compressionMethod: compressionMethod` (default: "manual")
@@ -415,10 +432,20 @@ await clearCache(); // this will clear cache of thumbnails cache directory
415
432
 
416
433
  ### audioCompresssionType
417
434
 
418
- - ###### `quality: qualityType` (default: medium)
435
+ - ###### `quality?: qualityType` (default: medium)
436
+
419
437
  we can also control bitrate through quality. qualityType can be `low` | `medium` | `high`
420
438
 
421
- **Note: manual bitrate, samplerate etc will add soon**
439
+ - ###### `bitrate?: number` Range [64000-320000]
440
+
441
+ we can control bitrate of audio through bitrate, it should be in the range of `64000-320000`
442
+
443
+ - ###### `samplerate?: number` Range [44100 - 192000]
444
+
445
+ we can control samplerate of audio through samplerate, it should be in the range of `44100 - 192000`
446
+
447
+ - ###### `channels?: number` Typically 1 or 2
448
+ we can control channels of audio through channels, Typically 1 or 2
422
449
 
423
450
  ## Background Upload
424
451
 
@@ -492,11 +519,11 @@ const metaData = await getVideoMetaData(filePath);
492
519
 
493
520
  ```
494
521
  {
495
- "duration": "6",
522
+ "duration": 20.11,
496
523
  "extension": "mp4",
497
- "height": "1080",
498
- "size": "16940.0",
499
- "width": "1920"
524
+ "height": 1080,
525
+ "size": 16940.0,
526
+ "width": 1920
500
527
  }
501
528
  ```
502
529
 
@@ -4,6 +4,7 @@ package com.reactnativecompressor.Audio
4
4
  import android.annotation.SuppressLint
5
5
  import com.facebook.react.bridge.Promise
6
6
  import com.facebook.react.bridge.ReactApplicationContext
7
+ import com.facebook.react.bridge.ReadableMap
7
8
  import com.naman14.androidlame.LameBuilder
8
9
  import com.naman14.androidlame.WaveReader
9
10
  import com.reactnativecompressor.Utils.MediaCache
@@ -27,21 +28,22 @@ class AudioCompressor {
27
28
  @JvmStatic
28
29
  fun CompressAudio(
29
30
  fileUrl: String,
30
- quality: String,
31
+ optionMap: ReadableMap,
31
32
  context: ReactApplicationContext,
32
33
  promise: Promise,
33
34
  ) {
34
- var _fileUrl=fileUrl
35
+ val realPath = Utils.getRealPath(fileUrl, context)
36
+ var _fileUrl=realPath
37
+ val filePathWithoutFileUri = realPath!!.replace("file://", "")
35
38
  try {
36
- val filePath = fileUrl.replace("file://", "")
37
- var wavPath=filePath;
39
+ var wavPath=filePathWithoutFileUri;
38
40
  var isNonWav:Boolean=false
39
41
  if (fileUrl.endsWith(".mp4", ignoreCase = true))
40
42
  {
41
43
  addLog("mp4 file found")
42
44
  val mp3Path= Utils.generateCacheFilePath("mp3", context)
43
45
  AudioExtractor().genVideoUsingMuxer(fileUrl, mp3Path, -1, -1, true, false)
44
- _fileUrl="file:/"+mp3Path
46
+ _fileUrl=Utils.slashifyFilePath(mp3Path)
45
47
  wavPath= Utils.generateCacheFilePath("wav", context)
46
48
  try {
47
49
  val converter = Converter()
@@ -58,7 +60,7 @@ class AudioCompressor {
58
60
  wavPath= Utils.generateCacheFilePath("wav", context)
59
61
  try {
60
62
  val converter = Converter()
61
- converter.convert(filePath, wavPath)
63
+ converter.convert(filePathWithoutFileUri, wavPath)
62
64
  } catch (e: JavaLayerException) {
63
65
  addLog("JavaLayerException error"+e.localizedMessage)
64
66
  e.printStackTrace();
@@ -67,7 +69,7 @@ class AudioCompressor {
67
69
  }
68
70
 
69
71
 
70
- autoCompressHelper(wavPath, quality,context) { mp3Path, finished ->
72
+ autoCompressHelper(wavPath,filePathWithoutFileUri, optionMap,context) { mp3Path, finished ->
71
73
  if (finished) {
72
74
  val returnableFilePath:String="file://$mp3Path"
73
75
  addLog("finished: " + returnableFilePath)
@@ -87,28 +89,23 @@ class AudioCompressor {
87
89
  }
88
90
  }
89
91
 
90
- private fun getAudioBitrateByQuality(quality: String): Int {
91
- return when (quality) {
92
- "low" -> 64 // Set your low bitrate value here
93
- "medium" -> 128 // Set your medium bitrate value here
94
- "high" -> 256 // Set your high bitrate value here
95
- else -> 128 // Default to medium bitrate if quality is not recognized
96
- }
97
- }
98
-
99
92
  @SuppressLint("WrongConstant")
100
93
  private fun autoCompressHelper(
101
94
  fileUrl: String,
102
- quality: String,
95
+ actualFileUrl: String,
96
+ optionMap: ReadableMap,
103
97
  context: ReactApplicationContext,
104
98
  completeCallback: (String, Boolean) -> Unit
105
99
  ) {
100
+
101
+ val options = AudioHelper.fromMap(optionMap)
102
+ val quality = options.quality
103
+
106
104
  var isCompletedCallbackTriggered:Boolean=false
107
105
  try {
108
106
  var mp3Path = Utils.generateCacheFilePath("mp3", context)
109
107
  val input = File(fileUrl)
110
108
  val output = File(mp3Path)
111
- val audioBitrate= getAudioBitrateByQuality(quality)
112
109
 
113
110
  val CHUNK_SIZE = 8192
114
111
  addLog("Initialising wav reader")
@@ -122,12 +119,46 @@ class AudioCompressor {
122
119
  }
123
120
 
124
121
  addLog("Intitialising encoder")
125
- val androidLame = LameBuilder()
126
- .setInSampleRate(waveReader!!.sampleRate)
127
- .setOutChannels(waveReader!!.channels)
128
- .setOutBitrate(audioBitrate)
129
- .setOutSampleRate(waveReader!!.sampleRate)
130
- .build()
122
+
123
+
124
+ // for bitrate
125
+ var audioBitrate:Int
126
+ if(options.bitrate != -1)
127
+ {
128
+ audioBitrate= options.bitrate/1000
129
+ }
130
+ else
131
+ {
132
+ audioBitrate=AudioHelper.getDestinationBitrateByQuality(actualFileUrl, quality!!)
133
+ Utils.addLog("dest bitrate: $audioBitrate")
134
+ }
135
+
136
+ var androidLame = LameBuilder();
137
+ androidLame.setOutBitrate(audioBitrate)
138
+
139
+ // for channels
140
+ var audioChannels:Int
141
+ if(options.channels != -1){
142
+ audioChannels= options.channels!!
143
+ }
144
+ else
145
+ {
146
+ audioChannels=waveReader!!.channels
147
+ }
148
+ androidLame.setOutChannels(audioChannels)
149
+
150
+ // for sample rate
151
+ androidLame.setInSampleRate(waveReader!!.sampleRate)
152
+ var audioSampleRate:Int
153
+ if(options.samplerate != -1){
154
+ audioSampleRate= options.samplerate!!
155
+ }
156
+ else
157
+ {
158
+ audioSampleRate=waveReader!!.sampleRate
159
+ }
160
+ androidLame.setOutSampleRate(audioSampleRate)
161
+ val androidLameBuild=androidLame.build()
131
162
 
132
163
  try {
133
164
  outputStream = BufferedOutputStream(FileOutputStream(output), OUTPUT_STREAM_BUFFER)
@@ -154,7 +185,7 @@ class AudioCompressor {
154
185
  if (bytesRead > 0) {
155
186
 
156
187
  var bytesEncoded = 0
157
- bytesEncoded = androidLame.encode(buffer_l, buffer_r, bytesRead, mp3Buf)
188
+ bytesEncoded = androidLameBuild.encode(buffer_l, buffer_r, bytesRead, mp3Buf)
158
189
  addLog("bytes encoded=$bytesEncoded")
159
190
 
160
191
  if (bytesEncoded > 0) {
@@ -177,7 +208,7 @@ class AudioCompressor {
177
208
  if (bytesRead > 0) {
178
209
  var bytesEncoded = 0
179
210
 
180
- bytesEncoded = androidLame.encode(buffer_l, buffer_l, bytesRead, mp3Buf)
211
+ bytesEncoded = androidLameBuild.encode(buffer_l, buffer_l, bytesRead, mp3Buf)
181
212
  addLog("bytes encoded=$bytesEncoded")
182
213
 
183
214
  if (bytesEncoded > 0) {
@@ -202,7 +233,7 @@ class AudioCompressor {
202
233
  }
203
234
 
204
235
  addLog("flushing final mp3buffer")
205
- val outputMp3buf = androidLame.flush(mp3Buf)
236
+ val outputMp3buf = androidLameBuild.flush(mp3Buf)
206
237
  addLog("flushed $outputMp3buf bytes")
207
238
  if (outputMp3buf > 0) {
208
239
  try {
@@ -1,10 +1,19 @@
1
1
  package com.reactnativecompressor.Audio
2
2
 
3
+ import android.media.MediaExtractor
4
+ import android.media.MediaFormat
3
5
  import com.facebook.react.bridge.ReadableMap
6
+ import com.reactnativecompressor.Utils.Utils
7
+ import java.io.File
8
+ import java.io.IOException
9
+
4
10
 
5
11
  class AudioHelper {
6
12
 
7
13
  var quality: String? = "medium"
14
+ var bitrate: Int = -1
15
+ var samplerate: Int = -1
16
+ var channels: Int = -1
8
17
  var progressDivider: Int? = 0
9
18
 
10
19
  companion object {
@@ -15,9 +24,49 @@ class AudioHelper {
15
24
  val key = iterator.nextKey()
16
25
  when (key) {
17
26
  "quality" -> options.quality = map.getString(key)
27
+ "bitrate" -> {
28
+ val bitrate = map.getInt(key)
29
+ options.bitrate = if (bitrate > 320000 || bitrate < 64000) 64000 else bitrate
30
+ }
31
+ "samplerate" -> options.samplerate = map.getInt(key)
32
+ "channels" -> options.channels = map.getInt(key)
18
33
  }
19
34
  }
20
35
  return options
21
36
  }
37
+
38
+
39
+ fun getAudioBitrate(path: String): Int {
40
+ val file = File(path)
41
+ val fileSize = file.length() * 8 // size in bits
42
+
43
+ val mex = MediaExtractor()
44
+ try {
45
+ mex.setDataSource(path)
46
+ } catch (e: IOException) {
47
+ e.printStackTrace()
48
+ }
49
+
50
+ val mf = mex.getTrackFormat(0)
51
+ val durationUs = mf.getLong(MediaFormat.KEY_DURATION)
52
+ val durationSec = durationUs / 1_000_000.0 // convert duration to seconds
53
+
54
+ return (fileSize / durationSec).toInt()/1000 // bitrate in bits per second
55
+ }
56
+ fun getDestinationBitrateByQuality(path: String, quality: String): Int {
57
+ val originalBitrate = getAudioBitrate(path)
58
+ var destinationBitrate = originalBitrate
59
+ Utils.addLog("source bitrate: $originalBitrate")
60
+
61
+ when (quality.toLowerCase()) {
62
+ "low" -> destinationBitrate = maxOf(64, (originalBitrate * 0.3).toInt())
63
+ "medium" -> destinationBitrate = (originalBitrate * 0.5).toInt()
64
+ "high" -> destinationBitrate = minOf(320, (originalBitrate * 0.7).toInt())
65
+ else -> Utils.addLog("Invalid quality level. Please enter 'low', 'medium', or 'high'.")
66
+ }
67
+
68
+ return destinationBitrate
69
+ }
70
+
22
71
  }
23
72
  }
@@ -3,7 +3,6 @@ package com.reactnativecompressor.Audio
3
3
  import com.facebook.react.bridge.Promise
4
4
  import com.facebook.react.bridge.ReactApplicationContext
5
5
  import com.facebook.react.bridge.ReadableMap
6
- import com.reactnativecompressor.Utils.Utils
7
6
 
8
7
  class AudioMain(private val reactContext: ReactApplicationContext) {
9
8
  fun compress_audio(
@@ -11,11 +10,8 @@ class AudioMain(private val reactContext: ReactApplicationContext) {
11
10
  optionMap: ReadableMap,
12
11
  promise: Promise) {
13
12
  try {
14
- val options = AudioHelper.fromMap(optionMap)
15
- val quality = options.quality
16
- val realPath = Utils.getRealPath(fileUrl, reactContext)
17
- Utils.addLog(fileUrl + "\n realPath= " + realPath)
18
- AudioCompressor.CompressAudio(realPath!!, quality!!,reactContext,promise)
13
+
14
+ AudioCompressor.CompressAudio(fileUrl,optionMap,reactContext,promise)
19
15
  } catch (ex: Exception) {
20
16
  promise.reject(ex)
21
17
  }
@@ -8,7 +8,6 @@ import android.net.Uri
8
8
  import android.os.Build
9
9
  import android.text.TextUtils
10
10
  import android.webkit.URLUtil
11
- import androidx.annotation.RequiresApi
12
11
  import com.facebook.react.bridge.Arguments
13
12
  import com.facebook.react.bridge.GuardedResultAsyncTask
14
13
  import com.facebook.react.bridge.Promise
@@ -39,7 +38,6 @@ class CreateVideoThumbnailClass(private val reactContext: ReactApplicationContex
39
38
  weakContext = WeakReference(reactContext.applicationContext)
40
39
  }
41
40
 
42
- @RequiresApi(Build.VERSION_CODES.N)
43
41
  override fun doInBackgroundGuarded(): ReadableMap? {
44
42
  val format = "jpeg"
45
43
  val cacheName = if (options.hasKey("cacheName")) options.getString("cacheName") else ""
@@ -0,0 +1,49 @@
1
+ //
2
+ // AudioHelper.swift
3
+ // react-native-compressor
4
+ //
5
+ // Created by Numan on 12/11/2023.
6
+ //
7
+
8
+ import AVFoundation
9
+
10
+ class AudioHelper {
11
+
12
+ static func getAudioBitrate(path: String) -> Int {
13
+ let audioURL = URL(fileURLWithPath: path)
14
+ let avAsset = AVURLAsset(url: audioURL)
15
+ let keys: Set<URLResourceKey> = [.totalFileSizeKey, .fileSizeKey]
16
+ let resourceValues = try? audioURL.resourceValues(forKeys: keys)
17
+ let fileSize = resourceValues?.fileSize ?? resourceValues?.totalFileSize
18
+
19
+ // Calculate bitrate in kbps
20
+ if let fileSize = fileSize, avAsset.duration.seconds > 0 {
21
+ return Int(Double(fileSize) * 8 / avAsset.duration.seconds)
22
+ }
23
+ return 0
24
+ }
25
+
26
+ static func getDestinationBitrateByQuality(path: String, quality: String) -> Int {
27
+ let originalBitrate = getAudioBitrate(path: path)
28
+ var destinationBitrate = originalBitrate
29
+ print("source bitrate: \(originalBitrate)")
30
+
31
+ // Calculate the percentage of the original bitrate relative to the range 64000 to 320000
32
+ let percentage = Double(originalBitrate - 64000) / Double(320000 - 64000)
33
+
34
+ switch quality.lowercased() {
35
+ case "low":
36
+ destinationBitrate = max(64000, Int(Double(originalBitrate) * 0.3))
37
+ case "medium":
38
+ // Set destination bitrate to 60% of the original bitrate
39
+ destinationBitrate = Int(Double(originalBitrate) * 0.5)
40
+ case "high":
41
+ // Set destination bitrate to 80% of the original bitrate
42
+ destinationBitrate = min(320000,Int(Double(originalBitrate) * 0.7))
43
+ default:
44
+ print("Invalid quality level. Please enter 'low', 'medium', or 'high'.")
45
+ }
46
+
47
+ return destinationBitrate
48
+ }
49
+ }
@@ -5,16 +5,21 @@
5
5
  // Created by Numan on 10/09/2023.
6
6
  //
7
7
 
8
- import Foundation
8
+
9
9
  import AVFoundation
10
+
11
+
12
+
10
13
  let AlAsset_Library_Scheme = "assets-library"
11
14
  class AudioMain{
12
15
  static func compress_audio(_ fileUrl: String, optionMap: NSDictionary, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
13
16
  do {
14
17
  var fileUrl = fileUrl
18
+
15
19
  if fileUrl.hasPrefix("file://") {
16
20
  fileUrl = fileUrl.replacingOccurrences(of: "file://", with: "")
17
21
  }
22
+
18
23
  let fileManager = FileManager.default
19
24
  var isDir: ObjCBool = false
20
25
  if !fileManager.fileExists(atPath: fileUrl, isDirectory: &isDir) || isDir.boolValue {
@@ -23,93 +28,52 @@ class AudioMain{
23
28
  return
24
29
  }
25
30
 
26
- let assetOptions: [String: Any] = [AVURLAssetPreferPreciseDurationAndTimingKey: true]
27
- let asset = AVURLAsset(url: URL(fileURLWithPath: fileUrl), options: assetOptions)
28
- let quality = optionMap["quality"] as? String ?? ""
29
- let qualityConstant = getAudioQualityConstant(quality)
30
- auto_compress_helper(asset, qualityConstant: qualityConstant) { mp3Path, finished in
31
- if finished {
32
- resolve("file://\(mp3Path ?? "")")
33
- } else {
34
- reject("Error", "Something went wrong", nil)
35
- }
36
- }
37
- } catch {
38
- reject(error.localizedDescription, error.localizedDescription, nil)
39
- }
40
- }
41
-
42
- static func getAudioQualityConstant(_ quality: String) -> String {
43
- let audioQualityArray = ["low", "medium", "high"]
44
- if let index = audioQualityArray.firstIndex(of: quality) {
45
- switch index {
46
- case 0:
47
- return AVAssetExportPresetLowQuality
48
- case 1:
49
- return AVAssetExportPresetMediumQuality
50
- case 2:
51
- return AVAssetExportPresetHighestQuality
52
- default:
53
- return AVAssetExportPresetMediumQuality
31
+ let audioOptions = AudioOptions.fromDictionary((optionMap as! [String : Any]))
32
+ let outputMp3Path = "file://\(Utils.generateCacheFilePath("m4a"))"
33
+
34
+ let oldUfileUrlRL = URL(string: fileUrl)!
35
+ let newURL = URL(string: outputMp3Path)!
36
+
37
+ var options = FormatConverter.Options()
38
+
39
+ if(audioOptions.samplerate != -1){
40
+ options.sampleRate = Double(audioOptions.samplerate)
54
41
  }
55
- }
56
- return AVAssetExportPresetMediumQuality
57
- }
58
-
59
- static func auto_compress_helper(_ avAsset: AVURLAsset, qualityConstant: String, complete completeCallback: @escaping (_ mp3Path: String?, _ finished: Bool) -> Void) {
60
- var path: String
61
- if avAsset.url.scheme == AlAsset_Library_Scheme {
62
- path = avAsset.url.query ?? ""
63
- if path.isEmpty {
64
- completeCallback(nil, false)
65
- return
42
+
43
+ if(audioOptions.bitrate != -1){
44
+ options.bitRate = UInt32(audioOptions.bitrate)
66
45
  }
67
- } else {
68
- path = avAsset.url.path
69
- if path.isEmpty || !FileManager.default.fileExists(atPath: path) {
70
- completeCallback(nil, false)
71
- return
46
+ else
47
+ {
48
+ let bitrate = AudioHelper.getDestinationBitrateByQuality(path: fileUrl,quality: audioOptions.quality)
49
+ print("output bitrate: \(bitrate)")
50
+ options.bitRate = UInt32(bitrate)
72
51
  }
73
- }
74
-
75
- let mp3Path = Utils.generateCacheFilePath("m4a")
76
-
77
- if FileManager.default.fileExists(atPath: mp3Path) {
78
- completeCallback(mp3Path, true)
79
- return
80
- }
81
-
82
- let compatiblePresets = AVAssetExportSession.exportPresets(compatibleWith: avAsset)
83
-
84
- if compatiblePresets.contains(qualityConstant) {
85
- let exportSession = AVAssetExportSession(asset: avAsset, presetName: AVAssetExportPresetAppleM4A)
86
52
 
87
- let mp3Url = URL(fileURLWithPath: mp3Path)
88
- exportSession?.outputURL = mp3Url
89
- exportSession?.shouldOptimizeForNetworkUse = true
90
- exportSession?.outputFileType = .m4a
91
53
 
92
- exportSession?.exportAsynchronously(completionHandler: {
93
- var finished = false
94
- switch exportSession?.status {
95
- case .failed:
96
- print("AVAssetExportSessionStatusFailed, error: \(exportSession?.error?.localizedDescription ?? "").")
97
- case .cancelled:
98
- print("AVAssetExportSessionStatusCancelled.")
99
- case .completed:
100
- print("AVAssetExportSessionStatusCompleted.")
101
- finished = true
102
- case .unknown:
103
- print("AVAssetExportSessionStatusUnknown")
104
- case .waiting:
105
- print("AVAssetExportSessionStatusWaiting")
106
- case .exporting:
107
- print("AVAssetExportSessionStatusExporting")
108
- default:
109
- break
54
+ if(audioOptions.channels != -1){
55
+ options.channels = UInt32(audioOptions.channels)
56
+ }
57
+
58
+ options.format = .m4a
59
+ options.eraseFile = false
60
+ options.bitDepthRule = .any
61
+
62
+ let converter = FormatConverter(inputURL: oldUfileUrlRL, outputURL: newURL, options: options)
63
+ converter.start { error in
64
+ // check to see if error isn't nil, otherwise you're good
65
+ if((error) != nil)
66
+ {
67
+ print("error=> \(error?.localizedDescription)")
68
+ reject(error?.localizedDescription,error?.localizedDescription, error)
69
+ return
110
70
  }
111
- completeCallback(mp3Path, finished)
112
- })
71
+
72
+ resolve(newURL.absoluteString)
73
+ }
74
+
75
+ } catch {
76
+ reject(error.localizedDescription, error.localizedDescription, nil)
113
77
  }
114
78
  }
115
79
  }
@@ -0,0 +1,37 @@
1
+ class AudioOptions: NSObject {
2
+ static func fromDictionary(_ dictionary: [String: Any]?) -> AudioOptions {
3
+ let options = AudioOptions()
4
+
5
+ guard let dictionary = dictionary else {
6
+ return options
7
+ }
8
+
9
+ for (key, value) in dictionary {
10
+ switch key {
11
+ case "quality":
12
+ options.quality = (value as? String) ?? "medium"
13
+ case "bitrate":
14
+ options.bitrate = (value as? Int) ?? -1
15
+ case "samplerate":
16
+ options.samplerate = (value as? Int) ?? -1
17
+ case "channels":
18
+ options.channels = (value as? Int) ?? -1
19
+
20
+ default:
21
+ break
22
+ }
23
+ }
24
+
25
+ return options
26
+ }
27
+
28
+ var quality: String = "medium"
29
+ var bitrate: Int = -1
30
+ var samplerate: Int = -1
31
+ var channels: Int = -1
32
+
33
+
34
+ override init() {
35
+ super.init()
36
+ }
37
+ }