23

ファイル パスから VideoView のビデオを設定する際に一貫性のない経験がありました。

VideoView myVideoView = findViewById(R.id.videoView);
...
myVideoView.setVideoPath(videoFilename);
...
myVideoView.start();

videoFilename は、キャッシュ ディレクトリ内のビデオの絶対パスです。

String videoFilename = new File(context.getCacheDir(), "myawesomevideo.mp4").getAbsolutePath();

Android SDK >= 16 (Jelly Bean) では、これは問題なく動作し、素晴らしいビデオが再生されます。Android 4.0.4 (SDK = 15) では、myVideoView.start() が呼び出されると MediaPlayer が壊れます。

エラーはこれまで役に立たなかった:

error (1, -2147483648)

ここで何が欠けていますか?パッケージ アセット (res/raw) またはインターネット ( http://something.com/myawesomeinternetvideo.mp4 ) からファイルを直接読み込みますが、キャッシュ ディレクトリからファイルを読み取る方法がわかりません。

4

2 に答える 2

15

結局のところ、エラー -2147483648 は不明なエラーを示しています。これは動画のエンコーディングに関係している可能性がありますが、ファイル パスが存在することと、VideoView がそれを読み取る権限を持っていることも確認する価値があります。

私の問題は、ファイルを Context.MODE_PRIVATE (デフォルト) で書き込んでいたことでした。

openFileOutput(filename, Context.MODE_PRIVATE);

これは、アプリケーションだけがファイルにアクセスできることを示しています。具体的な方法や理由はわかりませんが、Jelly Bean 以降では、アプリケーションであるかのように、指定したファイルにビデオ ビューがアクセスできるように見えますが、Jelly Bean の前に、ビデオ ビューはファイルを開こうとします。独自のコンテキストでファイルを作成します (アプリケーションのコンテキストではありません)。モードがプライベートであるため、失敗します。

1 つの解決策は、現在非推奨になっているContext.MODE_WORLD_READABLEを使用してファイルを書き込むことです。これは、誰でもそのパスでファイルを開くことができることを示しています。これは明らかに安全ではなく、お勧めできません。

このケースを処理するために、コンテンツ プロバイダーと独自の URI を作成することになりました。具体的には:

AndroidManfest.xml :

...
    <provider
        android:name="com.myexampleapp.video.VideoProvider"
            android:authorities="com.myexampleapp.video.VideoProvider.files"
        android:exported="false" />
    </application>
</manifest>

VideoProvider.java :

package com.myexampleapp.video;

import java.io.File;
import java.io.FileNotFoundException;

import android.content.ContentProvider;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.os.ParcelFileDescriptor;

public class VideoProvider extends ContentProvider { 
    public static final Uri CONTENT_URI_BASE =
            Uri.parse("content://com.myexampleapp.video.VideoProvider.files.files/");

    private static final String VIDEO_MIME_TYPE = "video/mp4";

    @Override
    public boolean onCreate() {
        return true;
    }

    @Override
    public String getType(final Uri uri) {
        return VIDEO_MIME_TYPE;
    }

    @Override
    public ParcelFileDescriptor openFile(final Uri uri, final String mode)
            throws FileNotFoundException {
        File f = new File(uri.getPath());

        if (f.exists())
            return (ParcelFileDescriptor.open(f,
                    ParcelFileDescriptor.MODE_READ_ONLY));

        throw new FileNotFoundException(uri.getPath());
    }

    @Override
    public int delete(final Uri uri, final String selection, final String[] selectionArgs) {
        throw new UnsupportedOperationException();
    }

    @Override
    public Uri insert(final Uri uri, final ContentValues values) {
        throw new UnsupportedOperationException();
    }

    @Override
    public Cursor query(final Uri uri, final String[] projection, final String selection, final String[] selectionArgs, final String sortOrder) {
        throw new UnsupportedOperationException();
    }

    @Override
    public int update(final Uri uri, final ContentValues values, final String selection, final String[] selectionArgs) {
        throw new UnsupportedOperationException();
    }
}

そして、ビデオファイルにアクセスする場所:

VideoView myVideoView = findViewById(R.id.videoView);
...
myVideoView.setVideoURI(
    Uri.parse(
        CachedActionProvider.CONTENT_URI_BASE + Uri.encode(videoFilename)));
...
myVideoView.start();

これは、VideoView に ContentProvider にデータへのファイル記述子を要求するよう指示する非常に長い方法です。ファイル記述子にはアクセス許可がないため、VideoView に独自のアクセス許可を使用してファイルを開くように要求するのではなく、アプリのアクセス許可を使用してファイルを開き、VideoView に渡します。

これで私の問題が解決し、できればあなたの問題も解決します!

于 2013-09-16T17:05:07.947 に答える
4

キャッシュディレクトリからビデオファイルをロードしているときにも、この問題が発生しました。この問題は Nexus デバイスでのみ発生しているように見えましたが、多くのデバイスではテストできませんでした。

Context.MODE_WORLD_READABLE でファイルを書き込もうとしましたが、その修正はうまくいきませんでした。VideoProvider.java もそれを修正しませんでした。

そこで、自分に合った別の解決策を見つけました。Activity に tempFile を作成し、VideoView に絶対パスを指定しました。

多分私のコードは誰かを助けることができます:)

 private MediaController mController;

private VideoView mVideoView;

private File mTmpFile = null;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_video);

    init();
}

private void init() {
    mProgressDialog = new ProgressDialog(this);

    mVideoView = (VideoView) findViewById(R.id.surface_video);
    mVideoView.requestFocus();
    mController = new MediaController(this);
    mVideoView.setMediaController(mController);

    mVideoView.setOnPreparedListener(this);
    mVideoView.setOnCompletionListener(this);
    mVideoView.setOnErrorListener(this);

    String url = getIntent().getExtras().getString(MainActivity.VIDEO_URL_EXTRA_KEY);
    if (VideoLoaderTask.hasVideo(this, url)) {
        url = getCacheDir().toString().concat("/" + VideoLoaderTask.VIDEO_DIR + "/").concat(url);
        copyToTmpFile(url);
        mVideoView.setVideoPath(mTmpFile.getAbsolutePath());
    } else {
        mVideoView.setVideoURI(Uri.parse(url));
    }

    mProgressDialog.show();
    mVideoView.start();
}

@Override
public void onDestroy() {
    if (mTmpFile != null) {
        mTmpFile.delete();
    }
    super.onDestroy();
}

private void copyToTmpFile(String url) {
    File f = new File(url);
    try {
        mTmpFile = File.createTempFile("video", null);
        mTmpFile.deleteOnExit();

        FileInputStream is = new FileInputStream(f);
        int size = is.available();
        byte[] buffer = new byte[size];
        is.read(buffer);
        is.close();

        FileOutputStream fos = new FileOutputStream(mTmpFile);
        fos.write(buffer);
        fos.close();
    } catch (Exception e) {
        mVideoView.setVideoURI(Uri.parse(url));
    }
}
于 2015-03-25T11:07:36.567 に答える