Parse stz2 Atoms during mp4 extraction.
------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=129892143
This commit is contained in:
parent
e883b7c270
commit
f6fdcee9b3
@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.google.android.exoplayer2.extractor.mp4;
|
||||||
|
|
||||||
|
import com.google.android.exoplayer2.util.ParsableByteArray;
|
||||||
|
import com.google.android.exoplayer2.util.Util;
|
||||||
|
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link AtomParsers}.
|
||||||
|
*/
|
||||||
|
public final class AtomParsersTest extends TestCase {
|
||||||
|
|
||||||
|
private static final String ATOM_HEADER = "000000000000000000000000";
|
||||||
|
private static final String SAMPLE_COUNT = "00000004";
|
||||||
|
private static final byte[] FOUR_BIT_STZ2 = Util.getBytesFromHexString(ATOM_HEADER + "00000004"
|
||||||
|
+ SAMPLE_COUNT + "1234");
|
||||||
|
private static final byte[] EIGHT_BIT_STZ2 = Util.getBytesFromHexString(ATOM_HEADER + "00000008"
|
||||||
|
+ SAMPLE_COUNT + "01020304");
|
||||||
|
private static final byte[] SIXTEEN_BIT_STZ2 = Util.getBytesFromHexString(ATOM_HEADER + "00000010"
|
||||||
|
+ SAMPLE_COUNT + "0001000200030004");
|
||||||
|
|
||||||
|
public void testStz2Parsing4BitFieldSize() {
|
||||||
|
verifyParsing(new Atom.LeafAtom(Atom.TYPE_stsz, new ParsableByteArray(FOUR_BIT_STZ2)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testStz2Parsing8BitFieldSize() {
|
||||||
|
verifyParsing(new Atom.LeafAtom(Atom.TYPE_stsz, new ParsableByteArray(EIGHT_BIT_STZ2)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testStz2Parsing16BitFieldSize() {
|
||||||
|
verifyParsing(new Atom.LeafAtom(Atom.TYPE_stsz, new ParsableByteArray(SIXTEEN_BIT_STZ2)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void verifyParsing(Atom.LeafAtom stz2Atom) {
|
||||||
|
AtomParsers.Stz2SampleSizeBox box = new AtomParsers.Stz2SampleSizeBox(stz2Atom);
|
||||||
|
assertEquals(4, box.getSampleCount());
|
||||||
|
assertFalse(box.isFixedSampleSize());
|
||||||
|
for (int i = 0; i < box.getSampleCount(); i++) {
|
||||||
|
assertEquals(i + 1, box.readNextSampleSize());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -111,6 +111,7 @@ import java.util.List;
|
|||||||
public static final int TYPE_ctts = Util.getIntegerCodeForString("ctts");
|
public static final int TYPE_ctts = Util.getIntegerCodeForString("ctts");
|
||||||
public static final int TYPE_stsc = Util.getIntegerCodeForString("stsc");
|
public static final int TYPE_stsc = Util.getIntegerCodeForString("stsc");
|
||||||
public static final int TYPE_stsz = Util.getIntegerCodeForString("stsz");
|
public static final int TYPE_stsz = Util.getIntegerCodeForString("stsz");
|
||||||
|
public static final int TYPE_stz2 = Util.getIntegerCodeForString("stz2");
|
||||||
public static final int TYPE_stco = Util.getIntegerCodeForString("stco");
|
public static final int TYPE_stco = Util.getIntegerCodeForString("stco");
|
||||||
public static final int TYPE_co64 = Util.getIntegerCodeForString("co64");
|
public static final int TYPE_co64 = Util.getIntegerCodeForString("co64");
|
||||||
public static final int TYPE_tx3g = Util.getIntegerCodeForString("tx3g");
|
public static final int TYPE_tx3g = Util.getIntegerCodeForString("tx3g");
|
||||||
|
@ -98,8 +98,22 @@ import java.util.List;
|
|||||||
*/
|
*/
|
||||||
public static TrackSampleTable parseStbl(Track track, Atom.ContainerAtom stblAtom,
|
public static TrackSampleTable parseStbl(Track track, Atom.ContainerAtom stblAtom,
|
||||||
GaplessInfoHolder gaplessInfoHolder) throws ParserException {
|
GaplessInfoHolder gaplessInfoHolder) throws ParserException {
|
||||||
// Array of sample sizes.
|
SampleSizeBox sampleSizeBox;
|
||||||
ParsableByteArray stsz = stblAtom.getLeafAtomOfType(Atom.TYPE_stsz).data;
|
Atom.LeafAtom stszAtom = stblAtom.getLeafAtomOfType(Atom.TYPE_stsz);
|
||||||
|
if (stszAtom != null) {
|
||||||
|
sampleSizeBox = new StszSampleSizeBox(stszAtom);
|
||||||
|
} else {
|
||||||
|
Atom.LeafAtom stz2Atom = stblAtom.getLeafAtomOfType(Atom.TYPE_stz2);
|
||||||
|
if (stz2Atom == null) {
|
||||||
|
throw new ParserException("Track has no sample table size information");
|
||||||
|
}
|
||||||
|
sampleSizeBox = new Stz2SampleSizeBox(stz2Atom);
|
||||||
|
}
|
||||||
|
|
||||||
|
int sampleCount = sampleSizeBox.getSampleCount();
|
||||||
|
if (sampleCount == 0) {
|
||||||
|
return new TrackSampleTable(new long[0], new int[0], 0, new long[0], new int[0]);
|
||||||
|
}
|
||||||
|
|
||||||
// Entries are byte offsets of chunks.
|
// Entries are byte offsets of chunks.
|
||||||
boolean chunkOffsetsAreLongs = false;
|
boolean chunkOffsetsAreLongs = false;
|
||||||
@ -120,14 +134,6 @@ import java.util.List;
|
|||||||
Atom.LeafAtom cttsAtom = stblAtom.getLeafAtomOfType(Atom.TYPE_ctts);
|
Atom.LeafAtom cttsAtom = stblAtom.getLeafAtomOfType(Atom.TYPE_ctts);
|
||||||
ParsableByteArray ctts = cttsAtom != null ? cttsAtom.data : null;
|
ParsableByteArray ctts = cttsAtom != null ? cttsAtom.data : null;
|
||||||
|
|
||||||
// Skip full atom.
|
|
||||||
stsz.setPosition(Atom.FULL_HEADER_SIZE);
|
|
||||||
int fixedSampleSize = stsz.readUnsignedIntToInt();
|
|
||||||
int sampleCount = stsz.readUnsignedIntToInt();
|
|
||||||
if (sampleCount == 0) {
|
|
||||||
return new TrackSampleTable(new long[0], new int[0], 0, new long[0], new int[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prepare to read chunk information.
|
// Prepare to read chunk information.
|
||||||
ChunkIterator chunkIterator = new ChunkIterator(stsc, chunkOffsets, chunkOffsetsAreLongs);
|
ChunkIterator chunkIterator = new ChunkIterator(stsc, chunkOffsets, chunkOffsetsAreLongs);
|
||||||
|
|
||||||
@ -160,7 +166,7 @@ import java.util.List;
|
|||||||
}
|
}
|
||||||
|
|
||||||
// True if we can rechunk fixed-sample-size data. Note that we only rechunk raw audio.
|
// True if we can rechunk fixed-sample-size data. Note that we only rechunk raw audio.
|
||||||
boolean isRechunkable = fixedSampleSize != 0
|
boolean isRechunkable = sampleSizeBox.isFixedSampleSize()
|
||||||
&& MimeTypes.AUDIO_RAW.equals(track.format.sampleMimeType)
|
&& MimeTypes.AUDIO_RAW.equals(track.format.sampleMimeType)
|
||||||
&& remainingTimestampDeltaChanges == 0 && remainingTimestampOffsetChanges == 0
|
&& remainingTimestampDeltaChanges == 0 && remainingTimestampOffsetChanges == 0
|
||||||
&& remainingSynchronizationSamples == 0;
|
&& remainingSynchronizationSamples == 0;
|
||||||
@ -204,7 +210,7 @@ import java.util.List;
|
|||||||
}
|
}
|
||||||
|
|
||||||
offsets[i] = offset;
|
offsets[i] = offset;
|
||||||
sizes[i] = fixedSampleSize == 0 ? stsz.readUnsignedIntToInt() : fixedSampleSize;
|
sizes[i] = sampleSizeBox.readNextSampleSize();
|
||||||
if (sizes[i] > maximumSize) {
|
if (sizes[i] > maximumSize) {
|
||||||
maximumSize = sizes[i];
|
maximumSize = sizes[i];
|
||||||
}
|
}
|
||||||
@ -253,6 +259,7 @@ import java.util.List;
|
|||||||
chunkOffsetsBytes[chunkIterator.index] = chunkIterator.offset;
|
chunkOffsetsBytes[chunkIterator.index] = chunkIterator.offset;
|
||||||
chunkSampleCounts[chunkIterator.index] = chunkIterator.numSamples;
|
chunkSampleCounts[chunkIterator.index] = chunkIterator.numSamples;
|
||||||
}
|
}
|
||||||
|
int fixedSampleSize = sampleSizeBox.readNextSampleSize();
|
||||||
FixedSampleSizeRechunker.Results rechunkedResults = FixedSampleSizeRechunker.rechunk(
|
FixedSampleSizeRechunker.Results rechunkedResults = FixedSampleSizeRechunker.rechunk(
|
||||||
fixedSampleSize, chunkOffsetsBytes, chunkSampleCounts, timestampDeltaInTimeUnits);
|
fixedSampleSize, chunkOffsetsBytes, chunkSampleCounts, timestampDeltaInTimeUnits);
|
||||||
offsets = rechunkedResults.offsets;
|
offsets = rechunkedResults.offsets;
|
||||||
@ -867,7 +874,9 @@ import java.util.List;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns the position of the esds box within a parent, or -1 if no esds box is found */
|
/**
|
||||||
|
* Returns the position of the esds box within a parent, or -1 if no esds box is found
|
||||||
|
*/
|
||||||
private static int findEsdsPosition(ParsableByteArray parent, int position, int size) {
|
private static int findEsdsPosition(ParsableByteArray parent, int position, int size) {
|
||||||
int childAtomPosition = parent.getPosition();
|
int childAtomPosition = parent.getPosition();
|
||||||
while (childAtomPosition - position < size) {
|
while (childAtomPosition - position < size) {
|
||||||
@ -883,7 +892,9 @@ import java.util.List;
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns codec-specific initialization data contained in an esds box. */
|
/**
|
||||||
|
* Returns codec-specific initialization data contained in an esds box.
|
||||||
|
*/
|
||||||
private static Pair<String, 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)
|
||||||
@ -1028,7 +1039,9 @@ import java.util.List;
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Parses the size of an expandable class, as specified by ISO 14496-1 subsection 8.3.3. */
|
/**
|
||||||
|
* Parses the size of an expandable class, as specified by ISO 14496-1 subsection 8.3.3.
|
||||||
|
*/
|
||||||
private static int parseExpandableClassSize(ParsableByteArray data) {
|
private static int parseExpandableClassSize(ParsableByteArray data) {
|
||||||
int currentByte = data.readUnsignedByte();
|
int currentByte = data.readUnsignedByte();
|
||||||
int size = currentByte & 0x7F;
|
int size = currentByte & 0x7F;
|
||||||
@ -1122,6 +1135,114 @@ import java.util.List;
|
|||||||
requiredSampleTransformation = Track.TRANSFORMATION_NONE;
|
requiredSampleTransformation = Track.TRANSFORMATION_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A box containing sample sizes (e.g. stsz, stz2).
|
||||||
|
*/
|
||||||
|
private interface SampleSizeBox {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of samples.
|
||||||
|
*/
|
||||||
|
int getSampleCount();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the size for the next sample.
|
||||||
|
*/
|
||||||
|
int readNextSampleSize();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether samples have a fixed size.
|
||||||
|
*/
|
||||||
|
boolean isFixedSampleSize();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An stsz sample size box.
|
||||||
|
*/
|
||||||
|
/* package */ static final class StszSampleSizeBox implements SampleSizeBox {
|
||||||
|
|
||||||
|
private final int fixedSampleSize;
|
||||||
|
private final int sampleCount;
|
||||||
|
private final ParsableByteArray data;
|
||||||
|
|
||||||
|
public StszSampleSizeBox(Atom.LeafAtom stszAtom) {
|
||||||
|
data = stszAtom.data;
|
||||||
|
data.setPosition(Atom.FULL_HEADER_SIZE);
|
||||||
|
fixedSampleSize = data.readUnsignedIntToInt();
|
||||||
|
sampleCount = data.readUnsignedIntToInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getSampleCount() {
|
||||||
|
return sampleCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int readNextSampleSize() {
|
||||||
|
return fixedSampleSize == 0 ? data.readUnsignedIntToInt() : fixedSampleSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isFixedSampleSize() {
|
||||||
|
return fixedSampleSize != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An stz2 sample size box.
|
||||||
|
*/
|
||||||
|
/* package */ static final class Stz2SampleSizeBox implements SampleSizeBox {
|
||||||
|
|
||||||
|
private final ParsableByteArray data;
|
||||||
|
private final int sampleCount;
|
||||||
|
private final int fieldSize; // Can be 4, 8, or 16.
|
||||||
|
|
||||||
|
// Used only if fieldSize == 4.
|
||||||
|
private int sampleIndex;
|
||||||
|
private int currentByte;
|
||||||
|
|
||||||
|
public Stz2SampleSizeBox(Atom.LeafAtom stz2Atom) {
|
||||||
|
data = stz2Atom.data;
|
||||||
|
data.setPosition(Atom.FULL_HEADER_SIZE);
|
||||||
|
fieldSize = data.readUnsignedIntToInt() & 0x000000FF;
|
||||||
|
sampleCount = data.readUnsignedIntToInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getSampleCount() {
|
||||||
|
return sampleCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int readNextSampleSize() {
|
||||||
|
if (fieldSize == 8) {
|
||||||
|
return data.readUnsignedByte();
|
||||||
|
} else if (fieldSize == 16) {
|
||||||
|
return data.readUnsignedShort();
|
||||||
|
} else {
|
||||||
|
// fieldSize == 4.
|
||||||
|
if ((sampleIndex++ % 2) == 0) {
|
||||||
|
// Read the next byte into our cached byte when we are reading the upper bits.
|
||||||
|
currentByte = data.readUnsignedByte();
|
||||||
|
// Read the upper bits from the byte and shift them to the lower 4 bits.
|
||||||
|
return (currentByte & 0xF0) >> 4;
|
||||||
|
} else {
|
||||||
|
// Mask out the upper 4 bits of the last byte we read.
|
||||||
|
return currentByte & 0x0F;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isFixedSampleSize() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -475,8 +475,9 @@ public final class Mp4Extractor implements Extractor, SeekMap {
|
|||||||
return atom == Atom.TYPE_mdhd || atom == Atom.TYPE_mvhd || atom == Atom.TYPE_hdlr
|
return atom == Atom.TYPE_mdhd || atom == Atom.TYPE_mvhd || atom == Atom.TYPE_hdlr
|
||||||
|| atom == Atom.TYPE_stsd || atom == Atom.TYPE_stts || atom == Atom.TYPE_stss
|
|| atom == Atom.TYPE_stsd || atom == Atom.TYPE_stts || atom == Atom.TYPE_stss
|
||||||
|| atom == Atom.TYPE_ctts || atom == Atom.TYPE_elst || atom == Atom.TYPE_stsc
|
|| atom == Atom.TYPE_ctts || atom == Atom.TYPE_elst || atom == Atom.TYPE_stsc
|
||||||
|| atom == Atom.TYPE_stsz || atom == Atom.TYPE_stco || atom == Atom.TYPE_co64
|
|| atom == Atom.TYPE_stsz || atom == Atom.TYPE_stz2 || atom == Atom.TYPE_stco
|
||||||
|| atom == Atom.TYPE_tkhd || atom == Atom.TYPE_ftyp || atom == Atom.TYPE_udta;
|
|| atom == Atom.TYPE_co64 || atom == Atom.TYPE_tkhd || atom == Atom.TYPE_ftyp
|
||||||
|
|| atom == Atom.TYPE_udta;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
x
Reference in New Issue
Block a user