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 # # 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 * Add `Player.onPlaybackSuppressionReasonChanged` to allow listeners to
detect playbacks suppressions (e.g. transient audio focus loss) directly detect playbacks suppressions (e.g. transient audio focus loss) directly

View File

@ -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.10.6' releaseVersion = '2.10.7'
releaseVersionCode = 2010006 releaseVersionCode = 2010007
minSdkVersion = 16 minSdkVersion = 16
targetSdkVersion = 28 targetSdkVersion = 28
compileSdkVersion = 28 compileSdkVersion = 28

View File

@ -308,7 +308,11 @@ public final class LeanbackPlayerAdapter extends PlayerAdapter implements Runnab
@Override @Override
public void onVideoSizeChanged( public void onVideoSizeChanged(
int width, int height, int unappliedRotationDegrees, float pixelWidthHeightRatio) { 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 @Override

View File

@ -700,6 +700,9 @@ public final class MediaSessionConnector {
/* position= */ 0, /* position= */ 0,
/* playbackSpeed= */ 0, /* playbackSpeed= */ 0,
/* updateTime= */ SystemClock.elapsedRealtime()); /* updateTime= */ SystemClock.elapsedRealtime());
mediaSession.setRepeatMode(PlaybackStateCompat.REPEAT_MODE_NONE);
mediaSession.setShuffleMode(PlaybackStateCompat.SHUFFLE_MODE_NONE);
mediaSession.setPlaybackState(builder.build()); mediaSession.setPlaybackState(builder.build());
return; return;
} }
@ -748,6 +751,18 @@ public final class MediaSessionConnector {
sessionPlaybackSpeed, sessionPlaybackSpeed,
/* updateTime= */ SystemClock.elapsedRealtime()) /* updateTime= */ SystemClock.elapsedRealtime())
.setExtras(extras); .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()); mediaSession.setPlaybackState(builder.build());
} }
@ -1047,21 +1062,11 @@ public final class MediaSessionConnector {
@Override @Override
public void onRepeatModeChanged(@Player.RepeatMode int repeatMode) { 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(); invalidateMediaSessionPlaybackState();
} }
@Override @Override
public void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) { public void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) {
mediaSession.setShuffleMode(
shuffleModeEnabled
? PlaybackStateCompat.SHUFFLE_MODE_ALL
: PlaybackStateCompat.SHUFFLE_MODE_NONE);
invalidateMediaSessionPlaybackState(); invalidateMediaSessionPlaybackState();
invalidateMediaSessionQueue(); 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". */ /** 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.10.6"; public static final String VERSION = "2.10.7";
/** 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.10.6"; public static final String VERSION_SLASHY = "ExoPlayerLib/2.10.7";
/** /**
* The version of the library expressed as an integer, for example 1002003. * 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). * 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 = 2010006; public static final int VERSION_INT = 2010007;
/** /**
* Whether the library was compiled with {@link com.google.android.exoplayer2.util.Assertions} * 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( } else if (codecInfo.isSeamlessAdaptationSupported(
oldFormat, newFormat, /* isNewFormatComplete= */ true)) { oldFormat, newFormat, /* isNewFormatComplete= */ true)) {
return KEEP_CODEC_RESULT_YES_WITHOUT_RECONFIGURATION; return KEEP_CODEC_RESULT_YES_WITHOUT_RECONFIGURATION;
} else if (areCodecConfigurationCompatible(oldFormat, newFormat)) { } else if (canKeepCodecWithFlush(oldFormat, newFormat)) {
return KEEP_CODEC_RESULT_YES_WITH_FLUSH; return KEEP_CODEC_RESULT_YES_WITH_FLUSH;
} else { } else {
return KEEP_CODEC_RESULT_NO; 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 @Override
public MediaClock getMediaClock() { public MediaClock getMediaClock() {
return this; return this;
@ -818,24 +839,6 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
return format.maxInputSize; 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} * Returns the framework {@link MediaFormat} that can be used to configure a {@link MediaCodec}
* for decoding the given {@link Format} for playback. * 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 #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. * {@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 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 * @param seekPosition If {@link #RESULT_SEEK} is returned, this holder is updated to hold the
* position of the required data. * 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); long durationUs = Util.scaleLargeTimestamp(duration, C.MICROS_PER_SECOND, track.timescale);
if (track.editListDurations == null || gaplessInfoHolder.hasGaplessInfo()) { if (track.editListDurations == null) {
// There is no edit list, or we are ignoring it as we already have gapless metadata to apply.
// This implementation does not support applying both gapless metadata and an edit list.
Util.scaleLargeTimestampsInPlace(timestamps, C.MICROS_PER_SECOND, track.timescale); Util.scaleLargeTimestampsInPlace(timestamps, C.MICROS_PER_SECOND, track.timescale);
return new TrackSampleTable( return new TrackSampleTable(
track, offsets, sizes, maximumSize, timestamps, flags, durationUs); track, offsets, sizes, maximumSize, timestamps, flags, durationUs);

View File

@ -78,17 +78,17 @@ import java.util.List;
List<byte[]> initializationData = Collections.singletonList(metadata); List<byte[]> initializationData = Collections.singletonList(metadata);
setupData.format = setupData.format =
Format.createAudioSampleFormat( Format.createAudioSampleFormat(
null, /* id= */ null,
MimeTypes.AUDIO_FLAC, MimeTypes.AUDIO_FLAC,
null, /* codecs= */ null,
Format.NO_VALUE,
streamMetadata.bitRate(), streamMetadata.bitRate(),
/* maxInputSize= */ Format.NO_VALUE,
streamMetadata.channels, streamMetadata.channels,
streamMetadata.sampleRate, streamMetadata.sampleRate,
initializationData, initializationData,
null, /* drmInitData= */ null,
0, /* selectionFlags= */ 0,
null); /* language= */ null);
} else if ((data[0] & 0x7F) == SEEKTABLE_PACKET_TYPE) { } else if ((data[0] & 0x7F) == SEEKTABLE_PACKET_TYPE) {
flacOggSeeker = new FlacOggSeeker(); flacOggSeeker = new FlacOggSeeker();
flacOggSeeker.parseSeekTable(packet); flacOggSeeker.parseSeekTable(packet);

View File

@ -23,7 +23,7 @@ import com.google.android.exoplayer2.util.Util;
/** Header for a WAV file. */ /** Header for a WAV file. */
/* package */ final class WavHeader implements SeekMap { /* package */ final class WavHeader implements SeekMap {
/** Number of audio chanels. */ /** Number of audio channels. */
private final int numChannels; private final int numChannels;
/** Sample rate in Hertz. */ /** Sample rate in Hertz. */
private final int sampleRateHz; 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. * 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 width Width in pixels.
* @param height Height in pixels. * @param height Height in pixels.
* @param frameRate Optional frame rate in frames per second. Ignored if set to * @param frameRate Optional frame rate in frames per second. Ignored if set to {@link
* {@link Format#NO_VALUE} or any value less than or equal to 0. * 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. * @return Whether the decoder supports video with the given width, height and frame rate.
*/ */
@TargetApi(21) @TargetApi(21)

View File

@ -32,7 +32,8 @@ public class Cue {
public static final Cue EMPTY = new Cue(""); public static final Cue EMPTY = new Cue("");
/** An unset position or width. */ /** 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}, * 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 numberOfTracks = 1
track 0: track 0:
format: format:
bitrate = -1 bitrate = 768000
id = null id = null
containerMimeType = null containerMimeType = null
sampleMimeType = audio/flac sampleMimeType = audio/flac
maxInputSize = 768000 maxInputSize = -1
width = -1 width = -1
height = -1 height = -1
frameRate = -1.0 frameRate = -1.0

View File

@ -5,11 +5,11 @@ seekMap:
numberOfTracks = 1 numberOfTracks = 1
track 0: track 0:
format: format:
bitrate = -1 bitrate = 768000
id = null id = null
containerMimeType = null containerMimeType = null
sampleMimeType = audio/flac sampleMimeType = audio/flac
maxInputSize = 768000 maxInputSize = -1
width = -1 width = -1
height = -1 height = -1
frameRate = -1.0 frameRate = -1.0

View File

@ -5,11 +5,11 @@ seekMap:
numberOfTracks = 1 numberOfTracks = 1
track 0: track 0:
format: format:
bitrate = -1 bitrate = 768000
id = null id = null
containerMimeType = null containerMimeType = null
sampleMimeType = audio/flac sampleMimeType = audio/flac
maxInputSize = 768000 maxInputSize = -1
width = -1 width = -1
height = -1 height = -1
frameRate = -1.0 frameRate = -1.0

View File

@ -5,11 +5,11 @@ seekMap:
numberOfTracks = 1 numberOfTracks = 1
track 0: track 0:
format: format:
bitrate = -1 bitrate = 768000
id = null id = null
containerMimeType = null containerMimeType = null
sampleMimeType = audio/flac sampleMimeType = audio/flac
maxInputSize = 768000 maxInputSize = -1
width = -1 width = -1
height = -1 height = -1
frameRate = -1.0 frameRate = -1.0

View File

@ -5,11 +5,11 @@ seekMap:
numberOfTracks = 1 numberOfTracks = 1
track 0: track 0:
format: format:
bitrate = -1 bitrate = 768000
id = null id = null
containerMimeType = null containerMimeType = null
sampleMimeType = audio/flac sampleMimeType = audio/flac
maxInputSize = 768000 maxInputSize = -1
width = -1 width = -1
height = -1 height = -1
frameRate = -1.0 frameRate = -1.0

View File

@ -5,11 +5,11 @@ seekMap:
numberOfTracks = 1 numberOfTracks = 1
track 0: track 0:
format: format:
bitrate = -1 bitrate = 768000
id = null id = null
containerMimeType = null containerMimeType = null
sampleMimeType = audio/flac sampleMimeType = audio/flac
maxInputSize = 768000 maxInputSize = -1
width = -1 width = -1
height = -1 height = -1
frameRate = -1.0 frameRate = -1.0

View File

@ -5,11 +5,11 @@ seekMap:
numberOfTracks = 1 numberOfTracks = 1
track 0: track 0:
format: format:
bitrate = -1 bitrate = 768000
id = null id = null
containerMimeType = null containerMimeType = null
sampleMimeType = audio/flac sampleMimeType = audio/flac
maxInputSize = 768000 maxInputSize = -1
width = -1 width = -1
height = -1 height = -1
frameRate = -1.0 frameRate = -1.0

View File

@ -5,11 +5,11 @@ seekMap:
numberOfTracks = 1 numberOfTracks = 1
track 0: track 0:
format: format:
bitrate = -1 bitrate = 768000
id = null id = null
containerMimeType = null containerMimeType = null
sampleMimeType = audio/flac sampleMimeType = audio/flac
maxInputSize = 768000 maxInputSize = -1
width = -1 width = -1
height = -1 height = -1
frameRate = -1.0 frameRate = -1.0

View File

@ -5,11 +5,11 @@ seekMap:
numberOfTracks = 1 numberOfTracks = 1
track 0: track 0:
format: format:
bitrate = -1 bitrate = 768000
id = null id = null
containerMimeType = null containerMimeType = null
sampleMimeType = audio/flac sampleMimeType = audio/flac
maxInputSize = 768000 maxInputSize = -1
width = -1 width = -1
height = -1 height = -1
frameRate = -1.0 frameRate = -1.0

View File

@ -5,11 +5,11 @@ seekMap:
numberOfTracks = 1 numberOfTracks = 1
track 0: track 0:
format: format:
bitrate = -1 bitrate = 768000
id = null id = null
containerMimeType = null containerMimeType = null
sampleMimeType = audio/flac sampleMimeType = audio/flac
maxInputSize = 768000 maxInputSize = -1
width = -1 width = -1
height = -1 height = -1
frameRate = -1.0 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) ? Util.getCodecsOfType(variant.format.codecs, C.TRACK_TYPE_AUDIO)
: null; : null;
sampleMimeType = codecs != null ? MimeTypes.getMediaMimeType(codecs) : 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 =
Format.createAudioContainerFormat( Format.createAudioContainerFormat(
/* id= */ formatId, /* id= */ formatId,
@ -819,13 +827,6 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
return roleFlags; 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 @Nullable
private static SchemeData parseDrmSchemeData( private static SchemeData parseDrmSchemeData(
String line, String keyFormat, Map<String, String> variableDefinitions) String line, String keyFormat, Map<String, String> variableDefinitions)

View File

@ -1348,7 +1348,12 @@ public class PlayerNotificationManager {
} }
@Override @Override
public void onRepeatModeChanged(int repeatMode) { public void onRepeatModeChanged(@Player.RepeatMode int repeatMode) {
startOrUpdateNotification();
}
@Override
public void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) {
startOrUpdateNotification(); startOrUpdateNotification();
} }
} }

View File

@ -60,6 +60,7 @@ public class FakeAdaptiveMediaPeriod extends FakeMediaPeriod
this.transferListener = transferListener; this.transferListener = transferListener;
this.durationUs = durationUs; this.durationUs = durationUs;
this.sampleStreams = newSampleStreamArray(0); this.sampleStreams = newSampleStreamArray(0);
this.sequenceableLoader = new CompositeSequenceableLoader(new SequenceableLoader[0]);
} }
@Override @Override