Search in sources :

Example 71 with ExoPlayer

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

the class DashMediaSource method getAvailableEndTimeInManifestUs.

private static long getAvailableEndTimeInManifestUs(Period period, long periodDurationUs, long nowUnixTimeUs) {
    long periodStartTimeInManifestUs = Util.msToUs(period.startMs);
    long availableEndTimeInManifestUs = Long.MAX_VALUE;
    boolean haveAudioVideoAdaptationSets = hasVideoOrAudioAdaptationSets(period);
    for (int i = 0; i < period.adaptationSets.size(); i++) {
        AdaptationSet adaptationSet = period.adaptationSets.get(i);
        List<Representation> representations = adaptationSet.representations;
        // or video adaptation set. See: https://github.com/google/ExoPlayer/issues/4029
        if ((haveAudioVideoAdaptationSets && adaptationSet.type == C.TRACK_TYPE_TEXT) || representations.isEmpty()) {
            continue;
        }
        @Nullable DashSegmentIndex index = representations.get(0).getIndex();
        if (index == null) {
            return periodStartTimeInManifestUs + periodDurationUs;
        }
        long availableSegmentCount = index.getAvailableSegmentCount(periodDurationUs, nowUnixTimeUs);
        if (availableSegmentCount == 0) {
            return periodStartTimeInManifestUs;
        }
        long firstAvailableSegmentNum = index.getFirstAvailableSegmentNum(periodDurationUs, nowUnixTimeUs);
        long lastAvailableSegmentNum = firstAvailableSegmentNum + availableSegmentCount - 1;
        long adaptationSetAvailableEndTimeInManifestUs = periodStartTimeInManifestUs + index.getTimeUs(lastAvailableSegmentNum) + index.getDurationUs(lastAvailableSegmentNum, periodDurationUs);
        availableEndTimeInManifestUs = min(availableEndTimeInManifestUs, adaptationSetAvailableEndTimeInManifestUs);
    }
    return availableEndTimeInManifestUs;
}
Also used : AdaptationSet(com.google.android.exoplayer2.source.dash.manifest.AdaptationSet) Representation(com.google.android.exoplayer2.source.dash.manifest.Representation) Nullable(androidx.annotation.Nullable)

Example 72 with ExoPlayer

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

the class AtomParsers method parseStbl.

/**
 * Parses an stbl atom (defined in ISO/IEC 14496-12).
 *
 * @param track Track to which this sample table corresponds.
 * @param stblAtom stbl (sample table) atom to decode.
 * @param gaplessInfoHolder Holder to populate with gapless playback information.
 * @return Sample table described by the stbl atom.
 * @throws ParserException Thrown if the stbl atom can't be parsed.
 */
