19

I'm trying to provide my users with the ability to use either external or internal storage. I'm displaying both images and videos (of a scientific nature). When storing the media on the SD card, all is fine. But when I store the media internally, only the images will display. No matter what I try I get various errors when trying to load and display the media stored under the applicationcontext.getFilesDir().

Is there a trick to setting a videoview's content to such a file?

Can a ContentResolver help me?

On a related note, is it considered bad form to assume that external storage exists?

Thanks in advance,

Sid

Below is one version that fails with "Cannot play video. Sorry, this video cannot be played". But I have many other modes of failure. I can copy the internal video to temp storage (external) and play it, so this copy to internal does indeed create a valid movie. It only fails when I try to play it directly from the internal storage.

videoFile = new File(this.getFilesDir() + File.separator + "test.mp4");


InputStream data = res.openRawResource(R.raw.moviegood);


try {
    OutputStream myOutputStream = new FileOutputStream(videoFile);


    byte[] buffer = new byte[8192];
    int length;
    while ( (length = data.read(buffer)) > 0 ) {
        myOutputStream.write(buffer);
    }

    //Close the streams
    myOutputStream.flush();
    myOutputStream.close();
    data.close();
} catch (FileNotFoundException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
} catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}




vview.setKeepScreenOn(true);
vview.setVideoPath(videoFile.getAbsolutePath());
vview.start();
jnthnjns
  • 8,962
  • 4
  • 42
  • 65
shellman
  • 193
  • 1
  • 1
  • 6
  • Please format the question / code, otherwise it's hard to read. What kind of exception are you getting? It would help if you would log the stack trace in your catch clause. Anyway, did you find a way to play the videos from internal storage? I'm looking for the same and getting an error like "java.io.IOException: Prepare failed." – Mathias Conradt Aug 04 '10 at 09:14
  • I filed a bug report at http://code.google.com/p/android/issues/detail?id=10197 – Mathias Conradt Aug 04 '10 at 19:54
  • Thanks for reporting the bug. I gave up on internal storage long ago. The client's budget did not allow for me to spend any more time working on this. – shellman Sep 06 '10 at 17:16

6 Answers6

25

MediaPlayer requires that the file being played has world-readable permissions. You can view the permissions of the file with the following command in adb shell:

ls -al /data/data/com.mypackage/myfile

You will probably see "-rw------", which means that only the owner (your app, not MediaPlayer) has read/write permissions.

Note: Your phone must be rooted in order to use the ls command without specifying the file (in the internal memory).

If your phone is rooted, you can add world-read permissions in adb shell with the following command:

chmod o+r /data/data/com.mypackage/myfile

If you need to modify these permissions programmatically (requires rooted phone!), you can use the following command in your app code:

Runtime.getRuntime().exec("chmod o+r /data/data/com.mypackage/myfile");

Which is basically a linux command. See https://help.ubuntu.com/community/FilePermissions for more on chmod.

EDIT: Found another simple approach here (useful for those without rooted phones). Since the application owns the file, it can create a file descriptor and pass that to mediaPlayer.setDataSource():

FileInputStream fileInputStream = new FileInputStream("/data/data/com.mypackage/myfile");
mediaPlayer.setDataSource(fileInputStream.getFD());

This approach avoids the permission issue completely.

