原文地址:http://blog.csdn.net/u012979009/article/details/50623460
前言
最近一直在忙公司的项目,主要针对视频播放这一块,说具体点是关于flowplayer的这一块。上一篇《基于web的视频在线编辑》已经简单总结了一下flowplayer的强大功能。今天的主角就是基于flowplayer的视频预览,如何实现?
大家在平时观看视频的视频网站中,比如优酷,爱奇艺,腾讯视频等,鼠标移动至播放条区域的时候,大家可以看到会弹出小的视频预览图片,这样子就可以给用户很好体验,至少可以知道前后播放的内容。最近公司业务需要,就不得不研究了。
本文将从三个方面进行总结
一、设计与逻辑(最重要)
二、优化拓展
三、代码
特别说明:本设计针对flowplayer版本为6.0版本,低于6.0版本在本设计中不适用,后面会简单说到在flowplayer的5.0版本如何设计的问题,整个设计会复杂很多。
一、设计与逻辑
首先有三个问题
(1)鼠标移到flowplayer的播放条上如何获取对应的时间。
(2)在对应的时间又如何获取到该时间点上的视频的缩略图。
(3)如何显示的样式问题,像爱奇艺的缩略图显示一排在播放器区域内显示。
第一个问题
在flowplayer6.0的版本中,flowplayer.min.js其实已经实现获取时间的功能,在官网的demo中,只要鼠标把移至播放的时间轴上,就会出现一个时间。下图是我在官网上的截图。
图一
从图上可以看到
-
<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<div
class="fp-timeline-tooltip fp-tooltip"></div>,并将时间显示在这个div。既然这样,那可以在flowplayer.js的文件中再重新定义一个函数,这个函数循环对象为<div
class="fp-timeline-tooltip fp-tooltip">
-
<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>
<div class="fp-thumbnails"></div>
另外一种是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 HTML5
-
Copyright (c) 2015-2016, Flowplayer Oy
-
Released under the MIT License:
-
http://www.opensource.org/licenses/mit-license.php
-
requires:
-
- Flowplayer HTML5 version 6.x or greater
-
revision: $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) {
-
// cleanup
-
bean.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 attempts
-
if (seconds < 0 || seconds > Math.round(api.video.duration)) {
-
return;
-
}
-
// enables greater interval than one second between thumbnails
-
seconds = Math.floor(seconds / interval);
-
-
var fpthumbnails = $(timelineTooltip).find('.fp-thumbnails'),
-
// 主缩略图的时间位置div
-
fpseektime = $(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'
-
});
-
// 左侧缩略图,创建div
-
if(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>');
-
}
-
}
-
// 右侧缩略图,创建div
-
if(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',
-
//这里配置
-
thumbnails: {
-
template: 'thumbnails/myvideo{time}.jpg',
-
height: 100,
-
thumbwidth: 150,
-
step: 5
-
-
},
-
-
sources: [
-
{
-
type: "video/mp4",
-
src: "http:/host/Video/201503/20150320105047342.mp4"
-
}
-
-
]
-
},
-
-
brand:{
-
text: "MyBrand",
-
showOnOrigin:false
-
}
-
});
-
</script>
直接上几个大图