From 52ac301ee15abd14cdb84053a5de4f1528c4b16c Mon Sep 17 00:00:00 2001 From: Gil Dobjanschi Date: Mon, 4 Oct 2010 11:00:30 -0700 Subject: [PATCH] Added audio track playback API Change-Id: I9884ab5d3a7d8d8310f4b4426e50b0496cbbd295 --- .../android/media/videoeditor/AudioTrack.java | 174 +++++++++++++++++- .../videoeditor/VideoEditorTestImpl.java | 13 +- 2 files changed, 178 insertions(+), 9 deletions(-) diff --git a/media/java/android/media/videoeditor/AudioTrack.java b/media/java/android/media/videoeditor/AudioTrack.java index 468ba2a2102c6..96d55f1f76fcd 100755 --- a/media/java/android/media/videoeditor/AudioTrack.java +++ b/media/java/android/media/videoeditor/AudioTrack.java @@ -18,12 +18,17 @@ package android.media.videoeditor; 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 @@ public class AudioTrack { // 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 @@ public class AudioTrack { } /** - * @return The id of the media item + * @return The id of the audio track */ public String getId() { return mUniqueId; @@ -311,6 +439,50 @@ public class AudioTrack { return mDuckingLowVolume; } + /** + * 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 diff --git a/media/java/android/media/videoeditor/VideoEditorTestImpl.java b/media/java/android/media/videoeditor/VideoEditorTestImpl.java index 16f9495b2701c..eb641dbc654c8 100644 --- a/media/java/android/media/videoeditor/VideoEditorTestImpl.java +++ b/media/java/android/media/videoeditor/VideoEditorTestImpl.java @@ -537,16 +537,13 @@ public class VideoEditorTestImpl implements VideoEditor { * {@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; } /*