gtkandroid
  • 496
  • 5
  • 6
  • Thanks gtkandroid, This makes perfect sense. (I'm an old time linux guy, from the days when you installed your OS using a box of floppy disks). Since our app has been released to the marketplace, and is a National Science Foundation funded project meant to reach the largest possible audience, I think sticking with SD card storage is fine for us. Cheers, Sid – shellman Mar 30 '11 at 17:12
  • @Tim mode WORLD_READABLE works great if you are creating the file with a FileOutputStream but for those using MediaRecorder this is not an option. – gtkandroid Apr 05 '11 at 23:32
  • How would you do this using only the VideoView though? – Drazen Bjelovuk Jun 04 '14 at 20:52
2

You can use:

videoView.setVideoURI(Uri.parse(file.getAbsolutePath()));

if the file is world readable

Or you can use a content provider

HocineHamdi
  • 191
  • 6
2

For detail check this tutorial

public class AndroidVideoViewExample extends Activity {

    private VideoView myVideoView;
    private int position = 0;
    private ProgressDialog progressDialog;
    private MediaController mediaControls;

    @Override
    protected void onCreate(final Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // set the main layout of the activity
        setContentView(R.layout.activity_main);

        //set the media controller buttons
        if (mediaControls == null) {
            mediaControls = new MediaController(AndroidVideoViewExample.this);
        }

        //initialize the VideoView
        myVideoView = (VideoView) findViewById(R.id.video_view);

        // create a progress bar while the video file is loading
        progressDialog = new ProgressDialog(AndroidVideoViewExample.this);
        // set a title for the progress bar
        progressDialog.setTitle("JavaCodeGeeks Android Video View Example");
        // set a message for the progress bar
        progressDialog.setMessage("Loading...");
        //set the progress bar not cancelable on users' touch
        progressDialog.setCancelable(false);
        // show the progress bar
        progressDialog.show();

        try {
            //set the media controller in the VideoView
            myVideoView.setMediaController(mediaControls);

            //set the uri of the video to be played
            myVideoView.setVideoURI(Uri.parse("android.resource://" + getPackageName() + "/" + R.raw.kitkat));

        } catch (Exception e) {
            Log.e("Error", e.getMessage());
            e.printStackTrace();
        }

        myVideoView.requestFocus();
        //we also set an setOnPreparedListener in order to know when the video file is ready for playback
        myVideoView.setOnPreparedListener(new OnPreparedListener() {

            public void onPrepared(MediaPlayer mediaPlayer) {
                // close the progress bar and play the video
                progressDialog.dismiss();
                //if we have a position on savedInstanceState, the video playback should start from here
                myVideoView.seekTo(position);
                if (position == 0) {
                    myVideoView.start();
                } else {
                    //if we come from a resumed activity, video playback will be paused
                    myVideoView.pause();
                }
            }
        });

    }

    @Override
    public void onSaveInstanceState(Bundle savedInstanceState) {
        super.onSaveInstanceState(savedInstanceState);
        //we use onSaveInstanceState in order to store the video playback position for orientation change
        savedInstanceState.putInt("Position", myVideoView.getCurrentPosition());
        myVideoView.pause();
    }

    @Override
    public void onRestoreInstanceState(Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);
        //we use onRestoreInstanceState in order to play the video playback from the stored position 
        position = savedInstanceState.getInt("Position");
        myVideoView.seekTo(position);
    }
}
Zar E Ahmer
  • 33,936
  • 20
  • 234
  • 300
1

I came across this thread with the same problem, I'm downloading my videos from the web to the internal storage, turns out when saving you can specify the RW mode, i.e change from PRIVATE to WORLD_READABLE

URL url = new URL(_url);
InputStream input = null;
FileOutputStream output = null;

try {
String outputName = "video.mp4";

input = url.openConnection().getInputStream();
output = c.openFileOutput(outputName, Context.MODE_WORLD_READABLE);

int read;
byte[] data = new byte[5120]; //5MB byte array
while ((read = input.read(data)) != -1)
output.write(data, 0, read);

return true;

} finally {
if (output != null)
   output.close();
if (input != null)
   input.close();
    }
}
tutts
  • 2,373
  • 2
  • 20
  • 24
1

I posted a custom VideoView implementation there.

The VideoView implementation has the setVideoFD(FileDescriptor fd) method and solves this issue.

sth
  • 222,467
  • 53
  • 283
  • 367
tuandroid
  • 11
  • 2
0

You can't just play it directly.

You need to implement a ContentProvider then pass the defined Uri to setVideoUri(uri) method.

HocineHamdi
  • 191
  • 6