Mp4Parser audio video merged output not playing in all devices

751 views Asked by At

In my android application i am downloading Facebook video and audio separately and merging it in an output file. The reason is Facebook videos URLs have no sound and audio URL is separate. I have tried using this mp4Parser for merging audio and video and it works pretty well in most of devices but having issues in Redmi/MI devices when playing from device files.

Steps i followed

  • Download NO SOUND Video in mp4 format.
  • Download audio in m4a format.
  • Merge both files and create an output file with mp4 format.
  • After merging is done deleted no sound video and temporary audio.

Below is my code

Dependencies

 implementation 'com.iceteck.silicompressorr:silicompressor:2.2.4'
    implementation('com.googlecode.mp4parser:isoparser:1.0.6') {
        exclude group: 'org.aspectj', module: 'aspectjrt'
    }

Merging code

  private fun mergeAudioAndVideo(
        id: Int,
        model: DatabaseDownloadItem?,
        file: File?,
        activityInstance: LocaleAwareCompatActivity?
    ) {
        val mergingDetail = sharedPreferencesManager.getItemInMergingList(id)
        if (mergingDetail != null) {
            val timeInMillis = System.currentTimeMillis()
            var list: ArrayList<DatabaseDownloadItem>? = sharedViewmodel.pendingMerge.value
            if (list == null) {
                list = arrayListOf()
            }
            list.add(model!!)
            sharedViewmodel.pendingMerge.postValue(list)
            sharedViewmodel.pendingMerge.value?.add(model!!)
            CoroutineScope(Dispatchers.Main).launch {
                var mergePendingItems: ArrayList<Int>? =
                    tinyDB?.getMergeList(AppConstants.MERGE_PENDING_DOWNLOADS)
                if (mergePendingItems == null) {
                    mergePendingItems = arrayListOf()
                }
                if (!mergePendingItems.contains(model.id)) {
                    mergePendingItems.add(model.id)
                }
                tinyDB?.putMergeList(AppConstants.MERGE_PENDING_DOWNLOADS, mergePendingItems)
                adapter.notifyDataSetChanged()
                CoroutineScope(Dispatchers.IO + exceptionHandler).async {
                    val audiopath = mergingDetail.audioPath
                    val videopath = mergingDetail.videoPath
                    /*
                    following comment is kept for reference
                    val audiopath = "/storage/emulated/0/VideoDownloader/tempAudio_1643715180488.m4a"
                    val videopath = "/storage/emulated/0/VideoDownloader/1643715180480.mp4"*/
                    val output: String
                    val outputName = "output_${mergingDetail.originalFileName}"
                    mergingDetail.outputFileName = outputName
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
                        val file = File(
                            getDownloaderFolderFromDownloads(),
                            "$outputName.mp4"
                        )
                        output = file.absolutePath
                        mergingDetail.outputFile = file

                    } else {
                        val file = File(
                            Environment.getExternalStorageDirectory()
                                .toString() + "/VideoDownloader/",
                            "$outputName.mp4"
                        )
                        output = file.absolutePath
                        mergingDetail.outputFile = file

                    }
                    var videoOutput: Movie? = null
                    var audioOutput: Movie? = null
                    kotlin.runCatching {
                        videoOutput = MovieCreator.build(videopath)
                        audioOutput = MovieCreator.build(audiopath)
                    }
                    val finalTrack: MutableList<Track> = ArrayList()
                    for (track in videoOutput?.tracks!!) {
                        if (track.handler.equals("vide")) finalTrack.add(track)
                    }
                    for (track in audioOutput?.tracks!!) {
                        if (track.handler.equals("soun")) finalTrack.add(track)
                    }
                    videoOutput?.tracks = finalTrack
                    val mp4file: Container = DefaultMp4Builder().build(videoOutput)
                    kotlin.runCatching {
                        val fc: FileChannel = FileOutputStream(File(output)).getChannel()
                        mp4file.writeContainer(fc)
                        fc.close()
                    }.onFailure { error ->
                        val abc = error
                    }.onSuccess { someFuncReturnValue ->
                        val abc = "dss"
                    }

                }.await()
                Toast.makeText(activityInstance, "Audio Video Merging Done", Toast.LENGTH_SHORT)
                    .show()
                mergingDetail.isMergingDone = true
                sharedPreferencesManager.updateItemInMergingList(id, mergingDetail)
                executeDownloadCompletion(model, file, activityInstance)
                deleteTempAudioFile(File(mergingDetail.audioPath!!), activityInstance)
                deleteOldMutedVideo(File(mergingDetail.videoPath!!), activityInstance)
                renameOutputFileWithDBName(mergingDetail, activityInstance)
                var list: ArrayList<DatabaseDownloadItem>? = sharedViewmodel.pendingMerge.value
                if (list != null && list!!.size > 0) {
                    list!!.remove(model!!)
                    sharedViewmodel.pendingMerge.postValue(list)
                    sharedViewmodel.pendingMerge.value?.add(model!!)
                }
                notificationWork(model, file, activityInstance)
                sharedPreferencesManager.deleteItemInMergingList(id)

            }
        }
    }

This works fine in most of devices but in MI Devices (i.e Redmi S2, MUI Version MUI Global 12.0.2) i am having an issue while playing the output file its corrupted and cannot be played. The error which shows up is just with title unknown.

Please note that i have also tried the following combination of dependencies, but this was generating lags in output file so removed these two.

implementation 'org.mp4parser:isoparser:1.9.41'
     implementation 'org.mp4parser:muxer:1.9.41

Can somebody please suggest the proper usgae and dependency version of mp4parser for merging audio file and video file.

Any help will be appreciated.

Thank you

1

There are 1 answers

4
Nabeel Ahmed On

Use FFmpeg library and it will do all the job. You just have to provide a Facebook Audio and Video link and the storage path of your directory which you will get through File API. It will download the audio and video and then merge them.

This simple command will do all the magic.

val cmd = "-i $videoUrl -i  $audioUrl -c:v copy -c:a aac $storagePath"
FFmpeg.executeAsync(cmd, object: ExecuteCallback())