@@ -818,6 +818,7 @@ fileSequence8.m4s
818
818
startCC : 10 , // w/o disco-sequence incremented
819
819
endCC : 11 ,
820
820
} ) ;
821
+ expect ( details2 . fragments [ 0 ] ) . to . include ( { sn : 102 , cc : 11 } ) ;
821
822
expect ( details2 . fragmentHint ) . to . include ( { sn : 109 , cc : 12 } ) ;
822
823
expect ( mergedSequence1 ) . to . equal (
823
824
'102-11,103-11,104-11,105-11,106-11,107-11,108-11' ,
@@ -845,13 +846,171 @@ fileSequence8.m4s
845
846
startCC : 11 , // w/ disco-sequence incremented
846
847
endCC : 11 ,
847
848
} ) ;
848
- expect ( details2 . fragmentHint ) . to . include ( { sn : 109 , cc : 12 } ) ;
849
+ expect ( details3 . fragments [ 0 ] ) . to . include ( { sn : 102 , cc : 11 } ) ;
850
+ expect ( details3 . fragmentHint ) . to . include ( { sn : 109 , cc : 12 } ) ;
849
851
expect ( mergedSequence2 ) . to . equal (
850
852
'102-11,103-11,104-11,105-11,106-11,107-11,108-11' ,
851
853
) ;
852
854
expect ( details3 . playlistParsingError ) . to . be . null ;
853
855
} ) ;
854
856
857
+ it ( 'handles delta Playlist updates with discontinuities and parts and no sequence change (missing PDT)' , function ( ) {
858
+ const playlist = `#EXTM3U
859
+ #EXT-X-VERSION:10
860
+ #EXT-X-INDEPENDENT-SEGMENTS
861
+ #EXT-X-TARGETDURATION:3
862
+ #EXT-X-SERVER-CONTROL:CAN-BLOCK-RELOAD=YES,PART-HOLD-BACK=3.126000,CAN-SKIP-UNTIL=20.0
863
+ #EXT-X-PART-INF:PART-TARGET=1.000000
864
+ #EXT-X-MEDIA-SEQUENCE:0
865
+ #EXT-X-DISCONTINUITY-SEQUENCE:0
866
+ #EXT-X-DATERANGE:ID="nmss-daterange",START-DATE="2025-05-22T07:59:38.809Z"
867
+
868
+ #EXT-X-MAP:URI="720p_0_0.m4s"
869
+ #EXTINF:2.500000,
870
+ 720p_0.m4v
871
+ #EXTINF:2.500000,
872
+ 720p_1.m4v
873
+ #EXTINF:2.500000,
874
+ 720p_2.m4v
875
+ #EXTINF:2.184667,
876
+ #EXT-X-DISCONTINUITY
877
+ #EXT-X-MAP:URI="720p_0_1.m4s
878
+ #EXTINF:2.500000,
879
+ 720p_3.m4v
880
+ #EXTINF:2.500000,
881
+ 720p_4.m4v
882
+ #EXTINF:2.500000,
883
+ 720p_5.m4v
884
+ #EXTINF:2.500000,
885
+ 720p_6.m4v
886
+ #EXTINF:2.500000,
887
+ 720p_7.m4v
888
+ #EXTINF:2.500000,
889
+ 720p_8.m4v
890
+ #EXT-X-PART:DURATION=1.000000,URI="720p_9_0.m4v",INDEPENDENT=YES
891
+ #EXT-X-PART:DURATION=1.000000,URI="720p_9_1.m4v"
892
+ #EXT-X-PART:DURATION=0.500000,URI="720p_9_2.m4v"
893
+ #EXTINF:2.500000,
894
+ 720p_9.m4v
895
+ #EXT-X-PART:DURATION=1.000000,URI="720p_10_0.m4v",INDEPENDENT=YES
896
+ #EXT-X-PART:DURATION=1.000000,URI="720p_10_1.m4v"
897
+ #EXT-X-PART:DURATION=0.500000,URI="720p_10_2.m4v"
898
+ #EXTINF:2.500000,
899
+ 720p_10.m4v
900
+ #EXT-X-PART:DURATION=1.000000,URI="720p_11_0.m4v",INDEPENDENT=YES
901
+ #EXT-X-PART:DURATION=1.000000,URI="720p_11_1.m4v"
902
+ #EXT-X-PART:DURATION=0.500000,URI="720p_11_2.m4v"
903
+ #EXTINF:2.500000,
904
+ 720p_11.m4v
905
+ #EXT-X-PART:DURATION=1.000000,URI="720p_12_0.m4v",INDEPENDENT=YES
906
+ #EXT-X-PART:DURATION=1.000000,URI="720p_12_0.m4v"
907
+ #EXT-X-PRELOAD-HINT:TYPE=PART,URI="720p_12_2.m4v"` ;
908
+ const deltaUpdate = `#EXTM3U
909
+ #EXT-X-VERSION:10
910
+ #EXT-X-INDEPENDENT-SEGMENTS
911
+ #EXT-X-TARGETDURATION:3
912
+ #EXT-X-SERVER-CONTROL:CAN-BLOCK-RELOAD=YES,PART-HOLD-BACK=3.126000,CAN-SKIP-UNTIL=20.0
913
+ #EXT-X-PART-INF:PART-TARGET=1.000000
914
+ #EXT-X-MEDIA-SEQUENCE:0
915
+ #EXT-X-DISCONTINUITY-SEQUENCE:0
916
+ #EXT-X-DATERANGE:ID="nmss-daterange",START-DATE="2025-05-22T07:59:38.809Z"
917
+ #EXT-X-SKIP:SKIPPED-SEGMENTS=4
918
+
919
+ #EXT-X-MAP:URI="720p_0_1.m4s"
920
+ 720p_4.m4v
921
+ #EXTINF:2.500000,
922
+ 720p_5.m4v
923
+ #EXTINF:2.500000,
924
+ 720p_6.m4v
925
+ #EXTINF:2.500000,
926
+ 720p_7.m4v
927
+ #EXTINF:2.500000,
928
+ 720p_8.m4v
929
+ #EXT-X-PART:DURATION=1.000000,URI="720p_9_0.m4v",INDEPENDENT=YES
930
+ #EXT-X-PART:DURATION=1.000000,URI="720p_9_1.m4v"
931
+ #EXT-X-PART:DURATION=0.500000,URI="720p_9_2.m4v"
932
+ #EXTINF:2.500000,
933
+ 720p_9.m4v
934
+ #EXT-X-PART:DURATION=1.000000,URI="720p_10_0.m4v",INDEPENDENT=YES
935
+ #EXT-X-PART:DURATION=1.000000,URI="720p_10_1.m4v"
936
+ #EXT-X-PART:DURATION=0.500000,URI="720p_10_2.m4v"
937
+ #EXTINF:2.500000,
938
+ 720p_10.m4v
939
+ #EXT-X-PART:DURATION=1.000000,URI="720p_11_0.m4v",INDEPENDENT=YES
940
+ #EXT-X-PART:DURATION=1.000000,URI="720p_11_1.m4v"
941
+ #EXT-X-PART:DURATION=0.500000,URI="720p_11_2.m4v"
942
+ #EXTINF:2.500000,
943
+ 720p_11.m4v
944
+ #EXT-X-PART:DURATION=1.000000,URI="720p_12_0.m4v",INDEPENDENT=YES
945
+ #EXT-X-PART:DURATION=1.000000,URI="720p_12_0.m4v"
946
+ #EXT-X-PART:DURATION=1.000000,URI="720p_12_0.m4v"
947
+ #EXTINF:2.500000,
948
+ 720p_12.m4v
949
+ #EXT-X-PRELOAD-HINT:TYPE=PART,URI="720p_13_0.m4` ;
950
+
951
+ const details1 = parseLevelPlaylist ( playlist ) ;
952
+ const details2 = parseLevelPlaylist ( deltaUpdate ) ;
953
+ expect ( details1 , 'details1' ) . to . include ( {
954
+ live : true ,
955
+ canSkipUntil : 20 ,
956
+ totalduration : 32 ,
957
+ startSN : 0 ,
958
+ endSN : 11 ,
959
+ fragmentStart : 0 ,
960
+ lastPartSn : 12 ,
961
+ lastPartIndex : 1 ,
962
+ startCC : 0 ,
963
+ endCC : 1 ,
964
+ } ) ;
965
+
966
+ expect (
967
+ details2 ,
968
+ 'delta w/o disco seq incremented before merging' ,
969
+ ) . to . include ( {
970
+ live : true ,
971
+ skippedSegments : 4 ,
972
+ canSkipUntil : 20 ,
973
+ totalduration : 32 ,
974
+ startSN : 0 ,
975
+ endSN : 12 ,
976
+ lastPartSn : 12 ,
977
+ lastPartIndex : 2 ,
978
+ startCC : 0 ,
979
+ endCC : 0 , // endCC of delta playlist will not be incremented until merged
980
+ } ) ;
981
+ expect (
982
+ details2 . fragments ,
983
+ 'delta w/o disco seq incremented fragments' ,
984
+ ) . to . have . lengthOf ( 13 ) ;
985
+
986
+ // This delta update does not increment discontinuity-sequence (discontinuity tag would appear before first segment)
987
+ details2 . reloaded ( details1 ) ;
988
+ expect ( details2 , 'delta w/o disco seq incremented reloaded' ) . to . include ( {
989
+ misses : 0 ,
990
+ advanced : true ,
991
+ updated : true ,
992
+ } ) ;
993
+ // discontinuity sequence numbers (frag.cc) should be carried over
994
+ mergeDetails ( details1 , details2 ) ;
995
+ const mergedSequence1 = getFragmentSequenceNumbers ( details2 ) ;
996
+ expect (
997
+ details2 ,
998
+ `delta w/o disco seq incremented merged (${ mergedSequence1 } )` ,
999
+ ) . to . include ( {
1000
+ skippedSegments : 4 ,
1001
+ deltaUpdateFailed : false ,
1002
+ startSN : 0 ,
1003
+ endSN : 12 ,
1004
+ startCC : 0 ,
1005
+ endCC : 1 , // incremented after merging
1006
+ } ) ;
1007
+ expect ( details2 . fragmentHint ) . to . include ( { sn : 13 , cc : 1 } ) ;
1008
+ expect ( mergedSequence1 ) . to . equal (
1009
+ '0-0,1-0,2-0,3-1,4-1,5-1,6-1,7-1,8-1,9-1,10-1,11-1,12-1' ,
1010
+ ) ;
1011
+ expect ( details2 . playlistParsingError ) . to . be . null ;
1012
+ } ) ;
1013
+
855
1014
it ( 'handles delta Playlist updates with multiple skip tags and removed date ranges' , function ( ) {
856
1015
const playlist = `#EXTM3U
857
1016
#EXT-X-TARGETDURATION:6
0 commit comments