Search in sources :

Example 56 with ParserException

use of com.google.android.exoplayer2.ParserException in project ExoPlayer by google.

the class AtomParsers method parseVideoSampleEntry.

// hdrStaticInfo is allocated using allocate() in allocateHdrStaticInfo().
@SuppressWarnings("ByteBufferBackingArray")
private static void parseVideoSampleEntry(ParsableByteArray parent, int atomType, int position, int size, int trackId, int rotationDegrees, @Nullable DrmInitData drmInitData, StsdData out, int entryIndex) throws ParserException {
    parent.setPosition(position + Atom.HEADER_SIZE + StsdData.STSD_HEADER_SIZE);
    parent.skipBytes(16);
    int width = parent.readUnsignedShort();
    int height = parent.readUnsignedShort();
    boolean pixelWidthHeightRatioFromPasp = false;
    float pixelWidthHeightRatio = 1;
    parent.skipBytes(50);
    int childPosition = parent.getPosition();
    if (atomType == Atom.TYPE_encv) {
        @Nullable Pair<Integer, TrackEncryptionBox> sampleEntryEncryptionData = parseSampleEntryEncryptionData(parent, position, size);
        if (sampleEntryEncryptionData != null) {
            atomType = sampleEntryEncryptionData.first;
            drmInitData = drmInitData == null ? null : drmInitData.copyWithSchemeType(sampleEntryEncryptionData.second.schemeType);
            out.trackEncryptionBoxes[entryIndex] = sampleEntryEncryptionData.second;
        }
        parent.setPosition(childPosition);
    }
    // TODO: Uncomment when [Internal: b/63092960] is fixed.
    // else {
    // drmInitData = null;
    // }
    @Nullable String mimeType = null;
    if (atomType == Atom.TYPE_m1v_) {
        mimeType = MimeTypes.VIDEO_MPEG;
    } else if (atomType == Atom.TYPE_H263) {
        mimeType = MimeTypes.VIDEO_H263;
    }
    @Nullable List<byte[]> initializationData = null;
    @Nullable String codecs = null;
    @Nullable byte[] projectionData = null;
    @C.StereoMode int stereoMode = Format.NO_VALUE;
    // HDR related metadata.
    @C.ColorSpace int colorSpace = Format.NO_VALUE;
    @C.ColorRange int colorRange = Format.NO_VALUE;
    @C.ColorTransfer int colorTransfer = Format.NO_VALUE;
    // The format of HDR static info is defined in CTA-861-G:2017, Table 45.
    @Nullable ByteBuffer hdrStaticInfo = null;
    while (childPosition - position < size) {
        parent.setPosition(childPosition);
        int childStartPosition = parent.getPosition();
        int childAtomSize = parent.readInt();
        if (childAtomSize == 0 && parent.getPosition() - position == size) {
            // Handle optional terminating four zero bytes in MOV files.
            break;
        }
        ExtractorUtil.checkContainerInput(childAtomSize > 0, "childAtomSize must be positive");
        int childAtomType = parent.readInt();
        if (childAtomType == Atom.TYPE_avcC) {
            ExtractorUtil.checkContainerInput(mimeType == null, /* message= */
            null);
            mimeType = MimeTypes.VIDEO_H264;
            parent.setPosition(childStartPosition + Atom.HEADER_SIZE);
            AvcConfig avcConfig = AvcConfig.parse(parent);
            initializationData = avcConfig.initializationData;
            out.nalUnitLengthFieldLength = avcConfig.nalUnitLengthFieldLength;
            if (!pixelWidthHeightRatioFromPasp) {
                pixelWidthHeightRatio = avcConfig.pixelWidthHeightRatio;
            }
            codecs = avcConfig.codecs;
        } else if (childAtomType == Atom.TYPE_hvcC) {
            ExtractorUtil.checkContainerInput(mimeType == null, /* message= */
            null);
            mimeType = MimeTypes.VIDEO_H265;
            parent.setPosition(childStartPosition + Atom.HEADER_SIZE);
            HevcConfig hevcConfig = HevcConfig.parse(parent);
            initializationData = hevcConfig.initializationData;
            out.nalUnitLengthFieldLength = hevcConfig.nalUnitLengthFieldLength;
            if (!pixelWidthHeightRatioFromPasp) {
                pixelWidthHeightRatio = hevcConfig.pixelWidthHeightRatio;
            }
            codecs = hevcConfig.codecs;
        } else if (childAtomType == Atom.TYPE_dvcC || childAtomType == Atom.TYPE_dvvC) {
            @Nullable DolbyVisionConfig dolbyVisionConfig = DolbyVisionConfig.parse(parent);
            if (dolbyVisionConfig != null) {
                codecs = dolbyVisionConfig.codecs;
                mimeType = MimeTypes.VIDEO_DOLBY_VISION;
            }
        } else if (childAtomType == Atom.TYPE_vpcC) {
            ExtractorUtil.checkContainerInput(mimeType == null, /* message= */
            null);
            mimeType = (atomType == Atom.TYPE_vp08) ? MimeTypes.VIDEO_VP8 : MimeTypes.VIDEO_VP9;
        } else if (childAtomType == Atom.TYPE_av1C) {
            ExtractorUtil.checkContainerInput(mimeType == null, /* message= */
            null);
            mimeType = MimeTypes.VIDEO_AV1;
        } else if (childAtomType == Atom.TYPE_clli) {
            if (hdrStaticInfo == null) {
                hdrStaticInfo = allocateHdrStaticInfo();
            }
            // The contents of the clli box occupy the last 4 bytes of the HDR static info array. Note
            // that each field is read in big endian and written in little endian.
            hdrStaticInfo.position(21);
            // max_content_light_level.
            hdrStaticInfo.putShort(parent.readShort());
            // max_pic_average_light_level.
            hdrStaticInfo.putShort(parent.readShort());
        } else if (childAtomType == Atom.TYPE_mdcv) {
            if (hdrStaticInfo == null) {
                hdrStaticInfo = allocateHdrStaticInfo();
            }
            // The contents of the mdcv box occupy 20 bytes after the first byte of the HDR static info
            // array. Note that each field is read in big endian and written in little endian.
            short displayPrimariesGX = parent.readShort();
            short displayPrimariesGY = parent.readShort();
            short displayPrimariesBX = parent.readShort();
            short displayPrimariesBY = parent.readShort();
            short displayPrimariesRX = parent.readShort();
            short displayPrimariesRY = parent.readShort();
            short whitePointX = parent.readShort();
            short whitePointY = parent.readShort();
            long maxDisplayMasteringLuminance = parent.readUnsignedInt();
            long minDisplayMasteringLuminance = parent.readUnsignedInt();
            hdrStaticInfo.position(1);
            hdrStaticInfo.putShort(displayPrimariesRX);
            hdrStaticInfo.putShort(displayPrimariesRY);
            hdrStaticInfo.putShort(displayPrimariesGX);
            hdrStaticInfo.putShort(displayPrimariesGY);
            hdrStaticInfo.putShort(displayPrimariesBX);
            hdrStaticInfo.putShort(displayPrimariesBY);
            hdrStaticInfo.putShort(whitePointX);
            hdrStaticInfo.putShort(whitePointY);
            hdrStaticInfo.putShort((short) (maxDisplayMasteringLuminance / 10000));
            hdrStaticInfo.putShort((short) (minDisplayMasteringLuminance / 10000));
        } else if (childAtomType == Atom.TYPE_d263) {
            ExtractorUtil.checkContainerInput(mimeType == null, /* message= */
            null);
            mimeType = MimeTypes.VIDEO_H263;
        } else if (childAtomType == Atom.TYPE_esds) {
            ExtractorUtil.checkContainerInput(mimeType == null, /* message= */
            null);
            Pair<@NullableType String, byte @NullableType []> mimeTypeAndInitializationDataBytes = parseEsdsFromParent(parent, childStartPosition);
            mimeType = mimeTypeAndInitializationDataBytes.first;
            @Nullable byte[] initializationDataBytes = mimeTypeAndInitializationDataBytes.second;
            if (initializationDataBytes != null) {
                initializationData = ImmutableList.of(initializationDataBytes);
            }
        } else if (childAtomType == Atom.TYPE_pasp) {
            pixelWidthHeightRatio = parsePaspFromParent(parent, childStartPosition);
            pixelWidthHeightRatioFromPasp = true;
        } else if (childAtomType == Atom.TYPE_sv3d) {
            projectionData = parseProjFromParent(parent, childStartPosition, childAtomSize);
        } else if (childAtomType == Atom.TYPE_st3d) {
            int version = parent.readUnsignedByte();
            // Flags.
            parent.skipBytes(3);
            if (version == 0) {
                int layout = parent.readUnsignedByte();
                switch(layout) {
                    case 0:
                        stereoMode = C.STEREO_MODE_MONO;
                        break;
                    case 1:
                        stereoMode = C.STEREO_MODE_TOP_BOTTOM;
                        break;
                    case 2:
                        stereoMode = C.STEREO_MODE_LEFT_RIGHT;
                        break;
                    case 3:
                        stereoMode = C.STEREO_MODE_STEREO_MESH;
                        break;
                    default:
                        break;
                }
            }
        } else if (childAtomType == Atom.TYPE_colr) {
            int colorType = parent.readInt();
            if (colorType == TYPE_nclx || colorType == TYPE_nclc) {
                // For more info on syntax, see Section 8.5.2.2 in ISO/IEC 14496-12:2012(E) and
                // https://developer.apple.com/library/archive/documentation/QuickTime/QTFF/QTFFChap3/qtff3.html.
                int colorPrimaries = parent.readUnsignedShort();
                int transferCharacteristics = parent.readUnsignedShort();
                // matrix_coefficients.
                parent.skipBytes(2);
                // Only try and read full_range_flag if the box is long enough. It should be present in
                // all colr boxes with type=nclx (Section 8.5.2.2 in ISO/IEC 14496-12:2012(E)) but some
                // device cameras record videos with type=nclx without this final flag (and therefore
                // size=18): https://github.com/google/ExoPlayer/issues/9332
                boolean fullRangeFlag = childAtomSize == 19 && (parent.readUnsignedByte() & 0b10000000) != 0;
                colorSpace = ColorInfo.isoColorPrimariesToColorSpace(colorPrimaries);
                colorRange = fullRangeFlag ? C.COLOR_RANGE_FULL : C.COLOR_RANGE_LIMITED;
                colorTransfer = ColorInfo.isoTransferCharacteristicsToColorTransfer(transferCharacteristics);
            } else {
                Log.w(TAG, "Unsupported color type: " + Atom.getAtomTypeString(colorType));
            }
        }
        childPosition += childAtomSize;
    }
    // If the media type was not recognized, ignore the track.
    if (mimeType == null) {
        return;
    }
    Format.Builder formatBuilder = new Format.Builder().setId(trackId).setSampleMimeType(mimeType).setCodecs(codecs).setWidth(width).setHeight(height).setPixelWidthHeightRatio(pixelWidthHeightRatio).setRotationDegrees(rotationDegrees).setProjectionData(projectionData).setStereoMode(stereoMode).setInitializationData(initializationData).setDrmInitData(drmInitData);
    if (colorSpace != Format.NO_VALUE || colorRange != Format.NO_VALUE || colorTransfer != Format.NO_VALUE || hdrStaticInfo != null) {
        // Note that if either mdcv or clli are missing, we leave the corresponding HDR static
        // metadata bytes with value zero. See [Internal ref: b/194535665].
        formatBuilder.setColorInfo(new ColorInfo(colorSpace, colorRange, colorTransfer, hdrStaticInfo != null ? hdrStaticInfo.array() : null));
    }
    out.format = formatBuilder.build();
}
Also used : DolbyVisionConfig(com.google.android.exoplayer2.video.DolbyVisionConfig) AvcConfig(com.google.android.exoplayer2.video.AvcConfig) ByteBuffer(java.nio.ByteBuffer) ColorInfo(com.google.android.exoplayer2.video.ColorInfo) HevcConfig(com.google.android.exoplayer2.video.HevcConfig) Format(com.google.android.exoplayer2.Format) Nullable(androidx.annotation.Nullable)

