Use AspectRatioVideoView instead of standard video view

This commit is contained in:
daniel oeh 2013-12-30 00:23:18 +01:00
parent 85800d14ac
commit b038f3c877
6 changed files with 172 additions and 27 deletions

View File

@ -1,13 +1,14 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical" > android:orientation="vertical">
<VideoView <de.danoeh.antennapod.view.AspectRatioVideoView
android:id="@+id/videoview" android:id="@+id/videoview"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" /> android:layout_height="wrap_content"
android:layout_gravity="center"/>
<ProgressBar <ProgressBar
android:id="@+id/progressIndicator" android:id="@+id/progressIndicator"
@ -15,7 +16,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center" android:layout_gravity="center"
android:visibility="invisible" android:visibility="invisible"
android:indeterminateOnly="true" /> android:indeterminateOnly="true"/>
<ImageButton <ImageButton
android:id="@+id/butPlay" android:id="@+id/butPlay"
@ -24,7 +25,7 @@
android:layout_gravity="center" android:layout_gravity="center"
android:scaleType="fitXY" android:scaleType="fitXY"
android:background="@drawable/overlay_button_circle_background" android:background="@drawable/overlay_button_circle_background"
android:src="@drawable/ic_action_pause_over_video" /> android:src="@drawable/ic_action_pause_over_video"/>
<LinearLayout <LinearLayout
android:id="@+id/overlay" android:id="@+id/overlay"
@ -32,14 +33,14 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="bottom|center" android:layout_gravity="bottom|center"
android:background="#80000000" android:background="#80000000"
android:orientation="vertical" > android:orientation="vertical">
<RelativeLayout <RelativeLayout
android:id="@+id/timecontrol" android:id="@+id/timecontrol"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="50dp" android:layout_height="50dp"
android:paddingTop="8dp" android:paddingTop="8dp"
android:layout_marginBottom="4dp" > android:layout_marginBottom="4dp">
<TextView <TextView
android:id="@+id/txtvPosition" android:id="@+id/txtvPosition"
@ -53,7 +54,7 @@
android:layout_marginTop="4dp" android:layout_marginTop="4dp"
android:textColor="@color/white" android:textColor="@color/white"
android:textStyle="bold" android:textStyle="bold"
android:text="@string/position_default_label" /> android:text="@string/position_default_label"/>
<TextView <TextView
android:id="@+id/txtvLength" android:id="@+id/txtvLength"
@ -67,7 +68,7 @@
android:layout_marginTop="4dp" android:layout_marginTop="4dp"
android:textColor="@color/white" android:textColor="@color/white"
android:textStyle="bold" android:textStyle="bold"
android:text="@string/position_default_label" /> android:text="@string/position_default_label"/>
<SeekBar <SeekBar
android:id="@+id/sbPosition" android:id="@+id/sbPosition"
@ -75,7 +76,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_toLeftOf="@+id/txtvLength" android:layout_toLeftOf="@+id/txtvLength"
android:layout_toRightOf="@+id/txtvPosition" android:layout_toRightOf="@+id/txtvPosition"
android:max="500" /> android:max="500"/>
</RelativeLayout> </RelativeLayout>
</LinearLayout> </LinearLayout>

View File

