Piped/src/utils/DashUtils.js

190 lines
6.7 KiB
JavaScript
Raw Normal View History

2021-02-24 09:35:41 +00:00
// Based of https://github.com/GilgusMaximus/yt-dash-manifest-generator/blob/master/src/DashGenerator.js
const xml = require('xml-js')
const DashUtils = {
generate_dash_file_from_formats(VideoFormats, VideoLength) {
const generatedJSON = this.generate_xmljs_json_from_data(VideoFormats, VideoLength)
return xml.json2xml(generatedJSON)
},
generate_xmljs_json_from_data(VideoFormatArray, VideoLength) {
const convertJSON = {
"declaration": {
"attributes": {
"version": "1.0",
"encoding": "utf-8"
}
},
"elements": [
{
"type": "element",
"name": "MPD",
"attributes": {
"xmlns": "urn:mpeg:dash:schema:mpd:2011",
"profiles": "urn:mpeg:dash:profile:full:2011",
"minBufferTime": "PT1.5S",
"type": "static",
"mediaPresentationDuration": `PT${VideoLength}S`
},
"elements": [
{
"type": "element",
"name": "Period",
"elements": this.generate_adaptation_set(VideoFormatArray)
}
]
}
]
}
return convertJSON
},
generate_adaptation_set(VideoFormatArray) {
const adaptationSets = []
const mimeTypes = []
const mimeObjects = [[]]
// sort the formats by mime types
VideoFormatArray.forEach((videoFormat) => {
// the dual formats should not be used
if (videoFormat.mimeType.indexOf("video") != -1 && !videoFormat.videoOnly) {
return
}
// if these properties are not available, then we skip it because we cannot set these properties
//if (!(videoFormat.hasOwnProperty('initRange') && videoFormat.hasOwnProperty('indexRange'))) {
// return
//}
const mimeType = videoFormat.mimeType
const mimeTypeIndex = mimeTypes.indexOf(mimeType)
if (mimeTypeIndex > -1) {
mimeObjects[mimeTypeIndex].push(videoFormat)
} else {
mimeTypes.push(mimeType)
mimeObjects.push([])
mimeObjects[mimeTypes.length - 1].push(videoFormat)
}
})
// for each MimeType generate a new Adaptation set with Representations as sub elements
for (let i = 0; i < mimeTypes.length; i++) {
let isVideoFormat = false
const adapSet = {
"type": "element",
"name": "AdaptationSet",
"attributes": {
"id": i,
"mimeType": mimeTypes[i],
"startWithSAP": "1",
"subsegmentAlignment": "true"
},
"elements": []
}
if (!mimeTypes[i].includes("audio")) {
adapSet.attributes.scanType = "progressive"
isVideoFormat = true
}
mimeObjects[i].forEach((format) => {
if (isVideoFormat) {
adapSet.elements.push(this.generate_representation_video(format))
} else {
adapSet.elements.push(this.generate_representation_audio(format))
}
})
adaptationSets.push(adapSet)
}
return adaptationSets
}, generate_representation_audio(Format) {
const representation =
{
"type": "element",
"name": "Representation",
"attributes": {
"id": Format.itag,
"codecs": Format.codec,
"bandwidth": Format.bitrate
},
"elements": [
{
"type": "element",
"name": "AudioChannelConfiguration",
"attributes": {
"schemeIdUri": "urn:mpeg:dash:23003:3:audio_channel_configuration:2011",
"value": "2"
},
},
{
"type": "element",
"name": "BaseURL",
"elements": [
{
"type": "text",
"text": Format.url
}
]
},
{
"type": "element",
"name": "SegmentBase",
"attributes": {
"indexRange": `${Format.indexStart}-${Format.indexEnd}`
},
"elements": [
{
"type": "element",
"name": "Initialization",
"attributes": {
"range": `${Format.initStart}-${Format.initEnd}`
}
}
]
}
]
}
return representation
},
generate_representation_video(Format) {
const representation =
{
"type": "element",
"name": "Representation",
"attributes": {
"id": Format.itag,
"codecs": Format.codec,
"bandwidth": Format.bitrate,
"width": Format.width,
"height": Format.height,
"maxPlayoutRate": "1",
"frameRate": Format.fps
},
"elements": [
{
"type": "element",
"name": "BaseURL",
"elements": [
{
"type": "text",
"text": Format.url
}
]
},
{
"type": "element",
"name": "SegmentBase",
"attributes": {
"indexRange": `${Format.indexStart}-${Format.indexEnd}`
},
"elements": [
{
"type": "element",
"name": "Initialization",
"attributes": {
"range": `${Format.initStart}-${Format.initEnd}`
}
}
]
}
]
}
return representation
}
}
export default DashUtils;