Skip to content

Commit

Permalink
Search for TrueHD syncframes
Browse files Browse the repository at this point in the history
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
  • Loading branch information
andrewlewis authored and ojw28 committed Apr 7, 2018
1 parent 1b84544 commit 280cc54
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 16 deletions.
3 changes: 3 additions & 0 deletions RELEASENOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@
advancing ([#3841](https://github.com/google/ExoPlayer/issues/3841)).
* Add an option to skip silent audio in `PlaybackParameters`
((#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:
* Add release method to Cache interface.
* Prevent multiple instances of SimpleCache in the same folder.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ private SyncFrameInfo(
/**
* 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.
Expand Down Expand Up @@ -463,6 +463,26 @@ public static int parseEAc3SyncframeAudioSampleCount(ByteBuffer buffer) {
: 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
* buffer is not the start of a syncframe.
Expand All @@ -481,25 +501,22 @@ public static int parseTrueHdSyncframeAudioSampleCount(byte[] syncframe) {
|| (syncframe[7] & 0xFE) != 0xBA) {
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
* is not the start of a syncframe. The buffer's position is not modified.
* Reads the number of audio samples represented by a TrueHD syncframe. The buffer's position is
* not modified.
*
* @param buffer The {@link ByteBuffer} from which to read the syncframe. Must have at least
* {@link #TRUEHD_SYNCFRAME_PREFIX_LENGTH} bytes remaining.
* @return The number of audio samples represented by the syncframe, or 0 if the buffer is not the
* start of a syncframe.
* @param buffer The {@link ByteBuffer} from which to read the syncframe.
* @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.
*/
public static int parseTrueHdSyncframeAudioSampleCount(ByteBuffer buffer) {
public static int parseTrueHdSyncframeAudioSampleCount(ByteBuffer buffer, int offset) {
// TODO: Link to specification if available.
// The syncword ends 0xBA for TrueHD or 0xBB for MLP.
if ((buffer.getInt(buffer.position() + 4) & 0xFEFFFFFF) != 0xBA6F72F8) {
return 0;
}
return 40 << (buffer.get(buffer.position() + 8) & 0x07);
boolean isMlp = (buffer.get(buffer.position() + offset + 7) & 0xFF) == 0xBB;
return 40 << ((buffer.get(buffer.position() + offset + (isMlp ? 9 : 8)) >> 4) & 0x07);
}

private static int getAc3SyncframeSize(int fscod, int frmsizecod) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1076,8 +1076,11 @@ private static int getFramesPerEncodedSample(@C.Encoding int encoding, ByteBuffe
} else if (encoding == C.ENCODING_E_AC3) {
return Ac3Util.parseEAc3SyncframeAudioSampleCount(buffer);
} else if (encoding == C.ENCODING_DOLBY_TRUEHD) {
return Ac3Util.parseTrueHdSyncframeAudioSampleCount(buffer)
* Ac3Util.TRUEHD_RECHUNK_SAMPLE_COUNT;
int syncframeOffset = Ac3Util.findTrueHdSyncframeOffset(buffer);
return syncframeOffset == C.INDEX_UNSET
? 0
: (Ac3Util.parseTrueHdSyncframeAudioSampleCount(buffer, syncframeOffset)
* Ac3Util.TRUEHD_RECHUNK_SAMPLE_COUNT);
} else {
throw new IllegalStateException("Unexpected audio encoding: " + encoding);
}
Expand Down

0 comments on commit 280cc54

Please sign in to comment.