From 89fa4ad53f2f4d57adbc97ae1149fc00c9b6f3c5 Mon Sep 17 00:00:00 2001 From: The Android Open Source Project Date: Tue, 3 Mar 2009 19:31:44 -0800 Subject: auto import from //depot/cupcake/@135843 --- media/libmedia/Android.mk | 39 ++ media/libmedia/AudioRecord.cpp | 574 ++++++++++++++++ media/libmedia/AudioSystem.cpp | 383 +++++++++++ media/libmedia/AudioTrack.cpp | 1021 ++++++++++++++++++++++++++++ media/libmedia/IAudioFlinger.cpp | 553 +++++++++++++++ media/libmedia/IAudioFlingerClient.cpp | 77 +++ media/libmedia/IAudioRecord.cpp | 100 +++ media/libmedia/IAudioTrack.cpp | 140 ++++ media/libmedia/IMediaMetadataRetriever.cpp | 218 ++++++ media/libmedia/IMediaPlayer.cpp | 275 ++++++++ media/libmedia/IMediaPlayerClient.cpp | 77 +++ media/libmedia/IMediaPlayerService.cpp | 198 ++++++ media/libmedia/IMediaRecorder.cpp | 417 ++++++++++++ media/libmedia/JetPlayer.cpp | 489 +++++++++++++ media/libmedia/MODULE_LICENSE_APACHE2 | 0 media/libmedia/NOTICE | 190 ++++++ media/libmedia/ToneGenerator.cpp | 730 ++++++++++++++++++++ media/libmedia/mediametadataretriever.cpp | 188 +++++ media/libmedia/mediaplayer.cpp | 624 +++++++++++++++++ media/libmedia/mediarecorder.cpp | 617 +++++++++++++++++ 20 files changed, 6910 insertions(+) create mode 100644 media/libmedia/Android.mk create mode 100644 media/libmedia/AudioRecord.cpp create mode 100644 media/libmedia/AudioSystem.cpp create mode 100644 media/libmedia/AudioTrack.cpp create mode 100644 media/libmedia/IAudioFlinger.cpp create mode 100644 media/libmedia/IAudioFlingerClient.cpp create mode 100644 media/libmedia/IAudioRecord.cpp create mode 100644 media/libmedia/IAudioTrack.cpp create mode 100644 media/libmedia/IMediaMetadataRetriever.cpp create mode 100644 media/libmedia/IMediaPlayer.cpp create mode 100644 media/libmedia/IMediaPlayerClient.cpp create mode 100644 media/libmedia/IMediaPlayerService.cpp create mode 100644 media/libmedia/IMediaRecorder.cpp create mode 100644 media/libmedia/JetPlayer.cpp create mode 100644 media/libmedia/MODULE_LICENSE_APACHE2 create mode 100644 media/libmedia/NOTICE create mode 100644 media/libmedia/ToneGenerator.cpp create mode 100644 media/libmedia/mediametadataretriever.cpp create mode 100644 media/libmedia/mediaplayer.cpp create mode 100644 media/libmedia/mediarecorder.cpp (limited to 'media/libmedia') diff --git a/media/libmedia/Android.mk b/media/libmedia/Android.mk new file mode 100644 index 0000000..8020da2 --- /dev/null +++ b/media/libmedia/Android.mk @@ -0,0 +1,39 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + AudioTrack.cpp \ + IAudioFlinger.cpp \ + IAudioFlingerClient.cpp \ + IAudioTrack.cpp \ + IAudioRecord.cpp \ + AudioRecord.cpp \ + AudioSystem.cpp \ + mediaplayer.cpp \ + IMediaPlayerService.cpp \ + IMediaPlayerClient.cpp \ + IMediaPlayer.cpp \ + IMediaRecorder.cpp \ + mediarecorder.cpp \ + IMediaMetadataRetriever.cpp \ + mediametadataretriever.cpp \ + ToneGenerator.cpp \ + JetPlayer.cpp + +LOCAL_SHARED_LIBRARIES := \ + libui libcutils libutils libsonivox + +LOCAL_MODULE:= libmedia + +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) + +include $(BUILD_SHARED_LIBRARY) diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp new file mode 100644 index 0000000..7594ff0 --- /dev/null +++ b/media/libmedia/AudioRecord.cpp @@ -0,0 +1,574 @@ +/* +** +** Copyright 2008, 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 "AudioRecord" + +#include +#include + +#include +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define LIKELY( exp ) (__builtin_expect( (exp) != 0, true )) +#define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false )) + +namespace android { + +// --------------------------------------------------------------------------- + +AudioRecord::AudioRecord() + : mStatus(NO_INIT) +{ +} + +AudioRecord::AudioRecord( + int streamType, + uint32_t sampleRate, + int format, + int channelCount, + int frameCount, + uint32_t flags, + callback_t cbf, + void* user, + int notificationFrames) + : mStatus(NO_INIT) +{ + mStatus = set(streamType, sampleRate, format, channelCount, + frameCount, flags, cbf, user, notificationFrames); +} + +AudioRecord::~AudioRecord() +{ + if (mStatus == NO_ERROR) { + // Make sure that callback function exits in the case where + // it is looping on buffer empty condition in obtainBuffer(). + // Otherwise the callback thread will never exit. + stop(); + if (mClientRecordThread != 0) { + mCblk->cv.signal(); + mClientRecordThread->requestExitAndWait(); + mClientRecordThread.clear(); + } + mAudioRecord.clear(); + IPCThreadState::self()->flushCommands(); + } +} + +status_t AudioRecord::set( + int streamType, + uint32_t sampleRate, + int format, + int channelCount, + int frameCount, + uint32_t flags, + callback_t cbf, + void* user, + int notificationFrames, + bool threadCanCallJava) +{ + + LOGV("set(): sampleRate %d, channelCount %d, frameCount %d",sampleRate, channelCount, frameCount); + if (mAudioFlinger != 0) { + return INVALID_OPERATION; + } + + const sp& audioFlinger = AudioSystem::get_audio_flinger(); + if (audioFlinger == 0) { + return NO_INIT; + } + + if (streamType == DEFAULT_INPUT) { + streamType = MIC_INPUT; + } + + if (sampleRate == 0) { + sampleRate = DEFAULT_SAMPLE_RATE; + } + // these below should probably come from the audioFlinger too... + if (format == 0) { + format = AudioSystem::PCM_16_BIT; + } + if (channelCount == 0) { + channelCount = 1; + } + + // validate parameters + if (format != AudioSystem::PCM_16_BIT) { + return BAD_VALUE; + } + if (channelCount != 1 && channelCount != 2) { + return BAD_VALUE; + } + + // validate framecount + size_t inputBuffSizeInBytes = -1; + if (AudioSystem::getInputBufferSize(sampleRate, format, channelCount, &inputBuffSizeInBytes) + != NO_ERROR) { + LOGE("AudioSystem could not query the input buffer size."); + return NO_INIT; + } + if (inputBuffSizeInBytes == 0) { + LOGE("Recording parameters are not supported: sampleRate %d, channelCount %d, format %d", + sampleRate, channelCount, format); + return BAD_VALUE; + } + int frameSizeInBytes = channelCount * (format == AudioSystem::PCM_16_BIT ? 2 : 1); + + // We use 2* size of input buffer for ping pong use of record buffer. + int minFrameCount = 2 * inputBuffSizeInBytes / frameSizeInBytes; + LOGV("AudioRecord::set() minFrameCount = %d", minFrameCount); + + if (frameCount == 0) { + frameCount = minFrameCount; + } else if (frameCount < minFrameCount) { + return BAD_VALUE; + } + + if (notificationFrames == 0) { + notificationFrames = frameCount/2; + } + + // open record channel + status_t status; + sp record = audioFlinger->openRecord(getpid(), streamType, + sampleRate, format, + channelCount, + frameCount, + ((uint16_t)flags) << 16, + &status); + if (record == 0) { + LOGE("AudioFlinger could not create record track, status: %d", status); + return status; + } + sp cblk = record->getCblk(); + if (cblk == 0) { + return NO_INIT; + } + if (cbf != 0) { + mClientRecordThread = new ClientRecordThread(*this, threadCanCallJava); + if (mClientRecordThread == 0) { + return NO_INIT; + } + } + + mStatus = NO_ERROR; + + mAudioFlinger = audioFlinger; + mAudioRecord = record; + mCblkMemory = cblk; + mCblk = static_cast(cblk->pointer()); + mCblk->buffers = (char*)mCblk + sizeof(audio_track_cblk_t); + mCblk->out = 0; + mSampleRate = sampleRate; + mFormat = format; + // Update buffer size in case it has been limited by AudioFlinger during track creation + mFrameCount = mCblk->frameCount; + mChannelCount = channelCount; + mActive = 0; + mCbf = cbf; + mNotificationFrames = notificationFrames; + mRemainingFrames = notificationFrames; + mUserData = user; + // TODO: add audio hardware input latency here + mLatency = (1000*mFrameCount) / mSampleRate; + mMarkerPosition = 0; + mNewPosition = 0; + mUpdatePeriod = 0; + + return NO_ERROR; +} + +status_t AudioRecord::initCheck() const +{ + return mStatus; +} + +// ------------------------------------------------------------------------- + +uint32_t AudioRecord::latency() const +{ + return mLatency; +} + +uint32_t AudioRecord::sampleRate() const +{ + return mSampleRate; +} + +int AudioRecord::format() const +{ + return mFormat; +} + +int AudioRecord::channelCount() const +{ + return mChannelCount; +} + +uint32_t AudioRecord::frameCount() const +{ + return mFrameCount; +} + +int AudioRecord::frameSize() const +{ + return channelCount()*((format() == AudioSystem::PCM_8_BIT) ? sizeof(uint8_t) : sizeof(int16_t)); +} + +// ------------------------------------------------------------------------- + +status_t AudioRecord::start() +{ + status_t ret = NO_ERROR; + sp t = mClientRecordThread; + + LOGV("start"); + + if (t != 0) { + if (t->exitPending()) { + if (t->requestExitAndWait() == WOULD_BLOCK) { + LOGE("AudioRecord::start called from thread"); + return WOULD_BLOCK; + } + } + t->mLock.lock(); + } + + if (android_atomic_or(1, &mActive) == 0) { + mNewPosition = mCblk->user + mUpdatePeriod; + mCblk->bufferTimeoutMs = MAX_RUN_TIMEOUT_MS; + mCblk->waitTimeMs = 0; + if (t != 0) { + t->run("ClientRecordThread", THREAD_PRIORITY_AUDIO_CLIENT); + } else { + setpriority(PRIO_PROCESS, 0, THREAD_PRIORITY_AUDIO_CLIENT); + } + ret = mAudioRecord->start(); + } + + if (t != 0) { + t->mLock.unlock(); + } + + return ret; +} + +status_t AudioRecord::stop() +{ + sp t = mClientRecordThread; + + LOGV("stop"); + + if (t != 0) { + t->mLock.lock(); + } + + if (android_atomic_and(~1, &mActive) == 1) { + mAudioRecord->stop(); + if (t != 0) { + t->requestExit(); + } else { + setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_NORMAL); + } + } + + if (t != 0) { + t->mLock.unlock(); + } + + return NO_ERROR; +} + +bool AudioRecord::stopped() const +{ + return !mActive; +} + +status_t AudioRecord::setMarkerPosition(uint32_t marker) +{ + if (mCbf == 0) return INVALID_OPERATION; + + mMarkerPosition = marker; + + return NO_ERROR; +} + +status_t AudioRecord::getMarkerPosition(uint32_t *marker) +{ + if (marker == 0) return BAD_VALUE; + + *marker = mMarkerPosition; + + return NO_ERROR; +} + +status_t AudioRecord::setPositionUpdatePeriod(uint32_t updatePeriod) +{ + if (mCbf == 0) return INVALID_OPERATION; + + uint32_t curPosition; + getPosition(&curPosition); + mNewPosition = curPosition + updatePeriod; + mUpdatePeriod = updatePeriod; + + return NO_ERROR; +} + +status_t AudioRecord::getPositionUpdatePeriod(uint32_t *updatePeriod) +{ + if (updatePeriod == 0) return BAD_VALUE; + + *updatePeriod = mUpdatePeriod; + + return NO_ERROR; +} + +status_t AudioRecord::getPosition(uint32_t *position) +{ + if (position == 0) return BAD_VALUE; + + *position = mCblk->user; + + return NO_ERROR; +} + + +// ------------------------------------------------------------------------- + +status_t AudioRecord::obtainBuffer(Buffer* audioBuffer, int32_t waitCount) +{ + int active; + int timeout = 0; + status_t result; + audio_track_cblk_t* cblk = mCblk; + uint32_t framesReq = audioBuffer->frameCount; + + audioBuffer->frameCount = 0; + audioBuffer->size = 0; + + uint32_t framesReady = cblk->framesReady(); + + if (framesReady == 0) { + Mutex::Autolock _l(cblk->lock); + goto start_loop_here; + while (framesReady == 0) { + active = mActive; + if (UNLIKELY(!active)) + return NO_MORE_BUFFERS; + if (UNLIKELY(!waitCount)) + return WOULD_BLOCK; + timeout = 0; + result = cblk->cv.waitRelative(cblk->lock, milliseconds(WAIT_PERIOD_MS)); + if (__builtin_expect(result!=NO_ERROR, false)) { + cblk->waitTimeMs += WAIT_PERIOD_MS; + if (cblk->waitTimeMs >= cblk->bufferTimeoutMs) { + LOGW( "obtainBuffer timed out (is the CPU pegged?) " + "user=%08x, server=%08x", cblk->user, cblk->server); + timeout = 1; + cblk->waitTimeMs = 0; + } + if (--waitCount == 0) { + return TIMED_OUT; + } + } + // read the server count again + start_loop_here: + framesReady = cblk->framesReady(); + } + } + + LOGW_IF(timeout, + "*** SERIOUS WARNING *** obtainBuffer() timed out " + "but didn't need to be locked. We recovered, but " + "this shouldn't happen (user=%08x, server=%08x)", cblk->user, cblk->server); + + cblk->waitTimeMs = 0; + + if (framesReq > framesReady) { + framesReq = framesReady; + } + + uint32_t u = cblk->user; + uint32_t bufferEnd = cblk->userBase + cblk->frameCount; + + if (u + framesReq > bufferEnd) { + framesReq = bufferEnd - u; + } + + audioBuffer->flags = 0; + audioBuffer->channelCount= mChannelCount; + audioBuffer->format = mFormat; + audioBuffer->frameCount = framesReq; + audioBuffer->size = framesReq*mChannelCount*sizeof(int16_t); + audioBuffer->raw = (int8_t*)cblk->buffer(u); + active = mActive; + return active ? status_t(NO_ERROR) : status_t(STOPPED); +} + +void AudioRecord::releaseBuffer(Buffer* audioBuffer) +{ + audio_track_cblk_t* cblk = mCblk; + cblk->stepUser(audioBuffer->frameCount); +} + +// ------------------------------------------------------------------------- + +ssize_t AudioRecord::read(void* buffer, size_t userSize) +{ + ssize_t read = 0; + Buffer audioBuffer; + int8_t *dst = static_cast(buffer); + + if (ssize_t(userSize) < 0) { + // sanity-check. user is most-likely passing an error code. + LOGE("AudioRecord::read(buffer=%p, size=%u (%d)", + buffer, userSize, userSize); + return BAD_VALUE; + } + + LOGV("read size: %d", userSize); + + do { + + audioBuffer.frameCount = userSize/mChannelCount/sizeof(int16_t); + + // Calling obtainBuffer() with a negative wait count causes + // an (almost) infinite wait time. + status_t err = obtainBuffer(&audioBuffer, -1); + if (err < 0) { + // out of buffers, return #bytes written + if (err == status_t(NO_MORE_BUFFERS)) + break; + return ssize_t(err); + } + + size_t bytesRead = audioBuffer.size; + memcpy(dst, audioBuffer.i8, bytesRead); + + dst += bytesRead; + userSize -= bytesRead; + read += bytesRead; + + releaseBuffer(&audioBuffer); + } while (userSize); + + return read; +} + +// ------------------------------------------------------------------------- + +bool AudioRecord::processAudioBuffer(const sp& thread) +{ + Buffer audioBuffer; + uint32_t frames = mRemainingFrames; + size_t readSize; + + // Manage marker callback + if (mMarkerPosition > 0) { + if (mCblk->user >= mMarkerPosition) { + mCbf(EVENT_MARKER, mUserData, (void *)&mMarkerPosition); + mMarkerPosition = 0; + } + } + + // Manage new position callback + if (mUpdatePeriod > 0) { + while (mCblk->user >= mNewPosition) { + mCbf(EVENT_NEW_POS, mUserData, (void *)&mNewPosition); + mNewPosition += mUpdatePeriod; + } + } + + do { + audioBuffer.frameCount = frames; + // Calling obtainBuffer() with a wait count of 1 + // limits wait time to WAIT_PERIOD_MS. This prevents from being + // stuck here not being able to handle timed events (position, markers). + status_t err = obtainBuffer(&audioBuffer, 1); + if (err < NO_ERROR) { + if (err != TIMED_OUT) { + LOGE("Error obtaining an audio buffer, giving up."); + return false; + } + break; + } + if (err == status_t(STOPPED)) return false; + + size_t reqSize = audioBuffer.size; + mCbf(EVENT_MORE_DATA, mUserData, &audioBuffer); + readSize = audioBuffer.size; + + // Sanity check on returned size + if (ssize_t(readSize) <= 0) break; + if (readSize > reqSize) readSize = reqSize; + + audioBuffer.size = readSize; + audioBuffer.frameCount = readSize/mChannelCount/sizeof(int16_t); + frames -= audioBuffer.frameCount; + + releaseBuffer(&audioBuffer); + + } while (frames); + + + // Manage overrun callback + if (mActive && (mCblk->framesAvailable_l() == 0)) { + LOGV("Overrun user: %x, server: %x, flowControlFlag %d", mCblk->user, mCblk->server, mCblk->flowControlFlag); + if (mCblk->flowControlFlag == 0) { + mCbf(EVENT_OVERRUN, mUserData, 0); + mCblk->flowControlFlag = 1; + } + } + + if (frames == 0) { + mRemainingFrames = mNotificationFrames; + } else { + mRemainingFrames = frames; + } + return true; +} + +// ========================================================================= + +AudioRecord::ClientRecordThread::ClientRecordThread(AudioRecord& receiver, bool bCanCallJava) + : Thread(bCanCallJava), mReceiver(receiver) +{ +} + +bool AudioRecord::ClientRecordThread::threadLoop() +{ + return mReceiver.processAudioBuffer(this); +} + +// ------------------------------------------------------------------------- + +}; // namespace android + diff --git a/media/libmedia/AudioSystem.cpp b/media/libmedia/AudioSystem.cpp new file mode 100644 index 0000000..63dfc3b --- /dev/null +++ b/media/libmedia/AudioSystem.cpp @@ -0,0 +1,383 @@ +/* + * Copyright (C) 2006-2007 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 "AudioSystem" +//#define LOG_NDEBUG 0 + +#include +#include +#include +#include + +namespace android { + +// client singleton for AudioFlinger binder interface +Mutex AudioSystem::gLock; +sp AudioSystem::gAudioFlinger; +sp AudioSystem::gAudioFlingerClient; +audio_error_callback AudioSystem::gAudioErrorCallback = NULL; +// Cached values +int AudioSystem::gOutSamplingRate[NUM_AUDIO_OUTPUT_TYPES]; +int AudioSystem::gOutFrameCount[NUM_AUDIO_OUTPUT_TYPES]; +uint32_t AudioSystem::gOutLatency[NUM_AUDIO_OUTPUT_TYPES]; +bool AudioSystem::gA2dpEnabled; +// Cached values for recording queries +uint32_t AudioSystem::gPrevInSamplingRate = 16000; +int AudioSystem::gPrevInFormat = AudioSystem::PCM_16_BIT; +int AudioSystem::gPrevInChannelCount = 1; +size_t AudioSystem::gInBuffSize = 0; + + +// establish binder interface to AudioFlinger service +const sp& AudioSystem::get_audio_flinger() +{ + Mutex::Autolock _l(gLock); + if (gAudioFlinger.get() == 0) { + sp sm = defaultServiceManager(); + sp binder; + do { + binder = sm->getService(String16("media.audio_flinger")); + if (binder != 0) + break; + LOGW("AudioFlinger not published, waiting..."); + usleep(500000); // 0.5 s + } while(true); + if (gAudioFlingerClient == NULL) { + gAudioFlingerClient = new AudioFlingerClient(); + } else { + if (gAudioErrorCallback) { + gAudioErrorCallback(NO_ERROR); + } + } + binder->linkToDeath(gAudioFlingerClient); + gAudioFlinger = interface_cast(binder); + gAudioFlinger->registerClient(gAudioFlingerClient); + // Cache frequently accessed parameters + for (int output = 0; output < NUM_AUDIO_OUTPUT_TYPES; output++) { + gOutFrameCount[output] = (int)gAudioFlinger->frameCount(output); + gOutSamplingRate[output] = (int)gAudioFlinger->sampleRate(output); + gOutLatency[output] = gAudioFlinger->latency(output); + } + gA2dpEnabled = gAudioFlinger->isA2dpEnabled(); + } + LOGE_IF(gAudioFlinger==0, "no AudioFlinger!?"); + return gAudioFlinger; +} + +// routing helper functions +status_t AudioSystem::speakerphone(bool state) { + uint32_t routes = state ? ROUTE_SPEAKER : ROUTE_EARPIECE; + return setRouting(MODE_IN_CALL, routes, ROUTE_ALL); +} + +status_t AudioSystem::isSpeakerphoneOn(bool* state) { + uint32_t routes = 0; + status_t s = getRouting(MODE_IN_CALL, &routes); + *state = !!(routes & ROUTE_SPEAKER); + return s; +} + +status_t AudioSystem::bluetoothSco(bool state) { + uint32_t mask = ROUTE_BLUETOOTH_SCO; + uint32_t routes = state ? mask : ROUTE_EARPIECE; + return setRouting(MODE_IN_CALL, routes, ROUTE_ALL); +} + +status_t AudioSystem::isBluetoothScoOn(bool* state) { + uint32_t routes = 0; + status_t s = getRouting(MODE_IN_CALL, &routes); + *state = !!(routes & ROUTE_BLUETOOTH_SCO); + return s; +} + +status_t AudioSystem::muteMicrophone(bool state) { + const sp& af = AudioSystem::get_audio_flinger(); + if (af == 0) return PERMISSION_DENIED; + return af->setMicMute(state); +} + +status_t AudioSystem::isMicrophoneMuted(bool* state) { + const sp& af = AudioSystem::get_audio_flinger(); + if (af == 0) return PERMISSION_DENIED; + *state = af->getMicMute(); + return NO_ERROR; +} + +status_t AudioSystem::setMasterVolume(float value) +{ + const sp& af = AudioSystem::get_audio_flinger(); + if (af == 0) return PERMISSION_DENIED; + af->setMasterVolume(value); + return NO_ERROR; +} + +status_t AudioSystem::setMasterMute(bool mute) +{ + const sp& af = AudioSystem::get_audio_flinger(); + if (af == 0) return PERMISSION_DENIED; + af->setMasterMute(mute); + return NO_ERROR; +} + +status_t AudioSystem::getMasterVolume(float* volume) +{ + const sp& af = AudioSystem::get_audio_flinger(); + if (af == 0) return PERMISSION_DENIED; + *volume = af->masterVolume(); + return NO_ERROR; +} + +status_t AudioSystem::getMasterMute(bool* mute) +{ + const sp& af = AudioSystem::get_audio_flinger(); + if (af == 0) return PERMISSION_DENIED; + *mute = af->masterMute(); + return NO_ERROR; +} + +status_t AudioSystem::setStreamVolume(int stream, float value) +{ + if (uint32_t(stream) >= NUM_STREAM_TYPES) return BAD_VALUE; + const sp& af = AudioSystem::get_audio_flinger(); + if (af == 0) return PERMISSION_DENIED; + af->setStreamVolume(stream, value); + return NO_ERROR; +} + +status_t AudioSystem::setStreamMute(int stream, bool mute) +{ + if (uint32_t(stream) >= NUM_STREAM_TYPES) return BAD_VALUE; + const sp& af = AudioSystem::get_audio_flinger(); + if (af == 0) return PERMISSION_DENIED; + af->setStreamMute(stream, mute); + return NO_ERROR; +} + +status_t AudioSystem::getStreamVolume(int stream, float* volume) +{ + if (uint32_t(stream) >= NUM_STREAM_TYPES) return BAD_VALUE; + const sp& af = AudioSystem::get_audio_flinger(); + if (af == 0) return PERMISSION_DENIED; + *volume = af->streamVolume(stream); + return NO_ERROR; +} + +status_t AudioSystem::getStreamMute(int stream, bool* mute) +{ + if (uint32_t(stream) >= NUM_STREAM_TYPES) return BAD_VALUE; + const sp& af = AudioSystem::get_audio_flinger(); + if (af == 0) return PERMISSION_DENIED; + *mute = af->streamMute(stream); + return NO_ERROR; +} + +status_t AudioSystem::setMode(int mode) +{ + if (mode >= NUM_MODES) return BAD_VALUE; + const sp& af = AudioSystem::get_audio_flinger(); + if (af == 0) return PERMISSION_DENIED; + return af->setMode(mode); +} + +status_t AudioSystem::getMode(int* mode) +{ + const sp& af = AudioSystem::get_audio_flinger(); + if (af == 0) return PERMISSION_DENIED; + *mode = af->getMode(); + return NO_ERROR; +} + +status_t AudioSystem::setRouting(int mode, uint32_t routes, uint32_t mask) +{ + const sp& af = AudioSystem::get_audio_flinger(); + if (af == 0) return PERMISSION_DENIED; + return af->setRouting(mode, routes, mask); +} + +status_t AudioSystem::getRouting(int mode, uint32_t* routes) +{ + const sp& af = AudioSystem::get_audio_flinger(); + if (af == 0) return PERMISSION_DENIED; + uint32_t r = af->getRouting(mode); + *routes = r; + return NO_ERROR; +} + +status_t AudioSystem::isMusicActive(bool* state) { + const sp& af = AudioSystem::get_audio_flinger(); + if (af == 0) return PERMISSION_DENIED; + *state = af->isMusicActive(); + return NO_ERROR; +} + +// Temporary interface, do not use +// TODO: Replace with a more generic key:value get/set mechanism +status_t AudioSystem::setParameter(const char* key, const char* value) { + const sp& af = AudioSystem::get_audio_flinger(); + if (af == 0) return PERMISSION_DENIED; + return af->setParameter(key, value); +} + +// convert volume steps to natural log scale + +// change this value to change volume scaling +static const float dBPerStep = 0.5f; +// shouldn't need to touch these +static const float dBConvert = -dBPerStep * 2.302585093f / 20.0f; +static const float dBConvertInverse = 1.0f / dBConvert; + +float AudioSystem::linearToLog(int volume) +{ + // float v = volume ? exp(float(100 - volume) * dBConvert) : 0; + // LOGD("linearToLog(%d)=%f", volume, v); + // return v; + return volume ? exp(float(100 - volume) * dBConvert) : 0; +} + +int AudioSystem::logToLinear(float volume) +{ + // int v = volume ? 100 - int(dBConvertInverse * log(volume) + 0.5) : 0; + // LOGD("logTolinear(%d)=%f", v, volume); + // return v; + return volume ? 100 - int(dBConvertInverse * log(volume) + 0.5) : 0; +} + +status_t AudioSystem::getOutputSamplingRate(int* samplingRate, int streamType) +{ + int output = getOutput(streamType); + + if (gOutSamplingRate[output] == 0) { + const sp& af = AudioSystem::get_audio_flinger(); + if (af == 0) return PERMISSION_DENIED; + // gOutSamplingRate is updated by get_audio_flinger() + } + LOGV("getOutputSamplingRate() streamType %d, output %d, sampling rate %d", streamType, output, gOutSamplingRate[output]); + *samplingRate = gOutSamplingRate[output]; + + return NO_ERROR; +} + +status_t AudioSystem::getOutputFrameCount(int* frameCount, int streamType) +{ + int output = getOutput(streamType); + + if (gOutFrameCount[output] == 0) { + const sp& af = AudioSystem::get_audio_flinger(); + if (af == 0) return PERMISSION_DENIED; + // gOutFrameCount is updated by get_audio_flinger() + } + LOGV("getOutputFrameCount() streamType %d, output %d, frame count %d", streamType, output, gOutFrameCount[output]); + + *frameCount = gOutFrameCount[output]; + return NO_ERROR; +} + +status_t AudioSystem::getOutputLatency(uint32_t* latency, int streamType) +{ + int output = getOutput(streamType); + + if (gOutLatency[output] == 0) { + const sp& af = AudioSystem::get_audio_flinger(); + if (af == 0) return PERMISSION_DENIED; + // gOutLatency is updated by get_audio_flinger() + } + LOGV("getOutputLatency() streamType %d, output %d, latency %d", streamType, output, gOutLatency[output]); + + *latency = gOutLatency[output]; + + return NO_ERROR; +} + +status_t AudioSystem::getInputBufferSize(uint32_t sampleRate, int format, int channelCount, + size_t* buffSize) +{ + // Do we have a stale gInBufferSize or are we requesting the input buffer size for new values + if ((gInBuffSize == 0) || (sampleRate != gPrevInSamplingRate) || (format != gPrevInFormat) + || (channelCount != gPrevInChannelCount)) { + // save the request params + gPrevInSamplingRate = sampleRate; + gPrevInFormat = format; + gPrevInChannelCount = channelCount; + + gInBuffSize = 0; + const sp& af = AudioSystem::get_audio_flinger(); + if (af == 0) { + return PERMISSION_DENIED; + } + gInBuffSize = af->getInputBufferSize(sampleRate, format, channelCount); + } + *buffSize = gInBuffSize; + + return NO_ERROR; +} + +// --------------------------------------------------------------------------- + +void AudioSystem::AudioFlingerClient::binderDied(const wp& who) { + Mutex::Autolock _l(AudioSystem::gLock); + AudioSystem::gAudioFlinger.clear(); + + for (int output = 0; output < NUM_AUDIO_OUTPUT_TYPES; output++) { + gOutFrameCount[output] = 0; + gOutSamplingRate[output] = 0; + gOutLatency[output] = 0; + } + AudioSystem::gInBuffSize = 0; + + if (gAudioErrorCallback) { + gAudioErrorCallback(DEAD_OBJECT); + } + LOGW("AudioFlinger server died!"); +} + +void AudioSystem::AudioFlingerClient::a2dpEnabledChanged(bool enabled) { + gA2dpEnabled = enabled; + LOGV("AudioFlinger A2DP enabled status changed! %d", enabled); +} + +void AudioSystem::setErrorCallback(audio_error_callback cb) { + Mutex::Autolock _l(AudioSystem::gLock); + gAudioErrorCallback = cb; +} + +int AudioSystem::getOutput(int streamType) +{ + if (streamType == DEFAULT) { + streamType = MUSIC; + } + if (gA2dpEnabled && routedToA2dpOutput(streamType)) { + return AUDIO_OUTPUT_A2DP; + } else { + return AUDIO_OUTPUT_HARDWARE; + } +} + +bool AudioSystem::routedToA2dpOutput(int streamType) { + switch(streamType) { + case MUSIC: + case VOICE_CALL: + case BLUETOOTH_SCO: + case SYSTEM: + return true; + default: + return false; + } +} + + + +}; // namespace android + diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp new file mode 100644 index 0000000..e79f336 --- /dev/null +++ b/media/libmedia/AudioTrack.cpp @@ -0,0 +1,1021 @@ +/* //device/extlibs/pv/android/AudioTrack.cpp +** +** Copyright 2007, 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 "AudioTrack" + +#include +#include +#include + +#include +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#define LIKELY( exp ) (__builtin_expect( (exp) != 0, true )) +#define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false )) + +namespace android { + +// --------------------------------------------------------------------------- + +AudioTrack::AudioTrack() + : mStatus(NO_INIT) +{ +} + +AudioTrack::AudioTrack( + int streamType, + uint32_t sampleRate, + int format, + int channelCount, + int frameCount, + uint32_t flags, + callback_t cbf, + void* user, + int notificationFrames) + : mStatus(NO_INIT) +{ + mStatus = set(streamType, sampleRate, format, channelCount, + frameCount, flags, cbf, user, notificationFrames, 0); +} + +AudioTrack::AudioTrack( + int streamType, + uint32_t sampleRate, + int format, + int channelCount, + const sp& sharedBuffer, + uint32_t flags, + callback_t cbf, + void* user, + int notificationFrames) + : mStatus(NO_INIT) +{ + mStatus = set(streamType, sampleRate, format, channelCount, + 0, flags, cbf, user, notificationFrames, sharedBuffer); +} + +AudioTrack::~AudioTrack() +{ + LOGV_IF(mSharedBuffer != 0, "Destructor sharedBuffer: %p", mSharedBuffer->pointer()); + + if (mStatus == NO_ERROR) { + // Make sure that callback function exits in the case where + // it is looping on buffer full condition in obtainBuffer(). + // Otherwise the callback thread will never exit. + stop(); + if (mAudioTrackThread != 0) { + mCblk->cv.signal(); + mAudioTrackThread->requestExitAndWait(); + mAudioTrackThread.clear(); + } + mAudioTrack.clear(); + IPCThreadState::self()->flushCommands(); + } +} + +status_t AudioTrack::set( + int streamType, + uint32_t sampleRate, + int format, + int channelCount, + int frameCount, + uint32_t flags, + callback_t cbf, + void* user, + int notificationFrames, + const sp& sharedBuffer, + bool threadCanCallJava) +{ + + LOGV_IF(sharedBuffer != 0, "sharedBuffer: %p, size: %d", sharedBuffer->pointer(), sharedBuffer->size()); + + if (mAudioFlinger != 0) { + LOGE("Track already in use"); + return INVALID_OPERATION; + } + + const sp& audioFlinger = AudioSystem::get_audio_flinger(); + if (audioFlinger == 0) { + LOGE("Could not get audioflinger"); + return NO_INIT; + } + int afSampleRate; + if (AudioSystem::getOutputSamplingRate(&afSampleRate, streamType) != NO_ERROR) { + return NO_INIT; + } + int afFrameCount; + if (AudioSystem::getOutputFrameCount(&afFrameCount, streamType) != NO_ERROR) { + return NO_INIT; + } + uint32_t afLatency; + if (AudioSystem::getOutputLatency(&afLatency, streamType) != NO_ERROR) { + return NO_INIT; + } + + // handle default values first. + if (streamType == AudioSystem::DEFAULT) { + streamType = AudioSystem::MUSIC; + } + if (sampleRate == 0) { + sampleRate = afSampleRate; + } + // these below should probably come from the audioFlinger too... + if (format == 0) { + format = AudioSystem::PCM_16_BIT; + } + if (channelCount == 0) { + channelCount = 2; + } + + // validate parameters + if (((format != AudioSystem::PCM_8_BIT) || sharedBuffer != 0) && + (format != AudioSystem::PCM_16_BIT)) { + LOGE("Invalid format"); + return BAD_VALUE; + } + if (channelCount != 1 && channelCount != 2) { + LOGE("Invalid channel number"); + return BAD_VALUE; + } + + // Ensure that buffer depth covers at least audio hardware latency + uint32_t minBufCount = afLatency / ((1000 * afFrameCount)/afSampleRate); + if (minBufCount < 2) minBufCount = 2; + + // When playing from shared buffer, playback will start even if last audioflinger + // block is partly filled. + if (sharedBuffer != 0 && minBufCount > 1) { + minBufCount--; + } + + int minFrameCount = (afFrameCount*sampleRate*minBufCount)/afSampleRate; + + if (sharedBuffer == 0) { + if (frameCount == 0) { + frameCount = minFrameCount; + } + if (notificationFrames == 0) { + notificationFrames = frameCount/2; + } + // Make sure that application is notified with sufficient margin + // before underrun + if (notificationFrames > frameCount/2) { + notificationFrames = frameCount/2; + } + } else { + // Ensure that buffer alignment matches channelcount + if (((uint32_t)sharedBuffer->pointer() & (channelCount | 1)) != 0) { + LOGE("Invalid buffer alignement: address %p, channelCount %d", sharedBuffer->pointer(), channelCount); + return BAD_VALUE; + } + frameCount = sharedBuffer->size()/channelCount/sizeof(int16_t); + } + + if (frameCount < minFrameCount) { + LOGE("Invalid buffer size: minFrameCount %d, frameCount %d", minFrameCount, frameCount); + return BAD_VALUE; + } + + // create the track + status_t status; + sp track = audioFlinger->createTrack(getpid(), + streamType, sampleRate, format, channelCount, frameCount, flags, sharedBuffer, &status); + + if (track == 0) { + LOGE("AudioFlinger could not create track, status: %d", status); + return status; + } + sp cblk = track->getCblk(); + if (cblk == 0) { + LOGE("Could not get control block"); + return NO_INIT; + } + if (cbf != 0) { + mAudioTrackThread = new AudioTrackThread(*this, threadCanCallJava); + if (mAudioTrackThread == 0) { + LOGE("Could not create callback thread"); + return NO_INIT; + } + } + + mStatus = NO_ERROR; + + mAudioFlinger = audioFlinger; + mAudioTrack = track; + mCblkMemory = cblk; + mCblk = static_cast(cblk->pointer()); + mCblk->out = 1; + // Update buffer size in case it has been limited by AudioFlinger during track creation + mFrameCount = mCblk->frameCount; + if (sharedBuffer == 0) { + mCblk->buffers = (char*)mCblk + sizeof(audio_track_cblk_t); + } else { + mCblk->buffers = sharedBuffer->pointer(); + // Force buffer full condition as data is already present in shared memory + mCblk->stepUser(mFrameCount); + } + mCblk->volume[0] = mCblk->volume[1] = 0x1000; + mVolume[LEFT] = 1.0f; + mVolume[RIGHT] = 1.0f; + mSampleRate = sampleRate; + mStreamType = streamType; + mFormat = format; + mChannelCount = channelCount; + mSharedBuffer = sharedBuffer; + mMuted = false; + mActive = 0; + mCbf = cbf; + mNotificationFrames = notificationFrames; + mRemainingFrames = notificationFrames; + mUserData = user; + mLatency = afLatency + (1000*mFrameCount) / mSampleRate; + mLoopCount = 0; + mMarkerPosition = 0; + mNewPosition = 0; + mUpdatePeriod = 0; + + return NO_ERROR; +} + +status_t AudioTrack::initCheck() const +{ + return mStatus; +} + +// ------------------------------------------------------------------------- + +uint32_t AudioTrack::latency() const +{ + return mLatency; +} + +int AudioTrack::streamType() const +{ + return mStreamType; +} + +uint32_t AudioTrack::sampleRate() const +{ + return mSampleRate; +} + +int AudioTrack::format() const +{ + return mFormat; +} + +int AudioTrack::channelCount() const +{ + return mChannelCount; +} + +uint32_t AudioTrack::frameCount() const +{ + return mFrameCount; +} + +int AudioTrack::frameSize() const +{ + return channelCount()*((format() == AudioSystem::PCM_8_BIT) ? sizeof(uint8_t) : sizeof(int16_t)); +} + +sp& AudioTrack::sharedBuffer() +{ + return mSharedBuffer; +} + +// ------------------------------------------------------------------------- + +void AudioTrack::start() +{ + sp t = mAudioTrackThread; + + LOGV("start %p", this); + if (t != 0) { + if (t->exitPending()) { + if (t->requestExitAndWait() == WOULD_BLOCK) { + LOGE("AudioTrack::start called from thread"); + return; + } + } + t->mLock.lock(); + } + + if (android_atomic_or(1, &mActive) == 0) { + mNewPosition = mCblk->server + mUpdatePeriod; + mCblk->bufferTimeoutMs = MAX_STARTUP_TIMEOUT_MS; + mCblk->waitTimeMs = 0; + if (t != 0) { + t->run("AudioTrackThread", THREAD_PRIORITY_AUDIO_CLIENT); + } else { + setpriority(PRIO_PROCESS, 0, THREAD_PRIORITY_AUDIO_CLIENT); + } + mAudioTrack->start(); + } + + if (t != 0) { + t->mLock.unlock(); + } +} + +void AudioTrack::stop() +{ + sp t = mAudioTrackThread; + + LOGV("stop %p", this); + if (t != 0) { + t->mLock.lock(); + } + + if (android_atomic_and(~1, &mActive) == 1) { + mAudioTrack->stop(); + // Cancel loops (If we are in the middle of a loop, playback + // would not stop until loopCount reaches 0). + setLoop(0, 0, 0); + // Force flush if a shared buffer is used otherwise audioflinger + // will not stop before end of buffer is reached. + if (mSharedBuffer != 0) { + flush(); + } + if (t != 0) { + t->requestExit(); + } else { + setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_NORMAL); + } + } + + if (t != 0) { + t->mLock.unlock(); + } +} + +bool AudioTrack::stopped() const +{ + return !mActive; +} + +void AudioTrack::flush() +{ + LOGV("flush"); + + if (!mActive) { + mCblk->lock.lock(); + mAudioTrack->flush(); + // Release AudioTrack callback thread in case it was waiting for new buffers + // in AudioTrack::obtainBuffer() + mCblk->cv.signal(); + mCblk->lock.unlock(); + } +} + +void AudioTrack::pause() +{ + LOGV("pause"); + if (android_atomic_and(~1, &mActive) == 1) { + mActive = 0; + mAudioTrack->pause(); + } +} + +void AudioTrack::mute(bool e) +{ + mAudioTrack->mute(e); + mMuted = e; +} + +bool AudioTrack::muted() const +{ + return mMuted; +} + +void AudioTrack::setVolume(float left, float right) +{ + mVolume[LEFT] = left; + mVolume[RIGHT] = right; + + // write must be atomic + mCblk->volumeLR = (int32_t(int16_t(left * 0x1000)) << 16) | int16_t(right * 0x1000); +} + +void AudioTrack::getVolume(float* left, float* right) +{ + *left = mVolume[LEFT]; + *right = mVolume[RIGHT]; +} + +void AudioTrack::setSampleRate(int rate) +{ + int afSamplingRate; + + if (AudioSystem::getOutputSamplingRate(&afSamplingRate, mStreamType) != NO_ERROR) { + return; + } + // Resampler implementation limits input sampling rate to 2 x output sampling rate. + if (rate <= 0) rate = 1; + if (rate > afSamplingRate*2) rate = afSamplingRate*2; + if (rate > MAX_SAMPLE_RATE) rate = MAX_SAMPLE_RATE; + + mCblk->sampleRate = rate; +} + +uint32_t AudioTrack::getSampleRate() +{ + return uint32_t(mCblk->sampleRate); +} + +status_t AudioTrack::setLoop(uint32_t loopStart, uint32_t loopEnd, int loopCount) +{ + audio_track_cblk_t* cblk = mCblk; + + + Mutex::Autolock _l(cblk->lock); + + if (loopCount == 0) { + cblk->loopStart = UINT_MAX; + cblk->loopEnd = UINT_MAX; + cblk->loopCount = 0; + mLoopCount = 0; + return NO_ERROR; + } + + if (loopStart >= loopEnd || + loopEnd - loopStart > mFrameCount) { + LOGE("setLoop invalid value: loopStart %d, loopEnd %d, loopCount %d, framecount %d, user %d", loopStart, loopEnd, loopCount, mFrameCount, cblk->user); + return BAD_VALUE; + } + + if ((mSharedBuffer != 0) && (loopEnd > mFrameCount)) { + LOGE("setLoop invalid value: loop markers beyond data: loopStart %d, loopEnd %d, framecount %d", + loopStart, loopEnd, mFrameCount); + return BAD_VALUE; + } + + cblk->loopStart = loopStart; + cblk->loopEnd = loopEnd; + cblk->loopCount = loopCount; + mLoopCount = loopCount; + + return NO_ERROR; +} + +status_t AudioTrack::getLoop(uint32_t *loopStart, uint32_t *loopEnd, int *loopCount) +{ + if (loopStart != 0) { + *loopStart = mCblk->loopStart; + } + if (loopEnd != 0) { + *loopEnd = mCblk->loopEnd; + } + if (loopCount != 0) { + if (mCblk->loopCount < 0) { + *loopCount = -1; + } else { + *loopCount = mCblk->loopCount; + } + } + + return NO_ERROR; +} + +status_t AudioTrack::setMarkerPosition(uint32_t marker) +{ + if (mCbf == 0) return INVALID_OPERATION; + + mMarkerPosition = marker; + + return NO_ERROR; +} + +status_t AudioTrack::getMarkerPosition(uint32_t *marker) +{ + if (marker == 0) return BAD_VALUE; + + *marker = mMarkerPosition; + + return NO_ERROR; +} + +status_t AudioTrack::setPositionUpdatePeriod(uint32_t updatePeriod) +{ + if (mCbf == 0) return INVALID_OPERATION; + + uint32_t curPosition; + getPosition(&curPosition); + mNewPosition = curPosition + updatePeriod; + mUpdatePeriod = updatePeriod; + + return NO_ERROR; +} + +status_t AudioTrack::getPositionUpdatePeriod(uint32_t *updatePeriod) +{ + if (updatePeriod == 0) return BAD_VALUE; + + *updatePeriod = mUpdatePeriod; + + return NO_ERROR; +} + +status_t AudioTrack::setPosition(uint32_t position) +{ + Mutex::Autolock _l(mCblk->lock); + + if (!stopped()) return INVALID_OPERATION; + + if (position > mCblk->user) return BAD_VALUE; + + mCblk->server = position; + mCblk->forceReady = 1; + + return NO_ERROR; +} + +status_t AudioTrack::getPosition(uint32_t *position) +{ + if (position == 0) return BAD_VALUE; + + *position = mCblk->server; + + return NO_ERROR; +} + +status_t AudioTrack::reload() +{ + if (!stopped()) return INVALID_OPERATION; + + flush(); + + mCblk->stepUser(mFrameCount); + + return NO_ERROR; +} + +// ------------------------------------------------------------------------- + +status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, int32_t waitCount) +{ + int active; + int timeout = 0; + status_t result; + audio_track_cblk_t* cblk = mCblk; + uint32_t framesReq = audioBuffer->frameCount; + + audioBuffer->frameCount = 0; + audioBuffer->size = 0; + + uint32_t framesAvail = cblk->framesAvailable(); + + if (framesAvail == 0) { + Mutex::Autolock _l(cblk->lock); + goto start_loop_here; + while (framesAvail == 0) { + active = mActive; + if (UNLIKELY(!active)) { + LOGV("Not active and NO_MORE_BUFFERS"); + return NO_MORE_BUFFERS; + } + if (UNLIKELY(!waitCount)) + return WOULD_BLOCK; + timeout = 0; + result = cblk->cv.waitRelative(cblk->lock, milliseconds(WAIT_PERIOD_MS)); + if (__builtin_expect(result!=NO_ERROR, false)) { + cblk->waitTimeMs += WAIT_PERIOD_MS; + if (cblk->waitTimeMs >= cblk->bufferTimeoutMs) { + // timing out when a loop has been set and we have already written upto loop end + // is a normal condition: no need to wake AudioFlinger up. + if (cblk->user < cblk->loopEnd) { + LOGW( "obtainBuffer timed out (is the CPU pegged?) %p " + "user=%08x, server=%08x", this, cblk->user, cblk->server); + //unlock cblk mutex before calling mAudioTrack->start() (see issue #1617140) + cblk->lock.unlock(); + mAudioTrack->start(); + cblk->lock.lock(); + timeout = 1; + } + cblk->waitTimeMs = 0; + } + + if (--waitCount == 0) { + return TIMED_OUT; + } + } + // read the server count again + start_loop_here: + framesAvail = cblk->framesAvailable_l(); + } + } + + cblk->waitTimeMs = 0; + + if (framesReq > framesAvail) { + framesReq = framesAvail; + } + + uint32_t u = cblk->user; + uint32_t bufferEnd = cblk->userBase + cblk->frameCount; + + if (u + framesReq > bufferEnd) { + framesReq = bufferEnd - u; + } + + LOGW_IF(timeout, + "*** SERIOUS WARNING *** obtainBuffer() timed out " + "but didn't need to be locked. We recovered, but " + "this shouldn't happen (user=%08x, server=%08x)", cblk->user, cblk->server); + + audioBuffer->flags = mMuted ? Buffer::MUTE : 0; + audioBuffer->channelCount= mChannelCount; + audioBuffer->format = AudioSystem::PCM_16_BIT; + audioBuffer->frameCount = framesReq; + audioBuffer->size = framesReq*mChannelCount*sizeof(int16_t); + audioBuffer->raw = (int8_t *)cblk->buffer(u); + active = mActive; + return active ? status_t(NO_ERROR) : status_t(STOPPED); +} + +void AudioTrack::releaseBuffer(Buffer* audioBuffer) +{ + audio_track_cblk_t* cblk = mCblk; + cblk->stepUser(audioBuffer->frameCount); +} + +// ------------------------------------------------------------------------- + +ssize_t AudioTrack::write(const void* buffer, size_t userSize) +{ + + if (mSharedBuffer != 0) return INVALID_OPERATION; + + if (ssize_t(userSize) < 0) { + // sanity-check. user is most-likely passing an error code. + LOGE("AudioTrack::write(buffer=%p, size=%u (%d)", + buffer, userSize, userSize); + return BAD_VALUE; + } + + LOGV("write %p: %d bytes, mActive=%d", this, userSize, mActive); + + ssize_t written = 0; + const int8_t *src = (const int8_t *)buffer; + Buffer audioBuffer; + + do { + audioBuffer.frameCount = userSize/mChannelCount; + if (mFormat == AudioSystem::PCM_16_BIT) { + audioBuffer.frameCount >>= 1; + } + // Calling obtainBuffer() with a negative wait count causes + // an (almost) infinite wait time. + status_t err = obtainBuffer(&audioBuffer, -1); + if (err < 0) { + // out of buffers, return #bytes written + if (err == status_t(NO_MORE_BUFFERS)) + break; + return ssize_t(err); + } + + size_t toWrite; + if (mFormat == AudioSystem::PCM_8_BIT) { + // Divide capacity by 2 to take expansion into account + toWrite = audioBuffer.size>>1; + // 8 to 16 bit conversion + int count = toWrite; + int16_t *dst = (int16_t *)(audioBuffer.i8); + while(count--) { + *dst++ = (int16_t)(*src++^0x80) << 8; + } + }else { + toWrite = audioBuffer.size; + memcpy(audioBuffer.i8, src, toWrite); + src += toWrite; + } + userSize -= toWrite; + written += toWrite; + + releaseBuffer(&audioBuffer); + } while (userSize); + + return written; +} + +// ------------------------------------------------------------------------- + +bool AudioTrack::processAudioBuffer(const sp& thread) +{ + Buffer audioBuffer; + uint32_t frames; + size_t writtenSize; + + // Manage underrun callback + if (mActive && (mCblk->framesReady() == 0)) { + LOGV("Underrun user: %x, server: %x, flowControlFlag %d", mCblk->user, mCblk->server, mCblk->flowControlFlag); + if (mCblk->flowControlFlag == 0) { + mCbf(EVENT_UNDERRUN, mUserData, 0); + if (mCblk->server == mCblk->frameCount) { + mCbf(EVENT_BUFFER_END, mUserData, 0); + } + mCblk->flowControlFlag = 1; + if (mSharedBuffer != 0) return false; + } + } + + // Manage loop end callback + while (mLoopCount > mCblk->loopCount) { + int loopCount = -1; + mLoopCount--; + if (mLoopCount >= 0) loopCount = mLoopCount; + + mCbf(EVENT_LOOP_END, mUserData, (void *)&loopCount); + } + + // Manage marker callback + if(mMarkerPosition > 0) { + if (mCblk->server >= mMarkerPosition) { + mCbf(EVENT_MARKER, mUserData, (void *)&mMarkerPosition); + mMarkerPosition = 0; + } + } + + // Manage new position callback + if(mUpdatePeriod > 0) { + while (mCblk->server >= mNewPosition) { + mCbf(EVENT_NEW_POS, mUserData, (void *)&mNewPosition); + mNewPosition += mUpdatePeriod; + } + } + + // If Shared buffer is used, no data is requested from client. + if (mSharedBuffer != 0) { + frames = 0; + } else { + frames = mRemainingFrames; + } + + do { + + audioBuffer.frameCount = frames; + + // Calling obtainBuffer() with a wait count of 1 + // limits wait time to WAIT_PERIOD_MS. This prevents from being + // stuck here not being able to handle timed events (position, markers, loops). + status_t err = obtainBuffer(&audioBuffer, 1); + if (err < NO_ERROR) { + if (err != TIMED_OUT) { + LOGE("Error obtaining an audio buffer, giving up."); + return false; + } + break; + } + if (err == status_t(STOPPED)) return false; + + // Divide buffer size by 2 to take into account the expansion + // due to 8 to 16 bit conversion: the callback must fill only half + // of the destination buffer + if (mFormat == AudioSystem::PCM_8_BIT) { + audioBuffer.size >>= 1; + } + + size_t reqSize = audioBuffer.size; + mCbf(EVENT_MORE_DATA, mUserData, &audioBuffer); + writtenSize = audioBuffer.size; + + // Sanity check on returned size + if (ssize_t(writtenSize) <= 0) break; + if (writtenSize > reqSize) writtenSize = reqSize; + + if (mFormat == AudioSystem::PCM_8_BIT) { + // 8 to 16 bit conversion + const int8_t *src = audioBuffer.i8 + writtenSize-1; + int count = writtenSize; + int16_t *dst = audioBuffer.i16 + writtenSize-1; + while(count--) { + *dst-- = (int16_t)(*src--^0x80) << 8; + } + writtenSize <<= 1; + } + + audioBuffer.size = writtenSize; + audioBuffer.frameCount = writtenSize/mChannelCount/sizeof(int16_t); + frames -= audioBuffer.frameCount; + + releaseBuffer(&audioBuffer); + } + while (frames); + + if (frames == 0) { + mRemainingFrames = mNotificationFrames; + } else { + mRemainingFrames = frames; + } + return true; +} + +status_t AudioTrack::dump(int fd, const Vector& args) const +{ + + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + + result.append(" AudioTrack::dump\n"); + snprintf(buffer, 255, " stream type(%d), left - right volume(%f, %f)\n", mStreamType, mVolume[0], mVolume[1]); + result.append(buffer); + snprintf(buffer, 255, " format(%d), channel count(%d), frame count(%d)\n", mFormat, mChannelCount, mFrameCount); + result.append(buffer); + snprintf(buffer, 255, " sample rate(%d), status(%d), muted(%d)\n", mSampleRate, mStatus, mMuted); + result.append(buffer); + snprintf(buffer, 255, " active(%d), latency (%d)\n", mActive, mLatency); + result.append(buffer); + ::write(fd, result.string(), result.size()); + return NO_ERROR; +} + +// ========================================================================= + +AudioTrack::AudioTrackThread::AudioTrackThread(AudioTrack& receiver, bool bCanCallJava) + : Thread(bCanCallJava), mReceiver(receiver) +{ +} + +bool AudioTrack::AudioTrackThread::threadLoop() +{ + return mReceiver.processAudioBuffer(this); +} + +status_t AudioTrack::AudioTrackThread::readyToRun() +{ + return NO_ERROR; +} + +void AudioTrack::AudioTrackThread::onFirstRef() +{ +} + +// ========================================================================= + +audio_track_cblk_t::audio_track_cblk_t() + : user(0), server(0), userBase(0), serverBase(0), buffers(0), frameCount(0), + loopStart(UINT_MAX), loopEnd(UINT_MAX), loopCount(0), volumeLR(0), flowControlFlag(1), forceReady(0) +{ +} + +uint32_t audio_track_cblk_t::stepUser(uint32_t frameCount) +{ + uint32_t u = this->user; + + u += frameCount; + // Ensure that user is never ahead of server for AudioRecord + if (out) { + // If stepServer() has been called once, switch to normal obtainBuffer() timeout period + if (bufferTimeoutMs == MAX_STARTUP_TIMEOUT_MS-1) { + bufferTimeoutMs = MAX_RUN_TIMEOUT_MS; + } + } else if (u > this->server) { + LOGW("stepServer occured after track reset"); + u = this->server; + } + + if (u >= userBase + this->frameCount) { + userBase += this->frameCount; + } + + this->user = u; + + // Clear flow control error condition as new data has been written/read to/from buffer. + flowControlFlag = 0; + + return u; +} + +bool audio_track_cblk_t::stepServer(uint32_t frameCount) +{ + // the code below simulates lock-with-timeout + // we MUST do this to protect the AudioFlinger server + // as this lock is shared with the client. + status_t err; + + err = lock.tryLock(); + if (err == -EBUSY) { // just wait a bit + usleep(1000); + err = lock.tryLock(); + } + if (err != NO_ERROR) { + // probably, the client just died. + return false; + } + + uint32_t s = this->server; + + s += frameCount; + if (out) { + // Mark that we have read the first buffer so that next time stepUser() is called + // we switch to normal obtainBuffer() timeout period + if (bufferTimeoutMs == MAX_STARTUP_TIMEOUT_MS) { + bufferTimeoutMs = MAX_RUN_TIMEOUT_MS - 1; + } + // It is possible that we receive a flush() + // while the mixer is processing a block: in this case, + // stepServer() is called After the flush() has reset u & s and + // we have s > u + if (s > this->user) { + LOGW("stepServer occured after track reset"); + s = this->user; + } + } + + if (s >= loopEnd) { + LOGW_IF(s > loopEnd, "stepServer: s %u > loopEnd %u", s, loopEnd); + s = loopStart; + if (--loopCount == 0) { + loopEnd = UINT_MAX; + loopStart = UINT_MAX; + } + } + if (s >= serverBase + this->frameCount) { + serverBase += this->frameCount; + } + + this->server = s; + + cv.signal(); + lock.unlock(); + return true; +} + +void* audio_track_cblk_t::buffer(uint32_t offset) const +{ + return (int16_t *)this->buffers + (offset-userBase)*this->channels; +} + +uint32_t audio_track_cblk_t::framesAvailable() +{ + Mutex::Autolock _l(lock); + return framesAvailable_l(); +} + +uint32_t audio_track_cblk_t::framesAvailable_l() +{ + uint32_t u = this->user; + uint32_t s = this->server; + + if (out) { + uint32_t limit = (s < loopStart) ? s : loopStart; + return limit + frameCount - u; + } else { + return frameCount + u - s; + } +} + +uint32_t audio_track_cblk_t::framesReady() +{ + uint32_t u = this->user; + uint32_t s = this->server; + + if (out) { + if (u < loopEnd) { + return u - s; + } else { + Mutex::Autolock _l(lock); + if (loopCount >= 0) { + return (loopEnd - loopStart)*loopCount + u - s; + } else { + return UINT_MAX; + } + } + } else { + return s - u; + } +} + +// ------------------------------------------------------------------------- + +}; // namespace android + diff --git a/media/libmedia/IAudioFlinger.cpp b/media/libmedia/IAudioFlinger.cpp new file mode 100644 index 0000000..5cbb25c --- /dev/null +++ b/media/libmedia/IAudioFlinger.cpp @@ -0,0 +1,553 @@ +/* //device/extlibs/pv/android/IAudioflinger.cpp +** +** Copyright 2007, 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 "IAudioFlinger" +#include + +#include +#include + +#include + +#include + +namespace android { + +enum { + CREATE_TRACK = IBinder::FIRST_CALL_TRANSACTION, + OPEN_RECORD, + SAMPLE_RATE, + CHANNEL_COUNT, + FORMAT, + FRAME_COUNT, + LATENCY, + SET_MASTER_VOLUME, + SET_MASTER_MUTE, + MASTER_VOLUME, + MASTER_MUTE, + SET_STREAM_VOLUME, + SET_STREAM_MUTE, + STREAM_VOLUME, + STREAM_MUTE, + SET_MODE, + GET_MODE, + SET_ROUTING, + GET_ROUTING, + SET_MIC_MUTE, + GET_MIC_MUTE, + IS_MUSIC_ACTIVE, + SET_PARAMETER, + REGISTER_CLIENT, + GET_INPUTBUFFERSIZE, + WAKE_UP, + IS_A2DP_ENABLED +}; + +class BpAudioFlinger : public BpInterface +{ +public: + BpAudioFlinger(const sp& impl) + : BpInterface(impl) + { + } + + virtual sp createTrack( + pid_t pid, + int streamType, + uint32_t sampleRate, + int format, + int channelCount, + int frameCount, + uint32_t flags, + const sp& sharedBuffer, + status_t *status) + { + Parcel data, reply; + data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); + data.writeInt32(pid); + data.writeInt32(streamType); + data.writeInt32(sampleRate); + data.writeInt32(format); + data.writeInt32(channelCount); + data.writeInt32(frameCount); + data.writeInt32(flags); + data.writeStrongBinder(sharedBuffer->asBinder()); + status_t lStatus = remote()->transact(CREATE_TRACK, data, &reply); + if (lStatus != NO_ERROR) { + LOGE("createTrack error: %s", strerror(-lStatus)); + } + lStatus = reply.readInt32(); + if (status) { + *status = lStatus; + } + return interface_cast(reply.readStrongBinder()); + } + + virtual sp openRecord( + pid_t pid, + int streamType, + uint32_t sampleRate, + int format, + int channelCount, + int frameCount, + uint32_t flags, + status_t *status) + { + Parcel data, reply; + data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); + data.writeInt32(pid); + data.writeInt32(streamType); + data.writeInt32(sampleRate); + data.writeInt32(format); + data.writeInt32(channelCount); + data.writeInt32(frameCount); + data.writeInt32(flags); + remote()->transact(OPEN_RECORD, data, &reply); + status_t lStatus = reply.readInt32(); + if (status) { + *status = lStatus; + } + return interface_cast(reply.readStrongBinder()); + } + + virtual uint32_t sampleRate(int output) const + { + Parcel data, reply; + data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); + data.writeInt32(output); + remote()->transact(SAMPLE_RATE, data, &reply); + return reply.readInt32(); + } + + virtual int channelCount(int output) const + { + Parcel data, reply; + data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); + data.writeInt32(output); + remote()->transact(CHANNEL_COUNT, data, &reply); + return reply.readInt32(); + } + + virtual int format(int output) const + { + Parcel data, reply; + data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); + data.writeInt32(output); + remote()->transact(FORMAT, data, &reply); + return reply.readInt32(); + } + + virtual size_t frameCount(int output) const + { + Parcel data, reply; + data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); + data.writeInt32(output); + remote()->transact(FRAME_COUNT, data, &reply); + return reply.readInt32(); + } + + virtual uint32_t latency(int output) const + { + Parcel data, reply; + data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); + data.writeInt32(output); + remote()->transact(LATENCY, data, &reply); + return reply.readInt32(); + } + + virtual status_t setMasterVolume(float value) + { + Parcel data, reply; + data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); + data.writeFloat(value); + remote()->transact(SET_MASTER_VOLUME, data, &reply); + return reply.readInt32(); + } + + virtual status_t setMasterMute(bool muted) + { + Parcel data, reply; + data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); + data.writeInt32(muted); + remote()->transact(SET_MASTER_MUTE, data, &reply); + return reply.readInt32(); + } + + virtual float masterVolume() const + { + Parcel data, reply; + data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); + remote()->transact(MASTER_VOLUME, data, &reply); + return reply.readFloat(); + } + + virtual bool masterMute() const + { + Parcel data, reply; + data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); + remote()->transact(MASTER_MUTE, data, &reply); + return reply.readInt32(); + } + + virtual status_t setStreamVolume(int stream, float value) + { + Parcel data, reply; + data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); + data.writeInt32(stream); + data.writeFloat(value); + remote()->transact(SET_STREAM_VOLUME, data, &reply); + return reply.readInt32(); + } + + virtual status_t setStreamMute(int stream, bool muted) + { + Parcel data, reply; + data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); + data.writeInt32(stream); + data.writeInt32(muted); + remote()->transact(SET_STREAM_MUTE, data, &reply); + return reply.readInt32(); + } + + virtual float streamVolume(int stream) const + { + Parcel data, reply; + data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); + data.writeInt32(stream); + remote()->transact(STREAM_VOLUME, data, &reply); + return reply.readFloat(); + } + + virtual bool streamMute(int stream) const + { + Parcel data, reply; + data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); + data.writeInt32(stream); + remote()->transact(STREAM_MUTE, data, &reply); + return reply.readInt32(); + } + + virtual status_t setRouting(int mode, uint32_t routes, uint32_t mask) + { + Parcel data, reply; + data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); + data.writeInt32(mode); + data.writeInt32(routes); + data.writeInt32(mask); + remote()->transact(SET_ROUTING, data, &reply); + return reply.readInt32(); + } + + virtual uint32_t getRouting(int mode) const + { + Parcel data, reply; + data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); + data.writeInt32(mode); + remote()->transact(GET_ROUTING, data, &reply); + return reply.readInt32(); + } + + virtual status_t setMode(int mode) + { + Parcel data, reply; + data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); + data.writeInt32(mode); + remote()->transact(SET_MODE, data, &reply); + return reply.readInt32(); + } + + virtual int getMode() const + { + Parcel data, reply; + data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); + remote()->transact(GET_MODE, data, &reply); + return reply.readInt32(); + } + + virtual status_t setMicMute(bool state) + { + Parcel data, reply; + data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); + data.writeInt32(state); + remote()->transact(SET_MIC_MUTE, data, &reply); + return reply.readInt32(); + } + + virtual bool getMicMute() const + { + Parcel data, reply; + data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); + remote()->transact(GET_MIC_MUTE, data, &reply); + return reply.readInt32(); + } + + virtual bool isMusicActive() const + { + Parcel data, reply; + data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); + remote()->transact(IS_MUSIC_ACTIVE, data, &reply); + return reply.readInt32(); + } + + virtual status_t setParameter(const char* key, const char* value) + { + Parcel data, reply; + data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); + data.writeCString(key); + data.writeCString(value); + remote()->transact(SET_PARAMETER, data, &reply); + return reply.readInt32(); + } + + virtual void registerClient(const sp& client) + { + Parcel data, reply; + data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); + data.writeStrongBinder(client->asBinder()); + remote()->transact(REGISTER_CLIENT, data, &reply); + } + + virtual size_t getInputBufferSize(uint32_t sampleRate, int format, int channelCount) + { + Parcel data, reply; + data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); + data.writeInt32(sampleRate); + data.writeInt32(format); + data.writeInt32(channelCount); + remote()->transact(GET_INPUTBUFFERSIZE, data, &reply); + return reply.readInt32(); + } + + virtual void wakeUp() + { + Parcel data, reply; + data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); + remote()->transact(WAKE_UP, data, &reply); + return; + } + + virtual bool isA2dpEnabled() const + { + Parcel data, reply; + data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); + remote()->transact(IS_A2DP_ENABLED, data, &reply); + return (bool)reply.readInt32(); + } +}; + +IMPLEMENT_META_INTERFACE(AudioFlinger, "android.media.IAudioFlinger"); + +// ---------------------------------------------------------------------- + +#define CHECK_INTERFACE(interface, data, reply) \ + do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \ + LOGW("Call incorrectly routed to " #interface); \ + return PERMISSION_DENIED; \ + } } while (0) + +status_t BnAudioFlinger::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch(code) { + case CREATE_TRACK: { + CHECK_INTERFACE(IAudioFlinger, data, reply); + pid_t pid = data.readInt32(); + int streamType = data.readInt32(); + uint32_t sampleRate = data.readInt32(); + int format = data.readInt32(); + int channelCount = data.readInt32(); + size_t bufferCount = data.readInt32(); + uint32_t flags = data.readInt32(); + sp buffer = interface_cast(data.readStrongBinder()); + status_t status; + sp track = createTrack(pid, + streamType, sampleRate, format, + channelCount, bufferCount, flags, buffer, &status); + reply->writeInt32(status); + reply->writeStrongBinder(track->asBinder()); + return NO_ERROR; + } break; + case OPEN_RECORD: { + CHECK_INTERFACE(IAudioFlinger, data, reply); + pid_t pid = data.readInt32(); + int streamType = data.readInt32(); + uint32_t sampleRate = data.readInt32(); + int format = data.readInt32(); + int channelCount = data.readInt32(); + size_t bufferCount = data.readInt32(); + uint32_t flags = data.readInt32(); + status_t status; + sp record = openRecord(pid, streamType, + sampleRate, format, channelCount, bufferCount, flags, &status); + reply->writeInt32(status); + reply->writeStrongBinder(record->asBinder()); + return NO_ERROR; + } break; + case SAMPLE_RATE: { + CHECK_INTERFACE(IAudioFlinger, data, reply); + int output = data.readInt32(); + reply->writeInt32( sampleRate(output) ); + return NO_ERROR; + } break; + case CHANNEL_COUNT: { + CHECK_INTERFACE(IAudioFlinger, data, reply); + int output = data.readInt32(); + reply->writeInt32( channelCount(output) ); + return NO_ERROR; + } break; + case FORMAT: { + CHECK_INTERFACE(IAudioFlinger, data, reply); + int output = data.readInt32(); + reply->writeInt32( format(output) ); + return NO_ERROR; + } break; + case FRAME_COUNT: { + CHECK_INTERFACE(IAudioFlinger, data, reply); + int output = data.readInt32(); + reply->writeInt32( frameCount(output) ); + return NO_ERROR; + } break; + case LATENCY: { + CHECK_INTERFACE(IAudioFlinger, data, reply); + int output = data.readInt32(); + reply->writeInt32( latency(output) ); + return NO_ERROR; + } break; + case SET_MASTER_VOLUME: { + CHECK_INTERFACE(IAudioFlinger, data, reply); + reply->writeInt32( setMasterVolume(data.readFloat()) ); + return NO_ERROR; + } break; + case SET_MASTER_MUTE: { + CHECK_INTERFACE(IAudioFlinger, data, reply); + reply->writeInt32( setMasterMute(data.readInt32()) ); + return NO_ERROR; + } break; + case MASTER_VOLUME: { + CHECK_INTERFACE(IAudioFlinger, data, reply); + reply->writeFloat( masterVolume() ); + return NO_ERROR; + } break; + case MASTER_MUTE: { + CHECK_INTERFACE(IAudioFlinger, data, reply); + reply->writeInt32( masterMute() ); + return NO_ERROR; + } break; + case SET_STREAM_VOLUME: { + CHECK_INTERFACE(IAudioFlinger, data, reply); + int stream = data.readInt32(); + reply->writeInt32( setStreamVolume(stream, data.readFloat()) ); + return NO_ERROR; + } break; + case SET_STREAM_MUTE: { + CHECK_INTERFACE(IAudioFlinger, data, reply); + int stream = data.readInt32(); + reply->writeInt32( setStreamMute(stream, data.readInt32()) ); + return NO_ERROR; + } break; + case STREAM_VOLUME: { + CHECK_INTERFACE(IAudioFlinger, data, reply); + int stream = data.readInt32(); + reply->writeFloat( streamVolume(stream) ); + return NO_ERROR; + } break; + case STREAM_MUTE: { + CHECK_INTERFACE(IAudioFlinger, data, reply); + int stream = data.readInt32(); + reply->writeInt32( streamMute(stream) ); + return NO_ERROR; + } break; + case SET_ROUTING: { + CHECK_INTERFACE(IAudioFlinger, data, reply); + int mode = data.readInt32(); + uint32_t routes = data.readInt32(); + uint32_t mask = data.readInt32(); + reply->writeInt32( setRouting(mode, routes, mask) ); + return NO_ERROR; + } break; + case GET_ROUTING: { + CHECK_INTERFACE(IAudioFlinger, data, reply); + int mode = data.readInt32(); + reply->writeInt32( getRouting(mode) ); + return NO_ERROR; + } break; + case SET_MODE: { + CHECK_INTERFACE(IAudioFlinger, data, reply); + int mode = data.readInt32(); + reply->writeInt32( setMode(mode) ); + return NO_ERROR; + } break; + case GET_MODE: { + CHECK_INTERFACE(IAudioFlinger, data, reply); + reply->writeInt32( getMode() ); + return NO_ERROR; + } break; + case SET_MIC_MUTE: { + CHECK_INTERFACE(IAudioFlinger, data, reply); + int state = data.readInt32(); + reply->writeInt32( setMicMute(state) ); + return NO_ERROR; + } break; + case GET_MIC_MUTE: { + CHECK_INTERFACE(IAudioFlinger, data, reply); + reply->writeInt32( getMicMute() ); + return NO_ERROR; + } break; + case IS_MUSIC_ACTIVE: { + CHECK_INTERFACE(IAudioFlinger, data, reply); + reply->writeInt32( isMusicActive() ); + return NO_ERROR; + } break; + case SET_PARAMETER: { + CHECK_INTERFACE(IAudioFlinger, data, reply); + const char *key = data.readCString(); + const char *value = data.readCString(); + reply->writeInt32( setParameter(key, value) ); + return NO_ERROR; + } break; + case REGISTER_CLIENT: { + CHECK_INTERFACE(IAudioFlinger, data, reply); + sp client = interface_cast(data.readStrongBinder()); + registerClient(client); + return NO_ERROR; + } break; + case GET_INPUTBUFFERSIZE: { + CHECK_INTERFACE(IAudioFlinger, data, reply); + uint32_t sampleRate = data.readInt32(); + int format = data.readInt32(); + int channelCount = data.readInt32(); + reply->writeInt32( getInputBufferSize(sampleRate, format, channelCount) ); + return NO_ERROR; + } break; + case WAKE_UP: { + CHECK_INTERFACE(IAudioFlinger, data, reply); + wakeUp(); + return NO_ERROR; + } break; + case IS_A2DP_ENABLED: { + CHECK_INTERFACE(IAudioFlinger, data, reply); + reply->writeInt32( (int)isA2dpEnabled() ); + return NO_ERROR; + } break; + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + +// ---------------------------------------------------------------------------- + +}; // namespace android diff --git a/media/libmedia/IAudioFlingerClient.cpp b/media/libmedia/IAudioFlingerClient.cpp new file mode 100644 index 0000000..5feb11f --- /dev/null +++ b/media/libmedia/IAudioFlingerClient.cpp @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2009 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 "IAudioFlingerClient" +#include + +#include +#include + +#include + +#include + +namespace android { + +enum { + AUDIO_OUTPUT_CHANGED = IBinder::FIRST_CALL_TRANSACTION +}; + +class BpAudioFlingerClient : public BpInterface +{ +public: + BpAudioFlingerClient(const sp& impl) + : BpInterface(impl) + { + } + + void a2dpEnabledChanged(bool enabled) + { + Parcel data, reply; + data.writeInterfaceToken(IAudioFlingerClient::getInterfaceDescriptor()); + data.writeInt32((int)enabled); + remote()->transact(AUDIO_OUTPUT_CHANGED, data, &reply); + } +}; + +IMPLEMENT_META_INTERFACE(AudioFlingerClient, "android.media.IAudioFlingerClient"); + +// ---------------------------------------------------------------------- + +#define CHECK_INTERFACE(interface, data, reply) \ + do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \ + LOGW("Call incorrectly routed to " #interface); \ + return PERMISSION_DENIED; \ + } } while (0) + +status_t BnAudioFlingerClient::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch(code) { + case AUDIO_OUTPUT_CHANGED: { + CHECK_INTERFACE(IAudioFlingerClient, data, reply); + bool enabled = (bool)data.readInt32(); + a2dpEnabledChanged(enabled); + return NO_ERROR; + } break; + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + +// ---------------------------------------------------------------------------- + +}; // namespace android diff --git a/media/libmedia/IAudioRecord.cpp b/media/libmedia/IAudioRecord.cpp new file mode 100644 index 0000000..6e42dac --- /dev/null +++ b/media/libmedia/IAudioRecord.cpp @@ -0,0 +1,100 @@ +/* +** +** Copyright 2007, 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 + +namespace android { + +enum { + GET_CBLK = IBinder::FIRST_CALL_TRANSACTION, + START, + STOP +}; + +class BpAudioRecord : public BpInterface +{ +public: + BpAudioRecord(const sp& impl) + : BpInterface(impl) + { + } + + virtual status_t start() + { + Parcel data, reply; + data.writeInterfaceToken(IAudioRecord::getInterfaceDescriptor()); + remote()->transact(START, data, &reply); + return reply.readInt32(); + } + + virtual void stop() + { + Parcel data, reply; + data.writeInterfaceToken(IAudioRecord::getInterfaceDescriptor()); + remote()->transact(STOP, data, &reply); + } + + virtual sp getCblk() const + { + Parcel data, reply; + data.writeInterfaceToken(IAudioRecord::getInterfaceDescriptor()); + remote()->transact(GET_CBLK, data, &reply); + return interface_cast(reply.readStrongBinder()); + } +}; + +IMPLEMENT_META_INTERFACE(AudioRecord, "android.media.IAudioRecord"); + +// ---------------------------------------------------------------------- + +#define CHECK_INTERFACE(interface, data, reply) \ + do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \ + LOGW("Call incorrectly routed to " #interface); \ + return PERMISSION_DENIED; \ + } } while (0) + +status_t BnAudioRecord::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch(code) { + case GET_CBLK: { + CHECK_INTERFACE(IAudioRecord, data, reply); + reply->writeStrongBinder(getCblk()->asBinder()); + return NO_ERROR; + } break; + case START: { + CHECK_INTERFACE(IAudioRecord, data, reply); + reply->writeInt32(start()); + return NO_ERROR; + } break; + case STOP: { + CHECK_INTERFACE(IAudioRecord, data, reply); + stop(); + return NO_ERROR; + } break; + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + +}; // namespace android + diff --git a/media/libmedia/IAudioTrack.cpp b/media/libmedia/IAudioTrack.cpp new file mode 100644 index 0000000..abc202d --- /dev/null +++ b/media/libmedia/IAudioTrack.cpp @@ -0,0 +1,140 @@ +/* //device/extlibs/pv/android/IAudioTrack.cpp +** +** Copyright 2007, 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 + +namespace android { + +enum { + GET_CBLK = IBinder::FIRST_CALL_TRANSACTION, + START, + STOP, + FLUSH, + MUTE, + PAUSE +}; + +class BpAudioTrack : public BpInterface +{ +public: + BpAudioTrack(const sp& impl) + : BpInterface(impl) + { + } + + virtual status_t start() + { + Parcel data, reply; + data.writeInterfaceToken(IAudioTrack::getInterfaceDescriptor()); + remote()->transact(START, data, &reply); + return reply.readInt32(); + } + + virtual void stop() + { + Parcel data, reply; + data.writeInterfaceToken(IAudioTrack::getInterfaceDescriptor()); + remote()->transact(STOP, data, &reply); + } + + virtual void flush() + { + Parcel data, reply; + data.writeInterfaceToken(IAudioTrack::getInterfaceDescriptor()); + remote()->transact(FLUSH, data, &reply); + } + + virtual void mute(bool e) + { + Parcel data, reply; + data.writeInterfaceToken(IAudioTrack::getInterfaceDescriptor()); + data.writeInt32(e); + remote()->transact(MUTE, data, &reply); + } + + virtual void pause() + { + Parcel data, reply; + data.writeInterfaceToken(IAudioTrack::getInterfaceDescriptor()); + remote()->transact(PAUSE, data, &reply); + } + + virtual sp getCblk() const + { + Parcel data, reply; + data.writeInterfaceToken(IAudioTrack::getInterfaceDescriptor()); + remote()->transact(GET_CBLK, data, &reply); + return interface_cast(reply.readStrongBinder()); + } +}; + +IMPLEMENT_META_INTERFACE(AudioTrack, "android.media.IAudioTrack"); + +// ---------------------------------------------------------------------- + +#define CHECK_INTERFACE(interface, data, reply) \ + do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \ + LOGW("Call incorrectly routed to " #interface); \ + return PERMISSION_DENIED; \ + } } while (0) + +status_t BnAudioTrack::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch(code) { + case GET_CBLK: { + CHECK_INTERFACE(IAudioTrack, data, reply); + reply->writeStrongBinder(getCblk()->asBinder()); + return NO_ERROR; + } break; + case START: { + CHECK_INTERFACE(IAudioTrack, data, reply); + reply->writeInt32(start()); + return NO_ERROR; + } break; + case STOP: { + CHECK_INTERFACE(IAudioTrack, data, reply); + stop(); + return NO_ERROR; + } break; + case FLUSH: { + CHECK_INTERFACE(IAudioTrack, data, reply); + flush(); + return NO_ERROR; + } break; + case MUTE: { + CHECK_INTERFACE(IAudioTrack, data, reply); + mute( data.readInt32() ); + return NO_ERROR; + } break; + case PAUSE: { + CHECK_INTERFACE(IAudioTrack, data, reply); + pause(); + return NO_ERROR; + } + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + +}; // namespace android + diff --git a/media/libmedia/IMediaMetadataRetriever.cpp b/media/libmedia/IMediaMetadataRetriever.cpp new file mode 100644 index 0000000..85b5944 --- /dev/null +++ b/media/libmedia/IMediaMetadataRetriever.cpp @@ -0,0 +1,218 @@ +/* +** +** Copyright (C) 2008 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 + +namespace android { + +enum { + DISCONNECT = IBinder::FIRST_CALL_TRANSACTION, + SET_DATA_SOURCE_URL, + SET_DATA_SOURCE_FD, + SET_MODE, + GET_MODE, + CAPTURE_FRAME, + EXTARCT_ALBUM_ART, + EXTRACT_METADATA, +}; + +class BpMediaMetadataRetriever: public BpInterface +{ +public: + BpMediaMetadataRetriever(const sp& impl) + : BpInterface(impl) + { + } + + // disconnect from media metadata retriever service + void disconnect() + { + Parcel data, reply; + data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor()); + remote()->transact(DISCONNECT, data, &reply); + } + + status_t setDataSource(const char* srcUrl) + { + Parcel data, reply; + data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor()); + data.writeCString(srcUrl); + remote()->transact(SET_DATA_SOURCE_URL, data, &reply); + return reply.readInt32(); + } + + status_t setDataSource(int fd, int64_t offset, int64_t length) + { + Parcel data, reply; + data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor()); + data.writeFileDescriptor(fd); + data.writeInt64(offset); + data.writeInt64(length); + remote()->transact(SET_DATA_SOURCE_FD, data, &reply); + return reply.readInt32(); + } + + status_t setMode(int mode) + { + Parcel data, reply; + data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor()); + data.writeInt32(mode); + remote()->transact(SET_MODE, data, &reply); + return reply.readInt32(); + } + + status_t getMode(int* mode) const + { + Parcel data, reply; + data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor()); + remote()->transact(GET_MODE, data, &reply); + *mode = reply.readInt32(); + return reply.readInt32(); + } + + sp captureFrame() + { + Parcel data, reply; + data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor()); + remote()->transact(CAPTURE_FRAME, data, &reply); + status_t ret = reply.readInt32(); + if (ret != NO_ERROR) { + return NULL; + } + return interface_cast(reply.readStrongBinder()); + } + + sp extractAlbumArt() + { + Parcel data, reply; + data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor()); + remote()->transact(EXTARCT_ALBUM_ART, data, &reply); + status_t ret = reply.readInt32(); + if (ret != NO_ERROR) { + return NULL; + } + return interface_cast(reply.readStrongBinder()); + } + + const char* extractMetadata(int keyCode) + { + Parcel data, reply; + data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor()); + data.writeInt32(keyCode); + remote()->transact(EXTRACT_METADATA, data, &reply); + status_t ret = reply.readInt32(); + if (ret != NO_ERROR) { + return NULL; + } + return reply.readCString(); + } +}; + +IMPLEMENT_META_INTERFACE(MediaMetadataRetriever, "android.hardware.IMediaMetadataRetriever"); + +// ---------------------------------------------------------------------- + +#define CHECK_INTERFACE(interface, data, reply) \ + do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \ + LOGW("Call incorrectly routed to " #interface); \ + return PERMISSION_DENIED; \ + } } while (0) + +status_t BnMediaMetadataRetriever::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch (code) { + case DISCONNECT: { + CHECK_INTERFACE(IMediaMetadataRetriever, data, reply); + disconnect(); + return NO_ERROR; + } break; + case SET_DATA_SOURCE_URL: { + CHECK_INTERFACE(IMediaMetadataRetriever, data, reply); + const char* srcUrl = data.readCString(); + reply->writeInt32(setDataSource(srcUrl)); + return NO_ERROR; + } break; + case SET_DATA_SOURCE_FD: { + CHECK_INTERFACE(IMediaMetadataRetriever, data, reply); + int fd = dup(data.readFileDescriptor()); + int64_t offset = data.readInt64(); + int64_t length = data.readInt64(); + reply->writeInt32(setDataSource(fd, offset, length)); + return NO_ERROR; + } break; + case SET_MODE: { + CHECK_INTERFACE(IMediaMetadataRetriever, data, reply); + int mode = data.readInt32(); + reply->writeInt32(setMode(mode)); + return NO_ERROR; + } break; + case GET_MODE: { + CHECK_INTERFACE(IMediaMetadataRetriever, data, reply); + int mode; + status_t status = getMode(&mode); + reply->writeInt32(mode); + reply->writeInt32(status); + return NO_ERROR; + } break; + case CAPTURE_FRAME: { + CHECK_INTERFACE(IMediaMetadataRetriever, data, reply); + sp bitmap = captureFrame(); + if (bitmap != 0) { // Don't send NULL across the binder interface + reply->writeInt32(NO_ERROR); + reply->writeStrongBinder(bitmap->asBinder()); + } else { + reply->writeInt32(UNKNOWN_ERROR); + } + return NO_ERROR; + } break; + case EXTARCT_ALBUM_ART: { + CHECK_INTERFACE(IMediaMetadataRetriever, data, reply); + sp albumArt = extractAlbumArt(); + if (albumArt != 0) { // Don't send NULL across the binder interface + reply->writeInt32(NO_ERROR); + reply->writeStrongBinder(albumArt->asBinder()); + } else { + reply->writeInt32(UNKNOWN_ERROR); + } + return NO_ERROR; + } break; + case EXTRACT_METADATA: { + CHECK_INTERFACE(IMediaMetadataRetriever, data, reply); + int keyCode = data.readInt32(); + const char* value = extractMetadata(keyCode); + if (value != NULL) { // Don't send NULL across the binder interface + reply->writeInt32(NO_ERROR); + reply->writeCString(value); + } else { + reply->writeInt32(UNKNOWN_ERROR); + } + return NO_ERROR; + } break; + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + +// ---------------------------------------------------------------------------- + +}; // namespace android + diff --git a/media/libmedia/IMediaPlayer.cpp b/media/libmedia/IMediaPlayer.cpp new file mode 100644 index 0000000..f37519f --- /dev/null +++ b/media/libmedia/IMediaPlayer.cpp @@ -0,0 +1,275 @@ +/* +** +** Copyright 2008, 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 + +namespace android { + +enum { + DISCONNECT = IBinder::FIRST_CALL_TRANSACTION, + SET_VIDEO_SURFACE, + PREPARE_ASYNC, + START, + STOP, + IS_PLAYING, + PAUSE, + SEEK_TO, + GET_CURRENT_POSITION, + GET_DURATION, + RESET, + SET_AUDIO_STREAM_TYPE, + SET_LOOPING, + SET_VOLUME +}; + +class BpMediaPlayer: public BpInterface +{ +public: + BpMediaPlayer(const sp& impl) + : BpInterface(impl) + { + } + + // disconnect from media player service + void disconnect() + { + Parcel data, reply; + data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor()); + remote()->transact(DISCONNECT, data, &reply); + } + + status_t setVideoSurface(const sp& surface) + { + Parcel data, reply; + data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor()); + data.writeStrongBinder(surface->asBinder()); + remote()->transact(SET_VIDEO_SURFACE, data, &reply); + return reply.readInt32(); + } + + status_t prepareAsync() + { + Parcel data, reply; + data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor()); + remote()->transact(PREPARE_ASYNC, data, &reply); + return reply.readInt32(); + } + + status_t start() + { + Parcel data, reply; + data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor()); + remote()->transact(START, data, &reply); + return reply.readInt32(); + } + + status_t stop() + { + Parcel data, reply; + data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor()); + remote()->transact(STOP, data, &reply); + return reply.readInt32(); + } + + status_t isPlaying(bool* state) + { + Parcel data, reply; + data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor()); + remote()->transact(IS_PLAYING, data, &reply); + *state = reply.readInt32(); + return reply.readInt32(); + } + + status_t pause() + { + Parcel data, reply; + data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor()); + remote()->transact(PAUSE, data, &reply); + return reply.readInt32(); + } + + status_t seekTo(int msec) + { + Parcel data, reply; + data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor()); + data.writeInt32(msec); + remote()->transact(SEEK_TO, data, &reply); + return reply.readInt32(); + } + + status_t getCurrentPosition(int* msec) + { + Parcel data, reply; + data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor()); + remote()->transact(GET_CURRENT_POSITION, data, &reply); + *msec = reply.readInt32(); + return reply.readInt32(); + } + + status_t getDuration(int* msec) + { + Parcel data, reply; + data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor()); + remote()->transact(GET_DURATION, data, &reply); + *msec = reply.readInt32(); + return reply.readInt32(); + } + + status_t reset() + { + Parcel data, reply; + data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor()); + remote()->transact(RESET, data, &reply); + return reply.readInt32(); + } + + status_t setAudioStreamType(int type) + { + Parcel data, reply; + data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor()); + data.writeInt32(type); + remote()->transact(SET_AUDIO_STREAM_TYPE, data, &reply); + return reply.readInt32(); + } + + status_t setLooping(int loop) + { + Parcel data, reply; + data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor()); + data.writeInt32(loop); + remote()->transact(SET_LOOPING, data, &reply); + return reply.readInt32(); + } + + status_t setVolume(float leftVolume, float rightVolume) + { + Parcel data, reply; + data.writeFloat(leftVolume); + data.writeFloat(rightVolume); + remote()->transact(SET_VOLUME, data, &reply); + return reply.readInt32(); + } +}; + +IMPLEMENT_META_INTERFACE(MediaPlayer, "android.hardware.IMediaPlayer"); + +// ---------------------------------------------------------------------- + +#define CHECK_INTERFACE(interface, data, reply) \ + do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \ + LOGW("Call incorrectly routed to " #interface); \ + return PERMISSION_DENIED; \ + } } while (0) + +status_t BnMediaPlayer::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch(code) { + case DISCONNECT: { + CHECK_INTERFACE(IMediaPlayer, data, reply); + disconnect(); + return NO_ERROR; + } break; + case SET_VIDEO_SURFACE: { + CHECK_INTERFACE(IMediaPlayer, data, reply); + sp surface = interface_cast(data.readStrongBinder()); + reply->writeInt32(setVideoSurface(surface)); + return NO_ERROR; + } break; + case PREPARE_ASYNC: { + CHECK_INTERFACE(IMediaPlayer, data, reply); + reply->writeInt32(prepareAsync()); + return NO_ERROR; + } break; + case START: { + CHECK_INTERFACE(IMediaPlayer, data, reply); + reply->writeInt32(start()); + return NO_ERROR; + } break; + case STOP: { + CHECK_INTERFACE(IMediaPlayer, data, reply); + reply->writeInt32(stop()); + return NO_ERROR; + } break; + case IS_PLAYING: { + CHECK_INTERFACE(IMediaPlayer, data, reply); + bool state; + status_t ret = isPlaying(&state); + reply->writeInt32(state); + reply->writeInt32(ret); + return NO_ERROR; + } break; + case PAUSE: { + CHECK_INTERFACE(IMediaPlayer, data, reply); + reply->writeInt32(pause()); + return NO_ERROR; + } break; + case SEEK_TO: { + CHECK_INTERFACE(IMediaPlayer, data, reply); + reply->writeInt32(seekTo(data.readInt32())); + return NO_ERROR; + } break; + case GET_CURRENT_POSITION: { + CHECK_INTERFACE(IMediaPlayer, data, reply); + int msec; + status_t ret = getCurrentPosition(&msec); + reply->writeInt32(msec); + reply->writeInt32(ret); + return NO_ERROR; + } break; + case GET_DURATION: { + CHECK_INTERFACE(IMediaPlayer, data, reply); + int msec; + status_t ret = getDuration(&msec); + reply->writeInt32(msec); + reply->writeInt32(ret); + return NO_ERROR; + } break; + case RESET: { + CHECK_INTERFACE(IMediaPlayer, data, reply); + reply->writeInt32(reset()); + return NO_ERROR; + } break; + case SET_AUDIO_STREAM_TYPE: { + CHECK_INTERFACE(IMediaPlayer, data, reply); + reply->writeInt32(setAudioStreamType(data.readInt32())); + return NO_ERROR; + } break; + case SET_LOOPING: { + CHECK_INTERFACE(IMediaPlayer, data, reply); + reply->writeInt32(setLooping(data.readInt32())); + return NO_ERROR; + } break; + case SET_VOLUME: { + reply->writeInt32(setVolume(data.readFloat(), data.readFloat())); + return NO_ERROR; + } break; + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + +// ---------------------------------------------------------------------------- + +}; // namespace android + diff --git a/media/libmedia/IMediaPlayerClient.cpp b/media/libmedia/IMediaPlayerClient.cpp new file mode 100644 index 0000000..65022cd --- /dev/null +++ b/media/libmedia/IMediaPlayerClient.cpp @@ -0,0 +1,77 @@ +/* +** +** Copyright 2008, 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 + +namespace android { + +enum { + NOTIFY = IBinder::FIRST_CALL_TRANSACTION, +}; + +class BpMediaPlayerClient: public BpInterface +{ +public: + BpMediaPlayerClient(const sp& impl) + : BpInterface(impl) + { + } + + virtual void notify(int msg, int ext1, int ext2) + { + Parcel data, reply; + data.writeInterfaceToken(IMediaPlayerClient::getInterfaceDescriptor()); + data.writeInt32(msg); + data.writeInt32(ext1); + data.writeInt32(ext2); + remote()->transact(NOTIFY, data, &reply, IBinder::FLAG_ONEWAY); + } +}; + +IMPLEMENT_META_INTERFACE(MediaPlayerClient, "android.hardware.IMediaPlayerClient"); + +// ---------------------------------------------------------------------- + +#define CHECK_INTERFACE(interface, data, reply) \ + do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \ + LOGW("Call incorrectly routed to " #interface); \ + return PERMISSION_DENIED; \ + } } while (0) + +status_t BnMediaPlayerClient::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch(code) { + case NOTIFY: { + CHECK_INTERFACE(IMediaPlayerClient, data, reply); + int msg = data.readInt32(); + int ext1 = data.readInt32(); + int ext2 = data.readInt32(); + notify(msg, ext1, ext2); + return NO_ERROR; + } break; + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + +}; // namespace android + diff --git a/media/libmedia/IMediaPlayerService.cpp b/media/libmedia/IMediaPlayerService.cpp new file mode 100644 index 0000000..370e3fb --- /dev/null +++ b/media/libmedia/IMediaPlayerService.cpp @@ -0,0 +1,198 @@ +/* +** +** Copyright 2008, 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 + +namespace android { + +enum { + CREATE_URL = IBinder::FIRST_CALL_TRANSACTION, + CREATE_FD, + DECODE_URL, + DECODE_FD, + CREATE_MEDIA_RECORDER, + CREATE_METADATA_RETRIEVER, +}; + +class BpMediaPlayerService: public BpInterface +{ +public: + BpMediaPlayerService(const sp& impl) + : BpInterface(impl) + { + } + + virtual sp createMetadataRetriever(pid_t pid) + { + Parcel data, reply; + data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor()); + data.writeInt32(pid); + remote()->transact(CREATE_METADATA_RETRIEVER, data, &reply); + return interface_cast(reply.readStrongBinder()); + } + + virtual sp create(pid_t pid, const sp& client, const char* url) + { + Parcel data, reply; + data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor()); + data.writeInt32(pid); + data.writeStrongBinder(client->asBinder()); + data.writeCString(url); + remote()->transact(CREATE_URL, data, &reply); + return interface_cast(reply.readStrongBinder()); + } + + virtual sp createMediaRecorder(pid_t pid) + { + Parcel data, reply; + data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor()); + data.writeInt32(pid); + remote()->transact(CREATE_MEDIA_RECORDER, data, &reply); + return interface_cast(reply.readStrongBinder()); + } + + virtual sp create(pid_t pid, const sp& client, int fd, int64_t offset, int64_t length) + { + Parcel data, reply; + data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor()); + data.writeInt32(pid); + data.writeStrongBinder(client->asBinder()); + data.writeFileDescriptor(fd); + data.writeInt64(offset); + data.writeInt64(length); + remote()->transact(CREATE_FD, data, &reply); + return interface_cast(reply.readStrongBinder()); + } + + virtual sp decode(const char* url, uint32_t *pSampleRate, int* pNumChannels, int* pFormat) + { + Parcel data, reply; + data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor()); + data.writeCString(url); + remote()->transact(DECODE_URL, data, &reply); + *pSampleRate = uint32_t(reply.readInt32()); + *pNumChannels = reply.readInt32(); + *pFormat = reply.readInt32(); + return interface_cast(reply.readStrongBinder()); + } + + virtual sp decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels, int* pFormat) + { + Parcel data, reply; + data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor()); + data.writeFileDescriptor(fd); + data.writeInt64(offset); + data.writeInt64(length); + remote()->transact(DECODE_FD, data, &reply); + *pSampleRate = uint32_t(reply.readInt32()); + *pNumChannels = reply.readInt32(); + *pFormat = reply.readInt32(); + return interface_cast(reply.readStrongBinder()); + } +}; + +IMPLEMENT_META_INTERFACE(MediaPlayerService, "android.hardware.IMediaPlayerService"); + +// ---------------------------------------------------------------------- + +#define CHECK_INTERFACE(interface, data, reply) \ + do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \ + LOGW("Call incorrectly routed to " #interface); \ + return PERMISSION_DENIED; \ + } } while (0) + +status_t BnMediaPlayerService::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch(code) { + case CREATE_URL: { + CHECK_INTERFACE(IMediaPlayerService, data, reply); + pid_t pid = data.readInt32(); + sp client = interface_cast(data.readStrongBinder()); + const char* url = data.readCString(); + sp player = create(pid, client, url); + reply->writeStrongBinder(player->asBinder()); + return NO_ERROR; + } break; + case CREATE_FD: { + CHECK_INTERFACE(IMediaPlayerService, data, reply); + pid_t pid = data.readInt32(); + sp client = interface_cast(data.readStrongBinder()); + int fd = dup(data.readFileDescriptor()); + int64_t offset = data.readInt64(); + int64_t length = data.readInt64(); + sp player = create(pid, client, fd, offset, length); + reply->writeStrongBinder(player->asBinder()); + return NO_ERROR; + } break; + case DECODE_URL: { + CHECK_INTERFACE(IMediaPlayerService, data, reply); + const char* url = data.readCString(); + uint32_t sampleRate; + int numChannels; + int format; + sp player = decode(url, &sampleRate, &numChannels, &format); + reply->writeInt32(sampleRate); + reply->writeInt32(numChannels); + reply->writeInt32(format); + reply->writeStrongBinder(player->asBinder()); + return NO_ERROR; + } break; + case DECODE_FD: { + CHECK_INTERFACE(IMediaPlayerService, data, reply); + int fd = dup(data.readFileDescriptor()); + int64_t offset = data.readInt64(); + int64_t length = data.readInt64(); + uint32_t sampleRate; + int numChannels; + int format; + sp player = decode(fd, offset, length, &sampleRate, &numChannels, &format); + reply->writeInt32(sampleRate); + reply->writeInt32(numChannels); + reply->writeInt32(format); + reply->writeStrongBinder(player->asBinder()); + return NO_ERROR; + } break; + case CREATE_MEDIA_RECORDER: { + CHECK_INTERFACE(IMediaPlayerService, data, reply); + pid_t pid = data.readInt32(); + sp recorder = createMediaRecorder(pid); + reply->writeStrongBinder(recorder->asBinder()); + return NO_ERROR; + } break; + case CREATE_METADATA_RETRIEVER: { + CHECK_INTERFACE(IMediaPlayerService, data, reply); + pid_t pid = data.readInt32(); + sp retriever = createMetadataRetriever(pid); + reply->writeStrongBinder(retriever->asBinder()); + return NO_ERROR; + } break; + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + +// ---------------------------------------------------------------------------- + +}; // namespace android + diff --git a/media/libmedia/IMediaRecorder.cpp b/media/libmedia/IMediaRecorder.cpp new file mode 100644 index 0000000..f187bf5 --- /dev/null +++ b/media/libmedia/IMediaRecorder.cpp @@ -0,0 +1,417 @@ +/* + ** + ** Copyright 2008, HTC Inc. + ** + ** 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 "IMediaRecorder" +#include +#include +#include +#include +#include +#include + +namespace android { + +enum { + RELEASE = IBinder::FIRST_CALL_TRANSACTION, + INIT, + CLOSE, + RESET, + STOP, + START, + PREPARE, + GET_MAX_AMPLITUDE, + SET_VIDEO_SOURCE, + SET_AUDIO_SOURCE, + SET_OUTPUT_FORMAT, + SET_VIDEO_ENCODER, + SET_AUDIO_ENCODER, + SET_OUTPUT_FILE_PATH, + SET_OUTPUT_FILE_FD, + SET_VIDEO_SIZE, + SET_VIDEO_FRAMERATE, + SET_PREVIEW_SURFACE, + SET_CAMERA, + SET_LISTENER +}; + +class BpMediaRecorder: public BpInterface +{ +public: + BpMediaRecorder(const sp& impl) + : BpInterface(impl) + { + } + + status_t setCamera(const sp& camera) + { + LOGV("setCamera(%p)", camera.get()); + Parcel data, reply; + data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor()); + data.writeStrongBinder(camera->asBinder()); + remote()->transact(SET_CAMERA, data, &reply); + return reply.readInt32(); + } + + status_t setPreviewSurface(const sp& surface) + { + LOGV("setPreviewSurface(%p)", surface.get()); + Parcel data, reply; + data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor()); + data.writeStrongBinder(surface->asBinder()); + remote()->transact(SET_PREVIEW_SURFACE, data, &reply); + return reply.readInt32(); + } + + status_t init() + { + LOGV("init"); + Parcel data, reply; + data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor()); + remote()->transact(INIT, data, &reply); + return reply.readInt32(); + } + + status_t setVideoSource(int vs) + { + LOGV("setVideoSource(%d)", vs); + Parcel data, reply; + data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor()); + data.writeInt32(vs); + remote()->transact(SET_VIDEO_SOURCE, data, &reply); + return reply.readInt32(); + } + + status_t setAudioSource(int as) + { + LOGV("setAudioSource(%d)", as); + Parcel data, reply; + data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor()); + data.writeInt32(as); + remote()->transact(SET_AUDIO_SOURCE, data, &reply); + return reply.readInt32(); + } + + status_t setOutputFormat(int of) + { + LOGV("setOutputFormat(%d)", of); + Parcel data, reply; + data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor()); + data.writeInt32(of); + remote()->transact(SET_OUTPUT_FORMAT, data, &reply); + return reply.readInt32(); + } + + status_t setVideoEncoder(int ve) + { + LOGV("setVideoEncoder(%d)", ve); + Parcel data, reply; + data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor()); + data.writeInt32(ve); + remote()->transact(SET_VIDEO_ENCODER, data, &reply); + return reply.readInt32(); + } + + status_t setAudioEncoder(int ae) + { + LOGV("setAudioEncoder(%d)", ae); + Parcel data, reply; + data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor()); + data.writeInt32(ae); + remote()->transact(SET_AUDIO_ENCODER, data, &reply); + return reply.readInt32(); + } + + status_t setOutputFile(const char* path) + { + LOGV("setOutputFile(%s)", path); + Parcel data, reply; + data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor()); + data.writeCString(path); + remote()->transact(SET_OUTPUT_FILE_PATH, data, &reply); + return reply.readInt32(); + } + + status_t setOutputFile(int fd, int64_t offset, int64_t length) { + LOGV("setOutputFile(%d, %lld, %lld)", fd, offset, length); + Parcel data, reply; + data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor()); + data.writeFileDescriptor(fd); + data.writeInt64(offset); + data.writeInt64(length); + remote()->transact(SET_OUTPUT_FILE_FD, data, &reply); + return reply.readInt32(); + } + + status_t setVideoSize(int width, int height) + { + LOGV("setVideoSize(%dx%d)", width, height); + Parcel data, reply; + data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor()); + data.writeInt32(width); + data.writeInt32(height); + remote()->transact(SET_VIDEO_SIZE, data, &reply); + return reply.readInt32(); + } + + status_t setVideoFrameRate(int frames_per_second) + { + LOGV("setVideoFrameRate(%d)", frames_per_second); + Parcel data, reply; + data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor()); + data.writeInt32(frames_per_second); + remote()->transact(SET_VIDEO_FRAMERATE, data, &reply); + return reply.readInt32(); + } + + status_t setListener(const sp& listener) + { + LOGV("setListener(%p)", listener.get()); + Parcel data, reply; + data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor()); + data.writeStrongBinder(listener->asBinder()); + remote()->transact(SET_LISTENER, data, &reply); + return reply.readInt32(); + } + + status_t prepare() + { + LOGV("prepare"); + Parcel data, reply; + data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor()); + remote()->transact(PREPARE, data, &reply); + return reply.readInt32(); + } + + status_t getMaxAmplitude(int* max) + { + LOGV("getMaxAmplitude"); + Parcel data, reply; + data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor()); + remote()->transact(GET_MAX_AMPLITUDE, data, &reply); + *max = reply.readInt32(); + return reply.readInt32(); + } + + status_t start() + { + LOGV("start"); + Parcel data, reply; + data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor()); + remote()->transact(START, data, &reply); + return reply.readInt32(); + } + + status_t stop() + { + LOGV("stop"); + Parcel data, reply; + data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor()); + remote()->transact(STOP, data, &reply); + return reply.readInt32(); + } + + status_t reset() + { + LOGV("reset"); + Parcel data, reply; + data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor()); + remote()->transact(RESET, data, &reply); + return reply.readInt32(); + } + + status_t close() + { + LOGV("close"); + Parcel data, reply; + data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor()); + remote()->transact(CLOSE, data, &reply); + return reply.readInt32(); + } + + status_t release() + { + LOGV("release"); + Parcel data, reply; + data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor()); + remote()->transact(RELEASE, data, &reply); + return reply.readInt32(); + } +}; + +IMPLEMENT_META_INTERFACE(MediaRecorder, "android.hardware.IMediaRecorder"); + +// ---------------------------------------------------------------------- + +#define CHECK_INTERFACE(interface, data, reply) \ + do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \ + LOGW("Call incorrectly routed to " #interface); \ + return PERMISSION_DENIED; \ + } } while (0) + +status_t BnMediaRecorder::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch(code) { + case RELEASE: { + LOGV("RELEASE"); + CHECK_INTERFACE(IMediaRecorder, data, reply); + reply->writeInt32(release()); + return NO_ERROR; + } break; + case INIT: { + LOGV("INIT"); + CHECK_INTERFACE(IMediaRecorder, data, reply); + reply->writeInt32(init()); + return NO_ERROR; + } break; + case CLOSE: { + LOGV("CLOSE"); + CHECK_INTERFACE(IMediaRecorder, data, reply); + reply->writeInt32(close()); + return NO_ERROR; + } break; + case RESET: { + LOGV("RESET"); + CHECK_INTERFACE(IMediaRecorder, data, reply); + reply->writeInt32(reset()); + return NO_ERROR; + } break; + case STOP: { + LOGV("STOP"); + CHECK_INTERFACE(IMediaRecorder, data, reply); + reply->writeInt32(stop()); + return NO_ERROR; + } break; + case START: { + LOGV("START"); + CHECK_INTERFACE(IMediaRecorder, data, reply); + reply->writeInt32(start()); + return NO_ERROR; + } break; + case PREPARE: { + LOGV("PREPARE"); + CHECK_INTERFACE(IMediaRecorder, data, reply); + reply->writeInt32(prepare()); + return NO_ERROR; + } break; + case GET_MAX_AMPLITUDE: { + LOGV("GET_MAX_AMPLITUDE"); + CHECK_INTERFACE(IMediaRecorder, data, reply); + int max = 0; + status_t ret = getMaxAmplitude(&max); + reply->writeInt32(max); + reply->writeInt32(ret); + return NO_ERROR; + } break; + case SET_VIDEO_SOURCE: { + LOGV("SET_VIDEO_SOURCE"); + CHECK_INTERFACE(IMediaRecorder, data, reply); + int vs = data.readInt32(); + reply->writeInt32(setVideoSource(vs)); + return NO_ERROR; + } break; + case SET_AUDIO_SOURCE: { + LOGV("SET_AUDIO_SOURCE"); + CHECK_INTERFACE(IMediaRecorder, data, reply); + int as = data.readInt32(); + reply->writeInt32(setAudioSource(as)); + return NO_ERROR; + } break; + case SET_OUTPUT_FORMAT: { + LOGV("SET_OUTPUT_FORMAT"); + CHECK_INTERFACE(IMediaRecorder, data, reply); + int of = data.readInt32(); + reply->writeInt32(setOutputFormat(of)); + return NO_ERROR; + } break; + case SET_VIDEO_ENCODER: { + LOGV("SET_VIDEO_ENCODER"); + CHECK_INTERFACE(IMediaRecorder, data, reply); + int ve = data.readInt32(); + reply->writeInt32(setVideoEncoder(ve)); + return NO_ERROR; + } break; + case SET_AUDIO_ENCODER: { + LOGV("SET_AUDIO_ENCODER"); + CHECK_INTERFACE(IMediaRecorder, data, reply); + int ae = data.readInt32(); + reply->writeInt32(setAudioEncoder(ae)); + return NO_ERROR; + + } break; + case SET_OUTPUT_FILE_PATH: { + LOGV("SET_OUTPUT_FILE_PATH"); + CHECK_INTERFACE(IMediaRecorder, data, reply); + const char* path = data.readCString(); + reply->writeInt32(setOutputFile(path)); + return NO_ERROR; + } break; + case SET_OUTPUT_FILE_FD: { + LOGV("SET_OUTPUT_FILE_FD"); + CHECK_INTERFACE(IMediaRecorder, data, reply); + int fd = dup(data.readFileDescriptor()); + int64_t offset = data.readInt64(); + int64_t length = data.readInt64(); + reply->writeInt32(setOutputFile(fd, offset, length)); + return NO_ERROR; + } break; + case SET_VIDEO_SIZE: { + LOGV("SET_VIDEO_SIZE"); + CHECK_INTERFACE(IMediaRecorder, data, reply); + int width = data.readInt32(); + int height = data.readInt32(); + reply->writeInt32(setVideoSize(width, height)); + return NO_ERROR; + } break; + case SET_VIDEO_FRAMERATE: { + LOGV("SET_VIDEO_FRAMERATE"); + CHECK_INTERFACE(IMediaRecorder, data, reply); + int frames_per_second = data.readInt32(); + reply->writeInt32(setVideoFrameRate(frames_per_second)); + return NO_ERROR; + } break; + case SET_LISTENER: { + LOGV("SET_LISTENER"); + CHECK_INTERFACE(IMediaRecorder, data, reply); + sp listener = + interface_cast(data.readStrongBinder()); + reply->writeInt32(setListener(listener)); + return NO_ERROR; + } break; + case SET_PREVIEW_SURFACE: { + LOGV("SET_PREVIEW_SURFACE"); + CHECK_INTERFACE(IMediaRecorder, data, reply); + sp surface = interface_cast(data.readStrongBinder()); + reply->writeInt32(setPreviewSurface(surface)); + return NO_ERROR; + } break; + case SET_CAMERA: { + LOGV("SET_CAMERA"); + CHECK_INTERFACE(IMediaRecorder, data, reply); + sp camera = interface_cast(data.readStrongBinder()); + reply->writeInt32(setCamera(camera)); + return NO_ERROR; + } break; + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + +// ---------------------------------------------------------------------------- + +}; // namespace android diff --git a/media/libmedia/JetPlayer.cpp b/media/libmedia/JetPlayer.cpp new file mode 100644 index 0000000..2c62104 --- /dev/null +++ b/media/libmedia/JetPlayer.cpp @@ -0,0 +1,489 @@ +/* + * Copyright (C) 2008 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 "JetPlayer-C" + +#include +#include + +#include + + +#ifdef HAVE_GETTID +static pid_t myTid() { return gettid(); } +#else +static pid_t myTid() { return getpid(); } +#endif + + +namespace android +{ + +static const int MIX_NUM_BUFFERS = 4; +static const S_EAS_LIB_CONFIG* pLibConfig = NULL; + +//------------------------------------------------------------------------------------------------- +JetPlayer::JetPlayer(jobject javaJetPlayer, int maxTracks, int trackBufferSize) : + mEventCallback(NULL), + mJavaJetPlayerRef(javaJetPlayer), + mTid(-1), + mRender(false), + mPaused(false), + mMaxTracks(maxTracks), + mEasData(NULL), + mEasJetFileLoc(NULL), + mAudioTrack(NULL), + mTrackBufferSize(trackBufferSize) +{ + LOGV("JetPlayer constructor"); + mPreviousJetStatus.currentUserID = -1; + mPreviousJetStatus.segmentRepeatCount = -1; + mPreviousJetStatus.numQueuedSegments = -1; + mPreviousJetStatus.paused = true; +} + +//------------------------------------------------------------------------------------------------- +JetPlayer::~JetPlayer() +{ + LOGV("~JetPlayer"); + release(); + +} + +//------------------------------------------------------------------------------------------------- +int JetPlayer::init() +{ + //Mutex::Autolock lock(&mMutex); + + EAS_RESULT result; + + // retrieve the EAS library settings + if (pLibConfig == NULL) + pLibConfig = EAS_Config(); + if (pLibConfig == NULL) { + LOGE("JetPlayer::init(): EAS library configuration could not be retrieved, aborting."); + return EAS_FAILURE; + } + + // init the EAS library + result = EAS_Init(&mEasData); + if( result != EAS_SUCCESS) { + LOGE("JetPlayer::init(): Error initializing Sonivox EAS library, aborting."); + mState = EAS_STATE_ERROR; + return result; + } + // init the JET library with the default app event controller range + result = JET_Init(mEasData, NULL, sizeof(S_JET_CONFIG)); + if( result != EAS_SUCCESS) { + LOGE("JetPlayer::init(): Error initializing JET library, aborting."); + mState = EAS_STATE_ERROR; + return result; + } + + // create the output AudioTrack + mAudioTrack = new AudioTrack(); + mAudioTrack->set(AudioSystem::MUSIC, //TODO parametrize this + pLibConfig->sampleRate, + 1, // format = PCM 16bits per sample, + pLibConfig->numChannels, + mTrackBufferSize, + 0); + + // create render and playback thread + { + Mutex::Autolock l(mMutex); + LOGV("JetPlayer::init(): trying to start render thread"); + createThreadEtc(renderThread, this, "jetRenderThread", ANDROID_PRIORITY_AUDIO); + mCondition.wait(mMutex); + } + if (mTid > 0) { + // render thread started, we're ready + LOGV("JetPlayer::init(): render thread(%d) successfully started.", mTid); + mState = EAS_STATE_READY; + } else { + LOGE("JetPlayer::init(): failed to start render thread."); + mState = EAS_STATE_ERROR; + return EAS_FAILURE; + } + + return EAS_SUCCESS; +} + +void JetPlayer::setEventCallback(jetevent_callback eventCallback) +{ + Mutex::Autolock l(mMutex); + mEventCallback = eventCallback; +} + +//------------------------------------------------------------------------------------------------- +int JetPlayer::release() +{ + LOGV("JetPlayer::release()"); + Mutex::Autolock lock(mMutex); + mPaused = true; + mRender = false; + if (mEasData) { + JET_Pause(mEasData); + JET_CloseFile(mEasData); + JET_Shutdown(mEasData); + EAS_Shutdown(mEasData); + } + if (mEasJetFileLoc) { + free(mEasJetFileLoc); + mEasJetFileLoc = NULL; + } + if (mAudioTrack) { + mAudioTrack->stop(); + mAudioTrack->flush(); + delete mAudioTrack; + mAudioTrack = NULL; + } + if (mAudioBuffer) { + delete mAudioBuffer; + mAudioBuffer = NULL; + } + mEasData = NULL; + + return EAS_SUCCESS; +} + + +//------------------------------------------------------------------------------------------------- +int JetPlayer::renderThread(void* p) { + + return ((JetPlayer*)p)->render(); +} + +//------------------------------------------------------------------------------------------------- +int JetPlayer::render() { + EAS_RESULT result = EAS_FAILURE; + EAS_I32 count; + int temp; + bool audioStarted = false; + + LOGV("JetPlayer::render(): entering"); + + // allocate render buffer + mAudioBuffer = + new EAS_PCM[pLibConfig->mixBufferSize * pLibConfig->numChannels * MIX_NUM_BUFFERS]; + if (!mAudioBuffer) { + LOGE("JetPlayer::render(): mAudioBuffer allocate failed"); + goto threadExit; + } + + // signal main thread that we started + { + Mutex::Autolock l(mMutex); + mTid = myTid(); + LOGV("JetPlayer::render(): render thread(%d) signal", mTid); + mCondition.signal(); + } + + while (1) { + mMutex.lock(); // [[[[[[[[ LOCK --------------------------------------- + + // nothing to render, wait for client thread to wake us up + while (!mRender) + { + LOGV("JetPlayer::render(): signal wait"); + if (audioStarted) { + mAudioTrack->pause(); + // we have to restart the playback once we start rendering again + audioStarted = false; + } + mCondition.wait(mMutex); + LOGV("JetPlayer::render(): signal rx'd"); + } + + // render midi data into the input buffer + int num_output = 0; + EAS_PCM* p = mAudioBuffer; + for (int i = 0; i < MIX_NUM_BUFFERS; i++) { + result = EAS_Render(mEasData, p, pLibConfig->mixBufferSize, &count); + if (result != EAS_SUCCESS) { + LOGE("JetPlayer::render(): EAS_Render returned error %ld", result); + } + p += count * pLibConfig->numChannels; + num_output += count * pLibConfig->numChannels * sizeof(EAS_PCM); + + // send events that were generated (if any) to the event callback + fireEventsFromJetQueue(); + } + + // update playback state + //LOGV("JetPlayer::render(): updating state"); + JET_Status(mEasData, &mJetStatus); + fireUpdateOnStatusChange(); + mPaused = mJetStatus.paused; + + mMutex.unlock(); // UNLOCK ]]]]]]]] ----------------------------------- + + // check audio output track + if (mAudioTrack == NULL) { + LOGE("JetPlayer::render(): output AudioTrack was not created"); + goto threadExit; + } + + // Write data to the audio hardware + //LOGV("JetPlayer::render(): writing to audio output"); + if ((temp = mAudioTrack->write(mAudioBuffer, num_output)) < 0) { + LOGE("JetPlayer::render(): Error in writing:%d",temp); + return temp; + } + + // start audio output if necessary + if (!audioStarted) { + LOGV("JetPlayer::render(): starting audio playback"); + mAudioTrack->start(); + audioStarted = true; + } + + }//while (1) + +threadExit: + mAudioTrack->flush(); + if (mAudioBuffer) { + delete [] mAudioBuffer; + mAudioBuffer = NULL; + } + mMutex.lock(); + mTid = -1; + mCondition.signal(); + mMutex.unlock(); + return result; +} + + +//------------------------------------------------------------------------------------------------- +// fire up an update if any of the status fields has changed +// precondition: mMutex locked +void JetPlayer::fireUpdateOnStatusChange() +{ + if( (mJetStatus.currentUserID != mPreviousJetStatus.currentUserID) + ||(mJetStatus.segmentRepeatCount != mPreviousJetStatus.segmentRepeatCount) ) { + if(mEventCallback) { + mEventCallback( + JetPlayer::JET_USERID_UPDATE, + mJetStatus.currentUserID, + mJetStatus.segmentRepeatCount, + mJavaJetPlayerRef); + } + mPreviousJetStatus.currentUserID = mJetStatus.currentUserID; + mPreviousJetStatus.segmentRepeatCount = mJetStatus.segmentRepeatCount; + } + + if(mJetStatus.numQueuedSegments != mPreviousJetStatus.numQueuedSegments) { + if(mEventCallback) { + mEventCallback( + JetPlayer::JET_NUMQUEUEDSEGMENT_UPDATE, + mJetStatus.numQueuedSegments, + -1, + mJavaJetPlayerRef); + } + mPreviousJetStatus.numQueuedSegments = mJetStatus.numQueuedSegments; + } + + if(mJetStatus.paused != mPreviousJetStatus.paused) { + if(mEventCallback) { + mEventCallback(JetPlayer::JET_PAUSE_UPDATE, + mJetStatus.paused, + -1, + mJavaJetPlayerRef); + } + mPreviousJetStatus.paused = mJetStatus.paused; + } + +} + + +//------------------------------------------------------------------------------------------------- +// fire up all the JET events in the JET engine queue (until the queue is empty) +// precondition: mMutex locked +void JetPlayer::fireEventsFromJetQueue() +{ + if(!mEventCallback) { + // no callback, just empty the event queue + while (JET_GetEvent(mEasData, NULL, NULL)) { } + return; + } + + EAS_U32 rawEvent; + while (JET_GetEvent(mEasData, &rawEvent, NULL)) { + mEventCallback( + JetPlayer::JET_EVENT, + rawEvent, + -1, + mJavaJetPlayerRef); + } +} + + +//------------------------------------------------------------------------------------------------- +int JetPlayer::loadFromFile(const char* path) +{ + LOGV("JetPlayer::loadFromFile(): path=%s", path); + + Mutex::Autolock lock(mMutex); + + mEasJetFileLoc = (EAS_FILE_LOCATOR) malloc(sizeof(EAS_FILE)); + memset(mJetFilePath, 0, 256); + strncpy(mJetFilePath, path, strlen(path)); + mEasJetFileLoc->path = mJetFilePath; + + mEasJetFileLoc->fd = 0; + mEasJetFileLoc->length = 0; + mEasJetFileLoc->offset = 0; + + EAS_RESULT result = JET_OpenFile(mEasData, mEasJetFileLoc); + if(result != EAS_SUCCESS) + mState = EAS_STATE_ERROR; + else + mState = EAS_STATE_OPEN; + return( result ); +} + + +//------------------------------------------------------------------------------------------------- +int JetPlayer::loadFromFD(const int fd, const long long offset, const long long length) +{ + LOGV("JetPlayer::loadFromFD(): fd=%d offset=%lld length=%lld", fd, offset, length); + + Mutex::Autolock lock(mMutex); + + mEasJetFileLoc = (EAS_FILE_LOCATOR) malloc(sizeof(EAS_FILE)); + mEasJetFileLoc->fd = fd; + mEasJetFileLoc->offset = offset; + mEasJetFileLoc->length = length; + mEasJetFileLoc->path = NULL; + + EAS_RESULT result = JET_OpenFile(mEasData, mEasJetFileLoc); + if(result != EAS_SUCCESS) + mState = EAS_STATE_ERROR; + else + mState = EAS_STATE_OPEN; + return( result ); +} + + +//------------------------------------------------------------------------------------------------- +int JetPlayer::closeFile() +{ + Mutex::Autolock lock(mMutex); + return JET_CloseFile(mEasData); +} + + +//------------------------------------------------------------------------------------------------- +int JetPlayer::play() +{ + LOGV("JetPlayer::play(): entering"); + Mutex::Autolock lock(mMutex); + + EAS_RESULT result = JET_Play(mEasData); + + mPaused = false; + mRender = true; + + JET_Status(mEasData, &mJetStatus); + this->dumpJetStatus(&mJetStatus); + + fireUpdateOnStatusChange(); + + // wake up render thread + LOGV("JetPlayer::play(): wakeup render thread"); + mCondition.signal(); + + return result; +} + +//------------------------------------------------------------------------------------------------- +int JetPlayer::pause() +{ + Mutex::Autolock lock(mMutex); + mPaused = true; + EAS_RESULT result = JET_Pause(mEasData); + + mRender = false; + + JET_Status(mEasData, &mJetStatus); + this->dumpJetStatus(&mJetStatus); + fireUpdateOnStatusChange(); + + + return result; +} + + +//------------------------------------------------------------------------------------------------- +int JetPlayer::queueSegment(int segmentNum, int libNum, int repeatCount, int transpose, + EAS_U32 muteFlags, EAS_U8 userID) +{ + LOGV("JetPlayer::queueSegment segmentNum=%d, libNum=%d, repeatCount=%d, transpose=%d", + segmentNum, libNum, repeatCount, transpose); + Mutex::Autolock lock(mMutex); + return JET_QueueSegment(mEasData, segmentNum, libNum, repeatCount, transpose, muteFlags, userID); +} + +//------------------------------------------------------------------------------------------------- +int JetPlayer::setMuteFlags(EAS_U32 muteFlags, bool sync) +{ + Mutex::Autolock lock(mMutex); + return JET_SetMuteFlags(mEasData, muteFlags, sync); +} + +//------------------------------------------------------------------------------------------------- +int JetPlayer::setMuteFlag(int trackNum, bool muteFlag, bool sync) +{ + Mutex::Autolock lock(mMutex); + return JET_SetMuteFlag(mEasData, trackNum, muteFlag, sync); +} + +//------------------------------------------------------------------------------------------------- +int JetPlayer::triggerClip(int clipId) +{ + LOGV("JetPlayer::triggerClip clipId=%d", clipId); + Mutex::Autolock lock(mMutex); + return JET_TriggerClip(mEasData, clipId); +} + +//------------------------------------------------------------------------------------------------- +int JetPlayer::clearQueue() +{ + LOGV("JetPlayer::clearQueue"); + Mutex::Autolock lock(mMutex); + return JET_Clear_Queue(mEasData); +} + +//------------------------------------------------------------------------------------------------- +void JetPlayer::dump() +{ + LOGE("JetPlayer dump: JET file=%s", mEasJetFileLoc->path); +} + +void JetPlayer::dumpJetStatus(S_JET_STATUS* pJetStatus) +{ + if(pJetStatus!=NULL) + LOGV(">> current JET player status: userID=%d segmentRepeatCount=%d numQueuedSegments=%d paused=%d", + pJetStatus->currentUserID, pJetStatus->segmentRepeatCount, + pJetStatus->numQueuedSegments, pJetStatus->paused); + else + LOGE(">> JET player status is NULL"); +} + + +} // end namespace android + diff --git a/media/libmedia/MODULE_LICENSE_APACHE2 b/media/libmedia/MODULE_LICENSE_APACHE2 new file mode 100644 index 0000000..e69de29 diff --git a/media/libmedia/NOTICE b/media/libmedia/NOTICE new file mode 100644 index 0000000..c5b1efa --- /dev/null +++ b/media/libmedia/NOTICE @@ -0,0 +1,190 @@ + + Copyright (c) 2005-2008, 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. + + 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. + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + diff --git a/media/libmedia/ToneGenerator.cpp b/media/libmedia/ToneGenerator.cpp new file mode 100644 index 0000000..5416629 --- /dev/null +++ b/media/libmedia/ToneGenerator.cpp @@ -0,0 +1,730 @@ +/* + * Copyright (C) 2008 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 "ToneGenerator" +#include + +#include +#include +#include +#include +#include +#include +#include "media/ToneGenerator.h" + +namespace android { + +// Descriptors for all available tones (See ToneGenerator::ToneDescriptor class declaration for details) +const ToneGenerator::ToneDescriptor + ToneGenerator::toneDescriptors[NUM_TONES] = { + // waveFreq[] segments[] repeatCnt + { { 1336, 941, 0 }, { ToneGenerator::TONEGEN_INF, 0 }, ToneGenerator::TONEGEN_INF }, // TONE_DTMF_0 + { { 1209, 697, 0 }, { ToneGenerator::TONEGEN_INF, 0 }, ToneGenerator::TONEGEN_INF }, // TONE_DTMF_1 + { { 1336, 697, 0 }, { ToneGenerator::TONEGEN_INF, 0 }, ToneGenerator::TONEGEN_INF }, // TONE_DTMF_2 + { { 1477, 697, 0 }, { ToneGenerator::TONEGEN_INF, 0 }, ToneGenerator::TONEGEN_INF }, // TONE_DTMF_3 + { { 1209, 770, 0 }, { ToneGenerator::TONEGEN_INF, 0 }, ToneGenerator::TONEGEN_INF }, // TONE_DTMF_4 + { { 1336, 770, 0 }, { ToneGenerator::TONEGEN_INF, 0 }, ToneGenerator::TONEGEN_INF }, // TONE_DTMF_5 + { { 1477, 770, 0 }, { ToneGenerator::TONEGEN_INF, 0 }, ToneGenerator::TONEGEN_INF }, // TONE_DTMF_6 + { { 1209, 852, 0 }, { ToneGenerator::TONEGEN_INF, 0 }, ToneGenerator::TONEGEN_INF }, // TONE_DTMF_7 + { { 1336, 852, 0 }, { ToneGenerator::TONEGEN_INF, 0 }, ToneGenerator::TONEGEN_INF }, // TONE_DTMF_8 + { { 1477, 852, 0 }, { ToneGenerator::TONEGEN_INF, 0 }, ToneGenerator::TONEGEN_INF }, // TONE_DTMF_9 + { { 1209, 941, 0 }, { ToneGenerator::TONEGEN_INF, 0 }, ToneGenerator::TONEGEN_INF }, // TONE_DTMF_S + { { 1477, 941, 0 }, { ToneGenerator::TONEGEN_INF, 0 }, ToneGenerator::TONEGEN_INF }, // TONE_DTMF_P + { { 1633, 697, 0 }, { ToneGenerator::TONEGEN_INF, 0 }, ToneGenerator::TONEGEN_INF }, // TONE_DTMF_A + { { 1633, 770, 0 }, { ToneGenerator::TONEGEN_INF, 0 }, ToneGenerator::TONEGEN_INF }, // TONE_DTMF_B + { { 1633, 852, 0 }, { ToneGenerator::TONEGEN_INF, 0 }, ToneGenerator::TONEGEN_INF }, // TONE_DTMF_C + { { 1633, 941, 0 }, { ToneGenerator::TONEGEN_INF, 0 }, ToneGenerator::TONEGEN_INF }, // TONE_DTMF_D + { { 425, 0 }, { ToneGenerator::TONEGEN_INF, 0 }, ToneGenerator::TONEGEN_INF }, // TONE_SUP_DIAL + { { 425, 0 }, { 500, 500, 0 }, ToneGenerator::TONEGEN_INF }, // TONE_SUP_BUSY + { { 425, 0 }, { 200, 200, 0 }, ToneGenerator::TONEGEN_INF }, // TONE_SUP_CONGESTION + { { 425, 0 }, { 200, 0 }, 0 }, // TONE_SUP_RADIO_ACK + { { 425, 0 }, { 200, 200, 0 }, 2 }, // TONE_SUP_RADIO_NOTAVAIL + { { 950, 1400, 1800, 0 }, { 330, 1000, 0 }, ToneGenerator::TONEGEN_INF }, // TONE_SUP_ERROR + { { 425, 0 }, { 200, 600, 200, 3000, 0 }, ToneGenerator::TONEGEN_INF }, // TONE_SUP_CALL_WAITING + { { 425, 0 }, { 1000, 4000, 0 }, ToneGenerator::TONEGEN_INF }, // TONE_SUP_RINGTONE + { { 400, 1200, 0 }, { 40, 0 }, 0 }, // TONE_PROP_BEEP + { { 1200, 0 }, { 100, 100, 0 }, 1 }, // TONE_PROP_ACK + { { 300, 400, 500, 0 }, { 400, 0 }, 0 }, // TONE_PROP_NACK + { { 400, 1200, 0 }, { 200, 0 }, 0 }, // TONE_PROP_PROMPT + { { 400, 1200, 0 }, { 40, 200, 40, 0 }, 0 } // TONE_PROP_BEEP2 + }; + +//////////////////////////////////////////////////////////////////////////////// +// ToneGenerator class Implementation +//////////////////////////////////////////////////////////////////////////////// + + +//---------------------------------- public methods ---------------------------- + + +//////////////////////////////////////////////////////////////////////////////// +// +// Method: ToneGenerator::ToneGenerator() +// +// Description: Constructor. Initializes the tone sequencer, intantiates required sine wave +// generators, instantiates output audio track. +// +// Input: +// toneType: Type of tone generated (values in enum tone_type) +// streamType: Type of stream used for tone playback (enum AudioTrack::stream_type) +// volume: volume applied to tone (0.0 to 1.0) +// +// Output: +// none +// +//////////////////////////////////////////////////////////////////////////////// +ToneGenerator::ToneGenerator(int streamType, float volume) { + + LOGV("ToneGenerator constructor: streamType=%d, volume=%f\n", streamType, volume); + + mState = TONE_IDLE; + + if (AudioSystem::getOutputSamplingRate(&mSamplingRate, streamType) != NO_ERROR) { + LOGE("Unable to marshal AudioFlinger"); + return; + } + mStreamType = streamType; + mVolume = volume; + mpAudioTrack = 0; + mpToneDesc = 0; + mpNewToneDesc = 0; + // Generate tone by chunks of 20 ms to keep cadencing precision + mProcessSize = (mSamplingRate * 20) / 1000; + + if (initAudioTrack()) { + LOGV("ToneGenerator INIT OK, time: %d\n", (unsigned int)(systemTime()/1000000)); + } else { + LOGV("!!!ToneGenerator INIT FAILED!!!\n"); + } +} + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// Method: ToneGenerator::~ToneGenerator() +// +// Description: Destructor. Stop sound playback and delete audio track if +// needed and delete sine wave generators. +// +// Input: +// none +// +// Output: +// none +// +//////////////////////////////////////////////////////////////////////////////// +ToneGenerator::~ToneGenerator() { + LOGV("ToneGenerator destructor\n"); + + if (mpAudioTrack) { + stopTone(); + LOGV("Delete Track: %p\n", mpAudioTrack); + delete mpAudioTrack; + } +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Method: ToneGenerator::startTone() +// +// Description: Starts tone playback. +// +// Input: +// none +// +// Output: +// none +// +//////////////////////////////////////////////////////////////////////////////// +bool ToneGenerator::startTone(int toneType) { + bool lResult = false; + + if (toneType >= NUM_TONES) + return lResult; + + if (mState == TONE_IDLE) { + LOGV("startTone: try to re-init AudioTrack"); + if (!initAudioTrack()) { + return lResult; + } + } + + LOGV("startTone\n"); + + mLock.lock(); + + // Get descriptor for requested tone + mpNewToneDesc = &toneDescriptors[toneType]; + + if (mState == TONE_INIT) { + if (prepareWave()) { + LOGV("Immediate start, time %d\n", (unsigned int)(systemTime()/1000000)); + lResult = true; + mState = TONE_STARTING; + mLock.unlock(); + mpAudioTrack->start(); + mLock.lock(); + if (mState == TONE_STARTING) { + LOGV("Wait for start callback"); + if (mWaitCbkCond.waitRelative(mLock, seconds(1)) != NO_ERROR) { + LOGE("--- Immediate start timed out"); + mState = TONE_IDLE; + lResult = false; + } + } + } else { + mState == TONE_IDLE; + } + } else { + LOGV("Delayed start\n"); + + mState = TONE_RESTARTING; + if (mWaitCbkCond.waitRelative(mLock, seconds(1)) == NO_ERROR) { + if (mState != TONE_IDLE) { + lResult = true; + } + LOGV("cond received"); + } else { + LOGE("--- Delayed start timed out"); + mState = TONE_IDLE; + } + } + mLock.unlock(); + + LOGV_IF(lResult, "Tone started, time %d\n", (unsigned int)(systemTime()/1000000)); + LOGW_IF(!lResult, "Tone start failed!!!, time %d\n", (unsigned int)(systemTime()/1000000)); + + return lResult; +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Method: ToneGenerator::stopTone() +// +// Description: Stops tone playback. +// +// Input: +// none +// +// Output: +// none +// +//////////////////////////////////////////////////////////////////////////////// +void ToneGenerator::stopTone() { + LOGV("stopTone"); + + mLock.lock(); + if (mState == TONE_PLAYING || mState == TONE_STARTING || mState == TONE_RESTARTING) { + mState = TONE_STOPPING; + LOGV("waiting cond"); + status_t lStatus = mWaitCbkCond.waitRelative(mLock, seconds(1)); + if (lStatus == NO_ERROR) { + LOGV("track stop complete, time %d", (unsigned int)(systemTime()/1000000)); + } else { + LOGE("--- Stop timed out"); + mState = TONE_IDLE; + mpAudioTrack->stop(); + } + } + + clearWaveGens(); + + mLock.unlock(); +} + +//---------------------------------- private methods --------------------------- + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// Method: ToneGenerator::initAudioTrack() +// +// Description: Allocates and configures AudioTrack used for PCM output. +// +// Input: +// none +// +// Output: +// none +// +//////////////////////////////////////////////////////////////////////////////// +bool ToneGenerator::initAudioTrack() { + + if (mpAudioTrack) { + delete mpAudioTrack; + mpAudioTrack = 0; + } + + // Open audio track in mono, PCM 16bit, default sampling rate, default buffer size + mpAudioTrack + = new AudioTrack(mStreamType, 0, AudioSystem::PCM_16_BIT, 1, 0, 0, audioCallback, this, 0); + + if (mpAudioTrack == 0) { + LOGE("AudioTrack allocation failed"); + goto initAudioTrack_exit; + } + LOGV("Create Track: %p\n", mpAudioTrack); + + if (mpAudioTrack->initCheck() != NO_ERROR) { + LOGE("AudioTrack->initCheck failed"); + goto initAudioTrack_exit; + } + + mpAudioTrack->setVolume(mVolume, mVolume); + + mState = TONE_INIT; + + return true; + +initAudioTrack_exit: + + // Cleanup + if (mpAudioTrack) { + LOGV("Delete Track I: %p\n", mpAudioTrack); + delete mpAudioTrack; + mpAudioTrack = 0; + } + + return false; +} + + +//////////////////////////////////////////////////////////////////////////////// +// +// Method: ToneGenerator::audioCallback() +// +// Description: AudioTrack callback implementation. Generates a block of +// PCM samples +// and manages tone generator sequencer: tones pulses, tone duration... +// +// Input: +// user reference (pointer to our ToneGenerator) +// info audio buffer descriptor +// +// Output: +// returned value: always true. +// +//////////////////////////////////////////////////////////////////////////////// +void ToneGenerator::audioCallback(int event, void* user, void *info) { + + if (event != AudioTrack::EVENT_MORE_DATA) return; + + const AudioTrack::Buffer *buffer = static_cast(info); + ToneGenerator *lpToneGen = static_cast(user); + short *lpOut = buffer->i16; + unsigned int lNumSmp = buffer->size/sizeof(short); + + if (buffer->size == 0) return; + + + // Clear output buffer: WaveGenerator accumulates into lpOut buffer + memset(lpOut, 0, buffer->size); + + while (lNumSmp) { + unsigned int lReqSmp = lNumSmp < lpToneGen->mProcessSize*2 ? lNumSmp : lpToneGen->mProcessSize; + unsigned int lGenSmp; + unsigned int lWaveCmd = WaveGenerator::WAVEGEN_CONT; + bool lSignal = false; + + lpToneGen->mLock.lock(); + + // Update pcm frame count and end time (current time at the end of this process) + lpToneGen->mTotalSmp += lReqSmp; + + // Update tone gen state machine and select wave gen command + switch (lpToneGen->mState) { + case TONE_PLAYING: + lWaveCmd = WaveGenerator::WAVEGEN_CONT; + break; + case TONE_STARTING: + LOGV("Starting Cbk"); + + lWaveCmd = WaveGenerator::WAVEGEN_START; + break; + case TONE_STOPPING: + case TONE_RESTARTING: + LOGV("Stop/restart Cbk"); + + lWaveCmd = WaveGenerator::WAVEGEN_STOP; + lpToneGen->mNextSegSmp = TONEGEN_INF; // forced to skip state machine management below + break; + default: + LOGV("Extra Cbk"); + goto audioCallback_EndLoop; + } + + + // Exit if tone sequence is over + if (lpToneGen->mpToneDesc->segments[lpToneGen->mCurSegment] == 0) { + if (lpToneGen->mState == TONE_PLAYING) { + lpToneGen->mState = TONE_STOPPING; + } + goto audioCallback_EndLoop; + } + + if (lpToneGen->mTotalSmp > lpToneGen->mNextSegSmp) { + // Time to go to next sequence segment + + LOGV("End Segment, time: %d\n", (unsigned int)(systemTime()/1000000)); + + lGenSmp = lReqSmp; + + if (lpToneGen->mCurSegment & 0x0001) { + // If odd segment, OFF -> ON transition : reset wave generator + lWaveCmd = WaveGenerator::WAVEGEN_START; + + LOGV("OFF->ON, lGenSmp: %d, lReqSmp: %d\n", lGenSmp, lReqSmp); + } else { + // If even segment, ON -> OFF transition : ramp volume down + lWaveCmd = WaveGenerator::WAVEGEN_STOP; + + LOGV("ON->OFF, lGenSmp: %d, lReqSmp: %d\n", lGenSmp, lReqSmp); + } + + // Pre increment segment index and handle loop if last segment reached + if (lpToneGen->mpToneDesc->segments[++lpToneGen->mCurSegment] == 0) { + LOGV("Last Seg: %d\n", lpToneGen->mCurSegment); + + // Pre increment loop count and restart if total count not reached. Stop sequence otherwise + if (++lpToneGen->mCurCount <= lpToneGen->mpToneDesc->repeatCnt) { + LOGV("Repeating Count: %d\n", lpToneGen->mCurCount); + + lpToneGen->mCurSegment = 0; + + LOGV("New segment %d, Next Time: %d\n", lpToneGen->mCurSegment, + (lpToneGen->mNextSegSmp*1000)/lpToneGen->mSamplingRate); + + } else { + LOGV("End repeat, time: %d\n", (unsigned int)(systemTime()/1000000)); + + // Cancel OFF->ON transition in case previous segment tone state was OFF + if (!(lpToneGen->mCurSegment & 0x0001)) { + lGenSmp = 0; + } + } + } else { + LOGV("New segment %d, Next Time: %d\n", lpToneGen->mCurSegment, + (lpToneGen->mNextSegSmp*1000)/lpToneGen->mSamplingRate); + } + + // Update next segment transition position. No harm to do it also for last segment as lpToneGen->mNextSegSmp won't be used any more + lpToneGen->mNextSegSmp + += (lpToneGen->mpToneDesc->segments[lpToneGen->mCurSegment] * lpToneGen->mSamplingRate) / 1000; + + } else { + // Inside a segment keep tone ON or OFF + if (lpToneGen->mCurSegment & 0x0001) { + lGenSmp = 0; // If odd segment, tone is currently OFF + } else { + lGenSmp = lReqSmp; // If event segment, tone is currently ON + } + } + + if (lGenSmp) { + // If samples must be generated, call all active wave generators and acumulate waves in lpOut + unsigned int lWaveIdx; + + for (lWaveIdx = 0; lWaveIdx < (unsigned int)lpToneGen->mWaveGens.size(); lWaveIdx++) { + WaveGenerator *lpWaveGen = lpToneGen->mWaveGens[lWaveIdx]; + lpWaveGen->getSamples(lpOut, lGenSmp, lWaveCmd); + } + } + + lNumSmp -= lReqSmp; + lpOut += lReqSmp; + +audioCallback_EndLoop: + + switch (lpToneGen->mState) { + case TONE_RESTARTING: + LOGV("Cbk restarting track\n"); + if (lpToneGen->prepareWave()) { + lpToneGen->mState = TONE_STARTING; + } else { + LOGW("Cbk restarting prepareWave() failed\n"); + lpToneGen->mState = TONE_IDLE; + lpToneGen->mpAudioTrack->stop(); + // Force loop exit + lNumSmp = 0; + } + lSignal = true; + break; + case TONE_STOPPING: + lpToneGen->mState = TONE_INIT; + LOGV("Cbk Stopping track\n"); + lSignal = true; + lpToneGen->mpAudioTrack->stop(); + + // Force loop exit + lNumSmp = 0; + break; + case TONE_STARTING: + LOGV("Cbk starting track\n"); + lpToneGen->mState = TONE_PLAYING; + lSignal = true; + break; + default: + break; + } + + if (lSignal) + lpToneGen->mWaitCbkCond.signal(); + lpToneGen->mLock.unlock(); + } +} + + +//////////////////////////////////////////////////////////////////////////////// +// +// Method: ToneGenerator::prepareWave() +// +// Description: Prepare wave generators and reset tone sequencer state machine. +// mpNewToneDesc must have been initialized befoire calling this function. +// Input: +// none +// +// Output: +// returned value: true if wave generators have been created, false otherwise +// +//////////////////////////////////////////////////////////////////////////////// +bool ToneGenerator::prepareWave() { + unsigned int lCnt = 0; + unsigned int lNumWaves; + + if (!mpNewToneDesc) { + return false; + } + // Remove existing wave generators if any + clearWaveGens(); + + mpToneDesc = mpNewToneDesc; + + // Get total number of sine waves: needed to adapt sine wave gain. + lNumWaves = numWaves(); + + // Instantiate as many wave generators as listed in descriptor + while (lCnt < lNumWaves) { + ToneGenerator::WaveGenerator *lpWaveGen = + new ToneGenerator::WaveGenerator((unsigned short)mSamplingRate, + mpToneDesc->waveFreq[lCnt], + TONEGEN_GAIN/lNumWaves); + if (lpWaveGen == 0) { + goto prepareWave_exit; + } + + mWaveGens.push(lpWaveGen); + LOGV("Create sine: %d\n", mpToneDesc->waveFreq[lCnt]); + lCnt++; + } + + // Initialize tone sequencer + mTotalSmp = 0; + mCurSegment = 0; + mCurCount = 0; + mNextSegSmp = (mpToneDesc->segments[0] * mSamplingRate) / 1000; + + return true; + +prepareWave_exit: + + clearWaveGens(); + + return false; +} + + +//////////////////////////////////////////////////////////////////////////////// +// +// Method: ToneGenerator::numWaves() +// +// Description: Count number of sine waves needed to generate tone (e.g 2 for DTMF). +// +// Input: +// none +// +// Output: +// returned value: nummber of sine waves +// +//////////////////////////////////////////////////////////////////////////////// +unsigned int ToneGenerator::numWaves() { + unsigned int lCnt = 0; + + while (mpToneDesc->waveFreq[lCnt]) { + lCnt++; + } + + return lCnt; +} + + +//////////////////////////////////////////////////////////////////////////////// +// +// Method: ToneGenerator::clearWaveGens() +// +// Description: Removes all wave generators. +// +// Input: +// none +// +// Output: +// none +// +//////////////////////////////////////////////////////////////////////////////// +void ToneGenerator::clearWaveGens() { + LOGV("Clearing mWaveGens:"); + + while (!mWaveGens.isEmpty()) { + delete mWaveGens.top(); + mWaveGens.pop(); + } +} + + +//////////////////////////////////////////////////////////////////////////////// +// WaveGenerator::WaveGenerator class Implementation +//////////////////////////////////////////////////////////////////////////////// + +//---------------------------------- public methods ---------------------------- + +//////////////////////////////////////////////////////////////////////////////// +// +// Method: WaveGenerator::WaveGenerator() +// +// Description: Constructor. +// +// Input: +// samplingRate: Output sampling rate in Hz +// frequency: Frequency of the sine wave to generate in Hz +// volume: volume (0.0 to 1.0) +// +// Output: +// none +// +//////////////////////////////////////////////////////////////////////////////// +ToneGenerator::WaveGenerator::WaveGenerator(unsigned short samplingRate, + unsigned short frequency, float volume) { + double d0; + double F_div_Fs; // frequency / samplingRate + + F_div_Fs = frequency / (double)samplingRate; + d0 = - (float)GEN_AMP * sin(2 * M_PI * F_div_Fs); + mS2_0 = (short)d0; + mS1 = 0; + mS2 = mS2_0; + + mAmplitude_Q15 = (short)(32767. * 32767. * volume / GEN_AMP); + // take some margin for amplitude fluctuation + if (mAmplitude_Q15 > 32500) + mAmplitude_Q15 = 32500; + + d0 = 32768.0 * cos(2 * M_PI * F_div_Fs); // Q14*2*cos() + if (d0 > 32767) + d0 = 32767; + mA1_Q14 = (short) d0; + + LOGV("WaveGenerator init, mA1_Q14: %d, mS2_0: %d, mAmplitude_Q15: %d\n", + mA1_Q14, mS2_0, mAmplitude_Q15); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Method: WaveGenerator::~WaveGenerator() +// +// Description: Destructor. +// +// Input: +// none +// +// Output: +// none +// +//////////////////////////////////////////////////////////////////////////////// +ToneGenerator::WaveGenerator::~WaveGenerator() { +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Method: WaveGenerator::getSamples() +// +// Description: Generates count samples of a sine wave and accumulates +// result in outBuffer. +// +// Input: +// outBuffer: Output buffer where to accumulate samples. +// count: number of samples to produce. +// command: special action requested (see enum gen_command). +// +// Output: +// none +// +//////////////////////////////////////////////////////////////////////////////// +void ToneGenerator::WaveGenerator::getSamples(short *outBuffer, + unsigned int count, unsigned int command) { + long lS1, lS2; + long lA1, lAmplitude; + long Sample; // current sample + + // init local + if (command == WAVEGEN_START) { + lS1 = (long)0; + lS2 = (long)mS2_0; + } else { + lS1 = (long)mS1; + lS2 = (long)mS2; + } + lA1 = (long)mA1_Q14; + lAmplitude = (long)mAmplitude_Q15; + + if (command == WAVEGEN_STOP) { + lAmplitude <<= 16; + if (count == 0) { + return; + } + long dec = lAmplitude/count; + // loop generation + while (count--) { + Sample = ((lA1 * lS1) >> S_Q14) - lS2; + // shift delay + lS2 = lS1; + lS1 = Sample; + Sample = ((lAmplitude>>16) * Sample) >> S_Q15; + *(outBuffer++) += (short)Sample; // put result in buffer + lAmplitude -= dec; + } + } else { + // loop generation + while (count--) { + Sample = ((lA1 * lS1) >> S_Q14) - lS2; + // shift delay + lS2 = lS1; + lS1 = Sample; + Sample = (lAmplitude * Sample) >> S_Q15; + *(outBuffer++) += (short)Sample; // put result in buffer + } + } + + // save status + mS1 = (short)lS1; + mS2 = (short)lS2; +} + +} // end namespace android + diff --git a/media/libmedia/mediametadataretriever.cpp b/media/libmedia/mediametadataretriever.cpp new file mode 100644 index 0000000..09afc6c --- /dev/null +++ b/media/libmedia/mediametadataretriever.cpp @@ -0,0 +1,188 @@ +/* +** +** Copyright 2008, 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 "MediaMetadataRetriever" + +#include +#include +#include +#include +#include +#include + +namespace android { + +// client singleton for binder interface to service +Mutex MediaMetadataRetriever::sServiceLock; +sp MediaMetadataRetriever::sService; +sp MediaMetadataRetriever::sDeathNotifier; + +const sp& MediaMetadataRetriever::getService() +{ + Mutex::Autolock lock(sServiceLock); + if (sService.get() == 0) { + sp sm = defaultServiceManager(); + sp binder; + do { + binder = sm->getService(String16("media.player")); + if (binder != 0) { + break; + } + LOGW("MediaPlayerService not published, waiting..."); + usleep(500000); // 0.5 s + } while(true); + if (sDeathNotifier == NULL) { + sDeathNotifier = new DeathNotifier(); + } + binder->linkToDeath(sDeathNotifier); + sService = interface_cast(binder); + } + LOGE_IF(sService == 0, "no MediaPlayerService!?"); + return sService; +} + +MediaMetadataRetriever::MediaMetadataRetriever() +{ + LOGV("constructor"); + const sp& service(getService()); + if (service == 0) { + LOGE("failed to obtain MediaMetadataRetrieverService"); + return; + } + sp retriever(service->createMetadataRetriever(getpid())); + if (retriever == 0) { + LOGE("failed to create IMediaMetadataRetriever object from server"); + } + mRetriever = retriever; +} + +MediaMetadataRetriever::~MediaMetadataRetriever() +{ + LOGV("destructor"); + disconnect(); + IPCThreadState::self()->flushCommands(); +} + +void MediaMetadataRetriever::disconnect() +{ + LOGV("disconnect"); + sp retriever; + { + Mutex::Autolock _l(mLock); + retriever = mRetriever; + mRetriever.clear(); + } + if (retriever != 0) { + retriever->disconnect(); + } +} + +status_t MediaMetadataRetriever::setDataSource(const char* srcUrl) +{ + LOGV("setDataSource"); + if (mRetriever == 0) { + LOGE("retriever is not initialized"); + return INVALID_OPERATION; + } + if (srcUrl == NULL) { + LOGE("data source is a null pointer"); + return UNKNOWN_ERROR; + } + LOGV("data source (%s)", srcUrl); + return mRetriever->setDataSource(srcUrl); +} + +status_t MediaMetadataRetriever::setDataSource(int fd, int64_t offset, int64_t length) +{ + LOGV("setDataSource(%d, %lld, %lld)", fd, offset, length); + if (mRetriever == 0) { + LOGE("retriever is not initialized"); + return INVALID_OPERATION; + } + if (fd < 0 || offset < 0 || length < 0) { + LOGE("Invalid negative argument"); + return UNKNOWN_ERROR; + } + return mRetriever->setDataSource(fd, offset, length); +} + +status_t MediaMetadataRetriever::setMode(int mode) +{ + LOGV("setMode(%d)", mode); + if (mRetriever == 0) { + LOGE("retriever is not initialized"); + return INVALID_OPERATION; + } + return mRetriever->setMode(mode); +} + +status_t MediaMetadataRetriever::getMode(int* mode) +{ + LOGV("getMode"); + if (mRetriever == 0) { + LOGE("retriever is not initialized"); + return INVALID_OPERATION; + } + return mRetriever->getMode(mode); +} + +sp MediaMetadataRetriever::captureFrame() +{ + LOGV("captureFrame"); + if (mRetriever == 0) { + LOGE("retriever is not initialized"); + return NULL; + } + return mRetriever->captureFrame(); +} + +const char* MediaMetadataRetriever::extractMetadata(int keyCode) +{ + LOGV("extractMetadata(%d)", keyCode); + if (mRetriever == 0) { + LOGE("retriever is not initialized"); + return NULL; + } + return mRetriever->extractMetadata(keyCode); +} + +sp MediaMetadataRetriever::extractAlbumArt() +{ + LOGV("extractAlbumArt"); + if (mRetriever == 0) { + LOGE("retriever is not initialized"); + return NULL; + } + return mRetriever->extractAlbumArt(); +} + +void MediaMetadataRetriever::DeathNotifier::binderDied(const wp& who) { + Mutex::Autolock lock(MediaMetadataRetriever::sServiceLock); + MediaMetadataRetriever::sService.clear(); + LOGW("MediaMetadataRetriever server died!"); +} + +MediaMetadataRetriever::DeathNotifier::~DeathNotifier() +{ + Mutex::Autolock lock(sServiceLock); + if (sService != 0) { + sService->asBinder()->unlinkToDeath(this); + } +} + +}; // namespace android diff --git a/media/libmedia/mediaplayer.cpp b/media/libmedia/mediaplayer.cpp new file mode 100644 index 0000000..bd8579c --- /dev/null +++ b/media/libmedia/mediaplayer.cpp @@ -0,0 +1,624 @@ +/* mediaplayer.cpp +** +** Copyright 2006, 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 "MediaPlayer" +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include + +namespace android { + +// client singleton for binder interface to service +Mutex MediaPlayer::sServiceLock; +sp MediaPlayer::sMediaPlayerService; +sp MediaPlayer::sDeathNotifier; +SortedVector< wp > MediaPlayer::sObitRecipients; + +// establish binder interface to service +const sp& MediaPlayer::getMediaPlayerService() +{ + Mutex::Autolock _l(sServiceLock); + if (sMediaPlayerService.get() == 0) { + sp sm = defaultServiceManager(); + sp binder; + do { + binder = sm->getService(String16("media.player")); + if (binder != 0) + break; + LOGW("MediaPlayerService not published, waiting..."); + usleep(500000); // 0.5 s + } while(true); + if (sDeathNotifier == NULL) { + sDeathNotifier = new DeathNotifier(); + } + binder->linkToDeath(sDeathNotifier); + sMediaPlayerService = interface_cast(binder); + } + LOGE_IF(sMediaPlayerService==0, "no MediaPlayerService!?"); + return sMediaPlayerService; +} + +void MediaPlayer::addObitRecipient(const wp& recipient) +{ + Mutex::Autolock _l(sServiceLock); + sObitRecipients.add(recipient); +} + +void MediaPlayer::removeObitRecipient(const wp& recipient) +{ + Mutex::Autolock _l(sServiceLock); + sObitRecipients.remove(recipient); +} + +MediaPlayer::MediaPlayer() +{ + LOGV("constructor"); + mListener = NULL; + mCookie = NULL; + mDuration = -1; + mStreamType = AudioSystem::MUSIC; + mCurrentPosition = -1; + mSeekPosition = -1; + mCurrentState = MEDIA_PLAYER_IDLE; + mPrepareSync = false; + mPrepareStatus = NO_ERROR; + mLoop = false; + mLeftVolume = mRightVolume = 1.0; + mVideoWidth = mVideoHeight = 0; +} + +void MediaPlayer::onFirstRef() +{ + addObitRecipient(this); +} + +MediaPlayer::~MediaPlayer() +{ + LOGV("destructor"); + removeObitRecipient(this); + disconnect(); + IPCThreadState::self()->flushCommands(); +} + +void MediaPlayer::disconnect() +{ + LOGV("disconnect"); + sp p; + { + Mutex::Autolock _l(mLock); + p = mPlayer; + mPlayer.clear(); + } + + if (p != 0) { + p->disconnect(); + } +} + +// always call with lock held +void MediaPlayer::clear_l() +{ + mDuration = -1; + mCurrentPosition = -1; + mSeekPosition = -1; + mVideoWidth = mVideoHeight = 0; +} + +status_t MediaPlayer::setListener(const sp& listener) +{ + LOGV("setListener"); + Mutex::Autolock _l(mLock); + mListener = listener; + return NO_ERROR; +} + + +status_t MediaPlayer::setDataSource(const sp& player) +{ + status_t err = UNKNOWN_ERROR; + sp p; + { // scope for the lock + Mutex::Autolock _l(mLock); + + if ( !( mCurrentState & ( MEDIA_PLAYER_IDLE | MEDIA_PLAYER_STATE_ERROR ) ) ) { + LOGE("setDataSource called in state %d", mCurrentState); + return INVALID_OPERATION; + } + + clear_l(); + p = mPlayer; + mPlayer = player; + if (player != 0) { + mCurrentState = MEDIA_PLAYER_INITIALIZED; + err = NO_ERROR; + } else { + LOGE("Unable to to create media player"); + } + } + + if (p != 0) { + p->disconnect(); + } + + return err; +} + +status_t MediaPlayer::setDataSource(const char *url) +{ + LOGV("setDataSource(%s)", url); + status_t err = BAD_VALUE; + if (url != NULL) { + const sp& service(getMediaPlayerService()); + if (service != 0) { + sp player(service->create(getpid(), this, url)); + err = setDataSource(player); + } + } + return err; +} + +status_t MediaPlayer::setDataSource(int fd, int64_t offset, int64_t length) +{ + LOGV("setDataSource(%d, %lld, %lld)", fd, offset, length); + status_t err = UNKNOWN_ERROR; + const sp& service(getMediaPlayerService()); + if (service != 0) { + sp player(service->create(getpid(), this, fd, offset, length)); + err = setDataSource(player); + } + return err; +} + +status_t MediaPlayer::setVideoSurface(const sp& surface) +{ + LOGV("setVideoSurface"); + Mutex::Autolock _l(mLock); + if (mPlayer == 0) return NO_INIT; + return mPlayer->setVideoSurface(surface->getISurface()); +} + +// must call with lock held +status_t MediaPlayer::prepareAsync_l() +{ + if ( (mPlayer != 0) && ( mCurrentState & ( MEDIA_PLAYER_INITIALIZED | MEDIA_PLAYER_STOPPED) ) ) { + mPlayer->setAudioStreamType(mStreamType); + mCurrentState = MEDIA_PLAYER_PREPARING; + return mPlayer->prepareAsync(); + } + LOGE("prepareAsync called in state %d", mCurrentState); + return INVALID_OPERATION; +} + +status_t MediaPlayer::prepare() +{ + LOGV("prepare"); + Mutex::Autolock _l(mLock); + if (mPrepareSync) return -EALREADY; + mPrepareSync = true; + status_t ret = prepareAsync_l(); + if (ret != NO_ERROR) return ret; + + if (mPrepareSync) { + mSignal.wait(mLock); // wait for prepare done + mPrepareSync = false; + } + LOGV("prepare complete - status=%d", mPrepareStatus); + return mPrepareStatus; +} + +status_t MediaPlayer::prepareAsync() +{ + LOGV("prepareAsync"); + Mutex::Autolock _l(mLock); + return prepareAsync_l(); +} + +status_t MediaPlayer::start() +{ + LOGV("start"); + Mutex::Autolock _l(mLock); + if (mCurrentState & MEDIA_PLAYER_STARTED) + return NO_ERROR; + if ( (mPlayer != 0) && ( mCurrentState & ( MEDIA_PLAYER_PREPARED | + MEDIA_PLAYER_PLAYBACK_COMPLETE | MEDIA_PLAYER_PAUSED ) ) ) { + mPlayer->setLooping(mLoop); + mPlayer->setVolume(mLeftVolume, mRightVolume); + mCurrentState = MEDIA_PLAYER_STARTED; + status_t ret = mPlayer->start(); + if (ret != NO_ERROR) { + mCurrentState = MEDIA_PLAYER_STATE_ERROR; + } else { + if (mCurrentState == MEDIA_PLAYER_PLAYBACK_COMPLETE) { + LOGV("playback completed immediately following start()"); + } + } + return ret; + } + LOGE("start called in state %d", mCurrentState); + return INVALID_OPERATION; +} + +status_t MediaPlayer::stop() +{ + LOGV("stop"); + Mutex::Autolock _l(mLock); + if (mCurrentState & MEDIA_PLAYER_STOPPED) return NO_ERROR; + if ( (mPlayer != 0) && ( mCurrentState & ( MEDIA_PLAYER_STARTED | MEDIA_PLAYER_PREPARED | + MEDIA_PLAYER_PAUSED | MEDIA_PLAYER_PLAYBACK_COMPLETE ) ) ) { + status_t ret = mPlayer->stop(); + if (ret != NO_ERROR) { + mCurrentState = MEDIA_PLAYER_STATE_ERROR; + } else { + mCurrentState = MEDIA_PLAYER_STOPPED; + } + return ret; + } + LOGE("stop called in state %d", mCurrentState); + return INVALID_OPERATION; +} + +status_t MediaPlayer::pause() +{ + LOGV("pause"); + Mutex::Autolock _l(mLock); + if (mCurrentState & MEDIA_PLAYER_PAUSED) + return NO_ERROR; + if ((mPlayer != 0) && (mCurrentState & MEDIA_PLAYER_STARTED)) { + status_t ret = mPlayer->pause(); + if (ret != NO_ERROR) { + mCurrentState = MEDIA_PLAYER_STATE_ERROR; + } else { + mCurrentState = MEDIA_PLAYER_PAUSED; + } + return ret; + } + LOGE("pause called in state %d", mCurrentState); + return INVALID_OPERATION; +} + +bool MediaPlayer::isPlaying() +{ + Mutex::Autolock _l(mLock); + if (mPlayer != 0) { + bool temp = false; + mPlayer->isPlaying(&temp); + LOGV("isPlaying: %d", temp); + if ((mCurrentState & MEDIA_PLAYER_STARTED) && ! temp) { + LOGE("internal/external state mismatch corrected"); + mCurrentState = MEDIA_PLAYER_PAUSED; + } + return temp; + } + LOGV("isPlaying: no active player"); + return false; +} + +status_t MediaPlayer::getVideoWidth(int *w) +{ + LOGV("getVideoWidth"); + Mutex::Autolock _l(mLock); + if (mPlayer == 0) return INVALID_OPERATION; + *w = mVideoWidth; + return NO_ERROR; +} + +status_t MediaPlayer::getVideoHeight(int *h) +{ + LOGV("getVideoHeight"); + Mutex::Autolock _l(mLock); + if (mPlayer == 0) return INVALID_OPERATION; + *h = mVideoHeight; + return NO_ERROR; +} + +status_t MediaPlayer::getCurrentPosition(int *msec) +{ + LOGV("getCurrentPosition"); + Mutex::Autolock _l(mLock); + if (mPlayer != 0) { + if (mCurrentPosition >= 0) { + LOGV("Using cached seek position: %d", mCurrentPosition); + *msec = mCurrentPosition; + return NO_ERROR; + } + return mPlayer->getCurrentPosition(msec); + } + return INVALID_OPERATION; +} + +status_t MediaPlayer::getDuration_l(int *msec) +{ + LOGV("getDuration"); + bool isValidState = (mCurrentState & (MEDIA_PLAYER_PREPARED | MEDIA_PLAYER_STARTED | MEDIA_PLAYER_PAUSED | MEDIA_PLAYER_STOPPED | MEDIA_PLAYER_PLAYBACK_COMPLETE)); + if (mPlayer != 0 && isValidState) { + status_t ret = NO_ERROR; + if (mDuration <= 0) + ret = mPlayer->getDuration(&mDuration); + if (msec) + *msec = mDuration; + return ret; + } + LOGE("Attempt to call getDuration without a valid mediaplayer"); + return INVALID_OPERATION; +} + +status_t MediaPlayer::getDuration(int *msec) +{ + Mutex::Autolock _l(mLock); + return getDuration_l(msec); +} + +status_t MediaPlayer::seekTo_l(int msec) +{ + LOGV("seekTo %d", msec); + if ((mPlayer != 0) && ( mCurrentState & ( MEDIA_PLAYER_STARTED | MEDIA_PLAYER_PREPARED | MEDIA_PLAYER_PAUSED | MEDIA_PLAYER_PLAYBACK_COMPLETE) ) ) { + if ( msec < 0 ) { + LOGW("Attempt to seek to invalid position: %d", msec); + msec = 0; + } else if ((mDuration > 0) && (msec > mDuration)) { + LOGW("Attempt to seek to past end of file: request = %d, EOF = %d", msec, mDuration); + msec = mDuration; + } + // cache duration + mCurrentPosition = msec; + if (mSeekPosition < 0) { + getDuration_l(NULL); + mSeekPosition = msec; + return mPlayer->seekTo(msec); + } + else { + LOGV("Seek in progress - queue up seekTo[%d]", msec); + return NO_ERROR; + } + } + LOGE("Attempt to perform seekTo in wrong state: mPlayer=%p, mCurrentState=%u", mPlayer.get(), mCurrentState); + return INVALID_OPERATION; +} + +status_t MediaPlayer::seekTo(int msec) +{ + Mutex::Autolock _l(mLock); + return seekTo_l(msec); +} + +status_t MediaPlayer::reset() +{ + LOGV("reset"); + Mutex::Autolock _l(mLock); + mLoop = false; + if (mCurrentState == MEDIA_PLAYER_IDLE) return NO_ERROR; + mPrepareSync = false; + if (mPlayer != 0) { + status_t ret = mPlayer->reset(); + if (ret != NO_ERROR) { + LOGE("reset() failed with return code (%d)", ret); + mCurrentState = MEDIA_PLAYER_STATE_ERROR; + } else { + mCurrentState = MEDIA_PLAYER_IDLE; + } + return ret; + } + clear_l(); + return NO_ERROR; +} + +status_t MediaPlayer::setAudioStreamType(int type) +{ + LOGV("MediaPlayer::setAudioStreamType"); + Mutex::Autolock _l(mLock); + if (mStreamType == type) return NO_ERROR; + if (mCurrentState & ( MEDIA_PLAYER_PREPARED | MEDIA_PLAYER_STARTED | + MEDIA_PLAYER_PAUSED | MEDIA_PLAYER_PLAYBACK_COMPLETE ) ) { + // Can't change the stream type after prepare + LOGE("setAudioStream called in state %d", mCurrentState); + return INVALID_OPERATION; + } + // cache + mStreamType = type; + return OK; +} + +status_t MediaPlayer::setLooping(int loop) +{ + LOGV("MediaPlayer::setLooping"); + Mutex::Autolock _l(mLock); + mLoop = (loop != 0); + if (mPlayer != 0) { + return mPlayer->setLooping(loop); + } + return OK; +} + +bool MediaPlayer::isLooping() { + LOGV("isLooping"); + Mutex::Autolock _l(mLock); + if (mPlayer != 0) { + return mLoop; + } + LOGV("isLooping: no active player"); + return false; +} + +status_t MediaPlayer::setVolume(float leftVolume, float rightVolume) +{ + LOGV("MediaPlayer::setVolume(%f, %f)", leftVolume, rightVolume); + Mutex::Autolock _l(mLock); + mLeftVolume = leftVolume; + mRightVolume = rightVolume; + if (mPlayer != 0) { + return mPlayer->setVolume(leftVolume, rightVolume); + } + return OK; +} + +void MediaPlayer::notify(int msg, int ext1, int ext2) +{ + LOGV("message received msg=%d, ext1=%d, ext2=%d", msg, ext1, ext2); + bool send = true; + + // TODO: In the future, we might be on the same thread if the app is + // running in the same process as the media server. In that case, + // this will deadlock. + mLock.lock(); + if (mPlayer == 0) { + LOGV("notify(%d, %d, %d) callback on disconnected mediaplayer", msg, ext1, ext2); + mLock.unlock(); // release the lock when done. + return; + } + + switch (msg) { + case MEDIA_NOP: // interface test message + break; + case MEDIA_PREPARED: + LOGV("prepared"); + mCurrentState = MEDIA_PLAYER_PREPARED; + if (mPrepareSync) { + LOGV("signal application thread"); + mPrepareSync = false; + mPrepareStatus = NO_ERROR; + mSignal.signal(); + } + break; + case MEDIA_PLAYBACK_COMPLETE: + LOGV("playback complete"); + if (!mLoop) { + mCurrentState = MEDIA_PLAYER_PLAYBACK_COMPLETE; + } + break; + case MEDIA_ERROR: + // Always log errors + LOGE("error (%d, %d)", ext1, ext2); + mCurrentState = MEDIA_PLAYER_STATE_ERROR; + if (mPrepareSync) + { + LOGV("signal application thread"); + mPrepareSync = false; + mPrepareStatus = ext1; + mSignal.signal(); + send = false; + } + break; + case MEDIA_SEEK_COMPLETE: + LOGV("Received seek complete"); + if (mSeekPosition != mCurrentPosition) { + LOGV("Executing queued seekTo(%d)", mSeekPosition); + mSeekPosition = -1; + seekTo_l(mCurrentPosition); + } + else { + LOGV("All seeks complete - return to regularly scheduled program"); + mCurrentPosition = mSeekPosition = -1; + } + break; + case MEDIA_BUFFERING_UPDATE: + LOGV("buffering %d", ext1); + break; + case MEDIA_SET_VIDEO_SIZE: + LOGV("New video size %d x %d", ext1, ext2); + mVideoWidth = ext1; + mVideoHeight = ext2; + break; + default: + LOGV("unrecognized message: (%d, %d, %d)", msg, ext1, ext2); + break; + } + + sp listener = mListener; + mLock.unlock(); + + // this prevents re-entrant calls into client code + if ((listener != 0) && send) { + Mutex::Autolock _l(mNotifyLock); + LOGV("callback application"); + listener->notify(msg, ext1, ext2); + LOGV("back from callback"); + } +} + +void MediaPlayer::DeathNotifier::binderDied(const wp& who) { + LOGW("MediaPlayer server died!"); + + // Need to do this with the lock held + SortedVector< wp > list; + { + Mutex::Autolock _l(MediaPlayer::sServiceLock); + MediaPlayer::sMediaPlayerService.clear(); + list = sObitRecipients; + } + + // Notify application when media server dies. + // Don't hold the static lock during callback in case app + // makes a call that needs the lock. + size_t count = list.size(); + for (size_t iter = 0; iter < count; ++iter) { + sp player = list[iter].promote(); + if ((player != 0) && (player->mPlayer != 0)) { + player->notify(MEDIA_ERROR, MEDIA_ERROR_SERVER_DIED, 0); + } + } +} + +MediaPlayer::DeathNotifier::~DeathNotifier() +{ + Mutex::Autolock _l(sServiceLock); + sObitRecipients.clear(); + if (sMediaPlayerService != 0) { + sMediaPlayerService->asBinder()->unlinkToDeath(this); + } +} + +/*static*/ sp MediaPlayer::decode(const char* url, uint32_t *pSampleRate, int* pNumChannels, int* pFormat) +{ + LOGV("decode(%s)", url); + sp p; + const sp& service = getMediaPlayerService(); + if (service != 0) { + p = sMediaPlayerService->decode(url, pSampleRate, pNumChannels, pFormat); + } else { + LOGE("Unable to locate media service"); + } + return p; + +} + +/*static*/ sp MediaPlayer::decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels, int* pFormat) +{ + LOGV("decode(%d, %lld, %lld)", fd, offset, length); + sp p; + const sp& service = getMediaPlayerService(); + if (service != 0) { + p = sMediaPlayerService->decode(fd, offset, length, pSampleRate, pNumChannels, pFormat); + } else { + LOGE("Unable to locate media service"); + } + return p; + +} + +}; // namespace android diff --git a/media/libmedia/mediarecorder.cpp b/media/libmedia/mediarecorder.cpp new file mode 100644 index 0000000..98aac39 --- /dev/null +++ b/media/libmedia/mediarecorder.cpp @@ -0,0 +1,617 @@ +/* + ** + ** Copyright (c) 2008 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 "MediaRecorder" +#include +#include +#include +#include +#include +#include + +namespace android { + +status_t MediaRecorder::setCamera(const sp& camera) +{ + LOGV("setCamera(%p)", camera.get()); + if(mMediaRecorder == NULL) { + LOGE("media recorder is not initialized yet"); + return INVALID_OPERATION; + } + if (!(mCurrentState & MEDIA_RECORDER_IDLE)) { + LOGE("setCamera called in an invalid state(%d)", mCurrentState); + return INVALID_OPERATION; + } + + status_t ret = mMediaRecorder->setCamera(camera); + if (OK != ret) { + LOGV("setCamera failed: %d", ret); + mCurrentState = MEDIA_RECORDER_ERROR; + return UNKNOWN_ERROR; + } + return ret; +} + +status_t MediaRecorder::setPreviewSurface(const sp& surface) +{ + LOGV("setPreviewSurface(%p)", surface.get()); + if(mMediaRecorder == NULL) { + LOGE("media recorder is not initialized yet"); + return INVALID_OPERATION; + } + if (!(mCurrentState & MEDIA_RECORDER_DATASOURCE_CONFIGURED)) { + LOGE("setPreviewSurface called in an invalid state(%d)", mCurrentState); + return INVALID_OPERATION; + } + if (!mIsVideoSourceSet) { + LOGE("try to set preview surface without setting the video source first"); + return INVALID_OPERATION; + } + + status_t ret = mMediaRecorder->setPreviewSurface(surface->getISurface()); + if (OK != ret) { + LOGV("setPreviewSurface failed: %d", ret); + mCurrentState = MEDIA_RECORDER_ERROR; + return UNKNOWN_ERROR; + } + return ret; +} + +status_t MediaRecorder::init() +{ + LOGV("init"); + if(mMediaRecorder == NULL) { + LOGE("media recorder is not initialized yet"); + return INVALID_OPERATION; + } + if (!(mCurrentState & MEDIA_RECORDER_IDLE)) { + LOGE("init called in an invalid state(%d)", mCurrentState); + return INVALID_OPERATION; + } + + status_t ret = mMediaRecorder->init(); + if (OK != ret) { + LOGV("init failed: %d", ret); + mCurrentState = MEDIA_RECORDER_ERROR; + return UNKNOWN_ERROR; + } + + ret = mMediaRecorder->setListener(this); + if (OK != ret) { + LOGV("setListener failed: %d", ret); + mCurrentState = MEDIA_RECORDER_ERROR; + return UNKNOWN_ERROR; + } + + mCurrentState = MEDIA_RECORDER_INITIALIZED; + return ret; +} + +status_t MediaRecorder::setVideoSource(int vs) +{ + LOGV("setVideoSource(%d)", vs); + if(mMediaRecorder == NULL) { + LOGE("media recorder is not initialized yet"); + return INVALID_OPERATION; + } + if (mIsVideoSourceSet) { + LOGE("video source has already been set"); + return INVALID_OPERATION; + } + if (mCurrentState & MEDIA_RECORDER_IDLE) { + LOGV("Call init() since the media recorder is not initialized yet"); + status_t ret = init(); + if (OK != ret) { + return ret; + } + } + if (!(mCurrentState & MEDIA_RECORDER_INITIALIZED)) { + LOGE("setVideoSource called in an invalid state(%d)", mCurrentState); + return INVALID_OPERATION; + } + + status_t ret = mMediaRecorder->setVideoSource(vs); + if (OK != ret) { + LOGV("setVideoSource failed: %d", ret); + mCurrentState = MEDIA_RECORDER_ERROR; + return UNKNOWN_ERROR; + } + mIsVideoSourceSet = true; + return ret; +} + +status_t MediaRecorder::setAudioSource(int as) +{ + LOGV("setAudioSource(%d)", as); + if(mMediaRecorder == NULL) { + LOGE("media recorder is not initialized yet"); + return INVALID_OPERATION; + } + if (mCurrentState & MEDIA_RECORDER_IDLE) { + LOGV("Call init() since the media recorder is not initialized yet"); + status_t ret = init(); + if (OK != ret) { + return ret; + } + } + if (mIsAudioSourceSet) { + LOGE("audio source has already been set"); + return INVALID_OPERATION; + } + if (!(mCurrentState & MEDIA_RECORDER_INITIALIZED)) { + LOGE("setAudioSource called in an invalid state(%d)", mCurrentState); + return INVALID_OPERATION; + } + + status_t ret = mMediaRecorder->setAudioSource(as); + if (OK != ret) { + LOGV("setAudioSource failed: %d", ret); + mCurrentState = MEDIA_RECORDER_ERROR; + return UNKNOWN_ERROR; + } + mIsAudioSourceSet = true; + return ret; +} + +status_t MediaRecorder::setOutputFormat(int of) +{ + LOGV("setOutputFormat(%d)", of); + if(mMediaRecorder == NULL) { + LOGE("media recorder is not initialized yet"); + return INVALID_OPERATION; + } + if (!(mCurrentState & MEDIA_RECORDER_INITIALIZED)) { + LOGE("setOutputFormat called in an invalid state: %d", mCurrentState); + return INVALID_OPERATION; + } + if (mIsVideoSourceSet && of >= OUTPUT_FORMAT_RAW_AMR) { + LOGE("output format (%d) is meant for audio recording only and incompatible with video recording", of); + return INVALID_OPERATION; + } + + status_t ret = mMediaRecorder->setOutputFormat(of); + if (OK != ret) { + LOGE("setOutputFormat failed: %d", ret); + mCurrentState = MEDIA_RECORDER_ERROR; + return UNKNOWN_ERROR; + } + mCurrentState = MEDIA_RECORDER_DATASOURCE_CONFIGURED; + return ret; +} + +status_t MediaRecorder::setVideoEncoder(int ve) +{ + LOGV("setVideoEncoder(%d)", ve); + if(mMediaRecorder == NULL) { + LOGE("media recorder is not initialized yet"); + return INVALID_OPERATION; + } + if (!mIsVideoSourceSet) { + LOGE("try to set the video encoder without setting the video source first"); + return INVALID_OPERATION; + } + if (mIsVideoEncoderSet) { + LOGE("video encoder has already been set"); + return INVALID_OPERATION; + } + if (!(mCurrentState & MEDIA_RECORDER_DATASOURCE_CONFIGURED)) { + LOGE("setVideoEncoder called in an invalid state(%d)", mCurrentState); + return INVALID_OPERATION; + } + + status_t ret = mMediaRecorder->setVideoEncoder(ve); + if (OK != ret) { + LOGV("setVideoEncoder failed: %d", ret); + mCurrentState = MEDIA_RECORDER_ERROR; + return UNKNOWN_ERROR; + } + mIsVideoEncoderSet = true; + return ret; +} + +status_t MediaRecorder::setAudioEncoder(int ae) +{ + LOGV("setAudioEncoder(%d)", ae); + if(mMediaRecorder == NULL) { + LOGE("media recorder is not initialized yet"); + return INVALID_OPERATION; + } + if (!mIsAudioSourceSet) { + LOGE("try to set the audio encoder without setting the audio source first"); + return INVALID_OPERATION; + } + if (mIsAudioEncoderSet) { + LOGE("audio encoder has already been set"); + return INVALID_OPERATION; + } + if (!(mCurrentState & MEDIA_RECORDER_DATASOURCE_CONFIGURED)) { + LOGE("setAudioEncoder called in an invalid state(%d)", mCurrentState); + return INVALID_OPERATION; + } + + status_t ret = mMediaRecorder->setAudioEncoder(ae); + if (OK != ret) { + LOGV("setAudioEncoder failed: %d", ret); + mCurrentState = MEDIA_RECORDER_ERROR; + return UNKNOWN_ERROR; + } + mIsAudioEncoderSet = true; + return ret; +} + +status_t MediaRecorder::setOutputFile(const char* path) +{ + LOGV("setOutputFile(%s)", path); + if(mMediaRecorder == NULL) { + LOGE("media recorder is not initialized yet"); + return INVALID_OPERATION; + } + if (mIsOutputFileSet) { + LOGE("output file has already been set"); + return INVALID_OPERATION; + } + if (!(mCurrentState & MEDIA_RECORDER_DATASOURCE_CONFIGURED)) { + LOGE("setOutputFile called in an invalid state(%d)", mCurrentState); + return INVALID_OPERATION; + } + + status_t ret = mMediaRecorder->setOutputFile(path); + if (OK != ret) { + LOGV("setOutputFile failed: %d", ret); + mCurrentState = MEDIA_RECORDER_ERROR; + return UNKNOWN_ERROR; + } + mIsOutputFileSet = true; + return ret; +} + +status_t MediaRecorder::setOutputFile(int fd, int64_t offset, int64_t length) +{ + LOGV("setOutputFile(%d, %lld, %lld)", fd, offset, length); + if(mMediaRecorder == NULL) { + LOGE("media recorder is not initialized yet"); + return INVALID_OPERATION; + } + if (mIsOutputFileSet) { + LOGE("output file has already been set"); + return INVALID_OPERATION; + } + if (!(mCurrentState & MEDIA_RECORDER_DATASOURCE_CONFIGURED)) { + LOGE("setOutputFile called in an invalid state(%d)", mCurrentState); + return INVALID_OPERATION; + } + + status_t ret = mMediaRecorder->setOutputFile(fd, offset, length); + if (OK != ret) { + LOGV("setOutputFile failed: %d", ret); + mCurrentState = MEDIA_RECORDER_ERROR; + return UNKNOWN_ERROR; + } + mIsOutputFileSet = true; + return ret; +} + +status_t MediaRecorder::setVideoSize(int width, int height) +{ + LOGV("setVideoSize(%d, %d)", width, height); + if(mMediaRecorder == NULL) { + LOGE("media recorder is not initialized yet"); + return INVALID_OPERATION; + } + if (!(mCurrentState & MEDIA_RECORDER_DATASOURCE_CONFIGURED)) { + LOGE("setVideoSize called in an invalid state: %d", mCurrentState); + return INVALID_OPERATION; + } + if (!mIsVideoSourceSet) { + LOGE("try to set video size without setting video source first"); + return INVALID_OPERATION; + } + + status_t ret = mMediaRecorder->setVideoSize(width, height); + if (OK != ret) { + LOGE("setVideoSize failed: %d", ret); + mCurrentState = MEDIA_RECORDER_ERROR; + return UNKNOWN_ERROR; + } + return ret; +} + +status_t MediaRecorder::setVideoFrameRate(int frames_per_second) +{ + LOGV("setVideoFrameRate(%d)", frames_per_second); + if(mMediaRecorder == NULL) { + LOGE("media recorder is not initialized yet"); + return INVALID_OPERATION; + } + if (!(mCurrentState & MEDIA_RECORDER_DATASOURCE_CONFIGURED)) { + LOGE("setVideoFrameRate called in an invalid state: %d", mCurrentState); + return INVALID_OPERATION; + } + if (!mIsVideoSourceSet) { + LOGE("try to set video frame rate without setting video source first"); + return INVALID_OPERATION; + } + + status_t ret = mMediaRecorder->setVideoFrameRate(frames_per_second); + if (OK != ret) { + LOGE("setVideoFrameRate failed: %d", ret); + mCurrentState = MEDIA_RECORDER_ERROR; + return UNKNOWN_ERROR; + } + return ret; +} + +status_t MediaRecorder::prepare() +{ + LOGV("prepare"); + if(mMediaRecorder == NULL) { + LOGE("media recorder is not initialized yet"); + return INVALID_OPERATION; + } + if (!(mCurrentState & MEDIA_RECORDER_DATASOURCE_CONFIGURED)) { + LOGE("prepare called in an invalid state: %d", mCurrentState); + return INVALID_OPERATION; + } + if (mIsAudioSourceSet != mIsAudioEncoderSet) { + if (mIsAudioSourceSet) { + LOGE("audio source is set, but audio encoder is not set"); + } else { // must not happen, since setAudioEncoder checks this already + LOGE("audio encoder is set, but audio source is not set"); + } + return INVALID_OPERATION; + } + + if (mIsVideoSourceSet != mIsVideoEncoderSet) { + if (mIsVideoSourceSet) { + LOGE("video source is set, but video encoder is not set"); + } else { // must not happen, since setVideoEncoder checks this already + LOGE("video encoder is set, but video source is not set"); + } + return INVALID_OPERATION; + } + + status_t ret = mMediaRecorder->prepare(); + if (OK != ret) { + LOGE("prepare failed: %d", ret); + mCurrentState = MEDIA_RECORDER_ERROR; + return UNKNOWN_ERROR; + } + mCurrentState = MEDIA_RECORDER_PREPARED; + return ret; +} + +status_t MediaRecorder::getMaxAmplitude(int* max) +{ + LOGV("getMaxAmplitude"); + if(mMediaRecorder == NULL) { + LOGE("media recorder is not initialized yet"); + return INVALID_OPERATION; + } + if (mCurrentState & MEDIA_RECORDER_ERROR) { + LOGE("getMaxAmplitude called in an invalid state: %d", mCurrentState); + return INVALID_OPERATION; + } + + status_t ret = mMediaRecorder->getMaxAmplitude(max); + if (OK != ret) { + LOGE("getMaxAmplitude failed: %d", ret); + mCurrentState = MEDIA_RECORDER_ERROR; + return UNKNOWN_ERROR; + } + return ret; +} + +status_t MediaRecorder::start() +{ + LOGV("start"); + if (mMediaRecorder == NULL) { + LOGE("media recorder is not initialized yet"); + return INVALID_OPERATION; + } + if (!(mCurrentState & MEDIA_RECORDER_PREPARED)) { + LOGE("start called in an invalid state: %d", mCurrentState); + return INVALID_OPERATION; + } + + status_t ret = mMediaRecorder->start(); + if (OK != ret) { + LOGE("start failed: %d", ret); + mCurrentState = MEDIA_RECORDER_ERROR; + return UNKNOWN_ERROR; + } + mCurrentState = MEDIA_RECORDER_RECORDING; + return ret; +} + +status_t MediaRecorder::stop() +{ + LOGV("stop"); + if (mMediaRecorder == NULL) { + LOGE("media recorder is not initialized yet"); + return INVALID_OPERATION; + } + if (!(mCurrentState & MEDIA_RECORDER_RECORDING)) { + LOGE("stop called in an invalid state: %d", mCurrentState); + return INVALID_OPERATION; + } + + status_t ret = mMediaRecorder->stop(); + if (OK != ret) { + LOGE("stop failed: %d", ret); + mCurrentState = MEDIA_RECORDER_ERROR; + return UNKNOWN_ERROR; + } + mCurrentState = MEDIA_RECORDER_IDLE; + return ret; +} + +// Reset should be OK in any state +status_t MediaRecorder::reset() +{ + LOGV("reset"); + if (mMediaRecorder == NULL) { + LOGE("media recorder is not initialized yet"); + return INVALID_OPERATION; + } + + doCleanUp(); + status_t ret = UNKNOWN_ERROR; + switch(mCurrentState) { + case MEDIA_RECORDER_IDLE: + ret = OK; + break; + + case MEDIA_RECORDER_RECORDING: + case MEDIA_RECORDER_DATASOURCE_CONFIGURED: + case MEDIA_RECORDER_PREPARED: + case MEDIA_RECORDER_ERROR: { + ret = doReset(); + if (OK != ret) { + return ret; // No need to continue + } + } // Intentional fall through + case MEDIA_RECORDER_INITIALIZED: + ret = close(); + break; + + default: { + LOGE("Unexpected non-existing state: %d", mCurrentState); + break; + } + } + return ret; +} + +status_t MediaRecorder::close() +{ + LOGV("close"); + if (!(mCurrentState & MEDIA_RECORDER_INITIALIZED)) { + LOGE("close called in an invalid state: %d", mCurrentState); + return INVALID_OPERATION; + } + status_t ret = mMediaRecorder->close(); + if (OK != ret) { + LOGE("close failed: %d", ret); + mCurrentState = MEDIA_RECORDER_ERROR; + return UNKNOWN_ERROR; + } else { + mCurrentState = MEDIA_RECORDER_IDLE; + } + return ret; +} + +status_t MediaRecorder::doReset() +{ + LOGV("doReset"); + status_t ret = mMediaRecorder->reset(); + if (OK != ret) { + LOGE("doReset failed: %d", ret); + mCurrentState = MEDIA_RECORDER_ERROR; + return UNKNOWN_ERROR; + } else { + mCurrentState = MEDIA_RECORDER_INITIALIZED; + } + return ret; +} + +void MediaRecorder::doCleanUp() +{ + LOGV("doCleanUp"); + mIsAudioSourceSet = false; + mIsVideoSourceSet = false; + mIsAudioEncoderSet = false; + mIsVideoEncoderSet = false; + mIsOutputFileSet = false; +} + +// Release should be OK in any state +status_t MediaRecorder::release() +{ + LOGV("release"); + if (mMediaRecorder != NULL) { + return mMediaRecorder->release(); + } + return INVALID_OPERATION; +} + +MediaRecorder::MediaRecorder() +{ + LOGV("constructor"); + sp sm = defaultServiceManager(); + sp binder; + + do { + binder = sm->getService(String16("media.player")); + if (binder != NULL) { + break; + } + LOGW("MediaPlayerService not published, waiting..."); + usleep(500000); // 0.5 s + } while(true); + + sp service = interface_cast(binder); + if (service != NULL) { + mMediaRecorder = service->createMediaRecorder(getpid()); + } + if (mMediaRecorder != NULL) { + mCurrentState = MEDIA_RECORDER_IDLE; + } + doCleanUp(); +} + +status_t MediaRecorder::initCheck() +{ + return mMediaRecorder != 0 ? NO_ERROR : NO_INIT; +} + +MediaRecorder::~MediaRecorder() +{ + LOGV("destructor"); + if (mMediaRecorder != NULL) { + mMediaRecorder.clear(); + } +} + +status_t MediaRecorder::setListener(const sp& listener) +{ + LOGV("setListener"); + Mutex::Autolock _l(mLock); + mListener = listener; + + return NO_ERROR; +} + +void MediaRecorder::notify(int msg, int ext1, int ext2) +{ + LOGV("message received msg=%d, ext1=%d, ext2=%d", msg, ext1, ext2); + + sp listener; + mLock.lock(); + listener = mListener; + mLock.unlock(); + + if (listener != NULL) { + Mutex::Autolock _l(mNotifyLock); + LOGV("callback application"); + listener->notify(msg, ext1, ext2); + LOGV("back from callback"); + } +} + +}; // namespace android + -- cgit v1.1