MediaPlayer 播放音频与视频

MediaPlayer 简 介

  • Android 多媒体中的——MediaPlayer 可以用来播放音频和视频,是 Androd 多媒体框架中的一个重要组件,通过该类,可以以最小的步骤来获取,解码 和播放音视频。
  • MediaPlayer 支持三种不同的媒体来源:

1) 本地资源

2) 内部的 URI,比如可以通过 ContentResolver 来获取

3) 外部的 URL(流) 对于Android所支持的的媒体格式列表,可见:Supported Media Formats 文档

1)获得 MediaPlayer 实例,可以直接 new 或者调用create方法创建,如下只是常用的两种,还有其它重载的方法:

MediaPlayer mp = new MediaPlayer(); // 之后 调用 setDataSource 设置播放的资源
MediaPlayer mp = MediaPlayer.create(this, R.raw.test);  //无需再调用 setDataSource 设置播放的资源

2)设置播放文件:

//①raw下的资源:
MediaPlayer.create(this, R.raw.test);

//②本地文件路径:
mp.setDataSource("/sdcard/test.mp3");

//③网络URL文件:
mp.setDataSource("http://www.xxx.com/music/test.mp3");

  • 另外 setDataSource() 方法有多个,里面有这样一个类型的参数:FileDescriptor,在使用这个 API的时候,需要把文件放到res文件夹平级的assets文件夹里,然后使用下述代码设置DataSource:

AssetFileDescriptor fileDescriptor = getAssets().openFd("rain.mp3");
m_mediaPlayer.setDataSource(fileDescriptor.getFileDescriptor(),fileDescriptor.getStartOffset(), fileDescriptor.getLength());

3)常用 API 描述

getCurrentPosition( ):得到当前的播放位置
getDuration() :得到文件的时间
getVideoHeight() :得到视频高度
getVideoWidth() :得到视频宽度
isLooping():是否循环播放
isPlaying():是否正在播放
pause():暂停
prepare():准备(同步)
prepareAsync():准备(异步)
release():释放MediaPlayer对象
reset():重置MediaPlayer对象
seekTo(int msec):指定播放的位置(以毫秒为单位的时间)
setAudioStreamType(int streamtype):指定流媒体的类型
setDisplay(SurfaceHolder sh):设置用SurfaceHolder来显示多媒体
setLooping(boolean looping):设置是否循环播放
setOnBufferingUpdateListener(MediaPlayer.OnBufferingUpdateListener listener): 网络流媒体的缓冲监听
setOnCompletionListener(MediaPlayer.OnCompletionListener listener): 网络流媒体播放结束监听
setOnErrorListener(MediaPlayer.OnErrorListener listener): 设置错误信息监听
setOnVideoSizeChangedListener(MediaPlayer.OnVideoSizeChangedListener listener): 视频尺寸监听
setScreenOnWhilePlaying(boolean screenOn):设置是否使用SurfaceHolder显示
setVolume(float leftVolume, float rightVolume):设置音量
start():开始播放
stop():停止播放

音频操作

播放 raw 中的文件

  • 现在来用 MediaPlayer 来写个简单的播放 res/raw 目录中的音频例子,效果如下:点击 “播放” 按钮,音乐开始播放;点击 “暂停” 按钮,音乐暂停播放,再次点击“播放”时继续;点击“退出”按钮,音乐退回初始位置,等待播放。

  • activity_main.xml 布局文件内容如下:
<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/GridLayout1"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:columnCount="3"
    android:orientation="horizontal"
    android:rowCount="3">

    <Button
        android:id="@+id/btnStart"
        android:layout_width="match_parent"
        android:layout_height="64dp"
        android:text="播放" />

    <Button
        android:id="@+id/btnStop"
        android:layout_width="match_parent"
        android:layout_height="64dp"
        android:text="暂停" />

    <Button
        android:id="@+id/btnQuit"
        android:layout_width="match_parent"
        android:layout_height="64dp"
        android:text="退出" />

</GridLayout>
  • MainActivity.java 文件内容如下:
package com.example.administrator.helloworld;

import android.media.AudioManager;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;

import java.io.IOException;

import static android.icu.lang.UCharacter.GraphemeClusterBreak.L;

