react-native-compressor 1.19.2 → 1.19.4
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/README.md +7 -1
- package/android/build.gradle +0 -1
- package/android/src/main/java/com/reactnativecompressor/Audio/AudioCompressor.kt +152 -139
- package/android/src/main/java/com/reactnativecompressor/Audio/AudioTranscoder.kt +204 -0
- package/android/src/main/java/com/reactnativecompressor/Video/VideoCompressor/compressor/Compressor.kt +401 -379
- package/ios/Image/ImageCompressor.swift +37 -16
- package/ios/Image/ImageMain.swift +21 -11
- package/package.json +1 -1
- package/android/src/main/java/com/reactnativecompressor/Audio/AudioExtractor.kt +0 -112
|
@@ -44,7 +44,7 @@ class ImageCompressor {
|
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
|
|
47
|
-
static func manualResize(_ image: UIImage, maxWidth: Int, maxHeight: Int) -> UIImage {
|
|
47
|
+
static func manualResize(_ image: UIImage, maxWidth: Int, maxHeight: Int) throws -> UIImage {
|
|
48
48
|
let targetSize = findTargetSize(image, maxWidth: maxWidth, maxHeight: maxHeight)
|
|
49
49
|
|
|
50
50
|
if let cgImage = image.cgImage {
|
|
@@ -82,6 +82,10 @@ class ImageCompressor {
|
|
|
82
82
|
free(&targetData)
|
|
83
83
|
let exception = NSException(name: NSExceptionName(rawValue: "drawing_error"), reason: "Problem while rendering your image", userInfo: nil)
|
|
84
84
|
exception.raise()
|
|
85
|
+
// Throw instead of raising an NSException (process abort no JS
|
|
86
|
+
// catch survives) — the caller rejects the promise.
|
|
87
|
+
throw NSError(domain: "drawing_error", code: 1,
|
|
88
|
+
userInfo: [NSLocalizedDescriptionKey: "Problem while rendering your image"])
|
|
85
89
|
}
|
|
86
90
|
|
|
87
91
|
let targetContext = CGContext(data: &targetData,
|
|
@@ -164,7 +168,7 @@ class ImageCompressor {
|
|
|
164
168
|
return destinationData as Data
|
|
165
169
|
}
|
|
166
170
|
|
|
167
|
-
static func writeImage(_ image: UIImage, output: Int, quality: Float, outputExtension: String, isBase64: Bool, disablePngTransparency: Bool, isEnableAutoCompress: Bool, actualImagePath: String?)-> String {
|
|
171
|
+
static func writeImage(_ image: UIImage, output: Int, quality: Float, outputExtension: String, isBase64: Bool, disablePngTransparency: Bool, isEnableAutoCompress: Bool, actualImagePath: String?) throws -> String {
|
|
168
172
|
var data: Data
|
|
169
173
|
var exception: NSException?
|
|
170
174
|
let normalizedQuality = CGFloat(min(max(quality, 0), 1))
|
|
@@ -212,14 +216,19 @@ class ImageCompressor {
|
|
|
212
216
|
} catch {
|
|
213
217
|
exception = NSException(name: NSExceptionName(rawValue: "file_error"), reason: "Error writing file", userInfo: nil)
|
|
214
218
|
exception?.raise()
|
|
219
|
+
// Throw instead of raising an NSException: a raise inside a
|
|
220
|
+
// TurboModule invocation aborts the whole app and no JS catch
|
|
221
|
+
// can intercept it — the caller rejects the promise instead.
|
|
222
|
+
throw NSError(domain: "file_error", code: 1,
|
|
223
|
+
userInfo: [NSLocalizedDescriptionKey: "Error writing file: \(error.localizedDescription)"])
|
|
215
224
|
}
|
|
216
225
|
}
|
|
217
226
|
return ""
|
|
218
227
|
}
|
|
219
228
|
|
|
220
229
|
|
|
221
|
-
static func manualCompress(_ image: UIImage, output: Int, quality: Float, outputExtension: String, isBase64: Bool, disablePngTransparency: Bool, actualImagePath: String?) -> String {
|
|
222
|
-
return writeImage(image, output: output, quality: quality, outputExtension: outputExtension, isBase64: isBase64, disablePngTransparency: disablePngTransparency, isEnableAutoCompress: false, actualImagePath: actualImagePath)
|
|
230
|
+
static func manualCompress(_ image: UIImage, output: Int, quality: Float, outputExtension: String, isBase64: Bool, disablePngTransparency: Bool, actualImagePath: String?) throws -> String {
|
|
231
|
+
return try writeImage(image, output: output, quality: quality, outputExtension: outputExtension, isBase64: isBase64, disablePngTransparency: disablePngTransparency, isEnableAutoCompress: false, actualImagePath: actualImagePath)
|
|
223
232
|
}
|
|
224
233
|
|
|
225
234
|
|
|
@@ -288,7 +297,7 @@ class ImageCompressor {
|
|
|
288
297
|
}
|
|
289
298
|
|
|
290
299
|
|
|
291
|
-
static func manualCompressHandler(imagePath: String?, base64: String?, options: ImageCompressorOptions) -> String {
|
|
300
|
+
static func manualCompressHandler(imagePath: String?, base64: String?, options: ImageCompressorOptions) throws -> String {
|
|
292
301
|
var exception: NSException?
|
|
293
302
|
var image: UIImage?
|
|
294
303
|
|
|
@@ -306,22 +315,28 @@ class ImageCompressor {
|
|
|
306
315
|
if let _image = image {
|
|
307
316
|
image = ImageCompressor.scaleAndRotateImage(_image)
|
|
308
317
|
let outputExtension = ImageCompressorOptions.getOutputInString(options.output)
|
|
309
|
-
let resizedImage = ImageCompressor.manualResize(image!, maxWidth: options.maxWidth, maxHeight: options.maxHeight)
|
|
318
|
+
let resizedImage = try ImageCompressor.manualResize(image!, maxWidth: options.maxWidth, maxHeight: options.maxHeight)
|
|
310
319
|
let isBase64 = options.returnableOutputType == .rbase64
|
|
311
|
-
return ImageCompressor.manualCompress(resizedImage, output: options.output.rawValue, quality: options.quality, outputExtension: outputExtension, isBase64: isBase64,disablePngTransparency: options.disablePngTransparency, actualImagePath: imagePath)
|
|
320
|
+
return try ImageCompressor.manualCompress(resizedImage, output: options.output.rawValue, quality: options.quality, outputExtension: outputExtension, isBase64: isBase64,disablePngTransparency: options.disablePngTransparency, actualImagePath: imagePath)
|
|
312
321
|
} else {
|
|
313
322
|
exception = NSException(name: NSExceptionName(rawValue: "unsupported_value"), reason: "Unsupported value type.", userInfo: nil)
|
|
314
323
|
exception?.raise()
|
|
324
|
+
// Throw instead of raising an NSException (process abort no JS
|
|
325
|
+
// catch survives — e.g. a stored file path invalidated by an iOS
|
|
326
|
+
// container relocation boot-looped an app at startup). The caller
|
|
327
|
+
// rejects the promise with the same name/message.
|
|
328
|
+
throw NSError(domain: "unsupported_value", code: 1,
|
|
329
|
+
userInfo: [NSLocalizedDescriptionKey: "Unsupported value type."])
|
|
315
330
|
}
|
|
316
331
|
|
|
317
332
|
return ""
|
|
318
333
|
}
|
|
319
334
|
|
|
320
335
|
|
|
321
|
-
static func autoCompressHandler(imagePath: String?, base64: String?, options: ImageCompressorOptions) -> String {
|
|
336
|
+
static func autoCompressHandler(imagePath: String?, base64: String?, options: ImageCompressorOptions) throws -> String {
|
|
322
337
|
var exception: NSException?
|
|
323
338
|
var image: UIImage?
|
|
324
|
-
|
|
339
|
+
|
|
325
340
|
switch options.input {
|
|
326
341
|
case .base64:
|
|
327
342
|
if let _base64 = base64 {
|
|
@@ -332,11 +347,11 @@ class ImageCompressor {
|
|
|
332
347
|
image = ImageCompressor.loadImage(_imagePath)
|
|
333
348
|
}
|
|
334
349
|
}
|
|
335
|
-
|
|
350
|
+
|
|
336
351
|
if var image = image {
|
|
337
352
|
image = ImageCompressor.scaleAndRotateImage(image)
|
|
338
353
|
let outputExtension = ImageCompressorOptions.getOutputInString(options.output)
|
|
339
|
-
|
|
354
|
+
|
|
340
355
|
var actualHeight = image.size.height
|
|
341
356
|
var actualWidth = image.size.width
|
|
342
357
|
let maxHeight: CGFloat = CGFloat(options.maxHeight)
|
|
@@ -344,7 +359,7 @@ class ImageCompressor {
|
|
|
344
359
|
var imgRatio = actualWidth / actualHeight
|
|
345
360
|
let maxRatio = maxWidth / maxHeight
|
|
346
361
|
let compressionQuality: CGFloat = CGFloat(options.quality)
|
|
347
|
-
|
|
362
|
+
|
|
348
363
|
if actualHeight > maxHeight || actualWidth > maxWidth {
|
|
349
364
|
if imgRatio < maxRatio {
|
|
350
365
|
imgRatio = maxHeight / actualHeight
|
|
@@ -359,20 +374,26 @@ class ImageCompressor {
|
|
|
359
374
|
actualWidth = maxWidth
|
|
360
375
|
}
|
|
361
376
|
}
|
|
362
|
-
|
|
377
|
+
|
|
363
378
|
let rect = CGRect(x: 0.0, y: 0.0, width: actualWidth, height: actualHeight)
|
|
364
379
|
UIGraphicsBeginImageContext(rect.size)
|
|
365
380
|
image.draw(in: rect)
|
|
366
381
|
let isBase64 = options.returnableOutputType == .rbase64
|
|
367
|
-
|
|
382
|
+
|
|
368
383
|
if let img = UIGraphicsGetImageFromCurrentImageContext() {
|
|
369
|
-
return writeImage(img, output: options.output.rawValue, quality: Float(compressionQuality), outputExtension: outputExtension, isBase64: isBase64, disablePngTransparency: options.disablePngTransparency, isEnableAutoCompress: true, actualImagePath: imagePath)
|
|
384
|
+
return try writeImage(img, output: options.output.rawValue, quality: Float(compressionQuality), outputExtension: outputExtension, isBase64: isBase64, disablePngTransparency: options.disablePngTransparency, isEnableAutoCompress: true, actualImagePath: imagePath)
|
|
370
385
|
}
|
|
371
386
|
} else {
|
|
372
387
|
exception = NSException(name: NSExceptionName(rawValue: "unsupported_value"), reason: "Unsupported value type.", userInfo: nil)
|
|
373
388
|
exception?.raise()
|
|
389
|
+
// Throw instead of raising an NSException (process abort no JS
|
|
390
|
+
// catch survives — e.g. a stored file path invalidated by an iOS
|
|
391
|
+
// container relocation boot-looped an app at startup). The caller
|
|
392
|
+
// rejects the promise with the same name/message.
|
|
393
|
+
throw NSError(domain: "unsupported_value", code: 1,
|
|
394
|
+
userInfo: [NSLocalizedDescriptionKey: "Unsupported value type."])
|
|
374
395
|
}
|
|
375
|
-
|
|
396
|
+
|
|
376
397
|
return ""
|
|
377
398
|
}
|
|
378
399
|
|
|
@@ -13,22 +13,32 @@ class ImageMain {
|
|
|
13
13
|
|
|
14
14
|
if options.input != InputType.base64 {
|
|
15
15
|
ImageCompressor.getAbsoluteImagePath(value, options: options) { absoluteImagePath in
|
|
16
|
+
// The escaping completion runs outside the outer do/catch,
|
|
17
|
+
// so failures here need their own catch to reject.
|
|
18
|
+
do {
|
|
19
|
+
if options.autoCompress {
|
|
20
|
+
let result = try ImageCompressor.autoCompressHandler(imagePath: absoluteImagePath, base64: nil, options: options)
|
|
21
|
+
resolve(result)
|
|
22
|
+
} else {
|
|
23
|
+
let result = try ImageCompressor.manualCompressHandler(imagePath: absoluteImagePath, base64: nil, options: options)
|
|
24
|
+
resolve(result)
|
|
25
|
+
}
|
|
26
|
+
} catch {
|
|
27
|
+
reject((error as NSError).domain, error.localizedDescription, error)
|
|
28
|
+
}
|
|
29
|
+
MediaCache.removeCompletedImagePath(absoluteImagePath)
|
|
30
|
+
}
|
|
31
|
+
} else {
|
|
32
|
+
do {
|
|
16
33
|
if options.autoCompress {
|
|
17
|
-
let result = ImageCompressor.autoCompressHandler(imagePath:
|
|
34
|
+
let result = try ImageCompressor.autoCompressHandler(imagePath: nil, base64: value, options: options)
|
|
18
35
|
resolve(result)
|
|
19
36
|
} else {
|
|
20
|
-
let result = ImageCompressor.manualCompressHandler(imagePath:
|
|
37
|
+
let result = try ImageCompressor.manualCompressHandler(imagePath: nil, base64: value, options: options)
|
|
21
38
|
resolve(result)
|
|
22
39
|
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
} else {
|
|
26
|
-
if options.autoCompress {
|
|
27
|
-
let result = ImageCompressor.autoCompressHandler(imagePath: nil, base64: value, options: options)
|
|
28
|
-
resolve(result)
|
|
29
|
-
} else {
|
|
30
|
-
let result = ImageCompressor.manualCompressHandler(imagePath: nil, base64: value, options: options)
|
|
31
|
-
resolve(result)
|
|
40
|
+
} catch {
|
|
41
|
+
reject((error as NSError).domain, error.localizedDescription, error)
|
|
32
42
|
}
|
|
33
43
|
}
|
|
34
44
|
} catch {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-compressor",
|
|
3
|
-
"version": "1.19.
|
|
3
|
+
"version": "1.19.4",
|
|
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",
|
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
package com.reactnativecompressor.Audio
|
|
2
|
-
|
|
3
|
-
import android.annotation.SuppressLint
|
|
4
|
-
import android.media.MediaCodec
|
|
5
|
-
import android.media.MediaExtractor
|
|
6
|
-
import android.media.MediaFormat
|
|
7
|
-
import android.media.MediaMetadataRetriever
|
|
8
|
-
import android.media.MediaMuxer
|
|
9
|
-
import android.util.Log
|
|
10
|
-
import java.io.IOException
|
|
11
|
-
import java.nio.ByteBuffer
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
class AudioExtractor {
|
|
15
|
-
/**
|
|
16
|
-
* @param srcPath the path of source video file.
|
|
17
|
-
* @param dstPath the path of destination video file.
|
|
18
|
-
* @param startMs starting time in milliseconds for trimming. Set to
|
|
19
|
-
* negative if starting from beginning.
|
|
20
|
-
* @param endMs end time for trimming in milliseconds. Set to negative if
|
|
21
|
-
* no trimming at the end.
|
|
22
|
-
* @param useAudio true if keep the audio track from the source.
|
|
23
|
-
* @param useVideo true if keep the video track from the source.
|
|
24
|
-
* @throws IOException
|
|
25
|
-
*/
|
|
26
|
-
@SuppressLint("NewApi", "WrongConstant")
|
|
27
|
-
@Throws(IOException::class)
|
|
28
|
-
fun genVideoUsingMuxer(srcPath: String?, dstPath: String?, startMs: Int, endMs: Int, useAudio: Boolean, useVideo: Boolean) {
|
|
29
|
-
// Set up MediaExtractor to read from the source.
|
|
30
|
-
val extractor = MediaExtractor()
|
|
31
|
-
extractor.setDataSource(srcPath!!)
|
|
32
|
-
val trackCount = extractor.trackCount
|
|
33
|
-
// Set up MediaMuxer for the destination.
|
|
34
|
-
val muxer: MediaMuxer
|
|
35
|
-
muxer = MediaMuxer(dstPath!!, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4)
|
|
36
|
-
// Set up the tracks and retrieve the max buffer size for selected
|
|
37
|
-
// tracks.
|
|
38
|
-
val indexMap = HashMap<Int, Int>(trackCount)
|
|
39
|
-
var bufferSize = -1
|
|
40
|
-
for (i in 0 until trackCount) {
|
|
41
|
-
val format = extractor.getTrackFormat(i)
|
|
42
|
-
val mime = format.getString(MediaFormat.KEY_MIME)
|
|
43
|
-
var selectCurrentTrack = false
|
|
44
|
-
if (mime!!.startsWith("audio/") && useAudio) {
|
|
45
|
-
selectCurrentTrack = true
|
|
46
|
-
} else if (mime.startsWith("video/") && useVideo) {
|
|
47
|
-
selectCurrentTrack = true
|
|
48
|
-
}
|
|
49
|
-
if (selectCurrentTrack) {
|
|
50
|
-
extractor.selectTrack(i)
|
|
51
|
-
val dstIndex = muxer.addTrack(format)
|
|
52
|
-
indexMap[i] = dstIndex
|
|
53
|
-
if (format.containsKey(MediaFormat.KEY_MAX_INPUT_SIZE)) {
|
|
54
|
-
val newSize = format.getInteger(MediaFormat.KEY_MAX_INPUT_SIZE)
|
|
55
|
-
bufferSize = if (newSize > bufferSize) newSize else bufferSize
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
if (bufferSize < 0) {
|
|
60
|
-
bufferSize = DEFAULT_BUFFER_SIZE
|
|
61
|
-
}
|
|
62
|
-
// Set up the orientation and starting time for extractor.
|
|
63
|
-
val retrieverSrc = MediaMetadataRetriever()
|
|
64
|
-
retrieverSrc.setDataSource(srcPath)
|
|
65
|
-
val degreesString = retrieverSrc.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION)
|
|
66
|
-
if (degreesString != null) {
|
|
67
|
-
val degrees = degreesString.toInt()
|
|
68
|
-
if (degrees >= 0) {
|
|
69
|
-
muxer.setOrientationHint(degrees)
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
if (startMs > 0) {
|
|
73
|
-
extractor.seekTo((startMs * 1000).toLong(), MediaExtractor.SEEK_TO_CLOSEST_SYNC)
|
|
74
|
-
}
|
|
75
|
-
// Copy the samples from MediaExtractor to MediaMuxer. We will loop
|
|
76
|
-
// for copying each sample and stop when we get to the end of the source
|
|
77
|
-
// file or exceed the end time of the trimming.
|
|
78
|
-
val offset = 0
|
|
79
|
-
var trackIndex = -1
|
|
80
|
-
val dstBuf = ByteBuffer.allocate(bufferSize)
|
|
81
|
-
val bufferInfo = MediaCodec.BufferInfo()
|
|
82
|
-
muxer.start()
|
|
83
|
-
while (true) {
|
|
84
|
-
bufferInfo.offset = offset
|
|
85
|
-
bufferInfo.size = extractor.readSampleData(dstBuf, offset)
|
|
86
|
-
if (bufferInfo.size < 0) {
|
|
87
|
-
Log.d(TAG, "Saw input EOS.")
|
|
88
|
-
bufferInfo.size = 0
|
|
89
|
-
break
|
|
90
|
-
} else {
|
|
91
|
-
bufferInfo.presentationTimeUs = extractor.sampleTime
|
|
92
|
-
if (endMs > 0 && bufferInfo.presentationTimeUs > endMs * 1000) {
|
|
93
|
-
Log.d(TAG, "The current sample is over the trim end time.")
|
|
94
|
-
break
|
|
95
|
-
} else {
|
|
96
|
-
bufferInfo.flags = extractor.sampleFlags
|
|
97
|
-
trackIndex = extractor.sampleTrackIndex
|
|
98
|
-
muxer.writeSampleData(indexMap[trackIndex]!!, dstBuf, bufferInfo)
|
|
99
|
-
extractor.advance()
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
muxer.stop()
|
|
104
|
-
muxer.release()
|
|
105
|
-
return
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
companion object {
|
|
109
|
-
private const val DEFAULT_BUFFER_SIZE = 1 * 1024 * 1024
|
|
110
|
-
private const val TAG = "AudioExtractorDecoder"
|
|
111
|
-
}
|
|
112
|
-
}
|