react-native-compressor 1.16.2 → 1.18.0

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.
@@ -67,6 +67,9 @@ android {
67
67
  targetSdkVersion getExtOrIntegerDefault("targetSdkVersion")
68
68
  buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()
69
69
  }
70
+ buildFeatures {
71
+ buildConfig true
72
+ }
70
73
  buildTypes {
71
74
  release {
72
75
  minifyEnabled false
@@ -29,12 +29,12 @@ object Utils {
29
29
  }
30
30
 
31
31
  @JvmStatic
32
- fun compressVideo(srcPath: String, destinationPath: String, resultWidth: Int, resultHeight: Int, videoBitRate: Float, uuid: String,progressDivider:Int, promise: Promise, reactContext: ReactApplicationContext) {
32
+ fun compressVideo(srcPath: String, destinationPath: String, resultWidth: Int, resultHeight: Int, videoBitRate: Float, frameRate: Int, uuid: String,progressDivider:Int, stripAudio: Boolean = false, promise: Promise, reactContext: ReactApplicationContext) {
33
33
  val currentVideoCompression = intArrayOf(0)
34
34
  val videoCompressorClass: VideoCompressorClass? = VideoCompressorClass(reactContext);
35
35
  compressorExports[uuid] = videoCompressorClass
36
36
  videoCompressorClass?.start(
37
- srcPath, destinationPath, resultWidth, resultHeight, videoBitRate.toInt(),
37
+ srcPath, destinationPath, resultWidth, resultHeight, videoBitRate.toInt(), frameRate, stripAudio,
38
38
  listener = object : CompressionListener {
39
39
  override fun onProgress(index: Int, percent: Float) {
40
40
  if (percent <= 100) {
@@ -10,7 +10,6 @@ import java.io.File
10
10
 
11
11
  object AutoVideoCompression {
12
12
  fun createCompressionSettings(fileUrl: String?, options: VideoCompressorHelper, promise: Promise, reactContext: ReactApplicationContext?) {
13
- val maxSize = options.maxSize
14
13
  val minimumFileSizeForCompress = options.minimumFileSizeForCompress
15
14
  try {
16
15
  val uri = Uri.parse(fileUrl)
@@ -22,18 +21,34 @@ object AutoVideoCompression {
22
21
  val sizeInMb = sizeInBytes / (1024 * 1024)
23
22
  if (sizeInMb > minimumFileSizeForCompress) {
24
23
  val destinationPath = generateCacheFilePath("mp4", reactContext!!)
25
- val actualHeight = metaRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT)!!.toInt()
26
- val actualWidth = metaRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH)!!.toInt()
27
- val bitrate = metaRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_BITRATE)!!.toInt()
28
- val scale = if (actualWidth > actualHeight) maxSize / actualWidth else maxSize / actualHeight
29
- val resultWidth = Math.round(actualWidth * Math.min(scale, 1f) / 2) * 2
30
- val resultHeight = Math.round(actualHeight * Math.min(scale, 1f) / 2) * 2
31
- val videoBitRate = makeVideoBitrate(
32
- actualHeight, actualWidth,
33
- bitrate,
34
- resultHeight, resultWidth
35
- ).toFloat()
36
- compressVideo(srcPath!!, destinationPath, resultWidth, resultHeight, videoBitRate, options.uuid!!,options.progressDivider!!, promise, reactContext)
24
+ val actualHeight = VideoCompressorHelper.getMetadataInt(metaRetriever, MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT)
25
+ val actualWidth = VideoCompressorHelper.getMetadataInt(metaRetriever, MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH)
26
+ val bitrate = VideoCompressorHelper.getMetadataInt(metaRetriever, MediaMetadataRetriever.METADATA_KEY_BITRATE)
27
+ val frameRate = VideoCompressorHelper.getMetadataInt(metaRetriever, MediaMetadataRetriever.METADATA_KEY_CAPTURE_FRAMERATE)
28
+ if (actualHeight <= 0 || actualWidth <= 0) {
29
+ promise.reject(Throwable("Failed to read the input video dimensions"))
30
+ return
31
+ }
32
+ val profile = VideoCompressionProfileFactory.createAuto(
33
+ sourceWidth = actualWidth,
34
+ sourceHeight = actualHeight,
35
+ sourceBitrate = bitrate,
36
+ sourceFrameRate = frameRate,
37
+ maxSize = options.maxSize,
38
+ )
39
+ compressVideo(
40
+ srcPath!!,
41
+ destinationPath,
42
+ profile.width,
43
+ profile.height,
44
+ profile.bitrate.toFloat(),
45
+ profile.frameRate,
46
+ options.uuid!!,
47
+ options.progressDivider!!,
48
+ options.stripAudio,
49
+ promise,
50
+ reactContext,
51
+ )
37
52
  } else {
38
53
  promise.resolve(fileUrl)
39
54
  }
@@ -41,23 +56,4 @@ object AutoVideoCompression {
41
56
  promise.reject(ex)
42
57
  }
43
58
  }
44
-
45
- fun makeVideoBitrate(originalHeight: Int, originalWidth: Int, originalBitrate: Int, height: Int, width: Int): Int {
46
- val compressFactor = 0.8f
47
- val minCompressFactor = 0.8f
48
- val maxBitrate = 1669000
49
- var remeasuredBitrate = (originalBitrate / Math.min(originalHeight / height.toFloat(), originalWidth / width.toFloat())).toInt()
50
- remeasuredBitrate = (remeasuredBitrate * compressFactor).toInt()
51
- val minBitrate = (getVideoBitrateWithFactor(minCompressFactor) / (1280f * 720f / (width * height))).toInt()
52
- if (originalBitrate < minBitrate) {
53
- return remeasuredBitrate
54
- }
55
- return if (remeasuredBitrate > maxBitrate) {
56
- maxBitrate
57
- } else Math.max(remeasuredBitrate, minBitrate)
58
- }
59
-
60
- private fun getVideoBitrateWithFactor(f: Float): Int {
61
- return (f * 2000f * 1000f * 1.13f).toInt()
62
- }
63
59
  }
@@ -0,0 +1,144 @@
1
+ package com.reactnativecompressor.Video
2
+
3
+ import kotlin.math.max
4
+ import kotlin.math.min
5
+ import kotlin.math.roundToInt
6
+
7
+ data class VideoCompressionProfile(
8
+ val width: Int,
9
+ val height: Int,
10
+ val bitrate: Int,
11
+ val frameRate: Int,
12
+ )
13
+
14
+ object VideoCompressionProfileFactory {
15
+ private const val DEFAULT_FRAME_RATE = 30
16
+
17
+ fun createAuto(
18
+ sourceWidth: Int,
19
+ sourceHeight: Int,
20
+ sourceBitrate: Int,
21
+ sourceFrameRate: Int,
22
+ maxSize: Float,
23
+ ): VideoCompressionProfile {
24
+ val dimensions = scaleWithin(sourceWidth, sourceHeight, maxSize.roundToInt())
25
+ val frameRate = normalizeFrameRate(sourceFrameRate)
26
+ val bitrate = estimateBitrate(
27
+ sourceWidth = sourceWidth,
28
+ sourceHeight = sourceHeight,
29
+ sourceBitrate = sourceBitrate,
30
+ sourceFrameRate = sourceFrameRate,
31
+ targetWidth = dimensions.first,
32
+ targetHeight = dimensions.second,
33
+ targetFrameRate = frameRate,
34
+ )
35
+
36
+ return VideoCompressionProfile(
37
+ width = dimensions.first,
38
+ height = dimensions.second,
39
+ bitrate = bitrate,
40
+ frameRate = frameRate,
41
+ )
42
+ }
43
+
44
+ fun createManual(
45
+ sourceWidth: Int,
46
+ sourceHeight: Int,
47
+ sourceBitrate: Int,
48
+ sourceFrameRate: Int,
49
+ maxSize: Float,
50
+ requestedBitrate: Float,
51
+ ): VideoCompressionProfile {
52
+ val dimensions = scaleWithin(sourceWidth, sourceHeight, maxSize.roundToInt())
53
+ val frameRate = normalizeFrameRate(sourceFrameRate)
54
+ val bitrate = if (requestedBitrate > 0f) {
55
+ requestedBitrate.roundToInt().coerceAtLeast(1)
56
+ } else {
57
+ estimateBitrate(
58
+ sourceWidth = sourceWidth,
59
+ sourceHeight = sourceHeight,
60
+ sourceBitrate = sourceBitrate,
61
+ sourceFrameRate = sourceFrameRate,
62
+ targetWidth = dimensions.first,
63
+ targetHeight = dimensions.second,
64
+ targetFrameRate = frameRate,
65
+ )
66
+ }
67
+
68
+ return VideoCompressionProfile(
69
+ width = dimensions.first,
70
+ height = dimensions.second,
71
+ bitrate = bitrate,
72
+ frameRate = frameRate,
73
+ )
74
+ }
75
+
76
+ fun normalizeDimension(value: Int): Int {
77
+ val positive = value.coerceAtLeast(2)
78
+ return if (positive % 2 == 0) positive else positive - 1
79
+ }
80
+
81
+ private fun scaleWithin(sourceWidth: Int, sourceHeight: Int, requestedMaxSize: Int): Pair<Int, Int> {
82
+ val safeWidth = normalizeDimension(sourceWidth)
83
+ val safeHeight = normalizeDimension(sourceHeight)
84
+ val longSide = max(safeWidth, safeHeight)
85
+ val boundedMaxSize = requestedMaxSize.coerceAtLeast(2)
86
+
87
+ if (longSide <= boundedMaxSize) {
88
+ return Pair(safeWidth, safeHeight)
89
+ }
90
+
91
+ val scale = boundedMaxSize.toFloat() / longSide.toFloat()
92
+ val width = normalizeDimension((safeWidth * scale).roundToInt())
93
+ val height = normalizeDimension((safeHeight * scale).roundToInt())
94
+ return Pair(width, height)
95
+ }
96
+
97
+ private fun normalizeFrameRate(sourceFrameRate: Int): Int {
98
+ if (sourceFrameRate <= 0) {
99
+ return DEFAULT_FRAME_RATE
100
+ }
101
+
102
+ return sourceFrameRate.coerceIn(1, DEFAULT_FRAME_RATE)
103
+ }
104
+
105
+ private fun estimateBitrate(
106
+ sourceWidth: Int,
107
+ sourceHeight: Int,
108
+ sourceBitrate: Int,
109
+ sourceFrameRate: Int,
110
+ targetWidth: Int,
111
+ targetHeight: Int,
112
+ targetFrameRate: Int,
113
+ ): Int {
114
+ val targetLongSide = max(targetWidth, targetHeight)
115
+ val floor = when {
116
+ targetLongSide >= 1920 -> 4_000_000
117
+ targetLongSide >= 1280 -> 2_200_000
118
+ targetLongSide >= 960 -> 1_600_000
119
+ targetLongSide >= 720 -> 1_200_000
120
+ else -> 850_000
121
+ }
122
+ val ceiling = when {
123
+ targetLongSide >= 1920 -> 8_000_000
124
+ targetLongSide >= 1280 -> 5_000_000
125
+ targetLongSide >= 960 -> 3_500_000
126
+ targetLongSide >= 720 -> 2_500_000
127
+ else -> 1_500_000
128
+ }
129
+
130
+ if (sourceBitrate <= 0) {
131
+ return floor
132
+ }
133
+
134
+ val sourcePixels = sourceWidth.toLong() * sourceHeight.toLong()
135
+ val targetPixels = targetWidth.toLong() * targetHeight.toLong()
136
+ val pixelRatio = if (sourcePixels == 0L) 1.0 else targetPixels.toDouble() / sourcePixels.toDouble()
137
+ val sourceFps = max(sourceFrameRate, 1)
138
+ val frameRateRatio = targetFrameRate.toDouble() / sourceFps.toDouble()
139
+ val scaledBitrate = (sourceBitrate * pixelRatio * max(frameRateRatio, 0.85)).roundToInt()
140
+ val sourceCap = (sourceBitrate * 0.95).roundToInt().coerceAtLeast(floor)
141
+
142
+ return scaledBitrate.coerceAtLeast(floor).coerceAtMost(min(ceiling, sourceCap))
143
+ }
144
+ }
@@ -24,6 +24,8 @@ class VideoCompressorClass(private val context: ReactApplicationContext) {
24
24
  outputWidth: Int,
25
25
  outputHeight: Int,
26
26
  bitrate: Int,
27
+ frameRate: Int,
28
+ stripAudio: Boolean = false,
27
29
  listener: CompressionListener,
28
30
  ) {
29
31
  val uris = mutableListOf<Uri>()
@@ -36,6 +38,8 @@ class VideoCompressorClass(private val context: ReactApplicationContext) {
36
38
  outputWidth,
37
39
  outputHeight,
38
40
  bitrate,
41
+ frameRate,
42
+ stripAudio,
39
43
  listener,
40
44
  destPath
41
45
  )
@@ -52,6 +56,8 @@ class VideoCompressorClass(private val context: ReactApplicationContext) {
52
56
  outputWidth: Int,
53
57
  outputHeight: Int,
54
58
  bitrate: Int,
59
+ frameRate: Int,
60
+ stripAudio: Boolean,
55
61
  listener: CompressionListener,
56
62
  destPath: String
57
63
  ) {
@@ -74,6 +80,8 @@ class VideoCompressorClass(private val context: ReactApplicationContext) {
74
80
  outputWidth,
75
81
  outputHeight,
76
82
  bitrate,
83
+ frameRate,
84
+ stripAudio,
77
85
  listener,
78
86
  )
79
87
 
@@ -96,6 +104,8 @@ class VideoCompressorClass(private val context: ReactApplicationContext) {
96
104
  outputWidth: Int,
97
105
  outputHeight: Int,
98
106
  bitrate: Int,
107
+ frameRate: Int,
108
+ disableAudio: Boolean = false,
99
109
  listener: CompressionListener,
100
110
  ): Result = withContext(Dispatchers.Default) {
101
111
  return@withContext compressVideo(
@@ -107,6 +117,8 @@ class VideoCompressorClass(private val context: ReactApplicationContext) {
107
117
  outputWidth,
108
118
  outputHeight,
109
119
  bitrate,
120
+ frameRate,
121
+ disableAudio,
110
122
  object : CompressionProgressListener {
111
123
  override fun onProgressChanged(index: Int, percent: Float) {
112
124
  listener.onProgress(index, percent)
@@ -54,6 +54,8 @@ object Compressor {
54
54
  outputWidth: Int,
55
55
  outputHeight: Int,
56
56
  outputBitrate: Int,
57
+ outputFrameRate: Int,
58
+ disableAudio: Boolean = false,
57
59
  listener: CompressionProgressListener,
58
60
  ): Result = withContext(Dispatchers.Default) {
59
61
 
@@ -123,8 +125,9 @@ object Compressor {
123
125
  newHeight!!,
124
126
  destination,
125
127
  newBitrate,
128
+ outputFrameRate,
126
129
  streamableFile,
127
- false,
130
+ disableAudio,
128
131
  extractor,
129
132
  listener,
130
133
  duration,
@@ -140,6 +143,7 @@ object Compressor {
140
143
  newHeight: Int,
141
144
  destination: String,
142
145
  newBitrate: Int,
146
+ outputFrameRate: Int,
143
147
  streamableFile: String?,
144
148
  disableAudio: Boolean,
145
149
  extractor: MediaExtractor,
@@ -178,6 +182,7 @@ object Compressor {
178
182
  inputFormat,
179
183
  outputFormat,
180
184
  newBitrate,
185
+ outputFrameRate,
181
186
  )
182
187
 
183
188
  val decoder: MediaCodec
@@ -374,7 +379,7 @@ object Compressor {
374
379
  }
375
380
  }
376
381
 
377
- } catch (exception: Exception) {
382
+ } catch (exception: Throwable) {
378
383
  printException(exception)
379
384
  return Result(id, success = false, failureMessage = exception.message)
380
385
  }
@@ -400,12 +405,14 @@ object Compressor {
400
405
  extractor.release()
401
406
  try {
402
407
  mediaMuxer.finishMovie()
403
- } catch (e: Exception) {
408
+ } catch (e: Throwable) {
404
409
  printException(e)
410
+ return Result(id, success = false, failureMessage = e.message ?: "Failed to finalize compressed video")
405
411
  }
406
412
 
407
- } catch (exception: Exception) {
413
+ } catch (exception: Throwable) {
408
414
  printException(exception)
415
+ return Result(id, success = false, failureMessage = exception.message)
409
416
  }
410
417
 
411
418
  var resultFile = cacheFile
@@ -423,6 +430,13 @@ object Compressor {
423
430
  printException(e)
424
431
  }
425
432
  }
433
+ if (!resultFile.exists() || resultFile.length() <= 32) {
434
+ return Result(
435
+ id,
436
+ success = false,
437
+ failureMessage = "Compressed video output is invalid"
438
+ )
439
+ }
426
440
  return Result(
427
441
  id,
428
442
  success = true,
@@ -70,8 +70,9 @@ object CompressorUtils {
70
70
  inputFormat: MediaFormat,
71
71
  outputFormat: MediaFormat,
72
72
  newBitrate: Int,
73
+ targetFrameRate: Int,
73
74
  ) {
74
- val newFrameRate = getFrameRate(inputFormat)
75
+ val newFrameRate = targetFrameRate.coerceAtLeast(1)
75
76
  val iFrameInterval = getIFrameIntervalRate(inputFormat)
76
77
  outputFormat.apply {
77
78
  setInteger(
@@ -107,12 +108,6 @@ object CompressorUtils {
107
108
  }
108
109
  }
109
110
 
110
- // Get the frame rate from the input format or use a default value
111
- private fun getFrameRate(format: MediaFormat): Int {
112
- return if (format.containsKey(MediaFormat.KEY_FRAME_RATE)) format.getInteger(MediaFormat.KEY_FRAME_RATE)
113
- else 30
114
- }
115
-
116
111
  // Get the I-frame (keyframe) interval from the input format or use a default value
117
112
  private fun getIFrameIntervalRate(format: MediaFormat): Int {
118
113
  return if (format.containsKey(MediaFormat.KEY_I_FRAME_INTERVAL)) format.getInteger(
@@ -172,7 +167,7 @@ object CompressorUtils {
172
167
  /**
173
168
  * Log an exception with a meaningful message.
174
169
  */
175
- fun printException(exception: Exception) {
170
+ fun printException(exception: Throwable) {
176
171
  var message = "An error has occurred!"
177
172
  exception.localizedMessage?.let {
178
173
  message = it
@@ -27,6 +27,7 @@ class VideoCompressorHelper {
27
27
  var maxSize = 640.0f
28
28
  var progressDivider: Int? = 0
29
29
  var minimumFileSizeForCompress = 0.0f
30
+ var stripAudio = false
30
31
 
31
32
  companion object {
32
33
  private var _reactContext: ReactApplicationContext? = null
@@ -84,10 +85,21 @@ class VideoCompressorHelper {
84
85
  "minimumFileSizeForCompress" -> options.minimumFileSizeForCompress = map.getDouble(key).toFloat()
85
86
  "bitrate" -> options.bitrate = map.getDouble(key).toFloat()
86
87
  "progressDivider" -> options.progressDivider = map.getInt(key)
88
+ "stripAudio" -> options.stripAudio = map.getBoolean(key)
87
89
  }
88
90
  }
89
91
  return options
90
92
  }
93
+
94
+ fun getMetadataInt(metaRetriever: MediaMetadataRetriever, key: Int): Int {
95
+ return metaRetriever.extractMetadata(key)
96
+ ?.toDoubleOrNull()
97
+ ?.toLong()
98
+ ?.coerceIn(0L, Int.MAX_VALUE.toLong())
99
+ ?.toInt()
100
+ ?: 0
101
+ }
102
+
91
103
  fun VideoCompressManual(fileUrl: String?, options: VideoCompressorHelper, promise: Promise, reactContext: ReactApplicationContext?) {
92
104
  try {
93
105
  val uri = Uri.parse(fileUrl)
@@ -95,24 +107,35 @@ class VideoCompressorHelper {
95
107
  val destinationPath = Utils.generateCacheFilePath("mp4", reactContext!!)
96
108
  val metaRetriever = MediaMetadataRetriever()
97
109
  metaRetriever.setDataSource(srcPath)
98
- var height = metaRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT)!!.toInt()
99
- var width = metaRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH)!!.toInt()
100
- val bitrate = metaRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_BITRATE)!!.toInt()
101
- val isPortrait = height > width
102
- val maxSize = options.maxSize.toInt()
103
- if (isPortrait && height > maxSize) {
104
- width = (maxSize.toFloat() / height * width).toInt()
105
- height = maxSize
106
- } else if (width > maxSize) {
107
- height = (maxSize.toFloat() / width * height).toInt()
108
- width = maxSize
109
- } else {
110
- if (options.bitrate == 0f) {
111
- options.bitrate = (bitrate * 0.8).toInt().toFloat()
112
- }
110
+ val height = getMetadataInt(metaRetriever, MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT)
111
+ val width = getMetadataInt(metaRetriever, MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH)
112
+ val bitrate = getMetadataInt(metaRetriever, MediaMetadataRetriever.METADATA_KEY_BITRATE)
113
+ val frameRate = getMetadataInt(metaRetriever, MediaMetadataRetriever.METADATA_KEY_CAPTURE_FRAMERATE)
114
+ if (height <= 0 || width <= 0) {
115
+ promise.reject(Throwable("Failed to read the input video dimensions"))
116
+ return
113
117
  }
114
- val videoBitRate = if (options.bitrate > 0) options.bitrate else (height * width * 1.5).toFloat()
115
- Utils.compressVideo(srcPath!!, destinationPath, width, height, videoBitRate, options.uuid!!,options.progressDivider!!, promise, reactContext)
118
+ val profile = VideoCompressionProfileFactory.createManual(
119
+ sourceWidth = width,
120
+ sourceHeight = height,
121
+ sourceBitrate = bitrate,
122
+ sourceFrameRate = frameRate,
123
+ maxSize = options.maxSize,
124
+ requestedBitrate = options.bitrate,
125
+ )
126
+ Utils.compressVideo(
127
+ srcPath!!,
128
+ destinationPath,
129
+ profile.width,
130
+ profile.height,
131
+ profile.bitrate.toFloat(),
132
+ profile.frameRate,
133
+ options.uuid!!,
134
+ options.progressDivider!!,
135
+ options.stripAudio,
136
+ promise,
137
+ reactContext,
138
+ )
116
139
  } catch (ex: Exception) {
117
140
  promise.reject(ex)
118
141
  }
@@ -70,6 +70,9 @@ open class NextLevelSessionExporter: NSObject {
70
70
 
71
71
  /// Audio output configuration dictionary, using keys defined in `<AVFoundation/AVAudioSettings.h>`
72
72
  public var audioOutputConfiguration: [String : Any]?
73
+
74
+ /// When true, audio tracks are excluded from the export entirely.
75
+ public var stripAudio: Bool = false
73
76
 
74
77
  /// Export session status state.
75
78
  public var status: AVAssetExportSession.Status {
@@ -247,8 +250,10 @@ extension NextLevelSessionExporter {
247
250
  }
248
251
 
249
252
  self.setupVideoOutput(withAsset: asset)
250
- self.setupAudioOutput(withAsset: asset)
251
- self.setupAudioInput()
253
+ if !self.stripAudio {
254
+ self.setupAudioOutput(withAsset: asset)
255
+ self.setupAudioInput()
256
+ }
252
257
 
253
258
  // export
254
259
 
@@ -104,11 +104,11 @@ class VideoCompressor {
104
104
 
105
105
  VideoCompressor.getAbsoluteVideoPath(url.absoluteString, options: options) { absoluteVideoPath in
106
106
  var minimumFileSizeForCompress:Double=0.0;
107
- let videoURL = URL(string: absoluteVideoPath)
107
+ let videoURL = URL(string: absoluteVideoPath)
108
108
  let fileSize=self.getfileSize(forURL: videoURL!);
109
- if((options["minimumFileSizeForCompress"]) != nil)
110
- {0
111
- minimumFileSizeForCompress=options["minimumFileSizeForCompress"] as! Double;
109
+ if((options["minimumFileSizeForCompress"]) != nil)
110
+ {
111
+ minimumFileSizeForCompress=(options["minimumFileSizeForCompress"] as? NSNumber)?.doubleValue ?? 0;
112
112
  }
113
113
  if(fileSize>minimumFileSizeForCompress)
114
114
  {
@@ -146,55 +146,129 @@ class VideoCompressor {
146
146
  }
147
147
 
148
148
 
149
- func makeVideoBitrate(originalHeight:Int,originalWidth:Int,originalBitrate:Int,height:Int,width:Int)->Int {
150
- let compressFactor:Float = 0.8
151
- let minCompressFactor:Float = 0.8
152
- let maxBitrate:Int = 1669000
153
- let minValue:Float=min(Float(originalHeight)/Float(height),Float(originalWidth)/Float(width))
154
- var remeasuredBitrate:Int = Int(Float(originalBitrate) / minValue)
155
- remeasuredBitrate = Int(Float(remeasuredBitrate)*compressFactor)
156
- let minBitrate:Int = Int(Float(self.getVideoBitrateWithFactor(f: minCompressFactor)) / (1280 * 720 / Float(width * height)))
157
- if (originalBitrate < minBitrate) {
158
- return remeasuredBitrate;
149
+ struct CompressionProfile {
150
+ let width: Int
151
+ let height: Int
152
+ let bitrate: Int
153
+ let frameRate: Int
154
+ }
155
+
156
+ func normalizeDimension(_ value: CGFloat) -> Int {
157
+ let rounded = max(Int(value.rounded()), 2)
158
+ return rounded % 2 == 0 ? rounded : rounded - 1
159
+ }
160
+
161
+ func normalizeFrameRate(for track: AVAssetTrack) -> Int {
162
+ let nominalFrameRate = Int(track.nominalFrameRate.rounded())
163
+ if nominalFrameRate <= 0 {
164
+ return 30
165
+ }
166
+
167
+ return min(max(nominalFrameRate, 1), 30)
168
+ }
169
+
170
+ func scaledDimensions(width: CGFloat, height: CGFloat, maxSize: CGFloat) -> (width: Int, height: Int) {
171
+ let safeWidth = normalizeDimension(width)
172
+ let safeHeight = normalizeDimension(height)
173
+ let longSide = max(safeWidth, safeHeight)
174
+ let safeMaxSize = max(Int(maxSize.rounded()), 2)
175
+
176
+ guard longSide > safeMaxSize else {
177
+ return (safeWidth, safeHeight)
159
178
  }
160
- if (remeasuredBitrate > maxBitrate) {
161
- return maxBitrate;
179
+
180
+ let scale = CGFloat(safeMaxSize) / CGFloat(longSide)
181
+ return (
182
+ normalizeDimension(CGFloat(safeWidth) * scale),
183
+ normalizeDimension(CGFloat(safeHeight) * scale)
184
+ )
185
+ }
186
+
187
+ func estimateBitrate(
188
+ originalWidth: Int,
189
+ originalHeight: Int,
190
+ originalBitrate: Int,
191
+ originalFrameRate: Int,
192
+ targetWidth: Int,
193
+ targetHeight: Int,
194
+ targetFrameRate: Int
195
+ ) -> Int {
196
+ let targetLongSide = max(targetWidth, targetHeight)
197
+ let floor: Int
198
+ let ceiling: Int
199
+
200
+ switch targetLongSide {
201
+ case 1920...:
202
+ floor = 4_000_000
203
+ ceiling = 8_000_000
204
+ case 1280...1919:
205
+ floor = 2_200_000
206
+ ceiling = 5_000_000
207
+ case 960...1279:
208
+ floor = 1_600_000
209
+ ceiling = 3_500_000
210
+ case 720...959:
211
+ floor = 1_200_000
212
+ ceiling = 2_500_000
213
+ default:
214
+ floor = 850_000
215
+ ceiling = 1_500_000
162
216
  }
163
- return max(remeasuredBitrate, minBitrate);
164
- }
165
- func getVideoBitrateWithFactor(f:Float)->Int {
166
- return Int(f * 2000 * 1000 * 1.13);
167
- }
217
+
218
+ guard originalBitrate > 0 else {
219
+ return floor
220
+ }
221
+
222
+ let originalPixels = max(originalWidth * originalHeight, 1)
223
+ let targetPixels = max(targetWidth * targetHeight, 1)
224
+ let pixelRatio = Double(targetPixels) / Double(originalPixels)
225
+ let safeOriginalFrameRate = max(originalFrameRate, 1)
226
+ let frameRateRatio = Double(targetFrameRate) / Double(safeOriginalFrameRate)
227
+ let scaledBitrate = Int((Double(originalBitrate) * pixelRatio * max(frameRateRatio, 0.85)).rounded())
228
+ let sourceCap = max(Int((Double(originalBitrate) * 0.95).rounded()), floor)
229
+ return min(max(scaledBitrate, floor), min(ceiling, sourceCap))
230
+ }
231
+
232
+ func createCompressionProfile(track: AVAssetTrack, maxSize: CGFloat, requestedBitrate: Int?) -> CompressionProfile {
233
+ let videoSize = track.naturalSize.applying(track.preferredTransform)
234
+ let actualWidth = abs(videoSize.width)
235
+ let actualHeight = abs(videoSize.height)
236
+ let dimensions = scaledDimensions(width: actualWidth, height: actualHeight, maxSize: maxSize)
237
+ let frameRate = normalizeFrameRate(for: track)
238
+ let sourceFrameRate = Int(track.nominalFrameRate.rounded())
239
+ let bitrate = (requestedBitrate != nil && requestedBitrate! > 0) ? requestedBitrate! : estimateBitrate(
240
+ originalWidth: normalizeDimension(actualWidth),
241
+ originalHeight: normalizeDimension(actualHeight),
242
+ originalBitrate: Int(abs(track.estimatedDataRate.rounded())),
243
+ originalFrameRate: sourceFrameRate,
244
+ targetWidth: dimensions.width,
245
+ targetHeight: dimensions.height,
246
+ targetFrameRate: frameRate
247
+ )
248
+
249
+ return CompressionProfile(
250
+ width: dimensions.width,
251
+ height: dimensions.height,
252
+ bitrate: max(bitrate, 1),
253
+ frameRate: frameRate
254
+ )
255
+ }
168
256
 
169
257
  func autoCompressionHelper(url: URL, options: [String: Any], onProgress: @escaping (Float) -> Void, onCompletion: @escaping (URL) -> Void, onFailure: @escaping (Error) -> Void){
170
- let maxSize:Float = options["maxSize"] as! Float;
258
+ let maxSize = (options["maxSize"] as? NSNumber)?.floatValue ?? 640
171
259
  let uuid:String = options["uuid"] as! String
172
260
  let progressDivider=options["progressDivider"] as? Int ?? 0
261
+ let stripAudio=options["stripAudio"] as? Bool ?? false
173
262
 
174
263
  let asset = AVAsset(url: url)
175
- guard asset.tracks.count >= 1 else {
264
+ guard let track = getVideoTrack(asset: asset) else {
176
265
  let error = CompressionError(message: "Invalid video URL, no track found")
177
266
  onFailure(error)
178
267
  return
179
268
  }
180
- let track = getVideoTrack(asset: asset);
181
-
182
- let videoSize = track.naturalSize.applying(track.preferredTransform);
183
- let actualWidth = Float(abs(videoSize.width))
184
- let actualHeight = Float(abs(videoSize.height))
185
-
186
- let bitrate=Float(abs(track.estimatedDataRate));
187
- let scale:Float = actualWidth > actualHeight ? (Float(maxSize) / actualWidth) : (Float(maxSize) / actualHeight);
188
- let resultWidth:Float = round(actualWidth * min(scale, 1) / 2) * 2;
189
- let resultHeight:Float = round(actualHeight * min(scale, 1) / 2) * 2;
190
-
191
- let videoBitRate:Int = self.makeVideoBitrate(
192
- originalHeight: Int(actualHeight), originalWidth: Int(actualWidth),
193
- originalBitrate: Int(bitrate),
194
- height: Int(resultHeight), width: Int(resultWidth)
195
- );
269
+ let profile = createCompressionProfile(track: track, maxSize: CGFloat(maxSize), requestedBitrate: nil)
196
270
 
197
- exportVideoHelper(url: url, asset: asset, bitRate: videoBitRate, resultWidth: resultWidth, resultHeight: resultHeight,uuid: uuid,progressDivider: progressDivider) { progress in
271
+ exportVideoHelper(url: url, asset: asset, bitRate: profile.bitrate, frameRate: profile.frameRate, resultWidth: profile.width, resultHeight: profile.height,uuid: uuid,progressDivider: progressDivider, stripAudio: stripAudio) { progress in
198
272
  onProgress(progress)
199
273
  } onCompletion: { outputURL in
200
274
  onCompletion(outputURL)
@@ -205,36 +279,19 @@ class VideoCompressor {
205
279
 
206
280
  func manualCompressionHelper(url: URL, options: [String: Any], onProgress: @escaping (Float) -> Void, onCompletion: @escaping (URL) -> Void, onFailure: @escaping (Error) -> Void){
207
281
  let uuid:String = options["uuid"] as! String
208
- var bitRate = (options["bitrate"] as? NSNumber)?.floatValue;
282
+ let bitRate = (options["bitrate"] as? NSNumber)?.intValue
209
283
  let progressDivider=options["progressDivider"] as? Int ?? 0
284
+ let stripAudio=options["stripAudio"] as? Bool ?? false
210
285
  let asset = AVAsset(url: url)
211
- guard asset.tracks.count >= 1 else {
286
+ guard let track = getVideoTrack(asset: asset) else {
212
287
  let error = CompressionError(message: "Invalid video URL, no track found")
213
288
  onFailure(error)
214
289
  return
215
290
  }
216
- let track = getVideoTrack(asset: asset);
217
-
218
- let videoSize = track.naturalSize.applying(track.preferredTransform);
219
- var width = Float(abs(videoSize.width))
220
- var height = Float(abs(videoSize.height))
221
- let isPortrait = height > width
222
- let maxSize = (options["maxSize"] as! Float?) ?? Float(1920);
223
- if(isPortrait && height > maxSize){
224
- width = (maxSize/height)*width
225
- height = maxSize
226
- }else if(width > maxSize){
227
- height = (maxSize/width)*height
228
- width = maxSize
229
- }
230
- else
231
- {
232
- bitRate=bitRate ?? Float(abs(track.estimatedDataRate))*0.8
233
- }
291
+ let maxSize = (options["maxSize"] as? NSNumber)?.floatValue ?? Float(1920)
292
+ let profile = createCompressionProfile(track: track, maxSize: CGFloat(maxSize), requestedBitrate: bitRate)
234
293
 
235
- let videoBitRate = bitRate ?? height*width*1.5
236
-
237
- exportVideoHelper(url: url, asset: asset, bitRate: Int(videoBitRate), resultWidth: width, resultHeight: height,uuid: uuid,progressDivider: progressDivider) { progress in
294
+ exportVideoHelper(url: url, asset: asset, bitRate: profile.bitrate, frameRate: profile.frameRate, resultWidth: profile.width, resultHeight: profile.height,uuid: uuid,progressDivider: progressDivider, stripAudio: stripAudio) { progress in
238
295
  onProgress(progress)
239
296
  } onCompletion: { outputURL in
240
297
  onCompletion(outputURL)
@@ -243,7 +300,7 @@ class VideoCompressor {
243
300
  }
244
301
  }
245
302
 
246
- func exportVideoHelper(url: URL,asset: AVAsset, bitRate: Int,resultWidth:Float,resultHeight:Float,uuid:String,progressDivider: Int, onProgress: @escaping (Float) -> Void, onCompletion: @escaping (URL) -> Void, onFailure: @escaping (Error) -> Void){
303
+ func exportVideoHelper(url: URL,asset: AVAsset, bitRate: Int, frameRate: Int, resultWidth:Int,resultHeight:Int,uuid:String,progressDivider: Int, stripAudio: Bool = false, onProgress: @escaping (Float) -> Void, onCompletion: @escaping (URL) -> Void, onFailure: @escaping (Error) -> Void){
247
304
  var currentVideoCompression:Int=0
248
305
 
249
306
  var tmpURL = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true)
@@ -258,21 +315,26 @@ class VideoCompressor {
258
315
  let compressionDict: [String: Any] = [
259
316
  AVVideoAverageBitRateKey: bitRate,
260
317
  AVVideoProfileLevelKey: AVVideoProfileLevelH264HighAutoLevel,
318
+ AVVideoExpectedSourceFrameRateKey: frameRate,
319
+ AVVideoAverageNonDroppableFrameRateKey: frameRate,
261
320
  ]
262
321
  exporter.optimizeForNetworkUse = true;
263
322
  exporter.videoOutputConfiguration = [
264
323
  AVVideoCodecKey: AVVideoCodecType.h264,
265
324
  AVVideoWidthKey: resultWidth,
266
325
  AVVideoHeightKey: resultHeight,
267
- AVVideoScalingModeKey: AVVideoScalingModeResizeAspectFill,
326
+ AVVideoScalingModeKey: AVVideoScalingModeResizeAspect,
268
327
  AVVideoCompressionPropertiesKey: compressionDict
269
328
  ]
270
- exporter.audioOutputConfiguration = [
271
- AVFormatIDKey: kAudioFormatMPEG4AAC,
272
- AVEncoderBitRateKey: NSNumber(integerLiteral: 128000),
273
- AVNumberOfChannelsKey: NSNumber(integerLiteral: 2),
274
- AVSampleRateKey: NSNumber(value: Float(44100))
275
- ]
329
+ exporter.stripAudio = stripAudio
330
+ if !stripAudio {
331
+ exporter.audioOutputConfiguration = [
332
+ AVFormatIDKey: kAudioFormatMPEG4AAC,
333
+ AVEncoderBitRateKey: NSNumber(integerLiteral: 128000),
334
+ AVNumberOfChannelsKey: NSNumber(integerLiteral: 2),
335
+ AVSampleRateKey: NSNumber(value: Float(44100))
336
+ ]
337
+ }
276
338
 
277
339
  compressorExports[uuid] = exporter
278
340
  exporter.export(progressHandler: { (progress) in
@@ -284,6 +346,7 @@ class VideoCompressor {
284
346
  }
285
347
  }, completionHandler: { result in
286
348
  currentVideoCompression=0;
349
+ self.compressorExports[uuid] = nil
287
350
  switch exporter.status {
288
351
  case .completed:
289
352
  onCompletion(exporter.outputURL!)
@@ -293,14 +356,17 @@ class VideoCompressor {
293
356
  onFailure(error)
294
357
  break
295
358
  default:
296
- onCompletion(url)
359
+ onFailure(exporter.error ?? CompressionError(message: "Compression failed"))
297
360
  break
298
361
  }
299
362
  })
300
363
  }
301
364
 
302
- func getVideoTrack(asset: AVAsset) -> AVAssetTrack {
365
+ func getVideoTrack(asset: AVAsset) -> AVAssetTrack? {
303
366
  let tracks = asset.tracks(withMediaType: AVMediaType.video)
367
+ guard tracks.count >= 1 else {
368
+ return nil
369
+ }
304
370
  return tracks[0];
305
371
  }
306
372
 
@@ -52,6 +52,9 @@ const Video = {
52
52
  if (options?.minimumFileSizeForCompress !== undefined) {
53
53
  modifiedOptions.minimumFileSizeForCompress = options?.minimumFileSizeForCompress;
54
54
  }
55
+ if (options?.stripAudio) {
56
+ modifiedOptions.stripAudio = options.stripAudio;
57
+ }
55
58
  if (options?.getCancellationId) {
56
59
  options?.getCancellationId(uuid);
57
60
  }
@@ -1 +1 @@
1
- {"version":3,"names":["_reactNative","require","_Main","_utils","VideoCompressEventEmitter","NativeEventEmitter","Compressor","NativeVideoCompressor","cancelCompression","cancellationId","exports","Video","compress","fileUrl","options","onProgress","uuid","uuidv4","subscription","subscription2","addListener","event","data","progress","downloadProgress","modifiedOptions","progressDivider","bitrate","compressionMethod","maxSize","minimumFileSizeForCompress","undefined","getCancellationId","result","remove","activateBackgroundTask","onExpired","deactivateBackgroundTask","removeAllListeners","_default","default"],"sourceRoot":"../../../src","sources":["Video/index.tsx"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["_reactNative","require","_Main","_utils","VideoCompressEventEmitter","NativeEventEmitter","Compressor","NativeVideoCompressor","cancelCompression","cancellationId","exports","Video","compress","fileUrl","options","onProgress","uuid","uuidv4","subscription","subscription2","addListener","event","data","progress","downloadProgress","modifiedOptions","progressDivider","bitrate","compressionMethod","maxSize","minimumFileSizeForCompress","undefined","stripAudio","getCancellationId","result","remove","activateBackgroundTask","onExpired","deactivateBackgroundTask","removeAllListeners","_default","default"],"sourceRoot":"../../../src","sources":["Video/index.tsx"],"mappings":"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","ignoreList":[]}
@@ -47,6 +47,9 @@ const Video = {
47
47
  if (options?.minimumFileSizeForCompress !== undefined) {
48
48
  modifiedOptions.minimumFileSizeForCompress = options?.minimumFileSizeForCompress;
49
49
  }
50
+ if (options?.stripAudio) {
51
+ modifiedOptions.stripAudio = options.stripAudio;
52
+ }
50
53
  if (options?.getCancellationId) {
51
54
  options?.getCancellationId(uuid);
52
55
  }
@@ -1 +1 @@
1
- {"version":3,"names":["NativeEventEmitter","Compressor","uuidv4","VideoCompressEventEmitter","NativeVideoCompressor","cancelCompression","cancellationId","Video","compress","fileUrl","options","onProgress","uuid","subscription","subscription2","addListener","event","data","progress","downloadProgress","modifiedOptions","progressDivider","bitrate","compressionMethod","maxSize","minimumFileSizeForCompress","undefined","getCancellationId","result","remove","activateBackgroundTask","onExpired","deactivateBackgroundTask","removeAllListeners"],"sourceRoot":"../../../src","sources":["Video/index.tsx"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["NativeEventEmitter","Compressor","uuidv4","VideoCompressEventEmitter","NativeVideoCompressor","cancelCompression","cancellationId","Video","compress","fileUrl","options","onProgress","uuid","subscription","subscription2","addListener","event","data","progress","downloadProgress","modifiedOptions","progressDivider","bitrate","compressionMethod","maxSize","minimumFileSizeForCompress","undefined","stripAudio","getCancellationId","result","remove","activateBackgroundTask","onExpired","deactivateBackgroundTask","removeAllListeners"],"sourceRoot":"../../../src","sources":["Video/index.tsx"],"mappings":"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","ignoreList":[]}
@@ -10,6 +10,11 @@ type videoCompresssionType = {
10
10
  * Default:0, we uses it when we use downloadProgress/onProgress
11
11
  */
12
12
  progressDivider?: number;
13
+ /***
14
+ * When true, the audio track is removed entirely from the output video.
15
+ * Default: false
16
+ */
17
+ stripAudio?: boolean;
13
18
  };
14
19
  export type VideoCompressorType = {
15
20
  compress(fileUrl: string, options?: videoCompresssionType, onProgress?: (progress: number) => void): Promise<string>;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/Video/index.tsx"],"names":[],"mappings":"AAKA,MAAM,MAAM,iBAAiB,GAAG,MAAM,GAAG,QAAQ,CAAC;AAClD,KAAK,qBAAqB,GAAG;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;IACtC,0BAA0B,CAAC,EAAE,MAAM,CAAC;IACpC,iBAAiB,CAAC,EAAE,CAAC,cAAc,EAAE,MAAM,KAAK,IAAI,CAAC;IACrD,gBAAgB,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9C;;OAEG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,qBAAqB,EAAE,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACrH,iBAAiB,CAAC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAChD,sBAAsB,CAAC,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,GAAG,KAAK,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IACtE,wBAAwB,IAAI,OAAO,CAAC,GAAG,CAAC,CAAC;CAC1C,CAAC;AAMF,eAAO,MAAM,iBAAiB,GAAI,gBAAgB,MAAM,QAEvD,CAAC;AAEF,QAAA,MAAM,KAAK,EAAE,mBAgFW,CAAC;AAEzB,eAAe,KAAK,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/Video/index.tsx"],"names":[],"mappings":"AAKA,MAAM,MAAM,iBAAiB,GAAG,MAAM,GAAG,QAAQ,CAAC;AAClD,KAAK,qBAAqB,GAAG;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;IACtC,0BAA0B,CAAC,EAAE,MAAM,CAAC;IACpC,iBAAiB,CAAC,EAAE,CAAC,cAAc,EAAE,MAAM,KAAK,IAAI,CAAC;IACrD,gBAAgB,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9C;;OAEG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,qBAAqB,EAAE,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACrH,iBAAiB,CAAC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAChD,sBAAsB,CAAC,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,GAAG,KAAK,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IACtE,wBAAwB,IAAI,OAAO,CAAC,GAAG,CAAC,CAAC;CAC1C,CAAC;AAMF,eAAO,MAAM,iBAAiB,GAAI,gBAAgB,MAAM,QAEvD,CAAC;AAEF,QAAA,MAAM,KAAK,EAAE,mBAoFW,CAAC;AAEzB,eAAe,KAAK,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-compressor",
3
- "version": "1.16.2",
3
+ "version": "1.18.0",
4
4
  "description": "Compress Image, Video, and Audio same like Whatsapp & Auto/Manual Compression | Background Upload | Download File | Create Video Thumbnail",
5
5
  "main": "lib/commonjs/index",
6
6
  "module": "lib/module/index",
@@ -15,6 +15,11 @@ type videoCompresssionType = {
15
15
  * Default:0, we uses it when we use downloadProgress/onProgress
16
16
  */
17
17
  progressDivider?: number;
18
+ /***
19
+ * When true, the audio track is removed entirely from the output video.
20
+ * Default: false
21
+ */
22
+ stripAudio?: boolean;
18
23
  };
19
24
 
20
25
  export type VideoCompressorType = {
@@ -63,6 +68,7 @@ const Video: VideoCompressorType = {
63
68
  maxSize?: number;
64
69
  minimumFileSizeForCompress?: number;
65
70
  progressDivider?: number;
71
+ stripAudio?: boolean;
66
72
  } = { uuid };
67
73
  if (options?.progressDivider) modifiedOptions.progressDivider = options?.progressDivider;
68
74
  if (options?.bitrate) modifiedOptions.bitrate = options?.bitrate;
@@ -79,6 +85,9 @@ const Video: VideoCompressorType = {
79
85
  if (options?.minimumFileSizeForCompress !== undefined) {
80
86
  modifiedOptions.minimumFileSizeForCompress = options?.minimumFileSizeForCompress;
81
87
  }
88
+ if (options?.stripAudio) {
89
+ modifiedOptions.stripAudio = options.stripAudio;
90
+ }
82
91
  if (options?.getCancellationId) {
83
92
  options?.getCancellationId(uuid);
84
93
  }