react-native-compressor 1.8.0 → 1.8.2

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 (47) hide show
  1. package/README.md +22 -1
  2. package/android/build.gradle +6 -2
  3. package/android/src/main/java/com/reactnativecompressor/Audio/AudioCompressor.kt +2 -2
  4. package/android/src/main/java/com/reactnativecompressor/CompressorModule.kt +12 -0
  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 +158 -0
  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 -0
  25. package/ios/Compressor.mm +9 -0
  26. package/ios/CompressorManager.swift +11 -0
  27. package/ios/Utils/CreateVideoThumbnail.swift +111 -0
  28. package/lib/commonjs/Spec/NativeCompressor.js.map +1 -1
  29. package/lib/commonjs/index.js +14 -0
  30. package/lib/commonjs/index.js.map +1 -1
  31. package/lib/commonjs/utils/index.js +12 -1
  32. package/lib/commonjs/utils/index.js.map +1 -1
  33. package/lib/module/Spec/NativeCompressor.js.map +1 -1
  34. package/lib/module/index.js +4 -2
  35. package/lib/module/index.js.map +1 -1
  36. package/lib/module/utils/index.js +7 -0
  37. package/lib/module/utils/index.js.map +1 -1
  38. package/lib/typescript/Spec/NativeCompressor.d.ts +8 -0
  39. package/lib/typescript/Spec/NativeCompressor.d.ts.map +1 -1
  40. package/lib/typescript/index.d.ts +15 -3
  41. package/lib/typescript/index.d.ts.map +1 -1
  42. package/lib/typescript/utils/index.d.ts +16 -1
  43. package/lib/typescript/utils/index.d.ts.map +1 -1
  44. package/package.json +1 -1
  45. package/src/Spec/NativeCompressor.ts +11 -0
  46. package/src/index.tsx +6 -0
  47. package/src/utils/index.tsx +28 -1