/**
 * 一个应用程序可以有1个或多个活动,而没有任何限制。
 * 每个为应用程序所定义的活动都需要在 AndroidManifest.xml 中声明,应用的主活动的意图过滤器标签中需要包含 MAIN 动作和 LAUNCHER 类别
 * 如果 MAIN 动作还是 LAUNCHER 类别没有在活动中声明,那么应用程序的图标将不会出现在主屏幕的应用列表中。
 * implements View.OnClickListener:用于绑定单击事件
 */
public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    /**
     * TAG:Log.d(String tag, String msg) 的消息标签
     * btn_play:播放按钮
     * btn_stop:暂停按钮
     * btn_quit:退出重置按钮
     * isRelease:判断 MediaPlayer 是否释放的标志
     */
    private static final String TAG = "MainActivity : ";

    private Button btn_play;
    private Button btn_stop;
    private Button btn_quit;
    private MediaPlayer mPlayer = null;
    private boolean isRelease = true;

    /**
     * 当活动第一次被创建时调用
     */
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        /**
         * 从项目的 res/layout 中的XML文件加载 UI 组件
         * android.util.Log#d(java.lang.String, java.lang.String) 方法用于在 Logcat 窗口中打印日志
         */
        setContentView(R.layout.activity_main);
        Log.d(TAG, "The onCreate(Bundle savedInstanceState) .....");

        bindViews();
    }

    /**
     * 为按钮 绑定单击事件
     */
    private void bindViews() {
        btn_play = findViewById(R.id.btnStart);
        btn_stop = findViewById(R.id.btnStop);
        btn_quit = findViewById(R.id.btnQuit);

        /**初始条件下只有 播放 按钮能点击,暂停、退出按钮不能点击*/
        btn_stop.setEnabled(false);
        btn_quit.setEnabled(false);

        btn_play.setOnClickListener(this);
        btn_stop.setOnClickListener(this);
        btn_quit.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        Log.d(TAG, "-----------" + v.getId());
        switch (v.getId()) {
            case R.id.btnStart:
                /**如果当前 MediaPlayer 未关联播放的文件,则进行关联
                 * 播放的是 res/raw 目录下的 day_by_day.mp3 文件
                 */
                if (isRelease) {
                    mPlayer = MediaPlayer.create(this, R.raw.day_by_day);
                    isRelease = false;  //设置标识
                }
                mPlayer.start();    //开始播放
                btn_play.setEnabled(false); //禁用播放按钮
                btn_stop.setEnabled(true);  //启用暂停按钮
                btn_quit.setEnabled(true);  //启用退出按钮

                int currentPosition = mPlayer.getCurrentPosition(); // 获取当前播放的位置,单位毫秒
                int duration = mPlayer.getDuration();//获取文件的总时间长度,成功时以毫秒返回,否则为 -1,如直播流
                boolean isLooping = mPlayer.isLooping();//是否正在循环播放

                Log.d(TAG, "文件总时间:" + duration + " 毫秒");
                Log.d(TAG, "文件当前播放时间:" + currentPosition + " 毫秒");
                Log.d(TAG, "文件是否循环播放:" + isLooping);

                break;
            case R.id.btnStop:
                mPlayer.pause();    //停止播放
                btn_play.setEnabled(true);  //启用播放按钮
                btn_stop.setEnabled(false); //禁用暂停按钮
                btn_quit.setEnabled(false); //禁用退出按钮
                break;
            case R.id.btnQuit:
                mPlayer.reset();     //重置MediaPlayer
                mPlayer.release();   //释放MediaPlayer,即释放与此媒体播放器对象关联的资源。
                isRelease = true;   //设置标识
                btn_play.setEnabled(true);  //启用播放按钮
                btn_stop.setEnabled(false); //禁用暂停按钮
                btn_quit.setEnabled(false); //禁用退出按钮
                break;
        }
    }
}
  • 以 “播放”—>“暂停”->"播放"->"暂停"->"播放"—>"退出"->"播放"的顺序点击后,控制台输出如下:

D/MainActivity :: -----------2131165220
D/MainActivity :: 文件总时间:60757 毫秒
                  文件当前播放时间:0 毫秒
                  文件是否循环播放:false
D/MainActivity :: -----------2131165221
D/MainActivity :: -----------2131165220
D/MainActivity :: 文件总时间:60757 毫秒
D/MainActivity :: 文件当前播放时间:6332 毫秒
                  文件是否循环播放:false
D/MainActivity :: -----------2131165221
D/MainActivity :: -----------2131165220
D/MainActivity :: 文件总时间:60757 毫秒
                  文件当前播放时间:13001 毫秒
                  文件是否循环播放:false
D/MainActivity :: -----------2131165219
D/MainActivity :: -----------2131165220
E/MediaPlayer: Should have subtitle controller already set
D/MainActivity :: 文件总时间:60757 毫秒
                  文件当前播放时间:0 毫秒
                  文件是否循环播放:false

视频操作

  • MediaPlayer 主要用于播放音频,没有提供图像输出界面,所以需要借助其他的组件来显示 MediaPlayer 播放的图像,可以使用 SurfaceView  来显示。

播放 raw 中的文件

  • 下面以播放 res/raw 目录下的视频文件为例进行说明,效果图如下:

  • 布局文件 activity_main.xml 代码如下:
<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/GridLayout1"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    android:columnCount="3"
    android:orientation="horizontal"
    android:rowCount="3">

    <SurfaceView
        android:id="@+id/sfv_show"
        android:layout_height="300dp"
        android:layout_columnSpan="3"
        android:layout_gravity="fill" />

    <Button
        android:id="@+id/btnStart"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="fill"
        android:text="开始" />

    <Button
        android:id="@+id/btnStop"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="fill"
        android:text="暂停 " />

    <Button
        android:id="@+id/btnQuit"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="fill"
        android:text="重新开始" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_columnSpan="3"
        android:layout_gravity="center"
        android:layout_marginBottom="5dp"
        android:layout_marginTop="100dp"
        android:textColor="#EA2000"
        android:text="···版权归贾媛媛男朋友所有···" />

</GridLayout>
  • MainActivity.java 文件内容如下:
package com.example.administrator.helloworld;

import android.media.AudioManager;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.widget.Button;

import java.io.IOException;

import static android.icu.lang.UCharacter.GraphemeClusterBreak.L;
import static android.icu.lang.UCharacter.GraphemeClusterBreak.T;

/**
 * 一个应用程序可以有1个或多个活动,而没有任何限制。
 * 每个为应用程序所定义的活动都需要在 AndroidManifest.xml 中声明,应用的主活动的意图过滤器标签中需要包含 MAIN 动作和 LAUNCHER 类别
 * 如果 MAIN 动作还是 LAUNCHER 类别没有在活动中声明,那么应用程序的图标将不会出现在主屏幕的应用列表中。
 * implements View.OnClickListener:用于绑定单击事件
 * implements SurfaceHolder.Callback:用于监测 surface 的状态,重写其三个方法
 * android.view.SurfaceHolder 是接口,Callback 是 SurfaceHolder 的子接口
 */
public class MainActivity extends AppCompatActivity implements View.OnClickListener, SurfaceHolder.Callback {

    /**
     * TAG:Log.d(String tag, String msg) 的消息标签
     * <p>
     * btn_play:播放按钮
     * btn_stop:暂停按钮
     * btn_quit:重新开始按钮
     * android.media.MediaPlayer:媒体播放器
     * android.view.SurfaceView:表面/外观视图
     * android.view.SurfaceHolder:表面/外观视图所有者
     */
    private static final String TAG = "MainActivity : ";

    private Button btn_play;
    private Button btn_stop;
    private Button btn_restart;
    private MediaPlayer mediaPlayer = null;
    private SurfaceView surfaceView;
    private SurfaceHolder surfaceHolder;

    /**
     * 当活动第一次被创建时调用
     */
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        /**
         * 从项目的 res/layout 中的XML文件加载 UI 组件
         * android.util.Log#d(java.lang.String, java.lang.String) 方法用于在 Logcat 窗口中打印日志
         */
        setContentView(R.layout.activity_main);
        Log.d(TAG, "The onCreate(Bundle savedInstanceState) .....");

