mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
Make HlsMediaSource add the media item to the timeline
PiperOrigin-RevId: 313605791
This commit is contained in:
parent
37024ae450
commit
0c81022aaa
2 changed files with 173 additions and 22 deletions
|
|
@ -15,6 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package com.google.android.exoplayer2.source.hls;
|
package com.google.android.exoplayer2.source.hls;
|
||||||
|
|
||||||
|
import static com.google.android.exoplayer2.util.Assertions.checkNotNull;
|
||||||
import static java.lang.annotation.RetentionPolicy.SOURCE;
|
import static java.lang.annotation.RetentionPolicy.SOURCE;
|
||||||
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
|
@ -49,7 +50,7 @@ import com.google.android.exoplayer2.upstream.DataSource;
|
||||||
import com.google.android.exoplayer2.upstream.DefaultLoadErrorHandlingPolicy;
|
import com.google.android.exoplayer2.upstream.DefaultLoadErrorHandlingPolicy;
|
||||||
import com.google.android.exoplayer2.upstream.LoadErrorHandlingPolicy;
|
import com.google.android.exoplayer2.upstream.LoadErrorHandlingPolicy;
|
||||||
import com.google.android.exoplayer2.upstream.TransferListener;
|
import com.google.android.exoplayer2.upstream.TransferListener;
|
||||||
import com.google.android.exoplayer2.util.Assertions;
|
import com.google.android.exoplayer2.util.MimeTypes;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.annotation.Documented;
|
import java.lang.annotation.Documented;
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
|
|
@ -121,7 +122,7 @@ public final class HlsMediaSource extends BaseMediaSource
|
||||||
* manifests, segments and keys.
|
* manifests, segments and keys.
|
||||||
*/
|
*/
|
||||||
public Factory(HlsDataSourceFactory hlsDataSourceFactory) {
|
public Factory(HlsDataSourceFactory hlsDataSourceFactory) {
|
||||||
this.hlsDataSourceFactory = Assertions.checkNotNull(hlsDataSourceFactory);
|
this.hlsDataSourceFactory = checkNotNull(hlsDataSourceFactory);
|
||||||
playlistParserFactory = new DefaultHlsPlaylistParserFactory();
|
playlistParserFactory = new DefaultHlsPlaylistParserFactory();
|
||||||
playlistTrackerFactory = DefaultHlsPlaylistTracker.FACTORY;
|
playlistTrackerFactory = DefaultHlsPlaylistTracker.FACTORY;
|
||||||
extractorFactory = HlsExtractorFactory.DEFAULT;
|
extractorFactory = HlsExtractorFactory.DEFAULT;
|
||||||
|
|
@ -332,7 +333,8 @@ public final class HlsMediaSource extends BaseMediaSource
|
||||||
@Deprecated
|
@Deprecated
|
||||||
@Override
|
@Override
|
||||||
public HlsMediaSource createMediaSource(Uri uri) {
|
public HlsMediaSource createMediaSource(Uri uri) {
|
||||||
return createMediaSource(new MediaItem.Builder().setUri(uri).build());
|
return createMediaSource(
|
||||||
|
new MediaItem.Builder().setUri(uri).setMimeType(MimeTypes.APPLICATION_M3U8).build());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -344,18 +346,29 @@ public final class HlsMediaSource extends BaseMediaSource
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public HlsMediaSource createMediaSource(MediaItem mediaItem) {
|
public HlsMediaSource createMediaSource(MediaItem mediaItem) {
|
||||||
Assertions.checkNotNull(mediaItem.playbackProperties);
|
checkNotNull(mediaItem.playbackProperties);
|
||||||
HlsPlaylistParserFactory playlistParserFactory = this.playlistParserFactory;
|
HlsPlaylistParserFactory playlistParserFactory = this.playlistParserFactory;
|
||||||
List<StreamKey> streamKeys =
|
List<StreamKey> streamKeys =
|
||||||
!mediaItem.playbackProperties.streamKeys.isEmpty()
|
mediaItem.playbackProperties.streamKeys.isEmpty()
|
||||||
? mediaItem.playbackProperties.streamKeys
|
? this.streamKeys
|
||||||
: this.streamKeys;
|
: mediaItem.playbackProperties.streamKeys;
|
||||||
if (!streamKeys.isEmpty()) {
|
if (!streamKeys.isEmpty()) {
|
||||||
playlistParserFactory =
|
playlistParserFactory =
|
||||||
new FilteringHlsPlaylistParserFactory(playlistParserFactory, streamKeys);
|
new FilteringHlsPlaylistParserFactory(playlistParserFactory, streamKeys);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean needsTag = mediaItem.playbackProperties.tag == null && tag != null;
|
||||||
|
boolean needsStreamKeys =
|
||||||
|
mediaItem.playbackProperties.streamKeys.isEmpty() && !streamKeys.isEmpty();
|
||||||
|
if (needsTag && needsStreamKeys) {
|
||||||
|
mediaItem = mediaItem.buildUpon().setTag(tag).setStreamKeys(streamKeys).build();
|
||||||
|
} else if (needsTag) {
|
||||||
|
mediaItem = mediaItem.buildUpon().setTag(tag).build();
|
||||||
|
} else if (needsStreamKeys) {
|
||||||
|
mediaItem = mediaItem.buildUpon().setStreamKeys(streamKeys).build();
|
||||||
|
}
|
||||||
return new HlsMediaSource(
|
return new HlsMediaSource(
|
||||||
mediaItem.playbackProperties.uri,
|
mediaItem,
|
||||||
hlsDataSourceFactory,
|
hlsDataSourceFactory,
|
||||||
extractorFactory,
|
extractorFactory,
|
||||||
compositeSequenceableLoaderFactory,
|
compositeSequenceableLoaderFactory,
|
||||||
|
|
@ -365,8 +378,7 @@ public final class HlsMediaSource extends BaseMediaSource
|
||||||
hlsDataSourceFactory, loadErrorHandlingPolicy, playlistParserFactory),
|
hlsDataSourceFactory, loadErrorHandlingPolicy, playlistParserFactory),
|
||||||
allowChunklessPreparation,
|
allowChunklessPreparation,
|
||||||
metadataType,
|
metadataType,
|
||||||
useSessionKeys,
|
useSessionKeys);
|
||||||
mediaItem.playbackProperties.tag != null ? mediaItem.playbackProperties.tag : tag);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -376,7 +388,8 @@ public final class HlsMediaSource extends BaseMediaSource
|
||||||
}
|
}
|
||||||
|
|
||||||
private final HlsExtractorFactory extractorFactory;
|
private final HlsExtractorFactory extractorFactory;
|
||||||
private final Uri manifestUri;
|
private final MediaItem mediaItem;
|
||||||
|
private final MediaItem.PlaybackProperties playbackProperties;
|
||||||
private final HlsDataSourceFactory dataSourceFactory;
|
private final HlsDataSourceFactory dataSourceFactory;
|
||||||
private final CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory;
|
private final CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory;
|
||||||
private final DrmSessionManager drmSessionManager;
|
private final DrmSessionManager drmSessionManager;
|
||||||
|
|
@ -385,12 +398,11 @@ public final class HlsMediaSource extends BaseMediaSource
|
||||||
private final @MetadataType int metadataType;
|
private final @MetadataType int metadataType;
|
||||||
private final boolean useSessionKeys;
|
private final boolean useSessionKeys;
|
||||||
private final HlsPlaylistTracker playlistTracker;
|
private final HlsPlaylistTracker playlistTracker;
|
||||||
@Nullable private final Object tag;
|
|
||||||
|
|
||||||
@Nullable private TransferListener mediaTransferListener;
|
@Nullable private TransferListener mediaTransferListener;
|
||||||
|
|
||||||
private HlsMediaSource(
|
private HlsMediaSource(
|
||||||
Uri manifestUri,
|
MediaItem mediaItem,
|
||||||
HlsDataSourceFactory dataSourceFactory,
|
HlsDataSourceFactory dataSourceFactory,
|
||||||
HlsExtractorFactory extractorFactory,
|
HlsExtractorFactory extractorFactory,
|
||||||
CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory,
|
CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory,
|
||||||
|
|
@ -399,9 +411,9 @@ public final class HlsMediaSource extends BaseMediaSource
|
||||||
HlsPlaylistTracker playlistTracker,
|
HlsPlaylistTracker playlistTracker,
|
||||||
boolean allowChunklessPreparation,
|
boolean allowChunklessPreparation,
|
||||||
@MetadataType int metadataType,
|
@MetadataType int metadataType,
|
||||||
boolean useSessionKeys,
|
boolean useSessionKeys) {
|
||||||
@Nullable Object tag) {
|
this.playbackProperties = checkNotNull(mediaItem.playbackProperties);
|
||||||
this.manifestUri = manifestUri;
|
this.mediaItem = mediaItem;
|
||||||
this.dataSourceFactory = dataSourceFactory;
|
this.dataSourceFactory = dataSourceFactory;
|
||||||
this.extractorFactory = extractorFactory;
|
this.extractorFactory = extractorFactory;
|
||||||
this.compositeSequenceableLoaderFactory = compositeSequenceableLoaderFactory;
|
this.compositeSequenceableLoaderFactory = compositeSequenceableLoaderFactory;
|
||||||
|
|
@ -411,13 +423,17 @@ public final class HlsMediaSource extends BaseMediaSource
|
||||||
this.allowChunklessPreparation = allowChunklessPreparation;
|
this.allowChunklessPreparation = allowChunklessPreparation;
|
||||||
this.metadataType = metadataType;
|
this.metadataType = metadataType;
|
||||||
this.useSessionKeys = useSessionKeys;
|
this.useSessionKeys = useSessionKeys;
|
||||||
this.tag = tag;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Nullable
|
@Nullable
|
||||||
public Object getTag() {
|
public Object getTag() {
|
||||||
return tag;
|
return playbackProperties.tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(bachinger): add @Override annotation once the method is defined by MediaSource.
|
||||||
|
public MediaItem getMediaItem() {
|
||||||
|
return mediaItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -425,7 +441,7 @@ public final class HlsMediaSource extends BaseMediaSource
|
||||||
this.mediaTransferListener = mediaTransferListener;
|
this.mediaTransferListener = mediaTransferListener;
|
||||||
drmSessionManager.prepare();
|
drmSessionManager.prepare();
|
||||||
EventDispatcher eventDispatcher = createEventDispatcher(/* mediaPeriodId= */ null);
|
EventDispatcher eventDispatcher = createEventDispatcher(/* mediaPeriodId= */ null);
|
||||||
playlistTracker.start(manifestUri, eventDispatcher, /* listener= */ this);
|
playlistTracker.start(playbackProperties.uri, eventDispatcher, /* listener= */ this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -477,7 +493,7 @@ public final class HlsMediaSource extends BaseMediaSource
|
||||||
long windowDefaultStartPositionUs = playlist.startOffsetUs;
|
long windowDefaultStartPositionUs = playlist.startOffsetUs;
|
||||||
// masterPlaylist is non-null because the first playlist has been fetched by now.
|
// masterPlaylist is non-null because the first playlist has been fetched by now.
|
||||||
HlsManifest manifest =
|
HlsManifest manifest =
|
||||||
new HlsManifest(Assertions.checkNotNull(playlistTracker.getMasterPlaylist()), playlist);
|
new HlsManifest(checkNotNull(playlistTracker.getMasterPlaylist()), playlist);
|
||||||
if (playlistTracker.isLive()) {
|
if (playlistTracker.isLive()) {
|
||||||
long offsetFromInitialStartTimeUs =
|
long offsetFromInitialStartTimeUs =
|
||||||
playlist.startTimeUs - playlistTracker.getInitialStartTimeUs();
|
playlist.startTimeUs - playlistTracker.getInitialStartTimeUs();
|
||||||
|
|
@ -511,7 +527,7 @@ public final class HlsMediaSource extends BaseMediaSource
|
||||||
/* isDynamic= */ !playlist.hasEndTag,
|
/* isDynamic= */ !playlist.hasEndTag,
|
||||||
/* isLive= */ true,
|
/* isLive= */ true,
|
||||||
manifest,
|
manifest,
|
||||||
tag);
|
mediaItem);
|
||||||
} else /* not live */ {
|
} else /* not live */ {
|
||||||
if (windowDefaultStartPositionUs == C.TIME_UNSET) {
|
if (windowDefaultStartPositionUs == C.TIME_UNSET) {
|
||||||
windowDefaultStartPositionUs = 0;
|
windowDefaultStartPositionUs = 0;
|
||||||
|
|
@ -529,7 +545,7 @@ public final class HlsMediaSource extends BaseMediaSource
|
||||||
/* isDynamic= */ false,
|
/* isDynamic= */ false,
|
||||||
/* isLive= */ false,
|
/* isLive= */ false,
|
||||||
manifest,
|
manifest,
|
||||||
tag);
|
mediaItem);
|
||||||
}
|
}
|
||||||
refreshSourceInfo(timeline);
|
refreshSourceInfo(timeline);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,135 @@
|
||||||
|
/*
|
||||||
|
* 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.source.hls;
|
||||||
|
|
||||||
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
|
import com.google.android.exoplayer2.MediaItem;
|
||||||
|
import com.google.android.exoplayer2.offline.StreamKey;
|
||||||
|
import com.google.android.exoplayer2.upstream.DataSource;
|
||||||
|
import java.util.Collections;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
/** Unit test for {@link DashMediaSource}. */
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
public class HlsMediaSourceTest {
|
||||||
|
|
||||||
|
// Tests backwards compatibility
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
@Test
|
||||||
|
public void factorySetTag_nullMediaItemTag_setsMediaItemTag() {
|
||||||
|
Object tag = new Object();
|
||||||
|
MediaItem mediaItem = MediaItem.fromUri("http://www.google.com");
|
||||||
|
HlsMediaSource.Factory factory =
|
||||||
|
new HlsMediaSource.Factory(mock(DataSource.Factory.class)).setTag(tag);
|
||||||
|
|
||||||
|
MediaItem dashMediaItem = factory.createMediaSource(mediaItem).getMediaItem();
|
||||||
|
|
||||||
|
assertThat(dashMediaItem.playbackProperties).isNotNull();
|
||||||
|
assertThat(dashMediaItem.playbackProperties.uri).isEqualTo(mediaItem.playbackProperties.uri);
|
||||||
|
assertThat(dashMediaItem.playbackProperties.tag).isEqualTo(tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests backwards compatibility
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
@Test
|
||||||
|
public void factorySetTag_nonNullMediaItemTag_doesNotOverrideMediaItemTag() {
|
||||||
|
Object factoryTag = new Object();
|
||||||
|
Object mediaItemTag = new Object();
|
||||||
|
MediaItem mediaItem =
|
||||||
|
new MediaItem.Builder().setUri("http://www.google.com").setTag(mediaItemTag).build();
|
||||||
|
HlsMediaSource.Factory factory =
|
||||||
|
new HlsMediaSource.Factory(mock(DataSource.Factory.class)).setTag(factoryTag);
|
||||||
|
|
||||||
|
MediaItem dashMediaItem = factory.createMediaSource(mediaItem).getMediaItem();
|
||||||
|
|
||||||
|
assertThat(dashMediaItem.playbackProperties).isNotNull();
|
||||||
|
assertThat(dashMediaItem.playbackProperties.uri).isEqualTo(mediaItem.playbackProperties.uri);
|
||||||
|
assertThat(dashMediaItem.playbackProperties.tag).isEqualTo(mediaItemTag);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests backwards compatibility
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
@Test
|
||||||
|
public void factorySetTag_setsDeprecatedMediaSourceTag() {
|
||||||
|
Object tag = new Object();
|
||||||
|
MediaItem mediaItem = MediaItem.fromUri("http://www.google.com");
|
||||||
|
HlsMediaSource.Factory factory =
|
||||||
|
new HlsMediaSource.Factory(mock(DataSource.Factory.class)).setTag(tag);
|
||||||
|
|
||||||
|
@Nullable Object mediaSourceTag = factory.createMediaSource(mediaItem).getTag();
|
||||||
|
|
||||||
|
assertThat(mediaSourceTag).isEqualTo(tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests backwards compatibility
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
@Test
|
||||||
|
public void factoryCreateMediaSource_setsDeprecatedMediaSourceTag() {
|
||||||
|
Object tag = new Object();
|
||||||
|
MediaItem mediaItem =
|
||||||
|
new MediaItem.Builder().setUri("http://www.google.com").setTag(tag).build();
|
||||||
|
HlsMediaSource.Factory factory =
|
||||||
|
new HlsMediaSource.Factory(mock(DataSource.Factory.class)).setTag(new Object());
|
||||||
|
|
||||||
|
@Nullable Object mediaSourceTag = factory.createMediaSource(mediaItem).getTag();
|
||||||
|
|
||||||
|
assertThat(mediaSourceTag).isEqualTo(tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests backwards compatibility
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
@Test
|
||||||
|
public void factorySetStreamKeys_emptyMediaItemStreamKeys_setsMediaItemStreamKeys() {
|
||||||
|
MediaItem mediaItem = MediaItem.fromUri("http://www.google.com");
|
||||||
|
StreamKey streamKey = new StreamKey(/* groupIndex= */ 0, /* trackIndex= */ 1);
|
||||||
|
HlsMediaSource.Factory factory =
|
||||||
|
new HlsMediaSource.Factory(mock(DataSource.Factory.class))
|
||||||
|
.setStreamKeys(Collections.singletonList(streamKey));
|
||||||
|
|
||||||
|
MediaItem dashMediaItem = factory.createMediaSource(mediaItem).getMediaItem();
|
||||||
|
|
||||||
|
assertThat(dashMediaItem.playbackProperties).isNotNull();
|
||||||
|
assertThat(dashMediaItem.playbackProperties.uri).isEqualTo(mediaItem.playbackProperties.uri);
|
||||||
|
assertThat(dashMediaItem.playbackProperties.streamKeys).containsExactly(streamKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests backwards compatibility
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
@Test
|
||||||
|
public void factorySetStreamKeys_withMediaItemStreamKeys_doesNotOverrideMediaItemStreamKeys() {
|
||||||
|
StreamKey mediaItemStreamKey = new StreamKey(/* groupIndex= */ 0, /* trackIndex= */ 1);
|
||||||
|
MediaItem mediaItem =
|
||||||
|
new MediaItem.Builder()
|
||||||
|
.setUri("http://www.google.com")
|
||||||
|
.setStreamKeys(Collections.singletonList(mediaItemStreamKey))
|
||||||
|
.build();
|
||||||
|
HlsMediaSource.Factory factory =
|
||||||
|
new HlsMediaSource.Factory(mock(DataSource.Factory.class))
|
||||||
|
.setStreamKeys(
|
||||||
|
Collections.singletonList(new StreamKey(/* groupIndex= */ 1, /* trackIndex= */ 0)));
|
||||||
|
|
||||||
|
MediaItem dashMediaItem = factory.createMediaSource(mediaItem).getMediaItem();
|
||||||
|
|
||||||
|
assertThat(dashMediaItem.playbackProperties).isNotNull();
|
||||||
|
assertThat(dashMediaItem.playbackProperties.uri).isEqualTo(mediaItem.playbackProperties.uri);
|
||||||
|
assertThat(dashMediaItem.playbackProperties.streamKeys).containsExactly(mediaItemStreamKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue