react-native-compressor 1.8.1 → 1.8.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/README.md +2 -1
  2. package/android/build.gradle +5 -2
  3. package/android/src/main/java/com/reactnativecompressor/Audio/AudioCompressor.kt +2 -2
  4. package/android/src/main/java/com/reactnativecompressor/CompressorModule.kt +3 -3
  5. package/android/src/main/java/com/reactnativecompressor/Utils/Uploader.kt +11 -11
  6. package/android/src/main/java/com/reactnativecompressor/Utils/Utils.kt +42 -36
  7. package/android/src/main/java/com/reactnativecompressor/Utils/createVideoThumbnail.kt +1 -1
  8. package/android/src/main/java/com/reactnativecompressor/Video/VideoCompressor/CompressionInterface.kt +73 -0
  9. package/android/src/main/java/com/reactnativecompressor/Video/VideoCompressor/VideoCompressorClass.kt +148 -0
  10. package/android/src/main/java/com/reactnativecompressor/Video/VideoCompressor/compressor/Compressor.kt +562 -0
  11. package/android/src/main/java/com/reactnativecompressor/Video/VideoCompressor/utils/Atoms.kt +55 -0
  12. package/android/src/main/java/com/reactnativecompressor/Video/VideoCompressor/utils/CompressorUtils.kt +196 -0
  13. package/android/src/main/java/com/reactnativecompressor/Video/VideoCompressor/utils/NumbersUtils.kt +47 -0
  14. package/android/src/main/java/com/reactnativecompressor/Video/VideoCompressor/utils/StreamableVideo.kt +209 -0
  15. package/android/src/main/java/com/reactnativecompressor/Video/VideoCompressor/videoHelpers/InputSurface.kt +128 -0
  16. package/android/src/main/java/com/reactnativecompressor/Video/VideoCompressor/videoHelpers/MP4Builder.kt +388 -0
  17. package/android/src/main/java/com/reactnativecompressor/Video/VideoCompressor/videoHelpers/Mdat.kt +74 -0
  18. package/android/src/main/java/com/reactnativecompressor/Video/VideoCompressor/videoHelpers/Mp4Movie.kt +54 -0
  19. package/android/src/main/java/com/reactnativecompressor/Video/VideoCompressor/videoHelpers/OutputSurface.kt +102 -0
  20. package/android/src/main/java/com/reactnativecompressor/Video/VideoCompressor/videoHelpers/Result.kt +9 -0
  21. package/android/src/main/java/com/reactnativecompressor/Video/VideoCompressor/videoHelpers/Sample.kt +3 -0
  22. package/android/src/main/java/com/reactnativecompressor/Video/VideoCompressor/videoHelpers/TextureRenderer.kt +214 -0
  23. package/android/src/main/java/com/reactnativecompressor/Video/VideoCompressor/videoHelpers/Track.kt +286 -0
  24. package/android/src/oldarch/CompressorSpec.kt +2 -2
  25. package/lib/commonjs/utils/index.js +2 -2
  26. package/lib/commonjs/utils/index.js.map +1 -1
  27. package/lib/module/utils/index.js +2 -2
  28. package/lib/module/utils/index.js.map +1 -1
  29. package/lib/typescript/index.d.ts +13 -3
  30. package/lib/typescript/index.d.ts.map +1 -1
  31. package/lib/typescript/utils/index.d.ts +16 -3
  32. package/lib/typescript/utils/index.d.ts.map +1 -1
  33. package/package.json +1 -1
  34. package/src/utils/index.tsx +22 -8
