@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)
               /// M: add more buffer to smooth audio ALPS00712496 and ALPS00723581 @{
               .maxPlayBufferCount(8)
               .decodeBufferMaxSize(40960)
               /// @}
               .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);
   }
 }
 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 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;
   }
 }
 @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;
   }
 }
 /** 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);
   }
 }
 @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;
   }
 }