blob: 3cf60dd8f9920ff1822bc1f08f0d6abd3ef4ca8e [file] [log] [blame]
/*
* Copyright (C) 2011 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.speech.tts;
import android.content.Context;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.ConditionVariable;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.util.Log;
/**
* A media player that allows blocking to wait for it to finish.
*/
class BlockingMediaPlayer {
private static final String TAG = "BlockMediaPlayer";
private static final String MEDIA_PLAYER_THREAD_NAME = "TTS-MediaPlayer";
private final Context mContext;
private final Uri mUri;
private final int mStreamType;
private final ConditionVariable mDone;
// Only accessed on the Handler thread
private MediaPlayer mPlayer;
private volatile boolean mFinished;
/**
* Creates a new blocking media player.
* Creating a blocking media player is a cheap operation.
*
* @param context
* @param uri
* @param streamType
*/
public BlockingMediaPlayer(Context context, Uri uri, int streamType) {
mContext = context;
mUri = uri;
mStreamType = streamType;
mDone = new ConditionVariable();
}
/**
* Starts playback and waits for it to finish.
* Can be called from any thread.
*
* @return {@code true} if the playback finished normally, {@code false} if the playback
* failed or {@link #stop} was called before the playback finished.
*/
public boolean startAndWait() {
HandlerThread thread = new HandlerThread(MEDIA_PLAYER_THREAD_NAME);
thread.start();
Handler handler = new Handler(thread.getLooper());
mFinished = false;
handler.post(new Runnable() {
@Override
public void run() {
startPlaying();
}
});
mDone.block();
handler.post(new Runnable() {
@Override
public void run() {
finish();
// No new messages should get posted to the handler thread after this
Looper.myLooper().quit();
}
});
return mFinished;
}
/**
* Stops playback. Can be called multiple times.
* Can be called from any thread.
*/
public void stop() {
mDone.open();
}
/**
* Starts playback.
* Called on the handler thread.
*/
private void startPlaying() {
mPlayer = MediaPlayer.create(mContext, mUri);
if (mPlayer == null) {
Log.w(TAG, "Failed to play " + mUri);
mDone.open();
return;
}
try {
mPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
@Override
public boolean onError(MediaPlayer mp, int what, int extra) {
Log.w(TAG, "Audio playback error: " + what + ", " + extra);
mDone.open();
return true;
}
});
mPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
mFinished = true;
mDone.open();
}
});
mPlayer.setAudioStreamType(mStreamType);
mPlayer.start();
} catch (IllegalArgumentException ex) {
Log.w(TAG, "MediaPlayer failed", ex);
mDone.open();
}
}
/**
* Stops playback and release the media player.
* Called on the handler thread.
*/
private void finish() {
try {
mPlayer.stop();
} catch (IllegalStateException ex) {
// Do nothing, the player is already stopped
}
mPlayer.release();
}
}