I've been having an inconsistent experience with setting a VideoView's video from a file path.
VideoView myVideoView = findViewById(R.id.videoView);
...
myVideoView.setVideoPath(videoFilename);
...
myVideoView.start();
videoFilename is the absolute path of a video in my cache directory:
String videoFilename = new File(context.getCacheDir(), "myawesomevideo.mp4").getAbsolutePath();
In Android SDK >= 16 (Jelly Bean), this works just fine and my awesome video plays. In Android 4.0.4 (SDK = 15), the MediaPlayer breaks when myVideoView.start() is called.
The error is the ever-unhelpful:
error (1, -2147483648)
What am I missing here? Loading a file directly from my package assets (res/raw) or the internet (http://something.com/myawesomeinternetvideo.mp4), but I can't figure out how to read files out of my cache directory!
As it turns out, error -2147483648 indicates an unknown error. This could have something to do with the video encoding, but it's also worth checking that the file path exists and that the VideoView has permission to read it.
My issue was that I was writing my files with Context.MODE_PRIVATE (the default).
openFileOutput(filename, Context.MODE_PRIVATE);
This indicates that only your application can access the file. I don't know specifically how or why, but in Jelly Bean and above, it appears that the video view is allowed to access the file you specify as if it were your application, but before Jelly Bean, the video view tries to open the file in its own context (not your application's). Since the mode is private, it fails.
One solution is to write your file with Context.MODE_WORLD_READABLE, which is now deprecated. This indicates that anyone can open the file at that path. This is obviously unsafe and discouraged.
I ended up creating a content provider and my own URI to handle this case. Specifically:
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();
}
}
And then, where I access my video files:
VideoView myVideoView = findViewById(R.id.videoView);
...
myVideoView.setVideoURI(
Uri.parse(
CachedActionProvider.CONTENT_URI_BASE + Uri.encode(videoFilename)));
...
myVideoView.start();
This is a really long-winded way of telling the VideoView to ask your ContentProvider for the file descriptor to the data. File descriptors aren't permissioned, so you open the file using your app's permissions and hand it off to the VideoView rather than asking the VideoView to open the file using its own permissions.
This fixes my issue and hopefully yours, too!