Example 57 with ParserException

use of com.google.android.exoplayer2.ParserException in project ExoPlayer by google.

the class FragmentedMp4Extractor method parseSidx.

/**
 * Parses a sidx atom (defined in 14496-12).
 *
 * @param atom The atom data.
 * @param inputPosition The input position of the first byte after the atom.
 * @return A pair consisting of the earliest presentation time in microseconds, and the parsed
 *     {@link ChunkIndex}.
 */
private static Pair<Long, ChunkIndex> parseSidx(ParsableByteArray atom, long inputPosition) throws ParserException {
    atom.setPosition(Atom.HEADER_SIZE);
    int fullAtom = atom.readInt();
    int version = Atom.parseFullAtomVersion(fullAtom);
    atom.skipBytes(4);
    long timescale = atom.readUnsignedInt();
    long earliestPresentationTime;
    long offset = inputPosition;
    if (version == 0) {
        earliestPresentationTime = atom.readUnsignedInt();
        offset += atom.readUnsignedInt();
    } else {
        earliestPresentationTime = atom.readUnsignedLongToLong();
        offset += atom.readUnsignedLongToLong();
    }
    long earliestPresentationTimeUs = Util.scaleLargeTimestamp(earliestPresentationTime, C.MICROS_PER_SECOND, timescale);
    atom.skipBytes(2);
    int referenceCount = atom.readUnsignedShort();
    int[] sizes = new int[referenceCount];
    long[] offsets = new long[referenceCount];
    long[] durationsUs = new long[referenceCount];
    long[] timesUs = new long[referenceCount];
    long time = earliestPresentationTime;
    long timeUs = earliestPresentationTimeUs;
    for (int i = 0; i < referenceCount; i++) {
        int firstInt = atom.readInt();
        int type = 0x80000000 & firstInt;
        if (type != 0) {
            throw ParserException.createForMalformedContainer("Unhandled indirect reference", /* cause= */
            null);
        }
        long referenceDuration = atom.readUnsignedInt();
        sizes[i] = 0x7FFFFFFF & firstInt;
        offsets[i] = offset;
        // Calculate time and duration values such that any rounding errors are consistent. i.e. That
        // timesUs[i] + durationsUs[i] == timesUs[i + 1].
        timesUs[i] = timeUs;
        time += referenceDuration;
        timeUs = Util.scaleLargeTimestamp(time, C.MICROS_PER_SECOND, timescale);
        durationsUs[i] = timeUs - timesUs[i];
        atom.skipBytes(4);
        offset += sizes[i];
    }
    return Pair.create(earliestPresentationTimeUs, new ChunkIndex(sizes, offsets, durationsUs, timesUs));
}
Also used : ChunkIndex(com.google.android.exoplayer2.extractor.ChunkIndex)

