使用mp4parser,我如何处理从Uri和ContentResolver中获取的视频?

问题描述 投票:14回答:1

背景

我们希望让用户从任何应用中选择视频,然后将视频剪裁为最多5秒。

问题

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

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

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

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

我试过的

库有两个地方使用文件:

  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”文件中,在“startTrim”中调用“genVideoUsingMp4Parser”函​​数。在那里,它使用以下方法调用“mp4parser”库: Movie movie = MovieCreator.build(new FileDataSourceViaHeapImpl(src.getAbsolutePath())); 它说他们使用FileDataSourceViaHeapImpl(来自“mp4parser”库)来避免Android上的OOM,所以我决定继续使用它。 事实是,它有4个CTORS,都期望文件有一些变化:File,filePath,FileChannel,FileChannel + fileName。

问题

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

也许通过使用FileChannelContentResolver实现Uri并模拟真实文件?我想这可能是有可能的,即使这意味着在需要时重新打开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而不是文件(没有复制到视频文件的解决方法)。

android inputstream android-contentresolver filechannel mp4parser
1个回答
4
投票

首先要注意的是:我对mp4parser库不熟悉,但你的问题看起来很有趣所以我看一下。

我认为值得你看一下代码评论所说的“主要用于测试”的类。 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支持的文件类对象。我想某种方式可以用到。

© www.soinside.com 2019 - 2024. All rights reserved.