@ -7,21 +7,21 @@ import android.os.AsyncTask;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.util.Log; import android.util.Log;
import android.util.Pair;
import android.view.*; import android.view.*;
import android.view.animation.Animation; import android.view.animation.Animation;
import android.view.animation.AnimationUtils; import android.view.animation.AnimationUtils;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.ProgressBar; import android.widget.ProgressBar;
import android.widget.SeekBar; import android.widget.SeekBar;
import android.widget.VideoView;
import de.danoeh.antennapod.AppConfig; import de.danoeh.antennapod.AppConfig;
import de.danoeh.antennapod.R; import de.danoeh.antennapod.R;
import de.danoeh.antennapod.feed.MediaType; import de.danoeh.antennapod.feed.MediaType;
import de.danoeh.antennapod.preferences.UserPreferences;
import de.danoeh.antennapod.service.playback.PlaybackService; import de.danoeh.antennapod.service.playback.PlaybackService;
import de.danoeh.antennapod.service.playback.PlayerStatus; import de.danoeh.antennapod.service.playback.PlayerStatus;
import de.danoeh.antennapod.util.playback.ExternalMedia; import de.danoeh.antennapod.util.playback.ExternalMedia;
import de.danoeh.antennapod.util.playback.Playable; import de.danoeh.antennapod.util.playback.Playable;
import de.danoeh.antennapod.view.AspectRatioVideoView;
/** /**
* Activity for playing video files. * Activity for playing video files.
@ -37,7 +37,7 @@ public class VideoplayerActivity extends MediaplayerActivity {
private VideoControlsHider videoControlsToggler; private VideoControlsHider videoControlsToggler;
private LinearLayout videoOverlay; private LinearLayout videoOverlay;
private VideoView videoview; private AspectRatioVideoView videoview;
private ProgressBar progressIndicator; private ProgressBar progressIndicator;
@Override @Override
@ -103,7 +103,7 @@ public class VideoplayerActivity extends MediaplayerActivity {
protected void setupGUI() { protected void setupGUI() {
super.setupGUI(); super.setupGUI();
videoOverlay = (LinearLayout) findViewById(R.id.overlay); videoOverlay = (LinearLayout) findViewById(R.id.overlay);
videoview = (VideoView) findViewById(R.id.videoview); videoview = (AspectRatioVideoView) findViewById(R.id.videoview);
progressIndicator = (ProgressBar) findViewById(R.id.progressIndicator); progressIndicator = (ProgressBar) findViewById(R.id.progressIndicator);
videoview.getHolder().addCallback(surfaceHolderCallback); videoview.getHolder().addCallback(surfaceHolderCallback);
videoview.setOnTouchListener(onVideoviewTouched); videoview.setOnTouchListener(onVideoviewTouched);
@ -119,6 +119,14 @@ public class VideoplayerActivity extends MediaplayerActivity {
if (AppConfig.DEBUG) if (AppConfig.DEBUG)
Log.d(TAG, Log.d(TAG,
"Videosurface already created, setting videosurface now"); "Videosurface already created, setting videosurface now");
Pair<Integer, Integer> videoSize = controller.getVideoSize();
if (videoSize != null && videoSize.first > 0 && videoSize.second > 0) {
if (AppConfig.DEBUG) Log.d(TAG, "Width,height of video: " + videoSize.first + ", " + videoSize.second);
videoview.setVideoSize(videoSize.first, videoSize.second);
} else {
Log.e(TAG, "Could not determine video size");
}
controller.setVideoSurface(videoview.getHolder()); controller.setVideoSurface(videoview.getHolder());
} }
} }

View File

@ -19,6 +19,7 @@ import android.os.IBinder;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import android.support.v4.app.NotificationCompat; import android.support.v4.app.NotificationCompat;
import android.util.Log; import android.util.Log;
import android.util.Pair;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.SurfaceHolder; import android.view.SurfaceHolder;
import de.danoeh.antennapod.AppConfig; import de.danoeh.antennapod.AppConfig;
@ -991,6 +992,10 @@ public class PlaybackService extends Service {
return mediaPlayer.isStreaming(); return mediaPlayer.isStreaming();
} }
public Pair<Integer, Integer> getVideoSize() {
return mediaPlayer.getVideoSize();
}
private void setCurrentlyPlayingMedia(long id) { private void setCurrentlyPlayingMedia(long id) {
SharedPreferences.Editor editor = PreferenceManager SharedPreferences.Editor editor = PreferenceManager
.getDefaultSharedPreferences(getApplicationContext()).edit(); .getDefaultSharedPreferences(getApplicationContext()).edit();

View File

@ -3,11 +3,11 @@ package de.danoeh.antennapod.service.playback;
import android.content.ComponentName; import android.content.ComponentName;
import android.content.Context; import android.content.Context;
import android.media.AudioManager; import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.RemoteControlClient; import android.media.RemoteControlClient;
import android.os.Handler; import android.os.Handler;
import android.os.Message; import android.os.Message;
import android.util.Log; import android.util.Log;
import android.util.Pair;
import android.view.SurfaceHolder; import android.view.SurfaceHolder;
import de.danoeh.antennapod.AppConfig; import de.danoeh.antennapod.AppConfig;
import de.danoeh.antennapod.feed.Chapter; import de.danoeh.antennapod.feed.Chapter;
@ -47,6 +47,7 @@ public class PlaybackServiceMediaPlayer {
private volatile MediaType mediaType; private volatile MediaType mediaType;
private volatile AtomicBoolean startWhenPrepared; private volatile AtomicBoolean startWhenPrepared;
private volatile boolean pausedBecauseOfTransientAudiofocusLoss; private volatile boolean pausedBecauseOfTransientAudiofocusLoss;
private volatile Pair<Integer, Integer> videoSize;
/** /**
* Some asynchronous calls might change the state of the MediaPlayer object. Therefore calls in other threads * Some asynchronous calls might change the state of the MediaPlayer object. Therefore calls in other threads
@ -76,15 +77,9 @@ public class PlaybackServiceMediaPlayer {
pausedBecauseOfTransientAudiofocusLoss = false; pausedBecauseOfTransientAudiofocusLoss = false;
mediaType = MediaType.UNKNOWN; mediaType = MediaType.UNKNOWN;
playerStatus = PlayerStatus.STOPPED; playerStatus = PlayerStatus.STOPPED;
videoSize = null;
} }
private Handler.Callback handlerCallback = new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
return false;
}
};
/** /**
* Starts or prepares playback of the specified Playable object. If another Playable object is already being played, the currently playing * Starts or prepares playback of the specified Playable object. If another Playable object is already being played, the currently playing
* episode will be stopped and replaced with the new Playable object. If the Playable object is already being played, the method will * episode will be stopped and replaced with the new Playable object. If the Playable object is already being played, the method will
@ -156,6 +151,7 @@ public class PlaybackServiceMediaPlayer {
this.media = playable; this.media = playable;
this.stream = stream; this.stream = stream;
this.mediaType = media.getMediaType(); this.mediaType = media.getMediaType();
this.videoSize = null;
PlaybackServiceMediaPlayer.this.startWhenPrepared.set(startWhenPrepared); PlaybackServiceMediaPlayer.this.startWhenPrepared.set(startWhenPrepared);
setPlayerStatus(PlayerStatus.INITIALIZING, media); setPlayerStatus(PlayerStatus.INITIALIZING, media);
try { try {
@ -169,7 +165,7 @@ public class PlaybackServiceMediaPlayer {
if (mediaType == MediaType.VIDEO) { if (mediaType == MediaType.VIDEO) {
VideoPlayer vp = (VideoPlayer) mediaPlayer; VideoPlayer vp = (VideoPlayer) mediaPlayer;
// vp.setVideoScalingMode(MediaPlayer.VIDEO_SCALING_MODE_SCALE_TO_FIT); // vp.setVideoScalingMode(MediaPlayer.VIDEO_SCALING_MODE_SCALE_TO_FIT);
} }
if (prepareImmediately) { if (prepareImmediately) {
@ -324,6 +320,11 @@ public class PlaybackServiceMediaPlayer {
if (AppConfig.DEBUG) if (AppConfig.DEBUG)
Log.d(TAG, "Resource prepared"); Log.d(TAG, "Resource prepared");
if (mediaType == MediaType.VIDEO) {
VideoPlayer vp = (VideoPlayer) mediaPlayer;
videoSize = new Pair<Integer, Integer>(vp.getVideoWidth(), vp.getVideoHeight());
}
mediaPlayer.seekTo(media.getPosition()); mediaPlayer.seekTo(media.getPosition());
if (media.getDuration() == 0) { if (media.getDuration() == 0) {
if (AppConfig.DEBUG) if (AppConfig.DEBUG)
@ -380,8 +381,8 @@ public class PlaybackServiceMediaPlayer {
|| playerStatus == PlayerStatus.PAUSED || playerStatus == PlayerStatus.PAUSED
|| playerStatus == PlayerStatus.PREPARED) { || playerStatus == PlayerStatus.PREPARED) {
if (stream) { if (stream) {
// statusBeforeSeeking = playerStatus; // statusBeforeSeeking = playerStatus;
// setPlayerStatus(PlayerStatus.SEEKING, media); // setPlayerStatus(PlayerStatus.SEEKING, media);
} }
mediaPlayer.seekTo(t); mediaPlayer.seekTo(t);
@ -603,6 +604,30 @@ public class PlaybackServiceMediaPlayer {
}); });
} }
/**
* Return width and height of the currently playing video as a pair.
*
* @return Width and height as a Pair or null if the video size could not be determined. The method might still
* return an invalid non-null value if the getVideoWidth() and getVideoHeight() methods of the media player return
* invalid values.
*/
public Pair<Integer, Integer> getVideoSize() {
if (!playerLock.tryLock()) {
// use cached value if lock can't be aquired
return videoSize;
}
Pair<Integer, Integer> res;
if (mediaPlayer == null || playerStatus == PlayerStatus.ERROR || mediaType != MediaType.VIDEO) {
res = null;
} else {
VideoPlayer vp = (VideoPlayer) mediaPlayer;
videoSize = new Pair<Integer, Integer>(vp.getVideoWidth(), vp.getVideoHeight());
res = videoSize;
}
playerLock.unlock();
return res;
}
/** /**
* Returns a PSMInfo object that contains information about the current state of the PSMP object. * Returns a PSMInfo object that contains information about the current state of the PSMP object.
* *

View File

@ -7,6 +7,7 @@ import android.os.AsyncTask;
import android.os.IBinder; import android.os.IBinder;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import android.util.Log; import android.util.Log;
import android.util.Pair;
import android.view.SurfaceHolder; import android.view.SurfaceHolder;
import android.view.View; import android.view.View;
import android.view.View.OnClickListener; import android.view.View.OnClickListener;
@ -698,6 +699,14 @@ public abstract class PlaybackController {
return false; return false;
} }
public Pair<Integer, Integer> getVideoSize() {
if (playbackService != null) {
return playbackService.getVideoSize();
} else {
return null;
}
}
/** /**
* Returns true if PlaybackController can communicate with the playback * Returns true if PlaybackController can communicate with the playback

View File

@ -0,0 +1,97 @@
package de.danoeh.antennapod.view;
/*
* Copyright (C) Google Inc. All rights reserved.
*
* 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.
*/
import android.content.Context;
import android.util.AttributeSet;
import android.widget.VideoView;
public class AspectRatioVideoView extends VideoView {
private int mVideoWidth;
private int mVideoHeight;
public AspectRatioVideoView(Context context) {
this(context, null);
}
public AspectRatioVideoView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public AspectRatioVideoView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mVideoWidth = 0;
mVideoHeight = 0;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (mVideoWidth <= 0 || mVideoHeight <= 0) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
return;
}
float heightRatio = (float) mVideoHeight / (float) getHeight();
float widthRatio = (float) mVideoWidth / (float) getWidth();
int scaledHeight;
int scaledWidth;
if (heightRatio > widthRatio) {
scaledHeight = (int) Math.ceil((float) mVideoHeight
/ heightRatio);
scaledWidth = (int) Math.ceil((float) mVideoWidth
/ heightRatio);
} else {
scaledHeight = (int) Math.ceil((float) mVideoHeight
/ widthRatio);
scaledWidth = (int) Math.ceil((float) mVideoWidth
/ widthRatio);
}
setMeasuredDimension(scaledWidth, scaledHeight);
}
/**
* Source code originally from:
* http://clseto.mysinablog.com/index.php?op=ViewArticle&articleId=2992625
*
* @param videoWidth
* @param videoHeight
*/
public void setVideoSize(int videoWidth, int videoHeight) {
// Set the new video size
mVideoWidth = videoWidth;
mVideoHeight = videoHeight;
/**
* If this isn't set the video is stretched across the
* SurfaceHolders display surface (i.e. the SurfaceHolder
* as the same size and the video is drawn to fit this
* display area). We want the size to be the video size
* and allow the aspectratio to handle how the surface is shown
*/
getHolder().setFixedSize(videoWidth, videoHeight);
requestLayout();
invalidate();
}
}