Android语音消息播放,Android播放audio音频踩坑实践

视频播放–踩坑小计

2018/06/09 · JavaScript
· 视频

初稿出处:
chenjsh36   

 

随着流量时代的赶到和硬件技术的升官,更多的网站希望能在PC端或运动端播放自己的摄像,而
<video>的包容性的逐渐完善,使得开发者更愿意利用它来促成视频播放场景。

本篇小说主要罗列__摄像播放的通用场景及各场景下踩过的坑__,希望能__帮衬开发者在蒙受须求开发时能更快地接纳合适的技巧方案同时收缩采坑的次数__。

相见的题材

集团官网首页需求播放一段介绍公司情状的视频,类似于宣传属性!给到自身的是
mp5 格式的视频 300 MB,这么些肯定无法平昔放到网页上播放!

明显要想的是该怎么压缩视频文件大小!

正文首假如排查Android一个广播语音难题推动的ANR至极以及偶尔播放败北的Bug
读书本文大概必要开销3分钟。

初稿地址

https://medium.com/uptech-team/audio-not-playing-in-android-cde9a0fdfafd

此情此景一:自动播放

autoPlay 布尔属性;指定后,摄像会即刻自动开头播报,不会停下来等着数量载入甘休。

视频自动播放可以在页面打开且资源加载足够的景况下让摄像自动播放,收缩三次用户点击的相互,同时可以运用在动效背景、H5仿摄像通话的效用。不过鉴于各类原因,自动播放无论在PC端依旧移动端都有两样程度的限定。

网页播放摄像格式比较

眼下自家询问到的福利在网页上播报的摄像格式有 flv,swf,mp4等等!

flv 和 swf 必要浏览器协理 「adobe
flash」然而据我所知不是享有的浏览器都扶助 flash 的,比如到就赶上 chrome
浏览器就不帮忙flash,但是听说有方法化解,这里自己就不赘述了!

本人动用的是 mp3 格式 + H5
<video>标签,那些具有的浏览器都帮衬,具有很好的普及型和兼容性!

引言

近些年项目中的IM模块收到反映,语消息息点了将来正在播放却绝非动静,有时甚至直接ANR相当,因项目中的IM选择的是新浪的云信,所以第一时间请教了云信的技术人员,获得的死灰复燃是他俩的SDK播放语音是一直封装调用了系统的Api,没有做此外处理。既然这样,那就只能自己探究下难点呀

开业介绍

Android固然现在曾经是最受欢迎的移动端操作系统,并且有一个特大的社区,不过有时如故有那么有些分化日常的标题,即使查阅StackOverflow也不可能行得通疾速的缓解。我曾经就遇到了那个题材中的一个,然后自己将在那篇小说中分享我有关那个难题部分踩坑的经历给那个要求这一个音讯的人。
题材大致是那般的:我索要过多的audio音频文件能立时被广播,并且用户能在急需的时候打开和倒闭,而且这么些声音能够循环播放。一眼看过去尚未怎么复杂的地方,当然,假设确实如此容易那么你就不必要阅读那篇小说了:)
本身花了一对一长的时辰来解决这一个题材,并且从中得到了过多。在化解那些难题的经过中,我采访了各类互连网上的资源以及个体经验,接下去自己就会讲课在Android上广播音频文件你恐怕会遇到的的坑以及解决方案。

移动端

MP4 

MP3是一种视频文件格式,不过视频文件格式下又有诸多编码格式,现在常用的唯有H264 和 MPEG4格式    H263 和VP6格式 已淘汰!

H.264被MPEG协会称作AVC(Advanced Video
Codec/先进视频编码),是MPEG4标准的第10有的,用来替代此前MPEG4第2局地(简称MPEG4P2)所制定的视频编码,因为AVC有着比MPEG4P2强很多的滑坡作用。最普遍的MPEG4P2编码器有divx和xvid(开源),最常见的AVC编码器是x264(开源)

MPEG-4是一套用于音频、视频信息的压缩编码标准,由国际标准化组织(ISO)和国际电工委员会(IEC)下属的“動態映像专家组”(Moving
Picture Experts
Group,即MPEG)制定,第一版在1998年五月通過,第二版在1999年17月通過。MPEG-4格式的首要用途在於網上串流、光碟、語音傳送(視訊電話),以及Android语音消息播放,Android播放audio音频踩坑实践。電視廣播

通过精选我主宰选用H.264,于是利用格式工厂对原来的视频格式举办格式转换,选择mp5 输出设置选取 AVC 480p,点击确定,然后选拔输出地点

