From 9c9958ca6c51676cab549d19311df59a1b4ccf1a Mon Sep 17 00:00:00 2001 From: Andrew Lee Date: Mon, 1 Jun 2015 14:03:39 -0700 Subject: Remove variable-speed library. Bug: 21444991 Change-Id: I3e1fc7f0773ddfa227cde6b5161102a087f47702 --- variablespeed/Android.mk | 40 - variablespeed/jni/Android.mk | 44 -- variablespeed/jni/decode_buffer.cc | 86 -- variablespeed/jni/decode_buffer.h | 59 -- variablespeed/jni/hlogging.h | 32 - variablespeed/jni/integral_types.h | 33 - variablespeed/jni/jni_entry.cc | 96 --- variablespeed/jni/macros.h | 66 -- variablespeed/jni/no_synchronization.h | 50 -- variablespeed/jni/profile_timer.h | 52 -- variablespeed/jni/ring_buffer.cc | 154 ---- variablespeed/jni/ring_buffer.h | 117 --- variablespeed/jni/sola_time_scaler.cc | 366 --------- variablespeed/jni/sola_time_scaler.h | 165 ---- variablespeed/jni/variablespeed.cc | 864 --------------------- variablespeed/jni/variablespeed.h | 167 ---- .../android/ex/variablespeed/EngineParameters.java | 158 ---- .../ex/variablespeed/MediaPlayerDataSource.java | 68 -- .../android/ex/variablespeed/MediaPlayerProxy.java | 50 -- .../SingleThreadedMediaPlayerProxy.java | 115 --- .../android/ex/variablespeed/VariableSpeed.java | 410 ---------- .../ex/variablespeed/VariableSpeedNative.java | 92 --- variablespeed/tests/Android.mk | 28 - variablespeed/tests/AndroidManifest.xml | 33 - variablespeed/tests/assets/README.txt | 4 - variablespeed/tests/assets/count_and_test.3gpp | Bin 7405 -> 0 bytes variablespeed/tests/assets/fake_voicemail.mp3 | Bin 20817 -> 0 bytes variablespeed/tests/assets/fake_voicemail2.mp3 | Bin 26336 -> 0 bytes .../tests/assets/quick_test_recording.mp3 | Bin 30591 -> 0 bytes .../variablespeed/AwaitableCompletionListener.java | 59 -- .../ex/variablespeed/AwaitableErrorListener.java | 67 -- .../com/android/ex/variablespeed/DynamicProxy.java | 62 -- .../ex/variablespeed/MediaPlayerProxyTestCase.java | 581 -------------- .../ex/variablespeed/RealMediaPlayerTest.java | 44 -- .../ex/variablespeed/VariableSpeedTest.java | 47 -- 35 files changed, 4209 deletions(-) delete mode 100644 variablespeed/Android.mk delete mode 100644 variablespeed/jni/Android.mk delete mode 100644 variablespeed/jni/decode_buffer.cc delete mode 100644 variablespeed/jni/decode_buffer.h delete mode 100644 variablespeed/jni/hlogging.h delete mode 100644 variablespeed/jni/integral_types.h delete mode 100644 variablespeed/jni/jni_entry.cc delete mode 100644 variablespeed/jni/macros.h delete mode 100644 variablespeed/jni/no_synchronization.h delete mode 100644 variablespeed/jni/profile_timer.h delete mode 100644 variablespeed/jni/ring_buffer.cc delete mode 100644 variablespeed/jni/ring_buffer.h delete mode 100644 variablespeed/jni/sola_time_scaler.cc delete mode 100644 variablespeed/jni/sola_time_scaler.h delete mode 100644 variablespeed/jni/variablespeed.cc delete mode 100644 variablespeed/jni/variablespeed.h delete mode 100644 variablespeed/src/com/android/ex/variablespeed/EngineParameters.java delete mode 100644 variablespeed/src/com/android/ex/variablespeed/MediaPlayerDataSource.java delete mode 100644 variablespeed/src/com/android/ex/variablespeed/MediaPlayerProxy.java delete mode 100644 variablespeed/src/com/android/ex/variablespeed/SingleThreadedMediaPlayerProxy.java delete mode 100644 variablespeed/src/com/android/ex/variablespeed/VariableSpeed.java delete mode 100644 variablespeed/src/com/android/ex/variablespeed/VariableSpeedNative.java delete mode 100644 variablespeed/tests/Android.mk delete mode 100644 variablespeed/tests/AndroidManifest.xml delete mode 100644 variablespeed/tests/assets/README.txt delete mode 100644 variablespeed/tests/assets/count_and_test.3gpp delete mode 100644 variablespeed/tests/assets/fake_voicemail.mp3 delete mode 100644 variablespeed/tests/assets/fake_voicemail2.mp3 delete mode 100644 variablespeed/tests/assets/quick_test_recording.mp3 delete mode 100644 variablespeed/tests/src/com/android/ex/variablespeed/AwaitableCompletionListener.java delete mode 100644 variablespeed/tests/src/com/android/ex/variablespeed/AwaitableErrorListener.java delete mode 100644 variablespeed/tests/src/com/android/ex/variablespeed/DynamicProxy.java delete mode 100644 variablespeed/tests/src/com/android/ex/variablespeed/MediaPlayerProxyTestCase.java delete mode 100644 variablespeed/tests/src/com/android/ex/variablespeed/RealMediaPlayerTest.java delete mode 100644 variablespeed/tests/src/com/android/ex/variablespeed/VariableSpeedTest.java diff --git a/variablespeed/Android.mk b/variablespeed/Android.mk deleted file mode 100644 index d5acbe0..0000000 --- a/variablespeed/Android.mk +++ /dev/null @@ -1,40 +0,0 @@ -# Copyright (C) 2011 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -LOCAL_PATH := $(call my-dir) - -# Build the java code, shared library. - -include $(CLEAR_VARS) - -LOCAL_MODULE_TAGS := optional - -LOCAL_MODULE := android-ex-variablespeed - -LOCAL_SDK_VERSION := current - -LOCAL_STATIC_JAVA_LIBRARIES := \ - android-common \ - guava \ - -LOCAL_SRC_FILES := \ - $(call all-java-files-under, src) - -include $(BUILD_STATIC_JAVA_LIBRARY) - -# Build the native code, library. - -include $(CLEAR_VARS) - -include $(LOCAL_PATH)/jni/Android.mk diff --git a/variablespeed/jni/Android.mk b/variablespeed/jni/Android.mk deleted file mode 100644 index 98b9325..0000000 --- a/variablespeed/jni/Android.mk +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright (C) 2011 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -LOCAL_PATH := $(call my-dir) -include $(CLEAR_VARS) -LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk - -# TODO: Remove the .cc extension, just .cpp. - -# Add in extra warnings. -LOCAL_CFLAGS += -Wall -LOCAL_CPPFLAGS += -Wall - -LOCAL_CPP_EXTENSION := .cc -LOCAL_MODULE := libvariablespeed -LOCAL_MODULE_TAGS := optional - -LOCAL_SRC_FILES := \ - variablespeed.cc \ - ring_buffer.cc \ - sola_time_scaler.cc \ - jni_entry.cc \ - decode_buffer.cc \ - -LOCAL_C_INCLUDES := \ - $(call include-path-for, wilhelm) \ - -LOCAL_SHARED_LIBRARIES := \ - libOpenSLES \ - liblog \ - -LOCAL_CLANG := true -include $(BUILD_SHARED_LIBRARY) diff --git a/variablespeed/jni/decode_buffer.cc b/variablespeed/jni/decode_buffer.cc deleted file mode 100644 index a08cfd6..0000000 --- a/variablespeed/jni/decode_buffer.cc +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -namespace { - -static const size_t kNumberOfBytesPerSample = 2; - -} // namespace - -DecodeBuffer::DecodeBuffer(size_t sizeOfOneBuffer, size_t maxSize) - : sizeOfOneBuffer_(sizeOfOneBuffer), maxSize_(maxSize), - start_(0), end_(0), advancedCount_(0), data_() { - Clear(); -} - -DecodeBuffer::~DecodeBuffer() { - Clear(); -} - -size_t DecodeBuffer::GetSizeInBytes() const { - return kNumberOfBytesPerSample * (end_ - start_); -} - -bool DecodeBuffer::IsTooLarge() const { - return GetSizeInBytes() > maxSize_; -} - -void DecodeBuffer::AddData(int8_t* pointer, size_t lengthInBytes) { - for (size_t i = 0; i < lengthInBytes / kNumberOfBytesPerSample; ++i) { - PushValue(reinterpret_cast(pointer)[i]); - } -} - -void DecodeBuffer::Clear() { - while (data_.size() > 0) { - delete[] data_.front(); - data_.erase(data_.begin()); - } - start_ = 0; - end_ = 0; - advancedCount_ = 0; -} - -size_t DecodeBuffer::GetTotalAdvancedCount() const { - return advancedCount_; -} - -void DecodeBuffer::AdvanceHeadPointerShorts(size_t numberOfShorts) { - start_ += numberOfShorts; - while (start_ > sizeOfOneBuffer_) { - data_.push_back(data_.front()); - data_.erase(data_.begin()); - start_ -= sizeOfOneBuffer_; - end_ -= sizeOfOneBuffer_; - } - advancedCount_ += numberOfShorts; -} - -void DecodeBuffer::PushValue(int16 value) { - size_t bufferIndex = end_ / sizeOfOneBuffer_; - if (bufferIndex >= data_.size()) { - data_.push_back(new int16[sizeOfOneBuffer_]); - } - data_.at(bufferIndex)[end_ % sizeOfOneBuffer_] = value; - ++end_; -} - -int16 DecodeBuffer::GetAtIndex(size_t index) { - return data_.at((start_ + index) / sizeOfOneBuffer_) - [(start_ + index) % sizeOfOneBuffer_]; -} diff --git a/variablespeed/jni/decode_buffer.h b/variablespeed/jni/decode_buffer.h deleted file mode 100644 index 2f1aa71..0000000 --- a/variablespeed/jni/decode_buffer.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef FRAMEWORKS_EX_VARIABLESPEED_JNI_DECODE_BUFFER_H_ -#define FRAMEWORKS_EX_VARIABLESPEED_JNI_DECODE_BUFFER_H_ - -#include -#include -#include -#include - -// DecodeBuffer is used to store arrays of int16 values for audio. -// -// This class is not thread-safe. You should provide your own -// synchronization if you wish to use it from multiple threads. -class DecodeBuffer { - public: - DecodeBuffer(size_t sizeOfOneBuffer, size_t maxSize); - virtual ~DecodeBuffer(); - size_t GetSizeInBytes() const; - void AddData(int8_t* pointer, size_t lengthInBytes); - void Clear(); - void AdvanceHeadPointerShorts(size_t numberOfShorts); - int16 GetAtIndex(size_t index); - bool IsTooLarge() const; - size_t GetTotalAdvancedCount() const; - - private: - void PushValue(int16 value); - - size_t sizeOfOneBuffer_; - size_t maxSize_; - size_t start_; - size_t end_; - size_t advancedCount_; - // This vector isn't ideal because we perform a number of queue-like - // operations: namely removing from the front and appending at the back. - // However we also need constant-time access to the elements of this - // vector, and therefore it's not good enough to use a std::queue. - // In practice this data structure choice doesn't seem to be a bottleneck. - std::vector data_; - - DISALLOW_COPY_AND_ASSIGN(DecodeBuffer); -}; - -#endif // FRAMEWORKS_EX_VARIABLESPEED_JNI_DECODE_BUFFER_H_ diff --git a/variablespeed/jni/hlogging.h b/variablespeed/jni/hlogging.h deleted file mode 100644 index 056f5a3..0000000 --- a/variablespeed/jni/hlogging.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef FRAMEWORKS_EX_VARIABLESPEED_JNI_HLOGGING_H_ -#define FRAMEWORKS_EX_VARIABLESPEED_JNI_HLOGGING_H_ - -#include -#include -#include - -// Simple logging macros. -#define LOG_TAG "VariableSpeed" -#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__) -#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__) -#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) -#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__) -#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) - -#endif // FRAMEWORKS_EX_VARIABLESPEED_JNI_HLOGGING_H_ diff --git a/variablespeed/jni/integral_types.h b/variablespeed/jni/integral_types.h deleted file mode 100644 index 8927d7c..0000000 --- a/variablespeed/jni/integral_types.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef FRAMEWORKS_EX_VARIABLESPEED_JNI_INTEGRAL_TYPES_H_ -#define FRAMEWORKS_EX_VARIABLESPEED_JNI_INTEGRAL_TYPES_H_ - -#include // for size_t - -// Standard typedefs -typedef signed char schar; -typedef signed char int8; -typedef short int16; // NOLINT -typedef int int32; -typedef long long int64; // NOLINT -typedef unsigned char uint8; -typedef unsigned short uint16; // NOLINT -typedef unsigned int uint32; -typedef unsigned long long uint64; // NOLINT - -#endif // FRAMEWORKS_EX_VARIABLESPEED_JNI_INTEGRAL_TYPES_H_ diff --git a/variablespeed/jni/jni_entry.cc b/variablespeed/jni/jni_entry.cc deleted file mode 100644 index 20bcf3c..0000000 --- a/variablespeed/jni/jni_entry.cc +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include - -#include -#include - -// Quick #define to make sure I get all the JNI method calls right. -#define JNI_METHOD(x, y) \ -JNIEXPORT y JNICALL \ -Java_com_android_ex_variablespeed_VariableSpeedNative_##x - -class MethodLog { - public: - explicit MethodLog(const char* name) : name_(name) { - LOGV("+ %s", name); - } - virtual ~MethodLog() { - LOGV("- %s", name_); - } - - private: - const char* name_; -}; - -extern "C" { -JNI_METHOD(playFileDescriptor, void) (JNIEnv*, jclass, jint fd, jlong offset, - jlong length) { - MethodLog _("playFileDescriptor"); - AudioEngine::GetEngine()->PlayFileDescriptor(fd, offset, length); -} - -JNI_METHOD(playUri, void) (JNIEnv* env, jclass, jstring uri) { - MethodLog _("playUri"); - const char* utf8 = env->GetStringUTFChars(uri, NULL); - CHECK(NULL != utf8); - AudioEngine::GetEngine()->PlayUri(utf8); -} - -JNI_METHOD(setVariableSpeed, void) (JNIEnv*, jclass, jfloat speed) { - MethodLog _("setVariableSpeed"); - AudioEngine::GetEngine()->SetVariableSpeed(speed); -} - -JNI_METHOD(startPlayback, void) (JNIEnv*, jclass) { - MethodLog _("startPlayback"); - AudioEngine::GetEngine()->RequestStart(); -} - -JNI_METHOD(stopPlayback, void) (JNIEnv*, jclass) { - MethodLog _("stopPlayback"); - AudioEngine::GetEngine()->RequestStop(); -} - -JNI_METHOD(getCurrentPosition, jint) (JNIEnv*, jclass) { - return AudioEngine::GetEngine()->GetCurrentPosition(); -} - -JNI_METHOD(getTotalDuration, jint) (JNIEnv*, jclass) { - return AudioEngine::GetEngine()->GetTotalDuration(); -} - -JNI_METHOD(initializeEngine, void) (JNIEnv*, jclass, - jint targetFrames, jfloat windowDuration, - jfloat windowOverlapDuration, jint maxPlayBufferCount, - jfloat initialRate, jint decodeInitialSize, jint decodeMaxSize, - jint startPositionMillis, jint audioStreamType) { - MethodLog _("initializeEngine"); - AudioEngine *engine = new AudioEngine(targetFrames, - windowDuration, windowOverlapDuration, maxPlayBufferCount, initialRate, - decodeInitialSize, decodeMaxSize, startPositionMillis, audioStreamType); - if (!AudioEngine::CompareAndSetEngine(NULL, engine)) { - delete engine; - } -} - -JNI_METHOD(shutdownEngine, void) (JNIEnv*, jclass) { - MethodLog _("shutdownEngine"); - AudioEngine::DeleteEngine(); -} -} // extern "C" diff --git a/variablespeed/jni/macros.h b/variablespeed/jni/macros.h deleted file mode 100644 index c08d44b..0000000 --- a/variablespeed/jni/macros.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef FRAMEWORKS_EX_VARIABLESPEED_JNI_MACROS_H_ -#define FRAMEWORKS_EX_VARIABLESPEED_JNI_MACROS_H_ - -#include - -inline float min(float a, float b) { - return (a < b) ? a : b; -} - -inline float max(float a, float b) { - return (a > b) ? a : b; -} - -template - ForwardIterator min_element(ForwardIterator first, ForwardIterator last) { - ForwardIterator lowest = first; - if (first == last) return last; - while (++first != last) - if (*first < *lowest) - lowest = first; - return lowest; -} - -// A macro to disallow the copy constructor and operator= functions -// This should be used in the private: declarations for a class -#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ - TypeName(const TypeName&); \ - void operator=(const TypeName&) - -#define CHECK(x) { \ - if (!(x)) { \ - LOGE("assertion failed: " #x); \ - LOGE("file: %s line: %d", __FILE__, __LINE__); \ - int* frob = NULL; \ - *frob = 5; \ - } \ -} - -template -inline Dest bit_cast(const Source& source) { - // Compile time assertion: sizeof(Dest) == sizeof(Source) - // A compile error here means your Dest and Source have different sizes. - typedef char VerifySizesAreEqual [sizeof(Dest) == sizeof(Source) ? 1 : -1]; // NOLINT - - Dest dest; - memcpy(&dest, &source, sizeof(dest)); - return dest; -} - -#endif // FRAMEWORKS_EX_VARIABLESPEED_JNI_MACROS_H_ diff --git a/variablespeed/jni/no_synchronization.h b/variablespeed/jni/no_synchronization.h deleted file mode 100644 index 751ebd9..0000000 --- a/variablespeed/jni/no_synchronization.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef FRAMEWORKS_EX_VARIABLESPEED_JNI_NO_SYNCHRONIZATION_H_ -#define FRAMEWORKS_EX_VARIABLESPEED_JNI_NO_SYNCHRONIZATION_H_ - -#include - -// We don't need any synchronization at the moment. -// The sola_time_scaler (which is the code that uses this mutex class) is -// currently being used in a single-threaded manner, driven from the main -// PlayFromThisSource method in variablespeed. -// As such no locking is actually required, and so this class contains a -// fake mutex that does nothing. - -class Mutex { - public: - Mutex() {} - virtual ~Mutex() {} - void Lock() {} - void Unlock() {} - - private: - DISALLOW_COPY_AND_ASSIGN(Mutex); -}; - -class MutexLock { - public: - explicit MutexLock(Mutex* mu) : mu_(mu) {} - virtual ~MutexLock() {} - - private: - Mutex* const mu_; - DISALLOW_COPY_AND_ASSIGN(MutexLock); -}; - -#endif // FRAMEWORKS_EX_VARIABLESPEED_JNI_NO_SYNCHRONIZATION_H_ diff --git a/variablespeed/jni/profile_timer.h b/variablespeed/jni/profile_timer.h deleted file mode 100644 index 6844cb2..0000000 --- a/variablespeed/jni/profile_timer.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef FRAMEWORKS_EX_VARIABLESPEED_JNI_PROFILE_TIMER_H_ -#define FRAMEWORKS_EX_VARIABLESPEED_JNI_PROFILE_TIMER_H_ - -#include -#include - -#include - -// Simple profiler for debugging method call duration. -class Timer { - public: - Timer() : startTime_(clock()) { - } - - virtual ~Timer() { - PrintElapsed("destructor"); - } - - void PrintElapsed(const char* message) { - clock_t endTime(clock()); - LOGD("Timer(%s): %d ms", message, - static_cast((endTime - startTime_) * 1000 / CLOCKS_PER_SEC)); - } - - size_t GetElapsed() { - clock_t endTime(clock()); - return (endTime - startTime_) * 1000 / CLOCKS_PER_SEC; - } - - private: - clock_t startTime_; - - DISALLOW_COPY_AND_ASSIGN(Timer); -}; - -#endif // FRAMEWORKS_EX_VARIABLESPEED_JNI_PROFILE_TIMER_H_ diff --git a/variablespeed/jni/ring_buffer.cc b/variablespeed/jni/ring_buffer.cc deleted file mode 100644 index 61c1490..0000000 --- a/variablespeed/jni/ring_buffer.cc +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "ring_buffer.h" - -#include - -#include "integral_types.h" - -namespace video_editing { - -void RingBuffer::Init(int size, int num_channels, int num_readers) { - size_ = size; - num_channels_ = num_channels; - num_readers_ = num_readers; - temp_read_buffer_size_ = 1024; - initialized_ = true; - Reset(); -} - -RingBuffer::RingBuffer() - : initialized_(false), samples_(NULL), - num_readers_(0), temp_read_buffer_(NULL) { -} - -RingBuffer::~RingBuffer() { - delete[] samples_; - delete[] temp_read_buffer_; -} - -void RingBuffer::Reset() { - delete[] samples_; - samples_ = new float[size_ * num_channels_]; - memset(samples_, 0, - size_ * num_channels_ * sizeof(samples_[0])); - - temp_read_buffer_size_ = 1024; - delete[] temp_read_buffer_; - temp_read_buffer_ = new float[temp_read_buffer_size_ * num_channels_]; - memset(temp_read_buffer_, 0, - temp_read_buffer_size_ * num_channels_ * sizeof(samples_[0])); - readers_.clear(); - for (int i = 0; i < num_readers_; ++i) { - readers_.push_back(0LL); - } - head_logical_ = 0LL; - head_ = 0; -} - -int RingBuffer::available(int reader) const { - return head_logical_ - readers_[reader]; -} - -int RingBuffer::overhead() const { - int64 tail = GetTail(); - return tail + size_ - head_logical_; -} - -int64 RingBuffer::GetTail() const { - return *std::min_element(readers_.begin(), readers_.end()); -} - -int64 RingBuffer::Tell(int reader) const { - return readers_[reader]; -} - -void RingBuffer::Seek(int reader, int64 position) { - readers_[reader] = position; -} - -void RingBuffer::Write(const float* samples, int num_frames) { - if (!num_frames) { - return; - } - if (head_ + num_frames <= size_) { - memcpy(samples_ + head_ * num_channels_, samples, - num_frames * num_channels_ * sizeof(samples[0])); - head_ += num_frames; - } else { - int overhead = size_ - head_; - memcpy(samples_ + head_ * num_channels_, samples, - num_channels_ * overhead * sizeof(samples[0])); - head_ = num_frames - overhead; - memcpy(samples_, samples + overhead * num_channels_, - num_channels_ * head_ * sizeof(samples[0])); - } - head_logical_ += num_frames; -} - -void RingBuffer::Copy(int reader, float* destination, int num_frames) const { - int pos = Tell(reader) % size_; - if (pos + num_frames <= size_) { - memcpy(destination, samples_ + pos * num_channels_, - num_channels_ * num_frames * sizeof(destination[0])); - } else { - int wrapped = size_ - pos; - memcpy(destination, samples_ + pos * num_channels_, - num_channels_ * wrapped * sizeof(destination[0])); - int remaining = num_frames - wrapped; - memcpy(destination + wrapped * num_channels_, samples_, - num_channels_ * remaining * sizeof(destination[0])); - } -} - -float* RingBuffer::GetPointer(int reader, int num_frames) { - int pos = Tell(reader) % size_; - if (pos + num_frames <= size_) { - return samples_ + pos * num_channels_; - } else { - if (num_frames > temp_read_buffer_size_) { - temp_read_buffer_size_ = num_frames; - delete[] temp_read_buffer_; - temp_read_buffer_ = - new float[temp_read_buffer_size_ * num_channels_]; // NOLINT - } - Copy(reader, temp_read_buffer_, num_frames); - return temp_read_buffer_; - } -} - -void RingBuffer::MergeBack(int reader, const float* source, int num_frames) { - // If the source pointer is not the temporary buffer, - // data updates were performed in place, so there is nothing to do. - // Otherwise, copy samples from the temp buffer back to the ring buffer. - if (source == temp_read_buffer_) { - int pos = Tell(reader) % size_; - if (pos + num_frames <= size_) { - memcpy(samples_ + (pos * num_channels_), source, - num_channels_ * num_frames * sizeof(source[0])); - } else { - int wrapped = size_ - pos; - memcpy(samples_ + (pos * num_channels_), source, - num_channels_ * wrapped * sizeof(source[0])); - int remaining = num_frames - wrapped; - memcpy(samples_, source + (wrapped * num_channels_), - num_channels_ * remaining * sizeof(source[0])); - } - } -} - -} // namespace video_editing diff --git a/variablespeed/jni/ring_buffer.h b/variablespeed/jni/ring_buffer.h deleted file mode 100644 index 8afe436..0000000 --- a/variablespeed/jni/ring_buffer.h +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef FRAMEWORKS_EX_VARIABLESPEED_JNI_RING_BUFFER_H_ -#define FRAMEWORKS_EX_VARIABLESPEED_JNI_RING_BUFFER_H_ - -#include - -#include "integral_types.h" -#include "macros.h" - -// Circular buffer of multichannel audio, maintaining the position of the -// writing head and several reading head, and providing a method for -// accessing a contiguous block (through direct reference or the copy in a -// temporary read buffer in case the requested block crosses the buffer -// boundaries). -// -// This code is not thread-safe. - -namespace video_editing { - -class RingBuffer { - public: - RingBuffer(); - virtual ~RingBuffer(); - - // Initializes a RingBuffer. - // @param size: size of the buffer in frames. - // @param num_channels: number of channels of the original audio. - // @param num_readers: number of reading heads. - void Init(int size, int num_channels, int num_readers); - - // Gets the position of a reading head. - // @param reader reading head index. - // @returns position pointed to by the #reader reading head. - int64 Tell(int reader) const; - - // Moves a reading head. - // @param reader reading head index. - // @param position target position. - void Seek(int reader, int64 position); - - // Reads samples for a reading head. - // @param reader reading head index. - // @param num_frames number of frames to read. - // @param destination float buffer to which the samples will be written. - void Copy(int reader, float* destination, int num_frames) const; - - // Writes samples. - // @param samples float buffer containing the samples. - // @param num_frames number of frames to write. - void Write(const float* samples, int num_frames); - - // Flushes the content of the buffer and reset the position of the heads. - void Reset(); - - // Returns the number of frames we can still write. - int overhead() const; - - // Returns the number of frames we can read for a given reader. - // @param reader reading head index. - int available(int reader) const; - - // Returns a pointer to num_frames x num_channels contiguous samples for - // a given reader. In most cases this directly returns a pointer to the - // data in the ring buffer. However, if the required block wraps around the - // boundaries of the ring buffer, the data is copied to a temporary buffer - // owned by the RingBuffer object. - // @param reader reading head index. - // @param num_frames number of frames to read. - // @returns pointer to a continuous buffer containing num_frames. - float* GetPointer(int reader, int num_frames); - - // Merges updated data back into the ring buffer, if it was updated in - // the temporary buffer. This operation follows a GetPointer() that - // was used to obtain data to rewrite. The buffer address supplied - // here must match the one returned by GetPointer(). - // @param reader reading head index. - // @param source pointer to a continuous buffer containing num_frames. - // @param num_frames number of frames to copy back to the ring buffer. - void MergeBack(int reader, const float* source, int num_frames); - - private: - // Returns the position of the laziest reader. - int64 GetTail() const; - - bool initialized_; - float* samples_; - std::vector readers_; - int size_; - int num_channels_; - int num_readers_; - int64 head_logical_; - int head_; - - float* temp_read_buffer_; - int temp_read_buffer_size_; - - DISALLOW_COPY_AND_ASSIGN(RingBuffer); -}; - -} // namespace video_editing - -#endif // FRAMEWORKS_EX_VARIABLESPEED_JNI_RING_BUFFER_H_ diff --git a/variablespeed/jni/sola_time_scaler.cc b/variablespeed/jni/sola_time_scaler.cc deleted file mode 100644 index 1ff1236..0000000 --- a/variablespeed/jni/sola_time_scaler.cc +++ /dev/null @@ -1,366 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "sola_time_scaler.h" - -#include -#include -#include - -#include "ring_buffer.h" - -#define FLAGS_sola_ring_buffer 2.0 -#define FLAGS_sola_enable_correlation true - - -namespace video_editing { - -// Returns a cross-correlation score for the specified buffers. -int SolaAnalyzer::Correlate(const float* buffer1, const float* buffer2, - int num_frames) { - CHECK(initialized_); - - int score = 0; - num_frames *= num_channels_; - while (num_frames-- > 0) { - // Increment the score if the sign bits match. - score += ((bit_cast(*buffer1++) ^ bit_cast(*buffer2++)) >= 0) - ? 1 : 0; - } - return score; -} - -// Trivial SolaAnalyzer class to bypass correlation. -class SolaBypassAnalyzer : public SolaAnalyzer { - public: - SolaBypassAnalyzer() { } - virtual int Correlate(const float*, const float*, int num_frames) { - return num_frames * num_channels_; - } -}; - - -// Default constructor. -SolaTimeScaler::SolaTimeScaler() - : input_buffer_(NULL), output_buffer_(NULL), analyzer_(NULL) { - sample_rate_ = 0; - num_channels_ = 0; - - draining_ = false; - initialized_ = false; -} - -SolaTimeScaler::~SolaTimeScaler() { - delete input_buffer_; - delete output_buffer_; - delete analyzer_; -} - -// Injects a SolaAnalyzer instance for analyzing signal frames. -void SolaTimeScaler::set_analyzer(SolaAnalyzer* analyzer) { - MutexLock lock(&mutex_); // lock out processing while updating - delete analyzer_; - analyzer_ = analyzer; -} - -// Initializes a SOLA timescaler. -void SolaTimeScaler::Init(double sample_rate, - int num_channels, - double initial_speed, - double window_duration, - double overlap_duration) { - MutexLock lock(&mutex_); // lock out processing while updating - - sample_rate_ = sample_rate; - num_channels_ = num_channels; - speed_ = initial_speed; - window_duration_ = window_duration; - overlap_duration_ = overlap_duration; - - initialized_ = true; - GenerateParameters(); - Reset(); -} - -// Adjusts the rate scaling factor. -void SolaTimeScaler::set_speed(double speed) { - MutexLock lock(&mutex_); // lock out processing while updating - - speed_ = speed; - GenerateParameters(); -} - -// Generates processing parameters from the current settings. -void SolaTimeScaler::GenerateParameters() { - if (speed_ < 0.1) { - LOGE("Requested speed %fx limited to 0.1x", speed_); - speed_ = 0.1; - } else if (speed_ > 8.0) { - LOGE("Requested speed %fx limited to 8.0x", speed_); - speed_ = 8.0; - } - - ratio_ = 1.0 / speed_; - - num_window_frames_ = nearbyint(sample_rate_ * window_duration_); - - // Limit the overlap to half the window size, and round up to an odd number. - // Half of overlap window (rounded down) is also a useful number. - overlap_duration_ = min(overlap_duration_, window_duration_ / 2.0); - num_overlap_frames_ = nearbyint(sample_rate_ * overlap_duration_); - num_overlap_frames_ |= 1; - half_overlap_frames_ = num_overlap_frames_ >> 1; - - if (speed_ >= 1.) { - // For compression (speed up), adjacent input windows overlap in the output. - input_window_offset_ = num_window_frames_; - target_merge_offset_ = nearbyint(num_window_frames_ * ratio_); - } else { - // For expansion (slow down), each input window start point overlaps the - // previous, and they are placed adjacently in the output - // (+/- half the overlap size). - input_window_offset_ = nearbyint(num_window_frames_ * speed_); - target_merge_offset_ = num_window_frames_; - } - - // Make sure we copy enough extra data to be able to perform a - // frame correlation over the range of target merge point +/- half overlap, - // even when the previous merge point was adjusted backwards a half overlap. - max_frames_to_merge_ = max(num_window_frames_, - target_merge_offset_ + (2 * num_overlap_frames_)); - min_output_to_hold_= - max_frames_to_merge_ + num_overlap_frames_ - target_merge_offset_; -} - -// The input buffer has one writer and reader. -// The output buffer has one reader/updater, and one reader/consumer. -static const int kInputReader = 0; -static const int kOutputAnalysis = 0; -static const int kOutputConsumer = 1; - -void SolaTimeScaler::Reset() { - CHECK(initialized_); - double duration = max(FLAGS_sola_ring_buffer, 20. * window_duration_); - draining_ = false; - - delete input_buffer_; - input_buffer_ = new RingBuffer(); - input_buffer_->Init(static_cast - (sample_rate_ * duration), num_channels_, 1); - - delete output_buffer_; - output_buffer_ = new RingBuffer(); - output_buffer_->Init(static_cast - (sample_rate_ * ratio_ * duration), num_channels_, 2); - - if (analyzer_ == NULL) { - if (FLAGS_sola_enable_correlation) { - analyzer_ = new SolaAnalyzer(); - } else { - analyzer_ = new SolaBypassAnalyzer(); - } - } - analyzer_->Init(sample_rate_, num_channels_); -} - -// Returns the number of frames that the input buffer can accept. -int SolaTimeScaler::input_limit() const { - CHECK(initialized_); - return input_buffer_->overhead(); -} - -// Returns the number of available output frames. -int SolaTimeScaler::available() { - CHECK(initialized_); - - int available = output_buffer_->available(kOutputConsumer); - if (available > min_output_to_hold_) { - available -= min_output_to_hold_; - } else if (draining_) { - Process(); - available = output_buffer_->available(kOutputConsumer); - if (available > min_output_to_hold_) { - available -= min_output_to_hold_; - } - } else { - available = 0; - } - return available; -} - -void SolaTimeScaler::Drain() { - CHECK(initialized_); - - draining_ = true; -} - - -// Feeds audio to the timescaler, and processes as much data as possible. -int SolaTimeScaler::InjectSamples(float* buffer, int num_frames) { - CHECK(initialized_); - - // Do not write more frames than the buffer can accept. - num_frames = min(input_limit(), num_frames); - if (!num_frames) { - return 0; - } - - // Copy samples to the input buffer and then process whatever can be consumed. - input_buffer_->Write(buffer, num_frames); - Process(); - return num_frames; -} - -// Retrieves audio data from the timescaler. -int SolaTimeScaler::RetrieveSamples(float* buffer, int num_frames) { - CHECK(initialized_); - - // Do not read more frames than available. - num_frames = min(available(), num_frames); - if (!num_frames) { - return 0; - } - - output_buffer_->Copy(kOutputConsumer, buffer, num_frames); - output_buffer_->Seek(kOutputConsumer, - output_buffer_->Tell(kOutputConsumer) + num_frames); - - return num_frames; -} - -// Munges input samples to produce output. -bool SolaTimeScaler::Process() { - CHECK(initialized_); - bool generated_data = false; - - // We can only process data if there is sufficient input available - // (or we are draining the latency), and there is sufficient room - // for output to be merged. - while (((input_buffer_->available(kInputReader) > max_frames_to_merge_) || - draining_) && (output_buffer_->overhead() >= max_frames_to_merge_)) { - MutexLock lock(&mutex_); // lock out updates while processing each window - - // Determine the number of samples to merge into the output. - int input_count = - min(input_buffer_->available(kInputReader), max_frames_to_merge_); - if (input_count == 0) { - break; - } - // The input reader always points to the next window to process. - float* input_pointer = input_buffer_->GetPointer(kInputReader, input_count); - - // The analysis reader always points to the ideal target merge point, - // minus half an overlap window (ie, the starting point for correlation). - // That means the available data from that point equals the number - // of samples that must be cross-faded. - int output_merge_cnt = output_buffer_->available(kOutputAnalysis); - float* output_pointer = - output_buffer_->GetPointer(kOutputAnalysis, output_merge_cnt); - - // If there is not enough data to do a proper correlation, - // just merge at the ideal target point. Otherwise, - // find the best correlation score, working from the center out. - int merge_offset = min(output_merge_cnt, half_overlap_frames_); - - if ((output_merge_cnt >= (2 * num_overlap_frames_)) && - (input_count >= num_overlap_frames_)) { - int best_offset = merge_offset; - int best_score = 0; - int score; - for (int i = 0; i <= half_overlap_frames_; ++i) { - score = analyzer_->Correlate(input_pointer, - output_pointer + ((merge_offset + i) * num_channels_), - num_overlap_frames_); - if (score > best_score) { - best_score = score; - best_offset = merge_offset + i; - if (score == (num_overlap_frames_ * num_channels_)) { - break; // It doesn't get better than perfect. - } - } - if (i > 0) { - score = analyzer_->Correlate(input_pointer, - output_pointer + ((merge_offset - i) * num_channels_), - num_overlap_frames_); - if (score > best_score) { - best_score = score; - best_offset = merge_offset - i; - if (score == (num_overlap_frames_ * num_channels_)) { - break; // It doesn't get better than perfect. - } - } - } - } - merge_offset = best_offset; - } else if ((output_merge_cnt > 0) && !draining_) { - LOGE("no correlation performed"); - } - - // Crossfade the overlap between input and output, and then - // copy in the remaining input. - int crossfade_count = max(0, (output_merge_cnt - merge_offset)); - crossfade_count = min(crossfade_count, input_count); - int remaining_count = input_count - crossfade_count; - - float* merge_pointer = output_pointer + (merge_offset * num_channels_); - float flt_count = static_cast(crossfade_count); - for (int i = 0; i < crossfade_count; ++i) { - // Linear cross-fade, for now. - float input_scale = static_cast(i) / flt_count; - float output_scale = 1. - input_scale; - for (int j = 0; j < num_channels_; ++j) { - *merge_pointer = (*merge_pointer * output_scale) + - (*input_pointer++ * input_scale); - ++merge_pointer; - } - } - // Copy the merged buffer back into the output, if necessary, and - // append the rest of the window. - output_buffer_->MergeBack(kOutputAnalysis, - output_pointer, output_merge_cnt); - output_buffer_->Write(input_pointer, remaining_count); - - // Advance the output analysis pointer to the next target merge point, - // minus half an overlap window. The target merge point is always - // calculated as a delta from the previous ideal target, not the actual - // target, to avoid drift. - int output_advance = target_merge_offset_; - if (output_merge_cnt < half_overlap_frames_) { - // On the first window, back up the pointer for the next correlation. - // Thereafter, that compensation is preserved. - output_advance -= half_overlap_frames_; - } - - // Don't advance beyond the available data, when finishing up. - if (draining_) { - output_advance = - min(output_advance, output_buffer_->available(kOutputAnalysis)); - } - output_buffer_->Seek(kOutputAnalysis, - output_buffer_->Tell(kOutputAnalysis) + output_advance); - - // Advance the input pointer beyond the frames that are no longer needed. - input_buffer_->Seek(kInputReader, input_buffer_->Tell(kInputReader) + - min(input_count, input_window_offset_)); - - if ((crossfade_count + remaining_count) > 0) { - generated_data = true; - } - } // while (more to process) - return generated_data; -} - -} // namespace video_editing diff --git a/variablespeed/jni/sola_time_scaler.h b/variablespeed/jni/sola_time_scaler.h deleted file mode 100644 index c903f61..0000000 --- a/variablespeed/jni/sola_time_scaler.h +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef FRAMEWORKS_EX_VARIABLESPEED_JNI_SOLA_TIME_SCALER_H_ -#define FRAMEWORKS_EX_VARIABLESPEED_JNI_SOLA_TIME_SCALER_H_ - -#include - -#include - -#include -#include - -#include "macros.h" - -// Time-domain audio playback rate scaler using phase-aligned Synchronized -// OverLap Add (SOLA). - -namespace video_editing { - -class RingBuffer; - -// The default SolaAnalyzer implements a sign-bit cross-correlation -// function for determining the best fit between two signals. -class SolaAnalyzer { - public: - SolaAnalyzer() : initialized_(false) { } - virtual ~SolaAnalyzer() { } - - // Initializes a SolaAnalyzer. - // @param sample_rate sample rate of the audio signal. - // @param num_channels number of interleaved channels in the signal. - void Init(int sample_rate, int num_channels) { - sample_rate_ = sample_rate; - num_channels_ = num_channels; - initialized_ = true; - } - - // Returns a cross-correlation score for the specified buffers. - // The correlation is performed over all channels of a multi-channel signal. - // @param buffer1 pointer to interleaved input samples - // @param buffer2 pointer to interleaved input samples - // @param num_frames number of input frames (that is to say, number of - // samples / number of channels) - // @param returns a correlation score in the range zero to num_frames - virtual int Correlate(const float* buffer1, const float* buffer2, - int num_frames); - - protected: - bool initialized_; - int sample_rate_; - int num_channels_; - - DISALLOW_COPY_AND_ASSIGN(SolaAnalyzer); -}; - - -class SolaTimeScaler { - public: - // Default constructor. - SolaTimeScaler(); - virtual ~SolaTimeScaler(); - - // Injects a SolaAnalyzer instance for analyzing signal frames. - // The scaler takes ownership of this instance. - // This is normally called once, before Init(). - // @param analyzer SolaAnalyzer instance - void set_analyzer(SolaAnalyzer* analyzer); - - // Initializes a SOLA timescaler. - // @param sample_rate sample rate of the signal to process - // @param num_channels number of channels of the signal to process - // @param initial_speed starting rate scaling factor - // @param window_duration processing window size, in seconds - // @param overlap_duration correlation overlap size, in seconds - void Init(double sample_rate, int num_channels, double initial_speed, - double window_duration, double overlap_duration); - - // Adjusts the rate scaling factor. - // This may be called concurrently with processing, and will - // take effect on the next processing window. - // @param speed rate scaling factor - void set_speed(double speed); - - // Indicates that we are done with the input and won't call Process anymore - // This processes all the data reamining in the analysis buffer. - void Drain(); - - // Flushes the buffers associated with the scaler. - void Reset(); - - // Feeds audio to the timescaler, and processes as much data as possible. - // @param buffer pointer to interleaved float input samples - // @param num_frames number of frames (num_samples / num_channels) - // @returns number of frames actually accepted - int InjectSamples(float* buffer, int num_frames); - - // Retrieves audio data from the timescaler. - // @param buffer pointer to buffer to receive interleaved float output - // @param num_frames maximum desired number of frames - // @returns number of frames actually returned - int RetrieveSamples(float* buffer, int num_frames); - - // Returns the number of frames that the input buffer can accept. - // @returns number of frames for the next Process() call - int input_limit() const; - - // Returns the number of available output frames. - // @returns number of frames that can be retrieved - int available(); - - int num_channels() const { return num_channels_; } - - private: - mutable Mutex mutex_; // allows concurrent produce/consume/param change - bool initialized_; // set true when input parameters have been set - bool draining_; // set true to drain latency - - // Input parameters. - int num_channels_; // channel valence of audio stream - double sample_rate_; // sample rate of audio stream - double window_duration_; // the nominal time quantum for processing - double overlap_duration_; // the maximum slip for correlating windows - double speed_; // varispeed rate - - // Derived parameters. - double ratio_; // inverse of speed - int num_window_frames_; // window_duration_ expressed as frame count - int num_overlap_frames_; // overlap_duration_ expressed as frame count - int half_overlap_frames_; // half of the overlap - int input_window_offset_; // frame delta between input windows - int target_merge_offset_; // ideal frame delta between output windows - int max_frames_to_merge_; // ideal frame count to merge to output - int min_output_to_hold_; // number of output frames needed for next merge - - RingBuffer* input_buffer_; - RingBuffer* output_buffer_; - SolaAnalyzer* analyzer_; - - // Generates processing parameters from the current settings. - void GenerateParameters(); - - // Munges input samples to produce output. - // @returns true if any output samples were generated - bool Process(); - - DISALLOW_COPY_AND_ASSIGN(SolaTimeScaler); -}; - -} // namespace video_editing - -#endif // FRAMEWORKS_EX_VARIABLESPEED_JNI_SOLA_TIME_SCALER_H_ diff --git a/variablespeed/jni/variablespeed.cc b/variablespeed/jni/variablespeed.cc deleted file mode 100644 index 8e161fc..0000000 --- a/variablespeed/jni/variablespeed.cc +++ /dev/null @@ -1,864 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -#include -#include - -#include -#include - -#include - -#include - -#include - -// **************************************************************************** -// Constants, utility methods, structures and other miscellany used throughout -// this file. - -namespace { - -// These variables are used to determine the size of the buffer queue used by -// the decoder. -// This is not the same as the large buffer used to hold the uncompressed data -// - for that see the member variable decodeBuffer_. -// The choice of 1152 corresponds to the number of samples per mp3 frame, so is -// a good choice of size for a decoding buffer in the absence of other -// information (we don't know exactly what formats we will be working with). -const size_t kNumberOfBuffersInQueue = 4; -const size_t kNumberOfSamplesPerBuffer = 1152; -const size_t kBufferSizeInBytes = 2 * kNumberOfSamplesPerBuffer; -const size_t kSampleSizeInBytes = 4; - -// When calculating play buffer size before pushing to audio player. -const size_t kNumberOfBytesPerInt16 = 2; - -// How long to sleep during the main play loop and the decoding callback loop. -// In due course this should be replaced with the better signal and wait on -// condition rather than busy-looping. -const int kSleepTimeMicros = 1000; - -// Used in detecting errors with the OpenSL ES framework. -const SLuint32 kPrefetchErrorCandidate = - SL_PREFETCHEVENT_STATUSCHANGE | SL_PREFETCHEVENT_FILLLEVELCHANGE; - -// Structure used when we perform a decoding callback. -typedef struct CallbackContext_ { - // 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. -int8_t pcmData[kNumberOfBuffersInQueue * kBufferSizeInBytes]; - -#define CheckSLResult(message, result) \ - CheckSLResult_Real(message, result, __LINE__) - -// Helper function for debugging - checks the OpenSL result for success. -void CheckSLResult_Real(const char* message, SLresult result, int line) { - // This can be helpful when debugging. - // LOGD("sl result %d for %s", result, message); - if (SL_RESULT_SUCCESS != result) { - LOGE("slresult was %d at %s file variablespeed line %d", - static_cast(result), message, line); - } - CHECK(SL_RESULT_SUCCESS == result); -} - -// Whether logging should be enabled. Only used if LOG_OPENSL_API_CALL is -// defined to use it. -bool gLogEnabled = false; -// The property to set in order to enable logging. -const char *const kLogTagVariableSpeed = "log.tag.VariableSpeed"; - -bool ShouldLog() { - char buffer[PROP_VALUE_MAX]; - __system_property_get(kLogTagVariableSpeed, buffer); - return strlen(buffer) > 0; -} - -} // namespace - -// **************************************************************************** -// Static instance of audio engine, and methods for getting, setting and -// deleting it. - -// The single global audio engine instance. -AudioEngine* AudioEngine::audioEngine_ = NULL; -android::Mutex publishEngineLock_; - -AudioEngine* AudioEngine::GetEngine() { - android::Mutex::Autolock autoLock(publishEngineLock_); - if (audioEngine_ == NULL) { - LOGE("you haven't initialized the audio engine"); - CHECK(false); - return NULL; - } - return audioEngine_; -} - -void AudioEngine::SetEngine(AudioEngine* engine) { - if (audioEngine_ != NULL) { - LOGE("you have already set the audio engine"); - CHECK(false); - return; - } - audioEngine_ = engine; -} - -bool AudioEngine::CompareAndSetEngine(AudioEngine* expect, AudioEngine* update) { - android::Mutex::Autolock autoLock(publishEngineLock_); - if (audioEngine_ == expect) { - DeleteEngine(); - audioEngine_ = update; - return true; - } - return false; -} - -void AudioEngine::DeleteEngine() { - if (audioEngine_ != NULL) { - delete audioEngine_; - audioEngine_ = NULL; - } -} - -// **************************************************************************** -// The callbacks from the engine require static callback functions. -// Here are the static functions - they just delegate to instance methods on -// the engine. - -static void PlayingBufferQueueCb(SLAndroidSimpleBufferQueueItf, void*) { - AudioEngine::GetEngine()->PlayingBufferQueueCallback(); -} - -static void PrefetchEventCb(SLPrefetchStatusItf caller, void*, SLuint32 event) { - AudioEngine::GetEngine()->PrefetchEventCallback(caller, event); -} - -static void DecodingBufferQueueCb(SLAndroidSimpleBufferQueueItf queueItf, - void *context) { - AudioEngine::GetEngine()->DecodingBufferQueueCallback(queueItf, context); -} - -static void DecodingEventCb(SLPlayItf caller, void*, SLuint32 event) { - AudioEngine::GetEngine()->DecodingEventCallback(caller, event); -} - -// **************************************************************************** -// Macros for making working with OpenSL easier. - -// Log based on the value of a property. -#define LOG_OPENSL_API_CALL(string) (gLogEnabled && LOGV(string)) - -// 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. - -// Set the audio stream type for the player. -// -// Must be called before it is realized. -// -// The caller must have requested the SL_IID_ANDROIDCONFIGURATION interface when -// creating the player. -static void setAudioStreamType(SLObjectItf audioPlayer, SLint32 audioStreamType) { - SLAndroidConfigurationItf playerConfig; - OpenSL(audioPlayer, GetInterface, SL_IID_ANDROIDCONFIGURATION, &playerConfig); - // The STREAM_XXX constants defined by android.media.AudioManager match the - // corresponding SL_ANDROID_STREAM_XXX constants defined by - // include/SLES/OpenSLES_AndroidConfiguration.h, so we can just pass the - // value across. - OpenSL(playerConfig, SetConfiguration, SL_ANDROID_KEY_STREAM_TYPE, - &audioStreamType, sizeof(audioStreamType)); -} - -// Must be called with callbackLock_ held. -static void ReadSampleRateAndChannelCount(CallbackContext *pContext, - SLuint32 *sampleRateOut, SLuint32 *channelsOut) { - SLMetadataExtractionItf decoderMetadata = pContext->decoderMetadata; - SLuint32 itemCount; - OpenSL(decoderMetadata, GetItemCount, &itemCount); - SLuint32 i, keySize, valueSize; - SLMetadataInfo *keyInfo, *value; - for (i = 0; i < itemCount; ++i) { - keyInfo = value = NULL; - keySize = valueSize = 0; - OpenSL(decoderMetadata, GetKeySize, i, &keySize); - keyInfo = static_cast(malloc(keySize)); - if (keyInfo) { - OpenSL(decoderMetadata, GetKey, i, keySize, keyInfo); - if (keyInfo->encoding == SL_CHARACTERENCODING_ASCII - || keyInfo->encoding == SL_CHARACTERENCODING_UTF8) { - OpenSL(decoderMetadata, GetValueSize, i, &valueSize); - value = static_cast(malloc(valueSize)); - if (value) { - OpenSL(decoderMetadata, GetValue, i, valueSize, value); - if (strcmp((char*) keyInfo->data, ANDROID_KEY_PCMFORMAT_SAMPLERATE) == 0) { - SLuint32 sampleRate = *(reinterpret_cast(value->data)); - LOGD("sample Rate: %d", sampleRate); - *sampleRateOut = sampleRate; - } else if (strcmp((char*) keyInfo->data, ANDROID_KEY_PCMFORMAT_NUMCHANNELS) == 0) { - SLuint32 channels = *(reinterpret_cast(value->data)); - LOGD("channels: %d", channels); - *channelsOut = channels; - } - free(value); - } - } - free(keyInfo); - } - } -} - -// Must be called with callbackLock_ held. -static void RegisterCallbackContextAndAddEnqueueBuffersToDecoder( - 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). - 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++) { - OpenSL(decoderQueue, Enqueue, context->pData, kBufferSizeInBytes); - context->pData += kBufferSizeInBytes; - } - context->pData = context->pDataBase; -} - -// **************************************************************************** -// Constructor and Destructor. - -AudioEngine::AudioEngine(size_t targetFrames, float windowDuration, - float windowOverlapDuration, size_t maxPlayBufferCount, float initialRate, - size_t decodeInitialSize, size_t decodeMaxSize, size_t startPositionMillis, - int audioStreamType) - : decodeBuffer_(decodeInitialSize, decodeMaxSize), - playingBuffers_(), freeBuffers_(), timeScaler_(NULL), - floatBuffer_(NULL), injectBuffer_(NULL), - mSampleRate(0), mChannels(0), - targetFrames_(targetFrames), - windowDuration_(windowDuration), - windowOverlapDuration_(windowOverlapDuration), - maxPlayBufferCount_(maxPlayBufferCount), initialRate_(initialRate), - startPositionMillis_(startPositionMillis), - audioStreamType_(audioStreamType), - totalDurationMs_(0), decoderCurrentPosition_(0), startRequested_(false), - stopRequested_(false), finishedDecoding_(false) { - // Determine whether we should log calls. - gLogEnabled = ShouldLog(); -} - -AudioEngine::~AudioEngine() { - // destroy the time scaler - if (timeScaler_ != NULL) { - delete timeScaler_; - timeScaler_ = NULL; - } - - // delete all outstanding playing and free buffers - android::Mutex::Autolock autoLock(playBufferLock_); - while (playingBuffers_.size() > 0) { - delete[] playingBuffers_.front(); - playingBuffers_.pop(); - } - while (freeBuffers_.size() > 0) { - delete[] freeBuffers_.top(); - freeBuffers_.pop(); - } - - delete[] floatBuffer_; - floatBuffer_ = NULL; - delete[] injectBuffer_; - injectBuffer_ = NULL; -} - -// **************************************************************************** -// Regular AudioEngine class methods. - -void AudioEngine::SetVariableSpeed(float speed) { - // TODO: Mutex for shared time scaler accesses. - if (HasSampleRateAndChannels()) { - GetTimeScaler()->set_speed(speed); - } else { - // This is being called at a point where we have not yet processed enough - // data to determine the sample rate and number of channels. - // Ignore the call. See http://b/5140693. - LOGD("set varaible speed called, sample rate and channels not ready yet"); - } -} - -void AudioEngine::RequestStart() { - android::Mutex::Autolock autoLock(lock_); - startRequested_ = true; -} - -void AudioEngine::ClearRequestStart() { - android::Mutex::Autolock autoLock(lock_); - startRequested_ = false; -} - -bool AudioEngine::GetWasStartRequested() { - android::Mutex::Autolock autoLock(lock_); - return startRequested_; -} - -void AudioEngine::RequestStop() { - android::Mutex::Autolock autoLock(lock_); - stopRequested_ = true; -} - -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( - (result * 1000) / mSampleRate / mChannels + startPositionMillis_); -} - -int AudioEngine::GetTotalDuration() { - android::Mutex::Autolock autoLock(lock_); - return static_cast(totalDurationMs_); -} - -video_editing::SolaTimeScaler* AudioEngine::GetTimeScaler() { - if (timeScaler_ == NULL) { - CHECK(HasSampleRateAndChannels()); - android::Mutex::Autolock autoLock(callbackLock_); - timeScaler_ = new video_editing::SolaTimeScaler(); - timeScaler_->Init(mSampleRate, mChannels, initialRate_, windowDuration_, - windowOverlapDuration_); - } - return timeScaler_; -} - -bool AudioEngine::EnqueueNextBufferOfAudio( - SLAndroidSimpleBufferQueueItf audioPlayerQueue) { - 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; - // If there is data already in the time scaler, retrieve it. - if (GetTimeScaler()->available() > 0) { - size_t retrieveCount = min(GetTimeScaler()->available(), framesLeft); - int count = GetTimeScaler()->RetrieveSamples( - floatBuffer_ + frameCount * channels, retrieveCount); - if (count <= 0) { - LOGD("error: count was %d", count); - break; - } - frameCount += count; - continue; - } - // If there is no data in the time scaler, then feed some into it. - android::Mutex::Autolock autoLock(decodeBufferLock_); - size_t framesInDecodeBuffer = - decodeBuffer_.GetSizeInBytes() / frameSizeInBytes; - size_t framesScalerCanHandle = GetTimeScaler()->input_limit(); - size_t framesToInject = min(framesInDecodeBuffer, - min(targetFrames_, framesScalerCanHandle)); - if (framesToInject <= 0) { - // No more frames left to inject. - break; - } - 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); - break; - } - decodeBuffer_.AdvanceHeadPointerShorts(count * channels); - } - if (frameCount <= 0) { - // We must have finished playback. - if (GetEndOfDecoderReached()) { - // If we've finished decoding, clear the buffer - so we will terminate. - ClearDecodeBuffer(); - } - return false; - } - - // Get a free playing buffer. - int16* playBuffer; - { - android::Mutex::Autolock autoLock(playBufferLock_); - if (freeBuffers_.size() > 0) { - // If we have a free buffer, recycle it. - playBuffer = freeBuffers_.top(); - freeBuffers_.pop(); - } else { - // Otherwise allocate a new one. - playBuffer = new int16[targetFrames_ * channels]; - } - } - - // Try to play the buffer. - for (size_t i = 0; i < frameCount * channels; ++i) { - playBuffer[i] = floatBuffer_[i]; - } - size_t sizeOfPlayBufferInBytes = - frameCount * channels * kNumberOfBytesPerInt16; - SLresult result = ReturnOpenSL(audioPlayerQueue, Enqueue, playBuffer, - sizeOfPlayBufferInBytes); - if (result == SL_RESULT_SUCCESS) { - android::Mutex::Autolock autoLock(playBufferLock_); - playingBuffers_.push(playBuffer); - } else { - LOGE("could not enqueue audio buffer"); - delete[] playBuffer; - } - - return (result == SL_RESULT_SUCCESS); -} - -bool AudioEngine::GetEndOfDecoderReached() { - android::Mutex::Autolock autoLock(lock_); - return finishedDecoding_; -} - -void AudioEngine::SetEndOfDecoderReached() { - android::Mutex::Autolock autoLock(lock_); - finishedDecoding_ = true; -} - -bool AudioEngine::PlayFileDescriptor(int fd, int64 offset, int64 length) { - SLDataLocator_AndroidFD loc_fd = { - SL_DATALOCATOR_ANDROIDFD, fd, offset, length }; - SLDataFormat_MIME format_mime = { - SL_DATAFORMAT_MIME, NULL, SL_CONTAINERTYPE_UNSPECIFIED }; - SLDataSource audioSrc = { &loc_fd, &format_mime }; - return PlayFromThisSource(audioSrc); -} - -bool AudioEngine::PlayUri(const char* uri) { - // Source of audio data for the decoding - SLDataLocator_URI decUri = { SL_DATALOCATOR_URI, - const_cast(reinterpret_cast(uri)) }; - SLDataFormat_MIME decMime = { - SL_DATAFORMAT_MIME, NULL, SL_CONTAINERTYPE_UNSPECIFIED }; - SLDataSource decSource = { &decUri, &decMime }; - return PlayFromThisSource(decSource); -} - -bool AudioEngine::IsDecodeBufferEmpty() { - android::Mutex::Autolock autoLock(decodeBufferLock_); - return decodeBuffer_.GetSizeInBytes() <= 0; -} - -void AudioEngine::ClearDecodeBuffer() { - android::Mutex::Autolock autoLock(decodeBufferLock_); - 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(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(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); - OpenSL(engine, Realize, SL_BOOLEAN_FALSE); - OpenSL(engine, GetInterface, SL_IID_ENGINE, &engineInterface); -} - -SLuint32 AudioEngine::GetSLSampleRate() { - android::Mutex::Autolock autoLock(callbackLock_); - return mSampleRate * 1000; -} - -SLuint32 AudioEngine::GetSLChannels() { - android::Mutex::Autolock autoLock(callbackLock_); - switch (mChannels) { - case 2: - return SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT; - case 1: - return SL_SPEAKER_FRONT_CENTER; - default: - 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, - SLuint32 channelCount, SLuint32 slChannels, SLint32 audioStreamType, 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, channelCount, slSampleRate, - SL_PCMSAMPLEFORMAT_FIXED_16, SL_PCMSAMPLEFORMAT_FIXED_16, - slChannels, SL_BYTEORDER_LITTLEENDIAN}; - SLDataSource playingSrc = {&loc_bufq, &format_pcm}; - SLDataLocator_OutputMix loc_outmix = {SL_DATALOCATOR_OUTPUTMIX, outputMix}; - SLDataSink audioSnk = {&loc_outmix, NULL}; - - // Create the audio player, which will play from the buffer queue and send to - // the output mix. - const size_t playerInterfaceCount = 2; - const SLInterfaceID iids[playerInterfaceCount] = { - SL_IID_ANDROIDSIMPLEBUFFERQUEUE, SL_IID_ANDROIDCONFIGURATION }; - const SLboolean reqs[playerInterfaceCount] = { SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE }; - OpenSL(engineInterface, CreateAudioPlayer, &audioPlayer, &playingSrc, - &audioSnk, playerInterfaceCount, iids, reqs); - setAudioStreamType(audioPlayer, audioStreamType); - 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(); - - SLObjectItf engine; - SLEngineItf engineInterface; - CreateAndRealizeEngine(engine, engineInterface); - - // Define the source and sink for the decoding player: comes from the source - // this method was called with, is sent to another buffer queue. - SLDataLocator_AndroidSimpleBufferQueue decBuffQueue; - decBuffQueue.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE; - decBuffQueue.numBuffers = kNumberOfBuffersInQueue; - // A valid value seems required here but is currently ignored. - SLDataFormat_PCM pcm = {SL_DATAFORMAT_PCM, 1, SL_SAMPLINGRATE_44_1, - SL_PCMSAMPLEFORMAT_FIXED_16, 16, - SL_SPEAKER_FRONT_LEFT, SL_BYTEORDER_LITTLEENDIAN}; - SLDataSink decDest = { &decBuffQueue, &pcm }; - - // Create the decoder with the given source and sink. - const size_t decoderInterfaceCount = 5; - SLObjectItf decoder; - const SLInterfaceID decodePlayerInterfaces[decoderInterfaceCount] = { - SL_IID_ANDROIDSIMPLEBUFFERQUEUE, SL_IID_PREFETCHSTATUS, SL_IID_SEEK, - SL_IID_METADATAEXTRACTION, SL_IID_ANDROIDCONFIGURATION }; - const SLboolean decodePlayerRequired[decoderInterfaceCount] = { - SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE }; - SLDataSource sourceCopy(audioSrc); - OpenSL(engineInterface, CreateAudioPlayer, &decoder, &sourceCopy, &decDest, - decoderInterfaceCount, decodePlayerInterfaces, decodePlayerRequired); - // Not sure if this is necessary, but just in case. - setAudioStreamType(decoder, audioStreamType_); - 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; - 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; - { - 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. - OpenSL(decoderPrefetch, SetCallbackEventsMask, kPrefetchErrorCandidate); - OpenSL(decoderPrefetch, RegisterCallback, PrefetchEventCb, &decoderPrefetch); - - // Seek to the start position. - OpenSL(decoderSeek, SetPosition, startPositionMillis_, SL_SEEKMODE_ACCURATE); - - // 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; - SLAndroidSimpleBufferQueueItf audioPlayerQueue = NULL; - - // The main loop - until we're told to stop: if there is audio data coming - // 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() && 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(), audioStreamType_, outputMix, audioPlayer, - engineInterface); - OpenSL(audioPlayer, GetInterface, SL_IID_PLAY, &audioPlayerPlay); - OpenSL(audioPlayer, GetInterface, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, - &audioPlayerQueue); - OpenSL(audioPlayerQueue, RegisterCallback, PlayingBufferQueueCb, NULL); - ClearRequestStart(); - OpenSL(audioPlayerPlay, SetPlayState, SL_PLAYSTATE_PLAYING); - } - EnqueueMoreAudioIfNecessary(audioPlayerQueue); - usleep(kSleepTimeMicros); - } - - // Delete the audio player and output mix, iff they have been created. - if (audioPlayer != NULL) { - 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; - outputMix = NULL; - } - - // Delete the decoder. - 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. - // OpenSL0(decoderQueue, Clear); - OpenSL(decoderQueue, RegisterCallback, NULL, NULL); - decoderSeek = NULL; - decoderPrefetch = NULL; - decoderQueue = NULL; - OpenSL(decoderPlay, RegisterCallback, NULL, NULL); - VoidOpenSL(decoder, AbortAsyncOperation); - VoidOpenSL(decoder, Destroy); - decoderPlay = NULL; - - // Delete the engine. - VoidOpenSL(engine, Destroy); - engineInterface = NULL; - - return true; -} - -bool AudioEngine::Finished() { - if (GetWasStopRequested()) { - return true; - } - android::Mutex::Autolock autoLock(playBufferLock_); - return playingBuffers_.size() <= 0 && - IsDecodeBufferEmpty() && - GetEndOfDecoderReached(); -} - -bool AudioEngine::GetWasStopRequested() { - android::Mutex::Autolock autoLock(lock_); - return stopRequested_; -} - -bool AudioEngine::GetHasReachedPlayingBuffersLimit() { - android::Mutex::Autolock autoLock(playBufferLock_); - return playingBuffers_.size() >= maxPlayBufferCount_; -} - -void AudioEngine::EnqueueMoreAudioIfNecessary( - SLAndroidSimpleBufferQueueItf audioPlayerQueue) { - bool keepEnqueueing = true; - while (audioPlayerQueue != NULL && - !GetWasStopRequested() && - !IsDecodeBufferEmpty() && - !GetHasReachedPlayingBuffersLimit() && - keepEnqueueing) { - keepEnqueueing = EnqueueNextBufferOfAudio(audioPlayerQueue); - } -} - -bool AudioEngine::DecodeBufferTooFull() { - android::Mutex::Autolock autoLock(decodeBufferLock_); - return decodeBuffer_.IsTooLarge(); -} - -// **************************************************************************** -// Code for handling the static callbacks. - -void AudioEngine::PlayingBufferQueueCallback() { - // The head playing buffer is done, move it to the free list. - android::Mutex::Autolock autoLock(playBufferLock_); - if (playingBuffers_.size() > 0) { - freeBuffers_.push(playingBuffers_.front()); - playingBuffers_.pop(); - } -} - -void AudioEngine::PrefetchEventCallback( - SLPrefetchStatusItf caller, SLuint32 event) { - // If there was a problem during decoding, then signal the end. - SLpermille level = 0; - SLuint32 status; - OpenSL(caller, GetFillLevel, &level); - OpenSL(caller, GetPrefetchStatus, &status); - if ((kPrefetchErrorCandidate == (event & kPrefetchErrorCandidate)) && - (level == 0) && - (status == SL_PREFETCHSTATUS_UNDERFLOW)) { - LOGI("prefetcheventcallback error while prefetching data"); - SetEndOfDecoderReached(); - } - if (SL_PREFETCHSTATUS_SUFFICIENTDATA == event) { - // android::Mutex::Autolock autoLock(prefetchLock_); - // prefetchCondition_.broadcast(); - } -} - -void AudioEngine::DecodingBufferQueueCallback( - SLAndroidSimpleBufferQueueItf queueItf, void *context) { - if (GetWasStopRequested()) { - return; - } - - CallbackContext *pCntxt; - { - android::Mutex::Autolock autoLock(callbackLock_); - pCntxt = reinterpret_cast(context); - } - { - android::Mutex::Autolock autoLock(decodeBufferLock_); - decodeBuffer_.AddData(pCntxt->pData, kBufferSizeInBytes); - } - - 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); - } - - OpenSL(queueItf, Enqueue, pCntxt->pData, kBufferSizeInBytes); - - // Increase data pointer by buffer size - pCntxt->pData += kBufferSizeInBytes; - if (pCntxt->pData >= pCntxt->pDataBase + - (kNumberOfBuffersInQueue * kBufferSizeInBytes)) { - pCntxt->pData = pCntxt->pDataBase; - } - - // If we get too much data into the decoder, - // sleep until the playback catches up. - while (!GetWasStopRequested() && DecodeBufferTooFull()) { - usleep(kSleepTimeMicros); - } -} - -void AudioEngine::DecodingEventCallback(SLPlayItf, SLuint32 event) { - if (SL_PLAYEVENT_HEADATEND & event) { - SetEndOfDecoderReached(); - } -} diff --git a/variablespeed/jni/variablespeed.h b/variablespeed/jni/variablespeed.h deleted file mode 100644 index 74710e5..0000000 --- a/variablespeed/jni/variablespeed.h +++ /dev/null @@ -1,167 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef FRAMEWORKS_EX_VARIABLESPEED_JNI_VARIABLESPEED_H_ -#define FRAMEWORKS_EX_VARIABLESPEED_JNI_VARIABLESPEED_H_ - -#include - -#include -#include -#include - -#include -#include - -#include -#include - -#include -#include - -namespace video_editing { - class SolaTimeScaler; -} - -// This is the audio engine class. -// It forms the bulk of the variablespeed library. -// It should not be used directly, but rather used indirectly from the java -// native methods. -class AudioEngine { - public: - AudioEngine(size_t targetFrames, float windowDuration, - float windowOverlapDuration, size_t maxPlayBufferCount, - float initialRate, size_t decodeInitialSize, size_t decodeMaxSize, - size_t startPositionMillis, int audioStreamType); - virtual ~AudioEngine(); - - bool PlayUri(const char* uri); - bool PlayFileDescriptor(int fd, int64 offset, int64 length); - void SetVariableSpeed(float speed); - void RequestStart(); - void RequestStop(); - int GetCurrentPosition(); - int GetTotalDuration(); - - void DecodingBufferQueueCallback( - SLAndroidSimpleBufferQueueItf queueItf, void *context); - void DecodingEventCallback(SLPlayItf caller, SLuint32 event); - void PrefetchEventCallback(SLPrefetchStatusItf caller, SLuint32 event); - void PlayingBufferQueueCallback(); - - static AudioEngine* GetEngine(); - static void SetEngine(AudioEngine* engine); - static bool CompareAndSetEngine(AudioEngine* expect, AudioEngine* update); - static void DeleteEngine(); - - private: - bool PlayFromThisSource(const SLDataSource& audioSrc); - void EnqueueMoreAudioIfNecessary(SLAndroidSimpleBufferQueueItf bufferQueue); - bool EnqueueNextBufferOfAudio(SLAndroidSimpleBufferQueueItf bufferQueue); - void PrefetchDurationSampleRateAndChannels( - SLPlayItf playItf, SLPrefetchStatusItf prefetchItf); - video_editing::SolaTimeScaler* GetTimeScaler(); - bool Finished(); - bool GetWasStartRequested(); - bool GetWasStopRequested(); - void ClearRequestStart(); - void SetEndOfDecoderReached(); - bool GetEndOfDecoderReached(); - bool DecodeBufferTooFull(); - void ClearDecodeBuffer(); - bool IsDecodeBufferEmpty(); - bool GetHasReachedPlayingBuffersLimit(); - bool HasSampleRateAndChannels(); - SLuint32 GetSLSampleRate(); - SLuint32 GetSLChannels(); - SLuint32 GetChannelCount(); - - // The single global audio engine instance. - static AudioEngine* audioEngine_; - - // Protects access to the shared decode buffer. - android::Mutex decodeBufferLock_; - // Buffer into which we put the audio data as we decode. - // Protected by decodeBufferLock_. - DecodeBuffer decodeBuffer_; - - // Protects access to the playingBuffers_ and freeBuffers_. - android::Mutex playBufferLock_; - // The buffers we're using for playback. - std::queue playingBuffers_; - std::stack freeBuffers_; - - // The time scaler. - video_editing::SolaTimeScaler* timeScaler_; - - // The frame buffer, used for converting between PCM data and float for - // time scaler. - float* floatBuffer_; - float* injectBuffer_; - - // 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_; - size_t maxPlayBufferCount_; - float initialRate_; - size_t startPositionMillis_; - // The type of audio stream as defined by the STREAM_XXX constants in - // android.media.AudioManager. These constant values actually match the - // corresponding SL_ANDROID_STREAM_XXX constants defined by - // include/SLES/OpenSLES_AndroidConfiguration.h - int audioStreamType_; - - // The prefetch callback signal, for letting the prefetch callback method - // indicate when it is done. - android::Mutex prefetchLock_; - android::Condition prefetchCondition_; - - // Protects access to the CallbackContext object. - // I don't believe this to be necessary, I think that it's thread-confined, - // but it also won't do any harm. - android::Mutex callbackLock_; - - // Protects access to the shared member variables below. - android::Mutex lock_; - // Protected by lock_. - // 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. - bool startRequested_; - // Protected by lock_. - // Set externally via RequestStop(), this tells us top stop playing - // and therefore shut everything down. - bool stopRequested_; - // Protected by lock_. - // This is set to true once we reach the end of the decoder stream. - bool finishedDecoding_; - - DISALLOW_COPY_AND_ASSIGN(AudioEngine); -}; - -#endif // FRAMEWORKS_EX_VARIABLESPEED_JNI_VARIABLESPEED_H_ diff --git a/variablespeed/src/com/android/ex/variablespeed/EngineParameters.java b/variablespeed/src/com/android/ex/variablespeed/EngineParameters.java deleted file mode 100644 index 1cc4012..0000000 --- a/variablespeed/src/com/android/ex/variablespeed/EngineParameters.java +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.ex.variablespeed; - -import android.media.AudioManager; - -import javax.annotation.concurrent.Immutable; -import javax.annotation.concurrent.NotThreadSafe; - -/** - * Encapsulates the parameters required to configure the audio engine. - *

