Search in sources :

Example 6 with State

use of com.google.android.exoplayer2.ext.ima.ImaServerSideAdInsertionMediaSource.AdsLoader.State in project ExoPlayer by google.

the class CastPlayerTest method autoTransition_notifiesMediaItemTransition.

@Test
public void autoTransition_notifiesMediaItemTransition() {
    int[] mediaQueueItemIds = new int[] { 1, 2 };
    // When the remote Cast player transitions to an item that wasn't played before, the media state
    // delivers the duration for that media item which updates the timeline accordingly.
    List<MediaItem> mediaItems = createMediaItems(mediaQueueItemIds);
    castPlayer.addMediaItems(mediaItems);
    updateTimeLine(mediaItems, mediaQueueItemIds, /* currentItemId= */
    1);
    updateTimeLine(mediaItems, mediaQueueItemIds, /* currentItemId= */
    2);
    InOrder inOrder = Mockito.inOrder(mockListener);
    inOrder.verify(mockListener).onMediaItemTransition(any(), eq(Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED));
    inOrder.verify(mockListener).onMediaItemTransition(mediaItemCaptor.capture(), eq(Player.MEDIA_ITEM_TRANSITION_REASON_AUTO));
    inOrder.verify(mockListener, never()).onMediaItemTransition(any(), anyInt());
    assertThat(mediaItemCaptor.getValue().localConfiguration.tag).isEqualTo(2);
}
Also used : InOrder(org.mockito.InOrder) MediaItem(com.google.android.exoplayer2.MediaItem) Test(org.junit.Test)

Example 7 with State

use of com.google.android.exoplayer2.ext.ima.ImaServerSideAdInsertionMediaSource.AdsLoader.State in project ExoPlayer by google.

the class CastPlayer method updateTracksAndSelectionsAndNotifyIfChanged.

/**
 * Updates the internal tracks and selection and returns whether they have changed.
 */
private boolean updateTracksAndSelectionsAndNotifyIfChanged() {
    if (remoteMediaClient == null) {
        // There is no session. We leave the state of the player as it is now.
        return false;
    }
    MediaStatus mediaStatus = getMediaStatus();
    MediaInfo mediaInfo = mediaStatus != null ? mediaStatus.getMediaInfo() : null;
    List<MediaTrack> castMediaTracks = mediaInfo != null ? mediaInfo.getMediaTracks() : null;
    if (castMediaTracks == null || castMediaTracks.isEmpty()) {
        boolean hasChanged = !currentTrackGroups.isEmpty();
        currentTrackGroups = TrackGroupArray.EMPTY;
        currentTrackSelection = EMPTY_TRACK_SELECTION_ARRAY;
        currentTracksInfo = TracksInfo.EMPTY;
        return hasChanged;
    }
    long[] activeTrackIds = mediaStatus.getActiveTrackIds();
    if (activeTrackIds == null) {
        activeTrackIds = EMPTY_TRACK_ID_ARRAY;
    }
    TrackGroup[] trackGroups = new TrackGroup[castMediaTracks.size()];
    @NullableType TrackSelection[] trackSelections = new TrackSelection[RENDERER_COUNT];
    TracksInfo.TrackGroupInfo[] trackGroupInfos = new TracksInfo.TrackGroupInfo[castMediaTracks.size()];
    for (int i = 0; i < castMediaTracks.size(); i++) {
        MediaTrack mediaTrack = castMediaTracks.get(i);
        trackGroups[i] = new TrackGroup(/* id= */
        Integer.toString(i), CastUtils.mediaTrackToFormat(mediaTrack));
        long id = mediaTrack.getId();
        @C.TrackType int trackType = MimeTypes.getTrackType(mediaTrack.getContentType());
        int rendererIndex = getRendererIndexForTrackType(trackType);
        boolean supported = rendererIndex != C.INDEX_UNSET;
        boolean selected = isTrackActive(id, activeTrackIds) && supported && trackSelections[rendererIndex] == null;
        if (selected) {
            trackSelections[rendererIndex] = new CastTrackSelection(trackGroups[i]);
        }
        @C.FormatSupport int[] trackSupport = new int[] { supported ? C.FORMAT_HANDLED : C.FORMAT_UNSUPPORTED_TYPE };
        final boolean[] trackSelected = new boolean[] { selected };
        trackGroupInfos[i] = new TracksInfo.TrackGroupInfo(trackGroups[i], trackSupport, trackType, trackSelected);
    }
    TrackGroupArray newTrackGroups = new TrackGroupArray(trackGroups);
    TrackSelectionArray newTrackSelections = new TrackSelectionArray(trackSelections);
    TracksInfo newTracksInfo = new TracksInfo(ImmutableList.copyOf(trackGroupInfos));
    if (!newTrackGroups.equals(currentTrackGroups) || !newTrackSelections.equals(currentTrackSelection) || !newTracksInfo.equals(currentTracksInfo)) {
        currentTrackSelection = newTrackSelections;
        currentTrackGroups = newTrackGroups;
        currentTracksInfo = newTracksInfo;
        return true;
    }
    return false;
}
Also used : TrackGroupArray(com.google.android.exoplayer2.source.TrackGroupArray) NullableType(org.checkerframework.checker.nullness.compatqual.NullableType) TracksInfo(com.google.android.exoplayer2.TracksInfo) TrackSelectionArray(com.google.android.exoplayer2.trackselection.TrackSelectionArray) MediaTrack(com.google.android.gms.cast.MediaTrack) MediaInfo(com.google.android.gms.cast.MediaInfo) TrackGroup(com.google.android.exoplayer2.source.TrackGroup) TrackSelection(com.google.android.exoplayer2.trackselection.TrackSelection) MediaStatus(com.google.android.gms.cast.MediaStatus)

