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 #
### 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) ###
* Core library:
@ -30,6 +56,10 @@
* Fix issue where player errors are thrown too early at playlist transitions
([#5407](https://github.com/google/ExoPlayer/issues/5407)).
* 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:
* Inject `DrmSessionManager` into the `MediaSources` instead of `Renderers`.
This allows each `MediaSource` in a `ConcatenatingMediaSource` to use a

View File

@ -13,8 +13,8 @@
// limitations under the License.
project.ext {
// ExoPlayer version and version code.
releaseVersion = '2.11.0'
releaseVersionCode = 2011000
releaseVersion = '2.11.1'
releaseVersionCode = 2011001
minSdkVersion = 16
appTargetSdkVersion = 29
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
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 ##
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
[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 ##
* [Troubleshooting using extensions][]
* [Javadoc][]: Classes matching `com.google.android.exoplayer2.ext.ffmpeg.*`
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

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
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 ##
* [Javadoc][]: Classes matching `com.google.android.exoplayer2.ext.flac.*`

View File

@ -462,8 +462,9 @@ bool FLACParser::getSeekPositions(int64_t timeUs,
if (sampleNumber <= targetSampleNumber) {
result[0] = (sampleNumber * 1000000LL) / sampleRate;
result[1] = firstFrameOffset + points[i - 1].stream_offset;
if (sampleNumber == targetSampleNumber || i >= length) {
// exact seek, or no following seek point.
if (sampleNumber == targetSampleNumber || i >= length ||
points[i].sample_number == -1) { // placeholder
// exact seek, or no following non-placeholder seek point
result[2] = result[0];
result[3] = result[1];
} 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
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 ##
* [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
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 ##
There are two possibilities for rendering the output `LibvpxVideoRenderer`

View File

@ -1,10 +1,24 @@
# 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
-dontnote 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);
}
-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
-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[]);
@ -61,8 +75,4 @@
# Don't warn about checkerframework and Kotlin annotations
-dontwarn org.checkerframework.**
-dontwarn kotlin.annotations.jvm.**
# Some members of this class are being accessed from native methods. Keep them unobfuscated.
-keep class com.google.android.exoplayer2.ext.video.VideoDecoderOutputBuffer {
*;
}
-dontwarn javax.annotation.**

View File

@ -29,11 +29,11 @@ public final class ExoPlayerLibraryInfo {
/** 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.
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}. */
// 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.
@ -43,7 +43,7 @@ public final class ExoPlayerLibraryInfo {
* integer version 123045006 (123-045-006).
*/
// 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}

View File

@ -804,9 +804,13 @@ public class AnalyticsCollector
/** Updates the queue with a newly created media period. */
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 =
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);
mediaPeriodIdToInfo.put(mediaPeriodId, mediaPeriodInfo);
lastPlayingMediaPeriod = mediaPeriodInfoQueue.get(0);

View File

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

View File

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

View File

@ -1635,6 +1635,16 @@ public class MatroskaExtractor implements Extractor {
sizes[cuePointsSize - 1] =
(int) (segmentContentPosition + segmentContentSize - offsets[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;
cueClusterPositions = null;
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) {
// The file is invalid because the atom size is too small for its header.
return false;

View File

@ -59,9 +59,7 @@ import java.util.List;
*/
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 {
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.DatabaseProvider;
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.Util;
import java.util.ArrayList;
@ -239,6 +241,9 @@ public final class DefaultDownloadIndex implements WritableDownloadIndex {
try {
ContentValues values = new ContentValues();
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();
writableDatabase.update(tableName, values, /* whereClause= */ null, /* whereArgs= */ null);
} catch (SQLException e) {
@ -351,14 +356,22 @@ public final class DefaultDownloadIndex implements WritableDownloadIndex {
DownloadProgress downloadProgress = new DownloadProgress();
downloadProgress.bytesDownloaded = cursor.getLong(COLUMN_INDEX_BYTES_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(
request,
/* state= */ cursor.getInt(COLUMN_INDEX_STATE),
state,
/* startTimeMs= */ cursor.getLong(COLUMN_INDEX_START_TIME_MS),
/* updateTimeMs= */ cursor.getLong(COLUMN_INDEX_UPDATE_TIME_MS),
/* contentLength= */ cursor.getLong(COLUMN_INDEX_CONTENT_LENGTH),
/* stopReason= */ cursor.getInt(COLUMN_INDEX_STOP_REASON),
/* failureReason= */ cursor.getInt(COLUMN_INDEX_FAILURE_REASON),
failureReason,
downloadProgress);
}

View File

@ -130,9 +130,9 @@ public final class Download {
@FailureReason int failureReason,
DownloadProgress 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) {
Assertions.checkState(state != STATE_DOWNLOADING && state != STATE_QUEUED);
Assertions.checkArgument(state != STATE_DOWNLOADING && state != STATE_QUEUED);
}
this.request = request;
this.state = state;

View File

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

View File

@ -37,6 +37,52 @@ import java.util.regex.Pattern;
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;
@SsaAlignment public final int alignment;
@ -77,22 +123,22 @@ import java.util.regex.Pattern;
// Swallow the exception and return UNKNOWN below.
}
Log.w(TAG, "Ignoring unknown alignment: " + alignmentStr);
return SsaAlignment.UNKNOWN;
return SSA_ALIGNMENT_UNKNOWN;
}
private static boolean isValidAlignment(@SsaAlignment int alignment) {
switch (alignment) {
case SsaAlignment.BOTTOM_CENTER:
case SsaAlignment.BOTTOM_LEFT:
case SsaAlignment.BOTTOM_RIGHT:
case SsaAlignment.MIDDLE_CENTER:
case SsaAlignment.MIDDLE_LEFT:
case SsaAlignment.MIDDLE_RIGHT:
case SsaAlignment.TOP_CENTER:
case SsaAlignment.TOP_LEFT:
case SsaAlignment.TOP_RIGHT:
case SSA_ALIGNMENT_BOTTOM_CENTER:
case SSA_ALIGNMENT_BOTTOM_LEFT:
case SSA_ALIGNMENT_BOTTOM_RIGHT:
case SSA_ALIGNMENT_MIDDLE_CENTER:
case SSA_ALIGNMENT_MIDDLE_LEFT:
case SSA_ALIGNMENT_MIDDLE_RIGHT:
case SSA_ALIGNMENT_TOP_CENTER:
case SSA_ALIGNMENT_TOP_LEFT:
case SSA_ALIGNMENT_TOP_RIGHT:
return true;
case SsaAlignment.UNKNOWN:
case SSA_ALIGNMENT_UNKNOWN:
default:
return false;
}
@ -177,7 +223,7 @@ import java.util.regex.Pattern;
}
public static Overrides parseFromDialogue(String text) {
@SsaAlignment int alignment = SsaAlignment.UNKNOWN;
@SsaAlignment int alignment = SSA_ALIGNMENT_UNKNOWN;
PointF position = null;
Matcher matcher = BRACES_PATTERN.matcher(text);
while (matcher.find()) {
@ -192,7 +238,7 @@ import java.util.regex.Pattern;
}
try {
@SsaAlignment int parsedAlignment = parseAlignmentOverride(braceContents);
if (parsedAlignment != SsaAlignment.UNKNOWN) {
if (parsedAlignment != SSA_ALIGNMENT_UNKNOWN) {
alignment = parsedAlignment;
}
} catch (RuntimeException e) {
@ -249,36 +295,7 @@ import java.util.regex.Pattern;
@SsaAlignment
private static int parseAlignmentOverride(String 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
@Retention(SOURCE)
@IntDef({
TextAlignment.START,
TextAlignment.CENTER,
TextAlignment.END,
TextAlignment.LEFT,
TextAlignment.RIGHT
TEXT_ALIGNMENT_START,
TEXT_ALIGNMENT_CENTER,
TEXT_ALIGNMENT_END,
TEXT_ALIGNMENT_LEFT,
TEXT_ALIGNMENT_RIGHT
})
public @interface TextAlignment {
/**
* See WebVTT's <a
* href="https://www.w3.org/TR/webvtt1/#webvtt-cue-start-alignment">align:start</a>.
*/
int START = 1;
/**
* See WebVTT's <a
* href="https://www.w3.org/TR/webvtt1/#webvtt-cue-center-alignment">align:center</a>.
*/
int CENTER = 2;
/**
* See WebVTT's <a
* href="https://www.w3.org/TR/webvtt1/#webvtt-cue-end-alignment">align:end</a>.
*/
int END = 3;
/**
* See WebVTT's <a
* href="https://www.w3.org/TR/webvtt1/#webvtt-cue-left-alignment">align:left</a>.
*/
int LEFT = 4;
/**
* See WebVTT's <a
* href="https://www.w3.org/TR/webvtt1/#webvtt-cue-right-alignment">align:right</a>.
*/
int RIGHT = 5;
}
public @interface TextAlignment {}
/**
* See WebVTT's <a
* href="https://www.w3.org/TR/webvtt1/#webvtt-cue-start-alignment">align:start</a>.
*/
public static final int TEXT_ALIGNMENT_START = 1;
/**
* See WebVTT's <a
* href="https://www.w3.org/TR/webvtt1/#webvtt-cue-center-alignment">align:center</a>.
*/
public static final int TEXT_ALIGNMENT_CENTER = 2;
/**
* See WebVTT's <a href="https://www.w3.org/TR/webvtt1/#webvtt-cue-end-alignment">align:end</a>.
*/
public static final int TEXT_ALIGNMENT_END = 3;
/**
* See WebVTT's <a href="https://www.w3.org/TR/webvtt1/#webvtt-cue-left-alignment">align:left</a>.
*/
public static final int TEXT_ALIGNMENT_LEFT = 4;
/**
* See WebVTT's <a
* href="https://www.w3.org/TR/webvtt1/#webvtt-cue-right-alignment">align:right</a>.
*/
public static final int TEXT_ALIGNMENT_RIGHT = 5;
private static final String TAG = "WebvttCueBuilder";
@ -140,7 +141,7 @@ public final class WebvttCue extends Cue {
endTime = 0;
text = null;
// Default: https://www.w3.org/TR/webvtt1/#webvtt-cue-text-alignment
textAlignment = TextAlignment.CENTER;
textAlignment = TEXT_ALIGNMENT_CENTER;
line = Cue.DIMEN_UNSET;
// Defaults to NUMBER (true): https://www.w3.org/TR/webvtt1/#webvtt-cue-snap-to-lines-flag
lineType = Cue.LINE_TYPE_NUMBER;
@ -251,13 +252,13 @@ public final class WebvttCue extends Cue {
// https://www.w3.org/TR/webvtt1/#webvtt-cue-position
private static float derivePosition(@TextAlignment int textAlignment) {
switch (textAlignment) {
case TextAlignment.LEFT:
case TEXT_ALIGNMENT_LEFT:
return 0.0f;
case TextAlignment.RIGHT:
case TEXT_ALIGNMENT_RIGHT:
return 1.0f;
case TextAlignment.START:
case TextAlignment.CENTER:
case TextAlignment.END:
case TEXT_ALIGNMENT_START:
case TEXT_ALIGNMENT_CENTER:
case TEXT_ALIGNMENT_END:
default:
return DEFAULT_POSITION;
}
@ -267,13 +268,13 @@ public final class WebvttCue extends Cue {
@AnchorType
private static int derivePositionAnchor(@TextAlignment int textAlignment) {
switch (textAlignment) {
case TextAlignment.LEFT:
case TextAlignment.START:
case TEXT_ALIGNMENT_LEFT:
case TEXT_ALIGNMENT_START:
return Cue.ANCHOR_TYPE_START;
case TextAlignment.RIGHT:
case TextAlignment.END:
case TEXT_ALIGNMENT_RIGHT:
case TEXT_ALIGNMENT_END:
return Cue.ANCHOR_TYPE_END;
case TextAlignment.CENTER:
case TEXT_ALIGNMENT_CENTER:
default:
return Cue.ANCHOR_TYPE_MIDDLE;
}
@ -282,13 +283,13 @@ public final class WebvttCue extends Cue {
@Nullable
private static Alignment convertTextAlignment(@TextAlignment int textAlignment) {
switch (textAlignment) {
case TextAlignment.START:
case TextAlignment.LEFT:
case TEXT_ALIGNMENT_START:
case TEXT_ALIGNMENT_LEFT:
return Alignment.ALIGN_NORMAL;
case TextAlignment.CENTER:
case TEXT_ALIGNMENT_CENTER:
return Alignment.ALIGN_CENTER;
case TextAlignment.END:
case TextAlignment.RIGHT:
case TEXT_ALIGNMENT_END:
case TEXT_ALIGNMENT_RIGHT:
return Alignment.ALIGN_OPPOSITE;
default:
Log.w(TAG, "Unknown textAlignment: " + textAlignment);

View File

@ -308,20 +308,20 @@ public final class WebvttCueParser {
private static int parseTextAlignment(String s) {
switch (s) {
case "start":
return WebvttCue.Builder.TextAlignment.START;
return WebvttCue.Builder.TEXT_ALIGNMENT_START;
case "left":
return WebvttCue.Builder.TextAlignment.LEFT;
return WebvttCue.Builder.TEXT_ALIGNMENT_LEFT;
case "center":
case "middle":
return WebvttCue.Builder.TextAlignment.CENTER;
return WebvttCue.Builder.TEXT_ALIGNMENT_CENTER;
case "end":
return WebvttCue.Builder.TextAlignment.END;
return WebvttCue.Builder.TEXT_ALIGNMENT_END;
case "right":
return WebvttCue.Builder.TextAlignment.RIGHT;
return WebvttCue.Builder.TEXT_ALIGNMENT_RIGHT;
default:
Log.w(TAG, "Invalid alignment value: " + s);
// 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
* 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
* 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>
*

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,6 +1,6 @@
seekMap:
isSeekable = true
duration = 1072000
duration = 1104000
getPosition(0) = [[timeUs=67000, position=5576]]
numberOfTracks = 2
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 {
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);
}
@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
public void setSingleDownloadStopReason_setReasonToNone() throws Exception {
String id = "id";

View File

@ -34,6 +34,8 @@ import com.google.android.exoplayer2.util.Util;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.NavigableSet;
import org.junit.After;
import org.junit.Before;
@ -48,20 +50,27 @@ public final class CacheDataSourceTest {
private static final int CACHE_FRAGMENT_SIZE = 3;
private static final String DATASPEC_KEY = "dataSpecKey";
// Test data
private Uri testDataUri;
private Map<String, String> httpRequestHeaders;
private DataSpec unboundedDataSpec;
private DataSpec boundedDataSpec;
private DataSpec unboundedDataSpecWithKey;
private DataSpec boundedDataSpecWithKey;
private String defaultCacheKey;
private String customCacheKey;
// Dependencies of SUT
private CacheKeyFactory cacheKeyFactory;
private File tempFolder;
private SimpleCache cache;
private FakeDataSource upstreamDataSource;
@Before
public void setUp() throws Exception {
testDataUri = Uri.parse("https://www.test.com/data");
httpRequestHeaders = new HashMap<>();
httpRequestHeaders.put("Test-key", "Test-val");
unboundedDataSpec = buildDataSpec(/* unbounded= */ true, /* key= */ null);
boundedDataSpec = buildDataSpec(/* unbounded= */ false, /* key= */ null);
unboundedDataSpecWithKey = buildDataSpec(/* unbounded= */ true, DATASPEC_KEY);
@ -69,9 +78,11 @@ public final class CacheDataSourceTest {
defaultCacheKey = CacheUtil.DEFAULT_CACHE_KEY_FACTORY.buildCacheKey(unboundedDataSpec);
customCacheKey = "customKey." + defaultCacheKey;
cacheKeyFactory = dataSpec -> customCacheKey;
tempFolder =
Util.createTempDirectory(ApplicationProvider.getApplicationContext(), "ExoPlayerTest");
cache = new SimpleCache(tempFolder, new NoOpCacheEvictor());
upstreamDataSource = new FakeDataSource();
}
@After
@ -111,6 +122,19 @@ public final class CacheDataSourceTest {
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
public void testUnsatisfiableRange() throws Exception {
// 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,
CacheDataSink cacheWriteDataSink,
CacheKeyFactory cacheKeyFactory) {
FakeDataSource upstream = new FakeDataSource();
FakeData fakeData =
upstream
upstreamDataSource
.getDataSet()
.newDefaultData()
.setSimulateUnknownLength(unknownLength)
@ -584,7 +607,7 @@ public final class CacheDataSourceTest {
}
return new CacheDataSource(
cache,
upstream,
upstreamDataSource,
new FileDataSource(),
cacheWriteDataSink,
flags,
@ -602,6 +625,11 @@ public final class CacheDataSourceTest {
private DataSpec buildDataSpec(long position, long length, @Nullable String key) {
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);
}
}
long windowStartTimeMs = manifest.availabilityStartTimeMs
+ manifest.getPeriod(0).startMs + C.usToMs(currentStartTimeUs);
long windowStartTimeMs = C.TIME_UNSET;
if (manifest.availabilityStartTimeMs != C.TIME_UNSET) {
windowStartTimeMs =
manifest.availabilityStartTimeMs
+ manifest.getPeriod(0).startMs
+ C.usToMs(currentStartTimeUs);
}
DashTimeline timeline =
new DashTimeline(
manifest.availabilityStartTimeMs,

View File

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

View File

@ -15,8 +15,11 @@
*/
package com.google.android.exoplayer2.source.hls;
import static java.lang.annotation.RetentionPolicy.SOURCE;
import android.net.Uri;
import android.os.Handler;
import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C;
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.util.Assertions;
import java.io.IOException;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.util.List;
/** An HLS {@link MediaSource}. */
@ -57,6 +62,28 @@ public final class HlsMediaSource extends BaseMediaSource
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. */
public static final class Factory implements MediaSourceFactory {
@ -70,7 +97,7 @@ public final class HlsMediaSource extends BaseMediaSource
private DrmSessionManager<?> drmSessionManager;
private LoadErrorHandlingPolicy loadErrorHandlingPolicy;
private boolean allowChunklessPreparation;
@HlsMetadataType private int metadataType;
@MetadataType private int metadataType;
private boolean useSessionKeys;
private boolean isCreateCalled;
@Nullable private Object tag;
@ -100,7 +127,7 @@ public final class HlsMediaSource extends BaseMediaSource
drmSessionManager = DrmSessionManager.getDummyDrmSessionManager();
loadErrorHandlingPolicy = new DefaultLoadErrorHandlingPolicy();
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
* 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
* 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
* stream only) will be unwrapped to expose the inner data. All other in-band metadata will be
* 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
* support EMSG.
*
* @param metadataType The type of metadata to extract.
* @return This factory, for convenience.
*/
public Factory setMetadataType(@HlsMetadataType int metadataType) {
public Factory setMetadataType(@MetadataType int metadataType) {
Assertions.checkState(!isCreateCalled);
this.metadataType = metadataType;
return this;
@ -347,7 +374,7 @@ public final class HlsMediaSource extends BaseMediaSource
private final DrmSessionManager<?> drmSessionManager;
private final LoadErrorHandlingPolicy loadErrorHandlingPolicy;
private final boolean allowChunklessPreparation;
private final @HlsMetadataType int metadataType;
private final @MetadataType int metadataType;
private final boolean useSessionKeys;
private final HlsPlaylistTracker playlistTracker;
@Nullable private final Object tag;
@ -363,7 +390,7 @@ public final class HlsMediaSource extends BaseMediaSource
LoadErrorHandlingPolicy loadErrorHandlingPolicy,
HlsPlaylistTracker playlistTracker,
boolean allowChunklessPreparation,
@HlsMetadataType int metadataType,
@MetadataType int metadataType,
boolean useSessionKeys,
@Nullable Object tag) {
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 Loader loader;
private final EventDispatcher eventDispatcher;
private final @HlsMetadataType int metadataType;
private final @HlsMediaSource.MetadataType int metadataType;
private final HlsChunkSource.HlsChunkHolder nextChunkHolder;
private final ArrayList<HlsMediaChunk> mediaChunks;
private final List<HlsMediaChunk> readOnlyMediaChunks;
@ -190,7 +190,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
DrmSessionManager<?> drmSessionManager,
LoadErrorHandlingPolicy loadErrorHandlingPolicy,
EventDispatcher eventDispatcher,
@HlsMetadataType int metadataType) {
@HlsMediaSource.MetadataType int metadataType) {
this.trackType = trackType;
this.callback = callback;
this.chunkSource = chunkSource;
@ -1362,14 +1362,15 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
private byte[] buffer;
private int bufferPosition;
public EmsgUnwrappingTrackOutput(TrackOutput delegate, @HlsMetadataType int metadataType) {
public EmsgUnwrappingTrackOutput(
TrackOutput delegate, @HlsMediaSource.MetadataType int metadataType) {
this.emsgDecoder = new EventMessageDecoder();
this.delegate = delegate;
switch (metadataType) {
case HlsMetadataType.ID3:
case HlsMediaSource.METADATA_TYPE_ID3:
delegateFormat = ID3_FORMAT;
break;
case HlsMetadataType.EMSG:
case HlsMediaSource.METADATA_TYPE_EMSG:
delegateFormat = EMSG_FORMAT;
break;
default:

View File

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

View File

@ -36,12 +36,15 @@ import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import androidx.annotation.ColorInt;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Util;
import java.util.Collections;
import java.util.Formatter;
import java.util.Locale;
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.
@ -199,6 +202,7 @@ public class DefaultTimeBar extends View implements TimeBar {
private int keyCountIncrement;
private long keyTimeIncrement;
private int lastCoarseScrubXPosition;
@MonotonicNonNull private Rect lastExclusionRectangle;
private boolean scrubbing;
private long scrubPosition;
@ -604,6 +608,9 @@ public class DefaultTimeBar extends View implements TimeBar {
seekBounds.set(seekLeft, barY, seekRight, barY + touchTargetHeight);
progressBar.set(seekBounds.left + scrubberPadding, progressY,
seekBounds.right - scrubberPadding, progressY + barHeight);
if (Util.SDK_INT >= 29) {
setSystemGestureExclusionRectsV29(width, height);
}
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() {
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.util.SparseArray;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.extractor.ExtractorOutput;
import com.google.android.exoplayer2.extractor.SeekMap;
import java.io.File;
@ -67,6 +68,19 @@ public final class FakeExtractorOutput implements ExtractorOutput, Dumper.Dumpab
@Override
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;
}

View File

@ -93,6 +93,12 @@ public final class FakeTrackOutput implements TrackOutput, Dumper.Dumpable {
@Override
public void sampleMetadata(long timeUs, @C.BufferFlags int flags, int size, int offset,
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);
sampleFlags.add(flags);
sampleStartOffsets.add(sampleData.length - offset - size);