From 052de3c6772a9932e7d30c217c23e89cea59b1f2 Mon Sep 17 00:00:00 2001 From: tonihei Date: Thu, 1 Mar 2018 10:32:27 +0000 Subject: [PATCH 001/157] Update ISSUE_TEMPLATE --- ISSUE_TEMPLATE | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/ISSUE_TEMPLATE b/ISSUE_TEMPLATE index e85c0c28c7..807888a447 100644 --- a/ISSUE_TEMPLATE +++ b/ISSUE_TEMPLATE @@ -24,7 +24,7 @@ Describe how the issue can be reproduced, ideally using the ExoPlayer demo app. ### Link to test content Provide a link to media that reproduces the issue. If you don't wish to post it publicly, please submit the issue, then email the link to -dev.exoplayer@gmail.com including the issue number in the subject line. +dev.exoplayer@gmail.com using a subject in the format "Issue #1234". ### Version of ExoPlayer being used Specify the absolute version number. Avoid using terms such as "latest". @@ -38,5 +38,4 @@ devices and Android versions. Capture a full bug report using "adb bugreport". Output from "adb logcat" or a log snippet is NOT sufficient. Please attach the captured bug report as a file. If you don't wish to post it publicly, please submit the issue, then email the -bug report to dev.exoplayer@gmail.com including the issue number in the subject -line. +bug report to dev.exoplayer@gmail.com using a subject in the format "Issue #1234". From 452e4debcc1529c190a73567ba84b223702a2dd7 Mon Sep 17 00:00:00 2001 From: andrewlewis Date: Mon, 26 Feb 2018 02:08:28 -0800 Subject: [PATCH 002/157] Fix ImaAdsMediaSource isTopLevelSource AdsMediaSource must be top-level. Currently the (deprecated) ImaAdsMediaSource can't be used because it prepares its contained AdsMediaSource as a child source. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=186990882 --- .../exoplayer2/ext/ima/ImaAdsMediaSource.java | 31 +++++++++++++------ 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/ImaAdsMediaSource.java b/extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/ImaAdsMediaSource.java index 981e8352e0..1899c815da 100644 --- a/extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/ImaAdsMediaSource.java +++ b/extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/ImaAdsMediaSource.java @@ -20,12 +20,12 @@ import android.support.annotation.Nullable; import android.view.ViewGroup; import com.google.android.exoplayer2.ExoPlayer; import com.google.android.exoplayer2.Timeline; -import com.google.android.exoplayer2.source.CompositeMediaSource; import com.google.android.exoplayer2.source.MediaPeriod; import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.source.ads.AdsMediaSource; import com.google.android.exoplayer2.upstream.Allocator; import com.google.android.exoplayer2.upstream.DataSource; +import java.io.IOException; /** * A {@link MediaSource} that inserts ads linearly with a provided content media source. @@ -33,10 +33,9 @@ import com.google.android.exoplayer2.upstream.DataSource; * @deprecated Use com.google.android.exoplayer2.source.ads.AdsMediaSource with ImaAdsLoader. */ @Deprecated -public final class ImaAdsMediaSource extends CompositeMediaSource { +public final class ImaAdsMediaSource implements MediaSource { private final AdsMediaSource adsMediaSource; - private Listener listener; /** * Constructs a new source that inserts ads linearly with the content specified by @@ -75,10 +74,23 @@ public final class ImaAdsMediaSource extends CompositeMediaSource { } @Override - public void prepareSource(ExoPlayer player, boolean isTopLevelSource, Listener listener) { - super.prepareSource(player, isTopLevelSource, listener); - this.listener = listener; - prepareChildSource(/* id= */ null, adsMediaSource); + public void prepareSource( + final ExoPlayer player, boolean isTopLevelSource, final Listener listener) { + adsMediaSource.prepareSource( + player, + isTopLevelSource, + new Listener() { + @Override + public void onSourceInfoRefreshed( + MediaSource source, Timeline timeline, @Nullable Object manifest) { + listener.onSourceInfoRefreshed(ImaAdsMediaSource.this, timeline, manifest); + } + }); + } + + @Override + public void maybeThrowSourceInfoRefreshError() throws IOException { + adsMediaSource.maybeThrowSourceInfoRefreshError(); } @Override @@ -92,8 +104,7 @@ public final class ImaAdsMediaSource extends CompositeMediaSource { } @Override - protected void onChildSourceInfoRefreshed( - Void id, MediaSource mediaSource, Timeline timeline, @Nullable Object manifest) { - listener.onSourceInfoRefreshed(this, timeline, manifest); + public void releaseSource() { + adsMediaSource.releaseSource(); } } From d8d3bd7b3fb30da8fe4dad6159674470ea2f5353 Mon Sep 17 00:00:00 2001 From: andrewlewis Date: Fri, 2 Mar 2018 00:46:10 -0800 Subject: [PATCH 003/157] Make the period and initial window positions match for all HLS streams Before this change, HlsMediaSource timelines had a period starting at the epoch. For VOD streams the window position in the period was the program date time. This change makes period and initial window positions match. For live streams the window position advances as segments are removed, so its position in the period is the difference between the initial program date time and the program date time of the latest playlist. This also makes it possible to insert ads in VOD HLS content with program date time, as the period and window are now aligned. Issue: #3865 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=187590948 --- RELEASENOTES.md | 12 +++++- .../exoplayer2/source/hls/HlsChunkSource.java | 14 +++++-- .../exoplayer2/source/hls/HlsMediaSource.java | 40 ++++++++++++++----- .../hls/playlist/HlsPlaylistTracker.java | 9 ++++- 4 files changed, 60 insertions(+), 15 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index f9a03390ef..f21ad7f392 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -1,5 +1,15 @@ # Release notes # +### 2.7.1 ### + +* HlsMediaSource: make HLS periods start at zero instead of the epoch. + Applications that rely on HLS timelines having a period starting at + the epoch will need to update their handling of HLS timelines. The program + date time is still available via the informational + `Timeline.Window.windowStartTimeMs` field + ([#3865](https://github.com/google/ExoPlayer/issues/3865), + [#3888](https://github.com/google/ExoPlayer/issues/3888)). + ### 2.7.0 ### * Player interface: @@ -21,7 +31,7 @@ * Add `ExoPlayer.setSeekParameters` for controlling how seek operations are performed. The `SeekParameters` class contains defaults for exact seeking and seeking to the closest sync points before, either side or after specified seek - positions. `SeekParameters` are not currently supported when playing HLS + positions. `SeekParameters` are not currently supported when playing HLS streams. * DefaultTrackSelector: * Replace `DefaultTrackSelector.Parameters` copy methods with a builder. diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsChunkSource.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsChunkSource.java index db0db47aee..d8496a63d2 100644 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsChunkSource.java +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsChunkSource.java @@ -261,9 +261,13 @@ import java.util.List; // If the playlist is too old to contain the chunk, we need to refresh it. chunkMediaSequence = mediaPlaylist.mediaSequence + mediaPlaylist.segments.size(); } else { - chunkMediaSequence = Util.binarySearchFloor(mediaPlaylist.segments, - targetPositionUs - mediaPlaylist.startTimeUs, true, - !playlistTracker.isLive() || previous == null) + mediaPlaylist.mediaSequence; + chunkMediaSequence = + Util.binarySearchFloor( + mediaPlaylist.segments, + targetPositionUs, + /* inclusive= */ true, + /* stayInBounds= */ !playlistTracker.isLive() || previous == null) + + mediaPlaylist.mediaSequence; if (chunkMediaSequence < mediaPlaylist.mediaSequence && previous != null) { // We try getting the next chunk without adapting in case that's the reason for falling // behind the live window. @@ -320,7 +324,9 @@ import java.util.List; } // Compute start time of the next chunk. - long startTimeUs = mediaPlaylist.startTimeUs + segment.relativeStartTimeUs; + long offsetFromInitialStartTimeUs = + mediaPlaylist.startTimeUs - playlistTracker.getInitialStartTimeUs(); + long startTimeUs = offsetFromInitialStartTimeUs + segment.relativeStartTimeUs; int discontinuitySequence = mediaPlaylist.discontinuitySequence + segment.relativeDiscontinuitySequence; TimestampAdjuster timestampAdjuster = timestampAdjusterProvider.getAdjuster( diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaSource.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaSource.java index 5113bef6e0..1fe0d72ea1 100644 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaSource.java +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaSource.java @@ -366,28 +366,50 @@ public final class HlsMediaSource implements MediaSource, @Override public void onPrimaryPlaylistRefreshed(HlsMediaPlaylist playlist) { SinglePeriodTimeline timeline; - long presentationStartTimeMs = playlist.hasProgramDateTime ? 0 : C.TIME_UNSET; long windowStartTimeMs = playlist.hasProgramDateTime ? C.usToMs(playlist.startTimeUs) : C.TIME_UNSET; + // For playlist types EVENT and VOD we know segments are never removed, so the presentation + // started at the same time as the window. Otherwise, we don't know the presentation start time. + long presentationStartTimeMs = + playlist.playlistType == HlsMediaPlaylist.PLAYLIST_TYPE_EVENT + || playlist.playlistType == HlsMediaPlaylist.PLAYLIST_TYPE_VOD + ? windowStartTimeMs + : C.TIME_UNSET; long windowDefaultStartPositionUs = playlist.startOffsetUs; if (playlistTracker.isLive()) { - long periodDurationUs = playlist.hasEndTag ? (playlist.startTimeUs + playlist.durationUs) - : C.TIME_UNSET; + long offsetFromInitialStartTimeUs = + playlist.startTimeUs - playlistTracker.getInitialStartTimeUs(); + long periodDurationUs = + playlist.hasEndTag ? offsetFromInitialStartTimeUs + playlist.durationUs : C.TIME_UNSET; List segments = playlist.segments; if (windowDefaultStartPositionUs == C.TIME_UNSET) { windowDefaultStartPositionUs = segments.isEmpty() ? 0 : segments.get(Math.max(0, segments.size() - 3)).relativeStartTimeUs; } - timeline = new SinglePeriodTimeline(presentationStartTimeMs, windowStartTimeMs, - periodDurationUs, playlist.durationUs, playlist.startTimeUs, windowDefaultStartPositionUs, - true, !playlist.hasEndTag); + timeline = + new SinglePeriodTimeline( + presentationStartTimeMs, + windowStartTimeMs, + periodDurationUs, + /* windowDurationUs= */ playlist.durationUs, + /* windowPositionInPeriodUs= */ offsetFromInitialStartTimeUs, + windowDefaultStartPositionUs, + /* isSeekable= */ true, + /* isDynamic= */ !playlist.hasEndTag); } else /* not live */ { if (windowDefaultStartPositionUs == C.TIME_UNSET) { windowDefaultStartPositionUs = 0; } - timeline = new SinglePeriodTimeline(presentationStartTimeMs, windowStartTimeMs, - playlist.startTimeUs + playlist.durationUs, playlist.durationUs, playlist.startTimeUs, - windowDefaultStartPositionUs, true, false); + timeline = + new SinglePeriodTimeline( + presentationStartTimeMs, + windowStartTimeMs, + /* periodDurationUs= */ playlist.durationUs, + /* windowDurationUs= */ playlist.durationUs, + /* windowPositionInPeriodUs= */ 0, + windowDefaultStartPositionUs, + /* isSeekable= */ true, + /* isDynamic= */ false); } sourceListener.onSourceInfoRefreshed(this, timeline, new HlsManifest(playlistTracker.getMasterPlaylist(), playlist)); diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistTracker.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistTracker.java index d2f16b5c27..2e565c322a 100644 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistTracker.java +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistTracker.java @@ -83,7 +83,6 @@ public final class HlsPlaylistTracker implements Loader.Callback(); playlistRefreshHandler = new Handler(); + initialStartTimeUs = C.TIME_UNSET; } /** @@ -208,6 +209,11 @@ public final class HlsPlaylistTracker implements Loader.Callback Date: Mon, 5 Mar 2018 08:00:58 -0800 Subject: [PATCH 004/157] Propagate failures handling messages on the playback thread This restores functionality that was lost when we added support for general timed message delivery. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=187866807 --- .../exoplayer2/ExoPlayerImplInternal.java | 35 ++++++++++--------- .../android/exoplayer2/PlayerMessage.java | 3 +- 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java index e05068a7b3..e004eec4d7 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java @@ -339,7 +339,7 @@ import java.util.Collections; } maybeNotifyPlaybackInfoChanged(); } catch (ExoPlaybackException e) { - Log.e(TAG, "Renderer error.", e); + Log.e(TAG, "Playback error.", e); stopInternal(/* reset= */ false, /* acknowledgeStop= */ false); eventHandler.obtainMessage(MSG_ERROR, e).sendToTarget(); maybeNotifyPlaybackInfoChanged(); @@ -805,7 +805,7 @@ import java.util.Collections; } } - private void sendMessageInternal(PlayerMessage message) { + private void sendMessageInternal(PlayerMessage message) throws ExoPlaybackException { if (message.getPositionMs() == C.TIME_UNSET) { // If no delivery time is specified, trigger immediate message delivery. sendMessageToTarget(message); @@ -824,7 +824,7 @@ import java.util.Collections; } } - private void sendMessageToTarget(PlayerMessage message) { + private void sendMessageToTarget(PlayerMessage message) throws ExoPlaybackException { if (message.getHandler().getLooper() == handler.getLooper()) { deliverMessage(message); if (playbackInfo.playbackState == Player.STATE_READY @@ -838,22 +838,24 @@ import java.util.Collections; } private void sendMessageToTargetThread(final PlayerMessage message) { - message - .getHandler() - .post( - new Runnable() { - @Override - public void run() { - deliverMessage(message); - } - }); + Handler handler = message.getHandler(); + handler.post( + new Runnable() { + @Override + public void run() { + try { + deliverMessage(message); + } catch (ExoPlaybackException e) { + Log.e(TAG, "Unexpected error delivering message on external thread.", e); + throw new RuntimeException(e); + } + } + }); } - private void deliverMessage(PlayerMessage message) { + private void deliverMessage(PlayerMessage message) throws ExoPlaybackException { try { message.getTarget().handleMessage(message.getType(), message.getPayload()); - } catch (ExoPlaybackException e) { - eventHandler.obtainMessage(MSG_ERROR, e).sendToTarget(); } finally { message.markAsProcessed(/* isDelivered= */ true); } @@ -899,7 +901,8 @@ import java.util.Collections; return true; } - private void maybeTriggerPendingMessages(long oldPeriodPositionUs, long newPeriodPositionUs) { + private void maybeTriggerPendingMessages(long oldPeriodPositionUs, long newPeriodPositionUs) + throws ExoPlaybackException { if (pendingMessages.isEmpty() || playbackInfo.periodId.isAd()) { return; } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/PlayerMessage.java b/library/core/src/main/java/com/google/android/exoplayer2/PlayerMessage.java index 1e8a89e102..408cbecaf1 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/PlayerMessage.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/PlayerMessage.java @@ -33,7 +33,8 @@ public final class PlayerMessage { * * @param messageType The message type. * @param payload The message payload. - * @throws ExoPlaybackException If an error occurred whilst handling the message. + * @throws ExoPlaybackException If an error occurred whilst handling the message. Should only be + * thrown by targets that handle messages on the playback thread. */ void handleMessage(int messageType, Object payload) throws ExoPlaybackException; } From b10f0332292e25417673f9819fa87f3bf99c7a63 Mon Sep 17 00:00:00 2001 From: olly Date: Tue, 6 Mar 2018 07:37:46 -0800 Subject: [PATCH 005/157] Derive duration from sample table if missing from track header Issue: #3926 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=188020756 --- RELEASENOTES.md | 2 + .../exoplayer2/extractor/mp4/AtomParsers.java | 79 ++++++++++++------- .../mp4/FixedSampleSizeRechunker.java | 13 ++- .../extractor/mp4/Mp4Extractor.java | 5 +- .../extractor/mp4/TrackSampleTable.java | 15 +++- 5 files changed, 79 insertions(+), 35 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index f21ad7f392..3223d6ae4c 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -9,6 +9,8 @@ `Timeline.Window.windowStartTimeMs` field ([#3865](https://github.com/google/ExoPlayer/issues/3865), [#3888](https://github.com/google/ExoPlayer/issues/3888)). +* Enable seeking in MP4 streams where duration is set incorrectly in the track + header ([#3926](https://github.com/google/ExoPlayer/issues/3926)). ### 2.7.0 ### diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/AtomParsers.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/AtomParsers.java index 588282bc9b..37cfce7c7c 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/AtomParsers.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/AtomParsers.java @@ -49,7 +49,6 @@ import java.util.List; private static final int TYPE_sbtl = Util.getIntegerCodeForString("sbtl"); private static final int TYPE_subt = Util.getIntegerCodeForString("subt"); private static final int TYPE_clcp = Util.getIntegerCodeForString("clcp"); - private static final int TYPE_cenc = Util.getIntegerCodeForString("cenc"); private static final int TYPE_meta = Util.getIntegerCodeForString("meta"); /** @@ -128,7 +127,8 @@ import java.util.List; int sampleCount = sampleSizeBox.getSampleCount(); if (sampleCount == 0) { - return new TrackSampleTable(new long[0], new int[0], 0, new long[0], new int[0]); + return new TrackSampleTable( + new long[0], new int[0], 0, new long[0], new int[0], C.TIME_UNSET); } // Entries are byte offsets of chunks. @@ -193,6 +193,7 @@ import java.util.List; long[] timestamps; int[] flags; long timestampTimeUnits = 0; + long duration; if (!isRechunkable) { offsets = new long[sampleCount]; @@ -260,6 +261,7 @@ import java.util.List; offset += sizes[i]; remainingSamplesInChunk--; } + duration = timestampTimeUnits + timestampOffset; Assertions.checkArgument(remainingSamplesAtTimestampOffset == 0); // Remove trailing ctts entries with 0-valued sample counts. @@ -294,13 +296,15 @@ import java.util.List; maximumSize = rechunkedResults.maximumSize; timestamps = rechunkedResults.timestamps; flags = rechunkedResults.flags; + duration = rechunkedResults.duration; } + long durationUs = Util.scaleLargeTimestamp(duration, C.MICROS_PER_SECOND, track.timescale); if (track.editListDurations == null || gaplessInfoHolder.hasGaplessInfo()) { // There is no edit list, or we are ignoring it as we already have gapless metadata to apply. // This implementation does not support applying both gapless metadata and an edit list. Util.scaleLargeTimestampsInPlace(timestamps, C.MICROS_PER_SECOND, track.timescale); - return new TrackSampleTable(offsets, sizes, maximumSize, timestamps, flags); + return new TrackSampleTable(offsets, sizes, maximumSize, timestamps, flags, durationUs); } // See the BMFF spec (ISO 14496-12) subsection 8.6.6. Edit lists that require prerolling from a @@ -317,10 +321,11 @@ import java.util.List; long editStartTime = track.editListMediaTimes[0]; long editEndTime = editStartTime + Util.scaleLargeTimestamp(track.editListDurations[0], track.timescale, track.movieTimescale); - long lastSampleEndTime = timestampTimeUnits; - if (timestamps[0] <= editStartTime && editStartTime < timestamps[1] - && timestamps[timestamps.length - 1] < editEndTime && editEndTime <= lastSampleEndTime) { - long paddingTimeUnits = lastSampleEndTime - editEndTime; + if (timestamps[0] <= editStartTime + && editStartTime < timestamps[1] + && timestamps[timestamps.length - 1] < editEndTime + && editEndTime <= duration) { + long paddingTimeUnits = duration - editEndTime; long encoderDelay = Util.scaleLargeTimestamp(editStartTime - timestamps[0], track.format.sampleRate, track.timescale); long encoderPadding = Util.scaleLargeTimestamp(paddingTimeUnits, @@ -330,7 +335,7 @@ import java.util.List; gaplessInfoHolder.encoderDelay = (int) encoderDelay; gaplessInfoHolder.encoderPadding = (int) encoderPadding; Util.scaleLargeTimestampsInPlace(timestamps, C.MICROS_PER_SECOND, track.timescale); - return new TrackSampleTable(offsets, sizes, maximumSize, timestamps, flags); + return new TrackSampleTable(offsets, sizes, maximumSize, timestamps, flags, durationUs); } } } @@ -339,11 +344,15 @@ import java.util.List; // The current version of the spec leaves handling of an edit with zero segment_duration in // unfragmented files open to interpretation. We handle this as a special case and include all // samples in the edit. + long editStartTime = track.editListMediaTimes[0]; for (int i = 0; i < timestamps.length; i++) { - timestamps[i] = Util.scaleLargeTimestamp(timestamps[i] - track.editListMediaTimes[0], - C.MICROS_PER_SECOND, track.timescale); + timestamps[i] = + Util.scaleLargeTimestamp( + timestamps[i] - editStartTime, C.MICROS_PER_SECOND, track.timescale); } - return new TrackSampleTable(offsets, sizes, maximumSize, timestamps, flags); + durationUs = + Util.scaleLargeTimestamp(duration - editStartTime, C.MICROS_PER_SECOND, track.timescale); + return new TrackSampleTable(offsets, sizes, maximumSize, timestamps, flags, durationUs); } // Omit any sample at the end point of an edit for audio tracks. @@ -354,13 +363,15 @@ import java.util.List; int nextSampleIndex = 0; boolean copyMetadata = false; for (int i = 0; i < track.editListDurations.length; i++) { - long mediaTime = track.editListMediaTimes[i]; - if (mediaTime != -1) { - long duration = Util.scaleLargeTimestamp(track.editListDurations[i], track.timescale, - track.movieTimescale); - int startIndex = Util.binarySearchCeil(timestamps, mediaTime, true, true); - int endIndex = Util.binarySearchCeil(timestamps, mediaTime + duration, omitClippedSample, - false); + long editMediaTime = track.editListMediaTimes[i]; + if (editMediaTime != -1) { + long editDuration = + Util.scaleLargeTimestamp( + track.editListDurations[i], track.timescale, track.movieTimescale); + int startIndex = Util.binarySearchCeil(timestamps, editMediaTime, true, true); + int endIndex = + Util.binarySearchCeil( + timestamps, editMediaTime + editDuration, omitClippedSample, false); editedSampleCount += endIndex - startIndex; copyMetadata |= nextSampleIndex != startIndex; nextSampleIndex = endIndex; @@ -377,12 +388,13 @@ import java.util.List; long pts = 0; int sampleIndex = 0; for (int i = 0; i < track.editListDurations.length; i++) { - long mediaTime = track.editListMediaTimes[i]; - long duration = track.editListDurations[i]; - if (mediaTime != -1) { - long endMediaTime = mediaTime + Util.scaleLargeTimestamp(duration, track.timescale, - track.movieTimescale); - int startIndex = Util.binarySearchCeil(timestamps, mediaTime, true, true); + long editMediaTime = track.editListMediaTimes[i]; + long editDuration = track.editListDurations[i]; + if (editMediaTime != -1) { + long endMediaTime = + editMediaTime + + Util.scaleLargeTimestamp(editDuration, track.timescale, track.movieTimescale); + int startIndex = Util.binarySearchCeil(timestamps, editMediaTime, true, true); int endIndex = Util.binarySearchCeil(timestamps, endMediaTime, omitClippedSample, false); if (copyMetadata) { int count = endIndex - startIndex; @@ -392,8 +404,9 @@ import java.util.List; } for (int j = startIndex; j < endIndex; j++) { long ptsUs = Util.scaleLargeTimestamp(pts, C.MICROS_PER_SECOND, track.movieTimescale); - long timeInSegmentUs = Util.scaleLargeTimestamp(timestamps[j] - mediaTime, - C.MICROS_PER_SECOND, track.timescale); + long timeInSegmentUs = + Util.scaleLargeTimestamp( + timestamps[j] - editMediaTime, C.MICROS_PER_SECOND, track.timescale); editedTimestamps[sampleIndex] = ptsUs + timeInSegmentUs; if (copyMetadata && editedSizes[sampleIndex] > editedMaximumSize) { editedMaximumSize = sizes[j]; @@ -401,8 +414,9 @@ import java.util.List; sampleIndex++; } } - pts += duration; + pts += editDuration; } + long editedDurationUs = Util.scaleLargeTimestamp(pts, C.MICROS_PER_SECOND, track.timescale); boolean hasSyncSample = false; for (int i = 0; i < editedFlags.length && !hasSyncSample; i++) { @@ -413,11 +427,16 @@ import java.util.List; // Such edit lists are often (although not always) broken, so we ignore it and continue. Log.w(TAG, "Ignoring edit list: Edited sample sequence does not contain a sync sample."); Util.scaleLargeTimestampsInPlace(timestamps, C.MICROS_PER_SECOND, track.timescale); - return new TrackSampleTable(offsets, sizes, maximumSize, timestamps, flags); + return new TrackSampleTable(offsets, sizes, maximumSize, timestamps, flags, durationUs); } - return new TrackSampleTable(editedOffsets, editedSizes, editedMaximumSize, editedTimestamps, - editedFlags); + return new TrackSampleTable( + editedOffsets, + editedSizes, + editedMaximumSize, + editedTimestamps, + editedFlags, + editedDurationUs); } /** diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/FixedSampleSizeRechunker.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/FixedSampleSizeRechunker.java index 5dd6c6ea9f..8336a280a2 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/FixedSampleSizeRechunker.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/FixedSampleSizeRechunker.java @@ -33,13 +33,21 @@ import com.google.android.exoplayer2.util.Util; public final int maximumSize; public final long[] timestamps; public final int[] flags; + public final long duration; - private Results(long[] offsets, int[] sizes, int maximumSize, long[] timestamps, int[] flags) { + private Results( + long[] offsets, + int[] sizes, + int maximumSize, + long[] timestamps, + int[] flags, + long duration) { this.offsets = offsets; this.sizes = sizes; this.maximumSize = maximumSize; this.timestamps = timestamps; this.flags = flags; + this.duration = duration; } } @@ -95,8 +103,9 @@ import com.google.android.exoplayer2.util.Util; newSampleIndex++; } } + long duration = timestampDeltaInTimeUnits * originalSampleIndex; - return new Results(offsets, sizes, maximumSize, timestamps, flags); + return new Results(offsets, sizes, maximumSize, timestamps, flags, duration); } } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/Mp4Extractor.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/Mp4Extractor.java index 112c2d1ba0..75bd2c16ee 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/Mp4Extractor.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/Mp4Extractor.java @@ -427,7 +427,10 @@ public final class Mp4Extractor implements Extractor, SeekMap { } mp4Track.trackOutput.format(format); - durationUs = Math.max(durationUs, track.durationUs); + durationUs = + Math.max( + durationUs, + track.durationUs != C.TIME_UNSET ? track.durationUs : trackSampleTable.durationUs); if (track.type == C.TRACK_TYPE_VIDEO && firstVideoTrackIndex == C.INDEX_UNSET) { firstVideoTrackIndex = tracks.size(); } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/TrackSampleTable.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/TrackSampleTable.java index cf479eaf3e..9f77c49664 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/TrackSampleTable.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/TrackSampleTable.java @@ -48,9 +48,19 @@ import com.google.android.exoplayer2.util.Util; * Sample flags. */ public final int[] flags; + /** + * The duration of the track sample table in microseconds, or {@link C#TIME_UNSET} if the sample + * table is empty. + */ + public final long durationUs; - public TrackSampleTable(long[] offsets, int[] sizes, int maximumSize, long[] timestampsUs, - int[] flags) { + public TrackSampleTable( + long[] offsets, + int[] sizes, + int maximumSize, + long[] timestampsUs, + int[] flags, + long durationUs) { Assertions.checkArgument(sizes.length == timestampsUs.length); Assertions.checkArgument(offsets.length == timestampsUs.length); Assertions.checkArgument(flags.length == timestampsUs.length); @@ -60,6 +70,7 @@ import com.google.android.exoplayer2.util.Util; this.maximumSize = maximumSize; this.timestampsUs = timestampsUs; this.flags = flags; + this.durationUs = durationUs; sampleCount = offsets.length; } From b7d2ae145790662f442d448063a2486fb6975b2e Mon Sep 17 00:00:00 2001 From: tonihei Date: Tue, 6 Mar 2018 08:22:41 -0800 Subject: [PATCH 006/157] Fix wrong start position in resetInternal. The start position was set to the old start position instead of the current playback position. We need to set the start position to the current playback position to ensure a repreperation with the same media starts from the last playback position. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=188025439 --- .../exoplayer2/ExoPlayerImplInternal.java | 2 +- .../android/exoplayer2/ExoPlayerTest.java | 86 ++++++++++++++++--- .../android/exoplayer2/testutil/Action.java | 30 +++++++ .../exoplayer2/testutil/ActionSchedule.java | 11 +++ 4 files changed, 116 insertions(+), 13 deletions(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java index e004eec4d7..2bbe725200 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java @@ -792,7 +792,7 @@ import java.util.Collections; resetState ? null : playbackInfo.manifest, resetPosition ? new MediaPeriodId(getFirstPeriodIndex()) : playbackInfo.periodId, // Set the start position to TIME_UNSET so that a subsequent seek to 0 isn't ignored. - resetPosition ? C.TIME_UNSET : playbackInfo.startPositionUs, + resetPosition ? C.TIME_UNSET : playbackInfo.positionUs, resetPosition ? C.TIME_UNSET : playbackInfo.contentPositionUs, playbackInfo.playbackState, /* isLoading= */ false, diff --git a/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java b/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java index 3d0cde5df8..571c411206 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java @@ -43,6 +43,7 @@ import com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinit import com.google.android.exoplayer2.testutil.FakeTrackSelection; import com.google.android.exoplayer2.testutil.FakeTrackSelector; import com.google.android.exoplayer2.upstream.Allocator; +import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -1169,10 +1170,8 @@ public final class ExoPlayerTest { Timeline timeline = new FakeTimeline(/* windowCount= */ 1); ActionSchedule actionSchedule = new ActionSchedule.Builder("testReprepareAfterPlaybackError") - .waitForPlaybackState(Player.STATE_BUFFERING) - // Cause an internal exception by seeking to an invalid position while the media source - // is still being prepared and the player doesn't immediately know it will fail. - .seek(/* windowIndex= */ 100, /* positionMs= */ 0) + .waitForPlaybackState(Player.STATE_READY) + .throwPlaybackException(ExoPlaybackException.createForSource(new IOException())) .waitForPlaybackState(Player.STATE_IDLE) .prepareSource( new FakeMediaSource(timeline, /* manifest= */ null), @@ -1203,11 +1202,8 @@ public final class ExoPlayerTest { ActionSchedule actionSchedule = new ActionSchedule.Builder("testReprepareAfterPlaybackError") .pause() - .waitForPlaybackState(Player.STATE_BUFFERING) - // Cause an internal exception by seeking to an invalid position while the media source - // is still being prepared and the player doesn't immediately know it will fail. - .seek(/* windowIndex= */ 100, /* positionMs= */ 0) - .waitForSeekProcessed() + .waitForPlaybackState(Player.STATE_READY) + .throwPlaybackException(ExoPlaybackException.createForSource(new IOException())) .waitForPlaybackState(Player.STATE_IDLE) .seek(/* positionMs= */ 50) .waitForSeekProcessed() @@ -1246,8 +1242,7 @@ public final class ExoPlayerTest { testRunner.assertTimelinesEqual(timeline, timeline); testRunner.assertTimelineChangeReasonsEqual( Player.TIMELINE_CHANGE_REASON_PREPARED, Player.TIMELINE_CHANGE_REASON_PREPARED); - testRunner.assertPositionDiscontinuityReasonsEqual( - Player.DISCONTINUITY_REASON_SEEK, Player.DISCONTINUITY_REASON_SEEK); + testRunner.assertPositionDiscontinuityReasonsEqual(Player.DISCONTINUITY_REASON_SEEK); assertThat(positionHolder[0]).isEqualTo(50); assertThat(positionHolder[1]).isEqualTo(50); } @@ -1288,6 +1283,72 @@ public final class ExoPlayerTest { testRunner.assertTimelineChangeReasonsEqual(Player.TIMELINE_CHANGE_REASON_PREPARED); } + @Test + public void testPlaybackErrorAndReprepareDoesNotResetPosition() throws Exception { + final Timeline timeline = new FakeTimeline(/* windowCount= */ 2); + final long[] positionHolder = new long[3]; + final int[] windowIndexHolder = new int[3]; + final FakeMediaSource secondMediaSource = + new FakeMediaSource(/* timeline= */ null, /* manifest= */ null); + ActionSchedule actionSchedule = + new ActionSchedule.Builder("testPlaybackErrorDoesNotResetPosition") + .pause() + .waitForPlaybackState(Player.STATE_READY) + .playUntilPosition(/* windowIndex= */ 1, /* positionMs= */ 500) + .throwPlaybackException(ExoPlaybackException.createForSource(new IOException())) + .waitForPlaybackState(Player.STATE_IDLE) + .executeRunnable( + new PlayerRunnable() { + @Override + public void run(SimpleExoPlayer player) { + // Position while in error state + positionHolder[0] = player.getCurrentPosition(); + windowIndexHolder[0] = player.getCurrentWindowIndex(); + } + }) + .prepareSource(secondMediaSource, /* resetPosition= */ false, /* resetState= */ false) + .waitForPlaybackState(Player.STATE_BUFFERING) + .executeRunnable( + new PlayerRunnable() { + @Override + public void run(SimpleExoPlayer player) { + // Position while repreparing. + positionHolder[1] = player.getCurrentPosition(); + windowIndexHolder[1] = player.getCurrentWindowIndex(); + secondMediaSource.setNewSourceInfo(timeline, /* newManifest= */ null); + } + }) + .waitForPlaybackState(Player.STATE_READY) + .executeRunnable( + new PlayerRunnable() { + @Override + public void run(SimpleExoPlayer player) { + // Position after repreparation finished. + positionHolder[2] = player.getCurrentPosition(); + windowIndexHolder[2] = player.getCurrentWindowIndex(); + } + }) + .play() + .build(); + ExoPlayerTestRunner testRunner = + new ExoPlayerTestRunner.Builder() + .setTimeline(timeline) + .setActionSchedule(actionSchedule) + .build(); + try { + testRunner.start().blockUntilActionScheduleFinished(TIMEOUT_MS).blockUntilEnded(TIMEOUT_MS); + fail(); + } catch (ExoPlaybackException e) { + // Expected exception. + } + assertThat(positionHolder[0]).isAtLeast(500L); + assertThat(positionHolder[1]).isEqualTo(positionHolder[0]); + assertThat(positionHolder[2]).isEqualTo(positionHolder[0]); + assertThat(windowIndexHolder[0]).isEqualTo(1); + assertThat(windowIndexHolder[1]).isEqualTo(1); + assertThat(windowIndexHolder[2]).isEqualTo(1); + } + @Test public void testSendMessagesDuringPreparation() throws Exception { Timeline timeline = new FakeTimeline(/* windowCount= */ 1); @@ -1421,7 +1482,8 @@ public final class ExoPlayerTest { new FakeMediaSource(timeline, null), /* resetPosition= */ false, /* resetState= */ true) - .waitForPlaybackState(Player.STATE_READY) + .waitForPlaybackState(Player.STATE_BUFFERING) + .waitForPlaybackState(Player.STATE_ENDED) .build(); new Builder() .setTimeline(timeline) diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/Action.java b/testutils/src/main/java/com/google/android/exoplayer2/testutil/Action.java index 1b3639788e..60cf6d278b 100644 --- a/testutils/src/main/java/com/google/android/exoplayer2/testutil/Action.java +++ b/testutils/src/main/java/com/google/android/exoplayer2/testutil/Action.java @@ -447,6 +447,36 @@ public abstract class Action { } + /** Throws a playback exception on the playback thread. */ + public static final class ThrowPlaybackException extends Action { + + private final ExoPlaybackException exception; + + /** + * @param tag A tag to use for logging. + * @param exception The exception to throw. + */ + public ThrowPlaybackException(String tag, ExoPlaybackException exception) { + super(tag, "ThrowPlaybackException:" + exception); + this.exception = exception; + } + + @Override + protected void doActionImpl( + SimpleExoPlayer player, MappingTrackSelector trackSelector, Surface surface) { + player + .createMessage( + new Target() { + @Override + public void handleMessage(int messageType, Object payload) + throws ExoPlaybackException { + throw exception; + } + }) + .send(); + } + } + /** * Schedules a play action to be executed, waits until the player reaches the specified position, * and pauses the player again. diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/ActionSchedule.java b/testutils/src/main/java/com/google/android/exoplayer2/testutil/ActionSchedule.java index 31c440553f..2ea8a50a84 100644 --- a/testutils/src/main/java/com/google/android/exoplayer2/testutil/ActionSchedule.java +++ b/testutils/src/main/java/com/google/android/exoplayer2/testutil/ActionSchedule.java @@ -40,6 +40,7 @@ import com.google.android.exoplayer2.testutil.Action.SetRepeatMode; import com.google.android.exoplayer2.testutil.Action.SetShuffleModeEnabled; import com.google.android.exoplayer2.testutil.Action.SetVideoSurface; import com.google.android.exoplayer2.testutil.Action.Stop; +import com.google.android.exoplayer2.testutil.Action.ThrowPlaybackException; import com.google.android.exoplayer2.testutil.Action.WaitForPlaybackState; import com.google.android.exoplayer2.testutil.Action.WaitForPositionDiscontinuity; import com.google.android.exoplayer2.testutil.Action.WaitForSeekProcessed; @@ -412,6 +413,16 @@ public final class ActionSchedule { return apply(new ExecuteRunnable(tag, runnable)); } + /** + * Schedules to throw a playback exception on the playback thread. + * + * @param exception The exception to throw. + * @return The builder, for convenience. + */ + public Builder throwPlaybackException(ExoPlaybackException exception) { + return apply(new ThrowPlaybackException(tag, exception)); + } + public ActionSchedule build() { CallbackAction callbackAction = new CallbackAction(tag); apply(callbackAction); From 0160b87c435926f0f61686c63a5ca34b59bf13ee Mon Sep 17 00:00:00 2001 From: tonihei Date: Tue, 6 Mar 2018 09:44:15 -0800 Subject: [PATCH 007/157] Fix divergence of timeline between ExoPlayerImpl and ExoPlayerImplInternal. The playback state in ExoPlayerImpl and ExoPlayerImplInternal is usually kept in sync. Only the timeline was so far not updated in the same way with the internal player using a null timeline while ExoPlayerImpl keeps the previous timeline. This change removes the need to keep a null timeline which allows to update the internal timeline in the same way as the external one. This fixes problems when retrying a failed playback multiple times. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=188034988 --- .../android/exoplayer2/ExoPlayerImpl.java | 6 +--- .../exoplayer2/ExoPlayerImplInternal.java | 35 ++++++++++--------- .../android/exoplayer2/PlaybackInfo.java | 6 ++-- .../android/exoplayer2/ExoPlayerTest.java | 32 +++++++++++++++++ 4 files changed, 55 insertions(+), 24 deletions(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java index 83bbdd1157..0e0a6e3c26 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java @@ -170,7 +170,7 @@ import java.util.concurrent.CopyOnWriteArraySet; // because it uses a callback. hasPendingPrepare = true; pendingOperationAcks++; - internalPlayer.prepare(mediaSource, resetPosition); + internalPlayer.prepare(mediaSource, resetPosition, resetState); updatePlaybackInfo( playbackInfo, /* positionDiscontinuity= */ false, @@ -567,10 +567,6 @@ import java.util.concurrent.CopyOnWriteArraySet; @DiscontinuityReason int positionDiscontinuityReason) { pendingOperationAcks -= operationAcks; if (pendingOperationAcks == 0) { - if (playbackInfo.timeline == null) { - // Replace internal null timeline with externally visible empty timeline. - playbackInfo = playbackInfo.copyWithTimeline(Timeline.EMPTY, playbackInfo.manifest); - } if (playbackInfo.startPositionUs == C.TIME_UNSET) { // Replace internal unset start position with externally visible start position of zero. playbackInfo = diff --git a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java index 2bbe725200..24bd31c62f 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java @@ -154,7 +154,7 @@ import java.util.Collections; seekParameters = SeekParameters.DEFAULT; playbackInfo = new PlaybackInfo( - /* timeline= */ null, /* startPositionUs= */ C.TIME_UNSET, emptyTrackSelectorResult); + Timeline.EMPTY, /* startPositionUs= */ C.TIME_UNSET, emptyTrackSelectorResult); playbackInfoUpdate = new PlaybackInfoUpdate(); rendererCapabilities = new RendererCapabilities[renderers.length]; for (int i = 0; i < renderers.length; i++) { @@ -176,8 +176,9 @@ import java.util.Collections; handler = clock.createHandler(internalPlaybackThread.getLooper(), this); } - public void prepare(MediaSource mediaSource, boolean resetPosition) { - handler.obtainMessage(MSG_PREPARE, resetPosition ? 1 : 0, 0, mediaSource) + public void prepare(MediaSource mediaSource, boolean resetPosition, boolean resetState) { + handler + .obtainMessage(MSG_PREPARE, resetPosition ? 1 : 0, resetState ? 1 : 0, mediaSource) .sendToTarget(); } @@ -286,7 +287,10 @@ import java.util.Collections; try { switch (msg.what) { case MSG_PREPARE: - prepareInternal((MediaSource) msg.obj, msg.arg1 != 0); + prepareInternal( + (MediaSource) msg.obj, + /* resetPosition= */ msg.arg1 != 0, + /* resetState= */ msg.arg2 != 0); break; case MSG_SET_PLAY_WHEN_READY: setPlayWhenReadyInternal(msg.arg1 != 0); @@ -387,9 +391,9 @@ import java.util.Collections; } } - private void prepareInternal(MediaSource mediaSource, boolean resetPosition) { + private void prepareInternal(MediaSource mediaSource, boolean resetPosition, boolean resetState) { pendingPrepareCount++; - resetInternal(/* releaseMediaSource= */ true, resetPosition, /* resetState= */ true); + resetInternal(/* releaseMediaSource= */ true, resetPosition, resetState); loadControl.onPrepared(); this.mediaSource = mediaSource; setState(Player.STATE_BUFFERING); @@ -576,7 +580,6 @@ import java.util.Collections; } private void seekToInternal(SeekPosition seekPosition) throws ExoPlaybackException { - Timeline timeline = playbackInfo.timeline; playbackInfoUpdate.incrementPendingOperationAcks(/* operationAcks= */ 1); MediaPeriodId periodId; @@ -607,7 +610,7 @@ import java.util.Collections; } try { - if (mediaSource == null || timeline == null) { + if (mediaSource == null || pendingPrepareCount > 0) { // Save seek position for later, as we are still waiting for a prepared source. pendingInitialSeekPosition = seekPosition; } else if (periodPositionUs == C.TIME_UNSET) { @@ -752,7 +755,7 @@ import java.util.Collections; private int getFirstPeriodIndex() { Timeline timeline = playbackInfo.timeline; - return timeline == null || timeline.isEmpty() + return timeline.isEmpty() ? 0 : timeline.getWindow(timeline.getFirstWindowIndex(shuffleModeEnabled), window) .firstPeriodIndex; @@ -779,7 +782,7 @@ import java.util.Collections; pendingInitialSeekPosition = null; } if (resetState) { - queue.setTimeline(null); + queue.setTimeline(Timeline.EMPTY); for (PendingMessageInfo pendingMessageInfo : pendingMessages) { pendingMessageInfo.message.markAsProcessed(/* isDelivered= */ false); } @@ -788,7 +791,7 @@ import java.util.Collections; } playbackInfo = new PlaybackInfo( - resetState ? null : playbackInfo.timeline, + resetState ? Timeline.EMPTY : playbackInfo.timeline, resetState ? null : playbackInfo.manifest, resetPosition ? new MediaPeriodId(getFirstPeriodIndex()) : playbackInfo.periodId, // Set the start position to TIME_UNSET so that a subsequent seek to 0 isn't ignored. @@ -809,7 +812,7 @@ import java.util.Collections; if (message.getPositionMs() == C.TIME_UNSET) { // If no delivery time is specified, trigger immediate message delivery. sendMessageToTarget(message); - } else if (playbackInfo.timeline == null) { + } else if (mediaSource == null || pendingPrepareCount > 0) { // Still waiting for initial timeline to resolve position. pendingMessages.add(new PendingMessageInfo(message)); } else { @@ -1133,7 +1136,7 @@ import java.util.Collections; playbackInfo = playbackInfo.copyWithTimeline(timeline, manifest); resolvePendingMessagePositions(); - if (oldTimeline == null) { + if (pendingPrepareCount > 0) { playbackInfoUpdate.incrementPendingOperationAcks(pendingPrepareCount); pendingPrepareCount = 0; if (pendingInitialSeekPosition != null) { @@ -1295,8 +1298,8 @@ import java.util.Collections; SeekPosition seekPosition, boolean trySubsequentPeriods) { Timeline timeline = playbackInfo.timeline; Timeline seekTimeline = seekPosition.timeline; - if (timeline == null) { - // We don't have a timeline yet, so we can't resolve the position. + if (timeline.isEmpty()) { + // We don't have a valid timeline yet, so we can't resolve the position. return null; } if (seekTimeline.isEmpty()) { @@ -1352,7 +1355,7 @@ import java.util.Collections; // The player has no media source yet. return; } - if (playbackInfo.timeline == null) { + if (pendingPrepareCount > 0) { // We're waiting to get information about periods. mediaSource.maybeThrowSourceInfoRefreshError(); return; diff --git a/library/core/src/main/java/com/google/android/exoplayer2/PlaybackInfo.java b/library/core/src/main/java/com/google/android/exoplayer2/PlaybackInfo.java index 3ff2ec9461..3a4ee0e501 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/PlaybackInfo.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/PlaybackInfo.java @@ -24,7 +24,7 @@ import com.google.android.exoplayer2.trackselection.TrackSelectorResult; */ /* package */ final class PlaybackInfo { - public final @Nullable Timeline timeline; + public final Timeline timeline; public final @Nullable Object manifest; public final MediaPeriodId periodId; public final long startPositionUs; @@ -37,7 +37,7 @@ import com.google.android.exoplayer2.trackselection.TrackSelectorResult; public volatile long bufferedPositionUs; public PlaybackInfo( - @Nullable Timeline timeline, long startPositionUs, TrackSelectorResult trackSelectorResult) { + Timeline timeline, long startPositionUs, TrackSelectorResult trackSelectorResult) { this( timeline, /* manifest= */ null, @@ -50,7 +50,7 @@ import com.google.android.exoplayer2.trackselection.TrackSelectorResult; } public PlaybackInfo( - @Nullable Timeline timeline, + Timeline timeline, @Nullable Object manifest, MediaPeriodId periodId, long startPositionUs, diff --git a/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java b/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java index 571c411206..8477a215d3 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java @@ -1349,6 +1349,38 @@ public final class ExoPlayerTest { assertThat(windowIndexHolder[2]).isEqualTo(1); } + @Test + public void testPlaybackErrorTwiceStillKeepsTimeline() throws Exception { + final Timeline timeline = new FakeTimeline(/* windowCount= */ 1); + final FakeMediaSource mediaSource2 = + new FakeMediaSource(/* timeline= */ null, /* manifest= */ null); + ActionSchedule actionSchedule = + new ActionSchedule.Builder("testPlaybackErrorDoesNotResetPosition") + .pause() + .waitForPlaybackState(Player.STATE_READY) + .throwPlaybackException(ExoPlaybackException.createForSource(new IOException())) + .waitForPlaybackState(Player.STATE_IDLE) + .prepareSource(mediaSource2, /* resetPosition= */ false, /* resetState= */ false) + .waitForPlaybackState(Player.STATE_BUFFERING) + .throwPlaybackException(ExoPlaybackException.createForSource(new IOException())) + .waitForPlaybackState(Player.STATE_IDLE) + .build(); + ExoPlayerTestRunner testRunner = + new ExoPlayerTestRunner.Builder() + .setTimeline(timeline) + .setActionSchedule(actionSchedule) + .build(); + try { + testRunner.start().blockUntilActionScheduleFinished(TIMEOUT_MS).blockUntilEnded(TIMEOUT_MS); + fail(); + } catch (ExoPlaybackException e) { + // Expected exception. + } + testRunner.assertTimelinesEqual(timeline, timeline); + testRunner.assertTimelineChangeReasonsEqual( + Player.TIMELINE_CHANGE_REASON_PREPARED, Player.TIMELINE_CHANGE_REASON_PREPARED); + } + @Test public void testSendMessagesDuringPreparation() throws Exception { Timeline timeline = new FakeTimeline(/* windowCount= */ 1); From 7a386d9179b91c8539cfc87e71013e007fafb3df Mon Sep 17 00:00:00 2001 From: bachinger Date: Tue, 6 Mar 2018 11:52:10 -0800 Subject: [PATCH 008/157] Add separate interface RatingCallback to handle user ratings. To support Google Assistant media apps need to support the action ACTION_SET_RATING. Before this change developers have to use a QueueEditor for this which does not have any other mandatory actions required for Assistant. With this change developers can implement the rating action with the PlaybackPreparer which they need to implement anyway to support Assistant. https://developer.android.com/guide/topics/media-apps/interacting-with-assistant.html#transport_controls ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=188056722 --- .../mediasession/MediaSessionConnector.java | 88 ++++++++++--------- .../ext/mediasession/TimelineQueueEditor.java | 11 --- 2 files changed, 48 insertions(+), 51 deletions(-) diff --git a/extensions/mediasession/src/main/java/com/google/android/exoplayer2/ext/mediasession/MediaSessionConnector.java b/extensions/mediasession/src/main/java/com/google/android/exoplayer2/ext/mediasession/MediaSessionConnector.java index 2b4409e0fb..544644d03b 100644 --- a/extensions/mediasession/src/main/java/com/google/android/exoplayer2/ext/mediasession/MediaSessionConnector.java +++ b/extensions/mediasession/src/main/java/com/google/android/exoplayer2/ext/mediasession/MediaSessionConnector.java @@ -105,23 +105,24 @@ public final class MediaSessionConnector { */ public interface PlaybackPreparer extends CommandReceiver { - long ACTIONS = PlaybackStateCompat.ACTION_PREPARE - | PlaybackStateCompat.ACTION_PREPARE_FROM_MEDIA_ID - | PlaybackStateCompat.ACTION_PREPARE_FROM_SEARCH - | PlaybackStateCompat.ACTION_PREPARE_FROM_URI - | PlaybackStateCompat.ACTION_PLAY_FROM_MEDIA_ID - | PlaybackStateCompat.ACTION_PLAY_FROM_SEARCH - | PlaybackStateCompat.ACTION_PLAY_FROM_URI; + long ACTIONS = + PlaybackStateCompat.ACTION_PREPARE + | PlaybackStateCompat.ACTION_PREPARE_FROM_MEDIA_ID + | PlaybackStateCompat.ACTION_PREPARE_FROM_SEARCH + | PlaybackStateCompat.ACTION_PREPARE_FROM_URI + | PlaybackStateCompat.ACTION_PLAY_FROM_MEDIA_ID + | PlaybackStateCompat.ACTION_PLAY_FROM_SEARCH + | PlaybackStateCompat.ACTION_PLAY_FROM_URI; /** * Returns the actions which are supported by the preparer. The supported actions must be a - * bitmask combined out of {@link PlaybackStateCompat#ACTION_PREPARE}, - * {@link PlaybackStateCompat#ACTION_PREPARE_FROM_MEDIA_ID}, - * {@link PlaybackStateCompat#ACTION_PREPARE_FROM_SEARCH}, - * {@link PlaybackStateCompat#ACTION_PREPARE_FROM_URI}, - * {@link PlaybackStateCompat#ACTION_PLAY_FROM_MEDIA_ID}, - * {@link PlaybackStateCompat#ACTION_PLAY_FROM_SEARCH} and - * {@link PlaybackStateCompat#ACTION_PLAY_FROM_URI}. + * bitmask combined out of {@link PlaybackStateCompat#ACTION_PREPARE}, {@link + * PlaybackStateCompat#ACTION_PREPARE_FROM_MEDIA_ID}, {@link + * PlaybackStateCompat#ACTION_PREPARE_FROM_SEARCH}, {@link + * PlaybackStateCompat#ACTION_PREPARE_FROM_URI}, {@link + * PlaybackStateCompat#ACTION_PLAY_FROM_MEDIA_ID}, {@link + * PlaybackStateCompat#ACTION_PLAY_FROM_SEARCH} and {@link + * PlaybackStateCompat#ACTION_PLAY_FROM_URI}. * * @return The bitmask of the supported media actions. */ @@ -264,15 +265,6 @@ public final class MediaSessionConnector { */ public interface QueueEditor extends CommandReceiver { - long ACTIONS = PlaybackStateCompat.ACTION_SET_RATING; - - /** - * Returns {@link PlaybackStateCompat#ACTION_SET_RATING} or {@code 0}. The Media API does - * not declare action constants for adding and removing queue items. - * - * @param player The {@link Player}. - */ - long getSupportedQueueEditorActions(@Nullable Player player); /** * See {@link MediaSessionCompat.Callback#onAddQueueItem(MediaDescriptionCompat description)}. */ @@ -291,9 +283,14 @@ public final class MediaSessionConnector { * See {@link MediaSessionCompat.Callback#onRemoveQueueItemAt(int index)}. */ void onRemoveQueueItemAt(Player player, int index); - /** - * See {@link MediaSessionCompat.Callback#onSetRating(RatingCompat)}. - */ + } + + /** Callback receiving a user rating for the active media item. */ + public interface RatingCallback extends CommandReceiver { + + long ACTIONS = PlaybackStateCompat.ACTION_SET_RATING; + + /** See {@link MediaSessionCompat.Callback#onSetRating(RatingCompat)}. */ void onSetRating(Player player, RatingCompat rating); } @@ -341,6 +338,7 @@ public final class MediaSessionConnector { private PlaybackPreparer playbackPreparer; private QueueNavigator queueNavigator; private QueueEditor queueEditor; + private RatingCallback ratingCallback; private ExoPlaybackException playbackException; /** @@ -471,6 +469,17 @@ public final class MediaSessionConnector { : EDITOR_MEDIA_SESSION_FLAGS); } + /** + * Sets the {@link RatingCallback} to handle user ratings. + * + * @param ratingCallback The rating callback. + */ + public void setRatingCallback(RatingCallback ratingCallback) { + unregisterCommandReceiver(this.ratingCallback); + this.ratingCallback = ratingCallback; + registerCommandReceiver(this.ratingCallback); + } + private void registerCommandReceiver(CommandReceiver commandReceiver) { if (commandReceiver != null && commandReceiver.getCommands() != null) { for (String command : commandReceiver.getCommands()) { @@ -539,8 +548,8 @@ public final class MediaSessionConnector { actions |= (QueueNavigator.ACTIONS & queueNavigator.getSupportedQueueNavigatorActions( player)); } - if (queueEditor != null) { - actions |= (QueueEditor.ACTIONS & queueEditor.getSupportedQueueEditorActions(player)); + if (ratingCallback != null) { + actions |= RatingCallback.ACTIONS; } return actions; } @@ -634,6 +643,10 @@ public final class MediaSessionConnector { & PlaybackPreparer.ACTIONS & action) != 0; } + private boolean canDispatchToRatingCallback(long action) { + return ratingCallback != null && (RatingCallback.ACTIONS & action) != 0; + } + private boolean canDispatchToPlaybackController(long action) { return (playbackController.getSupportedPlaybackActions(player) & PlaybackController.ACTIONS & action) != 0; @@ -644,11 +657,6 @@ public final class MediaSessionConnector { & QueueNavigator.ACTIONS & action) != 0; } - private boolean canDispatchToQueueEditor(long action) { - return queueEditor != null && (queueEditor.getSupportedQueueEditorActions(player) - & QueueEditor.ACTIONS & action) != 0; - } - private class ExoPlayerEventListener extends Player.DefaultEventListener { private int currentWindowIndex; @@ -879,6 +887,13 @@ public final class MediaSessionConnector { } } + @Override + public void onSetRating(RatingCompat rating) { + if (canDispatchToRatingCallback(PlaybackStateCompat.ACTION_SET_RATING)) { + ratingCallback.onSetRating(player, rating); + } + } + @Override public void onAddQueueItem(MediaDescriptionCompat description) { if (queueEditor != null) { @@ -907,13 +922,6 @@ public final class MediaSessionConnector { } } - @Override - public void onSetRating(RatingCompat rating) { - if (canDispatchToQueueEditor(PlaybackStateCompat.ACTION_SET_RATING)) { - queueEditor.onSetRating(player, rating); - } - } - } } diff --git a/extensions/mediasession/src/main/java/com/google/android/exoplayer2/ext/mediasession/TimelineQueueEditor.java b/extensions/mediasession/src/main/java/com/google/android/exoplayer2/ext/mediasession/TimelineQueueEditor.java index 65090a3c1c..402abf7c70 100644 --- a/extensions/mediasession/src/main/java/com/google/android/exoplayer2/ext/mediasession/TimelineQueueEditor.java +++ b/extensions/mediasession/src/main/java/com/google/android/exoplayer2/ext/mediasession/TimelineQueueEditor.java @@ -20,7 +20,6 @@ import android.os.ResultReceiver; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.media.MediaDescriptionCompat; -import android.support.v4.media.RatingCompat; import android.support.v4.media.session.MediaControllerCompat; import android.support.v4.media.session.MediaSessionCompat; import com.google.android.exoplayer2.C; @@ -164,11 +163,6 @@ public final class TimelineQueueEditor implements MediaSessionConnector.QueueEdi this.equalityChecker = equalityChecker; } - @Override - public long getSupportedQueueEditorActions(@Nullable Player player) { - return 0; - } - @Override public void onAddQueueItem(Player player, MediaDescriptionCompat description) { onAddQueueItem(player, description, player.getCurrentTimeline().getWindowCount()); @@ -200,11 +194,6 @@ public final class TimelineQueueEditor implements MediaSessionConnector.QueueEdi queueMediaSource.removeMediaSource(index); } - @Override - public void onSetRating(Player player, RatingCompat rating) { - // Do nothing. - } - // CommandReceiver implementation. @NonNull From f71b087fe4197e9e7215cb964d065dc17ad67c96 Mon Sep 17 00:00:00 2001 From: andrewlewis Date: Wed, 7 Mar 2018 01:21:08 -0800 Subject: [PATCH 009/157] Force rendering a frame every 100 ms Also remove logic for tracking the next output buffer in LibvpxVideoRenderer, as this allowed many consecutive frames to be rendered that were actually late after dropping to keyframe. It looks better to show frames at a consistent 100 ms rate. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=188144739 --- RELEASENOTES.md | 2 + .../ext/vp9/LibvpxVideoRenderer.java | 123 +++++++++--------- .../video/MediaCodecVideoRenderer.java | 38 ++++-- 3 files changed, 87 insertions(+), 76 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 3223d6ae4c..1762d15b1c 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -11,6 +11,8 @@ [#3888](https://github.com/google/ExoPlayer/issues/3888)). * Enable seeking in MP4 streams where duration is set incorrectly in the track header ([#3926](https://github.com/google/ExoPlayer/issues/3926)). +* Video: force rendering a frame periodically in `MediaCodecVideoRenderer` and + `LibvpxVideoRenderer`, even if it is late. ### 2.7.0 ### diff --git a/extensions/vp9/src/main/java/com/google/android/exoplayer2/ext/vp9/LibvpxVideoRenderer.java b/extensions/vp9/src/main/java/com/google/android/exoplayer2/ext/vp9/LibvpxVideoRenderer.java index d93aa6d39e..4d75f6076b 100644 --- a/extensions/vp9/src/main/java/com/google/android/exoplayer2/ext/vp9/LibvpxVideoRenderer.java +++ b/extensions/vp9/src/main/java/com/google/android/exoplayer2/ext/vp9/LibvpxVideoRenderer.java @@ -119,7 +119,6 @@ public class LibvpxVideoRenderer extends BaseRenderer { private VpxDecoder decoder; private VpxInputBuffer inputBuffer; private VpxOutputBuffer outputBuffer; - private VpxOutputBuffer nextOutputBuffer; private DrmSession drmSession; private DrmSession pendingDrmSession; @@ -128,7 +127,6 @@ public class LibvpxVideoRenderer extends BaseRenderer { private Bitmap bitmap; private boolean renderedFirstFrame; - private boolean forceRenderFrame; private long joiningDeadlineMs; private Surface surface; private VpxOutputBufferRenderer outputBufferRenderer; @@ -144,6 +142,7 @@ public class LibvpxVideoRenderer extends BaseRenderer { private int droppedFrames; private int consecutiveDroppedFrameCount; private int buffersInCodecCount; + private long lastRenderTimeUs; protected DecoderCounters decoderCounters; @@ -254,7 +253,7 @@ public class LibvpxVideoRenderer extends BaseRenderer { try { // Rendering loop. TraceUtil.beginSection("drainAndFeed"); - while (drainOutputBuffer(positionUs)) {} + while (drainOutputBuffer(positionUs, elapsedRealtimeUs)) {} while (feedInputBuffer()) {} TraceUtil.endSection(); } catch (VpxDecoderException e) { @@ -319,6 +318,7 @@ public class LibvpxVideoRenderer extends BaseRenderer { protected void onStarted() { droppedFrames = 0; droppedFrameAccumulationStartTimeMs = SystemClock.elapsedRealtime(); + lastRenderTimeUs = SystemClock.elapsedRealtime() * 1000; } @Override @@ -379,7 +379,6 @@ public class LibvpxVideoRenderer extends BaseRenderer { @CallSuper protected void flushDecoder() throws ExoPlaybackException { waitingForKeys = false; - forceRenderFrame = false; buffersInCodecCount = 0; if (decoderReinitializationState != REINITIALIZATION_STATE_NONE) { releaseDecoder(); @@ -390,10 +389,6 @@ public class LibvpxVideoRenderer extends BaseRenderer { outputBuffer.release(); outputBuffer = null; } - if (nextOutputBuffer != null) { - nextOutputBuffer.release(); - nextOutputBuffer = null; - } decoder.flush(); decoderReceivedBuffers = false; } @@ -408,13 +403,11 @@ public class LibvpxVideoRenderer extends BaseRenderer { inputBuffer = null; outputBuffer = null; - nextOutputBuffer = null; decoder.release(); decoder = null; decoderCounters.decoderReleaseCount++; decoderReinitializationState = REINITIALIZATION_STATE_NONE; decoderReceivedBuffers = false; - forceRenderFrame = false; buffersInCodecCount = 0; } @@ -482,22 +475,15 @@ public class LibvpxVideoRenderer extends BaseRenderer { } /** - * Returns whether the current frame should be dropped. + * Returns whether the buffer being processed should be dropped. * - * @param outputBufferTimeUs The timestamp of the current output buffer. - * @param nextOutputBufferTimeUs The timestamp of the next output buffer or {@link C#TIME_UNSET} - * if the next output buffer is unavailable. - * @param positionUs The current playback position. - * @param joiningDeadlineMs The joining deadline. - * @return Returns whether to drop the current output buffer. + * @param earlyUs The time until the buffer should be presented in microseconds. A negative value + * indicates that the buffer is late. + * @param elapsedRealtimeUs {@link android.os.SystemClock#elapsedRealtime()} in microseconds, + * measured at the start of the current iteration of the rendering loop. */ - protected boolean shouldDropOutputBuffer( - long outputBufferTimeUs, - long nextOutputBufferTimeUs, - long positionUs, - long joiningDeadlineMs) { - return isBufferLate(outputBufferTimeUs - positionUs) - && (joiningDeadlineMs != C.TIME_UNSET || nextOutputBufferTimeUs != C.TIME_UNSET); + protected boolean shouldDropOutputBuffer(long earlyUs, long elapsedRealtimeUs) { + return isBufferLate(earlyUs); } /** @@ -506,11 +492,26 @@ public class LibvpxVideoRenderer extends BaseRenderer { * * @param earlyUs The time until the current buffer should be presented in microseconds. A * negative value indicates that the buffer is late. + * @param elapsedRealtimeUs {@link android.os.SystemClock#elapsedRealtime()} in microseconds, + * measured at the start of the current iteration of the rendering loop. */ - protected boolean shouldDropBuffersToKeyframe(long earlyUs) { + protected boolean shouldDropBuffersToKeyframe(long earlyUs, long elapsedRealtimeUs) { return isBufferVeryLate(earlyUs); } + /** + * Returns whether to force rendering an output buffer. + * + * @param earlyUs The time until the current buffer should be presented in microseconds. A + * negative value indicates that the buffer is late. + * @param elapsedSinceLastRenderUs The elapsed time since the last output buffer was rendered, in + * microseconds. + * @return Returns whether to force rendering an output buffer. + */ + protected boolean shouldForceRenderOutputBuffer(long earlyUs, long elapsedSinceLastRenderUs) { + return isBufferLate(earlyUs) && elapsedSinceLastRenderUs > 100000; + } + /** * Skips the specified output buffer and releases it. * @@ -543,6 +544,7 @@ public class LibvpxVideoRenderer extends BaseRenderer { int bufferMode = outputBuffer.mode; boolean renderRgb = bufferMode == VpxDecoder.OUTPUT_MODE_RGB && surface != null; boolean renderYuv = bufferMode == VpxDecoder.OUTPUT_MODE_YUV && outputBufferRenderer != null; + lastRenderTimeUs = SystemClock.elapsedRealtime() * 1000; if (!renderRgb && !renderYuv) { dropOutputBuffer(outputBuffer); } else { @@ -755,22 +757,18 @@ public class LibvpxVideoRenderer extends BaseRenderer { /** * Attempts to dequeue an output buffer from the decoder and, if successful, passes it to {@link - * #processOutputBuffer(long)}. + * #processOutputBuffer(long, long)}. * * @param positionUs The player's current position. + * @param elapsedRealtimeUs {@link android.os.SystemClock#elapsedRealtime()} in microseconds, + * measured at the start of the current iteration of the rendering loop. * @return Whether it may be possible to drain more output data. * @throws ExoPlaybackException If an error occurs draining the output buffer. */ - private boolean drainOutputBuffer(long positionUs) + private boolean drainOutputBuffer(long positionUs, long elapsedRealtimeUs) throws ExoPlaybackException, VpxDecoderException { - // Acquire outputBuffer either from nextOutputBuffer or from the decoder. if (outputBuffer == null) { - if (nextOutputBuffer != null) { - outputBuffer = nextOutputBuffer; - nextOutputBuffer = null; - } else { - outputBuffer = decoder.dequeueOutputBuffer(); - } + outputBuffer = decoder.dequeueOutputBuffer(); if (outputBuffer == null) { return false; } @@ -778,10 +776,6 @@ public class LibvpxVideoRenderer extends BaseRenderer { buffersInCodecCount -= outputBuffer.skippedOutputBufferCount; } - if (nextOutputBuffer == null) { - nextOutputBuffer = decoder.dequeueOutputBuffer(); - } - if (outputBuffer.isEndOfStream()) { if (decoderReinitializationState == REINITIALIZATION_STATE_WAIT_END_OF_STREAM) { // We're waiting to re-initialize the decoder, and have now processed all final buffers. @@ -795,7 +789,12 @@ public class LibvpxVideoRenderer extends BaseRenderer { return false; } - return processOutputBuffer(positionUs); + boolean processedOutputBuffer = processOutputBuffer(positionUs, elapsedRealtimeUs); + if (processedOutputBuffer) { + onProcessedOutputBuffer(outputBuffer.timeUs); + outputBuffer = null; + } + return processedOutputBuffer; } /** @@ -803,53 +802,47 @@ public class LibvpxVideoRenderer extends BaseRenderer { * whether it may be possible to process another output buffer. * * @param positionUs The player's current position. + * @param elapsedRealtimeUs {@link android.os.SystemClock#elapsedRealtime()} in microseconds, + * measured at the start of the current iteration of the rendering loop. * @return Whether it may be possible to drain another output buffer. * @throws ExoPlaybackException If an error occurs processing the output buffer. */ - private boolean processOutputBuffer(long positionUs) throws ExoPlaybackException { + private boolean processOutputBuffer(long positionUs, long elapsedRealtimeUs) + throws ExoPlaybackException { + long earlyUs = outputBuffer.timeUs - positionUs; if (outputMode == VpxDecoder.OUTPUT_MODE_NONE) { // Skip frames in sync with playback, so we'll be at the right frame if the mode changes. - if (isBufferLate(outputBuffer.timeUs - positionUs)) { - forceRenderFrame = false; + if (isBufferLate(earlyUs)) { skipOutputBuffer(outputBuffer); - onProcessedOutputBuffer(outputBuffer.timeUs); - outputBuffer = null; return true; } return false; } - if (forceRenderFrame) { - forceRenderFrame = false; + long elapsedRealtimeNowUs = SystemClock.elapsedRealtime() * 1000; + boolean isStarted = getState() == STATE_STARTED; + if (!renderedFirstFrame + || (isStarted + && shouldForceRenderOutputBuffer(earlyUs, elapsedRealtimeNowUs - lastRenderTimeUs))) { renderOutputBuffer(outputBuffer); - onProcessedOutputBuffer(outputBuffer.timeUs); - outputBuffer = null; return true; } - long nextOutputBufferTimeUs = - nextOutputBuffer != null && !nextOutputBuffer.isEndOfStream() - ? nextOutputBuffer.timeUs - : C.TIME_UNSET; - - long earlyUs = outputBuffer.timeUs - positionUs; - if (shouldDropBuffersToKeyframe(earlyUs) && maybeDropBuffersToKeyframe(positionUs)) { - forceRenderFrame = true; + if (!isStarted) { return false; - } else if (shouldDropOutputBuffer( - outputBuffer.timeUs, nextOutputBufferTimeUs, positionUs, joiningDeadlineMs)) { + } + + if (shouldDropBuffersToKeyframe(earlyUs, elapsedRealtimeUs) + && maybeDropBuffersToKeyframe(positionUs)) { + return false; + } else if (shouldDropOutputBuffer(earlyUs, elapsedRealtimeUs)) { dropOutputBuffer(outputBuffer); - onProcessedOutputBuffer(outputBuffer.timeUs); - outputBuffer = null; return true; } - // If we have yet to render a frame to the current output (either initially or immediately - // following a seek), render one irrespective of the state or current position. - if (!renderedFirstFrame || (getState() == STATE_STARTED && earlyUs <= 30000)) { + if (earlyUs < 30000) { renderOutputBuffer(outputBuffer); - onProcessedOutputBuffer(outputBuffer.timeUs); - outputBuffer = null; + return true; } return false; diff --git a/library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java b/library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java index 62a7657ea7..2978d00d86 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java @@ -100,12 +100,12 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { @C.VideoScalingMode private int scalingMode; private boolean renderedFirstFrame; - private boolean forceRenderFrame; private long joiningDeadlineMs; private long droppedFrameAccumulationStartTimeMs; private int droppedFrames; private int consecutiveDroppedFrameCount; private int buffersInCodecCount; + private long lastRenderTimeUs; private int pendingRotationDegrees; private float pendingPixelWidthHeightRatio; @@ -314,6 +314,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { super.onStarted(); droppedFrames = 0; droppedFrameAccumulationStartTimeMs = SystemClock.elapsedRealtime(); + lastRenderTimeUs = SystemClock.elapsedRealtime() * 1000; } @Override @@ -438,7 +439,6 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { super.releaseCodec(); } finally { buffersInCodecCount = 0; - forceRenderFrame = false; if (dummySurface != null) { if (surface == dummySurface) { surface = null; @@ -454,7 +454,6 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { protected void flushCodec() throws ExoPlaybackException { super.flushCodec(); buffersInCodecCount = 0; - forceRenderFrame = false; } @Override @@ -546,15 +545,17 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { if (surface == dummySurface) { // Skip frames in sync with playback, so we'll be at the right frame if the mode changes. if (isBufferLate(earlyUs)) { - forceRenderFrame = false; skipOutputBuffer(codec, bufferIndex, presentationTimeUs); return true; } return false; } - if (!renderedFirstFrame || forceRenderFrame) { - forceRenderFrame = false; + long elapsedRealtimeNowUs = SystemClock.elapsedRealtime() * 1000; + boolean isStarted = getState() == STATE_STARTED; + if (!renderedFirstFrame + || (isStarted + && shouldForceRenderOutputBuffer(earlyUs, elapsedRealtimeNowUs - lastRenderTimeUs))) { if (Util.SDK_INT >= 21) { renderOutputBufferV21(codec, bufferIndex, presentationTimeUs, System.nanoTime()); } else { @@ -563,13 +564,13 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { return true; } - if (getState() != STATE_STARTED) { + if (!isStarted) { return false; } // Fine-grained adjustment of earlyUs based on the elapsed time since the start of the current // iteration of the rendering loop. - long elapsedSinceStartOfLoopUs = (SystemClock.elapsedRealtime() * 1000) - elapsedRealtimeUs; + long elapsedSinceStartOfLoopUs = elapsedRealtimeNowUs - elapsedRealtimeUs; earlyUs -= elapsedSinceStartOfLoopUs; // Compute the buffer's desired release time in nanoseconds. @@ -583,7 +584,6 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { if (shouldDropBuffersToKeyframe(earlyUs, elapsedRealtimeUs) && maybeDropBuffersToKeyframe(codec, bufferIndex, presentationTimeUs, positionUs)) { - forceRenderFrame = true; return false; } else if (shouldDropOutputBuffer(earlyUs, elapsedRealtimeUs)) { dropOutputBuffer(codec, bufferIndex, presentationTimeUs); @@ -607,6 +607,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { Thread.sleep((earlyUs - 10000) / 1000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); + return false; } } renderOutputBuffer(codec, bufferIndex, presentationTimeUs); @@ -654,6 +655,19 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { return isBufferVeryLate(earlyUs); } + /** + * Returns whether to force rendering an output buffer. + * + * @param earlyUs The time until the current buffer should be presented in microseconds. A + * negative value indicates that the buffer is late. + * @param elapsedSinceLastRenderUs The elapsed time since the last output buffer was rendered, in + * microseconds. + * @return Returns whether to force rendering an output buffer. + */ + protected boolean shouldForceRenderOutputBuffer(long earlyUs, long elapsedSinceLastRenderUs) { + return isBufferLate(earlyUs) && elapsedSinceLastRenderUs > 100000; + } + /** * Skips the output buffer with the specified index. * @@ -738,6 +752,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { TraceUtil.beginSection("releaseOutputBuffer"); codec.releaseOutputBuffer(index, true); TraceUtil.endSection(); + lastRenderTimeUs = SystemClock.elapsedRealtime() * 1000; decoderCounters.renderedOutputBufferCount++; consecutiveDroppedFrameCount = 0; maybeNotifyRenderedFirstFrame(); @@ -753,12 +768,13 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { * @param releaseTimeNs The wallclock time at which the frame should be displayed, in nanoseconds. */ @TargetApi(21) - protected void renderOutputBufferV21(MediaCodec codec, int index, long presentationTimeUs, - long releaseTimeNs) { + protected void renderOutputBufferV21( + MediaCodec codec, int index, long presentationTimeUs, long releaseTimeNs) { maybeNotifyVideoSizeChanged(); TraceUtil.beginSection("releaseOutputBuffer"); codec.releaseOutputBuffer(index, releaseTimeNs); TraceUtil.endSection(); + lastRenderTimeUs = SystemClock.elapsedRealtime() * 1000; decoderCounters.renderedOutputBufferCount++; consecutiveDroppedFrameCount = 0; maybeNotifyRenderedFirstFrame(); From 91c44902497c3ad1df5127d34d9975c99880ae19 Mon Sep 17 00:00:00 2001 From: andrewlewis Date: Wed, 7 Mar 2018 07:26:51 -0800 Subject: [PATCH 010/157] Work around stalled AudioTrack up to API 28 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=188173078 --- .../com/google/android/exoplayer2/audio/DefaultAudioSink.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/audio/DefaultAudioSink.java b/library/core/src/main/java/com/google/android/exoplayer2/audio/DefaultAudioSink.java index bb9135edbf..6d12dc66e8 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/audio/DefaultAudioSink.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/audio/DefaultAudioSink.java @@ -1443,7 +1443,7 @@ public final class DefaultAudioSink implements AudioSink { rawPlaybackHeadPosition += passthroughWorkaroundPauseOffset; } - if (Util.SDK_INT <= 26) { + if (Util.SDK_INT <= 28) { if (rawPlaybackHeadPosition == 0 && lastRawPlaybackHeadPosition > 0 && state == PLAYSTATE_PLAYING) { // If connecting a Bluetooth audio device fails, the AudioTrack may be left in a state From 7c7711c3558409f84a410b7edad07b68f56dba22 Mon Sep 17 00:00:00 2001 From: Aditya Anand <13274079+AdityaAnand1@users.noreply.github.com> Date: Mon, 26 Feb 2018 09:11:02 +0530 Subject: [PATCH 011/157] replace 'compile' with 'implementation' --- README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 7f35329516..8755ac588d 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ Next add a gradle compile dependency to the `build.gradle` file of your app module. The following will add a dependency to the full library: ```gradle -compile 'com.google.android.exoplayer:exoplayer:2.X.X' +implementation 'com.google.android.exoplayer:exoplayer:2.X.X' ``` where `2.X.X` is your preferred version. Alternatively, you can depend on only @@ -51,9 +51,9 @@ dependencies on the Core, DASH and UI library modules, as might be required for an app that plays DASH content: ```gradle -compile 'com.google.android.exoplayer:exoplayer-core:2.X.X' -compile 'com.google.android.exoplayer:exoplayer-dash:2.X.X' -compile 'com.google.android.exoplayer:exoplayer-ui:2.X.X' +implementation 'com.google.android.exoplayer:exoplayer-core:2.X.X' +implementation 'com.google.android.exoplayer:exoplayer-dash:2.X.X' +implementation 'com.google.android.exoplayer:exoplayer-ui:2.X.X' ``` The available library modules are listed below. Adding a dependency to the full @@ -105,9 +105,9 @@ You should now see the ExoPlayer modules appear as part of your project. You can depend on them as you would on any other local module, for example: ```gradle -compile project(':exoplayer-library-core') -compile project(':exoplayer-library-dash') -compile project(':exoplayer-library-ui') +implementation project(':exoplayer-library-core') +implementation project(':exoplayer-library-dash') +implementation project(':exoplayer-library-ui') ``` ## Developing ExoPlayer ## From 4519c31c5806cbad51bc1c6c731aee703842dc5e Mon Sep 17 00:00:00 2001 From: tonihei Date: Mon, 26 Feb 2018 07:58:29 -0800 Subject: [PATCH 012/157] Move extension tests to Robolectric. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=187021822 --- core_settings.gradle | 2 + extensions/cast/build.gradle | 5 +- extensions/cast/src/test/AndroidManifest.xml | 23 + .../ext/cast/CastTimelineTrackerTest.java | 28 +- .../src/test/resources/robolectric.properties | 1 + extensions/cronet/build.gradle | 8 +- .../{androidTest => test}/AndroidManifest.xml | 12 +- .../ByteArrayUploadDataProviderTest.java | 14 +- .../ext/cronet/CronetDataSourceTest.java | 524 ++++---- .../src/test/resources/robolectric.properties | 1 + library/core/build.gradle | 1 + .../upstream/ContentDataSourceTest.java | 2 +- .../android/exoplayer2/ExoPlayerTest.java | 1 + .../drm/OfflineLicenseHelperTest.java | 2 +- .../source/ClippingMediaSourceTest.java | 2 +- .../source/ConcatenatingMediaSourceTest.java | 2 +- .../DynamicConcatenatingMediaSourceTest.java | 2 +- .../source/LoopingMediaSourceTest.java | 2 +- .../source/MergingMediaSourceTest.java | 2 +- library/dash/build.gradle | 10 +- .../dash/offline/DashDownloadTestData.java | 100 -- .../source/dash/DashMediaSource.java | 5 +- .../{androidTest => test}/AndroidManifest.xml | 12 +- .../{androidTest => test}/assets/sample_mpd_1 | 0 .../assets/sample_mpd_2_unknown_mime_type | 0 .../assets/sample_mpd_3_segment_template | 0 .../assets/sample_mpd_4_event_stream | 0 .../source/dash/DashMediaSourceTest.java | 76 ++ .../exoplayer2/source/dash/DashUtilTest.java | 38 +- .../source/dash/EventSampleStreamTest.java | 356 ++++++ .../dash/manifest/DashManifestParserTest.java | 59 +- .../dash/manifest/DashManifestTest.java | 193 +-- .../source/dash/manifest/RangedUriTest.java | 16 +- .../dash/manifest/RepresentationTest.java | 50 +- .../source/dash/manifest/UrlTemplateTest.java | 17 +- .../dash/offline/DashDownloadActionTest.java | 162 +++ .../dash/offline/DashDownloadTestData.java | 103 ++ .../dash/offline/DashDownloaderTest.java | 283 +++-- .../src/test/resources/robolectric.properties | 1 + library/hls/build.gradle | 5 +- .../hls/offline/HlsDownloadTestData.java | 77 -- .../{androidTest => test}/AndroidManifest.xml | 12 +- .../hls/offline/HlsDownloadTestData.java | 83 ++ .../source/hls/offline/HlsDownloaderTest.java | 95 +- .../playlist/HlsMasterPlaylistParserTest.java | 138 ++- .../playlist/HlsMediaPlaylistParserTest.java | 86 +- .../src/test/resources/robolectric.properties | 1 + library/smoothstreaming/build.gradle | 5 +- .../{androidTest => test}/AndroidManifest.xml | 12 +- .../assets/sample_ismc_1 | 0 .../assets/sample_ismc_2 | 0 .../manifest/SsManifestParserTest.java | 28 +- .../manifest/SsManifestTest.java | 63 +- .../src/test/resources/robolectric.properties | 1 + testutils/build.gradle | 1 + .../exoplayer2/testutil/MockitoUtil.java | 54 - .../exoplayer2/testutil/OggTestData.java | 1073 ----------------- .../android/exoplayer2/testutil/TestUtil.java | 11 - testutils/src/test/AndroidManifest.xml | 23 + .../testutil/FakeAdaptiveDataSetTest.java | 147 +++ .../exoplayer2/testutil/FakeClockTest.java | 201 +++ .../exoplayer2/testutil/FakeDataSetTest.java | 119 ++ .../testutil/FakeDataSourceTest.java | 251 ++++ .../src/test/resources/robolectric.properties | 1 + testutils_robolectric/build.gradle | 37 + .../src/main/AndroidManifest.xml | 17 + .../exoplayer2/testutil/CacheAsserts.java | 5 +- .../testutil/FakeMediaClockRenderer.java | 5 +- .../exoplayer2/testutil/FakeShuffleOrder.java | 0 .../testutil/FakeTrackSelection.java | 9 +- .../testutil/FakeTrackSelector.java | 21 +- .../testutil/MediaSourceTestRunner.java | 107 +- .../exoplayer2/testutil/OggTestData.java | 1070 ++++++++++++++++ .../exoplayer2/testutil}/RobolectricUtil.java | 2 +- .../exoplayer2/testutil/StubExoPlayer.java | 1 - .../exoplayer2/testutil/TimelineAsserts.java | 44 +- 76 files changed, 3703 insertions(+), 2217 deletions(-) create mode 100644 extensions/cast/src/test/AndroidManifest.xml create mode 100644 extensions/cast/src/test/resources/robolectric.properties rename extensions/cronet/src/{androidTest => test}/AndroidManifest.xml (71%) rename extensions/cronet/src/{androidTest => test}/java/com/google/android/exoplayer2/ext/cronet/ByteArrayUploadDataProviderTest.java (90%) rename extensions/cronet/src/{androidTest => test}/java/com/google/android/exoplayer2/ext/cronet/CronetDataSourceTest.java (71%) create mode 100644 extensions/cronet/src/test/resources/robolectric.properties delete mode 100644 library/dash/src/androidTest/java/com/google/android/exoplayer2/source/dash/offline/DashDownloadTestData.java rename library/dash/src/{androidTest => test}/AndroidManifest.xml (71%) rename library/dash/src/{androidTest => test}/assets/sample_mpd_1 (100%) rename library/dash/src/{androidTest => test}/assets/sample_mpd_2_unknown_mime_type (100%) rename library/dash/src/{androidTest => test}/assets/sample_mpd_3_segment_template (100%) rename library/dash/src/{androidTest => test}/assets/sample_mpd_4_event_stream (100%) create mode 100644 library/dash/src/test/java/com/google/android/exoplayer2/source/dash/DashMediaSourceTest.java rename library/dash/src/{androidTest => test}/java/com/google/android/exoplayer2/source/dash/DashUtilTest.java (81%) create mode 100644 library/dash/src/test/java/com/google/android/exoplayer2/source/dash/EventSampleStreamTest.java rename library/dash/src/{androidTest => test}/java/com/google/android/exoplayer2/source/dash/manifest/DashManifestParserTest.java (85%) rename library/dash/src/{androidTest => test}/java/com/google/android/exoplayer2/source/dash/manifest/DashManifestTest.java (54%) rename library/dash/src/{androidTest => test}/java/com/google/android/exoplayer2/source/dash/manifest/RangedUriTest.java (92%) rename library/dash/src/{androidTest => test}/java/com/google/android/exoplayer2/source/dash/manifest/RepresentationTest.java (54%) rename library/dash/src/{androidTest => test}/java/com/google/android/exoplayer2/source/dash/manifest/UrlTemplateTest.java (89%) create mode 100644 library/dash/src/test/java/com/google/android/exoplayer2/source/dash/offline/DashDownloadActionTest.java create mode 100644 library/dash/src/test/java/com/google/android/exoplayer2/source/dash/offline/DashDownloadTestData.java rename library/dash/src/{androidTest => test}/java/com/google/android/exoplayer2/source/dash/offline/DashDownloaderTest.java (65%) create mode 100644 library/dash/src/test/resources/robolectric.properties delete mode 100644 library/hls/src/androidTest/java/com/google/android/exoplayer2/source/hls/offline/HlsDownloadTestData.java rename library/hls/src/{androidTest => test}/AndroidManifest.xml (71%) create mode 100644 library/hls/src/test/java/com/google/android/exoplayer2/source/hls/offline/HlsDownloadTestData.java rename library/hls/src/{androidTest => test}/java/com/google/android/exoplayer2/source/hls/offline/HlsDownloaderTest.java (78%) rename library/hls/src/{androidTest => test}/java/com/google/android/exoplayer2/source/hls/playlist/HlsMasterPlaylistParserTest.java (59%) rename library/hls/src/{androidTest => test}/java/com/google/android/exoplayer2/source/hls/playlist/HlsMediaPlaylistParserTest.java (76%) create mode 100644 library/hls/src/test/resources/robolectric.properties rename library/smoothstreaming/src/{androidTest => test}/AndroidManifest.xml (70%) rename library/smoothstreaming/src/{androidTest => test}/assets/sample_ismc_1 (100%) rename library/smoothstreaming/src/{androidTest => test}/assets/sample_ismc_2 (100%) rename library/smoothstreaming/src/{androidTest => test}/java/com/google/android/exoplayer2/source/smoothstreaming/manifest/SsManifestParserTest.java (60%) rename library/smoothstreaming/src/{androidTest => test}/java/com/google/android/exoplayer2/source/smoothstreaming/manifest/SsManifestTest.java (77%) create mode 100644 library/smoothstreaming/src/test/resources/robolectric.properties delete mode 100644 testutils/src/main/java/com/google/android/exoplayer2/testutil/MockitoUtil.java delete mode 100644 testutils/src/main/java/com/google/android/exoplayer2/testutil/OggTestData.java create mode 100644 testutils/src/test/AndroidManifest.xml create mode 100644 testutils/src/test/java/com/google/android/exoplayer2/testutil/FakeAdaptiveDataSetTest.java create mode 100644 testutils/src/test/java/com/google/android/exoplayer2/testutil/FakeClockTest.java create mode 100644 testutils/src/test/java/com/google/android/exoplayer2/testutil/FakeDataSetTest.java create mode 100644 testutils/src/test/java/com/google/android/exoplayer2/testutil/FakeDataSourceTest.java create mode 100644 testutils/src/test/resources/robolectric.properties create mode 100644 testutils_robolectric/build.gradle create mode 100644 testutils_robolectric/src/main/AndroidManifest.xml rename {testutils => testutils_robolectric}/src/main/java/com/google/android/exoplayer2/testutil/CacheAsserts.java (99%) rename {testutils => testutils_robolectric}/src/main/java/com/google/android/exoplayer2/testutil/FakeMediaClockRenderer.java (93%) rename {testutils => testutils_robolectric}/src/main/java/com/google/android/exoplayer2/testutil/FakeShuffleOrder.java (100%) rename {testutils => testutils_robolectric}/src/main/java/com/google/android/exoplayer2/testutil/FakeTrackSelection.java (94%) rename {testutils => testutils_robolectric}/src/main/java/com/google/android/exoplayer2/testutil/FakeTrackSelector.java (83%) rename {testutils => testutils_robolectric}/src/main/java/com/google/android/exoplayer2/testutil/MediaSourceTestRunner.java (85%) create mode 100644 testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/OggTestData.java rename {library/core/src/test/java/com/google/android/exoplayer2 => testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil}/RobolectricUtil.java (99%) rename {testutils => testutils_robolectric}/src/main/java/com/google/android/exoplayer2/testutil/StubExoPlayer.java (99%) rename {testutils => testutils_robolectric}/src/main/java/com/google/android/exoplayer2/testutil/TimelineAsserts.java (84%) diff --git a/core_settings.gradle b/core_settings.gradle index 20a7c87bde..c4914e3040 100644 --- a/core_settings.gradle +++ b/core_settings.gradle @@ -24,6 +24,7 @@ include modulePrefix + 'library-hls' include modulePrefix + 'library-smoothstreaming' include modulePrefix + 'library-ui' include modulePrefix + 'testutils' +include modulePrefix + 'testutils-robolectric' include modulePrefix + 'extension-ffmpeg' include modulePrefix + 'extension-flac' include modulePrefix + 'extension-gvr' @@ -43,6 +44,7 @@ project(modulePrefix + 'library-hls').projectDir = new File(rootDir, 'library/hl project(modulePrefix + 'library-smoothstreaming').projectDir = new File(rootDir, 'library/smoothstreaming') project(modulePrefix + 'library-ui').projectDir = new File(rootDir, 'library/ui') project(modulePrefix + 'testutils').projectDir = new File(rootDir, 'testutils') +project(modulePrefix + 'testutils-robolectric').projectDir = new File(rootDir, 'testutils_robolectric') project(modulePrefix + 'extension-ffmpeg').projectDir = new File(rootDir, 'extensions/ffmpeg') project(modulePrefix + 'extension-flac').projectDir = new File(rootDir, 'extensions/flac') project(modulePrefix + 'extension-gvr').projectDir = new File(rootDir, 'extensions/gvr') diff --git a/extensions/cast/build.gradle b/extensions/cast/build.gradle index d11f166c89..2f79c7a0ee 100644 --- a/extensions/cast/build.gradle +++ b/extensions/cast/build.gradle @@ -38,10 +38,7 @@ dependencies { compile 'com.google.android.gms:play-services-cast-framework:' + playServicesLibraryVersion compile project(modulePrefix + 'library-core') compile project(modulePrefix + 'library-ui') - testCompile project(modulePrefix + 'testutils') - testCompile 'junit:junit:' + junitVersion - testCompile 'org.mockito:mockito-core:' + mockitoVersion - testCompile 'org.robolectric:robolectric:' + robolectricVersion + testCompile project(modulePrefix + 'testutils-robolectric') } ext { diff --git a/extensions/cast/src/test/AndroidManifest.xml b/extensions/cast/src/test/AndroidManifest.xml new file mode 100644 index 0000000000..057efdc245 --- /dev/null +++ b/extensions/cast/src/test/AndroidManifest.xml @@ -0,0 +1,23 @@ + + + + + + + + diff --git a/extensions/cast/src/test/java/com/google/android/exoplayer2/ext/cast/CastTimelineTrackerTest.java b/extensions/cast/src/test/java/com/google/android/exoplayer2/ext/cast/CastTimelineTrackerTest.java index bf4b20e156..4c60e7c0b3 100644 --- a/extensions/cast/src/test/java/com/google/android/exoplayer2/ext/cast/CastTimelineTrackerTest.java +++ b/extensions/cast/src/test/java/com/google/android/exoplayer2/ext/cast/CastTimelineTrackerTest.java @@ -17,6 +17,7 @@ package com.google.android.exoplayer2.ext.cast; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.testutil.TimelineAsserts; +import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.gms.cast.MediaInfo; import com.google.android.gms.cast.MediaQueueItem; import com.google.android.gms.cast.MediaStatus; @@ -25,11 +26,9 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mockito; import org.robolectric.RobolectricTestRunner; -import org.robolectric.annotation.Config; /** Tests for {@link CastTimelineTracker}. */ @RunWith(RobolectricTestRunner.class) -@Config(sdk = Config.TARGET_SDK, manifest = Config.NONE) public class CastTimelineTrackerTest { private static final long DURATION_1_MS = 1000; @@ -49,12 +48,12 @@ public class CastTimelineTrackerTest { new long[] {DURATION_1_MS, MediaInfo.UNKNOWN_DURATION, MediaInfo.UNKNOWN_DURATION}); CastTimelineTracker tracker = new CastTimelineTracker(); - mediaInfo = mockMediaInfo("contentId1", DURATION_1_MS); + mediaInfo = getMediaInfo("contentId1", DURATION_1_MS); Mockito.when(status.getMediaInfo()).thenReturn(mediaInfo); TimelineAsserts.assertPeriodDurations( tracker.getCastTimeline(status), C.msToUs(DURATION_1_MS), C.TIME_UNSET, C.TIME_UNSET); - mediaInfo = mockMediaInfo("contentId3", DURATION_3_MS); + mediaInfo = getMediaInfo("contentId3", DURATION_3_MS); Mockito.when(status.getMediaInfo()).thenReturn(mediaInfo); TimelineAsserts.assertPeriodDurations( tracker.getCastTimeline(status), @@ -62,7 +61,7 @@ public class CastTimelineTrackerTest { C.TIME_UNSET, C.msToUs(DURATION_3_MS)); - mediaInfo = mockMediaInfo("contentId2", DURATION_2_MS); + mediaInfo = getMediaInfo("contentId2", DURATION_2_MS); Mockito.when(status.getMediaInfo()).thenReturn(mediaInfo); TimelineAsserts.assertPeriodDurations( tracker.getCastTimeline(status), @@ -80,7 +79,7 @@ public class CastTimelineTrackerTest { DURATION_5_MS, MediaInfo.UNKNOWN_DURATION }); - mediaInfo = mockMediaInfo("contentId5", DURATION_5_MS); + mediaInfo = getMediaInfo("contentId5", DURATION_5_MS); Mockito.when(newStatus.getMediaInfo()).thenReturn(mediaInfo); TimelineAsserts.assertPeriodDurations( tracker.getCastTimeline(newStatus), @@ -89,7 +88,7 @@ public class CastTimelineTrackerTest { C.msToUs(DURATION_5_MS), C.msToUs(DURATION_3_MS)); - mediaInfo = mockMediaInfo("contentId3", DURATION_3_MS); + mediaInfo = getMediaInfo("contentId3", DURATION_3_MS); Mockito.when(newStatus.getMediaInfo()).thenReturn(mediaInfo); TimelineAsserts.assertPeriodDurations( tracker.getCastTimeline(newStatus), @@ -98,7 +97,7 @@ public class CastTimelineTrackerTest { C.msToUs(DURATION_5_MS), C.msToUs(DURATION_3_MS)); - mediaInfo = mockMediaInfo("contentId4", DURATION_4_MS); + mediaInfo = getMediaInfo("contentId4", DURATION_4_MS); Mockito.when(newStatus.getMediaInfo()).thenReturn(mediaInfo); TimelineAsserts.assertPeriodDurations( tracker.getCastTimeline(newStatus), @@ -112,7 +111,7 @@ public class CastTimelineTrackerTest { int[] itemIds, String[] contentIds, long[] durationsMs) { ArrayList items = new ArrayList<>(); for (int i = 0; i < contentIds.length; i++) { - MediaInfo mediaInfo = mockMediaInfo(contentIds[i], durationsMs[i]); + MediaInfo mediaInfo = getMediaInfo(contentIds[i], durationsMs[i]); MediaQueueItem item = Mockito.mock(MediaQueueItem.class); Mockito.when(item.getMedia()).thenReturn(mediaInfo); Mockito.when(item.getItemId()).thenReturn(itemIds[i]); @@ -123,10 +122,11 @@ public class CastTimelineTrackerTest { return status; } - private static MediaInfo mockMediaInfo(String contentId, long durationMs) { - MediaInfo mediaInfo = Mockito.mock(MediaInfo.class); - Mockito.when(mediaInfo.getContentId()).thenReturn(contentId); - Mockito.when(mediaInfo.getStreamDuration()).thenReturn(durationMs); - return mediaInfo; + private static MediaInfo getMediaInfo(String contentId, long durationMs) { + return new MediaInfo.Builder(contentId) + .setStreamDuration(durationMs) + .setContentType(MimeTypes.APPLICATION_MP4) + .setStreamType(MediaInfo.STREAM_TYPE_NONE) + .build(); } } diff --git a/extensions/cast/src/test/resources/robolectric.properties b/extensions/cast/src/test/resources/robolectric.properties new file mode 100644 index 0000000000..2f3210368e --- /dev/null +++ b/extensions/cast/src/test/resources/robolectric.properties @@ -0,0 +1 @@ +manifest=src/test/AndroidManifest.xml diff --git a/extensions/cronet/build.gradle b/extensions/cronet/build.gradle index 0b6f9a587c..2d25c7299c 100644 --- a/extensions/cronet/build.gradle +++ b/extensions/cronet/build.gradle @@ -39,12 +39,8 @@ dependencies { compile files('libs/cronet_api.jar') compile files('libs/cronet_impl_common_java.jar') compile files('libs/cronet_impl_native_java.jar') - androidTestCompile project(modulePrefix + 'library') - androidTestCompile project(modulePrefix + 'testutils') - androidTestCompile 'com.google.dexmaker:dexmaker:' + dexmakerVersion - androidTestCompile 'com.google.dexmaker:dexmaker-mockito:' + dexmakerVersion - androidTestCompile 'org.mockito:mockito-core:' + mockitoVersion - androidTestCompile 'com.android.support.test:runner:' + testSupportLibraryVersion + testCompile project(modulePrefix + 'library') + testCompile project(modulePrefix + 'testutils-robolectric') } ext { diff --git a/extensions/cronet/src/androidTest/AndroidManifest.xml b/extensions/cronet/src/test/AndroidManifest.xml similarity index 71% rename from extensions/cronet/src/androidTest/AndroidManifest.xml rename to extensions/cronet/src/test/AndroidManifest.xml index 453cc68478..52be9aa157 100644 --- a/extensions/cronet/src/androidTest/AndroidManifest.xml +++ b/extensions/cronet/src/test/AndroidManifest.xml @@ -18,16 +18,6 @@ xmlns:tools="http://schemas.android.com/tools" package="com.google.android.exoplayer2.ext.cronet"> - - - - - - - + diff --git a/extensions/cronet/src/androidTest/java/com/google/android/exoplayer2/ext/cronet/ByteArrayUploadDataProviderTest.java b/extensions/cronet/src/test/java/com/google/android/exoplayer2/ext/cronet/ByteArrayUploadDataProviderTest.java similarity index 90% rename from extensions/cronet/src/androidTest/java/com/google/android/exoplayer2/ext/cronet/ByteArrayUploadDataProviderTest.java rename to extensions/cronet/src/test/java/com/google/android/exoplayer2/ext/cronet/ByteArrayUploadDataProviderTest.java index 28d22b91a5..291e73fcc1 100644 --- a/extensions/cronet/src/androidTest/java/com/google/android/exoplayer2/ext/cronet/ByteArrayUploadDataProviderTest.java +++ b/extensions/cronet/src/test/java/com/google/android/exoplayer2/ext/cronet/ByteArrayUploadDataProviderTest.java @@ -19,9 +19,6 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import android.support.test.InstrumentationRegistry; -import android.support.test.runner.AndroidJUnit4; -import com.google.android.exoplayer2.testutil.MockitoUtil; import java.io.IOException; import java.nio.ByteBuffer; import java.util.Arrays; @@ -30,11 +27,11 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; -/** - * Tests for {@link ByteArrayUploadDataProvider}. - */ -@RunWith(AndroidJUnit4.class) +/** Tests for {@link ByteArrayUploadDataProvider}. */ +@RunWith(RobolectricTestRunner.class) public final class ByteArrayUploadDataProviderTest { private static final byte[] TEST_DATA = new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; @@ -45,7 +42,7 @@ public final class ByteArrayUploadDataProviderTest { @Before public void setUp() { - MockitoUtil.setUpMockito(InstrumentationRegistry.getTargetContext(), this); + MockitoAnnotations.initMocks(this); byteBuffer = ByteBuffer.allocate(TEST_DATA.length); byteArrayUploadDataProvider = new ByteArrayUploadDataProvider(TEST_DATA); } @@ -90,5 +87,4 @@ public final class ByteArrayUploadDataProviderTest { assertThat(byteBuffer.array()).isEqualTo(TEST_DATA); verify(mockUploadDataSink).onRewindSucceeded(); } - } diff --git a/extensions/cronet/src/androidTest/java/com/google/android/exoplayer2/ext/cronet/CronetDataSourceTest.java b/extensions/cronet/src/test/java/com/google/android/exoplayer2/ext/cronet/CronetDataSourceTest.java similarity index 71% rename from extensions/cronet/src/androidTest/java/com/google/android/exoplayer2/ext/cronet/CronetDataSourceTest.java rename to extensions/cronet/src/test/java/com/google/android/exoplayer2/ext/cronet/CronetDataSourceTest.java index 79be44398e..4e990cd027 100644 --- a/extensions/cronet/src/androidTest/java/com/google/android/exoplayer2/ext/cronet/CronetDataSourceTest.java +++ b/extensions/cronet/src/test/java/com/google/android/exoplayer2/ext/cronet/CronetDataSourceTest.java @@ -31,10 +31,8 @@ import static org.mockito.Mockito.when; import android.net.Uri; import android.os.ConditionVariable; -import android.support.test.InstrumentationRegistry; -import android.support.test.runner.AndroidJUnit4; +import android.os.SystemClock; import com.google.android.exoplayer2.C; -import com.google.android.exoplayer2.testutil.MockitoUtil; import com.google.android.exoplayer2.upstream.DataSpec; import com.google.android.exoplayer2.upstream.HttpDataSource; import com.google.android.exoplayer2.upstream.HttpDataSource.HttpDataSourceException; @@ -50,6 +48,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicInteger; import org.chromium.net.CronetEngine; @@ -61,13 +60,14 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; +import org.mockito.MockitoAnnotations; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.shadows.ShadowSystemClock; -/** - * Tests for {@link CronetDataSource}. - */ -@RunWith(AndroidJUnit4.class) +/** Tests for {@link CronetDataSource}. */ +@RunWith(RobolectricTestRunner.class) public final class CronetDataSourceTest { private static final int TEST_CONNECT_TIMEOUT_MS = 100; @@ -85,18 +85,11 @@ public final class CronetDataSourceTest { private UrlResponseInfo testUrlResponseInfo; @Mock private UrlRequest.Builder mockUrlRequestBuilder; - @Mock - private UrlRequest mockUrlRequest; - @Mock - private Predicate mockContentTypePredicate; - @Mock - private TransferListener mockTransferListener; - @Mock - private Clock mockClock; - @Mock - private Executor mockExecutor; - @Mock - private NetworkException mockNetworkException; + @Mock private UrlRequest mockUrlRequest; + @Mock private Predicate mockContentTypePredicate; + @Mock private TransferListener mockTransferListener; + @Mock private Executor mockExecutor; + @Mock private NetworkException mockNetworkException; @Mock private CronetEngine mockCronetEngine; private CronetDataSource dataSourceUnderTest; @@ -104,30 +97,31 @@ public final class CronetDataSourceTest { @Before public void setUp() throws Exception { - MockitoUtil.setUpMockito(InstrumentationRegistry.getTargetContext(), this); - dataSourceUnderTest = spy( - new CronetDataSource( - mockCronetEngine, - mockExecutor, - mockContentTypePredicate, - mockTransferListener, - TEST_CONNECT_TIMEOUT_MS, - TEST_READ_TIMEOUT_MS, - true, // resetTimeoutOnRedirects - mockClock, - null, - false)); + MockitoAnnotations.initMocks(this); + dataSourceUnderTest = + spy( + new CronetDataSource( + mockCronetEngine, + mockExecutor, + mockContentTypePredicate, + mockTransferListener, + TEST_CONNECT_TIMEOUT_MS, + TEST_READ_TIMEOUT_MS, + true, // resetTimeoutOnRedirects + Clock.DEFAULT, + null, + false)); when(mockContentTypePredicate.evaluate(anyString())).thenReturn(true); when(mockCronetEngine.newUrlRequestBuilder( - anyString(), any(UrlRequest.Callback.class), any(Executor.class))) + anyString(), any(UrlRequest.Callback.class), any(Executor.class))) .thenReturn(mockUrlRequestBuilder); when(mockUrlRequestBuilder.allowDirectExecutor()).thenReturn(mockUrlRequestBuilder); when(mockUrlRequestBuilder.build()).thenReturn(mockUrlRequest); mockStatusResponse(); testDataSpec = new DataSpec(Uri.parse(TEST_URL), 0, C.LENGTH_UNSET, null); - testPostDataSpec = new DataSpec( - Uri.parse(TEST_URL), TEST_POST_BODY, 0, 0, C.LENGTH_UNSET, null, 0); + testPostDataSpec = + new DataSpec(Uri.parse(TEST_URL), TEST_POST_BODY, 0, 0, C.LENGTH_UNSET, null, 0); testResponseHeader = new HashMap<>(); testResponseHeader.put("Content-Type", TEST_CONTENT_TYPE); // This value can be anything since the DataSpec is unset. @@ -173,20 +167,19 @@ public final class CronetDataSourceTest { // Prepare a mock UrlRequest to be used in the second open() call. final UrlRequest mockUrlRequest2 = mock(UrlRequest.class); when(mockUrlRequestBuilder.build()).thenReturn(mockUrlRequest2); - doAnswer(new Answer() { - @Override - public Object answer(InvocationOnMock invocation) throws Throwable { - // Invoke the callback for the previous request. - dataSourceUnderTest.onFailed( - mockUrlRequest, - testUrlResponseInfo, - mockNetworkException); - dataSourceUnderTest.onResponseStarted( - mockUrlRequest2, - testUrlResponseInfo); - return null; - } - }).when(mockUrlRequest2).start(); + doAnswer( + new Answer() { + @Override + public Object answer(InvocationOnMock invocation) throws Throwable { + // Invoke the callback for the previous request. + dataSourceUnderTest.onFailed( + mockUrlRequest, testUrlResponseInfo, mockNetworkException); + dataSourceUnderTest.onResponseStarted(mockUrlRequest2, testUrlResponseInfo); + return null; + } + }) + .when(mockUrlRequest2) + .start(); dataSourceUnderTest.open(testDataSpec); } @@ -253,8 +246,8 @@ public final class CronetDataSourceTest { @Test public void testRequestOpenFailDueToDnsFailure() { mockResponseStartFailure(); - when(mockNetworkException.getErrorCode()).thenReturn( - NetworkException.ERROR_HOSTNAME_NOT_RESOLVED); + when(mockNetworkException.getErrorCode()) + .thenReturn(NetworkException.ERROR_HOSTNAME_NOT_RESOLVED); try { dataSourceUnderTest.open(testDataSpec); @@ -524,8 +517,8 @@ public final class CronetDataSourceTest { assertThat(bytesOverRead).isEqualTo(C.RESULT_END_OF_INPUT); assertThat(returnedBuffer).isEqualTo(new byte[16]); // C.RESULT_END_OF_INPUT should not be reported though the TransferListener. - verify(mockTransferListener, never()).onBytesTransferred(dataSourceUnderTest, - C.RESULT_END_OF_INPUT); + verify(mockTransferListener, never()) + .onBytesTransferred(dataSourceUnderTest, C.RESULT_END_OF_INPUT); // There should still be only one call to read on cronet. verify(mockUrlRequest, times(1)).read(any(ByteBuffer.class)); // Check for connection not automatically closed. @@ -534,10 +527,10 @@ public final class CronetDataSourceTest { } @Test - public void testConnectTimeout() { - when(mockClock.elapsedRealtime()).thenReturn(0L); + public void testConnectTimeout() throws InterruptedException { + long startTimeMs = SystemClock.elapsedRealtime(); final ConditionVariable startCondition = buildUrlRequestStartedCondition(); - final ConditionVariable timedOutCondition = new ConditionVariable(); + final CountDownLatch timedOutLatch = new CountDownLatch(1); new Thread() { @Override @@ -551,29 +544,29 @@ public final class CronetDataSourceTest { assertThat(e.getCause() instanceof SocketTimeoutException).isTrue(); assertThat(((CronetDataSource.OpenException) e).cronetConnectionStatus) .isEqualTo(TEST_CONNECTION_STATUS); - timedOutCondition.open(); + timedOutLatch.countDown(); } } }.start(); startCondition.block(); // We should still be trying to open. - assertThat(timedOutCondition.block(50)).isFalse(); + assertNotCountedDown(timedOutLatch); // We should still be trying to open as we approach the timeout. - when(mockClock.elapsedRealtime()).thenReturn((long) TEST_CONNECT_TIMEOUT_MS - 1); - assertThat(timedOutCondition.block(50)).isFalse(); + ShadowSystemClock.setCurrentTimeMillis(startTimeMs + TEST_CONNECT_TIMEOUT_MS - 1); + assertNotCountedDown(timedOutLatch); // Now we timeout. - when(mockClock.elapsedRealtime()).thenReturn((long) TEST_CONNECT_TIMEOUT_MS); - timedOutCondition.block(); + ShadowSystemClock.setCurrentTimeMillis(startTimeMs + TEST_CONNECT_TIMEOUT_MS + 10); + timedOutLatch.await(); verify(mockTransferListener, never()).onTransferStart(dataSourceUnderTest, testDataSpec); } @Test - public void testConnectInterrupted() { - when(mockClock.elapsedRealtime()).thenReturn(0L); + public void testConnectInterrupted() throws InterruptedException { + long startTimeMs = SystemClock.elapsedRealtime(); final ConditionVariable startCondition = buildUrlRequestStartedCondition(); - final ConditionVariable timedOutCondition = new ConditionVariable(); + final CountDownLatch timedOutLatch = new CountDownLatch(1); Thread thread = new Thread() { @@ -588,7 +581,7 @@ public final class CronetDataSourceTest { assertThat(e.getCause() instanceof CronetDataSource.InterruptedIOException).isTrue(); assertThat(((CronetDataSource.OpenException) e).cronetConnectionStatus) .isEqualTo(TEST_INVALID_CONNECTION_STATUS); - timedOutCondition.open(); + timedOutLatch.countDown(); } } }; @@ -596,29 +589,29 @@ public final class CronetDataSourceTest { startCondition.block(); // We should still be trying to open. - assertThat(timedOutCondition.block(50)).isFalse(); + assertNotCountedDown(timedOutLatch); // We should still be trying to open as we approach the timeout. - when(mockClock.elapsedRealtime()).thenReturn((long) TEST_CONNECT_TIMEOUT_MS - 1); - assertThat(timedOutCondition.block(50)).isFalse(); + ShadowSystemClock.setCurrentTimeMillis(startTimeMs + TEST_CONNECT_TIMEOUT_MS - 1); + assertNotCountedDown(timedOutLatch); // Now we interrupt. thread.interrupt(); - timedOutCondition.block(); + timedOutLatch.await(); verify(mockTransferListener, never()).onTransferStart(dataSourceUnderTest, testDataSpec); } @Test - public void testConnectResponseBeforeTimeout() { - when(mockClock.elapsedRealtime()).thenReturn(0L); + public void testConnectResponseBeforeTimeout() throws InterruptedException { + long startTimeMs = SystemClock.elapsedRealtime(); final ConditionVariable startCondition = buildUrlRequestStartedCondition(); - final ConditionVariable openCondition = new ConditionVariable(); + final CountDownLatch openLatch = new CountDownLatch(1); new Thread() { @Override public void run() { try { dataSourceUnderTest.open(testDataSpec); - openCondition.open(); + openLatch.countDown(); } catch (HttpDataSourceException e) { fail(); } @@ -627,20 +620,20 @@ public final class CronetDataSourceTest { startCondition.block(); // We should still be trying to open. - assertThat(openCondition.block(50)).isFalse(); + assertNotCountedDown(openLatch); // We should still be trying to open as we approach the timeout. - when(mockClock.elapsedRealtime()).thenReturn((long) TEST_CONNECT_TIMEOUT_MS - 1); - assertThat(openCondition.block(50)).isFalse(); + ShadowSystemClock.setCurrentTimeMillis(startTimeMs + TEST_CONNECT_TIMEOUT_MS - 1); + assertNotCountedDown(openLatch); // The response arrives just in time. dataSourceUnderTest.onResponseStarted(mockUrlRequest, testUrlResponseInfo); - openCondition.block(); + openLatch.await(); } @Test public void testRedirectIncreasesConnectionTimeout() throws InterruptedException { - when(mockClock.elapsedRealtime()).thenReturn(0L); + long startTimeMs = SystemClock.elapsedRealtime(); final ConditionVariable startCondition = buildUrlRequestStartedCondition(); - final ConditionVariable timedOutCondition = new ConditionVariable(); + final CountDownLatch timedOutLatch = new CountDownLatch(1); final AtomicInteger openExceptions = new AtomicInteger(0); new Thread() { @@ -654,40 +647,36 @@ public final class CronetDataSourceTest { assertThat(e instanceof CronetDataSource.OpenException).isTrue(); assertThat(e.getCause() instanceof SocketTimeoutException).isTrue(); openExceptions.getAndIncrement(); - timedOutCondition.open(); + timedOutLatch.countDown(); } } }.start(); startCondition.block(); // We should still be trying to open. - assertThat(timedOutCondition.block(50)).isFalse(); + assertNotCountedDown(timedOutLatch); // We should still be trying to open as we approach the timeout. - when(mockClock.elapsedRealtime()).thenReturn((long) TEST_CONNECT_TIMEOUT_MS - 1); - assertThat(timedOutCondition.block(50)).isFalse(); + ShadowSystemClock.setCurrentTimeMillis(startTimeMs + TEST_CONNECT_TIMEOUT_MS - 1); + assertNotCountedDown(timedOutLatch); // A redirect arrives just in time. - dataSourceUnderTest.onRedirectReceived(mockUrlRequest, testUrlResponseInfo, - "RandomRedirectedUrl1"); + dataSourceUnderTest.onRedirectReceived( + mockUrlRequest, testUrlResponseInfo, "RandomRedirectedUrl1"); long newTimeoutMs = 2 * TEST_CONNECT_TIMEOUT_MS - 1; - when(mockClock.elapsedRealtime()).thenReturn(newTimeoutMs - 1); - // Give the thread some time to run. - assertThat(timedOutCondition.block(newTimeoutMs)).isFalse(); + ShadowSystemClock.setCurrentTimeMillis(startTimeMs + newTimeoutMs - 1); // We should still be trying to open as we approach the new timeout. - assertThat(timedOutCondition.block(50)).isFalse(); + assertNotCountedDown(timedOutLatch); // A redirect arrives just in time. - dataSourceUnderTest.onRedirectReceived(mockUrlRequest, testUrlResponseInfo, - "RandomRedirectedUrl2"); + dataSourceUnderTest.onRedirectReceived( + mockUrlRequest, testUrlResponseInfo, "RandomRedirectedUrl2"); newTimeoutMs = 3 * TEST_CONNECT_TIMEOUT_MS - 2; - when(mockClock.elapsedRealtime()).thenReturn(newTimeoutMs - 1); - // Give the thread some time to run. - assertThat(timedOutCondition.block(newTimeoutMs)).isFalse(); + ShadowSystemClock.setCurrentTimeMillis(startTimeMs + newTimeoutMs - 1); // We should still be trying to open as we approach the new timeout. - assertThat(timedOutCondition.block(50)).isFalse(); + assertNotCountedDown(timedOutLatch); // Now we timeout. - when(mockClock.elapsedRealtime()).thenReturn(newTimeoutMs); - timedOutCondition.block(); + ShadowSystemClock.setCurrentTimeMillis(startTimeMs + newTimeoutMs + 10); + timedOutLatch.await(); verify(mockTransferListener, never()).onTransferStart(dataSourceUnderTest, testDataSpec); assertThat(openExceptions.get()).isEqualTo(1); @@ -707,20 +696,22 @@ public final class CronetDataSourceTest { } @Test - public void testRedirectParseAndAttachCookie_dataSourceHandlesSetCookie_andPreservesOriginalRequestHeaders() - throws HttpDataSourceException { - dataSourceUnderTest = spy( - new CronetDataSource( - mockCronetEngine, - mockExecutor, - mockContentTypePredicate, - mockTransferListener, - TEST_CONNECT_TIMEOUT_MS, - TEST_READ_TIMEOUT_MS, - true, // resetTimeoutOnRedirects - mockClock, - null, - true)); + public void + testRedirectParseAndAttachCookie_dataSourceHandlesSetCookie_andPreservesOriginalRequestHeaders() + throws HttpDataSourceException { + dataSourceUnderTest = + spy( + new CronetDataSource( + mockCronetEngine, + mockExecutor, + mockContentTypePredicate, + mockTransferListener, + TEST_CONNECT_TIMEOUT_MS, + TEST_READ_TIMEOUT_MS, + true, // resetTimeoutOnRedirects + Clock.DEFAULT, + null, + true)); dataSourceUnderTest.setRequestProperty("Content-Type", TEST_CONTENT_TYPE); mockSingleRedirectSuccess(); @@ -736,21 +727,23 @@ public final class CronetDataSourceTest { } @Test - public void testRedirectParseAndAttachCookie_dataSourceHandlesSetCookie_andPreservesOriginalRequestHeadersIncludingByteRangeHeader() - throws HttpDataSourceException { + public void + testRedirectParseAndAttachCookie_dataSourceHandlesSetCookie_andPreservesOriginalRequestHeadersIncludingByteRangeHeader() + throws HttpDataSourceException { testDataSpec = new DataSpec(Uri.parse(TEST_URL), 1000, 5000, null); - dataSourceUnderTest = spy( - new CronetDataSource( - mockCronetEngine, - mockExecutor, - mockContentTypePredicate, - mockTransferListener, - TEST_CONNECT_TIMEOUT_MS, - TEST_READ_TIMEOUT_MS, - true, // resetTimeoutOnRedirects - mockClock, - null, - true)); + dataSourceUnderTest = + spy( + new CronetDataSource( + mockCronetEngine, + mockExecutor, + mockContentTypePredicate, + mockTransferListener, + TEST_CONNECT_TIMEOUT_MS, + TEST_READ_TIMEOUT_MS, + true, // resetTimeoutOnRedirects + Clock.DEFAULT, + null, + true)); dataSourceUnderTest.setRequestProperty("Content-Type", TEST_CONTENT_TYPE); mockSingleRedirectSuccess(); @@ -778,18 +771,19 @@ public final class CronetDataSourceTest { @Test public void testRedirectNoSetCookieFollowsRedirect_dataSourceHandlesSetCookie() throws HttpDataSourceException { - dataSourceUnderTest = spy( - new CronetDataSource( - mockCronetEngine, - mockExecutor, - mockContentTypePredicate, - mockTransferListener, - TEST_CONNECT_TIMEOUT_MS, - TEST_READ_TIMEOUT_MS, - true, // resetTimeoutOnRedirects - mockClock, - null, - true)); + dataSourceUnderTest = + spy( + new CronetDataSource( + mockCronetEngine, + mockExecutor, + mockContentTypePredicate, + mockTransferListener, + TEST_CONNECT_TIMEOUT_MS, + TEST_READ_TIMEOUT_MS, + true, // resetTimeoutOnRedirects + Clock.DEFAULT, + null, + true)); mockSingleRedirectSuccess(); mockFollowRedirectSuccess(); @@ -804,8 +798,9 @@ public final class CronetDataSourceTest { // Make mockTransferListener throw an exception in CronetDataSource.close(). Ensure that // the subsequent open() call succeeds. - doThrow(new NullPointerException()).when(mockTransferListener).onTransferEnd( - dataSourceUnderTest); + doThrow(new NullPointerException()) + .when(mockTransferListener) + .onTransferEnd(dataSourceUnderTest); dataSourceUnderTest.open(testDataSpec); try { dataSourceUnderTest.close(); @@ -833,13 +828,12 @@ public final class CronetDataSourceTest { } @Test - public void testReadInterrupted() throws HttpDataSourceException { - when(mockClock.elapsedRealtime()).thenReturn(0L); + public void testReadInterrupted() throws HttpDataSourceException, InterruptedException { mockResponseStartSuccess(); dataSourceUnderTest.open(testDataSpec); final ConditionVariable startCondition = buildReadStartedCondition(); - final ConditionVariable timedOutCondition = new ConditionVariable(); + final CountDownLatch timedOutLatch = new CountDownLatch(1); byte[] returnedBuffer = new byte[8]; Thread thread = new Thread() { @@ -851,17 +845,17 @@ public final class CronetDataSourceTest { } catch (HttpDataSourceException e) { // Expected. assertThat(e.getCause() instanceof CronetDataSource.InterruptedIOException).isTrue(); - timedOutCondition.open(); + timedOutLatch.countDown(); } } }; thread.start(); startCondition.block(); - assertThat(timedOutCondition.block(50)).isFalse(); + assertNotCountedDown(timedOutLatch); // Now we interrupt. thread.interrupt(); - timedOutCondition.block(); + timedOutLatch.await(); } @Test @@ -876,122 +870,135 @@ public final class CronetDataSourceTest { // Helper methods. private void mockStatusResponse() { - doAnswer(new Answer() { - @Override - public Object answer(InvocationOnMock invocation) throws Throwable { - UrlRequest.StatusListener statusListener = - (UrlRequest.StatusListener) invocation.getArguments()[0]; - statusListener.onStatus(TEST_CONNECTION_STATUS); - return null; - } - }).when(mockUrlRequest).getStatus(any(UrlRequest.StatusListener.class)); + doAnswer( + new Answer() { + @Override + public Object answer(InvocationOnMock invocation) throws Throwable { + UrlRequest.StatusListener statusListener = + (UrlRequest.StatusListener) invocation.getArguments()[0]; + statusListener.onStatus(TEST_CONNECTION_STATUS); + return null; + } + }) + .when(mockUrlRequest) + .getStatus(any(UrlRequest.StatusListener.class)); } private void mockResponseStartSuccess() { - doAnswer(new Answer() { - @Override - public Object answer(InvocationOnMock invocation) throws Throwable { - dataSourceUnderTest.onResponseStarted( - mockUrlRequest, - testUrlResponseInfo); - return null; - } - }).when(mockUrlRequest).start(); + doAnswer( + new Answer() { + @Override + public Object answer(InvocationOnMock invocation) throws Throwable { + dataSourceUnderTest.onResponseStarted(mockUrlRequest, testUrlResponseInfo); + return null; + } + }) + .when(mockUrlRequest) + .start(); } private void mockResponseStartRedirect() { - doAnswer(new Answer() { - @Override - public Object answer(InvocationOnMock invocation) throws Throwable { - dataSourceUnderTest.onRedirectReceived( - mockUrlRequest, - createUrlResponseInfo(307), // statusCode - "http://redirect.location.com"); - return null; - } - }).when(mockUrlRequest).start(); + doAnswer( + new Answer() { + @Override + public Object answer(InvocationOnMock invocation) throws Throwable { + dataSourceUnderTest.onRedirectReceived( + mockUrlRequest, + createUrlResponseInfo(307), // statusCode + "http://redirect.location.com"); + return null; + } + }) + .when(mockUrlRequest) + .start(); } private void mockSingleRedirectSuccess() { - doAnswer(new Answer() { - @Override - public Object answer(InvocationOnMock invocation) throws Throwable { - if (!redirectCalled) { - redirectCalled = true; - dataSourceUnderTest.onRedirectReceived( - mockUrlRequest, - createUrlResponseInfoWithUrl("http://example.com/video", 300), - "http://example.com/video/redirect"); - } else { - dataSourceUnderTest.onResponseStarted( - mockUrlRequest, - testUrlResponseInfo); - } - return null; - } - }).when(mockUrlRequest).start(); + doAnswer( + new Answer() { + @Override + public Object answer(InvocationOnMock invocation) throws Throwable { + if (!redirectCalled) { + redirectCalled = true; + dataSourceUnderTest.onRedirectReceived( + mockUrlRequest, + createUrlResponseInfoWithUrl("http://example.com/video", 300), + "http://example.com/video/redirect"); + } else { + dataSourceUnderTest.onResponseStarted(mockUrlRequest, testUrlResponseInfo); + } + return null; + } + }) + .when(mockUrlRequest) + .start(); } private void mockFollowRedirectSuccess() { - doAnswer(new Answer() { - @Override - public Object answer(InvocationOnMock invocation) throws Throwable { - dataSourceUnderTest.onResponseStarted( - mockUrlRequest, - testUrlResponseInfo); - return null; - } - }).when(mockUrlRequest).followRedirect(); + doAnswer( + new Answer() { + @Override + public Object answer(InvocationOnMock invocation) throws Throwable { + dataSourceUnderTest.onResponseStarted(mockUrlRequest, testUrlResponseInfo); + return null; + } + }) + .when(mockUrlRequest) + .followRedirect(); } private void mockResponseStartFailure() { - doAnswer(new Answer() { - @Override - public Object answer(InvocationOnMock invocation) throws Throwable { - dataSourceUnderTest.onFailed( - mockUrlRequest, - createUrlResponseInfo(500), // statusCode - mockNetworkException); - return null; - } - }).when(mockUrlRequest).start(); + doAnswer( + new Answer() { + @Override + public Object answer(InvocationOnMock invocation) throws Throwable { + dataSourceUnderTest.onFailed( + mockUrlRequest, + createUrlResponseInfo(500), // statusCode + mockNetworkException); + return null; + } + }) + .when(mockUrlRequest) + .start(); } private void mockReadSuccess(int position, int length) { final int[] positionAndRemaining = new int[] {position, length}; - doAnswer(new Answer() { - @Override - public Void answer(InvocationOnMock invocation) throws Throwable { - if (positionAndRemaining[1] == 0) { - dataSourceUnderTest.onSucceeded(mockUrlRequest, testUrlResponseInfo); - } else { - ByteBuffer inputBuffer = (ByteBuffer) invocation.getArguments()[0]; - int readLength = Math.min(positionAndRemaining[1], inputBuffer.remaining()); - inputBuffer.put(buildTestDataBuffer(positionAndRemaining[0], readLength)); - positionAndRemaining[0] += readLength; - positionAndRemaining[1] -= readLength; - dataSourceUnderTest.onReadCompleted( - mockUrlRequest, - testUrlResponseInfo, - inputBuffer); - } - return null; - } - }).when(mockUrlRequest).read(any(ByteBuffer.class)); + doAnswer( + new Answer() { + @Override + public Void answer(InvocationOnMock invocation) throws Throwable { + if (positionAndRemaining[1] == 0) { + dataSourceUnderTest.onSucceeded(mockUrlRequest, testUrlResponseInfo); + } else { + ByteBuffer inputBuffer = (ByteBuffer) invocation.getArguments()[0]; + int readLength = Math.min(positionAndRemaining[1], inputBuffer.remaining()); + inputBuffer.put(buildTestDataBuffer(positionAndRemaining[0], readLength)); + positionAndRemaining[0] += readLength; + positionAndRemaining[1] -= readLength; + dataSourceUnderTest.onReadCompleted( + mockUrlRequest, testUrlResponseInfo, inputBuffer); + } + return null; + } + }) + .when(mockUrlRequest) + .read(any(ByteBuffer.class)); } private void mockReadFailure() { doAnswer( - new Answer() { - @Override - public Object answer(InvocationOnMock invocation) throws Throwable { - dataSourceUnderTest.onFailed( - mockUrlRequest, - createUrlResponseInfo(500), // statusCode - mockNetworkException); - return null; - } - }) + new Answer() { + @Override + public Object answer(InvocationOnMock invocation) throws Throwable { + dataSourceUnderTest.onFailed( + mockUrlRequest, + createUrlResponseInfo(500), // statusCode + mockNetworkException); + return null; + } + }) .when(mockUrlRequest) .read(any(ByteBuffer.class)); } @@ -999,13 +1006,13 @@ public final class CronetDataSourceTest { private ConditionVariable buildReadStartedCondition() { final ConditionVariable startedCondition = new ConditionVariable(); doAnswer( - new Answer() { - @Override - public Object answer(InvocationOnMock invocation) throws Throwable { - startedCondition.open(); - return null; - } - }) + new Answer() { + @Override + public Object answer(InvocationOnMock invocation) throws Throwable { + startedCondition.open(); + return null; + } + }) .when(mockUrlRequest) .read(any(ByteBuffer.class)); return startedCondition; @@ -1013,16 +1020,26 @@ public final class CronetDataSourceTest { private ConditionVariable buildUrlRequestStartedCondition() { final ConditionVariable startedCondition = new ConditionVariable(); - doAnswer(new Answer() { - @Override - public Object answer(InvocationOnMock invocation) throws Throwable { - startedCondition.open(); - return null; - } - }).when(mockUrlRequest).start(); + doAnswer( + new Answer() { + @Override + public Object answer(InvocationOnMock invocation) throws Throwable { + startedCondition.open(); + return null; + } + }) + .when(mockUrlRequest) + .start(); return startedCondition; } + private void assertNotCountedDown(CountDownLatch countDownLatch) throws InterruptedException { + // We are asserting that another thread does not count down the latch. We therefore sleep some + // time to give the other thread the chance to fail this test. + Thread.sleep(50); + assertThat(countDownLatch.getCount()).isGreaterThan(0L); + } + private static byte[] buildTestDataArray(int position, int length) { return buildTestDataBuffer(position, length).array(); } @@ -1045,5 +1062,4 @@ public final class CronetDataSourceTest { testBuffer.flip(); return testBuffer; } - } diff --git a/extensions/cronet/src/test/resources/robolectric.properties b/extensions/cronet/src/test/resources/robolectric.properties new file mode 100644 index 0000000000..2f3210368e --- /dev/null +++ b/extensions/cronet/src/test/resources/robolectric.properties @@ -0,0 +1 @@ +manifest=src/test/AndroidManifest.xml diff --git a/library/core/build.gradle b/library/core/build.gradle index a87e11065f..3d655ba543 100644 --- a/library/core/build.gradle +++ b/library/core/build.gradle @@ -31,6 +31,7 @@ android { } test { java.srcDirs += "../../testutils/src/main/java/" + java.srcDirs += "../../testutils_robolectric/src/main/java/" } } diff --git a/library/core/src/androidTest/java/com/google/android/exoplayer2/upstream/ContentDataSourceTest.java b/library/core/src/androidTest/java/com/google/android/exoplayer2/upstream/ContentDataSourceTest.java index 83a978219e..3465393853 100644 --- a/library/core/src/androidTest/java/com/google/android/exoplayer2/upstream/ContentDataSourceTest.java +++ b/library/core/src/androidTest/java/com/google/android/exoplayer2/upstream/ContentDataSourceTest.java @@ -89,7 +89,7 @@ public final class ContentDataSourceTest extends InstrumentationTestCase { ContentDataSource dataSource = new ContentDataSource(instrumentation.getContext()); try { DataSpec dataSpec = new DataSpec(contentUri, offset, length, null); - byte[] completeData = TestUtil.getByteArray(instrumentation, DATA_PATH); + byte[] completeData = TestUtil.getByteArray(instrumentation.getContext(), DATA_PATH); byte[] expectedData = Arrays.copyOfRange(completeData, offset, length == C.LENGTH_UNSET ? completeData.length : offset + length); TestUtil.assertDataSourceContent(dataSource, dataSpec, expectedData, !pipeMode); diff --git a/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java b/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java index 8477a215d3..b1ddcdb207 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java @@ -42,6 +42,7 @@ import com.google.android.exoplayer2.testutil.FakeTimeline; import com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinition; import com.google.android.exoplayer2.testutil.FakeTrackSelection; import com.google.android.exoplayer2.testutil.FakeTrackSelector; +import com.google.android.exoplayer2.testutil.RobolectricUtil; import com.google.android.exoplayer2.upstream.Allocator; import java.io.IOException; import java.util.ArrayList; diff --git a/library/core/src/test/java/com/google/android/exoplayer2/drm/OfflineLicenseHelperTest.java b/library/core/src/test/java/com/google/android/exoplayer2/drm/OfflineLicenseHelperTest.java index f67301f017..6f62b7fcfc 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/drm/OfflineLicenseHelperTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/drm/OfflineLicenseHelperTest.java @@ -22,8 +22,8 @@ import static org.mockito.Mockito.when; import android.util.Pair; import com.google.android.exoplayer2.C; -import com.google.android.exoplayer2.RobolectricUtil; import com.google.android.exoplayer2.drm.DrmInitData.SchemeData; +import com.google.android.exoplayer2.testutil.RobolectricUtil; import java.util.HashMap; import org.junit.After; import org.junit.Before; diff --git a/library/core/src/test/java/com/google/android/exoplayer2/source/ClippingMediaSourceTest.java b/library/core/src/test/java/com/google/android/exoplayer2/source/ClippingMediaSourceTest.java index 1e0d8681c5..a4aa3eb938 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/source/ClippingMediaSourceTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/source/ClippingMediaSourceTest.java @@ -20,7 +20,6 @@ import static org.junit.Assert.fail; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.Player; -import com.google.android.exoplayer2.RobolectricUtil; import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.Timeline.Period; import com.google.android.exoplayer2.Timeline.Window; @@ -29,6 +28,7 @@ import com.google.android.exoplayer2.testutil.FakeMediaSource; import com.google.android.exoplayer2.testutil.FakeTimeline; import com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinition; import com.google.android.exoplayer2.testutil.MediaSourceTestRunner; +import com.google.android.exoplayer2.testutil.RobolectricUtil; import com.google.android.exoplayer2.testutil.TimelineAsserts; import java.io.IOException; import org.junit.Before; diff --git a/library/core/src/test/java/com/google/android/exoplayer2/source/ConcatenatingMediaSourceTest.java b/library/core/src/test/java/com/google/android/exoplayer2/source/ConcatenatingMediaSourceTest.java index ccc3ddea46..465e08b5d2 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/source/ConcatenatingMediaSourceTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/source/ConcatenatingMediaSourceTest.java @@ -19,7 +19,6 @@ import static com.google.common.truth.Truth.assertThat; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.Player; -import com.google.android.exoplayer2.RobolectricUtil; import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId; import com.google.android.exoplayer2.testutil.FakeMediaSource; @@ -27,6 +26,7 @@ import com.google.android.exoplayer2.testutil.FakeShuffleOrder; import com.google.android.exoplayer2.testutil.FakeTimeline; import com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinition; import com.google.android.exoplayer2.testutil.MediaSourceTestRunner; +import com.google.android.exoplayer2.testutil.RobolectricUtil; import com.google.android.exoplayer2.testutil.TimelineAsserts; import java.io.IOException; import org.junit.Test; diff --git a/library/core/src/test/java/com/google/android/exoplayer2/source/DynamicConcatenatingMediaSourceTest.java b/library/core/src/test/java/com/google/android/exoplayer2/source/DynamicConcatenatingMediaSourceTest.java index c1537c50d3..24f1ddd5ed 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/source/DynamicConcatenatingMediaSourceTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/source/DynamicConcatenatingMediaSourceTest.java @@ -24,7 +24,6 @@ import android.os.Handler; import android.os.HandlerThread; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.Player; -import com.google.android.exoplayer2.RobolectricUtil; import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId; import com.google.android.exoplayer2.testutil.FakeMediaSource; @@ -32,6 +31,7 @@ import com.google.android.exoplayer2.testutil.FakeShuffleOrder; import com.google.android.exoplayer2.testutil.FakeTimeline; import com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinition; import com.google.android.exoplayer2.testutil.MediaSourceTestRunner; +import com.google.android.exoplayer2.testutil.RobolectricUtil; import com.google.android.exoplayer2.testutil.TimelineAsserts; import java.io.IOException; import java.util.Arrays; diff --git a/library/core/src/test/java/com/google/android/exoplayer2/source/LoopingMediaSourceTest.java b/library/core/src/test/java/com/google/android/exoplayer2/source/LoopingMediaSourceTest.java index f0b4772422..6aa710aff4 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/source/LoopingMediaSourceTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/source/LoopingMediaSourceTest.java @@ -17,12 +17,12 @@ package com.google.android.exoplayer2.source; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.Player; -import com.google.android.exoplayer2.RobolectricUtil; import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.testutil.FakeMediaSource; import com.google.android.exoplayer2.testutil.FakeTimeline; import com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinition; import com.google.android.exoplayer2.testutil.MediaSourceTestRunner; +import com.google.android.exoplayer2.testutil.RobolectricUtil; import com.google.android.exoplayer2.testutil.TimelineAsserts; import java.io.IOException; import org.junit.Before; diff --git a/library/core/src/test/java/com/google/android/exoplayer2/source/MergingMediaSourceTest.java b/library/core/src/test/java/com/google/android/exoplayer2/source/MergingMediaSourceTest.java index b03a76c23e..839492f196 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/source/MergingMediaSourceTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/source/MergingMediaSourceTest.java @@ -19,13 +19,13 @@ import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.fail; import com.google.android.exoplayer2.C; -import com.google.android.exoplayer2.RobolectricUtil; import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.source.MergingMediaSource.IllegalMergeException; import com.google.android.exoplayer2.testutil.FakeMediaSource; import com.google.android.exoplayer2.testutil.FakeTimeline; import com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinition; import com.google.android.exoplayer2.testutil.MediaSourceTestRunner; +import com.google.android.exoplayer2.testutil.RobolectricUtil; import java.io.IOException; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/library/dash/build.gradle b/library/dash/build.gradle index 99441a2849..6cf6f64443 100644 --- a/library/dash/build.gradle +++ b/library/dash/build.gradle @@ -35,15 +35,7 @@ android { dependencies { compile project(modulePrefix + 'library-core') compile 'com.android.support:support-annotations:' + supportLibraryVersion - androidTestCompile project(modulePrefix + 'testutils') - androidTestCompile 'com.google.dexmaker:dexmaker:' + dexmakerVersion - androidTestCompile 'com.google.dexmaker:dexmaker-mockito:' + dexmakerVersion - androidTestCompile 'org.mockito:mockito-core:' + mockitoVersion - testCompile project(modulePrefix + 'testutils') - testCompile 'com.google.truth:truth:' + truthVersion - testCompile 'junit:junit:' + junitVersion - testCompile 'org.mockito:mockito-core:' + mockitoVersion - testCompile 'org.robolectric:robolectric:' + robolectricVersion + testCompile project(modulePrefix + 'testutils-robolectric') } ext { diff --git a/library/dash/src/androidTest/java/com/google/android/exoplayer2/source/dash/offline/DashDownloadTestData.java b/library/dash/src/androidTest/java/com/google/android/exoplayer2/source/dash/offline/DashDownloadTestData.java deleted file mode 100644 index 50752c8a72..0000000000 --- a/library/dash/src/androidTest/java/com/google/android/exoplayer2/source/dash/offline/DashDownloadTestData.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright (C) 2017 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.dash.offline; - -import android.net.Uri; - -/** - * Data for DASH downloading tests. - */ -/* package */ interface DashDownloadTestData { - - Uri TEST_MPD_URI = Uri.parse("test.mpd"); - - byte[] TEST_MPD = - ("\n" - + "\n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - // Bounded range data - + " \n" - // Unbounded range data - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - // This segment list has a 1 second offset to make sure the progressive download order - + " \n" - + " \n" - + " \n" // 1s offset - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + "").getBytes(); - - byte[] TEST_MPD_NO_INDEX = - ("\n" - + "\n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + "").getBytes(); -} diff --git a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaSource.java b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaSource.java index eb9b18512c..98783ac93e 100644 --- a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaSource.java +++ b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaSource.java @@ -50,6 +50,7 @@ import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.nio.charset.Charset; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Locale; @@ -1113,7 +1114,9 @@ public final class DashMediaSource implements MediaSource { @Override public Long parse(Uri uri, InputStream inputStream) throws IOException { - String firstLine = new BufferedReader(new InputStreamReader(inputStream)).readLine(); + String firstLine = + new BufferedReader(new InputStreamReader(inputStream, Charset.forName(C.UTF8_NAME))) + .readLine(); try { Matcher matcher = TIMESTAMP_WITH_TIMEZONE_PATTERN.matcher(firstLine); if (!matcher.matches()) { diff --git a/library/dash/src/androidTest/AndroidManifest.xml b/library/dash/src/test/AndroidManifest.xml similarity index 71% rename from library/dash/src/androidTest/AndroidManifest.xml rename to library/dash/src/test/AndroidManifest.xml index 39596a8165..eecf596b92 100644 --- a/library/dash/src/androidTest/AndroidManifest.xml +++ b/library/dash/src/test/AndroidManifest.xml @@ -18,16 +18,6 @@ xmlns:tools="http://schemas.android.com/tools" package="com.google.android.exoplayer2.source.dash.test"> - - - - - - - + diff --git a/library/dash/src/androidTest/assets/sample_mpd_1 b/library/dash/src/test/assets/sample_mpd_1 similarity index 100% rename from library/dash/src/androidTest/assets/sample_mpd_1 rename to library/dash/src/test/assets/sample_mpd_1 diff --git a/library/dash/src/androidTest/assets/sample_mpd_2_unknown_mime_type b/library/dash/src/test/assets/sample_mpd_2_unknown_mime_type similarity index 100% rename from library/dash/src/androidTest/assets/sample_mpd_2_unknown_mime_type rename to library/dash/src/test/assets/sample_mpd_2_unknown_mime_type diff --git a/library/dash/src/androidTest/assets/sample_mpd_3_segment_template b/library/dash/src/test/assets/sample_mpd_3_segment_template similarity index 100% rename from library/dash/src/androidTest/assets/sample_mpd_3_segment_template rename to library/dash/src/test/assets/sample_mpd_3_segment_template diff --git a/library/dash/src/androidTest/assets/sample_mpd_4_event_stream b/library/dash/src/test/assets/sample_mpd_4_event_stream similarity index 100% rename from library/dash/src/androidTest/assets/sample_mpd_4_event_stream rename to library/dash/src/test/assets/sample_mpd_4_event_stream diff --git a/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/DashMediaSourceTest.java b/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/DashMediaSourceTest.java new file mode 100644 index 0000000000..1c440c70be --- /dev/null +++ b/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/DashMediaSourceTest.java @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2017 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.dash; + +import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.fail; + +import com.google.android.exoplayer2.ParserException; +import com.google.android.exoplayer2.upstream.ParsingLoadable; +import com.google.android.exoplayer2.util.Util; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; + +/** Unit test for {@link DashMediaSource}. */ +@RunWith(RobolectricTestRunner.class) +public final class DashMediaSourceTest { + + @Test + public void testIso8601ParserParse() throws IOException { + DashMediaSource.Iso8601Parser parser = new DashMediaSource.Iso8601Parser(); + // UTC. + assertParseStringToLong(1512381697000L, parser, "2017-12-04T10:01:37Z"); + assertParseStringToLong(1512381697000L, parser, "2017-12-04T10:01:37+00:00"); + assertParseStringToLong(1512381697000L, parser, "2017-12-04T10:01:37+0000"); + assertParseStringToLong(1512381697000L, parser, "2017-12-04T10:01:37+00"); + // Positive timezone offsets. + assertParseStringToLong(1512381697000L - 4980000L, parser, "2017-12-04T10:01:37+01:23"); + assertParseStringToLong(1512381697000L - 4980000L, parser, "2017-12-04T10:01:37+0123"); + assertParseStringToLong(1512381697000L - 3600000L, parser, "2017-12-04T10:01:37+01"); + // Negative timezone offsets with minus character. + assertParseStringToLong(1512381697000L + 4980000L, parser, "2017-12-04T10:01:37-01:23"); + assertParseStringToLong(1512381697000L + 4980000L, parser, "2017-12-04T10:01:37-0123"); + assertParseStringToLong(1512381697000L + 3600000L, parser, "2017-12-04T10:01:37-01:00"); + assertParseStringToLong(1512381697000L + 3600000L, parser, "2017-12-04T10:01:37-0100"); + assertParseStringToLong(1512381697000L + 3600000L, parser, "2017-12-04T10:01:37-01"); + // Negative timezone offsets with hyphen character. + assertParseStringToLong(1512381697000L + 4980000L, parser, "2017-12-04T10:01:37−01:23"); + assertParseStringToLong(1512381697000L + 4980000L, parser, "2017-12-04T10:01:37−0123"); + assertParseStringToLong(1512381697000L + 3600000L, parser, "2017-12-04T10:01:37−01:00"); + assertParseStringToLong(1512381697000L + 3600000L, parser, "2017-12-04T10:01:37−0100"); + assertParseStringToLong(1512381697000L + 3600000L, parser, "2017-12-04T10:01:37−01"); + } + + @Test + public void testIso8601ParserParseMissingTimezone() throws IOException { + DashMediaSource.Iso8601Parser parser = new DashMediaSource.Iso8601Parser(); + try { + assertParseStringToLong(0, parser, "2017-12-04T10:01:37"); + fail(); + } catch (ParserException e) { + // Expected. + } + } + + private static void assertParseStringToLong( + long expected, ParsingLoadable.Parser parser, String data) throws IOException { + long actual = parser.parse(null, new ByteArrayInputStream(Util.getUtf8Bytes(data))); + assertThat(actual).isEqualTo(expected); + } +} diff --git a/library/dash/src/androidTest/java/com/google/android/exoplayer2/source/dash/DashUtilTest.java b/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/DashUtilTest.java similarity index 81% rename from library/dash/src/androidTest/java/com/google/android/exoplayer2/source/dash/DashUtilTest.java rename to library/dash/src/test/java/com/google/android/exoplayer2/source/dash/DashUtilTest.java index 4ddbf429ab..15fa3b3355 100644 --- a/library/dash/src/androidTest/java/com/google/android/exoplayer2/source/dash/DashUtilTest.java +++ b/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/DashUtilTest.java @@ -28,33 +28,38 @@ import com.google.android.exoplayer2.source.dash.manifest.SegmentBase.SingleSegm import com.google.android.exoplayer2.upstream.DummyDataSource; import com.google.android.exoplayer2.util.MimeTypes; import java.util.Arrays; -import junit.framework.TestCase; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; -/** - * Unit tests for {@link DashUtil}. - */ -public final class DashUtilTest extends TestCase { +/** Unit tests for {@link DashUtil}. */ +@RunWith(RobolectricTestRunner.class) +public final class DashUtilTest { + @Test public void testLoadDrmInitDataFromManifest() throws Exception { Period period = newPeriod(newAdaptationSets(newRepresentations(newDrmInitData()))); DrmInitData drmInitData = DashUtil.loadDrmInitData(DummyDataSource.INSTANCE, period); assertThat(drmInitData).isEqualTo(newDrmInitData()); } + @Test public void testLoadDrmInitDataMissing() throws Exception { Period period = newPeriod(newAdaptationSets(newRepresentations(null /* no init data */))); DrmInitData drmInitData = DashUtil.loadDrmInitData(DummyDataSource.INSTANCE, period); assertThat(drmInitData).isNull(); } + @Test public void testLoadDrmInitDataNoRepresentations() throws Exception { - Period period = newPeriod(newAdaptationSets(/* no representation */)); + Period period = newPeriod(newAdaptationSets(/* no representation */ )); DrmInitData drmInitData = DashUtil.loadDrmInitData(DummyDataSource.INSTANCE, period); assertThat(drmInitData).isNull(); } + @Test public void testLoadDrmInitDataNoAdaptationSets() throws Exception { - Period period = newPeriod(/* no adaptation set */); + Period period = newPeriod(/* no adaptation set */ ); DrmInitData drmInitData = DashUtil.loadDrmInitData(DummyDataSource.INSTANCE, period); assertThat(drmInitData).isNull(); } @@ -68,8 +73,18 @@ public final class DashUtilTest extends TestCase { } private static Representation newRepresentations(DrmInitData drmInitData) { - Format format = Format.createVideoContainerFormat("id", MimeTypes.VIDEO_MP4, - MimeTypes.VIDEO_H264, "", Format.NO_VALUE, 1024, 768, Format.NO_VALUE, null, 0); + Format format = + Format.createVideoContainerFormat( + "id", + MimeTypes.VIDEO_MP4, + MimeTypes.VIDEO_H264, + "", + Format.NO_VALUE, + 1024, + 768, + Format.NO_VALUE, + null, + 0); if (drmInitData != null) { format = format.copyWithDrmInitData(drmInitData); } @@ -77,8 +92,7 @@ public final class DashUtilTest extends TestCase { } private static DrmInitData newDrmInitData() { - return new DrmInitData(new SchemeData(C.WIDEVINE_UUID, "mimeType", - new byte[] {1, 4, 7, 0, 3, 6})); + return new DrmInitData( + new SchemeData(C.WIDEVINE_UUID, "mimeType", new byte[] {1, 4, 7, 0, 3, 6})); } - } diff --git a/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/EventSampleStreamTest.java b/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/EventSampleStreamTest.java new file mode 100644 index 0000000000..9c3752551a --- /dev/null +++ b/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/EventSampleStreamTest.java @@ -0,0 +1,356 @@ +/* + * Copyright (C) 2017 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.dash; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.android.exoplayer2.C; +import com.google.android.exoplayer2.Format; +import com.google.android.exoplayer2.FormatHolder; +import com.google.android.exoplayer2.decoder.DecoderInputBuffer; +import com.google.android.exoplayer2.metadata.MetadataInputBuffer; +import com.google.android.exoplayer2.metadata.emsg.EventMessage; +import com.google.android.exoplayer2.metadata.emsg.EventMessageEncoder; +import com.google.android.exoplayer2.source.dash.manifest.EventStream; +import com.google.android.exoplayer2.util.MimeTypes; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; + +/** + * Unit test for {@link EventSampleStream}. + */ +@RunWith(RobolectricTestRunner.class) +public final class EventSampleStreamTest { + + private static final String SCHEME_ID = "urn:test"; + private static final String VALUE = "123"; + private static final Format FORMAT = Format.createSampleFormat("urn:test/123", + MimeTypes.APPLICATION_EMSG, null, Format.NO_VALUE, null); + private static final byte[] MESSAGE_DATA = new byte[] {1, 2, 3, 4}; + private static final long DURATION_MS = 3000; + private static final long TIME_SCALE = 1000; + + private FormatHolder formatHolder; + private MetadataInputBuffer inputBuffer; + private EventMessageEncoder eventMessageEncoder; + + @Before + public void setUp() { + formatHolder = new FormatHolder(); + inputBuffer = new MetadataInputBuffer(); + eventMessageEncoder = new EventMessageEncoder(); + } + + /** + * Tests that {@link EventSampleStream#readData(FormatHolder, DecoderInputBuffer, boolean)} will + * return format for the first call. + */ + @Test + public void testReadDataReturnFormatForFirstRead() { + EventStream eventStream = new EventStream(SCHEME_ID, VALUE, TIME_SCALE, + new long[0], new EventMessage[0]); + EventSampleStream sampleStream = new EventSampleStream(eventStream, FORMAT, false); + + int result = readData(sampleStream); + assertThat(result).isEqualTo(C.RESULT_FORMAT_READ); + assertThat(formatHolder.format).isEqualTo(FORMAT); + } + + /** + * Tests that a non-dynamic {@link EventSampleStream} will return a buffer with + * {@link C#BUFFER_FLAG_END_OF_STREAM} when trying to read sample out-of-bound. + */ + @Test + public void testReadDataOutOfBoundReturnEndOfStreamAfterFormatForNonDynamicEventSampleStream() { + EventStream eventStream = new EventStream(SCHEME_ID, VALUE, TIME_SCALE, + new long[0], new EventMessage[0]); + EventSampleStream sampleStream = new EventSampleStream(eventStream, FORMAT, false); + // first read - read format + readData(sampleStream); + + int result = readData(sampleStream); + assertThat(result).isEqualTo(C.RESULT_BUFFER_READ); + assertThat(inputBuffer.isEndOfStream()).isTrue(); + } + + /** + * Tests that a dynamic {@link EventSampleStream} will return {@link C#RESULT_NOTHING_READ} + * when trying to read sample out-of-bound. + */ + @Test + public void testReadDataOutOfBoundReturnEndOfStreamAfterFormatForDynamicEventSampleStream() { + EventStream eventStream = new EventStream(SCHEME_ID, VALUE, TIME_SCALE, + new long[0], new EventMessage[0]); + EventSampleStream sampleStream = new EventSampleStream(eventStream, FORMAT, true); + // first read - read format + readData(sampleStream); + + int result = readData(sampleStream); + assertThat(result).isEqualTo(C.RESULT_NOTHING_READ); + } + + /** + * Tests that {@link EventSampleStream#readData(FormatHolder, DecoderInputBuffer, boolean)} will + * return sample data after the first call. + */ + @Test + public void testReadDataReturnDataAfterFormat() { + long presentationTimeUs = 1000000; + EventMessage eventMessage = newEventMessageWithIdAndTime(1, presentationTimeUs); + EventStream eventStream = new EventStream(SCHEME_ID, VALUE, TIME_SCALE, + new long[] {presentationTimeUs}, new EventMessage[] {eventMessage}); + EventSampleStream sampleStream = new EventSampleStream(eventStream, FORMAT, false); + // first read - read format + readData(sampleStream); + + int result = readData(sampleStream); + assertThat(result).isEqualTo(C.RESULT_BUFFER_READ); + assertThat(inputBuffer.data.array()) + .isEqualTo(getEncodedMessage(eventMessage)); + } + + /** + * Tests that {@link EventSampleStream#skipData(long)} will skip until the given position, and + * the next {@link EventSampleStream#readData(FormatHolder, DecoderInputBuffer, boolean)} call + * will return sample data from that position. + */ + @Test + public void testSkipDataThenReadDataReturnDataFromSkippedPosition() { + long presentationTimeUs1 = 1000000; + long presentationTimeUs2 = 2000000; + EventMessage eventMessage1 = newEventMessageWithIdAndTime(1, presentationTimeUs1); + EventMessage eventMessage2 = newEventMessageWithIdAndTime(2, presentationTimeUs2); + EventStream eventStream = new EventStream(SCHEME_ID, VALUE, TIME_SCALE, + new long[] {presentationTimeUs1, presentationTimeUs2}, + new EventMessage[] {eventMessage1, eventMessage2}); + EventSampleStream sampleStream = new EventSampleStream(eventStream, FORMAT, false); + // first read - read format + readData(sampleStream); + + int skipped = sampleStream.skipData(presentationTimeUs2); + int result = readData(sampleStream); + assertThat(skipped).isEqualTo(1); + assertThat(result).isEqualTo(C.RESULT_BUFFER_READ); + assertThat(inputBuffer.data.array()) + .isEqualTo(getEncodedMessage(eventMessage2)); + } + + /** + * Tests that {@link EventSampleStream#seekToUs(long)} (long)} will seek to the given position, + * and the next {@link EventSampleStream#readData(FormatHolder, DecoderInputBuffer, boolean)} call + * will return sample data from that position. + */ + @Test + public void testSeekToUsThenReadDataReturnDataFromSeekPosition() { + long presentationTimeUs1 = 1000000; + long presentationTimeUs2 = 2000000; + EventMessage eventMessage1 = newEventMessageWithIdAndTime(1, presentationTimeUs1); + EventMessage eventMessage2 = newEventMessageWithIdAndTime(2, presentationTimeUs2); + EventStream eventStream = new EventStream(SCHEME_ID, VALUE, TIME_SCALE, + new long[] {presentationTimeUs1, presentationTimeUs2}, + new EventMessage[] {eventMessage1, eventMessage2}); + EventSampleStream sampleStream = new EventSampleStream(eventStream, FORMAT, false); + // first read - read format + readData(sampleStream); + + sampleStream.seekToUs(presentationTimeUs2); + int result = readData(sampleStream); + assertThat(result).isEqualTo(C.RESULT_BUFFER_READ); + assertThat(inputBuffer.data.array()) + .isEqualTo(getEncodedMessage(eventMessage2)); + } + + /** + * Tests that {@link EventSampleStream#updateEventStream(EventStream, boolean)} will update the + * underlying event stream, but keep the read timestamp, so the next + * {@link EventSampleStream#readData(FormatHolder, DecoderInputBuffer, boolean)} call + * will return sample data from after the last read sample timestamp. + */ + @Test + public void testUpdateEventStreamContinueToReadAfterLastReadSamplePresentationTime() { + long presentationTimeUs1 = 1000000; + long presentationTimeUs2 = 2000000; + long presentationTimeUs3 = 3000000; + EventMessage eventMessage1 = newEventMessageWithIdAndTime(1, presentationTimeUs1); + EventMessage eventMessage2 = newEventMessageWithIdAndTime(2, presentationTimeUs2); + EventMessage eventMessage3 = newEventMessageWithIdAndTime(3, presentationTimeUs3); + EventStream eventStream1 = new EventStream(SCHEME_ID, VALUE, TIME_SCALE, + new long[] {presentationTimeUs1, presentationTimeUs2}, + new EventMessage[] {eventMessage1, eventMessage2}); + EventStream eventStream2 = new EventStream(SCHEME_ID, VALUE, TIME_SCALE, + new long[] {presentationTimeUs1, presentationTimeUs2, presentationTimeUs3}, + new EventMessage[] {eventMessage1, eventMessage2, eventMessage3}); + EventSampleStream sampleStream = new EventSampleStream(eventStream1, FORMAT, true); + // first read - read format + readData(sampleStream); + // read first and second sample. + readData(sampleStream); + readData(sampleStream); + + sampleStream.updateEventStream(eventStream2, true); + int result = readData(sampleStream); + assertThat(result).isEqualTo(C.RESULT_BUFFER_READ); + assertThat(inputBuffer.data.array()) + .isEqualTo(getEncodedMessage(eventMessage3)); + } + + /** + * Tests that {@link EventSampleStream#updateEventStream(EventStream, boolean)} will update the + * underlying event stream, but keep the timestamp the stream has skipped to, so the next + * {@link EventSampleStream#readData(FormatHolder, DecoderInputBuffer, boolean)} call + * will return sample data from the skipped position. + */ + @Test + public void testSkipDataThenUpdateStreamContinueToReadFromSkippedPosition() { + long presentationTimeUs1 = 1000000; + long presentationTimeUs2 = 2000000; + long presentationTimeUs3 = 3000000; + EventMessage eventMessage1 = newEventMessageWithIdAndTime(1, presentationTimeUs1); + EventMessage eventMessage2 = newEventMessageWithIdAndTime(2, presentationTimeUs2); + EventMessage eventMessage3 = newEventMessageWithIdAndTime(3, presentationTimeUs3); + EventStream eventStream1 = new EventStream(SCHEME_ID, VALUE, TIME_SCALE, + new long[] {presentationTimeUs1, presentationTimeUs2}, + new EventMessage[] {eventMessage1, eventMessage2}); + EventStream eventStream2 = new EventStream(SCHEME_ID, VALUE, TIME_SCALE, + new long[] {presentationTimeUs1, presentationTimeUs2, presentationTimeUs3}, + new EventMessage[] {eventMessage1, eventMessage2, eventMessage3}); + EventSampleStream sampleStream = new EventSampleStream(eventStream1, FORMAT, true); + // first read - read format + readData(sampleStream); + sampleStream.skipData(presentationTimeUs2 + 1); + + sampleStream.updateEventStream(eventStream2, true); + int result = readData(sampleStream); + assertThat(result).isEqualTo(C.RESULT_BUFFER_READ); + assertThat(inputBuffer.data.array()) + .isEqualTo(getEncodedMessage(eventMessage3)); + } + + /** + * Tests that {@link EventSampleStream#skipData(long)} will only skip to the point right after + * it last event. A following {@link EventSampleStream#updateEventStream(EventStream, boolean)} + * will update the underlying event stream and keep the timestamp the stream has skipped to, so + * the next {@link EventSampleStream#readData(FormatHolder, DecoderInputBuffer, boolean)} call + * will return sample data from the skipped position. + */ + @Test + public void testSkipDataThenUpdateStreamContinueToReadDoNotSkippedMoreThanAvailable() { + long presentationTimeUs1 = 1000000; + long presentationTimeUs2 = 2000000; + long presentationTimeUs3 = 3000000; + EventMessage eventMessage1 = newEventMessageWithIdAndTime(1, presentationTimeUs1); + EventMessage eventMessage2 = newEventMessageWithIdAndTime(2, presentationTimeUs2); + EventMessage eventMessage3 = newEventMessageWithIdAndTime(3, presentationTimeUs3); + EventStream eventStream1 = new EventStream(SCHEME_ID, VALUE, TIME_SCALE, + new long[] {presentationTimeUs1}, + new EventMessage[] {eventMessage1}); + EventStream eventStream2 = new EventStream(SCHEME_ID, VALUE, TIME_SCALE, + new long[] {presentationTimeUs1, presentationTimeUs2, presentationTimeUs3}, + new EventMessage[] {eventMessage1, eventMessage2, eventMessage3}); + EventSampleStream sampleStream = new EventSampleStream(eventStream1, FORMAT, true); + // first read - read format + readData(sampleStream); + // even though the skip call is to 2000001, since eventStream1 only contains sample until + // 1000000, it will only skip to 1000001. + sampleStream.skipData(presentationTimeUs2 + 1); + + sampleStream.updateEventStream(eventStream2, true); + int result = readData(sampleStream); + assertThat(result).isEqualTo(C.RESULT_BUFFER_READ); + assertThat(inputBuffer.data.array()) + .isEqualTo(getEncodedMessage(eventMessage2)); + } + + /** + * Tests that {@link EventSampleStream#updateEventStream(EventStream, boolean)} will update the + * underlying event stream, but keep the timestamp the stream has seek to, so the next + * {@link EventSampleStream#readData(FormatHolder, DecoderInputBuffer, boolean)} call + * will return sample data from the seek position. + */ + @Test + public void testSeekToUsThenUpdateStreamContinueToReadFromSeekPosition() { + long presentationTimeUs1 = 1000000; + long presentationTimeUs2 = 2000000; + long presentationTimeUs3 = 3000000; + EventMessage eventMessage1 = newEventMessageWithIdAndTime(1, presentationTimeUs1); + EventMessage eventMessage2 = newEventMessageWithIdAndTime(2, presentationTimeUs2); + EventMessage eventMessage3 = newEventMessageWithIdAndTime(3, presentationTimeUs3); + EventStream eventStream1 = new EventStream(SCHEME_ID, VALUE, TIME_SCALE, + new long[] {presentationTimeUs1, presentationTimeUs2}, + new EventMessage[] {eventMessage1, eventMessage2}); + EventStream eventStream2 = new EventStream(SCHEME_ID, VALUE, TIME_SCALE, + new long[] {presentationTimeUs1, presentationTimeUs2, presentationTimeUs3}, + new EventMessage[] {eventMessage1, eventMessage2, eventMessage3}); + EventSampleStream sampleStream = new EventSampleStream(eventStream1, FORMAT, true); + // first read - read format + readData(sampleStream); + sampleStream.seekToUs(presentationTimeUs2); + + sampleStream.updateEventStream(eventStream2, true); + int result = readData(sampleStream); + assertThat(result).isEqualTo(C.RESULT_BUFFER_READ); + assertThat(inputBuffer.data.array()) + .isEqualTo(getEncodedMessage(eventMessage2)); + } + + /** + * Tests that {@link EventSampleStream#updateEventStream(EventStream, boolean)} will update the + * underlying event stream, but keep the timestamp the stream has seek to, so the next + * {@link EventSampleStream#readData(FormatHolder, DecoderInputBuffer, boolean)} call + * will return sample data from the seek position. + */ + @Test + public void testSeekToThenUpdateStreamContinueToReadFromSeekPositionEvenSeekMoreThanAvailable() { + long presentationTimeUs1 = 1000000; + long presentationTimeUs2 = 2000000; + long presentationTimeUs3 = 3000000; + EventMessage eventMessage1 = newEventMessageWithIdAndTime(1, presentationTimeUs1); + EventMessage eventMessage2 = newEventMessageWithIdAndTime(2, presentationTimeUs2); + EventMessage eventMessage3 = newEventMessageWithIdAndTime(3, presentationTimeUs3); + EventStream eventStream1 = new EventStream(SCHEME_ID, VALUE, TIME_SCALE, + new long[] {presentationTimeUs1}, + new EventMessage[] {eventMessage1}); + EventStream eventStream2 = new EventStream(SCHEME_ID, VALUE, TIME_SCALE, + new long[] {presentationTimeUs1, presentationTimeUs2, presentationTimeUs3}, + new EventMessage[] {eventMessage1, eventMessage2, eventMessage3}); + EventSampleStream sampleStream = new EventSampleStream(eventStream1, FORMAT, true); + // first read - read format + readData(sampleStream); + sampleStream.seekToUs(presentationTimeUs2 + 1); + + sampleStream.updateEventStream(eventStream2, true); + int result = readData(sampleStream); + assertThat(result).isEqualTo(C.RESULT_BUFFER_READ); + assertThat(inputBuffer.data.array()) + .isEqualTo(getEncodedMessage(eventMessage3)); + } + + private int readData(EventSampleStream sampleStream) { + inputBuffer.clear(); + return sampleStream.readData(formatHolder, inputBuffer, false); + } + + private EventMessage newEventMessageWithIdAndTime(int id, long presentationTimeUs) { + return new EventMessage(SCHEME_ID, VALUE, DURATION_MS, id, MESSAGE_DATA, presentationTimeUs); + } + + private byte[] getEncodedMessage(EventMessage eventMessage) { + return eventMessageEncoder.encode(eventMessage, TIME_SCALE); + } + +} diff --git a/library/dash/src/androidTest/java/com/google/android/exoplayer2/source/dash/manifest/DashManifestParserTest.java b/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/manifest/DashManifestParserTest.java similarity index 85% rename from library/dash/src/androidTest/java/com/google/android/exoplayer2/source/dash/manifest/DashManifestParserTest.java rename to library/dash/src/test/java/com/google/android/exoplayer2/source/dash/manifest/DashManifestParserTest.java index 3b7982592d..6f14c8790f 100644 --- a/library/dash/src/androidTest/java/com/google/android/exoplayer2/source/dash/manifest/DashManifestParserTest.java +++ b/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/manifest/DashManifestParserTest.java @@ -18,39 +18,47 @@ package com.google.android.exoplayer2.source.dash.manifest; import static com.google.common.truth.Truth.assertThat; import android.net.Uri; -import android.test.InstrumentationTestCase; +import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.metadata.emsg.EventMessage; import com.google.android.exoplayer2.testutil.TestUtil; import java.io.IOException; +import java.nio.charset.Charset; import java.util.Collections; import java.util.List; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; -/** - * Unit tests for {@link DashManifestParser}. - */ -public class DashManifestParserTest extends InstrumentationTestCase { +/** Unit tests for {@link DashManifestParser}. */ +@RunWith(RobolectricTestRunner.class) +public class DashManifestParserTest { private static final String SAMPLE_MPD_1 = "sample_mpd_1"; private static final String SAMPLE_MPD_2_UNKNOWN_MIME_TYPE = "sample_mpd_2_unknown_mime_type"; private static final String SAMPLE_MPD_3_SEGMENT_TEMPLATE = "sample_mpd_3_segment_template"; private static final String SAMPLE_MPD_4_EVENT_STREAM = "sample_mpd_4_event_stream"; - /** - * Simple test to ensure the sample manifests parse without any exceptions being thrown. - */ + /** Simple test to ensure the sample manifests parse without any exceptions being thrown. */ + @Test public void testParseMediaPresentationDescription() throws IOException { DashManifestParser parser = new DashManifestParser(); - parser.parse(Uri.parse("https://example.com/test.mpd"), - TestUtil.getInputStream(getInstrumentation(), SAMPLE_MPD_1)); - parser.parse(Uri.parse("https://example.com/test.mpd"), - TestUtil.getInputStream(getInstrumentation(), SAMPLE_MPD_2_UNKNOWN_MIME_TYPE)); + parser.parse( + Uri.parse("https://example.com/test.mpd"), + TestUtil.getInputStream(RuntimeEnvironment.application, SAMPLE_MPD_1)); + parser.parse( + Uri.parse("https://example.com/test.mpd"), + TestUtil.getInputStream(RuntimeEnvironment.application, SAMPLE_MPD_2_UNKNOWN_MIME_TYPE)); } + @Test public void testParseMediaPresentationDescriptionWithSegmentTemplate() throws IOException { DashManifestParser parser = new DashManifestParser(); - DashManifest mpd = parser.parse(Uri.parse("https://example.com/test.mpd"), - TestUtil.getInputStream(getInstrumentation(), SAMPLE_MPD_3_SEGMENT_TEMPLATE)); + DashManifest mpd = + parser.parse( + Uri.parse("https://example.com/test.mpd"), + TestUtil.getInputStream(RuntimeEnvironment.application, SAMPLE_MPD_3_SEGMENT_TEMPLATE)); assertThat(mpd.getPeriodCount()).isEqualTo(1); @@ -75,11 +83,13 @@ public class DashManifestParserTest extends InstrumentationTestCase { } } - public void testParseMediaPresentationDescriptionCanParseEventStream() - throws IOException { + @Test + public void testParseMediaPresentationDescriptionCanParseEventStream() throws IOException { DashManifestParser parser = new DashManifestParser(); - DashManifest mpd = parser.parse(Uri.parse("https://example.com/test.mpd"), - TestUtil.getInputStream(getInstrumentation(), SAMPLE_MPD_4_EVENT_STREAM)); + DashManifest mpd = + parser.parse( + Uri.parse("https://example.com/test.mpd"), + TestUtil.getInputStream(RuntimeEnvironment.application, SAMPLE_MPD_4_EVENT_STREAM)); Period period = mpd.getPeriod(0); assertThat(period.eventStreams).hasSize(3); @@ -87,8 +97,14 @@ public class DashManifestParserTest extends InstrumentationTestCase { // assert text-only event stream EventStream eventStream1 = period.eventStreams.get(0); assertThat(eventStream1.events.length).isEqualTo(1); - EventMessage expectedEvent1 = new EventMessage("urn:uuid:XYZY", "call", 10000, 0, - "+ 1 800 10101010".getBytes(), 0); + EventMessage expectedEvent1 = + new EventMessage( + "urn:uuid:XYZY", + "call", + 10000, + 0, + "+ 1 800 10101010".getBytes(Charset.forName(C.UTF8_NAME)), + 0); assertThat(eventStream1.events[0]).isEqualTo(expectedEvent1); // assert CData-structured event stream @@ -135,6 +151,7 @@ public class DashManifestParserTest extends InstrumentationTestCase { 1000000000)); } + @Test public void testParseCea608AccessibilityChannel() { assertThat( DashManifestParser.parseCea608AccessibilityChannel( @@ -175,6 +192,7 @@ public class DashManifestParserTest extends InstrumentationTestCase { .isEqualTo(Format.NO_VALUE); } + @Test public void testParseCea708AccessibilityChannel() { assertThat( DashManifestParser.parseCea708AccessibilityChannel( @@ -226,5 +244,4 @@ public class DashManifestParserTest extends InstrumentationTestCase { private static List buildCea708AccessibilityDescriptors(String value) { return Collections.singletonList(new Descriptor("urn:scte:dash:cc:cea-708:2015", value, null)); } - } diff --git a/library/dash/src/androidTest/java/com/google/android/exoplayer2/source/dash/manifest/DashManifestTest.java b/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/manifest/DashManifestTest.java similarity index 54% rename from library/dash/src/androidTest/java/com/google/android/exoplayer2/source/dash/manifest/DashManifestTest.java rename to library/dash/src/test/java/com/google/android/exoplayer2/source/dash/manifest/DashManifestTest.java index c6060a7caa..9cf3594116 100644 --- a/library/dash/src/androidTest/java/com/google/android/exoplayer2/source/dash/manifest/DashManifestTest.java +++ b/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/manifest/DashManifestTest.java @@ -24,109 +24,143 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Random; -import junit.framework.TestCase; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; -/** - * Unit tests for {@link DashManifest}. - */ -public class DashManifestTest extends TestCase { +/** Unit tests for {@link DashManifest}. */ +@RunWith(RobolectricTestRunner.class) +public class DashManifestTest { private static final UtcTimingElement DUMMY_UTC_TIMING = new UtcTimingElement("", ""); private static final SingleSegmentBase DUMMY_SEGMENT_BASE = new SingleSegmentBase(); private static final Format DUMMY_FORMAT = Format.createSampleFormat("", "", 0); + @Test public void testCopy() throws Exception { Representation[][][] representations = newRepresentations(3, 2, 3); - DashManifest sourceManifest = newDashManifest(10, - newPeriod("1", 1, - newAdaptationSet(2, representations[0][0]), - newAdaptationSet(3, representations[0][1])), - newPeriod("4", 4, - newAdaptationSet(5, representations[1][0]), - newAdaptationSet(6, representations[1][1])), - newPeriod("7", 7, - newAdaptationSet(8, representations[2][0]), - newAdaptationSet(9, representations[2][1]))); + DashManifest sourceManifest = + newDashManifest( + 10, + newPeriod( + "1", + 1, + newAdaptationSet(2, representations[0][0]), + newAdaptationSet(3, representations[0][1])), + newPeriod( + "4", + 4, + newAdaptationSet(5, representations[1][0]), + newAdaptationSet(6, representations[1][1])), + newPeriod( + "7", + 7, + newAdaptationSet(8, representations[2][0]), + newAdaptationSet(9, representations[2][1]))); - List keys = Arrays.asList( - new RepresentationKey(0, 0, 0), - new RepresentationKey(0, 0, 1), - new RepresentationKey(0, 1, 2), - - new RepresentationKey(1, 0, 1), - new RepresentationKey(1, 1, 0), - new RepresentationKey(1, 1, 2), - - new RepresentationKey(2, 0, 1), - new RepresentationKey(2, 0, 2), - new RepresentationKey(2, 1, 0)); + List keys = + Arrays.asList( + new RepresentationKey(0, 0, 0), + new RepresentationKey(0, 0, 1), + new RepresentationKey(0, 1, 2), + new RepresentationKey(1, 0, 1), + new RepresentationKey(1, 1, 0), + new RepresentationKey(1, 1, 2), + new RepresentationKey(2, 0, 1), + new RepresentationKey(2, 0, 2), + new RepresentationKey(2, 1, 0)); // Keys don't need to be in any particular order Collections.shuffle(keys, new Random(0)); DashManifest copyManifest = sourceManifest.copy(keys); - DashManifest expectedManifest = newDashManifest(10, - newPeriod("1", 1, - newAdaptationSet(2, representations[0][0][0], representations[0][0][1]), - newAdaptationSet(3, representations[0][1][2])), - newPeriod("4", 4, - newAdaptationSet(5, representations[1][0][1]), - newAdaptationSet(6, representations[1][1][0], representations[1][1][2])), - newPeriod("7", 7, - newAdaptationSet(8, representations[2][0][1], representations[2][0][2]), - newAdaptationSet(9, representations[2][1][0]))); + DashManifest expectedManifest = + newDashManifest( + 10, + newPeriod( + "1", + 1, + newAdaptationSet(2, representations[0][0][0], representations[0][0][1]), + newAdaptationSet(3, representations[0][1][2])), + newPeriod( + "4", + 4, + newAdaptationSet(5, representations[1][0][1]), + newAdaptationSet(6, representations[1][1][0], representations[1][1][2])), + newPeriod( + "7", + 7, + newAdaptationSet(8, representations[2][0][1], representations[2][0][2]), + newAdaptationSet(9, representations[2][1][0]))); assertManifestEquals(expectedManifest, copyManifest); } + @Test public void testCopySameAdaptationIndexButDifferentPeriod() throws Exception { Representation[][][] representations = newRepresentations(2, 1, 1); - DashManifest sourceManifest = newDashManifest(10, - newPeriod("1", 1, - newAdaptationSet(2, representations[0][0])), - newPeriod("4", 4, - newAdaptationSet(5, representations[1][0]))); + DashManifest sourceManifest = + newDashManifest( + 10, + newPeriod("1", 1, newAdaptationSet(2, representations[0][0])), + newPeriod("4", 4, newAdaptationSet(5, representations[1][0]))); - DashManifest copyManifest = sourceManifest.copy(Arrays.asList( - new RepresentationKey(0, 0, 0), - new RepresentationKey(1, 0, 0))); + DashManifest copyManifest = + sourceManifest.copy( + Arrays.asList(new RepresentationKey(0, 0, 0), new RepresentationKey(1, 0, 0))); - DashManifest expectedManifest = newDashManifest(10, - newPeriod("1", 1, - newAdaptationSet(2, representations[0][0])), - newPeriod("4", 4, - newAdaptationSet(5, representations[1][0]))); + DashManifest expectedManifest = + newDashManifest( + 10, + newPeriod("1", 1, newAdaptationSet(2, representations[0][0])), + newPeriod("4", 4, newAdaptationSet(5, representations[1][0]))); assertManifestEquals(expectedManifest, copyManifest); } + @Test public void testCopySkipPeriod() throws Exception { Representation[][][] representations = newRepresentations(3, 2, 3); - DashManifest sourceManifest = newDashManifest(10, - newPeriod("1", 1, - newAdaptationSet(2, representations[0][0]), - newAdaptationSet(3, representations[0][1])), - newPeriod("4", 4, - newAdaptationSet(5, representations[1][0]), - newAdaptationSet(6, representations[1][1])), - newPeriod("7", 7, - newAdaptationSet(8, representations[2][0]), - newAdaptationSet(9, representations[2][1]))); + DashManifest sourceManifest = + newDashManifest( + 10, + newPeriod( + "1", + 1, + newAdaptationSet(2, representations[0][0]), + newAdaptationSet(3, representations[0][1])), + newPeriod( + "4", + 4, + newAdaptationSet(5, representations[1][0]), + newAdaptationSet(6, representations[1][1])), + newPeriod( + "7", + 7, + newAdaptationSet(8, representations[2][0]), + newAdaptationSet(9, representations[2][1]))); - DashManifest copyManifest = sourceManifest.copy(Arrays.asList( - new RepresentationKey(0, 0, 0), - new RepresentationKey(0, 0, 1), - new RepresentationKey(0, 1, 2), + DashManifest copyManifest = + sourceManifest.copy( + Arrays.asList( + new RepresentationKey(0, 0, 0), + new RepresentationKey(0, 0, 1), + new RepresentationKey(0, 1, 2), + new RepresentationKey(2, 0, 1), + new RepresentationKey(2, 0, 2), + new RepresentationKey(2, 1, 0))); - new RepresentationKey(2, 0, 1), - new RepresentationKey(2, 0, 2), - new RepresentationKey(2, 1, 0))); - - DashManifest expectedManifest = newDashManifest(7, - newPeriod("1", 1, - newAdaptationSet(2, representations[0][0][0], representations[0][0][1]), - newAdaptationSet(3, representations[0][1][2])), - newPeriod("7", 4, - newAdaptationSet(8, representations[2][0][1], representations[2][0][2]), - newAdaptationSet(9, representations[2][1][0]))); + DashManifest expectedManifest = + newDashManifest( + 7, + newPeriod( + "1", + 1, + newAdaptationSet(2, representations[0][0][0], representations[0][0][1]), + newAdaptationSet(3, representations[0][1][2])), + newPeriod( + "7", + 4, + newAdaptationSet(8, representations[2][0][1], representations[2][0][2]), + newAdaptationSet(9, representations[2][1][0]))); assertManifestEquals(expectedManifest, copyManifest); } @@ -164,8 +198,8 @@ public class DashManifestTest extends TestCase { } } - private static Representation[][][] newRepresentations(int periodCount, int adaptationSetCounts, - int representationCounts) { + private static Representation[][][] newRepresentations( + int periodCount, int adaptationSetCounts, int representationCounts) { Representation[][][] representations = new Representation[periodCount][][]; for (int i = 0; i < periodCount; i++) { representations[i] = new Representation[adaptationSetCounts][]; @@ -184,8 +218,8 @@ public class DashManifestTest extends TestCase { } private static DashManifest newDashManifest(int duration, Period... periods) { - return new DashManifest(0, duration, 1, false, 2, 3, 4, 12345, DUMMY_UTC_TIMING, Uri.EMPTY, - Arrays.asList(periods)); + return new DashManifest( + 0, duration, 1, false, 2, 3, 4, 12345, DUMMY_UTC_TIMING, Uri.EMPTY, Arrays.asList(periods)); } private static Period newPeriod(String id, int startMs, AdaptationSet... adaptationSets) { @@ -195,5 +229,4 @@ public class DashManifestTest extends TestCase { private static AdaptationSet newAdaptationSet(int seed, Representation... representations) { return new AdaptationSet(++seed, ++seed, Arrays.asList(representations), null, null); } - } diff --git a/library/dash/src/androidTest/java/com/google/android/exoplayer2/source/dash/manifest/RangedUriTest.java b/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/manifest/RangedUriTest.java similarity index 92% rename from library/dash/src/androidTest/java/com/google/android/exoplayer2/source/dash/manifest/RangedUriTest.java rename to library/dash/src/test/java/com/google/android/exoplayer2/source/dash/manifest/RangedUriTest.java index 4073625cd1..16c9a4706e 100644 --- a/library/dash/src/androidTest/java/com/google/android/exoplayer2/source/dash/manifest/RangedUriTest.java +++ b/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/manifest/RangedUriTest.java @@ -18,17 +18,19 @@ package com.google.android.exoplayer2.source.dash.manifest; import static com.google.common.truth.Truth.assertThat; import com.google.android.exoplayer2.C; -import junit.framework.TestCase; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; -/** - * Unit test for {@link RangedUri}. - */ -public class RangedUriTest extends TestCase { +/** Unit test for {@link RangedUri}. */ +@RunWith(RobolectricTestRunner.class) +public class RangedUriTest { private static final String BASE_URI = "http://www.test.com/"; private static final String PARTIAL_URI = "path/file.ext"; private static final String FULL_URI = BASE_URI + PARTIAL_URI; + @Test public void testMerge() { RangedUri rangeA = new RangedUri(FULL_URI, 0, 10); RangedUri rangeB = new RangedUri(FULL_URI, 10, 10); @@ -36,6 +38,7 @@ public class RangedUriTest extends TestCase { assertMerge(rangeA, rangeB, expected, null); } + @Test public void testMergeUnbounded() { RangedUri rangeA = new RangedUri(FULL_URI, 0, 10); RangedUri rangeB = new RangedUri(FULL_URI, 10, C.LENGTH_UNSET); @@ -43,6 +46,7 @@ public class RangedUriTest extends TestCase { assertMerge(rangeA, rangeB, expected, null); } + @Test public void testNonMerge() { // A and B do not overlap, so should not merge RangedUri rangeA = new RangedUri(FULL_URI, 0, 10); @@ -65,6 +69,7 @@ public class RangedUriTest extends TestCase { assertNonMerge(rangeA, rangeB, null); } + @Test public void testMergeWithBaseUri() { RangedUri rangeA = new RangedUri(PARTIAL_URI, 0, 10); RangedUri rangeB = new RangedUri(FULL_URI, 10, 10); @@ -85,5 +90,4 @@ public class RangedUriTest extends TestCase { merged = rangeB.attemptMerge(rangeA, baseUrl); assertThat(merged).isNull(); } - } diff --git a/library/dash/src/androidTest/java/com/google/android/exoplayer2/source/dash/manifest/RepresentationTest.java b/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/manifest/RepresentationTest.java similarity index 54% rename from library/dash/src/androidTest/java/com/google/android/exoplayer2/source/dash/manifest/RepresentationTest.java rename to library/dash/src/test/java/com/google/android/exoplayer2/source/dash/manifest/RepresentationTest.java index 2ceb0f0506..309e6c8eb0 100644 --- a/library/dash/src/androidTest/java/com/google/android/exoplayer2/source/dash/manifest/RepresentationTest.java +++ b/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/manifest/RepresentationTest.java @@ -20,27 +20,49 @@ import static com.google.common.truth.Truth.assertThat; import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.source.dash.manifest.SegmentBase.SingleSegmentBase; import com.google.android.exoplayer2.util.MimeTypes; -import junit.framework.TestCase; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; -/** - * Unit test for {@link Representation}. - */ -public class RepresentationTest extends TestCase { +/** Unit test for {@link Representation}. */ +@RunWith(RobolectricTestRunner.class) +public class RepresentationTest { + @Test public void testGetCacheKey() { String uri = "http://www.google.com"; SegmentBase base = new SingleSegmentBase(new RangedUri(null, 0, 1), 1, 0, 1, 1); - Format format = Format.createVideoContainerFormat("0", MimeTypes.APPLICATION_MP4, null, - MimeTypes.VIDEO_H264, 2500000, 1920, 1080, Format.NO_VALUE, null, 0); - Representation representation = Representation.newInstance("test_stream_1", 3, format, uri, - base); + Format format = + Format.createVideoContainerFormat( + "0", + MimeTypes.APPLICATION_MP4, + null, + MimeTypes.VIDEO_H264, + 2500000, + 1920, + 1080, + Format.NO_VALUE, + null, + 0); + Representation representation = + Representation.newInstance("test_stream_1", 3, format, uri, base); assertThat(representation.getCacheKey()).isEqualTo("test_stream_1.0.3"); - format = Format.createVideoContainerFormat("150", MimeTypes.APPLICATION_MP4, null, - MimeTypes.VIDEO_H264, 2500000, 1920, 1080, Format.NO_VALUE, null, 0); - representation = Representation.newInstance("test_stream_1", Representation.REVISION_ID_DEFAULT, - format, uri, base); + format = + Format.createVideoContainerFormat( + "150", + MimeTypes.APPLICATION_MP4, + null, + MimeTypes.VIDEO_H264, + 2500000, + 1920, + 1080, + Format.NO_VALUE, + null, + 0); + representation = + Representation.newInstance( + "test_stream_1", Representation.REVISION_ID_DEFAULT, format, uri, base); assertThat(representation.getCacheKey()).isEqualTo("test_stream_1.150.-1"); } - } diff --git a/library/dash/src/androidTest/java/com/google/android/exoplayer2/source/dash/manifest/UrlTemplateTest.java b/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/manifest/UrlTemplateTest.java similarity index 89% rename from library/dash/src/androidTest/java/com/google/android/exoplayer2/source/dash/manifest/UrlTemplateTest.java rename to library/dash/src/test/java/com/google/android/exoplayer2/source/dash/manifest/UrlTemplateTest.java index b3221bbc18..4192280c81 100644 --- a/library/dash/src/androidTest/java/com/google/android/exoplayer2/source/dash/manifest/UrlTemplateTest.java +++ b/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/manifest/UrlTemplateTest.java @@ -16,14 +16,17 @@ package com.google.android.exoplayer2.source.dash.manifest; import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.fail; -import junit.framework.TestCase; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; -/** - * Unit test for {@link UrlTemplate}. - */ -public class UrlTemplateTest extends TestCase { +/** Unit test for {@link UrlTemplate}. */ +@RunWith(RobolectricTestRunner.class) +public class UrlTemplateTest { + @Test public void testRealExamples() { String template = "QualityLevels($Bandwidth$)/Fragments(video=$Time$,format=mpd-time-csf)"; UrlTemplate urlTemplate = UrlTemplate.compile(template); @@ -41,6 +44,7 @@ public class UrlTemplateTest extends TestCase { assertThat(url).isEqualTo("chunk_ctvideo_cfm4s_ridabc1_cn10_w2073857842_mpd.m4s"); } + @Test public void testFull() { String template = "$Bandwidth$_a_$RepresentationID$_b_$Time$_c_$Number$"; UrlTemplate urlTemplate = UrlTemplate.compile(template); @@ -48,6 +52,7 @@ public class UrlTemplateTest extends TestCase { assertThat(url).isEqualTo("650000_a_abc1_b_5000_c_10"); } + @Test public void testFullWithDollarEscaping() { String template = "$$$Bandwidth$$$_a$$_$RepresentationID$_b_$Time$_c_$Number$$$"; UrlTemplate urlTemplate = UrlTemplate.compile(template); @@ -55,6 +60,7 @@ public class UrlTemplateTest extends TestCase { assertThat(url).isEqualTo("$650000$_a$_abc1_b_5000_c_10$"); } + @Test public void testInvalidSubstitution() { String template = "$IllegalId$"; try { @@ -64,5 +70,4 @@ public class UrlTemplateTest extends TestCase { // Expected. } } - } diff --git a/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/offline/DashDownloadActionTest.java b/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/offline/DashDownloadActionTest.java new file mode 100644 index 0000000000..ea47722b69 --- /dev/null +++ b/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/offline/DashDownloadActionTest.java @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2017 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.dash.offline; + +import static com.google.common.truth.Truth.assertThat; + +import android.net.Uri; +import com.google.android.exoplayer2.offline.DownloadAction; +import com.google.android.exoplayer2.offline.DownloaderConstructorHelper; +import com.google.android.exoplayer2.source.dash.manifest.RepresentationKey; +import com.google.android.exoplayer2.upstream.DummyDataSource; +import com.google.android.exoplayer2.upstream.cache.Cache; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; + +/** + * Unit tests for {@link DashDownloadAction}. + */ +@RunWith(RobolectricTestRunner.class) +public class DashDownloadActionTest { + + @Test + public void testDownloadActionIsNotRemoveAction() throws Exception { + DashDownloadAction action = new DashDownloadAction(Uri.parse("uri"), false, null); + assertThat(action.isRemoveAction()).isFalse(); + } + + @Test + public void testRemoveActionIsRemoveAction() throws Exception { + DashDownloadAction action2 = new DashDownloadAction(Uri.parse("uri"), true, null); + assertThat(action2.isRemoveAction()).isTrue(); + } + + @Test + public void testCreateDownloader() throws Exception { + MockitoAnnotations.initMocks(this); + DashDownloadAction action = new DashDownloadAction(Uri.parse("uri"), false, null); + DownloaderConstructorHelper constructorHelper = new DownloaderConstructorHelper( + Mockito.mock(Cache.class), DummyDataSource.FACTORY); + assertThat(action.createDownloader(constructorHelper)).isNotNull(); + } + + @Test + public void testSameUriDifferentAction_IsSameMedia() throws Exception { + DashDownloadAction action1 = new DashDownloadAction(Uri.parse("uri"), true, null); + DashDownloadAction action2 = new DashDownloadAction(Uri.parse("uri"), false, null); + assertThat(action1.isSameMedia(action2)).isTrue(); + } + + @Test + public void testDifferentUriAndAction_IsNotSameMedia() throws Exception { + DashDownloadAction action3 = new DashDownloadAction(Uri.parse("uri2"), true, null); + DashDownloadAction action4 = new DashDownloadAction(Uri.parse("uri"), false, null); + assertThat(action3.isSameMedia(action4)).isFalse(); + } + + @SuppressWarnings("EqualsWithItself") + @Test + public void testEquals() throws Exception { + DashDownloadAction action1 = new DashDownloadAction(Uri.parse("uri"), true, null); + assertThat(action1.equals(action1)).isTrue(); + + DashDownloadAction action2 = new DashDownloadAction(Uri.parse("uri"), true, null); + DashDownloadAction action3 = new DashDownloadAction(Uri.parse("uri"), true, null); + assertEqual(action2, action3); + + DashDownloadAction action4 = new DashDownloadAction(Uri.parse("uri"), true, null); + DashDownloadAction action5 = new DashDownloadAction(Uri.parse("uri"), false, null); + assertNotEqual(action4, action5); + + DashDownloadAction action6 = new DashDownloadAction(Uri.parse("uri"), false, null); + DashDownloadAction action7 = + new DashDownloadAction(Uri.parse("uri"), false, null, new RepresentationKey(0, 0, 0)); + assertNotEqual(action6, action7); + + DashDownloadAction action8 = + new DashDownloadAction(Uri.parse("uri"), false, null, new RepresentationKey(1, 1, 1)); + DashDownloadAction action9 = + new DashDownloadAction(Uri.parse("uri"), false, null, new RepresentationKey(0, 0, 0)); + assertNotEqual(action8, action9); + + DashDownloadAction action10 = new DashDownloadAction(Uri.parse("uri"), true, null); + DashDownloadAction action11 = new DashDownloadAction(Uri.parse("uri2"), true, null); + assertNotEqual(action10, action11); + + DashDownloadAction action12 = new DashDownloadAction(Uri.parse("uri"), false, null, + new RepresentationKey(0, 0, 0), new RepresentationKey(1, 1, 1)); + DashDownloadAction action13 = new DashDownloadAction(Uri.parse("uri"), false, null, + new RepresentationKey(1, 1, 1), new RepresentationKey(0, 0, 0)); + assertEqual(action12, action13); + + DashDownloadAction action14 = new DashDownloadAction(Uri.parse("uri"), false, null, + new RepresentationKey(0, 0, 0)); + DashDownloadAction action15 = new DashDownloadAction(Uri.parse("uri"), false, null, + new RepresentationKey(1, 1, 1), new RepresentationKey(0, 0, 0)); + assertNotEqual(action14, action15); + + DashDownloadAction action16 = new DashDownloadAction(Uri.parse("uri"), false, null); + DashDownloadAction action17 = + new DashDownloadAction(Uri.parse("uri"), false, null, new RepresentationKey[0]); + assertEqual(action16, action17); + } + + @Test + public void testSerializerGetType() throws Exception { + DashDownloadAction action = new DashDownloadAction(Uri.parse("uri"), false, null); + assertThat(action.getType()).isNotNull(); + } + + @Test + public void testSerializerWriteRead() throws Exception { + doTestSerializationRoundTrip(new DashDownloadAction(Uri.parse("uri"), false, null)); + doTestSerializationRoundTrip(new DashDownloadAction(Uri.parse("uri"), true, null)); + doTestSerializationRoundTrip(new DashDownloadAction(Uri.parse("uri2"), false, null, + new RepresentationKey(0, 0, 0), new RepresentationKey(1, 1, 1))); + } + + private static void assertNotEqual(DashDownloadAction action1, DashDownloadAction action2) { + assertThat(action1).isNotEqualTo(action2); + assertThat(action2).isNotEqualTo(action1); + } + + private static void assertEqual(DashDownloadAction action1, DashDownloadAction action2) { + assertThat(action1).isEqualTo(action2); + assertThat(action2).isEqualTo(action1); + } + + private static void doTestSerializationRoundTrip(DashDownloadAction action1) throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + DataOutputStream output = new DataOutputStream(out); + action1.writeToStream(output); + + ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); + DataInputStream input = new DataInputStream(in); + DownloadAction action2 = + DashDownloadAction.DESERIALIZER.readFromStream(DownloadAction.MASTER_VERSION, input); + + assertThat(action1).isEqualTo(action2); + } + +} diff --git a/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/offline/DashDownloadTestData.java b/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/offline/DashDownloadTestData.java new file mode 100644 index 0000000000..a215347f15 --- /dev/null +++ b/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/offline/DashDownloadTestData.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2017 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.dash.offline; + +import android.net.Uri; +import com.google.android.exoplayer2.C; +import java.nio.charset.Charset; + +/** Data for DASH downloading tests. */ +/* package */ interface DashDownloadTestData { + + Uri TEST_MPD_URI = Uri.parse("test.mpd"); + + byte[] TEST_MPD = + ("\n" + + "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + // Bounded range data + + " \n" + // Unbounded range data + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + // This segment list has a 1 second offset to make sure the progressive download order + + " \n" + + " \n" + + " \n" // 1s offset + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + "") + .getBytes(Charset.forName(C.UTF8_NAME)); + + byte[] TEST_MPD_NO_INDEX = + ("\n" + + "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + "") + .getBytes(Charset.forName(C.UTF8_NAME)); +} diff --git a/library/dash/src/androidTest/java/com/google/android/exoplayer2/source/dash/offline/DashDownloaderTest.java b/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/offline/DashDownloaderTest.java similarity index 65% rename from library/dash/src/androidTest/java/com/google/android/exoplayer2/source/dash/offline/DashDownloaderTest.java rename to library/dash/src/test/java/com/google/android/exoplayer2/source/dash/offline/DashDownloaderTest.java index 5e7ce43ebf..d00b24e84f 100644 --- a/library/dash/src/androidTest/java/com/google/android/exoplayer2/source/dash/offline/DashDownloaderTest.java +++ b/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/offline/DashDownloaderTest.java @@ -22,10 +22,10 @@ import static com.google.android.exoplayer2.testutil.CacheAsserts.assertCacheEmp import static com.google.android.exoplayer2.testutil.CacheAsserts.assertCachedData; import static com.google.android.exoplayer2.testutil.CacheAsserts.assertDataCached; import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.fail; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import android.test.InstrumentationTestCase; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.offline.DownloadException; import com.google.android.exoplayer2.offline.Downloader.ProgressListener; @@ -35,7 +35,6 @@ import com.google.android.exoplayer2.source.dash.manifest.RepresentationKey; import com.google.android.exoplayer2.testutil.FakeDataSet; import com.google.android.exoplayer2.testutil.FakeDataSource; import com.google.android.exoplayer2.testutil.FakeDataSource.Factory; -import com.google.android.exoplayer2.testutil.MockitoUtil; import com.google.android.exoplayer2.testutil.TestUtil; import com.google.android.exoplayer2.upstream.DataSpec; import com.google.android.exoplayer2.upstream.cache.NoOpCacheEvictor; @@ -44,34 +43,38 @@ import com.google.android.exoplayer2.util.Util; import java.io.File; import java.io.IOException; import java.util.Arrays; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; import org.mockito.InOrder; import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; -/** - * Unit tests for {@link DashDownloader}. - */ -public class DashDownloaderTest extends InstrumentationTestCase { +/** Unit tests for {@link DashDownloader}. */ +@RunWith(RobolectricTestRunner.class) +public class DashDownloaderTest { private SimpleCache cache; private File tempFolder; - @Override + @Before public void setUp() throws Exception { - super.setUp(); - MockitoUtil.setUpMockito(this); - tempFolder = Util.createTempDirectory(getInstrumentation().getContext(), "ExoPlayerTest"); + MockitoAnnotations.initMocks(this); + tempFolder = Util.createTempDirectory(RuntimeEnvironment.application, "ExoPlayerTest"); cache = new SimpleCache(tempFolder, new NoOpCacheEvictor()); } - @Override + @After public void tearDown() throws Exception { Util.recursiveDelete(tempFolder); - super.tearDown(); } + @Test public void testGetManifest() throws Exception { - FakeDataSet fakeDataSet = new FakeDataSet() - .setData(TEST_MPD_URI, TEST_MPD); + FakeDataSet fakeDataSet = new FakeDataSet().setData(TEST_MPD_URI, TEST_MPD); DashDownloader dashDownloader = getDashDownloader(fakeDataSet); DashManifest manifest = dashDownloader.getManifest(); @@ -80,15 +83,17 @@ public class DashDownloaderTest extends InstrumentationTestCase { assertCachedData(cache, fakeDataSet); } + @Test public void testDownloadManifestFailure() throws Exception { byte[] testMpdFirstPart = Arrays.copyOf(TEST_MPD, 10); byte[] testMpdSecondPart = Arrays.copyOfRange(TEST_MPD, 10, TEST_MPD.length); - FakeDataSet fakeDataSet = new FakeDataSet() - .newData(TEST_MPD_URI) - .appendReadData(testMpdFirstPart) - .appendReadError(new IOException()) - .appendReadData(testMpdSecondPart) - .endData(); + FakeDataSet fakeDataSet = + new FakeDataSet() + .newData(TEST_MPD_URI) + .appendReadData(testMpdFirstPart) + .appendReadError(new IOException()) + .appendReadData(testMpdSecondPart) + .endData(); DashDownloader dashDownloader = getDashDownloader(fakeDataSet); // fails on the first try @@ -108,13 +113,15 @@ public class DashDownloaderTest extends InstrumentationTestCase { assertCachedData(cache, fakeDataSet); } + @Test public void testDownloadRepresentation() throws Exception { - FakeDataSet fakeDataSet = new FakeDataSet() - .setData(TEST_MPD_URI, TEST_MPD) - .setRandomData("audio_init_data", 10) - .setRandomData("audio_segment_1", 4) - .setRandomData("audio_segment_2", 5) - .setRandomData("audio_segment_3", 6); + FakeDataSet fakeDataSet = + new FakeDataSet() + .setData(TEST_MPD_URI, TEST_MPD) + .setRandomData("audio_init_data", 10) + .setRandomData("audio_segment_1", 4) + .setRandomData("audio_segment_2", 5) + .setRandomData("audio_segment_3", 6); DashDownloader dashDownloader = getDashDownloader(fakeDataSet); dashDownloader.selectRepresentations(new RepresentationKey[] {new RepresentationKey(0, 0, 0)}); @@ -123,17 +130,19 @@ public class DashDownloaderTest extends InstrumentationTestCase { assertCachedData(cache, fakeDataSet); } + @Test public void testDownloadRepresentationInSmallParts() throws Exception { - FakeDataSet fakeDataSet = new FakeDataSet() - .setData(TEST_MPD_URI, TEST_MPD) - .setRandomData("audio_init_data", 10) - .newData("audio_segment_1") - .appendReadData(TestUtil.buildTestData(10)) - .appendReadData(TestUtil.buildTestData(10)) - .appendReadData(TestUtil.buildTestData(10)) - .endData() - .setRandomData("audio_segment_2", 5) - .setRandomData("audio_segment_3", 6); + FakeDataSet fakeDataSet = + new FakeDataSet() + .setData(TEST_MPD_URI, TEST_MPD) + .setRandomData("audio_init_data", 10) + .newData("audio_segment_1") + .appendReadData(TestUtil.buildTestData(10)) + .appendReadData(TestUtil.buildTestData(10)) + .appendReadData(TestUtil.buildTestData(10)) + .endData() + .setRandomData("audio_segment_2", 5) + .setRandomData("audio_segment_3", 6); DashDownloader dashDownloader = getDashDownloader(fakeDataSet); dashDownloader.selectRepresentations(new RepresentationKey[] {new RepresentationKey(0, 0, 0)}); @@ -142,16 +151,18 @@ public class DashDownloaderTest extends InstrumentationTestCase { assertCachedData(cache, fakeDataSet); } + @Test public void testDownloadRepresentations() throws Exception { - FakeDataSet fakeDataSet = new FakeDataSet() - .setData(TEST_MPD_URI, TEST_MPD) - .setRandomData("audio_init_data", 10) - .setRandomData("audio_segment_1", 4) - .setRandomData("audio_segment_2", 5) - .setRandomData("audio_segment_3", 6) - .setRandomData("text_segment_1", 1) - .setRandomData("text_segment_2", 2) - .setRandomData("text_segment_3", 3); + FakeDataSet fakeDataSet = + new FakeDataSet() + .setData(TEST_MPD_URI, TEST_MPD) + .setRandomData("audio_init_data", 10) + .setRandomData("audio_segment_1", 4) + .setRandomData("audio_segment_2", 5) + .setRandomData("audio_segment_3", 6) + .setRandomData("text_segment_1", 1) + .setRandomData("text_segment_2", 2) + .setRandomData("text_segment_3", 3); DashDownloader dashDownloader = getDashDownloader(fakeDataSet); dashDownloader.selectRepresentations( @@ -161,19 +172,21 @@ public class DashDownloaderTest extends InstrumentationTestCase { assertCachedData(cache, fakeDataSet); } + @Test public void testDownloadAllRepresentations() throws Exception { - FakeDataSet fakeDataSet = new FakeDataSet() - .setData(TEST_MPD_URI, TEST_MPD) - .setRandomData("audio_init_data", 10) - .setRandomData("audio_segment_1", 4) - .setRandomData("audio_segment_2", 5) - .setRandomData("audio_segment_3", 6) - .setRandomData("text_segment_1", 1) - .setRandomData("text_segment_2", 2) - .setRandomData("text_segment_3", 3) - .setRandomData("period_2_segment_1", 1) - .setRandomData("period_2_segment_2", 2) - .setRandomData("period_2_segment_3", 3); + FakeDataSet fakeDataSet = + new FakeDataSet() + .setData(TEST_MPD_URI, TEST_MPD) + .setRandomData("audio_init_data", 10) + .setRandomData("audio_segment_1", 4) + .setRandomData("audio_segment_2", 5) + .setRandomData("audio_segment_3", 6) + .setRandomData("text_segment_1", 1) + .setRandomData("text_segment_2", 2) + .setRandomData("text_segment_3", 3) + .setRandomData("period_2_segment_1", 1) + .setRandomData("period_2_segment_2", 2) + .setRandomData("period_2_segment_3", 3); DashDownloader dashDownloader = getDashDownloader(fakeDataSet); // dashDownloader.selectRepresentations() isn't called @@ -195,21 +208,23 @@ public class DashDownloaderTest extends InstrumentationTestCase { dashDownloader.remove(); } + @Test public void testProgressiveDownload() throws Exception { - FakeDataSet fakeDataSet = new FakeDataSet() - .setData(TEST_MPD_URI, TEST_MPD) - .setRandomData("audio_init_data", 10) - .setRandomData("audio_segment_1", 4) - .setRandomData("audio_segment_2", 5) - .setRandomData("audio_segment_3", 6) - .setRandomData("text_segment_1", 1) - .setRandomData("text_segment_2", 2) - .setRandomData("text_segment_3", 3); + FakeDataSet fakeDataSet = + new FakeDataSet() + .setData(TEST_MPD_URI, TEST_MPD) + .setRandomData("audio_init_data", 10) + .setRandomData("audio_segment_1", 4) + .setRandomData("audio_segment_2", 5) + .setRandomData("audio_segment_3", 6) + .setRandomData("text_segment_1", 1) + .setRandomData("text_segment_2", 2) + .setRandomData("text_segment_3", 3); FakeDataSource fakeDataSource = new FakeDataSource(fakeDataSet); Factory factory = mock(Factory.class); when(factory.createDataSource()).thenReturn(fakeDataSource); - DashDownloader dashDownloader = new DashDownloader(TEST_MPD_URI, - new DownloaderConstructorHelper(cache, factory)); + DashDownloader dashDownloader = + new DashDownloader(TEST_MPD_URI, new DownloaderConstructorHelper(cache, factory)); dashDownloader.selectRepresentations( new RepresentationKey[] {new RepresentationKey(0, 0, 0), new RepresentationKey(0, 1, 0)}); @@ -227,21 +242,23 @@ public class DashDownloaderTest extends InstrumentationTestCase { assertThat(openedDataSpecs[7].uri.getPath()).isEqualTo("text_segment_3"); } + @Test public void testProgressiveDownloadSeparatePeriods() throws Exception { - FakeDataSet fakeDataSet = new FakeDataSet() - .setData(TEST_MPD_URI, TEST_MPD) - .setRandomData("audio_init_data", 10) - .setRandomData("audio_segment_1", 4) - .setRandomData("audio_segment_2", 5) - .setRandomData("audio_segment_3", 6) - .setRandomData("period_2_segment_1", 1) - .setRandomData("period_2_segment_2", 2) - .setRandomData("period_2_segment_3", 3); + FakeDataSet fakeDataSet = + new FakeDataSet() + .setData(TEST_MPD_URI, TEST_MPD) + .setRandomData("audio_init_data", 10) + .setRandomData("audio_segment_1", 4) + .setRandomData("audio_segment_2", 5) + .setRandomData("audio_segment_3", 6) + .setRandomData("period_2_segment_1", 1) + .setRandomData("period_2_segment_2", 2) + .setRandomData("period_2_segment_3", 3); FakeDataSource fakeDataSource = new FakeDataSource(fakeDataSet); Factory factory = mock(Factory.class); when(factory.createDataSource()).thenReturn(fakeDataSource); - DashDownloader dashDownloader = new DashDownloader(TEST_MPD_URI, - new DownloaderConstructorHelper(cache, factory)); + DashDownloader dashDownloader = + new DashDownloader(TEST_MPD_URI, new DownloaderConstructorHelper(cache, factory)); dashDownloader.selectRepresentations( new RepresentationKey[] {new RepresentationKey(0, 0, 0), new RepresentationKey(1, 0, 0)}); @@ -259,17 +276,19 @@ public class DashDownloaderTest extends InstrumentationTestCase { assertThat(openedDataSpecs[7].uri.getPath()).isEqualTo("period_2_segment_3"); } + @Test public void testDownloadRepresentationFailure() throws Exception { - FakeDataSet fakeDataSet = new FakeDataSet() - .setData(TEST_MPD_URI, TEST_MPD) - .setRandomData("audio_init_data", 10) - .setRandomData("audio_segment_1", 4) - .newData("audio_segment_2") - .appendReadData(TestUtil.buildTestData(2)) - .appendReadError(new IOException()) - .appendReadData(TestUtil.buildTestData(3)) - .endData() - .setRandomData("audio_segment_3", 6); + FakeDataSet fakeDataSet = + new FakeDataSet() + .setData(TEST_MPD_URI, TEST_MPD) + .setRandomData("audio_init_data", 10) + .setRandomData("audio_segment_1", 4) + .newData("audio_segment_2") + .appendReadData(TestUtil.buildTestData(2)) + .appendReadError(new IOException()) + .appendReadData(TestUtil.buildTestData(3)) + .endData() + .setRandomData("audio_segment_3", 6); DashDownloader dashDownloader = getDashDownloader(fakeDataSet); dashDownloader.selectRepresentations(new RepresentationKey[] {new RepresentationKey(0, 0, 0)}); @@ -285,17 +304,19 @@ public class DashDownloaderTest extends InstrumentationTestCase { assertCachedData(cache, fakeDataSet); } + @Test public void testCounters() throws Exception { - FakeDataSet fakeDataSet = new FakeDataSet() - .setData(TEST_MPD_URI, TEST_MPD) - .setRandomData("audio_init_data", 10) - .setRandomData("audio_segment_1", 4) - .newData("audio_segment_2") - .appendReadData(TestUtil.buildTestData(2)) - .appendReadError(new IOException()) - .appendReadData(TestUtil.buildTestData(3)) - .endData() - .setRandomData("audio_segment_3", 6); + FakeDataSet fakeDataSet = + new FakeDataSet() + .setData(TEST_MPD_URI, TEST_MPD) + .setRandomData("audio_init_data", 10) + .setRandomData("audio_segment_1", 4) + .newData("audio_segment_2") + .appendReadData(TestUtil.buildTestData(2)) + .appendReadError(new IOException()) + .appendReadData(TestUtil.buildTestData(3)) + .endData() + .setRandomData("audio_segment_3", 6); DashDownloader dashDownloader = getDashDownloader(fakeDataSet); assertCounters(dashDownloader, C.LENGTH_UNSET, C.LENGTH_UNSET, C.LENGTH_UNSET); @@ -319,13 +340,15 @@ public class DashDownloaderTest extends InstrumentationTestCase { assertCounters(dashDownloader, 4, 4, 10 + 4 + 5 + 6); } + @Test public void testListener() throws Exception { - FakeDataSet fakeDataSet = new FakeDataSet() - .setData(TEST_MPD_URI, TEST_MPD) - .setRandomData("audio_init_data", 10) - .setRandomData("audio_segment_1", 4) - .setRandomData("audio_segment_2", 5) - .setRandomData("audio_segment_3", 6); + FakeDataSet fakeDataSet = + new FakeDataSet() + .setData(TEST_MPD_URI, TEST_MPD) + .setRandomData("audio_init_data", 10) + .setRandomData("audio_segment_1", 4) + .setRandomData("audio_segment_2", 5) + .setRandomData("audio_segment_3", 6); DashDownloader dashDownloader = getDashDownloader(fakeDataSet); dashDownloader.selectRepresentations(new RepresentationKey[] {new RepresentationKey(0, 0, 0)}); @@ -340,16 +363,18 @@ public class DashDownloaderTest extends InstrumentationTestCase { inOrder.verifyNoMoreInteractions(); } + @Test public void testRemoveAll() throws Exception { - FakeDataSet fakeDataSet = new FakeDataSet() - .setData(TEST_MPD_URI, TEST_MPD) - .setRandomData("audio_init_data", 10) - .setRandomData("audio_segment_1", 4) - .setRandomData("audio_segment_2", 5) - .setRandomData("audio_segment_3", 6) - .setRandomData("text_segment_1", 1) - .setRandomData("text_segment_2", 2) - .setRandomData("text_segment_3", 3); + FakeDataSet fakeDataSet = + new FakeDataSet() + .setData(TEST_MPD_URI, TEST_MPD) + .setRandomData("audio_init_data", 10) + .setRandomData("audio_segment_1", 4) + .setRandomData("audio_segment_2", 5) + .setRandomData("audio_segment_3", 6) + .setRandomData("text_segment_1", 1) + .setRandomData("text_segment_2", 2) + .setRandomData("text_segment_3", 3); DashDownloader dashDownloader = getDashDownloader(fakeDataSet); dashDownloader.selectRepresentations( new RepresentationKey[] {new RepresentationKey(0, 0, 0), new RepresentationKey(0, 1, 0)}); @@ -360,10 +385,12 @@ public class DashDownloaderTest extends InstrumentationTestCase { assertCacheEmpty(cache); } + @Test public void testRepresentationWithoutIndex() throws Exception { - FakeDataSet fakeDataSet = new FakeDataSet() - .setData(TEST_MPD_URI, TEST_MPD_NO_INDEX) - .setRandomData("test_segment_1", 4); + FakeDataSet fakeDataSet = + new FakeDataSet() + .setData(TEST_MPD_URI, TEST_MPD_NO_INDEX) + .setRandomData("test_segment_1", 4); DashDownloader dashDownloader = getDashDownloader(fakeDataSet); dashDownloader.selectRepresentations(new RepresentationKey[] {new RepresentationKey(0, 0, 0)}); @@ -379,13 +406,15 @@ public class DashDownloaderTest extends InstrumentationTestCase { assertCacheEmpty(cache); } + @Test public void testSelectRepresentationsClearsPreviousSelection() throws Exception { - FakeDataSet fakeDataSet = new FakeDataSet() - .setData(TEST_MPD_URI, TEST_MPD) - .setRandomData("audio_init_data", 10) - .setRandomData("audio_segment_1", 4) - .setRandomData("audio_segment_2", 5) - .setRandomData("audio_segment_3", 6); + FakeDataSet fakeDataSet = + new FakeDataSet() + .setData(TEST_MPD_URI, TEST_MPD) + .setRandomData("audio_init_data", 10) + .setRandomData("audio_segment_1", 4) + .setRandomData("audio_segment_2", 5) + .setRandomData("audio_segment_3", 6); DashDownloader dashDownloader = getDashDownloader(fakeDataSet); dashDownloader.selectRepresentations( @@ -401,11 +430,13 @@ public class DashDownloaderTest extends InstrumentationTestCase { return new DashDownloader(TEST_MPD_URI, new DownloaderConstructorHelper(cache, factory)); } - private static void assertCounters(DashDownloader dashDownloader, int totalSegments, - int downloadedSegments, int downloadedBytes) { + private static void assertCounters( + DashDownloader dashDownloader, + int totalSegments, + int downloadedSegments, + int downloadedBytes) { assertThat(dashDownloader.getTotalSegments()).isEqualTo(totalSegments); assertThat(dashDownloader.getDownloadedSegments()).isEqualTo(downloadedSegments); assertThat(dashDownloader.getDownloadedBytes()).isEqualTo(downloadedBytes); } - } diff --git a/library/dash/src/test/resources/robolectric.properties b/library/dash/src/test/resources/robolectric.properties new file mode 100644 index 0000000000..2f3210368e --- /dev/null +++ b/library/dash/src/test/resources/robolectric.properties @@ -0,0 +1 @@ +manifest=src/test/AndroidManifest.xml diff --git a/library/hls/build.gradle b/library/hls/build.gradle index 5471eacec6..41e0b71da2 100644 --- a/library/hls/build.gradle +++ b/library/hls/build.gradle @@ -35,10 +35,7 @@ android { dependencies { compile project(modulePrefix + 'library-core') compile 'com.android.support:support-annotations:' + supportLibraryVersion - androidTestCompile project(modulePrefix + 'testutils') - androidTestCompile 'com.google.dexmaker:dexmaker:' + dexmakerVersion - androidTestCompile 'com.google.dexmaker:dexmaker-mockito:' + dexmakerVersion - androidTestCompile 'org.mockito:mockito-core:' + mockitoVersion + testCompile project(modulePrefix + 'testutils-robolectric') } ext { diff --git a/library/hls/src/androidTest/java/com/google/android/exoplayer2/source/hls/offline/HlsDownloadTestData.java b/library/hls/src/androidTest/java/com/google/android/exoplayer2/source/hls/offline/HlsDownloadTestData.java deleted file mode 100644 index ec70fb1200..0000000000 --- a/library/hls/src/androidTest/java/com/google/android/exoplayer2/source/hls/offline/HlsDownloadTestData.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 2017 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.offline; - -/** - * Data for HLS downloading tests. - */ -/* package */ interface HlsDownloadTestData { - - String MASTER_PLAYLIST_URI = "test.m3u8"; - - String MEDIA_PLAYLIST_0_DIR = "gear0/"; - String MEDIA_PLAYLIST_0_URI = MEDIA_PLAYLIST_0_DIR + "prog_index.m3u8"; - String MEDIA_PLAYLIST_1_DIR = "gear1/"; - String MEDIA_PLAYLIST_1_URI = MEDIA_PLAYLIST_1_DIR + "prog_index.m3u8"; - String MEDIA_PLAYLIST_2_DIR = "gear2/"; - String MEDIA_PLAYLIST_2_URI = MEDIA_PLAYLIST_2_DIR + "prog_index.m3u8"; - String MEDIA_PLAYLIST_3_DIR = "gear3/"; - String MEDIA_PLAYLIST_3_URI = MEDIA_PLAYLIST_3_DIR + "prog_index.m3u8"; - - byte[] MASTER_PLAYLIST_DATA = - ("#EXTM3U\n" - + "#EXT-X-STREAM-INF:BANDWIDTH=232370,CODECS=\"mp4a.40.2, avc1.4d4015\"\n" - + MEDIA_PLAYLIST_1_URI + "\n" - + "#EXT-X-STREAM-INF:BANDWIDTH=649879,CODECS=\"mp4a.40.2, avc1.4d401e\"\n" - + MEDIA_PLAYLIST_2_URI + "\n" - + "#EXT-X-STREAM-INF:BANDWIDTH=991714,CODECS=\"mp4a.40.2, avc1.4d401e\"\n" - + MEDIA_PLAYLIST_3_URI + "\n" - + "#EXT-X-STREAM-INF:BANDWIDTH=41457,CODECS=\"mp4a.40.2\"\n" - + MEDIA_PLAYLIST_0_URI).getBytes(); - - byte[] MEDIA_PLAYLIST_DATA = - ("#EXTM3U\n" - + "#EXT-X-TARGETDURATION:10\n" - + "#EXT-X-VERSION:3\n" - + "#EXT-X-MEDIA-SEQUENCE:0\n" - + "#EXT-X-PLAYLIST-TYPE:VOD\n" - + "#EXTINF:9.97667,\n" - + "fileSequence0.ts\n" - + "#EXTINF:9.97667,\n" - + "fileSequence1.ts\n" - + "#EXTINF:9.97667,\n" - + "fileSequence2.ts\n" - + "#EXT-X-ENDLIST").getBytes(); - - String ENC_MEDIA_PLAYLIST_URI = "enc_index.m3u8"; - - byte[] ENC_MEDIA_PLAYLIST_DATA = - ("#EXTM3U\n" - + "#EXT-X-TARGETDURATION:10\n" - + "#EXT-X-VERSION:3\n" - + "#EXT-X-MEDIA-SEQUENCE:0\n" - + "#EXT-X-PLAYLIST-TYPE:VOD\n" - + "#EXT-X-KEY:METHOD=AES-128,URI=\"enc.key\"\n" - + "#EXTINF:9.97667,\n" - + "fileSequence0.ts\n" - + "#EXTINF:9.97667,\n" - + "fileSequence1.ts\n" - + "#EXT-X-KEY:METHOD=AES-128,URI=\"enc2.key\"\n" - + "#EXTINF:9.97667,\n" - + "fileSequence2.ts\n" - + "#EXT-X-ENDLIST").getBytes(); - -} diff --git a/library/hls/src/androidTest/AndroidManifest.xml b/library/hls/src/test/AndroidManifest.xml similarity index 71% rename from library/hls/src/androidTest/AndroidManifest.xml rename to library/hls/src/test/AndroidManifest.xml index b1aadd203b..331f3439ad 100644 --- a/library/hls/src/androidTest/AndroidManifest.xml +++ b/library/hls/src/test/AndroidManifest.xml @@ -18,16 +18,6 @@ xmlns:tools="http://schemas.android.com/tools" package="com.google.android.exoplayer2.source.hls.test"> - - - - - - - + diff --git a/library/hls/src/test/java/com/google/android/exoplayer2/source/hls/offline/HlsDownloadTestData.java b/library/hls/src/test/java/com/google/android/exoplayer2/source/hls/offline/HlsDownloadTestData.java new file mode 100644 index 0000000000..55db28a59a --- /dev/null +++ b/library/hls/src/test/java/com/google/android/exoplayer2/source/hls/offline/HlsDownloadTestData.java @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2017 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.offline; + +import com.google.android.exoplayer2.C; +import java.nio.charset.Charset; + +/** Data for HLS downloading tests. */ +/* package */ interface HlsDownloadTestData { + + String MASTER_PLAYLIST_URI = "test.m3u8"; + + String MEDIA_PLAYLIST_0_DIR = "gear0/"; + String MEDIA_PLAYLIST_0_URI = MEDIA_PLAYLIST_0_DIR + "prog_index.m3u8"; + String MEDIA_PLAYLIST_1_DIR = "gear1/"; + String MEDIA_PLAYLIST_1_URI = MEDIA_PLAYLIST_1_DIR + "prog_index.m3u8"; + String MEDIA_PLAYLIST_2_DIR = "gear2/"; + String MEDIA_PLAYLIST_2_URI = MEDIA_PLAYLIST_2_DIR + "prog_index.m3u8"; + String MEDIA_PLAYLIST_3_DIR = "gear3/"; + String MEDIA_PLAYLIST_3_URI = MEDIA_PLAYLIST_3_DIR + "prog_index.m3u8"; + + byte[] MASTER_PLAYLIST_DATA = + ("#EXTM3U\n" + + "#EXT-X-STREAM-INF:BANDWIDTH=232370,CODECS=\"mp4a.40.2, avc1.4d4015\"\n" + + MEDIA_PLAYLIST_1_URI + + "\n" + + "#EXT-X-STREAM-INF:BANDWIDTH=649879,CODECS=\"mp4a.40.2, avc1.4d401e\"\n" + + MEDIA_PLAYLIST_2_URI + + "\n" + + "#EXT-X-STREAM-INF:BANDWIDTH=991714,CODECS=\"mp4a.40.2, avc1.4d401e\"\n" + + MEDIA_PLAYLIST_3_URI + + "\n" + + "#EXT-X-STREAM-INF:BANDWIDTH=41457,CODECS=\"mp4a.40.2\"\n" + + MEDIA_PLAYLIST_0_URI) + .getBytes(Charset.forName(C.UTF8_NAME)); + + byte[] MEDIA_PLAYLIST_DATA = + ("#EXTM3U\n" + + "#EXT-X-TARGETDURATION:10\n" + + "#EXT-X-VERSION:3\n" + + "#EXT-X-MEDIA-SEQUENCE:0\n" + + "#EXT-X-PLAYLIST-TYPE:VOD\n" + + "#EXTINF:9.97667,\n" + + "fileSequence0.ts\n" + + "#EXTINF:9.97667,\n" + + "fileSequence1.ts\n" + + "#EXTINF:9.97667,\n" + + "fileSequence2.ts\n" + + "#EXT-X-ENDLIST") + .getBytes(Charset.forName(C.UTF8_NAME)); + + String ENC_MEDIA_PLAYLIST_URI = "enc_index.m3u8"; + + byte[] ENC_MEDIA_PLAYLIST_DATA = + ("#EXTM3U\n" + + "#EXT-X-TARGETDURATION:10\n" + + "#EXT-X-VERSION:3\n" + + "#EXT-X-MEDIA-SEQUENCE:0\n" + + "#EXT-X-PLAYLIST-TYPE:VOD\n" + + "#EXT-X-KEY:METHOD=AES-128,URI=\"enc.key\"\n" + + "#EXTINF:9.97667,\n" + + "fileSequence0.ts\n" + + "#EXTINF:9.97667,\n" + + "fileSequence1.ts\n" + + "#EXT-X-KEY:METHOD=AES-128,URI=\"enc2.key\"\n" + + "#EXTINF:9.97667,\n" + + "fileSequence2.ts\n" + + "#EXT-X-ENDLIST") + .getBytes(Charset.forName(C.UTF8_NAME)); +} diff --git a/library/hls/src/androidTest/java/com/google/android/exoplayer2/source/hls/offline/HlsDownloaderTest.java b/library/hls/src/test/java/com/google/android/exoplayer2/source/hls/offline/HlsDownloaderTest.java similarity index 78% rename from library/hls/src/androidTest/java/com/google/android/exoplayer2/source/hls/offline/HlsDownloaderTest.java rename to library/hls/src/test/java/com/google/android/exoplayer2/source/hls/offline/HlsDownloaderTest.java index c068a8182b..1e6b98092b 100644 --- a/library/hls/src/androidTest/java/com/google/android/exoplayer2/source/hls/offline/HlsDownloaderTest.java +++ b/library/hls/src/test/java/com/google/android/exoplayer2/source/hls/offline/HlsDownloaderTest.java @@ -33,7 +33,6 @@ import static com.google.android.exoplayer2.testutil.CacheAsserts.assertCachedDa import static com.google.common.truth.Truth.assertThat; import android.net.Uri; -import android.test.InstrumentationTestCase; import com.google.android.exoplayer2.offline.DownloaderConstructorHelper; import com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist; import com.google.android.exoplayer2.testutil.FakeDataSet; @@ -42,40 +41,47 @@ import com.google.android.exoplayer2.upstream.cache.NoOpCacheEvictor; import com.google.android.exoplayer2.upstream.cache.SimpleCache; import com.google.android.exoplayer2.util.Util; import java.io.File; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; /** Unit tests for {@link HlsDownloader}. */ -public class HlsDownloaderTest extends InstrumentationTestCase { +@RunWith(RobolectricTestRunner.class) +public class HlsDownloaderTest { private SimpleCache cache; private File tempFolder; private FakeDataSet fakeDataSet; private HlsDownloader hlsDownloader; - @Override + @Before public void setUp() throws Exception { - super.setUp(); - tempFolder = Util.createTempDirectory(getInstrumentation().getContext(), "ExoPlayerTest"); + tempFolder = Util.createTempDirectory(RuntimeEnvironment.application, "ExoPlayerTest"); cache = new SimpleCache(tempFolder, new NoOpCacheEvictor()); - fakeDataSet = new FakeDataSet() - .setData(MASTER_PLAYLIST_URI, MASTER_PLAYLIST_DATA) - .setData(MEDIA_PLAYLIST_1_URI, MEDIA_PLAYLIST_DATA) - .setRandomData(MEDIA_PLAYLIST_1_DIR + "fileSequence0.ts", 10) - .setRandomData(MEDIA_PLAYLIST_1_DIR + "fileSequence1.ts", 11) - .setRandomData(MEDIA_PLAYLIST_1_DIR + "fileSequence2.ts", 12) - .setData(MEDIA_PLAYLIST_2_URI, MEDIA_PLAYLIST_DATA) - .setRandomData(MEDIA_PLAYLIST_2_DIR + "fileSequence0.ts", 13) - .setRandomData(MEDIA_PLAYLIST_2_DIR + "fileSequence1.ts", 14) - .setRandomData(MEDIA_PLAYLIST_2_DIR + "fileSequence2.ts", 15); + fakeDataSet = + new FakeDataSet() + .setData(MASTER_PLAYLIST_URI, MASTER_PLAYLIST_DATA) + .setData(MEDIA_PLAYLIST_1_URI, MEDIA_PLAYLIST_DATA) + .setRandomData(MEDIA_PLAYLIST_1_DIR + "fileSequence0.ts", 10) + .setRandomData(MEDIA_PLAYLIST_1_DIR + "fileSequence1.ts", 11) + .setRandomData(MEDIA_PLAYLIST_1_DIR + "fileSequence2.ts", 12) + .setData(MEDIA_PLAYLIST_2_URI, MEDIA_PLAYLIST_DATA) + .setRandomData(MEDIA_PLAYLIST_2_DIR + "fileSequence0.ts", 13) + .setRandomData(MEDIA_PLAYLIST_2_DIR + "fileSequence1.ts", 14) + .setRandomData(MEDIA_PLAYLIST_2_DIR + "fileSequence2.ts", 15); hlsDownloader = getHlsDownloader(MASTER_PLAYLIST_URI); } - @Override + @After public void tearDown() throws Exception { Util.recursiveDelete(tempFolder); - super.tearDown(); } + @Test public void testDownloadManifest() throws Exception { HlsMasterPlaylist manifest = hlsDownloader.getManifest(); @@ -83,17 +89,23 @@ public class HlsDownloaderTest extends InstrumentationTestCase { assertCachedData(cache, fakeDataSet, MASTER_PLAYLIST_URI); } + @Test public void testSelectRepresentationsClearsPreviousSelection() throws Exception { hlsDownloader.selectRepresentations(new String[] {MEDIA_PLAYLIST_1_URI}); hlsDownloader.selectRepresentations(new String[] {MEDIA_PLAYLIST_2_URI}); hlsDownloader.download(null); - assertCachedData(cache, fakeDataSet, MASTER_PLAYLIST_URI, MEDIA_PLAYLIST_2_URI, + assertCachedData( + cache, + fakeDataSet, + MASTER_PLAYLIST_URI, + MEDIA_PLAYLIST_2_URI, MEDIA_PLAYLIST_2_DIR + "fileSequence0.ts", MEDIA_PLAYLIST_2_DIR + "fileSequence1.ts", MEDIA_PLAYLIST_2_DIR + "fileSequence2.ts"); } + @Test public void testCounterMethods() throws Exception { hlsDownloader.selectRepresentations(new String[] {MEDIA_PLAYLIST_1_URI}); hlsDownloader.download(null); @@ -104,12 +116,12 @@ public class HlsDownloaderTest extends InstrumentationTestCase { .isEqualTo(MEDIA_PLAYLIST_DATA.length + 10 + 11 + 12); } + @Test public void testInitStatus() throws Exception { hlsDownloader.selectRepresentations(new String[] {MEDIA_PLAYLIST_1_URI}); hlsDownloader.download(null); - HlsDownloader newHlsDownloader = - getHlsDownloader(MASTER_PLAYLIST_URI); + HlsDownloader newHlsDownloader = getHlsDownloader(MASTER_PLAYLIST_URI); newHlsDownloader.selectRepresentations(new String[] {MEDIA_PLAYLIST_1_URI}); newHlsDownloader.init(); @@ -119,16 +131,22 @@ public class HlsDownloaderTest extends InstrumentationTestCase { .isEqualTo(MEDIA_PLAYLIST_DATA.length + 10 + 11 + 12); } + @Test public void testDownloadRepresentation() throws Exception { hlsDownloader.selectRepresentations(new String[] {MEDIA_PLAYLIST_1_URI}); hlsDownloader.download(null); - assertCachedData(cache, fakeDataSet, MASTER_PLAYLIST_URI, MEDIA_PLAYLIST_1_URI, + assertCachedData( + cache, + fakeDataSet, + MASTER_PLAYLIST_URI, + MEDIA_PLAYLIST_1_URI, MEDIA_PLAYLIST_1_DIR + "fileSequence0.ts", MEDIA_PLAYLIST_1_DIR + "fileSequence1.ts", MEDIA_PLAYLIST_1_DIR + "fileSequence2.ts"); } + @Test public void testDownloadMultipleRepresentations() throws Exception { hlsDownloader.selectRepresentations(new String[] {MEDIA_PLAYLIST_1_URI, MEDIA_PLAYLIST_2_URI}); hlsDownloader.download(null); @@ -136,9 +154,11 @@ public class HlsDownloaderTest extends InstrumentationTestCase { assertCachedData(cache, fakeDataSet); } + @Test public void testDownloadAllRepresentations() throws Exception { // Add data for the rest of the playlists - fakeDataSet.setData(MEDIA_PLAYLIST_0_URI, MEDIA_PLAYLIST_DATA) + fakeDataSet + .setData(MEDIA_PLAYLIST_0_URI, MEDIA_PLAYLIST_DATA) .setRandomData(MEDIA_PLAYLIST_0_DIR + "fileSequence0.ts", 10) .setRandomData(MEDIA_PLAYLIST_0_DIR + "fileSequence1.ts", 11) .setRandomData(MEDIA_PLAYLIST_0_DIR + "fileSequence2.ts", 12) @@ -167,6 +187,7 @@ public class HlsDownloaderTest extends InstrumentationTestCase { hlsDownloader.remove(); } + @Test public void testRemoveAll() throws Exception { hlsDownloader.selectRepresentations(new String[] {MEDIA_PLAYLIST_1_URI, MEDIA_PLAYLIST_2_URI}); hlsDownloader.download(null); @@ -175,27 +196,32 @@ public class HlsDownloaderTest extends InstrumentationTestCase { assertCacheEmpty(cache); } + @Test public void testDownloadMediaPlaylist() throws Exception { hlsDownloader = getHlsDownloader(MEDIA_PLAYLIST_1_URI); hlsDownloader.selectRepresentations(new String[] {MEDIA_PLAYLIST_1_URI}); hlsDownloader.download(null); - assertCachedData(cache, fakeDataSet, MEDIA_PLAYLIST_1_URI, + assertCachedData( + cache, + fakeDataSet, + MEDIA_PLAYLIST_1_URI, MEDIA_PLAYLIST_1_DIR + "fileSequence0.ts", MEDIA_PLAYLIST_1_DIR + "fileSequence1.ts", MEDIA_PLAYLIST_1_DIR + "fileSequence2.ts"); } + @Test public void testDownloadEncMediaPlaylist() throws Exception { - fakeDataSet = new FakeDataSet() - .setData(ENC_MEDIA_PLAYLIST_URI, ENC_MEDIA_PLAYLIST_DATA) - .setRandomData("enc.key", 8) - .setRandomData("enc2.key", 9) - .setRandomData("fileSequence0.ts", 10) - .setRandomData("fileSequence1.ts", 11) - .setRandomData("fileSequence2.ts", 12); - hlsDownloader = - getHlsDownloader(ENC_MEDIA_PLAYLIST_URI); + fakeDataSet = + new FakeDataSet() + .setData(ENC_MEDIA_PLAYLIST_URI, ENC_MEDIA_PLAYLIST_DATA) + .setRandomData("enc.key", 8) + .setRandomData("enc2.key", 9) + .setRandomData("fileSequence0.ts", 10) + .setRandomData("fileSequence1.ts", 11) + .setRandomData("fileSequence2.ts", 12); + hlsDownloader = getHlsDownloader(ENC_MEDIA_PLAYLIST_URI); hlsDownloader.selectRepresentations(new String[] {ENC_MEDIA_PLAYLIST_URI}); hlsDownloader.download(null); @@ -204,8 +230,7 @@ public class HlsDownloaderTest extends InstrumentationTestCase { private HlsDownloader getHlsDownloader(String mediaPlaylistUri) { Factory factory = new Factory(null).setFakeDataSet(fakeDataSet); - return new HlsDownloader(Uri.parse(mediaPlaylistUri), - new DownloaderConstructorHelper(cache, factory)); + return new HlsDownloader( + Uri.parse(mediaPlaylistUri), new DownloaderConstructorHelper(cache, factory)); } - } diff --git a/library/hls/src/androidTest/java/com/google/android/exoplayer2/source/hls/playlist/HlsMasterPlaylistParserTest.java b/library/hls/src/test/java/com/google/android/exoplayer2/source/hls/playlist/HlsMasterPlaylistParserTest.java similarity index 59% rename from library/hls/src/androidTest/java/com/google/android/exoplayer2/source/hls/playlist/HlsMasterPlaylistParserTest.java rename to library/hls/src/test/java/com/google/android/exoplayer2/source/hls/playlist/HlsMasterPlaylistParserTest.java index 2944d01b57..86426e1f94 100644 --- a/library/hls/src/androidTest/java/com/google/android/exoplayer2/source/hls/playlist/HlsMasterPlaylistParserTest.java +++ b/library/hls/src/test/java/com/google/android/exoplayer2/source/hls/playlist/HlsMasterPlaylistParserTest.java @@ -16,6 +16,7 @@ package com.google.android.exoplayer2.source.hls.playlist; import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.fail; import android.net.Uri; import com.google.android.exoplayer2.C; @@ -26,70 +27,85 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.nio.charset.Charset; import java.util.List; -import junit.framework.TestCase; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; -/** - * Test for {@link HlsMasterPlaylistParserTest}. - */ -public class HlsMasterPlaylistParserTest extends TestCase { +/** Test for {@link HlsMasterPlaylistParserTest}. */ +@RunWith(RobolectricTestRunner.class) +public class HlsMasterPlaylistParserTest { private static final String PLAYLIST_URI = "https://example.com/test.m3u8"; - private static final String PLAYLIST_SIMPLE = " #EXTM3U \n" - + "\n" - + "#EXT-X-STREAM-INF:BANDWIDTH=1280000,CODECS=\"mp4a.40.2,avc1.66.30\",RESOLUTION=304x128\n" - + "http://example.com/low.m3u8\n" - + "\n" - + "#EXT-X-STREAM-INF:BANDWIDTH=1280000,CODECS=\"mp4a.40.2 , avc1.66.30 \"\n" - + "http://example.com/spaces_in_codecs.m3u8\n" - + "\n" - + "#EXT-X-STREAM-INF:BANDWIDTH=2560000,FRAME-RATE=25,RESOLUTION=384x160\n" - + "http://example.com/mid.m3u8\n" - + "\n" - + "#EXT-X-STREAM-INF:BANDWIDTH=7680000,FRAME-RATE=29.997\n" - + "http://example.com/hi.m3u8\n" - + "\n" - + "#EXT-X-STREAM-INF:BANDWIDTH=65000,CODECS=\"mp4a.40.5\"\n" - + "http://example.com/audio-only.m3u8"; + private static final String PLAYLIST_SIMPLE = + " #EXTM3U \n" + + "\n" + + "#EXT-X-STREAM-INF:BANDWIDTH=1280000," + + "CODECS=\"mp4a.40.2,avc1.66.30\",RESOLUTION=304x128\n" + + "http://example.com/low.m3u8\n" + + "\n" + + "#EXT-X-STREAM-INF:BANDWIDTH=1280000,CODECS=\"mp4a.40.2 , avc1.66.30 \"\n" + + "http://example.com/spaces_in_codecs.m3u8\n" + + "\n" + + "#EXT-X-STREAM-INF:BANDWIDTH=2560000,FRAME-RATE=25,RESOLUTION=384x160\n" + + "http://example.com/mid.m3u8\n" + + "\n" + + "#EXT-X-STREAM-INF:BANDWIDTH=7680000,FRAME-RATE=29.997\n" + + "http://example.com/hi.m3u8\n" + + "\n" + + "#EXT-X-STREAM-INF:BANDWIDTH=65000,CODECS=\"mp4a.40.5\"\n" + + "http://example.com/audio-only.m3u8"; - private static final String PLAYLIST_WITH_AVG_BANDWIDTH = " #EXTM3U \n" - + "\n" - + "#EXT-X-STREAM-INF:BANDWIDTH=1280000,CODECS=\"mp4a.40.2,avc1.66.30\",RESOLUTION=304x128\n" - + "http://example.com/low.m3u8\n" - + "\n" - + "#EXT-X-STREAM-INF:BANDWIDTH=1280000,AVERAGE-BANDWIDTH=1270000," - + "CODECS=\"mp4a.40.2 , avc1.66.30 \"\n" - + "http://example.com/spaces_in_codecs.m3u8\n"; + private static final String PLAYLIST_WITH_AVG_BANDWIDTH = + " #EXTM3U \n" + + "\n" + + "#EXT-X-STREAM-INF:BANDWIDTH=1280000," + + "CODECS=\"mp4a.40.2,avc1.66.30\",RESOLUTION=304x128\n" + + "http://example.com/low.m3u8\n" + + "\n" + + "#EXT-X-STREAM-INF:BANDWIDTH=1280000,AVERAGE-BANDWIDTH=1270000," + + "CODECS=\"mp4a.40.2 , avc1.66.30 \"\n" + + "http://example.com/spaces_in_codecs.m3u8\n"; - private static final String PLAYLIST_WITH_INVALID_HEADER = "#EXTMU3\n" - + "#EXT-X-STREAM-INF:BANDWIDTH=1280000,CODECS=\"mp4a.40.2,avc1.66.30\",RESOLUTION=304x128\n" - + "http://example.com/low.m3u8\n"; + private static final String PLAYLIST_WITH_INVALID_HEADER = + "#EXTMU3\n" + + "#EXT-X-STREAM-INF:BANDWIDTH=1280000," + + "CODECS=\"mp4a.40.2,avc1.66.30\",RESOLUTION=304x128\n" + + "http://example.com/low.m3u8\n"; - private static final String PLAYLIST_WITH_CC = " #EXTM3U \n" - + "#EXT-X-MEDIA:TYPE=CLOSED-CAPTIONS,LANGUAGE=\"es\",NAME=\"Eng\",INSTREAM-ID=\"SERVICE4\"\n" - + "#EXT-X-STREAM-INF:BANDWIDTH=1280000,CODECS=\"mp4a.40.2,avc1.66.30\",RESOLUTION=304x128\n" - + "http://example.com/low.m3u8\n"; + private static final String PLAYLIST_WITH_CC = + " #EXTM3U \n" + + "#EXT-X-MEDIA:TYPE=CLOSED-CAPTIONS," + + "LANGUAGE=\"es\",NAME=\"Eng\",INSTREAM-ID=\"SERVICE4\"\n" + + "#EXT-X-STREAM-INF:BANDWIDTH=1280000," + + "CODECS=\"mp4a.40.2,avc1.66.30\",RESOLUTION=304x128\n" + + "http://example.com/low.m3u8\n"; - private static final String PLAYLIST_WITHOUT_CC = " #EXTM3U \n" - + "#EXT-X-MEDIA:TYPE=CLOSED-CAPTIONS,LANGUAGE=\"es\",NAME=\"Eng\",INSTREAM-ID=\"SERVICE4\"\n" - + "#EXT-X-STREAM-INF:BANDWIDTH=1280000,CODECS=\"mp4a.40.2,avc1.66.30\",RESOLUTION=304x128," - + "CLOSED-CAPTIONS=NONE\n" - + "http://example.com/low.m3u8\n"; + private static final String PLAYLIST_WITHOUT_CC = + " #EXTM3U \n" + + "#EXT-X-MEDIA:TYPE=CLOSED-CAPTIONS," + + "LANGUAGE=\"es\",NAME=\"Eng\",INSTREAM-ID=\"SERVICE4\"\n" + + "#EXT-X-STREAM-INF:BANDWIDTH=1280000," + + "CODECS=\"mp4a.40.2,avc1.66.30\",RESOLUTION=304x128," + + "CLOSED-CAPTIONS=NONE\n" + + "http://example.com/low.m3u8\n"; - private static final String PLAYLIST_WITH_AUDIO_MEDIA_TAG = "#EXTM3U\n" - + "#EXT-X-STREAM-INF:BANDWIDTH=2227464,CODECS=\"avc1.640020,mp4a.40.2\",AUDIO=\"aud1\"\n" - + "uri1.m3u8\n" - + "#EXT-X-STREAM-INF:BANDWIDTH=8178040,CODECS=\"avc1.64002a,mp4a.40.2\",AUDIO=\"aud1\"\n" - + "uri2.m3u8\n" - + "#EXT-X-STREAM-INF:BANDWIDTH=2448841,CODECS=\"avc1.640020,ac-3\",AUDIO=\"aud2\"\n" - + "uri1.m3u8\n" - + "#EXT-X-STREAM-INF:BANDWIDTH=8399417,CODECS=\"avc1.64002a,ac-3\",AUDIO=\"aud2\"\n" - + "uri2.m3u8\n" - + "#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID=\"aud1\",LANGUAGE=\"en\",NAME=\"English\"," - + "AUTOSELECT=YES,DEFAULT=YES,CHANNELS=\"2\",URI=\"a1/prog_index.m3u8\"\n" - + "#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID=\"aud2\",LANGUAGE=\"en\",NAME=\"English\"," - + "AUTOSELECT=YES,DEFAULT=YES,CHANNELS=\"6\",URI=\"a2/prog_index.m3u8\"\n"; + private static final String PLAYLIST_WITH_AUDIO_MEDIA_TAG = + "#EXTM3U\n" + + "#EXT-X-STREAM-INF:BANDWIDTH=2227464,CODECS=\"avc1.640020,mp4a.40.2\",AUDIO=\"aud1\"\n" + + "uri1.m3u8\n" + + "#EXT-X-STREAM-INF:BANDWIDTH=8178040,CODECS=\"avc1.64002a,mp4a.40.2\",AUDIO=\"aud1\"\n" + + "uri2.m3u8\n" + + "#EXT-X-STREAM-INF:BANDWIDTH=2448841,CODECS=\"avc1.640020,ac-3\",AUDIO=\"aud2\"\n" + + "uri1.m3u8\n" + + "#EXT-X-STREAM-INF:BANDWIDTH=8399417,CODECS=\"avc1.64002a,ac-3\",AUDIO=\"aud2\"\n" + + "uri2.m3u8\n" + + "#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID=\"aud1\",LANGUAGE=\"en\",NAME=\"English\"," + + "AUTOSELECT=YES,DEFAULT=YES,CHANNELS=\"2\",URI=\"a1/prog_index.m3u8\"\n" + + "#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID=\"aud2\",LANGUAGE=\"en\",NAME=\"English\"," + + "AUTOSELECT=YES,DEFAULT=YES,CHANNELS=\"6\",URI=\"a2/prog_index.m3u8\"\n"; + @Test public void testParseMasterPlaylist() throws IOException { HlsMasterPlaylist masterPlaylist = parseMasterPlaylist(PLAYLIST_URI, PLAYLIST_SIMPLE); @@ -129,9 +145,10 @@ public class HlsMasterPlaylistParserTest extends TestCase { assertThat(variants.get(4).url).isEqualTo("http://example.com/audio-only.m3u8"); } + @Test public void testMasterPlaylistWithBandwdithAverage() throws IOException { - HlsMasterPlaylist masterPlaylist = parseMasterPlaylist(PLAYLIST_URI, - PLAYLIST_WITH_AVG_BANDWIDTH); + HlsMasterPlaylist masterPlaylist = + parseMasterPlaylist(PLAYLIST_URI, PLAYLIST_WITH_AVG_BANDWIDTH); List variants = masterPlaylist.variants; @@ -139,6 +156,7 @@ public class HlsMasterPlaylistParserTest extends TestCase { assertThat(variants.get(1).format.bitrate).isEqualTo(1270000); } + @Test public void testPlaylistWithInvalidHeader() throws IOException { try { parseMasterPlaylist(PLAYLIST_URI, PLAYLIST_WITH_INVALID_HEADER); @@ -148,6 +166,7 @@ public class HlsMasterPlaylistParserTest extends TestCase { } } + @Test public void testPlaylistWithClosedCaption() throws IOException { HlsMasterPlaylist playlist = parseMasterPlaylist(PLAYLIST_URI, PLAYLIST_WITH_CC); assertThat(playlist.muxedCaptionFormats).hasSize(1); @@ -157,11 +176,13 @@ public class HlsMasterPlaylistParserTest extends TestCase { assertThat(closedCaptionFormat.language).isEqualTo("es"); } + @Test public void testPlaylistWithoutClosedCaptions() throws IOException { HlsMasterPlaylist playlist = parseMasterPlaylist(PLAYLIST_URI, PLAYLIST_WITHOUT_CC); assertThat(playlist.muxedCaptionFormats).isEmpty(); } + @Test public void testCodecPropagation() throws IOException { HlsMasterPlaylist playlist = parseMasterPlaylist(PLAYLIST_URI, PLAYLIST_WITH_AUDIO_MEDIA_TAG); @@ -177,9 +198,8 @@ public class HlsMasterPlaylistParserTest extends TestCase { private static HlsMasterPlaylist parseMasterPlaylist(String uri, String playlistString) throws IOException { Uri playlistUri = Uri.parse(uri); - ByteArrayInputStream inputStream = new ByteArrayInputStream( - playlistString.getBytes(Charset.forName(C.UTF8_NAME))); + ByteArrayInputStream inputStream = + new ByteArrayInputStream(playlistString.getBytes(Charset.forName(C.UTF8_NAME))); return (HlsMasterPlaylist) new HlsPlaylistParser().parse(playlistUri, inputStream); } - } diff --git a/library/hls/src/androidTest/java/com/google/android/exoplayer2/source/hls/playlist/HlsMediaPlaylistParserTest.java b/library/hls/src/test/java/com/google/android/exoplayer2/source/hls/playlist/HlsMediaPlaylistParserTest.java similarity index 76% rename from library/hls/src/androidTest/java/com/google/android/exoplayer2/source/hls/playlist/HlsMediaPlaylistParserTest.java rename to library/hls/src/test/java/com/google/android/exoplayer2/source/hls/playlist/HlsMediaPlaylistParserTest.java index 97a5386b04..b10997cfe9 100644 --- a/library/hls/src/androidTest/java/com/google/android/exoplayer2/source/hls/playlist/HlsMediaPlaylistParserTest.java +++ b/library/hls/src/test/java/com/google/android/exoplayer2/source/hls/playlist/HlsMediaPlaylistParserTest.java @@ -26,49 +26,53 @@ import java.io.InputStream; import java.nio.charset.Charset; import java.util.List; import java.util.Locale; -import junit.framework.TestCase; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; -/** - * Test for {@link HlsMediaPlaylistParserTest}. - */ -public class HlsMediaPlaylistParserTest extends TestCase { +/** Test for {@link HlsMediaPlaylistParserTest}. */ +@RunWith(RobolectricTestRunner.class) +public class HlsMediaPlaylistParserTest { - public void testParseMediaPlaylist() throws IOException { + @Test + public void testParseMediaPlaylist() throws Exception { Uri playlistUri = Uri.parse("https://example.com/test.m3u8"); - String playlistString = "#EXTM3U\n" - + "#EXT-X-VERSION:3\n" - + "#EXT-X-PLAYLIST-TYPE:VOD\n" - + "#EXT-X-START:TIME-OFFSET=-25" - + "#EXT-X-TARGETDURATION:8\n" - + "#EXT-X-MEDIA-SEQUENCE:2679\n" - + "#EXT-X-DISCONTINUITY-SEQUENCE:4\n" - + "#EXT-X-ALLOW-CACHE:YES\n" - + "\n" - + "#EXTINF:7.975,\n" - + "#EXT-X-BYTERANGE:51370@0\n" - + "https://priv.example.com/fileSequence2679.ts\n" - + "\n" - + "#EXT-X-KEY:METHOD=AES-128,URI=\"https://priv.example.com/key.php?r=2680\",IV=0x1566B\n" - + "#EXTINF:7.975,\n" - + "#EXT-X-BYTERANGE:51501@2147483648\n" - + "https://priv.example.com/fileSequence2680.ts\n" - + "\n" - + "#EXT-X-KEY:METHOD=NONE\n" - + "#EXTINF:7.941,\n" - + "#EXT-X-BYTERANGE:51501\n" // @2147535149 - + "https://priv.example.com/fileSequence2681.ts\n" - + "\n" - + "#EXT-X-DISCONTINUITY\n" - + "#EXT-X-KEY:METHOD=AES-128,URI=\"https://priv.example.com/key.php?r=2682\"\n" - + "#EXTINF:7.975,\n" - + "#EXT-X-BYTERANGE:51740\n" // @2147586650 - + "https://priv.example.com/fileSequence2682.ts\n" - + "\n" - + "#EXTINF:7.975,\n" - + "https://priv.example.com/fileSequence2683.ts\n" - + "#EXT-X-ENDLIST"; - InputStream inputStream = new ByteArrayInputStream( - playlistString.getBytes(Charset.forName(C.UTF8_NAME))); + String playlistString = + "#EXTM3U\n" + + "#EXT-X-VERSION:3\n" + + "#EXT-X-PLAYLIST-TYPE:VOD\n" + + "#EXT-X-START:TIME-OFFSET=-25" + + "#EXT-X-TARGETDURATION:8\n" + + "#EXT-X-MEDIA-SEQUENCE:2679\n" + + "#EXT-X-DISCONTINUITY-SEQUENCE:4\n" + + "#EXT-X-ALLOW-CACHE:YES\n" + + "\n" + + "#EXTINF:7.975,\n" + + "#EXT-X-BYTERANGE:51370@0\n" + + "https://priv.example.com/fileSequence2679.ts\n" + + "\n" + + "#EXT-X-KEY:METHOD=AES-128," + + "URI=\"https://priv.example.com/key.php?r=2680\",IV=0x1566B\n" + + "#EXTINF:7.975,\n" + + "#EXT-X-BYTERANGE:51501@2147483648\n" + + "https://priv.example.com/fileSequence2680.ts\n" + + "\n" + + "#EXT-X-KEY:METHOD=NONE\n" + + "#EXTINF:7.941,\n" + + "#EXT-X-BYTERANGE:51501\n" // @2147535149 + + "https://priv.example.com/fileSequence2681.ts\n" + + "\n" + + "#EXT-X-DISCONTINUITY\n" + + "#EXT-X-KEY:METHOD=AES-128,URI=\"https://priv.example.com/key.php?r=2682\"\n" + + "#EXTINF:7.975,\n" + + "#EXT-X-BYTERANGE:51740\n" // @2147586650 + + "https://priv.example.com/fileSequence2682.ts\n" + + "\n" + + "#EXTINF:7.975,\n" + + "https://priv.example.com/fileSequence2683.ts\n" + + "#EXT-X-ENDLIST"; + InputStream inputStream = + new ByteArrayInputStream(playlistString.getBytes(Charset.forName(C.UTF8_NAME))); HlsPlaylist playlist = new HlsPlaylistParser().parse(playlistUri, inputStream); HlsMediaPlaylist mediaPlaylist = (HlsMediaPlaylist) playlist; @@ -136,6 +140,7 @@ public class HlsMediaPlaylistParserTest extends TestCase { assertThat(segment.url).isEqualTo("https://priv.example.com/fileSequence2683.ts"); } + @Test public void testGapTag() throws IOException { Uri playlistUri = Uri.parse("https://example.com/test2.m3u8"); String playlistString = @@ -170,5 +175,4 @@ public class HlsMediaPlaylistParserTest extends TestCase { assertThat(playlist.segments.get(2).hasGapTag).isTrue(); assertThat(playlist.segments.get(3).hasGapTag).isFalse(); } - } diff --git a/library/hls/src/test/resources/robolectric.properties b/library/hls/src/test/resources/robolectric.properties new file mode 100644 index 0000000000..2f3210368e --- /dev/null +++ b/library/hls/src/test/resources/robolectric.properties @@ -0,0 +1 @@ +manifest=src/test/AndroidManifest.xml diff --git a/library/smoothstreaming/build.gradle b/library/smoothstreaming/build.gradle index ee5a8c4e73..b85f25e656 100644 --- a/library/smoothstreaming/build.gradle +++ b/library/smoothstreaming/build.gradle @@ -35,10 +35,7 @@ android { dependencies { compile project(modulePrefix + 'library-core') compile 'com.android.support:support-annotations:' + supportLibraryVersion - androidTestCompile project(modulePrefix + 'testutils') - androidTestCompile 'com.google.dexmaker:dexmaker:' + dexmakerVersion - androidTestCompile 'com.google.dexmaker:dexmaker-mockito:' + dexmakerVersion - androidTestCompile 'org.mockito:mockito-core:' + mockitoVersion + testCompile project(modulePrefix + 'testutils-robolectric') } ext { diff --git a/library/smoothstreaming/src/androidTest/AndroidManifest.xml b/library/smoothstreaming/src/test/AndroidManifest.xml similarity index 70% rename from library/smoothstreaming/src/androidTest/AndroidManifest.xml rename to library/smoothstreaming/src/test/AndroidManifest.xml index 8e1e69509d..1a8f8ee9c4 100644 --- a/library/smoothstreaming/src/androidTest/AndroidManifest.xml +++ b/library/smoothstreaming/src/test/AndroidManifest.xml @@ -18,16 +18,6 @@ xmlns:tools="http://schemas.android.com/tools" package="com.google.android.exoplayer2.source.smoothstreaming.test"> - - - - - - - + diff --git a/library/smoothstreaming/src/androidTest/assets/sample_ismc_1 b/library/smoothstreaming/src/test/assets/sample_ismc_1 similarity index 100% rename from library/smoothstreaming/src/androidTest/assets/sample_ismc_1 rename to library/smoothstreaming/src/test/assets/sample_ismc_1 diff --git a/library/smoothstreaming/src/androidTest/assets/sample_ismc_2 b/library/smoothstreaming/src/test/assets/sample_ismc_2 similarity index 100% rename from library/smoothstreaming/src/androidTest/assets/sample_ismc_2 rename to library/smoothstreaming/src/test/assets/sample_ismc_2 diff --git a/library/smoothstreaming/src/androidTest/java/com/google/android/exoplayer2/source/smoothstreaming/manifest/SsManifestParserTest.java b/library/smoothstreaming/src/test/java/com/google/android/exoplayer2/source/smoothstreaming/manifest/SsManifestParserTest.java similarity index 60% rename from library/smoothstreaming/src/androidTest/java/com/google/android/exoplayer2/source/smoothstreaming/manifest/SsManifestParserTest.java rename to library/smoothstreaming/src/test/java/com/google/android/exoplayer2/source/smoothstreaming/manifest/SsManifestParserTest.java index 4663f014ff..2ce9fec970 100644 --- a/library/smoothstreaming/src/androidTest/java/com/google/android/exoplayer2/source/smoothstreaming/manifest/SsManifestParserTest.java +++ b/library/smoothstreaming/src/test/java/com/google/android/exoplayer2/source/smoothstreaming/manifest/SsManifestParserTest.java @@ -16,27 +16,29 @@ package com.google.android.exoplayer2.source.smoothstreaming.manifest; import android.net.Uri; -import android.test.InstrumentationTestCase; import com.google.android.exoplayer2.testutil.TestUtil; import java.io.IOException; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; -/** - * Unit tests for {@link SsManifestParser}. - */ -public final class SsManifestParserTest extends InstrumentationTestCase { +/** Unit tests for {@link SsManifestParser}. */ +@RunWith(RobolectricTestRunner.class) +public final class SsManifestParserTest { private static final String SAMPLE_ISMC_1 = "sample_ismc_1"; private static final String SAMPLE_ISMC_2 = "sample_ismc_2"; - /** - * Simple test to ensure the sample manifests parse without any exceptions being thrown. - */ + /** Simple test to ensure the sample manifests parse without any exceptions being thrown. */ + @Test public void testParseSmoothStreamingManifest() throws IOException { SsManifestParser parser = new SsManifestParser(); - parser.parse(Uri.parse("https://example.com/test.ismc"), - TestUtil.getInputStream(getInstrumentation(), SAMPLE_ISMC_1)); - parser.parse(Uri.parse("https://example.com/test.ismc"), - TestUtil.getInputStream(getInstrumentation(), SAMPLE_ISMC_2)); + parser.parse( + Uri.parse("https://example.com/test.ismc"), + TestUtil.getInputStream(RuntimeEnvironment.application, SAMPLE_ISMC_1)); + parser.parse( + Uri.parse("https://example.com/test.ismc"), + TestUtil.getInputStream(RuntimeEnvironment.application, SAMPLE_ISMC_2)); } - } diff --git a/library/smoothstreaming/src/androidTest/java/com/google/android/exoplayer2/source/smoothstreaming/manifest/SsManifestTest.java b/library/smoothstreaming/src/test/java/com/google/android/exoplayer2/source/smoothstreaming/manifest/SsManifestTest.java similarity index 77% rename from library/smoothstreaming/src/androidTest/java/com/google/android/exoplayer2/source/smoothstreaming/manifest/SsManifestTest.java rename to library/smoothstreaming/src/test/java/com/google/android/exoplayer2/source/smoothstreaming/manifest/SsManifestTest.java index ecf9e77d28..fbb2c3d4c4 100644 --- a/library/smoothstreaming/src/androidTest/java/com/google/android/exoplayer2/source/smoothstreaming/manifest/SsManifestTest.java +++ b/library/smoothstreaming/src/test/java/com/google/android/exoplayer2/source/smoothstreaming/manifest/SsManifestTest.java @@ -26,52 +26,49 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Random; -import junit.framework.TestCase; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; -/** - * Unit tests for {@link SsManifest}. - */ -public class SsManifestTest extends TestCase { +/** Unit tests for {@link SsManifest}. */ +@RunWith(RobolectricTestRunner.class) +public class SsManifestTest { private static final ProtectionElement DUMMY_PROTECTION_ELEMENT = new ProtectionElement(C.WIDEVINE_UUID, new byte[] {0, 1, 2}); + @Test public void testCopy() throws Exception { Format[][] formats = newFormats(2, 3); - SsManifest sourceManifest = newSsManifest( - newStreamElement("1",formats[0]), - newStreamElement("2", formats[1])); + SsManifest sourceManifest = + newSsManifest(newStreamElement("1", formats[0]), newStreamElement("2", formats[1])); - List keys = Arrays.asList( - new TrackKey(0, 0), - new TrackKey(0, 2), - new TrackKey(1, 0)); + List keys = Arrays.asList(new TrackKey(0, 0), new TrackKey(0, 2), new TrackKey(1, 0)); // Keys don't need to be in any particular order Collections.shuffle(keys, new Random(0)); SsManifest copyManifest = sourceManifest.copy(keys); - SsManifest expectedManifest = newSsManifest( - newStreamElement("1", formats[0][0], formats[0][2]), - newStreamElement("2", formats[1][0])); + SsManifest expectedManifest = + newSsManifest( + newStreamElement("1", formats[0][0], formats[0][2]), + newStreamElement("2", formats[1][0])); assertManifestEquals(expectedManifest, copyManifest); } + @Test public void testCopyRemoveStreamElement() throws Exception { Format[][] formats = newFormats(2, 3); - SsManifest sourceManifest = newSsManifest( - newStreamElement("1", formats[0]), - newStreamElement("2", formats[1])); + SsManifest sourceManifest = + newSsManifest(newStreamElement("1", formats[0]), newStreamElement("2", formats[1])); - List keys = Arrays.asList( - new TrackKey(1, 0)); + List keys = Arrays.asList(new TrackKey(1, 0)); // Keys don't need to be in any particular order Collections.shuffle(keys, new Random(0)); SsManifest copyManifest = sourceManifest.copy(keys); - SsManifest expectedManifest = newSsManifest( - newStreamElement("2", formats[1][0])); + SsManifest expectedManifest = newSsManifest(newStreamElement("2", formats[1][0])); assertManifestEquals(expectedManifest, copyManifest); } @@ -117,13 +114,25 @@ public class SsManifestTest extends TestCase { } private static StreamElement newStreamElement(String name, Format... formats) { - return new StreamElement("baseUri", "chunkTemplate", C.TRACK_TYPE_VIDEO, "subType", - 1000, name, 1024, 768, 1024, 768, null, formats, Collections.emptyList(), 0); + return new StreamElement( + "baseUri", + "chunkTemplate", + C.TRACK_TYPE_VIDEO, + "subType", + 1000, + name, + 1024, + 768, + 1024, + 768, + null, + formats, + Collections.emptyList(), + 0); } private static Format newFormat(String id) { - return Format.createContainerFormat(id, MimeTypes.VIDEO_MP4, MimeTypes.VIDEO_H264, null, - Format.NO_VALUE, 0, null); + return Format.createContainerFormat( + id, MimeTypes.VIDEO_MP4, MimeTypes.VIDEO_H264, null, Format.NO_VALUE, 0, null); } - } diff --git a/library/smoothstreaming/src/test/resources/robolectric.properties b/library/smoothstreaming/src/test/resources/robolectric.properties new file mode 100644 index 0000000000..2f3210368e --- /dev/null +++ b/library/smoothstreaming/src/test/resources/robolectric.properties @@ -0,0 +1 @@ +manifest=src/test/AndroidManifest.xml diff --git a/testutils/build.gradle b/testutils/build.gradle index 7c4b163713..11ec55c047 100644 --- a/testutils/build.gradle +++ b/testutils/build.gradle @@ -35,4 +35,5 @@ dependencies { compile project(modulePrefix + 'library-core') compile 'org.mockito:mockito-core:' + mockitoVersion compile 'com.google.truth:truth:' + truthVersion + testCompile project(modulePrefix + 'testutils-robolectric') } diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/MockitoUtil.java b/testutils/src/main/java/com/google/android/exoplayer2/testutil/MockitoUtil.java deleted file mode 100644 index 6bd1048bc0..0000000000 --- a/testutils/src/main/java/com/google/android/exoplayer2/testutil/MockitoUtil.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C) 2017 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.testutil; - -import android.content.Context; -import android.test.InstrumentationTestCase; -import org.mockito.MockitoAnnotations; - -/** - * Utility for setting up Mockito for instrumentation tests. - */ -public final class MockitoUtil { - - /** - * Sets up Mockito for an instrumentation test. - * - * @param instrumentationTestCase The instrumentation test case class. - */ - public static void setUpMockito(InstrumentationTestCase instrumentationTestCase) { - // Workaround for https://code.google.com/p/dexmaker/issues/detail?id=2. - System.setProperty("dexmaker.dexcache", - instrumentationTestCase.getInstrumentation().getTargetContext().getCacheDir().getPath()); - MockitoAnnotations.initMocks(instrumentationTestCase); - } - - /** - * Sets up Mockito for a JUnit4 test. - * - * @param targetContext The target context. Usually obtained from - * {@code InstrumentationRegistry.getTargetContext()} - * @param testClass The JUnit4 test class. - */ - public static void setUpMockito(Context targetContext, Object testClass) { - // Workaround for https://code.google.com/p/dexmaker/issues/detail?id=2. - System.setProperty("dexmaker.dexcache", targetContext.getCacheDir().getPath()); - MockitoAnnotations.initMocks(testClass); - } - - private MockitoUtil() {} - -} diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/OggTestData.java b/testutils/src/main/java/com/google/android/exoplayer2/testutil/OggTestData.java deleted file mode 100644 index 7cae709438..0000000000 --- a/testutils/src/main/java/com/google/android/exoplayer2/testutil/OggTestData.java +++ /dev/null @@ -1,1073 +0,0 @@ -/* - * Copyright (C) 2016 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.testutil; - -/** - * Provides ogg/vorbis test data in bytes for unit tests. - */ -public final class OggTestData { - - public static FakeExtractorInput createInput(byte[] data, boolean simulateUnknownLength) { - return new FakeExtractorInput.Builder().setData(data).setSimulateIOErrors(true) - .setSimulateUnknownLength(simulateUnknownLength).setSimulatePartialReads(true).build(); - } - - public static byte[] buildOggHeader(int headerType, long granule, int pageSequenceCounter, - int pageSegmentCount) { - return TestUtil.createByteArray( - 0x4F, 0x67, 0x67, 0x53, // Oggs. - 0x00, // Stream revision. - headerType, - (int) (granule) & 0xFF, - (int) (granule >> 8) & 0xFF, - (int) (granule >> 16) & 0xFF, - (int) (granule >> 24) & 0xFF, - (int) (granule >> 32) & 0xFF, - (int) (granule >> 40) & 0xFF, - (int) (granule >> 48) & 0xFF, - (int) (granule >> 56) & 0xFF, - 0x00, // LSB of data serial number. - 0x10, - 0x00, - 0x00, // MSB of data serial number. - (pageSequenceCounter) & 0xFF, - (pageSequenceCounter >> 8) & 0xFF, - (pageSequenceCounter >> 16) & 0xFF, - (pageSequenceCounter >> 24) & 0xFF, - 0x00, // LSB of page checksum. - 0x00, - 0x10, - 0x00, // MSB of page checksum. - pageSegmentCount); - } - - /** - * Returns the initial two pages of bytes which by spec contain the three vorbis header packets: - * identification, comment and setup header. - */ - public static byte[] getVorbisHeaderPages() { - byte[] data = new byte[VORBIS_HEADER_PAGES.length]; - System.arraycopy(VORBIS_HEADER_PAGES, 0, data, 0, - VORBIS_HEADER_PAGES.length); - return data; - } - - /** - * Returns a valid vorbis identification header in bytes. - */ - public static byte[] getIdentificationHeaderData() { - int idHeaderStart = 28; - int idHeaderLength = 30; - byte[] idHeaderData = new byte[idHeaderLength]; - System.arraycopy(VORBIS_HEADER_PAGES, idHeaderStart, idHeaderData, 0, idHeaderLength); - return idHeaderData; - } - - /** - * Returns a valid vorbis comment header with 3 comments including utf8 chars in bytes. - */ - public static byte[] getCommentHeaderDataUTF8() { - byte[] commentHeaderData = new byte[COMMENT_HEADER_WITH_UTF8.length]; - System.arraycopy(COMMENT_HEADER_WITH_UTF8, 0, commentHeaderData, 0, - COMMENT_HEADER_WITH_UTF8.length); - return commentHeaderData; - } - - /** - * Returns a valid vorbis setup header in bytes. - */ - public static byte[] getSetupHeaderData() { - int setupHeaderStart = 146; - int setupHeaderLength = VORBIS_HEADER_PAGES.length - setupHeaderStart; - byte[] setupHeaderData = new byte[setupHeaderLength]; - System.arraycopy(VORBIS_HEADER_PAGES, setupHeaderStart, setupHeaderData, 0, setupHeaderLength); - return setupHeaderData; - } - - private static final byte[] COMMENT_HEADER_WITH_UTF8 = { - (byte) 0x03, (byte) 0x76, (byte) 0x6f, (byte) 0x72, // 3, v, o, r, - (byte) 0x62, (byte) 0x69, (byte) 0x73, (byte) 0x2b, // b, i, s, . - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x58, - (byte) 0x69, (byte) 0x70, (byte) 0x68, (byte) 0x2e, - (byte) 0x4f, (byte) 0x72, (byte) 0x67, (byte) 0x20, - (byte) 0x6c, (byte) 0x69, (byte) 0x62, (byte) 0x56, - (byte) 0x6f, (byte) 0x72, (byte) 0x62, (byte) 0x69, - (byte) 0x73, (byte) 0x20, (byte) 0x49, (byte) 0x20, - (byte) 0x32, (byte) 0x30, (byte) 0x31, (byte) 0x32, - (byte) 0x30, (byte) 0x32, (byte) 0x30, (byte) 0x33, - (byte) 0x20, (byte) 0x28, (byte) 0x4f, (byte) 0x6d, - (byte) 0x6e, (byte) 0x69, (byte) 0x70, (byte) 0x72, - (byte) 0x65, (byte) 0x73, (byte) 0x65, (byte) 0x6e, - (byte) 0x74, (byte) 0x29, (byte) 0x03, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x0a, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x41, (byte) 0x4c, - (byte) 0x42, (byte) 0x55, (byte) 0x4d, (byte) 0x3d, - (byte) 0xc3, (byte) 0xa4, (byte) 0xc3, (byte) 0xb6, - (byte) 0x13, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x54, (byte) 0x49, (byte) 0x54, (byte) 0x4c, - (byte) 0x45, (byte) 0x3d, (byte) 0x41, (byte) 0x20, - (byte) 0x73, (byte) 0x61, (byte) 0x6d, (byte) 0x70, - (byte) 0x6c, (byte) 0x65, (byte) 0x20, (byte) 0x73, - (byte) 0x6f, (byte) 0x6e, (byte) 0x67, (byte) 0x0d, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x41, - (byte) 0x52, (byte) 0x54, (byte) 0x49, (byte) 0x53, - (byte) 0x54, (byte) 0x3d, (byte) 0x47, (byte) 0x6f, - (byte) 0x6f, (byte) 0x67, (byte) 0x6c, (byte) 0x65, - (byte) 0x01 - }; - - // two OGG pages with 3 packets (id, comment and setup header) - // length: 3743 bytes - private static final byte[] VORBIS_HEADER_PAGES = { /* capture pattern ogg header 1 */ - (byte) 0x4f, (byte) 0x67, (byte) 0x67, (byte) 0x53, // O,g,g,S : start pos 0 - (byte) 0x00, (byte) 0x02, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x5e, (byte) 0x5f, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x83, (byte) 0x36, - (byte) 0xe3, (byte) 0x49, (byte) 0x01, (byte) 0x1e, /* capture pattern vorbis id header */ - (byte) 0x01, (byte) 0x76, (byte) 0x6f, (byte) 0x72, // 1,v,o,r : start pos 28 - (byte) 0x62, (byte) 0x69, (byte) 0x73, (byte) 0x00, // b,i,s,. - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x02, - (byte) 0x22, (byte) 0x56, (byte) 0x00, (byte) 0x00, - (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, - (byte) 0x6a, (byte) 0x04, (byte) 0x01, (byte) 0x00, - (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, /* capture pattern ogg header 2 */ - (byte) 0xa9, (byte) 0x01, (byte) 0x4f, (byte) 0x67, // .,.,O,g : start pos 86 - (byte) 0x67, (byte) 0x53, (byte) 0x00, (byte) 0x00, // g,S,.,. - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x5e, (byte) 0x5f, (byte) 0x00, (byte) 0x00, - (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x69, (byte) 0xf8, (byte) 0xeb, (byte) 0xe1, - (byte) 0x10, (byte) 0x2d, (byte) 0xff, (byte) 0xff, - (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, - (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, - (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, /* capture pattern vorbis comment header*/ - (byte) 0x1b, (byte) 0x03, (byte) 0x76, (byte) 0x6f, // .,3,v,o : start pos 101 - (byte) 0x72, (byte) 0x62, (byte) 0x69, (byte) 0x73, // r,b,i,s - (byte) 0x1d, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x58, (byte) 0x69, (byte) 0x70, (byte) 0x68, - (byte) 0x2e, (byte) 0x4f, (byte) 0x72, (byte) 0x67, - (byte) 0x20, (byte) 0x6c, (byte) 0x69, (byte) 0x62, - (byte) 0x56, (byte) 0x6f, (byte) 0x72, (byte) 0x62, - (byte) 0x69, (byte) 0x73, (byte) 0x20, (byte) 0x49, - (byte) 0x20, (byte) 0x32, (byte) 0x30, (byte) 0x30, - (byte) 0x33, (byte) 0x30, (byte) 0x39, (byte) 0x30, - (byte) 0x39, (byte) 0x00, (byte) 0x00, (byte) 0x00, /* capture pattern vorbis setup header */ - (byte) 0x00, (byte) 0x01, (byte) 0x05, (byte) 0x76, // .,.,5,v : start pos 146 - (byte) 0x6f, (byte) 0x72, (byte) 0x62, (byte) 0x69, // o,r,b,i - (byte) 0x73, (byte) 0x22, (byte) 0x42, (byte) 0x43, // s,. - (byte) 0x56, (byte) 0x01, (byte) 0x00, (byte) 0x40, - (byte) 0x00, (byte) 0x00, (byte) 0x18, (byte) 0x42, - (byte) 0x10, (byte) 0x2a, (byte) 0x05, (byte) 0xad, - (byte) 0x63, (byte) 0x8e, (byte) 0x3a, (byte) 0xc8, - (byte) 0x15, (byte) 0x21, (byte) 0x8c, (byte) 0x19, - (byte) 0xa2, (byte) 0xa0, (byte) 0x42, (byte) 0xca, - (byte) 0x29, (byte) 0xc7, (byte) 0x1d, (byte) 0x42, - (byte) 0xd0, (byte) 0x21, (byte) 0xa3, (byte) 0x24, - (byte) 0x43, (byte) 0x88, (byte) 0x3a, (byte) 0xc6, - (byte) 0x35, (byte) 0xc7, (byte) 0x18, (byte) 0x63, - (byte) 0x47, (byte) 0xb9, (byte) 0x64, (byte) 0x8a, - (byte) 0x42, (byte) 0xc9, (byte) 0x81, (byte) 0xd0, - (byte) 0x90, (byte) 0x55, (byte) 0x00, (byte) 0x00, - (byte) 0x40, (byte) 0x00, (byte) 0x00, (byte) 0xa4, - (byte) 0x1c, (byte) 0x57, (byte) 0x50, (byte) 0x72, - (byte) 0x49, (byte) 0x2d, (byte) 0xe7, (byte) 0x9c, - (byte) 0x73, (byte) 0xa3, (byte) 0x18, (byte) 0x57, - (byte) 0xcc, (byte) 0x71, (byte) 0xe8, (byte) 0x20, - (byte) 0xe7, (byte) 0x9c, (byte) 0x73, (byte) 0xe5, - (byte) 0x20, (byte) 0x67, (byte) 0xcc, (byte) 0x71, - (byte) 0x09, (byte) 0x25, (byte) 0xe7, (byte) 0x9c, - (byte) 0x73, (byte) 0x8e, (byte) 0x39, (byte) 0xe7, - (byte) 0x92, (byte) 0x72, (byte) 0x8e, (byte) 0x31, - (byte) 0xe7, (byte) 0x9c, (byte) 0x73, (byte) 0xa3, - (byte) 0x18, (byte) 0x57, (byte) 0x0e, (byte) 0x72, - (byte) 0x29, (byte) 0x2d, (byte) 0xe7, (byte) 0x9c, - (byte) 0x73, (byte) 0x81, (byte) 0x14, (byte) 0x47, - (byte) 0x8a, (byte) 0x71, (byte) 0xa7, (byte) 0x18, - (byte) 0xe7, (byte) 0x9c, (byte) 0x73, (byte) 0xa4, - (byte) 0x1c, (byte) 0x47, (byte) 0x8a, (byte) 0x71, - (byte) 0xa8, (byte) 0x18, (byte) 0xe7, (byte) 0x9c, - (byte) 0x73, (byte) 0x6d, (byte) 0x31, (byte) 0xb7, - (byte) 0x92, (byte) 0x72, (byte) 0xce, (byte) 0x39, - (byte) 0xe7, (byte) 0x9c, (byte) 0x73, (byte) 0xe6, - (byte) 0x20, (byte) 0x87, (byte) 0x52, (byte) 0x72, - (byte) 0xae, (byte) 0x35, (byte) 0xe7, (byte) 0x9c, - (byte) 0x73, (byte) 0xa4, (byte) 0x18, (byte) 0x67, - (byte) 0x0e, (byte) 0x72, (byte) 0x0b, (byte) 0x25, - (byte) 0xe7, (byte) 0x9c, (byte) 0x73, (byte) 0xc6, - (byte) 0x20, (byte) 0x67, (byte) 0xcc, (byte) 0x71, - (byte) 0xeb, (byte) 0x20, (byte) 0xe7, (byte) 0x9c, - (byte) 0x73, (byte) 0x8c, (byte) 0x35, (byte) 0xb7, - (byte) 0xd4, (byte) 0x72, (byte) 0xce, (byte) 0x39, - (byte) 0xe7, (byte) 0x9c, (byte) 0x73, (byte) 0xce, - (byte) 0x39, (byte) 0xe7, (byte) 0x9c, (byte) 0x73, - (byte) 0xce, (byte) 0x39, (byte) 0xe7, (byte) 0x9c, - (byte) 0x73, (byte) 0x8c, (byte) 0x31, (byte) 0xe7, - (byte) 0x9c, (byte) 0x73, (byte) 0xce, (byte) 0x39, - (byte) 0xe7, (byte) 0x9c, (byte) 0x73, (byte) 0x6e, - (byte) 0x31, (byte) 0xe7, (byte) 0x16, (byte) 0x73, - (byte) 0xae, (byte) 0x39, (byte) 0xe7, (byte) 0x9c, - (byte) 0x73, (byte) 0xce, (byte) 0x39, (byte) 0xe7, - (byte) 0x1c, (byte) 0x73, (byte) 0xce, (byte) 0x39, - (byte) 0xe7, (byte) 0x9c, (byte) 0x73, (byte) 0x20, - (byte) 0x34, (byte) 0x64, (byte) 0x15, (byte) 0x00, - (byte) 0x90, (byte) 0x00, (byte) 0x00, (byte) 0xa0, - (byte) 0xa1, (byte) 0x28, (byte) 0x8a, (byte) 0xe2, - (byte) 0x28, (byte) 0x0e, (byte) 0x10, (byte) 0x1a, - (byte) 0xb2, (byte) 0x0a, (byte) 0x00, (byte) 0xc8, - (byte) 0x00, (byte) 0x00, (byte) 0x10, (byte) 0x40, - (byte) 0x71, (byte) 0x14, (byte) 0x47, (byte) 0x91, - (byte) 0x14, (byte) 0x4b, (byte) 0xb1, (byte) 0x1c, - (byte) 0xcb, (byte) 0xd1, (byte) 0x24, (byte) 0x0d, - (byte) 0x08, (byte) 0x0d, (byte) 0x59, (byte) 0x05, - (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x00, - (byte) 0x08, (byte) 0x00, (byte) 0x00, (byte) 0xa0, - (byte) 0x48, (byte) 0x86, (byte) 0xa4, (byte) 0x48, - (byte) 0x8a, (byte) 0xa5, (byte) 0x58, (byte) 0x8e, - (byte) 0x66, (byte) 0x69, (byte) 0x9e, (byte) 0x26, - (byte) 0x7a, (byte) 0xa2, (byte) 0x28, (byte) 0x9a, - (byte) 0xa2, (byte) 0x2a, (byte) 0xab, (byte) 0xb2, - (byte) 0x69, (byte) 0xca, (byte) 0xb2, (byte) 0x2c, - (byte) 0xcb, (byte) 0xb2, (byte) 0xeb, (byte) 0xba, - (byte) 0x2e, (byte) 0x10, (byte) 0x1a, (byte) 0xb2, - (byte) 0x0a, (byte) 0x00, (byte) 0x48, (byte) 0x00, - (byte) 0x00, (byte) 0x50, (byte) 0x51, (byte) 0x14, - (byte) 0xc5, (byte) 0x70, (byte) 0x14, (byte) 0x07, - (byte) 0x08, (byte) 0x0d, (byte) 0x59, (byte) 0x05, - (byte) 0x00, (byte) 0x64, (byte) 0x00, (byte) 0x00, - (byte) 0x08, (byte) 0x60, (byte) 0x28, (byte) 0x8a, - (byte) 0xa3, (byte) 0x38, (byte) 0x8e, (byte) 0xe4, - (byte) 0x58, (byte) 0x92, (byte) 0xa5, (byte) 0x59, - (byte) 0x9e, (byte) 0x07, (byte) 0x84, (byte) 0x86, - (byte) 0xac, (byte) 0x02, (byte) 0x00, (byte) 0x80, - (byte) 0x00, (byte) 0x00, (byte) 0x04, (byte) 0x00, - (byte) 0x00, (byte) 0x50, (byte) 0x0c, (byte) 0x47, - (byte) 0xb1, (byte) 0x14, (byte) 0x4d, (byte) 0xf1, - (byte) 0x24, (byte) 0xcf, (byte) 0xf2, (byte) 0x3c, - (byte) 0xcf, (byte) 0xf3, (byte) 0x3c, (byte) 0xcf, - (byte) 0xf3, (byte) 0x3c, (byte) 0xcf, (byte) 0xf3, - (byte) 0x3c, (byte) 0xcf, (byte) 0xf3, (byte) 0x3c, - (byte) 0xcf, (byte) 0xf3, (byte) 0x3c, (byte) 0xcf, - (byte) 0xf3, (byte) 0x3c, (byte) 0x0d, (byte) 0x08, - (byte) 0x0d, (byte) 0x59, (byte) 0x05, (byte) 0x00, - (byte) 0x20, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x82, (byte) 0x28, (byte) 0x64, (byte) 0x18, - (byte) 0x03, (byte) 0x42, (byte) 0x43, (byte) 0x56, - (byte) 0x01, (byte) 0x00, (byte) 0x40, (byte) 0x00, - (byte) 0x00, (byte) 0x08, (byte) 0x21, (byte) 0x1a, - (byte) 0x19, (byte) 0x43, (byte) 0x9d, (byte) 0x52, - (byte) 0x12, (byte) 0x5c, (byte) 0x0a, (byte) 0x16, - (byte) 0x42, (byte) 0x1c, (byte) 0x11, (byte) 0x43, - (byte) 0x1d, (byte) 0x42, (byte) 0xce, (byte) 0x43, - (byte) 0xa9, (byte) 0xa5, (byte) 0x83, (byte) 0xe0, - (byte) 0x29, (byte) 0x85, (byte) 0x25, (byte) 0x63, - (byte) 0xd2, (byte) 0x53, (byte) 0xac, (byte) 0x41, - (byte) 0x08, (byte) 0x21, (byte) 0x7c, (byte) 0xef, - (byte) 0x3d, (byte) 0xf7, (byte) 0xde, (byte) 0x7b, - (byte) 0xef, (byte) 0x81, (byte) 0xd0, (byte) 0x90, - (byte) 0x55, (byte) 0x00, (byte) 0x00, (byte) 0x10, - (byte) 0x00, (byte) 0x00, (byte) 0x61, (byte) 0x14, - (byte) 0x38, (byte) 0x88, (byte) 0x81, (byte) 0xc7, - (byte) 0x24, (byte) 0x08, (byte) 0x21, (byte) 0x84, - (byte) 0x62, (byte) 0x14, (byte) 0x27, (byte) 0x44, - (byte) 0x71, (byte) 0xa6, (byte) 0x20, (byte) 0x08, - (byte) 0x21, (byte) 0x84, (byte) 0xe5, (byte) 0x24, - (byte) 0x58, (byte) 0xca, (byte) 0x79, (byte) 0xe8, - (byte) 0x24, (byte) 0x08, (byte) 0xdd, (byte) 0x83, - (byte) 0x10, (byte) 0x42, (byte) 0xb8, (byte) 0x9c, - (byte) 0x7b, (byte) 0xcb, (byte) 0xb9, (byte) 0xf7, - (byte) 0xde, (byte) 0x7b, (byte) 0x20, (byte) 0x34, - (byte) 0x64, (byte) 0x15, (byte) 0x00, (byte) 0x00, - (byte) 0x08, (byte) 0x00, (byte) 0xc0, (byte) 0x20, - (byte) 0x84, (byte) 0x10, (byte) 0x42, (byte) 0x08, - (byte) 0x21, (byte) 0x84, (byte) 0x10, (byte) 0x42, - (byte) 0x08, (byte) 0x29, (byte) 0xa4, (byte) 0x94, - (byte) 0x52, (byte) 0x48, (byte) 0x29, (byte) 0xa6, - (byte) 0x98, (byte) 0x62, (byte) 0x8a, (byte) 0x29, - (byte) 0xc7, (byte) 0x1c, (byte) 0x73, (byte) 0xcc, - (byte) 0x31, (byte) 0xc7, (byte) 0x20, (byte) 0x83, - (byte) 0x0c, (byte) 0x32, (byte) 0xe8, (byte) 0xa0, - (byte) 0x93, (byte) 0x4e, (byte) 0x3a, (byte) 0xc9, - (byte) 0xa4, (byte) 0x92, (byte) 0x4e, (byte) 0x3a, - (byte) 0xca, (byte) 0x24, (byte) 0xa3, (byte) 0x8e, - (byte) 0x52, (byte) 0x6b, (byte) 0x29, (byte) 0xb5, - (byte) 0x14, (byte) 0x53, (byte) 0x4c, (byte) 0xb1, - (byte) 0xe5, (byte) 0x16, (byte) 0x63, (byte) 0xad, - (byte) 0xb5, (byte) 0xd6, (byte) 0x9c, (byte) 0x73, - (byte) 0xaf, (byte) 0x41, (byte) 0x29, (byte) 0x63, - (byte) 0x8c, (byte) 0x31, (byte) 0xc6, (byte) 0x18, - (byte) 0x63, (byte) 0x8c, (byte) 0x31, (byte) 0xc6, - (byte) 0x18, (byte) 0x63, (byte) 0x8c, (byte) 0x31, - (byte) 0xc6, (byte) 0x18, (byte) 0x23, (byte) 0x08, - (byte) 0x0d, (byte) 0x59, (byte) 0x05, (byte) 0x00, - (byte) 0x80, (byte) 0x00, (byte) 0x00, (byte) 0x10, - (byte) 0x06, (byte) 0x19, (byte) 0x64, (byte) 0x90, - (byte) 0x41, (byte) 0x08, (byte) 0x21, (byte) 0x84, - (byte) 0x14, (byte) 0x52, (byte) 0x48, (byte) 0x29, - (byte) 0xa6, (byte) 0x98, (byte) 0x72, (byte) 0xcc, - (byte) 0x31, (byte) 0xc7, (byte) 0x1c, (byte) 0x03, - (byte) 0x42, (byte) 0x43, (byte) 0x56, (byte) 0x01, - (byte) 0x00, (byte) 0x80, (byte) 0x00, (byte) 0x00, - (byte) 0x02, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x1c, (byte) 0x45, (byte) 0x52, (byte) 0x24, - (byte) 0x47, (byte) 0x72, (byte) 0x24, (byte) 0x47, - (byte) 0x92, (byte) 0x24, (byte) 0xc9, (byte) 0x92, - (byte) 0x2c, (byte) 0x49, (byte) 0x93, (byte) 0x3c, - (byte) 0xcb, (byte) 0xb3, (byte) 0x3c, (byte) 0xcb, - (byte) 0xb3, (byte) 0x3c, (byte) 0x4d, (byte) 0xd4, - (byte) 0x44, (byte) 0x4d, (byte) 0x15, (byte) 0x55, - (byte) 0xd5, (byte) 0x55, (byte) 0x6d, (byte) 0xd7, - (byte) 0xf6, (byte) 0x6d, (byte) 0x5f, (byte) 0xf6, - (byte) 0x6d, (byte) 0xdf, (byte) 0xd5, (byte) 0x65, - (byte) 0xdf, (byte) 0xf6, (byte) 0x65, (byte) 0xdb, - (byte) 0xd5, (byte) 0x65, (byte) 0x5d, (byte) 0x96, - (byte) 0x65, (byte) 0xdd, (byte) 0xb5, (byte) 0x6d, - (byte) 0x5d, (byte) 0xd6, (byte) 0x5d, (byte) 0x5d, - (byte) 0xd7, (byte) 0x75, (byte) 0x5d, (byte) 0xd7, - (byte) 0x75, (byte) 0x5d, (byte) 0xd7, (byte) 0x75, - (byte) 0x5d, (byte) 0xd7, (byte) 0x75, (byte) 0x5d, - (byte) 0xd7, (byte) 0x75, (byte) 0x5d, (byte) 0xd7, - (byte) 0x81, (byte) 0xd0, (byte) 0x90, (byte) 0x55, - (byte) 0x00, (byte) 0x80, (byte) 0x04, (byte) 0x00, - (byte) 0x80, (byte) 0x8e, (byte) 0xe4, (byte) 0x38, - (byte) 0x8e, (byte) 0xe4, (byte) 0x38, (byte) 0x8e, - (byte) 0xe4, (byte) 0x48, (byte) 0x8e, (byte) 0xa4, - (byte) 0x48, (byte) 0x0a, (byte) 0x10, (byte) 0x1a, - (byte) 0xb2, (byte) 0x0a, (byte) 0x00, (byte) 0x90, - (byte) 0x01, (byte) 0x00, (byte) 0x10, (byte) 0x00, - (byte) 0x80, (byte) 0xa3, (byte) 0x38, (byte) 0x8a, - (byte) 0xe3, (byte) 0x48, (byte) 0x8e, (byte) 0xe4, - (byte) 0x58, (byte) 0x8e, (byte) 0x25, (byte) 0x59, - (byte) 0x92, (byte) 0x26, (byte) 0x69, (byte) 0x96, - (byte) 0x67, (byte) 0x79, (byte) 0x96, (byte) 0xa7, - (byte) 0x79, (byte) 0x9a, (byte) 0xa8, (byte) 0x89, - (byte) 0x1e, (byte) 0x10, (byte) 0x1a, (byte) 0xb2, - (byte) 0x0a, (byte) 0x00, (byte) 0x00, (byte) 0x04, - (byte) 0x00, (byte) 0x10, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80, - (byte) 0xa2, (byte) 0x28, (byte) 0x8a, (byte) 0xa3, - (byte) 0x38, (byte) 0x8e, (byte) 0x24, (byte) 0x59, - (byte) 0x96, (byte) 0xa6, (byte) 0x69, (byte) 0x9e, - (byte) 0xa7, (byte) 0x7a, (byte) 0xa2, (byte) 0x28, - (byte) 0x9a, (byte) 0xaa, (byte) 0xaa, (byte) 0x8a, - (byte) 0xa6, (byte) 0xa9, (byte) 0xaa, (byte) 0xaa, - (byte) 0x6a, (byte) 0x9a, (byte) 0xa6, (byte) 0x69, - (byte) 0x9a, (byte) 0xa6, (byte) 0x69, (byte) 0x9a, - (byte) 0xa6, (byte) 0x69, (byte) 0x9a, (byte) 0xa6, - (byte) 0x69, (byte) 0x9a, (byte) 0xa6, (byte) 0x69, - (byte) 0x9a, (byte) 0xa6, (byte) 0x69, (byte) 0x9a, - (byte) 0xa6, (byte) 0x69, (byte) 0x9a, (byte) 0xa6, - (byte) 0x69, (byte) 0x9a, (byte) 0xa6, (byte) 0x69, - (byte) 0x9a, (byte) 0xa6, (byte) 0x69, (byte) 0x9a, - (byte) 0xa6, (byte) 0x69, (byte) 0x02, (byte) 0xa1, - (byte) 0x21, (byte) 0xab, (byte) 0x00, (byte) 0x00, - (byte) 0x09, (byte) 0x00, (byte) 0x00, (byte) 0x1d, - (byte) 0xc7, (byte) 0x71, (byte) 0x1c, (byte) 0x47, - (byte) 0x71, (byte) 0x1c, (byte) 0xc7, (byte) 0x71, - (byte) 0x24, (byte) 0x47, (byte) 0x92, (byte) 0x24, - (byte) 0x20, (byte) 0x34, (byte) 0x64, (byte) 0x15, - (byte) 0x00, (byte) 0x20, (byte) 0x03, (byte) 0x00, - (byte) 0x20, (byte) 0x00, (byte) 0x00, (byte) 0x43, - (byte) 0x51, (byte) 0x1c, (byte) 0x45, (byte) 0x72, - (byte) 0x2c, (byte) 0xc7, (byte) 0x92, (byte) 0x34, - (byte) 0x4b, (byte) 0xb3, (byte) 0x3c, (byte) 0xcb, - (byte) 0xd3, (byte) 0x44, (byte) 0xcf, (byte) 0xf4, - (byte) 0x5c, (byte) 0x51, (byte) 0x36, (byte) 0x75, - (byte) 0x53, (byte) 0x57, (byte) 0x6d, (byte) 0x20, - (byte) 0x34, (byte) 0x64, (byte) 0x15, (byte) 0x00, - (byte) 0x00, (byte) 0x08, (byte) 0x00, (byte) 0x20, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0xc7, (byte) 0x73, - (byte) 0x3c, (byte) 0xc7, (byte) 0x73, (byte) 0x3c, - (byte) 0xc9, (byte) 0x93, (byte) 0x3c, (byte) 0xcb, - (byte) 0x73, (byte) 0x3c, (byte) 0xc7, (byte) 0x93, - (byte) 0x3c, (byte) 0x49, (byte) 0xd3, (byte) 0x34, - (byte) 0x4d, (byte) 0xd3, (byte) 0x34, (byte) 0x4d, - (byte) 0xd3, (byte) 0x34, (byte) 0x4d, (byte) 0xd3, - (byte) 0x34, (byte) 0x4d, (byte) 0xd3, (byte) 0x34, - (byte) 0x4d, (byte) 0xd3, (byte) 0x34, (byte) 0x4d, - (byte) 0xd3, (byte) 0x34, (byte) 0x4d, (byte) 0xd3, - (byte) 0x34, (byte) 0x4d, (byte) 0xd3, (byte) 0x34, - (byte) 0x4d, (byte) 0xd3, (byte) 0x34, (byte) 0x4d, - (byte) 0xd3, (byte) 0x34, (byte) 0x4d, (byte) 0xd3, - (byte) 0x34, (byte) 0x4d, (byte) 0xd3, (byte) 0x34, - (byte) 0x4d, (byte) 0xd3, (byte) 0x34, (byte) 0x4d, - (byte) 0x03, (byte) 0x42, (byte) 0x43, (byte) 0x56, - (byte) 0x02, (byte) 0x00, (byte) 0x64, (byte) 0x00, - (byte) 0x00, (byte) 0x90, (byte) 0x02, (byte) 0xcf, - (byte) 0x42, (byte) 0x29, (byte) 0x2d, (byte) 0x46, - (byte) 0x02, (byte) 0x1c, (byte) 0x88, (byte) 0x98, - (byte) 0xa3, (byte) 0xd8, (byte) 0x7b, (byte) 0xef, - (byte) 0xbd, (byte) 0xf7, (byte) 0xde, (byte) 0x7b, - (byte) 0x65, (byte) 0x3c, (byte) 0x92, (byte) 0x88, - (byte) 0x49, (byte) 0xed, (byte) 0x31, (byte) 0xf4, - (byte) 0xd4, (byte) 0x31, (byte) 0x07, (byte) 0xb1, - (byte) 0x67, (byte) 0xc6, (byte) 0x23, (byte) 0x66, - (byte) 0x94, (byte) 0xa3, (byte) 0xd8, (byte) 0x29, - (byte) 0xcf, (byte) 0x1c, (byte) 0x42, (byte) 0x0c, - (byte) 0x62, (byte) 0xe8, (byte) 0x3c, (byte) 0x74, - (byte) 0x4a, (byte) 0x31, (byte) 0x88, (byte) 0x29, - (byte) 0xf5, (byte) 0x52, (byte) 0x32, (byte) 0xc6, - (byte) 0x20, (byte) 0xc6, (byte) 0xd8, (byte) 0x63, - (byte) 0x0c, (byte) 0x21, (byte) 0x94, (byte) 0x18, - (byte) 0x08, (byte) 0x0d, (byte) 0x59, (byte) 0x21, - (byte) 0x00, (byte) 0x84, (byte) 0x66, (byte) 0x00, - (byte) 0x18, (byte) 0x24, (byte) 0x09, (byte) 0x90, - (byte) 0x34, (byte) 0x0d, (byte) 0x90, (byte) 0x34, - (byte) 0x0d, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x24, (byte) 0x4f, (byte) 0x03, (byte) 0x34, - (byte) 0x51, (byte) 0x04, (byte) 0x34, (byte) 0x4f, - (byte) 0x04, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x49, (byte) 0xf3, (byte) 0x00, (byte) 0x4d, - (byte) 0xf4, (byte) 0x00, (byte) 0x4d, (byte) 0x14, - (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x90, (byte) 0x3c, (byte) 0x0d, (byte) 0xf0, - (byte) 0x44, (byte) 0x11, (byte) 0xd0, (byte) 0x44, - (byte) 0x11, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x34, (byte) 0x51, (byte) 0x04, (byte) 0x44, - (byte) 0x51, (byte) 0x05, (byte) 0x44, (byte) 0xd5, - (byte) 0x04, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x4d, (byte) 0x14, (byte) 0x01, (byte) 0x4f, - (byte) 0x15, (byte) 0x01, (byte) 0xd1, (byte) 0x54, - (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x90, (byte) 0x34, (byte) 0x0f, (byte) 0xd0, - (byte) 0x44, (byte) 0x11, (byte) 0xf0, (byte) 0x44, - (byte) 0x11, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x34, (byte) 0x51, (byte) 0x04, (byte) 0x44, - (byte) 0xd5, (byte) 0x04, (byte) 0x3c, (byte) 0x51, - (byte) 0x05, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x4d, (byte) 0x14, (byte) 0x01, (byte) 0xd1, - (byte) 0x54, (byte) 0x01, (byte) 0x51, (byte) 0x15, - (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x04, - (byte) 0x00, (byte) 0x00, (byte) 0x04, (byte) 0x38, - (byte) 0x00, (byte) 0x00, (byte) 0x04, (byte) 0x58, - (byte) 0x08, (byte) 0x85, (byte) 0x86, (byte) 0xac, - (byte) 0x08, (byte) 0x00, (byte) 0xe2, (byte) 0x04, - (byte) 0x00, (byte) 0x04, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x10, - (byte) 0x00, (byte) 0x00, (byte) 0x30, (byte) 0xe0, - (byte) 0x00, (byte) 0x00, (byte) 0x10, (byte) 0x60, - (byte) 0x42, (byte) 0x19, (byte) 0x28, (byte) 0x34, - (byte) 0x64, (byte) 0x45, (byte) 0x00, (byte) 0x10, - (byte) 0x27, (byte) 0x00, (byte) 0x60, (byte) 0x70, - (byte) 0x1c, (byte) 0xcb, (byte) 0x02, (byte) 0x00, - (byte) 0x00, (byte) 0x47, (byte) 0x92, (byte) 0x34, - (byte) 0x0d, (byte) 0x00, (byte) 0x00, (byte) 0x1c, - (byte) 0x49, (byte) 0xd2, (byte) 0x34, (byte) 0x00, - (byte) 0x00, (byte) 0xd0, (byte) 0x34, (byte) 0x4d, - (byte) 0x14, (byte) 0x01, (byte) 0x00, (byte) 0xc0, - (byte) 0xd2, (byte) 0x34, (byte) 0x51, (byte) 0x04, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x10, (byte) 0x00, (byte) 0x00, (byte) 0x30, - (byte) 0xe0, (byte) 0x00, (byte) 0x00, (byte) 0x10, - (byte) 0x60, (byte) 0x42, (byte) 0x19, (byte) 0x28, - (byte) 0x34, (byte) 0x64, (byte) 0x25, (byte) 0x00, - (byte) 0x10, (byte) 0x05, (byte) 0x00, (byte) 0x60, - (byte) 0x30, (byte) 0x14, (byte) 0x4d, (byte) 0x03, - (byte) 0x58, (byte) 0x16, (byte) 0xc0, (byte) 0xb2, - (byte) 0x00, (byte) 0x9a, (byte) 0x06, (byte) 0xd0, - (byte) 0x34, (byte) 0x80, (byte) 0xe7, (byte) 0x01, - (byte) 0x3c, (byte) 0x11, (byte) 0x60, (byte) 0x9a, - (byte) 0x00, (byte) 0x40, (byte) 0x00, (byte) 0x00, - (byte) 0x40, (byte) 0x81, (byte) 0x03, (byte) 0x00, - (byte) 0x40, (byte) 0x80, (byte) 0x0d, (byte) 0x9a, - (byte) 0x12, (byte) 0x8b, (byte) 0x03, (byte) 0x14, - (byte) 0x1a, (byte) 0xb2, (byte) 0x12, (byte) 0x00, - (byte) 0x88, (byte) 0x02, (byte) 0x00, (byte) 0x30, - (byte) 0x28, (byte) 0x8a, (byte) 0x24, (byte) 0x59, - (byte) 0x96, (byte) 0xe7, (byte) 0x41, (byte) 0xd3, - (byte) 0x34, (byte) 0x4d, (byte) 0x14, (byte) 0xa1, - (byte) 0x69, (byte) 0x9a, (byte) 0x26, (byte) 0x8a, - (byte) 0xf0, (byte) 0x3c, (byte) 0xcf, (byte) 0x13, - (byte) 0x45, (byte) 0x78, (byte) 0x9e, (byte) 0xe7, - (byte) 0x99, (byte) 0x26, (byte) 0x44, (byte) 0xd1, - (byte) 0xf3, (byte) 0x4c, (byte) 0x13, (byte) 0xa2, - (byte) 0xe8, (byte) 0x79, (byte) 0xa6, (byte) 0x09, - (byte) 0xd3, (byte) 0x14, (byte) 0x45, (byte) 0xd3, - (byte) 0x04, (byte) 0xa2, (byte) 0x68, (byte) 0x9a, - (byte) 0x02, (byte) 0x00, (byte) 0x00, (byte) 0x0a, - (byte) 0x1c, (byte) 0x00, (byte) 0x00, (byte) 0x02, - (byte) 0x6c, (byte) 0xd0, (byte) 0x94, (byte) 0x58, - (byte) 0x1c, (byte) 0xa0, (byte) 0xd0, (byte) 0x90, - (byte) 0x95, (byte) 0x00, (byte) 0x40, (byte) 0x48, - (byte) 0x00, (byte) 0x80, (byte) 0x41, (byte) 0x51, - (byte) 0x2c, (byte) 0xcb, (byte) 0xf3, (byte) 0x44, - (byte) 0x51, (byte) 0x14, (byte) 0x4d, (byte) 0x53, - (byte) 0x55, (byte) 0x5d, (byte) 0x17, (byte) 0x9a, - (byte) 0xe6, (byte) 0x79, (byte) 0xa2, (byte) 0x28, - (byte) 0x8a, (byte) 0xa6, (byte) 0xa9, (byte) 0xaa, - (byte) 0xae, (byte) 0x0b, (byte) 0x4d, (byte) 0xf3, - (byte) 0x3c, (byte) 0x51, (byte) 0x14, (byte) 0x45, - (byte) 0xd3, (byte) 0x54, (byte) 0x55, (byte) 0xd7, - (byte) 0x85, (byte) 0xe7, (byte) 0x79, (byte) 0xa2, - (byte) 0x29, (byte) 0x9a, (byte) 0xa6, (byte) 0x69, - (byte) 0xaa, (byte) 0xaa, (byte) 0xeb, (byte) 0xc2, - (byte) 0xf3, (byte) 0x44, (byte) 0xd1, (byte) 0x34, - (byte) 0x4d, (byte) 0x53, (byte) 0x55, (byte) 0x55, - (byte) 0xd7, (byte) 0x75, (byte) 0xe1, (byte) 0x79, - (byte) 0xa2, (byte) 0x68, (byte) 0x9a, (byte) 0xa6, - (byte) 0xa9, (byte) 0xaa, (byte) 0xae, (byte) 0xeb, - (byte) 0xba, (byte) 0xf0, (byte) 0x3c, (byte) 0x51, - (byte) 0x34, (byte) 0x4d, (byte) 0xd3, (byte) 0x54, - (byte) 0x55, (byte) 0xd7, (byte) 0x95, (byte) 0x65, - (byte) 0x88, (byte) 0xa2, (byte) 0x28, (byte) 0x9a, - (byte) 0xa6, (byte) 0x69, (byte) 0xaa, (byte) 0xaa, - (byte) 0xeb, (byte) 0xca, (byte) 0x32, (byte) 0x10, - (byte) 0x45, (byte) 0xd3, (byte) 0x34, (byte) 0x4d, - (byte) 0x55, (byte) 0x75, (byte) 0x5d, (byte) 0x59, - (byte) 0x06, (byte) 0xa2, (byte) 0x68, (byte) 0x9a, - (byte) 0xaa, (byte) 0xea, (byte) 0xba, (byte) 0xae, - (byte) 0x2b, (byte) 0xcb, (byte) 0x40, (byte) 0x14, - (byte) 0x4d, (byte) 0x53, (byte) 0x55, (byte) 0x5d, - (byte) 0xd7, (byte) 0x75, (byte) 0x65, (byte) 0x19, - (byte) 0x98, (byte) 0xa6, (byte) 0x6a, (byte) 0xaa, - (byte) 0xaa, (byte) 0xeb, (byte) 0xca, (byte) 0xb2, - (byte) 0x2c, (byte) 0x03, (byte) 0x4c, (byte) 0x53, - (byte) 0x55, (byte) 0x5d, (byte) 0x57, (byte) 0x96, - (byte) 0x65, (byte) 0x19, (byte) 0xa0, (byte) 0xaa, - (byte) 0xae, (byte) 0xeb, (byte) 0xba, (byte) 0xb2, - (byte) 0x6c, (byte) 0xdb, (byte) 0x00, (byte) 0x55, - (byte) 0x75, (byte) 0x5d, (byte) 0xd7, (byte) 0x95, - (byte) 0x65, (byte) 0xdb, (byte) 0x06, (byte) 0xb8, - (byte) 0xae, (byte) 0xeb, (byte) 0xca, (byte) 0xb2, - (byte) 0x2c, (byte) 0xdb, (byte) 0x36, (byte) 0x00, - (byte) 0xd7, (byte) 0x95, (byte) 0x65, (byte) 0x59, - (byte) 0xb6, (byte) 0x6d, (byte) 0x01, (byte) 0x00, - (byte) 0x00, (byte) 0x07, (byte) 0x0e, (byte) 0x00, - (byte) 0x00, (byte) 0x01, (byte) 0x46, (byte) 0xd0, - (byte) 0x49, (byte) 0x46, (byte) 0x95, (byte) 0x45, - (byte) 0xd8, (byte) 0x68, (byte) 0xc2, (byte) 0x85, - (byte) 0x07, (byte) 0xa0, (byte) 0xd0, (byte) 0x90, - (byte) 0x15, (byte) 0x01, (byte) 0x40, (byte) 0x14, - (byte) 0x00, (byte) 0x00, (byte) 0x60, (byte) 0x8c, - (byte) 0x52, (byte) 0x8a, (byte) 0x29, (byte) 0x65, - (byte) 0x18, (byte) 0x93, (byte) 0x50, (byte) 0x4a, - (byte) 0x09, (byte) 0x0d, (byte) 0x63, (byte) 0x52, - (byte) 0x4a, (byte) 0x2a, (byte) 0xa5, (byte) 0x92, - (byte) 0x92, (byte) 0x52, (byte) 0x4a, (byte) 0xa5, - (byte) 0x54, (byte) 0x12, (byte) 0x52, (byte) 0x4a, - (byte) 0xa9, (byte) 0x94, (byte) 0x4a, (byte) 0x4a, - (byte) 0x4a, (byte) 0x29, (byte) 0x95, (byte) 0x92, - (byte) 0x51, (byte) 0x4a, (byte) 0x29, (byte) 0xb5, - (byte) 0x96, (byte) 0x2a, (byte) 0x29, (byte) 0xa9, - (byte) 0x94, (byte) 0x94, (byte) 0x52, (byte) 0x25, - (byte) 0xa5, (byte) 0xa4, (byte) 0x92, (byte) 0x52, - (byte) 0x2a, (byte) 0x00, (byte) 0x00, (byte) 0xec, - (byte) 0xc0, (byte) 0x01, (byte) 0x00, (byte) 0xec, - (byte) 0xc0, (byte) 0x42, (byte) 0x28, (byte) 0x34, - (byte) 0x64, (byte) 0x25, (byte) 0x00, (byte) 0x90, - (byte) 0x07, (byte) 0x00, (byte) 0x40, (byte) 0x10, - (byte) 0x82, (byte) 0x14, (byte) 0x63, (byte) 0x8c, - (byte) 0x39, (byte) 0x27, (byte) 0xa5, (byte) 0x54, - (byte) 0x8a, (byte) 0x31, (byte) 0xe7, (byte) 0x9c, - (byte) 0x93, (byte) 0x52, (byte) 0x2a, (byte) 0xc5, - (byte) 0x98, (byte) 0x73, (byte) 0xce, (byte) 0x49, - (byte) 0x29, (byte) 0x19, (byte) 0x63, (byte) 0xcc, - (byte) 0x39, (byte) 0xe7, (byte) 0xa4, (byte) 0x94, - (byte) 0x8c, (byte) 0x31, (byte) 0xe6, (byte) 0x9c, - (byte) 0x73, (byte) 0x52, (byte) 0x4a, (byte) 0xc6, - (byte) 0x9c, (byte) 0x73, (byte) 0xce, (byte) 0x39, - (byte) 0x29, (byte) 0x25, (byte) 0x63, (byte) 0xce, - (byte) 0x39, (byte) 0xe7, (byte) 0x9c, (byte) 0x94, - (byte) 0xd2, (byte) 0x39, (byte) 0xe7, (byte) 0x9c, - (byte) 0x83, (byte) 0x50, (byte) 0x4a, (byte) 0x29, - (byte) 0xa5, (byte) 0x73, (byte) 0xce, (byte) 0x41, - (byte) 0x28, (byte) 0xa5, (byte) 0x94, (byte) 0x12, - (byte) 0x42, (byte) 0xe7, (byte) 0x20, (byte) 0x94, - (byte) 0x52, (byte) 0x4a, (byte) 0xe9, (byte) 0x9c, - (byte) 0x73, (byte) 0x10, (byte) 0x0a, (byte) 0x00, - (byte) 0x00, (byte) 0x2a, (byte) 0x70, (byte) 0x00, - (byte) 0x00, (byte) 0x08, (byte) 0xb0, (byte) 0x51, - (byte) 0x64, (byte) 0x73, (byte) 0x82, (byte) 0x91, - (byte) 0xa0, (byte) 0x42, (byte) 0x43, (byte) 0x56, - (byte) 0x02, (byte) 0x00, (byte) 0xa9, (byte) 0x00, - (byte) 0x00, (byte) 0x06, (byte) 0xc7, (byte) 0xb1, - (byte) 0x2c, (byte) 0x4d, (byte) 0xd3, (byte) 0x34, - (byte) 0xcf, (byte) 0x13, (byte) 0x45, (byte) 0x4b, - (byte) 0x92, (byte) 0x34, (byte) 0xcf, (byte) 0x13, - (byte) 0x3d, (byte) 0x4f, (byte) 0x14, (byte) 0x4d, - (byte) 0xd5, (byte) 0x92, (byte) 0x24, (byte) 0xcf, - (byte) 0x13, (byte) 0x45, (byte) 0xcf, (byte) 0x13, - (byte) 0x4d, (byte) 0x53, (byte) 0xe5, (byte) 0x79, - (byte) 0x9e, (byte) 0x28, (byte) 0x8a, (byte) 0xa2, - (byte) 0x68, (byte) 0x9a, (byte) 0xaa, (byte) 0x4a, - (byte) 0x14, (byte) 0x45, (byte) 0x4f, (byte) 0x14, - (byte) 0x45, (byte) 0xd1, (byte) 0x34, (byte) 0x55, - (byte) 0x95, (byte) 0x2c, (byte) 0x8b, (byte) 0xa2, - (byte) 0x69, (byte) 0x9a, (byte) 0xa6, (byte) 0xaa, - (byte) 0xba, (byte) 0x2e, (byte) 0x5b, (byte) 0x16, - (byte) 0x45, (byte) 0xd3, (byte) 0x34, (byte) 0x4d, - (byte) 0x55, (byte) 0x75, (byte) 0x5d, (byte) 0x98, - (byte) 0xa6, (byte) 0x28, (byte) 0xaa, (byte) 0xaa, - (byte) 0xeb, (byte) 0xca, (byte) 0x2e, (byte) 0x4c, - (byte) 0x53, (byte) 0x14, (byte) 0x4d, (byte) 0xd3, - (byte) 0x75, (byte) 0x65, (byte) 0x19, (byte) 0xb2, - (byte) 0xad, (byte) 0x9a, (byte) 0xaa, (byte) 0xea, - (byte) 0xba, (byte) 0xb2, (byte) 0x0d, (byte) 0xdb, - (byte) 0x36, (byte) 0x4d, (byte) 0x55, (byte) 0x75, - (byte) 0x5d, (byte) 0x59, (byte) 0x06, (byte) 0xae, - (byte) 0xeb, (byte) 0xba, (byte) 0xb2, (byte) 0x6c, - (byte) 0xeb, (byte) 0xc0, (byte) 0x75, (byte) 0x5d, - (byte) 0x57, (byte) 0x96, (byte) 0x6d, (byte) 0x5d, - (byte) 0x00, (byte) 0x00, (byte) 0x78, (byte) 0x82, - (byte) 0x03, (byte) 0x00, (byte) 0x50, (byte) 0x81, - (byte) 0x0d, (byte) 0xab, (byte) 0x23, (byte) 0x9c, - (byte) 0x14, (byte) 0x8d, (byte) 0x05, (byte) 0x16, - (byte) 0x1a, (byte) 0xb2, (byte) 0x12, (byte) 0x00, - (byte) 0xc8, (byte) 0x00, (byte) 0x00, (byte) 0x20, - (byte) 0x08, (byte) 0x41, (byte) 0x48, (byte) 0x29, - (byte) 0x85, (byte) 0x90, (byte) 0x52, (byte) 0x0a, - (byte) 0x21, (byte) 0xa5, (byte) 0x14, (byte) 0x42, - (byte) 0x4a, (byte) 0x29, (byte) 0x84, (byte) 0x04, - (byte) 0x00, (byte) 0x00, (byte) 0x0c, (byte) 0x38, - (byte) 0x00, (byte) 0x00, (byte) 0x04, (byte) 0x98, - (byte) 0x50, (byte) 0x06, (byte) 0x0a, (byte) 0x0d, - (byte) 0x59, (byte) 0x09, (byte) 0x00, (byte) 0xa4, - (byte) 0x02, (byte) 0x00, (byte) 0x00, (byte) 0x10, - (byte) 0x42, (byte) 0x08, (byte) 0x21, (byte) 0x84, - (byte) 0x10, (byte) 0x42, (byte) 0x08, (byte) 0x21, - (byte) 0x84, (byte) 0x10, (byte) 0x42, (byte) 0x08, - (byte) 0x21, (byte) 0x84, (byte) 0x10, (byte) 0x42, - (byte) 0x08, (byte) 0x21, (byte) 0x84, (byte) 0x10, - (byte) 0x42, (byte) 0x08, (byte) 0x21, (byte) 0x84, - (byte) 0x10, (byte) 0x42, (byte) 0x08, (byte) 0x21, - (byte) 0x84, (byte) 0x10, (byte) 0x42, (byte) 0x08, - (byte) 0x21, (byte) 0x84, (byte) 0x10, (byte) 0x42, - (byte) 0x08, (byte) 0x21, (byte) 0x84, (byte) 0x10, - (byte) 0x42, (byte) 0x08, (byte) 0x21, (byte) 0x84, - (byte) 0x10, (byte) 0x42, (byte) 0x08, (byte) 0x21, - (byte) 0x84, (byte) 0xce, (byte) 0x39, (byte) 0xe7, - (byte) 0x9c, (byte) 0x73, (byte) 0xce, (byte) 0x39, - (byte) 0xe7, (byte) 0x9c, (byte) 0x73, (byte) 0xce, - (byte) 0x39, (byte) 0xe7, (byte) 0x9c, (byte) 0x73, - (byte) 0xce, (byte) 0x39, (byte) 0xe7, (byte) 0x9c, - (byte) 0x73, (byte) 0xce, (byte) 0x39, (byte) 0xe7, - (byte) 0x9c, (byte) 0x73, (byte) 0xce, (byte) 0x39, - (byte) 0xe7, (byte) 0x9c, (byte) 0x73, (byte) 0xce, - (byte) 0x39, (byte) 0xe7, (byte) 0x9c, (byte) 0x73, - (byte) 0xce, (byte) 0x39, (byte) 0xe7, (byte) 0x9c, - (byte) 0x73, (byte) 0xce, (byte) 0x39, (byte) 0xe7, - (byte) 0x9c, (byte) 0x73, (byte) 0xce, (byte) 0x39, - (byte) 0xe7, (byte) 0x9c, (byte) 0x73, (byte) 0xce, - (byte) 0x39, (byte) 0xe7, (byte) 0x9c, (byte) 0x73, - (byte) 0x02, (byte) 0x00, (byte) 0xb1, (byte) 0x2b, - (byte) 0x1c, (byte) 0x00, (byte) 0x76, (byte) 0x22, - (byte) 0x6c, (byte) 0x58, (byte) 0x1d, (byte) 0xe1, - (byte) 0xa4, (byte) 0x68, (byte) 0x2c, (byte) 0xb0, - (byte) 0xd0, (byte) 0x90, (byte) 0x95, (byte) 0x00, - (byte) 0x40, (byte) 0x38, (byte) 0x00, (byte) 0x00, - (byte) 0x60, (byte) 0x8c, (byte) 0x31, (byte) 0xce, - (byte) 0x59, (byte) 0xac, (byte) 0xb5, (byte) 0xd6, - (byte) 0x5a, (byte) 0x2b, (byte) 0xa5, (byte) 0x94, - (byte) 0x92, (byte) 0x50, (byte) 0x6b, (byte) 0xad, - (byte) 0xb5, (byte) 0xd6, (byte) 0x9a, (byte) 0x29, - (byte) 0xa4, (byte) 0x94, (byte) 0x84, (byte) 0x16, - (byte) 0x63, (byte) 0x8c, (byte) 0x31, (byte) 0xc6, - (byte) 0x98, (byte) 0x31, (byte) 0x08, (byte) 0x29, - (byte) 0xb5, (byte) 0x18, (byte) 0x63, (byte) 0x8c, - (byte) 0x31, (byte) 0xc6, (byte) 0x8c, (byte) 0x39, - (byte) 0x47, (byte) 0x2d, (byte) 0xc6, (byte) 0x18, - (byte) 0x63, (byte) 0x8c, (byte) 0x31, (byte) 0xb6, - (byte) 0x56, (byte) 0x4a, (byte) 0x6c, (byte) 0x31, - (byte) 0xc6, (byte) 0x18, (byte) 0x63, (byte) 0x8c, - (byte) 0xb1, (byte) 0xb5, (byte) 0x52, (byte) 0x62, - (byte) 0x8c, (byte) 0x31, (byte) 0xc6, (byte) 0x18, - (byte) 0x63, (byte) 0x8c, (byte) 0x31, (byte) 0xc6, - (byte) 0x16, (byte) 0x5b, (byte) 0x8c, (byte) 0x31, - (byte) 0xc6, (byte) 0x18, (byte) 0x63, (byte) 0x8c, - (byte) 0x31, (byte) 0xb6, (byte) 0x18, (byte) 0x63, - (byte) 0x8c, (byte) 0x31, (byte) 0xc6, (byte) 0x18, - (byte) 0x63, (byte) 0x8c, (byte) 0x31, (byte) 0xc6, - (byte) 0x18, (byte) 0x63, (byte) 0x8c, (byte) 0x31, - (byte) 0xc6, (byte) 0x18, (byte) 0x63, (byte) 0x8c, - (byte) 0x31, (byte) 0xb6, (byte) 0x18, (byte) 0x63, - (byte) 0x8c, (byte) 0x31, (byte) 0xc6, (byte) 0x18, - (byte) 0x63, (byte) 0x8c, (byte) 0x31, (byte) 0xc6, - (byte) 0x18, (byte) 0x63, (byte) 0x8c, (byte) 0x31, - (byte) 0xc6, (byte) 0x18, (byte) 0x63, (byte) 0x8c, - (byte) 0x31, (byte) 0xc6, (byte) 0x18, (byte) 0x63, - (byte) 0x8c, (byte) 0x31, (byte) 0xc6, (byte) 0x18, - (byte) 0x63, (byte) 0x6c, (byte) 0x31, (byte) 0xc6, - (byte) 0x18, (byte) 0x63, (byte) 0x8c, (byte) 0x31, - (byte) 0xc6, (byte) 0x18, (byte) 0x63, (byte) 0x8c, - (byte) 0x31, (byte) 0xc6, (byte) 0x18, (byte) 0x63, - (byte) 0x2c, (byte) 0x00, (byte) 0xc0, (byte) 0xe4, - (byte) 0xc1, (byte) 0x01, (byte) 0x00, (byte) 0x2a, - (byte) 0xc1, (byte) 0xc6, (byte) 0x19, (byte) 0x56, - (byte) 0x92, (byte) 0xce, (byte) 0x0a, (byte) 0x47, - (byte) 0x83, (byte) 0x0b, (byte) 0x0d, (byte) 0x59, - (byte) 0x09, (byte) 0x00, (byte) 0xe4, (byte) 0x06, - (byte) 0x00, (byte) 0x00, (byte) 0xc6, (byte) 0x28, - (byte) 0xc5, (byte) 0x98, (byte) 0x63, (byte) 0xce, - (byte) 0x41, (byte) 0x08, (byte) 0xa1, (byte) 0x94, - (byte) 0x12, (byte) 0x4a, (byte) 0x49, (byte) 0xad, - (byte) 0x75, (byte) 0xce, (byte) 0x39, (byte) 0x08, - (byte) 0x21, (byte) 0x94, (byte) 0x52, (byte) 0x4a, - (byte) 0x49, (byte) 0xa9, (byte) 0xb4, (byte) 0x94, - (byte) 0x62, (byte) 0xca, (byte) 0x98, (byte) 0x73, - (byte) 0xce, (byte) 0x41, (byte) 0x08, (byte) 0xa5, - (byte) 0x94, (byte) 0x12, (byte) 0x4a, (byte) 0x49, - (byte) 0xa9, (byte) 0xa5, (byte) 0xd4, (byte) 0x39, - (byte) 0xe7, (byte) 0x20, (byte) 0x94, (byte) 0x52, - (byte) 0x4a, (byte) 0x4a, (byte) 0x29, (byte) 0xa5, - (byte) 0x94, (byte) 0x5a, (byte) 0x6a, (byte) 0xad, - (byte) 0x73, (byte) 0x10, (byte) 0x42, (byte) 0x08, - (byte) 0xa5, (byte) 0x94, (byte) 0x52, (byte) 0x4a, - (byte) 0x4a, (byte) 0x29, (byte) 0xa5, (byte) 0xd4, - (byte) 0x52, (byte) 0x08, (byte) 0x21, (byte) 0x94, - (byte) 0x52, (byte) 0x4a, (byte) 0x2a, (byte) 0x29, - (byte) 0xa5, (byte) 0x94, (byte) 0x52, (byte) 0x6b, - (byte) 0xad, (byte) 0xa5, (byte) 0x10, (byte) 0x42, - (byte) 0x28, (byte) 0xa5, (byte) 0x94, (byte) 0x94, - (byte) 0x52, (byte) 0x4a, (byte) 0x29, (byte) 0xa5, - (byte) 0xd4, (byte) 0x5a, (byte) 0x8b, (byte) 0xa1, - (byte) 0x94, (byte) 0x90, (byte) 0x4a, (byte) 0x29, - (byte) 0x25, (byte) 0xa5, (byte) 0x94, (byte) 0x52, - (byte) 0x49, (byte) 0x2d, (byte) 0xb5, (byte) 0x96, - (byte) 0x5a, (byte) 0x2a, (byte) 0xa1, (byte) 0x94, - (byte) 0x54, (byte) 0x52, (byte) 0x4a, (byte) 0x29, - (byte) 0xa5, (byte) 0x94, (byte) 0x52, (byte) 0x6b, - (byte) 0xa9, (byte) 0xb5, (byte) 0x56, (byte) 0x4a, - (byte) 0x49, (byte) 0x25, (byte) 0xa5, (byte) 0x94, - (byte) 0x52, (byte) 0x4a, (byte) 0x29, (byte) 0xa5, - (byte) 0xd4, (byte) 0x62, (byte) 0x6b, (byte) 0x29, - (byte) 0x94, (byte) 0x92, (byte) 0x52, (byte) 0x49, - (byte) 0x29, (byte) 0xb5, (byte) 0x94, (byte) 0x52, - (byte) 0x4a, (byte) 0xad, (byte) 0xc5, (byte) 0xd8, - (byte) 0x62, (byte) 0x29, (byte) 0xad, (byte) 0xa4, - (byte) 0x94, (byte) 0x52, (byte) 0x4a, (byte) 0x29, - (byte) 0xa5, (byte) 0xd6, (byte) 0x52, (byte) 0x6c, - (byte) 0xad, (byte) 0xb5, (byte) 0xd8, (byte) 0x52, - (byte) 0x4a, (byte) 0x29, (byte) 0xa5, (byte) 0x96, - (byte) 0x5a, (byte) 0x4a, (byte) 0x29, (byte) 0xb5, - (byte) 0x16, (byte) 0x5b, (byte) 0x6a, (byte) 0x2d, - (byte) 0xa5, (byte) 0x94, (byte) 0x52, (byte) 0x4b, - (byte) 0x29, (byte) 0xa5, (byte) 0x96, (byte) 0x52, - (byte) 0x4b, (byte) 0x2d, (byte) 0xc6, (byte) 0xd6, - (byte) 0x5a, (byte) 0x4b, (byte) 0x29, (byte) 0xa5, - (byte) 0xd4, (byte) 0x52, (byte) 0x6a, (byte) 0xa9, - (byte) 0xa5, (byte) 0x94, (byte) 0x52, (byte) 0x6c, - (byte) 0xad, (byte) 0xb5, (byte) 0x98, (byte) 0x52, - (byte) 0x6a, (byte) 0x2d, (byte) 0xa5, (byte) 0xd4, - (byte) 0x52, (byte) 0x6b, (byte) 0x2d, (byte) 0xb5, - (byte) 0xd8, (byte) 0x52, (byte) 0x6a, (byte) 0x2d, - (byte) 0xb5, (byte) 0x94, (byte) 0x52, (byte) 0x6b, - (byte) 0xa9, (byte) 0xa5, (byte) 0x94, (byte) 0x5a, - (byte) 0x6b, (byte) 0x2d, (byte) 0xb6, (byte) 0xd8, - (byte) 0x5a, (byte) 0x6b, (byte) 0x29, (byte) 0xb5, - (byte) 0x94, (byte) 0x52, (byte) 0x4a, (byte) 0xa9, - (byte) 0xb5, (byte) 0x16, (byte) 0x5b, (byte) 0x8a, - (byte) 0xb1, (byte) 0xb5, (byte) 0xd4, (byte) 0x4a, - (byte) 0x4a, (byte) 0x29, (byte) 0xb5, (byte) 0xd4, - (byte) 0x5a, (byte) 0x6a, (byte) 0x2d, (byte) 0xb6, - (byte) 0x16, (byte) 0x5b, (byte) 0x6b, (byte) 0xad, - (byte) 0xa5, (byte) 0xd6, (byte) 0x5a, (byte) 0x6a, - (byte) 0x29, (byte) 0xa5, (byte) 0x16, (byte) 0x5b, - (byte) 0x8c, (byte) 0x31, (byte) 0xc6, (byte) 0x16, - (byte) 0x63, (byte) 0x6b, (byte) 0x31, (byte) 0xa5, - (byte) 0x94, (byte) 0x52, (byte) 0x4b, (byte) 0xa9, - (byte) 0xa5, (byte) 0x02, (byte) 0x00, (byte) 0x80, - (byte) 0x0e, (byte) 0x1c, (byte) 0x00, (byte) 0x00, - (byte) 0x02, (byte) 0x8c, (byte) 0xa8, (byte) 0xb4, - (byte) 0x10, (byte) 0x3b, (byte) 0xcd, (byte) 0xb8, - (byte) 0xf2, (byte) 0x08, (byte) 0x1c, (byte) 0x51, - (byte) 0xc8, (byte) 0x30, (byte) 0x01, (byte) 0x15, - (byte) 0x1a, (byte) 0xb2, (byte) 0x12, (byte) 0x00, - (byte) 0x20, (byte) 0x03, (byte) 0x00, (byte) 0x20, - (byte) 0x90, (byte) 0x69, (byte) 0x92, (byte) 0x39, - (byte) 0x49, (byte) 0xa9, (byte) 0x11, (byte) 0x26, - (byte) 0x39, (byte) 0xc5, (byte) 0xa0, (byte) 0x94, - (byte) 0xe6, (byte) 0x9c, (byte) 0x53, (byte) 0x4a, - (byte) 0x29, (byte) 0xa5, (byte) 0x34, (byte) 0x44, - (byte) 0x96, (byte) 0x64, (byte) 0x90, (byte) 0x62, - (byte) 0x50, (byte) 0x1d, (byte) 0x99, (byte) 0x8c, - (byte) 0x39, (byte) 0x49, (byte) 0x39, (byte) 0x43, - (byte) 0xa4, (byte) 0x31, (byte) 0xa4, (byte) 0x20, - (byte) 0xf5, (byte) 0x4c, (byte) 0x91, (byte) 0xc7, - (byte) 0x94, (byte) 0x62, (byte) 0x10, (byte) 0x43, - (byte) 0x48, (byte) 0x2a, (byte) 0x74, (byte) 0x8a, - (byte) 0x39, (byte) 0x6c, (byte) 0x35, (byte) 0xf9, - (byte) 0x58, (byte) 0x42, (byte) 0x07, (byte) 0xb1, - (byte) 0x06, (byte) 0x65, (byte) 0x8c, (byte) 0x70, - (byte) 0x29, (byte) 0xc5, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x08, (byte) 0x02, (byte) 0x00, - (byte) 0x04, (byte) 0x84, (byte) 0x04, (byte) 0x00, - (byte) 0x18, (byte) 0x20, (byte) 0x28, (byte) 0x98, - (byte) 0x01, (byte) 0x00, (byte) 0x06, (byte) 0x07, - (byte) 0x08, (byte) 0x23, (byte) 0x07, (byte) 0x02, - (byte) 0x1d, (byte) 0x01, (byte) 0x04, (byte) 0x0e, - (byte) 0x6d, (byte) 0x00, (byte) 0x80, (byte) 0x81, - (byte) 0x08, (byte) 0x99, (byte) 0x09, (byte) 0x0c, - (byte) 0x0a, (byte) 0xa1, (byte) 0xc1, (byte) 0x41, - (byte) 0x26, (byte) 0x00, (byte) 0x3c, (byte) 0x40, - (byte) 0x44, (byte) 0x48, (byte) 0x05, (byte) 0x00, - (byte) 0x89, (byte) 0x09, (byte) 0x8a, (byte) 0xd2, - (byte) 0x85, (byte) 0x2e, (byte) 0x08, (byte) 0x21, - (byte) 0x82, (byte) 0x74, (byte) 0x11, (byte) 0x64, - (byte) 0xf1, (byte) 0xc0, (byte) 0x85, (byte) 0x13, - (byte) 0x37, (byte) 0x9e, (byte) 0xb8, (byte) 0xe1, - (byte) 0x84, (byte) 0x0e, (byte) 0x6d, (byte) 0x20, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x20, (byte) 0x00, (byte) 0xf0, - (byte) 0x01, (byte) 0x00, (byte) 0x90, (byte) 0x50, - (byte) 0x00, (byte) 0x11, (byte) 0x11, (byte) 0xd1, - (byte) 0xcc, (byte) 0x55, (byte) 0x58, (byte) 0x5c, - (byte) 0x60, (byte) 0x64, (byte) 0x68, (byte) 0x6c, - (byte) 0x70, (byte) 0x74, (byte) 0x78, (byte) 0x7c, - (byte) 0x80, (byte) 0x84, (byte) 0x08, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x10, (byte) 0x00, (byte) 0x7c, (byte) 0x00, - (byte) 0x00, (byte) 0x24, (byte) 0x22, (byte) 0x40, - (byte) 0x44, (byte) 0x44, (byte) 0x34, (byte) 0x73, - (byte) 0x15, (byte) 0x16, (byte) 0x17, (byte) 0x18, - (byte) 0x19, (byte) 0x1a, (byte) 0x1b, (byte) 0x1c, - (byte) 0x1d, (byte) 0x1e, (byte) 0x1f, (byte) 0x20, - (byte) 0x21, (byte) 0x01, (byte) 0x00, (byte) 0x80, - (byte) 0x00, (byte) 0x02, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x20, (byte) 0x80, - (byte) 0x00, (byte) 0x04, (byte) 0x04, (byte) 0x04, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x02, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x04, (byte) 0x04 - }; - -} diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/TestUtil.java b/testutils/src/main/java/com/google/android/exoplayer2/testutil/TestUtil.java index fb50ef131b..ee17068242 100644 --- a/testutils/src/main/java/com/google/android/exoplayer2/testutil/TestUtil.java +++ b/testutils/src/main/java/com/google/android/exoplayer2/testutil/TestUtil.java @@ -18,7 +18,6 @@ package com.google.android.exoplayer2.testutil; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.fail; -import android.app.Instrumentation; import android.content.Context; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.extractor.Extractor; @@ -132,20 +131,10 @@ public class TestUtil { return joined; } - public static byte[] getByteArray(Instrumentation instrumentation, String fileName) - throws IOException { - return getByteArray(instrumentation.getContext(), fileName); - } - public static byte[] getByteArray(Context context, String fileName) throws IOException { return Util.toByteArray(getInputStream(context, fileName)); } - public static InputStream getInputStream(Instrumentation instrumentation, String fileName) - throws IOException { - return getInputStream(instrumentation.getContext(), fileName); - } - public static InputStream getInputStream(Context context, String fileName) throws IOException { return context.getResources().getAssets().open(fileName); } diff --git a/testutils/src/test/AndroidManifest.xml b/testutils/src/test/AndroidManifest.xml new file mode 100644 index 0000000000..9602d01633 --- /dev/null +++ b/testutils/src/test/AndroidManifest.xml @@ -0,0 +1,23 @@ + + + + + + + + diff --git a/testutils/src/test/java/com/google/android/exoplayer2/testutil/FakeAdaptiveDataSetTest.java b/testutils/src/test/java/com/google/android/exoplayer2/testutil/FakeAdaptiveDataSetTest.java new file mode 100644 index 0000000000..7fd84f6287 --- /dev/null +++ b/testutils/src/test/java/com/google/android/exoplayer2/testutil/FakeAdaptiveDataSetTest.java @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2017 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.testutil; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.android.exoplayer2.C; +import com.google.android.exoplayer2.Format; +import com.google.android.exoplayer2.source.TrackGroup; +import com.google.android.exoplayer2.testutil.FakeDataSet.FakeData; +import com.google.android.exoplayer2.testutil.FakeDataSet.FakeData.Segment; +import com.google.android.exoplayer2.util.MimeTypes; +import java.util.List; +import java.util.Random; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; + +/** Unit test for {@link FakeAdaptiveDataSet}. */ +@RunWith(RobolectricTestRunner.class) +public final class FakeAdaptiveDataSetTest { + + private static final Format[] TEST_FORMATS = { + Format.createVideoSampleFormat( + null, + MimeTypes.VIDEO_H264, + null, + 1000000, + Format.NO_VALUE, + 1280, + 720, + Format.NO_VALUE, + null, + null), + Format.createVideoSampleFormat( + null, + MimeTypes.VIDEO_H264, + null, + 300000, + Format.NO_VALUE, + 640, + 360, + Format.NO_VALUE, + null, + null) + }; + private static final TrackGroup TRACK_GROUP = new TrackGroup(TEST_FORMATS); + + @Test + public void testAdaptiveDataSet() { + long chunkDuration = 2 * C.MICROS_PER_SECOND; + FakeAdaptiveDataSet dataSet = + new FakeAdaptiveDataSet( + TRACK_GROUP, 10 * C.MICROS_PER_SECOND, chunkDuration, 0.0, new Random(0)); + assertThat(dataSet.getAllData().size()).isEqualTo(TEST_FORMATS.length); + assertThat(dataSet.getUri(0).equals(dataSet.getUri(1))).isFalse(); + assertThat(dataSet.getChunkCount()).isEqualTo(5); + assertThat(dataSet.getChunkIndexByPosition(4 * C.MICROS_PER_SECOND)).isEqualTo(2); + assertThat(dataSet.getChunkIndexByPosition(9 * C.MICROS_PER_SECOND)).isEqualTo(4); + for (int i = 0; i < dataSet.getChunkCount(); i++) { + assertThat(dataSet.getChunkDuration(i)).isEqualTo(chunkDuration); + } + assertChunkData(dataSet, chunkDuration); + } + + @Test + public void testAdaptiveDataSetTrailingSmallChunk() { + long chunkDuration = 3 * C.MICROS_PER_SECOND; + FakeAdaptiveDataSet dataSet = + new FakeAdaptiveDataSet( + TRACK_GROUP, 10 * C.MICROS_PER_SECOND, chunkDuration, 0.0, new Random(0)); + assertThat(dataSet.getAllData().size()).isEqualTo(TEST_FORMATS.length); + assertThat(dataSet.getUri(0).equals(dataSet.getUri(1))).isFalse(); + assertThat(dataSet.getChunkCount()).isEqualTo(4); + assertThat(dataSet.getChunkIndexByPosition(4 * C.MICROS_PER_SECOND)).isEqualTo(1); + assertThat(dataSet.getChunkIndexByPosition(9 * C.MICROS_PER_SECOND)).isEqualTo(3); + for (int i = 0; i < dataSet.getChunkCount() - 1; i++) { + assertThat(dataSet.getChunkDuration(i)).isEqualTo(chunkDuration); + } + assertThat(dataSet.getChunkDuration(3)).isEqualTo(1 * C.MICROS_PER_SECOND); + assertChunkData(dataSet, chunkDuration); + } + + @Test + public void testAdaptiveDataSetChunkSizeDistribution() { + double expectedStdDev = 4.0; + FakeAdaptiveDataSet dataSet = + new FakeAdaptiveDataSet( + TRACK_GROUP, + 100000 * C.MICROS_PER_SECOND, + 1 * C.MICROS_PER_SECOND, + expectedStdDev, + new Random(0)); + for (int i = 0; i < TEST_FORMATS.length; i++) { + FakeData data = dataSet.getData(dataSet.getUri(i)); + double mean = computeSegmentSizeMean(data.getSegments()); + double stddev = computeSegmentSizeStdDev(data.getSegments(), mean); + double relativePercentStdDev = stddev / mean * 100.0; + assertThat(relativePercentStdDev).isWithin(0.02).of(expectedStdDev); + assertThat(mean * 8 / TEST_FORMATS[i].bitrate).isWithin(0.01).of(1.0); + } + } + + private void assertChunkData(FakeAdaptiveDataSet dataSet, long chunkDuration) { + for (int i = 0; i < dataSet.getChunkCount(); i++) { + assertThat(dataSet.getStartTime(i)).isEqualTo(chunkDuration * i); + } + for (int s = 0; s < TEST_FORMATS.length; s++) { + FakeData data = dataSet.getData(dataSet.getUri(s)); + assertThat(data.getSegments().size()).isEqualTo(dataSet.getChunkCount()); + for (int i = 0; i < data.getSegments().size(); i++) { + long expectedLength = + TEST_FORMATS[s].bitrate * dataSet.getChunkDuration(i) / (8 * C.MICROS_PER_SECOND); + assertThat(data.getSegments().get(i).length).isEqualTo(expectedLength); + } + } + } + + private static double computeSegmentSizeMean(List segments) { + double totalSize = 0.0; + for (Segment segment : segments) { + totalSize += segment.length; + } + return totalSize / segments.size(); + } + + private static double computeSegmentSizeStdDev(List segments, double mean) { + double totalSquaredSize = 0.0; + for (Segment segment : segments) { + totalSquaredSize += (double) segment.length * segment.length; + } + return Math.sqrt(totalSquaredSize / segments.size() - mean * mean); + } +} diff --git a/testutils/src/test/java/com/google/android/exoplayer2/testutil/FakeClockTest.java b/testutils/src/test/java/com/google/android/exoplayer2/testutil/FakeClockTest.java new file mode 100644 index 0000000000..725753ce46 --- /dev/null +++ b/testutils/src/test/java/com/google/android/exoplayer2/testutil/FakeClockTest.java @@ -0,0 +1,201 @@ +/* + * Copyright (C) 2017 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.testutil; + +import static com.google.common.truth.Truth.assertThat; + +import android.os.ConditionVariable; +import android.os.HandlerThread; +import com.google.android.exoplayer2.util.Clock; +import com.google.android.exoplayer2.util.HandlerWrapper; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; + +/** Unit test for {@link FakeClock}. */ +@RunWith(RobolectricTestRunner.class) +@Config(shadows = {RobolectricUtil.CustomLooper.class, RobolectricUtil.CustomMessageQueue.class}) +public final class FakeClockTest { + + private static final long TIMEOUT_MS = 10000; + + @Test + public void testAdvanceTime() { + FakeClock fakeClock = new FakeClock(2000); + assertThat(fakeClock.elapsedRealtime()).isEqualTo(2000); + fakeClock.advanceTime(500); + assertThat(fakeClock.elapsedRealtime()).isEqualTo(2500); + fakeClock.advanceTime(0); + assertThat(fakeClock.elapsedRealtime()).isEqualTo(2500); + } + + @Test + public void testSleep() throws InterruptedException { + FakeClock fakeClock = new FakeClock(0); + SleeperThread sleeperThread = new SleeperThread(fakeClock, 1000); + sleeperThread.start(); + assertThat(sleeperThread.waitUntilAsleep(TIMEOUT_MS)).isTrue(); + assertThat(sleeperThread.isSleeping()).isTrue(); + fakeClock.advanceTime(1000); + sleeperThread.join(TIMEOUT_MS); + assertThat(sleeperThread.isSleeping()).isFalse(); + + sleeperThread = new SleeperThread(fakeClock, 0); + sleeperThread.start(); + sleeperThread.join(); + assertThat(sleeperThread.isSleeping()).isFalse(); + + SleeperThread[] sleeperThreads = new SleeperThread[5]; + sleeperThreads[0] = new SleeperThread(fakeClock, 1000); + sleeperThreads[1] = new SleeperThread(fakeClock, 1000); + sleeperThreads[2] = new SleeperThread(fakeClock, 2000); + sleeperThreads[3] = new SleeperThread(fakeClock, 3000); + sleeperThreads[4] = new SleeperThread(fakeClock, 4000); + for (SleeperThread thread : sleeperThreads) { + thread.start(); + assertThat(thread.waitUntilAsleep(TIMEOUT_MS)).isTrue(); + } + assertSleepingStates(new boolean[] {true, true, true, true, true}, sleeperThreads); + fakeClock.advanceTime(1500); + assertThat(sleeperThreads[0].waitUntilAwake(TIMEOUT_MS)).isTrue(); + assertThat(sleeperThreads[1].waitUntilAwake(TIMEOUT_MS)).isTrue(); + assertSleepingStates(new boolean[] {false, false, true, true, true}, sleeperThreads); + fakeClock.advanceTime(2000); + assertThat(sleeperThreads[2].waitUntilAwake(TIMEOUT_MS)).isTrue(); + assertThat(sleeperThreads[3].waitUntilAwake(TIMEOUT_MS)).isTrue(); + assertSleepingStates(new boolean[] {false, false, false, false, true}, sleeperThreads); + fakeClock.advanceTime(2000); + for (SleeperThread thread : sleeperThreads) { + thread.join(TIMEOUT_MS); + } + assertSleepingStates(new boolean[] {false, false, false, false, false}, sleeperThreads); + } + + @Test + public void testPostDelayed() { + HandlerThread handlerThread = new HandlerThread("FakeClockTest thread"); + handlerThread.start(); + FakeClock fakeClock = new FakeClock(0); + HandlerWrapper handler = + fakeClock.createHandler(handlerThread.getLooper(), /* callback= */ null); + + TestRunnable[] testRunnables = { + new TestRunnable(), + new TestRunnable(), + new TestRunnable(), + new TestRunnable(), + new TestRunnable() + }; + handler.postDelayed(testRunnables[0], 0); + handler.postDelayed(testRunnables[1], 100); + handler.postDelayed(testRunnables[2], 200); + waitForHandler(handler); + assertTestRunnableStates(new boolean[] {true, false, false, false, false}, testRunnables); + + fakeClock.advanceTime(150); + handler.postDelayed(testRunnables[3], 50); + handler.postDelayed(testRunnables[4], 100); + waitForHandler(handler); + assertTestRunnableStates(new boolean[] {true, true, false, false, false}, testRunnables); + + fakeClock.advanceTime(50); + waitForHandler(handler); + assertTestRunnableStates(new boolean[] {true, true, true, true, false}, testRunnables); + + fakeClock.advanceTime(1000); + waitForHandler(handler); + assertTestRunnableStates(new boolean[] {true, true, true, true, true}, testRunnables); + } + + private static void assertSleepingStates(boolean[] states, SleeperThread[] sleeperThreads) { + for (int i = 0; i < sleeperThreads.length; i++) { + assertThat(sleeperThreads[i].isSleeping()).isEqualTo(states[i]); + } + } + + private static void waitForHandler(HandlerWrapper handler) { + final ConditionVariable handlerFinished = new ConditionVariable(); + handler.post( + new Runnable() { + @Override + public void run() { + handlerFinished.open(); + } + }); + handlerFinished.block(); + } + + private static void assertTestRunnableStates(boolean[] states, TestRunnable[] testRunnables) { + for (int i = 0; i < testRunnables.length; i++) { + assertThat(testRunnables[i].hasRun).isEqualTo(states[i]); + } + } + + private static final class SleeperThread extends Thread { + + private final Clock clock; + private final long sleepDurationMs; + private final CountDownLatch fallAsleepCountDownLatch; + private final CountDownLatch wakeUpCountDownLatch; + + private volatile boolean isSleeping; + + public SleeperThread(Clock clock, long sleepDurationMs) { + this.clock = clock; + this.sleepDurationMs = sleepDurationMs; + this.fallAsleepCountDownLatch = new CountDownLatch(1); + this.wakeUpCountDownLatch = new CountDownLatch(1); + } + + public boolean waitUntilAsleep(long timeoutMs) throws InterruptedException { + return fallAsleepCountDownLatch.await(timeoutMs, TimeUnit.MILLISECONDS); + } + + public boolean waitUntilAwake(long timeoutMs) throws InterruptedException { + return wakeUpCountDownLatch.await(timeoutMs, TimeUnit.MILLISECONDS); + } + + public boolean isSleeping() { + return isSleeping; + } + + @Override + public void run() { + // This relies on the FakeClock's methods synchronizing on its own monitor to ensure that + // any interactions with it occur only after sleep() has called wait() or returned. + synchronized (clock) { + isSleeping = true; + fallAsleepCountDownLatch.countDown(); + clock.sleep(sleepDurationMs); + isSleeping = false; + wakeUpCountDownLatch.countDown(); + } + } + } + + private static final class TestRunnable implements Runnable { + + public boolean hasRun; + + @Override + public void run() { + hasRun = true; + } + } +} diff --git a/testutils/src/test/java/com/google/android/exoplayer2/testutil/FakeDataSetTest.java b/testutils/src/test/java/com/google/android/exoplayer2/testutil/FakeDataSetTest.java new file mode 100644 index 0000000000..75c6f886c2 --- /dev/null +++ b/testutils/src/test/java/com/google/android/exoplayer2/testutil/FakeDataSetTest.java @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2017 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.testutil; + +import static com.google.common.truth.Truth.assertThat; + +import android.net.Uri; +import com.google.android.exoplayer2.testutil.FakeDataSet.FakeData.Segment; +import java.io.IOException; +import java.util.List; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; + +/** Unit test for {@link FakeDataSet} */ +@RunWith(RobolectricTestRunner.class) +public final class FakeDataSetTest { + + @Test + public void testMultipleDataSets() { + byte[][] testData = new byte[4][]; + Uri[] uris = new Uri[3]; + for (int i = 0; i < 4; i++) { + testData[i] = TestUtil.buildTestData(10, i); + if (i > 0) { + uris[i - 1] = Uri.parse("test_uri_" + i); + } + } + FakeDataSet fakeDataSet = + new FakeDataSet() + .newDefaultData() + .appendReadData(testData[0]) + .endData() + .setData(uris[0], testData[1]) + .newData(uris[1]) + .appendReadData(testData[2]) + .endData() + .setData(uris[2], testData[3]); + + assertThat(fakeDataSet.getAllData().size()).isEqualTo(4); + assertThat(fakeDataSet.getData("unseen_uri")).isEqualTo(fakeDataSet.getData((Uri) null)); + for (int i = 0; i < 3; i++) { + assertThat(fakeDataSet.getData(uris[i]).uri).isEqualTo(uris[i]); + } + assertThat(fakeDataSet.getData((Uri) null).getData()).isEqualTo(testData[0]); + for (int i = 1; i < 4; i++) { + assertThat(fakeDataSet.getData(uris[i - 1]).getData()).isEqualTo(testData[i]); + } + } + + @Test + public void testSegmentTypes() { + byte[] testData = TestUtil.buildTestData(3); + Runnable runnable = + new Runnable() { + @Override + public void run() { + // Do nothing. + } + }; + IOException exception = new IOException(); + FakeDataSet fakeDataSet = + new FakeDataSet() + .newDefaultData() + .appendReadData(testData) + .appendReadData(testData) + .appendReadData(50) + .appendReadAction(runnable) + .appendReadError(exception) + .endData(); + + List segments = fakeDataSet.getData((Uri) null).getSegments(); + assertThat(segments.size()).isEqualTo(5); + assertSegment(segments.get(0), testData, 3, 0, null, null); + assertSegment(segments.get(1), testData, 3, 3, null, null); + assertSegment(segments.get(2), null, 50, 6, null, null); + assertSegment(segments.get(3), null, 0, 56, runnable, null); + assertSegment(segments.get(4), null, 0, 56, null, exception); + + byte[] allData = new byte[6]; + System.arraycopy(testData, 0, allData, 0, 3); + System.arraycopy(testData, 0, allData, 3, 3); + assertThat(fakeDataSet.getData((Uri) null).getData()).isEqualTo(allData); + } + + private static void assertSegment( + Segment segment, + byte[] data, + int length, + long byteOffset, + Runnable runnable, + IOException exception) { + if (data != null) { + assertThat(segment.data).isEqualTo(data); + assertThat(data).hasLength(length); + } else { + assertThat(segment.data).isNull(); + } + assertThat(segment.length).isEqualTo(length); + assertThat(segment.byteOffset).isEqualTo(byteOffset); + assertThat(segment.action).isEqualTo(runnable); + assertThat(segment.isActionSegment()).isEqualTo(runnable != null); + assertThat(segment.exception).isEqualTo(exception); + assertThat(segment.isErrorSegment()).isEqualTo(exception != null); + } +} diff --git a/testutils/src/test/java/com/google/android/exoplayer2/testutil/FakeDataSourceTest.java b/testutils/src/test/java/com/google/android/exoplayer2/testutil/FakeDataSourceTest.java new file mode 100644 index 0000000000..c88aba4e08 --- /dev/null +++ b/testutils/src/test/java/com/google/android/exoplayer2/testutil/FakeDataSourceTest.java @@ -0,0 +1,251 @@ +/* + * Copyright (C) 2017 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.testutil; + +import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.fail; + +import android.net.Uri; +import com.google.android.exoplayer2.C; +import com.google.android.exoplayer2.upstream.DataSpec; +import java.io.IOException; +import java.util.Arrays; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; + +/** Unit test for {@link FakeDataSource}. */ +@RunWith(RobolectricTestRunner.class) +public final class FakeDataSourceTest { + + private static final String URI_STRING = "test://test.test"; + private static final byte[] BUFFER = new byte[500]; + private static final byte[] TEST_DATA = TestUtil.buildTestData(15); + private static final byte[] TEST_DATA_PART_1 = Arrays.copyOf(TEST_DATA, 10); + private static final byte[] TEST_DATA_PART_2 = Arrays.copyOfRange(TEST_DATA, 10, 15); + + private static Uri uri; + private static FakeDataSet fakeDataSet; + + @Before + public void setUp() { + uri = Uri.parse(URI_STRING); + fakeDataSet = + new FakeDataSet() + .newData(uri.toString()) + .appendReadData(TEST_DATA_PART_1) + .appendReadData(TEST_DATA_PART_2) + .endData(); + } + + @Test + public void testReadFull() throws IOException { + FakeDataSource dataSource = new FakeDataSource(fakeDataSet); + assertThat(dataSource.open(new DataSpec(uri))).isEqualTo(15); + assertThat(dataSource.read(BUFFER, 0, BUFFER.length)).isEqualTo(10); + assertBuffer(TEST_DATA_PART_1); + assertThat(dataSource.read(BUFFER, 10, BUFFER.length)).isEqualTo(5); + assertBuffer(TEST_DATA); + assertThat(dataSource.read(BUFFER, 15, BUFFER.length)).isEqualTo(C.RESULT_END_OF_INPUT); + assertBuffer(TEST_DATA); + assertThat(dataSource.read(BUFFER, 20, BUFFER.length)).isEqualTo(C.RESULT_END_OF_INPUT); + dataSource.close(); + } + + @Test + public void testReadPartialOpenEnded() throws IOException { + FakeDataSource dataSource = new FakeDataSource(fakeDataSet); + assertThat(dataSource.open(new DataSpec(uri, 7, C.LENGTH_UNSET, null))).isEqualTo(8); + assertThat(dataSource.read(BUFFER, 0, BUFFER.length)).isEqualTo(3); + assertBuffer(TEST_DATA_PART_1, 7, 3); + assertThat(dataSource.read(BUFFER, 0, BUFFER.length)).isEqualTo(5); + assertBuffer(TEST_DATA_PART_2); + assertThat(dataSource.read(BUFFER, 15, BUFFER.length)).isEqualTo(C.RESULT_END_OF_INPUT); + dataSource.close(); + } + + @Test + public void testReadPartialBounded() throws IOException { + FakeDataSource dataSource = new FakeDataSource(fakeDataSet); + assertThat(dataSource.open(new DataSpec(uri, 9, 3, null))).isEqualTo(3); + assertThat(dataSource.read(BUFFER, 0, BUFFER.length)).isEqualTo(1); + assertBuffer(TEST_DATA_PART_1, 9, 1); + assertThat(dataSource.read(BUFFER, 0, BUFFER.length)).isEqualTo(2); + assertBuffer(TEST_DATA_PART_2, 0, 2); + assertThat(dataSource.read(BUFFER, 0, BUFFER.length)).isEqualTo(C.RESULT_END_OF_INPUT); + dataSource.close(); + + assertThat(dataSource.open(new DataSpec(uri, 11, 4, null))).isEqualTo(4); + assertThat(dataSource.read(BUFFER, 0, BUFFER.length)).isEqualTo(4); + assertBuffer(TEST_DATA_PART_2, 1, 4); + assertThat(dataSource.read(BUFFER, 0, BUFFER.length)).isEqualTo(C.RESULT_END_OF_INPUT); + dataSource.close(); + } + + @Test + public void testDummyData() throws IOException { + FakeDataSource dataSource = + new FakeDataSource( + new FakeDataSet() + .newData(uri.toString()) + .appendReadData(100) + .appendReadData(TEST_DATA) + .appendReadData(200) + .endData()); + assertThat(dataSource.open(new DataSpec(uri))).isEqualTo(315); + assertThat(dataSource.read(BUFFER, 0, BUFFER.length)).isEqualTo(100); + assertThat(dataSource.read(BUFFER, 0, BUFFER.length)).isEqualTo(15); + assertBuffer(TEST_DATA); + assertThat(dataSource.read(BUFFER, 0, BUFFER.length)).isEqualTo(200); + assertThat(dataSource.read(BUFFER, 0, BUFFER.length)).isEqualTo(C.RESULT_END_OF_INPUT); + dataSource.close(); + } + + @Test + public void testException() throws IOException { + String errorMessage = "error, error, error"; + IOException exception = new IOException(errorMessage); + FakeDataSource dataSource = + new FakeDataSource( + new FakeDataSet() + .newData(uri.toString()) + .appendReadData(TEST_DATA) + .appendReadError(exception) + .appendReadData(TEST_DATA) + .endData()); + assertThat(dataSource.open(new DataSpec(uri))).isEqualTo(30); + assertThat(dataSource.read(BUFFER, 0, BUFFER.length)).isEqualTo(15); + assertBuffer(TEST_DATA); + try { + dataSource.read(BUFFER, 0, BUFFER.length); + fail("IOException expected."); + } catch (IOException e) { + assertThat(e).hasMessageThat().isEqualTo(errorMessage); + } + try { + dataSource.read(BUFFER, 0, BUFFER.length); + fail("IOException expected."); + } catch (IOException e) { + assertThat(e).hasMessageThat().isEqualTo(errorMessage); + } + dataSource.close(); + assertThat(dataSource.open(new DataSpec(uri, 15, 15, null))).isEqualTo(15); + assertThat(dataSource.read(BUFFER, 0, BUFFER.length)).isEqualTo(15); + assertBuffer(TEST_DATA); + assertThat(dataSource.read(BUFFER, 0, BUFFER.length)).isEqualTo(C.RESULT_END_OF_INPUT); + dataSource.close(); + } + + @Test + public void testRunnable() throws IOException { + TestRunnable[] runnables = new TestRunnable[3]; + for (int i = 0; i < 3; i++) { + runnables[i] = new TestRunnable(); + } + FakeDataSource dataSource = + new FakeDataSource( + new FakeDataSet() + .newData(uri.toString()) + .appendReadData(TEST_DATA) + .appendReadAction(runnables[0]) + .appendReadData(TEST_DATA) + .appendReadAction(runnables[1]) + .appendReadAction(runnables[2]) + .appendReadData(TEST_DATA) + .endData()); + assertThat(dataSource.open(new DataSpec(uri))).isEqualTo(45); + assertThat(dataSource.read(BUFFER, 0, BUFFER.length)).isEqualTo(15); + assertBuffer(TEST_DATA); + for (int i = 0; i < 3; i++) { + assertThat(runnables[i].ran).isFalse(); + } + assertThat(dataSource.read(BUFFER, 0, BUFFER.length)).isEqualTo(15); + assertBuffer(TEST_DATA); + assertThat(runnables[0].ran).isTrue(); + assertThat(runnables[1].ran).isFalse(); + assertThat(runnables[2].ran).isFalse(); + assertThat(dataSource.read(BUFFER, 0, BUFFER.length)).isEqualTo(15); + assertBuffer(TEST_DATA); + for (int i = 0; i < 3; i++) { + assertThat(runnables[i].ran).isTrue(); + } + assertThat(dataSource.read(BUFFER, 0, BUFFER.length)).isEqualTo(C.RESULT_END_OF_INPUT); + dataSource.close(); + } + + @Test + public void testOpenSourceFailures() throws IOException { + // Empty data. + FakeDataSource dataSource = + new FakeDataSource(new FakeDataSet().newData(uri.toString()).endData()); + try { + dataSource.open(new DataSpec(uri)); + fail("IOException expected."); + } catch (IOException e) { + // Expected. + } finally { + dataSource.close(); + } + + // Non-existent data + dataSource = new FakeDataSource(new FakeDataSet()); + try { + dataSource.open(new DataSpec(uri)); + fail("IOException expected."); + } catch (IOException e) { + // Expected. + } finally { + dataSource.close(); + } + + // DataSpec out of bounds. + dataSource = + new FakeDataSource( + new FakeDataSet() + .newDefaultData() + .appendReadData(TestUtil.buildTestData(10)) + .endData()); + try { + dataSource.open(new DataSpec(uri, 5, 10, null)); + fail("IOException expected."); + } catch (IOException e) { + // Expected. + } finally { + dataSource.close(); + } + } + + private static void assertBuffer(byte[] expected) { + assertBuffer(expected, 0, expected.length); + } + + private static void assertBuffer(byte[] expected, int expectedStart, int expectedLength) { + for (int i = 0; i < expectedLength; i++) { + assertThat(BUFFER[i]).isEqualTo(expected[i + expectedStart]); + } + } + + private static final class TestRunnable implements Runnable { + + public boolean ran; + + @Override + public void run() { + ran = true; + } + } +} diff --git a/testutils/src/test/resources/robolectric.properties b/testutils/src/test/resources/robolectric.properties new file mode 100644 index 0000000000..2f3210368e --- /dev/null +++ b/testutils/src/test/resources/robolectric.properties @@ -0,0 +1 @@ +manifest=src/test/AndroidManifest.xml diff --git a/testutils_robolectric/build.gradle b/testutils_robolectric/build.gradle new file mode 100644 index 0000000000..f5a427a4b3 --- /dev/null +++ b/testutils_robolectric/build.gradle @@ -0,0 +1,37 @@ +// Copyright (C) 2018 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. +apply from: '../constants.gradle' +apply plugin: 'com.android.library' + +android { + compileSdkVersion project.ext.compileSdkVersion + buildToolsVersion project.ext.buildToolsVersion + + defaultConfig { + minSdkVersion project.ext.minSdkVersion + targetSdkVersion project.ext.targetSdkVersion + } + + lintOptions { + // Truth depends on JUnit, which depends on java.lang.management, which + // is not part of Android. Remove this when JUnit 4.13 or later is used. + // See: https://github.com/junit-team/junit4/pull/1187. + disable 'InvalidPackage' + } +} + +dependencies { + compile project(modulePrefix + 'testutils') + compile 'org.robolectric:robolectric:' + robolectricVersion +} diff --git a/testutils_robolectric/src/main/AndroidManifest.xml b/testutils_robolectric/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..057caad867 --- /dev/null +++ b/testutils_robolectric/src/main/AndroidManifest.xml @@ -0,0 +1,17 @@ + + + + diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/CacheAsserts.java b/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/CacheAsserts.java similarity index 99% rename from testutils/src/main/java/com/google/android/exoplayer2/testutil/CacheAsserts.java rename to testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/CacheAsserts.java index f9ababe389..6d3b15ac7a 100644 --- a/testutils/src/main/java/com/google/android/exoplayer2/testutil/CacheAsserts.java +++ b/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/CacheAsserts.java @@ -29,9 +29,7 @@ import com.google.android.exoplayer2.upstream.cache.CacheUtil; import java.io.IOException; import java.util.ArrayList; -/** - * Assertion methods for {@link Cache}. - */ +/** Assertion methods for {@link Cache}. */ public final class CacheAsserts { /** @@ -135,5 +133,4 @@ public final class CacheAsserts { } private CacheAsserts() {} - } diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeMediaClockRenderer.java b/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/FakeMediaClockRenderer.java similarity index 93% rename from testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeMediaClockRenderer.java rename to testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/FakeMediaClockRenderer.java index 4d118f9288..009afd1ff7 100644 --- a/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeMediaClockRenderer.java +++ b/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/FakeMediaClockRenderer.java @@ -19,9 +19,7 @@ import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Renderer; import com.google.android.exoplayer2.util.MediaClock; -/** - * Fake abstract {@link Renderer} which is also a {@link MediaClock}. - */ +/** Fake abstract {@link Renderer} which is also a {@link MediaClock}. */ public abstract class FakeMediaClockRenderer extends FakeRenderer implements MediaClock { public FakeMediaClockRenderer(Format... expectedFormats) { @@ -32,5 +30,4 @@ public abstract class FakeMediaClockRenderer extends FakeRenderer implements Med public MediaClock getMediaClock() { return this; } - } diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeShuffleOrder.java b/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/FakeShuffleOrder.java similarity index 100% rename from testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeShuffleOrder.java rename to testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/FakeShuffleOrder.java diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeTrackSelection.java b/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/FakeTrackSelection.java similarity index 94% rename from testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeTrackSelection.java rename to testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/FakeTrackSelection.java index f5f1987f31..8ceb5338a6 100644 --- a/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeTrackSelection.java +++ b/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/FakeTrackSelection.java @@ -25,8 +25,8 @@ import com.google.android.exoplayer2.trackselection.TrackSelection; import java.util.List; /** - * A fake {@link TrackSelection} that only returns 1 fixed track, and allows querying the number - * of calls to its methods. + * A fake {@link TrackSelection} that only returns 1 fixed track, and allows querying the number of + * calls to its methods. */ public final class FakeTrackSelection implements TrackSelection { @@ -118,8 +118,8 @@ public final class FakeTrackSelection implements TrackSelection { } @Override - public void updateSelectedTrack(long playbackPositionUs, long bufferedDurationUs, - long availableDurationUs) { + public void updateSelectedTrack( + long playbackPositionUs, long bufferedDurationUs, long availableDurationUs) { assertThat(isEnabled).isTrue(); } @@ -134,5 +134,4 @@ public final class FakeTrackSelection implements TrackSelection { assertThat(isEnabled).isTrue(); return false; } - } diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeTrackSelector.java b/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/FakeTrackSelector.java similarity index 83% rename from testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeTrackSelector.java rename to testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/FakeTrackSelector.java index da9a1a18ad..2daafbbb0b 100644 --- a/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeTrackSelector.java +++ b/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/FakeTrackSelector.java @@ -25,9 +25,7 @@ import com.google.android.exoplayer2.trackselection.TrackSelection; import java.util.ArrayList; import java.util.List; -/** - * A fake {@link MappingTrackSelector} that returns {@link FakeTrackSelection}s. - */ +/** A fake {@link MappingTrackSelector} that returns {@link FakeTrackSelection}s. */ public class FakeTrackSelector extends MappingTrackSelector { private final List selectedTrackSelections = new ArrayList<>(); @@ -38,17 +36,19 @@ public class FakeTrackSelector extends MappingTrackSelector { } /** - * @param mayReuseTrackSelection Whether this {@link FakeTrackSelector} will reuse - * {@link TrackSelection}s during track selection, when it finds previously-selected track - * selection using the same {@link TrackGroup}. + * @param mayReuseTrackSelection Whether this {@link FakeTrackSelector} will reuse {@link + * TrackSelection}s during track selection, when it finds previously-selected track selection + * using the same {@link TrackGroup}. */ public FakeTrackSelector(boolean mayReuseTrackSelection) { this.mayReuseTrackSelection = mayReuseTrackSelection; } @Override - protected TrackSelection[] selectTracks(RendererCapabilities[] rendererCapabilities, - TrackGroupArray[] rendererTrackGroupArrays, int[][][] rendererFormatSupports) + protected TrackSelection[] selectTracks( + RendererCapabilities[] rendererCapabilities, + TrackGroupArray[] rendererTrackGroupArrays, + int[][][] rendererFormatSupports) throws ExoPlaybackException { List resultList = new ArrayList<>(); for (TrackGroupArray trackGroupArray : rendererTrackGroupArrays) { @@ -76,11 +76,8 @@ public class FakeTrackSelector extends MappingTrackSelector { return trackSelectionForRenderer; } - /** - * Returns list of all {@link FakeTrackSelection}s that this track selector has made so far. - */ + /** Returns list of all {@link FakeTrackSelection}s that this track selector has made so far. */ public List getSelectedTrackSelections() { return selectedTrackSelections; } - } diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/MediaSourceTestRunner.java b/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/MediaSourceTestRunner.java similarity index 85% rename from testutils/src/main/java/com/google/android/exoplayer2/testutil/MediaSourceTestRunner.java rename to testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/MediaSourceTestRunner.java index 16389112ca..fbb48c9529 100644 --- a/testutils/src/main/java/com/google/android/exoplayer2/testutil/MediaSourceTestRunner.java +++ b/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/MediaSourceTestRunner.java @@ -37,9 +37,7 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.LinkedBlockingDeque; import java.util.concurrent.TimeUnit; -/** - * A runner for {@link MediaSource} tests. - */ +/** A runner for {@link MediaSource} tests. */ public class MediaSourceTestRunner { public static final int TIMEOUT_MS = 10000; @@ -78,18 +76,19 @@ public class MediaSourceTestRunner { public void runOnPlaybackThread(final Runnable runnable) { final Throwable[] throwable = new Throwable[1]; final ConditionVariable finishedCondition = new ConditionVariable(); - playbackHandler.post(new Runnable() { - @Override - public void run() { - try { - runnable.run(); - } catch (Throwable e) { - throwable[0] = e; - } finally { - finishedCondition.open(); - } - } - }); + playbackHandler.post( + new Runnable() { + @Override + public void run() { + try { + runnable.run(); + } catch (Throwable e) { + throwable[0] = e; + } finally { + finishedCondition.open(); + } + } + }); assertThat(finishedCondition.block(TIMEOUT_MS)).isTrue(); if (throwable[0] != null) { Util.sneakyThrow(throwable[0]); @@ -103,20 +102,21 @@ public class MediaSourceTestRunner { */ public Timeline prepareSource() throws IOException { final IOException[] prepareError = new IOException[1]; - runOnPlaybackThread(new Runnable() { - @Override - public void run() { - mediaSource.prepareSource(player, true, mediaSourceListener); - try { - // TODO: This only catches errors that are set synchronously in prepareSource. To capture - // async errors we'll need to poll maybeThrowSourceInfoRefreshError until the first call - // to onSourceInfoRefreshed. - mediaSource.maybeThrowSourceInfoRefreshError(); - } catch (IOException e) { - prepareError[0] = e; - } - } - }); + runOnPlaybackThread( + new Runnable() { + @Override + public void run() { + mediaSource.prepareSource(player, true, mediaSourceListener); + try { + // TODO: This only catches errors that are set synchronously in prepareSource. To + // capture async errors we'll need to poll maybeThrowSourceInfoRefreshError until the + // first call to onSourceInfoRefreshed. + mediaSource.maybeThrowSourceInfoRefreshError(); + } catch (IOException e) { + prepareError[0] = e; + } + } + }); if (prepareError[0] != null) { throw prepareError[0]; } @@ -132,12 +132,13 @@ public class MediaSourceTestRunner { */ public MediaPeriod createPeriod(final MediaPeriodId periodId) { final MediaPeriod[] holder = new MediaPeriod[1]; - runOnPlaybackThread(new Runnable() { - @Override - public void run() { - holder[0] = mediaSource.createPeriod(periodId, allocator); - } - }); + runOnPlaybackThread( + new Runnable() { + @Override + public void run() { + holder[0] = mediaSource.createPeriod(periodId, allocator); + } + }); assertThat(holder[0]).isNotNull(); return holder[0]; } @@ -183,24 +184,24 @@ public class MediaSourceTestRunner { * @param mediaPeriod The {@link MediaPeriod} to release. */ public void releasePeriod(final MediaPeriod mediaPeriod) { - runOnPlaybackThread(new Runnable() { - @Override - public void run() { - mediaSource.releasePeriod(mediaPeriod); - } - }); + runOnPlaybackThread( + new Runnable() { + @Override + public void run() { + mediaSource.releasePeriod(mediaPeriod); + } + }); } - /** - * Calls {@link MediaSource#releaseSource()} on the playback thread. - */ + /** Calls {@link MediaSource#releaseSource()} on the playback thread. */ public void releaseSource() { - runOnPlaybackThread(new Runnable() { - @Override - public void run() { - mediaSource.releaseSource(); - } - }); + runOnPlaybackThread( + new Runnable() { + @Override + public void run() { + mediaSource.releaseSource(); + } + }); } /** @@ -276,9 +277,7 @@ public class MediaSourceTestRunner { releasePeriod(secondMediaPeriod); } - /** - * Releases the runner. Should be called when the runner is no longer required. - */ + /** Releases the runner. Should be called when the runner is no longer required. */ public void release() { playbackThread.quit(); } @@ -290,7 +289,6 @@ public class MediaSourceTestRunner { Assertions.checkState(Looper.myLooper() == playbackThread.getLooper()); timelines.addLast(timeline); } - } private static class EventHandlingExoPlayer extends StubExoPlayer @@ -326,5 +324,4 @@ public class MediaSourceTestRunner { return true; } } - } diff --git a/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/OggTestData.java b/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/OggTestData.java new file mode 100644 index 0000000000..8dd0cd16b1 --- /dev/null +++ b/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/OggTestData.java @@ -0,0 +1,1070 @@ +/* + * Copyright (C) 2016 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.testutil; + +/** Provides ogg/vorbis test data in bytes for unit tests. */ +public final class OggTestData { + + public static FakeExtractorInput createInput(byte[] data, boolean simulateUnknownLength) { + return new FakeExtractorInput.Builder() + .setData(data) + .setSimulateIOErrors(true) + .setSimulateUnknownLength(simulateUnknownLength) + .setSimulatePartialReads(true) + .build(); + } + + public static byte[] buildOggHeader( + int headerType, long granule, int pageSequenceCounter, int pageSegmentCount) { + return TestUtil.createByteArray( + 0x4F, + 0x67, + 0x67, + 0x53, // Oggs. + 0x00, // Stream revision. + headerType, + (int) (granule) & 0xFF, + (int) (granule >> 8) & 0xFF, + (int) (granule >> 16) & 0xFF, + (int) (granule >> 24) & 0xFF, + (int) (granule >> 32) & 0xFF, + (int) (granule >> 40) & 0xFF, + (int) (granule >> 48) & 0xFF, + (int) (granule >> 56) & 0xFF, + 0x00, // LSB of data serial number. + 0x10, + 0x00, + 0x00, // MSB of data serial number. + (pageSequenceCounter) & 0xFF, + (pageSequenceCounter >> 8) & 0xFF, + (pageSequenceCounter >> 16) & 0xFF, + (pageSequenceCounter >> 24) & 0xFF, + 0x00, // LSB of page checksum. + 0x00, + 0x10, + 0x00, // MSB of page checksum. + pageSegmentCount); + } + + /** + * Returns the initial two pages of bytes which by spec contain the three vorbis header packets: + * identification, comment and setup header. + */ + public static byte[] getVorbisHeaderPages() { + byte[] data = new byte[VORBIS_HEADER_PAGES.length]; + System.arraycopy(VORBIS_HEADER_PAGES, 0, data, 0, VORBIS_HEADER_PAGES.length); + return data; + } + + /** Returns a valid vorbis identification header in bytes. */ + public static byte[] getIdentificationHeaderData() { + int idHeaderStart = 28; + int idHeaderLength = 30; + byte[] idHeaderData = new byte[idHeaderLength]; + System.arraycopy(VORBIS_HEADER_PAGES, idHeaderStart, idHeaderData, 0, idHeaderLength); + return idHeaderData; + } + + /** Returns a valid vorbis comment header with 3 comments including utf8 chars in bytes. */ + public static byte[] getCommentHeaderDataUTF8() { + byte[] commentHeaderData = new byte[COMMENT_HEADER_WITH_UTF8.length]; + System.arraycopy( + COMMENT_HEADER_WITH_UTF8, 0, commentHeaderData, 0, COMMENT_HEADER_WITH_UTF8.length); + return commentHeaderData; + } + + /** Returns a valid vorbis setup header in bytes. */ + public static byte[] getSetupHeaderData() { + int setupHeaderStart = 146; + int setupHeaderLength = VORBIS_HEADER_PAGES.length - setupHeaderStart; + byte[] setupHeaderData = new byte[setupHeaderLength]; + System.arraycopy(VORBIS_HEADER_PAGES, setupHeaderStart, setupHeaderData, 0, setupHeaderLength); + return setupHeaderData; + } + + private static final byte[] COMMENT_HEADER_WITH_UTF8 = { + (byte) 0x03, (byte) 0x76, (byte) 0x6f, (byte) 0x72, // 3, v, o, r, + (byte) 0x62, (byte) 0x69, (byte) 0x73, (byte) 0x2b, // b, i, s, . + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x58, + (byte) 0x69, (byte) 0x70, (byte) 0x68, (byte) 0x2e, + (byte) 0x4f, (byte) 0x72, (byte) 0x67, (byte) 0x20, + (byte) 0x6c, (byte) 0x69, (byte) 0x62, (byte) 0x56, + (byte) 0x6f, (byte) 0x72, (byte) 0x62, (byte) 0x69, + (byte) 0x73, (byte) 0x20, (byte) 0x49, (byte) 0x20, + (byte) 0x32, (byte) 0x30, (byte) 0x31, (byte) 0x32, + (byte) 0x30, (byte) 0x32, (byte) 0x30, (byte) 0x33, + (byte) 0x20, (byte) 0x28, (byte) 0x4f, (byte) 0x6d, + (byte) 0x6e, (byte) 0x69, (byte) 0x70, (byte) 0x72, + (byte) 0x65, (byte) 0x73, (byte) 0x65, (byte) 0x6e, + (byte) 0x74, (byte) 0x29, (byte) 0x03, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x0a, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x41, (byte) 0x4c, + (byte) 0x42, (byte) 0x55, (byte) 0x4d, (byte) 0x3d, + (byte) 0xc3, (byte) 0xa4, (byte) 0xc3, (byte) 0xb6, + (byte) 0x13, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x54, (byte) 0x49, (byte) 0x54, (byte) 0x4c, + (byte) 0x45, (byte) 0x3d, (byte) 0x41, (byte) 0x20, + (byte) 0x73, (byte) 0x61, (byte) 0x6d, (byte) 0x70, + (byte) 0x6c, (byte) 0x65, (byte) 0x20, (byte) 0x73, + (byte) 0x6f, (byte) 0x6e, (byte) 0x67, (byte) 0x0d, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x41, + (byte) 0x52, (byte) 0x54, (byte) 0x49, (byte) 0x53, + (byte) 0x54, (byte) 0x3d, (byte) 0x47, (byte) 0x6f, + (byte) 0x6f, (byte) 0x67, (byte) 0x6c, (byte) 0x65, + (byte) 0x01 + }; + + // two OGG pages with 3 packets (id, comment and setup header) + // length: 3743 bytes + private static final byte[] VORBIS_HEADER_PAGES = { /* capture pattern ogg header 1 */ + (byte) 0x4f, (byte) 0x67, (byte) 0x67, (byte) 0x53, // O,g,g,S : start pos 0 + (byte) 0x00, (byte) 0x02, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x5e, (byte) 0x5f, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x83, (byte) 0x36, + (byte) 0xe3, (byte) 0x49, (byte) 0x01, (byte) 0x1e, /* capture pattern vorbis id header */ + (byte) 0x01, (byte) 0x76, (byte) 0x6f, (byte) 0x72, // 1,v,o,r : start pos 28 + (byte) 0x62, (byte) 0x69, (byte) 0x73, (byte) 0x00, // b,i,s,. + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x02, + (byte) 0x22, (byte) 0x56, (byte) 0x00, (byte) 0x00, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0x6a, (byte) 0x04, (byte) 0x01, (byte) 0x00, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, /* capture pattern ogg header 2 */ + (byte) 0xa9, (byte) 0x01, (byte) 0x4f, (byte) 0x67, // .,.,O,g : start pos 86 + (byte) 0x67, (byte) 0x53, (byte) 0x00, (byte) 0x00, // g,S,.,. + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x5e, (byte) 0x5f, (byte) 0x00, (byte) 0x00, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x69, (byte) 0xf8, (byte) 0xeb, (byte) 0xe1, + (byte) 0x10, (byte) 0x2d, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, /* capture pattern vorbis comment header*/ + (byte) 0x1b, (byte) 0x03, (byte) 0x76, (byte) 0x6f, // .,3,v,o : start pos 101 + (byte) 0x72, (byte) 0x62, (byte) 0x69, (byte) 0x73, // r,b,i,s + (byte) 0x1d, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x58, (byte) 0x69, (byte) 0x70, (byte) 0x68, + (byte) 0x2e, (byte) 0x4f, (byte) 0x72, (byte) 0x67, + (byte) 0x20, (byte) 0x6c, (byte) 0x69, (byte) 0x62, + (byte) 0x56, (byte) 0x6f, (byte) 0x72, (byte) 0x62, + (byte) 0x69, (byte) 0x73, (byte) 0x20, (byte) 0x49, + (byte) 0x20, (byte) 0x32, (byte) 0x30, (byte) 0x30, + (byte) 0x33, (byte) 0x30, (byte) 0x39, (byte) 0x30, + (byte) 0x39, (byte) 0x00, (byte) 0x00, (byte) 0x00, /* capture pattern vorbis setup header */ + (byte) 0x00, (byte) 0x01, (byte) 0x05, (byte) 0x76, // .,.,5,v : start pos 146 + (byte) 0x6f, (byte) 0x72, (byte) 0x62, (byte) 0x69, // o,r,b,i + (byte) 0x73, (byte) 0x22, (byte) 0x42, (byte) 0x43, // s,. + (byte) 0x56, (byte) 0x01, (byte) 0x00, (byte) 0x40, + (byte) 0x00, (byte) 0x00, (byte) 0x18, (byte) 0x42, + (byte) 0x10, (byte) 0x2a, (byte) 0x05, (byte) 0xad, + (byte) 0x63, (byte) 0x8e, (byte) 0x3a, (byte) 0xc8, + (byte) 0x15, (byte) 0x21, (byte) 0x8c, (byte) 0x19, + (byte) 0xa2, (byte) 0xa0, (byte) 0x42, (byte) 0xca, + (byte) 0x29, (byte) 0xc7, (byte) 0x1d, (byte) 0x42, + (byte) 0xd0, (byte) 0x21, (byte) 0xa3, (byte) 0x24, + (byte) 0x43, (byte) 0x88, (byte) 0x3a, (byte) 0xc6, + (byte) 0x35, (byte) 0xc7, (byte) 0x18, (byte) 0x63, + (byte) 0x47, (byte) 0xb9, (byte) 0x64, (byte) 0x8a, + (byte) 0x42, (byte) 0xc9, (byte) 0x81, (byte) 0xd0, + (byte) 0x90, (byte) 0x55, (byte) 0x00, (byte) 0x00, + (byte) 0x40, (byte) 0x00, (byte) 0x00, (byte) 0xa4, + (byte) 0x1c, (byte) 0x57, (byte) 0x50, (byte) 0x72, + (byte) 0x49, (byte) 0x2d, (byte) 0xe7, (byte) 0x9c, + (byte) 0x73, (byte) 0xa3, (byte) 0x18, (byte) 0x57, + (byte) 0xcc, (byte) 0x71, (byte) 0xe8, (byte) 0x20, + (byte) 0xe7, (byte) 0x9c, (byte) 0x73, (byte) 0xe5, + (byte) 0x20, (byte) 0x67, (byte) 0xcc, (byte) 0x71, + (byte) 0x09, (byte) 0x25, (byte) 0xe7, (byte) 0x9c, + (byte) 0x73, (byte) 0x8e, (byte) 0x39, (byte) 0xe7, + (byte) 0x92, (byte) 0x72, (byte) 0x8e, (byte) 0x31, + (byte) 0xe7, (byte) 0x9c, (byte) 0x73, (byte) 0xa3, + (byte) 0x18, (byte) 0x57, (byte) 0x0e, (byte) 0x72, + (byte) 0x29, (byte) 0x2d, (byte) 0xe7, (byte) 0x9c, + (byte) 0x73, (byte) 0x81, (byte) 0x14, (byte) 0x47, + (byte) 0x8a, (byte) 0x71, (byte) 0xa7, (byte) 0x18, + (byte) 0xe7, (byte) 0x9c, (byte) 0x73, (byte) 0xa4, + (byte) 0x1c, (byte) 0x47, (byte) 0x8a, (byte) 0x71, + (byte) 0xa8, (byte) 0x18, (byte) 0xe7, (byte) 0x9c, + (byte) 0x73, (byte) 0x6d, (byte) 0x31, (byte) 0xb7, + (byte) 0x92, (byte) 0x72, (byte) 0xce, (byte) 0x39, + (byte) 0xe7, (byte) 0x9c, (byte) 0x73, (byte) 0xe6, + (byte) 0x20, (byte) 0x87, (byte) 0x52, (byte) 0x72, + (byte) 0xae, (byte) 0x35, (byte) 0xe7, (byte) 0x9c, + (byte) 0x73, (byte) 0xa4, (byte) 0x18, (byte) 0x67, + (byte) 0x0e, (byte) 0x72, (byte) 0x0b, (byte) 0x25, + (byte) 0xe7, (byte) 0x9c, (byte) 0x73, (byte) 0xc6, + (byte) 0x20, (byte) 0x67, (byte) 0xcc, (byte) 0x71, + (byte) 0xeb, (byte) 0x20, (byte) 0xe7, (byte) 0x9c, + (byte) 0x73, (byte) 0x8c, (byte) 0x35, (byte) 0xb7, + (byte) 0xd4, (byte) 0x72, (byte) 0xce, (byte) 0x39, + (byte) 0xe7, (byte) 0x9c, (byte) 0x73, (byte) 0xce, + (byte) 0x39, (byte) 0xe7, (byte) 0x9c, (byte) 0x73, + (byte) 0xce, (byte) 0x39, (byte) 0xe7, (byte) 0x9c, + (byte) 0x73, (byte) 0x8c, (byte) 0x31, (byte) 0xe7, + (byte) 0x9c, (byte) 0x73, (byte) 0xce, (byte) 0x39, + (byte) 0xe7, (byte) 0x9c, (byte) 0x73, (byte) 0x6e, + (byte) 0x31, (byte) 0xe7, (byte) 0x16, (byte) 0x73, + (byte) 0xae, (byte) 0x39, (byte) 0xe7, (byte) 0x9c, + (byte) 0x73, (byte) 0xce, (byte) 0x39, (byte) 0xe7, + (byte) 0x1c, (byte) 0x73, (byte) 0xce, (byte) 0x39, + (byte) 0xe7, (byte) 0x9c, (byte) 0x73, (byte) 0x20, + (byte) 0x34, (byte) 0x64, (byte) 0x15, (byte) 0x00, + (byte) 0x90, (byte) 0x00, (byte) 0x00, (byte) 0xa0, + (byte) 0xa1, (byte) 0x28, (byte) 0x8a, (byte) 0xe2, + (byte) 0x28, (byte) 0x0e, (byte) 0x10, (byte) 0x1a, + (byte) 0xb2, (byte) 0x0a, (byte) 0x00, (byte) 0xc8, + (byte) 0x00, (byte) 0x00, (byte) 0x10, (byte) 0x40, + (byte) 0x71, (byte) 0x14, (byte) 0x47, (byte) 0x91, + (byte) 0x14, (byte) 0x4b, (byte) 0xb1, (byte) 0x1c, + (byte) 0xcb, (byte) 0xd1, (byte) 0x24, (byte) 0x0d, + (byte) 0x08, (byte) 0x0d, (byte) 0x59, (byte) 0x05, + (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x00, + (byte) 0x08, (byte) 0x00, (byte) 0x00, (byte) 0xa0, + (byte) 0x48, (byte) 0x86, (byte) 0xa4, (byte) 0x48, + (byte) 0x8a, (byte) 0xa5, (byte) 0x58, (byte) 0x8e, + (byte) 0x66, (byte) 0x69, (byte) 0x9e, (byte) 0x26, + (byte) 0x7a, (byte) 0xa2, (byte) 0x28, (byte) 0x9a, + (byte) 0xa2, (byte) 0x2a, (byte) 0xab, (byte) 0xb2, + (byte) 0x69, (byte) 0xca, (byte) 0xb2, (byte) 0x2c, + (byte) 0xcb, (byte) 0xb2, (byte) 0xeb, (byte) 0xba, + (byte) 0x2e, (byte) 0x10, (byte) 0x1a, (byte) 0xb2, + (byte) 0x0a, (byte) 0x00, (byte) 0x48, (byte) 0x00, + (byte) 0x00, (byte) 0x50, (byte) 0x51, (byte) 0x14, + (byte) 0xc5, (byte) 0x70, (byte) 0x14, (byte) 0x07, + (byte) 0x08, (byte) 0x0d, (byte) 0x59, (byte) 0x05, + (byte) 0x00, (byte) 0x64, (byte) 0x00, (byte) 0x00, + (byte) 0x08, (byte) 0x60, (byte) 0x28, (byte) 0x8a, + (byte) 0xa3, (byte) 0x38, (byte) 0x8e, (byte) 0xe4, + (byte) 0x58, (byte) 0x92, (byte) 0xa5, (byte) 0x59, + (byte) 0x9e, (byte) 0x07, (byte) 0x84, (byte) 0x86, + (byte) 0xac, (byte) 0x02, (byte) 0x00, (byte) 0x80, + (byte) 0x00, (byte) 0x00, (byte) 0x04, (byte) 0x00, + (byte) 0x00, (byte) 0x50, (byte) 0x0c, (byte) 0x47, + (byte) 0xb1, (byte) 0x14, (byte) 0x4d, (byte) 0xf1, + (byte) 0x24, (byte) 0xcf, (byte) 0xf2, (byte) 0x3c, + (byte) 0xcf, (byte) 0xf3, (byte) 0x3c, (byte) 0xcf, + (byte) 0xf3, (byte) 0x3c, (byte) 0xcf, (byte) 0xf3, + (byte) 0x3c, (byte) 0xcf, (byte) 0xf3, (byte) 0x3c, + (byte) 0xcf, (byte) 0xf3, (byte) 0x3c, (byte) 0xcf, + (byte) 0xf3, (byte) 0x3c, (byte) 0x0d, (byte) 0x08, + (byte) 0x0d, (byte) 0x59, (byte) 0x05, (byte) 0x00, + (byte) 0x20, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x82, (byte) 0x28, (byte) 0x64, (byte) 0x18, + (byte) 0x03, (byte) 0x42, (byte) 0x43, (byte) 0x56, + (byte) 0x01, (byte) 0x00, (byte) 0x40, (byte) 0x00, + (byte) 0x00, (byte) 0x08, (byte) 0x21, (byte) 0x1a, + (byte) 0x19, (byte) 0x43, (byte) 0x9d, (byte) 0x52, + (byte) 0x12, (byte) 0x5c, (byte) 0x0a, (byte) 0x16, + (byte) 0x42, (byte) 0x1c, (byte) 0x11, (byte) 0x43, + (byte) 0x1d, (byte) 0x42, (byte) 0xce, (byte) 0x43, + (byte) 0xa9, (byte) 0xa5, (byte) 0x83, (byte) 0xe0, + (byte) 0x29, (byte) 0x85, (byte) 0x25, (byte) 0x63, + (byte) 0xd2, (byte) 0x53, (byte) 0xac, (byte) 0x41, + (byte) 0x08, (byte) 0x21, (byte) 0x7c, (byte) 0xef, + (byte) 0x3d, (byte) 0xf7, (byte) 0xde, (byte) 0x7b, + (byte) 0xef, (byte) 0x81, (byte) 0xd0, (byte) 0x90, + (byte) 0x55, (byte) 0x00, (byte) 0x00, (byte) 0x10, + (byte) 0x00, (byte) 0x00, (byte) 0x61, (byte) 0x14, + (byte) 0x38, (byte) 0x88, (byte) 0x81, (byte) 0xc7, + (byte) 0x24, (byte) 0x08, (byte) 0x21, (byte) 0x84, + (byte) 0x62, (byte) 0x14, (byte) 0x27, (byte) 0x44, + (byte) 0x71, (byte) 0xa6, (byte) 0x20, (byte) 0x08, + (byte) 0x21, (byte) 0x84, (byte) 0xe5, (byte) 0x24, + (byte) 0x58, (byte) 0xca, (byte) 0x79, (byte) 0xe8, + (byte) 0x24, (byte) 0x08, (byte) 0xdd, (byte) 0x83, + (byte) 0x10, (byte) 0x42, (byte) 0xb8, (byte) 0x9c, + (byte) 0x7b, (byte) 0xcb, (byte) 0xb9, (byte) 0xf7, + (byte) 0xde, (byte) 0x7b, (byte) 0x20, (byte) 0x34, + (byte) 0x64, (byte) 0x15, (byte) 0x00, (byte) 0x00, + (byte) 0x08, (byte) 0x00, (byte) 0xc0, (byte) 0x20, + (byte) 0x84, (byte) 0x10, (byte) 0x42, (byte) 0x08, + (byte) 0x21, (byte) 0x84, (byte) 0x10, (byte) 0x42, + (byte) 0x08, (byte) 0x29, (byte) 0xa4, (byte) 0x94, + (byte) 0x52, (byte) 0x48, (byte) 0x29, (byte) 0xa6, + (byte) 0x98, (byte) 0x62, (byte) 0x8a, (byte) 0x29, + (byte) 0xc7, (byte) 0x1c, (byte) 0x73, (byte) 0xcc, + (byte) 0x31, (byte) 0xc7, (byte) 0x20, (byte) 0x83, + (byte) 0x0c, (byte) 0x32, (byte) 0xe8, (byte) 0xa0, + (byte) 0x93, (byte) 0x4e, (byte) 0x3a, (byte) 0xc9, + (byte) 0xa4, (byte) 0x92, (byte) 0x4e, (byte) 0x3a, + (byte) 0xca, (byte) 0x24, (byte) 0xa3, (byte) 0x8e, + (byte) 0x52, (byte) 0x6b, (byte) 0x29, (byte) 0xb5, + (byte) 0x14, (byte) 0x53, (byte) 0x4c, (byte) 0xb1, + (byte) 0xe5, (byte) 0x16, (byte) 0x63, (byte) 0xad, + (byte) 0xb5, (byte) 0xd6, (byte) 0x9c, (byte) 0x73, + (byte) 0xaf, (byte) 0x41, (byte) 0x29, (byte) 0x63, + (byte) 0x8c, (byte) 0x31, (byte) 0xc6, (byte) 0x18, + (byte) 0x63, (byte) 0x8c, (byte) 0x31, (byte) 0xc6, + (byte) 0x18, (byte) 0x63, (byte) 0x8c, (byte) 0x31, + (byte) 0xc6, (byte) 0x18, (byte) 0x23, (byte) 0x08, + (byte) 0x0d, (byte) 0x59, (byte) 0x05, (byte) 0x00, + (byte) 0x80, (byte) 0x00, (byte) 0x00, (byte) 0x10, + (byte) 0x06, (byte) 0x19, (byte) 0x64, (byte) 0x90, + (byte) 0x41, (byte) 0x08, (byte) 0x21, (byte) 0x84, + (byte) 0x14, (byte) 0x52, (byte) 0x48, (byte) 0x29, + (byte) 0xa6, (byte) 0x98, (byte) 0x72, (byte) 0xcc, + (byte) 0x31, (byte) 0xc7, (byte) 0x1c, (byte) 0x03, + (byte) 0x42, (byte) 0x43, (byte) 0x56, (byte) 0x01, + (byte) 0x00, (byte) 0x80, (byte) 0x00, (byte) 0x00, + (byte) 0x02, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x1c, (byte) 0x45, (byte) 0x52, (byte) 0x24, + (byte) 0x47, (byte) 0x72, (byte) 0x24, (byte) 0x47, + (byte) 0x92, (byte) 0x24, (byte) 0xc9, (byte) 0x92, + (byte) 0x2c, (byte) 0x49, (byte) 0x93, (byte) 0x3c, + (byte) 0xcb, (byte) 0xb3, (byte) 0x3c, (byte) 0xcb, + (byte) 0xb3, (byte) 0x3c, (byte) 0x4d, (byte) 0xd4, + (byte) 0x44, (byte) 0x4d, (byte) 0x15, (byte) 0x55, + (byte) 0xd5, (byte) 0x55, (byte) 0x6d, (byte) 0xd7, + (byte) 0xf6, (byte) 0x6d, (byte) 0x5f, (byte) 0xf6, + (byte) 0x6d, (byte) 0xdf, (byte) 0xd5, (byte) 0x65, + (byte) 0xdf, (byte) 0xf6, (byte) 0x65, (byte) 0xdb, + (byte) 0xd5, (byte) 0x65, (byte) 0x5d, (byte) 0x96, + (byte) 0x65, (byte) 0xdd, (byte) 0xb5, (byte) 0x6d, + (byte) 0x5d, (byte) 0xd6, (byte) 0x5d, (byte) 0x5d, + (byte) 0xd7, (byte) 0x75, (byte) 0x5d, (byte) 0xd7, + (byte) 0x75, (byte) 0x5d, (byte) 0xd7, (byte) 0x75, + (byte) 0x5d, (byte) 0xd7, (byte) 0x75, (byte) 0x5d, + (byte) 0xd7, (byte) 0x75, (byte) 0x5d, (byte) 0xd7, + (byte) 0x81, (byte) 0xd0, (byte) 0x90, (byte) 0x55, + (byte) 0x00, (byte) 0x80, (byte) 0x04, (byte) 0x00, + (byte) 0x80, (byte) 0x8e, (byte) 0xe4, (byte) 0x38, + (byte) 0x8e, (byte) 0xe4, (byte) 0x38, (byte) 0x8e, + (byte) 0xe4, (byte) 0x48, (byte) 0x8e, (byte) 0xa4, + (byte) 0x48, (byte) 0x0a, (byte) 0x10, (byte) 0x1a, + (byte) 0xb2, (byte) 0x0a, (byte) 0x00, (byte) 0x90, + (byte) 0x01, (byte) 0x00, (byte) 0x10, (byte) 0x00, + (byte) 0x80, (byte) 0xa3, (byte) 0x38, (byte) 0x8a, + (byte) 0xe3, (byte) 0x48, (byte) 0x8e, (byte) 0xe4, + (byte) 0x58, (byte) 0x8e, (byte) 0x25, (byte) 0x59, + (byte) 0x92, (byte) 0x26, (byte) 0x69, (byte) 0x96, + (byte) 0x67, (byte) 0x79, (byte) 0x96, (byte) 0xa7, + (byte) 0x79, (byte) 0x9a, (byte) 0xa8, (byte) 0x89, + (byte) 0x1e, (byte) 0x10, (byte) 0x1a, (byte) 0xb2, + (byte) 0x0a, (byte) 0x00, (byte) 0x00, (byte) 0x04, + (byte) 0x00, (byte) 0x10, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80, + (byte) 0xa2, (byte) 0x28, (byte) 0x8a, (byte) 0xa3, + (byte) 0x38, (byte) 0x8e, (byte) 0x24, (byte) 0x59, + (byte) 0x96, (byte) 0xa6, (byte) 0x69, (byte) 0x9e, + (byte) 0xa7, (byte) 0x7a, (byte) 0xa2, (byte) 0x28, + (byte) 0x9a, (byte) 0xaa, (byte) 0xaa, (byte) 0x8a, + (byte) 0xa6, (byte) 0xa9, (byte) 0xaa, (byte) 0xaa, + (byte) 0x6a, (byte) 0x9a, (byte) 0xa6, (byte) 0x69, + (byte) 0x9a, (byte) 0xa6, (byte) 0x69, (byte) 0x9a, + (byte) 0xa6, (byte) 0x69, (byte) 0x9a, (byte) 0xa6, + (byte) 0x69, (byte) 0x9a, (byte) 0xa6, (byte) 0x69, + (byte) 0x9a, (byte) 0xa6, (byte) 0x69, (byte) 0x9a, + (byte) 0xa6, (byte) 0x69, (byte) 0x9a, (byte) 0xa6, + (byte) 0x69, (byte) 0x9a, (byte) 0xa6, (byte) 0x69, + (byte) 0x9a, (byte) 0xa6, (byte) 0x69, (byte) 0x9a, + (byte) 0xa6, (byte) 0x69, (byte) 0x02, (byte) 0xa1, + (byte) 0x21, (byte) 0xab, (byte) 0x00, (byte) 0x00, + (byte) 0x09, (byte) 0x00, (byte) 0x00, (byte) 0x1d, + (byte) 0xc7, (byte) 0x71, (byte) 0x1c, (byte) 0x47, + (byte) 0x71, (byte) 0x1c, (byte) 0xc7, (byte) 0x71, + (byte) 0x24, (byte) 0x47, (byte) 0x92, (byte) 0x24, + (byte) 0x20, (byte) 0x34, (byte) 0x64, (byte) 0x15, + (byte) 0x00, (byte) 0x20, (byte) 0x03, (byte) 0x00, + (byte) 0x20, (byte) 0x00, (byte) 0x00, (byte) 0x43, + (byte) 0x51, (byte) 0x1c, (byte) 0x45, (byte) 0x72, + (byte) 0x2c, (byte) 0xc7, (byte) 0x92, (byte) 0x34, + (byte) 0x4b, (byte) 0xb3, (byte) 0x3c, (byte) 0xcb, + (byte) 0xd3, (byte) 0x44, (byte) 0xcf, (byte) 0xf4, + (byte) 0x5c, (byte) 0x51, (byte) 0x36, (byte) 0x75, + (byte) 0x53, (byte) 0x57, (byte) 0x6d, (byte) 0x20, + (byte) 0x34, (byte) 0x64, (byte) 0x15, (byte) 0x00, + (byte) 0x00, (byte) 0x08, (byte) 0x00, (byte) 0x20, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0xc7, (byte) 0x73, + (byte) 0x3c, (byte) 0xc7, (byte) 0x73, (byte) 0x3c, + (byte) 0xc9, (byte) 0x93, (byte) 0x3c, (byte) 0xcb, + (byte) 0x73, (byte) 0x3c, (byte) 0xc7, (byte) 0x93, + (byte) 0x3c, (byte) 0x49, (byte) 0xd3, (byte) 0x34, + (byte) 0x4d, (byte) 0xd3, (byte) 0x34, (byte) 0x4d, + (byte) 0xd3, (byte) 0x34, (byte) 0x4d, (byte) 0xd3, + (byte) 0x34, (byte) 0x4d, (byte) 0xd3, (byte) 0x34, + (byte) 0x4d, (byte) 0xd3, (byte) 0x34, (byte) 0x4d, + (byte) 0xd3, (byte) 0x34, (byte) 0x4d, (byte) 0xd3, + (byte) 0x34, (byte) 0x4d, (byte) 0xd3, (byte) 0x34, + (byte) 0x4d, (byte) 0xd3, (byte) 0x34, (byte) 0x4d, + (byte) 0xd3, (byte) 0x34, (byte) 0x4d, (byte) 0xd3, + (byte) 0x34, (byte) 0x4d, (byte) 0xd3, (byte) 0x34, + (byte) 0x4d, (byte) 0xd3, (byte) 0x34, (byte) 0x4d, + (byte) 0x03, (byte) 0x42, (byte) 0x43, (byte) 0x56, + (byte) 0x02, (byte) 0x00, (byte) 0x64, (byte) 0x00, + (byte) 0x00, (byte) 0x90, (byte) 0x02, (byte) 0xcf, + (byte) 0x42, (byte) 0x29, (byte) 0x2d, (byte) 0x46, + (byte) 0x02, (byte) 0x1c, (byte) 0x88, (byte) 0x98, + (byte) 0xa3, (byte) 0xd8, (byte) 0x7b, (byte) 0xef, + (byte) 0xbd, (byte) 0xf7, (byte) 0xde, (byte) 0x7b, + (byte) 0x65, (byte) 0x3c, (byte) 0x92, (byte) 0x88, + (byte) 0x49, (byte) 0xed, (byte) 0x31, (byte) 0xf4, + (byte) 0xd4, (byte) 0x31, (byte) 0x07, (byte) 0xb1, + (byte) 0x67, (byte) 0xc6, (byte) 0x23, (byte) 0x66, + (byte) 0x94, (byte) 0xa3, (byte) 0xd8, (byte) 0x29, + (byte) 0xcf, (byte) 0x1c, (byte) 0x42, (byte) 0x0c, + (byte) 0x62, (byte) 0xe8, (byte) 0x3c, (byte) 0x74, + (byte) 0x4a, (byte) 0x31, (byte) 0x88, (byte) 0x29, + (byte) 0xf5, (byte) 0x52, (byte) 0x32, (byte) 0xc6, + (byte) 0x20, (byte) 0xc6, (byte) 0xd8, (byte) 0x63, + (byte) 0x0c, (byte) 0x21, (byte) 0x94, (byte) 0x18, + (byte) 0x08, (byte) 0x0d, (byte) 0x59, (byte) 0x21, + (byte) 0x00, (byte) 0x84, (byte) 0x66, (byte) 0x00, + (byte) 0x18, (byte) 0x24, (byte) 0x09, (byte) 0x90, + (byte) 0x34, (byte) 0x0d, (byte) 0x90, (byte) 0x34, + (byte) 0x0d, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x24, (byte) 0x4f, (byte) 0x03, (byte) 0x34, + (byte) 0x51, (byte) 0x04, (byte) 0x34, (byte) 0x4f, + (byte) 0x04, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x49, (byte) 0xf3, (byte) 0x00, (byte) 0x4d, + (byte) 0xf4, (byte) 0x00, (byte) 0x4d, (byte) 0x14, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x90, (byte) 0x3c, (byte) 0x0d, (byte) 0xf0, + (byte) 0x44, (byte) 0x11, (byte) 0xd0, (byte) 0x44, + (byte) 0x11, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x34, (byte) 0x51, (byte) 0x04, (byte) 0x44, + (byte) 0x51, (byte) 0x05, (byte) 0x44, (byte) 0xd5, + (byte) 0x04, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x4d, (byte) 0x14, (byte) 0x01, (byte) 0x4f, + (byte) 0x15, (byte) 0x01, (byte) 0xd1, (byte) 0x54, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x90, (byte) 0x34, (byte) 0x0f, (byte) 0xd0, + (byte) 0x44, (byte) 0x11, (byte) 0xf0, (byte) 0x44, + (byte) 0x11, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x34, (byte) 0x51, (byte) 0x04, (byte) 0x44, + (byte) 0xd5, (byte) 0x04, (byte) 0x3c, (byte) 0x51, + (byte) 0x05, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x4d, (byte) 0x14, (byte) 0x01, (byte) 0xd1, + (byte) 0x54, (byte) 0x01, (byte) 0x51, (byte) 0x15, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x04, + (byte) 0x00, (byte) 0x00, (byte) 0x04, (byte) 0x38, + (byte) 0x00, (byte) 0x00, (byte) 0x04, (byte) 0x58, + (byte) 0x08, (byte) 0x85, (byte) 0x86, (byte) 0xac, + (byte) 0x08, (byte) 0x00, (byte) 0xe2, (byte) 0x04, + (byte) 0x00, (byte) 0x04, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x10, + (byte) 0x00, (byte) 0x00, (byte) 0x30, (byte) 0xe0, + (byte) 0x00, (byte) 0x00, (byte) 0x10, (byte) 0x60, + (byte) 0x42, (byte) 0x19, (byte) 0x28, (byte) 0x34, + (byte) 0x64, (byte) 0x45, (byte) 0x00, (byte) 0x10, + (byte) 0x27, (byte) 0x00, (byte) 0x60, (byte) 0x70, + (byte) 0x1c, (byte) 0xcb, (byte) 0x02, (byte) 0x00, + (byte) 0x00, (byte) 0x47, (byte) 0x92, (byte) 0x34, + (byte) 0x0d, (byte) 0x00, (byte) 0x00, (byte) 0x1c, + (byte) 0x49, (byte) 0xd2, (byte) 0x34, (byte) 0x00, + (byte) 0x00, (byte) 0xd0, (byte) 0x34, (byte) 0x4d, + (byte) 0x14, (byte) 0x01, (byte) 0x00, (byte) 0xc0, + (byte) 0xd2, (byte) 0x34, (byte) 0x51, (byte) 0x04, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x10, (byte) 0x00, (byte) 0x00, (byte) 0x30, + (byte) 0xe0, (byte) 0x00, (byte) 0x00, (byte) 0x10, + (byte) 0x60, (byte) 0x42, (byte) 0x19, (byte) 0x28, + (byte) 0x34, (byte) 0x64, (byte) 0x25, (byte) 0x00, + (byte) 0x10, (byte) 0x05, (byte) 0x00, (byte) 0x60, + (byte) 0x30, (byte) 0x14, (byte) 0x4d, (byte) 0x03, + (byte) 0x58, (byte) 0x16, (byte) 0xc0, (byte) 0xb2, + (byte) 0x00, (byte) 0x9a, (byte) 0x06, (byte) 0xd0, + (byte) 0x34, (byte) 0x80, (byte) 0xe7, (byte) 0x01, + (byte) 0x3c, (byte) 0x11, (byte) 0x60, (byte) 0x9a, + (byte) 0x00, (byte) 0x40, (byte) 0x00, (byte) 0x00, + (byte) 0x40, (byte) 0x81, (byte) 0x03, (byte) 0x00, + (byte) 0x40, (byte) 0x80, (byte) 0x0d, (byte) 0x9a, + (byte) 0x12, (byte) 0x8b, (byte) 0x03, (byte) 0x14, + (byte) 0x1a, (byte) 0xb2, (byte) 0x12, (byte) 0x00, + (byte) 0x88, (byte) 0x02, (byte) 0x00, (byte) 0x30, + (byte) 0x28, (byte) 0x8a, (byte) 0x24, (byte) 0x59, + (byte) 0x96, (byte) 0xe7, (byte) 0x41, (byte) 0xd3, + (byte) 0x34, (byte) 0x4d, (byte) 0x14, (byte) 0xa1, + (byte) 0x69, (byte) 0x9a, (byte) 0x26, (byte) 0x8a, + (byte) 0xf0, (byte) 0x3c, (byte) 0xcf, (byte) 0x13, + (byte) 0x45, (byte) 0x78, (byte) 0x9e, (byte) 0xe7, + (byte) 0x99, (byte) 0x26, (byte) 0x44, (byte) 0xd1, + (byte) 0xf3, (byte) 0x4c, (byte) 0x13, (byte) 0xa2, + (byte) 0xe8, (byte) 0x79, (byte) 0xa6, (byte) 0x09, + (byte) 0xd3, (byte) 0x14, (byte) 0x45, (byte) 0xd3, + (byte) 0x04, (byte) 0xa2, (byte) 0x68, (byte) 0x9a, + (byte) 0x02, (byte) 0x00, (byte) 0x00, (byte) 0x0a, + (byte) 0x1c, (byte) 0x00, (byte) 0x00, (byte) 0x02, + (byte) 0x6c, (byte) 0xd0, (byte) 0x94, (byte) 0x58, + (byte) 0x1c, (byte) 0xa0, (byte) 0xd0, (byte) 0x90, + (byte) 0x95, (byte) 0x00, (byte) 0x40, (byte) 0x48, + (byte) 0x00, (byte) 0x80, (byte) 0x41, (byte) 0x51, + (byte) 0x2c, (byte) 0xcb, (byte) 0xf3, (byte) 0x44, + (byte) 0x51, (byte) 0x14, (byte) 0x4d, (byte) 0x53, + (byte) 0x55, (byte) 0x5d, (byte) 0x17, (byte) 0x9a, + (byte) 0xe6, (byte) 0x79, (byte) 0xa2, (byte) 0x28, + (byte) 0x8a, (byte) 0xa6, (byte) 0xa9, (byte) 0xaa, + (byte) 0xae, (byte) 0x0b, (byte) 0x4d, (byte) 0xf3, + (byte) 0x3c, (byte) 0x51, (byte) 0x14, (byte) 0x45, + (byte) 0xd3, (byte) 0x54, (byte) 0x55, (byte) 0xd7, + (byte) 0x85, (byte) 0xe7, (byte) 0x79, (byte) 0xa2, + (byte) 0x29, (byte) 0x9a, (byte) 0xa6, (byte) 0x69, + (byte) 0xaa, (byte) 0xaa, (byte) 0xeb, (byte) 0xc2, + (byte) 0xf3, (byte) 0x44, (byte) 0xd1, (byte) 0x34, + (byte) 0x4d, (byte) 0x53, (byte) 0x55, (byte) 0x55, + (byte) 0xd7, (byte) 0x75, (byte) 0xe1, (byte) 0x79, + (byte) 0xa2, (byte) 0x68, (byte) 0x9a, (byte) 0xa6, + (byte) 0xa9, (byte) 0xaa, (byte) 0xae, (byte) 0xeb, + (byte) 0xba, (byte) 0xf0, (byte) 0x3c, (byte) 0x51, + (byte) 0x34, (byte) 0x4d, (byte) 0xd3, (byte) 0x54, + (byte) 0x55, (byte) 0xd7, (byte) 0x95, (byte) 0x65, + (byte) 0x88, (byte) 0xa2, (byte) 0x28, (byte) 0x9a, + (byte) 0xa6, (byte) 0x69, (byte) 0xaa, (byte) 0xaa, + (byte) 0xeb, (byte) 0xca, (byte) 0x32, (byte) 0x10, + (byte) 0x45, (byte) 0xd3, (byte) 0x34, (byte) 0x4d, + (byte) 0x55, (byte) 0x75, (byte) 0x5d, (byte) 0x59, + (byte) 0x06, (byte) 0xa2, (byte) 0x68, (byte) 0x9a, + (byte) 0xaa, (byte) 0xea, (byte) 0xba, (byte) 0xae, + (byte) 0x2b, (byte) 0xcb, (byte) 0x40, (byte) 0x14, + (byte) 0x4d, (byte) 0x53, (byte) 0x55, (byte) 0x5d, + (byte) 0xd7, (byte) 0x75, (byte) 0x65, (byte) 0x19, + (byte) 0x98, (byte) 0xa6, (byte) 0x6a, (byte) 0xaa, + (byte) 0xaa, (byte) 0xeb, (byte) 0xca, (byte) 0xb2, + (byte) 0x2c, (byte) 0x03, (byte) 0x4c, (byte) 0x53, + (byte) 0x55, (byte) 0x5d, (byte) 0x57, (byte) 0x96, + (byte) 0x65, (byte) 0x19, (byte) 0xa0, (byte) 0xaa, + (byte) 0xae, (byte) 0xeb, (byte) 0xba, (byte) 0xb2, + (byte) 0x6c, (byte) 0xdb, (byte) 0x00, (byte) 0x55, + (byte) 0x75, (byte) 0x5d, (byte) 0xd7, (byte) 0x95, + (byte) 0x65, (byte) 0xdb, (byte) 0x06, (byte) 0xb8, + (byte) 0xae, (byte) 0xeb, (byte) 0xca, (byte) 0xb2, + (byte) 0x2c, (byte) 0xdb, (byte) 0x36, (byte) 0x00, + (byte) 0xd7, (byte) 0x95, (byte) 0x65, (byte) 0x59, + (byte) 0xb6, (byte) 0x6d, (byte) 0x01, (byte) 0x00, + (byte) 0x00, (byte) 0x07, (byte) 0x0e, (byte) 0x00, + (byte) 0x00, (byte) 0x01, (byte) 0x46, (byte) 0xd0, + (byte) 0x49, (byte) 0x46, (byte) 0x95, (byte) 0x45, + (byte) 0xd8, (byte) 0x68, (byte) 0xc2, (byte) 0x85, + (byte) 0x07, (byte) 0xa0, (byte) 0xd0, (byte) 0x90, + (byte) 0x15, (byte) 0x01, (byte) 0x40, (byte) 0x14, + (byte) 0x00, (byte) 0x00, (byte) 0x60, (byte) 0x8c, + (byte) 0x52, (byte) 0x8a, (byte) 0x29, (byte) 0x65, + (byte) 0x18, (byte) 0x93, (byte) 0x50, (byte) 0x4a, + (byte) 0x09, (byte) 0x0d, (byte) 0x63, (byte) 0x52, + (byte) 0x4a, (byte) 0x2a, (byte) 0xa5, (byte) 0x92, + (byte) 0x92, (byte) 0x52, (byte) 0x4a, (byte) 0xa5, + (byte) 0x54, (byte) 0x12, (byte) 0x52, (byte) 0x4a, + (byte) 0xa9, (byte) 0x94, (byte) 0x4a, (byte) 0x4a, + (byte) 0x4a, (byte) 0x29, (byte) 0x95, (byte) 0x92, + (byte) 0x51, (byte) 0x4a, (byte) 0x29, (byte) 0xb5, + (byte) 0x96, (byte) 0x2a, (byte) 0x29, (byte) 0xa9, + (byte) 0x94, (byte) 0x94, (byte) 0x52, (byte) 0x25, + (byte) 0xa5, (byte) 0xa4, (byte) 0x92, (byte) 0x52, + (byte) 0x2a, (byte) 0x00, (byte) 0x00, (byte) 0xec, + (byte) 0xc0, (byte) 0x01, (byte) 0x00, (byte) 0xec, + (byte) 0xc0, (byte) 0x42, (byte) 0x28, (byte) 0x34, + (byte) 0x64, (byte) 0x25, (byte) 0x00, (byte) 0x90, + (byte) 0x07, (byte) 0x00, (byte) 0x40, (byte) 0x10, + (byte) 0x82, (byte) 0x14, (byte) 0x63, (byte) 0x8c, + (byte) 0x39, (byte) 0x27, (byte) 0xa5, (byte) 0x54, + (byte) 0x8a, (byte) 0x31, (byte) 0xe7, (byte) 0x9c, + (byte) 0x93, (byte) 0x52, (byte) 0x2a, (byte) 0xc5, + (byte) 0x98, (byte) 0x73, (byte) 0xce, (byte) 0x49, + (byte) 0x29, (byte) 0x19, (byte) 0x63, (byte) 0xcc, + (byte) 0x39, (byte) 0xe7, (byte) 0xa4, (byte) 0x94, + (byte) 0x8c, (byte) 0x31, (byte) 0xe6, (byte) 0x9c, + (byte) 0x73, (byte) 0x52, (byte) 0x4a, (byte) 0xc6, + (byte) 0x9c, (byte) 0x73, (byte) 0xce, (byte) 0x39, + (byte) 0x29, (byte) 0x25, (byte) 0x63, (byte) 0xce, + (byte) 0x39, (byte) 0xe7, (byte) 0x9c, (byte) 0x94, + (byte) 0xd2, (byte) 0x39, (byte) 0xe7, (byte) 0x9c, + (byte) 0x83, (byte) 0x50, (byte) 0x4a, (byte) 0x29, + (byte) 0xa5, (byte) 0x73, (byte) 0xce, (byte) 0x41, + (byte) 0x28, (byte) 0xa5, (byte) 0x94, (byte) 0x12, + (byte) 0x42, (byte) 0xe7, (byte) 0x20, (byte) 0x94, + (byte) 0x52, (byte) 0x4a, (byte) 0xe9, (byte) 0x9c, + (byte) 0x73, (byte) 0x10, (byte) 0x0a, (byte) 0x00, + (byte) 0x00, (byte) 0x2a, (byte) 0x70, (byte) 0x00, + (byte) 0x00, (byte) 0x08, (byte) 0xb0, (byte) 0x51, + (byte) 0x64, (byte) 0x73, (byte) 0x82, (byte) 0x91, + (byte) 0xa0, (byte) 0x42, (byte) 0x43, (byte) 0x56, + (byte) 0x02, (byte) 0x00, (byte) 0xa9, (byte) 0x00, + (byte) 0x00, (byte) 0x06, (byte) 0xc7, (byte) 0xb1, + (byte) 0x2c, (byte) 0x4d, (byte) 0xd3, (byte) 0x34, + (byte) 0xcf, (byte) 0x13, (byte) 0x45, (byte) 0x4b, + (byte) 0x92, (byte) 0x34, (byte) 0xcf, (byte) 0x13, + (byte) 0x3d, (byte) 0x4f, (byte) 0x14, (byte) 0x4d, + (byte) 0xd5, (byte) 0x92, (byte) 0x24, (byte) 0xcf, + (byte) 0x13, (byte) 0x45, (byte) 0xcf, (byte) 0x13, + (byte) 0x4d, (byte) 0x53, (byte) 0xe5, (byte) 0x79, + (byte) 0x9e, (byte) 0x28, (byte) 0x8a, (byte) 0xa2, + (byte) 0x68, (byte) 0x9a, (byte) 0xaa, (byte) 0x4a, + (byte) 0x14, (byte) 0x45, (byte) 0x4f, (byte) 0x14, + (byte) 0x45, (byte) 0xd1, (byte) 0x34, (byte) 0x55, + (byte) 0x95, (byte) 0x2c, (byte) 0x8b, (byte) 0xa2, + (byte) 0x69, (byte) 0x9a, (byte) 0xa6, (byte) 0xaa, + (byte) 0xba, (byte) 0x2e, (byte) 0x5b, (byte) 0x16, + (byte) 0x45, (byte) 0xd3, (byte) 0x34, (byte) 0x4d, + (byte) 0x55, (byte) 0x75, (byte) 0x5d, (byte) 0x98, + (byte) 0xa6, (byte) 0x28, (byte) 0xaa, (byte) 0xaa, + (byte) 0xeb, (byte) 0xca, (byte) 0x2e, (byte) 0x4c, + (byte) 0x53, (byte) 0x14, (byte) 0x4d, (byte) 0xd3, + (byte) 0x75, (byte) 0x65, (byte) 0x19, (byte) 0xb2, + (byte) 0xad, (byte) 0x9a, (byte) 0xaa, (byte) 0xea, + (byte) 0xba, (byte) 0xb2, (byte) 0x0d, (byte) 0xdb, + (byte) 0x36, (byte) 0x4d, (byte) 0x55, (byte) 0x75, + (byte) 0x5d, (byte) 0x59, (byte) 0x06, (byte) 0xae, + (byte) 0xeb, (byte) 0xba, (byte) 0xb2, (byte) 0x6c, + (byte) 0xeb, (byte) 0xc0, (byte) 0x75, (byte) 0x5d, + (byte) 0x57, (byte) 0x96, (byte) 0x6d, (byte) 0x5d, + (byte) 0x00, (byte) 0x00, (byte) 0x78, (byte) 0x82, + (byte) 0x03, (byte) 0x00, (byte) 0x50, (byte) 0x81, + (byte) 0x0d, (byte) 0xab, (byte) 0x23, (byte) 0x9c, + (byte) 0x14, (byte) 0x8d, (byte) 0x05, (byte) 0x16, + (byte) 0x1a, (byte) 0xb2, (byte) 0x12, (byte) 0x00, + (byte) 0xc8, (byte) 0x00, (byte) 0x00, (byte) 0x20, + (byte) 0x08, (byte) 0x41, (byte) 0x48, (byte) 0x29, + (byte) 0x85, (byte) 0x90, (byte) 0x52, (byte) 0x0a, + (byte) 0x21, (byte) 0xa5, (byte) 0x14, (byte) 0x42, + (byte) 0x4a, (byte) 0x29, (byte) 0x84, (byte) 0x04, + (byte) 0x00, (byte) 0x00, (byte) 0x0c, (byte) 0x38, + (byte) 0x00, (byte) 0x00, (byte) 0x04, (byte) 0x98, + (byte) 0x50, (byte) 0x06, (byte) 0x0a, (byte) 0x0d, + (byte) 0x59, (byte) 0x09, (byte) 0x00, (byte) 0xa4, + (byte) 0x02, (byte) 0x00, (byte) 0x00, (byte) 0x10, + (byte) 0x42, (byte) 0x08, (byte) 0x21, (byte) 0x84, + (byte) 0x10, (byte) 0x42, (byte) 0x08, (byte) 0x21, + (byte) 0x84, (byte) 0x10, (byte) 0x42, (byte) 0x08, + (byte) 0x21, (byte) 0x84, (byte) 0x10, (byte) 0x42, + (byte) 0x08, (byte) 0x21, (byte) 0x84, (byte) 0x10, + (byte) 0x42, (byte) 0x08, (byte) 0x21, (byte) 0x84, + (byte) 0x10, (byte) 0x42, (byte) 0x08, (byte) 0x21, + (byte) 0x84, (byte) 0x10, (byte) 0x42, (byte) 0x08, + (byte) 0x21, (byte) 0x84, (byte) 0x10, (byte) 0x42, + (byte) 0x08, (byte) 0x21, (byte) 0x84, (byte) 0x10, + (byte) 0x42, (byte) 0x08, (byte) 0x21, (byte) 0x84, + (byte) 0x10, (byte) 0x42, (byte) 0x08, (byte) 0x21, + (byte) 0x84, (byte) 0xce, (byte) 0x39, (byte) 0xe7, + (byte) 0x9c, (byte) 0x73, (byte) 0xce, (byte) 0x39, + (byte) 0xe7, (byte) 0x9c, (byte) 0x73, (byte) 0xce, + (byte) 0x39, (byte) 0xe7, (byte) 0x9c, (byte) 0x73, + (byte) 0xce, (byte) 0x39, (byte) 0xe7, (byte) 0x9c, + (byte) 0x73, (byte) 0xce, (byte) 0x39, (byte) 0xe7, + (byte) 0x9c, (byte) 0x73, (byte) 0xce, (byte) 0x39, + (byte) 0xe7, (byte) 0x9c, (byte) 0x73, (byte) 0xce, + (byte) 0x39, (byte) 0xe7, (byte) 0x9c, (byte) 0x73, + (byte) 0xce, (byte) 0x39, (byte) 0xe7, (byte) 0x9c, + (byte) 0x73, (byte) 0xce, (byte) 0x39, (byte) 0xe7, + (byte) 0x9c, (byte) 0x73, (byte) 0xce, (byte) 0x39, + (byte) 0xe7, (byte) 0x9c, (byte) 0x73, (byte) 0xce, + (byte) 0x39, (byte) 0xe7, (byte) 0x9c, (byte) 0x73, + (byte) 0x02, (byte) 0x00, (byte) 0xb1, (byte) 0x2b, + (byte) 0x1c, (byte) 0x00, (byte) 0x76, (byte) 0x22, + (byte) 0x6c, (byte) 0x58, (byte) 0x1d, (byte) 0xe1, + (byte) 0xa4, (byte) 0x68, (byte) 0x2c, (byte) 0xb0, + (byte) 0xd0, (byte) 0x90, (byte) 0x95, (byte) 0x00, + (byte) 0x40, (byte) 0x38, (byte) 0x00, (byte) 0x00, + (byte) 0x60, (byte) 0x8c, (byte) 0x31, (byte) 0xce, + (byte) 0x59, (byte) 0xac, (byte) 0xb5, (byte) 0xd6, + (byte) 0x5a, (byte) 0x2b, (byte) 0xa5, (byte) 0x94, + (byte) 0x92, (byte) 0x50, (byte) 0x6b, (byte) 0xad, + (byte) 0xb5, (byte) 0xd6, (byte) 0x9a, (byte) 0x29, + (byte) 0xa4, (byte) 0x94, (byte) 0x84, (byte) 0x16, + (byte) 0x63, (byte) 0x8c, (byte) 0x31, (byte) 0xc6, + (byte) 0x98, (byte) 0x31, (byte) 0x08, (byte) 0x29, + (byte) 0xb5, (byte) 0x18, (byte) 0x63, (byte) 0x8c, + (byte) 0x31, (byte) 0xc6, (byte) 0x8c, (byte) 0x39, + (byte) 0x47, (byte) 0x2d, (byte) 0xc6, (byte) 0x18, + (byte) 0x63, (byte) 0x8c, (byte) 0x31, (byte) 0xb6, + (byte) 0x56, (byte) 0x4a, (byte) 0x6c, (byte) 0x31, + (byte) 0xc6, (byte) 0x18, (byte) 0x63, (byte) 0x8c, + (byte) 0xb1, (byte) 0xb5, (byte) 0x52, (byte) 0x62, + (byte) 0x8c, (byte) 0x31, (byte) 0xc6, (byte) 0x18, + (byte) 0x63, (byte) 0x8c, (byte) 0x31, (byte) 0xc6, + (byte) 0x16, (byte) 0x5b, (byte) 0x8c, (byte) 0x31, + (byte) 0xc6, (byte) 0x18, (byte) 0x63, (byte) 0x8c, + (byte) 0x31, (byte) 0xb6, (byte) 0x18, (byte) 0x63, + (byte) 0x8c, (byte) 0x31, (byte) 0xc6, (byte) 0x18, + (byte) 0x63, (byte) 0x8c, (byte) 0x31, (byte) 0xc6, + (byte) 0x18, (byte) 0x63, (byte) 0x8c, (byte) 0x31, + (byte) 0xc6, (byte) 0x18, (byte) 0x63, (byte) 0x8c, + (byte) 0x31, (byte) 0xb6, (byte) 0x18, (byte) 0x63, + (byte) 0x8c, (byte) 0x31, (byte) 0xc6, (byte) 0x18, + (byte) 0x63, (byte) 0x8c, (byte) 0x31, (byte) 0xc6, + (byte) 0x18, (byte) 0x63, (byte) 0x8c, (byte) 0x31, + (byte) 0xc6, (byte) 0x18, (byte) 0x63, (byte) 0x8c, + (byte) 0x31, (byte) 0xc6, (byte) 0x18, (byte) 0x63, + (byte) 0x8c, (byte) 0x31, (byte) 0xc6, (byte) 0x18, + (byte) 0x63, (byte) 0x6c, (byte) 0x31, (byte) 0xc6, + (byte) 0x18, (byte) 0x63, (byte) 0x8c, (byte) 0x31, + (byte) 0xc6, (byte) 0x18, (byte) 0x63, (byte) 0x8c, + (byte) 0x31, (byte) 0xc6, (byte) 0x18, (byte) 0x63, + (byte) 0x2c, (byte) 0x00, (byte) 0xc0, (byte) 0xe4, + (byte) 0xc1, (byte) 0x01, (byte) 0x00, (byte) 0x2a, + (byte) 0xc1, (byte) 0xc6, (byte) 0x19, (byte) 0x56, + (byte) 0x92, (byte) 0xce, (byte) 0x0a, (byte) 0x47, + (byte) 0x83, (byte) 0x0b, (byte) 0x0d, (byte) 0x59, + (byte) 0x09, (byte) 0x00, (byte) 0xe4, (byte) 0x06, + (byte) 0x00, (byte) 0x00, (byte) 0xc6, (byte) 0x28, + (byte) 0xc5, (byte) 0x98, (byte) 0x63, (byte) 0xce, + (byte) 0x41, (byte) 0x08, (byte) 0xa1, (byte) 0x94, + (byte) 0x12, (byte) 0x4a, (byte) 0x49, (byte) 0xad, + (byte) 0x75, (byte) 0xce, (byte) 0x39, (byte) 0x08, + (byte) 0x21, (byte) 0x94, (byte) 0x52, (byte) 0x4a, + (byte) 0x49, (byte) 0xa9, (byte) 0xb4, (byte) 0x94, + (byte) 0x62, (byte) 0xca, (byte) 0x98, (byte) 0x73, + (byte) 0xce, (byte) 0x41, (byte) 0x08, (byte) 0xa5, + (byte) 0x94, (byte) 0x12, (byte) 0x4a, (byte) 0x49, + (byte) 0xa9, (byte) 0xa5, (byte) 0xd4, (byte) 0x39, + (byte) 0xe7, (byte) 0x20, (byte) 0x94, (byte) 0x52, + (byte) 0x4a, (byte) 0x4a, (byte) 0x29, (byte) 0xa5, + (byte) 0x94, (byte) 0x5a, (byte) 0x6a, (byte) 0xad, + (byte) 0x73, (byte) 0x10, (byte) 0x42, (byte) 0x08, + (byte) 0xa5, (byte) 0x94, (byte) 0x52, (byte) 0x4a, + (byte) 0x4a, (byte) 0x29, (byte) 0xa5, (byte) 0xd4, + (byte) 0x52, (byte) 0x08, (byte) 0x21, (byte) 0x94, + (byte) 0x52, (byte) 0x4a, (byte) 0x2a, (byte) 0x29, + (byte) 0xa5, (byte) 0x94, (byte) 0x52, (byte) 0x6b, + (byte) 0xad, (byte) 0xa5, (byte) 0x10, (byte) 0x42, + (byte) 0x28, (byte) 0xa5, (byte) 0x94, (byte) 0x94, + (byte) 0x52, (byte) 0x4a, (byte) 0x29, (byte) 0xa5, + (byte) 0xd4, (byte) 0x5a, (byte) 0x8b, (byte) 0xa1, + (byte) 0x94, (byte) 0x90, (byte) 0x4a, (byte) 0x29, + (byte) 0x25, (byte) 0xa5, (byte) 0x94, (byte) 0x52, + (byte) 0x49, (byte) 0x2d, (byte) 0xb5, (byte) 0x96, + (byte) 0x5a, (byte) 0x2a, (byte) 0xa1, (byte) 0x94, + (byte) 0x54, (byte) 0x52, (byte) 0x4a, (byte) 0x29, + (byte) 0xa5, (byte) 0x94, (byte) 0x52, (byte) 0x6b, + (byte) 0xa9, (byte) 0xb5, (byte) 0x56, (byte) 0x4a, + (byte) 0x49, (byte) 0x25, (byte) 0xa5, (byte) 0x94, + (byte) 0x52, (byte) 0x4a, (byte) 0x29, (byte) 0xa5, + (byte) 0xd4, (byte) 0x62, (byte) 0x6b, (byte) 0x29, + (byte) 0x94, (byte) 0x92, (byte) 0x52, (byte) 0x49, + (byte) 0x29, (byte) 0xb5, (byte) 0x94, (byte) 0x52, + (byte) 0x4a, (byte) 0xad, (byte) 0xc5, (byte) 0xd8, + (byte) 0x62, (byte) 0x29, (byte) 0xad, (byte) 0xa4, + (byte) 0x94, (byte) 0x52, (byte) 0x4a, (byte) 0x29, + (byte) 0xa5, (byte) 0xd6, (byte) 0x52, (byte) 0x6c, + (byte) 0xad, (byte) 0xb5, (byte) 0xd8, (byte) 0x52, + (byte) 0x4a, (byte) 0x29, (byte) 0xa5, (byte) 0x96, + (byte) 0x5a, (byte) 0x4a, (byte) 0x29, (byte) 0xb5, + (byte) 0x16, (byte) 0x5b, (byte) 0x6a, (byte) 0x2d, + (byte) 0xa5, (byte) 0x94, (byte) 0x52, (byte) 0x4b, + (byte) 0x29, (byte) 0xa5, (byte) 0x96, (byte) 0x52, + (byte) 0x4b, (byte) 0x2d, (byte) 0xc6, (byte) 0xd6, + (byte) 0x5a, (byte) 0x4b, (byte) 0x29, (byte) 0xa5, + (byte) 0xd4, (byte) 0x52, (byte) 0x6a, (byte) 0xa9, + (byte) 0xa5, (byte) 0x94, (byte) 0x52, (byte) 0x6c, + (byte) 0xad, (byte) 0xb5, (byte) 0x98, (byte) 0x52, + (byte) 0x6a, (byte) 0x2d, (byte) 0xa5, (byte) 0xd4, + (byte) 0x52, (byte) 0x6b, (byte) 0x2d, (byte) 0xb5, + (byte) 0xd8, (byte) 0x52, (byte) 0x6a, (byte) 0x2d, + (byte) 0xb5, (byte) 0x94, (byte) 0x52, (byte) 0x6b, + (byte) 0xa9, (byte) 0xa5, (byte) 0x94, (byte) 0x5a, + (byte) 0x6b, (byte) 0x2d, (byte) 0xb6, (byte) 0xd8, + (byte) 0x5a, (byte) 0x6b, (byte) 0x29, (byte) 0xb5, + (byte) 0x94, (byte) 0x52, (byte) 0x4a, (byte) 0xa9, + (byte) 0xb5, (byte) 0x16, (byte) 0x5b, (byte) 0x8a, + (byte) 0xb1, (byte) 0xb5, (byte) 0xd4, (byte) 0x4a, + (byte) 0x4a, (byte) 0x29, (byte) 0xb5, (byte) 0xd4, + (byte) 0x5a, (byte) 0x6a, (byte) 0x2d, (byte) 0xb6, + (byte) 0x16, (byte) 0x5b, (byte) 0x6b, (byte) 0xad, + (byte) 0xa5, (byte) 0xd6, (byte) 0x5a, (byte) 0x6a, + (byte) 0x29, (byte) 0xa5, (byte) 0x16, (byte) 0x5b, + (byte) 0x8c, (byte) 0x31, (byte) 0xc6, (byte) 0x16, + (byte) 0x63, (byte) 0x6b, (byte) 0x31, (byte) 0xa5, + (byte) 0x94, (byte) 0x52, (byte) 0x4b, (byte) 0xa9, + (byte) 0xa5, (byte) 0x02, (byte) 0x00, (byte) 0x80, + (byte) 0x0e, (byte) 0x1c, (byte) 0x00, (byte) 0x00, + (byte) 0x02, (byte) 0x8c, (byte) 0xa8, (byte) 0xb4, + (byte) 0x10, (byte) 0x3b, (byte) 0xcd, (byte) 0xb8, + (byte) 0xf2, (byte) 0x08, (byte) 0x1c, (byte) 0x51, + (byte) 0xc8, (byte) 0x30, (byte) 0x01, (byte) 0x15, + (byte) 0x1a, (byte) 0xb2, (byte) 0x12, (byte) 0x00, + (byte) 0x20, (byte) 0x03, (byte) 0x00, (byte) 0x20, + (byte) 0x90, (byte) 0x69, (byte) 0x92, (byte) 0x39, + (byte) 0x49, (byte) 0xa9, (byte) 0x11, (byte) 0x26, + (byte) 0x39, (byte) 0xc5, (byte) 0xa0, (byte) 0x94, + (byte) 0xe6, (byte) 0x9c, (byte) 0x53, (byte) 0x4a, + (byte) 0x29, (byte) 0xa5, (byte) 0x34, (byte) 0x44, + (byte) 0x96, (byte) 0x64, (byte) 0x90, (byte) 0x62, + (byte) 0x50, (byte) 0x1d, (byte) 0x99, (byte) 0x8c, + (byte) 0x39, (byte) 0x49, (byte) 0x39, (byte) 0x43, + (byte) 0xa4, (byte) 0x31, (byte) 0xa4, (byte) 0x20, + (byte) 0xf5, (byte) 0x4c, (byte) 0x91, (byte) 0xc7, + (byte) 0x94, (byte) 0x62, (byte) 0x10, (byte) 0x43, + (byte) 0x48, (byte) 0x2a, (byte) 0x74, (byte) 0x8a, + (byte) 0x39, (byte) 0x6c, (byte) 0x35, (byte) 0xf9, + (byte) 0x58, (byte) 0x42, (byte) 0x07, (byte) 0xb1, + (byte) 0x06, (byte) 0x65, (byte) 0x8c, (byte) 0x70, + (byte) 0x29, (byte) 0xc5, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x08, (byte) 0x02, (byte) 0x00, + (byte) 0x04, (byte) 0x84, (byte) 0x04, (byte) 0x00, + (byte) 0x18, (byte) 0x20, (byte) 0x28, (byte) 0x98, + (byte) 0x01, (byte) 0x00, (byte) 0x06, (byte) 0x07, + (byte) 0x08, (byte) 0x23, (byte) 0x07, (byte) 0x02, + (byte) 0x1d, (byte) 0x01, (byte) 0x04, (byte) 0x0e, + (byte) 0x6d, (byte) 0x00, (byte) 0x80, (byte) 0x81, + (byte) 0x08, (byte) 0x99, (byte) 0x09, (byte) 0x0c, + (byte) 0x0a, (byte) 0xa1, (byte) 0xc1, (byte) 0x41, + (byte) 0x26, (byte) 0x00, (byte) 0x3c, (byte) 0x40, + (byte) 0x44, (byte) 0x48, (byte) 0x05, (byte) 0x00, + (byte) 0x89, (byte) 0x09, (byte) 0x8a, (byte) 0xd2, + (byte) 0x85, (byte) 0x2e, (byte) 0x08, (byte) 0x21, + (byte) 0x82, (byte) 0x74, (byte) 0x11, (byte) 0x64, + (byte) 0xf1, (byte) 0xc0, (byte) 0x85, (byte) 0x13, + (byte) 0x37, (byte) 0x9e, (byte) 0xb8, (byte) 0xe1, + (byte) 0x84, (byte) 0x0e, (byte) 0x6d, (byte) 0x20, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x20, (byte) 0x00, (byte) 0xf0, + (byte) 0x01, (byte) 0x00, (byte) 0x90, (byte) 0x50, + (byte) 0x00, (byte) 0x11, (byte) 0x11, (byte) 0xd1, + (byte) 0xcc, (byte) 0x55, (byte) 0x58, (byte) 0x5c, + (byte) 0x60, (byte) 0x64, (byte) 0x68, (byte) 0x6c, + (byte) 0x70, (byte) 0x74, (byte) 0x78, (byte) 0x7c, + (byte) 0x80, (byte) 0x84, (byte) 0x08, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x10, (byte) 0x00, (byte) 0x7c, (byte) 0x00, + (byte) 0x00, (byte) 0x24, (byte) 0x22, (byte) 0x40, + (byte) 0x44, (byte) 0x44, (byte) 0x34, (byte) 0x73, + (byte) 0x15, (byte) 0x16, (byte) 0x17, (byte) 0x18, + (byte) 0x19, (byte) 0x1a, (byte) 0x1b, (byte) 0x1c, + (byte) 0x1d, (byte) 0x1e, (byte) 0x1f, (byte) 0x20, + (byte) 0x21, (byte) 0x01, (byte) 0x00, (byte) 0x80, + (byte) 0x00, (byte) 0x02, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x20, (byte) 0x80, + (byte) 0x00, (byte) 0x04, (byte) 0x04, (byte) 0x04, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x02, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x04, (byte) 0x04 + }; +} diff --git a/library/core/src/test/java/com/google/android/exoplayer2/RobolectricUtil.java b/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/RobolectricUtil.java similarity index 99% rename from library/core/src/test/java/com/google/android/exoplayer2/RobolectricUtil.java rename to testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/RobolectricUtil.java index 0b5740b72c..93611b9b0a 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/RobolectricUtil.java +++ b/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/RobolectricUtil.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.google.android.exoplayer2; +package com.google.android.exoplayer2.testutil; import static org.robolectric.Shadows.shadowOf; import static org.robolectric.util.ReflectionHelpers.callInstanceMethod; diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/StubExoPlayer.java b/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/StubExoPlayer.java similarity index 99% rename from testutils/src/main/java/com/google/android/exoplayer2/testutil/StubExoPlayer.java rename to testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/StubExoPlayer.java index 40d5b6c3f9..af8b10e6d3 100644 --- a/testutils/src/main/java/com/google/android/exoplayer2/testutil/StubExoPlayer.java +++ b/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/StubExoPlayer.java @@ -271,5 +271,4 @@ public abstract class StubExoPlayer implements ExoPlayer { public long getContentPosition() { throw new UnsupportedOperationException(); } - } diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/TimelineAsserts.java b/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/TimelineAsserts.java similarity index 84% rename from testutils/src/main/java/com/google/android/exoplayer2/testutil/TimelineAsserts.java rename to testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/TimelineAsserts.java index 42136bfe4d..abef8e06be 100644 --- a/testutils/src/main/java/com/google/android/exoplayer2/testutil/TimelineAsserts.java +++ b/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/TimelineAsserts.java @@ -23,9 +23,7 @@ import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.Timeline.Period; import com.google.android.exoplayer2.Timeline.Window; -/** - * Unit test for {@link Timeline}. - */ +/** Unit test for {@link Timeline}. */ public final class TimelineAsserts { private static final int[] REPEAT_MODES = { @@ -34,9 +32,7 @@ public final class TimelineAsserts { private TimelineAsserts() {} - /** - * Assert that timeline is empty (i.e. has no windows or periods). - */ + /** Assert that timeline is empty (i.e. has no windows or periods). */ public static void assertEmpty(Timeline timeline) { assertWindowIds(timeline); assertPeriodCounts(timeline); @@ -63,9 +59,7 @@ public final class TimelineAsserts { } } - /** - * Asserts that window properties {@link Window}.isDynamic are set correctly. - */ + /** Asserts that window properties {@link Window}.isDynamic are set correctly. */ public static void assertWindowIsDynamic(Timeline timeline, boolean... windowIsDynamic) { Window window = new Window(); for (int i = 0; i < timeline.getWindowCount(); i++) { @@ -78,8 +72,10 @@ public final class TimelineAsserts { * Asserts that previous window indices for each window depending on the repeat mode and the * shuffle mode are equal to the given sequence. */ - public static void assertPreviousWindowIndices(Timeline timeline, - @Player.RepeatMode int repeatMode, boolean shuffleModeEnabled, + public static void assertPreviousWindowIndices( + Timeline timeline, + @Player.RepeatMode int repeatMode, + boolean shuffleModeEnabled, int... expectedPreviousWindowIndices) { for (int i = 0; i < timeline.getWindowCount(); i++) { assertThat(timeline.getPreviousWindowIndex(i, repeatMode, shuffleModeEnabled)) @@ -88,11 +84,14 @@ public final class TimelineAsserts { } /** - * Asserts that next window indices for each window depending on the repeat mode and the - * shuffle mode are equal to the given sequence. + * Asserts that next window indices for each window depending on the repeat mode and the shuffle + * mode are equal to the given sequence. */ - public static void assertNextWindowIndices(Timeline timeline, @Player.RepeatMode int repeatMode, - boolean shuffleModeEnabled, int... expectedNextWindowIndices) { + public static void assertNextWindowIndices( + Timeline timeline, + @Player.RepeatMode int repeatMode, + boolean shuffleModeEnabled, + int... expectedNextWindowIndices) { for (int i = 0; i < timeline.getWindowCount(); i++) { assertThat(timeline.getNextWindowIndex(i, repeatMode, shuffleModeEnabled)) .isEqualTo(expectedNextWindowIndices[i]); @@ -113,9 +112,9 @@ public final class TimelineAsserts { } /** - * Asserts that period counts for each window are set correctly. Also asserts that - * {@link Window#firstPeriodIndex} and {@link Window#lastPeriodIndex} are set correctly, and it - * asserts the correct behavior of {@link Timeline#getNextWindowIndex(int, int, boolean)}. + * Asserts that period counts for each window are set correctly. Also asserts that {@link + * Window#firstPeriodIndex} and {@link Window#lastPeriodIndex} are set correctly, and it asserts + * the correct behavior of {@link Timeline#getNextWindowIndex(int, int, boolean)}. */ public static void assertPeriodCounts(Timeline timeline, int... expectedPeriodCounts) { int windowCount = timeline.getWindowCount(); @@ -147,8 +146,8 @@ public final class TimelineAsserts { .isEqualTo(i + 1); } else { int nextWindow = timeline.getNextWindowIndex(expectedWindowIndex, repeatMode, false); - int nextPeriod = nextWindow == C.INDEX_UNSET ? C.INDEX_UNSET - : accumulatedPeriodCounts[nextWindow]; + int nextPeriod = + nextWindow == C.INDEX_UNSET ? C.INDEX_UNSET : accumulatedPeriodCounts[nextWindow]; assertThat(timeline.getNextPeriodIndex(i, period, window, repeatMode, false)) .isEqualTo(nextPeriod); } @@ -156,9 +155,7 @@ public final class TimelineAsserts { } } - /** - * Asserts that periods' {@link Period#getAdGroupCount()} are set correctly. - */ + /** Asserts that periods' {@link Period#getAdGroupCount()} are set correctly. */ public static void assertAdGroupCounts(Timeline timeline, int... expectedAdGroupCounts) { Period period = new Period(); for (int i = 0; i < timeline.getPeriodCount(); i++) { @@ -166,5 +163,4 @@ public final class TimelineAsserts { assertThat(period.getAdGroupCount()).isEqualTo(expectedAdGroupCounts[i]); } } - } From f91e6349fdd464db6902e66a672667a601abe797 Mon Sep 17 00:00:00 2001 From: hoangtc Date: Wed, 28 Feb 2018 05:24:42 -0800 Subject: [PATCH 013/157] Migrate ExoPlayer Gradle build files. - Change 'compile' configuration (deprecared) to using 'implementation' and 'api' configurations instead. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=187311778 --- RELEASENOTES.md | 6 ++++++ demos/cast/build.gradle | 16 +++++++++------- demos/ima/build.gradle | 13 +++++++------ demos/main/build.gradle | 23 ++++++++++++----------- extensions/cast/README.md | 2 +- extensions/cast/build.gradle | 25 +++++++++++++++---------- extensions/cronet/build.gradle | 13 +++++++------ extensions/ffmpeg/build.gradle | 2 +- extensions/flac/build.gradle | 4 ++-- extensions/gvr/README.md | 2 +- extensions/gvr/build.gradle | 4 ++-- extensions/ima/README.md | 2 +- extensions/ima/build.gradle | 13 ++++--------- extensions/leanback/README.md | 2 +- extensions/leanback/build.gradle | 4 ++-- extensions/mediasession/README.md | 2 +- extensions/mediasession/build.gradle | 4 ++-- extensions/okhttp/README.md | 2 +- extensions/okhttp/build.gradle | 5 +++-- extensions/opus/build.gradle | 2 +- extensions/rtmp/README.md | 2 +- extensions/rtmp/build.gradle | 5 +++-- extensions/vp9/build.gradle | 5 +++-- library/all/build.gradle | 10 +++++----- library/core/build.gradle | 18 +++++++++--------- library/dash/build.gradle | 6 +++--- library/hls/build.gradle | 6 +++--- library/smoothstreaming/build.gradle | 6 +++--- library/ui/build.gradle | 4 ++-- playbacktests/build.gradle | 8 ++++---- testutils/build.gradle | 9 +++++---- testutils_robolectric/build.gradle | 6 ++++-- 32 files changed, 124 insertions(+), 107 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 1762d15b1c..cfd06901b5 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -2,6 +2,12 @@ ### 2.7.1 ### +* Gradle: Replaced 'compile' (deprecated) with 'implementation' and + 'api'. This may lead to build breakage for applications upgrading from + previous version that rely on indirect dependencies of certain modules. In such + cases, application developers need to add the missing dependency to their + gradle file. You can read more about the new dependency configurations + [here](https://developer.android.com/studio/build/gradle-plugin-3-0-0-migration.html#new_configurations). * HlsMediaSource: make HLS periods start at zero instead of the epoch. Applications that rely on HLS timelines having a period starting at the epoch will need to update their handling of HLS timelines. The program diff --git a/demos/cast/build.gradle b/demos/cast/build.gradle index 8f074c9238..9bf62c8fc3 100644 --- a/demos/cast/build.gradle +++ b/demos/cast/build.gradle @@ -42,11 +42,13 @@ android { } dependencies { - compile project(modulePrefix + 'library-core') - compile project(modulePrefix + 'library-dash') - compile project(modulePrefix + 'library-hls') - compile project(modulePrefix + 'library-smoothstreaming') - compile project(modulePrefix + 'library-ui') - compile project(modulePrefix + 'extension-cast') - compile 'com.android.support:recyclerview-v7:' + supportLibraryVersion + implementation project(modulePrefix + 'library-core') + implementation project(modulePrefix + 'library-dash') + implementation project(modulePrefix + 'library-hls') + implementation project(modulePrefix + 'library-smoothstreaming') + implementation project(modulePrefix + 'library-ui') + implementation project(modulePrefix + 'extension-cast') + implementation 'com.android.support:support-v4:' + supportLibraryVersion + implementation 'com.android.support:appcompat-v7:' + supportLibraryVersion + implementation 'com.android.support:recyclerview-v7:' + supportLibraryVersion } diff --git a/demos/ima/build.gradle b/demos/ima/build.gradle index 5225c260f8..710e878d14 100644 --- a/demos/ima/build.gradle +++ b/demos/ima/build.gradle @@ -41,10 +41,11 @@ android { } dependencies { - compile project(modulePrefix + 'library-core') - compile project(modulePrefix + 'library-ui') - compile project(modulePrefix + 'library-dash') - compile project(modulePrefix + 'library-hls') - compile project(modulePrefix + 'library-smoothstreaming') - compile project(modulePrefix + 'extension-ima') + implementation project(modulePrefix + 'library-core') + implementation project(modulePrefix + 'library-ui') + implementation project(modulePrefix + 'library-dash') + implementation project(modulePrefix + 'library-hls') + implementation project(modulePrefix + 'library-smoothstreaming') + implementation project(modulePrefix + 'extension-ima') + implementation 'com.android.support:support-annotations:' + supportLibraryVersion } diff --git a/demos/main/build.gradle b/demos/main/build.gradle index f637e39ce4..5b199faf7a 100644 --- a/demos/main/build.gradle +++ b/demos/main/build.gradle @@ -55,15 +55,16 @@ android { } dependencies { - compile project(modulePrefix + 'library-core') - compile project(modulePrefix + 'library-dash') - compile project(modulePrefix + 'library-hls') - compile project(modulePrefix + 'library-smoothstreaming') - compile project(modulePrefix + 'library-ui') - withExtensionsCompile project(path: modulePrefix + 'extension-ffmpeg') - withExtensionsCompile project(path: modulePrefix + 'extension-flac') - withExtensionsCompile project(path: modulePrefix + 'extension-ima') - withExtensionsCompile project(path: modulePrefix + 'extension-opus') - withExtensionsCompile project(path: modulePrefix + 'extension-vp9') - withExtensionsCompile project(path: modulePrefix + 'extension-rtmp') + implementation 'com.android.support:support-annotations:' + supportLibraryVersion + implementation project(modulePrefix + 'library-core') + implementation project(modulePrefix + 'library-dash') + implementation project(modulePrefix + 'library-hls') + implementation project(modulePrefix + 'library-smoothstreaming') + implementation project(modulePrefix + 'library-ui') + withExtensionsImplementation project(path: modulePrefix + 'extension-ffmpeg') + withExtensionsImplementation project(path: modulePrefix + 'extension-flac') + withExtensionsImplementation project(path: modulePrefix + 'extension-ima') + withExtensionsImplementation project(path: modulePrefix + 'extension-opus') + withExtensionsImplementation project(path: modulePrefix + 'extension-vp9') + withExtensionsImplementation project(path: modulePrefix + 'extension-rtmp') } diff --git a/extensions/cast/README.md b/extensions/cast/README.md index 8666690661..a0c34236c6 100644 --- a/extensions/cast/README.md +++ b/extensions/cast/README.md @@ -12,7 +12,7 @@ Cast receiver app. The easiest way to use the extension is to add it as a gradle dependency: ```gradle -compile 'com.google.android.exoplayer:extension-cast:rX.X.X' +implementation 'com.google.android.exoplayer:extension-cast:rX.X.X' ``` where `rX.X.X` is the version, which must match the version of the ExoPlayer diff --git a/extensions/cast/build.gradle b/extensions/cast/build.gradle index 2f79c7a0ee..253a1922a8 100644 --- a/extensions/cast/build.gradle +++ b/extensions/cast/build.gradle @@ -26,19 +26,24 @@ android { } dependencies { - // This dependency is necessary to force the supportLibraryVersion of - // com.android.support:support-v4 to be used. Else an older version (25.2.0) - // is included via: + // These dependencies are necessary to force the supportLibraryVersion of + // com.android.support:support-v4, com.android.support:appcompat-v7 and + // com.android.support:mediarouter-v7 to be used. Else older versions are + // used, for example: // com.google.android.gms:play-services-cast-framework:11.4.2 // |-- com.google.android.gms:play-services-basement:11.4.2 // |-- com.android.support:support-v4:25.2.0 - compile 'com.android.support:support-v4:' + supportLibraryVersion - compile 'com.android.support:appcompat-v7:' + supportLibraryVersion - compile 'com.android.support:mediarouter-v7:' + supportLibraryVersion - compile 'com.google.android.gms:play-services-cast-framework:' + playServicesLibraryVersion - compile project(modulePrefix + 'library-core') - compile project(modulePrefix + 'library-ui') - testCompile project(modulePrefix + 'testutils-robolectric') + api 'com.android.support:support-v4:' + supportLibraryVersion + api 'com.android.support:appcompat-v7:' + supportLibraryVersion + api 'com.android.support:mediarouter-v7:' + supportLibraryVersion + api 'com.google.android.gms:play-services-cast-framework:' + playServicesLibraryVersion + implementation project(modulePrefix + 'library-core') + implementation project(modulePrefix + 'library-ui') + testImplementation project(modulePrefix + 'testutils') + testImplementation 'junit:junit:' + junitVersion + testImplementation 'org.mockito:mockito-core:' + mockitoVersion + testImplementation 'org.robolectric:robolectric:' + robolectricVersion + testImplementation project(modulePrefix + 'testutils-robolectric') } ext { diff --git a/extensions/cronet/build.gradle b/extensions/cronet/build.gradle index 2d25c7299c..1cfb4f5513 100644 --- a/extensions/cronet/build.gradle +++ b/extensions/cronet/build.gradle @@ -35,12 +35,13 @@ android { } dependencies { - compile project(modulePrefix + 'library-core') - compile files('libs/cronet_api.jar') - compile files('libs/cronet_impl_common_java.jar') - compile files('libs/cronet_impl_native_java.jar') - testCompile project(modulePrefix + 'library') - testCompile project(modulePrefix + 'testutils-robolectric') + api files('libs/cronet_api.jar') + implementation files('libs/cronet_impl_common_java.jar') + implementation files('libs/cronet_impl_native_java.jar') + implementation project(modulePrefix + 'library-core') + implementation 'com.android.support:support-annotations:' + supportLibraryVersion + testImplementation project(modulePrefix + 'library') + testImplementation project(modulePrefix + 'testutils-robolectric') } ext { diff --git a/extensions/ffmpeg/build.gradle b/extensions/ffmpeg/build.gradle index 9820818f3e..e2d3a08e36 100644 --- a/extensions/ffmpeg/build.gradle +++ b/extensions/ffmpeg/build.gradle @@ -31,7 +31,7 @@ android { } dependencies { - compile project(modulePrefix + 'library-core') + implementation project(modulePrefix + 'library-core') } ext { diff --git a/extensions/flac/build.gradle b/extensions/flac/build.gradle index 4d840d34ac..f617064ce5 100644 --- a/extensions/flac/build.gradle +++ b/extensions/flac/build.gradle @@ -31,8 +31,8 @@ android { } dependencies { - compile project(modulePrefix + 'library-core') - androidTestCompile project(modulePrefix + 'testutils') + implementation project(modulePrefix + 'library-core') + androidTestImplementation project(modulePrefix + 'testutils') } ext { diff --git a/extensions/gvr/README.md b/extensions/gvr/README.md index 250cf58c2f..f5a52d3162 100644 --- a/extensions/gvr/README.md +++ b/extensions/gvr/README.md @@ -12,7 +12,7 @@ of surround sound and ambisonic soundfields. The easiest way to use the extension is to add it as a gradle dependency: ```gradle -compile 'com.google.android.exoplayer:extension-gvr:rX.X.X' +implementation 'com.google.android.exoplayer:extension-gvr:rX.X.X' ``` where `rX.X.X` is the version, which must match the version of the ExoPlayer diff --git a/extensions/gvr/build.gradle b/extensions/gvr/build.gradle index 8236024512..f146ba4df6 100644 --- a/extensions/gvr/build.gradle +++ b/extensions/gvr/build.gradle @@ -25,8 +25,8 @@ android { } dependencies { - compile project(modulePrefix + 'library-core') - compile 'com.google.vr:sdk-audio:1.80.0' + implementation project(modulePrefix + 'library-core') + implementation 'com.google.vr:sdk-audio:1.80.0' } ext { diff --git a/extensions/ima/README.md b/extensions/ima/README.md index a796ca8694..208d64fe71 100644 --- a/extensions/ima/README.md +++ b/extensions/ima/README.md @@ -12,7 +12,7 @@ alongside content. The easiest way to use the extension is to add it as a gradle dependency: ```gradle -compile 'com.google.android.exoplayer:extension-ima:rX.X.X' +implementation 'com.google.android.exoplayer:extension-ima:rX.X.X' ``` where `rX.X.X` is the version, which must match the version of the ExoPlayer diff --git a/extensions/ima/build.gradle b/extensions/ima/build.gradle index 5038aaf5b9..3a20e378ae 100644 --- a/extensions/ima/build.gradle +++ b/extensions/ima/build.gradle @@ -26,7 +26,6 @@ android { } dependencies { - compile project(modulePrefix + 'library-core') // This dependency is necessary to force the supportLibraryVersion of // com.android.support:support-v4 to be used. Else an older version (25.2.0) // is included via: @@ -34,14 +33,10 @@ dependencies { // |-- com.google.android.gms:play-services-ads-lite:11.4.2 // |-- com.google.android.gms:play-services-basement:11.4.2 // |-- com.android.support:support-v4:25.2.0 - compile 'com.android.support:support-v4:' + supportLibraryVersion - compile 'com.google.ads.interactivemedia.v3:interactivemedia:3.7.4' - compile 'com.google.android.gms:play-services-ads:' + playServicesLibraryVersion - androidTestCompile project(modulePrefix + 'library') - androidTestCompile 'com.google.dexmaker:dexmaker:' + dexmakerVersion - androidTestCompile 'com.google.dexmaker:dexmaker-mockito:' + dexmakerVersion - androidTestCompile 'org.mockito:mockito-core:' + mockitoVersion - androidTestCompile 'com.android.support.test:runner:' + testSupportLibraryVersion + api 'com.android.support:support-v4:' + supportLibraryVersion + api 'com.google.ads.interactivemedia.v3:interactivemedia:3.7.4' + implementation project(modulePrefix + 'library-core') + implementation 'com.google.android.gms:play-services-ads:' + playServicesLibraryVersion } ext { diff --git a/extensions/leanback/README.md b/extensions/leanback/README.md index 1fa71c9a8c..e9a0e6fbc4 100644 --- a/extensions/leanback/README.md +++ b/extensions/leanback/README.md @@ -11,7 +11,7 @@ ExoPlayer. The easiest way to use the extension is to add it as a gradle dependency: ```gradle -compile 'com.google.android.exoplayer:extension-leanback:rX.X.X' +implementation 'com.google.android.exoplayer:extension-leanback:rX.X.X' ``` where `rX.X.X` is the version, which must match the version of the ExoPlayer diff --git a/extensions/leanback/build.gradle b/extensions/leanback/build.gradle index d8952ca2b8..dc187a5709 100644 --- a/extensions/leanback/build.gradle +++ b/extensions/leanback/build.gradle @@ -25,8 +25,8 @@ android { } dependencies { - compile project(modulePrefix + 'library-core') - compile('com.android.support:leanback-v17:' + supportLibraryVersion) + implementation project(modulePrefix + 'library-core') + implementation('com.android.support:leanback-v17:' + supportLibraryVersion) } ext { diff --git a/extensions/mediasession/README.md b/extensions/mediasession/README.md index 3278e8dba5..f89b27f0b4 100644 --- a/extensions/mediasession/README.md +++ b/extensions/mediasession/README.md @@ -12,7 +12,7 @@ behaviour can be extended to support other playback and custom actions. The easiest way to use the extension is to add it as a gradle dependency: ```gradle -compile 'com.google.android.exoplayer:extension-mediasession:rX.X.X' +implementation 'com.google.android.exoplayer:extension-mediasession:rX.X.X' ``` where `rX.X.X` is the version, which must match the version of the ExoPlayer diff --git a/extensions/mediasession/build.gradle b/extensions/mediasession/build.gradle index 651bd952f8..eaaf078b5c 100644 --- a/extensions/mediasession/build.gradle +++ b/extensions/mediasession/build.gradle @@ -25,8 +25,8 @@ android { } dependencies { - compile project(modulePrefix + 'library-core') - compile 'com.android.support:support-media-compat:' + supportLibraryVersion + implementation project(modulePrefix + 'library-core') + implementation 'com.android.support:support-media-compat:' + supportLibraryVersion } ext { diff --git a/extensions/okhttp/README.md b/extensions/okhttp/README.md index e40535d4e8..1469b6cd51 100644 --- a/extensions/okhttp/README.md +++ b/extensions/okhttp/README.md @@ -19,7 +19,7 @@ licensed separately. The easiest way to use the extension is to add it as a gradle dependency: ```gradle -compile 'com.google.android.exoplayer:extension-okhttp:rX.X.X' +implementation 'com.google.android.exoplayer:extension-okhttp:rX.X.X' ``` where `rX.X.X` is the version, which must match the version of the ExoPlayer diff --git a/extensions/okhttp/build.gradle b/extensions/okhttp/build.gradle index 13bcff8a4e..2da245b1a5 100644 --- a/extensions/okhttp/build.gradle +++ b/extensions/okhttp/build.gradle @@ -30,8 +30,9 @@ android { } dependencies { - compile project(modulePrefix + 'library-core') - compile('com.squareup.okhttp3:okhttp:3.9.0') { + implementation project(modulePrefix + 'library-core') + implementation 'com.android.support:support-annotations:' + supportLibraryVersion + implementation('com.squareup.okhttp3:okhttp:3.9.0') { exclude group: 'org.json' } } diff --git a/extensions/opus/build.gradle b/extensions/opus/build.gradle index 41b428070f..2d20c65697 100644 --- a/extensions/opus/build.gradle +++ b/extensions/opus/build.gradle @@ -31,7 +31,7 @@ android { } dependencies { - compile project(modulePrefix + 'library-core') + implementation project(modulePrefix + 'library-core') } ext { diff --git a/extensions/rtmp/README.md b/extensions/rtmp/README.md index fb822b8326..cf7edd7643 100644 --- a/extensions/rtmp/README.md +++ b/extensions/rtmp/README.md @@ -20,7 +20,7 @@ Android, which is licensed separately. The easiest way to use the extension is to add it as a gradle dependency: ```gradle -compile 'com.google.android.exoplayer:extension-rtmp:rX.X.X' +implementation 'com.google.android.exoplayer:extension-rtmp:rX.X.X' ``` where `rX.X.X` is the version, which must match the version of the ExoPlayer diff --git a/extensions/rtmp/build.gradle b/extensions/rtmp/build.gradle index 2afa4a4ea7..c34e0b9999 100644 --- a/extensions/rtmp/build.gradle +++ b/extensions/rtmp/build.gradle @@ -25,8 +25,9 @@ android { } dependencies { - compile project(modulePrefix + 'library-core') - compile 'net.butterflytv.utils:rtmp-client:3.0.1' + implementation project(modulePrefix + 'library-core') + implementation 'net.butterflytv.utils:rtmp-client:3.0.1' + implementation 'com.android.support:support-annotations:' + supportLibraryVersion } ext { diff --git a/extensions/vp9/build.gradle b/extensions/vp9/build.gradle index 3d68e1428f..7dc95b388f 100644 --- a/extensions/vp9/build.gradle +++ b/extensions/vp9/build.gradle @@ -31,8 +31,9 @@ android { } dependencies { - compile project(modulePrefix + 'library-core') - androidTestCompile 'com.google.truth:truth:' + truthVersion + implementation project(modulePrefix + 'library-core') + implementation 'com.android.support:support-annotations:' + supportLibraryVersion + androidTestImplementation 'com.google.truth:truth:' + truthVersion } ext { diff --git a/library/all/build.gradle b/library/all/build.gradle index 79ed9c747b..bb832ba0ff 100644 --- a/library/all/build.gradle +++ b/library/all/build.gradle @@ -25,11 +25,11 @@ android { } dependencies { - compile project(modulePrefix + 'library-core') - compile project(modulePrefix + 'library-dash') - compile project(modulePrefix + 'library-hls') - compile project(modulePrefix + 'library-smoothstreaming') - compile project(modulePrefix + 'library-ui') + api project(modulePrefix + 'library-core') + api project(modulePrefix + 'library-dash') + api project(modulePrefix + 'library-hls') + api project(modulePrefix + 'library-smoothstreaming') + api project(modulePrefix + 'library-ui') } ext { diff --git a/library/core/build.gradle b/library/core/build.gradle index 3d655ba543..fe6045c2e7 100644 --- a/library/core/build.gradle +++ b/library/core/build.gradle @@ -45,15 +45,15 @@ android { } dependencies { - compile 'com.android.support:support-annotations:' + supportLibraryVersion - androidTestCompile 'com.google.dexmaker:dexmaker:' + dexmakerVersion - androidTestCompile 'com.google.dexmaker:dexmaker-mockito:' + dexmakerVersion - androidTestCompile 'com.google.truth:truth:' + truthVersion - androidTestCompile 'org.mockito:mockito-core:' + mockitoVersion - testCompile 'com.google.truth:truth:' + truthVersion - testCompile 'junit:junit:' + junitVersion - testCompile 'org.mockito:mockito-core:' + mockitoVersion - testCompile 'org.robolectric:robolectric:' + robolectricVersion + implementation 'com.android.support:support-annotations:' + supportLibraryVersion + androidTestImplementation 'com.google.dexmaker:dexmaker:' + dexmakerVersion + androidTestImplementation 'com.google.dexmaker:dexmaker-mockito:' + dexmakerVersion + androidTestImplementation 'com.google.truth:truth:' + truthVersion + androidTestImplementation 'org.mockito:mockito-core:' + mockitoVersion + testImplementation 'com.google.truth:truth:' + truthVersion + testImplementation 'junit:junit:' + junitVersion + testImplementation 'org.mockito:mockito-core:' + mockitoVersion + testImplementation 'org.robolectric:robolectric:' + robolectricVersion } ext { diff --git a/library/dash/build.gradle b/library/dash/build.gradle index 6cf6f64443..d2692eb7d9 100644 --- a/library/dash/build.gradle +++ b/library/dash/build.gradle @@ -33,9 +33,9 @@ android { } dependencies { - compile project(modulePrefix + 'library-core') - compile 'com.android.support:support-annotations:' + supportLibraryVersion - testCompile project(modulePrefix + 'testutils-robolectric') + implementation project(modulePrefix + 'library-core') + implementation 'com.android.support:support-annotations:' + supportLibraryVersion + testImplementation project(modulePrefix + 'testutils-robolectric') } ext { diff --git a/library/hls/build.gradle b/library/hls/build.gradle index 41e0b71da2..c2268a3007 100644 --- a/library/hls/build.gradle +++ b/library/hls/build.gradle @@ -33,9 +33,9 @@ android { } dependencies { - compile project(modulePrefix + 'library-core') - compile 'com.android.support:support-annotations:' + supportLibraryVersion - testCompile project(modulePrefix + 'testutils-robolectric') + implementation 'com.android.support:support-annotations:' + supportLibraryVersion + implementation project(modulePrefix + 'library-core') + testImplementation project(modulePrefix + 'testutils-robolectric') } ext { diff --git a/library/smoothstreaming/build.gradle b/library/smoothstreaming/build.gradle index b85f25e656..6ca5570a93 100644 --- a/library/smoothstreaming/build.gradle +++ b/library/smoothstreaming/build.gradle @@ -33,9 +33,9 @@ android { } dependencies { - compile project(modulePrefix + 'library-core') - compile 'com.android.support:support-annotations:' + supportLibraryVersion - testCompile project(modulePrefix + 'testutils-robolectric') + implementation project(modulePrefix + 'library-core') + implementation 'com.android.support:support-annotations:' + supportLibraryVersion + testImplementation project(modulePrefix + 'testutils-robolectric') } ext { diff --git a/library/ui/build.gradle b/library/ui/build.gradle index 89734ed806..9689fcef97 100644 --- a/library/ui/build.gradle +++ b/library/ui/build.gradle @@ -33,8 +33,8 @@ android { } dependencies { - compile project(modulePrefix + 'library-core') - compile 'com.android.support:support-annotations:' + supportLibraryVersion + implementation project(modulePrefix + 'library-core') + implementation 'com.android.support:support-annotations:' + supportLibraryVersion } ext { diff --git a/playbacktests/build.gradle b/playbacktests/build.gradle index 6cd56868f9..d5d524b5a5 100644 --- a/playbacktests/build.gradle +++ b/playbacktests/build.gradle @@ -25,8 +25,8 @@ android { } dependencies { - androidTestCompile project(modulePrefix + 'library-core') - androidTestCompile project(modulePrefix + 'library-dash') - androidTestCompile project(modulePrefix + 'library-hls') - androidTestCompile project(modulePrefix + 'testutils') + androidTestImplementation project(modulePrefix + 'library-core') + androidTestImplementation project(modulePrefix + 'library-dash') + androidTestImplementation project(modulePrefix + 'library-hls') + androidTestImplementation project(modulePrefix + 'testutils') } diff --git a/testutils/build.gradle b/testutils/build.gradle index 11ec55c047..a7f05a2c5e 100644 --- a/testutils/build.gradle +++ b/testutils/build.gradle @@ -32,8 +32,9 @@ android { } dependencies { - compile project(modulePrefix + 'library-core') - compile 'org.mockito:mockito-core:' + mockitoVersion - compile 'com.google.truth:truth:' + truthVersion - testCompile project(modulePrefix + 'testutils-robolectric') + api 'org.mockito:mockito-core:' + mockitoVersion + api 'com.google.truth:truth:' + truthVersion + implementation 'com.android.support:support-annotations:' + supportLibraryVersion + implementation project(modulePrefix + 'library-core') + testImplementation project(modulePrefix + 'testutils-robolectric') } diff --git a/testutils_robolectric/build.gradle b/testutils_robolectric/build.gradle index f5a427a4b3..c221149c29 100644 --- a/testutils_robolectric/build.gradle +++ b/testutils_robolectric/build.gradle @@ -32,6 +32,8 @@ android { } dependencies { - compile project(modulePrefix + 'testutils') - compile 'org.robolectric:robolectric:' + robolectricVersion + api 'org.robolectric:robolectric:' + robolectricVersion + api project(modulePrefix + 'testutils') + implementation project(modulePrefix + 'library-core') + implementation 'com.android.support:support-annotations:' + supportLibraryVersion } From f44a96f184338a77432adfe8e8c6b2e1784736cb Mon Sep 17 00:00:00 2001 From: olly Date: Tue, 6 Mar 2018 10:07:23 -0800 Subject: [PATCH 014/157] Remove "r" from instructions for extension modules ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=188038757 --- extensions/cast/README.md | 4 ++-- extensions/gvr/README.md | 4 ++-- extensions/ima/README.md | 4 ++-- extensions/leanback/README.md | 4 ++-- extensions/mediasession/README.md | 4 ++-- extensions/okhttp/README.md | 4 ++-- extensions/rtmp/README.md | 4 ++-- 7 files changed, 14 insertions(+), 14 deletions(-) diff --git a/extensions/cast/README.md b/extensions/cast/README.md index a0c34236c6..cc72c5f9bc 100644 --- a/extensions/cast/README.md +++ b/extensions/cast/README.md @@ -12,10 +12,10 @@ Cast receiver app. The easiest way to use the extension is to add it as a gradle dependency: ```gradle -implementation 'com.google.android.exoplayer:extension-cast:rX.X.X' +implementation 'com.google.android.exoplayer:extension-cast:2.X.X' ``` -where `rX.X.X` is the version, which must match the version of the ExoPlayer +where `2.X.X` is the version, which must match the version of the ExoPlayer library being used. Alternatively, you can clone the ExoPlayer repository and depend on the module diff --git a/extensions/gvr/README.md b/extensions/gvr/README.md index f5a52d3162..5dab885436 100644 --- a/extensions/gvr/README.md +++ b/extensions/gvr/README.md @@ -12,10 +12,10 @@ of surround sound and ambisonic soundfields. The easiest way to use the extension is to add it as a gradle dependency: ```gradle -implementation 'com.google.android.exoplayer:extension-gvr:rX.X.X' +implementation 'com.google.android.exoplayer:extension-gvr:2.X.X' ``` -where `rX.X.X` is the version, which must match the version of the ExoPlayer +where `2.X.X` is the version, which must match the version of the ExoPlayer library being used. Alternatively, you can clone the ExoPlayer repository and depend on the module diff --git a/extensions/ima/README.md b/extensions/ima/README.md index 208d64fe71..c5ef1af35f 100644 --- a/extensions/ima/README.md +++ b/extensions/ima/README.md @@ -12,10 +12,10 @@ alongside content. The easiest way to use the extension is to add it as a gradle dependency: ```gradle -implementation 'com.google.android.exoplayer:extension-ima:rX.X.X' +implementation 'com.google.android.exoplayer:extension-ima:2.X.X' ``` -where `rX.X.X` is the version, which must match the version of the ExoPlayer +where `2.X.X` is the version, which must match the version of the ExoPlayer library being used. Alternatively, you can clone the ExoPlayer repository and depend on the module diff --git a/extensions/leanback/README.md b/extensions/leanback/README.md index e9a0e6fbc4..4eba6552e1 100644 --- a/extensions/leanback/README.md +++ b/extensions/leanback/README.md @@ -11,10 +11,10 @@ ExoPlayer. The easiest way to use the extension is to add it as a gradle dependency: ```gradle -implementation 'com.google.android.exoplayer:extension-leanback:rX.X.X' +implementation 'com.google.android.exoplayer:extension-leanback:2.X.X' ``` -where `rX.X.X` is the version, which must match the version of the ExoPlayer +where `2.X.X` is the version, which must match the version of the ExoPlayer library being used. Alternatively, you can clone the ExoPlayer repository and depend on the module diff --git a/extensions/mediasession/README.md b/extensions/mediasession/README.md index f89b27f0b4..bd6b59c0c1 100644 --- a/extensions/mediasession/README.md +++ b/extensions/mediasession/README.md @@ -12,10 +12,10 @@ behaviour can be extended to support other playback and custom actions. The easiest way to use the extension is to add it as a gradle dependency: ```gradle -implementation 'com.google.android.exoplayer:extension-mediasession:rX.X.X' +implementation 'com.google.android.exoplayer:extension-mediasession:2.X.X' ``` -where `rX.X.X` is the version, which must match the version of the ExoPlayer +where `2.X.X` is the version, which must match the version of the ExoPlayer library being used. Alternatively, you can clone the ExoPlayer repository and depend on the module diff --git a/extensions/okhttp/README.md b/extensions/okhttp/README.md index 1469b6cd51..73297b54a9 100644 --- a/extensions/okhttp/README.md +++ b/extensions/okhttp/README.md @@ -19,10 +19,10 @@ licensed separately. The easiest way to use the extension is to add it as a gradle dependency: ```gradle -implementation 'com.google.android.exoplayer:extension-okhttp:rX.X.X' +implementation 'com.google.android.exoplayer:extension-okhttp:2.X.X' ``` -where `rX.X.X` is the version, which must match the version of the ExoPlayer +where `2.X.X` is the version, which must match the version of the ExoPlayer library being used. Alternatively, you can clone the ExoPlayer repository and depend on the module diff --git a/extensions/rtmp/README.md b/extensions/rtmp/README.md index cf7edd7643..b222bdabd9 100644 --- a/extensions/rtmp/README.md +++ b/extensions/rtmp/README.md @@ -20,10 +20,10 @@ Android, which is licensed separately. The easiest way to use the extension is to add it as a gradle dependency: ```gradle -implementation 'com.google.android.exoplayer:extension-rtmp:rX.X.X' +implementation 'com.google.android.exoplayer:extension-rtmp:2.X.X' ``` -where `rX.X.X` is the version, which must match the version of the ExoPlayer +where `2.X.X` is the version, which must match the version of the ExoPlayer library being used. Alternatively, you can clone the ExoPlayer repository and depend on the module From 29839ab6a65ebbbe745612956291f240ead140ec Mon Sep 17 00:00:00 2001 From: olly Date: Mon, 12 Mar 2018 06:56:35 -0700 Subject: [PATCH 015/157] Fix min api check in RobolectricUtil ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=188716201 --- .../google/android/exoplayer2/testutil/RobolectricUtil.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/RobolectricUtil.java b/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/RobolectricUtil.java index 93611b9b0a..e606fd104b 100644 --- a/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/RobolectricUtil.java +++ b/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/RobolectricUtil.java @@ -185,9 +185,9 @@ public final class RobolectricUtil { @Override public int compareTo(@NonNull PendingMessage other) { - int res = Long.compare(this.when, other.when); + int res = Util.compareLong(this.when, other.when); if (res == 0 && this != other) { - res = Long.compare(this.sequenceNumber, other.sequenceNumber); + res = Util.compareLong(this.sequenceNumber, other.sequenceNumber); } return res; } From bdc5b5624d45f5abe3292d7dd03091c8f413f493 Mon Sep 17 00:00:00 2001 From: olly Date: Fri, 9 Mar 2018 08:44:24 -0800 Subject: [PATCH 016/157] Centralize version and target sdk constants We had these specified directly in AndroidManifest.xml due to our internal build setup. This change makes our internal build re-write AndroidManifest.xml to dynamically insert the required values, meaning they no longer need specifying in each manifest. Bonus 1: Internally built demo apps now include the CL number at which they're built in the version name :). Bonus 2: Removes lint warning that complains min/target SDK values in the manifest are redundant. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=188489115 --- constants.gradle | 6 ++++-- demos/cast/build.gradle | 2 ++ demos/cast/src/main/AndroidManifest.xml | 6 ++---- demos/ima/build.gradle | 2 ++ demos/main/build.gradle | 2 ++ demos/main/src/main/AndroidManifest.xml | 6 ++---- 6 files changed, 14 insertions(+), 10 deletions(-) diff --git a/constants.gradle b/constants.gradle index b02e2d4c37..a32aa2e8ab 100644 --- a/constants.gradle +++ b/constants.gradle @@ -12,13 +12,16 @@ // See the License for the specific language governing permissions and // limitations under the License. project.ext { + // ExoPlayer version and version code. + releaseVersion = '2.7.0' + releaseVersionCode = 2700 // Important: ExoPlayer specifies a minSdkVersion of 14 because various // components provided by the library may be of use on older devices. // However, please note that the core media playback functionality provided // by the library requires API level 16 or greater. minSdkVersion = 14 - compileSdkVersion = 27 targetSdkVersion = 27 + compileSdkVersion = 27 buildToolsVersion = '26.0.2' testSupportLibraryVersion = '0.5' supportLibraryVersion = '27.0.0' @@ -28,7 +31,6 @@ project.ext { junitVersion = '4.12' truthVersion = '0.39' robolectricVersion = '3.7.1' - releaseVersion = '2.7.0' modulePrefix = ':' if (gradle.ext.has('exoplayerModulePrefix')) { modulePrefix += gradle.ext.exoplayerModulePrefix diff --git a/demos/cast/build.gradle b/demos/cast/build.gradle index 9bf62c8fc3..c928d0e46e 100644 --- a/demos/cast/build.gradle +++ b/demos/cast/build.gradle @@ -19,6 +19,8 @@ android { buildToolsVersion project.ext.buildToolsVersion defaultConfig { + versionName project.ext.releaseVersion + versionCode project.ext.releaseVersionCode minSdkVersion 16 targetSdkVersion project.ext.targetSdkVersion } diff --git a/demos/cast/src/main/AndroidManifest.xml b/demos/cast/src/main/AndroidManifest.xml index d23576572a..ae16776333 100644 --- a/demos/cast/src/main/AndroidManifest.xml +++ b/demos/cast/src/main/AndroidManifest.xml @@ -14,12 +14,10 @@ limitations under the License. --> + package="com.google.android.exoplayer2.castdemo"> - + diff --git a/demos/ima/build.gradle b/demos/ima/build.gradle index 710e878d14..35c2daf88e 100644 --- a/demos/ima/build.gradle +++ b/demos/ima/build.gradle @@ -19,6 +19,8 @@ android { buildToolsVersion project.ext.buildToolsVersion defaultConfig { + versionName project.ext.releaseVersion + versionCode project.ext.releaseVersionCode minSdkVersion 16 targetSdkVersion project.ext.targetSdkVersion } diff --git a/demos/main/build.gradle b/demos/main/build.gradle index 5b199faf7a..ce0992eb7a 100644 --- a/demos/main/build.gradle +++ b/demos/main/build.gradle @@ -19,6 +19,8 @@ android { buildToolsVersion project.ext.buildToolsVersion defaultConfig { + versionName project.ext.releaseVersion + versionCode project.ext.releaseVersionCode minSdkVersion 16 targetSdkVersion project.ext.targetSdkVersion } diff --git a/demos/main/src/main/AndroidManifest.xml b/demos/main/src/main/AndroidManifest.xml index a98176d93b..cde95300ab 100644 --- a/demos/main/src/main/AndroidManifest.xml +++ b/demos/main/src/main/AndroidManifest.xml @@ -15,15 +15,13 @@ --> + package="com.google.android.exoplayer2.demo"> - + Date: Fri, 9 Mar 2018 11:38:20 -0800 Subject: [PATCH 017/157] Bump version to 2.7.1 and update release notes ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=188514063 --- RELEASENOTES.md | 10 +++++----- constants.gradle | 4 ++-- .../exoplayer2/ExoPlayerLibraryInfo.java | 18 +++++++----------- 3 files changed, 14 insertions(+), 18 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index cfd06901b5..17e5b19bca 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -4,11 +4,11 @@ * Gradle: Replaced 'compile' (deprecated) with 'implementation' and 'api'. This may lead to build breakage for applications upgrading from - previous version that rely on indirect dependencies of certain modules. In such - cases, application developers need to add the missing dependency to their - gradle file. You can read more about the new dependency configurations + previous version that rely on indirect dependencies of certain modules. In + such cases, application developers need to add the missing dependency to + their gradle file. You can read more about the new dependency configurations [here](https://developer.android.com/studio/build/gradle-plugin-3-0-0-migration.html#new_configurations). -* HlsMediaSource: make HLS periods start at zero instead of the epoch. +* HlsMediaSource: Make HLS periods start at zero instead of the epoch. Applications that rely on HLS timelines having a period starting at the epoch will need to update their handling of HLS timelines. The program date time is still available via the informational @@ -17,7 +17,7 @@ [#3888](https://github.com/google/ExoPlayer/issues/3888)). * Enable seeking in MP4 streams where duration is set incorrectly in the track header ([#3926](https://github.com/google/ExoPlayer/issues/3926)). -* Video: force rendering a frame periodically in `MediaCodecVideoRenderer` and +* Video: Force rendering a frame periodically in `MediaCodecVideoRenderer` and `LibvpxVideoRenderer`, even if it is late. ### 2.7.0 ### diff --git a/constants.gradle b/constants.gradle index a32aa2e8ab..3e75567231 100644 --- a/constants.gradle +++ b/constants.gradle @@ -13,8 +13,8 @@ // limitations under the License. project.ext { // ExoPlayer version and version code. - releaseVersion = '2.7.0' - releaseVersionCode = 2700 + releaseVersion = '2.7.1' + releaseVersionCode = 2701 // Important: ExoPlayer specifies a minSdkVersion of 14 because various // components provided by the library may be of use on older devices. // However, please note that the core media playback functionality provided diff --git a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java index 1dec506ec9..c34145a145 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java @@ -27,27 +27,23 @@ public final class ExoPlayerLibraryInfo { */ public static final String TAG = "ExoPlayer"; - /** - * 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. - public static final String VERSION = "2.7.0"; + public static final String VERSION = "2.7.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. - public static final String VERSION_SLASHY = "ExoPlayerLib/2.7.0"; + public static final String VERSION_SLASHY = "ExoPlayerLib/2.7.1"; /** * The version of the library expressed as an integer, for example 1002003. - *

- * Three digits are used for each component of {@link #VERSION}. For example "1.2.3" has the + * + *

Three digits are used for each component of {@link #VERSION}. For example "1.2.3" has the * corresponding integer version 1002003 (001-002-003), and "123.45.6" has the corresponding * 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 = 2007000; + public static final int VERSION_INT = 2007001; /** * Whether the library was compiled with {@link com.google.android.exoplayer2.util.Assertions} From 5706105b7c269d416f5732ee59a126dacd4bf25e Mon Sep 17 00:00:00 2001 From: aquilescanta Date: Tue, 13 Mar 2018 07:48:04 -0700 Subject: [PATCH 018/157] Recreate the cast media queue whenever the timeline is cleared ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=188871914 --- .../google/android/exoplayer2/castdemo/PlayerManager.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/demos/cast/src/main/java/com/google/android/exoplayer2/castdemo/PlayerManager.java b/demos/cast/src/main/java/com/google/android/exoplayer2/castdemo/PlayerManager.java index ac488ff3fd..a14978a46a 100644 --- a/demos/cast/src/main/java/com/google/android/exoplayer2/castdemo/PlayerManager.java +++ b/demos/cast/src/main/java/com/google/android/exoplayer2/castdemo/PlayerManager.java @@ -25,6 +25,7 @@ import com.google.android.exoplayer2.ExoPlayerFactory; import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.Player.DefaultEventListener; import com.google.android.exoplayer2.Player.DiscontinuityReason; +import com.google.android.exoplayer2.Player.TimelineChangeReason; import com.google.android.exoplayer2.RenderersFactory; import com.google.android.exoplayer2.SimpleExoPlayer; import com.google.android.exoplayer2.Timeline; @@ -281,8 +282,12 @@ import java.util.ArrayList; } @Override - public void onTimelineChanged(Timeline timeline, Object manifest) { + public void onTimelineChanged( + Timeline timeline, Object manifest, @TimelineChangeReason int reason) { updateCurrentItemIndex(); + if (timeline.isEmpty()) { + castMediaQueueCreationPending = true; + } } // CastPlayer.SessionAvailabilityListener implementation. From bd71a60f48c364b5de2a2c4ef0cd063870173ee5 Mon Sep 17 00:00:00 2001 From: Oliver Woodman Date: Tue, 13 Mar 2018 23:44:52 +0000 Subject: [PATCH 019/157] Remove stray test --- .../dash/offline/DashDownloadActionTest.java | 162 ------------------ 1 file changed, 162 deletions(-) delete mode 100644 library/dash/src/test/java/com/google/android/exoplayer2/source/dash/offline/DashDownloadActionTest.java diff --git a/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/offline/DashDownloadActionTest.java b/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/offline/DashDownloadActionTest.java deleted file mode 100644 index ea47722b69..0000000000 --- a/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/offline/DashDownloadActionTest.java +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Copyright (C) 2017 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.dash.offline; - -import static com.google.common.truth.Truth.assertThat; - -import android.net.Uri; -import com.google.android.exoplayer2.offline.DownloadAction; -import com.google.android.exoplayer2.offline.DownloaderConstructorHelper; -import com.google.android.exoplayer2.source.dash.manifest.RepresentationKey; -import com.google.android.exoplayer2.upstream.DummyDataSource; -import com.google.android.exoplayer2.upstream.cache.Cache; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.IOException; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mockito; -import org.mockito.MockitoAnnotations; -import org.robolectric.RobolectricTestRunner; - -/** - * Unit tests for {@link DashDownloadAction}. - */ -@RunWith(RobolectricTestRunner.class) -public class DashDownloadActionTest { - - @Test - public void testDownloadActionIsNotRemoveAction() throws Exception { - DashDownloadAction action = new DashDownloadAction(Uri.parse("uri"), false, null); - assertThat(action.isRemoveAction()).isFalse(); - } - - @Test - public void testRemoveActionIsRemoveAction() throws Exception { - DashDownloadAction action2 = new DashDownloadAction(Uri.parse("uri"), true, null); - assertThat(action2.isRemoveAction()).isTrue(); - } - - @Test - public void testCreateDownloader() throws Exception { - MockitoAnnotations.initMocks(this); - DashDownloadAction action = new DashDownloadAction(Uri.parse("uri"), false, null); - DownloaderConstructorHelper constructorHelper = new DownloaderConstructorHelper( - Mockito.mock(Cache.class), DummyDataSource.FACTORY); - assertThat(action.createDownloader(constructorHelper)).isNotNull(); - } - - @Test - public void testSameUriDifferentAction_IsSameMedia() throws Exception { - DashDownloadAction action1 = new DashDownloadAction(Uri.parse("uri"), true, null); - DashDownloadAction action2 = new DashDownloadAction(Uri.parse("uri"), false, null); - assertThat(action1.isSameMedia(action2)).isTrue(); - } - - @Test - public void testDifferentUriAndAction_IsNotSameMedia() throws Exception { - DashDownloadAction action3 = new DashDownloadAction(Uri.parse("uri2"), true, null); - DashDownloadAction action4 = new DashDownloadAction(Uri.parse("uri"), false, null); - assertThat(action3.isSameMedia(action4)).isFalse(); - } - - @SuppressWarnings("EqualsWithItself") - @Test - public void testEquals() throws Exception { - DashDownloadAction action1 = new DashDownloadAction(Uri.parse("uri"), true, null); - assertThat(action1.equals(action1)).isTrue(); - - DashDownloadAction action2 = new DashDownloadAction(Uri.parse("uri"), true, null); - DashDownloadAction action3 = new DashDownloadAction(Uri.parse("uri"), true, null); - assertEqual(action2, action3); - - DashDownloadAction action4 = new DashDownloadAction(Uri.parse("uri"), true, null); - DashDownloadAction action5 = new DashDownloadAction(Uri.parse("uri"), false, null); - assertNotEqual(action4, action5); - - DashDownloadAction action6 = new DashDownloadAction(Uri.parse("uri"), false, null); - DashDownloadAction action7 = - new DashDownloadAction(Uri.parse("uri"), false, null, new RepresentationKey(0, 0, 0)); - assertNotEqual(action6, action7); - - DashDownloadAction action8 = - new DashDownloadAction(Uri.parse("uri"), false, null, new RepresentationKey(1, 1, 1)); - DashDownloadAction action9 = - new DashDownloadAction(Uri.parse("uri"), false, null, new RepresentationKey(0, 0, 0)); - assertNotEqual(action8, action9); - - DashDownloadAction action10 = new DashDownloadAction(Uri.parse("uri"), true, null); - DashDownloadAction action11 = new DashDownloadAction(Uri.parse("uri2"), true, null); - assertNotEqual(action10, action11); - - DashDownloadAction action12 = new DashDownloadAction(Uri.parse("uri"), false, null, - new RepresentationKey(0, 0, 0), new RepresentationKey(1, 1, 1)); - DashDownloadAction action13 = new DashDownloadAction(Uri.parse("uri"), false, null, - new RepresentationKey(1, 1, 1), new RepresentationKey(0, 0, 0)); - assertEqual(action12, action13); - - DashDownloadAction action14 = new DashDownloadAction(Uri.parse("uri"), false, null, - new RepresentationKey(0, 0, 0)); - DashDownloadAction action15 = new DashDownloadAction(Uri.parse("uri"), false, null, - new RepresentationKey(1, 1, 1), new RepresentationKey(0, 0, 0)); - assertNotEqual(action14, action15); - - DashDownloadAction action16 = new DashDownloadAction(Uri.parse("uri"), false, null); - DashDownloadAction action17 = - new DashDownloadAction(Uri.parse("uri"), false, null, new RepresentationKey[0]); - assertEqual(action16, action17); - } - - @Test - public void testSerializerGetType() throws Exception { - DashDownloadAction action = new DashDownloadAction(Uri.parse("uri"), false, null); - assertThat(action.getType()).isNotNull(); - } - - @Test - public void testSerializerWriteRead() throws Exception { - doTestSerializationRoundTrip(new DashDownloadAction(Uri.parse("uri"), false, null)); - doTestSerializationRoundTrip(new DashDownloadAction(Uri.parse("uri"), true, null)); - doTestSerializationRoundTrip(new DashDownloadAction(Uri.parse("uri2"), false, null, - new RepresentationKey(0, 0, 0), new RepresentationKey(1, 1, 1))); - } - - private static void assertNotEqual(DashDownloadAction action1, DashDownloadAction action2) { - assertThat(action1).isNotEqualTo(action2); - assertThat(action2).isNotEqualTo(action1); - } - - private static void assertEqual(DashDownloadAction action1, DashDownloadAction action2) { - assertThat(action1).isEqualTo(action2); - assertThat(action2).isEqualTo(action1); - } - - private static void doTestSerializationRoundTrip(DashDownloadAction action1) throws IOException { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - DataOutputStream output = new DataOutputStream(out); - action1.writeToStream(output); - - ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); - DataInputStream input = new DataInputStream(in); - DownloadAction action2 = - DashDownloadAction.DESERIALIZER.readFromStream(DownloadAction.MASTER_VERSION, input); - - assertThat(action1).isEqualTo(action2); - } - -} From ff43df1e89ef11713e0e247ace65757a4b8e858c Mon Sep 17 00:00:00 2001 From: Oliver Woodman Date: Tue, 13 Mar 2018 23:48:55 +0000 Subject: [PATCH 020/157] Remove versions from IMA demo manifest that are now specified via gradle --- demos/ima/src/main/AndroidManifest.xml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/demos/ima/src/main/AndroidManifest.xml b/demos/ima/src/main/AndroidManifest.xml index 7f169b8095..50ad0c1b54 100644 --- a/demos/ima/src/main/AndroidManifest.xml +++ b/demos/ima/src/main/AndroidManifest.xml @@ -14,12 +14,10 @@ limitations under the License. --> + package="com.google.android.exoplayer2.imademo"> - + From 38914a260b1d9bc0870a0b6d59c78f5d4f47bf28 Mon Sep 17 00:00:00 2001 From: olly Date: Thu, 15 Mar 2018 01:32:11 -0700 Subject: [PATCH 021/157] Upgrade bintray-release and gradle versions ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=189153913 --- build.gradle | 2 +- gradle/wrapper/gradle-wrapper.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 9f9081a945..fa950a2e65 100644 --- a/build.gradle +++ b/build.gradle @@ -18,7 +18,7 @@ buildscript { } dependencies { classpath 'com.android.tools.build:gradle:3.0.1' - classpath 'com.novoda:bintray-release:0.5.0' + classpath 'com.novoda:bintray-release:0.8.0' } // Workaround for the following test coverage issue. Remove when fixed: // https://code.google.com/p/android/issues/detail?id=226070 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 32ec7e3327..b1289a577e 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip From 5ce0c6feadc2d1e1bb18183bb75b7e4d80f30d9e Mon Sep 17 00:00:00 2001 From: hoangtc Date: Thu, 15 Mar 2018 03:31:37 -0700 Subject: [PATCH 022/157] Match codecs starting with "mp4a" to different Audio MimeTypes Currently, we are treating all codecs starting with "mp4a" as AAC. However, some codec strings starting with "mp4a" are not AAC format, as should be treated differently. GitHub: #3779 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=189163517 --- RELEASENOTES.md | 5 + .../exoplayer2/extractor/mp4/AtomParsers.java | 48 ++------ .../android/exoplayer2/util/MimeTypes.java | 60 +++++++++- .../exoplayer2/util/MimeTypesTest.java | 106 ++++++++++++++++++ 4 files changed, 177 insertions(+), 42 deletions(-) create mode 100644 library/core/src/test/java/com/google/android/exoplayer2/util/MimeTypesTest.java diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 17e5b19bca..05571bad2a 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -1,5 +1,10 @@ # Release notes # +### 2.7.2 ### + +* Match codecs starting with "mp4a" to different Audio MimeTypes + ([#3779](https://github.com/google/ExoPlayer/issues/3779)). + ### 2.7.1 ### * Gradle: Replaced 'compile' (deprecated) with 'implementation' and diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/AtomParsers.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/AtomParsers.java index 37cfce7c7c..30358ff7c7 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/AtomParsers.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/AtomParsers.java @@ -15,6 +15,8 @@ */ package com.google.android.exoplayer2.extractor.mp4; +import static com.google.android.exoplayer2.util.MimeTypes.getMimeTypeFromMp4ObjectType; + import android.util.Log; import android.util.Pair; import com.google.android.exoplayer2.C; @@ -1030,47 +1032,11 @@ import java.util.List; // Set the MIME type based on the object type indication (14496-1 table 5). int objectTypeIndication = parent.readUnsignedByte(); - String mimeType; - switch (objectTypeIndication) { - case 0x60: - case 0x61: - mimeType = MimeTypes.VIDEO_MPEG2; - break; - case 0x20: - mimeType = MimeTypes.VIDEO_MP4V; - break; - case 0x21: - mimeType = MimeTypes.VIDEO_H264; - break; - case 0x23: - mimeType = MimeTypes.VIDEO_H265; - break; - case 0x6B: - mimeType = MimeTypes.AUDIO_MPEG; - return Pair.create(mimeType, null); - case 0x40: - case 0x66: - case 0x67: - case 0x68: - mimeType = MimeTypes.AUDIO_AAC; - break; - case 0xA5: - mimeType = MimeTypes.AUDIO_AC3; - break; - case 0xA6: - mimeType = MimeTypes.AUDIO_E_AC3; - break; - case 0xA9: - case 0xAC: - mimeType = MimeTypes.AUDIO_DTS; - return Pair.create(mimeType, null); - case 0xAA: - case 0xAB: - mimeType = MimeTypes.AUDIO_DTS_HD; - return Pair.create(mimeType, null); - default: - mimeType = null; - break; + String mimeType = getMimeTypeFromMp4ObjectType(objectTypeIndication); + if (MimeTypes.AUDIO_MPEG.equals(mimeType) + || MimeTypes.AUDIO_DTS.equals(mimeType) + || MimeTypes.AUDIO_DTS_HD.equals(mimeType)) { + return Pair.create(mimeType, null); } parent.skipBytes(12); diff --git a/library/core/src/main/java/com/google/android/exoplayer2/util/MimeTypes.java b/library/core/src/main/java/com/google/android/exoplayer2/util/MimeTypes.java index f39f897567..778ea9487b 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/util/MimeTypes.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/util/MimeTypes.java @@ -15,6 +15,7 @@ */ package com.google.android.exoplayer2.util; +import android.support.annotation.Nullable; import android.text.TextUtils; import com.google.android.exoplayer2.C; @@ -194,7 +195,20 @@ public final class MimeTypes { } else if (codec.startsWith("vp8") || codec.startsWith("vp08")) { return MimeTypes.VIDEO_VP8; } else if (codec.startsWith("mp4a")) { - return MimeTypes.AUDIO_AAC; + String mimeType = null; + if (codec.startsWith("mp4a.")) { + String objectTypeString = codec.substring(5); // remove the 'mp4a.' prefix + if (objectTypeString.length() >= 2) { + try { + String objectTypeHexString = objectTypeString.toUpperCase().substring(0, 2); + int objectTypeInt = Integer.parseInt(objectTypeHexString, 16); + mimeType = getMimeTypeFromMp4ObjectType(objectTypeInt); + } catch (NumberFormatException ignored) { + // ignored + } + } + } + return mimeType == null ? MimeTypes.AUDIO_AAC : mimeType; } else if (codec.startsWith("ac-3") || codec.startsWith("dac3")) { return MimeTypes.AUDIO_AC3; } else if (codec.startsWith("ec-3") || codec.startsWith("dec3")) { @@ -213,6 +227,50 @@ public final class MimeTypes { return null; } + /** + * Derives a mimeType from MP4 object type identifier, as defined in RFC 6381 and + * http://www.mp4ra.org/object.html. + * + * @param objectType The objectType identifier to derive. + * @return The mimeType, or null if it could not be derived. + */ + @Nullable + public static String getMimeTypeFromMp4ObjectType(int objectType) { + switch (objectType) { + case 0x60: + case 0x61: + return MimeTypes.VIDEO_MPEG2; + case 0x20: + return MimeTypes.VIDEO_MP4V; + case 0x21: + return MimeTypes.VIDEO_H264; + case 0x23: + return MimeTypes.VIDEO_H265; + case 0x69: + case 0x6B: + return MimeTypes.AUDIO_MPEG; + case 0x40: + case 0x66: + case 0x67: + case 0x68: + return MimeTypes.AUDIO_AAC; + case 0xA5: + return MimeTypes.AUDIO_AC3; + case 0xA6: + return MimeTypes.AUDIO_E_AC3; + case 0xA9: + case 0xAC: + return MimeTypes.AUDIO_DTS; + case 0xAA: + case 0xAB: + return MimeTypes.AUDIO_DTS_HD; + case 0xAD: + return MimeTypes.AUDIO_OPUS; + default: + return null; + } + } + /** * Returns the {@link C}{@code .TRACK_TYPE_*} constant that corresponds to a specified MIME type. * {@link C#TRACK_TYPE_UNKNOWN} if the MIME type is not known or the mapping cannot be diff --git a/library/core/src/test/java/com/google/android/exoplayer2/util/MimeTypesTest.java b/library/core/src/test/java/com/google/android/exoplayer2/util/MimeTypesTest.java new file mode 100644 index 0000000000..c607e92055 --- /dev/null +++ b/library/core/src/test/java/com/google/android/exoplayer2/util/MimeTypesTest.java @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2018 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.util; + +import static com.google.common.truth.Truth.assertThat; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; + +/** Unit test for {@link MimeTypes}. */ +@RunWith(RobolectricTestRunner.class) +public final class MimeTypesTest { + + @Test + public void testGetMediaMimeType_fromValidCodecs_returnsCorrectMimeType() { + assertThat(MimeTypes.getMediaMimeType("avc1")).isEqualTo(MimeTypes.VIDEO_H264); + assertThat(MimeTypes.getMediaMimeType("avc1.42E01E")).isEqualTo(MimeTypes.VIDEO_H264); + assertThat(MimeTypes.getMediaMimeType("avc1.42E01F")).isEqualTo(MimeTypes.VIDEO_H264); + assertThat(MimeTypes.getMediaMimeType("avc1.4D401F")).isEqualTo(MimeTypes.VIDEO_H264); + assertThat(MimeTypes.getMediaMimeType("avc1.4D4028")).isEqualTo(MimeTypes.VIDEO_H264); + assertThat(MimeTypes.getMediaMimeType("avc1.640028")).isEqualTo(MimeTypes.VIDEO_H264); + assertThat(MimeTypes.getMediaMimeType("avc1.640029")).isEqualTo(MimeTypes.VIDEO_H264); + assertThat(MimeTypes.getMediaMimeType("avc3")).isEqualTo(MimeTypes.VIDEO_H264); + assertThat(MimeTypes.getMediaMimeType("hev1")).isEqualTo(MimeTypes.VIDEO_H265); + assertThat(MimeTypes.getMediaMimeType("hvc1")).isEqualTo(MimeTypes.VIDEO_H265); + assertThat(MimeTypes.getMediaMimeType("vp08")).isEqualTo(MimeTypes.VIDEO_VP8); + assertThat(MimeTypes.getMediaMimeType("vp8")).isEqualTo(MimeTypes.VIDEO_VP8); + assertThat(MimeTypes.getMediaMimeType("vp09")).isEqualTo(MimeTypes.VIDEO_VP9); + assertThat(MimeTypes.getMediaMimeType("vp9")).isEqualTo(MimeTypes.VIDEO_VP9); + + assertThat(MimeTypes.getMediaMimeType("ac-3")).isEqualTo(MimeTypes.AUDIO_AC3); + assertThat(MimeTypes.getMediaMimeType("dac3")).isEqualTo(MimeTypes.AUDIO_AC3); + assertThat(MimeTypes.getMediaMimeType("dec3")).isEqualTo(MimeTypes.AUDIO_E_AC3); + assertThat(MimeTypes.getMediaMimeType("ec-3")).isEqualTo(MimeTypes.AUDIO_E_AC3); + assertThat(MimeTypes.getMediaMimeType("ec+3")).isEqualTo(MimeTypes.AUDIO_E_AC3_JOC); + assertThat(MimeTypes.getMediaMimeType("dtsc")).isEqualTo(MimeTypes.AUDIO_DTS); + assertThat(MimeTypes.getMediaMimeType("dtse")).isEqualTo(MimeTypes.AUDIO_DTS); + assertThat(MimeTypes.getMediaMimeType("dtsh")).isEqualTo(MimeTypes.AUDIO_DTS_HD); + assertThat(MimeTypes.getMediaMimeType("dtsl")).isEqualTo(MimeTypes.AUDIO_DTS_HD); + assertThat(MimeTypes.getMediaMimeType("opus")).isEqualTo(MimeTypes.AUDIO_OPUS); + assertThat(MimeTypes.getMediaMimeType("vorbis")).isEqualTo(MimeTypes.AUDIO_VORBIS); + assertThat(MimeTypes.getMediaMimeType("mp4a")).isEqualTo(MimeTypes.AUDIO_AAC); + assertThat(MimeTypes.getMediaMimeType("mp4a.40.02")).isEqualTo(MimeTypes.AUDIO_AAC); + assertThat(MimeTypes.getMediaMimeType("mp4a.40.05")).isEqualTo(MimeTypes.AUDIO_AAC); + assertThat(MimeTypes.getMediaMimeType("mp4a.40.2")).isEqualTo(MimeTypes.AUDIO_AAC); + assertThat(MimeTypes.getMediaMimeType("mp4a.40.5")).isEqualTo(MimeTypes.AUDIO_AAC); + assertThat(MimeTypes.getMediaMimeType("mp4a.40.29")).isEqualTo(MimeTypes.AUDIO_AAC); + assertThat(MimeTypes.getMediaMimeType("mp4a.66")).isEqualTo(MimeTypes.AUDIO_AAC); + assertThat(MimeTypes.getMediaMimeType("mp4a.67")).isEqualTo(MimeTypes.AUDIO_AAC); + assertThat(MimeTypes.getMediaMimeType("mp4a.68")).isEqualTo(MimeTypes.AUDIO_AAC); + assertThat(MimeTypes.getMediaMimeType("mp4a.69")).isEqualTo(MimeTypes.AUDIO_MPEG); + assertThat(MimeTypes.getMediaMimeType("mp4a.6B")).isEqualTo(MimeTypes.AUDIO_MPEG); + assertThat(MimeTypes.getMediaMimeType("mp4a.a5")).isEqualTo(MimeTypes.AUDIO_AC3); + assertThat(MimeTypes.getMediaMimeType("mp4a.A5")).isEqualTo(MimeTypes.AUDIO_AC3); + assertThat(MimeTypes.getMediaMimeType("mp4a.a6")).isEqualTo(MimeTypes.AUDIO_E_AC3); + assertThat(MimeTypes.getMediaMimeType("mp4a.A6")).isEqualTo(MimeTypes.AUDIO_E_AC3); + assertThat(MimeTypes.getMediaMimeType("mp4a.A9")).isEqualTo(MimeTypes.AUDIO_DTS); + assertThat(MimeTypes.getMediaMimeType("mp4a.AC")).isEqualTo(MimeTypes.AUDIO_DTS); + assertThat(MimeTypes.getMediaMimeType("mp4a.AA")).isEqualTo(MimeTypes.AUDIO_DTS_HD); + assertThat(MimeTypes.getMediaMimeType("mp4a.AB")).isEqualTo(MimeTypes.AUDIO_DTS_HD); + assertThat(MimeTypes.getMediaMimeType("mp4a.AD")).isEqualTo(MimeTypes.AUDIO_OPUS); + } + + @Test + public void testGetMimeTypeFromMp4ObjectType_forValidObjectType_returnsCorrectMimeType() { + assertThat(MimeTypes.getMimeTypeFromMp4ObjectType(0x60)).isEqualTo(MimeTypes.VIDEO_MPEG2); + assertThat(MimeTypes.getMimeTypeFromMp4ObjectType(0x61)).isEqualTo(MimeTypes.VIDEO_MPEG2); + assertThat(MimeTypes.getMimeTypeFromMp4ObjectType(0x20)).isEqualTo(MimeTypes.VIDEO_MP4V); + assertThat(MimeTypes.getMimeTypeFromMp4ObjectType(0x21)).isEqualTo(MimeTypes.VIDEO_H264); + assertThat(MimeTypes.getMimeTypeFromMp4ObjectType(0x23)).isEqualTo(MimeTypes.VIDEO_H265); + assertThat(MimeTypes.getMimeTypeFromMp4ObjectType(0x6B)).isEqualTo(MimeTypes.AUDIO_MPEG); + assertThat(MimeTypes.getMimeTypeFromMp4ObjectType(0x40)).isEqualTo(MimeTypes.AUDIO_AAC); + assertThat(MimeTypes.getMimeTypeFromMp4ObjectType(0x66)).isEqualTo(MimeTypes.AUDIO_AAC); + assertThat(MimeTypes.getMimeTypeFromMp4ObjectType(0x67)).isEqualTo(MimeTypes.AUDIO_AAC); + assertThat(MimeTypes.getMimeTypeFromMp4ObjectType(0x68)).isEqualTo(MimeTypes.AUDIO_AAC); + assertThat(MimeTypes.getMimeTypeFromMp4ObjectType(0xA5)).isEqualTo(MimeTypes.AUDIO_AC3); + assertThat(MimeTypes.getMimeTypeFromMp4ObjectType(0xA6)).isEqualTo(MimeTypes.AUDIO_E_AC3); + assertThat(MimeTypes.getMimeTypeFromMp4ObjectType(0xA9)).isEqualTo(MimeTypes.AUDIO_DTS); + assertThat(MimeTypes.getMimeTypeFromMp4ObjectType(0xAC)).isEqualTo(MimeTypes.AUDIO_DTS); + assertThat(MimeTypes.getMimeTypeFromMp4ObjectType(0xAA)).isEqualTo(MimeTypes.AUDIO_DTS_HD); + assertThat(MimeTypes.getMimeTypeFromMp4ObjectType(0xAB)).isEqualTo(MimeTypes.AUDIO_DTS_HD); + assertThat(MimeTypes.getMimeTypeFromMp4ObjectType(0xAD)).isEqualTo(MimeTypes.AUDIO_OPUS); + } + + @Test + public void testGetMimeTypeFromMp4ObjectType_forInvalidObjectType_returnsNull() { + assertThat(MimeTypes.getMimeTypeFromMp4ObjectType(0)).isNull(); + assertThat(MimeTypes.getMimeTypeFromMp4ObjectType(0x600)).isNull(); + assertThat(MimeTypes.getMimeTypeFromMp4ObjectType(0x01)).isNull(); + assertThat(MimeTypes.getMimeTypeFromMp4ObjectType(-1)).isNull(); + } +} From b04ec2190b80b0096653ce2fc8865b63d1ba2671 Mon Sep 17 00:00:00 2001 From: bachinger Date: Tue, 20 Mar 2018 04:31:36 -0700 Subject: [PATCH 023/157] Omit fast forward and rewind actions when current window is not seekable Issue: #4001 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=189722812 --- RELEASENOTES.md | 2 ++ .../ext/mediasession/DefaultPlaybackController.java | 7 +++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 05571bad2a..d303ecb533 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -4,6 +4,8 @@ * Match codecs starting with "mp4a" to different Audio MimeTypes ([#3779](https://github.com/google/ExoPlayer/issues/3779)). +* MediaSession extension: Omit fast forward and rewind actions when media is not + seekable ([#4001](https://github.com/google/ExoPlayer/issues/4001)). ### 2.7.1 ### diff --git a/extensions/mediasession/src/main/java/com/google/android/exoplayer2/ext/mediasession/DefaultPlaybackController.java b/extensions/mediasession/src/main/java/com/google/android/exoplayer2/ext/mediasession/DefaultPlaybackController.java index 1eb3ffd13d..ce597b45cd 100644 --- a/extensions/mediasession/src/main/java/com/google/android/exoplayer2/ext/mediasession/DefaultPlaybackController.java +++ b/extensions/mediasession/src/main/java/com/google/android/exoplayer2/ext/mediasession/DefaultPlaybackController.java @@ -77,11 +77,10 @@ public class DefaultPlaybackController implements MediaSessionConnector.Playback public long getSupportedPlaybackActions(Player player) { if (player == null || player.getCurrentTimeline().isEmpty()) { return 0; + } else if (!player.isCurrentWindowSeekable()) { + return BASE_ACTIONS; } - long actions = BASE_ACTIONS; - if (player.isCurrentWindowSeekable()) { - actions |= PlaybackStateCompat.ACTION_SEEK_TO; - } + long actions = BASE_ACTIONS | PlaybackStateCompat.ACTION_SEEK_TO; if (fastForwardIncrementMs > 0) { actions |= PlaybackStateCompat.ACTION_FAST_FORWARD; } From da6ec9cafe203de44f599939898d0fabeed5e0d2 Mon Sep 17 00:00:00 2001 From: aquilescanta Date: Wed, 21 Mar 2018 06:03:55 -0700 Subject: [PATCH 024/157] Add santoni and mido to codecNeedsSetOutputSurfaceWorkaround Issue:#4006 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=189896023 --- RELEASENOTES.md | 2 ++ .../exoplayer2/video/MediaCodecVideoRenderer.java | 10 +++++++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index d303ecb533..a8b263db4d 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -4,6 +4,8 @@ * Match codecs starting with "mp4a" to different Audio MimeTypes ([#3779](https://github.com/google/ExoPlayer/issues/3779)). +* Fix ANR issue on Redmi 4X and Redmi Note 4 + ([#4006](https://github.com/google/ExoPlayer/issues/4006)). * MediaSession extension: Omit fast forward and rewind actions when media is not seekable ([#4001](https://github.com/google/ExoPlayer/issues/4001)). diff --git a/library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java b/library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java index 2978d00d86..1519bfdd2b 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java @@ -1102,9 +1102,13 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { // Work around https://github.com/google/ExoPlayer/issues/3236, // https://github.com/google/ExoPlayer/issues/3355, // https://github.com/google/ExoPlayer/issues/3439, - // https://github.com/google/ExoPlayer/issues/3724 and - // https://github.com/google/ExoPlayer/issues/3835. - return (("deb".equals(Util.DEVICE) || "flo".equals(Util.DEVICE)) // Nexus 7 (2013) + // https://github.com/google/ExoPlayer/issues/3724, + // https://github.com/google/ExoPlayer/issues/3835 and + // https://github.com/google/ExoPlayer/issues/4006. + return (("deb".equals(Util.DEVICE) // Nexus 7 (2013) + || "flo".equals(Util.DEVICE) // Nexus 7 (2013) + || "mido".equals(Util.DEVICE) // Redmi Note 4 + || "santoni".equals(Util.DEVICE)) // Redmi 4X && "OMX.qcom.video.decoder.avc".equals(name)) || (("tcl_eu".equals(Util.DEVICE) // TCL Percee TV || "SVP-DTV15".equals(Util.DEVICE) // Sony Bravia 4K 2015 From febe604ca177a04442c9e997ddd51dcc476b5415 Mon Sep 17 00:00:00 2001 From: olly Date: Mon, 26 Mar 2018 04:05:13 -0700 Subject: [PATCH 025/157] Trim zero padding from EBML string values Issue #4010 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=190442962 --- RELEASENOTES.md | 2 ++ .../exoplayer2/extractor/mkv/DefaultEbmlReader.java | 12 +++++++++--- .../extractor/mkv/DefaultEbmlReaderTest.java | 8 ++++++++ 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index a8b263db4d..4fa71ec70f 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -6,6 +6,8 @@ ([#3779](https://github.com/google/ExoPlayer/issues/3779)). * Fix ANR issue on Redmi 4X and Redmi Note 4 ([#4006](https://github.com/google/ExoPlayer/issues/4006)). +* Fix handling of zero padded strings when parsing Matroska streams + ([#4010](https://github.com/google/ExoPlayer/issues/4010)). * MediaSession extension: Omit fast forward and rewind actions when media is not seekable ([#4001](https://github.com/google/ExoPlayer/issues/4001)). diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/mkv/DefaultEbmlReader.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/mkv/DefaultEbmlReader.java index 91bc170205..2c6130677f 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/mkv/DefaultEbmlReader.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/mkv/DefaultEbmlReader.java @@ -202,10 +202,11 @@ import java.util.Stack; } /** - * Reads and returns a string of length {@code byteLength} from the {@link ExtractorInput}. + * Reads a string of length {@code byteLength} from the {@link ExtractorInput}. Zero padding is + * removed, so the returned string may be shorter than {@code byteLength}. * * @param input The {@link ExtractorInput} from which to read. - * @param byteLength The length of the float being read. + * @param byteLength The length of the string being read, including zero padding. * @return The read string value. * @throws IOException If an error occurs reading from the input. * @throws InterruptedException If the thread is interrupted. @@ -217,7 +218,12 @@ import java.util.Stack; } byte[] stringBytes = new byte[byteLength]; input.readFully(stringBytes, 0, byteLength); - return new String(stringBytes); + // Remove zero padding. + int trimmedLength = byteLength; + while (trimmedLength > 0 && stringBytes[trimmedLength - 1] == 0) { + trimmedLength--; + } + return new String(stringBytes, 0, trimmedLength); } /** diff --git a/library/core/src/test/java/com/google/android/exoplayer2/extractor/mkv/DefaultEbmlReaderTest.java b/library/core/src/test/java/com/google/android/exoplayer2/extractor/mkv/DefaultEbmlReaderTest.java index 2fec5c7cab..e44da0404b 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/extractor/mkv/DefaultEbmlReaderTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/extractor/mkv/DefaultEbmlReaderTest.java @@ -89,6 +89,14 @@ public class DefaultEbmlReaderTest { assertEvents(input, expected.events); } + @Test + public void testStringElementWithZeroPadding() throws IOException, InterruptedException { + ExtractorInput input = createTestInput(0x42, 0x82, 0x86, 0x41, 0x62, 0x63, 0x00, 0x00, 0x00); + TestOutput expected = new TestOutput(); + expected.stringElement(TestOutput.ID_DOC_TYPE, "Abc"); + assertEvents(input, expected.events); + } + @Test public void testStringElementEmpty() throws IOException, InterruptedException { ExtractorInput input = createTestInput(0x42, 0x82, 0x80); From 6d9201e8db6c5a461eaf94b8751dc7d485adf62a Mon Sep 17 00:00:00 2001 From: aquilescanta Date: Tue, 27 Mar 2018 03:00:13 -0700 Subject: [PATCH 026/157] Escape :dash: emoji in RELEASENOTES ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=190591835 --- RELEASENOTES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 4fa71ec70f..0f51f5f005 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -85,7 +85,7 @@ ([#3630](https://github.com/google/ExoPlayer/issues/3630)). * DASH: * Support in-band Emsg events targeting the player with scheme id - "urn:mpeg:dash:event:2012" and scheme values "1", "2" and "3". + `urn:mpeg:dash:event:2012` and scheme values "1", "2" and "3". * Support EventStream elements in DASH manifests. * HLS: * Add opt-in support for chunkless preparation in HLS. This allows an From 50457d9110a4e27818b4802d6ef2595c958eb6bc Mon Sep 17 00:00:00 2001 From: aquilescanta Date: Tue, 27 Mar 2018 05:54:52 -0700 Subject: [PATCH 027/157] Assume TS when the HLS segment URI has no path Issue:#4033 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=190603518 --- .../exoplayer2/source/hls/DefaultHlsExtractorFactory.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/DefaultHlsExtractorFactory.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/DefaultHlsExtractorFactory.java index dc838c9506..702b1126cc 100644 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/DefaultHlsExtractorFactory.java +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/DefaultHlsExtractorFactory.java @@ -52,6 +52,9 @@ public final class DefaultHlsExtractorFactory implements HlsExtractorFactory { Format format, List muxedCaptionFormats, DrmInitData drmInitData, TimestampAdjuster timestampAdjuster) { String lastPathSegment = uri.getLastPathSegment(); + if (lastPathSegment == null) { + lastPathSegment = ""; + } boolean isPackedAudioExtractor = false; Extractor extractor; if (MimeTypes.TEXT_VTT.equals(format.sampleMimeType) From 9299d3b3a5aee8e0325e45bba539b9acd5949e50 Mon Sep 17 00:00:00 2001 From: hoangtc Date: Tue, 27 Mar 2018 10:26:09 -0700 Subject: [PATCH 028/157] Update Gradle version from 4.1 to 4.4 In Gradle 4.4, it is a bug to resolve a configuration before the lint task is created ([see [] Therefore, to upgrade gradle version, we need to change the "generateJavadoc" task to remove using files() call during initialization phase, but change move this to doFirst() instead. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=190634090 --- RELEASENOTES.md | 4 +++- gradle/wrapper/gradle-wrapper.properties | 2 +- javadoc_library.gradle | 8 ++++++-- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 0f51f5f005..5904e71684 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -2,6 +2,8 @@ ### 2.7.2 ### +* Gradle: Upgrade Gradle version from 4.1 to 4.4 so it can work with Android + Studio 3.1 ([#3708](https://github.com/google/ExoPlayer/issues/3708)). * Match codecs starting with "mp4a" to different Audio MimeTypes ([#3779](https://github.com/google/ExoPlayer/issues/3779)). * Fix ANR issue on Redmi 4X and Redmi Note 4 @@ -85,7 +87,7 @@ ([#3630](https://github.com/google/ExoPlayer/issues/3630)). * DASH: * Support in-band Emsg events targeting the player with scheme id - `urn:mpeg:dash:event:2012` and scheme values "1", "2" and "3". + "urn:mpeg:dash:event:2012" and scheme values "1", "2" and "3". * Support EventStream elements in DASH manifests. * HLS: * Add opt-in support for chunkless preparation in HLS. This allows an diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index b1289a577e..5559e8ccfa 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip diff --git a/javadoc_library.gradle b/javadoc_library.gradle index ea193e661c..f90131cc98 100644 --- a/javadoc_library.gradle +++ b/javadoc_library.gradle @@ -20,8 +20,6 @@ android.libraryVariants.all { variant -> description = "Generates Javadoc for the ${javadocTitle}." title = "ExoPlayer ${javadocTitle}" source = variant.javaCompile.source - classpath = files(variant.javaCompile.classpath.files, - project.android.getBootClasspath()) options { links "http://docs.oracle.com/javase/7/docs/api/" linksOffline "https://developer.android.com/reference", @@ -30,6 +28,12 @@ android.libraryVariants.all { variant -> } exclude "**/BuildConfig.java" exclude "**/R.java" + doFirst { + classpath = + files( + variant.javaCompile.classpath.files, + project.android.getBootClasspath()) + } doLast { copy { from "src/main/javadoc" From 516f163887ed973898a8402dc2ce7c678dcf8860 Mon Sep 17 00:00:00 2001 From: tonihei Date: Wed, 28 Mar 2018 04:13:59 -0700 Subject: [PATCH 029/157] Update Ffmpeg README with supported NDK versions disclaimer. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=190751660 --- extensions/ffmpeg/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/extensions/ffmpeg/README.md b/extensions/ffmpeg/README.md index b29c836887..fa7ac6b9fa 100644 --- a/extensions/ffmpeg/README.md +++ b/extensions/ffmpeg/README.md @@ -29,7 +29,8 @@ EXOPLAYER_ROOT="$(pwd)" FFMPEG_EXT_PATH="${EXOPLAYER_ROOT}/extensions/ffmpeg/src/main" ``` -* Download the [Android NDK][] and set its location in an environment variable: +* Download the [Android NDK][] and set its location in an environment variable. + Only versions up to NDK 15c are supported currently. ``` NDK_PATH="" From f36de381d9ba52cf903f112a0de7fd638c8c1d2c Mon Sep 17 00:00:00 2001 From: olly Date: Wed, 28 Mar 2018 04:34:24 -0700 Subject: [PATCH 030/157] Fix "FLAC decoder input buffer too small" issue Issue: #3514 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=190752950 --- RELEASENOTES.md | 1 + extensions/flac/src/androidTest/assets/bear.flac.0.dump | 2 +- extensions/flac/src/androidTest/assets/bear.flac.1.dump | 2 +- extensions/flac/src/androidTest/assets/bear.flac.2.dump | 2 +- extensions/flac/src/androidTest/assets/bear.flac.3.dump | 2 +- .../com/google/android/exoplayer2/ext/flac/FlacExtractor.java | 2 +- 6 files changed, 6 insertions(+), 5 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 5904e71684..3148cf0779 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -10,6 +10,7 @@ ([#4006](https://github.com/google/ExoPlayer/issues/4006)). * Fix handling of zero padded strings when parsing Matroska streams ([#4010](https://github.com/google/ExoPlayer/issues/4010)). +* Fix "Decoder input buffer too small" error when playing some FLAC streams. * MediaSession extension: Omit fast forward and rewind actions when media is not seekable ([#4001](https://github.com/google/ExoPlayer/issues/4001)). diff --git a/extensions/flac/src/androidTest/assets/bear.flac.0.dump b/extensions/flac/src/androidTest/assets/bear.flac.0.dump index 2a17cbdea6..ad88981718 100644 --- a/extensions/flac/src/androidTest/assets/bear.flac.0.dump +++ b/extensions/flac/src/androidTest/assets/bear.flac.0.dump @@ -9,7 +9,7 @@ track 0: id = null containerMimeType = null sampleMimeType = audio/raw - maxInputSize = -1 + maxInputSize = 16384 width = -1 height = -1 frameRate = -1.0 diff --git a/extensions/flac/src/androidTest/assets/bear.flac.1.dump b/extensions/flac/src/androidTest/assets/bear.flac.1.dump index 412e4a1b8f..22f30e9db2 100644 --- a/extensions/flac/src/androidTest/assets/bear.flac.1.dump +++ b/extensions/flac/src/androidTest/assets/bear.flac.1.dump @@ -9,7 +9,7 @@ track 0: id = null containerMimeType = null sampleMimeType = audio/raw - maxInputSize = -1 + maxInputSize = 16384 width = -1 height = -1 frameRate = -1.0 diff --git a/extensions/flac/src/androidTest/assets/bear.flac.2.dump b/extensions/flac/src/androidTest/assets/bear.flac.2.dump index 42ebb125d1..c52a74cbfb 100644 --- a/extensions/flac/src/androidTest/assets/bear.flac.2.dump +++ b/extensions/flac/src/androidTest/assets/bear.flac.2.dump @@ -9,7 +9,7 @@ track 0: id = null containerMimeType = null sampleMimeType = audio/raw - maxInputSize = -1 + maxInputSize = 16384 width = -1 height = -1 frameRate = -1.0 diff --git a/extensions/flac/src/androidTest/assets/bear.flac.3.dump b/extensions/flac/src/androidTest/assets/bear.flac.3.dump index 958cb0d418..760f369597 100644 --- a/extensions/flac/src/androidTest/assets/bear.flac.3.dump +++ b/extensions/flac/src/androidTest/assets/bear.flac.3.dump @@ -9,7 +9,7 @@ track 0: id = null containerMimeType = null sampleMimeType = audio/raw - maxInputSize = -1 + maxInputSize = 16384 width = -1 height = -1 frameRate = -1.0 diff --git a/extensions/flac/src/main/java/com/google/android/exoplayer2/ext/flac/FlacExtractor.java b/extensions/flac/src/main/java/com/google/android/exoplayer2/ext/flac/FlacExtractor.java index b630298c6e..6859b44877 100644 --- a/extensions/flac/src/main/java/com/google/android/exoplayer2/ext/flac/FlacExtractor.java +++ b/extensions/flac/src/main/java/com/google/android/exoplayer2/ext/flac/FlacExtractor.java @@ -116,7 +116,7 @@ public final class FlacExtractor implements Extractor { MimeTypes.AUDIO_RAW, null, streamInfo.bitRate(), - Format.NO_VALUE, + streamInfo.maxDecodedFrameSize(), streamInfo.channels, streamInfo.sampleRate, getPcmEncoding(streamInfo.bitsPerSample), From 9433e44c09198236b4e3ef6be7fb21b7ed546f47 Mon Sep 17 00:00:00 2001 From: olly Date: Thu, 29 Mar 2018 01:39:15 -0700 Subject: [PATCH 031/157] Update build tools for Android Studio 3.1 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=190896757 --- build.gradle | 4 ++-- constants.gradle | 2 +- javadoc_combined.gradle | 1 - publish.gradle | 4 ++-- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/build.gradle b/build.gradle index fa950a2e65..3813a241e0 100644 --- a/build.gradle +++ b/build.gradle @@ -17,8 +17,8 @@ buildscript { google() } dependencies { - classpath 'com.android.tools.build:gradle:3.0.1' - classpath 'com.novoda:bintray-release:0.8.0' + classpath 'com.android.tools.build:gradle:3.1.0' + classpath 'com.novoda:bintray-release:0.8.1' } // Workaround for the following test coverage issue. Remove when fixed: // https://code.google.com/p/android/issues/detail?id=226070 diff --git a/constants.gradle b/constants.gradle index 3e75567231..4619801390 100644 --- a/constants.gradle +++ b/constants.gradle @@ -22,7 +22,7 @@ project.ext { minSdkVersion = 14 targetSdkVersion = 27 compileSdkVersion = 27 - buildToolsVersion = '26.0.2' + buildToolsVersion = '27.0.3' testSupportLibraryVersion = '0.5' supportLibraryVersion = '27.0.0' playServicesLibraryVersion = '11.4.2' diff --git a/javadoc_combined.gradle b/javadoc_combined.gradle index 1fec48ca25..7e27f41c5e 100644 --- a/javadoc_combined.gradle +++ b/javadoc_combined.gradle @@ -35,7 +35,6 @@ class CombinedJavadocPlugin implements Plugin { } exclude "**/BuildConfig.java" exclude "**/R.java" - destinationDir project.file("$project.buildDir/docs/javadoc") doLast { libraryModules.each { libraryModule -> project.copy { diff --git a/publish.gradle b/publish.gradle index ca1a2cfd8b..85cf87aa85 100644 --- a/publish.gradle +++ b/publish.gradle @@ -16,8 +16,8 @@ if (project.ext.has("exoplayerPublishEnabled") apply plugin: 'bintray-release' publish { artifactId = releaseArtifact - description = releaseDescription - version = releaseVersion + desc = releaseDescription + publishVersion = releaseVersion repoName = getBintrayRepo() userOrg = 'google' groupId = 'com.google.android.exoplayer' From 90bdf09f1541a90ce63e5ec38e6a3662fe98c746 Mon Sep 17 00:00:00 2001 From: olly Date: Thu, 29 Mar 2018 05:20:58 -0700 Subject: [PATCH 032/157] Fix Javadoc generation + Javadoc ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=190916130 --- javadoc_combined.gradle | 32 +++++++++++++++++--------------- javadoc_library.gradle | 9 ++++----- javadoc_util.gradle | 31 +++++++++++++++++++++++++++++++ 3 files changed, 52 insertions(+), 20 deletions(-) create mode 100644 javadoc_util.gradle diff --git a/javadoc_combined.gradle b/javadoc_combined.gradle index 7e27f41c5e..901dc91544 100644 --- a/javadoc_combined.gradle +++ b/javadoc_combined.gradle @@ -11,6 +11,8 @@ // 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. +apply from: "${rootDir}/javadoc_util.gradle" + class CombinedJavadocPlugin implements Plugin { static final String TASK_NAME = "generateCombinedJavadoc" @@ -20,29 +22,35 @@ class CombinedJavadocPlugin implements Plugin { project.gradle.projectsEvaluated { Set libraryModules = getLibraryModules(project) if (!libraryModules.isEmpty()) { - String sdkDirectory = getSdkDirectory(libraryModules) project.task(TASK_NAME, type: Javadoc) { description = "Generates combined Javadoc." title = "ExoPlayer library" source = libraryModules.generateJavadoc.source - classpath = project.files(libraryModules.generateJavadoc.classpath) + classpath = project.files([]) destinationDir = project.file("$project.buildDir/docs/javadoc") options { - links "http://docs.oracle.com/javase/7/docs/api/" - linksOffline "https://developer.android.com/reference", - "${sdkDirectory}/docs/reference" + links "https://docs.oracle.com/javase/7/docs/api/", + "https://developer.android.com/reference" encoding = "UTF-8" } exclude "**/BuildConfig.java" exclude "**/R.java" - doLast { + doFirst { libraryModules.each { libraryModule -> - project.copy { - from "${libraryModule.projectDir}/src/main/javadoc" - into "${project.buildDir}/docs/javadoc" + libraryModule.android.libraryVariants.all { variant -> + def name = variant.buildType.name + if (name.equals("release")) { + classpath += + libraryModule.project.files( + variant.javaCompile.classpath.files, + libraryModule.project.android.getBootClasspath()) + } } } } + doLast { + project.javadocFixLinks() + } } } } @@ -56,12 +64,6 @@ class CombinedJavadocPlugin implements Plugin { } } - // Returns the Android SDK directory given a set of Android library modules. - private String getSdkDirectory(Set libraryModules) { - // We can retrieve the Android SDK directory from any module. - return libraryModules.iterator().next().android.sdkDirectory - } - } apply plugin: CombinedJavadocPlugin diff --git a/javadoc_library.gradle b/javadoc_library.gradle index f90131cc98..59c1e16515 100644 --- a/javadoc_library.gradle +++ b/javadoc_library.gradle @@ -11,6 +11,8 @@ // 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. +apply from: "${rootDir}/javadoc_util.gradle" + android.libraryVariants.all { variant -> def name = variant.buildType.name if (!name.equals("release")) { @@ -23,7 +25,7 @@ android.libraryVariants.all { variant -> options { links "http://docs.oracle.com/javase/7/docs/api/" linksOffline "https://developer.android.com/reference", - "${android.sdkDirectory}/docs/reference" + "${android.sdkDirectory}/docs/reference" encoding = "UTF-8" } exclude "**/BuildConfig.java" @@ -35,10 +37,7 @@ android.libraryVariants.all { variant -> project.android.getBootClasspath()) } doLast { - copy { - from "src/main/javadoc" - into "$buildDir/docs/javadoc" - } + project.javadocFixLinks() } } } diff --git a/javadoc_util.gradle b/javadoc_util.gradle new file mode 100644 index 0000000000..5e32e2dfc5 --- /dev/null +++ b/javadoc_util.gradle @@ -0,0 +1,31 @@ +// Copyright (C) 2018 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. +ext.javadocFixLinks = { + // Fix external Android links to target the top frame. + def androidRoot = "https://developer.android.com/reference/" + def androidLink = " Date: Thu, 29 Mar 2018 09:36:12 -0700 Subject: [PATCH 033/157] Bump version to 2.7.2 + update release notes ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=190941619 --- constants.gradle | 4 ++-- .../com/google/android/exoplayer2/ExoPlayerLibraryInfo.java | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/constants.gradle b/constants.gradle index 4619801390..61b2e44b27 100644 --- a/constants.gradle +++ b/constants.gradle @@ -13,8 +13,8 @@ // limitations under the License. project.ext { // ExoPlayer version and version code. - releaseVersion = '2.7.1' - releaseVersionCode = 2701 + releaseVersion = '2.7.2' + releaseVersionCode = 2702 // Important: ExoPlayer specifies a minSdkVersion of 14 because various // components provided by the library may be of use on older devices. // However, please note that the core media playback functionality provided diff --git a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java index c34145a145..bb3732fb94 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java @@ -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.7.1"; + public static final String VERSION = "2.7.2"; /** 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.7.1"; + public static final String VERSION_SLASHY = "ExoPlayerLib/2.7.2"; /** * 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 = 2007001; + public static final int VERSION_INT = 2007002; /** * Whether the library was compiled with {@link com.google.android.exoplayer2.util.Assertions} From f77077a84c404510f4cc2961b533b4e266bfcf0b Mon Sep 17 00:00:00 2001 From: olly Date: Thu, 29 Mar 2018 09:37:02 -0700 Subject: [PATCH 034/157] Don't call getLayoutDirection before API level 17 Weirdly, the Android Javadoc indicates that it returns something before the API level on which the same Javadoc states it was added. In any case, we can simply not call the method to avoid the warning, since we only use the value if the API level is at least 23 anyway. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=190941776 --- .../com/google/android/exoplayer2/ui/DefaultTimeBar.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/library/ui/src/main/java/com/google/android/exoplayer2/ui/DefaultTimeBar.java b/library/ui/src/main/java/com/google/android/exoplayer2/ui/DefaultTimeBar.java index 048bb3ffff..a15ee1b88e 100644 --- a/library/ui/src/main/java/com/google/android/exoplayer2/ui/DefaultTimeBar.java +++ b/library/ui/src/main/java/com/google/android/exoplayer2/ui/DefaultTimeBar.java @@ -250,7 +250,7 @@ public class DefaultTimeBar extends View implements TimeBar { try { scrubberDrawable = a.getDrawable(R.styleable.DefaultTimeBar_scrubber_drawable); if (scrubberDrawable != null) { - setDrawableLayoutDirection(scrubberDrawable, getLayoutDirection()); + setDrawableLayoutDirection(scrubberDrawable); defaultTouchTargetHeight = Math.max(scrubberDrawable.getMinimumHeight(), defaultTouchTargetHeight); } @@ -747,8 +747,8 @@ public class DefaultTimeBar extends View implements TimeBar { return true; } - private static int dpToPx(DisplayMetrics displayMetrics, int dps) { - return (int) (dps * displayMetrics.density + 0.5f); + private boolean setDrawableLayoutDirection(Drawable drawable) { + return Util.SDK_INT >= 23 && setDrawableLayoutDirection(drawable, getLayoutDirection()); } private static boolean setDrawableLayoutDirection(Drawable drawable, int layoutDirection) { @@ -771,4 +771,7 @@ public class DefaultTimeBar extends View implements TimeBar { return 0x33000000 | (adMarkerColor & 0x00FFFFFF); } + private static int dpToPx(DisplayMetrics displayMetrics, int dps) { + return (int) (dps * displayMetrics.density + 0.5f); + } } From 033b83c82eb3eef4a5f011017440191ebc42cb5b Mon Sep 17 00:00:00 2001 From: olly Date: Thu, 29 Mar 2018 09:38:54 -0700 Subject: [PATCH 035/157] Make toUpperCase conversions locale invariant ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=190942033 --- .../com/google/android/exoplayer2/util/MimeTypes.java | 2 +- .../java/com/google/android/exoplayer2/util/Util.java | 10 ++++++++++ .../hls/playlist/HlsMediaPlaylistParserTest.java | 6 +++--- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/util/MimeTypes.java b/library/core/src/main/java/com/google/android/exoplayer2/util/MimeTypes.java index 778ea9487b..041ee55cf1 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/util/MimeTypes.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/util/MimeTypes.java @@ -200,7 +200,7 @@ public final class MimeTypes { String objectTypeString = codec.substring(5); // remove the 'mp4a.' prefix if (objectTypeString.length() >= 2) { try { - String objectTypeHexString = objectTypeString.toUpperCase().substring(0, 2); + String objectTypeHexString = Util.toUpperInvariant(objectTypeString.substring(0, 2)); int objectTypeInt = Integer.parseInt(objectTypeHexString, 16); mimeType = getMimeTypeFromMp4ObjectType(objectTypeInt); } catch (NumberFormatException ignored) { diff --git a/library/core/src/main/java/com/google/android/exoplayer2/util/Util.java b/library/core/src/main/java/com/google/android/exoplayer2/util/Util.java index 2761cc3ce6..e01dcc6f3a 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/util/Util.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/util/Util.java @@ -306,6 +306,16 @@ public final class Util { return text == null ? null : text.toLowerCase(Locale.US); } + /** + * Converts text to upper case using {@link Locale#US}. + * + * @param text The text to convert. + * @return The upper case text, or null if {@code text} is null. + */ + public static String toUpperInvariant(String text) { + return text == null ? null : text.toUpperCase(Locale.US); + } + /** * Divides a {@code numerator} by a {@code denominator}, returning the ceiled result. * diff --git a/library/hls/src/test/java/com/google/android/exoplayer2/source/hls/playlist/HlsMediaPlaylistParserTest.java b/library/hls/src/test/java/com/google/android/exoplayer2/source/hls/playlist/HlsMediaPlaylistParserTest.java index b10997cfe9..5ba6f0c7f4 100644 --- a/library/hls/src/test/java/com/google/android/exoplayer2/source/hls/playlist/HlsMediaPlaylistParserTest.java +++ b/library/hls/src/test/java/com/google/android/exoplayer2/source/hls/playlist/HlsMediaPlaylistParserTest.java @@ -20,12 +20,12 @@ import static com.google.common.truth.Truth.assertThat; import android.net.Uri; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.source.hls.playlist.HlsMediaPlaylist.Segment; +import com.google.android.exoplayer2.util.Util; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.nio.charset.Charset; import java.util.List; -import java.util.Locale; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; @@ -122,7 +122,7 @@ public class HlsMediaPlaylistParserTest { .isEqualTo("https://priv.example.com/key.php?r=2682"); // 0xA7A == 2682. assertThat(segment.encryptionIV).isNotNull(); - assertThat(segment.encryptionIV.toUpperCase(Locale.getDefault())).isEqualTo("A7A"); + assertThat(Util.toUpperInvariant(segment.encryptionIV)).isEqualTo("A7A"); assertThat(segment.byterangeLength).isEqualTo(51740); assertThat(segment.byterangeOffset).isEqualTo(2147586650L); assertThat(segment.url).isEqualTo("https://priv.example.com/fileSequence2682.ts"); @@ -134,7 +134,7 @@ public class HlsMediaPlaylistParserTest { .isEqualTo("https://priv.example.com/key.php?r=2682"); // 0xA7B == 2683. assertThat(segment.encryptionIV).isNotNull(); - assertThat(segment.encryptionIV.toUpperCase(Locale.getDefault())).isEqualTo("A7B"); + assertThat(Util.toUpperInvariant(segment.encryptionIV)).isEqualTo("A7B"); assertThat(segment.byterangeLength).isEqualTo(C.LENGTH_UNSET); assertThat(segment.byterangeOffset).isEqualTo(0); assertThat(segment.url).isEqualTo("https://priv.example.com/fileSequence2683.ts"); From a9ed7a01818432c47f670990abbdd4e9078c9682 Mon Sep 17 00:00:00 2001 From: eguven Date: Thu, 29 Mar 2018 06:44:04 -0700 Subject: [PATCH 036/157] Fix DashDownloader failure for some multi segment representations Issue: #3729 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=190922866 --- .../android/exoplayer2/source/dash/offline/DashDownloader.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/offline/DashDownloader.java b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/offline/DashDownloader.java index abae7a5f1e..4a90feb532 100644 --- a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/offline/DashDownloader.java +++ b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/offline/DashDownloader.java @@ -117,7 +117,8 @@ public final class DashDownloader extends SegmentDownloader Date: Thu, 29 Mar 2018 13:01:11 -0700 Subject: [PATCH 037/157] Improve Javadoc postprocessing - Remove stray extra "/" from postprocessed oracle URLs - Remove date lines so the Javadoc diff better shows what actually changed between releases ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=190973079 --- javadoc_combined.gradle | 2 +- javadoc_library.gradle | 2 +- javadoc_util.gradle | 24 ++++++++++++++++-------- 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/javadoc_combined.gradle b/javadoc_combined.gradle index 901dc91544..061da9383c 100644 --- a/javadoc_combined.gradle +++ b/javadoc_combined.gradle @@ -49,7 +49,7 @@ class CombinedJavadocPlugin implements Plugin { } } doLast { - project.javadocFixLinks() + project.fixJavadoc() } } } diff --git a/javadoc_library.gradle b/javadoc_library.gradle index 59c1e16515..0894cad575 100644 --- a/javadoc_library.gradle +++ b/javadoc_library.gradle @@ -37,7 +37,7 @@ android.libraryVariants.all { variant -> project.android.getBootClasspath()) } doLast { - project.javadocFixLinks() + project.fixJavadoc() } } } diff --git a/javadoc_util.gradle b/javadoc_util.gradle index 5e32e2dfc5..7ea69a9406 100644 --- a/javadoc_util.gradle +++ b/javadoc_util.gradle @@ -11,21 +11,29 @@ // 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. -ext.javadocFixLinks = { +ext.fixJavadoc = { + def javadocPath = "${project.buildDir}/docs/javadoc" // Fix external Android links to target the top frame. def androidRoot = "https://developer.android.com/reference/" def androidLink = "\n" + ant.replaceregexp(match:dateMeta, replace:"") { + fileset(dir: "${javadocPath}", includes: "**/*.html") } } From 976182cb049d393536a2f69a3deca3a0bac8123e Mon Sep 17 00:00:00 2001 From: olly Date: Thu, 29 Mar 2018 13:26:15 -0700 Subject: [PATCH 038/157] Improve Javadoc postprocessing 2 - Fix typo - Reinstate copy step. It's needed for images ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=190976774 --- javadoc_combined.gradle | 6 ++++++ javadoc_library.gradle | 4 ++++ javadoc_util.gradle | 2 +- 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/javadoc_combined.gradle b/javadoc_combined.gradle index 061da9383c..aba4bf54bd 100644 --- a/javadoc_combined.gradle +++ b/javadoc_combined.gradle @@ -49,6 +49,12 @@ class CombinedJavadocPlugin implements Plugin { } } doLast { + libraryModules.each { libraryModule -> + project.copy { + from "${libraryModule.projectDir}/src/main/javadoc" + into "${project.buildDir}/docs/javadoc" + } + } project.fixJavadoc() } } diff --git a/javadoc_library.gradle b/javadoc_library.gradle index 0894cad575..a252b148c6 100644 --- a/javadoc_library.gradle +++ b/javadoc_library.gradle @@ -37,6 +37,10 @@ android.libraryVariants.all { variant -> project.android.getBootClasspath()) } doLast { + copy { + from "src/main/javadoc" + into "$buildDir/docs/javadoc" + } project.fixJavadoc() } } diff --git a/javadoc_util.gradle b/javadoc_util.gradle index 7ea69a9406..cff5f29392 100644 --- a/javadoc_util.gradle +++ b/javadoc_util.gradle @@ -24,7 +24,7 @@ ext.fixJavadoc = { def oracleRoot = "https://docs.oracle.com/javase/7/docs/api/" def oracleLink = " Date: Tue, 3 Apr 2018 01:03:23 -0700 Subject: [PATCH 039/157] Actually use IMA proguard configuration (oops!) Issue: #3723 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=191407560 --- extensions/ima/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/ima/build.gradle b/extensions/ima/build.gradle index 3a20e378ae..1a35ad3450 100644 --- a/extensions/ima/build.gradle +++ b/extensions/ima/build.gradle @@ -21,7 +21,7 @@ android { defaultConfig { minSdkVersion project.ext.minSdkVersion targetSdkVersion project.ext.targetSdkVersion - testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + consumerProguardFiles 'proguard-rules.txt' } } From fb868ad725f14c62143c8d5ccee7c576a54a13ef Mon Sep 17 00:00:00 2001 From: olly Date: Tue, 3 Apr 2018 01:27:10 -0700 Subject: [PATCH 040/157] Remove unnecessary line from test manifests ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=191409777 --- extensions/cast/src/test/AndroidManifest.xml | 1 - extensions/cronet/src/test/AndroidManifest.xml | 1 - library/core/src/test/AndroidManifest.xml | 1 - library/dash/src/test/AndroidManifest.xml | 1 - library/hls/src/test/AndroidManifest.xml | 1 - library/smoothstreaming/src/test/AndroidManifest.xml | 1 - testutils/src/test/AndroidManifest.xml | 1 - 7 files changed, 7 deletions(-) diff --git a/extensions/cast/src/test/AndroidManifest.xml b/extensions/cast/src/test/AndroidManifest.xml index 057efdc245..3f34bbb1f5 100644 --- a/extensions/cast/src/test/AndroidManifest.xml +++ b/extensions/cast/src/test/AndroidManifest.xml @@ -15,7 +15,6 @@ --> diff --git a/extensions/cronet/src/test/AndroidManifest.xml b/extensions/cronet/src/test/AndroidManifest.xml index 52be9aa157..a1512ae605 100644 --- a/extensions/cronet/src/test/AndroidManifest.xml +++ b/extensions/cronet/src/test/AndroidManifest.xml @@ -15,7 +15,6 @@ --> diff --git a/library/core/src/test/AndroidManifest.xml b/library/core/src/test/AndroidManifest.xml index 660c33c636..f2a4cd6647 100644 --- a/library/core/src/test/AndroidManifest.xml +++ b/library/core/src/test/AndroidManifest.xml @@ -15,7 +15,6 @@ --> diff --git a/library/dash/src/test/AndroidManifest.xml b/library/dash/src/test/AndroidManifest.xml index eecf596b92..d18291d0ee 100644 --- a/library/dash/src/test/AndroidManifest.xml +++ b/library/dash/src/test/AndroidManifest.xml @@ -15,7 +15,6 @@ --> diff --git a/library/hls/src/test/AndroidManifest.xml b/library/hls/src/test/AndroidManifest.xml index 331f3439ad..89fab4d9a2 100644 --- a/library/hls/src/test/AndroidManifest.xml +++ b/library/hls/src/test/AndroidManifest.xml @@ -15,7 +15,6 @@ --> diff --git a/library/smoothstreaming/src/test/AndroidManifest.xml b/library/smoothstreaming/src/test/AndroidManifest.xml index 1a8f8ee9c4..61eb3caddf 100644 --- a/library/smoothstreaming/src/test/AndroidManifest.xml +++ b/library/smoothstreaming/src/test/AndroidManifest.xml @@ -15,7 +15,6 @@ --> diff --git a/testutils/src/test/AndroidManifest.xml b/testutils/src/test/AndroidManifest.xml index 9602d01633..bea920852c 100644 --- a/testutils/src/test/AndroidManifest.xml +++ b/testutils/src/test/AndroidManifest.xml @@ -15,7 +15,6 @@ --> From ab3db0e4cba83676ab617b4bc84a873ba366caa0 Mon Sep 17 00:00:00 2001 From: olly Date: Tue, 3 Apr 2018 02:31:45 -0700 Subject: [PATCH 041/157] Remove view index adjustment for retry button (which no longer exists) Issue: #4059 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=191414566 --- .../java/com/google/android/exoplayer2/demo/PlayerActivity.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/demos/main/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java b/demos/main/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java index e91feaa291..058133895e 100644 --- a/demos/main/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java +++ b/demos/main/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java @@ -550,7 +550,7 @@ public class PlayerActivity extends Activity button.setText(label); button.setTag(i); button.setOnClickListener(this); - debugRootView.addView(button, debugRootView.getChildCount() - 1); + debugRootView.addView(button); } } } From 431c5b2354e44729b68c53323804bf5e426ec052 Mon Sep 17 00:00:00 2001 From: olly Date: Tue, 3 Apr 2018 03:16:25 -0700 Subject: [PATCH 042/157] Remove unnecessary testInstrumentationRunner lines ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=191418665 --- extensions/cast/build.gradle | 1 - extensions/cronet/build.gradle | 1 - 2 files changed, 2 deletions(-) diff --git a/extensions/cast/build.gradle b/extensions/cast/build.gradle index 253a1922a8..8780b961d6 100644 --- a/extensions/cast/build.gradle +++ b/extensions/cast/build.gradle @@ -21,7 +21,6 @@ android { defaultConfig { minSdkVersion 14 targetSdkVersion project.ext.targetSdkVersion - testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } } diff --git a/extensions/cronet/build.gradle b/extensions/cronet/build.gradle index 1cfb4f5513..0a52344464 100644 --- a/extensions/cronet/build.gradle +++ b/extensions/cronet/build.gradle @@ -21,7 +21,6 @@ android { defaultConfig { minSdkVersion project.ext.minSdkVersion targetSdkVersion project.ext.targetSdkVersion - testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } sourceSets.main { From 7171cce92d7670ec3689f9282a244f7babe0155b Mon Sep 17 00:00:00 2001 From: olly Date: Tue, 3 Apr 2018 03:38:01 -0700 Subject: [PATCH 043/157] Don't rely on rootDir for Javadoc gradle files Issue #4059 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=191419955 --- javadoc_combined.gradle | 2 +- javadoc_library.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/javadoc_combined.gradle b/javadoc_combined.gradle index aba4bf54bd..aea65d4d97 100644 --- a/javadoc_combined.gradle +++ b/javadoc_combined.gradle @@ -11,7 +11,7 @@ // 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. -apply from: "${rootDir}/javadoc_util.gradle" +apply from: "${buildscript.sourceFile.parentFile}/javadoc_util.gradle" class CombinedJavadocPlugin implements Plugin { diff --git a/javadoc_library.gradle b/javadoc_library.gradle index a252b148c6..65219843e3 100644 --- a/javadoc_library.gradle +++ b/javadoc_library.gradle @@ -11,7 +11,7 @@ // 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. -apply from: "${rootDir}/javadoc_util.gradle" +apply from: "${buildscript.sourceFile.parentFile}/javadoc_util.gradle" android.libraryVariants.all { variant -> def name = variant.buildType.name From 783a1b74e3c642efd1427375f71b1e8976c498b5 Mon Sep 17 00:00:00 2001 From: olly Date: Tue, 3 Apr 2018 04:16:38 -0700 Subject: [PATCH 044/157] OkHttp extension - Improved configuration - Upgrade to latest version - Use api dependency, since application code that uses the extension more has to use OkHttp directly to make an OkHttpClient instance - Add proguard configuration Issue #4059 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=191422594 --- extensions/okhttp/build.gradle | 3 ++- extensions/okhttp/proguard-rules.txt | 8 ++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 extensions/okhttp/proguard-rules.txt diff --git a/extensions/okhttp/build.gradle b/extensions/okhttp/build.gradle index 2da245b1a5..2f7d84d33b 100644 --- a/extensions/okhttp/build.gradle +++ b/extensions/okhttp/build.gradle @@ -21,6 +21,7 @@ android { defaultConfig { minSdkVersion project.ext.minSdkVersion targetSdkVersion project.ext.targetSdkVersion + consumerProguardFiles 'proguard-rules.txt' } lintOptions { @@ -32,7 +33,7 @@ android { dependencies { implementation project(modulePrefix + 'library-core') implementation 'com.android.support:support-annotations:' + supportLibraryVersion - implementation('com.squareup.okhttp3:okhttp:3.9.0') { + api('com.squareup.okhttp3:okhttp:3.10.0') { exclude group: 'org.json' } } diff --git a/extensions/okhttp/proguard-rules.txt b/extensions/okhttp/proguard-rules.txt new file mode 100644 index 0000000000..50d774a509 --- /dev/null +++ b/extensions/okhttp/proguard-rules.txt @@ -0,0 +1,8 @@ +# Proguard rules specific to the OkHttp extension. + +# Options specified by https://github.com/square/okhttp/blob/master/README.md +-dontwarn okhttp3.** +-dontwarn okio.** +-dontwarn javax.annotation.** +-dontwarn org.conscrypt.** +-keepnames class okhttp3.internal.publicsuffix.PublicSuffixDatabase From 738b519cef56925720a2386c4d288bf31f1e7216 Mon Sep 17 00:00:00 2001 From: olly Date: Tue, 3 Apr 2018 04:21:03 -0700 Subject: [PATCH 045/157] Add proguard configuration for Cast extension ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=191422866 --- extensions/cast/build.gradle | 1 + extensions/cast/proguard-rules.txt | 4 ++++ 2 files changed, 5 insertions(+) create mode 100644 extensions/cast/proguard-rules.txt diff --git a/extensions/cast/build.gradle b/extensions/cast/build.gradle index 8780b961d6..d7e99573cb 100644 --- a/extensions/cast/build.gradle +++ b/extensions/cast/build.gradle @@ -21,6 +21,7 @@ android { defaultConfig { minSdkVersion 14 targetSdkVersion project.ext.targetSdkVersion + consumerProguardFiles 'proguard-rules.txt' } } diff --git a/extensions/cast/proguard-rules.txt b/extensions/cast/proguard-rules.txt new file mode 100644 index 0000000000..bc94b33c1c --- /dev/null +++ b/extensions/cast/proguard-rules.txt @@ -0,0 +1,4 @@ +# Proguard rules specific to the Cast extension. + +# DefaultCastOptionsProvider is commonly referred to only by the app's manifest. +-keep class com.google.android.exoplayer2.ext.cast.DefaultCastOptionsProvider From e98209260af13c53d1258fe0b1750436d0d01c14 Mon Sep 17 00:00:00 2001 From: olly Date: Tue, 3 Apr 2018 08:17:46 -0700 Subject: [PATCH 046/157] Add proguard configuration for Cast demo app ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=191442704 --- demos/cast/build.gradle | 5 ++++- demos/cast/proguard-rules.txt | 6 ++++++ 2 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 demos/cast/proguard-rules.txt diff --git a/demos/cast/build.gradle b/demos/cast/build.gradle index c928d0e46e..ae6bdd1d94 100644 --- a/demos/cast/build.gradle +++ b/demos/cast/build.gradle @@ -29,7 +29,10 @@ android { release { shrinkResources true minifyEnabled true - proguardFiles getDefaultProguardFile('proguard-android.txt') + proguardFiles = [ + "proguard-rules.txt", + getDefaultProguardFile('proguard-android.txt') + ] } debug { jniDebuggable = true diff --git a/demos/cast/proguard-rules.txt b/demos/cast/proguard-rules.txt new file mode 100644 index 0000000000..3221818080 --- /dev/null +++ b/demos/cast/proguard-rules.txt @@ -0,0 +1,6 @@ +# Proguard rules specific to the Cast demo app. + +# Accessed via menu.xml +-keep class android.support.v7.app.MediaRouteActionProvider { + *; +} From ccddacc55b8e9711237d4145aeba5e9f9c209e79 Mon Sep 17 00:00:00 2001 From: olly Date: Wed, 4 Apr 2018 01:55:21 -0700 Subject: [PATCH 047/157] Bump version to 2.7.3 and update release notes ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=191561921 --- RELEASENOTES.md | 5 +++++ constants.gradle | 4 ++-- .../com/google/android/exoplayer2/ExoPlayerLibraryInfo.java | 6 +++--- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 3148cf0779..884cfe6bc2 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -1,5 +1,10 @@ # Release notes # +### 2.7.3 ### + +* Fix ProGuard configuration for Cast, IMA and OkHttp extensions. +* Update OkHttp extension to depend on OkHttp 3.10.0. + ### 2.7.2 ### * Gradle: Upgrade Gradle version from 4.1 to 4.4 so it can work with Android diff --git a/constants.gradle b/constants.gradle index 61b2e44b27..32210bc95c 100644 --- a/constants.gradle +++ b/constants.gradle @@ -13,8 +13,8 @@ // limitations under the License. project.ext { // ExoPlayer version and version code. - releaseVersion = '2.7.2' - releaseVersionCode = 2702 + releaseVersion = '2.7.3' + releaseVersionCode = 2703 // Important: ExoPlayer specifies a minSdkVersion of 14 because various // components provided by the library may be of use on older devices. // However, please note that the core media playback functionality provided diff --git a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java index bb3732fb94..e91495227e 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java @@ -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.7.2"; + public static final String VERSION = "2.7.3"; /** 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.7.2"; + public static final String VERSION_SLASHY = "ExoPlayerLib/2.7.3"; /** * 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 = 2007002; + public static final int VERSION_INT = 2007003; /** * Whether the library was compiled with {@link com.google.android.exoplayer2.util.Assertions} From 45ed7c304cf89735943a2441c2465012b74bdb6b Mon Sep 17 00:00:00 2001 From: olly Date: Mon, 16 Apr 2018 04:08:13 -0700 Subject: [PATCH 048/157] Update moe equivalence ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=193016758 --- .../src/main/res/values-af/strings.xml | 23 ++-------- .../src/main/res/values-am/strings.xml | 23 ++-------- .../src/main/res/values-ar/strings.xml | 23 ++-------- .../src/main/res/values-b+sr+Latn/strings.xml | 23 ++-------- .../src/main/res/values-bg/strings.xml | 23 ++-------- .../src/main/res/values-ca/strings.xml | 23 ++-------- .../src/main/res/values-cs/strings.xml | 23 ++-------- .../src/main/res/values-da/strings.xml | 23 ++-------- .../src/main/res/values-de/strings.xml | 23 ++-------- .../src/main/res/values-el/strings.xml | 23 ++-------- .../src/main/res/values-en-rAU/strings.xml | 23 ++-------- .../src/main/res/values-en-rGB/strings.xml | 23 ++-------- .../src/main/res/values-en-rIN/strings.xml | 23 ++-------- .../src/main/res/values-es-rUS/strings.xml | 23 ++-------- .../src/main/res/values-es/strings.xml | 23 ++-------- .../src/main/res/values-fa/strings.xml | 23 ++-------- .../src/main/res/values-fi/strings.xml | 23 ++-------- .../src/main/res/values-fr-rCA/strings.xml | 23 ++-------- .../src/main/res/values-fr/strings.xml | 23 ++-------- .../src/main/res/values-hi/strings.xml | 23 ++-------- .../src/main/res/values-hr/strings.xml | 23 ++-------- .../src/main/res/values-hu/strings.xml | 23 ++-------- .../src/main/res/values-in/strings.xml | 23 ++-------- .../src/main/res/values-it/strings.xml | 23 ++-------- .../src/main/res/values-iw/strings.xml | 23 ++-------- .../src/main/res/values-ja/strings.xml | 23 ++-------- .../src/main/res/values-ko/strings.xml | 23 ++-------- .../src/main/res/values-lt/strings.xml | 23 ++-------- .../src/main/res/values-lv/strings.xml | 23 ++-------- .../src/main/res/values-nb/strings.xml | 23 ++-------- .../src/main/res/values-nl/strings.xml | 23 ++-------- .../src/main/res/values-pl/strings.xml | 23 ++-------- .../src/main/res/values-pt-rPT/strings.xml | 23 ++-------- .../src/main/res/values-pt/strings.xml | 23 ++-------- .../src/main/res/values-ro/strings.xml | 23 ++-------- .../src/main/res/values-ru/strings.xml | 23 ++-------- .../src/main/res/values-sk/strings.xml | 23 ++-------- .../src/main/res/values-sl/strings.xml | 23 ++-------- .../src/main/res/values-sr/strings.xml | 20 ++------- .../src/main/res/values-sv/strings.xml | 23 ++-------- .../src/main/res/values-sw/strings.xml | 23 ++-------- .../src/main/res/values-th/strings.xml | 23 ++-------- .../src/main/res/values-tl/strings.xml | 23 ++-------- .../src/main/res/values-tr/strings.xml | 23 ++-------- .../src/main/res/values-uk/strings.xml | 23 ++-------- .../src/main/res/values-vi/strings.xml | 23 ++-------- .../src/main/res/values-zh-rCN/strings.xml | 23 ++-------- .../src/main/res/values-zh-rHK/strings.xml | 23 ++-------- .../src/main/res/values-zh-rTW/strings.xml | 23 ++-------- .../src/main/res/values-zu/strings.xml | 23 ++-------- .../exoplayer2/offline/DownloadService.java | 1 - library/ui/src/main/res/values-af/strings.xml | 41 +++++++---------- library/ui/src/main/res/values-am/strings.xml | 43 +++++++----------- library/ui/src/main/res/values-ar/strings.xml | 41 +++++++---------- .../src/main/res/values-b+sr+Latn/strings.xml | 44 +++++++------------ library/ui/src/main/res/values-bg/strings.xml | 44 +++++++------------ library/ui/src/main/res/values-ca/strings.xml | 44 +++++++------------ library/ui/src/main/res/values-cs/strings.xml | 44 +++++++------------ library/ui/src/main/res/values-da/strings.xml | 44 +++++++------------ library/ui/src/main/res/values-de/strings.xml | 41 +++++++---------- library/ui/src/main/res/values-el/strings.xml | 41 +++++++---------- .../ui/src/main/res/values-en-rAU/strings.xml | 44 +++++++------------ .../ui/src/main/res/values-en-rGB/strings.xml | 43 +++++++----------- .../ui/src/main/res/values-en-rIN/strings.xml | 43 +++++++----------- .../ui/src/main/res/values-es-rUS/strings.xml | 44 +++++++------------ library/ui/src/main/res/values-es/strings.xml | 41 +++++++---------- library/ui/src/main/res/values-fa/strings.xml | 43 +++++++----------- library/ui/src/main/res/values-fi/strings.xml | 44 +++++++------------ .../ui/src/main/res/values-fr-rCA/strings.xml | 44 +++++++------------ library/ui/src/main/res/values-fr/strings.xml | 41 +++++++---------- library/ui/src/main/res/values-hi/strings.xml | 43 +++++++----------- library/ui/src/main/res/values-hr/strings.xml | 41 +++++++---------- library/ui/src/main/res/values-hu/strings.xml | 41 +++++++---------- library/ui/src/main/res/values-in/strings.xml | 44 +++++++------------ library/ui/src/main/res/values-it/strings.xml | 41 +++++++---------- library/ui/src/main/res/values-iw/strings.xml | 44 +++++++------------ library/ui/src/main/res/values-ja/strings.xml | 43 +++++++----------- library/ui/src/main/res/values-ko/strings.xml | 43 +++++++----------- library/ui/src/main/res/values-lt/strings.xml | 41 +++++++---------- library/ui/src/main/res/values-lv/strings.xml | 41 +++++++---------- library/ui/src/main/res/values-nb/strings.xml | 44 +++++++------------ library/ui/src/main/res/values-nl/strings.xml | 44 +++++++------------ library/ui/src/main/res/values-pl/strings.xml | 41 +++++++---------- .../ui/src/main/res/values-pt-rPT/strings.xml | 41 +++++++---------- library/ui/src/main/res/values-pt/strings.xml | 44 +++++++------------ library/ui/src/main/res/values-ro/strings.xml | 44 +++++++------------ library/ui/src/main/res/values-ru/strings.xml | 44 +++++++------------ library/ui/src/main/res/values-sk/strings.xml | 41 +++++++---------- library/ui/src/main/res/values-sl/strings.xml | 44 +++++++------------ library/ui/src/main/res/values-sr/strings.xml | 41 +++++++---------- library/ui/src/main/res/values-sv/strings.xml | 41 +++++++---------- library/ui/src/main/res/values-sw/strings.xml | 43 +++++++----------- library/ui/src/main/res/values-th/strings.xml | 41 +++++++---------- library/ui/src/main/res/values-tl/strings.xml | 41 +++++++---------- library/ui/src/main/res/values-tr/strings.xml | 44 +++++++------------ library/ui/src/main/res/values-uk/strings.xml | 41 +++++++---------- library/ui/src/main/res/values-vi/strings.xml | 41 +++++++---------- .../ui/src/main/res/values-zh-rCN/strings.xml | 41 +++++++---------- .../ui/src/main/res/values-zh-rHK/strings.xml | 41 +++++++---------- .../ui/src/main/res/values-zh-rTW/strings.xml | 41 +++++++---------- library/ui/src/main/res/values-zu/strings.xml | 43 +++++++----------- 101 files changed, 995 insertions(+), 2275 deletions(-) diff --git a/extensions/mediasession/src/main/res/values-af/strings.xml b/extensions/mediasession/src/main/res/values-af/strings.xml index 4ef78cd84f..92d171cfdc 100644 --- a/extensions/mediasession/src/main/res/values-af/strings.xml +++ b/extensions/mediasession/src/main/res/values-af/strings.xml @@ -1,21 +1,6 @@ - - + - "Herhaal alles" - "Herhaal niks" - "Herhaal een" + Herhaal niks + Herhaal een + Herhaal alles diff --git a/extensions/mediasession/src/main/res/values-am/strings.xml b/extensions/mediasession/src/main/res/values-am/strings.xml index 531f605584..54509a65ab 100644 --- a/extensions/mediasession/src/main/res/values-am/strings.xml +++ b/extensions/mediasession/src/main/res/values-am/strings.xml @@ -1,21 +1,6 @@ - - + - "ሁሉንም ድገም" - "ምንም አትድገም" - "አንዱን ድገም" + ምንም አትድገም + አንድ ድገም + ሁሉንም ድገም diff --git a/extensions/mediasession/src/main/res/values-ar/strings.xml b/extensions/mediasession/src/main/res/values-ar/strings.xml index 0101a746e0..707ad41a16 100644 --- a/extensions/mediasession/src/main/res/values-ar/strings.xml +++ b/extensions/mediasession/src/main/res/values-ar/strings.xml @@ -1,21 +1,6 @@ - - + - "تكرار الكل" - "عدم التكرار" - "تكرار مقطع واحد" + عدم التكرار + تكرار مقطع صوتي واحد + تكرار الكل diff --git a/extensions/mediasession/src/main/res/values-b+sr+Latn/strings.xml b/extensions/mediasession/src/main/res/values-b+sr+Latn/strings.xml index 67a51cf85e..dcdcb9d977 100644 --- a/extensions/mediasession/src/main/res/values-b+sr+Latn/strings.xml +++ b/extensions/mediasession/src/main/res/values-b+sr+Latn/strings.xml @@ -1,21 +1,6 @@ - - + - "Ponovi sve" - "Ne ponavljaj nijednu" - "Ponovi jednu" + Ne ponavljaj nijednu + Ponovi jednu + Ponovi sve diff --git a/extensions/mediasession/src/main/res/values-bg/strings.xml b/extensions/mediasession/src/main/res/values-bg/strings.xml index 16910d640a..8a639c6cff 100644 --- a/extensions/mediasession/src/main/res/values-bg/strings.xml +++ b/extensions/mediasession/src/main/res/values-bg/strings.xml @@ -1,21 +1,6 @@ - - + - "Повтаряне на всички" - "Без повтаряне" - "Повтаряне на един елемент" + Без повтаряне + Повтаряне на един елемент + Повтаряне на всички diff --git a/extensions/mediasession/src/main/res/values-ca/strings.xml b/extensions/mediasession/src/main/res/values-ca/strings.xml index 89414d736e..cdb41b2b0a 100644 --- a/extensions/mediasession/src/main/res/values-ca/strings.xml +++ b/extensions/mediasession/src/main/res/values-ca/strings.xml @@ -1,21 +1,6 @@ - - + - "Repeteix-ho tot" - "No en repeteixis cap" - "Repeteix-ne un" + No en repeteixis cap + Repeteix una + Repeteix tot diff --git a/extensions/mediasession/src/main/res/values-cs/strings.xml b/extensions/mediasession/src/main/res/values-cs/strings.xml index 784d872570..4d25b3a3ba 100644 --- a/extensions/mediasession/src/main/res/values-cs/strings.xml +++ b/extensions/mediasession/src/main/res/values-cs/strings.xml @@ -1,21 +1,6 @@ - - + - "Opakovat vše" - "Neopakovat" - "Opakovat jednu položku" + Neopakovat + Opakovat jednu + Opakovat vše diff --git a/extensions/mediasession/src/main/res/values-da/strings.xml b/extensions/mediasession/src/main/res/values-da/strings.xml index 2c9784d122..f74409a50b 100644 --- a/extensions/mediasession/src/main/res/values-da/strings.xml +++ b/extensions/mediasession/src/main/res/values-da/strings.xml @@ -1,21 +1,6 @@ - - + - "Gentag alle" - "Gentag ingen" - "Gentag en" + Gentag ingen + Gentag én + Gentag alle diff --git a/extensions/mediasession/src/main/res/values-de/strings.xml b/extensions/mediasession/src/main/res/values-de/strings.xml index c11e449665..af3564cb41 100644 --- a/extensions/mediasession/src/main/res/values-de/strings.xml +++ b/extensions/mediasession/src/main/res/values-de/strings.xml @@ -1,21 +1,6 @@ - - + - "Alle wiederholen" - "Keinen Titel wiederholen" - "Einen Titel wiederholen" + Keinen wiederholen + Einen wiederholen + Alle wiederholen diff --git a/extensions/mediasession/src/main/res/values-el/strings.xml b/extensions/mediasession/src/main/res/values-el/strings.xml index 6279af5d64..e4f6666622 100644 --- a/extensions/mediasession/src/main/res/values-el/strings.xml +++ b/extensions/mediasession/src/main/res/values-el/strings.xml @@ -1,21 +1,6 @@ - - + - "Επανάληψη όλων" - "Καμία επανάληψη" - "Επανάληψη ενός στοιχείου" + Καμία επανάληψη + Επανάληψη ενός κομματιού + Επανάληψη όλων diff --git a/extensions/mediasession/src/main/res/values-en-rAU/strings.xml b/extensions/mediasession/src/main/res/values-en-rAU/strings.xml index a3fccf8b52..4170902688 100644 --- a/extensions/mediasession/src/main/res/values-en-rAU/strings.xml +++ b/extensions/mediasession/src/main/res/values-en-rAU/strings.xml @@ -1,21 +1,6 @@ - - + - "Repeat all" - "Repeat none" - "Repeat one" + Repeat none + Repeat one + Repeat all diff --git a/extensions/mediasession/src/main/res/values-en-rGB/strings.xml b/extensions/mediasession/src/main/res/values-en-rGB/strings.xml index a3fccf8b52..4170902688 100644 --- a/extensions/mediasession/src/main/res/values-en-rGB/strings.xml +++ b/extensions/mediasession/src/main/res/values-en-rGB/strings.xml @@ -1,21 +1,6 @@ - - + - "Repeat all" - "Repeat none" - "Repeat one" + Repeat none + Repeat one + Repeat all diff --git a/extensions/mediasession/src/main/res/values-en-rIN/strings.xml b/extensions/mediasession/src/main/res/values-en-rIN/strings.xml index a3fccf8b52..4170902688 100644 --- a/extensions/mediasession/src/main/res/values-en-rIN/strings.xml +++ b/extensions/mediasession/src/main/res/values-en-rIN/strings.xml @@ -1,21 +1,6 @@ - - + - "Repeat all" - "Repeat none" - "Repeat one" + Repeat none + Repeat one + Repeat all diff --git a/extensions/mediasession/src/main/res/values-es-rUS/strings.xml b/extensions/mediasession/src/main/res/values-es-rUS/strings.xml index 0fe29d3d5a..700e6de4e2 100644 --- a/extensions/mediasession/src/main/res/values-es-rUS/strings.xml +++ b/extensions/mediasession/src/main/res/values-es-rUS/strings.xml @@ -1,21 +1,6 @@ - - + - "Repetir todo" - "No repetir" - "Repetir uno" + No repetir + Repetir uno + Repetir todo diff --git a/extensions/mediasession/src/main/res/values-es/strings.xml b/extensions/mediasession/src/main/res/values-es/strings.xml index 0fe29d3d5a..700e6de4e2 100644 --- a/extensions/mediasession/src/main/res/values-es/strings.xml +++ b/extensions/mediasession/src/main/res/values-es/strings.xml @@ -1,21 +1,6 @@ - - + - "Repetir todo" - "No repetir" - "Repetir uno" + No repetir + Repetir uno + Repetir todo diff --git a/extensions/mediasession/src/main/res/values-fa/strings.xml b/extensions/mediasession/src/main/res/values-fa/strings.xml index e37a08de64..96e8a1e819 100644 --- a/extensions/mediasession/src/main/res/values-fa/strings.xml +++ b/extensions/mediasession/src/main/res/values-fa/strings.xml @@ -1,21 +1,6 @@ - - + - "تکرار همه" - "تکرار هیچ‌کدام" - "یک‌بار تکرار" + تکرار هیچ‌کدام + یکبار تکرار + تکرار همه diff --git a/extensions/mediasession/src/main/res/values-fi/strings.xml b/extensions/mediasession/src/main/res/values-fi/strings.xml index c920827976..db1aca3f5c 100644 --- a/extensions/mediasession/src/main/res/values-fi/strings.xml +++ b/extensions/mediasession/src/main/res/values-fi/strings.xml @@ -1,21 +1,6 @@ - - + - "Toista kaikki" - "Toista ei mitään" - "Toista yksi" + Ei uudelleentoistoa + Toista yksi uudelleen + Toista kaikki uudelleen diff --git a/extensions/mediasession/src/main/res/values-fr-rCA/strings.xml b/extensions/mediasession/src/main/res/values-fr-rCA/strings.xml index c5191e74a9..17e17fc8b5 100644 --- a/extensions/mediasession/src/main/res/values-fr-rCA/strings.xml +++ b/extensions/mediasession/src/main/res/values-fr-rCA/strings.xml @@ -1,21 +1,6 @@ - - + - "Tout lire en boucle" - "Aucune répétition" - "Répéter un élément" + Ne rien lire en boucle + Lire une chanson en boucle + Tout lire en boucle diff --git a/extensions/mediasession/src/main/res/values-fr/strings.xml b/extensions/mediasession/src/main/res/values-fr/strings.xml index 1d76358d1f..9e35e35a0c 100644 --- a/extensions/mediasession/src/main/res/values-fr/strings.xml +++ b/extensions/mediasession/src/main/res/values-fr/strings.xml @@ -1,21 +1,6 @@ - - + - "Tout lire en boucle" - "Ne rien lire en boucle" - "Lire en boucle un élément" + Ne rien lire en boucle + Lire un titre en boucle + Tout lire en boucle diff --git a/extensions/mediasession/src/main/res/values-hi/strings.xml b/extensions/mediasession/src/main/res/values-hi/strings.xml index 8ce336d5e5..66415ed45d 100644 --- a/extensions/mediasession/src/main/res/values-hi/strings.xml +++ b/extensions/mediasession/src/main/res/values-hi/strings.xml @@ -1,21 +1,6 @@ - - + - "सभी को दोहराएं" - "कुछ भी न दोहराएं" - "एक दोहराएं" + किसी को न दोहराएं + एक को दोहराएं + सभी को दोहराएं diff --git a/extensions/mediasession/src/main/res/values-hr/strings.xml b/extensions/mediasession/src/main/res/values-hr/strings.xml index 9f995ec15b..3b3f8170db 100644 --- a/extensions/mediasession/src/main/res/values-hr/strings.xml +++ b/extensions/mediasession/src/main/res/values-hr/strings.xml @@ -1,21 +1,6 @@ - - + - "Ponovi sve" - "Bez ponavljanja" - "Ponovi jedno" + Bez ponavljanja + Ponovi jedno + Ponovi sve diff --git a/extensions/mediasession/src/main/res/values-hu/strings.xml b/extensions/mediasession/src/main/res/values-hu/strings.xml index 2335ade72e..392959a462 100644 --- a/extensions/mediasession/src/main/res/values-hu/strings.xml +++ b/extensions/mediasession/src/main/res/values-hu/strings.xml @@ -1,21 +1,6 @@ - - + - "Összes ismétlése" - "Nincs ismétlés" - "Egy ismétlése" + Nincs ismétlés + Egy szám ismétlése + Összes szám ismétlése diff --git a/extensions/mediasession/src/main/res/values-in/strings.xml b/extensions/mediasession/src/main/res/values-in/strings.xml index 093a7f8576..1388877293 100644 --- a/extensions/mediasession/src/main/res/values-in/strings.xml +++ b/extensions/mediasession/src/main/res/values-in/strings.xml @@ -1,21 +1,6 @@ - - + - "Ulangi Semua" - "Jangan Ulangi" - "Ulangi Satu" + Jangan ulangi + Ulangi 1 + Ulangi semua diff --git a/extensions/mediasession/src/main/res/values-it/strings.xml b/extensions/mediasession/src/main/res/values-it/strings.xml index c0682519f9..8922453204 100644 --- a/extensions/mediasession/src/main/res/values-it/strings.xml +++ b/extensions/mediasession/src/main/res/values-it/strings.xml @@ -1,21 +1,6 @@ - - + - "Ripeti tutti" - "Non ripetere nessuno" - "Ripeti uno" + Non ripetere nulla + Ripeti uno + Ripeti tutto diff --git a/extensions/mediasession/src/main/res/values-iw/strings.xml b/extensions/mediasession/src/main/res/values-iw/strings.xml index 5cf23d5a4c..193a3ac606 100644 --- a/extensions/mediasession/src/main/res/values-iw/strings.xml +++ b/extensions/mediasession/src/main/res/values-iw/strings.xml @@ -1,21 +1,6 @@ - - + - "חזור על הכל" - "אל תחזור על כלום" - "חזור על פריט אחד" + אל תחזור על אף פריט + חזור על פריט אחד + חזור על הכול diff --git a/extensions/mediasession/src/main/res/values-ja/strings.xml b/extensions/mediasession/src/main/res/values-ja/strings.xml index 6f543fbdee..d1cd378d53 100644 --- a/extensions/mediasession/src/main/res/values-ja/strings.xml +++ b/extensions/mediasession/src/main/res/values-ja/strings.xml @@ -1,21 +1,6 @@ - - + - "全曲を繰り返し" - "繰り返しなし" - "1曲を繰り返し" + リピートなし + 1 曲をリピート + 全曲をリピート diff --git a/extensions/mediasession/src/main/res/values-ko/strings.xml b/extensions/mediasession/src/main/res/values-ko/strings.xml index d269937771..b561abc1d7 100644 --- a/extensions/mediasession/src/main/res/values-ko/strings.xml +++ b/extensions/mediasession/src/main/res/values-ko/strings.xml @@ -1,21 +1,6 @@ - - + - "전체 반복" - "반복 안함" - "한 항목 반복" + 반복 안함 + 현재 미디어 반복 + 모두 반복 diff --git a/extensions/mediasession/src/main/res/values-lt/strings.xml b/extensions/mediasession/src/main/res/values-lt/strings.xml index ae8f1cf8c3..20eb0e9b1f 100644 --- a/extensions/mediasession/src/main/res/values-lt/strings.xml +++ b/extensions/mediasession/src/main/res/values-lt/strings.xml @@ -1,21 +1,6 @@ - - + - "Kartoti viską" - "Nekartoti nieko" - "Kartoti vieną" + Nekartoti nieko + Kartoti vieną + Kartoti viską diff --git a/extensions/mediasession/src/main/res/values-lv/strings.xml b/extensions/mediasession/src/main/res/values-lv/strings.xml index a69f6a0ad5..44cddec124 100644 --- a/extensions/mediasession/src/main/res/values-lv/strings.xml +++ b/extensions/mediasession/src/main/res/values-lv/strings.xml @@ -1,21 +1,6 @@ - - + - "Atkārtot visu" - "Neatkārtot nevienu" - "Atkārtot vienu" + Neatkārtot nevienu + Atkārtot vienu + Atkārtot visu diff --git a/extensions/mediasession/src/main/res/values-nb/strings.xml b/extensions/mediasession/src/main/res/values-nb/strings.xml index 10f334b226..eab972792f 100644 --- a/extensions/mediasession/src/main/res/values-nb/strings.xml +++ b/extensions/mediasession/src/main/res/values-nb/strings.xml @@ -1,21 +1,6 @@ - - + - "Gjenta alle" - "Ikke gjenta noen" - "Gjenta én" + Ikke gjenta noen + Gjenta én + Gjenta alle diff --git a/extensions/mediasession/src/main/res/values-nl/strings.xml b/extensions/mediasession/src/main/res/values-nl/strings.xml index 55997be098..b1309f40d6 100644 --- a/extensions/mediasession/src/main/res/values-nl/strings.xml +++ b/extensions/mediasession/src/main/res/values-nl/strings.xml @@ -1,21 +1,6 @@ - - + - "Alles herhalen" - "Niet herhalen" - "Eén herhalen" + Niets herhalen + Eén herhalen + Alles herhalen diff --git a/extensions/mediasession/src/main/res/values-pl/strings.xml b/extensions/mediasession/src/main/res/values-pl/strings.xml index 6a52d58b63..5654c0f095 100644 --- a/extensions/mediasession/src/main/res/values-pl/strings.xml +++ b/extensions/mediasession/src/main/res/values-pl/strings.xml @@ -1,21 +1,6 @@ - - + - "Powtórz wszystkie" - "Nie powtarzaj" - "Powtórz jeden" + Nie powtarzaj + Powtórz jeden + Powtórz wszystkie diff --git a/extensions/mediasession/src/main/res/values-pt-rPT/strings.xml b/extensions/mediasession/src/main/res/values-pt-rPT/strings.xml index efb8fc433f..612be4b8f4 100644 --- a/extensions/mediasession/src/main/res/values-pt-rPT/strings.xml +++ b/extensions/mediasession/src/main/res/values-pt-rPT/strings.xml @@ -1,21 +1,6 @@ - - + - "Repetir tudo" - "Não repetir" - "Repetir um" + Não repetir nenhum + Repetir um + Repetir tudo diff --git a/extensions/mediasession/src/main/res/values-pt/strings.xml b/extensions/mediasession/src/main/res/values-pt/strings.xml index aadebbb3b0..a858ea4fc6 100644 --- a/extensions/mediasession/src/main/res/values-pt/strings.xml +++ b/extensions/mediasession/src/main/res/values-pt/strings.xml @@ -1,21 +1,6 @@ - - + - "Repetir tudo" - "Não repetir" - "Repetir uma" + Não repetir + Repetir uma + Repetir tudo diff --git a/extensions/mediasession/src/main/res/values-ro/strings.xml b/extensions/mediasession/src/main/res/values-ro/strings.xml index f6aee447e5..a88088fb0c 100644 --- a/extensions/mediasession/src/main/res/values-ro/strings.xml +++ b/extensions/mediasession/src/main/res/values-ro/strings.xml @@ -1,21 +1,6 @@ - - + - "Repetați toate" - "Repetați niciuna" - "Repetați unul" + Nu repetați niciunul + Repetați unul + Repetați-le pe toate diff --git a/extensions/mediasession/src/main/res/values-ru/strings.xml b/extensions/mediasession/src/main/res/values-ru/strings.xml index 575ad9f930..f350724813 100644 --- a/extensions/mediasession/src/main/res/values-ru/strings.xml +++ b/extensions/mediasession/src/main/res/values-ru/strings.xml @@ -1,21 +1,6 @@ - - + - "Повторять все" - "Не повторять" - "Повторять один элемент" + Не повторять + Повторять трек + Повторять все diff --git a/extensions/mediasession/src/main/res/values-sk/strings.xml b/extensions/mediasession/src/main/res/values-sk/strings.xml index 5d092003e5..9c0235daec 100644 --- a/extensions/mediasession/src/main/res/values-sk/strings.xml +++ b/extensions/mediasession/src/main/res/values-sk/strings.xml @@ -1,21 +1,6 @@ - - + - "Opakovať všetko" - "Neopakovať" - "Opakovať jednu položku" + Neopakovať + Opakovať jednu + Opakovať všetko diff --git a/extensions/mediasession/src/main/res/values-sl/strings.xml b/extensions/mediasession/src/main/res/values-sl/strings.xml index ecac3800c8..9ee3add8bc 100644 --- a/extensions/mediasession/src/main/res/values-sl/strings.xml +++ b/extensions/mediasession/src/main/res/values-sl/strings.xml @@ -1,21 +1,6 @@ - - + - "Ponovi vse" - "Ne ponovi" - "Ponovi eno" + Brez ponavljanja + Ponavljanje ene + Ponavljanje vseh diff --git a/extensions/mediasession/src/main/res/values-sr/strings.xml b/extensions/mediasession/src/main/res/values-sr/strings.xml index 881cb2703b..71edd5c341 100644 --- a/extensions/mediasession/src/main/res/values-sr/strings.xml +++ b/extensions/mediasession/src/main/res/values-sr/strings.xml @@ -1,18 +1,6 @@ - - + + Не понављај ниједну + Понови једну + Понови све diff --git a/extensions/mediasession/src/main/res/values-sv/strings.xml b/extensions/mediasession/src/main/res/values-sv/strings.xml index 3a7bb630aa..0956ac9fc7 100644 --- a/extensions/mediasession/src/main/res/values-sv/strings.xml +++ b/extensions/mediasession/src/main/res/values-sv/strings.xml @@ -1,21 +1,6 @@ - - + - "Upprepa alla" - "Upprepa inga" - "Upprepa en" + Upprepa inga + Upprepa en + Upprepa alla diff --git a/extensions/mediasession/src/main/res/values-sw/strings.xml b/extensions/mediasession/src/main/res/values-sw/strings.xml index 726012ab88..0010774a6f 100644 --- a/extensions/mediasession/src/main/res/values-sw/strings.xml +++ b/extensions/mediasession/src/main/res/values-sw/strings.xml @@ -1,21 +1,6 @@ - - + - "Rudia zote" - "Usirudie Yoyote" - "Rudia Moja" + Usirudie yoyote + Rudia moja + Rudia zote diff --git a/extensions/mediasession/src/main/res/values-th/strings.xml b/extensions/mediasession/src/main/res/values-th/strings.xml index af502b3a4c..bec0410a44 100644 --- a/extensions/mediasession/src/main/res/values-th/strings.xml +++ b/extensions/mediasession/src/main/res/values-th/strings.xml @@ -1,21 +1,6 @@ - - + - "เล่นซ้ำทั้งหมด" - "ไม่เล่นซ้ำ" - "เล่นซ้ำรายการเดียว" + ไม่เล่นซ้ำ + เล่นซ้ำเพลงเดียว + เล่นซ้ำทั้งหมด diff --git a/extensions/mediasession/src/main/res/values-tl/strings.xml b/extensions/mediasession/src/main/res/values-tl/strings.xml index 239972a4c7..6f8d8f4f88 100644 --- a/extensions/mediasession/src/main/res/values-tl/strings.xml +++ b/extensions/mediasession/src/main/res/values-tl/strings.xml @@ -1,21 +1,6 @@ - - + - "Ulitin Lahat" - "Walang Uulitin" - "Ulitin ang Isa" + Walang uulitin + Mag-ulit ng isa + Ulitin lahat diff --git a/extensions/mediasession/src/main/res/values-tr/strings.xml b/extensions/mediasession/src/main/res/values-tr/strings.xml index 89a98b1ed9..20c05d9fa6 100644 --- a/extensions/mediasession/src/main/res/values-tr/strings.xml +++ b/extensions/mediasession/src/main/res/values-tr/strings.xml @@ -1,21 +1,6 @@ - - + - "Tümünü Tekrarla" - "Hiçbirini Tekrarlama" - "Birini Tekrarla" + Hiçbirini tekrarlama + Bir şarkıyı tekrarla + Tümünü tekrarla diff --git a/extensions/mediasession/src/main/res/values-uk/strings.xml b/extensions/mediasession/src/main/res/values-uk/strings.xml index 4e1d25eb8a..44db07ef9c 100644 --- a/extensions/mediasession/src/main/res/values-uk/strings.xml +++ b/extensions/mediasession/src/main/res/values-uk/strings.xml @@ -1,21 +1,6 @@ - - + - "Повторити все" - "Не повторювати" - "Повторити один елемент" + Не повторювати + Повторити 1 + Повторити всі diff --git a/extensions/mediasession/src/main/res/values-vi/strings.xml b/extensions/mediasession/src/main/res/values-vi/strings.xml index dabc9e05d5..9de007cdb9 100644 --- a/extensions/mediasession/src/main/res/values-vi/strings.xml +++ b/extensions/mediasession/src/main/res/values-vi/strings.xml @@ -1,21 +1,6 @@ - - + - "Lặp lại tất cả" - "Không lặp lại" - "Lặp lại một mục" + Không lặp lại + Lặp lại một + Lặp lại tất cả diff --git a/extensions/mediasession/src/main/res/values-zh-rCN/strings.xml b/extensions/mediasession/src/main/res/values-zh-rCN/strings.xml index beb3403cb9..4d1f1346b9 100644 --- a/extensions/mediasession/src/main/res/values-zh-rCN/strings.xml +++ b/extensions/mediasession/src/main/res/values-zh-rCN/strings.xml @@ -1,21 +1,6 @@ - - + - "重复播放全部" - "不重复播放" - "重复播放单个视频" + 不重复播放 + 重复播放一项 + 全部重复播放 diff --git a/extensions/mediasession/src/main/res/values-zh-rHK/strings.xml b/extensions/mediasession/src/main/res/values-zh-rHK/strings.xml index 775cd6441c..e0ec62c533 100644 --- a/extensions/mediasession/src/main/res/values-zh-rHK/strings.xml +++ b/extensions/mediasession/src/main/res/values-zh-rHK/strings.xml @@ -1,21 +1,6 @@ - - + - "重複播放所有媒體項目" - "不重複播放任何媒體項目" - "重複播放一個媒體項目" + 不重複播放 + 重複播放一個 + 全部重複播放 diff --git a/extensions/mediasession/src/main/res/values-zh-rTW/strings.xml b/extensions/mediasession/src/main/res/values-zh-rTW/strings.xml index d3789f4145..5b91fbd9fe 100644 --- a/extensions/mediasession/src/main/res/values-zh-rTW/strings.xml +++ b/extensions/mediasession/src/main/res/values-zh-rTW/strings.xml @@ -1,21 +1,6 @@ - - + - "重複播放所有媒體項目" - "不重複播放" - "重複播放單一媒體項目" + 不重複播放 + 重複播放單一項目 + 重複播放所有項目 diff --git a/extensions/mediasession/src/main/res/values-zu/strings.xml b/extensions/mediasession/src/main/res/values-zu/strings.xml index 789b6fecb4..a6299ba987 100644 --- a/extensions/mediasession/src/main/res/values-zu/strings.xml +++ b/extensions/mediasession/src/main/res/values-zu/strings.xml @@ -1,21 +1,6 @@ - - + - "Phinda konke" - "Ungaphindi lutho" - "Phida okukodwa" + Phinda okungekho + Phinda okukodwa + Phinda konke diff --git a/library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadService.java b/library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadService.java index a5656ec109..0a6bc062f1 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadService.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadService.java @@ -289,7 +289,6 @@ public abstract class DownloadService extends Service implements DownloadManager @Override public void onIdle(DownloadManager downloadManager) { // Make sure startForeground is called before stopping. - // Workaround for https://buganizer.corp.google.com/issues/69424260 if (Util.SDK_INT >= 26) { Builder notificationBuilder = new Builder(this, getNotificationChannelId()); Notification foregroundNotification = notificationBuilder.build(); diff --git a/library/ui/src/main/res/values-af/strings.xml b/library/ui/src/main/res/values-af/strings.xml index f1c45cd7f7..0ede570914 100644 --- a/library/ui/src/main/res/values-af/strings.xml +++ b/library/ui/src/main/res/values-af/strings.xml @@ -1,30 +1,19 @@ - - "Vorige snit" - "Volgende snit" - "Wag" - "Speel" - "Stop" - "Spoel terug" - "Vinnig vorentoe" - "Herhaal alles" - "Herhaal niks" - "Herhaal een" - "Skommel" + Vorige snit + Volgende snit + Onderbreek + Speel + Stop + Spoel terug + Spoel vorentoe + Herhaal niks + Herhaal een + Herhaal alles + Skommel Volskermmodus + Aflaai op waglys + Laai tans af + Aflaai is voltooi + Kon nie aflaai nie diff --git a/library/ui/src/main/res/values-am/strings.xml b/library/ui/src/main/res/values-am/strings.xml index 14d3ff0242..80c4fd1fb6 100644 --- a/library/ui/src/main/res/values-am/strings.xml +++ b/library/ui/src/main/res/values-am/strings.xml @@ -1,30 +1,19 @@ - - "ቀዳሚ ትራክ" - "ቀጣይ ትራክ" - "ለአፍታ አቁም" - "አጫውት" - "አቁም" - "ወደኋላ አጠንጥን" - "በፍጥነት አሳልፍ" - "ሁሉንም ድገም" - "ምንም አትድገም" - "አንዱን ድገም" - "በው" - ባለ ሙሉ ማያ ገጽ ሁኔታ + ቀዳሚ ትራክ + ቀጣይ ትራክ + ላፍታ አቁም + አጫውት + አቁም + ወደኋላ መልስ + በፍጥነት አሳልፍ + ምንም አትድገም + አንድ ድገም + ሁሉንም ድገም + በውዝ + የሙሉ ማያ ሁነታ + ማውረድ ወረፋ ይዟል + በማውረድ ላይ + ማውረድ ተጠናቋል + ማውረድ አልተሳካም diff --git a/library/ui/src/main/res/values-ar/strings.xml b/library/ui/src/main/res/values-ar/strings.xml index 2cc56abbfa..5ccc6b3145 100644 --- a/library/ui/src/main/res/values-ar/strings.xml +++ b/library/ui/src/main/res/values-ar/strings.xml @@ -1,30 +1,19 @@ - - "المقطع الصوتي السابق" - "المقطع الصوتي التالي" - "إيقاف مؤقت" - "تشغيل" - "إيقاف" - "إرجاع" - "تقديم سريع" - "تكرار الكل" - "عدم التكرار" - "تكرار مقطع واحد" - "ترتيب عشوائي" + المقطع الصوتي السابق + المقطع الصوتي التالي + إيقاف مؤقت + تشغيل + إيقاف + إرجاع + تقديم سريع + عدم التكرار + تكرار مقطع صوتي واحد + تكرار الكل + ترتيب عشوائي وضع ملء الشاشة + التنزيل قيد الانتظار + تحميل + اكتمل التنزيل + تعذّر التنزيل diff --git a/library/ui/src/main/res/values-b+sr+Latn/strings.xml b/library/ui/src/main/res/values-b+sr+Latn/strings.xml index a9d35e5cb6..be0223898b 100644 --- a/library/ui/src/main/res/values-b+sr+Latn/strings.xml +++ b/library/ui/src/main/res/values-b+sr+Latn/strings.xml @@ -1,29 +1,19 @@ - - + - "Prethodna pesma" - "Sledeća pesma" - "Pauza" - "Pusti" - "Zaustavi" - "Premotaj unazad" - "Premotaj unapred" - "Ponovi sve" - "Ne ponavljaj nijednu" - "Ponovi jednu" - "Pusti nasumično" + Prethodna pesma + Sledeća pesma + Pauziraj + Pusti + Zaustavi + Premotaj unazad + Premotaj unapred + Ne ponavljaj nijednu + Ponovi jednu + Ponovi sve + Pusti nasumično + Režim celog ekrana + Preuzimanje je na čekanju + Preuzimanje + Preuzimanje je završeno + Preuzimanje nije uspelo diff --git a/library/ui/src/main/res/values-bg/strings.xml b/library/ui/src/main/res/values-bg/strings.xml index e350479788..14cc2c8fcc 100644 --- a/library/ui/src/main/res/values-bg/strings.xml +++ b/library/ui/src/main/res/values-bg/strings.xml @@ -1,29 +1,19 @@ - - + - "Предишен запис" - "Следващ запис" - "Пауза" - "Пускане" - "Спиране" - "Превъртане назад" - "Превъртане напред" - "Повтаряне на всички" - "Без повтаряне" - "Повтаряне на един елемент" - "Разбъркване" + Предишен запис + Следващ запис + Поставяне на пауза + Възпроизвеждане + Спиране + Превъртане назад + Превъртане напред + Без повтаряне + Повтаряне на един елемент + Повтаряне на всички + Разбъркване + Режим на цял екран + Изтеглянето е в опашката + Изтегля се + Изтеглянето завърши + Изтеглянето не бе успешно diff --git a/library/ui/src/main/res/values-ca/strings.xml b/library/ui/src/main/res/values-ca/strings.xml index fd76a8e08e..31fc1c59b3 100644 --- a/library/ui/src/main/res/values-ca/strings.xml +++ b/library/ui/src/main/res/values-ca/strings.xml @@ -1,29 +1,19 @@ - - + - "Ruta anterior" - "Ruta següent" - "Posa en pausa" - "Reprodueix" - "Atura" - "Rebobina" - "Avança ràpidament" - "Repeteix-ho tot" - "No en repeteixis cap" - "Repeteix-ne un" - "Reprodueix aleatòriament" + Pista anterior + Pista següent + Posa en pausa + Reprodueix + Atura + Rebobina + Avança ràpidament + No en repeteixis cap + Repeteix una + Repeteix tot + Reprodueix aleatòriament + Mode de pantalla completa + La baixada s\'ha posat a la cua + S\'està baixant + S\'ha completat la baixada + No s\'ha pogut baixar diff --git a/library/ui/src/main/res/values-cs/strings.xml b/library/ui/src/main/res/values-cs/strings.xml index 087ab79c25..75d5f6c1c1 100644 --- a/library/ui/src/main/res/values-cs/strings.xml +++ b/library/ui/src/main/res/values-cs/strings.xml @@ -1,29 +1,19 @@ - - + - "Předchozí skladba" - "Další skladba" - "Pozastavit" - "Přehrát" - "Zastavit" - "Přetočit zpět" - "Přetočit vpřed" - "Opakovat vše" - "Neopakovat" - "Opakovat jednu položku" - "Náhodně" + Předchozí skladba + Další skladba + Pozastavit + Přehrát + Zastavit + Přetočit zpět + Rychle vpřed + Neopakovat + Opakovat jednu + Opakovat vše + Náhodně + Režim celé obrazovky + Zařazeno do fronty stahování + Stahování + Stahování bylo dokončeno + Stažení se nezdařilo diff --git a/library/ui/src/main/res/values-da/strings.xml b/library/ui/src/main/res/values-da/strings.xml index 0ae23ee288..ce9c9e7da7 100644 --- a/library/ui/src/main/res/values-da/strings.xml +++ b/library/ui/src/main/res/values-da/strings.xml @@ -1,29 +1,19 @@ - - + - "Forrige nummer" - "Næste nummer" - "Pause" - "Afspil" - "Stop" - "Spol tilbage" - "Spol frem" - "Gentag alle" - "Gentag ingen" - "Gentag en" - "Bland" + Afspil forrige + Afspil næste + Sæt på pause + Afspil + Stop + Spol tilbage + Spol frem + Gentag ingen + Gentag én + Gentag alle + Bland + Fuld skærm + Downloaden er i kø + Download + Downloaden er udført + Download mislykkedes diff --git a/library/ui/src/main/res/values-de/strings.xml b/library/ui/src/main/res/values-de/strings.xml index b31ecc93e8..3fd38a784b 100644 --- a/library/ui/src/main/res/values-de/strings.xml +++ b/library/ui/src/main/res/values-de/strings.xml @@ -1,30 +1,19 @@ - - "Vorheriger Titel" - "Nächster Titel" - "Pausieren" - "Wiedergabe" - "Beenden" - "Zurückspulen" - "Vorspulen" - "Alle wiederholen" - "Keinen Titel wiederholen" - "Einen Titel wiederholen" - "Zufallsmix" + Vorheriger Titel + Nächster Titel + Pausieren + Wiedergeben + Beenden + Zurückspulen + Vorspulen + Keinen wiederholen + Einen wiederholen + Alle wiederholen + Zufallsmix Vollbildmodus + Download in der Warteschlange + Wird heruntergeladen + Download abgeschlossen + Download fehlgeschlagen diff --git a/library/ui/src/main/res/values-el/strings.xml b/library/ui/src/main/res/values-el/strings.xml index 9bc6a87889..c4d15a55d6 100644 --- a/library/ui/src/main/res/values-el/strings.xml +++ b/library/ui/src/main/res/values-el/strings.xml @@ -1,30 +1,19 @@ - - "Προηγούμενο κομμάτι" - "Επόμενο κομμάτι" - "Παύση" - "Αναπαραγωγή" - "Διακοπή" - "Επαναφορά" - "Γρήγορη προώθηση" - "Επανάληψη όλων" - "Καμία επανάληψη" - "Επανάληψη ενός στοιχείου" - "Τυχαία αναπαραγωγή" + Προηγούμενο κομμάτι + Επόμενο κομμάτι + Παύση + Αναπαραγωγή + Διακοπή + Επαναφορά + Γρήγορη προώθηση + Καμία επανάληψη + Επανάληψη ενός κομματιού + Επανάληψη όλων + Τυχαία αναπαραγωγή Λειτουργία πλήρους οθόνης + Η λήψη προστέθηκε στην ουρά + Λήψη + Η λήψη ολοκληρώθηκε + Η λήψη απέτυχε diff --git a/library/ui/src/main/res/values-en-rAU/strings.xml b/library/ui/src/main/res/values-en-rAU/strings.xml index 0b4c465853..cfb09d7f65 100644 --- a/library/ui/src/main/res/values-en-rAU/strings.xml +++ b/library/ui/src/main/res/values-en-rAU/strings.xml @@ -1,29 +1,19 @@ - - + - "Previous track" - "Next track" - "Pause" - "Play" - "Stop" - "Rewind" - "Fast-forward" - "Repeat all" - "Repeat none" - "Repeat one" - "Shuffle" + Previous track + Next track + Pause + Play + Stop + Rewind + Fast-forward + Repeat none + Repeat one + Repeat all + Shuffle + Full-screen mode + Download queued + Downloading + Download completed + Download failed diff --git a/library/ui/src/main/res/values-en-rGB/strings.xml b/library/ui/src/main/res/values-en-rGB/strings.xml index e80b2c70c6..cfb09d7f65 100644 --- a/library/ui/src/main/res/values-en-rGB/strings.xml +++ b/library/ui/src/main/res/values-en-rGB/strings.xml @@ -1,30 +1,19 @@ - - "Previous track" - "Next track" - "Pause" - "Play" - "Stop" - "Rewind" - "Fast-forward" - "Repeat all" - "Repeat none" - "Repeat one" - "Shuffle" - Fullscreen mode + Previous track + Next track + Pause + Play + Stop + Rewind + Fast-forward + Repeat none + Repeat one + Repeat all + Shuffle + Full-screen mode + Download queued + Downloading + Download completed + Download failed diff --git a/library/ui/src/main/res/values-en-rIN/strings.xml b/library/ui/src/main/res/values-en-rIN/strings.xml index e80b2c70c6..cfb09d7f65 100644 --- a/library/ui/src/main/res/values-en-rIN/strings.xml +++ b/library/ui/src/main/res/values-en-rIN/strings.xml @@ -1,30 +1,19 @@ - - "Previous track" - "Next track" - "Pause" - "Play" - "Stop" - "Rewind" - "Fast-forward" - "Repeat all" - "Repeat none" - "Repeat one" - "Shuffle" - Fullscreen mode + Previous track + Next track + Pause + Play + Stop + Rewind + Fast-forward + Repeat none + Repeat one + Repeat all + Shuffle + Full-screen mode + Download queued + Downloading + Download completed + Download failed diff --git a/library/ui/src/main/res/values-es-rUS/strings.xml b/library/ui/src/main/res/values-es-rUS/strings.xml index e6cf3fc6f2..990c2cc6ff 100644 --- a/library/ui/src/main/res/values-es-rUS/strings.xml +++ b/library/ui/src/main/res/values-es-rUS/strings.xml @@ -1,29 +1,19 @@ - - + - "Pista anterior" - "Siguiente pista" - "Pausar" - "Reproducir" - "Detener" - "Retroceder" - "Avanzar" - "Repetir todo" - "No repetir" - "Repetir uno" - "Reproducir aleatoriamente" + Pista anterior + Pista siguiente + Pausar + Reproducir + Detener + Retroceder + Avanzar + No repetir + Repetir uno + Repetir todo + Reproducir aleatoriamente + Modo de pantalla completa + Descarga en fila + Descargando + Se completó la descarga + No se pudo descargar diff --git a/library/ui/src/main/res/values-es/strings.xml b/library/ui/src/main/res/values-es/strings.xml index 2029ab833e..2210475e48 100644 --- a/library/ui/src/main/res/values-es/strings.xml +++ b/library/ui/src/main/res/values-es/strings.xml @@ -1,30 +1,19 @@ - - "Canción anterior" - "Siguiente canción" - "Pausar" - "Reproducir" - "Detener" - "Rebobinar" - "Avance rápido" - "Repetir todo" - "No repetir" - "Repetir uno" - "Reproducción aleatoria" + Pista anterior + Siguiente pista + Pausar + Reproducir + Detener + Rebobinar + Avanzar rápidamente + No repetir + Repetir uno + Repetir todo + Reproducir aleatoriamente Modo de pantalla completa + Descarga en cola + Descarga de archivos + Descarga de archivos completado + No se ha podido descargar diff --git a/library/ui/src/main/res/values-fa/strings.xml b/library/ui/src/main/res/values-fa/strings.xml index c2303a6e62..c31f9dafa8 100644 --- a/library/ui/src/main/res/values-fa/strings.xml +++ b/library/ui/src/main/res/values-fa/strings.xml @@ -1,30 +1,19 @@ - - "آهنگ قبلی" - "آهنگ بعدی" - "مکث" - "پخش" - "توقف" - "عقب بردن" - "جلو بردن سریع" - "تکرار همه" - "تکرار هیچ‌کدام" - "یک‌بار تکرار" - "پخش درهم" - حالت تمام صفحه + آهنگ قبلی + آهنگ بعدی + مکث + پخش + توقف + عقب بردن + جلو بردن سریع + تکرار هیچ‌کدام + یکبار تکرار + تکرار همه + درهم + حالت تمام‌صفحه + درانتظار بارگیری + درحال بارگیری + بارگیری کامل شد + بارگیری نشد diff --git a/library/ui/src/main/res/values-fi/strings.xml b/library/ui/src/main/res/values-fi/strings.xml index 92feb86683..4e2b6aaad0 100644 --- a/library/ui/src/main/res/values-fi/strings.xml +++ b/library/ui/src/main/res/values-fi/strings.xml @@ -1,29 +1,19 @@ - - + - "Edellinen raita" - "Seuraava raita" - "Tauko" - "Toista" - "Seis" - "Kelaa taakse" - "Kelaa eteen" - "Toista kaikki" - "Toista ei mitään" - "Toista yksi" - "Toista satunnaisesti" + Edellinen kappale + Seuraava kappale + Keskeytä + Toista + Lopeta + Kelaa taaksepäin + Kelaa eteenpäin + Ei uudelleentoistoa + Toista yksi uudelleen + Toista kaikki uudelleen + Satunnaistoisto + Koko näytön tila + Lataus jonossa + Ladataan + Lataus valmis + Lataus epäonnistui diff --git a/library/ui/src/main/res/values-fr-rCA/strings.xml b/library/ui/src/main/res/values-fr-rCA/strings.xml index 45fc0a86f9..06fff296af 100644 --- a/library/ui/src/main/res/values-fr-rCA/strings.xml +++ b/library/ui/src/main/res/values-fr-rCA/strings.xml @@ -1,29 +1,19 @@ - - + - "Chanson précédente" - "Chanson suivante" - "Pause" - "Lecture" - "Arrêter" - "Reculer" - "Avance rapide" - "Tout lire en boucle" - "Aucune répétition" - "Répéter un élément" - "Lecture aléatoire" + Chanson précédente + Chanson suivante + Pause + Lire + Arrêter + Retour arrière + Avance rapide + Ne rien lire en boucle + Lire une chanson en boucle + Tout lire en boucle + Lecture aléatoire + Mode Plein écran + File d\'attente de télécharg. + Téléchargement en cours… + Téléchargement terminé + Échec du téléchargement diff --git a/library/ui/src/main/res/values-fr/strings.xml b/library/ui/src/main/res/values-fr/strings.xml index 6617fd6e6a..2a6c79df5e 100644 --- a/library/ui/src/main/res/values-fr/strings.xml +++ b/library/ui/src/main/res/values-fr/strings.xml @@ -1,30 +1,19 @@ - - "Piste précédente" - "Piste suivante" - "Interrompre" - "Lire" - "Arrêter" - "Retour arrière" - "Avance rapide" - "Tout lire en boucle" - "Ne rien lire en boucle" - "Lire en boucle un élément" - "Lire en mode aléatoire" + Titre précédent + Titre suivant + Pause + Lecture + Arrêter + Retour arrière + Avance rapide + Ne rien lire en boucle + Lire un titre en boucle + Tout lire en boucle + Aléatoire Mode plein écran + Téléchargement en attente + Téléchargement… + Téléchargement terminé + Échec du téléchargement diff --git a/library/ui/src/main/res/values-hi/strings.xml b/library/ui/src/main/res/values-hi/strings.xml index 6545307e8c..e5b7554acf 100644 --- a/library/ui/src/main/res/values-hi/strings.xml +++ b/library/ui/src/main/res/values-hi/strings.xml @@ -1,30 +1,19 @@ - - "पिछला ट्रैक" - "अगला ट्रैक" - "रोकें" - "चलाएं" - "बंद करें" - "रिवाइंड करें" - "फ़ास्ट फ़ॉरवर्ड" - "सभी को दोहराएं" - "कुछ भी न दोहराएं" - "एक दोहराएं" - "शफ़ल करें" - पूर्ण-स्क्रीन मोड + पिछला ट्रैक + अगला ट्रैक + रोकें + चलाएं + बंद करें + पीछे ले जाएं + तेज़ी से आगे बढ़ाएं + किसी को न दोहराएं + एक को दोहराएं + सभी को दोहराएं + शफ़ल करें + फ़ुलस्क्रीन मोड + डाउनलोड को कतार में लगाया गया + डाउनलोड हो रहा है + डाउनलोड पूरा हुआ + डाउनलोड नहीं हो सका diff --git a/library/ui/src/main/res/values-hr/strings.xml b/library/ui/src/main/res/values-hr/strings.xml index dd7423032b..324dedf417 100644 --- a/library/ui/src/main/res/values-hr/strings.xml +++ b/library/ui/src/main/res/values-hr/strings.xml @@ -1,30 +1,19 @@ - - "Prethodna pjesma" - "Sljedeća pjesma" - "Pauziraj" - "Reproduciraj" - "Zaustavi" - "Unatrag" - "Brzo unaprijed" - "Ponovi sve" - "Bez ponavljanja" - "Ponovi jedno" - "Reproduciraj nasumično" + Prethodni zapis + Sljedeći zapis + Pauza + Reproduciraj + Zaustavi + Unatrag + Brzo unaprijed + Bez ponavljanja + Ponovi jedno + Ponovi sve + Reproduciraj nasumično Prikaz na cijelom zaslonu + Preuzimanje na čekanju + Preuzimanje datoteka + Preuzimanje je dovršeno + Preuzimanje nije uspjelo diff --git a/library/ui/src/main/res/values-hu/strings.xml b/library/ui/src/main/res/values-hu/strings.xml index abd9f9cfac..dd898ff22f 100644 --- a/library/ui/src/main/res/values-hu/strings.xml +++ b/library/ui/src/main/res/values-hu/strings.xml @@ -1,30 +1,19 @@ - - "Előző szám" - "Következő szám" - "Szünet" - "Lejátszás" - "Leállítás" - "Visszatekerés" - "Előretekerés" - "Összes ismétlése" - "Nincs ismétlés" - "Egy ismétlése" - "Véletlenszerű lejátszás" + Előző szám + Következő szám + Szüneteltetés + Lejátszás + Leállítás + Visszatekerés + Előretekerés + Nincs ismétlés + Egy szám ismétlése + Összes szám ismétlése + Keverés Teljes képernyős mód + Letöltés várólistára helyezve + Letöltés folyamatban + A letöltés befejeződött + Nem sikerült a letöltés diff --git a/library/ui/src/main/res/values-in/strings.xml b/library/ui/src/main/res/values-in/strings.xml index 09b05815e6..6bc073d1fa 100644 --- a/library/ui/src/main/res/values-in/strings.xml +++ b/library/ui/src/main/res/values-in/strings.xml @@ -1,29 +1,19 @@ - - + - "Lagu sebelumnya" - "Lagu berikutnya" - "Jeda" - "Putar" - "Berhenti" - "Putar Ulang" - "Maju cepat" - "Ulangi Semua" - "Jangan Ulangi" - "Ulangi Satu" - "Acak" + Lagu sebelumnya + Lagu berikutnya + Jeda + Putar + Berhenti + Putar Ulang + Maju cepat + Jangan ulangi + Ulangi 1 + Ulangi semua + Acak + Mode layar penuh + Download masih dalam antrean + Mendownload + Download selesai + Download gagal diff --git a/library/ui/src/main/res/values-it/strings.xml b/library/ui/src/main/res/values-it/strings.xml index 3300224fbb..9d84c760d1 100644 --- a/library/ui/src/main/res/values-it/strings.xml +++ b/library/ui/src/main/res/values-it/strings.xml @@ -1,30 +1,19 @@ - - "Traccia precedente" - "Traccia successiva" - "Metti in pausa" - "Riproduci" - "Interrompi" - "Riavvolgi" - "Avanti veloce" - "Ripeti tutti" - "Non ripetere nessuno" - "Ripeti uno" - "Riproduci casualmente" + Traccia precedente + Traccia successiva + Pausa + Riproduci + Interrompi + Riavvolgi + Avanti veloce + Non ripetere nulla + Ripeti uno + Ripeti tutto + Riproduzione casuale Modalità a schermo intero + Download aggiunto alla coda + Download + Download completato + Download non riuscito diff --git a/library/ui/src/main/res/values-iw/strings.xml b/library/ui/src/main/res/values-iw/strings.xml index dd973af50b..5e08c74969 100644 --- a/library/ui/src/main/res/values-iw/strings.xml +++ b/library/ui/src/main/res/values-iw/strings.xml @@ -1,29 +1,19 @@ - - + - "הרצועה הקודמת" - "הרצועה הבאה" - "השהה" - "הפעל" - "הפסק" - "הרץ אחורה" - "הרץ קדימה" - "חזור על הכל" - "אל תחזור על כלום" - "חזור על פריט אחד" - "ערבב" + הרצועה הקודמת + הרצועה הבאה + השהיה + הפעלה + הפסקה + הרצה אחורה + הרצה קדימה + אל תחזור על אף פריט + חזור על פריט אחד + חזור על הכול + ערבוב + מצב מסך מלא + ההורדה עדיין לא התחילה + מתבצעת הורדה + ההורדה הושלמה + ההורדה לא הושלמה diff --git a/library/ui/src/main/res/values-ja/strings.xml b/library/ui/src/main/res/values-ja/strings.xml index 2e0f23a78f..ae1578204e 100644 --- a/library/ui/src/main/res/values-ja/strings.xml +++ b/library/ui/src/main/res/values-ja/strings.xml @@ -1,30 +1,19 @@ - - "前のトラック" - "次のトラック" - "一時停止" - "再生" - "停止" - "巻き戻し" - "早送り" - "全曲を繰り返し" - "繰り返しなし" - "1曲を繰り返し" - "シャッフル" - フルスクリーンモード + 前のトラック + 次のトラック + 一時停止 + 再生 + 停止 + 巻き戻し + 早送り + リピートなし + 1 曲をリピート + 全曲をリピート + シャッフル + 全画面モード + ダウンロードを待機しています + ダウンロードしています + ダウンロードが完了しました + ダウンロードに失敗しました diff --git a/library/ui/src/main/res/values-ko/strings.xml b/library/ui/src/main/res/values-ko/strings.xml index 32d3deeb9e..21e2bece31 100644 --- a/library/ui/src/main/res/values-ko/strings.xml +++ b/library/ui/src/main/res/values-ko/strings.xml @@ -1,30 +1,19 @@ - - "이전 트랙" - "다음 트랙" - "일시중지" - "재생" - "중지" - "되감기" - "빨리 감기" - "전체 반복" - "반복 안함" - "한 항목 반복" - "셔플" - 전체 화면 모드 + 이전 트랙 + 다음 트랙 + 일시중지 + 재생 + 중지 + 되감기 + 빨리 감기 + 반복 안함 + 현재 미디어 반복 + 모두 반복 + 셔플 + 전체화면 모드 + 다운로드 대기 중 + 다운로드하는 중 + 다운로드 완료 + 다운로드 실패 diff --git a/library/ui/src/main/res/values-lt/strings.xml b/library/ui/src/main/res/values-lt/strings.xml index 091f2384b2..e756bd1019 100644 --- a/library/ui/src/main/res/values-lt/strings.xml +++ b/library/ui/src/main/res/values-lt/strings.xml @@ -1,30 +1,19 @@ - - "Ankstesnis takelis" - "Kitas takelis" - "Pristabdyti" - "Leisti" - "Stabdyti" - "Sukti atgal" - "Sukti pirmyn" - "Kartoti viską" - "Nekartoti nieko" - "Kartoti vieną" - "Maišyti" + Ankstesnis takelis + Kitas takelis + Pristabdyti + Leisti + Sustabdyti + Sukti atgal + Sukti pirmyn + Nekartoti nieko + Kartoti vieną + Kartoti viską + Maišyti Viso ekrano režimas + Atsisiunč. elem. laukia eilėje + Atsisiunčiama + Atsisiuntimo procesas baigtas + Nepavyko atsisiųsti diff --git a/library/ui/src/main/res/values-lv/strings.xml b/library/ui/src/main/res/values-lv/strings.xml index af982587bf..e330620e8b 100644 --- a/library/ui/src/main/res/values-lv/strings.xml +++ b/library/ui/src/main/res/values-lv/strings.xml @@ -1,30 +1,19 @@ - - "Iepriekšējais ieraksts" - "Nākamais ieraksts" - "Pārtraukt" - "Atskaņot" - "Apturēt" - "Attīt atpakaļ" - "Ātri patīt" - "Atkārtot visu" - "Neatkārtot nevienu" - "Atkārtot vienu" - "Atskaņot jauktā secībā" + Iepriekšējais ieraksts + Nākamais ieraksts + Pauzēt + Atskaņot + Apturēt + Attīt atpakaļ + Pārtīt uz priekšu + Neatkārtot nevienu + Atkārtot vienu + Atkārtot visu + Atskaņot jauktā secībā Pilnekrāna režīms + Lejupielāde gaida rindā + Notiek lejupielāde + Lejupielāde ir pabeigta + Lejupielāde neizdevās diff --git a/library/ui/src/main/res/values-nb/strings.xml b/library/ui/src/main/res/values-nb/strings.xml index 370c759b84..267a82994e 100644 --- a/library/ui/src/main/res/values-nb/strings.xml +++ b/library/ui/src/main/res/values-nb/strings.xml @@ -1,29 +1,19 @@ - - + - "Forrige spor" - "Neste spor" - "Sett på pause" - "Spill av" - "Stopp" - "Tilbakespoling" - "Fremoverspoling" - "Gjenta alle" - "Ikke gjenta noen" - "Gjenta én" - "Spill av i tilfeldig rekkefølge" + Forrige spor + Neste spor + Sett på pause + Spill av + Stopp + Spol tilbake + Spol forover + Ikke gjenta noen + Gjenta én + Gjenta alle + Tilfeldig rekkefølge + Fullskjermmodus + Nedlasting står i kø + Laster ned + Nedlastingen er fullført + Nedlastingen mislyktes diff --git a/library/ui/src/main/res/values-nl/strings.xml b/library/ui/src/main/res/values-nl/strings.xml index a67ab2968c..70bda2fdcc 100644 --- a/library/ui/src/main/res/values-nl/strings.xml +++ b/library/ui/src/main/res/values-nl/strings.xml @@ -1,29 +1,19 @@ - - + - "Vorig nummer" - "Volgend nummer" - "Onderbreken" - "Afspelen" - "Stoppen" - "Terugspoelen" - "Vooruitspoelen" - "Alles herhalen" - "Niet herhalen" - "Eén herhalen" - "Shuffle" + Vorige track + Volgende track + Pauzeren + Afspelen + Stoppen + Terugspoelen + Vooruitspoelen + Niets herhalen + Eén herhalen + Alles herhalen + Shuffle + Modus \'Volledig scherm\' + Download in de wachtrij + Downloaden + Downloaden voltooid + Downloaden mislukt diff --git a/library/ui/src/main/res/values-pl/strings.xml b/library/ui/src/main/res/values-pl/strings.xml index 981aa17543..ef341f88e4 100644 --- a/library/ui/src/main/res/values-pl/strings.xml +++ b/library/ui/src/main/res/values-pl/strings.xml @@ -1,30 +1,19 @@ - - "Poprzedni utwór" - "Następny utwór" - "Wstrzymaj" - "Odtwórz" - "Zatrzymaj" - "Przewiń do tyłu" - "Przewiń do przodu" - "Powtórz wszystkie" - "Nie powtarzaj" - "Powtórz jeden" - "Odtwarzaj losowo" + Poprzedni utwór + Następny utwór + Wstrzymaj + Odtwórz + Zatrzymaj + Przewiń do tyłu + Przewiń do przodu + Nie powtarzaj + Powtórz jeden + Powtórz wszystkie + Odtwarzanie losowe Tryb pełnoekranowy + W kolejce pobierania + Pobieranie + Zakończono pobieranie + Nie udało się pobrać diff --git a/library/ui/src/main/res/values-pt-rPT/strings.xml b/library/ui/src/main/res/values-pt-rPT/strings.xml index f0c3770c51..06fc3cc5eb 100644 --- a/library/ui/src/main/res/values-pt-rPT/strings.xml +++ b/library/ui/src/main/res/values-pt-rPT/strings.xml @@ -1,30 +1,19 @@ - - "Faixa anterior" - "Faixa seguinte" - "Interromper" - "Reproduzir" - "Parar" - "Rebobinar" - "Avançar" - "Repetir tudo" - "Não repetir" - "Repetir um" - "Reproduzir aleatoriamente" + Faixa anterior + Faixa seguinte + Colocar em pausa + Reproduzir + Parar + Recuar + Avançar + Não repetir nenhum + Repetir um + Repetir tudo + Reproduzir aleatoriamente Modo de ecrã inteiro + Transfer. em fila de espera + A transferir… + Transferência concluída + Falha na transferência diff --git a/library/ui/src/main/res/values-pt/strings.xml b/library/ui/src/main/res/values-pt/strings.xml index 8441e4e1cc..9c25f7de5f 100644 --- a/library/ui/src/main/res/values-pt/strings.xml +++ b/library/ui/src/main/res/values-pt/strings.xml @@ -1,29 +1,19 @@ - - + - "Faixa anterior" - "Próxima faixa" - "Pausar" - "Reproduzir" - "Parar" - "Retroceder" - "Avançar" - "Repetir tudo" - "Não repetir" - "Repetir uma" - "Reproduzir aleatoriamente" + Faixa anterior + Próxima faixa + Pausar + Reproduzir + Parar + Retroceder + Avançar + Não repetir + Repetir uma + Repetir tudo + Aleatório + Modo de tela cheia + Item na fila de download + Fazendo download + Download concluído + Falha no download diff --git a/library/ui/src/main/res/values-ro/strings.xml b/library/ui/src/main/res/values-ro/strings.xml index 6b8644e30a..4541a6c6e8 100644 --- a/library/ui/src/main/res/values-ro/strings.xml +++ b/library/ui/src/main/res/values-ro/strings.xml @@ -1,29 +1,19 @@ - - + - "Melodia anterioară" - "Melodia următoare" - "Pauză" - "Redați" - "Opriți" - "Derulați" - "Derulați rapid înainte" - "Repetați toate" - "Repetați niciuna" - "Repetați unul" - "Redați aleatoriu" + Melodia anterioară + Următoarea înregistrare + Întrerupeți + Redați + Opriți + Derulați înapoi + Derulați rapid înainte + Nu repetați niciunul + Repetați unul + Repetați-le pe toate + Redați aleatoriu + Modul Ecran complet + Descărcarea este în lista de așteptare + Se descarcă + Descărcarea a fost finalizată + Descărcarea nu a reușit diff --git a/library/ui/src/main/res/values-ru/strings.xml b/library/ui/src/main/res/values-ru/strings.xml index 51d11d6371..4e030ef5a4 100644 --- a/library/ui/src/main/res/values-ru/strings.xml +++ b/library/ui/src/main/res/values-ru/strings.xml @@ -1,29 +1,19 @@ - - + - "Предыдущий трек" - "Следующий трек" - "Приостановить" - "Воспроизвести" - "Остановить" - "Перемотать назад" - "Перемотать вперед" - "Повторять все" - "Не повторять" - "Повторять один элемент" - "Перемешать" + Предыдущий трек + Следующий трек + Приостановить + Воспроизвести + Остановить + Перемотать назад + Перемотать вперед + Не повторять + Повторять трек + Повторять все + Перемешать + Полноэкранный режим + В очереди на скачивание + Загрузка файлов + Скачивание завершено + Ошибка скачивания diff --git a/library/ui/src/main/res/values-sk/strings.xml b/library/ui/src/main/res/values-sk/strings.xml index a289e89d34..c827282b95 100644 --- a/library/ui/src/main/res/values-sk/strings.xml +++ b/library/ui/src/main/res/values-sk/strings.xml @@ -1,30 +1,19 @@ - - "Predchádzajúca stopa" - "Ďalšia stopa" - "Pozastaviť" - "Prehrať" - "Zastaviť" - "Pretočiť späť" - "Pretočiť dopredu" - "Opakovať všetko" - "Neopakovať" - "Opakovať jednu položku" - "Náhodne prehrávať" + Predchádzajúca skladba + Ďalšia skladba + Pozastaviť + Prehrať + Zastaviť + Pretočiť späť + Pretočiť dopredu + Neopakovať + Opakovať jednu + Opakovať všetko + Náhodne prehrávať Režim celej obrazovky + Sťahovanie je v poradí + Sťahuje sa + Sťahovanie bolo dokončené + Nepodarilo sa stiahnuť diff --git a/library/ui/src/main/res/values-sl/strings.xml b/library/ui/src/main/res/values-sl/strings.xml index 8ed731b0d3..c26a91826e 100644 --- a/library/ui/src/main/res/values-sl/strings.xml +++ b/library/ui/src/main/res/values-sl/strings.xml @@ -1,29 +1,19 @@ - - + - "Prejšnja skladba" - "Naslednja skladba" - "Zaustavi" - "Predvajaj" - "Ustavi" - "Previj nazaj" - "Previj naprej" - "Ponovi vse" - "Ne ponovi" - "Ponovi eno" - "Naključno predvajaj" + Prejšnja skladba + Naslednja skladba + Zaustavitev + Predvajanje + Ustavitev + Previjanje nazaj + Previjanje naprej + Brez ponavljanja + Ponavljanje ene + Ponavljanje vseh + Naključno predvajanje + Celozaslonski način + Prenos je v čakalni vrsti + Prenašanje + Prenos je končan + Prenos ni uspel diff --git a/library/ui/src/main/res/values-sr/strings.xml b/library/ui/src/main/res/values-sr/strings.xml index 9cff134a61..8ba19a51a6 100644 --- a/library/ui/src/main/res/values-sr/strings.xml +++ b/library/ui/src/main/res/values-sr/strings.xml @@ -1,30 +1,19 @@ - - "Претходна песма" - "Следећа песма" - "Пауза" - "Пусти" - "Заустави" - "Премотај уназад" - "Премотај унапред" - "Понови све" - "Понављање је искључено" - "Понови једну" - "Пусти насумично" + Претходна песма + Следећа песма + Паузирај + Пусти + Заустави + Премотај уназад + Премотај унапред + Не понављај ниједну + Понови једну + Понови све + Пусти насумично Режим целог екрана + Преузимање је на чекању + Преузимање + Преузимање је завршено + Преузимање није успело diff --git a/library/ui/src/main/res/values-sv/strings.xml b/library/ui/src/main/res/values-sv/strings.xml index b8fc7a1fff..6840738ba1 100644 --- a/library/ui/src/main/res/values-sv/strings.xml +++ b/library/ui/src/main/res/values-sv/strings.xml @@ -1,30 +1,19 @@ - - "Föregående spår" - "Nästa spår" - "Pausa" - "Spela upp" - "Avbryt" - "Spola tillbaka" - "Snabbspola framåt" - "Upprepa alla" - "Upprepa inga" - "Upprepa en" - "Blanda" + Föregående spår + Nästa spår + Pausa + Spela upp + Stoppa + Spola tillbaka + Snabbspola framåt + Upprepa inga + Upprepa en + Upprepa alla + Blanda spår Helskärmsläge + Nedladdningen har köplacerats + Laddar ned + Nedladdningen är klar + Nedladdningen misslyckades diff --git a/library/ui/src/main/res/values-sw/strings.xml b/library/ui/src/main/res/values-sw/strings.xml index 4451ad3c2b..5db4ee5995 100644 --- a/library/ui/src/main/res/values-sw/strings.xml +++ b/library/ui/src/main/res/values-sw/strings.xml @@ -1,30 +1,19 @@ - - "Wimbo uliotangulia" - "Wimbo unaofuata" - "Sitisha" - "Cheza" - "Simamisha" - "Rudisha nyuma" - "Peleka mbele kwa kasi" - "Rudia zote" - "Usirudie Yoyote" - "Rudia Moja" - "Changanya" - Hali ya skrini kamili + Wimbo uliotangulia + Wimbo unaofuata + Sitisha + Cheza + Simamisha + Rudisha nyuma + Sogeza mbele haraka + Usirudie yoyote + Rudia moja + Rudia zote + Changanya + Hali ya skrini nzima + Inasubiri kupakuliwa + Inapakua + Imepakuliwa + Imeshindwa kupakua diff --git a/library/ui/src/main/res/values-th/strings.xml b/library/ui/src/main/res/values-th/strings.xml index 664900e7da..bb5edb211c 100644 --- a/library/ui/src/main/res/values-th/strings.xml +++ b/library/ui/src/main/res/values-th/strings.xml @@ -1,30 +1,19 @@ - - "แทร็กก่อนหน้า" - "แทร็กถัดไป" - "หยุดชั่วคราว" - "เล่น" - "หยุด" - "กรอกลับ" - "กรอไปข้างหน้า" - "เล่นซ้ำทั้งหมด" - "ไม่เล่นซ้ำ" - "เล่นซ้ำรายการเดียว" - "สุ่มเพลง" + แทร็กก่อนหน้า + แทร็กถัดไป + หยุด + เล่น + หยุด + กรอกลับ + กรอไปข้างหน้า + ไม่เล่นซ้ำ + เล่นซ้ำเพลงเดียว + เล่นซ้ำทั้งหมด + สุ่ม โหมดเต็มหน้าจอ + การดาวน์โหลดอยู่ในคิว + กำลังดาวน์โหลด + การดาวน์โหลดเสร็จสมบูรณ์ + การดาวน์โหลดล้มเหลว diff --git a/library/ui/src/main/res/values-tl/strings.xml b/library/ui/src/main/res/values-tl/strings.xml index 471191a81a..4807026e05 100644 --- a/library/ui/src/main/res/values-tl/strings.xml +++ b/library/ui/src/main/res/values-tl/strings.xml @@ -1,30 +1,19 @@ - - "Nakaraang track" - "Susunod na track" - "I-pause" - "I-play" - "Ihinto" - "I-rewind" - "I-fast forward" - "Ulitin Lahat" - "Walang Uulitin" - "Ulitin ang Isa" - "I-shuffle" + Nakaraang track + Susunod na track + I-pause + I-play + Ihinto + I-rewind + I-fast forward + Walang uulitin + Mag-ulit ng isa + Ulitin lahat + I-shuffle Fullscreen mode + Naka-queue ang download + Nagda-download + Tapos na ang pag-download + Hindi na-download diff --git a/library/ui/src/main/res/values-tr/strings.xml b/library/ui/src/main/res/values-tr/strings.xml index cd1bfc5444..a8a409676f 100644 --- a/library/ui/src/main/res/values-tr/strings.xml +++ b/library/ui/src/main/res/values-tr/strings.xml @@ -1,29 +1,19 @@ - - + - "Önceki parça" - "Sonraki parça" - "Duraklat" - "Çal" - "Durdur" - "Geri sar" - "İleri sar" - "Tümünü Tekrarla" - "Hiçbirini Tekrarlama" - "Birini Tekrarla" - "Karıştır" + Önceki parça + Sonraki parça + Duraklat + Çal + Durdur + Geri sar + İleri sar + Hiçbirini tekrarlama + Birini tekrarla + Tümünü tekrarla + Karıştır + Tam ekran modu + İndirme işlemi sıraya alındı + İndiriliyor + İndirme işlemi tamamlandı + İndirilemedi diff --git a/library/ui/src/main/res/values-uk/strings.xml b/library/ui/src/main/res/values-uk/strings.xml index 36bfca2a34..44190c4dde 100644 --- a/library/ui/src/main/res/values-uk/strings.xml +++ b/library/ui/src/main/res/values-uk/strings.xml @@ -1,30 +1,19 @@ - - "Попередня композиція" - "Наступна композиція" - "Пауза" - "Відтворити" - "Зупинити" - "Перемотати назад" - "Перемотати вперед" - "Повторити все" - "Не повторювати" - "Повторити один елемент" - "Перемішати" + Попередня композиція + Наступна композиція + Призупинити + Відтворити + Припинити + Перемотати назад + Перемотати вперед + Не повторювати + Повторити 1 + Повторити всі + Перемішати Повноекранний режим + Завантаження розміщено в черзі + Завантажується + Завантаження завершено + Не вдалося завантажити diff --git a/library/ui/src/main/res/values-vi/strings.xml b/library/ui/src/main/res/values-vi/strings.xml index 748de96949..2754eec898 100644 --- a/library/ui/src/main/res/values-vi/strings.xml +++ b/library/ui/src/main/res/values-vi/strings.xml @@ -1,30 +1,19 @@ - - "Bản nhạc trước" - "Bản nhạc tiếp theo" - "Tạm dừng" - "Phát" - "Ngừng" - "Tua lại" - "Tua đi" - "Lặp lại tất cả" - "Không lặp lại" - "Lặp lại một mục" - "Trộn bài" + Bản nhạc trước + Bản nhạc tiếp theo + Tạm dừng + Phát + Dừng + Tua lại + Tua đi + Không lặp lại + Lặp lại một + Lặp lại tất cả + Phát ngẫu nhiên Chế độ toàn màn hình + Đã đưa tài nguyên đã tải xuống vào hàng đợi + Đang tải xuống + Đã hoàn tất tải xuống + Không tải xuống được diff --git a/library/ui/src/main/res/values-zh-rCN/strings.xml b/library/ui/src/main/res/values-zh-rCN/strings.xml index d357152a64..cb8beae7b9 100644 --- a/library/ui/src/main/res/values-zh-rCN/strings.xml +++ b/library/ui/src/main/res/values-zh-rCN/strings.xml @@ -1,30 +1,19 @@ - - "上一曲" - "下一曲" - "暂停" - "播放" - "停止" - "快退" - "快进" - "重复播放全部" - "不重复播放" - "重复播放单个视频" - "随机播放" + 上一曲 + 下一曲 + 暂停 + 播放 + 停止 + 快退 + 快进 + 不重复播放 + 重复播放一项 + 全部重复播放 + 随机播放 全屏模式 + 已加入待下载队列 + 正在下载 + 下载完毕 + 下载失败 diff --git a/library/ui/src/main/res/values-zh-rHK/strings.xml b/library/ui/src/main/res/values-zh-rHK/strings.xml index 3a26b8b5f0..a61c20a847 100644 --- a/library/ui/src/main/res/values-zh-rHK/strings.xml +++ b/library/ui/src/main/res/values-zh-rHK/strings.xml @@ -1,30 +1,19 @@ - - "上一首曲目" - "下一首曲目" - "暫停" - "播放" - "停止" - "倒帶" - "向前快轉" - "重複播放所有媒體項目" - "不重複播放任何媒體項目" - "重複播放一個媒體項目" - "隨機播放" + 上一首曲目 + 下一首曲目 + 暫停 + 播放 + 停止 + 倒轉 + 向前快轉 + 不重複播放 + 重複播放單一項目 + 全部重複播放 + 隨機播放 全螢幕模式 + 已加入下載列 + 正在下載 + 下載完畢 + 下載失敗 diff --git a/library/ui/src/main/res/values-zh-rTW/strings.xml b/library/ui/src/main/res/values-zh-rTW/strings.xml index 6f87d143ad..cd6a8c1703 100644 --- a/library/ui/src/main/res/values-zh-rTW/strings.xml +++ b/library/ui/src/main/res/values-zh-rTW/strings.xml @@ -1,30 +1,19 @@ - - "上一首曲目" - "下一首曲目" - "暫停" - "播放" - "停止" - "倒轉" - "快轉" - "重複播放所有媒體項目" - "不重複播放" - "重複播放單一媒體項目" - "隨機播放" + 上一首曲目 + 下一首曲目 + 暫停 + 播放 + 停止 + 倒轉 + 快轉 + 不重複播放 + 重複播放單一項目 + 重複播放所有項目 + 隨機播放 全螢幕模式 + 已排入下載佇列 + 下載中 + 下載完成 + 無法下載 diff --git a/library/ui/src/main/res/values-zu/strings.xml b/library/ui/src/main/res/values-zu/strings.xml index aff66ba0cf..19bfab08fc 100644 --- a/library/ui/src/main/res/values-zu/strings.xml +++ b/library/ui/src/main/res/values-zu/strings.xml @@ -1,30 +1,19 @@ - - "Ithrekhi yangaphambilini" - "Ithrekhi elandelayo" - "Misa isikhashana" - "Dlala" - "Misa" - "Buyisela emumva" - "Ukudlulisa ngokushesha" - "Phinda konke" - "Ungaphindi lutho" - "Phida okukodwa" - "Shova" - Imodi yesikrini esiphelele + Ithrekhi yangaphambilini + Ithrekhi elandelayo + Phumula + Dlala + Misa + Buyisela emuva + Dlulisela phambili + Phinda okungekho + Phinda okukodwa + Phinda konke + Shova + Imodi yesikrini esigcwele + Ukulanda kukulayini + Iyalanda + Ukulanda kuqedile + Ukulanda kuhlulekile From e99b8802eb80d650c4710df3ea6888c5675a4927 Mon Sep 17 00:00:00 2001 From: olly Date: Mon, 16 Apr 2018 08:34:09 -0700 Subject: [PATCH 049/157] Update translations ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=193044028 --- .../src/main/res/values-az-rAZ/strings.xml | 21 -------------- .../src/main/res/values-az/strings.xml | 6 ++++ .../src/main/res/values-be-rBY/strings.xml | 21 -------------- .../src/main/res/values-be/strings.xml | 6 ++++ .../src/main/res/values-bn-rBD/strings.xml | 21 -------------- .../src/main/res/values-bn/strings.xml | 6 ++++ .../src/main/res/values-bs-rBA/strings.xml | 21 -------------- .../src/main/res/values-bs/strings.xml | 6 ++++ .../src/main/res/values-et-rEE/strings.xml | 21 -------------- .../src/main/res/values-et/strings.xml | 6 ++++ .../src/main/res/values-eu-rES/strings.xml | 21 -------------- .../src/main/res/values-eu/strings.xml | 6 ++++ .../src/main/res/values-gl-rES/strings.xml | 21 -------------- .../src/main/res/values-gl/strings.xml | 6 ++++ .../src/main/res/values-gu-rIN/strings.xml | 21 -------------- .../src/main/res/values-gu/strings.xml | 6 ++++ .../src/main/res/values-hy-rAM/strings.xml | 21 -------------- .../src/main/res/values-hy/strings.xml | 6 ++++ .../src/main/res/values-is-rIS/strings.xml | 21 -------------- .../src/main/res/values-is/strings.xml | 6 ++++ .../src/main/res/values-ka-rGE/strings.xml | 21 -------------- .../src/main/res/values-ka/strings.xml | 6 ++++ .../src/main/res/values-kk-rKZ/strings.xml | 21 -------------- .../src/main/res/values-kk/strings.xml | 6 ++++ .../src/main/res/values-km-rKH/strings.xml | 21 -------------- .../src/main/res/values-km/strings.xml | 6 ++++ .../src/main/res/values-kn-rIN/strings.xml | 21 -------------- .../src/main/res/values-kn/strings.xml | 6 ++++ .../src/main/res/values-ky-rKG/strings.xml | 21 -------------- .../src/main/res/values-ky/strings.xml | 6 ++++ .../src/main/res/values-lo-rLA/strings.xml | 21 -------------- .../src/main/res/values-lo/strings.xml | 6 ++++ .../src/main/res/values-mk-rMK/strings.xml | 21 -------------- .../src/main/res/values-mk/strings.xml | 6 ++++ .../src/main/res/values-ml-rIN/strings.xml | 21 -------------- .../src/main/res/values-ml/strings.xml | 6 ++++ .../src/main/res/values-mn-rMN/strings.xml | 21 -------------- .../src/main/res/values-mn/strings.xml | 6 ++++ .../src/main/res/values-mr-rIN/strings.xml | 21 -------------- .../src/main/res/values-mr/strings.xml | 6 ++++ .../src/main/res/values-ms-rMY/strings.xml | 21 -------------- .../src/main/res/values-ms/strings.xml | 6 ++++ .../src/main/res/values-my-rMM/strings.xml | 21 -------------- .../src/main/res/values-my/strings.xml | 6 ++++ .../src/main/res/values-ne-rNP/strings.xml | 21 -------------- .../src/main/res/values-ne/strings.xml | 6 ++++ .../src/main/res/values-pa-rIN/strings.xml | 21 -------------- .../src/main/res/values-pa/strings.xml | 6 ++++ .../src/main/res/values-pt-rBR/strings.xml | 21 -------------- .../src/main/res/values-si-rLK/strings.xml | 21 -------------- .../src/main/res/values-si/strings.xml | 6 ++++ .../src/main/res/values-sq-rAL/strings.xml | 21 -------------- .../src/main/res/values-sq/strings.xml | 6 ++++ .../src/main/res/values-ta-rIN/strings.xml | 21 -------------- .../src/main/res/values-ta/strings.xml | 6 ++++ .../src/main/res/values-te-rIN/strings.xml | 21 -------------- .../src/main/res/values-te/strings.xml | 6 ++++ .../src/main/res/values-ur-rPK/strings.xml | 21 -------------- .../src/main/res/values-ur/strings.xml | 6 ++++ .../src/main/res/values-uz-rUZ/strings.xml | 21 -------------- .../src/main/res/values-uz/strings.xml | 6 ++++ .../ui/src/main/res/values-az-rAZ/strings.xml | 29 ------------------- library/ui/src/main/res/values-az/strings.xml | 19 ++++++++++++ .../ui/src/main/res/values-be-rBY/strings.xml | 29 ------------------- library/ui/src/main/res/values-be/strings.xml | 19 ++++++++++++ .../ui/src/main/res/values-bn-rBD/strings.xml | 29 ------------------- library/ui/src/main/res/values-bn/strings.xml | 19 ++++++++++++ .../ui/src/main/res/values-bs-rBA/strings.xml | 29 ------------------- library/ui/src/main/res/values-bs/strings.xml | 19 ++++++++++++ .../ui/src/main/res/values-et-rEE/strings.xml | 29 ------------------- library/ui/src/main/res/values-et/strings.xml | 19 ++++++++++++ .../ui/src/main/res/values-eu-rES/strings.xml | 29 ------------------- library/ui/src/main/res/values-eu/strings.xml | 19 ++++++++++++ .../ui/src/main/res/values-gl-rES/strings.xml | 29 ------------------- library/ui/src/main/res/values-gl/strings.xml | 19 ++++++++++++ .../ui/src/main/res/values-gu-rIN/strings.xml | 29 ------------------- library/ui/src/main/res/values-gu/strings.xml | 19 ++++++++++++ .../ui/src/main/res/values-hy-rAM/strings.xml | 29 ------------------- library/ui/src/main/res/values-hy/strings.xml | 19 ++++++++++++ .../ui/src/main/res/values-is-rIS/strings.xml | 29 ------------------- library/ui/src/main/res/values-is/strings.xml | 19 ++++++++++++ .../ui/src/main/res/values-ka-rGE/strings.xml | 29 ------------------- library/ui/src/main/res/values-ka/strings.xml | 19 ++++++++++++ .../ui/src/main/res/values-kk-rKZ/strings.xml | 29 ------------------- library/ui/src/main/res/values-kk/strings.xml | 19 ++++++++++++ .../ui/src/main/res/values-km-rKH/strings.xml | 29 ------------------- library/ui/src/main/res/values-km/strings.xml | 19 ++++++++++++ .../ui/src/main/res/values-kn-rIN/strings.xml | 29 ------------------- library/ui/src/main/res/values-kn/strings.xml | 19 ++++++++++++ .../ui/src/main/res/values-ky-rKG/strings.xml | 29 ------------------- library/ui/src/main/res/values-ky/strings.xml | 19 ++++++++++++ .../ui/src/main/res/values-lo-rLA/strings.xml | 29 ------------------- library/ui/src/main/res/values-lo/strings.xml | 19 ++++++++++++ .../ui/src/main/res/values-mk-rMK/strings.xml | 29 ------------------- library/ui/src/main/res/values-mk/strings.xml | 19 ++++++++++++ .../ui/src/main/res/values-ml-rIN/strings.xml | 29 ------------------- library/ui/src/main/res/values-ml/strings.xml | 19 ++++++++++++ .../ui/src/main/res/values-mn-rMN/strings.xml | 29 ------------------- library/ui/src/main/res/values-mn/strings.xml | 19 ++++++++++++ .../ui/src/main/res/values-mr-rIN/strings.xml | 29 ------------------- library/ui/src/main/res/values-mr/strings.xml | 19 ++++++++++++ .../ui/src/main/res/values-ms-rMY/strings.xml | 29 ------------------- library/ui/src/main/res/values-ms/strings.xml | 19 ++++++++++++ .../ui/src/main/res/values-my-rMM/strings.xml | 29 ------------------- library/ui/src/main/res/values-my/strings.xml | 19 ++++++++++++ .../ui/src/main/res/values-ne-rNP/strings.xml | 29 ------------------- library/ui/src/main/res/values-ne/strings.xml | 19 ++++++++++++ .../ui/src/main/res/values-pa-rIN/strings.xml | 29 ------------------- library/ui/src/main/res/values-pa/strings.xml | 19 ++++++++++++ .../ui/src/main/res/values-pt-rBR/strings.xml | 29 ------------------- .../ui/src/main/res/values-si-rLK/strings.xml | 29 ------------------- library/ui/src/main/res/values-si/strings.xml | 19 ++++++++++++ .../ui/src/main/res/values-sq-rAL/strings.xml | 29 ------------------- library/ui/src/main/res/values-sq/strings.xml | 19 ++++++++++++ .../ui/src/main/res/values-ta-rIN/strings.xml | 29 ------------------- library/ui/src/main/res/values-ta/strings.xml | 19 ++++++++++++ .../ui/src/main/res/values-te-rIN/strings.xml | 29 ------------------- library/ui/src/main/res/values-te/strings.xml | 19 ++++++++++++ .../ui/src/main/res/values-ur-rPK/strings.xml | 29 ------------------- library/ui/src/main/res/values-ur/strings.xml | 19 ++++++++++++ .../ui/src/main/res/values-uz-rUZ/strings.xml | 29 ------------------- library/ui/src/main/res/values-uz/strings.xml | 19 ++++++++++++ 122 files changed, 750 insertions(+), 1550 deletions(-) delete mode 100644 extensions/mediasession/src/main/res/values-az-rAZ/strings.xml create mode 100644 extensions/mediasession/src/main/res/values-az/strings.xml delete mode 100644 extensions/mediasession/src/main/res/values-be-rBY/strings.xml create mode 100644 extensions/mediasession/src/main/res/values-be/strings.xml delete mode 100644 extensions/mediasession/src/main/res/values-bn-rBD/strings.xml create mode 100644 extensions/mediasession/src/main/res/values-bn/strings.xml delete mode 100644 extensions/mediasession/src/main/res/values-bs-rBA/strings.xml create mode 100644 extensions/mediasession/src/main/res/values-bs/strings.xml delete mode 100644 extensions/mediasession/src/main/res/values-et-rEE/strings.xml create mode 100644 extensions/mediasession/src/main/res/values-et/strings.xml delete mode 100644 extensions/mediasession/src/main/res/values-eu-rES/strings.xml create mode 100644 extensions/mediasession/src/main/res/values-eu/strings.xml delete mode 100644 extensions/mediasession/src/main/res/values-gl-rES/strings.xml create mode 100644 extensions/mediasession/src/main/res/values-gl/strings.xml delete mode 100644 extensions/mediasession/src/main/res/values-gu-rIN/strings.xml create mode 100644 extensions/mediasession/src/main/res/values-gu/strings.xml delete mode 100644 extensions/mediasession/src/main/res/values-hy-rAM/strings.xml create mode 100644 extensions/mediasession/src/main/res/values-hy/strings.xml delete mode 100644 extensions/mediasession/src/main/res/values-is-rIS/strings.xml create mode 100644 extensions/mediasession/src/main/res/values-is/strings.xml delete mode 100644 extensions/mediasession/src/main/res/values-ka-rGE/strings.xml create mode 100644 extensions/mediasession/src/main/res/values-ka/strings.xml delete mode 100644 extensions/mediasession/src/main/res/values-kk-rKZ/strings.xml create mode 100644 extensions/mediasession/src/main/res/values-kk/strings.xml delete mode 100644 extensions/mediasession/src/main/res/values-km-rKH/strings.xml create mode 100644 extensions/mediasession/src/main/res/values-km/strings.xml delete mode 100644 extensions/mediasession/src/main/res/values-kn-rIN/strings.xml create mode 100644 extensions/mediasession/src/main/res/values-kn/strings.xml delete mode 100644 extensions/mediasession/src/main/res/values-ky-rKG/strings.xml create mode 100644 extensions/mediasession/src/main/res/values-ky/strings.xml delete mode 100644 extensions/mediasession/src/main/res/values-lo-rLA/strings.xml create mode 100644 extensions/mediasession/src/main/res/values-lo/strings.xml delete mode 100644 extensions/mediasession/src/main/res/values-mk-rMK/strings.xml create mode 100644 extensions/mediasession/src/main/res/values-mk/strings.xml delete mode 100644 extensions/mediasession/src/main/res/values-ml-rIN/strings.xml create mode 100644 extensions/mediasession/src/main/res/values-ml/strings.xml delete mode 100644 extensions/mediasession/src/main/res/values-mn-rMN/strings.xml create mode 100644 extensions/mediasession/src/main/res/values-mn/strings.xml delete mode 100644 extensions/mediasession/src/main/res/values-mr-rIN/strings.xml create mode 100644 extensions/mediasession/src/main/res/values-mr/strings.xml delete mode 100644 extensions/mediasession/src/main/res/values-ms-rMY/strings.xml create mode 100644 extensions/mediasession/src/main/res/values-ms/strings.xml delete mode 100644 extensions/mediasession/src/main/res/values-my-rMM/strings.xml create mode 100644 extensions/mediasession/src/main/res/values-my/strings.xml delete mode 100644 extensions/mediasession/src/main/res/values-ne-rNP/strings.xml create mode 100644 extensions/mediasession/src/main/res/values-ne/strings.xml delete mode 100644 extensions/mediasession/src/main/res/values-pa-rIN/strings.xml create mode 100644 extensions/mediasession/src/main/res/values-pa/strings.xml delete mode 100644 extensions/mediasession/src/main/res/values-pt-rBR/strings.xml delete mode 100644 extensions/mediasession/src/main/res/values-si-rLK/strings.xml create mode 100644 extensions/mediasession/src/main/res/values-si/strings.xml delete mode 100644 extensions/mediasession/src/main/res/values-sq-rAL/strings.xml create mode 100644 extensions/mediasession/src/main/res/values-sq/strings.xml delete mode 100644 extensions/mediasession/src/main/res/values-ta-rIN/strings.xml create mode 100644 extensions/mediasession/src/main/res/values-ta/strings.xml delete mode 100644 extensions/mediasession/src/main/res/values-te-rIN/strings.xml create mode 100644 extensions/mediasession/src/main/res/values-te/strings.xml delete mode 100644 extensions/mediasession/src/main/res/values-ur-rPK/strings.xml create mode 100644 extensions/mediasession/src/main/res/values-ur/strings.xml delete mode 100644 extensions/mediasession/src/main/res/values-uz-rUZ/strings.xml create mode 100644 extensions/mediasession/src/main/res/values-uz/strings.xml delete mode 100644 library/ui/src/main/res/values-az-rAZ/strings.xml create mode 100644 library/ui/src/main/res/values-az/strings.xml delete mode 100644 library/ui/src/main/res/values-be-rBY/strings.xml create mode 100644 library/ui/src/main/res/values-be/strings.xml delete mode 100644 library/ui/src/main/res/values-bn-rBD/strings.xml create mode 100644 library/ui/src/main/res/values-bn/strings.xml delete mode 100644 library/ui/src/main/res/values-bs-rBA/strings.xml create mode 100644 library/ui/src/main/res/values-bs/strings.xml delete mode 100644 library/ui/src/main/res/values-et-rEE/strings.xml create mode 100644 library/ui/src/main/res/values-et/strings.xml delete mode 100644 library/ui/src/main/res/values-eu-rES/strings.xml create mode 100644 library/ui/src/main/res/values-eu/strings.xml delete mode 100644 library/ui/src/main/res/values-gl-rES/strings.xml create mode 100644 library/ui/src/main/res/values-gl/strings.xml delete mode 100644 library/ui/src/main/res/values-gu-rIN/strings.xml create mode 100644 library/ui/src/main/res/values-gu/strings.xml delete mode 100644 library/ui/src/main/res/values-hy-rAM/strings.xml create mode 100644 library/ui/src/main/res/values-hy/strings.xml delete mode 100644 library/ui/src/main/res/values-is-rIS/strings.xml create mode 100644 library/ui/src/main/res/values-is/strings.xml delete mode 100644 library/ui/src/main/res/values-ka-rGE/strings.xml create mode 100644 library/ui/src/main/res/values-ka/strings.xml delete mode 100644 library/ui/src/main/res/values-kk-rKZ/strings.xml create mode 100644 library/ui/src/main/res/values-kk/strings.xml delete mode 100644 library/ui/src/main/res/values-km-rKH/strings.xml create mode 100644 library/ui/src/main/res/values-km/strings.xml delete mode 100644 library/ui/src/main/res/values-kn-rIN/strings.xml create mode 100644 library/ui/src/main/res/values-kn/strings.xml delete mode 100644 library/ui/src/main/res/values-ky-rKG/strings.xml create mode 100644 library/ui/src/main/res/values-ky/strings.xml delete mode 100644 library/ui/src/main/res/values-lo-rLA/strings.xml create mode 100644 library/ui/src/main/res/values-lo/strings.xml delete mode 100644 library/ui/src/main/res/values-mk-rMK/strings.xml create mode 100644 library/ui/src/main/res/values-mk/strings.xml delete mode 100644 library/ui/src/main/res/values-ml-rIN/strings.xml create mode 100644 library/ui/src/main/res/values-ml/strings.xml delete mode 100644 library/ui/src/main/res/values-mn-rMN/strings.xml create mode 100644 library/ui/src/main/res/values-mn/strings.xml delete mode 100644 library/ui/src/main/res/values-mr-rIN/strings.xml create mode 100644 library/ui/src/main/res/values-mr/strings.xml delete mode 100644 library/ui/src/main/res/values-ms-rMY/strings.xml create mode 100644 library/ui/src/main/res/values-ms/strings.xml delete mode 100644 library/ui/src/main/res/values-my-rMM/strings.xml create mode 100644 library/ui/src/main/res/values-my/strings.xml delete mode 100644 library/ui/src/main/res/values-ne-rNP/strings.xml create mode 100644 library/ui/src/main/res/values-ne/strings.xml delete mode 100644 library/ui/src/main/res/values-pa-rIN/strings.xml create mode 100644 library/ui/src/main/res/values-pa/strings.xml delete mode 100644 library/ui/src/main/res/values-pt-rBR/strings.xml delete mode 100644 library/ui/src/main/res/values-si-rLK/strings.xml create mode 100644 library/ui/src/main/res/values-si/strings.xml delete mode 100644 library/ui/src/main/res/values-sq-rAL/strings.xml create mode 100644 library/ui/src/main/res/values-sq/strings.xml delete mode 100644 library/ui/src/main/res/values-ta-rIN/strings.xml create mode 100644 library/ui/src/main/res/values-ta/strings.xml delete mode 100644 library/ui/src/main/res/values-te-rIN/strings.xml create mode 100644 library/ui/src/main/res/values-te/strings.xml delete mode 100644 library/ui/src/main/res/values-ur-rPK/strings.xml create mode 100644 library/ui/src/main/res/values-ur/strings.xml delete mode 100644 library/ui/src/main/res/values-uz-rUZ/strings.xml create mode 100644 library/ui/src/main/res/values-uz/strings.xml diff --git a/extensions/mediasession/src/main/res/values-az-rAZ/strings.xml b/extensions/mediasession/src/main/res/values-az-rAZ/strings.xml deleted file mode 100644 index 34408143fa..0000000000 --- a/extensions/mediasession/src/main/res/values-az-rAZ/strings.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - "Bütün təkrarlayın" - "Təkrar bir" - "Heç bir təkrar" - diff --git a/extensions/mediasession/src/main/res/values-az/strings.xml b/extensions/mediasession/src/main/res/values-az/strings.xml new file mode 100644 index 0000000000..33c1f341ba --- /dev/null +++ b/extensions/mediasession/src/main/res/values-az/strings.xml @@ -0,0 +1,6 @@ + + + Heç biri təkrarlanmasın + Biri təkrarlansın + Hamısı təkrarlansın + diff --git a/extensions/mediasession/src/main/res/values-be-rBY/strings.xml b/extensions/mediasession/src/main/res/values-be-rBY/strings.xml deleted file mode 100644 index 2f05607235..0000000000 --- a/extensions/mediasession/src/main/res/values-be-rBY/strings.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - "Паўтарыць усё" - "Паўтараць ні" - "Паўтарыць адзін" - diff --git a/extensions/mediasession/src/main/res/values-be/strings.xml b/extensions/mediasession/src/main/res/values-be/strings.xml new file mode 100644 index 0000000000..380794f281 --- /dev/null +++ b/extensions/mediasession/src/main/res/values-be/strings.xml @@ -0,0 +1,6 @@ + + + Не паўтараць нічога + Паўтарыць адзін элемент + Паўтарыць усе + diff --git a/extensions/mediasession/src/main/res/values-bn-rBD/strings.xml b/extensions/mediasession/src/main/res/values-bn-rBD/strings.xml deleted file mode 100644 index 8872b464c6..0000000000 --- a/extensions/mediasession/src/main/res/values-bn-rBD/strings.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - "সবগুলির পুনরাবৃত্তি করুন" - "একটিরও পুনরাবৃত্তি করবেন না" - "একটির পুনরাবৃত্তি করুন" - diff --git a/extensions/mediasession/src/main/res/values-bn/strings.xml b/extensions/mediasession/src/main/res/values-bn/strings.xml new file mode 100644 index 0000000000..c39f11e570 --- /dev/null +++ b/extensions/mediasession/src/main/res/values-bn/strings.xml @@ -0,0 +1,6 @@ + + + কোনও আইটেম আবার চালাবেন না + একটি আইটেম আবার চালান + সবগুলি আইটেম আবার চালান + diff --git a/extensions/mediasession/src/main/res/values-bs-rBA/strings.xml b/extensions/mediasession/src/main/res/values-bs-rBA/strings.xml deleted file mode 100644 index d0bf068573..0000000000 --- a/extensions/mediasession/src/main/res/values-bs-rBA/strings.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - "Ponovite sve" - "Ne ponavljaju" - "Ponovite jedan" - diff --git a/extensions/mediasession/src/main/res/values-bs/strings.xml b/extensions/mediasession/src/main/res/values-bs/strings.xml new file mode 100644 index 0000000000..44b5cb5dd6 --- /dev/null +++ b/extensions/mediasession/src/main/res/values-bs/strings.xml @@ -0,0 +1,6 @@ + + + Ne ponavljaj + Ponovi jedno + Ponovi sve + diff --git a/extensions/mediasession/src/main/res/values-et-rEE/strings.xml b/extensions/mediasession/src/main/res/values-et-rEE/strings.xml deleted file mode 100644 index 1bc3b59706..0000000000 --- a/extensions/mediasession/src/main/res/values-et-rEE/strings.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - "Korda kõike" - "Ära korda midagi" - "Korda ühte" - diff --git a/extensions/mediasession/src/main/res/values-et/strings.xml b/extensions/mediasession/src/main/res/values-et/strings.xml new file mode 100644 index 0000000000..1f629e68f5 --- /dev/null +++ b/extensions/mediasession/src/main/res/values-et/strings.xml @@ -0,0 +1,6 @@ + + + Ära korda ühtegi + Korda ühte + Korda kõiki + diff --git a/extensions/mediasession/src/main/res/values-eu-rES/strings.xml b/extensions/mediasession/src/main/res/values-eu-rES/strings.xml deleted file mode 100644 index f15f03160f..0000000000 --- a/extensions/mediasession/src/main/res/values-eu-rES/strings.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - "Errepikatu guztiak" - "Ez errepikatu" - "Errepikatu bat" - diff --git a/extensions/mediasession/src/main/res/values-eu/strings.xml b/extensions/mediasession/src/main/res/values-eu/strings.xml new file mode 100644 index 0000000000..34c1b9cde9 --- /dev/null +++ b/extensions/mediasession/src/main/res/values-eu/strings.xml @@ -0,0 +1,6 @@ + + + Ez errepikatu + Errepikatu bat + Errepikatu guztiak + diff --git a/extensions/mediasession/src/main/res/values-gl-rES/strings.xml b/extensions/mediasession/src/main/res/values-gl-rES/strings.xml deleted file mode 100644 index 6b65b3e843..0000000000 --- a/extensions/mediasession/src/main/res/values-gl-rES/strings.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - "Repetir todo" - "Non repetir" - "Repetir un" - diff --git a/extensions/mediasession/src/main/res/values-gl/strings.xml b/extensions/mediasession/src/main/res/values-gl/strings.xml new file mode 100644 index 0000000000..633e9669a7 --- /dev/null +++ b/extensions/mediasession/src/main/res/values-gl/strings.xml @@ -0,0 +1,6 @@ + + + Non repetir + Repetir unha pista + Repetir todas as pistas + diff --git a/extensions/mediasession/src/main/res/values-gu-rIN/strings.xml b/extensions/mediasession/src/main/res/values-gu-rIN/strings.xml deleted file mode 100644 index 0eb9cab37e..0000000000 --- a/extensions/mediasession/src/main/res/values-gu-rIN/strings.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - "બધા પુનરાવર્તન કરો" - "કંઈ પુનરાવર્તન કરો" - "એક પુનરાવર્તન કરો" - diff --git a/extensions/mediasession/src/main/res/values-gu/strings.xml b/extensions/mediasession/src/main/res/values-gu/strings.xml new file mode 100644 index 0000000000..ab17db814e --- /dev/null +++ b/extensions/mediasession/src/main/res/values-gu/strings.xml @@ -0,0 +1,6 @@ + + + કોઈ રિપીટ કરતા નહીં + એક રિપીટ કરો + બધાને રિપીટ કરો + diff --git a/extensions/mediasession/src/main/res/values-hy-rAM/strings.xml b/extensions/mediasession/src/main/res/values-hy-rAM/strings.xml deleted file mode 100644 index 19a89e6c87..0000000000 --- a/extensions/mediasession/src/main/res/values-hy-rAM/strings.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - "կրկնել այն ամենը" - "Չկրկնել" - "Կրկնել մեկը" - diff --git a/extensions/mediasession/src/main/res/values-hy/strings.xml b/extensions/mediasession/src/main/res/values-hy/strings.xml new file mode 100644 index 0000000000..ba4fff8fd2 --- /dev/null +++ b/extensions/mediasession/src/main/res/values-hy/strings.xml @@ -0,0 +1,6 @@ + + + Չկրկնել + Կրկնել մեկը + Կրկնել բոլորը + diff --git a/extensions/mediasession/src/main/res/values-is-rIS/strings.xml b/extensions/mediasession/src/main/res/values-is-rIS/strings.xml deleted file mode 100644 index b200abbdb2..0000000000 --- a/extensions/mediasession/src/main/res/values-is-rIS/strings.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - "Endurtaka allt" - "Endurtaka ekkert" - "Endurtaka eitt" - diff --git a/extensions/mediasession/src/main/res/values-is/strings.xml b/extensions/mediasession/src/main/res/values-is/strings.xml new file mode 100644 index 0000000000..9db4df88dd --- /dev/null +++ b/extensions/mediasession/src/main/res/values-is/strings.xml @@ -0,0 +1,6 @@ + + + Endurtaka ekkert + Endurtaka eitt + Endurtaka allt + diff --git a/extensions/mediasession/src/main/res/values-ka-rGE/strings.xml b/extensions/mediasession/src/main/res/values-ka-rGE/strings.xml deleted file mode 100644 index 96656612a7..0000000000 --- a/extensions/mediasession/src/main/res/values-ka-rGE/strings.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - "გამეორება ყველა" - "გაიმეორეთ არცერთი" - "გაიმეორეთ ერთი" - diff --git a/extensions/mediasession/src/main/res/values-ka/strings.xml b/extensions/mediasession/src/main/res/values-ka/strings.xml new file mode 100644 index 0000000000..5acf78cbf2 --- /dev/null +++ b/extensions/mediasession/src/main/res/values-ka/strings.xml @@ -0,0 +1,6 @@ + + + არცერთის გამეორება + ერთის გამეორება + ყველას გამეორება + diff --git a/extensions/mediasession/src/main/res/values-kk-rKZ/strings.xml b/extensions/mediasession/src/main/res/values-kk-rKZ/strings.xml deleted file mode 100644 index be4140120d..0000000000 --- a/extensions/mediasession/src/main/res/values-kk-rKZ/strings.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - "Барлығын қайталау" - "Ешқайсысын қайталамау" - "Біреуін қайталау" - diff --git a/extensions/mediasession/src/main/res/values-kk/strings.xml b/extensions/mediasession/src/main/res/values-kk/strings.xml new file mode 100644 index 0000000000..d13ea893a0 --- /dev/null +++ b/extensions/mediasession/src/main/res/values-kk/strings.xml @@ -0,0 +1,6 @@ + + + Ешқайсысын қайталамау + Біреуін қайталау + Барлығын қайталау + diff --git a/extensions/mediasession/src/main/res/values-km-rKH/strings.xml b/extensions/mediasession/src/main/res/values-km-rKH/strings.xml deleted file mode 100644 index dd4b734e30..0000000000 --- a/extensions/mediasession/src/main/res/values-km-rKH/strings.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - "ធ្វើ​ម្ដង​ទៀត​ទាំងអស់" - "មិន​ធ្វើ​ឡើង​វិញ" - "ធ្វើ​​ឡើងវិញ​ម្ដង" - diff --git a/extensions/mediasession/src/main/res/values-km/strings.xml b/extensions/mediasession/src/main/res/values-km/strings.xml new file mode 100644 index 0000000000..8cf4a2d344 --- /dev/null +++ b/extensions/mediasession/src/main/res/values-km/strings.xml @@ -0,0 +1,6 @@ + + + មិន​លេង​ឡើងវិញ + លេង​ឡើង​វិញ​ម្ដង + លេង​ឡើងវិញ​ទាំងអស់ + diff --git a/extensions/mediasession/src/main/res/values-kn-rIN/strings.xml b/extensions/mediasession/src/main/res/values-kn-rIN/strings.xml deleted file mode 100644 index 3d79aca9e2..0000000000 --- a/extensions/mediasession/src/main/res/values-kn-rIN/strings.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - "ಎಲ್ಲವನ್ನು ಪುನರಾವರ್ತಿಸಿ" - "ಯಾವುದನ್ನೂ ಪುನರಾವರ್ತಿಸಬೇಡಿ" - "ಒಂದನ್ನು ಪುನರಾವರ್ತಿಸಿ" - diff --git a/extensions/mediasession/src/main/res/values-kn/strings.xml b/extensions/mediasession/src/main/res/values-kn/strings.xml new file mode 100644 index 0000000000..2dea20044a --- /dev/null +++ b/extensions/mediasession/src/main/res/values-kn/strings.xml @@ -0,0 +1,6 @@ + + + ಯಾವುದನ್ನೂ ಪುನರಾವರ್ತಿಸಬೇಡಿ + ಒಂದನ್ನು ಪುನರಾವರ್ತಿಸಿ + ಎಲ್ಲವನ್ನು ಪುನರಾವರ್ತಿಸಿ + diff --git a/extensions/mediasession/src/main/res/values-ky-rKG/strings.xml b/extensions/mediasession/src/main/res/values-ky-rKG/strings.xml deleted file mode 100644 index a8978ecc61..0000000000 --- a/extensions/mediasession/src/main/res/values-ky-rKG/strings.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - "Баарын кайталоо" - "Эч бирин кайталабоо" - "Бирөөнү кайталоо" - diff --git a/extensions/mediasession/src/main/res/values-ky/strings.xml b/extensions/mediasession/src/main/res/values-ky/strings.xml new file mode 100644 index 0000000000..9352c7c4ca --- /dev/null +++ b/extensions/mediasession/src/main/res/values-ky/strings.xml @@ -0,0 +1,6 @@ + + + Кайталанбасын + Бирөөнү кайталоо + Баарын кайталоо + diff --git a/extensions/mediasession/src/main/res/values-lo-rLA/strings.xml b/extensions/mediasession/src/main/res/values-lo-rLA/strings.xml deleted file mode 100644 index 950a9ba097..0000000000 --- a/extensions/mediasession/src/main/res/values-lo-rLA/strings.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - "ຫຼິ້ນ​ຊ້ຳ​ທັງ​ໝົດ" - "​ບໍ່ຫຼິ້ນ​ຊ້ຳ" - "ຫຼິ້ນ​ຊ້ຳ" - diff --git a/extensions/mediasession/src/main/res/values-lo/strings.xml b/extensions/mediasession/src/main/res/values-lo/strings.xml new file mode 100644 index 0000000000..e89ee44e5e --- /dev/null +++ b/extensions/mediasession/src/main/res/values-lo/strings.xml @@ -0,0 +1,6 @@ + + + ບໍ່ຫຼິ້ນຊ້ຳ + ຫຼິ້ນຊໍ້າ + ຫຼິ້ນຊ້ຳທັງໝົດ + diff --git a/extensions/mediasession/src/main/res/values-mk-rMK/strings.xml b/extensions/mediasession/src/main/res/values-mk-rMK/strings.xml deleted file mode 100644 index ddf2a60c20..0000000000 --- a/extensions/mediasession/src/main/res/values-mk-rMK/strings.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - "Повтори ги сите" - "Не повторувај ниту една" - "Повтори една" - diff --git a/extensions/mediasession/src/main/res/values-mk/strings.xml b/extensions/mediasession/src/main/res/values-mk/strings.xml new file mode 100644 index 0000000000..0906c35cc3 --- /dev/null +++ b/extensions/mediasession/src/main/res/values-mk/strings.xml @@ -0,0 +1,6 @@ + + + Не повторувај ниту една + Повтори една + Повтори ги сите + diff --git a/extensions/mediasession/src/main/res/values-ml-rIN/strings.xml b/extensions/mediasession/src/main/res/values-ml-rIN/strings.xml deleted file mode 100644 index 6f869e2931..0000000000 --- a/extensions/mediasession/src/main/res/values-ml-rIN/strings.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - "എല്ലാം ആവർത്തിക്കുക" - "ഒന്നും ആവർത്തിക്കരുത്" - "ഒന്ന് ആവർത്തിക്കുക" - diff --git a/extensions/mediasession/src/main/res/values-ml/strings.xml b/extensions/mediasession/src/main/res/values-ml/strings.xml new file mode 100644 index 0000000000..1f3f023c88 --- /dev/null +++ b/extensions/mediasession/src/main/res/values-ml/strings.xml @@ -0,0 +1,6 @@ + + + ഒന്നും ആവർത്തിക്കരുത് + ഒരെണ്ണം ആവർത്തിക്കുക + എല്ലാം ആവർത്തിക്കുക + diff --git a/extensions/mediasession/src/main/res/values-mn-rMN/strings.xml b/extensions/mediasession/src/main/res/values-mn-rMN/strings.xml deleted file mode 100644 index 8d3074b91a..0000000000 --- a/extensions/mediasession/src/main/res/values-mn-rMN/strings.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - "Бүгдийг давтах" - "Алийг нь ч давтахгүй" - "Нэгийг давтах" - diff --git a/extensions/mediasession/src/main/res/values-mn/strings.xml b/extensions/mediasession/src/main/res/values-mn/strings.xml new file mode 100644 index 0000000000..4167e40548 --- /dev/null +++ b/extensions/mediasession/src/main/res/values-mn/strings.xml @@ -0,0 +1,6 @@ + + + Алийг нь ч дахин тоглуулахгүй + Одоогийн тоглуулж буй медиаг дахин тоглуулах + Бүгдийг нь дахин тоглуулах + diff --git a/extensions/mediasession/src/main/res/values-mr-rIN/strings.xml b/extensions/mediasession/src/main/res/values-mr-rIN/strings.xml deleted file mode 100644 index 6e4bfccc16..0000000000 --- a/extensions/mediasession/src/main/res/values-mr-rIN/strings.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - "सर्व पुनरावृत्ती करा" - "काहीही पुनरावृत्ती करू नका" - "एक पुनरावृत्ती करा" - diff --git a/extensions/mediasession/src/main/res/values-mr/strings.xml b/extensions/mediasession/src/main/res/values-mr/strings.xml new file mode 100644 index 0000000000..fe42b346bf --- /dev/null +++ b/extensions/mediasession/src/main/res/values-mr/strings.xml @@ -0,0 +1,6 @@ + + + रीपीट करू नका + एक रीपीट करा + सर्व रीपीट करा + diff --git a/extensions/mediasession/src/main/res/values-ms-rMY/strings.xml b/extensions/mediasession/src/main/res/values-ms-rMY/strings.xml deleted file mode 100644 index 829542b668..0000000000 --- a/extensions/mediasession/src/main/res/values-ms-rMY/strings.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - "Ulang semua" - "Tiada ulangan" - "Ulangan" - diff --git a/extensions/mediasession/src/main/res/values-ms/strings.xml b/extensions/mediasession/src/main/res/values-ms/strings.xml new file mode 100644 index 0000000000..5735d50947 --- /dev/null +++ b/extensions/mediasession/src/main/res/values-ms/strings.xml @@ -0,0 +1,6 @@ + + + Jangan ulang + Ulang satu + Ulang semua + diff --git a/extensions/mediasession/src/main/res/values-my-rMM/strings.xml b/extensions/mediasession/src/main/res/values-my-rMM/strings.xml deleted file mode 100644 index aeb1375ebf..0000000000 --- a/extensions/mediasession/src/main/res/values-my-rMM/strings.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - "အားလုံး ထပ်တလဲလဲဖွင့်ရန်" - "ထပ်တလဲလဲမဖွင့်ရန်" - "တစ်ခုအား ထပ်တလဲလဲဖွင့်ရန်" - diff --git a/extensions/mediasession/src/main/res/values-my/strings.xml b/extensions/mediasession/src/main/res/values-my/strings.xml new file mode 100644 index 0000000000..11677e06f7 --- /dev/null +++ b/extensions/mediasession/src/main/res/values-my/strings.xml @@ -0,0 +1,6 @@ + + + မည်သည်ကိုမျှ ပြန်မကျော့ရန် + တစ်ခုကို ပြန်ကျော့ရန် + အားလုံး ပြန်ကျော့ရန် + diff --git a/extensions/mediasession/src/main/res/values-ne-rNP/strings.xml b/extensions/mediasession/src/main/res/values-ne-rNP/strings.xml deleted file mode 100644 index 6d81ce5684..0000000000 --- a/extensions/mediasession/src/main/res/values-ne-rNP/strings.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - "सबै दोहोर्याउनुहोस्" - "कुनै पनि नदोहोर्याउनुहोस्" - "एउटा दोहोर्याउनुहोस्" - diff --git a/extensions/mediasession/src/main/res/values-ne/strings.xml b/extensions/mediasession/src/main/res/values-ne/strings.xml new file mode 100644 index 0000000000..0ef156ed57 --- /dev/null +++ b/extensions/mediasession/src/main/res/values-ne/strings.xml @@ -0,0 +1,6 @@ + + + कुनै पनि नदोहोर्‍याउनुहोस् + एउटा दोहोर्‍याउनुहोस् + सबै दोहोर्‍याउनुहोस् + diff --git a/extensions/mediasession/src/main/res/values-pa-rIN/strings.xml b/extensions/mediasession/src/main/res/values-pa-rIN/strings.xml deleted file mode 100644 index 8eee0bee16..0000000000 --- a/extensions/mediasession/src/main/res/values-pa-rIN/strings.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - "ਸਭ ਨੂੰ ਦੁਹਰਾਓ" - "ਕੋਈ ਵੀ ਨਹੀਂ ਦੁਹਰਾਓ" - "ਇੱਕ ਦੁਹਰਾਓ" - diff --git a/extensions/mediasession/src/main/res/values-pa/strings.xml b/extensions/mediasession/src/main/res/values-pa/strings.xml new file mode 100644 index 0000000000..0b7d72841c --- /dev/null +++ b/extensions/mediasession/src/main/res/values-pa/strings.xml @@ -0,0 +1,6 @@ + + + ਕਿਸੇ ਨੂੰ ਨਾ ਦੁਹਰਾਓ + ਇੱਕ ਵਾਰ ਦੁਹਰਾਓ + ਸਾਰਿਆਂ ਨੂੰ ਦੁਹਰਾਓ + diff --git a/extensions/mediasession/src/main/res/values-pt-rBR/strings.xml b/extensions/mediasession/src/main/res/values-pt-rBR/strings.xml deleted file mode 100644 index efb8fc433f..0000000000 --- a/extensions/mediasession/src/main/res/values-pt-rBR/strings.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - "Repetir tudo" - "Não repetir" - "Repetir um" - diff --git a/extensions/mediasession/src/main/res/values-si-rLK/strings.xml b/extensions/mediasession/src/main/res/values-si-rLK/strings.xml deleted file mode 100644 index 8e172ac268..0000000000 --- a/extensions/mediasession/src/main/res/values-si-rLK/strings.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - "සියලු නැවත" - "කිසිවක් නැවත" - "නැවත නැවත එක්" - diff --git a/extensions/mediasession/src/main/res/values-si/strings.xml b/extensions/mediasession/src/main/res/values-si/strings.xml new file mode 100644 index 0000000000..0d86d38e7f --- /dev/null +++ b/extensions/mediasession/src/main/res/values-si/strings.xml @@ -0,0 +1,6 @@ + + + කිසිවක් පුනරාවර්තනය නොකරන්න + එකක් පුනරාවර්තනය කරන්න + සියල්ල නැවත කරන්න + diff --git a/extensions/mediasession/src/main/res/values-sq-rAL/strings.xml b/extensions/mediasession/src/main/res/values-sq-rAL/strings.xml deleted file mode 100644 index 6da24cc4c7..0000000000 --- a/extensions/mediasession/src/main/res/values-sq-rAL/strings.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - "Përsërit të gjithë" - "Përsëritni asnjë" - "Përsëritni një" - diff --git a/extensions/mediasession/src/main/res/values-sq/strings.xml b/extensions/mediasession/src/main/res/values-sq/strings.xml new file mode 100644 index 0000000000..2461dcf0ca --- /dev/null +++ b/extensions/mediasession/src/main/res/values-sq/strings.xml @@ -0,0 +1,6 @@ + + + Mos përsërit asnjë + Përsërit një + Përsërit të gjitha + diff --git a/extensions/mediasession/src/main/res/values-ta-rIN/strings.xml b/extensions/mediasession/src/main/res/values-ta-rIN/strings.xml deleted file mode 100644 index 9364bc0be2..0000000000 --- a/extensions/mediasession/src/main/res/values-ta-rIN/strings.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - "அனைத்தையும் மீண்டும் இயக்கு" - "எதையும் மீண்டும் இயக்காதே" - "ஒன்றை மட்டும் மீண்டும் இயக்கு" - diff --git a/extensions/mediasession/src/main/res/values-ta/strings.xml b/extensions/mediasession/src/main/res/values-ta/strings.xml new file mode 100644 index 0000000000..b6fbcca4a1 --- /dev/null +++ b/extensions/mediasession/src/main/res/values-ta/strings.xml @@ -0,0 +1,6 @@ + + + எதையும் மீண்டும் இயக்காதே + இதை மட்டும் மீண்டும் இயக்கு + அனைத்தையும் மீண்டும் இயக்கு + diff --git a/extensions/mediasession/src/main/res/values-te-rIN/strings.xml b/extensions/mediasession/src/main/res/values-te-rIN/strings.xml deleted file mode 100644 index b7ee7345d5..0000000000 --- a/extensions/mediasession/src/main/res/values-te-rIN/strings.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - "అన్నీ పునరావృతం చేయి" - "ఏదీ పునరావృతం చేయవద్దు" - "ఒకదాన్ని పునరావృతం చేయి" - diff --git a/extensions/mediasession/src/main/res/values-te/strings.xml b/extensions/mediasession/src/main/res/values-te/strings.xml new file mode 100644 index 0000000000..b1249c7400 --- /dev/null +++ b/extensions/mediasession/src/main/res/values-te/strings.xml @@ -0,0 +1,6 @@ + + + దేన్నీ పునరావృతం చేయకండి + ఒకదాన్ని పునరావృతం చేయండి + అన్నింటినీ పునరావృతం చేయండి + diff --git a/extensions/mediasession/src/main/res/values-ur-rPK/strings.xml b/extensions/mediasession/src/main/res/values-ur-rPK/strings.xml deleted file mode 100644 index ab2631a4ec..0000000000 --- a/extensions/mediasession/src/main/res/values-ur-rPK/strings.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - "سبھی کو دہرائیں" - "کسی کو نہ دہرائیں" - "ایک کو دہرائیں" - diff --git a/extensions/mediasession/src/main/res/values-ur/strings.xml b/extensions/mediasession/src/main/res/values-ur/strings.xml new file mode 100644 index 0000000000..3860986e9c --- /dev/null +++ b/extensions/mediasession/src/main/res/values-ur/strings.xml @@ -0,0 +1,6 @@ + + + کسی کو نہ دہرائیں + ایک کو دہرائیں + سبھی کو دہرائیں + diff --git a/extensions/mediasession/src/main/res/values-uz-rUZ/strings.xml b/extensions/mediasession/src/main/res/values-uz-rUZ/strings.xml deleted file mode 100644 index c32d00af8e..0000000000 --- a/extensions/mediasession/src/main/res/values-uz-rUZ/strings.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - "Barchasini takrorlash" - "Takrorlamaslik" - "Bir marta takrorlash" - diff --git a/extensions/mediasession/src/main/res/values-uz/strings.xml b/extensions/mediasession/src/main/res/values-uz/strings.xml new file mode 100644 index 0000000000..3424c9f583 --- /dev/null +++ b/extensions/mediasession/src/main/res/values-uz/strings.xml @@ -0,0 +1,6 @@ + + + Takrorlanmasin + Bittasini takrorlash + Hammasini takrorlash + diff --git a/library/ui/src/main/res/values-az-rAZ/strings.xml b/library/ui/src/main/res/values-az-rAZ/strings.xml deleted file mode 100644 index 1071cd5542..0000000000 --- a/library/ui/src/main/res/values-az-rAZ/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - "Öncəki trek" - "Növbəti trek" - "Pauza" - "Oyun" - "Dayandır" - "Geri sarıma" - "Sürətlə irəli" - "Bütün təkrarlayın" - "Təkrar bir" - "Heç bir təkrar" - "Qarışdır" - diff --git a/library/ui/src/main/res/values-az/strings.xml b/library/ui/src/main/res/values-az/strings.xml new file mode 100644 index 0000000000..cb1328f0ba --- /dev/null +++ b/library/ui/src/main/res/values-az/strings.xml @@ -0,0 +1,19 @@ + + + Əvvəlki trek + Növbəti trek + Pauza + Oxudun + Dayandırın + Geri çəkin + İrəli çəkin + Heç biri təkrarlanmasın + Biri təkrarlansın + Hamısı təkrarlansın + Qarışdırın + Tam ekran rejimi + Endirmə gözlənilir + Endirilir + Endirmə tamamlandı + Endirmə alınmadı + diff --git a/library/ui/src/main/res/values-be-rBY/strings.xml b/library/ui/src/main/res/values-be-rBY/strings.xml deleted file mode 100644 index 69b24ad5e9..0000000000 --- a/library/ui/src/main/res/values-be-rBY/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - "Папярэдні трэк" - "Наступны трэк" - "Прыпыніць" - "Прайграць" - "Спыніць" - "Перамотка назад" - "Перамотка ўперад" - "Паўтарыць усё" - "Паўтараць ні" - "Паўтарыць адзін" - "Перамяшаць" - diff --git a/library/ui/src/main/res/values-be/strings.xml b/library/ui/src/main/res/values-be/strings.xml new file mode 100644 index 0000000000..ce6d8b1cc8 --- /dev/null +++ b/library/ui/src/main/res/values-be/strings.xml @@ -0,0 +1,19 @@ + + + Папярэдні трэк + Наступны трэк + Паўза + Гуляць + Спыніць + Пераматаць назад + Пераматаць уперад + Не паўтараць нічога + Паўтарыць адзін элемент + Паўтарыць усе + Перамяшаць + Поўнаэкранны рэжым + Спампоўка пастаўлена ў чаргу + Спампоўка + Спампоўка завершана + Збой спампоўкі + diff --git a/library/ui/src/main/res/values-bn-rBD/strings.xml b/library/ui/src/main/res/values-bn-rBD/strings.xml deleted file mode 100644 index 446ef982a3..0000000000 --- a/library/ui/src/main/res/values-bn-rBD/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - "পূর্ববর্তী ট্র্যাক" - "পরবর্তী ট্র্যাক" - "বিরাম দিন" - "প্লে করুন" - "থামান" - "গুটিয়ে নিন" - "দ্রুত সামনে এগোন" - "সবগুলির পুনরাবৃত্তি করুন" - "একটিরও পুনরাবৃত্তি করবেন না" - "একটির পুনরাবৃত্তি করুন" - "অদলবদল" - diff --git a/library/ui/src/main/res/values-bn/strings.xml b/library/ui/src/main/res/values-bn/strings.xml new file mode 100644 index 0000000000..539837dc04 --- /dev/null +++ b/library/ui/src/main/res/values-bn/strings.xml @@ -0,0 +1,19 @@ + + + আগের ট্র্যাক + পরবর্তী ট্র্যাক + পজ করুন + চালান + থামান + পিছিয়ে যান + দ্রুত এগিয়ে যান + কোনও আইটেম আবার চালাবেন না + একটি আইটেম আবার চালান + সবগুলি আইটেম আবার চালান + শাফেল করুন + পূর্ণ স্ক্রিন মোড + ডাউনলোড অপেক্ষমান + ডাউনলোড হচ্ছে + ডাউনলোড হয়ে গেছে + ডাউনলোড করা যায়নি + diff --git a/library/ui/src/main/res/values-bs-rBA/strings.xml b/library/ui/src/main/res/values-bs-rBA/strings.xml deleted file mode 100644 index 186b1058d9..0000000000 --- a/library/ui/src/main/res/values-bs-rBA/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - "Prethodna numera" - "Sljedeća numera" - "Pauziraj" - "Reproduciraj" - "Zaustavi" - "Premotaj" - "Ubrzaj" - "Ponovite sve" - "Ne ponavljaju" - "Ponovite jedan" - "Izmiješaj" - diff --git a/library/ui/src/main/res/values-bs/strings.xml b/library/ui/src/main/res/values-bs/strings.xml new file mode 100644 index 0000000000..f7f4bf5cbb --- /dev/null +++ b/library/ui/src/main/res/values-bs/strings.xml @@ -0,0 +1,19 @@ + + + Prethodna numera + Sljedeća numera + Pauza + Reproduciranje + Zaustavljanje + Premotavanje unazad + Premotavanje unaprijed + Ne ponavljaj + Ponovi jedno + Ponovi sve + Izmiješaj + Način rada preko cijelog ekrana + Preuzimanje je na čekanju + Preuzimanje + Preuzimanje je završeno + Preuzimanje nije uspjelo + diff --git a/library/ui/src/main/res/values-et-rEE/strings.xml b/library/ui/src/main/res/values-et-rEE/strings.xml deleted file mode 100644 index 004ec7e6c3..0000000000 --- a/library/ui/src/main/res/values-et-rEE/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - "Eelmine lugu" - "Järgmine lugu" - "Peata" - "Esita" - "Peata" - "Keri tagasi" - "Keri edasi" - "Korda kõike" - "Ära korda midagi" - "Korda ühte" - "Esita juhuslikus järjekorras" - diff --git a/library/ui/src/main/res/values-et/strings.xml b/library/ui/src/main/res/values-et/strings.xml new file mode 100644 index 0000000000..79242a3f45 --- /dev/null +++ b/library/ui/src/main/res/values-et/strings.xml @@ -0,0 +1,19 @@ + + + Eelmine lugu + Järgmine lugu + Peata + Esita + Lõpeta + Keri tagasi + Keri edasi + Ära korda ühtegi + Korda ühte + Korda kõiki + Esita juhuslikus järjekorras + Täisekraani režiim + Allalaadimine on järjekorras + Allalaadimine + Allalaadimine lõpetati + Allalaadimine ebaõnnestus + diff --git a/library/ui/src/main/res/values-eu-rES/strings.xml b/library/ui/src/main/res/values-eu-rES/strings.xml deleted file mode 100644 index 6a3345303a..0000000000 --- a/library/ui/src/main/res/values-eu-rES/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - "Aurreko pista" - "Hurrengo pista" - "Pausatu" - "Erreproduzitu" - "Gelditu" - "Atzeratu" - "Aurreratu" - "Errepikatu guztiak" - "Ez errepikatu" - "Errepikatu bat" - "Erreproduzitu ausaz" - diff --git a/library/ui/src/main/res/values-eu/strings.xml b/library/ui/src/main/res/values-eu/strings.xml new file mode 100644 index 0000000000..1358f57059 --- /dev/null +++ b/library/ui/src/main/res/values-eu/strings.xml @@ -0,0 +1,19 @@ + + + Aurreko pista + Hurrengo pista + Pausatu + Erreproduzitu + Gelditu + Atzeratu + Aurreratu + Ez errepikatu + Errepikatu bat + Errepikatu guztiak + Erreproduzitu ausaz + Pantaila osoko modua + Ilaran dago deskarga + Deskargatzen + Osatu da deskarga + Ezin izan da deskargatu + diff --git a/library/ui/src/main/res/values-gl-rES/strings.xml b/library/ui/src/main/res/values-gl-rES/strings.xml deleted file mode 100644 index 7062d8d023..0000000000 --- a/library/ui/src/main/res/values-gl-rES/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - "Pista anterior" - "Seguinte pista" - "Pausar" - "Reproducir" - "Deter" - "Rebobinar" - "Avance rápido" - "Repetir todo" - "Non repetir" - "Repetir un" - "Reprodución aleatoria" - diff --git a/library/ui/src/main/res/values-gl/strings.xml b/library/ui/src/main/res/values-gl/strings.xml new file mode 100644 index 0000000000..5a0e83fbf7 --- /dev/null +++ b/library/ui/src/main/res/values-gl/strings.xml @@ -0,0 +1,19 @@ + + + Pista anterior + Pista seguinte + Pausar + Reproducir + Deter + Rebobinar + Avance rápido + Non repetir + Repetir unha pista + Repetir todas as pistas + Reprodución aleatoria + Modo de pantalla completa + A descarga está na cola + Descargando + Completouse a descarga + Produciuse un erro na descarga + diff --git a/library/ui/src/main/res/values-gu-rIN/strings.xml b/library/ui/src/main/res/values-gu-rIN/strings.xml deleted file mode 100644 index ed78b1ee30..0000000000 --- a/library/ui/src/main/res/values-gu-rIN/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - "પહેલાનો ટ્રૅક" - "આગલો ટ્રૅક" - "થોભો" - "ચલાવો" - "રોકો" - "રીવાઇન્ડ કરો" - "ઝડપી ફોરવર્ડ કરો" - "બધા પુનરાવર્તન કરો" - "કંઈ પુનરાવર્તન કરો" - "એક પુનરાવર્તન કરો" - "શફલ કરો" - diff --git a/library/ui/src/main/res/values-gu/strings.xml b/library/ui/src/main/res/values-gu/strings.xml new file mode 100644 index 0000000000..ae1618d830 --- /dev/null +++ b/library/ui/src/main/res/values-gu/strings.xml @@ -0,0 +1,19 @@ + + + પહેલાંનો ટ્રૅક + આગલો ટ્રૅક + થોભો + ચલાવો + રોકો + રિવાઇન્ડ કરો + ફાસ્ટ ફૉરવર્ડ કરો + કોઈ રિપીટ કરતા નહીં + એક રિપીટ કરો + બધાને રિપીટ કરો + શફલ કરો + પૂર્ણસ્ક્રીન મોડ + ડાઉનલોડ માટે કતારમાં છે + ડાઉનલોડ કરી રહ્યાં છીએ + ડાઉનલોડ પૂર્ણ થયું + ડાઉનલોડ નિષ્ફળ થયું + diff --git a/library/ui/src/main/res/values-hy-rAM/strings.xml b/library/ui/src/main/res/values-hy-rAM/strings.xml deleted file mode 100644 index 13a489baf5..0000000000 --- a/library/ui/src/main/res/values-hy-rAM/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - "Նախորդը" - "Հաջորդը" - "Դադարեցնել" - "Նվագարկել" - "Դադարեցնել" - "Հետ փաթաթել" - "Արագ առաջ անցնել" - "կրկնել այն ամենը" - "Չկրկնել" - "Կրկնել մեկը" - "Խառնել" - diff --git a/library/ui/src/main/res/values-hy/strings.xml b/library/ui/src/main/res/values-hy/strings.xml new file mode 100644 index 0000000000..73f8bff5e0 --- /dev/null +++ b/library/ui/src/main/res/values-hy/strings.xml @@ -0,0 +1,19 @@ + + + Նախորդը + Հաջորդը + Դադարեցնել + Նվագարկել + Կանգնեցնել + Հետ + Առաջ + Չկրկնել + Կրկնել մեկը + Կրկնել բոլորը + Խառնել + Լիաէկրան ռեժիմ + Ներբեռնումը շուտով կսկսվի + Ներբեռնում + Ներբեռնումն ավարտվեց + Չհաջողվեց ներբեռնել + diff --git a/library/ui/src/main/res/values-is-rIS/strings.xml b/library/ui/src/main/res/values-is-rIS/strings.xml deleted file mode 100644 index 12c4632cdf..0000000000 --- a/library/ui/src/main/res/values-is-rIS/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - "Fyrra lag" - "Næsta lag" - "Hlé" - "Spila" - "Stöðva" - "Spóla til baka" - "Spóla áfram" - "Endurtaka allt" - "Endurtaka ekkert" - "Endurtaka eitt" - "Stokka" - diff --git a/library/ui/src/main/res/values-is/strings.xml b/library/ui/src/main/res/values-is/strings.xml new file mode 100644 index 0000000000..e54fa9d7d4 --- /dev/null +++ b/library/ui/src/main/res/values-is/strings.xml @@ -0,0 +1,19 @@ + + + Fyrra lag + Næsta lag + Hlé + Spila + Stöðva + Spóla til baka + Spóla áfram + Endurtaka ekkert + Endurtaka eitt + Endurtaka allt + Stokka + Allur skjárinn + Niðurhal í bið + Sækir + Niðurhali lokið + Niðurhal mistókst + diff --git a/library/ui/src/main/res/values-ka-rGE/strings.xml b/library/ui/src/main/res/values-ka-rGE/strings.xml deleted file mode 100644 index 252e52f151..0000000000 --- a/library/ui/src/main/res/values-ka-rGE/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - "წინა ჩანაწერი" - "შემდეგი ჩანაწერი" - "პაუზა" - "დაკვრა" - "შეწყვეტა" - "უკან გადახვევა" - "წინ გადახვევა" - "გამეორება ყველა" - "გაიმეორეთ არცერთი" - "გაიმეორეთ ერთი" - "არეულად დაკვრა" - diff --git a/library/ui/src/main/res/values-ka/strings.xml b/library/ui/src/main/res/values-ka/strings.xml new file mode 100644 index 0000000000..772137785f --- /dev/null +++ b/library/ui/src/main/res/values-ka/strings.xml @@ -0,0 +1,19 @@ + + + წინა ჩანაწერი + შემდეგი ჩანაწერი + პაუზა + დაკვრა + შეწყვეტა + უკან გადახვევა + წინ გადახვევა + არცერთის გამეორება + ერთის გამეორება + ყველას გამეორება + არეულად დაკვრა + სრულეკრანიანი რეჟიმი + ჩამოტვირთვა რიგს ელოდება + მიმდინარეობს ჩამოტვირთვა + ჩამოტვირთვა დასრულდა + ჩამოტვირთვა ვერ მოხერხდა + diff --git a/library/ui/src/main/res/values-kk-rKZ/strings.xml b/library/ui/src/main/res/values-kk-rKZ/strings.xml deleted file mode 100644 index 43eb3dd030..0000000000 --- a/library/ui/src/main/res/values-kk-rKZ/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - "Алдыңғы трек" - "Келесі трек" - "Кідірту" - "Ойнату" - "Тоқтату" - "Кері айналдыру" - "Жылдам алға айналдыру" - "Барлығын қайталау" - "Ешқайсысын қайталамау" - "Біреуін қайталау" - "Кездейсоқ ретпен ойнату" - diff --git a/library/ui/src/main/res/values-kk/strings.xml b/library/ui/src/main/res/values-kk/strings.xml new file mode 100644 index 0000000000..62958a6315 --- /dev/null +++ b/library/ui/src/main/res/values-kk/strings.xml @@ -0,0 +1,19 @@ + + + Алдыңғы аудиотрек + Келесі аудиотрек + Кідірту + Ойнату + Тоқтату + Артқа айналдыру + Жылдам алға айналдыру + Ешқайсысын қайталамау + Біреуін қайталау + Барлығын қайталау + Араластыру + Толық экран режимі + Жүктеп алу кезегіне қойылды + Жүктеп алынуда + Жүктеп алынды + Жүктеп алынбады + diff --git a/library/ui/src/main/res/values-km-rKH/strings.xml b/library/ui/src/main/res/values-km-rKH/strings.xml deleted file mode 100644 index 653c9f051d..0000000000 --- a/library/ui/src/main/res/values-km-rKH/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - "បទ​មុន" - "បទ​បន្ទាប់" - "ផ្អាក" - "ចាក់" - "បញ្ឈប់" - "ខា​ថយក្រោយ" - "ទៅ​មុខ​​​រហ័ស" - "ធ្វើ​ម្ដង​ទៀត​ទាំងអស់" - "មិន​ធ្វើ​ឡើង​វិញ" - "ធ្វើ​​ឡើងវិញ​ម្ដង" - "ច្របល់" - diff --git a/library/ui/src/main/res/values-km/strings.xml b/library/ui/src/main/res/values-km/strings.xml new file mode 100644 index 0000000000..8e0360a1d7 --- /dev/null +++ b/library/ui/src/main/res/values-km/strings.xml @@ -0,0 +1,19 @@ + + + សំនៀង​​មុន + សំនៀង​បន្ទាប់ + ផ្អាក + លេង + ឈប់ + ខា​ថយ​ក្រោយ + ទៅ​មុខ​​​រហ័ស + មិន​លេង​ឡើងវិញ + លេង​ឡើង​វិញ​ម្ដង + លេង​ឡើងវិញ​ទាំងអស់ + ច្របល់ + មុខងារពេញ​អេក្រង់ + បាន​ដាក់ការទាញយក​​ក្នុងជួរ + កំពុង​ទាញ​យក + បាន​បញ្ចប់​ការទាញយក + មិន​អាច​ទាញយក​បាន​ទេ + diff --git a/library/ui/src/main/res/values-kn-rIN/strings.xml b/library/ui/src/main/res/values-kn-rIN/strings.xml deleted file mode 100644 index 7368fc8ad3..0000000000 --- a/library/ui/src/main/res/values-kn-rIN/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - "ಹಿಂದಿನ ಟ್ರ್ಯಾಕ್" - "ಮುಂದಿನ ಟ್ರ್ಯಾಕ್" - "ವಿರಾಮಗೊಳಿಸು" - "ಪ್ಲೇ ಮಾಡು" - "ನಿಲ್ಲಿಸು" - "ರಿವೈಂಡ್ ಮಾಡು" - "ವೇಗವಾಗಿ ಮುಂದಕ್ಕೆ" - "ಎಲ್ಲವನ್ನು ಪುನರಾವರ್ತಿಸಿ" - "ಯಾವುದನ್ನೂ ಪುನರಾವರ್ತಿಸಬೇಡಿ" - "ಒಂದನ್ನು ಪುನರಾವರ್ತಿಸಿ" - "ಬೆರೆಸು" - diff --git a/library/ui/src/main/res/values-kn/strings.xml b/library/ui/src/main/res/values-kn/strings.xml new file mode 100644 index 0000000000..ff3d29450a --- /dev/null +++ b/library/ui/src/main/res/values-kn/strings.xml @@ -0,0 +1,19 @@ + + + ಹಿಂದಿನ ಟ್ರ್ಯಾಕ್ + ಮುಂದಿನ ಟ್ರ್ಯಾಕ್ + ವಿರಾಮ + ಪ್ಲೇ + ನಿಲ್ಲಿಸಿ + ರಿವೈಂಡ್ + ಫಾಸ್ಟ್ ಫಾರ್ವರ್ಡ್ + ಯಾವುದನ್ನೂ ಪುನರಾವರ್ತಿಸಬೇಡಿ + ಒಂದನ್ನು ಪುನರಾವರ್ತಿಸಿ + ಎಲ್ಲವನ್ನು ಪುನರಾವರ್ತಿಸಿ + ಶಫಲ್‌ + ಪೂರ್ಣ ಪರದೆ ಮೋಡ್ + ಡೌನ್‌ಲೋಡ್ ಸರದಿಯಲ್ಲಿದೆ + ಡೌನ್‌ಲೋಡ್ ಮಾಡಲಾಗುತ್ತಿದೆ + ಡೌನ್‌ಲೋಡ್ ಪೂರ್ಣಗೊಂಡಿದೆ + ಡೌನ್‌ಲೋಡ್‌ ವಿಫಲಗೊಂಡಿದೆ + diff --git a/library/ui/src/main/res/values-ky-rKG/strings.xml b/library/ui/src/main/res/values-ky-rKG/strings.xml deleted file mode 100644 index 9b903a124e..0000000000 --- a/library/ui/src/main/res/values-ky-rKG/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - "Мурунку трек" - "Кийинки трек" - "Тындыруу" - "Ойнотуу" - "Токтотуу" - "Артка түрүү" - "Алдыга түрүү" - "Баарын кайталоо" - "Эч бирин кайталабоо" - "Бирөөнү кайталоо" - "Аралаштыруу" - diff --git a/library/ui/src/main/res/values-ky/strings.xml b/library/ui/src/main/res/values-ky/strings.xml new file mode 100644 index 0000000000..0582cce53c --- /dev/null +++ b/library/ui/src/main/res/values-ky/strings.xml @@ -0,0 +1,19 @@ + + + Мурунку трек + Кийинки трек + Тындыруу + Ойнотуу + Токтотуу + Артка түрүү + Алдыга түрүү + Кайталанбасын + Бирөөнү кайталоо + Баарын кайталоо + Аралаштыруу + Толук экран режими + Жүктөп алуу кезекке коюлду + Жүктөлүп алынууда + Жүктөп алуу аяктады + Жүктөлүп алынбай калды + diff --git a/library/ui/src/main/res/values-lo-rLA/strings.xml b/library/ui/src/main/res/values-lo-rLA/strings.xml deleted file mode 100644 index 702cd54396..0000000000 --- a/library/ui/src/main/res/values-lo-rLA/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - "​ເພງ​ກ່ອນ​ໜ້າ" - "​ເພງ​ຕໍ່​ໄປ" - "ຢຸດຊົ່ວຄາວ" - "ຫຼິ້ນ" - "ຢຸດ" - "​ຣີ​​ວາຍກັບ" - "ເລື່ອນ​ໄປ​ໜ້າ" - "ຫຼິ້ນ​ຊ້ຳ​ທັງ​ໝົດ" - "​ບໍ່ຫຼິ້ນ​ຊ້ຳ" - "ຫຼິ້ນ​ຊ້ຳ" - "ຫຼີ້ນແບບສຸ່ມ" - diff --git a/library/ui/src/main/res/values-lo/strings.xml b/library/ui/src/main/res/values-lo/strings.xml new file mode 100644 index 0000000000..ef888ab7f0 --- /dev/null +++ b/library/ui/src/main/res/values-lo/strings.xml @@ -0,0 +1,19 @@ + + + ເພງກ່ອນໜ້າ + ເພງຕໍ່ໄປ + ຢຸດຊົ່ວຄາວ + ຫຼິ້ນ + ຢຸດ + ຍ້ອນກັບ + ເລື່ອນໄປໜ້າ + ບໍ່ຫຼິ້ນຊ້ຳ + ຫຼິ້ນຊໍ້າ + ຫຼິ້ນຊ້ຳທັງໝົດ + ຫຼີ້ນແບບສຸ່ມ + ໂໝດເຕັມຈໍ + ຈັດຄິວດາວໂຫລດໄວ້ແລ້ວ + ກຳລັງດາວໂຫລດ + ດາວໂຫລດສຳເລັດແລ້ວ + ດາວໂຫຼດບໍ່ສຳເລັດ + diff --git a/library/ui/src/main/res/values-mk-rMK/strings.xml b/library/ui/src/main/res/values-mk-rMK/strings.xml deleted file mode 100644 index 60858df8b1..0000000000 --- a/library/ui/src/main/res/values-mk-rMK/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - "Претходна песна" - "Следна песна" - "Пауза" - "Пушти" - "Запри" - "Премотај назад" - "Брзо премотај напред" - "Повтори ги сите" - "Не повторувај ниту една" - "Повтори една" - "По случаен избор" - diff --git a/library/ui/src/main/res/values-mk/strings.xml b/library/ui/src/main/res/values-mk/strings.xml new file mode 100644 index 0000000000..a014e1c0d1 --- /dev/null +++ b/library/ui/src/main/res/values-mk/strings.xml @@ -0,0 +1,19 @@ + + + Претходна песна + Следна песна + Пауза + Пушти + Сопри + Премотај наназад + Премотај напред + Не повторувај ниту една + Повтори една + Повтори ги сите + Измешај + Режим на цел екран + Преземањето чека на ред + Се презема + Преземањето заврши + Неуспешно преземање + diff --git a/library/ui/src/main/res/values-ml-rIN/strings.xml b/library/ui/src/main/res/values-ml-rIN/strings.xml deleted file mode 100644 index 4e5eddb93e..0000000000 --- a/library/ui/src/main/res/values-ml-rIN/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - "മുമ്പത്തെ ട്രാക്ക്" - "അടുത്ത ട്രാക്ക്" - "താൽക്കാലികമായി നിർത്തുക" - "പ്ലേ ചെയ്യുക" - "നിര്‍ത്തുക" - "റിവൈൻഡുചെയ്യുക" - "വേഗത്തിലുള്ള കൈമാറൽ" - "എല്ലാം ആവർത്തിക്കുക" - "ഒന്നും ആവർത്തിക്കരുത്" - "ഒന്ന് ആവർത്തിക്കുക" - "ഷഫിൾ ചെയ്യുക" - diff --git a/library/ui/src/main/res/values-ml/strings.xml b/library/ui/src/main/res/values-ml/strings.xml new file mode 100644 index 0000000000..37ea2be5e6 --- /dev/null +++ b/library/ui/src/main/res/values-ml/strings.xml @@ -0,0 +1,19 @@ + + + മുമ്പത്തെ ട്രാക്ക് + അടുത്ത ട്രാക്ക് + തൽക്കാലം നിർത്തുക + പ്ലേ ചെയ്യുക + നിര്‍ത്തുക + പിന്നിലേക്ക് പോവുക + വേഗത്തിൽ മുന്നോട്ട് പോവുക + ഒന്നും ആവർത്തിക്കരുത് + ഒരെണ്ണം ആവർത്തിക്കുക + എല്ലാം ആവർത്തിക്കുക + ഇടകലര്‍ത്തുക + പൂർണ്ണ സ്‌ക്രീൻ മോഡ് + ഡൗൺലോഡ് ‌ക്യൂവിലാണ് + ഡൗൺലോഡ് ചെയ്യുന്നു + ഡൗൺലോഡ് പൂർത്തിയായി + ഡൗൺലോഡ് പരാജയപ്പെട്ടു + diff --git a/library/ui/src/main/res/values-mn-rMN/strings.xml b/library/ui/src/main/res/values-mn-rMN/strings.xml deleted file mode 100644 index 4ab26a7f62..0000000000 --- a/library/ui/src/main/res/values-mn-rMN/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - "Өмнөх трек" - "Дараагийн трек" - "Түр зогсоох" - "Тоглуулах" - "Зогсоох" - "Буцааж хураах" - "Хурдан урагшлуулах" - "Бүгдийг давтах" - "Алийг нь ч давтахгүй" - "Нэгийг давтах" - "Холих" - diff --git a/library/ui/src/main/res/values-mn/strings.xml b/library/ui/src/main/res/values-mn/strings.xml new file mode 100644 index 0000000000..883489b53c --- /dev/null +++ b/library/ui/src/main/res/values-mn/strings.xml @@ -0,0 +1,19 @@ + + + Өмнөх бичлэг + Дараагийн бичлэг + Түр зогсоох + Тоглуулах + Зогсоох + Ухраах + Хурдан урагшлуулах + Алийг нь ч дахин тоглуулахгүй + Одоогийн тоглуулж буй медиаг дахин тоглуулах + Бүгдийг нь дахин тоглуулах + Холих + Бүтэн дэлгэцийн горим + Татан авалтыг жагсаасан + Татаж байна + Татаж дууссан + Татаж чадсангүй + diff --git a/library/ui/src/main/res/values-mr-rIN/strings.xml b/library/ui/src/main/res/values-mr-rIN/strings.xml deleted file mode 100644 index 7869355b59..0000000000 --- a/library/ui/src/main/res/values-mr-rIN/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - "मागील ट्रॅक" - "पुढील ट्रॅक" - "विराम द्या" - "प्ले करा" - "थांबा" - "रिवाईँड करा" - "फास्ट फॉरवर्ड करा" - "सर्व पुनरावृत्ती करा" - "काहीही पुनरावृत्ती करू नका" - "एक पुनरावृत्ती करा" - "शफल करा" - diff --git a/library/ui/src/main/res/values-mr/strings.xml b/library/ui/src/main/res/values-mr/strings.xml new file mode 100644 index 0000000000..ed9e44bbce --- /dev/null +++ b/library/ui/src/main/res/values-mr/strings.xml @@ -0,0 +1,19 @@ + + + मागील ट्रॅक + पुढील ट्रॅक + विराम + प्‍ले करा + थांबा + रीवाइंड करा + फास्ट फॉरवर्ड करा + रीपीट करू नका + एक रीपीट करा + सर्व रीपीट करा + शफल करा + पूर्ण स्क्रीन मोड + रांगेत लावलेले डाउनलोड करा + डाउनलोड होत आहे + डाउनलोड पूर्ण झाले + डाउनलोड अयशस्वी झाले + diff --git a/library/ui/src/main/res/values-ms-rMY/strings.xml b/library/ui/src/main/res/values-ms-rMY/strings.xml deleted file mode 100644 index fdde3de079..0000000000 --- a/library/ui/src/main/res/values-ms-rMY/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - "Lagu sebelumnya" - "Lagu seterusnya" - "Jeda" - "Main" - "Berhenti" - "Gulung semula" - "Mara laju" - "Ulang semua" - "Tiada ulangan" - "Ulangan" - "Rombak" - diff --git a/library/ui/src/main/res/values-ms/strings.xml b/library/ui/src/main/res/values-ms/strings.xml new file mode 100644 index 0000000000..aedb67fe92 --- /dev/null +++ b/library/ui/src/main/res/values-ms/strings.xml @@ -0,0 +1,19 @@ + + + Lagu sebelumnya + Lagu seterusnya + Jeda + Main + Berhenti + Mandir + Mundar laju + Jangan ulang + Ulang satu + Ulang semua + Rombak + Mod skrin penuh + Muat turun dibaris gilir + Memuat turun + Muat turun selesai + Muat turun gagal + diff --git a/library/ui/src/main/res/values-my-rMM/strings.xml b/library/ui/src/main/res/values-my-rMM/strings.xml deleted file mode 100644 index 3d7918d953..0000000000 --- a/library/ui/src/main/res/values-my-rMM/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - "ယခင် တစ်ပုဒ်" - "နောက် တစ်ပုဒ်" - "ခဏရပ်ရန်" - "ဖွင့်ရန်" - "ရပ်ရန်" - "ပြန်ရစ်ရန်" - "ရှေ့သို့ သွားရန်" - "အားလုံး ထပ်တလဲလဲဖွင့်ရန်" - "ထပ်တလဲလဲမဖွင့်ရန်" - "တစ်ခုအား ထပ်တလဲလဲဖွင့်ရန်" - "မွှေနှောက်ဖွင့်ရန်" - diff --git a/library/ui/src/main/res/values-my/strings.xml b/library/ui/src/main/res/values-my/strings.xml new file mode 100644 index 0000000000..fe509be395 --- /dev/null +++ b/library/ui/src/main/res/values-my/strings.xml @@ -0,0 +1,19 @@ + + + ယခင် တစ်ပုဒ် + နောက် တစ်ပုဒ် + ခဏရပ်ရန် + ဖွင့်ရန် + ရပ်ရန် + ပြန်ရစ်ရန် + ရှေ့သို့ အမြန်သွားရန် + မည်သည်ကိုမျှ ပြန်မကျော့ရန် + တစ်ခုကို ပြန်ကျော့ရန် + အားလုံး ပြန်ကျော့ရန် + ရောသမမွှေ + မျက်နှာပြင်အပြည့် မုဒ် + ဒေါင်းလုဒ်လုပ်ရန် စီထားသည် + ဒေါင်းလုဒ်လုပ်နေသည် + ဒေါင်းလုဒ်လုပ်ပြီးပါပြီ + ဒေါင်းလုဒ်လုပ်၍ မရပါ + diff --git a/library/ui/src/main/res/values-ne-rNP/strings.xml b/library/ui/src/main/res/values-ne-rNP/strings.xml deleted file mode 100644 index 19f43d0392..0000000000 --- a/library/ui/src/main/res/values-ne-rNP/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - "अघिल्लो ट्रयाक" - "अर्को ट्रयाक" - "रोक्नुहोस्" - "चलाउनुहोस्" - "रोक्नुहोस्" - "दोहोर्याउनुहोस्" - "फास्ट फर्वार्ड" - "सबै दोहोर्याउनुहोस्" - "कुनै पनि नदोहोर्याउनुहोस्" - "एउटा दोहोर्याउनुहोस्" - "मिसाउनुहोस्" - diff --git a/library/ui/src/main/res/values-ne/strings.xml b/library/ui/src/main/res/values-ne/strings.xml new file mode 100644 index 0000000000..ac3d71d356 --- /dev/null +++ b/library/ui/src/main/res/values-ne/strings.xml @@ -0,0 +1,19 @@ + + + अघिल्लो ट्रयाक + अर्को ट्र्याक + पज गर्नुहोस् + प्ले गर्नुहोस् + रोक्नुहोस् + रिवाइन्ड गर्नुहोस् + फास्ट फर्वार्ड गर्नुहोस् + कुनै पनि नदोहोर्‍याउनुहोस् + एउटा दोहोर्‍याउनुहोस् + सबै दोहोर्‍याउनुहोस् + मिसाउनुहोस् + पूर्ण स्क्रिन मोड + डाउनलोडलाई लाइनमा राखियो + डाउनलोड गरिँदै छ + डाउनलोड सम्पन्न भयो + डाउनलोड गर्न सकिएन + diff --git a/library/ui/src/main/res/values-pa-rIN/strings.xml b/library/ui/src/main/res/values-pa-rIN/strings.xml deleted file mode 100644 index 6250b90514..0000000000 --- a/library/ui/src/main/res/values-pa-rIN/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - "ਪਿਛਲਾ ਟਰੈਕ" - "ਅਗਲਾ ਟਰੈਕ" - "ਰੋਕੋ" - "ਪਲੇ ਕਰੋ" - "ਰੋਕੋ" - "ਰੀਵਾਈਂਡ ਕਰੋ" - "ਅੱਗੇ ਭੇਜੋ" - "ਸਭ ਨੂੰ ਦੁਹਰਾਓ" - "ਕੋਈ ਵੀ ਨਹੀਂ ਦੁਹਰਾਓ" - "ਇੱਕ ਦੁਹਰਾਓ" - "ਸ਼ੱਫਲ" - diff --git a/library/ui/src/main/res/values-pa/strings.xml b/library/ui/src/main/res/values-pa/strings.xml new file mode 100644 index 0000000000..59affc3aa9 --- /dev/null +++ b/library/ui/src/main/res/values-pa/strings.xml @@ -0,0 +1,19 @@ + + + ਪਿਛਲਾ ਟਰੈਕ + ਅਗਲਾ ਟਰੈਕ + ਰੋਕੋ + ਚਲਾਓ + ਬੰਦ ਕਰੋ + ਪਿੱਛੇ ਕਰੋ + ਤੇਜ਼ੀ ਨਾਲ ਅੱਗੇ ਕਰੋ + ਕਿਸੇ ਨੂੰ ਨਾ ਦੁਹਰਾਓ + ਇੱਕ ਵਾਰ ਦੁਹਰਾਓ + ਸਾਰਿਆਂ ਨੂੰ ਦੁਹਰਾਓ + ਬੇਤਰਤੀਬ ਕਰੋ + ਪੂਰੀ-ਸਕ੍ਰੀਨ ਮੋਡ + ਡਾਊਨਲੋਡ ਕਤਾਰਬੱਧ ਕੀਤਾ ਗਿਆ + ਡਾਊਨਲੋਡ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ + ਡਾਊਨਲੋਡ ਮੁਕੰਮਲ ਹੋਇਆ + ਡਾਊਨਲੋਡ ਅਸਫਲ ਰਿਹਾ + diff --git a/library/ui/src/main/res/values-pt-rBR/strings.xml b/library/ui/src/main/res/values-pt-rBR/strings.xml deleted file mode 100644 index 86a91b0677..0000000000 --- a/library/ui/src/main/res/values-pt-rBR/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - "Faixa anterior" - "Próxima faixa" - "Pausar" - "Reproduzir" - "Parar" - "Retroceder" - "Avançar" - "Repetir tudo" - "Não repetir" - "Repetir um" - "Reproduzir aleatoriamente" - diff --git a/library/ui/src/main/res/values-si-rLK/strings.xml b/library/ui/src/main/res/values-si-rLK/strings.xml deleted file mode 100644 index eb8453b156..0000000000 --- a/library/ui/src/main/res/values-si-rLK/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - "පෙර ගීතය" - "ඊළඟ ගීතය" - "විරාමය" - "ධාවනය කරන්න" - "නතර කරන්න" - "නැවත ඔතන්න" - "වේගයෙන් ඉදිරියට යන" - "සියලු නැවත" - "කිසිවක් නැවත" - "නැවත නැවත එක්" - "කලවම් කරන්න" - diff --git a/library/ui/src/main/res/values-si/strings.xml b/library/ui/src/main/res/values-si/strings.xml new file mode 100644 index 0000000000..0ccc0b9d26 --- /dev/null +++ b/library/ui/src/main/res/values-si/strings.xml @@ -0,0 +1,19 @@ + + + පෙර ඛණ්ඩය + ඊළඟ ඛණ්ඩය + විරාම කරන්න + ධාවනය කරන්න + නවත්වන්න + නැවත ඔතන්න + වේගයෙන් ඉදිරියට + කිසිවක් පුනරාවර්තනය නොකරන්න + එකක් පුනරාවර්තනය කරන්න + සියල්ල පුනරාවර්තනය කරන්න + කලවම් කරන්න + සම්පූර්ණ තිර ප්‍රකාරය + බාගැනීම පේළියට තබන ලදී + බාගනිමින් + බාගැනීම සම්පූර්ණ කරන ලදී + බාගැනීම අසමත් විය + diff --git a/library/ui/src/main/res/values-sq-rAL/strings.xml b/library/ui/src/main/res/values-sq-rAL/strings.xml deleted file mode 100644 index e2d209e10b..0000000000 --- a/library/ui/src/main/res/values-sq-rAL/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - "Kënga e mëparshme" - "Kënga tjetër" - "Pauzë" - "Luaj" - "Ndalo" - "Kthehu pas" - "Përparo me shpejtësi" - "Përsërit të gjithë" - "Përsëritni asnjë" - "Përsëritni një" - "Përziej" - diff --git a/library/ui/src/main/res/values-sq/strings.xml b/library/ui/src/main/res/values-sq/strings.xml new file mode 100644 index 0000000000..56868ce503 --- /dev/null +++ b/library/ui/src/main/res/values-sq/strings.xml @@ -0,0 +1,19 @@ + + + Kënga e mëparshme + Kënga tjetër + Pauzë + Luaj + Ndalo + Rikthe + Përparo me shpejtësi + Mos përsërit asnjë + Përsërit një + Përsërit të gjitha + Përziej + Modaliteti me ekran të plotë + Shkarkimi u vendos në radhë + Po shkarkohet + Shkarkimi përfundoi + Shkarkimi dështoi + diff --git a/library/ui/src/main/res/values-ta-rIN/strings.xml b/library/ui/src/main/res/values-ta-rIN/strings.xml deleted file mode 100644 index 43a925aa2e..0000000000 --- a/library/ui/src/main/res/values-ta-rIN/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - "முந்தைய ட்ராக்" - "அடுத்த ட்ராக்" - "இடைநிறுத்து" - "இயக்கு" - "நிறுத்து" - "மீண்டும் காட்டு" - "வேகமாக முன்செல்" - "அனைத்தையும் மீண்டும் இயக்கு" - "எதையும் மீண்டும் இயக்காதே" - "ஒன்றை மட்டும் மீண்டும் இயக்கு" - "குலை" - diff --git a/library/ui/src/main/res/values-ta/strings.xml b/library/ui/src/main/res/values-ta/strings.xml new file mode 100644 index 0000000000..65f02a38a2 --- /dev/null +++ b/library/ui/src/main/res/values-ta/strings.xml @@ -0,0 +1,19 @@ + + + முந்தைய டிராக் + அடுத்த டிராக் + இடைநிறுத்து + இயக்கு + நிறுத்து + பின்செல் + வேகமாக முன்செல் + எதையும் மீண்டும் இயக்காதே + இதை மட்டும் மீண்டும் இயக்கு + அனைத்தையும் மீண்டும் இயக்கு + கலைத்துப் போடு + முழுத்திரைப் பயன்முறை + பதிவிறக்கம், வரிசையில் உள்ளது + பதிவிறக்கப்படுகிறது + பதிவிறக்கப்பட்டது + பதிவிறக்க முடியவில்லை + diff --git a/library/ui/src/main/res/values-te-rIN/strings.xml b/library/ui/src/main/res/values-te-rIN/strings.xml deleted file mode 100644 index 8541a44553..0000000000 --- a/library/ui/src/main/res/values-te-rIN/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - "మునుపటి ట్రాక్" - "తదుపరి ట్రాక్" - "పాజ్ చేయి" - "ప్లే చేయి" - "ఆపివేయి" - "రివైండ్ చేయి" - "వేగంగా ఫార్వార్డ్ చేయి" - "అన్నీ పునరావృతం చేయి" - "ఏదీ పునరావృతం చేయవద్దు" - "ఒకదాన్ని పునరావృతం చేయి" - "షఫుల్ చేయి" - diff --git a/library/ui/src/main/res/values-te/strings.xml b/library/ui/src/main/res/values-te/strings.xml new file mode 100644 index 0000000000..d27afe9ce7 --- /dev/null +++ b/library/ui/src/main/res/values-te/strings.xml @@ -0,0 +1,19 @@ + + + మునుపటి ట్రాక్ + తదుపరి ట్రాక్ + పాజ్ చేయండి + ప్లే చేయండి + ఆపండి + రివైండ్ చేయండి + వేగంగా ఫార్వార్డ్ చేయండి + దేన్నీ పునరావృతం చేయకండి + ఒకదాన్ని పునరావృతం చేయండి + అన్నింటినీ పునరావృతం చేయండి + షఫుల్ చేయండి + పూర్తి స్క్రీన్ మోడ్ + డౌన్‌లోడ్ క్రమవరుసలో ఉంది + డౌన్‌లోడ్ చేస్తోంది + డౌన్‌లోడ్ పూర్తయింది + డౌన్‌లోడ్ విఫలమైంది + diff --git a/library/ui/src/main/res/values-ur-rPK/strings.xml b/library/ui/src/main/res/values-ur-rPK/strings.xml deleted file mode 100644 index f253e56c00..0000000000 --- a/library/ui/src/main/res/values-ur-rPK/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - "پچھلا ٹریک" - "اگلا ٹریک" - "موقوف کریں" - "چلائیں" - "روکیں" - "ریوائینڈ کریں" - "تیزی سے فارورڈ کریں" - "سبھی کو دہرائیں" - "کسی کو نہ دہرائیں" - "ایک کو دہرائیں" - "شفل کریں" - diff --git a/library/ui/src/main/res/values-ur/strings.xml b/library/ui/src/main/res/values-ur/strings.xml new file mode 100644 index 0000000000..26e1b0e060 --- /dev/null +++ b/library/ui/src/main/res/values-ur/strings.xml @@ -0,0 +1,19 @@ + + + پچھلا ٹریک + اگلا ٹریک + موقوف کریں + چلائیں + روکیں + ریوائینڈ کریں + تیزی سے فارورڈ کریں + کسی کو نہ دہرائیں + ایک کو دہرائیں + سبھی کو دہرائیں + شفل کریں + پوری اسکرین والی وضع + ڈاؤن لوڈ قطار بند ہے + ڈاؤن لوڈ کیا جا رہا ہے + ڈاؤن لوڈ مکمل ہو گیا + ڈاؤن لوڈ ناکام ہو گیا + diff --git a/library/ui/src/main/res/values-uz-rUZ/strings.xml b/library/ui/src/main/res/values-uz-rUZ/strings.xml deleted file mode 100644 index a322690b2d..0000000000 --- a/library/ui/src/main/res/values-uz-rUZ/strings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - "Avvalgi musiqa" - "Keyingi musiqa" - "To‘xtatib turish" - "Ijro qilish" - "To‘xtatish" - "Orqaga o‘tkazish" - "Oldinga o‘tkazish" - "Barchasini takrorlash" - "Takrorlamaslik" - "Bir marta takrorlash" - "Tasodifiy tartibda" - diff --git a/library/ui/src/main/res/values-uz/strings.xml b/library/ui/src/main/res/values-uz/strings.xml new file mode 100644 index 0000000000..26d37380d7 --- /dev/null +++ b/library/ui/src/main/res/values-uz/strings.xml @@ -0,0 +1,19 @@ + + + Avvalgi trek + Keyingi trek + Pauza + Ijro + To‘xtatish + Orqaga qaytarish + Oldinga o‘tkazish + Takrorlanmasin + Bittasini takrorlash + Hammasini takrorlash + Aralash + Butun ekran rejimi + Yuklab olish navbatga olindi + Yuklab olinmoqda + Yuklab olindi + Yuklab olinmadi + From fec7d32836dafd432894efe23d6cfeab1923d73f Mon Sep 17 00:00:00 2001 From: olly Date: Mon, 16 Apr 2018 08:46:22 -0700 Subject: [PATCH 050/157] Remove TrackSelection.Factory from SelectionOverride Issue: #3915 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=193045534 --- .../exoplayer2/demo/PlayerActivity.java | 2 +- .../exoplayer2/demo/TrackSelectionHelper.java | 13 ++------- .../trackselection/DefaultTrackSelector.java | 28 ++++++++----------- .../trackselection/MappingTrackSelector.java | 11 -------- 4 files changed, 16 insertions(+), 38 deletions(-) diff --git a/demos/main/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java b/demos/main/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java index 4765278fcb..9375f9bba6 100644 --- a/demos/main/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java +++ b/demos/main/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java @@ -361,7 +361,7 @@ public class PlayerActivity extends Activity new DefaultRenderersFactory(this, extensionRendererMode); trackSelector = new DefaultTrackSelector(trackSelectionFactory); - trackSelectionHelper = new TrackSelectionHelper(trackSelector, trackSelectionFactory); + trackSelectionHelper = new TrackSelectionHelper(trackSelector); lastSeenTrackGroupArray = null; player = diff --git a/demos/main/src/main/java/com/google/android/exoplayer2/demo/TrackSelectionHelper.java b/demos/main/src/main/java/com/google/android/exoplayer2/demo/TrackSelectionHelper.java index 89b008dbfe..d12d132c16 100644 --- a/demos/main/src/main/java/com/google/android/exoplayer2/demo/TrackSelectionHelper.java +++ b/demos/main/src/main/java/com/google/android/exoplayer2/demo/TrackSelectionHelper.java @@ -32,7 +32,6 @@ import com.google.android.exoplayer2.source.TrackGroupArray; import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; import com.google.android.exoplayer2.trackselection.DefaultTrackSelector.SelectionOverride; import com.google.android.exoplayer2.trackselection.MappingTrackSelector.MappedTrackInfo; -import com.google.android.exoplayer2.trackselection.TrackSelection; import java.util.Arrays; /** @@ -42,7 +41,6 @@ import java.util.Arrays; DialogInterface.OnClickListener { private final DefaultTrackSelector selector; - private final TrackSelection.Factory trackSelectionFactory; private MappedTrackInfo trackInfo; private int rendererIndex; @@ -55,14 +53,9 @@ import java.util.Arrays; private CheckedTextView defaultView; private CheckedTextView[][] trackViews; - /** - * @param selector The track selector. - * @param trackSelectionFactory A factory for overriding {@link TrackSelection}s. - */ - public TrackSelectionHelper( - DefaultTrackSelector selector, TrackSelection.Factory trackSelectionFactory) { + /** @param selector The track selector. */ + public TrackSelectionHelper(DefaultTrackSelector selector) { this.selector = selector; - this.trackSelectionFactory = trackSelectionFactory; } /** @@ -228,7 +221,7 @@ import java.util.Arrays; } private void setOverride(int group, int... tracks) { - override = new SelectionOverride(trackSelectionFactory, group, tracks); + override = new SelectionOverride(group, tracks); } // Track array manipulation. diff --git a/library/core/src/main/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelector.java b/library/core/src/main/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelector.java index 7edcf65320..80fbdf0611 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelector.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelector.java @@ -632,33 +632,20 @@ public class DefaultTrackSelector extends MappingTrackSelector { /** A track selection override. */ public static class SelectionOverride { - public final TrackSelection.Factory factory; public final int groupIndex; public final int[] tracks; public final int length; /** - * @param factory A factory for creating selections from this override. * @param groupIndex The overriding track group index. * @param tracks The overriding track indices within the track group. */ - public SelectionOverride(TrackSelection.Factory factory, int groupIndex, int... tracks) { - this.factory = factory; + public SelectionOverride(int groupIndex, int... tracks) { this.groupIndex = groupIndex; this.tracks = tracks; this.length = tracks.length; } - /** - * Creates an selection from this override. - * - * @param groups The track groups whose selection is being overridden. - * @return The selection. - */ - public TrackSelection createTrackSelection(TrackGroupArray groups) { - return factory.createTrackSelection(groups.get(groupIndex), tracks); - } - /** Returns whether this override contains the specified track index. */ public boolean containsTrack(int track) { for (int overrideTrack : tracks) { @@ -905,8 +892,17 @@ public class DefaultTrackSelector extends MappingTrackSelector { TrackGroupArray rendererTrackGroup = mappedTrackInfo.getTrackGroups(i); if (hasSelectionOverride(i, rendererTrackGroup)) { SelectionOverride override = selectionOverrides.get(i).get(rendererTrackGroup); - rendererTrackSelections[i] = - override == null ? null : override.createTrackSelection(rendererTrackGroup); + if (override == null) { + rendererTrackSelections[i] = null; + } else if (override.length == 1) { + rendererTrackSelections[i] = + new FixedTrackSelection( + rendererTrackGroup.get(override.groupIndex), override.tracks[0]); + } else { + rendererTrackSelections[i] = + adaptiveTrackSelectionFactory.createTrackSelection( + rendererTrackGroup.get(override.groupIndex), override.tracks); + } } } } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/trackselection/MappingTrackSelector.java b/library/core/src/main/java/com/google/android/exoplayer2/trackselection/MappingTrackSelector.java index 75a7565b98..7a5757b0ec 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/trackselection/MappingTrackSelector.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/trackselection/MappingTrackSelector.java @@ -264,17 +264,6 @@ public abstract class MappingTrackSelector extends TrackSelector { } - // TODO: Make DefaultTrackSelector.SelectionOverride final when this is removed. - /** @deprecated Use {@link DefaultTrackSelector.SelectionOverride} */ - @Deprecated - public static final class SelectionOverride extends DefaultTrackSelector.SelectionOverride { - - public SelectionOverride(TrackSelection.Factory factory, int groupIndex, int... tracks) { - super(factory, groupIndex, tracks); - } - - } - private MappedTrackInfo currentMappedTrackInfo; /** From a8e16f3cfe42a55842ab54a768581c00cd070f2b Mon Sep 17 00:00:00 2001 From: eguven Date: Tue, 17 Apr 2018 02:02:27 -0700 Subject: [PATCH 051/157] Simplify DownloadService notification requirements This makes DownloadService easier to use in general and when only single notification is used for all downloads. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=193165982 --- .../exoplayer2/offline/DownloadService.java | 238 ++++++++---------- .../dash/offline/DownloadServiceDashTest.java | 17 +- .../ui/DownloadNotificationUtil.java | 138 +++++----- .../exoplayer2/ui/NotificationUtil.java | 76 ++++++ library/ui/src/main/res/values-af/strings.xml | 1 - library/ui/src/main/res/values-am/strings.xml | 1 - library/ui/src/main/res/values-ar/strings.xml | 1 - library/ui/src/main/res/values-az/strings.xml | 1 - .../src/main/res/values-b+sr+Latn/strings.xml | 1 - library/ui/src/main/res/values-be/strings.xml | 1 - library/ui/src/main/res/values-bg/strings.xml | 1 - library/ui/src/main/res/values-bn/strings.xml | 1 - library/ui/src/main/res/values-bs/strings.xml | 1 - library/ui/src/main/res/values-ca/strings.xml | 1 - library/ui/src/main/res/values-cs/strings.xml | 1 - library/ui/src/main/res/values-da/strings.xml | 1 - library/ui/src/main/res/values-de/strings.xml | 1 - library/ui/src/main/res/values-el/strings.xml | 1 - .../ui/src/main/res/values-en-rAU/strings.xml | 1 - .../ui/src/main/res/values-en-rGB/strings.xml | 1 - .../ui/src/main/res/values-en-rIN/strings.xml | 1 - .../ui/src/main/res/values-es-rUS/strings.xml | 1 - library/ui/src/main/res/values-es/strings.xml | 1 - library/ui/src/main/res/values-et/strings.xml | 1 - library/ui/src/main/res/values-eu/strings.xml | 1 - library/ui/src/main/res/values-fa/strings.xml | 1 - library/ui/src/main/res/values-fi/strings.xml | 1 - .../ui/src/main/res/values-fr-rCA/strings.xml | 1 - library/ui/src/main/res/values-fr/strings.xml | 1 - library/ui/src/main/res/values-gl/strings.xml | 1 - library/ui/src/main/res/values-gu/strings.xml | 1 - library/ui/src/main/res/values-hi/strings.xml | 1 - library/ui/src/main/res/values-hr/strings.xml | 1 - library/ui/src/main/res/values-hu/strings.xml | 1 - library/ui/src/main/res/values-hy/strings.xml | 1 - library/ui/src/main/res/values-in/strings.xml | 1 - library/ui/src/main/res/values-is/strings.xml | 1 - library/ui/src/main/res/values-it/strings.xml | 1 - library/ui/src/main/res/values-iw/strings.xml | 1 - library/ui/src/main/res/values-ja/strings.xml | 1 - library/ui/src/main/res/values-ka/strings.xml | 1 - library/ui/src/main/res/values-kk/strings.xml | 1 - library/ui/src/main/res/values-km/strings.xml | 1 - library/ui/src/main/res/values-kn/strings.xml | 1 - library/ui/src/main/res/values-ko/strings.xml | 1 - library/ui/src/main/res/values-ky/strings.xml | 1 - library/ui/src/main/res/values-lo/strings.xml | 1 - library/ui/src/main/res/values-lt/strings.xml | 1 - library/ui/src/main/res/values-lv/strings.xml | 1 - library/ui/src/main/res/values-mk/strings.xml | 1 - library/ui/src/main/res/values-ml/strings.xml | 1 - library/ui/src/main/res/values-mn/strings.xml | 1 - library/ui/src/main/res/values-mr/strings.xml | 1 - library/ui/src/main/res/values-ms/strings.xml | 1 - library/ui/src/main/res/values-my/strings.xml | 1 - library/ui/src/main/res/values-nb/strings.xml | 1 - library/ui/src/main/res/values-ne/strings.xml | 1 - library/ui/src/main/res/values-nl/strings.xml | 1 - library/ui/src/main/res/values-pa/strings.xml | 1 - library/ui/src/main/res/values-pl/strings.xml | 1 - .../ui/src/main/res/values-pt-rPT/strings.xml | 1 - library/ui/src/main/res/values-pt/strings.xml | 1 - library/ui/src/main/res/values-ro/strings.xml | 1 - library/ui/src/main/res/values-ru/strings.xml | 1 - library/ui/src/main/res/values-si/strings.xml | 1 - library/ui/src/main/res/values-sk/strings.xml | 1 - library/ui/src/main/res/values-sl/strings.xml | 1 - library/ui/src/main/res/values-sq/strings.xml | 1 - library/ui/src/main/res/values-sr/strings.xml | 1 - library/ui/src/main/res/values-sv/strings.xml | 1 - library/ui/src/main/res/values-sw/strings.xml | 1 - library/ui/src/main/res/values-ta/strings.xml | 1 - library/ui/src/main/res/values-te/strings.xml | 1 - library/ui/src/main/res/values-th/strings.xml | 1 - library/ui/src/main/res/values-tl/strings.xml | 1 - library/ui/src/main/res/values-tr/strings.xml | 1 - library/ui/src/main/res/values-uk/strings.xml | 1 - library/ui/src/main/res/values-ur/strings.xml | 1 - library/ui/src/main/res/values-uz/strings.xml | 1 - library/ui/src/main/res/values-vi/strings.xml | 1 - .../ui/src/main/res/values-zh-rCN/strings.xml | 1 - .../ui/src/main/res/values-zh-rHK/strings.xml | 1 - .../ui/src/main/res/values-zh-rTW/strings.xml | 1 - library/ui/src/main/res/values-zu/strings.xml | 1 - library/ui/src/main/res/values/strings.xml | 2 - 85 files changed, 278 insertions(+), 273 deletions(-) create mode 100644 library/ui/src/main/java/com/google/android/exoplayer2/ui/NotificationUtil.java diff --git a/library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadService.java b/library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadService.java index 0a6bc062f1..7593d8083c 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadService.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadService.java @@ -16,19 +16,14 @@ package com.google.android.exoplayer2.offline; import android.app.Notification; -import android.app.Notification.Builder; -import android.app.NotificationChannel; -import android.app.NotificationManager; import android.app.Service; import android.content.Context; import android.content.Intent; import android.os.Handler; import android.os.IBinder; import android.os.Looper; -import android.support.annotation.CallSuper; import android.support.annotation.Nullable; import android.util.Log; -import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.offline.DownloadManager.DownloadState; import com.google.android.exoplayer2.scheduler.Requirements; import com.google.android.exoplayer2.scheduler.RequirementsWatcher; @@ -42,7 +37,7 @@ import java.io.IOException; *

To start the service, create an instance of one of the subclasses of {@link DownloadAction} * and call {@link #addDownloadAction(Context, Class, DownloadAction)} with it. */ -public abstract class DownloadService extends Service implements DownloadManager.DownloadListener { +public abstract class DownloadService extends Service { /** Use this action to initialize {@link DownloadManager}. */ public static final String ACTION_INIT = @@ -62,8 +57,8 @@ public abstract class DownloadService extends Service implements DownloadManager /** A {@link DownloadAction} to be executed. */ public static final String DOWNLOAD_ACTION = "DownloadAction"; - /** Default progress update interval in milliseconds. */ - public static final long DEFAULT_PROGRESS_UPDATE_INTERVAL_MILLIS = 1000; + /** Default foreground notification update interval in milliseconds. */ + public static final long DEFAULT_FOREGROUND_NOTIFICATION_UPDATE_INTERVAL = 1000; private static final String TAG = "DownloadService"; private static final boolean DEBUG = false; @@ -73,27 +68,35 @@ public abstract class DownloadService extends Service implements DownloadManager private static RequirementsWatcher requirementsWatcher; private static Scheduler scheduler; - private final int notificationIdOffset; - private final long progressUpdateIntervalMillis; + private final ForegroundNotificationUpdater foregroundNotificationUpdater; private DownloadManager downloadManager; - private ProgressUpdater progressUpdater; + private DownloadListener downloadListener; private int lastStartId; - /** @param notificationIdOffset Value to offset notification ids. Must be greater than 0. */ - protected DownloadService(int notificationIdOffset) { - this(notificationIdOffset, DEFAULT_PROGRESS_UPDATE_INTERVAL_MILLIS); + /** + * Creates a DownloadService with {@link #DEFAULT_FOREGROUND_NOTIFICATION_UPDATE_INTERVAL}. + * + * @param foregroundNotificationId The notification id for the foreground notification, must not + * be 0. + */ + protected DownloadService(int foregroundNotificationId) { + this(foregroundNotificationId, DEFAULT_FOREGROUND_NOTIFICATION_UPDATE_INTERVAL); } /** - * @param notificationIdOffset Value to offset notification ids. Must be greater than 0. - * @param progressUpdateIntervalMillis {@link #onProgressUpdate(DownloadState[])} is called using - * this interval. If it's {@link C#TIME_UNSET}, then {@link - * #onProgressUpdate(DownloadState[])} isn't called. + * Creates a DownloadService. + * + * @param foregroundNotificationId The notification id for the foreground notification, must not + * be 0. + * @param foregroundNotificationUpdateInterval The maximum interval to update foreground + * notification, in milliseconds. */ - protected DownloadService(int notificationIdOffset, long progressUpdateIntervalMillis) { - this.notificationIdOffset = notificationIdOffset; - this.progressUpdateIntervalMillis = progressUpdateIntervalMillis; + protected DownloadService( + int foregroundNotificationId, long foregroundNotificationUpdateInterval) { + foregroundNotificationUpdater = + new ForegroundNotificationUpdater( + foregroundNotificationId, foregroundNotificationUpdateInterval); } /** @@ -130,7 +133,8 @@ public abstract class DownloadService extends Service implements DownloadManager public void onCreate() { logd("onCreate"); downloadManager = getDownloadManager(); - downloadManager.addListener(this); + downloadListener = new DownloadListener(); + downloadManager.addListener(downloadListener); if (requirementsWatcher == null) { Requirements requirements = getRequirements(); @@ -143,15 +147,13 @@ public abstract class DownloadService extends Service implements DownloadManager requirementsWatcher.start(); } } - - progressUpdater = new ProgressUpdater(this, progressUpdateIntervalMillis); } @Override public void onDestroy() { logd("onDestroy"); - progressUpdater.stop(); - downloadManager.removeListener(this); + foregroundNotificationUpdater.stopPeriodicUpdates(); + downloadManager.removeListener(downloadListener); if (downloadManager.getTaskCount() == 0) { if (requirementsWatcher != null) { requirementsWatcher.stop(); @@ -186,12 +188,12 @@ public abstract class DownloadService extends Service implements DownloadManager case ACTION_ADD: byte[] actionData = intent.getByteArrayExtra(DOWNLOAD_ACTION); if (actionData == null) { - onCommandError(intent, new IllegalArgumentException("DownloadAction is missing.")); + onCommandError(new IllegalArgumentException("DownloadAction is missing.")); } else { try { - onNewTask(intent, downloadManager.handleAction(actionData)); + downloadManager.handleAction(actionData); } catch (IOException e) { - onCommandError(intent, e); + onCommandError(e); } } break; @@ -202,11 +204,11 @@ public abstract class DownloadService extends Service implements DownloadManager downloadManager.startDownloads(); break; default: - onCommandError(intent, new IllegalArgumentException("Unknown action: " + intentAction)); + onCommandError(new IllegalArgumentException("Unknown action: " + intentAction)); break; } if (downloadManager.isIdle()) { - onIdle(null); + stop(); } return START_STICKY; } @@ -227,130 +229,110 @@ public abstract class DownloadService extends Service implements DownloadManager /** Returns requirements for downloads to take place, or null. */ protected abstract @Nullable Requirements getRequirements(); - /** Called on error in start command. */ - protected void onCommandError(Intent intent, Exception error) { + /** + * Returns a notification to be displayed when this service running in the foreground. + * + *

This method is called when there is a download task state change and periodically while + * there is an active download. Update interval can be set using {@link #DownloadService(int, + * long)}. + * + *

On API level 26 and above, it may be also called just before the service stops with an empty + * {@code downloadStates} array, returned notification is used to satisfy system requirements for + * foreground services. + * + * @param downloadStates DownloadState for all tasks. + * @return A notification to be displayed when this service running in the foreground. + */ + protected abstract Notification getForegroundNotification(DownloadState[] downloadStates); + + /** Called when the download state changes. */ + protected void onStateChange(DownloadState downloadState) { // Do nothing. } - /** Called when a new task is added to the {@link DownloadManager}. */ - protected void onNewTask(Intent intent, int taskId) { - // Do nothing. + private void onCommandError(Exception error) { + Log.e(TAG, "Command error", error); } - /** Returns a notification channelId. See {@link NotificationChannel}. */ - protected abstract String getNotificationChannelId(); - - /** - * Helper method which calls {@link #startForeground(int, Notification)} with {@code - * notificationIdOffset} and {@code foregroundNotification}. - */ - public void startForeground(Notification foregroundNotification) { - // logd("start foreground"); - startForeground(notificationIdOffset, foregroundNotification); - } - - /** - * Sets/replaces or cancels the notification for the given id. - * - * @param id A unique id for the notification. This value is offset by {@code - * notificationIdOffset}. - * @param notification If not null, it's showed, replacing any previous notification. Otherwise - * any previous notification is canceled. - */ - public void setNotification(int id, @Nullable Notification notification) { - NotificationManager notificationManager = - (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); - if (notification != null) { - notificationManager.notify(notificationIdOffset + 1 + id, notification); - } else { - notificationManager.cancel(notificationIdOffset + 1 + id); - } - } - - /** - * Override this method to get notified. - * - *

{@inheritDoc} - */ - @CallSuper - @Override - public void onStateChange(DownloadManager downloadManager, DownloadState downloadState) { - if (downloadState.state == DownloadState.STATE_STARTED) { - progressUpdater.start(); - } - } - - /** - * Override this method to get notified. - * - *

{@inheritDoc} - */ - @CallSuper - @Override - public void onIdle(DownloadManager downloadManager) { + private void stop() { + foregroundNotificationUpdater.stopPeriodicUpdates(); // Make sure startForeground is called before stopping. + // Workaround for [Internal: b/69424260] if (Util.SDK_INT >= 26) { - Builder notificationBuilder = new Builder(this, getNotificationChannelId()); - Notification foregroundNotification = notificationBuilder.build(); - startForeground(foregroundNotification); + foregroundNotificationUpdater.showNotificationIfNotAlready(); } boolean stopSelfResult = stopSelfResult(lastStartId); logd("stopSelf(" + lastStartId + ") result: " + stopSelfResult); } - /** Override this method to get notified on every second while there are active downloads. */ - protected void onProgressUpdate(DownloadState[] activeDownloadTasks) { - // Do nothing. - } - private void logd(String message) { if (DEBUG) { Log.d(TAG, message); } } - private static final class ProgressUpdater implements Runnable { + private final class DownloadListener implements DownloadManager.DownloadListener { + @Override + public void onStateChange(DownloadManager downloadManager, DownloadState downloadState) { + DownloadService.this.onStateChange(downloadState); + if (downloadState.state == DownloadState.STATE_STARTED) { + foregroundNotificationUpdater.startPeriodicUpdates(); + } else { + foregroundNotificationUpdater.update(); + } + } - private final DownloadService downloadService; - private final long progressUpdateIntervalMillis; + @Override + public final void onIdle(DownloadManager downloadManager) { + stop(); + } + } + + private final class ForegroundNotificationUpdater implements Runnable { + + private final int notificationId; + private final long updateInterval; private final Handler handler; - private boolean stopped; - public ProgressUpdater(DownloadService downloadService, long progressUpdateIntervalMillis) { - this.downloadService = downloadService; - this.progressUpdateIntervalMillis = progressUpdateIntervalMillis; + private boolean periodicUpdatesStarted; + private boolean notificationDisplayed; + + public ForegroundNotificationUpdater(int notificationId, long updateInterval) { + this.notificationId = notificationId; + this.updateInterval = updateInterval; this.handler = new Handler(Looper.getMainLooper()); - stopped = true; + } + + public void startPeriodicUpdates() { + periodicUpdatesStarted = true; + update(); + } + + public void stopPeriodicUpdates() { + periodicUpdatesStarted = false; + handler.removeCallbacks(this); + } + + public void update() { + DownloadState[] downloadStates = downloadManager.getDownloadStates(); + startForeground(notificationId, getForegroundNotification(downloadStates)); + notificationDisplayed = true; + if (periodicUpdatesStarted) { + handler.removeCallbacks(this); + handler.postDelayed(this, updateInterval); + } + } + + public void showNotificationIfNotAlready() { + if (!notificationDisplayed) { + update(); + } } @Override public void run() { - DownloadState[] activeDownloadTasks = - downloadService.downloadManager.getActiveDownloadStates(); - if (activeDownloadTasks.length > 0) { - downloadService.onProgressUpdate(activeDownloadTasks); - if (progressUpdateIntervalMillis != C.TIME_UNSET) { - handler.postDelayed(this, progressUpdateIntervalMillis); - } - } else { - stop(); - } + update(); } - - public void stop() { - stopped = true; - handler.removeCallbacks(this); - } - - public void start() { - if (stopped) { - stopped = false; - if (progressUpdateIntervalMillis != C.TIME_UNSET) { - handler.post(this); - } - } - } - } private static final class RequirementsListener implements RequirementsWatcher.Listener { diff --git a/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/offline/DownloadServiceDashTest.java b/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/offline/DownloadServiceDashTest.java index e8044e57c1..7c01170f45 100644 --- a/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/offline/DownloadServiceDashTest.java +++ b/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/offline/DownloadServiceDashTest.java @@ -20,9 +20,12 @@ import static com.google.android.exoplayer2.source.dash.offline.DashDownloadTest import static com.google.android.exoplayer2.testutil.CacheAsserts.assertCacheEmpty; import static com.google.android.exoplayer2.testutil.CacheAsserts.assertCachedData; +import android.app.Notification; import android.content.Context; import android.content.Intent; +import android.support.annotation.Nullable; import com.google.android.exoplayer2.offline.DownloadManager; +import com.google.android.exoplayer2.offline.DownloadManager.DownloadState; import com.google.android.exoplayer2.offline.DownloadService; import com.google.android.exoplayer2.offline.DownloaderConstructorHelper; import com.google.android.exoplayer2.scheduler.Requirements; @@ -44,6 +47,7 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mockito; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; @@ -107,7 +111,7 @@ public class DownloadServiceDashTest { new Runnable() { @Override public void run() { - File actionFile = null; + File actionFile; try { actionFile = Util.createTempFile(context, "ExoPlayerTest"); } catch (IOException e) { @@ -126,7 +130,7 @@ public class DownloadServiceDashTest { dashDownloadManager.startDownloads(); dashDownloadService = - new DownloadService(101010) { + new DownloadService(/*foregroundNotificationId=*/ 1) { @Override protected DownloadManager getDownloadManager() { @@ -134,15 +138,18 @@ public class DownloadServiceDashTest { } @Override - protected String getNotificationChannelId() { - return ""; + protected Notification getForegroundNotification( + DownloadState[] downloadStates) { + return Mockito.mock(Notification.class); } + @Nullable @Override protected Scheduler getScheduler() { return null; } + @Nullable @Override protected Requirements getRequirements() { return null; @@ -216,7 +223,7 @@ public class DownloadServiceDashTest { callDownloadServiceOnStart(new DashDownloadAction(TEST_MPD_URI, false, null, keys)); } - private void callDownloadServiceOnStart(final DashDownloadAction action) throws Throwable { + private void callDownloadServiceOnStart(final DashDownloadAction action) { dummyMainThread.runOnMainThread( new Runnable() { @Override diff --git a/library/ui/src/main/java/com/google/android/exoplayer2/ui/DownloadNotificationUtil.java b/library/ui/src/main/java/com/google/android/exoplayer2/ui/DownloadNotificationUtil.java index b4f69809a5..9085397f14 100644 --- a/library/ui/src/main/java/com/google/android/exoplayer2/ui/DownloadNotificationUtil.java +++ b/library/ui/src/main/java/com/google/android/exoplayer2/ui/DownloadNotificationUtil.java @@ -16,23 +16,71 @@ package com.google.android.exoplayer2.ui; import android.app.Notification; -import android.app.Notification.BigTextStyle; -import android.app.Notification.Builder; import android.content.Context; import android.support.annotation.Nullable; +import android.support.v4.app.NotificationCompat; import com.google.android.exoplayer2.offline.DownloadManager; import com.google.android.exoplayer2.offline.DownloadManager.DownloadState; import com.google.android.exoplayer2.util.ErrorMessageProvider; -import com.google.android.exoplayer2.util.Util; /** Helper class to create notifications for downloads using {@link DownloadManager}. */ public final class DownloadNotificationUtil { + private static final int NULL_STRING_ID = 0; + private DownloadNotificationUtil() {} /** - * Returns a notification for the given {@link DownloadState}, or null if no notification should - * be displayed. + * Returns a progress notification for the given {@link DownloadState}s. + * + * @param downloadStates States of the downloads. + * @param context Used to access resources. + * @param smallIcon A small icon for the notification. + * @param channelId The id of the notification channel to use. Only required for API level 26 and + * above. + * @param message An optional message to display on the notification. + * @return A progress notification for the given {@link DownloadState}s. + */ + public static @Nullable Notification createProgressNotification( + DownloadState[] downloadStates, + Context context, + int smallIcon, + String channelId, + @Nullable String message) { + float totalPercentage = 0; + int determinatePercentageCount = 0; + boolean isAnyDownloadActive = false; + for (DownloadState downloadState : downloadStates) { + if (downloadState.downloadAction.isRemoveAction() + || downloadState.state != DownloadState.STATE_STARTED) { + continue; + } + float percentage = downloadState.downloadPercentage; + if (!Float.isNaN(percentage)) { + totalPercentage += percentage; + determinatePercentageCount++; + } + isAnyDownloadActive = true; + } + + int titleStringId = isAnyDownloadActive ? R.string.exo_downloading : NULL_STRING_ID; + NotificationCompat.Builder notificationBuilder = + createNotificationBuilder(context, smallIcon, channelId, message, titleStringId); + + notificationBuilder.setOngoing(true); + int max = 100; + int progress = (int) (totalPercentage / determinatePercentageCount); + boolean indeterminate = determinatePercentageCount == 0; + notificationBuilder.setProgress(max, progress, indeterminate); + + notificationBuilder.setShowWhen(false); + return notificationBuilder.build(); + } + + /** + * Returns a notification for a {@link DownloadState} which is in either {@link + * DownloadState#STATE_ENDED} or {@link DownloadState#STATE_ERROR} states. Returns null if it's + * some other state or it's state of a remove action. * * @param downloadState State of the download. * @param context Used to access resources. @@ -43,10 +91,11 @@ public final class DownloadNotificationUtil { * @param errorMessageProvider An optional {@link ErrorMessageProvider} for translating download * errors into readable error messages. If not null and there is a download error then the * error message is displayed instead of {@code message}. - * @return A notification for the given {@link DownloadState}, or null if no notification should - * be displayed. + * @return A notification for a {@link DownloadState} which is in either {@link + * DownloadState#STATE_ENDED} or {@link DownloadState#STATE_ERROR} states. Returns null if + * it's some other state or it's state of a remove action. */ - public static @Nullable Notification createNotification( + public static @Nullable Notification createDownloadFinishedNotification( DownloadState downloadState, Context context, int smallIcon, @@ -54,63 +103,36 @@ public final class DownloadNotificationUtil { @Nullable String message, @Nullable ErrorMessageProvider errorMessageProvider) { if (downloadState.downloadAction.isRemoveAction() - || downloadState.state == DownloadState.STATE_CANCELED) { + || (downloadState.state != DownloadState.STATE_ENDED + && downloadState.state != DownloadState.STATE_ERROR)) { return null; } - - Builder notificationBuilder = new Builder(context); - if (Util.SDK_INT >= 26) { - notificationBuilder.setChannelId(channelId); - } - notificationBuilder.setSmallIcon(smallIcon); - - int titleStringId = getTitleStringId(downloadState); - notificationBuilder.setContentTitle(context.getResources().getString(titleStringId)); - - if (downloadState.state == DownloadState.STATE_STARTED) { - notificationBuilder.setOngoing(true); - float percentage = downloadState.downloadPercentage; - boolean indeterminate = Float.isNaN(percentage); - notificationBuilder.setProgress(100, indeterminate ? 0 : (int) percentage, indeterminate); - } - if (Util.SDK_INT >= 17) { - // Hide timestamp on the notification while download progresses. - notificationBuilder.setShowWhen(downloadState.state != DownloadState.STATE_STARTED); - } - if (downloadState.error != null && errorMessageProvider != null) { message = errorMessageProvider.getErrorMessage(downloadState.error).second; } - if (message != null) { - if (Util.SDK_INT >= 16) { - notificationBuilder.setStyle(new BigTextStyle().bigText(message)); - } else { - notificationBuilder.setContentText(message); - } - } - return notificationBuilder.getNotification(); + int titleStringId = + downloadState.state == DownloadState.STATE_ENDED + ? R.string.exo_download_completed + : R.string.exo_download_failed; + NotificationCompat.Builder notificationBuilder = + createNotificationBuilder(context, smallIcon, channelId, message, titleStringId); + return notificationBuilder.build(); } - private static int getTitleStringId(DownloadState downloadState) { - int titleStringId; - switch (downloadState.state) { - case DownloadState.STATE_QUEUED: - titleStringId = R.string.exo_download_queued; - break; - case DownloadState.STATE_STARTED: - titleStringId = R.string.exo_downloading; - break; - case DownloadState.STATE_ENDED: - titleStringId = R.string.exo_download_completed; - break; - case DownloadState.STATE_ERROR: - titleStringId = R.string.exo_download_failed; - break; - case DownloadState.STATE_CANCELED: - default: - // Never happens. - throw new IllegalStateException(); + private static NotificationCompat.Builder createNotificationBuilder( + Context context, + int smallIcon, + String channelId, + @Nullable String message, + int titleStringId) { + NotificationCompat.Builder notificationBuilder = + new NotificationCompat.Builder(context, channelId).setSmallIcon(smallIcon); + if (titleStringId != NULL_STRING_ID) { + notificationBuilder.setContentTitle(context.getResources().getString(titleStringId)); } - return titleStringId; + if (message != null) { + notificationBuilder.setStyle(new NotificationCompat.BigTextStyle().bigText(message)); + } + return notificationBuilder; } } diff --git a/library/ui/src/main/java/com/google/android/exoplayer2/ui/NotificationUtil.java b/library/ui/src/main/java/com/google/android/exoplayer2/ui/NotificationUtil.java new file mode 100644 index 0000000000..ab665bcc01 --- /dev/null +++ b/library/ui/src/main/java/com/google/android/exoplayer2/ui/NotificationUtil.java @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2018 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.ui; + +import android.app.Notification; +import android.app.NotificationChannel; +import android.app.NotificationManager; +import android.content.Context; +import android.content.Intent; +import android.support.annotation.Nullable; +import com.google.android.exoplayer2.util.Util; + +/** Utility methods for displaying {@link android.app.Notification}s. */ +public final class NotificationUtil { + + private NotificationUtil() {} + + /** + * Creates a notification channel that notifications can be posted to. See {@link + * NotificationChannel} and {@link + * NotificationManager#createNotificationChannel(NotificationChannel)} for details. + * + * @param context A {@link Context} to retrieve {@link NotificationManager}. + * @param id The id of the channel. Must be unique per package. The value may be truncated if it + * is too long. + * @param name The user visible name of the channel. You can rename this channel when the system + * locale changes by listening for the {@link Intent#ACTION_LOCALE_CHANGED} broadcast. The + * recommended maximum length is 40 characters; the value may be truncated if it is too long. + * @param importance The importance of the channel. This controls how interruptive notifications + * posted to this channel are. + */ + public static void createNotificationChannel( + Context context, String id, int name, int importance) { + if (Util.SDK_INT >= 26) { + NotificationManager notificationManager = + (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); + NotificationChannel mChannel = + new NotificationChannel(id, context.getString(name), importance); + notificationManager.createNotificationChannel(mChannel); + } + } + + /** + * Post a notification to be shown in the status bar. If a notification with the same id has + * already been posted by your application and has not yet been canceled, it will be replaced by + * the updated information. If {@code notification} is null, then cancels a previously shown + * notification. + * + * @param context A {@link Context} to retrieve {@link NotificationManager}. + * @param id An identifier for this notification unique within your application. + * @param notification A {@link Notification} object describing what to show the user. If null, + * then cancels a previously shown notification. + */ + public static void setNotification(Context context, int id, @Nullable Notification notification) { + NotificationManager notificationManager = + (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); + if (notification != null) { + notificationManager.notify(id, notification); + } else { + notificationManager.cancel(id); + } + } +} diff --git a/library/ui/src/main/res/values-af/strings.xml b/library/ui/src/main/res/values-af/strings.xml index 0ede570914..e98f065a33 100644 --- a/library/ui/src/main/res/values-af/strings.xml +++ b/library/ui/src/main/res/values-af/strings.xml @@ -12,7 +12,6 @@ Herhaal alles Skommel Volskermmodus - Aflaai op waglys Laai tans af Aflaai is voltooi Kon nie aflaai nie diff --git a/library/ui/src/main/res/values-am/strings.xml b/library/ui/src/main/res/values-am/strings.xml index 80c4fd1fb6..fc7793a7dd 100644 --- a/library/ui/src/main/res/values-am/strings.xml +++ b/library/ui/src/main/res/values-am/strings.xml @@ -12,7 +12,6 @@ ሁሉንም ድገም በውዝ የሙሉ ማያ ሁነታ - ማውረድ ወረፋ ይዟል በማውረድ ላይ ማውረድ ተጠናቋል ማውረድ አልተሳካም diff --git a/library/ui/src/main/res/values-ar/strings.xml b/library/ui/src/main/res/values-ar/strings.xml index 5ccc6b3145..a25d8d863a 100644 --- a/library/ui/src/main/res/values-ar/strings.xml +++ b/library/ui/src/main/res/values-ar/strings.xml @@ -12,7 +12,6 @@ تكرار الكل ترتيب عشوائي وضع ملء الشاشة - التنزيل قيد الانتظار تحميل اكتمل التنزيل تعذّر التنزيل diff --git a/library/ui/src/main/res/values-az/strings.xml b/library/ui/src/main/res/values-az/strings.xml index cb1328f0ba..81b5db725c 100644 --- a/library/ui/src/main/res/values-az/strings.xml +++ b/library/ui/src/main/res/values-az/strings.xml @@ -12,7 +12,6 @@ Hamısı təkrarlansın Qarışdırın Tam ekran rejimi - Endirmə gözlənilir Endirilir Endirmə tamamlandı Endirmə alınmadı diff --git a/library/ui/src/main/res/values-b+sr+Latn/strings.xml b/library/ui/src/main/res/values-b+sr+Latn/strings.xml index be0223898b..90f82ba488 100644 --- a/library/ui/src/main/res/values-b+sr+Latn/strings.xml +++ b/library/ui/src/main/res/values-b+sr+Latn/strings.xml @@ -12,7 +12,6 @@ Ponovi sve Pusti nasumično Režim celog ekrana - Preuzimanje je na čekanju Preuzimanje Preuzimanje je završeno Preuzimanje nije uspelo diff --git a/library/ui/src/main/res/values-be/strings.xml b/library/ui/src/main/res/values-be/strings.xml index ce6d8b1cc8..cb7ec16a76 100644 --- a/library/ui/src/main/res/values-be/strings.xml +++ b/library/ui/src/main/res/values-be/strings.xml @@ -12,7 +12,6 @@ Паўтарыць усе Перамяшаць Поўнаэкранны рэжым - Спампоўка пастаўлена ў чаргу Спампоўка Спампоўка завершана Збой спампоўкі diff --git a/library/ui/src/main/res/values-bg/strings.xml b/library/ui/src/main/res/values-bg/strings.xml index 14cc2c8fcc..d13e98eea6 100644 --- a/library/ui/src/main/res/values-bg/strings.xml +++ b/library/ui/src/main/res/values-bg/strings.xml @@ -12,7 +12,6 @@ Повтаряне на всички Разбъркване Режим на цял екран - Изтеглянето е в опашката Изтегля се Изтеглянето завърши Изтеглянето не бе успешно diff --git a/library/ui/src/main/res/values-bn/strings.xml b/library/ui/src/main/res/values-bn/strings.xml index 539837dc04..98ca3c05db 100644 --- a/library/ui/src/main/res/values-bn/strings.xml +++ b/library/ui/src/main/res/values-bn/strings.xml @@ -12,7 +12,6 @@ সবগুলি আইটেম আবার চালান শাফেল করুন পূর্ণ স্ক্রিন মোড - ডাউনলোড অপেক্ষমান ডাউনলোড হচ্ছে ডাউনলোড হয়ে গেছে ডাউনলোড করা যায়নি diff --git a/library/ui/src/main/res/values-bs/strings.xml b/library/ui/src/main/res/values-bs/strings.xml index f7f4bf5cbb..3504767263 100644 --- a/library/ui/src/main/res/values-bs/strings.xml +++ b/library/ui/src/main/res/values-bs/strings.xml @@ -12,7 +12,6 @@ Ponovi sve Izmiješaj Način rada preko cijelog ekrana - Preuzimanje je na čekanju Preuzimanje Preuzimanje je završeno Preuzimanje nije uspjelo diff --git a/library/ui/src/main/res/values-ca/strings.xml b/library/ui/src/main/res/values-ca/strings.xml index 31fc1c59b3..c4212c590c 100644 --- a/library/ui/src/main/res/values-ca/strings.xml +++ b/library/ui/src/main/res/values-ca/strings.xml @@ -12,7 +12,6 @@ Repeteix tot Reprodueix aleatòriament Mode de pantalla completa - La baixada s\'ha posat a la cua S\'està baixant S\'ha completat la baixada No s\'ha pogut baixar diff --git a/library/ui/src/main/res/values-cs/strings.xml b/library/ui/src/main/res/values-cs/strings.xml index 75d5f6c1c1..e0f4771a1a 100644 --- a/library/ui/src/main/res/values-cs/strings.xml +++ b/library/ui/src/main/res/values-cs/strings.xml @@ -12,7 +12,6 @@ Opakovat vše Náhodně Režim celé obrazovky - Zařazeno do fronty stahování Stahování Stahování bylo dokončeno Stažení se nezdařilo diff --git a/library/ui/src/main/res/values-da/strings.xml b/library/ui/src/main/res/values-da/strings.xml index ce9c9e7da7..e96c37b9c4 100644 --- a/library/ui/src/main/res/values-da/strings.xml +++ b/library/ui/src/main/res/values-da/strings.xml @@ -12,7 +12,6 @@ Gentag alle Bland Fuld skærm - Downloaden er i kø Download Downloaden er udført Download mislykkedes diff --git a/library/ui/src/main/res/values-de/strings.xml b/library/ui/src/main/res/values-de/strings.xml index 3fd38a784b..7bfc01c0a8 100644 --- a/library/ui/src/main/res/values-de/strings.xml +++ b/library/ui/src/main/res/values-de/strings.xml @@ -12,7 +12,6 @@ Alle wiederholen Zufallsmix Vollbildmodus - Download in der Warteschlange Wird heruntergeladen Download abgeschlossen Download fehlgeschlagen diff --git a/library/ui/src/main/res/values-el/strings.xml b/library/ui/src/main/res/values-el/strings.xml index c4d15a55d6..d926c455b6 100644 --- a/library/ui/src/main/res/values-el/strings.xml +++ b/library/ui/src/main/res/values-el/strings.xml @@ -12,7 +12,6 @@ Επανάληψη όλων Τυχαία αναπαραγωγή Λειτουργία πλήρους οθόνης - Η λήψη προστέθηκε στην ουρά Λήψη Η λήψη ολοκληρώθηκε Η λήψη απέτυχε diff --git a/library/ui/src/main/res/values-en-rAU/strings.xml b/library/ui/src/main/res/values-en-rAU/strings.xml index cfb09d7f65..fddba2245c 100644 --- a/library/ui/src/main/res/values-en-rAU/strings.xml +++ b/library/ui/src/main/res/values-en-rAU/strings.xml @@ -12,7 +12,6 @@ Repeat all Shuffle Full-screen mode - Download queued Downloading Download completed Download failed diff --git a/library/ui/src/main/res/values-en-rGB/strings.xml b/library/ui/src/main/res/values-en-rGB/strings.xml index cfb09d7f65..fddba2245c 100644 --- a/library/ui/src/main/res/values-en-rGB/strings.xml +++ b/library/ui/src/main/res/values-en-rGB/strings.xml @@ -12,7 +12,6 @@ Repeat all Shuffle Full-screen mode - Download queued Downloading Download completed Download failed diff --git a/library/ui/src/main/res/values-en-rIN/strings.xml b/library/ui/src/main/res/values-en-rIN/strings.xml index cfb09d7f65..fddba2245c 100644 --- a/library/ui/src/main/res/values-en-rIN/strings.xml +++ b/library/ui/src/main/res/values-en-rIN/strings.xml @@ -12,7 +12,6 @@ Repeat all Shuffle Full-screen mode - Download queued Downloading Download completed Download failed diff --git a/library/ui/src/main/res/values-es-rUS/strings.xml b/library/ui/src/main/res/values-es-rUS/strings.xml index 990c2cc6ff..e4ef802db4 100644 --- a/library/ui/src/main/res/values-es-rUS/strings.xml +++ b/library/ui/src/main/res/values-es-rUS/strings.xml @@ -12,7 +12,6 @@ Repetir todo Reproducir aleatoriamente Modo de pantalla completa - Descarga en fila Descargando Se completó la descarga No se pudo descargar diff --git a/library/ui/src/main/res/values-es/strings.xml b/library/ui/src/main/res/values-es/strings.xml index 2210475e48..fdf98310b3 100644 --- a/library/ui/src/main/res/values-es/strings.xml +++ b/library/ui/src/main/res/values-es/strings.xml @@ -12,7 +12,6 @@ Repetir todo Reproducir aleatoriamente Modo de pantalla completa - Descarga en cola Descarga de archivos Descarga de archivos completado No se ha podido descargar diff --git a/library/ui/src/main/res/values-et/strings.xml b/library/ui/src/main/res/values-et/strings.xml index 79242a3f45..eb7662e0be 100644 --- a/library/ui/src/main/res/values-et/strings.xml +++ b/library/ui/src/main/res/values-et/strings.xml @@ -12,7 +12,6 @@ Korda kõiki Esita juhuslikus järjekorras Täisekraani režiim - Allalaadimine on järjekorras Allalaadimine Allalaadimine lõpetati Allalaadimine ebaõnnestus diff --git a/library/ui/src/main/res/values-eu/strings.xml b/library/ui/src/main/res/values-eu/strings.xml index 1358f57059..3f3e6e1a43 100644 --- a/library/ui/src/main/res/values-eu/strings.xml +++ b/library/ui/src/main/res/values-eu/strings.xml @@ -12,7 +12,6 @@ Errepikatu guztiak Erreproduzitu ausaz Pantaila osoko modua - Ilaran dago deskarga Deskargatzen Osatu da deskarga Ezin izan da deskargatu diff --git a/library/ui/src/main/res/values-fa/strings.xml b/library/ui/src/main/res/values-fa/strings.xml index c31f9dafa8..bcc8a884bc 100644 --- a/library/ui/src/main/res/values-fa/strings.xml +++ b/library/ui/src/main/res/values-fa/strings.xml @@ -12,7 +12,6 @@ تکرار همه درهم حالت تمام‌صفحه - درانتظار بارگیری درحال بارگیری بارگیری کامل شد بارگیری نشد diff --git a/library/ui/src/main/res/values-fi/strings.xml b/library/ui/src/main/res/values-fi/strings.xml index 4e2b6aaad0..b9eb2e1529 100644 --- a/library/ui/src/main/res/values-fi/strings.xml +++ b/library/ui/src/main/res/values-fi/strings.xml @@ -12,7 +12,6 @@ Toista kaikki uudelleen Satunnaistoisto Koko näytön tila - Lataus jonossa Ladataan Lataus valmis Lataus epäonnistui diff --git a/library/ui/src/main/res/values-fr-rCA/strings.xml b/library/ui/src/main/res/values-fr-rCA/strings.xml index 06fff296af..08a94dc696 100644 --- a/library/ui/src/main/res/values-fr-rCA/strings.xml +++ b/library/ui/src/main/res/values-fr-rCA/strings.xml @@ -12,7 +12,6 @@ Tout lire en boucle Lecture aléatoire Mode Plein écran - File d\'attente de télécharg. Téléchargement en cours… Téléchargement terminé Échec du téléchargement diff --git a/library/ui/src/main/res/values-fr/strings.xml b/library/ui/src/main/res/values-fr/strings.xml index 2a6c79df5e..07fc39bf71 100644 --- a/library/ui/src/main/res/values-fr/strings.xml +++ b/library/ui/src/main/res/values-fr/strings.xml @@ -12,7 +12,6 @@ Tout lire en boucle Aléatoire Mode plein écran - Téléchargement en attente Téléchargement… Téléchargement terminé Échec du téléchargement diff --git a/library/ui/src/main/res/values-gl/strings.xml b/library/ui/src/main/res/values-gl/strings.xml index 5a0e83fbf7..f46b00b9ec 100644 --- a/library/ui/src/main/res/values-gl/strings.xml +++ b/library/ui/src/main/res/values-gl/strings.xml @@ -12,7 +12,6 @@ Repetir todas as pistas Reprodución aleatoria Modo de pantalla completa - A descarga está na cola Descargando Completouse a descarga Produciuse un erro na descarga diff --git a/library/ui/src/main/res/values-gu/strings.xml b/library/ui/src/main/res/values-gu/strings.xml index ae1618d830..b129bc87ff 100644 --- a/library/ui/src/main/res/values-gu/strings.xml +++ b/library/ui/src/main/res/values-gu/strings.xml @@ -12,7 +12,6 @@ બધાને રિપીટ કરો શફલ કરો પૂર્ણસ્ક્રીન મોડ - ડાઉનલોડ માટે કતારમાં છે ડાઉનલોડ કરી રહ્યાં છીએ ડાઉનલોડ પૂર્ણ થયું ડાઉનલોડ નિષ્ફળ થયું diff --git a/library/ui/src/main/res/values-hi/strings.xml b/library/ui/src/main/res/values-hi/strings.xml index e5b7554acf..200f1ad140 100644 --- a/library/ui/src/main/res/values-hi/strings.xml +++ b/library/ui/src/main/res/values-hi/strings.xml @@ -12,7 +12,6 @@ सभी को दोहराएं शफ़ल करें फ़ुलस्क्रीन मोड - डाउनलोड को कतार में लगाया गया डाउनलोड हो रहा है डाउनलोड पूरा हुआ डाउनलोड नहीं हो सका diff --git a/library/ui/src/main/res/values-hr/strings.xml b/library/ui/src/main/res/values-hr/strings.xml index 324dedf417..6cec5d5074 100644 --- a/library/ui/src/main/res/values-hr/strings.xml +++ b/library/ui/src/main/res/values-hr/strings.xml @@ -12,7 +12,6 @@ Ponovi sve Reproduciraj nasumično Prikaz na cijelom zaslonu - Preuzimanje na čekanju Preuzimanje datoteka Preuzimanje je dovršeno Preuzimanje nije uspjelo diff --git a/library/ui/src/main/res/values-hu/strings.xml b/library/ui/src/main/res/values-hu/strings.xml index dd898ff22f..f3932ac085 100644 --- a/library/ui/src/main/res/values-hu/strings.xml +++ b/library/ui/src/main/res/values-hu/strings.xml @@ -12,7 +12,6 @@ Összes szám ismétlése Keverés Teljes képernyős mód - Letöltés várólistára helyezve Letöltés folyamatban A letöltés befejeződött Nem sikerült a letöltés diff --git a/library/ui/src/main/res/values-hy/strings.xml b/library/ui/src/main/res/values-hy/strings.xml index 73f8bff5e0..321bf61894 100644 --- a/library/ui/src/main/res/values-hy/strings.xml +++ b/library/ui/src/main/res/values-hy/strings.xml @@ -12,7 +12,6 @@ Կրկնել բոլորը Խառնել Լիաէկրան ռեժիմ - Ներբեռնումը շուտով կսկսվի Ներբեռնում Ներբեռնումն ավարտվեց Չհաջողվեց ներբեռնել diff --git a/library/ui/src/main/res/values-in/strings.xml b/library/ui/src/main/res/values-in/strings.xml index 6bc073d1fa..a4becda244 100644 --- a/library/ui/src/main/res/values-in/strings.xml +++ b/library/ui/src/main/res/values-in/strings.xml @@ -12,7 +12,6 @@ Ulangi semua Acak Mode layar penuh - Download masih dalam antrean Mendownload Download selesai Download gagal diff --git a/library/ui/src/main/res/values-is/strings.xml b/library/ui/src/main/res/values-is/strings.xml index e54fa9d7d4..02cee0d275 100644 --- a/library/ui/src/main/res/values-is/strings.xml +++ b/library/ui/src/main/res/values-is/strings.xml @@ -12,7 +12,6 @@ Endurtaka allt Stokka Allur skjárinn - Niðurhal í bið Sækir Niðurhali lokið Niðurhal mistókst diff --git a/library/ui/src/main/res/values-it/strings.xml b/library/ui/src/main/res/values-it/strings.xml index 9d84c760d1..9719029c12 100644 --- a/library/ui/src/main/res/values-it/strings.xml +++ b/library/ui/src/main/res/values-it/strings.xml @@ -12,7 +12,6 @@ Ripeti tutto Riproduzione casuale Modalità a schermo intero - Download aggiunto alla coda Download Download completato Download non riuscito diff --git a/library/ui/src/main/res/values-iw/strings.xml b/library/ui/src/main/res/values-iw/strings.xml index 5e08c74969..fb5eea4d9c 100644 --- a/library/ui/src/main/res/values-iw/strings.xml +++ b/library/ui/src/main/res/values-iw/strings.xml @@ -12,7 +12,6 @@ חזור על הכול ערבוב מצב מסך מלא - ההורדה עדיין לא התחילה מתבצעת הורדה ההורדה הושלמה ההורדה לא הושלמה diff --git a/library/ui/src/main/res/values-ja/strings.xml b/library/ui/src/main/res/values-ja/strings.xml index ae1578204e..678c042e25 100644 --- a/library/ui/src/main/res/values-ja/strings.xml +++ b/library/ui/src/main/res/values-ja/strings.xml @@ -12,7 +12,6 @@ 全曲をリピート シャッフル 全画面モード - ダウンロードを待機しています ダウンロードしています ダウンロードが完了しました ダウンロードに失敗しました diff --git a/library/ui/src/main/res/values-ka/strings.xml b/library/ui/src/main/res/values-ka/strings.xml index 772137785f..b04e660d52 100644 --- a/library/ui/src/main/res/values-ka/strings.xml +++ b/library/ui/src/main/res/values-ka/strings.xml @@ -12,7 +12,6 @@ ყველას გამეორება არეულად დაკვრა სრულეკრანიანი რეჟიმი - ჩამოტვირთვა რიგს ელოდება მიმდინარეობს ჩამოტვირთვა ჩამოტვირთვა დასრულდა ჩამოტვირთვა ვერ მოხერხდა diff --git a/library/ui/src/main/res/values-kk/strings.xml b/library/ui/src/main/res/values-kk/strings.xml index 62958a6315..f66e4ede58 100644 --- a/library/ui/src/main/res/values-kk/strings.xml +++ b/library/ui/src/main/res/values-kk/strings.xml @@ -12,7 +12,6 @@ Барлығын қайталау Араластыру Толық экран режимі - Жүктеп алу кезегіне қойылды Жүктеп алынуда Жүктеп алынды Жүктеп алынбады diff --git a/library/ui/src/main/res/values-km/strings.xml b/library/ui/src/main/res/values-km/strings.xml index 8e0360a1d7..72ddcb4c57 100644 --- a/library/ui/src/main/res/values-km/strings.xml +++ b/library/ui/src/main/res/values-km/strings.xml @@ -12,7 +12,6 @@ លេង​ឡើងវិញ​ទាំងអស់ ច្របល់ មុខងារពេញ​អេក្រង់ - បាន​ដាក់ការទាញយក​​ក្នុងជួរ កំពុង​ទាញ​យក បាន​បញ្ចប់​ការទាញយក មិន​អាច​ទាញយក​បាន​ទេ diff --git a/library/ui/src/main/res/values-kn/strings.xml b/library/ui/src/main/res/values-kn/strings.xml index ff3d29450a..5507d8ecff 100644 --- a/library/ui/src/main/res/values-kn/strings.xml +++ b/library/ui/src/main/res/values-kn/strings.xml @@ -12,7 +12,6 @@ ಎಲ್ಲವನ್ನು ಪುನರಾವರ್ತಿಸಿ ಶಫಲ್‌ ಪೂರ್ಣ ಪರದೆ ಮೋಡ್ - ಡೌನ್‌ಲೋಡ್ ಸರದಿಯಲ್ಲಿದೆ ಡೌನ್‌ಲೋಡ್ ಮಾಡಲಾಗುತ್ತಿದೆ ಡೌನ್‌ಲೋಡ್ ಪೂರ್ಣಗೊಂಡಿದೆ ಡೌನ್‌ಲೋಡ್‌ ವಿಫಲಗೊಂಡಿದೆ diff --git a/library/ui/src/main/res/values-ko/strings.xml b/library/ui/src/main/res/values-ko/strings.xml index 21e2bece31..a9abec2e7a 100644 --- a/library/ui/src/main/res/values-ko/strings.xml +++ b/library/ui/src/main/res/values-ko/strings.xml @@ -12,7 +12,6 @@ 모두 반복 셔플 전체화면 모드 - 다운로드 대기 중 다운로드하는 중 다운로드 완료 다운로드 실패 diff --git a/library/ui/src/main/res/values-ky/strings.xml b/library/ui/src/main/res/values-ky/strings.xml index 0582cce53c..93bd751893 100644 --- a/library/ui/src/main/res/values-ky/strings.xml +++ b/library/ui/src/main/res/values-ky/strings.xml @@ -12,7 +12,6 @@ Баарын кайталоо Аралаштыруу Толук экран режими - Жүктөп алуу кезекке коюлду Жүктөлүп алынууда Жүктөп алуу аяктады Жүктөлүп алынбай калды diff --git a/library/ui/src/main/res/values-lo/strings.xml b/library/ui/src/main/res/values-lo/strings.xml index ef888ab7f0..a72cefcc8e 100644 --- a/library/ui/src/main/res/values-lo/strings.xml +++ b/library/ui/src/main/res/values-lo/strings.xml @@ -12,7 +12,6 @@ ຫຼິ້ນຊ້ຳທັງໝົດ ຫຼີ້ນແບບສຸ່ມ ໂໝດເຕັມຈໍ - ຈັດຄິວດາວໂຫລດໄວ້ແລ້ວ ກຳລັງດາວໂຫລດ ດາວໂຫລດສຳເລັດແລ້ວ ດາວໂຫຼດບໍ່ສຳເລັດ diff --git a/library/ui/src/main/res/values-lt/strings.xml b/library/ui/src/main/res/values-lt/strings.xml index e756bd1019..3593a8080e 100644 --- a/library/ui/src/main/res/values-lt/strings.xml +++ b/library/ui/src/main/res/values-lt/strings.xml @@ -12,7 +12,6 @@ Kartoti viską Maišyti Viso ekrano režimas - Atsisiunč. elem. laukia eilėje Atsisiunčiama Atsisiuntimo procesas baigtas Nepavyko atsisiųsti diff --git a/library/ui/src/main/res/values-lv/strings.xml b/library/ui/src/main/res/values-lv/strings.xml index e330620e8b..e90977913d 100644 --- a/library/ui/src/main/res/values-lv/strings.xml +++ b/library/ui/src/main/res/values-lv/strings.xml @@ -12,7 +12,6 @@ Atkārtot visu Atskaņot jauktā secībā Pilnekrāna režīms - Lejupielāde gaida rindā Notiek lejupielāde Lejupielāde ir pabeigta Lejupielāde neizdevās diff --git a/library/ui/src/main/res/values-mk/strings.xml b/library/ui/src/main/res/values-mk/strings.xml index a014e1c0d1..b89c516ffa 100644 --- a/library/ui/src/main/res/values-mk/strings.xml +++ b/library/ui/src/main/res/values-mk/strings.xml @@ -12,7 +12,6 @@ Повтори ги сите Измешај Режим на цел екран - Преземањето чека на ред Се презема Преземањето заврши Неуспешно преземање diff --git a/library/ui/src/main/res/values-ml/strings.xml b/library/ui/src/main/res/values-ml/strings.xml index 37ea2be5e6..4eef4bb1d2 100644 --- a/library/ui/src/main/res/values-ml/strings.xml +++ b/library/ui/src/main/res/values-ml/strings.xml @@ -12,7 +12,6 @@ എല്ലാം ആവർത്തിക്കുക ഇടകലര്‍ത്തുക പൂർണ്ണ സ്‌ക്രീൻ മോഡ് - ഡൗൺലോഡ് ‌ക്യൂവിലാണ് ഡൗൺലോഡ് ചെയ്യുന്നു ഡൗൺലോഡ് പൂർത്തിയായി ഡൗൺലോഡ് പരാജയപ്പെട്ടു diff --git a/library/ui/src/main/res/values-mn/strings.xml b/library/ui/src/main/res/values-mn/strings.xml index 883489b53c..22f7c0c286 100644 --- a/library/ui/src/main/res/values-mn/strings.xml +++ b/library/ui/src/main/res/values-mn/strings.xml @@ -12,7 +12,6 @@ Бүгдийг нь дахин тоглуулах Холих Бүтэн дэлгэцийн горим - Татан авалтыг жагсаасан Татаж байна Татаж дууссан Татаж чадсангүй diff --git a/library/ui/src/main/res/values-mr/strings.xml b/library/ui/src/main/res/values-mr/strings.xml index ed9e44bbce..7e661b67ab 100644 --- a/library/ui/src/main/res/values-mr/strings.xml +++ b/library/ui/src/main/res/values-mr/strings.xml @@ -12,7 +12,6 @@ सर्व रीपीट करा शफल करा पूर्ण स्क्रीन मोड - रांगेत लावलेले डाउनलोड करा डाउनलोड होत आहे डाउनलोड पूर्ण झाले डाउनलोड अयशस्वी झाले diff --git a/library/ui/src/main/res/values-ms/strings.xml b/library/ui/src/main/res/values-ms/strings.xml index aedb67fe92..6bc72513cc 100644 --- a/library/ui/src/main/res/values-ms/strings.xml +++ b/library/ui/src/main/res/values-ms/strings.xml @@ -12,7 +12,6 @@ Ulang semua Rombak Mod skrin penuh - Muat turun dibaris gilir Memuat turun Muat turun selesai Muat turun gagal diff --git a/library/ui/src/main/res/values-my/strings.xml b/library/ui/src/main/res/values-my/strings.xml index fe509be395..d49258e90d 100644 --- a/library/ui/src/main/res/values-my/strings.xml +++ b/library/ui/src/main/res/values-my/strings.xml @@ -12,7 +12,6 @@ အားလုံး ပြန်ကျော့ရန် ရောသမမွှေ မျက်နှာပြင်အပြည့် မုဒ် - ဒေါင်းလုဒ်လုပ်ရန် စီထားသည် ဒေါင်းလုဒ်လုပ်နေသည် ဒေါင်းလုဒ်လုပ်ပြီးပါပြီ ဒေါင်းလုဒ်လုပ်၍ မရပါ diff --git a/library/ui/src/main/res/values-nb/strings.xml b/library/ui/src/main/res/values-nb/strings.xml index 267a82994e..e140fa4b94 100644 --- a/library/ui/src/main/res/values-nb/strings.xml +++ b/library/ui/src/main/res/values-nb/strings.xml @@ -12,7 +12,6 @@ Gjenta alle Tilfeldig rekkefølge Fullskjermmodus - Nedlasting står i kø Laster ned Nedlastingen er fullført Nedlastingen mislyktes diff --git a/library/ui/src/main/res/values-ne/strings.xml b/library/ui/src/main/res/values-ne/strings.xml index ac3d71d356..292e3ad362 100644 --- a/library/ui/src/main/res/values-ne/strings.xml +++ b/library/ui/src/main/res/values-ne/strings.xml @@ -12,7 +12,6 @@ सबै दोहोर्‍याउनुहोस् मिसाउनुहोस् पूर्ण स्क्रिन मोड - डाउनलोडलाई लाइनमा राखियो डाउनलोड गरिँदै छ डाउनलोड सम्पन्न भयो डाउनलोड गर्न सकिएन diff --git a/library/ui/src/main/res/values-nl/strings.xml b/library/ui/src/main/res/values-nl/strings.xml index 70bda2fdcc..20a5089729 100644 --- a/library/ui/src/main/res/values-nl/strings.xml +++ b/library/ui/src/main/res/values-nl/strings.xml @@ -12,7 +12,6 @@ Alles herhalen Shuffle Modus \'Volledig scherm\' - Download in de wachtrij Downloaden Downloaden voltooid Downloaden mislukt diff --git a/library/ui/src/main/res/values-pa/strings.xml b/library/ui/src/main/res/values-pa/strings.xml index 59affc3aa9..7a95d0b26a 100644 --- a/library/ui/src/main/res/values-pa/strings.xml +++ b/library/ui/src/main/res/values-pa/strings.xml @@ -12,7 +12,6 @@ ਸਾਰਿਆਂ ਨੂੰ ਦੁਹਰਾਓ ਬੇਤਰਤੀਬ ਕਰੋ ਪੂਰੀ-ਸਕ੍ਰੀਨ ਮੋਡ - ਡਾਊਨਲੋਡ ਕਤਾਰਬੱਧ ਕੀਤਾ ਗਿਆ ਡਾਊਨਲੋਡ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ ਡਾਊਨਲੋਡ ਮੁਕੰਮਲ ਹੋਇਆ ਡਾਊਨਲੋਡ ਅਸਫਲ ਰਿਹਾ diff --git a/library/ui/src/main/res/values-pl/strings.xml b/library/ui/src/main/res/values-pl/strings.xml index ef341f88e4..6beca2d401 100644 --- a/library/ui/src/main/res/values-pl/strings.xml +++ b/library/ui/src/main/res/values-pl/strings.xml @@ -12,7 +12,6 @@ Powtórz wszystkie Odtwarzanie losowe Tryb pełnoekranowy - W kolejce pobierania Pobieranie Zakończono pobieranie Nie udało się pobrać diff --git a/library/ui/src/main/res/values-pt-rPT/strings.xml b/library/ui/src/main/res/values-pt-rPT/strings.xml index 06fc3cc5eb..0251ce5bb6 100644 --- a/library/ui/src/main/res/values-pt-rPT/strings.xml +++ b/library/ui/src/main/res/values-pt-rPT/strings.xml @@ -12,7 +12,6 @@ Repetir tudo Reproduzir aleatoriamente Modo de ecrã inteiro - Transfer. em fila de espera A transferir… Transferência concluída Falha na transferência diff --git a/library/ui/src/main/res/values-pt/strings.xml b/library/ui/src/main/res/values-pt/strings.xml index 9c25f7de5f..af55905723 100644 --- a/library/ui/src/main/res/values-pt/strings.xml +++ b/library/ui/src/main/res/values-pt/strings.xml @@ -12,7 +12,6 @@ Repetir tudo Aleatório Modo de tela cheia - Item na fila de download Fazendo download Download concluído Falha no download diff --git a/library/ui/src/main/res/values-ro/strings.xml b/library/ui/src/main/res/values-ro/strings.xml index 4541a6c6e8..144049d231 100644 --- a/library/ui/src/main/res/values-ro/strings.xml +++ b/library/ui/src/main/res/values-ro/strings.xml @@ -12,7 +12,6 @@ Repetați-le pe toate Redați aleatoriu Modul Ecran complet - Descărcarea este în lista de așteptare Se descarcă Descărcarea a fost finalizată Descărcarea nu a reușit diff --git a/library/ui/src/main/res/values-ru/strings.xml b/library/ui/src/main/res/values-ru/strings.xml index 4e030ef5a4..53a91a08c5 100644 --- a/library/ui/src/main/res/values-ru/strings.xml +++ b/library/ui/src/main/res/values-ru/strings.xml @@ -12,7 +12,6 @@ Повторять все Перемешать Полноэкранный режим - В очереди на скачивание Загрузка файлов Скачивание завершено Ошибка скачивания diff --git a/library/ui/src/main/res/values-si/strings.xml b/library/ui/src/main/res/values-si/strings.xml index 0ccc0b9d26..0d5e334b6c 100644 --- a/library/ui/src/main/res/values-si/strings.xml +++ b/library/ui/src/main/res/values-si/strings.xml @@ -12,7 +12,6 @@ සියල්ල පුනරාවර්තනය කරන්න කලවම් කරන්න සම්පූර්ණ තිර ප්‍රකාරය - බාගැනීම පේළියට තබන ලදී බාගනිමින් බාගැනීම සම්පූර්ණ කරන ලදී බාගැනීම අසමත් විය diff --git a/library/ui/src/main/res/values-sk/strings.xml b/library/ui/src/main/res/values-sk/strings.xml index c827282b95..1ae27b1e4a 100644 --- a/library/ui/src/main/res/values-sk/strings.xml +++ b/library/ui/src/main/res/values-sk/strings.xml @@ -12,7 +12,6 @@ Opakovať všetko Náhodne prehrávať Režim celej obrazovky - Sťahovanie je v poradí Sťahuje sa Sťahovanie bolo dokončené Nepodarilo sa stiahnuť diff --git a/library/ui/src/main/res/values-sl/strings.xml b/library/ui/src/main/res/values-sl/strings.xml index c26a91826e..0a3a013ee0 100644 --- a/library/ui/src/main/res/values-sl/strings.xml +++ b/library/ui/src/main/res/values-sl/strings.xml @@ -12,7 +12,6 @@ Ponavljanje vseh Naključno predvajanje Celozaslonski način - Prenos je v čakalni vrsti Prenašanje Prenos je končan Prenos ni uspel diff --git a/library/ui/src/main/res/values-sq/strings.xml b/library/ui/src/main/res/values-sq/strings.xml index 56868ce503..0611398306 100644 --- a/library/ui/src/main/res/values-sq/strings.xml +++ b/library/ui/src/main/res/values-sq/strings.xml @@ -12,7 +12,6 @@ Përsërit të gjitha Përziej Modaliteti me ekran të plotë - Shkarkimi u vendos në radhë Po shkarkohet Shkarkimi përfundoi Shkarkimi dështoi diff --git a/library/ui/src/main/res/values-sr/strings.xml b/library/ui/src/main/res/values-sr/strings.xml index 8ba19a51a6..9e7b3918d5 100644 --- a/library/ui/src/main/res/values-sr/strings.xml +++ b/library/ui/src/main/res/values-sr/strings.xml @@ -12,7 +12,6 @@ Понови све Пусти насумично Режим целог екрана - Преузимање је на чекању Преузимање Преузимање је завршено Преузимање није успело diff --git a/library/ui/src/main/res/values-sv/strings.xml b/library/ui/src/main/res/values-sv/strings.xml index 6840738ba1..e7bcc05f6d 100644 --- a/library/ui/src/main/res/values-sv/strings.xml +++ b/library/ui/src/main/res/values-sv/strings.xml @@ -12,7 +12,6 @@ Upprepa alla Blanda spår Helskärmsläge - Nedladdningen har köplacerats Laddar ned Nedladdningen är klar Nedladdningen misslyckades diff --git a/library/ui/src/main/res/values-sw/strings.xml b/library/ui/src/main/res/values-sw/strings.xml index 5db4ee5995..5ee3613e8b 100644 --- a/library/ui/src/main/res/values-sw/strings.xml +++ b/library/ui/src/main/res/values-sw/strings.xml @@ -12,7 +12,6 @@ Rudia zote Changanya Hali ya skrini nzima - Inasubiri kupakuliwa Inapakua Imepakuliwa Imeshindwa kupakua diff --git a/library/ui/src/main/res/values-ta/strings.xml b/library/ui/src/main/res/values-ta/strings.xml index 65f02a38a2..7fa7a1e298 100644 --- a/library/ui/src/main/res/values-ta/strings.xml +++ b/library/ui/src/main/res/values-ta/strings.xml @@ -12,7 +12,6 @@ அனைத்தையும் மீண்டும் இயக்கு கலைத்துப் போடு முழுத்திரைப் பயன்முறை - பதிவிறக்கம், வரிசையில் உள்ளது பதிவிறக்கப்படுகிறது பதிவிறக்கப்பட்டது பதிவிறக்க முடியவில்லை diff --git a/library/ui/src/main/res/values-te/strings.xml b/library/ui/src/main/res/values-te/strings.xml index d27afe9ce7..038ac32afa 100644 --- a/library/ui/src/main/res/values-te/strings.xml +++ b/library/ui/src/main/res/values-te/strings.xml @@ -12,7 +12,6 @@ అన్నింటినీ పునరావృతం చేయండి షఫుల్ చేయండి పూర్తి స్క్రీన్ మోడ్ - డౌన్‌లోడ్ క్రమవరుసలో ఉంది డౌన్‌లోడ్ చేస్తోంది డౌన్‌లోడ్ పూర్తయింది డౌన్‌లోడ్ విఫలమైంది diff --git a/library/ui/src/main/res/values-th/strings.xml b/library/ui/src/main/res/values-th/strings.xml index bb5edb211c..f472433913 100644 --- a/library/ui/src/main/res/values-th/strings.xml +++ b/library/ui/src/main/res/values-th/strings.xml @@ -12,7 +12,6 @@ เล่นซ้ำทั้งหมด สุ่ม โหมดเต็มหน้าจอ - การดาวน์โหลดอยู่ในคิว กำลังดาวน์โหลด การดาวน์โหลดเสร็จสมบูรณ์ การดาวน์โหลดล้มเหลว diff --git a/library/ui/src/main/res/values-tl/strings.xml b/library/ui/src/main/res/values-tl/strings.xml index 4807026e05..31779c8172 100644 --- a/library/ui/src/main/res/values-tl/strings.xml +++ b/library/ui/src/main/res/values-tl/strings.xml @@ -12,7 +12,6 @@ Ulitin lahat I-shuffle Fullscreen mode - Naka-queue ang download Nagda-download Tapos na ang pag-download Hindi na-download diff --git a/library/ui/src/main/res/values-tr/strings.xml b/library/ui/src/main/res/values-tr/strings.xml index a8a409676f..66f7870839 100644 --- a/library/ui/src/main/res/values-tr/strings.xml +++ b/library/ui/src/main/res/values-tr/strings.xml @@ -12,7 +12,6 @@ Tümünü tekrarla Karıştır Tam ekran modu - İndirme işlemi sıraya alındı İndiriliyor İndirme işlemi tamamlandı İndirilemedi diff --git a/library/ui/src/main/res/values-uk/strings.xml b/library/ui/src/main/res/values-uk/strings.xml index 44190c4dde..a73379572d 100644 --- a/library/ui/src/main/res/values-uk/strings.xml +++ b/library/ui/src/main/res/values-uk/strings.xml @@ -12,7 +12,6 @@ Повторити всі Перемішати Повноекранний режим - Завантаження розміщено в черзі Завантажується Завантаження завершено Не вдалося завантажити diff --git a/library/ui/src/main/res/values-ur/strings.xml b/library/ui/src/main/res/values-ur/strings.xml index 26e1b0e060..99451aa622 100644 --- a/library/ui/src/main/res/values-ur/strings.xml +++ b/library/ui/src/main/res/values-ur/strings.xml @@ -12,7 +12,6 @@ سبھی کو دہرائیں شفل کریں پوری اسکرین والی وضع - ڈاؤن لوڈ قطار بند ہے ڈاؤن لوڈ کیا جا رہا ہے ڈاؤن لوڈ مکمل ہو گیا ڈاؤن لوڈ ناکام ہو گیا diff --git a/library/ui/src/main/res/values-uz/strings.xml b/library/ui/src/main/res/values-uz/strings.xml index 26d37380d7..fbc30c1de2 100644 --- a/library/ui/src/main/res/values-uz/strings.xml +++ b/library/ui/src/main/res/values-uz/strings.xml @@ -12,7 +12,6 @@ Hammasini takrorlash Aralash Butun ekran rejimi - Yuklab olish navbatga olindi Yuklab olinmoqda Yuklab olindi Yuklab olinmadi diff --git a/library/ui/src/main/res/values-vi/strings.xml b/library/ui/src/main/res/values-vi/strings.xml index 2754eec898..c9cc4d910a 100644 --- a/library/ui/src/main/res/values-vi/strings.xml +++ b/library/ui/src/main/res/values-vi/strings.xml @@ -12,7 +12,6 @@ Lặp lại tất cả Phát ngẫu nhiên Chế độ toàn màn hình - Đã đưa tài nguyên đã tải xuống vào hàng đợi Đang tải xuống Đã hoàn tất tải xuống Không tải xuống được diff --git a/library/ui/src/main/res/values-zh-rCN/strings.xml b/library/ui/src/main/res/values-zh-rCN/strings.xml index cb8beae7b9..84868e8746 100644 --- a/library/ui/src/main/res/values-zh-rCN/strings.xml +++ b/library/ui/src/main/res/values-zh-rCN/strings.xml @@ -12,7 +12,6 @@ 全部重复播放 随机播放 全屏模式 - 已加入待下载队列 正在下载 下载完毕 下载失败 diff --git a/library/ui/src/main/res/values-zh-rHK/strings.xml b/library/ui/src/main/res/values-zh-rHK/strings.xml index a61c20a847..8cc210b1ef 100644 --- a/library/ui/src/main/res/values-zh-rHK/strings.xml +++ b/library/ui/src/main/res/values-zh-rHK/strings.xml @@ -12,7 +12,6 @@ 全部重複播放 隨機播放 全螢幕模式 - 已加入下載列 正在下載 下載完畢 下載失敗 diff --git a/library/ui/src/main/res/values-zh-rTW/strings.xml b/library/ui/src/main/res/values-zh-rTW/strings.xml index cd6a8c1703..a30fea1c04 100644 --- a/library/ui/src/main/res/values-zh-rTW/strings.xml +++ b/library/ui/src/main/res/values-zh-rTW/strings.xml @@ -12,7 +12,6 @@ 重複播放所有項目 隨機播放 全螢幕模式 - 已排入下載佇列 下載中 下載完成 無法下載 diff --git a/library/ui/src/main/res/values-zu/strings.xml b/library/ui/src/main/res/values-zu/strings.xml index 19bfab08fc..de9a39d3bd 100644 --- a/library/ui/src/main/res/values-zu/strings.xml +++ b/library/ui/src/main/res/values-zu/strings.xml @@ -12,7 +12,6 @@ Phinda konke Shova Imodi yesikrini esigcwele - Ukulanda kukulayini Iyalanda Ukulanda kuqedile Ukulanda kuhlulekile diff --git a/library/ui/src/main/res/values/strings.xml b/library/ui/src/main/res/values/strings.xml index c5967a260a..2675445d7f 100644 --- a/library/ui/src/main/res/values/strings.xml +++ b/library/ui/src/main/res/values/strings.xml @@ -38,8 +38,6 @@ Shuffle Fullscreen mode - - Download queued Downloading From fb5e31d3d6627ab231ee10b758ae75633cfff549 Mon Sep 17 00:00:00 2001 From: hoangtc Date: Wed, 18 Apr 2018 02:38:42 -0700 Subject: [PATCH 052/157] Simplify logic to handle FLAC files with ID3 header. LibFlac internally can skip ID3 tags correctly. Therefore, we don't need to keep track of the whole ID3 header section and skip through this section in Java code. We can just set the whole stream to the native library, and it will handle skipping ID3 tags correctly. The only thing that the Java part need to do is peeking and parsing ID3 tags (if present), in order to populate the track format metadata. GitHub: #4055. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=193327602 --- .../exoplayer2/ext/flac/FlacExtractor.java | 46 ++++++------------- 1 file changed, 13 insertions(+), 33 deletions(-) diff --git a/extensions/flac/src/main/java/com/google/android/exoplayer2/ext/flac/FlacExtractor.java b/extensions/flac/src/main/java/com/google/android/exoplayer2/ext/flac/FlacExtractor.java index 729a406315..34a6e6820d 100644 --- a/extensions/flac/src/main/java/com/google/android/exoplayer2/ext/flac/FlacExtractor.java +++ b/extensions/flac/src/main/java/com/google/android/exoplayer2/ext/flac/FlacExtractor.java @@ -79,7 +79,7 @@ public final class FlacExtractor implements Extractor { private static final byte[] FLAC_SIGNATURE = {'f', 'L', 'a', 'C', 0, 0, 0, 0x22}; private final Id3Peeker id3Peeker; - private final @Flags int flags; + private final boolean isId3MetadataDisabled; private FlacDecoderJni decoderJni; @@ -90,7 +90,6 @@ public final class FlacExtractor implements Extractor { private ByteBuffer outputByteBuffer; private Metadata id3Metadata; - private long id3SectionSize; private boolean metadataParsed; @@ -105,8 +104,8 @@ public final class FlacExtractor implements Extractor { * @param flags Flags that control the extractor's behavior. */ public FlacExtractor(int flags) { - this.flags = flags; id3Peeker = new Id3Peeker(); + isId3MetadataDisabled = (flags & FLAG_DISABLE_ID3_METADATA) != 0; } @Override @@ -125,24 +124,16 @@ public final class FlacExtractor implements Extractor { public boolean sniff(ExtractorInput input) throws IOException, InterruptedException { if (input.getPosition() == 0) { id3Metadata = peekId3Data(input); - id3SectionSize = input.getPeekPosition(); } - boolean isFlacFormat = peekFlacSignature(input); - if (isFlacFormat) { - // If this is FLAC format, we should skip the whole ID3 section. - skipFullyId3Section(input); - } - return isFlacFormat; + return peekFlacSignature(input); } @Override public int read(final ExtractorInput input, PositionHolder seekPosition) throws IOException, InterruptedException { - if (input.getPosition() == 0) { + if (input.getPosition() == 0 && !isId3MetadataDisabled && id3Metadata == null) { id3Metadata = peekId3Data(input); - id3SectionSize = input.getPeekPosition(); } - skipFullyId3Section(input); decoderJni.setData(input); @@ -155,7 +146,7 @@ public final class FlacExtractor implements Extractor { } } catch (IOException e) { decoderJni.reset(0); - input.setRetryPosition(id3SectionSize, e); + input.setRetryPosition(0, e); throw e; // never executes } metadataParsed = true; @@ -163,7 +154,7 @@ public final class FlacExtractor implements Extractor { boolean isSeekable = decoderJni.getSeekPosition(0) != -1; extractorOutput.seekMap( isSeekable - ? new FlacSeekMap(streamInfo.durationUs(), decoderJni, id3SectionSize) + ? new FlacSeekMap(streamInfo.durationUs(), decoderJni) : new SeekMap.Unseekable(streamInfo.durationUs(), 0)); Format mediaFormat = Format.createAudioSampleFormat( @@ -181,7 +172,7 @@ public final class FlacExtractor implements Extractor { /* drmInitData= */ null, /* selectionFlags= */ 0, /* language= */ null, - (flags & FLAG_DISABLE_ID3_METADATA) != 0 ? null : id3Metadata); + isId3MetadataDisabled ? null : id3Metadata); trackOutput.format(mediaFormat); outputBuffer = new ParsableByteArray(streamInfo.maxDecodedFrameSize()); @@ -196,7 +187,7 @@ public final class FlacExtractor implements Extractor { } catch (IOException e) { if (lastDecodePosition >= 0) { decoderJni.reset(lastDecodePosition); - input.setRetryPosition(id3SectionSize + lastDecodePosition, e); + input.setRetryPosition(lastDecodePosition, e); } throw e; } @@ -212,12 +203,11 @@ public final class FlacExtractor implements Extractor { @Override public void seek(long position, long timeUs) { - if (position <= id3SectionSize) { + if (position == 0) { metadataParsed = false; } - long flacStreamPosition = Math.max(0, position - id3SectionSize); if (decoderJni != null) { - decoderJni.reset(flacStreamPosition); + decoderJni.reset(position); } } @@ -238,9 +228,8 @@ public final class FlacExtractor implements Extractor { @Nullable private Metadata peekId3Data(ExtractorInput input) throws IOException, InterruptedException { input.resetPeekPosition(); - boolean disableId3Frames = (flags & FLAG_DISABLE_ID3_METADATA) != 0; Id3Decoder.FramePredicate id3FramePredicate = - disableId3Frames ? Id3Decoder.NO_FRAMES_PREDICATE : null; + isId3MetadataDisabled ? Id3Decoder.NO_FRAMES_PREDICATE : null; return id3Peeker.peekId3Data(input, id3FramePredicate); } @@ -255,22 +244,14 @@ public final class FlacExtractor implements Extractor { return Arrays.equals(header, FLAC_SIGNATURE); } - /** Skips input until we have passed the whole Id3 section. */ - private void skipFullyId3Section(ExtractorInput input) throws IOException, InterruptedException { - int bytesToSkip = Math.max(0, (int) (id3SectionSize - input.getPosition())); - input.skipFully(bytesToSkip); - } - private static final class FlacSeekMap implements SeekMap { private final long durationUs; private final FlacDecoderJni decoderJni; - private final long id3SectionSize; - public FlacSeekMap(long durationUs, FlacDecoderJni decoderJni, long id3SectionSize) { + public FlacSeekMap(long durationUs, FlacDecoderJni decoderJni) { this.durationUs = durationUs; this.decoderJni = decoderJni; - this.id3SectionSize = id3SectionSize; } @Override @@ -281,8 +262,7 @@ public final class FlacExtractor implements Extractor { @Override public SeekPoints getSeekPoints(long timeUs) { // TODO: Access the seek table via JNI to return two seek points when appropriate. - return new SeekPoints( - new SeekPoint(timeUs, id3SectionSize + decoderJni.getSeekPosition(timeUs))); + return new SeekPoints(new SeekPoint(timeUs, decoderJni.getSeekPosition(timeUs))); } @Override From d4aceef8a8bdc868c9d8d2b82c597ab7057cca3a Mon Sep 17 00:00:00 2001 From: olly Date: Wed, 18 Apr 2018 12:41:15 -0700 Subject: [PATCH 053/157] Add getPlaybackError to Player/ExoPlayer interface ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=193400443 --- RELEASENOTES.md | 1 + .../android/exoplayer2/ext/cast/CastPlayer.java | 5 +++++ .../com/google/android/exoplayer2/ExoPlayer.java | 4 ++++ .../google/android/exoplayer2/ExoPlayerImpl.java | 14 ++++++++++++-- .../java/com/google/android/exoplayer2/Player.java | 11 +++++++++++ .../google/android/exoplayer2/SimpleExoPlayer.java | 5 +++++ .../android/exoplayer2/testutil/StubExoPlayer.java | 6 ++++++ 7 files changed, 44 insertions(+), 2 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 4a42639c84..72ca26c6b7 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -9,6 +9,7 @@ * Updated default max buffer length in `DefaultLoadControl`. * Added `AnalyticsListener` interface which can be registered in `SimpleExoPlayer` to receive detailed meta data for each ExoPlayer event. +* Added `getPlaybackError` to `Player` interface. * UI components: * Add support for listening to `AspectRatioFrameLayout`'s aspect ratio update ([#3736](https://github.com/google/ExoPlayer/issues/3736)). diff --git a/extensions/cast/src/main/java/com/google/android/exoplayer2/ext/cast/CastPlayer.java b/extensions/cast/src/main/java/com/google/android/exoplayer2/ext/cast/CastPlayer.java index 7e0753ba10..68226fdcf4 100644 --- a/extensions/cast/src/main/java/com/google/android/exoplayer2/ext/cast/CastPlayer.java +++ b/extensions/cast/src/main/java/com/google/android/exoplayer2/ext/cast/CastPlayer.java @@ -307,6 +307,11 @@ public final class CastPlayer implements Player { return playbackState; } + @Override + public Exception getPlaybackError() { + return null; + } + @Override public void setPlayWhenReady(boolean playWhenReady) { if (remoteMediaClient == null) { diff --git a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayer.java b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayer.java index 39a6243933..6d8dd5b7a8 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayer.java @@ -185,6 +185,10 @@ public interface ExoPlayer extends Player { */ Looper getPlaybackLooper(); + @Override + @Nullable + ExoPlaybackException getPlaybackError(); + /** * Prepares the player to play the provided {@link MediaSource}. Equivalent to * {@code prepare(mediaSource, true, true)}. diff --git a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java index daabbd5a72..5ca5994b6e 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java @@ -61,6 +61,7 @@ import java.util.concurrent.CopyOnWriteArraySet; private boolean hasPendingPrepare; private boolean hasPendingSeek; private PlaybackParameters playbackParameters; + private @Nullable ExoPlaybackException playbackError; // Playback information when there is no pending seek/set source operation. private PlaybackInfo playbackInfo; @@ -156,6 +157,11 @@ import java.util.concurrent.CopyOnWriteArraySet; return playbackInfo.playbackState; } + @Override + public @Nullable ExoPlaybackException getPlaybackError() { + return playbackError; + } + @Override public void prepare(MediaSource mediaSource) { prepare(mediaSource, true, true); @@ -163,6 +169,7 @@ import java.util.concurrent.CopyOnWriteArraySet; @Override public void prepare(MediaSource mediaSource, boolean resetPosition, boolean resetState) { + playbackError = null; PlaybackInfo playbackInfo = getResetPlaybackInfo( resetPosition, resetState, /* playbackState= */ Player.STATE_BUFFERING); @@ -325,6 +332,9 @@ import java.util.concurrent.CopyOnWriteArraySet; @Override public void stop(boolean reset) { + if (reset) { + playbackError = null; + } PlaybackInfo playbackInfo = getResetPlaybackInfo( /* resetPosition= */ reset, @@ -560,9 +570,9 @@ import java.util.concurrent.CopyOnWriteArraySet; } break; case ExoPlayerImplInternal.MSG_ERROR: - ExoPlaybackException exception = (ExoPlaybackException) msg.obj; + playbackError = (ExoPlaybackException) msg.obj; for (Player.EventListener listener : listeners) { - listener.onPlayerError(exception); + listener.onPlayerError(playbackError); } break; default: diff --git a/library/core/src/main/java/com/google/android/exoplayer2/Player.java b/library/core/src/main/java/com/google/android/exoplayer2/Player.java index 85872339a3..743a0241bd 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/Player.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/Player.java @@ -459,6 +459,17 @@ public interface Player { */ int getPlaybackState(); + /** + * Returns the error that caused playback to fail. This is the same error that will have been + * reported via @link Player.EventListener#onPlayerError(ExoPlaybackException)} at the time of + * failure. It can be queried using this method until {@code stop(true)} is called or the player + * is re-prepared. + * + * @return The error, or {@code null}. + */ + @Nullable + Exception getPlaybackError(); + /** * Sets whether playback should proceed when {@link #getPlaybackState()} == {@link #STATE_READY}. *

diff --git a/library/core/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java b/library/core/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java index b998027eb3..9a73f68f34 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java @@ -653,6 +653,11 @@ public class SimpleExoPlayer implements ExoPlayer, Player.VideoComponent, Player return player.getPlaybackState(); } + @Override + public ExoPlaybackException getPlaybackError() { + return player.getPlaybackError(); + } + @Override public void prepare(MediaSource mediaSource) { prepare(mediaSource, /* resetPosition= */ true, /* resetState= */ true); diff --git a/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/StubExoPlayer.java b/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/StubExoPlayer.java index 8de92ddfa9..d81cef9d8a 100644 --- a/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/StubExoPlayer.java +++ b/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/StubExoPlayer.java @@ -17,6 +17,7 @@ package com.google.android.exoplayer2.testutil; import android.os.Looper; import android.support.annotation.Nullable; +import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.ExoPlayer; import com.google.android.exoplayer2.PlaybackParameters; import com.google.android.exoplayer2.Player; @@ -63,6 +64,11 @@ public abstract class StubExoPlayer implements ExoPlayer { throw new UnsupportedOperationException(); } + @Override + public ExoPlaybackException getPlaybackError() { + throw new UnsupportedOperationException(); + } + @Override public void prepare(MediaSource mediaSource) { throw new UnsupportedOperationException(); From 8bbdb1b1437beab885a38e0077564309e9d6d290 Mon Sep 17 00:00:00 2001 From: andrewlewis Date: Thu, 19 Apr 2018 01:02:21 -0700 Subject: [PATCH 054/157] Add an @IntDef for notification channel importance This is to avoid callers needing to do an API check or suppress inlined constant warnings. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=193476188 --- .../exoplayer2/ui/NotificationUtil.java | 49 ++++++++++++++++--- 1 file changed, 41 insertions(+), 8 deletions(-) diff --git a/library/ui/src/main/java/com/google/android/exoplayer2/ui/NotificationUtil.java b/library/ui/src/main/java/com/google/android/exoplayer2/ui/NotificationUtil.java index ab665bcc01..94700b1662 100644 --- a/library/ui/src/main/java/com/google/android/exoplayer2/ui/NotificationUtil.java +++ b/library/ui/src/main/java/com/google/android/exoplayer2/ui/NotificationUtil.java @@ -15,18 +15,46 @@ */ package com.google.android.exoplayer2.ui; +import android.annotation.SuppressLint; import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager; import android.content.Context; import android.content.Intent; +import android.support.annotation.IntDef; import android.support.annotation.Nullable; +import android.support.annotation.StringRes; import com.google.android.exoplayer2.util.Util; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; /** Utility methods for displaying {@link android.app.Notification}s. */ +@SuppressLint("InlinedApi") public final class NotificationUtil { - private NotificationUtil() {} + /** Notification channel importance levels. */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({ + IMPORTANCE_UNSPECIFIED, + IMPORTANCE_NONE, + IMPORTANCE_MIN, + IMPORTANCE_LOW, + IMPORTANCE_DEFAULT, + IMPORTANCE_HIGH + }) + public @interface Importance {} + /** @see NotificationManager#IMPORTANCE_UNSPECIFIED */ + public static final int IMPORTANCE_UNSPECIFIED = NotificationManager.IMPORTANCE_UNSPECIFIED; + /** @see NotificationManager#IMPORTANCE_NONE */ + public static final int IMPORTANCE_NONE = NotificationManager.IMPORTANCE_NONE; + /** @see NotificationManager#IMPORTANCE_MIN */ + public static final int IMPORTANCE_MIN = NotificationManager.IMPORTANCE_MIN; + /** @see NotificationManager#IMPORTANCE_LOW */ + public static final int IMPORTANCE_LOW = NotificationManager.IMPORTANCE_LOW; + /** @see NotificationManager#IMPORTANCE_DEFAULT */ + public static final int IMPORTANCE_DEFAULT = NotificationManager.IMPORTANCE_DEFAULT; + /** @see NotificationManager#IMPORTANCE_HIGH */ + public static final int IMPORTANCE_HIGH = NotificationManager.IMPORTANCE_HIGH; /** * Creates a notification channel that notifications can be posted to. See {@link @@ -36,20 +64,23 @@ public final class NotificationUtil { * @param context A {@link Context} to retrieve {@link NotificationManager}. * @param id The id of the channel. Must be unique per package. The value may be truncated if it * is too long. - * @param name The user visible name of the channel. You can rename this channel when the system - * locale changes by listening for the {@link Intent#ACTION_LOCALE_CHANGED} broadcast. The - * recommended maximum length is 40 characters; the value may be truncated if it is too long. + * @param name A string resource identifier for the user visible name of the channel. You can + * rename this channel when the system locale changes by listening for the {@link + * Intent#ACTION_LOCALE_CHANGED} broadcast. The recommended maximum length is 40 characters; + * the value may be truncated if it is too long. * @param importance The importance of the channel. This controls how interruptive notifications - * posted to this channel are. + * posted to this channel are. One of {@link #IMPORTANCE_UNSPECIFIED}, {@link + * #IMPORTANCE_NONE}, {@link #IMPORTANCE_MIN}, {@link #IMPORTANCE_LOW}, {@link + * #IMPORTANCE_DEFAULT} and {@link #IMPORTANCE_HIGH}. */ public static void createNotificationChannel( - Context context, String id, int name, int importance) { + Context context, String id, @StringRes int name, @Importance int importance) { if (Util.SDK_INT >= 26) { NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); - NotificationChannel mChannel = + NotificationChannel channel = new NotificationChannel(id, context.getString(name), importance); - notificationManager.createNotificationChannel(mChannel); + notificationManager.createNotificationChannel(channel); } } @@ -73,4 +104,6 @@ public final class NotificationUtil { notificationManager.cancel(id); } } + + private NotificationUtil() {} } From 35e3892dbf96e733c5b4dcf503e1a921ca3a83c9 Mon Sep 17 00:00:00 2001 From: eguven Date: Thu, 19 Apr 2018 02:00:15 -0700 Subject: [PATCH 055/157] Invoke CacheDataSource EventListener.onCacheIgnored once per request ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=193480939 --- .../upstream/cache/CacheDataSource.java | 39 +++++++++++++------ 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CacheDataSource.java b/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CacheDataSource.java index 6de48cb9d2..fa0e909671 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CacheDataSource.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CacheDataSource.java @@ -85,10 +85,13 @@ public final class CacheDataSource implements DataSource { @IntDef({CACHE_IGNORED_REASON_ERROR, CACHE_IGNORED_REASON_UNSET_LENGTH}) public @interface CacheIgnoredReason {} - /** Cache ignored due to a cache related error */ + /** Cache not ignored. */ + private static final int CACHE_NOT_IGNORED = -1; + + /** Cache ignored due to a cache related error. */ public static final int CACHE_IGNORED_REASON_ERROR = 0; - /** Cache ignored due to a request with an unset length */ + /** Cache ignored due to a request with an unset length. */ public static final int CACHE_IGNORED_REASON_UNSET_LENGTH = 1; /** @@ -221,8 +224,13 @@ public final class CacheDataSource implements DataSource { actualUri = loadRedirectedUriOrReturnGivenUri(cache, key, uri); flags = dataSpec.flags; readPosition = dataSpec.position; - currentRequestIgnoresCache = (ignoreCacheOnError && seenCacheError) - || (dataSpec.length == C.LENGTH_UNSET && ignoreCacheForUnsetLengthRequests); + + int reason = shouldIgnoreCacheForRequest(dataSpec); + currentRequestIgnoresCache = reason != CACHE_NOT_IGNORED; + if (currentRequestIgnoresCache) { + notifyCacheIgnored(reason); + } + if (dataSpec.length != C.LENGTH_UNSET || currentRequestIgnoresCache) { bytesRemaining = dataSpec.length; } else { @@ -317,13 +325,6 @@ public final class CacheDataSource implements DataSource { CacheSpan nextSpan; if (currentRequestIgnoresCache) { nextSpan = null; - if (eventListener != null) { - int reason = - ignoreCacheOnError && seenCacheError - ? CACHE_IGNORED_REASON_ERROR - : CACHE_IGNORED_REASON_UNSET_LENGTH; - eventListener.onCacheIgnored(reason); - } } else if (blockOnCache) { try { nextSpan = cache.startReadWrite(key, readPosition); @@ -501,6 +502,22 @@ public final class CacheDataSource implements DataSource { } } + private int shouldIgnoreCacheForRequest(DataSpec dataSpec) { + if (ignoreCacheOnError && seenCacheError) { + return CACHE_IGNORED_REASON_ERROR; + } else if (ignoreCacheForUnsetLengthRequests && dataSpec.length == C.LENGTH_UNSET) { + return CACHE_IGNORED_REASON_UNSET_LENGTH; + } else { + return CACHE_NOT_IGNORED; + } + } + + private void notifyCacheIgnored(@CacheIgnoredReason int reason) { + if (eventListener != null) { + eventListener.onCacheIgnored(reason); + } + } + private void notifyBytesRead() { if (eventListener != null && totalCachedBytesRead > 0) { eventListener.onCachedBytesRead(cache.getCacheSpace(), totalCachedBytesRead); From 5fe0b0a13b9cfedfb3e9e5c2dcdd1d729f2b14bd Mon Sep 17 00:00:00 2001 From: andrewlewis Date: Thu, 19 Apr 2018 03:27:28 -0700 Subject: [PATCH 056/157] Fix @link typo ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=193491200 --- .../src/main/java/com/google/android/exoplayer2/Player.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/Player.java b/library/core/src/main/java/com/google/android/exoplayer2/Player.java index 743a0241bd..48f0e5dd29 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/Player.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/Player.java @@ -461,7 +461,7 @@ public interface Player { /** * Returns the error that caused playback to fail. This is the same error that will have been - * reported via @link Player.EventListener#onPlayerError(ExoPlaybackException)} at the time of + * reported via {@link Player.EventListener#onPlayerError(ExoPlaybackException)} at the time of * failure. It can be queried using this method until {@code stop(true)} is called or the player * is re-prepared. * From cdb13dd5481f24b999fccb9667f6c6e6337a6078 Mon Sep 17 00:00:00 2001 From: olly Date: Thu, 19 Apr 2018 04:05:34 -0700 Subject: [PATCH 057/157] Make SelectionOverride parcelable Issue: #3915 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=193494016 --- .../trackselection/DefaultTrackSelector.java | 55 ++++++++++++++++++- .../DefaultTrackSelectorTest.java | 19 +++++++ 2 files changed, 73 insertions(+), 1 deletion(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelector.java b/library/core/src/main/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelector.java index 80fbdf0611..5878a7612d 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelector.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelector.java @@ -37,6 +37,7 @@ import com.google.android.exoplayer2.upstream.BandwidthMeter; import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Util; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -630,7 +631,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { } /** A track selection override. */ - public static class SelectionOverride { + public static final class SelectionOverride implements Parcelable { public final int groupIndex; public final int[] tracks; @@ -646,6 +647,13 @@ public class DefaultTrackSelector extends MappingTrackSelector { this.length = tracks.length; } + /* package */ SelectionOverride(Parcel in) { + groupIndex = in.readInt(); + length = in.readByte(); + tracks = new int[length]; + in.readIntArray(tracks); + } + /** Returns whether this override contains the specified track index. */ public boolean containsTrack(int track) { for (int overrideTrack : tracks) { @@ -655,6 +663,51 @@ public class DefaultTrackSelector extends MappingTrackSelector { } return false; } + + @Override + public int hashCode() { + return 31 * groupIndex + Arrays.hashCode(tracks); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + SelectionOverride other = (SelectionOverride) obj; + return groupIndex == other.groupIndex && Arrays.equals(tracks, other.tracks); + } + + // Parcelable implementation. + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(groupIndex); + dest.writeInt(tracks.length); + dest.writeIntArray(tracks); + } + + public static final Parcelable.Creator CREATOR = + new Parcelable.Creator() { + + @Override + public SelectionOverride createFromParcel(Parcel in) { + return new SelectionOverride(in); + } + + @Override + public SelectionOverride[] newArray(int size) { + return new SelectionOverride[size]; + } + }; } /** diff --git a/library/core/src/test/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelectorTest.java b/library/core/src/test/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelectorTest.java index 4415d641e3..a97b9fc567 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelectorTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelectorTest.java @@ -34,6 +34,7 @@ import com.google.android.exoplayer2.source.TrackGroup; import com.google.android.exoplayer2.source.TrackGroupArray; import com.google.android.exoplayer2.trackselection.DefaultTrackSelector.Parameters; import com.google.android.exoplayer2.trackselection.DefaultTrackSelector.ParametersBuilder; +import com.google.android.exoplayer2.trackselection.DefaultTrackSelector.SelectionOverride; import com.google.android.exoplayer2.trackselection.TrackSelector.InvalidationListener; import com.google.android.exoplayer2.util.MimeTypes; import java.util.HashMap; @@ -147,6 +148,24 @@ public final class DefaultTrackSelectorTest { parcel.recycle(); } + /** Tests {@link SelectionOverride}'s {@link android.os.Parcelable} implementation. */ + @Test + public void testSelectionOverrideParcelable() { + int[] tracks = new int[] {2, 3}; + SelectionOverride selectionOverrideToParcel = + new SelectionOverride(/* groupIndex= */ 1, tracks); + + Parcel parcel = Parcel.obtain(); + selectionOverrideToParcel.writeToParcel(parcel, 0); + parcel.setDataPosition(0); + + SelectionOverride selectionOverrideFromParcel = + SelectionOverride.CREATOR.createFromParcel(parcel); + assertThat(selectionOverrideFromParcel).isEqualTo(selectionOverrideToParcel); + + parcel.recycle(); + } + /** Tests that a null override clears a track selection. */ @Test public void testSelectTracksWithNullOverride() throws ExoPlaybackException { From d2c6871ce613129cb60a26bf9a5cd180792a8041 Mon Sep 17 00:00:00 2001 From: aquilescanta Date: Thu, 19 Apr 2018 04:09:10 -0700 Subject: [PATCH 058/157] Fix HLS encryption method detection Issue:#4145 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=193494319 --- RELEASENOTES.md | 7 +- .../hls/playlist/HlsPlaylistParser.java | 3 +- .../playlist/HlsMediaPlaylistParserTest.java | 72 +++++++++++++++++++ 3 files changed, 79 insertions(+), 3 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 72ca26c6b7..ff723c943c 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -66,8 +66,11 @@ ([#4022][https://github.com/google/ExoPlayer/issues/4022]). * Fix handling of 307/308 redirects when making license requests ([#4108](https://github.com/google/ExoPlayer/issues/4108)). -* HLS: Fix playlist loading error propagation when the current selection does - not include all of the playlist's variants. +* HLS: + * Fix playlist loading error propagation when the current selection does + not include all of the playlist's variants. + * Fix SAMPLE-AES-CENC and SAMPLE-AES-CTR EXT-X-KEY methods + ([#4145](https://github.com/google/ExoPlayer/issues/4145)). * Fix ClearKey decryption error if the key contains a forward slash ([#4075](https://github.com/google/ExoPlayer/issues/4075)). * Fix crash when switching surface on Huawei P9 Lite diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistParser.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistParser.java index acd0746e72..68491f4434 100644 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistParser.java +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistParser.java @@ -123,7 +123,8 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser Date: Thu, 19 Apr 2018 08:25:49 -0700 Subject: [PATCH 059/157] Allow overriding skipping/scaling with custom AudioProcessors Issue: #3142 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=193519029 --- RELEASENOTES.md | 2 + .../exoplayer2/audio/DefaultAudioSink.java | 196 +++++++++++++----- .../audio/SilenceSkippingAudioProcessor.java | 2 +- 3 files changed, 151 insertions(+), 49 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index ff723c943c..0fd6f3c8ee 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -53,6 +53,8 @@ with encoder padding when the decoder returns a non-empty final buffer. * Allow trimming more than one sample when applying an elst audio edit via gapless playback info. + * Allow overriding skipping/scaling with custom `AudioProcessor`s + ((#3142)[https://github.com/google/ExoPlayer/issues/3142]). * Caching: * Add release method to Cache interface. * Prevent multiple instances of SimpleCache in the same folder. diff --git a/library/core/src/main/java/com/google/android/exoplayer2/audio/DefaultAudioSink.java b/library/core/src/main/java/com/google/android/exoplayer2/audio/DefaultAudioSink.java index a206d2c1ec..1025cb953b 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/audio/DefaultAudioSink.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/audio/DefaultAudioSink.java @@ -35,6 +35,8 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.ArrayDeque; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; /** * Plays audio data. The implementation delegates to an {@link AudioTrack} and handles playback @@ -64,6 +66,92 @@ public final class DefaultAudioSink implements AudioSink { } + /** + * Provides a chain of audio processors, which are used for any user-defined processing and + * applying playback parameters (if supported). Because applying playback parameters can skip and + * stretch/compress audio, the sink will query the chain for information on how to transform its + * output position to map it onto a media position, via {@link #getMediaDuration(long)} and {@link + * #getSkippedOutputFrameCount()}. + */ + public interface AudioProcessorChain { + + /** + * Returns the fixed chain of audio processors that will process audio. This method is called + * once during initialization, but audio processors may change state to become active/inactive + * during playback. + */ + AudioProcessor[] getAudioProcessors(); + + /** + * Configures audio processors to apply the specified playback parameters immediately, returning + * the new parameters, which may differ from those passed in. Only called when processors have + * no input pending. + * + * @param playbackParameters The playback parameters to try to apply. + * @return The playback parameters that were actually applied. + */ + PlaybackParameters applyPlaybackParameters(PlaybackParameters playbackParameters); + + /** + * Scales the specified playout duration to take into account speedup due to audio processing, + * returning an input media duration, in arbitrary units. + */ + long getMediaDuration(long playoutDuration); + + /** + * Returns the number of output audio frames skipped since the audio processors were last + * flushed. + */ + long getSkippedOutputFrameCount(); + } + + /** + * The default audio processor chain, which applies a (possibly empty) chain of user-defined audio + * processors followed by {@link SilenceSkippingAudioProcessor} and {@link SonicAudioProcessor}. + */ + public static class DefaultAudioProcessorChain implements AudioProcessorChain { + + private final AudioProcessor[] audioProcessors; + private final SilenceSkippingAudioProcessor silenceSkippingAudioProcessor; + private final SonicAudioProcessor sonicAudioProcessor; + + /** + * Creates a new default chain of audio processors, with the user-defined {@code + * audioProcessors} applied before silence skipping and playback parameters. + */ + public DefaultAudioProcessorChain(AudioProcessor... audioProcessors) { + this.audioProcessors = Arrays.copyOf(audioProcessors, audioProcessors.length + 2); + silenceSkippingAudioProcessor = new SilenceSkippingAudioProcessor(); + sonicAudioProcessor = new SonicAudioProcessor(); + this.audioProcessors[audioProcessors.length] = silenceSkippingAudioProcessor; + this.audioProcessors[audioProcessors.length + 1] = sonicAudioProcessor; + } + + @Override + public AudioProcessor[] getAudioProcessors() { + return audioProcessors; + } + + @Override + public PlaybackParameters applyPlaybackParameters(PlaybackParameters playbackParameters) { + silenceSkippingAudioProcessor.setEnabled(playbackParameters.skipSilence); + return new PlaybackParameters( + sonicAudioProcessor.setSpeed(playbackParameters.speed), + sonicAudioProcessor.setPitch(playbackParameters.pitch), + playbackParameters.skipSilence); + } + + @Override + public long getMediaDuration(long playoutDuration) { + return sonicAudioProcessor.scaleDurationForSpeedup(playoutDuration); + } + + @Override + public long getSkippedOutputFrameCount() { + return silenceSkippingAudioProcessor.getSkippedFrames(); + } + } + /** * A minimum length for the {@link AudioTrack} buffer, in microseconds. */ @@ -135,11 +223,10 @@ public final class DefaultAudioSink implements AudioSink { public static boolean failOnSpuriousAudioTimestamp = false; @Nullable private final AudioCapabilities audioCapabilities; + private final AudioProcessorChain audioProcessorChain; private final boolean enableConvertHighResIntPcmToFloat; private final ChannelMappingAudioProcessor channelMappingAudioProcessor; private final TrimmingAudioProcessor trimmingAudioProcessor; - private final SilenceSkippingAudioProcessor silenceSkippingAudioProcessor; - private final SonicAudioProcessor sonicAudioProcessor; private final AudioProcessor[] toIntPcmAvailableAudioProcessors; private final AudioProcessor[] toFloatPcmAvailableAudioProcessors; private final ConditionVariable releasingConditionVariable; @@ -181,7 +268,7 @@ public final class DefaultAudioSink implements AudioSink { private long startMediaTimeUs; private float volume; - private AudioProcessor[] audioProcessors; + private AudioProcessor[] activeAudioProcessors; private ByteBuffer[] outputBuffers; @Nullable private ByteBuffer inputBuffer; @Nullable private ByteBuffer outputBuffer; @@ -196,17 +283,21 @@ public final class DefaultAudioSink implements AudioSink { private long lastFeedElapsedRealtimeMs; /** + * Creates a new default audio sink. + * * @param audioCapabilities The audio capabilities for playback on this device. May be null if the * default capabilities (no encoded audio passthrough support) should be assumed. * @param audioProcessors An array of {@link AudioProcessor}s that will process PCM audio before * output. May be empty. */ - public DefaultAudioSink(@Nullable AudioCapabilities audioCapabilities, - AudioProcessor[] audioProcessors) { + public DefaultAudioSink( + @Nullable AudioCapabilities audioCapabilities, AudioProcessor[] audioProcessors) { this(audioCapabilities, audioProcessors, /* enableConvertHighResIntPcmToFloat= */ false); } /** + * Creates a new default audio sink, optionally using float output for high resolution PCM. + * * @param audioCapabilities The audio capabilities for playback on this device. May be null if the * default capabilities (no encoded audio passthrough support) should be assumed. * @param audioProcessors An array of {@link AudioProcessor}s that will process PCM audio before @@ -220,22 +311,45 @@ public final class DefaultAudioSink implements AudioSink { @Nullable AudioCapabilities audioCapabilities, AudioProcessor[] audioProcessors, boolean enableConvertHighResIntPcmToFloat) { + this( + audioCapabilities, + new DefaultAudioProcessorChain(audioProcessors), + enableConvertHighResIntPcmToFloat); + } + + /** + * Creates a new default audio sink, optionally using float output for high resolution PCM and + * with the specified {@code audioProcessorChain}. + * + * @param audioCapabilities The audio capabilities for playback on this device. May be null if the + * default capabilities (no encoded audio passthrough support) should be assumed. + * @param audioProcessorChain An {@link AudioProcessorChain} which is used to apply playback + * parameters adjustments. The instance passed in must not be reused in other sinks. + * @param enableConvertHighResIntPcmToFloat Whether to enable conversion of high resolution + * integer PCM to 32-bit float for output, if possible. Functionality that uses 16-bit integer + * audio processing (for example, speed and pitch adjustment) will not be available when float + * output is in use. + */ + public DefaultAudioSink( + @Nullable AudioCapabilities audioCapabilities, + AudioProcessorChain audioProcessorChain, + boolean enableConvertHighResIntPcmToFloat) { this.audioCapabilities = audioCapabilities; + this.audioProcessorChain = Assertions.checkNotNull(audioProcessorChain); this.enableConvertHighResIntPcmToFloat = enableConvertHighResIntPcmToFloat; releasingConditionVariable = new ConditionVariable(true); audioTrackPositionTracker = new AudioTrackPositionTracker(new PositionTrackerListener()); channelMappingAudioProcessor = new ChannelMappingAudioProcessor(); trimmingAudioProcessor = new TrimmingAudioProcessor(); - silenceSkippingAudioProcessor = new SilenceSkippingAudioProcessor(); - sonicAudioProcessor = new SonicAudioProcessor(); - toIntPcmAvailableAudioProcessors = new AudioProcessor[5 + audioProcessors.length]; - toIntPcmAvailableAudioProcessors[0] = new ResamplingAudioProcessor(); - toIntPcmAvailableAudioProcessors[1] = channelMappingAudioProcessor; - toIntPcmAvailableAudioProcessors[2] = trimmingAudioProcessor; - System.arraycopy( - audioProcessors, 0, toIntPcmAvailableAudioProcessors, 3, audioProcessors.length); - toIntPcmAvailableAudioProcessors[3 + audioProcessors.length] = silenceSkippingAudioProcessor; - toIntPcmAvailableAudioProcessors[4 + audioProcessors.length] = sonicAudioProcessor; + ArrayList toIntPcmAudioProcessors = new ArrayList<>(); + Collections.addAll( + toIntPcmAudioProcessors, + new ResamplingAudioProcessor(), + channelMappingAudioProcessor, + trimmingAudioProcessor); + Collections.addAll(toIntPcmAudioProcessors, audioProcessorChain.getAudioProcessors()); + toIntPcmAvailableAudioProcessors = + toIntPcmAudioProcessors.toArray(new AudioProcessor[toIntPcmAudioProcessors.size()]); toFloatPcmAvailableAudioProcessors = new AudioProcessor[] {new FloatResamplingAudioProcessor()}; volume = 1.0f; startMediaTimeState = START_NOT_SET; @@ -243,7 +357,7 @@ public final class DefaultAudioSink implements AudioSink { audioSessionId = C.AUDIO_SESSION_ID_UNSET; playbackParameters = PlaybackParameters.DEFAULT; drainingAudioProcessorIndex = C.INDEX_UNSET; - this.audioProcessors = new AudioProcessor[0]; + activeAudioProcessors = new AudioProcessor[0]; outputBuffers = new ByteBuffer[0]; playbackParametersCheckpoints = new ArrayDeque<>(); } @@ -423,14 +537,14 @@ public final class DefaultAudioSink implements AudioSink { } } int count = newAudioProcessors.size(); - audioProcessors = newAudioProcessors.toArray(new AudioProcessor[count]); + activeAudioProcessors = newAudioProcessors.toArray(new AudioProcessor[count]); outputBuffers = new ByteBuffer[count]; flushAudioProcessors(); } private void flushAudioProcessors() { - for (int i = 0; i < audioProcessors.length; i++) { - AudioProcessor audioProcessor = audioProcessors[i]; + for (int i = 0; i < activeAudioProcessors.length; i++) { + AudioProcessor audioProcessor = activeAudioProcessors[i]; audioProcessor.flush(); outputBuffers[i] = audioProcessor.getOutput(); } @@ -468,7 +582,7 @@ public final class DefaultAudioSink implements AudioSink { playbackParameters = canApplyPlaybackParameters - ? applyPlaybackParameters(playbackParameters) + ? audioProcessorChain.applyPlaybackParameters(playbackParameters) : PlaybackParameters.DEFAULT; setupAudioProcessors(); @@ -536,7 +650,7 @@ public final class DefaultAudioSink implements AudioSink { } PlaybackParameters newPlaybackParameters = afterDrainPlaybackParameters; afterDrainPlaybackParameters = null; - newPlaybackParameters = applyPlaybackParameters(newPlaybackParameters); + newPlaybackParameters = audioProcessorChain.applyPlaybackParameters(newPlaybackParameters); // Store the position and corresponding media time from which the parameters will apply. playbackParametersCheckpoints.add( new PlaybackParametersCheckpoint( @@ -601,7 +715,7 @@ public final class DefaultAudioSink implements AudioSink { } private void processBuffers(long avSyncPresentationTimeUs) throws WriteException { - int count = audioProcessors.length; + int count = activeAudioProcessors.length; int index = count; while (index >= 0) { ByteBuffer input = index > 0 ? outputBuffers[index - 1] @@ -609,7 +723,7 @@ public final class DefaultAudioSink implements AudioSink { if (index == count) { writeBuffer(input, avSyncPresentationTimeUs); } else { - AudioProcessor audioProcessor = audioProcessors[index]; + AudioProcessor audioProcessor = activeAudioProcessors[index]; audioProcessor.queueInput(input); ByteBuffer output = audioProcessor.getOutput(); outputBuffers[index] = output; @@ -706,11 +820,11 @@ public final class DefaultAudioSink implements AudioSink { private boolean drainAudioProcessorsToEndOfStream() throws WriteException { boolean audioProcessorNeedsEndOfStream = false; if (drainingAudioProcessorIndex == C.INDEX_UNSET) { - drainingAudioProcessorIndex = processingEnabled ? 0 : audioProcessors.length; + drainingAudioProcessorIndex = processingEnabled ? 0 : activeAudioProcessors.length; audioProcessorNeedsEndOfStream = true; } - while (drainingAudioProcessorIndex < audioProcessors.length) { - AudioProcessor audioProcessor = audioProcessors[drainingAudioProcessorIndex]; + while (drainingAudioProcessorIndex < activeAudioProcessors.length) { + AudioProcessor audioProcessor = activeAudioProcessors[drainingAudioProcessorIndex]; if (audioProcessorNeedsEndOfStream) { audioProcessor.queueEndOfStream(); } @@ -762,7 +876,7 @@ public final class DefaultAudioSink implements AudioSink { afterDrainPlaybackParameters = playbackParameters; } else { // Update the playback parameters now. - this.playbackParameters = applyPlaybackParameters(playbackParameters); + this.playbackParameters = audioProcessorChain.applyPlaybackParameters(playbackParameters); } } return this.playbackParameters; @@ -920,29 +1034,14 @@ public final class DefaultAudioSink implements AudioSink { }.start(); } - /** - * Configures audio processors to apply the specified playback parameters, returning the new - * parameters, which may differ from those passed in. - * - * @param playbackParameters The playback parameters to try to apply. - * @return The playback parameters that were actually applied. - */ - private PlaybackParameters applyPlaybackParameters(PlaybackParameters playbackParameters) { - silenceSkippingAudioProcessor.setEnabled(playbackParameters.skipSilence); - return new PlaybackParameters( - sonicAudioProcessor.setSpeed(playbackParameters.speed), - sonicAudioProcessor.setPitch(playbackParameters.pitch), - playbackParameters.skipSilence); - } - - /** - * Returns the underlying audio track {@code positionUs} with any applicable speedup applied. - */ private long applySpeedup(long positionUs) { + @Nullable PlaybackParametersCheckpoint checkpoint = null; while (!playbackParametersCheckpoints.isEmpty() && positionUs >= playbackParametersCheckpoints.getFirst().positionUs) { + checkpoint = playbackParametersCheckpoints.remove(); + } + if (checkpoint != null) { // We are playing (or about to play) media with the new playback parameters, so update them. - PlaybackParametersCheckpoint checkpoint = playbackParametersCheckpoints.remove(); playbackParameters = checkpoint.playbackParameters; playbackParametersPositionUs = checkpoint.positionUs; playbackParametersOffsetUs = checkpoint.mediaTimeUs - startMediaTimeUs; @@ -954,8 +1053,9 @@ public final class DefaultAudioSink implements AudioSink { if (playbackParametersCheckpoints.isEmpty()) { return playbackParametersOffsetUs - + sonicAudioProcessor.scaleDurationForSpeedup(positionUs - playbackParametersPositionUs); + + audioProcessorChain.getMediaDuration(positionUs - playbackParametersPositionUs); } + // We are playing data at a previous playback speed, so fall back to multiplying by the speed. return playbackParametersOffsetUs + Util.getMediaDurationForPlayoutDuration( @@ -963,7 +1063,7 @@ public final class DefaultAudioSink implements AudioSink { } private long applySkipping(long positionUs) { - return positionUs + framesToDurationUs(silenceSkippingAudioProcessor.getSkippedFrames()); + return positionUs + framesToDurationUs(audioProcessorChain.getSkippedOutputFrameCount()); } private boolean isInitialized() { diff --git a/library/core/src/main/java/com/google/android/exoplayer2/audio/SilenceSkippingAudioProcessor.java b/library/core/src/main/java/com/google/android/exoplayer2/audio/SilenceSkippingAudioProcessor.java index b748dbab5d..a289ced128 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/audio/SilenceSkippingAudioProcessor.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/audio/SilenceSkippingAudioProcessor.java @@ -27,7 +27,7 @@ import java.nio.ByteOrder; * An {@link AudioProcessor} that skips silence in the input stream. Input and output are 16-bit * PCM. */ -/* package */ final class SilenceSkippingAudioProcessor implements AudioProcessor { +public final class SilenceSkippingAudioProcessor implements AudioProcessor { /** * The minimum duration of audio that must be below {@link #SILENCE_THRESHOLD_LEVEL} to classify From 117dd6f33f806dfc0a2fb2753a66c402050d9254 Mon Sep 17 00:00:00 2001 From: andrewlewis Date: Thu, 19 Apr 2018 09:49:56 -0700 Subject: [PATCH 060/157] Mark optional parameters @Nullable ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=193530582 --- .../ext/mediasession/MediaSessionConnector.java | 10 ++++++---- .../exoplayer2/ui/PlayerNotificationManager.java | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/extensions/mediasession/src/main/java/com/google/android/exoplayer2/ext/mediasession/MediaSessionConnector.java b/extensions/mediasession/src/main/java/com/google/android/exoplayer2/ext/mediasession/MediaSessionConnector.java index 26e0d0e006..c90f7a84a5 100644 --- a/extensions/mediasession/src/main/java/com/google/android/exoplayer2/ext/mediasession/MediaSessionConnector.java +++ b/extensions/mediasession/src/main/java/com/google/android/exoplayer2/ext/mediasession/MediaSessionConnector.java @@ -403,16 +403,18 @@ public final class MediaSessionConnector { /** * Sets the player to be connected to the media session. - *

- * The order in which any {@link CustomActionProvider}s are passed determines the order of the + * + *

The order in which any {@link CustomActionProvider}s are passed determines the order of the * actions published with the playback state of the session. * * @param player The player to be connected to the {@code MediaSession}. * @param playbackPreparer An optional {@link PlaybackPreparer} for preparing the player. - * @param customActionProviders An optional {@link CustomActionProvider}s to publish and handle + * @param customActionProviders Optional {@link CustomActionProvider}s to publish and handle * custom actions. */ - public void setPlayer(Player player, PlaybackPreparer playbackPreparer, + public void setPlayer( + Player player, + @Nullable PlaybackPreparer playbackPreparer, CustomActionProvider... customActionProviders) { if (this.player != null) { this.player.removeListener(exoPlayerEventListener); diff --git a/library/ui/src/main/java/com/google/android/exoplayer2/ui/PlayerNotificationManager.java b/library/ui/src/main/java/com/google/android/exoplayer2/ui/PlayerNotificationManager.java index fd33fb8d21..53cf61b591 100644 --- a/library/ui/src/main/java/com/google/android/exoplayer2/ui/PlayerNotificationManager.java +++ b/library/ui/src/main/java/com/google/android/exoplayer2/ui/PlayerNotificationManager.java @@ -357,7 +357,7 @@ public class PlayerNotificationManager { *

If the player is released it must be removed from the manager by calling {@code * setPlayer(null)}. This will cancel the notification. */ - public final void setPlayer(Player player) { + public final void setPlayer(@Nullable Player player) { if (this.player == player) { return; } From f4ade38ae461a0e376dc7446693166dde9288da0 Mon Sep 17 00:00:00 2001 From: eguven Date: Thu, 19 Apr 2018 12:09:30 -0700 Subject: [PATCH 061/157] Add ability to download not DRM protected content to the demo app ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=193554024 --- RELEASENOTES.md | 1 + demos/main/src/main/AndroidManifest.xml | 19 + .../exoplayer2/demo/DemoDownloadService.java | 118 +++++ .../exoplayer2/demo/DownloaderActivity.java | 491 ++++++++++++++++++ .../exoplayer2/demo/PlayerActivity.java | 23 +- .../demo/SampleChooserActivity.java | 60 ++- .../main/res/layout/downloader_activity.xml | 84 +++ .../src/main/res/layout/sample_list_item.xml | 43 ++ demos/main/src/main/res/values/strings.xml | 20 + 9 files changed, 849 insertions(+), 10 deletions(-) create mode 100644 demos/main/src/main/java/com/google/android/exoplayer2/demo/DemoDownloadService.java create mode 100644 demos/main/src/main/java/com/google/android/exoplayer2/demo/DownloaderActivity.java create mode 100644 demos/main/src/main/res/layout/downloader_activity.xml create mode 100644 demos/main/src/main/res/layout/sample_list_item.xml diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 0fd6f3c8ee..1d54fdaea2 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -246,6 +246,7 @@ ([#3792](https://github.com/google/ExoPlayer/issues/3792). * Support 14-bit mode and little endianness in DTS PES packets ([#3340](https://github.com/google/ExoPlayer/issues/3340)). +* Demo app: Add ability to download not DRM protected content. ### 2.6.1 ### diff --git a/demos/main/src/main/AndroidManifest.xml b/demos/main/src/main/AndroidManifest.xml index cde95300ab..fb91de4328 100644 --- a/demos/main/src/main/AndroidManifest.xml +++ b/demos/main/src/main/AndroidManifest.xml @@ -19,6 +19,9 @@ + + + @@ -73,6 +76,22 @@ + + + + + + + + + + + diff --git a/demos/main/src/main/java/com/google/android/exoplayer2/demo/DemoDownloadService.java b/demos/main/src/main/java/com/google/android/exoplayer2/demo/DemoDownloadService.java new file mode 100644 index 0000000000..41fd165ebe --- /dev/null +++ b/demos/main/src/main/java/com/google/android/exoplayer2/demo/DemoDownloadService.java @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2017 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.demo; + +import android.app.Notification; +import android.app.NotificationManager; +import android.util.Pair; +import com.google.android.exoplayer2.offline.DownloadManager; +import com.google.android.exoplayer2.offline.DownloadManager.DownloadState; +import com.google.android.exoplayer2.offline.DownloadService; +import com.google.android.exoplayer2.offline.DownloaderConstructorHelper; +import com.google.android.exoplayer2.offline.ProgressiveDownloadAction; +import com.google.android.exoplayer2.scheduler.PlatformScheduler; +import com.google.android.exoplayer2.scheduler.Requirements; +import com.google.android.exoplayer2.source.dash.offline.DashDownloadAction; +import com.google.android.exoplayer2.source.hls.offline.HlsDownloadAction; +import com.google.android.exoplayer2.source.smoothstreaming.offline.SsDownloadAction; +import com.google.android.exoplayer2.ui.DownloadNotificationUtil; +import com.google.android.exoplayer2.ui.NotificationUtil; +import com.google.android.exoplayer2.util.ErrorMessageProvider; +import java.io.File; + +/** Demo DownloadService implementation. */ +public class DemoDownloadService extends DownloadService { + + private static final String CHANNEL_ID = "my_channel_01"; + + private static final int JOB_ID = 1; + + private static final int FOREGROUND_NOTIFICATION_ID = 1; + + private static DownloadManager downloadManager; + + public DemoDownloadService() { + super(FOREGROUND_NOTIFICATION_ID); + } + + @Override + public void onCreate() { + NotificationUtil.createNotificationChannel( + this, + CHANNEL_ID, + R.string.download_notifications_channel_name, + NotificationManager.IMPORTANCE_LOW); + super.onCreate(); + } + + @Override + protected DownloadManager getDownloadManager() { + if (downloadManager == null) { + DemoApplication application = (DemoApplication) getApplication(); + DownloaderConstructorHelper constructorHelper = + new DownloaderConstructorHelper( + application.getDownloadCache(), application.buildHttpDataSourceFactory(null)); + String actionFilePath = new File(getExternalCacheDir(), "actionFile").getAbsolutePath(); + downloadManager = + new DownloadManager( + constructorHelper, + /*maxSimultaneousDownloads=*/ 2, + DownloadManager.DEFAULT_MIN_RETRY_COUNT, + actionFilePath, + DashDownloadAction.DESERIALIZER, + HlsDownloadAction.DESERIALIZER, + SsDownloadAction.DESERIALIZER, + ProgressiveDownloadAction.DESERIALIZER); + } + return downloadManager; + } + + @Override + protected PlatformScheduler getScheduler() { + return new PlatformScheduler( + getApplicationContext(), getRequirements(), JOB_ID, ACTION_INIT, getPackageName()); + } + + @Override + protected Requirements getRequirements() { + return new Requirements(Requirements.NETWORK_TYPE_UNMETERED, false, false); + } + + @Override + protected Notification getForegroundNotification(DownloadState[] downloadStates) { + return DownloadNotificationUtil.createProgressNotification( + downloadStates, this, R.drawable.exo_controls_play, CHANNEL_ID, null); + } + + @Override + protected void onStateChange(DownloadState downloadState) { + int notificationId = FOREGROUND_NOTIFICATION_ID + 1 + downloadState.taskId; + Notification downloadNotification = + DownloadNotificationUtil.createDownloadFinishedNotification( + downloadState, + this, + R.drawable.exo_controls_play, + CHANNEL_ID, + downloadState.downloadAction.getData(), + new ErrorMessageProvider() { + @Override + public Pair getErrorMessage(Throwable throwable) { + return new Pair<>(0, throwable.getLocalizedMessage()); + } + }); + NotificationUtil.setNotification(this, notificationId, downloadNotification); + } +} diff --git a/demos/main/src/main/java/com/google/android/exoplayer2/demo/DownloaderActivity.java b/demos/main/src/main/java/com/google/android/exoplayer2/demo/DownloaderActivity.java new file mode 100644 index 0000000000..62bf3020bf --- /dev/null +++ b/demos/main/src/main/java/com/google/android/exoplayer2/demo/DownloaderActivity.java @@ -0,0 +1,491 @@ +/* + * Copyright (C) 2017 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.demo; + +import static com.google.android.exoplayer2.demo.PlayerActivity.DRM_SCHEME_EXTRA; +import static com.google.android.exoplayer2.demo.PlayerActivity.EXTENSION_EXTRA; + +import android.app.Activity; +import android.app.AlertDialog; +import android.content.DialogInterface; +import android.content.Intent; +import android.net.Uri; +import android.os.AsyncTask; +import android.os.Bundle; +import android.os.Parcelable; +import android.util.Log; +import android.util.SparseBooleanArray; +import android.view.View; +import android.widget.ArrayAdapter; +import android.widget.ListView; +import android.widget.TextView; +import android.widget.Toast; +import com.google.android.exoplayer2.C; +import com.google.android.exoplayer2.Format; +import com.google.android.exoplayer2.offline.DownloadAction; +import com.google.android.exoplayer2.offline.DownloadService; +import com.google.android.exoplayer2.offline.DownloaderConstructorHelper; +import com.google.android.exoplayer2.offline.ProgressiveDownloadAction; +import com.google.android.exoplayer2.offline.ProgressiveDownloader; +import com.google.android.exoplayer2.source.dash.manifest.Representation; +import com.google.android.exoplayer2.source.dash.manifest.RepresentationKey; +import com.google.android.exoplayer2.source.dash.offline.DashDownloadAction; +import com.google.android.exoplayer2.source.dash.offline.DashDownloader; +import com.google.android.exoplayer2.source.hls.offline.HlsDownloadAction; +import com.google.android.exoplayer2.source.hls.offline.HlsDownloader; +import com.google.android.exoplayer2.source.smoothstreaming.manifest.TrackKey; +import com.google.android.exoplayer2.source.smoothstreaming.offline.SsDownloadAction; +import com.google.android.exoplayer2.source.smoothstreaming.offline.SsDownloader; +import com.google.android.exoplayer2.util.ParcelableArray; +import com.google.android.exoplayer2.util.Util; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/** An activity that downloads streams. */ +public class DownloaderActivity extends Activity { + + public static final String PLAYER_INTENT = "player_intent"; + public static final String SAMPLE_NAME = "stream_name"; + + private static final String TAG = "DownloaderActivity"; + + private Intent playerIntent; + + @SuppressWarnings("rawtypes") + private AsyncTask manifestDownloaderTask; + + private DownloadUtilMethods downloadUtilMethods; + + private ListView representationList; + private ArrayAdapter arrayAdapter; + private AlertDialog cancelDialog; + private String sampleName; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.downloader_activity); + + Intent intent = getIntent(); + playerIntent = intent.getParcelableExtra(PLAYER_INTENT); + + TextView streamName = findViewById(R.id.sample_name); + sampleName = intent.getStringExtra(SAMPLE_NAME); + streamName.setText(sampleName); + + arrayAdapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_multiple_choice); + representationList = findViewById(R.id.representation_list); + representationList.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE); + representationList.setAdapter(arrayAdapter); + + if (playerIntent.hasExtra(DRM_SCHEME_EXTRA)) { + showToastAndFinish(R.string.not_supported_content_type); + } + + Uri sampleUri = playerIntent.getData(); + DemoApplication application = (DemoApplication) getApplication(); + DownloaderConstructorHelper constructorHelper = + new DownloaderConstructorHelper( + application.getDownloadCache(), application.buildHttpDataSourceFactory(null)); + String extension = playerIntent.getStringExtra(EXTENSION_EXTRA); + int type = Util.inferContentType(sampleUri, extension); + switch (type) { + case C.TYPE_DASH: + downloadUtilMethods = new DashDownloadUtilMethods(sampleUri, constructorHelper); + break; + case C.TYPE_HLS: + downloadUtilMethods = new HlsDownloadUtilMethods(sampleUri, constructorHelper); + break; + case C.TYPE_SS: + downloadUtilMethods = new SsDownloadUtilMethods(sampleUri, constructorHelper); + break; + case C.TYPE_OTHER: + downloadUtilMethods = new ProgressiveDownloadUtilMethods(sampleUri, constructorHelper); + break; + default: + showToastAndFinish(R.string.not_supported_content_type); + break; + } + + updateRepresentationsList(); + } + + @Override + protected void onPause() { + stopAll(); + super.onPause(); + } + + @Override + protected void onResume() { + super.onResume(); + updateRepresentationsList(); + } + + // This method is referenced in the layout file + public void onClick(View v) { + // switch-case doesn't work as in some compile configurations id definitions aren't constant + int id = v.getId(); + if (id == R.id.download_button) { + startDownload(); + } else if (id == R.id.remove_all_button) { + removeDownloaded(); + } else if (id == R.id.refresh) { + updateRepresentationsList(); + } else if (id == R.id.play_button) { + playDownloaded(); + } + } + + private void startDownload() { + ArrayList representationKeys = getSelectedRepresentationKeys(true); + if (representationKeys == null) { + return; + } + DownloadService.addDownloadAction( + this, + DemoDownloadService.class, + downloadUtilMethods.getDownloadAction(sampleName, representationKeys)); + } + + private void removeDownloaded() { + DownloadService.addDownloadAction( + this, DemoDownloadService.class, downloadUtilMethods.getRemoveAction()); + showToastAndFinish(R.string.removing_all); + } + + @SuppressWarnings("SuspiciousToArrayCall") + private void playDownloaded() { + ArrayList selectedRepresentationKeys = getSelectedRepresentationKeys(false); + if (selectedRepresentationKeys.isEmpty()) { + playerIntent.removeExtra(PlayerActivity.MANIFEST_FILTER_EXTRA); + } else if (selectedRepresentationKeys.get(0) instanceof Parcelable) { + Parcelable[] parcelables = new Parcelable[selectedRepresentationKeys.size()]; + selectedRepresentationKeys.toArray(parcelables); + playerIntent.putExtra( + PlayerActivity.MANIFEST_FILTER_EXTRA, new ParcelableArray<>(parcelables)); + } else { + String[] strings = new String[selectedRepresentationKeys.size()]; + selectedRepresentationKeys.toArray(strings); + playerIntent.putExtra(PlayerActivity.MANIFEST_FILTER_EXTRA, strings); + } + startActivity(playerIntent); + } + + private void stopAll() { + if (cancelDialog != null) { + cancelDialog.dismiss(); + } + + if (manifestDownloaderTask != null) { + manifestDownloaderTask.cancel(true); + manifestDownloaderTask = null; + } + } + + private void updateRepresentationsList() { + if (cancelDialog == null) { + cancelDialog = + new AlertDialog.Builder(this) + .setMessage("Please wait") + .setOnCancelListener( + new DialogInterface.OnCancelListener() { + @Override + public void onCancel(DialogInterface dialog) { + stopAll(); + } + }) + .create(); + } + cancelDialog.setTitle("Updating representations"); + cancelDialog.show(); + manifestDownloaderTask = new ManifestDownloaderTask().execute(); + } + + private ArrayList getSelectedRepresentationKeys(boolean unselect) { + SparseBooleanArray checked = representationList.getCheckedItemPositions(); + ArrayList representations = new ArrayList<>(checked.size()); + for (int i = 0; i < checked.size(); i++) { + if (checked.valueAt(i)) { + int position = checked.keyAt(i); + RepresentationItem item = + (RepresentationItem) representationList.getItemAtPosition(position); + representations.add(item.key); + if (unselect) { + representationList.setItemChecked(position, false); + } + } + } + return representations; + } + + private void showToastAndFinish(int resId) { + showToast(resId); + finish(); + } + + private void showToast(int resId) { + Toast.makeText(getApplicationContext(), resId, Toast.LENGTH_LONG).show(); + } + + private static final class RepresentationItem { + public final Object key; + public final String title; + + public RepresentationItem(Object key, String title) { + this.key = key; + this.title = title; + } + + @Override + public String toString() { + return title; + } + } + + private final class ManifestDownloaderTask + extends AsyncTask> { + + @Override + protected List doInBackground(Void... ignore) { + List items; + try { + items = downloadUtilMethods.getRepresentationItems(); + } catch (IOException | InterruptedException e) { + Log.e(TAG, "Getting representations failed", e); + return null; + } + return items; + } + + @Override + protected void onPostExecute(List items) { + if (items == null) { + showToastAndFinish(R.string.manifest_download_error); + return; + } + arrayAdapter.clear(); + for (RepresentationItem representationItem : items) { + arrayAdapter.add(representationItem); + } + stopAll(); + } + } + + private abstract static class DownloadUtilMethods { + + protected final Uri manifestUri; + protected final DownloaderConstructorHelper constructorHelper; + + public DownloadUtilMethods(Uri manifestUri, DownloaderConstructorHelper constructorHelper) { + this.manifestUri = manifestUri; + this.constructorHelper = constructorHelper; + } + + public abstract List getRepresentationItems() + throws IOException, InterruptedException; + + public abstract DownloadAction getDownloadAction( + String sampleName, ArrayList representationKeys); + + public abstract DownloadAction getRemoveAction(); + } + + private static final class DashDownloadUtilMethods extends DownloadUtilMethods { + + public DashDownloadUtilMethods(Uri manifestUri, DownloaderConstructorHelper constructorHelper) { + super(manifestUri, constructorHelper); + } + + @Override + public List getRepresentationItems() + throws IOException, InterruptedException { + DashDownloader downloader = new DashDownloader(manifestUri, constructorHelper); + ArrayList items = new ArrayList<>(); + for (RepresentationKey key : downloader.getAllRepresentationKeys()) { + downloader.selectRepresentations(new RepresentationKey[] {key}); + try { + downloader.init(); + } catch (IOException e) { + continue; + } + Representation representation = + downloader + .getManifest() + .getPeriod(key.periodIndex) + .adaptationSets + .get(key.adaptationSetIndex) + .representations + .get(key.representationIndex); + String trackName = DemoUtil.buildTrackName(representation.format); + int totalSegments = downloader.getTotalSegments(); + int downloadedRatio = 0; + if (totalSegments != 0) { + downloadedRatio = (downloader.getDownloadedSegments() * 100) / totalSegments; + } + String name = key.toString() + ' ' + trackName + ' ' + downloadedRatio + '%'; + items.add(new RepresentationItem(key, name)); + } + return items; + } + + @Override + public DownloadAction getDownloadAction( + String sampleName, ArrayList representationKeys) { + StringBuilder sb = new StringBuilder(sampleName); + RepresentationKey[] keys = + representationKeys.toArray(new RepresentationKey[representationKeys.size()]); + for (RepresentationKey representationKey : keys) { + sb.append('-').append(representationKey); + } + return new DashDownloadAction(manifestUri, false, sb.toString(), keys); + } + + @Override + public DownloadAction getRemoveAction() { + return new DashDownloadAction(manifestUri, true, null); + } + } + + private static final class HlsDownloadUtilMethods extends DownloadUtilMethods { + + public HlsDownloadUtilMethods(Uri manifestUri, DownloaderConstructorHelper constructorHelper) { + super(manifestUri, constructorHelper); + } + + @Override + public List getRepresentationItems() + throws IOException, InterruptedException { + HlsDownloader downloader = new HlsDownloader(manifestUri, constructorHelper); + ArrayList items = new ArrayList<>(); + for (String key : downloader.getAllRepresentationKeys()) { + downloader.selectRepresentations(new String[] {key}); + try { + downloader.init(); + } catch (IOException e) { + continue; + } + int totalSegments = downloader.getTotalSegments(); + int downloadedRatio = 0; + if (totalSegments != 0) { + downloadedRatio = (downloader.getDownloadedSegments() * 100) / totalSegments; + } + String name = key + ' ' /*+ trackName + ' '*/ + downloadedRatio + '%'; + items.add(new RepresentationItem(key, name)); + } + return items; + } + + @Override + public DownloadAction getDownloadAction( + String sampleName, ArrayList representationKeys) { + StringBuilder sb = new StringBuilder(sampleName); + String[] keys = representationKeys.toArray(new String[representationKeys.size()]); + for (String key : keys) { + sb.append('-').append(key); + } + return new HlsDownloadAction(manifestUri, false, sb.toString(), keys); + } + + @Override + public DownloadAction getRemoveAction() { + return new HlsDownloadAction(manifestUri, true, null); + } + } + + private static final class SsDownloadUtilMethods extends DownloadUtilMethods { + + public SsDownloadUtilMethods(Uri manifestUri, DownloaderConstructorHelper constructorHelper) { + super(manifestUri, constructorHelper); + } + + @Override + public List getRepresentationItems() + throws IOException, InterruptedException { + SsDownloader downloader = new SsDownloader(manifestUri, constructorHelper); + ArrayList items = new ArrayList<>(); + for (TrackKey key : downloader.getAllRepresentationKeys()) { + downloader.selectRepresentations(new TrackKey[] {key}); + try { + downloader.init(); + } catch (IOException e) { + continue; + } + Format format = + downloader.getManifest().streamElements[key.streamElementIndex].formats[key.trackIndex]; + String trackName = DemoUtil.buildTrackName(format); + int totalSegments = downloader.getTotalSegments(); + int downloadedRatio = 0; + if (totalSegments != 0) { + downloadedRatio = (downloader.getDownloadedSegments() * 100) / totalSegments; + } + String name = key.toString() + ' ' + trackName + ' ' + downloadedRatio + '%'; + items.add(new RepresentationItem(key, name)); + } + return items; + } + + @Override + public DownloadAction getDownloadAction( + String sampleName, ArrayList representationKeys) { + StringBuilder sb = new StringBuilder(sampleName); + TrackKey[] keys = representationKeys.toArray(new TrackKey[representationKeys.size()]); + for (TrackKey trackKey : keys) { + sb.append('-').append(trackKey); + } + return new SsDownloadAction(manifestUri, false, sb.toString(), keys); + } + + @Override + public DownloadAction getRemoveAction() { + return new SsDownloadAction(manifestUri, true, null); + } + } + + private static final class ProgressiveDownloadUtilMethods extends DownloadUtilMethods { + + public ProgressiveDownloadUtilMethods( + Uri manifestUri, DownloaderConstructorHelper constructorHelper) { + super(manifestUri, constructorHelper); + } + + @Override + public List getRepresentationItems() { + ProgressiveDownloader downloader = + new ProgressiveDownloader(manifestUri.toString(), null, constructorHelper); + ArrayList items = new ArrayList<>(); + { + downloader.init(); + int downloadedRatio = (int) downloader.getDownloadPercentage(); + String name = "track 1 - " + downloadedRatio + '%'; + items.add(new RepresentationItem(null, name)); + } + return items; + } + + @Override + public DownloadAction getDownloadAction( + String sampleName, ArrayList representationKeys) { + return new ProgressiveDownloadAction(manifestUri.toString(), null, false, sampleName); + } + + @Override + public DownloadAction getRemoveAction() { + return new ProgressiveDownloadAction(manifestUri.toString(), null, true, null); + } + } +} diff --git a/demos/main/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java b/demos/main/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java index 9375f9bba6..5f49998431 100644 --- a/demos/main/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java +++ b/demos/main/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java @@ -21,7 +21,6 @@ import android.content.pm.PackageManager; import android.net.Uri; import android.os.Bundle; import android.os.Handler; -import android.os.Parcelable; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.view.KeyEvent; @@ -84,6 +83,7 @@ import java.lang.reflect.Constructor; import java.net.CookieHandler; import java.net.CookieManager; import java.net.CookiePolicy; +import java.util.Arrays; import java.util.List; import java.util.UUID; @@ -276,11 +276,15 @@ public class PlayerActivity extends Activity String action = intent.getAction(); Uri[] uris; String[] extensions; - Parcelable[] manifestFilters; + Object[] manifestFilters; if (ACTION_VIEW.equals(action)) { uris = new Uri[] {intent.getData()}; extensions = new String[] {intent.getStringExtra(EXTENSION_EXTRA)}; - manifestFilters = new Parcelable[] {intent.getParcelableExtra(MANIFEST_FILTER_EXTRA)}; + Object filter = intent.getParcelableExtra(MANIFEST_FILTER_EXTRA); + if (filter == null) { + filter = intent.getStringArrayExtra(MANIFEST_FILTER_EXTRA); + } + manifestFilters = new Object[] {filter}; } else if (ACTION_VIEW_LIST.equals(action)) { String[] uriStrings = intent.getStringArrayExtra(URI_LIST_EXTRA); uris = new Uri[uriStrings.length]; @@ -293,7 +297,7 @@ public class PlayerActivity extends Activity } manifestFilters = intent.getParcelableArrayExtra(MANIFEST_FILTER_LIST_EXTRA); if (manifestFilters == null) { - manifestFilters = new Parcelable[uriStrings.length]; + manifestFilters = new Object[uriStrings.length]; } } else { showToast(getString(R.string.unexpected_intent_action, action)); @@ -385,8 +389,15 @@ public class PlayerActivity extends Activity MediaSource[] mediaSources = new MediaSource[uris.length]; for (int i = 0; i < uris.length; i++) { - ParcelableArray manifestFilter = (ParcelableArray) manifestFilters[i]; - List filter = manifestFilter != null ? manifestFilter.asList() : null; + List filter; + Object manifestFilter = manifestFilters[i]; + if (manifestFilter instanceof ParcelableArray) { + filter = ((ParcelableArray) manifestFilter).asList(); + } else if (manifestFilter instanceof String[]) { + filter = Arrays.asList((String[]) manifestFilter); + } else { + filter = null; + } mediaSources[i] = buildMediaSource(uris[i], extensions[i], filter); } mediaSource = diff --git a/demos/main/src/main/java/com/google/android/exoplayer2/demo/SampleChooserActivity.java b/demos/main/src/main/java/com/google/android/exoplayer2/demo/SampleChooserActivity.java index 5febb949f1..46faebc7fb 100644 --- a/demos/main/src/main/java/com/google/android/exoplayer2/demo/SampleChooserActivity.java +++ b/demos/main/src/main/java/com/google/android/exoplayer2/demo/SampleChooserActivity.java @@ -26,13 +26,16 @@ import android.util.JsonReader; import android.util.Log; import android.view.LayoutInflater; import android.view.View; +import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.BaseExpandableListAdapter; import android.widget.ExpandableListView; import android.widget.ExpandableListView.OnChildClickListener; +import android.widget.ImageButton; import android.widget.TextView; import android.widget.Toast; import com.google.android.exoplayer2.ParserException; +import com.google.android.exoplayer2.offline.DownloadService; import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DataSourceInputStream; import com.google.android.exoplayer2.upstream.DataSpec; @@ -81,6 +84,18 @@ public class SampleChooserActivity extends Activity { } SampleListLoader loaderTask = new SampleListLoader(); loaderTask.execute(uris); + + startDownloadServiceForeground(); + } + + private void startDownloadServiceForeground() { + Intent serviceIntent = + new Intent(DownloadService.ACTION_INIT).setPackage("com.google.android.exoplayer2.demo"); + if (Util.SDK_INT >= 26) { + startForegroundService(serviceIntent); + } else { + startService(serviceIntent); + } } private void onSampleGroups(final List groups, boolean sawError) { @@ -104,6 +119,21 @@ public class SampleChooserActivity extends Activity { startActivity(sample.buildIntent(this)); } + private void onSampleDownloadButtonClicked(Sample sample) { + if (!(sample instanceof UriSample) || sample.drmInfo != null) { + Toast.makeText( + getApplicationContext(), + R.string.supports_downloading_only_single_not_drm_protected, + Toast.LENGTH_SHORT) + .show(); + return; + } + Intent intent = new Intent(this, DownloaderActivity.class); + intent.putExtra(DownloaderActivity.SAMPLE_NAME, sample.name); + intent.putExtra(DownloaderActivity.PLAYER_INTENT, sample.buildIntent(this)); + startActivity(intent); + } + private final class SampleListLoader extends AsyncTask> { private boolean sawError; @@ -278,10 +308,11 @@ public class SampleChooserActivity extends Activity { } - private static final class SampleAdapter extends BaseExpandableListAdapter { + private final class SampleAdapter extends BaseExpandableListAdapter { private final Context context; private final List sampleGroups; + private OnClickListener onClickListener; public SampleAdapter(Context context, List sampleGroups) { this.context = context; @@ -303,10 +334,9 @@ public class SampleChooserActivity extends Activity { View convertView, ViewGroup parent) { View view = convertView; if (view == null) { - view = LayoutInflater.from(context).inflate(android.R.layout.simple_list_item_1, parent, - false); + view = LayoutInflater.from(context).inflate(R.layout.sample_list_item, parent, false); } - ((TextView) view).setText(getChild(groupPosition, childPosition).name); + initializeChildView(view, getChild(groupPosition, childPosition)); return view; } @@ -352,6 +382,28 @@ public class SampleChooserActivity extends Activity { return true; } + private void initializeChildView(View view, Sample sample) { + TextView sampleTitle = view.findViewById(R.id.sample_title); + sampleTitle.setText(sample.name); + + ImageButton downloadButton = view.findViewById(R.id.download_button); + downloadButton.setFocusable(false); + downloadButton.setOnClickListener(getOnClickListener()); + downloadButton.setTag(sample); + } + + private OnClickListener getOnClickListener() { + if (onClickListener == null) { + onClickListener = + new OnClickListener() { + @Override + public void onClick(View v) { + onSampleDownloadButtonClicked((Sample) v.getTag()); + } + }; + } + return onClickListener; + } } private static final class SampleGroup { diff --git a/demos/main/src/main/res/layout/downloader_activity.xml b/demos/main/src/main/res/layout/downloader_activity.xml new file mode 100644 index 0000000000..5b39e9c7fd --- /dev/null +++ b/demos/main/src/main/res/layout/downloader_activity.xml @@ -0,0 +1,84 @@ + + + + + + + + + + + + + +