图片 1

点击确定,然后点击起先转移就行

图片 2

更换达成后要么很不错的,300 MB 的文本转换完毕后变成的 29 MB
左右,而且清晰度也没错!

题材一定

先是从IM的SDK中的语音播放类入手,发现确实是调用了Android的连串语音播放。

图片 3

IM的SDK源码

那么大家去看一下Android的传媒播放类MediaPlayer的那一个章程的源码,分析一下题材,先看一下MediaPlayer的setDataSource方法,

图片 4

setDataSource

透过注释可以观看,那些点子是永葆传递本地文件路径或者是一个互联网路径的,推断是不是是因为在ui线程加载网络资源,导致了anr,大家随后往下看

图片 5

setDataSource的重载方法里对传播的数量来自做了分化,最终调用了native的setDataSource方法。

然后大家看一下prepare方法

图片 6

从注释能够看看,prepare方法还有此外一个prepareAsync方法,

图片 7

基于注释可以看出,prepareAsync格局是异步的去准备资源,基本阐明了我们事先的疑心,因为她俩最后都是调用了c++层的代码,那里大家直接去看一下他们的源码

源码地方frameworks/av/media/libmedia/mediaplayer.cpp

status_t MediaPlayer::prepare()
{
    ALOGV("prepare");
    Mutex::Autolock _l(mLock);
    mLockThreadId = getThreadId();
    if (mPrepareSync) {
        mLockThreadId = 0;
        return -EALREADY;
    }
    mPrepareSync = true;
    status_t ret = prepareAsync_l();
    if (ret != NO_ERROR) {
        mLockThreadId = 0;
        return ret;
    }

    if (mPrepareSync) {
        mSignal.wait(mLock);  // wait for prepare done
        mPrepareSync = false;
    }
    ALOGV("prepare complete - status=%d", mPrepareStatus);
    mLockThreadId = 0;
    return mPrepareStatus;
}
status_t MediaPlayer::prepareAsync()
{
    ALOGV("prepareAsync");
    Mutex::Autolock _l(mLock);
    return prepareAsync_l();
}

可以见见,不管是prepare还是prepareAsync主意,最后都是会调用prepareAsync_l(),但是prepare艺术中多了这一段,

    if (mPrepareSync) {
        mSignal.wait(mLock);  // wait for prepare done
        mPrepareSync = false;
    }

在那里调用了wait主意开展了守候,所以使得java层达到协同调用的法力,然后在prepare完结之后会调用notify方法唤醒它,代码如下

void MediaPlayer::notify(int msg, int ext1, int ext2, const Parcel *obj)
{
    ...
    case MEDIA_PREPARED:
        ALOGV("prepared");
        mCurrentState = MEDIA_PLAYER_PREPARED;
        if (mPrepareSync) {
            ALOGV("signal application thread");
            mPrepareSync = false;
            mPrepareStatus = NO_ERROR;
            mSignal.signal();
        }
        break;
}

不等的方案

先是大家列出Android播放audio文件的片段方案

  1. MediaPlayer
    那是最简单易行的同时用的最多的一个类,这么些不仅能播音频仍可以播放摄像。在这里不会讲课摄像方面的细节,只会讲课有关音频的局地。
  2. SoundPool
    在化解难题经过中窥见的一个Android
    class,可以被用来播放小的音频文件,那么些类的功用重如若足以同时控制播放三个小的声息。可以看出,我不止的强调那些“小”,容易的解释一下这几个规律,SoundPool
    接收一个文本(可能从raw文件夹或者是从本地存储)并解压缩成
    PCM,一种数字采样模拟信号。最根本的唤起就是,每一个解压缩的文件大小无法跨越1Mb,否则他们就不会播放。所以SoundPool一般用来播音相比较短的声响,比如游戏音效或者类似的事物。
  3. AudioTrack
    也是一个用来播音音频的,但是那么些相比较于前一个更低级一点,一般只好用来广播解码后的PCM流或者是不需求解码的wav文件。
  4. ExoPlayer
    那是google推荐用来取代
    MediaPlayer的一个播放器,在开始那篇文章的时候,release的本子是1.5.11,然后谷歌(Google)发表了2.0.0版本,比较前者有了有的改成。

IOS