Example 58 with ParserException

use of com.google.android.exoplayer2.ParserException in project ExoPlayer by google.

the class FragmentedMp4Extractor method onMoovContainerAtomRead.

private void onMoovContainerAtomRead(ContainerAtom moov) throws ParserException {
    checkState(sideloadedTrack == null, "Unexpected moov box.");
    @Nullable DrmInitData drmInitData = getDrmInitDataFromAtoms(moov.leafChildren);
    // Read declaration of track fragments in the moov box.
    ContainerAtom mvex = checkNotNull(moov.getContainerAtomOfType(Atom.TYPE_mvex));
    SparseArray<DefaultSampleValues> defaultSampleValuesArray = new SparseArray<>();
    long duration = C.TIME_UNSET;
    int mvexChildrenSize = mvex.leafChildren.size();
    for (int i = 0; i < mvexChildrenSize; i++) {
        Atom.LeafAtom atom = mvex.leafChildren.get(i);
        if (atom.type == Atom.TYPE_trex) {
            Pair<Integer, DefaultSampleValues> trexData = parseTrex(atom.data);
            defaultSampleValuesArray.put(trexData.first, trexData.second);
        } else if (atom.type == Atom.TYPE_mehd) {
            duration = parseMehd(atom.data);
        }
    }
    // Construction of tracks and sample tables.
    List<TrackSampleTable> sampleTables = parseTraks(moov, new GaplessInfoHolder(), duration, drmInitData, /* ignoreEditLists= */
    (flags & FLAG_WORKAROUND_IGNORE_EDIT_LISTS) != 0, /* isQuickTime= */
    false, this::modifyTrack);
    int trackCount = sampleTables.size();
    if (trackBundles.size() == 0) {
        // We need to create the track bundles.
        for (int i = 0; i < trackCount; i++) {
            TrackSampleTable sampleTable = sampleTables.get(i);
            Track track = sampleTable.track;
            TrackBundle trackBundle = new TrackBundle(extractorOutput.track(i, track.type), sampleTable, getDefaultSampleValues(defaultSampleValuesArray, track.id));
            trackBundles.put(track.id, trackBundle);
            durationUs = max(durationUs, track.durationUs);
        }
        extractorOutput.endTracks();
    } else {
        checkState(trackBundles.size() == trackCount);
        for (int i = 0; i < trackCount; i++) {
            TrackSampleTable sampleTable = sampleTables.get(i);
            Track track = sampleTable.track;
            trackBundles.get(track.id).reset(sampleTable, getDefaultSampleValues(defaultSampleValuesArray, track.id));
        }
    }
}
Also used : ContainerAtom(com.google.android.exoplayer2.extractor.mp4.Atom.ContainerAtom) LeafAtom(com.google.android.exoplayer2.extractor.mp4.Atom.LeafAtom) ContainerAtom(com.google.android.exoplayer2.extractor.mp4.Atom.ContainerAtom) LeafAtom(com.google.android.exoplayer2.extractor.mp4.Atom.LeafAtom) SparseArray(android.util.SparseArray) DrmInitData(com.google.android.exoplayer2.drm.DrmInitData) GaplessInfoHolder(com.google.android.exoplayer2.extractor.GaplessInfoHolder) Nullable(androidx.annotation.Nullable)

