mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
Imported from GitHub PR https://github.com/google/ExoPlayer/pull/8401 This will allow ExoPlayer to check if video track's profile and level are supported by decoder when playing progressive media sources. Merge e582fb91b73c7c95e754167140211d5473c36d14 into 1347d572ef9ce79aacd667cfffa7d7468c8408a4 Issue: #8393 COPYBARA_INTEGRATE_REVIEW=https://github.com/google/ExoPlayer/pull/8401 from equeim:hevc-codecs e582fb91b73c7c95e754167140211d5473c36d14 PiperOrigin-RevId: 350738065
This commit is contained in:
parent
eded1ca106
commit
ff8c8645ab
@ -38,12 +38,16 @@
|
||||
([#8349](https://github.com/google/ExoPlayer/issues/8349)).
|
||||
* Add `DefaultHttpDataSource.Factory` and deprecate
|
||||
`DefaultHttpDataSourceFactory`.
|
||||
* Populate codecs string for H.264/AVC in MP4, Matroska and FLV streams to
|
||||
allow decoder capability checks based on codec profile/level
|
||||
([#8393](https://github.com/google/ExoPlayer/issues/8393)).
|
||||
* Add option to `MergingMediaSource` to clip the durations of all sources
|
||||
to have the same length
|
||||
([#8422](https://github.com/google/ExoPlayer/issues/8422)).
|
||||
* Extractors:
|
||||
* Populate codecs string for H.264/AVC in MP4, Matroska and FLV streams to
|
||||
allow decoder capability checks based on codec profile/level
|
||||
([#8393](https://github.com/google/ExoPlayer/issues/8393)).
|
||||
* Populate codecs string for H.265/HEVC in MP4, Matroska and MPEG-TS
|
||||
streams to allow decoder capability checks based on codec profile/level
|
||||
([#8393](https://github.com/google/ExoPlayer/issues/8393)).
|
||||
* Track selection:
|
||||
* Allow parallel adaptation for video and audio
|
||||
([#5111](https://github.com/google/ExoPlayer/issues/5111)).
|
||||
|
@ -26,6 +26,8 @@ import java.util.List;
|
||||
public final class CodecSpecificDataUtil {
|
||||
|
||||
private static final byte[] NAL_START_CODE = new byte[] {0, 0, 0, 1};
|
||||
private static final String[] HEVC_GENERAL_PROFILE_SPACE_STRINGS =
|
||||
new String[] {"", "A", "B", "C"};
|
||||
|
||||
/**
|
||||
* Parses an ALAC AudioSpecificConfig (i.e. an <a
|
||||
@ -83,6 +85,49 @@ public final class CodecSpecificDataUtil {
|
||||
"avc1.%02X%02X%02X", profileIdc, constraintsFlagsAndReservedZero2Bits, levelIdc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an RFC 6381 HEVC codec string based on the SPS NAL unit read from the provided bit
|
||||
* array. The position of the bit array must be the start of an SPS NALU (nal_unit_header), and
|
||||
* the position may be modified by this method.
|
||||
*/
|
||||
public static String buildHevcCodecStringFromSps(ParsableNalUnitBitArray bitArray) {
|
||||
// Skip nal_unit_header, sps_video_parameter_set_id, sps_max_sub_layers_minus1 and
|
||||
// sps_temporal_id_nesting_flag.
|
||||
bitArray.skipBits(16 + 4 + 3 + 1);
|
||||
int generalProfileSpace = bitArray.readBits(2);
|
||||
boolean generalTierFlag = bitArray.readBit();
|
||||
int generalProfileIdc = bitArray.readBits(5);
|
||||
int generalProfileCompatibilityFlags = 0;
|
||||
for (int i = 0; i < 32; i++) {
|
||||
if (bitArray.readBit()) {
|
||||
generalProfileCompatibilityFlags |= (1 << i);
|
||||
}
|
||||
}
|
||||
int[] constraintBytes = new int[6];
|
||||
for (int i = 0; i < constraintBytes.length; ++i) {
|
||||
constraintBytes[i] = bitArray.readBits(8);
|
||||
}
|
||||
int generalLevelIdc = bitArray.readBits(8);
|
||||
StringBuilder builder =
|
||||
new StringBuilder(
|
||||
Util.formatInvariant(
|
||||
"hvc1.%s%d.%X.%c%d",
|
||||
HEVC_GENERAL_PROFILE_SPACE_STRINGS[generalProfileSpace],
|
||||
generalProfileIdc,
|
||||
generalProfileCompatibilityFlags,
|
||||
generalTierFlag ? 'H' : 'L',
|
||||
generalLevelIdc));
|
||||
// Omit trailing zero bytes.
|
||||
int trailingZeroIndex = constraintBytes.length;
|
||||
while (trailingZeroIndex > 0 && constraintBytes[trailingZeroIndex - 1] == 0) {
|
||||
trailingZeroIndex--;
|
||||
}
|
||||
for (int i = 0; i < trailingZeroIndex; i++) {
|
||||
builder.append(String.format(".%02X", constraintBytes[i]));
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a NAL unit consisting of the NAL start code followed by the specified data.
|
||||
*
|
||||
|
@ -17,8 +17,10 @@ package com.google.android.exoplayer2.video;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import com.google.android.exoplayer2.ParserException;
|
||||
import com.google.android.exoplayer2.util.CodecSpecificDataUtil;
|
||||
import com.google.android.exoplayer2.util.NalUnitUtil;
|
||||
import com.google.android.exoplayer2.util.ParsableByteArray;
|
||||
import com.google.android.exoplayer2.util.ParsableNalUnitBitArray;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@ -27,9 +29,6 @@ import java.util.List;
|
||||
*/
|
||||
public final class HevcConfig {
|
||||
|
||||
@Nullable public final List<byte[]> initializationData;
|
||||
public final int nalUnitLengthFieldLength;
|
||||
|
||||
/**
|
||||
* Parses HEVC configuration data.
|
||||
*
|
||||
@ -61,8 +60,9 @@ public final class HevcConfig {
|
||||
data.setPosition(csdStartPosition);
|
||||
byte[] buffer = new byte[csdLength];
|
||||
int bufferPosition = 0;
|
||||
@Nullable String codecs = null;
|
||||
for (int i = 0; i < numberOfArrays; i++) {
|
||||
data.skipBytes(1); // completeness (1), nal_unit_type (7)
|
||||
int nalUnitType = data.readUnsignedByte() & 0x7F; // completeness (1), nal_unit_type (7)
|
||||
int numberOfNalUnits = data.readUnsignedShort();
|
||||
for (int j = 0; j < numberOfNalUnits; j++) {
|
||||
int nalUnitLength = data.readUnsignedShort();
|
||||
@ -71,21 +71,49 @@ public final class HevcConfig {
|
||||
bufferPosition += NalUnitUtil.NAL_START_CODE.length;
|
||||
System.arraycopy(
|
||||
data.getData(), data.getPosition(), buffer, bufferPosition, nalUnitLength);
|
||||
if (nalUnitType == SPS_NAL_UNIT_TYPE && j == 0) {
|
||||
codecs =
|
||||
CodecSpecificDataUtil.buildHevcCodecStringFromSps(
|
||||
new ParsableNalUnitBitArray(buffer, bufferPosition, nalUnitLength));
|
||||
}
|
||||
bufferPosition += nalUnitLength;
|
||||
data.skipBytes(nalUnitLength);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
List<byte[]> initializationData = csdLength == 0 ? null : Collections.singletonList(buffer);
|
||||
return new HevcConfig(initializationData, lengthSizeMinusOne + 1);
|
||||
return new HevcConfig(initializationData, lengthSizeMinusOne + 1, codecs);
|
||||
} catch (ArrayIndexOutOfBoundsException e) {
|
||||
throw new ParserException("Error parsing HEVC config", e);
|
||||
}
|
||||
}
|
||||
|
||||
private HevcConfig(@Nullable List<byte[]> initializationData, int nalUnitLengthFieldLength) {
|
||||
private static final int SPS_NAL_UNIT_TYPE = 33;
|
||||
|
||||
/**
|
||||
* List of buffers containing the codec-specific data to be provided to the decoder, or {@code
|
||||
* null} if not known.
|
||||
*
|
||||
* @see com.google.android.exoplayer2.Format#initializationData
|
||||
*/
|
||||
@Nullable public final List<byte[]> initializationData;
|
||||
/** The length of the NAL unit length field in the bitstream's container, in bytes. */
|
||||
public final int nalUnitLengthFieldLength;
|
||||
/**
|
||||
* An RFC 6381 codecs string representing the video format, or {@code null} if not known.
|
||||
*
|
||||
* @see com.google.android.exoplayer2.Format#codecs
|
||||
*/
|
||||
@Nullable public final String codecs;
|
||||
|
||||
private HevcConfig(
|
||||
@Nullable List<byte[]> initializationData,
|
||||
int nalUnitLengthFieldLength,
|
||||
@Nullable String codecs) {
|
||||
this.initializationData = initializationData;
|
||||
this.nalUnitLengthFieldLength = nalUnitLengthFieldLength;
|
||||
this.codecs = codecs;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -2093,6 +2093,7 @@ public class MatroskaExtractor implements Extractor {
|
||||
HevcConfig hevcConfig = HevcConfig.parse(new ParsableByteArray(getCodecPrivate(codecId)));
|
||||
initializationData = hevcConfig.initializationData;
|
||||
nalUnitLengthFieldLength = hevcConfig.nalUnitLengthFieldLength;
|
||||
codecs = hevcConfig.codecs;
|
||||
break;
|
||||
case CODEC_ID_FOURCC:
|
||||
Pair<String, @NullableType List<byte[]>> pair =
|
||||
|
@ -1066,6 +1066,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
|
||||
HevcConfig hevcConfig = HevcConfig.parse(parent);
|
||||
initializationData = hevcConfig.initializationData;
|
||||
out.nalUnitLengthFieldLength = hevcConfig.nalUnitLengthFieldLength;
|
||||
codecs = hevcConfig.codecs;
|
||||
} else if (childAtomType == Atom.TYPE_dvcC || childAtomType == Atom.TYPE_dvvC) {
|
||||
@Nullable DolbyVisionConfig dolbyVisionConfig = DolbyVisionConfig.parse(parent);
|
||||
if (dolbyVisionConfig != null) {
|
||||
|
@ -24,6 +24,7 @@ import com.google.android.exoplayer2.extractor.ExtractorOutput;
|
||||
import com.google.android.exoplayer2.extractor.TrackOutput;
|
||||
import com.google.android.exoplayer2.extractor.ts.TsPayloadReader.TrackIdGenerator;
|
||||
import com.google.android.exoplayer2.util.Assertions;
|
||||
import com.google.android.exoplayer2.util.CodecSpecificDataUtil;
|
||||
import com.google.android.exoplayer2.util.Log;
|
||||
import com.google.android.exoplayer2.util.MimeTypes;
|
||||
import com.google.android.exoplayer2.util.NalUnitUtil;
|
||||
@ -336,9 +337,15 @@ public final class H265Reader implements ElementaryStreamReader {
|
||||
}
|
||||
}
|
||||
|
||||
// Parse the SPS to derive an RFC 6381 codecs string.
|
||||
bitArray.reset(sps.nalData, 0, sps.nalLength);
|
||||
bitArray.skipBits(24); // Skip start code.
|
||||
String codecs = CodecSpecificDataUtil.buildHevcCodecStringFromSps(bitArray);
|
||||
|
||||
return new Format.Builder()
|
||||
.setId(formatId)
|
||||
.setSampleMimeType(MimeTypes.VIDEO_H265)
|
||||
.setCodecs(codecs)
|
||||
.setWidth(picWidthInLumaSamples)
|
||||
.setHeight(picHeightInLumaSamples)
|
||||
.setPixelWidthHeightRatio(pixelWidthHeightRatio)
|
||||
|
@ -12,6 +12,7 @@ track 256:
|
||||
format 0:
|
||||
id = 1/256
|
||||
sampleMimeType = video/hevc
|
||||
codecs = hvc1.1.6.L90.90
|
||||
width = 854
|
||||
height = 480
|
||||
initializationData:
|
||||
|
@ -12,6 +12,7 @@ track 256:
|
||||
format 0:
|
||||
id = 1/256
|
||||
sampleMimeType = video/hevc
|
||||
codecs = hvc1.1.6.L90.90
|
||||
width = 854
|
||||
height = 480
|
||||
initializationData:
|
||||
|
@ -12,6 +12,7 @@ track 256:
|
||||
format 0:
|
||||
id = 1/256
|
||||
sampleMimeType = video/hevc
|
||||
codecs = hvc1.1.6.L90.90
|
||||
width = 854
|
||||
height = 480
|
||||
initializationData:
|
||||
|
@ -12,6 +12,7 @@ track 256:
|
||||
format 0:
|
||||
id = 1/256
|
||||
sampleMimeType = video/hevc
|
||||
codecs = hvc1.1.6.L90.90
|
||||
width = 854
|
||||
height = 480
|
||||
initializationData:
|
||||
|
@ -9,6 +9,7 @@ track 256:
|
||||
format 0:
|
||||
id = 1/256
|
||||
sampleMimeType = video/hevc
|
||||
codecs = hvc1.1.6.L90.90
|
||||
width = 854
|
||||
height = 480
|
||||
initializationData:
|
||||
|
Loading…
x
Reference in New Issue
Block a user