/**
* @file html5.js
*/
從 './tech.js' 導入技術;
import * as Dom from '../utils/dom.js';
import * as Url from '../utils/url.js';
從 '../utils/log.js' 導入日誌;
從 '../utils/browser.js' 導入 * 作為瀏覽器;
從“全局/文檔”導入文檔;
從“全局/窗口”導入窗口;
從'../utils/obj'導入{assign};
從 '../utils/merge-options.js' 導入 mergeOptions;
從 '../utils/string-cases.js' 導入 {toTitleCase};
從 '../tracks/track-types' 導入 {NORMAL as TRACK_TYPES, REMOTE};
從 './setup-sourceset' 導入 setupSourceset;
從 '../utils/define-lazy-property.js' 導入 defineLazyProperty;
從'../utils/promise'導入{silencePromise};
/**
* HTML5 媒體控制器 - HTML5 媒體 API 的包裝器
*
* @mixes Tech~SourceHandlerAdditions
* @extends 技術
*/
類 Html5 擴展技術 {
/**
* 創建該技術的一個實例。
*
* @param {對象} [選項]
* 播放器選項的鍵/值存儲。
*
* @param {Component~ReadyCallback} 準備好了
* `HTML5` 技術準備就緒時調用的回調函數。
*/
構造函數(選項,準備就緒){
超級(選項,準備就緒);
const source = options.source;
讓 crossoriginTracks = false;
this.featuresVideoFrameCallback = this.featuresVideoFrameCallback && this.el_.tagName === '視頻';
// 如果提供了源,則設置源
// 1) 檢查源是否是新的(如果不是,我們希望保留原件,這樣播放就不會中斷)
// 2) 檢查標籤的網絡狀態是否在初始化時失敗,如果是,則重置源
// 無論如何,錯誤被觸發。
if (source && (this.el_.currentSrc !== source.src || (options.tag && options.tag.initNetworkState_ === 3))) {
this.setSource(來源);
}其他{
this.handleLateInit_(this.el_);
}
// 在 late sourceset/init 之後設置 sourceset
如果(選項。enableSourceset){
this.setupSourcesetHandling_();
}
this.isScrubbing_ = false;
如果 (this.el_.hasChildNodes()) {
const nodes = this.el_.childNodes;
讓 nodesLength = nodes.length;
const removeNodes = [];
而(節點長度--){
const 節點 = 節點 [節點長度];
const nodeName = node.nodeName.toLowerCase();
如果(節點名稱==='跟踪'){
如果(!this.featuresNativeTextTracks){
// 清空視頻標籤軌道,以便內置播放器不使用它們。
// 這可能不夠快,無法阻止 HTML5 瀏覽器讀取標籤
// 所以如果我們手動執行,我們需要關閉任何默認軌道
// 字幕和副標題。視頻元素.textTracks
removeNodes.push(節點);
}其他{
// 將 HTMLTrackElement 和 TextTrack 存儲到遠程列表
this.remoteTextTrackEls().addTrackElement_(node);
this.remoteTextTracks().addTrack(node.track);
this.textTracks().addTrack(node.track);
如果(!crossoriginTracks &&
!this.el_.hasAttribute('crossorigin') &&
Url.isCrossOrigin(node.src)) {
跨域曲目=真;
}
}
}
}
對於(讓 i = 0;i < removeNodes.length;i++){
this.el_.removeChild(removeNodes[i]);
}
}
this.proxyNativeTracks_();
如果(this.featuresNativeTextTracks && crossoriginTracks){
log.warn('正在從另一個源加載文本軌道,但未使用 crossorigin 屬性。\\n' +
'這可能會阻止加載文本軌道。');
}
// 防止 iOS Safari 在本機播放期間禁用元數據文本軌道
this.restoreMetadataTracksInIOSNativePlayer_();
// 確定是否應該使用本機控件
// 我們的目標應該是讓自定義控件在移動設備上隨處可見
// 所以我們可以一起刪除它。現在這將阻止自定義
// 支持觸控的筆記本電腦(如 Chrome Pixel)上的控件
如果((瀏覽器.TOUCH_ENABLED || 瀏覽器.IS_IPHONE ||
browser.IS_NATIVE_ANDROID) && options.nativeControlsForTouch === true) {
this.setControls(true);
}
// 在 iOS 上,我們要代理 `webkitbeginfullscreen` 和 `webkitendfullscreen`
// 進入 `fullscreenchange` 事件
this.proxyWebkitFullscreen_();
this.triggerReady();
}
/**
* 處理 `HTML5` 媒體元素並刪除所有曲目。
*/
處置(){
如果 (this.el_ && this.el_.resetSourceset_) {
this.el_.resetSourceset_();
}
Html5.disposeMediaElement(this.el_);
this.options_ = null;
// 技術人員將處理模擬曲目列表的清除
super.dispose();
}
/**
* 修改媒體元素,以便我們可以檢測何時
* 來源已更改。在源更改後立即觸發 `sourceset`
*/
setupSourcesetHandling_() {
setupSourceset(這個);
}
/**
* 在 iOS Safari 本地播放器中啟用字幕軌道時,所有其他
* 曲目被禁用(包括元數據曲目),這會使它們的所有
* 關聯的提示點。這會將元數據軌道恢復到全屏前
* 在這些情況下說明,以免不必要地丟失提示點。
*
* @私人的
*/
restoreMetadataTracksInIOSNativePlayer_() {
const textTracks = this.textTracks();
讓 metadataTracksPreFullscreenState;
// 捕獲每個元數據軌道當前狀態的快照
const takeMetadataTrackSnapshot = () => {
metadataTracksPreFullscreenState = [];
對於(讓 i = 0;i < textTracks.length;i++){
const track = textTracks[i];
如果(track.kind ==='元數據'){
metadataTracksPreFullscreenState.push({
追踪,
存儲模式:track.mode
});
}
}
};
// 快照每個元數據軌道的初始狀態,並更新快照
// 每次都有一個軌道 'change' 事件
takeMetadataTrackSnapshot();
textTracks.addEventListener('change', takeMetadataTrackSnapshot);
this.on('dispose', () => textTracks.removeEventListener('change', takeMetadataTrackSnapshot));
const restoreTrackMode = () => {
for (let i = 0; i < metadataTracksPreFullscreenState.length; i++) {
const storedTrack = metadataTracksPreFullscreenState[i];
如果(storedTrack.track.mode === '禁用' && storedTrack.track.mode !== storedTrack.storedMode){
storedTrack.track.mode = storedTrack.storedMode;
}
}
// 我們只希望這個處理程序在第一個 'change' 事件上執行
textTracks.removeEventListener('change', restoreTrackMode);
};
// 當我們進入全屏播放時,停止更新快照和
// 將所有軌道模式恢復到全屏前的狀態
this.on('webkitbeginfullscreen', () => {
textTracks.removeEventListener('change', takeMetadataTrackSnapshot);
// 在添加之前刪除監聽器以防萬一它之前沒有被刪除
textTracks.removeEventListener('change', restoreTrackMode);
textTracks.addEventListener('change', restoreTrackMode);
});
// 離開全屏後再次開始更新快照
this.on('webkitendfullscreen', () => {
// 在添加之前刪除監聽器以防萬一它之前沒有被刪除
textTracks.removeEventListener('change', takeMetadataTrackSnapshot);
textTracks.addEventListener('change', takeMetadataTrackSnapshot);
// 刪除 restoreTrackMode 處理程序,以防它在全屏播放期間未被觸發
textTracks.removeEventListener('change', restoreTrackMode);
});
}
/**
* 嘗試強制覆蓋給定類型的軌道
*
* @param {string} type - 要覆蓋的軌道類型,可能的值包括“音頻”,
*“視頻”和“文本”。
* @param {boolean} override - 如果設置為 true 原生音頻/視頻將被覆蓋,
* 否則可能會使用原生音頻/視頻。
* @私人的
*/
overrideNative_(類型,覆蓋){
// 如果沒有行為改變,則不要添加/刪除偵聽器
如果(覆蓋!== this[`featuresNative${type}Tracks`]){
返回;
}
const lowerCaseType = type.toLowerCase();
如果(這個[`${lowerCaseType}TracksListeners_`]){
Object.keys(this[`${lowerCaseType}TracksListeners_`]).forEach((eventName) => {
const elTracks = this.el()[`${lowerCaseType}Tracks`];
elTracks.removeEventListener(eventName, this[`${lowerCaseType}TracksListeners_`][eventName]);
});
}
這個[`featuresNative${type}Tracks`] = !override;
這個[`${lowerCaseType}TracksListeners_`] = null;
this.proxyNativeTracksForType_(lowerCaseType);
}
/**
* 嘗試強制覆蓋本機音軌。
*
* @param {boolean} override - 如果設置為 true 原生音頻將被覆蓋,
*否則可能會使用本機音頻。
*/
overrideNativeAudioTracks(覆蓋){
this.overrideNative_('Audio', override);
}
/**
* 嘗試強制覆蓋本機視頻軌道。
*
* @param {boolean} override - 如果設置為 true 原生視頻將被覆蓋,
*否則可能會使用原生視頻。
*/
overrideNativeVideoTracks(覆蓋){
this.overrideNative_('Video', override);
}
/**
* 將給定類型的本機曲目列表事件代理到我們的曲目
* 列出我們正在播放的瀏覽器是否支持該類型的曲目列表。
*
* @param {string} name - 軌道類型;值包括“音頻”、“視頻”和“文本”
* @私人的
*/
proxyNativeTracksForType_(名稱){
const props = TRACK_TYPES[名稱];
const elTracks = this.el()[props.getterName];
const techTracks = this[props.getterName]();
如果 (!this[`featuresNative${props.capitalName}Tracks`] ||
!elTracks ||
!elTracks.addEventListener) {
返回;
}
const 監聽器 = {
改變:(e)=> {
const 事件 = {
類型:'改變',
目標:techTracks,
當前目標:techTracks,
srcElement:techTracks
};
techTracks.trigger(事件);
// 如果我們是文本軌道變化事件,我們也應該通知
// 遠程文本軌道列表。這可能會導致誤報
// 如果我們要在非遠程軌道上獲得更改事件並且
// 我們觸發了遠程文本軌道列表上的事件,但它沒有
// 包含那條軌道。但是,最佳實踐意味著遍歷
// 軌道列表並蒐索適當的模式值,因此,
// 這不應該成為問題
如果(名稱==='文本'){
這個[REMOTE.remoteText.getterName]().trigger(事件);
}
},
添加曲目(e){
techTracks.addTrack(e.track);
},
removetrack(e) {
techTracks.removeTrack(e.track);
}
};
const removeOldTracks = function() {
const removeTracks = [];
對於(讓 i = 0;i < techTracks.length;i++){
讓發現=假;
對於(設 j = 0;j < elTracks.length;j++){
如果(elTracks[j] === techTracks[i]){
發現=真;
休息;
}
}
如果(!發現){
removeTracks.push(techTracks[i]);
}
}
而(removeTracks.length){
techTracks.removeTrack(removeTracks.shift());
}
};
this[props.getterName + 'Listeners_'] = listeners;
Object.keys(listeners).forEach((eventName) => {
const listener = listeners[事件名稱];
elTracks.addEventListener(eventName, listener);
this.on('dispose', (e) => elTracks.removeEventListener(eventName, listener));
});
// 刪除不再使用的(原生)軌道
this.on('loadstart', removeOldTracks);
this.on('dispose', (e) => this.off('loadstart', removeOldTracks));
}
/**
* 如果我們正在播放的瀏覽器,則將所有本機曲目列表事件代理到我們的曲目列表
* 支持該類型的曲目列表。
*
* @私人的
*/
proxyNativeTracks_() {
TRACK_TYPES.names.forEach((名稱) => {
this.proxyNativeTracksForType_(名稱);
});
}
/**
* 創建 `Html5` Tech 的 DOM 元素。
*
* @return {元素}
* 被創建的元素。
*/
創建El() {
讓 el = this.options_.tag;
// 檢查此瀏覽器是否支持將元素移動到框中。
// 在 iPhone 上,如果你移動元素,視頻將會中斷,
// 所以我們必須創建一個全新的元素。
// 如果我們攝取播放器 div,則不需要移動媒體元素。
如果 (!el ||
!(this.options_.playerElIngest ||
this.movingMediaElementInDOM)) {
// 如果原始標籤仍然存在,則克隆並刪除它。
如果(EL){
const clone = el.cloneNode(true);
如果(el.parentNode){
el.parentNode.insertBefore(clone, el);
}
Html5.disposeMediaElement(el);
el = 克隆;
}其他{
el = document.createElement('視頻');
// 確定是否應該使用本機控件
const tagAttributes = this.options_.tag && Dom.getAttributes(this.options_.tag);
const attributes = mergeOptions({}, tagAttributes);
如果 (!browser.TOUCH_ENABLED || this.options_.nativeControlsForTouch !== true) {
刪除屬性。控件;
}
Dom.setAttributes(
埃爾,
分配(屬性,{
id: this.options_.techId,
類:'vjs-tech'
})
);
}
el.playerId = this.options_.playerId;
}
if (typeof this.options_.preload !== 'undefined') {
Dom.setAttribute(el, 'preload', this.options_.preload);
}
如果(this.options_.disablePictureInPicture !==未定義){
el.disablePictureInPicture = this.options_.disablePictureInPicture;
}
// 更新特定的標籤設置,以防它們被覆蓋
// `autoplay` 必須是 *last* 以便出現 `muted` 和 `playsinline`
// 當 iOS/Safari 或其他瀏覽器嘗試自動播放時。
const settingsAttrs = ['loop', 'muted', 'playsinline', 'autoplay'];
for (let i = 0; i < settingsAttrs.length; i++) {
const attr = settingAttrs[i];
const value = this.options_[attr];
如果(類型值!=='未定義'){
如果(值){
Dom.setAttribute(el, attr, attr);
}其他{
Dom.removeAttribute(el, attr);
}
el[屬性] = 值;
}
}
返回 el;
}
/**
* 如果 loadstart 事件已經觸發,這將在 videojs 之前被觸發
* 準備好。何時會發生這種情況的兩個已知示例是:
* 1。如果我們在開始加載後加載播放對象
* 2。媒體已經在播放(通常自動播放)然後
*
* 此函數將觸發另一個 loadstart,以便 videojs 可以趕上。
*
* @fires Tech#loadstart
*
* @return {未定義}
* 什麼都不返回。
*/
handleLateInit_(el) {
如果(el.networkState === 0 || el.networkState === 3){
// 視頻元素還沒有開始加載源
// 或者沒有找到資源
返回;
}
如果(el.readyState === 0){
// NetworkState 是同步設置的,但是 loadstart 是在
// 當前堆棧的末尾,通常在 setInterval(fn, 0) 之前。
// 所以此時我們知道 loadstart 可能已經觸發或正在觸發
// 即將開火,無論哪種方式,玩家都還沒有看到它。
// 我們不想在這裡過早地觸發 loadstart 並導致
// double loadstart 所以我們等著看它是否發生在現在
// 和下一個循環,如果沒有則觸發它。
// 但是,我們還想確保它在加載元數據之前觸發
// 這也可能發生在現在和下一個循環之間,所以我們將
// 也要注意這一點。
讓 loadstartFired = false;
const setLoadstartFired = function() {
loadstartFired = true;
};
this.on('loadstart', setLoadstartFired);
const triggerLoadstart = function() {
// 我們確實錯過了最初的 loadstart。確保播放器
// 在 loadedmetadata 之前看到 loadstart
如果(!loadstartFired){
this.trigger('loadstart');
}
};
this.on('loadedmetadata', triggerLoadstart);
這個。準備好(功能(){
this.off('loadstart', setLoadstartFired);
this.off('loadedmetadata', triggerLoadstart);
如果(!loadstartFired){
// 我們確實錯過了原始的原生 loadstart。現在開火。
this.trigger('loadstart');
}
});
返回;
}
// 從這裡我們知道 loadstart 已經觸發,但我們錯過了它。
// 如果我們加倍,其他 readyState 事件就不是什麼大問題
// 他們,所以不會像 loadstart 那樣麻煩來防止
// 除非我們找到理由。
const eventsToTrigger = ['loadstart'];
// 加載的元數據:新等於 HAVE_METADATA (1) 或更大
eventsToTrigger.push('loadedmetadata');
// 加載數據:新增加到 HAVE_CURRENT_DATA (2) 或更大
如果(el.readyState >= 2){
eventsToTrigger.push('loadeddata');
}
// canplay: 新增加到 HAVE_FUTURE_DATA (3) 或更大
如果(el.readyState >= 3){
eventsToTrigger.push('canplay');
}
// canplaythrough:新等於 HAVE_ENOUGH_DATA (4)
如果(el.readyState >= 4){
eventsToTrigger.push('canplaythrough');
}
// 我們仍然需要給玩家時間來添加事件監聽器
這個。準備好(功能(){
eventsToTrigger.forEach(函數(類型){
這個。觸發器(類型);
}, 這);
});
}
/**
* 設置我們是否正在擦洗。
* 這用於決定我們是否應該使用 `fastSeek` 或不。
* `fastSeek` 用於在 Safari 瀏覽器上提供特技播放。
*
* @param {boolean} isScrubbing
* - true 因為我們目前正在擦洗
* - false 因為我們不再擦洗
*/
setScrubbing(isScrubbing){
this.isScrubbing_ = isScrubbing;
}
/**
* 獲取我們是否正在擦洗。
*
* @return {boolean} isScrubbing
* - true 因為我們目前正在擦洗
* - false 因為我們不再擦洗
*/
擦洗(){
返回 this.isScrubbing_;
}
/**
* 為 `HTML5` 技術設置當前時間。
*
* @param {number} 秒
* 將媒體的當前時間設置為此。
*/
設置當前時間(秒){
嘗試{
如果(this.isScrubbing_ && this.el_.fastSeek && browser.IS_ANY_SAFARI){
this.el_.fastSeek(秒);
}其他{
this.el_.currentTime = seconds;
}
} 抓住 (e) {
log(e, '視頻還沒有準備好。(Video.js)');
// this.warning(VideoJS.warnings.videoNotReady);
}
}
/**
* 獲取 HTML5 媒體元素的當前持續時間。
*
* @return {數字}
* 媒體的持續時間,如果沒有持續時間則為 0。
*/
期間() {
// Android Chrome 將報告 VOD HLS 的持續時間為 Infinity 直到之後
// 播放已經開始,錯誤地觸發了實時顯示。
// 如果播放還沒有開始返回 NaN 並觸發一次 durationupdate
// 可以可靠地知道持續時間。
如果 (
this.el_.duration ===無限&&
瀏覽器.IS_ANDROID &&
瀏覽器.IS_CHROME &&
這個.el_.currentTime === 0
) {
// 等待 currentTime > 0 的第一個 `timeupdate` - 可能有
// 多個為 0
const checkProgress = () => {
如果 (this.el_.currentTime > 0) {
// 觸發真正直播視頻的持續時間變化
如果(this.el_.duration === Infinity){
this.trigger('durationchange');
}
this.off('timeupdate', checkProgress);
}
};
this.on('timeupdate', checkProgress);
返回南;
}
返回 this.el_.duration ||南;
}
/**
* 獲取 HTML5 媒體元素的當前寬度。
*
* @return {數字}
* HTML5 媒體元素的寬度。
*/
寬度() {
返回 this.el_.offsetWidth;
}
/**
* 獲取 HTML5 媒體元素的當前高度。
*
* @return {數字}
* HTML5 媒體元素的高度。
*/
高度() {
返回 this.el_.offsetHeight;
}
/**
* 代理 iOS `webkitbeginfullscreen` 和 `webkitendfullscreen` 進入
* `fullscreenchange` 事件。
*
* @私人的
* @fires fullscreenchange
* @listens webkitendfullscreen
* @listens webkitbeginfullscreen
* @listens webkitbeginfullscreen
*/
proxyWebkitFullscreen_() {
如果(!(this.el_中的'webkitDisplayingFullscreen')){
返回;
}
const endFn = function() {
this.trigger('fullscreenchange', { isFullscreen: false });
// 當存在全屏時,Safari 有時會在視頻元素上設置控件。
如果 (this.el_.controls && !this.options_.nativeControlsForTouch && this.controls()) {
this.el_.controls = false;
}
};
const beginFn = function() {
if ('webkitPresentationMode' in this.el_ && this.el_.webkitPresentationMode !== '畫中畫') {
this.one('webkitendfullscreen', endFn);
this.trigger('fullscreenchange', {
是全屏:真,
// 設置一個標誌以防其他技術觸發全屏更改
nativeIOS全屏:真
});
}
};
this.on('webkitbeginfullscreen', beginFn);
this.on('處置', () => {
this.off('webkitbeginfullscreen', beginFn);
this.off('webkitendfullscreen', endFn);
});
}
/**
* 檢查當前播放設備是否支持全屏。
*
* @return {布爾值}
* - 如果支持全屏則為真。
* - 如果不支持全屏則為假。
*/
支持全屏(){
if (typeof this.el_.webkitEnterFullScreen === '函數') {
const userAgent = window.navigator && window.navigator.userAgent || '';
// 似乎在 Chromium/Chrome && Safari in Leopard 中被破壞
if ((/Android/).test(userAgent) || !(/Chrome|Mac OS X 10.5/).test(userAgent)) {
返回真;
}
}
返回假;
}
/**
* 請求 `HTML5` 技術進入全屏。
*/
進入全屏(){
const video = this.el_;
如果(video.paused && video.networkState <= video.HAVE_METADATA){
// 嘗試啟動視頻元素以進行編程訪問
// 這在桌面上不是必需的,但不應該造成傷害
silencePromise(this.el_.play());
// 在過渡到全屏期間同步播放和暫停
// 可以讓 iOS ~6.1 設備進入播放/暫停循環
this.setTimeout(函數() {
視頻.暫停();
嘗試{
video.webkitEnterFullScreen();
} 抓住 (e) {
this.trigger('全屏錯誤', e);
}
}, 0);
}其他{
嘗試{
video.webkitEnterFullScreen();
} 抓住 (e) {
this.trigger('全屏錯誤', e);
}
}
}
/**
* 請求 `HTML5` 技術退出全屏。
*/
退出全屏(){
如果(!this.el_.webkitDisplayingFullscreen){
this.trigger('fullscreenerror', new Error('視頻不是全屏'));
返回;
}
this.el_.webkitExitFullScreen();
}
/**
* 創建一個始終位於其他窗口之上的浮動視頻窗口,以便用戶可以
* 在與其他內容網站互動時繼續消費媒體,或
* 他們設備上的應用程序。
*
* @see [規範]{@link https://wicg.github.io/picture-in-picture}
*
* @return {承諾}
* 帶有畫中畫窗口的承諾。
*/
請求畫中畫(){
返回 this.el_.requestPictureInPicture();
}
/**
* 本機 requestVideoFrameCallback(如果瀏覽器/技術支持)或回退
* 播放 DRM 時不要在 Safari 上使用 rVCF,因為它不會觸發
* 需要比構造函數晚檢查
* 對於在 Fairplay 源之後加載的清晰源,這將是誤報
*
* @param {function} cb 要調用的函數
* @return {number} 請求的id
*/
requestVideoFrameCallback(cb) {
如果(this.featuresVideoFrameCallback && !this.el_.webkitKeys){
返回 this.el_.requestVideoFrameCallback(cb);
}
返回 super.requestVideoFrameCallback(cb);
}
/**
* 本機或後備 requestVideoFrameCallback
*
* @param {number} id 要取消的請求id
*/
cancelVideoFrameCallback(id) {
如果(this.featuresVideoFrameCallback && !this.el_.webkitKeys){
this.el_.cancelVideoFrameCallback(id);
}其他{
super.cancelVideoFrameCallback(id);
}
}
/**
* `Html5` Tech 源對象的 getter/setter。
* > 注意:請使用{@link Html5#setSource}
*
* @param {Tech~SourceObject} [src]
* 您要在 `HTML5` 技術元素上設置的源對象。
*
* @return {Tech~SourceObject|undefined}
* - 未傳入源時的當前源對象。
* - 設置時未定義
*
* @deprecated 從版本 5 開始。
*/
源(源){
如果(源===未定義){
返回這個.el_.src;
}
// 通過 `src` 而不是 `setSrc` 設置 src 將被棄用
this.setSrc(src);
}
/**
*通過刪除所有來源然後調用來重置技術
* {@link Html5.resetMediaElement}。
*/
重置() {
Html5.resetMediaElement(this.el_);
}
/**
* 獲取 `HTML5` 技術的當前源代碼。回退到返回來源
* HTML5 媒體元素。
*
* @return {Tech~SourceObject}
* 來自 HTML5 技術的當前源對象。回退到
* 元素來源。
*/
當前源(){
如果(this.currentSource_){
返回 this.currentSource_.src;
}
返回 this.el_.currentSrc;
}
/**
* 為 HTML5 媒體元素設置控件屬性。
*
* @param {string} 值
* 將控件屬性設置為的值
*/
設置控件(值){
this.el_.controls = !!val;
}
/**
* 創建並返回一個遠程 {@link TextTrack} 對象。
*
* @param {string} 種類
* `TextTrack` 類型(字幕、字幕、說明、章節或元數據)
*
* @param {字符串} [標籤]
* 用於識別文本軌道的標籤
*
* @param {字符串} [語言]
* 兩個字母的語言縮寫
*
* @return {TextTrack}
* 創建的 TextTrack。
*/
addTextTrack(種類,標籤,語言){
如果(!this.featuresNativeTextTracks){
返回 super.addTextTrack(種類、標籤、語言);
}
返回 this.el_.addTextTrack(種類、標籤、語言);
}
/**
* 創建本機 TextTrack 或模擬 TextTrack,具體取決於
* 關於 `featuresNativeTextTracks` 的值
*
* @param {Object} 選項
* 該對象應包含用於初始化 TextTrack 的選項。
*
* @param {string} [options.kind]
* `TextTrack` 類型(字幕、字幕、說明、章節或元數據)。
*
* @param {string} [選項.標籤]
* 用於識別文本軌道的標籤
*
* @param {string} [選項.語言]
* 兩個字母的語言縮寫。
*
* @param {boolean} [options.default]
* 默認此軌道打開。
*
* @param {string} [options.id]
* 分配此軌道的內部 ID。
*
* @param {string} [options.src]
* 軌道的源 url。
*
* @return {HTMLTrackElement}
* 創建的軌道元素。
*/
createRemoteTextTrack(選項){
如果(!this.featuresNativeTextTracks){
返回 super.createRemoteTextTrack(選項);
}
const htmlTrackElement = document.createElement('track');
如果(選項。種類){
htmlTrackElement.kind = options.kind;
}
如果(選項。標籤){
htmlTrackElement.label = options.label;
}
如果(選項。語言||選項。srclang){
htmlTrackElement.srclang = options.language ||選項.srclang;
}
如果(選項。默認){
htmlTrackElement.default = options.default;
}
如果(選項。id){
htmlTrackElement.id = options.id;
}
如果(選項.src){
htmlTrackElement.src = options.src;
}
返回 htmlTrackElement;
}
/**
* 創建一個遠程文本軌道對象並返回一個 html 軌道元素。
*
* @param {Object} options 對象應該包含值
* 種類、語言、標籤和 src(WebVTT 文件的位置)
* @param {boolean} [manualCleanup=true] 如果設置為 false,TextTrack 將是
* 每當源更改時自動從視頻元素中刪除
* @return {HTMLTrackElement} 一個 Html 軌道元素。
* 這可以是模擬的 {@link HTMLTrackElement} 或原生的。
* @deprecated “manualCleanup”參數的默認值將默認
* 在即將推出的 Video.js 版本中設置為“false”
*/
addRemoteTextTrack(選項,manualCleanup){
常量 htmlTrackElement = super.addRemoteTextTrack(選項,manualCleanup);
如果(this.featuresNativeTextTracks){
this.el().appendChild(htmlTrackElement);
}
返回 htmlTrackElement;
}
/**
* 從 TextTrackList 對像中移除遠程 TextTrack
*
* @param {TextTrack} 軌道
* 要移除的 `TextTrack` 對象
*/
removeRemoteTextTrack(軌道){
super.removeRemoteTextTrack(track);
如果(this.featuresNativeTextTracks){
const tracks = this.$$('track');
設 i = tracks.length;
當我 - ) {
如果(軌道===軌道[i] ||軌道===軌道[i].軌道){
this.el().removeChild(tracks[i]);
}
}
}
}
/**
* 獲取 W3C 媒體指定的可用媒體播放質量指標
* 播放質量 API。
*
* @see [規範]{@link https://wicg.github.io/media-playback-quality}
*
* @return {對象}
* 具有支持的媒體播放質量指標的對象
*/
getVideoPlaybackQuality() {
if (typeof this.el().getVideoPlaybackQuality === '函數') {
返回 this.el().getVideoPlaybackQuality();
}
const videoPlaybackQuality = {};
if (typeof this.el().webkitDroppedFrameCount !== 'undefined' &&
typeof this.el().webkitDecodedFrameCount !== 'undefined') {
videoPlaybackQuality.droppedVideoFrames = this.el().webkitDroppedFrameCount;
videoPlaybackQuality.totalVideoFrames = this.el().webkitDecodedFrameCount;
}
如果(window.performance && typeof window.performance.now === '函數'){
videoPlaybackQuality.creationTime = window.performance.now();
} else if (window.performance &&
window.performance.timing &&
typeof window.performance.timing.navigationStart === 'number') {
videoPlaybackQuality.creationTime =
window.Date.now() - window.performance.timing.navigationStart;
}
返回視頻播放質量;
}
}
/* HTML5 支持測試 ------------------------------------------ ------ */
/**
* 用於測試瀏覽器 HTML5 媒體功能的元素
*
* @type {元素}
* @持續的
* @私人的
*/
defineLazyProperty(Html5, 'TEST_VID', function() {
如果(!Dom.isReal()){
返回;
}
const video = document.createElement('video');
const track = document.createElement('track');
track.kind = '字幕';
track.srclang = 'en';
track.label = '英文';
video.appendChild(軌道);
返回視頻;
});
/**
* 檢查此瀏覽器/設備是否支持 HTML5 媒體。
*
* @return {布爾值}
* - 如果支持 HTML5 媒體則為真。
* - 如果不支持 HTML5 媒體則為假。
*/
Html5.isSupported = function() {
// 沒有媒體播放器的 IE 是騙子! (#984)
嘗試{
html5.TEST_VID.volume = 0.5;
} 抓住 (e) {
返回假;
}
返回!!(Html5.TEST_VID && Html5.TEST_VID.canPlayType);
};
/**
* 檢查技術是否可以支持給定的類型
*
* @param {string} 類型
* 要檢查的 mimetype
* @return {string} 'probably', 'maybe', or '' (空字符串)
*/
Html5.canPlayType = 函數(類型){
返回 Html5.TEST_VID.canPlayType(類型);
};
/**
* 檢查技術是否可以支持給定的來源
*
* @param {對象} srcObj
* 源對象
* @param {Object} 選項
* 傳遞給技術的選項
* @return {string} 'probably', 'maybe', or '' (空字符串)
*/
Html5.canPlaySource = 函數(srcObj,選項){
返回 Html5.canPlayType(srcObj.type);
};
/**
* 檢查是否可以在此瀏覽器/設備中更改音量。
* 很多移動設備無法更改音量。
* 具體而言,在 iOS 上無法從 1 更改。
*
* @return {布爾值}
* - 如果可以控制音量則為真
* - 否則為假
*/
Html5.canControlVolume = function() {
// 如果未安裝 Windows Media Player,IE 將出錯 #3315
嘗試{
const volume = Html5.TEST_VID.volume;
html5.TEST_VID.volume = (volume / 2) + 0.1;
const canControl = volume !== Html5.TEST_VID.volume;
// 隨著 iOS 15 的引入,出現了將音量讀取為
// 已更改但會在下一個滴答開始時恢復到原始狀態。
//判斷音量是否可以在iOS上控制,
// 設置超時並異步檢查音量。
// 由於 `features` 當前不異步工作,因此該值是手動設置的。
如果(canControl && browser.IS_IOS){
window.setTimeout(() => {
如果(Html5 && Html5.prototype){
Html5.prototype.featuresVolumeControl = volume !== Html5.TEST_VID.volume;
}
});
// 默認 iOS 為 false,將在上面的超時時間更新。
返回假;
}
返回可以控制;
} 抓住 (e) {
返回假;
}
};
/**
* 檢查是否可以在此瀏覽器/設備中將音量靜音。
* 某些設備,例如 iOS,不允許改變音量
* 但允許靜音/取消靜音。
*
* @return {布爾值}
* - 如果音量可以靜音則為真
* - 否則為假
*/
Html5.canMuteVolume = function() {
嘗試{
const muted = Html5.TEST_VID.muted;
// 在某些版本的 iOS 中,muted 屬性並不總是
// 工作,所以我們想同時設置屬性和屬性
Html5.TEST_VID.muted = !muted;
如果 (Html5.TEST_VID.muted) {
Dom.setAttribute(Html5.TEST_VID, 'muted', 'muted');
}其他{
Dom.removeAttribute(Html5.TEST_VID, 'muted', 'muted');
}
返回靜音 !== Html5.TEST_VID.muted;
} 抓住 (e) {
返回假;
}
};
/**
* 檢查是否可以在此瀏覽器/設備中更改播放速率。
*
* @return {布爾值}
* - 如果可以控製播放速率則為真
* - 否則為假
*/
Html5.canControlPlaybackRate = function() {
// 播放速率 API 在 Android Chrome 中實現,但不做任何事情
// https://github.com/videojs/video.js/issues/3180
如果(瀏覽器.IS_ANDROID && 瀏覽器.IS_CHROME && 瀏覽器.CHROME_VERSION < 58){
返回假;
}
// 如果未安裝 Windows Media Player,IE 將出錯 #3315
嘗試{
const playbackRate = Html5.TEST_VID.playbackRate;
Html5.TEST_VID.playbackRate = (playbackRate / 2) + 0.1;
返回 playbackRate !== Html5.TEST_VID.playbackRate;
} 抓住 (e) {
返回假;
}
};
/**
* 檢查我們是否可以覆蓋視頻/音頻元素的屬性,
* 對象.defineProperty。
*
* @return {布爾值}
* - 如果可以覆蓋內置屬性則為真
* - 否則為假
*/
Html5.canOverrideAttributes = function() {
// 如果我們不能覆蓋 src/innerHTML 屬性,則不支持
// 例如 iOS 7 safari 不能這樣做。
嘗試{
const noop = () => {};
Object.defineProperty(document.createElement('video'), 'src', {get: noop, set: noop});
Object.defineProperty(document.createElement('audio'), 'src', {get: noop, set: noop});
Object.defineProperty(document.createElement('video'), 'innerHTML', {get: noop, set: noop});
Object.defineProperty(document.createElement('audio'), 'innerHTML', {get: noop, set: noop});
} 抓住 (e) {
返回假;
}
返回真;
};
/**
* 檢查此瀏覽器/設備是否支持本機 `TextTrack`。
*
* @return {布爾值}
* - 如果支持原生 `TextTrack` 則為真。
* - 否則為假
*/
Html5.supportsNativeTextTracks = function() {
返回 browser.IS_ANY_SAFARI || (瀏覽器.IS_IOS && 瀏覽器.IS_CHROME);
};
/**
* 檢查此瀏覽器/設備是否支持本機 `VideoTrack`
*
* @return {布爾值}
* - 如果支持原生 `VideoTrack` 則為真。
* - 否則為假
*/
Html5.supportsNativeVideoTracks = function() {
返回 !!(Html5.TEST_VID && Html5.TEST_VID.videoTracks);
};
/**
* 檢查此瀏覽器/設備是否支持本機 `AudioTrack`
*
* @return {布爾值}
* - 如果支持原生 `AudioTrack` 則為真。
* - 否則為假
*/
Html5.supportsNativeAudioTracks = function() {
返回!!(Html5.TEST_VID && Html5.TEST_VID.audioTracks);
};
/**
* Html5 技術上可用的一系列事件。
*
* @私人的
* @type {數組}
*/
Html5.事件 = [
'加載開始',
'暫停',
'中止',
'錯誤',
'清空',
'停滯不前',
'加載元數據',
'加載數據',
'可以玩',
'可以通過',
'玩',
'等待',
'尋求',
'尋求',
'結束',
'持續時間變化',
'時間更新',
'進步',
'玩',
'暫停',
'匯率變化',
'調整大小',
'體積變化'
];
/**
* 布爾值表示 `Tech` 是否支持音量控制。
*
* @type {布爾}
* @default {@link Html5.canControlVolume}
*/
/**
* 表示 `Tech` 是否支持靜音音量的布爾值。
*
* @type {布爾值}
* @default {@link Html5.canMuteVolume}
*/
/**
* 表示 `Tech` 是否支持改變媒體播放速度的布爾值
* 播放。範例:
* - 設置播放器以 2 倍(兩倍)的速度播放
* - 設置播放器播放速度為 0.5 倍(一半)
*
* @type {布爾}
* @default {@link Html5.canControlPlaybackRate}
*/
/**
* 表示 `Tech` 是否支持 `sourceset` 事件的布爾值。
*
* @type {布爾}
* @默認
*/
/**
* 表示 `HTML5` 技術當前是否支持原生 `TextTrack` 的布爾值。
*
* @type {布爾}
* @default {@link Html5.supportsNativeTextTracks}
*/
/**
* 表示 `HTML5` 技術當前是否支持原生 `VideoTrack` 的布爾值。
*
* @type {布爾}
* @default {@link Html5.supportsNativeVideoTracks}
*/
/**
* 表示 `HTML5` 技術當前是否支持原生 `AudioTrack` 的布爾值。
*
* @type {布爾}
* @default {@link Html5.supportsNativeAudioTracks}
*/
[
['featuresMuteControl', 'canMuteVolume'],
['featuresPlaybackRate', 'canControlPlaybackRate'],
['featuresSourceset', 'canOverrideAttributes'],
['featuresNativeTextTracks', 'supportsNativeTextTracks'],
['featuresNativeVideoTracks', 'supportsNativeVideoTracks'],
['featuresNativeAudioTracks', 'supportsNativeAudioTracks']
].forEach(函數([key, fn]) {
defineLazyProperty(Html5.prototype, key, () => Html5[fn](), true);
});
Html5.prototype.featuresVolumeControl = Html5.canControlVolume();
/**
* 布爾值,指示 `HTML5` 技術當前是否支持媒體元素
* 在 DOM 中移動。如果移動媒體元素,iOS 會中斷,因此將其設置為
* 假的。這在其他任何地方都應該是正確的。
*
* @type {布爾}
* @默認
*/
Html5.prototype.movingMediaElementInDOM = !browser.IS_IOS;
// 去做:上一條評論:似乎不再使用。大概可以去掉。
// 這是真的?
/**
* 表示 `HTML5` 技術當前是否支持自動調整媒體大小的布爾值
* 進入全屏時。
*
* @type {布爾}
* @默認
*/
Html5.prototype.featuresFullscreenResize = true;
/**
* 表示 `HTML5` 技術當前是否支持進度事件的布爾值。
* 如果這是 false,將改為觸發手動“progress”事件。
*
* @type {布爾}
* @默認
*/
Html5.prototype.featuresProgressEvents = true;
/**
* 表示 `HTML5` 技術當前是否支持 timeupdate 事件的布爾值。
* 如果這是 false,將改為觸發手動 `timeupdate` 事件。
*
* @默認
*/
Html5.prototype.featuresTimeupdateEvents = true;
/**
* HTML5 el是否支持`requestVideoFrameCallback`
*
* @type {布爾}
*/
Html5.prototype.featuresVideoFrameCallback = !!(Html5.TEST_VID && Html5.TEST_VID.requestVideoFrameCallback);
// HTML5 功能檢測和設備修復 ---------------------------------- //
讓 canPlayType;
Html5.patchCanPlayType = function() {
// Android 4.0及以上版本可以一定程度播放HLS但報無法播放
// Firefox 和 Chrome 正確報告
如果(瀏覽器.ANDROID_VERSION >= 4.0 && !browser.IS_FIREFOX && !browser.IS_CHROME){
canPlayType = Html5.TEST_VID && Html5.TEST_VID.constructor.prototype.canPlayType;
Html5.TEST_VID.constructor.prototype.canPlayType = 函數(類型){
const mpegurlRE = /^application\\/(?:x-|vnd\\.apple\\.)mpegurl/i;
如果(類型 && mpegurlRE.test(類型)){
返回“也許”;
}
返回 canPlayType.call(this, type);
};
}
};
Html5.unpatchCanPlayType = function() {
const r = Html5.TEST_VID.constructor.prototype.canPlayType;
如果(canPlayType){
Html5.TEST_VID.constructor.prototype.canPlayType = canPlayType;
}
返回 r;
};
// 默認情況下,修補媒體元素
Html5.patchCanPlayType();
Html5.disposeMediaElement = function(el) {
如果(!el){
返回;
}
如果(el.parentNode){
el.parentNode.removeChild(el);
}
// 刪除任何子軌道或源節點以防止加載它們
while (el.hasChildNodes()) {
el.removeChild(el.firstChild);
}
// 刪除任何 src 引用。不設置 `src=''` 因為這會導致警告
// 在火狐中
el.removeAttribute('src');
// 通過調用 load() 強制媒體元素更新其加載狀態
// 但是 Windows 7N 上的 IE 有一個會引發錯誤的錯誤,因此需要一個 try/catch (#793)
如果 (typeof el.load === '函數') {
// 包裝在一個 iife 中,這樣它就不會被取消優化 (#1060#discussion_r10324473)
(功能() {
嘗試{
el.load();
} 抓住 (e) {
// 不支持
}
}());
}
};
Html5.resetMediaElement = function(el) {
如果(!el){
返回;
}
const sources = el.querySelectorAll('source');
讓我=來源.長度;
當我 - ) {
el.removeChild(來源[i]);
}
// 刪除任何 src 引用。
// 不設置 `src=''` 因為那會拋出錯誤
el.removeAttribute('src');
如果 (typeof el.load === '函數') {
// 包裝在一個 iife 中,這樣它就不會被取消優化 (#1060#discussion_r10324473)
(功能() {
嘗試{
el.load();
} 抓住 (e) {
// 滿足 linter
}
}());
}
};
/* 原生 HTML5 元素屬性包裝 ---------------------------------- */
// 使用同時檢查屬性和屬性的 getter 包裝本機布爾屬性
// 列表如下:
// 靜音,默認靜音,自動播放,控制,循環,在線播放
[
/**
* 從媒體元素中獲取 `muted` 的值。 `muted` 表示
* 媒體音量應設置為靜音。這實際上並沒有改變
* `volume` 屬性。
*
* @method Html5#靜音
* @return {布爾值}
* - 如果應忽略 `volume` 的值並將音頻設置為靜音,則為 True。
* - 如果應該使用 `volume` 的值則為 false。
*
* @see [規範]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-muted}
*/
'靜音',
/**
* 從媒體元素中獲取 `defaultMuted` 的值。 `defaultMuted` 表示
*媒體是否應該開始靜音。只改變默認狀態
* 媒體。 `muted` 和 `defaultMuted` 可以有不同的值。 {@link Html5#muted} 表示
* 當前狀態。
*
* @method Html5#defaultMuted
* @return {布爾值}
* - 媒體元素中“defaultMuted”的值。
* - True 表示媒體應該開始靜音。
* - False 表示媒體不應該開始靜音
*
* @see [規範]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-defaultmuted}
*/
'默認靜音',
/**
* 從媒體元素中獲取 `autoplay` 的值。 `自動播放`表示
* 媒體應該在頁面準備好後立即開始播放。
*
* @method Html5#自動播放
* @return {布爾值}
* - 媒體元素中“autoplay”的值。
* - True 表示媒體應在頁面加載後立即啟動。
* - False 表示媒體不應在頁面加載後立即啟動。
*
* @see [規範]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-autoplay}
*/
'自動播放',
/**
* 從媒體元素中獲取 `controls` 的值。 `controls` 表示
* 本機媒體控件是否應顯示或隱藏。
*
* @method Html5#控件
* @return {布爾值}
* - 媒體元素中“controls”的值。
* - True 表示應顯示本機控件。
* - False 表示應隱藏本機控件。
*
* @see [規範]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-controls}
*/
“控制”,
/**
* 從媒體元素中獲取 `loop` 的值。 `loop` 表示
*媒體應該回到媒體的開始並繼續播放一次
* 它到達終點。
*
* @method Html5#循環
* @return {布爾值}
* - 媒體元素中 `loop` 的值。
* - True 表示播放應該重新開始一次
* 到達媒體的結尾。
* - False 表示當
* 到達媒體末尾。
*
* @see [規範]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-loop}
*/
'環形',
/**
* 從媒體元素中獲取 `playsinline` 的值。 `playsinline` 表示
* 瀏覽器在全屏時首選非全屏播放
* 播放是本機默認設置,例如在 iOS Safari 中。
*
* @method Html5#playsinline
* @return {布爾值}
* - 媒體元素中 `playsinline` 的值。
* - True 表示媒體應該內聯播放。
* - False 表示媒體不應內聯播放。
*
* @see [規範]{@link https://html.spec.whatwg.org/#attr-video-playsinline}
*/
'在線播放'
].forEach(函數(道具){
Html5.prototype[prop] = function() {
返回 this.el_[prop] || this.el_.hasAttribute(prop);
};
});
// 使用設置屬性和屬性的設置器包裝本機布爾屬性
// 列表如下:
// setMuted, setDefaultMuted, setAutoplay, setLoop, setPlaysinline
// setControls 是上面的特例
[
/**
* 在媒體元素上設置 `muted` 的值。 `muted` 表示當前
* 音量應該是無聲的。
*
* @method Html5#setMuted
* @param {boolean} 靜音
* - 如果音頻應設置為靜音則為真
* - 否則為假
*
* @see [規範]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-muted}
*/
'靜音',
/**
* 在媒體元素上設置 `defaultMuted` 的值。 `defaultMuted` 表示當前
* 音頻級別應該是無聲的,但只會影響初始播放時的靜音級別。
*
* @method Html5.prototype.setDefaultMuted
* @param {boolean} defaultMuted
* - 如果音頻應設置為靜音則為真
* - 否則為假
*
* @see [規範]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-defaultmuted}
*/
'默認靜音',
/**
* 在媒體元素上設置 `autoplay` 的值。 `自動播放`表示
* 媒體應該在頁面準備好後立即開始播放。
*
* @method Html5#setAutoplay
* @param {boolean} 自動播放
* - True 表示媒體應在頁面加載後立即啟動。
* - False 表示媒體不應在頁面加載後立即啟動。
*
* @see [規範]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-autoplay}
*/
'自動播放',
/**
* 在媒體元素上設置 `loop` 的值。 `loop` 表示
*媒體應該回到媒體的開始並繼續播放一次
* 它到達終點。
*
* @method Html5#setLoop
* @param {boolean} 循環
* - True 表示播放應該重新開始一次
* 到達媒體的結尾。
* - False 表示當
* 到達媒體末尾。
*
* @see [規範]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-loop}
*/
'環形',
/**
* 從媒體元素中設置 `playsinline` 的值。 `playsinline` 表示
* 瀏覽器在全屏時首選非全屏播放
* 播放是本機默認設置,例如在 iOS Safari 中。
*
* @method Html5#setPlaysinline
* @param {boolean} 在線播放
* - True 表示媒體應該內聯播放。
* - False 表示媒體不應內聯播放。
*
* @see [規範]{@link https://html.spec.whatwg.org/#attr-video-playsinline}
*/
'在線播放'
].forEach(函數(道具){
Html5.prototype['set' + toTitleCase(prop)] = function(v) {
this.el_[prop] = v;
如果(五){
this.el_.setAttribute(prop, prop);
}其他{
this.el_.removeAttribute(prop);
}
};
});
// 用 getter 包裝原生屬性
// 列表如下
// 暫停、當前時間、緩衝、音量、海報、預加載、錯誤、搜索
// seekable, ended, playbackRate, defaultPlaybackRate, disablePictureInPicture
// played, networkState, readyState, videoWidth, videoHeight, crossOrigin
[
/**
* 從媒體元素中獲取 `paused` 的值。 `paused` 表示媒體元素是否
* 當前是否暫停。
*
* @method Html5#暫停
* @return {布爾值}
* 媒體元素中 `paused` 的值。
*
* @see [規範]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-paused}
*/
'暫停',
/**
* 從媒體元素中獲取 `currentTime` 的值。 `currentTime` 表示
* 媒體播放的當前秒數。
*
* @method Html5#currentTime
* @return {數字}
* 來自媒體元素的 `currentTime` 的值。
*
* @see [規範]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-currenttime}
*/
'當前時間',
/**
* 從媒體元素中獲取 `buffered` 的值。 `buffered` 是一個 `TimeRange`
* 表示已經下載的媒體部分的對象
* 可用於播放。
*
* @method Html5#buffered
* @return {TimeRange}
* 來自媒體元素的 `buffered` 的值。
*
* @see [規範]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-buffered}
*/
'緩衝的',
/**
* 從媒體元素中獲取 `volume` 的值。 `volume`表示
* 媒體音頻的當前播放音量。 `volume` 將是一個從 0 開始的值
*(靜音)到 1(最大聲和默認)。
*
* @method Html5#volume
* @return {數字}
* 來自媒體元素的 `volume` 的值。值將在 0-1 之間。
*
* @see [規範]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-a-volume}
*/
'體積',
/**
* 從媒體元素中獲取 `poster` 的值。 `海報`表示
* 當沒有媒體數據可用時可以/將顯示的圖像文件的 url。
*
* @method Html5#海報
* @return {字符串}
* 來自媒體元素的 `poster` 的值。值將是一個 url
* 圖像。
*
* @see [規範]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-video-poster}
*/
'海報',
/**
* 從媒體元素中獲取 `preload` 的值。 `preload` 表示
* 在與媒體交互之前應該下載什麼。它可以有以下內容
* 值:
* - 無:不應下載任何內容
* - 元數據:海報和媒體的前幾幀可以下載以獲得
* 媒體維度和其他元數據
* - 自動:允許媒體和媒體的元數據在之前下載
* 相互作用
*
* @method Html5#預加載
* @return {字符串}
* 來自媒體元素的 `preload` 的值。將是“無”,“元數據”,
* 或“自動”。
*
* @see [規範]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-preload}
*/
'預加載',
/**
* 從媒體元素中獲取 `error` 的值。 `error` 表示任何
* 播放過程中可能發生的 MediaError。如果錯誤返回 null 則沒有
* 當前錯誤。
*
* @method Html5#錯誤
* @return {MediaError|null}
* 來自媒體元素的 `error` 的值。如果存在,將是 MediaError
* 是當前錯誤,否則為空。
*
* @see [規範]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-error}
*/
'錯誤',
/**
* 從媒體元素中獲取 `seeking` 的值。 `seeking` 表示是否
* 媒體目前正在尋求一個新的位置與否。
*
* @method Html5#seeking
* @return {布爾值}
* - 從媒體元素中尋找的值。
* - True 表示媒體當前正在尋找新位置。
* - False 表示媒體目前沒有尋求新的立場。
*
* @see [規範]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-seeking}
*/
'尋求',
/**
* 從媒體元素中獲取 `seekable` 的值。 `seekable` 返回一個
* `TimeRange` 對象指示當前可以“搜索”到的時間範圍。
*
* @method Html5#seekable
* @return {TimeRange}
* media 元素中 `seekable` 的值。一個 TimeRange 對象
* 指示當前可以搜索到的時間範圍。
*
* @see [規範]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-seekable}
*/
'可尋',
/**
* 從媒體元素中獲取 `ended` 的值。 `ended` 表示是否
* 媒體是否到達盡頭。
*
* @method Html5#ended
* @return {布爾值}
* - 來自媒體元素的 `ended` 的值。
* - True 表示媒體已經結束。
* - False 表示媒體尚未結束。
*
* @see [規範]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-ended}
*/
'結束',
/**
* 從媒體元素中獲取 `playbackRate` 的值。 `playbackRate` 表示
* 媒體當前播放的速率。範例:
* - 如果 playbackRate 設置為 2,媒體將以兩倍的速度播放。
* - 如果 playbackRate 設置為 0.5,媒體播放速度將減半。
*
* @method Html5#playbackRate
* @return {數字}
* 媒體元素中 `playbackRate` 的值。一個數字表示
* 媒體的當前播放速度,其中 1 是正常速度。
*
* @see [規範]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-playbackrate}
*/
'播放率',
/**
* 從媒體元素中獲取 `defaultPlaybackRate` 的值。 `defaultPlaybackRate` 表示
* 媒體當前播放的速率。該值將不表示當前
* 播放開始後的 `playbackRate`,為此使用 {@link Html5#playbackRate}。
*
* 例子:
* - 如果 defaultPlaybackRate 設置為 2,媒體將以兩倍的速度播放。
* - 如果 defaultPlaybackRate 設置為 0.5,媒體播放速度將減半。
*
* @method Html5.prototype.defaultPlaybackRate
* @return {數字}
* 來自媒體元素的 `defaultPlaybackRate` 的值。一個數字表示
* 媒體的當前播放速度,其中 1 是正常速度。
*
* @see [規範]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-playbackrate}
*/
'默認播放率',
/**
* 從視頻元素中獲取 'disablePictureInPicture' 的值。
*
* @method Html5#disablePictureInPicture
* @return {boolean} 值
* - video 元素中 `disablePictureInPicture` 的值。
* - True 表示視頻無法以畫中畫模式播放
* - false 表示視頻可以畫中畫模式播放
*
* @see [規範]{@link https://w3c.github.io/picture-in-picture/#disable-pip}
*/
'禁用畫中畫',
/**
* 從媒體元素中獲取 `played` 的值。 `played` 返回一個 `TimeRange`
* 表示媒體時間軸中已播放點的對象。
*
* @method Html5#播放
* @return {TimeRange}
* 媒體元素中“played”的值。一個 TimeRange 對象表示
* 已播放的時間範圍。
*
* @see [規範]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-played}
*/
'播放',
/**
* 從媒體元素中獲取 `networkState` 的值。 `networkState` 表示
* 當前網絡狀態。它從以下列表返回一個枚舉:
* - 0:NETWORK_EMPTY
* - 1:網絡空閒
* - 2:網絡加載
* - 3:NETWORK_NO_SOURCE
*
* @method Html5#networkState
* @return {數字}
* 來自媒體元素的 `networkState` 的值。這將是一個數字
* 來自描述中的列表。
*
* @see [規範] {@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-networkstate}
*/
'網絡狀態',
/**
* 從媒體元素中獲取 readyState 的值。 `readyState` 表示
* 媒體元素的當前狀態。它返回一個枚舉
* 以下列表:
* - 0:一無所有
* - 1:有_元數據
* - 2:有_CURRENT_DATA
* - 3:有_FUTURE_DATA
* - 4:有_ENOUGH_DATA
*
* @method Html5#readyState
* @return {數字}
* 來自媒體元素的 `readyState` 的值。這將是一個數字
* 來自描述中的列表。
*
* @see [規範] {@link https://www.w3.org/TR/html5/embedded-content-0.html#ready-states}
*/
'就緒狀態',
/**
* 從視頻元素中獲取 `videoWidth` 的值。 `videoWidth`表示
* 視頻的當前寬度(以 css 像素為單位)。
*
* @method Html5#videoWidth
* @return {數字}
* 來自視頻元素的 `videoWidth` 的值。這將是一個數字
* 以 css 像素為單位。
*
* @see [規範] {@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-video-videowidth}
*/
'視頻寬度',
/**
* 從視頻元素中獲取 `videoHeight` 的值。 `videoHeight`表示
* 視頻的當前高度(以 css 像素為單位)。
*
* @method Html5#videoHeight
* @return {數字}
* video 元素中 `videoHeight` 的值。這將是一個數字
* 以 css 像素為單位。
*
* @see [規範] {@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-video-videowidth}
*/
'視頻高度',
/**
* 從媒體元素中獲取 `crossOrigin` 的值。 `crossOrigin` 表示
* 發送給應該連同請求一起發送 cookie 的瀏覽器
* 不同的資產/播放列表
*
* @method Html5#crossOrigin
* @return {字符串}
* - 匿名表示媒體不應發送 cookie。
* - use-credentials 表示媒體應隨請求發送 cookie。
*
* @see [規範]{@link https://html.spec.whatwg.org/#attr-media-crossorigin}
*/
'跨域'
].forEach(函數(道具){
Html5.prototype[prop] = function() {
返回 this.el_[prop];
};
});
// 以此格式使用 setter 包裝本機屬性:
// 設置 + toTitleCase(名稱)
// 列表如下:
// setVolume, setSrc, setPoster, setPreload, setPlaybackRate, setDefaultPlaybackRate,
// setDisablePictureInPicture, setCrossOrigin
[
/**
* 在媒體元素上設置 `volume` 的值。 `volume`表示當前
* 以小數形式表示的音頻電平百分比。這意味著 1 是 100%,0.5 是 50%,而
* 很快。
*
* @method Html5#setVolume
* @param {number} percentAsDecimal
* 小數形式的體積百分比。有效範圍為 0-1。
*
* @see [規範]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-a-volume}
*/
'體積',
/**
* 在媒體元素上設置 `src` 的值。 `src`表示當前
* {@link Tech~SourceObject} 媒體。
*
* @method Html5#setSrc
* @param {Tech~SourceObject} 源
* 要設置為當前源的源對象。
*
* @see [規範]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-src}
*/
'來源',
/**
* 在媒體元素上設置 `poster` 的值。 `poster` 是網址
* 當沒有媒體數據可用時可以/將顯示的圖像文件。
*
* @method Html5#setPoster
* @param {string} 海報
* 應該用作媒體“海報”的圖像的 url
* 元素。
*
* @see [規範]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-poster}
*/
'海報',
/**
* 在媒體元素上設置 `preload` 的值。 `preload` 表示
* 在與媒體交互之前應該下載什麼。它可以有以下內容
* 值:
* - 無:不應下載任何內容
* - 元數據:海報和媒體的前幾幀可以下載以獲得
* 媒體維度和其他元數據
* - 自動:允許媒體和媒體的元數據在之前下載
* 相互作用
*
* @method Html5#setPreload
* @param {string} 預加載
* 在媒體元素上設置的 `preload` 的值。必須是“無”,“元數據”,
* 或“自動”。
*
* @see [規範]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-preload}
*/
'預加載',
/**
* 在媒體元素上設置 `playbackRate` 的值。 `playbackRate` 表示
* 媒體播放的速率。範例:
* - 如果 playbackRate 設置為 2,媒體將以兩倍的速度播放。
* - 如果 playbackRate 設置為 0.5,媒體播放速度將減半。
*
* @method Html5#setPlaybackRate
* @return {數字}
* 媒體元素中 `playbackRate` 的值。一個數字表示
* 媒體的當前播放速度,其中 1 是正常速度。
*
* @see [規範]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-playbackrate}
*/
'播放率',
/**
* 在媒體元素上設置 `defaultPlaybackRate` 的值。 `defaultPlaybackRate` 表示
* 媒體在初始啟動時應播放的速率。改變這個值
* 視頻開始後將不執行任何操作。相反,您應該使用 {@link Html5#setPlaybackRate}。
*
* 示例值:
* - 如果 playbackRate 設置為 2,媒體將以兩倍的速度播放。
* - 如果 playbackRate 設置為 0.5,媒體播放速度將減半。
*
* @method Html5.prototype.setDefaultPlaybackRate
* @return {數字}
* 來自媒體元素的 `defaultPlaybackRate` 的值。一個數字表示
* 媒體的當前播放速度,其中 1 是正常速度。
*
* @see [規範]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-defaultplaybackrate}
*/
'默認播放率',
/**
* 防止瀏覽器建議畫中畫上下文菜單
* 或在某些情況下自動請求畫中畫。
*
* @method Html5#setDisablePictureInPicture
* @param {boolean} 值
* 真值將禁用畫中畫模式。
*
* @see [規範]{@link https://w3c.github.io/picture-in-picture/#disable-pip}
*/
'禁用畫中畫',
/**
* 從媒體元素設置 `crossOrigin` 的值。 `crossOrigin` 表示
* 發送給應該連同請求一起發送 cookie 的瀏覽器
* 不同的資產/播放列表
*
* @method Html5#setCrossOrigin
* @param {string} 跨域
* - 匿名表示媒體不應發送 cookie。
* - use-credentials 表示媒體應隨請求發送 cookie。
*
* @see [規範]{@link https://html.spec.whatwg.org/#attr-media-crossorigin}
*/
'跨域'
].forEach(函數(道具){
Html5.prototype['set' + toTitleCase(prop)] = function(v) {
this.el_[prop] = v;
};
});
// 用函數包裝原生函數
// 列表如下:
// 暫停、加載、播放
[
/**
* 媒體元素“暫停”功能的包裝器。這將調用 `HTML5`
* 媒體元素`暫停`功能。
*
* @method Html5#暫停
* @see [規範]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-pause}
*/
'暫停',
/**
* 媒體元素 `load` 函數的包裝器。這將調用`HTML5`s
* 媒體元素 `load` 函數。
*
* @method Html5#load
* @see [規範]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-load}
*/
'加載',
/**
* 媒體元素“播放”功能的包裝器。這將調用`HTML5`s
* 媒體元素“播放”功能。
*
* @method Html5#播放
* @see [規範]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-play}
*/
'玩'
].forEach(函數(道具){
Html5.prototype[prop] = function() {
返回 this.el_[prop]();
};
});
Tech.withSourceHandlers(Html5);
/**
* Html5 的本機源處理程序,只需將源傳遞給媒體元素。
*
* @property {Tech~SourceObject} 來源
* 源對象
*
* @property {Html5} 技術
* HTML5 技術實例。
*/
Html5.nativeSourceHandler = {};
/**
* 檢查媒體元素是否可以播放給定的 MIME 類型。
*
* @param {string} 類型
* 要檢查的 mimetype
*
* @return {字符串}
*“可能”、“也許”或“”(空字符串)
*/
Html5.nativeSourceHandler.canPlayType = 函數(類型){
// 沒有 MediaPlayer 的 IE 會拋出錯誤 (#519)
嘗試{
返回 Html5.TEST_VID.canPlayType(類型);
} 抓住 (e) {
返回 '';
}
};
/**
* 檢查媒體元素是否可以本地處理源。
*
* @param {Tech~SourceObject} 來源
* 源對象
*
* @param {對象} [選項]
* 傳遞給技術的選項。
*
* @return {字符串}
*“可能”、“也許”或“”(空字符串)。
*/
Html5.nativeSourceHandler.canHandleSource = 函數(來源,選項){
// 如果提供了類型,我們應該依賴它
如果(來源。類型){
返回 Html5.nativeSourceHandler.canPlayType(source.type);
// 如果沒有類型,回退到檢查 'video/[EXTENSION]'
} else if (source.src) {
const ext = Url.getFileExtension(source.src);
返回 Html5.nativeSourceHandler.canPlayType(`video/${ext}`);
}
返回 '';
};
/**
* 將源傳遞給原生媒體元素。
*
* @param {Tech~SourceObject} 來源
* 源對象
*
* @param {Html5} 技術
* Html5技術實例
*
* @param {對象} [選項]
* 傳遞給源的選項
*/
Html5.nativeSourceHandler.handleSource = function(source, tech, options) {
tech.setSrc(source.src);
};
/**
* 本機處理函數的 noop,因為不需要清理。
*/
Html5.nativeSourceHandler.dispose = function() {};
// 註冊本機源處理程序
Html5.registerSourceHandler(Html5.nativeSourceHandler);
Tech.registerTech('Html5', Html5);
導出默認的 Html5;