开源安卓Android流媒体音视频播放器实现声音自动停止、恢复、一键静音功能源码
本文转自EasyDarwin团队John的博客:http://blog.csdn.net/jyt0551/article/details/60802145
我们在开发安卓Android流媒体音视频播放器时,可能会需要播放器静音或者降低音量的功能。比如说某款音乐播放器,当在后台播放时,如果此时有另外的系统通知声音发出,可能播放器会把音量降低,系统声音结束后,再调高;如果有来电了,播放器可能会把音乐暂停,等通话结束后再继续播放。还有,比方说我们在某个场合放个视频,不料音量很大,会引来很多目光(很尴尬),这时候可能我们需要一键静音的功能。那这些功能我们应该如何实现呢?
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的都只能算是半成品,都有继续优化的余地。
获取更多信息
Copyright © EasyDarwin.org 2012-2017
开源安卓Android流媒体音视频播放器实现声音自动停止、恢复、一键静音功能源码的更多相关文章
- 分享几个不错的Android开源音视频播放器
整理了一下Github上几个开源的音视频播放器项目,有兴趣的同学可以clone代码去研究学习. UniversalMusicPlayer https://github.com/googlesamp ...
- 一些不错的Android开源音视频播放器
摘要:来自Github上的一点点整理,希望对你有用! 整理了一下Github上几个开源的音视频播放器项目,有兴趣的同学可以clone代码去研究学习. 1.UniversalMusicPlayer ht ...
- Pyqt 音视频播放器
在寻找如何使用Pyqt做一个播放器时首先找到的是openCV2 openCV2 貌似太强大了,各种关于图像处理的事情它都能完成,如 读取摄像头.图像识别.人脸识别. 图像灰度处理 . 播放视频等,强 ...
- 一款开源免费跨浏览器的视频播放器--videojs使用介绍
最近项目中的视频功能,需要做到浏览器全兼容,所以之前用html5实现的视频功能就需要进行改造了.在网上翻了个遍,试来试去,在所有的视频播放器中,就数它最实际了.首先我们来看看它的优点: 1.它是开源免 ...
- 【转】一款开源免费跨浏览器的视频播放器--videojs使用介绍
特别提示:本人博客部分有参考网络其他博客,但均是本人亲手编写过并验证通过.如发现博客有错误,请及时提出以免误导其他人,谢谢!欢迎转载,但记得标明文章出处:http://www.cnblogs.com/ ...
- 自己做的一个android 音视频播放器
欢迎大家下载: http://download.csdn.net/detail/q610098308/8504335
- 开源可扩展的Web视频播放器:Clappr Player
http://www.open-open.com/lib/view/open1417057033846.html http://www.csdn.net/article/2014-11-27/2822 ...
- android形状属性、锁屏密码、动态模糊、kotlin项目、抖音动画、记账app、视频播放器等源码
Android精选源码 直观了解Android的"形状"属性如何影响Drawable的外观. 一个灵活的视频播放器, 可替换播放器内核. android锁屏输入密码功能源码 背景动 ...
- 安卓视频播放器(VideoView)
VideoView是安卓自带的视频播放器类,该类集成有显示和控制两大部分,在布局文件中添加VideoView然后在java文件中简单的调用控制命令,即可实现本地或者网络视频的播放.本章实现视频的居中播 ...
随机推荐
- 系统软键盘">Android在外接物理键盘时,如何强制调用系统软键盘?
第一次写,写的不好请见谅 物理键盘映射过程: 手机/system/usr/keylayout/*.kl :内核将keyCode映射成有含义的字符串KeycodeLabels.h : framework ...
- dpkg --add-architecture i386 && apt-get update && > apt-get install wine32
dpkg --add-architecture i386 && apt-get update &&> apt-get install wine32
- LeetCode OJ——Longest Valid Parentheses
http://oj.leetcode.com/problems/longest-valid-parentheses/ 最大括号匹配长度,括号是可以嵌套的 #include <string> ...
- js中加“var”和不加“var”的区别,看完觉得这么多年js白学了
Javascript声明变量的时候,虽然用var关键字声明和不用关键字声明,很多时候运行并没有问题,但是这两种方式还是有区别的.可以正常运行的代码并不代表是合适的代码. var num = 1: 是在 ...
- AC日记——[SDOI2015]星际战争 洛谷 P3324
题目描述 3333年,在银河系的某星球上,X军团和Y军团正在激烈地作战. 在战斗的某一阶段,Y军团一共派遣了N个巨型机器人进攻X军团的阵地,其中第i个巨型机器人的装甲值为Ai.当一个巨型机器人的装甲值 ...
- Codeforces Round #439 (Div. 2) 题解
题目链接 Round 439 div2 就做了两道题TAT 开场看C题就不会 然后想了好久才想到. 三种颜色挑出两种算方案数其实是独立的,于是就可以乘起来了. E题想了一会有了思路,然后YY出了一种 ...
- SpringCloud 分布式事务解决方案
目录 TX-LCN分布式事务框架 TX-LCN分布式事务框架 随着互联化的蔓延,各种项目都逐渐向分布式服务做转换.如今微服务已经普遍存在,本地事务已经无法满足分布式的要求,由此分布式事务问题诞生. 分 ...
- [Bzoj5358][Lydsy1805月赛]口算训练(预处理+动态开点线段树)
5358: [Lydsy1805月赛]口算训练 Time Limit: 5 Sec Memory Limit: 512 MBSubmit: 318 Solved: 105[Submit][Stat ...
- 在谈MVP之前,你真的懂MVC吗?
最近看到很多文章在谈论MVP或者MVVM模式的,但其实无论MVP还是MVVM都只是MVC模式的一种变种.而如果你对MVC的设计理念都还没有理解透彻,那么即使换成MVP亦或MVVM也不可能让你杂乱不堪的 ...
- SilverLight-DataControls:四、The PagedCollectionView(分页的集合视图) 对象
ylbtech-SilverLight-DataControls-PagedCollectionView:The PagedCollectionView(分页的集合视图) 对象 1.A, Buildi ...