Android 多媒體基礎(chǔ)實踐

Android三種播放視頻的方式(以下內(nèi)容大多使用真機測試,所以沒有運行圖片,大家可以自己實戰(zhàn)看看)

1、使用其自帶的播放器。指定Action為ACTION_VIEW,Data為Uri,Type為其MIME類型。

2、使用VideoView來播放。在布局文件中使用VideoView結(jié)合MediaController來實現(xiàn)對其控制。

3、使用MediaPlayer類和SurfaceView來實現(xiàn),這種方式很靈活。

使用前先添加權(quán)限:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

1、調(diào)用其自帶的播放器:

Uri uri = Uri.parse(Environment.getExternalStorageDirectory().getPath()+"/Movie.mp4");     
//調(diào)用系統(tǒng)自帶的播放器    
    Intent intent = new Intent(Intent.ACTION_VIEW);    
    Log.v("URI:::::::::", uri.toString());    
    intent.setDataAndType(uri, "video/mp4");    
    startActivity(intent);    

2、使用VideoView來播放)

使VideoView主要有以下方法:

方法名 作用
setVideoPath() 設(shè)置要播放的視頻的文件路徑
start() 開始或繼續(xù)播放
pause() 暫定播放
resume() 重新從頭開始播放
seekTo() 從指定位置開始播放
isPlaying() 判斷當前是否正在播放視頻
getDuration() 獲取載入視頻的播放時長
  • 做個小Demo練練手吧:

布局文件里放了三個Button,分別為播放,暫停,重新播放,在下面是一個VideoView控件,上代碼:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:tools="http://schemas.android.com/tools"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:orientation="vertical"
 tools:context=".MainActivity">
 
 <LinearLayout
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:orientation="horizontal">
 
  <Button
   android:id="@+id/btn_play"
   android:layout_width="0dp"
   android:layout_height="wrap_content"
   android:layout_weight="1"
   android:text="Play"
   android:textAllCaps="false" />
 
  <Button
   android:id="@+id/btn_pause"
   android:layout_width="0dp"
   android:layout_height="wrap_content"
   android:layout_weight="1"
   android:text="Pause"
   android:textAllCaps="false" />
 
  <Button
   android:id="@+id/btn_replay"
   android:layout_width="0dp"
   android:layout_height="wrap_content"
   android:layout_weight="1"
   android:text="Replay"
   android:textAllCaps="false" />
 </LinearLayout>
 <VideoView
  android:id="@+id/video_view"
  android:layout_width="match_parent"
  android:layout_height="wrap_content" />
 
</LinearLayout>

再看完整java代碼實例:

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
 
 private VideoView videoView;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  
  //在 onCreate 里,對界面的按鈕和顯示位置實例化,并檢查權(quán)限
  videoView = (VideoView)findViewById(R.id.video_view);
  Button btn_play = (Button)findViewById(R.id.btn_play);
  Button btn_pause = (Button)findViewById(R.id.btn_pause);
  Button btn_replay = (Button)findViewById(R.id.btn_replay);
 
  btn_play.setOnClickListener(this);
  btn_pause.setOnClickListener(this);
  btn_replay.setOnClickListener(this);
 
  if(ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED){
   ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
  }else {
   initVideoPath();//初始化MediaPlayer
  }
 }

單獨寫一個方法做視頻播放的初始化

 private void initVideoPath() {
  File file = new File(Environment.getExternalStorageDirectory(), "movie.mp4");
  videoView.setVideoPath(file.getPath());//指定視頻文件路徑
  videoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
   @Override
   public void onPrepared(MediaPlayer mp) {
    mp.setLooping(true);//讓電影循環(huán)播放
   }
  });
 }
 
 @Override
 public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
  switch (requestCode){
   case 1:
    if(grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){
     initVideoPath();
    }else{
     Toast.makeText(this, "拒絕權(quán)限,無法使用程序。", Toast.LENGTH_LONG).show();
     finish();
    }
    break;
   default:
    break;
  }
 }

在一個 onClick 方法中,統(tǒng)一處理 Play(播放)、Pause(暫停)、Replay(重新播放)的邏輯。

    @Override
    public void onClick(View v) {
     switch (v.getId()){
       case R.id.btn_play:
        if(!videoView.isPlaying()){
     videoView.start();//播放
    }
    break;
        case R.id.btn_pause:
    if(videoView.isPlaying()){
     videoView.pause();//暫停
    }
    break;
      case R.id.btn_replay:
    if(videoView.isPlaying()){
     videoView.resume();//重新播放
    }
    break;
    }
    }

由于視頻播放屬于比較占用資源,所以程序最后要釋放資源

 //執(zhí)行完畢,釋放所有資源。
 @Override
 protected void onDestroy() {
  super.onDestroy();
  if(videoView != null){
   videoView.suspend();
  }
 }
}

