HTML5VideoView.java revision b1c2c1e3c7bde3c40d7510fbda4ef8112396dd22
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    // 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