MediaPlayer 音频播放 示例

状态机、流程图、生命周期

对播放音频/视频文件和流的控制是通过一个状态机来管理的。下图显示一个MediaPlayer对象被支持的播放控制操作驱动的生命周期和状态。

椭圆代表MediaPlayer对象可能驻留的状态,弧线表示驱动MediaPlayer在各个状态之间迁移的播放控制操作。

这里有两种类型的弧线:由一个箭头开始的弧代表同步的方法调用,而以双箭头开头的代表的弧线代表异步方法调用。



通过这张图,我们可以知道一个MediaPlayer对象有以下的状态:

1、当一个MediaPlayer对象被刚刚用new创建或是调用了reset()方法后,它就处于Idle状态。当调用了release()方法后,它就处于End状态。这两种状态之间是MediaPlayer对象的生命周期。
  • 1.1) 在一个新构建的MediaPlayer对象和一个调用了reset()方法的MediaPlayer对象之间有一个微小的但是十分重要的差别。在处于Idle状态时,调用getCurrentPosition(), getDuration(), getVideoHeight(), getVideoWidth(), setAudioStreamType(int), setLooping(boolean), setVolume(float, float), pause(), start(), stop(), seekTo(int), prepare() 或者 prepareAsync() 方法都是编程错误。当一个MediaPlayer对象刚被构建的时候,内部的播放引擎和对象的状态都没有改变,在这个时候调用以上的那些方法,框架将无法回调客户端程序注册的OnErrorListener.onError()方法;但若这个MediaPlayer对象调用了reset()方法之后,再调用以上的那些方法,内部的播放引擎就会回调客户端程序注册的OnErrorListener.onError()方法了,并将错误的状态传入。
  • 1.2) 我们建议,一旦一个MediaPlayer对象不再被使用,应立即调用release()方法来释放在内部的播放引擎中与这个MediaPlayer对象关联的资源。资源可能包括如硬件加速组件的单态组件,若没有调用release()方法可能会导致之后的MediaPlayer对象实例无法使用这种单态硬件资源,从而退回到软件实现或运行失败。一旦MediaPlayer对象进入了End状态,它不能再被使用,也没有办法再迁移到其它状态。
  • 1.3) 此外,使用new操作符创建的MediaPlayer对象处于Idle状态,而那些通过重载的create()便利方法创建的MediaPlayer对象却不是处于Idle状态。事实上,如果成功调用了重载的create()方法,那么这些对象已经是Prepare状态了。
  2、 在一般情况下,由于种种原因一些播放控制操作可能会失败,如不支持的音频/视频格式,缺少隔行扫描的音频/视频,分辨率太高,流超时等原因,等等。因此,错误报告和恢复在这种情况下是非常重要的。有时,由于编程错误,在处于无效状态的情况下调用了一个播放控制操作可能发生。在所有这些错误条件下,内部的播放引擎会调用一个由客户端程序员提供的OnErrorListener.onError()方法。客户端程序员可以通过调用MediaPlayer.setOnErrorListener(android.media.MediaPlayer.OnErrorListener)方法来注册OnErrorListener.
  • 2.1) 一旦发生错误,MediaPlayer对象会进入到Error状态。
  • 2.2) 为了重用一个处于Error状态的MediaPlayer对象,可以调用reset()方法来把这个对象恢复成Idle状态。
  • 2.3) 注册一个OnErrorListener来获知内部播放引擎发生的错误是好的编程习惯。
  • 2.4) 在不合法的状态下调用一些方法,如prepare(),prepareAsync()和setDataSource()方法会抛出IllegalStateException异常。
  3、 调用setDataSource(***)方法方法会使处于Idle状态的对象迁移到Initialized状态。
  • 3.1) 若当此MediaPlayer处于其它的状态下,调用setDataSource()方法,会抛出IllegalStateException异常。
  • 3.2) 好的编程习惯是不要疏忽了调用setDataSource()方法的时候可能会抛出的IllegalArgumentException异常和IOException异常。
  4、在开始播放之前,MediaPlayer对象必须要进入Prepared状态。
  • 4.1) 有两种方法(同步和异步)可以使MediaPlayer对象进入Prepared状态:要么调用prepare()方法,此方法返回就表示该MediaPlayer对象已经进入了Prepared状态;要么调用prepareAsync()方法,此方法会使此MediaPlayer对象进入Preparing状态并返回,而内部的播放引擎会继续未完成的准备工作。当同步版本返回时或异步版本的准备工作完全完成时就会调用客户端程序员提供的OnPreparedListener.onPrepared()监听方法。可以调用MediaPlayer.setOnPreparedListener(android.media.MediaPlayer.OnPreparedListener)方法来注册OnPreparedListener.
  • 4.2) Preparing是一个中间状态,在此状态下调用任何具备边影响的方法的结果都是未知的!
  • 4.3) 在不合适的状态下调用prepare()和prepareAsync()方法会抛出IllegalStateException异常。当MediaPlayer对象处于Prepared状态的时候,可以调整音频/视频的属性,如音量,播放时是否一直亮屏,循环播放等。
  5、要开始播放,必须调用start()方法。当此方法成功返回时,MediaPlayer的对象处于Started状态。isPlaying()方法可以被调用来测试某个MediaPlayer对象是否在Started状态。
  • 5.1) 当处于Started状态时,内部播放引擎会调用客户端程序员提供的OnBufferingUpdateListener.onBufferingUpdate()回调方法,此回调方法允许应用程序追踪流播放的缓冲的状态。
  • 5.2) 对一个已经处于Started 状态的MediaPlayer对象调用start()方法没有影响。
  6、播放可以被暂停,停止,以及调整当前播放位置。当调用pause()方法并返回时,会使MediaPlayer对象进入Paused状态。注意Started与Paused状态的相互转换在内部的播放引擎中是异步的。所以可能需要一点时间在isPlaying()方法中更新状态,若在播放流内容,这段时间可能会有几秒钟。
  • 6.1) 调用start()方法会让一个处于Paused状态的MediaPlayer对象从之前暂停的地方恢复播放。当调用start()方法返回的时候,MediaPlayer对象的状态会又变成Started状态。
  • 6.2) 对一个已经处于Paused状态的MediaPlayer对象pause()方法没有影响。
  7、调用stop()方法会停止播放,并且还会让一个处于Started,Paused,Prepared或PlaybackCompleted状态的MediaPlayer进入Stopped状态。
  • 7.1) 对一个已经处于Stopped状态的MediaPlayer对象stop()方法没有影响。
  8、调用seekTo()方法可以调整播放的位置。
  • 8.1) seekTo(int)方法是异步执行的,所以它可以马上返回,但是实际的定位播放操作可能需要一段时间才能完成,尤其是播放流形式的音频/视频。当实际的定位播放操作完成之后,内部的播放引擎会调用客户端程序员提供的OnSeekComplete.onSeekComplete()回调方法。可以通过setOnSeekCompleteListener(OnSeekCompleteListener)方法注册。
  • 8.2) 注意,seekTo(int)方法也可以在其它状态下调用,比如Prepared,Paused和PlaybackCompleted状态。此外,目前的播放位置,实际可以调用getCurrentPosition()方法得到,它可以帮助如音乐播放器的应用程序不断更新播放进度
9、当播放到流的末尾,播放就完成了。
  • 9.1) 如果调用了setLooping(boolean)方法开启了循环模式,那么这个MediaPlayer对象会重新进入Started状态。
  • 9.2) 若没有开启循环模式,那么内部的播放引擎会调用客户端程序员提供的OnCompletion.onCompletion()回调方法。可以通过调用MediaPlayer.setOnCompletionListener(OnCompletionListener)方法来设置。内部的播放引擎一旦调用了OnCompletion.onCompletion()回调方法,说明这个MediaPlayer对象进入了PlaybackCompleted状态。
  • 9.3) 当处于PlaybackCompleted状态的时候,可以再调用start()方法来让这个MediaPlayer对象再进入Started状态。

常用API


静态构造方法
  • public static MediaPlayer create(Context context, Uri uri, SurfaceHolder holder)    指定从资源ID对应的资源文件中来装载音乐文件,同时指定了SurfaceHolder对象并返回MediaPlyaer对象
  • public static MediaPlayer create(Context context, int resid)    指定从资源ID对应的资源文件中来装载音乐文件,并返回MediaPlyaer对象
  • public static MediaPlayer create(Context context, Uri uri)    指定从Uri对应的资源文件中来装载音乐文件,并返回MediaPlyaer对象

常用方法,全部是 public void 格式的
  • start ()    开始或恢复播放
  • stop()    停止播放
  • pause()    暂停播放
  • setDataSource (String path)    从指定的装载path路径所代表的文件
  • setDataSource (FileDescriptor fd, long offset, long length)    指定装载fd所代表的文件中从offset开始、长度为length的文件内容
  • setDataSource (FileDescriptor fd)    指定装载fd所代表的文件
  • setDataSource (Context context, Uri uri)    指定装载uri所代表的文件
  • setDataSource (Context context, Uri uri, Map<String, String> headers)    指定装载uri所代表的文件
  • prepare()    准备,setDataSource()方法之后,MediaPlayer并未去装载音频文件,调用prepare()后才去准备音频
  • prepareAsync()    异步准备
  • setLooping(boolean looping)    设置是否循环播放这个音乐文件
  • setSurface(Surface surface)    设置Surface
  • setVolume(float leftVolume,float rightVolume)    设置音量
  • setDisplay(SurfaceHolder sh)    设置显示方式
  • seekTo(int mses)    寻求指定的时间位置。
  • isLooping()    判断是否循环播放
  • isPlaying()    判断是否正在播放
  • release()    释放相关该MediaPlayer对象的资源。

绑定事件监听器   
  • setOnCompletionListener (MediaPlayer.OnCompletionListener listener)    为MediaPlayer的播放完成事件绑定事件监听器
  • setOnErrorListener (MediaPlayer.OnErrorListener listener)    为MediaPlayer的播放错误事件绑定事件监听器
  • setOnPreparedListener (MediaPlayer.OnPreparedListener listener)    当MediaPlayer调用prepare()方法时触发该监听器
  • setOnSeekCompleteListener (MediaPlayer.OnSeekCompleteListener listener)    当MediaPlayer调用seek()方法的时候触发该监听器

示例说明

 

权限

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

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

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

代码

// 项目中,这些播放等方法都是放在服务中的,通过绑定服务,调用服务的方法。目的:防止后台播放时被系统回收

public class MainActivity extends Activity implements OnClickListener {

    private EditText et_path, et_Url;

    private Button bt_play, bt_playUrl, bt_pause, bt_stop, bt_replay;

    private MediaPlayer mediaPlayer;//多媒体播放器

    private static final String STATE_CONTINUE = "继续";

    private static final String STATE_PAUSE = "暂停";

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

        et_path = (EditText) findViewById(R.id.et_path);

        et_Url = (EditText) findViewById(R.id.et_Url);

        bt_play = (Button) findViewById(R.id.bt_play);

        bt_playUrl = (Button) findViewById(R.id.bt_playUrl);

        bt_pause = (Button) findViewById(R.id.bt_pause);

        bt_stop = (Button) findViewById(R.id.bt_stop);

        bt_replay = (Button) findViewById(R.id.bt_replay);

        bt_play.setOnClickListener(this);

        bt_playUrl.setOnClickListener(this);

        bt_pause.setOnClickListener(this);

        bt_stop.setOnClickListener(this);

        bt_replay.setOnClickListener(this);

