@svta/cml-cmaf-ham 0.23.2 → 0.24.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,3 +1,98 @@
1
+ //#region src/utils/byteRange.ts
2
+ /**
3
+ * Parses a byteRange string in either HLS format (length@offset) or DASH format (start-end)
4
+ * and converts it to an object with start and end properties.
5
+ *
6
+ * @param byteRangeString - String in format "length@offset" or "start-end"
7
+ * @returns Object with start and end properties, or undefined if input is invalid
8
+ *
9
+ * @example
10
+ * ```ts
11
+ * parseByteRangeString("1234@0") // { start: 0, end: 1233 }
12
+ * parseByteRangeString("0-1233") // { start: 0, end: 1233 }
13
+ * ```
14
+ *
15
+ * @alpha
16
+ */
17
+ function parseByteRangeString(byteRangeString) {
18
+ if (!byteRangeString) return;
19
+ if (byteRangeString.includes("@")) {
20
+ const [lengthStr, offsetStr] = byteRangeString.split("@");
21
+ const length = parseInt(lengthStr, 10);
22
+ const offset = parseInt(offsetStr, 10);
23
+ if (isNaN(length) || isNaN(offset)) return;
24
+ return {
25
+ start: offset,
26
+ end: offset + length - 1
27
+ };
28
+ }
29
+ if (byteRangeString.includes("-")) {
30
+ const [startStr, endStr] = byteRangeString.split("-");
31
+ const start = parseInt(startStr, 10);
32
+ const end = parseInt(endStr, 10);
33
+ if (isNaN(start) || isNaN(end)) return;
34
+ return {
35
+ start,
36
+ end
37
+ };
38
+ }
39
+ }
40
+ /**
41
+ * Converts a ByteRangeObject to HLS format string (length@offset)
42
+ *
43
+ * @param byteRange - Object with start and end properties
44
+ * @returns String in format "length@offset"
45
+ *
46
+ * @example
47
+ * ```ts
48
+ * byteRangeToHlsString({ start: 0, end: 1233 }) // "1234@0"
49
+ * ```
50
+ *
51
+ * @alpha
52
+ */
53
+ function byteRangeToHlsString(byteRange) {
54
+ if (!byteRange) return "";
55
+ return `${byteRange.end - byteRange.start + 1}@${byteRange.start}`;
56
+ }
57
+ /**
58
+ * Converts a ByteRangeObject to DASH format string (start-end)
59
+ *
60
+ * @param byteRange - Object with start and end properties
61
+ * @returns String in format "start-end"
62
+ *
63
+ * @example
64
+ * ```ts
65
+ * byteRangeToDashString({ start: 0, end: 1233 }) // "0-1233"
66
+ * ```
67
+ *
68
+ * @alpha
69
+ */
70
+ function byteRangeToDashString(byteRange) {
71
+ if (!byteRange) return "";
72
+ return `${byteRange.start}-${byteRange.end}`;
73
+ }
74
+ /**
75
+ * Converts HLS Byterange object (with length and offset) to ByteRangeObject
76
+ *
77
+ * @param byterange - Object with length and offset properties (from HLS parser)
78
+ * @returns Object with start and end properties
79
+ *
80
+ * @example
81
+ * ```ts
82
+ * hlsByterangeToByteRangeObject({ length: 1234, offset: 0 }) // { start: 0, end: 1233 }
83
+ * ```
84
+ *
85
+ * @alpha
86
+ */
87
+ function hlsByterangeToByteRangeObject(byterange) {
88
+ if (!byterange) return;
89
+ return {
90
+ start: byterange.offset,
91
+ end: byterange.offset + byterange.length - 1
92
+ };
93
+ }
94
+
95
+ //#endregion
1
96
  //#region src/mapper/dash/mapDashToHam/mapSegmentBase.ts
2
97
  /**
3
98
  * @internal
@@ -9,12 +104,19 @@
9
104
  * @returns list of ham segments
10
105
  */
