blob: d292881a67162c08d0678abbaa23c66c602506c7 [file] [log] [blame]
Nicolas Roard4b719132010-02-08 16:46:44 +00001/*
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
17package android.webkit;
18
19import android.media.MediaPlayer;
20import android.media.MediaPlayer.OnBufferingUpdateListener;
21import android.media.MediaPlayer.OnCompletionListener;
22import android.media.MediaPlayer.OnErrorListener;
23import android.media.MediaPlayer.OnPreparedListener;
24import android.media.MediaPlayer.OnSeekCompleteListener;
25import android.os.Handler;
26import android.os.Message;
27import android.util.Log;
28
29import java.io.IOException;
30import java.util.Timer;
31import java.util.TimerTask;
32
33/**
34 * <p>HTML5 support class for Audio.
35 */
36class 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 {
73 public void run() {
74 HTML5Audio.this.obtainMessage(TIMEUPDATE).sendToTarget();
75 }
76 }
77
78 @Override
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
99 public void onBufferingUpdate(MediaPlayer mp, int percent) {
100 nativeOnBuffering(percent, mNativePointer);
101 }
102
103 // MediaPlayer.OnCompletionListener;
104 public void onCompletion(MediaPlayer mp) {
105 resetMediaPlayer();
106 mState = IDLE;
107 nativeOnEnded(mNativePointer);
108 }
109
110 // MediaPlayer.OnErrorListener
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
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
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 */
141 public HTML5Audio(int nativePtr) {
142 // Save the native ptr
143 mNativePointer = nativePtr;
144 resetMediaPlayer();
145 }
146
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
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
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
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
204 private void seek(int msec) {
205 if (mState >= PREPARED) {
206 mMediaPlayer.seekTo(msec);
207 }
208 }
209
210 private void teardown() {
211 mMediaPlayer.release();
212 mState = ERROR;
213 mNativePointer = 0;
214 }
215
216 private float getMaxTimeSeekable() {
217 return mMediaPlayer.getDuration() / 1000.0f;
218 }
219
220 private native void nativeOnBuffering(int percent, int nativePointer);
221 private native void nativeOnEnded(int nativePointer);
222 private native void nativeOnPrepared(int duration, int width, int height, int nativePointer);
223 private native void nativeOnTimeupdate(int position, int nativePointer);
224}