1 /* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 * use this file except in compliance with the License. You may obtain a copy of 6 * 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, WITHOUT 12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 * License for the specific language governing permissions and limitations under 14 * the License. 15 */ 16 package android.speech.tts; 17 18 import android.content.Context; 19 import android.media.MediaPlayer; 20 import android.net.Uri; 21 import android.os.ConditionVariable; 22 import android.os.Handler; 23 import android.os.HandlerThread; 24 import android.os.Looper; 25 import android.util.Log; 26 27 /** 28 * A media player that allows blocking to wait for it to finish. 29 */ 30 class BlockingMediaPlayer { 31 32 private static final String TAG = "BlockMediaPlayer"; 33 34 private static final String MEDIA_PLAYER_THREAD_NAME = "TTS-MediaPlayer"; 35 36 private final Context mContext; 37 private final Uri mUri; 38 private final int mStreamType; 39 private final ConditionVariable mDone; 40 // Only accessed on the Handler thread 41 private MediaPlayer mPlayer; 42 private volatile boolean mFinished; 43 44 /** 45 * Creates a new blocking media player. 46 * Creating a blocking media player is a cheap operation. 47 * 48 * @param context 49 * @param uri 50 * @param streamType 51 */ 52 public BlockingMediaPlayer(Context context, Uri uri, int streamType) { 53 mContext = context; 54 mUri = uri; 55 mStreamType = streamType; 56 mDone = new ConditionVariable(); 57 58 } 59 60 /** 61 * Starts playback and waits for it to finish. 62 * Can be called from any thread. 63 * 64 * @return {@code true} if the playback finished normally, {@code false} if the playback 65 * failed or {@link #stop} was called before the playback finished. 66 */ 67 public boolean startAndWait() { 68 HandlerThread thread = new HandlerThread(MEDIA_PLAYER_THREAD_NAME); 69 thread.start(); 70 Handler handler = new Handler(thread.getLooper()); 71 mFinished = false; 72 handler.post(new Runnable() { 73 @Override 74 public void run() { 75 startPlaying(); 76 } 77 }); 78 mDone.block(); 79 handler.post(new Runnable() { 80 @Override 81 public void run() { 82 finish(); 83 // No new messages should get posted to the handler thread after this 84 Looper.myLooper().quit(); 85 } 86 }); 87 return mFinished; 88 } 89 90 /** 91 * Stops playback. Can be called multiple times. 92 * Can be called from any thread. 93 */ 94 public void stop() { 95 mDone.open(); 96 } 97 98 /** 99 * Starts playback. 100 * Called on the handler thread. 101 */ 102 private void startPlaying() { 103 mPlayer = MediaPlayer.create(mContext, mUri); 104 if (mPlayer == null) { 105 Log.w(TAG, "Failed to play " + mUri); 106 mDone.open(); 107 return; 108 } 109 try { 110 mPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() { 111 @Override 112 public boolean onError(MediaPlayer mp, int what, int extra) { 113 Log.w(TAG, "Audio playback error: " + what + ", " + extra); 114 mDone.open(); 115 return true; 116 } 117 }); 118 mPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() { 119 @Override 120 public void onCompletion(MediaPlayer mp) { 121 mFinished = true; 122 mDone.open(); 123 } 124 }); 125 mPlayer.setAudioStreamType(mStreamType); 126 mPlayer.start(); 127 } catch (IllegalArgumentException ex) { 128 Log.w(TAG, "MediaPlayer failed", ex); 129 mDone.open(); 130 } 131 } 132 133 /** 134 * Stops playback and release the media player. 135 * Called on the handler thread. 136 */ 137 private void finish() { 138 try { 139 mPlayer.stop(); 140 } catch (IllegalStateException ex) { 141 // Do nothing, the player is already stopped 142 } 143 mPlayer.release(); 144 } 145 146 }