Merge "resolved conflicts for merge of 242fa52b to master"
diff --git a/media/java/android/media/videoeditor/AudioTrack.java b/media/java/android/media/videoeditor/AudioTrack.java
index 468ba2a..96d55f1 100755
--- a/media/java/android/media/videoeditor/AudioTrack.java
+++ b/media/java/android/media/videoeditor/AudioTrack.java
@@ -18,12 +18,17 @@
 

 import java.io.IOException;

 

+import android.util.Log;

+

 /**

  * This class allows to handle an audio track. This audio file is mixed with the

  * audio samples of the MediaItems.

  * {@hide}

  */

 public class AudioTrack {

+    // Logging

+    private static final String TAG = "AudioTrack";

+

     // Instance variables

     private final String mUniqueId;

     private final String mFilename;

@@ -47,6 +52,129 @@
 

     // The audio waveform filename

     private String mAudioWaveformFilename;

+    private PlaybackThread mPlaybackThread;

+

+    /**

+     * This listener interface is used by the AudioTrack to emit playback

+     * progress notifications.

+     */

+    public interface PlaybackProgressListener {

+        /**

+         * This method notifies the listener of the current time position while

+         * playing an audio track

+         *

+         * @param audioTrack The audio track

+         * @param timeMs The current playback position (expressed in milliseconds

+         *            since the beginning of the audio track).

+         * @param end true if the end of the audio track was reached

+         */

+        public void onProgress(AudioTrack audioTrack, long timeMs, boolean end);

+    }

+

+    /**

+     * The playback thread

+     */

+    private class PlaybackThread extends Thread {

+        // Instance variables

+        private final PlaybackProgressListener mListener;

+        private final long mFromMs, mToMs;

+        private boolean mRun;

+        private final boolean mLoop;

+        private long mPositionMs;

+

+        /**

+         * Constructor

+         *

+         * @param fromMs The time (relative to the beginning of the audio track)

+         *            at which the playback will start

+         * @param toMs The time (relative to the beginning of the audio track) at

+         *            which the playback will stop. Use -1 to play to the end of

+         *            the audio track

+         * @param loop true if the playback should be looped once it reaches the

+         *            end

+         * @param listener The listener which will be notified of the playback

+         *            progress

+         */

+        public PlaybackThread(long fromMs, long toMs, boolean loop,

+                PlaybackProgressListener listener) {

+            mPositionMs = mFromMs = fromMs;

+            if (toMs < 0) {

+                mToMs = mDurationMs;

+            } else {

+                mToMs = toMs;

+            }

+            mLoop = loop;

+            mListener = listener;

+            mRun = true;

+        }

+

+        /*

+         * {@inheritDoc}

+         */

+        @Override

+        public void run() {

+            if (Log.isLoggable(TAG, Log.DEBUG)) {

+                Log.d(TAG, "===> PlaybackThread.run enter");

+            }

+

+            while (mRun) {

+                try {

+                    sleep(100);

+                } catch (InterruptedException ex) {

+                    break;

+                }

+

+                mPositionMs += 100;

+

+                if (mPositionMs >= mToMs) {

+                    if (!mLoop) {

+                        if (mListener != null) {

+                            mListener.onProgress(AudioTrack.this, mPositionMs, true);

+                        }

+                        if (Log.isLoggable(TAG, Log.DEBUG)) {

+                            Log.d(TAG, "PlaybackThread.run playback complete");

+                        }

+                        break;

+                    } else {

+                        // Fire a notification for the end of the clip

+                        if (mListener != null) {

+                            mListener.onProgress(AudioTrack.this, mToMs, false);

+                        }

+

+                        // Rewind

+                        mPositionMs = mFromMs;

+                        if (mListener != null) {

+                            mListener.onProgress(AudioTrack.this, mPositionMs, false);

+                        }

+                        if (Log.isLoggable(TAG, Log.DEBUG)) {

+                            Log.d(TAG, "PlaybackThread.run playback complete");

+                        }

+                    }

+                } else {

+                    if (mListener != null) {

+                        mListener.onProgress(AudioTrack.this, mPositionMs, false);

+                    }

+                }

+            }

+            if (Log.isLoggable(TAG, Log.DEBUG)) {

+                Log.d(TAG, "===> PlaybackThread.run exit");

+            }

+        }

+

+        /**

+         * Stop the playback

+         *

+         * @return The stop position

+         */

+        public long stopPlayback() {

+            mRun = false;

+            try {

+                join();

+            } catch (InterruptedException ex) {

+            }

+            return mPositionMs;

+        }

+    };

 

     /**

      * An object of this type cannot be instantiated by using the default

@@ -99,7 +227,7 @@
     }

 

     /**

-     * @return The id of the media item

+     * @return The id of the audio track

      */

     public String getId() {

         return mUniqueId;

@@ -312,6 +440,50 @@
     }

 

     /**

+     * Start the playback of this audio track. This method does not block (does

+     * not wait for the playback to complete).

+     *

+     * @param fromMs The time (relative to the beginning of the audio track) at

+     *            which the playback will start

+     * @param toMs The time (relative to the beginning of the audio track) at

+     *            which the playback will stop. Use -1 to play to the end of the

+     *            audio track

+     * @param loop true if the playback should be looped once it reaches the end

+     * @param listener The listener which will be notified of the playback

+     *            progress

+     * @throws IllegalArgumentException if fromMs or toMs is beyond the playback

+     *             duration

+     * @throws IllegalStateException if a playback, preview or an export is

+     *             already in progress

+     */

+    public void startPlayback(long fromMs, long toMs, boolean loop,

+            PlaybackProgressListener listener) {

+        if (fromMs >= mDurationMs) {

+            return;

+        }

+        mPlaybackThread = new PlaybackThread(fromMs, toMs, loop, listener);

+        mPlaybackThread.start();

+    }

+

+    /**

+     * Stop the audio track playback. This method blocks until the ongoing

+     * playback is stopped.

+     *

+     * @return The accurate current time when stop is effective expressed in

+     *         milliseconds

+     */

+    public long stopPlayback() {

+        final long stopTimeMs;

+        if (mPlaybackThread != null) {

+            stopTimeMs = mPlaybackThread.stopPlayback();

+            mPlaybackThread = null;

+        } else {

+            stopTimeMs = 0;

+        }

+        return stopTimeMs;

+    }

+

+    /**

      * This API allows to generate a file containing the sample volume levels of

      * this audio track object. This function may take significant time and is

      * blocking. The filename can be retrieved using getAudioWaveformFilename().

diff --git a/media/java/android/media/videoeditor/VideoEditorTestImpl.java b/media/java/android/media/videoeditor/VideoEditorTestImpl.java
index 16f9495..eb641db 100644
--- a/media/java/android/media/videoeditor/VideoEditorTestImpl.java
+++ b/media/java/android/media/videoeditor/VideoEditorTestImpl.java
@@ -537,16 +537,13 @@
      * {@inheritDoc}
      */
     public AudioTrack getAudioTrack(String audioTrackId) {
-        if (mPreviewThread != null) {
-            throw new IllegalStateException("Previewing is in progress");
+        for (AudioTrack at : mAudioTracks) {
+            if (at.getId().equals(audioTrackId)) {
+                return at;
+            }
         }
 
-        final AudioTrack audioTrack = getAudioTrack(audioTrackId);
-        if (audioTrack != null) {
-            mAudioTracks.remove(audioTrack);
-        }
-
-        return audioTrack;
+        return null;
     }
 
     /*