Vue:錄制視頻并壓縮視頻文件

文件上傳框<input type="file">,除了可以選擇文件上傳之外景殷,還可以調(diào)用攝像頭來拍攝照片或者視頻并上傳澡屡。capture屬性可以判斷前置or后置攝像頭。在視頻播放的過程中绩蜻,用canvas定時(shí)截取一張圖片室埋,然后用gif.js生成一張GIF圖,從而完成前端的視頻壓縮孕蝉。

我這里使用的是Vue寫的腌逢,以下是我的流程及代碼:

一、下載gif.js相關(guān)文件佳鳖,可以到這里下載,然后將這幾個(gè)文件放在根目錄的static/js里面系吩。

gif.js相關(guān)文件及存放路徑

二穿挨、下載依賴包:

npm i timers

三、在頁面中聲明:

import { setInterval, clearInterval } from "timers";
import GIF from "../../static/js/gif.js"

四尊搬、html代碼塊:

<template>
  <div>
      <input ref="changeInput" type="file" accept="video/*" capture="user" @change="changeVideo" />
      <div>
        <div>視頻大型晾浴:{{videoSize}}</div>
        <div>視頻時(shí)長(zhǎng):{{videoLength}}</div>
        <div>
          <video id="myvideo" :src="videoSrc" :width="winWidth" :height="winHeight" ref="videoId" autoplay="true" controls muted></video>
          <canvas id="canvas" :width="winWidth" :height="winHeight"></canvas>
        </div>
      </div>
  </div>
</template>

五幌墓、在頁面加載完成時(shí)初始化GIF:

mounted(){
    //初始gif
    this.gif = new GIF({
      workers: 1,
      quality: 1000,
      width: window.innerWidth,
      height: window.innerHeight,
      workerScript: '../../static/js/gif.worker.js',
    });
  },

六常侣、當(dāng)input錄制完視頻返回頁面中,獲取到這個(gè)視頻文件胳施,每次拿到視頻文件需要先移除之前的監(jiān)聽:

//input文件走向
    changeVideo(e){
      var file = e.target.files[0];
      const video = document.getElementById('myvideo');
      //視頻開始播放
      video.removeEventListener('play', this.videoPlay, false);
      //視頻播放完
      video.removeEventListener('ended', this.videoEnded, false); 
      this.androidFile(file);
    },

七舞肆、上一步提到的this.androidFile方法,是通過這個(gè)視頻文件椿胯,在頁面播放一遍,在這個(gè)播放過程處理視頻前方,完成整個(gè)轉(zhuǎn)換過程廉油,獲取到最終的文件:

//安卓拍攝視頻
    androidFile(file){
      //視頻字節(jié)大小
      this.videoSize = file.size;

      const that = this;
      const video = document.getElementById('myvideo');
      const canvas = document.getElementById('canvas');
      var context = canvas.getContext('2d');

      this.gifSetTime = true;
      this.gif.abort()
      this.gif.frames = [];

      //file轉(zhuǎn)base64
      var reader = new FileReader();
      reader.readAsDataURL(file);
      reader.onload = function () {
        that.videoSrc = this.result;
        video.play();
      }
      //視頻開始播放
      video.addEventListener('play', this.videoPlay, false);
      //視頻播放完
      video.addEventListener('ended', this.videoEnded, false); 
      //獲取到所有的圖片并渲染完后執(zhí)行
      this.gif.on('finished', function(blob) {
        if(that.fileAndroid.size == blob.size) return;
        console.log("gif的blob文件",blob);
        //file
        that.fileAndroid = that.convertBase64UrlToFile(blob);
        //上傳視頻文件
        that.uploadVideo(that.fileAndroid);
      });
    },

八抒线、步驟七所說的this.videoPlay方法。視頻在頁面播放過程中趣竣,每200毫秒通過canvas截取一張圖片,把這些圖片一張張給gif.js堆疊:

//視頻開始播放
    videoPlay(){
      const that = this;
      const video = document.getElementById('myvideo');
      const canvas = document.getElementById('canvas');
      var context = canvas.getContext('2d');
      console.log("視頻時(shí)長(zhǎng)",video.duration);
      this.videoLength = video.duration;
        //畫布上畫視頻蝙茶,需要?jiǎng)討B(tài)地獲取它,一幀一幀地畫出來
        var times = setInterval(function(){
            context.drawImage(video, 0, 0, that.winWidth, that.winHeight);
            that.gif.addFrame(context, {
              copy: true
            });
            if(that.gifSetTime == false){
              clearInterval(times);
            }
        }, 200);
    },