private static TrackSampleTable parseStbl(Track track, Atom.ContainerAtom stblAtom, GaplessInfoHolder gaplessInfoHolder) throws ParserException {
    SampleSizeBox sampleSizeBox;
    @Nullable Atom.LeafAtom stszAtom = stblAtom.getLeafAtomOfType(Atom.TYPE_stsz);
    if (stszAtom != null) {
        sampleSizeBox = new StszSampleSizeBox(stszAtom, track.format);
    } else {
        @Nullable Atom.LeafAtom stz2Atom = stblAtom.getLeafAtomOfType(Atom.TYPE_stz2);
        if (stz2Atom == null) {
            throw ParserException.createForMalformedContainer("Track has no sample table size information", /* cause= */
            null);
        }
        sampleSizeBox = new Stz2SampleSizeBox(stz2Atom);
    }
    int sampleCount = sampleSizeBox.getSampleCount();
    if (sampleCount == 0) {
        return new TrackSampleTable(track, /* offsets= */
        new long[0], /* sizes= */
        new int[0], /* maximumSize= */
        0, /* timestampsUs= */
        new long[0], /* flags= */
        new int[0], /* durationUs= */
        0);
    }
    // Entries are byte offsets of chunks.
    boolean chunkOffsetsAreLongs = false;
    @Nullable Atom.LeafAtom chunkOffsetsAtom = stblAtom.getLeafAtomOfType(Atom.TYPE_stco);
    if (chunkOffsetsAtom == null) {
        chunkOffsetsAreLongs = true;
        chunkOffsetsAtom = checkNotNull(stblAtom.getLeafAtomOfType(Atom.TYPE_co64));
    }
    ParsableByteArray chunkOffsets = chunkOffsetsAtom.data;
    // Entries are (chunk number, number of samples per chunk, sample description index).
    ParsableByteArray stsc = checkNotNull(stblAtom.getLeafAtomOfType(Atom.TYPE_stsc)).data;
    // Entries are (number of samples, timestamp delta between those samples).
    ParsableByteArray stts = checkNotNull(stblAtom.getLeafAtomOfType(Atom.TYPE_stts)).data;
    // Entries are the indices of samples that are synchronization samples.
    @Nullable Atom.LeafAtom stssAtom = stblAtom.getLeafAtomOfType(Atom.TYPE_stss);
    @Nullable ParsableByteArray stss = stssAtom != null ? stssAtom.data : null;
    // Entries are (number of samples, timestamp offset).
    @Nullable Atom.LeafAtom cttsAtom = stblAtom.getLeafAtomOfType(Atom.TYPE_ctts);
    @Nullable ParsableByteArray ctts = cttsAtom != null ? cttsAtom.data : null;
    // Prepare to read chunk information.
    ChunkIterator chunkIterator = new ChunkIterator(stsc, chunkOffsets, chunkOffsetsAreLongs);
    // Prepare to read sample timestamps.
    stts.setPosition(Atom.FULL_HEADER_SIZE);
    int remainingTimestampDeltaChanges = stts.readUnsignedIntToInt() - 1;
    int remainingSamplesAtTimestampDelta = stts.readUnsignedIntToInt();
    int timestampDeltaInTimeUnits = stts.readUnsignedIntToInt();
    // Prepare to read sample timestamp offsets, if ctts is present.
    int remainingSamplesAtTimestampOffset = 0;
    int remainingTimestampOffsetChanges = 0;
    int timestampOffset = 0;
    if (ctts != null) {
        ctts.setPosition(Atom.FULL_HEADER_SIZE);
        remainingTimestampOffsetChanges = ctts.readUnsignedIntToInt();
    }
    int nextSynchronizationSampleIndex = C.INDEX_UNSET;
    int remainingSynchronizationSamples = 0;
    if (stss != null) {
        stss.setPosition(Atom.FULL_HEADER_SIZE);
        remainingSynchronizationSamples = stss.readUnsignedIntToInt();
        if (remainingSynchronizationSamples > 0) {
            nextSynchronizationSampleIndex = stss.readUnsignedIntToInt() - 1;
        } else {
            // Ignore empty stss boxes, which causes all samples to be treated as sync samples.
            stss = null;
        }
    }
    // Fixed sample size raw audio may need to be rechunked.
    int fixedSampleSize = sampleSizeBox.getFixedSampleSize();
    @Nullable String sampleMimeType = track.format.sampleMimeType;
    boolean rechunkFixedSizeSamples = fixedSampleSize != C.LENGTH_UNSET && (MimeTypes.AUDIO_RAW.equals(sampleMimeType) || MimeTypes.AUDIO_MLAW.equals(sampleMimeType) || MimeTypes.AUDIO_ALAW.equals(sampleMimeType)) && remainingTimestampDeltaChanges == 0 && remainingTimestampOffsetChanges == 0 && remainingSynchronizationSamples == 0;
    long[] offsets;
    int[] sizes;
    int maximumSize = 0;
    long[] timestamps;
    int[] flags;
    long timestampTimeUnits = 0;
    long duration;
    if (rechunkFixedSizeSamples) {
        long[] chunkOffsetsBytes = new long[chunkIterator.length];
        int[] chunkSampleCounts = new int[chunkIterator.length];
        while (chunkIterator.moveNext()) {
            chunkOffsetsBytes[chunkIterator.index] = chunkIterator.offset;
            chunkSampleCounts[chunkIterator.index] = chunkIterator.numSamples;
        }
        FixedSampleSizeRechunker.Results rechunkedResults = FixedSampleSizeRechunker.rechunk(fixedSampleSize, chunkOffsetsBytes, chunkSampleCounts, timestampDeltaInTimeUnits);
        offsets = rechunkedResults.offsets;
        sizes = rechunkedResults.sizes;
        maximumSize = rechunkedResults.maximumSize;
        timestamps = rechunkedResults.timestamps;
        flags = rechunkedResults.flags;
        duration = rechunkedResults.duration;
    } else {
        offsets = new long[sampleCount];
        sizes = new int[sampleCount];
        timestamps = new long[sampleCount];
        flags = new int[sampleCount];
        long offset = 0;
        int remainingSamplesInChunk = 0;
        for (int i = 0; i < sampleCount; i++) {
            // Advance to the next chunk if necessary.
            boolean chunkDataComplete = true;
            while (remainingSamplesInChunk == 0 && (chunkDataComplete = chunkIterator.moveNext())) {
                offset = chunkIterator.offset;
                remainingSamplesInChunk = chunkIterator.numSamples;
            }
            if (!chunkDataComplete) {
                Log.w(TAG, "Unexpected end of chunk data");
                sampleCount = i;
                offsets = Arrays.copyOf(offsets, sampleCount);
                sizes = Arrays.copyOf(sizes, sampleCount);
                timestamps = Arrays.copyOf(timestamps, sampleCount);
                flags = Arrays.copyOf(flags, sampleCount);
                break;
            }
            // Add on the timestamp offset if ctts is present.
            if (ctts != null) {
                while (remainingSamplesAtTimestampOffset == 0 && remainingTimestampOffsetChanges > 0) {
                    remainingSamplesAtTimestampOffset = ctts.readUnsignedIntToInt();
                    // The BMFF spec (ISO/IEC 14496-12) states that sample offsets should be unsigned
                    // integers in version 0 ctts boxes, however some streams violate the spec and use
                    // signed integers instead. It's safe to always decode sample offsets as signed integers
                    // here, because unsigned integers will still be parsed correctly (unless their top bit
                    // is set, which is never true in practice because sample offsets are always small).
                    timestampOffset = ctts.readInt();
                    remainingTimestampOffsetChanges--;
                }
                remainingSamplesAtTimestampOffset--;
            }
            offsets[i] = offset;
            sizes[i] = sampleSizeBox.readNextSampleSize();
            if (sizes[i] > maximumSize) {
                maximumSize = sizes[i];
            }
            timestamps[i] = timestampTimeUnits + timestampOffset;
            // All samples are synchronization samples if the stss is not present.
            flags[i] = stss == null ? C.BUFFER_FLAG_KEY_FRAME : 0;
            if (i == nextSynchronizationSampleIndex) {
                flags[i] = C.BUFFER_FLAG_KEY_FRAME;
                remainingSynchronizationSamples--;
                if (remainingSynchronizationSamples > 0) {
                    nextSynchronizationSampleIndex = checkNotNull(stss).readUnsignedIntToInt() - 1;
                }
            }
            // Add on the duration of this sample.
            timestampTimeUnits += timestampDeltaInTimeUnits;
            remainingSamplesAtTimestampDelta--;
            if (remainingSamplesAtTimestampDelta == 0 && remainingTimestampDeltaChanges > 0) {
                remainingSamplesAtTimestampDelta = stts.readUnsignedIntToInt();
                // The BMFF spec (ISO/IEC 14496-12) states that sample deltas should be unsigned integers
                // in stts boxes, however some streams violate the spec and use signed integers instead.
                // See https://github.com/google/ExoPlayer/issues/3384. It's safe to always decode sample
                // deltas as signed integers here, because unsigned integers will still be parsed
                // correctly (unless their top bit is set, which is never true in practice because sample
                // deltas are always small).
                timestampDeltaInTimeUnits = stts.readInt();
                remainingTimestampDeltaChanges--;
            }
            offset += sizes[i];
            remainingSamplesInChunk--;
        }
        duration = timestampTimeUnits + timestampOffset;
        // If the stbl's child boxes are not consistent the container is malformed, but the stream may
        // still be playable.
        boolean isCttsValid = true;
        if (ctts != null) {
            while (remainingTimestampOffsetChanges > 0) {
                if (ctts.readUnsignedIntToInt() != 0) {
                    isCttsValid = false;
                    break;
                }
                // Ignore offset.
                ctts.readInt();
                remainingTimestampOffsetChanges--;
            }
        }
        if (remainingSynchronizationSamples != 0 || remainingSamplesAtTimestampDelta != 0 || remainingSamplesInChunk != 0 || remainingTimestampDeltaChanges != 0 || remainingSamplesAtTimestampOffset != 0 || !isCttsValid) {
            Log.w(TAG, "Inconsistent stbl box for track " + track.id + ": remainingSynchronizationSamples " + remainingSynchronizationSamples + ", remainingSamplesAtTimestampDelta " + remainingSamplesAtTimestampDelta + ", remainingSamplesInChunk " + remainingSamplesInChunk + ", remainingTimestampDeltaChanges " + remainingTimestampDeltaChanges + ", remainingSamplesAtTimestampOffset " + remainingSamplesAtTimestampOffset + (!isCttsValid ? ", ctts invalid" : ""));
        }
    }
    long durationUs = Util.scaleLargeTimestamp(duration, C.MICROS_PER_SECOND, track.timescale);
    if (track.editListDurations == null) {
        Util.scaleLargeTimestampsInPlace(timestamps, C.MICROS_PER_SECOND, track.timescale);
        return new TrackSampleTable(track, offsets, sizes, maximumSize, timestamps, flags, durationUs);
    }
    if (track.editListDurations.length == 1 && track.type == C.TRACK_TYPE_AUDIO && timestamps.length >= 2) {
        long editStartTime = checkNotNull(track.editListMediaTimes)[0];
        long editEndTime = editStartTime + Util.scaleLargeTimestamp(track.editListDurations[0], track.timescale, track.movieTimescale);
        if (canApplyEditWithGaplessInfo(timestamps, duration, editStartTime, editEndTime)) {
            long paddingTimeUnits = duration - editEndTime;
            long encoderDelay = Util.scaleLargeTimestamp(editStartTime - timestamps[0], track.format.sampleRate, track.timescale);
            long encoderPadding = Util.scaleLargeTimestamp(paddingTimeUnits, track.format.sampleRate, track.timescale);
            if ((encoderDelay != 0 || encoderPadding != 0) && encoderDelay <= Integer.MAX_VALUE && encoderPadding <= Integer.MAX_VALUE) {
                gaplessInfoHolder.encoderDelay = (int) encoderDelay;
                gaplessInfoHolder.encoderPadding = (int) encoderPadding;
                Util.scaleLargeTimestampsInPlace(timestamps, C.MICROS_PER_SECOND, track.timescale);
                long editedDurationUs = Util.scaleLargeTimestamp(track.editListDurations[0], C.MICROS_PER_SECOND, track.movieTimescale);
                return new TrackSampleTable(track, offsets, sizes, maximumSize, timestamps, flags, editedDurationUs);
            }
        }
    }
    if (track.editListDurations.length == 1 && track.editListDurations[0] == 0) {
        // The current version of the spec leaves handling of an edit with zero segment_duration in
        // unfragmented files open to interpretation. We handle this as a special case and include all
        // samples in the edit.
        long editStartTime = checkNotNull(track.editListMediaTimes)[0];
        for (int i = 0; i < timestamps.length; i++) {
            timestamps[i] = Util.scaleLargeTimestamp(timestamps[i] - editStartTime, C.MICROS_PER_SECOND, track.timescale);
        }
        durationUs = Util.scaleLargeTimestamp(duration - editStartTime, C.MICROS_PER_SECOND, track.timescale);
        return new TrackSampleTable(track, offsets, sizes, maximumSize, timestamps, flags, durationUs);
    }
    // Omit any sample at the end point of an edit for audio tracks.
    boolean omitClippedSample = track.type == C.TRACK_TYPE_AUDIO;
    // Count the number of samples after applying edits.
    int editedSampleCount = 0;
    int nextSampleIndex = 0;
    boolean copyMetadata = false;
    int[] startIndices = new int[track.editListDurations.length];
    int[] endIndices = new int[track.editListDurations.length];
    long[] editListMediaTimes = checkNotNull(track.editListMediaTimes);
    for (int i = 0; i < track.editListDurations.length; i++) {
        long editMediaTime = editListMediaTimes[i];
        if (editMediaTime != -1) {
            long editDuration = Util.scaleLargeTimestamp(track.editListDurations[i], track.timescale, track.movieTimescale);
            startIndices[i] = Util.binarySearchFloor(timestamps, editMediaTime, /* inclusive= */
            true, /* stayInBounds= */
            true);
            endIndices[i] = Util.binarySearchCeil(timestamps, editMediaTime + editDuration, /* inclusive= */
            omitClippedSample, /* stayInBounds= */
            false);
            while (startIndices[i] < endIndices[i] && (flags[startIndices[i]] & C.BUFFER_FLAG_KEY_FRAME) == 0) {
                // Applying the edit correctly would require prerolling from the previous sync sample. In
                // the current implementation we advance to the next sync sample instead. Only other
                // tracks (i.e. audio) will be rendered until the time of the first sync sample.
                // See https://github.com/google/ExoPlayer/issues/1659.
                startIndices[i]++;
            }
            editedSampleCount += endIndices[i] - startIndices[i];
            copyMetadata |= nextSampleIndex != startIndices[i];
            nextSampleIndex = endIndices[i];
        }
    }
    copyMetadata |= editedSampleCount != sampleCount;
    // Calculate edited sample timestamps and update the corresponding metadata arrays.
    long[] editedOffsets = copyMetadata ? new long[editedSampleCount] : offsets;
    int[] editedSizes = copyMetadata ? new int[editedSampleCount] : sizes;
    int editedMaximumSize = copyMetadata ? 0 : maximumSize;
    int[] editedFlags = copyMetadata ? new int[editedSampleCount] : flags;
    long[] editedTimestamps = new long[editedSampleCount];
    long pts = 0;
    int sampleIndex = 0;
    for (int i = 0; i < track.editListDurations.length; i++) {
        long editMediaTime = track.editListMediaTimes[i];
        int startIndex = startIndices[i];
        int endIndex = endIndices[i];
        if (copyMetadata) {
            int count = endIndex - startIndex;
            System.arraycopy(offsets, startIndex, editedOffsets, sampleIndex, count);
            System.arraycopy(sizes, startIndex, editedSizes, sampleIndex, count);
            System.arraycopy(flags, startIndex, editedFlags, sampleIndex, count);
        }
        for (int j = startIndex; j < endIndex; j++) {
            long ptsUs = Util.scaleLargeTimestamp(pts, C.MICROS_PER_SECOND, track.movieTimescale);
            long timeInSegmentUs = Util.scaleLargeTimestamp(max(0, timestamps[j] - editMediaTime), C.MICROS_PER_SECOND, track.timescale);
            editedTimestamps[sampleIndex] = ptsUs + timeInSegmentUs;
            if (copyMetadata && editedSizes[sampleIndex] > editedMaximumSize) {
                editedMaximumSize = sizes[j];
            }
            sampleIndex++;
        }
        pts += track.editListDurations[i];
    }
    long editedDurationUs = Util.scaleLargeTimestamp(pts, C.MICROS_PER_SECOND, track.movieTimescale);
    return new TrackSampleTable(track, editedOffsets, editedSizes, editedMaximumSize, editedTimestamps, editedFlags, editedDurationUs);
}
Also used : ParsableByteArray(com.google.android.exoplayer2.util.ParsableByteArray) Nullable(androidx.annotation.Nullable)

