Flash播放MP3文件疑难杂症汇总

音频播放是Flash的一个重要特性,很多产品线采用Flash作为音频播放的跨平台解决方案。但是,Flash内置的MP3播放内核并不十分完善。这里我们将Flash音频播放器项目过程中碰到的一些疑难杂症及对应解决方案做一个汇总,供类似项目借鉴。

1.歌曲长度获得不准确
现象演示:


原因:如果MP3文件与播放器跨域但没有相应的跨域策略访问文件,那么播放器将不能通过读取ID3标签的方式获得歌曲长度。这种情况下,我们只能通过sound.length * sound.bytesTotal / sound.bytesLoaded 来粗略估算歌曲长度,所以在歌曲文件完全加载完成之前,这个估算值是可能不准确且在变动的
解决方案:

2.调用soundChannel.stop()之后再去获取soundChannel.position,值会是0
原因:未知
解决方案:调用soundChannel.stop()之前先调用soundChannel.position,便可获得正确的position值,原因未知,很诡异。

3.某些浏览器下(比如maxthon),有可能在设置播放进度后,即使新的播放位置并非末尾,soundChannel也会抛出SOUND_COMPLETE事件
现象演示:


原因:如果MP3文件与播放器跨域但没有相应的跨域策略访问文件,那么播放器将不能通过读取ID3标签的方式获得歌曲长度。这种情况下,我们只能通过sound.length * sound.bytesTotal / sound.bytesLoaded 来粗略估算歌曲长度,所以在歌曲文件完全加载完成之前,这个估算值是可能不准确且在变动的
解决方案:在SOUND_COMPLETE事件的侦听器中,增加判断,如果当前播放进度确实是非常接近歌曲长度,才认为是真正的播放完成,否则忽略。
解决效果:

4.部分MP3文件可以加载,但总时长一直显示为0,播放进度也一直为0,没有声音
现象演示:


原因:这些MP3文件请求被重定向,返回的不是音频文件而是一段HTML。
解决方案:检测播放状态改变,在状态从缓冲变成播放时,如果获得的总时长为0,那么认为这是一个坏链,与加载失败一样处理。
解决效果:

5.部分MP3文件在正常播放过程中会卡住
现象演示:(播放会在26秒卡住)


原因:这些MP3文件是受损文件,而Flash 音频播放核心没有完善容错功能。
解决方案:在播放状态下,通过一个Daemon不停的监听播放进度是否正常,如果进度没有变化,则将播放进度前移一定长度(比如1秒),跳过坏点。虽然用户仍能明显听出这个坏点,但至少播放进度不会一直卡在坏点那里。
解决效果:

6.部分MP3文件可以正常加载并显示总时长,但播放进度一直为0,没有声音
现象演示:


原因:这些文件是伪MP3,虽然后缀是mp3,但实际上是其它格式的音频,比如WMA。
解决方案:没有很好的办法能够判断出伪MP3,只能同现象5的处理方案类似,在多次连续检测到播放进度没有改变后,认为这是一个坏链,与加载失败一样处理。
解决效果:


Less is more

近期生活上的一些变动让我倍感压力,不得不反思反思这些年自己的生活方式。反思的过程让我联想到很多,相信也能让正在屏幕前看文章的你联想很多,所以这里一一说开来,做个记录,做个分享。

我几乎每周都带老婆吃大餐,必胜客、新辣道、谕乡人家、中8楼、锦府盐帮,等等等等,越吃越觉得无味,不是厨师老了,而是我们的味觉疲了。

衣服呢,越买越多,但我很久没有感受到小时候过年穿新衣服的那种欣喜了。

我和老婆每个星期都至少看二部电影(去影院或者看电脑上的下载),同事们也都是,但大家都在感慨,实在没有好电影看了啊,但都还继续看,看电影已经变成了一种心理强迫症,周末不看上一两部就觉得周末白休息了。

书籍,计算机书我也看了不少,不乏经典之作,但有几本我去温故而知新了呢?留在我脑子里的太少。而我却一直在寻找更好更新的。

每天上班,我开着新浪微博(以前是Twitter),虽然只follow了100人不到,但我已经感到信息爆炸,太多信息对我来说没有意义,但我还是像吸毒一样一有空就去刷新刷新,它不仅耽误了我的时间,也扰乱了我工作的节奏。Blog比微博要有营养,但动辄1000+的未读,我又能体味吸收多少呢?

iPhone AppStore很帅,我从没见过如此丰富的软件,但很多优秀软件是收费的,所以我被迫克制。但后来,破解出来了,威锋出来了,我开始疯狂下载软件,听说有好的软件就必下,反正免费,结果软件图标达到了七八屏,而我真正会用的屈指可数,真正发挥软件价值的就更是寥寥无几了。