        mediaPlayer = new MediaPlayer();         mediaPlayer.setOnCompletionListener(new OnCompletionListener() {//播放完毕后回调                     public void onCompletion(MediaPlayer mp) {                         Toast.makeText(MainActivity.this, "播放完毕!", 0).show();                         mediaPlayer.reset();//MediaPlayer同时只能播放一个音乐文件,若要播另一个音乐文件,需先设置为初始状态                         bt_playUrl.setEnabled(true);                         bt_play.setEnabled(true);                     }                 });         mediaPlayer.setOnPreparedListener(new OnPreparedListener() {//准备完毕后回调                     @Override                     public void onPrepared(MediaPlayer mp) {                         mediaPlayer.start();//只有准备好以后才能播放                         Toast.makeText(MainActivity.this, "哈哈,准备好了!", 0).show();                     }                 });         mediaPlayer.setOnErrorListener(new OnErrorListener() {             @Override             public boolean onError(MediaPlayer paramMediaPlayer, int paramInt1, int paramInt2) {                 Toast.makeText(MainActivity.this, "报错了--" + paramInt1 + "--" + paramInt2, 0).show();                 return false;             }         });     }
    @Override     protected void onDestroy() {         super.onDestroy();         if (mediaPlayer != null) {             mediaPlayer.stop();             mediaPlayer.release();//释放播放器资源             mediaPlayer = null;         }     }     @Override     public void onClick(View v) {         switch (v.getId()) {         case R.id.bt_play:             play();             break;         case R.id.bt_playUrl:             playUrl();             break;         case R.id.bt_pause:             pause();             break;         case R.id.bt_stop:             stop();             break;         case R.id.bt_replay:             replay();             break;         default:             break;         }     }
    //******************************************************************************************************************     /**      * 播放本地音乐      */     public void play() {         String filepath = et_path.getText().toString().trim();         File file = new File(filepath);         if (file.exists()) {             try {                 mediaPlayer.setDataSource(filepath);//设置播放的数据源                 mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);//设置音频流的类型,不是必须的                 mediaPlayer.prepare();//准备开始播放,prepare方法是native类型的,播放的逻辑是由c代码在新的线程里面执行的                 bt_play.setEnabled(false);//播放时将“播放”按钮设置为不可点击                 bt_playUrl.setEnabled(false);             } catch (Exception e) {                 e.printStackTrace();                 Toast.makeText(this, "请检查是否有写SD卡权限", 0).show();             }         } else {             Toast.makeText(this, "文件不存在", 0).show();         }     }     /**      * 播放网络音乐      */     public void playUrl() {         String url = et_Url.getText().toString().trim();         if (!TextUtils.isEmpty(url)) {             try {                 mediaPlayer.setDataSource(url);//参数可以直接是网络路径                 mediaPlayer.prepareAsync();//异步准备                 bt_playUrl.setEnabled(false);//准备时就将“播放”按钮设置为不可点击                 bt_play.setEnabled(false);                 Toast.makeText(MainActivity.this, "准备中,可能需要点时间……", 1).show();             } catch (Exception e) {                 e.printStackTrace();                 Toast.makeText(this, "播放失败,请检查是否有网络权限", 0).show();             }         } else {             Toast.makeText(this, "路径不能为空", 0).show();         }     }     /**      * 暂停      */     public void pause() {         if (mediaPlayer != null) {             if (mediaPlayer.isPlaying()) {//只有播放器已初始化并且正在播放才可暂停                 mediaPlayer.pause();                 bt_pause.setText(STATE_CONTINUE);             } else {                 mediaPlayer.start();                 bt_pause.setText(STATE_PAUSE);                 return;             }         }     }     /**      * 停止      */     public void stop() {         if (mediaPlayer != null && mediaPlayer.isPlaying()) {             mediaPlayer.stop();         }         mediaPlayer.reset();         bt_play.setEnabled(true);         bt_playUrl.setEnabled(true);         bt_pause.setText("暂停");     }     /**      * 重播      */     public void replay() {         if (mediaPlayer != null) {             mediaPlayer.start();//这里调用start方法没意义,对一个已经处于Started 状态的MediaPlayer对象调用start()方法没有影响             mediaPlayer.seekTo(0);//重头开始播放本音乐         }         bt_pause.setText("暂停");     } }

布局


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    android:layout_gravity="center_horizontal"

    android:orientation="vertical" >

    <EditText

        android:id="@+id/et_path"

        android:layout_width="fill_parent"

        android:layout_height="wrap_content"

        android:text="/sdcard/1.mp3" />

    <EditText

        android:id="@+id/et_Url"

        android:layout_width="fill_parent"

        android:layout_height="wrap_content"

        android:text="http://music.baidutt.com/up/kwcywukk/ydsspc.mp3" />

    <LinearLayout

        android:layout_width="match_parent"

        android:layout_height="wrap_content"

        android:orientation="horizontal" >

        <Button

            android:id="@+id/bt_play"

            android:layout_width="0dp"

            android:layout_height="wrap_content"

            android:layout_weight="1"

            android:text="播放本地音乐" />

        <Button

            android:id="@+id/bt_playUrl"

            android:layout_width="0dp"

            android:layout_height="wrap_content"

            android:layout_weight="1"

            android:text="播放网络音乐" />

    </LinearLayout>

    <LinearLayout

        android:layout_width="match_parent"

        android:layout_height="wrap_content"

        android:orientation="horizontal" >

        <Button

            android:id="@+id/bt_pause"

            android:layout_width="0dp"

            android:layout_height="wrap_content"

            android:layout_weight="1"

            android:text="暂停" />

        <Button

            android:id="@+id/bt_stop"

            android:layout_width="0dp"

            android:layout_height="wrap_content"

            android:layout_weight="1"

            android:text="停止" />

        <Button

            android:id="@+id/bt_replay"

            android:layout_width="0dp"

            android:layout_height="wrap_content"

            android:layout_weight="1"

            android:text="重播" />

    </LinearLayout>

</LinearLayout>

来自为知笔记(Wiz)

时间: 2024-11-08 20:48:34

MediaPlayer 音频播放 示例的相关文章

最简单的视音频播放示例6:OpenGL播放YUV420P(通过Texture,使用Shader)

本文记录OpenGL播放视频的技术.上一篇文章中,介绍了一种简单的使用OpenGL显示视频的方式.但是那还不是OpenGL显示视频技术的精髓.和Direct3D一样,OpenGL更好的显示视频的方式也是通过纹理(Texture).本文介绍OpenGL通过纹理的方式显示视频的技术. OpenGL中坐标和Direct3D坐标的不同 OpenGL中的纹理的坐标和Direct3D中的坐标是不一样的. 在Direct3D中.纹理坐标如下图所示.取值是0到1.坐标系原点在左上角. 物体表面坐标如下图所示.取

最简单的视音频播放示例1:总述

前言 最近研究了一下Windows平台下的视音频播放的技术.在Windows平台下的视频播放技术主要有以下三种:GDI,Direct3D和OpenGL:音频播放技术主要是DirectSound.这些技术属于比较底层的技术,因此使用起来相对来说还是比较复杂的.我在学习的过程中也发现这一领域一直缺少比较简单直观的示例程序,因此打算做些示例程序,同时写一些相关的文章,方便这一领域的人学习相关的知识. 打算重点记录一下视频播放的技术.之前的研究一直集中于视频的编解码方面的技术,而很少接触到视频播放显示方

最简单的视音频播放示例8:DirectSound播放PCM

本文记录DirectSound播放音频的技术.DirectSound是Windows下最常见的音频播放技术.目前大部分的音频播放应用都是通过DirectSound来播放的.本文记录一个使用DirectSound播放PCM的例子.注:一位仁兄已经提醒我DirectSound已经计划被XAudio2取代了.后来考证了一下发现确有此事.因此在下次更新中考虑加入XAudio2播放PCM的例子.本文仍然记录一下DirectSound这位“元老”. DirectSound简介 DirectSound是微软所

最简单的视音频播放示例9:SDL2播放PCM

本文记录SDL播放音频的技术.在这里使用的版本是SDL2.实际上SDL本身并不提供视音频播放的功能,它只是封装了视音频播放的底层API.在Windows平台下,SDL封装了Direct3D这类的API用于播放视频:封装了DirectSound这类的API用于播放音频.因为SDL的编写目的就是简化视音频播放的开发难度,所以使用SDL播放视频(YUV/RGB)和音频(PCM)数据非常的容易. SDL简介 SDL(Simple DirectMedia Layer)是一套开放源代码的跨平台多媒体开发库,

最简单的视音频播放示例3:Direct3D播放YUV,RGB(通过Surface)

上一篇文章记录了GDI播放视频的技术.打算接下来写两篇文章记录Direct3D(简称D3D)播放视频的技术.Direct3D应该Windows下最常用的播放视频的技术.实际上视频播放只是Direct3D的"副业",它主要用于3D游戏制作.当前主流的游戏几乎都是使用Direct3D制作的,例如<地下城与勇士>,<穿越火线>,<英雄联盟>,<魔兽世界>,<QQ飞车>等等.使用Direct3D可以用两种方式渲染视频:Surface和

最简单的视音频播放示例2:GDI播放YUV, RGB

前一篇文章对"Simplest Media Play"工程作了概括性介绍.后续几篇文章打算详细介绍每个子工程中的几种技术.在记录Direct3D,OpenGL这两种相对复杂的技术之前,打算先记录一种和它们属于同一层面的的简单的技术--GDI作为热身. GDI简介 下面这段文字摘自维基百科: 图形设备接口(Graphics Device Interface或Graphical Device Interface,缩写GDI),是微软公司视窗操作系统(Microsoft Windows)的三

最简单的视音频播放示例4:Direct3D播放RGB(通过Texture)

本文接着上一篇文章继续记录Direct3D(简称D3D)播放视频的技术.上一篇文章中已经记录了使用Direct3D中的Surface渲染视频的技术.本文记录一种稍微复杂但是更加灵活的渲染视频的方式:使用Direct3D中的Texture(纹理)渲染视频. 纹理有关的基础知识 在记录使用Direct3D的Texture渲染视频的技术之前,首先记录一下有关纹理的基础知识.我自己归纳总结了以下几点知识. 1. 渲染(Render),纹理(Texture) 刚开始学习Direct3D显示视频技术的人一定

最简单的视音频播放示例4:OpenGL播放RGB/YUV

本文记录OpenGL播放视频的技术.OpenGL是一个和Direct3D同一层面的技术.相比于Direct3D,OpenGL具有跨平台的优势.尽管在游戏领域,DirectX的影响力已渐渐超越OpenGL并被大多数PC游戏开发商所采用,但在专业高端绘图领域,OpenGL因为色彩准确,仍然是不能被取代的主角. OpenGL简介 从网上搜集了一些有关OpenGL简介方面的知识,在这里列出来.开放图形库(英语:Open Graphics Library,缩写为OpenGL)是个定义了一个跨编程语言.跨平

Android 音频播放——AudioTrack直接播PCM、MediaPlayer播媒体文件可以是audio

http://blog.csdn.net/java_android_c/article/details/52678265 Android平台播放音频的方式一般有3种.1.利用系统内置的应用程序播放音频    2.利用AudioTrack播放原始音频   3.使用MediaPlayer播放.此3种音频播放方式,以第三种MediaPlayer播放这种方式使用的最多,必须掌握! 一.使用系统内置的程序. Google想的"周到",一般都给我们提供了一些内置程序,然而这些内置程序的UI效果,那