Skip to content

Commit

Permalink
Add support for Dolby TrueHD passthrough
Browse files Browse the repository at this point in the history
Issue: #2147

-------------
Created by MOE: https:/google/moe
MOE_MIGRATED_REVID=180678595
  • Loading branch information
andrewlewis authored and ojw28 committed Jan 4, 2018
1 parent 7314e9b commit 8e8e53c
Show file tree
Hide file tree
Showing 6 changed files with 211 additions and 54 deletions.
2 changes: 2 additions & 0 deletions RELEASENOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@
* DefaultTrackSelector: Support disabling of individual text track selection
flags.
* New Cast extension: Simplifies toggling between local and Cast playbacks.
* Audio: Support TrueHD passthrough for rechunked samples in Matroska files
([#2147](https:/google/ExoPlayer/issues/2147)).

### 2.6.1 ###

Expand Down
63 changes: 27 additions & 36 deletions library/core/src/main/java/com/google/android/exoplayer2/C.java
Original file line number Diff line number Diff line change
Expand Up @@ -122,13 +122,22 @@ private C() {}
*/
public static final int AUDIO_SESSION_ID_UNSET = AudioManager.AUDIO_SESSION_ID_GENERATE;

/**
* Represents an audio encoding, or an invalid or unset value.
*/
/** Represents an audio encoding, or an invalid or unset value. */
@Retention(RetentionPolicy.SOURCE)
@IntDef({Format.NO_VALUE, ENCODING_INVALID, ENCODING_PCM_8BIT, ENCODING_PCM_16BIT,
ENCODING_PCM_24BIT, ENCODING_PCM_32BIT, ENCODING_PCM_FLOAT, ENCODING_AC3, ENCODING_E_AC3,
ENCODING_DTS, ENCODING_DTS_HD})
@IntDef({
Format.NO_VALUE,
ENCODING_INVALID,
ENCODING_PCM_8BIT,
ENCODING_PCM_16BIT,
ENCODING_PCM_24BIT,
ENCODING_PCM_32BIT,
ENCODING_PCM_FLOAT,
ENCODING_AC3,
ENCODING_E_AC3,
ENCODING_DTS,
ENCODING_DTS_HD,
ENCODING_DOLBY_TRUEHD
})
public @interface Encoding {}

/**
Expand All @@ -138,46 +147,28 @@ private C() {}
@IntDef({Format.NO_VALUE, ENCODING_INVALID, ENCODING_PCM_8BIT, ENCODING_PCM_16BIT,
ENCODING_PCM_24BIT, ENCODING_PCM_32BIT, ENCODING_PCM_FLOAT})
public @interface PcmEncoding {}
/**
* @see AudioFormat#ENCODING_INVALID
*/
/** @see AudioFormat#ENCODING_INVALID */
public static final int ENCODING_INVALID = AudioFormat.ENCODING_INVALID;
/**
* @see AudioFormat#ENCODING_PCM_8BIT
*/
/** @see AudioFormat#ENCODING_PCM_8BIT */
public static final int ENCODING_PCM_8BIT = AudioFormat.ENCODING_PCM_8BIT;
/**
* @see AudioFormat#ENCODING_PCM_16BIT
*/
/** @see AudioFormat#ENCODING_PCM_16BIT */
public static final int ENCODING_PCM_16BIT = AudioFormat.ENCODING_PCM_16BIT;
/**
* PCM encoding with 24 bits per sample.
*/
/** PCM encoding with 24 bits per sample. */
public static final int ENCODING_PCM_24BIT = 0x80000000;
/**
* PCM encoding with 32 bits per sample.
*/
/** PCM encoding with 32 bits per sample. */
public static final int ENCODING_PCM_32BIT = 0x40000000;
/**
* @see AudioFormat#ENCODING_PCM_FLOAT
*/
/** @see AudioFormat#ENCODING_PCM_FLOAT */
public static final int ENCODING_PCM_FLOAT = AudioFormat.ENCODING_PCM_FLOAT;
/**
* @see AudioFormat#ENCODING_AC3
*/
/** @see AudioFormat#ENCODING_AC3 */
public static final int ENCODING_AC3 = AudioFormat.ENCODING_AC3;
/**
* @see AudioFormat#ENCODING_E_AC3
*/
/** @see AudioFormat#ENCODING_E_AC3 */
public static final int ENCODING_E_AC3 = AudioFormat.ENCODING_E_AC3;
/**
* @see AudioFormat#ENCODING_DTS
*/
/** @see AudioFormat#ENCODING_DTS */
public static final int ENCODING_DTS = AudioFormat.ENCODING_DTS;
/**
* @see AudioFormat#ENCODING_DTS_HD
*/
/** @see AudioFormat#ENCODING_DTS_HD */
public static final int ENCODING_DTS_HD = AudioFormat.ENCODING_DTS_HD;
/** @see AudioFormat#ENCODING_DOLBY_TRUEHD */
public static final int ENCODING_DOLBY_TRUEHD = AudioFormat.ENCODING_DOLBY_TRUEHD;

/**
* @see AudioFormat#CHANNEL_OUT_7POINT1_SURROUND
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,7 @@
import com.google.android.exoplayer2.util.ParsableByteArray;
import java.nio.ByteBuffer;

/**
* Utility methods for parsing (E-)AC-3 syncframes, which are access units in (E-)AC-3 bitstreams.
*/
/** Utility methods for parsing Dolby TrueHD and (E-)AC3 syncframes. */
public final class Ac3Util {

/**
Expand Down Expand Up @@ -93,6 +91,17 @@ private Ac3SyncFrameInfo(String mimeType, int streamType, int channelCount, int

}

/**
* The number of samples to store in each output chunk when rechunking TrueHD streams. The number
* of samples extracted from the container corresponding to one syncframe must be an integer
* multiple of this value.
*/
public static final int TRUEHD_RECHUNK_SAMPLE_COUNT = 8;
/**
* 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;

/**
* The number of new samples per (E-)AC-3 audio block.
*/
Expand Down Expand Up @@ -441,6 +450,43 @@ public static int parseEAc3SyncframeAudioSampleCount(ByteBuffer buffer) {
: BLOCKS_PER_SYNCFRAME_BY_NUMBLKSCOD[(buffer.get(buffer.position() + 4) & 0x30) >> 4]);
}

/**
* Returns the number of audio samples represented by the given TrueHD syncframe, or 0 if the
* buffer is not the start of a syncframe.
*
* @param syncframe The bytes from which to read the syncframe. Must be at least {@link
* #TRUEHD_SYNCFRAME_PREFIX_LENGTH} bytes long.
* @return The number of audio samples represented by the syncframe, or 0 if the buffer doesn't
* contain the start of a syncframe.
*/
public static int parseTrueHdSyncframeAudioSampleCount(byte[] syncframe) {
// TODO: Link to specification if available.
if (syncframe[4] != (byte) 0xF8
|| syncframe[5] != (byte) 0x72
|| syncframe[6] != (byte) 0x6F
|| syncframe[7] != (byte) 0xBA) {
return 0;
}
return 40 << (syncframe[8] & 7);
}

/**
* 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.
*
* @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.
*/
public static int parseTrueHdSyncframeAudioSampleCount(ByteBuffer buffer) {
// TODO: Link to specification if available.
if (buffer.getInt(buffer.position() + 4) != 0xBA6F72F8) {
return 0;
}
return 40 << (buffer.get(buffer.position() + 8) & 0x07);
}

private static int getAc3SyncframeSize(int fscod, int frmsizecod) {
int halfFrmsizecod = frmsizecod / 2;
if (fscod < 0 || fscod >= SAMPLE_RATE_BY_FSCOD.length || frmsizecod < 0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -446,9 +446,12 @@ public void configure(@C.Encoding int inputEncoding, int inputChannelCount, int
if (outputEncoding == C.ENCODING_AC3 || outputEncoding == C.ENCODING_E_AC3) {
// AC-3 allows bitrates up to 640 kbit/s.
bufferSize = (int) (PASSTHROUGH_BUFFER_DURATION_US * 80 * 1024 / C.MICROS_PER_SECOND);
} else /* (outputEncoding == C.ENCODING_DTS || outputEncoding == C.ENCODING_DTS_HD */ {
} else if (outputEncoding == C.ENCODING_DTS) {
// DTS allows an 'open' bitrate, but we assume the maximum listed value: 1536 kbit/s.
bufferSize = (int) (PASSTHROUGH_BUFFER_DURATION_US * 192 * 1024 / C.MICROS_PER_SECOND);
} else /* outputEncoding == C.ENCODING_DTS_HD || outputEncoding == C.ENCODING_DOLBY_TRUEHD*/ {
// HD passthrough requires a larger buffer to avoid underrun.
bufferSize = (int) (PASSTHROUGH_BUFFER_DURATION_US * 192 * 6 * 1024 / C.MICROS_PER_SECOND);
}
}
bufferSizeUs =
Expand Down Expand Up @@ -580,6 +583,13 @@ public boolean handleBuffer(ByteBuffer buffer, long presentationTimeUs)
if (!isInputPcm && framesPerEncodedSample == 0) {
// If this is the first encoded sample, calculate the sample size in frames.
framesPerEncodedSample = getFramesPerEncodedSample(outputEncoding, buffer);
if (framesPerEncodedSample == 0) {
// We still don't know the number of frames per sample, so drop the buffer.
// For TrueHD this can occur after some seek operations, as not every sample starts with
// a syncframe header. If we chunked samples together so the extracted samples always
// started with a syncframe header, the chunks would be too large.
return true;
}
}

if (drainingPlaybackParameters != null) {
Expand Down Expand Up @@ -1225,6 +1235,9 @@ private static int getFramesPerEncodedSample(@C.Encoding int encoding, ByteBuffe
return Ac3Util.getAc3SyncframeAudioSampleCount();
} 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;
} else {
throw new IllegalStateException("Unexpected audio encoding: " + encoding);
}
Expand Down
Loading

0 comments on commit 8e8e53c

Please sign in to comment.