Playing an audio file with ExoPlayer a few seconds after calling .prepare() - android

I have a feature in my app that has to play one short audio file multiple times, and using the code below I'm preparing ExoPlayer to play the audio:
SimpleExoPlayer player;
private void readyExoPlayerRaw(int rawSound) {
player = ExoPlayerFactory.newSimpleInstance(this, new DefaultTrackSelector());
DataSpec dataSpec = new DataSpec(RawResourceDataSource.buildRawResourceUri(rawSound));
final RawResourceDataSource rawResourceDataSource = new RawResourceDataSource(this);
try {
rawResourceDataSource.open(dataSpec);
} catch (RawResourceDataSource.RawResourceDataSourceException e) {
e.printStackTrace();
}
MediaSource audioSource =
new ExtractorMediaSource.Factory(
new DefaultDataSourceFactory(this, "appName"))
.createMediaSource(rawResourceDataSource.getUri());
player.prepare(audioSource);
//player.setPlayWhenReady(true);
}
The problem is that the sound plays fine when I uncomment the last line (//player.setPlayWhenReady(true);), but since I'm playing the sound a few seconds after I run this method, this line of code wont work! I think because it waits for a callback from previous line (.prepare(...)) and when it's ready, it will then play. So I thought that maybe I should call start() or something like that on the player, but there's just no such method.
So I'm stuck with calling .prepare() each time I want to play the audio, but since it plays in very short intervals, calling prepare() causes an unsuitable delay.
So am I missing something? How can I play a prepared MediaSource without preparing it again?

I think you're on the right track!
setPlayWhenReady is how you pause and play the audio, but it'll only actually play when it's "ready" aka done preparing.
So you should only have to call your readyExoPlayerRaw once probably in onResume or onCreate. Then at the end of that method you can set playWhenReady(false) so that it doesn't start playing until you tell it to.
Then when you want to play the sound, do something like the following:
private void playSound() {
// Go to the beginning of the audio (in case it has played before)
player.seekTo(0);
// Tell it to play the sound
exoPlayer. setPlayWhenReady(true);
}
Let me know if that works.

Related

Mediaplayer with two audio tracks

I have a problem with mediaplayer.
int inlevel=0;
int ingame=1;
MediaPlayer[] music = new MediaPlayer[2];
music[inlevel] = MediaPlayer.create(ctx, R.raw.jazz);
music[ingame] = MediaPlayer.create(ctx, R.raw.funky);
music[inlevel].start();
When the game begins I call a function for fadeout the music inlevel
fadeout();
music[inlevel].pause();
music[ingame].setVolume(volume, volume);
music[ingame].start();**
When the game finishs:
music[ingame].pause();
music[ingame].seekTo(0);
music[inlevel].start();
I don't need the music in level restart from the begin. I need that only for the music ingame.
Everything works good. I have only a problem the second time the game starts. Never happens at the first time.
At the begin of music in game(**), there is a very short piece of the music inlevel. Maybe it is the buffer of Mediaplayer.
I tried with two istance of Mediaplayer, but no sound.
Any solution? Thanks.

Android - changing/replaying video in videoView

App I'm developing contains many short (1-2 sec) videos.
The videos are displayed in one activity. User can either replay video (possibly while video is beeing played) or change actual video.
Part of code changing video:
String videoPath = getVideoPath();
videoView.setVideoPath(videoPath);
videoView.start();
Those 3 lines already causes app to load new video and play it.
Problem starts after video is completed. From this point loading new video causes many problems (Like sometimes for half a movie only sound is played while screen is black blank). There are similar problems with replaying video (which I end up with calling 3 lanes from above).
It seems like android after completing movie releases resources or something like this (and that's why I am settings same path, when I want to replay video).
Ideally I would want video to simply pause and seekTo to beggining of movie after finished playing (but I cannot do this in OnCompletedListener, since it already changed state to stopped...).
Can I somehow achieve this? (By this I mean -> after completed video pauses and seekTo to beginning)
I already tried all combinations of pausing vidoes, suspending them, setting OnPreparedListener, setting OnCompletedListener.
Thx!
Try something like
mVideoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer arg0) {
mVideoView.start();
}
});
mVideoView.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
public void onCompletion(MediaPlayer mp) {
mp.reset();
mVideoView.setVideoPath(file.getAbsolutePath());
mVideoView.start();
}
});
stop the playback, change video path, start video
videoView.stopPlayback();
videoView.setVideoPath(newVideoPath);
videoView.start();
I refactored my code in a following way: Everytime I need new video I reload whole activity. It works most of times, but now instead of video blackness at the beginning of playing I sometimes get "Cannot open media file" error.
Has it something to do with android resource managment? I release mediaPlayer in onCompletionListener.
Has anyone had such problem with playing many videos from external storage?
You can do something like this:
videoView.setOnCompletionListener(MediaPlayer.OnCompletionListener { mp ->
mp.seekTo(0);//go to second 0
mp.start()// start again
})
I discover in this link:
https://developer.android.com/reference/android/media/MediaPlayer
Do this.
videoView.setOnPreparedListener { mp ->
mp.isLooping = true
)

