diff --git a/library/core/src/main/java/com/google/android/exoplayer2/metadata/MetadataRenderer.java b/library/core/src/main/java/com/google/android/exoplayer2/metadata/MetadataRenderer.java index 1ab98378e4..fbaf904362 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/metadata/MetadataRenderer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/metadata/MetadataRenderer.java @@ -31,9 +31,7 @@ import com.google.android.exoplayer2.source.SampleStream.ReadDataResult; import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Util; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; -import org.checkerframework.checker.nullness.compatqual.NullableType; /** * A renderer for metadata. @@ -42,24 +40,18 @@ public final class MetadataRenderer extends BaseRenderer implements Callback { private static final String TAG = "MetadataRenderer"; private static final int MSG_INVOKE_RENDERER = 0; - // TODO: Holding multiple pending metadata objects is temporary mitigation against - // https://github.com/google/ExoPlayer/issues/1874. It should be removed once this issue has been - // addressed. - private static final int MAX_PENDING_METADATA_COUNT = 5; private final MetadataDecoderFactory decoderFactory; private final MetadataOutput output; @Nullable private final Handler outputHandler; private final MetadataInputBuffer buffer; - private final @NullableType Metadata[] pendingMetadata; - private final long[] pendingMetadataTimestamps; - private int pendingMetadataIndex; - private int pendingMetadataCount; @Nullable private MetadataDecoder decoder; private boolean inputStreamEnded; private boolean outputStreamEnded; private long subsampleOffsetUs; + private long pendingMetadataTimestampUs; + @Nullable private Metadata pendingMetadata; /** * @param output The output. @@ -90,8 +82,7 @@ public final class MetadataRenderer extends BaseRenderer implements Callback { outputLooper == null ? null : Util.createHandler(outputLooper, /* callback= */ this); this.decoderFactory = Assertions.checkNotNull(decoderFactory); buffer = new MetadataInputBuffer(); - pendingMetadata = new Metadata[MAX_PENDING_METADATA_COUNT]; - pendingMetadataTimestamps = new long[MAX_PENDING_METADATA_COUNT]; + pendingMetadataTimestampUs = C.TIME_UNSET; } @Override @@ -117,51 +108,18 @@ public final class MetadataRenderer extends BaseRenderer implements Callback { @Override protected void onPositionReset(long positionUs, boolean joining) { - flushPendingMetadata(); + pendingMetadata = null; + pendingMetadataTimestampUs = C.TIME_UNSET; inputStreamEnded = false; outputStreamEnded = false; } @Override public void render(long positionUs, long elapsedRealtimeUs) { - if (!inputStreamEnded && pendingMetadataCount < MAX_PENDING_METADATA_COUNT) { - buffer.clear(); - FormatHolder formatHolder = getFormatHolder(); - @ReadDataResult int result = readSource(formatHolder, buffer, /* readFlags= */ 0); - if (result == C.RESULT_BUFFER_READ) { - if (buffer.isEndOfStream()) { - inputStreamEnded = true; - } else { - buffer.subsampleOffsetUs = subsampleOffsetUs; - buffer.flip(); - @Nullable Metadata metadata = castNonNull(decoder).decode(buffer); - if (metadata != null) { - List entries = new ArrayList<>(metadata.length()); - decodeWrappedMetadata(metadata, entries); - if (!entries.isEmpty()) { - Metadata expandedMetadata = new Metadata(entries); - int index = - (pendingMetadataIndex + pendingMetadataCount) % MAX_PENDING_METADATA_COUNT; - pendingMetadata[index] = expandedMetadata; - pendingMetadataTimestamps[index] = buffer.timeUs; - pendingMetadataCount++; - } - } - } - } else if (result == C.RESULT_FORMAT_READ) { - subsampleOffsetUs = Assertions.checkNotNull(formatHolder.format).subsampleOffsetUs; - } - } - - if (pendingMetadataCount > 0 && pendingMetadataTimestamps[pendingMetadataIndex] <= positionUs) { - Metadata metadata = castNonNull(pendingMetadata[pendingMetadataIndex]); - invokeRenderer(metadata); - pendingMetadata[pendingMetadataIndex] = null; - pendingMetadataIndex = (pendingMetadataIndex + 1) % MAX_PENDING_METADATA_COUNT; - pendingMetadataCount--; - } - if (inputStreamEnded && pendingMetadataCount == 0) { - outputStreamEnded = true; + boolean working = true; + while (working) { + readMetadata(); + working = outputMetadata(positionUs); } } @@ -197,7 +155,8 @@ public final class MetadataRenderer extends BaseRenderer implements Callback { @Override protected void onDisabled() { - flushPendingMetadata(); + pendingMetadata = null; + pendingMetadataTimestampUs = C.TIME_UNSET; decoder = null; } @@ -211,20 +170,6 @@ public final class MetadataRenderer extends BaseRenderer implements Callback { return true; } - private void invokeRenderer(Metadata metadata) { - if (outputHandler != null) { - outputHandler.obtainMessage(MSG_INVOKE_RENDERER, metadata).sendToTarget(); - } else { - invokeRendererInternal(metadata); - } - } - - private void flushPendingMetadata() { - Arrays.fill(pendingMetadata, null); - pendingMetadataIndex = 0; - pendingMetadataCount = 0; - } - @Override public boolean handleMessage(Message msg) { switch (msg.what) { @@ -237,6 +182,56 @@ public final class MetadataRenderer extends BaseRenderer implements Callback { } } + private void readMetadata() { + if (!inputStreamEnded && pendingMetadata == null) { + buffer.clear(); + FormatHolder formatHolder = getFormatHolder(); + @ReadDataResult int result = readSource(formatHolder, buffer, /* readFlags= */ 0); + if (result == C.RESULT_BUFFER_READ) { + if (buffer.isEndOfStream()) { + inputStreamEnded = true; + } else { + buffer.subsampleOffsetUs = subsampleOffsetUs; + buffer.flip(); + @Nullable Metadata metadata = castNonNull(decoder).decode(buffer); + if (metadata != null) { + List entries = new ArrayList<>(metadata.length()); + decodeWrappedMetadata(metadata, entries); + if (!entries.isEmpty()) { + Metadata expandedMetadata = new Metadata(entries); + pendingMetadata = expandedMetadata; + pendingMetadataTimestampUs = buffer.timeUs; + } + } + } + } else if (result == C.RESULT_FORMAT_READ) { + subsampleOffsetUs = Assertions.checkNotNull(formatHolder.format).subsampleOffsetUs; + } + } + } + + private boolean outputMetadata(long positionUs) { + boolean didOutput = false; + if (pendingMetadata != null && pendingMetadataTimestampUs <= positionUs) { + invokeRenderer(pendingMetadata); + pendingMetadata = null; + pendingMetadataTimestampUs = C.TIME_UNSET; + didOutput = true; + } + if (inputStreamEnded && pendingMetadata == null) { + outputStreamEnded = true; + } + return didOutput; + } + + private void invokeRenderer(Metadata metadata) { + if (outputHandler != null) { + outputHandler.obtainMessage(MSG_INVOKE_RENDERER, metadata).sendToTarget(); + } else { + invokeRendererInternal(metadata); + } + } + private void invokeRendererInternal(Metadata metadata) { output.onMetadata(metadata); } diff --git a/testdata/src/test/assets/media/dash/emsg/sample.mpd b/testdata/src/test/assets/media/dash/emsg/sample.mpd index 56a9207b2e..a2ca662ac9 100644 --- a/testdata/src/test/assets/media/dash/emsg/sample.mpd +++ b/testdata/src/test/assets/media/dash/emsg/sample.mpd @@ -19,7 +19,8 @@ $ packager-linux \ - + + diff --git a/testdata/src/test/assets/playbackdumps/dash/emsg.dump b/testdata/src/test/assets/playbackdumps/dash/emsg.dump index a9aa2578dc..4a3c7b2e0e 100644 --- a/testdata/src/test/assets/playbackdumps/dash/emsg.dump +++ b/testdata/src/test/assets/playbackdumps/dash/emsg.dump @@ -97,3 +97,5 @@ MetadataOutput: entry[0] = EMSG: scheme=urn:mpeg:dash:event:callback:2015, id=0, durationMs=1000, value=1 Metadata[1]: entry[0] = EMSG: scheme=urn:mpeg:dash:event:callback:2015, id=1, durationMs=1000, value=1 + Metadata[2]: + entry[0] = EMSG: scheme=urn:mpeg:dash:event:callback:2015, id=2, durationMs=1000, value=1