Playback now handles any sample rate and channel combo.

Main stuff
- Delayed construction of audio player until after sample rate and
  channels have been read from the first callback.
- Removed all sample rate and channels from outward facing api.
- Remove pause playing and prefetch duration and sample rate hacky code.
- Fixes handling of any sample rate by removing switch statement.
- Fixes a HORRIBLE bug introduced in last cl where I was creating the
  callback context on the stack inside a method call, and using it a
  pointer to it long after the object had been erased from the stack.
  [worse yet: it still mostly worked fine!]

Other
- Fixes an obvious failure case of all tests - duh - I was calling setUp()
  before setting the executor properly, so they were giving NPEs.
- Correct calculation of totalDuration now done on first decode callback.
- This will let us remove MediaPlayer from prepare() in follow up.
- Initializing the engine outside of the PlaybackRunnable, is cleaner.
- Adds static methods for reading duration and position, again so that
  follow up can have accurate get current position rather than best guess.
- Buffers in use for decoding also not created until first decode callback.
- Introduces some wicked-cool OpenSL macro to log api calls, make the
  api calls and check the result value.

Bug: 5048252
Bug: 5048257
Change-Id: I60705fa6c6ab29a35740f22bef76450e8c1d25a2
diff --git a/variablespeed/jni/jni_entry.cc b/variablespeed/jni/jni_entry.cc
index f7b1f2e..0069956 100644
--- a/variablespeed/jni/jni_entry.cc
+++ b/variablespeed/jni/jni_entry.cc
@@ -28,10 +28,10 @@
 class MethodLog {
  public:
   explicit MethodLog(const char* name) : name_(name) {
-    LOGD("+ %s", name);
+    LOGV("+ %s", name);
   }
   virtual ~MethodLog() {
-    LOGD("- %s", name_);
+    LOGV("- %s", name_);
   }
 
  private:
@@ -75,13 +75,13 @@
   return AudioEngine::GetEngine()->GetTotalDuration();
 }
 