Example 8 with State

use of com.google.android.exoplayer2.ext.ima.ImaServerSideAdInsertionMediaSource.AdsLoader.State in project ExoPlayer by google.

the class ExoPlayerImpl method updateWakeAndWifiLock.

private void updateWakeAndWifiLock() {
    @State int playbackState = getPlaybackState();
    switch(playbackState) {
        case Player.STATE_READY:
        case Player.STATE_BUFFERING:
            boolean isSleeping = experimentalIsSleepingForOffload();
            wakeLockManager.setStayAwake(getPlayWhenReady() && !isSleeping);
            // The wifi lock is not released while sleeping to avoid interrupting downloads.
            wifiLockManager.setStayAwake(getPlayWhenReady());
            break;
        case Player.STATE_ENDED:
        case Player.STATE_IDLE:
            wakeLockManager.setStayAwake(false);
            wifiLockManager.setStayAwake(false);
            break;
        default:
            throw new IllegalStateException();
    }
}
Also used : Assertions.checkState(com.google.android.exoplayer2.util.Assertions.checkState) SuppressLint(android.annotation.SuppressLint)

Example 9 with State

use of com.google.android.exoplayer2.ext.ima.ImaServerSideAdInsertionMediaSource.AdsLoader.State in project ExoPlayer by google.

the class ExoPlayerImpl method updatePlaybackInfo.

