1b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson/* 2b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson * Copyright (C) 2011 The Android Open Source Project 3b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson * 4b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson * Licensed under the Apache License, Version 2.0 (the "License"); 5b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson * you may not use this file except in compliance with the License. 6b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson * You may obtain a copy of the License at 7b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson * 8b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson * http://www.apache.org/licenses/LICENSE-2.0 9b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson * 10b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson * Unless required by applicable law or agreed to in writing, software 11b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson * distributed under the License is distributed on an "AS IS" BASIS, 12b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson * See the License for the specific language governing permissions and 14b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson * limitations under the License. 15b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson */ 16b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson 17b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudsonpackage com.android.ex.variablespeed; 18b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson 199730f15ebbf4b64cd48e0777850e56cb516a9ed4Hugo Hudsonimport com.google.common.base.Preconditions; 209730f15ebbf4b64cd48e0777850e56cb516a9ed4Hugo Hudson 21b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudsonimport android.content.Context; 22b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudsonimport android.media.MediaPlayer; 23b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudsonimport android.net.Uri; 240bd6ec5bc06b869131ee0facf38ff02f81f65c10Hugo Hudsonimport android.util.Log; 25b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson 26b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudsonimport java.io.IOException; 27b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudsonimport java.util.concurrent.CountDownLatch; 28b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudsonimport java.util.concurrent.Executor; 29b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudsonimport java.util.concurrent.TimeUnit; 30b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudsonimport java.util.concurrent.TimeoutException; 31b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson 32b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudsonimport javax.annotation.concurrent.GuardedBy; 33b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudsonimport javax.annotation.concurrent.ThreadSafe; 34b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson 35b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson/** 36b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson * This class behaves in a similar fashion to the MediaPlayer, but by using 37b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson * native code it is able to use variable-speed playback. 38b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson * <p> 39b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson * This class is thread-safe. It's not yet perfect though, see the unit tests 40b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson * for details - there is insufficient testing for the concurrent logic. You are 41b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson * probably best advised to use thread confinment until the unit tests are more 42b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson * complete with regards to threading. 43b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson * <p> 44b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson * The easiest way to ensure that calls to this class are not made concurrently 45b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson * (besides only ever accessing it from one thread) is to wrap it in a 46b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson * {@link SingleThreadedMediaPlayerProxy}, designed just for this purpose. 47b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson */ 48b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson@ThreadSafe 49b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudsonpublic class VariableSpeed implements MediaPlayerProxy { 500bd6ec5bc06b869131ee0facf38ff02f81f65c10Hugo Hudson private static final String TAG = "VariableSpeed"; 510bd6ec5bc06b869131ee0facf38ff02f81f65c10Hugo Hudson 52b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson private final Executor mExecutor; 53b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson private final Object lock = new Object(); 54b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson @GuardedBy("lock") private MediaPlayerDataSource mDataSource; 55b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson @GuardedBy("lock") private boolean mIsPrepared; 56b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson @GuardedBy("lock") private boolean mHasDuration; 57b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson @GuardedBy("lock") private boolean mHasStartedPlayback; 58b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson @GuardedBy("lock") private CountDownLatch mEngineInitializedLatch; 59b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson @GuardedBy("lock") private CountDownLatch mPlaybackFinishedLatch; 60b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson @GuardedBy("lock") private boolean mHasBeenReleased = true; 61b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson @GuardedBy("lock") private boolean mIsReadyToReUse = true; 62b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson @GuardedBy("lock") private boolean mSkipCompletionReport; 63b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson @GuardedBy("lock") private int mStartPosition; 64b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson @GuardedBy("lock") private float mCurrentPlaybackRate = 1.0f; 65b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson @GuardedBy("lock") private int mDuration; 66b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson @GuardedBy("lock") private MediaPlayer.OnCompletionListener mCompletionListener; 67dc442b4d99512bf7c41ee5ceae6c93a3c3568b57Flavio Lerda @GuardedBy("lock") private int mAudioStreamType; 68b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson 69b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson private VariableSpeed(Executor executor) throws UnsupportedOperationException { 709730f15ebbf4b64cd48e0777850e56cb516a9ed4Hugo Hudson Preconditions.checkNotNull(executor); 71b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson mExecutor = executor; 72b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson try { 73b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson VariableSpeedNative.loadLibrary(); 74b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson } catch (UnsatisfiedLinkError e) { 75b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson throw new UnsupportedOperationException("could not load library", e); 76b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson } catch (SecurityException e) { 77b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson throw new UnsupportedOperationException("could not load library", e); 78b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson } 79b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson reset(); 80b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson } 81b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson 82b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson public static MediaPlayerProxy createVariableSpeed(Executor executor) 83b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson throws UnsupportedOperationException { 84b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson return new SingleThreadedMediaPlayerProxy(new VariableSpeed(executor)); 85b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson } 86b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson 87b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson @Override 88b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson public void setOnCompletionListener(MediaPlayer.OnCompletionListener listener) { 89b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson synchronized (lock) { 90b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson check(!mHasBeenReleased, "has been released, reset before use"); 91b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson mCompletionListener = listener; 92b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson } 93b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson } 94b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson 95b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson @Override 96b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson public void setOnErrorListener(MediaPlayer.OnErrorListener listener) { 97b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson synchronized (lock) { 98b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson check(!mHasBeenReleased, "has been released, reset before use"); 990bd6ec5bc06b869131ee0facf38ff02f81f65c10Hugo Hudson // TODO: I haven't actually added any error listener code. 100b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson } 101b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson } 102b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson 103b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson @Override 104b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson public void release() { 105b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson synchronized (lock) { 106b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson if (mHasBeenReleased) { 107b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson return; 108b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson } 109b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson mHasBeenReleased = true; 110b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson } 111b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson stopCurrentPlayback(); 112b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson boolean requiresShutdown = false; 113b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson synchronized (lock) { 114b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson requiresShutdown = hasEngineBeenInitialized(); 115b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson } 116b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson if (requiresShutdown) { 117b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson VariableSpeedNative.shutdownEngine(); 118b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson } 119b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson synchronized (lock) { 120b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson mIsReadyToReUse = true; 121b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson } 122b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson } 123b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson 124b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson private boolean hasEngineBeenInitialized() { 125b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson return mEngineInitializedLatch.getCount() <= 0; 126b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson } 127b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson 128b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson private boolean hasPlaybackFinished() { 129b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson return mPlaybackFinishedLatch.getCount() <= 0; 130b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson } 131b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson 132b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson /** 133b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson * Stops the current playback, returns once it has stopped. 134b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson */ 135b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson private void stopCurrentPlayback() { 136b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson boolean isPlaying; 137b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson CountDownLatch engineInitializedLatch; 138b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson CountDownLatch playbackFinishedLatch; 139b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson synchronized (lock) { 140b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson isPlaying = mHasStartedPlayback && !hasPlaybackFinished(); 141b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson engineInitializedLatch = mEngineInitializedLatch; 142b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson playbackFinishedLatch = mPlaybackFinishedLatch; 143b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson if (isPlaying) { 144b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson mSkipCompletionReport = true; 145b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson } 146b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson } 147b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson if (isPlaying) { 148b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson waitForLatch(engineInitializedLatch); 149b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson VariableSpeedNative.stopPlayback(); 150b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson waitForLatch(playbackFinishedLatch); 151b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson } 152b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson } 153b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson 154b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson private void waitForLatch(CountDownLatch latch) { 155b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson try { 1569730f15ebbf4b64cd48e0777850e56cb516a9ed4Hugo Hudson boolean success = latch.await(1, TimeUnit.SECONDS); 157b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson if (!success) { 158b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson reportException(new TimeoutException("waited too long")); 159b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson } 160b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson } catch (InterruptedException e) { 161b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson // Preserve the interrupt status, though this is unexpected. 162b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson Thread.currentThread().interrupt(); 163b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson reportException(e); 164b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson } 165b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson } 166b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson 167b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson @Override 168b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson public void setDataSource(Context context, Uri intentUri) { 169b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson checkNotNull(context, "context"); 170b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson checkNotNull(intentUri, "intentUri"); 171b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson innerSetDataSource(new MediaPlayerDataSource(context, intentUri)); 172b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson } 173b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson 174b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson @Override 175b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson public void setDataSource(String path) { 176b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson checkNotNull(path, "path"); 177b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson innerSetDataSource(new MediaPlayerDataSource(path)); 178b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson } 179b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson 180b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson private void innerSetDataSource(MediaPlayerDataSource source) { 181b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson checkNotNull(source, "source"); 182b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson synchronized (lock) { 183b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson check(!mHasBeenReleased, "has been released, reset before use"); 184b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson check(mDataSource == null, "cannot setDataSource more than once"); 185b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson mDataSource = source; 186b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson } 187b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson } 188b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson 189b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson @Override 190b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson public void reset() { 191b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson boolean requiresRelease; 192b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson synchronized (lock) { 193b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson requiresRelease = !mHasBeenReleased; 194b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson } 195b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson if (requiresRelease) { 196b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson release(); 197b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson } 198b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson synchronized (lock) { 199b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson check(mHasBeenReleased && mIsReadyToReUse, "to re-use, must call reset after release"); 200b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson mDataSource = null; 201b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson mIsPrepared = false; 202b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson mHasDuration = false; 203b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson mHasStartedPlayback = false; 204b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson mEngineInitializedLatch = new CountDownLatch(1); 205b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson mPlaybackFinishedLatch = new CountDownLatch(1); 206b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson mHasBeenReleased = false; 207b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson mIsReadyToReUse = false; 208b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson mSkipCompletionReport = false; 209b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson mStartPosition = 0; 210b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson mDuration = 0; 211b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson } 212b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson } 213b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson 214b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson @Override 215b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson public void prepare() throws IOException { 216b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson MediaPlayerDataSource dataSource; 217dc442b4d99512bf7c41ee5ceae6c93a3c3568b57Flavio Lerda int audioStreamType; 218b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson synchronized (lock) { 219b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson check(!mHasBeenReleased, "has been released, reset before use"); 220b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson check(mDataSource != null, "must setDataSource before you prepare"); 221b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson check(!mIsPrepared, "cannot prepare more than once"); 222b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson mIsPrepared = true; 223b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson dataSource = mDataSource; 224dc442b4d99512bf7c41ee5ceae6c93a3c3568b57Flavio Lerda audioStreamType = mAudioStreamType; 225b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson } 226b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson // NYI This should become another executable that we can wait on. 227b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson MediaPlayer mediaPlayer = new MediaPlayer(); 228dc442b4d99512bf7c41ee5ceae6c93a3c3568b57Flavio Lerda mediaPlayer.setAudioStreamType(audioStreamType); 229b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson dataSource.setAsSourceFor(mediaPlayer); 230b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson mediaPlayer.prepare(); 231b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson synchronized (lock) { 232b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson check(!mHasDuration, "can't have duration, this is impossible"); 233b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson mHasDuration = true; 234b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson mDuration = mediaPlayer.getDuration(); 235b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson } 236b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson mediaPlayer.release(); 237b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson } 238b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson 239b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson @Override 240b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson public int getDuration() { 241b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson synchronized (lock) { 242b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson check(!mHasBeenReleased, "has been released, reset before use"); 243b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson check(mHasDuration, "you haven't called prepare, can't get the duration"); 244b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson return mDuration; 245b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson } 246b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson } 247b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson 248b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson @Override 249b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson public void seekTo(int startPosition) { 250b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson boolean currentlyPlaying; 251b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson MediaPlayerDataSource dataSource; 252b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson synchronized (lock) { 253b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson check(!mHasBeenReleased, "has been released, reset before use"); 254b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson check(mHasDuration, "you can't seek until you have prepared"); 255b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson currentlyPlaying = mHasStartedPlayback && !hasPlaybackFinished(); 256b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson mStartPosition = Math.min(startPosition, mDuration); 257b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson dataSource = mDataSource; 258b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson } 259b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson if (currentlyPlaying) { 260b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson stopAndStartPlayingAgain(dataSource); 261b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson } 262b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson } 263b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson 264b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson private void stopAndStartPlayingAgain(MediaPlayerDataSource source) { 265b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson stopCurrentPlayback(); 266b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson reset(); 267b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson innerSetDataSource(source); 268b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson try { 269b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson prepare(); 270b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson } catch (IOException e) { 271b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson reportException(e); 272b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson return; 273b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson } 274b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson start(); 275b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson return; 276b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson } 277b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson 278b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson private void reportException(Exception e) { 2790bd6ec5bc06b869131ee0facf38ff02f81f65c10Hugo Hudson Log.e(TAG, "playback error:", e); 280b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson } 281b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson 282b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson @Override 283b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson public void start() { 284b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson MediaPlayerDataSource restartWithThisDataSource = null; 285b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson synchronized (lock) { 286b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson check(!mHasBeenReleased, "has been released, reset before use"); 287b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson check(mIsPrepared, "must have prepared before you can start"); 288b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson if (!mHasStartedPlayback) { 289b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson // Playback has not started. Start it. 290b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson mHasStartedPlayback = true; 291b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson EngineParameters engineParameters = new EngineParameters.Builder() 292b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson .initialRate(mCurrentPlaybackRate) 293dc442b4d99512bf7c41ee5ceae6c93a3c3568b57Flavio Lerda .startPositionMillis(mStartPosition) 294dc442b4d99512bf7c41ee5ceae6c93a3c3568b57Flavio Lerda .audioStreamType(mAudioStreamType) 295dc442b4d99512bf7c41ee5ceae6c93a3c3568b57Flavio Lerda .build(); 2960bd6ec5bc06b869131ee0facf38ff02f81f65c10Hugo Hudson VariableSpeedNative.initializeEngine(engineParameters); 2970bd6ec5bc06b869131ee0facf38ff02f81f65c10Hugo Hudson VariableSpeedNative.startPlayback(); 2980bd6ec5bc06b869131ee0facf38ff02f81f65c10Hugo Hudson mEngineInitializedLatch.countDown(); 2990bd6ec5bc06b869131ee0facf38ff02f81f65c10Hugo Hudson mExecutor.execute(new PlaybackRunnable(mDataSource)); 300b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson } else { 301b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson // Playback has already started. Restart it, without holding the 302b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson // lock. 303b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson restartWithThisDataSource = mDataSource; 304b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson } 305b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson } 306b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson if (restartWithThisDataSource != null) { 307b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson stopAndStartPlayingAgain(restartWithThisDataSource); 308b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson } 309b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson } 310b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson 311b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson /** A Runnable capable of driving the native audio playback methods. */ 312b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson private final class PlaybackRunnable implements Runnable { 313b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson private final MediaPlayerDataSource mInnerSource; 314b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson 3150bd6ec5bc06b869131ee0facf38ff02f81f65c10Hugo Hudson public PlaybackRunnable(MediaPlayerDataSource source) { 316b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson mInnerSource = source; 317b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson } 318b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson 319b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson @Override 320b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson public void run() { 321b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson try { 322b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson mInnerSource.playNative(); 323b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson } catch (IOException e) { 3240bd6ec5bc06b869131ee0facf38ff02f81f65c10Hugo Hudson Log.e(TAG, "error playing audio", e); 325b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson } 326b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson MediaPlayer.OnCompletionListener completionListener; 327b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson boolean skipThisCompletionReport; 328b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson synchronized (lock) { 329b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson completionListener = mCompletionListener; 330b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson skipThisCompletionReport = mSkipCompletionReport; 331b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson mPlaybackFinishedLatch.countDown(); 332b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson } 333b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson if (!skipThisCompletionReport && completionListener != null) { 334b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson completionListener.onCompletion(null); 335b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson } 336b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson } 337b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson } 338b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson 339b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson @Override 3408502b724a9fdc104e7b4a3aba1641e101b4c7be9Jay Shrauner public boolean isReadyToPlay() { 3418502b724a9fdc104e7b4a3aba1641e101b4c7be9Jay Shrauner synchronized (lock) { 3428502b724a9fdc104e7b4a3aba1641e101b4c7be9Jay Shrauner return !mHasBeenReleased && mHasDuration; 3438502b724a9fdc104e7b4a3aba1641e101b4c7be9Jay Shrauner } 3448502b724a9fdc104e7b4a3aba1641e101b4c7be9Jay Shrauner } 3458502b724a9fdc104e7b4a3aba1641e101b4c7be9Jay Shrauner 3468502b724a9fdc104e7b4a3aba1641e101b4c7be9Jay Shrauner @Override 347b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson public boolean isPlaying() { 348b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson synchronized (lock) { 3498502b724a9fdc104e7b4a3aba1641e101b4c7be9Jay Shrauner return isReadyToPlay() && mHasStartedPlayback && !hasPlaybackFinished(); 350b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson } 351b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson } 352b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson 353b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson @Override 354b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson public int getCurrentPosition() { 355b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson synchronized (lock) { 3569730f15ebbf4b64cd48e0777850e56cb516a9ed4Hugo Hudson check(!mHasBeenReleased, "has been released, reset before use"); 357b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson if (!mHasStartedPlayback) { 358b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson return 0; 359b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson } 360b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson if (!hasEngineBeenInitialized()) { 361b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson return 0; 362b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson } 363b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson if (!hasPlaybackFinished()) { 364b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson return VariableSpeedNative.getCurrentPosition(); 365b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson } 366b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson return mDuration; 367b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson } 368b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson } 369b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson 370b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson @Override 371b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson public void pause() { 372b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson synchronized (lock) { 373b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson check(!mHasBeenReleased, "has been released, reset before use"); 374b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson } 375b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson stopCurrentPlayback(); 376b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson } 377b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson 378b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson public void setVariableSpeed(float rate) { 3790bd6ec5bc06b869131ee0facf38ff02f81f65c10Hugo Hudson // TODO: are there situations in which the engine has been destroyed, so 380b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson // that this will segfault? 381b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson synchronized (lock) { 382b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson check(!mHasBeenReleased, "has been released, reset before use"); 3830bd6ec5bc06b869131ee0facf38ff02f81f65c10Hugo Hudson // TODO: This too is wrong, once we've started preparing the variable speed set 3840bd6ec5bc06b869131ee0facf38ff02f81f65c10Hugo Hudson // will not be enough. 385b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson if (mHasStartedPlayback) { 386b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson VariableSpeedNative.setVariableSpeed(rate); 387b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson } 388b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson mCurrentPlaybackRate = rate; 389b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson } 390b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson } 391b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson 392b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson private void check(boolean condition, String exception) { 393b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson if (!condition) { 394b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson throw new IllegalStateException(exception); 395b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson } 396b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson } 397b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson 398b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson private void checkNotNull(Object argument, String argumentName) { 399b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson if (argument == null) { 400b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson throw new IllegalArgumentException(argumentName + " must not be null"); 401b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson } 402b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson } 403dc442b4d99512bf7c41ee5ceae6c93a3c3568b57Flavio Lerda 404dc442b4d99512bf7c41ee5ceae6c93a3c3568b57Flavio Lerda @Override 405dc442b4d99512bf7c41ee5ceae6c93a3c3568b57Flavio Lerda public void setAudioStreamType(int audioStreamType) { 406dc442b4d99512bf7c41ee5ceae6c93a3c3568b57Flavio Lerda synchronized (lock) { 407dc442b4d99512bf7c41ee5ceae6c93a3c3568b57Flavio Lerda mAudioStreamType = audioStreamType; 408dc442b4d99512bf7c41ee5ceae6c93a3c3568b57Flavio Lerda } 409dc442b4d99512bf7c41ee5ceae6c93a3c3568b57Flavio Lerda } 410b83ad73794088498d6d38cd3b4fc9311f505d051Hugo Hudson} 411