/*
* Copyright (C) 2022 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 androidx.media3.exoplayer.source;
import androidx.annotation.Nullable;
import androidx.media3.common.MediaItem;
import androidx.media3.common.Timeline;
import androidx.media3.common.util.UnstableApi;
import androidx.media3.datasource.TransferListener;
import androidx.media3.exoplayer.analytics.PlayerId;
import androidx.media3.exoplayer.upstream.Allocator;
/**
* An abstract {@link MediaSource} wrapping a single child {@link MediaSource}.
*
* <p>The implementation may want to override the following methods as needed:
*
* <ul>
* <li>{@link #getMediaItem()}: Amend the {@link MediaItem} for this media source. This is only
* used before the child source is prepared.
* <li>{@link #onChildSourceInfoRefreshed(Timeline)}: Called whenever the child source's {@link
* Timeline} changed. This {@link Timeline} can be amended if needed, for example using {@link
* ForwardingTimeline}. The {@link Timeline} for the wrapping source needs to be published
* with {@link #refreshSourceInfo(Timeline)}.
* <li>{@link #createPeriod}/{@link #releasePeriod}: These methods create and release {@link
* MediaPeriod} instances. They typically forward to the wrapped media source and optionally
* wrap the returned {@link MediaPeriod}.
* </ul>
*
* <p>Other methods like {@link #prepareSourceInternal}, {@link #enableInternal}, {@link
* #disableInternal} or {@link #releaseSourceInternal} only need to be overwritten if required for
* resource management.
*/
@UnstableApi
public abstract class WrappingMediaSource extends CompositeMediaSource<Void> {
private static final Void CHILD_SOURCE_ID = null;
/** The wrapped child {@link MediaSource}. */
protected final MediaSource mediaSource;
/**
* Creates the wrapping {@link MediaSource}.
*
* @param mediaSource The wrapped child {@link MediaSource}.
*/
protected WrappingMediaSource(MediaSource mediaSource) {
this.mediaSource = mediaSource;
}
@Override
protected final void prepareSourceInternal(@Nullable TransferListener mediaTransferListener) {
super.prepareSourceInternal(mediaTransferListener);
prepareSourceInternal();
}
/**
* Starts source preparation and enables the source, see {@link #prepareSource(MediaSourceCaller,
* TransferListener, PlayerId)}. This method is called at most once until the next call to {@link
* #releaseSourceInternal()}.
*/
protected void prepareSourceInternal() {
prepareChildSource();
}
@Nullable
@Override
public Timeline getInitialTimeline() {
return mediaSource.getInitialTimeline();
}
@Override
public boolean isSingleWindow() {
return mediaSource.isSingleWindow();
}
/**
* Returns the {@link MediaItem} for this media source.
*
* <p>This method can be overridden to amend the {@link MediaItem} of the child source. It is only
* used before the child source is prepared.
*
* @see MediaSource#getMediaItem()
*/
@Override
public MediaItem getMediaItem() {
return mediaSource.getMediaItem();
}
/**
* Creates the requested {@link MediaPeriod}.
*
* <p>This method typically forwards to the wrapped media source and optionally wraps the returned
* {@link MediaPeriod}.
*
* @see MediaSource#createPeriod(MediaPeriodId, Allocator, long)
*/
@Override
public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator, long startPositionUs) {
return mediaSource.createPeriod(id, allocator, startPositionUs);
}
/**
* Releases a {@link MediaPeriod}.
*
* <p>This method typically forwards to the wrapped media source and optionally unwraps the
* provided {@link MediaPeriod}.
*
* @see MediaSource#releasePeriod(MediaPeriod)
*/
@Override
public void releasePeriod(MediaPeriod mediaPeriod) {
mediaSource.releasePeriod(mediaPeriod);
}
@Override
protected final void onChildSourceInfoRefreshed(
Void childSourceId, MediaSource mediaSource, Timeline newTimeline) {
onChildSourceInfoRefreshed(newTimeline);
}
/**
* Called when the child source info has been refreshed.
*
* <p>This {@link Timeline} can be amended if needed, for example using {@link
* ForwardingTimeline}. The {@link Timeline} for the wrapping source needs to be published with
* {@link #refreshSourceInfo(Timeline)}.
*
* @param newTimeline The timeline of the child source.
*/
protected void onChildSourceInfoRefreshed(Timeline newTimeline) {
refreshSourceInfo(newTimeline);
}
@Override
protected final int getWindowIndexForChildWindowIndex(Void childSourceId, int windowIndex) {
return getWindowIndexForChildWindowIndex(windowIndex);
}
/**
* Returns the window index in the wrapping source corresponding to the specified window index in
* a child source. The default implementation does not change the window index.
*
* @param windowIndex A window index of the child source.
* @return The corresponding window index in the wrapping source.
*/
protected int getWindowIndexForChildWindowIndex(int windowIndex) {
return windowIndex;
}
@Nullable
@Override
protected final MediaPeriodId getMediaPeriodIdForChildMediaPeriodId(
Void childSourceId, MediaPeriodId mediaPeriodId) {
return getMediaPeriodIdForChildMediaPeriodId(mediaPeriodId);
}
/**
* Returns the {@link MediaPeriodId} in the wrapping source corresponding to the specified {@link
* MediaPeriodId} in a child source. The default implementation does not change the media period
* id.
*
* @param mediaPeriodId A {@link MediaPeriodId} of the child source.
* @return The corresponding {@link MediaPeriodId} in the wrapping source. Null if no
* corresponding media period id can be determined.
*/
@Nullable
protected MediaPeriodId getMediaPeriodIdForChildMediaPeriodId(MediaPeriodId mediaPeriodId) {
return mediaPeriodId;
}
@Override
protected final long getMediaTimeForChildMediaTime(Void childSourceId, long mediaTimeMs) {
return getMediaTimeForChildMediaTime(mediaTimeMs);
}
/**
* Returns the media time in the {@link MediaPeriod} of the wrapping source corresponding to the
* specified media time in the {@link MediaPeriod} of the child source. The default implementation
* does not change the media time.
*
* @param mediaTimeMs A media time in the {@link MediaPeriod} of the child source, in
* milliseconds.
* @return The corresponding media time in the {@link MediaPeriod} of the wrapping source, in
* milliseconds.
*/
protected long getMediaTimeForChildMediaTime(long mediaTimeMs) {
return mediaTimeMs;
}
/**
* Prepares the wrapped child source.
*
* <p>{@link #onChildSourceInfoRefreshed(Timeline)} will be called when the child source updates
* its timeline.
*
* <p>If sources aren't explicitly released with {@link #releaseChildSource()} they will be
* released in {@link #releaseSourceInternal()}.
*/
protected final void prepareChildSource() {
prepareChildSource(CHILD_SOURCE_ID, mediaSource);
}
/** Enables the child source. */
protected final void enableChildSource() {
enableChildSource(CHILD_SOURCE_ID);
}
/** Disables the child source. */
protected final void disableChildSource() {
disableChildSource(CHILD_SOURCE_ID);
}
/** Releases the child source. */
protected final void releaseChildSource() {
releaseChildSource(CHILD_SOURCE_ID);
}
}