From 488c2d82708fcd547a5a0b19bd56160914ed8567 Mon Sep 17 00:00:00 2001 From: olly Date: Mon, 31 Oct 2016 07:10:31 -0700 Subject: [PATCH] Fixes for Issue #1962 - Use A/V tracks only for buffering position when available in ExtractorMediaPeriod. - Fix layering of exo_simple_player_view so that subtitles are above album art. The test stream provided has both. - Make album art in SimpleExoPlayer view respect the resize mode, like we do for video. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=137698473 --- .../source/ExtractorMediaPeriod.java | 31 +++++-- .../exoplayer2/ui/AspectRatioFrameLayout.java | 8 +- .../exoplayer2/ui/SimpleExoPlayerView.java | 88 +++++++++++-------- .../res/layout/exo_simple_player_view.xml | 14 +-- library/src/main/res/values/attrs.xml | 1 + library/src/main/res/values/ids.xml | 2 +- 6 files changed, 93 insertions(+), 51 deletions(-) diff --git a/library/src/main/java/com/google/android/exoplayer2/source/ExtractorMediaPeriod.java b/library/src/main/java/com/google/android/exoplayer2/source/ExtractorMediaPeriod.java index 18de1b9df9..3b18a06c75 100644 --- a/library/src/main/java/com/google/android/exoplayer2/source/ExtractorMediaPeriod.java +++ b/library/src/main/java/com/google/android/exoplayer2/source/ExtractorMediaPeriod.java @@ -39,6 +39,7 @@ import com.google.android.exoplayer2.upstream.Loader; import com.google.android.exoplayer2.upstream.Loader.Loadable; import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.ConditionVariable; +import com.google.android.exoplayer2.util.MimeTypes; import java.io.EOFException; import java.io.IOException; @@ -80,6 +81,8 @@ import java.io.IOException; private TrackGroupArray tracks; private long durationUs; private boolean[] trackEnabledStates; + private boolean[] trackIsAudioVideoFlags; + private boolean haveAudioVideoTracks; private long length; private long lastSeekPositionUs; @@ -259,11 +262,23 @@ import java.io.IOException; return C.TIME_END_OF_SOURCE; } else if (isPendingReset()) { return pendingResetPositionUs; - } else { - long largestQueuedTimestampUs = getLargestQueuedTimestampUs(); - return largestQueuedTimestampUs == Long.MIN_VALUE ? lastSeekPositionUs - : largestQueuedTimestampUs; } + long largestQueuedTimestampUs; + if (haveAudioVideoTracks) { + // Ignore non-AV tracks, which may be sparse or poorly interleaved. + largestQueuedTimestampUs = Long.MAX_VALUE; + int trackCount = sampleQueues.size(); + for (int i = 0; i < trackCount; i++) { + if (trackIsAudioVideoFlags[i]) { + largestQueuedTimestampUs = Math.min(largestQueuedTimestampUs, + sampleQueues.valueAt(i).getLargestQueuedTimestampUs()); + } + } + } else { + largestQueuedTimestampUs = getLargestQueuedTimestampUs(); + } + return largestQueuedTimestampUs == Long.MIN_VALUE ? lastSeekPositionUs + : largestQueuedTimestampUs; } @Override @@ -404,10 +419,16 @@ import java.io.IOException; } loadCondition.close(); TrackGroup[] trackArray = new TrackGroup[trackCount]; + trackIsAudioVideoFlags = new boolean[trackCount]; trackEnabledStates = new boolean[trackCount]; durationUs = seekMap.getDurationUs(); for (int i = 0; i < trackCount; i++) { - trackArray[i] = new TrackGroup(sampleQueues.valueAt(i).getUpstreamFormat()); + Format trackFormat = sampleQueues.valueAt(i).getUpstreamFormat(); + trackArray[i] = new TrackGroup(trackFormat); + String mimeType = trackFormat.sampleMimeType; + boolean isAudioVideo = MimeTypes.isVideo(mimeType) || MimeTypes.isAudio(mimeType); + trackIsAudioVideoFlags[i] = isAudioVideo; + haveAudioVideoTracks |= isAudioVideo; } tracks = new TrackGroupArray(trackArray); prepared = true; diff --git a/library/src/main/java/com/google/android/exoplayer2/ui/AspectRatioFrameLayout.java b/library/src/main/java/com/google/android/exoplayer2/ui/AspectRatioFrameLayout.java index f92903d65a..d3034a8bc8 100644 --- a/library/src/main/java/com/google/android/exoplayer2/ui/AspectRatioFrameLayout.java +++ b/library/src/main/java/com/google/android/exoplayer2/ui/AspectRatioFrameLayout.java @@ -34,7 +34,7 @@ public final class AspectRatioFrameLayout extends FrameLayout { * Resize modes for {@link AspectRatioFrameLayout}. */ @Retention(RetentionPolicy.SOURCE) - @IntDef({RESIZE_MODE_FIT, RESIZE_MODE_FIXED_WIDTH, RESIZE_MODE_FIXED_HEIGHT}) + @IntDef({RESIZE_MODE_FIT, RESIZE_MODE_FIXED_WIDTH, RESIZE_MODE_FIXED_HEIGHT, RESIZE_MODE_FILL}) public @interface ResizeMode {} /** @@ -49,6 +49,10 @@ public final class AspectRatioFrameLayout extends FrameLayout { * The height is fixed and the width is increased or decreased to obtain the desired aspect ratio. */ public static final int RESIZE_MODE_FIXED_HEIGHT = 2; + /** + * The specified aspect ratio is ignored. + */ + public static final int RESIZE_MODE_FILL = 3; /** * The {@link FrameLayout} will not resize itself if the fractional difference between its natural @@ -109,7 +113,7 @@ public final class AspectRatioFrameLayout extends FrameLayout { @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); - if (videoAspectRatio == 0) { + if (resizeMode == RESIZE_MODE_FILL || videoAspectRatio <= 0) { // Aspect ratio not set. return; } diff --git a/library/src/main/java/com/google/android/exoplayer2/ui/SimpleExoPlayerView.java b/library/src/main/java/com/google/android/exoplayer2/ui/SimpleExoPlayerView.java index ecb9319c1d..90a15da12c 100644 --- a/library/src/main/java/com/google/android/exoplayer2/ui/SimpleExoPlayerView.java +++ b/library/src/main/java/com/google/android/exoplayer2/ui/SimpleExoPlayerView.java @@ -18,6 +18,7 @@ package com.google.android.exoplayer2.ui; import android.annotation.TargetApi; import android.content.Context; import android.content.res.TypedArray; +import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.util.AttributeSet; import android.view.KeyEvent; @@ -56,8 +57,7 @@ public final class SimpleExoPlayerView extends FrameLayout { private static final int SURFACE_TYPE_SURFACE_VIEW = 1; private static final int SURFACE_TYPE_TEXTURE_VIEW = 2; - private final ViewGroup videoFrame; - private final AspectRatioFrameLayout aspectRatioVideoFrame; + private final AspectRatioFrameLayout contentFrame; private final View shutterView; private final View surfaceView; private final ImageView artworkView; @@ -112,40 +112,39 @@ public final class SimpleExoPlayerView extends FrameLayout { LayoutInflater.from(context).inflate(playerLayoutId, this); componentListener = new ComponentListener(); - videoFrame = (ViewGroup) findViewById(R.id.exo_video_frame); - if (videoFrame != null) { - if (videoFrame instanceof AspectRatioFrameLayout) { - aspectRatioVideoFrame = (AspectRatioFrameLayout) videoFrame; - setResizeModeRaw(aspectRatioVideoFrame, resizeMode); - } else { - aspectRatioVideoFrame = null; - } - shutterView = Assertions.checkNotNull(videoFrame.findViewById(R.id.exo_shutter)); - if (surfaceType != SURFACE_TYPE_NONE) { - ViewGroup.LayoutParams params = new ViewGroup.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); - surfaceView = surfaceType == SURFACE_TYPE_TEXTURE_VIEW ? new TextureView(context) - : new SurfaceView(context); - surfaceView.setLayoutParams(params); - videoFrame.addView(surfaceView, 0); - } else { - surfaceView = null; - } + // Content frame. + contentFrame = (AspectRatioFrameLayout) findViewById(R.id.exo_content_frame); + if (contentFrame != null) { + setResizeModeRaw(contentFrame, resizeMode); + } + + // Shutter view. + shutterView = findViewById(R.id.exo_shutter); + + // Create a surface view and insert it into the content frame, if there is one. + if (contentFrame != null && surfaceType != SURFACE_TYPE_NONE) { + ViewGroup.LayoutParams params = new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); + surfaceView = surfaceType == SURFACE_TYPE_TEXTURE_VIEW ? new TextureView(context) + : new SurfaceView(context); + surfaceView.setLayoutParams(params); + contentFrame.addView(surfaceView, 0); } else { - aspectRatioVideoFrame = null; - shutterView = null; surfaceView = null; } + // Artwork view. artworkView = (ImageView) findViewById(R.id.exo_artwork); this.useArtwork = useArtwork && artworkView != null; + // Subtitle view. subtitleView = (SubtitleView) findViewById(R.id.exo_subtitles); if (subtitleView != null) { subtitleView.setUserDefaultStyle(); subtitleView.setUserDefaultTextSize(); } + // Playback control view. PlaybackControlView controller = (PlaybackControlView) findViewById(R.id.exo_controller); if (controller != null) { controller.setRewindIncrementMs(rewindMs); @@ -223,8 +222,8 @@ public final class SimpleExoPlayerView extends FrameLayout { * @param resizeMode The resize mode. */ public void setResizeMode(@ResizeMode int resizeMode) { - Assertions.checkState(aspectRatioVideoFrame != null); - aspectRatioVideoFrame.setResizeMode(resizeMode); + Assertions.checkState(contentFrame != null); + contentFrame.setResizeMode(resizeMode); } /** @@ -403,16 +402,8 @@ public final class SimpleExoPlayerView extends FrameLayout { if (selection != null) { for (int j = 0; j < selection.length(); j++) { Metadata metadata = selection.getFormat(j).metadata; - if (metadata != null) { - for (int k = 0; k < metadata.length(); k++) { - Metadata.Entry metadataEntry = metadata.get(k); - if (metadataEntry instanceof ApicFrame) { - byte[] data = ((ApicFrame) metadataEntry).pictureData;; - artworkView.setImageBitmap(BitmapFactory.decodeByteArray(data, 0, data.length)); - artworkView.setVisibility(VISIBLE); - return; - } - } + if (metadata != null && setArtworkFromMetadata(metadata)) { + return; } } } @@ -422,6 +413,29 @@ public final class SimpleExoPlayerView extends FrameLayout { hideArtwork(); } + private boolean setArtworkFromMetadata(Metadata metadata) { + for (int i = 0; i < metadata.length(); i++) { + Metadata.Entry metadataEntry = metadata.get(i); + if (metadataEntry instanceof ApicFrame) { + byte[] bitmapData = ((ApicFrame) metadataEntry).pictureData; + Bitmap bitmap = BitmapFactory.decodeByteArray(bitmapData, 0, bitmapData.length); + if (bitmap != null) { + int bitmapWidth = bitmap.getWidth(); + int bitmapHeight = bitmap.getHeight(); + if (bitmapWidth > 0 && bitmapHeight > 0) { + if (contentFrame != null) { + contentFrame.setAspectRatio((float) bitmapWidth / bitmapHeight); + } + artworkView.setImageBitmap(bitmap); + artworkView.setVisibility(VISIBLE); + return true; + } + } + } + } + return false; + } + private void hideArtwork() { if (artworkView != null) { artworkView.setImageResource(android.R.color.transparent); // Clears any bitmap reference. @@ -457,9 +471,9 @@ public final class SimpleExoPlayerView extends FrameLayout { @Override public void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees, float pixelWidthHeightRatio) { - if (aspectRatioVideoFrame != null) { + if (contentFrame != null) { float aspectRatio = height == 0 ? 1 : (width * pixelWidthHeightRatio) / height; - aspectRatioVideoFrame.setAspectRatio(aspectRatio); + contentFrame.setAspectRatio(aspectRatio); } } diff --git a/library/src/main/res/layout/exo_simple_player_view.xml b/library/src/main/res/layout/exo_simple_player_view.xml index f89eafdd58..c4f34ef285 100644 --- a/library/src/main/res/layout/exo_simple_player_view.xml +++ b/library/src/main/res/layout/exo_simple_player_view.xml @@ -17,27 +17,29 @@ android:layout_height="match_parent" android:layout_width="match_parent"> - + + + + - - diff --git a/library/src/main/res/values/attrs.xml b/library/src/main/res/values/attrs.xml index 4be7cd590f..c3fc16495c 100644 --- a/library/src/main/res/values/attrs.xml +++ b/library/src/main/res/values/attrs.xml @@ -19,6 +19,7 @@ + diff --git a/library/src/main/res/values/ids.xml b/library/src/main/res/values/ids.xml index b768b75e89..00f35d9098 100644 --- a/library/src/main/res/values/ids.xml +++ b/library/src/main/res/values/ids.xml @@ -14,7 +14,7 @@ limitations under the License. --> - +