De-duplicate code for H264/H265 in MPEG-TS.

This commit is contained in:
Oliver Woodman 2015-06-05 18:27:20 +01:00
parent 10efe7904d
commit 96c1e670c4
4 changed files with 145 additions and 235 deletions

View File

@ -18,7 +18,6 @@ package com.google.android.exoplayer.extractor.ts;
import com.google.android.exoplayer.C; import com.google.android.exoplayer.C;
import com.google.android.exoplayer.MediaFormat; import com.google.android.exoplayer.MediaFormat;
import com.google.android.exoplayer.extractor.TrackOutput; import com.google.android.exoplayer.extractor.TrackOutput;
import com.google.android.exoplayer.util.Assertions;
import com.google.android.exoplayer.util.MimeTypes; import com.google.android.exoplayer.util.MimeTypes;
import com.google.android.exoplayer.util.NalUnitUtil; import com.google.android.exoplayer.util.NalUnitUtil;
import com.google.android.exoplayer.util.ParsableBitArray; import com.google.android.exoplayer.util.ParsableBitArray;
@ -46,26 +45,6 @@ import java.util.List;
private static final int NAL_UNIT_TYPE_SPS = 7; // Sequence parameter set private static final int NAL_UNIT_TYPE_SPS = 7; // Sequence parameter set
private static final int NAL_UNIT_TYPE_PPS = 8; // Picture parameter set private static final int NAL_UNIT_TYPE_PPS = 8; // Picture parameter set
private static final int NAL_UNIT_TYPE_AUD = 9; // Access unit delimiter private static final int NAL_UNIT_TYPE_AUD = 9; // Access unit delimiter
private static final int EXTENDED_SAR = 0xFF;
private static final float[] ASPECT_RATIO_IDC_VALUES = new float[] {
1f /* Unspecified. Assume square */,
1f,
12f / 11f,
10f / 11f,
16f / 11f,
40f / 33f,
24f / 11f,
20f / 11f,
32f / 11f,
80f / 33f,
18f / 11f,
15f / 11f,
64f / 33f,
160f / 99f,
4f / 3f,
3f / 2f,
2f
};
// State that should not be reset on seek. // State that should not be reset on seek.
private boolean hasOutputFormat; private boolean hasOutputFormat;
@ -172,7 +151,7 @@ import java.util.List;
// Notify the start of the next NAL unit. // Notify the start of the next NAL unit.
feedNalUnitTargetBuffersStart(nalUnitType); feedNalUnitTargetBuffersStart(nalUnitType);
// Continue scanning the data. // Continue scanning the data.
offset = nextNalUnitOffset + 4; offset = nextNalUnitOffset + 3;
} else { } else {
feedNalUnitTargetBuffersData(dataArray, offset, limit); feedNalUnitTargetBuffersData(dataArray, offset, limit);
offset = limit; offset = limit;
@ -312,14 +291,14 @@ import java.util.List;
boolean aspectRatioInfoPresentFlag = bitArray.readBit(); boolean aspectRatioInfoPresentFlag = bitArray.readBit();
if (aspectRatioInfoPresentFlag) { if (aspectRatioInfoPresentFlag) {
int aspectRatioIdc = bitArray.readBits(8); int aspectRatioIdc = bitArray.readBits(8);
if (aspectRatioIdc == EXTENDED_SAR) { if (aspectRatioIdc == NalUnitUtil.EXTENDED_SAR) {
int sarWidth = bitArray.readBits(16); int sarWidth = bitArray.readBits(16);
int sarHeight = bitArray.readBits(16); int sarHeight = bitArray.readBits(16);
if (sarWidth != 0 && sarHeight != 0) { if (sarWidth != 0 && sarHeight != 0) {
pixelWidthHeightRatio = (float) sarWidth / sarHeight; pixelWidthHeightRatio = (float) sarWidth / sarHeight;
} }
} else if (aspectRatioIdc < ASPECT_RATIO_IDC_VALUES.length) { } else if (aspectRatioIdc < NalUnitUtil.ASPECT_RATIO_IDC_VALUES.length) {
pixelWidthHeightRatio = ASPECT_RATIO_IDC_VALUES[aspectRatioIdc]; pixelWidthHeightRatio = NalUnitUtil.ASPECT_RATIO_IDC_VALUES[aspectRatioIdc];
} else { } else {
Log.w(TAG, "Unexpected aspect_ratio_idc value: " + aspectRatioIdc); Log.w(TAG, "Unexpected aspect_ratio_idc value: " + aspectRatioIdc);
} }
@ -394,96 +373,6 @@ import java.util.List;
return limit; return limit;
} }
/**
* A buffer that fills itself with data corresponding to a specific NAL unit, as it is
* encountered in the stream.
*/
private static final class NalUnitTargetBuffer {
private final int targetType;
private boolean isFilling;
private boolean isCompleted;
public byte[] nalData;
public int nalLength;
public NalUnitTargetBuffer(int targetType, int initialCapacity) {
this.targetType = targetType;
// Initialize data, writing the known NAL prefix into the first four bytes.
nalData = new byte[4 + initialCapacity];
nalData[2] = 1;
nalData[3] = (byte) targetType;
}
/**
* Resets the buffer, clearing any data that it holds.
*/
public void reset() {
isFilling = false;
isCompleted = false;
}
/**
* True if the buffer currently holds a complete NAL unit of the target type.
*/
public boolean isCompleted() {
return isCompleted;
}
/**
* Invoked to indicate that a NAL unit has started.
*
* @param type The type of the NAL unit.
*/
public void startNalUnit(int type) {
Assertions.checkState(!isFilling);
isFilling = type == targetType;
if (isFilling) {
// Length is initially the length of the NAL prefix.
nalLength = 4;
isCompleted = false;
}
}
/**
* Invoked to pass stream data. The data passed should not include 4 byte NAL unit prefixes.
*
* @param data Holds the data being passed.
* @param offset The offset of the data in {@code data}.
* @param limit The limit (exclusive) of the data in {@code data}.
*/
public void appendToNalUnit(byte[] data, int offset, int limit) {
if (!isFilling) {
return;
}
int readLength = limit - offset;
if (nalData.length < nalLength + readLength) {
nalData = Arrays.copyOf(nalData, (nalLength + readLength) * 2);
}
System.arraycopy(data, offset, nalData, nalLength, readLength);
nalLength += readLength;
}
/**
* Invoked to indicate that a NAL unit has ended.
*
* @param discardPadding The number of excess bytes that were passed to
* {@link #appendToNalUnit(byte[], int, int)}, which should be discarded.
* @return True if the ended NAL unit is of the target type. False otherwise.
*/
public boolean endNalUnit(int discardPadding) {
if (!isFilling) {
return false;
}
nalLength -= discardPadding;
isFilling = false;
isCompleted = true;
return true;
}
}
/** /**
* A buffer specifically for IFR units that can be used to parse the IFR's slice type. * A buffer specifically for IFR units that can be used to parse the IFR's slice type.
*/ */

View File

@ -18,7 +18,6 @@ package com.google.android.exoplayer.extractor.ts;
import com.google.android.exoplayer.C; import com.google.android.exoplayer.C;
import com.google.android.exoplayer.MediaFormat; import com.google.android.exoplayer.MediaFormat;
import com.google.android.exoplayer.extractor.TrackOutput; import com.google.android.exoplayer.extractor.TrackOutput;
import com.google.android.exoplayer.util.Assertions;
import com.google.android.exoplayer.util.MimeTypes; import com.google.android.exoplayer.util.MimeTypes;
import com.google.android.exoplayer.util.NalUnitUtil; import com.google.android.exoplayer.util.NalUnitUtil;
import com.google.android.exoplayer.util.ParsableBitArray; import com.google.android.exoplayer.util.ParsableBitArray;
@ -51,28 +50,6 @@ import java.util.Collections;
private static final int PREFIX_SEI_NUT = 39; private static final int PREFIX_SEI_NUT = 39;
private static final int SUFFIX_SEI_NUT = 40; private static final int SUFFIX_SEI_NUT = 40;
// TODO: Deduplicate with H264Reader.
private static final int EXTENDED_SAR = 0xFF;
private static final float[] ASPECT_RATIO_IDC_VALUES = new float[] {
1f /* Unspecified. Assume square */,
1f,
12f / 11f,
10f / 11f,
16f / 11f,
40f / 33f,
24f / 11f,
20f / 11f,
32f / 11f,
80f / 33f,
18f / 11f,
15f / 11f,
64f / 33f,
160f / 99f,
4f / 3f,
3f / 2f,
2f
};
// State that should not be reset on seek. // State that should not be reset on seek.
private boolean hasOutputFormat; private boolean hasOutputFormat;
@ -321,14 +298,14 @@ import java.util.Collections;
if (bitArray.readBit()) { // vui_parameters_present_flag if (bitArray.readBit()) { // vui_parameters_present_flag
if (bitArray.readBit()) { // aspect_ratio_info_present_flag if (bitArray.readBit()) { // aspect_ratio_info_present_flag
int aspectRatioIdc = bitArray.readBits(8); int aspectRatioIdc = bitArray.readBits(8);
if (aspectRatioIdc == EXTENDED_SAR) { if (aspectRatioIdc == NalUnitUtil.EXTENDED_SAR) {
int sarWidth = bitArray.readBits(16); int sarWidth = bitArray.readBits(16);
int sarHeight = bitArray.readBits(16); int sarHeight = bitArray.readBits(16);
if (sarWidth != 0 && sarHeight != 0) { if (sarWidth != 0 && sarHeight != 0) {
pixelWidthHeightRatio = (float) sarWidth / sarHeight; pixelWidthHeightRatio = (float) sarWidth / sarHeight;
} }
} else if (aspectRatioIdc < ASPECT_RATIO_IDC_VALUES.length) { } else if (aspectRatioIdc < NalUnitUtil.ASPECT_RATIO_IDC_VALUES.length) {
pixelWidthHeightRatio = ASPECT_RATIO_IDC_VALUES[aspectRatioIdc]; pixelWidthHeightRatio = NalUnitUtil.ASPECT_RATIO_IDC_VALUES[aspectRatioIdc];
} else { } else {
Log.w(TAG, "Unexpected aspect_ratio_idc value: " + aspectRatioIdc); Log.w(TAG, "Unexpected aspect_ratio_idc value: " + aspectRatioIdc);
} }
@ -464,92 +441,4 @@ import java.util.Collections;
} }
} }
// TODO: Deduplicate with H264Reader.NalUnitTargetBuffer.
/**
* A buffer that fills itself with data corresponding to a specific NAL unit, as it is
* encountered in the stream.
*/
private static final class NalUnitTargetBuffer {
private final int targetType;
private boolean isFilling;
private boolean isCompleted;
public byte[] nalData;
public int nalLength;
public NalUnitTargetBuffer(int targetType, int initialCapacity) {
this.targetType = targetType;
nalData = new byte[5 + initialCapacity];
nalData[2] = 1;
}
/**
* Resets the buffer, clearing any data that it holds.
*/
public void reset() {
isFilling = false;
isCompleted = false;
}
/**
* True if the buffer currently holds a complete NAL unit of the target type.
*/
public boolean isCompleted() {
return isCompleted;
}
/**
* Invoked to indicate that a NAL unit has started.
*
* @param type The type of the NAL unit.
*/
public void startNalUnit(int type) {
Assertions.checkState(!isFilling);
isFilling = type == targetType;
if (isFilling) {
nalLength = 3;
isCompleted = false;
}
}
/**
* Invoked to pass stream data. The data passed should not include 4 byte NAL unit prefixes.
*
* @param data Holds the data being passed.
* @param offset The offset of the data in {@code data}.
* @param limit The limit (exclusive) of the data in {@code data}.
*/
public void appendToNalUnit(byte[] data, int offset, int limit) {
if (!isFilling) {
return;
}
int readLength = limit - offset;
if (nalData.length < nalLength + readLength) {
nalData = Arrays.copyOf(nalData, (nalLength + readLength) * 2);
}
System.arraycopy(data, offset, nalData, nalLength, readLength);
nalLength += readLength;
}
/**
* Invoked to indicate that a NAL unit has ended.
*
* @param discardPadding The number of excess bytes that were passed to
* {@link #appendToNalUnit(byte[], int, int)}, which should be discarded.
* @return True if the ended NAL unit is of the target type. False otherwise.
*/
public boolean endNalUnit(int discardPadding) {
if (!isFilling) {
return false;
}
nalLength -= discardPadding;
isFilling = false;
isCompleted = true;
return true;
}
}
} }