Example 59 with ParserException

use of com.google.android.exoplayer2.ParserException in project ExoPlayer by google.

the class AdtsReader method parseAdtsHeader.

/**
 * Parses the sample header.
 */
@RequiresNonNull("output")
private void parseAdtsHeader() throws ParserException {
    adtsScratch.setPosition(0);
    if (!hasOutputFormat) {
        int audioObjectType = adtsScratch.readBits(2) + 1;
        if (audioObjectType != 2) {
            // The stream indicates AAC-Main (1), AAC-SSR (3) or AAC-LTP (4). When the stream indicates
            // AAC-Main it's more likely that the stream contains HE-AAC (5), which cannot be
            // represented correctly in the 2 bit audio_object_type field in the ADTS header. In
            // practice when the stream indicates AAC-SSR or AAC-LTP it more commonly contains AAC-LC or
            // HE-AAC. Since most Android devices don't support AAC-Main, AAC-SSR or AAC-LTP, and since
            // indicating AAC-LC works for HE-AAC streams, we pretend that we're dealing with AAC-LC and
            // hope for the best. In practice this often works.
            // See: https://github.com/google/ExoPlayer/issues/774
            // See: https://github.com/google/ExoPlayer/issues/1383
            Log.w(TAG, "Detected audio object type: " + audioObjectType + ", but assuming AAC LC.");
            audioObjectType = 2;
        }
        adtsScratch.skipBits(5);
        int channelConfig = adtsScratch.readBits(3);
        byte[] audioSpecificConfig = AacUtil.buildAudioSpecificConfig(audioObjectType, firstFrameSampleRateIndex, channelConfig);
        AacUtil.Config aacConfig = AacUtil.parseAudioSpecificConfig(audioSpecificConfig);
        Format format = new Format.Builder().setId(formatId).setSampleMimeType(MimeTypes.AUDIO_AAC).setCodecs(aacConfig.codecs).setChannelCount(aacConfig.channelCount).setSampleRate(aacConfig.sampleRateHz).setInitializationData(Collections.singletonList(audioSpecificConfig)).setLanguage(language).build();
        // In this class a sample is an access unit, but the MediaFormat sample rate specifies the
        // number of PCM audio samples per second.
        sampleDurationUs = (C.MICROS_PER_SECOND * 1024) / format.sampleRate;
        output.format(format);
        hasOutputFormat = true;
    } else {
        adtsScratch.skipBits(10);
    }
    adtsScratch.skipBits(4);
    int sampleSize = adtsScratch.readBits(13) - 2 - /* the sync word */
    HEADER_SIZE;
    if (hasCrc) {
        sampleSize -= CRC_SIZE;
    }
    setReadingSampleState(output, sampleDurationUs, 0, sampleSize);
}
Also used : AacUtil(com.google.android.exoplayer2.audio.AacUtil) Format(com.google.android.exoplayer2.Format) RequiresNonNull(org.checkerframework.checker.nullness.qual.RequiresNonNull)

