• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2012 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.webkit;
18 
19 import android.media.MediaPlayer;
20 import android.net.Uri;
21 import android.webkit.HTML5VideoViewProxy;
22 import java.io.IOException;
23 import java.util.HashMap;
24 import java.util.Map;
25 import java.util.Timer;
26 import java.util.TimerTask;
27 
28 /**
29  * @hide This is only used by the browser
30  */
31 public class HTML5VideoView implements MediaPlayer.OnPreparedListener {
32 
33     protected static final String LOGTAG = "HTML5VideoView";
34 
35     protected static final String COOKIE = "Cookie";
36     protected static final String HIDE_URL_LOGS = "x-hide-urls-from-log";
37 
38     // For handling the seekTo before prepared, we need to know whether or not
39     // the video is prepared. Therefore, we differentiate the state between
40     // prepared and not prepared.
41     // When the video is not prepared, we will have to save the seekTo time,
42     // and use it when prepared to play.
43     // NOTE: these values are in sync with VideoLayerAndroid.h in webkit side.
44     // Please keep them in sync when changed.
45     static final int STATE_INITIALIZED        = 0;
46     static final int STATE_PREPARING          = 1;
47     static final int STATE_PREPARED           = 2;
48     static final int STATE_PLAYING            = 3;
49     static final int STATE_RESETTED           = 4;
50     static final int STATE_RELEASED           = 5;
51 
52     protected HTML5VideoViewProxy mProxy;
53 
54     // Save the seek time when not prepared. This can happen when switching
55     // video besides initial load.
56     protected int mSaveSeekTime;
57 
58     // This is used to find the VideoLayer on the native side.
59     protected int mVideoLayerId;
60 
61     // Given the fact we only have one SurfaceTexture, we cannot support multiple
62     // player at the same time. We may recreate a new one and abandon the old
63     // one at transition time.
64     protected static MediaPlayer mPlayer = null;
65     protected static int mCurrentState = -1;
66 
67     // We need to save such info.
68     protected Uri mUri;
69     protected Map<String, String> mHeaders;
70 
71     // The timer for timeupate events.
72     // See http://www.whatwg.org/specs/web-apps/current-work/#event-media-timeupdate
73     protected static Timer mTimer;
74 
75     protected boolean mPauseDuringPreparing;
76 
77     // The spec says the timer should fire every 250 ms or less.
78     private static final int TIMEUPDATE_PERIOD = 250;  // ms
79     private boolean mSkipPrepare = false;
80 
81     // common Video control FUNCTIONS:
start()82     public void start() {
83         if (mCurrentState == STATE_PREPARED) {
84             // When replaying the same video, there is no onPrepared call.
85             // Therefore, the timer should be set up here.
86             if (mTimer == null)
87             {
88                 mTimer = new Timer();
89                 mTimer.schedule(new TimeupdateTask(mProxy), TIMEUPDATE_PERIOD,
90                         TIMEUPDATE_PERIOD);
91             }
92             mPlayer.start();
93             setPlayerBuffering(false);
94         }
95     }
96 
pause()97     public void pause() {
98         if (isPlaying()) {
99             mPlayer.pause();
100         } else if (mCurrentState == STATE_PREPARING) {
101             mPauseDuringPreparing = true;
102         }
103         // Delete the Timer to stop it since there is no stop call.
104         if (mTimer != null) {
105             mTimer.purge();
106             mTimer.cancel();
107             mTimer = null;
108         }
109     }
110 
getDuration()111     public int getDuration() {
112         if (mCurrentState == STATE_PREPARED) {
113             return mPlayer.getDuration();
114         } else {
115             return -1;
116         }
117     }
118 
getCurrentPosition()119     public int getCurrentPosition() {
120         if (mCurrentState == STATE_PREPARED) {
121             return mPlayer.getCurrentPosition();
122         }
123         return 0;
124     }
125 
seekTo(int pos)126     public void seekTo(int pos) {
127         if (mCurrentState == STATE_PREPARED)
128             mPlayer.seekTo(pos);
129         else
130             mSaveSeekTime = pos;
131     }
132 
isPlaying()133     public boolean isPlaying() {
134         if (mCurrentState == STATE_PREPARED) {
135             return mPlayer.isPlaying();
136         } else {
137             return false;
138         }
139     }
140 
reset()141     public void reset() {
142         if (mCurrentState < STATE_RESETTED) {
143             mPlayer.reset();
144         }
145         mCurrentState = STATE_RESETTED;
146     }
147 
stopPlayback()148     public void stopPlayback() {
149         if (mCurrentState == STATE_PREPARED) {
150             mPlayer.stop();
151         }
152     }
153 
release()154     public static void release() {
155         if (mPlayer != null && mCurrentState != STATE_RELEASED) {
156             mPlayer.release();
157             mPlayer = null;
158         }
159         mCurrentState = STATE_RELEASED;
160     }
161 
isReleased()162     public boolean isReleased() {
163         return mCurrentState == STATE_RELEASED;
164     }
165 
getPauseDuringPreparing()166     public boolean getPauseDuringPreparing() {
167         return mPauseDuringPreparing;
168     }
169 
170     // Every time we start a new Video, we create a VideoView and a MediaPlayer
init(int videoLayerId, int position, boolean skipPrepare)171     public void init(int videoLayerId, int position, boolean skipPrepare) {
172         if (mPlayer == null) {
173             mPlayer = new MediaPlayer();
174             mCurrentState = STATE_INITIALIZED;
175         }
176         mSkipPrepare = skipPrepare;
177         // If we want to skip the prepare, then we keep the state.
178         if (!mSkipPrepare) {
179             mCurrentState = STATE_INITIALIZED;
180         }
181         mProxy = null;
182         mVideoLayerId = videoLayerId;
183         mSaveSeekTime = position;
184         mTimer = null;
185         mPauseDuringPreparing = false;
186     }
187 
HTML5VideoView()188     protected HTML5VideoView() {
189     }
190 
generateHeaders(String url, HTML5VideoViewProxy proxy)191     protected static Map<String, String> generateHeaders(String url,
192             HTML5VideoViewProxy proxy) {
193         boolean isPrivate = proxy.getWebView().isPrivateBrowsingEnabled();
194         String cookieValue = CookieManager.getInstance().getCookie(url, isPrivate);
195         Map<String, String> headers = new HashMap<String, String>();
196         if (cookieValue != null) {
197             headers.put(COOKIE, cookieValue);
198         }
199         if (isPrivate) {
200             headers.put(HIDE_URL_LOGS, "true");
201         }
202 
203         return headers;
204     }
205 
setVideoURI(String uri, HTML5VideoViewProxy proxy)206     public void setVideoURI(String uri, HTML5VideoViewProxy proxy) {
207         // When switching players, surface texture will be reused.
208         mUri = Uri.parse(uri);
209         mHeaders = generateHeaders(uri, proxy);
210     }
211 
212     // Listeners setup FUNCTIONS:
setOnCompletionListener(HTML5VideoViewProxy proxy)213     public void setOnCompletionListener(HTML5VideoViewProxy proxy) {
214         mPlayer.setOnCompletionListener(proxy);
215     }
216 
setOnErrorListener(HTML5VideoViewProxy proxy)217     public void setOnErrorListener(HTML5VideoViewProxy proxy) {
218         mPlayer.setOnErrorListener(proxy);
219     }
220 
setOnPreparedListener(HTML5VideoViewProxy proxy)221     public void setOnPreparedListener(HTML5VideoViewProxy proxy) {
222         mProxy = proxy;
223         mPlayer.setOnPreparedListener(this);
224     }
225 
setOnInfoListener(HTML5VideoViewProxy proxy)226     public void setOnInfoListener(HTML5VideoViewProxy proxy) {
227         mPlayer.setOnInfoListener(proxy);
228     }
229 
prepareDataCommon(HTML5VideoViewProxy proxy)230     public void prepareDataCommon(HTML5VideoViewProxy proxy) {
231         if (!mSkipPrepare) {
232             try {
233                 mPlayer.reset();
234                 mPlayer.setDataSource(proxy.getContext(), mUri, mHeaders);
235                 mPlayer.prepareAsync();
236             } catch (IllegalArgumentException e) {
237                 e.printStackTrace();
238             } catch (IllegalStateException e) {
239                 e.printStackTrace();
240             } catch (IOException e) {
241                 e.printStackTrace();
242             }
243             mCurrentState = STATE_PREPARING;
244         } else {
245             // If we skip prepare and the onPrepared happened in inline mode, we
246             // don't need to call prepare again, we just need to call onPrepared
247             // to refresh the state here.
248             if (mCurrentState >= STATE_PREPARED) {
249                 onPrepared(mPlayer);
250             }
251             mSkipPrepare = false;
252         }
253     }
254 
reprepareData(HTML5VideoViewProxy proxy)255     public void reprepareData(HTML5VideoViewProxy proxy) {
256         mPlayer.reset();
257         prepareDataCommon(proxy);
258     }
259 
260     // Normally called immediately after setVideoURI. But for full screen,
261     // this should be after surface holder created
prepareDataAndDisplayMode(HTML5VideoViewProxy proxy)262     public void prepareDataAndDisplayMode(HTML5VideoViewProxy proxy) {
263         // SurfaceTexture will be created lazily here for inline mode
264         decideDisplayMode();
265 
266         setOnCompletionListener(proxy);
267         setOnPreparedListener(proxy);
268         setOnErrorListener(proxy);
269         setOnInfoListener(proxy);
270 
271         prepareDataCommon(proxy);
272     }
273 
274 
275     // Common code
getVideoLayerId()276     public int getVideoLayerId() {
277         return mVideoLayerId;
278     }
279 
280 
getCurrentState()281     public int getCurrentState() {
282         if (isPlaying()) {
283             return STATE_PLAYING;
284         } else {
285             return mCurrentState;
286         }
287     }
288 
289     private static final class TimeupdateTask extends TimerTask {
290         private HTML5VideoViewProxy mProxy;
291 
TimeupdateTask(HTML5VideoViewProxy proxy)292         public TimeupdateTask(HTML5VideoViewProxy proxy) {
293             mProxy = proxy;
294         }
295 
296         @Override
run()297         public void run() {
298             mProxy.onTimeupdate();
299         }
300     }
301 
302     @Override
onPrepared(MediaPlayer mp)303     public void onPrepared(MediaPlayer mp) {
304         mCurrentState = STATE_PREPARED;
305         seekTo(mSaveSeekTime);
306         if (mProxy != null) {
307             mProxy.onPrepared(mp);
308         }
309         if (mPauseDuringPreparing) {
310             pauseAndDispatch(mProxy);
311             mPauseDuringPreparing = false;
312         }
313     }
314 
315     // Pause the play and update the play/pause button
pauseAndDispatch(HTML5VideoViewProxy proxy)316     public void pauseAndDispatch(HTML5VideoViewProxy proxy) {
317         pause();
318         if (proxy != null) {
319             proxy.dispatchOnPaused();
320         }
321     }
322 
323     // Below are functions that are different implementation on inline and full-
324     // screen mode. Some are specific to one type, but currently are called
325     // directly from the proxy.
enterFullScreenVideoState(int layerId, HTML5VideoViewProxy proxy, WebViewClassic webView)326     public void enterFullScreenVideoState(int layerId,
327             HTML5VideoViewProxy proxy, WebViewClassic webView) {
328     }
329 
isFullScreenMode()330     public boolean isFullScreenMode() {
331         return false;
332     }
333 
decideDisplayMode()334     public void decideDisplayMode() {
335     }
336 
getReadyToUseSurfTex()337     public boolean getReadyToUseSurfTex() {
338         return false;
339     }
340 
deleteSurfaceTexture()341     public void deleteSurfaceTexture() {
342     }
343 
getTextureName()344     public int getTextureName() {
345         return 0;
346     }
347 
348     // This is true only when the player is buffering and paused
349     public boolean mPlayerBuffering = false;
350 
getPlayerBuffering()351     public boolean getPlayerBuffering() {
352         return mPlayerBuffering;
353     }
354 
setPlayerBuffering(boolean playerBuffering)355     public void setPlayerBuffering(boolean playerBuffering) {
356         mPlayerBuffering = playerBuffering;
357         switchProgressView(playerBuffering);
358     }
359 
360 
switchProgressView(boolean playerBuffering)361     protected void switchProgressView(boolean playerBuffering) {
362         // Only used in HTML5VideoFullScreen
363     }
364 
fullScreenExited()365     public boolean fullScreenExited() {
366         // Only meaningful for HTML5VideoFullScreen
367         return false;
368     }
369 
370     private boolean mStartWhenPrepared = false;
371 
setStartWhenPrepared(boolean willPlay)372     public void setStartWhenPrepared(boolean willPlay) {
373         mStartWhenPrepared  = willPlay;
374     }
375 
getStartWhenPrepared()376     public boolean getStartWhenPrepared() {
377         return mStartWhenPrepared;
378     }
379 
showControllerInFullScreen()380     public void showControllerInFullScreen() {
381     }
382 
383 }
384