summaryrefslogtreecommitdiffstats
path: root/variablespeed
diff options
context:
space:
mode:
authorHugo Hudson <hugohudson@google.com>2011-07-26 20:05:25 +0100
committerHugo Hudson <hugohudson@google.com>2011-07-29 11:52:12 +0100
commit0bd6ec5bc06b869131ee0facf38ff02f81f65c10 (patch)
tree444fd6f08804b5c6150be87abdce5764edd69e51 /variablespeed
parent4221a1d7cab69fd886c03abf53f2250025367f5e (diff)
downloadandroid_frameworks_ex-0bd6ec5bc06b869131ee0facf38ff02f81f65c10.tar.gz
android_frameworks_ex-0bd6ec5bc06b869131ee0facf38ff02f81f65c10.tar.bz2
android_frameworks_ex-0bd6ec5bc06b869131ee0facf38ff02f81f65c10.zip
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
Diffstat (limited to 'variablespeed')
-rw-r--r--variablespeed/jni/jni_entry.cc10
-rw-r--r--variablespeed/jni/variablespeed.cc498
-rw-r--r--variablespeed/jni/variablespeed.h23
-rw-r--r--variablespeed/src/com/android/ex/variablespeed/EngineParameters.java38
-rw-r--r--variablespeed/src/com/android/ex/variablespeed/VariableSpeed.java33
-rw-r--r--variablespeed/src/com/android/ex/variablespeed/VariableSpeedNative.java4
-rw-r--r--variablespeed/tests/src/com/android/ex/variablespeed/VariableSpeedTest.java10
7 files changed, 280 insertions, 336 deletions
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 @@ Java_com_android_ex_variablespeed_VariableSpeedNative_##x
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 @@ JNI_METHOD(getTotalDuration, int) (JNIEnv*, jclass) {
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 kNumberOfSamplesPerBuffer = 1152;
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 @@ const SLuint32 kPrefetchErrorCandidate =
// 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 @@ static void DecodingEventCb(SLPlayItf caller, void*, SLuint32 event) {
}
// ****************************************************************************
-// Static utility methods.
+// Macros for making working with OpenSL easier.
+
+// #define LOG_OPENSL_API_CALL(string) LOGV(string)
+#define LOG_OPENSL_API_CALL(string) false
-static void PausePlaying(SLPlayItf playItf) {
- SLresult result = (*playItf)->SetPlayState(playItf, SL_PLAYSTATE_PAUSED);
- CheckSLResult("pause playing", result);
+// 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); \
}
-static void StartPlaying(SLPlayItf playItf) {
- SLresult result = (*playItf)->SetPlayState(playItf, SL_PLAYSTATE_PLAYING);
- CheckSLResult("start playing", 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); \
}
-static void StopPlaying(SLPlayItf playItf) {
- SLresult result = (*playItf)->SetPlayState(playItf, SL_PLAYSTATE_STOPPED);
- CheckSLResult("stop playing", result);
+// 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); \
}
-static void ExtractMetadataFromDecoder(
- SLMetadataExtractionItf decoderMetadata) {
+// 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.
+
+// 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 ExtractMetadataFromDecoder(
}
}
-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 @@ static void RegisterCallbackContextAndAddEnqueueBuffersToDecoder(
// ****************************************************************************
// 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 @@ AudioEngine::~AudioEngine() {
// 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 @@ void AudioEngine::RequestStop() {
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 @@ int AudioEngine::GetTotalDuration() {
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 @@ bool AudioEngine::EnqueueNextBufferOfAudio(
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 @@ bool AudioEngine::EnqueueNextBufferOfAudio(
// 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 @@ bool AudioEngine::EnqueueNextBufferOfAudio(
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 @@ void AudioEngine::ClearDecodeBuffer() {
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 @@ static void CreateAndRealizeAudioPlayer(size_t sampleRate, size_t channels,
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);
-}
-
-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);
+ OpenSL(engineInterface, CreateAudioPlayer, &audioPlayer, &playingSrc,
+ &audioSnk, playerInterfaceCount, iids, reqs);
+ OpenSL(audioPlayer, Realize, SL_BOOLEAN_FALSE);
+}
+
+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 @@ bool AudioEngine::PlayFromThisSource(const SLDataSource& audioSrc) {
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);
-
- // Get the metadata interface from the decoder.
- SLMetadataExtractionItf decoderMetadata;
- result = (*decoder)->GetInterface(decoder,
- SL_IID_METADATAEXTRACTION, &decoderMetadata);
- CheckSLResult("getting metadata 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);
+ // 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);
-
- StartPlaying(decoderPlay);
+ // Start decoding immediately.
+ OpenSL(decoderPlay, SetPlayState, SL_PLAYSTATE_PLAYING);
+ // 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 @@ bool AudioEngine::PlayFromThisSource(const SLDataSource& audioSrc) {
// 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 @@ bool AudioEngine::PlayFromThisSource(const SLDataSource& audioSrc) {
// 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 @@ bool AudioEngine::PlayFromThisSource(const SLDataSource& audioSrc) {
}
// 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 @@ bool AudioEngine::GetHasReachedPlayingBuffersLimit() {
void AudioEngine::EnqueueMoreAudioIfNecessary(
SLAndroidSimpleBufferQueueItf audioPlayerQueue) {
bool keepEnqueueing = true;
- while (!GetWasStopRequested() &&
+ while (audioPlayerQueue != NULL &&
+ !GetWasStopRequested() &&
!IsDecodeBufferEmpty() &&
!GetHasReachedPlayingBuffersLimit() &&
keepEnqueueing) {
@@ -781,15 +749,13 @@ void AudioEngine::PrefetchEventCallback(
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 @@ void AudioEngine::DecodingBufferQueueCallback(
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 @@ void AudioEngine::DecodingBufferQueueCallback(
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 @@ namespace video_editing {
// 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 @@ class AudioEngine {
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 @@ class AudioEngine {
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 @@ class AudioEngine {
// 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 @@ import javax.annotation.concurrent.NotThreadSafe;
*/
@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 @@ import javax.annotation.concurrent.NotThreadSafe;
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 @@ import javax.annotation.concurrent.NotThreadSafe;
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 @@ import javax.annotation.concurrent.NotThreadSafe;
*/
@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 @@ import javax.annotation.concurrent.NotThreadSafe;
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 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;
@@ -44,9 +45,10 @@ import javax.annotation.concurrent.ThreadSafe;
* (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 class VariableSpeed implements MediaPlayerProxy {
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 @@ public class VariableSpeed implements MediaPlayerProxy {
}
private void reportException(Exception e) {
- // NYI
- e.printStackTrace(System.err);
+ Log.e(TAG, "playback error:", e);
}
@Override
@@ -283,13 +284,13 @@ public class VariableSpeed implements MediaPlayerProxy {
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 @@ public class VariableSpeed implements MediaPlayerProxy {
/** 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 @@ public class VariableSpeed implements MediaPlayerProxy {
if (!skipThisCompletionReport && completionListener != null) {
completionListener.onCompletion(null);
}
- // NYI exception handling.
}
}
@@ -370,10 +363,12 @@ public class VariableSpeed implements MediaPlayerProxy {
}
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 @@ import java.lang.reflect.Field;
/*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 @@ import java.util.concurrent.TimeUnit;
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 @@ public class VariableSpeedTest extends MediaPlayerProxyTestCase {
// 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;