Search in sources :

Example 41 with ExtractorOutput

use of com.google.android.exoplayer2.extractor.ExtractorOutput in project ExoPlayer by google.

the class FlacExtractorSeekTest method seeking_binarySearch_handlesSeekToZero.

@Test
public void seeking_binarySearch_handlesSeekToZero() throws IOException {
    String fileName = TEST_FILE_BINARY_SEARCH;
    Uri fileUri = TestUtil.buildAssetUri(fileName);
    SeekMap seekMap = TestUtil.extractSeekMap(extractor, extractorOutput, dataSource, fileUri);
    FakeTrackOutput trackOutput = extractorOutput.trackOutputs.get(0);
    long targetSeekTimeUs = 0;
    int extractedFrameIndex = TestUtil.seekToTimeUs(extractor, seekMap, targetSeekTimeUs, dataSource, trackOutput, fileUri);
    assertThat(extractedFrameIndex).isNotEqualTo(C.INDEX_UNSET);
    assertFirstFrameAfterSeekContainsTargetSeekTime(fileName, trackOutput, targetSeekTimeUs, extractedFrameIndex);
}
Also used : FakeTrackOutput(com.google.android.exoplayer2.testutil.FakeTrackOutput) SeekMap(com.google.android.exoplayer2.extractor.SeekMap) Uri(android.net.Uri) Test(org.junit.Test)

Example 42 with ExtractorOutput

use of com.google.android.exoplayer2.extractor.ExtractorOutput in project ExoPlayer by google.

the class FlvExtractorSeekTest method seeking_handlesSeekToEof.

@Test
public void seeking_handlesSeekToEof() throws Exception {
    String fileName = TEST_FILE_KEY_FRAME_INDEX;
    Uri fileUri = TestUtil.buildAssetUri(fileName);
    SeekMap seekMap = TestUtil.extractSeekMap(extractor, extractorOutput, dataSource, fileUri);
    int trackId = extractorOutput.trackOutputs.keyAt(0);
    FakeTrackOutput trackOutput = extractorOutput.trackOutputs.get(trackId);
    long targetSeekTimeUs = seekMap.getDurationUs();
    int extractedFrameIndex = TestUtil.seekToTimeUs(extractor, seekMap, targetSeekTimeUs, dataSource, trackOutput, fileUri);
    assertThat(extractedFrameIndex).isNotEqualTo(C.INDEX_UNSET);
    assertFirstFrameAfterSeekIsWithinKeyFrameInterval(fileName, trackId, trackOutput, extractedFrameIndex, targetSeekTimeUs);
}
Also used : FakeTrackOutput(com.google.android.exoplayer2.testutil.FakeTrackOutput) SeekMap(com.google.android.exoplayer2.extractor.SeekMap) Uri(android.net.Uri) Test(org.junit.Test)

Example 43 with ExtractorOutput

use of com.google.android.exoplayer2.extractor.ExtractorOutput in project ExoPlayer by google.

the class FlvExtractorSeekTest method seeking_handlesSeekingForward.

@Test
public void seeking_handlesSeekingForward() throws Exception {
    String fileName = TEST_FILE_KEY_FRAME_INDEX;
    Uri fileUri = TestUtil.buildAssetUri(fileName);
    SeekMap seekMap = TestUtil.extractSeekMap(extractor, extractorOutput, dataSource, fileUri);
    int trackId = extractorOutput.trackOutputs.keyAt(0);
    FakeTrackOutput trackOutput = extractorOutput.trackOutputs.get(trackId);
    long firstSeekTimeUs = seekMap.getDurationUs() / 3;
    TestUtil.seekToTimeUs(extractor, seekMap, firstSeekTimeUs, dataSource, trackOutput, fileUri);
    long targetSeekTimeUs = seekMap.getDurationUs() * 2 / 3;
    int extractedFrameIndex = TestUtil.seekToTimeUs(extractor, seekMap, targetSeekTimeUs, dataSource, trackOutput, fileUri);
    assertThat(extractedFrameIndex).isNotEqualTo(C.INDEX_UNSET);
    assertFirstFrameAfterSeekIsWithinKeyFrameInterval(fileName, trackId, trackOutput, extractedFrameIndex, targetSeekTimeUs);
}
Also used : FakeTrackOutput(com.google.android.exoplayer2.testutil.FakeTrackOutput) SeekMap(com.google.android.exoplayer2.extractor.SeekMap) Uri(android.net.Uri) Test(org.junit.Test)

Example 44 with ExtractorOutput

use of com.google.android.exoplayer2.extractor.ExtractorOutput 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 45 with ExtractorOutput

use of com.google.android.exoplayer2.extractor.ExtractorOutput in project ExoPlayer by google.

the class MatroskaExtractor method endMasterElement.

/**
 * Called when the end of a master element is encountered.
 *
 * @see EbmlProcessor#endMasterElement(int)
 */
