MP3的播放(多浏览器实现)

网络文摘 前端客 2599℃ 0评论

demo地址,先睹为快吧!

下面进入正文部分:

一、MP3文件在各浏览器中支持情况

1.1 HTML5 Audio

大家可能都知道,在HTML5中有一个标签:audio,他的作用就是播放音频文件,那么对MP3格式文件支持情况如何呢?

据度娘告诉我的,大概是酱紫情况:
Chrome, IE9+, Safari

但是还是有点担心,万一漏了,肿么办呢?
所以第一步,我们需要检测MP3文件的播放情况。

如下代码所示,我们可以通过Audio对象的创建、canPlayType方法,监听canPlaythrough、error事件来判断MP3是否可以播放。使用代码检测后,发现度娘的描述中确实漏掉了几个浏览器:Opera,Edge,还有各式各样的使用webkit内核的PC/Mobile浏览器,可见这一番功夫并没有白费!

//默认可以播放
var audioMp3Flag = true;

//执行检查方法
canPlayAudioMP3(function(flag){audioMp3Flag = flag;})                        
                        
/**
 * 检测浏览器是否能使用Audio对象播放mp3文件 
 */
function canPlayAudioMP3(callback){
    try {
        var audio = new Audio();
                
        //1、利用canPlayType方法,检测浏览器是否有MP3解码器,回调true
        if(audio.canPlayType('audio/mpeg') == "probably")
            callback(true);
        
        //2、利用canPlaythrough事件,监听音频是否可以加载成功,回调true
        audio.addEventListener('canplaythrough', function(e){
            callback(true);
        }, false);
        
        //3、利用error事件,监听音频播放是否会出现异常,回调false
        audio.addEventListener('error', function(e){
            callback(false);
        }, false);
        
        //base64编码最小的MP3文件
        audio.src = "data:audio/mpeg;base64,/+MYxAAAAANIAAAAAExBTUUzLjk4LjIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
        audio.load();
        
    }
    catch(e){
        4、Audio对象异常,回调false
        callback(false);
    }
}

1.2 再谈谈Flash

由于flash的安全漏洞,放弃flash的声音越来越多了。
但是作为一个功能研究,还是希望能够兼容尽可能多的浏览器的,优雅降级、渐进增强是每个开发者都想做到的事情吧!

尽管网页上嵌入flash有很多现成的js文件,比方说swfobject.js等,但作为一个小demo,还是自己练习一下嵌入flash所需的html!

1.2.1 object与embed

网上同类的科普文章有很多,如OBJECT和EMBED标签
OBJECT 标签是用于windows IE3.0及以后浏览器或者其它支持Activex控件的浏览器。
EMBED标签是用于Netscape Navigator2.0及以后的浏览器或其它支持Netscape插件的浏览器。

下面分享一下我的方法

/*
 * 生成嵌入网页的flash代码 
 * @param {Object} movieName 元素Id
 * @param {Object} src        swf文件
 * @param {Object} data 需要传入的参数
 */
function flashHtml(movieName, src, data) {
        var attr = {
                width: 1,
                height: 1,
                quality: 'high',
                style: '',
                wmode: 'opaque' /*window|transparent|opaque*/
        };
        if (exports.name == "ie" && exports.version<=7) {
                //旧版IE加载flash需要添加随机数,随机数取值0-999
                src += '?rnd=' + Math.floor(Math.random() * 1000);
        }
        /* 1、IE6-10: object标签 + classid + codebase
         * 2、IE11浏览器:object标签 + type=application/x-shockwave-flash
         * 3、其他浏览器(Chrome|Firefox|Opera)等:embed标签 + type=application/x-shockwave-flash
         */
        return exports.name == "ie" && exports.version',                        '',                        '',                        '',                        '',                        ''
                ].join('') :
                exports.name == "ie" && exports.version==11 ? [
                        '<object id="', movieName, '" data="', src, '" type="application/x-shockwave-flash" name="', movieName, '" width="', attr.width, '" height="', attr.height, '">',                        '<param name="wmode" value="', attr.wmode, '" />',                        '<param name="movie" value="', src, '" />',                        '<param name="quality" value="',attr.quality,'" />',                        '<param name="allowscriptaccess" value="always" />',                        '<param name="flashvars" value="', data , '" />',                        '</object>' ].join('') : [ '<embed id="', movieName, '" src="' + src + '" type="application/x-shockwave-flash" width="', attr.width, '" height="', attr.height, '" name="', movieName, '"></embed>', '' ].join(''); }

1.2.2 编写as脚本,用于和js交互

下面脚本借鉴了这位大神的代码 https://github.com/breily/jquery.player.js/blob/master/version_0.2/music.as

同时吸取了记ie8及以下版本ie的flash的addCallback的一坑这位大哥的as脚本编写的经验,减少了addcallback的数量,成功消灭了IE8以下版本的bug

编写好as文件后,可以利mtasc编译为swf文件,没有的朋友可以在 这里下载

import flash.external.ExternalInterface;

class Music {
    static var app:Music;
    var sound:Sound;
    var paused:Number;

    // Constructor, plus adds all the JS callbacks
    function Music() {
        this.sound = new Sound();
                paused = 0;
        ExternalInterface.addCallback("emit",  this, emit);
                ExternalInterface.addCallback("getPaused",  this, getPaused);
    }

        public function emit(action:String, arg:String) {
                switch(action){
                        case 'load':
                                this.load(arg);
                                break;
                        case 'play':
                                this.play();
                                break;
                        case 'stop':
                                this.stop();
                                break;
                }
        }
        