早期必必要有用户手势(user
gesture)video标签才方可播放; 从版本10起来修改了video的平整,苹果放宽了inline和autoplay,策略如下(仅适用于Safari浏览器):

  • <video> elements will be allowed to autoplay without a user
    gesture if their source media contains no audio tracks.(无音频源的
    video 元素 允许自动播放)
  • <video muted> elements will also be allowed to autoplay without a
    user gesture.(禁音的 video 元素允许自动播放)
  • If a <video> element gains an audio track or becomes un-muted
    without a user gesture, playback will pause.(如果 video
    元素在未曾用户手势下有了音频源或者变成非禁音,会搁浅播放)
  • <video autoplay> elements will only begin playing when visible
    on-screen such as when they are scrolled into the viewport, made
    visible through CSS, and inserted into the DOM.(video
    元素显示屏可知才起来广播)
  • <video autoplay> elements will pause if they become non-visible,
    such as by being scrolled out of the
    viewport.(video元素不可知后终止播放)

边加载边播放的 mp3

鉴于 MP4的摄像文件信息默许是停放在文件末尾,也就导致了必必要把公文加载落成才能播放视频,那显明是不好的,所以下边采用「MP4 Fast
Start」举行转向一下,把公文音讯移动到视频文件的面前,那样浏览器在加载时就可以一边加载一遍播放了!

缓解格局

经过看源码,果然可以规定是因为prepare方法会直接在现阶段线程去读取资源,即便资源文件是一个网络资源,当网络条件相比较差即弱网情况下时,那么暴发ANR的几率就会要命高了,而且如若请求中断或者文件不完整,也会招致播放战败,解决办法之一的话可以利用下边的法门去播放一个语音

       MediaPlayer mediaPlayer = new MediaPlayer();
       mediaPlayer.setDataSource(url);
       mediaPlayer.prepareAsync();
       mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
           @Override
           public void onPrepared(MediaPlayer mp) {
               mp.start();
           }
       });

可是为了使对互连网资源下载的进度可控,依然引进我们自己做判定,使用自己的网络下载格局去下载资源文件然后再将其的当地路径交由MediaPlayer播放。

由于品种中的IM使用的是云信的SDK,所以大家也不好改动他们的代码,就不得不在调用sdk的方法前自己先做判断,即使互连网资源则先下载好才去调用sdk的办法,然后也向云信反映了那一个难题,他们也表示应当做容错处理,应该会在后续版本革新吧。


详尽介绍

第一从 MediaPlayer
初始,那应当是上述列表中用的最多的一种了,提到那么些只好放出一个图,

图片 8

MediaPlayer State

其一图突显了一个MediaPlayer的生命周期…那是一个大的状态机,你需求试着去领悟整个工作流的运转,状态的切换。谷歌(Google)对于怎么利用MediaPlayer有一个相比较好的
引导
,假若你从未动用过这一个类,可以先读一下百分之百文档,那里自己就径直教学存在的标题了。

  • 多少个MediaPlayer的实例在Nexus 5和Nexus
    5x上有可能无法同时播报,我早已测试过这么些装备,在Nexus
    6p那款上边还无法确定,其中的原由还不了然。
  • 此外一个题材就是MediaPlayer的 isPlaying()
    方法在播放audio音频截止后仍旧有可能回到true,比如上边那段代码,在onCompletion回调中打印出isPlaying的结果

private void init() {  
    MediaPlayer mediaPlayer = new MediaPlayer();
    mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
    // some init goes here...
    mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
            @Override
            public void onCompletion(MediaPlayer mp) {
                Log.d("MediaPlayer", "onComplete, mediaPlayer.isPlaying() returns " + mediaPlayer.isPlaying());
            }   
        });
}

从底下的图可以见到确实在终止后照旧打印出了true。

图片 9

onCompletion

  • 除此之外,MediaPlayer中的 setVolume() 方法在Jelly Bean API
    16或者不会健康的做事,纵然现在那几个难点只是出现在LG
    Optimus那款设备上。为精通决那些标题心急火燎选择的Android中的奥迪(Audi)oManager来通过STREAM_MUSIC设置音量,就好像上边那样来收缩音量

private void decreaseVolume() {
    AudioManager audioManager = ((AudioManager) getSystemService(Context.AUDIO_SERVICE));
    int currentVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
    if (currentVolume > 0) {
        audioManager.setStreamVolume(AudioManager.STREAM_MUSIC,  currentVolume - 1, 0);
    }
}

