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:
parent
002acc680b
commit
0b7f93a5d4
@ -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
|
||||||
|
@ -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,10 +1260,16 @@ public class MatroskaExtractor implements Extractor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ((flags & C.BUFFER_FLAG_HAS_SUPPLEMENTAL_DATA) != 0) {
|
if ((flags & C.BUFFER_FLAG_HAS_SUPPLEMENTAL_DATA) != 0) {
|
||||||
// Append supplemental data.
|
if (blockSampleCount > 1) {
|
||||||
int blockAdditionalSize = blockAdditionalData.limit();
|
// There were multiple samples in the block. Appending the additional data to the last
|
||||||
track.output.sampleData(blockAdditionalData, blockAdditionalSize);
|
// sample doesn't make sense. Skip instead.
|
||||||
size += blockAdditionalSize;
|
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);
|
track.output.sampleMetadata(timeUs, flags, size, offset, track.cryptoData);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user