HTML5VideoView.java revision e4c89e321c7a33a7baf1a90237d21220d637e966
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    // Normally called immediately after setVideoURI. But for full screen,
198    // this should be after surface holder created
199    public void prepareDataAndDisplayMode(HTML5VideoViewProxy proxy) {
200        // SurfaceTexture will be created lazily here for inline mode
201        decideDisplayMode();
202
203        setOnCompletionListener(proxy);
204        setOnPreparedListener(proxy);
205        setOnErrorListener(proxy);
206        setOnInfoListener(proxy);
207        // When there is exception, we could just bail out silently.
208        // No Video will be played though. Write the stack for debug
209        try {
210            mPlayer.setDataSource(mProxy.getContext(), mUri, mHeaders);
211            mPlayer.prepareAsync();
212        } catch (IllegalArgumentException e) {
213            e.printStackTrace();
214        } catch (IllegalStateException e) {
215            e.printStackTrace();
216        } catch (IOException e) {
217            e.printStackTrace();
218        }
219        mCurrentState = STATE_NOTPREPARED;
220    }
221
222
223    // Common code
224    public int getVideoLayerId() {
225        return mVideoLayerId;
226    }
227
228
229    public int getCurrentState() {
230        if (isPlaying()) {
231            return STATE_PLAYING;
232        } else {
233            return mCurrentState;
234        }
235    }
236
237    private static final class TimeupdateTask extends TimerTask {
238        private HTML5VideoViewProxy mProxy;
239
240        public TimeupdateTask(HTML5VideoViewProxy proxy) {
241            mProxy = proxy;
242        }
243
244        @Override
245        public void run() {
246            mProxy.onTimeupdate();
247        }
248    }
249
250    @Override
251    public void onPrepared(MediaPlayer mp) {
252        mCurrentState = STATE_PREPARED;
253        seekTo(mSaveSeekTime);
254        if (mProxy != null) {
255            mProxy.onPrepared(mp);
256        }
257        if (mPauseDuringPreparing) {
258            pauseAndDispatch(mProxy);
259            mPauseDuringPreparing = false;
260        }
261    }
262
263    // Pause the play and update the play/pause button
264    public void pauseAndDispatch(HTML5VideoViewProxy proxy) {
265        pause();
266        if (proxy != null) {
267            proxy.dispatchOnPaused();
268        }
269    }
270
271    // Below are functions that are different implementation on inline and full-
272    // screen mode. Some are specific to one type, but currently are called
273    // directly from the proxy.
274    public void enterFullScreenVideoState(int layerId,
275            HTML5VideoViewProxy proxy, WebView webView) {
276    }
277
278    public boolean isFullScreenMode() {
279        return false;
280    }
281
282    public void decideDisplayMode() {
283    }
284
285    public boolean getReadyToUseSurfTex() {
286        return false;
287    }
288
289    public SurfaceTexture getSurfaceTexture(int videoLayerId) {
290        return null;
291    }
292
293    public void deleteSurfaceTexture() {
294    }
295
296    public int getTextureName() {
297        return 0;
298    }
299
300    // This is true only when the player is buffering and paused
301    public boolean mPlayerBuffering = false;
302
303    public boolean getPlayerBuffering() {
304        return mPlayerBuffering;
305    }
306
307    public void setPlayerBuffering(boolean playerBuffering) {
308        mPlayerBuffering = playerBuffering;
309        switchProgressView(playerBuffering);
310    }
311
312
313    protected void switchProgressView(boolean playerBuffering) {
314        // Only used in HTML5VideoFullScreen
315    }
316
317    public boolean surfaceTextureDeleted() {
318        // Only meaningful for HTML5VideoInline
319        return false;
320    }
321
322    public boolean fullScreenExited() {
323        // Only meaningful for HTML5VideoFullScreen
324        return false;
325    }
326
327}
328