Example 60 with ParserException

use of com.google.android.exoplayer2.ParserException in project ExoPlayer by google.

the class LatmReader method parseStreamMuxConfig.

/**
 * Parses a StreamMuxConfig as defined in ISO/IEC 14496-3:2009 Section 1.7.3.1, Table 1.42.
 */
@RequiresNonNull("output")
private void parseStreamMuxConfig(ParsableBitArray data) throws ParserException {
    int audioMuxVersion = data.readBits(1);
    audioMuxVersionA = audioMuxVersion == 1 ? data.readBits(1) : 0;
    if (audioMuxVersionA == 0) {
        if (audioMuxVersion == 1) {
            // Skip taraBufferFullness.
            latmGetValue(data);
        }
        if (!data.readBit()) {
            throw ParserException.createForMalformedContainer(/* message= */
            null, /* cause= */
            null);
        }
        numSubframes = data.readBits(6);
        int numProgram = data.readBits(4);
        int numLayer = data.readBits(3);
        if (numProgram != 0 || numLayer != 0) {
            throw ParserException.createForMalformedContainer(/* message= */
            null, /* cause= */
            null);
        }
        if (audioMuxVersion == 0) {
            int startPosition = data.getPosition();
            int readBits = parseAudioSpecificConfig(data);
            data.setPosition(startPosition);
            byte[] initData = new byte[(readBits + 7) / 8];
            data.readBits(initData, 0, readBits);
            Format format = new Format.Builder().setId(formatId).setSampleMimeType(MimeTypes.AUDIO_AAC).setCodecs(codecs).setChannelCount(channelCount).setSampleRate(sampleRateHz).setInitializationData(Collections.singletonList(initData)).setLanguage(language).build();
            if (!format.equals(this.format)) {
                this.format = format;
                sampleDurationUs = (C.MICROS_PER_SECOND * 1024) / format.sampleRate;
                output.format(format);
            }
        } else {
            int ascLen = (int) latmGetValue(data);
            int bitsRead = parseAudioSpecificConfig(data);
            // fillBits.
            data.skipBits(ascLen - bitsRead);
        }
        parseFrameLength(data);
        otherDataPresent = data.readBit();
        otherDataLenBits = 0;
        if (otherDataPresent) {
            if (audioMuxVersion == 1) {
                otherDataLenBits = latmGetValue(data);
            } else {
                boolean otherDataLenEsc;
                do {
                    otherDataLenEsc = data.readBit();
                    otherDataLenBits = (otherDataLenBits << 8) + data.readBits(8);
                } while (otherDataLenEsc);
            }
        }
        boolean crcCheckPresent = data.readBit();
        if (crcCheckPresent) {
            // crcCheckSum.
            data.skipBits(8);
        }
    } else {
        // This is not defined by ISO/IEC 14496-3:2009.
        throw ParserException.createForMalformedContainer(/* message= */
        null, /* cause= */
        null);
    }
}
Also used : Format(com.google.android.exoplayer2.Format) RequiresNonNull(org.checkerframework.checker.nullness.qual.RequiresNonNull)

