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