如何修剪Uri中的视频,包括`mp4parser`库可以处理的文件,而是改用Android的框架? [英] How can I trim a video from Uri, including files that `mp4parser` library can handle, but using Android's framework instead?

查看:141
本文介绍了如何修剪Uri中的视频,包括`mp4parser`库可以处理的文件,而是改用Android的框架?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在过去的几天中,我致力于制作可定制的,更新版本的视频修整库, 此库 )

Over the past few days, I've worked on making a customizable, more updated version of a library for video trimming, here (based on this library)

在大多数情况下,我已经成功实现了它的可定制性,甚至将所有文件都转换为Kotlin,但是修剪本身存在一个主要问题.

While for the most part, I've succeeded making it customizable and even converted all files into Kotlin, it had a major issue with the trimming itself.

它假定输入始终是一个文件,因此,如果用户从返回Uri的应用选择器中选择一个项目,则会崩溃.这样做的原因不仅在于UI本身,还在于它用于修整的库( mp4parser )假定仅输入文件(或文件路径),而不输入Uri(对此写信 此处 上写过.

It assumes the input is always a File, so if the user chooses an item from the apps chooser that returns a Uri, it crashes. The reason for this is not just the UI itself, but also because a library that it uses for trimming (mp4parser) assumes an input of only File (or filepath) and not a Uri (wrote about it here). I tried multiple ways to let it get a Uri instead, but failed. Also wrote about it here.

这就是为什么我使用在StackOverflow上找到的解决方案的原因( 在这里 )进行修剪.关于它的好处是它很安静,并且仅使用Android的框架本身.但是,对于某些视频文件,似乎总是无法修剪它们.作为此类文件的示例,原始库存储库中有一个文件, 此处 ).

That's why I used a solution that I've found on StackOverflow (here)for the trimming itself. The good thing about it is that it's quiet short and uses just Android's framework itself. However, it seems that for some video files, it always fails to trim them. As an example of such files, there is one on the original library repository, here (issue reported here).

看着异常,这就是我得到的:

Looking at the exception, this is what I got:

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. 考虑到此代码可能基于其他人的代码,我试图找到原始代码.我发现它基于其图库应用程序 此处 .

我用它编写的代码看起来像这样:

The code that I've made out of it looks as such:

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中,以避免真正的崩溃.

The exception is thrown on val dstIndex = muxer.addTrack(format) . For now, I've wrapped it in try-catch, to avoid a real crash.

我尝试搜索此代码的较新版本(假定以后已修复),但失败了.

I tried to search for newer versions of this code (assuming that it got fixed later), but failed.

  1. 在Internet上和此处进行搜索,我只发现了一个类似的问题, 此处 ,但根本不一样.

问题

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

  1. Is it possible to use Android's framework to trim such problematic files? Maybe there is a newer version of the trimming of the videos code? I'm interested of course only for the pure implementation of video trimming, like the function I wrote above, of "genVideoUsingMuxer" .

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

As a temporary solution, is it possible to detect problematic input videos, so that I won't let the user start to trim them, as I know they will fail?

这两个版本是否还有另一种选择,它们都具有许可许可证,并且不会膨胀该应用程序?对于mp4parser,我写了一个单独的问题, 此处 .

Is there maybe another alternative to both of those, that have a permissive license and doesn't bloat the app? For mp4parser, I wrote a separate question, here.

推荐答案

  1. 为什么会发生?

audio/ac3是不受支持的mime类型.

audio/ac3 is an unsupported mime type.

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

MediaMuxer.addTrack() (native) calls MPEG4Writer.addSource(), which prints this log message before returning an error.

编辑

我的目的不是为每个子问题提供答案,而是让您对基本问题有所了解.您选择的库依赖于Android的MediaMuxer组件.出于任何原因,MediaMuxer开发人员都未添加对此特定音频格式的支持.我们之所以知道这一点,是因为该软件会打印出一条明确的信息,然后立即抛出问题中提到的IllegalStateException.

My aim was not to provide an answer to each of your sub-questions, but to give you some insight into the fundamental problem. The library you have chosen relies on the Android's MediaMuxer component. For whatever reason, the MediaMuxer developers did not add support for this particular audio format. We know this because the software prints out an explicit message to that effect, then immediately throws the IllegalStateException mentioned in your question.

因为问题仅涉及特定的音频格式,所以当您提供仅视频输入时,一切正常.

Because the issue only involves a particular audio format, when you provide a video-only input, everything works fine.

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

To fix the problem, you can either alter the library to provide for the missing functionality, or find a new library that better suits your needs. sannies/mp4parser may be one such alternative, although it has different limitations (if I recall correctly, it requires all media to be in RAM during the mastering process). I do not know if it supports ac3 explicitly, but it should provide a framework to which you can add support for arbitrary mime types.

我鼓励您等待更完整的答案.可能有更好的方法来做您要尝试做的事情.但是很明显,您所使用的库并不支持所有可能的mime类型.

I would encourage you to wait for a more complete answer. There may be far better ways to do what you are trying to do. But it is apparent that the library you are using simply does not support all possible mime types.

这篇关于如何修剪Uri中的视频,包括`mp4parser`库可以处理的文件,而是改用Android的框架?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