@@ -0,0 +1,562 @@
1
+ package com.reactnativecompressor.Video.VideoCompressor.compressor
2
+
3
+ import android.content.Context
4
+ import android.media.MediaCodec
5
+ import android.media.MediaExtractor
6
+ import android.media.MediaFormat
7
+ import android.media.MediaMetadataRetriever
8
+ import android.net.Uri
9
+ import android.os.Build
10
+ import android.util.Log
11
+ import com.reactnativecompressor.Video.VideoCompressor.CompressionProgressListener
12
+ import com.reactnativecompressor.Video.VideoCompressor.utils.CompressorUtils.findTrack
13
+ import com.reactnativecompressor.Video.VideoCompressor.utils.CompressorUtils.hasQTI
14
+ import com.reactnativecompressor.Video.VideoCompressor.utils.CompressorUtils.prepareVideoHeight
15
+ import com.reactnativecompressor.Video.VideoCompressor.utils.CompressorUtils.prepareVideoWidth
16
+ import com.reactnativecompressor.Video.VideoCompressor.utils.CompressorUtils.printException
17
+ import com.reactnativecompressor.Video.VideoCompressor.utils.CompressorUtils.setOutputFileParameters
18
+ import com.reactnativecompressor.Video.VideoCompressor.utils.CompressorUtils.setUpMP4Movie
19
+ import com.reactnativecompressor.Video.VideoCompressor.utils.StreamableVideo
20
+ import com.reactnativecompressor.Video.VideoCompressor.video.InputSurface
21
+ import com.reactnativecompressor.Video.VideoCompressor.video.MP4Builder
22
+ import com.reactnativecompressor.Video.VideoCompressor.video.OutputSurface
23
+ import com.reactnativecompressor.Video.VideoCompressor.video.Result
24
+ import kotlinx.coroutines.Dispatchers
25
+ import kotlinx.coroutines.withContext
26
+ import java.io.File
27
+ import java.nio.ByteBuffer
28
+
29
+ object Compressor {
30
+
31
+ // Minimum bitrate for compression (2Mbps)
32
+ private const val MIN_BITRATE = 2000000
33
+
34
+ // MIME type for H.264 Advanced Video Coding
35
+ private const val MIME_TYPE = "video/avc"
36
+
37
+ // Default timeout for MediaCodec operations
38
+ private const val MEDIACODEC_TIMEOUT_DEFAULT = 100L
39
+
40
+ // Error message for invalid bitrate
41
+ private const val INVALID_BITRATE =
42
+ "The provided bitrate is smaller than what is needed for compression, " +
43
+ "try to set isMinBitRateEnabled to false"
44
+
45
+ // Flag to check if compression is running
46
+ var isRunning = true
47
+
48
+ suspend fun compressVideo(
49
+ index: Int,
50
+ context: Context,
51
+ srcUri: Uri,
52
+ destination: String,
53
+ streamableFile: String?,
54
+ outputWidth: Int,
55
+ outputHeight: Int,
56
+ outputBitrate: Int,
57
+ listener: CompressionProgressListener,
58
+ ): Result = withContext(Dispatchers.Default) {
59
+
60
+ // Initialize MediaExtractor and MediaMetadataRetriever
61
+ val extractor = MediaExtractor()
62
+ val mediaMetadataRetriever = MediaMetadataRetriever()
63
+
64
+ try {
65
+ // Set the data source for the MediaMetadataRetriever
66
+ mediaMetadataRetriever.setDataSource(context, srcUri)
67
+ } catch (exception: IllegalArgumentException) {
68
+ printException(exception)
69
+ return@withContext Result(
70
+ index,
71
+ success = false,
72
+ failureMessage = "${exception.message}"
73
+ )
74
+ }
75
+
76
+ runCatching {
77
+ // Set the data source for the MediaExtractor
78
+ extractor.setDataSource(context, srcUri, null)
79
+ }
80
+
81
+ // Retrieve video metadata
82
+ val height: Double = prepareVideoHeight(mediaMetadataRetriever)
83
+ val width: Double = prepareVideoWidth(mediaMetadataRetriever)
84
+ val rotationData = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION)
85
+ val bitrateData = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_BITRATE)
86
+ val durationData = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION)
87
+
88
+ // Check if any metadata is missing
89
+ if (rotationData.isNullOrEmpty() || bitrateData.isNullOrEmpty() || durationData.isNullOrEmpty()) {
90
+ // Exit execution
91
+ return@withContext Result(
92
+ index,
93
+ success = false,
94
+ failureMessage = "Failed to extract video metadata, please try again"
95
+ )
96
+ }
97
+
98
+ var rotation = rotationData.toInt()
99
+ val duration = durationData.toLong() * 1000
100
+
101
+ // Handle new bitrate value
102
+ val newBitrate: Int = outputBitrate
103
+
104
+ // Handle new width and height values
105
+ var (newWidth, newHeight) = Pair(outputWidth, outputHeight)
106
+
107
+ // Handle rotation values and swapping height and width if needed
108
+ rotation = when (rotation) {
109
+ 90, 270 -> {
110
+ val tempHeight = newHeight
111
+ newHeight = newWidth
112
+ newWidth = tempHeight
113
+ 0
114
+ }
115
+ 180 -> 0
116
+ else -> rotation
117
+ }
118
+
119
+ // Start video compression
120
+ return@withContext start(
121
+ index,
122
+ newWidth!!,
123
+ newHeight!!,
124
+ destination,
125
+ newBitrate,
126
+ streamableFile,
127
+ false,
128
+ extractor,
129
+ listener,
130
+ duration,
131
+ rotation
132
+ )
133
+ }
134
+
135
+ // Function to start video compression
136
+ @Suppress("DEPRECATION")
137
+ private fun start(
138
+ id: Int,
139
+ newWidth: Int,
140
+ newHeight: Int,
141
+ destination: String,
142
+ newBitrate: Int,
143
+ streamableFile: String?,
144
+ disableAudio: Boolean,
145
+ extractor: MediaExtractor,
146
+ compressionProgressListener: CompressionProgressListener,
147
+ duration: Long,
148
+ rotation: Int
149
+ ): Result {
150
+ // Check if newWidth and newHeight are valid
151
+ if (newWidth != 0 && newHeight != 0) {
152
+ // Create a cache file for the compressed video
153
+ val cacheFile = File(destination)
154
+
155
+ try {
156
+ // MediaCodec accesses encoder and decoder components and processes the new video
157
+ // input to generate a compressed/smaller size video
158
+ val bufferInfo = MediaCodec.BufferInfo()
159
+
160
+ // Setup mp4 movie
161
+ val movie = setUpMP4Movie(rotation, cacheFile)
162
+
163
+ // MediaMuxer outputs MP4 in this app
164
+ val mediaMuxer = MP4Builder().createMovie(movie)
165
+
166
+ // Start with the video track
167
+ val videoIndex = findTrack(extractor, isVideo = true)
168
+
169
+ extractor.selectTrack(videoIndex)
170
+ extractor.seekTo(0, MediaExtractor.SEEK_TO_PREVIOUS_SYNC)
171
+ val inputFormat = extractor.getTrackFormat(videoIndex)
172
+
173
+ val outputFormat: MediaFormat =
174
+ MediaFormat.createVideoFormat(MIME_TYPE, newWidth, newHeight)
175
+
176
+ // Set output format
177
+ setOutputFileParameters(
178
+ inputFormat,
179
+ outputFormat,
180
+ newBitrate,
181
+ )
182
+
183
+ val decoder: MediaCodec
184
+
185
+ // Check if QTI hardware acceleration is available
186
+ val hasQTI = hasQTI()
187
+
188
+ // Prepare the video encoder
189
+ val encoder = prepareEncoder(outputFormat, hasQTI)
190
+
191
+ val inputSurface: InputSurface
192
+ val outputSurface: OutputSurface
193
+
194
+ try {
195
+ var inputDone = false
196
+ var outputDone = false
197
+
198
+ var videoTrackIndex = -5
199
+
200
+ inputSurface = InputSurface(encoder.createInputSurface())
201
+ inputSurface.makeCurrent()
202
+ // Move to executing state
203
+ encoder.start()
204
+
205
+ outputSurface = OutputSurface()
206
+
207
+ decoder = prepareDecoder(inputFormat, outputSurface)
208
+
209
+ // Move to executing state
210
+ decoder.start()
211
+
212
+ while (!outputDone) {
213
+ if (!inputDone) {
214
+
215
+ val index = extractor.sampleTrackIndex
216
+
217
+ if (index == videoIndex) {
218
+ val inputBufferIndex =
219
+ decoder.dequeueInputBuffer(MEDIACODEC_TIMEOUT_DEFAULT)
220
+ if (inputBufferIndex >= 0) {
221
+ val inputBuffer = decoder.getInputBuffer(inputBufferIndex)
222
+ val chunkSize = extractor.readSampleData(inputBuffer!!, 0)
223
+ when {
224
+ chunkSize < 0 -> {
225
+
226
+ decoder.queueInputBuffer(
227
+ inputBufferIndex,
228
+ 0,
229
+ 0,
230
+ 0L,
231
+ MediaCodec.BUFFER_FLAG_END_OF_STREAM
232
+ )
233
+ inputDone = true
234
+ }
235
+ else -> {
236
+
237
+ decoder.queueInputBuffer(
238
+ inputBufferIndex,
239
+ 0,
240
+ chunkSize,
241
+ extractor.sampleTime,
242
+ 0
243
+ )
244
+ extractor.advance()
245
+
246
+ }
247
+ }
248
+ }
249
+
250
+ } else if (index == -1) { //end of file
251
+ val inputBufferIndex =
252
+ decoder.dequeueInputBuffer(MEDIACODEC_TIMEOUT_DEFAULT)
253
+ if (inputBufferIndex >= 0) {
254
+ decoder.queueInputBuffer(
255
+ inputBufferIndex,
256
+ 0,
257
+ 0,
258
+ 0L,
259
+ MediaCodec.BUFFER_FLAG_END_OF_STREAM
260
+ )
261
+ inputDone = true
262
+ }
263
+ }
264
+ }
265
+
266
+ var decoderOutputAvailable = true
267
+ var encoderOutputAvailable = true
268
+
269
+ loop@ while (decoderOutputAvailable || encoderOutputAvailable) {
270
+
271
+ if (!isRunning) {
272
+ dispose(
273
+ videoIndex,
274
+ decoder,
275
+ encoder,
276
+ inputSurface,
277
+ outputSurface,
278
+ extractor
279
+ )
280
+
281
+ compressionProgressListener.onProgressCancelled(id)
282
+ return Result(
283
+ id,
284
+ success = false,
285
+ failureMessage = "The compression has stopped!"
286
+ )
287
+ }
288
+
289
+ //Encoder
290
+ val encoderStatus =
291
+ encoder.dequeueOutputBuffer(bufferInfo, MEDIACODEC_TIMEOUT_DEFAULT)
292
+
293
+ when {
294
+ encoderStatus == MediaCodec.INFO_TRY_AGAIN_LATER -> encoderOutputAvailable =
295
+ false
296
+ encoderStatus == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED -> {
297
+ val newFormat = encoder.outputFormat
298
+ if (videoTrackIndex == -5)
299
+ videoTrackIndex = mediaMuxer.addTrack(newFormat, false)
300
+ }
301
+ encoderStatus == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED -> {
302
+ // ignore this status
303
+ }
304
+ encoderStatus < 0 -> throw RuntimeException("unexpected result from encoder.dequeueOutputBuffer: $encoderStatus")
305
+ else -> {
306
+ val encodedData = encoder.getOutputBuffer(encoderStatus)
307
+ ?: throw RuntimeException("encoderOutputBuffer $encoderStatus was null")
308
+
309
+ if (bufferInfo.size > 1) {
310
+ if ((bufferInfo.flags and MediaCodec.BUFFER_FLAG_CODEC_CONFIG) == 0) {
311
+ mediaMuxer.writeSampleData(
312
+ videoTrackIndex,
313
+ encodedData, bufferInfo, false
314
+ )
315
+ }
316
+
317
+ }
318
+
319
+ outputDone =
320
+ bufferInfo.flags and MediaCodec.BUFFER_FLAG_END_OF_STREAM != 0
321
+ encoder.releaseOutputBuffer(encoderStatus, false)
322
+ }
323
+ }
324
+ if (encoderStatus != MediaCodec.INFO_TRY_AGAIN_LATER) continue@loop
325
+
326
+ //Decoder
327
+ val decoderStatus =
328
+ decoder.dequeueOutputBuffer(bufferInfo, MEDIACODEC_TIMEOUT_DEFAULT)
329
+ when {
330
+ decoderStatus == MediaCodec.INFO_TRY_AGAIN_LATER -> decoderOutputAvailable =
331
+ false
332
+ decoderStatus == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED -> {
333
+ // ignore this status
334
+ }
335
+ decoderStatus == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED -> {
336
+ // ignore this status
337
+ }
338
+ decoderStatus < 0 -> throw RuntimeException("unexpected result from decoder.dequeueOutputBuffer: $decoderStatus")
339
+ else -> {
340
+ val doRender = bufferInfo.size != 0
341
+
342
+ decoder.releaseOutputBuffer(decoderStatus, doRender)
343
+ if (doRender) {
344
+ var errorWait = false
345
+ try {
346
+ outputSurface.awaitNewImage()
347
+ } catch (e: Exception) {
348
+ errorWait = true
349
+ Log.e(
350
+ "Compressor",
351
+ e.message ?: "Compression failed at swapping buffer"
352
+ )
353
+ }
354
+
355
+ if (!errorWait) {
356
+ outputSurface.drawImage()
357
+
358
+ inputSurface.setPresentationTime(bufferInfo.presentationTimeUs * 1000)
359
+
360
+ compressionProgressListener.onProgressChanged(
361
+ id,
362
+ bufferInfo.presentationTimeUs.toFloat() / duration.toFloat() * 100
363
+ )
364
+
365
+ inputSurface.swapBuffers()
366
+ }
367
+ }
368
+ if ((bufferInfo.flags and MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
369
+ decoderOutputAvailable = false
370
+ encoder.signalEndOfInputStream()
371
+ }
372
+ }
373
+ }
374
+ }
375
+ }
376
+
377
+ } catch (exception: Exception) {
378
+ printException(exception)
379
+ return Result(id, success = false, failureMessage = exception.message)
380
+ }
381
+
382
+ // Release resources
383
+ dispose(
384
+ videoIndex,
385
+ decoder,
386
+ encoder,
387
+ inputSurface,
388
+ outputSurface,
389
+ extractor
390
+ )
391
+
392
+ // Process audio if necessary
393
+ processAudio(
394
+ mediaMuxer = mediaMuxer,
395
+ bufferInfo = bufferInfo,
396
+ disableAudio = disableAudio,
397
+ extractor
398
+ )
399
+
400
+ extractor.release()
401
+ try {
402
+ mediaMuxer.finishMovie()
403
+ } catch (e: Exception) {
404
+ printException(e)
405
+ }
406
+
407
+ } catch (exception: Exception) {
408
+ printException(exception)
409
+ }
410
+
411
+ var resultFile = cacheFile
412
+
413
+ // Process the result and create a streamable video if requested
414
+ streamableFile?.let {
415
+ try {
416
+ val result = StreamableVideo.start(`in` = cacheFile, out = File(it))
417
+ resultFile = File(it)
418
+ if (result && cacheFile.exists()) {
419
+ cacheFile.delete()
420
+ }
421
+
422
+ } catch (e: Exception) {
423
+ printException(e)
424
+ }
425
+ }
426
+ return Result(
427
+ id,
428
+ success = true,
429
+ failureMessage = null,
430
+ size = resultFile.length(),
431
+ resultFile.path
432
+ )
433
+ }
434
+
435
+ return Result(
436
+ id,
437
+ success = false,
438
+ failureMessage = "Something went wrong, please try again"
439
+ )
440
+ }
441
+
442
+ // Function to process audio
443
+ private fun processAudio(
444
+ mediaMuxer: MP4Builder,
445
+ bufferInfo: MediaCodec.BufferInfo,
446
+ disableAudio: Boolean,
447
+ extractor: MediaExtractor
448
+ ) {
449
+ val audioIndex = findTrack(extractor, isVideo = false)
450
+ if (audioIndex >= 0 && !disableAudio) {
451
+ extractor.selectTrack(audioIndex)
452
+ val audioFormat = extractor.getTrackFormat(audioIndex)
453
+ val muxerTrackIndex = mediaMuxer.addTrack(audioFormat, true)
454
+ var maxBufferSize = audioFormat.getInteger(MediaFormat.KEY_MAX_INPUT_SIZE)
455
+
456
+ if (maxBufferSize <= 0) {
457
+ maxBufferSize = 64 * 1024
458
+ }
459
+
460
+ var buffer: ByteBuffer = ByteBuffer.allocateDirect(maxBufferSize)
461
+ if (Build.VERSION.SDK_INT >= 28) {
462
+ val size = extractor.sampleSize
463
+ if (size > maxBufferSize) {
464
+ maxBufferSize = (size + 1024).toInt()
465
+ buffer = ByteBuffer.allocateDirect(maxBufferSize)
466
+ }
467
+ }
468
+ var inputDone = false
469
+ extractor.seekTo(0, MediaExtractor.SEEK_TO_PREVIOUS_SYNC)
470
+
471
+ while (!inputDone) {
472
+ val index = extractor.sampleTrackIndex
473
+ if (index == audioIndex) {
474
+ bufferInfo.size = extractor.readSampleData(buffer, 0)
475
+
476
+ if (bufferInfo.size >= 0) {
477
+ bufferInfo.apply {
478
+ presentationTimeUs = extractor.sampleTime
479
+ offset = 0
480
+ flags = MediaCodec.BUFFER_FLAG_KEY_FRAME
481
+ }
482
+ mediaMuxer.writeSampleData(muxerTrackIndex, buffer, bufferInfo, true)
483
+ extractor.advance()
484
+
485
+ } else {
486
+ bufferInfo.size = 0
487
+ inputDone = true
488
+ }
489
+ } else if (index == -1) {
490
+ inputDone = true
491
+ }
492
+ }
493
+ extractor.unselectTrack(audioIndex)
494
+ }
495
+ }
496
+
497
+ // Function to prepare the video encoder
498
+ private fun prepareEncoder(outputFormat: MediaFormat, hasQTI: Boolean): MediaCodec {
499
+
500
+ // This seems to cause an issue with certain phones
501
+ // val encoderName = MediaCodecList(REGULAR_CODECS).findEncoderForFormat(outputFormat)
502
+ // val encoder: MediaCodec = MediaCodec.createByCodecName(encoderName)
503
+ // Log.i("encoderName", encoder.name)
504
+ // c2.qti.avc.encoder results in a corrupted .mp4 video that does not play in
505
+ // Mac and iphones
506
+ val encoder = if (hasQTI) {
507
+ MediaCodec.createByCodecName("c2.android.avc.encoder")
508
+ } else {
509
+ MediaCodec.createEncoderByType(MIME_TYPE)
510
+ }
511
+ encoder.configure(
512
+ outputFormat, null, null,
513
+ MediaCodec.CONFIGURE_FLAG_ENCODE
514
+ )
515
+
516
+ return encoder
517
+ }
518
+
519
+ // Function to prepare the video decoder
520
+ private fun prepareDecoder(
521
+ inputFormat: MediaFormat,
522
+ outputSurface: OutputSurface,
523
+ ): MediaCodec {
524
+ // This seems to cause an issue with certain phones
525
+ // val decoderName =
526
+ // MediaCodecList(REGULAR_CODECS).findDecoderForFormat(inputFormat)
527
+ // val decoder = MediaCodec.createByCodecName(decoderName)
528
+ // Log.i("decoderName", decoder.name)
529
+
530
+ // val decoder = if (hasQTI) {
531
+ // MediaCodec.createByCodecName("c2.android.avc.decoder")
532
+ //} else {
533
+
534
+ val decoder = MediaCodec.createDecoderByType(inputFormat.getString(MediaFormat.KEY_MIME)!!)
535
+ //}
536
+
537
+ decoder.configure(inputFormat, outputSurface.getSurface(), null, 0)
538
+
539
+ return decoder
540
+ }
541
+
542
+ // Function to release resources
543
+ private fun dispose(
544
+ videoIndex: Int,
545
+ decoder: MediaCodec,
546
+ encoder: MediaCodec,
547
+ inputSurface: InputSurface,
548
+ outputSurface: OutputSurface,
549
+ extractor: MediaExtractor
550
+ ) {
551
+ extractor.unselectTrack(videoIndex)
552
+
553
+ decoder.stop()
554
+ decoder.release()
555
+
556
+ encoder.stop()
557
+ encoder.release()
558
+
559
+ inputSurface.release()
560
+ outputSurface.release()
561
+ }
562
+ }
@@ -0,0 +1,55 @@
1
+ package com.reactnativecompressor.Video.VideoCompressor.data
2
+
3
+ import java.nio.ByteBuffer
4
+ import java.nio.ByteOrder
5
+
6
+ /**
7
+ * Converts a four-character code (FOURCC) represented as a byte array into an integer.
8
+ * FOURCC is an identifier for video codecs, compression formats, and color/pixel formats in media files.
9
+ * Each FOURCC code is a 32-bit value stored in 4 bytes.
10
+ */
11
+ fun fourCcToInt(byteArray: ByteArray): Int {
12
+ // The bytes in the byteArray are ordered from most significant to least significant.
13
+ return ByteBuffer.wrap(byteArray).order(ByteOrder.BIG_ENDIAN).int
14
+ }
15
+
16
+ // Constants for commonly used FOURCC codes in media file formats:
17
+
18
+ // Unused space available in the file.
19
+ val FREE_ATOM = fourCcToInt(byteArrayOf('f'.code.toByte(), 'r'.code.toByte(), 'e'.code.toByte(), 'e'.code.toByte()))
20
+
21
+ // Junk data in the file.
22
+ val JUNK_ATOM = fourCcToInt(byteArrayOf('j'.code.toByte(), 'u'.code.toByte(), 'n'.code.toByte(), 'k'.code.toByte()))
23
+
24
+ // Media data containing samples like video frames and audio groups.
25
+ val MDAT_ATOM = fourCcToInt(byteArrayOf('m'.code.toByte(), 'd'.code.toByte(), 'a'.code.toByte(), 't'.code.toByte()))
26
+
27
+ // Metadata about the movie, including the number and type of tracks and sample data location.
28
+ val MOOV_ATOM = fourCcToInt(byteArrayOf('m'.code.toByte(), 'o'.code.toByte(), 'o'.code.toByte(), 'v'.code.toByte()))
29
+
30
+ // Reference to movie preview data.
31
+ val PNOT_ATOM = fourCcToInt(byteArrayOf('p'.code.toByte(), 'n'.code.toByte(), 'o'.code.toByte(), 't'.code.toByte()))
32
+
33
+ // Unused space available in the file.
34
+ val SKIP_ATOM = fourCcToInt(byteArrayOf('s'.code.toByte(), 'k'.code.toByte(), 'i'.code.toByte(), 'p'.code.toByte()))
35
+
36
+ // Reserved space that can be overwritten by an extended size field.
37
+ val WIDE_ATOM = fourCcToInt(byteArrayOf('w'.code.toByte(), 'i'.code.toByte(), 'd'.code.toByte(), 'e'.code.toByte()))
38
+
39
+ // Picture atom for graphics data.
40
+ val PICT_ATOM = fourCcToInt(byteArrayOf('P'.code.toByte(), 'I'.code.toByte(), 'C'.code.toByte(), 'T'.code.toByte()))
41
+
42
+ // File type compatibility atom that identifies the file type.
43
+ val FTYP_ATOM = fourCcToInt(byteArrayOf('f'.code.toByte(), 't'.code.toByte(), 'y'.code.toByte(), 'p'.code.toByte()))
44
+
45
+ // Universally unique identifier atom.
46
+ val UUID_ATOM = fourCcToInt(byteArrayOf('u'.code.toByte(), 'u'.code.toByte(), 'i'.code.toByte(), 'd'.code.toByte()))
47
+
48
+ // Compressed movie atom.
49
+ val CMOV_ATOM = fourCcToInt(byteArrayOf('c'.code.toByte(), 'm'.code.toByte(), 'o'.code.toByte(), 'v'.code.toByte()))
50
+
51
+ // Sample table chunk offset atom.
52
+ val STCO_ATOM = fourCcToInt(byteArrayOf('s'.code.toByte(), 't'.code.toByte(), 'c'.code.toByte(), 'o'.code.toByte()))
53
+
54
+ // 64-bit chunk offset atom.
55
+ val CO64_ATOM = fourCcToInt(byteArrayOf('c'.code.toByte(), 'o'.code.toByte(), '6'.code.toByte(), '4'.code.toByte()))