web-dev-qa-db-ja.com

mp4parserを使用して、URIおよびContentResolverから取得したビデオを処理するにはどうすればよいですか?

バックグラウンド

ユーザーが任意のアプリから動画を選択できるようにしてから、動画を最大5秒にトリミングします。

問題

Uriを選択するために、正常に機能しました(ソリューションが利用可能です here )。

トリミング自体に関しては、 "k4l-video-trimmer"と呼ばれるものを除いて、パーミッシブライセンスを持つ優れたライブラリは見つかりませんでした。 -) 。たとえば、ライブラリ「FFmpeg」はGPLv3を使用しているため、許可されていないと見なされます。GPLv3を使用するアプリは、オープンソースでもある必要があります。その上、私が読んだように、それはかなり多く(約9MB)かかります。

残念ながら、このライブラリ(k4l-video-trimmer)は非常に古く、何年も更新されていなかったため、適切に処理するためにフォーク( here )する必要がありました。 "mp4parser" というオープンソースのライブラリを使用してトリミングを行います。

問題は、このライブラリはUriInputStreamではなくファイルのみを処理できるように見えるため、通常のファイルのように到達できないアイテムを選択すると、サンプルでさえクラッシュする可能性があることです。または、処理できないパスがある場合もあります。多くの場合、ファイルのパスを取得できることは知っていますが、そうでない場合も多く、ファイルをコピーすることも可能です( here )ですが、ファイルはすでにアクセス可能であるにもかかわらず、ファイルが大きくなり、多くのスペースを占める可能性があるため、これは適切な解決策ではありません。

私が試したこと

ライブラリがファイルを使用する場所は2つあります。

  1. 「K4LVideoTrimmer」ファイルの「setVideoURI」関数で、表示するファイルサイズを取得するだけです。ここでの解決策は、 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));
            }
        }
     ...
    
  2. 「TrimVideoUtils」ファイルの「genVideoUsingMp4Parser」関数を呼び出す「startTrim」。そこで、以下を使用して「mp4parser」ライブラリを呼び出します。

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

    AndroidでOOMを回避するために(「mp4parser」ライブラリから)FileDataSourceViaHeapImplを使用すると書かれているので、そのまま使用することにしました。

    つまり、4つのCTORがあり、すべてファイルのバリエーションが必要です:File、filePath、FileChannel、FileChannel + fileName。

質問

  1. これを克服する方法はありますか?

たぶん、FileChannelContentResolverを使用して、Uriを実装し、実際のファイルをシミュレートしますか?必要なときにInputStreamを再度開くことを意味する場合でも、それは可能かもしれないと思います...

私が何を機能させたかを確認するために、プロジェクトのクローンを作成できます ここ 。 「K4LVideoTrimmer」ファイルのコードがコメント化されているため、トリミングは行われないことを知っておいてください。

//TODO handle trimming using Uri
//TrimVideoUtils.startTrim(file, getDestinationPath(), mStartPosition, mEndPosition, mOnTrimVideoListener);
  1. おそらく、このトリミングライブラリのより良い代替手段がありますか?これも許容されます(たとえば、Apache2/MITライセンスを意味します)?この問題がない方は?あるいは、Androidフレームワーク自体?私は MediaMuxer クラスが役立つと思います(書かれているように here )、しかし私たちはAPI 21以上を処理する必要がある一方で、API26が必要かもしれないと思います。 。

編集:

自分自身をトリミングするために別の解決策を使用して解決策を見つけたと思い、それについて書きました ここ 、しかし悲しいことに一部の入力ビデオは処理できませんが、mp4parserライブラリはそれらを処理できます。

ファイルではなくURIからのものであっても、そのような入力ビデオを処理するようにmp4parserを変更できるかどうかを教えてください(ビデオファイルにコピーするだけの回避策なしで)。

16

まず最初に注意点:私はmp4parserライブラリに精通していませんが、あなたの質問は面白そうだったので調べました。

コードコメントが言うクラスの1つを見る価値は、「主にテスト用」だと思います。 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でバックアップされたファイルのようなオブジェクトを提供します。どういうわけかそれはうまくいくと思います。

4
dr_g