基于flowplayer的视频缩略图的视频预览

原文地址: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中,只要鼠标把移至播放的时间轴上,就会出现一个时间。下图是我在官网上的截图。


基于flowplayer的视频缩略图的视频预览

                                                                             图一

从图上可以看到

[html] view plain copy
  1. <div class="fp-timeline-tooltip fp-tooltip">  
  2.    <div class="fp-thumbnails"></div>  
  3.    <div class="fp-seektime" id="fp-seektime"></div>  
  4.  </div>  
其中,<div class="fp-thumbnails"></div>用于显示缩略图,<div class="fp-seektime" id="fp-seektime"></div>用于显示时间,最终要实现的形式如图下

基于flowplayer的视频缩略图的视频预览

                 图二

flowplayer.js<div class="fp-timeline-tooltip fp-tooltip"></div>,并将时间显示在这个div。既然这样,那可以在flowplayer.js的文件中再重新定义一个函数,这个函数循环对象为<div class="fp-timeline-tooltip fp-tooltip">

[html] view plain copy
  1. <div class="fp-thumbnails"></div>  
  2. <div class="fp-seektime" id="fp-seektime"></div>  
在flowplayer.js中的html的函数之后创建,如图所示

基于flowplayer的视频缩略图的视频预览

                                                                             图三

同时更改当鼠标移动时的促发的函数,原来是html函数的,现在要改为tpl函数。

基于flowplayer的视频缩略图的视频预览

                                                                     图四
这样,可以解决了第一个问题了。当鼠标放在播放的进度条时,如果设置一下
<div class="fp-thumbnails">的宽度width和高度height就可以出现图二的效果啦,因为还没有获取到缩略图,所以没有缩列图显示,时间可以显示。


第二个问题

要获取缩略图,肯定要图片,这个单纯用Javascript是不可能从视频中获取对应时间的图片的,下面会用到专注于多媒体处理的ffmpeg来将这个视频的关键帧分解出来。

ffmpeg所用的命令

[plain] view plain copy
  1. 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文件

