From c8a516a713ef7af04339639968567dfc362e2706 Mon Sep 17 00:00:00 2001 From: Chirag Maheshwari Date: Wed, 8 Mar 2017 21:25:22 +0530 Subject: [PATCH 1/4] adds subtitle view in video player --- .../activity/ServerFileVideoActivity.java | 30 ++++++++++++++- .../res/layout/activity_server_file_video.xml | 37 ++++++++++++++----- src/main/res/layout/subtitles_surface.xml | 6 +++ 3 files changed, 61 insertions(+), 12 deletions(-) create mode 100644 src/main/res/layout/subtitles_surface.xml diff --git a/src/main/java/org/amahi/anywhere/activity/ServerFileVideoActivity.java b/src/main/java/org/amahi/anywhere/activity/ServerFileVideoActivity.java index 1e24909cc..8d1817e50 100644 --- a/src/main/java/org/amahi/anywhere/activity/ServerFileVideoActivity.java +++ b/src/main/java/org/amahi/anywhere/activity/ServerFileVideoActivity.java @@ -19,12 +19,14 @@ package org.amahi.anywhere.activity; +import android.annotation.TargetApi; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.content.res.Configuration; import android.graphics.PixelFormat; +import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; @@ -35,6 +37,7 @@ import android.view.SurfaceView; import android.view.View; import android.view.ViewGroup; +import android.view.ViewStub; import android.widget.FrameLayout; import android.widget.MediaController; @@ -63,6 +66,8 @@ public class ServerFileVideoActivity extends AppCompatActivity implements MediaPlayer.EventListener, View.OnLayoutChangeListener { + private static final boolean ENABLE_SUBTITLES = true; + private VideoService videoService; private MediaControls videoControls; private FullScreenHelper fullScreen; @@ -75,6 +80,8 @@ public class ServerFileVideoActivity extends AppCompatActivity implements private int mVideoSarNum = 0; private int mVideoSarDen = 0; + private SurfaceView mSubtitlesSurface = null; + private enum SurfaceSizes { SURFACE_BEST_FIT, SURFACE_FIT_SCREEN, @@ -97,6 +104,8 @@ protected void onCreate(Bundle savedInstanceState) { setUpHomeNavigation(); + setUpViews(); + setUpVideo(); setUpFullScreen(); @@ -113,6 +122,15 @@ private void setUpHomeNavigation() { getSupportActionBar().setIcon(R.drawable.ic_launcher); } + private void setUpViews() { + if (ENABLE_SUBTITLES) { + final ViewStub stub = (ViewStub) findViewById(R.id.subtitles_stub); + mSubtitlesSurface = (SurfaceView) stub.inflate(); + mSubtitlesSurface.setZOrderMediaOverlay(true); + mSubtitlesSurface.getHolder().setFormat(PixelFormat.TRANSLUCENT); + } + } + private void setUpVideo() { setUpVideoTitle(); } @@ -185,6 +203,8 @@ private void setUpVideoView() { surfaceHolder.setKeepScreenOn(true); final IVLCVout vlcVout = getMediaPlayer().getVLCVout(); vlcVout.setVideoView(getSurface()); + if (mSubtitlesSurface != null) + vlcVout.setSubtitlesView(mSubtitlesSurface); vlcVout.attachViews(this); getMediaPlayer().setEventListener(this); } @@ -194,7 +214,7 @@ private SurfaceView getSurface() { } private FrameLayout getSurfaceFrame() { - return (FrameLayout) findViewById(R.id.layout_content); + return (FrameLayout) findViewById(R.id.video_surface_frame); } private void setUpVideoControls() { @@ -244,7 +264,7 @@ private void showThenAutoHideControls() { } private void showVideo() { - ViewDirector.of(this, R.id.animator).show(R.id.layout_content); + ViewDirector.of(this, R.id.animator).show(R.id.video_surface_frame); } private ServerShare getVideoShare() { @@ -271,6 +291,7 @@ public void run() { } }; + @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) @Override public void onNewVideoLayout(IVLCVout vout, int width, int height, int visibleWidth, int visibleHeight, int sarNum, int sarDen) { mVideoWidth = width; @@ -372,12 +393,17 @@ private void updateVideoSurfaces() { lp.width = (int) Math.ceil(dw * mVideoWidth / mVideoVisibleWidth); lp.height = (int) Math.ceil(dh * mVideoHeight / mVideoVisibleHeight); getSurface().setLayoutParams(lp); + if (mSubtitlesSurface != null) + mSubtitlesSurface.setLayoutParams(lp); + // set frame size (crop if necessary) lp = getSurfaceFrame().getLayoutParams(); lp.width = (int) Math.floor(dw); lp.height = (int) Math.floor(dh); getSurfaceFrame().setLayoutParams(lp); getSurface().invalidate(); + if (mSubtitlesSurface != null) + mSubtitlesSurface.invalidate(); } private void changeMediaPlayerLayout(int displayW, int displayH) { diff --git a/src/main/res/layout/activity_server_file_video.xml b/src/main/res/layout/activity_server_file_video.xml index d6986f5fc..a41ab480a 100644 --- a/src/main/res/layout/activity_server_file_video.xml +++ b/src/main/res/layout/activity_server_file_video.xml @@ -17,7 +17,9 @@ ~ along with Amahi. If not, see . --> - + android:layout_gravity="center"/> + android:layout_height="match_parent" + android:background="@android:color/background_dark" + android:keepScreenOn="true"> - + android:foregroundGravity="clip_horizontal|clip_vertical" + tools:ignore="true"> + + + + + + + android:layout_height="match_parent"/> - - \ No newline at end of file diff --git a/src/main/res/layout/subtitles_surface.xml b/src/main/res/layout/subtitles_surface.xml new file mode 100644 index 000000000..701bc75fd --- /dev/null +++ b/src/main/res/layout/subtitles_surface.xml @@ -0,0 +1,6 @@ + + + \ No newline at end of file From a5ce869605a3df5c34822ae6fc6c998a9ed96ab9 Mon Sep 17 00:00:00 2001 From: Chirag Maheshwari Date: Tue, 21 Mar 2017 02:31:35 +0530 Subject: [PATCH 2/4] adds buffer percentage and error toast --- .../amahi/anywhere/activity/ServerFileVideoActivity.java | 9 +++++---- src/main/res/values/strings.xml | 1 + 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/amahi/anywhere/activity/ServerFileVideoActivity.java b/src/main/java/org/amahi/anywhere/activity/ServerFileVideoActivity.java index 8d1817e50..64e0512dd 100644 --- a/src/main/java/org/amahi/anywhere/activity/ServerFileVideoActivity.java +++ b/src/main/java/org/amahi/anywhere/activity/ServerFileVideoActivity.java @@ -40,6 +40,7 @@ import android.view.ViewStub; import android.widget.FrameLayout; import android.widget.MediaController; +import android.widget.Toast; import org.amahi.anywhere.AmahiApplication; import org.amahi.anywhere.R; @@ -81,6 +82,7 @@ public class ServerFileVideoActivity extends AppCompatActivity implements private int mVideoSarDen = 0; private SurfaceView mSubtitlesSurface = null; + private float bufferPercent = 0.0f; private enum SurfaceSizes { SURFACE_BEST_FIT, @@ -508,7 +510,7 @@ public boolean isPlaying() { @Override public int getBufferPercentage() { - return 0; + return (int) bufferPercent; } @Override @@ -533,11 +535,10 @@ public void onEvent(MediaPlayer.Event event) { finish(); break; case MediaPlayer.Event.Buffering: - // Log.d("BUFFERING", ""+event.getBuffering()); - // TODO Use this and show buffering to users + bufferPercent = event.getBuffering(); break; case MediaPlayer.Event.EncounteredError: - // TODO Handle errors encountered if any + Toast.makeText(this, R.string.message_error_video, Toast.LENGTH_SHORT).show(); break; } diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index b386842c0..187b1f23b 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -51,6 +51,7 @@ Username or Password field cannot be left empty Connection Error Tap to retry + Error playing the video Check your settings and try again Create some and try again From 29e565dc22b826f91bc1ad718c58d2fa58a618b6 Mon Sep 17 00:00:00 2001 From: Chirag Maheshwari Date: Fri, 21 Apr 2017 11:59:33 +0530 Subject: [PATCH 3/4] adds subtitle fetch and display --- .../anywhere/server/model/ServerFile.java | 13 +++++ .../amahi/anywhere/service/VideoService.java | 52 ++++++++++++++++++- .../java/org/amahi/anywhere/util/Mimes.java | 4 ++ 3 files changed, 68 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/amahi/anywhere/server/model/ServerFile.java b/src/main/java/org/amahi/anywhere/server/model/ServerFile.java index 845c0c919..cf8a827e1 100644 --- a/src/main/java/org/amahi/anywhere/server/model/ServerFile.java +++ b/src/main/java/org/amahi/anywhere/server/model/ServerFile.java @@ -66,6 +66,10 @@ public String getName() { return name; } + public String getNameOnly() { + return name.replace("." + getExtension(), ""); + } + public String getMime() { return mime; } @@ -102,6 +106,15 @@ public String getPath() { return uri.build().getPath(); } + public String getExtension() { + String[] splitString = name.split("\\."); + if(splitString.length > 1) { + return splitString[splitString.length-1]; + } else { + return ""; + } + } + public static final Creator CREATOR = new Creator() { @Override diff --git a/src/main/java/org/amahi/anywhere/service/VideoService.java b/src/main/java/org/amahi/anywhere/service/VideoService.java index fcb3c936a..6c8a2cde9 100644 --- a/src/main/java/org/amahi/anywhere/service/VideoService.java +++ b/src/main/java/org/amahi/anywhere/service/VideoService.java @@ -25,15 +25,23 @@ import android.os.Binder; import android.os.IBinder; +import com.squareup.otto.Subscribe; + import org.amahi.anywhere.AmahiApplication; +import org.amahi.anywhere.bus.BusProvider; +import org.amahi.anywhere.bus.ServerFilesLoadedEvent; import org.amahi.anywhere.server.client.ServerClient; import org.amahi.anywhere.server.model.ServerFile; import org.amahi.anywhere.server.model.ServerShare; +import org.amahi.anywhere.util.Fragments; +import org.amahi.anywhere.util.Mimes; import org.videolan.libvlc.LibVLC; import org.videolan.libvlc.Media; import org.videolan.libvlc.MediaPlayer; import java.util.ArrayList; +import java.util.List; +import java.util.Objects; import javax.inject.Inject; @@ -63,6 +71,8 @@ public void onCreate() { setUpInjections(); setUpVideoPlayer(); + + BusProvider.getBus().register(this); } private void setUpInjections() { @@ -90,6 +100,7 @@ public void startVideo(ServerShare videoShare, ServerFile videoFile) { private void setUpVideoPlayback() { Media media = new Media(mLibVLC, getVideoUri()); mMediaPlayer.setMedia(media); + searchSubtitleFile(); media.release(); mMediaPlayer.play(); } @@ -98,6 +109,45 @@ private Uri getVideoUri() { return serverClient.getFileUri(videoShare, videoFile); } + private void searchSubtitleFile() { + if (serverClient.isConnected()){ + if (!isDirectoryAvailable()) { + serverClient.getFiles(videoShare); + } else { + serverClient.getFiles(videoShare, getDirectory()); + } + } + } + + @Subscribe + public void onFilesLoaded(ServerFilesLoadedEvent event) { + List files = event.getServerFiles(); + for (ServerFile file:files) { + if (videoFile.getNameOnly().equals(file.getNameOnly())) { + if (Mimes.match(file.getMime()) == Mimes.Type.SUBTITLE + || file.getExtension().equals("srt") + || file.getExtension().equals("sub")) { + mMediaPlayer.getMedia().addSlave( + new Media.Slave( + Media.Slave.Type.Subtitle, 4, getSubtitleUri(file))); + break; + } + } + } + } + + private String getSubtitleUri(ServerFile file) { + return serverClient.getFileUri(videoShare, file).toString(); + } + + private boolean isDirectoryAvailable() { + return getDirectory() != null; + } + + private ServerFile getDirectory() { + return videoFile.getParentFile(); + } + public MediaPlayer getMediaPlayer() { return mMediaPlayer; } @@ -114,12 +164,12 @@ public void pauseVideo() { mMediaPlayer.pause(); } - @Override public void onDestroy() { super.onDestroy(); tearDownVideoPlayback(); + BusProvider.getBus().unregister(this); } diff --git a/src/main/java/org/amahi/anywhere/util/Mimes.java b/src/main/java/org/amahi/anywhere/util/Mimes.java index 35582986f..a7aeb4b1d 100644 --- a/src/main/java/org/amahi/anywhere/util/Mimes.java +++ b/src/main/java/org/amahi/anywhere/util/Mimes.java @@ -72,6 +72,9 @@ public class Mimes { types.put("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", Type.SPREADSHEET); types.put("application/x-quicktimeplayer", Type.VIDEO); + + types.put("application/x-subrip", Mimes.Type.SUBTITLE); + types.put("image/vnd.dvb.subtitle", Mimes.Type.SUBTITLE); } public static int match(String mime) { @@ -127,6 +130,7 @@ public static final class Type { public static final int PRESENTATION = 7; public static final int SPREADSHEET = 8; public static final int VIDEO = 9; + public static final int SUBTITLE = 10; private Type() { } From 2b6fa8adfa1c7b6f583f21e2a01323c6fa39117c Mon Sep 17 00:00:00 2001 From: Chirag Maheshwari Date: Sun, 4 Jun 2017 21:11:30 +0530 Subject: [PATCH 4/4] resolves buggy video player controls --- .../activity/ServerFileAudioActivity.java | 6 +- .../activity/ServerFileVideoActivity.java | 24 ++--- .../amahi/anywhere/service/VideoService.java | 2 - .../amahi/anywhere/view/MediaControls.java | 90 ++----------------- .../res/layout/activity_server_file_video.xml | 42 ++++----- 5 files changed, 41 insertions(+), 123 deletions(-) diff --git a/src/main/java/org/amahi/anywhere/activity/ServerFileAudioActivity.java b/src/main/java/org/amahi/anywhere/activity/ServerFileAudioActivity.java index 24fa0a762..b0cb0d94d 100644 --- a/src/main/java/org/amahi/anywhere/activity/ServerFileAudioActivity.java +++ b/src/main/java/org/amahi/anywhere/activity/ServerFileAudioActivity.java @@ -290,7 +290,7 @@ private void showAudio() { private void showAudioControls() { if (areAudioControlsAvailable() && !audioControls.isShowing()) { - audioControls.showAnimated(); + audioControls.show(0); } } @@ -339,7 +339,7 @@ private void hideAudioMetadata() { private void hideAudioControls() { if (areAudioControlsAvailable() && audioControls.isShowing()) { - audioControls.hideAnimated(); + audioControls.hide(); } } @@ -423,7 +423,7 @@ protected void onResume() { private void showAudioControlsForced() { if (areAudioControlsAvailable() && !audioControls.isShowing()) { - audioControls.show(); + audioControls.show(0); } } diff --git a/src/main/java/org/amahi/anywhere/activity/ServerFileVideoActivity.java b/src/main/java/org/amahi/anywhere/activity/ServerFileVideoActivity.java index 64e0512dd..0b0463e77 100644 --- a/src/main/java/org/amahi/anywhere/activity/ServerFileVideoActivity.java +++ b/src/main/java/org/amahi/anywhere/activity/ServerFileVideoActivity.java @@ -40,6 +40,7 @@ import android.view.ViewStub; import android.widget.FrameLayout; import android.widget.MediaController; +import android.widget.ProgressBar; import android.widget.Toast; import org.amahi.anywhere.AmahiApplication; @@ -49,7 +50,6 @@ import org.amahi.anywhere.service.VideoService; import org.amahi.anywhere.util.FullScreenHelper; import org.amahi.anywhere.util.Intents; -import org.amahi.anywhere.util.ViewDirector; import org.amahi.anywhere.view.MediaControls; import org.videolan.libvlc.IVLCVout; import org.videolan.libvlc.Media; @@ -94,7 +94,7 @@ private enum SurfaceSizes { } private static SurfaceSizes CURRENT_SIZE = SurfaceSizes.SURFACE_BEST_FIT; - + //TODO Add feature for changing the screen size @Override @@ -146,9 +146,9 @@ private ServerFile getVideoFile() { } private void setUpFullScreen() { - fullScreen = new FullScreenHelper(getSupportActionBar(), getSurfaceFrame(), getControlsContainer()); + fullScreen = new FullScreenHelper(getSupportActionBar(), getVideoMainFrame()); fullScreen.enableOnClickToggle(false); - getSurfaceFrame().setOnClickListener(new View.OnClickListener() { + getVideoMainFrame().setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { fullScreen.toggle(); @@ -219,10 +219,13 @@ private FrameLayout getSurfaceFrame() { return (FrameLayout) findViewById(R.id.video_surface_frame); } + private FrameLayout getVideoMainFrame() { + return (FrameLayout) findViewById(R.id.video_main_frame); + } + private void setUpVideoControls() { if (!areVideoControlsAvailable()) { videoControls = new MediaControls(this); - videoControls.setMediaPlayer(this); videoControls.setAnchorView(getControlsContainer()); } @@ -248,7 +251,6 @@ private View getControlsContainer() { private void setUpVideoPlayback() { if (videoService.isVideoStarted()) { - showVideo(); showThenAutoHideControls(); } else { videoService.startVideo(getVideoShare(), getVideoFile()); @@ -260,13 +262,12 @@ private void showThenAutoHideControls() { if (!isFinishing()) { fullScreen.show(); fullScreen.delayedHide(); - videoControls.showAnimated(); - videoControls.hideControlsDelayed(); + videoControls.show(); } } - private void showVideo() { - ViewDirector.of(this, R.id.animator).show(R.id.video_surface_frame); + private ProgressBar getProgressBar() { + return (ProgressBar) findViewById(android.R.id.progress); } private ServerShare getVideoShare() { @@ -523,9 +524,10 @@ public void onEvent(MediaPlayer.Event event) { switch(event.type) { case MediaPlayer.Event.MediaChanged: - showVideo(); + getVideoMainFrame().setVisibility(View.VISIBLE); break; case MediaPlayer.Event.Playing: + getProgressBar().setVisibility(View.INVISIBLE); showThenAutoHideControls(); break; case MediaPlayer.Event.Paused: diff --git a/src/main/java/org/amahi/anywhere/service/VideoService.java b/src/main/java/org/amahi/anywhere/service/VideoService.java index 6c8a2cde9..a94b9e171 100644 --- a/src/main/java/org/amahi/anywhere/service/VideoService.java +++ b/src/main/java/org/amahi/anywhere/service/VideoService.java @@ -33,7 +33,6 @@ import org.amahi.anywhere.server.client.ServerClient; import org.amahi.anywhere.server.model.ServerFile; import org.amahi.anywhere.server.model.ServerShare; -import org.amahi.anywhere.util.Fragments; import org.amahi.anywhere.util.Mimes; import org.videolan.libvlc.LibVLC; import org.videolan.libvlc.Media; @@ -41,7 +40,6 @@ import java.util.ArrayList; import java.util.List; -import java.util.Objects; import javax.inject.Inject; diff --git a/src/main/java/org/amahi/anywhere/view/MediaControls.java b/src/main/java/org/amahi/anywhere/view/MediaControls.java index 5cd102727..2a29fc2bc 100644 --- a/src/main/java/org/amahi/anywhere/view/MediaControls.java +++ b/src/main/java/org/amahi/anywhere/view/MediaControls.java @@ -21,106 +21,28 @@ import android.app.Activity; import android.content.Context; -import android.os.Handler; import android.view.KeyEvent; -import android.view.View; -import android.view.animation.Animation; -import android.view.animation.AnimationUtils; import android.widget.MediaController; -import org.amahi.anywhere.R; - -import java.util.concurrent.TimeUnit; - /** * Media controls view. Does the same as {@link android.widget.MediaController} - * with a couple of modifications. Media controls do not auto-hide, back button do not hide - * controls but finishes parent {@link android.app.Activity}, there are methods to show and hide - * controls animated. + * with a couple of modifications. Back button do not hide controls but + * finishes parent {@link android.app.Activity}. */ -public class MediaControls extends MediaController implements Animation.AnimationListener { - private Handler videoControlsHandler; - private boolean isVisible = false; - Runnable mHideRunnable = new Runnable() { - @Override - public void run() { - hideAnimated(); - } - }; - private long autoHideDelayMillis = TimeUnit.SECONDS.toMillis(3); +public class MediaControls extends MediaController { public MediaControls(Context context) { super(context); - init(); - } - - public MediaControls(Context context, long autoHideDelayMillis) { - super(context); - this.autoHideDelayMillis = autoHideDelayMillis; - init(); - } - - private void init() { - videoControlsHandler = new Handler(); - setOnClickListener(new OnClickListener() { - @Override - public void onClick(View view) { - hideControlsDelayed(); - } - }); - } - - @Override - public void show(int timeout) { - super.show(0); - } - - public void showAnimated() { - if (!isVisible) { - videoControlsHandler.removeCallbacks(mHideRunnable); - Animation animation = AnimationUtils.loadAnimation(getContext(), R.anim.slide_up_view); - startAnimation(animation); - show(); - isVisible = true; - } - } - - public void hideAnimated() { - if (isVisible) { - Animation animation = AnimationUtils.loadAnimation(getContext(), R.anim.slide_down_view); - animation.setAnimationListener(this); - startAnimation(animation); - } - } - - public void hideControlsDelayed() { - videoControlsHandler.removeCallbacks(mHideRunnable); - videoControlsHandler.postDelayed(mHideRunnable, autoHideDelayMillis); } public void toggle() { - if (isVisible) { - hideAnimated(); + if (isShowing()) { + hide(); } else { - showAnimated(); - hideControlsDelayed(); + show(); } } - @Override - public void onAnimationEnd(Animation animation) { - hide(); - isVisible = false; - } - - @Override - public void onAnimationStart(Animation animation) { - } - - @Override - public void onAnimationRepeat(Animation animation) { - } - @Override public boolean dispatchKeyEvent(KeyEvent event) { if ((event.getKeyCode() == KeyEvent.KEYCODE_BACK) && (event.getAction() == KeyEvent.ACTION_DOWN)) { diff --git a/src/main/res/layout/activity_server_file_video.xml b/src/main/res/layout/activity_server_file_video.xml index a41ab480a..4eab7f86b 100644 --- a/src/main/res/layout/activity_server_file_video.xml +++ b/src/main/res/layout/activity_server_file_video.xml @@ -17,35 +17,25 @@ ~ along with Amahi. If not, see . --> - - - + android:layout_height="match_parent"> + android:keepScreenOn="true" + android:visibility="invisible"> + android:foregroundGravity="clip_horizontal|clip_vertical"> - - + + - \ No newline at end of file + + + \ No newline at end of file