1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
|
// Thanks: https://github.com/feross/render-media
// TODO: use render-media once https://github.com/feross/render-media/issues/32 is fixed
import { extname } from 'path'
import * as MediaElementWrapper from 'mediasource'
import * as videostream from 'videostream'
const VIDEOSTREAM_EXTS = [
'.m4a',
'.m4v',
'.mp4'
]
type RenderMediaOptions = {
controls: boolean
autoplay: boolean
}
function renderVideo (
file,
elem: HTMLVideoElement,
opts: RenderMediaOptions,
callback: (err: Error, renderer: any) => void
) {
validateFile(file)
return renderMedia(file, elem, opts, callback)
}
function renderMedia (file, elem: HTMLVideoElement, opts: RenderMediaOptions, callback: (err: Error, renderer: any) => void) {
const extension = extname(file.name).toLowerCase()
let preparedElem = undefined
let currentTime = 0
let renderer
if (VIDEOSTREAM_EXTS.indexOf(extension) >= 0) {
renderer = useVideostream()
} else {
renderer = useMediaSource()
}
function useVideostream () {
prepareElem()
preparedElem.addEventListener('error', fallbackToMediaSource)
preparedElem.addEventListener('loadstart', onLoadStart)
preparedElem.addEventListener('canplay', onCanPlay)
return videostream(file, preparedElem)
}
function useMediaSource () {
prepareElem()
preparedElem.addEventListener('error', callback)
preparedElem.addEventListener('loadstart', onLoadStart)
preparedElem.addEventListener('canplay', onCanPlay)
const wrapper = new MediaElementWrapper(preparedElem)
const writable = wrapper.createWriteStream(getCodec(file.name))
file.createReadStream().pipe(writable)
if (currentTime) preparedElem.currentTime = currentTime
return wrapper
}
function fallbackToMediaSource () {
preparedElem.removeEventListener('error', fallbackToMediaSource)
preparedElem.removeEventListener('canplay', onCanPlay)
useMediaSource()
}
function prepareElem () {
if (preparedElem === undefined) {
preparedElem = elem
preparedElem.addEventListener('progress', function () {
currentTime = elem.currentTime
})
}
}
function onLoadStart () {
preparedElem.removeEventListener('loadstart', onLoadStart)
if (opts.autoplay) preparedElem.play()
}
function onCanPlay () {
preparedElem.removeEventListener('canplay', onCanPlay)
callback(null, renderer)
}
}
function validateFile (file) {
if (file == null) {
throw new Error('file cannot be null or undefined')
}
if (typeof file.name !== 'string') {
throw new Error('missing or invalid file.name property')
}
if (typeof file.createReadStream !== 'function') {
throw new Error('missing or invalid file.createReadStream property')
}
}
function getCodec (name: string) {
const ext = extname(name).toLowerCase()
return {
'.m4a': 'audio/mp4; codecs="mp4a.40.5"',
'.m4v': 'video/mp4; codecs="avc1.640029, mp4a.40.5"',
'.mkv': 'video/webm; codecs="avc1.640029, mp4a.40.5"',
'.mp3': 'audio/mpeg',
'.mp4': 'video/mp4; codecs="avc1.640029, mp4a.40.5"',
'.webm': 'video/webm; codecs="vorbis, vp8"'
}[ext]
}
export {
renderVideo
}
|