Commit 488c2d82 by olly Committed by Oliver Woodman

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
parent daf7b948
...@@ -39,6 +39,7 @@ import com.google.android.exoplayer2.upstream.Loader; ...@@ -39,6 +39,7 @@ import com.google.android.exoplayer2.upstream.Loader;
import com.google.android.exoplayer2.upstream.Loader.Loadable; import com.google.android.exoplayer2.upstream.Loader.Loadable;
import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.ConditionVariable; import com.google.android.exoplayer2.util.ConditionVariable;
import com.google.android.exoplayer2.util.MimeTypes;
import java.io.EOFException; import java.io.EOFException;
import java.io.IOException; import java.io.IOException;
...@@ -80,6 +81,8 @@ import java.io.IOException; ...@@ -80,6 +81,8 @@ import java.io.IOException;
private TrackGroupArray tracks; private TrackGroupArray tracks;
private long durationUs; private long durationUs;
private boolean[] trackEnabledStates; private boolean[] trackEnabledStates;
private boolean[] trackIsAudioVideoFlags;
private boolean haveAudioVideoTracks;
private long length; private long length;
private long lastSeekPositionUs; private long lastSeekPositionUs;
...@@ -259,12 +262,24 @@ import java.io.IOException; ...@@ -259,12 +262,24 @@ import java.io.IOException;
return C.TIME_END_OF_SOURCE; return C.TIME_END_OF_SOURCE;
} else if (isPendingReset()) { } else if (isPendingReset()) {
return pendingResetPositionUs; return pendingResetPositionUs;
}
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 { } else {
long largestQueuedTimestampUs = getLargestQueuedTimestampUs(); largestQueuedTimestampUs = getLargestQueuedTimestampUs();
}
return largestQueuedTimestampUs == Long.MIN_VALUE ? lastSeekPositionUs return largestQueuedTimestampUs == Long.MIN_VALUE ? lastSeekPositionUs
: largestQueuedTimestampUs; : largestQueuedTimestampUs;
} }
}
@Override @Override
public long seekToUs(long positionUs) { public long seekToUs(long positionUs) {
...@@ -404,10 +419,16 @@ import java.io.IOException; ...@@ -404,10 +419,16 @@ import java.io.IOException;
} }
loadCondition.close(); loadCondition.close();
TrackGroup[] trackArray = new TrackGroup[trackCount]; TrackGroup[] trackArray = new TrackGroup[trackCount];
trackIsAudioVideoFlags = new boolean[trackCount];
trackEnabledStates = new boolean[trackCount]; trackEnabledStates = new boolean[trackCount];
durationUs = seekMap.getDurationUs(); durationUs = seekMap.getDurationUs();
for (int i = 0; i < trackCount; i++) { 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); tracks = new TrackGroupArray(trackArray);
prepared = true; prepared = true;
......
...@@ -34,7 +34,7 @@ public final class AspectRatioFrameLayout extends FrameLayout { ...@@ -34,7 +34,7 @@ public final class AspectRatioFrameLayout extends FrameLayout {
* Resize modes for {@link AspectRatioFrameLayout}. * Resize modes for {@link AspectRatioFrameLayout}.
*/ */
@Retention(RetentionPolicy.SOURCE) @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 {} public @interface ResizeMode {}
/** /**
...@@ -49,6 +49,10 @@ public final class AspectRatioFrameLayout extends FrameLayout { ...@@ -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. * 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; 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 * The {@link FrameLayout} will not resize itself if the fractional difference between its natural
...@@ -109,7 +113,7 @@ public final class AspectRatioFrameLayout extends FrameLayout { ...@@ -109,7 +113,7 @@ public final class AspectRatioFrameLayout extends FrameLayout {
@Override @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec); super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (videoAspectRatio == 0) { if (resizeMode == RESIZE_MODE_FILL || videoAspectRatio <= 0) {
// Aspect ratio not set. // Aspect ratio not set.
return; return;
} }
......
...@@ -18,6 +18,7 @@ package com.google.android.exoplayer2.ui; ...@@ -18,6 +18,7 @@ package com.google.android.exoplayer2.ui;
import android.annotation.TargetApi; import android.annotation.TargetApi;
import android.content.Context; import android.content.Context;
import android.content.res.TypedArray; import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory; import android.graphics.BitmapFactory;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.view.KeyEvent; import android.view.KeyEvent;
...@@ -56,8 +57,7 @@ public final class SimpleExoPlayerView extends FrameLayout { ...@@ -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_SURFACE_VIEW = 1;
private static final int SURFACE_TYPE_TEXTURE_VIEW = 2; private static final int SURFACE_TYPE_TEXTURE_VIEW = 2;
private final ViewGroup videoFrame; private final AspectRatioFrameLayout contentFrame;
private final AspectRatioFrameLayout aspectRatioVideoFrame;
private final View shutterView; private final View shutterView;
private final View surfaceView; private final View surfaceView;
private final ImageView artworkView; private final ImageView artworkView;
...@@ -112,40 +112,39 @@ public final class SimpleExoPlayerView extends FrameLayout { ...@@ -112,40 +112,39 @@ public final class SimpleExoPlayerView extends FrameLayout {
LayoutInflater.from(context).inflate(playerLayoutId, this); LayoutInflater.from(context).inflate(playerLayoutId, this);
componentListener = new ComponentListener(); componentListener = new ComponentListener();
videoFrame = (ViewGroup) findViewById(R.id.exo_video_frame); // Content frame.
if (videoFrame != null) { contentFrame = (AspectRatioFrameLayout) findViewById(R.id.exo_content_frame);
if (videoFrame instanceof AspectRatioFrameLayout) { if (contentFrame != null) {
aspectRatioVideoFrame = (AspectRatioFrameLayout) videoFrame; setResizeModeRaw(contentFrame, resizeMode);
setResizeModeRaw(aspectRatioVideoFrame, resizeMode);
} else {
aspectRatioVideoFrame = null;
} }
shutterView = Assertions.checkNotNull(videoFrame.findViewById(R.id.exo_shutter));
if (surfaceType != SURFACE_TYPE_NONE) { // 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 params = new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
surfaceView = surfaceType == SURFACE_TYPE_TEXTURE_VIEW ? new TextureView(context) surfaceView = surfaceType == SURFACE_TYPE_TEXTURE_VIEW ? new TextureView(context)
: new SurfaceView(context); : new SurfaceView(context);
surfaceView.setLayoutParams(params); surfaceView.setLayoutParams(params);
videoFrame.addView(surfaceView, 0); contentFrame.addView(surfaceView, 0);
} else {
surfaceView = null;
}
} else { } else {
aspectRatioVideoFrame = null;
shutterView = null;
surfaceView = null; surfaceView = null;
} }
// Artwork view.
artworkView = (ImageView) findViewById(R.id.exo_artwork); artworkView = (ImageView) findViewById(R.id.exo_artwork);
this.useArtwork = useArtwork && artworkView != null; this.useArtwork = useArtwork && artworkView != null;
// Subtitle view.
subtitleView = (SubtitleView) findViewById(R.id.exo_subtitles); subtitleView = (SubtitleView) findViewById(R.id.exo_subtitles);
if (subtitleView != null) { if (subtitleView != null) {
subtitleView.setUserDefaultStyle(); subtitleView.setUserDefaultStyle();
subtitleView.setUserDefaultTextSize(); subtitleView.setUserDefaultTextSize();
} }
// Playback control view.
PlaybackControlView controller = (PlaybackControlView) findViewById(R.id.exo_controller); PlaybackControlView controller = (PlaybackControlView) findViewById(R.id.exo_controller);
if (controller != null) { if (controller != null) {
controller.setRewindIncrementMs(rewindMs); controller.setRewindIncrementMs(rewindMs);
...@@ -223,8 +222,8 @@ public final class SimpleExoPlayerView extends FrameLayout { ...@@ -223,8 +222,8 @@ public final class SimpleExoPlayerView extends FrameLayout {
* @param resizeMode The resize mode. * @param resizeMode The resize mode.
*/ */
public void setResizeMode(@ResizeMode int resizeMode) { public void setResizeMode(@ResizeMode int resizeMode) {
Assertions.checkState(aspectRatioVideoFrame != null); Assertions.checkState(contentFrame != null);
aspectRatioVideoFrame.setResizeMode(resizeMode); contentFrame.setResizeMode(resizeMode);
} }
/** /**
...@@ -403,25 +402,40 @@ public final class SimpleExoPlayerView extends FrameLayout { ...@@ -403,25 +402,40 @@ public final class SimpleExoPlayerView extends FrameLayout {
if (selection != null) { if (selection != null) {
for (int j = 0; j < selection.length(); j++) { for (int j = 0; j < selection.length(); j++) {
Metadata metadata = selection.getFormat(j).metadata; Metadata metadata = selection.getFormat(j).metadata;
if (metadata != null) { if (metadata != null && setArtworkFromMetadata(metadata)) {
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; return;
} }
} }
} }
} }
} }
}
}
// Artwork disabled or unavailable. // Artwork disabled or unavailable.
hideArtwork(); 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() { private void hideArtwork() {
if (artworkView != null) { if (artworkView != null) {
artworkView.setImageResource(android.R.color.transparent); // Clears any bitmap reference. artworkView.setImageResource(android.R.color.transparent); // Clears any bitmap reference.
...@@ -457,9 +471,9 @@ public final class SimpleExoPlayerView extends FrameLayout { ...@@ -457,9 +471,9 @@ public final class SimpleExoPlayerView extends FrameLayout {
@Override @Override
public void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees, public void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees,
float pixelWidthHeightRatio) { float pixelWidthHeightRatio) {
if (aspectRatioVideoFrame != null) { if (contentFrame != null) {
float aspectRatio = height == 0 ? 1 : (width * pixelWidthHeightRatio) / height; float aspectRatio = height == 0 ? 1 : (width * pixelWidthHeightRatio) / height;
aspectRatioVideoFrame.setAspectRatio(aspectRatio); contentFrame.setAspectRatio(aspectRatio);
} }
} }
......
...@@ -17,27 +17,29 @@ ...@@ -17,27 +17,29 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_width="match_parent"> android:layout_width="match_parent">
<com.google.android.exoplayer2.ui.AspectRatioFrameLayout android:id="@id/exo_video_frame" <com.google.android.exoplayer2.ui.AspectRatioFrameLayout android:id="@id/exo_content_frame"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_gravity="center"> android:layout_gravity="center">
<!-- Video surface will be inserted as the first child of the content frame. -->
<View android:id="@id/exo_shutter" <View android:id="@id/exo_shutter"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="@android:color/black"/> android:background="@android:color/black"/>
<ImageView android:id="@id/exo_artwork"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitXY"/>
<com.google.android.exoplayer2.ui.SubtitleView android:id="@id/exo_subtitles" <com.google.android.exoplayer2.ui.SubtitleView android:id="@id/exo_subtitles"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"/> android:layout_height="match_parent"/>
</com.google.android.exoplayer2.ui.AspectRatioFrameLayout> </com.google.android.exoplayer2.ui.AspectRatioFrameLayout>
<ImageView android:id="@id/exo_artwork"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitCenter"/>
<View android:id="@id/exo_controller_placeholder" <View android:id="@id/exo_controller_placeholder"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"/> android:layout_height="match_parent"/>
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
<enum name="fit" value="0"/> <enum name="fit" value="0"/>
<enum name="fixed_width" value="1"/> <enum name="fixed_width" value="1"/>
<enum name="fixed_height" value="2"/> <enum name="fixed_height" value="2"/>
<enum name="fill" value="3"/>
</attr> </attr>
<!-- Must be kept in sync with SimpleExoPlayerView --> <!-- Must be kept in sync with SimpleExoPlayerView -->
<attr name="surface_type" format="enum"> <attr name="surface_type" format="enum">
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
limitations under the License. limitations under the License.
--> -->
<resources> <resources>
<item name="exo_video_frame" type="id"/> <item name="exo_content_frame" type="id"/>
<item name="exo_shutter" type="id"/> <item name="exo_shutter" type="id"/>
<item name="exo_subtitles" type="id"/> <item name="exo_subtitles" type="id"/>
<item name="exo_artwork" type="id"/> <item name="exo_artwork" type="id"/>
......
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