Example 73 with ExoPlayer

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

the class Tx3gDecoderTest method decodeWithStyl_startTooLarge_noSpanAdded.

/**
 * The 7-byte sample contains a 4-byte emoji. The start index (6) and end index (7) are valid as
 * byte offsets, but not a UTF-16 code-unit offset, so they're both truncated to 5 (the length of
 * the resulting the string in Java) and the spans end up empty (so we don't add them).
 *
 * <p>https://github.com/google/ExoPlayer/pull/8133
 */
@Test
public void decodeWithStyl_startTooLarge_noSpanAdded() throws Exception {
    Tx3gDecoder decoder = new Tx3gDecoder(ImmutableList.of());
    byte[] bytes = TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), SAMPLE_WITH_STYL_START_TOO_LARGE);
    Subtitle subtitle = decoder.decode(bytes, bytes.length, false);
    SpannedString text = new SpannedString(subtitle.getCues(0).get(0).text);
    assertThat(text.toString()).isEqualTo("CC 🙂");
    assertThat(text).hasNoSpans();
    assertFractionalLinePosition(subtitle.getCues(0).get(0), 0.85f);
}
Also used : Subtitle(com.google.android.exoplayer2.text.Subtitle) SpannedString(android.text.SpannedString) Test(org.junit.Test)

Example 74 with ExoPlayer

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

the class Tx3gDecoderTest method decodeWithStyl_endTooLarge_clippedToEndOfText.

/**
 * The 7-byte sample contains a 4-byte emoji. The end index (6) is valid as a byte offset, but not
 * a UTF-16 code-unit offset, so it's truncated to 5 (the length of the resulting the string in
 * Java).
 *
 * <p>https://github.com/google/ExoPlayer/pull/8133
 */
@Test
public void decodeWithStyl_endTooLarge_clippedToEndOfText() throws Exception {
    Tx3gDecoder decoder = new Tx3gDecoder(ImmutableList.of());
    byte[] bytes = TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), SAMPLE_WITH_STYL_END_TOO_LARGE);
    Subtitle subtitle = decoder.decode(bytes, bytes.length, false);
    SpannedString text = new SpannedString(subtitle.getCues(0).get(0).text);
    assertThat(text.toString()).isEqualTo("CC 🙂");
    assertThat(text).hasBoldItalicSpanBetween(0, 5);
    assertThat(text).hasUnderlineSpanBetween(0, 5);
    assertThat(text).hasForegroundColorSpanBetween(0, 5).withColor(Color.GREEN);
    assertFractionalLinePosition(subtitle.getCues(0).get(0), 0.85f);
}
Also used : Subtitle(com.google.android.exoplayer2.text.Subtitle) SpannedString(android.text.SpannedString) Test(org.junit.Test)

