Search in sources :

Example 96 with Extractor

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

the class ExtractorAsserts method assertBehavior.

/**
 * Asserts that an extractor consumes valid input data successfully successfully under the
 * conditions specified by {@code simulationConfig}.
 *
 * <p>The output of the extractor is compared against prerecorded dump files.
 *
 * @param assertionConfig Details of how to read and process the source and dump files.
 * @param simulationConfig Details on the environment to simulate and behaviours to assert.
 * @throws IOException If reading from the input fails.
 */
public static void assertBehavior(ExtractorFactory factory, String file, AssertionConfig assertionConfig, SimulationConfig simulationConfig) throws IOException {
    // Check behavior prior to initialization.
    Extractor extractor = factory.create();
    extractor.seek(0, 0);
    extractor.release();
    // Assert output.
    Context context = ApplicationProvider.getApplicationContext();
    byte[] fileData = TestUtil.getByteArray(context, file);
    String dumpFilesPrefix;
    if (assertionConfig.dumpFilesPrefix != null) {
        dumpFilesPrefix = assertionConfig.dumpFilesPrefix;
    } else {
        String[] path = file.split("/");
        checkState(path.length > 0 && path[0].equals("media"), "AssertionConfig.dumpFilesPrefix == null but file isn't in a media/ sub-directory.\n" + "Expected : 'media/<path-to-file>'\n" + "Found    : '" + file + "'\n" + "You need to set AssertionConfig.dumpFilesPrefix explicitly if your media and dump" + " file aren't located in the expected structure (see docs on" + " AssertionConfig.dumpFilesPrefix)");
        path[0] = "extractordumps";
        dumpFilesPrefix = Joiner.on('/').join(path);
    }
    assertOutput(factory.create(), dumpFilesPrefix, fileData, context, assertionConfig.deduplicateConsecutiveFormats, simulationConfig.sniffFirst, simulationConfig.simulateIOErrors, simulationConfig.simulateUnknownLength, simulationConfig.simulatePartialReads);
}
Also used : Context(android.content.Context) Extractor(com.google.android.exoplayer2.extractor.Extractor)

Example 97 with Extractor

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

the class ExtractorAsserts method assertOutput.

/**
 * Asserts that an extractor consumes valid input data successfully under the specified
 * conditions.
 *
 * @param extractor The {@link Extractor} to be tested.
 * @param dumpFilesPrefix The dump files prefix prepended to the dump files path.
 * @param data Content of the input file.
 * @param context To be used to load the sample file.
 * @param sniffFirst Whether to sniff the data by calling {@link Extractor#sniff(ExtractorInput)}
 *     prior to consuming it.
 * @param simulateIOErrors Whether to simulate IO errors.
 * @param simulateUnknownLength Whether to simulate unknown input length.
 * @param simulatePartialReads Whether to simulate partial reads.
 * @throws IOException If reading from the input fails.
 */
private static void assertOutput(Extractor extractor, String dumpFilesPrefix, byte[] data, Context context, boolean deduplicateConsecutiveFormats, boolean sniffFirst, boolean simulateIOErrors, boolean simulateUnknownLength, boolean simulatePartialReads) throws IOException {
    FakeExtractorInput input = new FakeExtractorInput.Builder().setData(data).setSimulateIOErrors(simulateIOErrors).setSimulateUnknownLength(simulateUnknownLength).setSimulatePartialReads(simulatePartialReads).build();
    if (sniffFirst) {
        assertSniff(extractor, input, /* expectedResult= */
        true);
        input.resetPeekPosition();
    }
    FakeExtractorOutput extractorOutput = consumeTestData(extractor, input, 0, true, deduplicateConsecutiveFormats);
    if (simulateUnknownLength) {
        DumpFileAsserts.assertOutput(context, extractorOutput, dumpFilesPrefix + UNKNOWN_LENGTH_EXTENSION);
    } else {
        DumpFileAsserts.assertOutput(context, extractorOutput, dumpFilesPrefix + ".0" + DUMP_EXTENSION);
    }
    // Seeking to (timeUs=0, position=0) should always work, and cause the same data to be output.
    extractorOutput.clearTrackOutputs();
    input.reset();
    consumeTestData(extractor, input, /* timeUs= */
    0, extractorOutput, false);
    if (simulateUnknownLength) {
        DumpFileAsserts.assertOutput(context, extractorOutput, dumpFilesPrefix + UNKNOWN_LENGTH_EXTENSION);
    } else {
        DumpFileAsserts.assertOutput(context, extractorOutput, dumpFilesPrefix + ".0" + DUMP_EXTENSION);
    }
    SeekMap seekMap = Assertions.checkNotNull(extractorOutput.seekMap);
    long durationUs = seekMap.getDurationUs();
    // Only seek to the timeUs=0 if the SeekMap is unseekable or the duration is unknown.
    int numberSeekTests = seekMap.isSeekable() && durationUs != C.TIME_UNSET ? 4 : 1;
    for (int j = 0; j < numberSeekTests; j++) {
        long timeUs = durationUs * j / 3;
        long position = seekMap.getSeekPoints(timeUs).first.position;
        if (timeUs == 0 && position == 0) {
            // Already tested.
            continue;
        }
        input.reset();
        input.setPosition((int) position);
        extractorOutput.clearTrackOutputs();
        consumeTestData(extractor, input, timeUs, extractorOutput, false);
        if (simulateUnknownLength && timeUs == 0) {
            DumpFileAsserts.assertOutput(context, extractorOutput, dumpFilesPrefix + UNKNOWN_LENGTH_EXTENSION);
        } else {
            DumpFileAsserts.assertOutput(context, extractorOutput, dumpFilesPrefix + '.' + j + DUMP_EXTENSION);
        }
    }
}
Also used : SeekMap(com.google.android.exoplayer2.extractor.SeekMap)

Example 98 with Extractor

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

the class TestUtil method extractSeekMap.

/**
 * Reads from the given input using the given {@link Extractor}, until it can produce the {@link
 * SeekMap} and all of the track formats have been identified, or until the extractor encounters
 * EOF.
 *
 * @param extractor The {@link Extractor} to extractor from input.
 * @param output The {@link FakeTrackOutput} to store the extracted {@link SeekMap} and track.
 * @param dataSource The {@link DataSource} that will be used to read from the input.
 * @param uri The Uri of the input.
 * @return The extracted {@link SeekMap}.
 * @throws IOException If an error occurred reading from the input, or if the extractor finishes
 *     reading from input without extracting any {@link SeekMap}.
 */
public static SeekMap extractSeekMap(Extractor extractor, FakeExtractorOutput output, DataSource dataSource, Uri uri) throws IOException {
    ExtractorInput input = getExtractorInputFromPosition(dataSource, /* position= */
    0, uri);
    extractor.init(output);
    PositionHolder positionHolder = new PositionHolder();
    int readResult = Extractor.RESULT_CONTINUE;
    while (true) {
        try {
            // Keep reading until we get the seek map and the track information.
            while (readResult == Extractor.RESULT_CONTINUE && (output.seekMap == null || !output.tracksEnded)) {
                readResult = extractor.read(input, positionHolder);
            }
            for (int i = 0; i < output.trackOutputs.size(); i++) {
                int trackId = output.trackOutputs.keyAt(i);
                while (readResult == Extractor.RESULT_CONTINUE && output.trackOutputs.get(trackId).lastFormat == null) {
                    readResult = extractor.read(input, positionHolder);
                }
            }
        } finally {
            DataSourceUtil.closeQuietly(dataSource);
        }
        if (readResult == Extractor.RESULT_SEEK) {
            input = getExtractorInputFromPosition(dataSource, positionHolder.position, uri);
            readResult = Extractor.RESULT_CONTINUE;
        } else if (readResult == Extractor.RESULT_END_OF_INPUT) {
            throw new IOException("EOF encountered without seekmap");
        }
        if (output.seekMap != null) {
            return output.seekMap;
        }
    }
}
Also used : ExtractorInput(com.google.android.exoplayer2.extractor.ExtractorInput) DefaultExtractorInput(com.google.android.exoplayer2.extractor.DefaultExtractorInput) PositionHolder(com.google.android.exoplayer2.extractor.PositionHolder) IOException(java.io.IOException)

