Fix download percentage reporting
- When calculating the downloaded percentage in DASH, there was no way to disambiguate between 0 of 0 segments being downloaded because there are no cached indexes (i.e. 0% downloaded) and 0 of 0 segments being downloaded because the index defines 0 segments (i.e. 100% downloaded). - Also replace use of NaN with a named constant. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=194453202
This commit is contained in:
parent
fe32401792
commit
3e76464666
@ -204,7 +204,8 @@ public class DownloadActivity extends Activity {
|
||||
public RepresentationItem(Parcelable key, String title, float percentDownloaded) {
|
||||
this.key = key;
|
||||
this.title = title;
|
||||
this.percentDownloaded = (int) percentDownloaded;
|
||||
this.percentDownloaded =
|
||||
(int) (percentDownloaded == C.PERCENTAGE_UNSET ? 0 : percentDownloaded);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -64,6 +64,9 @@ public final class C {
|
||||
*/
|
||||
public static final int LENGTH_UNSET = -1;
|
||||
|
||||
/** Represents an unset or unknown percentage. */
|
||||
public static final int PERCENTAGE_UNSET = -1;
|
||||
|
||||
/**
|
||||
* The number of microseconds in one second.
|
||||
*/
|
||||
|
@ -541,14 +541,11 @@ public final class DownloadManager {
|
||||
/** The state of the task. See {@link State}. */
|
||||
public final @State int state;
|
||||
/**
|
||||
* The download percentage, or {@link Float#NaN} if it can't be calculated or the task is for
|
||||
* removing.
|
||||
* The estimated download percentage, or {@link C#PERCENTAGE_UNSET} if no estimate is available
|
||||
* or if this is a removal task.
|
||||
*/
|
||||
public final float downloadPercentage;
|
||||
/**
|
||||
* The downloaded bytes, or {@link C#LENGTH_UNSET} if it hasn't been calculated yet or the task
|
||||
* is for removing.
|
||||
*/
|
||||
/** The total number of downloaded bytes. */
|
||||
public final long downloadedBytes;
|
||||
/** If {@link #state} is {@link #STATE_ERROR} then this is the cause, otherwise null. */
|
||||
public final Throwable error;
|
||||
@ -648,19 +645,16 @@ public final class DownloadManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the download percentage, or {@link Float#NaN} if it can't be calculated yet. This
|
||||
* value can be an estimation.
|
||||
* Returns the estimated download percentage, or {@link C#PERCENTAGE_UNSET} if no estimate is
|
||||
* available.
|
||||
*/
|
||||
public float getDownloadPercentage() {
|
||||
return downloader != null ? downloader.getDownloadPercentage() : Float.NaN;
|
||||
return downloader != null ? downloader.getDownloadPercentage() : C.PERCENTAGE_UNSET;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total number of downloaded bytes, or {@link C#LENGTH_UNSET} if it hasn't been
|
||||
* calculated yet.
|
||||
*/
|
||||
/** Returns the total number of downloaded bytes. */
|
||||
public long getDownloadedBytes() {
|
||||
return downloader != null ? downloader.getDownloadedBytes() : C.LENGTH_UNSET;
|
||||
return downloader != null ? downloader.getDownloadedBytes() : 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -29,8 +29,6 @@ public interface Downloader {
|
||||
* @throws DownloadException Thrown if the media cannot be downloaded.
|
||||
* @throws InterruptedException If the thread has been interrupted.
|
||||
* @throws IOException Thrown when there is an io error while reading from cache.
|
||||
* @see #getDownloadedBytes()
|
||||
* @see #getDownloadPercentage()
|
||||
*/
|
||||
void init() throws InterruptedException, IOException;
|
||||
|
||||
@ -50,20 +48,12 @@ public interface Downloader {
|
||||
*/
|
||||
void remove() throws InterruptedException;
|
||||
|
||||
/**
|
||||
* Returns the total number of downloaded bytes, or {@link C#LENGTH_UNSET} if it hasn't been
|
||||
* calculated yet.
|
||||
*
|
||||
* @see #init()
|
||||
*/
|
||||
/** Returns the total number of downloaded bytes. */
|
||||
long getDownloadedBytes();
|
||||
|
||||
/**
|
||||
* Returns the download percentage, or {@link Float#NaN} if it can't be calculated yet. This
|
||||
* value can be an estimation.
|
||||
*
|
||||
* @see #init()
|
||||
* Returns the estimated download percentage, or {@link C#PERCENTAGE_UNSET} if no estimate is
|
||||
* available.
|
||||
*/
|
||||
float getDownloadPercentage();
|
||||
|
||||
}
|
||||
|
@ -83,7 +83,8 @@ public final class ProgressiveDownloader implements Downloader {
|
||||
@Override
|
||||
public float getDownloadPercentage() {
|
||||
long contentLength = cachingCounters.contentLength;
|
||||
return contentLength == C.LENGTH_UNSET ? Float.NaN
|
||||
return contentLength == C.LENGTH_UNSET
|
||||
? C.PERCENTAGE_UNSET
|
||||
: ((cachingCounters.totalCachedBytes() * 100f) / contentLength);
|
||||
}
|
||||
|
||||
|
@ -17,6 +17,7 @@ package com.google.android.exoplayer2.offline;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.util.Pair;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.upstream.DataSource;
|
||||
import com.google.android.exoplayer2.upstream.DataSpec;
|
||||
@ -33,9 +34,6 @@ import java.util.List;
|
||||
/**
|
||||
* Base class for multi segment stream downloaders.
|
||||
*
|
||||
* <p>All of the methods are blocking. Also they are not thread safe, except {@link
|
||||
* #getTotalSegments()}, {@link #getDownloadedSegments()} and {@link #getDownloadedBytes()}.
|
||||
*
|
||||
* @param <M> The type of the manifest object.
|
||||
* @param <K> The type of the representation key object.
|
||||
*/
|
||||
@ -101,6 +99,9 @@ public abstract class SegmentDownloader<M extends FilterableManifest<M, K>, K>
|
||||
return getManifestIfNeeded(false);
|
||||
}
|
||||
|
||||
/** Returns keys for all representations. */
|
||||
public abstract K[] getAllRepresentationKeys() throws IOException;
|
||||
|
||||
/**
|
||||
* Selects multiple representations pointed to by the keys for downloading, checking status. Any
|
||||
* previous selection is cleared. If keys array is null or empty then all representations are
|
||||
@ -113,25 +114,13 @@ public abstract class SegmentDownloader<M extends FilterableManifest<M, K>, K>
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns keys for all representations.
|
||||
* Initializes the downloader for the selected representations.
|
||||
*
|
||||
* @see #selectRepresentations(Object[])
|
||||
*/
|
||||
public abstract K[] getAllRepresentationKeys() throws IOException;
|
||||
|
||||
/**
|
||||
* Initializes the total segments, downloaded segments and downloaded bytes counters for the
|
||||
* selected representations.
|
||||
*
|
||||
* @throws IOException Thrown when there is an io error while reading from cache.
|
||||
* @throws DownloadException Thrown if the media cannot be downloaded.
|
||||
* @throws IOException Thrown when there is an error downloading.
|
||||
* @throws InterruptedException If the thread has been interrupted.
|
||||
* @see #getTotalSegments()
|
||||
* @see #getDownloadedSegments()
|
||||
* @see #getDownloadedBytes()
|
||||
*/
|
||||
@Override
|
||||
public final void init() throws InterruptedException, IOException {
|
||||
public final void init() throws IOException, InterruptedException {
|
||||
try {
|
||||
getManifestIfNeeded(true);
|
||||
} catch (IOException e) {
|
||||
@ -140,7 +129,7 @@ public abstract class SegmentDownloader<M extends FilterableManifest<M, K>, K>
|
||||
}
|
||||
try {
|
||||
initStatus(true);
|
||||
} catch (IOException | InterruptedException e) {
|
||||
} catch (IOException e) {
|
||||
resetCounters();
|
||||
throw e;
|
||||
}
|
||||
@ -150,8 +139,7 @@ public abstract class SegmentDownloader<M extends FilterableManifest<M, K>, K>
|
||||
* Downloads the content for the selected representations in sync or resumes a previously stopped
|
||||
* download.
|
||||
*
|
||||
* @throws IOException Thrown when there is an io error while downloading.
|
||||
* @throws DownloadException Thrown if the media cannot be downloaded.
|
||||
* @throws IOException Thrown when there is an error downloading.
|
||||
* @throws InterruptedException If the thread has been interrupted.
|
||||
*/
|
||||
@Override
|
||||
@ -174,32 +162,6 @@ public abstract class SegmentDownloader<M extends FilterableManifest<M, K>, K>
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total number of segments in the representations which are selected, or {@link
|
||||
* C#LENGTH_UNSET} if it hasn't been calculated yet.
|
||||
*
|
||||
* @see #init()
|
||||
*/
|
||||
public final int getTotalSegments() {
|
||||
return totalSegments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total number of downloaded segments in the representations which are selected, or
|
||||
* {@link C#LENGTH_UNSET} if it hasn't been calculated yet.
|
||||
*
|
||||
* @see #init()
|
||||
*/
|
||||
public final int getDownloadedSegments() {
|
||||
return downloadedSegments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total number of downloaded bytes in the representations which are selected, or
|
||||
* {@link C#LENGTH_UNSET} if it hasn't been calculated yet.
|
||||
*
|
||||
* @see #init()
|
||||
*/
|
||||
@Override
|
||||
public final long getDownloadedBytes() {
|
||||
return downloadedBytes;
|
||||
@ -211,7 +173,7 @@ public abstract class SegmentDownloader<M extends FilterableManifest<M, K>, K>
|
||||
int totalSegments = this.totalSegments;
|
||||
int downloadedSegments = this.downloadedSegments;
|
||||
if (totalSegments == C.LENGTH_UNSET || downloadedSegments == C.LENGTH_UNSET) {
|
||||
return Float.NaN;
|
||||
return C.PERCENTAGE_UNSET;
|
||||
}
|
||||
return totalSegments == 0 ? 100f : (downloadedSegments * 100f) / totalSegments;
|
||||
}
|
||||
@ -228,7 +190,7 @@ public abstract class SegmentDownloader<M extends FilterableManifest<M, K>, K>
|
||||
if (manifest != null) {
|
||||
List<Segment> segments = null;
|
||||
try {
|
||||
segments = getSegments(offlineDataSource, manifest, true);
|
||||
segments = getSegments(offlineDataSource, manifest, true).first;
|
||||
} catch (IOException e) {
|
||||
// Ignore exceptions. We do our best with what's available offline.
|
||||
}
|
||||
@ -263,16 +225,17 @@ public abstract class SegmentDownloader<M extends FilterableManifest<M, K>, K>
|
||||
* @throws InterruptedException Thrown if the thread was interrupted.
|
||||
* @throws IOException Thrown if {@code allowPartialIndex} is false and a load error occurs, or if
|
||||
* the media is not in a form that allows for its segments to be listed.
|
||||
* @return A list of {@link Segment}s for given keys.
|
||||
* @return A list of {@link Segment}s for given keys, and a boolean indicating whether the list is
|
||||
* complete.
|
||||
*/
|
||||
protected abstract List<Segment> getSegments(
|
||||
protected abstract Pair<List<Segment>, Boolean> getSegments(
|
||||
DataSource dataSource, M manifest, boolean allowIncompleteIndex)
|
||||
throws InterruptedException, IOException;
|
||||
|
||||
private void resetCounters() {
|
||||
totalSegments = C.LENGTH_UNSET;
|
||||
downloadedSegments = C.LENGTH_UNSET;
|
||||
downloadedBytes = C.LENGTH_UNSET;
|
||||
downloadedSegments = 0;
|
||||
downloadedBytes = 0;
|
||||
}
|
||||
|
||||
private void remove(Uri uri) {
|
||||
@ -289,9 +252,11 @@ public abstract class SegmentDownloader<M extends FilterableManifest<M, K>, K>
|
||||
throws IOException, InterruptedException {
|
||||
DataSource dataSource = getDataSource(offline);
|
||||
M filteredManifest = keys.isEmpty() ? manifest : manifest.copy(keys);
|
||||
List<Segment> segments = getSegments(dataSource, filteredManifest, offline);
|
||||
Pair<List<Segment>, Boolean> result = getSegments(dataSource, filteredManifest, offline);
|
||||
List<Segment> segments = result.first;
|
||||
boolean isSegmentListComplete = result.second;
|
||||
CachingCounters cachingCounters = new CachingCounters();
|
||||
totalSegments = segments.size();
|
||||
totalSegments = isSegmentListComplete ? segments.size() : C.LENGTH_UNSET;
|
||||
downloadedSegments = 0;
|
||||
downloadedBytes = 0;
|
||||
for (int i = segments.size() - 1; i >= 0; i--) {
|
||||
|
@ -16,6 +16,7 @@
|
||||
package com.google.android.exoplayer2.source.dash.offline;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.util.Pair;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.extractor.ChunkIndex;
|
||||
import com.google.android.exoplayer2.offline.DownloadException;
|
||||
@ -39,9 +40,6 @@ import java.util.List;
|
||||
/**
|
||||
* Helper class to download DASH streams.
|
||||
*
|
||||
* <p>Except {@link #getTotalSegments()}, {@link #getDownloadedSegments()} and {@link
|
||||
* #getDownloadedBytes()}, this class isn't thread safe.
|
||||
*
|
||||
* <p>Example usage:
|
||||
*
|
||||
* <pre>{@code
|
||||
@ -89,29 +87,32 @@ public final class DashDownloader extends SegmentDownloader<DashManifest, Repres
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<Segment> getSegments(
|
||||
protected Pair<List<Segment>, Boolean> getSegments(
|
||||
DataSource dataSource, DashManifest manifest, boolean allowIndexLoadErrors)
|
||||
throws InterruptedException, IOException {
|
||||
ArrayList<Segment> segments = new ArrayList<>();
|
||||
boolean segmentListComplete = true;
|
||||
for (int i = 0; i < manifest.getPeriodCount(); i++) {
|
||||
Period period = manifest.getPeriod(i);
|
||||
long periodStartUs = C.msToUs(period.startMs);
|
||||
long periodDurationUs = manifest.getPeriodDurationUs(i);
|
||||
List<AdaptationSet> adaptationSets = period.adaptationSets;
|
||||
for (int j = 0; j < adaptationSets.size(); j++) {
|
||||
addSegmentsForAdaptationSet(
|
||||
if (!addSegmentsForAdaptationSet(
|
||||
dataSource,
|
||||
adaptationSets.get(j),
|
||||
periodStartUs,
|
||||
periodDurationUs,
|
||||
allowIndexLoadErrors,
|
||||
segments);
|
||||
segments)) {
|
||||
segmentListComplete = false;
|
||||
}
|
||||
}
|
||||
return segments;
|
||||
}
|
||||
return Pair.<List<Segment>, Boolean>create(segments, segmentListComplete);
|
||||
}
|
||||
|
||||
private static void addSegmentsForAdaptationSet(
|
||||
private static boolean addSegmentsForAdaptationSet(
|
||||
DataSource dataSource,
|
||||
AdaptationSet adaptationSet,
|
||||
long periodStartUs,
|
||||
@ -119,6 +120,7 @@ public final class DashDownloader extends SegmentDownloader<DashManifest, Repres
|
||||
boolean allowIndexLoadErrors,
|
||||
ArrayList<Segment> out)
|
||||
throws IOException, InterruptedException {
|
||||
boolean segmentListComplete = true;
|
||||
for (int i = 0; i < adaptationSet.representations.size(); i++) {
|
||||
Representation representation = adaptationSet.representations.get(i);
|
||||
DashSegmentIndex index;
|
||||
@ -129,12 +131,12 @@ public final class DashDownloader extends SegmentDownloader<DashManifest, Repres
|
||||
throw new DownloadException("Missing segment index");
|
||||
}
|
||||
} catch (IOException e) {
|
||||
if (allowIndexLoadErrors) {
|
||||
// Loading failed, but load errors are allowed. Advance to the next representation.
|
||||
continue;
|
||||
} else {
|
||||
if (!allowIndexLoadErrors) {
|
||||
throw e;
|
||||
}
|
||||
// Loading failed, but load errors are allowed. Advance to the next representation.
|
||||
segmentListComplete = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
int segmentCount = index.getSegmentCount(periodDurationUs);
|
||||
@ -157,6 +159,8 @@ public final class DashDownloader extends SegmentDownloader<DashManifest, Repres
|
||||
addSegment(periodStartUs + index.getTimeUs(j), baseUrl, index.getSegmentUrl(j), out);
|
||||
}
|
||||
}
|
||||
|
||||
return segmentListComplete;
|
||||
}
|
||||
|
||||
private static void addSegment(
|
||||
|
@ -26,7 +26,6 @@ import static org.junit.Assert.fail;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.offline.DownloadException;
|
||||
import com.google.android.exoplayer2.offline.DownloaderConstructorHelper;
|
||||
import com.google.android.exoplayer2.source.dash.manifest.DashManifest;
|
||||
@ -65,7 +64,7 @@ public class DashDownloaderTest {
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
public void tearDown() {
|
||||
Util.recursiveDelete(tempFolder);
|
||||
}
|
||||
|
||||
@ -311,11 +310,11 @@ public class DashDownloaderTest {
|
||||
.setRandomData("audio_segment_3", 6);
|
||||
DashDownloader dashDownloader = getDashDownloader(fakeDataSet);
|
||||
|
||||
assertCounters(dashDownloader, C.LENGTH_UNSET, C.LENGTH_UNSET, C.LENGTH_UNSET);
|
||||
assertThat(dashDownloader.getDownloadedBytes()).isEqualTo(0);
|
||||
|
||||
dashDownloader.selectRepresentations(new RepresentationKey[] {new RepresentationKey(0, 0, 0)});
|
||||
dashDownloader.init();
|
||||
assertCounters(dashDownloader, C.LENGTH_UNSET, C.LENGTH_UNSET, C.LENGTH_UNSET);
|
||||
assertThat(dashDownloader.getDownloadedBytes()).isEqualTo(0);
|
||||
|
||||
// downloadRepresentations fails after downloading init data, segment 1 and 2 bytes in segment 2
|
||||
try {
|
||||
@ -325,11 +324,10 @@ public class DashDownloaderTest {
|
||||
// ignore
|
||||
}
|
||||
dashDownloader.init();
|
||||
assertCounters(dashDownloader, 4, 2, 10 + 4 + 2);
|
||||
assertThat(dashDownloader.getDownloadedBytes()).isEqualTo(10 + 4 + 2);
|
||||
|
||||
dashDownloader.download();
|
||||
|
||||
assertCounters(dashDownloader, 4, 4, 10 + 4 + 5 + 6);
|
||||
assertThat(dashDownloader.getDownloadedBytes()).isEqualTo(10 + 4 + 5 + 6);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -399,13 +397,4 @@ public class DashDownloaderTest {
|
||||
return new DashDownloader(TEST_MPD_URI, new DownloaderConstructorHelper(cache, factory));
|
||||
}
|
||||
|
||||
private static void assertCounters(
|
||||
DashDownloader dashDownloader,
|
||||
int totalSegments,
|
||||
int downloadedSegments,
|
||||
int downloadedBytes) {
|
||||
assertThat(dashDownloader.getTotalSegments()).isEqualTo(totalSegments);
|
||||
assertThat(dashDownloader.getDownloadedSegments()).isEqualTo(downloadedSegments);
|
||||
assertThat(dashDownloader.getDownloadedBytes()).isEqualTo(downloadedBytes);
|
||||
}
|
||||
}
|
||||
|
@ -16,6 +16,7 @@
|
||||
package com.google.android.exoplayer2.source.hls.offline;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.util.Pair;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.offline.DownloaderConstructorHelper;
|
||||
import com.google.android.exoplayer2.offline.SegmentDownloader;
|
||||
@ -70,17 +71,17 @@ public final class HlsDownloader extends SegmentDownloader<HlsMasterPlaylist, Re
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<Segment> getSegments(
|
||||
DataSource dataSource,
|
||||
HlsMasterPlaylist manifest,
|
||||
boolean allowIndexLoadErrors)
|
||||
throws InterruptedException, IOException {
|
||||
protected Pair<List<Segment>, Boolean> getSegments(
|
||||
DataSource dataSource, HlsMasterPlaylist manifest, boolean allowIndexLoadErrors)
|
||||
throws IOException {
|
||||
HashSet<Uri> encryptionKeyUris = new HashSet<>();
|
||||
ArrayList<HlsUrl> renditionUrls = new ArrayList<>();
|
||||
renditionUrls.addAll(manifest.variants);
|
||||
renditionUrls.addAll(manifest.audios);
|
||||
renditionUrls.addAll(manifest.subtitles);
|
||||
ArrayList<Segment> segments = new ArrayList<>();
|
||||
|
||||
boolean segmentListComplete = true;
|
||||
for (HlsUrl renditionUrl : renditionUrls) {
|
||||
HlsMediaPlaylist mediaPlaylist = null;
|
||||
Uri uri = UriUtil.resolveToUri(manifest.baseUri, renditionUrl.url);
|
||||
@ -90,6 +91,7 @@ public final class HlsDownloader extends SegmentDownloader<HlsMasterPlaylist, Re
|
||||
if (!allowIndexLoadErrors) {
|
||||
throw e;
|
||||
}
|
||||
segmentListComplete = false;
|
||||
}
|
||||
segments.add(new Segment(mediaPlaylist != null ? mediaPlaylist.startTimeUs : Long.MIN_VALUE,
|
||||
new DataSpec(uri)));
|
||||
@ -109,7 +111,7 @@ public final class HlsDownloader extends SegmentDownloader<HlsMasterPlaylist, Re
|
||||
addSegment(segments, mediaPlaylist, segment, encryptionKeyUris);
|
||||
}
|
||||
}
|
||||
return segments;
|
||||
return Pair.<List<Segment>, Boolean>create(segments, segmentListComplete);
|
||||
}
|
||||
|
||||
private static HlsPlaylist loadManifest(DataSource dataSource, Uri uri) throws IOException {
|
||||
|
@ -111,8 +111,6 @@ public class HlsDownloaderTest {
|
||||
hlsDownloader.selectRepresentations(getKeys(MEDIA_PLAYLIST_1_URI));
|
||||
hlsDownloader.download();
|
||||
|
||||
assertThat(hlsDownloader.getTotalSegments()).isEqualTo(4);
|
||||
assertThat(hlsDownloader.getDownloadedSegments()).isEqualTo(4);
|
||||
assertThat(hlsDownloader.getDownloadedBytes())
|
||||
.isEqualTo(MEDIA_PLAYLIST_DATA.length + 10 + 11 + 12);
|
||||
}
|
||||
@ -126,8 +124,6 @@ public class HlsDownloaderTest {
|
||||
newHlsDownloader.selectRepresentations(getKeys(MEDIA_PLAYLIST_1_URI));
|
||||
newHlsDownloader.init();
|
||||
|
||||
assertThat(newHlsDownloader.getTotalSegments()).isEqualTo(4);
|
||||
assertThat(newHlsDownloader.getDownloadedSegments()).isEqualTo(4);
|
||||
assertThat(newHlsDownloader.getDownloadedBytes())
|
||||
.isEqualTo(MEDIA_PLAYLIST_DATA.length + 10 + 11 + 12);
|
||||
}
|
||||
|
@ -16,6 +16,7 @@
|
||||
package com.google.android.exoplayer2.source.smoothstreaming.offline;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.util.Pair;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.offline.DownloaderConstructorHelper;
|
||||
import com.google.android.exoplayer2.offline.SegmentDownloader;
|
||||
@ -34,9 +35,6 @@ import java.util.List;
|
||||
/**
|
||||
* Helper class to download SmoothStreaming streams.
|
||||
*
|
||||
* <p>Except {@link #getTotalSegments()}, {@link #getDownloadedSegments()} and {@link
|
||||
* #getDownloadedBytes()}, this class isn't thread safe.
|
||||
*
|
||||
* <p>Example usage:
|
||||
*
|
||||
* <pre>{@code
|
||||
@ -84,9 +82,8 @@ public final class SsDownloader extends SegmentDownloader<SsManifest, TrackKey>
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<Segment> getSegments(
|
||||
DataSource dataSource, SsManifest manifest, boolean allowIndexLoadErrors)
|
||||
throws InterruptedException, IOException {
|
||||
protected Pair<List<Segment>, Boolean> getSegments(
|
||||
DataSource dataSource, SsManifest manifest, boolean allowIndexLoadErrors) throws IOException {
|
||||
ArrayList<Segment> segments = new ArrayList<>();
|
||||
for (StreamElement streamElement : manifest.streamElements) {
|
||||
for (int i = 0; i < streamElement.formats.length; i++) {
|
||||
@ -98,7 +95,7 @@ public final class SsDownloader extends SegmentDownloader<SsManifest, TrackKey>
|
||||
}
|
||||
}
|
||||
}
|
||||
return segments;
|
||||
return Pair.<List<Segment>, Boolean>create(segments, true);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ import android.app.Notification;
|
||||
import android.content.Context;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.offline.DownloadManager;
|
||||
import com.google.android.exoplayer2.offline.DownloadManager.DownloadState;
|
||||
import com.google.android.exoplayer2.util.ErrorMessageProvider;
|
||||
@ -48,31 +49,31 @@ public final class DownloadNotificationUtil {
|
||||
String channelId,
|
||||
@Nullable String message) {
|
||||
float totalPercentage = 0;
|
||||
int determinatePercentageCount = 0;
|
||||
boolean isAnyDownloadActive = false;
|
||||
int downloadTaskCount = 0;
|
||||
boolean allDownloadPercentagesUnknown = true;
|
||||
boolean haveDownloadedBytes = false;
|
||||
for (DownloadState downloadState : downloadStates) {
|
||||
if (downloadState.downloadAction.isRemoveAction
|
||||
|| downloadState.state != DownloadState.STATE_STARTED) {
|
||||
continue;
|
||||
}
|
||||
float percentage = downloadState.downloadPercentage;
|
||||
if (!Float.isNaN(percentage)) {
|
||||
totalPercentage += percentage;
|
||||
determinatePercentageCount++;
|
||||
if (downloadState.downloadPercentage != C.PERCENTAGE_UNSET) {
|
||||
allDownloadPercentagesUnknown = false;
|
||||
totalPercentage += downloadState.downloadPercentage;
|
||||
}
|
||||
isAnyDownloadActive = true;
|
||||
haveDownloadedBytes |= downloadState.downloadedBytes > 0;
|
||||
downloadTaskCount++;
|
||||
}
|
||||
|
||||
int titleStringId = isAnyDownloadActive ? R.string.exo_download_downloading : NULL_STRING_ID;
|
||||
boolean haveDownloadTasks = downloadTaskCount > 0;
|
||||
int titleStringId = haveDownloadTasks ? R.string.exo_download_downloading : NULL_STRING_ID;
|
||||
NotificationCompat.Builder notificationBuilder =
|
||||
createNotificationBuilder(context, smallIcon, channelId, message, titleStringId);
|
||||
|
||||
int progress = haveDownloadTasks ? (int) (totalPercentage / downloadTaskCount) : 0;
|
||||
boolean indeterminate = allDownloadPercentagesUnknown && haveDownloadedBytes;
|
||||
notificationBuilder.setProgress(/* max= */ 100, progress, indeterminate);
|
||||
notificationBuilder.setOngoing(true);
|
||||
int max = 100;
|
||||
int progress = (int) (totalPercentage / determinatePercentageCount);
|
||||
boolean indeterminate = determinatePercentageCount == 0;
|
||||
notificationBuilder.setProgress(max, progress, indeterminate);
|
||||
|
||||
notificationBuilder.setShowWhen(false);
|
||||
return notificationBuilder.build();
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user