diff --git a/res/layout-land/videoplayer_activity.xml b/res/layout-land/videoplayer_activity.xml index 344e86ddd..13d075b1c 100644 --- a/res/layout-land/videoplayer_activity.xml +++ b/res/layout-land/videoplayer_activity.xml @@ -1,13 +1,14 @@ + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> - + android:layout_height="wrap_content" + android:layout_gravity="center"/> + android:indeterminateOnly="true"/> + android:src="@drawable/ic_action_pause_over_video"/> + android:orientation="vertical"> + android:layout_marginBottom="4dp"> + android:text="@string/position_default_label"/> + android:text="@string/position_default_label"/> + android:max="500"/> diff --git a/src/de/danoeh/antennapod/activity/VideoplayerActivity.java b/src/de/danoeh/antennapod/activity/VideoplayerActivity.java index c980c14cd..564dfb135 100644 --- a/src/de/danoeh/antennapod/activity/VideoplayerActivity.java +++ b/src/de/danoeh/antennapod/activity/VideoplayerActivity.java @@ -7,21 +7,21 @@ import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; import android.util.Log; +import android.util.Pair; import android.view.*; import android.view.animation.Animation; import android.view.animation.AnimationUtils; import android.widget.LinearLayout; import android.widget.ProgressBar; import android.widget.SeekBar; -import android.widget.VideoView; import de.danoeh.antennapod.AppConfig; import de.danoeh.antennapod.R; 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.PlayerStatus; import de.danoeh.antennapod.util.playback.ExternalMedia; import de.danoeh.antennapod.util.playback.Playable; +import de.danoeh.antennapod.view.AspectRatioVideoView; /** * Activity for playing video files. @@ -37,7 +37,7 @@ public class VideoplayerActivity extends MediaplayerActivity { private VideoControlsHider videoControlsToggler; private LinearLayout videoOverlay; - private VideoView videoview; + private AspectRatioVideoView videoview; private ProgressBar progressIndicator; @Override @@ -103,7 +103,7 @@ public class VideoplayerActivity extends MediaplayerActivity { protected void setupGUI() { super.setupGUI(); videoOverlay = (LinearLayout) findViewById(R.id.overlay); - videoview = (VideoView) findViewById(R.id.videoview); + videoview = (AspectRatioVideoView) findViewById(R.id.videoview); progressIndicator = (ProgressBar) findViewById(R.id.progressIndicator); videoview.getHolder().addCallback(surfaceHolderCallback); videoview.setOnTouchListener(onVideoviewTouched); @@ -119,6 +119,14 @@ public class VideoplayerActivity extends MediaplayerActivity { if (AppConfig.DEBUG) Log.d(TAG, "Videosurface already created, setting videosurface now"); + + Pair 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()); } } diff --git a/src/de/danoeh/antennapod/service/playback/PlaybackService.java b/src/de/danoeh/antennapod/service/playback/PlaybackService.java index 2b8a04a84..6cebce02c 100644 --- a/src/de/danoeh/antennapod/service/playback/PlaybackService.java +++ b/src/de/danoeh/antennapod/service/playback/PlaybackService.java @@ -19,6 +19,7 @@ import android.os.IBinder; import android.preference.PreferenceManager; import android.support.v4.app.NotificationCompat; import android.util.Log; +import android.util.Pair; import android.view.KeyEvent; import android.view.SurfaceHolder; import de.danoeh.antennapod.AppConfig; @@ -991,6 +992,10 @@ public class PlaybackService extends Service { return mediaPlayer.isStreaming(); } + public Pair getVideoSize() { + return mediaPlayer.getVideoSize(); + } + private void setCurrentlyPlayingMedia(long id) { SharedPreferences.Editor editor = PreferenceManager .getDefaultSharedPreferences(getApplicationContext()).edit(); diff --git a/src/de/danoeh/antennapod/service/playback/PlaybackServiceMediaPlayer.java b/src/de/danoeh/antennapod/service/playback/PlaybackServiceMediaPlayer.java index 8ffd55be6..04c088d0d 100644 --- a/src/de/danoeh/antennapod/service/playback/PlaybackServiceMediaPlayer.java +++ b/src/de/danoeh/antennapod/service/playback/PlaybackServiceMediaPlayer.java @@ -3,11 +3,11 @@ package de.danoeh.antennapod.service.playback; import android.content.ComponentName; import android.content.Context; import android.media.AudioManager; -import android.media.MediaPlayer; import android.media.RemoteControlClient; import android.os.Handler; import android.os.Message; import android.util.Log; +import android.util.Pair; import android.view.SurfaceHolder; import de.danoeh.antennapod.AppConfig; import de.danoeh.antennapod.feed.Chapter; @@ -47,6 +47,7 @@ public class PlaybackServiceMediaPlayer { private volatile MediaType mediaType; private volatile AtomicBoolean startWhenPrepared; private volatile boolean pausedBecauseOfTransientAudiofocusLoss; + private volatile Pair videoSize; /** * 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; mediaType = MediaType.UNKNOWN; 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 * 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.stream = stream; this.mediaType = media.getMediaType(); + this.videoSize = null; PlaybackServiceMediaPlayer.this.startWhenPrepared.set(startWhenPrepared); setPlayerStatus(PlayerStatus.INITIALIZING, media); try { @@ -169,7 +165,7 @@ public class PlaybackServiceMediaPlayer { if (mediaType == MediaType.VIDEO) { VideoPlayer vp = (VideoPlayer) mediaPlayer; - // vp.setVideoScalingMode(MediaPlayer.VIDEO_SCALING_MODE_SCALE_TO_FIT); + // vp.setVideoScalingMode(MediaPlayer.VIDEO_SCALING_MODE_SCALE_TO_FIT); } if (prepareImmediately) { @@ -324,6 +320,11 @@ public class PlaybackServiceMediaPlayer { if (AppConfig.DEBUG) Log.d(TAG, "Resource prepared"); + if (mediaType == MediaType.VIDEO) { + VideoPlayer vp = (VideoPlayer) mediaPlayer; + videoSize = new Pair(vp.getVideoWidth(), vp.getVideoHeight()); + } + mediaPlayer.seekTo(media.getPosition()); if (media.getDuration() == 0) { if (AppConfig.DEBUG) @@ -380,8 +381,8 @@ public class PlaybackServiceMediaPlayer { || playerStatus == PlayerStatus.PAUSED || playerStatus == PlayerStatus.PREPARED) { if (stream) { - // statusBeforeSeeking = playerStatus; - // setPlayerStatus(PlayerStatus.SEEKING, media); + // statusBeforeSeeking = playerStatus; + // setPlayerStatus(PlayerStatus.SEEKING, media); } 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 getVideoSize() { + if (!playerLock.tryLock()) { + // use cached value if lock can't be aquired + return videoSize; + } + Pair res; + if (mediaPlayer == null || playerStatus == PlayerStatus.ERROR || mediaType != MediaType.VIDEO) { + res = null; + } else { + VideoPlayer vp = (VideoPlayer) mediaPlayer; + videoSize = new Pair(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. * diff --git a/src/de/danoeh/antennapod/util/playback/PlaybackController.java b/src/de/danoeh/antennapod/util/playback/PlaybackController.java index 0d1e62909..74de81217 100644 --- a/src/de/danoeh/antennapod/util/playback/PlaybackController.java +++ b/src/de/danoeh/antennapod/util/playback/PlaybackController.java @@ -7,6 +7,7 @@ import android.os.AsyncTask; import android.os.IBinder; import android.preference.PreferenceManager; import android.util.Log; +import android.util.Pair; import android.view.SurfaceHolder; import android.view.View; import android.view.View.OnClickListener; @@ -698,6 +699,14 @@ public abstract class PlaybackController { return false; } + public Pair getVideoSize() { + if (playbackService != null) { + return playbackService.getVideoSize(); + } else { + return null; + } + } + /** * Returns true if PlaybackController can communicate with the playback diff --git a/src/de/danoeh/antennapod/view/AspectRatioVideoView.java b/src/de/danoeh/antennapod/view/AspectRatioVideoView.java new file mode 100644 index 000000000..f930c912a --- /dev/null +++ b/src/de/danoeh/antennapod/view/AspectRatioVideoView.java @@ -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(); + } + +}