1 // Thanks: https://github.com/feross/render-media
2 // TODO: use render-media once https://github.com/feross/render-media/issues/32 is fixed
4 import * as MediaElementWrapper from 'mediasource'
5 import { extname } from 'path'
6 import * as videostream from 'videostream'
8 const VIDEOSTREAM_EXTS = [
14 type RenderMediaOptions = {
19 function renderVideo (
21 elem: HTMLVideoElement,
22 opts: RenderMediaOptions,
23 callback: (err: Error, renderer: any) => void
27 return renderMedia(file, elem, opts, callback)
30 function renderMedia (file, elem: HTMLVideoElement, opts: RenderMediaOptions, callback: (err: Error, renderer?: any) => void) {
31 const extension = extname(file.name).toLowerCase()
32 let preparedElem = undefined
37 if (VIDEOSTREAM_EXTS.indexOf(extension) >= 0) {
38 renderer = useVideostream()
40 renderer = useMediaSource()
46 function useVideostream () {
48 preparedElem.addEventListener('error', function onError () {
49 preparedElem.removeEventListener('error', onError)
51 return fallbackToMediaSource()
53 preparedElem.addEventListener('loadstart', onLoadStart)
54 return videostream(file, preparedElem)
57 function useMediaSource (useVP9 = false) {
58 const codecs = getCodec(file.name, useVP9)
61 preparedElem.addEventListener('error', function onError(err) {
62 // Try with vp9 before returning an error
63 if (codecs.indexOf('vp8') !== -1) {
64 preparedElem.removeEventListener('error', onError)
66 return fallbackToMediaSource(true)
71 preparedElem.addEventListener('loadstart', onLoadStart)
73 const wrapper = new MediaElementWrapper(preparedElem)
74 const writable = wrapper.createWriteStream(codecs)
75 file.createReadStream().pipe(writable)
77 if (currentTime) preparedElem.currentTime = currentTime
82 function fallbackToMediaSource (useVP9 = false) {
83 if (useVP9 === true) console.log('Falling back to media source with VP9 enabled.')
84 else console.log('Falling back to media source..')
86 useMediaSource(useVP9)
89 function prepareElem () {
90 if (preparedElem === undefined) {
93 preparedElem.addEventListener('progress', function () {
94 currentTime = elem.currentTime
99 function onLoadStart () {
100 preparedElem.removeEventListener('loadstart', onLoadStart)
101 if (opts.autoplay) preparedElem.play()
103 callback(null, renderer)
107 function validateFile (file) {
109 throw new Error('file cannot be null or undefined')
111 if (typeof file.name !== 'string') {
112 throw new Error('missing or invalid file.name property')
114 if (typeof file.createReadStream !== 'function') {
115 throw new Error('missing or invalid file.createReadStream property')
119 function getCodec (name: string, useVP9 = false) {
120 const ext = extname(name).toLowerCase()
121 if (ext === '.mp4') {
122 return 'video/mp4; codecs="avc1.640029, mp4a.40.5"'
125 if (ext === '.webm') {
126 if (useVP9 === true) return 'video/webm; codecs="vp9, opus"'
128 return 'video/webm; codecs="vp8, vorbis"'