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_stsc = Util.getIntegerCodeForString("stsc");
|
||||
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_co64 = Util.getIntegerCodeForString("co64");
|
||||
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,
|
||||
GaplessInfoHolder gaplessInfoHolder) throws ParserException {
|
||||
// Array of sample sizes.
|
||||
ParsableByteArray stsz = stblAtom.getLeafAtomOfType(Atom.TYPE_stsz).data;
|
||||
SampleSizeBox sampleSizeBox;
|
||||
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.
|
||||
boolean chunkOffsetsAreLongs = false;
|
||||
@ -120,14 +134,6 @@ import java.util.List;
|
||||
Atom.LeafAtom cttsAtom = stblAtom.getLeafAtomOfType(Atom.TYPE_ctts);
|
||||
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.
|
||||
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.
|
||||
boolean isRechunkable = fixedSampleSize != 0
|
||||
boolean isRechunkable = sampleSizeBox.isFixedSampleSize()
|
||||
&& MimeTypes.AUDIO_RAW.equals(track.format.sampleMimeType)
|
||||
&& remainingTimestampDeltaChanges == 0 && remainingTimestampOffsetChanges == 0
|
||||
&& remainingSynchronizationSamples == 0;
|
||||
@ -204,7 +210,7 @@ import java.util.List;
|
||||
}
|
||||
|
||||
offsets[i] = offset;
|
||||
sizes[i] = fixedSampleSize == 0 ? stsz.readUnsignedIntToInt() : fixedSampleSize;
|
||||
sizes[i] = sampleSizeBox.readNextSampleSize();
|
||||
if (sizes[i] > maximumSize) {
|
||||
maximumSize = sizes[i];
|
||||
}
|
||||
@ -253,6 +259,7 @@ import java.util.List;
|
||||
chunkOffsetsBytes[chunkIterator.index] = chunkIterator.offset;
|
||||
chunkSampleCounts[chunkIterator.index] = chunkIterator.numSamples;
|
||||
}
|
||||
int fixedSampleSize = sampleSizeBox.readNextSampleSize();
|
||||
FixedSampleSizeRechunker.Results rechunkedResults = FixedSampleSizeRechunker.rechunk(
|
||||
fixedSampleSize, chunkOffsetsBytes, chunkSampleCounts, timestampDeltaInTimeUnits);
|
||||
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) {
|
||||
int childAtomPosition = parent.getPosition();
|
||||
while (childAtomPosition - position < size) {
|
||||
@ -883,7 +892,9 @@ import java.util.List;
|
||||
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) {
|
||||
parent.setPosition(position + Atom.HEADER_SIZE + 4);
|
||||
// Start of the ES_Descriptor (defined in 14496-1)
|
||||
@ -1028,7 +1039,9 @@ import java.util.List;
|
||||
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) {
|
||||
int currentByte = data.readUnsignedByte();
|
||||
int size = currentByte & 0x7F;
|
||||
@ -1122,6 +1135,114 @@ import java.util.List;
|
||||
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
|
||||
|| 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_stsz || atom == Atom.TYPE_stco || atom == Atom.TYPE_co64
|
||||
|| atom == Atom.TYPE_tkhd || atom == Atom.TYPE_ftyp || atom == Atom.TYPE_udta;
|
||||
|| atom == Atom.TYPE_stsz || atom == Atom.TYPE_stz2 || atom == Atom.TYPE_stco
|
||||
|| 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