Example 99 with Extractor

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

the class HlsSampleStreamWrapper method buildTracksFromSampleStreams.

/**
 * Builds tracks that are exposed by this {@link HlsSampleStreamWrapper} instance, as well as
 * internal data-structures required for operation.
 *
 * <p>Tracks in HLS are complicated. A HLS multivariant playlist contains a number of "variants".
 * Each variant stream typically contains muxed video, audio and (possibly) additional audio,
 * metadata and caption tracks. We wish to allow the user to select between an adaptive track that
 * spans all variants, as well as each individual variant. If multiple audio tracks are present
 * within each variant then we wish to allow the user to select between those also.
 *
 * <p>To do this, tracks are constructed as follows. The {@link HlsChunkSource} exposes (N+1)
 * tracks, where N is the number of variants defined in the HLS multivariant playlist. These
 * consist of one adaptive track defined to span all variants and a track for each individual
 * variant. The adaptive track is initially selected. The extractor is then prepared to discover
 * the tracks inside of each variant stream. The two sets of tracks are then combined by this
 * method to create a third set, which is the set exposed by this {@link HlsSampleStreamWrapper}:
 *
 * <ul>
 *   <li>The extractor tracks are inspected to infer a "primary" track type. If a video track is
 *       present then it is always the primary type. If not, audio is the primary type if present.
 *       Else text is the primary type if present. Else there is no primary type.
 *   <li>If there is exactly one extractor track of the primary type, it's expanded into (N+1)
 *       exposed tracks, all of which correspond to the primary extractor track and each of which
 *       corresponds to a different chunk source track. Selecting one of these tracks has the
 *       effect of switching the selected track on the chunk source.
 *   <li>All other extractor tracks are exposed directly. Selecting one of these tracks has the
 *       effect of selecting an extractor track, leaving the selected track on the chunk source
 *       unchanged.
 * </ul>
 */