11
106
  function mapSegmentBase(representation, duration) {
12
- return representation.SegmentBase?.map((segment) => {
13
- return {
107
+ let cumulativeTime = 0;
108
+ return representation.SegmentBase?.map((segment, index) => {
109
+ const startTime = cumulativeTime;
110
+ cumulativeTime += duration;
111
+ const byteRange = parseByteRangeString(segment.$.indexRange);
112
+ const result = {
113
+ id: `${representation.$.id}-segment-${index}`,
14
114
  duration,
15
115
  url: representation.BaseURL?.[0] ?? "",
16
- byteRange: segment.$.indexRange
116
+ startTime
17
117
  };
118
+ if (byteRange !== void 0) result.byteRange = byteRange;
119
+ return result;
18
120
  }) ?? [];
19
121
  }
20
122
 
@@ -48,11 +150,18 @@ function calculateDuration(duration, timescale) {
48
150
  */
49
151
  function mapSegmentList(segmentList) {
50
152
  const segments = [];
153
+ let segmentIndex = 0;
154
+ let cumulativeTime = 0;
51
155
  segmentList.map((segment) => {
52
156
  if (segment.SegmentURL) return segment.SegmentURL.forEach((segmentURL) => {
157
+ const duration = calculateDuration(segment.$.duration, segment.$.timescale);
158
+ const startTime = cumulativeTime;
159
+ cumulativeTime += duration;
53
160
  segments.push({
54
- duration: calculateDuration(segment.$.duration, segment.$.timescale),
55
- url: segmentURL.$.media ?? ""
161
+ id: `segment-${segmentIndex++}`,
162
+ duration,
163
+ url: segmentURL.$.media ?? "",
164
+ startTime
56
165
  });
57
166
  });
58
167
  });
@@ -124,10 +233,18 @@ function mapSegmentTemplate(representation, duration, segmentTemplate) {
124
233
  const numberOfSegments = getNumberOfSegments(segmentTemplate, duration);
125
234
  const init = +(segmentTemplate.$.startNumber ?? 0);
126
235
  const segments = [];
127
- for (let id = init; id < numberOfSegments + init; id++) segments.push({
128
- duration: calculateDuration(segmentTemplate.$.duration, segmentTemplate.$.timescale),
129
- url: getUrlFromTemplate(representation, segmentTemplate, id)
130
- });
236
+ let cumulativeTime = 0;
237
+ for (let id = init; id < numberOfSegments + init; id++) {
238
+ const segmentDuration = calculateDuration(segmentTemplate.$.duration, segmentTemplate.$.timescale);
239
+ const startTime = cumulativeTime;
240
+ cumulativeTime += segmentDuration;
241
+ segments.push({
242
+ id: `${representation.$.id}-segment-${id}`,
243
+ duration: segmentDuration,
244
+ url: getUrlFromTemplate(representation, segmentTemplate, id),
245
+ startTime
246
+ });
247
+ }
131
248
  return segments;
132
249
  }
133
250
 
@@ -229,8 +346,6 @@ const TEXT_SAMPLE_RATE = 1e3;
229
346
  const FRAME_RATE_NUMERATOR_30 = 30;
230
347
  const WHITE_SPACE = " ";
231
348
  const WHITE_SPACE_ENCODED = "%20";
232
- const HYPHEN_MINUS_SEPARATOR = "-";
233
- const AT_SEPARATOR = "@";
234
349
  const FRAME_RATE_SEPARATOR = "/";
235
350
  const NUMERATOR = 0;
236
351
  const DENOMINATOR = 1;
@@ -396,8 +511,13 @@ function getTimescale(track) {
396
511
  * @alpha
397
512
  */
398
513
  function encodeByteRange(track) {
399
- if (track.byteRange) return `BYTERANGE:${track.byteRange.replace(HYPHEN_MINUS_SEPARATOR, AT_SEPARATOR)}\n`;
400
- else if (track.segments?.at(0)?.byteRange) return `BYTERANGE:0@${Number(track.segments.at(0)?.byteRange?.replace(HYPHEN_MINUS_SEPARATOR, AT_SEPARATOR).split(AT_SEPARATOR)[0]) - 1}\n`;
514
+ if (track.initialization?.byteRange) {
515
+ const initByteRange = track.initialization.byteRange;
516
+ return `BYTERANGE:${initByteRange.end - initByteRange.start + 1}@${initByteRange.start}\n`;
517
+ } else if (track.segments?.at(0)?.byteRange) {
518
+ const firstSegmentByteRange = track.segments.at(0)?.byteRange;
519
+ if (firstSegmentByteRange) return `BYTERANGE:0@${firstSegmentByteRange.start - 1}\n`;
520
+ }
401
521
  return "";
402
522
  }
403
523
 
@@ -412,7 +532,7 @@ function encodeByteRange(track) {
412
532
  * @alpha
413
533
  */
414
534
  function getUrlInitialization(track) {
415
- return track.urlInitialization?.replaceAll(WHITE_SPACE, WHITE_SPACE_ENCODED) ?? "";
535
+ return track.initialization?.url?.replaceAll(WHITE_SPACE, WHITE_SPACE_ENCODED) ?? "";
416
536
  }
417
537
 
418
538
  //#endregion
@@ -441,9 +561,9 @@ function getPlaylistData(track) {
441
561
  */
442
562
  function getSegments(segments) {
443
563
  return segments.map((segment) => {
444
- const byteRange = segment.byteRange ? `#EXT-X-BYTERANGE:${segment.byteRange.replace(HYPHEN_MINUS_SEPARATOR, AT_SEPARATOR)}\n` : "";
564
+ const byteRangeStr = segment.byteRange ? `#EXT-X-BYTERANGE:${byteRangeToHlsString(segment.byteRange)}\n` : "";
445
565
  const url = segment.url.replaceAll(WHITE_SPACE, WHITE_SPACE_ENCODED);
446
- return `#EXTINF:${segment.duration},\n${byteRange}\n${url}`;
566
+ return `#EXTINF:${segment.duration},\n${byteRangeStr}\n${url}`;
447
567
  }).join("\n");
448
568
  }
449
569
 
@@ -473,13 +593,19 @@ function decodeByteRange(byteRange) {
473
593
  * @alpha
474
594
  */
475
595
  function formatSegments(segments) {
476
- return segments?.map((segment) => {
477
- const byteRange = decodeByteRange(segment?.byterange);
478
- return {
596
+ let cumulativeTime = 0;
597
+ return segments?.map((segment, index) => {
598
+ const byteRange = hlsByterangeToByteRangeObject(segment?.byterange);
599
+ const startTime = cumulativeTime;
600
+ cumulativeTime += segment.duration;
601
+ const result = {
602
+ id: `segment-${index}`,
479
603
  duration: segment.duration,
480
604
  url: segment.uri,
481
- ...byteRange && { byteRange }
605
+ startTime
482
606
  };
607
+ if (byteRange !== void 0) result.byteRange = byteRange;
608
+ return result;
483
609
  }) ?? [];
484
610
  }
485
611
 
@@ -576,9 +702,13 @@ function getInitializationUrl(adaptationSet, representation) {
576
702
  function mapTracks(adaptationSet, representation, segments, initializationUrl) {
577
703
  if (!adaptationSet) throw new Error("Error: AdaptationSet is undefined");
578
704
  const type = getContentType(adaptationSet, representation);
705
+ const codec = getCodec(adaptationSet, representation);
706
+ const mimeType = representation.$.mimeType ?? adaptationSet.$.mimeType ?? "";
579
707
  if (type === "video") return {
708
+ url: "",
580
709
  bandwidth: +(representation.$.bandwidth ?? 0),
581
- codec: getCodec(adaptationSet, representation),
710
+ codecs: codec ? [codec] : [],
711
+ mimeType,
582
712
  duration: getTrackDuration(segments),
583
713
  frameRate: getFrameRate(adaptationSet, representation),
584
714
  height: +(representation.$.height ?? 0),
@@ -590,29 +720,36 @@ function mapTracks(adaptationSet, representation, segments, initializationUrl) {
590
720
  segments,
591
721
  type,
592
722
  width: +(representation.$.width ?? 0),
593
- urlInitialization: initializationUrl
723
+ initialization: { url: initializationUrl ?? "" },
724
+ baseUrls: []
594
725
  };
595
726
  else if (type === "audio") return {
727
+ url: "",
596
728
  bandwidth: +(representation.$.bandwidth ?? 0),
597
729
  channels: getChannels(adaptationSet, representation),
598
- codec: getCodec(adaptationSet, representation),
730
+ codecs: codec ? [codec] : [],
731
+ mimeType,
599
732
  duration: getTrackDuration(segments),
600
733
  id: representation.$.id ?? "",
601
734
  language: getLanguage(adaptationSet),
602
735
  sampleRate: getSampleRate(adaptationSet, representation),
603
736
  segments,
604
737
  type,
605
- urlInitialization: initializationUrl
738
+ initialization: { url: initializationUrl ?? "" },
739
+ baseUrls: []
606
740
  };
607
741
  else return {
742
+ url: "",
608
743
  bandwidth: +(representation.$.bandwidth ?? 0),
609
- codec: getCodec(adaptationSet, representation),
744
+ codecs: codec ? [codec] : [],
745
+ mimeType,
610
746
  duration: getTrackDuration(segments),
611
747
  id: representation.$.id ?? "",
612
748
  language: getLanguage(adaptationSet),
613
749
  segments,
614
750
  type,
615
- urlInitialization: initializationUrl
751
+ initialization: { url: initializationUrl ?? "" },
752
+ baseUrls: []
616
753
  };
617
754
  }
618
755
 
@@ -636,18 +773,25 @@ function mapDashToHam(dash) {
636
773
  return mapTracks(adaptationSet, representation, mapSegments(adaptationSet, representation, duration), getInitializationUrl(adaptationSet, representation));
637
774
  });
638
775
  const group = getGroup(adaptationSet);
776
+ const contentType = getContentType(adaptationSet, adaptationSet.Representation[0]);
639
777
  if (!selectionSetGroups[group]) selectionSetGroups[group] = {
640
778
  id: group,
641
- switchingSets: []
779
+ switchingSets: [],
780
+ type: contentType
642
781
  };
643
782
  selectionSetGroups[group].switchingSets.push({
644
783
  id: adaptationSet.$.id ?? adaptationSet.ContentComponent?.at(0)?.$.id ?? group,
645
- tracks
784
+ tracks,
785
+ baseUrls: []
646
786
  });
647
787
  });
648
788
  return {
649
789
  id: presentationId,
650
- selectionSets: Object.values(selectionSetGroups)
790
+ selectionSets: Object.values(selectionSetGroups),
791
+ duration,
792
+ startTime: 0,
793
+ endTime: duration,
794
+ baseUrls: []
651
795
  };
652
796
  });
653
797
  }
@@ -656,12 +800,13 @@ function mapDashToHam(dash) {
656
800
  //#region src/mapper/dash/mapHamToDash/trackToSegmentBase.ts
657
801
  function trackToSegmentBase(track) {
658
802
  const segments = [];
659
- if (track.segments.length > 0 && track.byteRange && track.segments[0].byteRange && track.segments[0].byteRange.includes("@")) {
803
+ if (track.segments.length > 0 && track.initialization.byteRange && track.segments[0].byteRange) {
660
804
  let firstSegment = void 0;
661
- const initByteRange = track.byteRange.includes("-") ? track.byteRange.split("-")[1] : track.byteRange.includes("@") ? track.byteRange.split("@")[0] : "";
662
- const initRange = +initByteRange - 1;
805
+ const initByteRange = track.initialization.byteRange;
806
+ const initRange = initByteRange.end;
807
+ const numberFirstByteRange = track.segments[0].byteRange.start - 1;
663
808
  firstSegment = {
664
- $: { indexRange: `${initByteRange}-${+(track.segments[0].byteRange.includes("-") ? track.segments[0].byteRange.split("-")[1] : track.segments[0].byteRange.includes("@") ? track.segments[0].byteRange.split("@")[1] : "") - 1}` },
809
+ $: { indexRange: `${initByteRange.end}-${numberFirstByteRange}` },
665
810
  Initialization: [{ $: { range: `0-${initRange}` } }]
666
811
  };
667
812
  if (firstSegment && track.type === "audio") {
@@ -672,9 +817,9 @@ function trackToSegmentBase(track) {
672
817
  } else track.segments.forEach((segment) => {
673
818
  let newSegment;
674
819
  if (segment.byteRange) {
675
- const initRange = +segment.byteRange.split("-")[0] - 1;
820
+ const initRange = segment.byteRange.start - 1;
676
821
  newSegment = {
677
- $: { indexRange: segment.byteRange },
822
+ $: { indexRange: byteRangeToDashString(segment.byteRange) },
678
823
  Initialization: [{ $: { range: `0-${initRange}` } }]
679
824
  };
680
825
  }
@@ -702,7 +847,7 @@ function trackToSegmentList(track) {
702
847
  duration: (track.duration * timescale / segmentURLs.length).toString(),
703
848
  timescale: timescale.toString()
704
849
  },
705
- Initialization: [{ $: { sourceURL: track.urlInitialization } }],
850
+ Initialization: [{ $: { sourceURL: track.initialization.url } }],
706
851
  SegmentURL: segmentURLs
707
852
  });
708
853
  }
@@ -743,7 +888,7 @@ function tracksToRepresentation(tracks) {
743
888
  SegmentBase: trackToSegmentBase(track),
744
889
  SegmentList: trackToSegmentList(track)
745
890
  };
746
- representation.$.mimeType = `${track.type}/mp4`;
891
+ representation.$.mimeType = track.mimeType ?? `${track.type}/mp4`;
747
892
  if (track.type === "video") {
748
893
  const videoTrack = track;
749
894
  representation.$ = {
@@ -751,7 +896,7 @@ function tracksToRepresentation(tracks) {
751
896
  frameRate: getFrameRate$1(track),
752
897
  width: videoTrack.width.toString(),
753
898
  height: videoTrack.height.toString(),
754
- codecs: videoTrack.codec
899
+ codecs: videoTrack.codecs?.join(",")
755
900
  };
756
901
  if (videoTrack.scanType) representation.$.scanType = videoTrack.scanType;
757
902
  }
@@ -760,7 +905,7 @@ function tracksToRepresentation(tracks) {
760
905
  representation.$ = {
761
906
  ...representation.$,
762
907
  audioSamplingRate: audioTrack.sampleRate.toString(),
763
- codecs: audioTrack.codec
908
+ codecs: audioTrack.codecs?.join(",")
764
909
  };
765
910
  representation.AudioChannelConfiguration = [{ $: {
766
911
  schemeIdUri: "urn:mpeg:dash:23003:3:audio_channel_configuration:2011",
@@ -783,10 +928,10 @@ function selectionSetsToAdaptationSet(selectionsSets) {
783
928
  id: switchingSet.id,
784
929
  group: selectionSet.id,
785
930
  contentType: track?.type,
786
- mimeType: `${track?.type}/mp4`,
931
+ mimeType: track?.mimeType ?? `${track?.type}/mp4`,
787
932
  frameRate: getFrameRate$1(track),
788
933
  lang: track?.language,
789
- codecs: track?.codec
934
+ codecs: track?.codecs?.join(",")
790
935
  },
791
936
  Representation: tracksToRepresentation(switchingSet.tracks)
792
937
  };
@@ -999,12 +1144,12 @@ function hamToDash(presentation) {
999
1144
  //#region src/mapper/hls/mapHamToHls/generateManifestPlaylistPiece.ts
1000
1145
  function generateManifestPlaylistPiece(track) {
1001
1146
  const mediaSequence = 0;
1002
- const trackFileName = track.fileName ?? `${track.id}.m3u8`;
1147
+ const trackFileName = track.url ?? `${track.id}.m3u8`;
1003
1148
  let mainRef = "";
1004
1149
  let playlist = `#EXTM3U\n#EXT-X-TARGETDURATION:${track.duration / track.segments.length}\n#EXT-X-PLAYLIST-TYPE:VOD\n#EXT-X-MEDIA-SEQUENCE:${mediaSequence}\n`;
1005
1150
  if (track.type.toLowerCase() === "video") {
1006
1151
  const videoTrack = track;
1007
- mainRef += `#EXT-X-STREAM-INF:BANDWIDTH=${videoTrack.bandwidth},CODECS="${videoTrack.codec}",RESOLUTION=${videoTrack.width}x${videoTrack.height}\n${trackFileName}\n`;
1152
+ mainRef += `#EXT-X-STREAM-INF:BANDWIDTH=${videoTrack.bandwidth},CODECS="${videoTrack.codecs?.join(",") ?? ""}",RESOLUTION=${videoTrack.width}x${videoTrack.height}\n${trackFileName}\n`;
1008
1153
  playlist += getPlaylistData(videoTrack);
1009
1154
  } else if (track.type.toLowerCase() === "audio") {
1010
1155
  const audioTrack = track;
@@ -1032,7 +1177,7 @@ function mapHamToHls(presentations) {
1032
1177
  switchingSet.tracks.map((track) => {
1033
1178
  const { mainRef, playlist } = generateManifestPlaylistPiece(track);
1034
1179
  mainManifest += mainRef;
1035
- const manifestFileName = track.fileName ?? `${track.id}.m3u8`;
1180
+ const manifestFileName = track.url ?? `${track.id}.m3u8`;
1036
1181
  playlists.push({
1037
1182
  manifest: playlist,
1038
1183
  type: "hls",
@@ -1100,25 +1245,31 @@ function audioGroupsToSwitchingSets(mediaGroupsAudio, manifestPlaylists) {
1100
1245
  const map = audioParsed?.segments[0]?.map;
1101
1246
  const segments = formatSegments(audioParsed?.segments);
1102
1247
  const byteRange = decodeByteRange(map?.byterange);
1248
+ const codec = getHlsCodec("audio");
1103
1249
  audioTracks.push({
1104
1250
  id: audio,
1105
1251
  type: "audio",
1106
- fileName: uri,
1107
- codec: getHlsCodec("audio"),
1252
+ url: uri,
1253
+ codecs: codec ? [codec] : [],
1254
+ mimeType: "audio/mp4",
1108
1255
  duration: getDuration(audioParsed, segments),
1109
1256
  language,
1110
1257
  bandwidth: 0,
1111
1258
  segments,
1112
1259
  sampleRate: 0,
1113
1260
  channels: 2,
1114
- ...byteRange && { byteRange },
1115
- ...map?.uri && { urlInitialization: map?.uri }
1261
+ initialization: {
1262
+ url: map?.uri ?? "",
1263
+ ...byteRange && { byteRange }
1264
+ },
1265
+ baseUrls: []
1116
1266
  });
1117
1267
  }
1118
1268
  }
1119
1269
  audioSwitchingSets.push({
1120
1270
  id: "audio",
1121
- tracks: audioTracks
1271
+ tracks: audioTracks,
1272
+ baseUrls: []
1122
1273
  });
1123
1274
  return audioSwitchingSets;
1124
1275
  }
@@ -1134,21 +1285,26 @@ function subtitleGroupsToSwitchingSets(mediaGroupsSubtitles, manifestPlaylists)
1134
1285
  const { language, uri } = encodings[subtitle];
1135
1286
  const subtitleParsed = parseHlsManifest(manifestPlaylists.shift()?.manifest);
1136
1287
  const segments = formatSegments(subtitleParsed?.segments);
1288
+ const codec = getHlsCodec("text");
1137
1289
  textTracks.push({
1138
1290
  id: subtitle,
1139
1291
  type: "text",
1140
- fileName: uri,
1141
- codec: getHlsCodec("text"),
1292
+ url: uri,
1293
+ codecs: codec ? [codec] : [],
1294
+ mimeType: "text/vtt",
1142
1295
  duration: getDuration(subtitleParsed, segments),
1143
1296
  language,
1144
1297
  bandwidth: 0,
1145
- segments
1298
+ segments,
1299
+ initialization: { url: "" },
1300
+ baseUrls: []
1146
1301
  });
1147
1302
  }
1148
1303
  }
1149
1304
  subtitleSwitchingSets.push({
1150
1305
  id: "text",
1151
- tracks: textTracks
1306
+ tracks: textTracks,
1307
+ baseUrls: []
1152
1308
  });
1153
1309
  return subtitleSwitchingSets;
1154
1310
  }
@@ -1165,13 +1321,15 @@ function videoPlaylistsToSwitchingSets(playlists, manifestPlaylists) {
1165
1321
  const { LANGUAGE, CODECS, BANDWIDTH } = playlist.attributes;
1166
1322
  const map = parsedHlsManifest?.segments?.at(0)?.map;
1167
1323
  const byteRange = decodeByteRange(map?.byterange);
1324
+ const codec = getHlsCodec("video", CODECS);
1168
1325
  videoTracks.push({
1169
1326
  id: `video-${videoTrackId++}`,
1170
1327
  type: "video",
1171
- fileName: playlist.uri,
1172
- codec: getHlsCodec("video", CODECS),
1328
+ url: playlist.uri,
1329
+ codecs: codec ? [codec] : [],
1330
+ mimeType: "video/mp4",
1173
1331
  duration: getDuration(parsedHlsManifest, segments),
1174
- language: LANGUAGE ?? "und",
1332
+ ...LANGUAGE && { language: LANGUAGE },
1175
1333
  bandwidth: BANDWIDTH,
1176
1334
  segments,
1177
1335
  width: playlist.attributes.RESOLUTION.width,
@@ -1183,13 +1341,17 @@ function videoPlaylistsToSwitchingSets(playlists, manifestPlaylists) {
1183
1341
  par: "",
1184
1342
  sar: "",
1185
1343
  scanType: "",
1186
- ...byteRange && { byteRange },
1187
- ...map?.uri && { urlInitialization: map?.uri }
1344
+ initialization: {
1345
+ url: map?.uri ?? "",
1346
+ ...byteRange && { byteRange }
1347
+ },
1348
+ baseUrls: []
1188
1349
  });
1189
1350
  });
1190
1351
  switchingSetVideos.push({
1191
1352
  id: `video`,
1192
- tracks: videoTracks
1353
+ tracks: videoTracks,
1354
+ baseUrls: []
1193
1355
  });
1194
1356
  return switchingSetVideos;
1195
1357
  }
@@ -1216,20 +1378,29 @@ function mapHlsToHam(manifest) {
1216
1378
  let selectionSetId = 0;
1217
1379
  if (audioSwitchingSets.length > 0) selectionSets.push({
1218
1380
  id: (selectionSetId++).toString(),
1219
- switchingSets: audioSwitchingSets
1381
+ switchingSets: audioSwitchingSets,
1382
+ type: "audio"
1220
1383
  });
1221
1384
  if (subtitleSwitchingSets.length > 0) selectionSets.push({
1222
1385
  id: (selectionSetId++).toString(),
1223
- switchingSets: subtitleSwitchingSets
1386
+ switchingSets: subtitleSwitchingSets,
1387
+ type: "text"
1224
1388
  });
1225
1389
  if (videoSwitchingSets.length > 0) selectionSets.push({
1226
1390
  id: (selectionSetId++).toString(),
1227
- switchingSets: videoSwitchingSets
1391
+ switchingSets: videoSwitchingSets,
1392
+ type: "video"
1228
1393
  });
1394
+ let duration = 0;
1395
+ for (const selectionSet of selectionSets) for (const switchingSet of selectionSet.switchingSets) for (const track of switchingSet.tracks) if (track.duration > duration) duration = track.duration;
1229
1396
  let presentationId = 0;
1230
1397
  return [{
1231
1398
  id: (presentationId++).toString(),
1232
- selectionSets
1399
+ selectionSets,
1400
+ duration,
1401
+ startTime: 0,
1402
+ endTime: duration,
1403
+ baseUrls: []
1233
1404
  }];
1234
1405
  }
1235
1406
 
@@ -1498,9 +1669,9 @@ function _validateVideoTrack(videoTrack, switchingSetId, prevValidation) {
1498
1669
  errorMessages: []
1499
1670
  };
1500
1671
  const moreInformation = switchingSetId ? ` in the switching set with id = ${switchingSetId}` : ".";
1501
- if (!videoTrack.codec) {
1672
+ if (!videoTrack.codecs || videoTrack.codecs.length === 0) {
1502
1673
  validation.status = false;
1503
- validation.errorMessages.push(`VideoTrack with id: ${videoTrack.id} does not have codec${moreInformation}`);
1674
+ validation.errorMessages.push(`VideoTrack with id: ${videoTrack.id} does not have codecs${moreInformation}`);
1504
1675
  }
1505
1676
  return validation;
1506
1677
  }
@@ -1524,9 +1695,9 @@ function _validateAudioTrack(audioTrack, switchingSetId, prevValidation) {
1524
1695
  errorMessages: []
1525
1696
  };
1526
1697
  const moreInformation = switchingSetId ? ` in the switching set with id = ${switchingSetId}` : ".";
1527
- if (!audioTrack.codec) {
1698
+ if (!audioTrack.codecs || audioTrack.codecs.length === 0) {
1528
1699
  validation.status = false;
1529
- validation.errorMessages.push(`AudioTrack with id: ${audioTrack.id} does not have codec${moreInformation}`);
1700
+ validation.errorMessages.push(`AudioTrack with id: ${audioTrack.id} does not have codecs${moreInformation}`);
1530
1701
  }
1531
1702
  return validation;
1532
1703
  }
@@ -1802,5 +1973,5 @@ function validatePresentation(presentation) {
1802
1973
  }
1803
1974
 
1804
1975
  //#endregion
1805
- export { calculateDuration, dashToHam, decodeByteRange, encodeByteRange, formatSegments, getChannels, getCodec, getContentType, getDashParser, getDashSerializer, getDuration, getFrameRate, getGroup, getHlsCodec, getHlsParser, getLanguage, getNumberOfSegments, getPlaylistData, getPresentationId, getSampleRate, getSar, getSegments, getTimescale, getTrackDuration, getTracksFromPresentation, getTracksFromSelectionSet, getTracksFromSwitchingSet, getUrlFromTemplate, getUrlInitialization, hamToDash, hamToHls, hlsToHam, iso8601DurationToNumber, mapSegmentBase, mapSegmentList, mapSegmentTemplate, mapSegments, numberToIso8601Duration, parseDashManifest, parseHlsManifest, serializeDashManifest, setDashParser, setDashSerializer, setHlsParser, validatePresentation, validateSegment, validateSegments, validateSelectionSet, validateSelectionSets, validateSwitchingSet, validateSwitchingSets, validateTrack, validateTracks };
1976
+ export { byteRangeToDashString, byteRangeToHlsString, calculateDuration, dashToHam, decodeByteRange, encodeByteRange, formatSegments, getChannels, getCodec, getContentType, getDashParser, getDashSerializer, getDuration, getFrameRate, getGroup, getHlsCodec, getHlsParser, getLanguage, getNumberOfSegments, getPlaylistData, getPresentationId, getSampleRate, getSar, getSegments, getTimescale, getTrackDuration, getTracksFromPresentation, getTracksFromSelectionSet, getTracksFromSwitchingSet, getUrlFromTemplate, getUrlInitialization, hamToDash, hamToHls, hlsByterangeToByteRangeObject, hlsToHam, iso8601DurationToNumber, mapSegmentBase, mapSegmentList, mapSegmentTemplate, mapSegments, numberToIso8601Duration, parseByteRangeString, parseDashManifest, parseHlsManifest, serializeDashManifest, setDashParser, setDashSerializer, setHlsParser, validatePresentation, validateSegment, validateSegments, validateSelectionSet, validateSelectionSets, validateSwitchingSet, validateSwitchingSets, validateTrack, validateTracks };
1806
1977
  //# sourceMappingURL=index.js.map