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
36 if (VIDEOSTREAM_EXTS.indexOf(extension) >= 0) {
37 renderer = useVideostream()
39 renderer = useMediaSource()
42 function useVideostream () {
44 preparedElem.addEventListener('error', function onError () {
45 preparedElem.removeEventListener('error', onError)
47 return fallbackToMediaSource()
49 preparedElem.addEventListener('loadstart', onLoadStart)
50 return videostream(file, preparedElem)
53 function useMediaSource (useVP9 = false) {
54 const codecs = getCodec(file.name, useVP9)
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)
62 return fallbackToMediaSource(true)
67 preparedElem.addEventListener('loadstart', onLoadStart)
69 const wrapper = new MediaElementWrapper(preparedElem)
70 const writable = wrapper.createWriteStream(codecs)
71 file.createReadStream().pipe(writable)
73 if (currentTime) preparedElem.currentTime = currentTime
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..')
82 useMediaSource(useVP9)
85 function prepareElem () {
86 if (preparedElem === undefined) {
89 preparedElem.addEventListener('progress', function () {
90 currentTime = elem.currentTime
95 function onLoadStart () {
96 preparedElem.removeEventListener('loadstart', onLoadStart)
97 if (opts.autoplay) preparedElem.play()
99 callback(null, renderer)
103 function validateFile (file) {
105 throw new Error('file cannot be null or undefined')
107 if (typeof file.name !== 'string') {
108 throw new Error('missing or invalid file.name property')
110 if (typeof file.createReadStream !== 'function') {
111 throw new Error('missing or invalid file.createReadStream property')
115 function getCodec (name: string, useVP9 = false) {
116 const ext = extname(name).toLowerCase()
117 if (ext === '.mp4') {
118 return 'video/mp4; codecs="avc1.640029, mp4a.40.5"'
121 if (ext === '.webm') {
122 if (useVP9 === true) return 'video/webm; codecs="vp9, opus"'
124 return 'video/webm; codecs="vp8, vorbis"'