Search in sources :

Example 1 with ContainerAtom

use of com.google.android.exoplayer.parser.mp4.Atom.ContainerAtom in project edx-app-android by edx.

the class FragmentedMp4Extractor method parseTraf.

/**
 * Parses a traf atom (defined in 14496-12).
 */
private static void parseTraf(Track track, DefaultSampleValues extendsDefaults, ContainerAtom traf, TrackFragment out, int workaroundFlags, byte[] extendedTypeScratch) {
    LeafAtom tfdtAtom = traf.getLeafAtomOfType(Atom.TYPE_tfdt);
    long decodeTime = tfdtAtom == null ? 0 : parseTfdt(traf.getLeafAtomOfType(Atom.TYPE_tfdt).data);
    LeafAtom tfhd = traf.getLeafAtomOfType(Atom.TYPE_tfhd);
    DefaultSampleValues fragmentHeader = parseTfhd(extendsDefaults, tfhd.data);
    out.sampleDescriptionIndex = fragmentHeader.sampleDescriptionIndex;
    LeafAtom trun = traf.getLeafAtomOfType(Atom.TYPE_trun);
    parseTrun(track, fragmentHeader, decodeTime, workaroundFlags, trun.data, out);
    LeafAtom saiz = traf.getLeafAtomOfType(Atom.TYPE_saiz);
    if (saiz != null) {
        TrackEncryptionBox trackEncryptionBox = track.sampleDescriptionEncryptionBoxes[fragmentHeader.sampleDescriptionIndex];
        parseSaiz(trackEncryptionBox, saiz.data, out);
    }
    LeafAtom senc = traf.getLeafAtomOfType(Atom.TYPE_senc);
    if (senc != null) {
        parseSenc(senc.data, out);
    }
    LeafAtom uuid = traf.getLeafAtomOfType(Atom.TYPE_uuid);
    if (uuid != null) {
        parseUuid(uuid.data, out, extendedTypeScratch);
    }
}
Also used : LeafAtom(com.google.android.exoplayer.parser.mp4.Atom.LeafAtom)

Example 2 with ContainerAtom

use of com.google.android.exoplayer.parser.mp4.Atom.ContainerAtom in project ExoPlayer by google.

the class FragmentedMp4Extractor method readAtomHeader.

private boolean readAtomHeader(ExtractorInput input) throws IOException {
    if (atomHeaderBytesRead == 0) {
        // Read the standard length atom header.
        if (!input.readFully(atomHeader.getData(), 0, Atom.HEADER_SIZE, true)) {
            return false;
        }
        atomHeaderBytesRead = Atom.HEADER_SIZE;
        atomHeader.setPosition(0);
        atomSize = atomHeader.readUnsignedInt();
        atomType = atomHeader.readInt();
    }
    if (atomSize == Atom.DEFINES_LARGE_SIZE) {
        // Read the large size.
        int headerBytesRemaining = Atom.LONG_HEADER_SIZE - Atom.HEADER_SIZE;
        input.readFully(atomHeader.getData(), Atom.HEADER_SIZE, headerBytesRemaining);
        atomHeaderBytesRead += headerBytesRemaining;
        atomSize = atomHeader.readUnsignedLongToLong();
    } else if (atomSize == Atom.EXTENDS_TO_END_SIZE) {
        // The atom extends to the end of the file. Note that if the atom is within a container we can
        // work out its size even if the input length is unknown.
        long endPosition = input.getLength();
        if (endPosition == C.LENGTH_UNSET && !containerAtoms.isEmpty()) {
            endPosition = containerAtoms.peek().endPosition;
        }
        if (endPosition != C.LENGTH_UNSET) {
            atomSize = endPosition - input.getPosition() + atomHeaderBytesRead;
        }
    }
    if (atomSize < atomHeaderBytesRead) {
        throw ParserException.createForUnsupportedContainerFeature("Atom size less than header length (unsupported).");
    }
    long atomPosition = input.getPosition() - atomHeaderBytesRead;
    if (atomType == Atom.TYPE_moof || atomType == Atom.TYPE_mdat) {
        if (!haveOutputSeekMap) {
            // This must be the first moof or mdat in the stream.
            extractorOutput.seekMap(new SeekMap.Unseekable(durationUs, atomPosition));
            haveOutputSeekMap = true;
        }
    }
    if (atomType == Atom.TYPE_moof) {
        // The data positions may be updated when parsing the tfhd/trun.
        int trackCount = trackBundles.size();
        for (int i = 0; i < trackCount; i++) {
            TrackFragment fragment = trackBundles.valueAt(i).fragment;
            fragment.atomPosition = atomPosition;
            fragment.auxiliaryDataPosition = atomPosition;
            fragment.dataPosition = atomPosition;
        }
    }
    if (atomType == Atom.TYPE_mdat) {
        currentTrackBundle = null;
        endOfMdatPosition = atomPosition + atomSize;
        parserState = STATE_READING_ENCRYPTION_DATA;
        return true;
    }
    if (shouldParseContainerAtom(atomType)) {
        long endPosition = input.getPosition() + atomSize - Atom.HEADER_SIZE;
        containerAtoms.push(new ContainerAtom(atomType, endPosition));
        if (atomSize == atomHeaderBytesRead) {
            processAtomEnded(endPosition);
        } else {
            // Start reading the first child atom.
            enterReadingAtomHeaderState();
        }
    } else if (shouldParseLeafAtom(atomType)) {
        if (atomHeaderBytesRead != Atom.HEADER_SIZE) {
            throw ParserException.createForUnsupportedContainerFeature("Leaf atom defines extended atom size (unsupported).");
        }
        if (atomSize > Integer.MAX_VALUE) {
            throw ParserException.createForUnsupportedContainerFeature("Leaf atom with length > 2147483647 (unsupported).");
        }
        ParsableByteArray atomData = new ParsableByteArray((int) atomSize);
        System.arraycopy(atomHeader.getData(), 0, atomData.getData(), 0, Atom.HEADER_SIZE);
        this.atomData = atomData;
        parserState = STATE_READING_ATOM_PAYLOAD;
    } else {
        if (atomSize > Integer.MAX_VALUE) {
            throw ParserException.createForUnsupportedContainerFeature("Skipping atom with length > 2147483647 (unsupported).");
        }
        atomData = null;
        parserState = STATE_READING_ATOM_PAYLOAD;
    }
    return true;
}
Also used : ParsableByteArray(com.google.android.exoplayer2.util.ParsableByteArray) ContainerAtom(com.google.android.exoplayer2.extractor.mp4.Atom.ContainerAtom) SeekMap(com.google.android.exoplayer2.extractor.SeekMap)

