前言
MediaPlayer,可以播放視頻/音頻,並且它支持本地和網絡文件的播放.本片博客作為入門教程,先以最通俗的方式解釋播放文件本地視頻.(如果你嫌MediaPlayer還是太麻煩可以試試選擇VideoView)
實現流程
- 獲取權限
- 保持屏幕常亮
- 初始化SurfaceView的狀態監聽
- 初始化MediaPlayer
- 給MediaPlayer添加預覽SurfaceView的SurfaceHolder
- 添加需要播放的視頻並且配置MediaPlayer
- 播放視頻
- 暫停視頻
- 停止視頻
- 釋放內存
獲取權限
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
播放本地視頻,那就需要讀寫存儲權限,注意別忘記了動態授權.另外MediaPlayer是支持網絡視頻播放的如果你需要播放網絡視頻則還需要添加網絡權限
保持屏幕常亮
音視頻開發的基本操作,在xml的根布局上添加下面這個屬性,也可以在SurfaceView上添加
android:keepScreenOn="true"
初始化SurfaceView
作為顯示播放視頻的曲面View,如果你還不了解它.那么建議你先學習一些SurfaceView的知識.
我們需要監聽SurfaceView狀態,確定它啟動完畢之后我們在開始加載播放視頻.在onCreate執行下面的初始化initSurfaceviewStateListener
private void initSurfaceviewStateListener() { mSurfaceHolder = mVideoPlaySurfaceview.getHolder(); mSurfaceHolder.addCallback(new SurfaceHolder.Callback() { @Override public void surfaceCreated(SurfaceHolder holder) { mMediaPlayer.setDisplay(holder);//給mMediaPlayer添加預覽的SurfaceHolder setPlayVideo(mPath);//添加播放視頻的路徑 } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { Log.e(TAG, "surfaceChanged觸發: width=" + width + "height" + height); } @Override public void surfaceDestroyed(SurfaceHolder holder) { } }); }
在surfaceCreated方法里執行了2個方法:(當然可以先不關注,下面依然會重新提到它們)
- 給MediaPlayer添加預覽的SurfaceHolder.
- 添加播放視頻的路徑與配置MediaPlayer
初始化MediaPlayer
private void initMediaPalyer() { mMediaPlayer = new MediaPlayer(); }
直接new,沒說明好說的,也是可以先在onCreate執行initMediaPalyer
給MediaPlayer添加預覽SurfaceView的SurfaceHolder
SurfaceView初始化完成后直接給MediaPlayer設置SurfaceHolder >>>>> mMediaPlayer.setDisplay(holder);
mSurfaceHolder.addCallback(new SurfaceHolder.Callback() { @Override public void surfaceCreated(SurfaceHolder holder) { mMediaPlayer.setDisplay(holder);//給mMediaPlayer添加預覽的SurfaceHolder setPlayVideo(mPath);//添加播放視頻的路徑 }
添加需要播放的視頻並且配置MediaPlayer
private void setPlayVideo(String path) { try { mMediaPlayer.setDataSource(path);//設置播放視頻文件 mMediaPlayer.setVideoScalingMode(MediaPlayer.VIDEO_SCALING_MODE_SCALE_TO_FIT);//縮放模式 mMediaPlayer.setLooping(true);//設置循環播放 mMediaPlayer.prepareAsync();//異步准備 // mMediaPlayer.prepare();//同步准備,因為是同步在一些性能較差的設備上會導致UI卡頓 mMediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { //准備完成回調 @Override public void onPrepared(MediaPlayer mp) { mp.start();//啟動播放視頻 } }); } catch (IOException e) { e.printStackTrace(); } }
setPlayVideo方法,在surfaceCreated接口回調方法里執行即可.上面已經有寫就不在重復交代了
播放視頻
private void startPlay(){ if (!mMediaPlayer.isPlaying()){ //判斷視頻是否在播放 mMediaPlayer.start(); } }
暫停視頻
private void pausePlay(){ if (mMediaPlayer.isPlaying()){ mMediaPlayer.pause(); } }
停止視頻
private void stopPlay(){ if (mMediaPlayer.isPlaying()){ mMediaPlayer.stop(); } }
釋放內存
這是必要的,因為MediaPlayer底層是運行C++的函數方法.不要使用后,必需釋放內存
@Override protected void onDestroy() { super.onDestroy(); if (mMediaPlayer != null){ if (mMediaPlayer.isPlaying()){ mMediaPlayer.stop(); } mMediaPlayer.release(); mMediaPlayer = null; } }
API總匯
- void setDataSource(String path) 通過一個具體的路徑來設置MediaPlayer的數據源,path可以是本地的一個路徑,也可以是一個網絡路徑
- void setDataSource(Context context, Uri uri) 通過給定的Uri來設置MediaPlayer的數據源,這里的Uri可以是網絡路徑或是一個ContentProvider的Uri。
- void setDataSource(MediaDataSource dataSource) 通過提供的MediaDataSource來設置數據源
- void setDataSource(FileDescriptor fd) 通過文件描述符FileDescriptor來設置數據源
- int getCurrentPosition() 獲取當前播放的位置
- int getAudioSessionId() 返回音頻的session ID
- int getDuration() 得到文件的時間
- TrackInfo[] getTrackInfo() 返回一個track信息的數組
- boolean isLooping () 是否循環播放
- boolean isPlaying() 是否正在播放
- void pause () 暫停
- void start () 開始
- void stop () 停止
- void prepare() 同步的方式裝載流媒體文件。
- void prepareAsync() 異步的方式裝載流媒體文件。
- void reset() 重置MediaPlayer至未初始化狀態。
- void release () 回收流媒體資源。
- void seekTo(int msec) 指定播放的位置(以毫秒為單位的時間)
- void setAudioStreamType(int streamtype) 指定流媒體類型
- void setLooping(boolean looping) 設置是否單曲循環
- void setNextMediaPlayer(MediaPlayer next) 當 當前這個MediaPlayer播放完畢后,MediaPlayer next開始播放
- void setWakeMode(Context context, int mode):設置CPU喚醒的狀態。
- setOnBufferingUpdateListener(MediaPlayer.OnBufferingUpdateListener listener) 網絡流媒體的緩沖變化時回調
- setOnCompletionListener(MediaPlayer.OnCompletionListener listener) 網絡流媒體播放結束時回調
- setOnErrorListener(MediaPlayer.OnErrorListener listener) 發生錯誤時回調
- setOnPreparedListener(MediaPlayer.OnPreparedListener listener):當裝載流媒體完畢的時候回調。
- setOnInfoListener(OnInfoListener l) 信息監聽
mMediaPlayer.setOnInfoListener(new MediaPlayer.OnInfoListener() { @Override public boolean onInfo(MediaPlayer mp, int what, int extra) { //what 對應返回的值如下 // public static final int MEDIA_INFO_UNKNOWN = 1; 媒體信息未知 // public static final int MEDIA_INFO_VIDEO_TRACK_LAGGING = 700; 媒體信息視頻跟蹤滯后 // public static final int MEDIA_INFO_VIDEO_RENDERING_START = 3; 媒體信息\視頻渲染\開始 // public static final int MEDIA_INFO_BUFFERING_START = 701; 媒體信息緩沖啟動 // public static final int MEDIA_INFO_BUFFERING_END = 702; 媒體信息緩沖結束 // public static final int MEDIA_INFO_NETWORK_BANDWIDTH = 703; 媒體信息網絡帶寬(703) // public static final int MEDIA_INFO_BAD_INTERLEAVING = 800; 媒體-信息-壞-交錯 // public static final int MEDIA_INFO_NOT_SEEKABLE = 801; 媒體信息找不到 // public static final int MEDIA_INFO_METADATA_UPDATE = 802; 媒體信息元數據更新 // public static final int MEDIA_INFO_UNSUPPORTED_SUBTITLE = 901; 媒體信息不支持字幕 // public static final int MEDIA_INFO_SUBTITLE_TIMED_OUT = 902; 媒體信息字幕超時 return false; } });
貼心周到,貼全demo代碼,給你查缺補漏
xml文件
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true" android:keepScreenOn="true" android:background="@color/colorBlack1" tools:context=".work.share.VideoPlayActivity"> <SurfaceView android:id="@+id/video_play_surfaceview" android:layout_width="wrap_content" android:layout_height="wrap_content" app:layout_constraintHorizontal_bias="0.5" app:layout_constraintVertical_bias="0.5" app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent"/> <ImageView android:id="@+id/start_and_stop" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@mipmap/ic_start" android:layout_marginBottom="50dp" android:layout_marginLeft="20dp" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintBottom_toBottomOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout>
activity
import androidx.constraintlayout.widget.ConstraintLayout; import android.content.pm.ActivityInfo; import android.media.MediaPlayer; import android.os.Bundle; import android.util.Log; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.View; import android.widget.ImageView; import com.yt.kangaroo.R; import com.yt.kangaroo.app.BaseActivity; import java.io.File; import java.io.IOException; /** *@content:視頻播放activity *@time:2019-6-13 *@build: */ public class VideoPlayActivity extends BaseActivity implements View.OnClickListener { private static final String TAG = VideoPlayActivity.class.getSimpleName(); private SurfaceView mVideoPlaySurfaceview; private ImageView mStartAndStop; private MediaPlayer mMediaPlayer; private String mPath; private boolean isInitFinish = false; private SurfaceHolder mSurfaceHolder; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); File file = new File(getExternalCacheDir(), "demo.mp4"); mPath = file.getAbsolutePath(); initMediaPalyer(); initSurfaceviewStateListener(); } @Override protected void onPause() { super.onPause(); pausePlay(); } @Override public int getLayout() { return R.layout.activity_video_play; } @Override public void initView() { mVideoPlaySurfaceview = findViewById(R.id.video_play_surfaceview); mStartAndStop = findViewById(R.id.start_and_stop); mStartAndStop.setOnClickListener(this); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.start_and_stop: if (mMediaPlayer.isPlaying()) { pausePlay(); mStartAndStop.setImageResource(R.mipmap.ic_start); } else { startPlay(); mStartAndStop.setImageResource(R.mipmap.ic_stop); } break; default: break; } } private void initSurfaceviewStateListener() { mSurfaceHolder = mVideoPlaySurfaceview.getHolder(); mSurfaceHolder.addCallback(new SurfaceHolder.Callback() { @Override public void surfaceCreated(SurfaceHolder holder) { mMediaPlayer.setDisplay(holder);//給mMediaPlayer添加預覽的SurfaceHolder setPlayVideo(mPath);//添加播放視頻的路徑 } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { Log.e(TAG, "surfaceChanged觸發: width=" + width + "height" + height); } @Override public void surfaceDestroyed(SurfaceHolder holder) { } }); } private void initMediaPalyer() { mMediaPlayer = new MediaPlayer(); } private void setPlayVideo(String path) { try { mMediaPlayer.setDataSource(path);// mMediaPlayer.setVideoScalingMode(MediaPlayer.VIDEO_SCALING_MODE_SCALE_TO_FIT);//縮放模式 mMediaPlayer.setLooping(true);//設置循環播放 mMediaPlayer.prepareAsync();//異步准備 // mMediaPlayer.prepare();//同步准備,因為是同步在一些性能較差的設備上會導致UI卡頓 mMediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { //准備完成回調 @Override public void onPrepared(MediaPlayer mp) { isInitFinish = true; mp.start(); } }); } catch (IOException e) { e.printStackTrace(); } } private void startPlay(){ if (!mMediaPlayer.isPlaying()){ mMediaPlayer.start(); } } private void stopPlay(){ if (mMediaPlayer.isPlaying()){ mMediaPlayer.stop(); } } private void pausePlay(){ if (mMediaPlayer.isPlaying()){ mMediaPlayer.pause(); } } private void seekTo(int time){ mMediaPlayer.seekTo(time); } @Override protected void onDestroy() { super.onDestroy(); if (mMediaPlayer != null){ if (mMediaPlayer.isPlaying()){ mMediaPlayer.stop(); } mMediaPlayer.release(); mMediaPlayer = null; } } }
end