Chinaunix首页 | 论坛 | 博客
  • 博客访问: 460707
  • 博文数量: 155
  • 博客积分: 2954
  • 博客等级: 少校
  • 技术积分: 1000
  • 用 户 组: 普通用户
  • 注册时间: 2010-01-12 22:00
文章分类

全部博文(155)

文章存档

2014年(2)

2013年(5)

2012年(10)

2011年(33)

2010年(105)

我的朋友

分类: 系统运维

2010-01-13 21:22:25

视频站点的搭建
    挺久没动笔写blog了,换了新工作比较忙是一个原因。最近的工作是做一个素材管理的系统,其中有个要求做视频预览,将用户上传的视频转换并在网页上预览。在网页上看视频,现在大多数视频网站都是采用flv流媒体文件,用flash做的播放器播放,我们也采用了这种方式。流程大概主要:用户上传文件->后台转换文件成flv格式->flv播放器调用flv文件。
    转换视频、音频文件到flv格式可以使用mencoder或者ffmpeg,我们采用了mencoder,在linux上的安装参考这里,安装结束后记的设置环境变量:export LD_LIBRARY_PATH=/usr/local/lib:LD_LIBRARY_PATH
    java调用的话就是通过Process:
Process process = runtime.exec(cmd);
mencoder转换视频音频成flv命令:
mencoder 源文件 -o 目标文件.flv -of lavf    -lavfopts i_certify_that_my_video_stream_does_not_use_b_frames -oac mp3lame -lameopts abr:br=56 -ovc lavc -lavcopts vcodec=flv:vbitrate=400:mbd=2:mv0:trell:v4mv:cbp:last_pred=3:dia=4:cmp=6:vb_strategy=1 -vf scale=200:-3 -ofps 12 -srate 22050

取视频元信息命令(视频比特率、长宽等信息):
mplayer -identify 文件名 -ao null -vo null -frames 0

切割视频命令:
mencoder -ss 开始时间 -oac copy -ovc copy -endpos 终止时间 文件名 -o 目标文件名

    操作flv文件(给视频打上信息、切割之类)可以采用flvtool2。
   
    需要注意的是通过java调用的话,一定要处理标准输出和标准错误输出,不然进程会挂在那结束不了,可以开个线程取处理。在网页播放的话,可以考虑用这个播放器,具体参数看它的说明。最后一个问题,IE6的flash控件需要激活,这个问题的解决可以采用swfobject.js。
    有兴趣的老大们可以考虑自己搭个“土豆网”,说不定哪天拿了风投.....浮云


以下是源码:

Cnvert 类

package com.v246.convertFLV;


import java.util.ArrayList;
import java.util.List;


import com.v246.utils.Aqu;

