Track format changes in (E-)AC-3 syncframes

This allows propagating format changes to the track output.

Issue:#2552

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=152672983
This commit is contained in:
aquilescanta 2017-04-10 04:13:12 -07:00 committed by Oliver Woodman
parent 147020f816
commit 8da2e2c8d5
2 changed files with 100 additions and 90 deletions

View File

@ -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) {

View File

@ -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;
}
}