Example 3 with ContainerAtom

use of com.google.android.exoplayer.parser.mp4.Atom.ContainerAtom in project ExoPlayer by google.

the class Mp4Extractor method readAtomHeader.

private boolean readAtomHeader(ExtractorInput input) throws IOException {
    if (atomHeaderBytesRead == 0) {
        // Read the standard length atom header.
        if (!input.readFully(atomHeader.getData(), 0, Atom.HEADER_SIZE, true)) {
            processEndOfStreamReadingAtomHeader();
            return false;
        }
        atomHeaderBytesRead = Atom.HEADER_SIZE;
        atomHeader.setPosition(0);
        atomSize = atomHeader.readUnsignedInt();
        atomType = atomHeader.readInt();
    }
    if (atomSize == Atom.DEFINES_LARGE_SIZE) {
        // Read the large size.
        int headerBytesRemaining = Atom.LONG_HEADER_SIZE - Atom.HEADER_SIZE;
        input.readFully(atomHeader.getData(), Atom.HEADER_SIZE, headerBytesRemaining);
        atomHeaderBytesRead += headerBytesRemaining;
        atomSize = atomHeader.readUnsignedLongToLong();
    } else if (atomSize == Atom.EXTENDS_TO_END_SIZE) {
        // The atom extends to the end of the file. Note that if the atom is within a container we can
        // work out its size even if the input length is unknown.
        long endPosition = input.getLength();
        if (endPosition == C.LENGTH_UNSET) {
            @Nullable ContainerAtom containerAtom = containerAtoms.peek();
            if (containerAtom != null) {
                endPosition = containerAtom.endPosition;
            }
        }
        if (endPosition != C.LENGTH_UNSET) {
            atomSize = endPosition - input.getPosition() + atomHeaderBytesRead;
        }
    }
    if (atomSize < atomHeaderBytesRead) {
        throw ParserException.createForUnsupportedContainerFeature("Atom size less than header length (unsupported).");
    }
    if (shouldParseContainerAtom(atomType)) {
        long endPosition = input.getPosition() + atomSize - atomHeaderBytesRead;
        if (atomSize != atomHeaderBytesRead && atomType == Atom.TYPE_meta) {
            maybeSkipRemainingMetaAtomHeaderBytes(input);
        }
        containerAtoms.push(new ContainerAtom(atomType, endPosition));
        if (atomSize == atomHeaderBytesRead) {
            processAtomEnded(endPosition);
        } else {
            // Start reading the first child atom.
            enterReadingAtomHeaderState();
        }
    } else if (shouldParseLeafAtom(atomType)) {
        // We don't support parsing of leaf atoms that define extended atom sizes, or that have
        // lengths greater than Integer.MAX_VALUE.
        Assertions.checkState(atomHeaderBytesRead == Atom.HEADER_SIZE);
        Assertions.checkState(atomSize <= Integer.MAX_VALUE);
        ParsableByteArray atomData = new ParsableByteArray((int) atomSize);
        System.arraycopy(atomHeader.getData(), 0, atomData.getData(), 0, Atom.HEADER_SIZE);
        this.atomData = atomData;
        parserState = STATE_READING_ATOM_PAYLOAD;
    } else {
        processUnparsedAtom(input.getPosition() - atomHeaderBytesRead);
        atomData = null;
        parserState = STATE_READING_ATOM_PAYLOAD;
    }
    return true;
}
Also used : ParsableByteArray(com.google.android.exoplayer2.util.ParsableByteArray) ContainerAtom(com.google.android.exoplayer2.extractor.mp4.Atom.ContainerAtom) SeekPoint(com.google.android.exoplayer2.extractor.SeekPoint)

Example 4 with ContainerAtom

use of com.google.android.exoplayer.parser.mp4.Atom.ContainerAtom in project ExoPlayer by google.

the class Mp4Extractor method processMoovAtom.

/**
 * Updates the stored track metadata to reflect the contents of the specified moov atom.
 */
private void processMoovAtom(ContainerAtom moov) throws ParserException {
    int firstVideoTrackIndex = C.INDEX_UNSET;
    long durationUs = C.TIME_UNSET;
    List<Mp4Track> tracks = new ArrayList<>();
    // Process metadata.
    @Nullable Metadata udtaMetaMetadata = null;
    @Nullable Metadata smtaMetadata = null;
    boolean isQuickTime = fileType == FILE_TYPE_QUICKTIME;
    GaplessInfoHolder gaplessInfoHolder = new GaplessInfoHolder();
    @Nullable Atom.LeafAtom udta = moov.getLeafAtomOfType(Atom.TYPE_udta);
    if (udta != null) {
        Pair<@NullableType Metadata, @NullableType Metadata> udtaMetadata = AtomParsers.parseUdta(udta);
        udtaMetaMetadata = udtaMetadata.first;
        smtaMetadata = udtaMetadata.second;
        if (udtaMetaMetadata != null) {
            gaplessInfoHolder.setFromMetadata(udtaMetaMetadata);
        }
    }
    @Nullable Metadata mdtaMetadata = null;
    @Nullable Atom.ContainerAtom meta = moov.getContainerAtomOfType(Atom.TYPE_meta);
    if (meta != null) {
        mdtaMetadata = AtomParsers.parseMdtaFromMeta(meta);
    }
    boolean ignoreEditLists = (flags & FLAG_WORKAROUND_IGNORE_EDIT_LISTS) != 0;
    List<TrackSampleTable> trackSampleTables = parseTraks(moov, gaplessInfoHolder, /* duration= */
    C.TIME_UNSET, /* drmInitData= */
    null, ignoreEditLists, isQuickTime, /* modifyTrackFunction= */
    track -> track);
    ExtractorOutput extractorOutput = checkNotNull(this.extractorOutput);
    int trackCount = trackSampleTables.size();
    for (int i = 0; i < trackCount; i++) {
        TrackSampleTable trackSampleTable = trackSampleTables.get(i);
        if (trackSampleTable.sampleCount == 0) {
            continue;
        }
        Track track = trackSampleTable.track;
        long trackDurationUs = track.durationUs != C.TIME_UNSET ? track.durationUs : trackSampleTable.durationUs;
        durationUs = max(durationUs, trackDurationUs);
        Mp4Track mp4Track = new Mp4Track(track, trackSampleTable, extractorOutput.track(i, track.type));
        int maxInputSize;
        if (MimeTypes.AUDIO_TRUEHD.equals(track.format.sampleMimeType)) {
            // TrueHD groups samples per chunks of TRUEHD_RECHUNK_SAMPLE_COUNT samples.
            maxInputSize = trackSampleTable.maximumSize * Ac3Util.TRUEHD_RECHUNK_SAMPLE_COUNT;
        } else {
            // Each sample has up to three bytes of overhead for the start code that replaces its
            // length. Allow ten source samples per output sample, like the platform extractor.
            maxInputSize = trackSampleTable.maximumSize + 3 * 10;
        }
        Format.Builder formatBuilder = track.format.buildUpon();
        formatBuilder.setMaxInputSize(maxInputSize);
        if (track.type == C.TRACK_TYPE_VIDEO && trackDurationUs > 0 && trackSampleTable.sampleCount > 1) {
            float frameRate = trackSampleTable.sampleCount / (trackDurationUs / 1000000f);
            formatBuilder.setFrameRate(frameRate);
        }
        MetadataUtil.setFormatGaplessInfo(track.type, gaplessInfoHolder, formatBuilder);
        MetadataUtil.setFormatMetadata(track.type, udtaMetaMetadata, mdtaMetadata, formatBuilder, smtaMetadata, slowMotionMetadataEntries.isEmpty() ? null : new Metadata(slowMotionMetadataEntries));
        mp4Track.trackOutput.format(formatBuilder.build());
        if (track.type == C.TRACK_TYPE_VIDEO && firstVideoTrackIndex == C.INDEX_UNSET) {
            firstVideoTrackIndex = tracks.size();
        }
        tracks.add(mp4Track);
    }
    this.firstVideoTrackIndex = firstVideoTrackIndex;
    this.durationUs = durationUs;
    this.tracks = tracks.toArray(new Mp4Track[0]);
    accumulatedSampleSizes = calculateAccumulatedSampleSizes(this.tracks);
    extractorOutput.endTracks();
    extractorOutput.seekMap(this);
}
Also used : ExtractorOutput(com.google.android.exoplayer2.extractor.ExtractorOutput) ArrayList(java.util.ArrayList) MotionPhotoMetadata(com.google.android.exoplayer2.metadata.mp4.MotionPhotoMetadata) Metadata(com.google.android.exoplayer2.metadata.Metadata) ContainerAtom(com.google.android.exoplayer2.extractor.mp4.Atom.ContainerAtom) SeekPoint(com.google.android.exoplayer2.extractor.SeekPoint) ContainerAtom(com.google.android.exoplayer2.extractor.mp4.Atom.ContainerAtom) Format(com.google.android.exoplayer2.Format) GaplessInfoHolder(com.google.android.exoplayer2.extractor.GaplessInfoHolder) Nullable(androidx.annotation.Nullable)

