Android 多媒体基础实践

Android三种播放视频的方式(以下内容大多使用真机测试,所以没有运行图片,大家可以自己实战看看)

1、使用其自带的播放器。指定Action为ACTION_VIEW,Data为Uri,Type为其MIME类型。

2、使用VideoView来播放。在布局文件中使用VideoView结合MediaController来实现对其控制。

3、使用MediaPlayer类和SurfaceView来实现,这种方式很灵活。

使用前先添加权限:

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

1、调用其自带的播放器:

Uri uri = Uri.parse(Environment.getExternalStorageDirectory().getPath()+"/Movie.mp4");     
//调用系统自带的播放器    
    Intent intent = new Intent(Intent.ACTION_VIEW);    
    Log.v("URI:::::::::", uri.toString());    
    intent.setDataAndType(uri, "video/mp4");    
    startActivity(intent);    

2、使用VideoView来播放)

使VideoView主要有以下方法:

方法名 作用
setVideoPath() 设置要播放的视频的文件路径
start() 开始或继续播放
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);//让电影循环播放
   }
  });
 }
 
 @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 方法中,统一处理 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;
    }
    }

由于视频播放属于比较占用资源,所以程序最后要释放资源

 //执行完毕,释放所有资源。
 @Override
 protected void onDestroy() {
  super.onDestroy();
  if(videoView != null){
   videoView.suspend();
  }
 }
}

3、使用MediaPlayer,MediaPlayer优点多,灵活性强,但是难度较大。一般我们都使用surfaceview+mediaplayer的方式来播放视频,让我们来好好看看如何使用它:

  • 步骤:

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

MediaPlayer mp = new MediaPlayer();

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

MediaPlayer mp = MediaPlayer.create(this, R.raw.test);//这时就不用调用setDataSource了

调用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. 网络上的媒体文件
    例如:mp.setDataSource("http://www.citynorth.cn/music/confucius.mp3");(该网址可能已经失效)

3)调用MediaPlayer.setDisplay(holder)设置surfaceHolder,surfaceHolder可以通过surfaceview的getHolder()方法获得。
4)调用MediaPlayer.prepare()来准备。
5)调用MediaPlayer.start()来播放视频。

这是大致步骤,但只有这些是不够的
在第二步之前需要确保surfaceHolder已经准备好了。因此需要给surfaceHolder设置一个callback,

调用addCallback()方法。Callback 有三个回调函数,如下:

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被创建的时候回调,在这里可以做一些初始化的操作,surfaceDestroyed()会在SurfaceHolder被销毁的时候回调,在这里可以做一些释放资源的操作,防止内存泄漏。

一般,会在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的数据源,path可以是本地的一个路径,也可以是一个网络路径
void setDataSource(Context context, Uri uri) 通过给定的Uri来设置MediaPlayer的数据源,这里的Uri可以是网络路径或是一个ContentProvider的Uri。
void setDataSource(MediaDataSource dataSource) 通过提供的MediaDataSource来设置数据源
void setDataSource(FileDescriptor fd) 通过文件描述符FileDescriptor来设置数据源
int getCurrentPosition() 获取当前播放的位置
int getAudioSessionId() 返回音频的session ID
int getDuration() 得到文件的时间
TrackInfo[] getTrackInfo() 返回一个track信息的数组
boolean isLooping () 是否循环播放
boolean isPlaying() 是否正在播放
void pause() 暂停
void start() 开始
void stop() 停止
void prepare() 同步的方式装载流媒体文件。
void prepareAsync() 异步的方式装载流媒体文件。
void reset() 重置MediaPlayer至未初始化状态。
void release () 回收流媒体资源。
void seekTo(int msec) 指定播放的位置(以毫秒为单位的时间)
void setAudioStreamType(int streamtype) 指定流媒体类型
void setLooping(boolean looping) 设置是否单曲循环
void setNextMediaPlayer(MediaPlayer next) 当 当前这个MediaPlayer播放完毕后,MediaPlayer next开始播放
void setWakeMode(Context context, int mode) 设置CPU唤醒的状态。
setOnBufferingUpdateListener(MediaPlayer.OnBufferingUpdateListener listener) 网络流媒体的缓冲变化时回调
setOnCompletionListener(MediaPlayer.OnCompletionListener listener) 网络流媒体播放结束时回调
setOnErrorListener(MediaPlayer.OnErrorListener listener) 发生错误时回调
setOnPreparedListener(MediaPlayer.OnPreparedListener listener) 当装载流媒体完毕的时候回调。

用MediaPlayer播放音频

音频一般也是用MediaPlayer播放的,MediaPlayer的使用上面已经讲过了,在音频的使用和视频大同小异,所以我们不再赘述,实战感受一下吧。

我们来写个播放音频的项目试试

  • 布局文件很简单,就三个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文件里实现代码:
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();
        }
    }
}
  • 最后千万不要忘了权限声明
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,723评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,080评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,604评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,440评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,431评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,499评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,893评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,541评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,751评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,547评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,619评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,320评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,890评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,896评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,137评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,796评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,335评论 2 342

推荐阅读更多精彩内容