        public function getPaused() {
                return paused;
        }
        
    private function load(url:String) {
        this.sound.loadSound(url, true);
        this.sound.onSoundComplete = function() {
                this.paused = 0;
            ExternalInterface.call("v_stop");
        };
    }

    private function play() {
            this.paused = 1;
        this.sound.start(0);
    }

    private function stop() {
            this.paused = 0;
        this.sound.stop();
    }

    static function main(mc:MovieClip) {
        app = new Music();
    }
}

二、Music对象的编写

捋顺了外部依赖,下面可以编写Music对象了,我的思路大概是:

Music: 1、构造函数
2、init 初始化方法
3、emit (根据音乐状态play或stop音乐)

为了实现播放逻辑与视图显示的分离,在初始化方法中传入了逻辑层完成后需要回调的方法。

2.1 构造函数

/**
 * Music对象的构造函数
 * @param {Object} url        音乐文件地址
 * @param {Object} type 音乐类型、暂时只支持audio/mpeg,加一个参数便于拓展
 * @param {Object} viewCallback        音乐初始化、播放逻辑完成后的视图回调
 * @param {Object} eventCallback 音乐初始化、播放逻辑完成后的事件回调
 */
function Music(url, type, viewCallback, eventCallback){
        this.url = url;
        this.type = type;
        this.viewCallback = viewCallback;
        this.eventCallback = eventCallback;
        this.isSupportAudio = isSupportAudio(this.type);
}

2.2 init初始化方法

分别调用了createAudioPlayer()和createSwfPlayer()来创建音乐播放的载体

/**
 * init初始化方法
 * 在支持audio标签且能正常解码MP3的情况下创建audio标签
 * 否则使用flash的方式
 * 完成后,进行初始化方法的视图及事件回调
 */
Music.prototype.init = function(){
        var self = this;
        if (this.isSupportAudio) {
                this.createAudioPlayer();
        } else {
                try{this.createSwfPlayer()}catch(e){};
        }
        this.viewCallback['init'].call(this);
        this.eventCallback['init'].call(this);
}

createAudioPlayer和createSwfPlayer的目的只有一个,封装一个this.musicEl对象,让对象在audio|flash的模式下,都能支持play(播放)、stop(停止),getPaused(获取音乐状态)
createSwfPlayer的实现方式中有一点要注意:IE6,7需要使用window[id]才能获取到object对象,其他浏览器可以使用document[id]

Music.prototype.createAudioPlayer = function(){
        var self = this;
        this.musicEl = document.createElement('audio');
        this.musicEl.src = this.url;
        this.musicEl.type = this.type;
        this.musicEl.stop = function(){
                this.pause();
                this.currentTime = 0.0;
        }
        addEvent(this.musicEl, 'ended', function(){
                m_console('播放结束停止MP3');
                self.viewCallback['stop'].call();
        });
        this.musicEl.getPaused = function(){
                return this.paused;
        }
}

Music.prototype.createSwfPlayer = function(){                
        var self =this;
        var musicId = 'swfPlayer';
        var id = 'player_hidden';
        var swf = 'music.swf';
        var html = flashHtml(id, swf, 'id='+id);
    this.musicEl = document.createElement('div');
    this.musicEl.innerHTML += html;
    this.musicEl.id = musicId;
    document.body.innerHTML += this.musicEl.outerHTML;
    
    var flashEl = (exports.name == 'ie' && exports.version <= 7) ? window[id] : document[id]; 
    
    setTimeout(function(){
            flashEl.emit('load', self.url);
            flashEl.emit('stop');
            self.musicEl.play = function(){
                    flashEl.emit('play');
            }
            self.musicEl.stop = function(){
                    flashEl.emit('stop');
            }
            self.musicEl.getPaused = function(){
                    return flashEl.getPaused() == 0;
            }
    }, 250);
}

2.3 emit播放控制方法

emit的作用就是在音乐停止状态下,执行播放。播放状态下,执行停止。
play、stop,getPaused方法的具体实现原理(在createAudioPlayer和createSwfPlayer有代码说明)
HTML5 Audio
play方法:调用Audio对象的play方法
stop方法:调用Audio对象的pause方法,并将currentTime设为0
getPaused方法:调用Audio对象的paused属性
Flash
play方法:调用flash Sound类的start方法
stop方法:调用flash Sound类的stop方法
getPaused方法:在as代码中设置了一个变量paused,在播放时设为1,停止时设为0,js中调用flash接口读取此变量判断是否为0

Music.prototype.play = function(){
        m_console('播放MP3');
        this.musicEl.play();
        this.viewCallback['play'].call();
}
Music.prototype.stop = function(){
        m_console('手动停止MP3');
        this.musicEl.stop();
        this.viewCallback['stop'].call();
}
Music.prototype.emit = function(){
        if (this.musicEl.getPaused()) {
                this.play();
        } else {
                this.stop();
        }
}

三、视图、事件回调的编写

逻辑层与视图分离本身就是为了视图代码的编写可以拓展,我就不详细介绍怎么编写视图方法了。
具体实现的源码可以参考我实现的demo的源码

文章转载自:WindyRain_Happy http://www.w3cfuns.com/blog-5463326-5411499.html

版权所有,转载请注明:前端客 » MP3的播放(多浏览器实现)
喜欢 (0)or分享 (0)
发表我的评论
取消评论

表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)