移动端播放视频被三方浏览器劫持播放器解决方案

1,299 阅读3分钟

2018/07/30

移动端播放视频在三方浏览器下会被劫持播放器,特别安卓播放完成还有广告推送,且播放视频由于被劫持不能再理想的排版中进行播放。 故而采取首先将视频转换成图片,通过canvas绘制关键帧动画来实现一些对视频要求不高的播放需求。

关键帧构建器:

function VideoToBase64(){};
VideoToBase64.prototype = {
    constructor : VideoToBase64,
    setting : null,
    info : null,
    video : null,
    canvas : null,
    ctx : null,
    trigger : function(setting){
        var _this = this;
        if(document.querySelector('#info')){
            document.querySelector('#info').parentNode.removeChild(document.querySelector('#info'));
        }
        _this.setting = setting;
        _this.loadMetaData();
    },
    loadMetaData : function(){
        var _this = this;
        _this.video = document.createElement('video');
        _this.video.src = URL.createObjectURL(_this.setting.file);
        _this.video.addEventListener('loadedmetadata',function(){
            document.body.appendChild(_this.video);
            _this.info = {
                w : _this.video.clientWidth,
                h : _this.video.clientHeight,
                duration : _this.video.duration,
                interval : _this.setting.interval,
                times : _this.setting.times,
                data : []
            };
            document.body.removeChild(_this.video);
            _this.toBase64();
        });
        _this.video.addEventListener('error',function(){
            alert('视频加载失败!');
        });
        
    },
    toBase64 : function(){
        var _this = this;
        _this.canvas = document.createElement('canvas');
        _this.canvas.width = _this.info.w;
        _this.canvas.height = _this.info.h;
        document.body.appendChild(_this.canvas);
        _this.ctx = this.canvas.getContext("2d");
        _this.video.addEventListener('canplaythrough',function(){
            alert('开始获取base64,请等到视屏播放完成!');
            _this.video.play();
            _this.info.data.push(_this.getBase64());
            var i = 0;
            var interval = setInterval(function(){
                if(_this.video.currentTime >= _this.info.duration){
                    document.body.removeChild(_this.canvas);
                    clearInterval(interval);
                    _this.toHtml();
                    return;
                };
                _this.info.data.push(_this.getBase64());
            },100);
        });
    },
    getBase64 : function(){
        var _this = this;
        _this.ctx.clearRect(0,0,_this.info.w,_this.info.h);
        _this.ctx.drawImage(_this.video,0,0,_this.info.w,_this.info.h);
        return _this.canvas.toDataURL(_this.setting.type,_this.setting.quality)
    },
    toHtml : function(){
        var _this = this;
        var textarea = document.createElement('textarea');
        textarea.id = 'info';
        textarea.style.width = textarea.style.height = '500px';
        textarea.value = JSON.stringify(_this.info);
        document.body.appendChild(textarea);
        _this.setting = _this.info = _this.video = _this.canvas = _this.ctx = null;
    }
};

播放器:

function Player(){};
Player.prototype = {
    constructor : Player,
    //初始化属性
    initProperty : function(options){
        var _this = this;
        _this.videoUrl = options.videoUrl;
        _this.audioUrl = options.audioUrl ? options.audioUrl : null;
        _this.node = options.node;
        _this.videoSource = 
        _this.audio =
        _this.canvas =
        _this.ctx = 
        _this.image =
        _this.progressInterval = 
        _this.canvasInterval = null;
        _this.loadAudioState =
        _this.loadVideoState = 
        _this.loadErrorState = false;
        //帧
        _this.frame = 0;
        //播放次数
        _this.times = 0;
        //暂停状态
        _this.paused = true;
        //静音状态
        _this.muted = false;
        //类属性做为回调数据存储
        Player.videoSource = null;
    },
    //加载完成
    loadComplete : null,
    //播放一次完成
    playOnceComplete : null,
    //全部播放完成
    playComplete : null,
    //加载进度
    loadProgress : null,
    //触发
    trigger : function(options){
        var _this = this;
        if(!options.videoUrl || !options.node){ return }
        _this.delete();
        _this.initProperty(options);
        //延时执行,等待用户设置回调接口
        setTimeout(function(){
            _this.load();
        },0);
    },
    //加载
    load : function(){
        var _this = this;
        //加载视频资源
        _this.loadVideo();
        //加载音频资源
        _this.loadAudio();
        //加载进度
        _this.progress();
    },
    //加载进度
    progress : function(){
        var _this = this;
        if(typeof _this.loadProgress != 'function'){return}
        var progress = 0;
        _this.progressInterval = setInterval(function(){
            if(_this.checkLoadState() || _this.loadErrorState){
                _this.stopProgress();
                _this.loadProgress(_this.loadErrorState ? -1 : 100);
                return;
            }
            if(progress < 90){
                progress += progress > 50 ? 1 : 5;
            }
            _this.loadProgress(progress);
        },100);
    },
    //停止进度条
    stopProgress : function(){
        var _this = this;
        if(!_this.progressInterval){ return }
        clearInterval(_this.progressInterval);
        _this.progressInterval = null;
    },
    //检测加载状态
    checkLoadState : function(){
        var _this = this;
        var loadAudio = _this.loadAudioState || !_this.audioUrl;
        //判断音频加载状态
        if(_this.loadVideoState && loadAudio){
            return true;
        };
        return false;
    },
    //加载完成
    loaded : function(){
        var _this = this;
        if(_this.checkLoadState()){
            //创建画布
            _this.create();
            if(typeof _this.loadComplete == 'function'){
                _this.loadComplete();
            }
        }
    },
    //删除元素
    delete : function(){
        var _this = this;
        if(_this.canvas){
            _this.node.removeChild(_this.canvas);
        }
    },
    //创建元素
    create : function(){
        var _this = this;
        //创建canvas
        _this.canvas = document.createElement('canvas');
        _this.canvas.width = _this.videoSource.w;
        _this.canvas.height = _this.videoSource.h;
        _this.canvas.innerText = '请使用支持html5的浏览器!';
        _this.node.innerHTML = '';
        _this.node.appendChild(_this.canvas);
        //创建ctx
        _this.ctx = _this.canvas.getContext('2d');
        //创建image
        _this.image = new Image();
    },
    //加载视频
    loadVideo : function(){
        var _this = this;
        var script = document.createElement('script');
        script.type = "text/javascript";
        script.async = true;
        script.src = _this.videoUrl;
        //加载完成
        script.onload = function(){
            if(!Player.videoSource){return}
            _this.videoSource = Player.videoSource;
            _this.loadVideoState = true;
            document.body.removeChild(script);
            script = Player.videoSource = null;
            //调用加载完成
            _this.loaded();
        };
        //加载失败
        script.onerror = function(){
            _this.loadErrorState = true;
            document.body.removeChild(script);
            script = null;
        };
        document.body.appendChild(script);
    },
    //加载音频
    loadAudio : function(){
        var _this = this;
        if(!_this.audioUrl){
            return;
        }
        //创建audio
        _this.audio = new Audio();
        _this.audio.preload = true;
        _this.audio.muted = _this.muted;
        _this.audio.volume = 0;
        var canplaythroughEve = function(){
            _this.audio.volume = 1;
            _this.audio.pause();
            _this.audio.currentTime = 0;
            _this.loadAudioState = true;
            //调用加载完成
            _this.loaded();
            _this.audio.removeEventListener('canplaythrough',canplaythroughEve);
            document.removeEventListener('touchstart',touchstartEve);
        };
        _this.audio.addEventListener('canplaythrough',canplaythroughEve);
        //加载失败
        _this.audio.onerror = function(){
            _this.loadErrorState = true;
            _this.audio = null;
        };
        var touchstartEve = function(){
            if(_this.loadAudioState){ return }
            _this.audio.play();
        };
        //移动端音频预加载
        document.addEventListener('touchstart',touchstartEve);
        _this.audio.src = _this.audioUrl;
        if(!_this.loadAudioState){
            _this.audio.play();
        }
        
    },
    //播放
    play : function(){
        var _this = this;
        //播放状态不能重复点击
        if(!_this.paused){ return }
        _this.paused = false;
        _this.startAudio();
        _this.startCanvas();
    },
    //静音
    mute : function(){
        var _this = this;
        if(!_this.audio || _this.muted){ return }
        _this.audio.muted = _this.muted = true;
    },
    //声音
    sound : function(){
        var _this = this;
        if(!_this.audio || !_this.muted){ return }
        _this.audio.muted = _this.muted = false;
    },
    //暂停
    pause : function(){
        var _this = this;
        if(_this.paused){ return }
        _this.paused = true;
        _this.stopAudio();
        _this.stopCanvas();
    },
    //重置
    reset : function(){
        var _this = this;
        _this.frame = 0;
        if(_this.audio){
            _this.audio.currentTime = 0;
        }
    },
    //重播
    rePlay : function(){
        var _this = this;
        _this.pause();
        _this.reset();
        _this.play();
    },
    //绘制
    draw : function(){
        var _this = this;
        var w = _this.videoSource.w,
            h = _this.videoSource.h;
        _this.ctx.clearRect( 0, 0, w, h);
        _this.ctx.drawImage(_this.image, 0, 0,w, h);
    },
    //开始音频
    startAudio : function(){
        var _this = this;
        if(!_this.audio){ return };
        _this.audio.play();
    },
    //停止音频
    stopAudio : function(){
        var _this = this;
        if(!_this.audio){ return };
        _this.audio.pause();
    },
    //开始画布
    startCanvas : function(){
        var _this = this;
        var data = _this.videoSource.data,
            length = data.length,
            interval = Math.ceil(_this.videoSource.duration * 1000 / length);
        //执行
        _this.loadImg(data[_this.frame]);
        //定时器
        _this.canvasInterval = setInterval(function(){
            if(_this.frame >= length){
                //播放一次完成的回调
                if(typeof _this.playOnceComplete == 'function'){
                    _this.playOnceComplete();
                };
                //循环播放
                _this.loopPlay();
                return;
            }
            _this.loadImg(data[_this.frame]);
        }, interval);
    },
    //停止画布
    stopCanvas : function(){
        var _this = this;
        if(!_this.canvasInterval){ return }
        clearInterval(_this.canvasInterval);
        _this.canvasInterval = null;
    },
    //循环播放
    loopPlay : function(){
        var _this = this;
        var times = _this.videoSource.times;
        _this.times ++;
        //处于循环播放
        if(times <1 || times > _this.times){
            _this.rePlay();
            return;
        }
        //退出循环播放
        _this.pause();
        _this.reset();
        //播放完成
        if(typeof _this.playComplete == 'function'){
            _this.playComplete();
        }
        
    },
    //加载图片
    loadImg : function(src){
        var _this = this;
        //累加帧数
        _this.frame ++;
        //判断src
        if(typeof src == 'undefined'){return}
        //load-image
        _this.image.src = src;
        var fn = typeof fn == 'function' ? fn : function(){};
        if(_this.image.complete){
            _this.draw();
            return;
        };
        _this.image.onload = function(){
            _this.draw();
        };
    }
};