给女朋友做的视频播放平台,播放视频很卡,有几张方案解决

2023-09-21 18:06:56 +08:00
 KuAoaoaoao

1.首先,介绍场景:视频播放平台,系统使用 Springboot 和 Vue 编写。拥有前台和后台,前台进行视频播放,后台进行视频的上传,也可以上传音乐和图片,功能顺手都做了。

2.问题:浏览器播放视频卡顿。视频卡顿问题需要攻克 2 关。

3.解决过程: 1 )第一关:视频文件太大,浏览器加载时间长。采用后端对文件分块读取。 场景:编写完成文件的上传与下载接口,在浏览器使用<video>组件绑定 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 依赖 <dependency> <groupId>org.bytedeco</groupId> <artifactId>javacv-platform</artifactId> <version>1.5.3</version> </dependency> 代码: /**

4739 次点击
所在节点    Java
48 条回复
KuAoaoaoao
2023-09-21 20:40:18 +08:00
@herozzm 需要压缩
chaoschick
2023-09-21 22:12:38 +08:00
@KuAoaoaoao 不止视频, 音频 图像 等都可以这样做,流只是个思想
chaoschick
2023-09-21 22:13:40 +08:00
chaoschick
2023-09-21 22:14:44 +08:00
这个是用 MSE 制作的
liuguang
2023-09-21 22:19:52 +08:00
用 hls 切片
putyy
2023-09-21 22:28:37 +08:00
视频处理成 m3u8 ,在找个播放器
ikas
2023-09-21 22:29:21 +08:00
第一关可以 controller 返回 FileSystemResource 就可支持"Range"了

上传后队列里调用 ffmpeg 切片生成 m3u8..后续就用 m3u8 了
hefish
2023-09-21 22:33:14 +08:00
用 flv.js 也行的。我觉着比 video.js 好用。
转码肯定是要转的。可以直接调用 shell
切片也肯定是要切的。也可以调用 shell ,好像能转码切片一起干了。
MFWT
2023-09-21 22:39:58 +08:00
HLS ( m3u8+ts )的方案可以考虑下
mightybruce
2023-09-21 23:10:02 +08:00
ts, flv 压缩率太低,
建议采用 DASH 或 HLS(m3u8 + fmp4) 来播放视频分片。
m3u8 采用多级 m3u8 索引, 将视频转成多个不同分辨率的片段,每个子索引 m3u8 对应相应的分辨率片段,保证视频的播放平滑。
服务器带宽要高点。

使用 mp4 将视频压缩率提高, 并选用合适的分辨率。 移动 moov box 到 MP4 文件头部,播放器获取到 moov box 才能开始播放视频。视频传输采用 http 渐进式下载,

使用云服务的对象存储保存这些视频
winglight2016
2023-09-22 08:39:54 +08:00
我记得 v2 里有个老哥发了个开源前端播放器,挺炫酷的,可能可以解决 lz 的问题
cslive
2023-09-22 08:47:09 +08:00
放过自己,搭建个 emby 私服吧
jifengg
2023-09-22 08:53:54 +08:00
楼上关于切片的都很对。但是这不是卡顿的原因,最大的原因是,视频码率大于服务器出口带宽。
码率和带宽正好都是用 bit/s 为单位,所以,你要流畅播放,“理论上”视频码率就不能大于出口带宽,实际上码率还得降 20%。比如服务器是 10mb/s 的,“理论上”视频码率=10mb/s 是可以流畅播放的,实际上就看网络稳定性了。
尽量扩展带宽,在此基础上,尽量降低视频码率,继续在这个基础上,提供多码率供前端选择(这要转多个码率的视频,耗转码时间)。
至于不同分辨率用什么码率,可以去 B 站参考( youtu 的参考意义不大,因为他码率给得足)
EricInBj
2023-09-22 09:41:11 +08:00
mp4 元数据前移一下。

弄个 jellyfin 不香吗?
chf007
2023-09-22 10:17:53 +08:00
@KuAoaoaoao 花点 cdn 的流量费绝对比你这 1M 小水管体验好,如果你只是为了学习如何在有限条件下把事做好又不怕女友那另当别论
MrKrabs
2023-09-22 10:24:09 +08:00
-movflags +faststart
n1cogrv
2023-09-22 11:00:38 +08:00
@EricInBj #34 > 弄个 jellyfin 不香吗?
@KuAoaoaoao 看下来同感,别自己造轮子了。如果你女朋友又不是懂技术的人,你自己写出来的东西她用来用去都不顺手,那就不要再当沸羊羊了。
喜羊羊都是滚个开源的 jellyfin 轮子再带个 tailscale/zeroconf 或者是 openvpn ,再不行 nginx 反代 https 出去得了。美羊羊用的舒心最重要。
如果 jellyfin 没上传功能(我也不太清楚,没用过),你就自己用 go/java 写个 upload api 再 jellyfin 监听文件夹。
flexbug
2023-09-22 13:26:34 +08:00
可以试试 Cloudflare Stream
Granado
2023-09-22 13:45:46 +08:00
给她充个会员吧
chronos
2023-09-22 14:23:42 +08:00
将视频的元数据帧放到第一帧就能在未加载完成前拖动。ffmpeg 可以使用 -movflags +faststart ,或者 Handbrake 里面勾上[网页优化]。转换成 m3u8 + 切片的方式也行。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/975960

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX