add media item to create media sources

This change adds the createMediaSource(MediaItem mediaItem) method to the MediaSourceFactory interface. It doesn't deprecate createMediaSource(Uri uri) to keep the cl smaller. Deprecation and removing calls to the deprecated method from within the library and extension follow in a separate CL.

PiperOrigin-RevId: 298352442
This commit is contained in:
bachinger 2020-03-02 15:56:44 +00:00 committed by Oliver Woodman
parent 9c1c74ecc4
commit d1bbd3507a
9 changed files with 765 additions and 73 deletions

View File

@ -35,6 +35,7 @@ import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.C.ContentType;
import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.MediaItem;
import com.google.android.exoplayer2.PlaybackPreparer;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.RenderersFactory;
@ -74,6 +75,7 @@ import com.google.android.exoplayer2.ui.PlayerView;
import com.google.android.exoplayer2.ui.spherical.SphericalGLSurfaceView;
import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.HttpDataSource;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.ErrorMessageProvider;
import com.google.android.exoplayer2.util.EventLogger;
import com.google.android.exoplayer2.util.Util;
@ -636,9 +638,12 @@ public class PlayerActivity extends AppCompatActivity
@Override
@NonNull
public MediaSource createMediaSource(@NonNull Uri uri) {
public MediaSource createMediaSource(@NonNull MediaItem mediaItem) {
Assertions.checkNotNull(mediaItem.playbackProperties);
return PlayerActivity.this.createLeafMediaSource(
uri, /* extension=*/ null, drmSessionManager);
mediaItem.playbackProperties.sourceUri,
mediaItem.playbackProperties.extension,
drmSessionManager);
}
@Override

View File

@ -0,0 +1,448 @@
/*
* Copyright 2020 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;
import android.net.Uri;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.offline.StreamKey;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Util;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
/** Representation of a media item. */
public final class MediaItem {
/**
* Creates a {@link MediaItem} for the given source uri.
*
* @param sourceUri The source uri.
* @return An {@link MediaItem} for the given source uri.
*/
public static MediaItem fromUri(String sourceUri) {
return new MediaItem.Builder().setSourceUri(sourceUri).build();
}
/**
* Creates a {@link MediaItem} for the given {@link Uri source uri}.
*
* @param sourceUri The {@link Uri source uri}.
* @return An {@link MediaItem} for the given source uri.
*/
public static MediaItem fromUri(Uri sourceUri) {
return new MediaItem.Builder().setSourceUri(sourceUri).build();
}
/** A builder for {@link MediaItem} instances. */
public static final class Builder {
@Nullable private String mediaId;
@Nullable private Uri sourceUri;
@Nullable private String extension;
@Nullable private UriBundle drmLicenseUri;
@Nullable private UUID drmUuid;
private boolean drmMultiSession;
private List<StreamKey> streamKeys;
@Nullable private Object tag;
/** Creates a builder. */
public Builder() {
streamKeys = Collections.emptyList();
}
/**
* Sets the optional media id which identifies the media item. If not specified, {@code
* #setSourceUri} must be called and the string representation of {@link
* PlaybackProperties#sourceUri} is used as the media id.
*
* @throws IllegalStateException If {@link #build()} has already been called.
*/
public Builder setMediaId(@Nullable String mediaId) {
this.mediaId = mediaId;
return this;
}
/**
* Sets the optional source uri. If not specified, {@link #setMediaId(String)} must be called.
*
* @throws IllegalStateException If {@link #build()} has already been called.
*/
public Builder setSourceUri(@Nullable String sourceUri) {
return setSourceUri(sourceUri == null ? null : Uri.parse(sourceUri));
}
/**
* Sets the optional source {@link Uri}. If not specified, {@link #setMediaId(String)} must be
* called.
*
* @throws IllegalStateException If {@link #build()} has already been called.
*/
public Builder setSourceUri(@Nullable Uri sourceUri) {
this.sourceUri = sourceUri;
return this;
}
/**
* Sets the optional extension of the item.
*
* <p>The extension can be used to disambiguate media items that have a uri which does not allow
* to infer the actual media type.
*
* <p>If a {@link PlaybackProperties#sourceUri} is set, the extension is used to create a {@link
* PlaybackProperties} object. Otherwise it will be ignored.
*
* @throws IllegalStateException If {@link #build()} has already been called.
*/
public Builder setExtension(@Nullable String extension) {
this.extension = extension;
return this;
}
/**
* Sets the optional license server {@link UriBundle}. If a license uri is set, the {@link
* DrmConfiguration#uuid} needs to be specified as well.
*
* <p>if a {@link PlaybackProperties#sourceUri} is set, the drm license uri is used to create a
* {@link PlaybackProperties} object. Otherwise it will be ignored.
*
* @throws IllegalStateException If {@link #build()} has already been called.
*/
public Builder setDrmLicenseUri(@Nullable UriBundle licenseUri) {
drmLicenseUri = licenseUri;
return this;
}
/**
* Sets the optional license server {@link Uri}. If a license uri is set, the {@link
* DrmConfiguration#uuid} needs to be specified as well.
*
* <p>If a {@link PlaybackProperties#sourceUri} is set, the drm license uri is used to create a
* {@link PlaybackProperties} object. Otherwise it will be ignored.
*
* @throws IllegalStateException If {@link #build()} has already been called.
*/
public Builder setDrmLicenseUri(@Nullable Uri licenseUri) {
drmLicenseUri = licenseUri == null ? null : new UriBundle(licenseUri);
return this;
}
/**
* Sets the optional license server uri as a {@link String}. If a license uri is set, the {@link
* DrmConfiguration#uuid} needs to be specified as well.
*
* <p>If a {@link PlaybackProperties#sourceUri} is set, the drm license uri is used to create a
* {@link PlaybackProperties} object. Otherwise it will be ignored.
*
* @throws IllegalStateException If {@link #build()} has already been called.
*/
public Builder setDrmLicenseUri(@Nullable String licenseUri) {
drmLicenseUri = licenseUri == null ? null : new UriBundle(Uri.parse(licenseUri));
return this;
}
/**
* Sets the {@link UUID} of the protection scheme. If a drm system uuid is set, the {@link
* DrmConfiguration#licenseUri} needs to be set as well.
*
* <p>If a {@link PlaybackProperties#sourceUri} is set, the drm system uuid is used to create a
* {@link PlaybackProperties} object. Otherwise it will be ignored.
*
* @throws IllegalStateException If {@link #build()} has already been called.
*/
public Builder setDrmUuid(@Nullable UUID uuid) {
drmUuid = uuid;
return this;
}
/**
* Sets whether the drm configuration is multi session enabled.
*
* <p>If a {@link PlaybackProperties#sourceUri} is set, the drm multi session flag is used to
* create a {@link PlaybackProperties} object. Otherwise it will be ignored.
*
* @throws IllegalStateException If {@link #build()} has already been called.
*/
public Builder setDrmMultiSession(boolean multiSession) {
drmMultiSession = multiSession;
return this;
}
/**
* Sets the optional stream keys by which the manifest is filtered (only used for adaptive
* streams).
*
* <p>If a {@link PlaybackProperties#sourceUri} is set, the stream keys are used to create a
* {@link PlaybackProperties} object. Otherwise it will be ignored.
*
* @throws IllegalStateException If {@link #build()} has already been called.
*/
public Builder setStreamKeys(@Nullable List<StreamKey> streamKeys) {
this.streamKeys =
streamKeys != null && !streamKeys.isEmpty()
? Collections.unmodifiableList(new ArrayList<>(streamKeys))
: Collections.emptyList();
return this;
}
/**
* Sets the optional tag for custom attributes. The tag for the media source which will be
* published in the {@link com.google.android.exoplayer2.Timeline} of the source as {@link
* com.google.android.exoplayer2.Timeline.Window#tag}.
*
* <p>If a {@link PlaybackProperties#sourceUri} is set, the tag is used to create a {@link
* PlaybackProperties} object. Otherwise it will be ignored.
*
* @throws IllegalStateException If {@link #build()} has already been called.
*/
public Builder setTag(@Nullable Object tag) {
this.tag = tag;
return this;
}
/**
* Returns a new {@link MediaItem} instance with the current builder values.
*
* @throws IllegalStateException If a required property is not set.
*/
public MediaItem build() {
Assertions.checkState(drmLicenseUri == null || drmUuid != null);
@Nullable PlaybackProperties playbackProperties = null;
if (sourceUri != null) {
playbackProperties =
new PlaybackProperties(
sourceUri,
extension,
drmUuid != null
? new DrmConfiguration(drmUuid, drmLicenseUri, drmMultiSession)
: null,
streamKeys,
tag);
mediaId = mediaId != null ? mediaId : sourceUri.toString();
}
return new MediaItem(Assertions.checkNotNull(mediaId), playbackProperties);
}
}
/** Bundles a resource's URI with headers to attach to any request to that URI. */
public static final class UriBundle {
/** An empty {@link UriBundle}. */
public static final UriBundle EMPTY = new UriBundle(Uri.EMPTY);
/** A URI. */
public final Uri uri;
/** The headers to attach to any request for the given URI. */
public final Map<String, String> requestHeaders;
/**
* Creates an instance with no request headers.
*
* @param uri See {@link #uri}.
*/
public UriBundle(Uri uri) {
this(uri, Collections.emptyMap());
}
/**
* Creates an instance with the given URI and request headers.
*
* @param uri See {@link #uri}.
* @param requestHeaders See {@link #requestHeaders}.
*/
public UriBundle(Uri uri, Map<String, String> requestHeaders) {
this.uri = uri;
this.requestHeaders = Collections.unmodifiableMap(new HashMap<>(requestHeaders));
}
@Override
public boolean equals(@Nullable Object other) {
if (this == other) {
return true;
}
if (other == null || getClass() != other.getClass()) {
return false;
}
UriBundle uriBundle = (UriBundle) other;
return uri.equals(uriBundle.uri) && requestHeaders.equals(uriBundle.requestHeaders);
}
@Override
public int hashCode() {
int result = uri.hashCode();
result = 31 * result + requestHeaders.hashCode();
return result;
}
}
/** DRM configuration for a media item. */
public static final class DrmConfiguration {
/** The UUID of the protection scheme. */
public final UUID uuid;
/**
* Optional license server {@link Uri}. If {@code null} then the license server must be
* specified by the media.
*/
@Nullable public final UriBundle licenseUri;
/** Whether the drm configuration is multi session enabled. */
public final boolean multiSession;
/**
* Creates an instance.
*
* @param uuid See {@link #uuid}.
* @param licenseUri See {@link #licenseUri}.
* @param multiSession See {@link #multiSession}.
*/
public DrmConfiguration(UUID uuid, @Nullable UriBundle licenseUri, boolean multiSession) {
this.uuid = uuid;
this.licenseUri = licenseUri;
this.multiSession = multiSession;
}
@Override
public boolean equals(@Nullable Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
DrmConfiguration other = (DrmConfiguration) obj;
return uuid.equals(other.uuid)
&& Util.areEqual(licenseUri, other.licenseUri)
&& multiSession == other.multiSession;
}
@Override
public int hashCode() {
int result = uuid.hashCode();
result = 31 * result + (licenseUri != null ? licenseUri.hashCode() : 0);
result = 31 * result + (multiSession ? 1 : 0);
return result;
}
}
/** Properties for local playback. */
public static final class PlaybackProperties {
/** The source {@link Uri}. */
public final Uri sourceUri;
/**
* The optional extension of the item, or {@code null} if unspecified.
*
* <p>The extension can be used to disambiguate media items that have a uri which does not allow
* to infer the actual media type.
*/
@Nullable public final String extension;
/** Optional {@link DrmConfiguration} for the media. */
@Nullable public final DrmConfiguration drmConfiguration;
/** Optional stream keys by which the manifest is filtered. */
public final List<StreamKey> streamKeys;
/**
* Optional tag for custom attributes. The tag for the media source which will be published in
* the {@link com.google.android.exoplayer2.Timeline} of the source as {@link
* com.google.android.exoplayer2.Timeline.Window#tag}.
*/
@Nullable public final Object tag;
public PlaybackProperties(
Uri sourceUri,
@Nullable String extension,
@Nullable DrmConfiguration drmConfiguration,
List<StreamKey> streamKeys,
@Nullable Object tag) {
this.sourceUri = sourceUri;
this.extension = extension;
this.drmConfiguration = drmConfiguration;
this.streamKeys = streamKeys;
this.tag = tag;
}
@Override
public boolean equals(@Nullable Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
PlaybackProperties other = (PlaybackProperties) obj;
return sourceUri.equals(other.sourceUri)
&& Util.areEqual(extension, other.extension)
&& Util.areEqual(drmConfiguration, other.drmConfiguration)
&& Util.areEqual(streamKeys, other.streamKeys)
&& Util.areEqual(tag, other.tag);
}
@Override
public int hashCode() {
int result = sourceUri.hashCode();
result = 31 * result + (extension == null ? 0 : extension.hashCode());
result = 31 * result + (drmConfiguration == null ? 0 : drmConfiguration.hashCode());
result = 31 * result + streamKeys.hashCode();
result = 31 * result + (tag == null ? 0 : tag.hashCode());
return result;
}
}
/** Identifies the media item. */
public final String mediaId;
/** Optional playback properties. */
@Nullable public final PlaybackProperties playbackProperties;
private MediaItem(String mediaId, @Nullable PlaybackProperties playbackProperties) {
this.mediaId = mediaId;
this.playbackProperties = playbackProperties;
}
@Override
public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
MediaItem other = (MediaItem) o;
return Util.areEqual(mediaId, other.mediaId)
&& Util.areEqual(playbackProperties, other.playbackProperties);
}
@Override
public int hashCode() {
int result = mediaId.hashCode();
result = 31 * result + (playbackProperties != null ? playbackProperties.hashCode() : 0);
return result;
}
}

View File

@ -19,6 +19,7 @@ import android.net.Uri;
import android.os.Handler;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.MediaItem;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.drm.DrmSessionManager;
@ -111,13 +112,10 @@ public final class ExtractorMediaSource extends CompositeMediaSource<Void> {
}
/**
* Sets a tag for the media source which will be published in the {@link
* com.google.android.exoplayer2.Timeline} of the source as {@link
* com.google.android.exoplayer2.Timeline.Window#tag}.
*
* @param tag A tag for the media source.
* @return This factory, for convenience.
* @deprecated Use {@link MediaItem.PlaybackProperties#tag} and {@link
* #createMediaSource(MediaItem)} instead.
*/
@Deprecated
public Factory setTag(@Nullable Object tag) {
this.tag = tag;
return this;
@ -172,19 +170,32 @@ public final class ExtractorMediaSource extends CompositeMediaSource<Void> {
/**
* Returns a new {@link ExtractorMediaSource} using the current parameters.
*
* @param uri The {@link Uri}.
* @param uri The {@link Uri uri}.
* @return The new {@link ExtractorMediaSource}.
*/
@Override
public ExtractorMediaSource createMediaSource(Uri uri) {
return createMediaSource(new MediaItem.Builder().setSourceUri(uri).build());
}
/**
* Returns a new {@link ExtractorMediaSource} using the current parameters.
*
* @param mediaItem The {@link MediaItem}.
* @return The new {@link ExtractorMediaSource}.
* @throws NullPointerException if {@link MediaItem#playbackProperties} is {@code null}.
*/
@Override
public ExtractorMediaSource createMediaSource(MediaItem mediaItem) {
Assertions.checkNotNull(mediaItem.playbackProperties);
return new ExtractorMediaSource(
uri,
mediaItem.playbackProperties.sourceUri,
dataSourceFactory,
extractorsFactory,
loadErrorHandlingPolicy,
customCacheKey,
continueLoadingCheckIntervalBytes,
tag);
mediaItem.playbackProperties.tag != null ? mediaItem.playbackProperties.tag : tag);
}
/**

View File

@ -18,6 +18,7 @@ package com.google.android.exoplayer2.source;
import android.net.Uri;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.MediaItem;
import com.google.android.exoplayer2.drm.DrmSession;
import com.google.android.exoplayer2.drm.DrmSessionManager;
import com.google.android.exoplayer2.offline.StreamKey;
@ -26,12 +27,8 @@ import java.util.List;
/** Factory for creating {@link MediaSource}s from URIs. */
public interface MediaSourceFactory {
/**
* Sets a list of {@link StreamKey StreamKeys} by which the manifest is filtered.
*
* @param streamKeys A list of {@link StreamKey StreamKeys}.
* @return This factory, for convenience.
*/
/** @deprecated Use {@link MediaItem.PlaybackProperties#streamKeys} instead. */
@Deprecated
default MediaSourceFactory setStreamKeys(@Nullable List<StreamKey> streamKeys) {
return this;
}
@ -44,18 +41,28 @@ public interface MediaSourceFactory {
*/
MediaSourceFactory setDrmSessionManager(@Nullable DrmSessionManager<?> drmSessionManager);
/**
* Creates a new {@link MediaSource} with the specified {@code uri}.
*
* @param uri The URI to play.
* @return The new {@link MediaSource media source}.
*/
MediaSource createMediaSource(Uri uri);
/**
* Returns the {@link C.ContentType content types} supported by media sources created by this
* factory.
*/
@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}.
*/
MediaSource createMediaSource(MediaItem mediaItem);
/**
* Creates a new {@link MediaSource} with the specified {@code uri}.
*
* @param uri The URI to play.
* @return The new {@link MediaSource media source}.
*/
default MediaSource createMediaSource(Uri uri) {
return createMediaSource(new MediaItem.Builder().setSourceUri(uri).build());
}
}

View File

@ -18,6 +18,7 @@ package com.google.android.exoplayer2.source;
import android.net.Uri;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.MediaItem;
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.drm.DrmSession;
import com.google.android.exoplayer2.drm.DrmSessionManager;
@ -29,6 +30,7 @@ import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DefaultLoadErrorHandlingPolicy;
import com.google.android.exoplayer2.upstream.LoadErrorHandlingPolicy;
import com.google.android.exoplayer2.upstream.TransferListener;
import com.google.android.exoplayer2.util.Assertions;
/**
* Provides one period that loads data from a {@link Uri} and extracted using an {@link Extractor}.
@ -106,13 +108,10 @@ public final class ProgressiveMediaSource extends BaseMediaSource
}
/**
* Sets a tag for the media source which will be published in the {@link
* com.google.android.exoplayer2.Timeline} of the source as {@link
* com.google.android.exoplayer2.Timeline.Window#tag}.
*
* @param tag A tag for the media source.
* @return This factory, for convenience.
* @deprecated Use {@link MediaItem.PlaybackProperties#tag} and {@link
* #createMediaSource(MediaItem)} instead.
*/
@Deprecated
public Factory setTag(@Nullable Object tag) {
this.tag = tag;
return this;
@ -168,20 +167,33 @@ public final class ProgressiveMediaSource extends BaseMediaSource
/**
* Returns a new {@link ProgressiveMediaSource} using the current parameters.
*
* @param uri The {@link Uri}.
* @param uri The {@link Uri uri}.
* @return The new {@link ProgressiveMediaSource}.
*/
@Override
public ProgressiveMediaSource createMediaSource(Uri uri) {
return createMediaSource(new MediaItem.Builder().setSourceUri(uri).build());
}
/**
* Returns a new {@link ProgressiveMediaSource} using the current parameters.
*
* @param mediaItem The {@link MediaItem}.
* @return The new {@link ProgressiveMediaSource}.
* @throws NullPointerException if {@link MediaItem#playbackProperties} is {@code null}.
*/
@Override
public ProgressiveMediaSource createMediaSource(MediaItem mediaItem) {
Assertions.checkNotNull(mediaItem.playbackProperties);
return new ProgressiveMediaSource(
uri,
mediaItem.playbackProperties.sourceUri,
dataSourceFactory,
extractorsFactory,
drmSessionManager,
loadErrorHandlingPolicy,
customCacheKey,
continueLoadingCheckIntervalBytes,
tag);
mediaItem.playbackProperties.tag != null ? mediaItem.playbackProperties.tag : tag);
}
@Override

View File

@ -0,0 +1,137 @@
/*
* Copyright 2020 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;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertThrows;
import android.net.Uri;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.android.exoplayer2.offline.StreamKey;
import java.util.ArrayList;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
/** Unit test for {@link MediaItem MediaItems}. */
@RunWith(AndroidJUnit4.class)
public class MediaItemTest {
private static final String URI_STRING = "http://www.google.com";
@Test
public void builder_needsSourceUriOrMediaId() {
assertThrows(NullPointerException.class, () -> new MediaItem.Builder().build());
}
@Test
public void builderWithUri_setsSourceUri() {
Uri uri = Uri.parse(URI_STRING);
MediaItem mediaItem = MediaItem.fromUri(uri);
assertThat(mediaItem.playbackProperties.sourceUri.toString()).isEqualTo(URI_STRING);
assertThat(mediaItem.mediaId).isEqualTo(URI_STRING);
}
@Test
public void builderWithUriAsString_setsSourceUri() {
MediaItem mediaItem = MediaItem.fromUri(URI_STRING);
assertThat(mediaItem.playbackProperties.sourceUri.toString()).isEqualTo(URI_STRING);
assertThat(mediaItem.mediaId).isEqualTo(URI_STRING);
}
@Test
public void builderSetExtension_isNullByDefault() {
MediaItem mediaItem = MediaItem.fromUri(URI_STRING);
assertThat(mediaItem.playbackProperties.extension).isNull();
}
@Test
public void builderSetExtension_setsExtension() {
MediaItem mediaItem =
new MediaItem.Builder().setSourceUri(URI_STRING).setExtension("mpd").build();
assertThat(mediaItem.playbackProperties.extension).isEqualTo("mpd");
}
@Test
public void builderSetDrmConfig_isNullByDefault() {
// Null value by default.
MediaItem mediaItem = new MediaItem.Builder().setSourceUri(URI_STRING).build();
assertThat(mediaItem.playbackProperties.drmConfiguration).isNull();
}
@Test
public void builderSetDrmConfig_setsAllProperties() {
MediaItem.UriBundle licenseUri = new MediaItem.UriBundle(Uri.parse(URI_STRING));
MediaItem mediaItem =
new MediaItem.Builder()
.setSourceUri(URI_STRING)
.setDrmUuid(C.WIDEVINE_UUID)
.setDrmLicenseUri(licenseUri)
.setDrmMultiSession(/* multiSession= */ true)
.build();
assertThat(mediaItem.playbackProperties.drmConfiguration).isNotNull();
assertThat(mediaItem.playbackProperties.drmConfiguration.uuid).isEqualTo(C.WIDEVINE_UUID);
assertThat(mediaItem.playbackProperties.drmConfiguration.licenseUri).isEqualTo(licenseUri);
assertThat(mediaItem.playbackProperties.drmConfiguration.multiSession).isTrue();
}
@Test
public void builderSetDrmUuid_notCalled_throwsIllegalStateException() {
assertThrows(
IllegalStateException.class,
() ->
new MediaItem.Builder()
.setSourceUri(URI_STRING)
// missing uuid
.setDrmLicenseUri(new MediaItem.UriBundle(Uri.parse(URI_STRING)))
.build());
}
@Test
public void builderSetStreamKeys_setsStreamKeys() {
List<StreamKey> streamKeys = new ArrayList<>();
streamKeys.add(new StreamKey(1, 0, 0));
streamKeys.add(new StreamKey(0, 1, 1));
MediaItem mediaItem =
new MediaItem.Builder().setSourceUri(URI_STRING).setStreamKeys(streamKeys).build();
assertThat(mediaItem.playbackProperties.streamKeys).isEqualTo(streamKeys);
}
@Test
public void builderSetTag_isNullByDefault() {
MediaItem mediaItem = new MediaItem.Builder().setSourceUri(URI_STRING).build();
assertThat(mediaItem.playbackProperties.tag).isNull();
}
@Test
public void builderSetTag_setsTag() {
Object tag = new Object();
MediaItem mediaItem = new MediaItem.Builder().setSourceUri(URI_STRING).setTag(tag).build();
assertThat(mediaItem.playbackProperties.tag).isEqualTo(tag);
}
}

View File

@ -23,6 +23,7 @@ import android.util.SparseArray;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlayerLibraryInfo;
import com.google.android.exoplayer2.MediaItem;
import com.google.android.exoplayer2.ParserException;
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.drm.DrmSession;
@ -63,6 +64,7 @@ import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.TimeZone;
@ -88,7 +90,7 @@ public final class DashMediaSource extends BaseMediaSource {
private long livePresentationDelayMs;
private boolean livePresentationDelayOverridesManifest;
@Nullable private ParsingLoadable.Parser<? extends DashManifest> manifestParser;
@Nullable private List<StreamKey> streamKeys;
private List<StreamKey> streamKeys;
@Nullable private Object tag;
/**
@ -119,24 +121,28 @@ public final class DashMediaSource extends BaseMediaSource {
loadErrorHandlingPolicy = new DefaultLoadErrorHandlingPolicy();
livePresentationDelayMs = DEFAULT_LIVE_PRESENTATION_DELAY_MS;
compositeSequenceableLoaderFactory = new DefaultCompositeSequenceableLoaderFactory();
streamKeys = Collections.emptyList();
}
/**
* Sets a tag for the media source which will be published in the {@link
* com.google.android.exoplayer2.Timeline} of the source as {@link
* com.google.android.exoplayer2.Timeline.Window#tag}.
*
* @param tag A tag for the media source.
* @return This factory, for convenience.
* @deprecated Use {@link MediaItem.PlaybackProperties#tag} and {@link
* #createMediaSource(MediaItem)} instead.
*/
@Deprecated
public Factory setTag(@Nullable Object tag) {
this.tag = tag;
return this;
}
/**
* @deprecated Use {@link MediaItem.PlaybackProperties#streamKeys} and {@link
* #createMediaSource(MediaItem)} instead.
*/
@SuppressWarnings("deprecation")
@Deprecated
@Override
public Factory setStreamKeys(@Nullable List<StreamKey> streamKeys) {
this.streamKeys = streamKeys != null && !streamKeys.isEmpty() ? streamKeys : null;
this.streamKeys = streamKeys != null ? streamKeys : Collections.emptyList();
return this;
}
@ -304,21 +310,38 @@ public final class DashMediaSource extends BaseMediaSource {
/**
* Returns a new {@link DashMediaSource} using the current parameters.
*
* @param manifestUri The manifest {@link Uri}.
* @param uri The {@link Uri uri}.
* @return The new {@link DashMediaSource}.
*/
@Override
public DashMediaSource createMediaSource(Uri manifestUri) {
public DashMediaSource createMediaSource(Uri uri) {
return createMediaSource(new MediaItem.Builder().setSourceUri(uri).build());
}
/**
* Returns a new {@link DashMediaSource} using the current parameters.
*
* @param mediaItem The media item of the dash stream.
* @return The new {@link DashMediaSource}.
* @throws NullPointerException if {@link MediaItem#playbackProperties} is {@code null}.
*/
@Override
public DashMediaSource createMediaSource(MediaItem mediaItem) {
Assertions.checkNotNull(mediaItem.playbackProperties);
@Nullable ParsingLoadable.Parser<? extends DashManifest> manifestParser = this.manifestParser;
if (manifestParser == null) {
manifestParser = new DashManifestParser();
}
if (streamKeys != null) {
List<StreamKey> streamKeys =
!mediaItem.playbackProperties.streamKeys.isEmpty()
? mediaItem.playbackProperties.streamKeys
: this.streamKeys;
if (!streamKeys.isEmpty()) {
manifestParser = new FilteringManifestParser<>(manifestParser, streamKeys);
}
return new DashMediaSource(
/* manifest= */ null,
Assertions.checkNotNull(manifestUri),
mediaItem.playbackProperties.sourceUri,
manifestDataSourceFactory,
manifestParser,
chunkSourceFactory,
@ -327,7 +350,7 @@ public final class DashMediaSource extends BaseMediaSource {
loadErrorHandlingPolicy,
livePresentationDelayMs,
livePresentationDelayOverridesManifest,
tag);
mediaItem.playbackProperties.tag != null ? mediaItem.playbackProperties.tag : tag);
}
@Override

View File

@ -23,6 +23,7 @@ import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlayerLibraryInfo;
import com.google.android.exoplayer2.MediaItem;
import com.google.android.exoplayer2.drm.DrmSession;
import com.google.android.exoplayer2.drm.DrmSessionManager;
import com.google.android.exoplayer2.extractor.Extractor;
@ -52,6 +53,7 @@ import com.google.android.exoplayer2.util.Assertions;
import java.io.IOException;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.util.Collections;
import java.util.List;
/** An HLS {@link MediaSource}. */
@ -98,7 +100,7 @@ public final class HlsMediaSource extends BaseMediaSource
private boolean allowChunklessPreparation;
@MetadataType private int metadataType;
private boolean useSessionKeys;
@Nullable private List<StreamKey> streamKeys;
private List<StreamKey> streamKeys;
@Nullable private Object tag;
/**
@ -127,16 +129,14 @@ public final class HlsMediaSource extends BaseMediaSource
loadErrorHandlingPolicy = new DefaultLoadErrorHandlingPolicy();
compositeSequenceableLoaderFactory = new DefaultCompositeSequenceableLoaderFactory();
metadataType = METADATA_TYPE_ID3;
streamKeys = Collections.emptyList();
}
/**
* Sets a tag for the media source which will be published in the {@link
* com.google.android.exoplayer2.Timeline} of the source as {@link
* com.google.android.exoplayer2.Timeline.Window#tag}.
*
* @param tag A tag for the media source.
* @return This factory, for convenience.
* @deprecated Use {@link MediaItem.PlaybackProperties#tag} and {@link
* #createMediaSource(MediaItem)} instead.
*/
@Deprecated
public Factory setTag(@Nullable Object tag) {
this.tag = tag;
return this;
@ -298,9 +298,15 @@ public final class HlsMediaSource extends BaseMediaSource
return this;
}
/**
* @deprecated Use {@link MediaItem.PlaybackProperties#streamKeys} and {@link
* #createMediaSource(MediaItem)} instead.
*/
@SuppressWarnings("deprecation")
@Deprecated
@Override
public Factory setStreamKeys(@Nullable List<StreamKey> streamKeys) {
this.streamKeys = streamKeys != null && !streamKeys.isEmpty() ? streamKeys : null;
this.streamKeys = streamKeys != null ? streamKeys : Collections.emptyList();
return this;
}
@ -308,6 +314,7 @@ public final class HlsMediaSource extends BaseMediaSource
* @deprecated Use {@link #createMediaSource(Uri)} and {@link #addEventListener(Handler,
* MediaSourceEventListener)} instead.
*/
@SuppressWarnings("deprecation")
@Deprecated
public HlsMediaSource createMediaSource(
Uri playlistUri,
@ -323,17 +330,35 @@ public final class HlsMediaSource extends BaseMediaSource
/**
* Returns a new {@link HlsMediaSource} using the current parameters.
*
* @param uri The {@link Uri uri}.
* @return The new {@link HlsMediaSource}.
*/
@Override
public HlsMediaSource createMediaSource(Uri playlistUri) {
public HlsMediaSource createMediaSource(Uri uri) {
return createMediaSource(new MediaItem.Builder().setSourceUri(uri).build());
}
/**
* Returns a new {@link HlsMediaSource} using the current parameters.
*
* @param mediaItem The {@link MediaItem}.
* @return The new {@link HlsMediaSource}.
* @throws NullPointerException if {@link MediaItem#playbackProperties} is {@code null}.
*/
@Override
public HlsMediaSource createMediaSource(MediaItem mediaItem) {
Assertions.checkNotNull(mediaItem.playbackProperties);
HlsPlaylistParserFactory playlistParserFactory = this.playlistParserFactory;
if (streamKeys != null) {
List<StreamKey> streamKeys =
!mediaItem.playbackProperties.streamKeys.isEmpty()
? mediaItem.playbackProperties.streamKeys
: this.streamKeys;
if (!streamKeys.isEmpty()) {
playlistParserFactory =
new FilteringHlsPlaylistParserFactory(playlistParserFactory, streamKeys);
}
return new HlsMediaSource(
playlistUri,
mediaItem.playbackProperties.sourceUri,
hlsDataSourceFactory,
extractorFactory,
compositeSequenceableLoaderFactory,
@ -344,7 +369,7 @@ public final class HlsMediaSource extends BaseMediaSource
allowChunklessPreparation,
metadataType,
useSessionKeys,
tag);
mediaItem.playbackProperties.tag != null ? mediaItem.playbackProperties.tag : tag);
}
@Override

View File

@ -21,6 +21,7 @@ import android.os.SystemClock;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlayerLibraryInfo;
import com.google.android.exoplayer2.MediaItem;
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.drm.DrmSession;
import com.google.android.exoplayer2.drm.DrmSessionManager;
@ -53,6 +54,7 @@ import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Util;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/** A SmoothStreaming {@link MediaSource}. */
@ -74,7 +76,7 @@ public final class SsMediaSource extends BaseMediaSource
private LoadErrorHandlingPolicy loadErrorHandlingPolicy;
private long livePresentationDelayMs;
@Nullable private ParsingLoadable.Parser<? extends SsManifest> manifestParser;
@Nullable private List<StreamKey> streamKeys;
private List<StreamKey> streamKeys;
@Nullable private Object tag;
/**
@ -105,15 +107,14 @@ public final class SsMediaSource extends BaseMediaSource
loadErrorHandlingPolicy = new DefaultLoadErrorHandlingPolicy();
livePresentationDelayMs = DEFAULT_LIVE_PRESENTATION_DELAY_MS;
compositeSequenceableLoaderFactory = new DefaultCompositeSequenceableLoaderFactory();
streamKeys = Collections.emptyList();
}
/**
* Sets a tag for the media source which will be published in the {@link Timeline} of the source
* as {@link Timeline.Window#tag}.
*
* @param tag A tag for the media source.
* @return This factory, for convenience.
* @deprecated Use {@link MediaItem.PlaybackProperties#tag} and {@link
* #createMediaSource(MediaItem)} instead.
*/
@Deprecated
public Factory setTag(@Nullable Object tag) {
this.tag = tag;
return this;
@ -204,12 +205,29 @@ public final class SsMediaSource extends BaseMediaSource
return this;
}
/**
* @deprecated Use {@link MediaItem.PlaybackProperties#streamKeys} and {@link
* #createMediaSource(MediaItem)} instead.
*/
@SuppressWarnings("deprecation")
@Deprecated
@Override
public Factory setStreamKeys(List<StreamKey> streamKeys) {
this.streamKeys = streamKeys != null && !streamKeys.isEmpty() ? streamKeys : null;
public Factory setStreamKeys(@Nullable List<StreamKey> streamKeys) {
this.streamKeys = streamKeys != null ? streamKeys : Collections.emptyList();
return this;
}
/**
* Returns a new {@link SsMediaSource} using the current parameters.
*
* @param uri The {@link Uri uri}.
* @return The new {@link SsMediaSource}.
*/
@Override
public SsMediaSource createMediaSource(Uri uri) {
return createMediaSource(new MediaItem.Builder().setSourceUri(uri).build());
}
/**
* Returns a new {@link SsMediaSource} using the current parameters and the specified sideloaded
* manifest.
@ -220,7 +238,7 @@ public final class SsMediaSource extends BaseMediaSource
*/
public SsMediaSource createMediaSource(SsManifest manifest) {
Assertions.checkArgument(!manifest.isLive);
if (streamKeys != null) {
if (!streamKeys.isEmpty()) {
manifest = manifest.copy(streamKeys);
}
return new SsMediaSource(
@ -271,21 +289,27 @@ public final class SsMediaSource extends BaseMediaSource
/**
* Returns a new {@link SsMediaSource} using the current parameters.
*
* @param manifestUri The manifest {@link Uri}.
* @param mediaItem The {@link MediaItem}.
* @return The new {@link SsMediaSource}.
* @throws NullPointerException if {@link MediaItem#playbackProperties} is {@code null}.
*/
@Override
public SsMediaSource createMediaSource(Uri manifestUri) {
public SsMediaSource createMediaSource(MediaItem mediaItem) {
Assertions.checkNotNull(mediaItem.playbackProperties);
@Nullable ParsingLoadable.Parser<? extends SsManifest> manifestParser = this.manifestParser;
if (manifestParser == null) {
manifestParser = new SsManifestParser();
}
if (streamKeys != null) {
List<StreamKey> streamKeys =
!mediaItem.playbackProperties.streamKeys.isEmpty()
? mediaItem.playbackProperties.streamKeys
: this.streamKeys;
if (!streamKeys.isEmpty()) {
manifestParser = new FilteringManifestParser<>(manifestParser, streamKeys);
}
return new SsMediaSource(
/* manifest= */ null,
Assertions.checkNotNull(manifestUri),
mediaItem.playbackProperties.sourceUri,
manifestDataSourceFactory,
manifestParser,
chunkSourceFactory,
@ -293,7 +317,7 @@ public final class SsMediaSource extends BaseMediaSource
drmSessionManager,
loadErrorHandlingPolicy,
livePresentationDelayMs,
tag);
mediaItem.playbackProperties.tag != null ? mediaItem.playbackProperties.tag : tag);
}
@Override