九户秤、步驟七所說的this.videoEnded方法。視頻播放完转砖,通過gif.js將圖片堆疊的動(dòng)態(tài)圖渲染出來:

//視頻播放完
    videoEnded(){
      this.gifSetTime = false;
      console.log("視頻播放完畢鲸伴!")
      this.gif.render();
    },

十、步驟七所說的that.convertBase64UrlToFile方法姓赤。將gif.js生成的Blob文件轉(zhuǎn)換成File格式:

//blob to file
    convertBase64UrlToFile(blob) {
      var d = new Date().getTime();
      var type = 'image/gif'
      return new File([blob],"fileGif-" + d + '.gif', {type:type});
    },

最后通過步驟七所說的that.uploadVideo方法仲吏,上傳圖片給服務(wù)器:

//上傳視頻
    uploadVideo(file){
      console.log("上傳的視頻文件", file)
    },

在這提供我的全部代碼,Android的視頻文件比較大所以做壓縮誓斥,而IOS本身存在視頻壓縮许帐,所以我這里做了區(qū)分

<template>
  <div>
      <input ref="changeInput" type="file" accept="video/*" capture="user" @change="changeVideo" />
      <div>
        <div>視頻大小:{{videoSize}}</div>
        <div>視頻時(shí)長(zhǎng):{{videoLength}}</div>
        <div>
          <video id="myvideo" :src="videoSrc" :width="winWidth" :height="winHeight" ref="videoId" autoplay="true" controls muted></video>
          <canvas id="canvas" :width="winWidth" :height="winHeight"></canvas>
        </div>
      </div>
  </div>
</template>

<script>
import { setInterval, clearInterval } from "timers";
import GIF from "../../static/js/gif.js"
export default {
  data(){
    return {
      videoSize: '',
      videoSrc: '',
      videoLength: '',
      isAndroid: false,
      fileAndroid: {},
      winWidth: window.innerWidth,
      winHeight: window.innerHeight,
      gifSetTime: false,
      gif: '',
    }
  },
  created() {
    //判斷終端
    var u = navigator.userAgent;
    var isAndroid = u.indexOf('Android') > -1 || u.indexOf('Adr') > -1; //android終端
    var isiOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/); //ios終端
    if(isAndroid){
      console.log('isAndroid')
      this.isAndroid = true;
    }else if(isiOS){
      console.log('isiOS')
      this.isAndroid = false;
    }
  },
  mounted(){
    //初始gif
    this.gif = new GIF({
      workers: 1,
      quality: 1000,
      width: this.winWidth,
      height:this.winHeight,
      workerScript: '../../static/js/gif.worker.js',
    });
  },
  methods:{
    //input文件走向
    changeVideo(e){
      var file = e.target.files[0];
      const video = document.getElementById('myvideo');
      
      if(file !== undefined){
        //判斷走向
        if(this.isAndroid){
          //視頻開始播放
          video.removeEventListener('play', this.videoPlay, false);
          //視頻播放完
          video.removeEventListener('ended', this.videoEnded, false); 
          this.androidFile(file);
        }else{
          this.iphoneFile(file);
        }
      }
    },
    //IOS拍攝視頻
    iphoneFile(file){
      const that = this;
      //視頻字節(jié)大小
      this.videoSize = file.size;
      
      var url = null ; 
      //file轉(zhuǎn)換成blob
      if (window.createObjectURL!=undefined) { // basic
        url = window.createObjectURL(file) ;
      } else if (window.URL!=undefined) { // mozilla(firefox)
        url = window.URL.createObjectURL(file) ;
      } else if (window.webkitURL!=undefined) { // webkit or chrome
        url = window.webkitURL.createObjectURL(file) ;
      }
      this.videoSrc = url;
      if(file.size < 2100000 && file.size > 500000){
        this.uploadVideo(file);
      }else if(file.size >= 2100000){
        this.$vux.toast.text('視頻太大,請(qǐng)限制在10秒內(nèi)');
      }else{
        this.$vux.toast.text('視頻錄制不能少于5秒');
      }
    },
    //安卓拍攝視頻
    androidFile(file){
      //視頻字節(jié)大小
      this.videoSize = file.size;

      const that = this;
      const video = document.getElementById('myvideo');
      const canvas = document.getElementById('canvas');
      var context = canvas.getContext('2d');

      this.gifSetTime = true;
      this.gif.abort()
      this.gif.frames = [];

      //file轉(zhuǎn)base64
      var reader = new FileReader();
      reader.readAsDataURL(file);
      reader.onload = function () {
        that.videoSrc = this.result;
        video.play();
      }
      //視頻開始播放
      video.addEventListener('play', this.videoPlay, false);
      //視頻播放完
      video.addEventListener('ended', this.videoEnded, false); 
      
      this.gif.on('finished', function(blob) {
        if(that.fileAndroid.size == blob.size) return;
        console.log("gif的blob文件",blob);
        that.fileAndroid = that.convertBase64UrlToFile(blob);
        that.uploadVideo(that.fileAndroid);
      });
    },
    //視頻開始播放
    videoPlay(){
      const that = this;
      const video = document.getElementById('myvideo');
      const canvas = document.getElementById('canvas');
      var context = canvas.getContext('2d');
      console.log("視頻時(shí)長(zhǎng)",video.duration);
      this.videoLength = video.duration;
        //畫布上畫視頻,需要?jiǎng)討B(tài)地獲取它惧浴,一幀一幀地畫出來
        var times = setInterval(function(){
            context.drawImage(video, 0, 0, that.winWidth, that.winHeight);
            that.gif.addFrame(context, {
              copy: true
            });
            if(that.gifSetTime == false){
              clearInterval(times);
            }
        }, 200);
    },
    //視頻播放完
    videoEnded(){
      this.gifSetTime = false;
      console.log("視頻播放完畢!")
      this.gif.render();
    },
    //blob to file
    convertBase64UrlToFile(blob) {
      var d = new Date().getTime();
      var type = 'image/gif'
      return new File([blob],"fileGif-" + d + '.gif', {type:type});
    },
    //上傳視頻
    uploadVideo(file){
      console.log("上傳的視頻文件", file)
    },
  }
};
</script>
<style scoped>

