Set a MIME type based on the esds ObjectTypeIndication.

Issue: #576
This commit is contained in:
Oliver Woodman 2015-06-26 14:37:48 +01:00
parent a626a5e5d4
commit a4ff13d7da
5 changed files with 79 additions and 32 deletions

View File

@ -64,8 +64,9 @@ import java.util.List;
long mediaTimescale = parseMdhd(mdia.getLeafAtomOfType(Atom.TYPE_mdhd).data);
StsdDataHolder stsdData = parseStsd(stbl.getLeafAtomOfType(Atom.TYPE_stsd).data, durationUs);
return new Track(id, trackType, mediaTimescale, durationUs, stsdData.mediaFormat,
stsdData.trackEncryptionBoxes, stsdData.nalUnitLengthFieldLength);
return stsdData.mediaFormat == null ? null
: new Track(id, trackType, mediaTimescale, durationUs, stsdData.mediaFormat,
stsdData.trackEncryptionBoxes, stsdData.nalUnitLengthFieldLength);
}
/**
@ -388,9 +389,10 @@ import java.util.List;
out.nalUnitLengthFieldLength = hvcCData.second;
} else if (childAtomType == Atom.TYPE_esds) {
Assertions.checkState(mimeType == null);
mimeType = MimeTypes.VIDEO_MP4V;
initializationData =
Collections.singletonList(parseEsdsFromParent(parent, childStartPosition));
Pair<String, byte[]> mimeTypeAndInitializationData =
parseEsdsFromParent(parent, childStartPosition);
mimeType = mimeTypeAndInitializationData.first;
initializationData = Collections.singletonList(mimeTypeAndInitializationData.second);
} else if (childAtomType == Atom.TYPE_sinf) {
out.trackEncryptionBoxes[entryIndex] =
parseSinfFromParent(parent, childStartPosition, childAtomSize);
@ -399,6 +401,12 @@ import java.util.List;
}
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,
width, height, pixelWidthHeightRatio, initializationData);
}
@ -529,6 +537,14 @@ import java.util.List;
parent.skipBytes(4);
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;
int childPosition = parent.getPosition();
while (childPosition - position < size) {
@ -539,13 +555,18 @@ import java.util.List;
int childAtomType = parent.readInt();
if (atomType == Atom.TYPE_mp4a || atomType == Atom.TYPE_enca) {
if (childAtomType == Atom.TYPE_esds) {
initializationData = parseEsdsFromParent(parent, childStartPosition);
// TODO: Do we really need to do this? See [Internal: b/10903778]
// Update sampleRate and channelCount from the AudioSpecificConfig initialization data.
Pair<Integer, Integer> audioSpecificConfig =
CodecSpecificDataUtil.parseAudioSpecificConfig(initializationData);
sampleRate = audioSpecificConfig.first;
channelCount = audioSpecificConfig.second;
Pair<String, byte[]> mimeTypeAndInitializationData =
parseEsdsFromParent(parent, childStartPosition);
mimeType = mimeTypeAndInitializationData.first;
initializationData = mimeTypeAndInitializationData.second;
if (MimeTypes.AUDIO_AAC.equals(mimeType)) {
// TODO: Do we really need to do this? See [Internal: b/10903778]
// 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) {
out.trackEncryptionBoxes[entryIndex] = parseSinfFromParent(parent, childStartPosition,
childAtomSize);
@ -564,14 +585,9 @@ import java.util.List;
childPosition += childAtomSize;
}
// Set the MIME type for ac-3/ec-3 atoms even if the dac3/dec3 child atom is missing.
String mimeType;
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;
// If the media type was not recognized, ignore the track.
if (mimeType == null) {
return;
}
out.mediaFormat = MediaFormat.createAudioFormat(mimeType, sampleSize, durationUs, channelCount,
@ -580,7 +596,7 @@ import java.util.List;
}
/** 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);
// Start of the ES_Descriptor (defined in 14496-1)
parent.skipBytes(1); // ES_Descriptor tag
@ -607,9 +623,40 @@ import java.util.List;
while (varIntByte > 127) {
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
varIntByte = parent.readUnsignedByte();
int varInt = varIntByte & 0x7F;
@ -620,7 +667,7 @@ import java.util.List;
}
byte[] initializationData = new byte[varInt];
parent.readBytes(initializationData, 0, varInt);
return initializationData;
return Pair.create(mimeType, initializationData);
}
private AtomParsers() {

View File

@ -225,8 +225,8 @@ public final class Mp4Extractor implements Extractor, SeekMap {
}
Track track = AtomParsers.parseTrak(atom, moov.getLeafAtomOfType(Atom.TYPE_mvhd));
if (track == null || track.mediaFormat == null || (track.type != Track.TYPE_AUDIO
&& track.type != Track.TYPE_VIDEO && track.type != Track.TYPE_TEXT)) {
if (track == null || (track.type != Track.TYPE_AUDIO && track.type != Track.TYPE_VIDEO
&& track.type != Track.TYPE_TEXT)) {
continue;
}

View File

@ -165,9 +165,9 @@ import java.util.Collections;
adtsScratch.skipBits(1);
int channelConfig = adtsScratch.readBits(3);
byte[] audioSpecificConfig = CodecSpecificDataUtil.buildAudioSpecificConfig(
byte[] audioSpecificConfig = CodecSpecificDataUtil.buildAacAudioSpecificConfig(
audioObjectType, sampleRateIndex, channelConfig);
Pair<Integer, Integer> audioParams = CodecSpecificDataUtil.parseAudioSpecificConfig(
Pair<Integer, Integer> audioParams = CodecSpecificDataUtil.parseAacAudioSpecificConfig(
audioSpecificConfig);
MediaFormat mediaFormat = MediaFormat.createAudioFormat(MimeTypes.AUDIO_AAC,

View File

@ -388,7 +388,7 @@ public class SmoothStreamingChunkSource implements ChunkSource {
if (trackElement.csd != null) {
csd = Arrays.asList(trackElement.csd);
} else {
csd = Collections.singletonList(CodecSpecificDataUtil.buildAudioSpecificConfig(
csd = Collections.singletonList(CodecSpecificDataUtil.buildAacAudioSpecificConfig(
trackFormat.audioSamplingRate, trackFormat.numChannels));
}
MediaFormat format = MediaFormat.createAudioFormat(mimeType, MediaFormat.NO_VALUE,

View File

@ -47,7 +47,7 @@ public final class CodecSpecificDataUtil {
* @param audioSpecificConfig The AudioSpecificConfig to parse.
* @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 byteOffset = audioObjectType == 5 || audioObjectType == 29 ? 1 : 0;
int frequencyIndex = (audioSpecificConfig[byteOffset] & 0x7) << 1
@ -66,7 +66,7 @@ public final class CodecSpecificDataUtil {
* @param channelConfig The channel configuration.
* @return The AudioSpecificConfig.
*/
public static byte[] buildAudioSpecificConfig(int audioObjectType, int sampleRateIndex,
public static byte[] buildAacAudioSpecificConfig(int audioObjectType, int sampleRateIndex,
int channelConfig) {
byte[] audioSpecificConfig = new byte[2];
audioSpecificConfig[0] = (byte) ((audioObjectType << 3) & 0xF8 | (sampleRateIndex >> 1) & 0x07);
@ -81,7 +81,7 @@ public final class CodecSpecificDataUtil {
* @param numChannels The number of channels.
* @return The AudioSpecificConfig.
*/
public static byte[] buildAudioSpecificConfig(int sampleRate, int numChannels) {
public static byte[] buildAacAudioSpecificConfig(int sampleRate, int numChannels) {
int sampleRateIndex = -1;
for (int i = 0; i < AUDIO_SPECIFIC_CONFIG_SAMPLING_RATE_TABLE.length; ++i) {
if (sampleRate == AUDIO_SPECIFIC_CONFIG_SAMPLING_RATE_TABLE[i]) {