Merge pull request #6619 from google/dev-v2-r2.10.7

r2.10.7
This commit is contained in:
Oliver Woodman 2019-11-06 13:06:22 +00:00 committed by GitHub
commit d73d64bc6f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 110 additions and 80 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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();
}

View File

@ -29,11 +29,11 @@ public final class ExoPlayerLibraryInfo {
/** The version of the library expressed as a string, for example "1.2.3". */
// Intentionally hardcoded. Do not derive from other constants (e.g. VERSION_INT) or vice versa.
public static final String VERSION = "2.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}

View File

@ -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.

View File

@ -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.

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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)

View File

@ -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},

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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();
}
}

View File

@ -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