上两篇博客,Android-MediaPlayer-音频播放-普通准备,Android-MediaPlayer-音频播放-异步准备,主要是讲解了音频(.mp3文件)音乐🎵的播放
这篇博客是讲解视频(.mp4文件)视频的播放,无论是音频还是视频都是使用 MediaPlayer媒体播放器来播放
音频播放 与 视频播放 对比:
音频播放:MediaPlayer + 音频文件
视频播放:MediaPlayer + 视频文件 + SurfaceView
MediaPlayer是Android设计的媒体播放器,不仅仅可以播放音频文件,还可以播放视频文件
播放:Audio(音频,.mp3)相关
播放:Video(视频,.mp4)相关
以下图,是Android官方提供:MediaPlayer时序图:
只要会看这个图:就能实现音频/视频播放,暂停,继续,停止,重播,等等
看图规律:
1.蓝色椭圆形是状态,例如:Initialized已初始化状态,Prepared准备状态,Started启动状态,Stopped停止状态,End结束状态,等等;
2.单箭头是方法调用:例如:调用reset方法重置,调用prepare方法准备,调用start方法播放,等等;
3.双箭头是监听回调:例如:onError回调错误,等等

此MediaPlayer播放使用异步准备
package liudeli.my_media1; import android.database.Cursor; import android.media.MediaPlayer; import android.net.Uri; import android.os.Bundle; import android.os.Environment; import android.os.SystemClock; import android.provider.MediaStore; import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.View; import android.widget.Button; import android.widget.TextView; import android.widget.Toast; import java.io.File; /** * 此MediaPlayer播放使用使用异步准备,不是普通准备 */ public class MediaPlayAsyncVideoActivity extends AppCompatActivity { private TextView tvPlayerPath; // 显示播放的路径 private TextView tvThisDuration; // 当前播放的时长 private TextView tv_play_state; // 播放的状态 /** * 媒体播放器,可以播放(音频/视频) * 播放(音频/视频)操作一模一样 */ private MediaPlayer mediaPlayer; /** * SurfaceView 显示视频播放画面的控件 */ private SurfaceView surfaceView; /** * SurfaceView 需要使用SurfaceHolder来完成操作 */ private SurfaceHolder surfaceHolder; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_play_video); tvPlayerPath = findViewById(R.id.tv_player_path); tvThisDuration = findViewById(R.id.tv_this_duration); tv_play_state = findViewById(R.id.tv_play_state); // 获取 SurfaceView surfaceView = findViewById(R.id.surface_view); // 通过 SurfaceView 获取 SurfaceHolder surfaceHolder = surfaceView.getHolder(); // 设置直接把视频画面显示到SurfaceView中 surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); // 设置屏幕高亮,不灭屏, 注意:⚠ 所有的控件都可以设置.setKeepScreenOn(true); surfaceHolder.setKeepScreenOn(true); mediaPlayer = new MediaPlayer(); // 为了测试,这样写,真实开发中,不这样写 new Thread(){ @Override public void run() { super.run(); while (true) { runOnUiThread(new Runnable() { @Override public void run() { if (mediaPlayer.isPlaying()) { tvThisDuration.setText("当前时长:" + postions(mediaPlayer.getCurrentPosition())); } } }); SystemClock.sleep(1000); } } }.start(); /** * 监听播放完成 */ mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() { @Override public void onCompletion(MediaPlayer mp) { Log.d("mp", "播放完成"); Toast.makeText(MediaPlayAsyncVideoActivity.this, "播放完成", Toast.LENGTH_SHORT).show(); tvThisDuration.setText("当前时长:-"); } }); /** * 监听播放错误 */ mediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() { @Override public boolean onError(MediaPlayer mp, int what, int extra) { tv_play_state.setText("播放异常"); return false; } }); /** * 去获取第一条外置存储的视频文件.mp4 的路径 * 通过Uri uri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI; 获取外置存储视频文件 * getContentResolver.query(uri) */ initVideoPlayerPath(); tv_play_state.setText("---"); } /** * 去获取第一条外置存储的视频文件.mp4 的路径 */ private void initVideoPlayerPath() { /** * 获取外置存储视频的Uri MediaStore.Video.Media.EXTERNAL_CONTENT_URI; */ /*Uri uri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI; // 获取外置存储视频的Uri // 查询的列 String[] projection = new String[]{MediaStore.Video.Media.DATA // 视频路径 }; // 让Android系统也会去读取外置存储 Cursor cursor = getContentResolver().query(uri, projection, null, null, null, null); *//** * 把游标移到第一行:cursor.moveToFirst() * 把游标移到第六行:cursor.moveToPosition(6) *//* if (cursor.moveToPosition(1)) { // if (cursor.moveToFirst()) { tvPlayerPath.setText(cursor.getString(cursor.getColumnIndex(MediaStore.Video.Media.DATA))); }*/ File file = new File(Environment.getExternalStorageDirectory(), "my_video.mp4"); if (file.exists()) tvPlayerPath.setText(file.getAbsolutePath()); } /** * 转换时长值 */ private String postions(int postion) { int musicTime = postion / 1000; return musicTime / 60 + ":" + musicTime % 60; } /** * 开始播放:此次播放使用异步准备,不使用普通准备 * @param view */ public void player(View view) { try { // 重置 mediaPlayer.reset(); // 设置视频文件路径 mediaPlayer.setDataSource(tvPlayerPath.getText().toString().trim()); // 异步准备并播放 asyncPrepare(); } catch (Exception e) { e.printStackTrace(); } } /** * 异步准备的行为 */ private void asyncPrepare() { /** * 一定要在 MediaPlayer 准备之前 (无论是普通准备 还是 异步准备),把MediaPlayer和SurfaceHolder 进行绑定 */ mediaPlayer.setDisplay(surfaceHolder); // 准备:是操作硬件在播放,所以需要准备 mediaPlayer.prepareAsync(); // 监听异步准备,一旦准备完成,就会调用此方法 mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { @Override public void onPrepared(MediaPlayer mp) { // 调用此方法,代表异步准备完成✅ // 开始播放 mediaPlayer.start(); tv_play_state.setText("播放中..."); } }); } /** * 暂停播放 继续播放 * @param view */ public void pause(View view) { /** * 这种方式可以拿到控件 */ Button pause_continue = (Button) view; if (mediaPlayer.isPlaying()) { pause_continue.setText("继续"); // 暂停 mediaPlayer.pause(); tv_play_state.setText("暂停中..."); } else { pause_continue.setText("暂停"); // 继续播放 mediaPlayer.start(); tv_play_state.setText("播放中..."); } } /** * 停止播放 */ public void stop(View view) { // 停止播放 mediaPlayer.stop(); tv_play_state.setText("---"); tvThisDuration.setText("当前时长:---"); } /** * 重播 * @param view */ public void recorded(View view) { try { // 先停止 mediaPlayer.stop(); // 异步准备并播放 asyncPrepare(); } catch (Exception e) { e.printStackTrace(); } finally { if (mediaPlayer.isPlaying()) { tv_play_state.setText("播放中..."); } } } /** * 此Activity销毁后,一定要 * mediaPlayer.release(); * mediaPlayer = null; * 因为 MediaPlayer 是操作硬件在播放,所以一定要释放资源 */ @Override protected void onDestroy() { super.onDestroy(); mediaPlayer.release(); mediaPlayer = null; System.gc(); } }
AndroidManifest.xml 配置 外部存储读取权限:
<!-- 需要读取外部Sdcard存储的权限 --> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
布局文件:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout 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:orientation="vertical"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="10dp"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="播放视频.mp4路径:" /> <TextView android:id="@+id/tv_player_path" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="/mnt/sdcard/" /> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <Button android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="播放" android:onClick="player" /> <Button android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="暂停" android:onClick="pause" /> <Button android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="停止" android:onClick="stop" /> <Button android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="重播" android:onClick="recorded" /> </LinearLayout> <SurfaceView android:id="@+id/surface_view" android:layout_width="match_parent" android:layout_height="200dp" /> <TextView android:id="@+id/tv_this_duration" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="10dp" android:text="视频时长:-" /> <TextView android:id="@+id/tv_play_state" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="20sp" android:padding="30dp" android:layout_gravity="center_horizontal" /> </LinearLayout>
效果:

浙公网安备 33010602011771号