diff --git a/library/src/main/java/com/google/android/exoplayer/extractor/ts/H264Reader.java b/library/src/main/java/com/google/android/exoplayer/extractor/ts/H264Reader.java index c9dcb1fa7d..d246bd6d8c 100644 --- a/library/src/main/java/com/google/android/exoplayer/extractor/ts/H264Reader.java +++ b/library/src/main/java/com/google/android/exoplayer/extractor/ts/H264Reader.java @@ -18,7 +18,6 @@ package com.google.android.exoplayer.extractor.ts; import com.google.android.exoplayer.C; import com.google.android.exoplayer.MediaFormat; 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.NalUnitUtil; 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_PPS = 8; // Picture parameter set 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. private boolean hasOutputFormat; @@ -172,7 +151,7 @@ import java.util.List; // Notify the start of the next NAL unit. feedNalUnitTargetBuffersStart(nalUnitType); // Continue scanning the data. - offset = nextNalUnitOffset + 4; + offset = nextNalUnitOffset + 3; } else { feedNalUnitTargetBuffersData(dataArray, offset, limit); offset = limit; @@ -312,14 +291,14 @@ import java.util.List; boolean aspectRatioInfoPresentFlag = bitArray.readBit(); if (aspectRatioInfoPresentFlag) { int aspectRatioIdc = bitArray.readBits(8); - if (aspectRatioIdc == EXTENDED_SAR) { + if (aspectRatioIdc == NalUnitUtil.EXTENDED_SAR) { int sarWidth = bitArray.readBits(16); int sarHeight = bitArray.readBits(16); if (sarWidth != 0 && sarHeight != 0) { pixelWidthHeightRatio = (float) sarWidth / sarHeight; } - } else if (aspectRatioIdc < ASPECT_RATIO_IDC_VALUES.length) { - pixelWidthHeightRatio = ASPECT_RATIO_IDC_VALUES[aspectRatioIdc]; + } else if (aspectRatioIdc < NalUnitUtil.ASPECT_RATIO_IDC_VALUES.length) { + pixelWidthHeightRatio = NalUnitUtil.ASPECT_RATIO_IDC_VALUES[aspectRatioIdc]; } else { Log.w(TAG, "Unexpected aspect_ratio_idc value: " + aspectRatioIdc); } @@ -394,96 +373,6 @@ import java.util.List; 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. */ diff --git a/library/src/main/java/com/google/android/exoplayer/extractor/ts/H265Reader.java b/library/src/main/java/com/google/android/exoplayer/extractor/ts/H265Reader.java index 9728c59e1f..6170ad48a8 100644 --- a/library/src/main/java/com/google/android/exoplayer/extractor/ts/H265Reader.java +++ b/library/src/main/java/com/google/android/exoplayer/extractor/ts/H265Reader.java @@ -18,7 +18,6 @@ package com.google.android.exoplayer.extractor.ts; import com.google.android.exoplayer.C; import com.google.android.exoplayer.MediaFormat; 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.NalUnitUtil; 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 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. private boolean hasOutputFormat; @@ -321,14 +298,14 @@ import java.util.Collections; if (bitArray.readBit()) { // vui_parameters_present_flag if (bitArray.readBit()) { // aspect_ratio_info_present_flag int aspectRatioIdc = bitArray.readBits(8); - if (aspectRatioIdc == EXTENDED_SAR) { + if (aspectRatioIdc == NalUnitUtil.EXTENDED_SAR) { int sarWidth = bitArray.readBits(16); int sarHeight = bitArray.readBits(16); if (sarWidth != 0 && sarHeight != 0) { pixelWidthHeightRatio = (float) sarWidth / sarHeight; } - } else if (aspectRatioIdc < ASPECT_RATIO_IDC_VALUES.length) { - pixelWidthHeightRatio = ASPECT_RATIO_IDC_VALUES[aspectRatioIdc]; + } else if (aspectRatioIdc < NalUnitUtil.ASPECT_RATIO_IDC_VALUES.length) { + pixelWidthHeightRatio = NalUnitUtil.ASPECT_RATIO_IDC_VALUES[aspectRatioIdc]; } else { 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; - } - - } - } diff --git a/library/src/main/java/com/google/android/exoplayer/extractor/ts/NalUnitTargetBuffer.java b/library/src/main/java/com/google/android/exoplayer/extractor/ts/NalUnitTargetBuffer.java new file mode 100644 index 0000000000..9b8895082c --- /dev/null +++ b/library/src/main/java/com/google/android/exoplayer/extractor/ts/NalUnitTargetBuffer.java @@ -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; + } + +} diff --git a/library/src/main/java/com/google/android/exoplayer/util/NalUnitUtil.java b/library/src/main/java/com/google/android/exoplayer/util/NalUnitUtil.java index 0e390e4a94..6d658a750b 100644 --- a/library/src/main/java/com/google/android/exoplayer/util/NalUnitUtil.java +++ b/library/src/main/java/com/google/android/exoplayer/util/NalUnitUtil.java @@ -25,6 +25,29 @@ public final class NalUnitUtil { /** Four initial bytes that must prefix NAL units for decoding. */ 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 * {@code size} bytes preceding the buffer's position. @@ -79,7 +102,7 @@ public final class NalUnitUtil { /** * Finds the first NAL unit in {@code data}. *
- * 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. *
* 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; - // We're looking for the NAL unit start code prefix 0x000001, followed by a byte that matches - // the specified type. The value of i tracks the index of the third byte in the four bytes - // being examined. + // We're looking for the NAL unit start code prefix 0x000001. The value of i tracks the index of + // the third byte. for (int i = startOffset + 2; i < limit; i += 3) { if ((data[i] & 0xFE) != 0) { // 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) : length == 2 ? (prefixFlags[2] && data[endOffset - 2] == 0 && 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[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; }