系统管理员在成功登陆并验证身份后,进入管理后台,可以看到目前所有已上传的视频。点击“添加视频”按钮,跳转至添加视频的页面
第一步,填入视频的标题和对该视频内容的简明描述、选择文件资源,并提交,再通过upload.js的格式(类似于标题简介是否为空的校验)验证后,才开始上传视频文件,根据上传的进度,等待文件上传,表单中需设置enctype=”multipart/form-data”参数,告诉系统传输的是非文本内容。
第二步,视频上传成功后,后端使用MultipartFile这个类接收前端传过来的文件,然后再检验上传文件的格式和大小,符合要求的文件才会进入下一步。
第三步,系统根据检测的视频格式,使用TransfMediaTool对视频进行解码和再次编码,如果是ffmpeg不能够转化的格式则先用mencoder工具将其先转为avi,然后再使用ffmpeg转为mp4格式,转化成功后,将视频存入本地磁盘中,再去获取视频的缩略图,等待缩略图进程完成后,系统会获取当前时间和上传视频的编号,将该视频信息添加到数据库的videoffmpeg数据表中,整个上传视频的过程才算结束。
视频转码时,会先将转码的命令放入创建的List集合中。当视频后缀名为wmv9、rm、rmvb时,调用mencoder.exe工具将它们处理为avi的码流。该系统只用了一套固定格式的mencoder转码。指定源视频转码工具为mencoderPath和转码后的文件codcAviPath,设置音频编码为mp3,为了减少cpu的占用率,视频编码格式采用xvid,同时保持音视频同步要加上“-lameopts preset=64”。
public String processAVI(String mencoderPath, String upFilePath, String codcAviPath) {
List ransCode = new ArrayList();
try {
Process videoProcess = new ProcessBuilder(ransCode).redirectErrorStream(true).start();
new PrintStream(videoProcess.getInputStream()).start();
videoProcess.waitFor();
return codcAviPath;
} catch (Exception e) {
ransCode.add(mencoderPath);
ransCode.add(upFilePath);
ransCode.add("-oac");
ransCode.add(setparameter.getMoac());
ransCode.add("-lameopts");
ransCode.add(“preset=64”);
ransCode.add("-mc");
ransCode.add(“0”);
ransCode.add("-lavcopts"); ransCode.add(“acodec=”+setparameter.getMacodec()+":abitrate="+setparameter.getMabitrate());
ransCode.add("-ovc");
ransCode.add(setparameter.getMovc());
ransCode.add("-"+setparameter.getMmencopts());
ransCode.add(“bitrate=”+setparameter.getMbitrate());
ransCode.add("-of");
ransCode.add(setparameter.getMtype());
ransCode.add("-o");
ransCode.add(codcAviPath);
e.printStackTrace();
return null;
}
}
其他格式的视频使用ffmpeg工具转为mp4格式。Ffmpeg的常用编码器中,本身是不支持h264视频编码器,但是这种编码方式是比较大众化的,所以使用ffmpeg编译的过程中加入了libx264库来支持[9]。因为-qscale无法动态调整帧率,但又要保证视频的质量,所以换用-crf优化视频,其实就是通过增加编码帧的多少和依赖。如果视频的质量要求不是特别高的话,-crf值越小质量越好,值越大视频会出现模糊,但是视频大小会很节约资源。各参数的值也会通过后台管理中的参数设置进行实际调整。
public void processMP(String ffmpegPath, String upFilePath, String codcFilePath) {
List ransCode= new ArrayList();
ransCode.add(ffmpegPath);
ransCode.add("-i");
ransCode.add(upFilePath);
ransCode.add("-crf");
ransCode.add(setparameter.getFqscale());
ransCode.add("-ab");
ransCode.add(setparameter.getFab());
ransCode.add("-acodec");
ransCode.add(setparameter.getFacodec());
ransCode.add("-ac");
ransCode.add(setparameter.getFac());
ransCode.add("-ar");
ransCode.add(setparameter.getFar());
ransCode.add("-r");
ransCode.add(setparameter.getFr());
ransCode.add("-c:v");
ransCode.add(setparameter.getFcv());
ransCode.add("-strict");
ransCode.add("-2");
try {
Process videoProcess = new ProcessBuilder(ransCode).redirectErrorStream(true).start();
new PrintStream(videoProcess.getInputStream()).start();
videoProcess.waitFor();
} catch (IOException eo) {
eo.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
下图展示了视频转码之前所属的视频参数和格式,和转码会后的视频格式。
当视频转码成功后,再去获取缩略图。
public boolean processImg(String filename) {
List ransCode= new ArrayList();
ransCode.add(ffmpegpath);
ransCode.add("-ss");
ransCode.add(“00:00:10”);
ransCode.add("-i");
ransCode.add(sourceVideoPath);
ransCode.add("-f");
ransCode.add(“image2”);
ransCode.add("-y");
ransCode.add(imageRealPath + filename + “.jpg”);
try {
Process videoProcess = new ProcessBuilder(ransCode).redirectErrorStream(true).start();
new PrintStream(videoProcess.getInputStream()).start();
videoProcess.waitFor();
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}