Home | History | Annotate | Download | only in webkit
      1 
      2 package android.webkit;
      3 
      4 import android.graphics.SurfaceTexture;
      5 import android.media.MediaPlayer;
      6 import android.net.Uri;
      7 import android.util.Log;
      8 import android.view.SurfaceView;
      9 import android.webkit.HTML5VideoViewProxy;
     10 import java.io.IOException;
     11 import java.util.HashMap;
     12 import java.util.Map;
     13 import java.util.Timer;
     14 import java.util.TimerTask;
     15 
     16 /**
     17  * @hide This is only used by the browser
     18  */
     19 public class HTML5VideoView implements MediaPlayer.OnPreparedListener {
     20 
     21     protected static final String LOGTAG = "HTML5VideoView";
     22 
     23     protected static final String COOKIE = "Cookie";
     24     protected static final String HIDE_URL_LOGS = "x-hide-urls-from-log";
     25 
     26     // For handling the seekTo before prepared, we need to know whether or not
     27     // the video is prepared. Therefore, we differentiate the state between
     28     // prepared and not prepared.
     29     // When the video is not prepared, we will have to save the seekTo time,
     30     // and use it when prepared to play.
     31     // NOTE: these values are in sync with VideoLayerAndroid.h in webkit side.
     32     // Please keep them in sync when changed.
     33     static final int STATE_INITIALIZED        = 0;
     34     static final int STATE_NOTPREPARED        = 1;
     35     static final int STATE_PREPARED           = 2;
     36     static final int STATE_PLAYING            = 3;
     37     static final int STATE_RELEASED           = 4;
     38     protected int mCurrentState;
     39 
     40     protected HTML5VideoViewProxy mProxy;
     41 
     42     // Save the seek time when not prepared. This can happen when switching
     43     // video besides initial load.
     44     protected int mSaveSeekTime;
     45 
     46     // This is used to find the VideoLayer on the native side.
     47     protected int mVideoLayerId;
     48 
     49     // Every video will have one MediaPlayer. Given the fact we only have one
     50     // SurfaceTexture, there is only one MediaPlayer in action. Every time we
     51     // switch videos, a new instance of MediaPlayer will be created in reset().
     52     // Switching between inline and full screen will also create a new instance.
     53     protected MediaPlayer mPlayer;
     54 
     55     // This will be set up every time we create the Video View object.
     56     // Set to true only when switching into full screen while playing
     57     protected boolean mAutostart;
     58 
     59     // We need to save such info.
     60     protected Uri mUri;
     61     protected Map<String, String> mHeaders;
     62 
     63     // The timer for timeupate events.
     64     // See http://www.whatwg.org/specs/web-apps/current-work/#event-media-timeupdate
     65     protected static Timer mTimer;
     66 
     67     // The spec says the timer should fire every 250 ms or less.
     68     private static final int TIMEUPDATE_PERIOD = 250;  // ms
     69 
     70     protected boolean mPauseDuringPreparing;
     71     // common Video control FUNCTIONS:
     72     public void start() {
     73         if (mCurrentState == STATE_PREPARED) {
     74             // When replaying the same video, there is no onPrepared call.
     75             // Therefore, the timer should be set up here.
     76             if (mTimer == null)
     77             {
     78                 mTimer = new Timer();
     79                 mTimer.schedule(new TimeupdateTask(mProxy), TIMEUPDATE_PERIOD,
     80                         TIMEUPDATE_PERIOD);
     81             }
     82             mPlayer.start();
     83             setPlayerBuffering(false);
     84         }
     85     }
     86 
     87     public void pause() {
     88         if (isPlaying()) {
     89             mPlayer.pause();
     90         } else if (mCurrentState == STATE_NOTPREPARED) {
     91             mPauseDuringPreparing = true;
     92         }
     93         // Delete the Timer to stop it since there is no stop call.
     94         if (mTimer != null) {
     95             mTimer.purge();
     96             mTimer.cancel();
     97             mTimer = null;
     98         }
     99     }
    100 
    101     public int getDuration() {
    102         if (mCurrentState == STATE_PREPARED) {
    103             return mPlayer.getDuration();
    104         } else {
    105             return -1;
    106         }
    107     }
    108 
    109     public int getCurrentPosition() {
    110         if (mCurrentState == STATE_PREPARED) {
    111             return mPlayer.getCurrentPosition();
    112         }
    113         return 0;
    114     }
    115 
    116     public void seekTo(int pos) {
    117         if (mCurrentState == STATE_PREPARED)
    118             mPlayer.seekTo(pos);
    119         else
    120             mSaveSeekTime = pos;
    121     }
    122 
    123     public boolean isPlaying() {
    124         if (mCurrentState == STATE_PREPARED) {
    125             return mPlayer.isPlaying();
    126         } else {
    127             return false;
    128         }
    129     }
    130 
    131     public void release() {
    132         if (mCurrentState != STATE_RELEASED) {
    133             mPlayer.release();
    134         }
    135         mCurrentState = STATE_RELEASED;
    136     }
    137 
    138     public void stopPlayback() {
    139         if (mCurrentState == STATE_PREPARED) {
    140             mPlayer.stop();
    141         }
    142     }
    143 
    144     public boolean getAutostart() {
    145         return mAutostart;
    146     }
    147 
    148     public boolean getPauseDuringPreparing() {
    149         return mPauseDuringPreparing;
    150     }
    151 
    152     // Every time we start a new Video, we create a VideoView and a MediaPlayer
    153     public void init(int videoLayerId, int position, boolean autoStart) {
    154         mPlayer = new MediaPlayer();
    155         mCurrentState = STATE_INITIALIZED;
    156         mProxy = null;
    157         mVideoLayerId = videoLayerId;
    158         mSaveSeekTime = position;
    159         mAutostart = autoStart;
    160         mTimer = null;
    161         mPauseDuringPreparing = false;
    162     }
    163 
    164     protected HTML5VideoView() {
    165     }
    166 
    167     protected static Map<String, String> generateHeaders(String url,
    168             HTML5VideoViewProxy proxy) {
    169         boolean isPrivate = proxy.getWebView().isPrivateBrowsingEnabled();
    170         String cookieValue = CookieManager.getInstance().getCookie(url, isPrivate);
    171         Map<String, String> headers = new HashMap<String, String>();
    172         if (cookieValue != null) {
    173             headers.put(COOKIE, cookieValue);
    174         }
    175         if (isPrivate) {
    176             headers.put(HIDE_URL_LOGS, "true");
    177         }
    178 
    179         return headers;
    180     }
    181 
    182     public void setVideoURI(String uri, HTML5VideoViewProxy proxy) {
    183         // When switching players, surface texture will be reused.
    184         mUri = Uri.parse(uri);
    185         mHeaders = generateHeaders(uri, proxy);
    186     }
    187 
    188     // Listeners setup FUNCTIONS:
    189     public void setOnCompletionListener(HTML5VideoViewProxy proxy) {
    190         mPlayer.setOnCompletionListener(proxy);
    191     }
    192 
    193     public void setOnErrorListener(HTML5VideoViewProxy proxy) {
    194         mPlayer.setOnErrorListener(proxy);
    195     }
    196 
    197     public void setOnPreparedListener(HTML5VideoViewProxy proxy) {
    198         mProxy = proxy;
    199         mPlayer.setOnPreparedListener(this);
    200     }
    201 
    202     public void setOnInfoListener(HTML5VideoViewProxy proxy) {
    203         mPlayer.setOnInfoListener(proxy);
    204     }
    205 
    206     // Normally called immediately after setVideoURI. But for full screen,
    207     // this should be after surface holder created
    208     public void prepareDataAndDisplayMode(HTML5VideoViewProxy proxy) {
    209         // SurfaceTexture will be created lazily here for inline mode
    210         decideDisplayMode();
    211 
    212         setOnCompletionListener(proxy);
    213         setOnPreparedListener(proxy);
    214         setOnErrorListener(proxy);
    215         setOnInfoListener(proxy);
    216         // When there is exception, we could just bail out silently.
    217         // No Video will be played though. Write the stack for debug
    218         try {
    219             mPlayer.setDataSource(mProxy.getContext(), mUri, mHeaders);
    220             mPlayer.prepareAsync();
    221         } catch (IllegalArgumentException e) {
    222             e.printStackTrace();
    223         } catch (IllegalStateException e) {
    224             e.printStackTrace();
    225         } catch (IOException e) {
    226             e.printStackTrace();
    227         }
    228         mCurrentState = STATE_NOTPREPARED;
    229     }
    230 
    231 
    232     // Common code
    233     public int getVideoLayerId() {
    234         return mVideoLayerId;
    235     }
    236 
    237 
    238     public int getCurrentState() {
    239         if (isPlaying()) {
    240             return STATE_PLAYING;
    241         } else {
    242             return mCurrentState;
    243         }
    244     }
    245 
    246     private static final class TimeupdateTask extends TimerTask {
    247         private HTML5VideoViewProxy mProxy;
    248 
    249         public TimeupdateTask(HTML5VideoViewProxy proxy) {
    250             mProxy = proxy;
    251         }
    252 
    253         @Override
    254         public void run() {
    255             mProxy.onTimeupdate();
    256         }
    257     }
    258 
    259     @Override
    260     public void onPrepared(MediaPlayer mp) {
    261         mCurrentState = STATE_PREPARED;
    262         seekTo(mSaveSeekTime);
    263         if (mProxy != null) {
    264             mProxy.onPrepared(mp);
    265         }
    266         if (mPauseDuringPreparing) {
    267             pauseAndDispatch(mProxy);
    268             mPauseDuringPreparing = false;
    269         }
    270     }
    271 
    272     // Pause the play and update the play/pause button
    273     public void pauseAndDispatch(HTML5VideoViewProxy proxy) {
    274         pause();
    275         if (proxy != null) {
    276             proxy.dispatchOnPaused();
    277         }
    278     }
    279 
    280     // Below are functions that are different implementation on inline and full-
    281     // screen mode. Some are specific to one type, but currently are called
    282     // directly from the proxy.
    283     public void enterFullScreenVideoState(int layerId,
    284             HTML5VideoViewProxy proxy, WebView webView) {
    285     }
    286 
    287     public boolean isFullScreenMode() {
    288         return false;
    289     }
    290 
    291     public void decideDisplayMode() {
    292     }
    293 
    294     public boolean getReadyToUseSurfTex() {
    295         return false;
    296     }
    297 
    298     public SurfaceTexture getSurfaceTexture(int videoLayerId) {
    299         return null;
    300     }
    301 
    302     public void deleteSurfaceTexture() {
    303     }
    304 
    305     public int getTextureName() {
    306         return 0;
    307     }
    308 
    309     // This is true only when the player is buffering and paused
    310     public boolean mPlayerBuffering = false;
    311 
    312     public boolean getPlayerBuffering() {
    313         return mPlayerBuffering;
    314     }
    315 
    316     public void setPlayerBuffering(boolean playerBuffering) {
    317         mPlayerBuffering = playerBuffering;
    318         switchProgressView(playerBuffering);
    319     }
    320 
    321 
    322     protected void switchProgressView(boolean playerBuffering) {
    323         // Only used in HTML5VideoFullScreen
    324     }
    325 
    326     public boolean surfaceTextureDeleted() {
    327         // Only meaningful for HTML5VideoInline
    328         return false;
    329     }
    330 
    331     public boolean fullScreenExited() {
    332         // Only meaningful for HTML5VideoFullScreen
    333         return false;
    334     }
    335 
    336 }
    337