您的位置:首页 > 移动开发 > Android开发

Android 视频播放器笔记之播放器实例代码

2014-10-10 12:22 465 查看
前面有文章讲到android 实现视频播放的三种方式,本文将讲述以下使用MediaPlayer + SurfaceView方法实现的思路以及相关代码。因为它自定义的优点,所以实现起来会比较麻烦。如果只想实现播放功能可以参考前文:/article/8149642.html

本例是使用MediaPlayer来控制媒体的播放,暂停,进度等,使用SufaceView显示视频内容。

一、效果展示



二、功能介绍及代码实现

可以播放、暂停、显示进度,全屏/退出全屏;需要两个自定义控件来实现功能。

1、播放视频部分:自定义一个控件继承SurfaceView(显示视频内容),实现MediaPlayControl(控制视频播放)。

SurfaceHolder是用来监控SurfaceView的变化的;当SurfaceView变化的时候会触发SurfaceHolder.Callback的实现类中的一些方法;

mediaPlayer.setDisplay(holder)是关键,也可是mediaPlayer.setDisplay(surface);此方法设置哪个surfaceView显示视频,如果没有设置会导致视频只能播放声音而无法显示图像。使用mediaPlayer播放音频的时候就无需设置这个。下图展示了如何使用mediaPlayer:
此图摘自:【Android笔记】Android
MediaPlayer的生命周期



上代码:

package com.dream.videoplayer_v4;

import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Bitmap;
import android.media.AudioManager;
import android.media.MediaMetadataRetriever;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnPreparedListener;
import android.net.Uri;
import android.util.AttributeSet;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceHolder.Callback;
import android.view.SurfaceView;
import android.widget.MediaController.MediaPlayerControl;

public class CutomerVideoView extends SurfaceView implements MediaPlayerControl {

private static final String TAG = CutomerVideoView.class.getSimpleName();
private MediaPlayer mediaPlayer;
private SurfaceHolder surfaceHolder;// monitor changes to the surface
private Callback surfaceCallBack = new Callback() {//receive the information about changes to the surface

@Override
public void surfaceDestroyed(SurfaceHolder holder) {
// TODO Auto-generated method stub
surfaceHolder = null;
destroyPlayer();
}

@Override
public void surfaceCreated(SurfaceHolder holder) {
// TODO Auto-generated method stub
try {
surfaceHolder = holder;
mediaPlayer = new MediaPlayer();
mediaPlayer.setDisplay(holder);

} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
// TODO Auto-generated method stub

}
};

private String uri;
private OnPreparedListener onPreparedListener;

public void setOnPreparedListener(OnPreparedListener onPreparedListener) {
this.onPreparedListener = onPreparedListener;
}

public CutomerVideoView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// TODO Auto-generated constructor stub
initView();
}

public CutomerVideoView(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
initView();
}

public CutomerVideoView(Context context) {
super(context);
// TODO Auto-generated constructor stub
initView();
}

