Search for TrueHD syncframes

In MatroskaExtractor TrueHD audio samples are joined into larger chunks. For
some streams the resulting chunked samples seem never to start with a syncframe.
This could result in playback of TrueHD streams getting stuck after seeking
because we could never read a syncframe at the start of a sample to determine
the sample size.

Instead of expecting to find a syncframe at the start of a sample, search for it
within the sample, to fix this issue.

Note: this means that we may search a few thousand bytes to find the sample
size, but the cost is only incurred for the first audio sample read.

Issue: #3845

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=191775779
This commit is contained in:
andrewlewis 2018-04-05 11:52:28 -07:00 committed by Oliver Woodman
parent 1b84544264
commit 280cc545dd
3 changed files with 39 additions and 16 deletions

View File

@ -36,6 +36,9 @@
advancing ([#3841](https://github.com/google/ExoPlayer/issues/3841)). advancing ([#3841](https://github.com/google/ExoPlayer/issues/3841)).
* Add an option to skip silent audio in `PlaybackParameters` * Add an option to skip silent audio in `PlaybackParameters`
((#2635)[https://github.com/google/ExoPlayer/issues/2635]). ((#2635)[https://github.com/google/ExoPlayer/issues/2635]).
* Fix an issue where playback of TrueHD streams would get stuck after seeking
due to not finding a syncframe
((#3845)[https://github.com/google/ExoPlayer/issues/3845]).
* Caching: * Caching:
* Add release method to Cache interface. * Add release method to Cache interface.
* Prevent multiple instances of SimpleCache in the same folder. * Prevent multiple instances of SimpleCache in the same folder.

View File

@ -99,7 +99,7 @@ public final class Ac3Util {
/** /**
* The number of bytes that must be parsed from a TrueHD syncframe to calculate the sample count. * The number of bytes that must be parsed from a TrueHD syncframe to calculate the sample count.
*/ */
public static final int TRUEHD_SYNCFRAME_PREFIX_LENGTH = 12; public static final int TRUEHD_SYNCFRAME_PREFIX_LENGTH = 10;
/** /**
* The number of new samples per (E-)AC-3 audio block. * The number of new samples per (E-)AC-3 audio block.
@ -463,6 +463,26 @@ public final class Ac3Util {
: BLOCKS_PER_SYNCFRAME_BY_NUMBLKSCOD[(buffer.get(buffer.position() + 4) & 0x30) >> 4]); : BLOCKS_PER_SYNCFRAME_BY_NUMBLKSCOD[(buffer.get(buffer.position() + 4) & 0x30) >> 4]);
} }
/**
* Returns the offset relative to the buffer's position of the start of a TrueHD syncframe, or
* {@link C#INDEX_UNSET} if no syncframe was found. The buffer's position is not modified.
*
* @param buffer The {@link ByteBuffer} within which to find a syncframe.
* @return The offset relative to the buffer's position of the start of a TrueHD syncframe, or
* {@link C#INDEX_UNSET} if no syncframe was found.
*/
public static int findTrueHdSyncframeOffset(ByteBuffer buffer) {
int startIndex = buffer.position();
int endIndex = buffer.limit() - TRUEHD_SYNCFRAME_PREFIX_LENGTH;
for (int i = startIndex; i <= endIndex; i++) {
// The syncword ends 0xBA for TrueHD or 0xBB for MLP.
if ((buffer.getInt(i + 4) & 0xFEFFFFFF) == 0xBA6F72F8) {
return i - startIndex;
}
}
return C.INDEX_UNSET;
}
/** /**
* Returns the number of audio samples represented by the given TrueHD syncframe, or 0 if the * Returns the number of audio samples represented by the given TrueHD syncframe, or 0 if the
* buffer is not the start of a syncframe. * buffer is not the start of a syncframe.
@ -481,25 +501,22 @@ public final class Ac3Util {
|| (syncframe[7] & 0xFE) != 0xBA) { || (syncframe[7] & 0xFE) != 0xBA) {
return 0; return 0;
} }
return 40 << (syncframe[8] & 7); boolean isMlp = (syncframe[7] & 0xFF) == 0xBB;
return 40 << ((syncframe[isMlp ? 9 : 8] >> 4) & 0x07);
} }
/** /**
* Reads the number of audio samples represented by the given TrueHD syncframe, or 0 if the buffer * Reads the number of audio samples represented by a TrueHD syncframe. The buffer's position is
* is not the start of a syncframe. The buffer's position is not modified. * not modified.
* *
* @param buffer The {@link ByteBuffer} from which to read the syncframe. Must have at least * @param buffer The {@link ByteBuffer} from which to read the syncframe.
* {@link #TRUEHD_SYNCFRAME_PREFIX_LENGTH} bytes remaining. * @param offset The offset of the start of the syncframe relative to the buffer's position.
* @return The number of audio samples represented by the syncframe, or 0 if the buffer is not the * @return The number of audio samples represented by the syncframe.
* start of a syncframe.
*/ */
public static int parseTrueHdSyncframeAudioSampleCount(ByteBuffer buffer) { public static int parseTrueHdSyncframeAudioSampleCount(ByteBuffer buffer, int offset) {
// TODO: Link to specification if available. // TODO: Link to specification if available.
// The syncword ends 0xBA for TrueHD or 0xBB for MLP. boolean isMlp = (buffer.get(buffer.position() + offset + 7) & 0xFF) == 0xBB;
if ((buffer.getInt(buffer.position() + 4) & 0xFEFFFFFF) != 0xBA6F72F8) { return 40 << ((buffer.get(buffer.position() + offset + (isMlp ? 9 : 8)) >> 4) & 0x07);
return 0;
}
return 40 << (buffer.get(buffer.position() + 8) & 0x07);
} }
private static int getAc3SyncframeSize(int fscod, int frmsizecod) { private static int getAc3SyncframeSize(int fscod, int frmsizecod) {

View File

@ -1076,8 +1076,11 @@ public final class DefaultAudioSink implements AudioSink {
} else if (encoding == C.ENCODING_E_AC3) { } else if (encoding == C.ENCODING_E_AC3) {
return Ac3Util.parseEAc3SyncframeAudioSampleCount(buffer); return Ac3Util.parseEAc3SyncframeAudioSampleCount(buffer);
} else if (encoding == C.ENCODING_DOLBY_TRUEHD) { } else if (encoding == C.ENCODING_DOLBY_TRUEHD) {
return Ac3Util.parseTrueHdSyncframeAudioSampleCount(buffer) int syncframeOffset = Ac3Util.findTrueHdSyncframeOffset(buffer);
* Ac3Util.TRUEHD_RECHUNK_SAMPLE_COUNT; return syncframeOffset == C.INDEX_UNSET
? 0
: (Ac3Util.parseTrueHdSyncframeAudioSampleCount(buffer, syncframeOffset)
* Ac3Util.TRUEHD_RECHUNK_SAMPLE_COUNT);
} else { } else {
throw new IllegalStateException("Unexpected audio encoding: " + encoding); throw new IllegalStateException("Unexpected audio encoding: " + encoding);
} }