Skip to content

Commit

Permalink
fix: android audio compression
Browse files Browse the repository at this point in the history
  • Loading branch information
numandev1 committed Oct 1, 2023
1 parent 32f9ce2 commit 5a9c4d6
Show file tree
Hide file tree
Showing 10 changed files with 300 additions and 628 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -401,7 +401,7 @@ await clearCache(); // this will clear cache of thumbnails cache directory
- ###### `quality: qualityType` (default: medium)
we can also control bitrate through quality. qualityType can be `low` | `medium` | `high`

**Note: Audio compression will be add soon**
**Note: manual bitrate, samplerate etc will add soon**

## Background Upload

Expand Down
3 changes: 3 additions & 0 deletions android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ android {
repositories {
mavenCentral()
google()
maven { url "https://jitpack.io" }
}

def kotlin_version = getExtOrDefault("kotlinVersion")
Expand All @@ -116,6 +117,8 @@ dependencies {
implementation "com.googlecode.mp4parser:isoparser:1.0.6"

implementation 'io.github.lizhangqu:coreprogress:1.0.2'
implementation 'com.github.banketree:AndroidLame-kotlin:v0.0.1'
implementation 'javazoom:jlayer:1.0.1'
}

if (isNewArchitectureEnabled()) {
Expand Down
670 changes: 174 additions & 496 deletions android/src/main/java/com/reactnativecompressor/Audio/AudioCompressor.kt

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.reactnativecompressor.Audio

import com.facebook.react.bridge.ReadableMap

class AudioHelper {

var quality: String? = "medium"
var progressDivider: Int? = 0

companion object {
fun fromMap(map: ReadableMap): AudioHelper {
val options = AudioHelper()
val iterator = map.keySetIterator()
while (iterator.hasNextKey()) {
val key = iterator.nextKey()
when (key) {
"quality" -> options.quality = map.getString(key)
}
}
return options
}
}
}
18 changes: 5 additions & 13 deletions android/src/main/java/com/reactnativecompressor/Audio/AudioMain.kt
Original file line number Diff line number Diff line change
@@ -1,29 +1,21 @@
package com.reactnativecompressor.Audio

import android.media.MediaMetadataRetriever
import android.net.Uri
import android.util.Log
import com.facebook.react.bridge.Promise
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.bridge.ReadableMap
import com.reactnativecompressor.Utils.Utils
import com.reactnativecompressor.Video.VideoCompressorHelper

class AudioMain(private val reactContext: ReactApplicationContext) {
fun compress_audio(
fileUrl: String,
optionMap: ReadableMap,
promise: Promise) {
try {
val options = VideoCompressorHelper.fromMap(optionMap)
val uri = Uri.parse(fileUrl)
val srcPath = uri.path
val destinationPath = Utils.generateCacheFilePath("mp3", reactContext)
val metaRetriever = MediaMetadataRetriever()
metaRetriever.setDataSource(srcPath)
val bitrate = options.bitrate
Log.d("nomi onStart", destinationPath + "onProgress: " + bitrate)
AudioCompressor().CompressAudio(srcPath, destinationPath, bitrate.toInt() * 1000)
val options = AudioHelper.fromMap(optionMap)
val quality = options.quality
val realPath = Utils.getRealPath(fileUrl, reactContext)
Utils.addLog(fileUrl + "testPath" + realPath)
AudioCompressor.CompressAudio(realPath!!, quality!!,reactContext,promise)
} catch (ex: Exception) {
promise.reject(ex)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,21 @@ import android.os.Build
import android.os.Environment
import android.provider.DocumentsContract
import android.provider.MediaStore
import android.provider.OpenableColumns
import androidx.loader.content.CursorLoader
import com.facebook.react.bridge.ReactApplicationContext
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
import java.util.UUID


object RealPathUtil {
fun getRealPath(context: Context, fileUri: Uri): String? {

private const val FIELD_NAME = "name"
private const val FIELD_TYPE = "type"
private const val FIELD_SIZE = "size"
fun getRealPath(context: ReactApplicationContext, fileUri: Uri): String? {
val realPath: String?
// SDK < API11
realPath = if (Build.VERSION.SDK_INT < 11) {
Expand Down Expand Up @@ -65,7 +76,7 @@ object RealPathUtil {
* @author paulburke
*/
@SuppressLint("NewApi")
fun getRealPathFromURI_API19(context: Context, uri: Uri): String? {
fun getRealPathFromURI_API19(context: ReactApplicationContext, uri: Uri): String? {
val isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT

// DocumentProvider
Expand All @@ -84,7 +95,7 @@ object RealPathUtil {
val id = DocumentsContract.getDocumentId(uri)
val contentUri = ContentUris.withAppendedId(
Uri.parse("content://downloads/public_downloads"), java.lang.Long.valueOf(id))
return getDataColumn(context, contentUri, null, null)
return getDataColumn(context, contentUri, null, null,uri)
} else if (isMediaDocument(uri)) {
val docId = DocumentsContract.getDocumentId(uri)
val split = docId.split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
Expand All @@ -96,17 +107,19 @@ object RealPathUtil {
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI
} else if ("audio" == type) {
contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
}else {
contentUri = MediaStore.Files.getContentUri("external");
}
val selection = "_id=?"
val selectionArgs = arrayOf(
split[1]
)
return getDataColumn(context, contentUri, selection, selectionArgs)
return getDataColumn(context, contentUri, selection, selectionArgs,uri)
}
} else if ("content".equals(uri.scheme, ignoreCase = true)) {

// Return the remote address
return if (isGooglePhotosUri(uri)) uri.lastPathSegment else getDataColumn(context, uri, null, null)
return if (isGooglePhotosUri(uri)) uri.lastPathSegment else getDataColumn(context, uri, null, null,uri)
} else if ("file".equals(uri.scheme, ignoreCase = true)) {
return uri.path
}
Expand All @@ -123,8 +136,8 @@ object RealPathUtil {
* @param selectionArgs (Optional) Selection arguments used in the query.
* @return The value of the _data column, which is typically a file path.
*/
fun getDataColumn(context: Context, uri: Uri?, selection: String?,
selectionArgs: Array<String>?): String? {
fun getDataColumn(context: ReactApplicationContext, uri: Uri?, selection: String?,
selectionArgs: Array<String>?,actualUri: Uri?): String? {
var cursor: Cursor? = null
val column = "_data"
val projection = arrayOf(
Expand All @@ -137,13 +150,66 @@ object RealPathUtil {
val index = cursor.getColumnIndexOrThrow(column)
return cursor.getString(index)
}

val copyPath=copyFileToLocalStorage(actualUri!!, context)
MediaCache.addCompletedImagePath(copyPath)
return copyPath
} catch (e: Exception) {
val copyPath=copyFileToLocalStorage(actualUri!!, context)
MediaCache.addCompletedImagePath(copyPath)
return copyPath
} finally {
cursor?.close()
}
return null
}

/**
fun getFileExtension(fileName: String): String {
return if (fileName.contains(".")) {
fileName.substringAfterLast(".")
} else {
""
}
}

private fun copyFileToLocalStorage( uri: Uri,context: ReactApplicationContext): String {
val returnUri = uri
val returnCursor = context.contentResolver.query(returnUri, null, null, null, null)
returnCursor?.moveToFirst();
val nameIndex = returnCursor!!.getColumnIndex(OpenableColumns.DISPLAY_NAME)
val name = returnCursor!!.getString(nameIndex)
val fileExtension=getFileExtension(name)
var dir = context.cacheDir

// we don't want to rename the file so we put it into a unique location
dir = File(dir, UUID.randomUUID().toString())
try {
val destPath=Utils.generateCacheFilePath(fileExtension, context)
val destFile = File(destPath)
val copyPath: Uri = copyFile(context, uri, destFile)
return copyPath.toString()
} catch (e: java.lang.Exception) {
e.printStackTrace()
}
return uri.toString()
}

@Throws(IOException::class)
fun copyFile(context: Context, uri: Uri?, destFile: File?): Uri {
context.contentResolver.openInputStream(uri!!).use { inputStream ->
FileOutputStream(destFile).use { outputStream ->
val buf = ByteArray(8192)
var len: Int
while (inputStream!!.read(buf).also { len = it } > 0) {
outputStream.write(buf, 0, len)
}
return Uri.fromFile(destFile)
}
}
}


/**
* @param uri The Uri to check.
* @return Whether the Uri authority is ExternalStorageProvider.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import android.provider.OpenableColumns
import android.util.Log
import com.facebook.react.bridge.Promise
import com.facebook.react.bridge.ReactApplicationContext
import com.reactnativecompressor.Audio.AudioCompressor
import com.reactnativecompressor.Video.VideoCompressor.CompressionListener
import com.reactnativecompressor.Video.VideoCompressor.VideoCompressorClass
import java.io.FileNotFoundException
Expand Down Expand Up @@ -133,6 +134,10 @@ object Utils {
}
}

fun addLog(log: String) {
Log.d(AudioCompressor.TAG, log)
}

fun getLength(uri: Uri, contentResolver: ContentResolver): Long {
var assetFileDescriptor: AssetFileDescriptor? = null
try {
Expand Down
10 changes: 6 additions & 4 deletions example/src/Screens/Audio/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,13 @@ const Index = () => {
const res: any = await DocumentPicker.pick({
type: [DocumentPicker.types.audio],
});
setOrignalSize(prettyBytes(res.size));
setFileName(res.name);
setMimeType(res.type);
Audio.compress(res.uri, { quality: 'high' })
setOrignalSize(prettyBytes(res[0]?.size));
setFileName(res[0].name);
setMimeType(res[0].type);
console.log('source file: ', res[0].uri);
Audio.compress(res[0].uri, { quality: 'medium' })
.then(async (outputFilePath: string) => {
console.log(outputFilePath, 'outputFilePath compressed audio');
const detail: any = await getFileInfo(outputFilePath);
setCompressedSize(prettyBytes(parseInt(detail.size)));

Check warning on line 28 in example/src/Screens/Audio/index.tsx

View workflow job for this annotation

GitHub Actions / Lint JS (eslint, prettier)

Missing radix parameter.
})
Expand Down
61 changes: 5 additions & 56 deletions src/Audio/index.tsx
Original file line number Diff line number Diff line change
@@ -1,66 +1,15 @@
import { Compressor } from '../Main';

import {
AUDIO_BITRATE,
DEFAULT_COMPRESS_AUDIO_OPTIONS,
checkUrlAndOptions,
} from '../utils';
import type { AudioType, defaultResultType } from '../utils';
import { DEFAULT_COMPRESS_AUDIO_OPTIONS } from '../utils';
import type { AudioType } from '../utils';
const NativeAudio = Compressor;

const Audio: AudioType = {
compress: async (url, options = DEFAULT_COMPRESS_AUDIO_OPTIONS) => {
try {
const checkUrlAndOptionsResult: defaultResultType =
await checkUrlAndOptions(url, options);
if (!checkUrlAndOptionsResult.isCorrect) {
throw checkUrlAndOptionsResult.message;
} else {
// Get resulting output file path

// Get media details
// const mediaDetails: any = await getDetails(url).catch(() => null);
const mediaDetails: any = {
bitrate: 0,
};

// Initialize bitrate
let bitrate: any = DEFAULT_COMPRESS_AUDIO_OPTIONS.bitrate;

if (mediaDetails && mediaDetails.bitrate) {
// Check and return the appropriate bitrate according to quality expected
for (let i = 0; i < AUDIO_BITRATE.length; i++) {
// Check a particular bitrate to return its nearest lower according to quality
//@ts-ignore
if (mediaDetails.bitrate > AUDIO_BITRATE[i]) {
if (i + 2 < AUDIO_BITRATE.length) {
if (options.quality === 'low') bitrate = AUDIO_BITRATE[i + 2];
else if (options.quality === 'medium')
bitrate = AUDIO_BITRATE[i + 1];
else bitrate = AUDIO_BITRATE[i];
} else if (i + 1 < AUDIO_BITRATE.length) {
if (options.quality === 'low') bitrate = AUDIO_BITRATE[i + 1];
else bitrate = AUDIO_BITRATE[i];
} else bitrate = AUDIO_BITRATE[i];
break;
}

// Check if the matching bitrate is the last in the array
if (
//@ts-ignore
mediaDetails.bitrate <= AUDIO_BITRATE[AUDIO_BITRATE.length - 1]
) {
bitrate = AUDIO_BITRATE[AUDIO_BITRATE.length - 1];
break;
}
}
}

return NativeAudio.compress_audio(url, {
bitrate,
quality: options.quality,
});
}
return NativeAudio.compress_audio(url, {
quality: options.quality,
});
} catch (error: any) {
throw error.message;
}
Expand Down

0 comments on commit 5a9c4d6

Please sign in to comment.