@SuppressWarnings("deprecation")
private void initView() {
try {
getHolder().addCallback(surfaceCallBack);
getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

@Override
public boolean canPause() {
// TODO Auto-generated method stub
return false;
}

@Override
public boolean canSeekBackward() {
// TODO Auto-generated method stub
return false;
}

@Override
public boolean canSeekForward() {
// TODO Auto-generated method stub
return false;
}

@Override
public int getAudioSessionId() {
// TODO Auto-generated method stub
return 0;
}

@Override
public int getBufferPercentage() {
// TODO Auto-generated method stub
return 0;
}

@Override
public int getCurrentPosition() {
if (mediaPlayer != null)
return mediaPlayer.getCurrentPosition();

return 0;
}

@Override
public int getDuration() {
if (mediaPlayer != null)
return mediaPlayer.getDuration();

return 0;
}

@Override
public boolean isPlaying() {
if (mediaPlayer != null)
return mediaPlayer.isPlaying();

return false;
}

@Override
public void pause() {
if (mediaPlayer != null) {
mediaPlayer.pause();
}
}

@Override
public void seekTo(int pos) {
if (mediaPlayer != null) {
mediaPlayer.seekTo(pos);
}
}

@Override
public void start() {
if (mediaPlayer != null) {
mediaPlayer.start();
}
}

private void destroyPlayer() {
if (mediaPlayer != null) {
mediaPlayer.reset();
mediaPlayer.release();
mediaPlayer = null;
}
}

@SuppressLint("NewApi")
public Bitmap createVideoThumbnail(String filePath) {
Bitmap bm = null;
MediaMetadataRetriever mmr = new MediaMetadataRetriever();
mmr.setDataSource(filePath);
bm = mmr.getFrameAtTime();
return bm;
}

public void setVideoUri(String path) {
this.uri = path;
openVideo();
}

public void openVideo() {
try {
if(mediaPlayer==null){
mediaPlayer=new MediaPlayer();
}
if(uri == null){
uri=" ";
}
try {
Log.i(TAG,"uri is :"+uri);
mediaPlayer.setDataSource(getContext(), Uri.parse(uri));
} catch (Exception e) {
Log.i(TAG,"setDataSource fail");
e.printStackTrace();
}

mediaPlayer.prepareAsync();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setOnPreparedListener(onPreparedListener);

//			requestLayout();
//			invalidate();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

}


2、控制菜单栏(即有播放按钮、进度条、缩小按钮的那一栏)

1)进度条的更新

通过定义一个定时任务TimeTask来实现进度条的更新;

可以通过mediaPlayer.getCurrentPosition()来获取当前进度,通过mediaPlaer.getDuration()来获取总时长;

通过seekBar.seekTo();改变进度条进度。

值得注意的是,mediaPlaer.getDuration();方法只能在一定状态下使用才能得到正确的时长;

比如,Idle状态下调用getDuration()等方法是不正确的。通过new MediaPlayer()而进入Idle态不会报错,但也不会得到正确的时长,而通过reset()方法进入Idle态会导致mediaPlayer进入Error态;在Prepared态能得到正确的时长。
有兴趣的童鞋可以看一下MediaPlayer的状态图,可以避免开发中的很多错误:/article/8105927.html

2)进度条改变更新mediaPlayer进度

2.1)使用OnSeekBarChangeListener监听SeekBar进度的改变。

2.2)使用mediaPlayer.seekTo();改变视频播放进度;

上代码:

FullScreenActivity:

package com.dream.videoplayer_v4;

import java.util.Timer;
import java.util.TimerTask;

import android.app.Activity;
import android.content.pm.ActivityInfo;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnPreparedListener;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ImageView;
import android.widget.TextView;

public class FullScreenActivity extends Activity {

protected static final String TAG = FullScreenActivity.class.getSimpleName();
public CutomerVideoView videoView;
private CustomMediaController mediaController;
private ImageView iv;
private ImageView playBtn;
private TextView progressTv;
private int duration;

@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
setContentView(R.layout.full_screen_video_player);

initView();
}

private void initView() {
videoView = (CutomerVideoView) findViewById(R.id.videoview_fullscreen);
videoView.setOnPreparedListener(new OnPreparedListener() {

@Override
public void onPrepared(MediaPlayer mp) {
Log.i(TAG,"enter onPrepared");
mp.setLooping(false);

duration=mp.getDuration();

if(duration>0){
handler.sendEmptyMessage(1);
}

if(mediaController.isPlaying){
mp.start();
}
}
});
videoView.setVideoUri(MainActivity.URL);
mediaController = (CustomMediaController) findViewById(R.id.mediacontroller_fullscreen);
Timer timer=new Timer();
timer.schedule(timeTask, 0, 1000);

iv = (ImageView) findViewById(R.id.iv_fullscreen);
iv.setImageBitmap(videoView.createVideoThumbnail(MainActivity.URL));

playBtn = (ImageView) findViewById(R.id.playBtn);
playBtn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
doPlay();
}
});

progressTv=(TextView)findViewById(R.id.progressTv);

}

