Adjust SEF slow motion parsing to base data type off name.

PiperOrigin-RevId: 342050008
This commit is contained in:
samrobinson 2020-11-12 16:06:20 +00:00 committed by Christos Tsilopoulos
parent 99b87139df
commit 55afddf05c

View File

@ -16,10 +16,8 @@
package com.google.android.exoplayer2.extractor.mp4; package com.google.android.exoplayer2.extractor.mp4;
import static com.google.android.exoplayer2.extractor.Extractor.RESULT_SEEK; 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.IntDef;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ParserException; import com.google.android.exoplayer2.ParserException;
import com.google.android.exoplayer2.extractor.Extractor; 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.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.List; import java.util.List;
/** /**
@ -80,15 +77,20 @@ import java.util.List;
private static final String TAG = "SefReader"; 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).
*
* <p>This is the last 4 bytes of a file that has Samsung Extension Format (SEF) data.
*/
private static final int SAMSUNG_TAIL_SIGNATURE = 0x53454654; 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; 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 TAIL_FOOTER_LENGTH = 8;
private static final int LENGTH_OF_ONE_SDR = 12; 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<DataReference> dataReferences; private final List<DataReference> dataReferences;
@State private int readerState; @State private int readerState;
@ -145,7 +147,7 @@ import java.util.List;
return; 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. // account for the tail header, which needs to be ignored.
seekPosition.position = input.getPosition() - (tailLength - TAIL_HEADER_LENGTH); seekPosition.position = input.getPosition() - (tailLength - TAIL_HEADER_LENGTH);
readerState = STATE_READING_SDRS; readerState = STATE_READING_SDRS;
@ -182,48 +184,68 @@ import java.util.List;
return; return;
} }
Collections.sort(dataReferences, (o1, o2) -> Long.compare(o1.startOffset, o2.startOffset));
readerState = STATE_READING_SEF_DATA; readerState = STATE_READING_SEF_DATA;
seekPosition.position = dataReferences.get(0).startOffset; seekPosition.position = dataReferences.get(0).startOffset;
} }
private void readSefData(ExtractorInput input, List<Metadata.Entry> slowMotionMetadataEntries) private void readSefData(ExtractorInput input, List<Metadata.Entry> slowMotionMetadataEntries)
throws IOException { throws IOException {
checkNotNull(dataReferences); long dataStartOffset = input.getPosition();
Splitter splitter = Splitter.on(':');
int totalDataLength = (int) (input.getLength() - input.getPosition() - tailLength); int totalDataLength = (int) (input.getLength() - input.getPosition() - tailLength);
ParsableByteArray scratch = new ParsableByteArray(/* limit= */ totalDataLength); ParsableByteArray data = new ParsableByteArray(/* limit= */ totalDataLength);
input.readFully(scratch.getData(), 0, totalDataLength); input.readFully(data.getData(), 0, totalDataLength);
int totalDataReferenceBytesConsumed = 0;
for (int i = 0; i < dataReferences.size(); i++) { for (int i = 0; i < dataReferences.size(); i++) {
DataReference dataReference = dataReferences.get(i); DataReference dataReference = dataReferences.get(i);
if (dataReference.dataType == TYPE_SLOW_MOTION_DATA) { int intendedPosition = (int) (dataReference.startOffset - dataStartOffset);
scratch.skipBytes(23); // data type (2), data sub info (2), name len (4), name (15). data.setPosition(intendedPosition);
List<SlowMotionData.Segment> segments = new ArrayList<>();
int dataReferenceEndPosition = totalDataReferenceBytesConsumed + dataReference.size; // The data type is derived from the name because the SEF format has inconsistent data type
while (scratch.getPosition() < dataReferenceEndPosition) { // values.
@Nullable String data = scratch.readDelimiterTerminatedString('*'); data.skipBytes(4); // data type (2), data sub info (2).
List<String> values = splitter.splitToList(checkNotNull(data)); int nameLength = data.readLittleEndianInt();
if (values.size() != 3) { String name = data.readString(nameLength);
throw new ParserException(); @DataType int dataType = nameToDataType(name);
}
try { int remainingDataLength = dataReference.size - (8 + nameLength);
int startTimeMs = Integer.parseInt(values.get(0)); switch (dataType) {
int endTimeMs = Integer.parseInt(values.get(1)); case TYPE_SLOW_MOTION_DATA:
int speedMode = Integer.parseInt(values.get(2)); slowMotionMetadataEntries.add(readSlowMotionData(data, remainingDataLength));
int speedDivisor = 1 << (speedMode - 1); break;
segments.add(new SlowMotionData.Segment(startTimeMs, endTimeMs, speedDivisor)); case TYPE_SUPER_SLOW_MOTION_DATA:
} catch (NumberFormatException e) { case TYPE_SUPER_SLOW_MOTION_BGM:
throw new ParserException(e); case TYPE_SUPER_SLOW_MOTION_EDIT_DATA:
} case TYPE_SUPER_SLOW_DEFLICKERING_ON:
} break;
totalDataReferenceBytesConsumed += dataReference.size; default:
slowMotionMetadataEntries.add(new SlowMotionData(segments)); throw new IllegalStateException();
} }
} }
} }
private static SlowMotionData readSlowMotionData(ParsableByteArray data, int dataLength)
throws ParserException {
List<SlowMotionData.Segment> segments = new ArrayList<>();
String dataString = data.readString(dataLength);
List<String> segmentStrings = ASTERISK_SPLITTER.splitToList(dataString);
for (int i = 0; i < segmentStrings.size(); i++) {
List<String> 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 @DataType
private static int nameToDataType(String name) throws ParserException { private static int nameToDataType(String name) throws ParserException {
switch (name) { switch (name) {