Calling MediaPlayer.PrepareAsync from VideoView - android

I am trying to implement a playlist of videos so they have a smooth transition from one video to the next. The mediaPlayer object has a prepareasync() method that would seem to prebuffer the video so it's ready to play. How do I invoke the prepareasync method from the VideoView object? The examples I found for mediaPlayer either don't use VideoView and seem to create the surface from scratch. Or the examples use mediaPlayer as return parameter on videoview eventlisteners which seem to occur only after the videoview.play(). I would like have access to videoview's mediaplayer before invoking the play method so I can invoke the prepareasync() and then later the the play().

Just as user1023110 mentioned, VideoView is a wrapper around MediaPlayer. Diving into the source code (since the docs aren't useful at all) I confirmed that it internally calls prepareAsync() in its private method openVideo():
private void openVideo() {
if (mUri == null || mSurfaceHolder == null) {
// not ready for playback just yet, will try again later
return;
}
// Tell the music playback service to pause
// TODO: these constants need to be published somewhere in the framework.
Intent i = new Intent("com.android.music.musicservicecommand");
i.putExtra("command", "pause");
mContext.sendBroadcast(i);
// we shouldn't clear the target state, because somebody might have
// called start() previously
release(false);
try {
mMediaPlayer = new MediaPlayer();
if (mAudioSession != 0) {
mMediaPlayer.setAudioSessionId(mAudioSession);
} else {
mAudioSession = mMediaPlayer.getAudioSessionId();
}
mMediaPlayer.setOnPreparedListener(mPreparedListener);
mMediaPlayer.setOnVideoSizeChangedListener(mSizeChangedListener);
mMediaPlayer.setOnCompletionListener(mCompletionListener);
mMediaPlayer.setOnErrorListener(mErrorListener);
mMediaPlayer.setOnInfoListener(mOnInfoListener);
mMediaPlayer.setOnBufferingUpdateListener(mBufferingUpdateListener);
mCurrentBufferPercentage = 0;
mMediaPlayer.setDataSource(mContext, mUri, mHeaders);
mMediaPlayer.setDisplay(mSurfaceHolder);
mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mMediaPlayer.setScreenOnWhilePlaying(true);
mMediaPlayer.prepareAsync();
// we don't set the target state here either, but preserve the
// target state that was there before.
mCurrentState = STATE_PREPARING;
attachMediaController();
} catch (IOException ex) {
Log.w(TAG, "Unable to open content: " + mUri, ex);
mCurrentState = STATE_ERROR;
mTargetState = STATE_ERROR;
mErrorListener.onError(mMediaPlayer, MediaPlayer.MEDIA_ERROR_UNKNOWN, 0);
return;
} catch (IllegalArgumentException ex) {
Log.w(TAG, "Unable to open content: " + mUri, ex);
mCurrentState = STATE_ERROR;
mTargetState = STATE_ERROR;
mErrorListener.onError(mMediaPlayer, MediaPlayer.MEDIA_ERROR_UNKNOWN, 0);
return;
}
}

VideoView is a wrapper around MediaPlayer and SurfaceView so you don't have to do it yourself. I believe the wrapper does the prepare when you call setVideoURI.
Its possible to get hold of the mediaplayer inside the VideoView through the onPrepared eventhandler, and once you've got it, I guess you can call prepare yourself after the first time, but I gather the internals are tricky and not well documented so things might not work as expected.

Related

Android MediaPlayer Races Through First Song

I have a list of song objects that I want to iterate through using a MediaPlayer. It works, but the first time I launch the app, it races through the first song (sometimes several seconds, sometimes the whole song). If I completely close and reopen the app, it is fine in subsequent runs. Why is it doing this, and how can I avoid it?
My SoundPlayerMedia class is:
private MediaPlayer _soundPlayer;
public SoundPlayerMedia(Context applicationContext) {
super();
_soundPlayer = MediaPlayer.create(applicationContext, R.raw.ringtone_cesium);
}
private void initializeMedia(Context context, int soundID, boolean startPlaying) {
AssetFileDescriptor afd = context.getResources().openRawResourceFd(soundID);
try {
if (_soundPlayer == null)
return;
_soundPlayer.reset();
_soundPlayer.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getDeclaredLength());
_soundPlayer.setVolume(1f, 1f); // Forces MAX volume
_soundPlayer.prepare();
if (startPlaying)
_soundPlayer.start();
afd.close();
} catch (Exception e) {
Log.e("DEBUG", "Unable to switch sound due to exception: " + e.getMessage(), e);
}
}
I use this in a fragment. In the onViewCreated, I have:
_fSoundPlayer = new SoundPlayerMedia(getContext().getApplicationContext());
_fSoundPlayer.setLooping(false);
_soundPlayer.initializeSound(getContext(), currSongId, startPlaying);

MediaPlayer - Alarm is played in headphones AND on the phone

I create a MediaPlayer like in the appended code. Everything works fine, but I've one problem: whenever an alarm is played and I'm connected to headphones, the sound is played on the phone and in the headphones. I would like the phone to be quiet in such a situation.
Actually, if I use the same function to create another music player to play music and set the stream type to AudioManager.STREAM_MUSIC, everything works fine.
I'm getting this behaviour on an android 4.4.4 phone and I know, that this code worked as expected on my old phone... With android 4.3 I think...
int streamVolume = ((AudioManager) getApplicationContext().getSystemService(Context.AUDIO_SERVICE)).getStreamVolume(AudioManager.STREAM_ALARM);
if (streamVolume != 0)
{
mAlarmPlayer = createMediaPlayerIfNeeded(mAlarmPlayer, true, true, false);
try
{
mAlarmPlayer.setDataSource(this, Uri.parse(sound));
mAlarmPlayer.setAudioStreamType(AudioManager.STREAM_ALARM);
mAlarmPlayer.setVolume(streamVolume, streamVolume);
mAlarmPlayer.prepareAsync();
}
catch (IOException e)
{
Log.e("MusicService", "IOException playing alarm: " + e.getMessage());
e.printStackTrace();
}
}
And here is the create function:
private MediaPlayer createMediaPlayerIfNeeded(MediaPlayer player, boolean setListenerOnPrepared, boolean setListenerOnCompletion, boolean setListenerOnError)
{
if (player == null)
{
player = new MediaPlayer();
player.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);
if (setListenerOnPrepared)
player.setOnPreparedListener(this);
if (setListenerOnCompletion)
player.setOnCompletionListener(this);
if (setListenerOnError)
player.setOnErrorListener(this);
}
else
player.reset();
return player;
}