Aggregations

ParsableByteArray (com.google.android.exoplayer2.util.ParsableByteArray)25 Test (org.junit.Test)25 ParserException (com.google.android.exoplayer2.ParserException)21 MediaItem (com.google.android.exoplayer2.MediaItem)13 Timeline (com.google.android.exoplayer2.Timeline)13 Nullable (androidx.annotation.Nullable)12 Format (com.google.android.exoplayer2.Format)7 PositionHolder (com.google.android.exoplayer2.extractor.PositionHolder)5 LeafAtom (com.google.android.exoplayer2.extractor.mp4.Atom.LeafAtom)5 FakeExtractorInput (com.google.android.exoplayer2.testutil.FakeExtractorInput)5 ArrayList (java.util.ArrayList)5 Uri (android.net.Uri)3 DrmInitData (com.google.android.exoplayer2.drm.DrmInitData)3 ContainerAtom (com.google.android.exoplayer2.extractor.mp4.Atom.ContainerAtom)3 RequiresNonNull (org.checkerframework.checker.nullness.qual.RequiresNonNull)3 CallSuper (androidx.annotation.CallSuper)2 AacUtil (com.google.android.exoplayer2.audio.AacUtil)2 SchemeData (com.google.android.exoplayer2.drm.DrmInitData.SchemeData)2 GaplessInfoHolder (com.google.android.exoplayer2.extractor.GaplessInfoHolder)2 AvcConfig (com.google.android.exoplayer2.video.AvcConfig)2