mirror of https://github.com/Chocobozzz/PeerTube
135 lines
3.3 KiB
TypeScript
135 lines
3.3 KiB
TypeScript
// 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('canplay', onLoadStart)
|
|
return videostream(file, preparedElem)
|
|
}
|
|
|
|
function useMediaSource (useVP9 = false) {
|
|
const codecs = getCodec(file.name, useVP9)
|
|
|
|
prepareElem()
|
|
preparedElem.addEventListener('error', function onError (err) {
|
|
preparedElem.removeEventListener('error', onError)
|
|
|
|
// Try with vp9 before returning an error
|
|
if (codecs.indexOf('vp8') !== -1) return fallbackToMediaSource(true)
|
|
|
|
return callback(err)
|
|
})
|
|
preparedElem.addEventListener('canplay', 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('canplay', 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
|
|
}
|