视频播放实现示例Demo

news2025/1/15 19:49:28

学习链接

vue+springboot文件分片上传与边放边播实现

同步加载、播放视频的实现 ---- range blob mediaSource

通过调试技术,我理清了 b 站视频播放很快的原理

MSE (Media Source Extensions) 上手指南

浅聊音视频的媒体扩展(Media Source Extension)

今天又学到一种播放视频的方法,先把视频分块的都获取到,然后再使用URL.createObject(chunks)创建blobUrl,再把这个blobUrl给到video标签即可 播放视频(可以拖动视频进度条)。但是这种缺点也很明显,得把所有视频文件分块获取完成后,才能播放视频。体验上就不是很好。后面有时间可以看下MediaSource相关(能否实现边下载边播放,而不是非得等到全部下载完了再播放?)

在这里插入图片描述

VideoPlay.vue

<template>
  <div class="release_wrap">

    <el-card class="release_card">

      <el-table stripe :data="tableData" style="width: 100%" height="600px">
        <el-table-column prop="videoName" label="视频名称" min-width="280">
        </el-table-column>
        <el-table-column label="操作">
          <template slot-scope="scope">
            <el-button size="mini" type="primary" @click="playVideo(scope.$index, scope.row)">播放</el-button>
          </template>
        </el-table-column>
      </el-table>

    </el-card>

    <el-dialog :modal="false" title="视频播放" :visible.sync="dialogVisible" width="40%">
      <video :src="videoUrl" controls="controls" width="100%" @canplay="getVidDur()" id="myvideo"></video>
    </el-dialog>
    
  </div>
</template>

<script>

var video = () => {
  var videoTime = document.getElementById("myvideo");
  console.log(videoTime.duration); //获取视频时长
  console.log(videoTime.currentTime); //获取视频当前播放时间
};

export default {
  data() {
    return {
      title: "",
      videolist: "",
      //表格数据
      tableData: [],
      //弹框组件隐藏
      dialogVisible: false,
      //用于保存视频的id
      videoId: 0,
      //保存视频的名称
      videoName: '',
      videoUrl: '',
    };
  },

  created() {
    this.getVideoInfo();
  },

  methods: {
    jump_home() {
      this.$router.replace('/')
    },
    getVidDur() {
      video();
    },
    //获取video表格数据
    getVideoInfo() {
      this.$axios.get("http://127.0.0.1:9098/SelectVideo/table").then((res) => {
        this.tableData = res.data;
      });
    },
    
    // 点击播放按钮
    playVideo(i, val) {
    
      // 显示弹框
      this.dialogVisible = true;
      
      // 保存视频名字
      this.videoName = val.videoName;
      
      // 保存视频id
      this.videoId = val.id;
      
      // 发送HEAD请求获取视频的总大小
      this.$axios.get(`http://127.0.0.1:9098/SelectVideo/getVideoSizeById/${this.videoId}`).then(res => {
      
        const totalSize = res.data;
        const chunkSize = Math.ceil(totalSize / 20); // 设置分片大小为总大小的1/5

        // 定义分片传输的函数
        const loadVideoChunk = (startByte, endByte) => {
        
          return new Promise((resolve, reject) => {
          
            this.$axios.get(`http://127.0.0.1:9098/SelectVideo/policemen/${this.videoId}`, {
              headers: {
                Range: `bytes=${startByte}-${endByte}`
              },
              responseType: 'blob'
            }).then(response => {
              // 返回获取到的视频分片数据
              resolve(response.data);
            }).catch(error => {
              reject(error);
            });
          });
        };

        // 创建一个数组来保存所有分片的Promise
        const chunkPromises = [];

        // 获取所有分片的Promise
        for (let i = 0; i < 20; i++) {
          const startByte = i * chunkSize;
          const endByte = Math.min(startByte + chunkSize - 1, totalSize - 1);
          chunkPromises.push(loadVideoChunk(startByte, endByte));
        }

        // 执行所有分片请求,并在全部请求完成后开始播放视频
        Promise.all(chunkPromises).then(chunks => {
        
          // 将分片数据合并成完整的视频Blob
          const videoBlob = new Blob(chunks);
          
          const videoUrl = URL.createObjectURL(videoBlob);
          
          this.videoUrl = videoUrl;
          
        }).catch(error => {
          console.error('Failed to load video:', error);
        });

      }).catch(error => {
        console.error('Failed to get video size:', error);
      });

    },
  },
};
</script>

