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