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