使用MediaPlayer播放音视频精炼详解

这里写图片描述

一、前期基础知识储备

MediaPlayer是一个支持音频及视频文件播放的Android类,可播放不同来源(本地 网络)、多种格式(如WAV/MP3/MPEG-4/3GPP/Ogg Vorbis)的多媒体文件。
多媒体文件可以是存储在应用程序的 res/raw 文件夹下,也可以是存储在手机的文件系统中,甚至可以是来自与网络的流媒体。raw文件夹负责存放那些不需要Android编译系统特别处理的各类文件,res下默认是没有raw文件夹的,创建方法如下:
这里写图片描述
这里写图片描述
实例化一个MediaPlayer对象一共有三种方法:
1)create(Context context, Uri uri, SurfaceHolder holder)
Convenience method to create a MediaPlayer for a given Uri.
2)create(Context context, int resid)
Convenience method to create a MediaPlayer for a given resource id.
3)create(Context context, Uri uri)
Convenience method to create a MediaPlayer for a given Uri.
注意: 你只能通过标准输出设备来播放音频文件。目前来讲,就是通过手机的扬声器和蓝牙耳机。 不能在通话时播放音频文件。
使用MediaPlayer时申请的权限:
1)<uses-permission android:name="android.permission.INTERNET" />
Internet Permission - 如果你打算使用 MediaPlayer 来播放网络流媒体内容,那么你的应用需要有这个权限;
2)<uses-permission android:name="android.permission.WAKE_LOCK" />
Wake Lock Permission - 如果你想要应用不灭屏或者进程不进入休眠状态,或者你像要在你的应用程序中使用 MediaPlayer.setScreenOnWhilePlaying() 方法或 MediaPlayer.setWakeMode() 方法, 你需要添加这个权限

二、上代码,具体实现

以下是代码的整体设计图:
这里写图片描述
从图中可以看到代码中包含一个Fragment即一个用于托管Fragemnt的Activity。MyAudioMediaPlayer是我们编写的类,用于封装MediaPlayer类。也可以选择不封装MediaPlayer类,而让Fragment直接与MediaPLayer进行交互。不过,为了保持代码的整洁与独立,这里使用了封装MediaPlayer类的设计。
(1)Fragment代码及Fragemnt的布局文件;

public class HelloMoonFragment extends Fragment {
    private MyAudioPlayer mPlayer;
    private Button mPlayButton;
    private Button mStopButton;

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_hello_moon, container, false);

        mPlayButton = (Button) view.findViewById(R.id.hellomoon_playBtn);
        mPlayButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mPlayer.play(getActivity());
            }
        });
        mStopButton = (Button) view.findViewById(R.id.hellomoon_stopBtn);
        mStopButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mPlayer.stop();
            }
        });
        return view;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        mPlayer.stop();
    }
}
<?xml version="1.0" encoding="utf-8"?>
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <ImageView
        android:src="@drawable/bv"
        android:contentDescription="图片也可以有文字属性?"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scaleType="centerInside"
        android:layout_weight="1"/>

    <TableRow
        android:gravity="center|bottom"
        android:layout_weight="0">

        <Button
            android:id="@+id/hellomoon_playBtn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="play"/>

        <Button
            android:id="@+id/hellomoon_stopBtn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="stop"/>
    </TableRow>
</TableLayout>

使用MediaPlayer之后,Fragment中要覆写onDestroy()方法,这是因为MediaPlayer运行在一个不同的线程中,这里在Fragment的销毁方法中调用MediaPlayer的销毁方法,防止MediaPlayer一直占用着音频解码硬件及其他系统资源,而这些系统资源是由所有应用所共享的。
(2)Activity代码及Activity的布局文件;

public class PaintPositionInTest extends FragmentActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        PaintPositionIn paintPositionIn = new PaintPositionIn(this);
        setContentView(R.layout.activity_hello_moon);
    }
}
<?xml version="1.0" encoding="utf-8"?>
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/helloMoonFragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:name="com.example.administrator.animation_practice.fragment.HelloMoonFragment">

</fragment>

这里用Fragemnt的静态加载方法,将其直接添加进Activity的布局文件,使用静态加载Fragment的方式失去了灵活性和掌控能力,但是对于Activity的静态部分而言,静态加载也是一个不错的选择。
(3)封装MediaPlayer类的MyAudioPlayer类;

public class MyAudioPlayer {
    private MediaPlayer mPlayer;

    public void stop(){
        if (mPlayer != null){
            mPlayer.release();
            mPlayer = null;
        }
    }

    public void play(Context c){
        stop();

        mPlayer = MediaPlayer.create(c, R.raw.one_small_step);
        mPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
            @Override
            public void onCompletion(MediaPlayer mp) {
                stop();
            }
        });
        try {
            mPlayer.prepare(); //预加载音频 使用时放置在try catch中
        } catch (IOException e) {
            e.printStackTrace();
        }
        mPlayer.start(); //正式加载音频
    }
}

play()方法中,调用对应的create(Context context, int resid)创建mPlayer实例;并且在方法的开头调用stop()方法,可避免用户多次点击Play按钮创建多个MediaPlayer实例的情况的发生。同时设立监听器,音频文件播放完之后,立即调用stop()方法,尽可能快递释放MediaPlayer实例及其占用的资源。
stop()方法中,释放MediaPlayer实例并将mPlayer变量设置为null,调用Media.release()方法,销毁该实例。
封装MediaPlayer之后,之后管理播放器的状态会更加的方便,实际开发中,相机、音频播放、视频播放等多媒体行为,生命周期、状态非常多,需要多重控制,封装代码之后可以更加方便的管理。

三、使用MediaPlayer播放视频

在Android系统中,快速刷新显示的可视图像(比如视频、相机预览)是在SurfaceView中显示的,准确地说,是在SurfaceView内嵌的Surface中显示的。通过SurfaceView的SurfaceHolder,可实现Surface上显示视频。使用MediaPlayer播放视频的关键代码在于将MediaPlayer类与SurfaceHolder关联起来,方法为:
MediaPlayer.setDisplay(SurfaceHolder);

public class MainActivity extends AppCompatActivity implements SurfaceHolder.Callback{

    ...
    private SurfaceView mSurfaceView;
    private String mMediaPath;
    private MediaPlayer mMediaPlayer;
    private SurfaceHolder mSurfaceHolder;
    ...

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //6.0及以上系统请求运行时权限 利用权限申请工具类
        requestCameraAndStoragePermission();

        mSurfaceView = (SurfaceView) findViewById(R.id.surface_view);
        mSurfaceView.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); //必须-设置Surface不需要维护自己的缓冲区
        initBtnClick();

        SurfaceHolder holder =  mSurfaceView.getHolder();
        holder.addCallback(this);
    }

    private void requestCameraAndStoragePermission() {
        ...
        ...
    };

    ......
    ......

    private void initBtnClick() {

        mPlayBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (mMediaPlayer == null) {
                    mMediaPlayer = new MediaPlayer();
                    mMediaPlayer.reset();
                    Uri uri = Uri.parse(mMediaPath);
                    mMediaPlayer = MediaPlayer.create(MainActivity.this,uri);
                    mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
                    mMediaPlayer.setDisplay(mSurfaceHolder);
                    try{
                        mMediaPlayer.prepare();
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                    mMediaPlayer.start();
                }
            }
        });

    }


    @Override
    public void surfaceCreated(SurfaceHolder surfaceHolder) {
        mSurfaceHolder = surfaceHolder;
    }

    @Override
    public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {
        mSurfaceHolder = surfaceHolder;
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
        mSurfaceView = null;
        mSurfaceHolder = null;
        releaseMediaRecorder();

        if (mCamera != null) {
            mCamera.release();
            mCamera = null;
        }
        if (mMediaPlayer != null){
            mMediaPlayer.release();
            mMediaPlayer = null;
        }
    }
}

通常来说,使用VideoView实例播放视频更加容易些,不同于SurfaceView同MediaPlayer的交互,VideoView是与MediaController交互的,这样可以方便地提供视频播放界面。

猜你喜欢

转载自blog.csdn.net/weixin_41101173/article/details/81052189