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:
parent
147020f816
commit
8da2e2c8d5
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user