progressive video download using android media player

I'm working on Video streaming over Delay-Tolerant Networks using android mobiles. For that im trying to implement progressive video download based on the tutorial given by pocket journey
http://blog.infidian.com/2008/04/04/tutorial-custom-media-streaming-for-androids-mediaplayer/
While creating a new media player the prepareAsync function is blocking the video display, making streaming unbearable. Is there a way to display something while the media player is prepared?
I'm using SurfaceView and SUrfaceHolder to display the video as suggested in the API media demos.
private MediaPlayer createMediaPlayer(File mediaFile)
throws IOException {
MediaPlayer mPlayer = new MediaPlayer();
mPlayer.setOnErrorListener(
new MediaPlayer.OnErrorListener() {
public boolean onError(MediaPlayer mp, int what, int extra) {
Log.e(TAG, "Error in MediaPlayer: (" + what +") with extra (" +extra +")" );
return true;
}
});
// It appears that for security/permission reasons, it is better to pass a FileDescriptor rather than a direct path to the File.
// Also I have seen errors such as "PVMFErrNotSupported" and "Prepare failed.: status=0x1" if a file path String is passed to
// setDataSource(). So unless otherwise noted, we use a FileDescriptor here.
FileInputStream fis = new FileInputStream(mediaFile);
mPlayer.setDataSource(fis.getFD());
mPlayer.setDisplay(holder);
mPlayer.prepareAsync();
return mPlayer;
}
/**
* Transfer buffered data to the MediaPlayer.
* NOTE: Interacting with a MediaPlayer on a non-main UI thread can cause thread-lock and crashes so
* this method should always be called using a Handler.
*/
private void transferBufferToMediaPlayer() {
try {
// First determine if we need to restart the player after transferring data...e.g. perhaps the user pressed pause
final boolean wasPlaying = mediaPlayer.isPlaying();
final int curPosition = mediaPlayer.getCurrentPosition();
Log.e(TAG, "Current position: "+curPosition);
final MediaPlayer temp = mediaPlayer;
// Copy the currently downloaded content to a new buffered File. Store the old File for deleting later.
File oldBufferedFile = new File(context.getCacheDir(),"playingMedia" + counter + ".dat");
File bufferedFile = new File(context.getCacheDir(),"playingMedia" + (counter++) + ".dat");
// This may be the last buffered File so ask that it be delete on exit. If it's already deleted, then this won't mean anything. If you want to
// keep and track fully downloaded files for later use, write caching code and please send me a copy.
bufferedFile.deleteOnExit();
moveFile(downloadingMediaFile,bufferedFile);
// Pause the current player now as we are about to create and start a new one. So far (Android v1.5),
// this always happens so quickly that the user never realized we've stopped the player and started a new one
if(wasPlaying){
Log.e(TAG, "mediaPlayer paused");
mediaPlayer.pause();
}
//mediaPlayer.release();
// Create a new MediaPlayer rather than try to re-prepare the prior one.
mediaPlayer = createMediaPlayer(bufferedFile);
mediaPlayer.setOnPreparedListener(
new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mPlayer) {
// TODO Auto-generated method stub
mediaPlayer.seekTo(curPosition);
// Restart if at end of prior buffered content or mediaPlayer was previously playing.
// NOTE: We test for < 1second of data because the media player can stop when there is still
// a few milliseconds of data left to play
int mediaLength = mediaPlayer.getDuration() - mediaPlayer.getCurrentPosition();
boolean atEndOfFile = mediaLength >= 1000;
Log.e(TAG, "MediaLen:"+mediaLength);
if (wasPlaying || atEndOfFile){
Log.e(TAG, "starting new player");
temp.release();
mediaPlayer.start();
}
}
});
// Lastly delete the previously playing buffered File as it's no longer needed.
oldBufferedFile.delete();
}catch (Exception e) {
Log.e(TAG, "Error updating to newly loaded content.", e);
}
}
Consider using exoplayer
http://developer.android.com/guide/topics/media/exoplayer.html
it was built with streaming in mind from the docs:
ExoPlayer supports features not currently provided by MediaPlayer, including Dynamic adaptive streaming over HTTP (DASH), SmoothStreaming, and persistent caching. ExoPlayer can be extended to handle additional media formats, and because you include it as part of your app code, you can update it along with your app.

How do I get my Media Player to double buffer?

My media player is in a service object of it's own. Here's the create code.
public void onCreate() {
Toast.makeText(this, "My Service Created", Toast.LENGTH_LONG).show();
Log.d(TAG, "onCreate");
player = new MediaPlayer();
try {
player.setDataSource(path);
player.prepare();
} catch (Exception e) {
}
player.setLooping(false); // Set looping
}
It is streaming from online. However, it's pretty choppy 3 minutes later. I want to double buffer this to help remove that. Any ideas on how I should do this?
Have you looked at the android Double Buffer class?

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

Categories

Resources