View File

@ -0,0 +1,110 @@
/*
* Copyright (C) 2014 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.exoplayer.extractor.ts;
import com.google.android.exoplayer.util.Assertions;
import java.util.Arrays;
/**
* A buffer that fills itself with data corresponding to a specific NAL unit, as it is
* encountered in the stream.
*/
/* package */ final class NalUnitTargetBuffer {
private final int targetType;
private boolean isFilling;
private boolean isCompleted;
public byte[] nalData;
public int nalLength;
public NalUnitTargetBuffer(int targetType, int initialCapacity) {
this.targetType = targetType;
// Initialize data with a start code in the first three bytes.
nalData = new byte[3 + initialCapacity];
nalData[2] = 1;
}
/**
* Resets the buffer, clearing any data that it holds.
*/
public void reset() {
isFilling = false;
isCompleted = false;
}
/**
* Returns whether the buffer currently holds a complete NAL unit of the target type.
*/
public boolean isCompleted() {
return isCompleted;
}
/**
* Invoked to indicate that a NAL unit has started.
*
* @param type The type of the NAL unit.
*/
public void startNalUnit(int type) {
Assertions.checkState(!isFilling);
isFilling = type == targetType;
if (isFilling) {
// Skip the three byte start code when writing data.
nalLength = 3;
isCompleted = false;
}
}
/**
* Invoked to pass stream data. The data passed should not include the 3 byte start code.
*
* @param data Holds the data being passed.
* @param offset The offset of the data in {@code data}.
* @param limit The limit (exclusive) of the data in {@code data}.
*/
public void appendToNalUnit(byte[] data, int offset, int limit) {
if (!isFilling) {
return;
}
int readLength = limit - offset;
if (nalData.length < nalLength + readLength) {
nalData = Arrays.copyOf(nalData, (nalLength + readLength) * 2);
}
System.arraycopy(data, offset, nalData, nalLength, readLength);
nalLength += readLength;
}
/**
* Invoked to indicate that a NAL unit has ended.
*
* @param discardPadding The number of excess bytes that were passed to
* {@link #appendToNalUnit(byte[], int, int)}, which should be discarded.
* @return True if the ended NAL unit is of the target type. False otherwise.
*/
public boolean endNalUnit(int discardPadding) {
if (!isFilling) {
return false;
}
nalLength -= discardPadding;
isFilling = false;
isCompleted = true;
return true;
}
}