// Calling deprecated listeners.
@SuppressWarnings("deprecation")
private void updatePlaybackInfo(PlaybackInfo playbackInfo, @TimelineChangeReason int timelineChangeReason, @PlayWhenReadyChangeReason int playWhenReadyChangeReason, boolean seekProcessed, boolean positionDiscontinuity, @DiscontinuityReason int positionDiscontinuityReason, long discontinuityWindowStartPositionUs, int oldMaskingMediaItemIndex) {
    // Assign playback info immediately such that all getters return the right values, but keep
    // snapshot of previous and new state so that listener invocations are triggered correctly.
    PlaybackInfo previousPlaybackInfo = this.playbackInfo;
    PlaybackInfo newPlaybackInfo = playbackInfo;
    this.playbackInfo = playbackInfo;
    Pair<Boolean, Integer> mediaItemTransitionInfo = evaluateMediaItemTransitionReason(newPlaybackInfo, previousPlaybackInfo, positionDiscontinuity, positionDiscontinuityReason, !previousPlaybackInfo.timeline.equals(newPlaybackInfo.timeline));
    boolean mediaItemTransitioned = mediaItemTransitionInfo.first;
    int mediaItemTransitionReason = mediaItemTransitionInfo.second;
    MediaMetadata newMediaMetadata = mediaMetadata;
    @Nullable MediaItem mediaItem = null;
    if (mediaItemTransitioned) {
        if (!newPlaybackInfo.timeline.isEmpty()) {
            int windowIndex = newPlaybackInfo.timeline.getPeriodByUid(newPlaybackInfo.periodId.periodUid, period).windowIndex;
            mediaItem = newPlaybackInfo.timeline.getWindow(windowIndex, window).mediaItem;
        }
        staticAndDynamicMediaMetadata = MediaMetadata.EMPTY;
    }
    if (mediaItemTransitioned || !previousPlaybackInfo.staticMetadata.equals(newPlaybackInfo.staticMetadata)) {
        staticAndDynamicMediaMetadata = staticAndDynamicMediaMetadata.buildUpon().populateFromMetadata(newPlaybackInfo.staticMetadata).build();
        newMediaMetadata = buildUpdatedMediaMetadata();
    }
    boolean metadataChanged = !newMediaMetadata.equals(mediaMetadata);
    mediaMetadata = newMediaMetadata;
    boolean playWhenReadyChanged = previousPlaybackInfo.playWhenReady != newPlaybackInfo.playWhenReady;
    boolean playbackStateChanged = previousPlaybackInfo.playbackState != newPlaybackInfo.playbackState;
    if (playbackStateChanged || playWhenReadyChanged) {
        updateWakeAndWifiLock();
    }
    boolean isLoadingChanged = previousPlaybackInfo.isLoading != newPlaybackInfo.isLoading;
    if (isLoadingChanged) {
        updatePriorityTaskManagerForIsLoadingChange(newPlaybackInfo.isLoading);
    }
    if (!previousPlaybackInfo.timeline.equals(newPlaybackInfo.timeline)) {
        listeners.queueEvent(Player.EVENT_TIMELINE_CHANGED, listener -> listener.onTimelineChanged(newPlaybackInfo.timeline, timelineChangeReason));
    }
    if (positionDiscontinuity) {
        PositionInfo previousPositionInfo = getPreviousPositionInfo(positionDiscontinuityReason, previousPlaybackInfo, oldMaskingMediaItemIndex);
        PositionInfo positionInfo = getPositionInfo(discontinuityWindowStartPositionUs);
        listeners.queueEvent(Player.EVENT_POSITION_DISCONTINUITY, listener -> {
            listener.onPositionDiscontinuity(positionDiscontinuityReason);
            listener.onPositionDiscontinuity(previousPositionInfo, positionInfo, positionDiscontinuityReason);
        });
    }
    if (mediaItemTransitioned) {
        @Nullable final MediaItem finalMediaItem = mediaItem;
        listeners.queueEvent(Player.EVENT_MEDIA_ITEM_TRANSITION, listener -> listener.onMediaItemTransition(finalMediaItem, mediaItemTransitionReason));
    }
    if (previousPlaybackInfo.playbackError != newPlaybackInfo.playbackError) {
        listeners.queueEvent(Player.EVENT_PLAYER_ERROR, listener -> listener.onPlayerErrorChanged(newPlaybackInfo.playbackError));
        if (newPlaybackInfo.playbackError != null) {
            listeners.queueEvent(Player.EVENT_PLAYER_ERROR, listener -> listener.onPlayerError(newPlaybackInfo.playbackError));
        }
    }
    if (previousPlaybackInfo.trackSelectorResult != newPlaybackInfo.trackSelectorResult) {
        trackSelector.onSelectionActivated(newPlaybackInfo.trackSelectorResult.info);
        TrackSelectionArray newSelection = new TrackSelectionArray(newPlaybackInfo.trackSelectorResult.selections);
        listeners.queueEvent(Player.EVENT_TRACKS_CHANGED, listener -> listener.onTracksChanged(newPlaybackInfo.trackGroups, newSelection));
        listeners.queueEvent(Player.EVENT_TRACKS_CHANGED, listener -> listener.onTracksInfoChanged(newPlaybackInfo.trackSelectorResult.tracksInfo));
    }
    if (metadataChanged) {
        final MediaMetadata finalMediaMetadata = mediaMetadata;
        listeners.queueEvent(EVENT_MEDIA_METADATA_CHANGED, listener -> listener.onMediaMetadataChanged(finalMediaMetadata));
    }
    if (isLoadingChanged) {
        listeners.queueEvent(Player.EVENT_IS_LOADING_CHANGED, listener -> {
            listener.onLoadingChanged(newPlaybackInfo.isLoading);
            listener.onIsLoadingChanged(newPlaybackInfo.isLoading);
        });
    }
    if (playbackStateChanged || playWhenReadyChanged) {
        listeners.queueEvent(/* eventFlag= */
        C.INDEX_UNSET, listener -> listener.onPlayerStateChanged(newPlaybackInfo.playWhenReady, newPlaybackInfo.playbackState));
    }
    if (playbackStateChanged) {
        listeners.queueEvent(Player.EVENT_PLAYBACK_STATE_CHANGED, listener -> listener.onPlaybackStateChanged(newPlaybackInfo.playbackState));
    }
    if (playWhenReadyChanged) {
        listeners.queueEvent(Player.EVENT_PLAY_WHEN_READY_CHANGED, listener -> listener.onPlayWhenReadyChanged(newPlaybackInfo.playWhenReady, playWhenReadyChangeReason));
    }
    if (previousPlaybackInfo.playbackSuppressionReason != newPlaybackInfo.playbackSuppressionReason) {
        listeners.queueEvent(Player.EVENT_PLAYBACK_SUPPRESSION_REASON_CHANGED, listener -> listener.onPlaybackSuppressionReasonChanged(newPlaybackInfo.playbackSuppressionReason));
    }
    if (isPlaying(previousPlaybackInfo) != isPlaying(newPlaybackInfo)) {
        listeners.queueEvent(Player.EVENT_IS_PLAYING_CHANGED, listener -> listener.onIsPlayingChanged(isPlaying(newPlaybackInfo)));
    }
    if (!previousPlaybackInfo.playbackParameters.equals(newPlaybackInfo.playbackParameters)) {
        listeners.queueEvent(Player.EVENT_PLAYBACK_PARAMETERS_CHANGED, listener -> listener.onPlaybackParametersChanged(newPlaybackInfo.playbackParameters));
    }
    if (seekProcessed) {
        listeners.queueEvent(/* eventFlag= */
        C.INDEX_UNSET, Listener::onSeekProcessed);
    }
    updateAvailableCommands();
    listeners.flushEvents();
    if (previousPlaybackInfo.offloadSchedulingEnabled != newPlaybackInfo.offloadSchedulingEnabled) {
        for (AudioOffloadListener listener : audioOffloadListeners) {
            listener.onExperimentalOffloadSchedulingEnabledChanged(newPlaybackInfo.offloadSchedulingEnabled);
        }
    }
    if (previousPlaybackInfo.sleepingForOffload != newPlaybackInfo.sleepingForOffload) {
        for (AudioOffloadListener listener : audioOffloadListeners) {
            listener.onExperimentalSleepingForOffloadChanged(newPlaybackInfo.sleepingForOffload);
        }
    }
}
Also used : CameraMotionListener(com.google.android.exoplayer2.video.spherical.CameraMotionListener) VideoFrameMetadataListener(com.google.android.exoplayer2.video.VideoFrameMetadataListener) AudioRendererEventListener(com.google.android.exoplayer2.audio.AudioRendererEventListener) AnalyticsListener(com.google.android.exoplayer2.analytics.AnalyticsListener) VideoRendererEventListener(com.google.android.exoplayer2.video.VideoRendererEventListener) SuppressLint(android.annotation.SuppressLint) TrackSelectionArray(com.google.android.exoplayer2.trackselection.TrackSelectionArray) Nullable(androidx.annotation.Nullable)

