HTML5VideoView.java revision cd445624556d8bd83313fed56388d4e6939ac5e9
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    // common Video control FUNCTIONS:
69    public void start() {
70        if (mCurrentState == STATE_PREPARED) {
71            // When replaying the same video, there is no onPrepared call.
72            // Therefore, the timer should be set up here.
73            if (mTimer == null)
74            {
75                mTimer = new Timer();
76                mTimer.schedule(new TimeupdateTask(mProxy), TIMEUPDATE_PERIOD,
77                        TIMEUPDATE_PERIOD);
78            }
79            mPlayer.start();
80        }
81    }
82
83    public void pause() {
84        if (mCurrentState == STATE_PREPARED && mPlayer.isPlaying()) {
85            mPlayer.pause();
86        }
87
88        // Delete the Timer to stop it since there is no stop call.
89        if (mTimer != null) {
90            mTimer.purge();
91            mTimer.cancel();
92            mTimer = null;
93        }
94    }
95
96    public int getDuration() {
97        if (mCurrentState == STATE_PREPARED) {
98            return mPlayer.getDuration();
99        } else {
100            return -1;
101        }
102    }
103
104    public int getCurrentPosition() {
105        if (mCurrentState == STATE_PREPARED) {
106            return mPlayer.getCurrentPosition();
107        }
108        return 0;
109    }
110
111    public void seekTo(int pos) {
112        if (mCurrentState == STATE_PREPARED)
113            mPlayer.seekTo(pos);
114        else
115            mSaveSeekTime = pos;
116    }
117
118    public boolean isPlaying() {
119        return mPlayer.isPlaying();
120    }
121
122    public void release() {
123        mPlayer.release();
124    }
125
126    public void stopPlayback() {
127        if (mCurrentState == STATE_PREPARED) {
128            mPlayer.stop();
129        }
130    }
131
132    public boolean getAutostart() {
133        return mAutostart;
134    }
135
136    // Every time we start a new Video, we create a VideoView and a MediaPlayer
137    public void init(int videoLayerId, int position, boolean autoStart) {
138        mPlayer = new MediaPlayer();
139        mCurrentState = STATE_INITIALIZED;
140        mProxy = null;
141        mVideoLayerId = videoLayerId;
142        mSaveSeekTime = position;
143        mAutostart = autoStart;
144        mTimer = null;
145    }
146
147    protected HTML5VideoView() {
148    }
149
150    protected static Map<String, String> generateHeaders(String url,
151            HTML5VideoViewProxy proxy) {
152        boolean isPrivate = proxy.getWebView().isPrivateBrowsingEnabled();
153        String cookieValue = CookieManager.getInstance().getCookie(url, isPrivate);
154        Map<String, String> headers = new HashMap<String, String>();
155        if (cookieValue != null) {
156            headers.put(COOKIE, cookieValue);
157        }
158        if (isPrivate) {
159            headers.put(HIDE_URL_LOGS, "true");
160        }
161
162        return headers;
163    }
164
165    public void setVideoURI(String uri, HTML5VideoViewProxy proxy) {
166        // When switching players, surface texture will be reused.
167        mUri = uri;
168        mHeaders = generateHeaders(uri, proxy);
169    }
170
171    // Listeners setup FUNCTIONS:
172    public void setOnCompletionListener(HTML5VideoViewProxy proxy) {
173        mPlayer.setOnCompletionListener(proxy);
174    }
175
176    public void setOnErrorListener(HTML5VideoViewProxy proxy) {
177        mPlayer.setOnErrorListener(proxy);
178    }
179
180    public void setOnPreparedListener(HTML5VideoViewProxy proxy) {
181        mProxy = proxy;
182        mPlayer.setOnPreparedListener(this);
183    }
184
185    // Normally called immediately after setVideoURI. But for full screen,
186    // this should be after surface holder created
187    public void prepareDataAndDisplayMode(HTML5VideoViewProxy proxy) {
188        // SurfaceTexture will be created lazily here for inline mode
189        decideDisplayMode();
190
191        setOnCompletionListener(proxy);
192        setOnPreparedListener(proxy);
193        setOnErrorListener(proxy);
194
195        // When there is exception, we could just bail out silently.
196        // No Video will be played though. Write the stack for debug
197        try {
198            mPlayer.setDataSource(mUri, mHeaders);
199            mPlayer.prepareAsync();
200        } catch (IllegalArgumentException e) {
201            e.printStackTrace();
202        } catch (IllegalStateException e) {
203            e.printStackTrace();
204        } catch (IOException e) {
205            e.printStackTrace();
206        }
207        mCurrentState = STATE_NOTPREPARED;
208    }
209
210
211    // Common code
212    public int getVideoLayerId() {
213        return mVideoLayerId;
214    }
215
216
217    public int getCurrentState() {
218        if (mPlayer.isPlaying()) {
219            return STATE_PLAYING;
220        } else {
221            return mCurrentState;
222        }
223    }
224
225    private static final class TimeupdateTask extends TimerTask {
226        private HTML5VideoViewProxy mProxy;
227
228        public TimeupdateTask(HTML5VideoViewProxy proxy) {
229            mProxy = proxy;
230        }
231
232        @Override
233        public void run() {
234            mProxy.onTimeupdate();
235        }
236    }
237
238    @Override
239    public void onPrepared(MediaPlayer mp) {
240        mCurrentState = STATE_PREPARED;
241        seekTo(mSaveSeekTime);
242        if (mProxy != null) {
243            mProxy.onPrepared(mp);
244        }
245    }
246
247    // Pause the play and update the play/pause button
248    public void pauseAndDispatch(HTML5VideoViewProxy proxy) {
249        if (isPlaying()) {
250            pause();
251            if (proxy != null) {
252                proxy.dispatchOnPaused();
253            }
254        }
255    }
256
257    // Below are functions that are different implementation on inline and full-
258    // screen mode. Some are specific to one type, but currently are called
259    // directly from the proxy.
260    public void enterFullScreenVideoState(int layerId,
261            HTML5VideoViewProxy proxy, WebView webView) {
262    }
263
264    public boolean isFullScreenMode() {
265        return false;
266    }
267
268    public void decideDisplayMode() {
269    }
270
271    public boolean getReadyToUseSurfTex() {
272        return false;
273    }
274
275    public SurfaceTexture getSurfaceTexture() {
276        return null;
277    }
278
279    public void deleteSurfaceTexture() {
280    }
281
282    public int getTextureName() {
283        return 0;
284    }
285
286}
287