长驱直入,傻瓜式操作就是改造VideoView源码,谁让这个控件就是经典Mediaplayer搭配SurfaceView使用,天然教程。
没有设置AudioStremType,因为我的文件没有音频轨道。
记得先准备好音视频文件。
一、VideoView源码
梳理一下VideoView源码,就会发现,删除了多余的subtitle和MediaController外,videoview就只剩下了mediaplayer和surfaceview。
剩下的根据需要改就行了,这里我就改了两个地方:
- MediaPlayer播放assets目录文件
AssetFileDescriptor afd = this.getContext().getAssets().openFd(mFileName);
mMediaPlayer.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
- setDataSource后不openVideo
为了更贴合人工造的自定义VideoView,只保留mSHCallback里的surfaceCreated里的openVideo。
二、VideoView使用教程:
添加videoview控件到窗口上, 准备好assets目录下的音频文件,然后请往下看。
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<com.nio.player.VideoView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/vii_scene"/>
</RelativeLayout>
public SceneView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
//封装surfaceview为自定义控件时, root 必须设置,否则没办法添加surfaceview到窗口上
View mRootView = LayoutInflater.from(context).inflate(R.layout.video_view, this);
mVideoView = (VideoView) mRootView.findViewById(R.id.vii_scene);
}
public class VideoView extends SurfaceView {
private static final String TAG = "VideoView";
// all possible internal states
private static final int STATE_ERROR = -1;
private static final int STATE_IDLE = 0;
private static final int STATE_PREPARING = 1;
private static final int STATE_PREPARED = 2;
private static final int STATE_PLAYING = 3;
private static final int STATE_PAUSED = 4;
private static final int STATE_PLAYBACK_COMPLETED = 5;
private String mFileName;
private int mCurrentState = STATE_IDLE;
private int mTargetState = STATE_IDLE;
private SurfaceHolder mHolder;
private MediaPlayer mMediaPlayer = null;
private int mAudioSession;
private int mSeekWhenPrepared; // recording the seek position while preparing
public VideoView(Context context) {
this(context, null);
}
public VideoView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public VideoView(Context context, AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
public VideoView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
Log.i(TAG, "init");
mHolder = getHolder();
mHolder.addCallback(mSHCallback);
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
mCurrentState = STATE_IDLE;
mTargetState = STATE_IDLE;
}
@Override
public CharSequence getAccessibilityClassName() {
return VideoView.class.getName();
}
public void setDataSource(String fileName) {
mFileName = fileName;
mSeekWhenPrepared = 0;
requestFocus();
requestLayout();
invalidate();
}
public void stopPlayback() {
if (mMediaPlayer != null) {
mMediaPlayer.stop();
mMediaPlayer.release();
mMediaPlayer = null;
mCurrentState = STATE_IDLE;
mTargetState = STATE_IDLE;
}
}
private void openVideo() {
if (mFileName.isEmpty() || mHolder == null) {
return;
}
release(false);
try {
mMediaPlayer = new MediaPlayer();
if (mAudioSession != 0) {
mMediaPlayer.setAudioSessionId(mAudioSession);
} else {
mAudioSession = mMediaPlayer.getAudioSessionId();
}
mMediaPlayer.setOnPreparedListener(mPreparedListener);
// 播放assets目录下的文件
AssetFileDescriptor afd = this.getContext().getAssets().openFd(mFileName);
mMediaPlayer.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
mMediaPlayer.setDisplay(mHolder);
mMediaPlayer.setScreenOnWhilePlaying(true);
mMediaPlayer.prepareAsync();
mMediaPlayer.setLooping(true);
mCurrentState = STATE_PREPARING;
} catch (IOException | IllegalArgumentException ex) {
Log.w(TAG, "Unable to open content: " + mFileName, ex);
mCurrentState = STATE_ERROR;
mTargetState = STATE_ERROR;
}
}
MediaPlayer.OnPreparedListener mPreparedListener = new MediaPlayer.OnPreparedListener() {
public void onPrepared(MediaPlayer mp) {
mCurrentState = STATE_PREPARED;
start();
}
};
SurfaceHolder.Callback mSHCallback = new SurfaceHolder.Callback()
{
public void surfaceChanged(SurfaceHolder holder, int format,
int w, int h)
{
Log.i(TAG, "surfaceChanged + " + System.currentTimeMillis());
boolean isValidState = (mTargetState == STATE_PLAYING);
if (mMediaPlayer != null && isValidState) {
if (mSeekWhenPrepared != 0) {
seekTo(mSeekWhenPrepared);
}
start();
}
}
public void surfaceCreated(SurfaceHolder holder)
{
mHolder = holder;
Log.i(TAG, "surfaceCreated");
// 放个assets目录的mp4资源
// 其他格式或路径请自行探索
setDataSource("gouhuo.mp4");
openVideo();
}
public void surfaceDestroyed(SurfaceHolder holder)
{
Log.i(TAG, "surfaceDestroyed + " + System.currentTimeMillis());
// after we return from this we can't use the surface any more
release(true);
}
};
/*
* release the media player in any state
*/
private void release(boolean cleartargetstate) {
if (mMediaPlayer != null) {
mMediaPlayer.reset();
mMediaPlayer.release();
mMediaPlayer = null;
mCurrentState = STATE_IDLE;
if (cleartargetstate) {
mTargetState = STATE_IDLE;
}
}
}
public void start() {
if (isInPlaybackState()) {
mMediaPlayer.start();
mCurrentState = STATE_PLAYING;
}
mTargetState = STATE_PLAYING;
}
public void pause() {
if (isInPlaybackState()) {
if (mMediaPlayer.isPlaying()) {
mMediaPlayer.pause();
mCurrentState = STATE_PAUSED;
}
}
mTargetState = STATE_PAUSED;
}
public int getCurrentPosition() {
if (isInPlaybackState()) {
return mMediaPlayer.getCurrentPosition();
}
return 0;
}
public void seekTo(int msec) {
if (isInPlaybackState()) {
mMediaPlayer.seekTo(msec);
mSeekWhenPrepared = 0;
} else {
mSeekWhenPrepared = msec;
}
}
private boolean isInPlaybackState() {
return (mMediaPlayer != null &&
mCurrentState != STATE_ERROR &&
mCurrentState != STATE_IDLE &&
mCurrentState != STATE_PREPARING);
}
}
先从分享基础开始,拓展项再努力摸索,摸索出来的才是自己的,诸君共勉!