Check for TS synchronization before parsing packet from random position

#minor-release
Issue: #9100
PiperOrigin-RevId: 384962258
This commit is contained in:
aquilescanta 2021-07-15 18:50:11 +01:00 committed by Ian Baker
parent 7157f3c119
commit 99abb4e1e9
3 changed files with 39 additions and 2 deletions

View File

@ -109,6 +109,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

View File

@ -180,10 +180,13 @@ import java.io.IOException;
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);

View File

@ -21,6 +21,38 @@ import com.google.android.exoplayer2.util.ParsableByteArray;
/** 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.
*
* <p>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.