diff options
-rw-r--r-- | include/media/AudioEffect.h | 70 | ||||
-rw-r--r-- | include/media/EffectVisualizerApi.h | 55 | ||||
-rw-r--r-- | include/media/IMediaPlayerService.h | 4 | ||||
-rw-r--r-- | include/media/Visualizer.h | 160 | ||||
-rw-r--r-- | include/media/mediaplayer.h | 1 | ||||
-rw-r--r-- | media/libeffects/Android.mk | 30 | ||||
-rw-r--r-- | media/libeffects/EffectVisualizer.cpp | 401 | ||||
-rw-r--r-- | media/libmedia/Android.mk | 3 | ||||
-rw-r--r-- | media/libmedia/AudioEffect.cpp | 36 | ||||
-rw-r--r-- | media/libmedia/IMediaPlayerService.cpp | 17 | ||||
-rw-r--r-- | media/libmedia/Visualizer.cpp | 330 | ||||
-rw-r--r-- | media/libmedia/mediaplayer.cpp | 57 | ||||
-rw-r--r-- | media/libmediaplayerservice/MediaPlayerService.cpp | 121 | ||||
-rw-r--r-- | media/libmediaplayerservice/MediaPlayerService.h | 4 |
14 files changed, 1030 insertions, 259 deletions
diff --git a/include/media/AudioEffect.h b/include/media/AudioEffect.h index 66670f3..e9ff8a3 100644 --- a/include/media/AudioEffect.h +++ b/include/media/AudioEffect.h @@ -307,29 +307,18 @@ public: int32_t priority() const { return mPriority; } - /* Enables the effect engine. + /* Enables or disables the effect engine. * * Parameters: - * None. + * enabled: requested enable state. * * Returned status (from utils/Errors.h) can be: * - NO_ERROR: successful operation - * - INVALID_OPERATION: the application does not have control of the effect engine + * - INVALID_OPERATION: the application does not have control of the effect engine or the + * effect is already in the requested state. */ - status_t enable(); - - /* Disables the effect engine. - * - * Parameters: - * None. - * - * Returned status (from utils/Errors.h) can be: - * - NO_ERROR: successful operation - * - INVALID_OPERATION: the application does not have control of the effect engine - */ - status_t disable(); - - bool isEnabled() const; + virtual status_t setEnabled(bool enabled); + bool getEnabled() const; /* Sets a parameter value. * @@ -342,7 +331,7 @@ public: * - BAD_VALUE: invalid parameter identifier or value. * - DEAD_OBJECT: the effect engine has been deleted. */ - status_t setParameter(effect_param_t *param); + virtual status_t setParameter(effect_param_t *param); /* Prepare a new parameter value that will be set by next call to * setParameterCommit(). This method can be used to set multiple parameters @@ -359,7 +348,7 @@ public: * - NO_MEMORY: no more space available in shared memory used for deferred parameter * setting. */ - status_t setParameterDeferred(effect_param_t *param); + virtual status_t setParameterDeferred(effect_param_t *param); /* Commit all parameter values previously prepared by setParameterDeferred(). * @@ -373,7 +362,7 @@ public: * as to which of the parameters caused this error. * - DEAD_OBJECT: the effect engine has been deleted. */ - status_t setParameterCommit(); + virtual status_t setParameterCommit(); /* Gets a parameter value. * @@ -387,13 +376,17 @@ public: * - BAD_VALUE: invalid parameter identifier. * - DEAD_OBJECT: the effect engine has been deleted. */ - status_t getParameter(effect_param_t *param); + virtual status_t getParameter(effect_param_t *param); /* Sends a command and receives a response to/from effect engine. * See EffectApi.h for details on effect command() function, valid command codes * and formats. */ - status_t command(int32_t cmdCode, int32_t cmdSize, void *cmdData, int32_t *replySize, void *replyData); + virtual status_t command(int32_t cmdCode, + int32_t cmdSize, + void *cmdData, + int32_t *replySize, + void *replyData); /* @@ -409,6 +402,17 @@ public: */ static status_t guidToString(const effect_uuid_t *guid, char *str, size_t maxLen); +protected: + volatile int32_t mEnabled; // enable state + int32_t mSessionId; // audio session ID + int32_t mPriority; // priority for effect control + status_t mStatus; // effect status + effect_callback_t mCbf; // callback function for status, control and + // parameter changes notifications + void* mUserData; // client context for callback function + effect_descriptor_t mDescriptor; // effect descriptor + int32_t mId; // system wide unique effect engine instance ID + private: // Implements the IEffectClient interface @@ -419,9 +423,17 @@ private: EffectClient(AudioEffect *effect) : mEffect(effect){} // IEffectClient - virtual void controlStatusChanged(bool controlGranted) {mEffect->controlStatusChanged(controlGranted);} - virtual void enableStatusChanged(bool enabled) {mEffect->enableStatusChanged(enabled);} - virtual void commandExecuted(int cmdCode, int cmdSize, void *pCmdData, int replySize, void *pReplyData) { + virtual void controlStatusChanged(bool controlGranted) { + mEffect->controlStatusChanged(controlGranted); + } + virtual void enableStatusChanged(bool enabled) { + mEffect->enableStatusChanged(enabled); + } + virtual void commandExecuted(int cmdCode, + int cmdSize, + void *pCmdData, + int replySize, + void *pReplyData) { mEffect->commandExecuted(cmdCode, cmdSize, pCmdData, replySize, pReplyData); } @@ -446,14 +458,6 @@ private: sp<EffectClient> mIEffectClient; // IEffectClient implementation sp<IMemory> mCblkMemory; // shared memory for deferred parameter setting effect_param_cblk_t* mCblk; // control block for deferred parameter setting - int32_t mPriority; // priority for effect control - status_t mStatus; // effect status - volatile int32_t mEnabled; // enable state - effect_callback_t mCbf; // callback function for status, control, parameter changes notifications - void* mUserData; // client context for callback function - effect_descriptor_t mDescriptor; // effect descriptor - int32_t mId; // system wide unique effect engine instance identifier - int32_t mSessionId; // audio session ID }; diff --git a/include/media/EffectVisualizerApi.h b/include/media/EffectVisualizerApi.h new file mode 100644 index 0000000..1155db8 --- /dev/null +++ b/include/media/EffectVisualizerApi.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 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. + */ + +#ifndef ANDROID_EFFECTVISUALIZERAPI_H_ +#define ANDROID_EFFECTVISUALIZERAPI_H_ + +#include <media/EffectApi.h> + +#if __cplusplus +extern "C" { +#endif + +//TODO replace by openSL ES include when available +static const effect_uuid_t SL_IID_VISUALIZATION_ = + { 0xe46b26a0, 0xdddd, 0x11db, 0x8afd, { 0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b } }; +const effect_uuid_t * const SL_IID_VISUALIZATION = &SL_IID_VISUALIZATION_; + +#define VISUALIZER_CAPTURE_SIZE_MAX 1024 // maximum capture size in samples +#define VISUALIZER_CAPTURE_SIZE_MIN 128 // minimum capture size in samples + +/* enumerated parameters for Visualizer effect */ +typedef enum +{ + VISU_PARAM_CAPTURE_SIZE, // Sets the number PCM samples in the capture. +} t_visualizer_params; + +/* commands */ +typedef enum +{ + VISU_CMD_CAPTURE = EFFECT_CMD_FIRST_PROPRIETARY, // Gets the latest PCM capture. +}t_visualizer_cmds; + +// VISU_CMD_CAPTURE retrieves the latest PCM snapshot captured by the visualizer engine. +// It returns the number of samples specified by VISU_PARAM_CAPTURE_SIZE +// in 8 bit unsigned format (0 = 0x80) + +#if __cplusplus +} // extern "C" +#endif + + +#endif /*ANDROID_EFFECTVISUALIZERAPI_H_*/ diff --git a/include/media/IMediaPlayerService.h b/include/media/IMediaPlayerService.h index e892875..9416ca1 100644 --- a/include/media/IMediaPlayerService.h +++ b/include/media/IMediaPlayerService.h @@ -48,10 +48,6 @@ public: virtual sp<IMemory> decode(const char* url, uint32_t *pSampleRate, int* pNumChannels, int* pFormat) = 0; virtual sp<IMemory> decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels, int* pFormat) = 0; virtual sp<IOMX> getOMX() = 0; - - // Take a peek at currently playing audio, for visualization purposes. - // This returns a buffer of 16 bit mono PCM data, or NULL if no visualization buffer is currently available. - virtual sp<IMemory> snoop() = 0; }; // ---------------------------------------------------------------------------- diff --git a/include/media/Visualizer.h b/include/media/Visualizer.h new file mode 100644 index 0000000..5d51de8 --- /dev/null +++ b/include/media/Visualizer.h @@ -0,0 +1,160 @@ +/* + * Copyright (C) 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. + */ + +#ifndef ANDROID_MEDIA_VISUALIZER_H +#define ANDROID_MEDIA_VISUALIZER_H + +#include <media/AudioEffect.h> +#include <media/EffectVisualizerApi.h> +#include <string.h> + +/** + * The Visualizer class enables application to retrieve part of the currently playing audio for + * visualization purpose. It is not an audio recording interface and only returns partial and low + * quality audio content. However, to protect privacy of certain audio data (e.g voice mail) the use + * of the visualizer requires the permission android.permission.RECORD_AUDIO. + * The audio session ID passed to the constructor indicates which audio content should be + * visualized: + * - If the session is 0, the audio output mix is visualized + * - If the session is not 0, the audio from a particular MediaPlayer or AudioTrack + * using this audio session is visualized + * Two types of representation of audio content can be captured: + * - Waveform data: consecutive 8-bit (unsigned) mono samples by using the getWaveForm() method + * - Frequency data: 8-bit magnitude FFT by using the getFft() method + * + * The length of the capture can be retrieved or specified by calling respectively + * getCaptureSize() and setCaptureSize() methods. Note that the size of the FFT + * is half of the specified capture size but both sides of the spectrum are returned yielding in a + * number of bytes equal to the capture size. The capture size must be a power of 2 in the range + * returned by getMinCaptureSize() and getMaxCaptureSize(). + * In addition to the polling capture mode, a callback mode is also available by installing a + * callback function by use of the setCaptureCallBack() method. The rate at which the callback + * is called as well as the type of data returned is specified. + * Before capturing data, the Visualizer must be enabled by calling the setEnabled() method. + * When data capture is not needed any more, the Visualizer should be disabled. + */ + + +namespace android { + +// ---------------------------------------------------------------------------- + +class Visualizer: public AudioEffect { +public: + + enum callback_flags { + CAPTURE_WAVEFORM = 0x00000001, // capture callback returns a PCM wave form + CAPTURE_FFT = 0x00000002, // apture callback returns a frequency representation + CAPTURE_CALL_JAVA = 0x00000004 // the callback thread can call java + }; + + + /* Constructor. + * See AudioEffect constructor for details on parameters. + */ + Visualizer(int32_t priority = 0, + effect_callback_t cbf = 0, + void* user = 0, + int sessionId = 0); + + ~Visualizer(); + + virtual status_t setEnabled(bool enabled); + + // maximum capture size in samples + static uint32_t getMaxCaptureSize() { return VISUALIZER_CAPTURE_SIZE_MAX; } + // minimum capture size in samples + static uint32_t getMinCaptureSize() { return VISUALIZER_CAPTURE_SIZE_MIN; } + // maximum capture rate in millihertz + static uint32_t getMaxCaptureRate() { return CAPTURE_RATE_MAX; } + + // callback used to return periodic PCM or FFT captures to the application. Either one or both + // types of data are returned (PCM and FFT) according to flags indicated when installing the + // callback. When a type of data is not present, the corresponding size (waveformSize or + // fftSize) is 0. + typedef void (*capture_cbk_t)(void* user, + uint32_t waveformSize, + uint8_t *waveform, + uint32_t fftSize, + uint8_t *fft, + uint32_t samplingrate); + + // install a callback to receive periodic captures. The capture rate is specified in milliHertz + // and the capture format is according to flags (see callback_flags). + status_t setCaptureCallBack(capture_cbk_t cbk, void* user, uint32_t flags, uint32_t rate); + + // set the capture size capture size must be a power of two in the range + // [VISUALIZER_CAPTURE_SIZE_MAX. VISUALIZER_CAPTURE_SIZE_MIN] + // must be called when the visualizer is not enabled + status_t setCaptureSize(uint32_t size); + uint32_t getCaptureSize() { return mCaptureSize; } + + // returns the capture rate indicated when installing the callback + uint32_t getCaptureRate() { return mCaptureRate; } + + // returns the sampling rate of the audio being captured + uint32_t getSamplingRate() { return mSampleRate; } + + // return a capture in PCM 8 bit unsigned format. The size of the capture is equal to + // getCaptureSize() + status_t getWaveForm(uint8_t *waveform); + + // return a capture in FFT 8 bit signed format. The size of the capture is equal to + // getCaptureSize() but the length of the FFT is half of the size (both parts of the spectrum + // are returned + status_t getFft(uint8_t *fft); + +private: + + static const uint32_t CAPTURE_RATE_MAX = 20000; + static const uint32_t CAPTURE_RATE_DEF = 10000; + static const uint32_t CAPTURE_SIZE_DEF = VISUALIZER_CAPTURE_SIZE_MAX; + + /* internal class to handle the callback */ + class CaptureThread : public Thread + { + public: + CaptureThread(Visualizer& receiver, uint32_t captureRate, bool bCanCallJava = false); + + private: + friend class Visualizer; + virtual bool threadLoop(); + virtual status_t readyToRun(); + virtual void onFirstRef(); + Visualizer& mReceiver; + Mutex mLock; + uint32_t mSleepTimeUs; + }; + + status_t doFft(uint8_t *fft, uint8_t *waveform); + void periodicCapture(); + uint32_t initCaptureSize(); + + Mutex mLock; + uint32_t mCaptureRate; + uint32_t mCaptureSize; + uint32_t mSampleRate; + capture_cbk_t mCaptureCallBack; + void *mCaptureCbkUser; + sp<CaptureThread> mCaptureThread; + uint32_t mCaptureFlags; + void *mFftTable; +}; + + +}; // namespace android + +#endif // ANDROID_MEDIA_VISUALIZER_H diff --git a/include/media/mediaplayer.h b/include/media/mediaplayer.h index 62a4e50..4963f73 100644 --- a/include/media/mediaplayer.h +++ b/include/media/mediaplayer.h @@ -166,7 +166,6 @@ public: void notify(int msg, int ext1, int ext2); static sp<IMemory> decode(const char* url, uint32_t *pSampleRate, int* pNumChannels, int* pFormat); static sp<IMemory> decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels, int* pFormat); - static int snoop(short *data, int len, int kind); status_t invoke(const Parcel& request, Parcel *reply); status_t setMetadataFilter(const Parcel& filter); status_t getMetadata(bool update_only, bool apply_filter, Parcel *metadata); diff --git a/media/libeffects/Android.mk b/media/libeffects/Android.mk index b5f1d42..54e87f3 100644 --- a/media/libeffects/Android.mk +++ b/media/libeffects/Android.mk @@ -94,3 +94,33 @@ LOCAL_PRELINK_MODULE := false include $(BUILD_SHARED_LIBRARY) endif + + +# Visualizer library +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + EffectVisualizer.cpp + +LOCAL_CFLAGS+= -O2 + +LOCAL_SHARED_LIBRARIES := \ + libcutils + +LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/soundfx +LOCAL_MODULE:= libvisualizer + +ifeq ($(TARGET_OS)-$(TARGET_SIMULATOR),linux-true) +LOCAL_LDLIBS += -ldl +endif + +ifneq ($(TARGET_SIMULATOR),true) +LOCAL_SHARED_LIBRARIES += libdl +endif + +LOCAL_C_INCLUDES := \ + $(call include-path-for, graphics corecg) + +LOCAL_PRELINK_MODULE := false + +include $(BUILD_SHARED_LIBRARY) diff --git a/media/libeffects/EffectVisualizer.cpp b/media/libeffects/EffectVisualizer.cpp new file mode 100644 index 0000000..f27e296 --- /dev/null +++ b/media/libeffects/EffectVisualizer.cpp @@ -0,0 +1,401 @@ +/* + * Copyright (C) 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_TAG "Visualizer" +//#define LOG_NDEBUG 0 +#include <cutils/log.h> +#include <assert.h> +#include <stdlib.h> +#include <string.h> +#include <new> +#include <media/EffectVisualizerApi.h> + +namespace android { + +// effect_interface_t interface implementation for visualizer effect +extern "C" const struct effect_interface_s gVisualizerInterface; + +// Google Visualizer UUID: d069d9e0-8329-11df-9168-0002a5d5c51b +const effect_descriptor_t gVisualizerDescriptor = { + {0xe46b26a0, 0xdddd, 0x11db, 0x8afd, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, // type + {0xd069d9e0, 0x8329, 0x11df, 0x9168, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, // uuid + EFFECT_API_VERSION, + (EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_INSERT_FIRST), + 0, // TODO + 1, + "Visualizer", + "Google Inc.", +}; + +enum visualizer_state_e { + VISUALIZER_STATE_UNINITIALIZED, + VISUALIZER_STATE_INITIALIZED, + VISUALIZER_STATE_ACTIVE, +}; + +struct VisualizerContext { + const struct effect_interface_s *mItfe; + effect_config_t mConfig; + uint32_t mState; + uint32_t mCaptureIdx; + uint32_t mCaptureSize; + uint32_t mCurrentBuf; + uint8_t mCaptureBuf[2][VISUALIZER_CAPTURE_SIZE_MAX]; +}; + + +// +//--- Local functions +// + +void Visualizer_reset(VisualizerContext *pContext) +{ + pContext->mCaptureIdx = 0; + pContext->mCurrentBuf = 0; + memset(pContext->mCaptureBuf[0], 0, VISUALIZER_CAPTURE_SIZE_MAX); + memset(pContext->mCaptureBuf[1], 0, VISUALIZER_CAPTURE_SIZE_MAX); +} + +//---------------------------------------------------------------------------- +// Visualizer_configure() +//---------------------------------------------------------------------------- +// Purpose: Set input and output audio configuration. +// +// Inputs: +// pContext: effect engine context +// pConfig: pointer to effect_config_t structure holding input and output +// configuration parameters +// +// Outputs: +// +//---------------------------------------------------------------------------- + +int Visualizer_configure(VisualizerContext *pContext, effect_config_t *pConfig) +{ + LOGV("Visualizer_configure start"); + + if (pConfig->inputCfg.samplingRate != pConfig->outputCfg.samplingRate) return -EINVAL; + if (pConfig->inputCfg.channels != pConfig->outputCfg.channels) return -EINVAL; + if (pConfig->inputCfg.format != pConfig->outputCfg.format) return -EINVAL; + if (pConfig->inputCfg.channels != CHANNEL_STEREO) return -EINVAL; + if (pConfig->outputCfg.accessMode != EFFECT_BUFFER_ACCESS_WRITE && + pConfig->outputCfg.accessMode != EFFECT_BUFFER_ACCESS_ACCUMULATE) return -EINVAL; + if (pConfig->inputCfg.format != SAMPLE_FORMAT_PCM_S15) return -EINVAL; + + memcpy(&pContext->mConfig, pConfig, sizeof(effect_config_t)); + + Visualizer_reset(pContext); + + return 0; +} + + +//---------------------------------------------------------------------------- +// Visualizer_init() +//---------------------------------------------------------------------------- +// Purpose: Initialize engine with default configuration. +// +// Inputs: +// pContext: effect engine context +// +// Outputs: +// +//---------------------------------------------------------------------------- + +int Visualizer_init(VisualizerContext *pContext) +{ + pContext->mConfig.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ; + pContext->mConfig.inputCfg.channels = CHANNEL_STEREO; + pContext->mConfig.inputCfg.format = SAMPLE_FORMAT_PCM_S15; + pContext->mConfig.inputCfg.samplingRate = 44100; + pContext->mConfig.inputCfg.bufferProvider.getBuffer = NULL; + pContext->mConfig.inputCfg.bufferProvider.releaseBuffer = NULL; + pContext->mConfig.inputCfg.bufferProvider.cookie = NULL; + pContext->mConfig.inputCfg.mask = EFFECT_CONFIG_ALL; + pContext->mConfig.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_ACCUMULATE; + pContext->mConfig.outputCfg.channels = CHANNEL_STEREO; + pContext->mConfig.outputCfg.format = SAMPLE_FORMAT_PCM_S15; + pContext->mConfig.outputCfg.samplingRate = 44100; + pContext->mConfig.outputCfg.bufferProvider.getBuffer = NULL; + pContext->mConfig.outputCfg.bufferProvider.releaseBuffer = NULL; + pContext->mConfig.outputCfg.bufferProvider.cookie = NULL; + pContext->mConfig.outputCfg.mask = EFFECT_CONFIG_ALL; + + pContext->mCaptureSize = VISUALIZER_CAPTURE_SIZE_MAX; + + Visualizer_configure(pContext, &pContext->mConfig); + + return 0; +} + +// +//--- Effect Library Interface Implementation +// + +extern "C" int EffectQueryNumberEffects(uint32_t *pNumEffects) { + *pNumEffects = 1; + return 0; +} + +extern "C" int EffectQueryEffect(uint32_t index, effect_descriptor_t *pDescriptor) { + if (pDescriptor == NULL) { + return -EINVAL; + } + if (index > 0) { + return -EINVAL; + } + memcpy(pDescriptor, &gVisualizerDescriptor, sizeof(effect_descriptor_t)); + return 0; +} + +extern "C" int EffectCreate(effect_uuid_t *uuid, + int32_t sessionId, + int32_t ioId, + effect_interface_t *pInterface) { + int ret; + int i; + + if (pInterface == NULL || uuid == NULL) { + return -EINVAL; + } + + if (memcmp(uuid, &gVisualizerDescriptor.uuid, sizeof(effect_uuid_t)) != 0) { + return -EINVAL; + } + + VisualizerContext *pContext = new VisualizerContext; + + pContext->mItfe = &gVisualizerInterface; + pContext->mState = VISUALIZER_STATE_UNINITIALIZED; + + ret = Visualizer_init(pContext); + if (ret < 0) { + LOGW("EffectCreate() init failed"); + delete pContext; + return ret; + } + + *pInterface = (effect_interface_t)pContext; + + pContext->mState = VISUALIZER_STATE_INITIALIZED; + + LOGV("EffectCreate %p", pContext); + + return 0; + +} + +extern "C" int EffectRelease(effect_interface_t interface) { + VisualizerContext * pContext = (VisualizerContext *)interface; + + LOGV("EffectRelease %p", interface); + if (pContext == NULL) { + return -EINVAL; + } + pContext->mState = VISUALIZER_STATE_UNINITIALIZED; + delete pContext; + + return 0; +} + +// +//--- Effect Control Interface Implementation +// + +static inline int16_t clamp16(int32_t sample) +{ + if ((sample>>15) ^ (sample>>31)) + sample = 0x7FFF ^ (sample>>31); + return sample; +} + +extern "C" int Visualizer_process( + effect_interface_t self,audio_buffer_t *inBuffer, audio_buffer_t *outBuffer) +{ + android::VisualizerContext * pContext = (android::VisualizerContext *)self; + + if (pContext == NULL) { + return -EINVAL; + } + if (pContext->mState != VISUALIZER_STATE_ACTIVE) { + return -ENOSYS; + } + + if (inBuffer == NULL || inBuffer->raw == NULL || + outBuffer == NULL || outBuffer->raw == NULL || + inBuffer->frameCount != outBuffer->frameCount || + inBuffer->frameCount == 0) { + return -EINVAL; + } + + // all code below assumes stereo 16 bit PCM output and input + uint32_t captIdx; + uint32_t inIdx; + uint8_t *buf = pContext->mCaptureBuf[pContext->mCurrentBuf]; + for (inIdx = 0, captIdx = pContext->mCaptureIdx; + inIdx < inBuffer->frameCount && captIdx < pContext->mCaptureSize; + inIdx++, captIdx++) { + int32_t smp = inBuffer->s16[2 * inIdx] + inBuffer->s16[2 * inIdx + 1]; + smp = (smp + (1 << 8)) >> 9; + buf[captIdx] = ((uint8_t)smp)^0x80; + } + pContext->mCaptureIdx = captIdx; + + // go to next buffer when buffer full + if (pContext->mCaptureIdx == pContext->mCaptureSize) { + pContext->mCurrentBuf ^= 1; + pContext->mCaptureIdx = 0; + } + + if (inBuffer->raw != outBuffer->raw) { + if (pContext->mConfig.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE) { + for (size_t i = 0; i < outBuffer->frameCount*2; i++) { + outBuffer->s16[i] = clamp16(outBuffer->s16[i] + inBuffer->s16[i]); + } + } else { + memcpy(outBuffer->raw, inBuffer->raw, outBuffer->frameCount * 2 * sizeof(int16_t)); + } + } + return 0; +} // end Visualizer_process + +extern "C" int Visualizer_command(effect_interface_t self, int cmdCode, int cmdSize, + void *pCmdData, int *replySize, void *pReplyData) { + + android::VisualizerContext * pContext = (android::VisualizerContext *)self; + int retsize; + + if (pContext == NULL || pContext->mState == VISUALIZER_STATE_UNINITIALIZED) { + return -EINVAL; + } + +// LOGV("Visualizer_command command %d cmdSize %d",cmdCode, cmdSize); + + switch (cmdCode) { + case EFFECT_CMD_INIT: + if (pReplyData == NULL || *replySize != sizeof(int)) { + return -EINVAL; + } + *(int *) pReplyData = Visualizer_init(pContext); + break; + case EFFECT_CMD_CONFIGURE: + if (pCmdData == NULL || cmdSize != sizeof(effect_config_t) + || pReplyData == NULL || *replySize != sizeof(int)) { + return -EINVAL; + } + *(int *) pReplyData = Visualizer_configure(pContext, + (effect_config_t *) pCmdData); + break; + case EFFECT_CMD_RESET: + Visualizer_reset(pContext); + break; + case EFFECT_CMD_ENABLE: + if (pReplyData == NULL || *replySize != sizeof(int)) { + return -EINVAL; + } + if (pContext->mState != VISUALIZER_STATE_INITIALIZED) { + return -ENOSYS; + } + pContext->mState = VISUALIZER_STATE_ACTIVE; + LOGV("EFFECT_CMD_ENABLE() OK"); + *(int *)pReplyData = 0; + break; + case EFFECT_CMD_DISABLE: + if (pReplyData == NULL || *replySize != sizeof(int)) { + return -EINVAL; + } + if (pContext->mState != VISUALIZER_STATE_ACTIVE) { + return -ENOSYS; + } + pContext->mState = VISUALIZER_STATE_INITIALIZED; + LOGV("EFFECT_CMD_DISABLE() OK"); + *(int *)pReplyData = 0; + break; + case EFFECT_CMD_GET_PARAM: { + if (pCmdData == NULL || + cmdSize != (int)(sizeof(effect_param_t) + sizeof(uint32_t)) || + pReplyData == NULL || + *replySize < (int)(sizeof(effect_param_t) + sizeof(uint32_t) + sizeof(uint32_t))) { + return -EINVAL; + } + memcpy(pReplyData, pCmdData, sizeof(effect_param_t) + sizeof(uint32_t)); + effect_param_t *p = (effect_param_t *)pReplyData; + p->status = 0; + *replySize = sizeof(effect_param_t) + sizeof(uint32_t); + if (p->psize != sizeof(uint32_t) || + *(uint32_t *)p->data != VISU_PARAM_CAPTURE_SIZE) { + p->status = -EINVAL; + break; + } + LOGV("get mCaptureSize = %d", pContext->mCaptureSize); + *((uint32_t *)p->data + 1) = pContext->mCaptureSize; + p->vsize = sizeof(uint32_t); + *replySize += sizeof(uint32_t); + } break; + case EFFECT_CMD_SET_PARAM: { + if (pCmdData == NULL || + cmdSize != (int)(sizeof(effect_param_t) + sizeof(uint32_t) + sizeof(uint32_t)) || + pReplyData == NULL || *replySize != sizeof(int32_t)) { + return -EINVAL; + } + *(int32_t *)pReplyData = 0; + effect_param_t *p = (effect_param_t *)pCmdData; + if (p->psize != sizeof(uint32_t) || + p->vsize != sizeof(uint32_t) || + *(uint32_t *)p->data != VISU_PARAM_CAPTURE_SIZE) { + *(int32_t *)pReplyData = -EINVAL; + break;; + } + pContext->mCaptureSize = *((uint32_t *)p->data + 1); + LOGV("set mCaptureSize = %d", pContext->mCaptureSize); + } break; + case EFFECT_CMD_SET_DEVICE: + case EFFECT_CMD_SET_VOLUME: + case EFFECT_CMD_SET_AUDIO_MODE: + break; + + + case VISU_CMD_CAPTURE: + if (pReplyData == NULL || *replySize != (int)pContext->mCaptureSize) { + LOGV("VISU_CMD_CAPTURE() error *replySize %d pContext->mCaptureSize %d", + *replySize, pContext->mCaptureSize); + return -EINVAL; + } + if (pContext->mState == VISUALIZER_STATE_ACTIVE) { + memcpy(pReplyData, + pContext->mCaptureBuf[pContext->mCurrentBuf ^ 1], + pContext->mCaptureSize); + } else { + memset(pReplyData, 0x80, pContext->mCaptureSize); + } + break; + + default: + LOGW("Visualizer_command invalid command %d",cmdCode); + return -EINVAL; + } + + return 0; +} + +// effect_interface_t interface implementation for visualizer effect +const struct effect_interface_s gVisualizerInterface = { + Visualizer_process, + Visualizer_command +}; + +} // namespace + diff --git a/media/libmedia/Android.mk b/media/libmedia/Android.mk index de9e51d..977e6be 100644 --- a/media/libmedia/Android.mk +++ b/media/libmedia/Android.mk @@ -30,7 +30,8 @@ LOCAL_SRC_FILES:= \ MediaProfiles.cpp \ IEffect.cpp \ IEffectClient.cpp \ - AudioEffect.cpp + AudioEffect.cpp \ + Visualizer.cpp LOCAL_SHARED_LIBRARIES := \ libui libcutils libutils libbinder libsonivox libicuuc libexpat libsurfaceflinger_client libcamera_client diff --git a/media/libmedia/AudioEffect.cpp b/media/libmedia/AudioEffect.cpp index 4afa2dc..783249d 100644 --- a/media/libmedia/AudioEffect.cpp +++ b/media/libmedia/AudioEffect.cpp @@ -171,7 +171,7 @@ AudioEffect::~AudioEffect() LOGV("Destructor %p", this); if (mStatus == NO_ERROR || mStatus == ALREADY_EXISTS) { - disable(); + setEnabled(false); if (mIEffect != NULL) { mIEffect->disconnect(); mIEffect->asBinder()->unlinkToDeath(mIEffectClient); @@ -196,36 +196,28 @@ effect_descriptor_t AudioEffect::descriptor() const return mDescriptor; } -bool AudioEffect::isEnabled() const +bool AudioEffect::getEnabled() const { return (mEnabled != 0); } -status_t AudioEffect::enable() +status_t AudioEffect::setEnabled(bool enabled) { if (mStatus != NO_ERROR) { return INVALID_OPERATION; } - LOGV("enable %p", this); - if (android_atomic_or(1, &mEnabled) == 0) { - return mIEffect->enable(); - } - - return INVALID_OPERATION; -} - -status_t AudioEffect::disable() -{ - if (mStatus != NO_ERROR) { - return INVALID_OPERATION; - } - LOGV("disable %p", this); - - if (android_atomic_and(~1, &mEnabled) == 1) { - return mIEffect->disable(); + if (enabled) { + LOGV("enable %p", this); + if (android_atomic_or(1, &mEnabled) == 0) { + return mIEffect->enable(); + } + } else { + LOGV("disable %p", this); + if (android_atomic_and(~1, &mEnabled) == 1) { + return mIEffect->disable(); + } } - return INVALID_OPERATION; } @@ -349,7 +341,7 @@ void AudioEffect::controlStatusChanged(bool controlGranted) void AudioEffect::enableStatusChanged(bool enabled) { - LOGV("enableStatusChanged %p enabled %d", this, enabled); + LOGV("enableStatusChanged %p enabled %d mCbf %p", this, enabled, mCbf); if (mStatus == ALREADY_EXISTS) { mEnabled = enabled; if (mCbf) { diff --git a/media/libmedia/IMediaPlayerService.cpp b/media/libmedia/IMediaPlayerService.cpp index 1ae222e..4abfa75 100644 --- a/media/libmedia/IMediaPlayerService.cpp +++ b/media/libmedia/IMediaPlayerService.cpp @@ -35,8 +35,7 @@ enum { DECODE_FD, CREATE_MEDIA_RECORDER, CREATE_METADATA_RETRIEVER, - GET_OMX, - SNOOP + GET_OMX }; class BpMediaPlayerService: public BpInterface<IMediaPlayerService> @@ -134,14 +133,6 @@ public: return interface_cast<IMemory>(reply.readStrongBinder()); } - virtual sp<IMemory> snoop() - { - Parcel data, reply; - data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor()); - remote()->transact(SNOOP, data, &reply); - return interface_cast<IMemory>(reply.readStrongBinder()); - } - virtual sp<IOMX> getOMX() { Parcel data, reply; data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor()); @@ -221,12 +212,6 @@ status_t BnMediaPlayerService::onTransact( reply->writeStrongBinder(player->asBinder()); return NO_ERROR; } break; - case SNOOP: { - CHECK_INTERFACE(IMediaPlayerService, data, reply); - sp<IMemory> snooped_audio = snoop(); - reply->writeStrongBinder(snooped_audio->asBinder()); - return NO_ERROR; - } break; case CREATE_MEDIA_RECORDER: { CHECK_INTERFACE(IMediaPlayerService, data, reply); pid_t pid = data.readInt32(); diff --git a/media/libmedia/Visualizer.cpp b/media/libmedia/Visualizer.cpp new file mode 100644 index 0000000..47e96e5 --- /dev/null +++ b/media/libmedia/Visualizer.cpp @@ -0,0 +1,330 @@ +/* +** +** 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 <media/Visualizer.h> + +extern "C" { +#define FLOATING_POINT 1 +#include "fftwrap.h" +} + +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(); + if (mCaptureSize != 0) { + mFftTable = spx_fft_init(mCaptureSize); + } else { + mFftTable = NULL; + } +} + +Visualizer::~Visualizer() +{ + if (mFftTable != NULL) { + spx_fft_destroy(mFftTable); + } +} + +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 || + AudioSystem::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 = VISU_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; + if (mFftTable != NULL) { + spx_fft_destroy(mFftTable); + } + mFftTable = spx_fft_init(mCaptureSize); + LOGV("setCaptureSize size %d mFftTable %p", mCaptureSize, mFftTable); + } + + 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) { + int32_t replySize = mCaptureSize; + status_t status = command(VISU_CMD_CAPTURE, 0, NULL, &replySize, waveform); + if (replySize == 0) { + status = NOT_ENOUGH_DATA; + } + } else { + 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_t 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) +{ + if (mFftTable == NULL) { + return NO_INIT; + } + + float fsrc[mCaptureSize]; + for (uint32_t i = 0; i < mCaptureSize; i++) { + fsrc[i] = (int16_t)(waveform[i] ^ 0x80) << 8; + } + float fdst[mCaptureSize]; + spx_fft_float(mFftTable, fsrc, fdst); + for (uint32_t i = 0; i < mCaptureSize; i++) { + fft[i] = (uint8_t)((int32_t)fdst[i] >> 8); + } + 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 = VISU_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 + diff --git a/media/libmedia/mediaplayer.cpp b/media/libmedia/mediaplayer.cpp index d5a3c13..b43f75f 100644 --- a/media/libmedia/mediaplayer.cpp +++ b/media/libmedia/mediaplayer.cpp @@ -658,61 +658,4 @@ void MediaPlayer::died() } -extern "C" { -#define FLOATING_POINT 1 -#include "fftwrap.h" -} - -static void *ffttable = NULL; - -// peeks at the audio data and fills 'data' with the requested kind -// (currently kind=0 returns mono 16 bit PCM data, and kind=1 returns -// 256 point FFT data). Return value is number of samples returned, -// which may be 0. -/*static*/ int MediaPlayer::snoop(short* data, int len, int kind) { - - sp<IMemory> p; - const sp<IMediaPlayerService>& service = getMediaPlayerService(); - if (service != 0) { - // Take a peek at the waveform. The returned data consists of 16 bit mono PCM data. - p = service->snoop(); - - if (p == NULL) { - return 0; - } - - if (kind == 0) { // return waveform data - int plen = p->size(); - len *= 2; // number of shorts -> number of bytes - short *src = (short*) p->pointer(); - if (plen > len) { - plen = len; - } - memcpy(data, src, plen); - return plen / sizeof(short); // return number of samples - } else if (kind == 1) { - // TODO: use a more efficient FFT - // Right now this uses the speex library, which is compiled to do a float FFT - if (!ffttable) ffttable = spx_fft_init(512); - short *usrc = (short*) p->pointer(); - float fsrc[512]; - for (int i=0;i<512;i++) - fsrc[i] = usrc[i]; - float fdst[512]; - spx_fft_float(ffttable, fsrc, fdst); - if (len > 512) { - len = 512; - } - len /= 2; // only half the output data is valid - for (int i=0; i < len; i++) - data[i] = fdst[i]; - return len; - } - - } else { - LOGE("Unable to locate media service"); - } - return 0; -} - }; // namespace android diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp index 82d5c14..4872047 100644 --- a/media/libmediaplayerservice/MediaPlayerService.cpp +++ b/media/libmediaplayerservice/MediaPlayerService.cpp @@ -1265,98 +1265,6 @@ Exit: return mem; } -/* - * Avert your eyes, ugly hack ahead. - * The following is to support music visualizations. - */ - -static const int NUMVIZBUF = 32; -static const int VIZBUFFRAMES = 1024; -static const int BUFTIMEMSEC = NUMVIZBUF * VIZBUFFRAMES * 1000 / 44100; -static const int TOTALBUFTIMEMSEC = NUMVIZBUF * BUFTIMEMSEC; - -static bool gotMem = false; -static sp<MemoryHeapBase> heap; -static sp<MemoryBase> mem[NUMVIZBUF]; -static uint64_t endTime; -static uint64_t lastReadTime; -static uint64_t lastWriteTime; -static int writeIdx = 0; - -static void allocVizBufs() { - if (!gotMem) { - heap = new MemoryHeapBase(NUMVIZBUF * VIZBUFFRAMES * 2, 0, "snooper"); - for (int i=0;i<NUMVIZBUF;i++) { - mem[i] = new MemoryBase(heap, VIZBUFFRAMES * 2 * i, VIZBUFFRAMES * 2); - } - endTime = 0; - gotMem = true; - } -} - - -/* - * Get a buffer of audio data that is about to be played. - * We don't synchronize this because in practice the writer - * is ahead of the reader, and even if we did happen to catch - * a buffer while it's being written, it's just a visualization, - * so no harm done. - */ -static sp<MemoryBase> getVizBuffer() { - - allocVizBufs(); - - lastReadTime = uptimeMillis(); - - // if there is no recent buffer (yet), just return empty handed - if (lastWriteTime + TOTALBUFTIMEMSEC < lastReadTime) { - //LOGI("@@@@ no audio data to look at yet: %d + %d < %d", (int)lastWriteTime, TOTALBUFTIMEMSEC, (int)lastReadTime); - return NULL; - } - - int timedelta = endTime - lastReadTime; - if (timedelta < 0) timedelta = 0; - int framedelta = timedelta * 44100 / 1000; - int headIdx = (writeIdx - framedelta) / VIZBUFFRAMES - 1; - while (headIdx < 0) { - headIdx += NUMVIZBUF; - } - return mem[headIdx]; -} - -// Append the data to the vizualization buffer -static void makeVizBuffers(const char *data, int len, uint64_t time) { - - allocVizBufs(); - - uint64_t startTime = time; - const int frameSize = 4; // 16 bit stereo sample is 4 bytes - int offset = writeIdx; - int maxoff = heap->getSize() / 2; // in shorts - short *base = (short*)heap->getBase(); - short *src = (short*)data; - while (len > 0) { - - // Degrade quality by mixing to mono and clearing the lowest 3 bits. - // This should still be good enough for a visualization - base[offset++] = ((int(src[0]) + int(src[1])) >> 1) & ~0x7; - src += 2; - len -= frameSize; - if (offset >= maxoff) { - offset = 0; - } - } - writeIdx = offset; - endTime = time + (len / frameSize) / 44; - //LOGI("@@@ stored buffers from %d to %d", uint32_t(startTime), uint32_t(time)); -} - -sp<IMemory> MediaPlayerService::snoop() -{ - sp<MemoryBase> mem = getVizBuffer(); - return mem; -} - #undef LOG_TAG #define LOG_TAG "AudioSink" @@ -1371,7 +1279,6 @@ MediaPlayerService::AudioOutput::AudioOutput(int sessionId) mRightVolume = 1.0; mLatency = 0; mMsecsPerFrame = 0; - mNumFramesWritten = 0; setMinBufferCount(); } @@ -1516,30 +1423,9 @@ void MediaPlayerService::AudioOutput::start() if (mTrack) { mTrack->setVolume(mLeftVolume, mRightVolume); mTrack->start(); - mTrack->getPosition(&mNumFramesWritten); } } -void MediaPlayerService::AudioOutput::snoopWrite(const void* buffer, size_t size) { - // Only make visualization buffers if anyone recently requested visualization data - uint64_t now = uptimeMillis(); - if (lastReadTime + TOTALBUFTIMEMSEC >= now) { - // Based on the current play counter, the number of frames written and - // the current real time we can calculate the approximate real start - // time of the buffer we're about to write. - uint32_t pos; - mTrack->getPosition(&pos); - - // we're writing ahead by this many frames: - int ahead = mNumFramesWritten - pos; - //LOGI("@@@ written: %d, playpos: %d, latency: %d", mNumFramesWritten, pos, mTrack->latency()); - // which is this many milliseconds, assuming 44100 Hz: - ahead /= 44; - - makeVizBuffers((const char*)buffer, size, now + ahead + mTrack->latency()); - lastWriteTime = now; - } -} ssize_t MediaPlayerService::AudioOutput::write(const void* buffer, size_t size) @@ -1548,9 +1434,7 @@ ssize_t MediaPlayerService::AudioOutput::write(const void* buffer, size_t size) //LOGV("write(%p, %u)", buffer, size); if (mTrack) { - snoopWrite(buffer, size); ssize_t ret = mTrack->write(buffer, size); - mNumFramesWritten += ret / 4; // assume 16 bit stereo return ret; } return NO_INIT; @@ -1560,7 +1444,6 @@ void MediaPlayerService::AudioOutput::stop() { LOGV("stop"); if (mTrack) mTrack->stop(); - lastWriteTime = 0; } void MediaPlayerService::AudioOutput::flush() @@ -1573,7 +1456,6 @@ void MediaPlayerService::AudioOutput::pause() { LOGV("pause"); if (mTrack) mTrack->pause(); - lastWriteTime = 0; } void MediaPlayerService::AudioOutput::close() @@ -1609,9 +1491,6 @@ void MediaPlayerService::AudioOutput::CallbackWrapper( buffer->size = actualSize; - if (actualSize > 0) { - me->snoopWrite(buffer->raw, actualSize); - } } #undef LOG_TAG diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h index 60b91c6..39f525e 100644 --- a/media/libmediaplayerservice/MediaPlayerService.h +++ b/media/libmediaplayerservice/MediaPlayerService.h @@ -113,9 +113,6 @@ class MediaPlayerService : public BnMediaPlayerService static bool mIsOnEmulator; static int mMinBufferCount; // 12 for emulator; otherwise 4 - public: // visualization hack support - uint32_t mNumFramesWritten; - void snoopWrite(const void*, size_t); }; class AudioCache : public MediaPlayerBase::AudioSink @@ -191,7 +188,6 @@ public: virtual sp<IMediaPlayer> create(pid_t pid, const sp<IMediaPlayerClient>& client, int fd, int64_t offset, int64_t length, int audioSessionId); virtual sp<IMemory> decode(const char* url, uint32_t *pSampleRate, int* pNumChannels, int* pFormat); virtual sp<IMemory> decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels, int* pFormat); - virtual sp<IMemory> snoop(); virtual sp<IOMX> getOMX(); virtual status_t dump(int fd, const Vector<String16>& args); |