Android plays sounds after delay

I have to play sounds on GUI events, like clicking buttons etc. For this purpose, I call the following native code from WebView:
MediaPlayer _SoundPlayer = new MediaPlayer();
private void playSound(String sound)
{
_SoundPlayer.reset();
try
{
AssetFileDescriptor afd = getAssets().openFd("sound/" + sound + ".mp3");
_SoundPlayer.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
_SoundPlayer.prepare();
_SoundPlayer.start();
}
catch (Exception e) { }
}
The problem is there is a delay ~500ms between an event and its sound. Can I optimize playing sound somehow, maybe, creating a dedicated MediaPlayer instances for every kind of sound?
Regards,
Use SoundPool for low-latency media playback, instead of MediaPlayer.
I see that this already has an accepted answer, but I would add that there is no complete solution at this time: Android currently has very large audio latency. Devs are still waiting for a good solution.
This issue refers to the NDK, but the issue is general:
http://code.google.com/p/android/issues/detail?id=3434

Android Media Player - Getting the number of times a video played in SetLooping

I am Playing the video in loop using set Looping(true) option and i will stop the media player after a particular event happened. It is working fine. But i want to know the number of times that my video got played in the loop.
MediaPlayer doesn't provide that information, you need to make it loop yourself and count how many times it has restarted. To do this, something in your app will need to extend OnCompletionListener and do something like
int count = 0;
public void onCompletion(MediaPlayer mediaPlayer) {
count++;
mediaplayer.seekTo(0);
mediaplayer.start();
}
And you need to set mediaPlayer.setLooping(false)

Android: MediaPlayer gapless or seamless Video Playing