[javascript] view plain copy
  1. /*! 
  2.    Thumbnail image plugin for Flowplayer HTML5 
  3.    Copyright (c) 2015-2016, Flowplayer Oy 
  4.    Released under the MIT License: 
  5.    http://www.opensource.org/licenses/mit-license.php 
  6.    requires: 
  7.    - Flowplayer HTML5 version 6.x or greater 
  8.    revision: $GIT_ID$ 
  9. */  
  10. (function (flowplayer,$) {  
  11.     "use strict";  
  12.     flowplayer(function (api, root) {  
  13.         var common = flowplayer.common,  
  14.             bean = flowplayer.bean,  
  15.             support = flowplayer.support,  
  16.             timeline = common.find('.fp-timeline', root)[0],  
  17.             timelineTooltip = common.find('.fp-timeline-tooltip', root)[0];  
  18.   
  19.         if (support.touch || !support.inlineVideo) {  
  20.             return;  
  21.         }  
  22.   
  23.         api.on('ready'function (ev, a, video) {  
  24.             // cleanup  
  25.             bean.off(root, '.thumbnails');  
  26.               
  27.             common.css(timelineTooltip, {  
  28.                 'border''1px solid #333',  
  29.                 'color':'#fff',  
  30.                 'background-color':'#000',  
  31.                   
  32.             });  
  33.             var c = flowplayer.extend({}, api.conf.thumbnails, video.thumbnails);  
  34.   
  35.             if (!c.template) {  
  36.                 return;  
  37.             }  
  38.   
  39.             var height = c.height || 80,  
  40.                 interval = c.interval || 1,  
  41.                 template = c.template,  
  42.                 ratio = video.height / video.width,  
  43.                 preloadImages = function (tmpl, max, start) {  
  44.                     max = Math.floor(max / interval);  
  45.                     if (start === undefined) {  
  46.                         start = 1;  
  47.                     }  
  48.                     function load() {  
  49.                         if (start > max) {  
  50.                             return;  
  51.                         }  
  52.                         var img = new Image();  
  53.                         img.src = tmpl.replace('{time}', start);  
  54.                         img.onload = function () {  
  55.                             start += 1;  
  56.                             load();  
  57.                         };  
  58.                     }  
  59.                     load();  
  60.                 };  
  61.   
  62.             if (c.preload !== false) {  
  63.                 preloadImages(template, video.duration);  
  64.             }  
  65.   
  66.             // 鼠标移动至播放条,可以显示出预览图  
  67.             bean.on(root, 'mousemove.thumbnails''.fp-timeline'function (ev) {  
  68.   
  69.                 $('div.fp-pre,div.fp-next').remove();  
  70.   
  71.                 var x = ev.pageX || ev.clientX,  
  72.                     delta = x - common.offset(timeline).left,   
  73.                     percentage = delta / common.width(timeline),  
  74.                     seconds = Math.round(percentage * api.video.duration);  
  75.   
  76.                 // 2nd condition safeguards at out of range retrieval attempts  
  77.                 if (seconds < 0 || seconds > Math.round(api.video.duration)) {  
  78.                     return;  
  79.                 }  
  80.                 // enables greater interval than one second between thumbnails  
  81.                 seconds = Math.floor(seconds / interval);  
  82.                       
  83.                 var fpthumbnails = $(timelineTooltip).find('.fp-thumbnails'),  
  84.                     // 主缩略图的时间位置div  
  85.                     fpseektime = $(timelineTooltip).find('#fp-seektime'),  
  86.                     // 控制面板的宽度,视频宽度  
  87.                     width = $(root).find('.fp-controls').width(),  
  88.                     // 主缩略图距离视频左侧位置  
  89.                     left = $(timelineTooltip).position().left,  
  90.                     // 主缩略图距离视频右侧位置  
  91.                     right = width - left - (height / ratio)-2,  
  92.                     // 左右侧缩略图的宽度  
  93.                     thumbwidth = c.thumbwidth || 150,  
  94.                     // 步调,即每张缩略图显示的时间隔间  
  95.                     step=c.step || 10;  
  96.   
  97.                 // 主缩略图的样式以及背景图片  
  98.                 fpthumbnails.css({  
  99.                     width: (height / ratio) + 'px',  
  100.                     height: height + 'px',  
  101.                     // {time} template expected to start at 1, video time/first frame starts at 0  
  102.                     'background-image'"url('" + template.replace('{time}', seconds + 1) + "')",  
  103.                     'background-repeat''no-repeat',  
  104.                     'background-size':'100% 100%',  
  105.                     '-moz-background-size':'100% 100%'/* 老版本的 Firefox */  
  106.                     'border''1px solid #333'  
  107.                 });  
  108.                 // 主缩略图的时间样式  
  109.                 fpseektime.css({  
  110.                     height:20 + 'px',  
  111.                     'text-align':'center',  
  112.                     'text-shadow''1px 1px #000'  
  113.                 });  
  114.                 // 左侧缩略图,创建div  
  115.                 if(left>0) {  
  116.                     var leftnum = Math.ceil(left/thumbwidth);  
  117.                     for(var i=0;i<leftnum;i++) {  
  118.                         $(timelineTooltip).parent('.fp-controls').append('<div class="fp-pre"></div>');   
  119.                     }  
  120.                 }  
  121.                 // 右侧缩略图,创建div  
  122.                 if(right>0) {  
  123.                     var rightnum = Math.ceil(right/thumbwidth);  
  124.                     for(var i=0;i<rightnum;i++) {  
  125.                        $(timelineTooltip).parent('.fp-controls').append('<div class="fp-next"></div>');   
  126.                     }  
  127.                 }  
  128.                  
  129.                 //左侧只能显示一个缩略图位置时   
  130.                 if(leftnum==1){  
  131.                     $('.fp-pre').css({  
  132.                         'width':(left-2)+'px',  
  133.                         'height':height + 'px',  
  134.                         'position':'absolute',  
  135.                         'bottom':'30px',  
  136.                         'border-top''1px solid #333',  
  137.                         'border-right''1px solid #333',  
  138.                         'border-bottom''1px solid #333',  
  139.                         'color':'#fff',  
  140.                         'background-color':'#000',  
  141.                         'overflow':'hidden',  
  142.                         'left':'1px',  
  143.                         'background-image':"url('" + template.replace('{time}', seconds + 1-step) + "')"  
  144.                     });  
  145.                 }else {  
  146.                     // 出来最后一个元素的其他元素的样式设置  
  147.                     $('.fp-pre').not(':last').each(function(i,value){  
  148.                         $(this).css({  
  149.                             'width':thumbwidth + 'px',  
  150.                             'height':height + 'px',  
  151.                             'position':'absolute',  
  152.                             'bottom':'30px',  
  153.                             'border''1px solid #333',  
  154.                             'color':'#fff',  
  155.                             'background-color':'#000',  
  156.                             'left':(left - (i+1)*(thumbwidth+2)) + 'px',  
  157.                             'background-image':"url('" + template.replace('{time}', seconds + 1-(i+1)*step) + "')",/*步调*/  
  158.                             'background-repeat''no-repeat',  
  159.                             'background-size':'100% 100%',  
  160.                             '-moz-background-size':'100% 100%'/* 老版本的 Firefox */  
  161.                         });  
  162.                     });  
  163.                     // 设置最后一个的样式  
  164.                     $('.fp-pre').last().css({  
  165.                             'width':(left-(leftnum-1)*(thumbwidth+2)-1)+'px',  
  166.                             'height':height + 'px',  
  167.                             'position':'absolute',  
  168.                             'bottom':'30px',  
  169.                             'border-top''1px solid #333',  
  170.                             'border-right''1px solid #333',  
  171.                             'border-bottom''1px solid #333',  
  172.                             'color':'#fff',  
  173.                             'color':'#fff',  
  174.                             'background-color':'#000',  
  175.                             'overflow':'hidden',  
  176.                             'left':'1px',  
  177.                             'background-image':"url('" + template.replace('{time}', seconds + 1-(leftnum-1)*step) + "')"  
  178.                     });  
  179.                 }  
  180.                 // 右侧只有一个div时的样式  
  181.                 if(rightnum==1){  
  182.                     $('.fp-next').css({  
  183.                         'width':(width-(left+(height / ratio)+2)-2)+'px',  
  184.                         'height':height + 'px',  
  185.                         'position':'absolute',  
  186.                         'bottom':'30px',  
  187.                         'border-top''1px solid #333',  
  188.                         'border-left''1px solid #333',  
  189.                         'border-bottom''1px solid #333',  
  190.                         'color':'#fff',  
  191.                         'color':'#fff',  
  192.                         'background-color':'#000',  
  193.                         'left':(left + (height / ratio)+2) + 'px',  
  194.                         'overflow':'hidden',  
  195.                         'background-image':"url('" + template.replace('{time}', seconds + 1+step) + "')"  
  196.   
  197.                     });  
  198.                 }else {  
  199.                     // 除了最后一个的其他样式  
  200.                     $('.fp-next').not(':last').each(function(i,value){  
  201.                         $(this).css({  
  202.                             'width':thumbwidth + 'px',  
  203.                             'height':height + 'px',  
  204.                             'position':'absolute',  
  205.                             'bottom':'30px',  
  206.                             'border''1px solid #333',  
  207.                             'color':'#fff',  
  208.                             'background-color':'#000',  
  209.                             'left':(left + (height / ratio)+2 + i*(thumbwidth+2))+ 'px',  
  210.                             'background-image':"url('" + template.replace('{time}', seconds + 1+(i+1)*step) + "')",  
  211.                             'background-repeat''no-repeat',  
  212.                             'background-size':'100% 100%',  
  213.                             '-moz-background-size':'100% 100%'/* 老版本的 Firefox */  
  214.   
  215.                         });  
  216.                     });  
  217.                     // 右侧最后一个div样式  
  218.                     $('.fp-next').last().css({  
  219.                             'width':(right - (rightnum-1)*(thumbwidth+2)-1)+'px',  
  220.                             'height':height + 'px',  
  221.                             'position':'absolute',  
  222.                             'bottom':'30px',  
  223.                             'border-top''1px solid #333',  
  224.                             'border-left''1px solid #333',  
  225.                             'border-bottom''1px solid #333',  
  226.                             'color':'#fff',  
  227.                             'background-color':'#000',  
  228.                             'left':(left + (height / ratio)+2 + ((rightnum-1)*(thumbwidth+2))) + 'px',  
  229.                             'overflow':'hidden',  
  230.                             'background-image':"url('" + template.replace('{time}', seconds + 1 +(rightnum-1)*step) + "')"  
  231.   
  232.                         });  
  233.                     }     
  234.   
  235.                 });  
  236.               
  237.     
  238.                 $('.fp-timeline').on('mouseout',function(){  
  239.                     $('div.fp-pre,div.fp-next').remove();  
  240.                 });  
  241.               
  242.         });  
  243.   
  244.     });  
  245.   
  246. })((typeof module === "object" && module.exports)  
  247.     ? require('flowplayer')  
  248.     : window.flowplayer,jQuery);  



