diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 17e5b19bca..3148cf0779 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -1,5 +1,19 @@ # 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 ### * Gradle: Replaced 'compile' (deprecated) with 'implementation' and diff --git a/build.gradle b/build.gradle index 9f9081a945..3813a241e0 100644 --- a/build.gradle +++ b/build.gradle @@ -17,8 +17,8 @@ buildscript { google() } dependencies { - classpath 'com.android.tools.build:gradle:3.0.1' - classpath 'com.novoda:bintray-release:0.5.0' + classpath 'com.android.tools.build:gradle:3.1.0' + classpath 'com.novoda:bintray-release:0.8.1' } // Workaround for the following test coverage issue. Remove when fixed: // https://code.google.com/p/android/issues/detail?id=226070 diff --git a/constants.gradle b/constants.gradle index 3e75567231..61b2e44b27 100644 --- a/constants.gradle +++ b/constants.gradle @@ -13,8 +13,8 @@ // limitations under the License. project.ext { // ExoPlayer version and version code. - releaseVersion = '2.7.1' - releaseVersionCode = 2701 + releaseVersion = '2.7.2' + releaseVersionCode = 2702 // Important: ExoPlayer specifies a minSdkVersion of 14 because various // components provided by the library may be of use on older devices. // However, please note that the core media playback functionality provided @@ -22,7 +22,7 @@ project.ext { minSdkVersion = 14 targetSdkVersion = 27 compileSdkVersion = 27 - buildToolsVersion = '26.0.2' + buildToolsVersion = '27.0.3' testSupportLibraryVersion = '0.5' supportLibraryVersion = '27.0.0' playServicesLibraryVersion = '11.4.2' diff --git a/extensions/ffmpeg/README.md b/extensions/ffmpeg/README.md index b29c836887..fa7ac6b9fa 100644 --- a/extensions/ffmpeg/README.md +++ b/extensions/ffmpeg/README.md @@ -29,7 +29,8 @@ EXOPLAYER_ROOT="$(pwd)" 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="" diff --git a/extensions/flac/src/androidTest/assets/bear.flac.0.dump b/extensions/flac/src/androidTest/assets/bear.flac.0.dump index 2a17cbdea6..ad88981718 100644 --- a/extensions/flac/src/androidTest/assets/bear.flac.0.dump +++ b/extensions/flac/src/androidTest/assets/bear.flac.0.dump @@ -9,7 +9,7 @@ track 0: id = null containerMimeType = null sampleMimeType = audio/raw - maxInputSize = -1 + maxInputSize = 16384 width = -1 height = -1 frameRate = -1.0 diff --git a/extensions/flac/src/androidTest/assets/bear.flac.1.dump b/extensions/flac/src/androidTest/assets/bear.flac.1.dump index 412e4a1b8f..22f30e9db2 100644 --- a/extensions/flac/src/androidTest/assets/bear.flac.1.dump +++ b/extensions/flac/src/androidTest/assets/bear.flac.1.dump @@ -9,7 +9,7 @@ track 0: id = null containerMimeType = null sampleMimeType = audio/raw - maxInputSize = -1 + maxInputSize = 16384 width = -1 height = -1 frameRate = -1.0 diff --git a/extensions/flac/src/androidTest/assets/bear.flac.2.dump b/extensions/flac/src/androidTest/assets/bear.flac.2.dump index 42ebb125d1..c52a74cbfb 100644 --- a/extensions/flac/src/androidTest/assets/bear.flac.2.dump +++ b/extensions/flac/src/androidTest/assets/bear.flac.2.dump @@ -9,7 +9,7 @@ track 0: id = null containerMimeType = null sampleMimeType = audio/raw - maxInputSize = -1 + maxInputSize = 16384 width = -1 height = -1 frameRate = -1.0 diff --git a/extensions/flac/src/androidTest/assets/bear.flac.3.dump b/extensions/flac/src/androidTest/assets/bear.flac.3.dump index 958cb0d418..760f369597 100644 --- a/extensions/flac/src/androidTest/assets/bear.flac.3.dump +++ b/extensions/flac/src/androidTest/assets/bear.flac.3.dump @@ -9,7 +9,7 @@ track 0: id = null containerMimeType = null sampleMimeType = audio/raw - maxInputSize = -1 + maxInputSize = 16384 width = -1 height = -1 frameRate = -1.0 diff --git a/extensions/flac/src/main/java/com/google/android/exoplayer2/ext/flac/FlacExtractor.java b/extensions/flac/src/main/java/com/google/android/exoplayer2/ext/flac/FlacExtractor.java index b630298c6e..6859b44877 100644 --- a/extensions/flac/src/main/java/com/google/android/exoplayer2/ext/flac/FlacExtractor.java +++ b/extensions/flac/src/main/java/com/google/android/exoplayer2/ext/flac/FlacExtractor.java @@ -116,7 +116,7 @@ public final class FlacExtractor implements Extractor { MimeTypes.AUDIO_RAW, null, streamInfo.bitRate(), - Format.NO_VALUE, + streamInfo.maxDecodedFrameSize(), streamInfo.channels, streamInfo.sampleRate, getPcmEncoding(streamInfo.bitsPerSample), diff --git a/extensions/mediasession/src/main/java/com/google/android/exoplayer2/ext/mediasession/DefaultPlaybackController.java b/extensions/mediasession/src/main/java/com/google/android/exoplayer2/ext/mediasession/DefaultPlaybackController.java index 1eb3ffd13d..ce597b45cd 100644 --- a/extensions/mediasession/src/main/java/com/google/android/exoplayer2/ext/mediasession/DefaultPlaybackController.java +++ b/extensions/mediasession/src/main/java/com/google/android/exoplayer2/ext/mediasession/DefaultPlaybackController.java @@ -77,11 +77,10 @@ public class DefaultPlaybackController implements MediaSessionConnector.Playback public long getSupportedPlaybackActions(Player player) { if (player == null || player.getCurrentTimeline().isEmpty()) { return 0; + } else if (!player.isCurrentWindowSeekable()) { + return BASE_ACTIONS; } - long actions = BASE_ACTIONS; - if (player.isCurrentWindowSeekable()) { - actions |= PlaybackStateCompat.ACTION_SEEK_TO; - } + long actions = BASE_ACTIONS | PlaybackStateCompat.ACTION_SEEK_TO; if (fastForwardIncrementMs > 0) { actions |= PlaybackStateCompat.ACTION_FAST_FORWARD; } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 32ec7e3327..5559e8ccfa 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME 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 diff --git a/javadoc_combined.gradle b/javadoc_combined.gradle index 1fec48ca25..aba4bf54bd 100644 --- a/javadoc_combined.gradle +++ b/javadoc_combined.gradle @@ -11,6 +11,8 @@ // 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. +apply from: "${rootDir}/javadoc_util.gradle" + class CombinedJavadocPlugin implements Plugin { static final String TASK_NAME = "generateCombinedJavadoc" @@ -20,22 +22,32 @@ class CombinedJavadocPlugin implements Plugin { project.gradle.projectsEvaluated { Set libraryModules = getLibraryModules(project) if (!libraryModules.isEmpty()) { - String sdkDirectory = getSdkDirectory(libraryModules) project.task(TASK_NAME, type: Javadoc) { description = "Generates combined Javadoc." title = "ExoPlayer library" source = libraryModules.generateJavadoc.source - classpath = project.files(libraryModules.generateJavadoc.classpath) + classpath = project.files([]) destinationDir = project.file("$project.buildDir/docs/javadoc") options { - links "http://docs.oracle.com/javase/7/docs/api/" - linksOffline "https://developer.android.com/reference", - "${sdkDirectory}/docs/reference" + links "https://docs.oracle.com/javase/7/docs/api/", + "https://developer.android.com/reference" encoding = "UTF-8" } exclude "**/BuildConfig.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 { libraryModules.each { libraryModule -> project.copy { @@ -43,6 +55,7 @@ class CombinedJavadocPlugin implements Plugin { into "${project.buildDir}/docs/javadoc" } } + project.fixJavadoc() } } } @@ -57,12 +70,6 @@ class CombinedJavadocPlugin implements Plugin { } } - // Returns the Android SDK directory given a set of Android library modules. - private String getSdkDirectory(Set libraryModules) { - // We can retrieve the Android SDK directory from any module. - return libraryModules.iterator().next().android.sdkDirectory - } - } apply plugin: CombinedJavadocPlugin diff --git a/javadoc_library.gradle b/javadoc_library.gradle index ea193e661c..a252b148c6 100644 --- a/javadoc_library.gradle +++ b/javadoc_library.gradle @@ -11,6 +11,8 @@ // 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. +apply from: "${rootDir}/javadoc_util.gradle" + android.libraryVariants.all { variant -> def name = variant.buildType.name if (!name.equals("release")) { @@ -20,21 +22,26 @@ android.libraryVariants.all { variant -> description = "Generates Javadoc for the ${javadocTitle}." title = "ExoPlayer ${javadocTitle}" source = variant.javaCompile.source - classpath = files(variant.javaCompile.classpath.files, - project.android.getBootClasspath()) options { links "http://docs.oracle.com/javase/7/docs/api/" linksOffline "https://developer.android.com/reference", - "${android.sdkDirectory}/docs/reference" + "${android.sdkDirectory}/docs/reference" encoding = "UTF-8" } exclude "**/BuildConfig.java" exclude "**/R.java" + doFirst { + classpath = + files( + variant.javaCompile.classpath.files, + project.android.getBootClasspath()) + } doLast { copy { from "src/main/javadoc" into "$buildDir/docs/javadoc" } + project.fixJavadoc() } } } diff --git a/javadoc_util.gradle b/javadoc_util.gradle new file mode 100644 index 0000000000..cff5f29392 --- /dev/null +++ b/javadoc_util.gradle @@ -0,0 +1,39 @@ +// 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 = "\n" + ant.replaceregexp(match:dateMeta, replace:"") { + fileset(dir: "${javadocPath}", includes: "**/*.html") + } +} diff --git a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java index c34145a145..bb3732fb94 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java @@ -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.7.1"; + public static final String VERSION = "2.7.2"; /** 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.7.1"; + public static final String VERSION_SLASHY = "ExoPlayerLib/2.7.2"; /** * 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 = 2007001; + public static final int VERSION_INT = 2007002; /** * Whether the library was compiled with {@link com.google.android.exoplayer2.util.Assertions} diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/mkv/DefaultEbmlReader.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/mkv/DefaultEbmlReader.java index 91bc170205..2c6130677f 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/mkv/DefaultEbmlReader.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/mkv/DefaultEbmlReader.java @@ -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 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. * @throws IOException If an error occurs reading from the input. * @throws InterruptedException If the thread is interrupted. @@ -217,7 +218,12 @@ import java.util.Stack; } byte[] stringBytes = new byte[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); } /** diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/AtomParsers.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/AtomParsers.java index 37cfce7c7c..30358ff7c7 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/AtomParsers.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/AtomParsers.java @@ -15,6 +15,8 @@ */ package com.google.android.exoplayer2.extractor.mp4; +import static com.google.android.exoplayer2.util.MimeTypes.getMimeTypeFromMp4ObjectType; + import android.util.Log; import android.util.Pair; import com.google.android.exoplayer2.C; @@ -1030,47 +1032,11 @@ import java.util.List; // Set the MIME type based on the object type indication (14496-1 table 5). int objectTypeIndication = parent.readUnsignedByte(); - String mimeType; - switch (objectTypeIndication) { - case 0x60: - case 0x61: - mimeType = MimeTypes.VIDEO_MPEG2; - 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; + String mimeType = getMimeTypeFromMp4ObjectType(objectTypeIndication); + if (MimeTypes.AUDIO_MPEG.equals(mimeType) + || MimeTypes.AUDIO_DTS.equals(mimeType) + || MimeTypes.AUDIO_DTS_HD.equals(mimeType)) { + return Pair.create(mimeType, null); } parent.skipBytes(12); diff --git a/library/core/src/main/java/com/google/android/exoplayer2/util/MimeTypes.java b/library/core/src/main/java/com/google/android/exoplayer2/util/MimeTypes.java index f39f897567..041ee55cf1 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/util/MimeTypes.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/util/MimeTypes.java @@ -15,6 +15,7 @@ */ package com.google.android.exoplayer2.util; +import android.support.annotation.Nullable; import android.text.TextUtils; import com.google.android.exoplayer2.C; @@ -194,7 +195,20 @@ public final class MimeTypes { } else if (codec.startsWith("vp8") || codec.startsWith("vp08")) { return MimeTypes.VIDEO_VP8; } 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")) { return MimeTypes.AUDIO_AC3; } else if (codec.startsWith("ec-3") || codec.startsWith("dec3")) { @@ -213,6 +227,50 @@ public final class MimeTypes { return null; } + /** + * 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. * {@link C#TRACK_TYPE_UNKNOWN} if the MIME type is not known or the mapping cannot be diff --git a/library/core/src/main/java/com/google/android/exoplayer2/util/Util.java b/library/core/src/main/java/com/google/android/exoplayer2/util/Util.java index 2761cc3ce6..e01dcc6f3a 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/util/Util.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/util/Util.java @@ -306,6 +306,16 @@ public final class Util { return text == null ? null : text.toLowerCase(Locale.US); } + /** + * 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. * diff --git a/library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java b/library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java index 2978d00d86..1519bfdd2b 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java @@ -1102,9 +1102,13 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { // Work around https://github.com/google/ExoPlayer/issues/3236, // https://github.com/google/ExoPlayer/issues/3355, // https://github.com/google/ExoPlayer/issues/3439, - // https://github.com/google/ExoPlayer/issues/3724 and - // https://github.com/google/ExoPlayer/issues/3835. - return (("deb".equals(Util.DEVICE) || "flo".equals(Util.DEVICE)) // Nexus 7 (2013) + // https://github.com/google/ExoPlayer/issues/3724, + // https://github.com/google/ExoPlayer/issues/3835 and + // 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)) || (("tcl_eu".equals(Util.DEVICE) // TCL Percee TV || "SVP-DTV15".equals(Util.DEVICE) // Sony Bravia 4K 2015 diff --git a/library/core/src/test/java/com/google/android/exoplayer2/extractor/mkv/DefaultEbmlReaderTest.java b/library/core/src/test/java/com/google/android/exoplayer2/extractor/mkv/DefaultEbmlReaderTest.java index 2fec5c7cab..e44da0404b 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/extractor/mkv/DefaultEbmlReaderTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/extractor/mkv/DefaultEbmlReaderTest.java @@ -89,6 +89,14 @@ public class DefaultEbmlReaderTest { assertEvents(input, expected.events); } + @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 { ExtractorInput input = createTestInput(0x42, 0x82, 0x80); diff --git a/library/core/src/test/java/com/google/android/exoplayer2/util/MimeTypesTest.java b/library/core/src/test/java/com/google/android/exoplayer2/util/MimeTypesTest.java new file mode 100644 index 0000000000..c607e92055 --- /dev/null +++ b/library/core/src/test/java/com/google/android/exoplayer2/util/MimeTypesTest.java @@ -0,0 +1,106 @@ +/* + * 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(); + } +} diff --git a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/offline/DashDownloader.java b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/offline/DashDownloader.java index abae7a5f1e..4a90feb532 100644 --- a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/offline/DashDownloader.java +++ b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/offline/DashDownloader.java @@ -117,7 +117,8 @@ public final class DashDownloader extends SegmentDownloader muxedCaptionFormats, DrmInitData drmInitData, TimestampAdjuster timestampAdjuster) { String lastPathSegment = uri.getLastPathSegment(); + if (lastPathSegment == null) { + lastPathSegment = ""; + } boolean isPackedAudioExtractor = false; Extractor extractor; if (MimeTypes.TEXT_VTT.equals(format.sampleMimeType) diff --git a/library/hls/src/test/java/com/google/android/exoplayer2/source/hls/playlist/HlsMediaPlaylistParserTest.java b/library/hls/src/test/java/com/google/android/exoplayer2/source/hls/playlist/HlsMediaPlaylistParserTest.java index b10997cfe9..5ba6f0c7f4 100644 --- a/library/hls/src/test/java/com/google/android/exoplayer2/source/hls/playlist/HlsMediaPlaylistParserTest.java +++ b/library/hls/src/test/java/com/google/android/exoplayer2/source/hls/playlist/HlsMediaPlaylistParserTest.java @@ -20,12 +20,12 @@ import static com.google.common.truth.Truth.assertThat; import android.net.Uri; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.source.hls.playlist.HlsMediaPlaylist.Segment; +import com.google.android.exoplayer2.util.Util; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.nio.charset.Charset; import java.util.List; -import java.util.Locale; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; @@ -122,7 +122,7 @@ public class HlsMediaPlaylistParserTest { .isEqualTo("https://priv.example.com/key.php?r=2682"); // 0xA7A == 2682. 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.byterangeOffset).isEqualTo(2147586650L); assertThat(segment.url).isEqualTo("https://priv.example.com/fileSequence2682.ts"); @@ -134,7 +134,7 @@ public class HlsMediaPlaylistParserTest { .isEqualTo("https://priv.example.com/key.php?r=2682"); // 0xA7B == 2683. 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.byterangeOffset).isEqualTo(0); assertThat(segment.url).isEqualTo("https://priv.example.com/fileSequence2683.ts"); diff --git a/library/ui/src/main/java/com/google/android/exoplayer2/ui/DefaultTimeBar.java b/library/ui/src/main/java/com/google/android/exoplayer2/ui/DefaultTimeBar.java index 048bb3ffff..a15ee1b88e 100644 --- a/library/ui/src/main/java/com/google/android/exoplayer2/ui/DefaultTimeBar.java +++ b/library/ui/src/main/java/com/google/android/exoplayer2/ui/DefaultTimeBar.java @@ -250,7 +250,7 @@ public class DefaultTimeBar extends View implements TimeBar { try { scrubberDrawable = a.getDrawable(R.styleable.DefaultTimeBar_scrubber_drawable); if (scrubberDrawable != null) { - setDrawableLayoutDirection(scrubberDrawable, getLayoutDirection()); + setDrawableLayoutDirection(scrubberDrawable); defaultTouchTargetHeight = Math.max(scrubberDrawable.getMinimumHeight(), defaultTouchTargetHeight); } @@ -747,8 +747,8 @@ public class DefaultTimeBar extends View implements TimeBar { return true; } - private static int dpToPx(DisplayMetrics displayMetrics, int dps) { - return (int) (dps * displayMetrics.density + 0.5f); + private boolean setDrawableLayoutDirection(Drawable drawable) { + return Util.SDK_INT >= 23 && setDrawableLayoutDirection(drawable, getLayoutDirection()); } private static boolean setDrawableLayoutDirection(Drawable drawable, int layoutDirection) { @@ -771,4 +771,7 @@ public class DefaultTimeBar extends View implements TimeBar { return 0x33000000 | (adMarkerColor & 0x00FFFFFF); } + private static int dpToPx(DisplayMetrics displayMetrics, int dps) { + return (int) (dps * displayMetrics.density + 0.5f); + } } diff --git a/publish.gradle b/publish.gradle index ca1a2cfd8b..85cf87aa85 100644 --- a/publish.gradle +++ b/publish.gradle @@ -16,8 +16,8 @@ if (project.ext.has("exoplayerPublishEnabled") apply plugin: 'bintray-release' publish { artifactId = releaseArtifact - description = releaseDescription - version = releaseVersion + desc = releaseDescription + publishVersion = releaseVersion repoName = getBintrayRepo() userOrg = 'google' groupId = 'com.google.android.exoplayer'