Commit 4a745b1c by Oliver Woodman

Optimize out quite a few allocations in FragmentedMp4Parser.

parent 005e98fc
...@@ -52,7 +52,7 @@ public abstract class Chunk implements Loadable { ...@@ -52,7 +52,7 @@ public abstract class Chunk implements Loadable {
/** /**
* @param dataSource The source from which the data should be loaded. * @param dataSource The source from which the data should be loaded.
* @param dataSpec Defines the data to be loaded. {@code dataSpec.length} must not exceed * @param dataSpec Defines the data to be loaded. {@code dataSpec.length} must not exceed
* {@link Integer#MAX_VALUE}. If {@code dataSpec.length == DataSpec.LENGTH_UNBOUNDED} then * {@link Integer#MAX_VALUE}. If {@code dataSpec.length == C.LENGTH_UNBOUNDED} then
* the length resolved by {@code dataSource.open(dataSpec)} must not exceed * the length resolved by {@code dataSource.open(dataSpec)} must not exceed
* {@link Integer#MAX_VALUE}. * {@link Integer#MAX_VALUE}.
* @param format See {@link #format}. * @param format See {@link #format}.
......
...@@ -16,7 +16,6 @@ ...@@ -16,7 +16,6 @@
package com.google.android.exoplayer.parser.mp4; package com.google.android.exoplayer.parser.mp4;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List;
/* package */ abstract class Atom { /* package */ abstract class Atom {
...@@ -24,7 +23,6 @@ import java.util.List; ...@@ -24,7 +23,6 @@ import java.util.List;
public static final int TYPE_avc3 = 0x61766333; public static final int TYPE_avc3 = 0x61766333;
public static final int TYPE_esds = 0x65736473; public static final int TYPE_esds = 0x65736473;
public static final int TYPE_mdat = 0x6D646174; public static final int TYPE_mdat = 0x6D646174;
public static final int TYPE_mfhd = 0x6D666864;
public static final int TYPE_mp4a = 0x6D703461; public static final int TYPE_mp4a = 0x6D703461;
public static final int TYPE_tfdt = 0x74666474; public static final int TYPE_tfdt = 0x74666474;
public static final int TYPE_tfhd = 0x74666864; public static final int TYPE_tfhd = 0x74666864;
...@@ -64,17 +62,13 @@ import java.util.List; ...@@ -64,17 +62,13 @@ import java.util.List;
public final static class LeafAtom extends Atom { public final static class LeafAtom extends Atom {
private final ParsableByteArray data; public final ParsableByteArray data;
public LeafAtom(int type, ParsableByteArray data) { public LeafAtom(int type, ParsableByteArray data) {
super(type); super(type);
this.data = data; this.data = data;
} }
public ParsableByteArray getData() {
return data;
}
} }
public final static class ContainerAtom extends Atom { public final static class ContainerAtom extends Atom {
...@@ -91,7 +85,8 @@ import java.util.List; ...@@ -91,7 +85,8 @@ import java.util.List;
} }
public LeafAtom getLeafAtomOfType(int type) { public LeafAtom getLeafAtomOfType(int type) {
for (int i = 0; i < children.size(); i++) { int childrenSize = children.size();
for (int i = 0; i < childrenSize; i++) {
Atom atom = children.get(i); Atom atom = children.get(i);
if (atom.type == type) { if (atom.type == type) {
return (LeafAtom) atom; return (LeafAtom) atom;
...@@ -101,7 +96,8 @@ import java.util.List; ...@@ -101,7 +96,8 @@ import java.util.List;
} }
public ContainerAtom getContainerAtomOfType(int type) { public ContainerAtom getContainerAtomOfType(int type) {
for (int i = 0; i < children.size(); i++) { int childrenSize = children.size();
for (int i = 0; i < childrenSize; i++) {
Atom atom = children.get(i); Atom atom = children.get(i);
if (atom.type == type) { if (atom.type == type) {
return (ContainerAtom) atom; return (ContainerAtom) atom;
...@@ -110,10 +106,6 @@ import java.util.List; ...@@ -110,10 +106,6 @@ import java.util.List;
return null; return null;
} }
public List<Atom> getChildren() {
return children;
}
} }
} }
...@@ -27,7 +27,7 @@ import java.util.List; ...@@ -27,7 +27,7 @@ import java.util.List;
/** /**
* Provides static utility methods for manipulating various types of codec specific data. * Provides static utility methods for manipulating various types of codec specific data.
*/ */
public class CodecSpecificDataUtil { public final class CodecSpecificDataUtil {
private static final byte[] NAL_START_CODE = new byte[] {0, 0, 0, 1}; private static final byte[] NAL_START_CODE = new byte[] {0, 0, 0, 1};
......
...@@ -23,17 +23,14 @@ import java.nio.ByteBuffer; ...@@ -23,17 +23,14 @@ import java.nio.ByteBuffer;
*/ */
/* package */ final class ParsableByteArray { /* package */ final class ParsableByteArray {
private final byte[] data; public byte[] data;
private int position; private int position;
public ParsableByteArray(int length) { public ParsableByteArray(int length) {
this.data = new byte[length]; this.data = new byte[length];
} }
public byte[] getData() {
return data;
}
public int length() { public int length() {
return data.length; return data.length;
} }
......
...@@ -18,7 +18,7 @@ package com.google.android.exoplayer.parser.mp4; ...@@ -18,7 +18,7 @@ package com.google.android.exoplayer.parser.mp4;
/** /**
* Encapsulates information parsed from a track encryption (tenc) box in an MP4 stream. * Encapsulates information parsed from a track encryption (tenc) box in an MP4 stream.
*/ */
public class TrackEncryptionBox { public final class TrackEncryptionBox {
/** /**
* Indicates the encryption state of the samples in the sample group. * Indicates the encryption state of the samples in the sample group.
......
...@@ -15,41 +15,136 @@ ...@@ -15,41 +15,136 @@
*/ */
package com.google.android.exoplayer.parser.mp4; package com.google.android.exoplayer.parser.mp4;
import com.google.android.exoplayer.upstream.NonBlockingInputStream;
/** /**
* A holder for information corresponding to a single fragment of an mp4 file. * A holder for information corresponding to a single fragment of an mp4 file.
*/ */
/* package */ class TrackFragment { /* package */ final class TrackFragment {
public int sampleDescriptionIndex; public int sampleDescriptionIndex;
/**
* The number of samples contained by the fragment.
*/
public int length; public int length;
/**
* The size of each sample in the run.
*/
public int[] sampleSizeTable; public int[] sampleSizeTable;
/**
* The decoding time of each sample in the run.
*/
public int[] sampleDecodingTimeTable; public int[] sampleDecodingTimeTable;
/**
* The composition time offset of each sample in the run.
*/
public int[] sampleCompositionTimeOffsetTable; public int[] sampleCompositionTimeOffsetTable;
/**
* Indicates which samples are sync frames.
*/
public boolean[] sampleIsSyncFrameTable; public boolean[] sampleIsSyncFrameTable;
/**
* True if the fragment defines encryption data. False otherwise.
*/
public boolean definesEncryptionData;
/**
* If {@link #definesEncryptionData} is true, indicates which samples use sub-sample encryption.
* Undefined otherwise.
*/
public boolean[] sampleHasSubsampleEncryptionTable; public boolean[] sampleHasSubsampleEncryptionTable;
/**
* If {@link #definesEncryptionData} is true, indicates the length of the sample encryption data.
* Undefined otherwise.
*/
public int sampleEncryptionDataLength;
/**
* If {@link #definesEncryptionData} is true, contains binary sample encryption data. Undefined
* otherwise.
*/
public ParsableByteArray sampleEncryptionData; public ParsableByteArray sampleEncryptionData;
/**
* Whether {@link #sampleEncryptionData} needs populating with the actual encryption data.
*/
public boolean sampleEncryptionDataNeedsFill; public boolean sampleEncryptionDataNeedsFill;
public void setSampleDescriptionIndex(int sampleDescriptionIndex) { /**
this.sampleDescriptionIndex = sampleDescriptionIndex; * Resets the fragment.
* <p>
* The {@link #length} is set to 0, and both {@link #definesEncryptionData} and
* {@link #sampleEncryptionDataNeedsFill} is set to false.
*/
public void reset() {
length = 0;
definesEncryptionData = false;
sampleEncryptionDataNeedsFill = false;
}
/**
* Configures the fragment for the specified number of samples.
* <p>
* The {@link #length} of the fragment is set to the specified sample count, and the contained
* tables are resized if necessary such that they are at least this length.
*
* @param sampleCount The number of samples in the new run.
*/
public void initTables(int sampleCount) {
length = sampleCount;
if (sampleSizeTable == null || sampleSizeTable.length < length) {
// Size the tables 25% larger than needed, so as to make future resize operations less
// likely. The choice of 25% is relatively arbitrary.
int tableSize = (sampleCount * 125) / 100;
sampleSizeTable = new int[tableSize];
sampleDecodingTimeTable = new int[tableSize];
sampleCompositionTimeOffsetTable = new int[tableSize];
sampleIsSyncFrameTable = new boolean[tableSize];
sampleHasSubsampleEncryptionTable = new boolean[tableSize];
}
}
/**
* Configures the fragment to be one that defines encryption data of the specified length.
* <p>
* {@link #definesEncryptionData} is set to true, {@link #sampleEncryptionDataLength} is set to
* the specified length, and {@link #sampleEncryptionData} is resized if necessary such that it
* is at least this length.
*
* @param length The length in bytes of the encryption data.
*/
public void initEncryptionData(int length) {
if (sampleEncryptionData == null || sampleEncryptionData.length() < length) {
sampleEncryptionData = new ParsableByteArray(length);
}
sampleEncryptionDataLength = length;
definesEncryptionData = true;
sampleEncryptionDataNeedsFill = true;
} }
public void setSampleTables(int[] sampleSizeTable, int[] sampleDecodingTimeTable, /**
int[] sampleCompositionTimeOffsetTable, boolean[] sampleIsSyncFrameTable) { * Fills {@link #sampleEncryptionData} from the provided source.
this.sampleSizeTable = sampleSizeTable; *
this.sampleDecodingTimeTable = sampleDecodingTimeTable; * @param source A source from which to read the encryption data.
this.sampleCompositionTimeOffsetTable = sampleCompositionTimeOffsetTable; */
this.sampleIsSyncFrameTable = sampleIsSyncFrameTable; public void fillEncryptionData(ParsableByteArray source) {
this.length = sampleSizeTable.length; source.readBytes(sampleEncryptionData.data, 0, sampleEncryptionDataLength);
sampleEncryptionData.setPosition(0);
sampleEncryptionDataNeedsFill = false;
} }
public void setSampleEncryptionData(boolean[] sampleHasSubsampleEncryptionTable, /**
ParsableByteArray sampleEncryptionData, boolean sampleEncryptionDataNeedsFill) { * Fills {@link #sampleEncryptionData} for the current run from the provided source.
this.sampleHasSubsampleEncryptionTable = sampleHasSubsampleEncryptionTable; *
this.sampleEncryptionData = sampleEncryptionData; * @param source A source from which to read the encryption data.
this.sampleEncryptionDataNeedsFill = sampleEncryptionDataNeedsFill; * @return True if the encryption data was filled. False if the source had insufficient data.
*/
public boolean fillEncryptionData(NonBlockingInputStream source) {
if (source.getAvailableByteCount() < sampleEncryptionDataLength) {
return false;
}
source.read(sampleEncryptionData.data, 0, sampleEncryptionDataLength);
sampleEncryptionData.setPosition(0);
sampleEncryptionDataNeedsFill = false;
return true;
} }
public int getSamplePresentationTime(int index) { public int getSamplePresentationTime(int index) {
......
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