-JNI_METHOD(initializeEngine, void) (JNIEnv*, jclass, int channels,
-    int sampleRate, int targetFrames, float windowDuration,
+JNI_METHOD(initializeEngine, void) (JNIEnv*, jclass,
+    int targetFrames, float windowDuration,
     float windowOverlapDuration, size_t maxPlayBufferCount,
     float initialRate, size_t decodeInitialSize, size_t decodeMaxSize,
     size_t startPositionMillis) {
   MethodLog _("initializeEngine");
-  AudioEngine::SetEngine(new AudioEngine(channels, sampleRate, targetFrames,
+  AudioEngine::SetEngine(new AudioEngine(targetFrames,
       windowDuration, windowOverlapDuration, maxPlayBufferCount, initialRate,
       decodeInitialSize, decodeMaxSize, startPositionMillis));
 }
diff --git a/variablespeed/jni/variablespeed.cc b/variablespeed/jni/variablespeed.cc
index bf99c4d..12684f1 100644
--- a/variablespeed/jni/variablespeed.cc
+++ b/variablespeed/jni/variablespeed.cc
@@ -44,6 +44,11 @@
 const size_t kBufferSizeInBytes = 2 * kNumberOfSamplesPerBuffer;
 const size_t kSampleSizeInBytes = 4;
 
+// Keys used when extracting metadata from the decoder.
+// TODO: Remove these constants once they are part of OpenSLES_Android.h.
+const char* kKeyPcmFormatNumChannels = "AndroidPcmFormatNumChannels";
+const char* kKeyPcmFormatSamplesPerSec = "AndroidPcmFormatSamplesPerSec";
+
 // When calculating play buffer size before pushing to audio player.
 const size_t kNumberOfBytesPerInt16 = 2;
 
@@ -58,11 +63,15 @@
 
 // Structure used when we perform a decoding callback.
 typedef struct CallbackContext_ {
-    SLMetadataExtractionItf decoderMetadata;
-    // Pointer to local storage buffers for decoded audio data.
-    int8_t* pDataBase;
-    // Pointer to the current buffer within local storage.
-    int8_t* pData;
+  // Pointer to local storage buffers for decoded audio data.
+  int8_t* pDataBase;
+  // Pointer to the current buffer within local storage.
+  int8_t* pData;
+  // Used to read the sample rate and channels from the decoding stream during
+  // the first decoding callback.
+  SLMetadataExtractionItf decoderMetadata;
+  // The play interface used for reading duration.
+  SLPlayItf playItf;
 } CallbackContext;
 
 // Local storage for decoded audio data.
@@ -144,57 +153,76 @@
 }
 
 // ****************************************************************************
+// Macros for making working with OpenSL easier.
+
+// #define LOG_OPENSL_API_CALL(string) LOGV(string)
+#define LOG_OPENSL_API_CALL(string) false
+
+// The regular macro: log an api call, make the api call, check the result.
+#define OpenSL(obj, method, ...) \
+{ \
+  LOG_OPENSL_API_CALL("OpenSL " #method "(" #obj ", " #__VA_ARGS__ ")"); \
+  SLresult result = (*obj)->method(obj, __VA_ARGS__); \
+  CheckSLResult("OpenSL " #method "(" #obj ", " #__VA_ARGS__ ")", result); \
+}
+
+// Special case call for api call that has void return value, can't be checked.
+#define VoidOpenSL(obj, method) \
+{ \
+  LOG_OPENSL_API_CALL("OpenSL (void) " #method "(" #obj ")"); \
+  (*obj)->method(obj); \
+}
+
+// Special case for api call with checked result but takes no arguments.
+#define OpenSL0(obj, method) \
+{ \
+  LOG_OPENSL_API_CALL("OpenSL " #method "(" #obj ")"); \
+  SLresult result = (*obj)->method(obj); \
+  CheckSLResult("OpenSL " #method "(" #obj ")", result); \
+}
+
+// Special case for api call whose result we want to store, not check.
+// We have to encapsulate the two calls in braces, so that this expression
+// evaluates to the last expression not the first.
+#define ReturnOpenSL(obj, method, ...) \
+( \
+    LOG_OPENSL_API_CALL("OpenSL (int) " \
+        #method "(" #obj ", " #__VA_ARGS__ ")"), \
+    (*obj)->method(obj, __VA_ARGS__) \
+) \
+
+// ****************************************************************************
 // Static utility methods.
 
-static void PausePlaying(SLPlayItf playItf) {
-  SLresult result = (*playItf)->SetPlayState(playItf, SL_PLAYSTATE_PAUSED);
-  CheckSLResult("pause playing", result);
-}
-
-static void StartPlaying(SLPlayItf playItf) {
-  SLresult result = (*playItf)->SetPlayState(playItf, SL_PLAYSTATE_PLAYING);
-  CheckSLResult("start playing", result);
-}
-
-static void StopPlaying(SLPlayItf playItf) {
-  SLresult result = (*playItf)->SetPlayState(playItf, SL_PLAYSTATE_STOPPED);
-  CheckSLResult("stop playing", result);
-}
-
-static void ExtractMetadataFromDecoder(
-    SLMetadataExtractionItf decoderMetadata) {
+// Must be called with callbackLock_ held.
+static void ReadSampleRateAndChannelCount(CallbackContext *pContext,
+    SLuint32 *sampleRateOut, SLuint32 *channelsOut) {
+  SLMetadataExtractionItf decoderMetadata = pContext->decoderMetadata;
   SLuint32 itemCount;
-  SLresult result = (*decoderMetadata)->GetItemCount(
-      decoderMetadata, &itemCount);
-  CheckSLResult("getting item count", result);
+  OpenSL(decoderMetadata, GetItemCount, &itemCount);
   SLuint32 i, keySize, valueSize;
   SLMetadataInfo *keyInfo, *value;
-  for (i = 0; i < itemCount ; ++i) {
-    keyInfo = NULL;
-    keySize = 0;
-    value = NULL;
-    valueSize = 0;
-    result = (*decoderMetadata)->GetKeySize(decoderMetadata, i, &keySize);
-    CheckSLResult("get key size", result);
+  for (i = 0; i < itemCount; ++i) {
+    keyInfo = value = NULL;
+    keySize = valueSize = 0;
+    OpenSL(decoderMetadata, GetKeySize, i, &keySize);
     keyInfo = static_cast<SLMetadataInfo*>(malloc(keySize));
     if (keyInfo) {
-      result = (*decoderMetadata)->GetKey(
-          decoderMetadata, i, keySize, keyInfo);
-      CheckSLResult("get key", result);
+      OpenSL(decoderMetadata, GetKey, i, keySize, keyInfo);
       if (keyInfo->encoding == SL_CHARACTERENCODING_ASCII
           || keyInfo->encoding == SL_CHARACTERENCODING_UTF8) {
-        result = (*decoderMetadata)->GetValueSize(
-            decoderMetadata, i, &valueSize);
-        CheckSLResult("get value size", result);
+        OpenSL(decoderMetadata, GetValueSize, i, &valueSize);
         value = static_cast<SLMetadataInfo*>(malloc(valueSize));
         if (value) {
-          result = (*decoderMetadata)->GetValue(
-              decoderMetadata, i, valueSize, value);
-          CheckSLResult("get value", result);
-          if (value->encoding == SL_CHARACTERENCODING_BINARY) {
-            LOGD("key[%d] size=%d, name=%s value size=%d value=%d",
-                i, keyInfo->size, keyInfo->data, value->size,
-                *(reinterpret_cast<SLuint32*>(value->data)));
+          OpenSL(decoderMetadata, GetValue, i, valueSize, value);
+          if (strcmp((char*) keyInfo->data, kKeyPcmFormatSamplesPerSec) == 0) {
+            SLuint32 sampleRate = *(reinterpret_cast<SLuint32*>(value->data));
+            LOGD("sample Rate: %d", sampleRate);
+            *sampleRateOut = sampleRate;
+          } else if (strcmp((char*) keyInfo->data, kKeyPcmFormatNumChannels) == 0) {
+            SLuint32 channels = *(reinterpret_cast<SLuint32*>(value->data));
+            LOGD("channels: %d", channels);
+            *channelsOut = channels;
           }
           free(value);
         }
@@ -204,35 +232,18 @@
   }
 }
 
-static void SeekToPosition(SLSeekItf seekItf, size_t startPositionMillis) {
-  SLresult result = (*seekItf)->SetPosition(
-      seekItf, startPositionMillis, SL_SEEKMODE_ACCURATE);
-  CheckSLResult("seek to position", result);
-}
-
+// Must be called with callbackLock_ held.
 static void RegisterCallbackContextAndAddEnqueueBuffersToDecoder(
-    SLAndroidSimpleBufferQueueItf decoderQueue,
-    SLMetadataExtractionItf decoderMetadata, android::Mutex &callbackLock,
-    CallbackContext* context) {
-  android::Mutex::Autolock autoLock(callbackLock);
-  // Initialize the callback structure, used during the decoding.
-  // Then register a callback on the decoder queue, so that we will be called
+    SLAndroidSimpleBufferQueueItf decoderQueue, CallbackContext* context) {
+  // Register a callback on the decoder queue, so that we will be called
   // throughout the decoding process (and can then extract the decoded audio
   // for the next bit of the pipeline).
-  context->decoderMetadata = decoderMetadata;
-  context->pDataBase = pcmData;
-  context->pData = pcmData;
-
-  SLresult result = (*decoderQueue)->RegisterCallback(
-      decoderQueue, DecodingBufferQueueCb, context);
-  CheckSLResult("decode callback", result);
+  OpenSL(decoderQueue, RegisterCallback, DecodingBufferQueueCb, context);
 
   // Enqueue buffers to map the region of memory allocated to store the
   // decoded data.
   for (size_t i = 0; i < kNumberOfBuffersInQueue; i++) {
-    SLresult result = (*decoderQueue)->Enqueue(
-        decoderQueue, context->pData, kBufferSizeInBytes);
-    CheckSLResult("enqueue something", result);
+    OpenSL(decoderQueue, Enqueue, context->pData, kBufferSizeInBytes);
     context->pData += kBufferSizeInBytes;
   }
   context->pData = context->pDataBase;
@@ -241,23 +252,20 @@
 // ****************************************************************************
 // Constructor and Destructor.
 
-AudioEngine::AudioEngine(size_t channels, size_t sampleRate,
-    size_t targetFrames, float windowDuration, float windowOverlapDuration,
-    size_t maxPlayBufferCount, float initialRate, size_t decodeInitialSize,
-    size_t decodeMaxSize, size_t startPositionMillis)
+AudioEngine::AudioEngine(size_t targetFrames, float windowDuration,
+    float windowOverlapDuration, size_t maxPlayBufferCount, float initialRate,
+    size_t decodeInitialSize, size_t decodeMaxSize, size_t startPositionMillis)
     : decodeBuffer_(decodeInitialSize, decodeMaxSize),
       playingBuffers_(), freeBuffers_(), timeScaler_(NULL),
       floatBuffer_(NULL), injectBuffer_(NULL),
-      channels_(channels), sampleRate_(sampleRate),
+      mSampleRate(0), mChannels(0),
       targetFrames_(targetFrames),
       windowDuration_(windowDuration),
       windowOverlapDuration_(windowOverlapDuration),
       maxPlayBufferCount_(maxPlayBufferCount), initialRate_(initialRate),
       startPositionMillis_(startPositionMillis),
-      totalDurationMs_(0), startRequested_(false),
+      totalDurationMs_(0), decoderCurrentPosition_(0), startRequested_(false),
       stopRequested_(false), finishedDecoding_(false) {
-  floatBuffer_ = new float[targetFrames_ * channels_];
-  injectBuffer_ = new float[targetFrames_ * channels_];
 }
 
 AudioEngine::~AudioEngine() {
@@ -288,6 +296,8 @@
 // Regular AudioEngine class methods.
 
 void AudioEngine::SetVariableSpeed(float speed) {
+  // TODO: Add test, prove that this doesn't crash if called before playing.
+  // TODO: Mutex for shared time scaler accesses.
   GetTimeScaler()->set_speed(speed);
 }
 
@@ -314,8 +324,11 @@
 int AudioEngine::GetCurrentPosition() {
   android::Mutex::Autolock autoLock(decodeBufferLock_);
   double result = decodeBuffer_.GetTotalAdvancedCount();
+  // TODO: This is horrible, but should be removed soon once the outstanding
+  // issue with get current position on decoder is fixed.
+  android::Mutex::Autolock autoLock2(callbackLock_);
   return static_cast<int>(
-      (result * 1000) / sampleRate_ / channels_ + startPositionMillis_);
+      (result * 1000) / mSampleRate / mChannels + startPositionMillis_);
 }
 
 int AudioEngine::GetTotalDuration() {
@@ -325,44 +338,23 @@
 
 video_editing::SolaTimeScaler* AudioEngine::GetTimeScaler() {
   if (timeScaler_ == NULL) {
+    CHECK(HasSampleRateAndChannels());
+    android::Mutex::Autolock autoLock(callbackLock_);
     timeScaler_ = new video_editing::SolaTimeScaler();
-    timeScaler_->Init(sampleRate_, channels_, initialRate_, windowDuration_,
+    timeScaler_->Init(mSampleRate, mChannels, initialRate_, windowDuration_,
         windowOverlapDuration_);
   }
   return timeScaler_;
 }
 
-void AudioEngine::PrefetchDurationSampleRateAndChannels(
-    SLPlayItf playItf, SLPrefetchStatusItf prefetchItf) {
-  // Set play state to pause, to begin the prefetching.
-  PausePlaying(playItf);
-
-  // Wait until the data has been prefetched.
-  {
-    SLuint32 prefetchStatus = SL_PREFETCHSTATUS_UNDERFLOW;
-    android::Mutex::Autolock autoLock(prefetchLock_);
-    while (prefetchStatus != SL_PREFETCHSTATUS_SUFFICIENTDATA) {
-      LOGI("waiting for condition");
-      // prefetchCondition_.waitRelative(prefetchLock, 1000 * 1000 * 10);
-      usleep(10 * 1000);
-      LOGI("getting the value");
-      (*prefetchItf)->GetPrefetchStatus(prefetchItf, &prefetchStatus);
-    }
-    LOGI("done with wait");
-  }
-
-  SLmillisecond durationInMsec = SL_TIME_UNKNOWN;
-  SLresult result = (*playItf)->GetDuration(playItf, &durationInMsec);
-  CheckSLResult("getting duration", result);
-  CHECK(durationInMsec != SL_TIME_UNKNOWN);
-  LOGD("duration: %d", static_cast<int>(durationInMsec));
-  android::Mutex::Autolock autoLock(lock_);
-  totalDurationMs_ = durationInMsec;
-}
-
 bool AudioEngine::EnqueueNextBufferOfAudio(
     SLAndroidSimpleBufferQueueItf audioPlayerQueue) {
-  size_t frameSizeInBytes = kSampleSizeInBytes * channels_;
+  size_t channels;
+  {
+    android::Mutex::Autolock autoLock(callbackLock_);
+    channels = mChannels;
+  }
+  size_t frameSizeInBytes = kSampleSizeInBytes * channels;
   size_t frameCount = 0;
   while (frameCount < targetFrames_) {
     size_t framesLeft = targetFrames_ - frameCount;
@@ -370,9 +362,9 @@
     if (GetTimeScaler()->available() > 0) {
       size_t retrieveCount = min(GetTimeScaler()->available(), framesLeft);
       int count = GetTimeScaler()->RetrieveSamples(
-          floatBuffer_ + frameCount * channels_, retrieveCount);
+          floatBuffer_ + frameCount * channels, retrieveCount);
       if (count <= 0) {
-        LOGD("ERROR: Count was %d", count);
+        LOGD("error: count was %d", count);
         break;
       }
       frameCount += count;
@@ -389,15 +381,15 @@
       // No more frames left to inject.
       break;
     }
-    for (size_t i = 0; i < framesToInject * channels_ ; ++i) {
+    for (size_t i = 0; i < framesToInject * channels; ++i) {
       injectBuffer_[i] = decodeBuffer_.GetAtIndex(i);
     }
     int count = GetTimeScaler()->InjectSamples(injectBuffer_, framesToInject);
     if (count <= 0) {
-      LOGD("ERROR: Count was %d", count);
+      LOGD("error: count was %d", count);
       break;
     }
-    decodeBuffer_.AdvanceHeadPointerShorts(count * channels_);
+    decodeBuffer_.AdvanceHeadPointerShorts(count * channels);
   }
   if (frameCount <= 0) {
     // We must have finished playback.
@@ -418,19 +410,18 @@
       freeBuffers_.pop();
     } else {
       // Otherwise allocate a new one.
-      playBuffer = new int16[targetFrames_ * channels_];
+      playBuffer = new int16[targetFrames_ * channels];
     }
   }
 
   // Try to play the buffer.
-  for (size_t i = 0; i < frameCount * channels_ ; ++i) {
+  for (size_t i = 0; i < frameCount * channels; ++i) {
     playBuffer[i] = floatBuffer_[i];
   }
   size_t sizeOfPlayBufferInBytes =
-      frameCount * channels_ * kNumberOfBytesPerInt16;
-  SLresult result = (*audioPlayerQueue)->Enqueue(audioPlayerQueue, playBuffer,
+      frameCount * channels * kNumberOfBytesPerInt16;
+  SLresult result = ReturnOpenSL(audioPlayerQueue, Enqueue, playBuffer,
       sizeOfPlayBufferInBytes);
-  CheckSLResult("enqueue prebuilt audio", result);
   if (result == SL_RESULT_SUCCESS) {
     android::Mutex::Autolock autoLock(playBufferLock_);
     playingBuffers_.push(playBuffer);
@@ -481,70 +472,71 @@
   decodeBuffer_.Clear();
 }
 
+static size_t ReadDuration(SLPlayItf playItf) {
+  SLmillisecond durationInMsec = SL_TIME_UNKNOWN;
+  OpenSL(playItf, GetDuration, &durationInMsec);
+  if (durationInMsec == SL_TIME_UNKNOWN) {
+    LOGE("can't get duration");
+    return 0;
+  }
+  LOGD("duration: %d", static_cast<int>(durationInMsec));
+  return durationInMsec;
+}
+
+static size_t ReadPosition(SLPlayItf playItf) {
+  SLmillisecond positionInMsec = SL_TIME_UNKNOWN;
+  OpenSL(playItf, GetPosition, &positionInMsec);
+  if (positionInMsec == SL_TIME_UNKNOWN) {
+    LOGE("can't get position");
+    return 0;
+  }
+  LOGW("decoder position: %d", static_cast<int>(positionInMsec));
+  return positionInMsec;
+}
+
 static void CreateAndRealizeEngine(SLObjectItf &engine,
     SLEngineItf &engineInterface) {
   SLEngineOption EngineOption[] = { {
       SL_ENGINEOPTION_THREADSAFE, SL_BOOLEAN_TRUE } };
   SLresult result = slCreateEngine(&engine, 1, EngineOption, 0, NULL, NULL);
   CheckSLResult("create engine", result);
-  result = (*engine)->Realize(engine, SL_BOOLEAN_FALSE);
-  CheckSLResult("realise engine", result);
-  result = (*engine)->GetInterface(engine, SL_IID_ENGINE, &engineInterface);
-  CheckSLResult("get interface", result);
+  OpenSL(engine, Realize, SL_BOOLEAN_FALSE);
+  OpenSL(engine, GetInterface, SL_IID_ENGINE, &engineInterface);
 }
 
-static void CreateAndRealizeOutputMix(SLEngineItf &engineInterface,
-    SLObjectItf &outputMix) {
-  SLresult result;
-  // Create the output mix for playing.
-  result = (*engineInterface)->CreateOutputMix(
-      engineInterface, &outputMix, 0, NULL, NULL);
-  CheckSLResult("create output mix", result);
-  result = (*outputMix)->Realize(outputMix, SL_BOOLEAN_FALSE);
-  CheckSLResult("realize", result);
+SLuint32 AudioEngine::GetSLSampleRate() {
+  android::Mutex::Autolock autoLock(callbackLock_);
+  return mSampleRate * 1000;
 }
 
-static void CreateAndRealizeAudioPlayer(size_t sampleRate, size_t channels,
-    SLObjectItf &outputMix, SLObjectItf &audioPlayer,
-    SLEngineItf &engineInterface) {
-  SLresult result;
-  SLuint32 slSampleRate;
-  SLuint32 slOutputChannels;
-  switch (sampleRate) {
-    case 44100:
-      slSampleRate = SL_SAMPLINGRATE_44_1;
-      break;
-    case 8000:
-      slSampleRate = SL_SAMPLINGRATE_8;
-      break;
-    case 11025:
-      slSampleRate = SL_SAMPLINGRATE_11_025;
-      break;
-    default:
-      LOGE("unknown sample rate, using SL_SAMPLINGRATE_44_1");
-      slSampleRate = SL_SAMPLINGRATE_44_1;
-      break;
-  }
-  switch (channels) {
+SLuint32 AudioEngine::GetSLChannels() {
+  android::Mutex::Autolock autoLock(callbackLock_);
+  switch (mChannels) {
     case 2:
-      slOutputChannels = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
-      break;
+      return SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
     case 1:
-      slOutputChannels = SL_SPEAKER_FRONT_LEFT;
-      break;
+      return SL_SPEAKER_FRONT_CENTER;
     default:
-      LOGE("unknown channels, using 2");
-      slOutputChannels = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
-      break;
+      LOGE("unknown channels %d, using 2", mChannels);
+      return SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
   }
+}
 
+SLuint32 AudioEngine::GetChannelCount() {
+  android::Mutex::Autolock autoLock(callbackLock_);
+  return mChannels;
+}
+
+static void CreateAndRealizeAudioPlayer(SLuint32 slSampleRate,
+    size_t channelCount, SLuint32 slChannels, SLObjectItf &outputMix,
+    SLObjectItf &audioPlayer, SLEngineItf &engineInterface) {
   // Define the source and sink for the audio player: comes from a buffer queue
   // and goes to the output mix.
   SLDataLocator_AndroidSimpleBufferQueue loc_bufq = {
       SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2 };
-  SLDataFormat_PCM format_pcm = {SL_DATAFORMAT_PCM, channels, slSampleRate,
+  SLDataFormat_PCM format_pcm = {SL_DATAFORMAT_PCM, channelCount, slSampleRate,
       SL_PCMSAMPLEFORMAT_FIXED_16, SL_PCMSAMPLEFORMAT_FIXED_16,
-      slOutputChannels, SL_BYTEORDER_LITTLEENDIAN};
+      slChannels, SL_BYTEORDER_LITTLEENDIAN};
   SLDataSource playingSrc = {&loc_bufq, &format_pcm};
   SLDataLocator_OutputMix loc_outmix = {SL_DATALOCATOR_OUTPUTMIX, outputMix};
   SLDataSink audioSnk = {&loc_outmix, NULL};
@@ -555,36 +547,19 @@
   const SLInterfaceID iids[playerInterfaceCount] = {
       SL_IID_ANDROIDSIMPLEBUFFERQUEUE };
   const SLboolean reqs[playerInterfaceCount] = { SL_BOOLEAN_TRUE };
-  result = (*engineInterface)->CreateAudioPlayer(engineInterface, &audioPlayer,
-      &playingSrc, &audioSnk, playerInterfaceCount, iids, reqs);
-  CheckSLResult("create audio player", result);
-  result = (*audioPlayer)->Realize(audioPlayer, SL_BOOLEAN_FALSE);
-  CheckSLResult("realize buffer queue", result);
+  OpenSL(engineInterface, CreateAudioPlayer, &audioPlayer, &playingSrc,
+      &audioSnk, playerInterfaceCount, iids, reqs);
+  OpenSL(audioPlayer, Realize, SL_BOOLEAN_FALSE);
 }
 
-static void GetAudioPlayInterfacesAndRegisterCallback(SLObjectItf &audioPlayer,
-    SLPlayItf &audioPlayerPlay,
-    SLAndroidSimpleBufferQueueItf &audioPlayerQueue) {
-  SLresult result;
-  // Get the play interface from the player, as well as the buffer queue
-  // interface from its source.
-  // Register for callbacks during play.
-  result = (*audioPlayer)->GetInterface(
-      audioPlayer, SL_IID_PLAY, &audioPlayerPlay);
-  CheckSLResult("get interface", result);
-  result = (*audioPlayer)->GetInterface(audioPlayer,
-      SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &audioPlayerQueue);
-  CheckSLResult("get interface again", result);
-  result = (*audioPlayerQueue)->RegisterCallback(
-      audioPlayerQueue, PlayingBufferQueueCb, NULL);
-  CheckSLResult("register callback", result);
+bool AudioEngine::HasSampleRateAndChannels() {
+  android::Mutex::Autolock autoLock(callbackLock_);
+  return mChannels != 0 && mSampleRate != 0;
 }
 
 bool AudioEngine::PlayFromThisSource(const SLDataSource& audioSrc) {
   ClearDecodeBuffer();
 
-  SLresult result;
-
   SLObjectItf engine;
   SLEngineItf engineInterface;
   CreateAndRealizeEngine(engine, engineInterface);
@@ -609,61 +584,52 @@
   const SLboolean decodePlayerRequired[decoderInterfaceCount] = {
       SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE };
   SLDataSource sourceCopy(audioSrc);
-  result = (*engineInterface)->CreateAudioPlayer(engineInterface, &decoder,
-      &sourceCopy, &decDest, decoderInterfaceCount, decodePlayerInterfaces,
-      decodePlayerRequired);
-  CheckSLResult("create audio player", result);
-  result = (*decoder)->Realize(decoder, SL_BOOLEAN_FALSE);
-  CheckSLResult("realize in sync mode", result);
+  OpenSL(engineInterface, CreateAudioPlayer, &decoder, &sourceCopy, &decDest,
+      decoderInterfaceCount, decodePlayerInterfaces, decodePlayerRequired);
+  OpenSL(decoder, Realize, SL_BOOLEAN_FALSE);
 
   // Get the play interface from the decoder, and register event callbacks.
   // Get the buffer queue, prefetch and seek interfaces.
   SLPlayItf decoderPlay = NULL;
-  result = (*decoder)->GetInterface(decoder, SL_IID_PLAY, &decoderPlay);
-  CheckSLResult("get play interface, implicit", result);
-  result = (*decoderPlay)->SetCallbackEventsMask(
-      decoderPlay, SL_PLAYEVENT_HEADATEND);
-  CheckSLResult("set the event mask", result);
-  result = (*decoderPlay)->RegisterCallback(
-      decoderPlay, DecodingEventCb, NULL);
-  CheckSLResult("register decoding event callback", result);
-  SLAndroidSimpleBufferQueueItf decoderQueue;
-  result = (*decoder)->GetInterface(
-      decoder, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &decoderQueue);
-  CheckSLResult("get decoder buffer queue", result);
-  SLPrefetchStatusItf decoderPrefetch;
-  result = (*decoder)->GetInterface(
-      decoder, SL_IID_PREFETCHSTATUS, &decoderPrefetch);
-  CheckSLResult("get prefetch status interface", result);
-  SLSeekItf decoderSeek;
-  result = (*decoder)->GetInterface(decoder, SL_IID_SEEK, &decoderSeek);
-  CheckSLResult("get seek interface", result);
+  SLAndroidSimpleBufferQueueItf decoderQueue = NULL;
+  SLPrefetchStatusItf decoderPrefetch = NULL;
+  SLSeekItf decoderSeek = NULL;
+  SLMetadataExtractionItf decoderMetadata = NULL;
+  OpenSL(decoder, GetInterface, SL_IID_PLAY, &decoderPlay);
+  OpenSL(decoderPlay, SetCallbackEventsMask, SL_PLAYEVENT_HEADATEND);
+  OpenSL(decoderPlay, RegisterCallback, DecodingEventCb, NULL);
+  OpenSL(decoder, GetInterface, SL_IID_PREFETCHSTATUS, &decoderPrefetch);
+  OpenSL(decoder, GetInterface, SL_IID_SEEK, &decoderSeek);
+  OpenSL(decoder, GetInterface, SL_IID_METADATAEXTRACTION, &decoderMetadata);
+  OpenSL(decoder, GetInterface, SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
+      &decoderQueue);
 
-  // Get the metadata interface from the decoder.
-  SLMetadataExtractionItf decoderMetadata;
-  result = (*decoder)->GetInterface(decoder,
-      SL_IID_METADATAEXTRACTION, &decoderMetadata);
-  CheckSLResult("getting metadata interface", result);
-
+  // Initialize the callback structure, used during the decoding.
   CallbackContext callbackContext;
-  RegisterCallbackContextAndAddEnqueueBuffersToDecoder(
-      decoderQueue, decoderMetadata, callbackLock_, &callbackContext);
+  {
+    android::Mutex::Autolock autoLock(callbackLock_);
+    callbackContext.pDataBase = pcmData;
+    callbackContext.pData = pcmData;
+    callbackContext.decoderMetadata = decoderMetadata;
+    callbackContext.playItf = decoderPlay;
+    RegisterCallbackContextAndAddEnqueueBuffersToDecoder(
+        decoderQueue, &callbackContext);
+  }
 
   // Initialize the callback for prefetch errors, if we can't open the
   // resource to decode.
-  result = (*decoderPrefetch)->SetCallbackEventsMask(
-      decoderPrefetch, kPrefetchErrorCandidate);
-  CheckSLResult("set prefetch callback mask", result);
-  result = (*decoderPrefetch)->RegisterCallback(
-      decoderPrefetch, PrefetchEventCb, &decoderPrefetch);
-  CheckSLResult("set prefetch callback", result);
+  OpenSL(decoderPrefetch, SetCallbackEventsMask, kPrefetchErrorCandidate);
+  OpenSL(decoderPrefetch, RegisterCallback, PrefetchEventCb, &decoderPrefetch);
 
-  SeekToPosition(decoderSeek, startPositionMillis_);
+  // Seek to the start position.
+  OpenSL(decoderSeek, SetPosition, startPositionMillis_, SL_SEEKMODE_ACCURATE);
 
-  PrefetchDurationSampleRateAndChannels(decoderPlay, decoderPrefetch);
+  // Start decoding immediately.
+  OpenSL(decoderPlay, SetPlayState, SL_PLAYSTATE_PLAYING);
 
-  StartPlaying(decoderPlay);
-
+  // These variables hold the audio player and its output.
+  // They will only be constructed once the decoder has invoked the callback,
+  // and given us the correct sample rate, number of channels and duration.
   SLObjectItf outputMix = NULL;
   SLObjectItf audioPlayer = NULL;
   SLPlayItf audioPlayerPlay = NULL;
@@ -673,14 +639,21 @@
   // out of the decoder, feed it through the time scaler.
   // As it comes out of the time scaler, feed it into the audio player.
   while (!Finished()) {
-    if (GetWasStartRequested()) {
-      CreateAndRealizeOutputMix(engineInterface, outputMix);
-      CreateAndRealizeAudioPlayer(sampleRate_, channels_, outputMix,
-          audioPlayer, engineInterface);
-      GetAudioPlayInterfacesAndRegisterCallback(audioPlayer, audioPlayerPlay,
-          audioPlayerQueue);
+    if (GetWasStartRequested() && HasSampleRateAndChannels()) {
+      // Build the audio player.
+      // TODO: What happens if I maliciously call start lots of times?
+      floatBuffer_ = new float[targetFrames_ * mChannels];
+      injectBuffer_ = new float[targetFrames_ * mChannels];
+      OpenSL(engineInterface, CreateOutputMix, &outputMix, 0, NULL, NULL);
+      OpenSL(outputMix, Realize, SL_BOOLEAN_FALSE);
+      CreateAndRealizeAudioPlayer(GetSLSampleRate(), GetChannelCount(),
+          GetSLChannels(), outputMix, audioPlayer, engineInterface);
+      OpenSL(audioPlayer, GetInterface, SL_IID_PLAY, &audioPlayerPlay);
+      OpenSL(audioPlayer, GetInterface, SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
+          &audioPlayerQueue);
+      OpenSL(audioPlayerQueue, RegisterCallback, PlayingBufferQueueCb, NULL);
       ClearRequestStart();
-      StartPlaying(audioPlayerPlay);
+      OpenSL(audioPlayerPlay, SetPlayState, SL_PLAYSTATE_PLAYING);
     }
     EnqueueMoreAudioIfNecessary(audioPlayerQueue);
     usleep(kSleepTimeMicros);
@@ -688,14 +661,12 @@
 
   // Delete the audio player and output mix, iff they have been created.
   if (audioPlayer != NULL) {
-    StopPlaying(audioPlayerPlay);
-    result = (*audioPlayerQueue)->Clear(audioPlayerQueue);
-    CheckSLResult("clear audio player queue", result);
-    result = (*audioPlayerQueue)->RegisterCallback(audioPlayerQueue, NULL, NULL);
-    CheckSLResult("clear callback", result);
-    (*audioPlayer)->AbortAsyncOperation(audioPlayer);
-    (*audioPlayer)->Destroy(audioPlayer);
-    (*outputMix)->Destroy(outputMix);
+    OpenSL(audioPlayerPlay, SetPlayState, SL_PLAYSTATE_STOPPED);
+    OpenSL0(audioPlayerQueue, Clear);
+    OpenSL(audioPlayerQueue, RegisterCallback, NULL, NULL);
+    VoidOpenSL(audioPlayer, AbortAsyncOperation);
+    VoidOpenSL(audioPlayer, Destroy);
+    VoidOpenSL(outputMix, Destroy);
     audioPlayer = NULL;
     audioPlayerPlay = NULL;
     audioPlayerQueue = NULL;
@@ -703,27 +674,23 @@
   }
 
   // Delete the decoder.
-  StopPlaying(decoderPlay);
-  result = (*decoderPrefetch)->RegisterCallback(decoderPrefetch, NULL, NULL);
-  CheckSLResult("clearing prefetch error callback", result);
+  OpenSL(decoderPlay, SetPlayState, SL_PLAYSTATE_STOPPED);
+  OpenSL(decoderPrefetch, RegisterCallback, NULL, NULL);
   // This is returning slresult 13 if I do no playback.
   // Repro is to comment out all before this line, and all after enqueueing
   // my buffers.
-  // result = (*decoderQueue)->Clear(decoderQueue);
-  // CheckSLResult("clearing decode buffer queue", result);
-  result = (*decoderQueue)->RegisterCallback(decoderQueue, NULL, NULL);
-  CheckSLResult("clearing decode callback", result);
+  // OpenSL0(decoderQueue, Clear);
+  OpenSL(decoderQueue, RegisterCallback, NULL, NULL);
   decoderSeek = NULL;
   decoderPrefetch = NULL;
   decoderQueue = NULL;
-  result = (*decoderPlay)->RegisterCallback(decoderPlay, NULL, NULL);
-  CheckSLResult("clear decoding event callback", result);
-  (*decoder)->AbortAsyncOperation(decoder);
+  OpenSL(decoderPlay, RegisterCallback, NULL, NULL);
+  VoidOpenSL(decoder, AbortAsyncOperation);
+  VoidOpenSL(decoder, Destroy);
   decoderPlay = NULL;
-  (*decoder)->Destroy(decoder);
 
   // Delete the engine.
-  (*engine)->Destroy(engine);
+  VoidOpenSL(engine, Destroy);
   engineInterface = NULL;
 
   return true;
@@ -752,7 +719,8 @@
 void AudioEngine::EnqueueMoreAudioIfNecessary(
     SLAndroidSimpleBufferQueueItf audioPlayerQueue) {
   bool keepEnqueueing = true;
-  while (!GetWasStopRequested() &&
+  while (audioPlayerQueue != NULL &&
+         !GetWasStopRequested() &&
          !IsDecodeBufferEmpty() &&
          !GetHasReachedPlayingBuffersLimit() &&
          keepEnqueueing) {
@@ -781,15 +749,13 @@
     SLPrefetchStatusItf caller, SLuint32 event) {
   // If there was a problem during decoding, then signal the end.
   SLpermille level = 0;
-  SLresult result = (*caller)->GetFillLevel(caller, &level);
-  CheckSLResult("get fill level", result);
   SLuint32 status;
-  result = (*caller)->GetPrefetchStatus(caller, &status);
-  CheckSLResult("get prefetch status", result);
+  OpenSL(caller, GetFillLevel, &level);
+  OpenSL(caller, GetPrefetchStatus, &status);
   if ((kPrefetchErrorCandidate == (event & kPrefetchErrorCandidate)) &&
       (level == 0) &&
       (status == SL_PREFETCHSTATUS_UNDERFLOW)) {
-    LOGI("PrefetchEventCallback error while prefetching data");
+    LOGI("prefetcheventcallback error while prefetching data");
     SetEndOfDecoderReached();
   }
   if (SL_PREFETCHSTATUS_SUFFICIENTDATA == event) {
@@ -814,9 +780,19 @@
     decodeBuffer_.AddData(pCntxt->pDataBase, kBufferSizeInBytes);
   }
 
-  // TODO: This call must be added back in to fix the bug relating to using
-  // the correct sample rate and channels.  I will do this in the follow-up.
-  // ExtractMetadataFromDecoder(pCntxt->decoderMetadata);
+  if (!HasSampleRateAndChannels()) {
+    android::Mutex::Autolock autoLock(callbackLock_);
+    ReadSampleRateAndChannelCount(pCntxt, &mSampleRate, &mChannels);
+  }
+
+  {
+    android::Mutex::Autolock autoLock(lock_);
+    if (totalDurationMs_ == 0) {
+      totalDurationMs_ = ReadDuration(pCntxt->playItf);
+    }
+    // TODO: This isn't working, it always reports zero.
+    // ReadPosition(pCntxt->playItf);
+  }
 
   // Increase data pointer by buffer size
   pCntxt->pData += kBufferSizeInBytes;
@@ -825,9 +801,7 @@
     pCntxt->pData = pCntxt->pDataBase;
   }
 
-  SLresult result = (*queueItf)->Enqueue(
-      queueItf, pCntxt->pDataBase, kBufferSizeInBytes);
-  CheckSLResult("enqueue something else", result);
+  OpenSL(queueItf, Enqueue, pCntxt->pDataBase, kBufferSizeInBytes);
 
   // If we get too much data into the decoder,
   // sleep until the playback catches up.
diff --git a/variablespeed/jni/variablespeed.h b/variablespeed/jni/variablespeed.h
index 9da7df1..05b27a5 100644
--- a/variablespeed/jni/variablespeed.h
+++ b/variablespeed/jni/variablespeed.h
@@ -42,10 +42,10 @@
 // native methods.
 class AudioEngine {
  public:
-  AudioEngine(size_t channels, size_t sampleRate, size_t targetFrames,
-      float windowDuration, float windowOverlapDuration,
-      size_t maxPlayBufferCount, float initialRate, size_t decodeInitialSize,
-      size_t decodeMaxSize, size_t startPositionMillis);
+  AudioEngine(size_t targetFrames, float windowDuration,
+      float windowOverlapDuration, size_t maxPlayBufferCount,
+      float initialRate, size_t decodeInitialSize, size_t decodeMaxSize,
+      size_t startPositionMillis);
   virtual ~AudioEngine();
 
   bool PlayUri(const char* uri);
@@ -83,6 +83,10 @@
   void ClearDecodeBuffer();
   bool IsDecodeBufferEmpty();
   bool GetHasReachedPlayingBuffersLimit();
+  bool HasSampleRateAndChannels();
+  SLuint32 GetSLSampleRate();
+  SLuint32 GetSLChannels();
+  size_t GetChannelCount();
 
   // The single global audio engine instance.
   static AudioEngine* audioEngine_;
@@ -107,8 +111,12 @@
   float* floatBuffer_;
   float* injectBuffer_;
 
-  size_t channels_;
-  size_t sampleRate_;
+  // Required when we create the audio player.
+  // Set during the first callback from the decoder.
+  // Guarded by callbackLock_.
+  SLuint32 mSampleRate;
+  SLuint32 mChannels;
+
   size_t targetFrames_;
   float windowDuration_;
   float windowOverlapDuration_;
@@ -132,6 +140,9 @@
   // Stores the total duration of the track.
   SLmillisecond totalDurationMs_;
   // Protected by lock_.
+  // Stores the current position of the decoder head.
+  SLmillisecond decoderCurrentPosition_;
+  // Protected by lock_.
   // Set externally via RequestStart(), this determines when we begin to
   // playback audio.
   // Until this is set to true, our audio player will remain stopped.
diff --git a/variablespeed/src/com/android/ex/variablespeed/EngineParameters.java b/variablespeed/src/com/android/ex/variablespeed/EngineParameters.java
index 60cbea6..e57e9f9 100644
--- a/variablespeed/src/com/android/ex/variablespeed/EngineParameters.java
+++ b/variablespeed/src/com/android/ex/variablespeed/EngineParameters.java
@@ -27,8 +27,6 @@
  */
 @Immutable
 /*package*/ final class EngineParameters {
-    private final int mChannels;
-    private final int mSampleRate;
     private final int mTargetFrames;
     private final int mMaxPlayBufferCount;
     private final float mWindowDuration;
@@ -38,14 +36,6 @@
     private final int mDecodeBufferMaxSize;
     private final int mStartPositionMillis;
 
-    public int getChannels() {
-        return mChannels;
-    }
-
-    public int getSampleRate() {
-        return mSampleRate;
-    }
-
     public int getTargetFrames() {
         return mTargetFrames;
     }
@@ -78,12 +68,9 @@
         return mStartPositionMillis;
     }
 
-    private EngineParameters(int channels, int sampleRate, int targetFrames,
-            int maxPlayBufferCount, float windowDuration, float windowOverlapDuration,
-            float initialRate, int decodeBufferInitialSize, int decodeBufferMaxSize,
-            int startPositionMillis) {
-        mChannels = channels;
-        mSampleRate = sampleRate;
+    private EngineParameters(int targetFrames, int maxPlayBufferCount, float windowDuration,
+            float windowOverlapDuration, float initialRate, int decodeBufferInitialSize,
+            int decodeBufferMaxSize, int startPositionMillis) {
         mTargetFrames = targetFrames;
         mMaxPlayBufferCount = maxPlayBufferCount;
         mWindowDuration = windowDuration;
@@ -103,8 +90,6 @@
      */
     @NotThreadSafe
     public static class Builder {
-        private int mChannels = 2;
-        private int mSampleRate = 44100;
         private int mTargetFrames = 1000;
         private int mMaxPlayBufferCount = 2;
         private float mWindowDuration = 0.08f;
@@ -115,26 +100,11 @@
         private int mStartPositionMillis = 0;
 
         public EngineParameters build() {
-            return new EngineParameters(mChannels, mSampleRate, mTargetFrames, mMaxPlayBufferCount,
+            return new EngineParameters(mTargetFrames, mMaxPlayBufferCount,
                     mWindowDuration, mWindowOverlapDuration, mInitialRate,
                     mDecodeBufferInitialSize, mDecodeBufferMaxSize, mStartPositionMillis);
         }
 
-        public Builder channels(int channels) {
-            mChannels = channels;
-            return this;
-        }
-
-        public Builder sampleRate(int sampleRate) {
-            mSampleRate = sampleRate;
-            return this;
-        }
-
-        public Builder targetFrames(int targetFrames) {
-            mTargetFrames = targetFrames;
-            return this;
-        }
-
         public Builder maxPlayBufferCount(int maxPlayBufferCount) {
             mMaxPlayBufferCount = maxPlayBufferCount;
             return this;
diff --git a/variablespeed/src/com/android/ex/variablespeed/VariableSpeed.java b/variablespeed/src/com/android/ex/variablespeed/VariableSpeed.java
index 1d4e973..1f86a95 100644
--- a/variablespeed/src/com/android/ex/variablespeed/VariableSpeed.java
+++ b/variablespeed/src/com/android/ex/variablespeed/VariableSpeed.java
@@ -21,6 +21,7 @@
 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;
@@ -44,9 +45,10 @@
  * (besides only ever accessing it from one thread) is to wrap it in a
  * {@link SingleThreadedMediaPlayerProxy}, designed just for this purpose.
  */
-// TODO: There are a couple of NYI still to iron out in this file.
 @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;
@@ -93,7 +95,7 @@
     public void setOnErrorListener(MediaPlayer.OnErrorListener listener) {
         synchronized (lock) {
             check(!mHasBeenReleased, "has been released, reset before use");
-            // NYI
+            // TODO: I haven't actually added any error listener code.
         }
     }
 
@@ -270,8 +272,7 @@
     }
 
     private void reportException(Exception e) {
-        // NYI
-        e.printStackTrace(System.err);
+        Log.e(TAG, "playback error:", e);
     }
 
     @Override
@@ -283,13 +284,13 @@
             if (!mHasStartedPlayback) {
                 // Playback has not started. Start it.
                 mHasStartedPlayback = true;
-                // TODO: This will be dynamically calculated soon, waiting for a bugfix in media.
                 EngineParameters engineParameters = new EngineParameters.Builder()
-                        .sampleRate(11025).channels(1)
-//                        .sampleRate(44100).channels(2)
                         .initialRate(mCurrentPlaybackRate)
                         .startPositionMillis(mStartPosition).build();
-                mExecutor.execute(new PlaybackRunnable(mDataSource, engineParameters));
+                VariableSpeedNative.initializeEngine(engineParameters);
+                VariableSpeedNative.startPlayback();
+                mEngineInitializedLatch.countDown();
+                mExecutor.execute(new PlaybackRunnable(mDataSource));
             } else {
                 // Playback has already started. Restart it, without holding the
                 // lock.
@@ -304,24 +305,17 @@
     /** A Runnable capable of driving the native audio playback methods. */
     private final class PlaybackRunnable implements Runnable {
         private final MediaPlayerDataSource mInnerSource;
-        private final EngineParameters mEngineParameters;
 
-        public PlaybackRunnable(MediaPlayerDataSource source, EngineParameters engineParameters) {
+        public PlaybackRunnable(MediaPlayerDataSource source) {
             mInnerSource = source;
-            mEngineParameters = engineParameters;
         }
 
         @Override
         public void run() {
-            synchronized (lock) {
-                VariableSpeedNative.initializeEngine(mEngineParameters);
-                mEngineInitializedLatch.countDown();
-            }
             try {
-                VariableSpeedNative.startPlayback();
                 mInnerSource.playNative();
             } catch (IOException e) {
-                // NYI exception handling.
+                Log.e(TAG, "error playing audio", e);
             }
             MediaPlayer.OnCompletionListener completionListener;
             boolean skipThisCompletionReport;
@@ -333,7 +327,6 @@
             if (!skipThisCompletionReport && completionListener != null) {
                 completionListener.onCompletion(null);
             }
-            // NYI exception handling.
         }
     }
 
@@ -370,10 +363,12 @@
     }
 
     public void setVariableSpeed(float rate) {
-        // NYI are there situations in which the engine has been destroyed, so
+        // 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);
             }
diff --git a/variablespeed/src/com/android/ex/variablespeed/VariableSpeedNative.java b/variablespeed/src/com/android/ex/variablespeed/VariableSpeedNative.java
index 90fc49e..ed7e80b 100644
--- a/variablespeed/src/com/android/ex/variablespeed/VariableSpeedNative.java
+++ b/variablespeed/src/com/android/ex/variablespeed/VariableSpeedNative.java
@@ -78,14 +78,14 @@
     /*package*/ static native int getTotalDuration();
 
     /*package*/ static void initializeEngine(EngineParameters params) {
-        initializeEngine(params.getChannels(), params.getSampleRate(), params.getTargetFrames(),
+        initializeEngine(params.getTargetFrames(),
                 params.getWindowDuration(), params.getWindowOverlapDuration(),
                 params.getMaxPlayBufferCount(), params.getInitialRate(),
                 params.getDecodeBufferInitialSize(), params.getDecodeBufferMaxSize(),
                 params.getStartPositionMillis());
     }
 
-    private static native void initializeEngine(int channels, int sampleRate, int targetFrames,
+    private static native void initializeEngine(int targetFrames,
             float windowDuration, float windowOverlapDuration, int maxPlayBufferCount,
             float initialRate, int decodeBufferInitialSize, int decodeBufferMaxSize,
             int startPositionMillis);
diff --git a/variablespeed/tests/src/com/android/ex/variablespeed/VariableSpeedTest.java b/variablespeed/tests/src/com/android/ex/variablespeed/VariableSpeedTest.java
index 433b7a5..62cabeb 100644
--- a/variablespeed/tests/src/com/android/ex/variablespeed/VariableSpeedTest.java
+++ b/variablespeed/tests/src/com/android/ex/variablespeed/VariableSpeedTest.java
@@ -26,13 +26,7 @@
 public class VariableSpeedTest extends MediaPlayerProxyTestCase {
     private static final String TAG = "VariableSpeedTest";
 
-    private ScheduledExecutorService mExecutor;
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        mExecutor = Executors.newScheduledThreadPool(2);
-    }
+    private ScheduledExecutorService mExecutor = Executors.newScheduledThreadPool(2);
 
     @Override
     protected void tearDown() throws Exception {
@@ -40,7 +34,7 @@
         // the media player before I can be confident that I can shut down the executor service.
         super.tearDown();
         mExecutor.shutdown();
-        if (mExecutor.awaitTermination(10, TimeUnit.SECONDS)) {
+        if (!mExecutor.awaitTermination(10, TimeUnit.SECONDS)) {
             Log.e(TAG, "Couldn't shut down Executor during test, check your cleanup code!");
         }
         mExecutor = null;