I can play the videos fine back to back by implementing the OnCompletionListener to set the data source to a different file. No problems there. I call reset() and prepare() just fine.
What I haven't been able to figure out, is how to get rid of the 1-2 second gap screen flicker between the data source change and the new video starting. The gap shows a black screen, and I haven't found any way to get around it.
I've tried setting the background of the parent view to an image, but it manages to bypass that. Even if the SurfaceView is transparent (which it is by default.) I've also tried to have the multiple video files played at the same time, and switching mediaplayer's display when one ends and the other is supposed to start.
The last thing I tried, was to have a second view in the background that I show temporarily while the video is "preparing" and removing it when the video is ready to start. That also wasn't very seamless.
Is there any way to get rid of that gap. Running a video in a loop works wonderfully and does exactly what I want with the exception that it's looking through the same video instead of playing a different one that I pick.
main.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:background="#drawable/background"
android:layout_height="fill_parent">
<SurfaceView
android:id="#+id/surface"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_gravity="center">
</SurfaceView>
</FrameLayout>
Player.java
public class Player extends Activity implements
OnCompletionListener, MediaPlayer.OnPreparedListener, SurfaceHolder.Callback {
private MediaPlayer player;
private SurfaceView surface;
private SurfaceHolder holder;
public void onCreate(Bundle b) {
super.onCreate(b);
setContentView(R.layout.main);
surface = (SurfaceView)findViewById(R.id.surface);
holder = surface.getHolder();
holder.addCallback(this);
holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
public void onCompletion(MediaPlayer arg0) {
File clip = new File(Environment.getExternalStorageDirectory(),"file2.mp4");
playVideo(clip.getAbsolutePath());
}
public void onPrepared(MediaPlayer mediaplayer) {
holder.setFixedSize(player.getVideoWidth(), player.getVideoHeight());
player.start();
}
private void playVideo(String url) {
try {
File clip = new File(Environment.getExternalStorageDirectory(),"file1.mp4");
if (player == null) {
player = new MediaPlayer();
player.setScreenOnWhilePlaying(true);
}
else {
player.stop();
player.reset();
}
player.setDataSource(url);
player.setDisplay(holder);
player.setOnPreparedListener(this);
player.prepare();
player.setOnCompletionListener(this);
}
catch (Throwable t) {
Log.e("ERROR", "Exception Error", t);
}
}
I too have the same problem as outlined by below link
VideoView Disappears for a second when video is changed
But this issue wont occur if you try using Android 4.0+ (ICS). I started to port VideoView.java and MediaPlayer.java from 4.0 to my app , but thats seems complex and no luck till now. Basically it seems a bug in the native code of the older versions.
after too much wasted time trying to figure out how to play consecutive videos without the "gap", i'm leaning towards impossible. unless of course you're able to dig down to the native level and implement your own player, Android's media player simply doesn't support seamless playback as of the moment.
I've not done this with video playback on a MediaPlayer but I've done something similar with audio files when a stream gets interrupted because a user has switched from 3G to Wifi.
I think the delay that you're seeing is whilst the media player is buffering the input file.
So maybe you can define both players at the start? You should do define the datasource and prepare both players but only start one of them.
Then in your onCompletionListener you can flip them over instead of resetting the existing one and setting a new datasource.
player.release();
player = flipPlayer;
flipPlayer = null;
player.start();
Obviously you'd need to either use a different onPreparedListener for flipPlayer or take the player.start() out of the onPrepare. (Since you're calling it synchronously I wouldn't have thought this was an issue).
I don't think it's possible.
Reason: Mortplayer was also available for windows mobile and one of its strengths was that it supported gapless play. However it doesn't in the android version of the app, and the developer itself writes that the SDK does not allow it on xda:
http://forum.xda-developers.com/showpost.php?p=5364530&postcount=5
HTH, Daniele
Did you try to have ready (opened/prepared) 2 VideoView's, with one being visible, other invisible and stopped, and ass soon you get OnCompletionListener callback, make 2nd one visible, start it, and hide 1st one.
In meantime, as 2nd one plays, you can call open/prepare on 1st VideoView to open/prepare another file.
#user1263019 - were you able to port Android 4.0 MediaPlayer to your app? I'm facing the same issue and I'm looking for a nice solution. My case is having an image over the SurfaceView which should be hidden in order to show the video playing, but there is a gap between calling start() and the actual start of the video. The only solution so far is to start a Thread that checks if getCurrentPosition() is > 0.
On the topic - I think it is not possible to have gapless playback, though Winamp claim to have such abilities. A dirty hack is to prepare the second player several seconds before the end of the playback of the first player and then call start() of the second player 100-200ms before end of playback.
In your exoplayer implementation (or mediaplayer) use a handler to repeatedly post a runnable, while playing, that gets the current tracking time and passes it to a callback:
public interface MediaAction {
public void onTrackingChanged(long currentPosition);
}
public class ExoPlayerImplementation implements Exoplayer {
..
private MediaAction mediaActionCallback;
public void setMediaActionCallback(MediaAction ma) {
mediaActionCallback = ma;
}
public void releaseMediaAction() { mediaActionCallback = null; }
private Handler trackingCallback = new Handler(Looper.getMainLooper());
Runnable trackingTask;
private Runnable getTrackingTask() {
Runnable r = new Runnable {
#Override public void run() {
long currentPosition = getCurrentPosition();
if (mediaActionCallback != null) {
mediaActionCallback.onTrackingChanged(currentPosition);
}
// 60hz is 16 ms frame delay...
trackingCallback.postDelayed(getTrackingTask(), 15);
}
};
trackingTask = r;
return r;
}
public void play() {
// exoplayer start code
trackingCallback.post(getTrackingTask());
}
public void pause/stop() {
trackingCallback.removeCallbacks(trackingTask);
}
..
}
And now you can track when the current position actually has changed--if it has changed, then you can show your video output view / swap views / remove preview (ie, use MediaExtractor to grab initial frames, overlay as preview in an ImageView then hide once current position is increasing). You can make this code more complete by combining it with the state listener: then you know if you are buffering (check buffering position versus current position), actually playing, paused or stopped with proper callbacks (in MediaAction). You can apply this method to both MediaPlayer and ExoPlayer classes (since it is just an interface and handler).
Hope that helps!

Categories

Resources