Skip to content

Commit

Permalink
Update presentation time of metadata when the stream offset changes
Browse files Browse the repository at this point in the history
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
  • Loading branch information
marcbaechinger committed Oct 19, 2022
1 parent 92cfa74 commit 5a12237
Show file tree
Hide file tree
Showing 3 changed files with 134 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,19 @@ public Metadata copyWithAppendedEntries(Entry... 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
public boolean equals(@Nullable Object obj) {
if (this == obj) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,11 @@ public String getName() {
@Override
protected void onStreamChanged(Format[] formats, long startPositionUs, long offsetUs) {
decoder = decoderFactory.createDecoder(formats[0]);
if (pendingMetadata != null) {
pendingMetadata =
pendingMetadata.copyWithPresentationTimeUs(
pendingMetadata.presentationTimeUs + outputStreamOffsetUs - offsetUs);
}
outputStreamOffsetUs = offsetUs;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,122 @@ public void renderMetadata_withEarlyOutput() throws Exception {
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 {
List<Metadata> metadata = new ArrayList<>();
MetadataRenderer renderer = new MetadataRenderer(metadata::add, /* outputLooper= */ null);
Expand Down

0 comments on commit 5a12237

Please sign in to comment.