Use a MediaSource factory internally in AdsMediaSource
Support ad MediaSources that aren't prepared immediately by using DeferredMediaPeriod, moved up from DynamicConcatenatingMediaSource. In a later change the new interfaces will be made public so that apps can provide their own MediaSource factories. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=177424172
This commit is contained in:
parent
882d698d5f
commit
5865f1fe40
@ -0,0 +1,139 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2017 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;
|
||||||
|
|
||||||
|
import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
|
||||||
|
import com.google.android.exoplayer2.trackselection.TrackSelection;
|
||||||
|
import com.google.android.exoplayer2.upstream.Allocator;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Media period that wraps a media source and defers calling its
|
||||||
|
* {@link MediaSource#createPeriod(MediaPeriodId, Allocator)} method until {@link #createPeriod()}
|
||||||
|
* has been called. This is useful if you need to return a media period immediately but the media
|
||||||
|
* source that should create it is not yet prepared.
|
||||||
|
*/
|
||||||
|
public final class DeferredMediaPeriod implements MediaPeriod, MediaPeriod.Callback {
|
||||||
|
|
||||||
|
public final MediaSource mediaSource;
|
||||||
|
|
||||||
|
private final MediaPeriodId id;
|
||||||
|
private final Allocator allocator;
|
||||||
|
|
||||||
|
private MediaPeriod mediaPeriod;
|
||||||
|
private Callback callback;
|
||||||
|
private long preparePositionUs;
|
||||||
|
|
||||||
|
public DeferredMediaPeriod(MediaSource mediaSource, MediaPeriodId id, Allocator allocator) {
|
||||||
|
this.id = id;
|
||||||
|
this.allocator = allocator;
|
||||||
|
this.mediaSource = mediaSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calls {@link MediaSource#createPeriod(MediaPeriodId, Allocator)} on the wrapped source then
|
||||||
|
* prepares it if {@link #prepare(Callback, long)} has been called. Call {@link #releasePeriod()}
|
||||||
|
* to release the period.
|
||||||
|
*/
|
||||||
|
public void createPeriod() {
|
||||||
|
mediaPeriod = mediaSource.createPeriod(id, allocator);
|
||||||
|
if (callback != null) {
|
||||||
|
mediaPeriod.prepare(this, preparePositionUs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Releases the period.
|
||||||
|
*/
|
||||||
|
public void releasePeriod() {
|
||||||
|
if (mediaPeriod != null) {
|
||||||
|
mediaSource.releasePeriod(mediaPeriod);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void prepare(Callback callback, long preparePositionUs) {
|
||||||
|
this.callback = callback;
|
||||||
|
this.preparePositionUs = preparePositionUs;
|
||||||
|
if (mediaPeriod != null) {
|
||||||
|
mediaPeriod.prepare(this, preparePositionUs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void maybeThrowPrepareError() throws IOException {
|
||||||
|
if (mediaPeriod != null) {
|
||||||
|
mediaPeriod.maybeThrowPrepareError();
|
||||||
|
} else {
|
||||||
|
mediaSource.maybeThrowSourceInfoRefreshError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TrackGroupArray getTrackGroups() {
|
||||||
|
return mediaPeriod.getTrackGroups();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long selectTracks(TrackSelection[] selections, boolean[] mayRetainStreamFlags,
|
||||||
|
SampleStream[] streams, boolean[] streamResetFlags, long positionUs) {
|
||||||
|
return mediaPeriod.selectTracks(selections, mayRetainStreamFlags, streams, streamResetFlags,
|
||||||
|
positionUs);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void discardBuffer(long positionUs, boolean toKeyframe) {
|
||||||
|
mediaPeriod.discardBuffer(positionUs, toKeyframe);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long readDiscontinuity() {
|
||||||
|
return mediaPeriod.readDiscontinuity();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getBufferedPositionUs() {
|
||||||
|
return mediaPeriod.getBufferedPositionUs();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long seekToUs(long positionUs) {
|
||||||
|
return mediaPeriod.seekToUs(positionUs);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getNextLoadPositionUs() {
|
||||||
|
return mediaPeriod.getNextLoadPositionUs();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean continueLoading(long positionUs) {
|
||||||
|
return mediaPeriod != null && mediaPeriod.continueLoading(positionUs);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onContinueLoadingRequested(MediaPeriod source) {
|
||||||
|
callback.onContinueLoadingRequested(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// MediaPeriod.Callback implementation
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPrepared(MediaPeriod mediaPeriod) {
|
||||||
|
callback.onPrepared(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -27,7 +27,6 @@ import com.google.android.exoplayer2.ExoPlayer.ExoPlayerComponent;
|
|||||||
import com.google.android.exoplayer2.ExoPlayer.ExoPlayerMessage;
|
import com.google.android.exoplayer2.ExoPlayer.ExoPlayerMessage;
|
||||||
import com.google.android.exoplayer2.Timeline;
|
import com.google.android.exoplayer2.Timeline;
|
||||||
import com.google.android.exoplayer2.source.ShuffleOrder.DefaultShuffleOrder;
|
import com.google.android.exoplayer2.source.ShuffleOrder.DefaultShuffleOrder;
|
||||||
import com.google.android.exoplayer2.trackselection.TrackSelection;
|
|
||||||
import com.google.android.exoplayer2.upstream.Allocator;
|
import com.google.android.exoplayer2.upstream.Allocator;
|
||||||
import com.google.android.exoplayer2.util.Assertions;
|
import com.google.android.exoplayer2.util.Assertions;
|
||||||
import com.google.android.exoplayer2.util.Util;
|
import com.google.android.exoplayer2.util.Util;
|
||||||
@ -758,111 +757,5 @@ public final class DynamicConcatenatingMediaSource implements MediaSource, ExoPl
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Media period used for periods created from unprepared media sources exposed through
|
|
||||||
* {@link DeferredTimeline}. Period preparation is postponed until the actual media source becomes
|
|
||||||
* available.
|
|
||||||
*/
|
|
||||||
private static final class DeferredMediaPeriod implements MediaPeriod, MediaPeriod.Callback {
|
|
||||||
|
|
||||||
public final MediaSource mediaSource;
|
|
||||||
|
|
||||||
private final MediaPeriodId id;
|
|
||||||
private final Allocator allocator;
|
|
||||||
|
|
||||||
private MediaPeriod mediaPeriod;
|
|
||||||
private Callback callback;
|
|
||||||
private long preparePositionUs;
|
|
||||||
|
|
||||||
public DeferredMediaPeriod(MediaSource mediaSource, MediaPeriodId id, Allocator allocator) {
|
|
||||||
this.id = id;
|
|
||||||
this.allocator = allocator;
|
|
||||||
this.mediaSource = mediaSource;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void createPeriod() {
|
|
||||||
mediaPeriod = mediaSource.createPeriod(id, allocator);
|
|
||||||
if (callback != null) {
|
|
||||||
mediaPeriod.prepare(this, preparePositionUs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void releasePeriod() {
|
|
||||||
if (mediaPeriod != null) {
|
|
||||||
mediaSource.releasePeriod(mediaPeriod);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void prepare(Callback callback, long preparePositionUs) {
|
|
||||||
this.callback = callback;
|
|
||||||
this.preparePositionUs = preparePositionUs;
|
|
||||||
if (mediaPeriod != null) {
|
|
||||||
mediaPeriod.prepare(this, preparePositionUs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void maybeThrowPrepareError() throws IOException {
|
|
||||||
if (mediaPeriod != null) {
|
|
||||||
mediaPeriod.maybeThrowPrepareError();
|
|
||||||
} else {
|
|
||||||
mediaSource.maybeThrowSourceInfoRefreshError();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public TrackGroupArray getTrackGroups() {
|
|
||||||
return mediaPeriod.getTrackGroups();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long selectTracks(TrackSelection[] selections, boolean[] mayRetainStreamFlags,
|
|
||||||
SampleStream[] streams, boolean[] streamResetFlags, long positionUs) {
|
|
||||||
return mediaPeriod.selectTracks(selections, mayRetainStreamFlags, streams, streamResetFlags,
|
|
||||||
positionUs);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void discardBuffer(long positionUs, boolean toKeyframe) {
|
|
||||||
mediaPeriod.discardBuffer(positionUs, toKeyframe);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long readDiscontinuity() {
|
|
||||||
return mediaPeriod.readDiscontinuity();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long getBufferedPositionUs() {
|
|
||||||
return mediaPeriod.getBufferedPositionUs();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long seekToUs(long positionUs) {
|
|
||||||
return mediaPeriod.seekToUs(positionUs);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long getNextLoadPositionUs() {
|
|
||||||
return mediaPeriod.getNextLoadPositionUs();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean continueLoading(long positionUs) {
|
|
||||||
return mediaPeriod != null && mediaPeriod.continueLoading(positionUs);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onContinueLoadingRequested(MediaPeriod source) {
|
|
||||||
callback.onContinueLoadingRequested(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPrepared(MediaPeriod mediaPeriod) {
|
|
||||||
callback.onPrepared(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.google.android.exoplayer2.source.ads;
|
package com.google.android.exoplayer2.source.ads;
|
||||||
|
|
||||||
|
import android.net.Uri;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
@ -23,15 +24,19 @@ import android.view.ViewGroup;
|
|||||||
import com.google.android.exoplayer2.C;
|
import com.google.android.exoplayer2.C;
|
||||||
import com.google.android.exoplayer2.ExoPlayer;
|
import com.google.android.exoplayer2.ExoPlayer;
|
||||||
import com.google.android.exoplayer2.Timeline;
|
import com.google.android.exoplayer2.Timeline;
|
||||||
|
import com.google.android.exoplayer2.source.DeferredMediaPeriod;
|
||||||
import com.google.android.exoplayer2.source.ExtractorMediaSource;
|
import com.google.android.exoplayer2.source.ExtractorMediaSource;
|
||||||
|
import com.google.android.exoplayer2.source.ExtractorMediaSource.EventListener;
|
||||||
import com.google.android.exoplayer2.source.MediaPeriod;
|
import com.google.android.exoplayer2.source.MediaPeriod;
|
||||||
import com.google.android.exoplayer2.source.MediaSource;
|
import com.google.android.exoplayer2.source.MediaSource;
|
||||||
import com.google.android.exoplayer2.upstream.Allocator;
|
import com.google.android.exoplayer2.upstream.Allocator;
|
||||||
import com.google.android.exoplayer2.upstream.DataSource;
|
import com.google.android.exoplayer2.upstream.DataSource;
|
||||||
import com.google.android.exoplayer2.util.Assertions;
|
import com.google.android.exoplayer2.util.Assertions;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -68,12 +73,12 @@ public final class AdsMediaSource implements MediaSource {
|
|||||||
private static final String TAG = "AdsMediaSource";
|
private static final String TAG = "AdsMediaSource";
|
||||||
|
|
||||||
private final MediaSource contentMediaSource;
|
private final MediaSource contentMediaSource;
|
||||||
private final DataSource.Factory dataSourceFactory;
|
|
||||||
private final AdsLoader adsLoader;
|
private final AdsLoader adsLoader;
|
||||||
private final ViewGroup adUiViewGroup;
|
private final ViewGroup adUiViewGroup;
|
||||||
private final Handler mainHandler;
|
private final Handler mainHandler;
|
||||||
private final ComponentListener componentListener;
|
private final ComponentListener componentListener;
|
||||||
private final Map<MediaPeriod, MediaSource> adMediaSourceByMediaPeriod;
|
private final AdMediaSourceFactory adMediaSourceFactory;
|
||||||
|
private final Map<MediaSource, List<DeferredMediaPeriod>> deferredMediaPeriodByAdMediaSource;
|
||||||
private final Timeline.Period period;
|
private final Timeline.Period period;
|
||||||
@Nullable
|
@Nullable
|
||||||
private final Handler eventHandler;
|
private final Handler eventHandler;
|
||||||
@ -95,6 +100,9 @@ public final class AdsMediaSource implements MediaSource {
|
|||||||
/**
|
/**
|
||||||
* Constructs a new source that inserts ads linearly with the content specified by
|
* Constructs a new source that inserts ads linearly with the content specified by
|
||||||
* {@code contentMediaSource}.
|
* {@code contentMediaSource}.
|
||||||
|
* <p>
|
||||||
|
* Ad media is loaded using {@link ExtractorMediaSource}. If {@code eventListener} is
|
||||||
|
* non-{@code null} it will be notified of both ad tag and ad media load errors.
|
||||||
*
|
*
|
||||||
* @param contentMediaSource The {@link MediaSource} providing the content to play.
|
* @param contentMediaSource The {@link MediaSource} providing the content to play.
|
||||||
* @param dataSourceFactory Factory for data sources used to load ad media.
|
* @param dataSourceFactory Factory for data sources used to load ad media.
|
||||||
@ -109,6 +117,9 @@ public final class AdsMediaSource implements MediaSource {
|
|||||||
/**
|
/**
|
||||||
* Constructs a new source that inserts ads linearly with the content specified by
|
* Constructs a new source that inserts ads linearly with the content specified by
|
||||||
* {@code contentMediaSource}.
|
* {@code contentMediaSource}.
|
||||||
|
* <p>
|
||||||
|
* Ad media is loaded using {@link ExtractorMediaSource}. If {@code eventListener} is
|
||||||
|
* non-{@code null} it will be notified of both ad tag and ad media load errors.
|
||||||
*
|
*
|
||||||
* @param contentMediaSource The {@link MediaSource} providing the content to play.
|
* @param contentMediaSource The {@link MediaSource} providing the content to play.
|
||||||
* @param dataSourceFactory Factory for data sources used to load ad media.
|
* @param dataSourceFactory Factory for data sources used to load ad media.
|
||||||
@ -121,18 +132,18 @@ public final class AdsMediaSource implements MediaSource {
|
|||||||
AdsLoader adsLoader, ViewGroup adUiViewGroup, @Nullable Handler eventHandler,
|
AdsLoader adsLoader, ViewGroup adUiViewGroup, @Nullable Handler eventHandler,
|
||||||
@Nullable AdsListener eventListener) {
|
@Nullable AdsListener eventListener) {
|
||||||
this.contentMediaSource = contentMediaSource;
|
this.contentMediaSource = contentMediaSource;
|
||||||
this.dataSourceFactory = dataSourceFactory;
|
|
||||||
this.adsLoader = adsLoader;
|
this.adsLoader = adsLoader;
|
||||||
this.adUiViewGroup = adUiViewGroup;
|
this.adUiViewGroup = adUiViewGroup;
|
||||||
this.eventHandler = eventHandler;
|
this.eventHandler = eventHandler;
|
||||||
this.eventListener = eventListener;
|
this.eventListener = eventListener;
|
||||||
mainHandler = new Handler(Looper.getMainLooper());
|
mainHandler = new Handler(Looper.getMainLooper());
|
||||||
componentListener = new ComponentListener();
|
componentListener = new ComponentListener();
|
||||||
adMediaSourceByMediaPeriod = new HashMap<>();
|
adMediaSourceFactory = new ExtractorAdMediaSourceFactory(dataSourceFactory);
|
||||||
|
deferredMediaPeriodByAdMediaSource = new HashMap<>();
|
||||||
period = new Timeline.Period();
|
period = new Timeline.Period();
|
||||||
adGroupMediaSources = new MediaSource[0][];
|
adGroupMediaSources = new MediaSource[0][];
|
||||||
adDurationsUs = new long[0][];
|
adDurationsUs = new long[0][];
|
||||||
adsLoader.setSupportedContentTypes(C.TYPE_OTHER);
|
adsLoader.setSupportedContentTypes(adMediaSourceFactory.getSupportedTypes());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -173,10 +184,9 @@ public final class AdsMediaSource implements MediaSource {
|
|||||||
final int adGroupIndex = id.adGroupIndex;
|
final int adGroupIndex = id.adGroupIndex;
|
||||||
final int adIndexInAdGroup = id.adIndexInAdGroup;
|
final int adIndexInAdGroup = id.adIndexInAdGroup;
|
||||||
if (adGroupMediaSources[adGroupIndex].length <= adIndexInAdGroup) {
|
if (adGroupMediaSources[adGroupIndex].length <= adIndexInAdGroup) {
|
||||||
MediaSource adMediaSource = new ExtractorMediaSource.Builder(
|
Uri adUri = adPlaybackState.adUris[id.adGroupIndex][id.adIndexInAdGroup];
|
||||||
adPlaybackState.adUris[id.adGroupIndex][id.adIndexInAdGroup], dataSourceFactory)
|
final MediaSource adMediaSource =
|
||||||
.setEventListener(mainHandler, componentListener)
|
adMediaSourceFactory.createAdMediaSource(adUri, mainHandler, componentListener);
|
||||||
.build();
|
|
||||||
int oldAdCount = adGroupMediaSources[id.adGroupIndex].length;
|
int oldAdCount = adGroupMediaSources[id.adGroupIndex].length;
|
||||||
if (adIndexInAdGroup >= oldAdCount) {
|
if (adIndexInAdGroup >= oldAdCount) {
|
||||||
int adCount = adIndexInAdGroup + 1;
|
int adCount = adIndexInAdGroup + 1;
|
||||||
@ -186,30 +196,37 @@ public final class AdsMediaSource implements MediaSource {
|
|||||||
Arrays.fill(adDurationsUs[adGroupIndex], oldAdCount, adCount, C.TIME_UNSET);
|
Arrays.fill(adDurationsUs[adGroupIndex], oldAdCount, adCount, C.TIME_UNSET);
|
||||||
}
|
}
|
||||||
adGroupMediaSources[adGroupIndex][adIndexInAdGroup] = adMediaSource;
|
adGroupMediaSources[adGroupIndex][adIndexInAdGroup] = adMediaSource;
|
||||||
adMediaSource.prepareSource(player, false, new Listener() {
|
deferredMediaPeriodByAdMediaSource.put(adMediaSource, new ArrayList<DeferredMediaPeriod>());
|
||||||
|
adMediaSource.prepareSource(player, false, new MediaSource.Listener() {
|
||||||
@Override
|
@Override
|
||||||
public void onSourceInfoRefreshed(MediaSource source, Timeline timeline,
|
public void onSourceInfoRefreshed(MediaSource source, Timeline timeline,
|
||||||
Object manifest) {
|
@Nullable Object manifest) {
|
||||||
onAdSourceInfoRefreshed(adGroupIndex, adIndexInAdGroup, timeline);
|
onAdSourceInfoRefreshed(adMediaSource, adGroupIndex, adIndexInAdGroup, timeline);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
MediaSource mediaSource = adGroupMediaSources[adGroupIndex][adIndexInAdGroup];
|
MediaSource mediaSource = adGroupMediaSources[adGroupIndex][adIndexInAdGroup];
|
||||||
MediaPeriod mediaPeriod = mediaSource.createPeriod(new MediaPeriodId(0), allocator);
|
DeferredMediaPeriod deferredMediaPeriod =
|
||||||
adMediaSourceByMediaPeriod.put(mediaPeriod, mediaSource);
|
new DeferredMediaPeriod(mediaSource, new MediaPeriodId(0), allocator);
|
||||||
return mediaPeriod;
|
List<DeferredMediaPeriod> mediaPeriods = deferredMediaPeriodByAdMediaSource.get(mediaSource);
|
||||||
|
if (mediaPeriods == null) {
|
||||||
|
deferredMediaPeriod.createPeriod();
|
||||||
|
} else {
|
||||||
|
// Keep track of the deferred media period so it can be populated with the real media period
|
||||||
|
// when the source's info becomes available.
|
||||||
|
mediaPeriods.add(deferredMediaPeriod);
|
||||||
|
}
|
||||||
|
return deferredMediaPeriod;
|
||||||
} else {
|
} else {
|
||||||
return contentMediaSource.createPeriod(id, allocator);
|
DeferredMediaPeriod mediaPeriod = new DeferredMediaPeriod(contentMediaSource, id, allocator);
|
||||||
|
mediaPeriod.createPeriod();
|
||||||
|
return mediaPeriod;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void releasePeriod(MediaPeriod mediaPeriod) {
|
public void releasePeriod(MediaPeriod mediaPeriod) {
|
||||||
if (adMediaSourceByMediaPeriod.containsKey(mediaPeriod)) {
|
((DeferredMediaPeriod) mediaPeriod).releasePeriod();
|
||||||
adMediaSourceByMediaPeriod.remove(mediaPeriod).releasePeriod(mediaPeriod);
|
|
||||||
} else {
|
|
||||||
contentMediaSource.releasePeriod(mediaPeriod);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -264,9 +281,17 @@ public final class AdsMediaSource implements MediaSource {
|
|||||||
maybeUpdateSourceInfo();
|
maybeUpdateSourceInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onAdSourceInfoRefreshed(int adGroupIndex, int adIndexInAdGroup, Timeline timeline) {
|
private void onAdSourceInfoRefreshed(MediaSource mediaSource, int adGroupIndex,
|
||||||
|
int adIndexInAdGroup, Timeline timeline) {
|
||||||
Assertions.checkArgument(timeline.getPeriodCount() == 1);
|
Assertions.checkArgument(timeline.getPeriodCount() == 1);
|
||||||
adDurationsUs[adGroupIndex][adIndexInAdGroup] = timeline.getPeriod(0, period).getDurationUs();
|
adDurationsUs[adGroupIndex][adIndexInAdGroup] = timeline.getPeriod(0, period).getDurationUs();
|
||||||
|
if (deferredMediaPeriodByAdMediaSource.containsKey(mediaSource)) {
|
||||||
|
List<DeferredMediaPeriod> mediaPeriods = deferredMediaPeriodByAdMediaSource.get(mediaSource);
|
||||||
|
for (int i = 0; i < mediaPeriods.size(); i++) {
|
||||||
|
mediaPeriods.get(i).createPeriod();
|
||||||
|
}
|
||||||
|
deferredMediaPeriodByAdMediaSource.remove(mediaSource);
|
||||||
|
}
|
||||||
maybeUpdateSourceInfo();
|
maybeUpdateSourceInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -285,7 +310,7 @@ public final class AdsMediaSource implements MediaSource {
|
|||||||
* Listener for component events. All methods are called on the main thread.
|
* Listener for component events. All methods are called on the main thread.
|
||||||
*/
|
*/
|
||||||
private final class ComponentListener implements AdsLoader.EventListener,
|
private final class ComponentListener implements AdsLoader.EventListener,
|
||||||
ExtractorMediaSource.EventListener {
|
AdMediaSourceLoadErrorListener {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAdPlaybackState(final AdPlaybackState adPlaybackState) {
|
public void onAdPlaybackState(final AdPlaybackState adPlaybackState) {
|
||||||
@ -349,4 +374,76 @@ public final class AdsMediaSource implements MediaSource {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listener for errors while loading an ad {@link MediaSource}.
|
||||||
|
*/
|
||||||
|
private interface AdMediaSourceLoadErrorListener {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when an error occurs loading media data.
|
||||||
|
*
|
||||||
|
* @param error The load error.
|
||||||
|
*/
|
||||||
|
void onLoadError(IOException error);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Factory for {@link MediaSource}s for loading ad media.
|
||||||
|
*/
|
||||||
|
private interface AdMediaSourceFactory {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new {@link MediaSource} for loading the ad media with the specified {@code uri}.
|
||||||
|
*
|
||||||
|
* @param uri The URI of the ad.
|
||||||
|
* @param handler A handler for listener events.
|
||||||
|
* @param listener A listener for ad load errors. To have ad media source load errors notified
|
||||||
|
* via the ads media source's listener, call this listener's onLoadError method from your
|
||||||
|
* new media source's load error listener using the specified {@code handler}. Otherwise,
|
||||||
|
* this parameter can be ignored.
|
||||||
|
* @return The new media source.
|
||||||
|
*/
|
||||||
|
MediaSource createAdMediaSource(Uri uri, Handler handler,
|
||||||
|
AdMediaSourceLoadErrorListener listener);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the content types supported by media sources created by this factory. Each element
|
||||||
|
* should be one of {@link C#TYPE_DASH}, {@link C#TYPE_SS}, {@link C#TYPE_HLS} or
|
||||||
|
* {@link C#TYPE_OTHER}.
|
||||||
|
*
|
||||||
|
* @return The content types supported by the factory.
|
||||||
|
*/
|
||||||
|
int[] getSupportedTypes();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class ExtractorAdMediaSourceFactory implements AdMediaSourceFactory {
|
||||||
|
|
||||||
|
private final DataSource.Factory dataSourceFactory;
|
||||||
|
|
||||||
|
public ExtractorAdMediaSourceFactory(DataSource.Factory dataSourceFactory) {
|
||||||
|
this.dataSourceFactory = dataSourceFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MediaSource createAdMediaSource(Uri uri, Handler handler,
|
||||||
|
final AdMediaSourceLoadErrorListener listener) {
|
||||||
|
return new ExtractorMediaSource.Builder(uri, dataSourceFactory).setEventListener(handler,
|
||||||
|
new EventListener() {
|
||||||
|
@Override
|
||||||
|
public void onLoadError(IOException error) {
|
||||||
|
listener.onLoadError(error);
|
||||||
|
}
|
||||||
|
}).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int[] getSupportedTypes() {
|
||||||
|
// Only ExtractorMediaSource is supported.
|
||||||
|
return new int[] {C.TYPE_OTHER};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user