Example 5 with ContainerAtom

use of com.google.android.exoplayer.parser.mp4.Atom.ContainerAtom in project ExoPlayer by google.

the class Mp4Extractor method processAtomEnded.

private void processAtomEnded(long atomEndPosition) throws ParserException {
    while (!containerAtoms.isEmpty() && containerAtoms.peek().endPosition == atomEndPosition) {
        Atom.ContainerAtom containerAtom = containerAtoms.pop();
        if (containerAtom.type == Atom.TYPE_moov) {
            // We've reached the end of the moov atom. Process it and prepare to read samples.
            processMoovAtom(containerAtom);
            containerAtoms.clear();
            parserState = STATE_READING_SAMPLE;
        } else if (!containerAtoms.isEmpty()) {
            containerAtoms.peek().add(containerAtom);
        }
    }
    if (parserState != STATE_READING_SAMPLE) {
        enterReadingAtomHeaderState();
    }
}
Also used : ContainerAtom(com.google.android.exoplayer2.extractor.mp4.Atom.ContainerAtom) ContainerAtom(com.google.android.exoplayer2.extractor.mp4.Atom.ContainerAtom)

Aggregations

ContainerAtom (com.google.android.exoplayer2.extractor.mp4.Atom.ContainerAtom)5 SuppressLint (android.annotation.SuppressLint)3 ContainerAtom (com.google.android.exoplayer.parser.mp4.Atom.ContainerAtom)3 Nullable (androidx.annotation.Nullable)2 LeafAtom (com.google.android.exoplayer.parser.mp4.Atom.LeafAtom)2 GaplessInfoHolder (com.google.android.exoplayer2.extractor.GaplessInfoHolder)2 SeekPoint (com.google.android.exoplayer2.extractor.SeekPoint)2 ParsableByteArray (com.google.android.exoplayer2.util.ParsableByteArray)2 SparseArray (android.util.SparseArray)1 MediaFormat (com.google.android.exoplayer.MediaFormat)1 Format (com.google.android.exoplayer2.Format)1 DrmInitData (com.google.android.exoplayer2.drm.DrmInitData)1 ExtractorOutput (com.google.android.exoplayer2.extractor.ExtractorOutput)1 SeekMap (com.google.android.exoplayer2.extractor.SeekMap)1 LeafAtom (com.google.android.exoplayer2.extractor.mp4.Atom.LeafAtom)1 Metadata (com.google.android.exoplayer2.metadata.Metadata)1 MotionPhotoMetadata (com.google.android.exoplayer2.metadata.mp4.MotionPhotoMetadata)1 ArrayList (java.util.ArrayList)1 UUID (java.util.UUID)1