<style></style>

SelectVideoController

    //查询视频流的接口
    @GetMapping("/policemen/{videoId}")
    public void videoPreview(HttpServletRequest request, HttpServletResponse response, @PathVariable("videoId") String videoId) throws Exception
    {
        System.out.println(videoId);
      
        VideoUpload videoPathList = videoUploadMapper.SelectVideoId(Integer.parseInt(videoId));
        String videoPathUrl = videoPathList.getVideoUrl();
        Path filePath = Paths.get(videoPathUrl);

        if (Files.exists(filePath))
        {
            String mimeType = Files.probeContentType(filePath);
            if (StringUtils.hasText(mimeType))
            {
                response.setContentType(mimeType);
            }

            // 设置支持部分请求(范围请求)的 'Accept-Ranges' 响应头
            response.setHeader("Accept-Ranges", "bytes");

            // 从请求头中获取请求的视频片段的范围(如果提供)
            long startByte = 0;
            long endByte = Files.size(filePath) - 1;
            String rangeHeader = request.getHeader("Range");
            // System.out.println("rangeHeader:" + rangeHeader);
            if (rangeHeader != null && rangeHeader.startsWith("bytes="))
            {
                String[] range = rangeHeader.substring(6).split("-");
                startByte = Long.parseLong(range[0]);
                if (range.length == 2)
                {
                    endByte = Long.parseLong(range[1]);
                }
            }

            // System.out.println("start:" + startByte + ",end:" + endByte);
            log.info("start:" + startByte + ",end:" + endByte);

            // 设置 'Content-Length' 响应头,指示正在发送的视频片段的大小
            long contentLength = endByte - startByte + 1;
            response.setHeader("Content-Length", String.valueOf(contentLength));

            // 设置 'Content-Range' 响应头,指示正在发送的视频片段的范围
            response.setHeader("Content-Range", "bytes " + startByte + "-" + endByte + "/" + Files.size(filePath));

            // 设置响应状态为 '206 Partial Content'
            response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);

            // 使用 'RangeFileChannel' 进行视频片段的传输,以高效地只读取文件的请求部分
            ServletOutputStream outputStream = response.getOutputStream();
            try (RandomAccessFile file = new RandomAccessFile(filePath.toFile(), "r"); FileChannel fileChannel = file.getChannel())
            {
                fileChannel.transferTo(startByte, contentLength, Channels.newChannel(outputStream));
            } finally
            {
                outputStream.close();
            }
        } else
        {
            response.setStatus(HttpServletResponse.SC_NOT_FOUND);
            response.setCharacterEncoding(StandardCharsets.UTF_8.toString());
        }
    }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/896900.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

曲线救国 | 双非渣硕的秋招路

作者 | 带带大兄弟 面试锦囊之面经分享系列&#xff0c;持续更新中 欢迎后台回复"面试"加入讨论组交流噢 一篇旧文&#xff0c;可以参考~ 写在前面 双非渣硕&#xff0c;0实习&#xff0c;3篇水文&#xff0c;三个给老板当打工仔的nlp横向项目&#xff0c;八月份开…

uniapp 微信小程序 绘制海报,长按图片分享,保存海报

uView UI 2.0 dcloud 插件市场地址 弹窗海报源码 <template><!-- 推荐商品弹窗 --><u-popup :show"haibaoShow" mode"center" round26rpx z-index10076 bgColortransparent safeAreaInsetTop close"goodsclose"><image …