View File

@ -25,6 +25,29 @@ public final class NalUnitUtil {
/** Four initial bytes that must prefix NAL units for decoding. */ /** Four initial bytes that must prefix NAL units for decoding. */
public static final byte[] NAL_START_CODE = new byte[] {0, 0, 0, 1}; public static final byte[] NAL_START_CODE = new byte[] {0, 0, 0, 1};
/** Value for aspect_ratio_idc indicating an extended aspect ratio, in H.264 and H.265 SPSs. */
public static final int EXTENDED_SAR = 0xFF;
/** Aspect ratios indexed by aspect_ratio_idc, in H.264 and H.265 SPSs. */
public static final float[] ASPECT_RATIO_IDC_VALUES = new float[] {
1f /* Unspecified. Assume square */,
1f,
12f / 11f,
10f / 11f,
16f / 11f,
40f / 33f,
24f / 11f,
20f / 11f,
32f / 11f,
80f / 33f,
18f / 11f,
15f / 11f,
64f / 33f,
160f / 99f,
4f / 3f,
3f / 2f,
2f
};
/** /**
* Replaces length prefixes of NAL units in {@code buffer} with start code prefixes, within the * Replaces length prefixes of NAL units in {@code buffer} with start code prefixes, within the
* {@code size} bytes preceding the buffer's position. * {@code size} bytes preceding the buffer's position.
@ -79,7 +102,7 @@ public final class NalUnitUtil {
/** /**
* Finds the first NAL unit in {@code data}. * Finds the first NAL unit in {@code data}.
* <p> * <p>
* If {@code prefixFlags} is null then the first four bytes of a NAL unit must be entirely * If {@code prefixFlags} is null then the first three bytes of a NAL unit must be entirely
* contained within the part of the array being searched in order for it to be found. * contained within the part of the array being searched in order for it to be found.
* <p> * <p>
* When {@code prefixFlags} is non-null, this method supports finding NAL units whose first four * When {@code prefixFlags} is non-null, this method supports finding NAL units whose first four
@ -121,9 +144,8 @@ public final class NalUnitUtil {
} }
int limit = endOffset - 1; int limit = endOffset - 1;
// We're looking for the NAL unit start code prefix 0x000001, followed by a byte that matches // We're looking for the NAL unit start code prefix 0x000001. The value of i tracks the index of
// the specified type. The value of i tracks the index of the third byte in the four bytes // the third byte.
// being examined.
for (int i = startOffset + 2; i < limit; i += 3) { for (int i = startOffset + 2; i < limit; i += 3) {
if ((data[i] & 0xFE) != 0) { if ((data[i] & 0xFE) != 0) {
// There isn't a NAL prefix here, or at the next two positions. Do nothing and let the // There isn't a NAL prefix here, or at the next two positions. Do nothing and let the
@ -146,10 +168,10 @@ public final class NalUnitUtil {
? (data[endOffset - 3] == 0 && data[endOffset - 2] == 0 && data[endOffset - 1] == 1) ? (data[endOffset - 3] == 0 && data[endOffset - 2] == 0 && data[endOffset - 1] == 1)
: length == 2 ? (prefixFlags[2] && data[endOffset - 2] == 0 && data[endOffset - 1] == 1) : length == 2 ? (prefixFlags[2] && data[endOffset - 2] == 0 && data[endOffset - 1] == 1)
: (prefixFlags[1] && data[endOffset - 1] == 1); : (prefixFlags[1] && data[endOffset - 1] == 1);
// True if the last three bytes in the data seen so far are {0,0}. // True if the last two bytes in the data seen so far are {0,0}.
prefixFlags[1] = length > 1 ? data[endOffset - 2] == 0 && data[endOffset - 1] == 0 prefixFlags[1] = length > 1 ? data[endOffset - 2] == 0 && data[endOffset - 1] == 0
: prefixFlags[2] && data[endOffset - 1] == 0; : prefixFlags[2] && data[endOffset - 1] == 0;
// True if the last three bytes in the data seen so far are {0}. // True if the last byte in the data seen so far is {0}.
prefixFlags[2] = data[endOffset - 1] == 0; prefixFlags[2] = data[endOffset - 1] == 0;
} }