react-native-compressor 1.8.24 → 1.9.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 +1 -1
- package/android/src/main/java/com/reactnativecompressor/Image/ImageMain.kt +2 -2
- package/android/src/main/java/com/reactnativecompressor/Utils/Utils.kt +10 -1
- package/android/src/main/java/com/reactnativecompressor/Video/VideoCompressor/videoHelpers/MP4Builder.kt +5 -3
- package/android/src/main/java/com/reactnativecompressor/Video/VideoCompressor/videoHelpers/Mdat.kt +9 -17
- package/android/src/main/java/com/reactnativecompressor/Video/VideoCompressor/videoHelpers/Mp4Movie.kt +1 -1
- package/android/src/main/java/com/reactnativecompressor/Video/VideoCompressor/videoHelpers/Track.kt +17 -34
- package/android/src/main/java/com/reactnativecompressor/Video/VideoMain.kt +2 -2
- package/ios/Audio/AudioMain.swift +15 -20
- package/ios/Image/ImageCompressor.swift +1 -1
- package/ios/Utils/CreateVideoThumbnail.swift +8 -2
- package/ios/Video/VideoMain.swift +36 -36
- package/package.json +1 -1
package/android/build.gradle
CHANGED
|
@@ -114,7 +114,7 @@ dependencies {
|
|
|
114
114
|
|
|
115
115
|
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4"
|
|
116
116
|
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4"
|
|
117
|
-
implementation
|
|
117
|
+
implementation 'org.mp4parser:isoparser:1.9.56'
|
|
118
118
|
implementation 'com.github.banketree:AndroidLame-kotlin:v0.0.1'
|
|
119
119
|
implementation 'javazoom:jlayer:1.0.1'
|
|
120
120
|
}
|
|
@@ -42,7 +42,7 @@ class ImageMain(private val reactContext: ReactApplicationContext) {
|
|
|
42
42
|
val srcPath = uri.path
|
|
43
43
|
val params = Arguments.createMap()
|
|
44
44
|
val file = File(srcPath)
|
|
45
|
-
val
|
|
45
|
+
val sizeInBytes = file.length().toDouble()
|
|
46
46
|
val exif = ExifInterface(srcPath!!)
|
|
47
47
|
for (tag in exifAttributes) {
|
|
48
48
|
val value: String? = exif.getAttribute(tag)
|
|
@@ -53,7 +53,7 @@ class ImageMain(private val reactContext: ReactApplicationContext) {
|
|
|
53
53
|
|
|
54
54
|
}
|
|
55
55
|
val extension = filePath!!.substring(filePath.lastIndexOf(".") + 1)
|
|
56
|
-
params.putDouble("size",
|
|
56
|
+
params.putDouble("size", sizeInBytes)
|
|
57
57
|
params.putString("extension", extension)
|
|
58
58
|
promise.resolve(params)
|
|
59
59
|
} catch (e: Exception) {
|
|
@@ -14,9 +14,10 @@ import java.io.FileNotFoundException
|
|
|
14
14
|
import java.io.IOException
|
|
15
15
|
import java.net.HttpURLConnection
|
|
16
16
|
import java.net.URL
|
|
17
|
+
import java.nio.ByteBuffer
|
|
17
18
|
import java.util.UUID
|
|
18
19
|
import java.util.regex.Pattern
|
|
19
|
-
|
|
20
|
+
|
|
20
21
|
|
|
21
22
|
object Utils {
|
|
22
23
|
private const val TAG = "react-native-compessor"
|
|
@@ -328,4 +329,12 @@ object Utils {
|
|
|
328
329
|
-1L
|
|
329
330
|
}
|
|
330
331
|
}
|
|
332
|
+
|
|
333
|
+
fun subBuffer(buf: ByteBuffer, start: Int, count: Int = buf.remaining() - start): ByteBuffer {
|
|
334
|
+
val newBuf = buf.duplicate()
|
|
335
|
+
val bytes = ByteArray(count)
|
|
336
|
+
newBuf.position(start)
|
|
337
|
+
newBuf[bytes, 0, bytes.size]
|
|
338
|
+
return ByteBuffer.wrap(bytes)
|
|
339
|
+
}
|
|
331
340
|
}
|
|
@@ -2,8 +2,10 @@ package com.reactnativecompressor.Video.VideoCompressor.video
|
|
|
2
2
|
|
|
3
3
|
import android.media.MediaCodec
|
|
4
4
|
import android.media.MediaFormat
|
|
5
|
-
import
|
|
6
|
-
import
|
|
5
|
+
import org.mp4parser.Box
|
|
6
|
+
import org.mp4parser.boxes.iso14496.part12.*
|
|
7
|
+
|
|
8
|
+
import org.mp4parser.support.Matrix
|
|
7
9
|
import java.io.FileOutputStream
|
|
8
10
|
import java.nio.ByteBuffer
|
|
9
11
|
import java.nio.channels.FileChannel
|
|
@@ -42,7 +44,7 @@ class MP4Builder {
|
|
|
42
44
|
@Throws(Exception::class)
|
|
43
45
|
private fun flushCurrentMdat() {
|
|
44
46
|
val oldPosition = fc.position()
|
|
45
|
-
fc.position(mdat.
|
|
47
|
+
fc.position(mdat.getOffset())
|
|
46
48
|
mdat.getBox(fc)
|
|
47
49
|
fc.position(oldPosition)
|
|
48
50
|
mdat.setDataOffset(0)
|
package/android/src/main/java/com/reactnativecompressor/Video/VideoCompressor/videoHelpers/Mdat.kt
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
package com.reactnativecompressor.Video.VideoCompressor.video
|
|
2
2
|
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import
|
|
8
|
-
|
|
3
|
+
import org.mp4parser.Box
|
|
4
|
+
import org.mp4parser.BoxParser
|
|
5
|
+
import org.mp4parser.Container
|
|
6
|
+
import org.mp4parser.IsoFile
|
|
7
|
+
import org.mp4parser.tools.IsoTypeWriter
|
|
8
|
+
|
|
9
9
|
import java.nio.ByteBuffer
|
|
10
10
|
import java.nio.channels.WritableByteChannel
|
|
11
11
|
|
|
@@ -15,15 +15,15 @@ class Mdat : Box {
|
|
|
15
15
|
private var contentSize = (1024 * 1024 * 1024).toLong()
|
|
16
16
|
private var dataOffset: Long = 0
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
fun getParent(): Container = parent
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
fun setParent(parent: Container) {
|
|
21
21
|
this.parent = parent
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
override fun getSize(): Long = 16 + contentSize
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
fun getOffset(): Long = dataOffset
|
|
27
27
|
|
|
28
28
|
fun setDataOffset(offset: Long) {
|
|
29
29
|
dataOffset = offset
|
|
@@ -63,12 +63,4 @@ class Mdat : Box {
|
|
|
63
63
|
bb.rewind()
|
|
64
64
|
writableByteChannel.write(bb)
|
|
65
65
|
}
|
|
66
|
-
|
|
67
|
-
override fun parse(
|
|
68
|
-
dataSource: DataSource?,
|
|
69
|
-
header: ByteBuffer?,
|
|
70
|
-
contentSize: Long,
|
|
71
|
-
boxParser: BoxParser?
|
|
72
|
-
) {
|
|
73
|
-
}
|
|
74
66
|
}
|
package/android/src/main/java/com/reactnativecompressor/Video/VideoCompressor/videoHelpers/Track.kt
CHANGED
|
@@ -3,15 +3,16 @@ package com.reactnativecompressor.Video.VideoCompressor.video
|
|
|
3
3
|
import android.media.MediaCodec
|
|
4
4
|
import android.media.MediaCodecInfo
|
|
5
5
|
import android.media.MediaFormat
|
|
6
|
-
import com.
|
|
7
|
-
import
|
|
8
|
-
import
|
|
9
|
-
import
|
|
10
|
-
import
|
|
11
|
-
import
|
|
12
|
-
import
|
|
13
|
-
import
|
|
14
|
-
import
|
|
6
|
+
import com.reactnativecompressor.Utils.Utils
|
|
7
|
+
import org.mp4parser.boxes.iso14496.part1.objectdescriptors.AudioSpecificConfig
|
|
8
|
+
import org.mp4parser.boxes.iso14496.part1.objectdescriptors.DecoderConfigDescriptor
|
|
9
|
+
import org.mp4parser.boxes.iso14496.part1.objectdescriptors.ESDescriptor
|
|
10
|
+
import org.mp4parser.boxes.iso14496.part1.objectdescriptors.SLConfigDescriptor
|
|
11
|
+
import org.mp4parser.boxes.iso14496.part12.SampleDescriptionBox
|
|
12
|
+
import org.mp4parser.boxes.iso14496.part14.ESDescriptorBox
|
|
13
|
+
import org.mp4parser.boxes.iso14496.part15.AvcConfigurationBox
|
|
14
|
+
import org.mp4parser.boxes.sampleentry.AudioSampleEntry
|
|
15
|
+
import org.mp4parser.boxes.sampleentry.VisualSampleEntry
|
|
15
16
|
import java.util.*
|
|
16
17
|
|
|
17
18
|
class Track(id: Int, format: MediaFormat, audio: Boolean) {
|
|
@@ -66,28 +67,10 @@ class Track(id: Int, format: MediaFormat, audio: Boolean) {
|
|
|
66
67
|
VisualSampleEntry(VisualSampleEntry.TYPE3).setup(width, height)
|
|
67
68
|
|
|
68
69
|
val avcConfigurationBox = AvcConfigurationBox()
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
val spsBytes = ByteArray(spsBuff.remaining())
|
|
75
|
-
spsBuff[spsBytes]
|
|
76
|
-
spsArray.add(spsBytes)
|
|
77
|
-
|
|
78
|
-
val ppsArray = ArrayList<ByteArray>()
|
|
79
|
-
val ppsBuff = format.getByteBuffer("csd-1")
|
|
80
|
-
ppsBuff?.let {
|
|
81
|
-
it.position(4)
|
|
82
|
-
|
|
83
|
-
val ppsBytes = ByteArray(it.remaining())
|
|
84
|
-
it[ppsBytes]
|
|
85
|
-
|
|
86
|
-
ppsArray.add(ppsBytes)
|
|
87
|
-
avcConfigurationBox.sequenceParameterSets = spsArray
|
|
88
|
-
avcConfigurationBox.pictureParameterSets = ppsArray
|
|
89
|
-
}
|
|
90
|
-
}
|
|
70
|
+
avcConfigurationBox.sequenceParameterSets =
|
|
71
|
+
format.getByteBuffer("csd-0")?.let { listOf(Utils.subBuffer(it, 4)) }
|
|
72
|
+
avcConfigurationBox.pictureParameterSets =
|
|
73
|
+
format.getByteBuffer("csd-1")?.let { listOf(Utils.subBuffer(it, 4)) }
|
|
91
74
|
|
|
92
75
|
if (format.containsKey("level")) {
|
|
93
76
|
when (format.getInteger("level")) {
|
|
@@ -186,7 +169,8 @@ class Track(id: Int, format: MediaFormat, audio: Boolean) {
|
|
|
186
169
|
val decoderConfigDescriptor = DecoderConfigDescriptor().setup()
|
|
187
170
|
|
|
188
171
|
val audioSpecificConfig = AudioSpecificConfig()
|
|
189
|
-
|
|
172
|
+
|
|
173
|
+
audioSpecificConfig.setOriginalAudioObjectType(2)
|
|
190
174
|
audioSpecificConfig.setSamplingFrequencyIndex(
|
|
191
175
|
samplingFrequencyIndexMap[audioSampleEntry.sampleRate.toInt()]!!
|
|
192
176
|
)
|
|
@@ -194,9 +178,8 @@ class Track(id: Int, format: MediaFormat, audio: Boolean) {
|
|
|
194
178
|
decoderConfigDescriptor.audioSpecificInfo = audioSpecificConfig
|
|
195
179
|
descriptor.decoderConfigDescriptor = decoderConfigDescriptor
|
|
196
180
|
|
|
197
|
-
val data = descriptor.serialize()
|
|
198
181
|
esds.esDescriptor = descriptor
|
|
199
|
-
|
|
182
|
+
|
|
200
183
|
audioSampleEntry.addBox(esds)
|
|
201
184
|
sampleDescriptionBox.addBox(audioSampleEntry)
|
|
202
185
|
}
|
|
@@ -65,14 +65,14 @@ class VideoMain(private val reactContext: ReactApplicationContext) {
|
|
|
65
65
|
val metaRetriever = MediaMetadataRetriever()
|
|
66
66
|
metaRetriever.setDataSource(srcPath)
|
|
67
67
|
val file = File(srcPath)
|
|
68
|
-
val
|
|
68
|
+
val sizeInBytes = (file.length()).toDouble()
|
|
69
69
|
val actualHeight = metaRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT)!!.toInt()
|
|
70
70
|
val actualWidth = metaRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH)!!.toInt()
|
|
71
71
|
val duration = metaRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION)!!.toDouble()
|
|
72
72
|
val creationTime = metaRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DATE)
|
|
73
73
|
val extension = filePath!!.substring(filePath.lastIndexOf(".") + 1)
|
|
74
74
|
val params = Arguments.createMap()
|
|
75
|
-
params.putDouble("size",
|
|
75
|
+
params.putDouble("size", sizeInBytes)
|
|
76
76
|
params.putInt("width", actualWidth)
|
|
77
77
|
params.putInt("height", actualHeight)
|
|
78
78
|
params.putDouble("duration", duration / 1000)
|
|
@@ -14,12 +14,7 @@ let AlAsset_Library_Scheme = "assets-library"
|
|
|
14
14
|
class AudioMain{
|
|
15
15
|
static func compress_audio(_ fileUrl: String, optionMap: NSDictionary, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
|
|
16
16
|
do {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
if fileUrl.hasPrefix("file://") {
|
|
20
|
-
fileUrl = fileUrl.replacingOccurrences(of: "file://", with: "")
|
|
21
|
-
}
|
|
22
|
-
|
|
17
|
+
let fileUrl = fileUrl.replacingOccurrences(of: "file://", with: "")
|
|
23
18
|
let fileManager = FileManager.default
|
|
24
19
|
var isDir: ObjCBool = false
|
|
25
20
|
if !fileManager.fileExists(atPath: fileUrl, isDirectory: &isDir) || isDir.boolValue {
|
|
@@ -27,19 +22,19 @@ class AudioMain{
|
|
|
27
22
|
reject(String(err.code), err.localizedDescription, err)
|
|
28
23
|
return
|
|
29
24
|
}
|
|
30
|
-
|
|
25
|
+
|
|
31
26
|
let audioOptions = AudioOptions.fromDictionary((optionMap as! [String : Any]))
|
|
32
|
-
let outputMp3Path =
|
|
33
|
-
|
|
34
|
-
let oldUfileUrlRL = URL(
|
|
35
|
-
let newURL = URL(
|
|
36
|
-
|
|
27
|
+
let outputMp3Path = Utils.generateCacheFilePath("m4a")
|
|
28
|
+
|
|
29
|
+
let oldUfileUrlRL = URL(fileURLWithPath: fileUrl)
|
|
30
|
+
let newURL = URL(fileURLWithPath: outputMp3Path)
|
|
31
|
+
|
|
37
32
|
var options = FormatConverter.Options()
|
|
38
|
-
|
|
33
|
+
|
|
39
34
|
if(audioOptions.samplerate != -1){
|
|
40
35
|
options.sampleRate = Double(audioOptions.samplerate)
|
|
41
36
|
}
|
|
42
|
-
|
|
37
|
+
|
|
43
38
|
if(audioOptions.bitrate != -1){
|
|
44
39
|
options.bitRate = UInt32(audioOptions.bitrate)
|
|
45
40
|
}
|
|
@@ -49,16 +44,16 @@ class AudioMain{
|
|
|
49
44
|
print("output bitrate: \(bitrate)")
|
|
50
45
|
options.bitRate = UInt32(bitrate)
|
|
51
46
|
}
|
|
52
|
-
|
|
53
|
-
|
|
47
|
+
|
|
48
|
+
|
|
54
49
|
if(audioOptions.channels != -1){
|
|
55
50
|
options.channels = UInt32(audioOptions.channels)
|
|
56
51
|
}
|
|
57
|
-
|
|
52
|
+
|
|
58
53
|
options.format = .m4a
|
|
59
54
|
options.eraseFile = false
|
|
60
55
|
options.bitDepthRule = .any
|
|
61
|
-
|
|
56
|
+
|
|
62
57
|
let converter = FormatConverter(inputURL: oldUfileUrlRL, outputURL: newURL, options: options)
|
|
63
58
|
converter.start { error in
|
|
64
59
|
// check to see if error isn't nil, otherwise you're good
|
|
@@ -68,10 +63,10 @@ class AudioMain{
|
|
|
68
63
|
reject(error?.localizedDescription,error?.localizedDescription, error)
|
|
69
64
|
return
|
|
70
65
|
}
|
|
71
|
-
|
|
66
|
+
|
|
72
67
|
resolve(newURL.absoluteString)
|
|
73
68
|
}
|
|
74
|
-
|
|
69
|
+
|
|
75
70
|
} catch {
|
|
76
71
|
reject(error.localizedDescription, error.localizedDescription, nil)
|
|
77
72
|
}
|
|
@@ -14,7 +14,7 @@ class CreateVideoThumbnail: NSObject {
|
|
|
14
14
|
func create(_ fileUrl:String, options: NSDictionary, resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
|
|
15
15
|
let headers = options["headers"] as? [String: Any] ?? [:]
|
|
16
16
|
let format = "jpeg"
|
|
17
|
-
|
|
17
|
+
|
|
18
18
|
do {
|
|
19
19
|
// Prepare cache folder
|
|
20
20
|
var tempDirectory = NSSearchPathForDirectoriesInDomains(.cachesDirectory, .userDomainMask, true).last ?? ""
|
|
@@ -77,8 +77,14 @@ class CreateVideoThumbnail: NSObject {
|
|
|
77
77
|
let cachePathDir: String = cacheDir.isEmpty ? "thumbnails/" : cacheDir
|
|
78
78
|
var cacheDirectoryPath = NSSearchPathForDirectoriesInDomains(.cachesDirectory, .userDomainMask, true).last ?? ""
|
|
79
79
|
cacheDirectoryPath = (cacheDirectoryPath as NSString).appendingPathComponent(cachePathDir)
|
|
80
|
-
|
|
80
|
+
|
|
81
81
|
let fm = FileManager.default
|
|
82
|
+
|
|
83
|
+
if !fm.fileExists(atPath: cacheDirectoryPath) {
|
|
84
|
+
reject("Error", "Directory does not exist", nil)
|
|
85
|
+
return
|
|
86
|
+
}
|
|
87
|
+
|
|
82
88
|
do {
|
|
83
89
|
let files = try fm.contentsOfDirectory(atPath: cacheDirectoryPath)
|
|
84
90
|
for file in files {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import Foundation
|
|
2
2
|
import AssetsLibrary
|
|
3
|
-
import AVFoundation
|
|
3
|
+
import AVFoundation
|
|
4
4
|
import Photos
|
|
5
5
|
import MobileCoreServices
|
|
6
6
|
|
|
@@ -10,7 +10,7 @@ struct CompressionError: Error {
|
|
|
10
10
|
var localizedDescription: String {
|
|
11
11
|
return message
|
|
12
12
|
}
|
|
13
|
-
|
|
13
|
+
|
|
14
14
|
init(message: String) {
|
|
15
15
|
self.message = message
|
|
16
16
|
}
|
|
@@ -18,9 +18,9 @@ struct CompressionError: Error {
|
|
|
18
18
|
|
|
19
19
|
class VideoCompressor {
|
|
20
20
|
var backgroundTaskId: UIBackgroundTaskIdentifier = .invalid;
|
|
21
|
-
|
|
21
|
+
|
|
22
22
|
var compressorExports: [String: NextLevelSessionExporter] = [:]
|
|
23
|
-
|
|
23
|
+
|
|
24
24
|
let metadatas: [String] = [
|
|
25
25
|
"albumName",
|
|
26
26
|
"artist",
|
|
@@ -74,11 +74,11 @@ class VideoCompressor {
|
|
|
74
74
|
reject("failed", "Compression Failed", error)
|
|
75
75
|
})
|
|
76
76
|
}
|
|
77
|
-
|
|
77
|
+
|
|
78
78
|
func cancelCompression(uuid: String) -> Void {
|
|
79
79
|
compressorExports[uuid]?.cancelExport()
|
|
80
80
|
}
|
|
81
|
-
|
|
81
|
+
|
|
82
82
|
func getfileSize(forURL url: Any) -> Double {
|
|
83
83
|
var fileURL: URL?
|
|
84
84
|
var fileSize: Double = 0.0
|
|
@@ -98,11 +98,11 @@ class VideoCompressor {
|
|
|
98
98
|
}
|
|
99
99
|
return fileSize
|
|
100
100
|
}
|
|
101
|
-
|
|
102
|
-
|
|
101
|
+
|
|
102
|
+
|
|
103
103
|
func compressVideo(url: URL, options: [String: Any], onProgress: @escaping (Float) -> Void, onCompletion: @escaping (URL) -> Void, onFailure: @escaping (Error) -> Void){
|
|
104
104
|
let uuid:String = options["uuid"] as! String
|
|
105
|
-
|
|
105
|
+
|
|
106
106
|
VideoCompressor.getAbsoluteVideoPath(url.absoluteString, options: options) { absoluteVideoPath in
|
|
107
107
|
var minimumFileSizeForCompress:Double=0.0;
|
|
108
108
|
let videoURL = URL(string: absoluteVideoPath)
|
|
@@ -135,8 +135,8 @@ class VideoCompressor {
|
|
|
135
135
|
onFailure(error)
|
|
136
136
|
}
|
|
137
137
|
}
|
|
138
|
-
|
|
139
|
-
|
|
138
|
+
|
|
139
|
+
|
|
140
140
|
|
|
141
141
|
}
|
|
142
142
|
else
|
|
@@ -166,12 +166,12 @@ class VideoCompressor {
|
|
|
166
166
|
func getVideoBitrateWithFactor(f:Float)->Int {
|
|
167
167
|
return Int(f * 2000 * 1000 * 1.13);
|
|
168
168
|
}
|
|
169
|
-
|
|
169
|
+
|
|
170
170
|
func autoCompressionHelper(url: URL, options: [String: Any], onProgress: @escaping (Float) -> Void, onCompletion: @escaping (URL) -> Void, onFailure: @escaping (Error) -> Void){
|
|
171
171
|
let maxSize:Float = options["maxSize"] as! Float;
|
|
172
172
|
let uuid:String = options["uuid"] as! String
|
|
173
173
|
let progressDivider=options["progressDivider"] as? Int ?? 0
|
|
174
|
-
|
|
174
|
+
|
|
175
175
|
let asset = AVAsset(url: url)
|
|
176
176
|
guard asset.tracks.count >= 1 else {
|
|
177
177
|
let error = CompressionError(message: "Invalid video URL, no track found")
|
|
@@ -179,7 +179,7 @@ class VideoCompressor {
|
|
|
179
179
|
return
|
|
180
180
|
}
|
|
181
181
|
let track = getVideoTrack(asset: asset);
|
|
182
|
-
|
|
182
|
+
|
|
183
183
|
let videoSize = track.naturalSize.applying(track.preferredTransform);
|
|
184
184
|
let actualWidth = Float(abs(videoSize.width))
|
|
185
185
|
let actualHeight = Float(abs(videoSize.height))
|
|
@@ -194,7 +194,7 @@ class VideoCompressor {
|
|
|
194
194
|
originalBitrate: Int(bitrate),
|
|
195
195
|
height: Int(resultHeight), width: Int(resultWidth)
|
|
196
196
|
);
|
|
197
|
-
|
|
197
|
+
|
|
198
198
|
exportVideoHelper(url: url, asset: asset, bitRate: videoBitRate, resultWidth: resultWidth, resultHeight: resultHeight,uuid: uuid,progressDivider: progressDivider) { progress in
|
|
199
199
|
onProgress(progress)
|
|
200
200
|
} onCompletion: { outputURL in
|
|
@@ -206,7 +206,7 @@ class VideoCompressor {
|
|
|
206
206
|
|
|
207
207
|
func manualCompressionHelper(url: URL, options: [String: Any], onProgress: @escaping (Float) -> Void, onCompletion: @escaping (URL) -> Void, onFailure: @escaping (Error) -> Void){
|
|
208
208
|
let uuid:String = options["uuid"] as! String
|
|
209
|
-
var bitRate=options["bitrate"] as
|
|
209
|
+
var bitRate = (options["bitrate"] as? NSNumber)?.floatValue;
|
|
210
210
|
let progressDivider=options["progressDivider"] as? Int ?? 0
|
|
211
211
|
let asset = AVAsset(url: url)
|
|
212
212
|
guard asset.tracks.count >= 1 else {
|
|
@@ -215,7 +215,7 @@ class VideoCompressor {
|
|
|
215
215
|
return
|
|
216
216
|
}
|
|
217
217
|
let track = getVideoTrack(asset: asset);
|
|
218
|
-
|
|
218
|
+
|
|
219
219
|
let videoSize = track.naturalSize.applying(track.preferredTransform);
|
|
220
220
|
var width = Float(abs(videoSize.width))
|
|
221
221
|
var height = Float(abs(videoSize.height))
|
|
@@ -243,19 +243,19 @@ class VideoCompressor {
|
|
|
243
243
|
onFailure(error)
|
|
244
244
|
}
|
|
245
245
|
}
|
|
246
|
-
|
|
246
|
+
|
|
247
247
|
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){
|
|
248
248
|
var currentVideoCompression:Int=0
|
|
249
|
-
|
|
249
|
+
|
|
250
250
|
var tmpURL = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true)
|
|
251
251
|
.appendingPathComponent(ProcessInfo().globallyUniqueString)
|
|
252
252
|
.appendingPathExtension("mp4")
|
|
253
253
|
tmpURL = URL(string: Utils.makeValidUri(filePath: tmpURL.absoluteString))!
|
|
254
|
-
|
|
254
|
+
|
|
255
255
|
let exporter = NextLevelSessionExporter(withAsset: asset)
|
|
256
256
|
exporter.outputURL = tmpURL
|
|
257
257
|
exporter.outputFileType = AVFileType.mp4
|
|
258
|
-
|
|
258
|
+
|
|
259
259
|
let compressionDict: [String: Any] = [
|
|
260
260
|
AVVideoAverageBitRateKey: bitRate,
|
|
261
261
|
AVVideoProfileLevelKey: AVVideoProfileLevelH264HighAutoLevel,
|
|
@@ -274,7 +274,7 @@ class VideoCompressor {
|
|
|
274
274
|
AVNumberOfChannelsKey: NSNumber(integerLiteral: 2),
|
|
275
275
|
AVSampleRateKey: NSNumber(value: Float(44100))
|
|
276
276
|
]
|
|
277
|
-
|
|
277
|
+
|
|
278
278
|
compressorExports[uuid] = exporter
|
|
279
279
|
exporter.export(progressHandler: { (progress) in
|
|
280
280
|
let roundProgress:Int=Int((progress*100).rounded());
|
|
@@ -299,33 +299,33 @@ class VideoCompressor {
|
|
|
299
299
|
}
|
|
300
300
|
})
|
|
301
301
|
}
|
|
302
|
-
|
|
302
|
+
|
|
303
303
|
func getVideoTrack(asset: AVAsset) -> AVAssetTrack {
|
|
304
304
|
let tracks = asset.tracks(withMediaType: AVMediaType.video)
|
|
305
305
|
return tracks[0];
|
|
306
306
|
}
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
|
|
310
310
|
func getVideoMetaData(_ filePath: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
|
|
311
311
|
do {
|
|
312
312
|
VideoCompressor.getAbsoluteVideoPath(filePath, options: [:]) { absoluteImagePath in
|
|
313
313
|
if absoluteImagePath.hasPrefix("file://") {
|
|
314
|
-
|
|
314
|
+
|
|
315
315
|
let absoluteImagePath = URL(string: absoluteImagePath)!.path
|
|
316
316
|
let fileManager = FileManager.default
|
|
317
317
|
var isDir: ObjCBool = false
|
|
318
|
-
|
|
318
|
+
|
|
319
319
|
if !fileManager.fileExists(atPath: absoluteImagePath, isDirectory: &isDir) || isDir.boolValue {
|
|
320
320
|
let err = NSError(domain: "file not found", code: -15, userInfo: nil)
|
|
321
321
|
reject(String(err.code), err.localizedDescription, err)
|
|
322
322
|
return
|
|
323
323
|
}
|
|
324
|
-
|
|
324
|
+
|
|
325
325
|
let attrs = try? fileManager.attributesOfItem(atPath: absoluteImagePath)
|
|
326
326
|
if let fileSize = attrs?[FileAttributeKey.size] as? UInt64 {
|
|
327
327
|
let fileSizeString = fileSize
|
|
328
|
-
|
|
328
|
+
|
|
329
329
|
var result: [String: Any] = [:]
|
|
330
330
|
let assetOptions: [String: Any] = [AVURLAssetPreferPreciseDurationAndTimingKey: true]
|
|
331
331
|
let asset = AVURLAsset(url: URL(fileURLWithPath: absoluteImagePath), options: assetOptions)
|
|
@@ -334,25 +334,25 @@ class VideoCompressor {
|
|
|
334
334
|
let _extension = (absoluteImagePath as NSString).pathExtension
|
|
335
335
|
let time = asset.duration
|
|
336
336
|
let seconds = Double(time.value) / Double(time.timescale)
|
|
337
|
-
|
|
337
|
+
|
|
338
338
|
result["width"] = size.width
|
|
339
339
|
result["height"] = size.height
|
|
340
340
|
result["extension"] = _extension
|
|
341
341
|
result["size"] = fileSizeString
|
|
342
342
|
result["duration"] = seconds
|
|
343
|
-
|
|
343
|
+
|
|
344
344
|
var commonMetadata: [AVMetadataItem] = []
|
|
345
345
|
for key in self.metadatas {
|
|
346
346
|
let items = AVMetadataItem.metadataItems(from: asset.commonMetadata, withKey: key, keySpace: AVMetadataKeySpace.common)
|
|
347
347
|
commonMetadata.append(contentsOf: items)
|
|
348
348
|
}
|
|
349
|
-
|
|
349
|
+
|
|
350
350
|
for item in commonMetadata {
|
|
351
351
|
if let value = item.value {
|
|
352
352
|
result[item.commonKey!.rawValue] = value
|
|
353
353
|
}
|
|
354
354
|
}
|
|
355
|
-
|
|
355
|
+
|
|
356
356
|
resolve(result)
|
|
357
357
|
}
|
|
358
358
|
}
|
|
@@ -362,7 +362,7 @@ class VideoCompressor {
|
|
|
362
362
|
reject(error.localizedDescription, error.localizedDescription, nil)
|
|
363
363
|
}
|
|
364
364
|
}
|
|
365
|
-
|
|
365
|
+
|
|
366
366
|
static func getAbsoluteVideoPath(_ videoPath: String, options: [String: Any], completionHandler: @escaping (String) -> Void) {
|
|
367
367
|
if videoPath.hasPrefix("http://") || videoPath.hasPrefix("https://") {
|
|
368
368
|
let uuid=options["uuid"] as? String ?? ""
|
|
@@ -434,5 +434,5 @@ class VideoCompressor {
|
|
|
434
434
|
}
|
|
435
435
|
}
|
|
436
436
|
}
|
|
437
|
-
|
|
437
|
+
|
|
438
438
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-compressor",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.9.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",
|