美文网首页
微信H5调用原生摄像头录像

微信H5调用原生摄像头录像

作者: 回到唐朝做IT | 来源:发表于2023-02-28 11:17 被阅读0次

    使用H5调用原生 getUserMedia方法获取摄像头权限进行录制视频,并且获取录制后的视频文件向后端进行存储,包含暂停录制、重新录制、录制时提示时间等功能

    image.png
    录制视频组件 VideoRecorder.vue
    <template>
      <div class='VideoRecorder'>
        <video ref="video" :controls="false" width="340px" muted height="auto" class="my-video" :webkit-playsinline="true" :playsinline="true"></video>
      </div>
    </template>
    
    <script>
    import moment from 'moment'
    var stopRecordCallback;
    export default {
      name: 'VideoRecorder',
      data () {
        return {
          mediaRecorder:null,
          mediaStream:null,
          recorderFile:null,
          chunks:[],
        }
      },
      mounted () {
        this.openCamera()  //摄像头初始化
      },
      destroyed(){
        this.closeBtn()
      },
      methods: { 
        //开始录制
        start(){
          this.mediaRecorder.start();
        },
       //停止录制
        stop(){
          this.stopRecord(()=> {
            this.$Dialog.alert({
              message: '录制成功!',
              confirmButtonColor: '#fda21d'
            }).then(() => {
              this.send();
            })
          });
        },
      //重新录制
        reStart(){
          this.mediaRecorder.start();
        },
        // 停止录制
        stopRecord(callback) {
          stopRecordCallback = callback;
          // 终止录制器
          this.mediaRecorder.stop();
          // 关闭媒体流
    
        },
        closeBtn(){
          this.closeStream(this.mediaStream);
        },
    
        openCamera() {
          var constraints = { 
            audio: true,
            video: { 
              // width: { min: 1024, ideal: 1280, max: 1920 },
              // height: { min: 776, ideal: 720, max: 1080 },
              deviceId: "default",
              facingMode: "user" //调用前置摄像头
            } 
          };
          this.getUserMedia(constraints,(err, stream)=> {
            if(err) {
              this.$emit('error',err)
              throw err;
            } else {
              // 通过 MediaRecorder 记录获取到的媒体流
              console.log();
              const video = this.$refs.video;
              this.mediaRecorder = new MediaRecorder(stream);
              this.mediaStream = stream;
              this.chunks = [],
              video.srcObject = stream;
              video.play();
    
              this.mediaRecorder.ondataavailable = (e)=> {
                this.mediaRecorder.blobs.push(e.data);
                this.chunks.push(e.data);
              };
              this.mediaRecorder.blobs = [];
    
              this.mediaRecorder.onstop = (e)=> {
                this.recorderFile = new Blob(this.chunks, {
                  'type': this.mediaRecorder.mimeType
                });
                this.chunks = [];
                if(null != stopRecordCallback) {
                  stopRecordCallback();
                }
              };
            }
          });
        },
    
        /**
         * 获取用户媒体设备(处理兼容的问题)
         * @param videoEnable {boolean} - 是否启用摄像头
         * @param audioEnable {boolean} - 是否启用麦克风
         * @param callback {Function} - 处理回调
         */
         getUserMedia(constraints,callback) {
          navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia ||
            navigator.msGetUserMedia || window.getUserMedia;
          if(navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
            navigator.mediaDevices.getUserMedia(constraints).then(function(stream) {
              callback(false, stream);
            })['catch'](function(err) {
              callback(err);
            });
          } else if(navigator.getUserMedia) {
            navigator.getUserMedia(constraints, function(stream) {
              callback(false, stream);
            }, function(err) {
              callback(err);
            });
          } else {
            callback(new Error('Not support userMedia'));
          }
        },
    
        /**
         * 关闭媒体流
         * @param stream {MediaStream} - 需要关闭的流
         */
        closeStream(stream) {
          if(!stream) return;
          if(typeof stream.stop === 'function') {
            stream.stop();
          } else {
            let trackList = [stream.getAudioTracks(), stream.getVideoTracks()];
    
            for(let i = 0; i < trackList.length; i++) {
              let tracks = trackList[i];
              if(tracks && tracks.length > 0) {
                for(let j = 0; j < tracks.length; j++) {
                  let track = tracks[j];
                  if(typeof track.stop === 'function') {
                    track.stop();
                  }
                }
              }
            }
          }
        },
    
        saver() {
          var file = new File([this.recorderFile], 'msr-' + (new Date).toISOString().replace(/:|\./g, '-') + '.mp4', {
            type: 'video/mp4'
          });
          FileSaver.saveAs(file);
        },
    
         send() {
          //格式: subject-张大宝-宣导页视频-20221214172357.mp4
          let time =moment().format("YYYYMMDDHHmmss");
          let localUrl=this.blobToUrl(this.recorderFile)
          let fileName=`subject-${this.subjectId}-宣导页视频-${time}.mp4`  // 可自定义文件名称
          var file = new File([this.recorderFile],fileName , {
            type: 'video/mp4'
          });
          var formData = new FormData();
          formData.append("fileName", fileName);
          formData.append("file", file);
          console.log('===176===录制文件', formData)
          const recorder = {
            formData: formData,
            localUrl: localUrl
          }
          this.$emit('success', recorder)
        },
        // blob 转化url
        blobToUrl(blob){
          let url = window.URL.createObjectURL(blob)
          return url
        }
    
      }
    }
    </script>
    
    <style lang="scss" scoped>
    
      .VideoRecorder{
        position: relative;
        .my-video{
          position: relative;
          left: -44px;
        }
        .my-video::-webkit-media-controls-enclosure{ 
            display: none;
        }
      }
    
    </style>
    
    使用组件 recordVideo.vue
      <template>
      <div class='recordVideo'>
        <div class="content">
          {{videoContent}}
        </div>
        <div class="video-container">
          <video v-if="videoSrc" ref="myVideo" class="my-video" :controls="false" :src="videoSrc" autoplay loop :webkit-playsinline="true" :playsinline="true" x5-video-player-type="h5">
          </video>
          <VideoRecorder v-else ref='videoRecorder' @success='handlerSuccess' @error='handlerError'></VideoRecorder>
        </div>
        <div v-if="!videoSrc" class="record-time">
          <img v-if="isStart" src="@/assets/images/ing.gif"  alt=""> 
          <span >{{digitFormat(recrodTime)}}</span>
        </div>
        <div class="video-action" v-if="isCanUse">
          <div class="start" v-if="!videoSrc" @touchstart.prevent="touchstart"
          @touchend="touchend">长按录制</div>
          <span class="starting" v-if="isStart"></span>
          <div class="reStart" v-if="videoSrc" @click="handleReStart">重新录制</div>
          <div class="complate" v-if="videoSrc" @click="handleComplate">完成</div>
        </div>
        <div style="text-align: center;" v-else> 当前手机系统版本不支持摄像录制</div>
        <div class="tips">使用适中的语速,逐字清晰地朗读顶部文字</div>
      </div>
    </template>
    
    <script>
    import VideoRecorder from '@/components/VideoRecorder'
    import {fileuploadext, processfilesave } from '@/api/user.js'
    export default {
      name: 'recordVideo',
      components: { 
        VideoRecorder
      },
      data () {
        return {
          recorder:null,
          videoSrc:'',
          // videoSrc:'https://oceanus-trial-file-dev.obs.cn-east-3.myhuaweicloud.com/20221221/b746d257a9644ff7b702e243f2cdd467.mp4',
          recrodTime:0,  
          intervalTime:null,
          isStart:false,
          // ---
          videoContent:'',  //录制所读内容
          formData:null, //录制文件
          isCanUse:true,  //录制组件是否可用
        }
      },
      created () {
        this.videoContent=this.$route.query.videoContent
      },
      mounted () {
    
      },
      destroyed(){
        this.clearIntervaltime()
      },
      methods: { 
        // 开始录制
        touchstart(){
          console.log('===53===开始')
          this.isStart=true
          this.$nextTick(()=>{
            this.startRecordReadTime() //读秒计时
            this.$refs.videoRecorder&&this.$refs.videoRecorder.start()
          })
        },
        // 暂停录制
        touchend(){
          console.log('===56===结束' )
          this.$refs.videoRecorder.stop()
          this.isStart=false
        },
        // 重新录制
        handleReStart(){
          this.clearIntervaltime()
          this.videoSrc='';
        },
        // 完成
        handleComplate(){
          // 上传文件获取文件id   自己业务逻辑
          fileuploadext(this.formData).then(({success, data}) => {
            if(success){
              //...
            }
          })
        },
        handlerSuccess(val){
          console.log('文件返回信息=============', val)
          this.formData=val.formData
          this.videoSrc=val.localUrl
          this.$nextTick(()=>{
            this.$refs.myVideo&&this.$refs.myVideo.play()
          })
        },
        handlerError(val){
          console.log('失败===70===', val)
          this.isCanUse=false
        },
        // 清除定时器
        clearIntervaltime() {
          this.recrodTime=0
          if (this.intervalTime) {
            clearInterval(this.intervalTime)
          }
        },
        // 录制时间
        startRecordReadTime() {
          this.intervalTime = setInterval(() => {
            if(this.isStart){
              this.recrodTime+=1
            }
          }, 1000)
        },
        digitFormat(time) {
          if (!time) {
            return '00:00'
          }
          var minute = Math.floor(time / 60)
          var second = time % 60
          var hour = Math.floor(minute / 60)
          minute = minute % 60
          if (hour < 10) {
            hour = '0' + hour
          }
          if (minute < 10) {
            minute = '0' + minute
          }
          if (second < 10) {
            second = '0' + second
          }
          return  minute + ':' + second
        }
    
      }
    }
    </script>
    
    <style lang="scss" scoped>
      .recordVideo{
        .content{
          box-sizing: border-box;
          padding:24px 21px;
          width: 343px;
          border-radius: 8px;
          background: #fff;
          box-shadow: 0 0 10px rgba(0, 0, 0, 0.06);
          font-size: 20px;
          line-height: 30px;
          text-align: left;
          color: #666;
          margin:0 auto;
          margin-top:30px;
        }
        .video-container{
          width: 256px;
          height: 256px;
          background-color: #999999;
          border-radius: 100% !important;
          margin:0 auto;
          text-align: center;
          margin-top: 54px;
          overflow: hidden !important;
          -webkit-backface-visibility: hidden;  //兼容ios
          -webkit-transform: translate3d(0, 0, 0);
          position: relative;
          .my-video{
            position:absolute;
            left:0;
            top:0;
            width:100%;
            height:100%;
            align-items:center;
            // object-fit: fill; //视频铺满  仅android可以
            object-fit: cover;  //视频铺满 android ios 都可以
          }
          /* 隐藏video 所有控件 */
          .show-video::-webkit-media-controls-enclosure{ 
              display: none !important;
          }
        }
        .record-time{
          text-align: center;
          margin-top:20px;
          color:#333;
          img{
            width:10px;
            height:10px;
            margin-right:5px;
          }
        }
        .video-action{
          margin-top:70px;
          display: flex;
          position: relative;
          div{
            width: 166px;
            height: 40px;
            line-height:40px;
            text-align: center;
            border-radius: 20px;
            font-size: 14px;
            margin:0 auto;
          }
          .start{
            color: #fff;
            background: #fda21d;
            border: 1px solid #fda21d;
            z-index: 99;
          }
          .starting{
            position: absolute;
            width: 180px;
            height: 56px;
            border-radius: 28px;
            left:0;
            right:0;
            top:-7px;
            margin:0 auto;
            animation: breating 1.5s linear infinite;
          }
          @keyframes breating {
              0% {
                transform: scale(1.0);
                background: #fecaa7;
              }
              50% {
                transform: scale(1.1);
                background: #ffe4d0;
              }
              100% {
                background: #fff6ef;
                transform: scale(1.2)
              }
          }
          .stop{
            color:#666;
            background: #fff;
            border: 1px solid #dcdee2;
          }
          .reStart{
            color:#666;
            background: #fff;
            border: 1px solid #dcdee2;
          }
          .complate{
            color: #fff;
            background: #fda21d;
            border: 1px solid #fda21d;
          }
        }
        .tips{
          font-size: 14px;
          line-height: 20px;
          text-align: center;
          color: #4284f5;
          margin-top:24px;
        }
      }
    </style>
    

    相关文章

      网友评论

          本文标题:微信H5调用原生摄像头录像

          本文链接:https://www.haomeiwen.com/subject/sjhlldtx.html