Android MediaPlayer和VideoView的使用


 
 
 

MediaPlayer

MediaPlayer類是Androd多媒體框架中的一個重要組件,通過該類,我們可以以最小的步驟來獲取,解碼和播放音視頻。它支持三種不同的媒體來源:

  • 本地資源
  • 內部URI,比如你可以通過ContentResolver來獲取
  • 外部URL(流)

對於Android支持的媒體格式列表,可見:Supported Media Formats文檔

在播放網絡上的視頻流時,Android原生的MediaPlayer支持兩種協議,HTTPRTSP,這兩種協議最大的不同是,RTSP協議支持實時流媒體的播放,而HTTP協議不支持。因為VideoView的底層實現是MediaPlayer,因此VideoView也支持以上兩種協議。 
但是Android原生MediaPalyer支持的協議(不支持RTMP、MMS等)和封裝格式實在太有限了,如果我們想播放那些它不支持的視頻,這時候就需要第三方播放器了,很多第三方播放器的底層實現都是基於FFmpeg,后續我會專門來講講第三方播放器和FFmpeg的使用。

相關方法詳解

(1)獲得MediaPlayer實例: 
可以直接new或者調用create方法創建:

MediaPlayer mp = new MediaPlayer(); MediaPlayer mp = MediaPlayer.create(this, R.raw.test); //無需再調用setDataSource

另外create還有這樣的形式:

create(Context context, Uri uri, SurfaceHolder holder) 

通過Uri和指定 SurfaceHolder 【抽象類】 創建一個多媒體播放器。

(2)設置播放文件:

MediaPlayer.create(this, R.raw.test); //①raw下的資源 mp.setDataSource("/sdcard/test.mp3"); //②本地文件路徑 mp.setDataSource("http://www.xxx.com/music/test.mp3");//③網絡URL文件

另外setDataSource()方法有多個,里面有這樣一個類型的參數:FileDescriptor 
在使用這個API的時候,需要把文件放到res文件夾平級的assets文件夾里,然后使用下述代碼設置DataSource:

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

(3)其他方法

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():停止播放

生命周期

這里寫圖片描述

  • 狀態1:Idel(空閑)狀態 
    當 mediaplayer創建或者執行reset()方法后處於這個狀態。
  • 狀態2:Initialized(已初始化)狀態 
    當調用mediaplayer的setDataResource()方法給mediaplayer設置播放的數據源后,mediaplayer會處於該狀態。
  • 狀態3:Prepared(准備就續)狀態 
    設置完數據源后,調用mediaplayer的prepare()方法,讓mediaplayer准備播放。值得一提的是,這里除了prepare()方法,還有prepareAsnyc()方法,此方法是異步方法,一般用於網絡視頻的緩沖。當緩沖完畢后,就會觸發准備完畢的事件。我們要做的就是監聽該事件(OnPreparedListener),當緩沖完成時,執行相應的操作。在此狀態上,我們可以調用seekTo()方法定位視頻,此方法不改變mediaplayer的狀態;亦可調用stop()放棄視頻播放,使mediaplayer處於Stopped狀態。一般我們會在此狀態上調用start()方法開始播放視頻。
  • 狀態4:Started(開始)狀態 
    當處於Prepared狀態、Paused狀態和PlayebackCompeleted狀態時,調用Started()方法即可進入該狀態。在該狀態中,mediaplayer開始播放視頻,可以通過seekTo()方法和start()方法改變視頻播放的進度,當Looping為真且播放完畢后,它會重新開始播放(即循環播放);否則播放完畢后,會觸發事件並調用OnCompletionaListener.OnCompletion()方法,進行特定操作,並進入PlaybackCompleted狀態。在此狀態中,亦可調用pause()方法或者stop()方法讓視頻暫停或停止,此時mediaplayer分別處於Stopped和Paused狀態。
  • 狀態5:Stopped(停止)狀態 
    當 mediaplayer處於Prepared、Started、Paused、PlaybackCompleted狀態時,調用stop()方法即可進入本狀態。應特別注意的是,在本狀態中,若想重新開始播放,不能直接調用start()方法,必須調用prepare()方法或prepareAsync()方法重新讓mediaplayer處於Prepared狀態方可調用start()方法播放視頻。
  • 狀態6:Paused(暫停)狀態 
    當mediaplayer處於Started狀態是,調用pause()方法即可進入本狀態。在本狀態里,可直接調用start()方法使,mediaplayer回到Started狀態,亦可調用stop()方法停止視頻播放,讓播放器處於停止態。
  • 狀態7:PlaybackCompleted(播放完成)狀態 
    當mediaplayer播放完成且Looping為假時即可進入本狀態。在本狀態可調用start()方法使mediaplayer回到Started狀態(注意此時是從頭開始播放);亦可調用stop()方法使mediaplayer處於停止態,結束播放。
  • 狀態8:Error(錯誤)狀態 
    當mediaplayer出現錯誤時處於此狀態。

