使用mp4parser,如何处理从Uri和ContentResolver拍摄的视频? [英] Using mp4parser , how can I handle videos that are taken from Uri and ContentResolver?

查看:160
本文介绍了使用mp4parser,如何处理从Uri和ContentResolver拍摄的视频?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

背景

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

问题

要选择一个Uri,我们可以使其正常运行(解决方案 此处 ).

对于修整本身,我们找不到具有许可许可的任何好的库,除了一个名为 .例如,由于库FFmpeg使用GPLv3,因此该库被视为未经许可,这要求使用该库的应用也必须是开源的.此外,据我了解,这需要很多时间(大约9MB).

可悲的是,这个库(k4l-video-trimmer)很旧,多年没有更新,因此我不得不对其进行分叉(此处),以便更好地进行处理.它使用名为 "mp4parser" 的开源库进行修整./p>

但问题是,这个库似乎只能够处理文件,而不是一个,所以即使样品可以选择所有不可像一个正常的文件项时崩溃,甚至有无法处理的路径.我知道在很多情况下都可以获取文件的路径,但在其他情况下却不能,而且我也知道可以仅复制文件( Google的文档 :

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));
        }
    }
 ...

  • 在"TrimVideoUtils"文件的"startTrim"文件中,该文件调用"genVideoUsingMp4Parser"函​​数.在那里,它使用以下命令调用"mp4parser"库:

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

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

    事情是,它有4个CTORS,所有人都希望文件有一些变化:File,filePath,FileChannel,FileChannel + fileName.

  • 问题

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

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

    为了查看我的工作方式,可以在此处克隆项目.只知道它不做任何修整,因为"K4LVideoTrimmer"文件中的代码已注释:

    //TODO handle trimming using Uri
    //TrimVideoUtils.startTrim(file, getDestinationPath(), mStartPosition, mEndPosition, mOnTrimVideoListener);
    

    1. 是否可以使用这个修整库更好的替代方法,它也是允许的(例如,Apache2/MIT许可证的含义)?一个没有这个问题的人吗?也许甚至是Android框架本身?我认为 MediaMuxer 类可能会有所帮助(写成 此处 ),但我认为它可能需要API 26,而我们需要处理API 21和以上...


    我以为我已经找到了通过使用其他解决方案来解决自身问题的解决方案,并在 此处 strong> ,但可悲的是它不能处理某些输入视频,而mp4parser库可以处理它们.

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

    解决方案

    首先要警告:我对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支持的文件状对象.我认为可以以某种方式解决此问题.

    Background

    We want to let the user choose a video from any app, and then trim a video to be of max of 5 seconds.

    The problem

    For getting a Uri to be selected, we got it working fine (solution available here) .

    As for the trimming itself, we couldn't find any good library that has permissive license, except for one called "k4l-video-trimmer" . The library "FFmpeg", for example, is considered not permission as it uses GPLv3, which requires the app that uses it to also be open sourced. Besides, as I've read, it takes quite a lot (about 9MB).

    Sadly, this library (k4l-video-trimmer) is very old and wasn't updated in years, so I had to fork it (here) in order to handle it nicely. It uses a open sourced library called "mp4parser" to do the trimming.

    Problem is, this library seems to be able to handle files only, and not a Uri or InputStream, so even the sample can crash when selecting items that aren't reachable like a normal file, or even have paths that it can't handle. I know that in many cases it is possible to get a path of a file, but in many other cases, it's not, and I also know it's possible to just copy the file (here), but this isn't a good solution, as the file could be large and take a lot of space even though it's already accessible.

    What I've tried

    There are 2 places that the library uses a file:

    1. In "K4LVideoTrimmer" file, in the "setVideoURI" function, which just gets the file size to be shown. Here the solution is quite easy, based on 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. In "TrimVideoUtils" file, in "startTrim" which calls "genVideoUsingMp4Parser" function. There, it calls the "mp4parser" library using :

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

      It says that they use FileDataSourceViaHeapImpl (from "mp4parser" library) to avoid OOM on Android, so I decided to stay with it.

      Thing is, there are 4 CTORS for it, all expect some variation of a file: File, filePath, FileChannel , FileChannel+fileName .

    The questions

    1. Is there a way to overcome this?

    Maybe implement FileChannel and simulate a real file, by using ContentResolver and Uri ? I guess it might be possible, even if it means re-opening the InputStream when needed...

    In order to see what I got working, you can clone the project here. Just know that it doesn't do any trimming, as the code for it in "K4LVideoTrimmer" file is commented:

    //TODO handle trimming using Uri
    //TrimVideoUtils.startTrim(file, getDestinationPath(), mStartPosition, mEndPosition, mOnTrimVideoListener);
    

    1. Is there perhaps a better alternative to this trimming library, which is also permissive (meaning of Apache2/MIT licences , for example) ? One that don't have this issue? Or maybe even something of Android framework itself? I think MediaMuxer class could help (as written here), but I think it might need API 26, while we need to handle API 21 and above...


    EDIT:

    I thought I've found a solution by using a different solution for trimming itself, and wrote about it here, but sadly it can't handle some input videos, while mp4parser library can handle them.

    Please let me know if it's possible to modify mp4parser to handle such input videos even if it's from Uri and not a File (without a workaround of just copying to a video file).

    解决方案

    First of all a caveat: I am not familiar with the mp4parser library but your question looked interesting so I took a look.

    I think its worth you looking at one of the classes the code comments say is "mainly for testing". InMemRandomAccessSourceImpl. To create a Movie from any URI, the code would be as follows:

    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();
    }
    

    But I would say, there looks to be somewhat of a conflict between what you want to achieve and the approach the parser takes. It is depending on local files to avoid large memory overheads, and random access to bytes can only be done if the entire set of data is available, which differs from a streaming approach.

    It will require buffering at least the amount of data required for your clip in one go before the parser is given the buffer. That might be workable for you if you are looking to grab short sections and the buffering is not too cumbersome. You may be subject to IO exceptions and the like if the read from the InputStream has issues, especially if it is remote content, whereas you really aren't expecting that with a file on a modern system.

    There is also MemoryFile to consider which provides an ashmem backed file-like object. I think somehow that could be worked in.

    这篇关于使用mp4parser,如何处理从Uri和ContentResolver拍摄的视频?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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