//作者:古刹飞鹰 blog:懒汉模式
public class Convert {

private static Convert instance = null;

//作者:古刹飞鹰 blog: flv截图工具的绝对地址
private String aquInterceptPicToolsPath = "c:\\FLVTools\\ffmpeg.exe";

//作者:古刹飞鹰 blog: flv修复工具的绝对地址
private String fixFLVToolsPath = "c:\\FLVTools\\flvmdi.exe";

//作者:古刹飞鹰 blog: flv转换工具的绝对地址
private String aquCoverFLVToolsPath = "c:\\FLVTools\\mencoder\\mencoder.exe";

private Convert() {
  //作者:古刹飞鹰 blog:init();
}

public static Convert getInstance() {
  if (instance == null) {
   instance = new Convert();
  }
  return instance;
}
public void reset(){
  //作者:古刹飞鹰 blog:init();
}
private void init() {
  /*aquInterceptPicToolsPath = Aqu.getXMLValue("convertFLV.xml",
    "//作者:古刹飞鹰 blog:古刹飞鹰/FLV转换工具全路径")[0];
  fixFLVToolsPath = Aqu
    .getXMLValue("convertFLV.xml", "//作者:古刹飞鹰 blog:古刹飞鹰/FLV截图工具全路径")[0];
  aquCoverFLVToolsPath = Aqu.getXMLValue("convertFLV.xml",
    "//作者:古刹飞鹰 blog:古刹飞鹰/FLV修复工具全路径")[0];
  aquInterceptPicToolsPath = aquInterceptPicToolsPath.replace("/", "\\");
  fixFLVToolsPath = fixFLVToolsPath.replace("/", "\\");
  aquCoverFLVToolsPath = aquCoverFLVToolsPath.replace("/", "\\");
  System.out.println(aquInterceptPicToolsPath);
  System.out.println(fixFLVToolsPath);
  System.out.println(aquCoverFLVToolsPath);*/

}

public synchronized boolean convert(String aquInputPath, String outPath) {
  boolean re = false;
  //作者:古刹飞鹰 blog: 通一路径定位符
  aquInputPath = aquInputPath.replace("/", "\\");
  //作者:古刹飞鹰 blog: 通一路径定位符
  outPath = outPath.replace("/", "\\");
  //作者:古刹飞鹰 blog: 分析不包含文件名的输出路径
  String interceptPicSavePath = outPath.substring(0, outPath
    .lastIndexOf("\\") + 1);
  //作者:古刹飞鹰 blog: 分析输出的文件名
  String outFileName = outPath.substring(outPath.lastIndexOf("\\") + 1,
    outPath.length());
  //作者:古刹飞鹰 blog: 继续分析得出不包含扩展名的文件名
  String outFileNameNoExt = outFileName.substring(0, outFileName
    .lastIndexOf("."));

  //作者:古刹飞鹰 blog: 工作目录,随便设置
  //作者:古刹飞鹰 blog: String workdirectory = "c:\\windows\\temp";
  //作者:古刹飞鹰 blog: 修复命令
  List parameterForFix = new ArrayList(100);
  //作者:古刹飞鹰 blog: 转换命令
  List aquParameterForConvert = new ArrayList(100);
  //作者:古刹飞鹰 blog: 截图命令
  List aquParameterForIntercept = new ArrayList(100);
  //作者:古刹飞鹰 blog: 构建转换命令
  aquParameterForConvert.add(aquCoverFLVToolsPath);
  aquParameterForConvert.add("-vf");
  aquParameterForConvert.add("scale=320:240");
  aquParameterForConvert.add("-ffourcc");
  aquParameterForConvert.add("FLV1");
  aquParameterForConvert.add("-of");
  aquParameterForConvert.add("lavf");
  aquParameterForConvert.add("-lavfopts");
  aquParameterForConvert
    .add("i_certify_that_my_video_stream_does_not_use_b_frames");
  aquParameterForConvert.add("-ovc");
  aquParameterForConvert.add("lavc");
  aquParameterForConvert.add("-lavcopts");
  aquParameterForConvert.add("vcodec=flv:vbitrate=200");
  aquParameterForConvert.add("-srate");
  aquParameterForConvert.add("22050");
  aquParameterForConvert.add("-oac");
  aquParameterForConvert.add("lavc");
  aquParameterForConvert.add("-lavcopts");
  aquParameterForConvert.add("acodec=mp3:abitrate=56");
  aquParameterForConvert.add(aquInputPath);
  aquParameterForConvert.add("-o");
  aquParameterForConvert.add(outPath);

  //作者:古刹飞鹰 blog: 构建修复命令
  parameterForFix.add(fixFLVToolsPath);
  parameterForFix.add(outPath);
  //作者:古刹飞鹰 blog: 构建截图命令
  aquParameterForIntercept.add(aquInterceptPicToolsPath);
  aquParameterForIntercept.add("-i");
  aquParameterForIntercept.add(outPath);
  aquParameterForIntercept.add("-y");
  aquParameterForIntercept.add("-f");
  aquParameterForIntercept.add("image2");
  aquParameterForIntercept.add("-ss");
  aquParameterForIntercept.add("8");
  aquParameterForIntercept.add("-t");
  aquParameterForIntercept.add("0.001");
  aquParameterForIntercept.add("-s");
  aquParameterForIntercept.add("320x240");
  aquParameterForIntercept.add(interceptPicSavePath + outFileNameNoExt
    + ".jpg");
  //作者:古刹飞鹰 blog:转换
  String tmp1 = Aqu.exec(aquParameterForConvert);
  //作者:古刹飞鹰 blog:截图
  String tmp2 = Aqu.exec(aquParameterForIntercept);
  return re;
}

public static void main(String[] args) {
  getInstance().convert("h:\\QQ28095553\\古刹飞鹰.wmv", "h:\\aquaqu(quana)gmail.com\\古刹飞鹰.flv");
}
}

