最近的项目里面调试手机用的音乐播放网站,发现智能手机浏览器的HTML5 Audio播放真是各种别别窍啊,就在这里记录下下吧。
所用手机:
在日本发卖的大多数智能手机,包括iPhone3GS、iPhone4、iPhone4S、iPhone5(iOS5和6),Android2.3、4.0、4.1、4.2的各种手机。
实现的功能:
单首歌播放和停止,多首歌连续播放,歌曲都是MP4
原有实现流程:
1,为了处理共通化和隔离,不在网页里面直接写<audio>,而是写<a>并且用javascript在上面挂onclick事件处理,播放的时候new出audio对象来使用。
2,audio对象的属性:
audio.preload="auto";
audio.autoplay=true;
在audio上挂playing,end事件改变画面显示,挂error事件显示出错的dialog
3,因为设了autoplay,所以播放的时候不显式调用audio.load(),而是直接调用audio.play()
4,连续播放时在audio的end事件中播放下一首
5,每一次播放都重新生成一个新的audio对象(问原来的担当者,说是如果不这样做,在某些浏览器上放下一首歌的时候会有一瞬间的怪声。。)
6,停止播放时是调用audio.pause(),因为audio并没有提供单独的stop之类的方法
问题一,iOS不能连续播放
现象:不论是iOS5还是6,播放完一首歌的end事件里面丢弃原有对象新new一个audio播放都不出声
解决:调查发现说是iOS认为每个播放都必须来源于一次用户操作,也就是说如果不是在<a>的onclick里面播放就不被允许。
所以end事件里面不能播放一个新生成的audio对象。
但是经过试验,如果重用前一次生成的audio对象而只是改变audio.src指向新的歌曲就可以播放。
而Android各个版本都没这个问题,于是修改,对iOS不再新建audio对象,而是只重复用开始创建的那一个。
问题二,iOS5不能连续播放三首以上
现象:经过了上述修改,iOS6没问题了,但是iOS5在播放完第二首之后,第三首却不能播放了
解决:没有明确的文档,试验了多种方法都不行,于是只有限定iOS5不提供连续播放功能了。。
问题三,某些机型preload无效
现象:一段时间以后出现了一些新机型,其中两款(P-02E和N-04E,版本是Android4.1.2)出现了无法播放的问题
解决:调查发现,貌似是preload没起作用,于是改为如下流程就ok了
1,开始播放时设完audio.src之后不直接调用audio.play(),而是调用audio.load(),显式指定读取数据
2,在audio上挂canplay事件处理,这个事件会在播放器读取到足够开始播放的数据时发生,在事件中再调用audio.play()播放
注意,这里虽然显式调用audio.load(),但是并没有把audio.preload设为"none",因为有些机种可能可以通过preload加快数据读取速度
问题四,生成过多的audio对象占用资源造成无法播放
现象:在测试时偶然发现Galaxy Nexus在同一个页面播放多首歌之后(大概10几首)就再也不能放了,一播放就直接结束。
而等待一段时间之后又偶尔可以再放一首,然后又不行了。
解决:因为iOS不发生,所以推测有可能是每次被生成的audio对象没有被释放,因为虽然每次都把旧对象的变量设为null,
但是真正的资源回收依赖于浏览器的GC,不能及时回收是可以想见的。
询问原来的担当者并在几款手机上做了测试之后发现,重用原来的audio对象也并没有发生怪声,估计那个怪声现象是PC上的某款浏览器中的问题,
所以把Android下也改为重用audio对象,以观后效。
问题五,在数据读取途中调用audio.pause()无效
现象:在audio.load()被调用到canplay事件发生之间的时候,即使调用了audio.pause(),canplay事件还是会发生,于是播放还是会开始
解决:虽然可以用加flag的方法控制canplay中是否调用audio.play(),但是flag多了容易混乱,于是搜索加试验,发现有如下方法
1,在数据读取中要中断的时候,可以把audio.src设为null,并显式调用audio.load(),
此时对于Android会中断数据读取,并且canplay也不会发生,
而iOS还是的canplay还是会发生,但是因为audio.src变为了"",所以可以用判断audio.src是不是""的方法决定是否调用audio.play(),比用flag好
2,需要注意的是,audio.src必须设为null而不是"",因为audio.src设置的是url,可以是绝对地址也可以是相对地址,
所以如果设为"",最后实际被设置的就是你的网页地址(相当于base url + ""),这样不利于canplay中的判断。而null则只会被解释为空。
3,另外,这种做法其实会引起audio读不到正确数据而引发error,所以在做这件事之前需要暂时删除error处理事件,在error发生之后再加回去,
否则你就会看到你预设的出错dialog总是被弹出了。
不过要注意,加回error处理事件的动作不能在audio.load()刚调用完就做,因为error事件只有实际尝试访问之后才会发生,
所以最好是删除正常error处理之后先加上一个临时error处理,这个处理中只做一件事,就是当发生之后再把自己删除并把正常error处理加回去。
4,最后还有一个要改,就是在iOS下,因为即使把audio.src设为null并调用audio.load()也不能中断数据读取,
所以虽然audio.src变成空了,但是前面已经开始的那次有效数据读取会继续进行(可能是因为读取错误而没能改变前一次读取的目标),
如果此时audio.autoplay是true,那么读取结束之后还是会播放那首歌,于是需要把audio.autoplay设为false。
问题六,网络没有连接时不触发error事件
现象:在Android下,网络没有连接的时候audio.load()没有触发error事件,于是不能报告错误。
解决:audio除了error事件还有一个emptied事件,按照文档说明,是NETWORK变为empty状态时触发,那么应该就是断网时触发了吧。
但是经过试验(尝试(A)网络没有连接,(B)网络有连接但是audio.src设置错误的地址),有点哭笑不得,状况如下:
iOS下,(A)和(B)都会既触发error又触发emptied。
Android2.3下,(A)和(B)都既不触发error又不触发emptied。
Android4.0/4.1下,(A)和(B)都不触发error,但是都触发emptied。
和文档的规定都不合啊,而且都不能区分是断网还是地址错误,好吧,考虑下该怎么办吧。。
总结一下就是,不同设备的不同浏览器动作不同,各种对应害死人啊。。
后面还有关于Video的,下次再说。
转自:http://hi.baidu.com/hf_zd/item/944b4bb0641e03492bebe369