From 9cd6b50595ba4d1fc9783a530c022f17cb313940 Mon Sep 17 00:00:00 2001 From: aquilescanta Date: Thu, 15 Jul 2021 18:50:11 +0100 Subject: [PATCH] Check for TS synchronization before parsing packet from random position #minor-release Issue: #9100 PiperOrigin-RevId: 384962258 --- RELEASENOTES.md | 2 ++ .../extractor/ts/TsDurationReader.java | 7 ++-- .../exoplayer2/extractor/ts/TsUtil.java | 32 +++++++++++++++++++ 3 files changed, 39 insertions(+), 2 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index e4987d1f9c7..c77f182a252 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -27,6 +27,8 @@ ([#7608](https://github.com/google/ExoPlayer/issues/7608)). * Add support for MP4 H263 atom type ([#9158](https://github.com/google/ExoPlayer/issues/9158)). + * Fix issue around TS synchronization when reading a file's duration + ([#9100](https://github.com/google/ExoPlayer/pull/9100)). * HLS: * Fix issue where playback of a live event could become stuck rather than transitioning to `STATE_ENDED` when the event ends diff --git a/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/ts/TsDurationReader.java b/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/ts/TsDurationReader.java index 504b84d575c..05577ca2421 100644 --- a/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/ts/TsDurationReader.java +++ b/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/ts/TsDurationReader.java @@ -180,10 +180,13 @@ private int readLastPcrValue(ExtractorInput input, PositionHolder seekPositionHo private long readLastPcrValueFromBuffer(ParsableByteArray packetBuffer, int pcrPid) { int searchStartPosition = packetBuffer.getPosition(); int searchEndPosition = packetBuffer.limit(); - for (int searchPosition = searchEndPosition - 1; + // We start searching 'TsExtractor.TS_PACKET_SIZE' bytes from the end to prevent trying to read + // from an incomplete TS packet. + for (int searchPosition = searchEndPosition - TsExtractor.TS_PACKET_SIZE; searchPosition >= searchStartPosition; searchPosition--) { - if (packetBuffer.getData()[searchPosition] != TsExtractor.TS_SYNC_BYTE) { + if (!TsUtil.isStartOfTsPacket( + packetBuffer.getData(), searchStartPosition, searchEndPosition, searchPosition)) { continue; } long pcrValue = TsUtil.readPcrFromPacket(packetBuffer, searchPosition, pcrPid); diff --git a/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/ts/TsUtil.java b/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/ts/TsUtil.java index 2a7a0d25ab9..c8e6ec08597 100644 --- a/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/ts/TsUtil.java +++ b/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/ts/TsUtil.java @@ -21,6 +21,38 @@ /** Utilities method for extracting MPEG-TS streams. */ public final class TsUtil { + + /** + * Returns whether a TS packet starts at {@code searchPosition} according to the MPEG-TS + * synchronization recommendations. + * + *

ISO/IEC 13818-1:2015 Annex G recommends that 5 sync bytes emulating the start of 5 + * consecutive TS packets should never occur as part of the TS packets' contents. So, this method + * returns true when {@code data} contains a sync byte at {@code searchPosition}, and said sync + * byte is also one of five consecutive sync bytes separated from each other by the size of a TS + * packet. + * + * @param data The array holding the data to search in. + * @param start The first valid position in {@code data} from which a sync byte can be read. + * @param limit The first invalid position in {@code data}, after which no data should be read. + * @param searchPosition The position to check for a TS packet start. + * @return Whether a TS packet starts at {@code searchPosition}. + */ + public static boolean isStartOfTsPacket(byte[] data, int start, int limit, int searchPosition) { + int consecutiveSyncByteCount = 0; + for (int i = -4; i <= 4; i++) { + int currentPosition = searchPosition + i * TsExtractor.TS_PACKET_SIZE; + if (currentPosition < start + || currentPosition >= limit + || data[currentPosition] != TsExtractor.TS_SYNC_BYTE) { + consecutiveSyncByteCount = 0; + } else if (++consecutiveSyncByteCount == 5) { + return true; + } + } + return false; + } + /** * Returns the position of the first TS_SYNC_BYTE within the range [startPosition, limitPosition) * from the provided data array, or returns limitPosition if sync byte could not be found.