@@ -372,16 +372,44 @@ fileprivate struct HLSPlaylistStructureConstructor {
372
372
var currentSegmentDuration : CMTime = CMTime . invalid
373
373
var discontinuity = false
374
374
let tagDescriptor = self . tagDescriptor ( forTags: tags)
375
-
376
- // figure out our media sequence start (defaults to 1 if not specified)
377
- let mediaSequenceTags = tags. filter { $0. tagDescriptor == PantosTag . EXT_X_MEDIA_SEQUENCE }
378
- if mediaSequenceTags. count > 0 {
379
- assert ( mediaSequenceTags. count == 1 , " Unexpected to have more than one media sequence " )
380
- if let startMediaSequence: MediaSequence = mediaSequenceTags. first? . value ( forValueIdentifier: PantosValue . sequence) {
381
- currentMediaSequence = startMediaSequence
375
+
376
+ // collect media sequence and skip tag (if they exist) as they impact the initial media sequence value
377
+ var mediaSequenceTag : HLSTag ?
378
+ var skipTag : HLSTag ?
379
+ for tag in tags {
380
+ switch tag. tagDescriptor {
381
+ case PantosTag . EXT_X_MEDIA_SEQUENCE: mediaSequenceTag = tag
382
+ case PantosTag . EXT_X_SKIP: skipTag = tag
383
+ case PantosTag . Location:
384
+ // Both the EXT-X-MEDIA-SEQUNCE and the EXT-X-SKIP tag are expected to occur before any Media Segments.
385
+ //
386
+ // For EXT-X-MEDIA-SEQUNCE section 4.4.3.2 indicates:
387
+ // The EXT-X-MEDIA-SEQUENCE tag MUST appear before the first Media Segment in the Playlist.
388
+ //
389
+ // For EXT-X-SKIP section 4.4.5.2 indicates:
390
+ // A server produces a Playlist Delta Update (Section 6.2.5.1), by replacing tags earlier than the
391
+ // Skip Boundary with an EXT-X-SKIP tag. When replacing Media Segments, the EXT-X-SKIP tag replaces
392
+ // the segment URI lines and all Media Segment Tags tags that are applied to those segments.
393
+ //
394
+ // Exiting early at the first Location helps us avoid having to loop through the entire playlist when we
395
+ // know that the tags we're looking for MUST NOT exist.
396
+ break
397
+ default : continue
382
398
}
383
399
}
384
-
400
+
401
+ // figure out our media sequence start (defaults to 0 if not specified)
402
+ if let startMediaSequence: MediaSequence = mediaSequenceTag? . value ( forValueIdentifier: PantosValue . sequence) {
403
+ currentMediaSequence = startMediaSequence
404
+ }
405
+
406
+ // account for any skip tag (since a delta update replaces all segments earlier than the skip boundary, the
407
+ // SKIPPED-SEGMENTS value will effectively update the current media sequence value of the first segment, so safe
408
+ // to do this here and not within the looping through media group tags below).
409
+ if let skippedSegments: Int = skipTag? . value ( forValueIdentifier: PantosValue . skippedSegments) {
410
+ currentMediaSequence += skippedSegments
411
+ }
412
+
385
413
// find the "header" portion by finding the first ".mediaSegment" scoped tag
386
414
let mediaStartIndexOptional = tags. firstIndex ( where: { $0. scope ( ) == . mediaSegment } )
387
415
0 commit comments