JS截取視頻第一幀

接上篇 js圖片/視頻預覽 - URL.createObjectURL()html

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

1、文件上傳

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

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

2、canvas截取圖片

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

  1. 建立畫布canvas或在html中直接寫入。
var canvas = document.createElement('canvas');
複製代碼
  1. 建立基於canvas的繪圖環境
var ctx = canvas.getContext('2d');
複製代碼

附 Q&A:web

  • 什麼是繪圖環境?

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

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

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

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

返回一個CanvasRenderingContext2D對象,也就是上文所說的可以支持絕大多數對畫布的操做。bash

  1. canvas上繪製圖片
// ctx.drawImage(file,sx,sy,swidth,sheight,x,y,width,height);
ctx.drawImage(this, 0, 0, swidth, sheight);
複製代碼

在不須要剪裁的狀況下,使用上述參數即截取操做file的所有,繪製到canvas網絡

關於參數(w3school)dom

參數 描述
file 規定要使用的圖像、畫布或視頻。
sx 可選。開始剪切的 x 座標位置。
sy 可選。開始剪切的 y 座標位置。
swidth 可選。被剪切圖像的寬度。
sheight 可選。被剪切圖像的高度。
x 在畫布上放置圖像的 x 座標位置。
y 在畫布上放置圖像的 y 座標位置。
width 可選。要使用的圖像的寬度。(伸展或縮小圖像)
height 可選。要使用的圖像的高度。(伸展或縮小圖像)
  1. canvas導出成圖片放入src
var src = canvas.toDataURL('image/jpeg');
複製代碼

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

canvas.toDataURL(type, encoderOptions);
複製代碼

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

3、截取視頻第一幀

上傳文件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
		
	})
複製代碼

效果

相關文章
相關標籤/搜索