Commit 33938c05 by Oliver Woodman Committed by GitHub

Merge pull request #6664 from google/dev-v2-r2.10.8

r2.10.8
parents d73d64bc 30f79a4c
Showing with 156 additions and 65 deletions
# Release notes #
### 2.10.7 (2019-11-12) ###
### 2.10.8 (2019-11-19) ###
* E-AC3 JOC
* Handle new signaling in DASH manifests
([#6636](https://github.com/google/ExoPlayer/issues/6636)).
* Fix E-AC3 JOC passthrough playback failing to initialize due to incorrect
channel count check.
* FLAC
* Fix sniffing for some FLAC streams.
* Fix FLAC `Format.bitrate` values.
* Parse ALAC channel count and sample rate information from a more robust source
when contained in MP4
([#6648](https://github.com/google/ExoPlayer/issues/6648)).
* Fix seeking into multi-period content in the edge case that the period
containing the seek position has just been removed
([#6641](https://github.com/google/ExoPlayer/issues/6641)).
### 2.10.7 (2019-11-06) ###
* HLS: Fix detection of Dolby Atmos to match the HLS authoring specification.
* MediaSession extension: Update shuffle and repeat modes when playback state
......
......@@ -13,8 +13,8 @@
// limitations under the License.
project.ext {
// ExoPlayer version and version code.
releaseVersion = '2.10.7'
releaseVersionCode = 2010007
releaseVersion = '2.10.8'
releaseVersionCode = 2010008
minSdkVersion = 16
targetSdkVersion = 28
compileSdkVersion = 28
......
......@@ -5,7 +5,7 @@ seekMap:
numberOfTracks = 1
track 0:
format:
bitrate = 768000
bitrate = 1536000
id = null
containerMimeType = null
sampleMimeType = audio/raw
......
......@@ -5,7 +5,7 @@ seekMap:
numberOfTracks = 1
track 0:
format:
bitrate = 768000
bitrate = 1536000
id = null
containerMimeType = null
sampleMimeType = audio/raw
......
......@@ -5,7 +5,7 @@ seekMap:
numberOfTracks = 1
track 0:
format:
bitrate = 768000
bitrate = 1536000
id = null
containerMimeType = null
sampleMimeType = audio/raw
......
......@@ -5,7 +5,7 @@ seekMap:
numberOfTracks = 1
track 0:
format:
bitrate = 768000
bitrate = 1536000
id = null
containerMimeType = null
sampleMimeType = audio/raw
......
......@@ -5,7 +5,7 @@ seekMap:
numberOfTracks = 1
track 0:
format:
bitrate = 768000
bitrate = 1536000
id = null
containerMimeType = null
sampleMimeType = audio/raw
......
......@@ -5,7 +5,7 @@ seekMap:
numberOfTracks = 1
track 0:
format:
bitrate = 768000
bitrate = 1536000
id = null
containerMimeType = null
sampleMimeType = audio/raw
......
......@@ -5,7 +5,7 @@ seekMap:
numberOfTracks = 1
track 0:
format:
bitrate = 768000
bitrate = 1536000
id = null
containerMimeType = null
sampleMimeType = audio/raw
......
......@@ -5,7 +5,7 @@ seekMap:
numberOfTracks = 1
track 0:
format:
bitrate = 768000
bitrate = 1536000
id = null
containerMimeType = null
sampleMimeType = audio/raw
......
......@@ -72,11 +72,8 @@ public final class FlacExtractor implements Extractor {
*/
public static final int FLAG_DISABLE_ID3_METADATA = 1;
/**
* FLAC signature: first 4 is the signature word, second 4 is the sizeof STREAMINFO. 0x22 is the
* mandatory STREAMINFO.
*/
private static final byte[] FLAC_SIGNATURE = {'f', 'L', 'a', 'C', 0, 0, 0, 0x22};
/** FLAC stream marker */
private static final byte[] FLAC_STREAM_MARKER = {'f', 'L', 'a', 'C'};
private final ParsableByteArray outputBuffer;
private final Id3Peeker id3Peeker;
......@@ -126,7 +123,7 @@ public final class FlacExtractor implements Extractor {
if (input.getPosition() == 0) {
id3Metadata = peekId3Data(input);
}
return peekFlacSignature(input);
return peekFlacStreamMarker(input);
}
@Override
......@@ -255,15 +252,15 @@ public final class FlacExtractor implements Extractor {
}
/**
* Peeks from the beginning of the input to see if {@link #FLAC_SIGNATURE} is present.
* Peeks from the beginning of the input to see if {@link #FLAC_STREAM_MARKER} is present.
*
* @return Whether the input begins with {@link #FLAC_SIGNATURE}.
* @return Whether the input begins with {@link #FLAC_STREAM_MARKER}.
*/
private static boolean peekFlacSignature(ExtractorInput input)
private static boolean peekFlacStreamMarker(ExtractorInput input)
throws IOException, InterruptedException {
byte[] header = new byte[FLAC_SIGNATURE.length];
input.peekFully(header, /* offset= */ 0, FLAC_SIGNATURE.length);
return Arrays.equals(header, FLAC_SIGNATURE);
byte[] header = new byte[FLAC_STREAM_MARKER.length];
input.peekFully(header, /* offset= */ 0, FLAC_STREAM_MARKER.length);
return Arrays.equals(header, FLAC_STREAM_MARKER);
}
/**
......
......@@ -1433,6 +1433,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
* @throws IllegalSeekPositionException If the window index of the seek position is outside the
* bounds of the timeline.
*/
@Nullable
private Pair<Object, Long> resolveSeekPosition(
SeekPosition seekPosition, boolean trySubsequentPeriods) {
Timeline timeline = playbackInfo.timeline;
......@@ -1467,11 +1468,12 @@ import java.util.concurrent.atomic.AtomicBoolean;
}
if (trySubsequentPeriods) {
// Try and find a subsequent period from the seek timeline in the internal timeline.
@Nullable
Object periodUid = resolveSubsequentPeriod(periodPosition.first, seekTimeline, timeline);
if (periodUid != null) {
// We found one. Map the SeekPosition onto the corresponding default position.
// We found one. Use the default position of the corresponding window.
return getPeriodPosition(
timeline, timeline.getPeriod(periodIndex, period).windowIndex, C.TIME_UNSET);
timeline, timeline.getPeriodByUid(periodUid, period).windowIndex, C.TIME_UNSET);
}
}
// We didn't find one. Give up.
......
......@@ -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.7";
public static final String VERSION = "2.10.8";
/** 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.7";
public static final String VERSION_SLASHY = "ExoPlayerLib/2.10.8";
/**
* 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 = 2010007;
public static final int VERSION_INT = 2010008;
/**
* Whether the library was compiled with {@link com.google.android.exoplayer2.util.Assertions}
......
......@@ -543,7 +543,8 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
@C.Encoding
protected int getPassthroughEncoding(int channelCount, String mimeType) {
if (MimeTypes.AUDIO_E_AC3_JOC.equals(mimeType)) {
if (audioSink.supportsOutput(channelCount, C.ENCODING_E_AC3_JOC)) {
// E-AC3 JOC is object-based so the output channel count is arbitrary.
if (audioSink.supportsOutput(/* channelCount= */ Format.NO_VALUE, C.ENCODING_E_AC3_JOC)) {
return MimeTypes.getEncoding(MimeTypes.AUDIO_E_AC3_JOC);
}
// E-AC3 receivers can decode JOC streams, but in 2-D rather than 3-D, so try to fall back.
......
......@@ -1114,8 +1114,8 @@ import java.util.List;
mimeType = mimeTypeAndInitializationData.first;
initializationData = mimeTypeAndInitializationData.second;
if (MimeTypes.AUDIO_AAC.equals(mimeType)) {
// TODO: Do we really need to do this? See [Internal: b/10903778]
// Update sampleRate and channelCount from the AudioSpecificConfig initialization data.
// Update sampleRate and channelCount from the AudioSpecificConfig initialization data,
// which is more reliable. See [Internal: b/10903778].
Pair<Integer, Integer> audioSpecificConfig =
CodecSpecificDataUtil.parseAacAudioSpecificConfig(initializationData);
sampleRate = audioSpecificConfig.first;
......@@ -1160,6 +1160,12 @@ import java.util.List;
initializationData = new byte[childAtomBodySize];
parent.setPosition(childPosition + Atom.FULL_HEADER_SIZE);
parent.readBytes(initializationData, /* offset= */ 0, childAtomBodySize);
// Update sampleRate and channelCount from the AudioSpecificConfig initialization data,
// which is more reliable. See https://github.com/google/ExoPlayer/pull/6629.
Pair<Integer, Integer> audioSpecificConfig =
CodecSpecificDataUtil.parseAlacAudioSpecificConfig(initializationData);
sampleRate = audioSpecificConfig.first;
channelCount = audioSpecificConfig.second;
}
childPosition += childAtomSize;
}
......
......@@ -73,6 +73,8 @@ import java.util.List;
byte[] data = packet.data;
if (streamMetadata == null) {
streamMetadata = new FlacStreamMetadata(data, 17);
int maxInputSize =
streamMetadata.maxFrameSize == 0 ? Format.NO_VALUE : streamMetadata.maxFrameSize;
byte[] metadata = Arrays.copyOfRange(data, 9, packet.limit());
metadata[4] = (byte) 0x80; // Set the last metadata block flag, ignore the other blocks
List<byte[]> initializationData = Collections.singletonList(metadata);
......@@ -82,7 +84,7 @@ import java.util.List;
MimeTypes.AUDIO_FLAC,
/* codecs= */ null,
streamMetadata.bitRate(),
/* maxInputSize= */ Format.NO_VALUE,
maxInputSize,
streamMetadata.channels,
streamMetadata.sampleRate,
initializationData,
......
......@@ -83,7 +83,7 @@ public final class CodecSpecificDataUtil {
private CodecSpecificDataUtil() {}
/**
* Parses an AudioSpecificConfig, as defined in ISO 14496-3 1.6.2.1
* Parses an AAC AudioSpecificConfig, as defined in ISO 14496-3 1.6.2.1
*
* @param audioSpecificConfig A byte array containing the AudioSpecificConfig to parse.
* @return A pair consisting of the sample rate in Hz and the channel count.
......@@ -95,7 +95,7 @@ public final class CodecSpecificDataUtil {
}
/**
* Parses an AudioSpecificConfig, as defined in ISO 14496-3 1.6.2.1
* Parses an AAC AudioSpecificConfig, as defined in ISO 14496-3 1.6.2.1
*
* @param bitArray A {@link ParsableBitArray} containing the AudioSpecificConfig to parse. The
* position is advanced to the end of the AudioSpecificConfig.
......@@ -104,8 +104,8 @@ public final class CodecSpecificDataUtil {
* @return A pair consisting of the sample rate in Hz and the channel count.
* @throws ParserException If the AudioSpecificConfig cannot be parsed as it's not supported.
*/
public static Pair<Integer, Integer> parseAacAudioSpecificConfig(ParsableBitArray bitArray,
boolean forceReadToEnd) throws ParserException {
public static Pair<Integer, Integer> parseAacAudioSpecificConfig(
ParsableBitArray bitArray, boolean forceReadToEnd) throws ParserException {
int audioObjectType = getAacAudioObjectType(bitArray);
int sampleRate = getAacSamplingFrequency(bitArray);
int channelConfiguration = bitArray.readBits(4);
......@@ -166,10 +166,10 @@ public final class CodecSpecificDataUtil {
* Builds a simple HE-AAC LC AudioSpecificConfig, as defined in ISO 14496-3 1.6.2.1
*
* @param sampleRate The sample rate in Hz.
* @param numChannels The number of channels.
* @param channelCount The channel count.
* @return The AudioSpecificConfig.
*/
public static byte[] buildAacLcAudioSpecificConfig(int sampleRate, int numChannels) {
public static byte[] buildAacLcAudioSpecificConfig(int sampleRate, int channelCount) {
int sampleRateIndex = C.INDEX_UNSET;
for (int i = 0; i < AUDIO_SPECIFIC_CONFIG_SAMPLING_RATE_TABLE.length; ++i) {
if (sampleRate == AUDIO_SPECIFIC_CONFIG_SAMPLING_RATE_TABLE[i]) {
......@@ -178,13 +178,13 @@ public final class CodecSpecificDataUtil {
}
int channelConfig = C.INDEX_UNSET;
for (int i = 0; i < AUDIO_SPECIFIC_CONFIG_CHANNEL_COUNT_TABLE.length; ++i) {
if (numChannels == AUDIO_SPECIFIC_CONFIG_CHANNEL_COUNT_TABLE[i]) {
if (channelCount == AUDIO_SPECIFIC_CONFIG_CHANNEL_COUNT_TABLE[i]) {
channelConfig = i;
}
}
if (sampleRate == C.INDEX_UNSET || channelConfig == C.INDEX_UNSET) {
throw new IllegalArgumentException("Invalid sample rate or number of channels: "
+ sampleRate + ", " + numChannels);
throw new IllegalArgumentException(
"Invalid sample rate or number of channels: " + sampleRate + ", " + channelCount);
}
return buildAacAudioSpecificConfig(AUDIO_OBJECT_TYPE_AAC_LC, sampleRateIndex, channelConfig);
}
......@@ -206,6 +206,22 @@ public final class CodecSpecificDataUtil {
}
/**
* Parses an ALAC AudioSpecificConfig (i.e. an <a
* href="https://github.com/macosforge/alac/blob/master/ALACMagicCookieDescription.txt">ALACSpecificConfig</a>).
*
* @param audioSpecificConfig A byte array containing the AudioSpecificConfig to parse.
* @return A pair consisting of the sample rate in Hz and the channel count.
*/
public static Pair<Integer, Integer> parseAlacAudioSpecificConfig(byte[] audioSpecificConfig) {
ParsableByteArray byteArray = new ParsableByteArray(audioSpecificConfig);
byteArray.setPosition(9);
int channelCount = byteArray.readUnsignedByte();
byteArray.setPosition(20);
int sampleRate = byteArray.readUnsignedIntToInt();
return Pair.create(sampleRate, channelCount);
}
/**
* Builds an RFC 6381 AVC codec string using the provided parameters.
*
* @param profileIdc The encoding profile.
......
......@@ -109,7 +109,7 @@ public final class FlacStreamMetadata {
/** Returns the bit-rate of the FLAC stream. */
public int bitRate() {
return bitsPerSample * sampleRate;
return bitsPerSample * sampleRate * channels;
}
/** Returns the duration of the FLAC stream in microseconds. */
......
......@@ -36,7 +36,7 @@ public final class DolbyVisionConfig {
int dvProfile = (profileData >> 1);
int dvLevel = ((profileData & 0x1) << 5) | ((data.readUnsignedByte() >> 3) & 0x1F);
String codecsPrefix;
if (dvProfile == 4 || dvProfile == 5) {
if (dvProfile == 4 || dvProfile == 5 || dvProfile == 7) {
codecsPrefix = "dvhe";
} else if (dvProfile == 8) {
codecsPrefix = "hev1";
......
......@@ -5,11 +5,11 @@ seekMap:
numberOfTracks = 1
track 0:
format:
bitrate = 768000
bitrate = 1536000
id = null
containerMimeType = null
sampleMimeType = audio/flac
maxInputSize = -1
maxInputSize = 5776
width = -1
height = -1
frameRate = -1.0
......
......@@ -5,11 +5,11 @@ seekMap:
numberOfTracks = 1
track 0:
format:
bitrate = 768000
bitrate = 1536000
id = null
containerMimeType = null
sampleMimeType = audio/flac
maxInputSize = -1
maxInputSize = 5776
width = -1
height = -1
frameRate = -1.0
......
......@@ -5,11 +5,11 @@ seekMap:
numberOfTracks = 1
track 0:
format:
bitrate = 768000
bitrate = 1536000
id = null
containerMimeType = null
sampleMimeType = audio/flac
maxInputSize = -1
maxInputSize = 5776
width = -1
height = -1
frameRate = -1.0
......
......@@ -5,11 +5,11 @@ seekMap:
numberOfTracks = 1
track 0:
format:
bitrate = 768000
bitrate = 1536000
id = null
containerMimeType = null
sampleMimeType = audio/flac
maxInputSize = -1
maxInputSize = 5776
width = -1
height = -1
frameRate = -1.0
......
......@@ -5,11 +5,11 @@ seekMap:
numberOfTracks = 1
track 0:
format:
bitrate = 768000
bitrate = 1536000
id = null
containerMimeType = null
sampleMimeType = audio/flac
maxInputSize = -1
maxInputSize = 5776
width = -1
height = -1
frameRate = -1.0
......
......@@ -5,11 +5,11 @@ seekMap:
numberOfTracks = 1
track 0:
format:
bitrate = 768000
bitrate = 1536000
id = null
containerMimeType = null
sampleMimeType = audio/flac
maxInputSize = -1
maxInputSize = 5776
width = -1
height = -1
frameRate = -1.0
......
......@@ -5,11 +5,11 @@ seekMap:
numberOfTracks = 1
track 0:
format:
bitrate = 768000
bitrate = 1536000
id = null
containerMimeType = null
sampleMimeType = audio/flac
maxInputSize = -1
maxInputSize = 5776
width = -1
height = -1
frameRate = -1.0
......
......@@ -5,11 +5,11 @@ seekMap:
numberOfTracks = 1
track 0:
format:
bitrate = 768000
bitrate = 1536000
id = null
containerMimeType = null
sampleMimeType = audio/flac
maxInputSize = -1
maxInputSize = 5776
width = -1
height = -1
frameRate = -1.0
......
......@@ -5,11 +5,11 @@ seekMap:
numberOfTracks = 1
track 0:
format:
bitrate = 768000
bitrate = 1536000
id = null
containerMimeType = null
sampleMimeType = audio/flac
maxInputSize = -1
maxInputSize = 5776
width = -1
height = -1
frameRate = -1.0
......
......@@ -5,11 +5,11 @@ seekMap:
numberOfTracks = 1
track 0:
format:
bitrate = 768000
bitrate = 1536000
id = null
containerMimeType = null
sampleMimeType = audio/flac
maxInputSize = -1
maxInputSize = 5776
width = -1
height = -1
frameRate = -1.0
......
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.exoplayer2.util;
import static com.google.common.truth.Truth.assertThat;
import android.util.Pair;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
/** Unit test for {@link CodecSpecificDataUtil}. */
@RunWith(AndroidJUnit4.class)
public class CodecSpecificDataUtilTest {
@Test
public void parseAlacAudioSpecificConfig() {
byte[] alacSpecificConfig =
new byte[] {
0, 0, 16, 0, // frameLength
0, // compatibleVersion
16, // bitDepth
40, 10, 14, // tuning parameters
2, // numChannels = 2
0, 0, // maxRun
0, 0, 64, 4, // maxFrameBytes
0, 46, -32, 0, // avgBitRate
0, 1, 119, 0, // sampleRate = 96000
};
Pair<Integer, Integer> sampleRateAndChannelCount =
CodecSpecificDataUtil.parseAlacAudioSpecificConfig(alacSpecificConfig);
assertThat(sampleRateAndChannelCount.first).isEqualTo(96000);
assertThat(sampleRateAndChannelCount.second).isEqualTo(2);
}
}
......@@ -1417,8 +1417,10 @@ public class DashManifestParser extends DefaultHandler
for (int i = 0; i < supplementalProperties.size(); i++) {
Descriptor descriptor = supplementalProperties.get(i);
String schemeIdUri = descriptor.schemeIdUri;
if ("tag:dolby.com,2014:dash:DolbyDigitalPlusExtensionType:2014".equals(schemeIdUri)
&& "ec+3".equals(descriptor.value)) {
if (("tag:dolby.com,2018:dash:EC3_ExtensionComplexityIndex:2018".equals(schemeIdUri)
&& "JOC".equals(descriptor.value))
|| ("tag:dolby.com,2014:dash:DolbyDigitalPlusExtensionType:2014".equals(schemeIdUri)
&& "ec+3".equals(descriptor.value))) {
return MimeTypes.AUDIO_E_AC3_JOC;
}
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment