使用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交互的,這樣可以方便地提供視頻播放界面。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章