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) ###
* Matroska: Support lacing in Blocks
([#3026](https://github.com/google/ExoPlayer/issues/3026)).
* Add Java FLAC extractor
([#6406](https://github.com/google/ExoPlayer/issues/6406)).
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).
return;
}
// If the ReferenceBlock element was not found for this sample, then it is a keyframe.
if (!blockHasReferenceBlock) {
blockFlags |= C.BUFFER_FLAG_KEY_FRAME;
// Commit sample metadata.
int sampleOffset = 0;
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;
break;
case ID_CONTENT_ENCODING:
@ -1102,10 +1110,6 @@ public class MatroskaExtractor implements Extractor {
blockSampleSizes = ensureArrayCapacity(blockSampleSizes, 1);
blockSampleSizes[0] = contentSize - blockTrackNumberLength - 3;
} else {
if (id != ID_SIMPLE_BLOCK) {
throw new ParserException("Lacing only supported in SimpleBlocks.");
}
// Read the sample count (1 byte).
readScratch(input, 4);
blockSampleCount = (scratch.data[3] & 0xFF) + 1;
@ -1187,7 +1191,8 @@ public class MatroskaExtractor implements Extractor {
}
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) {
int sampleSize = writeSampleData(input, track, blockSampleSizes[blockSampleIndex]);
long sampleTimeUs =
@ -1197,9 +1202,16 @@ public class MatroskaExtractor implements Extractor {
}
blockState = BLOCK_STATE_START;
} else {
// For Block, we send the metadata at the end of the BlockGroup element since we'll know
// if the sample is a keyframe or not only at that point.
blockSampleSizes[0] = writeSampleData(input, track, blockSampleSizes[0]);
// For Block, we need to wait until the end of the BlockGroup element before committing
// sample metadata. This is so that we can handle ReferenceBlock (which can be used to
// 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;
@ -1234,7 +1246,9 @@ public class MatroskaExtractor implements Extractor {
track.trueHdSampleRechunker.sampleMetadata(track, timeUs, flags, size, offset);
} else {
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.");
} else {
setSubtitleEndTime(track.codecId, durationUs, subtitleSample.data);
@ -1246,10 +1260,16 @@ public class MatroskaExtractor implements Extractor {
}
if ((flags & C.BUFFER_FLAG_HAS_SUPPLEMENTAL_DATA) != 0) {
// Append supplemental data.
int blockAdditionalSize = blockAdditionalData.limit();
track.output.sampleData(blockAdditionalData, blockAdditionalSize);
size += blockAdditionalSize;
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.
int blockAdditionalSize = blockAdditionalData.limit();
track.output.sampleData(blockAdditionalData, blockAdditionalSize);
size += blockAdditionalSize;
}
}
track.output.sampleMetadata(timeUs, flags, size, offset, track.cryptoData);
}