]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - client/src/assets/player/video-renderer.ts
Fix tool documentation
[github/Chocobozzz/PeerTube.git] / client / src / assets / player / video-renderer.ts
CommitLineData
aa8b6df4
C
1// Thanks: https://github.com/feross/render-media
2// TODO: use render-media once https://github.com/feross/render-media/issues/32 is fixed
3
aa8b6df4 4import * as MediaElementWrapper from 'mediasource'
bf5685f0 5import { extname } from 'path'
aa8b6df4
C
6import * as videostream from 'videostream'
7
8const VIDEOSTREAM_EXTS = [
9 '.m4a',
10 '.m4v',
11 '.mp4'
12]
13
14type RenderMediaOptions = {
15 controls: boolean
16 autoplay: boolean
17}
18
19function renderVideo (
20 file,
21 elem: HTMLVideoElement,
22 opts: RenderMediaOptions,
23 callback: (err: Error, renderer: any) => void
24) {
25 validateFile(file)
26
27 return renderMedia(file, elem, opts, callback)
28}
29
bf5685f0 30function renderMedia (file, elem: HTMLVideoElement, opts: RenderMediaOptions, callback: (err: Error, renderer?: any) => void) {
aa8b6df4
C
31 const extension = extname(file.name).toLowerCase()
32 let preparedElem = undefined
33 let currentTime = 0
34 let renderer
35
36 if (VIDEOSTREAM_EXTS.indexOf(extension) >= 0) {
37 renderer = useVideostream()
38 } else {
39 renderer = useMediaSource()
40 }
41
42 function useVideostream () {
43 prepareElem()
bf5685f0
C
44 preparedElem.addEventListener('error', function onError () {
45 preparedElem.removeEventListener('error', onError)
46
47 return fallbackToMediaSource()
48 })
aa8b6df4 49 preparedElem.addEventListener('loadstart', onLoadStart)
aa8b6df4
C
50 return videostream(file, preparedElem)
51 }
52
bf5685f0
C
53 function useMediaSource (useVP9 = false) {
54 const codecs = getCodec(file.name, useVP9)
55
aa8b6df4 56 prepareElem()
bf5685f0
C
57 preparedElem.addEventListener('error', function onError(err) {
58 // Try with vp9 before returning an error
59 if (codecs.indexOf('vp8') !== -1) {
60 preparedElem.removeEventListener('error', onError)
61
62 return fallbackToMediaSource(true)
63 }
64
65 return callback(err)
66 })
aa8b6df4 67 preparedElem.addEventListener('loadstart', onLoadStart)
aa8b6df4
C
68
69 const wrapper = new MediaElementWrapper(preparedElem)
bf5685f0 70 const writable = wrapper.createWriteStream(codecs)
aa8b6df4
C
71 file.createReadStream().pipe(writable)
72
73 if (currentTime) preparedElem.currentTime = currentTime
74
75 return wrapper
76 }
77
bf5685f0
C
78 function fallbackToMediaSource (useVP9 = false) {
79 if (useVP9 === true) console.log('Falling back to media source with VP9 enabled.')
80 else console.log('Falling back to media source..')
aa8b6df4 81
bf5685f0 82 useMediaSource(useVP9)
aa8b6df4
C
83 }
84
85 function prepareElem () {
86 if (preparedElem === undefined) {
87 preparedElem = elem
88
89 preparedElem.addEventListener('progress', function () {
90 currentTime = elem.currentTime
91 })
92 }
93 }
94
95 function onLoadStart () {
96 preparedElem.removeEventListener('loadstart', onLoadStart)
97 if (opts.autoplay) preparedElem.play()
aa8b6df4 98
aa8b6df4
C
99 callback(null, renderer)
100 }
101}
102
103function validateFile (file) {
104 if (file == null) {
105 throw new Error('file cannot be null or undefined')
106 }
107 if (typeof file.name !== 'string') {
108 throw new Error('missing or invalid file.name property')
109 }
110 if (typeof file.createReadStream !== 'function') {
111 throw new Error('missing or invalid file.createReadStream property')
112 }
113}
114
bf5685f0 115function getCodec (name: string, useVP9 = false) {
aa8b6df4 116 const ext = extname(name).toLowerCase()
bf5685f0
C
117 if (ext === '.mp4') {
118 return 'video/mp4; codecs="avc1.640029, mp4a.40.5"'
119 }
120
121 if (ext === '.webm') {
122 if (useVP9 === true) return 'video/webm; codecs="vp9, opus"'
123
124 return 'video/webm; codecs="vp8, vorbis"'
125 }
126
127 return undefined
aa8b6df4
C
128}
129
130export {
131 renderVideo
132}