Как объединить (объединить) видео и аудио, чтобы звук зацикливался в выходном видео, если оно слишком короткое? - PullRequest
0 голосов
/ 19 февраля 2019

Фон

Мне необходимо объединить видеофайл и аудиофайл в один видеофайл, чтобы:

  1. Выходной видеофайл имел одинаковую длительность.в качестве входного видеофайла
  2. Звук в выходном файле будет только из входного аудиофайла.Если оно слишком короткое, оно зациклится до конца (при необходимости может остановиться в конце).Это означает, что как только аудио закончится, а видео нет, я должен проигрывать его снова и снова, пока видео не закончится (конкатенация аудио).

Технический термин этого слиянияОперация называется «мультиплексирование», как я прочитал.

В качестве примера предположим, что у нас есть входное видео 10 секунд, а звуковой файл 4 секунды, выходное видео будет 10 секунд (всегда совпадает с входным видео), и звук будет воспроизводиться 2,5 раза (первые 2 охватывают первые 8 секунд, а затем 2 секунды из 4 для остальных).

Проблемы

Хотя я нашел решение о том, как объединить видео и аудио ( здесь ), я столкнулся с несколькими проблемами:

  1. Я не могу понять, как зацикливать запись аудиоконтента при необходимости.Он продолжает выдавать ошибку, независимо от того, что я пытаюсь

  2. Входные файлы должны быть определенных форматов файлов.В противном случае это может вызвать исключение или (в очень редких случаях) хуже: создать видеофайл с черным содержимым.Более того: иногда файл '.mkv' (например) может подойти, а иногда он не будет принят (и оба могут быть воспроизведены в приложении видеоплеера).

  3. Текущий код обрабатывает буферы, а не реальную продолжительность.Это означает, что во многих случаях я мог бы прекратить мультиплексирование звука, даже если бы не делал этого, и выходной видеофайл будет иметь более короткий аудиоконтент по сравнению с оригиналом, даже если видео достаточно длинное.

Что я пробовал

  • Я пытался заставить MediaExtractor аудио начинаться каждый раз, когда он достигал конца, используя:

            if (audioBufferInfo.size < 0) {
                Log.d("AppLog", "reached end of audio, looping...")
                audioExtractor.seekTo(0, MediaExtractor.SEEK_TO_CLOSEST_SYNC)
                audioBufferInfo.size = audioExtractor.readSampleData(audioBuf, 0)
            }
    
  • Для проверки типов файлов я попытался использовать MediaMetadataRetriever, а затем проверить MIME-тип.Я думаю, что поддерживаемые доступны в документах ( здесь ), как те, которые помечены "Encoder".Не уверен насчет этого.Я также не знаю, какой тип пантомимы относится к тому типу, который там упоминается.

  • Я также пытался переинициализировать все, что связано со звуком, но он тоже не работал.

Вот мой текущий коддля самого мультиплексирования (полный пример проекта доступен здесь ):

