diff --git a/library/core/src/main/java/com/google/android/exoplayer2/audio/Ac3Util.java b/library/core/src/main/java/com/google/android/exoplayer2/audio/Ac3Util.java index adb3d5999b..4b64ffb030 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/audio/Ac3Util.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/audio/Ac3Util.java @@ -28,6 +28,44 @@ import java.nio.ByteBuffer; */ public final class Ac3Util { + /** + * Holds sample format information as presented by a syncframe header. + */ + public static final class Ac3SyncFrameInfo { + + /** + * The sample mime type of the bitstream. One of {@link MimeTypes#AUDIO_AC3} and + * {@link MimeTypes#AUDIO_E_AC3}. + */ + public final String mimeType; + /** + * The audio sampling rate in Hz. + */ + public final int sampleRate; + /** + * The number of audio channels + */ + public final int channelCount; + /** + * The size of the frame. + */ + public final int frameSize; + /** + * Number of audio samples in the frame. + */ + public final int sampleCount; + + private Ac3SyncFrameInfo(String mimeType, int channelCount, int sampleRate, int frameSize, + int sampleCount) { + this.mimeType = mimeType; + this.channelCount = channelCount; + this.sampleRate = sampleRate; + this.frameSize = frameSize; + this.sampleCount = sampleCount; + } + + } + /** * The number of new samples per (E-)AC-3 audio block. */ @@ -114,62 +152,61 @@ public final class Ac3Util { } /** - * Returns the AC-3 format given {@code data} containing a syncframe. The reading position of - * {@code data} will be modified. + * Returns (E-)AC-3 format information given {@code data} containing a syncframe. The reading + * position of {@code data} will be modified. * * @param data The data to parse, positioned at the start of the syncframe. - * @param trackId The track identifier to set on the format, or null. - * @param language The language to set on the format. - * @param drmInitData {@link DrmInitData} to be included in the format. - * @return The AC-3 format parsed from data in the header. + * @return The (E-)AC-3 format data parsed from the header. */ - public static Format parseAc3SyncframeFormat(ParsableBitArray data, String trackId, - String language, DrmInitData drmInitData) { - data.skipBits(16 + 16); // syncword, crc1 - int fscod = data.readBits(2); - data.skipBits(6 + 5 + 3); // frmsizecod, bsid, bsmod - int acmod = data.readBits(3); - if ((acmod & 0x01) != 0 && acmod != 1) { - data.skipBits(2); // cmixlev - } - if ((acmod & 0x04) != 0) { - data.skipBits(2); // surmixlev - } - if (acmod == 2) { - data.skipBits(2); // dsurmod - } - boolean lfeon = data.readBit(); - return Format.createAudioSampleFormat(trackId, MimeTypes.AUDIO_AC3, null, Format.NO_VALUE, - Format.NO_VALUE, CHANNEL_COUNT_BY_ACMOD[acmod] + (lfeon ? 1 : 0), - SAMPLE_RATE_BY_FSCOD[fscod], null, drmInitData, 0, language); - } - - /** - * Returns the E-AC-3 format given {@code data} containing a syncframe. The reading position of - * {@code data} will be modified. - * - * @param data The data to parse, positioned at the start of the syncframe. - * @param trackId The track identifier to set on the format, or null. - * @param language The language to set on the format. - * @param drmInitData {@link DrmInitData} to be included in the format. - * @return The E-AC-3 format parsed from data in the header. - */ - public static Format parseEac3SyncframeFormat(ParsableBitArray data, String trackId, - String language, DrmInitData drmInitData) { - data.skipBits(16 + 2 + 3 + 11); // syncword, strmtype, substreamid, frmsiz + public static Ac3SyncFrameInfo parseAc3SyncframeInfo(ParsableBitArray data) { + int initialPosition = data.getPosition(); + data.skipBits(40); + boolean isEac3 = data.readBits(5) == 16; + data.setPosition(initialPosition); + String mimeType; int sampleRate; - int fscod = data.readBits(2); - if (fscod == 3) { - sampleRate = SAMPLE_RATE_BY_FSCOD2[data.readBits(2)]; - } else { - data.skipBits(2); // numblkscod + int acmod; + int frameSize; + int sampleCount; + if (isEac3) { + mimeType = MimeTypes.AUDIO_E_AC3; + data.skipBits(16 + 2 + 3); // syncword, strmtype, substreamid + frameSize = (data.readBits(11) + 1) * 2; + int fscod = data.readBits(2); + int audioBlocks; + if (fscod == 3) { + sampleRate = SAMPLE_RATE_BY_FSCOD2[data.readBits(2)]; + audioBlocks = 6; + } else { + int numblkscod = data.readBits(2); + audioBlocks = BLOCKS_PER_SYNCFRAME_BY_NUMBLKSCOD[numblkscod]; + sampleRate = SAMPLE_RATE_BY_FSCOD[fscod]; + } + sampleCount = AUDIO_SAMPLES_PER_AUDIO_BLOCK * audioBlocks; + acmod = data.readBits(3); + } else /* is AC-3 */ { + mimeType = MimeTypes.AUDIO_AC3; + data.skipBits(16 + 16); // syncword, crc1 + int fscod = data.readBits(2); + int frmsizecod = data.readBits(6); + frameSize = getAc3SyncframeSize(fscod, frmsizecod); + data.skipBits(5 + 3); // bsid, bsmod + acmod = data.readBits(3); + if ((acmod & 0x01) != 0 && acmod != 1) { + data.skipBits(2); // cmixlev + } + if ((acmod & 0x04) != 0) { + data.skipBits(2); // surmixlev + } + if (acmod == 2) { + data.skipBits(2); // dsurmod + } sampleRate = SAMPLE_RATE_BY_FSCOD[fscod]; + sampleCount = AC3_SYNCFRAME_AUDIO_SAMPLE_COUNT; } - int acmod = data.readBits(3); boolean lfeon = data.readBit(); - return Format.createAudioSampleFormat(trackId, MimeTypes.AUDIO_E_AC3, null, Format.NO_VALUE, - Format.NO_VALUE, CHANNEL_COUNT_BY_ACMOD[acmod] + (lfeon ? 1 : 0), sampleRate, null, - drmInitData, 0, language); + int channelCount = CHANNEL_COUNT_BY_ACMOD[acmod] + (lfeon ? 1 : 0); + return new Ac3SyncFrameInfo(mimeType, channelCount, sampleRate, frameSize, sampleCount); } /** @@ -187,16 +224,6 @@ public final class Ac3Util { return getAc3SyncframeSize(fscod, frmsizecod); } - /** - * Returns the size in bytes of the given E-AC-3 syncframe. - * - * @param data The syncframe to parse. - * @return The syncframe size in bytes. - */ - public static int parseEAc3SyncframeSize(byte[] data) { - return 2 * (((data[2] & 0x07) << 8) + (data[3] & 0xFF) + 1); // frmsiz - } - /** * Returns the number of audio samples in an AC-3 syncframe. */ @@ -205,22 +232,10 @@ public final class Ac3Util { } /** - * Returns the number of audio samples represented by the given E-AC-3 syncframe. + * Reads the number of audio samples represented by the given E-AC-3 syncframe. The buffer's + * position is not modified. * - * @param data The syncframe to parse. - * @return The number of audio samples represented by the syncframe. - */ - public static int parseEAc3SyncframeAudioSampleCount(byte[] data) { - // See ETSI TS 102 366 subsection E.1.2.2. - return AUDIO_SAMPLES_PER_AUDIO_BLOCK * (((data[4] & 0xC0) >> 6) == 0x03 ? 6 // fscod - : BLOCKS_PER_SYNCFRAME_BY_NUMBLKSCOD[(data[4] & 0x30) >> 4]); - } - - /** - * Like {@link #parseEAc3SyncframeAudioSampleCount(byte[])} but reads from a {@link ByteBuffer}. - * The buffer's position is not modified. - * - * @param buffer The {@link ByteBuffer} from which to read. + * @param buffer The {@link ByteBuffer} from which to read the syncframe. * @return The number of audio samples represented by the syncframe. */ public static int parseEAc3SyncframeAudioSampleCount(ByteBuffer buffer) { diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/Ac3Reader.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/Ac3Reader.java index 248161f28f..58d5310bd7 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/Ac3Reader.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/Ac3Reader.java @@ -52,7 +52,6 @@ public final class Ac3Reader implements ElementaryStreamReader { private long sampleDurationUs; private Format format; private int sampleSize; - private boolean isEac3; // Used when reading the samples. private long timeUs; @@ -177,26 +176,22 @@ public final class Ac3Reader implements ElementaryStreamReader { /** * Parses the sample header. */ + @SuppressWarnings("ReferenceEquality") private void parseHeader() { - if (format == null) { - // We read ahead to distinguish between AC-3 and E-AC-3. - headerScratchBits.skipBits(40); - isEac3 = headerScratchBits.readBits(5) == 16; - headerScratchBits.setPosition(headerScratchBits.getPosition() - 45); - format = isEac3 - ? Ac3Util.parseEac3SyncframeFormat(headerScratchBits, trackFormatId, language , null) - : Ac3Util.parseAc3SyncframeFormat(headerScratchBits, trackFormatId, language, null); + headerScratchBits.setPosition(0); + Ac3Util.Ac3SyncFrameInfo frameInfo = Ac3Util.parseAc3SyncframeInfo(headerScratchBits); + if (format == null || frameInfo.channelCount != format.channelCount + || frameInfo.sampleRate != format.sampleRate + || frameInfo.mimeType != format.sampleMimeType) { + format = Format.createAudioSampleFormat(trackFormatId, frameInfo.mimeType, null, + Format.NO_VALUE, Format.NO_VALUE, frameInfo.channelCount, frameInfo.sampleRate, null, + null, 0, language); output.format(format); } - sampleSize = isEac3 ? Ac3Util.parseEAc3SyncframeSize(headerScratchBits.data) - : Ac3Util.parseAc3SyncframeSize(headerScratchBits.data); - int audioSamplesPerSyncframe = isEac3 - ? Ac3Util.parseEAc3SyncframeAudioSampleCount(headerScratchBits.data) - : Ac3Util.getAc3SyncframeAudioSampleCount(); + sampleSize = frameInfo.frameSize; // In this class a sample is an access unit (syncframe in AC-3), but the MediaFormat sample rate // specifies the number of PCM audio samples per second. - sampleDurationUs = - (int) (C.MICROS_PER_SECOND * audioSamplesPerSyncframe / format.sampleRate); + sampleDurationUs = C.MICROS_PER_SECOND * frameInfo.sampleCount / format.sampleRate; } }