Example 75 with ExoPlayer

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

the class ExoPlayerTest method seekToCurrentPosition_inEndedState_switchesToBufferingStateAndContinuesPlayback.

@Test
public void seekToCurrentPosition_inEndedState_switchesToBufferingStateAndContinuesPlayback() throws Exception {
    MediaSource mediaSource = new FakeMediaSource(new FakeTimeline(/* windowCount = */
    1));
    AtomicInteger mediaItemIndexAfterFinalEndedState = new AtomicInteger();
    ActionSchedule actionSchedule = new ActionSchedule.Builder(TAG).waitForPlaybackState(Player.STATE_ENDED).addMediaSources(mediaSource).executeRunnable(new PlayerRunnable() {

        @Override
        public void run(ExoPlayer player) {
            player.seekTo(player.getCurrentPosition());
        }
    }).waitForPlaybackState(Player.STATE_READY).waitForPlaybackState(Player.STATE_ENDED).executeRunnable(new PlayerRunnable() {

        @Override
        public void run(ExoPlayer player) {
            mediaItemIndexAfterFinalEndedState.set(player.getCurrentMediaItemIndex());
        }
    }).build();
    new ExoPlayerTestRunner.Builder(context).setMediaSources(mediaSource).setActionSchedule(actionSchedule).build().start().blockUntilActionScheduleFinished(TIMEOUT_MS).blockUntilEnded(TIMEOUT_MS);
    assertThat(mediaItemIndexAfterFinalEndedState.get()).isEqualTo(1);
}
Also used : ServerSideAdInsertionMediaSource(com.google.android.exoplayer2.source.ads.ServerSideAdInsertionMediaSource) FakeAdaptiveMediaSource(com.google.android.exoplayer2.testutil.FakeAdaptiveMediaSource) MaskingMediaSource(com.google.android.exoplayer2.source.MaskingMediaSource) ConcatenatingMediaSource(com.google.android.exoplayer2.source.ConcatenatingMediaSource) MediaSource(com.google.android.exoplayer2.source.MediaSource) CompositeMediaSource(com.google.android.exoplayer2.source.CompositeMediaSource) ClippingMediaSource(com.google.android.exoplayer2.source.ClippingMediaSource) FakeMediaSource(com.google.android.exoplayer2.testutil.FakeMediaSource) FakeMediaSource(com.google.android.exoplayer2.testutil.FakeMediaSource) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) ActionSchedule(com.google.android.exoplayer2.testutil.ActionSchedule) PlayerRunnable(com.google.android.exoplayer2.testutil.ActionSchedule.PlayerRunnable) FakeTimeline(com.google.android.exoplayer2.testutil.FakeTimeline) TestExoPlayerBuilder(com.google.android.exoplayer2.testutil.TestExoPlayerBuilder) Test(org.junit.Test)

