summaryrefslogtreecommitdiffstats
path: root/variablespeed/src/com/android/ex/variablespeed/VariableSpeed.java
diff options
context:
space:
mode:
Diffstat (limited to 'variablespeed/src/com/android/ex/variablespeed/VariableSpeed.java')
-rw-r--r--variablespeed/src/com/android/ex/variablespeed/VariableSpeed.java410
1 files changed, 0 insertions, 410 deletions
diff --git a/variablespeed/src/com/android/ex/variablespeed/VariableSpeed.java b/variablespeed/src/com/android/ex/variablespeed/VariableSpeed.java
deleted file mode 100644
index e44a375..0000000
--- a/variablespeed/src/com/android/ex/variablespeed/VariableSpeed.java
+++ /dev/null
@@ -1,410 +0,0 @@
-/*
- * 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 com.android.ex.variablespeed;
-
-import com.google.common.base.Preconditions;
-
-import android.content.Context;
-import android.media.MediaPlayer;
-import android.net.Uri;
-import android.util.Log;
-
-import java.io.IOException;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.Executor;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-
-import javax.annotation.concurrent.GuardedBy;
-import javax.annotation.concurrent.ThreadSafe;
-
-/**
- * This class behaves in a similar fashion to the MediaPlayer, but by using
- * native code it is able to use variable-speed playback.
- * <p>
- * This class is thread-safe. It's not yet perfect though, see the unit tests
- * for details - there is insufficient testing for the concurrent logic. You are
- * probably best advised to use thread confinment until the unit tests are more
- * complete with regards to threading.
- * <p>
- * The easiest way to ensure that calls to this class are not made concurrently
- * (besides only ever accessing it from one thread) is to wrap it in a
- * {@link SingleThreadedMediaPlayerProxy}, designed just for this purpose.
- */
-@ThreadSafe
-public class VariableSpeed implements MediaPlayerProxy {
- private static final String TAG = "VariableSpeed";
-
- private final Executor mExecutor;
- private final Object lock = new Object();
- @GuardedBy("lock") private MediaPlayerDataSource mDataSource;
- @GuardedBy("lock") private boolean mIsPrepared;
- @GuardedBy("lock") private boolean mHasDuration;
- @GuardedBy("lock") private boolean mHasStartedPlayback;
- @GuardedBy("lock") private CountDownLatch mEngineInitializedLatch;
- @GuardedBy("lock") private CountDownLatch mPlaybackFinishedLatch;
- @GuardedBy("lock") private boolean mHasBeenReleased = true;
- @GuardedBy("lock") private boolean mIsReadyToReUse = true;
- @GuardedBy("lock") private boolean mSkipCompletionReport;
- @GuardedBy("lock") private int mStartPosition;
- @GuardedBy("lock") private float mCurrentPlaybackRate = 1.0f;
- @GuardedBy("lock") private int mDuration;
- @GuardedBy("lock") private MediaPlayer.OnCompletionListener mCompletionListener;
- @GuardedBy("lock") private int mAudioStreamType;
-
- private VariableSpeed(Executor executor) throws UnsupportedOperationException {
- Preconditions.checkNotNull(executor);
- mExecutor = executor;
- try {
- VariableSpeedNative.loadLibrary();
- } catch (UnsatisfiedLinkError e) {
- throw new UnsupportedOperationException("could not load library", e);
- } catch (SecurityException e) {
- throw new UnsupportedOperationException("could not load library", e);
- }
- reset();
- }
-
- public static MediaPlayerProxy createVariableSpeed(Executor executor)
- throws UnsupportedOperationException {
- return new SingleThreadedMediaPlayerProxy(new VariableSpeed(executor));
- }
-
- @Override
- public void setOnCompletionListener(MediaPlayer.OnCompletionListener listener) {
- synchronized (lock) {
- check(!mHasBeenReleased, "has been released, reset before use");
- mCompletionListener = listener;
- }
- }
-
- @Override
- public void setOnErrorListener(MediaPlayer.OnErrorListener listener) {
- synchronized (lock) {
- check(!mHasBeenReleased, "has been released, reset before use");
- // TODO: I haven't actually added any error listener code.
- }
- }
-
- @Override
- public void release() {
- synchronized (lock) {
- if (mHasBeenReleased) {
- return;
- }
- mHasBeenReleased = true;
- }
- stopCurrentPlayback();
- boolean requiresShutdown = false;
- synchronized (lock) {
- requiresShutdown = hasEngineBeenInitialized();
- }
- if (requiresShutdown) {
- VariableSpeedNative.shutdownEngine();
- }
- synchronized (lock) {
- mIsReadyToReUse = true;
- }
- }
-
- private boolean hasEngineBeenInitialized() {
- return mEngineInitializedLatch.getCount() <= 0;
- }
-
- private boolean hasPlaybackFinished() {
- return mPlaybackFinishedLatch.getCount() <= 0;
- }
-
- /**
- * Stops the current playback, returns once it has stopped.
- */
- private void stopCurrentPlayback() {
- boolean isPlaying;
- CountDownLatch engineInitializedLatch;
- CountDownLatch playbackFinishedLatch;
- synchronized (lock) {
- isPlaying = mHasStartedPlayback && !hasPlaybackFinished();
- engineInitializedLatch = mEngineInitializedLatch;
- playbackFinishedLatch = mPlaybackFinishedLatch;
- if (isPlaying) {
- mSkipCompletionReport = true;
- }
- }
- if (isPlaying) {
- waitForLatch(engineInitializedLatch);
- VariableSpeedNative.stopPlayback();
- waitForLatch(playbackFinishedLatch);
- }
- }
-
- private void waitForLatch(CountDownLatch latch) {
- try {
- boolean success = latch.await(1, TimeUnit.SECONDS);
- if (!success) {
- reportException(new TimeoutException("waited too long"));
- }
- } catch (InterruptedException e) {
- // Preserve the interrupt status, though this is unexpected.
- Thread.currentThread().interrupt();
- reportException(e);
- }
- }
-
- @Override
- public void setDataSource(Context context, Uri intentUri) {
- checkNotNull(context, "context");
- checkNotNull(intentUri, "intentUri");
- innerSetDataSource(new MediaPlayerDataSource(context, intentUri));
- }
-
- @Override
- public void setDataSource(String path) {
- checkNotNull(path, "path");
- innerSetDataSource(new MediaPlayerDataSource(path));
- }
-
- private void innerSetDataSource(MediaPlayerDataSource source) {
- checkNotNull(source, "source");
- synchronized (lock) {
- check(!mHasBeenReleased, "has been released, reset before use");
- check(mDataSource == null, "cannot setDataSource more than once");
- mDataSource = source;
- }
- }
-
- @Override
- public void reset() {
- boolean requiresRelease;
- synchronized (lock) {
- requiresRelease = !mHasBeenReleased;
- }
- if (requiresRelease) {
- release();
- }
- synchronized (lock) {
- check(mHasBeenReleased && mIsReadyToReUse, "to re-use, must call reset after release");
- mDataSource = null;
- mIsPrepared = false;
- mHasDuration = false;
- mHasStartedPlayback = false;
- mEngineInitializedLatch = new CountDownLatch(1);
- mPlaybackFinishedLatch = new CountDownLatch(1);
- mHasBeenReleased = false;
- mIsReadyToReUse = false;
- mSkipCompletionReport = false;
- mStartPosition = 0;
- mDuration = 0;
- }
- }
-
- @Override
- public void prepare() throws IOException {
- MediaPlayerDataSource dataSource;
- int audioStreamType;
- synchronized (lock) {
- check(!mHasBeenReleased, "has been released, reset before use");
- check(mDataSource != null, "must setDataSource before you prepare");
- check(!mIsPrepared, "cannot prepare more than once");
- mIsPrepared = true;
- dataSource = mDataSource;
- audioStreamType = mAudioStreamType;
- }
- // NYI This should become another executable that we can wait on.
- MediaPlayer mediaPlayer = new MediaPlayer();
- mediaPlayer.setAudioStreamType(audioStreamType);
- dataSource.setAsSourceFor(mediaPlayer);
- mediaPlayer.prepare();
- synchronized (lock) {
- check(!mHasDuration, "can't have duration, this is impossible");
- mHasDuration = true;
- mDuration = mediaPlayer.getDuration();
- }
- mediaPlayer.release();
- }
-
- @Override
- public int getDuration() {
- synchronized (lock) {
- check(!mHasBeenReleased, "has been released, reset before use");
- check(mHasDuration, "you haven't called prepare, can't get the duration");
- return mDuration;
- }
- }
-
- @Override
- public void seekTo(int startPosition) {
- boolean currentlyPlaying;
- MediaPlayerDataSource dataSource;
- synchronized (lock) {
- check(!mHasBeenReleased, "has been released, reset before use");
- check(mHasDuration, "you can't seek until you have prepared");
- currentlyPlaying = mHasStartedPlayback && !hasPlaybackFinished();
- mStartPosition = Math.min(startPosition, mDuration);
- dataSource = mDataSource;
- }
- if (currentlyPlaying) {
- stopAndStartPlayingAgain(dataSource);
- }
- }
-
- private void stopAndStartPlayingAgain(MediaPlayerDataSource source) {
- stopCurrentPlayback();
- reset();
- innerSetDataSource(source);
- try {
- prepare();
- } catch (IOException e) {
- reportException(e);
- return;
- }
- start();
- return;
- }
-
- private void reportException(Exception e) {
- Log.e(TAG, "playback error:", e);
- }
-
- @Override
- public void start() {
- MediaPlayerDataSource restartWithThisDataSource = null;
- synchronized (lock) {
- check(!mHasBeenReleased, "has been released, reset before use");
- check(mIsPrepared, "must have prepared before you can start");
- if (!mHasStartedPlayback) {
- // Playback has not started. Start it.
- mHasStartedPlayback = true;
- EngineParameters engineParameters = new EngineParameters.Builder()
- .initialRate(mCurrentPlaybackRate)
- .startPositionMillis(mStartPosition)
- .audioStreamType(mAudioStreamType)
- .build();
- VariableSpeedNative.initializeEngine(engineParameters);
- VariableSpeedNative.startPlayback();
- mEngineInitializedLatch.countDown();
- mExecutor.execute(new PlaybackRunnable(mDataSource));
- } else {
- // Playback has already started. Restart it, without holding the
- // lock.
- restartWithThisDataSource = mDataSource;
- }
- }
- if (restartWithThisDataSource != null) {
- stopAndStartPlayingAgain(restartWithThisDataSource);
- }
- }
-
- /** A Runnable capable of driving the native audio playback methods. */
- private final class PlaybackRunnable implements Runnable {
- private final MediaPlayerDataSource mInnerSource;
-
- public PlaybackRunnable(MediaPlayerDataSource source) {
- mInnerSource = source;
- }
-
- @Override
- public void run() {
- try {
- mInnerSource.playNative();
- } catch (IOException e) {
- Log.e(TAG, "error playing audio", e);
- }
- MediaPlayer.OnCompletionListener completionListener;
- boolean skipThisCompletionReport;
- synchronized (lock) {
- completionListener = mCompletionListener;
- skipThisCompletionReport = mSkipCompletionReport;
- mPlaybackFinishedLatch.countDown();
- }
- if (!skipThisCompletionReport && completionListener != null) {
- completionListener.onCompletion(null);
- }
- }
- }
-
- @Override
- public boolean isReadyToPlay() {
- synchronized (lock) {
- return !mHasBeenReleased && mHasDuration;
- }
- }
-
- @Override
- public boolean isPlaying() {
- synchronized (lock) {
- return isReadyToPlay() && mHasStartedPlayback && !hasPlaybackFinished();
- }
- }
-
- @Override
- public int getCurrentPosition() {
- synchronized (lock) {
- check(!mHasBeenReleased, "has been released, reset before use");
- if (!mHasStartedPlayback) {
- return 0;
- }
- if (!hasEngineBeenInitialized()) {
- return 0;
- }
- if (!hasPlaybackFinished()) {
- return VariableSpeedNative.getCurrentPosition();
- }
- return mDuration;
- }
- }
-
- @Override
- public void pause() {
- synchronized (lock) {
- check(!mHasBeenReleased, "has been released, reset before use");
- }
- stopCurrentPlayback();
- }
-
- public void setVariableSpeed(float rate) {
- // TODO: are there situations in which the engine has been destroyed, so
- // that this will segfault?
- synchronized (lock) {
- check(!mHasBeenReleased, "has been released, reset before use");
- // TODO: This too is wrong, once we've started preparing the variable speed set
- // will not be enough.
- if (mHasStartedPlayback) {
- VariableSpeedNative.setVariableSpeed(rate);
- }
- mCurrentPlaybackRate = rate;
- }
- }
-
- private void check(boolean condition, String exception) {
- if (!condition) {
- throw new IllegalStateException(exception);
- }
- }
-
- private void checkNotNull(Object argument, String argumentName) {
- if (argument == null) {
- throw new IllegalArgumentException(argumentName + " must not be null");
- }
- }
-
- @Override
- public void setAudioStreamType(int audioStreamType) {
- synchronized (lock) {
- mAudioStreamType = audioStreamType;
- }
- }
-}