5

I am working on an android app applying video effects like slow and fast motions. My app is working fine below android 10, but not on android 10 and FFMPEG is not showing any error just blank error message on the onFailure callback method.

I did some research and find out that android has introduced scoped permissions and you can temporarily bypass this by adding this line on manifest android:requestLegacyExternalStorage="true" and you get the storage permission. After adding this line rest portation of app is just working fine fetching user videos and etc but FFMPEG is not working.

If anyone has an idea or clue about this issue please help me out.

Zain
  • 81
  • 1
  • 9
  • Maybe [FFmpeg doesn't work on android 10, goes strait to onFailure(String message) with empty message](https://stackoverflow.com/questions/58565300/ffmpeg-doesnt-work-on-android-10-goes-strait-to-onfailurestring-message-with) will be helpful. – llogan Jan 13 '20 at 19:22
  • actually it is saying that FFmpeg is doing execution on the home directory and android 10 removed execute permission for app home directory. It looks like a dead-end till now. – Zain Jan 14 '20 at 13:56
  • https://stackoverflow.com/a/59686838/8416317 – Vinesh Chauhan Jan 18 '20 at 06:29
  • thanks for the link, these library callbacks are working in some different way but I will try that out – Zain Jan 19 '20 at 20:17
  • I have the same problem. – Bob Lissner Feb 05 '20 at 20:22
  • or see https://github.com/alexcohn/FFmpeg-Android/tree/pull_request_Q and https://github.com/bravobit/FFmpeg-Android/issues/126 – Alex Cohn Sep 10 '20 at 08:15

3 Answers3

4

here is a working version for compatible devices min API 16 onwards. Support android API 29 (Q)

dependencies {
    implementation 'com.arthenica:mobile-ffmpeg-full:4.4.LTS'
}

Long Term Support packages for MobileFFmpeg v4.4

Supports the following device specifications

Android

Android 4.1 (API level 16) or later

x86 and x86-64 architecturesarm-v7a, arm-v7a-neon, arm64-v8a,

You can find more information on the official repo on github

Update: 20 June 2021

Not maintained anymore as explained in What’s next for MobileFFmpeg?

Now is Superseded by FFmpegKit

Here you can find FFmpegKit For Android

Features

  • Supports API Level 24+ on Main releases and API Level 16+ on LTS releases
  • Includes arm-v7a, arm-v7a-neon, arm64-v8a, x86 and x86_64 architectures
  • Can handle Storage Access Framework (SAF) Uris
  • Camera access on supported devices
  • Builds shared native libraries (.so)
  • Creates Android archive with .aar extension
Quimbo
  • 634
  • 6
  • 17
0

Try this

dependencies {

implementation 'com.arthenica:mobile-ffmpeg-full:4.4.LTS'

}

Also see this.. ffmpeg-video-editor-android

Umesh Lakhani
  • 2,382
  • 1
  • 14
  • 10
0

Use this library which supports targetSdkVersion 29.

implementation 'com.arthenica:mobile-ffmpeg-video:4.4'

Command used for video compression:

val complexCommand = arrayOf( "-y", "-i", inputPath!!, "-s", "640x480", "-r", "25", "-vcodec", "mpeg4", "-b:v", "1000k", "-b:a", "48000", "-ac", "2", "-ar", "22050", outputFilePath )

Compression method:

  private fun execFFmpegBinary(command: Array<String>, inputPath: String?, listener: CompressionListener?, outputFilePath: String) {
    Config.enableLogCallback { message -> Log.e(Config.TAG, message.text) }
    Config.enableStatisticsCallback { newStatistics ->
        Log.e(
            Config.TAG,
            String.format(
                "frame: %d, time: %d",
                newStatistics.videoFrameNumber,
                newStatistics.time
            )
        )
        Log.d(
            TAG,
            "Started command : ffmpeg " + Arrays.toString(command)
        )
        
        val videoLength = inputPath?.let { VideoUtils.getVideoDuration(it) }
        Log.d(TAG, "execFFmpegBinary: Video Length : $videoLength")
        val progress: Float =
            java.lang.String.valueOf(newStatistics.time).toFloat() / videoLength!!
        val progressFinal = progress * 100
        Log.d(TAG, "Video Length: $progressFinal")
        Log.d(
            Config.TAG,
            java.lang.String.format(
                "frame: %d, time: %d",
                newStatistics.videoFrameNumber,
                newStatistics.time
            )
        )
        Log.d(
            Config.TAG,
            java.lang.String.format(
                "Quality: %f, time: %f",
                newStatistics.videoQuality,
                newStatistics.videoFps
            )
        )
        //progressDialog.setProgress(progressFinal.toInt())
        //val adjustProgress = progressFinal/1.5f
        Log.d(TAG, "execFFmpegBinary: Progress: ${progressFinal.toInt()}")
        listener?.onProgress(progressFinal.toInt())
        Log.d(TAG, "progress : $newStatistics")
    }
    Log.d(
        TAG,
        "Started command : ffmpeg " + Arrays.toString(command)
    )
   /* progressDialog.setMessage("Processing...")
    progressDialog.show()*/
    val executionId = com.arthenica.mobileffmpeg.FFmpeg.executeAsync(
        command
    ) { executionId1: Long, returnCode: Int ->
        if (returnCode == RETURN_CODE_SUCCESS) {
            Log.d(
                TAG,
                "Finished command : ffmpeg " + Arrays.toString(command)
            )

            listener?.compressionFinished(SUCCESS, true, fileOutputPath = outputFilePath)
        } else if (returnCode == Config.RETURN_CODE_CANCEL) {
            Log.e(
                TAG,
                "Async command execution cancelled by user."
            )
            listener?.onFailure(String.format(
                "Async command execution cancelled by user."
            ))
            //if (progressDialog != null) progressDialog.dismiss()
        } else {
            Log.e(
                TAG,
                String.format(
                    "Async command execution failed with returnCode=%d.",
                    returnCode
                )
            )
            listener?.onFailure(String.format(
                "Async command execution failed with returnCode=%d.",
                returnCode
            ))
           // if (progressDialog != null) progressDialog.dismiss()
        }
    }
    Log.e(TAG, "execFFmpegMergeVideo executionId-$executionId")
}

Compression Listener:

 interface CompressionListener {
    fun compressionFinished(
        status: Int,
        isVideo: Boolean,
        fileOutputPath: String?
    )

    fun onFailure(message: String?)
    fun onProgress(progress: Int)
}

Call using:

execFFmpegBinary(complexCommand, inputPath, listener, outputFilePath)
VIVEK CHOUDHARY
  • 468
  • 5
  • 8