Aggregations

Test (org.junit.Test)248 FakeMediaSource (com.google.android.exoplayer2.testutil.FakeMediaSource)179 TestExoPlayerBuilder (com.google.android.exoplayer2.testutil.TestExoPlayerBuilder)172 FakeTimeline (com.google.android.exoplayer2.testutil.FakeTimeline)121 PlayerRunnable (com.google.android.exoplayer2.testutil.ActionSchedule.PlayerRunnable)108 ActionSchedule (com.google.android.exoplayer2.testutil.ActionSchedule)92 SinglePeriodTimeline (com.google.android.exoplayer2.source.SinglePeriodTimeline)89 NoUidTimeline (com.google.android.exoplayer2.testutil.NoUidTimeline)89 Listener (com.google.android.exoplayer2.Player.Listener)85 TimelineWindowDefinition (com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinition)73 ExoPlayerTestRunner (com.google.android.exoplayer2.testutil.ExoPlayerTestRunner)67 ConcatenatingMediaSource (com.google.android.exoplayer2.source.ConcatenatingMediaSource)65 MediaSource (com.google.android.exoplayer2.source.MediaSource)55 FakeClock (com.google.android.exoplayer2.testutil.FakeClock)48 ClippingMediaSource (com.google.android.exoplayer2.source.ClippingMediaSource)47 CompositeMediaSource (com.google.android.exoplayer2.source.CompositeMediaSource)47 MaskingMediaSource (com.google.android.exoplayer2.source.MaskingMediaSource)47 ServerSideAdInsertionMediaSource (com.google.android.exoplayer2.source.ads.ServerSideAdInsertionMediaSource)47 FakeAdaptiveMediaSource (com.google.android.exoplayer2.testutil.FakeAdaptiveMediaSource)47 ExoPlayer (com.google.android.exoplayer2.ExoPlayer)44