mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
parent
a626a5e5d4
commit
a4ff13d7da
5 changed files with 79 additions and 32 deletions
|
|
@ -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;
|
||||||
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;
|
||||||
}
|
}
|
||||||
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;
|
||||||
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;
|
||||||
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;
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 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;
|
||||||
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;
|
||||||
}
|
}
|
||||||
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 {
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
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 {
|
||||||
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 {
|
||||||
* @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 {
|
||||||
* @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 {
|
||||||
* @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]) {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue