HTML5VideoView.java revision 1ab0df7f5a761c580c72c96eb81997a531f25018
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    protected static final int STATE_NOTPREPARED        = 0;
31    protected static final int STATE_PREPARED           = 1;
32
33    protected int mCurrentState;
34
35    protected HTML5VideoViewProxy mProxy;
36
37    // Save the seek time when not prepared. This can happen when switching
38    // video besides initial load.
39    protected int mSaveSeekTime;
40
41    // This is used to find the VideoLayer on the native side.
42    protected int mVideoLayerId;
43
44    // Every video will have one MediaPlayer. Given the fact we only have one
45    // SurfaceTexture, there is only one MediaPlayer in action. Every time we
46    // switch videos, a new instance of MediaPlayer will be created in reset().
47    // Switching between inline and full screen will also create a new instance.
48    protected MediaPlayer mPlayer;
49
50    // This will be set up every time we create the Video View object.
51    // Set to true only when switching into full screen while playing
52    protected boolean mAutostart;
53
54    // We need to save such info.
55    protected String mUri;
56    protected Map<String, String> mHeaders;
57
58    // The timer for timeupate events.
59    // See http://www.whatwg.org/specs/web-apps/current-work/#event-media-timeupdate
60    protected static Timer mTimer;
61
62    // The spec says the timer should fire every 250 ms or less.
63    private static final int TIMEUPDATE_PERIOD = 250;  // ms
64
65    // common Video control FUNCTIONS:
66    public void start() {
67        if (mCurrentState == STATE_PREPARED) {
68            mPlayer.start();
69        }
70    }
71
72    public void pause() {
73        if (mCurrentState == STATE_PREPARED && mPlayer.isPlaying()) {
74            mPlayer.pause();
75        }
76        if (mTimer != null) {
77            mTimer.purge();
78        }
79    }
80
81    public int getDuration() {
82        if (mCurrentState == STATE_PREPARED) {
83            return mPlayer.getDuration();
84        } else {
85            return -1;
86        }
87    }
88
89    public int getCurrentPosition() {
90        if (mCurrentState == STATE_PREPARED) {
91            return mPlayer.getCurrentPosition();
92        }
93        return 0;
94    }
95
96    public void seekTo(int pos) {
97        if (mCurrentState == STATE_PREPARED)
98            mPlayer.seekTo(pos);
99        else
100            mSaveSeekTime = pos;
101    }
102
103    public boolean isPlaying() {
104        return mPlayer.isPlaying();
105    }
106
107    public void release() {
108        mPlayer.release();
109    }
110
111    public void stopPlayback() {
112        if (mCurrentState == STATE_PREPARED) {
113            mPlayer.stop();
114        }
115    }
116
117    public boolean getAutostart() {
118        return mAutostart;
119    }
120
121    // Every time we start a new Video, we create a VideoView and a MediaPlayer
122    public void init(int videoLayerId, int position, boolean autoStart) {
123        mPlayer = new MediaPlayer();
124        mCurrentState = STATE_NOTPREPARED;
125        mProxy = null;
126        mVideoLayerId = videoLayerId;
127        mSaveSeekTime = position;
128        mAutostart = autoStart;
129    }
130
131    protected HTML5VideoView() {
132    }
133
134    protected static Map<String, String> generateHeaders(String url,
135            HTML5VideoViewProxy proxy) {
136        boolean isPrivate = proxy.getWebView().isPrivateBrowsingEnabled();
137        String cookieValue = CookieManager.getInstance().getCookie(url, isPrivate);
138        Map<String, String> headers = new HashMap<String, String>();
139        if (cookieValue != null) {
140            headers.put(COOKIE, cookieValue);
141        }
142        if (isPrivate) {
143            headers.put(HIDE_URL_LOGS, "true");
144        }
145
146        return headers;
147    }
148
149    public void setVideoURI(String uri, HTML5VideoViewProxy proxy) {
150        // When switching players, surface texture will be reused.
151        mUri = uri;
152        mHeaders = generateHeaders(uri, proxy);
153
154        mTimer = new Timer();
155    }
156
157    // Listeners setup FUNCTIONS:
158    public void setOnCompletionListener(HTML5VideoViewProxy proxy) {
159        mPlayer.setOnCompletionListener(proxy);
160    }
161
162    public void setOnErrorListener(HTML5VideoViewProxy proxy) {
163        mPlayer.setOnErrorListener(proxy);
164    }
165
166    public void setOnPreparedListener(HTML5VideoViewProxy proxy) {
167        mProxy = proxy;
168        mPlayer.setOnPreparedListener(this);
169    }
170
171    // Normally called immediately after setVideoURI. But for full screen,
172    // this should be after surface holder created
173    public void prepareDataAndDisplayMode(HTML5VideoViewProxy proxy) {
174        // SurfaceTexture will be created lazily here for inline mode
175        decideDisplayMode();
176
177        setOnCompletionListener(proxy);
178        setOnPreparedListener(proxy);
179        setOnErrorListener(proxy);
180
181        // When there is exception, we could just bail out silently.
182        // No Video will be played though. Write the stack for debug
183        try {
184            mPlayer.setDataSource(mUri, mHeaders);
185            mPlayer.prepareAsync();
186        } catch (IllegalArgumentException e) {
187            e.printStackTrace();
188        } catch (IllegalStateException e) {
189            e.printStackTrace();
190        } catch (IOException e) {
191            e.printStackTrace();
192        }
193    }
194
195
196    // Common code
197    public int getVideoLayerId() {
198        return mVideoLayerId;
199    }
200
201    private static final class TimeupdateTask extends TimerTask {
202        private HTML5VideoViewProxy mProxy;
203
204        public TimeupdateTask(HTML5VideoViewProxy proxy) {
205            mProxy = proxy;
206        }
207
208        @Override
209        public void run() {
210            mProxy.onTimeupdate();
211        }
212    }
213
214    @Override
215    public void onPrepared(MediaPlayer mp) {
216        mCurrentState = STATE_PREPARED;
217        seekTo(mSaveSeekTime);
218        if (mProxy != null)
219            mProxy.onPrepared(mp);
220
221        mTimer.schedule(new TimeupdateTask(mProxy), TIMEUPDATE_PERIOD, TIMEUPDATE_PERIOD);
222
223    }
224
225    // Pause the play and update the play/pause button
226    public void pauseAndDispatch(HTML5VideoViewProxy proxy) {
227        if (isPlaying()) {
228            pause();
229            if (proxy != null) {
230                proxy.dispatchOnPaused();
231            }
232        }
233    }
234
235    // Below are functions that are different implementation on inline and full-
236    // screen mode. Some are specific to one type, but currently are called
237    // directly from the proxy.
238    public void enterFullScreenVideoState(int layerId,
239            HTML5VideoViewProxy proxy, WebView webView) {
240    }
241
242    public boolean isFullScreenMode() {
243        return false;
244    }
245
246    public void decideDisplayMode() {
247    }
248
249    public boolean getReadyToUseSurfTex() {
250        return false;
251    }
252
253    public SurfaceTexture getSurfaceTexture() {
254        return null;
255    }
256
257    public void deleteSurfaceTexture() {
258    }
259
260    public int getTextureName() {
261        return 0;
262    }
263
264}
265