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