Basically, we want to build a paged horizontal scroll view with one video player on each page; Video should auto-play and auto-pause/stop when page focus is changed. So I decided to use a ViewPager to show video Fragments. Each fragment has its own SurfaceView and MediaPlayer for video playing. The goal is to auto-play video when initial page loads and when user swipes to select a new page.
I am facing a few issues:
- I couldn't get callback from ViewPager when initial page is loaded. I’ve try implementing ViewPager.OnPageChangeListener in order to pause video on previous page and attempt to play video on the newly selected page. However, when page with Index 0 as the initial page is loaded, “onPageSelected“ is not called. Does anyone know there is another way to get call back of the initial page loads?
Here's my implementation of ViewPager.OnPageChangeListener
@Override
protected void onCreate(Bundle savedInstanceState) {
…
mViewPager.setAdapter(mVideoFragmentPagerAdapter);
mViewPager.setOnPageChangeListener(this);
mViewPager.setCurrentItem(mSelectedClipIndex);
…
}
....
/* ViewPager.OnPageChangeListener */
private int mCurrentPagerIndex = 0;
@Override
public void onPageScrolled(int i, float v, int i2) {
}
@Override
public void onPageSelected(int i) {
if (mCurrentPagerIndex != i) {
VideoFragment currentPage = (VideoFragment)mVideoFragmentPagerAdapter.getFragment(mCurrentPagerIndex);
if (currentPage != null) {
currentPage.pause();
}
}
VideoFragment newPage = (VideoFragment) mVideoFragmentPagerAdapter.getFragment(i);
if (newPage != null) {
newPage.playVideo();
}
mCurrentPagerIndex = i;
}
@Override
public void onPageScrollStateChanged(int state) {
}
...
/* part of the adapter implementation */
....
private Map<Integer, Fragment> mPageReferenceMap = new HashMap<Integer, Fragment>();
public Fragment getItem(int i) {
VideoFragment fragment = VideoFragment.newInstance(i, video);
mPageReferenceMap.put(i, fragment);
return fragment;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
mPageReferenceMap.remove(position);
super.destroyItem(container, position, object);
}
public Fragment getFragment(int key) {
return mPageReferenceMap.get(key);
}
2. UI freezes for seconds during the page transition if I attempt to swipe through quickly, as in you literally see two pages on the screen side by side. Sometimes I get application not responding error and see various errors and warnings in Logcat output but it doesn’t happen all the time:
I/Choreographer﹕ Skipped 169 frames! The application may be doing too much work on its main thread.
…
W/System.err﹕ java.lang.IllegalArgumentException: The surface has been released
W/System.err﹕ at android.media.MediaPlayer._setVideoSurface(Native Method)
W/System.err﹕ at android.media.MediaPlayer.setDisplay(MediaPlayer.java:688)
W/System.err﹕ at com.fbwmedia.AFV.fragments.VideoFragment.playVideo(VideoFragment.java:232)
W/System.err﹕ at com.fbwmedia.AFV.activities.VideosActivity.onPageSelected(VideosActivity.java:145)
W/System.err﹕ at android.support.v4.view.ViewPager.scrollToItem(ViewPager.java:572)
….
E/MediaPlayer﹕ Attempt to call getDuration without a valid mediaplayer
E/MediaPlayer﹕ error (-38, 0)
implementation of playVideo() on Fragment:
public void playVideo() {
mMediaController.setEnabled(true);
mMediaController.setMediaPlayer(this);
mMediaController.setAnchorView(this.getView().findViewById(R.id.layout_video_player));
AudioManager am = (AudioManager) this.getActivity().getSystemService(Context.AUDIO_SERVICE);
am.requestAudioFocus(this, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
try {
if (mMediaPlayer == null) {
mMediaPlayer = new MediaPlayer();
} else {
if (mMediaPlayer.isPlaying()) {
mMediaPlayer.stop();
}
mMediaPlayer.reset();
}
mMediaPlayer.setDataSource(mPath);
mMediaPlayer.setDisplay(mHolder);
mMediaPlayer.setScreenOnWhilePlaying(true);
mMediaPlayer.setOnErrorListener(this);
mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mMediaPlayer.setOnPreparedListener(this);
mMediaPlayer.prepareAsync();
mMediaPlayer.setOnCompletionListener(this);
wasPlayStarted = true;
isPrepared = false;
} catch (Exception e) {
e.printStackTrace();
}
}
public void onPrepared(MediaPlayer mediaplayer) {
mWidth = mediaplayer.getVideoWidth();
mHeight = mediaplayer.getVideoHeight();
if (mWidth != 0 && mHeight != 0 && this.getView() != null) {
setVideoProgressContentVisibility(View.GONE);
mHolder.setFixedSize(mWidth, mHeight);
mMediaPlayer.start();
mMediaController.show();
}
}
public void pause() {
if (mMediaPlayer != null && isPlaying()) {
mMediaPlayer.pause();
}
}
I'm suspecting that the process of preparing video player is blocking the UI thread causing the application not responding. Though I really ran out of ideas how to fix it and why it occasionally throwing the Surface being released error but not every time?