乍一看这标题,有点吊炸天的赶脚,canvas跟<video>能有什么联系?不过请放心我不是标题党。事情是这样的:
HTML5的<video>标签所支持的视频格式确实有限,mp4文件必须是H264编码的才行,若不是H264编码,在chrome下会只有声音没有画面,在FireFox下直接连声音也没有,而且控制台会显示警告:
因为浏览器使用的解码器也是H264的。如果用户在本地可以观看的mp4视频上传后却无法正常播放,这种体验是相当糟糕的。
我尝试在文件上传阶段进行检测,然而HTML5的FILE API能力也是有限的,只能获取文件名称、大小、MIME类型,对于视频的编码却无法检测到。
既然无法从上传阶段阻止用户,那么退一步讲,在视频无法播放的时候,我们希望可以检测到,并且给用户一个提示“视频解码错误”,这样他就不会有疑惑“我的视频为什么无法播放呢?”。
首先想到的是video的API,video有onerror事件,但是此事件只能在src地址错误或其他原因加载不到视频资源时触发,当加载到视频发生解码错误时,并不会触发。略蛋疼。这么看来按照标准的东西是无法检测到了,所以必须另辟蹊径了。答案就是:
canvas读取图片像素点的能力
前些天看了前端手记的这篇文章印象颇深,http://www.cssha.com/video2txt-canvas。利用canvas读取图片像素点,进而转化为文本图片。更厉害的是canvas的drawImage方法还可以传入视频,获取到视频某一帧的像素点。于是一个想法在脑中萦绕,解码错误的视频是没有画面的黑屏,我可以用canvas绘制视频,根据所绘制的内容来判断画面是不是在动,遂想到如下思路:
- 在视频开始播放时,每隔一定时间用canvas绘制一次视频画面
- 对每次canvas绘制的图片进行像素点采样,存入数组
- 扫描几次后,比较每次采样的像素点rgb值是否相同,即检测画面是否变化了
- 根据画面是否在“运动”来检测是否解码成功了
这种办法当然也有局限,下面是几个注意事项:
- canvas绘制视频画面的次数控制。绘制图片并采样获取像素点是消耗性能的,所以这个扫描过程不应该伴随视频播放的整个时间段。只需在开始播放的几秒内进行检测即可。
- 若恰巧有某个视频,开始的几秒内就是一个静止的画面,那检测就出错了。
局限归局限,先把想法写成代码试试,于是有如下代码:
当我怀着激动的心情开始测试时,发现事实真不是想象的那样。Chrome下,当一个视频无法解码时,drawImage方法直接无法执行,会报错。完了,美好的想法泡汤了。。。
不过转而一想,视频解码错误,drawImage方法就报错,如果写在try catch语句中,不就可以捕捉到了吗?看来还没到死路,这样连像素点采样都省了,可以直接检测到了。于是乎代码就简化成了下面这样:
//检测视频是否解码错误 function checkVideoParseError(){ var videos = $('video'); if(videos.length>0){ var can = $('<canvas id="canvas" style="display:none"></canvas>').appendTo('body'); var canvas = can.get(0); var ctxt = canvas.getContext('2d'); var scanImg = function(video){ try{ ctxt.drawImage(video, 0, 0); } catch(e){ alert('视频解码错误,请使用H264编码的mp4文件!'); } } videos.on('play',function(){ var _this = this; scanImg(_this); setTimeout(function(){scanImg(_this);},1000); }) } }
在1秒后的绘制图片动作会捕捉到异常。没想到,竟然这样成功了!
该方法纯属个人想出来的,还有诸多不完善之处,遇到同样问题的同学可以试试这个思路~
本文转自吕大豹博客园博客,原文链接:http://www.cnblogs.com/lvdabao/p/3414038.html,如需转载请自行联系原作者