John Grossman 881186c322 Enhance Visualizer behavior in the case of mediaserver death.
Bring the Visualizer class into line with the SDK documentation by
returning ERROR_DEAD_OBJECT instead of ERROR_INVALID_OPERATION when
the Visualizer loses its binder connection to the mediaserver because
of a mediaserver restart.

Also add a new callback interface to allow clients to be
asynchronously notified in the case of server death.  Right now, the
interface definition and the registration method are flagged as hidden
pending API council review/approval.

See http://b/issue?id=5717519 for details.

Change-Id: Id428fb946d6d7676bffd2a597366e8444ebe24f2
Signed-off-by: John Grossman <johngro@google.com>
2012-01-12 14:36:16 -08:00

329 lines
8.1 KiB
C++

/*
**
** Copyright 2010, 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 "Visualizer"
#include <utils/Log.h>
#include <stdint.h>
#include <sys/types.h>
#include <limits.h>
#include <cutils/bitops.h>
#include <media/Visualizer.h>
extern void fixed_fft_real(int n, int32_t *v);
namespace android {
// ---------------------------------------------------------------------------
Visualizer::Visualizer (int32_t priority,
effect_callback_t cbf,
void* user,
int sessionId)
: AudioEffect(SL_IID_VISUALIZATION, NULL, priority, cbf, user, sessionId),
mCaptureRate(CAPTURE_RATE_DEF),
mCaptureSize(CAPTURE_SIZE_DEF),
mSampleRate(44100000),
mCaptureCallBack(NULL),
mCaptureCbkUser(NULL)
{
initCaptureSize();
}
Visualizer::~Visualizer()
{
}
status_t Visualizer::setEnabled(bool enabled)
{
Mutex::Autolock _l(mLock);
sp<CaptureThread> t = mCaptureThread;
if (t != 0) {
if (enabled) {
if (t->exitPending()) {
if (t->requestExitAndWait() == WOULD_BLOCK) {
LOGE("Visualizer::enable() called from thread");
return INVALID_OPERATION;
}
}
}
t->mLock.lock();
}
status_t status = AudioEffect::setEnabled(enabled);
if (status == NO_ERROR) {
if (t != 0) {
if (enabled) {
t->run("AudioTrackThread");
} else {
t->requestExit();
}
}
}
if (t != 0) {
t->mLock.unlock();
}
return status;
}
status_t Visualizer::setCaptureCallBack(capture_cbk_t cbk, void* user, uint32_t flags, uint32_t rate)
{
if (rate > CAPTURE_RATE_MAX) {
return BAD_VALUE;
}
Mutex::Autolock _l(mLock);
if (mEnabled) {
return INVALID_OPERATION;
}
sp<CaptureThread> t = mCaptureThread;
if (t != 0) {
t->mLock.lock();
}
mCaptureThread.clear();
mCaptureCallBack = cbk;
mCaptureCbkUser = user;
mCaptureFlags = flags;
mCaptureRate = rate;
if (t != 0) {
t->mLock.unlock();
}
if (cbk != NULL) {
mCaptureThread = new CaptureThread(*this, rate, ((flags & CAPTURE_CALL_JAVA) != 0));
if (mCaptureThread == 0) {
LOGE("Could not create callback thread");
return NO_INIT;
}
}
LOGV("setCaptureCallBack() rate: %d thread %p flags 0x%08x",
rate, mCaptureThread.get(), mCaptureFlags);
return NO_ERROR;
}
status_t Visualizer::setCaptureSize(uint32_t size)
{
if (size > VISUALIZER_CAPTURE_SIZE_MAX ||
size < VISUALIZER_CAPTURE_SIZE_MIN ||
popcount(size) != 1) {
return BAD_VALUE;
}
Mutex::Autolock _l(mLock);
if (mEnabled) {
return INVALID_OPERATION;
}
uint32_t buf32[sizeof(effect_param_t) / sizeof(uint32_t) + 2];
effect_param_t *p = (effect_param_t *)buf32;
p->psize = sizeof(uint32_t);
p->vsize = sizeof(uint32_t);
*(int32_t *)p->data = VISUALIZER_PARAM_CAPTURE_SIZE;
*((int32_t *)p->data + 1)= size;
status_t status = setParameter(p);
LOGV("setCaptureSize size %d status %d p->status %d", size, status, p->status);
if (status == NO_ERROR) {
status = p->status;
}
if (status == NO_ERROR) {
mCaptureSize = size;
}
return status;
}
status_t Visualizer::getWaveForm(uint8_t *waveform)
{
if (waveform == NULL) {
return BAD_VALUE;
}
if (mCaptureSize == 0) {
return NO_INIT;
}
status_t status = NO_ERROR;
if (mEnabled) {
uint32_t replySize = mCaptureSize;
status = command(VISUALIZER_CMD_CAPTURE, 0, NULL, &replySize, waveform);
LOGV("getWaveForm() command returned %d", status);
if ((status == NO_ERROR) && (replySize == 0)) {
status = NOT_ENOUGH_DATA;
}
} else {
LOGV("getWaveForm() disabled");
memset(waveform, 0x80, mCaptureSize);
}
return status;
}
status_t Visualizer::getFft(uint8_t *fft)
{
if (fft == NULL) {
return BAD_VALUE;
}
if (mCaptureSize == 0) {
return NO_INIT;
}
status_t status = NO_ERROR;
if (mEnabled) {
uint8_t buf[mCaptureSize];
status = getWaveForm(buf);
if (status == NO_ERROR) {
status = doFft(fft, buf);
}
} else {
memset(fft, 0, mCaptureSize);
}
return status;
}
status_t Visualizer::doFft(uint8_t *fft, uint8_t *waveform)
{
int32_t workspace[mCaptureSize >> 1];
int32_t nonzero = 0;
for (uint32_t i = 0; i < mCaptureSize; i += 2) {
workspace[i >> 1] =
((waveform[i] ^ 0x80) << 24) | ((waveform[i + 1] ^ 0x80) << 8);
nonzero |= workspace[i >> 1];
}
if (nonzero) {
fixed_fft_real(mCaptureSize >> 1, workspace);
}
for (uint32_t i = 0; i < mCaptureSize; i += 2) {
short tmp = workspace[i >> 1] >> 21;
while (tmp > 127 || tmp < -128) tmp >>= 1;
fft[i] = tmp;
tmp = workspace[i >> 1];
tmp >>= 5;
while (tmp > 127 || tmp < -128) tmp >>= 1;
fft[i + 1] = tmp;
}
return NO_ERROR;
}
void Visualizer::periodicCapture()
{
Mutex::Autolock _l(mLock);
LOGV("periodicCapture() %p mCaptureCallBack %p mCaptureFlags 0x%08x",
this, mCaptureCallBack, mCaptureFlags);
if (mCaptureCallBack != NULL &&
(mCaptureFlags & (CAPTURE_WAVEFORM|CAPTURE_FFT)) &&
mCaptureSize != 0) {
uint8_t waveform[mCaptureSize];
status_t status = getWaveForm(waveform);
if (status != NO_ERROR) {
return;
}
uint8_t fft[mCaptureSize];
if (mCaptureFlags & CAPTURE_FFT) {
status = doFft(fft, waveform);
}
if (status != NO_ERROR) {
return;
}
uint8_t *wavePtr = NULL;
uint8_t *fftPtr = NULL;
uint32_t waveSize = 0;
uint32_t fftSize = 0;
if (mCaptureFlags & CAPTURE_WAVEFORM) {
wavePtr = waveform;
waveSize = mCaptureSize;
}
if (mCaptureFlags & CAPTURE_FFT) {
fftPtr = fft;
fftSize = mCaptureSize;
}
mCaptureCallBack(mCaptureCbkUser, waveSize, wavePtr, fftSize, fftPtr, mSampleRate);
}
}
uint32_t Visualizer::initCaptureSize()
{
uint32_t buf32[sizeof(effect_param_t) / sizeof(uint32_t) + 2];
effect_param_t *p = (effect_param_t *)buf32;
p->psize = sizeof(uint32_t);
p->vsize = sizeof(uint32_t);
*(int32_t *)p->data = VISUALIZER_PARAM_CAPTURE_SIZE;
status_t status = getParameter(p);
if (status == NO_ERROR) {
status = p->status;
}
uint32_t size = 0;
if (status == NO_ERROR) {
size = *((int32_t *)p->data + 1);
}
mCaptureSize = size;
LOGV("initCaptureSize size %d status %d", mCaptureSize, status);
return size;
}
//-------------------------------------------------------------------------
Visualizer::CaptureThread::CaptureThread(Visualizer& receiver, uint32_t captureRate, bool bCanCallJava)
: Thread(bCanCallJava), mReceiver(receiver)
{
mSleepTimeUs = 1000000000 / captureRate;
LOGV("CaptureThread cstor %p captureRate %d mSleepTimeUs %d", this, captureRate, mSleepTimeUs);
}
bool Visualizer::CaptureThread::threadLoop()
{
LOGV("CaptureThread %p enter", this);
while (!exitPending())
{
usleep(mSleepTimeUs);
mReceiver.periodicCapture();
}
LOGV("CaptureThread %p exiting", this);
return false;
}
status_t Visualizer::CaptureThread::readyToRun()
{
return NO_ERROR;
}
void Visualizer::CaptureThread::onFirstRef()
{
}
}; // namespace android