vlc-android 非常的强大,目前我们主要是使用vlc来播送组播UDP TS流。类似地址:udp://@239.1.1.0:2860这样的地址。在此之前发现声音会卡顿,效果不太好。今天发现是底层驱动的问题。
但是在进行vlc stop时候不能进行资源释放,很是奇怪。明天解决这个问题。文章后续会继续整理!!!
---------------------------------------------------------------------------
针对此前遇到vlc stop时候不能进行资源释放,后来是参考了VLC-android的源码,
具体类文件目录是:
类的代码:看一下VLC是如何释放的,这样处理逻辑比较好。
/*****************************************************************************
* VideoPlayerActivity.java
*****************************************************************************
* Copyright © 2011-2014 VLC authors and VideoLAN
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
package org.videolan.vlc.gui.video;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.StreamCorruptedException;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Method;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Date;
import java.util.Locale;
import java.util.Map;
import org.videolan.libvlc.EventHandler;
import org.videolan.libvlc.IVideoPlayer;
import org.videolan.libvlc.LibVLC;
import org.videolan.libvlc.LibVlcException;
import org.videolan.libvlc.LibVlcUtil;
import org.videolan.libvlc.Media;
import org.videolan.vlc.MediaDatabase;
import org.videolan.vlc.R;
import org.videolan.vlc.VLCApplication;
import org.videolan.vlc.audio.AudioServiceController;
import org.videolan.vlc.gui.CommonDialogs;
import org.videolan.vlc.gui.CommonDialogs.MenuType;
import org.videolan.vlc.gui.MainActivity;
import org.videolan.vlc.gui.PreferencesActivity;
import org.videolan.vlc.util.AndroidDevices;
import org.videolan.vlc.util.Strings;
import org.videolan.vlc.util.VLCInstance;
import org.videolan.vlc.util.WeakHandler;
import android.annotation.TargetApi;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.KeyguardManager;
import android.app.Presentation;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.database.Cursor;
import android.graphics.Color;
import android.graphics.ImageFormat;
import android.graphics.PixelFormat;
import android.media.AudioManager;
import android.media.AudioManager.OnAudioFocusChangeListener;
import android.media.MediaRouter;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.preference.PreferenceManager;
import android.provider.MediaStore;
import android.provider.Settings.SettingNotFoundException;
import android.text.format.DateFormat;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Display;
import android.view.MotionEvent;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceHolder.Callback;
import android.view.SurfaceView;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnSystemUiVisibilityChangeListener;
import android.view.ViewGroup.LayoutParams;
import android.view.WindowManager;
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
import android.view.animation.AnimationUtils;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.RotateAnimation;
import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;
public class VideoPlayerActivity extends Activity implements IVideoPlayer {
public final static String TAG = "VLC/VideoPlayerActivity";
// Internal intent identifier to distinguish between internal launch and
// external intent.
private final static String PLAY_FROM_VIDEOGRID = "org.videolan.vlc.gui.video.PLAY_FROM_VIDEOGRID";
private SurfaceView mSurface;
private SurfaceView mSubtitlesSurface;
private SurfaceHolder mSurfaceHolder;
private SurfaceHolder mSubtitlesSurfaceHolder;
private FrameLayout mSurfaceFrame;
private MediaRouter mMediaRouter;
private MediaRouter.SimpleCallback mMediaRouterCallback;
private SecondaryDisplay mPresentation;
private LibVLC mLibVLC;
private String mLocation;
private static final int SURFACE_BEST_FIT = 0;
private static final int SURFACE_FIT_HORIZONTAL = 1;
private static final int SURFACE_FIT_VERTICAL = 2;
private static final int SURFACE_FILL = 3;
private static final int SURFACE_16_9 = 4;
private static final int SURFACE_4_3 = 5;
private static final int SURFACE_ORIGINAL = 6;
private int mCurrentSize = SURFACE_BEST_FIT;
private SharedPreferences mSettings;
/** Overlay */
private View mOverlayHeader;
private View mOverlayOption;
private View mOverlayProgress;
private View mOverlayBackground;
private static final int OVERLAY_TIMEOUT = 4000;
private static final int OVERLAY_INFINITE = 3600000;
private static final int FADE_OUT = 1;
private static final int SHOW_PROGRESS = 2;
private static final int SURFACE_SIZE = 3;
private static final int AUDIO_SERVICE_CONNECTION_SUCCESS = 5;
private static final int AUDIO_SERVICE_CONNECTION_FAILED = 6;
private static final int FADE_OUT_INFO = 4;
private boolean mDragging;
private boolean mShowing;
private int mUiVisibility = -1;
private SeekBar mSeekbar;
private TextView mTitle;
private TextView mSysTime;
private TextView mBattery;
private TextView mTime;
private TextView mLength;
private TextView mInfo;
private ImageView mLoading;
private TextView mLoadingText;
private ImageButton mPlayPause;
private ImageButton mBackward;
private ImageButton mForward;
private boolean mEnableJumpButtons;
private boolean mEnableBrightnessGesture;
private boolean mEnableCloneMode;
private boolean mDisplayRemainingTime = false;
private int mScreenOrientation;
private ImageButton mAudioTrack;
private ImageButton mSubtitle;
private ImageButton mLock;
private ImageButton mSize;
private ImageButton mMenu;
private boolean mIsLocked = false;
private int mLastAudioTrack = -1;
private int mLastSpuTrack = -2;
/**
* For uninterrupted switching between audio and video mode
*/
private boolean mSwitchingView;
private boolean mEndReached;
private boolean mCanSeek;
// Playlist
private int savedIndexPosition = -1;
// size of the video
private int mVideoHeight;
private int mVideoWidth;
private int mVideoVisibleHeight;
private int mVideoVisibleWidth;
private int mSarNum;
private int mSarDen;
//Volume
private AudioManager mAudioManager;
private int mAudioMax;
private OnAudioFocusChangeListener mAudioFocusListener;
//Touch Events
private static final int TOUCH_NONE = 0;
private static final int TOUCH_VOLUME = 1;
private static final int TOUCH_BRIGHTNESS = 2;
private static final int TOUCH_SEEK = 3;
private int mTouchAction;
private int mSurfaceYDisplayRange;
private float mTouchY, mTouchX, mVol;
// Brightness
private boolean mIsFirstBrightnessGesture = true;
// Tracks & Subtitles
private Map mAudioTracksList;
private Map mSubtitleTracksList;
/**
* Used to store a selected subtitle; see onActivityResult.
* It is possible to have multiple custom subs in one session
* (just like desktop VLC allows you as well.)
*/
private final ArrayList mSubtitleSelectedFiles = new ArrayList();
// Whether fallback from HW acceleration to SW decoding was done.
private boolean mDisabledHardwareAcceleration = false;
private int mPreviousHardwareAccelerationMode;
// Tips
private View mOverlayTips;
private static final String PREF_TIPS_SHOWN = "video_player_tips_shown";
// Navigation handling (DVD, Blu-Ray...)
private ImageButton mNavMenu;
private boolean mHasMenu = false;
private boolean mIsNavMenu = false;
@Override
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.i(TAG, "onCreate.");
if (LibVlcUtil.isJellyBeanMR1OrLater()) {
// Get the media router service (Miracast)
mMediaRouter = (MediaRouter) getSystemService(Context.MEDIA_ROUTER_SERVICE);
mMediaRouterCallback = new MediaRouter.SimpleCallback() {
@Override
public void onRoutePresentationDisplayChanged(
MediaRouter router, MediaRouter.RouteInfo info) {
Log.d(TAG, "onRoutePresentationDisplayChanged: info=" + info);
removePresentation();
}
};
Log.d(TAG, "MediaRouter information : " + mMediaRouter .toString());
}
mSettings = PreferenceManager.getDefaultSharedPreferences(this);
/* Services and miscellaneous */
mAudioManager = (AudioManager) getSystemService(AUDIO_SERVICE);
mAudioMax = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
mEnableCloneMode = mSettings.getBoolean("enable_clone_mode", false);
createPresentation();
setContentView(mPresentation == null ? R.layout.player : R.layout.player_remote_control);
if (LibVlcUtil.isICSOrLater())
getWindow().getDecorView().findViewById(android.R.id.content).setOnSystemUiVisibilityChangeListener(
new OnSystemUiVisibilityChangeListener() {
@Override
public void onSystemUiVisibilityChange(int visibility) {
if (visibility == mUiVisibility)
return;
setSurfaceSize(mVideoWidth, mVideoHeight, mVideoVisibleWidth, mVideoVisibleHeight, mSarNum, mSarDen);
if (visibility == View.SYSTEM_UI_FLAG_VISIBLE && !mShowing && !isFinishing()) {
showOverlay();
}
mUiVisibility = visibility;
}
}
);
/** initialize Views an their Events */
mOverlayHeader = findViewById(R.id.player_overlay_header);
mOverlayOption = findViewById(R.id.option_overlay);
mOverlayProgress = findViewById(R.id.progress_overlay);
mOverlayBackground = findViewById(R.id.player_overlay_background);
/* header */
mTitle = (TextView) findViewById(R.id.player_overlay_title);
mSysTime = (TextView) findViewById(R.id.player_overlay_systime);
mBattery = (TextView) findViewById(R.id.player_overlay_battery);
// Position and remaining time
mTime = (TextView) findViewById(R.id.player_overlay_time);
mTime.setOnClickListener(mRemainingTimeListener);
mLength = (TextView) findViewById(R.id.player_overlay_length);
mLength.setOnClickListener(mRemainingTimeListener);
// the info textView is not on the overlay
mInfo = (TextView) findViewById(R.id.player_overlay_info);
mEnableBrightnessGesture = mSettings.getBoolean("enable_brightness_gesture", true);
mScreenOrientation = Integer.valueOf(
mSettings.getString("screen_orientation_value", "4" /*SCREEN_ORIENTATION_SENSOR*/));
mEnableJumpButtons = mSettings.getBoolean("enable_jump_buttons", false);
mPlayPause = (ImageButton) findViewById(R.id.player_overlay_play);
mPlayPause.setOnClickListener(mPlayPauseListener);
mBackward = (ImageButton) findViewById(R.id.player_overlay_backward);
mBackward.setOnClickListener(mBackwardListener);
mForward = (ImageButton) findViewById(R.id.player_overlay_forward);
mForward.setOnClickListener(mForwardListener);
mAudioTrack = (ImageButton) findViewById(R.id.player_overlay_audio);
mAudioTrack.setVisibility(View.GONE);
mSubtitle = (ImageButton) findViewById(R.id.player_overlay_subtitle);
mSubtitle.setVisibility(View.GONE);
mNavMenu = (ImageButton) findViewById(R.id.player_overlay_navmenu);
mNavMenu.setVisibility(View.GONE);
mLock = (ImageButton) findViewById(R.id.lock_overlay_button);
mLock.setOnClickListener(mLockListener);
mSize = (ImageButton) findViewById(R.id.player_overlay_size);
mSize.setOnClickListener(mSizeListener);
mMenu = (ImageButton) findViewById(R.id.player_overlay_adv_function);
try {
mLibVLC = VLCInstance.getLibVlcInstance();
} catch (LibVlcException e) {
Log.d(TAG, "LibVLC initialisation failed");
return;
}
mSurface = (SurfaceView) findViewById(R.id.player_surface);
mSurfaceHolder = mSurface.getHolder();
mSurfaceFrame = (FrameLayout) findViewById(R.id.player_surface_frame);
String chroma = mSettings.getString("chroma_format", "");
if(LibVlcUtil.isGingerbreadOrLater() && chroma.equals("YV12")) {
mSurfaceHolder.setFormat(ImageFormat.YV12);
} else if (chroma.equals("RV16")) {
mSurfaceHolder.setFormat(PixelFormat.RGB_565);
} else {
mSurfaceHolder.setFormat(PixelFormat.RGBX_8888);
}
mSubtitlesSurface = (SurfaceView) findViewById(R.id.subtitles_surface);
mSubtitlesSurfaceHolder = mSubtitlesSurface.getHolder();
mSubtitlesSurfaceHolder.setFormat(PixelFormat.RGBA_8888);
mSubtitlesSurface.setZOrderMediaOverlay(true);
if (mPresentation == null) {
mSurfaceHolder.addCallback(mSurfaceCallback);
mSubtitlesSurfaceHolder.addCallback(mSubtitlesSurfaceCallback);
}
mSeekbar = (SeekBar) findViewById(R.id.player_overlay_seekbar);
mSeekbar.setOnSeekBarChangeListener(mSeekListener);
/* Loading view */
mLoading = (ImageView) findViewById(R.id.player_overlay_loading);
mLoadingText = (TextView) findViewById(R.id.player_overlay_loading_text);
startLoadingAnimation();
mSwitchingView = false;
mEndReached = false;
// Clear the resume time, since it is only used for resumes in external
// videos.
SharedPreferences.Editor editor = mSettings.edit();
editor.putLong(PreferencesActivity.VIDEO_RESUME_TIME, -1);
// Also clear the subs list, because it is supposed to be per session
// only (like desktop VLC). We don't want the customs subtitle file
// to persist forever with this video.
editor.putString(PreferencesActivity.VIDEO_SUBTITLE_FILES, null);
editor.commit();
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_BATTERY_CHANGED);
filter.addAction(VLCApplication.SLEEP_INTENT);
registerReceiver(mReceiver, filter);
Log.d(TAG,
"Hardware acceleration mode: "
+ Integer.toString(mLibVLC.getHardwareAcceleration()));
/* Only show the subtitles surface when using "Full Acceleration" mode */
if (mLibVLC.getHardwareAcceleration() == LibVLC.HW_ACCELERATION_FULL)
mSubtitlesSurface.setVisibility(View.VISIBLE);
// Signal to LibVLC that the videoPlayerActivity was created, thus the
// SurfaceView is now available for MediaCodec direct rendering.
mLibVLC.eventVideoPlayerActivityCreated(true);
//mLibVLC.setHardwareAcceleration(LibVLC.HW_ACCELERATION_FULL);
EventHandler em = EventHandler.getInstance();
em.addHandler(eventHandler);
this.setVolumeControlStream(AudioManager.STREAM_MUSIC);
// Extra initialization when no secondary display is detected
if (mPresentation == null) {
// Orientation
// 100 is the value for screen_orientation_start_lock
setRequestedOrientation(mScreenOrientation != 100
? mScreenOrientation
: getScreenOrientation());
// Tips
mOverlayTips = findViewById(R.id.player_overlay_tips);
if(mSettings.getBoolean(PREF_TIPS_SHOWN, false))
mOverlayTips.setVisibility(View.GONE);
else {
mOverlayTips.bringToFront();
mOverlayTips.invalidate();
}
} else
setRequestedOrientation(getScreenOrientation());
updateNavStatus();
}
@Override
protected void onPause() {
super.onPause();
Log.i(TAG, "onPause. mSwitchingView="+mSwitchingView);
if (mMediaRouter != null) {
// Stop listening for changes to media routes.
mediaRouterAddCallback(false);
}
if(mSwitchingView) {
Log.d(TAG, "mLocation = \"" + mLocation + "\"");
AudioServiceController.getInstance().showWithoutParse(savedIndexPosition);
AudioServiceController.getInstance().unbindAudioService(this);
return;
}
long time = mLibVLC.getTime();
long length = mLibVLC.getLength();
//remove saved position if in the last 5 seconds
if (length - time < 5000)
time = 0;
else
time -= 5000; // go back 5 seconds, to compensate loading time
/*
* Pausing here generates errors because the vout is constantly
* trying to refresh itself every 80ms while the surface is not
* accessible anymore.
* To workaround that, we keep the last known position in the playlist
* in savedIndexPosition to be able to restore it during onResume().
*/
mLibVLC.stop();
mSurface.setKeepScreenOn(false);
SharedPreferences.Editor editor = mSettings.edit();
// Save position
if (time >= 0 && mCanSeek) {
if(MediaDatabase.getInstance().mediaItemExists(mLocation)) {
MediaDatabase.getInstance().updateMedia(
mLocation,
MediaDatabase.mediaColumn.MEDIA_TIME,
time);
} else {
// Video file not in media library, store time just for onResume()
editor.putLong(PreferencesActivity.VIDEO_RESUME_TIME, time);
}
}
// Save selected subtitles
String subtitleList_serialized = null;
if(mSubtitleSelectedFiles.size() > 0) {
Log.d(TAG, "Saving selected subtitle files");
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try {
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(mSubtitleSelectedFiles);
subtitleList_serialized = bos.toString();
} catch(IOException e) {}
}
editor.putString(PreferencesActivity.VIDEO_SUBTITLE_FILES, subtitleList_serialized);
editor.commit();
AudioServiceController.getInstance().unbindAudioService(this);
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
@Override
protected void onStop() {
super.onStop();
Log.i(TAG, "onStop.");
// Dismiss the presentation when the activity is not visible.
if (mPresentation != null) {
Log.i(TAG, "Dismissing presentation because the activity is no longer visible.");
mPresentation.dismiss();
mPresentation = null;
}
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.i(TAG, "onDestroy.");
unregisterReceiver(mReceiver);
EventHandler em = EventHandler.getInstance();
em.removeHandler(eventHandler);
// MediaCodec opaque direct rendering should not be used anymore since there is no surface to attach.
mLibVLC.eventVideoPlayerActivityCreated(false);
// HW acceleration was temporarily disabled because of an error, restore the previous value.
if (mDisabledHardwareAcceleration)
mLibVLC.setHardwareAcceleration(mPreviousHardwareAccelerationMode);
mAudioManager = null;
}
@Override
protected void onResume() {
super.onResume();
Log.i(TAG, "onResume.");
mSwitchingView = false;
AudioServiceController.getInstance().bindAudioService(this,
new AudioServiceController.AudioServiceConnectionListener() {
@Override
public void onConnectionSuccess() {
mHandler.sendEmptyMessage(AUDIO_SERVICE_CONNECTION_SUCCESS);
}
@Override
public void onConnectionFailed() {
mHandler.sendEmptyMessage(AUDIO_SERVICE_CONNECTION_FAILED);
}
});
if (mMediaRouter != null) {
// Listen for changes to media routes.
mediaRouterAddCallback(true);
}
}
/**
* Add or remove MediaRouter callbacks. This is provided for version targeting.
*
* @param add true to add, false to remove
*/
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
private void mediaRouterAddCallback(boolean add) {
if(!LibVlcUtil.isJellyBeanMR1OrLater() || mMediaRouter == null) return;
if(add)
mMediaRouter.addCallback(MediaRouter.ROUTE_TYPE_LIVE_VIDEO, mMediaRouterCallback);
else
mMediaRouter.removeCallback(mMediaRouterCallback);
}
private void startPlayback() {
Log.i(TAG, "startPlayback.");
loadMedia();
/*
* if the activity has been paused by pressing the power button,
* pressing it again will show the lock screen.
* But onResume will also be called, even if vlc-android is still in the background.
* To workaround that, pause playback if the lockscreen is displayed
*/
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
if (mLibVLC != null && mLibVLC.isPlaying()) {
KeyguardManager km = (KeyguardManager)getSystemService(KEYGUARD_SERVICE);
if (km.inKeyguardRestrictedInputMode())
mLibVLC.pause();
}
}}, 500);
// Add any selected subtitle file from the file picker
if(mSubtitleSelectedFiles.size() > 0) {
for(String file : mSubtitleSelectedFiles) {
Log.i(TAG, "Adding user-selected subtitle " + file);
mLibVLC.addSubtitleTrack(file);
}
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if(data == null) return;
if(data.getDataString() == null) {
Log.d(TAG, "Subtitle selection dialog was cancelled");
}
if(data.getData() == null) return;
String subtitlePath = data.getData().getPath();
if(requestCode == CommonDialogs.INTENT_SPECIFIC) {
Log.d(TAG, "Specific subtitle file: " + subtitlePath);
} else if(requestCode == CommonDialogs.INTENT_GENERIC) {
Log.d(TAG, "Generic subtitle file: " + subtitlePath);
}
mSubtitleSelectedFiles.add(subtitlePath);
}
public static void start(Context context, String location) {
start(context, location, null, -1, false, false);
}
public static void start(Context context, String location, Boolean fromStart) {
start(context, location, null, -1, false, fromStart);
}
public static void start(Context context, String location, String title, Boolean dontParse) {
start(context, location, title, -1, dontParse, false);
}
public static void start(Context context, String location, String title, int position, Boolean dontParse) {
start(context, location, title, position, dontParse, false);
}
public static void start(Context context, String location, String title, int position, Boolean dontParse, Boolean fromStart) {
Intent intent = new Intent(context, VideoPlayerActivity.class);
intent.setAction(VideoPlayerActivity.PLAY_FROM_VIDEOGRID);
intent.putExtra("itemLocation", location);
intent.putExtra("itemTitle", title);
intent.putExtra("dontParse", dontParse);
intent.putExtra("fromStart", fromStart);
intent.putExtra("itemPosition", position);
if (dontParse)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
context.startActivity(intent);
}
private final BroadcastReceiver mReceiver = new BroadcastReceiver()
{
@Override
public void onReceive(Context context, Intent intent)
{
String action = intent.getAction();
if (action.equalsIgnoreCase(Intent.ACTION_BATTERY_CHANGED)) {
int batteryLevel = intent.getIntExtra("level", 0);
if (batteryLevel >= 50)
mBattery.setTextColor(Color.GREEN);
else if (batteryLevel >= 30)
mBattery.setTextColor(Color.YELLOW);
else
mBattery.setTextColor(Color.RED);
mBattery.setText(String.format("%d%%", batteryLevel));
}
else if (action.equalsIgnoreCase(VLCApplication.SLEEP_INTENT)) {
finish();
}
}
};
@Override
public boolean onTrackballEvent(MotionEvent event) {
showOverlay();
return true;
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
setSurfaceSize(mVideoWidth, mVideoHeight, mVideoVisibleWidth, mVideoVisibleHeight, mSarNum, mSarDen);
super.onConfigurationChanged(newConfig);
}
@Override
public void setSurfaceSize(int width, int height, int visible_width, int visible_height, int sar_num, int sar_den) {
if (width * height == 0)
return;
// store video size
mVideoHeight = height;
mVideoWidth = width;
mVideoVisibleHeight = visible_height;
mVideoVisibleWidth = visible_width;
mSarNum = sar_num;
mSarDen = sar_den;
Message msg = mHandler.obtainMessage(SURFACE_SIZE);
mHandler.sendMessage(msg);
}
/**
* Lock screen rotation
*/
private void lockScreen() {
if(mScreenOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR) {
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2)
setRequestedOrientation(14 /* SCREEN_ORIENTATION_LOCKED */);
else
setRequestedOrientation(getScreenOrientation());
}
showInfo(R.string.locked, 1000);
mLock.setBackgroundResource(R.drawable.ic_locked);
mTime.setEnabled(false);
mSeekbar.setEnabled(false);
mLength.setEnabled(false);
hideOverlay(true);
}
/**
* Remove screen lock
*/
private void unlockScreen() {
if(mScreenOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR)
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);
showInfo(R.string.unlocked, 1000);
mLock.setBackgroundResource(R.drawable.ic_lock);
mTime.setEnabled(true);
mSeekbar.setEnabled(true);
mLength.setEnabled(true);
mShowing = false;
showOverlay();
}
/**
* Show text in the info view for "duration" milliseconds
* @param text
* @param duration
*/
private void showInfo(String text, int duration) {
mInfo.setVisibility(View.VISIBLE);
mInfo.setText(text);
mHandler.removeMessages(FADE_OUT_INFO);
mHandler.sendEmptyMessageDelayed(FADE_OUT_INFO, duration);
}
private void showInfo(int textid, int duration) {
mInfo.setVisibility(View.VISIBLE);
mInfo.setText(textid);
mHandler.removeMessages(FADE_OUT_INFO);
mHandler.sendEmptyMessageDelayed(FADE_OUT_INFO, duration);
}
/**
* Show text in the info view
* @param text
*/
private void showInfo(String text) {
mInfo.setVisibility(View.VISIBLE);
mInfo.setText(text);
mHandler.removeMessages(FADE_OUT_INFO);
}
/**
* hide the info view with "delay" milliseconds delay
* @param delay
*/
private void hideInfo(int delay) {
mHandler.sendEmptyMessageDelayed(FADE_OUT_INFO, delay);
}
/**
* hide the info view
*/
private void hideInfo() {
hideInfo(0);
}
private void fadeOutInfo() {
if (mInfo.getVisibility() == View.VISIBLE)
mInfo.startAnimation(AnimationUtils.loadAnimation(
VideoPlayerActivity.this, android.R.anim.fade_out));
mInfo.setVisibility(View.INVISIBLE);
}
@TargetApi(Build.VERSION_CODES.FROYO)
private int changeAudioFocus(boolean acquire) {
if(!LibVlcUtil.isFroyoOrLater()) // NOP if not supported
return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
if (mAudioFocusListener == null) {
mAudioFocusListener = new OnAudioFocusChangeListener() {
@Override
public void onAudioFocusChange(int focusChange) {
/*
* Pause playback during alerts and notifications
*/
switch (focusChange)
{
case AudioManager.AUDIOFOCUS_LOSS:
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
if (mLibVLC.isPlaying())
mLibVLC.pause();
break;
case AudioManager.AUDIOFOCUS_GAIN:
case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT:
case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK:
if (!mLibVLC.isPlaying())
mLibVLC.play();
break;
}
}
};
}
int result;
if(acquire) {
result = mAudioManager.requestAudioFocus(mAudioFocusListener,
AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
mAudioManager.setParameters("bgm_state=true");
}
else {
if (mAudioManager != null) {
result = mAudioManager.abandonAudioFocus(mAudioFocusListener);
mAudioManager.setParameters("bgm_state=false");
}
else
result = AudioManager.AUDIOFOCUS_REQUEST_FAILED;
}
return result;
}
/**
* Handle libvlc asynchronous events
*/
private final Handler eventHandler = new VideoPlayerEventHandler(this);
private static class VideoPlayerEventHandler extends WeakHandler {
public VideoPlayerEventHandler(VideoPlayerActivity owner) {
super(owner);
}
@Override
public void handleMessage(Message msg) {
VideoPlayerActivity activity = getOwner();
if(activity == null) return;
// Do not handle events if we are leaving the VideoPlayerActivity
if (activity.mSwitchingView) return;
switch (msg.getData().getInt("event")) {
case EventHandler.MediaParsedChanged:
Log.i(TAG, "MediaParsedChanged");
activity.updateNavStatus();
if (!activity.mHasMenu && activity.mLibVLC.getVideoTracksCount() < 1) {
Log.i(TAG, "No video track, open in audio mode");
activity.switchToAudioMode();
}
break;
case EventHandler.MediaPlayerPlaying:
Log.i(TAG, "MediaPlayerPlaying");
activity.stopLoadingAnimation();
activity.showOverlay();
/** FIXME: update the track list when it changes during the
* playback. (#7540) */
activity.setESTrackLists(true);
activity.setESTracks();
activity.changeAudioFocus(true);
activity.updateNavStatus();
break;
case EventHandler.MediaPlayerPaused:
Log.i(TAG, "MediaPlayerPaused");
break;
case EventHandler.MediaPlayerStopped:
Log.i(TAG, "MediaPlayerStopped");
activity.changeAudioFocus(false);
break;
case EventHandler.MediaPlayerEndReached:
Log.i(TAG, "MediaPlayerEndReached");
activity.changeAudioFocus(false);
activity.endReached();
break;
case EventHandler.MediaPlayerVout:
activity.updateNavStatus();
if (!activity.mHasMenu)
activity.handleVout(msg);
break;
case EventHandler.MediaPlayerPositionChanged:
if (!activity.mCanSeek)
activity.mCanSeek = true;
//don't spam the logs
break;
case EventHandler.MediaPlayerEncounteredError:
Log.i(TAG, "MediaPlayerEncounteredError");
activity.encounteredError();
break;
case EventHandler.HardwareAccelerationError:
Log.i(TAG, "HardwareAccelerationError");
activity.handleHardwareAccelerationError();
break;
case EventHandler.MediaPlayerTimeChanged:
// avoid useless error logs
break;
default:
Log.e(TAG, String.format("Event not handled (0x%x)", msg.getData().getInt("event")));
break;
}
activity.updateOverlayPausePlay();
}
};
/**
* Handle resize of the surface and the overlay
*/
private final Handler mHandler = new VideoPlayerHandler(this);
private static class VideoPlayerHandler extends WeakHandler {
public VideoPlayerHandler(VideoPlayerActivity owner) {
super(owner);
}
@Override
public void handleMessage(Message msg) {
VideoPlayerActivity activity = getOwner();
if(activity == null) // WeakReference could be GC'ed early
return;
switch (msg.what) {
case FADE_OUT:
activity.hideOverlay(false);
break;
case SHOW_PROGRESS:
int pos = activity.setOverlayProgress();
if (activity.canShowProgress()) {
msg = obtainMessage(SHOW_PROGRESS);
sendMessageDelayed(msg, 1000 - (pos % 1000));
}
break;
case SURFACE_SIZE:
activity.changeSurfaceSize();
break;
case FADE_OUT_INFO:
activity.fadeOutInfo();
break;
case AUDIO_SERVICE_CONNECTION_SUCCESS:
activity.startPlayback();
break;
case AUDIO_SERVICE_CONNECTION_FAILED:
activity.finish();
break;
}
}
};
private boolean canShowProgress() {
return !mDragging && mShowing && mLibVLC.isPlaying();
}
private void endReached() {
if(mLibVLC.getMediaList().expandMedia(savedIndexPosition) == 0) {
Log.d(TAG, "Found a video playlist, expanding it");
eventHandler.postDelayed(new Runnable() {
@Override
public void run() {
loadMedia();
}
}, 1000);
} else {
/* Exit player when reaching the end */
mEndReached = true;
finish();
}
}
private void encounteredError() {
/* Encountered Error, exit player with a message */
AlertDialog dialog = new AlertDialog.Builder(VideoPlayerActivity.this)
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
finish();
}
})
.setTitle(R.string.encountered_error_title)
.setMessage(R.string.encountered_error_message)
.create();
dialog.show();
}
public void eventHardwareAccelerationError() {
EventHandler em = EventHandler.getInstance();
em.callback(EventHandler.HardwareAccelerationError, new Bundle());
}
private void handleHardwareAccelerationError() {
mLibVLC.stop();
AlertDialog dialog = new AlertDialog.Builder(VideoPlayerActivity.this)
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
mDisabledHardwareAcceleration = true;
mPreviousHardwareAccelerationMode = mLibVLC.getHardwareAcceleration();
mLibVLC.setHardwareAcceleration(LibVLC.HW_ACCELERATION_DISABLED);
mSubtitlesSurface.setVisibility(View.INVISIBLE);
loadMedia();
}
})
.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
finish();
}
})
.setTitle(R.string.hardware_acceleration_error_title)
.setMessage(R.string.hardware_acceleration_error_message)
.create();
if(!isFinishing())
dialog.show();
}
private void handleVout(Message msg) {
if (msg.getData().getInt("data") == 0 && !mEndReached) {
/* Video track lost, open in audio mode */
Log.i(TAG, "Video track lost, switching to audio");
mSwitchingView = true;
finish();
}
}
private void switchToAudioMode() {
mSwitchingView = true;
// Show the MainActivity if it is not in background.
if (getIntent().getAction() != null
&& getIntent().getAction().equals(Intent.ACTION_VIEW)) {
Intent i = new Intent(this, MainActivity.class);
startActivity(i);
}
finish();
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
private void changeSurfaceSize() {
int sw;
int sh;
// get screen size
if (mPresentation == null) {
sw = getWindow().getDecorView().getWidth();
sh = getWindow().getDecorView().getHeight();
} else {
sw = mPresentation.getWindow().getDecorView().getWidth();
sh = mPresentation.getWindow().getDecorView().getHeight();
}
double dw = sw, dh = sh;
boolean isPortrait;
if (mPresentation == null) {
// getWindow().getDecorView() doesn't always take orientation into account, we have to correct the values
isPortrait = getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT;
} else {
isPortrait = false;
}
if (sw > sh && isPortrait || sw < sh && !isPortrait) {
dw = sh;
dh = sw;
}
// sanity check
if (dw * dh == 0 || mVideoWidth * mVideoHeight == 0) {
Log.e(TAG, "Invalid surface size");
return;
}
// compute the aspect ratio
double ar, vw;
if (mSarDen == mSarNum) {
/* No indication about the density, assuming 1:1 */
vw = mVideoVisibleWidth;
ar = (double)mVideoVisibleWidth / (double)mVideoVisibleHeight;
} else {
/* Use the specified aspect ratio */
vw = mVideoVisibleWidth * (double)mSarNum / mSarDen;
ar = vw / mVideoVisibleHeight;
}
// compute the display aspect ratio
double dar = dw / dh;
switch (mCurrentSize) {
case SURFACE_BEST_FIT:
if (dar < ar)
dh = dw / ar;
else
dw = dh * ar;
break;
case SURFACE_FIT_HORIZONTAL:
dh = dw / ar;
break;
case SURFACE_FIT_VERTICAL:
dw = dh * ar;
break;
case SURFACE_FILL:
break;
case SURFACE_16_9:
ar = 16.0 / 9.0;
if (dar < ar)
dh = dw / ar;
else
dw = dh * ar;
break;
case SURFACE_4_3:
ar = 4.0 / 3.0;
if (dar < ar)
dh = dw / ar;
else
dw = dh * ar;
break;
case SURFACE_ORIGINAL:
dh = mVideoVisibleHeight;
dw = vw;
break;
}
SurfaceView surface;
SurfaceView subtitlesSurface;
SurfaceHolder surfaceHolder;
SurfaceHolder subtitlesSurfaceHolder;
FrameLayout surfaceFrame;
if (mPresentation == null) {
surface = mSurface;
subtitlesSurface = mSubtitlesSurface;
surfaceHolder = mSurfaceHolder;
subtitlesSurfaceHolder = mSubtitlesSurfaceHolder;
surfaceFrame = mSurfaceFrame;
} else {
surface = mPresentation.mSurface;
subtitlesSurface = mPresentation.mSubtitlesSurface;
surfaceHolder = mPresentation.mSurfaceHolder;
subtitlesSurfaceHolder = mPresentation.mSubtitlesSurfaceHolder;
surfaceFrame = mPresentation.mSurfaceFrame;
}
// force surface buffer size
surfaceHolder.setFixedSize(mVideoWidth, mVideoHeight);
subtitlesSurfaceHolder.setFixedSize(mVideoWidth, mVideoHeight);
// set display size
LayoutParams lp = surface.getLayoutParams();
lp.width = (int) Math.ceil(dw * mVideoWidth / mVideoVisibleWidth);
lp.height = (int) Math.ceil(dh * mVideoHeight / mVideoVisibleHeight);
surface.setLayoutParams(lp);
subtitlesSurface.setLayoutParams(lp);
// set frame size (crop if necessary)
lp = surfaceFrame.getLayoutParams();
lp.width = (int) Math.floor(dw);
lp.height = (int) Math.floor(dh);
surfaceFrame.setLayoutParams(lp);
surface.invalidate();
subtitlesSurface.invalidate();
}
/**
* show/hide the overlay
*/
@Override
public boolean onTouchEvent(MotionEvent event) {
if (mIsLocked) {
// locked, only handle show/hide & ignore all actions
if (event.getAction() == MotionEvent.ACTION_UP) {
if (!mShowing) {
showOverlay();
} else {
hideOverlay(true);
}
}
return false;
}
DisplayMetrics screen = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(screen);
if (mSurfaceYDisplayRange == 0)
mSurfaceYDisplayRange = Math.min(screen.widthPixels, screen.heightPixels);
float y_changed = event.getRawY() - mTouchY;
float x_changed = event.getRawX() - mTouchX;
// coef is the gradient's move to determine a neutral zone
float coef = Math.abs (y_changed / x_changed);
float xgesturesize = ((x_changed / screen.xdpi) * 2.54f);
/* Offset for Mouse Events */
int[] offset = new int[2];
mSurface.getLocationOnScreen(offset);
int xTouch = Math.round((event.getRawX() - offset[0]) * mVideoWidth / mSurface.getWidth());
int yTouch = Math.round((event.getRawY() - offset[1]) * mVideoHeight / mSurface.getHeight());
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// Audio
mTouchY = event.getRawY();
mVol = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
mTouchAction = TOUCH_NONE;
// Seek
mTouchX = event.getRawX();
// Mouse events for the core
LibVLC.sendMouseEvent(MotionEvent.ACTION_DOWN, 0, xTouch, yTouch);
break;
case MotionEvent.ACTION_MOVE:
// Mouse events for the core
LibVLC.sendMouseEvent(MotionEvent.ACTION_MOVE, 0, xTouch, yTouch);
// No volume/brightness action if coef < 2 or a secondary display is connected
//TODO : Volume action when a secondary display is connected
if (coef > 2 && mPresentation == null) {
// Volume (Up or Down - Right side)
if (!mEnableBrightnessGesture || (int)mTouchX > (screen.widthPixels / 2)){
doVolumeTouch(y_changed);
}
// Brightness (Up or Down - Left side)
if (mEnableBrightnessGesture && (int)mTouchX < (screen.widthPixels / 2)){
doBrightnessTouch(y_changed);
}
// Extend the overlay for a little while, so that it doesn't
// disappear on the user if more adjustment is needed. This
// is because on devices with soft navigation (e.g. Galaxy
// Nexus), gestures can't be made without activating the UI.
if(AndroidDevices.hasNavBar())
showOverlay();
}
// Seek (Right or Left move)
doSeekTouch(coef, xgesturesize, false);
break;
case MotionEvent.ACTION_UP:
// Mouse events for the core
LibVLC.sendMouseEvent(MotionEvent.ACTION_UP, 0, xTouch, yTouch);
// Audio or Brightness
if ( mTouchAction == TOUCH_NONE) {
if (!mShowing) {
showOverlay();
} else {
hideOverlay(true);
}
}
// Seek
doSeekTouch(coef, xgesturesize, true);
break;
}
return mTouchAction != TOUCH_NONE;
}
private void doSeekTouch(float coef, float gesturesize, boolean seek) {
// No seek action if coef > 0.5 and gesturesize < 1cm
if (coef > 0.5 || Math.abs(gesturesize) < 1 || !mCanSeek)
return;
if (mTouchAction != TOUCH_NONE && mTouchAction != TOUCH_SEEK)
return;
mTouchAction = TOUCH_SEEK;
// Always show seekbar when searching
if (!mShowing) showOverlay();
long length = mLibVLC.getLength();
long time = mLibVLC.getTime();
// Size of the jump, 10 minutes max (600000), with a bi-cubic progression, for a 8cm gesture
int jump = (int) (Math.signum(gesturesize) * ((600000 * Math.pow((gesturesize / 8), 4)) + 3000));
// Adjust the jump
if ((jump > 0) && ((time + jump) > length))
jump = (int) (length - time);
if ((jump < 0) && ((time + jump) < 0))
jump = (int) -time;
//Jump !
if (seek && length > 0)
mLibVLC.setTime(time + jump);
if (length > 0)
//Show the jump's size
showInfo(String.format("%s%s (%s)",
jump >= 0 ? "+" : "",
Strings.millisToString(jump),
Strings.millisToString(time + jump)), 1000);
else
showInfo(R.string.unseekable_stream, 1000);
}
private void doVolumeTouch(float y_changed) {
if (mTouchAction != TOUCH_NONE && mTouchAction != TOUCH_VOLUME)
return;
int delta = -(int) ((y_changed / mSurfaceYDisplayRange) * mAudioMax);
int vol = (int) Math.min(Math.max(mVol + delta, 0), mAudioMax);
if (delta != 0) {
mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, vol, 0);
mTouchAction = TOUCH_VOLUME;
showInfo(getString(R.string.volume) + '\u00A0' + Integer.toString(vol),1000);
}
}
private void initBrightnessTouch() {
float brightnesstemp = 0.01f;
// Initialize the layoutParams screen brightness
try {
brightnesstemp = android.provider.Settings.System.getInt(getContentResolver(),
android.provider.Settings.System.SCREEN_BRIGHTNESS) / 255.0f;
} catch (SettingNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
WindowManager.LayoutParams lp = getWindow().getAttributes();
lp.screenBrightness = brightnesstemp;
getWindow().setAttributes(lp);
mIsFirstBrightnessGesture = false;
}
private void doBrightnessTouch(float y_changed) {
if (mTouchAction != TOUCH_NONE && mTouchAction != TOUCH_BRIGHTNESS)
return;
if (mIsFirstBrightnessGesture) initBrightnessTouch();
mTouchAction = TOUCH_BRIGHTNESS;
// Set delta : 0.07f is arbitrary for now, it possibly will change in the future
float delta = - y_changed / mSurfaceYDisplayRange * 0.07f;
// Estimate and adjust Brightness
WindowManager.LayoutParams lp = getWindow().getAttributes();
lp.screenBrightness = Math.min(Math.max(lp.screenBrightness + delta, 0.01f), 1);
// Set Brightness
getWindow().setAttributes(lp);
showInfo(getString(R.string.brightness) + '\u00A0' + Math.round(lp.screenBrightness*15),1000);
}
/**
* handle changes of the seekbar (slicer)
*/
private final OnSeekBarChangeListener mSeekListener = new OnSeekBarChangeListener() {
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
mDragging = true;
showOverlay(OVERLAY_INFINITE);
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
mDragging = false;
showOverlay();
hideInfo();
}
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if (fromUser && mCanSeek) {
mLibVLC.setTime(progress);
setOverlayProgress();
mTime.setText(Strings.millisToString(progress));
showInfo(Strings.millisToString(progress));
}
}
};
/**
*
*/
private final OnClickListener mAudioTrackListener = new OnClickListener() {
@Override
public void onClick(View v) {
final String[] arrList = new String[mAudioTracksList.size()];
int i = 0;
int listPosition = 0;
for(Map.Entry entry : mAudioTracksList.entrySet()) {
arrList[i] = entry.getValue();
// map the track position to the list position
if(entry.getKey() == mLibVLC.getAudioTrack())
listPosition = i;
i++;
}
AlertDialog dialog = new AlertDialog.Builder(VideoPlayerActivity.this)
.setTitle(R.string.track_audio)
.setSingleChoiceItems(arrList, listPosition, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int listPosition) {
int trackID = -1;
// Reverse map search...
for(Map.Entry entry : mAudioTracksList.entrySet()) {
if(arrList[listPosition].equals(entry.getValue())) {
trackID = entry.getKey();
break;
}
}
if(trackID < 0) return;
MediaDatabase.getInstance().updateMedia(
mLocation,
MediaDatabase.mediaColumn.MEDIA_AUDIOTRACK,
trackID);
mLibVLC.setAudioTrack(trackID);
dialog.dismiss();
}
})
.create();
dialog.setCanceledOnTouchOutside(true);
dialog.setOwnerActivity(VideoPlayerActivity.this);
dialog.show();
}
};
/**
*
*/
private final OnClickListener mSubtitlesListener = new OnClickListener() {
@Override
public void onClick(View v) {
final String[] arrList = new String[mSubtitleTracksList.size()];
int i = 0;
int listPosition = 0;
for(Map.Entry entry : mSubtitleTracksList.entrySet()) {
arrList[i] = entry.getValue();
// map the track position to the list position
if(entry.getKey() == mLibVLC.getSpuTrack())
listPosition = i;
i++;
}
AlertDialog dialog = new AlertDialog.Builder(VideoPlayerActivity.this)
.setTitle(R.string.track_text)
.setSingleChoiceItems(arrList, listPosition, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int listPosition) {
int trackID = -2;
// Reverse map search...
for(Map.Entry entry : mSubtitleTracksList.entrySet()) {
if(arrList[listPosition].equals(entry.getValue())) {
trackID = entry.getKey();
break;
}
}
if(trackID < -1) return;
MediaDatabase.getInstance().updateMedia(
mLocation,
MediaDatabase.mediaColumn.MEDIA_SPUTRACK,
trackID);
mLibVLC.setSpuTrack(trackID);
dialog.dismiss();
}
})
.create();
dialog.setCanceledOnTouchOutside(true);
dialog.setOwnerActivity(VideoPlayerActivity.this);
dialog.show();
}
};
private final OnClickListener mNavMenuListener = new OnClickListener() {
@Override
public void onClick(View v) {
/* Try to return to the menu. */
/* FIXME: not working correctly in all cases */
mLibVLC.setTitle(0);
}
};
/**
*
*/
private final OnClickListener mPlayPauseListener = new OnClickListener() {
@Override
public void onClick(View v) {
if (mLibVLC.isPlaying())
pause();
else
play();
showOverlay();
}
};
/**
*
*/
private final OnClickListener mBackwardListener = new OnClickListener() {
@Override
public void onClick(View v) {
seek(-10000);
}
};
/**
*
*/
private final OnClickListener mForwardListener = new OnClickListener() {
@Override
public void onClick(View v) {
seek(10000);
}
};
public void seek(int delta) {
// unseekable stream
if(mLibVLC.getLength() <= 0 || !mCanSeek) return;
long position = mLibVLC.getTime() + delta;
if (position < 0) position = 0;
mLibVLC.setTime(position);
showOverlay();
}
/**
*
*/
private final OnClickListener mLockListener = new OnClickListener() {
@Override
public void onClick(View v) {
if (mIsLocked) {
mIsLocked = false;
unlockScreen();
} else {
mIsLocked = true;
lockScreen();
}
}
};
/**
*
*/
private final OnClickListener mSizeListener = new OnClickListener() {
@Override
public void onClick(View v) {
if (mCurrentSize < SURFACE_ORIGINAL) {
mCurrentSize++;
} else {
mCurrentSize = 0;
}
changeSurfaceSize();
switch (mCurrentSize) {
case SURFACE_BEST_FIT:
showInfo(R.string.surface_best_fit, 1000);
break;
case SURFACE_FIT_HORIZONTAL:
showInfo(R.string.surface_fit_horizontal, 1000);
break;
case SURFACE_FIT_VERTICAL:
showInfo(R.string.surface_fit_vertical, 1000);
break;
case SURFACE_FILL:
showInfo(R.string.surface_fill, 1000);
break;
case SURFACE_16_9:
showInfo("16:9", 1000);
break;
case SURFACE_4_3:
showInfo("4:3", 1000);
break;
case SURFACE_ORIGINAL:
showInfo(R.string.surface_original, 1000);
break;
}
showOverlay();
}
};
private final OnClickListener mRemainingTimeListener = new OnClickListener() {
@Override
public void onClick(View v) {
mDisplayRemainingTime = !mDisplayRemainingTime;
showOverlay();
}
};
/**
* attach and disattach surface to the lib
*/
private final SurfaceHolder.Callback mSurfaceCallback = new Callback() {
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
if(format == PixelFormat.RGBX_8888)
Log.d(TAG, "Pixel format is RGBX_8888");
else if(format == PixelFormat.RGB_565)
Log.d(TAG, "Pixel format is RGB_565");
else if(format == ImageFormat.YV12)
Log.d(TAG, "Pixel format is YV12");
else
Log.d(TAG, "Pixel format is other/unknown");
if(mLibVLC != null)
mLibVLC.attachSurface(holder.getSurface(), VideoPlayerActivity.this);
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
if(mLibVLC != null)
mLibVLC.detachSurface();
}
};
private final SurfaceHolder.Callback mSubtitlesSurfaceCallback = new Callback() {
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
if(mLibVLC != null)
mLibVLC.attachSubtitlesSurface(holder.getSurface());
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
if(mLibVLC != null)
mLibVLC.detachSubtitlesSurface();
}
};
/**
* show overlay the the default timeout
*/
private void showOverlay() {
showOverlay(OVERLAY_TIMEOUT);
}
/**
* show overlay
*/
private void showOverlay(int timeout) {
if (mIsNavMenu)
return;
mHandler.sendEmptyMessage(SHOW_PROGRESS);
if (!mShowing) {
mShowing = true;
if (!mIsLocked) {
mOverlayHeader.setVisibility(View.VISIBLE);
mOverlayOption.setVisibility(View.VISIBLE);
mPlayPause.setVisibility(View.VISIBLE);
mMenu.setVisibility(View.VISIBLE);
dimStatusBar(false);
}
mOverlayProgress.setVisibility(View.VISIBLE);
if (mPresentation != null) mOverlayBackground.setVisibility(View.VISIBLE);
}
Message msg = mHandler.obtainMessage(FADE_OUT);
if (timeout != 0) {
mHandler.removeMessages(FADE_OUT);
mHandler.sendMessageDelayed(msg, timeout);
}
updateOverlayPausePlay();
}
/**
* hider overlay
*/
private void hideOverlay(boolean fromUser) {
if (mShowing) {
mHandler.removeMessages(SHOW_PROGRESS);
Log.i(TAG, "remove View!");
if (mOverlayTips != null) mOverlayTips.setVisibility(View.INVISIBLE);
if (!fromUser && !mIsLocked) {
mOverlayHeader.startAnimation(AnimationUtils.loadAnimation(this, android.R.anim.fade_out));
mOverlayOption.startAnimation(AnimationUtils.loadAnimation(this, android.R.anim.fade_out));
mOverlayProgress.startAnimation(AnimationUtils.loadAnimation(this, android.R.anim.fade_out));
mPlayPause.startAnimation(AnimationUtils.loadAnimation(this, android.R.anim.fade_out));
mMenu.startAnimation(AnimationUtils.loadAnimation(this, android.R.anim.fade_out));
}
if (mPresentation != null) {
mOverlayBackground.startAnimation(AnimationUtils.loadAnimation(this, android.R.anim.fade_out));
mOverlayBackground.setVisibility(View.INVISIBLE);
}
mOverlayHeader.setVisibility(View.INVISIBLE);
mOverlayOption.setVisibility(View.INVISIBLE);
mOverlayProgress.setVisibility(View.INVISIBLE);
mPlayPause.setVisibility(View.INVISIBLE);
mMenu.setVisibility(View.INVISIBLE);
mShowing = false;
dimStatusBar(true);
}
}
/**
* Dim the status bar and/or navigation icons when needed on Android 3.x.
* Hide it on Android 4.0 and later
*/
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
private void dimStatusBar(boolean dim) {
if (!LibVlcUtil.isHoneycombOrLater() || !AndroidDevices.hasNavBar() || mIsNavMenu)
return;
int layout = 0;
if (!AndroidDevices.hasCombBar() && LibVlcUtil.isJellyBeanOrLater())
layout = View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
int visibility = (dim ? (AndroidDevices.hasCombBar()
? View.SYSTEM_UI_FLAG_LOW_PROFILE
: View.SYSTEM_UI_FLAG_HIDE_NAVIGATION)
: View.SYSTEM_UI_FLAG_VISIBLE) | layout;
mSurface.setSystemUiVisibility(visibility);
mSubtitlesSurface.setSystemUiVisibility(visibility);
}
private void updateOverlayPausePlay() {
if (mLibVLC == null)
return;
if (mPresentation == null)
mPlayPause.setBackgroundResource(mLibVLC.isPlaying() ? R.drawable.ic_pause_circle
: R.drawable.ic_play_circle);
else
mPlayPause.setBackgroundResource(mLibVLC.isPlaying() ? R.drawable.ic_pause_circle_big_o
: R.drawable.ic_play_circle_big_o);
}
/**
* update the overlay
*/
private int setOverlayProgress() {
if (mLibVLC == null) {
return 0;
}
int time = (int) mLibVLC.getTime();
int length = (int) mLibVLC.getLength();
if (length == 0) {
Media media = MediaDatabase.getInstance().getMedia(mLocation);
if (media != null)
length = (int) media.getLength();
}
// Update all view elements
boolean isSeekable = mEnableJumpButtons && length > 0;
mBackward.setVisibility(isSeekable ? View.VISIBLE : View.GONE);
mForward.setVisibility(isSeekable ? View.VISIBLE : View.GONE);
mSeekbar.setMax(length);
mSeekbar.setProgress(time);
mSysTime.setText(DateFormat.getTimeFormat(this).format(new Date(System.currentTimeMillis())));
if (time >= 0) mTime.setText(Strings.millisToString(time));
if (length >= 0) mLength.setText(mDisplayRemainingTime && length > 0
? "- " + Strings.millisToString(length - time)
: Strings.millisToString(length));
return time;
}
private void setESTracks() {
if (mLastAudioTrack >= 0) {
mLibVLC.setAudioTrack(mLastAudioTrack);
mLastAudioTrack = -1;
}
if (mLastSpuTrack >= -1) {
mLibVLC.setSpuTrack(mLastSpuTrack);
mLastSpuTrack = -2;
}
}
private void setESTrackLists(boolean force) {
if(mAudioTracksList == null || force) {
if (mLibVLC.getAudioTracksCount() > 2) {
mAudioTracksList = mLibVLC.getAudioTrackDescription();
mAudioTrack.setOnClickListener(mAudioTrackListener);
mAudioTrack.setVisibility(View.VISIBLE);
}
else {
mAudioTrack.setVisibility(View.GONE);
mAudioTrack.setOnClickListener(null);
}
}
if (mSubtitleTracksList == null || force) {
if (mLibVLC.getSpuTracksCount() > 0) {
mSubtitleTracksList = mLibVLC.getSpuTrackDescription();
mSubtitle.setOnClickListener(mSubtitlesListener);
mSubtitle.setVisibility(View.VISIBLE);
}
else {
mSubtitle.setVisibility(View.GONE);
mSubtitle.setOnClickListener(null);
}
}
}
/**
*
*/
private void play() {
mLibVLC.play();
mSurface.setKeepScreenOn(true);
}
/**
*
*/
private void pause() {
mLibVLC.pause();
mSurface.setKeepScreenOn(false);
}
/**
* External extras:
* - position (long) - position of the video to start with (in ms)
*/
@SuppressWarnings({ "unchecked" })
private void loadMedia() {
mLocation = null;
String title = getResources().getString(R.string.title);
boolean dontParse = false;
boolean fromStart = false;
String itemTitle = null;
int itemPosition = -1; // Index in the media list as passed by AudioServer (used only for vout transition internally)
long intentPosition = -1; // position passed in by intent (ms)
if (getIntent().getAction() != null
&& getIntent().getAction().equals(Intent.ACTION_VIEW)) {
/* Started from external application 'content' */
if (getIntent().getData() != null
&& getIntent().getData().getScheme() != null
&& getIntent().getData().getScheme().equals("content")) {
// Media or MMS URI
if(getIntent().getData().getHost().equals("media")
|| getIntent().getData().getHost().equals("mms")) {
try {
Cursor cursor = getContentResolver().query(getIntent().getData(),
new String[]{ MediaStore.Video.Media.DATA }, null, null, null);
if (cursor != null) {
int column_index = cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DATA);
if (cursor.moveToFirst())
mLocation = LibVLC.PathToURI(cursor.getString(column_index));
cursor.close();
}
} catch (Exception e) {
Log.e(TAG, "Couldn't read the file from media or MMS");
encounteredError();
}
}
// Mail-based apps - download the stream to a temporary file and play it
else if(getIntent().getData().getHost().equals("com.fsck.k9.attachmentprovider")
|| getIntent().getData().getHost().equals("gmail-ls")) {
try {
Cursor cursor = getContentResolver().query(getIntent().getData(),
new String[]{MediaStore.MediaColumns.DISPLAY_NAME}, null, null, null);
if (cursor != null) {
cursor.moveToFirst();
String filename = cursor.getString(cursor.getColumnIndex(MediaStore.MediaColumns.DISPLAY_NAME));
cursor.close();
Log.i(TAG, "Getting file " + filename + " from content:// URI");
InputStream is = getContentResolver().openInputStream(getIntent().getData());
OutputStream os = new FileOutputStream(Environment.getExternalStorageDirectory().getPath() + "/Download/" + filename);
byte[] buffer = new byte[1024];
int bytesRead = 0;
while((bytesRead = is.read(buffer)) >= 0) {
os.write(buffer, 0, bytesRead);
}
os.close();
is.close();
mLocation = LibVLC.PathToURI(Environment.getExternalStorageDirectory().getPath() + "/Download/" + filename);
}
} catch (Exception e) {
Log.e(TAG, "Couldn't download file from mail URI");
encounteredError();
}
}
// other content-based URI (probably file pickers)
else {
mLocation = getIntent().getData().getPath();
}
} /* External application */
else if (getIntent().getDataString() != null) {
// Plain URI
mLocation = getIntent().getDataString();
// Remove VLC prefix if needed
if (mLocation.startsWith("vlc://")) {
mLocation = mLocation.substring(6);
}
// Decode URI
if (!mLocation.contains("/")){
try {
mLocation = URLDecoder.decode(mLocation,"UTF-8");
} catch (UnsupportedEncodingException e) {
Log.w(TAG, "UnsupportedEncodingException while decoding MRL " + mLocation);
}
}
} else {
Log.e(TAG, "Couldn't understand the intent");
encounteredError();
}
// Try to get the position
if(getIntent().getExtras() != null)
intentPosition = getIntent().getExtras().getLong("position", -1);
} /* ACTION_VIEW */
/* Started from VideoListActivity */
else if(getIntent().getAction() != null
&& getIntent().getAction().equals(PLAY_FROM_VIDEOGRID)
&& getIntent().getExtras() != null) {
mLocation = getIntent().getExtras().getString("itemLocation");
itemTitle = getIntent().getExtras().getString("itemTitle");
dontParse = getIntent().getExtras().getBoolean("dontParse");
fromStart = getIntent().getExtras().getBoolean("fromStart");
itemPosition = getIntent().getExtras().getInt("itemPosition", -1);
}
mSurface.setKeepScreenOn(true);
if(mLibVLC == null)
return;
/* WARNING: hack to avoid a crash in mediacodec on KitKat.
* Disable hardware acceleration if the media has a ts extension. */
if (mLocation != null && LibVlcUtil.isKitKatOrLater()) {
String locationLC = mLocation.toLowerCase(Locale.ENGLISH);
if (locationLC.endsWith(".ts")
|| locationLC.endsWith(".tts")
|| locationLC.endsWith(".m2t")
|| locationLC.endsWith(".mts")
|| locationLC.endsWith(".m2ts")) {
mDisabledHardwareAcceleration = true;
mPreviousHardwareAccelerationMode = mLibVLC.getHardwareAcceleration();
// mLibVLC.setHardwareAcceleration(LibVLC.HW_ACCELERATION_DISABLED);
mLibVLC.setHardwareAcceleration(LibVLC.HW_ACCELERATION_FULL);
}
}
mLibVLC.setHardwareAcceleration(LibVLC.HW_ACCELERATION_DISABLED);
/* Start / resume playback */
if(dontParse && itemPosition >= 0) {
// Provided externally from AudioService
Log.d(TAG, "Continuing playback from AudioService at index " + itemPosition);
savedIndexPosition = itemPosition;
if(!mLibVLC.isPlaying()) {
// AudioService-transitioned playback for item after sleep and resume
mLibVLC.playIndex(savedIndexPosition);
dontParse = false;
}
else {
stopLoadingAnimation();
showOverlay();
}
updateNavStatus();
} else if (savedIndexPosition > -1) {
AudioServiceController.getInstance().stop(); // Stop the previous playback.
mLibVLC.setMediaList();
mLibVLC.playIndex(savedIndexPosition);
} else if (mLocation != null && mLocation.length() > 0 && !dontParse) {
AudioServiceController.getInstance().stop(); // Stop the previous playback.
mLibVLC.setMediaList();
mLibVLC.getMediaList().add(new Media(mLibVLC, mLocation));
savedIndexPosition = mLibVLC.getMediaList().size() - 1;
mLibVLC.playIndex(savedIndexPosition);
}
mCanSeek = false;
if (mLocation != null && mLocation.length() > 0 && !dontParse) {
// restore last position
Media media = MediaDatabase.getInstance().getMedia(mLocation);
if(media != null) {
// in media library
if(media.getTime() > 0 && !fromStart)
mLibVLC.setTime(media.getTime());
// Consume fromStart option after first use to prevent
// restarting again when playback is paused.
getIntent().putExtra("fromStart", false);
mLastAudioTrack = media.getAudioTrack();
mLastSpuTrack = media.getSpuTrack();
} else {
// not in media library
long rTime = mSettings.getLong(PreferencesActivity.VIDEO_RESUME_TIME, -1);
Editor editor = mSettings.edit();
editor.putLong(PreferencesActivity.VIDEO_RESUME_TIME, -1);
editor.commit();
if(rTime > 0)
mLibVLC.setTime(rTime);
if(intentPosition > 0)
mLibVLC.setTime(intentPosition);
}
// Get possible subtitles
String subtitleList_serialized = mSettings.getString(PreferencesActivity.VIDEO_SUBTITLE_FILES, null);
ArrayList prefsList = new ArrayList();
if(subtitleList_serialized != null) {
ByteArrayInputStream bis = new ByteArrayInputStream(subtitleList_serialized.getBytes());
try {
ObjectInputStream ois = new ObjectInputStream(bis);
prefsList = (ArrayList)ois.readObject();
} catch(ClassNotFoundException e) {}
catch (StreamCorruptedException e) {}
catch (IOException e) {}
}
for(String x : prefsList){
if(!mSubtitleSelectedFiles.contains(x))
mSubtitleSelectedFiles.add(x);
}
// Get the title
try {
title = URLDecoder.decode(mLocation, "UTF-8");
} catch (UnsupportedEncodingException e) {
} catch (IllegalArgumentException e) {
}
if (title.startsWith("file:")) {
title = new File(title).getName();
int dotIndex = title.lastIndexOf('.');
if (dotIndex != -1)
title = title.substring(0, dotIndex);
}
} else if(itemTitle != null) {
title = itemTitle;
}
mTitle.setText(title);
}
@SuppressWarnings("deprecation")
private int getScreenRotation(){
WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO /* Android 2.2 has getRotation */) {
try {
Method m = display.getClass().getDeclaredMethod("getRotation");
return (Integer) m.invoke(display);
} catch (Exception e) {
return Surface.ROTATION_0;
}
} else {
return display.getOrientation();
}
}
@TargetApi(Build.VERSION_CODES.GINGERBREAD)
private int getScreenOrientation(){
WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
int rot = getScreenRotation();
/*
* Since getRotation() returns the screen's "natural" orientation,
* which is not guaranteed to be SCREEN_ORIENTATION_PORTRAIT,
* we have to invert the SCREEN_ORIENTATION value if it is "naturally"
* landscape.
*/
@SuppressWarnings("deprecation")
boolean defaultWide = display.getWidth() > display.getHeight();
if(rot == Surface.ROTATION_90 || rot == Surface.ROTATION_270)
defaultWide = !defaultWide;
if(defaultWide) {
switch (rot) {
case Surface.ROTATION_0:
return ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
case Surface.ROTATION_90:
return ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
case Surface.ROTATION_180:
// SCREEN_ORIENTATION_REVERSE_PORTRAIT only available since API
// Level 9+
return (Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO ? ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE
: ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
case Surface.ROTATION_270:
// SCREEN_ORIENTATION_REVERSE_LANDSCAPE only available since API
// Level 9+
return (Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO ? ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT
: ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
default:
return 0;
}
} else {
switch (rot) {
case Surface.ROTATION_0:
return ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
case Surface.ROTATION_90:
return ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
case Surface.ROTATION_180:
// SCREEN_ORIENTATION_REVERSE_PORTRAIT only available since API
// Level 9+
return (Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO ? ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT
: ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
case Surface.ROTATION_270:
// SCREEN_ORIENTATION_REVERSE_LANDSCAPE only available since API
// Level 9+
return (Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO ? ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE
: ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
default:
return 0;
}
}
}
public void showAdvancedOptions(View v) {
CommonDialogs.advancedOptions(this, v, MenuType.Video);
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
private void createPresentation() {
if (mMediaRouter == null || mEnableCloneMode)
return;
// Get the current route and its presentation display.
MediaRouter.RouteInfo route = mMediaRouter.getSelectedRoute(
MediaRouter.ROUTE_TYPE_LIVE_VIDEO);
Display presentationDisplay = route != null ? route.getPresentationDisplay() : null;
if (presentationDisplay != null) {
// Show a new presentation if possible.
Log.i(TAG, "Showing presentation on display: " + presentationDisplay);
mPresentation = new SecondaryDisplay(this, presentationDisplay);
mPresentation.setOnDismissListener(mOnDismissListener);
try {
mPresentation.show();
} catch (WindowManager.InvalidDisplayException ex) {
Log.w(TAG, "Couldn't show presentation! Display was removed in "
+ "the meantime.", ex);
mPresentation = null;
}
} else
Log.i(TAG, "No secondary display detected");
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
private void removePresentation() {
if (mMediaRouter == null)
return;
// Dismiss the current presentation if the display has changed.
Log.i(TAG, "Dismissing presentation because the current route no longer "
+ "has a presentation display.");
mLibVLC.pause(); // Stop sending frames to avoid a crash.
finish(); //TODO restore the video on the new display instead of closing
if (mPresentation != null) mPresentation.dismiss();
mPresentation = null;
}
/**
* Listens for when presentations are dismissed.
*/
private final DialogInterface.OnDismissListener mOnDismissListener = new DialogInterface.OnDismissListener() {
@Override
public void onDismiss(DialogInterface dialog) {
if (dialog == mPresentation) {
Log.i(TAG, "Presentation was dismissed.");
mPresentation = null;
}
}
};
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
private final class SecondaryDisplay extends Presentation {
public final static String TAG = "VLC/SecondaryDisplay";
private SurfaceView mSurface;
private SurfaceView mSubtitlesSurface;
private SurfaceHolder mSurfaceHolder;
private SurfaceHolder mSubtitlesSurfaceHolder;
private FrameLayout mSurfaceFrame;
private LibVLC mLibVLC;
public SecondaryDisplay(Context context, Display display) {
super(context, display);
if (context instanceof Activity) {
setOwnerActivity((Activity) context);
}
try {
mLibVLC = VLCInstance.getLibVlcInstance();
} catch (LibVlcException e) {
Log.d(TAG, "LibVLC initialisation failed");
return;
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.player_remote);
mSurface = (SurfaceView) findViewById(R.id.remote_player_surface);
mSurfaceHolder = mSurface.getHolder();
mSurfaceFrame = (FrameLayout) findViewById(R.id.remote_player_surface_frame);
String chroma = mSettings.getString("chroma_format", "");
if(LibVlcUtil.isGingerbreadOrLater() && chroma.equals("YV12")) {
mSurfaceHolder.setFormat(ImageFormat.YV12);
} else if (chroma.equals("RV16")) {
mSurfaceHolder.setFormat(PixelFormat.RGB_565);
} else {
mSurfaceHolder.setFormat(PixelFormat.RGBX_8888);
}
VideoPlayerActivity activity = (VideoPlayerActivity)getOwnerActivity();
if (activity == null) {
Log.e(TAG, "Failed to get the VideoPlayerActivity instance, secondary display won't work");
return;
}
mSurfaceHolder.addCallback(activity.mSurfaceCallback);
mSubtitlesSurface = (SurfaceView) findViewById(R.id.remote_subtitles_surface);
mSubtitlesSurfaceHolder = mSubtitlesSurface.getHolder();
mSubtitlesSurfaceHolder.setFormat(PixelFormat.RGBA_8888);
mSubtitlesSurface.setZOrderMediaOverlay(true);
mSubtitlesSurfaceHolder.addCallback(activity.mSubtitlesSurfaceCallback);
/* Only show the subtitles surface when using "Full Acceleration" mode */
if (mLibVLC != null && mLibVLC.getHardwareAcceleration() == LibVLC.HW_ACCELERATION_FULL)
mSubtitlesSurface.setVisibility(View.VISIBLE);
Log.i(TAG, "Secondary display created");
}
}
/**
* Start the video loading animation.
*/
private void startLoadingAnimation() {
AnimationSet anim = new AnimationSet(true);
RotateAnimation rotate = new RotateAnimation(0f, 360f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
rotate.setDuration(800);
rotate.setInterpolator(new DecelerateInterpolator());
rotate.setRepeatCount(RotateAnimation.INFINITE);
anim.addAnimation(rotate);
mLoading.startAnimation(anim);
mLoadingText.setVisibility(View.VISIBLE);
}
/**
* Stop the video loading animation.
*/
private void stopLoadingAnimation() {
mLoading.setVisibility(View.INVISIBLE);
mLoading.clearAnimation();
mLoadingText.setVisibility(View.GONE);
}
public void onClickOverlayTips(View v) {
mOverlayTips.setVisibility(View.GONE);
}
public void onClickDismissTips(View v) {
mOverlayTips.setVisibility(View.GONE);
Editor editor = mSettings.edit();
editor.putBoolean(PREF_TIPS_SHOWN, true);
editor.commit();
}
private void updateNavStatus() {
mHasMenu = mLibVLC.getChapterCountForTitle(0) > 1 && mLibVLC.getTitleCount() > 1;
mIsNavMenu = mHasMenu && mLibVLC.getTitle() == 0;
/***
* HACK ALERT: assume that any media with >1 titles = DVD with menus
* Should be replaced with a more robust title/chapter selection popup
*/
Log.d(TAG,
"updateNavStatus: getChapterCountForTitle(0) = "
+ mLibVLC.getChapterCountForTitle(0)
+ ", getTitleCount() = " + mLibVLC.getTitleCount());
if (mIsNavMenu) {
/*
* Keep the overlay hidden in order to have touch events directly
* transmitted to navigation handling.
*/
hideOverlay(false);
}
else if (mHasMenu) {
setESTrackLists(true);
setESTracks();
/* Show the return to menu button. */
mNavMenu.setVisibility(View.VISIBLE);
mNavMenu.setOnClickListener(mNavMenuListener);
}
else
mNavMenu.setVisibility(View.GONE);
}
}
收到了很多人评论 关于没有android源码 的确现在VLC没有提供了源码 需要自己编译,这里我把最近收藏的一个已经编译好的版本分享出来
https://pan.baidu.com/s/1eSbZqeA