1

在我的 android 应用程序中,我分别下载 Facebook 视频和音频并将其合并到一个输出文件中。原因是 Facebook 视频 URL 没有声音,而音频 URL 是独立的。我曾尝试使用此mp4Parser来合并音频和视频,它在大多数设备中运行良好,但在从设备文件播放时在 Redmi/MI 设备中出现问题。

我遵循的步骤

  • 下载 mp4 格式的无声视频。
  • 下载 m4a 格式的音频。
  • 合并这两个文件并创建一个 mp4 格式的输出文件。
  • 合并完成后删除无声视频和临时音频。

下面是我的代码

依赖项

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

合并代码

  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)

            }
        }
    }

这在大多数设备中都可以正常工作,但在 MI 设备(即Redmi S2,MUI 版本 MUI Global 12.0.2)中,我在播放已损坏且无法播放的输出文件时遇到问题。出现的错误只是标题未知。

请注意,我也尝试了以下依赖项组合,但这会在输出文件中产生滞后,因此删除了这两个。

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

有人可以建议正确的 usgae 和依赖版本的mp4parser用于合并音频文件和视频文件。

任何帮助将不胜感激。

谢谢

4

1 回答 1

0

使用FFmpeg库,它将完成所有工作。您只需提供 Facebook 音频和视频链接以及您将通过 File API 获得的目录的存储路径。它将下载音频和视频,然后将它们合并。

这个简单的命令将完成所有的魔法。

val cmd = "-i $videoUrl -i  $audioUrl -c:v copy -c:a aac $storagePath"
FFmpeg.executeAsync(cmd, object: ExecuteCallback())
于 2022-02-19T19:22:18.683 回答