【问题标题】:Using mp4parser , how can I handle videos that are taken from Uri and ContentResolver?使用 mp4parser ,我如何处理从 Uri 和 ContentResolver 获取的视频?
【发布时间】:2019-02-03 13:25:20
【问题描述】:

背景

我们想让用户从任何应用程序中选择一个视频,然后将视频修剪为最长 5 秒。

问题

为了让 Uri 被选中,我们让它工作正常(解决方案可用here)。

至于修剪本身,我们找不到任何具有许可许可证的好库,除了一个名为 "k4l-video-trimmer" 的库。例如,库“FFmpeg”被认为是未经许可的,因为它使用 GPLv3,这要求使用它的应用程序也必须是开源的。此外,正如我所读到的,它需要很多(大约 9MB)。

遗憾的是,这个库 (k4l-video-trimmer) 已经很老了,而且很多年都没有更新,所以我不得不分叉它 (here) 才能很好地处理它。它使用一个名为"mp4parser" 的开源库来进行修剪。

问题是,这个库似乎只能处理文件,而不是 UriInputStream,所以即使是示例在选择像普通文件一样无法访问的项目时也会崩溃,甚至有它无法处理的路径。我知道在很多情况下可以获得文件的路径,但在很多其他情况下,它不是,而且我也知道可以只复制文件(here),但这不是一个好解决方案,因为文件可能很大并且占用大量空间,即使它已经可以访问。

我尝试过的

库使用文件的地方有 2 个:

  1. 在“K4LVideoTrimmer”文件中,在“setVideoURI”函数中,它只是获取要显示的文件大小。这里的解决方案很简单,基于Google's documentation

    public void setVideoURI(final Uri videoURI) {
        mSrc = videoURI;
        if (mOriginSizeFile == 0) {
            final Cursor cursor = getContext().getContentResolver().query(videoURI, null, null, null, null);
            if (cursor != null) {
                int sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE);
                cursor.moveToFirst();
                mOriginSizeFile = cursor.getLong(sizeIndex);
                cursor.close();
                mTextSize.setText(Formatter.formatShortFileSize(getContext(), mOriginSizeFile));
            }
        }
     ...
    
  2. 在“TrimVideoUtils”文件中,在调用“genVideoUsingMp4Parser”函​​数的“startTrim”中。在那里,它使用以下方法调用“mp4parser”库:

    Movie movie = MovieCreator.build(new FileDataSourceViaHeapImpl(src.getAbsolutePath()));
    

    它说他们使用FileDataSourceViaHeapImpl(来自“mp4parser”库)来避免Android上的OOM,所以我决定继续使用它。

    问题是,它有 4 个 CTOR,都期望文件有一些变体:File、filePath、FileChannel、FileChannel+fileName。

问题

  1. 有没有办法克服这个问题?

也许实现FileChannel 并使用ContentResolverUri 模拟真实文件?我想这可能是可能的,即使这意味着在需要时重新打开 InputStream...

为了看看我做了什么,你可以克隆项目here。只知道它不做任何修剪,因为它在“K4LVideoTrimmer”文件中的代码被注释了:

//TODO handle trimming using Uri
//TrimVideoUtils.startTrim(file, getDestinationPath(), mStartPosition, mEndPosition, mOnTrimVideoListener);
  1. 这个修剪库是否有更好的替代方案,它也是允许的(例如 Apache2/MIT 许可证的含义)?没有这个问题的?或者甚至可能是 Android 框架本身的东西?我认为MediaMuxer 类可能会有所帮助(如here 所写),但我认为它可能需要 API 26,而我们需要处理 API 21 及更高版本...

编辑:

我想我已经找到了一个解决方案,通过使用不同的解决方案来修剪自己,并写了here,但遗憾的是它无法处理一些输入视频,而mp4parser 库可以处理它们。

请让我知道是否可以修改 mp4parser 以处理此类输入视频,即使它来自 Uri 而不是文件(没有仅复制到视频文件的解决方法)。

