Android 多媒體基礎實踐

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

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

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

3闰蚕、使用MediaPlayer類和SurfaceView來實現(xià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() 設置要播放的視頻的文件路徑
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 里强岸,對界面的按鈕和顯示位置實例化锻弓,并檢查權限
  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, "拒絕權限,無法使用程序蝌箍。", 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()方法設置要播放的資源蚌堵,可以是文件、文件路徑沛婴、或者URL吼畏。
MediaPlayer的setDataSource一共四個方法:

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

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

這是大致步驟旁仿,但只有這些是不夠的
在第二步之前需要確保surfaceHolder已經(jīng)準備好了。因此需要給surfaceHolder設置一個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設置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) 通過一個具體的路徑來設置MediaPlayer的數(shù)據(jù)源,path可以是本地的一個路徑魄健,也可以是一個網(wǎng)絡路徑
void setDataSource(Context context, Uri uri) 通過給定的Uri來設置MediaPlayer的數(shù)據(jù)源赋铝,這里的Uri可以是網(wǎng)絡路徑或是一個ContentProvider的Uri。
void setDataSource(MediaDataSource dataSource) 通過提供的MediaDataSource來設置數(shù)據(jù)源
void setDataSource(FileDescriptor fd) 通過文件描述符FileDescriptor來設置數(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) 設置是否單曲循環(huán)
void setNextMediaPlayer(MediaPlayer next) 當 當前這個MediaPlayer播放完畢后良哲,MediaPlayer next開始播放
void setWakeMode(Context context, int mode) 設置CPU喚醒的狀態(tài)。
setOnBufferingUpdateListener(MediaPlayer.OnBufferingUpdateListener listener) 網(wǎng)絡流媒體的緩沖變化時回調(diào)
setOnCompletionListener(MediaPlayer.OnCompletionListener listener) 網(wǎng)絡流媒體播放結束時回調(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>
  • 再到java文件里實現(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,"拒絕權限將無法使用程序",
                            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;
     }
    }

    //釋放相關資源
    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mediaPlayer != null){
            mediaPlayer.stop();
            mediaPlayer.release();
        }
    }
}
  • 最后千萬不要忘了權限聲明
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末洗显,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子原环,更是在濱河造成了極大的恐慌挠唆,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,681評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件嘱吗,死亡現(xiàn)場離奇詭異玄组,居然都是意外死亡,警方通過查閱死者的電腦和手機谒麦,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,205評論 3 399
  • 文/潘曉璐 我一進店門俄讹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人绕德,你說我怎么就攤上這事患膛。” “怎么了耻蛇?”我有些...
    開封第一講書人閱讀 169,421評論 0 362
  • 文/不壞的土叔 我叫張陵踪蹬,是天一觀的道長。 經(jīng)常有香客問我臣咖,道長跃捣,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,114評論 1 300
  • 正文 為了忘掉前任亡哄,我火速辦了婚禮枝缔,結果婚禮上,老公的妹妹穿的比我還像新娘蚊惯。我一直安慰自己愿卸,他們只是感情好,可當我...
    茶點故事閱讀 69,116評論 6 398
  • 文/花漫 我一把揭開白布截型。 她就那樣靜靜地躺著趴荸,像睡著了一般。 火紅的嫁衣襯著肌膚如雪宦焦。 梳的紋絲不亂的頭發(fā)上发钝,一...
    開封第一講書人閱讀 52,713評論 1 312
  • 那天顿涣,我揣著相機與錄音,去河邊找鬼酝豪。 笑死涛碑,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的孵淘。 我是一名探鬼主播蒲障,決...
    沈念sama閱讀 41,170評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼瘫证!你這毒婦竟也來了揉阎?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 40,116評論 0 277
  • 序言:老撾萬榮一對情侶失蹤背捌,失蹤者是張志新(化名)和其女友劉穎毙籽,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體毡庆,經(jīng)...
    沈念sama閱讀 46,651評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡坑赡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,714評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了扭仁。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片垮衷。...
    茶點故事閱讀 40,865評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡厅翔,死狀恐怖乖坠,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情刀闷,我是刑警寧澤熊泵,帶...
    沈念sama閱讀 36,527評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站甸昏,受9級特大地震影響顽分,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜施蜜,卻給世界環(huán)境...
    茶點故事閱讀 42,211評論 3 336
  • 文/蒙蒙 一卒蘸、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧翻默,春花似錦缸沃、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,699評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至肯污,卻和暖如春翘单,著一層夾襖步出監(jiān)牢的瞬間吨枉,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,814評論 1 274
  • 我被黑心中介騙來泰國打工哄芜, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留貌亭,地道東北人。 一個月前我還...
    沈念sama閱讀 49,299評論 3 379
  • 正文 我出身青樓认臊,卻偏偏與公主長得像属提,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子美尸,可洞房花燭夜當晚...
    茶點故事閱讀 45,870評論 2 361

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