• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010 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.media.MediaPlayer.OnBufferingUpdateListener;
21 import android.media.MediaPlayer.OnCompletionListener;
22 import android.media.MediaPlayer.OnErrorListener;
23 import android.media.MediaPlayer.OnPreparedListener;
24 import android.media.MediaPlayer.OnSeekCompleteListener;
25 import android.os.Handler;
26 import android.os.Message;
27 import android.util.Log;
28 
29 import java.io.IOException;
30 import java.util.Timer;
31 import java.util.TimerTask;
32 
33 /**
34  * <p>HTML5 support class for Audio.
35  */
36 class HTML5Audio extends Handler
37                  implements MediaPlayer.OnBufferingUpdateListener,
38                             MediaPlayer.OnCompletionListener,
39                             MediaPlayer.OnErrorListener,
40                             MediaPlayer.OnPreparedListener,
41                             MediaPlayer.OnSeekCompleteListener {
42     // Logging tag.
43     private static final String LOGTAG = "HTML5Audio";
44 
45     private MediaPlayer mMediaPlayer;
46 
47     // The C++ MediaPlayerPrivateAndroid object.
48     private int mNativePointer;
49 
50     private static int IDLE        =  0;
51     private static int INITIALIZED =  1;
52     private static int PREPARED    =  2;
53     private static int STARTED     =  4;
54     private static int COMPLETE    =  5;
55     private static int PAUSED      =  6;
56     private static int STOPPED     = -2;
57     private static int ERROR       = -1;
58 
59     private int mState = IDLE;
60 
61     private String mUrl;
62     private boolean mAskToPlay = false;
63 
64     // Timer thread -> UI thread
65     private static final int TIMEUPDATE = 100;
66 
67     // The spec says the timer should fire every 250 ms or less.
68     private static final int TIMEUPDATE_PERIOD = 250;  // ms
69     // The timer for timeupate events.
70     // See http://www.whatwg.org/specs/web-apps/current-work/#event-media-timeupdate
71     private Timer mTimer;
72     private final class TimeupdateTask extends TimerTask {
run()73         public void run() {
74             HTML5Audio.this.obtainMessage(TIMEUPDATE).sendToTarget();
75         }
76     }
77 
78     @Override
handleMessage(Message msg)79     public void handleMessage(Message msg) {
80         switch (msg.what) {
81             case TIMEUPDATE: {
82                 try {
83                     if (mState != ERROR && mMediaPlayer.isPlaying()) {
84                         int position = mMediaPlayer.getCurrentPosition();
85                         nativeOnTimeupdate(position, mNativePointer);
86                     }
87                 } catch (IllegalStateException e) {
88                     mState = ERROR;
89                 }
90             }
91         }
92     }
93 
94     // event listeners for MediaPlayer
95     // Those are called from the same thread we created the MediaPlayer
96     // (i.e. the webviewcore thread here)
97 
98     // MediaPlayer.OnBufferingUpdateListener
onBufferingUpdate(MediaPlayer mp, int percent)99     public void onBufferingUpdate(MediaPlayer mp, int percent) {
100         nativeOnBuffering(percent, mNativePointer);
101     }
102 
103     // MediaPlayer.OnCompletionListener;
onCompletion(MediaPlayer mp)104     public void onCompletion(MediaPlayer mp) {
105         resetMediaPlayer();
106         mState = IDLE;
107         nativeOnEnded(mNativePointer);
108     }
109 
110     // MediaPlayer.OnErrorListener
onError(MediaPlayer mp, int what, int extra)111     public boolean onError(MediaPlayer mp, int what, int extra) {
112         mState = ERROR;
113         resetMediaPlayer();
114         mState = IDLE;
115         return false;
116     }
117 
118     // MediaPlayer.OnPreparedListener
onPrepared(MediaPlayer mp)119     public void onPrepared(MediaPlayer mp) {
120         mState = PREPARED;
121         if (mTimer != null) {
122             mTimer.schedule(new TimeupdateTask(),
123                             TIMEUPDATE_PERIOD, TIMEUPDATE_PERIOD);
124         }
125         nativeOnPrepared(mp.getDuration(), 0, 0, mNativePointer);
126         if (mAskToPlay) {
127             mAskToPlay = false;
128             play();
129         }
130     }
131 
132     // MediaPlayer.OnSeekCompleteListener
onSeekComplete(MediaPlayer mp)133     public void onSeekComplete(MediaPlayer mp) {
134         nativeOnTimeupdate(mp.getCurrentPosition(), mNativePointer);
135     }
136 
137 
138     /**
139      * @param nativePtr is the C++ pointer to the MediaPlayerPrivate object.
140      */
HTML5Audio(int nativePtr)141     public HTML5Audio(int nativePtr) {
142         // Save the native ptr
143         mNativePointer = nativePtr;
144         resetMediaPlayer();
145     }
146 
resetMediaPlayer()147     private void resetMediaPlayer() {
148         if (mMediaPlayer == null) {
149             mMediaPlayer = new MediaPlayer();
150         } else {
151             mMediaPlayer.reset();
152         }
153         mMediaPlayer.setOnBufferingUpdateListener(this);
154         mMediaPlayer.setOnCompletionListener(this);
155         mMediaPlayer.setOnErrorListener(this);
156         mMediaPlayer.setOnPreparedListener(this);
157         mMediaPlayer.setOnSeekCompleteListener(this);
158 
159         if (mTimer != null) {
160             mTimer.cancel();
161         }
162         mTimer = new Timer();
163         mState = IDLE;
164     }
165 
setDataSource(String url)166     private void setDataSource(String url) {
167         mUrl = url;
168         try {
169             if (mState != IDLE) {
170                 resetMediaPlayer();
171             }
172             mMediaPlayer.setDataSource(url);
173             mState = INITIALIZED;
174             mMediaPlayer.prepareAsync();
175         } catch (IOException e) {
176             Log.e(LOGTAG, "couldn't load the resource: " + url + " exc: " + e);
177             resetMediaPlayer();
178         }
179     }
180 
play()181     private void play() {
182         if ((mState == ERROR || mState == IDLE) && mUrl != null) {
183             resetMediaPlayer();
184             setDataSource(mUrl);
185             mAskToPlay = true;
186         }
187 
188         if (mState >= PREPARED) {
189             mMediaPlayer.start();
190             mState = STARTED;
191         }
192     }
193 
pause()194     private void pause() {
195         if (mState == STARTED) {
196             if (mTimer != null) {
197                 mTimer.purge();
198             }
199             mMediaPlayer.pause();
200             mState = PAUSED;
201         }
202     }
203 
seek(int msec)204     private void seek(int msec) {
205         if (mState >= PREPARED) {
206             mMediaPlayer.seekTo(msec);
207         }
208     }
209 
teardown()210     private void teardown() {
211         mMediaPlayer.release();
212         mState = ERROR;
213         mNativePointer = 0;
214     }
215 
getMaxTimeSeekable()216     private float getMaxTimeSeekable() {
217         return mMediaPlayer.getDuration() / 1000.0f;
218     }
219 
nativeOnBuffering(int percent, int nativePointer)220     private native void nativeOnBuffering(int percent, int nativePointer);
nativeOnEnded(int nativePointer)221     private native void nativeOnEnded(int nativePointer);
nativeOnPrepared(int duration, int width, int height, int nativePointer)222     private native void nativeOnPrepared(int duration, int width, int height, int nativePointer);
nativeOnTimeupdate(int position, int nativePointer)223     private native void nativeOnTimeupdate(int position, int nativePointer);
224 }
225