Initialize Format.codecs from AVC SPS NAL unit (#8393)

This will allow ExoPlayer to check if video track's profile
and level are supported by decoder when playing progressive media sources.

Also fix typo in AvcConfig.
This commit is contained in:
Alexey Rochev 2020-12-24 16:22:25 +03:00
parent 1347d572ef
commit 78a975c268
4 changed files with 12 additions and 3 deletions

View File

@ -15,6 +15,7 @@
*/ */
package com.google.android.exoplayer2.video; package com.google.android.exoplayer2.video;
import androidx.annotation.Nullable;
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.util.CodecSpecificDataUtil; import com.google.android.exoplayer2.util.CodecSpecificDataUtil;
@ -34,13 +35,14 @@ public final class AvcConfig {
public final int width; public final int width;
public final int height; public final int height;
public final float pixelWidthAspectRatio; public final float pixelWidthAspectRatio;
@Nullable public final String codecs;
/** /**
* Parses AVC configuration data. * Parses AVC configuration data.
* *
* @param data A {@link ParsableByteArray}, whose position is set to the start of the AVC * @param data A {@link ParsableByteArray}, whose position is set to the start of the AVC
* configuration data to parse. * configuration data to parse.
* @return A parsed representation of the HEVC configuration data. * @return A parsed representation of the AVC configuration data.
* @throws ParserException If an error occurred parsing the data. * @throws ParserException If an error occurred parsing the data.
*/ */
public static AvcConfig parse(ParsableByteArray data) throws ParserException { public static AvcConfig parse(ParsableByteArray data) throws ParserException {
@ -63,6 +65,7 @@ public final class AvcConfig {
int width = Format.NO_VALUE; int width = Format.NO_VALUE;
int height = Format.NO_VALUE; int height = Format.NO_VALUE;
float pixelWidthAspectRatio = 1; float pixelWidthAspectRatio = 1;
@Nullable String codecs = null;
if (numSequenceParameterSets > 0) { if (numSequenceParameterSets > 0) {
byte[] sps = initializationData.get(0); byte[] sps = initializationData.get(0);
SpsData spsData = NalUnitUtil.parseSpsNalUnit(initializationData.get(0), SpsData spsData = NalUnitUtil.parseSpsNalUnit(initializationData.get(0),
@ -70,21 +73,24 @@ public final class AvcConfig {
width = spsData.width; width = spsData.width;
height = spsData.height; height = spsData.height;
pixelWidthAspectRatio = spsData.pixelWidthAspectRatio; pixelWidthAspectRatio = spsData.pixelWidthAspectRatio;
codecs = CodecSpecificDataUtil.buildAvcCodecString(spsData.profileIdc, spsData.constraintsFlagsAndReservedZero2Bits, spsData.levelIdc);
} }
return new AvcConfig(initializationData, nalUnitLengthFieldLength, width, height, return new AvcConfig(initializationData, nalUnitLengthFieldLength, width, height,
pixelWidthAspectRatio); pixelWidthAspectRatio, codecs);
} catch (ArrayIndexOutOfBoundsException e) { } catch (ArrayIndexOutOfBoundsException e) {
throw new ParserException("Error parsing AVC config", e); throw new ParserException("Error parsing AVC config", e);
} }
} }
private AvcConfig(List<byte[]> initializationData, int nalUnitLengthFieldLength, private AvcConfig(List<byte[]> initializationData, int nalUnitLengthFieldLength,
int width, int height, float pixelWidthAspectRatio) { int width, int height, float pixelWidthAspectRatio, @Nullable String codecs) {
this.initializationData = initializationData; this.initializationData = initializationData;
this.nalUnitLengthFieldLength = nalUnitLengthFieldLength; this.nalUnitLengthFieldLength = nalUnitLengthFieldLength;
this.width = width; this.width = width;
this.height = height; this.height = height;
this.pixelWidthAspectRatio = pixelWidthAspectRatio; this.pixelWidthAspectRatio = pixelWidthAspectRatio;
this.codecs = codecs;
} }
private static byte[] buildNalUnitForChild(ParsableByteArray data) { private static byte[] buildNalUnitForChild(ParsableByteArray data) {

View File

@ -93,6 +93,7 @@ import com.google.android.exoplayer2.video.AvcConfig;
Format format = Format format =
new Format.Builder() new Format.Builder()
.setSampleMimeType(MimeTypes.VIDEO_H264) .setSampleMimeType(MimeTypes.VIDEO_H264)
.setCodecs(avcConfig.codecs)
.setWidth(avcConfig.width) .setWidth(avcConfig.width)
.setHeight(avcConfig.height) .setHeight(avcConfig.height)
.setPixelWidthHeightRatio(avcConfig.pixelWidthAspectRatio) .setPixelWidthHeightRatio(avcConfig.pixelWidthAspectRatio)

View File

@ -2086,6 +2086,7 @@ public class MatroskaExtractor implements Extractor {
AvcConfig avcConfig = AvcConfig.parse(new ParsableByteArray(getCodecPrivate(codecId))); AvcConfig avcConfig = AvcConfig.parse(new ParsableByteArray(getCodecPrivate(codecId)));
initializationData = avcConfig.initializationData; initializationData = avcConfig.initializationData;
nalUnitLengthFieldLength = avcConfig.nalUnitLengthFieldLength; nalUnitLengthFieldLength = avcConfig.nalUnitLengthFieldLength;
codecs = avcConfig.codecs;
break; break;
case CODEC_ID_H265: case CODEC_ID_H265:
mimeType = MimeTypes.VIDEO_H265; mimeType = MimeTypes.VIDEO_H265;

View File

@ -1058,6 +1058,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
if (!pixelWidthHeightRatioFromPasp) { if (!pixelWidthHeightRatioFromPasp) {
pixelWidthHeightRatio = avcConfig.pixelWidthAspectRatio; pixelWidthHeightRatio = avcConfig.pixelWidthAspectRatio;
} }
codecs = avcConfig.codecs;
} 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;