MatroskaExtractor: Support lacing in full blocks

Caveats:

- Block additional data is ignored if the block is laced
  and contains multiple samples. Note that this is not
  a loss of functionality (SimpleBlock cannot have block
  additional data, and lacing was previously completely
  unsupported for Block)

- Subrip and ASS samples are dropped if they're in laced
  blocks with multiple samples (I don't think this is
  valid anyway)

Issue: #3026
PiperOrigin-RevId: 284545197
This commit is contained in:
olly 2019-12-09 15:02:11 +00:00 committed by Oliver Woodman
parent 002acc680b
commit 0b7f93a5d4
2 changed files with 44 additions and 22 deletions

View File

@ -2,6 +2,8 @@
### dev-v2 (not yet released) ### ### dev-v2 (not yet released) ###
* Matroska: Support lacing in Blocks
([#3026](https://github.com/google/ExoPlayer/issues/3026)).
* Add Java FLAC extractor * Add Java FLAC extractor
([#6406](https://github.com/google/ExoPlayer/issues/6406)). ([#6406](https://github.com/google/ExoPlayer/issues/6406)).
This extractor does not support seeking and live streams. If This extractor does not support seeking and live streams. If

View File

@ -682,16 +682,24 @@ public class MatroskaExtractor implements Extractor {
// We've skipped this block (due to incompatible track number). // We've skipped this block (due to incompatible track number).
return; return;
} }
// If the ReferenceBlock element was not found for this sample, then it is a keyframe. // Commit sample metadata.
if (!blockHasReferenceBlock) { int sampleOffset = 0;
blockFlags |= C.BUFFER_FLAG_KEY_FRAME; for (int i = 0; i < blockSampleCount; i++) {
sampleOffset += blockSampleSizes[i];
}
Track track = tracks.get(blockTrackNumber);
for (int i = 0; i < blockSampleCount; i++) {
long sampleTimeUs = blockTimeUs + (i * track.defaultSampleDurationNs) / 1000;
int sampleFlags = blockFlags;
if (i == 0 && !blockHasReferenceBlock) {
// If the ReferenceBlock element was not found in this block, then the first frame is a
// keyframe.
sampleFlags |= C.BUFFER_FLAG_KEY_FRAME;
}
int sampleSize = blockSampleSizes[i];
sampleOffset -= sampleSize; // The offset is to the end of the sample.
commitSampleToOutput(track, sampleTimeUs, sampleFlags, sampleSize, sampleOffset);
} }
commitSampleToOutput(
tracks.get(blockTrackNumber),
blockTimeUs,
blockFlags,
blockSampleSizes[0],
/* offset= */ 0);
blockState = BLOCK_STATE_START; blockState = BLOCK_STATE_START;
break; break;
case ID_CONTENT_ENCODING: case ID_CONTENT_ENCODING:
@ -1102,10 +1110,6 @@ public class MatroskaExtractor implements Extractor {
blockSampleSizes = ensureArrayCapacity(blockSampleSizes, 1); blockSampleSizes = ensureArrayCapacity(blockSampleSizes, 1);
blockSampleSizes[0] = contentSize - blockTrackNumberLength - 3; blockSampleSizes[0] = contentSize - blockTrackNumberLength - 3;
} else { } else {
if (id != ID_SIMPLE_BLOCK) {
throw new ParserException("Lacing only supported in SimpleBlocks.");
}
// Read the sample count (1 byte). // Read the sample count (1 byte).
readScratch(input, 4); readScratch(input, 4);
blockSampleCount = (scratch.data[3] & 0xFF) + 1; blockSampleCount = (scratch.data[3] & 0xFF) + 1;
@ -1187,7 +1191,8 @@ public class MatroskaExtractor implements Extractor {
} }
if (id == ID_SIMPLE_BLOCK) { if (id == ID_SIMPLE_BLOCK) {
// For SimpleBlock, we have metadata for each sample here. // For SimpleBlock, we can write sample data and immediately commit the corresponding
// sample metadata.
while (blockSampleIndex < blockSampleCount) { while (blockSampleIndex < blockSampleCount) {
int sampleSize = writeSampleData(input, track, blockSampleSizes[blockSampleIndex]); int sampleSize = writeSampleData(input, track, blockSampleSizes[blockSampleIndex]);
long sampleTimeUs = long sampleTimeUs =
@ -1197,9 +1202,16 @@ public class MatroskaExtractor implements Extractor {
} }
blockState = BLOCK_STATE_START; blockState = BLOCK_STATE_START;
} else { } else {
// For Block, we send the metadata at the end of the BlockGroup element since we'll know // For Block, we need to wait until the end of the BlockGroup element before committing
// if the sample is a keyframe or not only at that point. // sample metadata. This is so that we can handle ReferenceBlock (which can be used to
blockSampleSizes[0] = writeSampleData(input, track, blockSampleSizes[0]); // infer whether the first sample in the block is a keyframe), and BlockAdditions (which
// can contain additional sample data to append) contained in the block group. Just output
// the sample data, storing the final sample sizes for when we commit the metadata.
while (blockSampleIndex < blockSampleCount) {
blockSampleSizes[blockSampleIndex] =
writeSampleData(input, track, blockSampleSizes[blockSampleIndex]);
blockSampleIndex++;
}
} }
break; break;
@ -1234,7 +1246,9 @@ public class MatroskaExtractor implements Extractor {
track.trueHdSampleRechunker.sampleMetadata(track, timeUs, flags, size, offset); track.trueHdSampleRechunker.sampleMetadata(track, timeUs, flags, size, offset);
} else { } else {
if (CODEC_ID_SUBRIP.equals(track.codecId) || CODEC_ID_ASS.equals(track.codecId)) { if (CODEC_ID_SUBRIP.equals(track.codecId) || CODEC_ID_ASS.equals(track.codecId)) {
if (durationUs == C.TIME_UNSET) { if (blockSampleCount > 1) {
Log.w(TAG, "Skipping subtitle sample in laced block.");
} else if (durationUs == C.TIME_UNSET) {
Log.w(TAG, "Skipping subtitle sample with no duration."); Log.w(TAG, "Skipping subtitle sample with no duration.");
} else { } else {
setSubtitleEndTime(track.codecId, durationUs, subtitleSample.data); setSubtitleEndTime(track.codecId, durationUs, subtitleSample.data);
@ -1246,11 +1260,17 @@ public class MatroskaExtractor implements Extractor {
} }
if ((flags & C.BUFFER_FLAG_HAS_SUPPLEMENTAL_DATA) != 0) { if ((flags & C.BUFFER_FLAG_HAS_SUPPLEMENTAL_DATA) != 0) {
if (blockSampleCount > 1) {
// There were multiple samples in the block. Appending the additional data to the last
// sample doesn't make sense. Skip instead.
flags &= ~C.BUFFER_FLAG_HAS_SUPPLEMENTAL_DATA;
} else {
// Append supplemental data. // Append supplemental data.
int blockAdditionalSize = blockAdditionalData.limit(); int blockAdditionalSize = blockAdditionalData.limit();
track.output.sampleData(blockAdditionalData, blockAdditionalSize); track.output.sampleData(blockAdditionalData, blockAdditionalSize);
size += blockAdditionalSize; size += blockAdditionalSize;
} }
}
track.output.sampleMetadata(timeUs, flags, size, offset, track.cryptoData); track.output.sampleMetadata(timeUs, flags, size, offset, track.cryptoData);
} }
haveOutputSample = true; haveOutputSample = true;