調用release()方法即可釋放此mediaplayer對象。

使用MediaPlayer播放音頻

@Override public void onClick(View v) { switch (v.getId()){ case R.id.btn_play: if(isRelease){ mPlayer = MediaPlayer.create(this,R.raw.fly); isRelease = false; } mPlayer.start(); //開始播放 break; case R.id.btn_pause: mPlayer.pause(); //停止播放 break; case R.id.btn_stop: mPlayer.reset(); //重置MediaPlayer mPlayer.release(); //釋放MediaPlayer isRelease = true; break; }

注意事項: 
播放的是res/raw目錄下的音頻文件,創建MediaPlayer調用的是create方法,第一次啟動播放前不需要再調用prepare(),如果是使用構造方法構造的話,則需要調用一次prepare()方法! 
另外貼下官方文檔中,從其他兩種途徑播放音頻的示例代碼: 
本地Uri:

Uri myUri = ....;   /**initialize Uri here*/ MediaPlayer mediaPlayer = new MediaPlayer(); mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); mediaPlayer.setDataSource(getApplicationContext(), myUri); mediaPlayer.prepare(); mediaPlayer.start();

外部URL:

String url = "http://........"; // your URL here MediaPlayer mediaPlayer = new MediaPlayer(); mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); mediaPlayer.setDataSource(url); mediaPlayer.prepare(); // might take long! (for buffering, etc) mediaPlayer.start();

Note:假如你通過一個URL以流的形式播放在線音頻文件,該文件必須可以進行漸進式下載。

使用MediaPlayer + SurfaceView播放視頻

MediaPlayer主要用於播放音頻,沒有提供圖像輸出界面,所以我們需要借助其他的組件來顯示MediaPlayer播放的圖像輸出,我們可以使用SurfaceView來顯示。

<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <SurfaceView android:id="@+id/surfaceView" android:layout_width="fill_parent" android:layout_height="300dp" /> <LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:gravity="center_horizontal" android:orientation="horizontal" > <ImageButton android:id="@+id/btnplay" android:layout_width="60dp" android:layout_height="60dp" android:background="@mipmap/play"/> <ImageButton android:id="@+id/btnpause" android:layout_width="60dp" android:layout_height="60dp" android:background="@mipmap/pause"/> <ImageButton android:id="@+id/btnstop" android:layout_width="60dp" android:layout_height="60dp" android:background="@mipmap/stop"/> </LinearLayout> </LinearLayout>
public class MediaPlayerActivity extends Activity implements View.OnClickListener { private ImageButton btnplay, btnstop, btnpause; private SurfaceView surfaceView; private MediaPlayer mediaPlayer; private int position; private String url1 = "http://flashmedia.eastday.com/newdate/news/2016-11/shznews1125-19.mp4"; private String url2 = "rtsp://184.72.239.149/vod/mp4:BigBuckBunny_115k.mov"; private String url3 = "http://42.96.249.166/live/388.m3u8"; private String url4 = "http://61.129.89.191/ThroughTrain/download.html?id=4035&flag=-org-"; //音頻url public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_mediaplayer); btnplay = (ImageButton) this.findViewById(R.id.btnplay); btnstop = (ImageButton) this.findViewById(R.id.btnstop); btnpause = (ImageButton) this.findViewById(R.id.btnpause); btnstop.setOnClickListener(this); btnplay.setOnClickListener(this); btnpause.setOnClickListener(this); mediaPlayer = new MediaPlayer(); surfaceView = (SurfaceView) this.findViewById(R.id.surfaceView); // 設置SurfaceView自己不管理的緩沖區 surfaceView.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); surfaceView.getHolder().addCallback(new SurfaceHolder.Callback() { @Override public void surfaceDestroyed(SurfaceHolder holder) { } @Override public void surfaceCreated(SurfaceHolder holder) { if (position > 0) { try { // 開始播放 play(); // 並直接從指定位置開始播放 mediaPlayer.seekTo(position); position = 0; } catch (Exception e) { } } } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } }); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.btnplay: play(); break; case R.id.btnpause: if (mediaPlayer.isPlaying()) { mediaPlayer.pause(); } else { mediaPlayer.start(); } break; case R.id.btnstop: if (mediaPlayer.isPlaying()) { mediaPlayer.stop(); } break; default: break; } } @Override protected void onPause() { // 先判斷是否正在播放 if (mediaPlayer.isPlaying()) { // 如果正在播放我們就先保存這個播放位置 position = mediaPlayer.getCurrentPosition(); mediaPlayer.stop(); } super.onPause(); } private void play() { try { mediaPlayer.reset(); mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); // 設置需要播放的視頻 Uri uri = Uri.parse(url1); mediaPlayer.setDataSource(getApplicationContext(), uri); // 把視頻畫面輸出到SurfaceView mediaPlayer.setDisplay(surfaceView.getHolder()); mediaPlayer.prepare(); // 播放 mediaPlayer.start(); Toast.makeText(this, "開始播放!", Toast.LENGTH_LONG).show(); } catch (Exception e) { } } }

代碼很簡單,布局有個SurfaceView,然后調用getHolder獲得一個SurfaceHolder對象,在這里完成SurfaceView相關的設置,設置了類型以及一個Callback接口,重寫了SurfaceView創建時,發生變化時,以及銷毀時的三個方法!然后按鈕控制播放、暫停以及停止而已。 
這里寫圖片描述

注:這里視頻寬高總是等於定義的SurfaceView布局寬高,所以視頻可能會被拉伸變形。那么如何解決呢?

            ...

            mMediaPlayer.setOnVideoSizeChangedListener(new OnVideoSizeChangedListener(){ @Override public void onVideoSizeChanged(MediaPlayer mp, int width, int height) { if (width == 0 || height == 0) { Log.e(TAG, "invalid video width(" + width + ") or height(" + height + ")"); return; } mIsVideoSizeKnown = true; mVideoWidth = width; mVideoHeight = height; if (mIsVideoReadyToBePlayed && mIsVideoSizeKnown) { startVideoPlayback(); } } }); mMediaPlayer.setOnPreparedListener(new OnPreparedListener(){ @Override public void onPrepared(MediaPlayer mp) { mIsVideoReadyToBePlayed = true; if (mIsVideoReadyToBePlayed && mIsVideoSizeKnown) { startVideoPlayback(); } } }); private void startVideoPlayback() { holder.setFixedSize(mVideoWidth, mVideoHeight); mMediaPlayer.start(); }

VideoView

除了使用MediaPlayer + SurfaceView播放視頻的方式,我們還可以使用VideoView來直接播放視頻。SurfaceView播放視頻時,如果不進行設置,視頻寬高總是等於定義的SurfaceView布局寬高,所以視頻可能會被拉伸變形。而使用VideoView時,視頻寬度等於VideoView布局寬,但是高是自適應的,自動調整寬高比到視頻原始比例,所以不會有拉伸。

<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="com.hx.mediaplay.MainActivity" android:orientation="vertical"> <Button android:id="@+id/go" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="mediaPlayer+sufaceview" /> <EditText android:id="@+id/url" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="url" /> <Button android:id="@+id/play" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Play" /> <VideoView android:id="@+id/video" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_centerInParent="true" /> </LinearLayout>
public class MainActivity extends AppCompatActivity { private Button go; private EditText url; private Button button; private VideoView videoview; private MediaController mMediaController; private String url1 = "http://flashmedia.eastday.com/newdate/news/2016-11/shznews1125-19.mp4"; private String url2 = "rtsp://184.72.239.149/vod/mp4:BigBuckBunny_115k.mov"; private String url3 = "http://42.96.249.166/live/388.m3u8"; private String url4 = "http://61.129.89.191/ThroughTrain/download.html?id=4035&flag=-org-"; //音頻url @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); go = (Button) findViewById(R.id.go); url = (EditText) findViewById(R.id.url); button = (Button) findViewById(R.id.play); videoview = (VideoView) findViewById(R.id.video); mMediaController = new MediaController(this); videoview.setMediaController(mMediaController); url.setText(url1); button.setOnClickListener(new View.OnClickListener(){ @Override public void onClick(View v) { loadView(url.getText().toString()); } }); go.setOnClickListener(new View.OnClickListener(){ @Override public void onClick(View v) { Intent intent = new Intent(MainActivity.this, MediaPlayerActivity.class); startActivity(intent); } }); } public void loadView(String path) { Uri uri = Uri.parse(path); videoview.setVideoURI(uri); videoview.start(); videoview.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { @Override public void onPrepared(MediaPlayer mp) { // mp.setLooping(true); mp.start();// 播放 Toast.makeText(MainActivity.this, "開始播放!", Toast.LENGTH_LONG).show(); } }); videoview.setOnCompletionListener(new MediaPlayer.OnCompletionListener() { @Override public void onCompletion(MediaPlayer mp) { Toast.makeText(MainActivity.this, "播放完畢", Toast.LENGTH_SHORT).show(); } }); } }

這里寫圖片描述

Demo下載地址


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM