Additional extraction for AC3

This commit is contained in:
Oliver Woodman 2014-11-13 16:13:55 +00:00
parent 456d53e178
commit d14e11c507
2 changed files with 117 additions and 20 deletions

View File

@ -24,6 +24,10 @@ import java.util.ArrayList;
public static final int TYPE_esds = 0x65736473; public static final int TYPE_esds = 0x65736473;
public static final int TYPE_mdat = 0x6D646174; public static final int TYPE_mdat = 0x6D646174;
public static final int TYPE_mp4a = 0x6D703461; public static final int TYPE_mp4a = 0x6D703461;
public static final int TYPE_ac_3 = 0x61632D33; // ac-3
public static final int TYPE_dac3 = 0x64616333;
public static final int TYPE_ec_3 = 0x65632D33; // ec-3
public static final int TYPE_dec3 = 0x64656333;
public static final int TYPE_tfdt = 0x74666474; public static final int TYPE_tfdt = 0x74666474;
public static final int TYPE_tfhd = 0x74666864; public static final int TYPE_tfhd = 0x74666864;
public static final int TYPE_trex = 0x74726578; public static final int TYPE_trex = 0x74726578;

View File

@ -65,6 +65,11 @@ public final class FragmentedMp4Extractor implements Extractor {
private static final byte[] NAL_START_CODE = new byte[] {0, 0, 0, 1}; private static final byte[] NAL_START_CODE = new byte[] {0, 0, 0, 1};
private static final byte[] PIFF_SAMPLE_ENCRYPTION_BOX_EXTENDED_TYPE = private static final byte[] PIFF_SAMPLE_ENCRYPTION_BOX_EXTENDED_TYPE =
new byte[] {-94, 57, 79, 82, 90, -101, 79, 20, -94, 68, 108, 66, 124, 100, -115, -12}; new byte[] {-94, 57, 79, 82, 90, -101, 79, 20, -94, 68, 108, 66, 124, 100, -115, -12};
/** Channel counts for AC-3 audio, indexed by acmod. (See ETSI TS 102 366.) */
private static final int[] AC3_CHANNEL_COUNTS = new int[] {2, 1, 2, 3, 3, 4, 4, 5};
/** Nominal bit-rates for AC-3 audio in kbps, indexed by bit_rate_code. (See ETSI TS 102 366.) */
private static final int[] AC3_BIT_RATES = new int[] {32, 40, 48, 56, 64, 80, 96, 112, 128, 160,
192, 224, 256, 320, 384, 448, 512, 576, 640};
// Parser states // Parser states
private static final int STATE_READING_ATOM_HEADER = 0; private static final int STATE_READING_ATOM_HEADER = 0;
@ -512,11 +517,12 @@ public final class FragmentedMp4Extractor implements Extractor {
parseAvcFromParent(stsd, childStartPosition, childAtomSize); parseAvcFromParent(stsd, childStartPosition, childAtomSize);
mediaFormat = avc.first; mediaFormat = avc.first;
trackEncryptionBoxes[i] = avc.second; trackEncryptionBoxes[i] = avc.second;
} else if (childAtomType == Atom.TYPE_mp4a || childAtomType == Atom.TYPE_enca) { } else if (childAtomType == Atom.TYPE_mp4a || childAtomType == Atom.TYPE_enca
Pair<MediaFormat, TrackEncryptionBox> mp4a = || childAtomType == Atom.TYPE_ac_3) {
parseMp4aFromParent(stsd, childStartPosition, childAtomSize); Pair<MediaFormat, TrackEncryptionBox> audioSampleEntry =
mediaFormat = mp4a.first; parseAudioSampleEntry(stsd, childAtomType, childStartPosition, childAtomSize);
trackEncryptionBoxes[i] = mp4a.second; mediaFormat = audioSampleEntry.first;
trackEncryptionBoxes[i] = audioSampleEntry.second;
} }
stsd.setPosition(childStartPosition + childAtomSize); stsd.setPosition(childStartPosition + childAtomSize);
} }
@ -556,15 +562,15 @@ public final class FragmentedMp4Extractor implements Extractor {
return Pair.create(format, trackEncryptionBox); return Pair.create(format, trackEncryptionBox);
} }
private static Pair<MediaFormat, TrackEncryptionBox> parseMp4aFromParent(ParsableByteArray parent, private static Pair<MediaFormat, TrackEncryptionBox> parseAudioSampleEntry(
int position, int size) { ParsableByteArray parent, int atomType, int position, int size) {
parent.setPosition(position + ATOM_HEADER_SIZE); parent.setPosition(position + ATOM_HEADER_SIZE);
// Start of the mp4a atom (defined in 14496-14)
parent.skip(16); parent.skip(16);
int channelCount = parent.readUnsignedShort(); int channelCount = parent.readUnsignedShort();
int sampleSize = parent.readUnsignedShort(); int sampleSize = parent.readUnsignedShort();
parent.skip(4); parent.skip(4);
int sampleRate = parent.readUnsignedFixedPoint1616(); int sampleRate = parent.readUnsignedFixedPoint1616();
int bitrate = MediaFormat.NO_VALUE;
byte[] initializationData = null; byte[] initializationData = null;
TrackEncryptionBox trackEncryptionBox = null; TrackEncryptionBox trackEncryptionBox = null;
@ -574,25 +580,97 @@ public final class FragmentedMp4Extractor implements Extractor {
int childStartPosition = parent.getPosition(); int childStartPosition = parent.getPosition();
int childAtomSize = parent.readInt(); int childAtomSize = parent.readInt();
int childAtomType = parent.readInt(); int childAtomType = parent.readInt();
if (childAtomType == Atom.TYPE_esds) { if (atomType == Atom.TYPE_mp4a || atomType == Atom.TYPE_enca) {
initializationData = parseEsdsFromParent(parent, childStartPosition); if (childAtomType == Atom.TYPE_esds) {
// TODO: Do we really need to do this? See [redacted] initializationData = parseEsdsFromParent(parent, childStartPosition);
// Update sampleRate and channelCount from the AudioSpecificConfig initialization data. // TODO: Do we really need to do this? See [redacted]
Pair<Integer, Integer> audioSpecificConfig = // Update sampleRate and channelCount from the AudioSpecificConfig initialization data.
CodecSpecificDataUtil.parseAudioSpecificConfig(initializationData); Pair<Integer, Integer> audioSpecificConfig =
sampleRate = audioSpecificConfig.first; CodecSpecificDataUtil.parseAudioSpecificConfig(initializationData);
channelCount = audioSpecificConfig.second; sampleRate = audioSpecificConfig.first;
} else if (childAtomType == Atom.TYPE_sinf) { channelCount = audioSpecificConfig.second;
trackEncryptionBox = parseSinfFromParent(parent, childStartPosition, childAtomSize); } else if (childAtomType == Atom.TYPE_sinf) {
trackEncryptionBox = parseSinfFromParent(parent, childStartPosition, childAtomSize);
}
} else if (atomType == Atom.TYPE_ac_3 && childAtomType == Atom.TYPE_dac3) {
// TODO: Choose the right AC-3 track based on the contents of dac3/dec3.
Ac3Format ac3Format =
parseAc3SpecificBoxFromParent(parent, childStartPosition);
if (ac3Format != null) {
sampleRate = ac3Format.sampleRate;
channelCount = ac3Format.channelCount;
bitrate = ac3Format.bitrate;
}
// TODO: Add support for encrypted AC-3.
trackEncryptionBox = null;
} else if (atomType == Atom.TYPE_ec_3 && childAtomType == Atom.TYPE_dec3) {
sampleRate = parseEc3SpecificBoxFromParent(parent, childStartPosition);
trackEncryptionBox = null;
} }
childPosition += childAtomSize; childPosition += childAtomSize;
} }
MediaFormat format = MediaFormat.createAudioFormat("audio/mp4a-latm", sampleSize, channelCount, String mimeType;
sampleRate, Collections.singletonList(initializationData)); if (atomType == Atom.TYPE_ac_3) {
mimeType = MimeTypes.AUDIO_AC3;
} else if (atomType == Atom.TYPE_ec_3) {
mimeType = MimeTypes.AUDIO_EC3;
} else {
mimeType = MimeTypes.AUDIO_AAC;
}
MediaFormat format = MediaFormat.createAudioFormat(
mimeType, sampleSize, channelCount, sampleRate, bitrate,
initializationData == null ? null : Collections.singletonList(initializationData));
return Pair.create(format, trackEncryptionBox); return Pair.create(format, trackEncryptionBox);
} }
private static Ac3Format parseAc3SpecificBoxFromParent(ParsableByteArray parent, int position) {
// Start of the dac3 atom (defined in ETSI TS 102 366)
parent.setPosition(position + ATOM_HEADER_SIZE);
// fscod (sample rate code)
int fscod = (parent.readUnsignedByte() & 0xC0) >> 6;
int sampleRate;
switch (fscod) {
case 0:
sampleRate = 48000;
break;
case 1:
sampleRate = 44100;
break;
case 2:
sampleRate = 32000;
break;
default:
// TODO: The decoder should not use this stream.
return null;
}
int nextByte = parent.readUnsignedByte();
// Map acmod (audio coding mode) onto a channel count.
int channelCount = AC3_CHANNEL_COUNTS[(nextByte & 0x38) >> 3];
// lfeon (low frequency effects on)
if ((nextByte & 0x04) != 0) {
channelCount++;
}
// Map bit_rate_code onto a bit-rate in kbit/s.
int bitrate = AC3_BIT_RATES[((nextByte & 0x03) << 3) + (parent.readUnsignedByte() >> 5)];
return new Ac3Format(channelCount, sampleRate, bitrate);
}
private static int parseEc3SpecificBoxFromParent(ParsableByteArray parent, int position) {
// Start of the dec3 atom (defined in ETSI TS 102 366)
parent.setPosition(position + ATOM_HEADER_SIZE);
// TODO: Implement parsing for enhanced AC-3 with multiple sub-streams.
return 0;
}
private static List<byte[]> parseAvcCFromParent(ParsableByteArray parent, int position) { private static List<byte[]> parseAvcCFromParent(ParsableByteArray parent, int position) {
parent.setPosition(position + ATOM_HEADER_SIZE + 4); parent.setPosition(position + ATOM_HEADER_SIZE + 4);
// Start of the AVCDecoderConfigurationRecord (defined in 14496-15) // Start of the AVCDecoderConfigurationRecord (defined in 14496-15)
@ -1182,4 +1260,19 @@ public final class FragmentedMp4Extractor implements Extractor {
return result; return result;
} }
/** Represents the format for AC-3 audio. */
private static final class Ac3Format {
public final int channelCount;
public final int sampleRate;
public final int bitrate;
public Ac3Format(int channelCount, int sampleRate, int bitrate) {
this.channelCount = channelCount;
this.sampleRate = sampleRate;
this.bitrate = bitrate;
}
}
} }