Commit 3604d589 by olly Committed by Oliver Woodman

Deduplication AVC/HEVC config parsing + other TODOs

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=128979671
parent 8d122a10
...@@ -514,14 +514,10 @@ public final class DefaultTrackOutput implements TrackOutput { ...@@ -514,14 +514,10 @@ public final class DefaultTrackOutput implements TrackOutput {
if ((flags & C.BUFFER_FLAG_KEY_FRAME) == 0 || !infoQueue.attemptSplice(timeUs)) { if ((flags & C.BUFFER_FLAG_KEY_FRAME) == 0 || !infoQueue.attemptSplice(timeUs)) {
return; return;
} }
// TODO - We should be able to actually remove the data from the rolling buffer after a
// splice succeeds, but doing so is a little bit tricky; it requires moving data written
// after the last committed sample.
pendingSplice = false; pendingSplice = false;
} }
if (needKeyframe) { if (needKeyframe) {
if ((flags & C.BUFFER_FLAG_KEY_FRAME) == 0) { if ((flags & C.BUFFER_FLAG_KEY_FRAME) == 0) {
// TODO - As above, although this case is probably less worthwhile.
return; return;
} }
needKeyframe = false; needKeyframe = false;
......
...@@ -19,12 +19,10 @@ import com.google.android.exoplayer2.C; ...@@ -19,12 +19,10 @@ import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.ParserException; import com.google.android.exoplayer2.ParserException;
import com.google.android.exoplayer2.extractor.TrackOutput; import com.google.android.exoplayer2.extractor.TrackOutput;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.NalUnitUtil; import com.google.android.exoplayer2.util.NalUnitUtil;
import com.google.android.exoplayer2.util.ParsableByteArray; import com.google.android.exoplayer2.util.ParsableByteArray;
import java.util.ArrayList; import com.google.android.exoplayer2.video.AvcConfig;
import java.util.List;
/** /**
* Parses video tags from an FLV stream and extracts H.264 nal units. * Parses video tags from an FLV stream and extracts H.264 nal units.
...@@ -87,15 +85,12 @@ import java.util.List; ...@@ -87,15 +85,12 @@ import java.util.List;
if (packetType == AVC_PACKET_TYPE_SEQUENCE_HEADER && !hasOutputFormat) { if (packetType == AVC_PACKET_TYPE_SEQUENCE_HEADER && !hasOutputFormat) {
ParsableByteArray videoSequence = new ParsableByteArray(new byte[data.bytesLeft()]); ParsableByteArray videoSequence = new ParsableByteArray(new byte[data.bytesLeft()]);
data.readBytes(videoSequence.data, 0, data.bytesLeft()); data.readBytes(videoSequence.data, 0, data.bytesLeft());
AvcConfig avcConfig = AvcConfig.parse(videoSequence);
AvcSequenceHeaderData avcData = parseAvcCodecPrivate(videoSequence); nalUnitLengthFieldLength = avcConfig.nalUnitLengthFieldLength;
nalUnitLengthFieldLength = avcData.nalUnitLengthFieldLength;
// Construct and output the format. // Construct and output the format.
Format format = Format.createVideoSampleFormat(null, MimeTypes.VIDEO_H264, null, Format format = Format.createVideoSampleFormat(null, MimeTypes.VIDEO_H264, null,
Format.NO_VALUE, Format.NO_VALUE, avcData.width, avcData.height, Format.NO_VALUE, Format.NO_VALUE, avcConfig.width, avcConfig.height, Format.NO_VALUE,
Format.NO_VALUE, avcData.initializationData, Format.NO_VALUE, avcConfig.initializationData, Format.NO_VALUE, avcConfig.pixelWidthAspectRatio, null);
avcData.pixelWidthAspectRatio, null);
output.format(format); output.format(format);
hasOutputFormat = true; hasOutputFormat = true;
} else if (packetType == AVC_PACKET_TYPE_AVC_NALU) { } else if (packetType == AVC_PACKET_TYPE_AVC_NALU) {
...@@ -132,62 +127,4 @@ import java.util.List; ...@@ -132,62 +127,4 @@ import java.util.List;
} }
} }
/**
* Builds initialization data for a {@link Format} from H.264 (AVC) codec private data.
*
* @return The AvcSequenceHeader data needed to initialize the video codec.
*/
private AvcSequenceHeaderData parseAvcCodecPrivate(ParsableByteArray buffer) {
// TODO: Deduplicate with AtomParsers.parseAvcCFromParent.
buffer.setPosition(4);
int nalUnitLengthFieldLength = (buffer.readUnsignedByte() & 0x03) + 1;
Assertions.checkState(nalUnitLengthFieldLength != 3);
List<byte[]> initializationData = new ArrayList<>();
int numSequenceParameterSets = buffer.readUnsignedByte() & 0x1F;
for (int i = 0; i < numSequenceParameterSets; i++) {
initializationData.add(NalUnitUtil.parseChildNalUnit(buffer));
}
int numPictureParameterSets = buffer.readUnsignedByte();
for (int j = 0; j < numPictureParameterSets; j++) {
initializationData.add(NalUnitUtil.parseChildNalUnit(buffer));
}
float pixelWidthAspectRatio = 1;
int width = Format.NO_VALUE;
int height = Format.NO_VALUE;
if (numSequenceParameterSets > 0) {
byte[] sps = initializationData.get(0);
NalUnitUtil.SpsData spsData =
NalUnitUtil.parseSpsNalUnit(sps, nalUnitLengthFieldLength, sps.length);
width = spsData.width;
height = spsData.height;
pixelWidthAspectRatio = spsData.pixelWidthAspectRatio;
}
return new AvcSequenceHeaderData(initializationData, nalUnitLengthFieldLength,
width, height, pixelWidthAspectRatio);
}
/**
* Holds data parsed from an Sequence Header video tag atom.
*/
private static final class AvcSequenceHeaderData {
public final List<byte[]> initializationData;
public final int nalUnitLengthFieldLength;
public final float pixelWidthAspectRatio;
public final int width;
public final int height;
public AvcSequenceHeaderData(List<byte[]> initializationData, int nalUnitLengthFieldLength,
int width, int height, float pixelWidthAspectRatio) {
this.initializationData = initializationData;
this.nalUnitLengthFieldLength = nalUnitLengthFieldLength;
this.pixelWidthAspectRatio = pixelWidthAspectRatio;
this.width = width;
this.height = height;
}
}
} }
...@@ -15,7 +15,6 @@ ...@@ -15,7 +15,6 @@
*/ */
package com.google.android.exoplayer2.extractor.mkv; package com.google.android.exoplayer2.extractor.mkv;
import android.util.Pair;
import android.util.SparseArray; import android.util.SparseArray;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
...@@ -35,6 +34,8 @@ import com.google.android.exoplayer2.util.MimeTypes; ...@@ -35,6 +34,8 @@ import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.NalUnitUtil; import com.google.android.exoplayer2.util.NalUnitUtil;
import com.google.android.exoplayer2.util.ParsableByteArray; import com.google.android.exoplayer2.util.ParsableByteArray;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
import com.google.android.exoplayer2.video.AvcConfig;
import com.google.android.exoplayer2.video.HevcConfig;
import java.io.IOException; import java.io.IOException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.ByteOrder; import java.nio.ByteOrder;
...@@ -1251,17 +1252,15 @@ public final class MatroskaExtractor implements Extractor { ...@@ -1251,17 +1252,15 @@ public final class MatroskaExtractor implements Extractor {
break; break;
case CODEC_ID_H264: case CODEC_ID_H264:
mimeType = MimeTypes.VIDEO_H264; mimeType = MimeTypes.VIDEO_H264;
Pair<List<byte[]>, Integer> h264Data = parseAvcCodecPrivate( AvcConfig avcConfig = AvcConfig.parse(new ParsableByteArray(codecPrivate));
new ParsableByteArray(codecPrivate)); initializationData = avcConfig.initializationData;
initializationData = h264Data.first; nalUnitLengthFieldLength = avcConfig.nalUnitLengthFieldLength;
nalUnitLengthFieldLength = h264Data.second;
break; break;
case CODEC_ID_H265: case CODEC_ID_H265:
mimeType = MimeTypes.VIDEO_H265; mimeType = MimeTypes.VIDEO_H265;
Pair<List<byte[]>, Integer> hevcData = parseHevcCodecPrivate( HevcConfig hevcConfig = HevcConfig.parse(new ParsableByteArray(codecPrivate));
new ParsableByteArray(codecPrivate)); initializationData = hevcConfig.initializationData;
initializationData = hevcData.first; nalUnitLengthFieldLength = hevcConfig.nalUnitLengthFieldLength;
nalUnitLengthFieldLength = hevcData.second;
break; break;
case CODEC_ID_FOURCC: case CODEC_ID_FOURCC:
mimeType = MimeTypes.VIDEO_VC1; mimeType = MimeTypes.VIDEO_VC1;
...@@ -1415,89 +1414,6 @@ public final class MatroskaExtractor implements Extractor { ...@@ -1415,89 +1414,6 @@ public final class MatroskaExtractor implements Extractor {
} }
/** /**
* Builds initialization data for a {@link Format} from H.264 (AVC) codec private data.
*
* @return The initialization data for the {@link Format}.
* @throws ParserException If the initialization data could not be built.
*/
private static Pair<List<byte[]>, Integer> parseAvcCodecPrivate(ParsableByteArray buffer)
throws ParserException {
try {
// TODO: Deduplicate with AtomParsers.parseAvcCFromParent.
buffer.setPosition(4);
int nalUnitLengthFieldLength = (buffer.readUnsignedByte() & 0x03) + 1;
if (nalUnitLengthFieldLength == 3) {
throw new ParserException();
}
List<byte[]> initializationData = new ArrayList<>();
int numSequenceParameterSets = buffer.readUnsignedByte() & 0x1F;
for (int i = 0; i < numSequenceParameterSets; i++) {
initializationData.add(NalUnitUtil.parseChildNalUnit(buffer));
}
int numPictureParameterSets = buffer.readUnsignedByte();
for (int j = 0; j < numPictureParameterSets; j++) {
initializationData.add(NalUnitUtil.parseChildNalUnit(buffer));
}
return Pair.create(initializationData, nalUnitLengthFieldLength);
} catch (ArrayIndexOutOfBoundsException e) {
throw new ParserException("Error parsing AVC codec private");
}
}
/**
* Builds initialization data for a {@link Format} from H.265 (HEVC) codec private data.
*
* @return The initialization data for the {@link Format}.
* @throws ParserException If the initialization data could not be built.
*/
private static Pair<List<byte[]>, Integer> parseHevcCodecPrivate(ParsableByteArray parent)
throws ParserException {
try {
// TODO: Deduplicate with AtomParsers.parseHvcCFromParent.
parent.setPosition(21);
int lengthSizeMinusOne = parent.readUnsignedByte() & 0x03;
// Calculate the combined size of all VPS/SPS/PPS bitstreams.
int numberOfArrays = parent.readUnsignedByte();
int csdLength = 0;
int csdStartPosition = parent.getPosition();
for (int i = 0; i < numberOfArrays; i++) {
parent.skipBytes(1); // completeness (1), nal_unit_type (7)
int numberOfNalUnits = parent.readUnsignedShort();
for (int j = 0; j < numberOfNalUnits; j++) {
int nalUnitLength = parent.readUnsignedShort();
csdLength += 4 + nalUnitLength; // Start code and NAL unit.
parent.skipBytes(nalUnitLength);
}
}
// Concatenate the codec-specific data into a single buffer.
parent.setPosition(csdStartPosition);
byte[] buffer = new byte[csdLength];
int bufferPosition = 0;
for (int i = 0; i < numberOfArrays; i++) {
parent.skipBytes(1); // completeness (1), nal_unit_type (7)
int numberOfNalUnits = parent.readUnsignedShort();
for (int j = 0; j < numberOfNalUnits; j++) {
int nalUnitLength = parent.readUnsignedShort();
System.arraycopy(NalUnitUtil.NAL_START_CODE, 0, buffer, bufferPosition,
NalUnitUtil.NAL_START_CODE.length);
bufferPosition += NalUnitUtil.NAL_START_CODE.length;
System.arraycopy(parent.data, parent.getPosition(), buffer, bufferPosition,
nalUnitLength);
bufferPosition += nalUnitLength;
parent.skipBytes(nalUnitLength);
}
}
List<byte[]> initializationData = csdLength == 0 ? null : Collections.singletonList(buffer);
return Pair.create(initializationData, lengthSizeMinusOne + 1);
} catch (ArrayIndexOutOfBoundsException e) {
throw new ParserException("Error parsing HEVC codec private");
}
}
/**
* Builds initialization data for a {@link Format} from Vorbis codec private data. * Builds initialization data for a {@link Format} from Vorbis codec private data.
* *
* @return The initialization data for the {@link Format}. * @return The initialization data for the {@link Format}.
......
...@@ -25,10 +25,10 @@ import com.google.android.exoplayer2.extractor.GaplessInfoHolder; ...@@ -25,10 +25,10 @@ import com.google.android.exoplayer2.extractor.GaplessInfoHolder;
import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.CodecSpecificDataUtil; import com.google.android.exoplayer2.util.CodecSpecificDataUtil;
import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.NalUnitUtil;
import com.google.android.exoplayer2.util.ParsableByteArray; import com.google.android.exoplayer2.util.ParsableByteArray;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
import java.util.ArrayList; import com.google.android.exoplayer2.video.AvcConfig;
import com.google.android.exoplayer2.video.HevcConfig;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
...@@ -56,7 +56,7 @@ import java.util.List; ...@@ -56,7 +56,7 @@ import java.util.List;
* @return A {@link Track} instance, or {@code null} if the track's type isn't supported. * @return A {@link Track} instance, or {@code null} if the track's type isn't supported.
*/ */
public static Track parseTrak(Atom.ContainerAtom trak, Atom.LeafAtom mvhd, long duration, public static Track parseTrak(Atom.ContainerAtom trak, Atom.LeafAtom mvhd, long duration,
DrmInitData drmInitData, boolean isQuickTime) { DrmInitData drmInitData, boolean isQuickTime) throws ParserException {
Atom.ContainerAtom mdia = trak.getContainerAtomOfType(Atom.TYPE_mdia); Atom.ContainerAtom mdia = trak.getContainerAtomOfType(Atom.TYPE_mdia);
int trackType = parseHdlr(mdia.getLeafAtomOfType(Atom.TYPE_hdlr).data); int trackType = parseHdlr(mdia.getLeafAtomOfType(Atom.TYPE_hdlr).data);
if (trackType == C.TRACK_TYPE_UNKNOWN) { if (trackType == C.TRACK_TYPE_UNKNOWN) {
...@@ -589,7 +589,7 @@ import java.util.List; ...@@ -589,7 +589,7 @@ import java.util.List;
* @return An object containing the parsed data. * @return An object containing the parsed data.
*/ */
private static StsdData parseStsd(ParsableByteArray stsd, int trackId, int rotationDegrees, private static StsdData parseStsd(ParsableByteArray stsd, int trackId, int rotationDegrees,
String language, DrmInitData drmInitData, boolean isQuickTime) { String language, DrmInitData drmInitData, boolean isQuickTime) throws ParserException {
stsd.setPosition(Atom.FULL_HEADER_SIZE); stsd.setPosition(Atom.FULL_HEADER_SIZE);
int numberOfEntries = stsd.readInt(); int numberOfEntries = stsd.readInt();
StsdData out = new StsdData(numberOfEntries); StsdData out = new StsdData(numberOfEntries);
...@@ -638,7 +638,7 @@ import java.util.List; ...@@ -638,7 +638,7 @@ import java.util.List;
private static void parseVideoSampleEntry(ParsableByteArray parent, int atomType, int position, private static void parseVideoSampleEntry(ParsableByteArray parent, int atomType, int position,
int size, int trackId, int rotationDegrees, DrmInitData drmInitData, StsdData out, int size, int trackId, int rotationDegrees, DrmInitData drmInitData, StsdData out,
int entryIndex) { int entryIndex) throws ParserException {
parent.setPosition(position + Atom.HEADER_SIZE); parent.setPosition(position + Atom.HEADER_SIZE);
parent.skipBytes(24); parent.skipBytes(24);
...@@ -669,18 +669,20 @@ import java.util.List; ...@@ -669,18 +669,20 @@ import java.util.List;
if (childAtomType == Atom.TYPE_avcC) { if (childAtomType == Atom.TYPE_avcC) {
Assertions.checkState(mimeType == null); Assertions.checkState(mimeType == null);
mimeType = MimeTypes.VIDEO_H264; mimeType = MimeTypes.VIDEO_H264;
AvcCData avcCData = parseAvcCFromParent(parent, childStartPosition); parent.setPosition(childStartPosition + Atom.HEADER_SIZE);
initializationData = avcCData.initializationData; AvcConfig avcConfig = AvcConfig.parse(parent);
out.nalUnitLengthFieldLength = avcCData.nalUnitLengthFieldLength; initializationData = avcConfig.initializationData;
out.nalUnitLengthFieldLength = avcConfig.nalUnitLengthFieldLength;
if (!pixelWidthHeightRatioFromPasp) { if (!pixelWidthHeightRatioFromPasp) {
pixelWidthHeightRatio = avcCData.pixelWidthAspectRatio; pixelWidthHeightRatio = avcConfig.pixelWidthAspectRatio;
} }
} else if (childAtomType == Atom.TYPE_hvcC) { } else if (childAtomType == Atom.TYPE_hvcC) {
Assertions.checkState(mimeType == null); Assertions.checkState(mimeType == null);
mimeType = MimeTypes.VIDEO_H265; mimeType = MimeTypes.VIDEO_H265;
Pair<List<byte[]>, Integer> hvcCData = parseHvcCFromParent(parent, childStartPosition); parent.setPosition(childStartPosition + Atom.HEADER_SIZE);
initializationData = hvcCData.first; HevcConfig hevcConfig = HevcConfig.parse(parent);
out.nalUnitLengthFieldLength = hvcCData.second; initializationData = hevcConfig.initializationData;
out.nalUnitLengthFieldLength = hevcConfig.nalUnitLengthFieldLength;
} else if (childAtomType == Atom.TYPE_vpcC) { } else if (childAtomType == Atom.TYPE_vpcC) {
Assertions.checkState(mimeType == null); Assertions.checkState(mimeType == null);
mimeType = (atomType == Atom.TYPE_vp08) ? MimeTypes.VIDEO_VP8 : MimeTypes.VIDEO_VP9; mimeType = (atomType == Atom.TYPE_vp08) ? MimeTypes.VIDEO_VP8 : MimeTypes.VIDEO_VP9;
...@@ -710,76 +712,6 @@ import java.util.List; ...@@ -710,76 +712,6 @@ import java.util.List;
rotationDegrees, pixelWidthHeightRatio, drmInitData); rotationDegrees, pixelWidthHeightRatio, drmInitData);
} }
private static AvcCData parseAvcCFromParent(ParsableByteArray parent, int position) {
parent.setPosition(position + Atom.HEADER_SIZE + 4);
// Start of the AVCDecoderConfigurationRecord (defined in 14496-15)
int nalUnitLengthFieldLength = (parent.readUnsignedByte() & 0x3) + 1;
if (nalUnitLengthFieldLength == 3) {
throw new IllegalStateException();
}
List<byte[]> initializationData = new ArrayList<>();
float pixelWidthAspectRatio = 1;
int numSequenceParameterSets = parent.readUnsignedByte() & 0x1F;
for (int j = 0; j < numSequenceParameterSets; j++) {
initializationData.add(NalUnitUtil.parseChildNalUnit(parent));
}
int numPictureParameterSets = parent.readUnsignedByte();
for (int j = 0; j < numPictureParameterSets; j++) {
initializationData.add(NalUnitUtil.parseChildNalUnit(parent));
}
if (numSequenceParameterSets > 0) {
// Parse the first sequence parameter set to obtain pixelWidthAspectRatio.
byte[] sps = initializationData.get(0);
pixelWidthAspectRatio = NalUnitUtil
.parseSpsNalUnit(sps, nalUnitLengthFieldLength, sps.length).pixelWidthAspectRatio;
}
return new AvcCData(initializationData, nalUnitLengthFieldLength, pixelWidthAspectRatio);
}
private static Pair<List<byte[]>, Integer> parseHvcCFromParent(ParsableByteArray parent,
int position) {
// Skip to the NAL unit length size field.
parent.setPosition(position + Atom.HEADER_SIZE + 21);
int lengthSizeMinusOne = parent.readUnsignedByte() & 0x03;
// Calculate the combined size of all VPS/SPS/PPS bitstreams.
int numberOfArrays = parent.readUnsignedByte();
int csdLength = 0;
int csdStartPosition = parent.getPosition();
for (int i = 0; i < numberOfArrays; i++) {
parent.skipBytes(1); // completeness (1), nal_unit_type (7)
int numberOfNalUnits = parent.readUnsignedShort();
for (int j = 0; j < numberOfNalUnits; j++) {
int nalUnitLength = parent.readUnsignedShort();
csdLength += 4 + nalUnitLength; // Start code and NAL unit.
parent.skipBytes(nalUnitLength);
}
}
// Concatenate the codec-specific data into a single buffer.
parent.setPosition(csdStartPosition);
byte[] buffer = new byte[csdLength];
int bufferPosition = 0;
for (int i = 0; i < numberOfArrays; i++) {
parent.skipBytes(1); // completeness (1), nal_unit_type (7)
int numberOfNalUnits = parent.readUnsignedShort();
for (int j = 0; j < numberOfNalUnits; j++) {
int nalUnitLength = parent.readUnsignedShort();
System.arraycopy(NalUnitUtil.NAL_START_CODE, 0, buffer, bufferPosition,
NalUnitUtil.NAL_START_CODE.length);
bufferPosition += NalUnitUtil.NAL_START_CODE.length;
System.arraycopy(parent.data, parent.getPosition(), buffer, bufferPosition, nalUnitLength);
bufferPosition += nalUnitLength;
parent.skipBytes(nalUnitLength);
}
}
List<byte[]> initializationData = csdLength == 0 ? null : Collections.singletonList(buffer);
return Pair.create(initializationData, lengthSizeMinusOne + 1);
}
/** /**
* Parses the edts atom (defined in 14496-12 subsection 8.6.5). * Parses the edts atom (defined in 14496-12 subsection 8.6.5).
* *
...@@ -1192,22 +1124,4 @@ import java.util.List; ...@@ -1192,22 +1124,4 @@ import java.util.List;
} }
/**
* Holds data parsed from an AvcC atom.
*/
private static final class AvcCData {
public final List<byte[]> initializationData;
public final int nalUnitLengthFieldLength;
public final float pixelWidthAspectRatio;
public AvcCData(List<byte[]> initializationData, int nalUnitLengthFieldLength,
float pixelWidthAspectRatio) {
this.initializationData = initializationData;
this.nalUnitLengthFieldLength = nalUnitLengthFieldLength;
this.pixelWidthAspectRatio = pixelWidthAspectRatio;
}
}
} }
...@@ -330,7 +330,7 @@ public final class FragmentedMp4Extractor implements Extractor { ...@@ -330,7 +330,7 @@ public final class FragmentedMp4Extractor implements Extractor {
} }
} }
private void onMoovContainerAtomRead(ContainerAtom moov) { private void onMoovContainerAtomRead(ContainerAtom moov) throws ParserException {
Assertions.checkState(sideloadedTrack == null, "Unexpected moov box."); Assertions.checkState(sideloadedTrack == null, "Unexpected moov box.");
List<Atom.LeafAtom> moovLeafChildren = moov.leafChildren; List<Atom.LeafAtom> moovLeafChildren = moov.leafChildren;
int moovLeafChildrenSize = moovLeafChildren.size(); int moovLeafChildrenSize = moovLeafChildren.size();
......
...@@ -389,11 +389,9 @@ import java.util.Arrays; ...@@ -389,11 +389,9 @@ import java.util.Arrays;
if (dimensions != 0) { if (dimensions != 0) {
lookupValuesCount = mapType1QuantValues(entries, dimensions); lookupValuesCount = mapType1QuantValues(entries, dimensions);
} else { } else {
// TODO no sample file found yet
lookupValuesCount = 0; lookupValuesCount = 0;
} }
} else { } else {
// TODO no sample file found yet
lookupValuesCount = entries * dimensions; lookupValuesCount = entries * dimensions;
} }
// discard (no decoding required yet) // discard (no decoding required yet)
......
/*
* 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.video;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.ParserException;
import com.google.android.exoplayer2.util.NalUnitUtil;
import com.google.android.exoplayer2.util.NalUnitUtil.SpsData;
import com.google.android.exoplayer2.util.ParsableByteArray;
import java.util.ArrayList;
import java.util.List;
/**
* AVC configuration data.
*/
public final class AvcConfig {
public final List<byte[]> initializationData;
public final int nalUnitLengthFieldLength;
public final int width;
public final int height;
public final float pixelWidthAspectRatio;
/**
* Parses AVC configuration data.
*
* @param data A {@link ParsableByteArray}, whose position is set to the start of the AVC
* configuration data to parse.
* @return A parsed representation of the HEVC configuration data.
* @throws ParserException If an error occurred parsing the data.
*/
public static AvcConfig parse(ParsableByteArray data) throws ParserException {
try {
data.skipBytes(4); // Skip to the AVCDecoderConfigurationRecord (defined in 14496-15)
int nalUnitLengthFieldLength = (data.readUnsignedByte() & 0x3) + 1;
if (nalUnitLengthFieldLength == 3) {
throw new IllegalStateException();
}
List<byte[]> initializationData = new ArrayList<>();
int numSequenceParameterSets = data.readUnsignedByte() & 0x1F;
for (int j = 0; j < numSequenceParameterSets; j++) {
initializationData.add(NalUnitUtil.parseChildNalUnit(data));
}
int numPictureParameterSets = data.readUnsignedByte();
for (int j = 0; j < numPictureParameterSets; j++) {
initializationData.add(NalUnitUtil.parseChildNalUnit(data));
}
int width = Format.NO_VALUE;
int height = Format.NO_VALUE;
float pixelWidthAspectRatio = 1;
if (numSequenceParameterSets > 0) {
byte[] sps = initializationData.get(0);
SpsData spsData = NalUnitUtil.parseSpsNalUnit(initializationData.get(0),
nalUnitLengthFieldLength, sps.length);
width = spsData.width;
height = spsData.height;
pixelWidthAspectRatio = spsData.pixelWidthAspectRatio;
}
return new AvcConfig(initializationData, nalUnitLengthFieldLength, width, height,
pixelWidthAspectRatio);
} catch (ArrayIndexOutOfBoundsException e) {
throw new ParserException("Error parsing AVC config", e);
}
}
private AvcConfig(List<byte[]> initializationData, int nalUnitLengthFieldLength,
int width, int height, float pixelWidthAspectRatio) {
this.initializationData = initializationData;
this.nalUnitLengthFieldLength = nalUnitLengthFieldLength;
this.width = width;
this.height = height;
this.pixelWidthAspectRatio = pixelWidthAspectRatio;
}
}
/*
* 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.video;
import com.google.android.exoplayer2.ParserException;
import com.google.android.exoplayer2.util.NalUnitUtil;
import com.google.android.exoplayer2.util.ParsableByteArray;
import java.util.Collections;
import java.util.List;
/**
* HEVC configuration data.
*/
public final class HevcConfig {
public final List<byte[]> initializationData;
public final int nalUnitLengthFieldLength;
/**
* Parses HEVC configuration data.
*
* @param data A {@link ParsableByteArray}, whose position is set to the start of the HEVC
* configuration data to parse.
* @return A parsed representation of the HEVC configuration data.
* @throws ParserException If an error occurred parsing the data.
*/
public static HevcConfig parse(ParsableByteArray data) throws ParserException {
try {
data.skipBytes(21); // Skip to the NAL unit length size field.
int lengthSizeMinusOne = data.readUnsignedByte() & 0x03;
// Calculate the combined size of all VPS/SPS/PPS bitstreams.
int numberOfArrays = data.readUnsignedByte();
int csdLength = 0;
int csdStartPosition = data.getPosition();
for (int i = 0; i < numberOfArrays; i++) {
data.skipBytes(1); // completeness (1), nal_unit_type (7)
int numberOfNalUnits = data.readUnsignedShort();
for (int j = 0; j < numberOfNalUnits; j++) {
int nalUnitLength = data.readUnsignedShort();
csdLength += 4 + nalUnitLength; // Start code and NAL unit.
data.skipBytes(nalUnitLength);
}
}
// Concatenate the codec-specific data into a single buffer.
data.setPosition(csdStartPosition);
byte[] buffer = new byte[csdLength];
int bufferPosition = 0;
for (int i = 0; i < numberOfArrays; i++) {
data.skipBytes(1); // completeness (1), nal_unit_type (7)
int numberOfNalUnits = data.readUnsignedShort();
for (int j = 0; j < numberOfNalUnits; j++) {
int nalUnitLength = data.readUnsignedShort();
System.arraycopy(NalUnitUtil.NAL_START_CODE, 0, buffer, bufferPosition,
NalUnitUtil.NAL_START_CODE.length);
bufferPosition += NalUnitUtil.NAL_START_CODE.length;
System
.arraycopy(data.data, data.getPosition(), buffer, bufferPosition, nalUnitLength);
bufferPosition += nalUnitLength;
data.skipBytes(nalUnitLength);
}
}
List<byte[]> initializationData = csdLength == 0 ? null : Collections.singletonList(buffer);
return new HevcConfig(initializationData, lengthSizeMinusOne + 1);
} catch (ArrayIndexOutOfBoundsException e) {
throw new ParserException("Error parsing HEVC config", e);
}
}
private HevcConfig(List<byte[]> initializationData, int nalUnitLengthFieldLength) {
this.initializationData = initializationData;
this.nalUnitLengthFieldLength = nalUnitLengthFieldLength;
}
}
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