Add tracksInfoAvailable parameter in DownloadHelper.Callback.onPrepared

PiperOrigin-RevId: 733311800
This commit is contained in:
tianyifeng 2025-03-04 06:51:38 -08:00 committed by Copybara-Service
parent c3d734066d
commit 4ab7ddea93
3 changed files with 96 additions and 28 deletions

View File

@ -168,6 +168,7 @@ public class DownloadTracker {
private final DownloadHelper downloadHelper;
private final MediaItem mediaItem;
private boolean tracksInfoAvailable;
private TrackSelectionDialog trackSelectionDialog;
private WidevineOfflineLicenseFetchTask widevineOfflineLicenseFetchTask;
@Nullable private byte[] keySetId;
@ -193,7 +194,8 @@ public class DownloadTracker {
// DownloadHelper.Callback implementation.
@Override
public void onPrepared(DownloadHelper helper) {
public void onPrepared(DownloadHelper helper, boolean tracksInfoAvailable) {
this.tracksInfoAvailable = tracksInfoAvailable;
@Nullable Format format = getFirstFormatWithDrmInitData(helper);
if (format == null) {
onDownloadPrepared(helper);
@ -237,6 +239,7 @@ public class DownloadTracker {
@Override
public void onTracksSelected(TrackSelectionParameters trackSelectionParameters) {
checkState(tracksInfoAvailable);
for (int periodIndex = 0; periodIndex < downloadHelper.getPeriodCount(); periodIndex++) {
downloadHelper.clearTrackSelections(periodIndex);
downloadHelper.addTrackSelection(periodIndex, trackSelectionParameters);
@ -265,6 +268,9 @@ public class DownloadTracker {
*/
@Nullable
private Format getFirstFormatWithDrmInitData(DownloadHelper helper) {
if (!tracksInfoAvailable) {
return null;
}
for (int periodIndex = 0; periodIndex < helper.getPeriodCount(); periodIndex++) {
MappedTrackInfo mappedTrackInfo = helper.getMappedTrackInfo(periodIndex);
for (int rendererIndex = 0;
@ -304,6 +310,13 @@ public class DownloadTracker {
return;
}
if (!tracksInfoAvailable) {
Log.d(TAG, "Tracks info is unavailable. Downloading entire stream.");
startDownload();
downloadHelper.release();
return;
}
Tracks tracks = downloadHelper.getTracks(/* periodIndex= */ 0);
if (!TrackSelectionDialog.willHaveContent(tracks)) {
Log.d(TAG, "No dialog content. Downloading entire stream.");

View File

@ -155,8 +155,9 @@ public final class DownloadHelper {
* Called when preparation completes.
*
* @param helper The reporting {@link DownloadHelper}.
* @param tracksInfoAvailable Whether tracks information is available.
*/
void onPrepared(DownloadHelper helper);
void onPrepared(DownloadHelper helper, boolean tracksInfoAvailable);
/**
* Called when preparation fails.
@ -550,7 +551,7 @@ public final class DownloadHelper {
if (mode != MODE_NOT_PREPARE) {
mediaPreparer = new MediaPreparer(checkNotNull(mediaSource), /* downloadHelper= */ this);
} else {
callbackHandler.post(() -> callback.onPrepared(this));
callbackHandler.post(() -> callback.onPrepared(this, /* tracksInfoAvailable= */ false));
}
}
@ -564,12 +565,12 @@ public final class DownloadHelper {
}
/**
* Returns the manifest, or null if no manifest is loaded. Must not be called until after
* preparation completes.
* Returns the manifest, or null if no manifest is loaded. Must not be called until {@link
* Callback#onPrepared(DownloadHelper, boolean)} is triggered.
*/
@Nullable
public Object getManifest() {
if (mediaSource == null) {
if (mode == MODE_NOT_PREPARE) {
return null;
}
assertPreparedWithMedia();
@ -579,11 +580,11 @@ public final class DownloadHelper {
}
/**
* Returns the number of periods for which media is available. Must not be called until after
* preparation completes.
* Returns the number of periods for which media is available. Must not be called until {@link
* Callback#onPrepared(DownloadHelper, boolean)} is triggered.
*/
public int getPeriodCount() {
if (mediaSource == null) {
if (mode == MODE_NOT_PREPARE) {
return 0;
}
assertPreparedWithMedia();
@ -591,8 +592,9 @@ public final class DownloadHelper {
}
/**
* Returns {@link Tracks} for the given period. Must not be called until after preparation
* completes.
* Returns {@link Tracks} for the given period. Must not be called until {@link
* Callback#onPrepared(DownloadHelper, boolean)} is triggered and the passed {@code
* tracksInfoAvailable} is {@code true}.
*
* @param periodIndex The period index.
* @return The {@link Tracks} for the period. May be {@link Tracks#EMPTY} for single stream
@ -605,8 +607,9 @@ public final class DownloadHelper {
}
/**
* Returns the track groups for the given period. Must not be called until after preparation
* completes.
* Returns the track groups for the given period. Must not be called until {@link
* Callback#onPrepared(DownloadHelper, boolean)} is triggered and the passed {@code
* tracksInfoAvailable} is {@code true}.
*
* <p>Use {@link #getMappedTrackInfo(int)} to get the track groups mapped to renderers.
*
@ -620,8 +623,9 @@ public final class DownloadHelper {
}
/**
* Returns the mapped track info for the given period. Must not be called until after preparation
* completes.
* Returns the mapped track info for the given period. Must not be called until {@link
* Callback#onPrepared(DownloadHelper, boolean)} is triggered and the passed {@code
* tracksInfoAvailable} is {@code true}.
*
* @param periodIndex The period index.
* @return The {@link MappedTrackInfo} for the period.
@ -633,7 +637,8 @@ public final class DownloadHelper {
/**
* Returns all {@link ExoTrackSelection track selections} for a period and renderer. Must not be
* called until after preparation completes.
* called until {@link Callback#onPrepared(DownloadHelper, boolean)} is triggered and the passed
* {@code tracksInfoAvailable} is {@code true}.
*
* @param periodIndex The period index.
* @param rendererIndex The renderer index.
@ -645,8 +650,9 @@ public final class DownloadHelper {
}
/**
* Clears the selection of tracks for a period. Must not be called until after preparation
* completes.
* Clears the selection of tracks for a period. Must not be called until {@link
* Callback#onPrepared(DownloadHelper, boolean)} is triggered and the passed {@code
* tracksInfoAvailable} is {@code true}.
*
* @param periodIndex The period index for which track selections are cleared.
*/
@ -658,8 +664,9 @@ public final class DownloadHelper {
}
/**
* Replaces a selection of tracks to be downloaded. Must not be called until after preparation
* completes.
* Replaces a selection of tracks to be downloaded. Must not be called until {@link
* Callback#onPrepared(DownloadHelper, boolean)} is triggered and the passed {@code
* tracksInfoAvailable} is {@code true}.
*
* @param periodIndex The period index for which the track selection is replaced.
* @param trackSelectionParameters The {@link TrackSelectionParameters} to obtain the new
@ -677,8 +684,9 @@ public final class DownloadHelper {
}
/**
* Adds a selection of tracks to be downloaded. Must not be called until after preparation
* completes.
* Adds a selection of tracks to be downloaded. Must not be called until {@link
* Callback#onPrepared(DownloadHelper, boolean)} is triggered and the passed {@code
* tracksInfoAvailable} is {@code true}.
*
* @param periodIndex The period index this track selection is added for.
* @param trackSelectionParameters The {@link TrackSelectionParameters} to obtain the new
@ -697,7 +705,8 @@ public final class DownloadHelper {
/**
* Convenience method to add selections of tracks for all specified audio languages. If an audio
* track in one of the specified languages is not available, the default fallback audio track is
* used instead. Must not be called until after preparation completes.
* used instead. Must not be called until {@link Callback#onPrepared(DownloadHelper, boolean)} is
* triggered and the passed {@code tracksInfoAvailable} is {@code true}.
*
* @param languages A list of audio languages for which tracks should be added to the download
* selection, as IETF BCP 47 conformant tags.
@ -733,7 +742,8 @@ public final class DownloadHelper {
/**
* Convenience method to add selections of tracks for all specified text languages. Must not be
* called until after preparation completes.
* called until {@link Callback#onPrepared(DownloadHelper, boolean)} is triggered and the passed
* {@code tracksInfoAvailable} is {@code true}.
*
* @param selectUndeterminedTextLanguage Whether a text track with undetermined language should be
* selected for downloading if no track with one of the specified {@code languages} is
@ -774,7 +784,8 @@ public final class DownloadHelper {
/**
* Convenience method to add a selection of tracks to be downloaded for a single renderer. Must
* not be called until after preparation completes.
* not be called until {@link Callback#onPrepared(DownloadHelper, boolean)} is triggered and the
* passed {@code tracksInfoAvailable} is {@code true}.
*
* @param periodIndex The period index the track selection is added for.
* @param rendererIndex The renderer index the track selection is added for.
@ -987,6 +998,7 @@ public final class DownloadHelper {
checkNotNull(mediaPreparer);
checkNotNull(mediaPreparer.mediaPeriods);
checkNotNull(mediaPreparer.timeline);
boolean tracksInfoAvailable;
if (mode == MODE_PREPARE_NON_PROGRESSIVE_SOURCE_AND_SELECT_TRACKS) {
int periodCount = mediaPreparer.mediaPeriods.length;
int rendererCount = rendererCapabilities.size();
@ -1009,13 +1021,16 @@ public final class DownloadHelper {
trackSelector.onSelectionActivated(trackSelectorResult.info);
mappedTrackInfos[i] = checkNotNull(trackSelector.getCurrentMappedTrackInfo());
}
tracksInfoAvailable = true;
setPreparedWithNonProgressiveSourceAndTracksSelected();
} else {
checkState(mode == MODE_PREPARE_PROGRESSIVE_SOURCE);
checkNotNull(mediaPreparer.seekMap);
tracksInfoAvailable = false;
setPreparedWithProgressiveSource();
}
checkNotNull(callbackHandler).post(() -> checkNotNull(callback).onPrepared(this));
checkNotNull(callbackHandler)
.post(() -> checkNotNull(callback).onPrepared(this, tracksInfoAvailable));
}
private void onMediaPreparationFailed(IOException error) {

View File

@ -60,6 +60,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import org.junit.Before;
import org.junit.BeforeClass;
@ -136,6 +137,41 @@ public class DownloadHelperTest {
.createRendererCapabilitiesList());
}
@Test
public void prepare_withoutMediaSource_tracksInfoNotAvailable() throws Exception {
// DownloadHelper will be constructed without MediaSource if no DataSource.Factory is provided.
DownloadHelper downloadHelper =
DownloadHelper.forMediaItem(
getApplicationContext(), MediaItem.fromUri("asset:///media/mp4/sample.mp4"));
boolean tracksInfoAvailable = prepareDownloadHelper(downloadHelper);
assertThat(tracksInfoAvailable).isFalse();
}
@Test
public void prepare_prepareProgressiveSource_tracksInfoNotAvailable() throws Exception {
Context context = getApplicationContext();
DownloadHelper downloadHelper =
DownloadHelper.forMediaItem(
context,
MediaItem.fromUri("asset:///media/mp4/sample.mp4"),
new DefaultDataSource.Factory(context));
boolean tracksInfoAvailable = prepareDownloadHelper(downloadHelper);
assertThat(tracksInfoAvailable).isFalse();
}
@Test
public void prepare_prepareNonProgressiveSource_tracksInfoAvailable() throws Exception {
// We use this.downloadHelper as it was created with a TestMediaSource, thus the DownloadHelper
// will treat it as non-progressive.
boolean tracksInfoAvailable = prepareDownloadHelper(downloadHelper);
assertThat(tracksInfoAvailable).isTrue();
}
@Test
public void getManifest_returnsManifest() throws Exception {
prepareDownloadHelper(downloadHelper);
@ -749,14 +785,16 @@ public class DownloadHelperTest {
assertThat(exception.get()).isNull();
}
private static void prepareDownloadHelper(DownloadHelper downloadHelper) throws Exception {
private static boolean prepareDownloadHelper(DownloadHelper downloadHelper) throws Exception {
AtomicBoolean tracksInfoAvailableRef = new AtomicBoolean();
AtomicReference<Exception> prepareException = new AtomicReference<>(null);
CountDownLatch preparedLatch = new CountDownLatch(1);
downloadHelper.prepare(
new Callback() {
@Override
public void onPrepared(DownloadHelper helper) {
public void onPrepared(DownloadHelper helper, boolean tracksInfoAvailable) {
preparedLatch.countDown();
tracksInfoAvailableRef.set(tracksInfoAvailable);
}
@Override
@ -771,6 +809,8 @@ public class DownloadHelperTest {
if (prepareException.get() != null) {
throw prepareException.get();
}
return tracksInfoAvailableRef.get();
}
private static Format createVideoFormat(int bitrate) {