Merge pull request #6790 from google/dev-v2-r2.11.1

r2.11.1
This commit is contained in:
Oliver Woodman 2019-12-20 23:03:17 +00:00 committed by GitHub
commit 6aa35aaaa5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
46 changed files with 1595 additions and 269 deletions

View File

@ -1,5 +1,31 @@
# Release notes # # Release notes #
### 2.11.1 (2019-12-20) ###
* UI: Exclude `DefaultTimeBar` region from system gesture detection
([#6685](https://github.com/google/ExoPlayer/issues/6685)).
* ProGuard fixes:
* Ensure `Libgav1VideoRenderer` constructor is kept for use by
`DefaultRenderersFactory`
([#6773](https://github.com/google/ExoPlayer/issues/6773)).
* Ensure `VideoDecoderOutputBuffer` and its members are kept for use by video
decoder extensions.
* Ensure raw resources used with `RawResourceDataSource` are kept.
* Suppress spurious warnings about the `javax.annotation` package, and
restructure use of `IntDef` annotations to remove spurious warnings about
`SsaStyle$SsaAlignment`
([#6771](https://github.com/google/ExoPlayer/issues/6771)).
* Fix `CacheDataSource` to correctly propagate `DataSpec.httpRequestHeaders`.
* Fix issue with `DefaultDownloadIndex` that could result in an
`IllegalStateException` being thrown from
`DefaultDownloadIndex.getDownloadForCurrentRow`
([#6785](https://github.com/google/ExoPlayer/issues/6785)).
* Fix `IndexOutOfBoundsException` in `SinglePeriodTimeline.getWindow`
([#6776](https://github.com/google/ExoPlayer/issues/6776)).
* Add missing `@Nullable` to `MediaCodecAudioRenderer.getMediaClock` and
`SimpleDecoderAudioRenderer.getMediaClock`
([#6792](https://github.com/google/ExoPlayer/issues/6792)).
### 2.11.0 (2019-12-11) ### ### 2.11.0 (2019-12-11) ###
* Core library: * Core library:
@ -30,6 +56,10 @@
* Fix issue where player errors are thrown too early at playlist transitions * Fix issue where player errors are thrown too early at playlist transitions
([#5407](https://github.com/google/ExoPlayer/issues/5407)). ([#5407](https://github.com/google/ExoPlayer/issues/5407)).
* Add `Format` and renderer support flags to renderer `ExoPlaybackException`s. * Add `Format` and renderer support flags to renderer `ExoPlaybackException`s.
* Where there are multiple platform decoders for a given MIME type, prefer to
use one that advertises support for the profile and level of the media being
played over one that does not, even if it does not come first in the
`MediaCodecList`.
* DRM: * DRM:
* Inject `DrmSessionManager` into the `MediaSources` instead of `Renderers`. * Inject `DrmSessionManager` into the `MediaSources` instead of `Renderers`.
This allows each `MediaSource` in a `ConcatenatingMediaSource` to use a This allows each `MediaSource` in a `ConcatenatingMediaSource` to use a

View File

@ -13,8 +13,8 @@
// limitations under the License. // limitations under the License.
project.ext { project.ext {
// ExoPlayer version and version code. // ExoPlayer version and version code.
releaseVersion = '2.11.0' releaseVersion = '2.11.1'
releaseVersionCode = 2011000 releaseVersionCode = 2011001
minSdkVersion = 16 minSdkVersion = 16
appTargetSdkVersion = 29 appTargetSdkVersion = 29
targetSdkVersion = 28 // TODO: Bump once b/143232359 is resolved targetSdkVersion = 28 // TODO: Bump once b/143232359 is resolved

View File

@ -96,6 +96,14 @@ a custom track selector the choice of `Renderer` is up to your implementation.
You need to make sure you are passing a `Libgav1VideoRenderer` to the player and You need to make sure you are passing a `Libgav1VideoRenderer` to the player and
then you need to implement your own logic to use the renderer for a given track. then you need to implement your own logic to use the renderer for a given track.
## Using the extension in the demo application ##
To try out playback using the extension in the [demo application][], see
[enabling extension decoders][].
[demo application]: https://exoplayer.dev/demo-application.html
[enabling extension decoders]: https://exoplayer.dev/demo-application.html#enabling-extension-decoders
## Rendering options ## ## Rendering options ##
There are two possibilities for rendering the output `Libgav1VideoRenderer` There are two possibilities for rendering the output `Libgav1VideoRenderer`

View File

@ -106,9 +106,19 @@ then implement your own logic to use the renderer for a given track.
[#2781]: https://github.com/google/ExoPlayer/issues/2781 [#2781]: https://github.com/google/ExoPlayer/issues/2781
[Supported formats]: https://exoplayer.dev/supported-formats.html#ffmpeg-extension [Supported formats]: https://exoplayer.dev/supported-formats.html#ffmpeg-extension
## Using the extension in the demo application ##
To try out playback using the extension in the [demo application][], see
[enabling extension decoders][].
[demo application]: https://exoplayer.dev/demo-application.html
[enabling extension decoders]: https://exoplayer.dev/demo-application.html#enabling-extension-decoders
## Links ## ## Links ##
* [Troubleshooting using extensions][]
* [Javadoc][]: Classes matching `com.google.android.exoplayer2.ext.ffmpeg.*` * [Javadoc][]: Classes matching `com.google.android.exoplayer2.ext.ffmpeg.*`
belong to this module. belong to this module.
[Troubleshooting using extensions]: https://exoplayer.dev/troubleshooting.html#how-can-i-get-a-decoding-extension-to-load-and-be-used-for-playback
[Javadoc]: https://exoplayer.dev/doc/reference/index.html [Javadoc]: https://exoplayer.dev/doc/reference/index.html

View File

@ -97,6 +97,14 @@ a custom track selector the choice of `Renderer` is up to your implementation,
so you need to make sure you are passing an `LibflacAudioRenderer` to the so you need to make sure you are passing an `LibflacAudioRenderer` to the
player, then implement your own logic to use the renderer for a given track. player, then implement your own logic to use the renderer for a given track.
## Using the extension in the demo application ##
To try out playback using the extension in the [demo application][], see
[enabling extension decoders][].
[demo application]: https://exoplayer.dev/demo-application.html
[enabling extension decoders]: https://exoplayer.dev/demo-application.html#enabling-extension-decoders
## Links ## ## Links ##
* [Javadoc][]: Classes matching `com.google.android.exoplayer2.ext.flac.*` * [Javadoc][]: Classes matching `com.google.android.exoplayer2.ext.flac.*`

View File

@ -462,8 +462,9 @@ bool FLACParser::getSeekPositions(int64_t timeUs,
if (sampleNumber <= targetSampleNumber) { if (sampleNumber <= targetSampleNumber) {
result[0] = (sampleNumber * 1000000LL) / sampleRate; result[0] = (sampleNumber * 1000000LL) / sampleRate;
result[1] = firstFrameOffset + points[i - 1].stream_offset; result[1] = firstFrameOffset + points[i - 1].stream_offset;
if (sampleNumber == targetSampleNumber || i >= length) { if (sampleNumber == targetSampleNumber || i >= length ||
// exact seek, or no following seek point. points[i].sample_number == -1) { // placeholder
// exact seek, or no following non-placeholder seek point
result[2] = result[0]; result[2] = result[0];
result[3] = result[1]; result[3] = result[1];
} else { } else {

View File

@ -101,6 +101,14 @@ a custom track selector the choice of `Renderer` is up to your implementation,
so you need to make sure you are passing an `LibopusAudioRenderer` to the so you need to make sure you are passing an `LibopusAudioRenderer` to the
player, then implement your own logic to use the renderer for a given track. player, then implement your own logic to use the renderer for a given track.
## Using the extension in the demo application ##
To try out playback using the extension in the [demo application][], see
[enabling extension decoders][].
[demo application]: https://exoplayer.dev/demo-application.html
[enabling extension decoders]: https://exoplayer.dev/demo-application.html#enabling-extension-decoders
## Links ## ## Links ##
* [Javadoc][]: Classes matching `com.google.android.exoplayer2.ext.opus.*` * [Javadoc][]: Classes matching `com.google.android.exoplayer2.ext.opus.*`

View File

@ -114,6 +114,14 @@ a custom track selector the choice of `Renderer` is up to your implementation,
so you need to make sure you are passing an `LibvpxVideoRenderer` to the so you need to make sure you are passing an `LibvpxVideoRenderer` to the
player, then implement your own logic to use the renderer for a given track. player, then implement your own logic to use the renderer for a given track.
## Using the extension in the demo application ##
To try out playback using the extension in the [demo application][], see
[enabling extension decoders][].
[demo application]: https://exoplayer.dev/demo-application.html
[enabling extension decoders]: https://exoplayer.dev/demo-application.html#enabling-extension-decoders
## Rendering options ## ## Rendering options ##
There are two possibilities for rendering the output `LibvpxVideoRenderer` There are two possibilities for rendering the output `LibvpxVideoRenderer`

View File

@ -1,10 +1,24 @@
# Proguard rules specific to the core module. # Proguard rules specific to the core module.
# Constant folding for resource integers may mean that a resource passed to this method appears to be unused. Keep the method to prevent this from happening.
-keep class com.google.android.exoplayer2.upstream.RawResourceDataSource {
public static android.net.Uri buildRawResourceUri(int);
}
# Some members of this class are being accessed from native methods. Keep them unobfuscated.
-keep class com.google.android.exoplayer2.video.VideoDecoderOutputBuffer {
*;
}
# Constructors accessed via reflection in DefaultRenderersFactory # Constructors accessed via reflection in DefaultRenderersFactory
-dontnote com.google.android.exoplayer2.ext.vp9.LibvpxVideoRenderer -dontnote com.google.android.exoplayer2.ext.vp9.LibvpxVideoRenderer
-keepclassmembers class com.google.android.exoplayer2.ext.vp9.LibvpxVideoRenderer { -keepclassmembers class com.google.android.exoplayer2.ext.vp9.LibvpxVideoRenderer {
<init>(long, android.os.Handler, com.google.android.exoplayer2.video.VideoRendererEventListener, int); <init>(long, android.os.Handler, com.google.android.exoplayer2.video.VideoRendererEventListener, int);
} }
-dontnote com.google.android.exoplayer2.ext.av1.Libgav1VideoRenderer
-keepclassmembers class com.google.android.exoplayer2.ext.av1.Libgav1VideoRenderer {
<init>(long, android.os.Handler, com.google.android.exoplayer2.video.VideoRendererEventListener, int);
}
-dontnote com.google.android.exoplayer2.ext.opus.LibopusAudioRenderer -dontnote com.google.android.exoplayer2.ext.opus.LibopusAudioRenderer
-keepclassmembers class com.google.android.exoplayer2.ext.opus.LibopusAudioRenderer { -keepclassmembers class com.google.android.exoplayer2.ext.opus.LibopusAudioRenderer {
<init>(android.os.Handler, com.google.android.exoplayer2.audio.AudioRendererEventListener, com.google.android.exoplayer2.audio.AudioProcessor[]); <init>(android.os.Handler, com.google.android.exoplayer2.audio.AudioRendererEventListener, com.google.android.exoplayer2.audio.AudioProcessor[]);
@ -61,8 +75,4 @@
# Don't warn about checkerframework and Kotlin annotations # Don't warn about checkerframework and Kotlin annotations
-dontwarn org.checkerframework.** -dontwarn org.checkerframework.**
-dontwarn kotlin.annotations.jvm.** -dontwarn kotlin.annotations.jvm.**
-dontwarn javax.annotation.**
# Some members of this class are being accessed from native methods. Keep them unobfuscated.
-keep class com.google.android.exoplayer2.ext.video.VideoDecoderOutputBuffer {
*;
}

View File

@ -29,11 +29,11 @@ public final class ExoPlayerLibraryInfo {
/** The version of the library expressed as a string, for example "1.2.3". */ /** The version of the library expressed as a string, for example "1.2.3". */
// Intentionally hardcoded. Do not derive from other constants (e.g. VERSION_INT) or vice versa. // Intentionally hardcoded. Do not derive from other constants (e.g. VERSION_INT) or vice versa.
public static final String VERSION = "2.11.0"; public static final String VERSION = "2.11.1";
/** The version of the library expressed as {@code "ExoPlayerLib/" + VERSION}. */ /** The version of the library expressed as {@code "ExoPlayerLib/" + VERSION}. */
// Intentionally hardcoded. Do not derive from other constants (e.g. VERSION) or vice versa. // Intentionally hardcoded. Do not derive from other constants (e.g. VERSION) or vice versa.
public static final String VERSION_SLASHY = "ExoPlayerLib/2.11.0"; public static final String VERSION_SLASHY = "ExoPlayerLib/2.11.1";
/** /**
* The version of the library expressed as an integer, for example 1002003. * The version of the library expressed as an integer, for example 1002003.
@ -43,7 +43,7 @@ public final class ExoPlayerLibraryInfo {
* integer version 123045006 (123-045-006). * integer version 123045006 (123-045-006).
*/ */
// Intentionally hardcoded. Do not derive from other constants (e.g. VERSION) or vice versa. // Intentionally hardcoded. Do not derive from other constants (e.g. VERSION) or vice versa.
public static final int VERSION_INT = 2011000; public static final int VERSION_INT = 2011001;
/** /**
* Whether the library was compiled with {@link com.google.android.exoplayer2.util.Assertions} * Whether the library was compiled with {@link com.google.android.exoplayer2.util.Assertions}

View File

@ -804,9 +804,13 @@ public class AnalyticsCollector
/** Updates the queue with a newly created media period. */ /** Updates the queue with a newly created media period. */
public void onMediaPeriodCreated(int windowIndex, MediaPeriodId mediaPeriodId) { public void onMediaPeriodCreated(int windowIndex, MediaPeriodId mediaPeriodId) {
boolean isInTimeline = timeline.getIndexOfPeriod(mediaPeriodId.periodUid) != C.INDEX_UNSET; int periodIndex = timeline.getIndexOfPeriod(mediaPeriodId.periodUid);
boolean isInTimeline = periodIndex != C.INDEX_UNSET;
MediaPeriodInfo mediaPeriodInfo = MediaPeriodInfo mediaPeriodInfo =
new MediaPeriodInfo(mediaPeriodId, isInTimeline ? timeline : Timeline.EMPTY, windowIndex); new MediaPeriodInfo(
mediaPeriodId,
isInTimeline ? timeline : Timeline.EMPTY,
isInTimeline ? timeline.getPeriod(periodIndex, period).windowIndex : windowIndex);
mediaPeriodInfoQueue.add(mediaPeriodInfo); mediaPeriodInfoQueue.add(mediaPeriodInfo);
mediaPeriodIdToInfo.put(mediaPeriodId, mediaPeriodInfo); mediaPeriodIdToInfo.put(mediaPeriodId, mediaPeriodInfo);
lastPlayingMediaPeriod = mediaPeriodInfoQueue.get(0); lastPlayingMediaPeriod = mediaPeriodInfoQueue.get(0);

View File

@ -520,6 +520,7 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
} }
@Override @Override
@Nullable
public MediaClock getMediaClock() { public MediaClock getMediaClock() {
return this; return this;
} }

View File

@ -218,6 +218,7 @@ public abstract class SimpleDecoderAudioRenderer extends BaseRenderer implements
} }
@Override @Override
@Nullable
public MediaClock getMediaClock() { public MediaClock getMediaClock() {
return this; return this;
} }

View File

@ -1635,6 +1635,16 @@ public class MatroskaExtractor implements Extractor {
sizes[cuePointsSize - 1] = sizes[cuePointsSize - 1] =
(int) (segmentContentPosition + segmentContentSize - offsets[cuePointsSize - 1]); (int) (segmentContentPosition + segmentContentSize - offsets[cuePointsSize - 1]);
durationsUs[cuePointsSize - 1] = durationUs - timesUs[cuePointsSize - 1]; durationsUs[cuePointsSize - 1] = durationUs - timesUs[cuePointsSize - 1];
long lastDurationUs = durationsUs[cuePointsSize - 1];
if (lastDurationUs <= 0) {
Log.w(TAG, "Discarding last cue point with unexpected duration: " + lastDurationUs);
sizes = Arrays.copyOf(sizes, sizes.length - 1);
offsets = Arrays.copyOf(offsets, offsets.length - 1);
durationsUs = Arrays.copyOf(durationsUs, durationsUs.length - 1);
timesUs = Arrays.copyOf(timesUs, timesUs.length - 1);
}
cueTimesUs = null; cueTimesUs = null;
cueClusterPositions = null; cueClusterPositions = null;
return new ChunkIndex(sizes, offsets, durationsUs, timesUs); return new ChunkIndex(sizes, offsets, durationsUs, timesUs);

View File

@ -118,10 +118,6 @@ import java.io.IOException;
} }
} }
if (inputLength != C.LENGTH_UNSET && bytesSearched + atomSize > inputLength) {
// The file is invalid because the atom extends past the end of the file.
return false;
}
if (atomSize < headerSize) { if (atomSize < headerSize) {
// The file is invalid because the atom size is too small for its header. // The file is invalid because the atom size is too small for its header.
return false; return false;

View File

@ -59,9 +59,7 @@ import java.util.List;
*/ */
public abstract class MediaCodecRenderer extends BaseRenderer { public abstract class MediaCodecRenderer extends BaseRenderer {
/** /** Thrown when a failure occurs instantiating a decoder. */
* Thrown when a failure occurs instantiating a decoder.
*/
public static class DecoderInitializationException extends Exception { public static class DecoderInitializationException extends Exception {
private static final int CUSTOM_ERROR_CODE_BASE = -50000; private static final int CUSTOM_ERROR_CODE_BASE = -50000;

View File

@ -26,6 +26,8 @@ import androidx.annotation.VisibleForTesting;
import com.google.android.exoplayer2.database.DatabaseIOException; import com.google.android.exoplayer2.database.DatabaseIOException;
import com.google.android.exoplayer2.database.DatabaseProvider; import com.google.android.exoplayer2.database.DatabaseProvider;
import com.google.android.exoplayer2.database.VersionTable; import com.google.android.exoplayer2.database.VersionTable;
import com.google.android.exoplayer2.offline.Download.FailureReason;
import com.google.android.exoplayer2.offline.Download.State;
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;
import java.util.ArrayList; import java.util.ArrayList;
@ -239,6 +241,9 @@ public final class DefaultDownloadIndex implements WritableDownloadIndex {
try { try {
ContentValues values = new ContentValues(); ContentValues values = new ContentValues();
values.put(COLUMN_STATE, Download.STATE_REMOVING); values.put(COLUMN_STATE, Download.STATE_REMOVING);
// Only downloads in STATE_FAILED are allowed a failure reason, so we need to clear it here in
// case we're moving downloads from STATE_FAILED to STATE_REMOVING.
values.put(COLUMN_FAILURE_REASON, Download.FAILURE_REASON_NONE);
SQLiteDatabase writableDatabase = databaseProvider.getWritableDatabase(); SQLiteDatabase writableDatabase = databaseProvider.getWritableDatabase();
writableDatabase.update(tableName, values, /* whereClause= */ null, /* whereArgs= */ null); writableDatabase.update(tableName, values, /* whereClause= */ null, /* whereArgs= */ null);
} catch (SQLException e) { } catch (SQLException e) {
@ -351,14 +356,22 @@ public final class DefaultDownloadIndex implements WritableDownloadIndex {
DownloadProgress downloadProgress = new DownloadProgress(); DownloadProgress downloadProgress = new DownloadProgress();
downloadProgress.bytesDownloaded = cursor.getLong(COLUMN_INDEX_BYTES_DOWNLOADED); downloadProgress.bytesDownloaded = cursor.getLong(COLUMN_INDEX_BYTES_DOWNLOADED);
downloadProgress.percentDownloaded = cursor.getFloat(COLUMN_INDEX_PERCENT_DOWNLOADED); downloadProgress.percentDownloaded = cursor.getFloat(COLUMN_INDEX_PERCENT_DOWNLOADED);
@State int state = cursor.getInt(COLUMN_INDEX_STATE);
// It's possible the database contains failure reasons for non-failed downloads, which is
// invalid. Clear them here. See https://github.com/google/ExoPlayer/issues/6785.
@FailureReason
int failureReason =
state == Download.STATE_FAILED
? cursor.getInt(COLUMN_INDEX_FAILURE_REASON)
: Download.FAILURE_REASON_NONE;
return new Download( return new Download(
request, request,
/* state= */ cursor.getInt(COLUMN_INDEX_STATE), state,
/* startTimeMs= */ cursor.getLong(COLUMN_INDEX_START_TIME_MS), /* startTimeMs= */ cursor.getLong(COLUMN_INDEX_START_TIME_MS),
/* updateTimeMs= */ cursor.getLong(COLUMN_INDEX_UPDATE_TIME_MS), /* updateTimeMs= */ cursor.getLong(COLUMN_INDEX_UPDATE_TIME_MS),
/* contentLength= */ cursor.getLong(COLUMN_INDEX_CONTENT_LENGTH), /* contentLength= */ cursor.getLong(COLUMN_INDEX_CONTENT_LENGTH),
/* stopReason= */ cursor.getInt(COLUMN_INDEX_STOP_REASON), /* stopReason= */ cursor.getInt(COLUMN_INDEX_STOP_REASON),
/* failureReason= */ cursor.getInt(COLUMN_INDEX_FAILURE_REASON), failureReason,
downloadProgress); downloadProgress);
} }

View File

@ -130,9 +130,9 @@ public final class Download {
@FailureReason int failureReason, @FailureReason int failureReason,
DownloadProgress progress) { DownloadProgress progress) {
Assertions.checkNotNull(progress); Assertions.checkNotNull(progress);
Assertions.checkState((failureReason == FAILURE_REASON_NONE) == (state != STATE_FAILED)); Assertions.checkArgument((failureReason == FAILURE_REASON_NONE) == (state != STATE_FAILED));
if (stopReason != 0) { if (stopReason != 0) {
Assertions.checkState(state != STATE_DOWNLOADING && state != STATE_QUEUED); Assertions.checkArgument(state != STATE_DOWNLOADING && state != STATE_QUEUED);
} }
this.request = request; this.request = request;
this.state = state; this.state = state;

View File

@ -300,12 +300,12 @@ public final class SsaDecoder extends SimpleSubtitleDecoder {
float screenWidth, float screenWidth,
float screenHeight) { float screenHeight) {
@SsaStyle.SsaAlignment int alignment; @SsaStyle.SsaAlignment int alignment;
if (styleOverrides.alignment != SsaStyle.SsaAlignment.UNKNOWN) { if (styleOverrides.alignment != SsaStyle.SSA_ALIGNMENT_UNKNOWN) {
alignment = styleOverrides.alignment; alignment = styleOverrides.alignment;
} else if (style != null) { } else if (style != null) {
alignment = style.alignment; alignment = style.alignment;
} else { } else {
alignment = SsaStyle.SsaAlignment.UNKNOWN; alignment = SsaStyle.SSA_ALIGNMENT_UNKNOWN;
} }
@Cue.AnchorType int positionAnchor = toPositionAnchor(alignment); @Cue.AnchorType int positionAnchor = toPositionAnchor(alignment);
@Cue.AnchorType int lineAnchor = toLineAnchor(alignment); @Cue.AnchorType int lineAnchor = toLineAnchor(alignment);
@ -337,19 +337,19 @@ public final class SsaDecoder extends SimpleSubtitleDecoder {
@Nullable @Nullable
private static Layout.Alignment toTextAlignment(@SsaStyle.SsaAlignment int alignment) { private static Layout.Alignment toTextAlignment(@SsaStyle.SsaAlignment int alignment) {
switch (alignment) { switch (alignment) {
case SsaStyle.SsaAlignment.BOTTOM_LEFT: case SsaStyle.SSA_ALIGNMENT_BOTTOM_LEFT:
case SsaStyle.SsaAlignment.MIDDLE_LEFT: case SsaStyle.SSA_ALIGNMENT_MIDDLE_LEFT:
case SsaStyle.SsaAlignment.TOP_LEFT: case SsaStyle.SSA_ALIGNMENT_TOP_LEFT:
return Layout.Alignment.ALIGN_NORMAL; return Layout.Alignment.ALIGN_NORMAL;
case SsaStyle.SsaAlignment.BOTTOM_CENTER: case SsaStyle.SSA_ALIGNMENT_BOTTOM_CENTER:
case SsaStyle.SsaAlignment.MIDDLE_CENTER: case SsaStyle.SSA_ALIGNMENT_MIDDLE_CENTER:
case SsaStyle.SsaAlignment.TOP_CENTER: case SsaStyle.SSA_ALIGNMENT_TOP_CENTER:
return Layout.Alignment.ALIGN_CENTER; return Layout.Alignment.ALIGN_CENTER;
case SsaStyle.SsaAlignment.BOTTOM_RIGHT: case SsaStyle.SSA_ALIGNMENT_BOTTOM_RIGHT:
case SsaStyle.SsaAlignment.MIDDLE_RIGHT: case SsaStyle.SSA_ALIGNMENT_MIDDLE_RIGHT:
case SsaStyle.SsaAlignment.TOP_RIGHT: case SsaStyle.SSA_ALIGNMENT_TOP_RIGHT:
return Layout.Alignment.ALIGN_OPPOSITE; return Layout.Alignment.ALIGN_OPPOSITE;
case SsaStyle.SsaAlignment.UNKNOWN: case SsaStyle.SSA_ALIGNMENT_UNKNOWN:
return null; return null;
default: default:
Log.w(TAG, "Unknown alignment: " + alignment); Log.w(TAG, "Unknown alignment: " + alignment);
@ -360,19 +360,19 @@ public final class SsaDecoder extends SimpleSubtitleDecoder {
@Cue.AnchorType @Cue.AnchorType
private static int toLineAnchor(@SsaStyle.SsaAlignment int alignment) { private static int toLineAnchor(@SsaStyle.SsaAlignment int alignment) {
switch (alignment) { switch (alignment) {
case SsaStyle.SsaAlignment.BOTTOM_LEFT: case SsaStyle.SSA_ALIGNMENT_BOTTOM_LEFT:
case SsaStyle.SsaAlignment.BOTTOM_CENTER: case SsaStyle.SSA_ALIGNMENT_BOTTOM_CENTER:
case SsaStyle.SsaAlignment.BOTTOM_RIGHT: case SsaStyle.SSA_ALIGNMENT_BOTTOM_RIGHT:
return Cue.ANCHOR_TYPE_END; return Cue.ANCHOR_TYPE_END;
case SsaStyle.SsaAlignment.MIDDLE_LEFT: case SsaStyle.SSA_ALIGNMENT_MIDDLE_LEFT:
case SsaStyle.SsaAlignment.MIDDLE_CENTER: case SsaStyle.SSA_ALIGNMENT_MIDDLE_CENTER:
case SsaStyle.SsaAlignment.MIDDLE_RIGHT: case SsaStyle.SSA_ALIGNMENT_MIDDLE_RIGHT:
return Cue.ANCHOR_TYPE_MIDDLE; return Cue.ANCHOR_TYPE_MIDDLE;
case SsaStyle.SsaAlignment.TOP_LEFT: case SsaStyle.SSA_ALIGNMENT_TOP_LEFT:
case SsaStyle.SsaAlignment.TOP_CENTER: case SsaStyle.SSA_ALIGNMENT_TOP_CENTER:
case SsaStyle.SsaAlignment.TOP_RIGHT: case SsaStyle.SSA_ALIGNMENT_TOP_RIGHT:
return Cue.ANCHOR_TYPE_START; return Cue.ANCHOR_TYPE_START;
case SsaStyle.SsaAlignment.UNKNOWN: case SsaStyle.SSA_ALIGNMENT_UNKNOWN:
return Cue.TYPE_UNSET; return Cue.TYPE_UNSET;
default: default:
Log.w(TAG, "Unknown alignment: " + alignment); Log.w(TAG, "Unknown alignment: " + alignment);
@ -383,19 +383,19 @@ public final class SsaDecoder extends SimpleSubtitleDecoder {
@Cue.AnchorType @Cue.AnchorType
private static int toPositionAnchor(@SsaStyle.SsaAlignment int alignment) { private static int toPositionAnchor(@SsaStyle.SsaAlignment int alignment) {
switch (alignment) { switch (alignment) {
case SsaStyle.SsaAlignment.BOTTOM_LEFT: case SsaStyle.SSA_ALIGNMENT_BOTTOM_LEFT:
case SsaStyle.SsaAlignment.MIDDLE_LEFT: case SsaStyle.SSA_ALIGNMENT_MIDDLE_LEFT:
case SsaStyle.SsaAlignment.TOP_LEFT: case SsaStyle.SSA_ALIGNMENT_TOP_LEFT:
return Cue.ANCHOR_TYPE_START; return Cue.ANCHOR_TYPE_START;
case SsaStyle.SsaAlignment.BOTTOM_CENTER: case SsaStyle.SSA_ALIGNMENT_BOTTOM_CENTER:
case SsaStyle.SsaAlignment.MIDDLE_CENTER: case SsaStyle.SSA_ALIGNMENT_MIDDLE_CENTER:
case SsaStyle.SsaAlignment.TOP_CENTER: case SsaStyle.SSA_ALIGNMENT_TOP_CENTER:
return Cue.ANCHOR_TYPE_MIDDLE; return Cue.ANCHOR_TYPE_MIDDLE;
case SsaStyle.SsaAlignment.BOTTOM_RIGHT: case SsaStyle.SSA_ALIGNMENT_BOTTOM_RIGHT:
case SsaStyle.SsaAlignment.MIDDLE_RIGHT: case SsaStyle.SSA_ALIGNMENT_MIDDLE_RIGHT:
case SsaStyle.SsaAlignment.TOP_RIGHT: case SsaStyle.SSA_ALIGNMENT_TOP_RIGHT:
return Cue.ANCHOR_TYPE_END; return Cue.ANCHOR_TYPE_END;
case SsaStyle.SsaAlignment.UNKNOWN: case SsaStyle.SSA_ALIGNMENT_UNKNOWN:
return Cue.TYPE_UNSET; return Cue.TYPE_UNSET;
default: default:
Log.w(TAG, "Unknown alignment: " + alignment); Log.w(TAG, "Unknown alignment: " + alignment);

View File

@ -37,6 +37,52 @@ import java.util.regex.Pattern;
private static final String TAG = "SsaStyle"; private static final String TAG = "SsaStyle";
/**
* The SSA/ASS alignments.
*
* <p>Allowed values:
*
* <ul>
* <li>{@link #SSA_ALIGNMENT_UNKNOWN}
* <li>{@link #SSA_ALIGNMENT_BOTTOM_LEFT}
* <li>{@link #SSA_ALIGNMENT_BOTTOM_CENTER}
* <li>{@link #SSA_ALIGNMENT_BOTTOM_RIGHT}
* <li>{@link #SSA_ALIGNMENT_MIDDLE_LEFT}
* <li>{@link #SSA_ALIGNMENT_MIDDLE_CENTER}
* <li>{@link #SSA_ALIGNMENT_MIDDLE_RIGHT}
* <li>{@link #SSA_ALIGNMENT_TOP_LEFT}
* <li>{@link #SSA_ALIGNMENT_TOP_CENTER}
* <li>{@link #SSA_ALIGNMENT_TOP_RIGHT}
* </ul>
*/
@IntDef({
SSA_ALIGNMENT_UNKNOWN,
SSA_ALIGNMENT_BOTTOM_LEFT,
SSA_ALIGNMENT_BOTTOM_CENTER,
SSA_ALIGNMENT_BOTTOM_RIGHT,
SSA_ALIGNMENT_MIDDLE_LEFT,
SSA_ALIGNMENT_MIDDLE_CENTER,
SSA_ALIGNMENT_MIDDLE_RIGHT,
SSA_ALIGNMENT_TOP_LEFT,
SSA_ALIGNMENT_TOP_CENTER,
SSA_ALIGNMENT_TOP_RIGHT,
})
@Documented
@Retention(SOURCE)
public @interface SsaAlignment {}
// The numbering follows the ASS (v4+) spec (i.e. the points on the number pad).
public static final int SSA_ALIGNMENT_UNKNOWN = -1;
public static final int SSA_ALIGNMENT_BOTTOM_LEFT = 1;
public static final int SSA_ALIGNMENT_BOTTOM_CENTER = 2;
public static final int SSA_ALIGNMENT_BOTTOM_RIGHT = 3;
public static final int SSA_ALIGNMENT_MIDDLE_LEFT = 4;
public static final int SSA_ALIGNMENT_MIDDLE_CENTER = 5;
public static final int SSA_ALIGNMENT_MIDDLE_RIGHT = 6;
public static final int SSA_ALIGNMENT_TOP_LEFT = 7;
public static final int SSA_ALIGNMENT_TOP_CENTER = 8;
public static final int SSA_ALIGNMENT_TOP_RIGHT = 9;
public final String name; public final String name;
@SsaAlignment public final int alignment; @SsaAlignment public final int alignment;
@ -77,22 +123,22 @@ import java.util.regex.Pattern;
// Swallow the exception and return UNKNOWN below. // Swallow the exception and return UNKNOWN below.
} }
Log.w(TAG, "Ignoring unknown alignment: " + alignmentStr); Log.w(TAG, "Ignoring unknown alignment: " + alignmentStr);
return SsaAlignment.UNKNOWN; return SSA_ALIGNMENT_UNKNOWN;
} }
private static boolean isValidAlignment(@SsaAlignment int alignment) { private static boolean isValidAlignment(@SsaAlignment int alignment) {
switch (alignment) { switch (alignment) {
case SsaAlignment.BOTTOM_CENTER: case SSA_ALIGNMENT_BOTTOM_CENTER:
case SsaAlignment.BOTTOM_LEFT: case SSA_ALIGNMENT_BOTTOM_LEFT:
case SsaAlignment.BOTTOM_RIGHT: case SSA_ALIGNMENT_BOTTOM_RIGHT:
case SsaAlignment.MIDDLE_CENTER: case SSA_ALIGNMENT_MIDDLE_CENTER:
case SsaAlignment.MIDDLE_LEFT: case SSA_ALIGNMENT_MIDDLE_LEFT:
case SsaAlignment.MIDDLE_RIGHT: case SSA_ALIGNMENT_MIDDLE_RIGHT:
case SsaAlignment.TOP_CENTER: case SSA_ALIGNMENT_TOP_CENTER:
case SsaAlignment.TOP_LEFT: case SSA_ALIGNMENT_TOP_LEFT:
case SsaAlignment.TOP_RIGHT: case SSA_ALIGNMENT_TOP_RIGHT:
return true; return true;
case SsaAlignment.UNKNOWN: case SSA_ALIGNMENT_UNKNOWN:
default: default:
return false; return false;
} }
@ -177,7 +223,7 @@ import java.util.regex.Pattern;
} }
public static Overrides parseFromDialogue(String text) { public static Overrides parseFromDialogue(String text) {
@SsaAlignment int alignment = SsaAlignment.UNKNOWN; @SsaAlignment int alignment = SSA_ALIGNMENT_UNKNOWN;
PointF position = null; PointF position = null;
Matcher matcher = BRACES_PATTERN.matcher(text); Matcher matcher = BRACES_PATTERN.matcher(text);
while (matcher.find()) { while (matcher.find()) {
@ -192,7 +238,7 @@ import java.util.regex.Pattern;
} }
try { try {
@SsaAlignment int parsedAlignment = parseAlignmentOverride(braceContents); @SsaAlignment int parsedAlignment = parseAlignmentOverride(braceContents);
if (parsedAlignment != SsaAlignment.UNKNOWN) { if (parsedAlignment != SSA_ALIGNMENT_UNKNOWN) {
alignment = parsedAlignment; alignment = parsedAlignment;
} }
} catch (RuntimeException e) { } catch (RuntimeException e) {
@ -249,36 +295,7 @@ import java.util.regex.Pattern;
@SsaAlignment @SsaAlignment
private static int parseAlignmentOverride(String braceContents) { private static int parseAlignmentOverride(String braceContents) {
Matcher matcher = ALIGNMENT_OVERRIDE_PATTERN.matcher(braceContents); Matcher matcher = ALIGNMENT_OVERRIDE_PATTERN.matcher(braceContents);
return matcher.find() ? parseAlignment(matcher.group(1)) : SsaAlignment.UNKNOWN; return matcher.find() ? parseAlignment(matcher.group(1)) : SSA_ALIGNMENT_UNKNOWN;
} }
} }
/** The SSA/ASS alignments. */
@IntDef({
SsaAlignment.UNKNOWN,
SsaAlignment.BOTTOM_LEFT,
SsaAlignment.BOTTOM_CENTER,
SsaAlignment.BOTTOM_RIGHT,
SsaAlignment.MIDDLE_LEFT,
SsaAlignment.MIDDLE_CENTER,
SsaAlignment.MIDDLE_RIGHT,
SsaAlignment.TOP_LEFT,
SsaAlignment.TOP_CENTER,
SsaAlignment.TOP_RIGHT,
})
@Documented
@Retention(SOURCE)
/* package */ @interface SsaAlignment {
// The numbering follows the ASS (v4+) spec (i.e. the points on the number pad).
int UNKNOWN = -1;
int BOTTOM_LEFT = 1;
int BOTTOM_CENTER = 2;
int BOTTOM_RIGHT = 3;
int MIDDLE_LEFT = 4;
int MIDDLE_CENTER = 5;
int MIDDLE_RIGHT = 6;
int TOP_LEFT = 7;
int TOP_CENTER = 8;
int TOP_RIGHT = 9;
}
} }

View File

@ -77,39 +77,40 @@ public final class WebvttCue extends Cue {
@Documented @Documented
@Retention(SOURCE) @Retention(SOURCE)
@IntDef({ @IntDef({
TextAlignment.START, TEXT_ALIGNMENT_START,
TextAlignment.CENTER, TEXT_ALIGNMENT_CENTER,
TextAlignment.END, TEXT_ALIGNMENT_END,
TextAlignment.LEFT, TEXT_ALIGNMENT_LEFT,
TextAlignment.RIGHT TEXT_ALIGNMENT_RIGHT
}) })
public @interface TextAlignment { public @interface TextAlignment {}
/** /**
* See WebVTT's <a * See WebVTT's <a
* href="https://www.w3.org/TR/webvtt1/#webvtt-cue-start-alignment">align:start</a>. * href="https://www.w3.org/TR/webvtt1/#webvtt-cue-start-alignment">align:start</a>.
*/ */
int START = 1; public static final int TEXT_ALIGNMENT_START = 1;
/** /**
* See WebVTT's <a * See WebVTT's <a
* href="https://www.w3.org/TR/webvtt1/#webvtt-cue-center-alignment">align:center</a>. * href="https://www.w3.org/TR/webvtt1/#webvtt-cue-center-alignment">align:center</a>.
*/ */
int CENTER = 2; public static final int TEXT_ALIGNMENT_CENTER = 2;
/** /**
* See WebVTT's <a * See WebVTT's <a href="https://www.w3.org/TR/webvtt1/#webvtt-cue-end-alignment">align:end</a>.
* href="https://www.w3.org/TR/webvtt1/#webvtt-cue-end-alignment">align:end</a>.
*/ */
int END = 3; public static final int TEXT_ALIGNMENT_END = 3;
/** /**
* See WebVTT's <a * See WebVTT's <a href="https://www.w3.org/TR/webvtt1/#webvtt-cue-left-alignment">align:left</a>.
* href="https://www.w3.org/TR/webvtt1/#webvtt-cue-left-alignment">align:left</a>.
*/ */
int LEFT = 4; public static final int TEXT_ALIGNMENT_LEFT = 4;
/** /**
* See WebVTT's <a * See WebVTT's <a
* href="https://www.w3.org/TR/webvtt1/#webvtt-cue-right-alignment">align:right</a>. * href="https://www.w3.org/TR/webvtt1/#webvtt-cue-right-alignment">align:right</a>.
*/ */
int RIGHT = 5; public static final int TEXT_ALIGNMENT_RIGHT = 5;
}
private static final String TAG = "WebvttCueBuilder"; private static final String TAG = "WebvttCueBuilder";
@ -140,7 +141,7 @@ public final class WebvttCue extends Cue {
endTime = 0; endTime = 0;
text = null; text = null;
// Default: https://www.w3.org/TR/webvtt1/#webvtt-cue-text-alignment // Default: https://www.w3.org/TR/webvtt1/#webvtt-cue-text-alignment
textAlignment = TextAlignment.CENTER; textAlignment = TEXT_ALIGNMENT_CENTER;
line = Cue.DIMEN_UNSET; line = Cue.DIMEN_UNSET;
// Defaults to NUMBER (true): https://www.w3.org/TR/webvtt1/#webvtt-cue-snap-to-lines-flag // Defaults to NUMBER (true): https://www.w3.org/TR/webvtt1/#webvtt-cue-snap-to-lines-flag
lineType = Cue.LINE_TYPE_NUMBER; lineType = Cue.LINE_TYPE_NUMBER;
@ -251,13 +252,13 @@ public final class WebvttCue extends Cue {
// https://www.w3.org/TR/webvtt1/#webvtt-cue-position // https://www.w3.org/TR/webvtt1/#webvtt-cue-position
private static float derivePosition(@TextAlignment int textAlignment) { private static float derivePosition(@TextAlignment int textAlignment) {
switch (textAlignment) { switch (textAlignment) {
case TextAlignment.LEFT: case TEXT_ALIGNMENT_LEFT:
return 0.0f; return 0.0f;
case TextAlignment.RIGHT: case TEXT_ALIGNMENT_RIGHT:
return 1.0f; return 1.0f;
case TextAlignment.START: case TEXT_ALIGNMENT_START:
case TextAlignment.CENTER: case TEXT_ALIGNMENT_CENTER:
case TextAlignment.END: case TEXT_ALIGNMENT_END:
default: default:
return DEFAULT_POSITION; return DEFAULT_POSITION;
} }
@ -267,13 +268,13 @@ public final class WebvttCue extends Cue {
@AnchorType @AnchorType
private static int derivePositionAnchor(@TextAlignment int textAlignment) { private static int derivePositionAnchor(@TextAlignment int textAlignment) {
switch (textAlignment) { switch (textAlignment) {
case TextAlignment.LEFT: case TEXT_ALIGNMENT_LEFT:
case TextAlignment.START: case TEXT_ALIGNMENT_START:
return Cue.ANCHOR_TYPE_START; return Cue.ANCHOR_TYPE_START;
case TextAlignment.RIGHT: case TEXT_ALIGNMENT_RIGHT:
case TextAlignment.END: case TEXT_ALIGNMENT_END:
return Cue.ANCHOR_TYPE_END; return Cue.ANCHOR_TYPE_END;
case TextAlignment.CENTER: case TEXT_ALIGNMENT_CENTER:
default: default:
return Cue.ANCHOR_TYPE_MIDDLE; return Cue.ANCHOR_TYPE_MIDDLE;
} }
@ -282,13 +283,13 @@ public final class WebvttCue extends Cue {
@Nullable @Nullable
private static Alignment convertTextAlignment(@TextAlignment int textAlignment) { private static Alignment convertTextAlignment(@TextAlignment int textAlignment) {
switch (textAlignment) { switch (textAlignment) {
case TextAlignment.START: case TEXT_ALIGNMENT_START:
case TextAlignment.LEFT: case TEXT_ALIGNMENT_LEFT:
return Alignment.ALIGN_NORMAL; return Alignment.ALIGN_NORMAL;
case TextAlignment.CENTER: case TEXT_ALIGNMENT_CENTER:
return Alignment.ALIGN_CENTER; return Alignment.ALIGN_CENTER;
case TextAlignment.END: case TEXT_ALIGNMENT_END:
case TextAlignment.RIGHT: case TEXT_ALIGNMENT_RIGHT:
return Alignment.ALIGN_OPPOSITE; return Alignment.ALIGN_OPPOSITE;
default: default:
Log.w(TAG, "Unknown textAlignment: " + textAlignment); Log.w(TAG, "Unknown textAlignment: " + textAlignment);

View File

@ -308,20 +308,20 @@ public final class WebvttCueParser {
private static int parseTextAlignment(String s) { private static int parseTextAlignment(String s) {
switch (s) { switch (s) {
case "start": case "start":
return WebvttCue.Builder.TextAlignment.START; return WebvttCue.Builder.TEXT_ALIGNMENT_START;
case "left": case "left":
return WebvttCue.Builder.TextAlignment.LEFT; return WebvttCue.Builder.TEXT_ALIGNMENT_LEFT;
case "center": case "center":
case "middle": case "middle":
return WebvttCue.Builder.TextAlignment.CENTER; return WebvttCue.Builder.TEXT_ALIGNMENT_CENTER;
case "end": case "end":
return WebvttCue.Builder.TextAlignment.END; return WebvttCue.Builder.TEXT_ALIGNMENT_END;
case "right": case "right":
return WebvttCue.Builder.TextAlignment.RIGHT; return WebvttCue.Builder.TEXT_ALIGNMENT_RIGHT;
default: default:
Log.w(TAG, "Invalid alignment value: " + s); Log.w(TAG, "Invalid alignment value: " + s);
// Default value: https://www.w3.org/TR/webvtt1/#webvtt-cue-text-alignment // Default value: https://www.w3.org/TR/webvtt1/#webvtt-cue-text-alignment
return WebvttCue.Builder.TextAlignment.CENTER; return WebvttCue.Builder.TEXT_ALIGNMENT_CENTER;
} }
} }

View File

@ -75,7 +75,7 @@ import com.google.android.exoplayer2.util.Assertions;
* the two are tightly bound together. It may only be possible to play a certain combination tracks * the two are tightly bound together. It may only be possible to play a certain combination tracks
* if the renderers are configured in a particular way. Equally, it may only be possible to * if the renderers are configured in a particular way. Equally, it may only be possible to
* configure renderers in a particular way if certain tracks are selected. Hence it makes sense to * configure renderers in a particular way if certain tracks are selected. Hence it makes sense to
* determined the track selection and corresponding renderer configurations in a single step. * determine the track selection and corresponding renderer configurations in a single step.
* *
* <h3>Threading model</h3> * <h3>Threading model</h3>
* *

View File

@ -71,6 +71,7 @@ public class DefaultLoadErrorHandlingPolicy implements LoadErrorHandlingPolicy {
int responseCode = ((InvalidResponseCodeException) exception).responseCode; int responseCode = ((InvalidResponseCodeException) exception).responseCode;
return responseCode == 404 // HTTP 404 Not Found. return responseCode == 404 // HTTP 404 Not Found.
|| responseCode == 410 // HTTP 410 Gone. || responseCode == 410 // HTTP 410 Gone.
|| responseCode == 416 // HTTP 416 Range Not Satisfiable.
? DEFAULT_TRACK_BLACKLIST_MS ? DEFAULT_TRACK_BLACKLIST_MS
: C.TIME_UNSET; : C.TIME_UNSET;
} }

View File

@ -138,7 +138,8 @@ public final class CacheDataSource implements DataSource {
@Nullable private Uri actualUri; @Nullable private Uri actualUri;
@HttpMethod private int httpMethod; @HttpMethod private int httpMethod;
@Nullable private byte[] httpBody; @Nullable private byte[] httpBody;
private int flags; private Map<String, String> httpRequestHeaders = Collections.emptyMap();
@DataSpec.Flags private int flags;
@Nullable private String key; @Nullable private String key;
private long readPosition; private long readPosition;
private long bytesRemaining; private long bytesRemaining;
@ -263,6 +264,7 @@ public final class CacheDataSource implements DataSource {
actualUri = getRedirectedUriOrDefault(cache, key, /* defaultUri= */ uri); actualUri = getRedirectedUriOrDefault(cache, key, /* defaultUri= */ uri);
httpMethod = dataSpec.httpMethod; httpMethod = dataSpec.httpMethod;
httpBody = dataSpec.httpBody; httpBody = dataSpec.httpBody;
httpRequestHeaders = dataSpec.httpRequestHeaders;
flags = dataSpec.flags; flags = dataSpec.flags;
readPosition = dataSpec.position; readPosition = dataSpec.position;
@ -353,6 +355,10 @@ public final class CacheDataSource implements DataSource {
actualUri = null; actualUri = null;
httpMethod = DataSpec.HTTP_METHOD_GET; httpMethod = DataSpec.HTTP_METHOD_GET;
httpBody = null; httpBody = null;
httpRequestHeaders = Collections.emptyMap();
flags = 0;
readPosition = 0;
key = null;
notifyBytesRead(); notifyBytesRead();
try { try {
closeCurrentSource(); closeCurrentSource();
@ -399,7 +405,15 @@ public final class CacheDataSource implements DataSource {
nextDataSource = upstreamDataSource; nextDataSource = upstreamDataSource;
nextDataSpec = nextDataSpec =
new DataSpec( new DataSpec(
uri, httpMethod, httpBody, readPosition, readPosition, bytesRemaining, key, flags); uri,
httpMethod,
httpBody,
readPosition,
readPosition,
bytesRemaining,
key,
flags,
httpRequestHeaders);
} else if (nextSpan.isCached) { } else if (nextSpan.isCached) {
// Data is cached, read from cache. // Data is cached, read from cache.
Uri fileUri = Uri.fromFile(nextSpan.file); Uri fileUri = Uri.fromFile(nextSpan.file);
@ -408,6 +422,8 @@ public final class CacheDataSource implements DataSource {
if (bytesRemaining != C.LENGTH_UNSET) { if (bytesRemaining != C.LENGTH_UNSET) {
length = Math.min(length, bytesRemaining); length = Math.min(length, bytesRemaining);
} }
// Deliberately skip the HTTP-related parameters since we're reading from the cache, not
// making an HTTP request.
nextDataSpec = new DataSpec(fileUri, readPosition, filePosition, length, key, flags); nextDataSpec = new DataSpec(fileUri, readPosition, filePosition, length, key, flags);
nextDataSource = cacheReadDataSource; nextDataSource = cacheReadDataSource;
} else { } else {
@ -422,7 +438,16 @@ public final class CacheDataSource implements DataSource {
} }
} }
nextDataSpec = nextDataSpec =
new DataSpec(uri, httpMethod, httpBody, readPosition, readPosition, length, key, flags); new DataSpec(
uri,
httpMethod,
httpBody,
readPosition,
readPosition,
length,
key,
flags,
httpRequestHeaders);
if (cacheWriteDataSource != null) { if (cacheWriteDataSource != null) {
nextDataSource = cacheWriteDataSource; nextDataSource = cacheWriteDataSource;
} else { } else {

View File

@ -1,6 +1,6 @@
seekMap: seekMap:
isSeekable = true isSeekable = true
duration = 1072000 duration = 1104000
getPosition(0) = [[timeUs=67000, position=5576]] getPosition(0) = [[timeUs=67000, position=5576]]
numberOfTracks = 2 numberOfTracks = 2
track 1: track 1:

View File

@ -1,6 +1,6 @@
seekMap: seekMap:
isSeekable = true isSeekable = true
duration = 1072000 duration = 1104000
getPosition(0) = [[timeUs=67000, position=5576]] getPosition(0) = [[timeUs=67000, position=5576]]
numberOfTracks = 2 numberOfTracks = 2
track 1: track 1:
@ -27,93 +27,85 @@ track 1:
initializationData: initializationData:
data = length 30, hash F6F3D010 data = length 30, hash F6F3D010
data = length 10, hash 7A0D0F2B data = length 10, hash 7A0D0F2B
total output bytes = 30995 total output bytes = 29422
sample count = 22 sample count = 20
sample 0: sample 0:
time = 334000
flags = 0
data = length 953, hash 7160C661
sample 1:
time = 300000
flags = 0
data = length 620, hash 7A7AE07C
sample 2:
time = 367000 time = 367000
flags = 0 flags = 0
data = length 405, hash 5CC7F4E7 data = length 405, hash 5CC7F4E7
sample 3: sample 1:
time = 500000 time = 500000
flags = 0 flags = 0
data = length 4852, hash 9DB6979D data = length 4852, hash 9DB6979D
sample 4: sample 2:
time = 467000 time = 467000
flags = 0 flags = 0
data = length 547, hash E31A6979 data = length 547, hash E31A6979
sample 5: sample 3:
time = 434000 time = 434000
flags = 0 flags = 0
data = length 570, hash FEC40D00 data = length 570, hash FEC40D00
sample 6: sample 4:
time = 634000 time = 634000
flags = 0 flags = 0
data = length 5525, hash 7C478F7E data = length 5525, hash 7C478F7E
sample 7: sample 5:
time = 567000 time = 567000
flags = 0 flags = 0
data = length 1082, hash DA07059A data = length 1082, hash DA07059A
sample 8: sample 6:
time = 534000 time = 534000
flags = 0 flags = 0
data = length 807, hash 93478E6B data = length 807, hash 93478E6B
sample 9: sample 7:
time = 600000 time = 600000
flags = 0 flags = 0
data = length 744, hash 9A8E6026 data = length 744, hash 9A8E6026
sample 10: sample 8:
time = 767000 time = 767000
flags = 0 flags = 0
data = length 4732, hash C73B23C0 data = length 4732, hash C73B23C0
sample 11: sample 9:
time = 700000 time = 700000
flags = 0 flags = 0
data = length 1004, hash 8A19A228 data = length 1004, hash 8A19A228
sample 12: sample 10:
time = 667000 time = 667000
flags = 0 flags = 0
data = length 794, hash 8126022C data = length 794, hash 8126022C
sample 13: sample 11:
time = 734000 time = 734000
flags = 0 flags = 0
data = length 645, hash F08300E5 data = length 645, hash F08300E5
sample 14: sample 12:
time = 900000 time = 900000
flags = 0 flags = 0
data = length 2684, hash 727FE378 data = length 2684, hash 727FE378
sample 15: sample 13:
time = 834000 time = 834000
flags = 0 flags = 0
data = length 787, hash 419A7821 data = length 787, hash 419A7821
sample 16: sample 14:
time = 800000 time = 800000
flags = 0 flags = 0
data = length 649, hash 5C159346 data = length 649, hash 5C159346
sample 17: sample 15:
time = 867000 time = 867000
flags = 0 flags = 0
data = length 509, hash F912D655 data = length 509, hash F912D655
sample 18: sample 16:
time = 1034000 time = 1034000
flags = 0 flags = 0
data = length 1226, hash 29815C21 data = length 1226, hash 29815C21
sample 19: sample 17:
time = 967000 time = 967000
flags = 0 flags = 0
data = length 898, hash D997AD0A data = length 898, hash D997AD0A
sample 20: sample 18:
time = 934000 time = 934000
flags = 0 flags = 0
data = length 476, hash A0423645 data = length 476, hash A0423645
sample 21: sample 19:
time = 1000000 time = 1000000
flags = 0 flags = 0
data = length 486, hash DDF32CBB data = length 486, hash DDF32CBB

View File

@ -1,6 +1,6 @@
seekMap: seekMap:
isSeekable = true isSeekable = true
duration = 1072000 duration = 1104000
getPosition(0) = [[timeUs=67000, position=5576]] getPosition(0) = [[timeUs=67000, position=5576]]
numberOfTracks = 2 numberOfTracks = 2
track 1: track 1:
@ -27,49 +27,41 @@ track 1:
initializationData: initializationData:
data = length 30, hash F6F3D010 data = length 30, hash F6F3D010
data = length 10, hash 7A0D0F2B data = length 10, hash 7A0D0F2B
total output bytes = 10158 total output bytes = 8360
sample count = 11 sample count = 9
sample 0: sample 0:
time = 700000
flags = 0
data = length 1004, hash 8A19A228
sample 1:
time = 667000
flags = 0
data = length 794, hash 8126022C
sample 2:
time = 734000 time = 734000
flags = 0 flags = 0
data = length 645, hash F08300E5 data = length 645, hash F08300E5
sample 3: sample 1:
time = 900000 time = 900000
flags = 0 flags = 0
data = length 2684, hash 727FE378 data = length 2684, hash 727FE378
sample 4: sample 2:
time = 834000 time = 834000
flags = 0 flags = 0
data = length 787, hash 419A7821 data = length 787, hash 419A7821
sample 5: sample 3:
time = 800000 time = 800000
flags = 0 flags = 0
data = length 649, hash 5C159346 data = length 649, hash 5C159346
sample 6: sample 4:
time = 867000 time = 867000
flags = 0 flags = 0
data = length 509, hash F912D655 data = length 509, hash F912D655
sample 7: sample 5:
time = 1034000 time = 1034000
flags = 0 flags = 0
data = length 1226, hash 29815C21 data = length 1226, hash 29815C21
sample 8: sample 6:
time = 967000 time = 967000
flags = 0 flags = 0
data = length 898, hash D997AD0A data = length 898, hash D997AD0A
sample 9: sample 7:
time = 934000 time = 934000
flags = 0 flags = 0
data = length 476, hash A0423645 data = length 476, hash A0423645
sample 10: sample 8:
time = 1000000 time = 1000000
flags = 0 flags = 0
data = length 486, hash DDF32CBB data = length 486, hash DDF32CBB

View File

@ -1,6 +1,6 @@
seekMap: seekMap:
isSeekable = true isSeekable = true
duration = 1072000 duration = 1104000
getPosition(0) = [[timeUs=67000, position=5576]] getPosition(0) = [[timeUs=67000, position=5576]]
numberOfTracks = 2 numberOfTracks = 2
track 1: track 1:

View File

@ -0,0 +1,357 @@
seekMap:
isSeekable = true
duration = 1024000
getPosition(0) = [[timeUs=0, position=2192]]
numberOfTracks = 2
track 0:
format:
bitrate = -1
id = 1
containerMimeType = null
sampleMimeType = video/avc
maxInputSize = 36722
width = 1080
height = 720
frameRate = 29.970028
rotationDegrees = 0
pixelWidthHeightRatio = 1.0
channelCount = -1
sampleRate = -1
pcmEncoding = -1
encoderDelay = 0
encoderPadding = 0
subsampleOffsetUs = 9223372036854775807
selectionFlags = 0
language = null
drmInitData = -
initializationData:
data = length 29, hash 4746B5D9
data = length 10, hash 7A0D0F2B
total output bytes = 89876
sample count = 30
sample 0:
time = 0
flags = 1
data = length 36692, hash D216076E
sample 1:
time = 66733
flags = 0
data = length 5312, hash D45D3CA0
sample 2:
time = 33366
flags = 0
data = length 599, hash 1BE7812D
sample 3:
time = 200200
flags = 0
data = length 7735, hash 4490F110
sample 4:
time = 133466
flags = 0
data = length 987, hash 560B5036
sample 5:
time = 100100
flags = 0
data = length 673, hash ED7CD8C7
sample 6:
time = 166833
flags = 0
data = length 523, hash 3020DF50
sample 7:
time = 333666
flags = 0
data = length 6061, hash 736C72B2
sample 8:
time = 266933
flags = 0
data = length 992, hash FE132F23
sample 9:
time = 233566
flags = 0
data = length 623, hash 5B2C1816
sample 10:
time = 300300
flags = 0
data = length 421, hash 742E69C1
sample 11:
time = 433766
flags = 0
data = length 4899, hash F72F86A1
sample 12:
time = 400400
flags = 0
data = length 568, hash 519A8E50
sample 13:
time = 367033
flags = 0
data = length 620, hash 3990AA39
sample 14:
time = 567233
flags = 0
data = length 5450, hash F06EC4AA
sample 15:
time = 500500
flags = 0
data = length 1051, hash 92DFA63A
sample 16:
time = 467133
flags = 0
data = length 874, hash 69587FB4
sample 17:
time = 533866
flags = 0
data = length 781, hash 36BE495B
sample 18:
time = 700700
flags = 0
data = length 4725, hash AC0C8CD3
sample 19:
time = 633966
flags = 0
data = length 1022, hash 5D8BFF34
sample 20:
time = 600600
flags = 0
data = length 790, hash 99413A99
sample 21:
time = 667333
flags = 0
data = length 610, hash 5E129290
sample 22:
time = 834166
flags = 0
data = length 2751, hash 769974CB
sample 23:
time = 767433
flags = 0
data = length 745, hash B78A477A
sample 24:
time = 734066
flags = 0
data = length 621, hash CF741E7A
sample 25:
time = 800800
flags = 0
data = length 505, hash 1DB4894E
sample 26:
time = 967633
flags = 0
data = length 1268, hash C15348DC
sample 27:
time = 900900
flags = 0
data = length 880, hash C2DE85D0
sample 28:
time = 867533
flags = 0
data = length 530, hash C98BC6A8
sample 29:
time = 934266
flags = 536870912
data = length 568, hash 4FE5C8EA
track 1:
format:
bitrate = -1
id = 2
containerMimeType = null
sampleMimeType = audio/mp4a-latm
maxInputSize = 294
width = -1
height = -1
frameRate = -1.0
rotationDegrees = 0
pixelWidthHeightRatio = 1.0
channelCount = 1
sampleRate = 44100
pcmEncoding = -1
encoderDelay = 0
encoderPadding = 0
subsampleOffsetUs = 9223372036854775807
selectionFlags = 0
language = und
drmInitData = -
initializationData:
data = length 2, hash 5F7
total output bytes = 9529
sample count = 45
sample 0:
time = 44000
flags = 1
data = length 23, hash 47DE9131
sample 1:
time = 67219
flags = 1
data = length 6, hash 31EC5206
sample 2:
time = 90439
flags = 1
data = length 148, hash 894A176B
sample 3:
time = 113659
flags = 1
data = length 189, hash CEF235A1
sample 4:
time = 136879
flags = 1
data = length 205, hash BBF5F7B0
sample 5:
time = 160099
flags = 1
data = length 210, hash F278B193
sample 6:
time = 183319
flags = 1
data = length 210, hash 82DA1589
sample 7:
time = 206539
flags = 1
data = length 207, hash 5BE231DF
sample 8:
time = 229759
flags = 1
data = length 225, hash 18819EE1
sample 9:
time = 252979
flags = 1
data = length 215, hash CA7FA67B
sample 10:
time = 276199
flags = 1
data = length 211, hash 581A1C18
sample 11:
time = 299419
flags = 1
data = length 216, hash ADB88187
sample 12:
time = 322639
flags = 1
data = length 229, hash 2E8BA4DC
sample 13:
time = 345859
flags = 1
data = length 232, hash 22F0C510
sample 14:
time = 369079
flags = 1
data = length 235, hash 867AD0DC
sample 15:
time = 392299
flags = 1
data = length 231, hash 84E823A8
sample 16:
time = 415519
flags = 1
data = length 226, hash 1BEF3A95
sample 17:
time = 438739
flags = 1
data = length 216, hash EAA345AE
sample 18:
time = 461959
flags = 1
data = length 229, hash 6957411F
sample 19:
time = 485179
flags = 1
data = length 219, hash 41275022
sample 20:
time = 508399
flags = 1
data = length 241, hash 6495DF96
sample 21:
time = 531619
flags = 1
data = length 228, hash 63D95906
sample 22:
time = 554839
flags = 1
data = length 238, hash 34F676F9
sample 23:
time = 578058
flags = 1
data = length 234, hash E5CBC045
sample 24:
time = 601278
flags = 1
data = length 231, hash 5FC43661
sample 25:
time = 624498
flags = 1
data = length 217, hash 682708ED
sample 26:
time = 647718
flags = 1
data = length 239, hash D43780FC
sample 27:
time = 670938
flags = 1
data = length 243, hash C5E17980
sample 28:
time = 694158
flags = 1
data = length 231, hash AC5837BA
sample 29:
time = 717378
flags = 1
data = length 230, hash 169EE895
sample 30:
time = 740598
flags = 1
data = length 238, hash C48FF3F1
sample 31:
time = 763818
flags = 1
data = length 225, hash 531E4599
sample 32:
time = 787038
flags = 1
data = length 232, hash CB3C6B8D
sample 33:
time = 810258
flags = 1
data = length 243, hash F8C94C7
sample 34:
time = 833478
flags = 1
data = length 232, hash A646A7D0
sample 35:
time = 856698
flags = 1
data = length 237, hash E8B787A5
sample 36:
time = 879918
flags = 1
data = length 228, hash 3FA7A29F
sample 37:
time = 903138
flags = 1
data = length 235, hash B9B33B0A
sample 38:
time = 926358
flags = 1
data = length 264, hash 71A4869E
sample 39:
time = 949578
flags = 1
data = length 257, hash D049B54C
sample 40:
time = 972798
flags = 1
data = length 227, hash 66757231
sample 41:
time = 996018
flags = 1
data = length 227, hash BD374F1B
sample 42:
time = 1019238
flags = 1
data = length 235, hash 999477F6
sample 43:
time = 1042458
flags = 1
data = length 229, hash FFF98DF0
sample 44:
time = 1065678
flags = 536870913
data = length 6, hash 31B22286
tracksEnded = true

View File

@ -0,0 +1,309 @@
seekMap:
isSeekable = true
duration = 1024000
getPosition(0) = [[timeUs=0, position=2192]]
numberOfTracks = 2
track 0:
format:
bitrate = -1
id = 1
containerMimeType = null
sampleMimeType = video/avc
maxInputSize = 36722
width = 1080
height = 720
frameRate = 29.970028
rotationDegrees = 0
pixelWidthHeightRatio = 1.0
channelCount = -1
sampleRate = -1
pcmEncoding = -1
encoderDelay = 0
encoderPadding = 0
subsampleOffsetUs = 9223372036854775807
selectionFlags = 0
language = null
drmInitData = -
initializationData:
data = length 29, hash 4746B5D9
data = length 10, hash 7A0D0F2B
total output bytes = 89876
sample count = 30
sample 0:
time = 0
flags = 1
data = length 36692, hash D216076E
sample 1:
time = 66733
flags = 0
data = length 5312, hash D45D3CA0
sample 2:
time = 33366
flags = 0
data = length 599, hash 1BE7812D
sample 3:
time = 200200
flags = 0
data = length 7735, hash 4490F110
sample 4:
time = 133466
flags = 0
data = length 987, hash 560B5036
sample 5:
time = 100100
flags = 0
data = length 673, hash ED7CD8C7
sample 6:
time = 166833
flags = 0
data = length 523, hash 3020DF50
sample 7:
time = 333666
flags = 0
data = length 6061, hash 736C72B2
sample 8:
time = 266933
flags = 0
data = length 992, hash FE132F23
sample 9:
time = 233566
flags = 0
data = length 623, hash 5B2C1816
sample 10:
time = 300300
flags = 0
data = length 421, hash 742E69C1
sample 11:
time = 433766
flags = 0
data = length 4899, hash F72F86A1
sample 12:
time = 400400
flags = 0
data = length 568, hash 519A8E50
sample 13:
time = 367033
flags = 0
data = length 620, hash 3990AA39
sample 14:
time = 567233
flags = 0
data = length 5450, hash F06EC4AA
sample 15:
time = 500500
flags = 0
data = length 1051, hash 92DFA63A
sample 16:
time = 467133
flags = 0
data = length 874, hash 69587FB4
sample 17:
time = 533866
flags = 0
data = length 781, hash 36BE495B
sample 18:
time = 700700
flags = 0
data = length 4725, hash AC0C8CD3
sample 19:
time = 633966
flags = 0
data = length 1022, hash 5D8BFF34
sample 20:
time = 600600
flags = 0
data = length 790, hash 99413A99
sample 21:
time = 667333
flags = 0
data = length 610, hash 5E129290
sample 22:
time = 834166
flags = 0
data = length 2751, hash 769974CB
sample 23:
time = 767433
flags = 0
data = length 745, hash B78A477A
sample 24:
time = 734066
flags = 0
data = length 621, hash CF741E7A
sample 25:
time = 800800
flags = 0
data = length 505, hash 1DB4894E
sample 26:
time = 967633
flags = 0
data = length 1268, hash C15348DC
sample 27:
time = 900900
flags = 0
data = length 880, hash C2DE85D0
sample 28:
time = 867533
flags = 0
data = length 530, hash C98BC6A8
sample 29:
time = 934266
flags = 536870912
data = length 568, hash 4FE5C8EA
track 1:
format:
bitrate = -1
id = 2
containerMimeType = null
sampleMimeType = audio/mp4a-latm
maxInputSize = 294
width = -1
height = -1
frameRate = -1.0
rotationDegrees = 0
pixelWidthHeightRatio = 1.0
channelCount = 1
sampleRate = 44100
pcmEncoding = -1
encoderDelay = 0
encoderPadding = 0
subsampleOffsetUs = 9223372036854775807
selectionFlags = 0
language = und
drmInitData = -
initializationData:
data = length 2, hash 5F7
total output bytes = 7464
sample count = 33
sample 0:
time = 322639
flags = 1
data = length 229, hash 2E8BA4DC
sample 1:
time = 345859
flags = 1
data = length 232, hash 22F0C510
sample 2:
time = 369079
flags = 1
data = length 235, hash 867AD0DC
sample 3:
time = 392299
flags = 1
data = length 231, hash 84E823A8
sample 4:
time = 415519
flags = 1
data = length 226, hash 1BEF3A95
sample 5:
time = 438739
flags = 1
data = length 216, hash EAA345AE
sample 6:
time = 461959
flags = 1
data = length 229, hash 6957411F
sample 7:
time = 485179
flags = 1
data = length 219, hash 41275022
sample 8:
time = 508399
flags = 1
data = length 241, hash 6495DF96
sample 9:
time = 531619
flags = 1
data = length 228, hash 63D95906
sample 10:
time = 554839
flags = 1
data = length 238, hash 34F676F9
sample 11:
time = 578058
flags = 1
data = length 234, hash E5CBC045
sample 12:
time = 601278
flags = 1
data = length 231, hash 5FC43661
sample 13:
time = 624498
flags = 1
data = length 217, hash 682708ED
sample 14:
time = 647718
flags = 1
data = length 239, hash D43780FC
sample 15:
time = 670938
flags = 1
data = length 243, hash C5E17980
sample 16:
time = 694158
flags = 1
data = length 231, hash AC5837BA
sample 17:
time = 717378
flags = 1
data = length 230, hash 169EE895
sample 18:
time = 740598
flags = 1
data = length 238, hash C48FF3F1
sample 19:
time = 763818
flags = 1
data = length 225, hash 531E4599
sample 20:
time = 787038
flags = 1
data = length 232, hash CB3C6B8D
sample 21:
time = 810258
flags = 1
data = length 243, hash F8C94C7
sample 22:
time = 833478
flags = 1
data = length 232, hash A646A7D0
sample 23:
time = 856698
flags = 1
data = length 237, hash E8B787A5
sample 24:
time = 879918
flags = 1
data = length 228, hash 3FA7A29F
sample 25:
time = 903138
flags = 1
data = length 235, hash B9B33B0A
sample 26:
time = 926358
flags = 1
data = length 264, hash 71A4869E
sample 27:
time = 949578
flags = 1
data = length 257, hash D049B54C
sample 28:
time = 972798
flags = 1
data = length 227, hash 66757231
sample 29:
time = 996018
flags = 1
data = length 227, hash BD374F1B
sample 30:
time = 1019238
flags = 1
data = length 235, hash 999477F6
sample 31:
time = 1042458
flags = 1
data = length 229, hash FFF98DF0
sample 32:
time = 1065678
flags = 536870913
data = length 6, hash 31B22286
tracksEnded = true

View File

@ -0,0 +1,249 @@
seekMap:
isSeekable = true
duration = 1024000
getPosition(0) = [[timeUs=0, position=2192]]
numberOfTracks = 2
track 0:
format:
bitrate = -1
id = 1
containerMimeType = null
sampleMimeType = video/avc
maxInputSize = 36722
width = 1080
height = 720
frameRate = 29.970028
rotationDegrees = 0
pixelWidthHeightRatio = 1.0
channelCount = -1
sampleRate = -1
pcmEncoding = -1
encoderDelay = 0
encoderPadding = 0
subsampleOffsetUs = 9223372036854775807
selectionFlags = 0
language = null
drmInitData = -
initializationData:
data = length 29, hash 4746B5D9
data = length 10, hash 7A0D0F2B
total output bytes = 89876
sample count = 30
sample 0:
time = 0
flags = 1
data = length 36692, hash D216076E
sample 1:
time = 66733
flags = 0
data = length 5312, hash D45D3CA0
sample 2:
time = 33366
flags = 0
data = length 599, hash 1BE7812D
sample 3:
time = 200200
flags = 0
data = length 7735, hash 4490F110
sample 4:
time = 133466
flags = 0
data = length 987, hash 560B5036
sample 5:
time = 100100
flags = 0
data = length 673, hash ED7CD8C7
sample 6:
time = 166833
flags = 0
data = length 523, hash 3020DF50
sample 7:
time = 333666
flags = 0
data = length 6061, hash 736C72B2
sample 8:
time = 266933
flags = 0
data = length 992, hash FE132F23
sample 9:
time = 233566
flags = 0
data = length 623, hash 5B2C1816
sample 10:
time = 300300
flags = 0
data = length 421, hash 742E69C1
sample 11:
time = 433766
flags = 0
data = length 4899, hash F72F86A1
sample 12:
time = 400400
flags = 0
data = length 568, hash 519A8E50
sample 13:
time = 367033
flags = 0
data = length 620, hash 3990AA39
sample 14:
time = 567233
flags = 0
data = length 5450, hash F06EC4AA
sample 15:
time = 500500
flags = 0
data = length 1051, hash 92DFA63A
sample 16:
time = 467133
flags = 0
data = length 874, hash 69587FB4
sample 17:
time = 533866
flags = 0
data = length 781, hash 36BE495B
sample 18:
time = 700700
flags = 0
data = length 4725, hash AC0C8CD3
sample 19:
time = 633966
flags = 0
data = length 1022, hash 5D8BFF34
sample 20:
time = 600600
flags = 0
data = length 790, hash 99413A99
sample 21:
time = 667333
flags = 0
data = length 610, hash 5E129290
sample 22:
time = 834166
flags = 0
data = length 2751, hash 769974CB
sample 23:
time = 767433
flags = 0
data = length 745, hash B78A477A
sample 24:
time = 734066
flags = 0
data = length 621, hash CF741E7A
sample 25:
time = 800800
flags = 0
data = length 505, hash 1DB4894E
sample 26:
time = 967633
flags = 0
data = length 1268, hash C15348DC
sample 27:
time = 900900
flags = 0
data = length 880, hash C2DE85D0
sample 28:
time = 867533
flags = 0
data = length 530, hash C98BC6A8
sample 29:
time = 934266
flags = 536870912
data = length 568, hash 4FE5C8EA
track 1:
format:
bitrate = -1
id = 2
containerMimeType = null
sampleMimeType = audio/mp4a-latm
maxInputSize = 294
width = -1
height = -1
frameRate = -1.0
rotationDegrees = 0
pixelWidthHeightRatio = 1.0
channelCount = 1
sampleRate = 44100
pcmEncoding = -1
encoderDelay = 0
encoderPadding = 0
subsampleOffsetUs = 9223372036854775807
selectionFlags = 0
language = und
drmInitData = -
initializationData:
data = length 2, hash 5F7
total output bytes = 4019
sample count = 18
sample 0:
time = 670938
flags = 1
data = length 243, hash C5E17980
sample 1:
time = 694158
flags = 1
data = length 231, hash AC5837BA
sample 2:
time = 717378
flags = 1
data = length 230, hash 169EE895
sample 3:
time = 740598
flags = 1
data = length 238, hash C48FF3F1
sample 4:
time = 763818
flags = 1
data = length 225, hash 531E4599
sample 5:
time = 787038
flags = 1
data = length 232, hash CB3C6B8D
sample 6:
time = 810258
flags = 1
data = length 243, hash F8C94C7
sample 7:
time = 833478
flags = 1
data = length 232, hash A646A7D0
sample 8:
time = 856698
flags = 1
data = length 237, hash E8B787A5
sample 9:
time = 879918
flags = 1
data = length 228, hash 3FA7A29F
sample 10:
time = 903138
flags = 1
data = length 235, hash B9B33B0A
sample 11:
time = 926358
flags = 1
data = length 264, hash 71A4869E
sample 12:
time = 949578
flags = 1
data = length 257, hash D049B54C
sample 13:
time = 972798
flags = 1
data = length 227, hash 66757231
sample 14:
time = 996018
flags = 1
data = length 227, hash BD374F1B
sample 15:
time = 1019238
flags = 1
data = length 235, hash 999477F6
sample 16:
time = 1042458
flags = 1
data = length 229, hash FFF98DF0
sample 17:
time = 1065678
flags = 536870913
data = length 6, hash 31B22286
tracksEnded = true

View File

@ -0,0 +1,189 @@
seekMap:
isSeekable = true
duration = 1024000
getPosition(0) = [[timeUs=0, position=2192]]
numberOfTracks = 2
track 0:
format:
bitrate = -1
id = 1
containerMimeType = null
sampleMimeType = video/avc
maxInputSize = 36722
width = 1080
height = 720
frameRate = 29.970028
rotationDegrees = 0
pixelWidthHeightRatio = 1.0
channelCount = -1
sampleRate = -1
pcmEncoding = -1
encoderDelay = 0
encoderPadding = 0
subsampleOffsetUs = 9223372036854775807
selectionFlags = 0
language = null
drmInitData = -
initializationData:
data = length 29, hash 4746B5D9
data = length 10, hash 7A0D0F2B
total output bytes = 89876
sample count = 30
sample 0:
time = 0
flags = 1
data = length 36692, hash D216076E
sample 1:
time = 66733
flags = 0
data = length 5312, hash D45D3CA0
sample 2:
time = 33366
flags = 0
data = length 599, hash 1BE7812D
sample 3:
time = 200200
flags = 0
data = length 7735, hash 4490F110
sample 4:
time = 133466
flags = 0
data = length 987, hash 560B5036
sample 5:
time = 100100
flags = 0
data = length 673, hash ED7CD8C7
sample 6:
time = 166833
flags = 0
data = length 523, hash 3020DF50
sample 7:
time = 333666
flags = 0
data = length 6061, hash 736C72B2
sample 8:
time = 266933
flags = 0
data = length 992, hash FE132F23
sample 9:
time = 233566
flags = 0
data = length 623, hash 5B2C1816
sample 10:
time = 300300
flags = 0
data = length 421, hash 742E69C1
sample 11:
time = 433766
flags = 0
data = length 4899, hash F72F86A1
sample 12:
time = 400400
flags = 0
data = length 568, hash 519A8E50
sample 13:
time = 367033
flags = 0
data = length 620, hash 3990AA39
sample 14:
time = 567233
flags = 0
data = length 5450, hash F06EC4AA
sample 15:
time = 500500
flags = 0
data = length 1051, hash 92DFA63A
sample 16:
time = 467133
flags = 0
data = length 874, hash 69587FB4
sample 17:
time = 533866
flags = 0
data = length 781, hash 36BE495B
sample 18:
time = 700700
flags = 0
data = length 4725, hash AC0C8CD3
sample 19:
time = 633966
flags = 0
data = length 1022, hash 5D8BFF34
sample 20:
time = 600600
flags = 0
data = length 790, hash 99413A99
sample 21:
time = 667333
flags = 0
data = length 610, hash 5E129290
sample 22:
time = 834166
flags = 0
data = length 2751, hash 769974CB
sample 23:
time = 767433
flags = 0
data = length 745, hash B78A477A
sample 24:
time = 734066
flags = 0
data = length 621, hash CF741E7A
sample 25:
time = 800800
flags = 0
data = length 505, hash 1DB4894E
sample 26:
time = 967633
flags = 0
data = length 1268, hash C15348DC
sample 27:
time = 900900
flags = 0
data = length 880, hash C2DE85D0
sample 28:
time = 867533
flags = 0
data = length 530, hash C98BC6A8
sample 29:
time = 934266
flags = 536870912
data = length 568, hash 4FE5C8EA
track 1:
format:
bitrate = -1
id = 2
containerMimeType = null
sampleMimeType = audio/mp4a-latm
maxInputSize = 294
width = -1
height = -1
frameRate = -1.0
rotationDegrees = 0
pixelWidthHeightRatio = 1.0
channelCount = 1
sampleRate = 44100
pcmEncoding = -1
encoderDelay = 0
encoderPadding = 0
subsampleOffsetUs = 9223372036854775807
selectionFlags = 0
language = und
drmInitData = -
initializationData:
data = length 2, hash 5F7
total output bytes = 470
sample count = 3
sample 0:
time = 1019238
flags = 1
data = length 235, hash 999477F6
sample 1:
time = 1042458
flags = 1
data = length 229, hash FFF98DF0
sample 2:
time = 1065678
flags = 536870913
data = length 6, hash 31B22286
tracksEnded = true

View File

@ -28,4 +28,13 @@ public final class Mp4ExtractorTest {
public void testMp4Sample() throws Exception { public void testMp4Sample() throws Exception {
ExtractorAsserts.assertBehavior(Mp4Extractor::new, "mp4/sample.mp4"); ExtractorAsserts.assertBehavior(Mp4Extractor::new, "mp4/sample.mp4");
} }
/**
* Test case for https://github.com/google/ExoPlayer/issues/6774. The sample file contains an mdat
* atom whose size indicates that it extends 8 bytes beyond the end of the file.
*/
@Test
public void testMp4SampleWithMdatTooLong() throws Exception {
ExtractorAsserts.assertBehavior(Mp4Extractor::new, "mp4/sample_mdat_too_long.mp4");
}
} }

View File

@ -248,6 +248,23 @@ public class DefaultDownloadIndexTest {
assertEqual(readDownload, download); assertEqual(readDownload, download);
} }
@Test
public void setStatesToRemoving_setsStateAndClearsFailureReason() throws Exception {
String id = "id";
DownloadBuilder downloadBuilder =
new DownloadBuilder(id)
.setState(Download.STATE_FAILED)
.setFailureReason(Download.FAILURE_REASON_UNKNOWN);
Download download = downloadBuilder.build();
downloadIndex.putDownload(download);
downloadIndex.setStatesToRemoving();
download = downloadIndex.getDownload(id);
assertThat(download.state).isEqualTo(Download.STATE_REMOVING);
assertThat(download.failureReason).isEqualTo(Download.FAILURE_REASON_NONE);
}
@Test @Test
public void setSingleDownloadStopReason_setReasonToNone() throws Exception { public void setSingleDownloadStopReason_setReasonToNone() throws Exception {
String id = "id"; String id = "id";

View File

@ -34,6 +34,8 @@ import com.google.android.exoplayer2.util.Util;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.NavigableSet; import java.util.NavigableSet;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
@ -48,20 +50,27 @@ public final class CacheDataSourceTest {
private static final int CACHE_FRAGMENT_SIZE = 3; private static final int CACHE_FRAGMENT_SIZE = 3;
private static final String DATASPEC_KEY = "dataSpecKey"; private static final String DATASPEC_KEY = "dataSpecKey";
// Test data
private Uri testDataUri; private Uri testDataUri;
private Map<String, String> httpRequestHeaders;
private DataSpec unboundedDataSpec; private DataSpec unboundedDataSpec;
private DataSpec boundedDataSpec; private DataSpec boundedDataSpec;
private DataSpec unboundedDataSpecWithKey; private DataSpec unboundedDataSpecWithKey;
private DataSpec boundedDataSpecWithKey; private DataSpec boundedDataSpecWithKey;
private String defaultCacheKey; private String defaultCacheKey;
private String customCacheKey; private String customCacheKey;
// Dependencies of SUT
private CacheKeyFactory cacheKeyFactory; private CacheKeyFactory cacheKeyFactory;
private File tempFolder; private File tempFolder;
private SimpleCache cache; private SimpleCache cache;
private FakeDataSource upstreamDataSource;
@Before @Before
public void setUp() throws Exception { public void setUp() throws Exception {
testDataUri = Uri.parse("https://www.test.com/data"); testDataUri = Uri.parse("https://www.test.com/data");
httpRequestHeaders = new HashMap<>();
httpRequestHeaders.put("Test-key", "Test-val");
unboundedDataSpec = buildDataSpec(/* unbounded= */ true, /* key= */ null); unboundedDataSpec = buildDataSpec(/* unbounded= */ true, /* key= */ null);
boundedDataSpec = buildDataSpec(/* unbounded= */ false, /* key= */ null); boundedDataSpec = buildDataSpec(/* unbounded= */ false, /* key= */ null);
unboundedDataSpecWithKey = buildDataSpec(/* unbounded= */ true, DATASPEC_KEY); unboundedDataSpecWithKey = buildDataSpec(/* unbounded= */ true, DATASPEC_KEY);
@ -69,9 +78,11 @@ public final class CacheDataSourceTest {
defaultCacheKey = CacheUtil.DEFAULT_CACHE_KEY_FACTORY.buildCacheKey(unboundedDataSpec); defaultCacheKey = CacheUtil.DEFAULT_CACHE_KEY_FACTORY.buildCacheKey(unboundedDataSpec);
customCacheKey = "customKey." + defaultCacheKey; customCacheKey = "customKey." + defaultCacheKey;
cacheKeyFactory = dataSpec -> customCacheKey; cacheKeyFactory = dataSpec -> customCacheKey;
tempFolder = tempFolder =
Util.createTempDirectory(ApplicationProvider.getApplicationContext(), "ExoPlayerTest"); Util.createTempDirectory(ApplicationProvider.getApplicationContext(), "ExoPlayerTest");
cache = new SimpleCache(tempFolder, new NoOpCacheEvictor()); cache = new SimpleCache(tempFolder, new NoOpCacheEvictor());
upstreamDataSource = new FakeDataSource();
} }
@After @After
@ -111,6 +122,19 @@ public final class CacheDataSourceTest {
assertCacheAndRead(boundedDataSpec, /* unknownLength= */ false); assertCacheAndRead(boundedDataSpec, /* unknownLength= */ false);
} }
@Test
public void testPropagatesHttpHeadersUpstream() throws Exception {
CacheDataSource cacheDataSource =
createCacheDataSource(/* setReadException= */ false, /* unknownLength= */ false);
DataSpec dataSpec = buildDataSpec(/* position= */ 2, /* length= */ 5);
cacheDataSource.open(dataSpec);
DataSpec[] upstreamDataSpecs = upstreamDataSource.getAndClearOpenedDataSpecs();
assertThat(upstreamDataSpecs).hasLength(1);
assertThat(upstreamDataSpecs[0].httpRequestHeaders).isEqualTo(this.httpRequestHeaders);
}
@Test @Test
public void testUnsatisfiableRange() throws Exception { public void testUnsatisfiableRange() throws Exception {
// Bounded request but the content length is unknown. This forces all data to be cached but not // Bounded request but the content length is unknown. This forces all data to be cached but not
@ -572,9 +596,8 @@ public final class CacheDataSourceTest {
@CacheDataSource.Flags int flags, @CacheDataSource.Flags int flags,
CacheDataSink cacheWriteDataSink, CacheDataSink cacheWriteDataSink,
CacheKeyFactory cacheKeyFactory) { CacheKeyFactory cacheKeyFactory) {
FakeDataSource upstream = new FakeDataSource();
FakeData fakeData = FakeData fakeData =
upstream upstreamDataSource
.getDataSet() .getDataSet()
.newDefaultData() .newDefaultData()
.setSimulateUnknownLength(unknownLength) .setSimulateUnknownLength(unknownLength)
@ -584,7 +607,7 @@ public final class CacheDataSourceTest {
} }
return new CacheDataSource( return new CacheDataSource(
cache, cache,
upstream, upstreamDataSource,
new FileDataSource(), new FileDataSource(),
cacheWriteDataSink, cacheWriteDataSink,
flags, flags,
@ -602,6 +625,11 @@ public final class CacheDataSourceTest {
private DataSpec buildDataSpec(long position, long length, @Nullable String key) { private DataSpec buildDataSpec(long position, long length, @Nullable String key) {
return new DataSpec( return new DataSpec(
testDataUri, position, length, key, DataSpec.FLAG_ALLOW_CACHE_FRAGMENTATION); testDataUri,
position,
length,
key,
DataSpec.FLAG_ALLOW_CACHE_FRAGMENTATION,
httpRequestHeaders);
} }
} }

View File

@ -1010,8 +1010,13 @@ public final class DashMediaSource extends BaseMediaSource {
windowDurationUs / 2); windowDurationUs / 2);
} }
} }
long windowStartTimeMs = manifest.availabilityStartTimeMs long windowStartTimeMs = C.TIME_UNSET;
+ manifest.getPeriod(0).startMs + C.usToMs(currentStartTimeUs); if (manifest.availabilityStartTimeMs != C.TIME_UNSET) {
windowStartTimeMs =
manifest.availabilityStartTimeMs
+ manifest.getPeriod(0).startMs
+ C.usToMs(currentStartTimeUs);
}
DashTimeline timeline = DashTimeline timeline =
new DashTimeline( new DashTimeline(
manifest.availabilityStartTimeMs, manifest.availabilityStartTimeMs,

View File

@ -75,7 +75,7 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper
private final TimestampAdjusterProvider timestampAdjusterProvider; private final TimestampAdjusterProvider timestampAdjusterProvider;
private final CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory; private final CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory;
private final boolean allowChunklessPreparation; private final boolean allowChunklessPreparation;
private final @HlsMetadataType int metadataType; private final @HlsMediaSource.MetadataType int metadataType;
private final boolean useSessionKeys; private final boolean useSessionKeys;
@Nullable private Callback callback; @Nullable private Callback callback;
@ -118,7 +118,7 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper
Allocator allocator, Allocator allocator,
CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory, CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory,
boolean allowChunklessPreparation, boolean allowChunklessPreparation,
@HlsMetadataType int metadataType, @HlsMediaSource.MetadataType int metadataType,
boolean useSessionKeys) { boolean useSessionKeys) {
this.extractorFactory = extractorFactory; this.extractorFactory = extractorFactory;
this.playlistTracker = playlistTracker; this.playlistTracker = playlistTracker;

View File

@ -15,8 +15,11 @@
*/ */
package com.google.android.exoplayer2.source.hls; package com.google.android.exoplayer2.source.hls;
import static java.lang.annotation.RetentionPolicy.SOURCE;
import android.net.Uri; import android.net.Uri;
import android.os.Handler; import android.os.Handler;
import androidx.annotation.IntDef;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlayerLibraryInfo; import com.google.android.exoplayer2.ExoPlayerLibraryInfo;
@ -47,6 +50,8 @@ import com.google.android.exoplayer2.upstream.LoadErrorHandlingPolicy;
import com.google.android.exoplayer2.upstream.TransferListener; import com.google.android.exoplayer2.upstream.TransferListener;
import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Assertions;
import java.io.IOException; import java.io.IOException;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.util.List; import java.util.List;
/** An HLS {@link MediaSource}. */ /** An HLS {@link MediaSource}. */
@ -57,6 +62,28 @@ public final class HlsMediaSource extends BaseMediaSource
ExoPlayerLibraryInfo.registerModule("goog.exo.hls"); ExoPlayerLibraryInfo.registerModule("goog.exo.hls");
} }
/**
* The types of metadata that can be extracted from HLS streams.
*
* <p>Allowed values:
*
* <ul>
* <li>{@link #METADATA_TYPE_ID3}
* <li>{@link #METADATA_TYPE_EMSG}
* </ul>
*
* <p>See {@link Factory#setMetadataType(int)}.
*/
@Documented
@Retention(SOURCE)
@IntDef({METADATA_TYPE_ID3, METADATA_TYPE_EMSG})
public @interface MetadataType {}
/** Type for ID3 metadata in HLS streams. */
public static final int METADATA_TYPE_ID3 = 1;
/** Type for ESMG metadata in HLS streams. */
public static final int METADATA_TYPE_EMSG = 3;
/** Factory for {@link HlsMediaSource}s. */ /** Factory for {@link HlsMediaSource}s. */
public static final class Factory implements MediaSourceFactory { public static final class Factory implements MediaSourceFactory {
@ -70,7 +97,7 @@ public final class HlsMediaSource extends BaseMediaSource
private DrmSessionManager<?> drmSessionManager; private DrmSessionManager<?> drmSessionManager;
private LoadErrorHandlingPolicy loadErrorHandlingPolicy; private LoadErrorHandlingPolicy loadErrorHandlingPolicy;
private boolean allowChunklessPreparation; private boolean allowChunklessPreparation;
@HlsMetadataType private int metadataType; @MetadataType private int metadataType;
private boolean useSessionKeys; private boolean useSessionKeys;
private boolean isCreateCalled; private boolean isCreateCalled;
@Nullable private Object tag; @Nullable private Object tag;
@ -100,7 +127,7 @@ public final class HlsMediaSource extends BaseMediaSource
drmSessionManager = DrmSessionManager.getDummyDrmSessionManager(); drmSessionManager = DrmSessionManager.getDummyDrmSessionManager();
loadErrorHandlingPolicy = new DefaultLoadErrorHandlingPolicy(); loadErrorHandlingPolicy = new DefaultLoadErrorHandlingPolicy();
compositeSequenceableLoaderFactory = new DefaultCompositeSequenceableLoaderFactory(); compositeSequenceableLoaderFactory = new DefaultCompositeSequenceableLoaderFactory();
metadataType = HlsMetadataType.ID3; metadataType = METADATA_TYPE_ID3;
} }
/** /**
@ -246,24 +273,24 @@ public final class HlsMediaSource extends BaseMediaSource
/** /**
* Sets the type of metadata to extract from the HLS source (defaults to {@link * Sets the type of metadata to extract from the HLS source (defaults to {@link
* HlsMetadataType#ID3}). * #METADATA_TYPE_ID3}).
* *
* <p>HLS supports in-band ID3 in both TS and fMP4 streams, but in the fMP4 case the data is * <p>HLS supports in-band ID3 in both TS and fMP4 streams, but in the fMP4 case the data is
* wrapped in an EMSG box [<a href="https://aomediacodec.github.io/av1-id3/">spec</a>]. * wrapped in an EMSG box [<a href="https://aomediacodec.github.io/av1-id3/">spec</a>].
* *
* <p>If this is set to {@link HlsMetadataType#ID3} then raw ID3 metadata of will be extracted * <p>If this is set to {@link #METADATA_TYPE_ID3} then raw ID3 metadata of will be extracted
* from TS sources. From fMP4 streams EMSGs containing metadata of this type (in the variant * from TS sources. From fMP4 streams EMSGs containing metadata of this type (in the variant
* stream only) will be unwrapped to expose the inner data. All other in-band metadata will be * stream only) will be unwrapped to expose the inner data. All other in-band metadata will be
* dropped. * dropped.
* *
* <p>If this is set to {@link HlsMetadataType#EMSG} then all EMSG data from the fMP4 variant * <p>If this is set to {@link #METADATA_TYPE_EMSG} then all EMSG data from the fMP4 variant
* stream will be extracted. No metadata will be extracted from TS streams, since they don't * stream will be extracted. No metadata will be extracted from TS streams, since they don't
* support EMSG. * support EMSG.
* *
* @param metadataType The type of metadata to extract. * @param metadataType The type of metadata to extract.
* @return This factory, for convenience. * @return This factory, for convenience.
*/ */
public Factory setMetadataType(@HlsMetadataType int metadataType) { public Factory setMetadataType(@MetadataType int metadataType) {
Assertions.checkState(!isCreateCalled); Assertions.checkState(!isCreateCalled);
this.metadataType = metadataType; this.metadataType = metadataType;
return this; return this;
@ -347,7 +374,7 @@ public final class HlsMediaSource extends BaseMediaSource
private final DrmSessionManager<?> drmSessionManager; private final DrmSessionManager<?> drmSessionManager;
private final LoadErrorHandlingPolicy loadErrorHandlingPolicy; private final LoadErrorHandlingPolicy loadErrorHandlingPolicy;
private final boolean allowChunklessPreparation; private final boolean allowChunklessPreparation;
private final @HlsMetadataType int metadataType; private final @MetadataType int metadataType;
private final boolean useSessionKeys; private final boolean useSessionKeys;
private final HlsPlaylistTracker playlistTracker; private final HlsPlaylistTracker playlistTracker;
@Nullable private final Object tag; @Nullable private final Object tag;
@ -363,7 +390,7 @@ public final class HlsMediaSource extends BaseMediaSource
LoadErrorHandlingPolicy loadErrorHandlingPolicy, LoadErrorHandlingPolicy loadErrorHandlingPolicy,
HlsPlaylistTracker playlistTracker, HlsPlaylistTracker playlistTracker,
boolean allowChunklessPreparation, boolean allowChunklessPreparation,
@HlsMetadataType int metadataType, @MetadataType int metadataType,
boolean useSessionKeys, boolean useSessionKeys,
@Nullable Object tag) { @Nullable Object tag) {
this.manifestUri = manifestUri; this.manifestUri = manifestUri;

View File

@ -1,38 +0,0 @@
/*
* Copyright (C) 2019 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.hls;
import static java.lang.annotation.RetentionPolicy.SOURCE;
import androidx.annotation.IntDef;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
/**
* The types of metadata that can be extracted from HLS streams.
*
* <p>See {@link HlsMediaSource.Factory#setMetadataType(int)}.
*/
@Documented
@Retention(SOURCE)
@IntDef({HlsMetadataType.ID3, HlsMetadataType.EMSG})
public @interface HlsMetadataType {
/** Type for ID3 metadata in HLS streams. */
int ID3 = 1;
/** Type for ESMG metadata in HLS streams. */
int EMSG = 3;
}

View File

@ -116,7 +116,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
private final LoadErrorHandlingPolicy loadErrorHandlingPolicy; private final LoadErrorHandlingPolicy loadErrorHandlingPolicy;
private final Loader loader; private final Loader loader;
private final EventDispatcher eventDispatcher; private final EventDispatcher eventDispatcher;
private final @HlsMetadataType int metadataType; private final @HlsMediaSource.MetadataType int metadataType;
private final HlsChunkSource.HlsChunkHolder nextChunkHolder; private final HlsChunkSource.HlsChunkHolder nextChunkHolder;
private final ArrayList<HlsMediaChunk> mediaChunks; private final ArrayList<HlsMediaChunk> mediaChunks;
private final List<HlsMediaChunk> readOnlyMediaChunks; private final List<HlsMediaChunk> readOnlyMediaChunks;
@ -190,7 +190,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
DrmSessionManager<?> drmSessionManager, DrmSessionManager<?> drmSessionManager,
LoadErrorHandlingPolicy loadErrorHandlingPolicy, LoadErrorHandlingPolicy loadErrorHandlingPolicy,
EventDispatcher eventDispatcher, EventDispatcher eventDispatcher,
@HlsMetadataType int metadataType) { @HlsMediaSource.MetadataType int metadataType) {
this.trackType = trackType; this.trackType = trackType;
this.callback = callback; this.callback = callback;
this.chunkSource = chunkSource; this.chunkSource = chunkSource;
@ -1362,14 +1362,15 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
private byte[] buffer; private byte[] buffer;
private int bufferPosition; private int bufferPosition;
public EmsgUnwrappingTrackOutput(TrackOutput delegate, @HlsMetadataType int metadataType) { public EmsgUnwrappingTrackOutput(
TrackOutput delegate, @HlsMediaSource.MetadataType int metadataType) {
this.emsgDecoder = new EventMessageDecoder(); this.emsgDecoder = new EventMessageDecoder();
this.delegate = delegate; this.delegate = delegate;
switch (metadataType) { switch (metadataType) {
case HlsMetadataType.ID3: case HlsMediaSource.METADATA_TYPE_ID3:
delegateFormat = ID3_FORMAT; delegateFormat = ID3_FORMAT;
break; break;
case HlsMetadataType.EMSG: case HlsMediaSource.METADATA_TYPE_EMSG:
delegateFormat = EMSG_FORMAT; delegateFormat = EMSG_FORMAT;
break; break;
default: default:

View File

@ -92,7 +92,7 @@ public final class HlsMediaPeriodTest {
mock(Allocator.class), mock(Allocator.class),
mock(CompositeSequenceableLoaderFactory.class), mock(CompositeSequenceableLoaderFactory.class),
/* allowChunklessPreparation =*/ true, /* allowChunklessPreparation =*/ true,
HlsMetadataType.ID3, HlsMediaSource.METADATA_TYPE_ID3,
/* useSessionKeys= */ false); /* useSessionKeys= */ false);
}; };

View File

@ -36,12 +36,15 @@ import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import androidx.annotation.ColorInt; import androidx.annotation.ColorInt;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
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;
import java.util.Collections;
import java.util.Formatter; import java.util.Formatter;
import java.util.Locale; import java.util.Locale;
import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.CopyOnWriteArraySet;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
/** /**
* A time bar that shows a current position, buffered position, duration and ad markers. * A time bar that shows a current position, buffered position, duration and ad markers.
@ -199,6 +202,7 @@ public class DefaultTimeBar extends View implements TimeBar {
private int keyCountIncrement; private int keyCountIncrement;
private long keyTimeIncrement; private long keyTimeIncrement;
private int lastCoarseScrubXPosition; private int lastCoarseScrubXPosition;
@MonotonicNonNull private Rect lastExclusionRectangle;
private boolean scrubbing; private boolean scrubbing;
private long scrubPosition; private long scrubPosition;
@ -604,6 +608,9 @@ public class DefaultTimeBar extends View implements TimeBar {
seekBounds.set(seekLeft, barY, seekRight, barY + touchTargetHeight); seekBounds.set(seekLeft, barY, seekRight, barY + touchTargetHeight);
progressBar.set(seekBounds.left + scrubberPadding, progressY, progressBar.set(seekBounds.left + scrubberPadding, progressY,
seekBounds.right - scrubberPadding, progressY + barHeight); seekBounds.right - scrubberPadding, progressY + barHeight);
if (Util.SDK_INT >= 29) {
setSystemGestureExclusionRectsV29(width, height);
}
update(); update();
} }
@ -834,6 +841,18 @@ public class DefaultTimeBar extends View implements TimeBar {
} }
} }
@RequiresApi(29)
private void setSystemGestureExclusionRectsV29(int width, int height) {
if (lastExclusionRectangle != null
&& lastExclusionRectangle.width() == width
&& lastExclusionRectangle.height() == height) {
// Allocating inside onLayout is considered a DrawAllocation lint error, so avoid if possible.
return;
}
lastExclusionRectangle = new Rect(/* left= */ 0, /* top= */ 0, width, height);
setSystemGestureExclusionRects(Collections.singletonList(lastExclusionRectangle));
}
private String getProgressText() { private String getProgressText() {
return Util.getStringForTime(formatBuilder, formatter, position); return Util.getStringForTime(formatBuilder, formatter, position);
} }

View File

@ -20,6 +20,7 @@ import static com.google.common.truth.Truth.assertWithMessage;
import android.content.Context; import android.content.Context;
import android.util.SparseArray; import android.util.SparseArray;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.extractor.ExtractorOutput; import com.google.android.exoplayer2.extractor.ExtractorOutput;
import com.google.android.exoplayer2.extractor.SeekMap; import com.google.android.exoplayer2.extractor.SeekMap;
import java.io.File; import java.io.File;
@ -67,6 +68,19 @@ public final class FakeExtractorOutput implements ExtractorOutput, Dumper.Dumpab
@Override @Override
public void seekMap(SeekMap seekMap) { public void seekMap(SeekMap seekMap) {
if (seekMap.isSeekable()) {
SeekMap.SeekPoints seekPoints = seekMap.getSeekPoints(0);
if (!seekPoints.first.equals(seekPoints.second)) {
throw new IllegalStateException("SeekMap defines two seek points for t=0");
}
long durationUs = seekMap.getDurationUs();
if (durationUs != C.TIME_UNSET) {
seekPoints = seekMap.getSeekPoints(durationUs);
if (!seekPoints.first.equals(seekPoints.second)) {
throw new IllegalStateException("SeekMap defines two seek points for t=durationUs");
}
}
}
this.seekMap = seekMap; this.seekMap = seekMap;
} }

View File

@ -93,6 +93,12 @@ public final class FakeTrackOutput implements TrackOutput, Dumper.Dumpable {
@Override @Override
public void sampleMetadata(long timeUs, @C.BufferFlags int flags, int size, int offset, public void sampleMetadata(long timeUs, @C.BufferFlags int flags, int size, int offset,
CryptoData cryptoData) { CryptoData cryptoData) {
if (format == null) {
throw new IllegalStateException("TrackOutput must receive format before sampleMetadata");
}
if (format.maxInputSize != Format.NO_VALUE && size > format.maxInputSize) {
throw new IllegalStateException("Sample size exceeds Format.maxInputSize");
}
sampleTimesUs.add(timeUs); sampleTimesUs.add(timeUs);
sampleFlags.add(flags); sampleFlags.add(flags);
sampleStartOffsets.add(sampleData.length - offset - size); sampleStartOffsets.add(sampleData.length - offset - size);