我怎样才能修剪Uri的视频,包括`mp4parser`库可以处理的文件,而是使用Android的框架呢?

and*_*per 15 android video-editing mediamuxer

背景

在过去的几年里,我一直在做的视频修剪库的定制,更多更新的版本的工作,在这里(基于这个库)

问题

虽然在大多数情况下,我已经成功地将其定制,甚至将所有文件转换为Kotlin,但它在修剪本身时遇到了一个重大问题.

它假定输入始终是文件,因此如果用户从返回Uri的应用选择器中选择一个项目,它就会崩溃.这样做的原因不仅仅是UI本身,还因为它用于修剪的库(mp4parser)假定只输入File(或文件路径)而不是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)
Run Code Online (Sandbox Code Playgroud)

我发现了什么

  1. 这里报道了这个问题.我不认为它会得到答案,因为图书馆多年来没有更新......
  2. 看着异常,我试着在没有声音的情况下修剪.这有效,但这不是一件好事,因为我们想要正常修剪.
  3. 认为这段代码可能基于别人的代码,我试图找到原始代码.我发现它基于其图库应用程序中的一些旧的Google代码,在此处,在"Gallery3d"包中名为"VideoUtils.java"的类中.可悲的是,我没有看到任何新版本.我看到的最新的是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
    }

}
Run Code Online (Sandbox Code Playgroud)

抛出异常val dstIndex = muxer.addTrack(format).现在,我把它包装在try-catch中,以避免真正的崩溃.

我试图搜索此代码的更新版本(假设它稍后修复),但失败了.

  1. 搜索互联网和这里,我发现只有一个类似的问题,在这里,但它是不一样的.

问题

  1. 是否可以使用Android的框架来修剪这些有问题的文件?也许有更新版本的视频代码修剪?我当然只对纯粹实现视频修剪感兴趣,就像我上面写的"genVideoUsingMuxer"一样.

  2. 作为临时解决方案,是否可以检测有问题的输入视频,以便我不会让用户开始修剪它们,因为我知道它们会失败?

  3. 是否可能有另一种替代方案,它们具有许可许可并且不会使应用程序膨胀?因为mp4parser,我在这里写了一个单独的问题.

gre*_*e31 3

  1. 为什么会出现这种情况?

audio/ac3是不受支持的 MIME 类型。

MediaMuxer.addTrack()(本机)调用MPEG4Writer.addSource(),它在返回错误之前打印此日志消息。

编辑

我的目的不是为您的每个子问题提供答案,而是让您深入了解基本问题。您选择的库依赖于 Android 的MediaMuxer组件。无论出于何种原因,MediaMuxer开发人员没有添加对这种特定音频格式的支持。我们知道这一点是因为该软件会打印出一条明确的消息来说明这一点,然后立即抛出IllegalStateException您问题中提到的内容。

由于问题仅涉及特定的音频格式,因此当您提供纯视频输入时,一切正常。

要解决此问题,您可以更改库以提供缺少的功能,或者找到更适合您需求的新库。sannies/mp4parser可能是这样的一种选择,尽管它有不同的限制(如果我没记错的话,它要求在母带处理过程中所有媒体都位于 RAM 中)。我不知道它是否明确支持 ac3,但它应该提供一个框架,您可以在其中添加对任意 mime 类型的支持。

我鼓励您等待更完整的答案。可能有更好的方法来完成您想做的事情。但很明显,您使用的库根本不支持所有可能的 mime 类型。