This commit is contained in:
Harvey Zack committed 2018-10-12 16:58:58 +08:00
1 parent b74ff6052c
commit 495782f5c6
11 files changed
+504 -7

No files matched your search

+5 -1
View File
@@ -4,6 +4,7 @@ import Emitter from 'tiny-emitter';
import Template from './template';
import I18n from './i18n';
import Player from './player';
import Mse from './mse';
import Controls from './controls';
import Contextmenu from './contextmenu';
import Danmaku from './danmaku';
@@ -35,6 +36,8 @@ class Artplayer extends Emitter {
volume: 0.7,
autoplay: false,
preload: 'auto',
type: '',
mimeCodec: '',
lang: navigator.language.toLowerCase()
};
}
@@ -66,6 +69,7 @@ class Artplayer extends Emitter {
this.template = new Template(this);
this.i18n = new I18n(this);
this.player = new Player(this);
this.mse = new Mse(this);
this.controls = new Controls(this);
this.contextmenu = new Contextmenu(this);
this.danmaku = new Danmaku(this);
@@ -79,7 +83,7 @@ class Artplayer extends Emitter {
}
destroy() {
this.destroyEvents.forEach(event => event.destroy());
this.destroyEvents.forEach(event => event());
this.refs.container.innerHTML = '';
instances.splice(instances.indexOf(this), 1);
}
+121
View File
@@ -0,0 +1,121 @@
import { errorHandle, request } from './utils';
import mimeCodeces from './utils/mimeCodeces';
import mseConfig from './utils/mseConfig';
export default class Mse {
constructor(art) {
this.art = art;
this.setMimeCodec();
this.mediaSourceEventFn = this.mediaSourceEventFn.bind(this);
this.sourceBufferEventFn = this.sourceBufferEventFn.bind(this);
this.sourceBuffersEventFn = this.sourceBuffersEventFn.bind(this);
this.activeSourceBuffersEventFn = this.activeSourceBuffersEventFn.bind(this);
this.init();
this.eventBind();
this.eventStart();
}
setMimeCodec() {
const { option } = this.art;
if (!option.type) {
const urlArr = option.url
.trim()
.toLowerCase()
.split('.');
const type = urlArr[urlArr.length - 1];
errorHandle(
Object.keys(mimeCodeces).includes(type),
`Can't find video's type from ${option.url}`
);
option.type = type;
}
if (!option.mimeCodec) {
const mimeCodec = mimeCodeces[option.type];
errorHandle(
mimeCodec,
`Can't find video's mimeCodec from ${option.type}`
);
option.mimeCodec = mimeCodec;
}
}
init() {
const {
option,
refs: { $video }
} = this.art;
errorHandle(
MediaSource.isTypeSupported(option.mimeCodec),
`Unsupported MIME type or codec: ${option.mimeCodec}`
);
this.mediaSource = new MediaSource();
$video.src = URL.createObjectURL(this.mediaSource);
this.art.destroyEvents.push(() => {
URL.revokeObjectURL($video.src);
});
}
eventBind() {
const { instance, sourceBufferList } = mseConfig;
instance.events.forEach(eventName => {
this.mediaSource.addEventListener(eventName, this.mediaSourceEventFn);
this.art.destroyEvents.push(() => {
this.mediaSource.removeEventListener(eventName, this.mediaSourceEventFn);
});
});
sourceBufferList.events.forEach(eventName => {
this.mediaSource.sourceBuffers.addEventListener(eventName, this.sourceBuffersEventFn);
this.art.destroyEvents.push(() => {
this.mediaSource.sourceBuffers.removeEventListener(eventName, this.sourceBuffersEventFn);
});
this.mediaSource.activeSourceBuffers.addEventListener(eventName, this.activeSourceBuffersEventFn);
this.art.destroyEvents.push(() => {
this.mediaSource.activeSourceBuffers.removeEventListener(eventName, this.activeSourceBuffersEventFn);
});
});
}
eventStart() {
const { option } = this.art;
const { sourceBuffer } = mseConfig;
this.art.on('mediaSource:sourceopen', () => {
this.sourceBuffer = this.mediaSource.addSourceBuffer(option.mimeCodec);
sourceBuffer.events.forEach(eventName => {
this.sourceBuffer.addEventListener(eventName, this.sourceBufferEventFn);
this.art.destroyEvents.push(() => {
this.sourceBuffer.removeEventListener(eventName, this.sourceBufferEventFn);
});
});
});
this.art.on('sourceBuffer:updateend', () => {
this.mediaSource.endOfStream();
});
request(option.url).then(response => {
this.sourceBuffer.appendBuffer(response);
});
}
mediaSourceEventFn(event) {
this.art.emit(`mediaSource:${event.type}`, event);
}
sourceBufferEventFn(event) {
this.art.emit(`sourceBuffer:${event.type}`, event);
}
sourceBuffersEventFn(event) {
this.art.emit(`sourceBuffers:${event.type}`, event);
}
activeSourceBuffersEventFn(event) {
this.art.emit(`activeSourceBuffers:${event.type}`, event);
}
}
+20 -2
View File
@@ -1,19 +1,37 @@
import { clamp } from './utils';
import mediaElement from './utils/mediaElement';
export default class Player {
constructor(art) {
this.art = art;
this.eventFn = this.eventFn.bind(this);
this.init();
this.eventBind();
}
init() {
const { option } = this.art;
const { $video } = this.art.refs;
$video.controls = false;
$video.controls = true;
$video.poster = option.poster;
$video.volume = clamp(option.volume, 0, 1);
$video.autoplay = option.autoplay;
$video.preload = option.preload;
$video.src = option.url;
}
eventBind() {
const { $video } = this.art.refs;
const { events } = mediaElement;
for (let index = 0; index < events.length; index++) {
const eventName = events[index];
$video.addEventListener(eventName, this.eventFn);
this.art.destroyEvents.push(() => {
$video.removeEventListener(eventName, this.eventFn);
});
}
}
eventFn(event) {
this.art.emit(`video:${event.type}`, event);
}
}
+8
View File
@@ -9,3 +9,11 @@ export function errorHandle(condition, msg) {
export function clamp(num, a, b) {
return Math.max(Math.min(num, Math.max(a, b)), Math.min(a, b));
}
export function request(url) {
return fetch(url)
.then(response => response.arrayBuffer())
.catch(err => {
throw new ArtPlayerError(err.message);
});
}
+64
View File
@@ -0,0 +1,64 @@
export default {
propertys: [
'audioTracks',
'autoplay',
'buffered',
'controller',
'controls',
'crossOrigin',
'currentSrc',
'currentTime',
'defaultMuted',
'defaultPlaybackRate',
'duration',
'ended',
'error',
'loop',
'mediaGroup',
'muted',
'networkState',
'paused',
'playbackRate',
'played',
'preload',
'readyState',
'seekable',
'seeking',
'src',
'startDate',
'textTracks',
'videoTracks',
'volume'
],
methods: [
'addTextTrack',
'canPlayType',
'load',
'play',
'pause'
],
events: [
'abort',
'canplay',
'canplaythrough',
'durationchange',
'emptied',
'ended',
'error',
'loadeddata',
'loadedmetadata',
'loadstart',
'pause',
'play',
'playing',
'progress',
'ratechange',
'seeked',
'seeking',
'stalled',
'suspend',
'timeupdate',
'volumechange',
'waiting'
]
};
+5
View File
@@ -0,0 +1,5 @@
export default {
mp4: 'video/mp4; codecs="avc1.42E01E, mp4a.40.2"',
webm: 'video/webm; codecs="vorbis, vp8"',
ts: 'video/mp2t; codecs="avc1.42E01E, mp4a.40.2"'
};
+58
View File
@@ -0,0 +1,58 @@
export default {
instance: {
propertys: [
'activeSourceBuffers',
'duration',
'readyState',
'sourceBuffers'
],
methods: [
'addSourceBuffer',
'endOfStream',
'removeSourceBuffer',
'clearLiveSeekableRange',
'setLiveSeekableRange'
],
events: [
'sourceclose',
'sourceended',
'sourceopen'
]
},
sourceBuffer: {
propertys: [
'mode',
'updating',
'buffered',
'timestampOffset',
'audioTracks',
'videoTracks',
'textTracks',
'appendWindowStart',
'appendWindowEnd',
'trackDefaults'
],
methods: [
'appendBuffer',
'appendStream',
'abort',
'remove'
],
events: [
'abort',
'error',
'update',
'updateend',
'updatestart'
]
},
sourceBufferList: {
propertys: [
'length'
],
events: [
'addsourcebuffer',
'removesourcebuffer'
]
}
};
+10
View File
@@ -46,4 +46,14 @@ export default function verification(option) {
`'lang' option require 'string' type, but got '${typeof option.lang}'.`
);
errorHandle(
typeof option.type === 'string',
`'type' option require 'string' type, but got '${typeof option.type}'.`
);
errorHandle(
typeof option.mimeCodec === 'string',
`'mimeCodec' option require 'string' type, but got '${typeof option.mimeCodec}'.`
);
}