mirror of
https://github.com/samsonjs/media.git
synced 2026-04-15 12:55:46 +00:00
commit
6673483aca
16 changed files with 90 additions and 117 deletions
|
|
@ -1,22 +1,27 @@
|
|||
# Release notes #
|
||||
|
||||
### r2.0.4 ###
|
||||
|
||||
This release contains important bug fixes. Users of earlier r2.0.x versions
|
||||
should proactively update to this version.
|
||||
|
||||
* Fix crash on Jellybean devices when using playback controls
|
||||
([#1965](https://github.com/google/ExoPlayer/issues/1965)).
|
||||
|
||||
### r2.0.3 ###
|
||||
|
||||
This release contains important bug fixes. Users of r2.0.0, r2.0.1 and r2.0.2
|
||||
should proactively update to this version.
|
||||
|
||||
* Fixed NullPointerException in ExtractorMediaSource
|
||||
([#1914](https://github.com/google/ExoPlayer/issues/1914).
|
||||
([#1914](https://github.com/google/ExoPlayer/issues/1914)).
|
||||
* Fixed NullPointerException in HlsMediaPeriod
|
||||
([#1907](https://github.com/google/ExoPlayer/issues/1907).
|
||||
([#1907](https://github.com/google/ExoPlayer/issues/1907)).
|
||||
* Fixed memory leak in PlaybackControlView
|
||||
([#1908](https://github.com/google/ExoPlayer/issues/1908).
|
||||
([#1908](https://github.com/google/ExoPlayer/issues/1908)).
|
||||
* Fixed strict mode violation when using
|
||||
SimpleExoPlayer.setVideoPlayerTextureView().
|
||||
* Fixed L3 Widevine provisioning
|
||||
([#1925](https://github.com/google/ExoPlayer/issues/1925).
|
||||
([#1925](https://github.com/google/ExoPlayer/issues/1925)).
|
||||
* Fixed hiding of controls with use_controller="false"
|
||||
([#1919](https://github.com/google/ExoPlayer/issues/1919).
|
||||
([#1919](https://github.com/google/ExoPlayer/issues/1919)).
|
||||
* Improvements to Cronet network stack extension.
|
||||
* Misc bug fixes.
|
||||
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ allprojects {
|
|||
releaseRepoName = 'exoplayer'
|
||||
releaseUserOrg = 'google'
|
||||
releaseGroupId = 'com.google.android.exoplayer'
|
||||
releaseVersion = 'r2.0.3'
|
||||
releaseVersion = 'r2.0.4'
|
||||
releaseWebsite = 'https://github.com/google/ExoPlayer'
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,8 +16,8 @@
|
|||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.google.android.exoplayer2.demo"
|
||||
android:versionCode="2003"
|
||||
android:versionName="2.0.3">
|
||||
android:versionCode="2004"
|
||||
android:versionName="2.0.4">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ import static org.junit.Assert.assertFalse;
|
|||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.anyInt;
|
||||
import static org.mockito.Matchers.anyString;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
|
|
@ -52,7 +51,6 @@ import java.net.UnknownHostException;
|
|||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
|
@ -88,20 +86,7 @@ public final class CronetDataSourceTest {
|
|||
private Map<String, String> testResponseHeader;
|
||||
private UrlResponseInfo testUrlResponseInfo;
|
||||
|
||||
/**
|
||||
* MockableCronetEngine is an abstract class for helping creating new Requests.
|
||||
*/
|
||||
public abstract static class MockableCronetEngine extends CronetEngine {
|
||||
|
||||
@Override
|
||||
public abstract UrlRequest createRequest(String url, UrlRequest.Callback callback,
|
||||
Executor executor, int priority,
|
||||
Collection<Object> connectionAnnotations,
|
||||
boolean disableCache,
|
||||
boolean disableConnectionMigration,
|
||||
boolean allowDirectExecutor);
|
||||
}
|
||||
|
||||
@Mock private UrlRequest.Builder mockUrlRequestBuilder;
|
||||
@Mock
|
||||
private UrlRequest mockUrlRequest;
|
||||
@Mock
|
||||
|
|
@ -114,8 +99,7 @@ public final class CronetDataSourceTest {
|
|||
private Executor mockExecutor;
|
||||
@Mock
|
||||
private UrlRequestException mockUrlRequestException;
|
||||
@Mock
|
||||
private MockableCronetEngine mockCronetEngine;
|
||||
@Mock private CronetEngine mockCronetEngine;
|
||||
|
||||
private CronetDataSource dataSourceUnderTest;
|
||||
|
||||
|
|
@ -135,15 +119,10 @@ public final class CronetDataSourceTest {
|
|||
true, // resetTimeoutOnRedirects
|
||||
mockClock));
|
||||
when(mockContentTypePredicate.evaluate(anyString())).thenReturn(true);
|
||||
when(mockCronetEngine.createRequest(
|
||||
anyString(),
|
||||
any(UrlRequest.Callback.class),
|
||||
any(Executor.class),
|
||||
anyInt(),
|
||||
eq(Collections.emptyList()),
|
||||
any(Boolean.class),
|
||||
any(Boolean.class),
|
||||
any(Boolean.class))).thenReturn(mockUrlRequest);
|
||||
when(mockCronetEngine.newUrlRequestBuilder(
|
||||
anyString(), any(UrlRequest.Callback.class), any(Executor.class)))
|
||||
.thenReturn(mockUrlRequestBuilder);
|
||||
when(mockUrlRequestBuilder.build()).thenReturn(mockUrlRequest);
|
||||
mockStatusResponse();
|
||||
|
||||
testDataSpec = new DataSpec(Uri.parse(TEST_URL), 0, C.LENGTH_UNSET, null);
|
||||
|
|
@ -184,15 +163,7 @@ public final class CronetDataSourceTest {
|
|||
dataSourceUnderTest.close();
|
||||
// Prepare a mock UrlRequest to be used in the second open() call.
|
||||
final UrlRequest mockUrlRequest2 = mock(UrlRequest.class);
|
||||
when(mockCronetEngine.createRequest(
|
||||
anyString(),
|
||||
any(UrlRequest.Callback.class),
|
||||
any(Executor.class),
|
||||
anyInt(),
|
||||
eq(Collections.emptyList()),
|
||||
any(Boolean.class),
|
||||
any(Boolean.class),
|
||||
any(Boolean.class))).thenReturn(mockUrlRequest2);
|
||||
when(mockUrlRequestBuilder.build()).thenReturn(mockUrlRequest2);
|
||||
doAnswer(new Answer<Object>() {
|
||||
@Override
|
||||
public Object answer(InvocationOnMock invocation) throws Throwable {
|
||||
|
|
@ -215,15 +186,8 @@ public final class CronetDataSourceTest {
|
|||
mockResponseStartSuccess();
|
||||
|
||||
dataSourceUnderTest.open(testDataSpec);
|
||||
verify(mockCronetEngine).createRequest(
|
||||
eq(TEST_URL),
|
||||
any(UrlRequest.Callback.class),
|
||||
any(Executor.class),
|
||||
anyInt(),
|
||||
eq(Collections.emptyList()),
|
||||
any(Boolean.class),
|
||||
any(Boolean.class),
|
||||
any(Boolean.class));
|
||||
verify(mockCronetEngine)
|
||||
.newUrlRequestBuilder(eq(TEST_URL), any(UrlRequest.Callback.class), any(Executor.class));
|
||||
verify(mockUrlRequest).start();
|
||||
}
|
||||
|
||||
|
|
@ -237,9 +201,9 @@ public final class CronetDataSourceTest {
|
|||
|
||||
dataSourceUnderTest.open(testDataSpec);
|
||||
// The header value to add is current position to current position + length - 1.
|
||||
verify(mockUrlRequest).addHeader("Range", "bytes=1000-5999");
|
||||
verify(mockUrlRequest).addHeader("firstHeader", "firstValue");
|
||||
verify(mockUrlRequest).addHeader("secondHeader", "secondValue");
|
||||
verify(mockUrlRequestBuilder).addHeader("Range", "bytes=1000-5999");
|
||||
verify(mockUrlRequestBuilder).addHeader("firstHeader", "firstValue");
|
||||
verify(mockUrlRequestBuilder).addHeader("secondHeader", "secondValue");
|
||||
verify(mockUrlRequest).start();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -412,8 +412,8 @@ public class CronetDataSource extends UrlRequest.Callback implements HttpDataSou
|
|||
// Internal methods.
|
||||
|
||||
private UrlRequest buildRequest(DataSpec dataSpec) throws OpenException {
|
||||
UrlRequest.Builder requestBuilder = new UrlRequest.Builder(dataSpec.uri.toString(), this,
|
||||
executor, cronetEngine);
|
||||
UrlRequest.Builder requestBuilder = cronetEngine.newUrlRequestBuilder(dataSpec.uri.toString(),
|
||||
this, executor);
|
||||
// Set the headers.
|
||||
synchronized (requestProperties) {
|
||||
if (dataSpec.postBody != null && dataSpec.postBody.length != 0
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ public class OkHttpDataSource implements HttpDataSource {
|
|||
private long bytesRead;
|
||||
|
||||
/**
|
||||
* @param callFactory An {@link Call.Factory} for use by the source.
|
||||
* @param callFactory A {@link Call.Factory} for use by the source.
|
||||
* @param userAgent The User-Agent string that should be used.
|
||||
* @param contentTypePredicate An optional {@link Predicate}. If a content type is rejected by the
|
||||
* predicate then a InvalidContentTypeException} is thrown from {@link #open(DataSpec)}.
|
||||
|
|
@ -76,7 +76,7 @@ public class OkHttpDataSource implements HttpDataSource {
|
|||
}
|
||||
|
||||
/**
|
||||
* @param callFactory An {@link Call.Factory} for use by the source.
|
||||
* @param callFactory A {@link Call.Factory} for use by the source.
|
||||
* @param userAgent The User-Agent string that should be used.
|
||||
* @param contentTypePredicate An optional {@link Predicate}. If a content type is rejected by the
|
||||
* predicate then a {@link InvalidContentTypeException} is thrown from
|
||||
|
|
|
|||
|
|
@ -177,8 +177,7 @@ public class CacheDataSourceTest extends InstrumentationTestCase {
|
|||
builder.setSimulateUnknownLength(simulateUnknownLength);
|
||||
builder.appendReadData(TEST_DATA);
|
||||
FakeDataSource upstream = builder.build();
|
||||
return new CacheDataSource(simpleCache, upstream,
|
||||
CacheDataSource.FLAG_BLOCK_ON_CACHE | CacheDataSource.FLAG_CACHE_UNBOUNDED_REQUESTS,
|
||||
return new CacheDataSource(simpleCache, upstream, CacheDataSource.FLAG_BLOCK_ON_CACHE,
|
||||
MAX_CACHE_FILE_SIZE);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ public interface ExoPlayerLibraryInfo {
|
|||
/**
|
||||
* The version of the library, expressed as a string.
|
||||
*/
|
||||
String VERSION = "2.0.3";
|
||||
String VERSION = "2.0.4";
|
||||
|
||||
/**
|
||||
* The version of the library, expressed as an integer.
|
||||
|
|
@ -32,7 +32,7 @@ public interface ExoPlayerLibraryInfo {
|
|||
* corresponding integer version 1002003 (001-002-003), and "123.45.6" has the corresponding
|
||||
* integer version 123045006 (123-045-006).
|
||||
*/
|
||||
int VERSION_INT = 2000003;
|
||||
int VERSION_INT = 2000004;
|
||||
|
||||
/**
|
||||
* Whether the library was compiled with {@link com.google.android.exoplayer2.util.Assertions}
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ import java.util.Collections;
|
|||
private static final int SUFFIX_SEI_NUT = 40;
|
||||
|
||||
private TrackOutput output;
|
||||
private SampleReader sampleReader;
|
||||
private SeiReader seiReader;
|
||||
|
||||
// State that should not be reset on seek.
|
||||
|
|
@ -56,7 +57,6 @@ import java.util.Collections;
|
|||
private final NalUnitTargetBuffer pps;
|
||||
private final NalUnitTargetBuffer prefixSei;
|
||||
private final NalUnitTargetBuffer suffixSei; // TODO: Are both needed?
|
||||
private final SampleReader sampleReader;
|
||||
private long totalBytesWritten;
|
||||
|
||||
// Per packet state that gets reset at the start of each packet.
|
||||
|
|
@ -72,7 +72,6 @@ import java.util.Collections;
|
|||
pps = new NalUnitTargetBuffer(PPS_NUT, 128);
|
||||
prefixSei = new NalUnitTargetBuffer(PREFIX_SEI_NUT, 128);
|
||||
suffixSei = new NalUnitTargetBuffer(SUFFIX_SEI_NUT, 128);
|
||||
sampleReader = new SampleReader(output);
|
||||
seiWrapper = new ParsableByteArray();
|
||||
}
|
||||
|
||||
|
|
@ -91,6 +90,7 @@ import java.util.Collections;
|
|||
@Override
|
||||
public void init(ExtractorOutput extractorOutput, TrackIdGenerator idGenerator) {
|
||||
output = extractorOutput.track(idGenerator.getNextId());
|
||||
sampleReader = new SampleReader(output);
|
||||
seiReader = new SeiReader(extractorOutput.track(idGenerator.getNextId()));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -39,7 +39,6 @@ import com.google.android.exoplayer2.upstream.Loader;
|
|||
import com.google.android.exoplayer2.upstream.Loader.Loadable;
|
||||
import com.google.android.exoplayer2.util.Assertions;
|
||||
import com.google.android.exoplayer2.util.ConditionVariable;
|
||||
import com.google.android.exoplayer2.util.Util;
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
|
||||
|
|
@ -593,8 +592,7 @@ import java.io.IOException;
|
|||
ExtractorInput input = null;
|
||||
try {
|
||||
long position = positionHolder.position;
|
||||
length = dataSource.open(
|
||||
new DataSpec(uri, position, C.LENGTH_UNSET, Util.sha1(uri.toString())));
|
||||
length = dataSource.open(new DataSpec(uri, position, C.LENGTH_UNSET, null));
|
||||
if (length != C.LENGTH_UNSET) {
|
||||
length += position;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -124,7 +124,7 @@ public final class DashMediaSource implements MediaSource {
|
|||
this.minLoadableRetryCount = minLoadableRetryCount;
|
||||
this.livePresentationDelayMs = livePresentationDelayMs;
|
||||
eventDispatcher = new EventDispatcher(eventHandler, eventListener);
|
||||
manifestParser = new DashManifestParser(generateContentId());
|
||||
manifestParser = new DashManifestParser();
|
||||
manifestCallback = new ManifestCallback();
|
||||
manifestUriLock = new Object();
|
||||
periodsById = new SparseArray<>();
|
||||
|
|
@ -468,10 +468,6 @@ public final class DashMediaSource implements MediaSource {
|
|||
}
|
||||
}
|
||||
|
||||
private String generateContentId() {
|
||||
return Util.sha1(manifestUri.toString());
|
||||
}
|
||||
|
||||
private static final class PeriodSeekInfo {
|
||||
|
||||
public static PeriodSeekInfo createPeriodSeekInfo(
|
||||
|
|
|
|||
|
|
@ -57,7 +57,6 @@ public abstract class Representation {
|
|||
*/
|
||||
public final long presentationTimeOffsetUs;
|
||||
|
||||
private final String cacheKey;
|
||||
private final RangedUri initializationUri;
|
||||
|
||||
/**
|
||||
|
|
@ -81,7 +80,8 @@ public abstract class Representation {
|
|||
* @param revisionId Identifies the revision of the content.
|
||||
* @param format The format of the representation.
|
||||
* @param segmentBase A segment base element for the representation.
|
||||
* @param customCacheKey A custom value to be returned from {@link #getCacheKey()}, or null.
|
||||
* @param customCacheKey A custom value to be returned from {@link #getCacheKey()}, or null. This
|
||||
* parameter is ignored if {@code segmentBase} consists of multiple segments.
|
||||
* @return The constructed instance.
|
||||
*/
|
||||
public static Representation newInstance(String contentId, long revisionId, Format format,
|
||||
|
|
@ -91,7 +91,7 @@ public abstract class Representation {
|
|||
(SingleSegmentBase) segmentBase, customCacheKey, C.LENGTH_UNSET);
|
||||
} else if (segmentBase instanceof MultiSegmentBase) {
|
||||
return new MultiSegmentRepresentation(contentId, revisionId, format,
|
||||
(MultiSegmentBase) segmentBase, customCacheKey);
|
||||
(MultiSegmentBase) segmentBase);
|
||||
} else {
|
||||
throw new IllegalArgumentException("segmentBase must be of type SingleSegmentBase or "
|
||||
+ "MultiSegmentBase");
|
||||
|
|
@ -99,12 +99,10 @@ public abstract class Representation {
|
|||
}
|
||||
|
||||
private Representation(String contentId, long revisionId, Format format,
|
||||
SegmentBase segmentBase, String customCacheKey) {
|
||||
SegmentBase segmentBase) {
|
||||
this.contentId = contentId;
|
||||
this.revisionId = revisionId;
|
||||
this.format = format;
|
||||
this.cacheKey = customCacheKey != null ? customCacheKey
|
||||
: contentId + "." + format.id + "." + revisionId;
|
||||
initializationUri = segmentBase.getInitialization(this);
|
||||
presentationTimeOffsetUs = segmentBase.getPresentationTimeOffsetUs();
|
||||
}
|
||||
|
|
@ -129,12 +127,10 @@ public abstract class Representation {
|
|||
public abstract DashSegmentIndex getIndex();
|
||||
|
||||
/**
|
||||
* Returns a cache key for the representation, in the format
|
||||
* {@code contentId + "." + format.id + "." + revisionId}.
|
||||
* Returns a cache key for the representation if a custom cache key or content id has been
|
||||
* provided and there is only single segment.
|
||||
*/
|
||||
public String getCacheKey() {
|
||||
return cacheKey;
|
||||
}
|
||||
public abstract String getCacheKey();
|
||||
|
||||
/**
|
||||
* A DASH representation consisting of a single segment.
|
||||
|
|
@ -151,6 +147,7 @@ public abstract class Representation {
|
|||
*/
|
||||
public final long contentLength;
|
||||
|
||||
private final String cacheKey;
|
||||
private final RangedUri indexUri;
|
||||
private final SingleSegmentIndex segmentIndex;
|
||||
|
||||
|
|
@ -187,9 +184,11 @@ public abstract class Representation {
|
|||
*/
|
||||
public SingleSegmentRepresentation(String contentId, long revisionId, Format format,
|
||||
SingleSegmentBase segmentBase, String customCacheKey, long contentLength) {
|
||||
super(contentId, revisionId, format, segmentBase, customCacheKey);
|
||||
super(contentId, revisionId, format, segmentBase);
|
||||
this.uri = Uri.parse(segmentBase.uri);
|
||||
this.indexUri = segmentBase.getIndex();
|
||||
this.cacheKey = customCacheKey != null ? customCacheKey
|
||||
: contentId != null ? contentId + "." + format.id + "." + revisionId : null;
|
||||
this.contentLength = contentLength;
|
||||
// If we have an index uri then the index is defined externally, and we shouldn't return one
|
||||
// directly. If we don't, then we can't do better than an index defining a single segment.
|
||||
|
|
@ -207,6 +206,11 @@ public abstract class Representation {
|
|||
return segmentIndex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCacheKey() {
|
||||
return cacheKey;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -222,11 +226,10 @@ public abstract class Representation {
|
|||
* @param revisionId Identifies the revision of the content.
|
||||
* @param format The format of the representation.
|
||||
* @param segmentBase The segment base underlying the representation.
|
||||
* @param customCacheKey A custom value to be returned from {@link #getCacheKey()}, or null.
|
||||
*/
|
||||
public MultiSegmentRepresentation(String contentId, long revisionId, Format format,
|
||||
MultiSegmentBase segmentBase, String customCacheKey) {
|
||||
super(contentId, revisionId, format, segmentBase, customCacheKey);
|
||||
MultiSegmentBase segmentBase) {
|
||||
super(contentId, revisionId, format, segmentBase);
|
||||
this.segmentBase = segmentBase;
|
||||
}
|
||||
|
||||
|
|
@ -240,6 +243,11 @@ public abstract class Representation {
|
|||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCacheKey() {
|
||||
return null;
|
||||
}
|
||||
|
||||
// DashSegmentIndex implementation.
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -440,6 +440,10 @@ import java.util.Locale;
|
|||
}
|
||||
HlsMediaPlaylist oldMediaPlaylist = variantPlaylists[oldVariantIndex];
|
||||
HlsMediaPlaylist newMediaPlaylist = variantPlaylists[newVariantIndex];
|
||||
if (previousChunkIndex < oldMediaPlaylist.mediaSequence) {
|
||||
// We have fallen behind the live window.
|
||||
return newMediaPlaylist.mediaSequence - 1;
|
||||
}
|
||||
double offsetToLiveInstantSecs = 0;
|
||||
for (int i = previousChunkIndex - oldMediaPlaylist.mediaSequence;
|
||||
i < oldMediaPlaylist.segments.size(); i++) {
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
package com.google.android.exoplayer2.ui;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.os.SystemClock;
|
||||
|
|
@ -75,6 +76,7 @@ public class PlaybackControlView extends FrameLayout {
|
|||
private ExoPlayer player;
|
||||
private VisibilityListener visibilityListener;
|
||||
|
||||
private boolean isAttachedToWindow;
|
||||
private boolean dragging;
|
||||
private int rewindMs;
|
||||
private int fastForwardMs;
|
||||
|
|
@ -264,7 +266,7 @@ public class PlaybackControlView extends FrameLayout {
|
|||
removeCallbacks(hideAction);
|
||||
if (showTimeoutMs > 0) {
|
||||
hideAtMs = SystemClock.uptimeMillis() + showTimeoutMs;
|
||||
if (isAttachedToWindow()) {
|
||||
if (isAttachedToWindow) {
|
||||
postDelayed(hideAction, showTimeoutMs);
|
||||
}
|
||||
} else {
|
||||
|
|
@ -279,7 +281,7 @@ public class PlaybackControlView extends FrameLayout {
|
|||
}
|
||||
|
||||
private void updatePlayPauseButton() {
|
||||
if (!isVisible() || !isAttachedToWindow()) {
|
||||
if (!isVisible() || !isAttachedToWindow) {
|
||||
return;
|
||||
}
|
||||
boolean playing = player != null && player.getPlayWhenReady();
|
||||
|
|
@ -291,7 +293,7 @@ public class PlaybackControlView extends FrameLayout {
|
|||
}
|
||||
|
||||
private void updateNavigation() {
|
||||
if (!isVisible() || !isAttachedToWindow()) {
|
||||
if (!isVisible() || !isAttachedToWindow) {
|
||||
return;
|
||||
}
|
||||
Timeline currentTimeline = player != null ? player.getCurrentTimeline() : null;
|
||||
|
|
@ -315,7 +317,7 @@ public class PlaybackControlView extends FrameLayout {
|
|||
}
|
||||
|
||||
private void updateProgress() {
|
||||
if (!isVisible() || !isAttachedToWindow()) {
|
||||
if (!isVisible() || !isAttachedToWindow) {
|
||||
return;
|
||||
}
|
||||
long duration = player == null ? 0 : player.getDuration();
|
||||
|
|
@ -350,13 +352,18 @@ public class PlaybackControlView extends FrameLayout {
|
|||
private void setButtonEnabled(boolean enabled, View view) {
|
||||
view.setEnabled(enabled);
|
||||
if (Util.SDK_INT >= 11) {
|
||||
view.setAlpha(enabled ? 1f : 0.3f);
|
||||
setViewAlphaV11(view, enabled ? 1f : 0.3f);
|
||||
view.setVisibility(VISIBLE);
|
||||
} else {
|
||||
view.setVisibility(enabled ? VISIBLE : INVISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
@TargetApi(11)
|
||||
private void setViewAlphaV11(View view, float alpha) {
|
||||
view.setAlpha(alpha);
|
||||
}
|
||||
|
||||
private String stringForTime(long timeMs) {
|
||||
if (timeMs == C.TIME_UNSET) {
|
||||
timeMs = 0;
|
||||
|
|
@ -426,6 +433,7 @@ public class PlaybackControlView extends FrameLayout {
|
|||
@Override
|
||||
public void onAttachedToWindow() {
|
||||
super.onAttachedToWindow();
|
||||
isAttachedToWindow = true;
|
||||
if (hideAtMs != C.TIME_UNSET) {
|
||||
long delayMs = hideAtMs - SystemClock.uptimeMillis();
|
||||
if (delayMs <= 0) {
|
||||
|
|
@ -440,6 +448,7 @@ public class PlaybackControlView extends FrameLayout {
|
|||
@Override
|
||||
public void onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow();
|
||||
isAttachedToWindow = false;
|
||||
removeCallbacks(updateProgressAction);
|
||||
removeCallbacks(hideAction);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import com.google.android.exoplayer2.upstream.DataSpec;
|
|||
import com.google.android.exoplayer2.upstream.FileDataSource;
|
||||
import com.google.android.exoplayer2.upstream.TeeDataSource;
|
||||
import com.google.android.exoplayer2.upstream.cache.CacheDataSink.CacheDataSinkException;
|
||||
import com.google.android.exoplayer2.util.Util;
|
||||
import java.io.IOException;
|
||||
import java.io.InterruptedIOException;
|
||||
import java.lang.annotation.Retention;
|
||||
|
|
@ -50,8 +51,7 @@ public final class CacheDataSource implements DataSource {
|
|||
* Flags controlling the cache's behavior.
|
||||
*/
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@IntDef(flag = true, value = {FLAG_BLOCK_ON_CACHE, FLAG_IGNORE_CACHE_ON_ERROR,
|
||||
FLAG_CACHE_UNBOUNDED_REQUESTS})
|
||||
@IntDef(flag = true, value = {FLAG_BLOCK_ON_CACHE, FLAG_IGNORE_CACHE_ON_ERROR})
|
||||
public @interface Flags {}
|
||||
/**
|
||||
* A flag indicating whether we will block reads if the cache key is locked. If this flag is
|
||||
|
|
@ -66,13 +66,6 @@ public final class CacheDataSource implements DataSource {
|
|||
*/
|
||||
public static final int FLAG_IGNORE_CACHE_ON_ERROR = 1 << 1;
|
||||
|
||||
/**
|
||||
* A flag indicating whether the response is cached if the range of the request is unbounded.
|
||||
* Disabled by default because, as a side effect, this may allow streams with every chunk from a
|
||||
* separate URL cached which is broken currently.
|
||||
*/
|
||||
public static final int FLAG_CACHE_UNBOUNDED_REQUESTS = 1 << 2;
|
||||
|
||||
/**
|
||||
* Listener of {@link CacheDataSource} events.
|
||||
*/
|
||||
|
|
@ -98,7 +91,6 @@ public final class CacheDataSource implements DataSource {
|
|||
|
||||
private final boolean blockOnCache;
|
||||
private final boolean ignoreCacheOnError;
|
||||
private final boolean bypassUnboundedRequests;
|
||||
|
||||
private DataSource currentDataSource;
|
||||
private boolean currentRequestUnbounded;
|
||||
|
|
@ -127,8 +119,8 @@ public final class CacheDataSource implements DataSource {
|
|||
*
|
||||
* @param cache The cache.
|
||||
* @param upstream A {@link DataSource} for reading data not in the cache.
|
||||
* @param flags A combination of {@link #FLAG_BLOCK_ON_CACHE}, {@link #FLAG_IGNORE_CACHE_ON_ERROR}
|
||||
* and {@link #FLAG_CACHE_UNBOUNDED_REQUESTS} or 0.
|
||||
* @param flags A combination of {@link #FLAG_BLOCK_ON_CACHE} and {@link
|
||||
* #FLAG_IGNORE_CACHE_ON_ERROR} or 0.
|
||||
* @param maxCacheFileSize The maximum size of a cache file, in bytes. If the cached data size
|
||||
* exceeds this value, then the data will be fragmented into multiple cache files. The
|
||||
* finer-grained this is the finer-grained the eviction policy can be.
|
||||
|
|
@ -148,8 +140,8 @@ public final class CacheDataSource implements DataSource {
|
|||
* @param upstream A {@link DataSource} for reading data not in the cache.
|
||||
* @param cacheReadDataSource A {@link DataSource} for reading data from the cache.
|
||||
* @param cacheWriteDataSink A {@link DataSink} for writing data to the cache.
|
||||
* @param flags A combination of {@link #FLAG_BLOCK_ON_CACHE}, {@link #FLAG_IGNORE_CACHE_ON_ERROR}
|
||||
* and {@link #FLAG_CACHE_UNBOUNDED_REQUESTS} or 0.
|
||||
* @param flags A combination of {@link #FLAG_BLOCK_ON_CACHE} and {@link
|
||||
* #FLAG_IGNORE_CACHE_ON_ERROR} or 0.
|
||||
* @param eventListener An optional {@link EventListener} to receive events.
|
||||
*/
|
||||
public CacheDataSource(Cache cache, DataSource upstream, DataSource cacheReadDataSource,
|
||||
|
|
@ -158,7 +150,6 @@ public final class CacheDataSource implements DataSource {
|
|||
this.cacheReadDataSource = cacheReadDataSource;
|
||||
this.blockOnCache = (flags & FLAG_BLOCK_ON_CACHE) != 0;
|
||||
this.ignoreCacheOnError = (flags & FLAG_IGNORE_CACHE_ON_ERROR) != 0;
|
||||
this.bypassUnboundedRequests = (flags & FLAG_CACHE_UNBOUNDED_REQUESTS) == 0;
|
||||
this.upstreamDataSource = upstream;
|
||||
if (cacheWriteDataSink != null) {
|
||||
this.cacheWriteDataSource = new TeeDataSource(upstream, cacheWriteDataSink);
|
||||
|
|
@ -173,10 +164,9 @@ public final class CacheDataSource implements DataSource {
|
|||
try {
|
||||
uri = dataSpec.uri;
|
||||
flags = dataSpec.flags;
|
||||
key = dataSpec.key;
|
||||
key = dataSpec.key != null ? dataSpec.key : Util.sha1(uri.toString());
|
||||
readPosition = dataSpec.position;
|
||||
currentRequestIgnoresCache = (ignoreCacheOnError && seenCacheError)
|
||||
|| (bypassUnboundedRequests && dataSpec.length == C.LENGTH_UNSET);
|
||||
currentRequestIgnoresCache = ignoreCacheOnError && seenCacheError;
|
||||
if (dataSpec.length != C.LENGTH_UNSET || currentRequestIgnoresCache) {
|
||||
bytesRemaining = dataSpec.length;
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -17,8 +17,8 @@
|
|||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="com.google.android.exoplayer2.playbacktests"
|
||||
android:versionCode="2003"
|
||||
android:versionName="2.0.3">
|
||||
android:versionCode="2004"
|
||||
android:versionName="2.0.4">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK"/>
|
||||
|
|
|
|||
Loading…
Reference in a new issue