mirror of
https://github.com/androidx/media.git
synced 2025-05-05 22:50:57 +08:00
commit
ce48a28aec
1
.gitignore
vendored
1
.gitignore
vendored
@ -47,6 +47,7 @@ bazel-testlogs
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
cmake-build-debug
|
cmake-build-debug
|
||||||
dist
|
dist
|
||||||
|
jacoco.exec
|
||||||
tmp
|
tmp
|
||||||
|
|
||||||
# External native builds
|
# External native builds
|
||||||
|
@ -1,5 +1,38 @@
|
|||||||
# Release notes
|
# Release notes
|
||||||
|
|
||||||
|
### 2.13.2 (2021-02-25)
|
||||||
|
|
||||||
|
* Extractors:
|
||||||
|
* Add support for MP4 and QuickTime meta atoms that are not full atoms.
|
||||||
|
* UI:
|
||||||
|
* Make conditions to enable UI actions consistent in
|
||||||
|
`DefaultControlDispatcher`, `PlayerControlView`,
|
||||||
|
`StyledPlayerControlView`, `PlayerNotificationManager` and
|
||||||
|
`TimelineQueueNavigator`.
|
||||||
|
* Fix conditions to enable seeking to next/previous media item to handle
|
||||||
|
the case where a live stream has ended.
|
||||||
|
* Audio:
|
||||||
|
* Fix `SimpleExoPlayer` reporting audio session ID as 0 in some cases
|
||||||
|
([#8585](https://github.com/google/ExoPlayer/issues/8585)).
|
||||||
|
* IMA extension:
|
||||||
|
* Fix a bug where playback could get stuck when seeking into a playlist
|
||||||
|
item with ads, if the preroll ad had preloaded but the window position
|
||||||
|
of the seek should instead trigger playback of a midroll.
|
||||||
|
* Fix a bug with playback of ads in playlists, where the incorrect period
|
||||||
|
index was used when deciding whether to trigger playback of an ad after
|
||||||
|
a seek.
|
||||||
|
* Text:
|
||||||
|
* Parse SSA/ASS font size in `Style:` lines
|
||||||
|
([#8435](https://github.com/google/ExoPlayer/issues/8435)).
|
||||||
|
* VP9 extension: Update to use NDK r21
|
||||||
|
([#8581](https://github.com/google/ExoPlayer/issues/8581)).
|
||||||
|
* FLAC extension: Update to use NDK r21
|
||||||
|
([#8581](https://github.com/google/ExoPlayer/issues/8581)).
|
||||||
|
* Opus extension: Update to use NDK r21
|
||||||
|
([#8581](https://github.com/google/ExoPlayer/issues/8581)).
|
||||||
|
* FFmpeg extension: Update to use NDK r21
|
||||||
|
([#8581](https://github.com/google/ExoPlayer/issues/8581)).
|
||||||
|
|
||||||
### 2.13.1 (2021-02-12)
|
### 2.13.1 (2021-02-12)
|
||||||
|
|
||||||
* Live streaming:
|
* Live streaming:
|
||||||
|
@ -13,8 +13,8 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
project.ext {
|
project.ext {
|
||||||
// ExoPlayer version and version code.
|
// ExoPlayer version and version code.
|
||||||
releaseVersion = '2.13.1'
|
releaseVersion = '2.13.2'
|
||||||
releaseVersionCode = 2013001
|
releaseVersionCode = 2013002
|
||||||
minSdkVersion = 16
|
minSdkVersion = 16
|
||||||
appTargetSdkVersion = 29
|
appTargetSdkVersion = 29
|
||||||
targetSdkVersion = 28 // TODO: Bump once b/143232359 is resolved. Also fix TODOs in UtilTest.
|
targetSdkVersion = 28 // TODO: Bump once b/143232359 is resolved. Also fix TODOs in UtilTest.
|
||||||
|
@ -30,7 +30,7 @@ FFMPEG_EXT_PATH="${EXOPLAYER_ROOT}/extensions/ffmpeg/src/main"
|
|||||||
```
|
```
|
||||||
|
|
||||||
* Download the [Android NDK][] and set its location in a shell variable.
|
* Download the [Android NDK][] and set its location in a shell variable.
|
||||||
This build configuration has been tested on NDK r20.
|
This build configuration has been tested on NDK r21.
|
||||||
|
|
||||||
```
|
```
|
||||||
NDK_PATH="<path to Android NDK>"
|
NDK_PATH="<path to Android NDK>"
|
||||||
|
@ -29,7 +29,7 @@ FLAC_EXT_PATH="${EXOPLAYER_ROOT}/extensions/flac/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.
|
||||||
This build configuration has been tested on NDK r20.
|
This build configuration has been tested on NDK r21.
|
||||||
|
|
||||||
```
|
```
|
||||||
NDK_PATH="<path to Android NDK>"
|
NDK_PATH="<path to Android NDK>"
|
||||||
|
@ -341,11 +341,25 @@ import java.util.Map;
|
|||||||
|
|
||||||
boolean playWhenReady = player.getPlayWhenReady();
|
boolean playWhenReady = player.getPlayWhenReady();
|
||||||
onTimelineChanged(player.getCurrentTimeline(), Player.TIMELINE_CHANGE_REASON_SOURCE_UPDATE);
|
onTimelineChanged(player.getCurrentTimeline(), Player.TIMELINE_CHANGE_REASON_SOURCE_UPDATE);
|
||||||
if (!AdPlaybackState.NONE.equals(adPlaybackState)
|
@Nullable AdsManager adsManager = this.adsManager;
|
||||||
&& adsManager != null
|
if (!AdPlaybackState.NONE.equals(adPlaybackState) && adsManager != null && imaPausedContent) {
|
||||||
&& imaPausedContent
|
// Check whether the current ad break matches the expected ad break based on the current
|
||||||
&& playWhenReady) {
|
// position. If not, discard the current ad break so that the correct ad break can load.
|
||||||
adsManager.resume();
|
long contentPositionMs = getContentPeriodPositionMs(player, timeline, period);
|
||||||
|
int adGroupForPositionIndex =
|
||||||
|
adPlaybackState.getAdGroupIndexForPositionUs(
|
||||||
|
C.msToUs(contentPositionMs), C.msToUs(contentDurationMs));
|
||||||
|
if (adGroupForPositionIndex != C.INDEX_UNSET
|
||||||
|
&& imaAdInfo != null
|
||||||
|
&& imaAdInfo.adGroupIndex != adGroupForPositionIndex) {
|
||||||
|
if (configuration.debugModeEnabled) {
|
||||||
|
Log.d(TAG, "Discarding preloaded ad " + imaAdInfo);
|
||||||
|
}
|
||||||
|
adsManager.discardAdBreak();
|
||||||
|
}
|
||||||
|
if (playWhenReady) {
|
||||||
|
adsManager.resume();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -826,7 +840,7 @@ import java.util.Map;
|
|||||||
ensureSentContentCompleteIfAtEndOfStream();
|
ensureSentContentCompleteIfAtEndOfStream();
|
||||||
if (!sentContentComplete && !timeline.isEmpty()) {
|
if (!sentContentComplete && !timeline.isEmpty()) {
|
||||||
long positionMs = getContentPeriodPositionMs(player, timeline, period);
|
long positionMs = getContentPeriodPositionMs(player, timeline, period);
|
||||||
timeline.getPeriod(/* periodIndex= */ 0, period);
|
timeline.getPeriod(player.getCurrentPeriodIndex(), period);
|
||||||
int newAdGroupIndex = period.getAdGroupIndexForPositionUs(C.msToUs(positionMs));
|
int newAdGroupIndex = period.getAdGroupIndexForPositionUs(C.msToUs(positionMs));
|
||||||
if (newAdGroupIndex != C.INDEX_UNSET) {
|
if (newAdGroupIndex != C.INDEX_UNSET) {
|
||||||
sentPendingContentPositionMs = false;
|
sentPendingContentPositionMs = false;
|
||||||
|
@ -98,8 +98,8 @@ public abstract class TimelineQueueNavigator implements MediaSessionConnector.Qu
|
|||||||
if (!timeline.isEmpty() && !player.isPlayingAd()) {
|
if (!timeline.isEmpty() && !player.isPlayingAd()) {
|
||||||
timeline.getWindow(player.getCurrentWindowIndex(), window);
|
timeline.getWindow(player.getCurrentWindowIndex(), window);
|
||||||
enableSkipTo = timeline.getWindowCount() > 1;
|
enableSkipTo = timeline.getWindowCount() > 1;
|
||||||
enablePrevious = window.isSeekable || !window.isDynamic || player.hasPrevious();
|
enablePrevious = window.isSeekable || !window.isLive() || player.hasPrevious();
|
||||||
enableNext = window.isDynamic || player.hasNext();
|
enableNext = (window.isLive() && window.isDynamic) || player.hasNext();
|
||||||
}
|
}
|
||||||
|
|
||||||
long actions = 0;
|
long actions = 0;
|
||||||
|
@ -29,7 +29,7 @@ OPUS_EXT_PATH="${EXOPLAYER_ROOT}/extensions/opus/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.
|
||||||
This build configuration has been tested on NDK r20.
|
This build configuration has been tested on NDK r21.
|
||||||
|
|
||||||
```
|
```
|
||||||
NDK_PATH="<path to Android NDK>"
|
NDK_PATH="<path to Android NDK>"
|
||||||
|
@ -29,7 +29,7 @@ VP9_EXT_PATH="${EXOPLAYER_ROOT}/extensions/vp9/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.
|
||||||
This build configuration has been tested on NDK r20.
|
This build configuration has been tested on NDK r21.
|
||||||
|
|
||||||
```
|
```
|
||||||
NDK_PATH="<path to Android NDK>"
|
NDK_PATH="<path to Android NDK>"
|
||||||
|
@ -30,11 +30,11 @@ public final class ExoPlayerLibraryInfo {
|
|||||||
|
|
||||||
/** The version of the library expressed as a string, for example "1.2.3". */
|
/** The version of the library expressed as a string, for example "1.2.3". */
|
||||||
// Intentionally hardcoded. Do not derive from other constants (e.g. VERSION_INT) or vice versa.
|
// Intentionally hardcoded. Do not derive from other constants (e.g. VERSION_INT) or vice versa.
|
||||||
public static final String VERSION = "2.13.1";
|
public static final String VERSION = "2.13.2";
|
||||||
|
|
||||||
/** The version of the library expressed as {@code "ExoPlayerLib/" + VERSION}. */
|
/** The version of the library expressed as {@code "ExoPlayerLib/" + VERSION}. */
|
||||||
// Intentionally hardcoded. Do not derive from other constants (e.g. VERSION) or vice versa.
|
// Intentionally hardcoded. Do not derive from other constants (e.g. VERSION) or vice versa.
|
||||||
public static final String VERSION_SLASHY = "ExoPlayerLib/2.13.1";
|
public static final String VERSION_SLASHY = "ExoPlayerLib/2.13.2";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The version of the library expressed as an integer, for example 1002003.
|
* The version of the library expressed as an integer, for example 1002003.
|
||||||
@ -44,7 +44,7 @@ public final class ExoPlayerLibraryInfo {
|
|||||||
* integer version 123045006 (123-045-006).
|
* integer version 123045006 (123-045-006).
|
||||||
*/
|
*/
|
||||||
// Intentionally hardcoded. Do not derive from other constants (e.g. VERSION) or vice versa.
|
// Intentionally hardcoded. Do not derive from other constants (e.g. VERSION) or vice versa.
|
||||||
public static final int VERSION_INT = 2013001;
|
public static final int VERSION_INT = 2013002;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The default user agent for requests made by the library.
|
* The default user agent for requests made by the library.
|
||||||
|
@ -79,11 +79,12 @@ public class DefaultControlDispatcher implements ControlDispatcher {
|
|||||||
int windowIndex = player.getCurrentWindowIndex();
|
int windowIndex = player.getCurrentWindowIndex();
|
||||||
timeline.getWindow(windowIndex, window);
|
timeline.getWindow(windowIndex, window);
|
||||||
int previousWindowIndex = player.getPreviousWindowIndex();
|
int previousWindowIndex = player.getPreviousWindowIndex();
|
||||||
|
boolean isUnseekableLiveStream = window.isLive() && !window.isSeekable;
|
||||||
if (previousWindowIndex != C.INDEX_UNSET
|
if (previousWindowIndex != C.INDEX_UNSET
|
||||||
&& (player.getCurrentPosition() <= MAX_POSITION_FOR_SEEK_TO_PREVIOUS
|
&& (player.getCurrentPosition() <= MAX_POSITION_FOR_SEEK_TO_PREVIOUS
|
||||||
|| (window.isDynamic && !window.isSeekable))) {
|
|| isUnseekableLiveStream)) {
|
||||||
player.seekTo(previousWindowIndex, C.TIME_UNSET);
|
player.seekTo(previousWindowIndex, C.TIME_UNSET);
|
||||||
} else {
|
} else if (!isUnseekableLiveStream) {
|
||||||
player.seekTo(windowIndex, /* positionMs= */ 0);
|
player.seekTo(windowIndex, /* positionMs= */ 0);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@ -96,10 +97,11 @@ public class DefaultControlDispatcher implements ControlDispatcher {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
int windowIndex = player.getCurrentWindowIndex();
|
int windowIndex = player.getCurrentWindowIndex();
|
||||||
|
timeline.getWindow(windowIndex, window);
|
||||||
int nextWindowIndex = player.getNextWindowIndex();
|
int nextWindowIndex = player.getNextWindowIndex();
|
||||||
if (nextWindowIndex != C.INDEX_UNSET) {
|
if (nextWindowIndex != C.INDEX_UNSET) {
|
||||||
player.seekTo(nextWindowIndex, C.TIME_UNSET);
|
player.seekTo(nextWindowIndex, C.TIME_UNSET);
|
||||||
} else if (timeline.getWindow(windowIndex, window).isLive()) {
|
} else if (window.isLive() && window.isDynamic) {
|
||||||
player.seekTo(windowIndex, C.TIME_UNSET);
|
player.seekTo(windowIndex, C.TIME_UNSET);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
@ -2140,7 +2140,6 @@ public class SimpleExoPlayer extends BasePlayer
|
|||||||
analyticsCollector.onAudioDisabled(counters);
|
analyticsCollector.onAudioDisabled(counters);
|
||||||
audioFormat = null;
|
audioFormat = null;
|
||||||
audioDecoderCounters = null;
|
audioDecoderCounters = null;
|
||||||
audioSessionId = C.AUDIO_SESSION_ID_UNSET;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -305,11 +305,15 @@ public final class SilenceMediaSource extends BaseMediaSource {
|
|||||||
return C.RESULT_BUFFER_READ;
|
return C.RESULT_BUFFER_READ;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
buffer.timeUs = getAudioPositionUs(positionBytes);
|
||||||
|
buffer.addFlag(C.BUFFER_FLAG_KEY_FRAME);
|
||||||
|
if (buffer.isFlagsOnly()) {
|
||||||
|
return C.RESULT_BUFFER_READ;
|
||||||
|
}
|
||||||
|
|
||||||
int bytesToWrite = (int) min(SILENCE_SAMPLE.length, bytesRemaining);
|
int bytesToWrite = (int) min(SILENCE_SAMPLE.length, bytesRemaining);
|
||||||
buffer.ensureSpaceForWrite(bytesToWrite);
|
buffer.ensureSpaceForWrite(bytesToWrite);
|
||||||
buffer.data.put(SILENCE_SAMPLE, /* offset= */ 0, bytesToWrite);
|
buffer.data.put(SILENCE_SAMPLE, /* offset= */ 0, bytesToWrite);
|
||||||
buffer.timeUs = getAudioPositionUs(positionBytes);
|
|
||||||
buffer.addFlag(C.BUFFER_FLAG_KEY_FRAME);
|
|
||||||
positionBytes += bytesToWrite;
|
positionBytes += bytesToWrite;
|
||||||
return C.RESULT_BUFFER_READ;
|
return C.RESULT_BUFFER_READ;
|
||||||
}
|
}
|
||||||
|
@ -51,9 +51,9 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
|
|||||||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@link MediaSource} that inserts ads linearly with a provided content media source. This source
|
* A {@link MediaSource} that inserts ads linearly into a provided content media source.
|
||||||
* cannot be used as a child source in a composition. It must be the top-level source used to
|
*
|
||||||
* prepare the player.
|
* <p>The wrapped content media source must contain a single {@link Timeline.Period}.
|
||||||
*/
|
*/
|
||||||
public final class AdsMediaSource extends CompositeMediaSource<MediaPeriodId> {
|
public final class AdsMediaSource extends CompositeMediaSource<MediaPeriodId> {
|
||||||
|
|
||||||
|
@ -314,6 +314,10 @@ public final class SsaDecoder extends SimpleSubtitleDecoder {
|
|||||||
/* end= */ spannableText.length(),
|
/* end= */ spannableText.length(),
|
||||||
SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE);
|
SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||||
}
|
}
|
||||||
|
if (style.fontSize != Cue.DIMEN_UNSET && screenHeight != Cue.DIMEN_UNSET) {
|
||||||
|
cue.setTextSize(
|
||||||
|
style.fontSize / screenHeight, Cue.TEXT_SIZE_TYPE_FRACTIONAL_IGNORE_PADDING);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SsaStyle.SsaAlignment int alignment;
|
@SsaStyle.SsaAlignment int alignment;
|
||||||
|
@ -27,6 +27,7 @@ import androidx.annotation.ColorInt;
|
|||||||
import androidx.annotation.IntDef;
|
import androidx.annotation.IntDef;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import com.google.android.exoplayer2.C;
|
import com.google.android.exoplayer2.C;
|
||||||
|
import com.google.android.exoplayer2.text.Cue;
|
||||||
import com.google.android.exoplayer2.util.Assertions;
|
import com.google.android.exoplayer2.util.Assertions;
|
||||||
import com.google.android.exoplayer2.util.Log;
|
import com.google.android.exoplayer2.util.Log;
|
||||||
import com.google.android.exoplayer2.util.Util;
|
import com.google.android.exoplayer2.util.Util;
|
||||||
@ -90,12 +91,17 @@ import java.util.regex.Pattern;
|
|||||||
public final String name;
|
public final String name;
|
||||||
@SsaAlignment public final int alignment;
|
@SsaAlignment public final int alignment;
|
||||||
@Nullable @ColorInt public final Integer primaryColor;
|
@Nullable @ColorInt public final Integer primaryColor;
|
||||||
|
public final float fontSize;
|
||||||
|
|
||||||
private SsaStyle(
|
private SsaStyle(
|
||||||
String name, @SsaAlignment int alignment, @Nullable @ColorInt Integer primaryColor) {
|
String name,
|
||||||
|
@SsaAlignment int alignment,
|
||||||
|
@Nullable @ColorInt Integer primaryColor,
|
||||||
|
float fontSize) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.alignment = alignment;
|
this.alignment = alignment;
|
||||||
this.primaryColor = primaryColor;
|
this.primaryColor = primaryColor;
|
||||||
|
this.fontSize = fontSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@ -114,7 +120,8 @@ import java.util.regex.Pattern;
|
|||||||
return new SsaStyle(
|
return new SsaStyle(
|
||||||
styleValues[format.nameIndex].trim(),
|
styleValues[format.nameIndex].trim(),
|
||||||
parseAlignment(styleValues[format.alignmentIndex].trim()),
|
parseAlignment(styleValues[format.alignmentIndex].trim()),
|
||||||
parseColor(styleValues[format.primaryColorIndex].trim()));
|
parseColor(styleValues[format.primaryColorIndex].trim()),
|
||||||
|
parseFontSize(styleValues[format.fontSizeIndex].trim()));
|
||||||
} catch (RuntimeException e) {
|
} catch (RuntimeException e) {
|
||||||
Log.w(TAG, "Skipping malformed 'Style:' line: '" + styleLine + "'", e);
|
Log.w(TAG, "Skipping malformed 'Style:' line: '" + styleLine + "'", e);
|
||||||
return null;
|
return null;
|
||||||
@ -191,6 +198,15 @@ import java.util.regex.Pattern;
|
|||||||
return Color.argb(a, r, g, b);
|
return Color.argb(a, r, g, b);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static float parseFontSize(String fontSize) {
|
||||||
|
try {
|
||||||
|
return Float.parseFloat(fontSize);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
Log.w(TAG, "Failed to parse font size: '" + fontSize + "'", e);
|
||||||
|
return Cue.DIMEN_UNSET;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a {@code Format:} line from the {@code [V4+ Styles]} section
|
* Represents a {@code Format:} line from the {@code [V4+ Styles]} section
|
||||||
*
|
*
|
||||||
@ -202,12 +218,15 @@ import java.util.regex.Pattern;
|
|||||||
public final int nameIndex;
|
public final int nameIndex;
|
||||||
public final int alignmentIndex;
|
public final int alignmentIndex;
|
||||||
public final int primaryColorIndex;
|
public final int primaryColorIndex;
|
||||||
|
public final int fontSizeIndex;
|
||||||
public final int length;
|
public final int length;
|
||||||
|
|
||||||
private Format(int nameIndex, int alignmentIndex, int primaryColorIndex, int length) {
|
private Format(
|
||||||
|
int nameIndex, int alignmentIndex, int primaryColorIndex, int fontSizeIndex, int length) {
|
||||||
this.nameIndex = nameIndex;
|
this.nameIndex = nameIndex;
|
||||||
this.alignmentIndex = alignmentIndex;
|
this.alignmentIndex = alignmentIndex;
|
||||||
this.primaryColorIndex = primaryColorIndex;
|
this.primaryColorIndex = primaryColorIndex;
|
||||||
|
this.fontSizeIndex = fontSizeIndex;
|
||||||
this.length = length;
|
this.length = length;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -221,6 +240,7 @@ import java.util.regex.Pattern;
|
|||||||
int nameIndex = C.INDEX_UNSET;
|
int nameIndex = C.INDEX_UNSET;
|
||||||
int alignmentIndex = C.INDEX_UNSET;
|
int alignmentIndex = C.INDEX_UNSET;
|
||||||
int primaryColorIndex = C.INDEX_UNSET;
|
int primaryColorIndex = C.INDEX_UNSET;
|
||||||
|
int fontSizeIndex = C.INDEX_UNSET;
|
||||||
String[] keys =
|
String[] keys =
|
||||||
TextUtils.split(styleFormatLine.substring(SsaDecoder.FORMAT_LINE_PREFIX.length()), ",");
|
TextUtils.split(styleFormatLine.substring(SsaDecoder.FORMAT_LINE_PREFIX.length()), ",");
|
||||||
for (int i = 0; i < keys.length; i++) {
|
for (int i = 0; i < keys.length; i++) {
|
||||||
@ -234,10 +254,13 @@ import java.util.regex.Pattern;
|
|||||||
case "primarycolour":
|
case "primarycolour":
|
||||||
primaryColorIndex = i;
|
primaryColorIndex = i;
|
||||||
break;
|
break;
|
||||||
|
case "fontsize":
|
||||||
|
fontSizeIndex = i;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nameIndex != C.INDEX_UNSET
|
return nameIndex != C.INDEX_UNSET
|
||||||
? new Format(nameIndex, alignmentIndex, primaryColorIndex, keys.length)
|
? new Format(nameIndex, alignmentIndex, primaryColorIndex, fontSizeIndex, keys.length)
|
||||||
: null;
|
: null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -47,7 +47,8 @@ public final class SsaDecoderTest {
|
|||||||
private static final String INVALID_TIMECODES = "media/ssa/invalid_timecodes";
|
private static final String INVALID_TIMECODES = "media/ssa/invalid_timecodes";
|
||||||
private static final String INVALID_POSITIONS = "media/ssa/invalid_positioning";
|
private static final String INVALID_POSITIONS = "media/ssa/invalid_positioning";
|
||||||
private static final String POSITIONS_WITHOUT_PLAYRES = "media/ssa/positioning_without_playres";
|
private static final String POSITIONS_WITHOUT_PLAYRES = "media/ssa/positioning_without_playres";
|
||||||
private static final String COLORS = "media/ssa/colors";
|
private static final String STYLE_COLORS = "media/ssa/style_colors";
|
||||||
|
private static final String STYLE_FONT_SIZE = "media/ssa/style_font_size";
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void decodeEmpty() throws IOException {
|
public void decodeEmpty() throws IOException {
|
||||||
@ -274,7 +275,7 @@ public final class SsaDecoderTest {
|
|||||||
@Test
|
@Test
|
||||||
public void decodeColors() throws IOException {
|
public void decodeColors() throws IOException {
|
||||||
SsaDecoder decoder = new SsaDecoder();
|
SsaDecoder decoder = new SsaDecoder();
|
||||||
byte[] bytes = TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), COLORS);
|
byte[] bytes = TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), STYLE_COLORS);
|
||||||
Subtitle subtitle = decoder.decode(bytes, bytes.length, false);
|
Subtitle subtitle = decoder.decode(bytes, bytes.length, false);
|
||||||
assertThat(subtitle.getEventTimeCount()).isEqualTo(14);
|
assertThat(subtitle.getEventTimeCount()).isEqualTo(14);
|
||||||
// &H000000FF (AABBGGRR) -> #FFFF0000 (AARRGGBB)
|
// &H000000FF (AABBGGRR) -> #FFFF0000 (AARRGGBB)
|
||||||
@ -319,6 +320,22 @@ public final class SsaDecoderTest {
|
|||||||
.hasNoForegroundColorSpanBetween(0, seventhCueText.length());
|
.hasNoForegroundColorSpanBetween(0, seventhCueText.length());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void decodeFontSize() throws IOException {
|
||||||
|
SsaDecoder decoder = new SsaDecoder();
|
||||||
|
byte[] bytes =
|
||||||
|
TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), STYLE_FONT_SIZE);
|
||||||
|
Subtitle subtitle = decoder.decode(bytes, bytes.length, false);
|
||||||
|
assertThat(subtitle.getEventTimeCount()).isEqualTo(4);
|
||||||
|
|
||||||
|
Cue firstCue = Iterables.getOnlyElement(subtitle.getCues(subtitle.getEventTime(0)));
|
||||||
|
assertThat(firstCue.textSize).isWithin(1.0e-8f).of(30f / 720f);
|
||||||
|
assertThat(firstCue.textSizeType).isEqualTo(Cue.TEXT_SIZE_TYPE_FRACTIONAL_IGNORE_PADDING);
|
||||||
|
Cue secondCue = Iterables.getOnlyElement(subtitle.getCues(subtitle.getEventTime(2)));
|
||||||
|
assertThat(secondCue.textSize).isWithin(1.0e-8f).of(72.2f / 720f);
|
||||||
|
assertThat(secondCue.textSizeType).isEqualTo(Cue.TEXT_SIZE_TYPE_FRACTIONAL_IGNORE_PADDING);
|
||||||
|
}
|
||||||
|
|
||||||
private static void assertTypicalCue1(Subtitle subtitle, int eventIndex) {
|
private static void assertTypicalCue1(Subtitle subtitle, int eventIndex) {
|
||||||
assertThat(subtitle.getEventTime(eventIndex)).isEqualTo(0);
|
assertThat(subtitle.getEventTime(eventIndex)).isEqualTo(0);
|
||||||
assertThat(subtitle.getCues(subtitle.getEventTime(eventIndex)).get(0).text.toString())
|
assertThat(subtitle.getCues(subtitle.getEventTime(eventIndex)).get(0).text.toString())
|
||||||
|
@ -17,7 +17,6 @@ package com.google.android.exoplayer2.upstream.cache;
|
|||||||
|
|
||||||
import static com.google.android.exoplayer2.testutil.CacheAsserts.assertCachedData;
|
import static com.google.android.exoplayer2.testutil.CacheAsserts.assertCachedData;
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
import static java.lang.Math.min;
|
|
||||||
import static org.junit.Assert.assertThrows;
|
import static org.junit.Assert.assertThrows;
|
||||||
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
@ -36,65 +35,18 @@ import org.junit.After;
|
|||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.mockito.Answers;
|
|
||||||
import org.mockito.Mock;
|
|
||||||
import org.mockito.MockitoAnnotations;
|
import org.mockito.MockitoAnnotations;
|
||||||
|
|
||||||
/** Unit tests for {@link CacheWriter}. */
|
/** Unit tests for {@link CacheWriter}. */
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
public final class CacheWriterTest {
|
public final class CacheWriterTest {
|
||||||
|
|
||||||
/**
|
|
||||||
* Abstract fake Cache implementation used by the test. This class must be public so Mockito can
|
|
||||||
* create a proxy for it.
|
|
||||||
*/
|
|
||||||
public abstract static class AbstractFakeCache implements Cache {
|
|
||||||
|
|
||||||
// This array is set to alternating length of cached and not cached regions in tests:
|
|
||||||
// spansAndGaps = {<length of 1st cached region>, <length of 1st not cached region>,
|
|
||||||
// <length of 2nd cached region>, <length of 2nd not cached region>, ... }
|
|
||||||
// Ideally it should end with a cached region but it shouldn't matter for any code.
|
|
||||||
private int[] spansAndGaps;
|
|
||||||
private long contentLength;
|
|
||||||
|
|
||||||
private void init() {
|
|
||||||
spansAndGaps = new int[] {};
|
|
||||||
contentLength = C.LENGTH_UNSET;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long getCachedLength(String key, long position, long length) {
|
|
||||||
if (length == C.LENGTH_UNSET) {
|
|
||||||
length = Long.MAX_VALUE;
|
|
||||||
}
|
|
||||||
for (int i = 0; i < spansAndGaps.length; i++) {
|
|
||||||
int spanOrGap = spansAndGaps[i];
|
|
||||||
if (position < spanOrGap) {
|
|
||||||
long left = min(spanOrGap - position, length);
|
|
||||||
return (i & 1) == 1 ? -left : left;
|
|
||||||
}
|
|
||||||
position -= spanOrGap;
|
|
||||||
}
|
|
||||||
return -length;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ContentMetadata getContentMetadata(String key) {
|
|
||||||
DefaultContentMetadata metadata = new DefaultContentMetadata();
|
|
||||||
ContentMetadataMutations mutations = new ContentMetadataMutations();
|
|
||||||
ContentMetadataMutations.setContentLength(mutations, contentLength);
|
|
||||||
return metadata.copyWithMutationsApplied(mutations);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Mock(answer = Answers.CALLS_REAL_METHODS) private AbstractFakeCache mockCache;
|
|
||||||
private File tempFolder;
|
private File tempFolder;
|
||||||
private SimpleCache cache;
|
private SimpleCache cache;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() throws Exception {
|
public void setUp() throws Exception {
|
||||||
MockitoAnnotations.initMocks(this);
|
MockitoAnnotations.initMocks(this);
|
||||||
mockCache.init();
|
|
||||||
tempFolder =
|
tempFolder =
|
||||||
Util.createTempDirectory(ApplicationProvider.getApplicationContext(), "ExoPlayerTest");
|
Util.createTempDirectory(ApplicationProvider.getApplicationContext(), "ExoPlayerTest");
|
||||||
cache =
|
cache =
|
||||||
|
@ -145,12 +145,11 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
|
|||||||
* Parses a udta atom.
|
* Parses a udta atom.
|
||||||
*
|
*
|
||||||
* @param udtaAtom The udta (user data) atom to decode.
|
* @param udtaAtom The udta (user data) atom to decode.
|
||||||
* @param isQuickTime True for QuickTime media. False otherwise.
|
|
||||||
* @return A {@link Pair} containing the metadata from the meta child atom as first value (if
|
* @return A {@link Pair} containing the metadata from the meta child atom as first value (if
|
||||||
* any), and the metadata from the smta child atom as second value (if any).
|
* any), and the metadata from the smta child atom as second value (if any).
|
||||||
*/
|
*/
|
||||||
public static Pair<@NullableType Metadata, @NullableType Metadata> parseUdta(
|
public static Pair<@NullableType Metadata, @NullableType Metadata> parseUdta(
|
||||||
Atom.LeafAtom udtaAtom, boolean isQuickTime) {
|
Atom.LeafAtom udtaAtom) {
|
||||||
ParsableByteArray udtaData = udtaAtom.data;
|
ParsableByteArray udtaData = udtaAtom.data;
|
||||||
udtaData.setPosition(Atom.HEADER_SIZE);
|
udtaData.setPosition(Atom.HEADER_SIZE);
|
||||||
@Nullable Metadata metaMetadata = null;
|
@Nullable Metadata metaMetadata = null;
|
||||||
@ -159,8 +158,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
|
|||||||
int atomPosition = udtaData.getPosition();
|
int atomPosition = udtaData.getPosition();
|
||||||
int atomSize = udtaData.readInt();
|
int atomSize = udtaData.readInt();
|
||||||
int atomType = udtaData.readInt();
|
int atomType = udtaData.readInt();
|
||||||
// Meta boxes are regular boxes rather than full boxes in QuickTime. Ignore them for now.
|
if (atomType == Atom.TYPE_meta) {
|
||||||
if (atomType == Atom.TYPE_meta && !isQuickTime) {
|
|
||||||
udtaData.setPosition(atomPosition);
|
udtaData.setPosition(atomPosition);
|
||||||
metaMetadata = parseUdtaMeta(udtaData, atomPosition + atomSize);
|
metaMetadata = parseUdtaMeta(udtaData, atomPosition + atomSize);
|
||||||
} else if (atomType == Atom.TYPE_smta) {
|
} else if (atomType == Atom.TYPE_smta) {
|
||||||
@ -227,6 +225,30 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
|
|||||||
return entries.isEmpty() ? null : new Metadata(entries);
|
return entries.isEmpty() ? null : new Metadata(entries);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Possibly skips the version and flags fields (1+3 byte) of a full meta atom.
|
||||||
|
*
|
||||||
|
* <p>Atoms of type {@link Atom#TYPE_meta} are defined to be full atoms which have four additional
|
||||||
|
* bytes for a version and a flags field (see 4.2 'Object Structure' in ISO/IEC 14496-12:2005).
|
||||||
|
* QuickTime do not have such a full box structure. Since some of these files are encoded wrongly,
|
||||||
|
* we can't rely on the file type though. Instead we must check the 8 bytes after the common
|
||||||
|
* header bytes ourselves.
|
||||||
|
*
|
||||||
|
* @param meta The 8 or more bytes following the meta atom size and type.
|
||||||
|
*/
|
||||||
|
public static void maybeSkipRemainingMetaAtomHeaderBytes(ParsableByteArray meta) {
|
||||||
|
int endPosition = meta.getPosition();
|
||||||
|
// The next 8 bytes can be either:
|
||||||
|
// (iso) [1 byte version + 3 bytes flags][4 byte size of next atom]
|
||||||
|
// (qt) [4 byte size of next atom ][4 byte hdlr atom type ]
|
||||||
|
// In case of (iso) we need to skip the next 4 bytes.
|
||||||
|
meta.skipBytes(4);
|
||||||
|
if (meta.readInt() != Atom.TYPE_hdlr) {
|
||||||
|
endPosition += 4;
|
||||||
|
}
|
||||||
|
meta.setPosition(endPosition);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses a trak atom (defined in ISO/IEC 14496-12).
|
* Parses a trak atom (defined in ISO/IEC 14496-12).
|
||||||
*
|
*
|
||||||
@ -677,7 +699,8 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
|
|||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private static Metadata parseUdtaMeta(ParsableByteArray meta, int limit) {
|
private static Metadata parseUdtaMeta(ParsableByteArray meta, int limit) {
|
||||||
meta.skipBytes(Atom.FULL_HEADER_SIZE);
|
meta.skipBytes(Atom.HEADER_SIZE);
|
||||||
|
maybeSkipRemainingMetaAtomHeaderBytes(meta);
|
||||||
while (meta.getPosition() < limit) {
|
while (meta.getPosition() < limit) {
|
||||||
int atomPosition = meta.getPosition();
|
int atomPosition = meta.getPosition();
|
||||||
int atomSize = meta.readInt();
|
int atomSize = meta.readInt();
|
||||||
|
@ -470,7 +470,7 @@ public final class Mp4Extractor implements Extractor, SeekMap {
|
|||||||
@Nullable Atom.LeafAtom udta = moov.getLeafAtomOfType(Atom.TYPE_udta);
|
@Nullable Atom.LeafAtom udta = moov.getLeafAtomOfType(Atom.TYPE_udta);
|
||||||
if (udta != null) {
|
if (udta != null) {
|
||||||
Pair<@NullableType Metadata, @NullableType Metadata> udtaMetadata =
|
Pair<@NullableType Metadata, @NullableType Metadata> udtaMetadata =
|
||||||
AtomParsers.parseUdta(udta, isQuickTime);
|
AtomParsers.parseUdta(udta);
|
||||||
udtaMetaMetadata = udtaMetadata.first;
|
udtaMetaMetadata = udtaMetadata.first;
|
||||||
smtaMetadata = udtaMetadata.second;
|
smtaMetadata = udtaMetadata.second;
|
||||||
if (udtaMetaMetadata != null) {
|
if (udtaMetaMetadata != null) {
|
||||||
@ -727,29 +727,12 @@ public final class Mp4Extractor implements Extractor, SeekMap {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Possibly skips the version and flags fields (1+3 byte) of a full meta atom of the {@code
|
|
||||||
* input}.
|
|
||||||
*
|
|
||||||
* <p>Atoms of type {@link Atom#TYPE_meta} are defined to be full atoms which have four additional
|
|
||||||
* bytes for a version and a flags field (see 4.2 'Object Structure' in ISO/IEC 14496-12:2005).
|
|
||||||
* QuickTime do not have such a full box structure. Since some of these files are encoded wrongly,
|
|
||||||
* we can't rely on the file type though. Instead we must check the 8 bytes after the common
|
|
||||||
* header bytes ourselves.
|
|
||||||
*/
|
|
||||||
private void maybeSkipRemainingMetaAtomHeaderBytes(ExtractorInput input) throws IOException {
|
private void maybeSkipRemainingMetaAtomHeaderBytes(ExtractorInput input) throws IOException {
|
||||||
scratch.reset(8);
|
scratch.reset(8);
|
||||||
// Peek the next 8 bytes which can be either
|
|
||||||
// (iso) [1 byte version + 3 bytes flags][4 byte size of next atom]
|
|
||||||
// (qt) [4 byte size of next atom ][4 byte hdlr atom type ]
|
|
||||||
// In case of (iso) we need to skip the next 4 bytes.
|
|
||||||
input.peekFully(scratch.getData(), 0, 8);
|
input.peekFully(scratch.getData(), 0, 8);
|
||||||
scratch.skipBytes(4);
|
AtomParsers.maybeSkipRemainingMetaAtomHeaderBytes(scratch);
|
||||||
if (scratch.readInt() == Atom.TYPE_hdlr) {
|
input.skipFully(scratch.getPosition());
|
||||||
input.resetPeekPosition();
|
input.resetPeekPosition();
|
||||||
} else {
|
|
||||||
input.skipFully(4);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Processes an atom whose payload does not need to be parsed. */
|
/** Processes an atom whose payload does not need to be parsed. */
|
||||||
|
@ -913,10 +913,10 @@ public class PlayerControlView extends FrameLayout {
|
|||||||
timeline.getWindow(player.getCurrentWindowIndex(), window);
|
timeline.getWindow(player.getCurrentWindowIndex(), window);
|
||||||
boolean isSeekable = window.isSeekable;
|
boolean isSeekable = window.isSeekable;
|
||||||
enableSeeking = isSeekable;
|
enableSeeking = isSeekable;
|
||||||
enablePrevious = isSeekable || !window.isDynamic || player.hasPrevious();
|
enablePrevious = isSeekable || !window.isLive() || player.hasPrevious();
|
||||||
enableRewind = isSeekable && controlDispatcher.isRewindEnabled();
|
enableRewind = isSeekable && controlDispatcher.isRewindEnabled();
|
||||||
enableFastForward = isSeekable && controlDispatcher.isFastForwardEnabled();
|
enableFastForward = isSeekable && controlDispatcher.isFastForwardEnabled();
|
||||||
enableNext = window.isDynamic || player.hasNext();
|
enableNext = (window.isLive() && window.isDynamic) || player.hasNext();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1227,10 +1227,11 @@ public class PlayerNotificationManager {
|
|||||||
Timeline timeline = player.getCurrentTimeline();
|
Timeline timeline = player.getCurrentTimeline();
|
||||||
if (!timeline.isEmpty() && !player.isPlayingAd()) {
|
if (!timeline.isEmpty() && !player.isPlayingAd()) {
|
||||||
timeline.getWindow(player.getCurrentWindowIndex(), window);
|
timeline.getWindow(player.getCurrentWindowIndex(), window);
|
||||||
enablePrevious = window.isSeekable || !window.isDynamic || player.hasPrevious();
|
boolean isSeekable = window.isSeekable;
|
||||||
enableRewind = controlDispatcher.isRewindEnabled();
|
enablePrevious = isSeekable || !window.isLive() || player.hasPrevious();
|
||||||
enableFastForward = controlDispatcher.isFastForwardEnabled();
|
enableRewind = isSeekable && controlDispatcher.isRewindEnabled();
|
||||||
enableNext = window.isDynamic || player.hasNext();
|
enableFastForward = isSeekable && controlDispatcher.isFastForwardEnabled();
|
||||||
|
enableNext = (window.isLive() && window.isDynamic) || player.hasNext();
|
||||||
}
|
}
|
||||||
|
|
||||||
List<String> stringActions = new ArrayList<>();
|
List<String> stringActions = new ArrayList<>();
|
||||||
|
@ -1141,10 +1141,10 @@ public class StyledPlayerControlView extends FrameLayout {
|
|||||||
timeline.getWindow(player.getCurrentWindowIndex(), window);
|
timeline.getWindow(player.getCurrentWindowIndex(), window);
|
||||||
boolean isSeekable = window.isSeekable;
|
boolean isSeekable = window.isSeekable;
|
||||||
enableSeeking = isSeekable;
|
enableSeeking = isSeekable;
|
||||||
enablePrevious = isSeekable || !window.isDynamic || player.hasPrevious();
|
enablePrevious = isSeekable || !window.isLive() || player.hasPrevious();
|
||||||
enableRewind = isSeekable && controlDispatcher.isRewindEnabled();
|
enableRewind = isSeekable && controlDispatcher.isRewindEnabled();
|
||||||
enableFastForward = isSeekable && controlDispatcher.isFastForwardEnabled();
|
enableFastForward = isSeekable && controlDispatcher.isFastForwardEnabled();
|
||||||
enableNext = window.isDynamic || player.hasNext();
|
enableNext = (window.isLive() && window.isDynamic) || player.hasNext();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
android:addStatesFromChildren="true"
|
android:addStatesFromChildren="true"
|
||||||
style="@style/ExoStyledControls.Button.Center">
|
style="@style/ExoStyledControls.Button.Center">
|
||||||
<!-- View's don't have foreground until API 23 so we have to nest in a parent. -->
|
<!-- View's don't have foreground until API 23 so we have to nest in a parent. -->
|
||||||
<Button android:id="@+id/exo_ffwd_with_amount"
|
<Button android:id="@id/exo_ffwd_with_amount"
|
||||||
android:background="@drawable/exo_styled_controls_fastforward"
|
android:background="@drawable/exo_styled_controls_fastforward"
|
||||||
android:layout_marginLeft="0dp"
|
android:layout_marginLeft="0dp"
|
||||||
android:layout_marginRight="0dp"
|
android:layout_marginRight="0dp"
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
android:addStatesFromChildren="true"
|
android:addStatesFromChildren="true"
|
||||||
style="@style/ExoStyledControls.Button.Center">
|
style="@style/ExoStyledControls.Button.Center">
|
||||||
<!-- View's don't have foreground until API 23 so we have to nest in a parent. -->
|
<!-- View's don't have foreground until API 23 so we have to nest in a parent. -->
|
||||||
<Button android:id="@+id/exo_rew_with_amount"
|
<Button android:id="@id/exo_rew_with_amount"
|
||||||
android:background="@drawable/exo_styled_controls_rewind"
|
android:background="@drawable/exo_styled_controls_rewind"
|
||||||
android:layout_marginLeft="0dp"
|
android:layout_marginLeft="0dp"
|
||||||
android:layout_marginRight="0dp"
|
android:layout_marginRight="0dp"
|
||||||
|
@ -15,13 +15,13 @@
|
|||||||
-->
|
-->
|
||||||
<merge xmlns:android="http://schemas.android.com/apk/res/android">
|
<merge xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
<View android:id="@+id/exo_controls_background"
|
<View android:id="@id/exo_controls_background"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
android:background="@color/exo_black_opacity_60"/>
|
android:background="@color/exo_black_opacity_60"/>
|
||||||
|
|
||||||
<FrameLayout android:id="@+id/exo_bottom_bar"
|
<FrameLayout android:id="@id/exo_bottom_bar"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="@dimen/exo_styled_bottom_bar_height"
|
android:layout_height="@dimen/exo_styled_bottom_bar_height"
|
||||||
android:layout_marginTop="@dimen/exo_styled_bottom_bar_margin_top"
|
android:layout_marginTop="@dimen/exo_styled_bottom_bar_margin_top"
|
||||||
@ -29,7 +29,7 @@
|
|||||||
android:background="@color/exo_bottom_bar_background"
|
android:background="@color/exo_bottom_bar_background"
|
||||||
android:layoutDirection="ltr">
|
android:layoutDirection="ltr">
|
||||||
|
|
||||||
<LinearLayout android:id="@+id/exo_time"
|
<LinearLayout android:id="@id/exo_time"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:paddingStart="@dimen/exo_styled_bottom_bar_time_padding"
|
android:paddingStart="@dimen/exo_styled_bottom_bar_time_padding"
|
||||||
@ -39,58 +39,58 @@
|
|||||||
android:layout_gravity="center_vertical|start"
|
android:layout_gravity="center_vertical|start"
|
||||||
android:layoutDirection="ltr">
|
android:layoutDirection="ltr">
|
||||||
|
|
||||||
<TextView android:id="@+id/exo_position"
|
<TextView android:id="@id/exo_position"
|
||||||
style="@style/ExoStyledControls.TimeText.Position"/>
|
style="@style/ExoStyledControls.TimeText.Position"/>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
style="@style/ExoStyledControls.TimeText.Separator"/>
|
style="@style/ExoStyledControls.TimeText.Separator"/>
|
||||||
|
|
||||||
<TextView android:id="@+id/exo_duration"
|
<TextView android:id="@id/exo_duration"
|
||||||
style="@style/ExoStyledControls.TimeText.Duration"/>
|
style="@style/ExoStyledControls.TimeText.Duration"/>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<LinearLayout android:id="@+id/exo_basic_controls"
|
<LinearLayout android:id="@id/exo_basic_controls"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center_vertical|end"
|
android:layout_gravity="center_vertical|end"
|
||||||
android:layoutDirection="ltr">
|
android:layoutDirection="ltr">
|
||||||
|
|
||||||
<ImageButton android:id="@+id/exo_vr"
|
<ImageButton android:id="@id/exo_vr"
|
||||||
style="@style/ExoStyledControls.Button.Bottom.VR"/>
|
style="@style/ExoStyledControls.Button.Bottom.VR"/>
|
||||||
|
|
||||||
<ImageButton android:id="@+id/exo_shuffle"
|
<ImageButton android:id="@id/exo_shuffle"
|
||||||
style="@style/ExoStyledControls.Button.Bottom.Shuffle"/>
|
style="@style/ExoStyledControls.Button.Bottom.Shuffle"/>
|
||||||
|
|
||||||
<ImageButton android:id="@+id/exo_repeat_toggle"
|
<ImageButton android:id="@id/exo_repeat_toggle"
|
||||||
style="@style/ExoStyledControls.Button.Bottom.RepeatToggle"/>
|
style="@style/ExoStyledControls.Button.Bottom.RepeatToggle"/>
|
||||||
|
|
||||||
<ImageButton android:id="@+id/exo_subtitle"
|
<ImageButton android:id="@id/exo_subtitle"
|
||||||
style="@style/ExoStyledControls.Button.Bottom.CC"/>
|
style="@style/ExoStyledControls.Button.Bottom.CC"/>
|
||||||
|
|
||||||
<ImageButton android:id="@+id/exo_settings"
|
<ImageButton android:id="@id/exo_settings"
|
||||||
style="@style/ExoStyledControls.Button.Bottom.Settings"/>
|
style="@style/ExoStyledControls.Button.Bottom.Settings"/>
|
||||||
|
|
||||||
<ImageButton android:id="@+id/exo_fullscreen"
|
<ImageButton android:id="@id/exo_fullscreen"
|
||||||
style="@style/ExoStyledControls.Button.Bottom.FullScreen"/>
|
style="@style/ExoStyledControls.Button.Bottom.FullScreen"/>
|
||||||
|
|
||||||
<ImageButton android:id="@+id/exo_overflow_show"
|
<ImageButton android:id="@id/exo_overflow_show"
|
||||||
style="@style/ExoStyledControls.Button.Bottom.OverflowShow"/>
|
style="@style/ExoStyledControls.Button.Bottom.OverflowShow"/>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<HorizontalScrollView android:id="@+id/exo_extra_controls_scroll_view"
|
<HorizontalScrollView android:id="@id/exo_extra_controls_scroll_view"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center_vertical|end"
|
android:layout_gravity="center_vertical|end"
|
||||||
android:visibility="invisible">
|
android:visibility="invisible">
|
||||||
|
|
||||||
<LinearLayout android:id="@+id/exo_extra_controls"
|
<LinearLayout android:id="@id/exo_extra_controls"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layoutDirection="ltr">
|
android:layoutDirection="ltr">
|
||||||
|
|
||||||
<ImageButton android:id="@+id/exo_overflow_hide"
|
<ImageButton android:id="@id/exo_overflow_hide"
|
||||||
style="@style/ExoStyledControls.Button.Bottom.OverflowHide"/>
|
style="@style/ExoStyledControls.Button.Bottom.OverflowHide"/>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
@ -99,13 +99,13 @@
|
|||||||
|
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
|
|
||||||
<View android:id="@+id/exo_progress_placeholder"
|
<View android:id="@id/exo_progress_placeholder"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="@dimen/exo_styled_progress_layout_height"
|
android:layout_height="@dimen/exo_styled_progress_layout_height"
|
||||||
android:layout_gravity="bottom"
|
android:layout_gravity="bottom"
|
||||||
android:layout_marginBottom="@dimen/exo_styled_progress_margin_bottom"/>
|
android:layout_marginBottom="@dimen/exo_styled_progress_margin_bottom"/>
|
||||||
|
|
||||||
<LinearLayout android:id="@+id/exo_minimal_controls"
|
<LinearLayout android:id="@id/exo_minimal_controls"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="bottom|end"
|
android:layout_gravity="bottom|end"
|
||||||
@ -114,13 +114,13 @@
|
|||||||
android:gravity="center_vertical"
|
android:gravity="center_vertical"
|
||||||
android:layoutDirection="ltr">
|
android:layoutDirection="ltr">
|
||||||
|
|
||||||
<ImageButton android:id="@+id/exo_minimal_fullscreen"
|
<ImageButton android:id="@id/exo_minimal_fullscreen"
|
||||||
style="@style/ExoStyledControls.Button.Bottom.FullScreen"/>
|
style="@style/ExoStyledControls.Button.Bottom.FullScreen"/>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/exo_center_controls"
|
android:id="@id/exo_center_controls"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
@ -128,18 +128,17 @@
|
|||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:padding="@dimen/exo_styled_controls_padding">
|
android:padding="@dimen/exo_styled_controls_padding">
|
||||||
|
|
||||||
|
<ImageButton android:id="@id/exo_prev"
|
||||||
<ImageButton android:id="@+id/exo_prev"
|
|
||||||
style="@style/ExoStyledControls.Button.Center.Previous"/>
|
style="@style/ExoStyledControls.Button.Center.Previous"/>
|
||||||
|
|
||||||
<include layout="@layout/exo_styled_player_control_rewind_button" />
|
<include layout="@layout/exo_styled_player_control_rewind_button" />
|
||||||
|
|
||||||
<ImageButton android:id="@+id/exo_play_pause"
|
<ImageButton android:id="@id/exo_play_pause"
|
||||||
style="@style/ExoStyledControls.Button.Center.PlayPause"/>
|
style="@style/ExoStyledControls.Button.Center.PlayPause"/>
|
||||||
|
|
||||||
<include layout="@layout/exo_styled_player_control_ffwd_button" />
|
<include layout="@layout/exo_styled_player_control_ffwd_button" />
|
||||||
|
|
||||||
<ImageButton android:id="@+id/exo_next"
|
<ImageButton android:id="@id/exo_next"
|
||||||
style="@style/ExoStyledControls.Button.Center.Next"/>
|
style="@style/ExoStyledControls.Button.Center.Next"/>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
@ -58,4 +58,5 @@
|
|||||||
android:textDirection="locale"
|
android:textDirection="locale"
|
||||||
android:textSize="@dimen/exo_settings_sub_text_size"/>
|
android:textSize="@dimen/exo_settings_sub_text_size"/>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
@ -43,4 +43,5 @@
|
|||||||
android:textColor="@color/exo_white"
|
android:textColor="@color/exo_white"
|
||||||
android:textDirection="locale"
|
android:textDirection="locale"
|
||||||
android:textSize="@dimen/exo_settings_main_text_size"/>
|
android:textSize="@dimen/exo_settings_main_text_size"/>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
@ -27,7 +27,9 @@
|
|||||||
<item name="exo_pause" type="id"/>
|
<item name="exo_pause" type="id"/>
|
||||||
<item name="exo_play_pause" type="id"/>
|
<item name="exo_play_pause" type="id"/>
|
||||||
<item name="exo_rew" type="id"/>
|
<item name="exo_rew" type="id"/>
|
||||||
|
<item name="exo_rew_with_amount" type="id"/>
|
||||||
<item name="exo_ffwd" type="id"/>
|
<item name="exo_ffwd" type="id"/>
|
||||||
|
<item name="exo_ffwd_with_amount" type="id"/>
|
||||||
<item name="exo_prev" type="id"/>
|
<item name="exo_prev" type="id"/>
|
||||||
<item name="exo_next" type="id"/>
|
<item name="exo_next" type="id"/>
|
||||||
<item name="exo_shuffle" type="id"/>
|
<item name="exo_shuffle" type="id"/>
|
||||||
@ -41,4 +43,17 @@
|
|||||||
<item name="exo_vr" type="id"/>
|
<item name="exo_vr" type="id"/>
|
||||||
<item name="exo_subtitle" type="id"/>
|
<item name="exo_subtitle" type="id"/>
|
||||||
<item name="exo_fullscreen" type="id"/>
|
<item name="exo_fullscreen" type="id"/>
|
||||||
|
<item name="exo_settings" type="id"/>
|
||||||
|
<item name="exo_controls_background" type="id"/>
|
||||||
|
<item name="exo_basic_controls" type="id"/>
|
||||||
|
<item name="exo_center_controls" type="id"/>
|
||||||
|
<item name="exo_bottom_bar" type="id"/>
|
||||||
|
<item name="exo_time" type="id"/>
|
||||||
|
<item name="exo_overflow_show" type="id"/>
|
||||||
|
<item name="exo_overflow_hide" type="id"/>
|
||||||
|
<item name="exo_extra_controls" type="id"/>
|
||||||
|
<item name="exo_extra_controls_scroll_view" type="id"/>
|
||||||
|
<item name="exo_minimal_fullscreen" type="id"/>
|
||||||
|
<item name="exo_minimal_controls" type="id"/>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -11,8 +11,10 @@
|
|||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
if (project.ext.has("exoplayerPublishEnabled")
|
if (project.ext.has("exoplayerPublishEnabled")
|
||||||
&& project.ext.exoplayerPublishEnabled) {
|
&& project.ext.exoplayerPublishEnabled) {
|
||||||
|
// For publishing to Bintray.
|
||||||
apply plugin: 'bintray-release'
|
apply plugin: 'bintray-release'
|
||||||
publish {
|
publish {
|
||||||
artifactId = releaseArtifact
|
artifactId = releaseArtifact
|
||||||
@ -38,6 +40,47 @@ if (project.ext.has("exoplayerPublishEnabled")
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// For publishing to a Maven repository.
|
||||||
|
apply plugin: 'maven-publish'
|
||||||
|
afterEvaluate {
|
||||||
|
publishing {
|
||||||
|
repositories {
|
||||||
|
maven {
|
||||||
|
url = findProperty('mavenRepo') ?: "${buildDir}/repo"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
publications {
|
||||||
|
release(MavenPublication) {
|
||||||
|
from components.release
|
||||||
|
artifact androidSourcesJar
|
||||||
|
groupId = 'com.google.android.exoplayer'
|
||||||
|
artifactId = releaseArtifact
|
||||||
|
version releaseVersion
|
||||||
|
pom {
|
||||||
|
name = releaseArtifact
|
||||||
|
description = releaseDescription
|
||||||
|
licenses {
|
||||||
|
license {
|
||||||
|
name = 'The Apache Software License, Version 2.0'
|
||||||
|
url = 'http://www.apache.org/licenses/LICENSE-2.0.txt'
|
||||||
|
distribution = 'repo'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
developers {
|
||||||
|
developer {
|
||||||
|
name = 'The Android Open Source Project'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
scm {
|
||||||
|
connection = 'scm:git:https://github.com/google/ExoPlayer.git'
|
||||||
|
url = 'https://github.com/google/ExoPlayer'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def getBintrayRepo() {
|
def getBintrayRepo() {
|
||||||
@ -66,3 +109,8 @@ static void addLicense(File pom) {
|
|||||||
printer.print(xml)
|
printer.print(xml)
|
||||||
writer.close()
|
writer.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
task androidSourcesJar(type: Jar) {
|
||||||
|
archiveClassifier.set('sources')
|
||||||
|
from android.sourceSets.main.java.srcDirs
|
||||||
|
}
|
||||||
|
18
testdata/src/test/assets/media/ssa/style_font_size
vendored
Normal file
18
testdata/src/test/assets/media/ssa/style_font_size
vendored
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
[Script Info]
|
||||||
|
Title: SSA/ASS Test
|
||||||
|
Original Script: Arnold Szabo
|
||||||
|
Script Type: V4.00+
|
||||||
|
PlayResX: 1280
|
||||||
|
PlayResY: 720
|
||||||
|
|
||||||
|
[V4+ Styles]
|
||||||
|
Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
|
||||||
|
Style: FontSizeSmall ,Roboto,30, &H000000FF,&H000000FF,&H00000000,&H00000000,0,0,0,0,100,100,0,0,1,3,0,2,50,50,70,1
|
||||||
|
Style: FontSizeBig ,Roboto,72.2,&H000000FF,&H000000FF,&H00000000,&H00000000,0,0,0,0,100,100,0,0,1,3,0,2,50,50,70,1
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[Events]
|
||||||
|
Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
|
||||||
|
Dialogue: 0,0:00:00.95,0:00:03.11,FontSizeSmall ,Arnold,0,0,0,,First line with font size 30.
|
||||||
|
Dialogue: 0,0:00:08.50,0:00:11.50,FontSizeBig ,Arnold,0,0,0,,Second line with font size 72.2.
|
@ -147,7 +147,6 @@ public class HttpDataSourceTestEnv extends ExternalResource {
|
|||||||
.setName(name)
|
.setName(name)
|
||||||
.setUri(Uri.parse(server.url(resource.getPath()).toString()))
|
.setUri(Uri.parse(server.url(resource.getPath()).toString()))
|
||||||
.setExpectedBytes(resource.getData())
|
.setExpectedBytes(resource.getData())
|
||||||
.setEndOfInputExpected(!resource.resolvesToUnknownLength())
|
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,7 @@ import com.google.common.collect.Maps;
|
|||||||
import java.lang.annotation.Documented;
|
import java.lang.annotation.Documented;
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
@ -277,21 +278,19 @@ public class WebServerDispatcher extends Dispatcher {
|
|||||||
if (!resource.supportsRangeRequests() || rangeHeader == null) {
|
if (!resource.supportsRangeRequests() || rangeHeader == null) {
|
||||||
switch (preferredContentCoding) {
|
switch (preferredContentCoding) {
|
||||||
case "gzip":
|
case "gzip":
|
||||||
|
setResponseBody(
|
||||||
|
response, Util.gzip(resourceData), /* chunked= */ resource.resolvesToUnknownLength);
|
||||||
response
|
response
|
||||||
.setBody(new Buffer().write(Util.gzip(resourceData)))
|
|
||||||
.setHeader("Content-Encoding", "gzip");
|
.setHeader("Content-Encoding", "gzip");
|
||||||
break;
|
break;
|
||||||
case "identity":
|
case "identity":
|
||||||
|
setResponseBody(response, resourceData, /* chunked= */ resource.resolvesToUnknownLength);
|
||||||
response
|
response
|
||||||
.setBody(new Buffer().write(resourceData))
|
|
||||||
.setHeader("Content-Encoding", "identity");
|
.setHeader("Content-Encoding", "identity");
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new IllegalStateException("Unexpected content coding: " + preferredContentCoding);
|
throw new IllegalStateException("Unexpected content coding: " + preferredContentCoding);
|
||||||
}
|
}
|
||||||
if (resource.resolvesToUnknownLength()) {
|
|
||||||
response.setHeader("Content-Length", "");
|
|
||||||
}
|
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -328,11 +327,11 @@ public class WebServerDispatcher extends Dispatcher {
|
|||||||
+ "-"
|
+ "-"
|
||||||
+ (resourceData.length - 1)
|
+ (resourceData.length - 1)
|
||||||
+ "/"
|
+ "/"
|
||||||
+ (resource.resolvesToUnknownLength() ? "*" : resourceData.length))
|
+ (resource.resolvesToUnknownLength() ? "*" : resourceData.length));
|
||||||
.setBody(new Buffer().write(resourceData, start, resourceData.length - start));
|
setResponseBody(
|
||||||
if (resource.resolvesToUnknownLength()) {
|
response,
|
||||||
response.setHeader("Content-Length", "");
|
Arrays.copyOfRange(resourceData, start, resourceData.length),
|
||||||
}
|
/* chunked= */ resource.resolvesToUnknownLength);
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -345,7 +344,7 @@ public class WebServerDispatcher extends Dispatcher {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int end = min(range.second + 1, resourceData.length);
|
int end = min(range.second + 1, resourceData.length);
|
||||||
return response
|
response
|
||||||
.setResponseCode(206)
|
.setResponseCode(206)
|
||||||
.setHeader(
|
.setHeader(
|
||||||
"Content-Range",
|
"Content-Range",
|
||||||
@ -354,8 +353,26 @@ public class WebServerDispatcher extends Dispatcher {
|
|||||||
+ "-"
|
+ "-"
|
||||||
+ (end - 1)
|
+ (end - 1)
|
||||||
+ "/"
|
+ "/"
|
||||||
+ (resource.resolvesToUnknownLength() ? "*" : resourceData.length))
|
+ (resource.resolvesToUnknownLength() ? "*" : resourceData.length));
|
||||||
.setBody(new Buffer().write(resourceData, range.first, end - range.first));
|
setResponseBody(
|
||||||
|
response, Arrays.copyOfRange(resourceData, range.first, end), /* chunked= */ false);
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Populates a response with the specified body.
|
||||||
|
*
|
||||||
|
* @param response The response whose body should be populated.
|
||||||
|
* @param body The body data.
|
||||||
|
* @param chunked Whether to use chunked transfer encoding. Note that if set to {@code true}, the
|
||||||
|
* "Content-Length" header will not be set.
|
||||||
|
*/
|
||||||
|
private static void setResponseBody(MockResponse response, byte[] body, boolean chunked) {
|
||||||
|
if (chunked) {
|
||||||
|
response.setChunkedBody(new Buffer().write(body), /* maxChunkSize= */ Integer.MAX_VALUE);
|
||||||
|
} else {
|
||||||
|
response.setBody(new Buffer().write(body));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -257,7 +257,7 @@ public class WebServerDispatcherTest {
|
|||||||
try (Response response = client.newCall(request).execute()) {
|
try (Response response = client.newCall(request).execute()) {
|
||||||
assertThat(response.code()).isEqualTo(200);
|
assertThat(response.code()).isEqualTo(200);
|
||||||
assertThat(response.header("Accept-Ranges")).isEqualTo("bytes");
|
assertThat(response.header("Accept-Ranges")).isEqualTo("bytes");
|
||||||
assertThat(response.header("Content-Length")).isEmpty();
|
assertThat(response.header("Content-Length")).isNull();
|
||||||
assertThat(response.header("Content-Range")).isNull();
|
assertThat(response.header("Content-Range")).isNull();
|
||||||
assertThat(response.body().contentLength()).isEqualTo(-1);
|
assertThat(response.body().contentLength()).isEqualTo(-1);
|
||||||
|
|
||||||
@ -298,7 +298,7 @@ public class WebServerDispatcherTest {
|
|||||||
try (Response response = client.newCall(request).execute()) {
|
try (Response response = client.newCall(request).execute()) {
|
||||||
assertThat(response.code()).isEqualTo(206);
|
assertThat(response.code()).isEqualTo(206);
|
||||||
assertThat(response.header("Accept-Ranges")).isEqualTo("bytes");
|
assertThat(response.header("Accept-Ranges")).isEqualTo("bytes");
|
||||||
assertThat(response.header("Content-Length")).isEmpty();
|
assertThat(response.header("Content-Length")).isNull();
|
||||||
assertThat(response.header("Content-Range")).isEqualTo("bytes 5-19/*");
|
assertThat(response.header("Content-Range")).isEqualTo("bytes 5-19/*");
|
||||||
assertThat(response.body().contentLength()).isEqualTo(-1);
|
assertThat(response.body().contentLength()).isEqualTo(-1);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user