diff options
| -rw-r--r-- | media/jni/soundpool/tests/Android.bp | 28 | ||||
| -rwxr-xr-x | media/jni/soundpool/tests/build_and_run.sh | 30 | ||||
| -rw-r--r-- | media/jni/soundpool/tests/soundpool_stress.cpp | 309 |
3 files changed, 367 insertions, 0 deletions
diff --git a/media/jni/soundpool/tests/Android.bp b/media/jni/soundpool/tests/Android.bp new file mode 100644 index 000000000000..96ec4e5d5913 --- /dev/null +++ b/media/jni/soundpool/tests/Android.bp @@ -0,0 +1,28 @@ +cc_binary { + name: "soundpool_stress", + host_supported: false, + + include_dirs: [ + "frameworks/base/media/jni/" + ], + + shared_libs: [ + "libaudioutils", + "libbinder", + "liblog", + "libmedia", + "libsoundpool", + "libstagefright", + "libutils", + ], + + srcs: [ + "soundpool_stress.cpp" + ], + + cflags: [ + "-Wall", + "-Werror", + "-Wextra", + ], +} diff --git a/media/jni/soundpool/tests/build_and_run.sh b/media/jni/soundpool/tests/build_and_run.sh new file mode 100755 index 000000000000..741f2efbfddb --- /dev/null +++ b/media/jni/soundpool/tests/build_and_run.sh @@ -0,0 +1,30 @@ +#!/bin/bash +# +# Run samples from this directory +# + +if [ -z "$ANDROID_BUILD_TOP" ]; then + echo "Android build environment not set" + exit -1 +fi + +# ensure we have mm +. $ANDROID_BUILD_TOP/build/envsetup.sh + +mm + +echo "waiting for device" + +adb root && adb wait-for-device remount + +echo "========================================" +echo "testing soundpool_stress" +uidir="/product/media/audio/notifications" +adb push $OUT/system/bin/soundpool_stress /system/bin + +# test SoundPool playback of all the UI sound samples (loaded twice) looping 10s 1 thread. +#adb shell /system/bin/soundpool_stress -l -1 $uidir/*.ogg $uidir/*.ogg + +# performance test SoundPool playback of all the UI sound samples (x2) +# 1 iterations, looping, 1 second playback, 4 threads. +adb shell /system/bin/soundpool_stress -i 1 -l -1 -p 1 -t 4 $uidir/*.ogg $uidir/*.ogg diff --git a/media/jni/soundpool/tests/soundpool_stress.cpp b/media/jni/soundpool/tests/soundpool_stress.cpp new file mode 100644 index 000000000000..212662fb6b96 --- /dev/null +++ b/media/jni/soundpool/tests/soundpool_stress.cpp @@ -0,0 +1,309 @@ +/* + * Copyright (C) 2014 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. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "soundpool" + +#include <fcntl.h> +#include <stdio.h> +#include <sys/stat.h> + +#include <atomic> +#include <future> +#include <mutex> +#include <set> +#include <vector> + +#include <audio_utils/clock.h> +#include <binder/ProcessState.h> +#include <media/stagefright/MediaExtractorFactory.h> +#include <soundpool/SoundPool.h> // direct include, this is not an NDK feature. +#include <system/audio.h> +#include <utils/Log.h> + +using namespace android; + +// Errors and diagnostic messages all go to stdout. + +namespace { + +void usage(const char *name) +{ + printf("Usage: %s " + "[-i #iterations] [-l #loop] [-p #playback_seconds] [-s #streams] [-t #threads] " + "[-z #snoozeSec] <input-file>+\n", name); + printf("Uses soundpool to load and play a file (the first 10 seconds)\n"); + printf(" -i #iterations, default 1\n"); + printf(" -l #loop looping mode, -1 forever\n"); + printf(" -p #playback_seconds, default 10\n"); + printf(" -s #streams for concurrent sound playback, default 20\n"); + printf(" -t #threads, default 1\n"); + printf(" -z #snoozeSec after stopping, -1 forever, default 0\n"); + printf(" <input-file>+ files to be played\n"); +} + +std::atomic_int32_t gErrors{}; +std::atomic_int32_t gWarnings{}; + +void printEvent(const SoundPoolEvent *event) { + printf("{ msg:%d id:%d status:%d }\n", event->mMsg, event->mArg1, event->mArg2); +} + +class CallbackManager { +public: + int32_t getNumberEvents(int32_t soundID) { + std::lock_guard lock(mLock); + return mEvents[soundID] > 0; + } + + void setSoundPool(SoundPool* soundPool) { + std::lock_guard lock(mLock); + mSoundPool = soundPool; + } + + void callback(SoundPoolEvent event, const SoundPool *soundPool) { + std::lock_guard lock(mLock); + printEvent(&event); + if (soundPool != mSoundPool) { + printf("ERROR: mismatched soundpool: %p\n", soundPool); + ++gErrors; + return; + } + if (event.mMsg != 1 /* SoundPoolEvent::SOUND_LOADED */) { + printf("ERROR: invalid event msg: %d\n", event.mMsg); + ++gErrors; + return; + } + if (event.mArg2 != 0) { + printf("ERROR: event status(%d) != 0\n", event.mArg2); + ++gErrors; + return; + } + if (event.mArg1 <= 0) { + printf("ERROR: event soundID(%d) < 0\n", event.mArg1); + ++gErrors; + return; + } + ++mEvents[event.mArg1]; + } + +private: + std::mutex mLock; + SoundPool *mSoundPool = nullptr; + std::map<int32_t /* soundID */, int32_t /* count */> mEvents; +} gCallbackManager; + + +void StaticCallbackManager(SoundPoolEvent event, SoundPool* soundPool, void* user) { + ((CallbackManager *)user)->callback(event, soundPool); +} + +void testStreams(SoundPool *soundPool, const std::vector<const char *> &filenames, + int loop, int playSec) +{ + const int64_t startTimeNs = systemTime(); + std::vector<int32_t> soundIDs; + for (auto filename : filenames) { + struct stat st; + if (stat(filename, &st) < 0) { + printf("ERROR: cannot stat %s\n", filename); + return; + } + const uint64_t length = uint64_t(st.st_size); + const int inp = open(filename, O_RDONLY); + if (inp < 0) { + printf("ERROR: cannot open %s\n", filename); + return; + } + printf("loading (%s) size (%llu)\n", filename, (unsigned long long)length); + const int32_t soundID = soundPool->load( + inp, 0 /*offset*/, length, 0 /*priority - unused*/); + if (soundID == 0) { + printf("ERROR: cannot load %s\n", filename); + return; + } + close(inp); + soundIDs.emplace_back(soundID); + printf("loaded %s soundID(%d)\n", filename, soundID); + } + const int64_t requestLoadTimeNs = systemTime(); + printf("\nrequestLoadTimeMs: %d\n", + (int)((requestLoadTimeNs - startTimeNs) / NANOS_PER_MILLISECOND)); + + // create stream & get Id (playing) + const float maxVol = 1.f; + const float silentVol = 0.f; + const int priority = 0; // lowest + const float rate = 1.f; // normal + + // Loading is done by a SoundPool Worker thread. + // TODO: Use SoundPool::setCallback() for wait + + for (int32_t soundID : soundIDs) { + while (true) { + const int32_t streamID = + soundPool->play(soundID, silentVol, silentVol, priority, 0 /*loop*/, rate); + if (streamID != 0) { + const int32_t events = gCallbackManager.getNumberEvents(soundID); + if (events != 1) { + printf("WARNING: successful play for streamID:%d soundID:%d" + " but callback events(%d) != 1\n", streamID, soundID, events); + ++gWarnings; + } + soundPool->stop(streamID); + break; + } + usleep(1000); + } + printf("[%d]", soundID); + fflush(stdout); + } + + const int64_t loadTimeNs = systemTime(); + printf("\nloadTimeMs: %d\n", (int)((loadTimeNs - startTimeNs) / NANOS_PER_MILLISECOND)); + + // check and play (overlap with above). + std::vector<int32_t> streamIDs; + for (int32_t soundID : soundIDs) { + printf("\nplaying soundID=%d", soundID); + const int32_t streamID = soundPool->play(soundID, maxVol, maxVol, priority, loop, rate); + if (streamID == 0) { + printf(" failed! ERROR"); + ++gErrors; + } else { + printf(" streamID=%d", streamID); + streamIDs.emplace_back(streamID); + } + } + const int64_t playTimeNs = systemTime(); + printf("\nplayTimeMs: %d\n", (int)((playTimeNs - loadTimeNs) / NANOS_PER_MILLISECOND)); + + for (int i = 0; i < playSec; ++i) { + sleep(1); + printf("."); + fflush(stdout); + } + + for (int32_t streamID : streamIDs) { + soundPool->stop(streamID); + } + + for (int32_t soundID : soundIDs) { + soundPool->unload(soundID); + } + printf("\nDone!\n"); +} + +} // namespace + +int main(int argc, char *argv[]) +{ + const char * const me = argv[0]; + + int iterations = 1; + int loop = 0; // disable looping + int maxStreams = 40; // change to have more concurrent playback streams + int playSec = 10; + int snoozeSec = 0; + int threadCount = 1; + for (int ch; (ch = getopt(argc, argv, "i:l:p:s:t:z:")) != -1; ) { + switch (ch) { + case 'i': + iterations = atoi(optarg); + break; + case 'l': + loop = atoi(optarg); + break; + case 'p': + playSec = atoi(optarg); + break; + case 's': + maxStreams = atoi(optarg); + break; + case 't': + threadCount = atoi(optarg); + break; + case 'z': + snoozeSec = atoi(optarg); + break; + default: + usage(me); + return EXIT_FAILURE; + } + } + + argc -= optind; + argv += optind; + if (argc <= 0) { + usage(me); + return EXIT_FAILURE; + } + + std::vector<const char *> filenames(argv, argv + argc); + + android::ProcessState::self()->startThreadPool(); + + // O and later requires data sniffer registration for proper file type detection + MediaExtractorFactory::LoadExtractors(); + + // create soundpool + audio_attributes_t aa = { + .content_type = AUDIO_CONTENT_TYPE_MUSIC, + .usage = AUDIO_USAGE_MEDIA, + }; + auto soundPool = std::make_unique<SoundPool>(maxStreams, &aa); + + gCallbackManager.setSoundPool(soundPool.get()); + soundPool->setCallback(StaticCallbackManager, &gCallbackManager); + + const int64_t startTimeNs = systemTime(); + + for (int it = 0; it < iterations; ++it) { + // One instance: + // testStreams(soundPool.get(), filenames, loop, playSec); + + // Test multiple instances + std::vector<std::future<void>> threads(threadCount); + printf("testing %zu threads\n", threads.size()); + for (auto &thread : threads) { + thread = std::async(std::launch::async, + [&]{ testStreams(soundPool.get(), filenames, loop, playSec);}); + } + // automatically joins. + } + + const int64_t endTimeNs = systemTime(); + + // snooze before cleaning up to examine soundpool dumpsys state after stop + for (int i = 0; snoozeSec < 0 || i < snoozeSec; ++i) { + printf("z"); + fflush(stdout); + sleep(1); + }; + + gCallbackManager.setSoundPool(nullptr); + soundPool.reset(); + + printf("total time in ms: %lld\n", (endTimeNs - startTimeNs) / NANOS_PER_MILLISECOND); + if (gWarnings != 0) { + printf("%d warnings!\n", gWarnings.load()); + } + if (gErrors != 0) { + printf("%d errors!\n", gErrors.load()); + return EXIT_FAILURE; + } + return EXIT_SUCCESS; +} |