web-dev-qa-db-fra.com

Android Erreur MediaPlayer / VideoView (1, -2147483648)

J'ai eu une expérience incohérente avec la définition d'une vidéo VideoView à partir d'un chemin de fichier.

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

videoFilename est le chemin absolu d'une vidéo dans mon répertoire cache:

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

En Android SDK> = 16 (Jelly bean), cela fonctionne très bien et ma superbe vidéo est lue. En Android 4.0.4 (SDK = 15), MediaPlayer se casse lorsque myVideoView.start () est appelé.

L'erreur est toujours inutile:

error (1, -2147483648)

Qu'est-ce que j'oublie ici? Chargement d'un fichier directement à partir des ressources de mon package (res/raw) ou d'Internet ( http://something.com/myawesomeinternetvideo.mp4 ), mais je ne sais pas comment lire les fichiers mon répertoire cache!

23
spitzanator

Il s'avère que l'erreur -2147483648 indique une erreur inconnue. Cela pourrait avoir quelque chose à voir avec l'encodage vidéo, mais cela vaut également la peine de vérifier que le chemin du fichier existe et que VideoView a l'autorisation de le lire .

Mon problème était que j'écrivais mes fichiers avec Context.MODE_PRIVATE (par défaut).

openFileOutput(filename, Context.MODE_PRIVATE);

Cela indique que seule votre application peut accéder au fichier. Je ne sais pas précisément comment ni pourquoi, mais dans Jelly bean et au-dessus, il semble que la vue vidéo est autorisée à accéder au fichier que vous spécifiez comme s'il s'agissait de votre application, mais avant Jelly bean, la vue vidéo tente d'ouvrir le fichier dans son propre contexte (pas celui de votre application). Le mode étant privé, il échoue.

Une solution consiste à écrire votre fichier avec Context.MODE_WORLD_READABLE , qui est désormais obsolète. Cela indique que n'importe qui peut ouvrir le fichier sur ce chemin. C'est évidemment dangereux et découragé.

J'ai fini par créer un fournisseur de contenu et mon propre URI pour gérer ce cas. Plus précisément:

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

Et puis, où j'accède à mes fichiers vidéo:

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

Il s'agit d'une manière très longue de dire à VideoView de demander à votre ContentProvider le descripteur de fichier des données. Les descripteurs de fichiers ne sont pas autorisés, vous ouvrez donc le fichier en utilisant les autorisations de votre application et le remettez au VideoView plutôt que de demander au VideoView d'ouvrir le fichier en utilisant ses propres autorisations.

Cela corrige mon problème et, espérons-le, le vôtre aussi!

15
spitzanator

J'ai également eu ce problème lors du chargement d'un fichier vidéo à partir du répertoire cache. Le problème semblait se produire uniquement sur les appareils Nexus, mais je ne l'ai pas testé sur de nombreux appareils.

J'ai essayé d'écrire le fichier avec Context.MODE_WORLD_READABLE mais ce correctif n'a pas fonctionné pour moi. Le VideoProvider.Java ne l'a pas non plus corrigé.

J'ai donc trouvé une autre solution qui fonctionnait pour moi. J'ai créé un tempFile dans mon activité et ai donné à VideoView le chemin absolu.

Peut-être que mon code peut aider quelqu'un :)

 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));
    }
}
4
Maik Peschutter