第四十九周周报

学习目标&#xff1a; VITGAN实验 学习时间&#xff1a; 2023.8.12-20238.18 学习产出&#xff1a; 一、实验 1、内容&#xff1a;在原模型的基础上加上相对位置编码&#xff0c;结果&#xff1a;比原模型差 2、内容&#xff1a;在原模型的基础上加上可学习位置编码相对位…

常用系统命令

重定向 cat aa.txt > bbb.txt 将输出定向到bbb.txt cat aaa.txt >> bbb.txt 输出并追加查看进程 ps ps -ef 显示所有进程 例⼦&#xff1a;ps -ef | grep mysql |&#xff1a;管道符 kill pid 结束进程&#xff0c; 如 kill 3732&#xff1b;根据进程名结束进程可以先…

终端美化+自动补全和语法高亮 + zsh

终端美化自动补全和语法高亮 zsh 一、前言 1、最终效果 2、所用工具 zsh shell # bash改zsh&#xff0c;选用原因后文提及oh my zsh # 插件管理powerlevel10k # 主题auto-suggestion # 自动补全syntax highlighting # 语法高亮3、bash vs zsh vs fish 经典话题&#xff0c;…

Shell编程之免交户

expect 一、多行重定向1.1 格式1.2 注意事项1.3 使用实例 二、expect免交户2.1 expect概述2.1.1 定义2.1.2 安装2.1.3 命令格式 2.2 相关命令2.2.1 spawn2.2.2 expect.3 send2.2.3 set2.2.4 send_user2.2.5 exp_continue2.2.6 脚本解释器2.2.7 结束符2.2.8 exit 2.3 运用实例.1…

【Django】Task3 外键的使用、Queryset和Instance

【Django】Task3 外键的使用、Queryset和Instance Task3主要理解数据库外键的使用场景&#xff0c;了解Queryset的功能&#xff0c;通过编写代码体验Queryset中对数据库实例的curd操作&#xff0c;同时了解到Instance的定义。 1.外键的使用 1.1什么是外键 数据表外键是数据…

SHELL 基础

echo 打印命令 &#xff1a; 显示字符串 [rootserver ~]# echo this is SHELL language this is SHELL language [rootserver ~]# echo this is SHELL language this is SHELL language [rootserver ~]# echo "this is SHELL language" this is SHELL language…

el-table 实现动态表头 静态内容 根据数据显示动态输入框

直接放代码了 <el-table:data"form.tableDataA"borderstripestyle"width: 100%; margin-top: 20px"><el-table-columnv-for"(category, categoryIndex) in form.tableDataA":key"categoryIndex":label"category.name&qu…

AWS security 培训笔记

云计算的好处 Amazon S3 (Storage) Amazon EC2 (Compute) 上图aws 的几个支柱&#xff1a;安全是其中一个啦 其中安全有几个方面 IAMdetection基础架构保护数据保护应急响应 关于云供应商的责任 data center 原来长这样 &#xff0c;据说非常之隐蔽的 如果有天退役了&#xf…

回归预测 | MATLAB实现TSO-BP金枪鱼群优化算法优化BP神经网络多输入单输出回归预测(多指标,多图)

回归预测 | MATLAB实现TSO-BP金枪鱼群优化算法优化BP神经网络多输入单输出回归预测&#xff08;多指标&#xff0c;多图&#xff09; 目录 回归预测 | MATLAB实现TSO-BP金枪鱼群优化算法优化BP神经网络多输入单输出回归预测&#xff08;多指标&#xff0c;多图&#xff09;效果…

第1篇:Arduino与ESP32开发板的安装方法