@EnsuresNonNull({ "trackGroups", "optionalTrackGroups", "trackGroupToSampleQueueIndex" })
private void buildTracksFromSampleStreams() {
    // Iterate through the extractor tracks to discover the "primary" track type, and the index
    // of the single track of this type.
    int primaryExtractorTrackType = C.TRACK_TYPE_NONE;
    int primaryExtractorTrackIndex = C.INDEX_UNSET;
    int extractorTrackCount = sampleQueues.length;
    for (int i = 0; i < extractorTrackCount; i++) {
        @Nullable String sampleMimeType = Assertions.checkStateNotNull(sampleQueues[i].getUpstreamFormat()).sampleMimeType;
        int trackType;
        if (MimeTypes.isVideo(sampleMimeType)) {
            trackType = C.TRACK_TYPE_VIDEO;
        } else if (MimeTypes.isAudio(sampleMimeType)) {
            trackType = C.TRACK_TYPE_AUDIO;
        } else if (MimeTypes.isText(sampleMimeType)) {
            trackType = C.TRACK_TYPE_TEXT;
        } else {
            trackType = C.TRACK_TYPE_NONE;
        }
        if (getTrackTypeScore(trackType) > getTrackTypeScore(primaryExtractorTrackType)) {
            primaryExtractorTrackType = trackType;
            primaryExtractorTrackIndex = i;
        } else if (trackType == primaryExtractorTrackType && primaryExtractorTrackIndex != C.INDEX_UNSET) {
            // We have multiple tracks of the primary type. We only want an index if there only exists a
            // single track of the primary type, so unset the index again.
            primaryExtractorTrackIndex = C.INDEX_UNSET;
        }
    }
    TrackGroup chunkSourceTrackGroup = chunkSource.getTrackGroup();
    int chunkSourceTrackCount = chunkSourceTrackGroup.length;
    // Instantiate the necessary internal data-structures.
    primaryTrackGroupIndex = C.INDEX_UNSET;
    trackGroupToSampleQueueIndex = new int[extractorTrackCount];
    for (int i = 0; i < extractorTrackCount; i++) {
        trackGroupToSampleQueueIndex[i] = i;
    }
    // Construct the set of exposed track groups.
    TrackGroup[] trackGroups = new TrackGroup[extractorTrackCount];
    for (int i = 0; i < extractorTrackCount; i++) {
        Format sampleFormat = Assertions.checkStateNotNull(sampleQueues[i].getUpstreamFormat());
        if (i == primaryExtractorTrackIndex) {
            Format[] formats = new Format[chunkSourceTrackCount];
            for (int j = 0; j < chunkSourceTrackCount; j++) {
                Format playlistFormat = chunkSourceTrackGroup.getFormat(j);
                if (primaryExtractorTrackType == C.TRACK_TYPE_AUDIO && muxedAudioFormat != null) {
                    playlistFormat = playlistFormat.withManifestFormatInfo(muxedAudioFormat);
                }
                // If there's only a single variant (chunkSourceTrackCount == 1) then we can safely
                // retain all fields from sampleFormat. Else we need to use deriveFormat to retain only
                // the fields that will be the same for all variants.
                formats[j] = chunkSourceTrackCount == 1 ? sampleFormat.withManifestFormatInfo(playlistFormat) : deriveFormat(playlistFormat, sampleFormat, /* propagateBitrates= */
                true);
            }
            trackGroups[i] = new TrackGroup(uid, formats);
            primaryTrackGroupIndex = i;
        } else {
            @Nullable Format playlistFormat = primaryExtractorTrackType == C.TRACK_TYPE_VIDEO && MimeTypes.isAudio(sampleFormat.sampleMimeType) ? muxedAudioFormat : null;
            String muxedTrackGroupId = uid + ":muxed:" + (i < primaryExtractorTrackIndex ? i : i - 1);
            trackGroups[i] = new TrackGroup(muxedTrackGroupId, deriveFormat(playlistFormat, sampleFormat, /* propagateBitrates= */
            false));
        }
    }
    this.trackGroups = createTrackGroupArrayWithDrmInfo(trackGroups);
    Assertions.checkState(optionalTrackGroups == null);
    optionalTrackGroups = Collections.emptySet();
}
Also used : Format(com.google.android.exoplayer2.Format) TrackGroup(com.google.android.exoplayer2.source.TrackGroup) Nullable(androidx.annotation.Nullable) EnsuresNonNull(org.checkerframework.checker.nullness.qual.EnsuresNonNull)

Example 100 with Extractor

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

the class DefaultHlsExtractorFactory method createExtractor.