@CallSuper
protected void endMasterElement(int id) throws ParserException {
    assertInitialized();
    switch(id) {
        case ID_SEGMENT_INFO:
            if (timecodeScale == C.TIME_UNSET) {
                // timecodeScale was omitted. Use the default value.
                timecodeScale = 1000000;
            }
            if (durationTimecode != C.TIME_UNSET) {
                durationUs = scaleTimecodeToUs(durationTimecode);
            }
            break;
        case ID_SEEK:
            if (seekEntryId == UNSET_ENTRY_ID || seekEntryPosition == C.POSITION_UNSET) {
                throw ParserException.createForMalformedContainer("Mandatory element SeekID or SeekPosition not found", /* cause= */
                null);
            }
            if (seekEntryId == ID_CUES) {
                cuesContentPosition = seekEntryPosition;
            }
            break;
        case ID_CUES:
            if (!sentSeekMap) {
                extractorOutput.seekMap(buildSeekMap(cueTimesUs, cueClusterPositions));
                sentSeekMap = true;
            } else {
            // We have already built the cues. Ignore.
            }
            this.cueTimesUs = null;
            this.cueClusterPositions = null;
            break;
        case ID_BLOCK_GROUP:
            if (blockState != BLOCK_STATE_DATA) {
                // We've skipped this block (due to incompatible track number).
                return;
            }
            // Commit sample metadata.
            int sampleOffset = 0;
            for (int i = 0; i < blockSampleCount; i++) {
                sampleOffset += blockSampleSizes[i];
            }
            Track track = tracks.get(blockTrackNumber);
            track.assertOutputInitialized();
            for (int i = 0; i < blockSampleCount; i++) {
                long sampleTimeUs = blockTimeUs + (i * track.defaultSampleDurationNs) / 1000;
                int sampleFlags = blockFlags;
                if (i == 0 && !blockHasReferenceBlock) {
                    // If the ReferenceBlock element was not found in this block, then the first frame is a
                    // keyframe.
                    sampleFlags |= C.BUFFER_FLAG_KEY_FRAME;
                }
                int sampleSize = blockSampleSizes[i];
                // The offset is to the end of the sample.
                sampleOffset -= sampleSize;
                commitSampleToOutput(track, sampleTimeUs, sampleFlags, sampleSize, sampleOffset);
            }
            blockState = BLOCK_STATE_START;
            break;
        case ID_CONTENT_ENCODING:
            assertInTrackEntry(id);
            if (currentTrack.hasContentEncryption) {
                if (currentTrack.cryptoData == null) {
                    throw ParserException.createForMalformedContainer("Encrypted Track found but ContentEncKeyID was not found", /* cause= */
                    null);
                }
                currentTrack.drmInitData = new DrmInitData(new SchemeData(C.UUID_NIL, MimeTypes.VIDEO_WEBM, currentTrack.cryptoData.encryptionKey));
            }
            break;
        case ID_CONTENT_ENCODINGS:
            assertInTrackEntry(id);
            if (currentTrack.hasContentEncryption && currentTrack.sampleStrippedBytes != null) {
                throw ParserException.createForMalformedContainer("Combining encryption and compression is not supported", /* cause= */
                null);
            }
            break;
        case ID_TRACK_ENTRY:
            Track currentTrack = checkStateNotNull(this.currentTrack);
            if (currentTrack.codecId == null) {
                throw ParserException.createForMalformedContainer("CodecId is missing in TrackEntry element", /* cause= */
                null);
            } else {
                if (isCodecSupported(currentTrack.codecId)) {
                    currentTrack.initializeOutput(extractorOutput, currentTrack.number);
                    tracks.put(currentTrack.number, currentTrack);
                }
            }
            this.currentTrack = null;
            break;
        case ID_TRACKS:
            if (tracks.size() == 0) {
                throw ParserException.createForMalformedContainer("No valid tracks were found", /* cause= */
                null);
            }
            extractorOutput.endTracks();
            break;
        default:
            break;
    }
}
Also used : DrmInitData(com.google.android.exoplayer2.drm.DrmInitData) SchemeData(com.google.android.exoplayer2.drm.DrmInitData.SchemeData) CallSuper(androidx.annotation.CallSuper)

Aggregations

SeekMap (com.google.android.exoplayer2.extractor.SeekMap)79 Test (org.junit.Test)66 Uri (android.net.Uri)59 FakeTrackOutput (com.google.android.exoplayer2.testutil.FakeTrackOutput)57 FakeExtractorOutput (com.google.android.exoplayer2.testutil.FakeExtractorOutput)31 Nullable (androidx.annotation.Nullable)12 TrackOutput (com.google.android.exoplayer2.extractor.TrackOutput)9 Format (com.google.android.exoplayer2.Format)6 TrackIdGenerator (com.google.android.exoplayer2.extractor.ts.TsPayloadReader.TrackIdGenerator)6 Metadata (com.google.android.exoplayer2.metadata.Metadata)6 RequiresNonNull (org.checkerframework.checker.nullness.qual.RequiresNonNull)5 Extractor (com.google.android.exoplayer2.extractor.Extractor)4 OutputFrameHolder (com.google.android.exoplayer2.ext.flac.FlacBinarySearchSeeker.OutputFrameHolder)3 ExtractorInput (com.google.android.exoplayer2.extractor.ExtractorInput)3 ExtractorOutput (com.google.android.exoplayer2.extractor.ExtractorOutput)3 Mp3Extractor (com.google.android.exoplayer2.extractor.mp3.Mp3Extractor)3 Before (org.junit.Before)3 CallSuper (androidx.annotation.CallSuper)2 Format (androidx.media3.common.Format)2 Metadata (androidx.media3.common.Metadata)2