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!
Related
My problem is similar to this one, but there was no correct answer, so I thought I give it a go.
In my application I've got two video players (TextureView + MediaPlayer- based on VideoView) playing from URL side by side on one screen. I got it working quite right. As I need then them to play simultaneously I synced their controllers, to when I start one of them the second starts too (the same with pausing, seeking, buffering, etc).
videoView1.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
public void onPrepared(MediaPlayer mp) {
mp.setVolume(0.0f, 0.0f);
firstVideoPrepared = true;
tryStart();
}
});
videoView2.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
public void onPrepared(MediaPlayer mp) {
secondVideoPrepared = true;
tryStart();
}
});
private void tryStart() {
// here is also buffered data level check
if (firstVideoPrepared && secondVideoPrepared) {
videoView1.start();
videoView2.start();
}
}
The problem is that despite pausing both videos for buffering, when I start() them they sometimes get out of sync. Let's say that there is the same video in both players- I need them to be sync almost ideally as I can see any difference. Is there any way to keep them synced so precisely?
I've read about libraries like ExoPlayer, FFMPEGMediaPlayer or MediaPalyer-Extended but none of them seems to have what I need.
Is it even possible to keep that accuracy of videos sync?
I am learning to code for Android and I have a problem with... maybe performance?
I want to play very short sound every second. I have created a CountDownTimer (with tick interval 20ms so very accurate) and putted there in onTick to play it. But the sound is played not precisely after one second and I can hear this - this is the problem...
fragment of my code:
private class ExerciseCountDownTimer extends CountDownTimer
{
...
public void onTick(long millisUntilFinished)
{
...
if(/*this is a next second not just a tick*/)
playSound(R.raw.quick_rest, true);
}
}
private void playSound(int resId, boolean releaseAfter)
{
if (currentMediaPlayerRes != resId || mediaPlayer == null)
{
if (mediaPlayer != null)
{
if (mediaPlayer.isPlaying())
mediaPlayer.stop();
mediaPlayer.reset();
mediaPlayer.release();
}
currentMediaPlayerRes = resId;
mediaPlayer = MediaPlayer.create(this, resId);
if (releaseAfter)
{
mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener()
{
#Override
public void onCompletion(MediaPlayer mp)
{
mediaPlayer.release();
mediaPlayer = null;
}
});
}
}
mediaPlayer.start();
}
Is it possible to have counter like this?
use this code to play a music one time
MediaPlayer AlarmMusic;
AlarmMusic = MediaPlayer.create(G.context, R.raw.music1);
AlarmMusic.setLooping(false);
AlarmMusic.start();
this worked for me !
Because of the lack of real time guarantees in Java/Android, using one thread to cue another probably will always have some inaccuracy.
I can think of two approaches that might work for you:
(1) make the sound file exactly 1 second long, e.g., 44100 frames at 44100 fps, if you are using the standard "CD-quality" format that Java supports. The sound file can probably be edited with Audacity to an exact frame length. Then, use a looping playback.
(2) Open a line for streaming audio. I can't recall exactly what the Android-supported command is for this, but I know it exists. With it, count elapsing frames while sending silence (bytes with 0 value). When you get to the 44100th frame, start feeding the PCM data that you wish to hear. When it is done, go back to feeding 0's until the next 44100 frame arrives. Never stop the line--keep it running in its own thread, probably with a high priority. It is generally okay to give audio a high priority (as long as you are doing nothing else on the line) as audio spends a vast majority of its time in a "blocked" state during which it is yielding to other lines.
A fellow on java-gaming.org made a metronome and got it working on Android. I bet if you search for "metronome" on that site, his thread will pop up and have some useful info. He basically used the second approach that I described above.
I researched a little bit, but couldn't find any solutions to this problem:
I would like to play a MediaPlayer and pause/stop it at a given time.. (ie: play from second 6 to second 17).
I know that I can set its starting point with seekTo() method, but can I pause/stop it from playing by setting an end point (of course, before reaching the file end limit)?
There are different ways you could do this, here's one:
int startFrom = 6000;
int endAt = 11000;
MediaPlayer mp;
Runnable stopPlayerTask = new Runnable(){
#Override
public void run() {
mp.pause();
}};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mp = MediaPlayer.create(this, R.raw.my_sound_file);
mp.seekTo(startFrom);
mp.start();
Handler handler = new Handler();
handler.postDelayed(stopPlayerTask, endAt);
}
The mediaplayer will start playing 6 seconds in and pause it 11 seconds after that (at second 17).
I think you can create Timer and call seekTo() directly from its task. Then call stop()/pause() inside of that Timer Task.
Maybe this post will be helpfull for you.
Or you can use handler for this task, like Ken Wolf shows you.
Best wishes.
You can use CountDownTimer
new CountDownTimer(30000, 1000) {
public void onTick(long millisUntilFinished) {
}
public void onFinish() {
mp.stop;
mp.relese();
}
}.start();
I know this already has an answer but here is an alternative for anyone wanting to do this efficiently for a video with controls.
I saw a different answer that involved constantly checking the position.
Note that this assumes that you do not need a lot of different portions of one video. Even in that case I suggest to follow the below.
If you only need to play a portion of the video and want it to end somewhere, then why not just use free video editing software and clip the end? You can still start it from anywhere using seekTo() but this way you don't have to waste resources checking for a certain position (video with controls).
If you do not have video player controls the accepted answer will work. But if you do have player controls then it would not since a user could pause and play the video.
There's a time difference between videoView.start() and video prepared to play depend on video format, those time difference is probably more than 10 frames.
so the best way to do is to start the timer inside OnPreparedListener to minimise the time difference, or even more to get the current playing duration and set a postDelayed timer at that point.
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
)
Such Problem: I have video file recorded with two sound channels. I tried to switch off left sound channel by this code:
MediaPlayer mp;
....
mp.setVolume(0.f, 1f);
... and on Tablet this work good (right volume channel sounds well). But then I tried it on googleTv which I connect to Samsung UE46ES6307U and this code did not work, sound swichs off.
Maybe it is bounds to Dolby Digital Plus / Dolby Pulse audio? Can I somehow programmatically discover how sound channels device has, and what volume in each chanels setuped?
Update:
On this forum http://www.googletvforum.org/forum/logitech-revue/375-audio-problems-logitech-revue.html in one of replies such message: "Logitech has not yet figured out how to pipe multichannel audio thru hdmi.you have to use the optical output. Which is ok."
"How are you constructing the MediaPlayer?"
Videoview vv;
...............
vv.setOnPreparedListener(new OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
mp.setVolume(0.f, 1f);
}
});
Update:
public class MainActivity extends Activity {
MediaPlayer mp = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (mp != null) {
mp.reset();
mp.release();
}
mp = MediaPlayer.create(this, R.raw.test);
mp.start();
}
public void onTurnOffLeft(View v){
mp.setVolume(0.f, 1.f);
}
public void onTurnOffRight(View v){
mp.setVolume(1.f, 0.f);
}
}
Method onTurnOffLeft switchs off all sound, and onTurnOffRight method has no effect.
Update2
I tried to play .ogg audio file codded with Vorbis codec - channels turns off well. But I tried to play video files codded with mp3, ac3, pcm, aac - and problem with turning off channels is still there... I need to turn off audio channels in video, but how to solve that problem, I do not know yet.
The MediaPlayer object is backed by different libraries across the devices (not the same between a tablet and a Google TV). How are you constructing the MediaPlayer?
One thing you may want to try is calling #reset() on the MediaPlayer right after it is constructed. By default when you use a "new" operator to construct a MediaPlayer instance it is in an IDLE state (at least on Google TV). By calling reset you allow your own OnErrorListener.onError() handler to be invoked. This will let you see if there is some underlying error that is not visible otherwise.
You may also want to look at AudioManager#setStreamVolume(int, int, int) which sets the volume of ALL streams of a particular type.
Edit 1:
Since you are just grabbing the VideoView from layout (I'm guessing since that code was omitted) after you setup the listener you should call reset on the video view.