LightCompressor icon indicating copy to clipboard operation
LightCompressor copied to clipboard

Issue when I try to compress a video in Android API 33

Open EbelloImbox opened this issue 3 years ago • 6 comments

Hello, I am having an issue when I try to compress a video in Android API 33, this is the logcat:

Process: com.spotbros.defense, PID: 6652
    java.io.FileNotFoundException: : open failed: ENOENT (No such file or directory)
        at libcore.io.IoBridge.open(IoBridge.java:574)
        at java.io.FileInputStream.<init>(FileInputStream.java:160)
        at com.abedelazizshe.lightcompressorlibrary.VideoCompressor.saveVideoInExternal(VideoCompressor.kt:255)
        at com.abedelazizshe.lightcompressorlibrary.VideoCompressor.saveVideoFile(VideoCompressor.kt:182)
        at com.abedelazizshe.lightcompressorlibrary.VideoCompressor.access$saveVideoFile(VideoCompressor.kt:26)
        at com.abedelazizshe.lightcompressorlibrary.VideoCompressor$doVideoCompression$1.invokeSuspend(VideoCompressor.kt:104)
        at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
        at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
        at android.os.Handler.handleCallback(Handler.java:942)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at android.os.Looper.loopOnce(Looper.java:201)
        at android.os.Looper.loop(Looper.java:288)
        at android.app.ActivityThread.main(ActivityThread.java:7898)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:936)
     Caused by: android.system.ErrnoException: open failed: ENOENT (No such file or directory)
        at libcore.io.Linux.open(Native Method)
        at libcore.io.ForwardingOs.open(ForwardingOs.java:563)
        at libcore.io.BlockGuardOs.open(BlockGuardOs.java:274)
        at libcore.io.ForwardingOs.open(ForwardingOs.java:563)
        at android.app.ActivityThread$AndroidOs.open(ActivityThread.java:7784)
        at libcore.io.IoBridge.open(IoBridge.java:560)
        at java.io.FileInputStream.<init>(FileInputStream.java:160) 
        at com.abedelazizshe.lightcompressorlibrary.VideoCompressor.saveVideoInExternal(VideoCompressor.kt:255) 
        at com.abedelazizshe.lightcompressorlibrary.VideoCompressor.saveVideoFile(VideoCompressor.kt:182) 
        at com.abedelazizshe.lightcompressorlibrary.VideoCompressor.access$saveVideoFile(VideoCompressor.kt:26) 
        at com.abedelazizshe.lightcompressorlibrary.VideoCompressor$doVideoCompression$1.invokeSuspend(VideoCompressor.kt:104) 
        at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) 
        at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106) 
        at android.os.Handler.handleCallback(Handler.java:942) 
        at android.os.Handler.dispatchMessage(Handler.java:99) 
        at android.os.Looper.loopOnce(Looper.java:201) 
        at android.os.Looper.loop(Looper.java:288) 
        at android.app.ActivityThread.main(ActivityThread.java:7898) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:936) 

I am using a video saved in standar video directory from Android Emulator, uri: /storage/emulated/0/Movies/VID_20220830_180410.mp4, and in this part of the library code:

private fun getMediaPath(context: Context, uri: Uri): String {

        val resolver = context.contentResolver
        val projection = arrayOf(MediaStore.Video.Media.DATA)
        var cursor: Cursor? = null
        try {
            cursor = resolver.query(uri, projection, null, null, null)
            return if (cursor != null) {
                val columnIndex = cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DATA)
                cursor.moveToFirst()
                cursor.getString(columnIndex)

            } else ""

        } catch (e: Exception) {
            resolver.let {
                val filePath = (context.applicationInfo.dataDir + File.separator
                        + System.currentTimeMillis())
                val file = File(filePath)

                resolver.openInputStream(uri)?.use { inputStream ->
                    FileOutputStream(file).use { outputStream ->
                        val buf = ByteArray(4096)
                        var len: Int
                        while (inputStream.read(buf).also { len = it } > 0) outputStream.write(
                            buf,
                            0,
                            len
                        )
                    }
                }
                return file.absolutePath
            }
        } finally {
            cursor?.close()
        }
    }

When this part of the code is executing:

cursor = resolver.query(uri, projection, null, null, null)
            return if (cursor != null) {
                val columnIndex = cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DATA)
                cursor.moveToFirst()
                cursor.getString(columnIndex)

            } else ""

