/*
* Copyright 2013 wada811<at.wada811@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package at.wada811.app.fragment;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.res.AssetManager;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnBufferingUpdateListener;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaPlayer.OnErrorListener;
import android.media.MediaPlayer.OnInfoListener;
import android.media.MediaPlayer.OnPreparedListener;
import android.media.MediaPlayer.OnVideoSizeChangedListener;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import at.wada811.android.library.R;
import at.wada811.utils.DisplayUtils;
import at.wada811.utils.LogUtils;
import java.io.FileDescriptor;
import java.io.IOException;
import java.util.Map;
public class VideoFragment extends Fragment implements SurfaceHolder.Callback, OnVideoSizeChangedListener, OnBufferingUpdateListener, OnPreparedListener, OnCompletionListener {
public static final String TAG = VideoFragment.class.getSimpleName();
private VideoCallback mCallback;
public SurfaceView mSurfaceView;
public SurfaceHolder mHolder;
public MediaPlayer mMediaPlayer;
public static final String KEY_RES_ID = "KEY_RES_ID";
public static final String KEY_FILE_PATH = "KEY_FILE_PATH";
private int mDisplayWidth = 0;
private int mDisplayHeight = 0;
private int mVideoWidth;
private int mVideoHeight;
private boolean mIsVideoSizeKnown = false;
private boolean mIsVideoReadyToBePlayed = false;
private boolean mIsVideoAutoPlay = false;
public static interface VideoCallbackProvider {
public VideoCallback getVideoCallback();
}
public static interface VideoCallback {
public void onActivityCreated(VideoFragment videoFragment);
public void onPrepared(VideoFragment videoFragment);
public void onCompletion(VideoFragment videoFragment);
public void surfaceDestroyed(VideoFragment videoFragment);
}
public static VideoFragment newInstance(int videoResId){
VideoFragment fragment = new VideoFragment();
Bundle args = new Bundle();
args.putInt(VideoFragment.KEY_RES_ID, videoResId);
fragment.setArguments(args);
return fragment;
}
public static VideoFragment newInstance(String videoFilePath){
VideoFragment fragment = new VideoFragment();
Bundle args = new Bundle();
args.putString(VideoFragment.KEY_FILE_PATH, videoFilePath);
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
LogUtils.i();
setRetainInstance(true);
}
@Override
public void onAttach(Activity activity){
super.onAttach(activity);
LogUtils.i();
if(activity instanceof VideoCallbackProvider == false){
throw new ClassCastException("activity must implements VideoCallbackPicker.");
}
VideoCallbackProvider picker = (VideoCallbackProvider)activity;
mCallback = picker.getVideoCallback();
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
LogUtils.i();
mSurfaceView = (SurfaceView)inflater.inflate(R.layout.fragment_video, null);
mHolder = mSurfaceView.getHolder();
mHolder.addCallback(this);
return mSurfaceView;
}
@Override
public void onActivityCreated(Bundle savedInstanceState){
super.onActivityCreated(savedInstanceState);
LogUtils.i();
initMediaPlayer();
}
private void initMediaPlayer(){
LogUtils.i();
if(getArguments().keySet().contains(KEY_RES_ID)){
mMediaPlayer = MediaPlayer.create(getActivity(), getArguments().getInt(KEY_RES_ID));
}else{
mMediaPlayer = new MediaPlayer();
}
mMediaPlayer.setOnVideoSizeChangedListener(this);
mMediaPlayer.setOnBufferingUpdateListener(this);
mMediaPlayer.setOnPreparedListener(this);
mMediaPlayer.setOnCompletionListener(this);
mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mMediaPlayer.setOnInfoListener(new OnInfoListener(){
@Override
public boolean onInfo(MediaPlayer mp, int what, int extra){
switch(what){
case MediaPlayer.MEDIA_INFO_BAD_INTERLEAVING:
LogUtils.i("MediaPlayer.MEDIA_INFO_BAD_INTERLEAVING");
break;
case MediaPlayer.MEDIA_INFO_BUFFERING_END:
LogUtils.i("MediaPlayer.MEDIA_INFO_BUFFERING_END");
break;
case MediaPlayer.MEDIA_INFO_BUFFERING_START:
LogUtils.i("MediaPlayer.MEDIA_INFO_BUFFERING_START");
break;
case MediaPlayer.MEDIA_INFO_METADATA_UPDATE:
LogUtils.i("MediaPlayer.MEDIA_INFO_METADATA_UPDATE");
break;
case MediaPlayer.MEDIA_INFO_NOT_SEEKABLE:
LogUtils.i("MediaPlayer.MEDIA_INFO_NOT_SEEKABLE");
break;
case MediaPlayer.MEDIA_INFO_SUBTITLE_TIMED_OUT:
LogUtils.i("MediaPlayer.MEDIA_INFO_SUBTITLE_TIMED_OUT");
break;
case MediaPlayer.MEDIA_INFO_UNSUPPORTED_SUBTITLE:
LogUtils.i("MediaPlayer.MEDIA_INFO_UNSUPPORTED_SUBTITLE");
break;
case MediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START:
LogUtils.i("MediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START");
break;
case MediaPlayer.MEDIA_INFO_VIDEO_TRACK_LAGGING:
LogUtils.i("MediaPlayer.MEDIA_INFO_VIDEO_TRACK_LAGGING");
break;
case MediaPlayer.MEDIA_INFO_UNKNOWN:
default:
LogUtils.i("MediaPlayer.MEDIA_INFO_UNKNOWN");
break;
}
LogUtils.d("extra: " + extra);
return false;
}
});
mMediaPlayer.setOnErrorListener(new OnErrorListener(){
@Override
public boolean onError(MediaPlayer mp, int what, int extra){
switch(what){
case MediaPlayer.MEDIA_ERROR_SERVER_DIED:
LogUtils.e("MediaPlayer.MEDIA_ERROR_SERVER_DIED");
break;
case MediaPlayer.MEDIA_ERROR_UNKNOWN:
default:
LogUtils.e("MediaPlayer.MEDIA_ERROR_UNKNOWN");
break;
}
switch(extra){
case MediaPlayer.MEDIA_ERROR_IO:
LogUtils.e("MediaPlayer.MEDIA_ERROR_IO");
break;
case MediaPlayer.MEDIA_ERROR_MALFORMED:
LogUtils.e("MediaPlayer.MEDIA_ERROR_MALFORMED");
break;
case MediaPlayer.MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK:
LogUtils.e("MediaPlayer.MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK");
break;
case MediaPlayer.MEDIA_ERROR_TIMED_OUT:
LogUtils.e("MediaPlayer.MEDIA_ERROR_TIMED_OUT");
break;
case MediaPlayer.MEDIA_ERROR_UNSUPPORTED:
LogUtils.e("MediaPlayer.MEDIA_ERROR_UNSUPPORTED");
break;
default:
LogUtils.e("extra: " + extra);
break;
}
return false;
}
});
mCallback.onActivityCreated(this);
}
@Override
public void onDetach(){
super.onDetach();
LogUtils.i();
stop();
reset();
release();
mHolder.removeCallback(this);
doCleanUp();
}
@Override
public void surfaceCreated(SurfaceHolder holder){
LogUtils.i();
mMediaPlayer.setDisplay(holder);
if(getArguments().keySet().contains(KEY_FILE_PATH)){
LogUtils.d(getArguments().getString(KEY_FILE_PATH));
try{
setDateSource(getArguments().getString(KEY_FILE_PATH));
prepare();
}catch(Exception e){
e.printStackTrace();
}
}
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height){
LogUtils.i("format: " + format);
LogUtils.i("width: " + width + ", height: " + height);
}
@Override
public void surfaceDestroyed(SurfaceHolder holder){
LogUtils.i();
mCallback.surfaceDestroyed(this);
}
@Override
public void onVideoSizeChanged(MediaPlayer mp, int width, int height){
LogUtils.i("width: " + width + ", height: " + height);
if(width == 0 || height == 0){
LogUtils.e("invalid video width(" + width + ") or height(" + height + ")");
return;
}
mIsVideoSizeKnown = true;
mVideoWidth = width;
mVideoHeight = height;
if(mIsVideoReadyToBePlayed && mIsVideoSizeKnown && mIsVideoAutoPlay){
start();
}
}
@Override
public void onBufferingUpdate(MediaPlayer mp, int percent){
LogUtils.i("percent: " + percent);
}
@Override
public void onPrepared(MediaPlayer mp){
LogUtils.i();
mCallback.onPrepared(this);
mIsVideoReadyToBePlayed = true;
if(mIsVideoReadyToBePlayed && mIsVideoSizeKnown && mIsVideoAutoPlay){
start();
}
}
@Override
public void onCompletion(MediaPlayer mp){
LogUtils.i();
mCallback.onCompletion(this);
}
public void setVideoAutoPlay(boolean isVideoAutoPlay){
mIsVideoAutoPlay = isVideoAutoPlay;
}
public void setDateSource(AssetManager assets, String fileName) throws IllegalArgumentException, IllegalStateException, IOException{
mMediaPlayer.setDataSource(assets.openFd(fileName).getFileDescriptor());
}
public void setDateSource(FileDescriptor fd) throws IllegalArgumentException, IllegalStateException, IOException{
mMediaPlayer.setDataSource(fd);
}
public void setDateSource(String path) throws IllegalArgumentException, SecurityException, IllegalStateException, IOException{
mMediaPlayer.setDataSource(path);
}
public void setDateSource(Uri uri) throws IllegalArgumentException, SecurityException, IllegalStateException, IOException{
mMediaPlayer.setDataSource(getActivity(), uri);
}
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
public void setDateSource(Uri uri, Map<String, String> headers) throws IllegalArgumentException, SecurityException, IllegalStateException, IOException{
mMediaPlayer.setDataSource(getActivity(), uri, headers);
}
public void setDateSource(FileDescriptor fd, long offset, long length) throws IllegalArgumentException, IllegalStateException, IOException{
mMediaPlayer.setDataSource(fd, offset, length);
}
public void setDisplayWidth(int width){
mDisplayWidth = width;
}
public void setDisplayHeight(int height){
mDisplayHeight = height;
}
public void prepare() throws IllegalStateException, IOException{
mMediaPlayer.prepare();
}
private void setSize(){
LogUtils.i("width: " + mVideoWidth + ", height: " + mVideoHeight);
int videoWidth = DisplayUtils.getWidth(getActivity());
int videoHeight = DisplayUtils.getHeight(getActivity());
if(mDisplayWidth == 0 && mDisplayHeight == 0){
videoHeight = mVideoHeight * videoWidth / mVideoWidth;
}else if(mDisplayWidth != 0 && mDisplayHeight == 0){
videoWidth = mDisplayWidth;
videoHeight = mVideoHeight * videoWidth / mVideoWidth;
}else if(mDisplayWidth == 0 && mDisplayHeight != 0){
videoHeight = mDisplayHeight;
videoWidth = mVideoWidth * videoHeight / mVideoHeight;
}else if(mDisplayWidth != 0 && mDisplayHeight != 0){
videoWidth = mDisplayWidth;
videoHeight = mDisplayHeight;
}
LogUtils.i("width: " + videoWidth + ", height: " + videoHeight);
mSurfaceView.setLayoutParams(new FrameLayout.LayoutParams(videoWidth, videoHeight));
mHolder.setFixedSize(videoWidth, videoHeight);
LogUtils.d("CurrentPosition: " + mMediaPlayer.getCurrentPosition());
}
public void seekTo(int msec){
mMediaPlayer.seekTo(msec);
}
public void start(){
setSize();
mMediaPlayer.seekTo(mMediaPlayer.getCurrentPosition());
mMediaPlayer.start();
}
public void pause(){
if(mMediaPlayer != null){
if(mMediaPlayer.isPlaying()){
mMediaPlayer.pause();
}
}
}
public void stop(){
if(mMediaPlayer != null){
pause();
mMediaPlayer.stop();
}
}
public void reset(){
if(mMediaPlayer != null){
mMediaPlayer.reset();
}
}
public void release(){
if(mMediaPlayer != null){
mMediaPlayer.release();
mMediaPlayer = null;
}
}
private void doCleanUp(){
mVideoWidth = 0;
mVideoHeight = 0;
mIsVideoReadyToBePlayed = false;
mIsVideoSizeKnown = false;
}
}