Update presentation time of metadata when the stream offset changes
The stream offset is used to calculate the presentation time of a metadata object when reading and later when playing, to calculate the current presentation time to decide whether to send the metadata to the output. Accordingly, the presentation time of a pending metadata that has been calculated with a given offset needs to be recalculated when the stream offset changes. #minor-release PiperOrigin-RevId: 472499943 (cherry picked from commit 5a1223777c4d4ee2faa4043be2079971e44093a9)
This commit is contained in:
parent
d2623dda9b
commit
d7d75a0157
@ -156,6 +156,19 @@ public final class Metadata implements Parcelable {
|
|||||||
presentationTimeUs, Util.nullSafeArrayConcatenation(entries, entriesToAppend));
|
presentationTimeUs, Util.nullSafeArrayConcatenation(entries, entriesToAppend));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a copy of this metadata with the specified presentation time.
|
||||||
|
*
|
||||||
|
* @param presentationTimeUs The new presentation time, in microseconds.
|
||||||
|
* @return The metadata instance with the new presentation time.
|
||||||
|
*/
|
||||||
|
public Metadata copyWithPresentationTimeUs(long presentationTimeUs) {
|
||||||
|
if (this.presentationTimeUs == presentationTimeUs) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
return new Metadata(presentationTimeUs, entries);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(@Nullable Object obj) {
|
public boolean equals(@Nullable Object obj) {
|
||||||
if (this == obj) {
|
if (this == obj) {
|
||||||
|
@ -137,6 +137,11 @@ public final class MetadataRenderer extends BaseRenderer implements Callback {
|
|||||||
@Override
|
@Override
|
||||||
protected void onStreamChanged(Format[] formats, long startPositionUs, long offsetUs) {
|
protected void onStreamChanged(Format[] formats, long startPositionUs, long offsetUs) {
|
||||||
decoder = decoderFactory.createDecoder(formats[0]);
|
decoder = decoderFactory.createDecoder(formats[0]);
|
||||||
|
if (pendingMetadata != null) {
|
||||||
|
pendingMetadata =
|
||||||
|
pendingMetadata.copyWithPresentationTimeUs(
|
||||||
|
pendingMetadata.presentationTimeUs + outputStreamOffsetUs - offsetUs);
|
||||||
|
}
|
||||||
outputStreamOffsetUs = offsetUs;
|
outputStreamOffsetUs = offsetUs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -220,6 +220,122 @@ public class MetadataRendererTest {
|
|||||||
assertThat(metadata.get(1).presentationTimeUs).isEqualTo(1_000_000);
|
assertThat(metadata.get(1).presentationTimeUs).isEqualTo(1_000_000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void replaceStream_withIncreasingOffsetUs_updatesPendingMetadataPresentationTime()
|
||||||
|
throws Exception {
|
||||||
|
EventMessage emsg =
|
||||||
|
new EventMessage(
|
||||||
|
"urn:test-scheme-id",
|
||||||
|
/* value= */ "",
|
||||||
|
/* durationMs= */ 1,
|
||||||
|
/* id= */ 0,
|
||||||
|
"Test data".getBytes(UTF_8));
|
||||||
|
byte[] encodedEmsg = eventMessageEncoder.encode(emsg);
|
||||||
|
List<Metadata> metadataOutput = new ArrayList<>();
|
||||||
|
MetadataRenderer renderer =
|
||||||
|
new MetadataRenderer(
|
||||||
|
/* output= */ metadataOutput::add,
|
||||||
|
/* outputLooper= */ null,
|
||||||
|
MetadataDecoderFactory.DEFAULT,
|
||||||
|
/* outputMetadataEarly= */ false);
|
||||||
|
FakeSampleStream fakeSampleStream =
|
||||||
|
createFakeSampleStream(
|
||||||
|
ImmutableList.of(
|
||||||
|
sample(/* timeUs= */ 100_000, C.BUFFER_FLAG_KEY_FRAME, encodedEmsg),
|
||||||
|
sample(/* timeUs= */ 200_000, C.BUFFER_FLAG_KEY_FRAME, encodedEmsg),
|
||||||
|
END_OF_STREAM_ITEM));
|
||||||
|
fakeSampleStream.writeData(/* startPositionUs= */ 0);
|
||||||
|
// Start of the first reading period.
|
||||||
|
renderer.replaceStream(
|
||||||
|
new Format[] {EMSG_FORMAT},
|
||||||
|
fakeSampleStream,
|
||||||
|
/* startPositionUs= */ 0L,
|
||||||
|
/* offsetUs= */ 0L);
|
||||||
|
// Read the format
|
||||||
|
renderer.render(/* positionUs= */ 0, /* elapsedRealtimeUs= */ 0);
|
||||||
|
|
||||||
|
// Read and render the first metadata. The second metadata is immediately read as pending.
|
||||||
|
// The offset is added to timeUs of the samples when reading (100_000 and 200_000).
|
||||||
|
renderer.render(/* positionUs= */ 99_999, /* elapsedRealtimeUs= */ 0);
|
||||||
|
assertThat(metadataOutput).isEmpty();
|
||||||
|
renderer.render(/* positionUs= */ 100_000, /* elapsedRealtimeUs= */ 0);
|
||||||
|
assertThat(metadataOutput).hasSize(1);
|
||||||
|
|
||||||
|
// Start of the 2nd reading period. Replace the stream with a different offset. This adjusts the
|
||||||
|
// presentation time of the pending metadata.
|
||||||
|
renderer.replaceStream(
|
||||||
|
new Format[] {EMSG_FORMAT},
|
||||||
|
fakeSampleStream,
|
||||||
|
/* startPositionUs= */ 0L,
|
||||||
|
/* offsetUs= */ 100_000L);
|
||||||
|
renderer.render(/* positionUs= */ 199_999, /* elapsedRealtimeUs= */ 0);
|
||||||
|
assertThat(metadataOutput).hasSize(1);
|
||||||
|
|
||||||
|
// Output second metadata.
|
||||||
|
renderer.render(/* positionUs= */ 200_000, /* elapsedRealtimeUs= */ 0);
|
||||||
|
assertThat(metadataOutput).hasSize(2);
|
||||||
|
assertThat(metadataOutput.get(0).presentationTimeUs).isEqualTo(100_000);
|
||||||
|
assertThat(metadataOutput.get(1).presentationTimeUs).isEqualTo(100_000);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void replaceStream_withDecreasingOffsetUs_updatesPendingMetadataPresentationTime()
|
||||||
|
throws Exception {
|
||||||
|
EventMessage emsg =
|
||||||
|
new EventMessage(
|
||||||
|
"urn:test-scheme-id",
|
||||||
|
/* value= */ "",
|
||||||
|
/* durationMs= */ 1,
|
||||||
|
/* id= */ 0,
|
||||||
|
"Test data".getBytes(UTF_8));
|
||||||
|
byte[] encodedEmsg = eventMessageEncoder.encode(emsg);
|
||||||
|
List<Metadata> metadataOutput = new ArrayList<>();
|
||||||
|
MetadataRenderer renderer =
|
||||||
|
new MetadataRenderer(
|
||||||
|
/* output= */ metadataOutput::add,
|
||||||
|
/* outputLooper= */ null,
|
||||||
|
MetadataDecoderFactory.DEFAULT,
|
||||||
|
/* outputMetadataEarly= */ false);
|
||||||
|
FakeSampleStream fakeSampleStream =
|
||||||
|
createFakeSampleStream(
|
||||||
|
ImmutableList.of(
|
||||||
|
sample(/* timeUs= */ 100_000, C.BUFFER_FLAG_KEY_FRAME, encodedEmsg),
|
||||||
|
sample(/* timeUs= */ 200_000, C.BUFFER_FLAG_KEY_FRAME, encodedEmsg),
|
||||||
|
END_OF_STREAM_ITEM));
|
||||||
|
fakeSampleStream.writeData(/* startPositionUs= */ 0);
|
||||||
|
// Start of the first reading period.
|
||||||
|
renderer.replaceStream(
|
||||||
|
new Format[] {EMSG_FORMAT},
|
||||||
|
fakeSampleStream,
|
||||||
|
/* startPositionUs= */ 0L,
|
||||||
|
/* offsetUs= */ 100_000L);
|
||||||
|
// Read the format
|
||||||
|
renderer.render(/* positionUs= */ 0, /* elapsedRealtimeUs= */ 0);
|
||||||
|
|
||||||
|
// Read and render the first metadata. The second metadata is immediately read as pending.
|
||||||
|
// The offset of 0 is added to timeUs of the samples when reading (100_000 and 200_000).
|
||||||
|
renderer.render(/* positionUs= */ 199_999, /* elapsedRealtimeUs= */ 0);
|
||||||
|
assertThat(metadataOutput).isEmpty();
|
||||||
|
renderer.render(/* positionUs= */ 200_000, /* elapsedRealtimeUs= */ 0);
|
||||||
|
assertThat(metadataOutput).hasSize(1);
|
||||||
|
|
||||||
|
// Start of the 2nd reading period. Replace the stream with a different offset and adjust the
|
||||||
|
// presentation time of the pending metadata.
|
||||||
|
renderer.replaceStream(
|
||||||
|
new Format[] {EMSG_FORMAT},
|
||||||
|
fakeSampleStream,
|
||||||
|
/* startPositionUs= */ 0L,
|
||||||
|
/* offsetUs= */ 0L);
|
||||||
|
renderer.render(/* positionUs= */ 299_999, /* elapsedRealtimeUs= */ 0);
|
||||||
|
assertThat(metadataOutput).hasSize(1);
|
||||||
|
|
||||||
|
// Output second metadata.
|
||||||
|
renderer.render(/* positionUs= */ 300_000, /* elapsedRealtimeUs= */ 0);
|
||||||
|
assertThat(metadataOutput).hasSize(2);
|
||||||
|
assertThat(metadataOutput.get(0).presentationTimeUs).isEqualTo(100_000);
|
||||||
|
assertThat(metadataOutput.get(1).presentationTimeUs).isEqualTo(300_000);
|
||||||
|
}
|
||||||
|
|
||||||
private static List<Metadata> runRenderer(byte[] input) throws ExoPlaybackException {
|
private static List<Metadata> runRenderer(byte[] input) throws ExoPlaybackException {
|
||||||
List<Metadata> metadata = new ArrayList<>();
|
List<Metadata> metadata = new ArrayList<>();
|
||||||
MetadataRenderer renderer = new MetadataRenderer(metadata::add, /* outputLooper= */ null);
|
MetadataRenderer renderer = new MetadataRenderer(metadata::add, /* outputLooper= */ null);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user