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