3、使用MediaPlayer,MediaPlayer優(yōu)點多,靈活性強,但是難度較大。一般我們都使用surfaceview+mediaplayer的方式來播放視頻,讓我們來好好看看如何使用它:

  • 步驟:

1)獲得MediaPlayer實例:
可以使用直接new的方式:

MediaPlayer mp = new MediaPlayer();

也可以使用create的方式,如:

MediaPlayer mp = MediaPlayer.create(this, R.raw.test);//這時就不用調(diào)用setDataSource了

調(diào)用player.setDataSource()方法設(shè)置要播放的資源,可以是文件、文件路徑、或者URL。
MediaPlayer的setDataSource一共四個方法:

setDataSource (String path) 
setDataSource (FileDescriptor fd) 
setDataSource (Context context, Uri uri) 
setDataSource (FileDescriptor fd, long offset, long length)
  1. 如何設(shè)置要播放的文件:
    MediaPlayer要播放的文件主要包括3個來源:
    a. 用戶在應(yīng)用中事先自帶的resource資源
    例如:MediaPlayer.create(this, R.raw.test);
    b. 存儲在SD卡或其他文件路徑下的媒體文件
    例如:mp.setDataSource("/sdcard/test.mp3");
    c. 網(wǎng)絡(luò)上的媒體文件
    例如:mp.setDataSource("http://www.citynorth.cn/music/confucius.mp3");(該網(wǎng)址可能已經(jīng)失效)

3)調(diào)用MediaPlayer.setDisplay(holder)設(shè)置surfaceHolder,surfaceHolder可以通過surfaceview的getHolder()方法獲得。
4)調(diào)用MediaPlayer.prepare()來準備。
5)調(diào)用MediaPlayer.start()來播放視頻。

這是大致步驟,但只有這些是不夠的
在第二步之前需要確保surfaceHolder已經(jīng)準備好了。因此需要給surfaceHolder設(shè)置一個callback,

調(diào)用addCallback()方法。Callback 有三個回調(diào)函數(shù),如下:

SurfaceHolder.Callback {
        @Override
        public void surfaceCreated(SurfaceHolder holder) {
        }

        @Override
        public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        }

        @Override
        public void surfaceDestroyed(SurfaceHolder holder) {
        }
    }

surfaceCreated()會在SurfaceHolder被創(chuàng)建的時候回調(diào),在這里可以做一些初始化的操作,surfaceDestroyed()會在SurfaceHolder被銷毀的時候回調(diào),在這里可以做一些釋放資源的操作,防止內(nèi)存泄漏。

一般,會在surfaceCreated中給MediaPlayer設(shè)置surfaceHolder。

 @Override
        public void surfaceCreated(SurfaceHolder holder) {
            player.setDisplay(holder);
        }

那么具體如何操作呢,看代碼:

public class MainActivity extends AppCompatActivity {

    private SurfaceView surfaceView;
    private MediaPlayer player;
    private SurfaceHolder holder;
    private ProgressBar progressBar;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        surfaceView = (SurfaceView) findViewById(R.id.surfaceView);
        progressBar= (ProgressBar) findViewById(R.id.progressBar);

      
        player=new MediaPlayer();
        try {
             File file = new File(Environment.getExternalStorageDirectory(),
                    "2d1c41ae2482271297c2b6b4e6abf2cf.mp4");//播放手機相冊里的視頻
             player.setDataSource(file.getPath());
            holder=surfaceView.getHolder();
            holder.addCallback(new MyCallBack());
            //player.prepare();
            player.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
                @Override
                public void onPrepared(MediaPlayer mp) {
                    progressBar.setVisibility(View.INVISIBLE);
                    player.start();
                    player.setLooping(true);
                }
            });
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private class MyCallBack implements SurfaceHolder.Callback {
        @Override
        public void surfaceCreated(SurfaceHolder holder) {
            player.setDisplay(holder);
        }

        @Override
        public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

        }

        @Override
        public void surfaceDestroyed(SurfaceHolder holder) {

        }
    }
}

MediaPlayer的具體方法介紹:

