commit
d73d64bc6f
@ -1,6 +1,14 @@
|
||||
# Release notes #
|
||||
|
||||
### 2.10.6 (2019-10-18) ###
|
||||
### 2.10.7 (2019-11-12) ###
|
||||
|
||||
* HLS: Fix detection of Dolby Atmos to match the HLS authoring specification.
|
||||
* MediaSession extension: Update shuffle and repeat modes when playback state
|
||||
is invalidated ([#6582](https://github.com/google/ExoPlayer/issues/6582)).
|
||||
* Fix the start of audio getting truncated when transitioning to a new
|
||||
item in a playlist of opus streams.
|
||||
|
||||
### 2.10.6 (2019-10-17) ###
|
||||
|
||||
* Add `Player.onPlaybackSuppressionReasonChanged` to allow listeners to
|
||||
detect playbacks suppressions (e.g. transient audio focus loss) directly
|
||||
|
@ -13,8 +13,8 @@
|
||||
// limitations under the License.
|
||||
project.ext {
|
||||
// ExoPlayer version and version code.
|
||||
releaseVersion = '2.10.6'
|
||||
releaseVersionCode = 2010006
|
||||
releaseVersion = '2.10.7'
|
||||
releaseVersionCode = 2010007
|
||||
minSdkVersion = 16
|
||||
targetSdkVersion = 28
|
||||
compileSdkVersion = 28
|
||||
|
@ -308,7 +308,11 @@ public final class LeanbackPlayerAdapter extends PlayerAdapter implements Runnab
|
||||
@Override
|
||||
public void onVideoSizeChanged(
|
||||
int width, int height, int unappliedRotationDegrees, float pixelWidthHeightRatio) {
|
||||
getCallback().onVideoSizeChanged(LeanbackPlayerAdapter.this, width, height);
|
||||
// There's no way to pass pixelWidthHeightRatio to leanback, so we scale the width that we
|
||||
// pass to take it into account. This is necessary to ensure that leanback uses the correct
|
||||
// aspect ratio when playing content with non-square pixels.
|
||||
int scaledWidth = Math.round(width * pixelWidthHeightRatio);
|
||||
getCallback().onVideoSizeChanged(LeanbackPlayerAdapter.this, scaledWidth, height);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -700,6 +700,9 @@ public final class MediaSessionConnector {
|
||||
/* position= */ 0,
|
||||
/* playbackSpeed= */ 0,
|
||||
/* updateTime= */ SystemClock.elapsedRealtime());
|
||||
|
||||
mediaSession.setRepeatMode(PlaybackStateCompat.REPEAT_MODE_NONE);
|
||||
mediaSession.setShuffleMode(PlaybackStateCompat.SHUFFLE_MODE_NONE);
|
||||
mediaSession.setPlaybackState(builder.build());
|
||||
return;
|
||||
}
|
||||
@ -748,6 +751,18 @@ public final class MediaSessionConnector {
|
||||
sessionPlaybackSpeed,
|
||||
/* updateTime= */ SystemClock.elapsedRealtime())
|
||||
.setExtras(extras);
|
||||
|
||||
@Player.RepeatMode int repeatMode = player.getRepeatMode();
|
||||
mediaSession.setRepeatMode(
|
||||
repeatMode == Player.REPEAT_MODE_ONE
|
||||
? PlaybackStateCompat.REPEAT_MODE_ONE
|
||||
: repeatMode == Player.REPEAT_MODE_ALL
|
||||
? PlaybackStateCompat.REPEAT_MODE_ALL
|
||||
: PlaybackStateCompat.REPEAT_MODE_NONE);
|
||||
mediaSession.setShuffleMode(
|
||||
player.getShuffleModeEnabled()
|
||||
? PlaybackStateCompat.SHUFFLE_MODE_ALL
|
||||
: PlaybackStateCompat.SHUFFLE_MODE_NONE);
|
||||
mediaSession.setPlaybackState(builder.build());
|
||||
}
|
||||
|
||||
@ -1047,21 +1062,11 @@ public final class MediaSessionConnector {
|
||||
|
||||
@Override
|
||||
public void onRepeatModeChanged(@Player.RepeatMode int repeatMode) {
|
||||
mediaSession.setRepeatMode(
|
||||
repeatMode == Player.REPEAT_MODE_ONE
|
||||
? PlaybackStateCompat.REPEAT_MODE_ONE
|
||||
: repeatMode == Player.REPEAT_MODE_ALL
|
||||
? PlaybackStateCompat.REPEAT_MODE_ALL
|
||||
: PlaybackStateCompat.REPEAT_MODE_NONE);
|
||||
invalidateMediaSessionPlaybackState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) {
|
||||
mediaSession.setShuffleMode(
|
||||
shuffleModeEnabled
|
||||
? PlaybackStateCompat.SHUFFLE_MODE_ALL
|
||||
: PlaybackStateCompat.SHUFFLE_MODE_NONE);
|
||||
invalidateMediaSessionPlaybackState();
|
||||
invalidateMediaSessionQueue();
|
||||
}
|
||||
|
@ -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.10.6";
|
||||
public static final String VERSION = "2.10.7";
|
||||
|
||||
/** 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.10.6";
|
||||
public static final String VERSION_SLASHY = "ExoPlayerLib/2.10.7";
|
||||
|
||||
/**
|
||||
* 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 = 2010006;
|
||||
public static final int VERSION_INT = 2010007;
|
||||
|
||||
/**
|
||||
* Whether the library was compiled with {@link com.google.android.exoplayer2.util.Assertions}
|
||||
|
@ -434,13 +434,34 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
|
||||
} else if (codecInfo.isSeamlessAdaptationSupported(
|
||||
oldFormat, newFormat, /* isNewFormatComplete= */ true)) {
|
||||
return KEEP_CODEC_RESULT_YES_WITHOUT_RECONFIGURATION;
|
||||
} else if (areCodecConfigurationCompatible(oldFormat, newFormat)) {
|
||||
} else if (canKeepCodecWithFlush(oldFormat, newFormat)) {
|
||||
return KEEP_CODEC_RESULT_YES_WITH_FLUSH;
|
||||
} else {
|
||||
return KEEP_CODEC_RESULT_NO;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the codec can be flushed and reused when switching to a new format. Reuse is
|
||||
* generally possible when the codec would be configured in an identical way after the format
|
||||
* change (excluding {@link MediaFormat#KEY_MAX_INPUT_SIZE} and configuration that does not come
|
||||
* from the {@link Format}).
|
||||
*
|
||||
* @param oldFormat The first format.
|
||||
* @param newFormat The second format.
|
||||
* @return Whether the codec can be flushed and reused when switching to a new format.
|
||||
*/
|
||||
protected boolean canKeepCodecWithFlush(Format oldFormat, Format newFormat) {
|
||||
// Flush and reuse the codec if the audio format and initialization data matches. For Opus, we
|
||||
// don't flush and reuse the codec because the decoder may discard samples after flushing, which
|
||||
// would result in audio being dropped just after a stream change (see [Internal: b/143450854]).
|
||||
return Util.areEqual(oldFormat.sampleMimeType, newFormat.sampleMimeType)
|
||||
&& oldFormat.channelCount == newFormat.channelCount
|
||||
&& oldFormat.sampleRate == newFormat.sampleRate
|
||||
&& oldFormat.initializationDataEquals(newFormat)
|
||||
&& !MimeTypes.AUDIO_OPUS.equals(oldFormat.sampleMimeType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MediaClock getMediaClock() {
|
||||
return this;
|
||||
@ -818,24 +839,6 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
|
||||
return format.maxInputSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether two {@link Format}s will cause the same codec to be configured in an identical
|
||||
* way, excluding {@link MediaFormat#KEY_MAX_INPUT_SIZE} and configuration that does not come from
|
||||
* the {@link Format}.
|
||||
*
|
||||
* @param oldFormat The first format.
|
||||
* @param newFormat The second format.
|
||||
* @return Whether the two formats will cause a codec to be configured in an identical way,
|
||||
* excluding {@link MediaFormat#KEY_MAX_INPUT_SIZE} and configuration that does not come from
|
||||
* the {@link Format}.
|
||||
*/
|
||||
protected boolean areCodecConfigurationCompatible(Format oldFormat, Format newFormat) {
|
||||
return Util.areEqual(oldFormat.sampleMimeType, newFormat.sampleMimeType)
|
||||
&& oldFormat.channelCount == newFormat.channelCount
|
||||
&& oldFormat.sampleRate == newFormat.sampleRate
|
||||
&& oldFormat.initializationDataEquals(newFormat);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the framework {@link MediaFormat} that can be used to configure a {@link MediaCodec}
|
||||
* for decoding the given {@link Format} for playback.
|
||||
|
@ -89,6 +89,10 @@ public interface Extractor {
|
||||
* {@link #RESULT_SEEK} is returned. If the extractor reached the end of the data provided by the
|
||||
* {@link ExtractorInput}, then {@link #RESULT_END_OF_INPUT} is returned.
|
||||
*
|
||||
* <p>When this method throws an {@link IOException} or an {@link InterruptedException},
|
||||
* extraction may continue by providing an {@link ExtractorInput} with an unchanged {@link
|
||||
* ExtractorInput#getPosition() read position} to a subsequent call to this method.
|
||||
*
|
||||
* @param input The {@link ExtractorInput} from which data should be read.
|
||||
* @param seekPosition If {@link #RESULT_SEEK} is returned, this holder is updated to hold the
|
||||
* position of the required data.
|
||||
|
@ -348,9 +348,7 @@ import java.util.List;
|
||||
}
|
||||
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.
|
||||
if (track.editListDurations == null) {
|
||||
Util.scaleLargeTimestampsInPlace(timestamps, C.MICROS_PER_SECOND, track.timescale);
|
||||
return new TrackSampleTable(
|
||||
track, offsets, sizes, maximumSize, timestamps, flags, durationUs);
|
||||
|
@ -78,17 +78,17 @@ import java.util.List;
|
||||
List<byte[]> initializationData = Collections.singletonList(metadata);
|
||||
setupData.format =
|
||||
Format.createAudioSampleFormat(
|
||||
null,
|
||||
/* id= */ null,
|
||||
MimeTypes.AUDIO_FLAC,
|
||||
null,
|
||||
Format.NO_VALUE,
|
||||
/* codecs= */ null,
|
||||
streamMetadata.bitRate(),
|
||||
/* maxInputSize= */ Format.NO_VALUE,
|
||||
streamMetadata.channels,
|
||||
streamMetadata.sampleRate,
|
||||
initializationData,
|
||||
null,
|
||||
0,
|
||||
null);
|
||||
/* drmInitData= */ null,
|
||||
/* selectionFlags= */ 0,
|
||||
/* language= */ null);
|
||||
} else if ((data[0] & 0x7F) == SEEKTABLE_PACKET_TYPE) {
|
||||
flacOggSeeker = new FlacOggSeeker();
|
||||
flacOggSeeker.parseSeekTable(packet);
|
||||
|
@ -23,7 +23,7 @@ import com.google.android.exoplayer2.util.Util;
|
||||
/** Header for a WAV file. */
|
||||
/* package */ final class WavHeader implements SeekMap {
|
||||
|
||||
/** Number of audio chanels. */
|
||||
/** Number of audio channels. */
|
||||
private final int numChannels;
|
||||
/** Sample rate in Hertz. */
|
||||
private final int sampleRateHz;
|
||||
|
@ -328,13 +328,13 @@ public final class MediaCodecInfo {
|
||||
|
||||
/**
|
||||
* Whether the decoder supports video with a given width, height and frame rate.
|
||||
* <p>
|
||||
* Must not be called if the device SDK version is less than 21.
|
||||
*
|
||||
* <p>Must not be called if the device SDK version is less than 21.
|
||||
*
|
||||
* @param width Width in pixels.
|
||||
* @param height Height in pixels.
|
||||
* @param frameRate Optional frame rate in frames per second. Ignored if set to
|
||||
* {@link Format#NO_VALUE} or any value less than or equal to 0.
|
||||
* @param frameRate Optional frame rate in frames per second. Ignored if set to {@link
|
||||
* Format#NO_VALUE} or any value less than or equal to 0.
|
||||
* @return Whether the decoder supports video with the given width, height and frame rate.
|
||||
*/
|
||||
@TargetApi(21)
|
||||
|
@ -32,7 +32,8 @@ public class Cue {
|
||||
public static final Cue EMPTY = new Cue("");
|
||||
|
||||
/** An unset position or width. */
|
||||
public static final float DIMEN_UNSET = Float.MIN_VALUE;
|
||||
// Note: We deliberately don't use Float.MIN_VALUE because it's positive & very close to zero.
|
||||
public static final float DIMEN_UNSET = -Float.MAX_VALUE;
|
||||
|
||||
/**
|
||||
* The type of anchor, which may be unset. One of {@link #TYPE_UNSET}, {@link #ANCHOR_TYPE_START},
|
||||
|
@ -5,11 +5,11 @@ seekMap:
|
||||
numberOfTracks = 1
|
||||
track 0:
|
||||
format:
|
||||
bitrate = -1
|
||||
bitrate = 768000
|
||||
id = null
|
||||
containerMimeType = null
|
||||
sampleMimeType = audio/flac
|
||||
maxInputSize = 768000
|
||||
maxInputSize = -1
|
||||
width = -1
|
||||
height = -1
|
||||
frameRate = -1.0
|
||||
|
@ -5,11 +5,11 @@ seekMap:
|
||||
numberOfTracks = 1
|
||||
track 0:
|
||||
format:
|
||||
bitrate = -1
|
||||
bitrate = 768000
|
||||
id = null
|
||||
containerMimeType = null
|
||||
sampleMimeType = audio/flac
|
||||
maxInputSize = 768000
|
||||
maxInputSize = -1
|
||||
width = -1
|
||||
height = -1
|
||||
frameRate = -1.0
|
||||
|
@ -5,11 +5,11 @@ seekMap:
|
||||
numberOfTracks = 1
|
||||
track 0:
|
||||
format:
|
||||
bitrate = -1
|
||||
bitrate = 768000
|
||||
id = null
|
||||
containerMimeType = null
|
||||
sampleMimeType = audio/flac
|
||||
maxInputSize = 768000
|
||||
maxInputSize = -1
|
||||
width = -1
|
||||
height = -1
|
||||
frameRate = -1.0
|
||||
|
@ -5,11 +5,11 @@ seekMap:
|
||||
numberOfTracks = 1
|
||||
track 0:
|
||||
format:
|
||||
bitrate = -1
|
||||
bitrate = 768000
|
||||
id = null
|
||||
containerMimeType = null
|
||||
sampleMimeType = audio/flac
|
||||
maxInputSize = 768000
|
||||
maxInputSize = -1
|
||||
width = -1
|
||||
height = -1
|
||||
frameRate = -1.0
|
||||
|
@ -5,11 +5,11 @@ seekMap:
|
||||
numberOfTracks = 1
|
||||
track 0:
|
||||
format:
|
||||
bitrate = -1
|
||||
bitrate = 768000
|
||||
id = null
|
||||
containerMimeType = null
|
||||
sampleMimeType = audio/flac
|
||||
maxInputSize = 768000
|
||||
maxInputSize = -1
|
||||
width = -1
|
||||
height = -1
|
||||
frameRate = -1.0
|
||||
|
@ -5,11 +5,11 @@ seekMap:
|
||||
numberOfTracks = 1
|
||||
track 0:
|
||||
format:
|
||||
bitrate = -1
|
||||
bitrate = 768000
|
||||
id = null
|
||||
containerMimeType = null
|
||||
sampleMimeType = audio/flac
|
||||
maxInputSize = 768000
|
||||
maxInputSize = -1
|
||||
width = -1
|
||||
height = -1
|
||||
frameRate = -1.0
|
||||
|
@ -5,11 +5,11 @@ seekMap:
|
||||
numberOfTracks = 1
|
||||
track 0:
|
||||
format:
|
||||
bitrate = -1
|
||||
bitrate = 768000
|
||||
id = null
|
||||
containerMimeType = null
|
||||
sampleMimeType = audio/flac
|
||||
maxInputSize = 768000
|
||||
maxInputSize = -1
|
||||
width = -1
|
||||
height = -1
|
||||
frameRate = -1.0
|
||||
|
@ -5,11 +5,11 @@ seekMap:
|
||||
numberOfTracks = 1
|
||||
track 0:
|
||||
format:
|
||||
bitrate = -1
|
||||
bitrate = 768000
|
||||
id = null
|
||||
containerMimeType = null
|
||||
sampleMimeType = audio/flac
|
||||
maxInputSize = 768000
|
||||
maxInputSize = -1
|
||||
width = -1
|
||||
height = -1
|
||||
frameRate = -1.0
|
||||
|
@ -5,11 +5,11 @@ seekMap:
|
||||
numberOfTracks = 1
|
||||
track 0:
|
||||
format:
|
||||
bitrate = -1
|
||||
bitrate = 768000
|
||||
id = null
|
||||
containerMimeType = null
|
||||
sampleMimeType = audio/flac
|
||||
maxInputSize = 768000
|
||||
maxInputSize = -1
|
||||
width = -1
|
||||
height = -1
|
||||
frameRate = -1.0
|
||||
|
@ -5,11 +5,11 @@ seekMap:
|
||||
numberOfTracks = 1
|
||||
track 0:
|
||||
format:
|
||||
bitrate = -1
|
||||
bitrate = 768000
|
||||
id = null
|
||||
containerMimeType = null
|
||||
sampleMimeType = audio/flac
|
||||
maxInputSize = 768000
|
||||
maxInputSize = -1
|
||||
width = -1
|
||||
height = -1
|
||||
frameRate = -1.0
|
||||
|
@ -445,7 +445,15 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
|
||||
? Util.getCodecsOfType(variant.format.codecs, C.TRACK_TYPE_AUDIO)
|
||||
: null;
|
||||
sampleMimeType = codecs != null ? MimeTypes.getMediaMimeType(codecs) : null;
|
||||
int channelCount = parseChannelsAttribute(line, variableDefinitions);
|
||||
String channelsString =
|
||||
parseOptionalStringAttr(line, REGEX_CHANNELS, variableDefinitions);
|
||||
int channelCount = Format.NO_VALUE;
|
||||
if (channelsString != null) {
|
||||
channelCount = Integer.parseInt(Util.splitAtFirst(channelsString, "/")[0]);
|
||||
if (MimeTypes.AUDIO_E_AC3.equals(sampleMimeType) && channelsString.endsWith("/JOC")) {
|
||||
sampleMimeType = MimeTypes.AUDIO_E_AC3_JOC;
|
||||
}
|
||||
}
|
||||
format =
|
||||
Format.createAudioContainerFormat(
|
||||
/* id= */ formatId,
|
||||
@ -819,13 +827,6 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
|
||||
return roleFlags;
|
||||
}
|
||||
|
||||
private static int parseChannelsAttribute(String line, Map<String, String> variableDefinitions) {
|
||||
String channelsString = parseOptionalStringAttr(line, REGEX_CHANNELS, variableDefinitions);
|
||||
return channelsString != null
|
||||
? Integer.parseInt(Util.splitAtFirst(channelsString, "/")[0])
|
||||
: Format.NO_VALUE;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static SchemeData parseDrmSchemeData(
|
||||
String line, String keyFormat, Map<String, String> variableDefinitions)
|
||||
|
@ -1348,7 +1348,12 @@ public class PlayerNotificationManager {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRepeatModeChanged(int repeatMode) {
|
||||
public void onRepeatModeChanged(@Player.RepeatMode int repeatMode) {
|
||||
startOrUpdateNotification();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) {
|
||||
startOrUpdateNotification();
|
||||
}
|
||||
}
|
||||
|
@ -60,6 +60,7 @@ public class FakeAdaptiveMediaPeriod extends FakeMediaPeriod
|
||||
this.transferListener = transferListener;
|
||||
this.durationUs = durationUs;
|
||||
this.sampleStreams = newSampleStreamArray(0);
|
||||
this.sequenceableLoader = new CompositeSequenceableLoader(new SequenceableLoader[0]);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
Loading…
x
Reference in New Issue
Block a user