public interface

MediaSource

 androidx.media3.exoplayer.source.MediaSource

Subclasses:

ImaServerSideAdInsertionMediaSource, RtspMediaSource, HlsMediaSource, SsMediaSource, DashMediaSource, MaskingMediaSource, CompositeMediaSource<T>, ConcatenatingMediaSource2, WrappingMediaSource, MergingMediaSource, SilenceMediaSource, ExternallyLoadedMediaSource, ClippingMediaSource, ProgressiveMediaSource, FilteringMediaSource, ConcatenatingMediaSource, LoopingMediaSource, BaseMediaSource, SingleSampleMediaSource, AdsMediaSource, ServerSideAdInsertionMediaSource, PreloadMediaSource, FakeMediaSource, FakeAdaptiveMediaSource

Gradle dependencies

compile group: 'androidx.media3', name: 'media3-exoplayer', version: '1.5.0-alpha01'

  • groupId: androidx.media3
  • artifactId: media3-exoplayer
  • version: 1.5.0-alpha01

Artifact androidx.media3:media3-exoplayer:1.5.0-alpha01 it located at Google repository (https://maven.google.com/)

Overview

Defines and provides media to be played by an ExoPlayer. A MediaSource has two main responsibilities:

MediaSource methods should not be called from application code. Instead, the playback logic in ExoPlayer will trigger methods at the right time.

Instances can be re-used, but only for one ExoPlayer instance simultaneously.

MediaSource methods will be called on one of two threads, an application thread or a playback thread. Each method is documented with the thread it is called on.

Summary

Methods
public voidaddDrmEventListener(Handler handler, DrmSessionEventListener eventListener)

Adds a DrmSessionEventListener to the list of listeners which are notified of DRM events for this media source.

public voidaddEventListener(Handler handler, MediaSourceEventListener eventListener)

Adds a MediaSourceEventListener to the list of listeners which are notified of media source events.

public booleancanUpdateMediaItem(MediaItem mediaItem)

Returns whether the MediaItem for this source can be updated with the provided item.

public MediaPeriodcreatePeriod(MediaSource.MediaPeriodId id, Allocator allocator, long startPositionUs)

Returns a new MediaPeriod identified by periodId.

public voiddisable(MediaSource.MediaSourceCaller caller)

Disables the source for the creation of MediaPeriods.

public voidenable(MediaSource.MediaSourceCaller caller)

Enables the source for the creation of MediaPeriods.

public TimelinegetInitialTimeline()

Returns the initial placeholder timeline that is returned immediately when the real timeline is not yet known, or null to let the player create an initial timeline.

public MediaItemgetMediaItem()

Returns the MediaItem whose media is provided by the source.

public booleanisSingleWindow()

Returns true if the media source is guaranteed to never have zero or more than one window.

public voidmaybeThrowSourceInfoRefreshError()

Throws any pending error encountered while loading or refreshing source information.

public voidprepareSource(MediaSource.MediaSourceCaller caller, TransferListener mediaTransferListener)

public voidprepareSource(MediaSource.MediaSourceCaller caller, TransferListener mediaTransferListener, PlayerId playerId)

Registers a MediaSource.MediaSourceCaller.

public voidreleasePeriod(MediaPeriod mediaPeriod)

Releases the period.

public voidreleaseSource(MediaSource.MediaSourceCaller caller)

Unregisters a caller, and disables and releases the source if no longer required.

public voidremoveDrmEventListener(DrmSessionEventListener eventListener)

Removes a DrmSessionEventListener from the list of listeners which are notified of DRM events for this media source.

public voidremoveEventListener(MediaSourceEventListener eventListener)

Removes a MediaSourceEventListener from the list of listeners which are notified of media source events.

public voidupdateMediaItem(MediaItem mediaItem)

Updates the MediaItem for this source.

Methods

public void addEventListener(Handler handler, MediaSourceEventListener eventListener)

Adds a MediaSourceEventListener to the list of listeners which are notified of media source events.

Should not be called directly from application code.

This method must be called on the playback thread.

Parameters:

handler: A handler on the which listener events will be posted.
eventListener: The listener to be added.

public void removeEventListener(MediaSourceEventListener eventListener)

Removes a MediaSourceEventListener from the list of listeners which are notified of media source events.

Should not be called directly from application code.

This method must be called on the playback thread.

Parameters:

eventListener: The listener to be removed.

public void addDrmEventListener(Handler handler, DrmSessionEventListener eventListener)

Adds a DrmSessionEventListener to the list of listeners which are notified of DRM events for this media source.

Should not be called directly from application code.

This method must be called on the playback thread.

Parameters:

handler: A handler on the which listener events will be posted.
eventListener: The listener to be added.

public void removeDrmEventListener(DrmSessionEventListener eventListener)

Removes a DrmSessionEventListener from the list of listeners which are notified of DRM events for this media source.

Should not be called directly from application code.

This method must be called on the playback thread.

Parameters:

eventListener: The listener to be removed.

public Timeline getInitialTimeline()

Returns the initial placeholder timeline that is returned immediately when the real timeline is not yet known, or null to let the player create an initial timeline.

Should not be called directly from application code.

The initial timeline must use the same uids for windows and periods that the real timeline will use. It also must provide windows which are marked as dynamic to indicate that the window is expected to change when the real timeline arrives.

Any media source which has multiple windows should typically provide such an initial timeline to make sure the player reports the correct number of windows immediately.

This method must be called on the application thread.

public boolean isSingleWindow()

Returns true if the media source is guaranteed to never have zero or more than one window.

Should not be called directly from application code.

The default implementation returns true.

This method must be called on the application thread.

Returns:

true if the source has exactly one window.

public MediaItem getMediaItem()

Returns the MediaItem whose media is provided by the source.

Should not be called directly from application code.

This method must be called on the application thread.

public boolean canUpdateMediaItem(MediaItem mediaItem)

Returns whether the MediaItem for this source can be updated with the provided item.

Should not be called directly from application code.

This method must be called on the application thread.

Parameters:

mediaItem: The new MediaItem.

Returns:

Whether the source can be updated using this item.

public void updateMediaItem(MediaItem mediaItem)

Updates the MediaItem for this source.

Should not be called directly from application code.

This method must be called on the playback thread and only if MediaSource.canUpdateMediaItem(MediaItem) returns true for the new MediaItem.

Parameters:

mediaItem: The new MediaItem.

public void prepareSource(MediaSource.MediaSourceCaller caller, TransferListener mediaTransferListener)

Deprecated: Implement MediaSource.prepareSource(MediaSource.MediaSourceCaller, TransferListener, PlayerId) instead.

public void prepareSource(MediaSource.MediaSourceCaller caller, TransferListener mediaTransferListener, PlayerId playerId)

Registers a MediaSource.MediaSourceCaller. Starts source preparation if needed and enables the source for the creation of MediaPerods.

Should not be called directly from application code.

MediaSource.MediaSourceCaller.onSourceInfoRefreshed(MediaSource, Timeline) will be called once the source has a Timeline.

For each call to this method, a call to MediaSource.releaseSource(MediaSource.MediaSourceCaller) is needed to remove the caller and to release the source if no longer required.

This method must be called on the playback thread.

Parameters:

caller: The MediaSource.MediaSourceCaller to be registered.
mediaTransferListener: The transfer listener which should be informed of any media data transfers. May be null if no listener is available. Note that this listener should be only informed of transfers related to the media loads and not of auxiliary loads for manifests and other data.
playerId: The PlayerId of the player using this media source.

public void maybeThrowSourceInfoRefreshError()

Throws any pending error encountered while loading or refreshing source information.

Should not be called directly from application code.

This method must be called on the playback thread and only after MediaSource.prepareSource(MediaSource.MediaSourceCaller, TransferListener, PlayerId).

public void enable(MediaSource.MediaSourceCaller caller)

Enables the source for the creation of MediaPeriods.

Should not be called directly from application code.

This method must be called on the playback thread and only after MediaSource.prepareSource(MediaSource.MediaSourceCaller, TransferListener, PlayerId).

Parameters:

caller: The MediaSource.MediaSourceCaller enabling the source.

public MediaPeriod createPeriod(MediaSource.MediaPeriodId id, Allocator allocator, long startPositionUs)

Returns a new MediaPeriod identified by periodId.

Should not be called directly from application code.

This method must be called on the playback thread and only if the source is enabled.

Parameters:

id: The identifier of the period.
allocator: An Allocator from which to obtain media buffer allocations.
startPositionUs: The expected start position, in microseconds.

Returns:

A new MediaPeriod.

public void releasePeriod(MediaPeriod mediaPeriod)

Releases the period.

Should not be called directly from application code.

This method must be called on the playback thread.

Parameters:

mediaPeriod: The period to release.

public void disable(MediaSource.MediaSourceCaller caller)

Disables the source for the creation of MediaPeriods. The implementation should not hold onto limited resources used for the creation of media periods.

Should not be called directly from application code.

This method must be called on the playback thread and only after all MediaPeriods previously created by MediaSource.createPeriod(MediaSource.MediaPeriodId, Allocator, long) have been released by MediaSource.releasePeriod(MediaPeriod).

Parameters:

caller: The MediaSource.MediaSourceCaller disabling the source.

public void releaseSource(MediaSource.MediaSourceCaller caller)

Unregisters a caller, and disables and releases the source if no longer required.

Should not be called directly from application code.

This method must be called on the playback thread and only if all created MediaPeriods have been released by MediaSource.releasePeriod(MediaPeriod).

Parameters:

caller: The MediaSource.MediaSourceCaller to be unregistered.

Source

/*
 * Copyright (C) 2016 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 android.os.Handler;
import androidx.annotation.Nullable;
import androidx.media3.common.C;
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.ExoPlayer;
import androidx.media3.exoplayer.analytics.PlayerId;
import androidx.media3.exoplayer.drm.DrmSessionEventListener;
import androidx.media3.exoplayer.drm.DrmSessionManager;
import androidx.media3.exoplayer.drm.DrmSessionManagerProvider;
import androidx.media3.exoplayer.upstream.Allocator;
import androidx.media3.exoplayer.upstream.CmcdConfiguration;
import androidx.media3.exoplayer.upstream.LoadErrorHandlingPolicy;
import androidx.media3.extractor.text.SubtitleParser;
import java.io.IOException;

/**
 * Defines and provides media to be played by an {@link ExoPlayer}. A MediaSource has two main
 * responsibilities:
 *
 * <ul>
 *   <li>To provide the player with a {@link Timeline} defining the structure of its media, and to
 *       provide a new timeline whenever the structure of the media changes. The MediaSource
 *       provides these timelines by calling {@link MediaSourceCaller#onSourceInfoRefreshed} on the
 *       {@link MediaSourceCaller}s passed to {@link #prepareSource(MediaSourceCaller,
 *       TransferListener, PlayerId)}.
 *   <li>To provide {@link MediaPeriod} instances for the periods in its timeline. MediaPeriods are
 *       obtained by calling {@link #createPeriod(MediaPeriodId, Allocator, long)}, and provide a
 *       way for the player to load and read the media.
 * </ul>
 *
 * <p>{@code MediaSource} methods should not be called from application code. Instead, the playback
 * logic in {@link ExoPlayer} will trigger methods at the right time.
 *
 * <p>Instances can be re-used, but only for one {@link ExoPlayer} instance simultaneously.
 *
 * <p>MediaSource methods will be called on one of two threads, an application thread or a playback
 * thread. Each method is documented with the thread it is called on.
 */
public interface MediaSource {

  /** Factory for creating {@link MediaSource MediaSources} from {@link MediaItem MediaItems}. */
  interface Factory {

    /**
     * An instance that throws {@link UnsupportedOperationException} from {@link #createMediaSource}
     * and {@link #getSupportedTypes()}.
     */
    @UnstableApi
    @SuppressWarnings("deprecation")
    Factory UNSUPPORTED = MediaSourceFactory.UNSUPPORTED;

    /**
     * Sets the {@link CmcdConfiguration.Factory} used to obtain a {@link CmcdConfiguration} for a
     * {@link MediaItem}.
     *
     * @return This factory, for convenience.
     */
    @UnstableApi
    default Factory setCmcdConfigurationFactory(
        CmcdConfiguration.Factory cmcdConfigurationFactory) {
      // do nothing
      return this;
    }

    /**
     * Sets the {@link DrmSessionManagerProvider} used to obtain a {@link DrmSessionManager} for a
     * {@link MediaItem}.
     *
     * @return This factory, for convenience.
     */
    @UnstableApi
    Factory setDrmSessionManagerProvider(DrmSessionManagerProvider drmSessionManagerProvider);

    /**
     * Sets an optional {@link LoadErrorHandlingPolicy}.
     *
     * @return This factory, for convenience.
     */
    @UnstableApi
    Factory setLoadErrorHandlingPolicy(LoadErrorHandlingPolicy loadErrorHandlingPolicy);

    /**
     * Sets whether subtitles should be parsed as part of extraction (before being added to the
     * sample queue) or as part of rendering (when being taken from the sample queue). Defaults to
     * {@code true} (i.e. subtitles will be parsed during extraction).
     *
     * <p>This method is experimental and will be renamed or removed in a future release.
     *
     * @deprecated This method (and all support for 'legacy' subtitle decoding during rendering)
     *     will be removed in a future release.
     * @param parseSubtitlesDuringExtraction Whether to parse subtitles during extraction or
     *     rendering.
     * @return This factory, for convenience.
     */
    @UnstableApi
    @Deprecated
    default Factory experimentalParseSubtitlesDuringExtraction(
        boolean parseSubtitlesDuringExtraction) {
      return this;
    }

    /**
     * Sets the {@link SubtitleParser.Factory} to be used for parsing subtitles during extraction.
     *
     * @param subtitleParserFactory The {@link SubtitleParser.Factory} for parsing subtitles during
     *     extraction.
     * @return This factory, for convenience.
     */
    @UnstableApi
    default Factory setSubtitleParserFactory(SubtitleParser.Factory subtitleParserFactory) {
      return this;
    }

    /**
     * Returns the {@link C.ContentType content types} supported by media sources created by this
     * factory.
     */
    @UnstableApi
    @C.ContentType
    int[] getSupportedTypes();

    /**
     * Creates a new {@link MediaSource} with the specified {@link MediaItem}.
     *
     * @param mediaItem The media item to play.
     * @return The new {@link MediaSource media source}.
     */
    @UnstableApi
    MediaSource createMediaSource(MediaItem mediaItem);
  }

  /** A caller of media sources, which will be notified of source events. */
  @UnstableApi
  interface MediaSourceCaller {

    /**
     * Called when the {@link Timeline} has been refreshed.
     *
     * <p>Called on the playback thread.
     *
     * @param source The {@link MediaSource} whose info has been refreshed.
     * @param timeline The source's timeline.
     */
    void onSourceInfoRefreshed(MediaSource source, Timeline timeline);
  }

  /** Identifier for a {@link MediaPeriod}. */
  @UnstableApi
  final class MediaPeriodId {

    /** The unique id of the timeline period. */
    public final Object periodUid;

    /**
     * If the media period is in an ad group, the index of the ad group in the period. {@link
     * C#INDEX_UNSET} otherwise.
     */
    public final int adGroupIndex;

    /**
     * If the media period is in an ad group, the index of the ad in its ad group in the period.
     * {@link C#INDEX_UNSET} otherwise.
     */
    public final int adIndexInAdGroup;

    /**
     * The sequence number of the window in the buffered sequence of windows this media period is
     * part of. {@link C#INDEX_UNSET} if the media period id is not part of a buffered sequence of
     * windows.
     */
    public final long windowSequenceNumber;

    /**
     * The index of the next ad group to which the media period's content is clipped, or {@link
     * C#INDEX_UNSET} if there is no following ad group or if this media period is an ad.
     */
    public final int nextAdGroupIndex;

    /**
     * Creates a media period identifier for a period which is not part of a buffered sequence of
     * windows.
     *
     * @param periodUid The unique id of the timeline period.
     */
    public MediaPeriodId(Object periodUid) {
      this(periodUid, /* windowSequenceNumber= */ C.INDEX_UNSET);
    }

    /**
     * Creates a media period identifier for the specified period in the timeline.
     *
     * @param periodUid The unique id of the timeline period.
     * @param windowSequenceNumber The sequence number of the window in the buffered sequence of
     *     windows this media period is part of.
     */
    public MediaPeriodId(Object periodUid, long windowSequenceNumber) {
      this(
          periodUid,
          /* adGroupIndex= */ C.INDEX_UNSET,
          /* adIndexInAdGroup= */ C.INDEX_UNSET,
          windowSequenceNumber,
          /* nextAdGroupIndex= */ C.INDEX_UNSET);
    }

    /**
     * Creates a media period identifier for the specified clipped period in the timeline.
     *
     * @param periodUid The unique id of the timeline period.
     * @param windowSequenceNumber The sequence number of the window in the buffered sequence of
     *     windows this media period is part of.
     * @param nextAdGroupIndex The index of the next ad group to which the media period's content is
     *     clipped.
     */
    public MediaPeriodId(Object periodUid, long windowSequenceNumber, int nextAdGroupIndex) {
      this(
          periodUid,
          /* adGroupIndex= */ C.INDEX_UNSET,
          /* adIndexInAdGroup= */ C.INDEX_UNSET,
          windowSequenceNumber,
          nextAdGroupIndex);
    }

    /**
     * Creates a media period identifier that identifies an ad within an ad group at the specified
     * timeline period.
     *
     * @param periodUid The unique id of the timeline period that contains the ad group.
     * @param adGroupIndex The index of the ad group.
     * @param adIndexInAdGroup The index of the ad in the ad group.
     * @param windowSequenceNumber The sequence number of the window in the buffered sequence of
     *     windows this media period is part of.
     */
    public MediaPeriodId(
        Object periodUid, int adGroupIndex, int adIndexInAdGroup, long windowSequenceNumber) {
      this(
          periodUid,
          adGroupIndex,
          adIndexInAdGroup,
          windowSequenceNumber,
          /* nextAdGroupIndex= */ C.INDEX_UNSET);
    }

    private MediaPeriodId(
        Object periodUid,
        int adGroupIndex,
        int adIndexInAdGroup,
        long windowSequenceNumber,
        int nextAdGroupIndex) {
      this.periodUid = periodUid;
      this.adGroupIndex = adGroupIndex;
      this.adIndexInAdGroup = adIndexInAdGroup;
      this.windowSequenceNumber = windowSequenceNumber;
      this.nextAdGroupIndex = nextAdGroupIndex;
    }

    /** Returns a copy of this period identifier but with {@code newPeriodUid} as its period uid. */
    public MediaPeriodId copyWithPeriodUid(Object newPeriodUid) {
      return periodUid.equals(newPeriodUid)
          ? this
          : new MediaPeriodId(
              newPeriodUid, adGroupIndex, adIndexInAdGroup, windowSequenceNumber, nextAdGroupIndex);
    }

    /** Returns a copy of this period identifier with a new {@code windowSequenceNumber}. */
    public MediaPeriodId copyWithWindowSequenceNumber(long windowSequenceNumber) {
      return this.windowSequenceNumber == windowSequenceNumber
          ? this
          : new MediaPeriodId(
              periodUid, adGroupIndex, adIndexInAdGroup, windowSequenceNumber, nextAdGroupIndex);
    }

    /** Returns whether this period identifier identifies an ad in an ad group in a period. */
    public boolean isAd() {
      return adGroupIndex != C.INDEX_UNSET;
    }

    @Override
    public boolean equals(@Nullable Object obj) {
      if (this == obj) {
        return true;
      }
      if (!(obj instanceof MediaPeriodId)) {
        return false;
      }

      MediaPeriodId periodId = (MediaPeriodId) obj;
      return periodUid.equals(periodId.periodUid)
          && adGroupIndex == periodId.adGroupIndex
          && adIndexInAdGroup == periodId.adIndexInAdGroup
          && windowSequenceNumber == periodId.windowSequenceNumber
          && nextAdGroupIndex == periodId.nextAdGroupIndex;
    }

    @Override
    public int hashCode() {
      int result = 17;
      result = 31 * result + periodUid.hashCode();
      result = 31 * result + adGroupIndex;
      result = 31 * result + adIndexInAdGroup;
      result = 31 * result + (int) windowSequenceNumber;
      result = 31 * result + nextAdGroupIndex;
      return result;
    }
  }

  /**
   * Adds a {@link MediaSourceEventListener} to the list of listeners which are notified of media
   * source events.
   *
   * <p>Should not be called directly from application code.
   *
   * <p>This method must be called on the playback thread.
   *
   * @param handler A handler on the which listener events will be posted.
   * @param eventListener The listener to be added.
   */
  @UnstableApi
  void addEventListener(Handler handler, MediaSourceEventListener eventListener);

  /**
   * Removes a {@link MediaSourceEventListener} from the list of listeners which are notified of
   * media source events.
   *
   * <p>Should not be called directly from application code.
   *
   * <p>This method must be called on the playback thread.
   *
   * @param eventListener The listener to be removed.
   */
  @UnstableApi
  void removeEventListener(MediaSourceEventListener eventListener);

  /**
   * Adds a {@link DrmSessionEventListener} to the list of listeners which are notified of DRM
   * events for this media source.
   *
   * <p>Should not be called directly from application code.
   *
   * <p>This method must be called on the playback thread.
   *
   * @param handler A handler on the which listener events will be posted.
   * @param eventListener The listener to be added.
   */
  @UnstableApi
  void addDrmEventListener(Handler handler, DrmSessionEventListener eventListener);

  /**
   * Removes a {@link DrmSessionEventListener} from the list of listeners which are notified of DRM
   * events for this media source.
   *
   * <p>Should not be called directly from application code.
   *
   * <p>This method must be called on the playback thread.
   *
   * @param eventListener The listener to be removed.
   */
  @UnstableApi
  void removeDrmEventListener(DrmSessionEventListener eventListener);

  /**
   * Returns the initial placeholder timeline that is returned immediately when the real timeline is
   * not yet known, or null to let the player create an initial timeline.
   *
   * <p>Should not be called directly from application code.
   *
   * <p>The initial timeline must use the same uids for windows and periods that the real timeline
   * will use. It also must provide windows which are marked as dynamic to indicate that the window
   * is expected to change when the real timeline arrives.
   *
   * <p>Any media source which has multiple windows should typically provide such an initial
   * timeline to make sure the player reports the correct number of windows immediately.
   *
   * <p>This method must be called on the application thread.
   */
  @UnstableApi
  @Nullable
  default Timeline getInitialTimeline() {
    return null;
  }

  /**
   * Returns true if the media source is guaranteed to never have zero or more than one window.
   *
   * <p>Should not be called directly from application code.
   *
   * <p>The default implementation returns {@code true}.
   *
   * <p>This method must be called on the application thread.
   *
   * @return true if the source has exactly one window.
   */
  @UnstableApi
  default boolean isSingleWindow() {
    return true;
  }

  /**
   * Returns the {@link MediaItem} whose media is provided by the source.
   *
   * <p>Should not be called directly from application code.
   *
   * <p>This method must be called on the application thread.
   */
  @UnstableApi
  MediaItem getMediaItem();

  /**
   * Returns whether the {@link MediaItem} for this source can be updated with the provided item.
   *
   * <p>Should not be called directly from application code.
   *
   * <p>This method must be called on the application thread.
   *
   * @param mediaItem The new {@link MediaItem}.
   * @return Whether the source can be updated using this item.
   */
  @UnstableApi
  default boolean canUpdateMediaItem(MediaItem mediaItem) {
    return false;
  }

  /**
   * Updates the {@link MediaItem} for this source.
   *
   * <p>Should not be called directly from application code.
   *
   * <p>This method must be called on the playback thread and only if {@link #canUpdateMediaItem}
   * returns {@code true} for the new {@link MediaItem}.
   *
   * @param mediaItem The new {@link MediaItem}.
   */
  @UnstableApi
  default void updateMediaItem(MediaItem mediaItem) {}

  /**
   * @deprecated Implement {@link #prepareSource(MediaSourceCaller, TransferListener, PlayerId)}
   *     instead.
   */
  @UnstableApi
  @Deprecated
  default void prepareSource(
      MediaSourceCaller caller, @Nullable TransferListener mediaTransferListener) {
    prepareSource(caller, mediaTransferListener, PlayerId.UNSET);
  }

  /**
   * Registers a {@link MediaSourceCaller}. Starts source preparation if needed and enables the
   * source for the creation of {@link MediaPeriod MediaPerods}.
   *
   * <p>Should not be called directly from application code.
   *
   * <p>{@link MediaSourceCaller#onSourceInfoRefreshed(MediaSource, Timeline)} will be called once
   * the source has a {@link Timeline}.
   *
   * <p>For each call to this method, a call to {@link #releaseSource(MediaSourceCaller)} is needed
   * to remove the caller and to release the source if no longer required.
   *
   * <p>This method must be called on the playback thread.
   *
   * @param caller The {@link MediaSourceCaller} to be registered.
   * @param mediaTransferListener The transfer listener which should be informed of any media data
   *     transfers. May be null if no listener is available. Note that this listener should be only
   *     informed of transfers related to the media loads and not of auxiliary loads for manifests
   *     and other data.
   * @param playerId The {@link PlayerId} of the player using this media source.
   */
  @UnstableApi
  void prepareSource(
      MediaSourceCaller caller,
      @Nullable TransferListener mediaTransferListener,
      PlayerId playerId);

  /**
   * Throws any pending error encountered while loading or refreshing source information.
   *
   * <p>Should not be called directly from application code.
   *
   * <p>This method must be called on the playback thread and only after {@link
   * #prepareSource(MediaSourceCaller, TransferListener, PlayerId)}.
   */
  @UnstableApi
  void maybeThrowSourceInfoRefreshError() throws IOException;

  /**
   * Enables the source for the creation of {@link MediaPeriod MediaPeriods}.
   *
   * <p>Should not be called directly from application code.
   *
   * <p>This method must be called on the playback thread and only after {@link
   * #prepareSource(MediaSourceCaller, TransferListener, PlayerId)}.
   *
   * @param caller The {@link MediaSourceCaller} enabling the source.
   */
  @UnstableApi
  void enable(MediaSourceCaller caller);

  /**
   * Returns a new {@link MediaPeriod} identified by {@code periodId}.
   *
   * <p>Should not be called directly from application code.
   *
   * <p>This method must be called on the playback thread and only if the source is enabled.
   *
   * @param id The identifier of the period.
   * @param allocator An {@link Allocator} from which to obtain media buffer allocations.
   * @param startPositionUs The expected start position, in microseconds.
   * @return A new {@link MediaPeriod}.
   */
  @UnstableApi
  MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator, long startPositionUs);

  /**
   * Releases the period.
   *
   * <p>Should not be called directly from application code.
   *
   * <p>This method must be called on the playback thread.
   *
   * @param mediaPeriod The period to release.
   */
  @UnstableApi
  void releasePeriod(MediaPeriod mediaPeriod);

  /**
   * Disables the source for the creation of {@link MediaPeriod MediaPeriods}. The implementation
   * should not hold onto limited resources used for the creation of media periods.
   *
   * <p>Should not be called directly from application code.
   *
   * <p>This method must be called on the playback thread and only after all {@link MediaPeriod
   * MediaPeriods} previously created by {@link #createPeriod(MediaPeriodId, Allocator, long)} have
   * been released by {@link #releasePeriod(MediaPeriod)}.
   *
   * @param caller The {@link MediaSourceCaller} disabling the source.
   */
  @UnstableApi
  void disable(MediaSourceCaller caller);

  /**
   * Unregisters a caller, and disables and releases the source if no longer required.
   *
   * <p>Should not be called directly from application code.
   *
   * <p>This method must be called on the playback thread and only if all created {@link MediaPeriod
   * MediaPeriods} have been released by {@link #releasePeriod(MediaPeriod)}.
   *
   * @param caller The {@link MediaSourceCaller} to be unregistered.
   */
  @UnstableApi
  void releaseSource(MediaSourceCaller caller);
}