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.
- package/android/build.gradle +3 -0
- package/android/src/main/java/com/reactnativecompressor/Utils/Utils.kt +2 -2
- package/android/src/main/java/com/reactnativecompressor/Video/AutoVideoCompression.kt +28 -32
- package/android/src/main/java/com/reactnativecompressor/Video/VideoCompressionProfile.kt +144 -0
- package/android/src/main/java/com/reactnativecompressor/Video/VideoCompressor/VideoCompressorClass.kt +12 -0
- package/android/src/main/java/com/reactnativecompressor/Video/VideoCompressor/compressor/Compressor.kt +18 -4
- package/android/src/main/java/com/reactnativecompressor/Video/VideoCompressor/utils/CompressorUtils.kt +3 -8
- package/android/src/main/java/com/reactnativecompressor/Video/VideoCompressorHelper.kt +40 -17
- package/ios/Video/NextLevelSessionExporter.swift +7 -2
- package/ios/Video/VideoMain.swift +139 -73
- package/lib/commonjs/Video/index.js +3 -0
- package/lib/commonjs/Video/index.js.map +1 -1
- package/lib/module/Video/index.js +3 -0
- package/lib/module/Video/index.js.map +1 -1
- package/lib/typescript/src/Video/index.d.ts +5 -0
- package/lib/typescript/src/Video/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/Video/index.tsx +9 -0
package/android/build.gradle
CHANGED
|
@@ -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 =
|
|
26
|
-
val actualWidth =
|
|
27
|
-
val bitrate =
|
|
28
|
-
val
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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
|
-
|
|
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:
|
|
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:
|
|
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:
|
|
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 =
|
|
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:
|
|
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
|
-
|
|
99
|
-
|
|
100
|
-
val bitrate = metaRetriever
|
|
101
|
-
val
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
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
|
|
115
|
-
|
|
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.
|
|
251
|
-
|
|
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
|
-
|
|
107
|
+
let videoURL = URL(string: absoluteVideoPath)
|
|
108
108
|
let fileSize=self.getfileSize(forURL: videoURL!);
|
|
109
|
-
|
|
110
|
-
{
|
|
111
|
-
minimumFileSizeForCompress=options["minimumFileSizeForCompress"] as
|
|
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
|
-
|
|
150
|
-
let
|
|
151
|
-
let
|
|
152
|
-
let
|
|
153
|
-
let
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
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
|
-
|
|
161
|
-
|
|
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
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
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
|
|
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
|
|
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
|
|
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:
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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:
|
|
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:
|
|
326
|
+
AVVideoScalingModeKey: AVVideoScalingModeResizeAspect,
|
|
268
327
|
AVVideoCompressionPropertiesKey: compressionDict
|
|
269
328
|
]
|
|
270
|
-
exporter.
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
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
|
-
|
|
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":";;;;;;AAAA,IAAAA,YAAA,GAAAC,OAAA;AAEA,IAAAC,KAAA,GAAAD,OAAA;AACA,IAAAE,MAAA,GAAAF,OAAA;
|
|
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":";;AAAA,SAASA,kBAAkB,QAAQ,cAAc;AAEjD,SAASC,UAAU,QAAQ,SAAS;AACpC,SAASC,MAAM,QAAQ,UAAU;
|
|
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":";;AAAA,SAASA,kBAAkB,QAAQ,cAAc;AAEjD,SAASC,UAAU,QAAQ,SAAS;AACpC,SAASC,MAAM,QAAQ,UAAU;AA4BjC,MAAMC,yBAAyB,GAAG,IAAIH,kBAAkB,CAACC,UAAU,CAAC;AAEpE,MAAMG,qBAAqB,GAAGH,UAAU;AAExC,OAAO,MAAMI,iBAAiB,GAAIC,cAAsB,IAAK;EAC3D,OAAOF,qBAAqB,CAACC,iBAAiB,CAACC,cAAc,CAAC;AAChE,CAAC;AAED,MAAMC,KAA0B,GAAG;EACjCC,QAAQ,EAAE,MAAAA,CAAOC,OAAe,EAAEC,OAA+B,EAAEC,UAAuC,KAAK;IAC7G,MAAMC,IAAI,GAAGV,MAAM,CAAC,CAAC;IACrB,IAAIW,YAAqC;IACzC,IAAIC,aAAsC;IAE1C,IAAI;MACF,IAAIH,UAAU,EAAE;QACdE,YAAY,GAAGV,yBAAyB,CAACY,WAAW,CAAC,uBAAuB,EAAGC,KAAU,IAAK;UAC5F,IAAIA,KAAK,CAACJ,IAAI,KAAKA,IAAI,EAAE;YACvBD,UAAU,CAACK,KAAK,CAACC,IAAI,CAACC,QAAQ,CAAC;UACjC;QACF,CAAC,CAAC;MACJ;MAEA,IAAIR,OAAO,EAAES,gBAAgB,EAAE;QAC7B;QACAL,aAAa,GAAGX,yBAAyB,CAACY,WAAW,CAAC,kBAAkB,EAAGC,KAAU,IAAK;UACxF,IAAIA,KAAK,CAACJ,IAAI,KAAKA,IAAI,EAAE;YACvBF,OAAO,CAACS,gBAAgB,IAAIT,OAAO,CAACS,gBAAgB,CAACH,KAAK,CAACC,IAAI,CAACC,QAAQ,CAAC;UAC3E;QACF,CAAC,CAAC;MACJ;MAEA,MAAME,eAQL,GAAG;QAAER;MAAK,CAAC;MACZ,IAAIF,OAAO,EAAEW,eAAe,EAAED,eAAe,CAACC,eAAe,GAAGX,OAAO,EAAEW,eAAe;MACxF,IAAIX,OAAO,EAAEY,OAAO,EAAEF,eAAe,CAACE,OAAO,GAAGZ,OAAO,EAAEY,OAAO;MAChE,IAAIZ,OAAO,EAAEa,iBAAiB,EAAE;QAC9BH,eAAe,CAACG,iBAAiB,GAAGb,OAAO,EAAEa,iBAAiB;MAChE,CAAC,MAAM;QACLH,eAAe,CAACG,iBAAiB,GAAG,MAAM;MAC5C;MACA,IAAIb,OAAO,EAAEc,OAAO,EAAE;QACpBJ,eAAe,CAACI,OAAO,GAAGd,OAAO,EAAEc,OAAO;MAC5C,CAAC,MAAM;QACLJ,eAAe,CAACI,OAAO,GAAG,GAAG;MAC/B;MACA,IAAId,OAAO,EAAEe,0BAA0B,KAAKC,SAAS,EAAE;QACrDN,eAAe,CAACK,0BAA0B,GAAGf,OAAO,EAAEe,0BAA0B;MAClF;MACA,IAAIf,OAAO,EAAEiB,UAAU,EAAE;QACvBP,eAAe,CAACO,UAAU,GAAGjB,OAAO,CAACiB,UAAU;MACjD;MACA,IAAIjB,OAAO,EAAEkB,iBAAiB,EAAE;QAC9BlB,OAAO,EAAEkB,iBAAiB,CAAChB,IAAI,CAAC;MAClC;MAEA,MAAMiB,MAAM,GAAG,MAAMzB,qBAAqB,CAACI,QAAQ,CAACC,OAAO,EAAEW,eAAe,CAAC;MAC7E,OAAOS,MAAM;IACf,CAAC,SAAS;MACR;MACA,IAAIhB,YAAY,EAAE;QAChBA,YAAY,CAACiB,MAAM,CAAC,CAAC;MACvB;MACA;MACA,IAAIhB,aAAa,EAAE;QACjBA,aAAa,CAACgB,MAAM,CAAC,CAAC;MACxB;IACF;EACF,CAAC;EACDzB,iBAAiB;EACjB0B,sBAAsBA,CAACC,SAAU,EAAE;IACjC,IAAIA,SAAS,EAAE;MACb,MAAMnB,YAAqC,GAAGV,yBAAyB,CAACY,WAAW,CAAC,uBAAuB,EAAGC,KAAU,IAAK;QAC3HgB,SAAS,CAAChB,KAAK,CAAC;QAChB,IAAIH,YAAY,EAAE;UAChBA,YAAY,CAACiB,MAAM,CAAC,CAAC;QACvB;MACF,CAAC,CAAC;IACJ;IACA,OAAO1B,qBAAqB,CAAC2B,sBAAsB,CAAC,CAAC,CAAC,CAAC;EACzD,CAAC;EACDE,wBAAwBA,CAAA,EAAG;IACzB9B,yBAAyB,CAAC+B,kBAAkB,CAAC,uBAAuB,CAAC;IACrE,OAAO9B,qBAAqB,CAAC6B,wBAAwB,CAAC,CAAC,CAAC,CAAC;EAC3D;AACF,CAAwB;AAExB,eAAe1B,KAAK","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;
|
|
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.
|
|
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",
|
package/src/Video/index.tsx
CHANGED
|
@@ -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
|
}
|