JS截取視頻第一幀

當視頻能夠預覽並上傳後,非要來一張視頻第一幀的截圖貼上,第一幀是黑的怎麼辦,下一幀。

一、文件上傳

使用 <inputtype="file">上傳, change事件作爲預覽 videosrc的觸發條件 新鮮源碼:

<video controls width="700" height="300" src="" id="video"></video>	
<input type="file" id="input" hidden />	
<button id="fileBtn">點擊上傳視頻</button>

二、canvas截取圖片

關於截取或者處理圖片/視頻/富文本編輯器, canvas是一個非常nice的選擇。

1.創建畫布 canvas或在 html中直接寫入。

var canvas = document.createElement('canvas');

2.創建基於 canvas的繪圖環境

var ctx = canvas.getContext('2d');

附 Q&A:

  • 什麼是繪圖環境?

網絡上的常規理解是“在準備畫布後,需要一些‘染料、畫筆、繪圖工具’的準備工作。” 比較官方的說話是返回 canvas的上下文環境, 說人話是'你能夠更好的操作你的 canvas'。

  • 關於 getContext('2d')的參數

方法中的 2d參數目前可以理解爲是 固定參數,表示想要一個 二維繪製環境。雖然大家都認爲有 2d自然應該有 3d,然而實際上本身設計時也是這麼考慮的,不過大家有點等不起了,所以都去選擇 webGL了。webGL是啥?瀏覽器端藉助系統顯卡進行 3D 繪圖。這是另一個故事了( IE別想了)。

  • 關於 canvas.getContext('2d')的返回值

返回一個 CanvasRenderingContext2D對象,也就是上文所說的能夠支持絕大多數對畫布的操作。

3.在 canvas上繪製圖片

    // ctx.drawImage(file,sx,sy,swidth,sheight,x,y,width,height);	
    ctx.drawImage(this, 0, 0, swidth, sheight);

在不需要剪裁的情況下,使用上述參數即截取操作 file的全部,繪製到 canvas

關於參數(w3school)

參數描述
file規定要使用的圖像、畫布或視頻。
sx可選。開始剪切的 x 座標位置。
sy可選。開始剪切的 y 座標位置。
swidth可選。被剪切圖像的寬度。
sheight可選。被剪切圖像的高度。
x在畫布上放置圖像的 x 座標位置。
y在畫布上放置圖像的 y 座標位置。
width可選。要使用的圖像的寬度。(伸展或縮小圖像)
height可選。要使用的圖像的高度。(伸展或縮小圖像)

4.將 canvas導出成圖片放入 src

var src = canvas.toDataURL('image/jpeg');

關於 toDataURL()方法。將 canvas的內容導出

canvas.toDataURL(type, encoderOptions);

type: 圖片格式,默認 image/jpegencoderOptions:圖片質量,取值範圍爲0到1,默認0.92。返回值:包含 data URIDOMString,也就是 base64格式。

三、截取視頻第一幀

上傳文件OK,用 canvas截取OK,怎麼找 第一幀呢?(啥時候開始截取呢?)

當然是多媒體的事件來觸發。關於 video的事件非常多(全部事件),這裏只討論能夠影響到截取到第一幀的各個事件。

    video.addEventListener('loadeddata', consoleString.bind(video, 'loadeddata')) // 當前幀加載完畢	
    video.addEventListener('loadedmetadata', consoleString.bind(video, 'loadedmetadata')) // 視頻元數據加載完畢	
    video.addEventListener('canplay', consoleString.bind(video, 'canplay')) // 視頻緩衝能夠開始播放	
    video.addEventListener('timeupdate', consoleString.bind(video, 'timeupdate')) // 播放位置發生改變時	
    video.addEventListener('play', consoleString.bind(video, 'play')) // 開始播放時	
    video.addEventListener('waiting', consoleString.bind(video, 'waiting')) // 要播放下一幀而需要緩衝時	
    function consoleString(string) {	
        console.log(string)	
    }
// 執行結果	
// timeupdate	
// loadedmetadata	
// loadeddata	
// canplay	
// play(開始播放)	
// 沒有waiting, 因爲視頻較小不需要緩衝
  • 根據順序,第一個被觸發的竟然是 timeupdate事件,按設想來說,最先執行的應該是 loadedmetadata,元數據加載完畢。關於這一點,在MDN上沒有明確的說明,但是可以推理一下:

currentTime更新時會觸發 timeupdate事件

來源:MDN

loadedmetadata的元數據恰好是指 時長、尺寸(僅視頻)以及文本軌道,也就是說在 video未定義的時候 currentTimeNaNNULL,當元數據中時長加載完畢後, currentTime更新至 0,因此觸發。

結論:雖然最先觸發,但是此時視頻文件尚未加載,截取的是 canvas的無內容本身。注:timeupdate事件根據使用的系統不同,每秒觸發4-66次,且由於觸發頻率高,單位過小(毫秒級別),事件響應需要延遲等原因,無法完全精準的控制。

  • loadedmetadata 上文提到,元數據加載完畢之後即觸發,但數據中並不包括視頻文件本身。結論:如果視頻文件較大,加載時間較長,仍然無法截取到已加載的第一幀。補充:通過 URL.createObjectURL()方法能夠基本做到無察覺,但並不保險。

  • loadeddata 

    當前幀數(第一幀)加載完畢觸發,沒毛病。結論:可用。補充:萬一第一幀是黑屏想用下一幀怎麼辦,對不起,餘下幀數加沒加載完不在它的考慮範圍之類,這個事件不管。

  • canplay

    視頻能夠開始播放時觸發,也就是根據上傳的視頻幀數決定加載多少幀(24/25/30/60等等)後滿足播放畫面後觸發。總結:因爲加載相對於 loadeddata的事件來說更多(多一丟丟),總體可行。補充:通過控制 currentTime可以滿足(但不可能是第二幀那麼準確),可以看做“當前播放幀”。

  • play 

    開始播放時纔會觸發,和上傳快速截取的需求不是很符合。

  • waiting

     已播放但下一畫面沒緩衝好時觸發,適合插播小廣告。

文件、方法、事件都OK了。截就完事兒了。

    video.addEventListener('loadeddata', function (e) {	
            canvas.width = this.videoWidth	
            canvas.height = this.videoHeight	
            width = this.videoWidth	
            height = this.videoHeight	
            ctx.drawImage(this, 0, 0, width, height);	
            var src = canvas.toDataURL('image/jpeg');	
            img.src = src;      	
            // var currentTime = this.currentTime	
            // duration = this.duration	
            // var fps = duration / 30	
    })

Dome:https://243341386.github.io/review_dome/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章