</style>

試過很多種方法纵朋,而這種在移動(dòng)端瀏覽器(特別是微信瀏覽器G研洹)的兼容性是最好的。但是這個(gè)生成的視頻文件將會(huì)失去音頻宪祥,如果需要音頻的可以看我另一篇簡(jiǎn)書有說明幾種方法。有更好的方法歡迎大家留言藏澳,互相學(xué)習(xí)~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末耀找,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子凉驻,更是在濱河造成了極大的恐慌复罐,老刑警劉巖雄家,帶你破解...
    沈念sama閱讀 217,277評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件趟济,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡顷编,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門双肤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來钮惠,“玉大人,你說我怎么就攤上這事蔑赘。” “怎么了缩赛?”我有些...
    開封第一講書人閱讀 163,624評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵酥馍,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我卤材,道長(zhǎng)峦失,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,356評(píng)論 1 293
  • 正文 為了忘掉前任帆精,我火速辦了婚禮隧魄,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘购啄。我一直安慰自己,他們只是感情好顽悼,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評(píng)論 6 392
  • 文/花漫 我一把揭開白布几迄。 她就那樣靜靜地躺著,像睡著了一般木羹。 火紅的嫁衣襯著肌膚如雪解孙。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,292評(píng)論 1 301
  • 那天穷遂,我揣著相機(jī)與錄音娱据,去河邊找鬼盅惜。 笑死忌穿,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的屈芜。 我是一名探鬼主播朴译,決...
    沈念sama閱讀 40,135評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼躬翁!你這毒婦竟也來了盯拱?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,992評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤宁舰,失蹤者是張志新(化名)和其女友劉穎奢浑,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體雀彼,經(jīng)...
    沈念sama閱讀 45,429評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡详羡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評(píng)論 3 334
  • 正文 我和宋清朗相戀三年嘿悬,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片窒盐。...
    茶點(diǎn)故事閱讀 39,785評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蟹漓,死狀恐怖源内,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤嗽交,帶...
    沈念sama閱讀 35,492評(píng)論 5 345
  • 正文 年R本政府宣布夫壁,位于F島的核電站,受9級(jí)特大地震影響盒让,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜姨蝴,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評(píng)論 3 328
  • 文/蒙蒙 一撩扒、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧炒辉,春花似錦泉手、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至姆吭,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間检眯,已是汗流浹背昆淡。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留避凝,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,891評(píng)論 2 370
  • 正文 我出身青樓鹏氧,卻偏偏與公主長(zhǎng)得像佩谣,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子茸俭,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容