Example 10 with State

use of com.google.android.exoplayer2.ext.ima.ImaServerSideAdInsertionMediaSource.AdsLoader.State in project ExoPlayer by google.

the class ImaUtil method splitAdPlaybackStateForPeriods.

/**
 * Splits an {@link AdPlaybackState} into a separate {@link AdPlaybackState} for each period of a
 * content timeline.
 *
 * <p>If a period is enclosed by an ad group, the period is considered an ad period. Splitting
 * results in a separate {@link AdPlaybackState ad playback state} for each period that has either
 * no ads or a single ad. In the latter case, the duration of the single ad is set to the duration
 * of the period consuming the entire duration of the period. Accordingly an ad period does not
 * contribute to the duration of the containing window.
 *
 * @param adPlaybackState The ad playback state to be split.
 * @param contentTimeline The content timeline for each period of which to create an {@link
 *     AdPlaybackState}.
 * @return A map of ad playback states for each period UID in the content timeline.
 */
public static ImmutableMap<Object, AdPlaybackState> splitAdPlaybackStateForPeriods(AdPlaybackState adPlaybackState, Timeline contentTimeline) {
    Timeline.Period period = new Timeline.Period();
    if (contentTimeline.getPeriodCount() == 1) {
        // A single period gets the entire ad playback state that may contain multiple ad groups.
        return ImmutableMap.of(checkNotNull(contentTimeline.getPeriod(/* periodIndex= */
        0, period, /* setIds= */
        true).uid), adPlaybackState);
    }
    int periodIndex = 0;
    long totalElapsedContentDurationUs = 0;
    Object adsId = checkNotNull(adPlaybackState.adsId);
    AdPlaybackState contentOnlyAdPlaybackState = new AdPlaybackState(adsId);
    Map<Object, AdPlaybackState> adPlaybackStates = new HashMap<>();
    for (int i = adPlaybackState.removedAdGroupCount; i < adPlaybackState.adGroupCount; i++) {
        AdPlaybackState.AdGroup adGroup = adPlaybackState.getAdGroup(/* adGroupIndex= */
        i);
        if (adGroup.timeUs == C.TIME_END_OF_SOURCE) {
            checkState(i == adPlaybackState.adGroupCount - 1);
            // The last ad group is a placeholder for a potential post roll. We can just stop here.
            break;
        }
        // The ad group start timeUs is in content position. We need to add the ad
        // duration before the ad group to translate the start time to the position in the period.
        long adGroupDurationUs = sum(adGroup.durationsUs);
        long elapsedAdGroupAdDurationUs = 0;
        for (int j = periodIndex; j < contentTimeline.getPeriodCount(); j++) {
            contentTimeline.getPeriod(j, period, /* setIds= */
            true);
            // Subtract one microsecond to work around rounding errors with adGroup.timeUs.
            if (totalElapsedContentDurationUs < adGroup.timeUs - 1) {
                // Period starts before the ad group, so it is a content period.
                adPlaybackStates.put(checkNotNull(period.uid), contentOnlyAdPlaybackState);
                totalElapsedContentDurationUs += period.durationUs;
            } else {
                long periodStartUs = totalElapsedContentDurationUs + elapsedAdGroupAdDurationUs;
                // Add one microsecond to work around rounding errors with adGroup.timeUs.
                if (periodStartUs + period.durationUs <= adGroup.timeUs + adGroupDurationUs + 1) {
                    // The period ends before the end of the ad group, so it is an ad period (Note: A VOD ad
                    // reported by the IMA SDK spans multiple periods before the LOADED event arrives).
                    adPlaybackStates.put(checkNotNull(period.uid), splitAdGroupForPeriod(adsId, adGroup, periodStartUs, period.durationUs));
                    elapsedAdGroupAdDurationUs += period.durationUs;
                } else {
                    // Period is after the current ad group. Continue with next ad group.
                    break;
                }
            }
            // Increment the period index to the next unclassified period.
            periodIndex++;
        }
    }
    // The remaining periods end after the last ad group, so these are content periods.
    for (int i = periodIndex; i < contentTimeline.getPeriodCount(); i++) {
        contentTimeline.getPeriod(i, period, /* setIds= */
        true);
        adPlaybackStates.put(checkNotNull(period.uid), contentOnlyAdPlaybackState);
    }
    return ImmutableMap.copyOf(adPlaybackStates);
}
Also used : Timeline(com.google.android.exoplayer2.Timeline) AdPlaybackState(com.google.android.exoplayer2.source.ads.AdPlaybackState) HashMap(java.util.HashMap)