在html页面中的用法

[html] view plain copy
  1. <div class="player1  no-toggle play-button">  
  2.           
  3.       </div>  
  4.       <div style="overflow:hidden;width:200px;height:161px;background-image:url('thumbnails/myvideo8.jpg')">  
  5.         <!-- <img src="thumbnails/myvideo8.jpg"> -->  
  6.       </div>  
  7.     </div>  
  8.     <link rel="stylesheet" type="text/css" href="./flowplayer.quality-selector.css">  
  9.     <script type="text/javascript" src="./jquery-1.11.0.min.js"></script>  
  10.     <script type="text/javascript" src="./flowplayer6.js"></script>  
  11.     <script type="text/javascript" src="./thumbnails.js"></script>  
  12.     <script type="text/javascript" src="./flowplayer.quality-selector.js"></script>  
  13.     <script>  
  14.       
  15.     $('.player1').flowplayer({  
  16.       adaptiveRatio:true,  
  17.       debug:true,  
  18.       autoplay:true,  
  19.       embed:false,   
  20.       clip: {  
  21.         title: 'Bauhausfffff',  
  22. //这里配置  
  23.         thumbnails: {  
  24.           template: 'thumbnails/myvideo{time}.jpg',  
  25.           height: 100,  
  26.           thumbwidth: 150,  
  27.           step: 5  
  28.   
  29.         },
  30.               
  31.         sources: [  
  32.             {   
  33.               type: "video/mp4",  
  34.               src:  "http:/host/Video/201503/20150320105047342.mp4"   
  35.             }  
  36.   
  37.         ]  
  38.       },  
  39.   
  40.       brand:{  
  41.         text: "MyBrand",  
  42.         showOnOrigin:false  
  43.       }  
  44.     });  
  45.     </script>  


直接上几个大图

基于flowplayer的视频缩略图的视频预览


基于flowplayer的视频缩略图的视频预览