美食,靓衣,电影,书籍,信息,软件,这些都是有价值的东西,但为什么对我来说,它们充斥着我的生活和工作却没有给我带来应有的乐趣呢。我理所当然的想到了“滥用”这个词,紧跟着想到了“节制”。

这是一个过度消费的时代,有外界的诱惑,更有我们的不克制。我们破坏了坏境,更破坏了内心的平静,物欲横流却行尸走肉。

节制是一种态度,它让我们能够有时间有精力,静下心来欣赏品味我们已经拥有的一切,获得得来不易的欣喜和用心体验的愉悦。

“Less is more。”是一句建筑界名言,但现在,我有新的理解。


Flash相册程序的内存占用优化

最近协助同事对Flash相册项目做了程序优化,觉得每个优化步骤的试验结果都挺有价值,所以在这里做一个记录分享。

项目现状
Flash相册程序,在播放60张图片后(60是相册图片数量上限),内存占用接近1G。

当前技术实现
使用Loader加载图片文件,然后新建BitmapData对Loader进行draw操作保存位图数据,随后loader.unload()释放原始图片资源。播放某一张图片时,取出对应的位图数据,根据当前播放窗口大小进行平滑缩放,随后生成Bitmap对象,展现。

优化思考1:所有已加载的图片都保存其位图数据,导致内存占用过大。那么可以考虑限制位图数据对内存的占用总量,如果位图数据内存占用超出一个设定值(比如200MB)后销毁离当前播放index最远的图片位图数据。
问题:由于已加载数据可能会被销毁,所以会经常发生图片需要再次加载的情况,影响相册观看流畅度。另外,通过测试发现,即使设定一个比较宽松的上限值(比如200MB),也只能容纳几张图片的位图数据。
该方法损失了用户体验,但问题仍然没有得到较好解决。

优化思考2:BitmapData位图数据占用内存过大,如果Loader加载图片文件后不转化为位图数据,而直接保存loader对象,会不会减小内存占用?
经过试验发现,一个1280*800的jpg(130K),通过Loader加载进舞台并显示,不做任何处理,内存占用高达4MB。将loader对象从舞台上remove掉,只保留引用,内存占用仍不见减少。同时此做法会带来一个问题,loader对象的直接缩放会产生锯齿,这对相册程序而言是不可以接受的(注:(loader.content as Bitmap).smoothing = true可以消除缩放锯齿)。
此路不通。

优化思考3:既然直接保存BitmapData对象很吃内存,那么我通过JPGEncoder将其转化为ByteArray再进行保存,势必会减少开销。需要使用图片时,再通过loader.loadBytes来加载ByteArray数据。
经过试验发现,经过JPGEncoder转化后的ByteArray数据很小,只占用几十KB内存,效果明显。使用loader.loadBytes加载ByteArray显示也很顺利很流程。但一个致命的问题出现了,JPGEncoder执行效率非常低下,即使只是宽高1280*800的BitmapData,转化过程也会长达6秒之久(我的机器配置不低),更糟糕的是,由于Flash是单线程运行环境,所以此操作会导致整个界面卡住,程序陷入假死状态。
(延伸阅读:使用Vector优化JPGEncoder执行效率    异步JPGEncoder

优化思考4:经Laan提醒,想到ByteArray不需要通过对BitmapData数据JPGEncode来做,通过URLLoader指定BINARY方式读取图片文件,其data属性就是JPG或者其它压缩格式的ByteArray数据。
试验通过,问题终于得到顺利解决。

优化效果
经过上述优化,60张图片播放后,内存占用仍然不到100M,优化效果显著。

补充:加载图片时,获得BitmapData对象不需要通过BitmapData来draw,可以通过(loader.content as Bitmap).bitmapData直接获得(感谢天地会同学的转载和讨论)。

最后,说一下如何强制Player进行垃圾回收
如果只想在Debug Player中使用,则可以使用System.gc();
如果想在所有运行环境都能运行,则可以使用下面这一段垃圾回收触发代码(查看原帖):

try {
    new LocalConnection().connect(“gcHacker”);
    new LocalConnection().connect(“gcHacker”);
} catch (e:Error) {
}


新的启程

由于众所周知的原因,我转移了域名和主机,其间的一些差错导致之前的Blog资料尽失。我本来非常懊恼,但后来一想,追悔不如迎接一个全新的开始,于是便有了这个全新的博客。

这几年,3G网络建成并迅速扩张,以iPhone为代表的智能手机更是迅猛发展。不可否认,移动互联网将要成为未来很长一段时间内IT行业的主要增长点,我们的生活方式也会发生很大的改变。更重要的是,这一切,我身在其中,感受深刻!

在这个大背景下,我除了继续关注Flash技术,还将关注移动互联网的产品设计和研发。这个博客也会记录我的成长和成果。此外,生活上的点滴还有资讯分享,我将不在这里记录,因为这些更适合微博发布(我的新浪微博),这个博客的内容将会更加专业。

新的启程口号:让技术为生活创造价值!

加油!