PeerTube/client/src/assets/player/shared/webtorrent/video-renderer.ts

135 lines
3.3 KiB
TypeScript

// Thanks: https://github.com/feross/render-media
const MediaElementWrapper = require('mediasource')
import { logger } from '@root-helpers/logger'
import { extname } from 'path'
const Videostream = require('videostream')
const VIDEOSTREAM_EXTS = [
'.m4a',
'.m4v',
'.mp4'
]
type RenderMediaOptions = {
controls: boolean
autoplay: boolean
}
function renderVideo (
file: any,
elem: HTMLVideoElement,
opts: RenderMediaOptions,
callback: (err: Error, renderer: any) => void
) {
validateFile(file)
return renderMedia(file, elem, opts, callback)
}
function renderMedia (file: any, elem: HTMLVideoElement, opts: RenderMediaOptions, callback: (err: Error, renderer?: any) => void) {
const extension = extname(file.name).toLowerCase()
let preparedElem: any
let currentTime = 0
let renderer: any
try {
if (VIDEOSTREAM_EXTS.includes(extension)) {
renderer = useVideostream()
} else {
renderer = useMediaSource()
}
} catch (err) {
return callback(err)
}
function useVideostream () {
prepareElem()
preparedElem.addEventListener('error', function onError (err: Error) {
preparedElem.removeEventListener('error', onError)
return callback(err)
})
preparedElem.addEventListener('loadstart', onLoadStart)
return new Videostream(file, preparedElem)
}
function useMediaSource (useVP9 = false) {
const codecs = getCodec(file.name, useVP9)
prepareElem()
preparedElem.addEventListener('error', function onError (err: Error) {
preparedElem.removeEventListener('error', onError)
// Try with vp9 before returning an error
if (codecs.includes('vp8')) 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) logger.info('Falling back to media source with VP9 enabled.')
else logger.info('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: any) {
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
}