mirror of
https://github.com/TeamPiped/Piped.git
synced 2025-10-14 19:38:18 +00:00
Commit everything.
This commit is contained in:
189
src/utils/DashUtils.js
Normal file
189
src/utils/DashUtils.js
Normal file
@@ -0,0 +1,189 @@
|
||||
// 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;
|
Reference in New Issue
Block a user