方法名 功能描述
void setDataSource(String path) 通過一個具體的路徑來設(shè)置MediaPlayer的數(shù)據(jù)源,path可以是本地的一個路徑,也可以是一個網(wǎng)絡(luò)路徑
void setDataSource(Context context, Uri uri) 通過給定的Uri來設(shè)置MediaPlayer的數(shù)據(jù)源,這里的Uri可以是網(wǎng)絡(luò)路徑或是一個ContentProvider的Uri。
void setDataSource(MediaDataSource dataSource) 通過提供的MediaDataSource來設(shè)置數(shù)據(jù)源
void setDataSource(FileDescriptor fd) 通過文件描述符FileDescriptor來設(shè)置數(shù)據(jù)源
int getCurrentPosition() 獲取當前播放的位置
int getAudioSessionId() 返回音頻的session ID
int getDuration() 得到文件的時間
TrackInfo[] getTrackInfo() 返回一個track信息的數(shù)組
boolean isLooping () 是否循環(huán)播放
boolean isPlaying() 是否正在播放
void pause() 暫停
void start() 開始
void stop() 停止
void prepare() 同步的方式裝載流媒體文件。
void prepareAsync() 異步的方式裝載流媒體文件。
void reset() 重置MediaPlayer至未初始化狀態(tài)。
void release () 回收流媒體資源。
void seekTo(int msec) 指定播放的位置(以毫秒為單位的時間)
void setAudioStreamType(int streamtype) 指定流媒體類型
void setLooping(boolean looping) 設(shè)置是否單曲循環(huán)
void setNextMediaPlayer(MediaPlayer next) 當 當前這個MediaPlayer播放完畢后,MediaPlayer next開始播放
void setWakeMode(Context context, int mode) 設(shè)置CPU喚醒的狀態(tài)。
setOnBufferingUpdateListener(MediaPlayer.OnBufferingUpdateListener listener) 網(wǎng)絡(luò)流媒體的緩沖變化時回調(diào)
setOnCompletionListener(MediaPlayer.OnCompletionListener listener) 網(wǎng)絡(luò)流媒體播放結(jié)束時回調(diào)
setOnErrorListener(MediaPlayer.OnErrorListener listener) 發(fā)生錯誤時回調(diào)
setOnPreparedListener(MediaPlayer.OnPreparedListener listener) 當裝載流媒體完畢的時候回調(diào)。

用MediaPlayer播放音頻

音頻一般也是用MediaPlayer播放的,MediaPlayer的使用上面已經(jīng)講過了,在音頻的使用和視頻大同小異,所以我們不再贅述,實戰(zhàn)感受一下吧。

我們來寫個播放音頻的項目試試

  • 布局文件很簡單,就三個Button,分別為播放,暫停,停止。
<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"
    tools:context=".MainActivity">
    <Button
        android:id="@+id/btn_play"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="點擊播放"/>
    <Button
        android:id="@+id/btn_pause"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="點擊暫停"/>
    <Button
        android:id="@+id/btn_stop"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="點擊停止播放"/>
</LinearLayout>
  • 再到j(luò)ava文件里實現(xiàn)代碼:
public class MainActivity extends AppCompatActivity implements View.OnClickListener{
private Button playBtn,resumeBtn,pauseBtn;
private MediaPlayer mediaPlayer ;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        pauseBtn = findViewById(R.id.btn_pause);
        stopBtn = findViewById(R.id.btn_stop);
        playBtn = findViewById(R.id.btn_play);

        playBtn.setOnClickListener(this);
        pauseBtn.setOnClickListener(this);
        stopBtn.setOnClickListener(this);
        if (ContextCompat.checkSelfPermission(MainActivity.this,
                Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(MainActivity.this,new String[]{
                    Manifest.permission.WRITE_EXTERNAL_STORAGE
            },1);
        }else {
             init();
        }
    }

    private void init(){
       try{
       File file = new File(Envioronment.getExternalStorageDirectory(),"2d1c41ae2482271297c2b6b4e6abf2cf.mp4");
       mediaPlayer.setDataSource(file.getPath());
       mediaPlayer.setprepare();
      
       } catch (Exception e) {
       e.printStackTrace();
       }

    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull
            int[] grantResults) {
        switch (requestCode){
            case 1:
                if (grantResults.length >0 &&
                        grantResults[0] == PackageManager.PERMISSION_GRANTED){
                    init();
                }else{
                    Toast.makeText(this,"拒絕權(quán)限將無法使用程序",
                            Toast.LENGTH_SHORT).show();
                    finish();
                }
                break;
                default:
        }
    }

    @Override
    public void onClick(View v) {
     switch (v.getId()){
         case R.id.btn_play:
             if (!mediaPlayer.isPlaying()){
                 mediaPlayer.start();//開始播放
             }
             break;
         case R.id.btn_pause:
             if (mediaPlayer.isPlaying()){
                 mediaPlayer.pause();//暫停播放
             }
             break;
         case R.id.btn_stop:
             if (mediaPlayer.isPlaying()){
                 mediaPlayer.reset();//停止播放
                 init();
             }
             break;
             default:
                 break;
     }
    }

    //釋放相關(guān)資源
    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mediaPlayer != null){
            mediaPlayer.stop();
            mediaPlayer.release();
        }
    }
}
  • 最后千萬不要忘了權(quán)限聲明
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,505評論 6 533
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,556評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 176,463評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,009評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,778評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,218評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,281評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,436評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,969評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,795評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,993評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,537評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,229評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,659評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,917評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,687評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,990評論 2 374

推薦閱讀更多精彩內(nèi)容