23

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!

spitzanator
  • 1,877
  • 4
  • 19
  • 29

2 Answers2

15

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!

spitzanator
  • 1,877
  • 4
  • 19
  • 29
  • Hello, I tried your solution to play video in video view.It plays well in android version 4.1 but when I run this in emulators i am getting same error `Error (1,-2147483648)`. I have tried .3gp and .mp4 videos but it gives me same error in emulators. What should I do to solve this problem, please guide me. This error is really vague, I am not getting what to do now. – Dory Sep 30 '13 at 13:45
  • What versions are your emulators running? – spitzanator Sep 30 '13 at 17:12
  • I've had some serious woes playing videos on emulators, regardless of version. I wouldn't trust them to be indicative of what'd happen on a real device. Is Android 4.1 your only device? – spitzanator Oct 01 '13 at 13:13
  • 1
    yes my device version is 4.1 and video plays well in it. But in another device which is version 2.2 video does not play. – Dory Oct 01 '13 at 13:19
  • Can you play that same video in a stock media app, like the Gallery? I've found some decoding issues with phones, as well. – spitzanator Oct 01 '13 at 18:57
  • No its not playing video. – Dory Oct 02 '13 at 06:01
  • Well, then it's not a problem with your VideoView setup. Make sure that your video is encoded in a compatible format. Lots of older devices can't handle high-quality recordings. http://developer.android.com/guide/appendix/media-formats.html#recommendations – spitzanator Oct 02 '13 at 13:15
  • okay, I wanted to know is this any fault in development or its device one that device not supporting video format or something like that. And could you guide me how to check video encoded formats. I am new in this. – Dory Oct 02 '13 at 13:17
  • If you can't play it with the stock media player, you're going to have a hard time getting it to work in a VideoView, in my experience. – spitzanator Oct 02 '13 at 17:33
  • Yes I am having hard time with getting this work, but in your experience what is possible solution. Any idea. – Dory Oct 03 '13 at 05:49
  • This is maybe not the right format. I asked a new question and answered it here: http://stackoverflow.com/questions/19160199/encoding-video-for-android/19160200 – spitzanator Oct 03 '13 at 13:15
  • Okay, I will go through this post. And thanks for guiding me in this. Hope I would be able to solve this weird problem. – Dory Oct 03 '13 at 13:32
  • I've had this error popup when I changed the path of a video and didnt update it in my code... – jesses.co.tt Jul 09 '14 at 23:27
  • I think this may be the cause of an error I'm having playing video on the first inflation only of a fragment containing my texture surface (and this is in Android 4.4.2!). Would you know if it could be necessary or even possible to do this with local .mp4 files in the /raw directory? – Daniel Wilson Sep 22 '14 at 15:48
  • class not found "CachedActionProvider" and i am using video url – Prasad Oct 07 '14 at 07:37
  • I'm getting these statements in log.. And video is not played. `E/ActivityThread﹕ Failed to find provider info for com.example.dotweb.imageproject.VideoProvider.files.files E/MediaPlayer﹕ Unable to create media player` – Prabs Aug 20 '15 at 07:23
4

I also had this problem while loading a video file from the cache-directory. The problem seemed to occure on Nexus-devices only but I coundn't test it on much devices.

I tried to write the file with Context.MODE_WORLD_READABLE but that fix didn't work for me. The VideoProvider.java also didn't fixed it.

So I found a other solution that worked for me. I created a tempFile in my Activity and gave the VideoView the absolut path to it.

Maybe my code can help someone :)

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