http://blog.csdn.net/leixiaohua1020/article/details/15811977/javascript
本文记录一个最简单的视频网站系统。此前作过一些基于JavaEE中的 SSH (Strut2 + Spring + Hibernate)的网站系统,可是一直没有作过一个视频网站系统,因此就打算作一个“精简”的视频网站系统,以方便之后测试以及学习使用。本视频网站支持直播(经过RTMP实现)和点播(经过HTTP实现)。为了保持精简,这个视频网站系统仅制做了网络视频的管理功能(增删改查),以及相关的参数配置功能。因为本身在JavaEE方面没有深刻学习过,因此这个系统有部分功能还没搞完,之后有时间再慢慢完善。html
系统前台显示采用了Div + CSS+ Javascript技术。其中前台显示用了一些Javascript插件,例如说生成旋转灯笼式效果的CloudCarousel,生成导航菜单的 ddsmoothmenu,用于表单验证的jQuery Validation Engine等等。其中视频播放器用了FlowPlayer(包含了RTMP插件)来支持HTTP的点播和RTMP的直播。按理说视频文件上传的时候可使用Ajax实现,可是考虑工做比较繁琐,因此暂时仍是使用直接文件上传的方式。java
系统的后台使用了JavaEE系统中最传统的三层架构:Action层,Service层以及DAO层。其中Action层使用了Struts2框架,用于处理前台页面传来的请求;DAO层使用了Hibernate框架,用于和数据库的交互。Spring框架则用于将Action层,Service层以及 DAO层整合起来。系统的先后台交互主要使用了JSTL标签和EL表达式。此外考虑到国际化方面的要求,采用了Struts2的i18n方式,将全部页面上的文字都抽取坐来保存到单独的文件中,这样就能够实现多种语言的显示。git
系统开始运行后,会开启两个线程VideoThumbnailThread和VideoTranscoderThread。 VideoThumbnailThread用于截取视频的缩略图,VideoTranscoderThread则用于转码上传的视频。这两个线程都是经过调用ffmpeg.exe完成相应的功能。程序员
此外,系统中还包含了查看媒体信息的功能。该功能经过调用MediaInfo.dll完成,再也不详述。github
整个系统的框架以下图所示,后文中再详细记录每部分的功能。web
系统主要包含了如下几个页面:sql
主页(index.jsp):进入系统的第一个页面。
视频列表(videolist.jsp):列表显示视频资源的页面。按照视频源的类型的不一样能够分为点播(VOD)视频列表和直播(Live)视频列表。
视频编辑(videoedit.jsp):编辑视频资源的页面。按照视频源的类型的不一样能够分为点播(VOD)编辑和直播(Live)编辑。按照操做的不一样能够分为添加(Add)和编辑(Edit)。
视频内容(videocontent.jsp):查看视频的内容的页面。按照视频源的类型的不一样能够分为点播(VOD)视频内容和直播(Live)视频内容。
设置(configure.jsp):设置页面。用于配制系统的参数。
关于(about.jsp):显示系统相关的一些信息的页面。
网站首页的以下图所示。Logo位于左上角,菜单栏位于右上角。为了美观一些,页面上方作了一个灯箱效果的特效,随机显示一些视频的缩略图。页面下方则分别列出“点播列表”和“直播列表”中最近添加的几个视频。数据库
点播列表页面以下图所示。在该页面中,能够对点播视频进行简单的管理:查看内容,添加,修改以及删除。编程
点播列表页面以下图所示。直播列表实质上使用了和点播列表相同的页面。在该页面中,能够对直播视频进行简单的管理:查看内容,添加,修改以及删除。
视频内容页面以下所示。该页面中包含了一个FlowPlayer播放器,该播放器用于播放视频内容。
点播视频内容页面以下所示。
直播视频内容页面以下所示。尽管表面上看点播和直播页面彻底类似,可是实际上点播和直播的机制是不同的。点播依靠的是HTTP渐进式下载,而直播依靠的是FlowPlayer的RTMP组件。
点播视频添加页面以下所示。该页面用于上传一个点播视频。“名称”输入框用于指定视频的名称,“文件”用于指定上传的视频文件,“简介”能够指定针对该视频的一段简介。其中,“名称”和“文件”两个输入框为必填项,不然没法提交该页面的表单。
直播视频添加页面以下所示。从图中能够看出直播和点播的添加页面很相似。它们的不一样在于:点播是上传一个视频文件,而直播是指定一个URL(目前该URL只支持RTMP)。和点播相似,“名称”和“URL”两个输入框为必填项,不然没法提交该页面的表单。
点播视频编辑页面以下所示。该页面用于编辑已经完成上传的点播视频的信息。
直播视频编辑页面以下所示。该页面用于编辑直播视频的信息。
系统的配置页面以下图所示。该页面用于配置系统的各类设置(以转码设置为主)。每次配置完成后重启系统后设置生效。包含如下参数。
MediaInfo信息位于视频内容页面中,在默认的状况下是不显示的。经过修改网页中代码能够显示视频的MediaInfo信息。
转码前文件信息以下图所示,从图中能够看出转码前的文件格式是MPEG-PS,视频编码为MPEG-1,音频编码为MP2。
转码后文件信息以下图所示,从图中能够看出转码前的文件是FLV格式的,视频编码为H.264,音频编码MP3。
本系统支持多国语言。选择右上角菜单的“语言”->“English”能够将系统切换到英文。英文界面以下图所示。
英文版的视频点播列表以下图所示。
英文版的视频内容页面以下图所示。
英文版的配置界面以下图所示。
下面简单记录一下网站部分的关键源代码。分红三个部分:数据库、前台和后台。
系统包含了4张表:video,videostate,category,configure。下面简单记录每一个表的含义。
Video表用于存储视频记录。每个视频对应Video表中的一条记录。该表中的字段以下表所示。
备注
*其中点播视频的URL为视频文件的相对路径。
下面例举Video表中的几项和URL有关数据(受限于篇幅,省略其它字段)。
Videostate表用于存储视频的状态。该表中字段以下表所示。
Category表用于存储视频的分类。该表能够用于肯定视频的分类,目前尚未作太多开发。
Configure表
Configure表用于存储系统配置信息。该表中字段以下表所示。
该表的内容以下。
前台部分没有太多能够记录的,基本上都是各类DIV和CSS的调整。其中使用了较多的JSTL标签以及EL表达式。此外使用了Struts2标签用于支持国际化。基本的jsp页面只有6个:
index.jsp:首页。
videolist.jsp:视频列表——直播视频列表,点播视频列表。
videoedit.jsp:视频编辑——直播视频添加,直播视频编辑,点播视频添加,点播视频编辑。
videocontent.jsp:视频内容——直播视频内容,点播视频内容。
configure.jsp:配置页面。
about.jsp:关于页面。
除了基本页面以外,还有一些辅助页面:
cfooter.jsp:页脚。
cheader.jsp:标题栏。
csidebar_recent.jsp:侧边栏(最近添加)。
error.jsp:错误页面。
前台页面中用于视频播放的播放器是FlowPlayer。FlowPlayer播放器安装有3个步骤:
(1)包含FlowPlayer的Js文件
<script type="text/javascript" src="videoplayer/flowplayer-3.2.8.min.js"></script>
(2)添加一个<a>标记
<a href="http://www.mywebsite.com/myVideo.flv" style="display:block;width:425px;height:300px;" id="player"> </a>
(3)添加一段Javascript代码,其中要包含FlowPlayer的Swf文件
<script language="JavaScript"> flowplayer("player", "path/to/the/flowplayer-3.2.18.swf"); </script>
FlowPlayer经过HTTP播放点播视频文件的源代码以下所示。${video.url}中保存了视频的相对URL。
<a id="player" href="${pageContext.request.scheme}://${pageContext.request.serverName}:${pageContext.request.serverPort}${pageContext.request.contextPath}/${video.url}"> </a> <script> flowplayer("player", "videoplayer/flowplayer-3.2.8.swf"); </script>
FlowPlayer 经过RTMP播放直播视频文件的示例代码以下所示。${video.url}中保存了视频的URL。播放RTMP的时候用到了FlowPlayer的 RTMP Plugin。须要注意的是,RTMP的URL须要拆分开来分别填到不一样的位置。在这里经过split(‘/’)函数按照“/”将字符串分离为字符串数组。而后将“protocol://server/app”填至plugins的netConnectionUrl字段,playpath填至clip的 url字段。
<a id="player"> </a> <!-- Parse RTMP URL --> <script> str='${video.url}'; arr=str.split('/'); //rtmp://server/app/playpath protocol=arr[0]; server=arr[2]; app=arr[3]; playpath=arr[4]; lowplayer("player", "videoplayer/flowplayer-3.2.8.swf",{ clip: { //scaling:'orig', url: playpath, provider: 'rtmp', live:'true' }, plugins: { rtmp: { url: 'videoplayer/flowplayer.rtmp-3.2.8.swf', netConnectionUrl: protocol+'//'+server+'/'+ app } } }); script>
后台部分采用了JavaEE的SSH (Struts2 + Spring + Hibernate)的三层架构,其中的代码再也不详细记录。三层分别为Action层、Service层和DAO层。其中Action层采用了 Struts2框架,DAO层采用了Hibernate框架,而Spring则整合了这三个层面。
SSH 三层架构虽然在解耦合方面作得很好,可是却致使开发起来很是繁琐,严重下降了开发的速度。为了方便起见,本系统中在Service层编写了 BaseService用于完成通用的Service操做;在DAO层也编写了BaseDAO用于完成通用的DAO操做。这样除非有特殊需求,通常状况下只要调用BaseService中的方法就能够完成各类基本的操做。BaseService包含的接口以下所示。
/** * 最简单的视频网站 * Simplest Video Website * * 雷霄骅 Lei Xiaohua * * leixiaohua1020@126.com * 中国传媒大学/数字电视技术 * Communication University of China / Digital TV Technology * http://blog.csdn.net/leixiaohua1020 * * 本程序是一个最简单的视频网站视频。它支持 * 1.直播 * 2.点播 * This software is the simplest video website. * It support: * 1. live broadcast * 2. VOD */ package service; import java.util.List; /** * @author 雷霄骅 * 对Object的Service * 包含了一些通用的方法 */ public interface BaseService { /** * 保存一个对象 * @param object 一个对象 */ public void save(Object object); /** * 更新一个对象 * @param object 一个对象 */ public void update(Object object); /** * 删除一个对象 * @param object 一个对象 */ public void delete(Object object); /** * 根据ID读取一个指定名称的对象 * @param targetName 对象的名称 * @param id 对象的ID * @return 一个对象 */ public Object ReadByID(String targetName,int id); @SuppressWarnings("rawtypes") /** * 获取指定类型的全部对象 * @param targetName 对象类型名称 * @return 对象的列表 */ public List ReadAll(String targetName); @SuppressWarnings("rawtypes") /** * 根据“属性-值”获取多个指定类型的对象 * @param targetName 对象类型名称 * @param propertyName 对象中属性的名称 * @param propertyValue 对象中属性的值 * @return 对象的列表 */ public List ReadByProperty(String targetName,String propertyName,Object propertyValue); /** * 根据“属性-值”获取一个指定类型的对象 * @param targetName 对象类型名称 * @param propertyName 对象中属性的名称 * @param propertyValue 对象中属性的值 * @return 一个对象 */ public Object ReadSingle(String targetName,String propertyName,Object propertyValue); /** * 获取多个指定类型的对象,能够限定获取对象数目的多少,而且根据特定的属性进行排序。 * @param targetName 对象类型名称 * @param propertyName 对象中属性的名称,用于排序 * @param num 结果对象列表的最大数目 * @param order 排序方式,能够选择“asc”或者“desc” * @return 对象的列表 */ public List ReadLimitedByOrder(String targetName,String propertyName,int num,String order); /** * 获取指定类型的对象的数量。 * @param targetName 对象类型名称 * @return 数量 */ public int ReadCount(String targetName); /** * 根据“属性-值”为条件,获取指定类型的对象的数量。 * @param targetName 对象类型名称 * @param propertyName 对象中属性的名称 * @param propertyValue 对象中属性的值 * @return 数量 */ public int ReadCountByProperty(final String targetName,String propertyName, Object value); /** * 两个功能: * 1.根据“属性-值”获取多个指定类型的对象 * 2.限定获取对象数目的多少,而且根据特定的属性进行排序。 * @param targetName 对象类型名称 * @param readpropertyName 对象中属性的名称,用于获取对象 * @param readvalue 对象中属性的值 * @param orderpropertyName 对象中属性的名称,用于排序 * @param num 结果对象列表的最大数目 * @param order 排序方式,能够选择“asc”或者“desc” * @return */ public List ReadByPropertyAndLimitedByOrder(final String targetName, final String readpropertyName, final Object readvalue,final String orderpropertyName, final int num, final String order); }
系统在启动后会开启两个线程:VideoThumbnailThread和VideoTranscoderThread。其中 VideoThumbnailThread线程会不停的检测须要截取缩略图的视频,调用相应的FFmpeg命令截取缩略图;VideoTranscoderThread线程会不停的监测须要转码的视频,调用相应的FFmpeg命令转码视频。何以肯定视频是否须要截取缩略图以及是否须要转码?这是经过video表中的videostateid字段来标识的。视频开始上传后,该视频记录会被标记为“等待上传”(第1步);上传完毕后,该视频记录会被标记为“等待截图”(第2步);截图完毕后,该视频记录会被标记为“等待转码”(第3步);转码完毕后,该视频记录会被标记为“完成”(第4步)。VideoThumbnailThread会不停地检查系统中“等待截图”的视频,截图完成后将视频标记为“等待转码”;VideoTranscoderThread会不停地检查系统中“等待转码”的视频,转码完成后将视频标记为“完成”。
FFmpeg和Java整合的过程当中有如下几个须要注意的地方:
(1)路径的处理
FFmpeg处理视频的时候须要用到绝对路径,因此涉及到绝对路径获取的问题。使用ServletContext的getRealPath("/")方法能够得到当前Web应用根目录的绝对路径。例如在本身的电脑上下述代码能够得到Web应用的据对路径:
ServletContext servletContext; System.out.println(servletContext.getRealPath("/"));
执行完后输出为:
D:\MyEclipseWorkspace\.metadata\.me_tcat\webapps\simplest_video_website\
相对路径中的URL中路径的分隔采用“/”(正斜杠,Unix系统使用),而上述代码中路径的分隔采用“\”(反斜杠,Windows系统使用)。若是把相对文件路径和绝对目录路径拼接起来传递给FFmpeg的话,FFmpeg是能够识别的,可是这样一来路径中一会“/”一会“\”会给人一种很别扭的感受,所以可使用String的replace()方法将“\”统一替换为“/”,这样就整齐多了。例以下面代码:
ServletContext servletContext; System.out.println(servletContext.getRealPath("/").replace('\\', '/'));
执行完后输出为:
D:/MyEclipseWorkspace/.metadata/.me_tcat/webapps/simplest_video_website/
(2)FFmpeg命令行的调用
FFmpeg命令行的的调用能够分红两个步骤:
(a)生成符合设置的命令
这一步骤实际上就是一个简单的字符串拼接的过程。根据配置的参数拼接成相应的转码命令。须要注意的是,在输入和输出的文件路径两边要加上双引号。不然当文件路径中包含空格的时候,会致使路径解析错误。
(b)调用命令行
调用命令行使用Runtime.getRuntime().exec(cmdstr)方法就能够了。其中cmdstr常见的格式有两种:
cmd /c {FFmpeg Command} cmd /c start {FFmpeg Command}
第一种格式是在本窗口中直接执行命令,第二种格式是新打开一个窗口执行命令。第一种方法我在JavaEE环境中测试有问题,并且不弹出窗口不便于调试,因此使用第二种执行方法。
(c)等待调用的命令行执行完毕
Runtime.getRuntime().exec()在调用命令后就直接返回当前线程了。这不符合实际的需求。实际中须要系统完成FFmpeg转码工做后,才能作下一步操做。能够用Process的waitFor()方法阻塞当先线程直至调用程序运行结束。
FFmpeg截取缩略图命令以下:
ffmpeg -y -i "ourtime.flv" -ss 5 -s 220x110 -f image2 -vframes 1 "thumbnail.jpg"
参数含义以下:
-y:输出文件重名的时候,自动覆盖。
-i:输入文件路径(能够是相对路径或者绝对路径)。
-ss:截取缩略图的时间点,这里是5s处。
-s:输出缩略图的分辨率,这里是220x110。
-f:输出文件格式,这里的image2表明文件格式为图片。
-vframes:输出视频帧的个数,这里是1。
最后一个参数为输出的缩略图文件路径。
FFmpeg转码命令以下:
ffmpeg -y -i "ourtime.flv" -vcodec libx264 -b:v 500000 -r 25 -acodec libmp3lame -b:a 64000 -ar 22050 -vf scale=w=640:h=360:force_original_aspect_ratio=decrease,pad=w=640:h=360:x=(ow-iw)/2:y=(oh-ih)/2[aa];movie=svw.png[bb];[aa][bb]overlay=x=5:y=5 "ourtime_convert.flv"
参数含义以下:
-y:输出文件重名的时候,自动覆盖。
-i:输入文件路径(能够是相对路径或者绝对路径)。
-vcodec:视频编码器,这里是libx264。
-b:v:视频码率,这里是500000bps。
-r:视频帧率,这里是25fps。
-acodec:音频编码器,这里是libmp3lame。
-b:a:音频码率,这里是64000bps。
-ar:音频采样率,这里是22050Hz。
-vf:滤镜,用于图像拉伸以及水印叠加。
最后一个参数为输出的视频文件路径。
Filter(滤镜)配置
几句话介绍一下Filter的配置:
下面分别记录几个滤镜的使用方法。
【scale滤镜】
scale滤镜使用libswscale对图像进行拉伸。它的参数含义以下:
w:输出图像的宽。
h:输出图像的高。
force_original_aspect_ratio:保持视频宽高比的方法,可使用以下值:(1)disable——不保持宽高比;(2)decrease——须要的时候下降宽或者高;(3)increase——须要的时候提升宽或者高。
有关force_original_aspect_ratio能够举个例子:输入视频分辨率是1920x800,设置滤镜为:
scale=w=640:h=360:force_original_aspect_ratio=decrease
输出的视频分辨率为640x266。
【pad滤镜】
pad滤镜用于给拉伸后的图像加“黑边”。通过scale滤镜处理以后,视频的宽必定小于等于640,而视频的高必定小于等于360,此时须要使用pad滤镜填充视频的两边(上下或者是左右),保证输出的视频的分辨率为640x360。
Pad滤镜有四个基本的参数:
w:填充后视频的宽度
h:填充后视频的高度
x:输入视频的左上角在填充后视频中的x坐标
y:输入视频的左上角在填充后视频中的y坐标
一个基本的如法以下所示:
pad=w=640:h=480:x=0:y=40
上述滤镜将视频填充为640x480,同时将输入视频的左上角放在(0,40)的位置上。
在给视频加黑边的过程当中,须要把输入的视频放在填充后视频的中央,而pad滤镜中输入视频的位置是以左上角来肯定的,所以肯定输入视频的左上角的坐标是一个比较麻烦的事。对此,pad滤镜提供了如下几个变量。
iw:输入视频的宽
ih:输入视频的高
ow:输出视频的宽
oh:输出视频的高
经过上述几个变量,能够得知若是想把输入视频放在填充后视频的中央,输入视频的x坐标应该为(ow-iw)/2,y坐标应该为(oh-ih)/2。
【movie滤镜】
Movie 滤镜属于Source滤镜,它用于读取水印图片文件(通常状况下是一个PNG文件)。有关movie滤镜有一个地方须要特别注意:它在Windows下彷佛不支持输入路径为绝对路径。由于Windows下文件的绝对路径一般为“D:\test\......\watermark.png”,即其中开头的盘符后面跟着一个“:”。“:”在Filter中是一个特殊字符,会被解析成参数的分隔符,从而致使输入的绝对路径被解析为2个参数,最后形成错误。这个问题当时我调试了半天仍然没有获得解决。所以,Windows下使用movie滤镜的时候,须要保证水印文件就在当前的工做目录中,才能正常运行。
【overlay滤镜】
Overlay滤镜用于叠加两路输入。具体到本文的系统中就是叠加水印文件到视频文件上。它经常使用的有两个参数:
x:水印左上角的x坐标
y:水印左上角的y坐标
通过上述几个滤镜的处理,最终就能够获得一个分辨率为640x360,保持宽高比(加黑边),叠加过水印的视频。
Simplest Video Website
开源中国:http://git.oschina.net/leixiaohua1020/simplest_video_website
安装步骤
1.安装JavaEE环境:
(1)下载安装JDK
(2)下载安装Tomcat
(3)下载安装MySQL
2.FFmpeg
(1)下载而且解压缩FFmpeg可执行程序
(2)把FFmpeg的bin目录(其中包含ffmpeg.exe)添加至系统的"path"环境变量(重要,这样才能够在系统任意目录中使用ffmpeg命令)
3.复制程序
(1)修改Webroot\WEB-INF\classes\hibernate.cfg.xml中数据库的用户名和密码
(2)拷贝WebRoot目录至Tomcat的webapps目录,从新取个名字(例如"svw")
(3)在MySQL中建立数据库"svw",在其中执行svw.sql,建立数据库中的表,而且添加测试数据
4.启动Tomcat
5.使用浏览器访问http://localhost:8080/svw
http://www.cnblogs.com/xkfz007/p/4517966.html
♣FFmpeg是什么?
♣FFmpeg组成
♣下载工具
♣安装FFmpeg
♣应用到j2ee项目
前言:学习视频编码,必定要知道雷霄骅(leixiaohua1020)的专栏 ,伟大的程序员,26岁去世。雷霄骅---巨星陨落。此文多为摘录自ffmpeg的文档。
github地址:https://github.com/FFmpeg/FFmpeg
1.FFmpeg是什么?
构成FFmpeg主要有三个部分,
2.1第一部分是四个做用不一样的工具软件,分别是:ffmpeg.exe,ffplay.exe,ffserver.exe和ffprobe.exe。
2.2第二部分是能够供开发者使用的SDK,为各个不一样平台编译完成的库。若是说上面的四个工具软件都是完整成品形式的玩具,那么这些库就至关于乐高积木同样,咱们能够根据本身的需求使用这些库开发本身的应用程序。这些库有:
2.3第三部分是整个工程的源代码,不管是编译出来的可执行程序仍是SDK,都是由这些源代码编译出来的。FFmpeg的源代码由C语言实现,主要在Linux平台上进行开发。FFmpeg不是一个孤立的工程,它还存在多个依赖的第三方工程来加强它自身的功能。在当前这一系列的博文/视频中,咱们暂时不会涉及太多源代码相关的内容,主要以FFmpeg的工具和SDK的调用为主。到下一系列咱们将专门研究如何编译源代码并根据源代码来进行二次开发。
3.下载工具
在官网上咱们能够找到"Download"页面,该页上能够下载FFmpeg的工具、库和源代码等。在选择"Windows Packages"下的Windows Builds后,会跳转到Windows版本的下载页面:
在下载页面上,咱们能够看到,对于32位和64位版本,分别提供了三种不一样的模式:static、shared和dev
4.安装FFmpeg
4.1 这里下载的是static版本,将其下载解压到E:\javaEdit\ffmpeg目录下:
4.2配置环境变量:E:\javaEdit\ffmpeg\bin;
4.3 dos窗口输入 ffmpeg -version 命令,若有信息则说明成功:
5.应用到j2ee项目: