SoundPool: Add native stress test
Test: soundpool_stress (this is it) Bug: 140807581 Change-Id: I777a3adfe3fa7e8a9c9162c98e472c2e38e1e7bf
This commit is contained in:
28
media/jni/soundpool/tests/Android.bp
Normal file
28
media/jni/soundpool/tests/Android.bp
Normal file
@@ -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",
|
||||
],
|
||||
}
|
||||
30
media/jni/soundpool/tests/build_and_run.sh
Executable file
30
media/jni/soundpool/tests/build_and_run.sh
Executable file
@@ -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
|
||||
309
media/jni/soundpool/tests/soundpool_stress.cpp
Normal file
309
media/jni/soundpool/tests/soundpool_stress.cpp
Normal file
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user