文章目录

  • 学习链接
  • hls.js播放m3u8视频
    • 效果
    • 代码
      • 前端代码
        • 安装hls.js
        • App.vue
      • 后台代码
        • 准备文件
          • mp4文件切片java实现
        • TsController
        • TsService
  • DPlayer播放m3u8视频
    • 效果
      • 扩展内容:MSE(Media Source Extensions)介绍
        • 1 概述
        • 2 why MSE
    • 代码
      • 安装依赖
      • App.vue

学习链接

vue3项目实战 仿百度网盘

hls的github文档中的api详细说明
hlsjs-dev官方的demo演示
hls播放m3u8 添加header请求头,在请求ts的url上添加参数

Vue 之 视频流 - Hls.js

hls.js如何播放m3u8文件(实例)

网页前端video播放m3u8(HLS)
ffmpeg 视频ts切片生成m3u8

mp4视频分片生成m3u8流文件并加密,
Java使用FFmpeg(自定义cmd)系列之官方API获取视频/音频信息(URL方式)
Java使用FFmpeg(自定义cmd)系列之MP4 转码 HLS m3u8 AES128 加密(源码已保存在gitee上)
这个加密后面可以试一下,因为分片完,前端随便拿一个分片,这个分片单独保存下来使用vlc播放器也是可以播放的
Spring boot视频播放(解决MP4大文件无法播放),整合ffmpeg,用m3u8切片播放。(包含加密方式)

Java使用ffmpeg进行视频格式转换、音视频合并、播放、截图


DPlayer官网文档
vue使用dplayer 播放m3u8格式的视频——播放m3u8格式视频(三)
vue使用vue-dplayer播放m3u8格式的视频——播放m3u8格式视频(二)
vue使用原生videojs 播放m3u8格式的视频——播放m3u8格式视频(一)

MSE(Media Source Extensions)介绍

hls.js播放m3u8视频

HLS (HTTP Live Streaming)是Apple公司研发的流媒体传输技术,包括一个m3u8的索引文件、多个ts分片文件和key加密串文件。这项技术主要应用于点播和直播领域。

效果

  • 发送的请求有2个相同的是因为第一个是预检请求
  • 请求中可以携带自定义请求头,因此在后台可以校验这个请求头,从而验证用户,才允许播放,但不能防止用户获取视频数据合并下载
  • api可以参考hls的github上的地址

代码

前端代码

