Android MediaPlayer setNextMediaPlayer not working - android

My objective is to create a VideoView that can play videos in a pre-defined play list.
I'm trying to use MediaPlayer.setNextMediaPlayer(...) to allow a seamless transition between two videos. However, when the first video finishes playing, the 2nd video will not start automatically as it should according to the documentation.
Xamarin Android Code:
Queue<MediaPlayer> MediaPlayerList = null;
private void PlayVideo()
{
MediaPlayerList = new Queue<MediaPlayer>();
//Let's go ahead and create all media players
VideoView_CurrentVideoView = new VideoView(this);
VideoView_CurrentVideoView.Completion += mVideoView_Completion;
VideoView_CurrentVideoView.Prepared += mVideoView_Prepared;
//Let's prepare all MediaPlayer
for (int i = 0; i < VideoView_CurrentVideoChannel.VideoAssetList.Count; i++)
{
string filePath = FilePath[i];
if (i == 0)
{
VideoView_CurrentVideoView.SetVideoPath(filePath);
VideoContainer.AddView(VideoView_CurrentVideoView);
}
else
{
MediaPlayer mpNew = new MediaPlayer();
mpNew.SetDataSource(filePath);
MediaPlayerList.Enqueue(mpNew);
}
}
VideoView_CurrentVideoView.Start();
}
void mVideoView_Completion(object sender, EventArgs e)
{
MediaPlayer mp = (MediaPlayer)sender;
mp.Release();
}
void mVideoView_Prepared(object sender, EventArgs e)
{
MediaPlayer mp = (MediaPlayer)sender;
//Take the next available MediaPlayer from the queue
MediaPlayer nextMediaPlayer = MediaPlayerList.Dequeue();
//Put the current MediaPlayer at the end of the queue
MediaPlayerList.Enqueue(mp);
nextMediaPlayer.Prepare();
mp.SetNextMediaPlayer(nextMediaPlayer);
}
Any help or suggestions will be greatly appreciated. This is coded in Xamarin Android.
Update #1: After moving the .Prepare() away from Prepared() event
Queue<string> VideoListQueue = null;
MediaPlayer NextMediaPlayer = null;
private void PlayVideo()
{
string filePath = FilePath[0];
//Create video view
if (VideoContainer.ChildCount == 0)
{
//setup the videoview container
VideoView_CurrentVideoView = new VideoView(this);
VideoView_CurrentVideoView.Info += VideoView_CurrentVideoView_Info;
VideoView_CurrentVideoView.Error += VideoView_CurrentVideoView_Error;
LinearLayout.LayoutParams param = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.FillParent, ViewGroup.LayoutParams.FillParent);
param.LeftMargin = 0;
param.RightMargin = 0;
param.BottomMargin = 0;
param.TopMargin = 0;
VideoView_CurrentVideoView.LayoutParameters = param;
VideoView_CurrentVideoView.LayoutParameters.Width = ViewGroup.LayoutParams.FillParent;
VideoView_CurrentVideoView.LayoutParameters.Height = ViewGroup.LayoutParams.FillParent;
VideoView_CurrentVideoView.Completion += VideoView_CurrentVideoView_Completion;
VideoView_CurrentVideoView.Prepared += VideoView_CurrentVideoView_Prepared;
VideoContainer.AddView(VideoView_CurrentVideoView);
}
VideoView_CurrentVideoView.SetVideoPath(filePath);
VideoView_CurrentVideoView.SeekTo(0);
VideoView_CurrentVideoView.Start();
}
void VideoView_CurrentVideoView_Prepared(object sender, EventArgs e)
{
//Do nothing at this moment
MediaPlayer mp = (MediaPlayer)sender;
}
void VideoView_CurrentVideoView_Completion(object sender, EventArgs e)
{
//GC the finished MediaPlayer
MediaPlayer mp = (MediaPlayer)sender;
mp.Reset();
mp.Release();
mp = null;
//Preparing the next MediaPlayer
MediaPlayer currentPlayer = NextMediaPlayer;
NextMediaPlayer = SetupNextMediaPlayer();
currentPlayer.SetNextMediaPlayer(NextMediaPlayer);
}
MediaPlayer SetupNextMediaPlayer()
{
MediaPlayer mp = new MediaPlayer();
//When the video start playing, let's get ready for next one
string sourceURL = VideoListQueue.Dequeue();
VideoListQueue.Enqueue(sourceURL);
string filePath = sourceURL;
mp = new MediaPlayer();
mp.Info += VideoView_CurrentVideoView_Info;
mp.Completion += VideoView_CurrentVideoView_Completion;
mp.Prepared += VideoView_CurrentVideoView_Prepared;
mp.SetDataSource(filePath);
mp.Prepare();
//Fire back the created MediaPlayer object to the caller
return mp;
}
void VideoView_CurrentVideoView_Info(object sender, MediaPlayer.InfoEventArgs e)
{
Console.WriteLine("What = " + e.What);
switch (e.What)
{
case MediaInfo.VideoRenderingStart:
{
//This is only happening on video first started
NextMediaPlayer = SetupNextMediaPlayer();
e.Mp.SetNextMediaPlayer(NextMediaPlayer);
break;
}
}
}
void VideoView_CurrentVideoView_Error(object sender, MediaPlayer.ErrorEventArgs e)
{
e.Handled = true;
}
At this moment, the media player will begin playing the 2nd video once the first one is done. However, the 2nd video only have sound and no video showing.
Anyone know what I did wrong? I have a feeling that it has to do something with the MediaPlayer not attached to the SurfaceView. However, I created the view using VideoView, how can I get the Surface from VideoView?

Regarding playing 2nd video only with sound: try to implement OnCompletionListener listener for each MediaPlayer with this:
mediaPlayer.setOnCompletionListener(new OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mp) {
mp.setDisplay(null); //for current mediaPlayer
nextMediaPlayer.setDisplay(getHolder()); //for next video
}
});
I can't say that is is gapless, but somehow it works. To archive this I don't use standard VideoView, but custom View, that extends from SurfaceView.

what if you prepare and play the next mediaplayer on .Completion event? have you try it? although it may have a small delay

After many years of testing, this problem does not happen on all hardware. For example, I run the same APK on Nexus7, it appears to be seamless and everything is working. In contrast, if I run it on Amlogic media player board, it will render the above-described problem.
I decided to close this post off with a conclusion that it is something to do with the hardware. I know someone overcome this limitation by run everything in OpenGL, but that is completely a separate beast to deal with.
Conclusion
If you are having a similar problem as described above, there's nothing much you can do as it is heavily dependent on the hardware.

Related

creating delay while playing multiple Audio files simultaneously using MediaPlayer

Outrageous number of similar questions exist here, sadly none did help me.
I am trying to play 3 Audio files simultaneously, one is .wav , the other is .3gp and the other is .mp3 . Since the size exceeds more than 1MB , I cannot use Android SoundPool here. So far, everything works well without any error. Here is my code :
MediaPlayer mp = new MediaPlayer();
MediaPlayer songPlayer = new MediaPlayer();
MediaPlayer voicePlayer = new MediaPlayer();
private String song,voice,text;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_time__date);
String temp;
SharedPreferences preferences1 = getSharedPreferences("musicList", MODE_PRIVATE);
song = preferences1.getString("MUSICONE", "");
SharedPreferences preferences2 = getSharedPreferences("recordList",MODE_PRIVATE);
temp = preferences2.getString("VOICEONE","");
voice = Environment.getExternalStorageDirectory()+"/myAppCache2/"+temp;
SharedPreferences preferences3 = getSharedPreferences("TextList",MODE_PRIVATE);
temp=preferences3.getString("ALARMONE","");
text=Environment.getExternalStorageDirectory()+"/myAppCache/"+temp;
try {
mp.setDataSource(text);
mp.prepare();
mp.setLooping(true);
songPlayer.setDataSource(song);
songPlayer.prepare();
songPlayer.setLooping(true);
voicePlayer.setDataSource(voice);
voicePlayer.prepare();
voicePlayer.setLooping(true);
}
catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
mp.start();
songPlayer.start();
voicePlayer.start();
}
Now, my query is how do I add a delay to one ore more Audio files, Say If I want to add a delay of 5000 for voicePlayer before it loops again?
Now, my query is how do I add a delay to one ore more Audio files, Say If I want to add a delay of 5000 for voicePlayer before it loops again?
You could remove setLooping and use an OnCompletionListener instead. When you get the onCompletion callback, use postDelayed to post a Runnable that starts the player again.

play audio only when button is pressed otherwise not in android

