Commit 280cc545 by andrewlewis Committed by Oliver Woodman

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
parent 1b845442
...@@ -36,6 +36,9 @@ ...@@ -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.
......
...@@ -99,7 +99,7 @@ public final class Ac3Util { ...@@ -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.
...@@ -464,6 +464,26 @@ public final class Ac3Util { ...@@ -464,6 +464,26 @@ public final class Ac3Util {
} }
/** /**
* 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 { ...@@ -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) {
......
...@@ -1076,8 +1076,11 @@ public final class DefaultAudioSink implements AudioSink { ...@@ -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);
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment