网页播放视频踩过的坑,Android播放audio音频踩坑实践

视频播放–踩坑小计

2018/06/09 · JavaScript
· 视频

初稿出处:
chenjsh36   

网页播放视频踩过的坑,Android播放audio音频踩坑实践。 

乘势流量时代的来临和硬件技术的升级换代,越来越多的网站希望能在PC端或移动端播放自己的视频,而
<video>的包容性的逐月周详,使得开发者更乐于利用它来贯彻视频播放场景。

本篇文章主要罗列__摄像播放的通用场景及各场景下踩过的坑__,希望能__协理开发者在遇见须要开发时能更快地拔取适宜的技艺方案同时缩小采坑的次数__。

蒙受的题材

商店官网首页需求播放一段介绍公司景况的摄像,类似于宣传属性!给到本人的是
mp4 格式的视频 300 MB,那么些肯定无法一向放到网页上广播!

妇孺皆知要想的是该如何压缩视频文件大小!

正文首要是排查Android一个广播语音难题拉动的ANR相当以及偶尔播放失利的Bug
阅读本文大约需求开销3分钟。

原文地址

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

情景一:自动播放

autoPlay 布尔属性;指定后,视频会立刻自动早先广播,不会停下来等着多少载入为止。

视频自动播放能够在页面打开且资源加载丰硕的事态下让摄像自动播放,收缩五遍用户点击的竞相,同时可以动用在动效背景、H5仿摄像通话的法力。不过是因为各个缘由,自动播放无论在PC端依然移动端都有不一样水平的范围。

网页播放摄像格式相比较

眼前本人了然到的惠及在网页上播报的摄像格式有 flv,swf,mp3等等!

flv 和 swf 需求浏览器协理 「adobe
flash」然则据我所知不是兼备的浏览器都协理 flash 的,比如到就赶上 chrome
浏览器就不扶助flash,可是听说有艺术解决,那里自己就不赘述了!

本身利用的是 mp3 格式 + H5
<video>标签,那个拥有的浏览器都补助,具有很好的普及型和包容性!

引言

近日项目中的IM模块收到反映,语消息息点了后来正在播放却不曾声响,有时如故直接ANR万分,因项目中的IM选择的是新浪的云信,所以第一时间请教了云信的技术人士,获得的回复是他们的SDK播放语音是直接封装调用了系统的Api,没有做任何处理。既然这样,那就不得不自己商讨下难点啊

开篇介绍

Android尽管现在已经是最受欢迎的移动端操作系统,并且有一个庞然大物的社区,可是有时如故有那么有些破例的标题,即便查阅StackOverflow也不可见行得通急忙的缓解。我已经就蒙受了这个题材中的一个,然后我将在这篇文章中享用我有关那几个难点部分踩坑的阅历给那多少个急需那一个新闻的人。
难题大约是如此的:我须要广大的audio音频文件能即时被广播,并且用户能在须求的时候打开和关闭,而且这几个声音可以循环播放。一眼看千古不曾怎么复杂的地方,当然,倘若真的如此不难那么您就不须要阅读那篇著作了:)
我花了一定长的时光来缓解这一个难题,并且从中得到了重重。在解决这些标题标经过中,我搜集了种种互连网上的资源以及民用经验,接下去自己就会讲课在Android上广播音频文件你可能会碰着的的坑以及缓解方案。

移动端

MP4 

mp4是一种视频文件格式,可是视频文件格式下又有广大编码格式,现在常用的只有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年1五月通過。MPEG-4格式的主要用途在於網上串流、光碟、語音傳送(視訊電話),以及電視廣播

透过精选我说了算利用
H.264,于是利用格式工厂对原先的视频格式举行格式转换,选取mp3 输出设置采纳 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元素不可知后终止播放)

边加载边播放的 VCD

出于 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中的奥迪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

发表评论

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

网站地图xml地图