47
47
import androidx .media3 .extractor .Ac4Util ;
48
48
import androidx .media3 .extractor .CeaUtil ;
49
49
import androidx .media3 .extractor .ChunkIndex ;
50
+ import androidx .media3 .extractor .ChunkIndicesWrapper ;
50
51
import androidx .media3 .extractor .Extractor ;
51
52
import androidx .media3 .extractor .ExtractorInput ;
52
53
import androidx .media3 .extractor .ExtractorOutput ;
@@ -246,6 +247,11 @@ public static ExtractorsFactory newFactory(SubtitleParser.Factory subtitleParser
246
247
// Whether extractorOutput.seekMap has been called.
247
248
private boolean haveOutputSeekMap ;
248
249
250
+ // Whether a fragmented sidx has been fully collected and output.
251
+ private boolean haveOutputSeekMapComplete ;
252
+ private final ChunkIndicesWrapper wrappingSegmentIndex = new ChunkIndicesWrapper ();
253
+ private long resetCallerSeekTo ;
254
+
249
255
/**
250
256
* @deprecated Use {@link #FragmentedMp4Extractor(SubtitleParser.Factory)} instead
251
257
*/
@@ -509,26 +515,56 @@ public void release() {
509
515
// Do nothing
510
516
}
511
517
518
+ private void readRemainingSidxAtomsIntoTrack (ExtractorInput input ) throws IOException {
519
+ enterReadingAtomHeaderState ();
520
+ while (readAtomHeader (input , true )) {
521
+ if (atomType == Mp4Box .TYPE_sidx ) {
522
+ ParsableByteArray inputArray = new ParsableByteArray ((int ) atomSize );
523
+ System .arraycopy (atomHeader .getData (), 0 , inputArray .getData (), 0 , Mp4Box .HEADER_SIZE );
524
+ input .readFully (
525
+ inputArray .getData (), Mp4Box .HEADER_SIZE , (int ) (atomSize - atomHeaderBytesRead ));
526
+ LeafBox leafBox = new LeafBox (Mp4Box .TYPE_sidx , inputArray );
527
+ Pair <Long , ChunkIndex > sidxCur = parseSidx (leafBox .data , input .getPeekPosition ());
528
+ wrappingSegmentIndex .merge (sidxCur .second );
529
+ } else {
530
+ input .skipFully ((int ) atomSize - atomHeaderBytesRead , true );
531
+ }
532
+ enterReadingAtomHeaderState ();
533
+ }
534
+ }
535
+
512
536
@ Override
513
537
public int read (ExtractorInput input , PositionHolder seekPosition ) throws IOException {
514
- while (true ) {
515
- switch (parserState ) {
516
- case STATE_READING_ATOM_HEADER :
517
- if (!readAtomHeader (input )) {
518
- reorderingSeiMessageQueue .flush ();
519
- return Extractor .RESULT_END_OF_INPUT ;
520
- }
521
- break ;
522
- case STATE_READING_ATOM_PAYLOAD :
523
- readAtomPayload (input );
524
- break ;
525
- case STATE_READING_ENCRYPTION_DATA :
526
- readEncryptionData (input );
527
- break ;
528
- default :
529
- if (readSample (input )) {
530
- return RESULT_CONTINUE ;
531
- }
538
+ try {
539
+ while (true ) {
540
+ switch (parserState ) {
541
+ case STATE_READING_ATOM_HEADER :
542
+ if (!readAtomHeader (input , false )) {
543
+ if (resetCallerSeekTo > 0 ) {
544
+ seekPosition .position = resetCallerSeekTo ;
545
+ return Extractor .RESULT_SEEK ;
546
+ } else {
547
+ reorderingSeiMessageQueue .flush ();
548
+ return Extractor .RESULT_END_OF_INPUT ;
549
+ }
550
+ }
551
+ break ;
552
+ case STATE_READING_ATOM_PAYLOAD :
553
+ readAtomPayload (input );
554
+ break ;
555
+ case STATE_READING_ENCRYPTION_DATA :
556
+ readEncryptionData (input );
557
+ break ;
558
+ default :
559
+ if (readSample (input )) {
560
+ return RESULT_CONTINUE ;
561
+ }
562
+ }
563
+ }
564
+ } finally {
565
+ if (resetCallerSeekTo > 0 ) {
566
+ seekPosition .position = resetCallerSeekTo ;
567
+ resetCallerSeekTo = 0 ;
532
568
}
533
569
}
534
570
}
@@ -538,7 +574,7 @@ private void enterReadingAtomHeaderState() {
538
574
atomHeaderBytesRead = 0 ;
539
575
}
540
576
541
- private boolean readAtomHeader (ExtractorInput input ) throws IOException {
577
+ private boolean readAtomHeader (ExtractorInput input , boolean skipAtomParse ) throws IOException {
542
578
if (atomHeaderBytesRead == 0 ) {
543
579
// Read the standard length atom header.
544
580
if (!input .readFully (atomHeader .getData (), 0 , Mp4Box .HEADER_SIZE , true )) {
@@ -573,6 +609,10 @@ private boolean readAtomHeader(ExtractorInput input) throws IOException {
573
609
"Atom size less than header length (unsupported)." );
574
610
}
575
611
612
+ if (skipAtomParse ) {
613
+ return true ;
614
+ }
615
+
576
616
long atomPosition = input .getPosition () - atomHeaderBytesRead ;
577
617
if (atomType == Mp4Box .TYPE_moof || atomType == Mp4Box .TYPE_mdat ) {
578
618
if (!haveOutputSeekMap ) {
@@ -639,7 +679,7 @@ private void readAtomPayload(ExtractorInput input) throws IOException {
639
679
@ Nullable ParsableByteArray atomData = this .atomData ;
640
680
if (atomData != null ) {
641
681
input .readFully (atomData .getData (), Mp4Box .HEADER_SIZE , atomPayloadSize );
642
- onLeafAtomRead (new LeafBox (atomType , atomData ), input .getPosition ());
682
+ onLeafAtomRead (new LeafBox (atomType , atomData ), input .getPosition (), input );
643
683
} else {
644
684
input .skipFully (atomPayloadSize );
645
685
}
@@ -653,14 +693,27 @@ private void processAtomEnded(long atomEndPosition) throws ParserException {
653
693
enterReadingAtomHeaderState ();
654
694
}
655
695
656
- private void onLeafAtomRead (LeafBox leaf , long inputPosition ) throws ParserException {
696
+ private void onLeafAtomRead (LeafBox leaf , long inputPosition , ExtractorInput input )
697
+ throws ParserException , IOException {
657
698
if (!containerAtoms .isEmpty ()) {
658
699
containerAtoms .peek ().add (leaf );
659
700
} else if (leaf .type == Mp4Box .TYPE_sidx ) {
660
701
Pair <Long , ChunkIndex > result = parseSidx (leaf .data , inputPosition );
661
- segmentIndexEarliestPresentationTimeUs = result .first ;
662
- extractorOutput .seekMap (result .second );
663
- haveOutputSeekMap = true ;
702
+
703
+ wrappingSegmentIndex .merge (result .second );
704
+ if (!haveOutputSeekMap ) {
705
+ segmentIndexEarliestPresentationTimeUs = result .first ;
706
+ extractorOutput .seekMap (result .second );
707
+ haveOutputSeekMap = true ;
708
+ } else if (!haveOutputSeekMapComplete && wrappingSegmentIndex .size () > 1 ) {
709
+ resetCallerSeekTo = inputPosition ;
710
+ try {
711
+ readRemainingSidxAtomsIntoTrack (input );
712
+ haveOutputSeekMapComplete = true ;
713
+ } finally {
714
+ extractorOutput .seekMap (wrappingSegmentIndex .toChunkIndex ());
715
+ }
716
+ }
664
717
} else if (leaf .type == Mp4Box .TYPE_emsg ) {
665
718
onEmsgLeafAtomRead (leaf .data );
666
719
}
0 commit comments