/**
* @파일 tech.js
*/
'../component'에서 컴포넌트 가져오기;
'../utils/merge-options.js'에서 mergeOptions 가져오기;
import * as Fn from '../utils/fn.js';
'../utils/log.js'에서 로그 가져오기;
import { createTimeRange } from '../utils/time-ranges.js';
import { bufferedPercent } from '../utils/buffer.js';
'../media-error.js'에서 MediaError 가져오기;
'글로벌/창'에서 창 가져오기;
'글로벌/문서'에서 문서 가져오기;
'../utils/obj'에서 {isPlain} 가져오기;
import * as TRACK_TYPES from '../tracks/track-types';
'../utils/string-cases.js'에서 {toTitleCase, toLowerCase} 가져오기;
'videojs-vtt.js'에서 vtt 가져오기;
import * as Guid from '../utils/guid.js';
/**
* `{src: 'url', type: 'mimetype'}` 또는 문자열과 같은 구조를 포함하는 객체
* src url만 포함하는 것입니다.
* * `var SourceObject = {src: 'http://ex.com/video.mp4', type: 'video/mp4'};`
* `var SourceString = 'http://example.com/some-video.mp4';`
*
* @typedef {객체|문자열} Tech~SourceObject
*
* @property {문자열} src
* 소스 URL
*
* @property {문자열} 유형
* 소스의 MIME 유형
*/
/**
* 새로운 {@link TextTrack}을 생성하기 위해 {@link Tech}에서 사용하는 기능.
*
* @사적인
*
* @param {기술} 자신
* Tech 클래스의 인스턴스입니다.
*
* @param {문자열} 종류
* `TextTrack` 종류(자막, 캡션, 설명, 챕터 또는 메타데이터)
*
* @param {문자열} [레이블]
* 텍스트 트랙을 식별하는 레이블
*
* @param {문자열} [언어]
* 두 글자 언어 약어
*
* @param {객체} [옵션={}]
* 추가 텍스트 추적 옵션이 있는 개체
*
* @return {텍스트 트랙}
* 생성된 텍스트 트랙.
*/
함수 createTrackHelper(self, kind, label, language, options = {}) {
const 트랙 = self.textTracks();
options.kind = 종류;
if(레이블) {
options.label = 라벨;
}
if (언어) {
options.language = 언어;
}
options.tech = 자기;
const track = new TRACK_TYPES.ALL.text.TrackClass(options);
tracks.addTrack(트랙);
리턴 트랙;
}
/**
* 이것은 다음과 같은 미디어 재생 기술 컨트롤러의 기본 클래스입니다.
* {@링크 HTML5}
*
* @extends 컴포넌트
*/
클래스 기술 확장 구성 요소 {
/**
* 이 기술의 인스턴스를 만듭니다.
*
* @param {객체} [옵션]
* 플레이어 옵션의 키/값 저장소.
*
* @param {Component~ReadyCallback} 준비 완료
* `HTML5` Tech가 준비되면 호출하는 콜백 함수.
*/
생성자(옵션 = {}, 준비 = function() {}) {
// 우리는 기술자가 사용자 활동을 자동으로 보고하는 것을 원하지 않습니다.
// 이것은 addControlsListeners에서 수동으로 수행됩니다.
options.reportTouchActivity = 거짓;
슈퍼(널, 옵션, 준비);
this.onDurationChange_ = (e) => this.onDurationChange(e);
this.trackProgress_ = (e) => this.trackProgress(e);
this.trackCurrentTime_ = (e) => this.trackCurrentTime(e);
this.stopTrackingCurrentTime_ = (e) => this.stopTrackingCurrentTime(e);
this.disposeSourceHandler_ = (e) => this.disposeSourceHandler(e);
this.queuedHanders_ = new Set();
// 현재 소스가 재생되었는지 여부를 추적합니다.
// 매우 제한된 playing() 구현
this.hasStarted_ = 거짓;
this.on('재생 중', function() {
this.hasStarted_ = 참;
});
this.on('loadstart', function() {
this.hasStarted_ = 거짓;
});
TRACK_TYPES.ALL.names.forEach((이름) => {
const props = TRACK_TYPES.ALL[이름];
if (옵션 && 옵션[props.getterName]) {
this[props.privateName] = 옵션[props.getterName];
}
});
// 브라우저/기술자가 보고하지 않는 경우 진행 상황을 수동으로 추적합니다.
if (!this.featuresProgressEvents) {
this.manualProgressOn();
}
// 브라우저/기술자가 보고하지 않는 경우 시간 업데이트를 수동으로 추적합니다.
if (!this.featuresTimeupdateEvents) {
this.manualTimeUpdatesOn();
}
['텍스트', '오디오', '비디오'].forEach((트랙) => {
if (옵션[`네이티브${트랙}트랙`] === 거짓) {
this[`featuresNative${track}트랙`] = 거짓;
}
});
if (options.nativeCaptions === false || options.nativeTextTracks === false) {
this.featuresNativeTextTracks = 거짓;
} 그렇지 않으면 (options.nativeCaptions === 참 || options.nativeTextTracks === 참) {
this.featuresNativeTextTracks = 참;
}
if (!this.featuresNativeTextTracks) {
this.emulateTextTracks();
}
this.preloadTextTracks = options.preloadTextTracks !== 거짓;
this.autoRemoteTextTracks_ = new TRACK_TYPES.ALL.text.ListClass();
this.initTrackListeners();
// 네이티브 컨트롤을 사용하지 않는 경우에만 구성요소 탭 이벤트를 켭니다.
if (!options.nativeControlsForTouch) {
this.emitTapEvents();
}
if (이.생성자) {
this.name_ = this.constructor.name || '알 수 없는 기술';
}
}
/**
* 플레이어가 허용하는 방식으로 소스 세트를 트리거하는 특수 기능
* 플레이어나 기술이 아직 준비되지 않은 경우 다시 트리거합니다.
*
* @fires Tech#sourceset
* @param {string} src 소스 변경 시점의 소스 문자열.
*/
triggerSourceset(src) {
if (!this.isReady_) {
// 초기 준비 시 소스 세트를 트리거해야 합니다.
// 플레이어가 볼 수 있도록 준비 후 1ms.
this.one('준비', () => this.setTimeout(() => this.triggerSourceset(src), 1));
}
/**
* 미디어 요소를 유발하는 기술에 소스가 설정되면 실행됩니다.
* 다시 로드합니다.
*
* @see {@link Player#event:sourceset}
* @event Tech#sourceset
* @type {이벤트대상~이벤트}
*/
this.trigger({
소스,
유형: '소스셋'
});
}
/* 지원되지 않는 이벤트 유형에 대한 폴백
==================================================== ============================== */
/**
* 기본적으로 지원하지 않는 브라우저에 대해 `progress` 이벤트를 폴리필합니다.
*
* @see {@link Tech#trackProgress}
*/
manualProgressOn() {
this.on('durationchange', this.onDurationChange_);
this.manualProgress = 참;
// 소스가 로드되기 시작하면 진행 상황을 감시하도록 트리거합니다.
this.one('준비', this.trackProgress_);
}
/**
* 생성된 `progress` 이벤트에 대한 폴리필을 끕니다.
* {@link Tech#manualProgressOn}
*/
manualProgressOff() {
this.manualProgress = 거짓;
this.stopTrackingProgress();
this.off('durationchange', this.onDurationChange_);
}
/**
* 버퍼링된 백분율이 변경될 때 `progress` 이벤트를 트리거하는 데 사용됩니다. 그것
* 500밀리초마다 호출되는 간격 함수를 설정하여
* 버퍼 종료 비율이 변경되었습니다.
*
* > 이 함수는 {@link Tech#manualProgressOn}에 의해 호출됩니다.
*
* @param {EventTarget~Event} 이벤트
* 이것을 실행하게 만든 `ready` 이벤트.
*
* @listens Tech#ready
* @fires Tech#progress
*/
trackProgress(이벤트) {
this.stopTrackingProgress();
this.progressInterval = this.setInterval(Fn.bind(this, function() {
// 버퍼링된 양이 지난 시간보다 크지 않으면 트리거하지 않음
const numBufferedPercent = this.bufferedPercent();
if (this.bufferedPercent_ !== numBufferedPercent) {
/**
* {@link Player#progress} 참조
*
* @event 기술#진행
* @type {이벤트대상~이벤트}
*/
this.trigger('진행');
}
this.bufferedPercent_ = numBufferedPercent;
if (numBufferedPercent === 1) {
this.stopTrackingProgress();
}
}), 500);
}
/**
* 호출하여 `durationchange` 이벤트에서 내부 기간을 업데이트합니다.
* {@link Tech#duration}.
*
* @param {EventTarget~Event} 이벤트
* 이것을 실행하게 만든 `durationchange` 이벤트.
*
* @listens Tech#durationchange
*/
onDurationChange(이벤트) {
this.duration_ = this.duration();
}
/**
* 버퍼링을 위한 `TimeRange` 개체를 가져오고 만듭니다.
*
* @return {시간 범위}
* 생성된 시간 범위 개체입니다.
*/
버퍼링() {
return createTimeRange(0, 0);
}
/**
* 현재 버퍼링된 현재 비디오의 백분율을 가져옵니다.
*
* @return {숫자}
* 0에서 1까지의 숫자로 소수 비율을 나타냅니다.
* 버퍼링된 비디오.
*
*/
bufferedPercent() {
return bufferedPercent(this.buffered(), this.duration_);
}
/**
* 생성된 `progress` 이벤트에 대한 폴리필을 끕니다.
* {@link Tech#manualProgressOn}
* 설정한 간격을 지워 수동으로 진행 이벤트 추적을 중지합니다.
* {@link Tech#trackProgress}.
*/
stopTrackingProgress() {
this.clearInterval(this.progressInterval);
}
/**
* 지원하지 않는 브라우저에 대해 `timeupdate` 이벤트를 폴리필합니다.
*
* @see {@link Tech#trackCurrentTime}
*/
manualTimeUpdatesOn() {
this.manualTimeUpdates = 참;
this.on('재생', this.trackCurrentTime_);
this.on('일시정지', this.stopTrackingCurrentTime_);
}
/**
* 생성된 `timeupdate` 이벤트에 대한 폴리필을 끕니다.
* {@link Tech#manualTimeUpdatesOn}
*/
manualTimeUpdatesOff() {
this.manualTimeUpdates = 거짓;
this.stopTrackingCurrentTime();
this.off('재생', this.trackCurrentTime_);
this.off('일시중지', this.stopTrackingCurrentTime_);
}
/**
* 현재 시간을 추적하고 'timeupdate'를 트리거하는 간격 기능을 설정합니다.
* 250밀리초.
*
* @listens Tech#play
* @triggers Tech#timeupdate
*/
trackCurrentTime() {
if (이.currentTimeInterval) {
this.stopTrackingCurrentTime();
}
this.currentTimeInterval = this.setInterval(function() {
/**
* 250ms 간격으로 트리거되어 비디오에서 시간이 흐르고 있음을 나타냅니다.
*
* @event Tech#timeupdate
* @type {이벤트대상~이벤트}
*/
this.trigger({ type: 'timeupdate', target: this, manualTriggered: true });
// 42 = 24fps // 250은 Webkit이 사용하는 것입니다. // FF는 15를 사용합니다.
}, 250);
}
/**
* {@link Tech#trackCurrentTime}에서 생성된 간격 함수를 중지하여
* `timeupdate` 이벤트가 더 이상 발생하지 않습니다.
*
* @listens {Tech#pause}
*/
stopTrackingCurrentTime() {
this.clearInterval(this.currentTimeInterval);
// #1002 - 다음 시간 업데이트가 발생하기 직전에 동영상이 종료되면
// 진행률 표시줄이 끝까지 도달하지 않습니다.
this.trigger({ type: 'timeupdate', target: this, manualTriggered: true });
}
/**
* 모든 이벤트 폴리필을 끄고 `Tech`의 {@link AudioTrackList}를 지우고,
* {@link VideoTrackList} 및 {@link TextTrackList}, 그리고 이 기술을 폐기하십시오.
*
* @fires 컴포넌트#dispose
*/
폐기() {
// 기술자 간에 재사용할 수 없기 때문에 모든 트랙을 지웁니다.
this.clearTracks(TRACK_TYPES.NORMAL.names);
// 수동 진행률 또는 시간 업데이트 추적을 끕니다.
if (이.수동 진행) {
this.manualProgressOff();
}
if (this.manualTimeUpdates) {
this.manualTimeUpdatesOff();
}
super.dispose();
}
/**
* 이름이 지정된 단일 `TrackList` 또는 `TrackLists` 배열을 지웁니다.
*
* > 메모: 소스 핸들러가 없는 기술자는 `비디오` 소스 간에 이것을 호출해야 합니다.
* & '오디오' 트랙. 당신은 트랙 사이에 사용하고 싶지 않아!
*
* @param {문자열[]|문자열} 유형
* 지울 TrackList 이름, 유효한 이름은 `video`, `audio` 및
* `텍스트`.
*/
clearTracks(유형) {
유형 = [].concat(유형);
// 기술자 간에 재사용할 수 없기 때문에 모든 트랙을 지웁니다.
types.forEach((유형) => {
const list = this[`${type}Tracks`]() || [];
let i = list.length;
동안 (i--) {
const track = list[i];
if (유형 === '텍스트') {
this.removeRemoteTextTrack(트랙);
}
list.removeTrack(트랙);
}
});
}
/**
* addRemoteTextTrack을 통해 추가된 모든 TextTrack을 제거합니다.
* 자동 가비지 수집용으로 표시됨
*/
cleanupAutoTextTracks() {
const list = this.autoRemoteTextTracks_ || [];
let i = list.length;
동안 (i--) {
const track = list[i];
this.removeRemoteTextTrack(트랙);
}
}
/**
* 기술을 재설정하면 모든 소스가 제거되고 내부 readyState가 재설정됩니다.
*
* @추상적인
*/
초기화() {}
/**
* 기술에서 `crossOrigin` 값을 가져옵니다.
*
* @추상적인
*
* @see {Html5#crossOrigin}
*/
크로스오리진() {}
/**
* 기술에서 `crossOrigin` 값을 설정하십시오.
*
* @추상적인
*
* @param {string} crossOrigin crossOrigin 값
* @see {Html5#setCrossOrigin}
*/
setCrossOrigin() {}
/**
* Tech에서 오류를 얻거나 설정하십시오.
*
* @param {MediaError} [오류]
* Tech에서 설정 오류
*
* @return {MediaError|null}
* 기술의 현재 오류 개체 또는 없는 경우 null입니다.
*/
오류(오류) {
if (err !== 정의되지 않음) {
this.error_ = new MediaError(err);
this.trigger('오류');
}
반환 this.error_;
}
/**
* 현재 소스에 대해 재생된 `TimeRange`를 반환합니다.
*
* > 메모: 이 구현은 불완전합니다. 재생된 `TimeRange`를 추적하지 않습니다.
* 소스가 전혀 재생되지 않았는지 여부만 확인합니다.
*
* @return {시간 범위}
* - 이 동영상이 재생된 경우 단일 시간 범위
* - 그렇지 않은 경우 빈 범위 집합입니다.
*/
플레이() {
if (this.hasStarted_) {
return createTimeRange(0, 0);
}
return createTimeRange();
}
/**
* 재생 시작
*
* @추상적인
*
* @see {Html5#play}
*/
놀다() {}
/**
* 스크러빙 여부 설정
*
* @추상적인
*
* @see {Html5#setScrubbing}
*/
setScrubbing() {}
/**
* 스크러빙 여부 확인
*
* @추상적인
*
* @see {Html5#scrubbing}
*/
스크러빙() {}
/**
* {@link Tech#manualTimeUpdatesOn}이 발생한 경우 수동 시간 업데이트가 발생합니다.
* 이전에 호출되었습니다.
*
* @fires Tech#timeupdate
*/
setCurrentTime() {
// 수동 시간 업데이트의 정확도 향상
if (this.manualTimeUpdates) {
/**
* 수동 `timeupdate` 이벤트.
*
* @event Tech#timeupdate
* @type {이벤트대상~이벤트}
*/
this.trigger({ type: 'timeupdate', target: this, manualTriggered: true });
}
}
/**
* {@link VideoTrackList}, {@link {AudioTrackList} 및
* {@link TextTrackList} 이벤트.
*
* 이렇게 하면 `addtrack` 및 `removetrack`에 대한 {@link EventTarget~EventListeners}가 추가됩니다.
*
* @fires Tech#audiotrackchange
* @fires Tech#videotrackchange
* @fires Tech#texttrackchange
*/
initTrackListeners() {
/**
* 기술 {@link AudioTrackList}에서 트랙이 추가되거나 제거될 때 트리거됨
*
* @event Tech#audiotrackchange
* @type {이벤트대상~이벤트}
*/
/**
* 기술 {@link VideoTrackList}에서 트랙이 추가되거나 제거될 때 트리거됨
*
* @event Tech#videotrackchange
* @type {이벤트대상~이벤트}
*/
/**
* 기술 {@link TextTrackList}에서 트랙이 추가되거나 제거될 때 트리거됨
*
* @event Tech#texttrackchange
* @type {이벤트대상~이벤트}
*/
TRACK_TYPES.NORMAL.names.forEach((이름) => {
const 소품 = TRACK_TYPES.NORMAL[이름];
const trackListChanges = () => {
this.trigger(`${name}trackchange`);
};
const track = this[props.getterName]();
tracks.addEventListener('removetrack', trackListChanges);
Tracks.addEventListener('addtrack', trackListChanges);
this.on('처리', () => {
tracks.removeEventListener('removetrack', trackListChanges);
Tracks.removeEventListener('addtrack', trackListChanges);
});
});
}
/**
* 필요한 경우 vtt.js를 사용하여 TextTracks 에뮬레이트
*
* @fires Tech#vttjs로드됨
* @fires Tech#vttjserror
*/
addWebVttScript_() {
경우 (window.WebVTT) {
반품;
}
// 처음에 Tech.el_은 구성 요소 시스템이 완료될 때까지 대기하는 dummy-div의 자식입니다.
// Tech.el_이 DOM의 일부인 지점에서 Tech가 준비되었음을 알립니다.
// WebVTT 스크립트를 삽입하기 전에
if (document.body.contains(this.el())) {
// 사용 가능하고 vtt.js 스크립트 위치가 전달되지 않은 경우 require를 통해 로드
// 옵션으로. novtt 빌드는 위의 요청 호출을 빈 개체로 바꿉니다.
// 검사가 항상 실패하면 이 문제가 발생합니다.
if (!this.options_['vtt.js'] && 일반(vtt) && 객체.키(vtt).길이 > 0) {
this.trigger('vttjsloaded');
반품;
}
// 스크립트 위치 옵션을 통해 vtt.js를 로드하거나 위치가 없는 cdn이
// 통과
const 스크립트 = document.createElement('스크립트');
script.src = this.options_['vtt.js'] || 'https://vjs.zencdn.net/vttjs/0.14.1/vtt.min.js';
script.onload = () => {
/**
* vtt.js가 로드되면 시작됩니다.
*
* @event Tech#vttjs로드됨
* @type {이벤트대상~이벤트}
*/
this.trigger('vttjsloaded');
};
script.onerror = () => {
/**
* 오류로 인해 vtt.js가 로드되지 않았을 때 발생
*
* @event Tech#vttjs로드됨
* @type {이벤트대상~이벤트}
*/
this.trigger('vttjserror');
};
this.on('처리', () => {
script.onload = null;
script.onerror = null;
});
// 하지만 아직 로드되지 않았으며 주입 전에 true로 설정하여
// 삽입된 window.WebVTT가 바로 로드되면 덮어쓰지 않습니다.
window.WebVTT = 참;
this.el().parentNode.appendChild(스크립트);
} else {
this.ready(this.addWebVttScript_);
}
}
/**
* 텍스트 트랙 에뮬레이트
*
*/
emulateTextTracks() {
const 트랙 = this.textTracks();
const remoteTracks = this.remoteTextTracks();
const handleAddTrack = (e) => tracks.addTrack(e.track);
const handleRemoveTrack = (e) => tracks.removeTrack(e.track);
remoteTracks.on('addtrack', handleAddTrack);
remoteTracks.on('removetrack', handleRemoveTrack);
this.addWebVttScript_();
const 업데이트디스플레이 = () => this.trigger('texttrackchange');
const textTracksChanges = () => {
업데이트디스플레이();
에 대한 (하자 i = 0; i < 트랙.길이; i++) {
const 트랙 = 트랙[i];
track.removeEventListener('cuechange', updateDisplay);
if (track.mode === '표시') {
track.addEventListener('cuechange', updateDisplay);
}
}
};
textTracksChanges();
tracks.addEventListener('변경', textTracksChanges);
tracks.addEventListener('addtrack', textTracksChanges);
tracks.addEventListener('removetrack', textTracksChanges);
this.on('처리', function() {
remoteTracks.off('addtrack', handleAddTrack);
remoteTracks.off('removetrack', handleRemoveTrack);
tracks.removeEventListener('변경', textTracksChanges);
tracks.removeEventListener('addtrack', textTracksChanges);
tracks.removeEventListener('removetrack', textTracksChanges);
에 대한 (하자 i = 0; i < 트랙.길이; i++) {
const 트랙 = 트랙[i];
track.removeEventListener('cuechange', updateDisplay);
}
});
}
/**
* 원격 {@link TextTrack} 객체를 생성하고 반환합니다.
*
* @param {문자열} 종류
* `TextTrack` 종류(자막, 캡션, 설명, 챕터 또는 메타데이터)
*
* @param {문자열} [레이블]
* 텍스트 트랙을 식별하는 레이블
*
* @param {문자열} [언어]
* 두 글자 언어 약어
*
* @return {텍스트 트랙}
* 생성되는 TextTrack.
*/
addTextTrack(종류, 라벨, 언어) {
if (! 종류) {
throw new Error('TextTrack 종류가 필요하지만 제공되지 않았습니다.');
}
return createTrackHelper(this, kind, label, language);
}
/**
* addRemoteTextTrack에서 사용할 에뮬레이트된 TextTrack 만들기
*
* 이것은 다음에서 상속받은 클래스에 의해 재정의됩니다.
* 기본 또는 사용자 정의 TextTracks를 생성하기 위한 기술.
*
* @param {객체} 옵션
* 객체는 TextTrack을 초기화하는 옵션을 포함해야 합니다.
*
* @param {문자열} [옵션.종류]
* `TextTrack` 종류(자막, 캡션, 설명, 챕터 또는 메타데이터).
*
* @param {문자열} [옵션.레이블].
* 텍스트 트랙을 식별하는 레이블
*
* @param {문자열} [옵션.언어]
* 두 글자 언어 약어.
*
* @return {HTMLTrackElement}
* 생성되는 트랙 요소.
*/
createRemoteTextTrack(옵션) {
const 트랙 = mergeOptions(옵션, {
기술: 이
});
return new TRACK_TYPES.REMOTE.remoteTextEl.TrackClass(track);
}
/**
* 원격 텍스트 추적 개체를 만들고 html 추적 요소를 반환합니다.
*
* > 메모: 이것은 에뮬레이트된 {@link HTMLTrackElement} 또는 네이티브 것일 수 있습니다.
*
* @param {객체} 옵션
* 자세한 속성은 {@link Tech#createRemoteTextTrack}을 참조하세요.
*
* @param {부울} [manualCleanup=true]
* - false인 경우: TextTrack이 비디오에서 자동으로 제거됩니다.
* 소스가 변경될 때마다 요소
* - 참인 경우: TextTrack은 수동으로 정리해야 합니다.
*
* @return {HTMLTrackElement}
* Html 트랙 요소.
*
* @deprecated 이 함수의 기본 기능은 동일합니다.
* 앞으로 "manualCleanup=false"로. manualCleanup 매개변수는
* 또한 제거됩니다.
*/
addRemoteTextTrack(옵션 = {}, manualCleanup) {
const htmlTrackElement = this.createRemoteTextTrack(옵션);
if (manualCleanup !== 참 && manualCleanup !== 거짓) {
// 사용 중단 경고
log.warn('manualCleanup' 매개변수를 'true'로 명시적으로 설정하지 않고 addRemoteTextTrack을 호출하는 것은 더 이상 사용되지 않으며 향후 video.js 버전에서는 'false'로 기본 설정됩니다.');
manualCleanup = 참;
}
// HTMLTrackElement 및 TextTrack을 원격 목록에 저장
this.remoteTextTrackEls().addTrackElement_(htmlTrackElement);
this.remoteTextTracks().addTrack(htmlTrackElement.track);
if (manualCleanup !== 참) {
// 존재하지 않는 경우 TextTrackList를 생성합니다.
this.ready(() => this.autoRemoteTextTracks_.addTrack(htmlTrackElement.track));
}
htmlTrackElement 반환;
}
/**
* 원격 `TextTrackList`에서 원격 텍스트 트랙을 제거합니다.
*
* @param {TextTrack} 트랙
* `TextTrackList`에서 제거할 `TextTrack`
*/
removeRemoteTextTrack(트랙) {
const trackElement = this.remoteTextTrackEls().getTrackElementByTrack_(track);
// 원격 목록에서 HTMLTrackElement 및 TextTrack 제거
this.remoteTextTrackEls().removeTrackElement_(trackElement);
this.remoteTextTracks().removeTrack(트랙);
this.autoRemoteTextTracks_.removeTrack(트랙);
}
/**
* W3C의 미디어에서 지정한 대로 사용 가능한 미디어 재생 품질 메트릭을 가져옵니다.
* 재생 품질 API.
*
* @see [사양]{@link https://wicg.github.io/media-playback-quality}
*
* @return {객체}
* 지원되는 미디어 재생 품질 메트릭이 있는 개체
*
* @추상적인
*/
getVideoPlaybackQuality() {
반품 {};
}
/**
* 항상 다른 창 위에 플로팅 비디오 창 만들기 시도
* 사용자가 다른 사용자와 상호 작용하는 동안 미디어를 계속 소비할 수 있도록
* 콘텐츠 사이트 또는 기기의 애플리케이션.
*
* @see [사양]{@link https://wicg.github.io/picture-in-picture}
*
* @return {약속|정의되지 않음}
* 브라우저가 지원하는 경우 Picture-in-Picture 창에 대한 약속
* 약속(또는 하나가 옵션으로 전달됨). 그것은 정의되지 않은 반환
* 그렇지 않으면.
*
* @추상적인
*/
requestPictureInPicture() {
const PromiseClass = this.options_.Promise || 창. 약속;
경우 (PromiseClass) {
반환 PromiseClass.reject();
}
}
/**
* 'disablePictureInPicture' 값을 확인하는 방법 < 동영상> 재산.
* 기술이 pip를 지원하지 않는 경우 비활성화된 것으로 간주되어야 하므로 기본값은 true입니다.
*
* @추상적인
*/
disablePictureInPicture() {
true를 반환합니다.
}
/**
* 'disablePictureInPicture'를 설정하거나 해제하는 방법 < 동영상> 재산.
*
* @추상적인
*/
setDisablePictureInPicture() {}
/**
* requestAnimationFrame을 사용한 requestVideoFrameCallback의 폴백 구현
*
* @param {함수} cb
* @return {숫자} 요청 ID
*/
requestVideoFrameCallback(cb) {
const id = Guid.newGUID();
if (!this.isReady_ || this.paused()) {
this.queuedHanders_.add(id);
this.one('재생 중', () => {
if (this.queuedHanders_.has(id)) {
this.queuedHanders_.delete(id);
cb();
}
});
} else {
this.requestNamedAnimationFrame(id, cb);
}
반환 ID;
}
/**
* cancelVideoFrameCallback의 폴백 구현
*
* @param {number} id 취소할 콜백의 id
*/
cancelVideoFrameCallback(id) {
if (this.queuedHanders_.has(id)) {
this.queuedHanders_.delete(id);
} else {
this.cancelNamedAnimationFrame(id);
}
}
/**
* `테크`에서 포스터를 설정하는 방법.
*
* @추상적인
*/
setPoster() {}
/**
* 'playsinline'의 존재를 확인하는 방법 < 동영상> 기인하다.
*
* @추상적인
*/
플레이인라인() {}
/**
* 'playsinline'을 설정하거나 해제하는 방법 < 동영상> 기인하다.
*
* @추상적인
*/
setPlaysinline() {}
/**
* 기본 오디오 트랙을 강제로 무시하려고 시도합니다.
*
* @param {boolean} override - true로 설정하면 기본 오디오가 무시됩니다.
* 그렇지 않으면 기본 오디오가 잠재적으로 사용됩니다.
*
* @추상적인
*/
overrideNativeAudioTracks() {}
/**
* 기본 비디오 트랙을 강제로 무시하려고 시도합니다.
*
* @param {boolean} override - true로 설정하면 기본 동영상이 무시됩니다.
* 그렇지 않으면 네이티브 비디오가 잠재적으로 사용됩니다.
*
* @추상적인
*/
overrideNativeVideoTracks() {}
/*
* 기술이 주어진 MIME 유형을 지원할 수 있는지 확인하십시오.
*
* 기본 기술은 어떤 유형도 지원하지 않지만 소스 핸들러는
* 이것을 덮어씁니다.
*
* @param {문자열} 유형
* 지원 여부를 확인할 mimetype
*
* @return {문자열}
* 'probably', 'maybe' 또는 빈 문자열
*
* @see [Spec]{@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/canPlayType}
*
* @추상적인
*/
canPlayType() {
반품 '';
}
/**
* 이 기술에서 지원하는 유형인지 확인하십시오.
*
* 기본 기술은 어떤 유형도 지원하지 않지만 소스 핸들러는
* 이것을 덮어씁니다.
*
* @param {문자열} 유형
* 확인할 미디어 유형
* @return {string} 네이티브 동영상 요소의 응답을 반환합니다.
*/
정적 canPlayType() {
반품 '';
}
/**
* 기술이 주어진 소스를 지원할 수 있는지 확인
*
* @param {객체} srcObj
* 소스 객체
* @param {객체} 옵션
* 기술자에게 전달된 옵션
* @return {string} 'probably', 'maybe' 또는 '' (빈 문자열)
*/
정적 canPlaySource(srcObj, 옵션) {
return Tech.canPlayType(srcObj.type);
}
/*
* 인수가 Tech인지 여부를 반환합니다.
* `Html5`와 같은 클래스 또는 `player.tech_`와 같은 인스턴스를 전달할 수 있습니다.
*
* @param {객체} 컴포넌트
* 확인할 항목
*
* @return {부울}
* 기술 여부
* - 기술인 경우 참
* - 그렇지 않은 경우 거짓
*/
정적 isTech(구성 요소) {
return component.prototype instanceof 기술 ||
Tech의 구성 요소 인스턴스 ||
구성 요소 === 기술;
}
/**
* videojs의 공유 목록에 `Tech`를 등록합니다.
*
* @param {문자열} 이름
* 등록할 `Tech`의 이름입니다.
*
* @param {객체} 기술
* 등록할 `Tech` 클래스입니다.
*/
정적 registerTech(이름, 기술) {
if (!Tech.techs_) {
Tech.techs_ = {};
}
if (!Tech.isTech(기술)) {
throw new Error(`기술 ${이름}은 기술이어야 함`);
}
if (!Tech.canPlayType) {
throw new Error('기술자는 정적 canPlayType 메서드를 가지고 있어야 합니다.');
}
if (!Tech.canPlaySource) {
throw new Error('기술자는 정적 canPlaySource 메서드를 가지고 있어야 합니다.');
}
name = toTitleCase(이름);
Tech.techs_[이름] = 기술;
Tech.techs_[toLowerCase(이름)] = 기술;
if (이름 !== '기술') {
// 카멜 케이스 techOrder에서 사용하기 위한 techName
Tech.defaultTechOrder_.push(이름);
}
반환 기술;
}
/**
* 이름으로 공유 목록에서 `Tech`를 가져옵니다.
*
* @param {문자열} 이름
* 가져올 기술의 `camelCase` 또는 `TitleCase` 이름
*
* @return {기술|정의되지 않음}
* 요청한 이름의 기술이 없는 경우 `Tech` 또는 정의되지 않음.
*/
정적 getTech(이름) {
if (!이름) {
반품;
}
if (Tech.techs_ && Tech.techs_[이름]) {
return Tech.techs_[이름];
}
name = toTitleCase(이름);
만약 (창 && window.videojs && window.videojs[이름]) {
log.warn(`videojs.registerTech(name, tech)를 사용하여 등록해야 할 때 ${name} 기술이 videojs 개체에 추가되었습니다.`);
return window.videojs[이름];
}
}
}
/**
* {@link VideoTrackList} 받기
*
* @returns {VideoTrackList}
* @method Tech.prototype.videoTracks
*/
/**
* {@link AudioTrackList} 받기
*
* @returns {오디오트랙리스트}
* @method Tech.prototype.audioTracks
*/
/**
* {@link TextTrackList} 받기
*
* @returns {TextTrackList}
* @method Tech.prototype.textTracks
*/
/**
* 원격 요소 가져오기 {@link TextTrackList}
*
* @returns {TextTrackList}
* @method Tech.prototype.remoteTextTracks
*/
/**
* 원격 요소 가져오기 {@link HtmlTrackElementList}
*
* @returns {HtmlTrackElementList}
* @method Tech.prototype.remoteTextTrackEls
*/
TRACK_TYPES.ALL.names.forEach(기능(이름) {
const props = TRACK_TYPES.ALL[이름];
Tech.prototype[props.getterName] = function() {
this[props.privateName] = this[props.privateName] || 새로운 props.ListClass();
return this[props.privateName];
};
});
/**
* 관련 텍스트 트랙 목록
*
* @type {TextTrackList}
* @사적인
* @property Tech#textTracks_
*/
/**
* 관련 오디오 트랙 목록.
*
* @type {오디오트랙리스트}
* @사적인
* @property Tech#audioTracks_
*/
/**
* 관련 비디오 트랙 목록.
*
* @type {VideoTrackList}
* @사적인
* @property Tech#videoTracks_
*/
/**
* `Tech`가 볼륨 조절을 지원하는지 여부를 나타내는 부울입니다.
*
* @유형 {부울}
* @기본
*/
Tech.prototype.featuresVolumeControl = 참;
/**
* `Tech`가 음소거 볼륨을 지원하는지 여부를 나타내는 부울입니다.
*
* @type {볼린}
* @기본
*/
Tech.prototype.featuresMuteControl = 참;
/**
* `Tech`가 전체 화면 크기 조정 제어를 지원하는지 여부를 나타내는 부울입니다.
* 요청 전체 화면을 사용하여 플러그인 크기를 조정하면 플러그인이 다시 로드됨
*
* @유형 {부울}
* @기본
*/
Tech.prototype.featuresFullscreenResize = 거짓;
/**
* 'Tech'가 비디오 재생 속도 변경을 지원하는지 여부를 나타내는 부울
* 연극. 예:
* - 플레이어가 2배(2배) 빠르게 플레이하도록 설정
* - 플레이어가 0.5배(절반) 빠르게 플레이하도록 설정
*
* @유형 {부울}
* @기본
*/
Tech.prototype.featuresPlaybackRate = 거짓;
/**
* `Tech`가 `progress` 이벤트를 지원하는지 여부를 나타내는 부울입니다. 이것은 현재
* video-js-swf에 의해 트리거되지 않습니다. 다음을 결정하는 데 사용됩니다.
* {@link Tech#manualProgressOn}을 호출해야 합니다.
*
* @유형 {부울}
* @기본
*/
Tech.prototype.featuresProgressEvents = 거짓;
/**
* `Tech`가 `sourceset` 이벤트를 지원하는지 여부를 나타내는 부울입니다.
*
* 기술자는 이것을 `true`로 설정한 다음 {@link Tech#triggerSourceset}를 사용해야 합니다.
* 받은 후 가장 빠른 시간에 {@link Tech#event:sourceset} 트리거
* 새로운 소스입니다.
*
* @유형 {부울}
* @기본
*/
Tech.prototype.featuresSourceset = 거짓;
/**
* `Tech`가 `timeupdate` 이벤트를 지원하는지 여부를 나타내는 부울입니다. 이것은 현재
* video-js-swf에 의해 트리거되지 않습니다. 다음을 결정하는 데 사용됩니다.
* {@link Tech#manualTimeUpdates}를 호출해야 합니다.
*
* @유형 {부울}
* @기본
*/
Tech.prototype.featuresTimeupdateEvents = 거짓;
/**
* `Tech`가 기본 `TextTrack`을 지원하는지 여부를 나타내는 부울입니다.
* 브라우저에서 지원하는 경우 기본 `TextTrack`과 통합하는 데 도움이 됩니다.
*
* @유형 {부울}
* @기본
*/
Tech.prototype.featuresNativeTextTracks = 거짓;
/**
* `Tech`가 `requestVideoFrameCallback`을 지원하는지 여부를 나타내는 부울입니다.
*
* @유형 {부울}
* @기본
*/
Tech.prototype.featuresVideoFrameCallback = 거짓;
/**
* 소스 핸들러 패턴을 사용하려는 기술자를 위한 기능적 혼합.
* 소스 처리기는 특정 형식을 처리하기 위한 스크립트입니다.
* 소스 핸들러 패턴은 적응형 형식(HLS, DASH)에 사용됩니다.
* 비디오 데이터를 수동으로 로드하고 소스 버퍼(Media Source Extensions)에 공급
* 예: `Tech.withSourceHandlers.call(MyTech);`
*
* @param {기술} _기술
* 소스 핸들러 기능을 추가하는 기술.
*
* @mixes Tech~SourceHandlerAdditions
*/
Tech.withSourceHandlers = function(_Tech) {
/**
* 소스 핸들러 등록
*
* @param {함수} 핸들러
* 소스 핸들러 클래스
*
* @param {숫자} [인덱스]
* 아래 인덱스에 등록
*/
_Tech.registerSourceHandler = 함수(핸들러, 인덱스) {
let handlers = _Tech.sourceHandlers;
if (!핸들러) {
핸들러 = _Tech.sourceHandlers = [];
}
if (인덱스 === 정의되지 않음) {
// 리스트 끝에 추가
인덱스 = handlers.length;
}
handlers.splice(인덱스, 0, 핸들러);
};
/**
* 기술이 주어진 유형을 지원할 수 있는지 확인하십시오. 또한 확인
* Techs sourceHandlers.
*
* @param {문자열} 유형
* 확인할 mimetype입니다.
*
* @return {문자열}
* 'probably', 'maybe' 또는 '' (빈 문자열)
*/
_Tech.canPlayType = 기능(유형) {
const 핸들러 = _Tech.sourceHandlers || [];
할 수 있습니다;
에 대한 (하자 i = 0; i < 핸들러.길이; i++) {
can = 핸들러[i].canPlayType(유형);
만약 (할 수) {
반환할 수 있습니다.
}
}
반품 '';
};
/**
* 소스를 지원하는 첫 번째 소스 핸들러를 반환합니다.
*
* 할 것: 답 질문: '아마도'보다 '아마도'가 우선되어야 합니다.
*
* @param {Tech~SourceObject} 소스
* 소스 객체
*
* @param {객체} 옵션
* 기술자에게 전달된 옵션
*
* @return {SourceHandler|null}
* 소스를 지원하는 첫 번째 소스 핸들러 또는 null인 경우
* SourceHandler가 소스를 지원하지 않음
*/
_Tech.selectSourceHandler = 기능(소스, 옵션) {
const 핸들러 = _Tech.sourceHandlers || [];
할 수 있습니다;
에 대한 (하자 i = 0; i < 핸들러.길이; i++) {
can = 핸들러[i].canHandleSource(소스, 옵션);
만약 (할 수) {
반환 핸들러[i];
}
}
null을 반환합니다.
};
/**
* 기술이 주어진 소스를 지원할 수 있는지 확인하십시오.
*
* @param {Tech~SourceObject} srcObj
* 소스 객체
*
* @param {객체} 옵션
* 기술자에게 전달된 옵션
*
* @return {문자열}
* 'probably', 'maybe' 또는 '' (빈 문자열)
*/
_Tech.canPlaySource = 함수(srcObj, 옵션) {
const sh = _Tech.selectSourceHandler(srcObj, 옵션);
만약 (쉬) {
sh.canHandleSource(srcObj, 옵션)를 반환합니다.
}
반품 '';
};
/**
* 소스 처리기를 사용하는 경우 다음과 같은 구현을 선호합니다.
* 일반적으로 기술자가 제공하는 모든 기능.
*/
const 지연 가능 = [
'찾을 수 있는',
'찾다',
'지속'
];
/**
* `SourceHandler`의 검색 가능 항목을 호출하는 {@link Tech#seekable} 주변의 래퍼
* 함수가 존재하는 경우 Techs 검색 가능 함수로 폴백합니다.
*
* @method _Tech.seekable
*/
/**
* `SourceHandler` 기간을 호출하는 {@link Tech#duration} 주변의 래퍼
* 존재하는 경우 기능, 그렇지 않으면 techs 기간 기능으로 대체됩니다.
*
* @method _Tech.duration
*/
deferrable.forEach(function(fnName) {
const originalFn = this[fnName];
if (typeof originalFn !== '함수') {
반품;
}
this[fnName] = 함수() {
if (이.소스핸들러_ && this.sourceHandler_[fnName]) {
return this.sourceHandler_[fnName].apply(this.sourceHandler_, 인수);
}
return originalFn.apply(this, arguments);
};
}, _Tech.prototype);
/**
* 소스 객체를 이용하여 소스를 설정하는 함수 생성
* 및 소스 핸들러.
* 소스 핸들러가 발견되지 않으면 호출해서는 안 됩니다.
*
* @param {Tech~SourceObject} 소스
* src 및 type 키가 있는 소스 객체
*/
_Tech.prototype.setSource = 함수(소스) {
let sh = _Tech.selectSourceHandler(source, this.options_);
경우 (! 쉬) {
// 지원되지 않는 소스가 있는 경우 네이티브 소스 핸들러로 폴백
// 의도적으로 설정
if (_Tech.nativeSourceHandler) {
sh = _Tech.nativeSourceHandler;
} else {
log.error('현재 소스에 대한 소스 핸들러를 찾을 수 없습니다.');
}
}
// 기존 소스 핸들러 폐기
this.disposeSourceHandler();
this.off('dispose', this.disposeSourceHandler_);
if (sh !== _Tech.nativeSourceHandler) {
this.currentSource_ = 소스;
}
this.sourceHandler_ = sh.handleSource(source, this, this.options_);
this.one('dispose', this.disposeSourceHandler_);
};
/**
* Tech가 폐기될 때 기존 SourceHandlers 및 수신기를 정리합니다.
*
* @listens Tech#dispose
*/
_Tech.prototype.disposeSourceHandler = 함수() {
// 소스가 있고 다른 소스가 있는 경우
// 그런 다음 새로운 것을 로드합니다.
// 현재 트랙을 모두 지우는 것보다
if (이.currentSource_) {
this.clearTracks(['오디오', '비디오']);
this.currentSource_ = null;
}
// 항상 자동 텍스트 트랙을 정리합니다.
this.cleanupAutoTextTracks();
if (this.sourceHandler_) {
if (this.sourceHandler_.dispose) {
this.sourceHandler_.dispose();
}
this.sourceHandler_ = null;
}
};
};
// 기본 Tech 클래스는 Component로 등록되어야 합니다. 그것은 유일한
// 컴포넌트로 등록할 수 있는 기술.
Component.registerComponent('기술', 기술);
Tech.registerTech('기술', 기술);
/**
* 플레이어의 techOrder에 추가해야 하는 기술 목록
*
* @사적인
*/
Tech.defaultTechOrder_ = [];
수출 기본 기술;