        bindViews();
    }

    /**
     * 为按钮 绑定单击事件
     * 初始化 SurfaceHolder 类,SurfaceView 的控制器
     */
    private void bindViews() {
        surfaceView = findViewById(R.id.sfv_show);
        btn_play = findViewById(R.id.btnStart);
        btn_stop = findViewById(R.id.btnStop);
        btn_restart = findViewById(R.id.btnRestart);

        /**通过android.view.SurfaceView 获得 android.view.SurfaceHolder 实例*/
        surfaceHolder = surfaceView.getHolder();
        /**添加回调*/
        surfaceHolder.addCallback(this);

        /**设置表面视图显示的分辨率,不设置为视频默认*/
        surfaceHolder.setFixedSize(320, 220);

    }

    @Override
    public void onClick(View v) {
        Log.d(TAG, "onClick-----------" + v.getId());
        switch (v.getId()) {
            case R.id.btnStart:// 播放按钮
                mediaPlayer.start();//开始播放
                btn_play.setEnabled(false);//禁用播放按钮
                btn_stop.setEnabled(true);//启用暂停按钮
                btn_restart.setEnabled(true);//启用重新播放按钮
                break;
            case R.id.btnStop:
                mediaPlayer.pause();//暂停播放
                btn_play.setEnabled(true);//启用播放按钮
                btn_stop.setEnabled(false);//禁用暂停按钮
                btn_restart.setEnabled(true);//启用重新播放按钮
                break;
            case R.id.btnRestart:
                btn_play.setEnabled(false);//禁用播放按钮
                btn_stop.setEnabled(true);//启用暂停按钮
                btn_restart.setEnabled(false);//禁用重新播放按钮

                mediaPlayer.reset();//重置 MediaPlayer 对象
                mediaPlayer.release();// 释放MediaPlayer资源
                /**
                 * 然后重新进行绑定资源、
                 * 指定流媒体类型、
                 * 设置用SurfaceHolder来显示多媒体、
                 * 再重新播放
                 */
                mediaPlayer = MediaPlayer.create(MainActivity.this, R.raw.la_isla);
                mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
                mediaPlayer.setDisplay(surfaceHolder);
                mediaPlayer.start();
                break;
        }
    }

    /**
     * 重写的 android.view.SurfaceHolder.Callback 接口中的方法
     * surfaceView 视图每次创建时都会自动执行此方法
     * 只要视频播放界面重新回到第一层,即用户能直接操作时,就会自动执行此方法
     *
     * @param holder
     */
    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        Log.d(TAG, "surfaceCreated......................");
        /**播放前,设置播放按钮激活、暂停按钮禁用、重新开始按钮禁用*/
        btn_play.setOnClickListener(this);
        btn_stop.setOnClickListener(this);
        btn_restart.setOnClickListener(this);
        /**界面初始化时媒体播放器,然后设置流媒体类型,最后用 SurfaceHolder显示出来*/
        mediaPlayer = MediaPlayer.create(MainActivity.this, R.raw.la_isla);
        mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
        mediaPlayer.setDisplay(surfaceHolder);    //设置显示视频显示在SurfaceView上
    }

    /**
     * 重写的 android.view.SurfaceHolder.Callback 接口中的方法
     * 只要 Surface 视图有变化,此方法就会自动执行
     *
     * @param holder
     * @param format
     * @param width
     * @param height
     */
    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        Log.d(TAG, "surfaceChanged.................");
    }

    /**
     * 重写的 android.view.SurfaceHolder.Callback 接口中的方法
     * 当界面被其它界面覆盖时,或者直接被退出时,就会自动执行此方法
     * 被覆盖时,则停止播放,然后释放资源
     * 注意:如果界面被覆盖了没有 stop停止播放,或者释放资源,就会出现视频在后台有声音出来
     * 当再次进入界面时,只有声音,没有画面
     *
     * @param holder
     */
    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        Log.d(TAG, "surfaceDestroyed.................");
        if (mediaPlayer.isPlaying()) {
            mediaPlayer.stop();
        }
        mediaPlayer.release();
    }
}

猜你喜欢

转载自blog.csdn.net/wangmx1993328/article/details/82798626