/* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.webkit; import android.media.MediaPlayer; import android.media.MediaPlayer.OnBufferingUpdateListener; import android.media.MediaPlayer.OnCompletionListener; import android.media.MediaPlayer.OnErrorListener; import android.media.MediaPlayer.OnPreparedListener; import android.media.MediaPlayer.OnSeekCompleteListener; import android.os.Handler; import android.os.Message; import android.util.Log; import java.io.IOException; import java.util.Timer; import java.util.TimerTask; /** *
HTML5 support class for Audio. */ class HTML5Audio extends Handler implements MediaPlayer.OnBufferingUpdateListener, MediaPlayer.OnCompletionListener, MediaPlayer.OnErrorListener, MediaPlayer.OnPreparedListener, MediaPlayer.OnSeekCompleteListener { // Logging tag. private static final String LOGTAG = "HTML5Audio"; private MediaPlayer mMediaPlayer; // The C++ MediaPlayerPrivateAndroid object. private int mNativePointer; private static int IDLE = 0; private static int INITIALIZED = 1; private static int PREPARED = 2; private static int STARTED = 4; private static int COMPLETE = 5; private static int PAUSED = 6; private static int STOPPED = -2; private static int ERROR = -1; private int mState = IDLE; private String mUrl; private boolean mAskToPlay = false; // Timer thread -> UI thread private static final int TIMEUPDATE = 100; // The spec says the timer should fire every 250 ms or less. private static final int TIMEUPDATE_PERIOD = 250; // ms // The timer for timeupate events. // See http://www.whatwg.org/specs/web-apps/current-work/#event-media-timeupdate private Timer mTimer; private final class TimeupdateTask extends TimerTask { public void run() { HTML5Audio.this.obtainMessage(TIMEUPDATE).sendToTarget(); } } @Override public void handleMessage(Message msg) { switch (msg.what) { case TIMEUPDATE: { try { if (mState != ERROR && mMediaPlayer.isPlaying()) { int position = mMediaPlayer.getCurrentPosition(); nativeOnTimeupdate(position, mNativePointer); } } catch (IllegalStateException e) { mState = ERROR; } } } } // event listeners for MediaPlayer // Those are called from the same thread we created the MediaPlayer // (i.e. the webviewcore thread here) // MediaPlayer.OnBufferingUpdateListener public void onBufferingUpdate(MediaPlayer mp, int percent) { nativeOnBuffering(percent, mNativePointer); } // MediaPlayer.OnCompletionListener; public void onCompletion(MediaPlayer mp) { resetMediaPlayer(); mState = IDLE; nativeOnEnded(mNativePointer); } // MediaPlayer.OnErrorListener public boolean onError(MediaPlayer mp, int what, int extra) { mState = ERROR; resetMediaPlayer(); mState = IDLE; return false; } // MediaPlayer.OnPreparedListener public void onPrepared(MediaPlayer mp) { mState = PREPARED; if (mTimer != null) { mTimer.schedule(new TimeupdateTask(), TIMEUPDATE_PERIOD, TIMEUPDATE_PERIOD); } nativeOnPrepared(mp.getDuration(), 0, 0, mNativePointer); if (mAskToPlay) { mAskToPlay = false; play(); } } // MediaPlayer.OnSeekCompleteListener public void onSeekComplete(MediaPlayer mp) { nativeOnTimeupdate(mp.getCurrentPosition(), mNativePointer); } /** * @param nativePtr is the C++ pointer to the MediaPlayerPrivate object. */ public HTML5Audio(int nativePtr) { // Save the native ptr mNativePointer = nativePtr; resetMediaPlayer(); } private void resetMediaPlayer() { if (mMediaPlayer == null) { mMediaPlayer = new MediaPlayer(); } else { mMediaPlayer.reset(); } mMediaPlayer.setOnBufferingUpdateListener(this); mMediaPlayer.setOnCompletionListener(this); mMediaPlayer.setOnErrorListener(this); mMediaPlayer.setOnPreparedListener(this); mMediaPlayer.setOnSeekCompleteListener(this); if (mTimer != null) { mTimer.cancel(); } mTimer = new Timer(); mState = IDLE; } private void setDataSource(String url) { mUrl = url; try { if (mState != IDLE) { resetMediaPlayer(); } mMediaPlayer.setDataSource(url); mState = INITIALIZED; mMediaPlayer.prepareAsync(); } catch (IOException e) { Log.e(LOGTAG, "couldn't load the resource: " + url + " exc: " + e); resetMediaPlayer(); } } private void play() { if ((mState == ERROR || mState == IDLE) && mUrl != null) { resetMediaPlayer(); setDataSource(mUrl); mAskToPlay = true; } if (mState >= PREPARED) { mMediaPlayer.start(); mState = STARTED; } } private void pause() { if (mState == STARTED) { if (mTimer != null) { mTimer.purge(); } mMediaPlayer.pause(); mState = PAUSED; } } private void seek(int msec) { if (mState >= PREPARED) { mMediaPlayer.seekTo(msec); } } private void teardown() { mMediaPlayer.release(); mState = ERROR; mNativePointer = 0; } private float getMaxTimeSeekable() { return mMediaPlayer.getDuration() / 1000.0f; } private native void nativeOnBuffering(int percent, int nativePointer); private native void nativeOnEnded(int nativePointer); private native void nativeOnPrepared(int duration, int width, int height, int nativePointer); private native void nativeOnTimeupdate(int position, int nativePointer); }