2.问题:浏览器播放视频卡顿。视频卡顿问题需要攻克 2 关。
3.解决过程:
1 )第一关:视频文件太大,浏览器加载时间长。采用后端对文件分块读取。
场景:编写完成文件的上传与下载接口,在浏览器使用组件绑定 url 进行观看视频。按照以上步骤,浏览器是能正常播放视频的,但是我把项目发布到服务器后,就出现另一个情况了。我发现浏览器会一直加载视频,浏览器中心呈现转圈动画,进度条会一点一点增长,但是没有画面出现。直到进度条加载完,才会出现画面。
原因:后端的下载接口是把整个视频文件一下子发送到浏览器,所以浏览器一直在接收文件,接收完文件后,浏览器的 video 组件才能进行播放。
解决:后端接口使用 randomAccessFile 类读取文件,这个类取到 file 的信息后,你便可以设置从文件的哪个位置开始读取,读取多少字节,然后把数据响应到浏览器。
这样就解决了浏览器一直加载视频的问题。例子代码在末尾。
2 )第二关:服务器带宽太低,视频下载赶不上视频播放,造成视频播放卡顿。采用 ffmpeg 组件进行画质压缩。
场景:假如有一个 30 秒 90M 的视频(我手机录的),上传到服务器了,然后在浏览器进行播放,那么播放视频就会卡顿了。
原因:因为服务器的带宽是 1M/s ,每秒能传送 1M 文件到浏览器,但是浏览器要想流畅播放那个视频,浏览器需要每秒接收 3M 文件,90M 的视频,30 秒,每秒需要播放 3M ,所以这就造成视频播放卡顿了。
解决:使用 ffmepg 组件,把 90M 的视频压缩到 30M ,可以压缩视频的比率和分辨率,最好进行相同比例的压缩,不然画质会变糊。
代码:
1 )第一关:
/**
* 斷點播放
* @param request
* @param response
* @throws IOException
*/
public void play(HttpServletRequest request, HttpServletResponse response) throws IOException{
response.reset();
File file = new File("本地的一個視頻地址");
long fileLength = file.length();
// 随机读文件
RandomAccessFile randomAccessFile = new RandomAccessFile(file, "r");
//获取从那个字节开始读取文件
String rangeString = request.getHeader("Range");
long range=0;
if (StrUtil.isNotBlank(rangeString)) {
range = Long.valueOf(rangeString.substring(rangeString.indexOf("=") + 1, rangeString.indexOf("-")));
}
//获取响应的输出流
OutputStream outputStream = response.getOutputStream();
//设置内容类型
response.setHeader("Content-Type", "video/mp4");
//返回码需要为 206 ,代表只处理了部分请求,响应了部分数据
response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
// 移动访问指针到指定位置
randomAccessFile.seek(range);
// 每次请求只返回 1MB 的视频流
byte[] bytes = new byte[1024 * 1024];
int len = randomAccessFile.read(bytes);
//设置此次相应返回的数据长度
response.setContentLength(len);
//设置此次相应返回的数据范围
response.setHeader("Content-Range", "bytes "+range+"-"+(fileLength-1)+"/"+fileLength);
// 将这 1MB 的视频流响应给客户端
outputStream.write(bytes, 0, len);
outputStream.close();
randomAccessFile.close();
// System.out.println("返回数据区间: ["+range+"-"+(range+len)+"] ");
}
2 )第二关:
ffmepg 依赖
org.bytedeco
javacv-platform
1.5.3
代码:
/**
/
public static String modifyResolution(
String imagePath, String outputDir, Integer width, Integer height, Integer bitRate)
throws Exception {
if (bitRate == null) {
bitRate = 2000;
}
List paths = Splitter.on(".").splitToList(imagePath);
String ext = paths.get(paths.size() - 1);
if (!Arrays.asList("mp4").contains(ext)) {
throw new Exception("format error");
}
String resultPath =
Joiner.on(File.separator).join(Arrays.asList(outputDir, IdUtil.simpleUUID() + "." + ext));
String ffmpeg = Loader.load(org.bytedeco.ffmpeg.ffmpeg.class);
ProcessBuilder builder =
new ProcessBuilder(
ffmpeg,
"-i",
imagePath,
"-s",
MessageFormat.format("{0}{1}", String.valueOf(width), String.valueOf(height)),
"-b",
MessageFormat.format("{0}k", String.valueOf(bitRate)),
resultPath);
builder.inheritIO().start().waitFor();
return resultPath;
}