一、下载安装包 arduino2.1.0-esp32_2.0.9-esp8266_3.1.1离线安装包 百度网盘 请输入提取码百度网盘为您提供文件的网络备份、同步和分享服务。空间大、速度快、安全稳固&#xff0c;支持教育网加速&#xff0c;支持手机端。注册使用百度网盘即可享受免费存储空间https://pan…

计算机网络——OSI与TCP/IP各层的结构与功能,都有哪些协议?

文章目录 一 OSI与TCP/IP各层的结构与功能,都有哪些协议?1.1 应用层1.2 运输层1.3 网络层1.4 数据链路层1.5 物理层1.6 总结一下 二 ⭐TCP 三次握手和四次挥手(面试常客)2.1 TCP 三次握手漫画图解2.2 为什么要三次握手⭐2.3 第2次握手传回了ACK&#xff0c;为什么还要传回SYN&…

小程序的数据绑定和事件绑定

小程序的数据绑定 1.需要渲染的数据放在index.js中的data里 Page({data: {info:HELLO WORLD,imgSrc:/images/1.jpg,randomNum:Math.random()*10,randomNum1:Math.random().toFixed(2)}, }) 2.在WXML中通过{{}}获取数据 <view>{{info}}</view><image src"{{…

C#反编译工具ILSPY

ILSPY ILSpy 是一个开源的.Net程序集浏览器和反编译工具。 Visual Studio 2022附带了默认情况下启用的F12反编译支持&#xff08;使用我们的引擎v7.1&#xff09;。 在Visual Studio 2019中&#xff0c;您必须手动启用F12支持。转到“工具”/“选项”/“文本编辑器”/C#/Adva…

最新ai系统ChatGPT程序源码+详细搭建教程+mj以图生图+Dall-E2绘画+支持GPT4+AI绘画+H5端+Prompt知识库

目录 一、前言 二、系统演示 三、功能模块 3.1 GPT模型提问 3.2 应用工作台 3.3 Midjourney专业绘画 3.4 mind思维导图 四、源码系统 4.1 前台演示站点 4.2 SparkAi源码下载 4.3 SparkAi系统文档 五、详细搭建教程 5.1 基础env环境配置 5.2 env.env文件配置 六、环境…

回归预测 | MATLAB实现SOM-BP自组织映射结合BP神经网络多输入单输出回归预测(多指标,多图)

回归预测 | MATLAB实现SOM-BP自组织映射结合BP神经网络多输入单输出回归预测&#xff08;多指标&#xff0c;多图&#xff09; 目录 回归预测 | MATLAB实现SOM-BP自组织映射结合BP神经网络多输入单输出回归预测&#xff08;多指标&#xff0c;多图&#xff09;效果一览基本介绍…

对比学习损失—InfoNCE理论理解

InfoNoise的理解 InfoNCE loss温度系数 τ \tau τ InfoNCE loss 最近在看对比学习的东西&#xff0c;记录点基础的东西 「对比学习」 属于无监督学习的一种&#xff0c;给一堆数据&#xff0c;没有标签&#xff0c;自己学习出一种特征表示。 InfoNCE 这个损失是来自于论文&am…

9.文件基本操作

第四章 文件管理 9.文件基本操作 ​    “打开文件和关闭文件”与平常鼠标双击打开文件和点击“X”关闭文件是有所不同的。 ​    操作系统在处理open系统调用时主要做了以下两件事情&#xff0c;①根据我们提供的文件存放路径在外存当中找到这个目录对应的目录表&#x…

【java毕业设计】基于ssm+mysql+jsp的个性化影片推荐系统设计与实现(程序源码)-个性化影片推荐系统

基于ssmmysqljsp的个性化影片推荐系统设计与实现&#xff08;程序源码毕业论文&#xff09; 大家好&#xff0c;今天给大家介绍基于ssmmysqljsp的个性化影片推荐系统设计与实现&#xff0c;本论文只截取部分文章重点&#xff0c;文章末尾附有本毕业设计完整源码及论文的获取方式…