Commit a4ff13d7 by Oliver Woodman

Set a MIME type based on the esds ObjectTypeIndication.

Issue: #576
parent a626a5e5
...@@ -64,8 +64,9 @@ import java.util.List; ...@@ -64,8 +64,9 @@ import java.util.List;
long mediaTimescale = parseMdhd(mdia.getLeafAtomOfType(Atom.TYPE_mdhd).data); long mediaTimescale = parseMdhd(mdia.getLeafAtomOfType(Atom.TYPE_mdhd).data);
StsdDataHolder stsdData = parseStsd(stbl.getLeafAtomOfType(Atom.TYPE_stsd).data, durationUs); StsdDataHolder stsdData = parseStsd(stbl.getLeafAtomOfType(Atom.TYPE_stsd).data, durationUs);
return new Track(id, trackType, mediaTimescale, durationUs, stsdData.mediaFormat, return stsdData.mediaFormat == null ? null
stsdData.trackEncryptionBoxes, stsdData.nalUnitLengthFieldLength); : new Track(id, trackType, mediaTimescale, durationUs, stsdData.mediaFormat,
stsdData.trackEncryptionBoxes, stsdData.nalUnitLengthFieldLength);
} }
/** /**
...@@ -388,9 +389,10 @@ import java.util.List; ...@@ -388,9 +389,10 @@ import java.util.List;
out.nalUnitLengthFieldLength = hvcCData.second; out.nalUnitLengthFieldLength = hvcCData.second;
} else if (childAtomType == Atom.TYPE_esds) { } else if (childAtomType == Atom.TYPE_esds) {
Assertions.checkState(mimeType == null); Assertions.checkState(mimeType == null);
mimeType = MimeTypes.VIDEO_MP4V; Pair<String, byte[]> mimeTypeAndInitializationData =
initializationData = parseEsdsFromParent(parent, childStartPosition);
Collections.singletonList(parseEsdsFromParent(parent, childStartPosition)); mimeType = mimeTypeAndInitializationData.first;
initializationData = Collections.singletonList(mimeTypeAndInitializationData.second);
} else if (childAtomType == Atom.TYPE_sinf) { } else if (childAtomType == Atom.TYPE_sinf) {
out.trackEncryptionBoxes[entryIndex] = out.trackEncryptionBoxes[entryIndex] =
parseSinfFromParent(parent, childStartPosition, childAtomSize); parseSinfFromParent(parent, childStartPosition, childAtomSize);
...@@ -399,6 +401,12 @@ import java.util.List; ...@@ -399,6 +401,12 @@ import java.util.List;
} }
childPosition += childAtomSize; childPosition += childAtomSize;
} }
// If the media type was not recognized, ignore the track.
if (mimeType == null) {
return;
}
out.mediaFormat = MediaFormat.createVideoFormat(mimeType, MediaFormat.NO_VALUE, durationUs, out.mediaFormat = MediaFormat.createVideoFormat(mimeType, MediaFormat.NO_VALUE, durationUs,
width, height, pixelWidthHeightRatio, initializationData); width, height, pixelWidthHeightRatio, initializationData);
} }
...@@ -529,6 +537,14 @@ import java.util.List; ...@@ -529,6 +537,14 @@ import java.util.List;
parent.skipBytes(4); parent.skipBytes(4);
int sampleRate = parent.readUnsignedFixedPoint1616(); int sampleRate = parent.readUnsignedFixedPoint1616();
// If the atom type determines a MIME type, set it immediately.
String mimeType = null;
if (atomType == Atom.TYPE_ac_3) {
mimeType = MimeTypes.AUDIO_AC3;
} else if (atomType == Atom.TYPE_ec_3) {
mimeType = MimeTypes.AUDIO_EC3;
}
byte[] initializationData = null; byte[] initializationData = null;
int childPosition = parent.getPosition(); int childPosition = parent.getPosition();
while (childPosition - position < size) { while (childPosition - position < size) {
...@@ -539,13 +555,18 @@ import java.util.List; ...@@ -539,13 +555,18 @@ import java.util.List;
int childAtomType = parent.readInt(); int childAtomType = parent.readInt();
if (atomType == Atom.TYPE_mp4a || atomType == Atom.TYPE_enca) { if (atomType == Atom.TYPE_mp4a || atomType == Atom.TYPE_enca) {
if (childAtomType == Atom.TYPE_esds) { if (childAtomType == Atom.TYPE_esds) {
initializationData = parseEsdsFromParent(parent, childStartPosition); Pair<String, byte[]> mimeTypeAndInitializationData =
// TODO: Do we really need to do this? See [Internal: b/10903778] parseEsdsFromParent(parent, childStartPosition);
// Update sampleRate and channelCount from the AudioSpecificConfig initialization data. mimeType = mimeTypeAndInitializationData.first;
Pair<Integer, Integer> audioSpecificConfig = initializationData = mimeTypeAndInitializationData.second;
CodecSpecificDataUtil.parseAudioSpecificConfig(initializationData); if (MimeTypes.AUDIO_AAC.equals(mimeType)) {
sampleRate = audioSpecificConfig.first; // TODO: Do we really need to do this? See [Internal: b/10903778]
channelCount = audioSpecificConfig.second; // Update sampleRate and channelCount from the AudioSpecificConfig initialization data.
Pair<Integer, Integer> audioSpecificConfig =
CodecSpecificDataUtil.parseAacAudioSpecificConfig(initializationData);
sampleRate = audioSpecificConfig.first;
channelCount = audioSpecificConfig.second;
}
} else if (childAtomType == Atom.TYPE_sinf) { } else if (childAtomType == Atom.TYPE_sinf) {
out.trackEncryptionBoxes[entryIndex] = parseSinfFromParent(parent, childStartPosition, out.trackEncryptionBoxes[entryIndex] = parseSinfFromParent(parent, childStartPosition,
childAtomSize); childAtomSize);
...@@ -564,14 +585,9 @@ import java.util.List; ...@@ -564,14 +585,9 @@ import java.util.List;
childPosition += childAtomSize; childPosition += childAtomSize;
} }
// Set the MIME type for ac-3/ec-3 atoms even if the dac3/dec3 child atom is missing. // If the media type was not recognized, ignore the track.
String mimeType; if (mimeType == null) {
if (atomType == Atom.TYPE_ac_3) { return;
mimeType = MimeTypes.AUDIO_AC3;
} else if (atomType == Atom.TYPE_ec_3) {
mimeType = MimeTypes.AUDIO_EC3;
} else {
mimeType = MimeTypes.AUDIO_AAC;
} }
out.mediaFormat = MediaFormat.createAudioFormat(mimeType, sampleSize, durationUs, channelCount, out.mediaFormat = MediaFormat.createAudioFormat(mimeType, sampleSize, durationUs, channelCount,
...@@ -580,7 +596,7 @@ import java.util.List; ...@@ -580,7 +596,7 @@ import java.util.List;
} }
/** Returns codec-specific initialization data contained in an esds box. */ /** Returns codec-specific initialization data contained in an esds box. */
private static byte[] parseEsdsFromParent(ParsableByteArray parent, int position) { private static Pair<String, byte[]> parseEsdsFromParent(ParsableByteArray parent, int position) {
parent.setPosition(position + Atom.HEADER_SIZE + 4); parent.setPosition(position + Atom.HEADER_SIZE + 4);
// Start of the ES_Descriptor (defined in 14496-1) // Start of the ES_Descriptor (defined in 14496-1)
parent.skipBytes(1); // ES_Descriptor tag parent.skipBytes(1); // ES_Descriptor tag
...@@ -607,9 +623,40 @@ import java.util.List; ...@@ -607,9 +623,40 @@ import java.util.List;
while (varIntByte > 127) { while (varIntByte > 127) {
varIntByte = parent.readUnsignedByte(); varIntByte = parent.readUnsignedByte();
} }
parent.skipBytes(13);
// Start of AudioSpecificConfig (defined in 14496-3) // Set the MIME type based on the object type indication (14496-1 table 5).
int objectTypeIndication = parent.readUnsignedByte();
String mimeType;
switch (objectTypeIndication) {
case 0x20:
mimeType = MimeTypes.VIDEO_MP4V;
break;
case 0x21:
mimeType = MimeTypes.VIDEO_H264;
break;
case 0x23:
mimeType = MimeTypes.VIDEO_H265;
break;
case 0x40:
mimeType = MimeTypes.AUDIO_AAC;
break;
case 0x6B:
mimeType = MimeTypes.AUDIO_MPEG;
break;
case 0xA5:
mimeType = MimeTypes.AUDIO_AC3;
break;
case 0xA6:
mimeType = MimeTypes.AUDIO_EC3;
break;
default:
mimeType = null;
break;
}
parent.skipBytes(12);
// Start of the AudioSpecificConfig.
parent.skipBytes(1); // AudioSpecificConfig tag parent.skipBytes(1); // AudioSpecificConfig tag
varIntByte = parent.readUnsignedByte(); varIntByte = parent.readUnsignedByte();
int varInt = varIntByte & 0x7F; int varInt = varIntByte & 0x7F;
...@@ -620,7 +667,7 @@ import java.util.List; ...@@ -620,7 +667,7 @@ import java.util.List;
} }
byte[] initializationData = new byte[varInt]; byte[] initializationData = new byte[varInt];
parent.readBytes(initializationData, 0, varInt); parent.readBytes(initializationData, 0, varInt);
return initializationData; return Pair.create(mimeType, initializationData);
} }
private AtomParsers() { private AtomParsers() {
......
...@@ -225,8 +225,8 @@ public final class Mp4Extractor implements Extractor, SeekMap { ...@@ -225,8 +225,8 @@ public final class Mp4Extractor implements Extractor, SeekMap {
} }
Track track = AtomParsers.parseTrak(atom, moov.getLeafAtomOfType(Atom.TYPE_mvhd)); Track track = AtomParsers.parseTrak(atom, moov.getLeafAtomOfType(Atom.TYPE_mvhd));
if (track == null || track.mediaFormat == null || (track.type != Track.TYPE_AUDIO if (track == null || (track.type != Track.TYPE_AUDIO && track.type != Track.TYPE_VIDEO
&& track.type != Track.TYPE_VIDEO && track.type != Track.TYPE_TEXT)) { && track.type != Track.TYPE_TEXT)) {
continue; continue;
} }
......
...@@ -165,9 +165,9 @@ import java.util.Collections; ...@@ -165,9 +165,9 @@ import java.util.Collections;
adtsScratch.skipBits(1); adtsScratch.skipBits(1);
int channelConfig = adtsScratch.readBits(3); int channelConfig = adtsScratch.readBits(3);
byte[] audioSpecificConfig = CodecSpecificDataUtil.buildAudioSpecificConfig( byte[] audioSpecificConfig = CodecSpecificDataUtil.buildAacAudioSpecificConfig(
audioObjectType, sampleRateIndex, channelConfig); audioObjectType, sampleRateIndex, channelConfig);
Pair<Integer, Integer> audioParams = CodecSpecificDataUtil.parseAudioSpecificConfig( Pair<Integer, Integer> audioParams = CodecSpecificDataUtil.parseAacAudioSpecificConfig(
audioSpecificConfig); audioSpecificConfig);
MediaFormat mediaFormat = MediaFormat.createAudioFormat(MimeTypes.AUDIO_AAC, MediaFormat mediaFormat = MediaFormat.createAudioFormat(MimeTypes.AUDIO_AAC,
......
...@@ -388,7 +388,7 @@ public class SmoothStreamingChunkSource implements ChunkSource { ...@@ -388,7 +388,7 @@ public class SmoothStreamingChunkSource implements ChunkSource {
if (trackElement.csd != null) { if (trackElement.csd != null) {
csd = Arrays.asList(trackElement.csd); csd = Arrays.asList(trackElement.csd);
} else { } else {
csd = Collections.singletonList(CodecSpecificDataUtil.buildAudioSpecificConfig( csd = Collections.singletonList(CodecSpecificDataUtil.buildAacAudioSpecificConfig(
trackFormat.audioSamplingRate, trackFormat.numChannels)); trackFormat.audioSamplingRate, trackFormat.numChannels));
} }
MediaFormat format = MediaFormat.createAudioFormat(mimeType, MediaFormat.NO_VALUE, MediaFormat format = MediaFormat.createAudioFormat(mimeType, MediaFormat.NO_VALUE,
......
...@@ -47,7 +47,7 @@ public final class CodecSpecificDataUtil { ...@@ -47,7 +47,7 @@ public final class CodecSpecificDataUtil {
* @param audioSpecificConfig The AudioSpecificConfig to parse. * @param audioSpecificConfig The AudioSpecificConfig to parse.
* @return A pair consisting of the sample rate in Hz and the channel count. * @return A pair consisting of the sample rate in Hz and the channel count.
*/ */
public static Pair<Integer, Integer> parseAudioSpecificConfig(byte[] audioSpecificConfig) { public static Pair<Integer, Integer> parseAacAudioSpecificConfig(byte[] audioSpecificConfig) {
int audioObjectType = (audioSpecificConfig[0] >> 3) & 0x1F; int audioObjectType = (audioSpecificConfig[0] >> 3) & 0x1F;
int byteOffset = audioObjectType == 5 || audioObjectType == 29 ? 1 : 0; int byteOffset = audioObjectType == 5 || audioObjectType == 29 ? 1 : 0;
int frequencyIndex = (audioSpecificConfig[byteOffset] & 0x7) << 1 int frequencyIndex = (audioSpecificConfig[byteOffset] & 0x7) << 1
...@@ -66,7 +66,7 @@ public final class CodecSpecificDataUtil { ...@@ -66,7 +66,7 @@ public final class CodecSpecificDataUtil {
* @param channelConfig The channel configuration. * @param channelConfig The channel configuration.
* @return The AudioSpecificConfig. * @return The AudioSpecificConfig.
*/ */
public static byte[] buildAudioSpecificConfig(int audioObjectType, int sampleRateIndex, public static byte[] buildAacAudioSpecificConfig(int audioObjectType, int sampleRateIndex,
int channelConfig) { int channelConfig) {
byte[] audioSpecificConfig = new byte[2]; byte[] audioSpecificConfig = new byte[2];
audioSpecificConfig[0] = (byte) ((audioObjectType << 3) & 0xF8 | (sampleRateIndex >> 1) & 0x07); audioSpecificConfig[0] = (byte) ((audioObjectType << 3) & 0xF8 | (sampleRateIndex >> 1) & 0x07);
...@@ -81,7 +81,7 @@ public final class CodecSpecificDataUtil { ...@@ -81,7 +81,7 @@ public final class CodecSpecificDataUtil {
* @param numChannels The number of channels. * @param numChannels The number of channels.
* @return The AudioSpecificConfig. * @return The AudioSpecificConfig.
*/ */
public static byte[] buildAudioSpecificConfig(int sampleRate, int numChannels) { public static byte[] buildAacAudioSpecificConfig(int sampleRate, int numChannels) {
int sampleRateIndex = -1; int sampleRateIndex = -1;
for (int i = 0; i < AUDIO_SPECIFIC_CONFIG_SAMPLING_RATE_TABLE.length; ++i) { for (int i = 0; i < AUDIO_SPECIFIC_CONFIG_SAMPLING_RATE_TABLE.length; ++i) {
if (sampleRate == AUDIO_SPECIFIC_CONFIG_SAMPLING_RATE_TABLE[i]) { if (sampleRate == AUDIO_SPECIFIC_CONFIG_SAMPLING_RATE_TABLE[i]) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment