@@ -66,6 +66,9 @@ const getIteratedSequence = (oldPlaylist, newPlaylist) => {
66
66
return actual ;
67
67
} ;
68
68
69
+ const getFragmentSequenceNumbers = ( details : LevelDetails ) =>
70
+ details . fragments . map ( ( f ) => `${ f ?. sn } -${ f ?. cc } ` ) . join ( ',' ) ;
71
+
69
72
describe ( 'LevelHelper Tests' , function ( ) {
70
73
let sandbox ;
71
74
beforeEach ( function ( ) {
@@ -180,9 +183,6 @@ describe('LevelHelper Tests', function () {
180
183
} ) ;
181
184
182
185
describe ( 'mergeDetails' , function ( ) {
183
- const getFragmentSequenceNumbers = ( details : LevelDetails ) =>
184
- details . fragments . map ( ( f ) => `${ f ?. sn } -${ f ?. cc } ` ) . join ( ',' ) ;
185
-
186
186
it ( 'transfers start times where segments overlap, and extrapolates the start of any new segment' , function ( ) {
187
187
const oldPlaylist = generatePlaylist ( [ 1 , 2 , 3 , 4 ] ) ; // start times: 0, 5, 10, 15
188
188
const newPlaylist = generatePlaylist ( [ 2 , 3 , 4 , 5 ] ) ;
@@ -704,6 +704,7 @@ fileSequence7.m4s
704
704
#EXT-X-SERVER-CONTROL:CAN-BLOCK-RELOAD=YES,CAN-SKIP-UNTIL=24,PART-HOLD-BACK=3.0
705
705
#EXT-X-MEDIA-SEQUENCE:102
706
706
#EXT-X-DISCONTINUITY-SEQUENCE:10
707
+ #EXT-X-DISCONTINUITY
707
708
#EXT-X-SKIP:SKIPPED-SEGMENTS=4
708
709
#EXTINF:6,
709
710
fileSequence6.m4s
@@ -768,8 +769,8 @@ fileSequence8.m4s
768
769
endSN : 108 ,
769
770
lastPartSn : 109 ,
770
771
lastPartIndex : 0 ,
771
- startCC : 10 , // w/o disco-sequence incremented
772
- endCC : 11 , // end CC reflects delta details until merged with previous
772
+ startCC : 10 ,
773
+ endCC : 12 ,
773
774
} ) ;
774
775
expect (
775
776
details2 . fragments ,
@@ -979,6 +980,151 @@ fileSequence6.ts`;
979
980
mergeDetails ( details , details ) ;
980
981
expect ( details . fragmentStart ) . to . equal ( 10 ) ;
981
982
} ) ;
983
+
984
+ it ( 'aligns discontinuities based on media sequence number' , function ( ) {
985
+ const playlist1 = `#EXTM3U
986
+ #EXT-X-VERSION:6
987
+ #EXT-X-TARGETDURATION:2
988
+ #EXT-X-MEDIA-SEQUENCE:26
989
+ #EXT-X-MAP:URI="video_init.mp4"
990
+ #EXTINF:2.000,
991
+ video_26.m4s
992
+ #EXT-X-DISCONTINUITY
993
+ #EXTINF:2.000,
994
+ video_27.m4s
995
+ #EXT-X-DISCONTINUITY
996
+ #EXTINF:2.000,
997
+ video_28.m4s
998
+ #EXT-X-DISCONTINUITY
999
+ #EXTINF:2.000,
1000
+ video_29.m4s
1001
+ #EXTINF:2.000,
1002
+ video_30.m4s
1003
+ #EXT-X-DISCONTINUITY
1004
+ #EXTINF:2.000,
1005
+ video_31.m4s` ;
1006
+ const playlist2 = `#EXTM3U
1007
+ #EXT-X-VERSION:6
1008
+ #EXT-X-TARGETDURATION:2
1009
+ #EXT-X-MEDIA-SEQUENCE:28
1010
+ #EXT-X-MAP:URI="video_init.mp4"
1011
+ #EXTINF:2.000,
1012
+ video_28.m4s
1013
+ #EXT-X-DISCONTINUITY
1014
+ #EXTINF:2.000,
1015
+ video_29.m4s
1016
+ #EXTINF:2.000,
1017
+ video_30.m4s
1018
+ #EXT-X-DISCONTINUITY
1019
+ #EXTINF:2.000,
1020
+ video_31.m4s
1021
+ #EXT-X-DISCONTINUITY
1022
+ #EXTINF:2.000,
1023
+ video_32.m4s` ;
1024
+ const oldPlaylist = parseLevelPlaylist ( playlist1 ) ;
1025
+ const newPlaylist = parseLevelPlaylist ( playlist2 ) ;
1026
+
1027
+ expect ( oldPlaylist ) . to . include ( {
1028
+ startSN : 26 ,
1029
+ startCC : 0 ,
1030
+ endCC : 4 ,
1031
+ } ) ;
1032
+ expect ( newPlaylist ) . to . include ( {
1033
+ startSN : 28 ,
1034
+ startCC : 0 ,
1035
+ endCC : 3 ,
1036
+ } ) ;
1037
+
1038
+ mergeDetails ( oldPlaylist , newPlaylist ) ;
1039
+
1040
+ expect ( newPlaylist . playlistParsingError ) . to . be . null ;
1041
+ expect ( newPlaylist ) . to . include ( {
1042
+ startSN : 28 ,
1043
+ startCC : 1 ,
1044
+ endCC : 5 ,
1045
+ } ) ;
1046
+
1047
+ const mergedSequence = getFragmentSequenceNumbers ( newPlaylist ) ;
1048
+ expect ( mergedSequence ) . to . equal ( '28-2,29-3,30-3,31-4,32-5' ) ;
1049
+ } ) ;
1050
+
1051
+ it ( 'aligns discontinuities based on media sequence number (#7163)' , function ( ) {
1052
+ const playlist1 = `#EXTM3U
1053
+ #EXT-X-VERSION:7
1054
+ #EXT-X-TARGETDURATION:2
1055
+ #EXT-X-MEDIA-SEQUENCE:0
1056
+ #EXT-X-DISCONTINUITY
1057
+ #EXT-X-MAP:URI="getMP4InitFragment.mp4"
1058
+ #EXT-X-PROGRAM-DATE-TIME:2025-04-08T14:20:25.166Z
1059
+ #EXTINF:1.875,
1060
+ getMP4MediaFragment.mp4?FragmentNumber=91343852333398821492057832144523283530704043374
1061
+ #EXT-X-DISCONTINUITY
1062
+ #EXT-X-PROGRAM-DATE-TIME:2025-04-08T14:20:27.155Z
1063
+ #EXTINF:1.73,
1064
+ getMP4MediaFragment.mp4?FragmentNumber=91343852333398821497009592301664805164162363767
1065
+ #EXT-X-DISCONTINUITY
1066
+ #EXT-X-PROGRAM-DATE-TIME:2025-04-08T14:20:28.696Z
1067
+ #EXTINF:1.698,
1068
+ getMP4MediaFragment.mp4?FragmentNumber=91343852333398821501961352458806326677392019605
1069
+ #EXT-X-DISCONTINUITY
1070
+ #EXT-X-PROGRAM-DATE-TIME:2025-04-08T14:20:30.387Z
1071
+ #EXTINF:1.742,
1072
+ getMP4MediaFragment.mp4?FragmentNumber=91343852333398821506913112615947848230969811191
1073
+ #EXT-X-DISCONTINUITY
1074
+ #EXT-X-PROGRAM-DATE-TIME:2025-04-08T14:20:32.335Z
1075
+ #EXTINF:1.685,
1076
+ getMP4MediaFragment.mp4?FragmentNumber=91343852333398821511864872773089369853347903342` ;
1077
+ const playlist2 = `#EXTM3U
1078
+ #EXT-X-VERSION:7
1079
+ #EXT-X-TARGETDURATION:2
1080
+ #EXT-X-MEDIA-SEQUENCE:1
1081
+ #EXT-X-DISCONTINUITY
1082
+ #EXT-X-MAP:URI="getMP4InitFragment.mp4"
1083
+ #EXT-X-PROGRAM-DATE-TIME:2025-04-08T14:20:27.155Z
1084
+ #EXTINF:1.73,
1085
+ getMP4MediaFragment.mp4?FragmentNumber=91343852333398821497009592301664805164162363767
1086
+ #EXT-X-DISCONTINUITY
1087
+ #EXT-X-PROGRAM-DATE-TIME:2025-04-08T14:20:28.696Z
1088
+ #EXTINF:1.698,
1089
+ getMP4MediaFragment.mp4?FragmentNumber=91343852333398821501961352458806326677392019605
1090
+ #EXT-X-DISCONTINUITY
1091
+ #EXT-X-PROGRAM-DATE-TIME:2025-04-08T14:20:30.387Z
1092
+ #EXTINF:1.742,
1093
+ getMP4MediaFragment.mp4?FragmentNumber=91343852333398821506913112615947848230969811191
1094
+ #EXT-X-DISCONTINUITY
1095
+ #EXT-X-PROGRAM-DATE-TIME:2025-04-08T14:20:32.335Z
1096
+ #EXTINF:1.685,
1097
+ getMP4MediaFragment.mp4?FragmentNumber=91343852333398821511864872773089369853347903342
1098
+ #EXT-X-DISCONTINUITY
1099
+ #EXT-X-PROGRAM-DATE-TIME:2025-04-08T14:20:33.806Z
1100
+ #EXTINF:1.677,
1101
+ getMP4MediaFragment.mp4?FragmentNumber=91343852333398821516816632930230891347979802607` ;
1102
+ const oldPlaylist = parseLevelPlaylist ( playlist1 ) ;
1103
+ const newPlaylist = parseLevelPlaylist ( playlist2 ) ;
1104
+
1105
+ expect ( oldPlaylist ) . to . include ( {
1106
+ startSN : 0 ,
1107
+ startCC : 0 ,
1108
+ endCC : 5 ,
1109
+ } ) ;
1110
+ expect ( newPlaylist ) . to . include ( {
1111
+ startSN : 1 ,
1112
+ startCC : 0 ,
1113
+ endCC : 5 ,
1114
+ } ) ;
1115
+
1116
+ mergeDetails ( oldPlaylist , newPlaylist ) ;
1117
+
1118
+ expect ( newPlaylist . playlistParsingError ) . to . be . null ;
1119
+ expect ( newPlaylist ) . to . include ( {
1120
+ startSN : 1 ,
1121
+ startCC : 1 ,
1122
+ endCC : 6 ,
1123
+ } ) ;
1124
+
1125
+ const mergedSequence = getFragmentSequenceNumbers ( newPlaylist ) ;
1126
+ expect ( mergedSequence ) . to . equal ( '1-2,2-3,3-4,4-5,5-6' ) ;
1127
+ } ) ;
982
1128
} ) ;
983
1129
984
1130
describe ( 'computeReloadInterval' , function ( ) {
0 commit comments