object VideoAndAudioMuxer {
    //   based on:  https://stackoverflow.com/a/31591485/878126
    @WorkerThread
    fun joinVideoAndAudio(videoFile: File, audioFile: File, outputFile: File): Boolean {
        try {
            //            val videoMediaMetadataRetriever = MediaMetadataRetriever()
            //            videoMediaMetadataRetriever.setDataSource(videoFile.absolutePath)
            //            val videoDurationInMs =
            //                videoMediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION).toLong()
            //            val videoMimeType =
            //                videoMediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_MIMETYPE)
            //            val audioMediaMetadataRetriever = MediaMetadataRetriever()
            //            audioMediaMetadataRetriever.setDataSource(audioFile.absolutePath)
            //            val audioDurationInMs =
            //                audioMediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION).toLong()
            //            val audioMimeType =
            //                audioMediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_MIMETYPE)
            //            Log.d(
            //                "AppLog",
            //                "videoDuration:$videoDurationInMs audioDuration:$audioDurationInMs videoMimeType:$videoMimeType audioMimeType:$audioMimeType"
            //            )
            //            videoMediaMetadataRetriever.release()
            //            audioMediaMetadataRetriever.release()
            outputFile.delete()
            outputFile.createNewFile()
            val muxer = MediaMuxer(outputFile.absolutePath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4)
            val sampleSize = 256 * 1024
            //video
            val videoExtractor = MediaExtractor()
            videoExtractor.setDataSource(videoFile.absolutePath)
            videoExtractor.selectTrack(0)
            videoExtractor.seekTo(0, MediaExtractor.SEEK_TO_CLOSEST_SYNC)
            val videoFormat = videoExtractor.getTrackFormat(0)
            val videoTrack = muxer.addTrack(videoFormat)
            val videoBuf = ByteBuffer.allocate(sampleSize)
            val videoBufferInfo = MediaCodec.BufferInfo()
//            Log.d("AppLog", "Video Format $videoFormat")
            //audio
            val audioExtractor = MediaExtractor()
            audioExtractor.setDataSource(audioFile.absolutePath)
            audioExtractor.selectTrack(0)
            audioExtractor.seekTo(0, MediaExtractor.SEEK_TO_CLOSEST_SYNC)
            val audioFormat = audioExtractor.getTrackFormat(0)
            val audioTrack = muxer.addTrack(audioFormat)
            val audioBuf = ByteBuffer.allocate(sampleSize)
            val audioBufferInfo = MediaCodec.BufferInfo()
//            Log.d("AppLog", "Audio Format $audioFormat")
            //
            muxer.start()
//            Log.d("AppLog", "muxing video&audio...")
            //            val minimalDurationInMs = Math.min(videoDurationInMs, audioDurationInMs)
            while (true) {
                videoBufferInfo.size = videoExtractor.readSampleData(videoBuf, 0)
                audioBufferInfo.size = audioExtractor.readSampleData(audioBuf, 0)
                if (audioBufferInfo.size < 0) {
                    //                    Log.d("AppLog", "reached end of audio, looping...")
                    //TODO somehow start from beginning of the audio again, for looping till the video ends
                    //                    audioExtractor.seekTo(0, MediaExtractor.SEEK_TO_CLOSEST_SYNC)
                    //                    audioBufferInfo.size = audioExtractor.readSampleData(audioBuf, 0)
                }
                if (videoBufferInfo.size < 0 || audioBufferInfo.size < 0) {
//                    Log.d("AppLog", "reached end of video")
                    videoBufferInfo.size = 0
                    audioBufferInfo.size = 0
                    break
                } else {
                    //                    val donePercentage = videoExtractor.sampleTime / minimalDurationInMs / 10L
                    //                    Log.d("AppLog", "$donePercentage")
                    // video muxing
                    videoBufferInfo.presentationTimeUs = videoExtractor.sampleTime
                    videoBufferInfo.flags = videoExtractor.sampleFlags
                    muxer.writeSampleData(videoTrack, videoBuf, videoBufferInfo)
                    videoExtractor.advance()
                    // audio muxing
                    audioBufferInfo.presentationTimeUs = audioExtractor.sampleTime
                    audioBufferInfo.flags = audioExtractor.sampleFlags
                    muxer.writeSampleData(audioTrack, audioBuf, audioBufferInfo)
                    audioExtractor.advance()
                }
            }
            muxer.stop()
            muxer.release()
//            Log.d("AppLog", "success")
            return true
        } catch (e: Exception) {
            e.printStackTrace()
//            Log.d("AppLog", "Error " + e.message)
        }
        return false
    }
}
  • Я также пытался использовать FFMPEG libary ( здесь и здесь ), чтобы узнать, как это сделать.Он работал нормально, но у него есть некоторые возможные проблемы: библиотека, кажется, занимает много места, раздражает условия лицензирования, и по какой-то причине образец не может воспроизвести выходной файл, который я должен создать, если я не удаляю что-то вкоманда, которая сделает преобразование намного медленнее.Я действительно предпочел бы использовать встроенный API, чем использовать эту библиотеку, хотя это очень мощная библиотека ... Кроме того, кажется, что для некоторых входных файлов она не зацикливается ...

Вопросы

  1. Как я могу мультиплексировать видео и аудио файлы, чтобы звук зацикливался, если звук короче (по длительности) по сравнению с видео?

  2. Как я могу сделать так, чтобы звук был обрезан точно по окончании видео (без остатков на видео и аудио)?

  3. КакМогу ли я проверить перед вызовом этой функции, может ли текущее устройство обрабатывать заданные входные файлы и фактически их мультиплексировать?Есть ли способ проверки во время выполнения, которые поддерживаются для такого рода операций, вместо того, чтобы полагаться на список документов, которые могут измениться в будущем?

...