I want to make an entertainment app which will produce sound only when the play button is pressed, but it is not working properly .
My code is
final MediaPlayer mp = new MediaPlayer();
play.setOnClickListener(new OnClickListener() {
public void onClick(View arg0) {
try{
AssetFileDescriptor descriptor = getAssets().openFd("3dpolicesi_mAYkXAbm.mp3");
long start = descriptor.getStartOffset();
long end = descriptor.getLength();
mp.setDataSource(descriptor.getFileDescriptor(), start, end);
mp.prepare();
mp.start();
mp.stop();
}catch(Exception e){e.printStackTrace();}
}
} );
It is because mp.stop() is called directly after mp.start(). It won't get a chance to play.
First delete this 3 line of your code:
mp.prepare();
mp.start();
mp.stop();
Write this outside of onClick:
int x = 0;
Then write this inside of onClick:
if(x == 0){
mp.start();
x = 1;
}else if(x == 1){
mp.stop();
x = 0;
}
At first time x is 0 so when you click on button player will be start and x will be replace with 1. next time when you click on button x is 1 so player will be stop.I hope it help.

Strange flow for seekBar on Android MediaPlayer

I use MediaPlayer for playing a single MP3 song from network. Data source is the file downloaded from network. This files comes in chuncks.
Let's assume we have following playing state.
Song duration: 4:00
Current chunck loaded in player: 1:00
Let's say I want to skip some part of a song and seek forward. I do it with MediaPlayer.seekTo() method. If I seek to position (1:40) it is not performed correctly, seek bar goes at start position (00:00) and the playerr stops.
public static void seekTo(int progress) {
try {
MediaPlayer mp = new MediaPlayer();
mp.setDataSource(sFilePath);
mp.prepare();
int offset = (progress * mp.getDuration()) / 100;
if (sCompleted)
return;
sLastSeek = offset;
if (offset > sMediaPlayer.getDuration()) {
sMediaPlayer.reset();
sMediaPlayer.setDataSource(sFilePath);
sMediaPlayer.prepareAsync();
} else {
sMediaPlayer.seekTo(offset);
}
} catch (Exception ex) {
ex.printStackTrace();
}
}

android-midi-lib delay before notes

I'm trying to generate midi file and play it on Android. I found android-midi-lib, but there are almost no any documentation about this library. I tried to run example from this lib. It works. But there is delay about 6 seconds before track from my notes start playing. I don't know anything about notes and midi format. Everything is new for me.
Here is my code:
public class MyActivity extends Activity {
private MediaPlayer player = new MediaPlayer();
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
MidiTrack tempoTrack = new MidiTrack();
MidiTrack noteTrack = new MidiTrack();
// 2. Add events to the tracks
// 2a. Track 0 is typically the tempo map
Tempo t = new Tempo();
t.setBpm(228);
tempoTrack.insertEvent(t);
// 2b. Track 1 will have some notes in it
for(int i = 0; i < 128; i++) {
int channel = 0, pitch = i, velocity = 100;
NoteOn on = new NoteOn(i*480, channel, pitch, velocity);
NoteOff off = new NoteOff(i*480 + 120, channel, pitch, 0);
noteTrack.insertEvent(on);
noteTrack.insertEvent(off);
}
// It's best not to manually insert EndOfTrack events; MidiTrack will
// call closeTrack() on itself before writing itself to a file
// 3. Create a MidiFile with the tracks we created
ArrayList<MidiTrack> tracks = new ArrayList<MidiTrack>();
tracks.add(tempoTrack);
tracks.add(noteTrack);
MidiFile midi = new MidiFile(MidiFile.DEFAULT_RESOLUTION, tracks);
// 4. Write the MIDI data to a file
File output = new File("/sdcard/example.mid");
try {
midi.writeToFile(output);
} catch(IOException e) {
Log.e(getClass().toString(), e.getMessage(), e);
}
try {
player.setDataSource(output.getAbsolutePath());
player.prepare();
} catch (Exception e) {
Log.e(getClass().toString(), e.getMessage(), e);
}
player.start();
}
#Override
protected void onDestroy() {
player.stop();
player.release();
super.onDestroy();
}
}
I figured out that this delay depends on first param in NoteOn constructor (maybe NoteOff too). I don't understand what is 480 number is. I tried to change this number, and than less this number than shorter delay before track, BUT whole track is shorter to.
Seems like time between notes with 480 value is fine for me, but I don't need a delay before them.
Help me please!
Ok, I figured out what is the problem.
According to this url http://www.phys.unsw.edu.au/jw/notes.html MIDI values for piano for example starts from 21. So if I start cycle from 0, then first 20 values won't play anything.
Now about delay.
The cycle should look like this:
delay = 0;
duration = 480; // ms
for (int i = 21; i < 108; ++i) {
noteTrack.insertNote(chanel, i, velocity, delay, duration);
delay += duration;
}
Delay means at what time note should be played. So if we want to play all notes one by one, we need to set delay as sum of all previous notes duration.

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