那种做法其实有不可计数的通病,通过那种方法来设置音量会改变所有的音频流,也就是说,所有应用MediaPlayer播放的顺序以及其它的app都会联合共用那几个音量,那明显不是一个好的章程。

  • MediaPlayer的OnErrorListener 接口回调方法有一个艺术
    onError(MediaPlayer mp, int what, int extra),即使官方已经有
    文档
    提到了一部分吸收的code码,可是依旧有一些当先了文档的限制,比如,你恐怕获得一个code像那一个图一律

图片 10

error code

(-38,0),那些实际上表现网络存在难点,除此之外,也有一部分其他的奇奇怪怪的code,也是文档没有提及的。

SoundPool.
因为那一个对于文件大小的限量,我实际从来从未用过它,不过在how-bad-is-android-soundpool-what-alternative-to-use
那几个题材上对此这么些类的用处有一个比较详细的叙说,所以能够一贯看那几个就好了。当然作为个体提议,若是您的公文当先30秒,那么极端不用拔取这一个。

**AudioTrack. **
正如前面所说的那么,那些类有很大的局限性,一般不推荐使用那几个,假使一定想要了解这几个,可以看一下那篇教导,
AudioTrack
tutorial

ExoPlayer. 明白怎么拔取这一个可以先check一份官方的资料 the official
page
,那么些库的可定制性是很强的,大致可以已毕全部你须要的要求。我本来想说有些自身用这些时遇上过的局地题目,但是在发布的2.0.0版本中曾经全副修复了
: )
,当然我也会提示那个还一贯不迁移到2.0本子的,相比中才能觉察进步。在1.5.11版本中循环那些职能还不有限帮忙(可用),你不行以在
onComplete
回调中去手动重启你的播放器,这会造成在下一个广播此前出现一个间隔。不过在2.0+的本子中,已经有了一个LoopingMediaSource
,这些类的功力让您感到不到一个文书的广播完成或起初,无闲暇的回看也早已支撑。
在首先个本子中大家在尚未其他零件的情事下不能设置音量,不得不动用发送一个message的点子,就像是上面那样

private void setVolume(float volume) {
    ExoPlayer player = ExoPlayer.Factory.newInstance(1);
    SampleSource source = new ExtractorSampleSource(Uri.parse("audiourl"),
            new DefaultUriDataSource(this, Util.getUserAgent(this, getString(R.string.app_name))),
            new DefaultAllocator(BUFFER_SEGMENT_SIZE), BUFFER_SEGMENT_SIZE * BUFFER_SEGMENT_COUNT);
    MediaCodecAudioTrackRenderer renderer = new MediaCodecAudioTrackRenderer(source,
            MediaCodecSelector.DEFAULT);
    player.prepare(renderer);
    player.sendMessage(renderer, MediaCodecAudioTrackRenderer.MSG_SET_VOLUME, volume);
}

而从2.0.0本子初阶已经在SimpleExoPlayer中早就有了一个 setVolume
方法,那充足的惠及,并且修复了ExoPlayer 在API 16 Jelly
Bean上有些机型的题材,难题如那个issue所提,this
issue。

据悉那样多的探讨以及个体使用经验,我只得提一个指出:使用ExoPlayer,首要基于以下多少个原因,社区立异很快,有难点立即申报和缓解,定制能力没有其他的library可以与之比较,而且选择起来极度概括,没有其余困难。

连带资源:
How bad is SoundPool? What alternative to
use?
Multiple MediaPlayers do not work on Nexus
5
Unable to play two MediaPlayer at same time in Nexus
5
Choppy Audio with ofxAndroidSoundPlayer
(MediaPlayer)

安卓

__早期__如出一辙必要用户手势才得以播放; 安卓的 chrome 53
放松了自动播放策略,策略分化于IOS的Safari,须求同时对
video 设置 autoplay 和 muted(是或不是禁音),才允许自动播放;
__安卓的 FireFox 和 UC 浏览器__支持其余动静下的自动播放;
安卓的别样浏览器暂时不清楚境况;

假如以为对您具备辅助,请点个赞,谢谢。你的鼓励是自我最大的引力。

PC端

早期是__支撑自动播放,但__近来
Safari、Chrome
 陆续修改了自动播放的国策……

欢迎关怀EoniJJ的简书

不定期与您分享有关Android开发的点点滴滴。

Safari 浏览器

__Safari
10 后__带音频的视频和拍子默认禁止自动播放,越多消息可以参考那篇作品;

Chrome(旧版本) 下自动播放:

图片 11

Safari (10后)不自动播放:

图片 12

相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

*
*
Website