Commit 1c6149b4 by ojw28 Committed by GitHub

Merge pull request #4054 from google/dev-v2-r2.7.2

r2.7.2
parents b6155d2e 976182cb
Showing with 323 additions and 91 deletions
# Release notes # # Release notes #
### 2.7.2 ###
* Gradle: Upgrade Gradle version from 4.1 to 4.4 so it can work with Android
Studio 3.1 ([#3708](https://github.com/google/ExoPlayer/issues/3708)).
* Match codecs starting with "mp4a" to different Audio MimeTypes
([#3779](https://github.com/google/ExoPlayer/issues/3779)).
* Fix ANR issue on Redmi 4X and Redmi Note 4
([#4006](https://github.com/google/ExoPlayer/issues/4006)).
* Fix handling of zero padded strings when parsing Matroska streams
([#4010](https://github.com/google/ExoPlayer/issues/4010)).
* Fix "Decoder input buffer too small" error when playing some FLAC streams.
* MediaSession extension: Omit fast forward and rewind actions when media is not
seekable ([#4001](https://github.com/google/ExoPlayer/issues/4001)).
### 2.7.1 ### ### 2.7.1 ###
* Gradle: Replaced 'compile' (deprecated) with 'implementation' and * Gradle: Replaced 'compile' (deprecated) with 'implementation' and
......
...@@ -17,8 +17,8 @@ buildscript { ...@@ -17,8 +17,8 @@ buildscript {
google() google()
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:3.0.1' classpath 'com.android.tools.build:gradle:3.1.0'
classpath 'com.novoda:bintray-release:0.5.0' classpath 'com.novoda:bintray-release:0.8.1'
} }
// Workaround for the following test coverage issue. Remove when fixed: // Workaround for the following test coverage issue. Remove when fixed:
// https://code.google.com/p/android/issues/detail?id=226070 // https://code.google.com/p/android/issues/detail?id=226070
......
...@@ -13,8 +13,8 @@ ...@@ -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.7.1' releaseVersion = '2.7.2'
releaseVersionCode = 2701 releaseVersionCode = 2702
// Important: ExoPlayer specifies a minSdkVersion of 14 because various // Important: ExoPlayer specifies a minSdkVersion of 14 because various
// components provided by the library may be of use on older devices. // components provided by the library may be of use on older devices.
// However, please note that the core media playback functionality provided // However, please note that the core media playback functionality provided
...@@ -22,7 +22,7 @@ project.ext { ...@@ -22,7 +22,7 @@ project.ext {
minSdkVersion = 14 minSdkVersion = 14
targetSdkVersion = 27 targetSdkVersion = 27
compileSdkVersion = 27 compileSdkVersion = 27
buildToolsVersion = '26.0.2' buildToolsVersion = '27.0.3'
testSupportLibraryVersion = '0.5' testSupportLibraryVersion = '0.5'
supportLibraryVersion = '27.0.0' supportLibraryVersion = '27.0.0'
playServicesLibraryVersion = '11.4.2' playServicesLibraryVersion = '11.4.2'
......
...@@ -29,7 +29,8 @@ EXOPLAYER_ROOT="$(pwd)" ...@@ -29,7 +29,8 @@ EXOPLAYER_ROOT="$(pwd)"
FFMPEG_EXT_PATH="${EXOPLAYER_ROOT}/extensions/ffmpeg/src/main" FFMPEG_EXT_PATH="${EXOPLAYER_ROOT}/extensions/ffmpeg/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.
Only versions up to NDK 15c are supported currently.
``` ```
NDK_PATH="<path to Android NDK>" NDK_PATH="<path to Android NDK>"
......
...@@ -9,7 +9,7 @@ track 0: ...@@ -9,7 +9,7 @@ track 0:
id = null id = null
containerMimeType = null containerMimeType = null
sampleMimeType = audio/raw sampleMimeType = audio/raw
maxInputSize = -1 maxInputSize = 16384
width = -1 width = -1
height = -1 height = -1
frameRate = -1.0 frameRate = -1.0
......
...@@ -9,7 +9,7 @@ track 0: ...@@ -9,7 +9,7 @@ track 0:
id = null id = null
containerMimeType = null containerMimeType = null
sampleMimeType = audio/raw sampleMimeType = audio/raw
maxInputSize = -1 maxInputSize = 16384
width = -1 width = -1
height = -1 height = -1
frameRate = -1.0 frameRate = -1.0
......
...@@ -9,7 +9,7 @@ track 0: ...@@ -9,7 +9,7 @@ track 0:
id = null id = null
containerMimeType = null containerMimeType = null
sampleMimeType = audio/raw sampleMimeType = audio/raw
maxInputSize = -1 maxInputSize = 16384
width = -1 width = -1
height = -1 height = -1
frameRate = -1.0 frameRate = -1.0
......
...@@ -9,7 +9,7 @@ track 0: ...@@ -9,7 +9,7 @@ track 0:
id = null id = null
containerMimeType = null containerMimeType = null
sampleMimeType = audio/raw sampleMimeType = audio/raw
maxInputSize = -1 maxInputSize = 16384
width = -1 width = -1
height = -1 height = -1
frameRate = -1.0 frameRate = -1.0
......
...@@ -116,7 +116,7 @@ public final class FlacExtractor implements Extractor { ...@@ -116,7 +116,7 @@ public final class FlacExtractor implements Extractor {
MimeTypes.AUDIO_RAW, MimeTypes.AUDIO_RAW,
null, null,
streamInfo.bitRate(), streamInfo.bitRate(),
Format.NO_VALUE, streamInfo.maxDecodedFrameSize(),
streamInfo.channels, streamInfo.channels,
streamInfo.sampleRate, streamInfo.sampleRate,
getPcmEncoding(streamInfo.bitsPerSample), getPcmEncoding(streamInfo.bitsPerSample),
......
...@@ -77,11 +77,10 @@ public class DefaultPlaybackController implements MediaSessionConnector.Playback ...@@ -77,11 +77,10 @@ public class DefaultPlaybackController implements MediaSessionConnector.Playback
public long getSupportedPlaybackActions(Player player) { public long getSupportedPlaybackActions(Player player) {
if (player == null || player.getCurrentTimeline().isEmpty()) { if (player == null || player.getCurrentTimeline().isEmpty()) {
return 0; return 0;
} else if (!player.isCurrentWindowSeekable()) {
return BASE_ACTIONS;
} }
long actions = BASE_ACTIONS; long actions = BASE_ACTIONS | PlaybackStateCompat.ACTION_SEEK_TO;
if (player.isCurrentWindowSeekable()) {
actions |= PlaybackStateCompat.ACTION_SEEK_TO;
}
if (fastForwardIncrementMs > 0) { if (fastForwardIncrementMs > 0) {
actions |= PlaybackStateCompat.ACTION_FAST_FORWARD; actions |= PlaybackStateCompat.ACTION_FAST_FORWARD;
} }
......
...@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME ...@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip
...@@ -11,6 +11,8 @@ ...@@ -11,6 +11,8 @@
// 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.
apply from: "${rootDir}/javadoc_util.gradle"
class CombinedJavadocPlugin implements Plugin<Project> { class CombinedJavadocPlugin implements Plugin<Project> {
static final String TASK_NAME = "generateCombinedJavadoc" static final String TASK_NAME = "generateCombinedJavadoc"
...@@ -20,22 +22,32 @@ class CombinedJavadocPlugin implements Plugin<Project> { ...@@ -20,22 +22,32 @@ class CombinedJavadocPlugin implements Plugin<Project> {
project.gradle.projectsEvaluated { project.gradle.projectsEvaluated {
Set<Project> libraryModules = getLibraryModules(project) Set<Project> libraryModules = getLibraryModules(project)
if (!libraryModules.isEmpty()) { if (!libraryModules.isEmpty()) {
String sdkDirectory = getSdkDirectory(libraryModules)
project.task(TASK_NAME, type: Javadoc) { project.task(TASK_NAME, type: Javadoc) {
description = "Generates combined Javadoc." description = "Generates combined Javadoc."
title = "ExoPlayer library" title = "ExoPlayer library"
source = libraryModules.generateJavadoc.source source = libraryModules.generateJavadoc.source
classpath = project.files(libraryModules.generateJavadoc.classpath) classpath = project.files([])
destinationDir = project.file("$project.buildDir/docs/javadoc") destinationDir = project.file("$project.buildDir/docs/javadoc")
options { options {
links "http://docs.oracle.com/javase/7/docs/api/" links "https://docs.oracle.com/javase/7/docs/api/",
linksOffline "https://developer.android.com/reference", "https://developer.android.com/reference"
"${sdkDirectory}/docs/reference"
encoding = "UTF-8" encoding = "UTF-8"
} }
exclude "**/BuildConfig.java" exclude "**/BuildConfig.java"
exclude "**/R.java" exclude "**/R.java"
destinationDir project.file("$project.buildDir/docs/javadoc") doFirst {
libraryModules.each { libraryModule ->
libraryModule.android.libraryVariants.all { variant ->
def name = variant.buildType.name
if (name.equals("release")) {
classpath +=
libraryModule.project.files(
variant.javaCompile.classpath.files,
libraryModule.project.android.getBootClasspath())
}
}
}
}
doLast { doLast {
libraryModules.each { libraryModule -> libraryModules.each { libraryModule ->
project.copy { project.copy {
...@@ -43,6 +55,7 @@ class CombinedJavadocPlugin implements Plugin<Project> { ...@@ -43,6 +55,7 @@ class CombinedJavadocPlugin implements Plugin<Project> {
into "${project.buildDir}/docs/javadoc" into "${project.buildDir}/docs/javadoc"
} }
} }
project.fixJavadoc()
} }
} }
} }
...@@ -57,12 +70,6 @@ class CombinedJavadocPlugin implements Plugin<Project> { ...@@ -57,12 +70,6 @@ class CombinedJavadocPlugin implements Plugin<Project> {
} }
} }
// Returns the Android SDK directory given a set of Android library modules.
private String getSdkDirectory(Set<Project> libraryModules) {
// We can retrieve the Android SDK directory from any module.
return libraryModules.iterator().next().android.sdkDirectory
}
} }
apply plugin: CombinedJavadocPlugin apply plugin: CombinedJavadocPlugin
...@@ -11,6 +11,8 @@ ...@@ -11,6 +11,8 @@
// 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.
apply from: "${rootDir}/javadoc_util.gradle"
android.libraryVariants.all { variant -> android.libraryVariants.all { variant ->
def name = variant.buildType.name def name = variant.buildType.name
if (!name.equals("release")) { if (!name.equals("release")) {
...@@ -20,21 +22,26 @@ android.libraryVariants.all { variant -> ...@@ -20,21 +22,26 @@ android.libraryVariants.all { variant ->
description = "Generates Javadoc for the ${javadocTitle}." description = "Generates Javadoc for the ${javadocTitle}."
title = "ExoPlayer ${javadocTitle}" title = "ExoPlayer ${javadocTitle}"
source = variant.javaCompile.source source = variant.javaCompile.source
classpath = files(variant.javaCompile.classpath.files,
project.android.getBootClasspath())
options { options {
links "http://docs.oracle.com/javase/7/docs/api/" links "http://docs.oracle.com/javase/7/docs/api/"
linksOffline "https://developer.android.com/reference", linksOffline "https://developer.android.com/reference",
"${android.sdkDirectory}/docs/reference" "${android.sdkDirectory}/docs/reference"
encoding = "UTF-8" encoding = "UTF-8"
} }
exclude "**/BuildConfig.java" exclude "**/BuildConfig.java"
exclude "**/R.java" exclude "**/R.java"
doFirst {
classpath =
files(
variant.javaCompile.classpath.files,
project.android.getBootClasspath())
}
doLast { doLast {
copy { copy {
from "src/main/javadoc" from "src/main/javadoc"
into "$buildDir/docs/javadoc" into "$buildDir/docs/javadoc"
} }
project.fixJavadoc()
} }
} }
} }
// Copyright (C) 2018 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.
ext.fixJavadoc = {
def javadocPath = "${project.buildDir}/docs/javadoc"
// Fix external Android links to target the top frame.
def androidRoot = "https://developer.android.com/reference/"
def androidLink = "<a href=\"(${androidRoot}.*?)\\?is-external=true\""
def androidFixed = "<a href=\"\\1\" target=\"_top\""
ant.replaceregexp(match:androidLink, replace:androidFixed, flags:'g') {
fileset(dir: "${javadocPath}", includes: "**/*.html")
}
// Fix external Oracle links to use frames and target the top frame.
def oracleRoot = "https://docs.oracle.com/javase/7/docs/api/"
def oracleLink = "<a href=\"(${oracleRoot})(.*?)\\?is-external=true\""
def oracleFixed = "<a href=\"\\1index.html\\?\\2\" target=\"_top\""
ant.replaceregexp(match:oracleLink, replace:oracleFixed, flags:'g') {
fileset(dir: "${javadocPath}", includes: "**/*.html")
}
// Remove date metadata that changes every time Javadoc is generated.
def javadocGeneratedBy = "<!-- Generated by javadoc.*?-->\n"
ant.replaceregexp(match:javadocGeneratedBy, replace:"") {
fileset(dir: "${javadocPath}", includes: "**/*.html")
}
def dateMeta = "<meta name=\"date\".*?>\n"
ant.replaceregexp(match:dateMeta, replace:"") {
fileset(dir: "${javadocPath}", includes: "**/*.html")
}
}
...@@ -29,11 +29,11 @@ public final class ExoPlayerLibraryInfo { ...@@ -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.7.1"; public static final String VERSION = "2.7.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.7.1"; public static final String VERSION_SLASHY = "ExoPlayerLib/2.7.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.
...@@ -43,7 +43,7 @@ public final class ExoPlayerLibraryInfo { ...@@ -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 = 2007001; public static final int VERSION_INT = 2007002;
/** /**
* 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}
......
...@@ -202,10 +202,11 @@ import java.util.Stack; ...@@ -202,10 +202,11 @@ import java.util.Stack;
} }
/** /**
* Reads and returns a string of length {@code byteLength} from the {@link ExtractorInput}. * Reads a string of length {@code byteLength} from the {@link ExtractorInput}. Zero padding is
* removed, so the returned string may be shorter than {@code byteLength}.
* *
* @param input The {@link ExtractorInput} from which to read. * @param input The {@link ExtractorInput} from which to read.
* @param byteLength The length of the float being read. * @param byteLength The length of the string being read, including zero padding.
* @return The read string value. * @return The read string value.
* @throws IOException If an error occurs reading from the input. * @throws IOException If an error occurs reading from the input.
* @throws InterruptedException If the thread is interrupted. * @throws InterruptedException If the thread is interrupted.
...@@ -217,7 +218,12 @@ import java.util.Stack; ...@@ -217,7 +218,12 @@ import java.util.Stack;
} }
byte[] stringBytes = new byte[byteLength]; byte[] stringBytes = new byte[byteLength];
input.readFully(stringBytes, 0, byteLength); input.readFully(stringBytes, 0, byteLength);
return new String(stringBytes); // Remove zero padding.
int trimmedLength = byteLength;
while (trimmedLength > 0 && stringBytes[trimmedLength - 1] == 0) {
trimmedLength--;
}
return new String(stringBytes, 0, trimmedLength);
} }
/** /**
......
...@@ -15,6 +15,8 @@ ...@@ -15,6 +15,8 @@
*/ */
package com.google.android.exoplayer2.extractor.mp4; package com.google.android.exoplayer2.extractor.mp4;
import static com.google.android.exoplayer2.util.MimeTypes.getMimeTypeFromMp4ObjectType;
import android.util.Log; import android.util.Log;
import android.util.Pair; import android.util.Pair;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
...@@ -1030,47 +1032,11 @@ import java.util.List; ...@@ -1030,47 +1032,11 @@ import java.util.List;
// Set the MIME type based on the object type indication (14496-1 table 5). // Set the MIME type based on the object type indication (14496-1 table 5).
int objectTypeIndication = parent.readUnsignedByte(); int objectTypeIndication = parent.readUnsignedByte();
String mimeType; String mimeType = getMimeTypeFromMp4ObjectType(objectTypeIndication);
switch (objectTypeIndication) { if (MimeTypes.AUDIO_MPEG.equals(mimeType)
case 0x60: || MimeTypes.AUDIO_DTS.equals(mimeType)
case 0x61: || MimeTypes.AUDIO_DTS_HD.equals(mimeType)) {
mimeType = MimeTypes.VIDEO_MPEG2; return Pair.create(mimeType, null);
break;
case 0x20:
mimeType = MimeTypes.VIDEO_MP4V;
break;
case 0x21:
mimeType = MimeTypes.VIDEO_H264;
break;
case 0x23:
mimeType = MimeTypes.VIDEO_H265;
break;
case 0x6B:
mimeType = MimeTypes.AUDIO_MPEG;
return Pair.create(mimeType, null);
case 0x40:
case 0x66:
case 0x67:
case 0x68:
mimeType = MimeTypes.AUDIO_AAC;
break;
case 0xA5:
mimeType = MimeTypes.AUDIO_AC3;
break;
case 0xA6:
mimeType = MimeTypes.AUDIO_E_AC3;
break;
case 0xA9:
case 0xAC:
mimeType = MimeTypes.AUDIO_DTS;
return Pair.create(mimeType, null);
case 0xAA:
case 0xAB:
mimeType = MimeTypes.AUDIO_DTS_HD;
return Pair.create(mimeType, null);
default:
mimeType = null;
break;
} }
parent.skipBytes(12); parent.skipBytes(12);
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
*/ */
package com.google.android.exoplayer2.util; package com.google.android.exoplayer2.util;
import android.support.annotation.Nullable;
import android.text.TextUtils; import android.text.TextUtils;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
...@@ -194,7 +195,20 @@ public final class MimeTypes { ...@@ -194,7 +195,20 @@ public final class MimeTypes {
} else if (codec.startsWith("vp8") || codec.startsWith("vp08")) { } else if (codec.startsWith("vp8") || codec.startsWith("vp08")) {
return MimeTypes.VIDEO_VP8; return MimeTypes.VIDEO_VP8;
} else if (codec.startsWith("mp4a")) { } else if (codec.startsWith("mp4a")) {
return MimeTypes.AUDIO_AAC; String mimeType = null;
if (codec.startsWith("mp4a.")) {
String objectTypeString = codec.substring(5); // remove the 'mp4a.' prefix
if (objectTypeString.length() >= 2) {
try {
String objectTypeHexString = Util.toUpperInvariant(objectTypeString.substring(0, 2));
int objectTypeInt = Integer.parseInt(objectTypeHexString, 16);
mimeType = getMimeTypeFromMp4ObjectType(objectTypeInt);
} catch (NumberFormatException ignored) {
// ignored
}
}
}
return mimeType == null ? MimeTypes.AUDIO_AAC : mimeType;
} else if (codec.startsWith("ac-3") || codec.startsWith("dac3")) { } else if (codec.startsWith("ac-3") || codec.startsWith("dac3")) {
return MimeTypes.AUDIO_AC3; return MimeTypes.AUDIO_AC3;
} else if (codec.startsWith("ec-3") || codec.startsWith("dec3")) { } else if (codec.startsWith("ec-3") || codec.startsWith("dec3")) {
...@@ -214,6 +228,50 @@ public final class MimeTypes { ...@@ -214,6 +228,50 @@ public final class MimeTypes {
} }
/** /**
* Derives a mimeType from MP4 object type identifier, as defined in RFC 6381 and
* http://www.mp4ra.org/object.html.
*
* @param objectType The objectType identifier to derive.
* @return The mimeType, or null if it could not be derived.
*/
@Nullable
public static String getMimeTypeFromMp4ObjectType(int objectType) {
switch (objectType) {
case 0x60:
case 0x61:
return MimeTypes.VIDEO_MPEG2;
case 0x20:
return MimeTypes.VIDEO_MP4V;
case 0x21:
return MimeTypes.VIDEO_H264;
case 0x23:
return MimeTypes.VIDEO_H265;
case 0x69:
case 0x6B:
return MimeTypes.AUDIO_MPEG;
case 0x40:
case 0x66:
case 0x67:
case 0x68:
return MimeTypes.AUDIO_AAC;
case 0xA5:
return MimeTypes.AUDIO_AC3;
case 0xA6:
return MimeTypes.AUDIO_E_AC3;
case 0xA9:
case 0xAC:
return MimeTypes.AUDIO_DTS;
case 0xAA:
case 0xAB:
return MimeTypes.AUDIO_DTS_HD;
case 0xAD:
return MimeTypes.AUDIO_OPUS;
default:
return null;
}
}
/**
* Returns the {@link C}{@code .TRACK_TYPE_*} constant that corresponds to a specified MIME type. * Returns the {@link C}{@code .TRACK_TYPE_*} constant that corresponds to a specified MIME type.
* {@link C#TRACK_TYPE_UNKNOWN} if the MIME type is not known or the mapping cannot be * {@link C#TRACK_TYPE_UNKNOWN} if the MIME type is not known or the mapping cannot be
* established. * established.
......
...@@ -307,6 +307,16 @@ public final class Util { ...@@ -307,6 +307,16 @@ public final class Util {
} }
/** /**
* Converts text to upper case using {@link Locale#US}.
*
* @param text The text to convert.
* @return The upper case text, or null if {@code text} is null.
*/
public static String toUpperInvariant(String text) {
return text == null ? null : text.toUpperCase(Locale.US);
}
/**
* Divides a {@code numerator} by a {@code denominator}, returning the ceiled result. * Divides a {@code numerator} by a {@code denominator}, returning the ceiled result.
* *
* @param numerator The numerator to divide. * @param numerator The numerator to divide.
......
...@@ -1102,9 +1102,13 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { ...@@ -1102,9 +1102,13 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
// Work around https://github.com/google/ExoPlayer/issues/3236, // Work around https://github.com/google/ExoPlayer/issues/3236,
// https://github.com/google/ExoPlayer/issues/3355, // https://github.com/google/ExoPlayer/issues/3355,
// https://github.com/google/ExoPlayer/issues/3439, // https://github.com/google/ExoPlayer/issues/3439,
// https://github.com/google/ExoPlayer/issues/3724 and // https://github.com/google/ExoPlayer/issues/3724,
// https://github.com/google/ExoPlayer/issues/3835. // https://github.com/google/ExoPlayer/issues/3835 and
return (("deb".equals(Util.DEVICE) || "flo".equals(Util.DEVICE)) // Nexus 7 (2013) // https://github.com/google/ExoPlayer/issues/4006.
return (("deb".equals(Util.DEVICE) // Nexus 7 (2013)
|| "flo".equals(Util.DEVICE) // Nexus 7 (2013)
|| "mido".equals(Util.DEVICE) // Redmi Note 4
|| "santoni".equals(Util.DEVICE)) // Redmi 4X
&& "OMX.qcom.video.decoder.avc".equals(name)) && "OMX.qcom.video.decoder.avc".equals(name))
|| (("tcl_eu".equals(Util.DEVICE) // TCL Percee TV || (("tcl_eu".equals(Util.DEVICE) // TCL Percee TV
|| "SVP-DTV15".equals(Util.DEVICE) // Sony Bravia 4K 2015 || "SVP-DTV15".equals(Util.DEVICE) // Sony Bravia 4K 2015
......
...@@ -90,6 +90,14 @@ public class DefaultEbmlReaderTest { ...@@ -90,6 +90,14 @@ public class DefaultEbmlReaderTest {
} }
@Test @Test
public void testStringElementWithZeroPadding() throws IOException, InterruptedException {
ExtractorInput input = createTestInput(0x42, 0x82, 0x86, 0x41, 0x62, 0x63, 0x00, 0x00, 0x00);
TestOutput expected = new TestOutput();
expected.stringElement(TestOutput.ID_DOC_TYPE, "Abc");
assertEvents(input, expected.events);
}
@Test
public void testStringElementEmpty() throws IOException, InterruptedException { public void testStringElementEmpty() throws IOException, InterruptedException {
ExtractorInput input = createTestInput(0x42, 0x82, 0x80); ExtractorInput input = createTestInput(0x42, 0x82, 0x80);
TestOutput expected = new TestOutput(); TestOutput expected = new TestOutput();
......
/*
* Copyright (C) 2018 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 org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
/** Unit test for {@link MimeTypes}. */
@RunWith(RobolectricTestRunner.class)
public final class MimeTypesTest {
@Test
public void testGetMediaMimeType_fromValidCodecs_returnsCorrectMimeType() {
assertThat(MimeTypes.getMediaMimeType("avc1")).isEqualTo(MimeTypes.VIDEO_H264);
assertThat(MimeTypes.getMediaMimeType("avc1.42E01E")).isEqualTo(MimeTypes.VIDEO_H264);
assertThat(MimeTypes.getMediaMimeType("avc1.42E01F")).isEqualTo(MimeTypes.VIDEO_H264);
assertThat(MimeTypes.getMediaMimeType("avc1.4D401F")).isEqualTo(MimeTypes.VIDEO_H264);
assertThat(MimeTypes.getMediaMimeType("avc1.4D4028")).isEqualTo(MimeTypes.VIDEO_H264);
assertThat(MimeTypes.getMediaMimeType("avc1.640028")).isEqualTo(MimeTypes.VIDEO_H264);
assertThat(MimeTypes.getMediaMimeType("avc1.640029")).isEqualTo(MimeTypes.VIDEO_H264);
assertThat(MimeTypes.getMediaMimeType("avc3")).isEqualTo(MimeTypes.VIDEO_H264);
assertThat(MimeTypes.getMediaMimeType("hev1")).isEqualTo(MimeTypes.VIDEO_H265);
assertThat(MimeTypes.getMediaMimeType("hvc1")).isEqualTo(MimeTypes.VIDEO_H265);
assertThat(MimeTypes.getMediaMimeType("vp08")).isEqualTo(MimeTypes.VIDEO_VP8);
assertThat(MimeTypes.getMediaMimeType("vp8")).isEqualTo(MimeTypes.VIDEO_VP8);
assertThat(MimeTypes.getMediaMimeType("vp09")).isEqualTo(MimeTypes.VIDEO_VP9);
assertThat(MimeTypes.getMediaMimeType("vp9")).isEqualTo(MimeTypes.VIDEO_VP9);
assertThat(MimeTypes.getMediaMimeType("ac-3")).isEqualTo(MimeTypes.AUDIO_AC3);
assertThat(MimeTypes.getMediaMimeType("dac3")).isEqualTo(MimeTypes.AUDIO_AC3);
assertThat(MimeTypes.getMediaMimeType("dec3")).isEqualTo(MimeTypes.AUDIO_E_AC3);
assertThat(MimeTypes.getMediaMimeType("ec-3")).isEqualTo(MimeTypes.AUDIO_E_AC3);
assertThat(MimeTypes.getMediaMimeType("ec+3")).isEqualTo(MimeTypes.AUDIO_E_AC3_JOC);
assertThat(MimeTypes.getMediaMimeType("dtsc")).isEqualTo(MimeTypes.AUDIO_DTS);
assertThat(MimeTypes.getMediaMimeType("dtse")).isEqualTo(MimeTypes.AUDIO_DTS);
assertThat(MimeTypes.getMediaMimeType("dtsh")).isEqualTo(MimeTypes.AUDIO_DTS_HD);
assertThat(MimeTypes.getMediaMimeType("dtsl")).isEqualTo(MimeTypes.AUDIO_DTS_HD);
assertThat(MimeTypes.getMediaMimeType("opus")).isEqualTo(MimeTypes.AUDIO_OPUS);
assertThat(MimeTypes.getMediaMimeType("vorbis")).isEqualTo(MimeTypes.AUDIO_VORBIS);
assertThat(MimeTypes.getMediaMimeType("mp4a")).isEqualTo(MimeTypes.AUDIO_AAC);
assertThat(MimeTypes.getMediaMimeType("mp4a.40.02")).isEqualTo(MimeTypes.AUDIO_AAC);
assertThat(MimeTypes.getMediaMimeType("mp4a.40.05")).isEqualTo(MimeTypes.AUDIO_AAC);
assertThat(MimeTypes.getMediaMimeType("mp4a.40.2")).isEqualTo(MimeTypes.AUDIO_AAC);
assertThat(MimeTypes.getMediaMimeType("mp4a.40.5")).isEqualTo(MimeTypes.AUDIO_AAC);
assertThat(MimeTypes.getMediaMimeType("mp4a.40.29")).isEqualTo(MimeTypes.AUDIO_AAC);
assertThat(MimeTypes.getMediaMimeType("mp4a.66")).isEqualTo(MimeTypes.AUDIO_AAC);
assertThat(MimeTypes.getMediaMimeType("mp4a.67")).isEqualTo(MimeTypes.AUDIO_AAC);
assertThat(MimeTypes.getMediaMimeType("mp4a.68")).isEqualTo(MimeTypes.AUDIO_AAC);
assertThat(MimeTypes.getMediaMimeType("mp4a.69")).isEqualTo(MimeTypes.AUDIO_MPEG);
assertThat(MimeTypes.getMediaMimeType("mp4a.6B")).isEqualTo(MimeTypes.AUDIO_MPEG);
assertThat(MimeTypes.getMediaMimeType("mp4a.a5")).isEqualTo(MimeTypes.AUDIO_AC3);
assertThat(MimeTypes.getMediaMimeType("mp4a.A5")).isEqualTo(MimeTypes.AUDIO_AC3);
assertThat(MimeTypes.getMediaMimeType("mp4a.a6")).isEqualTo(MimeTypes.AUDIO_E_AC3);
assertThat(MimeTypes.getMediaMimeType("mp4a.A6")).isEqualTo(MimeTypes.AUDIO_E_AC3);
assertThat(MimeTypes.getMediaMimeType("mp4a.A9")).isEqualTo(MimeTypes.AUDIO_DTS);
assertThat(MimeTypes.getMediaMimeType("mp4a.AC")).isEqualTo(MimeTypes.AUDIO_DTS);
assertThat(MimeTypes.getMediaMimeType("mp4a.AA")).isEqualTo(MimeTypes.AUDIO_DTS_HD);
assertThat(MimeTypes.getMediaMimeType("mp4a.AB")).isEqualTo(MimeTypes.AUDIO_DTS_HD);
assertThat(MimeTypes.getMediaMimeType("mp4a.AD")).isEqualTo(MimeTypes.AUDIO_OPUS);
}
@Test
public void testGetMimeTypeFromMp4ObjectType_forValidObjectType_returnsCorrectMimeType() {
assertThat(MimeTypes.getMimeTypeFromMp4ObjectType(0x60)).isEqualTo(MimeTypes.VIDEO_MPEG2);
assertThat(MimeTypes.getMimeTypeFromMp4ObjectType(0x61)).isEqualTo(MimeTypes.VIDEO_MPEG2);
assertThat(MimeTypes.getMimeTypeFromMp4ObjectType(0x20)).isEqualTo(MimeTypes.VIDEO_MP4V);
assertThat(MimeTypes.getMimeTypeFromMp4ObjectType(0x21)).isEqualTo(MimeTypes.VIDEO_H264);
assertThat(MimeTypes.getMimeTypeFromMp4ObjectType(0x23)).isEqualTo(MimeTypes.VIDEO_H265);
assertThat(MimeTypes.getMimeTypeFromMp4ObjectType(0x6B)).isEqualTo(MimeTypes.AUDIO_MPEG);
assertThat(MimeTypes.getMimeTypeFromMp4ObjectType(0x40)).isEqualTo(MimeTypes.AUDIO_AAC);
assertThat(MimeTypes.getMimeTypeFromMp4ObjectType(0x66)).isEqualTo(MimeTypes.AUDIO_AAC);
assertThat(MimeTypes.getMimeTypeFromMp4ObjectType(0x67)).isEqualTo(MimeTypes.AUDIO_AAC);
assertThat(MimeTypes.getMimeTypeFromMp4ObjectType(0x68)).isEqualTo(MimeTypes.AUDIO_AAC);
assertThat(MimeTypes.getMimeTypeFromMp4ObjectType(0xA5)).isEqualTo(MimeTypes.AUDIO_AC3);
assertThat(MimeTypes.getMimeTypeFromMp4ObjectType(0xA6)).isEqualTo(MimeTypes.AUDIO_E_AC3);
assertThat(MimeTypes.getMimeTypeFromMp4ObjectType(0xA9)).isEqualTo(MimeTypes.AUDIO_DTS);
assertThat(MimeTypes.getMimeTypeFromMp4ObjectType(0xAC)).isEqualTo(MimeTypes.AUDIO_DTS);
assertThat(MimeTypes.getMimeTypeFromMp4ObjectType(0xAA)).isEqualTo(MimeTypes.AUDIO_DTS_HD);
assertThat(MimeTypes.getMimeTypeFromMp4ObjectType(0xAB)).isEqualTo(MimeTypes.AUDIO_DTS_HD);
assertThat(MimeTypes.getMimeTypeFromMp4ObjectType(0xAD)).isEqualTo(MimeTypes.AUDIO_OPUS);
}
@Test
public void testGetMimeTypeFromMp4ObjectType_forInvalidObjectType_returnsNull() {
assertThat(MimeTypes.getMimeTypeFromMp4ObjectType(0)).isNull();
assertThat(MimeTypes.getMimeTypeFromMp4ObjectType(0x600)).isNull();
assertThat(MimeTypes.getMimeTypeFromMp4ObjectType(0x01)).isNull();
assertThat(MimeTypes.getMimeTypeFromMp4ObjectType(-1)).isNull();
}
}
...@@ -117,7 +117,8 @@ public final class DashDownloader extends SegmentDownloader<DashManifest, Repres ...@@ -117,7 +117,8 @@ public final class DashDownloader extends SegmentDownloader<DashManifest, Repres
} }
} }
int segmentCount = index.getSegmentCount(C.TIME_UNSET); long periodDurationUs = manifest.getPeriodDurationUs(key.periodIndex);
int segmentCount = index.getSegmentCount(periodDurationUs);
if (segmentCount == DashSegmentIndex.INDEX_UNBOUNDED) { if (segmentCount == DashSegmentIndex.INDEX_UNBOUNDED) {
throw new DownloadException("Unbounded index for representation: " + key); throw new DownloadException("Unbounded index for representation: " + key);
} }
......
...@@ -52,6 +52,9 @@ public final class DefaultHlsExtractorFactory implements HlsExtractorFactory { ...@@ -52,6 +52,9 @@ public final class DefaultHlsExtractorFactory implements HlsExtractorFactory {
Format format, List<Format> muxedCaptionFormats, DrmInitData drmInitData, Format format, List<Format> muxedCaptionFormats, DrmInitData drmInitData,
TimestampAdjuster timestampAdjuster) { TimestampAdjuster timestampAdjuster) {
String lastPathSegment = uri.getLastPathSegment(); String lastPathSegment = uri.getLastPathSegment();
if (lastPathSegment == null) {
lastPathSegment = "";
}
boolean isPackedAudioExtractor = false; boolean isPackedAudioExtractor = false;
Extractor extractor; Extractor extractor;
if (MimeTypes.TEXT_VTT.equals(format.sampleMimeType) if (MimeTypes.TEXT_VTT.equals(format.sampleMimeType)
......
...@@ -20,12 +20,12 @@ import static com.google.common.truth.Truth.assertThat; ...@@ -20,12 +20,12 @@ import static com.google.common.truth.Truth.assertThat;
import android.net.Uri; import android.net.Uri;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.source.hls.playlist.HlsMediaPlaylist.Segment; import com.google.android.exoplayer2.source.hls.playlist.HlsMediaPlaylist.Segment;
import com.google.android.exoplayer2.util.Util;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.util.List; import java.util.List;
import java.util.Locale;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner; import org.robolectric.RobolectricTestRunner;
...@@ -122,7 +122,7 @@ public class HlsMediaPlaylistParserTest { ...@@ -122,7 +122,7 @@ public class HlsMediaPlaylistParserTest {
.isEqualTo("https://priv.example.com/key.php?r=2682"); .isEqualTo("https://priv.example.com/key.php?r=2682");
// 0xA7A == 2682. // 0xA7A == 2682.
assertThat(segment.encryptionIV).isNotNull(); assertThat(segment.encryptionIV).isNotNull();
assertThat(segment.encryptionIV.toUpperCase(Locale.getDefault())).isEqualTo("A7A"); assertThat(Util.toUpperInvariant(segment.encryptionIV)).isEqualTo("A7A");
assertThat(segment.byterangeLength).isEqualTo(51740); assertThat(segment.byterangeLength).isEqualTo(51740);
assertThat(segment.byterangeOffset).isEqualTo(2147586650L); assertThat(segment.byterangeOffset).isEqualTo(2147586650L);
assertThat(segment.url).isEqualTo("https://priv.example.com/fileSequence2682.ts"); assertThat(segment.url).isEqualTo("https://priv.example.com/fileSequence2682.ts");
...@@ -134,7 +134,7 @@ public class HlsMediaPlaylistParserTest { ...@@ -134,7 +134,7 @@ public class HlsMediaPlaylistParserTest {
.isEqualTo("https://priv.example.com/key.php?r=2682"); .isEqualTo("https://priv.example.com/key.php?r=2682");
// 0xA7B == 2683. // 0xA7B == 2683.
assertThat(segment.encryptionIV).isNotNull(); assertThat(segment.encryptionIV).isNotNull();
assertThat(segment.encryptionIV.toUpperCase(Locale.getDefault())).isEqualTo("A7B"); assertThat(Util.toUpperInvariant(segment.encryptionIV)).isEqualTo("A7B");
assertThat(segment.byterangeLength).isEqualTo(C.LENGTH_UNSET); assertThat(segment.byterangeLength).isEqualTo(C.LENGTH_UNSET);
assertThat(segment.byterangeOffset).isEqualTo(0); assertThat(segment.byterangeOffset).isEqualTo(0);
assertThat(segment.url).isEqualTo("https://priv.example.com/fileSequence2683.ts"); assertThat(segment.url).isEqualTo("https://priv.example.com/fileSequence2683.ts");
......
...@@ -250,7 +250,7 @@ public class DefaultTimeBar extends View implements TimeBar { ...@@ -250,7 +250,7 @@ public class DefaultTimeBar extends View implements TimeBar {
try { try {
scrubberDrawable = a.getDrawable(R.styleable.DefaultTimeBar_scrubber_drawable); scrubberDrawable = a.getDrawable(R.styleable.DefaultTimeBar_scrubber_drawable);
if (scrubberDrawable != null) { if (scrubberDrawable != null) {
setDrawableLayoutDirection(scrubberDrawable, getLayoutDirection()); setDrawableLayoutDirection(scrubberDrawable);
defaultTouchTargetHeight = defaultTouchTargetHeight =
Math.max(scrubberDrawable.getMinimumHeight(), defaultTouchTargetHeight); Math.max(scrubberDrawable.getMinimumHeight(), defaultTouchTargetHeight);
} }
...@@ -747,8 +747,8 @@ public class DefaultTimeBar extends View implements TimeBar { ...@@ -747,8 +747,8 @@ public class DefaultTimeBar extends View implements TimeBar {
return true; return true;
} }
private static int dpToPx(DisplayMetrics displayMetrics, int dps) { private boolean setDrawableLayoutDirection(Drawable drawable) {
return (int) (dps * displayMetrics.density + 0.5f); return Util.SDK_INT >= 23 && setDrawableLayoutDirection(drawable, getLayoutDirection());
} }
private static boolean setDrawableLayoutDirection(Drawable drawable, int layoutDirection) { private static boolean setDrawableLayoutDirection(Drawable drawable, int layoutDirection) {
...@@ -771,4 +771,7 @@ public class DefaultTimeBar extends View implements TimeBar { ...@@ -771,4 +771,7 @@ public class DefaultTimeBar extends View implements TimeBar {
return 0x33000000 | (adMarkerColor & 0x00FFFFFF); return 0x33000000 | (adMarkerColor & 0x00FFFFFF);
} }
private static int dpToPx(DisplayMetrics displayMetrics, int dps) {
return (int) (dps * displayMetrics.density + 0.5f);
}
} }
...@@ -16,8 +16,8 @@ if (project.ext.has("exoplayerPublishEnabled") ...@@ -16,8 +16,8 @@ if (project.ext.has("exoplayerPublishEnabled")
apply plugin: 'bintray-release' apply plugin: 'bintray-release'
publish { publish {
artifactId = releaseArtifact artifactId = releaseArtifact
description = releaseDescription desc = releaseDescription
version = releaseVersion publishVersion = releaseVersion
repoName = getBintrayRepo() repoName = getBintrayRepo()
userOrg = 'google' userOrg = 'google'
groupId = 'com.google.android.exoplayer' groupId = 'com.google.android.exoplayer'
......
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