package/README.md CHANGED
@@ -68,6 +68,7 @@ We should use **react-native-compressor** instead of **FFmpeg** because **react-
68
68
  - [Audio](#audio)
69
69
  - [Background Upload](#background-upload)
70
70
  - [Download File](#download)
71
+ - [Create Video Thumbnail and Clear Cache](#create-video-thumbnail-and-clear-cache)
71
72
 
72
73
  * [Other Utilities](#api)
73
74
  - [Background Upload](#background-upload-1)
@@ -431,7 +432,7 @@ type FileSystemUploadOptions = (
431
432
 
432
433
  - ##### download: ( fileUrl: string, downloadProgress?: (progress: number) => void, progressDivider?: number ) => Promise< string >
433
434
 
434
- ### Create Video Thumbnail and clear cache
435
+ ### Create Video Thumbnail and Clear Cache
435
436
 
436
437
  - #### createVideoThumbnail( fileUrl: string, options: {header:Object} ): Promise<{ path: string;size: number; mime: string; width: number; height: number; }>
437
438
 
@@ -110,9 +110,12 @@ dependencies {
110
110
  //noinspection GradleDynamicVersion
111
111
  implementation "com.facebook.react:react-native:+"
112
112
  implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
113
- implementation 'commons-io:commons-io:2.8.0'
113
+
114
+ implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4"
115
+ implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4"
116
+ implementation "com.googlecode.mp4parser:isoparser:1.0.6"
117
+
114
118
  implementation 'io.github.lizhangqu:coreprogress:1.0.2'
115
- implementation 'com.github.numandev1:VideoCompressor:1a262bba37'
116
119
  }
117
120
 
118
121
  if (isNewArchitectureEnabled()) {
@@ -11,7 +11,7 @@ import android.media.MediaMuxer
11
11
  import android.os.Build
12
12
  import android.util.Log
13
13
  import androidx.annotation.RequiresApi
14
- import numan.dev.videocompressor.codecinputsurface.CodecInputSurface
14
+ import com.reactnativecompressor.Video.VideoCompressor.video.OutputSurface
15
15
  import java.io.File
16
16
  import java.io.IOException
17
17
  import java.nio.ByteBuffer
@@ -24,7 +24,7 @@ class AudioCompressor {
24
24
  private var mEncoder: MediaCodec? = null
25
25
  private var mDecoder: MediaCodec? = null
26
26
  private var mTrackIndex = 0
27
- private var mInputSurface: CodecInputSurface? = null
27
+ private var mInputSurface: OutputSurface? = null
28
28
 
29
29
  // bit rate, in bits per second
30
30
  private var mBitRate = -1
@@ -8,7 +8,7 @@ import com.facebook.react.bridge.ReactMethod
8
8
  import com.facebook.react.bridge.ReadableMap
9
9
  import com.reactnativecompressor.Audio.AudioMain
10
10
  import com.reactnativecompressor.Image.ImageMain
11
- import com.reactnativecompressor.Utils.CreateVideoThumbnail
11
+ import com.reactnativecompressor.Utils.CreateVideoThumbnailClass
12
12
  import com.reactnativecompressor.Utils.Downloader
13
13
  import com.reactnativecompressor.Utils.EventEmitterHandler
14
14
  import com.reactnativecompressor.Utils.Uploader
@@ -21,7 +21,7 @@ class CompressorModule(private val reactContext: ReactApplicationContext) : Comp
21
21
  private val imageMain: ImageMain = ImageMain(reactContext)
22
22
  private val videoMain: VideoMain = VideoMain(reactContext)
23
23
  private val audioMain: AudioMain = AudioMain(reactContext)
24
- private val videoThumbnail: CreateVideoThumbnail = CreateVideoThumbnail(reactContext)
24
+ private val videoThumbnail: CreateVideoThumbnailClass = CreateVideoThumbnailClass(reactContext)
25
25
 
26
26
  override fun initialize() {
27
27
  super.initialize()
@@ -156,7 +156,7 @@ class CompressorModule(private val reactContext: ReactApplicationContext) : Comp
156
156
 
157
157
  @ReactMethod
158
158
  override fun clearCache(cacheDir:String?, promise:Promise) {
159
- CreateVideoThumbnail.clearCache(cacheDir, promise, reactContext)
159
+ CreateVideoThumbnailClass.clearCache(cacheDir, promise, reactContext)
160
160
  }
161
161
 
162
162
  @ReactMethod
@@ -32,17 +32,17 @@ object Uploader {
32
32
  if (url != null) {
33
33
  builder.url(url)
34
34
  }
35
- val headerIterator = options!!.headers!!.keySetIterator()
36
- while (headerIterator.hasNextKey()) {
37
- val key = headerIterator.nextKey()
38
- val value = options.headers!!.getString(key)
39
- Log.d(TAG, "$key value: $value")
40
- builder.addHeader(key, value.toString())
41
- if (key.lowercase(Locale.getDefault()) === "content-type:") {
42
- contentType = value
43
- }
35
+ val headerIterator = options?.headers?.keySetIterator()
36
+ while (headerIterator?.hasNextKey() == true) {
37
+ val key = headerIterator.nextKey()
38
+ val value = options.headers?.getString(key)
39
+ Log.d(TAG, "$key value: $value")
40
+ builder.addHeader(key, value.toString())
41
+ if (key.lowercase(Locale.getDefault()) == "content-type:") {
42
+ contentType = value
43
+ }
44
44
  }
45
- val mediaType: MediaType = contentType?.toMediaTypeOrNull()!!;
45
+ val mediaType: MediaType? = contentType?.toMediaTypeOrNull();
46
46
  val body = RequestBody.create(mediaType, uploadableFile)
47
47
  val requestBody = ProgressHelper.withProgress(body, object : ProgressUIListener() {
48
48
  //if you don't need this method, don't override this methd. It isn't an abstract method, just an empty method.
@@ -52,7 +52,7 @@ object Uploader {
52
52
  }
53
53
 
54
54
  override fun onUIProgressChanged(numBytes: Long, totalBytes: Long, percent: Float, speed: Float) {
55
- EventEmitterHandler.sendUploadProgressEvent(numBytes,totalBytes,options.uuid)
55
+ EventEmitterHandler.sendUploadProgressEvent(numBytes,totalBytes,options?.uuid)
56
56
  Log.d(TAG, "=============start===============")
57
57
  Log.d(TAG, "numBytes:$numBytes")
58
58
  Log.d(TAG, "totalBytes:$totalBytes")
@@ -7,8 +7,8 @@ import android.provider.OpenableColumns
7
7
  import android.util.Log
8
8
  import com.facebook.react.bridge.Promise
9
9
  import com.facebook.react.bridge.ReactApplicationContext
10
- import numan.dev.videocompressor.VideoCompressTask
11
- import numan.dev.videocompressor.VideoCompressor
10
+ import com.reactnativecompressor.Video.VideoCompressor.CompressionListener
11
+ import com.reactnativecompressor.Video.VideoCompressor.VideoCompressorClass
12
12
  import java.io.FileNotFoundException
13
13
  import java.io.IOException
14
14
  import java.net.HttpURLConnection
@@ -17,7 +17,7 @@ import java.util.UUID
17
17
 
18
18
  object Utils {
19
19
  private const val TAG = "react-native-compessor"
20
- var compressorExports: MutableMap<String, VideoCompressTask> = HashMap()
20
+ var compressorExports: MutableMap<String, VideoCompressorClass?> = HashMap()
21
21
  @JvmStatic
22
22
  fun generateCacheFilePath(extension: String, reactContext: ReactApplicationContext): String {
23
23
  val outputDir = reactContext.cacheDir
@@ -26,48 +26,54 @@ object Utils {
26
26
 
27
27
  @JvmStatic
28
28
  fun compressVideo(srcPath: String, destinationPath: String, resultWidth: Int, resultHeight: Int, videoBitRate: Float, uuid: String,progressDivider:Int, promise: Promise, reactContext: ReactApplicationContext) {
29
- val currentVideoCompression = intArrayOf(0)
30
- try {
31
- val export = VideoCompressor.convertVideo(srcPath, destinationPath, resultWidth, resultHeight, videoBitRate.toInt(), object : VideoCompressor.ProgressListener {
32
- override fun onStart() {
33
- //convert start
29
+ val currentVideoCompression = intArrayOf(0)
30
+ val videoCompressorClass: VideoCompressorClass? = VideoCompressorClass(reactContext);
31
+ compressorExports[uuid] = videoCompressorClass
32
+ videoCompressorClass?.start(srcPath, destinationPath, resultWidth, resultHeight, videoBitRate.toInt(),
33
+ listener = object : CompressionListener {
34
+ override fun onProgress(index: Int, percent: Float) {
35
+ if (percent <= 100)
36
+ {
37
+ val roundProgress = Math.round(percent)
38
+ if (progressDivider==0||(roundProgress % progressDivider == 0 && roundProgress > currentVideoCompression[0])) {
39
+ EventEmitterHandler.emitVideoCompressProgress((percent / 100).toDouble(),uuid)
40
+ currentVideoCompression[0] = roundProgress
34
41
  }
42
+ }
43
+ }
35
44
 
36
- override fun onFinish(result: Boolean) {
37
- val fileUrl = "file://$destinationPath"
38
- //convert finish,result(true is success,false is fail)
39
- promise.resolve(fileUrl)
40
- MediaCache.removeCompletedImagePath(fileUrl)
41
- }
45
+ override fun onStart(index: Int) {
42
46
 
43
- override fun onError(errorMessage: String) {
44
- if (errorMessage == "class java.lang.AssertionError") {
45
- promise.resolve("file://$srcPath")
46
- } else {
47
- promise.reject("Compression has canncelled")
48
- }
49
- }
47
+ }
50
48
 
51
- override fun onProgress(percent: Float) {
52
- val roundProgress = Math.round(percent)
53
- if (progressDivider==0||(roundProgress % progressDivider == 0 && roundProgress > currentVideoCompression[0])) {
54
- EventEmitterHandler.emitVideoCompressProgress((percent / 100).toDouble(),uuid)
55
- currentVideoCompression[0] = roundProgress
56
- }
57
- }
58
- })
59
- compressorExports[uuid] = export
60
- } catch (ex: Exception) {
61
- promise.reject(ex)
62
- } finally {
63
- currentVideoCompression[0] = 0
64
- }
49
+ override fun onSuccess(index: Int, size: Long, path: String?) {
50
+ val fileUrl = "file://$destinationPath"
51
+ //convert finish,result(true is success,false is fail)
52
+ promise.resolve(fileUrl)
53
+ MediaCache.removeCompletedImagePath(fileUrl)
54
+ currentVideoCompression[0] = 0
55
+ compressorExports[uuid]=null
56
+ }
57
+
58
+ override fun onFailure(index: Int, failureMessage: String) {
59
+ Log.wtf("failureMessage", failureMessage)
60
+ currentVideoCompression[0] = 0
61
+ }
62
+
63
+ override fun onCancelled(index: Int) {
64
+ Log.wtf("TAG", "compression has been cancelled")
65
+ // make UI changes, cleanup, etc
66
+ currentVideoCompression[0] = 0
67
+ }
68
+ },
69
+ )
65
70
  }
66
71
 
67
72
  fun cancelCompressionHelper(uuid: String) {
68
73
  try {
69
74
  val export = compressorExports[uuid]
70
- export!!.cancel(true)
75
+ export?.cancel()
76
+ compressorExports[uuid]=null
71
77
  } catch (ex: Exception) {
72
78
  }
73
79
  }
@@ -26,7 +26,7 @@ import java.net.URLDecoder
26
26
  import java.util.UUID
27
27
 
28
28
 
29
- class CreateVideoThumbnail(private val reactContext: ReactApplicationContext) {
29
+ class CreateVideoThumbnailClass(private val reactContext: ReactApplicationContext) {
30
30
  @ReactMethod
31
31
  fun create(fileUrl:String,options: ReadableMap, promise: Promise) {
32
32
  ProcessDataTask(reactContext,fileUrl, promise, options).execute()
@@ -0,0 +1,73 @@
1
+ package com.reactnativecompressor.Video.VideoCompressor
2
+
3
+ import androidx.annotation.MainThread
4
+ import androidx.annotation.WorkerThread
5
+
6
+ /**
7
+ * Interface for listening to video compression events.
8
+ */
9
+ interface CompressionListener {
10
+ /**
11
+ * Called when video compression starts.
12
+ *
13
+ * @param index The index of the video being compressed.
14
+ */
15
+ @MainThread
16
+ fun onStart(index: Int)
17
+
18
+ /**
19
+ * Called when video compression succeeds.
20
+ *
21
+ * @param index The index of the video that was compressed.
22
+ * @param size The size of the compressed video.
23
+ * @param path The path to the compressed video file (nullable).
24
+ */
25
+ @MainThread
26
+ fun onSuccess(index: Int, size: Long, path: String?)
27
+
28
+ /**
29
+ * Called when video compression fails.
30
+ *
31
+ * @param index The index of the video that failed to compress.
32
+ * @param failureMessage A message describing the reason for the failure.
33
+ */
34
+ @MainThread
35
+ fun onFailure(index: Int, failureMessage: String)
36
+
37
+ /**
38
+ * Called to report the progress of video compression.
39
+ *
40
+ * @param index The index of the video being compressed.
41
+ * @param percent The progress percentage (0.0 to 100.0).
42
+ */
43
+ @WorkerThread
44
+ fun onProgress(index: Int, percent: Float)
45
+
46
+ /**
47
+ * Called when video compression is canceled.
48
+ *
49
+ * @param index The index of the video that was canceled.
50
+ */
51
+ @WorkerThread
52
+ fun onCancelled(index: Int)
53
+ }
54
+
55
+ /**
56
+ * Interface for tracking the progress of video compression.
57
+ */
58
+ interface CompressionProgressListener {
59
+ /**
60
+ * Called when the progress of video compression changes.
61
+ *
62
+ * @param index The index of the video being compressed.
63
+ * @param percent The current progress percentage (0.0 to 100.0).
64
+ */
65
+ fun onProgressChanged(index: Int, percent: Float)
66
+
67
+ /**
68
+ * Called when the compression progress is canceled.
69
+ *
70
+ * @param index The index of the video compression that was canceled.
71
+ */
72
+ fun onProgressCancelled(index: Int)
73
+ }
@@ -0,0 +1,148 @@
1
+ package com.reactnativecompressor.Video.VideoCompressor
2
+
3
+ import android.database.Cursor
4
+ import android.net.Uri
5
+ import android.provider.MediaStore
6
+ import com.facebook.react.bridge.ReactApplicationContext
7
+ import com.reactnativecompressor.Video.VideoCompressor.compressor.Compressor.compressVideo
8
+ import com.reactnativecompressor.Video.VideoCompressor.compressor.Compressor.isRunning
9
+ import com.reactnativecompressor.Video.VideoCompressor.video.Result
10
+ import kotlinx.coroutines.CoroutineScope
11
+ import kotlinx.coroutines.Dispatchers
12
+ import kotlinx.coroutines.Job
13
+ import kotlinx.coroutines.launch
14
+ import kotlinx.coroutines.withContext
15
+ import java.io.File
16
+
17
+ class VideoCompressorClass(private val context: ReactApplicationContext) {
18
+ private var job: Job? = null
19
+
20
+ @JvmOverloads
21
+ fun start(
22
+ srcPath: String,
23
+ destPath: String,
24
+ outputWidth: Int,
25
+ outputHeight: Int,
26
+ bitrate: Int,
27
+ listener: CompressionListener,
28
+ ) {
29
+ val uris = mutableListOf<Uri>()
30
+ val uri = Uri.parse(srcPath)
31
+ uris.add(uri)
32
+ val isStreamable: Boolean = false
33
+ doVideoCompression(
34
+ uris,
35
+ isStreamable,
36
+ outputWidth,
37
+ outputHeight,
38
+ bitrate,
39
+ listener,
40
+ destPath
41
+ )
42
+ }
43
+
44
+ fun cancel() {
45
+ job?.cancel()
46
+ isRunning = false
47
+ }
48
+
49
+ private fun doVideoCompression(
50
+ uris: List<Uri>,
51
+ isStreamable: Boolean,
52
+ outputWidth: Int,
53
+ outputHeight: Int,
54
+ bitrate: Int,
55
+ listener: CompressionListener,
56
+ destPath: String
57
+ ) {
58
+ var streamableFile: File? = null
59
+ for (i in uris.indices) {
60
+ job = CoroutineScope(Dispatchers.Main).launch {
61
+ val desFile = File(destPath)
62
+
63
+ if (isStreamable)
64
+ streamableFile = File(destPath)
65
+
66
+ desFile?.let {
67
+ isRunning = true
68
+ listener.onStart(i)
69
+ val result = startCompression(
70
+ i,
71
+ uris[i],
72
+ desFile.path,
73
+ streamableFile?.path,
74
+ outputWidth,
75
+ outputHeight,
76
+ bitrate,
77
+ listener,
78
+ )
79
+
80
+ // Runs in Main(UI) Thread
81
+ if (result.success) {
82
+ listener.onSuccess(i, result.size, result.path)
83
+ } else {
84
+ listener.onFailure(i, result.failureMessage ?: "An error has occurred!")
85
+ }
86
+ }
87
+ }
88
+ }
89
+ }
90
+
91
+ private suspend fun startCompression(
92
+ index: Int,
93
+ srcUri: Uri,
94
+ destPath: String,
95
+ streamableFile: String? = null,
96
+ outputWidth: Int,
97
+ outputHeight: Int,
98
+ bitrate: Int,
99
+ listener: CompressionListener,
100
+ ): Result = withContext(Dispatchers.Default) {
101
+ return@withContext compressVideo(
102
+ index,
103
+ context,
104
+ srcUri,
105
+ destPath,
106
+ streamableFile,
107
+ outputWidth,
108
+ outputHeight,
109
+ bitrate,
110
+ object : CompressionProgressListener {
111
+ override fun onProgressChanged(index: Int, percent: Float) {
112
+ listener.onProgress(index, percent)
113
+ }
114
+
115
+ override fun onProgressCancelled(index: Int) {
116
+ listener.onCancelled(index)
117
+ }
118
+ },
119
+ )
120
+ }
121
+
122
+ private fun getMediaPath(uri: Uri): String {
123
+ val scheme = uri.scheme
124
+ if (scheme == null || scheme == "file") {
125
+ // The URI is a file URI, so use the path directly.
126
+ return uri.path ?: ""
127
+ } else {
128
+ // The URI is not a file URI, so try to copy the content to a file.
129
+ val resolver = context.contentResolver
130
+ val projection = arrayOf(MediaStore.Video.Media.DATA)
131
+ var cursor: Cursor? = null
132
+ try {
133
+ cursor = resolver.query(uri, projection, null, null, null)
134
+ if (cursor != null && cursor.moveToFirst()) {
135
+ val columnIndex = cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DATA)
136
+ return cursor.getString(columnIndex)
137
+ }
138
+ } catch (e: Exception) {
139
+ // Handle the exception as needed.
140
+ } finally {
141
+ cursor?.close()
142
+ }
143
+ }
144
+
145
+ // If all else fails, return an empty string or handle the error accordingly.
146
+ return ""
147
+ }
148
+ }