diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 0372191683..9d98f2aae0 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -29,6 +29,8 @@ * Add optional parameter to `Player.stop` to reset the player when stopping. * Fix handling of playback parameters changes while paused when followed by a seek. +* Fix playback of live FLV streams that do not contain an audio track + ([#3188](https://github.com/google/ExoPlayer/issues/3188)). * Use the same listener `MediaSourceEventListener` for all MediaSource implementations. * CEA-608: Fix handling of row count changes in roll-up mode diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/flv/FlvExtractor.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/flv/FlvExtractor.java index 2da075ff53..d908f28945 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/flv/FlvExtractor.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/flv/FlvExtractor.java @@ -78,6 +78,7 @@ public final class FlvExtractor implements Extractor { private ExtractorOutput extractorOutput; private @States int state; + private long mediaTagTimestampOffsetUs; private int bytesToNextTagHeader; private int tagType; private int tagDataSize; @@ -93,6 +94,7 @@ public final class FlvExtractor implements Extractor { tagData = new ParsableByteArray(); metadataReader = new ScriptTagPayloadReader(); state = STATE_READING_FLV_HEADER; + mediaTagTimestampOffsetUs = C.TIME_UNSET; } @Override @@ -134,6 +136,7 @@ public final class FlvExtractor implements Extractor { @Override public void seek(long position, long timeUs) { state = STATE_READING_FLV_HEADER; + mediaTagTimestampOffsetUs = C.TIME_UNSET; bytesToNextTagHeader = 0; } @@ -255,11 +258,11 @@ public final class FlvExtractor implements Extractor { private boolean readTagData(ExtractorInput input) throws IOException, InterruptedException { boolean wasConsumed = true; if (tagType == TAG_TYPE_AUDIO && audioReader != null) { - ensureOutputSeekMap(); - audioReader.consume(prepareTagData(input), tagTimestampUs); + ensureReadyForMediaOutput(); + audioReader.consume(prepareTagData(input), mediaTagTimestampOffsetUs + tagTimestampUs); } else if (tagType == TAG_TYPE_VIDEO && videoReader != null) { - ensureOutputSeekMap(); - videoReader.consume(prepareTagData(input), tagTimestampUs); + ensureReadyForMediaOutput(); + videoReader.consume(prepareTagData(input), mediaTagTimestampOffsetUs + tagTimestampUs); } else if (tagType == TAG_TYPE_SCRIPT_DATA && !outputSeekMap) { metadataReader.consume(prepareTagData(input), tagTimestampUs); long durationUs = metadataReader.getDurationUs(); @@ -288,11 +291,15 @@ public final class FlvExtractor implements Extractor { return tagData; } - private void ensureOutputSeekMap() { + private void ensureReadyForMediaOutput() { if (!outputSeekMap) { extractorOutput.seekMap(new SeekMap.Unseekable(C.TIME_UNSET)); outputSeekMap = true; } + if (mediaTagTimestampOffsetUs == C.TIME_UNSET) { + mediaTagTimestampOffsetUs = + metadataReader.getDurationUs() == C.TIME_UNSET ? -tagTimestampUs : 0; + } } }