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