private void setProgressTv(int duration, int pos) {
progressTv.setText(Util.parseMillionToTime(pos)+"/"+Util.parseMillionToTime(duration));
}

public void doPlay() {
playBtn.setVisibility(View.GONE);
if(iv.getVisibility()==View.VISIBLE){
iv.setVisibility(View.GONE);
videoView.openVideo();
}else{
videoView.start();
}

mediaController.playOrPauseBtn.setBackgroundResource(R.drawable.pause_btn_selector);
mediaController.isPlaying=true;
}

public void doPause(){
playBtn.setVisibility(View.VISIBLE);
videoView.pause();
mediaController.isPlaying=false;
}

TimerTask timeTask = new TimerTask() {

@Override
public void run() {
try {
if(videoView==null || !videoView.isPlaying())
return;
if(!mediaController.isPressSeekBar()){
handler.sendEmptyMessage(0);
}
if(videoView.getCurrentPosition()==duration && duration>0){
handler.sendEmptyMessage(0);
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}
};

private Handler handler = new Handler() {
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
case 0:
changeSeekInfo();
break;
case 1:
Log.i("info","duration is : "+duration);
setProgressTv(duration, 0);
break;
}

}

private void changeSeekInfo() {
try {
int position = videoView.getCurrentPosition();
if(duration>0){
mediaController.seekTo(position, duration);
setProgressTv(duration, position);
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
};
};

}


CustomMediaController(菜单栏):

package com.dream.videoplayer_v4;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.view.View.OnClickListener;

public class CustomMediaController extends FrameLayout implements OnClickListener{
private static final String TAG = CustomMediaController.class.getSimpleName();
public ImageButton playOrPauseBtn;
public ImageButton fullOrNotBtn;
private SeekBar progressBar;

public boolean isPlaying=false;
private FullScreenActivity mAct;
int mProgress;

public CustomMediaController(Context context, AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);
// TODO Auto-generated constructor stub
initView(context);
}

public CustomMediaController(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
initView(context);
}

public CustomMediaController(Context context) {
super(context);
// TODO Auto-generated constructor stub
initView(context);
}

private void initView(Context context){
mAct=(FullScreenActivity)context;
View view=View.inflate(context, R.layout.media_controller, null);
playOrPauseBtn=(ImageButton)view.findViewById(R.id.pauseOrPlay);
fullOrNotBtn=(ImageButton)view.findViewById(R.id.fullOrNot);
progressBar=(SeekBar)view.findViewById(R.id.progressBar);

playOrPauseBtn.setOnClickListener(this);
fullOrNotBtn.setOnClickListener(this);

progressBar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {

@Override
public void onStopTrackingTouch(SeekBar seekBar) {
mAct.videoView.seekTo(mProgress);
}

@Override
public void onStartTrackingTouch(SeekBar seekBar) {
// TODO Auto-generated method stub

}

@Override
public void onProgressChanged(SeekBar seekBar, int progress,
boolean fromUser) {
mProgress=progress*mAct.videoView.getDuration()/seekBar.getMax();
}
});

this.addView(view);
}

@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.pauseOrPlay:
doPauseOrPlay();
break;

case R.id.fullOrNot:
doFullOrNot();
break;
}
}

private void doPauseOrPlay() {
if(isPlaying){
playOrPauseBtn.setBackgroundResource(R.drawable.play_btn_selector);
isPlaying=false;
mAct.doPause();
}else{
playOrPauseBtn.setBackgroundResource(R.drawable.pause_btn_selector);
isPlaying=true;
mAct.doPlay();
}
}

private void doFullOrNot() {
mAct.finish();
}

public void seekTo(int position,int duration){
mProgress=progressBar.getMax()*position/duration;
progressBar.setProgress(mProgress);
}

public boolean isPressSeekBar(){
if(progressBar.isPressed()){
return true;
}
return false;
}
}


三、资料
MediaPlayer API 文档:/article/4602823.html

MediaPlayer 生命周期介绍:/article/8105927.html

MediaPlayer 使用方式:/article/8105925.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: