diff --git a/app/src/main/java/com/example/gsyvideoplayer/exosource/GSYDefaultHttpDataSource.java b/app/src/main/java/com/example/gsyvideoplayer/exosource/GSYDefaultHttpDataSource.java index 07ac8198a9b662123343a193a9a985832706e2b0..01700c852cde35db92f570ca4fcc93210607bfb1 100644 --- a/app/src/main/java/com/example/gsyvideoplayer/exosource/GSYDefaultHttpDataSource.java +++ b/app/src/main/java/com/example/gsyvideoplayer/exosource/GSYDefaultHttpDataSource.java @@ -22,6 +22,7 @@ import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import com.google.android.exoplayer2.C; +import com.google.android.exoplayer2.ExoPlayerLibraryInfo; import com.google.android.exoplayer2.upstream.BaseDataSource; import com.google.android.exoplayer2.upstream.DataSourceException; import com.google.android.exoplayer2.upstream.DataSpec; @@ -29,8 +30,8 @@ import com.google.android.exoplayer2.upstream.DataSpec.HttpMethod; import com.google.android.exoplayer2.upstream.HttpDataSource; import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Log; -import com.google.android.exoplayer2.util.Predicate; import com.google.android.exoplayer2.util.Util; +import com.google.common.base.Predicate; import java.io.EOFException; import java.io.IOException; @@ -62,6 +63,9 @@ import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; +import static java.lang.Math.max; +import static java.lang.Math.min; + /** * An {@link HttpDataSource} that uses Android's {@link HttpURLConnection}. * @@ -76,6 +80,7 @@ import javax.net.ssl.X509TrustManager; */ public class GSYDefaultHttpDataSource extends BaseDataSource implements HttpDataSource { + /** * The default connection timeout, in milliseconds. */ @@ -120,6 +125,18 @@ public class GSYDefaultHttpDataSource extends BaseDataSource implements HttpData private long bytesRead; /** + * Creates an instance. + */ + public GSYDefaultHttpDataSource() { + this( + ExoPlayerLibraryInfo.DEFAULT_USER_AGENT, + DEFAULT_CONNECT_TIMEOUT_MILLIS, + DEFAULT_READ_TIMEOUT_MILLIS); + } + + /** + * Creates an instance. + * * @param userAgent The User-Agent string that should be used. */ public GSYDefaultHttpDataSource(String userAgent) { @@ -127,6 +144,8 @@ public class GSYDefaultHttpDataSource extends BaseDataSource implements HttpData } /** + * Creates an instance. + * * @param userAgent The User-Agent string that should be used. * @param connectTimeoutMillis The connection timeout, in milliseconds. A timeout of zero is * interpreted as an infinite timeout. @@ -143,6 +162,8 @@ public class GSYDefaultHttpDataSource extends BaseDataSource implements HttpData } /** + * Creates an instance. + * * @param userAgent The User-Agent string that should be used. * @param connectTimeoutMillis The connection timeout, in milliseconds. A timeout of zero is * interpreted as an infinite timeout. Pass {@link #DEFAULT_CONNECT_TIMEOUT_MILLIS} to use the @@ -170,6 +191,8 @@ public class GSYDefaultHttpDataSource extends BaseDataSource implements HttpData } /** + * Creates an instance. + * * @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 HttpDataSource.InvalidContentTypeException} is thrown from {@link @@ -177,6 +200,7 @@ public class GSYDefaultHttpDataSource extends BaseDataSource implements HttpData * @deprecated Use {@link #GSYDefaultHttpDataSource(String)} and {@link * #setContentTypePredicate(Predicate)}. */ + @SuppressWarnings("deprecation") @Deprecated public GSYDefaultHttpDataSource(String userAgent, @Nullable Predicate contentTypePredicate) { this( @@ -187,6 +211,8 @@ public class GSYDefaultHttpDataSource extends BaseDataSource implements HttpData } /** + * Creates an instance. + * * @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 HttpDataSource.InvalidContentTypeException} is thrown from {@link @@ -215,6 +241,8 @@ public class GSYDefaultHttpDataSource extends BaseDataSource implements HttpData } /** + * Creates an instance. + * * @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 HttpDataSource.InvalidContentTypeException} is thrown from {@link @@ -306,8 +334,8 @@ public class GSYDefaultHttpDataSource extends BaseDataSource implements HttpData try { connection = makeConnection(dataSpec); } catch (IOException e) { - throw new HttpDataSourceException("Unable to connect to " + dataSpec.uri.toString(), e, - dataSpec, HttpDataSourceException.TYPE_OPEN); + throw new HttpDataSourceException( + "Unable to connect", e, dataSpec, HttpDataSourceException.TYPE_OPEN); } String responseMessage; @@ -316,16 +344,27 @@ public class GSYDefaultHttpDataSource extends BaseDataSource implements HttpData responseMessage = connection.getResponseMessage(); } catch (IOException e) { closeConnectionQuietly(); - throw new HttpDataSourceException("Unable to connect to " + dataSpec.uri.toString(), e, - dataSpec, HttpDataSourceException.TYPE_OPEN); + throw new HttpDataSourceException( + "Unable to connect", e, dataSpec, HttpDataSourceException.TYPE_OPEN); } // Check for a valid response code. if (responseCode < 200 || responseCode > 299) { Map> headers = connection.getHeaderFields(); + @Nullable InputStream errorStream = connection.getErrorStream(); + byte[] errorResponseBody; + try { + errorResponseBody = + errorStream != null ? Util.toByteArray(errorStream) : Util.EMPTY_BYTE_ARRAY; + } catch (IOException e) { + throw new HttpDataSourceException( + "Error reading non-2xx response body", e, dataSpec, HttpDataSourceException.TYPE_OPEN); + } closeConnectionQuietly(); InvalidResponseCodeException exception = - new InvalidResponseCodeException(responseCode, responseMessage, headers, dataSpec); + new InvalidResponseCodeException( + responseCode, responseMessage, headers, dataSpec, errorResponseBody); + if (responseCode == 416) { exception.initCause(new DataSourceException(DataSourceException.POSITION_OUT_OF_RANGE)); } @@ -334,7 +373,7 @@ public class GSYDefaultHttpDataSource extends BaseDataSource implements HttpData // Check for a valid content type. String contentType = connection.getContentType(); - if (contentTypePredicate != null && !contentTypePredicate.evaluate(contentType)) { + if (contentTypePredicate != null && !contentTypePredicate.apply(contentType)) { closeConnectionQuietly(); throw new InvalidContentTypeException(contentType, dataSpec); } @@ -413,8 +452,8 @@ public class GSYDefaultHttpDataSource extends BaseDataSource implements HttpData * * @return The current open connection, or null. */ - protected final @Nullable - HttpURLConnection getConnection() { + @Nullable + protected final HttpURLConnection getConnection() { return connection; } @@ -456,7 +495,7 @@ public class GSYDefaultHttpDataSource extends BaseDataSource implements HttpData private HttpURLConnection makeConnection(DataSpec dataSpec) throws IOException { URL url = new URL(dataSpec.uri.toString()); @HttpMethod int httpMethod = dataSpec.httpMethod; - byte[] httpBody = dataSpec.httpBody; + @Nullable byte[] httpBody = dataSpec.httpBody; long position = dataSpec.position; long length = dataSpec.length; boolean allowGzip = dataSpec.isFlagSet(DataSpec.FLAG_ALLOW_GZIP); @@ -475,6 +514,7 @@ public class GSYDefaultHttpDataSource extends BaseDataSource implements HttpData dataSpec.httpRequestHeaders); } + // We need to handle redirects ourselves to allow cross-protocol redirects. int redirectCount = 0; while (redirectCount++ <= MAX_REDIRECTS) { @@ -523,7 +563,7 @@ public class GSYDefaultHttpDataSource extends BaseDataSource implements HttpData * * @param url The url to connect to. * @param httpMethod The http method. - * @param httpBody The body data. + * @param httpBody The body data, or {@code null} if not required. * @param position The byte offset of the requested data. * @param length The length of the requested data, or {@link C#LENGTH_UNSET}. * @param allowGzip Whether to allow the use of gzip. @@ -533,7 +573,7 @@ public class GSYDefaultHttpDataSource extends BaseDataSource implements HttpData private HttpURLConnection makeConnection( URL url, @HttpMethod int httpMethod, - byte[] httpBody, + @Nullable byte[] httpBody, long position, long length, boolean allowGzip, @@ -642,11 +682,11 @@ public class GSYDefaultHttpDataSource extends BaseDataSource implements HttpData * Handles a redirect. * * @param originalUrl The original URL. - * @param location The Location header in the response. + * @param location The Location header in the response. May be {@code null}. * @return The next URL. * @throws IOException If redirection isn't possible. */ - private static URL handleRedirect(URL originalUrl, String location) throws IOException { + private static URL handleRedirect(URL originalUrl, @Nullable String location) throws IOException { if (location == null) { throw new ProtocolException("Null location redirect"); } @@ -701,7 +741,7 @@ public class GSYDefaultHttpDataSource extends BaseDataSource implements HttpData // increase it. Log.w(TAG, "Inconsistent headers [" + contentLengthHeader + "] [" + contentRangeHeader + "]"); - contentLength = Math.max(contentLength, contentLengthFromRange); + contentLength = max(contentLength, contentLengthFromRange); } } catch (NumberFormatException e) { Log.e(TAG, "Unexpected Content-Range [" + contentRangeHeader + "]"); @@ -731,7 +771,7 @@ public class GSYDefaultHttpDataSource extends BaseDataSource implements HttpData } while (bytesSkipped != bytesToSkip) { - int readLength = (int) Math.min(bytesToSkip - bytesSkipped, skipBuffer.length); + int readLength = (int) min(bytesToSkip - bytesSkipped, skipBuffer.length); int read = inputStream.read(skipBuffer, 0, readLength); if (Thread.currentThread().isInterrupted()) { throw new InterruptedIOException(); @@ -770,7 +810,7 @@ public class GSYDefaultHttpDataSource extends BaseDataSource implements HttpData if (bytesRemaining == 0) { return C.RESULT_END_OF_INPUT; } - readLength = (int) Math.min(readLength, bytesRemaining); + readLength = (int) min(readLength, bytesRemaining); } int read = inputStream.read(buffer, offset, readLength); diff --git a/gsyVideoPlayer-exo_player2/src/main/java/tv/danmaku/ijk/media/exo2/ExoSourceManager.java b/gsyVideoPlayer-exo_player2/src/main/java/tv/danmaku/ijk/media/exo2/ExoSourceManager.java index 0460e408b5bfba4a4fa073ee0598ebb023f7c253..f91ca19ba0ac2ed2e73060d38d6ca3f0d8c8754d 100644 --- a/gsyVideoPlayer-exo_player2/src/main/java/tv/danmaku/ijk/media/exo2/ExoSourceManager.java +++ b/gsyVideoPlayer-exo_player2/src/main/java/tv/danmaku/ijk/media/exo2/ExoSourceManager.java @@ -30,8 +30,9 @@ import com.google.android.exoplayer2.upstream.RawResourceDataSource; import com.google.android.exoplayer2.upstream.cache.Cache; import com.google.android.exoplayer2.upstream.cache.CacheDataSource; import com.google.android.exoplayer2.upstream.cache.CacheDataSourceFactory; +import com.google.android.exoplayer2.upstream.cache.CacheKeyFactory; import com.google.android.exoplayer2.upstream.cache.CacheSpan; -import com.google.android.exoplayer2.upstream.cache.CacheUtil; +import com.google.android.exoplayer2.upstream.cache.CacheWriter; import com.google.android.exoplayer2.upstream.cache.ContentMetadata; import com.google.android.exoplayer2.upstream.cache.LeastRecentlyUsedCacheEvictor; import com.google.android.exoplayer2.upstream.cache.SimpleCache; @@ -41,6 +42,8 @@ import java.io.File; import java.util.Map; import java.util.NavigableSet; +import static com.google.android.exoplayer2.upstream.cache.CacheKeyFactory.DEFAULT; + /** * Created by guoshuyu on 2018/5/18. */ @@ -56,6 +59,7 @@ public class ExoSourceManager { private static Cache mCache; /** * 忽律Https证书校验 + * * @deprecated 如果需要忽略证书,请直接使用 ExoMediaSourceInterceptListener 的 getHttpDataSourceFactory */ @Deprecated @@ -238,12 +242,12 @@ public class ExoSourceManager { Cache cache = getCacheSingleInstance(context, cacheDir); if (!TextUtils.isEmpty(url)) { if (cache != null) { - CacheUtil.remove(cache, CacheUtil.generateKey(Uri.parse(url))); + removeCache(cache, url); } } else { if (cache != null) { for (String key : cache.getKeys()) { - CacheUtil.remove(cache, key); + removeCache(cache, key); } } } @@ -252,6 +256,24 @@ public class ExoSourceManager { } } + + public static void removeCache(Cache cache, String url) { + NavigableSet cachedSpans = cache.getCachedSpans(buildCacheKey(url)); + for (CacheSpan cachedSpan : cachedSpans) { + try { + cache.removeSpan(cachedSpan); + } catch (Exception e) { + // Do nothing. + } + } + } + + public static String buildCacheKey(String url) { + DataSpec dataSpec = new DataSpec(Uri.parse(url)); + String key = CacheKeyFactory.DEFAULT.buildCacheKey(dataSpec); + return key; + } + public static boolean cachePreView(Context context, File cacheDir, String url) { return resolveCacheState(getCacheSingleInstance(context, cacheDir), url); } @@ -262,6 +284,7 @@ public class ExoSourceManager { /** * 忽律Https证书校验 + * * @deprecated 如果需要忽略证书,请直接使用 ExoMediaSourceInterceptListener 的 getHttpDataSourceFactory */ @Deprecated @@ -367,7 +390,7 @@ public class ExoSourceManager { private static boolean resolveCacheState(Cache cache, String url) { boolean isCache = true; if (!TextUtils.isEmpty(url)) { - String key = CacheUtil.generateKey(Uri.parse(url)); + String key = buildCacheKey(url); if (!TextUtils.isEmpty(key)) { NavigableSet cachedSpans = cache.getCachedSpans(key); if (cachedSpans.size() == 0) { diff --git a/gsyVideoPlayer-exo_player2/src/main/java/tv/danmaku/ijk/media/exo2/IjkExo2MediaPlayer.java b/gsyVideoPlayer-exo_player2/src/main/java/tv/danmaku/ijk/media/exo2/IjkExo2MediaPlayer.java index e567b5120e9f96dba0459edb3c19c9cdc1366e04..3e200299607491a15dcc709c10f84915235972a3 100644 --- a/gsyVideoPlayer-exo_player2/src/main/java/tv/danmaku/ijk/media/exo2/IjkExo2MediaPlayer.java +++ b/gsyVideoPlayer-exo_player2/src/main/java/tv/danmaku/ijk/media/exo2/IjkExo2MediaPlayer.java @@ -650,51 +650,6 @@ public class IjkExo2MediaPlayer extends AbstractMediaPlayer implements Player.Ev } - @Override - public void onLoadStarted(EventTime eventTime, MediaSourceEventListener.LoadEventInfo loadEventInfo, MediaSourceEventListener.MediaLoadData mediaLoadData) { - - } - - @Override - public void onLoadCompleted(EventTime eventTime, MediaSourceEventListener.LoadEventInfo loadEventInfo, MediaSourceEventListener.MediaLoadData mediaLoadData) { - - } - - @Override - public void onLoadCanceled(EventTime eventTime, MediaSourceEventListener.LoadEventInfo loadEventInfo, MediaSourceEventListener.MediaLoadData mediaLoadData) { - - } - - @Override - public void onLoadError(EventTime eventTime, MediaSourceEventListener.LoadEventInfo loadEventInfo, MediaSourceEventListener.MediaLoadData mediaLoadData, IOException error, boolean wasCanceled) { - - } - - @Override - public void onDownstreamFormatChanged(EventTime eventTime, MediaSourceEventListener.MediaLoadData mediaLoadData) { - - } - - @Override - public void onUpstreamDiscarded(EventTime eventTime, MediaSourceEventListener.MediaLoadData mediaLoadData) { - - } - - @Override - public void onMediaPeriodCreated(EventTime eventTime) { - - } - - @Override - public void onMediaPeriodReleased(EventTime eventTime) { - - } - - @Override - public void onReadingStarted(EventTime eventTime) { - - } - @Override public void onBandwidthEstimate(EventTime eventTime, int totalLoadTimeMs, long totalBytesLoaded, long bitrateEstimate) { diff --git a/gsyVideoPlayer-exo_player2/src/main/java/tv/danmaku/ijk/media/exo2/demo/EventLogger.java b/gsyVideoPlayer-exo_player2/src/main/java/tv/danmaku/ijk/media/exo2/demo/EventLogger.java index ba825a93948372a21da84b228178a32eef0ac96a..cac2e2e55f88acb0394de23e64896dfc50cae286 100644 --- a/gsyVideoPlayer-exo_player2/src/main/java/tv/danmaku/ijk/media/exo2/demo/EventLogger.java +++ b/gsyVideoPlayer-exo_player2/src/main/java/tv/danmaku/ijk/media/exo2/demo/EventLogger.java @@ -29,7 +29,6 @@ import com.google.android.exoplayer2.RendererCapabilities; import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.audio.AudioRendererEventListener; import com.google.android.exoplayer2.decoder.DecoderCounters; -import com.google.android.exoplayer2.drm.DefaultDrmSessionEventListener; import com.google.android.exoplayer2.metadata.Metadata; import com.google.android.exoplayer2.metadata.MetadataOutput; import com.google.android.exoplayer2.metadata.emsg.EventMessage; @@ -56,8 +55,7 @@ import java.util.Locale; public final class EventLogger implements Player.EventListener, MetadataOutput, - AudioRendererEventListener, VideoRendererEventListener, MediaSourceEventListener, - DefaultDrmSessionEventListener { + AudioRendererEventListener, VideoRendererEventListener, MediaSourceEventListener{ private static final String TAG = "EventLogger"; private static final int MAX_TIMELINE_ITEM_LINES = 3; @@ -230,12 +228,6 @@ public final class EventLogger implements Player.EventListener, MetadataOutput, Log.d(TAG, "audioDisabled [" + getSessionTimeString() + "]"); } - @Override - public void onAudioSinkUnderrun(int bufferSize, long bufferSizeMs, long elapsedSinceLastFeedMs) { - printInternalError("audioTrackUnderrun [" + bufferSize + ", " + bufferSizeMs + ", " - + elapsedSinceLastFeedMs + "]", null); - } - // VideoRendererEventListener @Override @@ -276,28 +268,6 @@ public final class EventLogger implements Player.EventListener, MetadataOutput, Log.d(TAG, "renderedFirstFrame [" + surface + "]"); } - // DefaultDrmSessionEventListener - - @Override - public void onDrmSessionManagerError(Exception e) { - printInternalError("drmSessionManagerError", e); - } - - @Override - public void onDrmKeysRestored() { - Log.d(TAG, "drmKeysRestored [" + getSessionTimeString() + "]"); - } - - @Override - public void onDrmKeysRemoved() { - Log.d(TAG, "drmKeysRemoved [" + getSessionTimeString() + "]"); - } - - @Override - public void onDrmKeysLoaded() { - Log.d(TAG, "drmKeysLoaded [" + getSessionTimeString() + "]"); - } - //MediaSourceEventListener @Override @@ -323,51 +293,6 @@ public final class EventLogger implements Player.EventListener, MetadataOutput, Log.d(TAG, "]"); } - @Override - public void onMediaPeriodCreated(int windowIndex, MediaSource.MediaPeriodId mediaPeriodId) { - - } - - @Override - public void onMediaPeriodReleased(int windowIndex, MediaSource.MediaPeriodId mediaPeriodId) { - - } - - @Override - public void onLoadStarted(int windowIndex, @Nullable MediaSource.MediaPeriodId mediaPeriodId, LoadEventInfo loadEventInfo, MediaLoadData mediaLoadData) { - - } - - @Override - public void onLoadCompleted(int windowIndex, @Nullable MediaSource.MediaPeriodId mediaPeriodId, LoadEventInfo loadEventInfo, MediaLoadData mediaLoadData) { - - } - - @Override - public void onLoadCanceled(int windowIndex, @Nullable MediaSource.MediaPeriodId mediaPeriodId, LoadEventInfo loadEventInfo, MediaLoadData mediaLoadData) { - - } - - @Override - public void onLoadError(int windowIndex, @Nullable MediaSource.MediaPeriodId mediaPeriodId, LoadEventInfo loadEventInfo, MediaLoadData mediaLoadData, IOException error, boolean wasCanceled) { - printInternalError("loadError", error); - } - - @Override - public void onReadingStarted(int windowIndex, MediaSource.MediaPeriodId mediaPeriodId) { - - } - - @Override - public void onUpstreamDiscarded(int windowIndex, MediaSource.MediaPeriodId mediaPeriodId, MediaLoadData mediaLoadData) { - - } - - @Override - public void onDownstreamFormatChanged(int windowIndex, @Nullable MediaSource.MediaPeriodId mediaPeriodId, MediaLoadData mediaLoadData) { - - } - // Internal methods private void printInternalError(String type, Exception e) {