利用MediaRecorder录制视频切片上传到ftp服务器

1.前言

之前项目中用Canvas+H5合并流实现录屏功能,但是因为视频流是一直保存到内存中,当你的合成的视频流越来越来,那么导致内存消耗越大(后面浏览器会卡死崩溃),所以必须做些优化,需要将一个大的视频切割成若干个小的视频,然后每个上传到ftp服务器!

2.思路

思路一

MediaRecorder使用start()方法,这个方法可以设置时间,比如start(1000)相当于把每隔个1000毫秒把媒体流存放到一个数组中,比如你需要200M,你可以去计算每隔1000毫秒添加进去的blob大小去计算,当blob的总的大小为200M就把这个视频上传,我之前是按这种方式去做的,但是后面发现有问题,这个分割出来的视频只有第一个视频能播放,后面的视频不能播放(无效视频),当时没发现啥问题,这个问题困扰了我一天,后来才发现原来是元数据的问题,元数据好比描述文件结构,那么可以理解一个文件里面需要元数据和实际的数据,在datavailable事件中data中获得的内容只是生成的整个文件的一部分. 第一个通常包含元数据和一些其他数据,但下一部分不包含元数据,这个就是问题所在,所以为什么只能播放第一个视频,后面的播放不了,因为后面的数据不包含元数据,所以这种方法果断放弃了!

思路二

需要生成多个独立文件,为此,意味着你需要在规定的间隔时间内(5000毫秒)生成一个新的MediaRecorder对象,这样生成出来的所有的文件都是单独独立的文件,包含元数据和实际的数据,项目中我就是用这种方法,大概测试了下1分钟大约需要30M,那么设置10分钟也就差不多300M,这个数据也不大,所以一般的内存300M足够了,下面看下我的代码核心部分

    // 开始录制
    isRecord() {
      let record = this.$refs.record;
      if (this.stopRecord) {
        record.title = "停止录制";
        let stream = this.mergeStream(),
          //注意要判断浏览器对webm的支持情况,有些时候video格式不对,在ondataavailable监听的时候会拿不到data数据(data的size为空)
          mime = MediaRecorder.isTypeSupported("video/webm; codecs=vp9")
            ? "video/webm; codecs=vp9"
            : "video/webm";
         //第一次建新的MediaRecorder对象
        this.recorder = this.recordAndUpload(stream, mime, this.recordTime);
        //后面每隔一分钟创建新的MediaRecorder对象
        this.setIntervalTimer = setInterval(() => {
          this.recorder = this.recordAndUpload(stream, mime, this.recordTime);
        }, this.recordTime * 1000);
        this.stopRecord = false;
        this.$message({
          message: "开始录制",
          type: "success",
          customClass: "message-alert",
        });
      } else {
        record.title = "开始录制";
        this.endCapture();
        this.$message({
          message: "录制完成",
          type: "success",
          customClass: "message-alert",
        });
        //取消动画
        window.cancelAnimationFrame(this.animationFrame);
        this.stopRecord = true;
        this.recorder = null;
      }
    },
    //每隔recordTime时间(60秒)录制一个视频
    recordAndUpload(stream, mime, recordTime) {
      let chunks = [],
        that = this,
        videoStartTime,
        //创建新的MediaRecorder对象
        recorder = new MediaRecorder(stream, {
          mimeType: mime,
        });
      //当触发start或者stop都会执行这个方法
      recorder.ondataavailable = function (e) {
        chunks.push(e.data);
      };
      recorder.onstop = () => {
        //因为设置了时间,所以文件的时间是确定的
        if (that.setTimeoutTimer) {
          that.uploadFile(chunks, recordTime * 1000);
        } else {
          //当手动点了停止,那么这个时间就不是确定的了,需要计算
          that.uploadFile(chunks, Date.now() - videoStartTime);
        }
      };
      //每隔recordTime时间(60秒)自动去触发停止stop录像事件
      this.setTimeoutTimer = setTimeout(() => {
        recorder.stop();
      }, recordTime * 1000);
      recorder.start();
      videoStartTime = Date.now();
      return recorder
    },
    
    //上传文件到服务,保存录像到数据库
 	uploadFile(chunks, recordTime) {
      let blob = new Blob(chunks, {
          type: "video/mp4",
        }),
        that = this;
      //recordTime为文件的时间,fixWebmDuration方法为文件设置时间(可以快进,快退),第三个参数是一个回调函数
      fixWebmDuration(blob, recordTime, (fixedBlob) => {
        //下载MP4到本地
        let fileName = `file_${new Date().getTime()}.mp4`,
          file = new window.File([fixedBlob], fileName),
          formData = new FormData();
        let url = URL.createObjectURL(
            new Blob([fixedBlob], { type: "video/mp4" })
          ),
          aLink = document.createElement("a");
        aLink.download = fileName;
        aLink.href = url;
        document.body.appendChild(aLink);
        aLink.click();
        aLink.remove();
        window.URL.revokeObjectURL(url);

		//上传到ftp服务器
        formData.append("file", file);
        that.axios
          .post(
            //接口
            `/ftp/ftpUpload`,
            formData,
            {
              headers: { "Content-Type": "multipart/form-data" },
            }
          )
          .then((data) => {
            //保存录像到数据库
            let params = new URLSearchParams(); //装载post传值
            params.append("cameraID", that.cameraIdChanged);
            params.append("videoName", fileName);
            params.append("videoUrl", fileName);
            that.axios
              .post("/video/addVideo", params)
              .then((data) => {
                console.log(data);
              })
              .catch((error) => {
                that.$message.error("服务器异常");
                console.log(error);
              });
          })
          .catch((error) => {
            that.$message.error("服务器异常");
            console.log(error);
          });
      });
    },

    //结束录制
    endCapture() {
      //清除定时器
      clearTimeout(this.setTimeoutTimer);
      clearInterval(this.setIntervalTimer);
      this.setTimeoutTimer = this.setIntervalTimer = null;
      this.recorder.stop();
    },

下面是效果图
在这里插入图片描述

  • 4
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值