安装hls.js
npm install --save hls.js
App.vue
<template><div style="text-align: center;"><!-- 仅一个video元素 --><video id="video" ref="videoRef" width="800" height="400" controls></video><p><!-- 点击触发播放视频 --><button @click="playVideo">播放视频</button> &nbsp;<button @click="pauseVideo">暂停视频</button></p></div>
</template><script setup>
import { ref, reactive, onMounted } from 'vue'
import Hls from 'hls.js';const videoRef = ref()var videoSrc = 'http://127.0.0.1:8085/ts/video/playM3U8video/YsIlFSjnlh'; // 用于获取m3u8文件的地址, 后端处理跨域function playVideo() {videoRef.value.play()
}function pauseVideo() {videoRef.value.pause()
}function loadVideo() {if (Hls.isSupported()) {// 组件挂载完成, 才能获取到video元素var video = document.getElementById('video');// 可参考: // hls的github文档中的api:(https://github.com/video-dev/hls.js/blob/master/docs/API.md)// hls.js如何播放m3u8文件(实例)?(https://blog.csdn.net/ffffffff8/article/details/129314268)// Vue 之 视频流 - Hls.js(https://blog.csdn.net/a15297701931/article/details/115478652)let config = {xhrSetup: function (xhr, url) {xhr.withCredentials = true; // 会携带cookiexhr.setRequestHeader('token',"my-token")},}var hls = new Hls(config);hls.on(Hls.Events.MEDIA_ATTACHED, function () {console.log('video and hls.js are now bound together !');});hls.on(Hls.Events.MANIFEST_PARSED, function (event, data) {console.log('manifest loaded, found ' + data.levels.length + ' quality level');// video.play() // 不能在这里就播放, 需要用于与dom元素有交互才可以播放, 否则浏览器会报错});hls.loadSource(videoSrc);// bind them togetherhls.attachMedia(video);}
}onMounted(() => {loadVideo()
})</script><style scoped>
p {margin: 0;
}
</style>

后台代码

准备文件


其中index.m3u8文件

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-MEDIA-SEQUENCE:0
#EXT-X-ALLOW-CACHE:YES
#EXT-X-TARGETDURATION:30
#EXTINF:30.000000,
YsIlFSjnlh_0000.ts
#EXTINF:30.000000,
YsIlFSjnlh_0001.ts
#EXTINF:30.000000,
YsIlFSjnlh_0002.ts
#EXTINF:30.000000,
YsIlFSjnlh_0003.ts
#EXTINF:30.000000,
YsIlFSjnlh_0004.ts
#EXTINF:30.000000,
YsIlFSjnlh_0005.ts
#EXTINF:30.000000,
YsIlFSjnlh_0006.ts
#EXTINF:30.000000,
YsIlFSjnlh_0007.ts
#EXTINF:30.000000,
YsIlFSjnlh_0008.ts
#EXTINF:30.000000,
YsIlFSjnlh_0009.ts
#EXTINF:30.000000,
YsIlFSjnlh_0010.ts
#EXTINF:30.000000,
YsIlFSjnlh_0011.ts
#EXTINF:30.000000,
YsIlFSjnlh_0012.ts
#EXTINF:30.000000,
YsIlFSjnlh_0013.ts
#EXTINF:30.000000,
YsIlFSjnlh_0014.ts
#EXTINF:30.000000,
YsIlFSjnlh_0015.ts
#EXTINF:30.000000,
YsIlFSjnlh_0016.ts
#EXTINF:30.000000,
YsIlFSjnlh_0017.ts
#EXTINF:5.400000,
YsIlFSjnlh_0018.ts
#EXT-X-ENDLIST
mp4文件切片java实现

摘自 B站程序员老罗网盘项目代码
cutFile4Video

private void cutFile4Video(String fileId, String videoFilePath) {// videoFilePath 如: D:/document/easypan/easypan-java/file//202305/{userId}{fileId}.mp4//创建同名切片目录,// tsFolder 与 视频文件在同一目录下,并且tsFolder是以视频真实文件名(不带后缀)作为文件夹名// tsFolder 如: D:/document/easypan/easypan-java/file//202305/{userId}{fileId} - 这是个文件夹File tsFolder = new File(videoFilePath.substring(0, videoFilePath.lastIndexOf(".")));if (!tsFolder.exists()) {tsFolder.mkdirs();}// 这里也尝试过直接对mp4文件直接转为.m3u8 + ts分段视频文件,但转的速度比较慢,不知道是不是参数设置的原因,ffmpeg不是很清楚它的命令// 命令如:ffmpeg -i ./jvm.mp4 -c:v h264 -flags +cgop -g 30 -hls_time 60 -hls_list_size 0 -hls_segment_filename index%3d.ts index.m3u8// 这个命令,会将执行命令时所在的当前目录下的jvm.mp4文件按照视频的每60s切割成一个ts文件 和 .m3u8索引文件 到 当前目录// 这里先转为ts文件,然后,再切割这个ts文件,生成.m3u8索引文件(速度比上面快)// 1. 将 整个视频文件 转成ts文件:index.ts//    ffmpeg -y -i {mp4视频文件路径}  -vcodec copy -acodec copy -vbsf h264_mp4toannexb {放入到哪一个文件位置}//    如:ffmpeg -y -i D:/test/jvm.mp4  -vcodec copy -acodec copy -vbsf h264_mp4toannexb D:/test/jvm/index.ts//    这个命令会将 第一个 所指向的mp4视频文件 转成 ts文件 存储到 D:/test/jvm/index.ts,注意第二个文件路径在执行命令前必须要存在// 2. 将index.ts文件进行分片//    ffmpeg -i {index文件的文件路径} -c copy -map 0 -f segment -segment_list {要生成的m3u8索引文件路径} -segment_time 30 {生成的ts切片路径}/{文件名前面部分}_%4d.ts//    生成的ts文件路径%%4d,写了2个百分号是为了防止jdk的MessageFormat处理//    如:ffmpeg -i D:/test/jvm/index.ts -c copy -map 0 -f segment -segment_list D:/test/jvm/index.m3u8 -segment_time 30 D:/test/jvm/jjvvmm_%4d.ts//    这个 命令会将 第一个 所指向的ts视频文件 按照 每30s 切割成一个小的ts视频文件,放入到指定的文件夹中,并且有指定格式的文件名(占4位,递增),并且会生成一个m3u8的索引文件// mp4转ts文件的ffmpeg命令// 如:ffmpeg -y -i D:/document/easypan/easypan-java/file//202305/{userId}{fileId}.mp4  -vcodec copy -acodec copy -vbsf h264_mp4toannexb D:/document/easypan/easypan-java/file//202305/{userId}{fileId}/index.tsfinal String CMD_TRANSFER_2TS = "ffmpeg -y -i %s  -vcodec copy -acodec copy -vbsf h264_mp4toannexb %s";// ts文件拆分成小的ts文件 和 生成一个.m3u8文件// 如:ffmpeg -i D:/document/easypan/easypan-java/file//202305/{userId}{fileId}/index.ts -c copy -map 0 -f segment -segment_list D:/document/easypan/easypan-java/file//202305/{userId}{fileId}/index.m3u8 -segment_time 30 D:/document/easypan/easypan-java/file//202305/{userId}{fileId}/{fileId}_%%4d.tsfinal String CMD_CUT_TS = "ffmpeg -i %s -c copy -map 0 -f segment -segment_list %s -segment_time 30 %s/%s_%%4d.ts";// 转成的ts文件所在路径:D:/document/easypan/easypan-java/file//202305/{userId}{fileId}/index.tsString tsPath = tsFolder + "/" + Constants.TS_NAME;String cmd = String.format(CMD_TRANSFER_2TS, videoFilePath, tsPath);ProcessUtils.executeCommand(cmd, false);//生成索引文件.m3u8 和切片.tscmd = String.format(CMD_CUT_TS, tsPath, tsFolder.getPath() + "/" + Constants.M3U8_NAME, tsFolder.getPath(), fileId);ProcessUtils.executeCommand(cmd, false);//删除index.tsnew File(tsPath).delete();
}

ProcessUtils 工具类

package com.easypan.utils;import com.easypan.exception.BusinessException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;public class ProcessUtils {private static final Logger logger = LoggerFactory.getLogger(ProcessUtils.class);public static String executeCommand(String cmd, Boolean outprintLog) throws BusinessException {if (StringTools.isEmpty(cmd)) {logger.error("--- 指令执行失败,因为要执行的FFmpeg指令为空! ---");return null;}Runtime runtime = Runtime.getRuntime();Process process = null;try {process = Runtime.getRuntime().exec(cmd);// 执行ffmpeg指令// 取出输出流和错误流的信息// 注意:必须要取出ffmpeg在执行命令过程中产生的输出信息,如果不取的话当输出流信息填满jvm存储输出留信息的缓冲区时,线程就回阻塞住PrintStream errorStream = new PrintStream(process.getErrorStream());PrintStream inputStream = new PrintStream(process.getInputStream());errorStream.start();inputStream.start();// 等待ffmpeg命令执行完process.waitFor();// 获取执行结果字符串String result = errorStream.stringBuffer.append(inputStream.stringBuffer + "\n").toString();// 输出执行的命令信息if (outprintLog) {logger.info("执行命令:{},已执行完毕,执行结果:{}", cmd, result);} else {logger.info("执行命令:{},已执行完毕", cmd);}return result;} catch (Exception e) {// logger.error("执行命令失败:{} ", e.getMessage());e.printStackTrace();throw new BusinessException("视频转换失败");} finally {if (null != process) {ProcessKiller ffmpegKiller = new ProcessKiller(process);runtime.addShutdownHook(ffmpegKiller);}}}/*** 在程序退出前结束已有的FFmpeg进程*/private static class ProcessKiller extends Thread {private Process process;public ProcessKiller(Process process) {this.process = process;}@Overridepublic void run() {this.process.destroy();}}/*** 用于取出ffmpeg线程执行过程中产生的各种输出和错误流的信息*/static class PrintStream extends Thread {InputStream inputStream = null;BufferedReader bufferedReader = null;StringBuffer stringBuffer = new StringBuffer();public PrintStream(InputStream inputStream) {this.inputStream = inputStream;}@Overridepublic void run() {try {if (null == inputStream) {return;}bufferedReader = new BufferedReader(new InputStreamReader(inputStream));String line = null;while ((line = bufferedReader.readLine()) != null) {stringBuffer.append(line);}} catch (Exception e) {logger.error("读取输入流出错了!错误信息:" + e.getMessage());} finally {try {if (null != bufferedReader) {bufferedReader.close();}if (null != inputStream) {inputStream.close();}} catch (IOException e) {logger.error("调用PrintStream读取输出流后,关闭流时出错!");}}}}
}
TsController
@RestController
@RequestMapping("ts/video")
public class TsController {@Autowiredprivate TsService tsService;@GetMapping("playM3U8video/{fileId}")public void playM3U8video(@PathVariable("fileId") String fileId) {tsService.playM3U8video(fileId);}
}
TsService
@Slf4j
@Service
public class TsService {private static final String VIDEO_ROOT_PATH = "d:\\test";@Autowired@SuppressWarnings("all")private HttpServletResponse response;// 可参考:ffmpeg 视频ts切片生成m3u8// 网页前端video播放m3u8(HLS) https://www.cnblogs.com/fieldtianye/p/13166957.html// https://www.cnblogs.com/fieldtianye/category/1789297.htmlpublic void playM3U8video(String fileId) {FileSystemResourceLoader fileResourceLoader = new FileSystemResourceLoader();// 先获取m3u8文件if (!fileId.endsWith("ts")) {Resource resource = fileResourceLoader.getResource(VIDEO_ROOT_PATH + File.separator + fileId + "/index.m3u8");try {StreamUtils.copy(resource.getInputStream(), response.getOutputStream());} catch (IOException e) {log.error("写出m3u8文件流失败:{}",e);}} else {// 请求以ts结尾,则获取ts分段文件Resource resource = fileResourceLoader.getResource(VIDEO_ROOT_PATH + File.separator + fileId.split("_")[0] + File.separator +fileId);try {StreamUtils.copy(resource.getInputStream(), response.getOutputStream());} catch (IOException e) {log.error("写出m3u8文件流失败:{}",e);}}}
}

DPlayer播放m3u8视频

效果

  • 效果与直接使用hls效果一致,但是优点是它还支持其它的MSE(Media Source Extensions)

  • 携带了自定义的请求头

扩展内容:MSE(Media Source Extensions)介绍

摘自:MSE(Media Source Extensions)介绍

本文介绍 MSE(Media Source Extensions) 的相关知识。

1 概述

MSE(Media Source Extensions),即媒体源扩展,可以理解为一种API,其提供了实现无插件(js插件外的插件)且基于 Web 的流媒体的功能。通过 MSE,媒体串流能够通过 JavaScript 创建,并且可以使用 HTML5 的 <audio><video> 标签进行播放。

2 why MSE

以前,用户在通过Web浏览器浏览网页内容,尤其是视频内容时,需要使用诸如 Adobe Flash或是微软的 Silverlight 等类似的插件,才能播放视音频内容,这些插件对于Web浏览器来说,扮演着媒体播放器角色。但是,在浏览器中使用插件是不便捷并且不安全的(不法分子会在插件上动手脚),因此,最新的HTML5标准中,定义了一系列新的元素来避免使用插件,其中就包含了大名鼎鼎的 <video> 标签

有了<video>标签,支持 HTML5 的Web浏览器就能够实现无需插件播放媒体内容了,但是 HTML5 的 <video> 标签对媒体的格式有所限制(在 Windows 电脑上只支持 mp4、webm、ogg 等格式)。这里简单介绍一下媒体格式,媒体的格式分为“编码格式”和“封装格式”,原视频数据通过编码来压缩视频数据大小,这里用到的格式为编码格式(如常见的H.264编码),再通过封装将压缩后的视音频、字幕组合到一个容器中,这里用到的格式为封装格式(如常见的mp4格式)

我们可以把 <video> 标签看做是浏览器自带的、具有解封和解码功能的视频播放器。但是,随着视频点播、直播等视频业务的兴起,视频数据会通过流媒体传输协议(目前常用的 MPEG-DASH 和 Apple 的 HLS)从服务端分发给客户端,在这种情况下,媒体内容就被包含在传输协议中了,此时 HTML5 的 <video> 标签就无法识别并播放媒体内容了。以 HLS 协议为例。HLS 将视频源文件的内容分散地封装到一个个TS文件中,此时仅靠 <video> 标签是无法识别这些TS文件的,所以,我们就需要引入了 MSE 来帮助Web浏览器识别并处理TS文件,将其变回Web浏览器可识别的媒体容器格式(mp4格式),经过 MSE 的处理,<video> 标签就可以识别并播放视频源文件了。例如,hls.js 中就利用 MSE 将视频分片内容进行合流,并组成 HTML5 的 <video> 标签可播放的媒体资源文件。再比如,flv.js 通过 MSE 播放 HTTP-FLV 协议的视频直播流。简单总结一下,引入 MSE 之后,支持 HTML5 的Web浏览器就变成了能够解析流协议的播放器了

从另外一个角度来说,通过引入 MSE,HTML5 的<video> 标签不仅可以直接播放其默认支持的 mp4、m3u8、webm、ogg 等格式,还可以支持能够被 (具备MSE功能的)JS 处理的视频流格式。如此一来,我们就可以通过 (具备MSE功能的)JS,把一些 标签原本不支持的视频流格式,转化为其支持的格式(如 H.264 的 mp4)。B站开源的 flv.js 就是此技术的一个典型应用场景,B站的HTML5播放器,通过使用 MSE 技术,将FLV源用 JS(flv.js) 实时转码成 HTML5 支持的视频流编码格式,提供给 HTML5 播放器播放。

代码

后端代码不变,修改前端代码即可。

安装依赖

npm i dplayer -S // 视频播放器插件
npm i hls.js -S  // 播放hls流插件

App.vue

<template><!-- 只写宽度,高度会自适应 --><div id="dplayer" style="width: 400px;"></div><p><!-- 点击触发播放视频 --><button @click="playVideo">播放视频</button> &nbsp;<button @click="pauseVideo">暂停视频</button></p>
</template><script setup>import { ref, reactive, onMounted } from 'vue'import Hls from 'hls.js';
import DPlayer from 'dplayer';console.log(DPlayer);let dpInstance = nullfunction playVideo() {console.log(dpInstance.play);dpInstance.play()
}function pauseVideo() {dpInstance.pause()
}onMounted(() => {// 另一种方式,使用 customTypeconst dp = new DPlayer({container: document.getElementById('dplayer'),video: {url: 'http://127.0.0.1:8085/ts/video/playM3U8video/YsIlFSjnlh',type: 'customHls',customType: {customHls: function (video, player) {let config = {xhrSetup: function (xhr, url) {xhr.withCredentials = true; // 会携带cookiexhr.setRequestHeader('token', "my-token")},}const hls = new Hls(config);hls.loadSource(video.src);hls.attachMedia(video);},},},});window.dp = dpdpInstance = dp})</script><style lang="scss"></style>

Hls.js播放m3u8视频 DPlayer视频播放器(easypan) MSE简介相关推荐

  1. m3u8.php怎么调用,怎样使用Vue结合Video.js播放m3u8视频

    这次给大家带来怎样使用Vue结合Video.js播放m3u8视频,使用Vue结合Video.js播放m3u8视频的注意事项有哪些,下面就是实战案例,一起来看一下. 首先,我们需要在vue工程中安装vi ...

  2. video.js播放m3u8视频

    m3u8 是一种基于HTTP Live Streaming(HLS)文件视频格式,它主要是存放整个视频的基本信息和分片(Segment)组成.目前 由 Apple.inc 率先提出的 HLS 协议在 ...

  3. 使用hls.js播放m3u8视频流

    1.安装hls.js npm install hls.js -S 2.使用 <template><div class="video_con"><vid ...

  4. 360极速浏览器不能添加hls插件,怎样播放m3u8视频?

    谷歌和火狐浏览器都可以在扩展程序添加Native HLS Playback插件播放m3u8视频,但是360搜了一下没有类似的插件. 那怎么办呢,有个好办法就是大家可以简单粗暴直接换成Chrome浏览器 ...

  5. video.js兼容微信浏览器,(播放.m3u8视频源)解决微信浏览器黑屏问题

    video.js兼容微信浏览器,(播放.m3u8视频源)解决微信浏览器黑屏问题 一.问题描述: 在h5做根据视频源直播功能时,在浏览器能打开视频,但是在部分安卓机和苹果机在微信浏览器打开会出现黑屏的状 ...

  6. 怎么播放m3u8视频

    为了播放 m3u8 视频,你需要有一个支持 m3u8 格式的视频播放器.常用的视频播放器有 VLC.Potplayer 等. 你需要在这些视频播放器中打开 m3u8 文件,即可播放视频. 你也可以使用 ...

  7. Web端直接播放 .ts 视频及mux.js播放ts视频没有声音

    最近项目中需要前端播放 .ts 格式视频,捣鼓了几天学习到很多知识,也发掘了一种优秀的解决方案,分享给有同样需求的同学. 常见方案 在网上查找的大部分解决方案都是用诸如videojs等网页播放器,接收 ...

  8. vue中DPlayer视频播放器使用方法

    vue中DPlayer视频播放器使用方法 1通过npm下载 npm install dplayer - s 2在需要使用的组件中导入 import Dplayer from 'Dplayer' 3页面 ...

  9. android 3.0 m3u8,在Android中播放m3u8视频

    我想直播视频,并且它采用m3u8格式.所以,我想下面的代码在Android中播放m3u8视频 public class StreamingPlayer extends Activity impleme ...

最新文章

  1. jmeter启动警告项解决方案
  2. 沉浸式小说App获得200万美元投资
  3. java calendar与date_java---Calendar与Date
  4. 记录下openstack部署和使用时遇到的一些问题
  5. Jquery Table 的基本操作
  6. 异步执行和多线程编程的关系
  7. slider获取点击 unity_Unity基础 | 70分钟带你轻松入门
  8. 这就是八成女性不爱用刷脸支付的原因!支付宝:一周内给安排上
  9. Java Design Pattern(Factory,Singleton,Prototype,Proxy)
  10. 用友套打的打印机型号
  11. Android 如何反编译apk查看源码
  12. a 标签 jq js 打开新页面跳转
  13. win10计算机方差,标准差计算工具
  14. andriod 连接数据库(MySQL)
  15. PS制作心跳二维码动画 学会后能增加粉丝关注率哦
  16. 戴尔390计算机电源问题,DELL笔记本电脑电源适配器无电压输出故障
  17. 强制在线带修区间LCM(线段树+质因子状压)
  18. Adobe25年软件精华CS3中文版发布.
  19. java医院挂号代码_基于SSM开发的Java医院预约挂号系统 源码下载
  20. 【H.264/AVC视频编解码技术详解】十七:帧内预测编码的预测实现方法

热门文章

  1. Lazada精细化店铺运营数据分析工具用什么?店梯erp告诉你
  2. 浅析领导力和执行力在企业管理中的运用
  3. [vue-demo]实时时间转换 v-time
  4. 单例模式多线程环境实现之几句呢喃
  5. 高中学历学Java能找到工作吗?
  6. bootstrap.min.css.map
  7. 小张学算法之音视频算法:2.jpeg图像压缩算法
  8. ECC内存和普通内存有什么不同?为什么对金融和医疗业务至关重要?
  9. VC(VISUAL_C++)虚拟键VK值列表
  10. 华为od机试真题 JS 实现【热点网站统计】