前言
不得不吐槽一下咯,年终奖发了不到半个月的工资,心醉了,心凉了!不过技术知识是属于自己的东西,有新的想法,学到新的知识还是的总结出来,和大家分享分享!
最近一直在忙公司的项目,主要针对视频播放这一块,说具体点是关于flowplayer的这一块。上一篇《基于web的视频在线编辑》已经简单总结了一下flowplayer的强大功能。今天的主角就是基于flowplayer的视频预览,如何实现?
大家在平时观看视频的视频网站中,比如优酷,爱奇艺,腾讯视频等,鼠标移动至播放条区域的时候,大家可以看到会弹出小的视频预览图片,这样子就可以给用户很好体验,至少可以知道前后播放的内容。最近公司业务需要,就不得不研究了。
本文将从三个方面进行总结
一、设计与逻辑(最重要)
二、优化拓展
三、代码
特别说明:本设计针对flowplayer版本为6.0版本,低于6.0版本在本设计中不适用,后面会简单说到在flowplayer的5.0版本如何设计的问题,整个设计会复杂很多。
一、设计与逻辑
首先有三个问题
(1)鼠标移到flowplayer的播放条上如何获取对应的时间。
(2)在对应的时间又如何获取到该时间点上的视频的缩略图。
(3)如何显示的样式问题,像爱奇艺的缩略图显示一排在播放器区域内显示。
第一个问题
在flowplayer6.0的版本中,flowplayer.min.js其实已经实现获取时间的功能,在官网的demo中,只要鼠标把移至播放的时间轴上,就会出现一个时间。下图是我在官网上的截图。
图一
从图上可以看到,鼠标移至00:02时,出现一个div,显示时间,可以在firebug的调试中找到该<div>,其实是,可以针对该div入手。首先,我们可以<div class="fp-timeline-tooltip fp-tooltip">在里面再创建两个<div>。创建之后形式如下
<div class="fp-timeline-tooltip fp-tooltip"><div class="fp-thumbnails"></div><div class="fp-seektime" id="fp-seektime"></div></div>
其中,
<div class="fp-thumbnails"></div>
用于显示缩略图,
<div class="fp-seektime" id="fp-seektime"></div>
用于显示时间,最终要实现的形式如图下
图二
这样,我们又产生一个问题了,如何创建这些元素呢?这就需要我们能够去看flowplayer.js的内部代码了,因为在加载这个flowplayer.js的时候,会内部创建<div class="fp-timeline-tooltip fp-tooltip"></div>,并将时间显示在这个div。既然这样,那可以在flowplayer.js的文件中再重新定义一个函数,这个函数循环对象为<div class="fp-timeline-tooltip fp-tooltip"></div>,创建需要的两个div
<div class="fp-thumbnails"></div>
<div class="fp-seektime" id="fp-seektime"></div>
在flowplayer.js中的html的函数之后创建,如图所示
图三
同时更改当鼠标移动时的促发的函数,原来是html函数的,现在要改为tpl函数。
图四
这样,可以解决了第一个问题了。当鼠标放在播放的进度条时,如果设置一下<div class="fp-thumbnails">的宽度width和高度height就可以出现图二的效果啦,因为还没有获取到缩略图,所以没有缩列图显示,时间可以显示。
第二个问题
要获取缩略图,肯定要图片,这个单纯用Javascript是不可能从视频中获取对应时间的图片的,下面会用到专注于多媒体处理的ffmpeg来将这个视频的关键帧分解出来。
ffmpeg所用的命令
ffmpeg -i my.mp4 -filter:v framerate=1/1,scale=-1:100 -q:v 5 myvideo%d.jpg
这个命令可以将一个视频的关键帧图片全部分解出来,其中的参数可以自己查询资料。图片的命名是myvideo1.jpg,myvideo2.jpg......
其中这个framerate的值代表帧率,即相隔多少秒,截一张图,这里设置等于1,说明myvideo1.jpg就是0秒的图,myvideo2.jpg就是代表1秒的图。。。。这样子的话,就非常好办了,鼠标移到00:02时,可以取对应的myvideo3的图片,就相当于在这一秒的画面。将这个图作为<div class="fp-thumbnails"></div>的背景,也就是显示了这一张图了。
那么,问题又来了,如何获取鼠标放在进度条上的时间呢?方法也很简单哦哦!有很多种!
最简单一种是直接获取创建的<div class="fp-seektime" id="fp-seektime"></div>的值,因为这里面显示的就是时间,格式是00:00:00,需要自己重新将格式转为时间秒数,比如格式00:00:20的秒数为second=20,那么可以动态的设置
<div class="fp-thumbnails"></div>的背景为url了,例如url(./myvideo20.jpg),那么很容易就可以将对应的视频的缩略图显示出来了。
另外一种是flowplayer的原生方法,通过鼠标距离左侧的位置
var x = ev.pageX || ev.clientX,
delta = x - common.offset(timeline).left,
percentage = delta / common.width(timeline),
seconds = Math.round(percentage * api.video.duration);
具体要自己了解flowplayer.js
如果显示多个缩略图又怎么办呢,显示的原理是一样的,只不过又要动态地创建多个<div>,这就涉及到样式问题,要以主缩略图为中心(即是刚才创建的那个缩略图),动态创建<div>。再可以设置一个步调step,获取每张相隔step秒的图片显示在主缩略图的左右两侧。比如鼠标移至时间是00:00:50,即现在在50秒显示的是主缩略图,则左侧可以显示的图分别是在10,20,30,40秒处的视频图,右边则是60,70,80,,,,,所以整个很重要。
其实说到这里,基本整个设计与逻辑就已经完了,剩下的是css和js的逻辑问题了,在这里我就不多说了。
第三个问题
直接看代码,主要是样式问题,如何更好地显示一排出来。
二、优化拓展
(1)尽量写成js插件形式
(2)样式外观
(3)点击缩略图,播放跳至对应时间播放
这几个是很有必要优化的
三、代码
这是一个插件的形式来的,用的时候,要在这个文件之前引入需要引入jquery文件
/*!Thumbnail image plugin for Flowplayer HTML5Copyright (c) 2015-2016, Flowplayer OyReleased under the MIT License:http://www.opensource.org/licenses/mit-license.phprequires:- Flowplayer HTML5 version 6.x or greaterrevision: $GIT_ID$
*/
(function (flowplayer,$) {"use strict";flowplayer(function (api, root) {var common = flowplayer.common,bean = flowplayer.bean,support = flowplayer.support,timeline = common.find('.fp-timeline', root)[0],timelineTooltip = common.find('.fp-timeline-tooltip', root)[0];if (support.touch || !support.inlineVideo) {return;}api.on('ready', function (ev, a, video) {// cleanupbean.off(root, '.thumbnails');common.css(timelineTooltip, {'border': '1px solid #333','color':'#fff','background-color':'#000',});var c = flowplayer.extend({}, api.conf.thumbnails, video.thumbnails);if (!c.template) {return;}var height = c.height || 80,interval = c.interval || 1,template = c.template,ratio = video.height / video.width,preloadImages = function (tmpl, max, start) {max = Math.floor(max / interval);if (start === undefined) {start = 1;}function load() {if (start > max) {return;}var img = new Image();img.src = tmpl.replace('{time}', start);img.onload = function () {start += 1;load();};}load();};if (c.preload !== false) {preloadImages(template, video.duration);}// 鼠标移动至播放条,可以显示出预览图bean.on(root, 'mousemove.thumbnails', '.fp-timeline', function (ev) {$('div.fp-pre,div.fp-next').remove();var x = ev.pageX || ev.clientX,delta = x - common.offset(timeline).left, percentage = delta / common.width(timeline),seconds = Math.round(percentage * api.video.duration);// 2nd condition safeguards at out of range retrieval attemptsif (seconds < 0 || seconds > Math.round(api.video.duration)) {return;}// enables greater interval than one second between thumbnailsseconds = Math.floor(seconds / interval);var fpthumbnails = $(timelineTooltip).find('.fp-thumbnails'),// 主缩略图的时间位置divfpseektime = $(timelineTooltip).find('#fp-seektime'),// 控制面板的宽度,视频宽度width = $(root).find('.fp-controls').width(),// 主缩略图距离视频左侧位置left = $(timelineTooltip).position().left,// 主缩略图距离视频右侧位置right = width - left - (height / ratio)-2,// 左右侧缩略图的宽度thumbwidth = c.thumbwidth || 150,// 步调,即每张缩略图显示的时间隔间step=c.step || 10;// 主缩略图的样式以及背景图片fpthumbnails.css({width: (height / ratio) + 'px',height: height + 'px',// {time} template expected to start at 1, video time/first frame starts at 0'background-image': "url('" + template.replace('{time}', seconds + 1) + "')",'background-repeat': 'no-repeat','background-size':'100% 100%','-moz-background-size':'100% 100%', /* 老版本的 Firefox */'border': '1px solid #333'});// 主缩略图的时间样式fpseektime.css({height:20 + 'px','text-align':'center','text-shadow': '1px 1px #000'});// 左侧缩略图,创建divif(left>0) {var leftnum = Math.ceil(left/thumbwidth);for(var i=0;i<leftnum;i++) {$(timelineTooltip).parent('.fp-controls').append('<div class="fp-pre"></div>'); }}// 右侧缩略图,创建divif(right>0) {var rightnum = Math.ceil(right/thumbwidth);for(var i=0;i<rightnum;i++) {$(timelineTooltip).parent('.fp-controls').append('<div class="fp-next"></div>'); }}//左侧只能显示一个缩略图位置时 if(leftnum==1){$('.fp-pre').css({'width':(left-2)+'px','height':height + 'px','position':'absolute','bottom':'30px','border-top': '1px solid #333','border-right': '1px solid #333','border-bottom': '1px solid #333','color':'#fff','background-color':'#000','overflow':'hidden','left':'1px','background-image':"url('" + template.replace('{time}', seconds + 1-step) + "')"});}else {// 出来最后一个元素的其他元素的样式设置$('.fp-pre').not(':last').each(function(i,value){$(this).css({'width':thumbwidth + 'px','height':height + 'px','position':'absolute','bottom':'30px','border': '1px solid #333','color':'#fff','background-color':'#000','left':(left - (i+1)*(thumbwidth+2)) + 'px','background-image':"url('" + template.replace('{time}', seconds + 1-(i+1)*step) + "')",/*步调*/'background-repeat': 'no-repeat','background-size':'100% 100%','-moz-background-size':'100% 100%', /* 老版本的 Firefox */});});// 设置最后一个的样式$('.fp-pre').last().css({'width':(left-(leftnum-1)*(thumbwidth+2)-1)+'px','height':height + 'px','position':'absolute','bottom':'30px','border-top': '1px solid #333','border-right': '1px solid #333','border-bottom': '1px solid #333','color':'#fff','color':'#fff','background-color':'#000','overflow':'hidden','left':'1px','background-image':"url('" + template.replace('{time}', seconds + 1-(leftnum-1)*step) + "')"});}// 右侧只有一个div时的样式if(rightnum==1){$('.fp-next').css({'width':(width-(left+(height / ratio)+2)-2)+'px','height':height + 'px','position':'absolute','bottom':'30px','border-top': '1px solid #333','border-left': '1px solid #333','border-bottom': '1px solid #333','color':'#fff','color':'#fff','background-color':'#000','left':(left + (height / ratio)+2) + 'px','overflow':'hidden','background-image':"url('" + template.replace('{time}', seconds + 1+step) + "')"});}else {// 除了最后一个的其他样式$('.fp-next').not(':last').each(function(i,value){$(this).css({'width':thumbwidth + 'px','height':height + 'px','position':'absolute','bottom':'30px','border': '1px solid #333','color':'#fff','background-color':'#000','left':(left + (height / ratio)+2 + i*(thumbwidth+2))+ 'px','background-image':"url('" + template.replace('{time}', seconds + 1+(i+1)*step) + "')",'background-repeat': 'no-repeat','background-size':'100% 100%','-moz-background-size':'100% 100%', /* 老版本的 Firefox */});});// 右侧最后一个div样式$('.fp-next').last().css({'width':(right - (rightnum-1)*(thumbwidth+2)-1)+'px','height':height + 'px','position':'absolute','bottom':'30px','border-top': '1px solid #333','border-left': '1px solid #333','border-bottom': '1px solid #333','color':'#fff','background-color':'#000','left':(left + (height / ratio)+2 + ((rightnum-1)*(thumbwidth+2))) + 'px','overflow':'hidden','background-image':"url('" + template.replace('{time}', seconds + 1 +(rightnum-1)*step) + "')"});} });$('.fp-timeline').on('mouseout',function(){$('div.fp-pre,div.fp-next').remove();});});});})((typeof module === "object" && module.exports)? require('flowplayer'): window.flowplayer,jQuery);
在html页面中的用法
<div class="player1 no-toggle play-button"></div><div style="overflow:hidden;width:200px;height:161px;background-image:url('thumbnails/myvideo8.jpg')"><!-- <img src="thumbnails/myvideo8.jpg"> --></div></div><link rel="stylesheet" type="text/css" href="./flowplayer.quality-selector.css"><script type="text/javascript" src="./jquery-1.11.0.min.js"></script><script type="text/javascript" src="./flowplayer6.js"></script><script type="text/javascript" src="./thumbnails.js"></script><script type="text/javascript" src="./flowplayer.quality-selector.js"></script><script>$('.player1').flowplayer({adaptiveRatio:true,debug:true,autoplay:true,embed:false, clip: {title: 'Bauhausfffff',
<span style="color:#FF0000;"> //这里配置
thumbnails: {template: 'thumbnails/myvideo{time}.jpg',height: 100,thumbwidth: 150,step: 5},</span>sources: [{ type: "video/mp4",src: "http:/host/Video/201503/20150320105047342.mp4" }]},brand:{text: "MyBrand",showOnOrigin:false}});</script>
直接上几个大图