diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 4f147e2bbd..f9f6b02c19 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -1,11 +1,29 @@ # Release notes # +### r2.4.3 ### + +* Audio: Workaround custom audio decoders misreporting their maximum supported + channel counts ([#2940](https://github.com/google/ExoPlayer/issues/2940)). +* Audio: Workaround for broken MediaTek raw decoder on some devices + ([#2873](https://github.com/google/ExoPlayer/issues/2873)). +* Captions: Fix TTML captions appearing at the top of the screen + ([#2953](https://github.com/google/ExoPlayer/issues/2953)). +* Captions: Fix handling of some DVB subtitles + ([#2957](https://github.com/google/ExoPlayer/issues/2957)). +* Track selection: Fix setSelectionOverride(index, tracks, null) + ([#2988](https://github.com/google/ExoPlayer/issues/2988)). +* GVR extension: Add support for mono input + ([#2710](https://github.com/google/ExoPlayer/issues/2710)). +* FLAC extension: Fix failing build + ([#2977](https://github.com/google/ExoPlayer/pull/2977)). +* Misc bugfixes. + ### r2.4.2 ### * Stability: Work around Nexus 10 reboot when playing certain content - ([2806](https://github.com/google/ExoPlayer/issues/2806)). + ([#2806](https://github.com/google/ExoPlayer/issues/2806)). * MP3: Correctly treat MP3s with INFO headers as constant bitrate - ([2895](https://github.com/google/ExoPlayer/issues/2895)). + ([#2895](https://github.com/google/ExoPlayer/issues/2895)). * HLS: Use average rather than peak bandwidth when available ([#2863](https://github.com/google/ExoPlayer/issues/2863)). * SmoothStreaming: Fix timeline for live streams diff --git a/build.gradle b/build.gradle index 4f18e7c801..01d8b6616c 100644 --- a/build.gradle +++ b/build.gradle @@ -48,7 +48,7 @@ allprojects { releaseRepoName = getBintrayRepo() releaseUserOrg = 'google' releaseGroupId = 'com.google.android.exoplayer' - releaseVersion = 'r2.4.2' + releaseVersion = 'r2.4.3' releaseWebsite = 'https://github.com/google/ExoPlayer' } if (it.hasProperty('externalBuildDir')) { diff --git a/demo/src/main/AndroidManifest.xml b/demo/src/main/AndroidManifest.xml index 34256d41c1..addce60cad 100644 --- a/demo/src/main/AndroidManifest.xml +++ b/demo/src/main/AndroidManifest.xml @@ -16,8 +16,8 @@ + android:versionCode="2403" + android:versionName="2.4.3"> diff --git a/extensions/flac/src/main/jni/flac_parser.cc b/extensions/flac/src/main/jni/flac_parser.cc index e4925cb462..6c6e57f5f7 100644 --- a/extensions/flac/src/main/jni/flac_parser.cc +++ b/extensions/flac/src/main/jni/flac_parser.cc @@ -22,6 +22,7 @@ #include #include +#include #define LOG_TAG "FLACParser" #define ALOGE(...) \ diff --git a/extensions/gvr/build.gradle b/extensions/gvr/build.gradle index f622a73758..e15c8b1ad8 100644 --- a/extensions/gvr/build.gradle +++ b/extensions/gvr/build.gradle @@ -25,7 +25,7 @@ android { dependencies { compile project(':library-core') - compile 'com.google.vr:sdk-audio:1.30.0' + compile 'com.google.vr:sdk-audio:1.60.1' } ext { diff --git a/extensions/gvr/src/main/java/com/google/android/exoplayer2/ext/gvr/GvrAudioProcessor.java b/extensions/gvr/src/main/java/com/google/android/exoplayer2/ext/gvr/GvrAudioProcessor.java index 980424904d..a56bc7f0a9 100644 --- a/extensions/gvr/src/main/java/com/google/android/exoplayer2/ext/gvr/GvrAudioProcessor.java +++ b/extensions/gvr/src/main/java/com/google/android/exoplayer2/ext/gvr/GvrAudioProcessor.java @@ -82,6 +82,9 @@ public final class GvrAudioProcessor implements AudioProcessor { maybeReleaseGvrAudioSurround(); int surroundFormat; switch (channelCount) { + case 1: + surroundFormat = GvrAudioSurround.SurroundFormat.SURROUND_MONO; + break; case 2: surroundFormat = GvrAudioSurround.SurroundFormat.SURROUND_STEREO; break; diff --git a/extensions/okhttp/src/main/java/com/google/android/exoplayer2/ext/okhttp/OkHttpDataSource.java b/extensions/okhttp/src/main/java/com/google/android/exoplayer2/ext/okhttp/OkHttpDataSource.java index 47850c0637..167fc68e86 100644 --- a/extensions/okhttp/src/main/java/com/google/android/exoplayer2/ext/okhttp/OkHttpDataSource.java +++ b/extensions/okhttp/src/main/java/com/google/android/exoplayer2/ext/okhttp/OkHttpDataSource.java @@ -16,6 +16,8 @@ package com.google.android.exoplayer2.ext.okhttp; import android.net.Uri; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.upstream.DataSourceException; import com.google.android.exoplayer2.upstream.DataSpec; @@ -45,13 +47,14 @@ public class OkHttpDataSource implements HttpDataSource { private static final AtomicReference skipBufferReference = new AtomicReference<>(); - private final Call.Factory callFactory; - private final String userAgent; - private final Predicate contentTypePredicate; - private final TransferListener listener; - private final CacheControl cacheControl; - private final RequestProperties defaultRequestProperties; - private final RequestProperties requestProperties; + @NonNull private final Call.Factory callFactory; + @NonNull private final RequestProperties requestProperties; + + @Nullable private final String userAgent; + @Nullable private final Predicate contentTypePredicate; + @Nullable private final TransferListener listener; + @Nullable private final CacheControl cacheControl; + @Nullable private final RequestProperties defaultRequestProperties; private DataSpec dataSpec; private Response response; @@ -67,33 +70,34 @@ public class OkHttpDataSource implements HttpDataSource { /** * @param callFactory A {@link Call.Factory} (typically an {@link okhttp3.OkHttpClient}) for use * by the source. - * @param userAgent The User-Agent string that should be used. + * @param userAgent An optional User-Agent string. * @param contentTypePredicate An optional {@link Predicate}. If a content type is rejected by the * predicate then a InvalidContentTypeException} is thrown from {@link #open(DataSpec)}. */ - public OkHttpDataSource(Call.Factory callFactory, String userAgent, - Predicate contentTypePredicate) { + public OkHttpDataSource(@NonNull Call.Factory callFactory, @Nullable String userAgent, + @Nullable Predicate contentTypePredicate) { this(callFactory, userAgent, contentTypePredicate, null); } /** * @param callFactory A {@link Call.Factory} (typically an {@link okhttp3.OkHttpClient}) for use * by the source. - * @param userAgent The User-Agent string that should be used. + * @param userAgent An optional User-Agent string. * @param contentTypePredicate An optional {@link Predicate}. If a content type is rejected by the * predicate then a {@link InvalidContentTypeException} is thrown from * {@link #open(DataSpec)}. * @param listener An optional listener. */ - public OkHttpDataSource(Call.Factory callFactory, String userAgent, - Predicate contentTypePredicate, TransferListener listener) { + public OkHttpDataSource(@NonNull Call.Factory callFactory, @Nullable String userAgent, + @Nullable Predicate contentTypePredicate, + @Nullable TransferListener listener) { this(callFactory, userAgent, contentTypePredicate, listener, null, null); } /** * @param callFactory A {@link Call.Factory} (typically an {@link okhttp3.OkHttpClient}) for use * by the source. - * @param userAgent The User-Agent string that should be used. + * @param userAgent An optional User-Agent string. * @param contentTypePredicate An optional {@link Predicate}. If a content type is rejected by the * predicate then a {@link InvalidContentTypeException} is thrown from * {@link #open(DataSpec)}. @@ -102,11 +106,12 @@ public class OkHttpDataSource implements HttpDataSource { * @param defaultRequestProperties The optional default {@link RequestProperties} to be sent to * the server as HTTP headers on every request. */ - public OkHttpDataSource(Call.Factory callFactory, String userAgent, - Predicate contentTypePredicate, TransferListener listener, - CacheControl cacheControl, RequestProperties defaultRequestProperties) { + public OkHttpDataSource(@NonNull Call.Factory callFactory, @Nullable String userAgent, + @Nullable Predicate contentTypePredicate, + @Nullable TransferListener listener, + @Nullable CacheControl cacheControl, @Nullable RequestProperties defaultRequestProperties) { this.callFactory = Assertions.checkNotNull(callFactory); - this.userAgent = Assertions.checkNotEmpty(userAgent); + this.userAgent = userAgent; this.contentTypePredicate = contentTypePredicate; this.listener = listener; this.cacheControl = cacheControl; @@ -280,7 +285,10 @@ public class OkHttpDataSource implements HttpDataSource { } builder.addHeader("Range", rangeRequest); } - builder.addHeader("User-Agent", userAgent); + if (userAgent != null) { + builder.addHeader("User-Agent", userAgent); + } + if (!allowGzip) { builder.addHeader("Accept-Encoding", "identity"); } diff --git a/extensions/okhttp/src/main/java/com/google/android/exoplayer2/ext/okhttp/OkHttpDataSourceFactory.java b/extensions/okhttp/src/main/java/com/google/android/exoplayer2/ext/okhttp/OkHttpDataSourceFactory.java index 5228065db1..32fc5a58cb 100644 --- a/extensions/okhttp/src/main/java/com/google/android/exoplayer2/ext/okhttp/OkHttpDataSourceFactory.java +++ b/extensions/okhttp/src/main/java/com/google/android/exoplayer2/ext/okhttp/OkHttpDataSourceFactory.java @@ -15,6 +15,8 @@ */ package com.google.android.exoplayer2.ext.okhttp; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.HttpDataSource; import com.google.android.exoplayer2.upstream.HttpDataSource.BaseFactory; @@ -28,31 +30,32 @@ import okhttp3.Call; */ public final class OkHttpDataSourceFactory extends BaseFactory { - private final Call.Factory callFactory; - private final String userAgent; - private final TransferListener listener; - private final CacheControl cacheControl; + @NonNull private final Call.Factory callFactory; + @Nullable private final String userAgent; + @Nullable private final TransferListener listener; + @Nullable private final CacheControl cacheControl; /** * @param callFactory A {@link Call.Factory} (typically an {@link okhttp3.OkHttpClient}) for use * by the sources created by the factory. - * @param userAgent The User-Agent string that should be used. + * @param userAgent An optional User-Agent string. * @param listener An optional listener. */ - public OkHttpDataSourceFactory(Call.Factory callFactory, String userAgent, - TransferListener listener) { + public OkHttpDataSourceFactory(@NonNull Call.Factory callFactory, @Nullable String userAgent, + @Nullable TransferListener listener) { this(callFactory, userAgent, listener, null); } /** * @param callFactory A {@link Call.Factory} (typically an {@link okhttp3.OkHttpClient}) for use * by the sources created by the factory. - * @param userAgent The User-Agent string that should be used. + * @param userAgent An optional User-Agent string. * @param listener An optional listener. * @param cacheControl An optional {@link CacheControl} for setting the Cache-Control header. */ - public OkHttpDataSourceFactory(Call.Factory callFactory, String userAgent, - TransferListener listener, CacheControl cacheControl) { + public OkHttpDataSourceFactory(@NonNull Call.Factory callFactory, @Nullable String userAgent, + @Nullable TransferListener listener, + @Nullable CacheControl cacheControl) { this.callFactory = callFactory; this.userAgent = userAgent; this.listener = listener; diff --git a/extensions/vp9/src/main/java/com/google/android/exoplayer2/ext/vp9/LibvpxVideoRenderer.java b/extensions/vp9/src/main/java/com/google/android/exoplayer2/ext/vp9/LibvpxVideoRenderer.java index 4b629c8d2a..29423547b6 100644 --- a/extensions/vp9/src/main/java/com/google/android/exoplayer2/ext/vp9/LibvpxVideoRenderer.java +++ b/extensions/vp9/src/main/java/com/google/android/exoplayer2/ext/vp9/LibvpxVideoRenderer.java @@ -480,11 +480,11 @@ public final class LibvpxVideoRenderer extends BaseRenderer { protected void onStarted() { droppedFrames = 0; droppedFrameAccumulationStartTimeMs = SystemClock.elapsedRealtime(); - joiningDeadlineMs = C.TIME_UNSET; } @Override protected void onStopped() { + joiningDeadlineMs = C.TIME_UNSET; maybeNotifyDroppedFrames(); } diff --git a/library/core/src/androidTest/AndroidManifest.xml b/library/core/src/androidTest/AndroidManifest.xml index 9eab386b51..a50de35b62 100644 --- a/library/core/src/androidTest/AndroidManifest.xml +++ b/library/core/src/androidTest/AndroidManifest.xml @@ -24,6 +24,9 @@ android:allowBackup="false" tools:ignore="MissingApplicationIcon,HardcodedDebugMode"> + { - /** Wraps the exception which is the cause of the error state. */ + /** Wraps the throwable which is the cause of the error state. */ class DrmSessionException extends Exception { - public DrmSessionException(Exception e) { - super(e); + public DrmSessionException(Throwable cause) { + super(cause); } } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/flv/AudioTagPayloadReader.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/flv/AudioTagPayloadReader.java index 8e3bd08375..2f21898007 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/flv/AudioTagPayloadReader.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/flv/AudioTagPayloadReader.java @@ -67,7 +67,7 @@ import java.util.Collections; hasOutputFormat = true; } else if (audioFormat == AUDIO_FORMAT_ALAW || audioFormat == AUDIO_FORMAT_ULAW) { String type = audioFormat == AUDIO_FORMAT_ALAW ? MimeTypes.AUDIO_ALAW - : MimeTypes.AUDIO_ULAW; + : MimeTypes.AUDIO_MLAW; int pcmEncoding = (header & 0x01) == 1 ? C.ENCODING_PCM_16BIT : C.ENCODING_PCM_8BIT; Format format = Format.createAudioSampleFormat(null, type, null, Format.NO_VALUE, Format.NO_VALUE, 1, 8000, pcmEncoding, null, null, 0, null); diff --git a/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecInfo.java b/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecInfo.java index 3c788a60a4..a7c237edb2 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecInfo.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecInfo.java @@ -264,7 +264,9 @@ public final class MediaCodecInfo { logNoSupport("channelCount.aCaps"); return false; } - if (audioCapabilities.getMaxInputChannelCount() < channelCount) { + int maxInputChannelCount = adjustMaxInputChannelCount(name, mimeType, + audioCapabilities.getMaxInputChannelCount()); + if (maxInputChannelCount < channelCount) { logNoSupport("channelCount.support, " + channelCount); return false; } @@ -281,6 +283,40 @@ public final class MediaCodecInfo { + Util.DEVICE_DEBUG_INFO + "]"); } + private static int adjustMaxInputChannelCount(String name, String mimeType, int maxChannelCount) { + if (maxChannelCount > 1 || (Util.SDK_INT >= 26 && maxChannelCount > 0)) { + // The maximum channel count looks like it's been set correctly. + return maxChannelCount; + } + if (MimeTypes.AUDIO_MPEG.equals(mimeType) + || MimeTypes.AUDIO_AMR_NB.equals(mimeType) + || MimeTypes.AUDIO_AMR_WB.equals(mimeType) + || MimeTypes.AUDIO_AAC.equals(mimeType) + || MimeTypes.AUDIO_VORBIS.equals(mimeType) + || MimeTypes.AUDIO_OPUS.equals(mimeType) + || MimeTypes.AUDIO_RAW.equals(mimeType) + || MimeTypes.AUDIO_FLAC.equals(mimeType) + || MimeTypes.AUDIO_ALAW.equals(mimeType) + || MimeTypes.AUDIO_MLAW.equals(mimeType) + || MimeTypes.AUDIO_MSGSM.equals(mimeType)) { + // Platform code should have set a default. + return maxChannelCount; + } + // The maximum channel count looks incorrect. Adjust it to an assumed default. + int assumedMaxChannelCount; + if (MimeTypes.AUDIO_AC3.equals(mimeType)) { + assumedMaxChannelCount = 6; + } else if (MimeTypes.AUDIO_E_AC3.equals(mimeType)) { + assumedMaxChannelCount = 16; + } else { + // Default to the platform limit, which is 30. + assumedMaxChannelCount = 30; + } + Log.w(TAG, "AssumedMaxChannelAdjustment: " + name + ", [" + maxChannelCount + " to " + + assumedMaxChannelCount + "]"); + return assumedMaxChannelCount; + } + private static boolean isAdaptive(CodecCapabilities capabilities) { return Util.SDK_INT >= 19 && isAdaptiveV19(capabilities); } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecUtil.java b/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecUtil.java index 5369dffeb6..392162f607 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecUtil.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecUtil.java @@ -56,8 +56,10 @@ public final class MediaCodecUtil { } private static final String TAG = "MediaCodecUtil"; + private static final String GOOGLE_RAW_DECODER_NAME = "OMX.google.raw.decoder"; + private static final String MTK_RAW_DECODER_NAME = "OMX.MTK.AUDIO.DECODER.RAW"; private static final MediaCodecInfo PASSTHROUGH_DECODER_INFO = - MediaCodecInfo.newPassthroughInstance("OMX.google.raw.decoder"); + MediaCodecInfo.newPassthroughInstance(GOOGLE_RAW_DECODER_NAME); private static final Pattern PROFILE_PATTERN = Pattern.compile("^\\D?(\\d+)$"); private static final HashMap> decoderInfosCache = new HashMap<>(); @@ -155,6 +157,7 @@ public final class MediaCodecUtil { + ". Assuming: " + decoderInfos.get(0).name); } } + applyWorkarounds(decoderInfos); decoderInfos = Collections.unmodifiableList(decoderInfos); decoderInfosCache.put(key, decoderInfos); return decoderInfos; @@ -339,6 +342,27 @@ public final class MediaCodecUtil { return true; } + /** + * Modifies a list of {@link MediaCodecInfo}s to apply workarounds where we know better than the + * platform. + * + * @param decoderInfos The list to modify. + */ + private static void applyWorkarounds(List decoderInfos) { + if (Util.SDK_INT < 26 && decoderInfos.size() > 1 + && MTK_RAW_DECODER_NAME.equals(decoderInfos.get(0).name)) { + // Prefer the Google raw decoder over the MediaTek one [Internal: b/62337687]. + for (int i = 1; i < decoderInfos.size(); i++) { + MediaCodecInfo decoderInfo = decoderInfos.get(i); + if (GOOGLE_RAW_DECODER_NAME.equals(decoderInfo.name)) { + decoderInfos.remove(i); + decoderInfos.add(0, decoderInfo); + break; + } + } + } + } + /** * Returns whether the decoder is known to fail when adapting, despite advertising itself as an * adaptive decoder. diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/MediaPeriod.java b/library/core/src/main/java/com/google/android/exoplayer2/source/MediaPeriod.java index 3b06542855..aaf4c89ff7 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/MediaPeriod.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/MediaPeriod.java @@ -106,6 +106,8 @@ public interface MediaPeriod extends SequenceableLoader { /** * Discards buffered media up to the specified position. + *

+ * This method should only be called after the period has been prepared. * * @param positionUs The position in microseconds. */ @@ -116,6 +118,8 @@ public interface MediaPeriod extends SequenceableLoader { *

* After this method has returned a value other than {@link C#TIME_UNSET}, all * {@link SampleStream}s provided by the period are guaranteed to start from a key frame. + *

+ * This method should only be called after the period has been prepared. * * @return If a discontinuity was read then the playback position in microseconds after the * discontinuity. Else {@link C#TIME_UNSET}. diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/TrackGroup.java b/library/core/src/main/java/com/google/android/exoplayer2/source/TrackGroup.java index 393ac1988a..06410d5426 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/TrackGroup.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/TrackGroup.java @@ -42,7 +42,7 @@ public final class TrackGroup { private int hashCode; /** - * @param formats The track formats. Must not be null or contain null elements. + * @param formats The track formats. Must not be null, contain null elements or be of length 0. */ public TrackGroup(Format... formats) { Assertions.checkState(formats.length > 0); diff --git a/library/core/src/main/java/com/google/android/exoplayer2/text/dvb/DvbParser.java b/library/core/src/main/java/com/google/android/exoplayer2/text/dvb/DvbParser.java index 96c8a89801..c0caf1e57a 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/text/dvb/DvbParser.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/text/dvb/DvbParser.java @@ -667,13 +667,15 @@ import java.util.List; int runLength = 0; int clutIndex = 0; int peek = data.readBits(2); - if (!data.readBit()) { + if (peek != 0x00) { runLength = 1; clutIndex = peek; } else if (data.readBit()) { runLength = 3 + data.readBits(3); clutIndex = data.readBits(2); - } else if (!data.readBit()) { + } else if (data.readBit()) { + runLength = 1; + } else { switch (data.readBits(2)) { case 0x00: endOfPixelCodeString = true; diff --git a/library/core/src/main/java/com/google/android/exoplayer2/text/ttml/TtmlDecoder.java b/library/core/src/main/java/com/google/android/exoplayer2/text/ttml/TtmlDecoder.java index 0012ce2c22..e438aa1837 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/text/ttml/TtmlDecoder.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/text/ttml/TtmlDecoder.java @@ -222,9 +222,9 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder { /** * Parses a region declaration. *

- * If the region defines an origin and/or extent, it is required that they're defined as - * percentages of the viewport. Region declarations that define origin and/or extent in other - * formats are unsupported, and null is returned. + * If the region defines an origin and extent, it is required that they're defined as percentages + * of the viewport. Region declarations that define origin and extent in other formats are + * unsupported, and null is returned. */ private TtmlRegion parseRegionAttributes(XmlPullParser xmlParser) { String regionId = XmlPullParserUtil.getAttributeValue(xmlParser, TtmlNode.ATTR_ID); @@ -250,9 +250,13 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder { return null; } } else { + Log.w(TAG, "Ignoring region without an origin"); + return null; + // TODO: Should default to top left as below in this case, but need to fix + // https://github.com/google/ExoPlayer/issues/2953 first. // Origin is omitted. Default to top left. - position = 0; - line = 0; + // position = 0; + // line = 0; } float width; @@ -273,9 +277,13 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder { return null; } } else { + Log.w(TAG, "Ignoring region without an extent"); + return null; + // TODO: Should default to extent of parent as below in this case, but need to fix + // https://github.com/google/ExoPlayer/issues/2953 first. // Extent is omitted. Default to extent of parent. - width = 1; - height = 1; + // width = 1; + // height = 1; } @Cue.AnchorType int lineAnchor = Cue.ANCHOR_TYPE_START; diff --git a/library/core/src/main/java/com/google/android/exoplayer2/trackselection/MappingTrackSelector.java b/library/core/src/main/java/com/google/android/exoplayer2/trackselection/MappingTrackSelector.java index 690723cf15..3499efdb16 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/trackselection/MappingTrackSelector.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/trackselection/MappingTrackSelector.java @@ -304,10 +304,10 @@ public abstract class MappingTrackSelector extends TrackSelector { trackSelections[i] = null; } else { TrackGroupArray rendererTrackGroup = rendererTrackGroupArrays[i]; - Map overrides = selectionOverrides.get(i); - SelectionOverride override = overrides == null ? null : overrides.get(rendererTrackGroup); - if (override != null) { - trackSelections[i] = override.createTrackSelection(rendererTrackGroup); + if (hasSelectionOverride(i, rendererTrackGroup)) { + SelectionOverride override = selectionOverrides.get(i).get(rendererTrackGroup); + trackSelections[i] = override == null ? null + : override.createTrackSelection(rendererTrackGroup); } } } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/upstream/ContentDataSource.java b/library/core/src/main/java/com/google/android/exoplayer2/upstream/ContentDataSource.java index f806f47410..d118b91378 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/upstream/ContentDataSource.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/upstream/ContentDataSource.java @@ -22,6 +22,7 @@ import android.net.Uri; import com.google.android.exoplayer2.C; import java.io.EOFException; import java.io.FileInputStream; +import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; @@ -71,9 +72,13 @@ public final class ContentDataSource implements DataSource { try { uri = dataSpec.uri; assetFileDescriptor = resolver.openAssetFileDescriptor(uri, "r"); + if (assetFileDescriptor == null) { + throw new FileNotFoundException("Could not open file descriptor for: " + uri); + } inputStream = new FileInputStream(assetFileDescriptor.getFileDescriptor()); - long skipped = inputStream.skip(dataSpec.position); - if (skipped < dataSpec.position) { + long assertStartOffset = assetFileDescriptor.getStartOffset(); + long skipped = inputStream.skip(assertStartOffset + dataSpec.position) - assertStartOffset; + if (skipped != dataSpec.position) { // We expect the skip to be satisfied in full. If it isn't then we're probably trying to // skip beyond the end of the data. throw new EOFException(); @@ -81,12 +86,16 @@ public final class ContentDataSource implements DataSource { if (dataSpec.length != C.LENGTH_UNSET) { bytesRemaining = dataSpec.length; } else { - bytesRemaining = inputStream.available(); - if (bytesRemaining == 0) { - // FileInputStream.available() returns 0 if the remaining length cannot be determined, or - // if it's greater than Integer.MAX_VALUE. We don't know the true length in either case, - // so treat as unbounded. - bytesRemaining = C.LENGTH_UNSET; + bytesRemaining = assetFileDescriptor.getLength(); + if (bytesRemaining == AssetFileDescriptor.UNKNOWN_LENGTH) { + // The asset must extend to the end of the file. + bytesRemaining = inputStream.available(); + if (bytesRemaining == 0) { + // FileInputStream.available() returns 0 if the remaining length cannot be determined, + // or if it's greater than Integer.MAX_VALUE. We don't know the true length in either + // case, so treat as unbounded. + bytesRemaining = C.LENGTH_UNSET; + } } } } catch (IOException e) { 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 e227ea1068..db1122dbe7 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 @@ -48,7 +48,7 @@ public final class MimeTypes { public static final String AUDIO_MPEG_L2 = BASE_TYPE_AUDIO + "/mpeg-L2"; public static final String AUDIO_RAW = BASE_TYPE_AUDIO + "/raw"; public static final String AUDIO_ALAW = BASE_TYPE_AUDIO + "/g711-alaw"; - public static final String AUDIO_ULAW = BASE_TYPE_AUDIO + "/g711-mlaw"; + public static final String AUDIO_MLAW = BASE_TYPE_AUDIO + "/g711-mlaw"; public static final String AUDIO_AC3 = BASE_TYPE_AUDIO + "/ac3"; public static final String AUDIO_E_AC3 = BASE_TYPE_AUDIO + "/eac3"; public static final String AUDIO_TRUEHD = BASE_TYPE_AUDIO + "/true-hd"; @@ -59,8 +59,9 @@ public final class MimeTypes { public static final String AUDIO_OPUS = BASE_TYPE_AUDIO + "/opus"; public static final String AUDIO_AMR_NB = BASE_TYPE_AUDIO + "/3gpp"; public static final String AUDIO_AMR_WB = BASE_TYPE_AUDIO + "/amr-wb"; - public static final String AUDIO_FLAC = BASE_TYPE_AUDIO + "/x-flac"; + public static final String AUDIO_FLAC = BASE_TYPE_AUDIO + "/flac"; public static final String AUDIO_ALAC = BASE_TYPE_AUDIO + "/alac"; + public static final String AUDIO_MSGSM = BASE_TYPE_AUDIO + "/gsm"; public static final String AUDIO_UNKNOWN = BASE_TYPE_AUDIO + "/x-unknown"; public static final String TEXT_VTT = BASE_TYPE_TEXT + "/vtt"; 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 0304c33b3c..990a29dc4e 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 @@ -260,11 +260,11 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { super.onStarted(); droppedFrames = 0; droppedFrameAccumulationStartTimeMs = SystemClock.elapsedRealtime(); - joiningDeadlineMs = C.TIME_UNSET; } @Override protected void onStopped() { + joiningDeadlineMs = C.TIME_UNSET; maybeNotifyDroppedFrames(); super.onStopped(); } diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistParser.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistParser.java index a867659838..c64b98b5f3 100644 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistParser.java +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistParser.java @@ -30,6 +30,7 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.Collections; +import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Queue; @@ -174,6 +175,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser variantUrls = new HashSet<>(); ArrayList variants = new ArrayList<>(); ArrayList audios = new ArrayList<>(); ArrayList subtitles = new ArrayList<>(); @@ -251,11 +253,13 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser= 0 ? scrubberEnabledSize : scrubberDisabledSize); - } - private void update() { bufferedBar.set(progressBar); scrubberBar.set(progressBar); @@ -543,6 +531,8 @@ public class DefaultTimeBar extends View implements TimeBar { if (duration <= 0) { return; } + int scrubberSize = (scrubbing || isFocused()) ? scrubberDraggedSize + : (isEnabled() ? scrubberEnabledSize : scrubberDisabledSize); int playheadRadius = scrubberSize / 2; int playheadCenter = Util.constrainValue(scrubberBar.right, scrubberBar.left, progressBar.right); diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/TestUtil.java b/testutils/src/main/java/com/google/android/exoplayer2/testutil/TestUtil.java index 75a4a01923..363f60b10d 100644 --- a/testutils/src/main/java/com/google/android/exoplayer2/testutil/TestUtil.java +++ b/testutils/src/main/java/com/google/android/exoplayer2/testutil/TestUtil.java @@ -17,11 +17,14 @@ package com.google.android.exoplayer2.testutil; import android.app.Instrumentation; import android.test.InstrumentationTestCase; +import android.test.MoreAsserts; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.extractor.Extractor; import com.google.android.exoplayer2.extractor.PositionHolder; import com.google.android.exoplayer2.extractor.SeekMap; import com.google.android.exoplayer2.testutil.FakeExtractorInput.SimulatedIOException; +import com.google.android.exoplayer2.upstream.DataSource; +import com.google.android.exoplayer2.upstream.DataSpec; import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Util; import java.io.IOException; @@ -64,6 +67,22 @@ public class TestUtil { } } + public static byte[] readToEnd(DataSource dataSource) throws IOException { + byte[] data = new byte[1024]; + int position = 0; + int bytesRead = 0; + while (bytesRead != C.RESULT_END_OF_INPUT) { + if (position == data.length) { + data = Arrays.copyOf(data, data.length * 2); + } + bytesRead = dataSource.read(data, position, data.length - position); + if (bytesRead != C.RESULT_END_OF_INPUT) { + position += bytesRead; + } + } + return Arrays.copyOf(data, position); + } + public static FakeExtractorOutput consumeTestData(Extractor extractor, FakeExtractorInput input, long timeUs) throws IOException, InterruptedException { return consumeTestData(extractor, input, timeUs, false); @@ -373,4 +392,24 @@ public class TestUtil { } } + /** + * Asserts that data read from a {@link DataSource} matches {@code expected}. + * + * @param dataSource The {@link DataSource} through which to read. + * @param dataSpec The {@link DataSpec} to use when opening the {@link DataSource}. + * @param expectedData The expected data. + * @throws IOException If an error occurs reading fom the {@link DataSource}. + */ + public static void assertDataSourceContent(DataSource dataSource, DataSpec dataSpec, + byte[] expectedData) throws IOException { + try { + long length = dataSource.open(dataSpec); + Assert.assertEquals(length, expectedData.length); + byte[] readData = TestUtil.readToEnd(dataSource); + MoreAsserts.assertEquals(expectedData, readData); + } finally { + dataSource.close(); + } + } + }