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
This commit is contained in:
olly 2016-10-31 07:10:31 -07:00 committed by Oliver Woodman
parent daf7b948a1
commit 488c2d8270
6 changed files with 93 additions and 51 deletions

View file

@ -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;

View file

@ -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;
}

View file

@ -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);
}
}

View file

@ -17,27 +17,29 @@
android:layout_height="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_height="match_parent"
android:layout_gravity="center">
<!-- Video surface will be inserted as the first child of the content frame. -->
<View android:id="@id/exo_shutter"
android:layout_width="match_parent"
android:layout_height="match_parent"
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"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</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"
android:layout_width="match_parent"
android:layout_height="match_parent"/>

View file

@ -19,6 +19,7 @@
<enum name="fit" value="0"/>
<enum name="fixed_width" value="1"/>
<enum name="fixed_height" value="2"/>
<enum name="fill" value="3"/>
</attr>
<!-- Must be kept in sync with SimpleExoPlayerView -->
<attr name="surface_type" format="enum">

View file

@ -14,7 +14,7 @@
limitations under the License.
-->
<resources>
<item name="exo_video_frame" type="id"/>
<item name="exo_content_frame" type="id"/>
<item name="exo_shutter" type="id"/>
<item name="exo_subtitles" type="id"/>
<item name="exo_artwork" type="id"/>