1.首先,介绍场景:视频播放平台,系统使用 Springboot 和 Vue 编写。拥有前台和后台,前台进行视频播放,后台进行视频的上传,也可以上传音乐和图片,功能顺手都做了。
2.问题:浏览器播放视频卡顿。视频卡顿问题需要攻克 2 关。
3.解决过程:
1 )第一关:视频文件太大,浏览器加载时间长。采用后端对文件分块读取。
场景:编写完成文件的上传与下载接口,在浏览器使用

  • 斷點播放
  • request
  • response
  • 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

代码:
/**

修改视频分辨率

@param imagePath 原视频地址
@param outputDir 输出目录
@param width 宽度
@param height 高度
@param bitRate 码率
@return 视频地址
@throws Exception 异常
/
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;
}

视频切片。m3u8 ,然后你就不需要做其他任何处理了。

#1是的,楼主这个需求可以转画质后通过 m3u8 实现。。。

判断一下视频时长,1 分钟以内的 MP4 直链,中长视频直接无脑切片就行。

要不试试用 cdn 顶上?感觉是服务器带宽不够

如果使用 video 标签绑定 url 播放可以试试 videojs 这个库,但是这个有点问题就是视频不能过大 谷歌浏览器好像是不能超过 150MB

直接 emby ,plex

前端流式播放视频的技术 感觉还不太成熟,还没有统一,PC 端与安卓端现在大部分都是才用的不同技术,安卓那边一般是用 m3u8 ,PC 这边一般是 flv

b 站是转成 dash ,腾讯视频是 hls不切片 1M 的小水管估计够呛,因为比如拖动进度条到第 60 秒,但上个关键帧是在第 30 秒,结果就是浏览器要从 30 秒的地方开始下载,这 30 秒的视频大小可能超过 10M ,这样就导致要等待 10 秒的时间才能开始播放第 60 秒的视频

cdn 加速应该可以 但是需要付费哦,商业项目可以加

如果只上传项目定位是只播放 150M 以下大小的视频 有机会试试

知识盲区了 我去学习一下

我瞅瞅

知识盲区了 俺学习下

原来简简单单的文件下载 涉及到视频文件时便成了流式传输领域

醍醐灌顶

硬盘寄过去不好吗?

你和我当时犯的错误一样,就是把视频通过后端处理然后返回给前端,这个对服务的影响其实蛮大的。楼上的几个老哥也说复杂了,实际上视频无论是 mp4 还是 flv ,套一层 nginx ,把静态链接丢给前端就可以流式播放了,不需要手动处理。另外就是,视频这种的大静态文件最好不要直接丢在服务器上

直接对象存储 mp4 默认支持流式传输

文件太大,不应该要压缩一下吗?光切片没啥用

需要考虑一下项目的定位,小项目小视频就放自己服务器。大项目可以采用 oss 提高用户体验。

需要压缩

不止视频, 音频 图像 等都可以这样做,流只是个思想

yuki.elsie.cc/video_stream/

这个是用 MSE 制作的

用 hls 切片

视频处理成 m3u8 ,在找个播放器

第一关可以 controller 返回 FileSystemResource 就可支持"Range"了上传后队列里调用 ffmpeg 切片生成 m3u8..后续就用 m3u8 了

用 flv.js 也行的。我觉着比 video.js 好用。转码肯定是要转的。可以直接调用 shell切片也肯定是要切的。也可以调用 shell ,好像能转码切片一起干了。

HLS ( m3u8+ts )的方案可以考虑下

ts, flv 压缩率太低,建议采用 DASH 或 HLS(m3u8 + fmp4) 来播放视频分片。m3u8 采用多级 m3u8 索引, 将视频转成多个不同分辨率的片段,每个子索引 m3u8 对应相应的分辨率片段,保证视频的播放平滑。 服务器带宽要高点。使用 mp4 将视频压缩率提高, 并选用合适的分辨率。 移动 moov box 到 MP4 文件头部,播放器获取到 moov box 才能开始播放视频。视频传输采用 http 渐进式下载,使用云服务的对象存储保存这些视频

我记得 v2 里有个老哥发了个开源前端播放器,挺炫酷的,可能可以解决 lz 的问题

放过自己,搭建个 emby 私服吧

楼上关于切片的都很对。但是这不是卡顿的原因,最大的原因是,视频码率大于服务器出口带宽。码率和带宽正好都是用 bit/s 为单位,所以,你要流畅播放,“理论上”视频码率就不能大于出口带宽,实际上码率还得降 20%。比如服务器是 10mb/s 的,“理论上”视频码率=10mb/s 是可以流畅播放的,实际上就看网络稳定性了。尽量扩展带宽,在此基础上,尽量降低视频码率,继续在这个基础上,提供多码率供前端选择(这要转多个码率的视频,耗转码时间)。至于不同分辨率用什么码率,可以去 B 站参考( youtu 的参考意义不大,因为他码率给得足)

mp4 元数据前移一下。弄个 jellyfin 不香吗?

花点 cdn 的流量费绝对比你这 1M 小水管体验好,如果你只是为了学习如何在有限条件下把事做好又不怕女友那另当别论

-movflags +faststart

#34 > 弄个 jellyfin 不香吗? 看下来同感,别自己造轮子了。如果你女朋友又不是懂技术的人,你自己写出来的东西她用来用去都不顺手,那就不要再当沸羊羊了。喜羊羊都是滚个开源的 jellyfin 轮子再带个 tailscale/zeroconf 或者是 openvpn ,再不行 nginx 反代 https 出去得了。美羊羊用的舒心最重要。如果 jellyfin 没上传功能(我也不太清楚,没用过),你就自己用 go/java 写个 upload api 再 jellyfin 监听文件夹。

可以试试 Cloudflare Stream

给她充个会员吧

将视频的元数据帧放到第一帧就能在未加载完成前拖动。ffmpeg 可以使用 -movflags +faststart ,或者 Handbrake 里面勾上[网页优化]。转换成 m3u8 + 切片的方式也行。

这怎么说呢,可能就是一片心意,但是实际点说,写得再好,体验也比成熟产品差。。。也许到最后就是花了一大堆时间写了一个女朋友只用了几分钟的东西。。。反正个人觉得,时间成本也是成本。。。

好好好,这么玩是吧