Как я могу обрезать видео из Uri, включая файлы, которые может обрабатывать библиотека `mp4parser`, но вместо этого используя платформу Android? - PullRequest
0 голосов
/ 07 февраля 2019

Фон

За последние несколько дней я работал над созданием настраиваемой, более обновленной версии библиотеки для обрезки видео, здесь (на основена этой библиотеке )

Проблема

Хотя по большей части мне удалось сделать ее настраиваемой и даже преобразовать все файлы в KotlinУ него была серьезная проблема с самой отделкой.

Предполагается, что ввод всегда является файлом, поэтому, если пользователь выбирает элемент из списка приложений, который возвращает Uri, происходит сбой.Причина этого не только в самом пользовательском интерфейсе, но и в том, что библиотека, которую он использует для обрезки ( mp4parser ), предполагает ввод только File (или filepath), а не Uri(писал об этом здесь ).Я попробовал несколько способов, чтобы получить вместо него Uri, но не получилось.Также писал об этом здесь .

Вот почему я использовал решение, которое я нашел в StackOverflow ( здесь ) для самой обрезки.Хорошая вещь об этом - то, что это тихо и использует только платформу Android непосредственно.Однако, кажется, что для некоторых видеофайлов всегда не удается их обрезать.В качестве примера таких файлов в исходном хранилище библиотеки есть один здесь (сообщается о проблеме здесь ).

Глядя на исключение, вот что я получил:

E: Unsupported mime 'audio/ac3'
E: FATAL EXCEPTION: pool-1-thread-1
    Process: life.knowledge4.videocroppersample, PID: 26274
    java.lang.IllegalStateException: Failed to add the track to the muxer
        at android.media.MediaMuxer.nativeAddTrack(Native Method)
        at android.media.MediaMuxer.addTrack(MediaMuxer.java:626)
        at life.knowledge4.videotrimmer.utils.TrimVideoUtils.genVideoUsingMuxer(TrimVideoUtils.kt:77)
        at life.knowledge4.videotrimmer.utils.TrimVideoUtils.genVideoUsingMp4Parser(TrimVideoUtils.kt:144)
        at life.knowledge4.videotrimmer.utils.TrimVideoUtils.startTrim(TrimVideoUtils.kt:47)
        at life.knowledge4.videotrimmer.BaseVideoTrimmerView$initiateTrimming$1.execute(BaseVideoTrimmerView.kt:220)
        at life.knowledge4.videotrimmer.utils.BackgroundExecutor$Task.run(BackgroundExecutor.java:210)
        at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:458)
        at java.util.concurrent.FutureTask.run(FutureTask.java:266)
        at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:301)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
        at java.lang.Thread.run(Thread.java:764)

Что я нашел

  1. Сообщил о проблеме здесь .Я не думаю, что он получит ответ, поскольку библиотека не обновлялась годами ...
  2. Глядя на исключение, я пытался обрезать без звука.Это работает, но это не очень хорошая вещь, потому что мы хотим нормально обрезать.
  3. Думая, что этот код может быть основан на чужом коде, я попытался найти оригинальный.Я обнаружил, что он основан на каком-то старом коде Google в приложении-галерее, здесь , в классе "VideoUtils.java" в пакете "Gallery3d".К сожалению, я не вижу никакой новой версии для этого.Последнее, что я вижу, это Gingerbread, здесь .

Код, который я из него сделал, выглядит так:

object TrimVideoUtils {
    private const val DEFAULT_BUFFER_SIZE = 1024 * 1024

    @JvmStatic
    @WorkerThread
    fun startTrim(context: Context, src: Uri, dst: File, startMs: Long, endMs: Long, callback: VideoTrimmingListener) {
        dst.parentFile.mkdirs()
        //Log.d(TAG, "Generated file path " + filePath);
        val succeeded = genVideoUsingMuxer(context, src, dst.absolutePath, startMs, endMs, true, true)
        Handler(Looper.getMainLooper()).post { callback.onFinishedTrimming(if (succeeded) Uri.parse(dst.toString()) else null) }
    }

    //https://stackoverflow.com/a/44653626/878126 https://android.googlesource.com/platform/packages/apps/Gallery2/+/634248d/src/com/android/gallery3d/app/VideoUtils.java
    @JvmStatic
    @WorkerThread
    private fun genVideoUsingMuxer(context: Context, uri: Uri, dstPath: String, startMs: Long, endMs: Long, useAudio: Boolean, useVideo: Boolean): Boolean {
        // Set up MediaExtractor to read from the source.
        val extractor = MediaExtractor()
        //       val isRawResId=uri.scheme == "android.resource" && uri.host == context.packageName && !uri.pathSegments.isNullOrEmpty())
        val fileDescriptor = context.contentResolver.openFileDescriptor(uri, "r")!!.fileDescriptor
        extractor.setDataSource(fileDescriptor)
        val trackCount = extractor.trackCount
        // Set up MediaMuxer for the destination.
        val muxer = MediaMuxer(dstPath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4)
        // Set up the tracks and retrieve the max buffer size for selected tracks.
        val indexMap = SparseIntArray(trackCount)
        var bufferSize = -1
        try {
            for (i in 0 until trackCount) {
                val format = extractor.getTrackFormat(i)
                val mime = format.getString(MediaFormat.KEY_MIME)
                var selectCurrentTrack = false
                if (mime.startsWith("audio/") && useAudio) {
                    selectCurrentTrack = true
                } else if (mime.startsWith("video/") && useVideo) {
                    selectCurrentTrack = true
                }
                if (selectCurrentTrack) {
                    extractor.selectTrack(i)
                    val dstIndex = muxer.addTrack(format)
                    indexMap.put(i, dstIndex)
                    if (format.containsKey(MediaFormat.KEY_MAX_INPUT_SIZE)) {
                        val newSize = format.getInteger(MediaFormat.KEY_MAX_INPUT_SIZE)
                        bufferSize = if (newSize > bufferSize) newSize else bufferSize
                    }
                }
            }
            if (bufferSize < 0)
                bufferSize = DEFAULT_BUFFER_SIZE
            // Set up the orientation and starting time for extractor.
            val retrieverSrc = MediaMetadataRetriever()
            retrieverSrc.setDataSource(fileDescriptor)
            val degreesString = retrieverSrc.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION)
            if (degreesString != null) {
                val degrees = Integer.parseInt(degreesString)
                if (degrees >= 0)
                    muxer.setOrientationHint(degrees)
            }
            if (startMs > 0)
                extractor.seekTo(startMs * 1000, MediaExtractor.SEEK_TO_CLOSEST_SYNC)
            // Copy the samples from MediaExtractor to MediaMuxer. We will loop
            // for copying each sample and stop when we get to the end of the source
            // file or exceed the end time of the trimming.
            val offset = 0
            var trackIndex: Int
            val dstBuf = ByteBuffer.allocate(bufferSize)
            val bufferInfo = MediaCodec.BufferInfo()
//        try {
            muxer.start()
            while (true) {
                bufferInfo.offset = offset
                bufferInfo.size = extractor.readSampleData(dstBuf, offset)
                if (bufferInfo.size < 0) {
                    //InstabugSDKLogger.d(TAG, "Saw input EOS.");
                    bufferInfo.size = 0
                    break
                } else {
                    bufferInfo.presentationTimeUs = extractor.sampleTime
                    if (endMs > 0 && bufferInfo.presentationTimeUs > endMs * 1000) {
                        //InstabugSDKLogger.d(TAG, "The current sample is over the trim end time.");
                        break
                    } else {
                        bufferInfo.flags = extractor.sampleFlags
                        trackIndex = extractor.sampleTrackIndex
                        muxer.writeSampleData(indexMap.get(trackIndex), dstBuf,
                                bufferInfo)
                        extractor.advance()
                    }
                }
            }
            muxer.stop()
            return true
            //        } catch (e: IllegalStateException) {
            // Swallow the exception due to malformed source.
            //InstabugSDKLogger.w(TAG, "The source video file is malformed");
        } catch (e: Exception) {
            e.printStackTrace()
        } finally {
            muxer.release()
        }
        return false
    }

}

Исключение выдается val dstIndex = muxer.addTrack(format).Пока я завернул его в try-catch, чтобы избежать реального сбоя.

Я пытался искать более новые версии этого кода (при условии, что он был исправлен позже), но не получилось.

Поиск в Интернете и здесь, я нашел только один подобный вопрос, здесь , но это совсем не то же самое.

Вопросы

  1. Можно ли использовать платформу Android для обрезки таких проблемных файлов?Может быть, есть более новая версия обрезки видео кода?Меня, конечно, интересует только чистая реализация обрезки видео, подобная функции, которую я написал выше, для genVideoUsingMuxer.

  2. Как временное решение, возможно ли обнаружить проблемные входные видео, чтобы я не позволил пользователю начать обрезать их, поскольку я знаю, что они потерпят неудачу?

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

1 Ответ

0 голосов
/ 07 февраля 2019
  1. Почему это происходит?

audio/ac3 - неподдерживаемый тип пантомимы.

MediaMuxer.addTrack() (собственный) вызовы MPEG4Writer.addSource(), который печатает это сообщение журнала перед возвратом ошибки.

РЕДАКТИРОВАТЬ

Моя цель состояла не в том, чтобы дать ответ каждому из ваших подчиненныхвопросы, но чтобы дать вам некоторое представление о фундаментальной проблеме.Библиотека, которую вы выбрали, основана на компоненте Android MediaMuxer.По какой-то причине разработчики MediaMuxer не добавили поддержку этого конкретного аудиоформата.Мы знаем это, потому что программное обеспечение распечатывает явное сообщение на этот счет, а затем сразу выбрасывает IllegalStateException, упомянутый в вашем вопросе.

Поскольку проблема связана только с определенным аудиоформатом, когда вы предоставляете только видеоНа входе все работает нормально.

Чтобы устранить проблему, вы можете либо изменить библиотеку, чтобы обеспечить отсутствующую функциональность, либо найти новую библиотеку, которая лучше соответствует вашим потребностям.sannies/mp4parser может быть одной из таких альтернатив, хотя она имеет различные ограничения (если я правильно помню, она требует, чтобы все носители были в ОЗУ во время процесса мастеринга).Я не знаю, поддерживает ли он явно ac3, но он должен предоставить платформу, в которую вы можете добавить поддержку произвольных типов пантомимы.

Я бы посоветовал вам подождать более полного ответа.Там могут быть гораздо лучшие способы сделать то, что вы пытаетесь сделать.Но очевидно, что используемая вами библиотека просто не поддерживает все возможные типы MIME.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...