Aggregations

Test (org.junit.Test)26 TestExoPlayerBuilder (com.google.android.exoplayer2.testutil.TestExoPlayerBuilder)10 Nullable (androidx.annotation.Nullable)9 ServerSideAdInsertionUtil.addAdGroupToAdPlaybackState (com.google.android.exoplayer2.source.ads.ServerSideAdInsertionUtil.addAdGroupToAdPlaybackState)9 ActionSchedule (com.google.android.exoplayer2.testutil.ActionSchedule)9 SinglePeriodTimeline (com.google.android.exoplayer2.source.SinglePeriodTimeline)8 PlayerRunnable (com.google.android.exoplayer2.testutil.ActionSchedule.PlayerRunnable)8 SuppressLint (android.annotation.SuppressLint)7 ExoPlayerTestRunner (com.google.android.exoplayer2.testutil.ExoPlayerTestRunner)7 FakeTimeline (com.google.android.exoplayer2.testutil.FakeTimeline)7 NoUidTimeline (com.google.android.exoplayer2.testutil.NoUidTimeline)7 AdPlaybackState (com.google.android.exoplayer2.source.ads.AdPlaybackState)6 FakeMediaSource (com.google.android.exoplayer2.testutil.FakeMediaSource)6 ArrayList (java.util.ArrayList)6 AnalyticsListener (com.google.android.exoplayer2.analytics.AnalyticsListener)5 Listener (com.google.android.exoplayer2.Player.Listener)4 MediaSource (com.google.android.exoplayer2.source.MediaSource)4 TrackSelection (com.google.android.exoplayer2.trackselection.TrackSelection)4 IOException (java.io.IOException)4 Pair (android.util.Pair)3