Android EasyPlayer声音自动停止、恢复,一键静音等功能

Android EasyPlayer声音自动停止、恢复,一键静音等功能

我们在开发播放器时,可能会需要静音或者降低音量的功能。比如说某款音乐播放器,当在后台播放时,如果此时有另外的系统通知声音发出,可能播放器会把音量降低,系统声音结束后,再调高;如果有来电了,播放器可能会把音乐暂停,等通话结束后再继续播放。还有,比方说我们在某个场合放个视频,不料音量很大,会引来很多目光(很尴尬),这时候可能我们需要一键静音的功能。那这些功能我们应该如何实现呢?

Android播放声音的类为AudioTrack,播放器会先把音频流demux出来,再decode,之后,把音频PCM数据通过AudioTrack类write到音频设备中,从而通过话筒或者扬声器发出声音。

为了方便地实现声音控制,我们需要从应用的最上层进行操作(因为底层可能已经被抽象成库了),也就是要从AudioTrack来入手。让我们看看AudioTrack的一些API吧。

int getPlayState ()
Returns the playback state of the AudioTrack instance.
获取当前的播放状态。这个接口会返回PLAYSTATE_STOPPED、PLAYSTATE_PAUSED、PLAYSTATE_PLAYING
三种状态,分别表示未播放、暂停中、正在播放
void pause ()
Pauses the playback of the audio data. Data that has not been played back will not be discarded. Subsequent calls to play() will play this data back. See flush() to discard this data.
暂停播放音频数据。已经在缓冲区中的未播放数据将不会被丢弃,在下次play的时候继续播放。调用flush则会丢弃缓冲数据。
void play ()
Starts playing an AudioTrack.
开始播放
int setStereoVolume (float leftGain,
        float rightGain)

Sets the specified left and right output gain values on the AudioTrack.
设置左右声道的音量增益。

有了这几个API,足以满足我们的需求。实现起来就非常简单了。

首先我们做一键静音功能。我们可以做个切换的按钮,这个按钮初始状态是要显示当前的播放状态:正在播放音频或未在播放音频。播放状态可以调用getPlayState ()来获取到;然后按钮按下后,再根据播放状态进行播放或暂停。

代码如下:

mAudioEnable = mAudioTrack!=null && mAudioTrack.getPlayState()==PLAYSTATE_PLAYING;

public void setAudioEnable(boolean enable) {
   mAudioEnable = enable;
   AudioTrack at = mAudioTrack;
   if (at != null) {
     synchronized (at) {
       if (!enable) {
         at.pause();
         at.flush();
       } else {
         at.flush();
         at.play();
       }
     }
   }
 }

注意这里在pause之后,play之前都调用了flush接口。这样可以确保在由暂停到播放切换时,不会把暂停时未播放的“旧数据”播放出来。

接下来我们实现音频资源被其它进程占用(失去焦点)时,自动降低声音或者停止声音;在音频资源又被释放(重新获取到焦点)时再恢复播放的功能。

我们需要通过AudioManager来判断当前音频资源的状态,并且在音频焦点更改时得到回调。其关键API接口有:

int requestAudioFocus (AudioManager.OnAudioFocusChangeListener l,
        int streamType,
        int durationHint)
Request audio focus. Send a request to obtain the audio focus
请求获取音频焦点。
第一个参数为音频焦点更改时的回调;
第二个参数为音频类型,在我们调节音量时可以看到有若干种音量,就对应的这里的streamType,这里我们基本用MUSIC,表示“媒体”。
第三个参数表示获取焦点的“时长”,有如下几种情况:
AUDIOFOCUS_GAIN_TRANSIENT
表示仅仅为临时获取焦点。比如播放导航语音、通知声音等,属于时间很短暂的情况;
AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK
表示为DUCK模式,表示当获取焦点后,允许先前获取过焦点的程序在降低输出音量的前提下继续播放。
AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE
痛第一种情况类似,只是不允许系统再播放其他声音。通常应用在语音备忘、语音识别等情况;
AUDIOFOCUS_GAIN
表示要获取焦点的时长未知。比如播放音乐等等。

