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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
|
// Thanks: https://github.com/feross/render-media
// TODO: use render-media once https://github.com/feross/render-media/issues/32 is fixed
import * as MediaElementWrapper from 'mediasource'
import { extname } from 'path'
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
try {
if (VIDEOSTREAM_EXTS.indexOf(extension) >= 0) {
renderer = useVideostream()
} else {
renderer = useMediaSource()
}
} catch (err) {
return callback(err)
}
function useVideostream () {
prepareElem()
preparedElem.addEventListener('error', function onError () {
preparedElem.removeEventListener('error', onError)
return fallbackToMediaSource()
})
preparedElem.addEventListener('loadstart', onLoadStart)
return videostream(file, preparedElem)
}
function useMediaSource (useVP9 = false) {
const codecs = getCodec(file.name, useVP9)
prepareElem()
preparedElem.addEventListener('error', function onError(err) {
// Try with vp9 before returning an error
if (codecs.indexOf('vp8') !== -1) {
preparedElem.removeEventListener('error', onError)
return fallbackToMediaSource(true)
}
return callback(err)
})
preparedElem.addEventListener('loadstart', onLoadStart)
const wrapper = new MediaElementWrapper(preparedElem)
const writable = wrapper.createWriteStream(codecs)
file.createReadStream().pipe(writable)
if (currentTime) preparedElem.currentTime = currentTime
return wrapper
}
function fallbackToMediaSource (useVP9 = false) {
if (useVP9 === true) console.log('Falling back to media source with VP9 enabled.')
else console.log('Falling back to media source..')
useMediaSource(useVP9)
}
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()
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, useVP9 = false) {
const ext = extname(name).toLowerCase()
if (ext === '.mp4') {
return 'video/mp4; codecs="avc1.640029, mp4a.40.5"'
}
if (ext === '.webm') {
if (useVP9 === true) return 'video/webm; codecs="vp9, opus"'
return 'video/webm; codecs="vp8, vorbis"'
}
return undefined
}
export {
renderVideo
}
|