前言
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
请注意,MediaPlayer最好是全局变量让某个class一直持有直到你不需要为止,如果是局部变量会有概率出现播放时突然中断的问题
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