cursor is always null, so it will return always "" and for this reason I am experimenting this error.

Any help?

EbelloImbox avatar Aug 31 '22 10:08 EbelloImbox

I am watching that the problem is only happening in APi > 30 for this code:

if (Build.VERSION.SDK_INT >= 30) {
                if (!storageConfiguration.isExternal) {
                    return saveVideoInInternal(context, videoFileName, videoFile)
                } else {
                    return saveVideoInExternal(context, videoFileName, folderName, videoFile)
                }
            }

videoFile is always empty because of the cursor in the code above is null.

EbelloImbox avatar Aug 31 '22 10:08 EbelloImbox

I dealt with the same issue today using API 31!

james04gr avatar Aug 31 '22 14:08 james04gr

the problem is that you are using an uri like mine and you have to use an uri like a fileprovider: content://com.google.android.apps.photos.contentprovider/-1/2/content%3A%2F%2Fmedia%2Fexternal%2Fvideo%2Fmedia%2F1000000095/ORIGINAL/NONE/video%2Fmp4/1108575362

I am trying to parse to this kind of uri, if you know how to do it tell me it please.

EbelloImbox avatar Aug 31 '22 14:08 EbelloImbox

the problem is that you are using an uri like mine and you have to use an uri like a fileprovider: content://com.google.android.apps.photos.contentprovider/-1/2/content%3A%2F%2Fmedia%2Fexternal%2Fvideo%2Fmedia%2F1000000095/ORIGINAL/NONE/video%2Fmp4/1108575362

I am trying to parse to this kind of uri, if you know how to do it tell me it please.

You are right. Now that i save my video to Enviroment.DIRECTORY_MOVIES i get a content:// uri. Temporarilly i will use this. But as soon as i figure it out (because this is not where i want to save my videos before compress) i will inform you!

james04gr avatar Aug 31 '22 15:08 james04gr

The way to get this kind of URI is using this: FileProvider.getUriForFile(context, authority, file), but I am seeing to come back to version 1.0.1 because there are a lot of bugs in this new version in kotlin.

EbelloImbox avatar Sep 01 '22 07:09 EbelloImbox

I'm also getting the same issue in API 33

philblais avatar Sep 09 '22 23:09 philblais

hey @EbelloImbox i've faced the same issue with this package and found the cause, with newer version of android their is scoped storage to access file outside of it's own directory. before API 30 we needed to add android:requestLegacyExternalStorage="true" flag to access file in external storage but After Android 11, Android just ignores android:requestLegacyExternalStorage="true" and android:requestLegacyExternalStorage="true"" flags and i assume this package is not yet compatible with scoped storage for flutter a workaround can be compressing the video in the internal directory then storing it in the external dir with other packages which does support scoped storage

Amberon-voldi avatar Oct 15 '22 21:10 Amberon-voldi

I am watching that the problem is only happening in APi > 30 for this code:

if (Build.VERSION.SDK_INT >= 30) {
                if (!storageConfiguration.isExternal) {
                    return saveVideoInInternal(context, videoFileName, videoFile)
                } else {
                    return saveVideoInExternal(context, videoFileName, folderName, videoFile)
                }
            }

videoFile is always empty because of the cursor in the code above is null.

Any solution?

escorcia21 avatar Nov 23 '22 20:11 escorcia21

I am watching that the problem is only happening in APi > 30 for this code:

if (Build.VERSION.SDK_INT >= 30) {
                if (!storageConfiguration.isExternal) {
                    return saveVideoInInternal(context, videoFileName, videoFile)
                } else {
                    return saveVideoInExternal(context, videoFileName, folderName, videoFile)
                }
            }

videoFile is always empty because of the cursor in the code above is null.

Any solution?

a workaround can be compressing the file into the app's internal directory then use something like gallery_saver package to save the file in the external storage

Amberon-voldi avatar Nov 23 '22 20:11 Amberon-voldi

gallery_saver package is available in kotlin/java?

escorcia21 avatar Nov 24 '22 00:11 escorcia21

Hi @EbelloImbox thank you for openning the issue and apolgies for the late reply. Was very busy the past 10 months.

This issue is happening due to permission issues as Google has been changing the behaviours a lot when it comes to privacy and media access.

I will release a new version later today with updated README. A new permission should be requested now for API >= 33;

AbedElazizShe avatar Dec 11 '22 15:12 AbedElazizShe