HTML5VideoView.java revision 2fcf82aee30da977849adaaadf89d81c17afbac2
1
2package android.webkit;
3
4import android.graphics.SurfaceTexture;
5import android.media.MediaPlayer;
6import android.util.Log;
7import android.view.SurfaceView;
8import android.webkit.HTML5VideoViewProxy;
9import java.io.IOException;
10import java.util.HashMap;
11import java.util.Map;
12import java.util.Timer;
13import java.util.TimerTask;
14
15/**
16 * @hide This is only used by the browser
17 */
18public class HTML5VideoView implements MediaPlayer.OnPreparedListener {
19
20    protected static final String LOGTAG = "HTML5VideoView";
21
22    protected static final String COOKIE = "Cookie";
23    protected static final String HIDE_URL_LOGS = "x-hide-urls-from-log";
24
25    // For handling the seekTo before prepared, we need to know whether or not
26    // the video is prepared. Therefore, we differentiate the state between
27    // prepared and not prepared.
28    // When the video is not prepared, we will have to save the seekTo time,
29    // and use it when prepared to play.
30    // NOTE: these values are in sync with VideoLayerAndroid.h in webkit side.
31    // Please keep them in sync when changed.
32    static final int STATE_INITIALIZED        = 0;
33    static final int STATE_NOTPREPARED        = 1;
34    static final int STATE_PREPARED           = 2;
35    static final int STATE_PLAYING            = 3;
36    protected int mCurrentState;
37
38    protected HTML5VideoViewProxy mProxy;
39
40    // Save the seek time when not prepared. This can happen when switching
41    // video besides initial load.
42    protected int mSaveSeekTime;
43
44    // This is used to find the VideoLayer on the native side.
45    protected int mVideoLayerId;
46
47    // Every video will have one MediaPlayer. Given the fact we only have one
48    // SurfaceTexture, there is only one MediaPlayer in action. Every time we
49    // switch videos, a new instance of MediaPlayer will be created in reset().
50    // Switching between inline and full screen will also create a new instance.
51    protected MediaPlayer mPlayer;
52
53    // This will be set up every time we create the Video View object.
54    // Set to true only when switching into full screen while playing
55    protected boolean mAutostart;
56
57    // We need to save such info.
58    protected String mUri;
59    protected Map<String, String> mHeaders;
60
61    // The timer for timeupate events.
62    // See http://www.whatwg.org/specs/web-apps/current-work/#event-media-timeupdate
63    protected static Timer mTimer;
64
65    // The spec says the timer should fire every 250 ms or less.
66    private static final int TIMEUPDATE_PERIOD = 250;  // ms
67
68    protected boolean mPauseDuringPreparing;
69    // common Video control FUNCTIONS:
70    public void start() {
71        if (mCurrentState == STATE_PREPARED) {
72            // When replaying the same video, there is no onPrepared call.
73            // Therefore, the timer should be set up here.
74            if (mTimer == null)
75            {
76                mTimer = new Timer();
77                mTimer.schedule(new TimeupdateTask(mProxy), TIMEUPDATE_PERIOD,
78                        TIMEUPDATE_PERIOD);
79            }
80            mPlayer.start();
81            setPlayerBuffering(false);
82        }
83    }
84
85    public void pause() {
86        if (mCurrentState == STATE_PREPARED && mPlayer.isPlaying()) {
87            mPlayer.pause();
88        } else if (mCurrentState == STATE_NOTPREPARED) {
89            mPauseDuringPreparing = true;
90        }
91        // Delete the Timer to stop it since there is no stop call.
92        if (mTimer != null) {
93            mTimer.purge();
94            mTimer.cancel();
95            mTimer = null;
96        }
97    }
98
99    public int getDuration() {
100        if (mCurrentState == STATE_PREPARED) {
101            return mPlayer.getDuration();
102        } else {
103            return -1;
104        }
105    }
106
107    public int getCurrentPosition() {
108        if (mCurrentState == STATE_PREPARED) {
109            return mPlayer.getCurrentPosition();
110        }
111        return 0;
112    }
113
114    public void seekTo(int pos) {
115        if (mCurrentState == STATE_PREPARED)
116            mPlayer.seekTo(pos);
117        else
118            mSaveSeekTime = pos;
119    }
120
121    public boolean isPlaying() {
122        return mPlayer.isPlaying();
123    }
124
125    public void release() {
126        mPlayer.release();
127    }
128
129    public void stopPlayback() {
130        if (mCurrentState == STATE_PREPARED) {
131            mPlayer.stop();
132        }
133    }
134
135    public boolean getAutostart() {
136        return mAutostart;
137    }
138
139    public boolean getPauseDuringPreparing() {
140        return mPauseDuringPreparing;
141    }
142
143    // Every time we start a new Video, we create a VideoView and a MediaPlayer
144    public void init(int videoLayerId, int position, boolean autoStart) {
145        mPlayer = new MediaPlayer();
146        mCurrentState = STATE_INITIALIZED;
147        mProxy = null;
148        mVideoLayerId = videoLayerId;
149        mSaveSeekTime = position;
150        mAutostart = autoStart;
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;
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(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 (mPlayer.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() {
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}
318