react-native-compressor 1.8.6 → 1.8.7
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 +2 -1
- package/android/build.gradle +0 -1
- package/android/src/main/java/com/reactnativecompressor/Audio/AudioCompressor.kt +33 -5
- package/android/src/main/java/com/reactnativecompressor/Audio/AudioExtractor.kt +112 -0
- package/android/src/main/java/com/reactnativecompressor/Audio/AudioMain.kt +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -267,7 +267,7 @@ Video.cancelCompression(cancellationVideoId);
|
|
|
267
267
|
import { Audio } from 'react-native-compressor';
|
|
268
268
|
|
|
269
269
|
const result = await Audio.compress(
|
|
270
|
-
'file://path_of_file/file_example_MP3_2MG.
|
|
270
|
+
'file://path_of_file/file_example_MP3_2MG.wav', // recommended wav file but can be use mp3 file
|
|
271
271
|
{ quality: 'medium' }
|
|
272
272
|
);
|
|
273
273
|
```
|
|
@@ -395,6 +395,7 @@ await clearCache(); // this will clear cache of thumbnails cache directory
|
|
|
395
395
|
## Audio
|
|
396
396
|
|
|
397
397
|
- ###### `compress(url: string, options?: audioCompresssionType): Promise<string>`
|
|
398
|
+
Android: recommended to use `wav` file as we convert mp3 to wav then apply bitrate
|
|
398
399
|
|
|
399
400
|
### audioCompresssionType
|
|
400
401
|
|
package/android/build.gradle
CHANGED
|
@@ -116,7 +116,6 @@ dependencies {
|
|
|
116
116
|
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4"
|
|
117
117
|
implementation "com.googlecode.mp4parser:isoparser:1.0.6"
|
|
118
118
|
|
|
119
|
-
implementation 'io.github.lizhangqu:coreprogress:1.0.2'
|
|
120
119
|
implementation 'com.github.banketree:AndroidLame-kotlin:v0.0.1'
|
|
121
120
|
implementation 'javazoom:jlayer:1.0.1'
|
|
122
121
|
}
|
|
@@ -10,6 +10,7 @@ import com.reactnativecompressor.Utils.MediaCache
|
|
|
10
10
|
import com.reactnativecompressor.Utils.Utils
|
|
11
11
|
import com.reactnativecompressor.Utils.Utils.addLog
|
|
12
12
|
import javazoom.jl.converter.Converter
|
|
13
|
+
import javazoom.jl.decoder.JavaLayerException
|
|
13
14
|
import java.io.BufferedOutputStream
|
|
14
15
|
import java.io.File
|
|
15
16
|
import java.io.FileNotFoundException
|
|
@@ -30,16 +31,38 @@ class AudioCompressor {
|
|
|
30
31
|
context: ReactApplicationContext,
|
|
31
32
|
promise: Promise,
|
|
32
33
|
) {
|
|
34
|
+
var _fileUrl=fileUrl
|
|
33
35
|
try {
|
|
34
36
|
val filePath = fileUrl.replace("file://", "")
|
|
35
37
|
var wavPath=filePath;
|
|
36
38
|
var isNonWav:Boolean=false
|
|
37
|
-
if (
|
|
39
|
+
if (fileUrl.endsWith(".mp4", ignoreCase = true))
|
|
38
40
|
{
|
|
39
|
-
addLog("
|
|
41
|
+
addLog("mp4 file found")
|
|
42
|
+
val mp3Path= Utils.generateCacheFilePath("mp3", context)
|
|
43
|
+
AudioExtractor().genVideoUsingMuxer(fileUrl, mp3Path, -1, -1, true, false)
|
|
44
|
+
_fileUrl="file:/"+mp3Path
|
|
40
45
|
wavPath= Utils.generateCacheFilePath("wav", context)
|
|
46
|
+
try {
|
|
47
|
+
val converter = Converter()
|
|
48
|
+
converter.convert(mp3Path, wavPath)
|
|
49
|
+
} catch (e: JavaLayerException) {
|
|
50
|
+
addLog("JavaLayerException error"+e.localizedMessage)
|
|
51
|
+
e.printStackTrace();
|
|
52
|
+
}
|
|
53
|
+
isNonWav=true
|
|
54
|
+
}
|
|
55
|
+
else if (!fileUrl.endsWith(".wav", ignoreCase = true))
|
|
56
|
+
{
|
|
57
|
+
addLog("non wav file found")
|
|
58
|
+
wavPath= Utils.generateCacheFilePath("wav", context)
|
|
59
|
+
try {
|
|
41
60
|
val converter = Converter()
|
|
42
61
|
converter.convert(filePath, wavPath)
|
|
62
|
+
} catch (e: JavaLayerException) {
|
|
63
|
+
addLog("JavaLayerException error"+e.localizedMessage)
|
|
64
|
+
e.printStackTrace();
|
|
65
|
+
}
|
|
43
66
|
isNonWav=true
|
|
44
67
|
}
|
|
45
68
|
|
|
@@ -56,11 +79,11 @@ class AudioCompressor {
|
|
|
56
79
|
promise.resolve(returnableFilePath)
|
|
57
80
|
} else {
|
|
58
81
|
addLog("error: "+mp3Path)
|
|
59
|
-
promise.resolve(
|
|
82
|
+
promise.resolve(_fileUrl)
|
|
60
83
|
}
|
|
61
84
|
}
|
|
62
85
|
} catch (e: Exception) {
|
|
63
|
-
promise.resolve(
|
|
86
|
+
promise.resolve(_fileUrl)
|
|
64
87
|
}
|
|
65
88
|
}
|
|
66
89
|
|
|
@@ -80,6 +103,7 @@ class AudioCompressor {
|
|
|
80
103
|
context: ReactApplicationContext,
|
|
81
104
|
completeCallback: (String, Boolean) -> Unit
|
|
82
105
|
) {
|
|
106
|
+
var isCompletedCallbackTriggered:Boolean=false
|
|
83
107
|
try {
|
|
84
108
|
var mp3Path = Utils.generateCacheFilePath("mp3", context)
|
|
85
109
|
val input = File(fileUrl)
|
|
@@ -180,7 +204,6 @@ class AudioCompressor {
|
|
|
180
204
|
addLog("flushing final mp3buffer")
|
|
181
205
|
val outputMp3buf = androidLame.flush(mp3Buf)
|
|
182
206
|
addLog("flushed $outputMp3buf bytes")
|
|
183
|
-
|
|
184
207
|
if (outputMp3buf > 0) {
|
|
185
208
|
try {
|
|
186
209
|
addLog("writing final mp3buffer to outputstream")
|
|
@@ -188,6 +211,7 @@ class AudioCompressor {
|
|
|
188
211
|
addLog("closing output stream")
|
|
189
212
|
outputStream!!.close()
|
|
190
213
|
completeCallback(output.absolutePath, true)
|
|
214
|
+
isCompletedCallbackTriggered=true
|
|
191
215
|
} catch (e: IOException) {
|
|
192
216
|
completeCallback(e.localizedMessage, false)
|
|
193
217
|
e.printStackTrace()
|
|
@@ -197,6 +221,10 @@ class AudioCompressor {
|
|
|
197
221
|
} catch (e: IOException) {
|
|
198
222
|
completeCallback(e.localizedMessage, false)
|
|
199
223
|
}
|
|
224
|
+
if(!isCompletedCallbackTriggered)
|
|
225
|
+
{
|
|
226
|
+
completeCallback("something went wrong", false)
|
|
227
|
+
}
|
|
200
228
|
}
|
|
201
229
|
|
|
202
230
|
|
|
@@ -0,0 +1,112 @@
|
|
|
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
|
+
}
|
|
@@ -14,7 +14,7 @@ class AudioMain(private val reactContext: ReactApplicationContext) {
|
|
|
14
14
|
val options = AudioHelper.fromMap(optionMap)
|
|
15
15
|
val quality = options.quality
|
|
16
16
|
val realPath = Utils.getRealPath(fileUrl, reactContext)
|
|
17
|
-
Utils.addLog(fileUrl + "
|
|
17
|
+
Utils.addLog(fileUrl + "\n realPath= " + realPath)
|
|
18
18
|
AudioCompressor.CompressAudio(realPath!!, quality!!,reactContext,promise)
|
|
19
19
|
} catch (ex: Exception) {
|
|
20
20
|
promise.reject(ex)
|