- * You should not need to use this class directly, it exists for the benefit of - * this package and the classes contained therein. - */ -@Immutable -/*package*/ final class EngineParameters { - private final int mTargetFrames; - private final int mMaxPlayBufferCount; - private final float mWindowDuration; - private final float mWindowOverlapDuration; - private final float mInitialRate; - private final int mDecodeBufferInitialSize; - private final int mDecodeBufferMaxSize; - private final int mStartPositionMillis; - private final int mAudioStreamType; - - public int getTargetFrames() { - return mTargetFrames; - } - - public int getMaxPlayBufferCount() { - return mMaxPlayBufferCount; - } - - public float getWindowDuration() { - return mWindowDuration; - } - - public float getWindowOverlapDuration() { - return mWindowOverlapDuration; - } - - public float getInitialRate() { - return mInitialRate; - } - - public int getDecodeBufferInitialSize() { - return mDecodeBufferInitialSize; - } - - public int getDecodeBufferMaxSize() { - return mDecodeBufferMaxSize; - } - - public int getStartPositionMillis() { - return mStartPositionMillis; - } - - public int getAudioStreamType() { - return mAudioStreamType; - } - - private EngineParameters(int targetFrames, int maxPlayBufferCount, float windowDuration, - float windowOverlapDuration, float initialRate, int decodeBufferInitialSize, - int decodeBufferMaxSize, int startPositionMillis, int audioStreamType) { - mTargetFrames = targetFrames; - mMaxPlayBufferCount = maxPlayBufferCount; - mWindowDuration = windowDuration; - mWindowOverlapDuration = windowOverlapDuration; - mInitialRate = initialRate; - mDecodeBufferInitialSize = decodeBufferInitialSize; - mDecodeBufferMaxSize = decodeBufferMaxSize; - mStartPositionMillis = startPositionMillis; - mAudioStreamType = audioStreamType; - } - - /** - * We use the builder pattern to construct an {@link EngineParameters} - * object. - *

- * This class is not thread safe, you should confine its use to one thread - * or provide your own synchronization. - */ - @NotThreadSafe - public static class Builder { - private int mTargetFrames = 1000; - private int mMaxPlayBufferCount = 2; - private float mWindowDuration = 0.08f; - private float mWindowOverlapDuration = 0.008f; - private float mInitialRate = 1.0f; - private int mDecodeBufferInitialSize = 5 * 1024; - private int mDecodeBufferMaxSize = 20 * 1024; - private int mStartPositionMillis = 0; - private int mAudioStreamType = AudioManager.STREAM_MUSIC; - - public EngineParameters build() { - return new EngineParameters(mTargetFrames, mMaxPlayBufferCount, - mWindowDuration, mWindowOverlapDuration, mInitialRate, - mDecodeBufferInitialSize, mDecodeBufferMaxSize, mStartPositionMillis, - mAudioStreamType); - } - - public Builder maxPlayBufferCount(int maxPlayBufferCount) { - mMaxPlayBufferCount = maxPlayBufferCount; - return this; - } - - public Builder windowDuration(int windowDuration) { - mWindowDuration = windowDuration; - return this; - } - - public Builder windowOverlapDuration(int windowOverlapDuration) { - mWindowOverlapDuration = windowOverlapDuration; - return this; - } - - public Builder initialRate(float initialRate) { - mInitialRate = initialRate; - return this; - } - - public Builder decodeBufferInitialSize(int decodeBufferInitialSize) { - mDecodeBufferInitialSize = decodeBufferInitialSize; - return this; - } - - public Builder decodeBufferMaxSize(int decodeBufferMaxSize) { - mDecodeBufferMaxSize = decodeBufferMaxSize; - return this; - } - - public Builder startPositionMillis(int startPositionMillis) { - mStartPositionMillis = startPositionMillis; - return this; - } - - public Builder audioStreamType(int audioStreamType) { - mAudioStreamType = audioStreamType; - return this; - } - } -} diff --git a/variablespeed/src/com/android/ex/variablespeed/MediaPlayerDataSource.java b/variablespeed/src/com/android/ex/variablespeed/MediaPlayerDataSource.java deleted file mode 100644 index 1c6a8cb..0000000 --- a/variablespeed/src/com/android/ex/variablespeed/MediaPlayerDataSource.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.ex.variablespeed; - -import android.content.Context; -import android.media.MediaPlayer; -import android.net.Uri; - -import java.io.IOException; - -import javax.annotation.concurrent.Immutable; - -/** - * Encapsulates the data source for a media player. - *

- * Is used to make the setting of the data source for a - * {@link android.media.MediaPlayer} easier, or the calling of the correct - * {@link VariableSpeedNative} method done correctly. You should not use this class - * directly, it is for the benefit of the {@link VariableSpeed} implementation. - */ -@Immutable -/*package*/ class MediaPlayerDataSource { - private final Context mContext; - private final Uri mUri; - private final String mPath; - - public MediaPlayerDataSource(Context context, Uri intentUri) { - mContext = context; - mUri = intentUri; - mPath = null; - } - - public MediaPlayerDataSource(String path) { - mContext = null; - mUri = null; - mPath = path; - } - - public void setAsSourceFor(MediaPlayer mediaPlayer) throws IOException { - if (mContext != null) { - mediaPlayer.setDataSource(mContext, mUri); - } else { - mediaPlayer.setDataSource(mPath); - } - } - - public void playNative() throws IOException { - if (mContext != null) { - VariableSpeedNative.playFromContext(mContext, mUri); - } else { - VariableSpeedNative.playUri(mPath); - } - } -} diff --git a/variablespeed/src/com/android/ex/variablespeed/MediaPlayerProxy.java b/variablespeed/src/com/android/ex/variablespeed/MediaPlayerProxy.java deleted file mode 100644 index 3b7b576..0000000 --- a/variablespeed/src/com/android/ex/variablespeed/MediaPlayerProxy.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.ex.variablespeed; - -import android.content.Context; -import android.media.MediaPlayer; -import android.net.Uri; - -import java.io.IOException; - -/** - * Interface that supports a subset of the operations on {@link android.media.MediaPlayer}. - * - *

This subset is arbitrarily defined - at the moment it is the subset that the voicemail - * playback requires.

- * - *

This interface exists to make alternate implementations to the standard media player - * swappable, as well as making it much easier to test code that directly uses a media player. - */ -public interface MediaPlayerProxy { - void setOnErrorListener(MediaPlayer.OnErrorListener listener); - void setOnCompletionListener(MediaPlayer.OnCompletionListener listener); - void release(); - void reset(); - void setDataSource(String path) throws IllegalStateException, IOException; - void setDataSource(Context context, Uri intentUri) throws IllegalStateException, IOException; - void prepare() throws IOException; - int getDuration(); - void seekTo(int startPosition); - void start(); - boolean isReadyToPlay(); - boolean isPlaying(); - int getCurrentPosition(); - void pause(); - void setAudioStreamType(int streamType); -} diff --git a/variablespeed/src/com/android/ex/variablespeed/SingleThreadedMediaPlayerProxy.java b/variablespeed/src/com/android/ex/variablespeed/SingleThreadedMediaPlayerProxy.java deleted file mode 100644 index c9a9741..0000000 --- a/variablespeed/src/com/android/ex/variablespeed/SingleThreadedMediaPlayerProxy.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.ex.variablespeed; - -import android.content.Context; -import android.media.MediaPlayer; -import android.net.Uri; - -import java.io.IOException; - -/** - * Simple wrapper around a {@link MediaPlayerProxy}, guaranteeing that every call made to the - * MediaPlayerProxy is single-threaded. - */ -public class SingleThreadedMediaPlayerProxy implements MediaPlayerProxy { - private final MediaPlayerProxy mDelegate; - - public SingleThreadedMediaPlayerProxy(MediaPlayerProxy delegate) { - mDelegate = delegate; - } - - @Override - public synchronized void setOnErrorListener(MediaPlayer.OnErrorListener listener) { - mDelegate.setOnErrorListener(listener); - } - - @Override - public synchronized void setOnCompletionListener(MediaPlayer.OnCompletionListener listener) { - mDelegate.setOnCompletionListener(listener); - } - - @Override - public synchronized void release() { - mDelegate.release(); - } - - @Override - public synchronized void reset() { - mDelegate.reset(); - } - - @Override - public synchronized void setDataSource(String path) throws IllegalStateException, IOException { - mDelegate.setDataSource(path); - } - - @Override - public synchronized void setDataSource(Context context, Uri intentUri) - throws IllegalStateException, IOException { - mDelegate.setDataSource(context, intentUri); - } - - @Override - public synchronized void prepare() throws IOException { - mDelegate.prepare(); - } - - @Override - public synchronized int getDuration() { - return mDelegate.getDuration(); - } - - @Override - public synchronized void seekTo(int startPosition) { - mDelegate.seekTo(startPosition); - } - - @Override - public synchronized void start() { - mDelegate.start(); - } - - @Override - public synchronized boolean isReadyToPlay() { - return mDelegate.isReadyToPlay(); - } - - @Override - public synchronized boolean isPlaying() { - return mDelegate.isPlaying(); - } - - @Override - public synchronized int getCurrentPosition() { - return mDelegate.getCurrentPosition(); - } - - public void setVariableSpeed(float rate) { - ((VariableSpeed) mDelegate).setVariableSpeed(rate); - } - - @Override - public synchronized void pause() { - mDelegate.pause(); - } - - @Override - public void setAudioStreamType(int streamType) { - mDelegate.setAudioStreamType(streamType); - } -} diff --git a/variablespeed/src/com/android/ex/variablespeed/VariableSpeed.java b/variablespeed/src/com/android/ex/variablespeed/VariableSpeed.java deleted file mode 100644 index e44a375..0000000 --- a/variablespeed/src/com/android/ex/variablespeed/VariableSpeed.java +++ /dev/null @@ -1,410 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.ex.variablespeed; - -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; -import java.util.concurrent.Executor; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; - -import javax.annotation.concurrent.GuardedBy; -import javax.annotation.concurrent.ThreadSafe; - -/** - * This class behaves in a similar fashion to the MediaPlayer, but by using - * native code it is able to use variable-speed playback. - *

- * This class is thread-safe. It's not yet perfect though, see the unit tests - * for details - there is insufficient testing for the concurrent logic. You are - * probably best advised to use thread confinment until the unit tests are more - * complete with regards to threading. - *

- * The easiest way to ensure that calls to this class are not made concurrently - * (besides only ever accessing it from one thread) is to wrap it in a - * {@link SingleThreadedMediaPlayerProxy}, designed just for this purpose. - */ -@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; - @GuardedBy("lock") private boolean mIsPrepared; - @GuardedBy("lock") private boolean mHasDuration; - @GuardedBy("lock") private boolean mHasStartedPlayback; - @GuardedBy("lock") private CountDownLatch mEngineInitializedLatch; - @GuardedBy("lock") private CountDownLatch mPlaybackFinishedLatch; - @GuardedBy("lock") private boolean mHasBeenReleased = true; - @GuardedBy("lock") private boolean mIsReadyToReUse = true; - @GuardedBy("lock") private boolean mSkipCompletionReport; - @GuardedBy("lock") private int mStartPosition; - @GuardedBy("lock") private float mCurrentPlaybackRate = 1.0f; - @GuardedBy("lock") private int mDuration; - @GuardedBy("lock") private MediaPlayer.OnCompletionListener mCompletionListener; - @GuardedBy("lock") private int mAudioStreamType; - - 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 static MediaPlayerProxy createVariableSpeed(Executor executor) - throws UnsupportedOperationException { - return new SingleThreadedMediaPlayerProxy(new VariableSpeed(executor)); - } - - @Override - public void setOnCompletionListener(MediaPlayer.OnCompletionListener listener) { - synchronized (lock) { - check(!mHasBeenReleased, "has been released, reset before use"); - mCompletionListener = listener; - } - } - - @Override - public void setOnErrorListener(MediaPlayer.OnErrorListener listener) { - synchronized (lock) { - check(!mHasBeenReleased, "has been released, reset before use"); - // TODO: I haven't actually added any error listener code. - } - } - - @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; - } - } - - private boolean hasEngineBeenInitialized() { - return mEngineInitializedLatch.getCount() <= 0; - } - - private boolean hasPlaybackFinished() { - return mPlaybackFinishedLatch.getCount() <= 0; - } - - /** - * 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); - } - } - - private void waitForLatch(CountDownLatch latch) { - try { - boolean success = latch.await(1, TimeUnit.SECONDS); - if (!success) { - reportException(new TimeoutException("waited too long")); - } - } catch (InterruptedException e) { - // Preserve the interrupt status, though this is unexpected. - Thread.currentThread().interrupt(); - reportException(e); - } - } - - @Override - public void setDataSource(Context context, Uri intentUri) { - checkNotNull(context, "context"); - checkNotNull(intentUri, "intentUri"); - innerSetDataSource(new MediaPlayerDataSource(context, intentUri)); - } - - @Override - public void setDataSource(String path) { - checkNotNull(path, "path"); - innerSetDataSource(new MediaPlayerDataSource(path)); - } - - private void innerSetDataSource(MediaPlayerDataSource source) { - checkNotNull(source, "source"); - synchronized (lock) { - check(!mHasBeenReleased, "has been released, reset before use"); - check(mDataSource == null, "cannot setDataSource more than once"); - mDataSource = source; - } - } - - @Override - public void reset() { - boolean requiresRelease; - synchronized (lock) { - requiresRelease = !mHasBeenReleased; - } - if (requiresRelease) { - release(); - } - synchronized (lock) { - check(mHasBeenReleased && mIsReadyToReUse, "to re-use, must call reset after release"); - mDataSource = null; - mIsPrepared = false; - mHasDuration = false; - mHasStartedPlayback = false; - mEngineInitializedLatch = new CountDownLatch(1); - mPlaybackFinishedLatch = new CountDownLatch(1); - mHasBeenReleased = false; - mIsReadyToReUse = false; - mSkipCompletionReport = false; - mStartPosition = 0; - mDuration = 0; - } - } - - @Override - public void prepare() throws IOException { - MediaPlayerDataSource dataSource; - int audioStreamType; - synchronized (lock) { - check(!mHasBeenReleased, "has been released, reset before use"); - check(mDataSource != null, "must setDataSource before you prepare"); - check(!mIsPrepared, "cannot prepare more than once"); - mIsPrepared = true; - dataSource = mDataSource; - audioStreamType = mAudioStreamType; - } - // NYI This should become another executable that we can wait on. - MediaPlayer mediaPlayer = new MediaPlayer(); - mediaPlayer.setAudioStreamType(audioStreamType); - dataSource.setAsSourceFor(mediaPlayer); - mediaPlayer.prepare(); - synchronized (lock) { - check(!mHasDuration, "can't have duration, this is impossible"); - mHasDuration = true; - mDuration = mediaPlayer.getDuration(); - } - mediaPlayer.release(); - } - - @Override - public int getDuration() { - synchronized (lock) { - check(!mHasBeenReleased, "has been released, reset before use"); - check(mHasDuration, "you haven't called prepare, can't get the duration"); - return mDuration; - } - } - - @Override - public void seekTo(int startPosition) { - boolean currentlyPlaying; - MediaPlayerDataSource dataSource; - synchronized (lock) { - check(!mHasBeenReleased, "has been released, reset before use"); - check(mHasDuration, "you can't seek until you have prepared"); - currentlyPlaying = mHasStartedPlayback && !hasPlaybackFinished(); - mStartPosition = Math.min(startPosition, mDuration); - dataSource = mDataSource; - } - if (currentlyPlaying) { - stopAndStartPlayingAgain(dataSource); - } - } - - private void stopAndStartPlayingAgain(MediaPlayerDataSource source) { - stopCurrentPlayback(); - reset(); - innerSetDataSource(source); - try { - prepare(); - } catch (IOException e) { - reportException(e); - return; - } - start(); - return; - } - - private void reportException(Exception e) { - Log.e(TAG, "playback error:", e); - } - - @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) - .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); - } - } - - /** A Runnable capable of driving the native audio playback methods. */ - private final class PlaybackRunnable implements Runnable { - private final MediaPlayerDataSource mInnerSource; - - public PlaybackRunnable(MediaPlayerDataSource source) { - mInnerSource = source; - } - - @Override - public void run() { - try { - mInnerSource.playNative(); - } catch (IOException e) { - Log.e(TAG, "error playing audio", e); - } - MediaPlayer.OnCompletionListener completionListener; - boolean skipThisCompletionReport; - synchronized (lock) { - completionListener = mCompletionListener; - skipThisCompletionReport = mSkipCompletionReport; - mPlaybackFinishedLatch.countDown(); - } - if (!skipThisCompletionReport && completionListener != null) { - completionListener.onCompletion(null); - } - } - } - - @Override - public boolean isReadyToPlay() { - synchronized (lock) { - return !mHasBeenReleased && mHasDuration; - } - } - - @Override - public boolean isPlaying() { - synchronized (lock) { - return isReadyToPlay() && mHasStartedPlayback && !hasPlaybackFinished(); - } - } - - @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; - } - } - - @Override - public void pause() { - synchronized (lock) { - check(!mHasBeenReleased, "has been released, reset before use"); - } - stopCurrentPlayback(); - } - - 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; - } - } - - private void check(boolean condition, String exception) { - if (!condition) { - throw new IllegalStateException(exception); - } - } - - private void checkNotNull(Object argument, String argumentName) { - if (argument == null) { - throw new IllegalArgumentException(argumentName + " must not be null"); - } - } - - @Override - public void setAudioStreamType(int audioStreamType) { - synchronized (lock) { - mAudioStreamType = audioStreamType; - } - } -} diff --git a/variablespeed/src/com/android/ex/variablespeed/VariableSpeedNative.java b/variablespeed/src/com/android/ex/variablespeed/VariableSpeedNative.java deleted file mode 100644 index 07195db..0000000 --- a/variablespeed/src/com/android/ex/variablespeed/VariableSpeedNative.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.ex.variablespeed; - -import com.android.common.io.MoreCloseables; - -import android.content.Context; -import android.content.res.AssetFileDescriptor; -import android.net.Uri; - -import java.io.FileDescriptor; -import java.io.FileNotFoundException; -import java.lang.reflect.Field; - -/** - * Provides all the native calls through to the underlying audio library. - *

- * You should not use this class directly. Prefer to use the {@link VariableSpeed} - * class instead. - */ -/*package*/ class VariableSpeedNative { - /*package*/ static void loadLibrary() throws UnsatisfiedLinkError, SecurityException { - System.loadLibrary("variablespeed"); - } - - /*package*/ static boolean playFromContext(Context context, Uri uri) - throws FileNotFoundException { - AssetFileDescriptor afd = context.getContentResolver().openAssetFileDescriptor(uri, "r"); - try { - FileDescriptor fileDescriptor = afd.getFileDescriptor(); - Field descriptorField = fileDescriptor.getClass().getDeclaredField("descriptor"); - descriptorField.setAccessible(true); - int fd = descriptorField.getInt(fileDescriptor); - VariableSpeedNative.playFileDescriptor(fd, afd.getStartOffset(), afd.getLength()); - return true; - } catch (SecurityException e) { - // Fall through. - } catch (NoSuchFieldException e) { - // Fall through. - } catch (IllegalArgumentException e) { - // Fall through. - } catch (IllegalAccessException e) { - // Fall through. - } finally { - MoreCloseables.closeQuietly(afd); - } - return false; - } - - /*package*/ static native void playUri(String uri); - - /*package*/ static native void playFileDescriptor(int fd, long offset, long length); - - /*package*/ static native void setVariableSpeed(float speed); - - /*package*/ static native void startPlayback(); - - /*package*/ static native void stopPlayback(); - - /*package*/ static native void shutdownEngine(); - - /*package*/ static native int getCurrentPosition(); - - /*package*/ static native int getTotalDuration(); - - /*package*/ static void initializeEngine(EngineParameters params) { - initializeEngine(params.getTargetFrames(), - params.getWindowDuration(), params.getWindowOverlapDuration(), - params.getMaxPlayBufferCount(), params.getInitialRate(), - params.getDecodeBufferInitialSize(), params.getDecodeBufferMaxSize(), - params.getStartPositionMillis(), params.getAudioStreamType()); - } - - private static native void initializeEngine(int targetFrames, - float windowDuration, float windowOverlapDuration, int maxPlayBufferCount, - float initialRate, int decodeBufferInitialSize, int decodeBufferMaxSize, - int startPositionMillis, int audioStreamType); -} diff --git a/variablespeed/tests/Android.mk b/variablespeed/tests/Android.mk deleted file mode 100644 index fe386a2..0000000 --- a/variablespeed/tests/Android.mk +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright (C) 2011 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -LOCAL_PATH := $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_CERTIFICATE := shared -LOCAL_JAVA_LIBRARIES := android.test.runner -LOCAL_MODULE_TAGS := tests -LOCAL_PACKAGE_NAME := AndroidExVariablespeedTests -LOCAL_SDK_VERSION := current -LOCAL_SRC_FILES := $(call all-java-files-under, src) -LOCAL_STATIC_JAVA_LIBRARIES := android-ex-variablespeed -LOCAL_REQUIRED_MODULES := libvariablespeed -LOCAL_PROGUARD_ENABLED := disabled - -include $(BUILD_PACKAGE) diff --git a/variablespeed/tests/AndroidManifest.xml b/variablespeed/tests/AndroidManifest.xml deleted file mode 100644 index a7d2ed1..0000000 --- a/variablespeed/tests/AndroidManifest.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - - - diff --git a/variablespeed/tests/assets/README.txt b/variablespeed/tests/assets/README.txt deleted file mode 100644 index 3e69968..0000000 --- a/variablespeed/tests/assets/README.txt +++ /dev/null @@ -1,4 +0,0 @@ -Files quick_test_recording.mp3 and count_and_test.3gpp are copyright 2011 by -Hugo Hudson and are licensed under a -Creative Commons Attribution 3.0 Unported License: - http://creativecommons.org/licenses/by/3.0/ diff --git a/variablespeed/tests/assets/count_and_test.3gpp b/variablespeed/tests/assets/count_and_test.3gpp deleted file mode 100644 index c71a423..0000000 Binary files a/variablespeed/tests/assets/count_and_test.3gpp and /dev/null differ diff --git a/variablespeed/tests/assets/fake_voicemail.mp3 b/variablespeed/tests/assets/fake_voicemail.mp3 deleted file mode 100644 index 23fc415..0000000 Binary files a/variablespeed/tests/assets/fake_voicemail.mp3 and /dev/null differ diff --git a/variablespeed/tests/assets/fake_voicemail2.mp3 b/variablespeed/tests/assets/fake_voicemail2.mp3 deleted file mode 100644 index 854186d..0000000 Binary files a/variablespeed/tests/assets/fake_voicemail2.mp3 and /dev/null differ diff --git a/variablespeed/tests/assets/quick_test_recording.mp3 b/variablespeed/tests/assets/quick_test_recording.mp3 deleted file mode 100644 index ad7cb9c..0000000 Binary files a/variablespeed/tests/assets/quick_test_recording.mp3 and /dev/null differ diff --git a/variablespeed/tests/src/com/android/ex/variablespeed/AwaitableCompletionListener.java b/variablespeed/tests/src/com/android/ex/variablespeed/AwaitableCompletionListener.java deleted file mode 100644 index ef2648c..0000000 --- a/variablespeed/tests/src/com/android/ex/variablespeed/AwaitableCompletionListener.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.ex.variablespeed; - -import android.media.MediaPlayer; - -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; - -import javax.annotation.concurrent.ThreadSafe; - -// TODO: There is sufficent similarity between this and the awaitable error listener that I should -// extract a common base class. -/** Implementation of {@link MediaPlayer.OnErrorListener} that we can wait for in tests. */ -@ThreadSafe -public class AwaitableCompletionListener implements MediaPlayer.OnCompletionListener { - private final BlockingQueue mQueue = new LinkedBlockingQueue(); - - @Override - public void onCompletion(MediaPlayer mp) { - try { - mQueue.put(new Object()); - } catch (InterruptedException e) { - // This should not happen in practice, the queue is unbounded so this method will not - // block. - // If this thread is using interrupt to shut down, preserve interrupt status and return. - Thread.currentThread().interrupt(); - } - } - - public void awaitOneCallback(long timeout, TimeUnit unit) throws InterruptedException, - TimeoutException { - if (mQueue.poll(timeout, unit) == null) { - throw new TimeoutException(); - } - } - - public void assertNoMoreCallbacks() { - if (mQueue.peek() != null) { - throw new IllegalStateException("there was an unexpected callback on the queue"); - } - } -} diff --git a/variablespeed/tests/src/com/android/ex/variablespeed/AwaitableErrorListener.java b/variablespeed/tests/src/com/android/ex/variablespeed/AwaitableErrorListener.java deleted file mode 100644 index bf5fb42..0000000 --- a/variablespeed/tests/src/com/android/ex/variablespeed/AwaitableErrorListener.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.ex.variablespeed; - -import android.media.MediaPlayer; - -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; - -import javax.annotation.concurrent.ThreadSafe; - -/** Implementation of {@link MediaPlayer.OnCompletionListener} that we can wait for in tests. */ -@ThreadSafe -public class AwaitableErrorListener implements MediaPlayer.OnErrorListener { - private final BlockingQueue mQueue = new LinkedBlockingQueue(); - private volatile boolean mOnErrorReturnValue = true; - - @Override - public boolean onError(MediaPlayer mp, int what, int extra) { - addAnObjectToTheQueue(); - return mOnErrorReturnValue; - } - - public void setOnErrorReturnValue(boolean value) { - mOnErrorReturnValue = value; - } - - private void addAnObjectToTheQueue() { - try { - mQueue.put(new Object()); - } catch (InterruptedException e) { - // This should not happen in practice, the queue is unbounded so this method will not - // block. - // If this thread is using interrupt to shut down, preserve interrupt status and return. - Thread.currentThread().interrupt(); - } - } - - public void awaitOneCallback(long timeout, TimeUnit unit) throws InterruptedException, - TimeoutException { - if (mQueue.poll(timeout, unit) == null) { - throw new TimeoutException(); - } - } - - public void assertNoMoreCallbacks() { - if (mQueue.peek() != null) { - throw new IllegalStateException("there was an unexpected callback on the queue"); - } - } -} diff --git a/variablespeed/tests/src/com/android/ex/variablespeed/DynamicProxy.java b/variablespeed/tests/src/com/android/ex/variablespeed/DynamicProxy.java deleted file mode 100644 index 429f2cc..0000000 --- a/variablespeed/tests/src/com/android/ex/variablespeed/DynamicProxy.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.ex.variablespeed; - -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.lang.reflect.Proxy; - -/** - * Contains a utility method for adapting a given interface against a real implementation. - *

- * This class is thead-safe. - */ -public class DynamicProxy { - /** - * Dynamically adapts a given interface against a delegate object. - *

- * For the given {@code clazz} object, which should be an interface, we return a new dynamic - * proxy object implementing that interface, which will forward all method calls made on the - * interface onto the delegate object. - *

- * In practice this means that you can make it appear as though {@code delegate} implements the - * {@code clazz} interface, without this in practice being the case. As an example, if you - * create an interface representing the {@link android.media.MediaPlayer}, you could pass this - * interface in as the first argument, and a real {@link android.media.MediaPlayer} in as the - * second argument, and now calls to the interface will be automatically sent on to the real - * media player. The reason you may be interested in doing this in the first place is that this - * allows you to test classes that have dependencies that are final or cannot be easily mocked. - */ - // This is safe, because we know that proxy instance implements the interface. - @SuppressWarnings("unchecked") - public static T dynamicProxy(Class clazz, final Object delegate) { - InvocationHandler invoke = new InvocationHandler() { - @Override - public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { - try { - return delegate.getClass() - .getMethod(method.getName(), method.getParameterTypes()) - .invoke(delegate, args); - } catch (InvocationTargetException e) { - throw e.getCause(); - } - } - }; - return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class[] { clazz }, invoke); - } -} diff --git a/variablespeed/tests/src/com/android/ex/variablespeed/MediaPlayerProxyTestCase.java b/variablespeed/tests/src/com/android/ex/variablespeed/MediaPlayerProxyTestCase.java deleted file mode 100644 index 37a0cce..0000000 --- a/variablespeed/tests/src/com/android/ex/variablespeed/MediaPlayerProxyTestCase.java +++ /dev/null @@ -1,581 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.ex.variablespeed; - -import com.google.common.io.Closeables; - -import android.content.ContentResolver; -import android.content.ContentValues; -import android.content.res.AssetManager; -import android.net.Uri; -import android.provider.VoicemailContract; -import android.test.InstrumentationTestCase; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.TimeUnit; - -/** - * Base test for checking implementations of {@link MediaPlayerProxy}. - *

- * The purpose behind this class is to collect tests that implementations of - * MediaPlayerProxy should support. - *

- * This allows tests to show that the built-in {@link android.media.MediaPlayer} is performing - * correctly with respect to the contract it provides, i.e. test my understanding of that contract. - *

- * It allows us to test the current {@link VariableSpeed} implementation, and make sure that this - * too corresponds with the MediaPlayer implementation. - *

- * These tests cannot be run on their own - you must provide a concrete subclass of this test case - - * and in that subclass you will provide an implementation of the abstract - * {@link #createTestMediaPlayer()} method to construct the player you would like to test. Every - * test will construct the player in {@link #setUp()} and release it in {@link #tearDown()}. - */ -public abstract class MediaPlayerProxyTestCase extends InstrumentationTestCase { - private static final float ERROR_TOLERANCE_MILLIS = 1000f; - - /** The phone number to use when inserting test data into the content provider. */ - private static final String CONTACT_NUMBER = "01234567890"; - - /** - * A map from filename + mime type to the uri we can use to play from the content provider. - *

- * This is lazily filled in by the {@link #getTestContentUri(String, String)} method. - *

- * This map is keyed from the concatenation of filename and mime type with a "+" separator, it's - * not perfect but it doesn't matter in this test code. - */ - private final Map mContentUriMap = new HashMap(); - - /** The system under test. */ - private MediaPlayerProxy mPlayer; - - private AwaitableCompletionListener mCompletionListener; - private AwaitableErrorListener mErrorListener; - - @Override - protected void setUp() throws Exception { - super.setUp(); - mPlayer = createTestMediaPlayer(); - mCompletionListener = new AwaitableCompletionListener(); - mErrorListener = new AwaitableErrorListener(); - } - - @Override - protected void tearDown() throws Exception { - mCompletionListener = null; - mErrorListener = null; - mPlayer.release(); - mPlayer = null; - cleanupContentUriIfNecessary(); - super.tearDown(); - } - - public abstract MediaPlayerProxy createTestMediaPlayer() throws Exception; - - /** Annotation to indicate that test should throw an {@link IllegalStateException}. */ - @Retention(RetentionPolicy.RUNTIME) - public @interface ShouldThrowIllegalStateException { - } - - @Override - protected void runTest() throws Throwable { - // Tests annotated with ShouldThrowIllegalStateException will fail if they don't. - // Tests not annotated this way are run as normal. - if (getClass().getMethod(getName()).isAnnotationPresent( - ShouldThrowIllegalStateException.class)) { - try { - super.runTest(); - fail("Expected this method to throw an IllegalStateException, but it didn't"); - } catch (IllegalStateException e) { - // Expected. - } - } else { - super.runTest(); - } - } - - public void testReleaseMultipleTimesHasNoEffect() throws Exception { - mPlayer.release(); - mPlayer.release(); - } - - public void testResetOnNewlyCreatedObject() throws Exception { - mPlayer.reset(); - } - - public void testSetDataSource() throws Exception { - setDataSourceFromContentProvider(mPlayer, "quick_test_recording.mp3", "audio/mp3"); - } - - @ShouldThrowIllegalStateException - public void testSetDataSourceTwice_ShouldFailWithIllegalState() throws Exception { - setDataSourceFromContentProvider(mPlayer, "quick_test_recording.mp3", "audio/mp3"); - setDataSourceFromContentProvider(mPlayer, "quick_test_recording.mp3", "audio/mp3"); - } - - @ShouldThrowIllegalStateException - public void testSetDataSourceAfterRelease_ShouldFailWithIllegalState() throws Exception { - mPlayer.release(); - setDataSourceFromContentProvider(mPlayer, "quick_test_recording.mp3", "audio/mp3"); - } - - public void testPrepare() throws Exception { - setDataSourceFromContentProvider(mPlayer, "quick_test_recording.mp3", "audio/mp3"); - mPlayer.prepare(); - } - - @ShouldThrowIllegalStateException - public void testPrepareBeforeSetDataSource_ShouldFail() throws Exception { - mPlayer.prepare(); - } - - @ShouldThrowIllegalStateException - public void testPrepareTwice_ShouldFailWithIllegalState() throws Exception { - setDataSourceFromContentProvider(mPlayer, "quick_test_recording.mp3", "audio/mp3"); - mPlayer.prepare(); - mPlayer.prepare(); - } - - public void testStartThenImmediatelyRelease() throws Exception { - setDataSourceFromContentProvider(mPlayer, "quick_test_recording.mp3", "audio/mp3"); - mPlayer.prepare(); - mPlayer.start(); - } - - public void testPlayABitThenRelease() throws Exception { - setDataSourceFromContentProvider(mPlayer, "quick_test_recording.mp3", "audio/mp3"); - mPlayer.prepare(); - mPlayer.start(); - Thread.sleep(2000); - } - - public void testPlayFully() throws Exception { - setDataSourceFromContentProvider(mPlayer, "quick_test_recording.mp3", "audio/mp3"); - mPlayer.prepare(); - mPlayer.setOnCompletionListener(mCompletionListener); - mPlayer.start(); - mCompletionListener.awaitOneCallback(10, TimeUnit.SECONDS); - } - - public void testGetDuration() throws Exception { - setDataSourceFromContentProvider(mPlayer, "quick_test_recording.mp3", "audio/mp3"); - mPlayer.prepare(); - int duration = mPlayer.getDuration(); - assertTrue("duration was " + duration, duration > 0); - mPlayer.setOnCompletionListener(mCompletionListener); - mPlayer.start(); - assertEquals(duration, mPlayer.getDuration()); - mCompletionListener.awaitOneCallback(10, TimeUnit.SECONDS); - assertEquals(duration, mPlayer.getDuration()); - } - - @ShouldThrowIllegalStateException - public void testGetDurationAfterRelease_ShouldFail() throws Exception { - setDataSourceFromContentProvider(mPlayer, "quick_test_recording.mp3", "audio/mp3"); - mPlayer.release(); - mPlayer.getDuration(); - } - - @ShouldThrowIllegalStateException - public void testGetPositionAfterRelease_ShouldFail() throws Exception { - setDataSourceFromContentProvider(mPlayer, "quick_test_recording.mp3", "audio/mp3"); - mPlayer.release(); - mPlayer.getCurrentPosition(); - } - - public void testGetCurrentPosition_ZeroBeforePlaybackBegins() throws Exception { - setDataSourceFromContentProvider(mPlayer, "quick_test_recording.mp3", "audio/mp3"); - assertEquals(0, mPlayer.getCurrentPosition()); - mPlayer.prepare(); - assertEquals(0, mPlayer.getCurrentPosition()); - } - - public void testGetCurrentPosition_DuringPlayback() throws Exception { - setDataSourceFromContentProvider(mPlayer, "quick_test_recording.mp3", "audio/mp3"); - mPlayer.prepare(); - mPlayer.start(); - Thread.sleep(2000); - assertEquals(2000, mPlayer.getCurrentPosition(), ERROR_TOLERANCE_MILLIS); - } - - public void testGetCurrentPosition_FinishedPlaying() throws Exception { - setDataSourceFromContentProvider(mPlayer, "quick_test_recording.mp3", "audio/mp3"); - mPlayer.prepare(); - mPlayer.setOnCompletionListener(mCompletionListener); - mPlayer.start(); - mCompletionListener.awaitOneCallback(10, TimeUnit.SECONDS); - assertEquals(mPlayer.getDuration(), mPlayer.getCurrentPosition(), ERROR_TOLERANCE_MILLIS); - } - - public void testGetCurrentPosition_DuringPlaybackWithSeek() throws Exception { - setDataSourceFromContentProvider(mPlayer, "quick_test_recording.mp3", "audio/mp3"); - mPlayer.prepare(); - mPlayer.seekTo(1500); - mPlayer.start(); - Thread.sleep(1500); - assertEquals(3000, mPlayer.getCurrentPosition(), ERROR_TOLERANCE_MILLIS); - } - - public void testSeekHalfWayBeforePlaying() throws Exception { - setDataSourceFromContentProvider(mPlayer, "quick_test_recording.mp3", "audio/mp3"); - mPlayer.prepare(); - assertTrue(mPlayer.getDuration() > 0); - mPlayer.seekTo(mPlayer.getDuration() / 2); - mPlayer.start(); - mPlayer.setOnCompletionListener(mCompletionListener); - mCompletionListener.awaitOneCallback(10, TimeUnit.SECONDS); - } - - public void testHalfWaySeekWithStutteringAudio() throws Exception { - // The audio contained in this file has a stutter if we seek to half way and play. - // It shouldn't have. - setDataSourceFromContentProvider(mPlayer, "fake_voicemail2.mp3", "audio/mp3"); - mPlayer.prepare(); - assertTrue(mPlayer.getDuration() > 0); - mPlayer.seekTo(mPlayer.getDuration() / 2); - mPlayer.start(); - mPlayer.setOnCompletionListener(mCompletionListener); - mCompletionListener.awaitOneCallback(10, TimeUnit.SECONDS); - } - - public void testResetWithoutReleaseAndThenReUse() throws Exception { - setDataSourceFromContentProvider(mPlayer, "quick_test_recording.mp3", "audio/mp3"); - mPlayer.reset(); - setDataSourceFromContentProvider(mPlayer, "quick_test_recording.mp3", "audio/mp3"); - mPlayer.prepare(); - mPlayer.seekTo(mPlayer.getDuration() / 2); - mPlayer.start(); - Thread.sleep(1000); - } - - public void testResetAfterPlaybackThenReUse() throws Exception { - setDataSourceFromContentProvider(mPlayer, "quick_test_recording.mp3", "audio/mp3"); - mPlayer.setOnCompletionListener(mCompletionListener); - mPlayer.prepare(); - mPlayer.start(); - mCompletionListener.awaitOneCallback(10, TimeUnit.SECONDS); - mPlayer.reset(); - setDataSourceFromContentProvider(mPlayer, "quick_test_recording.mp3", "audio/mp3"); - mPlayer.prepare(); - mPlayer.start(); - Thread.sleep(2000); - } - - public void testResetDuringPlaybackThenReUse() throws Exception { - setDataSourceFromContentProvider(mPlayer, "quick_test_recording.mp3", "audio/mp3"); - mPlayer.prepare(); - mPlayer.start(); - Thread.sleep(2000); - mPlayer.reset(); - setDataSourceFromContentProvider(mPlayer, "quick_test_recording.mp3", "audio/mp3"); - mPlayer.prepare(); - mPlayer.start(); - Thread.sleep(2000); - } - - public void testFinishPlayingThenSeekToHalfWayThenPlayAgain() throws Exception { - setDataSourceFromContentProvider(mPlayer, "quick_test_recording.mp3", "audio/mp3"); - mPlayer.prepare(); - mPlayer.setOnCompletionListener(mCompletionListener); - mPlayer.start(); - mCompletionListener.awaitOneCallback(10, TimeUnit.SECONDS); - mPlayer.seekTo(mPlayer.getDuration() / 2); - mPlayer.start(); - mCompletionListener.awaitOneCallback(10, TimeUnit.SECONDS); - } - - public void testPause_DuringPlayback() throws Exception { - setDataSourceFromContentProvider(mPlayer, "quick_test_recording.mp3", "audio/mp3"); - mPlayer.prepare(); - mPlayer.start(); - assertTrue(mPlayer.isPlaying()); - Thread.sleep(2000); - assertTrue(mPlayer.isPlaying()); - mPlayer.pause(); - assertFalse(mPlayer.isPlaying()); - } - - public void testPause_DoesNotInvokeCallback() throws Exception { - setDataSourceFromContentProvider(mPlayer, "quick_test_recording.mp3", "audio/mp3"); - mPlayer.prepare(); - mPlayer.setOnCompletionListener(mCompletionListener); - mPlayer.start(); - mPlayer.pause(); - Thread.sleep(200); - mCompletionListener.assertNoMoreCallbacks(); - } - - public void testReset_DoesNotInvokeCallback() throws Exception { - setDataSourceFromContentProvider(mPlayer, "quick_test_recording.mp3", "audio/mp3"); - mPlayer.prepare(); - mPlayer.setOnCompletionListener(mCompletionListener); - mPlayer.start(); - mPlayer.reset(); - Thread.sleep(200); - mCompletionListener.assertNoMoreCallbacks(); - } - - public void testPause_MultipleTimes() throws Exception { - setDataSourceFromContentProvider(mPlayer, "quick_test_recording.mp3", "audio/mp3"); - mPlayer.prepare(); - mPlayer.start(); - Thread.sleep(2000); - mPlayer.pause(); - mPlayer.pause(); - } - - public void testDoubleStartWaitingForFinish() throws Exception { - setDataSourceFromContentProvider(mPlayer, "quick_test_recording.mp3", "audio/mp3"); - mPlayer.prepare(); - mPlayer.setOnCompletionListener(mCompletionListener); - mPlayer.start(); - mCompletionListener.awaitOneCallback(10, TimeUnit.SECONDS); - mPlayer.start(); - mCompletionListener.awaitOneCallback(10, TimeUnit.SECONDS); - } - - public void testTwoFastConsecutiveStarts() throws Exception { - setDataSourceFromContentProvider(mPlayer, "quick_test_recording.mp3", "audio/mp3"); - mPlayer.prepare(); - mPlayer.setOnCompletionListener(mCompletionListener); - mPlayer.start(); - mPlayer.start(); - mCompletionListener.awaitOneCallback(10, TimeUnit.SECONDS); - Thread.sleep(200); - mCompletionListener.assertNoMoreCallbacks(); - } - - public void testThreeFastConsecutiveStarts() throws Exception { - setDataSourceFromContentProvider(mPlayer, "quick_test_recording.mp3", "audio/mp3"); - mPlayer.prepare(); - mPlayer.setOnCompletionListener(mCompletionListener); - mPlayer.start(); - mPlayer.start(); - mPlayer.start(); - mCompletionListener.awaitOneCallback(10, TimeUnit.SECONDS); - Thread.sleep(4000); - mCompletionListener.assertNoMoreCallbacks(); - } - - public void testSeekDuringPlayback() throws Exception { - setDataSourceFromContentProvider(mPlayer, "quick_test_recording.mp3", "audio/mp3"); - mPlayer.prepare(); - mPlayer.setOnCompletionListener(mCompletionListener); - mPlayer.start(); - Thread.sleep(2000); - mPlayer.seekTo(0); - mCompletionListener.awaitOneCallback(10, TimeUnit.SECONDS); - Thread.sleep(200); - mCompletionListener.assertNoMoreCallbacks(); - } - - public void testPlaySingleChannelLowSampleRate3gppFile() throws Exception { - setDataSourceFromContentProvider(mPlayer, "count_and_test.3gpp", "audio/3gpp"); - mPlayer.prepare(); - mPlayer.setOnCompletionListener(mCompletionListener); - mPlayer.start(); - mCompletionListener.awaitOneCallback(10, TimeUnit.SECONDS); - } - - public void testPlayTwoDifferentTypesWithSameMediaPlayer() throws Exception { - setDataSourceFromContentProvider(mPlayer, "quick_test_recording.mp3", "audio/mp3"); - mPlayer.prepare(); - mPlayer.setOnCompletionListener(mCompletionListener); - mPlayer.start(); - mCompletionListener.awaitOneCallback(10, TimeUnit.SECONDS); - mPlayer.reset(); - setDataSourceFromContentProvider(mPlayer, "count_and_test.3gpp", "audio/3gpp"); - mPlayer.prepare(); - mPlayer.start(); - mCompletionListener.awaitOneCallback(10, TimeUnit.SECONDS); - } - - public void testIllegalPreparingDoesntFireErrorListener() throws Exception { - mPlayer.setOnErrorListener(mErrorListener); - try { - mPlayer.prepare(); - fail("This should have thrown an IllegalStateException"); - } catch (IllegalStateException e) { - // Good, expected. - } - mErrorListener.assertNoMoreCallbacks(); - } - - public void testSetDataSourceForMissingFile_ThrowsIOExceptionInPrepare() throws Exception { - mPlayer.setOnErrorListener(mErrorListener); - mPlayer.setDataSource("/this/file/does/not/exist/"); - try { - mPlayer.prepare(); - fail("Should have thrown IOException"); - } catch (IOException e) { - // Good, expected. - } - // Synchronous prepare does not report errors to the error listener. - mErrorListener.assertNoMoreCallbacks(); - } - - public void testRepeatedlySeekingDuringPlayback() throws Exception { - // Start playback then seek repeatedly during playback to the same point. - // The real media player should play a stuttering audio, hopefully my player does too. - setDataSourceFromContentProvider(mPlayer, "quick_test_recording.mp3", "audio/mp3"); - mPlayer.prepare(); - mPlayer.setOnCompletionListener(mCompletionListener); - mPlayer.start(); - Thread.sleep(500); - for (int i = 0; i < 40; ++i) { - Thread.sleep(200); - mPlayer.seekTo(2000); - } - mCompletionListener.awaitOneCallback(10, TimeUnit.SECONDS); - } - - public void testRepeatedlySeekingDuringPlaybackRandomAndVeryFast() throws Exception { - setDataSourceFromContentProvider(mPlayer, "quick_test_recording.mp3", "audio/mp3"); - mPlayer.prepare(); - mPlayer.setOnCompletionListener(mCompletionListener); - mPlayer.start(); - Thread.sleep(500); - for (int i = 0; i < 40; ++i) { - Thread.sleep(250); - mPlayer.seekTo(1500 + (int) (Math.random() * 1000)); - } - mCompletionListener.awaitOneCallback(10, TimeUnit.SECONDS); - } - - public void testSeekToEndThenPlayThenRateChangeCrash() throws Exception { - // Unit test for this bug: http://b/5140693 - // This test proves that the bug is fixed. - setDataSourceFromContentProvider(mPlayer, "fake_voicemail.mp3", "audio/mp3"); - mPlayer.prepare(); - mPlayer.seekTo(mPlayer.getDuration() - 1); - mPlayer.setOnCompletionListener(mCompletionListener); - mPlayer.start(); - mCompletionListener.awaitOneCallback(10, TimeUnit.SECONDS); - // Prior to the fix, this next line was causing a crash. - // The reason behind this was due to our having seeked so close to the end of the file - // that insufficient data was being read, and thus we weren't able to yet determine the - // sample rate and number of channels, which was causing an assertion failure when trying - // to create the time scaler. - setVariableSpeedRateIfSupported(1.0f); - } - - public void testVariableSpeedRateChangeAtDifferentTimes() throws Exception { - // Just check that we can set the rate at any point during playback. - setVariableSpeedRateIfSupported(1.05f); - setDataSourceFromContentProvider(mPlayer, "fake_voicemail.mp3", "audio/mp3"); - setVariableSpeedRateIfSupported(1.10f); - mPlayer.prepare(); - setVariableSpeedRateIfSupported(1.15f); - mPlayer.seekTo(mPlayer.getDuration() / 2); - setVariableSpeedRateIfSupported(1.20f); - mPlayer.setOnCompletionListener(mCompletionListener); - setVariableSpeedRateIfSupported(1.25f); - mPlayer.start(); - setVariableSpeedRateIfSupported(1.30f); - mCompletionListener.awaitOneCallback(10, TimeUnit.SECONDS); - setVariableSpeedRateIfSupported(1.35f); - } - - /** - * If we have a variable speed media player proxy, set the variable speed rate. - *

- * If we don't have a variable speed media player proxy, this method will be a no-op. - */ - private void setVariableSpeedRateIfSupported(float rate) { - if (mPlayer instanceof SingleThreadedMediaPlayerProxy) { - ((SingleThreadedMediaPlayerProxy) mPlayer).setVariableSpeed(rate); - } else if (mPlayer instanceof VariableSpeed) { - ((VariableSpeed) mPlayer).setVariableSpeed(rate); - } - } - - /** - * Gets the {@link Uri} for the test audio content we should play. - *

- * If this is the first time we've called this method, for a given file type and mime type, then - * we'll have to insert some data into the content provider so that we can play it. - *

- * This is not thread safe, but doesn't need to be because all unit tests are executed from a - * single thread, sequentially. - */ - private Uri getTestContentUri(String assetFilename, String assetMimeType) throws IOException { - String key = keyFor(assetFilename, assetMimeType); - if (mContentUriMap.containsKey(key)) { - return mContentUriMap.get(key); - } - ContentValues values = new ContentValues(); - values.put(VoicemailContract.Voicemails.DATE, String.valueOf(System.currentTimeMillis())); - values.put(VoicemailContract.Voicemails.NUMBER, CONTACT_NUMBER); - values.put(VoicemailContract.Voicemails.MIME_TYPE, assetMimeType); - String packageName = getInstrumentation().getTargetContext().getPackageName(); - Uri uri = getContentResolver().insert( - VoicemailContract.Voicemails.buildSourceUri(packageName), values); - AssetManager assets = getAssets(); - OutputStream outputStream = null; - InputStream inputStream = null; - try { - inputStream = assets.open(assetFilename); - outputStream = getContentResolver().openOutputStream(uri); - copyBetweenStreams(inputStream, outputStream); - mContentUriMap.put(key, uri); - return uri; - } finally { - Closeables.closeQuietly(outputStream); - Closeables.closeQuietly(inputStream); - } - } - - private String keyFor(String assetFilename, String assetMimeType) { - return assetFilename + "+" + assetMimeType; - } - - public void copyBetweenStreams(InputStream in, OutputStream out) throws IOException { - byte[] buffer = new byte[1024]; - int bytesRead; - while ((bytesRead = in.read(buffer)) != -1) { - out.write(buffer, 0, bytesRead); - } - } - - private void cleanupContentUriIfNecessary() { - for (Uri uri : mContentUriMap.values()) { - getContentResolver().delete(uri, null, null); - } - mContentUriMap.clear(); - } - - private void setDataSourceFromContentProvider(MediaPlayerProxy player, String assetFilename, - String assetMimeType) throws IOException { - player.setDataSource(getInstrumentation().getTargetContext(), - getTestContentUri(assetFilename, assetMimeType)); - } - - private ContentResolver getContentResolver() { - return getInstrumentation().getContext().getContentResolver(); - } - - private AssetManager getAssets() { - return getInstrumentation().getContext().getAssets(); - } -} diff --git a/variablespeed/tests/src/com/android/ex/variablespeed/RealMediaPlayerTest.java b/variablespeed/tests/src/com/android/ex/variablespeed/RealMediaPlayerTest.java deleted file mode 100644 index 7f12671..0000000 --- a/variablespeed/tests/src/com/android/ex/variablespeed/RealMediaPlayerTest.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.ex.variablespeed; - -import android.media.MediaPlayer; - -import java.util.concurrent.Callable; -import java.util.concurrent.FutureTask; -import java.util.concurrent.TimeUnit; - -/** - * Tests that MediaPlayerProxyTestCase contains reasonable tests with a real {@link MediaPlayer}. - */ -public class RealMediaPlayerTest extends MediaPlayerProxyTestCase { - @Override - public MediaPlayerProxy createTestMediaPlayer() throws Exception { - // We have to construct the MediaPlayer on the main thread (or at least on a thread with an - // associated looper) otherwise we don't get sent the messages when callbacks should be - // invoked. I've raised a bug for this: http://b/4602011. - Callable callable = new Callable() { - @Override - public MediaPlayer call() throws Exception { - return new MediaPlayer(); - } - }; - FutureTask future = new FutureTask(callable); - getInstrumentation().runOnMainSync(future); - return DynamicProxy.dynamicProxy(MediaPlayerProxy.class, future.get(1, TimeUnit.SECONDS)); - } -} diff --git a/variablespeed/tests/src/com/android/ex/variablespeed/VariableSpeedTest.java b/variablespeed/tests/src/com/android/ex/variablespeed/VariableSpeedTest.java deleted file mode 100644 index 62cabeb..0000000 --- a/variablespeed/tests/src/com/android/ex/variablespeed/VariableSpeedTest.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.ex.variablespeed; - -import android.util.Log; - -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; - -/** Tests for the {@link VariableSpeed} class. */ -public class VariableSpeedTest extends MediaPlayerProxyTestCase { - private static final String TAG = "VariableSpeedTest"; - - private ScheduledExecutorService mExecutor = Executors.newScheduledThreadPool(2); - - @Override - protected void tearDown() throws Exception { - // I explicitly want to do super's tear-down first, because I need to get it to reset - // 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)) { - Log.e(TAG, "Couldn't shut down Executor during test, check your cleanup code!"); - } - mExecutor = null; - } - - @Override - public MediaPlayerProxy createTestMediaPlayer() throws Exception { - return VariableSpeed.createVariableSpeed(mExecutor); - } -} -- cgit v1.2.3