ConvertThread 类

package com.v246.convertFLV;

public class ConvertThread extends Thread{

private String fromPath = null;
private String toPath = null;
@Override
public void run(){
  Convert.getInstance().convert(fromPath, toPath);
}
public void setFromPath(String fromPath) {
  this.fromPath = fromPath;
}
public void setToPath(String toPath) {
  this.toPath = toPath;
}
}



ConvertThreadProxy类:

package com.v246.convertFLV;

public class ConvertThreadProxy {
public static void convert(String fromPath, String toPath) {
  ConvertThread ct = new ConvertThread();
  ct.setFromPath(fromPath);
  ct.setToPath(toPath);
  ct.start();
}
}





使用的时候只要通过ConvertThreadProxy 类的静态方法将源视频绝对地址(包括文件名+括展名)和要生成的FLV文件的绝对地址(包括文件名+括展名)以字符串的方式传进去即可!因为用的是多线程,所以转换过程不会占用当前线程!

核心转换类是线程同步的,所以您不用担心并法问题,因为一次只能转换一个文件!

注:安装和使用ffmpeg转换视频为flv文件(windows和linux)
用java程序调用ffmpeg执行视频文件格式转换flv


   Mencoder是Mplayer自带的编码工具(Mplayer是Linux下的播放器,开源,支持几乎所有视频格式的播放,现在有windows和Mac版本)。

Mplayer的获得与配置:

     Mplayer windows版本下载列表:

     列表中会有版本发布日期,可以挑选最新的版本,也可以选择old/去下载老的版本,笔者用的是6月份的版本。下载后解压到某个文件夹中即可。

     Codecs下载列表:

     选择windows-all-********.zip(*表示年月日)下载,将zip包中的文件夹下所有文件,包括*.dll、*.acm、*.ax等等全部copy到Mplayer根目录下的codecs文件夹中。

     此时最好把Mplayer.exe所在路径,同时也是Mencoder.exe所在路径添加到环境变量path中。

     现在可以试试用Mplayer播放视频,比如有个视频位于D:\music\APerfectMatch.wmv,那么可以打开一个cmd窗口,输入 mplayer “D:\music\APerfectMatch.wmv”,感受一下来自Linux的播放器吧,可以通过键盘来操纵。

Mencoder转换视频格式:

     以将各种格式转换为flv格式为例(flv格式是flash支持的视频格式):

     mencoder "E:\test.m2p" -o "E:\output.flv" -of lavf  -lavfopts i_certify_that_my_video_stream_does_not_use_b_frames -oac mp3lame -lameopts abr:br=56 -ovc lavc -lavcopts vcodec=flv:vbitrate=500:mbd=2:mv0:trell:v4mv:cbp:last_pred=3:dia=4:cmp=6:vb_strategy=1 -vf scale=512:-3 -ofps 12 -srate 22050

     在命令行中输入这行代码(注意:windows的命令行是不支持换行的),按回车运行,一段时间之后就可以得到一个.flv文件,播放一下看看品质如何(可以直接用Mplayer播放)。

     下图是我这边转换的效果对比,左边是原视频,右边是转换后的视频:



    所有人都会觉得,转换后品质下降了很多,确实是这样,同时可以发现转换后的文件由原来的13M变成了1M,如果要提高品质,可以将vbitrate=500改为vbitrate=5000。

    转换后的效果对比,左边是原视频,右边是转换后的视频:



     品质几乎一样了,但同时,转换文件变成了6M。关于命令中的一些参数,解释一下:

     -o "E:\output.flv":是输出文件路径;

     -of lavf:是输出文件格式,这里不是flv而是lavf,是因为flv属于libavformat;

     -lavfopts i_certify_that_my_video_stream_does_not_use_b_frames:-lavfopts表示对lavf进行一些设置,设置的内容就是i_certify_that_my_video_stream_does_not_use_b_frames,翻译成中文就是:我确定,我的视频不用B frames,对于转换flv格式,最好加上这个参数,否则可能会报错,如图:


     -oac mp3lame:oac=output audio codec,输出音频编码器,这里用的mp3lame;

     -lameopts abr:br=56:lame options,是专门针对mp3lame的参数设置,abr:br=56,是设置音频比特率为56(比特率:每秒钟输出的音频大小,单位kb/s);

     -ovc lavc:ovc=output video codec,输出视频编码器,lavc表示one of libavcodec’s video codec,输出格式为libavformat之一,编码器当然也是libavcodec之一啦,至于libavodec里都有哪些编码器,可以查看 Mplayer的官方文档;

    -lavcoptsvcodec=flv:vbitrate=500:mbd=2:mv0:v4mv:trell:cbp:last_pred=3:dia=4:cmp=6:vb_strategy=1:对lavc进行一些设置,详细介绍如下:

     vcodec=flv:指定视频编码器为flv;

     vbitrate=500:指定视频比特率为500,这个参数很重要,vbitrate大了可以让视频品质增加,但会让文件变的很大(可以参考flash8自带的转换工具的参数:低品质为150kb/s,中等品质为400kb/s,高品质为700kb/s)。

     mbd:宏模块选择算法,值为0~2默认为0,值越大转换越缓慢,但在品质和文件大小方面有好处;

     mv0:编译每个宏模块并选择最好的一个,当mbd=0时无效;

     v4mv:会稍微增加品质,mbd>0时效果更明显;

     trell:量子化网格搜索,对每8×8block找到最优化编码;

     cbp:只能和trell一期使用,评估失真的图像块编码;

     last_pred=3:与上一帧相比的移动数量预测,值为0~99,1~3比较合适,大于3时对品质的提高已经无关紧要,但却会降低速度;

     dia=4:移动搜索范围,值为-99~6,对于快速转换,-1是个不错的值,如果不是很重视速度,可以考虑2~4;

     cmp=6:值为1~2000,默认为0,设置用于全象素移动预算的比较函数

     vb_strategy=1:对动作很大的场景会有帮助,对有些视频,设置vmax_b_frames会有损品质,加上vb_strategy=1会好点。

     以上是对-lavcopts的详细解释,接下来继续说明mencoder的参数:

     -vf scale=512:-3:-vf表示视频格式,scale是缩放,512:-3表示强制将宽度设置为512,高度写为-3表示保持高宽比,也可以设置为 -1或-2,具体表示什么,有兴趣的可以尝试一下。如果要强制转化为统一大小,可以直接写-vf scale=640:480,但笔者个人建议用-3来保持高宽比。-vf里还有expand和crop参数,例如:-vf scale=512:384,expand=512:384:::1,crop=512:384:0:0,expand表示膨胀,crop表示裁剪;

     -ofps 12:输出视频的帧频,一般,用于flash播放的视频帧频高了没有没有意义,反而会增加视频文件大小,但如果ofps设置的不合适,比如源视频帧频不是 ofps的整数倍,可能会导致转换后的视频、音频不同步,似乎可以将这一参数改为-noskip来解决这一问题;  

    -srate 22050:音频采样率一般为22050或44100。

    对于转换flv格式,lavsopts的设置比较重要,还有很多参数本文没有涉及到,在笔者参考的文献中会有具体的说明,感兴趣的可以去看一下。我对于Mencoder的认识主要也是来源于下面2篇文章,实践的比较少,如果有理解和翻译的失误,再次欢迎与我交流。
 
 
原文地址:
http://www.blogjava.net/killme2008/archive/2007/12/19/168788.html
http://jiataibin.javaeye.com/blog/198235

阅读(1375) | 评论(0) | 转发(0) |
0

上一篇:flv网页播放器

下一篇:Jquery的跨域调用

给主人留下些什么吧!~~