I have a simple activity which contains one instance of a VideoView and a reference to its' MediaPlayer. My goal was to use the setNextMediaPlayer() api from the MediaPlayer object in order to minimize the switching time between 2 videos.
In the below code, the 1st video plays well. When the 1st video completes, the 2nd video's audio begins to play in the background, but only the last frame of the 1st video is shown.
Do you know what the problem is? Why isn't the 2nd video's video displaying?
private VideoView player1;
private MediaPlayer mediaPlayer1;
public static final String URL_1 = "https://example.com/video1.mp4";
public static final String URL_2 = "https://example.com/video2.mp4";
public static final String TAG = "PrebufferingActivity";
public boolean FIRST_TIME = true;
public int count = 0;
#SuppressLint("NewApi")
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_prebuffering);
player1 = (VideoView) findViewById(R.id.videoPlayer1);
player1.setOnPreparedListener(new OnPreparedListener(){
#Override
public void onPrepared(MediaPlayer mp) {
mediaPlayer1 = mp;
if(FIRST_TIME == true)
{
mediaPlayer1.start();
player1.requestFocus();
FIRST_TIME = false;
}
}
});
player1.setOnInfoListener(new OnInfoListener(){
#Override
public boolean onInfo(MediaPlayer mp, int what, int extra)
{
if(what == MediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START)
{
count++;
if(count % 2 != 0)
{
Log.d(TAG, "Odd count (" + count + ") Prebuffering URL_2");
MediaPlayer myMediaPlayer = MediaPlayer.create(PrebufferingActivity.this, Uri.parse(URL_2));
mp.setNextMediaPlayer(myMediaPlayer);
}
else
{
Log.d(TAG, "Even count (" + count + ") Prebuffering URL_1");
MediaPlayer myMediaPlayer = MediaPlayer.create(PrebufferingActivity.this, Uri.parse(URL_1));
mp.setNextMediaPlayer(myMediaPlayer);
}
}
return false;
}
});
player1.setOnCompletionListener(new OnCompletionListener(){
#Override
public void onCompletion(MediaPlayer mp) {
}
});
// Player 1
player1.setMediaController(new MediaController(this));
player1.setVideoURI(Uri.parse(URL_1));
}
In your example you create new MediaPlayer, but VideoView knows nothing about and can't control it. It wouldn't work properly.
If you need some specific functions probably you need to build your analog of VideoView, that would use MediaPlayer.
Regarding playing 2nd video by using setNextMediaPlayer() you can find some hints here:
https://stackoverflow.com/a/28465846/755313
Related
In my Android application I want to play sequentially some sound that was divided to parts before. Each part is a .wave file with 2-3 seconds length.
I do perform that successfully, but I have a noticeable delay between those parts.
My code looks now like that -
localMediaPlayer = new MediaPlayer[3];
localMediaPlayer[0] = MediaPlayer.create(this, R.raw.sound_1);
localMediaPlayer[1] = MediaPlayer.create(this, R.raw.sound_2);
localMediaPlayer[2] = MediaPlayer.create(this, R.raw.sound_3);
public void onClick_localBtn(View v){
Toast.makeText(this, "Play Local Sound", Toast.LENGTH_LONG).show();
localMediaPlayer[0].start();
localMediaPlayer[0].setOnCompletionListener(new MediaPlayer.OnCompletionListener(){
public void onCompletion(MediaPlayer mp) {
localMediaPlayer[1].start();
}
});
localMediaPlayer[1].setOnCompletionListener(new MediaPlayer.OnCompletionListener(){
public void onCompletion(MediaPlayer mp) {
localMediaPlayer[2].start();
}
});
}
How can I improve my code that those parts will play smooth and with no delay, like if it were a 1 file sound?
Thanks.
When you start playing your (N)th media player, call prepareAsync() on your (N+1)th media player:
localMediaPlayer[0].start();
localMediaPlayer[1].prepareAsync();
localMediaPlayer[0].setOnCompletionListener(new MediaPlayer.OnCompletionListener(){
public void onCompletion(MediaPlayer mp) {
localMediaPlayer[1].start();
localMediaPlayer[2].prepareAsync();
}
});
localMediaPlayer[1].setOnCompletionListener(new MediaPlayer.OnCompletionListener(){
public void onCompletion(MediaPlayer mp) {
localMediaPlayer[2].start();
}
});
Edit
Based on your update and your comment, perhaps a better way would to be time the playing of the next media player according to the duration of the previous player minus some delta:
private static final int DURATION_DELTA = 1000;
private Handler mHandler = new Handler();
public void playMediaPlayer(final int index) {
if (index >= localMediaPlayer.length || localMediaPlayer[index] == null)
return;
localMediaPlayer[index].start();
final int duration = localMediaPlayer[index].getDuration();
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
playMediaPlayer(index + 1);
}
}, duration - DURATION_DELTA);
}
This code will basically play a media player, then schedule playing the next media player approx 1 second before the next media player ends. It's a bit "hacky", but you can play around with it (the value for DURATION_DELTA) until you get the best results.
I am using Android's MediaPlayer to loop audio files. I set the media player looping with
mMediaPlayer.setLooping(true);
After several repetitions, the loop starts earlier than it should do i.e. if I play the same loop on a computer, with a metronome running independently at the same BPM as the loop, Android's Media player and metronome stay synced for a few bars, but after a couple of loop, the loop played with the app starts too early.
I've read about other peoples complaining about this problem.
I reproduce this problem both on Android 4.4 and Android 6
Here is a minimal Android project for reproducing the problem:
https://drive.google.com/open?id=0B9FRLIzBQgR1WWdMU29ZcHdsMXc
In my project, I had the same issue and I found he solution in SO somewhere, I don't remember exactly where. But here what I've done:
You can try using a handler to set seek to beginning x milliseconds before the end of file
loopTimer = new Timer();
loopTask = new TimerTask() {
#Override public void run() {
mMediaPlayer.seekTo(0);
}
};
long waitingTime = mMediaPlayer.getDuration()-mLoopingPreview;
loopTimer.schedule(loopTask, waitingTime, waitingTime);
Now just set mLoopingPreview to some reasonable time value. I'm using 80ms.
I also faced this problem...in Mediaplayer
There is couple of solution:
1.use ogg format sound file (https://en.wikipedia.org/wiki/.ogg)
2.Can use soundpool it has a looping and caching facility for media play (https://developer.android.com/reference/android/media/SoundPool.html)
3.Also you can use the following class: it initialize a new mediaplayer as soon as the first one finishes..so the delay is less more obvious
public class LoopMediaPlayer {
public static final String TAG = LoopMediaPlayer.class.getSimpleName();
private Context mContext = null;
private int mResId = 0;
private int mCounter = 1;
private MediaPlayer mCurrentPlayer = null;
private MediaPlayer mNextPlayer = null;
public static LoopMediaPlayer create(Context context, int resId) {
return new LoopMediaPlayer(context, resId);
}
private LoopMediaPlayer(Context context, int resId) {
mContext = context;
mResId = resId;
mCurrentPlayer = MediaPlayer.create(mContext, mResId);
mCurrentPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mediaPlayer) {
mCurrentPlayer.start();
}
});
createNextMediaPlayer();
}
private void createNextMediaPlayer() {
mNextPlayer = MediaPlayer.create(mContext, mResId);
mCurrentPlayer.setNextMediaPlayer(mNextPlayer);
mCurrentPlayer.setOnCompletionListener(onCompletionListener);
}
private MediaPlayer.OnCompletionListener onCompletionListener = new MediaPlayer.OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mediaPlayer) {
mediaPlayer.release();
mCurrentPlayer = mNextPlayer;
createNextMediaPlayer();
Log.d(TAG, String.format("Loop #%d", ++mCounter));
}
};
}
to use it just call
LoopMediaPlayer.create(context, R.raw.sound_file_name);
instead of mp.setLooping(true);
What im trying to achieve is to play multiple videos stored on raw folder to be played on loop and sequentially one after the other?
I can play only one in loop in videoview but cant access other ones.
Thanks in advance.
Here is my videoview.
private VideoView myVideo1;
String path = "http://192.168.0.22/output/files/video/";
Uri uri=Uri.parse(path);
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getWindow().setFormat(PixelFormat.TRANSLUCENT);
setContentView(R.layout.activity_main);
myVideo1=(VideoView)findViewById(R.id.myvideoview);
myVideo1.setVideoURI(uri);
myVideo1.start();
myVideo1.requestFocus();
myVideo1.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
mp.setLooping(true);
}
});
}
To play multiple videos located in raw, try the following approach:
(NOTE: take care of the index and your video files naming. This example assumes your videos are named video1, video2..... videoX)
private final int COUNT = 3;
private int index = 1;
private VideoView myVideo1;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getWindow().setFormat(PixelFormat.TRANSLUCENT);
setContentView(R.layout.activity_main);
myVideo1 = (VideoView) findViewById(R.id.myvideoview);
myVideo1.requestFocus();
myVideo1.setVideoURI(getPath(index));
index++;
myVideo1.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
myVideo1.start();
}
});
myVideo1.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mediaPlayer) {
//videos count +1 since we started with 1
if (index == COUNT + 1) index = 1;
myVideo1.setVideoURI(getPath(index));
index++;
}
});
}
private Uri getPath(int id) {
return Uri.parse("android.resource://" + getPackageName() + "/raw/video" + id);
}
Getting resources from raw explained: android.resource:// is a constant part of the path, getPackageName() points to your application, /raw/ tells the system where to look for the file, video is the constant naming prefix of your files and the id is a dynamic suffix of your file names.
VideoView uses the MediaPlayer for playing videos, here's an overview of its states (taken from the official docs) for better understanding:
i am trying to develop a android app for playing a video from raw folder. am using custom control options. the getduration() returns -1 so i cant set max value to my seekbar. here is my code
private MediaController mediaController;
private VideoView videoView;
public TextView duration;
private int timeElapsed=0,finalTime=0;
private int forwardTime=2000, backwardTime=2000;
private SeekBar seekBar;
private Handler durationHandler = new Handler();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initialize();
}
public void initialize()
{
videoView =(VideoView)findViewById(R.id.videoView1);
mediaController= new MediaController(this);
mediaController.setAnchorView(videoView);
String
uri1="android.resource://"+getPackageName()+"/"+R.raw.materialdesign;
Uri uri = Uri.parse(uri1);
videoView.setMediaController(null);
videoView.setVideoPath("android.resource://" + getPackageName() + "/" +
R.raw.materialdesign);
Log.e("finalTime", "" + finalTime);
finalTime = videoView.getDuration();
Log.e("finalTime", ""+finalTime);
Log.e("finalTime", ""+videoView.getDuration());
duration = (TextView) findViewById(R.id.songDuration);
seekBar = (SeekBar) findViewById(R.id.seekBar);
seekBar.setMax(finalTime);
seekBar.setClickable(false);
videoView.requestFocus();
}
In case you haven't found a solution, VideoView.getDuration() will return -1 if the video is not in playback state. The video is not in playback state until it has been prepared. So calling VideoView.getDuration() directly after setting the URI does not guarantee that the video has been prepared.
I found this by looking at the source of VideoView:
#Override
public int getDuration() {
if (isInPlaybackState()) {
return mMediaPlayer.getDuration();
}
return -1;
}
The solution is to set an OnPreparedListener to your VideoView, and obtain the duration once the video is prepared. You can then use VideoView.getDuration() or MediaPlayer.getDuration(), which are nearly identical.
Solution:
videoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
int duration = mp.getDuration();
int videoDuration = videoView.getDuration();
Log.d(TAG, String.format("onPrepared: duration=%d, videoDuration=%d", duration,
videoDuration);
}
seekBar.setMax(videoDuration);
});
i am displaying images and video in imageview and videoview but the issue is when video is
playing onpreparedlistener called but when video finish oncompletion listener not called
when videoview complete i increment the i for next video or images
also it gives me error in logcat like this but video is playing
10-29 20:12:47.770: E/MediaPlayer(3975): error (1, -2147483648)
private void nextVideo(String path){
mImageview.setVisibility(View.GONE);
if(mVideoview.getVisibility()==View.GONE){
mVideoview.setVisibility(View.VISIBLE);
}
controller = new MediaController(HomeActivityNewViewPager.this);
mVideoview.setVideoURI(Uri.parse(path));
mVideoview.setMediaController(null);
controller.setMediaPlayer(mVideoview);
mVideoview.setOnPreparedListener(new OnPreparedListener() {
public void onPrepared(MediaPlayer mp) {
mVideoview.start();
long duration = mVideoview.getDuration();
second=duration;
//handler.removeCallbacks(runnable);
//handler.postDelayed(runnable,second);
}
});
mVideoview.setOnCompletionListener(new OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mp) {
Log.v("video view completed","---"+i);
mp.reset();
if(automode){
if(i==myplaylistlocal.size() || i>myplaylistlocal.size())
{
String checkcount=spreferences.getString("roundcount", "");
Log.v("roundcount==Before Integer.parseInt","---->"+roundcount);
if(roundcount>=Integer.parseInt(checkcount))
{
roundcount=0;
Log.v("roundcount==After Integer.parseInt","---->"+roundcount);
updateplaylist();
}
i=0;
indexplus();
imagesautomode();
i++;
}
else if(i==myplaylistlocal.size()-1)
{
imagesautomode();
i++;
}
else{
imagesautomode();
}
}
else{
i++;
images();
}
}
});
mVideoview.setOnErrorListener(new OnErrorListener() {
#Override
public boolean onError(MediaPlayer mp, int what, int extra) {
Log.v("Error in video playing","----->"+i);
return true;
}
});
}
Either way, the error referenced above is MEDIA_ERROR_UNKNOWN. If this video was made for this app, I would make sure that it is properly encoded for Android. Also make sure that is clearly defines its endpoint.
http://developer.android.com/reference/android/media/MediaPlayer.html#MEDIA_ERROR_UNKNOWN
this is a work around but could possbly work in your situation:
#Override
public boolean onError(MediaPlayer mp, int what, int extra) {
if(what == MediaPlayer.MEDIA_ERROR_UNKNOWN)
//ERROR UNKNOWN - COULD BE IMPROPERLY FORMATTED VIDEO {
//MOVE ON TO NEXT VIDEO
//DO LOGGING
}
}