第三篇、vlc-android之UDP组播使用

       vlc-android 非常的强大,目前我们主要是使用vlc来播送组播UDP TS流。类似地址:udp://@239.1.1.0:2860这样的地址。在此之前发现声音会卡顿,效果不太好。今天发现是底层驱动的问题。

      但是在进行vlc stop时候不能进行资源释放,很是奇怪。明天解决这个问题。文章后续会继续整理!!!

     ---------------------------------------------------------------------------

      针对此前遇到vlc stop时候不能进行资源释放,后来是参考了VLC-android的源码,

具体类文件目录是:


类的代码:看一下VLC是如何释放的,这样处理逻辑比较好。

 

[java]  view plain copy
 
  1. /***************************************************************************** 
  2.  * VideoPlayerActivity.java 
  3.  ***************************************************************************** 
  4.  * Copyright © 2011-2014 VLC authors and VideoLAN 
  5.  * 
  6.  * This program is free software; you can redistribute it and/or modify 
  7.  * it under the terms of the GNU General Public License as published by 
  8.  * the Free Software Foundation; either version 2 of the License, or 
  9.  * (at your option) any later version. 
  10.  * 
  11.  * This program is distributed in the hope that it will be useful, 
  12.  * but WITHOUT ANY WARRANTY; without even the implied warranty of 
  13.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
  14.  * GNU General Public License for more details. 
  15.  * 
  16.  * You should have received a copy of the GNU General Public License 
  17.  * along with this program; if not, write to the Free Software 
  18.  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. 
  19.  *****************************************************************************/  
  20.   
  21. package org.videolan.vlc.gui.video;  
  22.   
  23. import java.io.ByteArrayInputStream;  
  24. import java.io.ByteArrayOutputStream;  
  25. import java.io.File;  
  26. import java.io.FileOutputStream;  
  27. import java.io.IOException;  
  28. import java.io.InputStream;  
  29. import java.io.ObjectInputStream;  
  30. import java.io.ObjectOutputStream;  
  31. import java.io.OutputStream;  
  32. import java.io.StreamCorruptedException;  
  33. import java.io.UnsupportedEncodingException;  
  34. import java.lang.reflect.Method;  
  35. import java.net.URLDecoder;  
  36. import java.util.ArrayList;  
  37. import java.util.Date;  
  38. import java.util.Locale;  
  39. import java.util.Map;  
  40.   
  41. import org.videolan.libvlc.EventHandler;  
  42. import org.videolan.libvlc.IVideoPlayer;  
  43. import org.videolan.libvlc.LibVLC;  
  44. import org.videolan.libvlc.LibVlcException;  
  45. import org.videolan.libvlc.LibVlcUtil;  
  46. import org.videolan.libvlc.Media;  
  47. import org.videolan.vlc.MediaDatabase;  
  48. import org.videolan.vlc.R;  
  49. import org.videolan.vlc.VLCApplication;  
  50. import org.videolan.vlc.audio.AudioServiceController;  
  51. import org.videolan.vlc.gui.CommonDialogs;  
  52. import org.videolan.vlc.gui.CommonDialogs.MenuType;  
  53. import org.videolan.vlc.gui.MainActivity;  
  54. import org.videolan.vlc.gui.PreferencesActivity;  
  55. import org.videolan.vlc.util.AndroidDevices;  
  56. import org.videolan.vlc.util.Strings;  
  57. import org.videolan.vlc.util.VLCInstance;  
  58. import org.videolan.vlc.util.WeakHandler;  
  59.   
  60. import android.annotation.TargetApi;  
  61. import android.app.Activity;  
  62. import android.app.AlertDialog;  
  63. import android.app.KeyguardManager;  
  64. import android.app.Presentation;  
  65. import android.content.BroadcastReceiver;  
  66. import android.content.Context;  
  67. import android.content.DialogInterface;  
  68. import android.content.Intent;  
  69. import android.content.IntentFilter;  
  70. import android.content.SharedPreferences;  
  71. import android.content.SharedPreferences.Editor;  
  72. import android.content.pm.ActivityInfo;  
  73. import android.content.res.Configuration;  
  74. import android.database.Cursor;  
  75. import android.graphics.Color;  
  76. import android.graphics.ImageFormat;  
  77. import android.graphics.PixelFormat;  
  78. import android.media.AudioManager;  
  79. import android.media.AudioManager.OnAudioFocusChangeListener;  
  80. import android.media.MediaRouter;  
  81. import android.os.Build;  
  82. import android.os.Bundle;  
  83. import android.os.Environment;  
  84. import android.os.Handler;  
  85. import android.os.Message;  
  86. import android.preference.PreferenceManager;  
  87. import android.provider.MediaStore;  
  88. import android.provider.Settings.SettingNotFoundException;  
  89. import android.text.format.DateFormat;  
  90. import android.util.DisplayMetrics;  
  91. import android.util.Log;  
  92. import android.view.Display;  
  93. import android.view.MotionEvent;  
  94. import android.view.Surface;  
  95. import android.view.SurfaceHolder;  
  96. import android.view.SurfaceHolder.Callback;  
  97. import android.view.SurfaceView;  
  98. import android.view.View;  
  99. import android.view.View.OnClickListener;  
  100. import android.view.View.OnSystemUiVisibilityChangeListener;  
  101. import android.view.ViewGroup.LayoutParams;  
  102. import android.view.WindowManager;  
  103. import android.view.animation.Animation;  
  104. import android.view.animation.AnimationSet;  
  105. import android.view.animation.AnimationUtils;  
  106. import android.view.animation.DecelerateInterpolator;  
  107. import android.view.animation.RotateAnimation;  
  108. import android.widget.FrameLayout;  
  109. import android.widget.ImageButton;  
  110. import android.widget.ImageView;  
  111. import android.widget.SeekBar;  
  112. import android.widget.SeekBar.OnSeekBarChangeListener;  
  113. import android.widget.TextView;  
  114.   
  115. public class VideoPlayerActivity extends Activity implements IVideoPlayer {  
  116.     public final static String TAG = "VLC/VideoPlayerActivity";  
  117.   
  118.     // Internal intent identifier to distinguish between internal launch and  
  119.     // external intent.  
  120.     private final static String PLAY_FROM_VIDEOGRID = "org.videolan.vlc.gui.video.PLAY_FROM_VIDEOGRID";  
  121.   
  122.     private SurfaceView mSurface;  
  123.     private SurfaceView mSubtitlesSurface;  
  124.     private SurfaceHolder mSurfaceHolder;  
  125.     private SurfaceHolder mSubtitlesSurfaceHolder;  
  126.     private FrameLayout mSurfaceFrame;  
  127.     private MediaRouter mMediaRouter;  
  128.     private MediaRouter.SimpleCallback mMediaRouterCallback;  
  129.     private SecondaryDisplay mPresentation;  
  130.     private LibVLC mLibVLC;  
  131.     private String mLocation;  
  132.   
  133.     private static final int SURFACE_BEST_FIT = 0;  
  134.     private static final int SURFACE_FIT_HORIZONTAL = 1;  
  135.     private static final int SURFACE_FIT_VERTICAL = 2;  
  136.     private static final int SURFACE_FILL = 3;  
  137.     private static final int SURFACE_16_9 = 4;  
  138.     private static final int SURFACE_4_3 = 5;  
  139.     private static final int SURFACE_ORIGINAL = 6;  
  140.     private int mCurrentSize = SURFACE_BEST_FIT;  
  141.   
  142.     private SharedPreferences mSettings;  
  143.   
  144.     /** Overlay */  
  145.     private View mOverlayHeader;  
  146.     private View mOverlayOption;  
  147.     private View mOverlayProgress;  
  148.     private View mOverlayBackground;  
  149.     private static final int OVERLAY_TIMEOUT = 4000;  
  150.     private static final int OVERLAY_INFINITE = 3600000;  
  151.     private static final int FADE_OUT = 1;  
  152.     private static final int SHOW_PROGRESS = 2;  
  153.     private static final int SURFACE_SIZE = 3;  
  154.     private static final int AUDIO_SERVICE_CONNECTION_SUCCESS = 5;  
  155.     private static final int AUDIO_SERVICE_CONNECTION_FAILED = 6;  
  156.     private static final int FADE_OUT_INFO = 4;  
  157.     private boolean mDragging;  
  158.     private boolean mShowing;  
  159.     private int mUiVisibility = -1;  
  160.     private SeekBar mSeekbar;  
  161.     private TextView mTitle;  
  162.     private TextView mSysTime;  
  163.     private TextView mBattery;  
  164.     private TextView mTime;  
  165.     private TextView mLength;  
  166.     private TextView mInfo;  
  167.     private ImageView mLoading;  
  168.     private TextView mLoadingText;  
  169.     private ImageButton mPlayPause;  
  170.     private ImageButton mBackward;  
  171.     private ImageButton mForward;  
  172.     private boolean mEnableJumpButtons;  
  173.     private boolean mEnableBrightnessGesture;  
  174.     private boolean mEnableCloneMode;  
  175.     private boolean mDisplayRemainingTime = false;  
  176.     private int mScreenOrientation;  
  177.     private ImageButton mAudioTrack;  
  178.     private ImageButton mSubtitle;  
  179.     private ImageButton mLock;  
  180.     private ImageButton mSize;  
  181.     private ImageButton mMenu;  
  182.     private boolean mIsLocked = false;  
  183.     private int mLastAudioTrack = -1;  
  184.     private int mLastSpuTrack = -2;  
  185.   
  186.     /** 
  187.      * For uninterrupted switching between audio and video mode 
  188.      */  
  189.     private boolean mSwitchingView;  
  190.     private boolean mEndReached;  
  191.     private boolean mCanSeek;  
  192.   
  193.     // Playlist  
  194.     private int savedIndexPosition = -1;  
  195.   
  196.     // size of the video  
  197.     private int mVideoHeight;  
  198.     private int mVideoWidth;  
  199.     private int mVideoVisibleHeight;  
  200.     private int mVideoVisibleWidth;  
  201.     private int mSarNum;  
  202.     private int mSarDen;  
  203.   
  204.     //Volume  
  205.     private AudioManager mAudioManager;  
  206.     private int mAudioMax;  
  207.     private OnAudioFocusChangeListener mAudioFocusListener;  
  208.   
  209.     //Touch Events  
  210.     private static final int TOUCH_NONE = 0;  
  211.     private static final int TOUCH_VOLUME = 1;  
  212.     private static final int TOUCH_BRIGHTNESS = 2;  
  213.     private static final int TOUCH_SEEK = 3;  
  214.     private int mTouchAction;  
  215.     private int mSurfaceYDisplayRange;  
  216.     private float mTouchY, mTouchX, mVol;  
  217.   
  218.     // Brightness  
  219.     private boolean mIsFirstBrightnessGesture = true;  
  220.   
  221.     // Tracks & Subtitles  
  222.     private Map<Integer,String> mAudioTracksList;  
  223.     private Map<Integer,String> mSubtitleTracksList;  
  224.     /** 
  225.      * Used to store a selected subtitle; see onActivityResult. 
  226.      * It is possible to have multiple custom subs in one session 
  227.      * (just like desktop VLC allows you as well.) 
  228.      */  
  229.     private final ArrayList<String> mSubtitleSelectedFiles = new ArrayList<String>();  
  230.   
  231.     // Whether fallback from HW acceleration to SW decoding was done.  
  232.     private boolean mDisabledHardwareAcceleration = false;  
  233.     private int mPreviousHardwareAccelerationMode;  
  234.   
  235.     // Tips  
  236.     private View mOverlayTips;  
  237.     private static final String PREF_TIPS_SHOWN = "video_player_tips_shown";  
  238.   
  239.     // Navigation handling (DVD, Blu-Ray...)  
  240.     private ImageButton mNavMenu;  
  241.     private boolean mHasMenu = false;  
  242.     private boolean mIsNavMenu = false;  
  243.   
  244.     @Override  
  245.     @TargetApi(Build.VERSION_CODES.JELLY_BEAN)  
  246.     protected void onCreate(Bundle savedInstanceState) {  
  247.         super.onCreate(savedInstanceState);  
  248.         Log.i(TAG, "onCreate.");  
  249.         if (LibVlcUtil.isJellyBeanMR1OrLater()) {  
  250.             // Get the media router service (Miracast)  
  251.             mMediaRouter = (MediaRouter) getSystemService(Context.MEDIA_ROUTER_SERVICE);  
  252.             mMediaRouterCallback = new MediaRouter.SimpleCallback() {  
  253.                 @Override  
  254.                 public void onRoutePresentationDisplayChanged(  
  255.                         MediaRouter router, MediaRouter.RouteInfo info) {  
  256.                     Log.d(TAG, "onRoutePresentationDisplayChanged: info=" + info);  
  257.                     removePresentation();  
  258.                 }  
  259.             };  
  260.             Log.d(TAG, "MediaRouter information : " + mMediaRouter  .toString());  
  261.         }  
  262.   
  263.         mSettings = PreferenceManager.getDefaultSharedPreferences(this);  
  264.   
  265.         /* Services and miscellaneous */  
  266.         mAudioManager = (AudioManager) getSystemService(AUDIO_SERVICE);  
  267.         mAudioMax = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);  
  268.   
  269.         mEnableCloneMode = mSettings.getBoolean("enable_clone_mode"false);  
  270.         createPresentation();  
  271.         setContentView(mPresentation == null ? R.layout.player : R.layout.player_remote_control);  
  272.   
  273.         if (LibVlcUtil.isICSOrLater())  
  274.             getWindow().getDecorView().findViewById(android.R.id.content).setOnSystemUiVisibilityChangeListener(  
  275.                     new OnSystemUiVisibilityChangeListener() {  
  276.                         @Override  
  277.                         public void onSystemUiVisibilityChange(int visibility) {  
  278.                             if (visibility == mUiVisibility)  
  279.                                 return;  
  280.                             setSurfaceSize(mVideoWidth, mVideoHeight, mVideoVisibleWidth, mVideoVisibleHeight, mSarNum, mSarDen);  
  281.                             if (visibility == View.SYSTEM_UI_FLAG_VISIBLE && !mShowing && !isFinishing()) {  
  282.                                 showOverlay();  
  283.                             }  
  284.                             mUiVisibility = visibility;  
  285.                         }  
  286.                     }  
  287.             );  
  288.   
  289.         /** initialize Views an their Events */  
  290.         mOverlayHeader = findViewById(R.id.player_overlay_header);  
  291.         mOverlayOption = findViewById(R.id.option_overlay);  
  292.         mOverlayProgress = findViewById(R.id.progress_overlay);  
  293.         mOverlayBackground = findViewById(R.id.player_overlay_background);  
  294.   
  295.         /* header */  
  296.         mTitle = (TextView) findViewById(R.id.player_overlay_title);  
  297.         mSysTime = (TextView) findViewById(R.id.player_overlay_systime);  
  298.         mBattery = (TextView) findViewById(R.id.player_overlay_battery);  
  299.   
  300.         // Position and remaining time  
  301.         mTime = (TextView) findViewById(R.id.player_overlay_time);  
  302.         mTime.setOnClickListener(mRemainingTimeListener);  
  303.         mLength = (TextView) findViewById(R.id.player_overlay_length);  
  304.         mLength.setOnClickListener(mRemainingTimeListener);  
  305.   
  306.         // the info textView is not on the overlay  
  307.         mInfo = (TextView) findViewById(R.id.player_overlay_info);  
  308.   
  309.         mEnableBrightnessGesture = mSettings.getBoolean("enable_brightness_gesture"true);  
  310.         mScreenOrientation = Integer.valueOf(  
  311.                 mSettings.getString("screen_orientation_value""4" /*SCREEN_ORIENTATION_SENSOR*/));  
  312.   
  313.         mEnableJumpButtons = mSettings.getBoolean("enable_jump_buttons"false);  
  314.         mPlayPause = (ImageButton) findViewById(R.id.player_overlay_play);  
  315.         mPlayPause.setOnClickListener(mPlayPauseListener);  
  316.         mBackward = (ImageButton) findViewById(R.id.player_overlay_backward);  
  317.         mBackward.setOnClickListener(mBackwardListener);  
  318.         mForward = (ImageButton) findViewById(R.id.player_overlay_forward);  
  319.         mForward.setOnClickListener(mForwardListener);  
  320.   
  321.         mAudioTrack = (ImageButton) findViewById(R.id.player_overlay_audio);  
  322.         mAudioTrack.setVisibility(View.GONE);  
  323.         mSubtitle = (ImageButton) findViewById(R.id.player_overlay_subtitle);  
  324.         mSubtitle.setVisibility(View.GONE);  
  325.         mNavMenu = (ImageButton) findViewById(R.id.player_overlay_navmenu);  
  326.         mNavMenu.setVisibility(View.GONE);  
  327.   
  328.         mLock = (ImageButton) findViewById(R.id.lock_overlay_button);  
  329.         mLock.setOnClickListener(mLockListener);  
  330.   
  331.         mSize = (ImageButton) findViewById(R.id.player_overlay_size);  
  332.         mSize.setOnClickListener(mSizeListener);  
  333.   
  334.         mMenu = (ImageButton) findViewById(R.id.player_overlay_adv_function);  
  335.   
  336.         try {  
  337.             mLibVLC = VLCInstance.getLibVlcInstance();  
  338.         } catch (LibVlcException e) {  
  339.             Log.d(TAG, "LibVLC initialisation failed");  
  340.             return;  
  341.         }  
  342.   
  343.         mSurface = (SurfaceView) findViewById(R.id.player_surface);  
  344.         mSurfaceHolder = mSurface.getHolder();  
  345.         mSurfaceFrame = (FrameLayout) findViewById(R.id.player_surface_frame);  
  346.         String chroma = mSettings.getString("chroma_format""");  
  347.         if(LibVlcUtil.isGingerbreadOrLater() && chroma.equals("YV12")) {  
  348.             mSurfaceHolder.setFormat(ImageFormat.YV12);  
  349.         } else if (chroma.equals("RV16")) {  
  350.             mSurfaceHolder.setFormat(PixelFormat.RGB_565);  
  351.         } else {  
  352.             mSurfaceHolder.setFormat(PixelFormat.RGBX_8888);  
  353.         }  
  354.   
  355.         mSubtitlesSurface = (SurfaceView) findViewById(R.id.subtitles_surface);  
  356.         mSubtitlesSurfaceHolder = mSubtitlesSurface.getHolder();  
  357.         mSubtitlesSurfaceHolder.setFormat(PixelFormat.RGBA_8888);  
  358.         mSubtitlesSurface.setZOrderMediaOverlay(true);  
  359.         if (mPresentation == null) {  
  360.             mSurfaceHolder.addCallback(mSurfaceCallback);  
  361.             mSubtitlesSurfaceHolder.addCallback(mSubtitlesSurfaceCallback);  
  362.         }  
  363.   
  364.         mSeekbar = (SeekBar) findViewById(R.id.player_overlay_seekbar);  
  365.         mSeekbar.setOnSeekBarChangeListener(mSeekListener);  
  366.   
  367.         /* Loading view */  
  368.         mLoading = (ImageView) findViewById(R.id.player_overlay_loading);  
  369.         mLoadingText = (TextView) findViewById(R.id.player_overlay_loading_text);  
  370.         startLoadingAnimation();  
  371.   
  372.         mSwitchingView = false;  
  373.         mEndReached = false;  
  374.   
  375.         // Clear the resume time, since it is only used for resumes in external  
  376.         // videos.  
  377.         SharedPreferences.Editor editor = mSettings.edit();  
  378.         editor.putLong(PreferencesActivity.VIDEO_RESUME_TIME, -1);  
  379.         // Also clear the subs list, because it is supposed to be per session  
  380.         // only (like desktop VLC). We don't want the customs subtitle file  
  381.         // to persist forever with this video.  
  382.         editor.putString(PreferencesActivity.VIDEO_SUBTITLE_FILES, null);  
  383.         editor.commit();  
  384.   
  385.         IntentFilter filter = new IntentFilter();  
  386.         filter.addAction(Intent.ACTION_BATTERY_CHANGED);  
  387.         filter.addAction(VLCApplication.SLEEP_INTENT);  
  388.         registerReceiver(mReceiver, filter);  
  389.   
  390.         Log.d(TAG,  
  391.                 "Hardware acceleration mode: "  
  392.                         + Integer.toString(mLibVLC.getHardwareAcceleration()));  
  393.   
  394.         /* Only show the subtitles surface when using "Full Acceleration" mode */  
  395.         if (mLibVLC.getHardwareAcceleration() == LibVLC.HW_ACCELERATION_FULL)  
  396.             mSubtitlesSurface.setVisibility(View.VISIBLE);  
  397.         // Signal to LibVLC that the videoPlayerActivity was created, thus the  
  398.         // SurfaceView is now available for MediaCodec direct rendering.  
  399.         mLibVLC.eventVideoPlayerActivityCreated(true);  
  400.         //mLibVLC.setHardwareAcceleration(LibVLC.HW_ACCELERATION_FULL);  
  401.         EventHandler em = EventHandler.getInstance();  
  402.         em.addHandler(eventHandler);  
  403.   
  404.         this.setVolumeControlStream(AudioManager.STREAM_MUSIC);  
  405.   
  406.         // Extra initialization when no secondary display is detected  
  407.         if (mPresentation == null) {  
  408.             // Orientation  
  409.             // 100 is the value for screen_orientation_start_lock  
  410.             setRequestedOrientation(mScreenOrientation != 100  
  411.                     ? mScreenOrientation  
  412.                     : getScreenOrientation());  
  413.             // Tips  
  414.             mOverlayTips = findViewById(R.id.player_overlay_tips);  
  415.             if(mSettings.getBoolean(PREF_TIPS_SHOWN, false))  
  416.                 mOverlayTips.setVisibility(View.GONE);  
  417.             else {  
  418.                 mOverlayTips.bringToFront();  
  419.                 mOverlayTips.invalidate();  
  420.             }  
  421.         } else  
  422.             setRequestedOrientation(getScreenOrientation());  
  423.   
  424.         updateNavStatus();  
  425.     }  
  426.   
  427.     @Override  
  428.     protected void onPause() {  
  429.         super.onPause();  
  430.         Log.i(TAG, "onPause. mSwitchingView="+mSwitchingView);  
  431.         if (mMediaRouter != null) {  
  432.             // Stop listening for changes to media routes.  
  433.             mediaRouterAddCallback(false);  
  434.         }  
  435.   
  436.         if(mSwitchingView) {  
  437.             Log.d(TAG, "mLocation = \"" + mLocation + "\"");  
  438.             AudioServiceController.getInstance().showWithoutParse(savedIndexPosition);  
  439.             AudioServiceController.getInstance().unbindAudioService(this);  
  440.             return;  
  441.         }  
  442.   
  443.         long time = mLibVLC.getTime();  
  444.         long length = mLibVLC.getLength();  
  445.         //remove saved position if in the last 5 seconds  
  446.         if (length - time < 5000)  
  447.             time = 0;  
  448.         else  
  449.             time -= 5000// go back 5 seconds, to compensate loading time  
  450.   
  451.         /* 
  452.          * Pausing here generates errors because the vout is constantly 
  453.          * trying to refresh itself every 80ms while the surface is not 
  454.          * accessible anymore. 
  455.          * To workaround that, we keep the last known position in the playlist 
  456.          * in savedIndexPosition to be able to restore it during onResume(). 
  457.          */  
  458.         mLibVLC.stop();  
  459.   
  460.         mSurface.setKeepScreenOn(false);  
  461.   
  462.         SharedPreferences.Editor editor = mSettings.edit();  
  463.         // Save position  
  464.         if (time >= 0 && mCanSeek) {  
  465.             if(MediaDatabase.getInstance().mediaItemExists(mLocation)) {  
  466.                 MediaDatabase.getInstance().updateMedia(  
  467.                         mLocation,  
  468.                         MediaDatabase.mediaColumn.MEDIA_TIME,  
  469.                         time);  
  470.             } else {  
  471.                 // Video file not in media library, store time just for onResume()  
  472.                 editor.putLong(PreferencesActivity.VIDEO_RESUME_TIME, time);  
  473.             }  
  474.         }  
  475.         // Save selected subtitles  
  476.         String subtitleList_serialized = null;  
  477.         if(mSubtitleSelectedFiles.size() > 0) {  
  478.             Log.d(TAG, "Saving selected subtitle files");  
  479.             ByteArrayOutputStream bos = new ByteArrayOutputStream();  
  480.             try {  
  481.                 ObjectOutputStream oos = new ObjectOutputStream(bos);  
  482.                 oos.writeObject(mSubtitleSelectedFiles);  
  483.                 subtitleList_serialized = bos.toString();  
  484.             } catch(IOException e) {}  
  485.         }  
  486.         editor.putString(PreferencesActivity.VIDEO_SUBTITLE_FILES, subtitleList_serialized);  
  487.   
  488.         editor.commit();  
  489.         AudioServiceController.getInstance().unbindAudioService(this);  
  490.     }  
  491.   
  492.     @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)  
  493.     @Override  
  494.     protected void onStop() {  
  495.         super.onStop();  
  496.         Log.i(TAG, "onStop.");  
  497.         // Dismiss the presentation when the activity is not visible.  
  498.         if (mPresentation != null) {  
  499.             Log.i(TAG, "Dismissing presentation because the activity is no longer visible.");  
  500.             mPresentation.dismiss();  
  501.             mPresentation = null;  
  502.         }  
  503.     }  
  504.   
  505.     @Override  
  506.     protected void onDestroy() {  
  507.         super.onDestroy();  
  508.         Log.i(TAG, "onDestroy.");  
  509.         unregisterReceiver(mReceiver);  
  510.   
  511.         EventHandler em = EventHandler.getInstance();  
  512.         em.removeHandler(eventHandler);  
  513.   
  514.         // MediaCodec opaque direct rendering should not be used anymore since there is no surface to attach.  
  515.         mLibVLC.eventVideoPlayerActivityCreated(false);  
  516.         // HW acceleration was temporarily disabled because of an error, restore the previous value.  
  517.         if (mDisabledHardwareAcceleration)  
  518.             mLibVLC.setHardwareAcceleration(mPreviousHardwareAccelerationMode);  
  519.   
  520.         mAudioManager = null;  
  521.     }  
  522.   
  523.     @Override  
  524.     protected void onResume() {  
  525.         super.onResume();  
  526.         Log.i(TAG, "onResume.");  
  527.           
  528.         mSwitchingView = false;  
  529.         AudioServiceController.getInstance().bindAudioService(this,  
  530.                 new AudioServiceController.AudioServiceConnectionListener() {  
  531.             @Override  
  532.             public void onConnectionSuccess() {  
  533.                 mHandler.sendEmptyMessage(AUDIO_SERVICE_CONNECTION_SUCCESS);  
  534.             }  
  535.   
  536.             @Override  
  537.             public void onConnectionFailed() {  
  538.                 mHandler.sendEmptyMessage(AUDIO_SERVICE_CONNECTION_FAILED);  
  539.             }  
  540.         });  
  541.   
  542.         if (mMediaRouter != null) {  
  543.             // Listen for changes to media routes.  
  544.             mediaRouterAddCallback(true);  
  545.         }  
  546.     }  
  547.   
  548.     /** 
  549.      * Add or remove MediaRouter callbacks. This is provided for version targeting. 
  550.      * 
  551.      * @param add true to add, false to remove 
  552.      */  
  553.     @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)  
  554.     private void mediaRouterAddCallback(boolean add) {  
  555.         if(!LibVlcUtil.isJellyBeanMR1OrLater() || mMediaRouter == nullreturn;  
  556.   
  557.         if(add)  
  558.             mMediaRouter.addCallback(MediaRouter.ROUTE_TYPE_LIVE_VIDEO, mMediaRouterCallback);  
  559.         else  
  560.             mMediaRouter.removeCallback(mMediaRouterCallback);  
  561.     }  
  562.   
  563.     private void startPlayback() {  
  564.          Log.i(TAG, "startPlayback.");  
  565.            
  566.           
  567.         loadMedia();  
  568.   
  569.         /* 
  570.          * if the activity has been paused by pressing the power button, 
  571.          * pressing it again will show the lock screen. 
  572.          * But onResume will also be called, even if vlc-android is still in the background. 
  573.          * To workaround that, pause playback if the lockscreen is displayed 
  574.          */  
  575.         mHandler.postDelayed(new Runnable() {  
  576.             @Override  
  577.             public void run() {  
  578.                 if (mLibVLC != null && mLibVLC.isPlaying()) {  
  579.                     KeyguardManager km = (KeyguardManager)getSystemService(KEYGUARD_SERVICE);  
  580.                     if (km.inKeyguardRestrictedInputMode())  
  581.                         mLibVLC.pause();  
  582.                 }  
  583.             }}, 500);  
  584.   
  585.         // Add any selected subtitle file from the file picker  
  586.         if(mSubtitleSelectedFiles.size() > 0) {  
  587.             for(String file : mSubtitleSelectedFiles) {  
  588.                 Log.i(TAG, "Adding user-selected subtitle " + file);  
  589.                 mLibVLC.addSubtitleTrack(file);  
  590.             }  
  591.         }  
  592.     }  
  593.   
  594.     @Override  
  595.     protected void onActivityResult(int requestCode, int resultCode, Intent data) {  
  596.         if(data == nullreturn;  
  597.   
  598.         if(data.getDataString() == null) {  
  599.             Log.d(TAG, "Subtitle selection dialog was cancelled");  
  600.         }  
  601.         if(data.getData() == nullreturn;  
  602.   
  603.         String subtitlePath = data.getData().getPath();  
  604.         if(requestCode == CommonDialogs.INTENT_SPECIFIC) {  
  605.             Log.d(TAG, "Specific subtitle file: " + subtitlePath);  
  606.         } else if(requestCode == CommonDialogs.INTENT_GENERIC) {  
  607.             Log.d(TAG, "Generic subtitle file: " + subtitlePath);  
  608.         }  
  609.         mSubtitleSelectedFiles.add(subtitlePath);  
  610.     }  
  611.   
  612.     public static void start(Context context, String location) {  
  613.         start(context, location, null, -1falsefalse);  
  614.     }  
  615.   
  616.     public static void start(Context context, String location, Boolean fromStart) {  
  617.         start(context, location, null, -1false, fromStart);  
  618.     }  
  619.   
  620.     public static void start(Context context, String location, String title, Boolean dontParse) {  
  621.         start(context, location, title, -1, dontParse, false);  
  622.     }  
  623.   
  624.     public static void start(Context context, String location, String title, int position, Boolean dontParse) {  
  625.         start(context, location, title, position, dontParse, false);  
  626.     }  
  627.   
  628.     public static void start(Context context, String location, String title, int position, Boolean dontParse, Boolean fromStart) {  
  629.         Intent intent = new Intent(context, VideoPlayerActivity.class);  
  630.         intent.setAction(VideoPlayerActivity.PLAY_FROM_VIDEOGRID);  
  631.         intent.putExtra("itemLocation", location);  
  632.         intent.putExtra("itemTitle", title);  
  633.         intent.putExtra("dontParse", dontParse);  
  634.         intent.putExtra("fromStart", fromStart);  
  635.         intent.putExtra("itemPosition", position);  
  636.   
  637.         if (dontParse)  
  638.             intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);  
  639.   
  640.         context.startActivity(intent);  
  641.     }  
  642.   
  643.     private final BroadcastReceiver mReceiver = new BroadcastReceiver()  
  644.     {  
  645.         @Override  
  646.         public void onReceive(Context context, Intent intent)  
  647.         {  
  648.             String action = intent.getAction();  
  649.             if (action.equalsIgnoreCase(Intent.ACTION_BATTERY_CHANGED)) {  
  650.                 int batteryLevel = intent.getIntExtra("level"0);  
  651.                 if (batteryLevel >= 50)  
  652.                     mBattery.setTextColor(Color.GREEN);  
  653.                 else if (batteryLevel >= 30)  
  654.                     mBattery.setTextColor(Color.YELLOW);  
  655.                 else  
  656.                     mBattery.setTextColor(Color.RED);  
  657.                 mBattery.setText(String.format("%d%%", batteryLevel));  
  658.             }  
  659.             else if (action.equalsIgnoreCase(VLCApplication.SLEEP_INTENT)) {  
  660.                 finish();  
  661.             }  
  662.         }  
  663.     };  
  664.   
  665.     @Override  
  666.     public boolean onTrackballEvent(MotionEvent event) {  
  667.         showOverlay();  
  668.         return true;  
  669.     }  
  670.   
  671.     @Override  
  672.     public void onConfigurationChanged(Configuration newConfig) {  
  673.         setSurfaceSize(mVideoWidth, mVideoHeight, mVideoVisibleWidth, mVideoVisibleHeight, mSarNum, mSarDen);  
  674.         super.onConfigurationChanged(newConfig);  
  675.     }  
  676.   
  677.     @Override  
  678.     public void setSurfaceSize(int width, int height, int visible_width, int visible_height, int sar_num, int sar_den) {  
  679.         if (width * height == 0)  
  680.             return;  
  681.   
  682.         // store video size  
  683.         mVideoHeight = height;  
  684.         mVideoWidth = width;  
  685.         mVideoVisibleHeight = visible_height;  
  686.         mVideoVisibleWidth  = visible_width;  
  687.         mSarNum = sar_num;  
  688.         mSarDen = sar_den;  
  689.         Message msg = mHandler.obtainMessage(SURFACE_SIZE);  
  690.         mHandler.sendMessage(msg);  
  691.     }  
  692.   
  693.     /** 
  694.      * Lock screen rotation 
  695.      */  
  696.     private void lockScreen() {  
  697.         if(mScreenOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR) {  
  698.             if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2)  
  699.                 setRequestedOrientation(14 /* SCREEN_ORIENTATION_LOCKED */);  
  700.             else  
  701.                 setRequestedOrientation(getScreenOrientation());  
  702.         }  
  703.         showInfo(R.string.locked, 1000);  
  704.         mLock.setBackgroundResource(R.drawable.ic_locked);  
  705.         mTime.setEnabled(false);  
  706.         mSeekbar.setEnabled(false);  
  707.         mLength.setEnabled(false);  
  708.         hideOverlay(true);  
  709.     }  
  710.   
  711.     /** 
  712.      * Remove screen lock 
  713.      */  
  714.     private void unlockScreen() {  
  715.         if(mScreenOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR)  
  716.             setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);  
  717.         showInfo(R.string.unlocked, 1000);  
  718.         mLock.setBackgroundResource(R.drawable.ic_lock);  
  719.         mTime.setEnabled(true);  
  720.         mSeekbar.setEnabled(true);  
  721.         mLength.setEnabled(true);  
  722.         mShowing = false;  
  723.         showOverlay();  
  724.     }  
  725.   
  726.     /** 
  727.      * Show text in the info view for "duration" milliseconds 
  728.      * @param text 
  729.      * @param duration 
  730.      */  
  731.     private void showInfo(String text, int duration) {  
  732.         mInfo.setVisibility(View.VISIBLE);  
  733.         mInfo.setText(text);  
  734.         mHandler.removeMessages(FADE_OUT_INFO);  
  735.         mHandler.sendEmptyMessageDelayed(FADE_OUT_INFO, duration);  
  736.     }  
  737.   
  738.     private void showInfo(int textid, int duration) {  
  739.         mInfo.setVisibility(View.VISIBLE);  
  740.         mInfo.setText(textid);  
  741.         mHandler.removeMessages(FADE_OUT_INFO);  
  742.         mHandler.sendEmptyMessageDelayed(FADE_OUT_INFO, duration);  
  743.     }  
  744.   
  745.     /** 
  746.      * Show text in the info view 
  747.      * @param text 
  748.      */  
  749.     private void showInfo(String text) {  
  750.         mInfo.setVisibility(View.VISIBLE);  
  751.         mInfo.setText(text);  
  752.         mHandler.removeMessages(FADE_OUT_INFO);  
  753.     }  
  754.   
  755.     /** 
  756.      * hide the info view with "delay" milliseconds delay 
  757.      * @param delay 
  758.      */  
  759.     private void hideInfo(int delay) {  
  760.         mHandler.sendEmptyMessageDelayed(FADE_OUT_INFO, delay);  
  761.     }  
  762.   
  763.     /** 
  764.      * hide the info view 
  765.      */  
  766.     private void hideInfo() {  
  767.         hideInfo(0);  
  768.     }  
  769.   
  770.     private void fadeOutInfo() {  
  771.         if (mInfo.getVisibility() == View.VISIBLE)  
  772.             mInfo.startAnimation(AnimationUtils.loadAnimation(  
  773.                     VideoPlayerActivity.this, android.R.anim.fade_out));  
  774.         mInfo.setVisibility(View.INVISIBLE);  
  775.     }  
  776.   
  777.     @TargetApi(Build.VERSION_CODES.FROYO)  
  778.     private int changeAudioFocus(boolean acquire) {  
  779.         if(!LibVlcUtil.isFroyoOrLater()) // NOP if not supported  
  780.             return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;  
  781.   
  782.         if (mAudioFocusListener == null) {  
  783.             mAudioFocusListener = new OnAudioFocusChangeListener() {  
  784.                 @Override  
  785.                 public void onAudioFocusChange(int focusChange) {  
  786.                     /* 
  787.                      * Pause playback during alerts and notifications 
  788.                      */  
  789.                     switch (focusChange)  
  790.                     {  
  791.                         case AudioManager.AUDIOFOCUS_LOSS:  
  792.                         case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:  
  793.                         case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:  
  794.                             if (mLibVLC.isPlaying())  
  795.                                 mLibVLC.pause();  
  796.                             break;  
  797.                         case AudioManager.AUDIOFOCUS_GAIN:  
  798.                         case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT:  
  799.                         case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK:  
  800.                             if (!mLibVLC.isPlaying())  
  801.                                 mLibVLC.play();  
  802.                             break;  
  803.                     }  
  804.                 }  
  805.             };  
  806.         }  
  807.   
  808.         int result;  
  809.         if(acquire) {  
  810.             result = mAudioManager.requestAudioFocus(mAudioFocusListener,  
  811.                     AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);  
  812.             mAudioManager.setParameters("bgm_state=true");  
  813.         }  
  814.         else {  
  815.             if (mAudioManager != null) {  
  816.                 result = mAudioManager.abandonAudioFocus(mAudioFocusListener);  
  817.                 mAudioManager.setParameters("bgm_state=false");  
  818.             }  
  819.             else  
  820.                 result = AudioManager.AUDIOFOCUS_REQUEST_FAILED;  
  821.         }  
  822.   
  823.         return result;  
  824.     }  
  825.   
  826.     /** 
  827.      *  Handle libvlc asynchronous events 
  828.      */  
  829.     private final Handler eventHandler = new VideoPlayerEventHandler(this);  
  830.   
  831.     private static class VideoPlayerEventHandler extends WeakHandler<VideoPlayerActivity> {  
  832.         public VideoPlayerEventHandler(VideoPlayerActivity owner) {  
  833.             super(owner);  
  834.         }  
  835.   
  836.         @Override  
  837.         public void handleMessage(Message msg) {  
  838.             VideoPlayerActivity activity = getOwner();  
  839.             if(activity == nullreturn;  
  840.             // Do not handle events if we are leaving the VideoPlayerActivity  
  841.             if (activity.mSwitchingView) return;  
  842.   
  843.             switch (msg.getData().getInt("event")) {  
  844.                 case EventHandler.MediaParsedChanged:  
  845.                     Log.i(TAG, "MediaParsedChanged");  
  846.                     activity.updateNavStatus();  
  847.                     if (!activity.mHasMenu && activity.mLibVLC.getVideoTracksCount() < 1) {  
  848.                         Log.i(TAG, "No video track, open in audio mode");  
  849.                         activity.switchToAudioMode();  
  850.                     }  
  851.                     break;  
  852.                 case EventHandler.MediaPlayerPlaying:  
  853.                     Log.i(TAG, "MediaPlayerPlaying");  
  854.                     activity.stopLoadingAnimation();  
  855.                     activity.showOverlay();  
  856.                     /** FIXME: update the track list when it changes during the 
  857.                      *  playback. (#7540) */  
  858.                     activity.setESTrackLists(true);  
  859.                     activity.setESTracks();  
  860.                     activity.changeAudioFocus(true);  
  861.                     activity.updateNavStatus();  
  862.                     break;  
  863.                 case EventHandler.MediaPlayerPaused:  
  864.                     Log.i(TAG, "MediaPlayerPaused");  
  865.                     break;  
  866.                 case EventHandler.MediaPlayerStopped:  
  867.                     Log.i(TAG, "MediaPlayerStopped");  
  868.                     activity.changeAudioFocus(false);  
  869.                     break;  
  870.                 case EventHandler.MediaPlayerEndReached:  
  871.                     Log.i(TAG, "MediaPlayerEndReached");  
  872.                     activity.changeAudioFocus(false);  
  873.                     activity.endReached();  
  874.                     break;  
  875.                 case EventHandler.MediaPlayerVout:  
  876.                     activity.updateNavStatus();  
  877.                     if (!activity.mHasMenu)  
  878.                         activity.handleVout(msg);  
  879.                     break;  
  880.                 case EventHandler.MediaPlayerPositionChanged:  
  881.                     if (!activity.mCanSeek)  
  882.                         activity.mCanSeek = true;  
  883.                     //don't spam the logs  
  884.                     break;  
  885.                 case EventHandler.MediaPlayerEncounteredError:  
  886.                     Log.i(TAG, "MediaPlayerEncounteredError");  
  887.                     activity.encounteredError();  
  888.                     break;  
  889.                 case EventHandler.HardwareAccelerationError:  
  890.                     Log.i(TAG, "HardwareAccelerationError");  
  891.                     activity.handleHardwareAccelerationError();  
  892.                     break;  
  893.                 case EventHandler.MediaPlayerTimeChanged:  
  894.                     // avoid useless error logs  
  895.                     break;  
  896.                 default:  
  897.                     Log.e(TAG, String.format("Event not handled (0x%x)", msg.getData().getInt("event")));  
  898.                     break;  
  899.             }  
  900.             activity.updateOverlayPausePlay();  
  901.         }  
  902.     };  
  903.   
  904.     /** 
  905.      * Handle resize of the surface and the overlay 
  906.      */  
  907.     private final Handler mHandler = new VideoPlayerHandler(this);  
  908.   
  909.     private static class VideoPlayerHandler extends WeakHandler<VideoPlayerActivity> {  
  910.         public VideoPlayerHandler(VideoPlayerActivity owner) {  
  911.             super(owner);  
  912.         }  
  913.   
  914.         @Override  
  915.         public void handleMessage(Message msg) {  
  916.             VideoPlayerActivity activity = getOwner();  
  917.             if(activity == null// WeakReference could be GC'ed early  
  918.                 return;  
  919.   
  920.             switch (msg.what) {  
  921.                 case FADE_OUT:  
  922.                     activity.hideOverlay(false);  
  923.                     break;  
  924.                 case SHOW_PROGRESS:  
  925.                     int pos = activity.setOverlayProgress();  
  926.                     if (activity.canShowProgress()) {  
  927.                         msg = obtainMessage(SHOW_PROGRESS);  
  928.                         sendMessageDelayed(msg, 1000 - (pos % 1000));  
  929.                     }  
  930.                     break;  
  931.                 case SURFACE_SIZE:  
  932.                     activity.changeSurfaceSize();  
  933.                     break;  
  934.                 case FADE_OUT_INFO:  
  935.                     activity.fadeOutInfo();  
  936.                     break;  
  937.                 case AUDIO_SERVICE_CONNECTION_SUCCESS:  
  938.                     activity.startPlayback();  
  939.                     break;  
  940.                 case AUDIO_SERVICE_CONNECTION_FAILED:  
  941.                     activity.finish();  
  942.                     break;  
  943.             }  
  944.         }  
  945.     };  
  946.   
  947.     private boolean canShowProgress() {  
  948.         return !mDragging && mShowing && mLibVLC.isPlaying();  
  949.     }  
  950.   
  951.     private void endReached() {  
  952.         if(mLibVLC.getMediaList().expandMedia(savedIndexPosition) == 0) {  
  953.             Log.d(TAG, "Found a video playlist, expanding it");  
  954.             eventHandler.postDelayed(new Runnable() {  
  955.                 @Override  
  956.                 public void run() {  
  957.                     loadMedia();  
  958.                 }  
  959.             }, 1000);  
  960.         } else {  
  961.             /* Exit player when reaching the end */  
  962.             mEndReached = true;  
  963.             finish();  
  964.         }  
  965.     }  
  966.   
  967.     private void encounteredError() {  
  968.         /* Encountered Error, exit player with a message */  
  969.         AlertDialog dialog = new AlertDialog.Builder(VideoPlayerActivity.this)  
  970.         .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {  
  971.             @Override  
  972.             public void onClick(DialogInterface dialog, int id) {  
  973.                 finish();  
  974.             }  
  975.         })  
  976.         .setTitle(R.string.encountered_error_title)  
  977.         .setMessage(R.string.encountered_error_message)  
  978.         .create();  
  979.         dialog.show();  
  980.     }  
  981.   
  982.     public void eventHardwareAccelerationError() {  
  983.         EventHandler em = EventHandler.getInstance();  
  984.         em.callback(EventHandler.HardwareAccelerationError, new Bundle());  
  985.     }  
  986.   
  987.     private void handleHardwareAccelerationError() {  
  988.         mLibVLC.stop();  
  989.         AlertDialog dialog = new AlertDialog.Builder(VideoPlayerActivity.this)  
  990.         .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {  
  991.             @Override  
  992.             public void onClick(DialogInterface dialog, int id) {  
  993.                 mDisabledHardwareAcceleration = true;  
  994.                 mPreviousHardwareAccelerationMode = mLibVLC.getHardwareAcceleration();  
  995.                 mLibVLC.setHardwareAcceleration(LibVLC.HW_ACCELERATION_DISABLED);  
  996.                 mSubtitlesSurface.setVisibility(View.INVISIBLE);  
  997.                 loadMedia();  
  998.             }  
  999.         })  
  1000.         .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {  
  1001.             @Override  
  1002.             public void onClick(DialogInterface dialog, int id) {  
  1003.                 finish();  
  1004.             }  
  1005.         })  
  1006.         .setTitle(R.string.hardware_acceleration_error_title)  
  1007.         .setMessage(R.string.hardware_acceleration_error_message)  
  1008.         .create();  
  1009.         if(!isFinishing())  
  1010.             dialog.show();  
  1011.     }  
  1012.   
  1013.     private void handleVout(Message msg) {  
  1014.         if (msg.getData().getInt("data") == 0 && !mEndReached) {  
  1015.             /* Video track lost, open in audio mode */  
  1016.             Log.i(TAG, "Video track lost, switching to audio");  
  1017.             mSwitchingView = true;  
  1018.             finish();  
  1019.         }  
  1020.     }  
  1021.   
  1022.     private void switchToAudioMode() {  
  1023.       
  1024.           
  1025.         mSwitchingView = true;  
  1026.         // Show the MainActivity if it is not in background.  
  1027.         if (getIntent().getAction() != null  
  1028.             && getIntent().getAction().equals(Intent.ACTION_VIEW)) {  
  1029.             Intent i = new Intent(this, MainActivity.class);  
  1030.             startActivity(i);  
  1031.         }  
  1032.         finish();  
  1033.     }  
  1034.   
  1035.     @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)  
  1036.     private void changeSurfaceSize() {  
  1037.         int sw;  
  1038.         int sh;  
  1039.   
  1040.         // get screen size  
  1041.         if (mPresentation == null) {  
  1042.             sw = getWindow().getDecorView().getWidth();  
  1043.             sh = getWindow().getDecorView().getHeight();  
  1044.         } else {  
  1045.             sw = mPresentation.getWindow().getDecorView().getWidth();  
  1046.             sh = mPresentation.getWindow().getDecorView().getHeight();  
  1047.         }  
  1048.   
  1049.         double dw = sw, dh = sh;  
  1050.         boolean isPortrait;  
  1051.   
  1052.         if (mPresentation == null) {  
  1053.             // getWindow().getDecorView() doesn't always take orientation into account, we have to correct the values  
  1054.             isPortrait = getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT;  
  1055.         } else {  
  1056.             isPortrait = false;  
  1057.         }  
  1058.   
  1059.         if (sw > sh && isPortrait || sw < sh && !isPortrait) {  
  1060.             dw = sh;  
  1061.             dh = sw;  
  1062.         }  
  1063.   
  1064.         // sanity check  
  1065.         if (dw * dh == 0 || mVideoWidth * mVideoHeight == 0) {  
  1066.             Log.e(TAG, "Invalid surface size");  
  1067.             return;  
  1068.         }  
  1069.   
  1070.         // compute the aspect ratio  
  1071.         double ar, vw;  
  1072.         if (mSarDen == mSarNum) {  
  1073.             /* No indication about the density, assuming 1:1 */  
  1074.             vw = mVideoVisibleWidth;  
  1075.             ar = (double)mVideoVisibleWidth / (double)mVideoVisibleHeight;  
  1076.         } else {  
  1077.             /* Use the specified aspect ratio */  
  1078.             vw = mVideoVisibleWidth * (double)mSarNum / mSarDen;  
  1079.             ar = vw / mVideoVisibleHeight;  
  1080.         }  
  1081.   
  1082.         // compute the display aspect ratio  
  1083.         double dar = dw / dh;  
  1084.   
  1085.         switch (mCurrentSize) {  
  1086.             case SURFACE_BEST_FIT:  
  1087.                 if (dar < ar)  
  1088.                     dh = dw / ar;  
  1089.                 else  
  1090.                     dw = dh * ar;  
  1091.                 break;  
  1092.             case SURFACE_FIT_HORIZONTAL:  
  1093.                 dh = dw / ar;  
  1094.                 break;  
  1095.             case SURFACE_FIT_VERTICAL:  
  1096.                 dw = dh * ar;  
  1097.                 break;  
  1098.             case SURFACE_FILL:  
  1099.                 break;  
  1100.             case SURFACE_16_9:  
  1101.                 ar = 16.0 / 9.0;  
  1102.                 if (dar < ar)  
  1103.                     dh = dw / ar;  
  1104.                 else  
  1105.                     dw = dh * ar;  
  1106.                 break;  
  1107.             case SURFACE_4_3:  
  1108.                 ar = 4.0 / 3.0;  
  1109.                 if (dar < ar)  
  1110.                     dh = dw / ar;  
  1111.                 else  
  1112.                     dw = dh * ar;  
  1113.                 break;  
  1114.             case SURFACE_ORIGINAL:  
  1115.                 dh = mVideoVisibleHeight;  
  1116.                 dw = vw;  
  1117.                 break;  
  1118.         }  
  1119.   
  1120.         SurfaceView surface;  
  1121.         SurfaceView subtitlesSurface;  
  1122.         SurfaceHolder surfaceHolder;  
  1123.         SurfaceHolder subtitlesSurfaceHolder;  
  1124.         FrameLayout surfaceFrame;  
  1125.   
  1126.         if (mPresentation == null) {  
  1127.             surface = mSurface;  
  1128.             subtitlesSurface = mSubtitlesSurface;  
  1129.             surfaceHolder = mSurfaceHolder;  
  1130.             subtitlesSurfaceHolder = mSubtitlesSurfaceHolder;  
  1131.             surfaceFrame = mSurfaceFrame;  
  1132.         } else {  
  1133.             surface = mPresentation.mSurface;  
  1134.             subtitlesSurface = mPresentation.mSubtitlesSurface;  
  1135.             surfaceHolder = mPresentation.mSurfaceHolder;  
  1136.             subtitlesSurfaceHolder = mPresentation.mSubtitlesSurfaceHolder;  
  1137.             surfaceFrame = mPresentation.mSurfaceFrame;  
  1138.         }  
  1139.   
  1140.         // force surface buffer size  
  1141.         surfaceHolder.setFixedSize(mVideoWidth, mVideoHeight);  
  1142.         subtitlesSurfaceHolder.setFixedSize(mVideoWidth, mVideoHeight);  
  1143.   
  1144.         // set display size  
  1145.         LayoutParams lp = surface.getLayoutParams();  
  1146.         lp.width  = (int) Math.ceil(dw * mVideoWidth / mVideoVisibleWidth);  
  1147.         lp.height = (int) Math.ceil(dh * mVideoHeight / mVideoVisibleHeight);  
  1148.         surface.setLayoutParams(lp);  
  1149.         subtitlesSurface.setLayoutParams(lp);  
  1150.   
  1151.         // set frame size (crop if necessary)  
  1152.         lp = surfaceFrame.getLayoutParams();  
  1153.         lp.width = (int) Math.floor(dw);  
  1154.         lp.height = (int) Math.floor(dh);  
  1155.         surfaceFrame.setLayoutParams(lp);  
  1156.   
  1157.         surface.invalidate();  
  1158.         subtitlesSurface.invalidate();  
  1159.     }  
  1160.   
  1161.     /** 
  1162.      * show/hide the overlay 
  1163.      */  
  1164.   
  1165.     @Override  
  1166.     public boolean onTouchEvent(MotionEvent event) {  
  1167.         if (mIsLocked) {  
  1168.             // locked, only handle show/hide & ignore all actions  
  1169.             if (event.getAction() == MotionEvent.ACTION_UP) {  
  1170.                 if (!mShowing) {  
  1171.                     showOverlay();  
  1172.                 } else {  
  1173.                     hideOverlay(true);  
  1174.                 }  
  1175.             }  
  1176.             return false;  
  1177.         }  
  1178.   
  1179.         DisplayMetrics screen = new DisplayMetrics();  
  1180.         getWindowManager().getDefaultDisplay().getMetrics(screen);  
  1181.   
  1182.         if (mSurfaceYDisplayRange == 0)  
  1183.             mSurfaceYDisplayRange = Math.min(screen.widthPixels, screen.heightPixels);  
  1184.   
  1185.         float y_changed = event.getRawY() - mTouchY;  
  1186.         float x_changed = event.getRawX() - mTouchX;  
  1187.   
  1188.         // coef is the gradient's move to determine a neutral zone  
  1189.         float coef = Math.abs (y_changed / x_changed);  
  1190.         float xgesturesize = ((x_changed / screen.xdpi) * 2.54f);  
  1191.   
  1192.         /* Offset for Mouse Events */  
  1193.         int[] offset = new int[2];  
  1194.         mSurface.getLocationOnScreen(offset);  
  1195.         int xTouch = Math.round((event.getRawX() - offset[0]) * mVideoWidth / mSurface.getWidth());  
  1196.         int yTouch = Math.round((event.getRawY() - offset[1]) * mVideoHeight / mSurface.getHeight());  
  1197.   
  1198.         switch (event.getAction()) {  
  1199.   
  1200.         case MotionEvent.ACTION_DOWN:  
  1201.             // Audio  
  1202.             mTouchY = event.getRawY();  
  1203.             mVol = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);  
  1204.             mTouchAction = TOUCH_NONE;  
  1205.             // Seek  
  1206.             mTouchX = event.getRawX();  
  1207.             // Mouse events for the core  
  1208.             LibVLC.sendMouseEvent(MotionEvent.ACTION_DOWN, 0, xTouch, yTouch);  
  1209.             break;  
  1210.   
  1211.         case MotionEvent.ACTION_MOVE:  
  1212.             // Mouse events for the core  
  1213.             LibVLC.sendMouseEvent(MotionEvent.ACTION_MOVE, 0, xTouch, yTouch);  
  1214.   
  1215.             // No volume/brightness action if coef < 2 or a secondary display is connected  
  1216.             //TODO : Volume action when a secondary display is connected  
  1217.             if (coef > 2 && mPresentation == null) {  
  1218.                 // Volume (Up or Down - Right side)  
  1219.                 if (!mEnableBrightnessGesture || (int)mTouchX > (screen.widthPixels / 2)){  
  1220.                     doVolumeTouch(y_changed);  
  1221.                 }  
  1222.                 // Brightness (Up or Down - Left side)  
  1223.                 if (mEnableBrightnessGesture && (int)mTouchX < (screen.widthPixels / 2)){  
  1224.                     doBrightnessTouch(y_changed);  
  1225.                 }  
  1226.                 // Extend the overlay for a little while, so that it doesn't  
  1227.                 // disappear on the user if more adjustment is needed. This  
  1228.                 // is because on devices with soft navigation (e.g. Galaxy  
  1229.                 // Nexus), gestures can't be made without activating the UI.  
  1230.                 if(AndroidDevices.hasNavBar())  
  1231.                     showOverlay();  
  1232.             }  
  1233.             // Seek (Right or Left move)  
  1234.             doSeekTouch(coef, xgesturesize, false);  
  1235.             break;  
  1236.   
  1237.         case MotionEvent.ACTION_UP:  
  1238.             // Mouse events for the core  
  1239.             LibVLC.sendMouseEvent(MotionEvent.ACTION_UP, 0, xTouch, yTouch);  
  1240.   
  1241.             // Audio or Brightness  
  1242.             if ( mTouchAction == TOUCH_NONE) {  
  1243.                 if (!mShowing) {  
  1244.                     showOverlay();  
  1245.                 } else {  
  1246.                     hideOverlay(true);  
  1247.                 }  
  1248.             }  
  1249.             // Seek  
  1250.             doSeekTouch(coef, xgesturesize, true);  
  1251.             break;  
  1252.         }  
  1253.         return mTouchAction != TOUCH_NONE;  
  1254.     }  
  1255.   
  1256.     private void doSeekTouch(float coef, float gesturesize, boolean seek) {  
  1257.         // No seek action if coef > 0.5 and gesturesize < 1cm  
  1258.         if (coef > 0.5 || Math.abs(gesturesize) < 1 || !mCanSeek)  
  1259.             return;  
  1260.   
  1261.         if (mTouchAction != TOUCH_NONE && mTouchAction != TOUCH_SEEK)  
  1262.             return;  
  1263.         mTouchAction = TOUCH_SEEK;  
  1264.   
  1265.         // Always show seekbar when searching  
  1266.         if (!mShowing) showOverlay();  
  1267.   
  1268.         long length = mLibVLC.getLength();  
  1269.         long time = mLibVLC.getTime();  
  1270.   
  1271.         // Size of the jump, 10 minutes max (600000), with a bi-cubic progression, for a 8cm gesture  
  1272.         int jump = (int) (Math.signum(gesturesize) * ((600000 * Math.pow((gesturesize / 8), 4)) + 3000));  
  1273.   
  1274.         // Adjust the jump  
  1275.         if ((jump > 0) && ((time + jump) > length))  
  1276.             jump = (int) (length - time);  
  1277.         if ((jump < 0) && ((time + jump) < 0))  
  1278.             jump = (int) -time;  
  1279.   
  1280.         //Jump !  
  1281.         if (seek && length > 0)  
  1282.             mLibVLC.setTime(time + jump);  
  1283.   
  1284.         if (length > 0)  
  1285.             //Show the jump's size  
  1286.             showInfo(String.format("%s%s (%s)",  
  1287.                     jump >= 0 ? "+" : "",  
  1288.                     Strings.millisToString(jump),  
  1289.                     Strings.millisToString(time + jump)), 1000);  
  1290.         else  
  1291.             showInfo(R.string.unseekable_stream, 1000);  
  1292.     }  
  1293.   
  1294.     private void doVolumeTouch(float y_changed) {  
  1295.         if (mTouchAction != TOUCH_NONE && mTouchAction != TOUCH_VOLUME)  
  1296.             return;  
  1297.         int delta = -(int) ((y_changed / mSurfaceYDisplayRange) * mAudioMax);  
  1298.         int vol = (int) Math.min(Math.max(mVol + delta, 0), mAudioMax);  
  1299.         if (delta != 0) {  
  1300.             mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, vol, 0);  
  1301.             mTouchAction = TOUCH_VOLUME;  
  1302.             showInfo(getString(R.string.volume) + '\u00A0' + Integer.toString(vol),1000);  
  1303.         }  
  1304.     }  
  1305.   
  1306.     private void initBrightnessTouch() {  
  1307.         float brightnesstemp = 0.01f;  
  1308.         // Initialize the layoutParams screen brightness  
  1309.         try {  
  1310.             brightnesstemp = android.provider.Settings.System.getInt(getContentResolver(),  
  1311.                     android.provider.Settings.System.SCREEN_BRIGHTNESS) / 255.0f;  
  1312.         } catch (SettingNotFoundException e) {  
  1313.             // TODO Auto-generated catch block  
  1314.             e.printStackTrace();  
  1315.         }  
  1316.         WindowManager.LayoutParams lp = getWindow().getAttributes();  
  1317.         lp.screenBrightness = brightnesstemp;  
  1318.         getWindow().setAttributes(lp);  
  1319.         mIsFirstBrightnessGesture = false;  
  1320.     }  
  1321.   
  1322.     private void doBrightnessTouch(float y_changed) {  
  1323.         if (mTouchAction != TOUCH_NONE && mTouchAction != TOUCH_BRIGHTNESS)  
  1324.             return;  
  1325.         if (mIsFirstBrightnessGesture) initBrightnessTouch();  
  1326.         mTouchAction = TOUCH_BRIGHTNESS;  
  1327.   
  1328.         // Set delta : 0.07f is arbitrary for now, it possibly will change in the future  
  1329.         float delta = - y_changed / mSurfaceYDisplayRange * 0.07f;  
  1330.   
  1331.         // Estimate and adjust Brightness  
  1332.         WindowManager.LayoutParams lp = getWindow().getAttributes();  
  1333.         lp.screenBrightness =  Math.min(Math.max(lp.screenBrightness + delta, 0.01f), 1);  
  1334.   
  1335.         // Set Brightness  
  1336.         getWindow().setAttributes(lp);  
  1337.         showInfo(getString(R.string.brightness) + '\u00A0' + Math.round(lp.screenBrightness*15),1000);  
  1338.     }  
  1339.   
  1340.     /** 
  1341.      * handle changes of the seekbar (slicer) 
  1342.      */  
  1343.     private final OnSeekBarChangeListener mSeekListener = new OnSeekBarChangeListener() {  
  1344.   
  1345.         @Override  
  1346.         public void onStartTrackingTouch(SeekBar seekBar) {  
  1347.             mDragging = true;  
  1348.             showOverlay(OVERLAY_INFINITE);  
  1349.         }  
  1350.   
  1351.         @Override  
  1352.         public void onStopTrackingTouch(SeekBar seekBar) {  
  1353.             mDragging = false;  
  1354.             showOverlay();  
  1355.             hideInfo();  
  1356.         }  
  1357.   
  1358.         @Override  
  1359.         public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {  
  1360.             if (fromUser && mCanSeek) {  
  1361.                 mLibVLC.setTime(progress);  
  1362.                 setOverlayProgress();  
  1363.                 mTime.setText(Strings.millisToString(progress));  
  1364.                 showInfo(Strings.millisToString(progress));  
  1365.             }  
  1366.   
  1367.         }  
  1368.     };  
  1369.   
  1370.     /** 
  1371.     * 
  1372.     */  
  1373.     private final OnClickListener mAudioTrackListener = new OnClickListener() {  
  1374.         @Override  
  1375.         public void onClick(View v) {  
  1376.             final String[] arrList = new String[mAudioTracksList.size()];  
  1377.             int i = 0;  
  1378.             int listPosition = 0;  
  1379.             for(Map.Entry<Integer,String> entry : mAudioTracksList.entrySet()) {  
  1380.                 arrList[i] = entry.getValue();  
  1381.                 // map the track position to the list position  
  1382.                 if(entry.getKey() == mLibVLC.getAudioTrack())  
  1383.                     listPosition = i;  
  1384.                 i++;  
  1385.             }  
  1386.             AlertDialog dialog = new AlertDialog.Builder(VideoPlayerActivity.this)  
  1387.             .setTitle(R.string.track_audio)  
  1388.             .setSingleChoiceItems(arrList, listPosition, new DialogInterface.OnClickListener() {  
  1389.                 @Override  
  1390.                 public void onClick(DialogInterface dialog, int listPosition) {  
  1391.                     int trackID = -1;  
  1392.                     // Reverse map search...  
  1393.                     for(Map.Entry<Integer, String> entry : mAudioTracksList.entrySet()) {  
  1394.                         if(arrList[listPosition].equals(entry.getValue())) {  
  1395.                             trackID = entry.getKey();  
  1396.                             break;  
  1397.                         }  
  1398.                     }  
  1399.                     if(trackID < 0return;  
  1400.   
  1401.                     MediaDatabase.getInstance().updateMedia(  
  1402.                             mLocation,  
  1403.                             MediaDatabase.mediaColumn.MEDIA_AUDIOTRACK,  
  1404.                             trackID);  
  1405.                     mLibVLC.setAudioTrack(trackID);  
  1406.                     dialog.dismiss();  
  1407.                 }  
  1408.             })  
  1409.             .create();  
  1410.             dialog.setCanceledOnTouchOutside(true);  
  1411.             dialog.setOwnerActivity(VideoPlayerActivity.this);  
  1412.             dialog.show();  
  1413.         }  
  1414.     };  
  1415.   
  1416.     /** 
  1417.     * 
  1418.     */  
  1419.     private final OnClickListener mSubtitlesListener = new OnClickListener() {  
  1420.         @Override  
  1421.         public void onClick(View v) {  
  1422.             final String[] arrList = new String[mSubtitleTracksList.size()];  
  1423.             int i = 0;  
  1424.             int listPosition = 0;  
  1425.             for(Map.Entry<Integer,String> entry : mSubtitleTracksList.entrySet()) {  
  1426.                 arrList[i] = entry.getValue();  
  1427.                 // map the track position to the list position  
  1428.                 if(entry.getKey() == mLibVLC.getSpuTrack())  
  1429.                     listPosition = i;  
  1430.                 i++;  
  1431.             }  
  1432.   
  1433.             AlertDialog dialog = new AlertDialog.Builder(VideoPlayerActivity.this)  
  1434.             .setTitle(R.string.track_text)  
  1435.             .setSingleChoiceItems(arrList, listPosition, new DialogInterface.OnClickListener() {  
  1436.                 @Override  
  1437.                 public void onClick(DialogInterface dialog, int listPosition) {  
  1438.                     int trackID = -2;  
  1439.                     // Reverse map search...  
  1440.                     for(Map.Entry<Integer, String> entry : mSubtitleTracksList.entrySet()) {  
  1441.                         if(arrList[listPosition].equals(entry.getValue())) {  
  1442.                             trackID = entry.getKey();  
  1443.                             break;  
  1444.                         }  
  1445.                     }  
  1446.                     if(trackID < -1return;  
  1447.   
  1448.                     MediaDatabase.getInstance().updateMedia(  
  1449.                             mLocation,  
  1450.                             MediaDatabase.mediaColumn.MEDIA_SPUTRACK,  
  1451.                             trackID);  
  1452.                     mLibVLC.setSpuTrack(trackID);  
  1453.                     dialog.dismiss();  
  1454.                 }  
  1455.             })  
  1456.             .create();  
  1457.             dialog.setCanceledOnTouchOutside(true);  
  1458.             dialog.setOwnerActivity(VideoPlayerActivity.this);  
  1459.             dialog.show();  
  1460.         }  
  1461.     };  
  1462.   
  1463.     private final OnClickListener mNavMenuListener = new OnClickListener() {  
  1464.         @Override  
  1465.         public void onClick(View v) {  
  1466.             /* Try to return to the menu. */  
  1467.             /* FIXME: not working correctly in all cases */  
  1468.             mLibVLC.setTitle(0);  
  1469.         }  
  1470.     };  
  1471.   
  1472.     /** 
  1473.     * 
  1474.     */  
  1475.     private final OnClickListener mPlayPauseListener = new OnClickListener() {  
  1476.         @Override  
  1477.         public void onClick(View v) {  
  1478.             if (mLibVLC.isPlaying())  
  1479.                 pause();  
  1480.             else  
  1481.                 play();  
  1482.             showOverlay();  
  1483.         }  
  1484.     };  
  1485.   
  1486.     /** 
  1487.     * 
  1488.     */  
  1489.     private final OnClickListener mBackwardListener = new OnClickListener() {  
  1490.         @Override  
  1491.         public void onClick(View v) {  
  1492.             seek(-10000);  
  1493.         }  
  1494.     };  
  1495.   
  1496.     /** 
  1497.     * 
  1498.     */  
  1499.     private final OnClickListener mForwardListener = new OnClickListener() {  
  1500.         @Override  
  1501.         public void onClick(View v) {  
  1502.             seek(10000);  
  1503.         }  
  1504.     };  
  1505.   
  1506.     public void seek(int delta) {  
  1507.         // unseekable stream  
  1508.         if(mLibVLC.getLength() <= 0 || !mCanSeek) return;  
  1509.   
  1510.         long position = mLibVLC.getTime() + delta;  
  1511.         if (position < 0) position = 0;  
  1512.         mLibVLC.setTime(position);  
  1513.         showOverlay();  
  1514.     }  
  1515.   
  1516.     /** 
  1517.      * 
  1518.      */  
  1519.     private final OnClickListener mLockListener = new OnClickListener() {  
  1520.   
  1521.         @Override  
  1522.         public void onClick(View v) {  
  1523.             if (mIsLocked) {  
  1524.                 mIsLocked = false;  
  1525.                 unlockScreen();  
  1526.             } else {  
  1527.                 mIsLocked = true;  
  1528.                 lockScreen();  
  1529.             }  
  1530.         }  
  1531.     };  
  1532.   
  1533.     /** 
  1534.      * 
  1535.      */  
  1536.     private final OnClickListener mSizeListener = new OnClickListener() {  
  1537.   
  1538.         @Override  
  1539.         public void onClick(View v) {  
  1540.   
  1541.             if (mCurrentSize < SURFACE_ORIGINAL) {  
  1542.                 mCurrentSize++;  
  1543.             } else {  
  1544.                 mCurrentSize = 0;  
  1545.             }  
  1546.             changeSurfaceSize();  
  1547.             switch (mCurrentSize) {  
  1548.                 case SURFACE_BEST_FIT:  
  1549.                     showInfo(R.string.surface_best_fit, 1000);  
  1550.                     break;  
  1551.                 case SURFACE_FIT_HORIZONTAL:  
  1552.                     showInfo(R.string.surface_fit_horizontal, 1000);  
  1553.                     break;  
  1554.                 case SURFACE_FIT_VERTICAL:  
  1555.                     showInfo(R.string.surface_fit_vertical, 1000);  
  1556.                     break;  
  1557.                 case SURFACE_FILL:  
  1558.                     showInfo(R.string.surface_fill, 1000);  
  1559.                     break;  
  1560.                 case SURFACE_16_9:  
  1561.                     showInfo("16:9"1000);  
  1562.                     break;  
  1563.                 case SURFACE_4_3:  
  1564.                     showInfo("4:3"1000);  
  1565.                     break;  
  1566.                 case SURFACE_ORIGINAL:  
  1567.                     showInfo(R.string.surface_original, 1000);  
  1568.                     break;  
  1569.             }  
  1570.             showOverlay();  
  1571.         }  
  1572.     };  
  1573.   
  1574.     private final OnClickListener mRemainingTimeListener = new OnClickListener() {  
  1575.         @Override  
  1576.         public void onClick(View v) {  
  1577.             mDisplayRemainingTime = !mDisplayRemainingTime;  
  1578.             showOverlay();  
  1579.         }  
  1580.     };  
  1581.   
  1582.     /** 
  1583.      * attach and disattach surface to the lib 
  1584.      */  
  1585.     private final SurfaceHolder.Callback mSurfaceCallback = new Callback() {  
  1586.         @Override  
  1587.         public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {  
  1588.             if(format == PixelFormat.RGBX_8888)  
  1589.                 Log.d(TAG, "Pixel format is RGBX_8888");  
  1590.             else if(format == PixelFormat.RGB_565)  
  1591.                 Log.d(TAG, "Pixel format is RGB_565");  
  1592.             else if(format == ImageFormat.YV12)  
  1593.                 Log.d(TAG, "Pixel format is YV12");  
  1594.             else  
  1595.                 Log.d(TAG, "Pixel format is other/unknown");  
  1596.             if(mLibVLC != null)  
  1597.                 mLibVLC.attachSurface(holder.getSurface(), VideoPlayerActivity.this);  
  1598.         }  
  1599.   
  1600.         @Override  
  1601.         public void surfaceCreated(SurfaceHolder holder) {  
  1602.         }  
  1603.   
  1604.         @Override  
  1605.         public void surfaceDestroyed(SurfaceHolder holder) {  
  1606.             if(mLibVLC != null)  
  1607.                 mLibVLC.detachSurface();  
  1608.         }  
  1609.     };  

你可能感兴趣的:(第三篇、vlc-android之UDP组播使用)