【问题讨论】:

  • API 18 Onwards 支持您提到的 MediaMuxer 类 [developer.android.com/reference/android/media/MediaMuxer].而答案stackoverflow.com/a/44653626/878126指的是Lollipop,也就是API21。我相信你可以轻松使用它!此外,如果您有 URI,那么围绕它创建一个 File 对象并将其传递给库进行处理有什么问题?你试过这个吗?如果您这样做了,响应/错误/问题是什么?
  • @RahulShukla 我发现的代码有效(它来自 API 18,顺便说一句,而不是链接上的 21),但仅适用于某些输入文件。对于其他一些人,它引发了异常,我在这里写了:stackoverflow.com/q/54573454/878126。你可以在这里查看我为此制作的 POC:github.com/AndroidDeveloperLB/VideoTrimmer。至于使用来自 Uri 的文件,这并不总是可能的。示例来自 Google Drive 应用程序。我目前的解决方案同时使用了这两种方法,但仍然会失败:(

标签: android inputstream android-contentresolver filechannel mp4parser


【解决方案1】:

首先警告:我不熟悉 mp4parser 库,但您的问题看起来很有趣,所以我看了一下。

我认为值得您查看代码 cmets 所说的“主要用于测试”的类之一。 InMemRandomAccessSourceImpl。要从任何 URI 创建电影,代码如下:

try {
    InputStream  inputStream = getContentResolver().openInputStream(uri);
    Log.e("InputStream Size","Size " + inputStream);
    int  bytesAvailable = inputStream.available();
    int bufferSize = Math.min(bytesAvailable, MAX_BUFFER_SIZE);
    final byte[] buffer = new byte[bufferSize];

    int read = 0;
    int total = 0;
    while ((read = inputStream.read(buffer)) !=-1 ) {
        total += read;
    }
    if( total < bytesAvailable ){
        Log.e(TAG, "Read from input stream failed")
        return;
    }
    //or try inputStream.readAllBytes() if using Java 9
    inputStream.close();

    ByteBuffer bb = ByteBuffer.wrap(buffer);
    Movie m2 = MovieCreator.build(new ByteBufferByteChannel(bb),
        new InMemRandomAccessSourceImpl(bb), "inmem");

} catch (FileNotFoundException e) {
    e.printStackTrace();
} catch (IOException e) {
    e.printStackTrace();
}

但我想说,您想要实现的目标与解析器采用的方法之间似乎有些冲突。它依赖于本地文件以避免大的内存开销,并且只有在整个数据集可用的情况下才能对字节进行随机访问,这与流式方法不同。

在为解析器提供缓冲区之前,至少需要一次性缓冲剪辑所需的数据量。如果您希望抓取较短的部分并且缓冲不太麻烦,这可能对您有用。如果从InputStream 读取有问题,尤其是远程内容时,您可能会遇到 IO 异常等问题,而现代系统上的文件确实不希望出现这种情况。

还有 MemoryFile 需要考虑,它提供了一个 ashmem 支持的类似文件的对象。我认为可以通过某种方式解决。

【讨论】:

  • 有一个缓冲区是有意义的,修剪应该使用一些内存(不是大量的内存,但仍然......)。这似乎合乎逻辑,但我该怎么做呢?给定 Uri 和/或创建 InputStream 的方法,您是否有一个可行的解决方案?对给定的输入不做任何假设。这意味着它可能具有非常高的质量和/或很长的持续时间,我们可以将其修剪到很小的持续时间或很大的持续时间(查看实际修剪功能的参数)...
  • 在您的问题中,您提到了 2 个不同的地方:1. 在“K4LVideoTrimmer”文件中,以及 2.“TrimVideoUtils”。您发布了 1. 的解决方案,以替换为 Uri。上面的代码示例应该为您提供从 Uri 和内容解析器开始的 2 的解决方案。但我同意,高质量和长持续时间需要考虑你如何缓冲。当您从 InputStream 中读取数据时,MemoryFile 可以提供一个灵活的中介。在内存不足的情况下,您可以启用系统清除文件的设置。可以使用环形缓冲区和/或一次读取部分进一步优化此任务
  • “k4l-video-trimmer”库使用“mprparser”,需要使用文件。此处提到了可以处理 Uri 的第二种解决方案:stackoverflow.com/q/54573454/878126,但它似乎不支持某些视频(在我的情况下,那些视频中包含“audio/ac3”)。现在你可以检查我的 Github 存储库,它使用两种方式(第二种是第一种的后备):github.com/AndroidDeveloperLB/VideoTrimmer。关于MemoryFile,你试过了吗?它使用大量内存吗?请分享它的代码。您可以为此使用我的存储库。
  • @androiddeveloper 你最后做了什么?
  • @androiddeveloper 看来你还没有解决这个问题,看这个-github.com/AndroidDeveloperLB/VideoTrimmer/blob/…
【解决方案2】:

接下来的片段展示了如何使用 Mp4Parser 中的 IsoFile 打开 MediaStore Uri。因此,您可以了解如何从 Uri 获取 FileChannel。

public void test(@NonNull final Context context, @NonNull final Uri uri) throws IOException
{
    ParcelFileDescriptor fileDescriptor = null;

    try
    {
        final ContentResolver resolver = context.getContentResolver();
        fileDescriptor = resolver.openFileDescriptor(uri, "rw");

        if (fileDescriptor == null)
        {
            throw new IOException("Failed to open Uri.");
        }

        final FileDescriptor  fd          = fileDescriptor.getFileDescriptor();
        final FileInputStream inputStream = new FileInputStream(fd);
        final FileChannel     fileChannel = inputStream.getChannel();

        final DataSource channel = new FileDataSourceImpl(fileChannel);
        final IsoFile    isoFile = new IsoFile(channel);

        ... do what you need ....
    }
    finally
    {
        if (fileDescriptor != null)
        {
            fileDescriptor.close();
        }
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-10-27
    • 1970-01-01
    • 2019-07-01
    • 1970-01-01
    • 2014-07-31
    • 1970-01-01
    • 1970-01-01
    • 2016-01-21
    相关资源
    最近更新 更多