Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Test case for fix for issue #7975 #9174

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9373,6 +9373,54 @@ public void noTargetLiveOffsetInMedia_doesNotAdjustLiveOffset() throws Exception
assertThat(liveOffsetAtEnd).isIn(Range.closed(11_900L, 12_100L));
}

@Test
public void seekTo_toPositionZero_overridesLiveStartPosition()
throws Exception {
ExoPlayer player =
new TestExoPlayerBuilder(context)
.setClock(
new FakeClock(/* initialTimeMs= */ 20_000, /* isAutoAdvancing= */ true))
.setUseLazyPreparation(true) // This is default for ExoPlayer.Builder()
.build();

// TODO - if you set expectedInitialPositionUs to 0, the test will fail... And it shouldn't
int expectedInitialPositionUs = 10_000;
player.seekTo(expectedInitialPositionUs);
FakeMediaSource mediaSource = new FakeMediaSource();
player.setMediaSource(mediaSource, false);
player.prepare();
TestPlayerRunHelper.runUntilPlaybackState(player, Player.STATE_BUFFERING);

MediaItem mediaItem =
new MediaItem.Builder().setUri(Uri.EMPTY).build();
Timeline liveTimeline =
new SinglePeriodTimeline(
/* presentationStartTimeMs= */ C.TIME_UNSET,
/* windowStartTimeMs= */ C.TIME_UNSET,
/* elapsedRealtimeEpochOffsetMs= */ C.TIME_UNSET,
/* periodDurationUs= */ 1000 * C.MICROS_PER_SECOND,
/* windowDurationUs= */ 1000 * C.MICROS_PER_SECOND,
/* windowPositionInPeriodUs= */ 0,
/* windowDefaultStartPositionUs= */ 20 * C.MICROS_PER_SECOND,
/* isSeekable= */ true,
/* isDynamic= */ true,
/* manifest= */ null,
mediaItem,
mediaItem.liveConfiguration);
mediaSource.setNewSourceInfo(liveTimeline, true);

runUntilTimelineChanged(player);

// Trigger EPII to copy current PlaybackInfo to EP - TODO I'll use
player.setPlaybackParameters(new PlaybackParameters(/* speed= */ 2.0f));
runUntilPendingCommandsAreFullyHandled(player);
long contentPosition = player.getCurrentPosition();
player.release();

assertThat(contentPosition).isEqualTo(expectedInitialPositionUs);

}

@Test
public void onEvents_correspondToListenerCalls() throws Exception {
ExoPlayer player = new TestExoPlayerBuilder(context).build();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.exoplayer2.source;

import android.net.Uri;
import androidx.test.ext.junit.runners.AndroidJUnit4;

import java.io.IOException;

import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;

import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.MediaItem;
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.testutil.FakeMediaPeriod;
import com.google.android.exoplayer2.testutil.MediaSourceTestRunner;
import com.google.android.exoplayer2.upstream.Allocator;
import com.google.android.exoplayer2.upstream.TransferListener;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertThrows;

/** Unit tests for {@link MaskingMediaSource}. */
@RunWith(AndroidJUnit4.class)
public class MaskingMediaSourceTest {
private static final MediaItem EMPTY_MEDIA_ITEM =
new MediaItem.Builder().setUri(Uri.EMPTY).build();
@Rule
public final MockitoRule mockito = MockitoJUnit.rule();

@Mock
private MediaPeriod mockMediaPeriod;


class FakeDynamicTimelineMediaSource extends BaseMediaSource {

@Override
protected void prepareSourceInternal(TransferListener mediaTransferListener) {
}

@Override
protected void releaseSourceInternal() {
}

@Override
public MediaItem getMediaItem() {
return EMPTY_MEDIA_ITEM;
}

@Override
public void maybeThrowSourceInfoRefreshError() {
}

@Override
public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator, long startPositionUs) {
return mockMediaPeriod;
}

@Override
public void releasePeriod(MediaPeriod mediaPeriod) {

}

public void setNewSourceInfo(Timeline liveTimeline) {
refreshSourceInfo(liveTimeline);
}
}

@Before
public void setupMocks() {

}

@Test
public void onChildSourceInfoRefreshed_withLiveTimeline_initialSeek() throws IOException {
MediaItem mediaItem =
new MediaItem.Builder().setUri(Uri.EMPTY).build();
Timeline liveTimeline =
new SinglePeriodTimeline(
/* presentationStartTimeMs= */ 0,
/* windowStartTimeMs= */ 0,
/* elapsedRealtimeEpochOffsetMs= */ C.TIME_UNSET,
/* periodDurationUs= */ 1000 * C.MICROS_PER_SECOND,
/* windowDurationUs= */ 1000 * C.MICROS_PER_SECOND,
/* windowPositionInPeriodUs= */ 0,
/* windowDefaultStartPositionUs= */ 20 * C.MICROS_PER_SECOND,
/* isSeekable= */ true,
/* isDynamic= */ true,
/* manifest= */ null,
mediaItem,
mediaItem.liveConfiguration);
Object periodId = liveTimeline.getUidOfPeriod(0);
FakeDynamicTimelineMediaSource mediaSource = new FakeDynamicTimelineMediaSource();
MaskingMediaSource testedMediaSource = new MaskingMediaSource(mediaSource, true);
MediaSourceTestRunner testRunner = new MediaSourceTestRunner(mediaSource, null);
try {
testRunner.runOnPlaybackThread(() -> {
testedMediaSource.prepareSourceInternal(null);
});

// This is the sequence of calls when EPII:
// - lazy prepares the initial masked media source
// - updatePeriods() creates the first period
// - the Timeline update occurs with a live timeline with start position and duration

testRunner.prepareSourceLazy();

testRunner.runOnPlaybackThread(() -> {
int startPositionUs = 20; // TODO - if this value is 0, the test will fail, of course it should not.
MaskingMediaPeriod period = testedMediaSource.createPeriod(new MediaSource.MediaPeriodId(periodId), null, startPositionUs);
mediaSource.setNewSourceInfo(liveTimeline);
assertThat(period.getPreparePositionOverrideUs()).isEqualTo(startPositionUs);
});


testRunner.releaseSource();
} finally {
testRunner.release();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.source.LoadEventInfo;
import com.google.android.exoplayer2.source.MaskingMediaSource;
import com.google.android.exoplayer2.source.MediaLoadData;
import com.google.android.exoplayer2.source.MediaPeriod;
import com.google.android.exoplayer2.source.MediaSource;
Expand Down Expand Up @@ -131,6 +132,17 @@ public Timeline prepareSource() throws IOException {
return assertTimelineChangeBlocking();
}

/**
* Prepares source on playback thread without expecting a timeline change. Use for "lazy" prepare
* MediaSource, e.g. {@link MaskingMediaSource}
*/
public void prepareSourceLazy() {
runOnPlaybackThread(
() -> {
mediaSource.prepareSource(mediaSourceListener, /* mediaTransferListener= */ null);
});
}

/**
* Calls {@link MediaSource#createPeriod(MediaSource.MediaPeriodId, Allocator, long)} with a zero
* start position on the playback thread, asserting that a non-null {@link MediaPeriod} is
Expand Down