当获取到焦点时,函数放回AUDIOFOCUS_REQUEST_GRANTED,当获取失败时,返回AUDIOFOCUS_REQUEST_FAILED

结合上面的API说明,参考如下代码以及解释:

// 获取AudioManager实例
final AudioManager am = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
AudioManager.OnAudioFocusChangeListener l = new AudioManager.OnAudioFocusChangeListener() {
  @Override
  public void onAudioFocusChange(int focusChange) {
    if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {// 焦点获取到了,那继续播放,并恢复音量。
      AudioTrack audioTrack = mAudioTrack;
      if (audioTrack != null) {
        audioTrack.setStereoVolume(1.0f, 1.0f);
        if (audioTrack.getPlayState() == AudioTrack.PLAYSTATE_PAUSED) {
          audioTrack.flush();
          audioTrack.play();
        }
      }
    } else if (focusChange == AudioManager.AUDIOFOCUS_LOSS) {// 焦点丢失了,暂停播放。
       AudioTrack audioTrack = mAudioTrack;
      if (audioTrack != null) {
        if (audioTrack.getPlayState() == AudioTrack.PLAYSTATE_PLAYING) {
          audioTrack.pause();
        }
      }
    } else if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) { // 焦点丢失了,但是允许在降低音量的前提下继续播放,那么降低声音。
      AudioTrack audioTrack = mAudioTrack;
      if (audioTrack != null) {
        audioTrack.setStereoVolume(0.5f, 0.5f);
      }
    }
  }
};
// 因为这里要获得的焦点无法预知时长,因此用AUDIOFOCUS_GAIN模式。
int requestCode = am.requestAudioFocus(l, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
if (requestCode == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
  // 成功获取到了焦点。那启动播放
  AudioTrack audioTrack = mAudioTrack;
  if (audioTrack != null) {
    audioTrack.setStereoVolume(1.0f, 1.0f);
    if (audioTrack.getPlayState() == AudioTrack.PLAYSTATE_PAUSED) {
      audioTrack.flush();
      audioTrack.play();
    }
  }
}else{ // 没有获取到音频焦点。那不播放声音
  AudioTrack audioTrack = mAudioTrack;
  if (audioTrack != null) {
    if (audioTrack.getPlayState() == AudioTrack.PLAYSTATE_PLAYING) {
      audioTrack.pause();
    }
  }
}

至此,我们便实现了EasyPlayer的声音自动停止、恢复,一键静音的功能的实现。看起来挺麻烦对吗?其实做一个app很容易,但是要想做的好,各种情况都兼顾了,却是很不容易的。我们不防多看些系统APP的实现,或者Google官方的一些DEMO,它们往往都看似功能很简单,会让我们觉得:“如果是我做的话,几行代码即可搞定。。”,但是它们的代码量却很大,因为它们兼顾了各种细节。而往往我们开发出来绝大多数app的都只能算是半成品,都有继续优化的余地。

感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!

(0)

相关推荐

  • Android中 视频屏幕左半部分上下滑动改变亮度右半部分上下滑动改变声音

    说明: 实现功能: (1)屏幕右半部分上滑,声音变大,下滑,声音变小 屏幕左半部分上滑,亮度变大,下滑,亮度变小 (2)如果亮度>1或者小于0.2时,手机震动 private float startY;//记录手指按下时的Y坐标 private float startX;//记录手指按下时的Y坐标 private int downVol;//记录手指按下时的音量 private Vibrator vibrator;//手机震动器 //不要忘记震动权限<uses-permission andro

  • Android中简单调用图片、视频、音频、录音和拍照的方法

    本文实例讲述了Android中简单调用图片.视频.音频.录音和拍照的方法.分享给大家供大家参考,具体如下: //选择图片 requestCode 返回的标识 Intent innerIntent = new Intent(Intent.ACTION_GET_CONTENT); //"android.intent.action.GET_CONTENT" innerIntent.setType(contentType); //查看类型 String IMAGE_UNSPECIFIED =

  • Android开发中播放声音的两种方法分析

    本文实例讲述了Android开发中播放声音的两种方法.分享给大家供大家参考,具体如下: 在Android中,音频.视频等多媒体元素的加入,使得应用程序的用户体验更好.可以说,现在的手机,已经远远不只作为通信工具,更成为娱乐.办公的必备产品. Android提供了简单的音频API.一般大家使用的是MediaPlayer播放音频,这也是最常见的一种播放声音的工具.这种工具在互联网上有大量的实例,因此在此只做简单的介绍. 对播放行为的控制是三个大家非常熟悉的方法:start().stop()和paus

  • Android编程实现播放音频的方法示例

    本文实例讲述了Android编程实现播放音频的方法.分享给大家供大家参考,具体如下: 在 Android 中播放音频文件一般都是使用 MediaPlayer 类来实现的,它对多种格式的音 频文件提供了非常全面的控制方法,从而使得播放音乐的工作变得十分简单.下表列出了 MediaPlayer 类中一些较为常用的控制方法. 方法名 功能描述 setDataSource() 设置要播放的音频文件的位置. prepare() 在开始播放之前调用这个方法完成准备工作. start() 开始或继续播放音频.

  • Android录制声音文件(音频)并播放

    本文实例为大家分享了Android录制音频文件的具体代码,供大家参考,具体内容如下 1.这个demo中没有对多次点击同一个声音文件做详细处理,偶尔会有崩溃,用的时候需要注意. 2.按住录音按钮录音过程中,只对竖直方向处理了一下,水平方向没写: 3.没有做删除某个声音文件的操作,但是测试的时候实现了功能,需要用到的话,在MainActivity->onItemClick中的TODO中有详细说明: 4.这只是个demo,如果要在项目中使用,先写出demo,没问题了,再引入项目,在写成demo后,在真

  • Android中通知Notification使用实例(振动、灯光、声音)

    本文实例讲解了通知Notification使用方法,此知识点就是用作通知的显示,包括振动.灯光.声音等效果,分享给大家供大家参考,具体内容如下 效果图: MainActivity: import java.io.File; import android.app.Activity; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; im

  • Android实现歌曲播放时歌词同步显示具体思路

    我们需要读取以上歌词文件的每一行转换成成一个个歌词实体: 复制代码 代码如下: public class LyricObject { public int begintime; // 开始时间 public int endtime; // 结束时间 public int timeline; // 单句歌词用时 public String lrc; // 单句歌词 } 可根据当前播放器的播放进度与每句歌词的开始时间,得到当前屏幕中央高亮显示的那句歌词.在UI线程中另起线程,通过回调函数 onDra

  • Android提高之MediaPlayer播放网络视频的实现方法

    前面讲解了MediaPlayer播放网络音频,主要介绍了MediaPlayer关于网络音频的缓冲和进度条控制的方法,本文再来讲解一下MediaPlayer播放网络视频的方法.播放网络视频比播放网络音频需要多一个SurfaceView而已,在已经熟悉了MediaPlayer播放网络音频之后,相信大家对本文所述的播放网络视频也能很快地掌握. 先来看看本文程序运行截图,如下所示: 本文程序的视频来自http://daily3gp.com,大家可以替换程序中的视频链接,试试其他影片. main.xml的

  • Android提高之MediaPlayer播放网络音频的实现方法

    前面有文章曾经地介绍过MediaPlayer的基本用法,这里就更加深入地讲解MediaPlayer的在线播放功能.本文主要实现MediaPlayer在线播放音频的功能,由于在线视频播放比在线音频播放复杂,因此先介绍在线音频播放的实现,这样可以帮助大家逐步深入了解MediaPlayer的在线播放功能. 先来看看本文程序运行的结果如下图所示: main.xml的源码如下: <?xml version="1.0" encoding="utf-8"?> <

  • Android如何为按键添加声音

    为程序的按钮添加按键声音能更好的亲和用户,产生更好的使用体验,下面就是为按键添加声音的方法: public class MainActivity extends Activity { private Button mButton01; private SoundPool sp;//声明一个SoundPool private int music;//定义一个整型用load():来设置suondID @Override public void onCreate(Bundle savedInstanc

  • Android使用VideoView播放本地视频和网络视频的方法

    1.效果展示 2.布局文件 <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="matc

随机推荐