Android - Load video from private folder of app - android
I have a problem that I have been trying to find a solution for a long time. My situation is as follows:
I have an app that downloads zipped videos and unzips them at the application's private folder and more specifically at a subfolder. For example at /data/data/my.app.package.name.here/files/assets/assets-955.
Inside this folder the video is unzipped. The unzipping process is completed successfully since I can pull and view the video without problems when running the app on the emulator.
I then have another activity that is accessing this folder, finds the video file and tries to open it. At this point I get an error that "Sorry, this video cannot be played" with the following error stack:
01-30 17:36:17.770: D/ContentDemoActivity(6757): File: /data/data/xxxx/files/assets/assets-955/bank_2.mp4
01-30 17:36:17.830: I/MediaPlayer(6757): prepareAsync called in state 4
01-30 17:36:17.830: E/MediaPlayer(6757): error (1, -2147483648)
01-30 17:36:17.860: E/MediaPlayer(6757): Error (1,-2147483648)
01-30 17:36:17.860: D/VideoView(6757): Error: 1,-2147483648
01-30 17:36:19.370: E/MediaPlayer(6757): stop called in state 0
01-30 17:36:19.370: E/MediaPlayer(6757): error (-38, 0)
01-30 17:36:19.370: W/MediaPlayer(6757): mediaplayer went away with unhandled events
The code with which I am trying to play the video is pretty basic:
mView = (VideoView) findViewById(R.id.videoView);
mMediaPlayer = new MediaPlayer();
mView.requestFocus();
mHolder = mView.getHolder();
Log.d(tag, "Populating content. Assets path: " + mAssetsPath);
File f = new File(mAssetsPath);
File[] files = f.listFiles();
Log.d(tag, "File: " + files[0].toString());
mView.setVideoURI(Uri.parse(files[0].toString()));
mView.setMediaController(new MediaController(this));
and the layout of the activity has a plain VideoView, nothing fancy there.
The strangest thing is that for testing purposes I used the same video, this time loading it from the "raw" folder and it runs smoothly without problem. In that case though I had to load it with:
Uri video = Uri.parse("android.resource://" + getPackageName() + "/" + R.raw.bank_2);
mVideoView.setVideoURI(video);
mVideoView.start();
I would do the same with the downloaded videos but there doesn't seem to be any function at the API that will allow me to load a video Uri from the application's private folder.
I have found various solutions by using file descriptors, listeners for the videoView, flags indicating MODE_WORLD_READABLE, pre-calculation of the dimensions of the videoView, etc but none of them had positive results.
In a nutshell, my questions are:
Why do I get those errors which according to what I have found online are errors that are related with problematic encoding of the video file ?
What is the best things to use in my case, a VideoView or a surfaceView ?
Which is the ideal method to load a video from the application's private folder and be able to play it?
Thanks.
EDIT
After CommonsWare suggestion, I went with the following implementation:
File f = new File(mAssetsPath);
File[] files = f.listFiles();
Log.d(tag, "File: " + files[0].toString());
URI uri = URI.create("file://" + (files[0].toString()));
File file = new File(uri);
try {
Log.d(tag, "1");
ParcelFileDescriptor parcel = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_WRITE);
Log.d(tag, "2");
mMediaPlayer.setDataSource(parcel.getFileDescriptor());
Log.d(tag, "3");
mMediaPlayer.start();
Log.d(tag, "4");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
Log.d(tag, "5");
Unfortunately, this time I get the following errors:
01-31 12:40:11.480: D/ContentDemoActivity(15896): File: /data/data/com.houseofradon.meb/files/assets/assets-955/bank_2.mp4
01-31 12:40:11.480: D/ContentDemoActivity(15896): 1
01-31 12:40:11.480: D/ContentDemoActivity(15896): 2
01-31 12:40:11.500: D/ContentDemoActivity(15896): 3
01-31 12:40:11.500: E/MediaPlayer(15896): start called in state 2
01-31 12:40:11.500: E/MediaPlayer(15896): error (-38, 0)
01-31 12:40:11.500: D/ContentDemoActivity(15896): 4
01-31 12:40:11.500: D/ContentDemoActivity(15896): 5
01-31 12:40:11.530: E/MediaPlayer(15896): Error (-38,0)
So, something happens when the media player starts. Error code -38 doesn't seem to mean anything specific as I found here.
Any idea what I am missing ???
EDIT #2
I now use a mediaPlayer and a SurfaceView to do the whole process along with a surfaceHolder listener. Here is the code:
mMediaPlayer = new MediaPlayer();
mSurfaceView = (SurfaceView) findViewById(R.id.surface);
mHolder = mSurfaceView.getHolder();
mHolder.addCallback(this);
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
Log.d(tag, "surfaceChanged");
try {
mMediaPlayer.setDisplay(mHolder);
Log.d(tag, "7");
mMediaPlayer.start();
Log.d(tag, "8");
} catch (IllegalStateException e) {
e.printStackTrace();
}
Log.d(tag, "9");
}
public void surfaceCreated(SurfaceHolder holder) {
Log.d(tag, "surfaceCreated");
File f = new File(mAssetsPath);
File[] files = f.listFiles();
Log.d(tag, "File: " + files[0].toString());
URI uri = URI.create("file://" + (files[0].toString()));
File file = new File(uri);
try {
Log.d(tag, "1");
ParcelFileDescriptor parcel = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_WRITE);
Log.d(tag, "2");
mMediaPlayer.setDataSource(parcel.getFileDescriptor());
Log.d(tag, "3");
mMediaPlayer.setVolume(100, 100);
Log.d(tag, "4");
mMediaPlayer.prepare();
Log.d(tag, "5");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
Log.d(tag, "6");
}
I can listen to the audio of the video but the picture is just a plain black color. I also get an error almost at the end of the video playback that says:
01-31 14:26:01.300: W/AudioSystem(17165): AudioFlinger server died!
01-31 14:26:01.300: W/IMediaDeathNotifier(17165): media server died
01-31 14:26:01.300: E/MediaPlayer(17165): error (100, 0)
01-31 14:26:01.300: E/MediaPlayer(17165): Error (100,0)
I am using an actual device, Samsung Galaxy Tab 10.1. Any ideas ?
Why do I get those errors which according to what I have found online are errors that are related with problematic encoding of the video file ?
Because the media playback engine runs in its own process, and it does not have rights to read your file.
What is the best things to use in my case, a VideoView or a surfaceView ?
A VideoView contains a SurfaceView. Whether you use VideoView or a combination of MediaPlayer and SurfaceView is up to you.
Which is the ideal method to load a video from the application's private folder and be able to play it?
Either create a ContentProvider that can serve up your local file and use the provider Uri instead of the Uri to a local file, or create the local file using openFileOutput() and MODE_WORLD_READABLE.
Related
AudioRecord file size is a little large
I am struggling with this, I have changed the bitrate to reduce the recording filesize, my app correctly posts audio files to a server, yet I want to minimize filesize, this is my record code private void startRecording() throws IOException { String state = android.os.Environment.getExternalStorageState(); if (!state.equals(android.os.Environment.MEDIA_MOUNTED)) { throw new IOException("No SD mounted. It is" + state + "."); } mRecorder = new MediaRecorder(); mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); mRecorder.setAudioSamplingRate(44100); mRecorder.setAudioEncodingBitRate(44100); mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP); File path = new File(Environment.getExternalStorageDirectory() .getPath()); if (!path.exists() && !path.mkdirs()) { throw new IOException("The file directory is invalid."); }else{ try { archivo = File.createTempFile("audio", ".3gp", path); } catch (IOException e) { } } mRecorder.setOutputFile(archivo.getAbsolutePath()); mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC); try { mRecorder.prepare(); } catch (IOException e) { Log.e(LOG_TAG, "prepare() failed"); } mRecorder.start(); } I am getting like 336 kb for 1 minute recording right now, I want to decrease it to around 100 - 200 kb per minute without loosing too much quality
A couple of things you can try. 1) use AMR (adaptive multi-rate) for higher compression: recorder.setAudioEncoder(mRecorder.AudioEncoder.AMR_NB); AMR Wideband and AMR Narrowband are the encoding methods used by the device for telephone calls. Might not have the quality you require. 2) use mono 1 channel: mRecorder.setAudioChannels (1) // Sets the number of audio channels for recording. Call this method before // prepare(). // Usually it is either 1 (mono) or 2 (stereo).
Error on some devices starting MediaRecorder start failed -19
I am trying to record video in my app, on my Nexus 5 and a ZTE this code works fine but on Samsumg Galaxy S2 and S3 fails with the exception "start failed -19" I'm a noob with the MediaRecorder, I only need a configuration that works on all cameras that record a .mp4 videofile file with normal quality. try { prCamera.unlock(); prMediaRecorder.setCamera(prCamera); prMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); prMediaRecorder.setVideoSource(MediaRecorder.VideoSource.DEFAULT); prMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4); prMediaRecorder.setVideoFrameRate(24); prMediaRecorder.setVideoSize(720, 480); prMediaRecorder.setVideoFrameRate(24); prMediaRecorder.setVideoEncodingBitRate(3000000); prMediaRecorder.setAudioEncodingBitRate(12200); prMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); prMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264); prMediaRecorder.setMaxDuration(cMaxRecordDurationInMs); // Nombre del video de salida String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.ENGLISH).format(new Date()); nombreVideo = "video_" + timeStamp; prRecordedFile = new File(cVideoFilePath + nombreVideo + ".mp4"); prMediaRecorder.setOutputFile(prRecordedFile.getPath()); prMediaRecorder.setPreviewDisplay(prSurfaceHolder.getSurface()); try { prMediaRecorder.prepare(); // Fail on next line prMediaRecorder.start(); } catch (IllegalStateException e) { Log.d(tag, "IllegalStateException preparing MediaRecorder: " + e.getMessage()); timer.cancel(); cerrarCamara(); return false; } catch (IOException e) { Log.d(tag, "IOException preparing MediaRecorder: " + e.getMessage()); timer.cancel(); cerrarCamara(); return false; } catch (Exception e) { Log.d(tag, "Unknown exception preparing MediaRecorder: " + e.getMessage()); timer.cancel(); cerrarCamara(); return false; } prRecordInProcess = true;
Why you don't use CamcorderProfile profile = CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH); I wasn't able to find the exact settings of this profile but I'm pretty sure the outputformat was mpeg4 and that is what you want... I remember that most media recorder problems regard to wrong setted parameters. Try use this profile. It guarantees the highest available and working setup for the device. You can read further informations about this and other profiles which may fits your needs better here http://developer.android.com/reference/android/media/CamcorderProfile.html#QUALITY_HIGH
Using MediaPlayer to play an AMR file created by MediaRecorder fails with muri null
I am writing an app that records voice from the microphone in AMR format using MediaRecorder, and then plays the data back using MediaPlayer. That's the goal anyway. I am fairly confident my MediaRecorder side is working, I'm producing the data file in the right place at the right data rate. Here's how I start and stop my MediaRecorder public void OnStartRecord(View v ) { System.out.println( "StartRecord"); try { audioFile = File.createTempFile("amrtmp", ".amr", getApplicationContext().getFilesDir()); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println( "Recording to " + audioFile.getAbsolutePath()); mRecorder = new MediaRecorder(); mRecorder.setAudioSource(MediaRecorder.AudioSource.VOICE_COMMUNICATION); mRecorder.setOutputFormat(MediaRecorder.OutputFormat.AMR_NB); mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); mRecorder.setAudioEncodingBitRate(4750); mRecorder.setAudioSamplingRate(8000); mRecorder.setOutputFile(audioFile.getAbsolutePath()); try { mRecorder.prepare(); } catch (IllegalStateException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } mRecorder.start(); } public void OnStopRecord(View v ) { System.out.println( "StopRecord"); mRecorder.stop(); mRecorder.release(); } This works like a charm. Typical output is something like StartRecord Recording to /data/data/com.test.playback/files/amrtmp-235967797.amr And when I start, then stop recording I can see that the file has been created and it has a certain amount of data in it that properly corresponds to the settings. Side note: I detect an odd buzzing at my speaker while this runs. Any idea what that is? When I try to play the file back however I have no end of trouble. I have tried the following: public void OnPlay(View v ) { MediaPlayer mPlayer = new MediaPlayer(); mPlayer.setAudioStreamType(AudioManager.STREAM_VOICE_CALL); FileInputStream FIS = null; try { FIS = new FileInputStream(audioFile.getAbsolutePath()); mPlayer.setDataSource(FIS.getFD()); mPlayer.prepare(); } catch( Exception e ) { e.printStackTrace(); } mPlayer.start(); } This results in nothing being played at all with the following output from MediaPlayer: start() mURI is null I have also tried the same code, but setting mPlayer's data source differently: mPlayer.setDataSource(audioFile.getAbsolutePath()); This fails when prepare is called witha java.io.IOException status 0x1. I have to imagine there is something else I need to do with MediaPlayer to set it up properly. Any suggestions?
Media Player Show video
Why doesn't the MediaPlayer show the video as soon as it is available. What I mean is on the IPhone when a video is played the video shows up right away. Even when returning from pause. But on the Android the screen stays black for a couple of milliseconds to a second depending on the device used and how many processes are running in the background. I'm asking this because i want to use one of the beginning frames from my video play as a type of screenshot and currently I'm using a handler to wait 1 second before pausing the video. Can someone tell me a quick way to make the video show up as soon as it is started or even prepared instead of my workaround? EDIT: Here is how I prepare my video player so It should be prepared right. private void initVideo() { Log.i("VideoPlayer", "Initialize Video File" + videoFileName); AssetFileDescriptor afd; try { if(videoFileName != null); { afd = getAssets().openFd(videoFileName); vidplayer = new MediaPlayer(); vidplayer.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getDeclaredLength()); vidplayer.setDisplay(holder); vidplayer.prepare(); vidplayer.setOnCompletionListener(this); vidplayer.setOnPreparedListener(this); //Log.i("INITVIDEO", Integer.toString(videoPausedAt)); vidplayer.seekTo(videoPausedAt); //Log.i("VideoPlayer", "video Prepared"); videoDuration = vidplayer.getDuration()/1000; isVideoReady = true; } } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (IllegalStateException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (Exception e) { //Log.i("InitPlayer", e.getClass().toString()); e.printStackTrace(); } }
For the background, you can get a thumbnail of the video: private Bitmap getThumbnail(String path){ try{ return ThumbnailUtils.createVideoThumbnail(path, MediaStore.Images.Thumbnails.MINI_KIND); }catch(Exception e){ return null; } } When the video starts, you'll need to set the background back to null or you won't be able to see the video. As for it not playing right away, it should play as soon as start() is called if you prepared it correctly, but it could be delayed if it has to load data let's say from a stream over the internet.
I have found that it is the phones fault.(mostly) Video's will show up automatically unless phone is bogged down with apps and thus loading of the video takes longer (noticed after having a voip service running).
Problems with MediaPlayer, raw resources, stop and start
I'm new to Android development and I have a question/problem. I'm playing around with the MediaPlayer class to reproduce some sounds/music. I am playing raw resources (res/raw) and it looks kind of easy. To play a raw resource, the MediaPlayer has to be initialized like this: MediaPlayer mp = MediaPlayer.create(appContext, R.raw.song); mp.start(); Until here there is no problem. The sound is played, and everything works fine. My problem appears when I want to add more options to my application. Specifically when I add the "Stop" button/option. Basically, what I want to do is...when I press "Stop", the music stops. And when I press "Start", the song/sound starts over. (pretty basic!) To stop the media player, you only have to call stop(). But to play the sound again, the media player has to be reseted and prepared. mp.reset(); mp.setDataSource(params); mp.prepare(); The problem is that the method setDataSource() only accepts as params a file path, Content Provider URI, streaming media URL path, or File Descriptor. So, since this method doesn't accept a resource identifier, I don't know how to set the data source in order to call prepare(). In addition, I don't understand why you can't use a Resouce identifier to set the data source, but you can use a resource identifier when initializing the MediaPlayer. I guess I'm missing something. I wonder if I am mixing concepts, and the method stop() doesn't have to be called in the "Stop" button. Any help? Thanks in advance!!!
Here is what I did to load multiple resources with a single MediaPlayer: /** * Play a sample with the Android MediaPLayer. * * #param resid Resource ID if the sample to play. */ private void playSample(int resid) { AssetFileDescriptor afd = context.getResources().openRawResourceFd(resid); try { mediaPlayer.reset(); mediaPlayer.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getDeclaredLength()); mediaPlayer.prepare(); mediaPlayer.start(); afd.close(); } catch (IllegalArgumentException e) { Log.e(TAG, "Unable to play audio queue do to exception: " + e.getMessage(), e); } catch (IllegalStateException e) { Log.e(TAG, "Unable to play audio queue do to exception: " + e.getMessage(), e); } catch (IOException e) { Log.e(TAG, "Unable to play audio queue do to exception: " + e.getMessage(), e); } mediaPlay is a member variable that get created and released at other points in the class. This may not be the best way (I am new to Android myself), but it seems to work. Just note that the code will probably fall trough to the bottom of the method before the mediaPlayer is done playing. If you need to play a series of resources, you will still need to handle this case.
this is how MediaPlayer.create method works to open a raw file: public static MediaPlayer create(Context context, int resid) { try { AssetFileDescriptor afd = context.getResources().openRawResourceFd(resid); if (afd == null) return null; MediaPlayer mp = new MediaPlayer(); mp.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength()); afd.close(); mp.prepare(); return mp; } catch (IOException ex) { Log.d(TAG, "create failed:", ex); // fall through } catch (IllegalArgumentException ex) { Log.d(TAG, "create failed:", ex); // fall through } catch (SecurityException ex) { Log.d(TAG, "create failed:", ex); // fall through } return null; }
Or, you could access the resource in this way: mediaPlayer.setDataSource(context, Uri.parse("android.resource://com.package.name/raw/song")); where com.package.name is the name of your application package
You can use mp.pause(); mp.seekTo(0); to stop music player.
Finally, the way it works for me: public class MainStart extends Activity { ImageButton buttonImage; MediaPlayer mp; Boolean playing = false; #Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); buttonImage = (ImageButton)findViewById(R.id.ButtonID); buttonImage.setOnClickListener(new OnClickListener() { #Override public void onClick(View v) { if(playing){ mp.stop(); playing = false; }else{ mp = MediaPlayer.create(getApplicationContext(), R.raw.sound_u_want); mp.start(); playing = true; } } }); } }
MR. Rectangle, this message maybe too late for it, but I proudly write these codes to your idea: I have mp for mediaplayer and sescal9 is a button. .... if(btnClicked.getId() == sescal9_ornek_muzik.getId()) { mp.start(); mp.seekTo(380); mp2.start(); mp2.seekTo(360); mp3.start(); mp3.seekTo(340); ... }
Recheck your passing parameters not null Possible reasons Context may be null Your media file may be corrupted