Skip to content

Commit d7534e9

Browse files
committed
Changes based on internal review
1 parent a728a36 commit d7534e9

9 files changed

+8803
-1079
lines changed

libraries/common/src/main/java/androidx/media3/common/util/Util.java

-48
Original file line numberDiff line numberDiff line change
@@ -703,54 +703,6 @@ public static <T> void nullSafeListToArray(List<T> list, T[] array) {
703703
list.toArray(array);
704704
}
705705

706-
/**
707-
* Creates a new array containing the concatenation of multiple non-null {@code int[]} arrays.
708-
*
709-
* @param arrays A list of non-null {@code int[]} arrays to concatenate.
710-
* @return The concatenated result.
711-
*/
712-
@UnstableApi
713-
public static int[] nullSafeIntArraysConcatenation(List<int[]> arrays) {
714-
int totalLength = 0;
715-
for (int[] array : arrays) {
716-
totalLength += array.length;
717-
}
718-
719-
int[] concatenation = new int[totalLength];
720-
721-
int offset = 0;
722-
for (int[] array : arrays) {
723-
System.arraycopy(array, 0, concatenation, offset, array.length);
724-
offset += array.length;
725-
}
726-
727-
return concatenation;
728-
}
729-
730-
/**
731-
* Creates a new array containing the concatenation of multiple non-null {@code long[]} arrays.
732-
*
733-
* @param arrays A list of non-null {@code long[]} arrays to concatenate.
734-
* @return The concatenated result.
735-
*/
736-
@UnstableApi
737-
public static long[] nullSafeLongArraysConcatenation(List<long[]> arrays) {
738-
int totalLength = 0;
739-
for (long[] array : arrays) {
740-
totalLength += array.length;
741-
}
742-
743-
long[] res = new long[totalLength];
744-
745-
int offset = 0;
746-
for (long[] array : arrays) {
747-
System.arraycopy(array, 0, res, offset, array.length);
748-
offset += array.length;
749-
}
750-
751-
return res;
752-
}
753-
754706
/**
755707
* Creates a {@link Handler} on the current {@link Looper} thread.
756708
*

libraries/extractor/src/main/java/androidx/media3/extractor/MergedChunkIndex.java renamed to libraries/extractor/src/main/java/androidx/media3/extractor/ChunkIndexMerger.java

+21-26
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,12 @@
1616
package androidx.media3.extractor;
1717

1818
import androidx.media3.common.util.UnstableApi;
19-
import androidx.media3.common.util.Util;
19+
import com.google.common.primitives.Ints;
20+
import com.google.common.primitives.Longs;
2021
import java.util.ArrayList;
21-
import java.util.HashSet;
22+
import java.util.LinkedHashMap;
2223
import java.util.List;
23-
import java.util.Set;
24+
import java.util.Map;
2425

2526
/**
2627
* A utility class for merging multiple {@link ChunkIndex} instances into a single {@link
@@ -30,18 +31,14 @@
3031
* unified index is needed for seeking or playback.
3132
*/
3233
@UnstableApi
33-
public class MergedChunkIndex {
34+
public final class ChunkIndexMerger {
3435

35-
/** The individual {@link ChunkIndex} entries being merged. */
36-
private final List<ChunkIndex> chunks;
37-
38-
/** The set of first start times seen so far across added {@link ChunkIndex} instances. */
39-
private final Set<Long> uniqueStartTimes;
36+
/** Start time in microseconds to {@link ChunkIndex} mapping. Maintains insertion order. */
37+
private final Map<Long, ChunkIndex> chunkMap;
4038

4139
/** Creates an instance. */
42-
public MergedChunkIndex() {
43-
this.chunks = new ArrayList<>();
44-
this.uniqueStartTimes = new HashSet<>();
40+
public ChunkIndexMerger() {
41+
this.chunkMap = new LinkedHashMap<>();
4542
}
4643

4744
/**
@@ -51,42 +48,40 @@ public MergedChunkIndex() {
5148
*
5249
* @param chunk The {@link ChunkIndex} to add.
5350
*/
54-
public void merge(ChunkIndex chunk) {
55-
if (chunk.timesUs.length > 0 && !uniqueStartTimes.contains(chunk.timesUs[0])) {
56-
chunks.add(chunk);
57-
uniqueStartTimes.add(chunk.timesUs[0]);
51+
public void add(ChunkIndex chunk) {
52+
if (chunk.timesUs.length > 0 && !chunkMap.containsKey(chunk.timesUs[0])) {
53+
chunkMap.put(chunk.timesUs[0], chunk);
5854
}
5955
}
6056

61-
/** Returns a single {@link ChunkIndex} that combines all added chunk indices. */
62-
public ChunkIndex toChunkIndex() {
57+
/** Returns a single {@link ChunkIndex} that merges all added chunk indices. */
58+
public ChunkIndex merge() {
6359
List<int[]> sizesList = new ArrayList<>();
6460
List<long[]> offsetsList = new ArrayList<>();
6561
List<long[]> durationsList = new ArrayList<>();
6662
List<long[]> timesList = new ArrayList<>();
6763

68-
for (ChunkIndex chunk : chunks) {
64+
for (ChunkIndex chunk : chunkMap.values()) {
6965
sizesList.add(chunk.sizes);
7066
offsetsList.add(chunk.offsets);
7167
durationsList.add(chunk.durationsUs);
7268
timesList.add(chunk.timesUs);
7369
}
7470

7571
return new ChunkIndex(
76-
Util.nullSafeIntArraysConcatenation(sizesList),
77-
Util.nullSafeLongArraysConcatenation(offsetsList),
78-
Util.nullSafeLongArraysConcatenation(durationsList),
79-
Util.nullSafeLongArraysConcatenation(timesList));
72+
Ints.concat(sizesList.toArray(new int[sizesList.size()][])),
73+
Longs.concat(offsetsList.toArray(new long[offsetsList.size()][])),
74+
Longs.concat(durationsList.toArray(new long[durationsList.size()][])),
75+
Longs.concat(timesList.toArray(new long[timesList.size()][])));
8076
}
8177

8278
/** Clears all added chunk indices and internal state. */
8379
public void clear() {
84-
chunks.clear();
85-
uniqueStartTimes.clear();
80+
chunkMap.clear();
8681
}
8782

8883
/** Returns the number of chunk indices added so far. */
8984
public int size() {
90-
return chunks.size();
85+
return chunkMap.size();
9186
}
9287
}

libraries/extractor/src/main/java/androidx/media3/extractor/mp4/FragmentedMp4Extractor.java

+48-36
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,12 @@
4747
import androidx.media3.extractor.Ac4Util;
4848
import androidx.media3.extractor.CeaUtil;
4949
import androidx.media3.extractor.ChunkIndex;
50+
import androidx.media3.extractor.ChunkIndexMerger;
5051
import androidx.media3.extractor.Extractor;
5152
import androidx.media3.extractor.ExtractorInput;
5253
import androidx.media3.extractor.ExtractorOutput;
5354
import androidx.media3.extractor.ExtractorsFactory;
5455
import androidx.media3.extractor.GaplessInfoHolder;
55-
import androidx.media3.extractor.MergedChunkIndex;
5656
import androidx.media3.extractor.PositionHolder;
5757
import androidx.media3.extractor.SeekMap;
5858
import androidx.media3.extractor.SniffFailure;
@@ -224,7 +224,7 @@ public static ExtractorsFactory newFactory(SubtitleParser.Factory subtitleParser
224224
private final ReorderingSeiMessageQueue reorderingSeiMessageQueue;
225225
@Nullable private final TrackOutput additionalEmsgTrackOutput;
226226

227-
private final MergedChunkIndex mergedChunkIndex;
227+
private final ChunkIndexMerger chunkIndexMerger;
228228

229229
private ImmutableList<SniffFailure> lastSniffFailures;
230230
private int parserState;
@@ -439,7 +439,7 @@ public FragmentedMp4Extractor(
439439
new ReorderingSeiMessageQueue(
440440
(presentationTimeUs, seiBuffer) ->
441441
CeaUtil.consume(presentationTimeUs, seiBuffer, ceaTrackOutputs));
442-
mergedChunkIndex = new MergedChunkIndex();
442+
chunkIndexMerger = new ChunkIndexMerger();
443443
seekPositionBeforeSidxProcessing = C.INDEX_UNSET;
444444
}
445445

@@ -524,30 +524,37 @@ public void release() {
524524

525525
@Override
526526
public int read(ExtractorInput input, PositionHolder seekPosition) throws IOException {
527-
while (true) {
528-
switch (parserState) {
529-
case STATE_READING_ATOM_HEADER:
530-
if (!readAtomHeader(input, /* skipPayloadParsing= */ false)) {
531-
if (seekPositionBeforeSidxProcessing != C.INDEX_UNSET) {
532-
seekPosition.position = seekPositionBeforeSidxProcessing;
533-
seekPositionBeforeSidxProcessing = C.INDEX_UNSET;
534-
return Extractor.RESULT_SEEK;
535-
} else {
536-
reorderingSeiMessageQueue.flush();
537-
return Extractor.RESULT_END_OF_INPUT;
527+
try{
528+
while (true) {
529+
switch (parserState) {
530+
case STATE_READING_ATOM_HEADER:
531+
if (!readAtomHeader(input, /* skipPayloadParsing= */ false)) {
532+
if (seekPositionBeforeSidxProcessing != C.INDEX_UNSET) {
533+
seekPosition.position = seekPositionBeforeSidxProcessing;
534+
seekPositionBeforeSidxProcessing = C.INDEX_UNSET;
535+
return Extractor.RESULT_SEEK;
536+
} else {
537+
reorderingSeiMessageQueue.flush();
538+
return Extractor.RESULT_END_OF_INPUT;
539+
}
538540
}
539-
}
540-
break;
541-
case STATE_READING_ATOM_PAYLOAD:
542-
readAtomPayload(input);
543-
break;
544-
case STATE_READING_ENCRYPTION_DATA:
545-
readEncryptionData(input);
546-
break;
547-
default:
548-
if (readSample(input)) {
549-
return RESULT_CONTINUE;
550-
}
541+
break;
542+
case STATE_READING_ATOM_PAYLOAD:
543+
readAtomPayload(input);
544+
break;
545+
case STATE_READING_ENCRYPTION_DATA:
546+
readEncryptionData(input);
547+
break;
548+
default:
549+
if (readSample(input)) {
550+
return RESULT_CONTINUE;
551+
}
552+
}
553+
}
554+
} finally {
555+
if (seekPositionBeforeSidxProcessing != C.INDEX_UNSET) {
556+
seekPosition.position = seekPositionBeforeSidxProcessing;
557+
seekPositionBeforeSidxProcessing = C.INDEX_UNSET;
551558
}
552559
}
553560
}
@@ -683,16 +690,21 @@ private void onLeafAtomRead(LeafBox leaf, ExtractorInput input) throws IOExcepti
683690
} else if (leaf.type == Mp4Box.TYPE_sidx) {
684691
long inputPosition = input.getPosition();
685692
Pair<Long, ChunkIndex> result = parseSidx(leaf.data, inputPosition);
686-
mergedChunkIndex.merge(result.second);
693+
chunkIndexMerger.add(result.second);
687694
if (!haveOutputSeekMap) {
688695
segmentIndexEarliestPresentationTimeUs = result.first;
689696
extractorOutput.seekMap(result.second);
690697
haveOutputSeekMap = true;
691-
} else if (!haveOutputCompleteSeekMap && mergedChunkIndex.size() > 1) {
698+
} else if ((flags & FLAG_MERGE_FRAGMENTED_SIDX) != 0
699+
&& !haveOutputCompleteSeekMap
700+
&& chunkIndexMerger.size() > 1) {
692701
seekPositionBeforeSidxProcessing = inputPosition;
693-
processRemainingSidxAtoms(input);
694-
extractorOutput.seekMap(mergedChunkIndex.toChunkIndex());
695-
haveOutputCompleteSeekMap = true;
702+
try {
703+
processRemainingSidxAtoms(input);
704+
haveOutputCompleteSeekMap = true;
705+
} finally {
706+
extractorOutput.seekMap(chunkIndexMerger.merge());
707+
}
696708
}
697709
} else if (leaf.type == Mp4Box.TYPE_emsg) {
698710
onEmsgLeafAtomRead(leaf.data);
@@ -703,14 +715,14 @@ private void processRemainingSidxAtoms(ExtractorInput input) throws IOException
703715
enterReadingAtomHeaderState();
704716
while (readAtomHeader(input, /* skipPayloadParsing= */ true)) {
705717
if (atomType == Mp4Box.TYPE_sidx) {
706-
ParsableByteArray inputArray = new ParsableByteArray((int) atomSize);
707-
System.arraycopy(atomHeader.getData(), 0, inputArray.getData(), 0, Mp4Box.HEADER_SIZE);
718+
scratch.reset((int) atomSize);
719+
System.arraycopy(atomHeader.getData(), 0, scratch.getData(), 0, Mp4Box.HEADER_SIZE);
708720
input.readFully(
709-
inputArray.getData(), Mp4Box.HEADER_SIZE, (int) (atomSize - atomHeaderBytesRead));
721+
scratch.getData(), Mp4Box.HEADER_SIZE, (int) (atomSize - atomHeaderBytesRead));
710722

711-
LeafBox sidxBox = new LeafBox(Mp4Box.TYPE_sidx, inputArray);
723+
LeafBox sidxBox = new LeafBox(Mp4Box.TYPE_sidx, scratch);
712724
Pair<Long, ChunkIndex> result = parseSidx(sidxBox.data, input.getPeekPosition());
713-
mergedChunkIndex.merge(result.second);
725+
chunkIndexMerger.add(result.second);
714726
} else {
715727
input.skipFully((int) (atomSize - atomHeaderBytesRead), /* allowEndOfInput= */ true);
716728
}

0 commit comments

Comments
 (0)