@Override
public BundledHlsMediaChunkExtractor createExtractor(Uri uri, Format format, @Nullable List<Format> muxedCaptionFormats, TimestampAdjuster timestampAdjuster, Map<String, List<String>> responseHeaders, ExtractorInput sniffingExtractorInput, PlayerId playerId) throws IOException {
    @FileTypes.Type int formatInferredFileType = FileTypes.inferFileTypeFromMimeType(format.sampleMimeType);
    @FileTypes.Type int responseHeadersInferredFileType = FileTypes.inferFileTypeFromResponseHeaders(responseHeaders);
    @FileTypes.Type int uriInferredFileType = FileTypes.inferFileTypeFromUri(uri);
    // Defines the order in which to try the extractors.
    List<Integer> fileTypeOrder = new ArrayList<>(/* initialCapacity= */
    DEFAULT_EXTRACTOR_ORDER.length);
    addFileTypeIfValidAndNotPresent(formatInferredFileType, fileTypeOrder);
    addFileTypeIfValidAndNotPresent(responseHeadersInferredFileType, fileTypeOrder);
    addFileTypeIfValidAndNotPresent(uriInferredFileType, fileTypeOrder);
    for (int fileType : DEFAULT_EXTRACTOR_ORDER) {
        addFileTypeIfValidAndNotPresent(fileType, fileTypeOrder);
    }
    // Extractor to be used if the type is not recognized.
    @Nullable Extractor fallBackExtractor = null;
    sniffingExtractorInput.resetPeekPosition();
    for (int i = 0; i < fileTypeOrder.size(); i++) {
        int fileType = fileTypeOrder.get(i);
        Extractor extractor = checkNotNull(createExtractorByFileType(fileType, format, muxedCaptionFormats, timestampAdjuster));
        if (sniffQuietly(extractor, sniffingExtractorInput)) {
            return new BundledHlsMediaChunkExtractor(extractor, format, timestampAdjuster);
        }
        if (fallBackExtractor == null && (fileType == formatInferredFileType || fileType == responseHeadersInferredFileType || fileType == uriInferredFileType || fileType == FileTypes.TS)) {
            // If sniffing fails, fallback to the file types inferred from context. If all else fails,
            // fallback to Transport Stream. See https://github.com/google/ExoPlayer/issues/8219.
            fallBackExtractor = extractor;
        }
    }
    return new BundledHlsMediaChunkExtractor(checkNotNull(fallBackExtractor), format, timestampAdjuster);
}
Also used : ArrayList(java.util.ArrayList) FragmentedMp4Extractor(com.google.android.exoplayer2.extractor.mp4.FragmentedMp4Extractor) Extractor(com.google.android.exoplayer2.extractor.Extractor) TsExtractor(com.google.android.exoplayer2.extractor.ts.TsExtractor) Mp3Extractor(com.google.android.exoplayer2.extractor.mp3.Mp3Extractor) Ac4Extractor(com.google.android.exoplayer2.extractor.ts.Ac4Extractor) AdtsExtractor(com.google.android.exoplayer2.extractor.ts.AdtsExtractor) Ac3Extractor(com.google.android.exoplayer2.extractor.ts.Ac3Extractor) SuppressLint(android.annotation.SuppressLint) Nullable(androidx.annotation.Nullable)

Aggregations

Test (org.junit.Test)87 SeekMap (com.google.android.exoplayer2.extractor.SeekMap)74 Uri (android.net.Uri)66 FakeTrackOutput (com.google.android.exoplayer2.testutil.FakeTrackOutput)59 FakeExtractorOutput (com.google.android.exoplayer2.testutil.FakeExtractorOutput)44 ExtractorInput (com.google.android.exoplayer2.extractor.ExtractorInput)14 FakeExtractorInput (com.google.android.exoplayer2.testutil.FakeExtractorInput)12 DefaultExtractorInput (com.google.android.exoplayer2.extractor.DefaultExtractorInput)11 Extractor (com.google.android.exoplayer2.extractor.Extractor)11 Nullable (androidx.annotation.Nullable)9 FragmentedMp4Extractor (com.google.android.exoplayer2.extractor.mp4.FragmentedMp4Extractor)9 Mp3Extractor (com.google.android.exoplayer2.extractor.mp3.Mp3Extractor)8 WebvttDecoder (com.google.android.exoplayer2.text.webvtt.WebvttDecoder)8 Format (com.google.android.exoplayer2.Format)7 PositionHolder (com.google.android.exoplayer2.extractor.PositionHolder)7 Ac3Extractor (com.google.android.exoplayer2.extractor.ts.Ac3Extractor)7 AdtsExtractor (com.google.android.exoplayer2.extractor.ts.AdtsExtractor)7 TsExtractor (com.google.android.exoplayer2.extractor.ts.TsExtractor)7 ExtractorsFactory (com.google.android.exoplayer2.extractor.ExtractorsFactory)5 MatroskaExtractor (com.google.android.exoplayer2.extractor.mkv.MatroskaExtractor)5