diff --git a/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/mp4/SefReader.java b/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/mp4/SefReader.java index 1b2e6a445f..aaf4975352 100644 --- a/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/mp4/SefReader.java +++ b/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/mp4/SefReader.java @@ -16,10 +16,8 @@ package com.google.android.exoplayer2.extractor.mp4; import static com.google.android.exoplayer2.extractor.Extractor.RESULT_SEEK; -import static com.google.android.exoplayer2.util.Assertions.checkNotNull; import androidx.annotation.IntDef; -import androidx.annotation.Nullable; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.ParserException; import com.google.android.exoplayer2.extractor.Extractor; @@ -34,7 +32,6 @@ import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; -import java.util.Collections; import java.util.List; /** @@ -80,15 +77,20 @@ import java.util.List; private static final String TAG = "SefReader"; - // Hex representation of `SEFT` (in ASCII). This is the last byte of a file that has Samsung - // Extension Format (SEF) data. + /** + * Hex representation of `SEFT` (in ASCII). + * + *

This is the last 4 bytes of a file that has Samsung Extension Format (SEF) data. + */ private static final int SAMSUNG_TAIL_SIGNATURE = 0x53454654; - - // Start signature (4 bytes), SEF version (4 bytes), SDR count (4 bytes). + /** Start signature (4 bytes), SEF version (4 bytes), SDR count (4 bytes). */ private static final int TAIL_HEADER_LENGTH = 12; - // Tail offset (4 bytes), tail signature (4 bytes). + /** Tail offset (4 bytes), tail signature (4 bytes). */ private static final int TAIL_FOOTER_LENGTH = 8; + private static final int LENGTH_OF_ONE_SDR = 12; + private static final Splitter COLON_SPLITTER = Splitter.on(':'); + private static final Splitter ASTERISK_SPLITTER = Splitter.on('*'); private final List dataReferences; @State private int readerState; @@ -145,7 +147,7 @@ import java.util.List; return; } - // input.getPosition is at the very end of the tail, so jump forward by sefTailLength, but + // input.getPosition is at the very end of the tail, so jump forward by tailLength, but // account for the tail header, which needs to be ignored. seekPosition.position = input.getPosition() - (tailLength - TAIL_HEADER_LENGTH); readerState = STATE_READING_SDRS; @@ -182,48 +184,68 @@ import java.util.List; return; } - Collections.sort(dataReferences, (o1, o2) -> Long.compare(o1.startOffset, o2.startOffset)); readerState = STATE_READING_SEF_DATA; seekPosition.position = dataReferences.get(0).startOffset; } private void readSefData(ExtractorInput input, List slowMotionMetadataEntries) throws IOException { - checkNotNull(dataReferences); - Splitter splitter = Splitter.on(':'); + long dataStartOffset = input.getPosition(); int totalDataLength = (int) (input.getLength() - input.getPosition() - tailLength); - ParsableByteArray scratch = new ParsableByteArray(/* limit= */ totalDataLength); - input.readFully(scratch.getData(), 0, totalDataLength); + ParsableByteArray data = new ParsableByteArray(/* limit= */ totalDataLength); + input.readFully(data.getData(), 0, totalDataLength); - int totalDataReferenceBytesConsumed = 0; for (int i = 0; i < dataReferences.size(); i++) { DataReference dataReference = dataReferences.get(i); - if (dataReference.dataType == TYPE_SLOW_MOTION_DATA) { - scratch.skipBytes(23); // data type (2), data sub info (2), name len (4), name (15). - List segments = new ArrayList<>(); - int dataReferenceEndPosition = totalDataReferenceBytesConsumed + dataReference.size; - while (scratch.getPosition() < dataReferenceEndPosition) { - @Nullable String data = scratch.readDelimiterTerminatedString('*'); - List values = splitter.splitToList(checkNotNull(data)); - if (values.size() != 3) { - throw new ParserException(); - } - try { - int startTimeMs = Integer.parseInt(values.get(0)); - int endTimeMs = Integer.parseInt(values.get(1)); - int speedMode = Integer.parseInt(values.get(2)); - int speedDivisor = 1 << (speedMode - 1); - segments.add(new SlowMotionData.Segment(startTimeMs, endTimeMs, speedDivisor)); - } catch (NumberFormatException e) { - throw new ParserException(e); - } - } - totalDataReferenceBytesConsumed += dataReference.size; - slowMotionMetadataEntries.add(new SlowMotionData(segments)); + int intendedPosition = (int) (dataReference.startOffset - dataStartOffset); + data.setPosition(intendedPosition); + + // The data type is derived from the name because the SEF format has inconsistent data type + // values. + data.skipBytes(4); // data type (2), data sub info (2). + int nameLength = data.readLittleEndianInt(); + String name = data.readString(nameLength); + @DataType int dataType = nameToDataType(name); + + int remainingDataLength = dataReference.size - (8 + nameLength); + switch (dataType) { + case TYPE_SLOW_MOTION_DATA: + slowMotionMetadataEntries.add(readSlowMotionData(data, remainingDataLength)); + break; + case TYPE_SUPER_SLOW_MOTION_DATA: + case TYPE_SUPER_SLOW_MOTION_BGM: + case TYPE_SUPER_SLOW_MOTION_EDIT_DATA: + case TYPE_SUPER_SLOW_DEFLICKERING_ON: + break; + default: + throw new IllegalStateException(); } } } + private static SlowMotionData readSlowMotionData(ParsableByteArray data, int dataLength) + throws ParserException { + List segments = new ArrayList<>(); + String dataString = data.readString(dataLength); + List segmentStrings = ASTERISK_SPLITTER.splitToList(dataString); + for (int i = 0; i < segmentStrings.size(); i++) { + List values = COLON_SPLITTER.splitToList(segmentStrings.get(i)); + if (values.size() != 3) { + throw new ParserException(); + } + try { + int startTimeMs = Integer.parseInt(values.get(0)); + int endTimeMs = Integer.parseInt(values.get(1)); + int speedMode = Integer.parseInt(values.get(2)); + int speedDivisor = 1 << (speedMode - 1); + segments.add(new SlowMotionData.Segment(startTimeMs, endTimeMs, speedDivisor)); + } catch (NumberFormatException e) { + throw new ParserException(e); + } + } + return new SlowMotionData(segments); + } + @DataType private static int nameToDataType(String name) throws ParserException { switch (name) {