diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2008-10-21 07:00:00 -0700 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2008-10-21 07:00:00 -0700 |
commit | 2729ea9262ca60d93047e984739887cfc89e82eb (patch) | |
tree | 25962a3f1c5f70586af45c926c037e03c643857f /media/libmedia | |
download | frameworks_av-2729ea9262ca60d93047e984739887cfc89e82eb.zip frameworks_av-2729ea9262ca60d93047e984739887cfc89e82eb.tar.gz frameworks_av-2729ea9262ca60d93047e984739887cfc89e82eb.tar.bz2 |
Initial Contribution
Diffstat (limited to 'media/libmedia')
-rw-r--r-- | media/libmedia/Android.mk | 34 | ||||
-rw-r--r-- | media/libmedia/AudioRecord.cpp | 397 | ||||
-rw-r--r-- | media/libmedia/AudioSystem.cpp | 255 | ||||
-rw-r--r-- | media/libmedia/AudioTrack.cpp | 596 | ||||
-rw-r--r-- | media/libmedia/IAudioFlinger.cpp | 450 | ||||
-rw-r--r-- | media/libmedia/IAudioRecord.cpp | 100 | ||||
-rw-r--r-- | media/libmedia/IAudioTrack.cpp | 140 | ||||
-rw-r--r-- | media/libmedia/IMediaPlayer.cpp | 295 | ||||
-rw-r--r-- | media/libmedia/IMediaPlayerClient.cpp | 77 | ||||
-rw-r--r-- | media/libmedia/IMediaPlayerService.cpp | 157 | ||||
-rw-r--r-- | media/libmedia/MODULE_LICENSE_APACHE2 | 0 | ||||
-rw-r--r-- | media/libmedia/NOTICE | 190 | ||||
-rw-r--r-- | media/libmedia/ToneGenerator.cpp | 662 | ||||
-rw-r--r-- | media/libmedia/mediametadataretriever.cpp | 182 | ||||
-rw-r--r-- | media/libmedia/mediaplayer.cpp | 582 |
15 files changed, 4117 insertions, 0 deletions
diff --git a/media/libmedia/Android.mk b/media/libmedia/Android.mk new file mode 100644 index 0000000..cfa837a --- /dev/null +++ b/media/libmedia/Android.mk @@ -0,0 +1,34 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + AudioTrack.cpp \ + IAudioFlinger.cpp \ + IAudioTrack.cpp \ + IAudioRecord.cpp \ + AudioRecord.cpp \ + AudioSystem.cpp \ + mediaplayer.cpp \ + IMediaPlayerService.cpp \ + IMediaPlayerClient.cpp \ + IMediaPlayer.cpp \ + mediametadataretriever.cpp \ + ToneGenerator.cpp + +LOCAL_SHARED_LIBRARIES := \ + libui libcutils libutils + +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..f3e4123 --- /dev/null +++ b/media/libmedia/AudioRecord.cpp @@ -0,0 +1,397 @@ +/* +** +** 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_TAG "AudioRecord" + +#include <stdint.h> +#include <sys/types.h> + +#include <sched.h> +#include <sys/resource.h> + +#include <private/media/AudioTrackShared.h> + +#include <media/AudioSystem.h> +#include <media/AudioRecord.h> + +#include <utils/IServiceManager.h> +#include <utils/Log.h> +#include <utils/MemoryDealer.h> +#include <utils/Parcel.h> +#include <utils/IPCThreadState.h> +#include <utils/Timers.h> +#include <cutils/atomic.h> + +#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 bufferCount, + uint32_t flags, + callback_t cbf, void* user) + : mStatus(NO_INIT) +{ + mStatus = set(streamType, sampleRate, format, channelCount, + bufferCount, flags, cbf, user); +} + +AudioRecord::~AudioRecord() +{ + if (mStatus == NO_ERROR) { + if (mPosition) { + releaseBuffer(&mAudioBuffer); + } + // obtainBuffer() will give up with an error + mAudioRecord->stop(); + if (mClientRecordThread != 0) { + mClientRecordThread->requestExitAndWait(); + mClientRecordThread.clear(); + } + mAudioRecord.clear(); + IPCThreadState::self()->flushCommands(); + } +} + +status_t AudioRecord::set( + int streamType, + uint32_t sampleRate, + int format, + int channelCount, + int bufferCount, + uint32_t flags, + callback_t cbf, void* user) +{ + + if (mAudioFlinger != 0) { + return INVALID_OPERATION; + } + + const sp<IAudioFlinger>& 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; + } + if (bufferCount == 0) { + bufferCount = 2; + } else if (bufferCount < 2) { + return BAD_VALUE; + } + + // validate parameters + if (format != AudioSystem::PCM_16_BIT) { + return BAD_VALUE; + } + if (channelCount != 1 && channelCount != 2) { + return BAD_VALUE; + } + if (bufferCount < 2) { + return BAD_VALUE; + } + + // open record channel + sp<IAudioRecord> record = audioFlinger->openRecord(getpid(), streamType, + sampleRate, format, channelCount, bufferCount, flags); + if (record == 0) { + return NO_INIT; + } + sp<IMemory> cblk = record->getCblk(); + if (cblk == 0) { + return NO_INIT; + } + if (cbf != 0) { + mClientRecordThread = new ClientRecordThread(*this); + if (mClientRecordThread == 0) { + return NO_INIT; + } + } + + mStatus = NO_ERROR; + + mAudioFlinger = audioFlinger; + mAudioRecord = record; + mCblkMemory = cblk; + mCblk = static_cast<audio_track_cblk_t*>(cblk->pointer()); + mCblk->buffers = (char*)mCblk + sizeof(audio_track_cblk_t); + mSampleRate = sampleRate; + mFrameCount = audioFlinger->frameCount(); + mFormat = format; + mBufferCount = bufferCount; + mChannelCount = channelCount; + mActive = 0; + mCbf = cbf; + mUserData = user; + mLatency = seconds(mFrameCount) / mSampleRate; + mPosition = 0; + return NO_ERROR; +} + +status_t AudioRecord::initCheck() const +{ + return mStatus; +} + +// ------------------------------------------------------------------------- + +nsecs_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; +} + +int AudioRecord::bufferCount() const +{ + return mBufferCount; +} + +// ------------------------------------------------------------------------- + +status_t AudioRecord::start() +{ + status_t ret = NO_ERROR; + + // If using record thread, protect start sequence to make sure that + // no stop command is processed before the thread is started + if (mClientRecordThread != 0) { + mRecordThreadLock.lock(); + } + + if (android_atomic_or(1, &mActive) == 0) { + setpriority(PRIO_PROCESS, 0, THREAD_PRIORITY_AUDIO_CLIENT); + ret = mAudioRecord->start(); + if (ret == NO_ERROR) { + if (mClientRecordThread != 0) { + mClientRecordThread->run("ClientRecordThread", THREAD_PRIORITY_AUDIO_CLIENT); + } + } + } + + if (mClientRecordThread != 0) { + mRecordThreadLock.unlock(); + } + + return ret; +} + +status_t AudioRecord::stop() +{ + // If using record thread, protect stop sequence to make sure that + // no start command is processed before requestExit() is called + if (mClientRecordThread != 0) { + mRecordThreadLock.lock(); + } + + if (android_atomic_and(~1, &mActive) == 1) { + if (mPosition) { + mPosition = 0; + releaseBuffer(&mAudioBuffer); + } + mAudioRecord->stop(); + setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_NORMAL); + if (mClientRecordThread != 0) { + mClientRecordThread->requestExit(); + } + } + + if (mClientRecordThread != 0) { + mRecordThreadLock.unlock(); + } + + return NO_ERROR; +} + +bool AudioRecord::stopped() const +{ + return !mActive; +} + +// ------------------------------------------------------------------------- + +status_t AudioRecord::obtainBuffer(Buffer* audioBuffer, bool blocking) +{ + int active = mActive; + int timeout = 0; + status_t result; + audio_track_cblk_t* cblk = mCblk; + + const uint32_t u = cblk->user; + uint32_t s = cblk->server; + + if (u == s) { + Mutex::Autolock _l(cblk->lock); + goto start_loop_here; + while (u == s) { + active = mActive; + if (UNLIKELY(!active)) + return NO_MORE_BUFFERS; + if (UNLIKELY(!blocking)) + return WOULD_BLOCK; + timeout = 0; + result = cblk->cv.waitRelative(cblk->lock, seconds(1)); + if (__builtin_expect(result!=NO_ERROR, false)) { + LOGW( "obtainBuffer timed out (is the CPU pegged?) " + "user=%08x, server=%08x", u, s); + timeout = 1; + } + // read the server count again + start_loop_here: + s = cblk->server; + } + } + + 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)", u, s); + + audioBuffer->flags = 0; + audioBuffer->channelCount= mChannelCount; + audioBuffer->format = mFormat; + audioBuffer->frameCount = mFrameCount; + audioBuffer->size = cblk->size; + audioBuffer->raw = (int8_t*) + cblk->buffer(cblk->user & audio_track_cblk_t::BUFFER_MASK); + return active ? status_t(NO_ERROR) : status_t(STOPPED); +} + +void AudioRecord::releaseBuffer(Buffer* audioBuffer) +{ + // next buffer... + if (UNLIKELY(mPosition)) { + // clean the remaining part of the buffer + size_t capacity = mAudioBuffer.size - mPosition; + memset(mAudioBuffer.i8 + mPosition, 0, capacity); + } + audio_track_cblk_t* cblk = mCblk; + cblk->stepUser(mBufferCount); +} + +// ------------------------------------------------------------------------- + +ssize_t AudioRecord::read(void* buffer, size_t userSize) +{ + ssize_t read = 0; + do { + if (mPosition == 0) { + status_t err = obtainBuffer(&mAudioBuffer, true); + if (err < 0) { + // out of buffers, return #bytes written + if (err == status_t(NO_MORE_BUFFERS)) + break; + return ssize_t(err); + } + } + + size_t capacity = mAudioBuffer.size - mPosition; + size_t toRead = userSize < capacity ? userSize : capacity; + + memcpy(buffer, mAudioBuffer.i8 + mPosition, toRead); + + buffer = static_cast<int8_t*>(buffer) + toRead; + mPosition += toRead; + userSize -= toRead; + capacity -= toRead; + read += toRead; + + if (capacity == 0) { + mPosition = 0; + releaseBuffer(&mAudioBuffer); + } + } while (userSize); + + return read; +} + +// ------------------------------------------------------------------------- + +bool AudioRecord::processAudioBuffer(const sp<ClientRecordThread>& thread) +{ + Buffer audioBuffer; + bool more; + + do { + status_t err = obtainBuffer(&audioBuffer, true); + if (err < NO_ERROR) { + LOGE("Error obtaining an audio buffer, giving up."); + return false; + } + more = mCbf(mUserData, audioBuffer); + releaseBuffer(&audioBuffer); + } while (more && !thread->exitPending()); + + // stop the track automatically + this->stop(); + + return true; +} + +// ========================================================================= + +AudioRecord::ClientRecordThread::ClientRecordThread(AudioRecord& receiver) + : Thread(false), 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..22de463 --- /dev/null +++ b/media/libmedia/AudioSystem.cpp @@ -0,0 +1,255 @@ +/* + * 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" +#include <utils/Log.h> +#include <utils/IServiceManager.h> +#include <media/AudioSystem.h> +#include <media/AudioTrack.h> +#include <math.h> + +namespace android { + +// client singleton for AudioFlinger binder interface +Mutex AudioSystem::gLock; +sp<IAudioFlinger> AudioSystem::gAudioFlinger; +sp<AudioSystem::DeathNotifier> AudioSystem::gDeathNotifier; +audio_error_callback AudioSystem::gAudioErrorCallback = NULL; + +// establish binder interface to AudioFlinger service +const sp<IAudioFlinger>& AudioSystem::get_audio_flinger() +{ + Mutex::Autolock _l(gLock); + if (gAudioFlinger.get() == 0) { + sp<IServiceManager> sm = defaultServiceManager(); + sp<IBinder> 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 (gDeathNotifier == NULL) { + gDeathNotifier = new DeathNotifier(); + } else { + if (gAudioErrorCallback) { + gAudioErrorCallback(NO_ERROR); + } + } + binder->linkToDeath(gDeathNotifier); + gAudioFlinger = interface_cast<IAudioFlinger>(binder); + } + 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; + 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); + return s; +} + +status_t AudioSystem::muteMicrophone(bool state) { + const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger(); + if (af == 0) return PERMISSION_DENIED; + return af->setMicMute(state); +} + +status_t AudioSystem::isMicrophoneMuted(bool* state) { + const sp<IAudioFlinger>& 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<IAudioFlinger>& 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<IAudioFlinger>& 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<IAudioFlinger>& 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<IAudioFlinger>& 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) >= AudioTrack::NUM_STREAM_TYPES) return BAD_VALUE; + const sp<IAudioFlinger>& 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) >= AudioTrack::NUM_STREAM_TYPES) return BAD_VALUE; + const sp<IAudioFlinger>& 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) >= AudioTrack::NUM_STREAM_TYPES) return BAD_VALUE; + const sp<IAudioFlinger>& 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) >= AudioTrack::NUM_STREAM_TYPES) return BAD_VALUE; + const sp<IAudioFlinger>& 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<IAudioFlinger>& af = AudioSystem::get_audio_flinger(); + if (af == 0) return PERMISSION_DENIED; + return af->setMode(mode); +} + +status_t AudioSystem::getMode(int* mode) +{ + const sp<IAudioFlinger>& 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<IAudioFlinger>& 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<IAudioFlinger>& 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<IAudioFlinger>& 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<IAudioFlinger>& 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; +} + +// --------------------------------------------------------------------------- + +void AudioSystem::DeathNotifier::binderDied(const wp<IBinder>& who) { + Mutex::Autolock _l(AudioSystem::gLock); + AudioSystem::gAudioFlinger.clear(); + if (gAudioErrorCallback) { + gAudioErrorCallback(DEAD_OBJECT); + } + LOGW("AudioFlinger server died!"); +} + +void AudioSystem::setErrorCallback(audio_error_callback cb) { + Mutex::Autolock _l(AudioSystem::gLock); + gAudioErrorCallback = cb; +} + +}; // namespace android + diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp new file mode 100644 index 0000000..298170a --- /dev/null +++ b/media/libmedia/AudioTrack.cpp @@ -0,0 +1,596 @@ +/* //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 <stdint.h> +#include <sys/types.h> + +#include <sched.h> +#include <sys/resource.h> + +#include <private/media/AudioTrackShared.h> + +#include <media/AudioSystem.h> +#include <media/AudioTrack.h> + +#include <utils/Log.h> +#include <utils/MemoryDealer.h> +#include <utils/Parcel.h> +#include <utils/IPCThreadState.h> +#include <utils/Timers.h> +#include <cutils/atomic.h> + +#define LIKELY( exp ) (__builtin_expect( (exp) != 0, true )) +#define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false )) + +namespace android { + +// --------------------------------------------------------------------------- + +static volatile size_t gFrameCount = 0; + +size_t AudioTrack::frameCount() +{ + if (gFrameCount) return gFrameCount; + const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger(); + if (af == 0) return PERMISSION_DENIED; + gFrameCount = af->frameCount(); + return gFrameCount; +} + +// --------------------------------------------------------------------------- + +AudioTrack::AudioTrack() + : mStatus(NO_INIT) +{ +} + +AudioTrack::AudioTrack( + int streamType, + uint32_t sampleRate, + int format, + int channelCount, + int bufferCount, + uint32_t flags, + callback_t cbf, void* user) + : mStatus(NO_INIT) +{ + mStatus = set(streamType, sampleRate, format, channelCount, + bufferCount, flags, cbf, user); +} + +AudioTrack::~AudioTrack() +{ + if (mStatus == NO_ERROR) { + if (mPosition) { + releaseBuffer(&mAudioBuffer); + } + // obtainBuffer() will give up with an error + mAudioTrack->stop(); + if (mAudioTrackThread != 0) { + mAudioTrackThread->requestExitAndWait(); + mAudioTrackThread.clear(); + } + mAudioTrack.clear(); + IPCThreadState::self()->flushCommands(); + } +} + +status_t AudioTrack::set( + int streamType, + uint32_t sampleRate, + int format, + int channelCount, + int bufferCount, + uint32_t flags, + callback_t cbf, void* user) +{ + + if (mAudioFlinger != 0) { + LOGE("Track already in use"); + return INVALID_OPERATION; + } + + const sp<IAudioFlinger>& audioFlinger = AudioSystem::get_audio_flinger(); + if (audioFlinger == 0) { + LOGE("Could not get audioflinger"); + return NO_INIT; + } + + // handle default values first. + if (streamType == DEFAULT) { + streamType = MUSIC; + } + if (sampleRate == 0) { + sampleRate = audioFlinger->sampleRate(); + } + // these below should probably come from the audioFlinger too... + if (format == 0) { + format = AudioSystem::PCM_16_BIT; + } + if (channelCount == 0) { + channelCount = 2; + } + if (bufferCount == 0) { + bufferCount = 2; + } + + // validate parameters + if (format != AudioSystem::PCM_16_BIT) { + LOGE("Invalid format"); + return BAD_VALUE; + } + if (channelCount != 1 && channelCount != 2) { + LOGE("Invalid channel number"); + return BAD_VALUE; + } + if (bufferCount < 2) { + LOGE("Invalid buffer count"); + return BAD_VALUE; + } + + // create the track + sp<IAudioTrack> track = audioFlinger->createTrack(getpid(), + streamType, sampleRate, format, channelCount, bufferCount, flags); + if (track == 0) { + LOGE("AudioFlinger could not create track"); + return NO_INIT; + } + sp<IMemory> cblk = track->getCblk(); + if (cblk == 0) { + LOGE("Could not get control block"); + return NO_INIT; + } + if (cbf != 0) { + mAudioTrackThread = new AudioTrackThread(*this); + if (mAudioTrackThread == 0) { + LOGE("Could not create callback thread"); + return NO_INIT; + } + } + + mStatus = NO_ERROR; + + mAudioFlinger = audioFlinger; + mAudioTrack = track; + mCblkMemory = cblk; + mCblk = static_cast<audio_track_cblk_t*>(cblk->pointer()); + mCblk->buffers = (char*)mCblk + sizeof(audio_track_cblk_t); + mCblk->volume[0] = mCblk->volume[1] = 0x1000; + mVolume[LEFT] = 1.0f; + mVolume[RIGHT] = 1.0f; + mSampleRate = sampleRate; + mFrameCount = audioFlinger->frameCount(); + mStreamType = streamType; + mFormat = format; + mBufferCount = bufferCount; + mChannelCount = channelCount; + mMuted = false; + mActive = 0; + mReserved = 0; + mCbf = cbf; + mUserData = user; + mLatency = seconds(mFrameCount) / mSampleRate; + mPosition = 0; + return NO_ERROR; +} + +status_t AudioTrack::initCheck() const +{ + return mStatus; +} + +// ------------------------------------------------------------------------- + +nsecs_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; +} + +int AudioTrack::bufferCount() const +{ + return mBufferCount; +} + +// ------------------------------------------------------------------------- + +void AudioTrack::start() +{ + sp<AudioTrackThread> t = mAudioTrackThread; + + LOGV("start"); + 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) { + 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<AudioTrackThread> t = mAudioTrackThread; + + LOGV("stop"); + if (t != 0) { + t->mLock.lock(); + } + + if (android_atomic_and(~1, &mActive) == 1) { + if (mPosition) { + releaseBuffer(&mAudioBuffer); + } + mAudioTrack->stop(); + 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) +{ + if (rate > MAX_SAMPLE_RATE) rate = MAX_SAMPLE_RATE; + mCblk->sampleRate = rate; +} + +uint32_t AudioTrack::getSampleRate() +{ + return uint32_t(mCblk->sampleRate); +} + +// ------------------------------------------------------------------------- + +status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, bool blocking) +{ + int active; + int timeout = 0; + status_t result; + audio_track_cblk_t* cblk = mCblk; + + uint32_t u = cblk->user; + uint32_t u_seq = u & audio_track_cblk_t::SEQUENCE_MASK; + uint32_t u_buf = u & audio_track_cblk_t::BUFFER_MASK; + + uint32_t s = cblk->server; + uint32_t s_seq = s & audio_track_cblk_t::SEQUENCE_MASK; + uint32_t s_buf = s & audio_track_cblk_t::BUFFER_MASK; + + LOGW_IF(u_seq < s_seq, "user doesn't fill buffers fast enough"); + + if (u_seq > s_seq && u_buf == s_buf) { + Mutex::Autolock _l(cblk->lock); + goto start_loop_here; + while (u_seq > s_seq && u_buf == s_buf) { + active = mActive; + if (UNLIKELY(!active)) { + LOGV("Not active and NO_MORE_BUFFERS"); + return NO_MORE_BUFFERS; + } + if (UNLIKELY(!blocking)) + return WOULD_BLOCK; + timeout = 0; + result = cblk->cv.waitRelative(cblk->lock, seconds(1)); + if (__builtin_expect(result!=NO_ERROR, false)) { + LOGW( "obtainBuffer timed out (is the CPU pegged?) " + "user=%08x, server=%08x", u, s); + mAudioTrack->start(); // FIXME: Wake up audioflinger + timeout = 1; + } + // Read user count in case a flush has reset while we where waiting on cv. + u = cblk->user; + u_seq = u & audio_track_cblk_t::SEQUENCE_MASK; + u_buf = u & audio_track_cblk_t::BUFFER_MASK; + + // read the server count again + start_loop_here: + s = cblk->server; + s_seq = s & audio_track_cblk_t::SEQUENCE_MASK; + s_buf = s & audio_track_cblk_t::BUFFER_MASK; + } + } + + 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)", u, s); + + audioBuffer->flags = mMuted ? Buffer::MUTE : 0; + audioBuffer->channelCount= mChannelCount; + audioBuffer->format = mFormat; + audioBuffer->frameCount = mFrameCount; + audioBuffer->size = cblk->size; + audioBuffer->raw = (int8_t *)cblk->buffer(u_buf); + active = mActive; + return active ? status_t(NO_ERROR) : status_t(STOPPED); +} + +void AudioTrack::releaseBuffer(Buffer* audioBuffer) +{ + // next buffer... + if (UNLIKELY(mPosition)) { + // clean the remaining part of the buffer + size_t capacity = mAudioBuffer.size - mPosition; + memset(mAudioBuffer.i8 + mPosition, 0, capacity); + mPosition = 0; + } + audio_track_cblk_t* cblk = mCblk; + cblk->stepUser(mBufferCount); +} + +// ------------------------------------------------------------------------- + +ssize_t AudioTrack::write(const void* buffer, size_t userSize) +{ + 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 %d bytes, mActive=%d", userSize, mActive); + ssize_t written = 0; + do { + if (mPosition == 0) { + status_t err = obtainBuffer(&mAudioBuffer, true); + if (err < 0) { + // out of buffers, return #bytes written + if (err == status_t(NO_MORE_BUFFERS)) + break; + return ssize_t(err); + } + } + + size_t capacity = mAudioBuffer.size - mPosition; + size_t toWrite = userSize < capacity ? userSize : capacity; + + memcpy(mAudioBuffer.i8 + mPosition, buffer, toWrite); + buffer = static_cast<const int8_t*>(buffer) + toWrite; + mPosition += toWrite; + userSize -= toWrite; + capacity -= toWrite; + written += toWrite; + + if (capacity == 0) { + mPosition = 0; + releaseBuffer(&mAudioBuffer); + } + } while (userSize); + + return written; +} + +// ------------------------------------------------------------------------- + +bool AudioTrack::processAudioBuffer(const sp<AudioTrackThread>& thread) +{ + Buffer audioBuffer; + + status_t err = obtainBuffer(&audioBuffer, true); + if (err < NO_ERROR) { + LOGE("Error obtaining an audio buffer, giving up."); + return false; + } + if (err == status_t(STOPPED)) return false; + mCbf(mUserData, audioBuffer); + releaseBuffer(&audioBuffer); + + return true; +} + +status_t AudioTrack::dump(int fd, const Vector<String16>& 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), buffer count(%d)\n", mFormat, mChannelCount, mFrameCount, mBufferCount); + result.append(buffer); + snprintf(buffer, 255, " sample rate(%d), status(%d), muted(%d), reserved(%d)\n", mSampleRate, mStatus, mMuted, mReserved); + result.append(buffer); + snprintf(buffer, 255, " active(%d), latency (%lld), position(%d)\n", mActive, mLatency, mPosition); + result.append(buffer); + ::write(fd, result.string(), result.size()); + return NO_ERROR; +} + +// ========================================================================= + +AudioTrack::AudioTrackThread::AudioTrackThread(AudioTrack& receiver) + : Thread(false), 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), volumeLR(0), buffers(0), size(0) +{ +} + +uint32_t audio_track_cblk_t::stepUser(int bufferCount) +{ + uint32_t u = this->user; + uint32_t u_seq = u & audio_track_cblk_t::SEQUENCE_MASK; + uint32_t u_buf = u & audio_track_cblk_t::BUFFER_MASK; + if (++u_buf >= uint32_t(bufferCount)) { + u_seq += 0x100; + u_buf = 0; + } + u = u_seq | u_buf; + this->user = u; + return u; +} + +bool audio_track_cblk_t::stepServer(int bufferCount) +{ + // 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; + uint32_t s_seq = s & audio_track_cblk_t::SEQUENCE_MASK; + uint32_t s_buf = s & audio_track_cblk_t::BUFFER_MASK; + s_buf++; + if (s_buf >= uint32_t(bufferCount)) { + s_seq += 0x100; + s_buf = 0; + } + s = s_seq | s_buf; + + this->server = s; + cv.signal(); + lock.unlock(); + return true; +} + +void* audio_track_cblk_t::buffer(int id) const +{ + return (char*)this->buffers + id * this->size; +} + +// ------------------------------------------------------------------------- + +}; // namespace android + diff --git a/media/libmedia/IAudioFlinger.cpp b/media/libmedia/IAudioFlinger.cpp new file mode 100644 index 0000000..474381b --- /dev/null +++ b/media/libmedia/IAudioFlinger.cpp @@ -0,0 +1,450 @@ +/* //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 <utils/Log.h> + +#include <stdint.h> +#include <sys/types.h> + +#include <utils/Parcel.h> + +#include <media/IAudioFlinger.h> + +namespace android { + +enum { + CREATE_TRACK = IBinder::FIRST_CALL_TRANSACTION, + OPEN_RECORD, + SAMPLE_RATE, + CHANNEL_COUNT, + FORMAT, + FRAME_COUNT, + 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, +}; + +class BpAudioFlinger : public BpInterface<IAudioFlinger> +{ +public: + BpAudioFlinger(const sp<IBinder>& impl) + : BpInterface<IAudioFlinger>(impl) + { + } + + virtual sp<IAudioTrack> createTrack( + pid_t pid, + int streamType, + uint32_t sampleRate, + int format, + int channelCount, + int bufferCount, + uint32_t flags) + { + Parcel data, reply; + data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); + data.writeInt32(pid); + data.writeInt32(streamType); + data.writeInt32(sampleRate); + data.writeInt32(format); + data.writeInt32(channelCount); + data.writeInt32(bufferCount); + data.writeInt32(flags); + status_t status = remote()->transact(CREATE_TRACK, data, &reply); + if ( status != NO_ERROR) { + LOGE("createTrack error: %s", strerror(-status)); + } + + return interface_cast<IAudioTrack>(reply.readStrongBinder()); + } + + virtual sp<IAudioRecord> openRecord( + pid_t pid, + int streamType, + uint32_t sampleRate, + int format, + int channelCount, + int bufferCount, + uint32_t flags) + { + Parcel data, reply; + data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); + data.writeInt32(pid); + data.writeInt32(streamType); + data.writeInt32(sampleRate); + data.writeInt32(format); + data.writeInt32(channelCount); + data.writeInt32(bufferCount); + data.writeInt32(flags); + remote()->transact(OPEN_RECORD, data, &reply); + return interface_cast<IAudioRecord>(reply.readStrongBinder()); + } + + virtual uint32_t sampleRate() const + { + Parcel data, reply; + data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); + remote()->transact(SAMPLE_RATE, data, &reply); + return reply.readInt32(); + } + + virtual int channelCount() const + { + Parcel data, reply; + data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); + remote()->transact(CHANNEL_COUNT, data, &reply); + return reply.readInt32(); + } + + virtual int format() const + { + Parcel data, reply; + data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); + remote()->transact(FORMAT, data, &reply); + return reply.readInt32(); + } + + virtual size_t frameCount() const + { + Parcel data, reply; + data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); + remote()->transact(FRAME_COUNT, 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(); + } +}; + +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<IAudioTrack> track = createTrack(pid, + streamType, sampleRate, format, + channelCount, bufferCount, flags); + 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(); + sp<IAudioRecord> record = openRecord(pid, streamType, + sampleRate, format, channelCount, bufferCount, flags); + reply->writeStrongBinder(record->asBinder()); + return NO_ERROR; + } break; + case SAMPLE_RATE: { + CHECK_INTERFACE(IAudioFlinger, data, reply); + reply->writeInt32( sampleRate() ); + return NO_ERROR; + } break; + case CHANNEL_COUNT: { + CHECK_INTERFACE(IAudioFlinger, data, reply); + reply->writeInt32( channelCount() ); + return NO_ERROR; + } break; + case FORMAT: { + CHECK_INTERFACE(IAudioFlinger, data, reply); + reply->writeInt32( format() ); + return NO_ERROR; + } break; + case FRAME_COUNT: { + CHECK_INTERFACE(IAudioFlinger, data, reply); + reply->writeInt32( frameCount() ); + 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; + 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 <stdint.h> +#include <sys/types.h> + +#include <utils/Parcel.h> + +#include <media/IAudioRecord.h> + +namespace android { + +enum { + GET_CBLK = IBinder::FIRST_CALL_TRANSACTION, + START, + STOP +}; + +class BpAudioRecord : public BpInterface<IAudioRecord> +{ +public: + BpAudioRecord(const sp<IBinder>& impl) + : BpInterface<IAudioRecord>(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<IMemory> getCblk() const + { + Parcel data, reply; + data.writeInterfaceToken(IAudioRecord::getInterfaceDescriptor()); + remote()->transact(GET_CBLK, data, &reply); + return interface_cast<IMemory>(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 <stdint.h> +#include <sys/types.h> + +#include <utils/Parcel.h> + +#include <media/IAudioTrack.h> + +namespace android { + +enum { + GET_CBLK = IBinder::FIRST_CALL_TRANSACTION, + START, + STOP, + FLUSH, + MUTE, + PAUSE +}; + +class BpAudioTrack : public BpInterface<IAudioTrack> +{ +public: + BpAudioTrack(const sp<IBinder>& impl) + : BpInterface<IAudioTrack>(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<IMemory> getCblk() const + { + Parcel data, reply; + data.writeInterfaceToken(IAudioTrack::getInterfaceDescriptor()); + remote()->transact(GET_CBLK, data, &reply); + return interface_cast<IMemory>(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/IMediaPlayer.cpp b/media/libmedia/IMediaPlayer.cpp new file mode 100644 index 0000000..8385114 --- /dev/null +++ b/media/libmedia/IMediaPlayer.cpp @@ -0,0 +1,295 @@ +/* +** +** 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 <stdint.h> +#include <sys/types.h> + +#include <utils/Parcel.h> + +#include <media/IMediaPlayer.h> +#include <ui/ISurface.h> + +namespace android { + +enum { + DISCONNECT = IBinder::FIRST_CALL_TRANSACTION, + SET_VIDEO_SURFACE, + PREPARE_ASYNC, + START, + STOP, + IS_PLAYING, + PAUSE, + GET_VIDEO_SIZE, + SEEK_TO, + GET_CURRENT_POSITION, + GET_DURATION, + RESET, + SET_AUDIO_STREAM_TYPE, + SET_LOOPING, + SET_VOLUME +}; + +class BpMediaPlayer: public BpInterface<IMediaPlayer> +{ +public: + BpMediaPlayer(const sp<IBinder>& impl) + : BpInterface<IMediaPlayer>(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<ISurface>& 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 getVideoSize(int* w, int* h) + { + Parcel data, reply; + data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor()); + remote()->transact(GET_VIDEO_SIZE, data, &reply); + *w = reply.readInt32(); + *h = reply.readInt32(); + 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<ISurface> surface = interface_cast<ISurface>(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 GET_VIDEO_SIZE: { + CHECK_INTERFACE(IMediaPlayer, data, reply); + int w, h; + status_t ret = getVideoSize(&w, &h); + reply->writeInt32(w); + reply->writeInt32(h); + reply->writeInt32(ret); + 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 <utils/RefBase.h> +#include <utils/IInterface.h> +#include <utils/Parcel.h> + +#include <media/IMediaPlayerClient.h> + +namespace android { + +enum { + NOTIFY = IBinder::FIRST_CALL_TRANSACTION, +}; + +class BpMediaPlayerClient: public BpInterface<IMediaPlayerClient> +{ +public: + BpMediaPlayerClient(const sp<IBinder>& impl) + : BpInterface<IMediaPlayerClient>(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..b087100 --- /dev/null +++ b/media/libmedia/IMediaPlayerService.cpp @@ -0,0 +1,157 @@ +/* +** +** 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 <stdint.h> +#include <sys/types.h> +#include <utils/Parcel.h> + +#include <utils/IMemory.h> +#include <media/IMediaPlayerService.h> + +namespace android { + +enum { + CREATE_URL = IBinder::FIRST_CALL_TRANSACTION, + CREATE_FD, + DECODE_URL, + DECODE_FD, +}; + +class BpMediaPlayerService: public BpInterface<IMediaPlayerService> +{ +public: + BpMediaPlayerService(const sp<IBinder>& impl) + : BpInterface<IMediaPlayerService>(impl) + { + } + + virtual sp<IMediaPlayer> create(pid_t pid, const sp<IMediaPlayerClient>& 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<IMediaPlayer>(reply.readStrongBinder()); + } + + virtual sp<IMediaPlayer> create(pid_t pid, const sp<IMediaPlayerClient>& 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<IMediaPlayer>(reply.readStrongBinder()); + } + + virtual sp<IMemory> decode(const char* url, uint32_t *pSampleRate, int* pNumChannels) + { + Parcel data, reply; + data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor()); + data.writeCString(url); + remote()->transact(DECODE_URL, data, &reply); + *pSampleRate = uint32_t(reply.readInt32()); + *pNumChannels = reply.readInt32(); + return interface_cast<IMemory>(reply.readStrongBinder()); + } + + virtual sp<IMemory> decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels) + { + 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(); + return interface_cast<IMemory>(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<IMediaPlayerClient> client = interface_cast<IMediaPlayerClient>(data.readStrongBinder()); + const char* url = data.readCString(); + sp<IMediaPlayer> 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<IMediaPlayerClient> client = interface_cast<IMediaPlayerClient>(data.readStrongBinder()); + int fd = dup(data.readFileDescriptor()); + int64_t offset = data.readInt64(); + int64_t length = data.readInt64(); + sp<IMediaPlayer> 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; + sp<IMemory> player = decode(url, &sampleRate, &numChannels); + reply->writeInt32(sampleRate); + reply->writeInt32(numChannels); + 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; + sp<IMemory> player = decode(fd, offset, length, &sampleRate, &numChannels); + reply->writeInt32(sampleRate); + reply->writeInt32(numChannels); + reply->writeStrongBinder(player->asBinder()); + return NO_ERROR; + } break; + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + +// ---------------------------------------------------------------------------- + +}; // namespace android + diff --git a/media/libmedia/MODULE_LICENSE_APACHE2 b/media/libmedia/MODULE_LICENSE_APACHE2 new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/media/libmedia/MODULE_LICENSE_APACHE2 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..89ab2be --- /dev/null +++ b/media/libmedia/ToneGenerator.cpp @@ -0,0 +1,662 @@ +/* + * 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 <utils/threads.h> + +#include <stdio.h> +#include <math.h> +#include <utils/Log.h> +#include <sys/resource.h> +#include <utils/RefBase.h> +#include <utils/Timers.h> +#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 }, { 35, 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 }, { 35, 200, 35, 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) { + const sp<IAudioFlinger>& lpAudioFlinger = AudioSystem::get_audio_flinger(); + + LOGV("ToneGenerator constructor: streamType=%d, volume=%f\n", streamType, volume); + + mState = TONE_IDLE; + mpAudioTrack = 0; + mpToneDesc = 0; + mpNewToneDesc = 0; + + if (lpAudioFlinger == 0) { + LOGE("Unable to marshal AudioFlinger"); + goto ToneGenerator_exit; + } + + mSamplingRate = lpAudioFlinger->sampleRate(); + + mVolume = volume; + // Open audio track in mono, PCM 16bit, default sampling rate, 2 buffers + mpAudioTrack + = new AudioTrack(streamType, 0, AudioSystem::PCM_16_BIT, 1, NUM_PCM_BUFFERS, 0, audioCallback, this); + + if (mpAudioTrack == 0) { + LOGE("AudioTrack allocation failed"); + goto ToneGenerator_exit; + } + LOGV("Create Track: %p\n", mpAudioTrack); + + if (mpAudioTrack->initCheck() != NO_ERROR) { + LOGE("AudioTrack->initCheck failed"); + goto ToneGenerator_exit; + } + + mpAudioTrack->setVolume(volume, volume); + + LOGV("ToneGenerator INIT OK, time: %d\n", (unsigned int)(systemTime()/1000000)); + + mState = TONE_INIT; + + return; + +ToneGenerator_exit: + + // Cleanup + if (mpAudioTrack) { + LOGV("Delete Track I: %p\n", mpAudioTrack); + delete mpAudioTrack; + } + + 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 (mState == TONE_IDLE || toneType >= NUM_TONES) + 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)); + + mState = TONE_STARTING; + mLock.unlock(); + mpAudioTrack->start(); + mLock.lock(); + if (mState == TONE_STARTING) { + if (mWaitCbkCond.waitRelative(mLock, seconds(1)) != NO_ERROR) + LOGE("--- timed out"); + } + + if (mState == TONE_PLAYING) + lResult = true; + } + } else { + LOGV("Delayed start\n"); + + mState = TONE_RESTARTING; + if (mWaitCbkCond.waitRelative(mLock, seconds(1)) == NO_ERROR) { + if (mState != TONE_INIT) { + lResult = true; + } + LOGV("cond received"); + } else { + LOGE("--- timed out"); + } + } + mLock.unlock(); + + LOGV("Tone started, 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("--- timed out"); + mState = TONE_INIT; + } + } + + clearWaveGens(); + + mLock.unlock(); +} + +//---------------------------------- private methods --------------------------- + + +//////////////////////////////////////////////////////////////////////////////// +// +// 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(void* user, const AudioTrack::Buffer& info) { + ToneGenerator *lpToneGen = static_cast<ToneGenerator *>(user); + short *lpOut = info.i16; + unsigned int lReqSmp = info.size/sizeof(short); + unsigned int lGenSmp; + unsigned int lWaveCmd = WaveGenerator::WAVEGEN_CONT; + bool lSignal = false; + + + lpToneGen->mLock.lock(); + + // Clear output buffer: WaveGenerator accumulates into lpOut buffer + memset(lpOut, 0, info.size); + + // 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_Exit; + } + + // Exit if to sequence is over + if (lpToneGen->mpToneDesc->segments[lpToneGen->mCurSegment] == 0) { + goto audioCallback_Exit; + } + + 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); + } + } + +audioCallback_Exit: + + switch (lpToneGen->mState) { + case TONE_RESTARTING: + LOGV("Cbk restarting track\n"); + if (lpToneGen->prepareWave()) { + lpToneGen->mState = TONE_STARTING; + } else { + lpToneGen->mState = TONE_INIT; + lpToneGen->mpAudioTrack->stop(); + } + lSignal = true; + break; + case TONE_STOPPING: + lpToneGen->mState = TONE_INIT; + LOGV("Cbk Stopping track\n"); + lSignal = true; + lpToneGen->mpAudioTrack->stop(); + 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..9cbafbc --- /dev/null +++ b/media/libmedia/mediametadataretriever.cpp @@ -0,0 +1,182 @@ +/* +** +** 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 <media/mediametadataretriever.h> + +#ifdef LOG_TAG +#undef LOG_TAG +#define LOG_TAG "MediaMetadataRetriever" +#endif + +#include <utils/Log.h> +#include <dlfcn.h> + +namespace android { + +// Factory class function in shared libpvplayer.so +typedef MediaMetadataRetrieverImpl* (*createRetriever_f)(); + +MediaMetadataRetrieverImpl *MediaMetadataRetriever::mRetriever = NULL; +void *MediaMetadataRetriever::mLibHandler = NULL; + +void MediaMetadataRetriever::create() +{ + // Load libpvplayer library once and only once. + if (!mLibHandler) { + mLibHandler = dlopen("libopencoreplayer.so", RTLD_NOW); + if (!mLibHandler) { + LOGE("setDataSource: dlopen failed on libopencoreplayer.so"); + return; + } + } + + // Each time create a new MediaMetadataRetrieverImpl object. + if (mRetriever) { + delete mRetriever; + } + createRetriever_f createRetriever = reinterpret_cast<createRetriever_f>(dlsym(mLibHandler, "createRetriever")); + if (!createRetriever) { + LOGE("setDataSource: dlsym failed on createRetriever in libpvplayer.so"); + return; + } + mRetriever = createRetriever(); + if (!mRetriever) { + LOGE("setDataSource: createRetriever failed in libpvplayer.so"); + } +} + +status_t MediaMetadataRetriever::setDataSource(const char* srcUrl) +{ + if (srcUrl == NULL) { + return UNKNOWN_ERROR; + } + + if (mRetriever) { + return mRetriever->setDataSource(srcUrl); + } + return UNKNOWN_ERROR; +} + +const char* MediaMetadataRetriever::extractMetadata(int keyCode) +{ + if (mRetriever) { + return mRetriever->extractMetadata(keyCode); + } + return NULL; +} + +MediaAlbumArt* MediaMetadataRetriever::extractAlbumArt() +{ + if (mRetriever) { + return mRetriever->extractAlbumArt(); + } + return NULL; +} + +SkBitmap* MediaMetadataRetriever::captureFrame() +{ + if (mRetriever) { + return mRetriever->captureFrame(); + } + return NULL; +} + +void MediaMetadataRetriever::setMode(int mode) +{ + if (mRetriever) { + mRetriever->setMode(mode); + } +} + +void MediaMetadataRetriever::release() +{ + if (!mLibHandler) { + dlclose(mLibHandler); + mLibHandler = NULL; + } + if (!mRetriever) { + delete mRetriever; + mRetriever = NULL; + } +} + +void MediaAlbumArt::clearData() { + if (data != NULL) { + delete []data; + data = NULL; + } + length = 0; +} + + +MediaAlbumArt::MediaAlbumArt(const char* url) +{ + length = 0; + data = NULL; + FILE *in = fopen(url, "r"); + if (!in) { + LOGE("extractExternalAlbumArt: Failed to open external album art url: %s.", url); + return; + } + fseek(in, 0, SEEK_END); + length = ftell(in); // Allocating buffer of size equals to the external file size. + if (length == 0 || (data = new char[length]) == NULL) { + if (length == 0) { + LOGE("extractExternalAlbumArt: External album art url: %s has a size of 0.", url); + } else if (data == NULL) { + LOGE("extractExternalAlbumArt: No enough memory for storing the retrieved album art."); + length = 0; + } + fclose(in); + return; + } + rewind(in); + if (fread(data, 1, length, in) != length) { // Read failed. + length = 0; + delete []data; + data = NULL; + LOGE("extractExternalAlbumArt: Failed to retrieve the contents of an external album art."); + } + fclose(in); +} + +status_t MediaAlbumArt::setData(unsigned int len, const char* buf) { + clearData(); + length = len; + data = copyData(len, buf); + return (data != NULL)? OK: UNKNOWN_ERROR; +} + +char* MediaAlbumArt::copyData(unsigned int len, const char* buf) { + if (len == 0 || !buf) { + if (len == 0) { + LOGE("copyData: Length is 0."); + } else if (!buf) { + LOGE("copyData: buf is NULL pointer"); + } + return NULL; + } + char* copy = new char[len]; + if (!copy) { + LOGE("copyData: No enough memory to copy out the data."); + return NULL; + } + memcpy(copy, buf, len); + return copy; +} + +}; // namespace android diff --git a/media/libmedia/mediaplayer.cpp b/media/libmedia/mediaplayer.cpp new file mode 100644 index 0000000..736d84a --- /dev/null +++ b/media/libmedia/mediaplayer.cpp @@ -0,0 +1,582 @@ +/* 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 <utils/Log.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <fcntl.h> + +#include <utils/IServiceManager.h> +#include <utils/IPCThreadState.h> + +#include <media/mediaplayer.h> +#include <libsonivox/eas.h> + +#include <utils/MemoryBase.h> + +namespace android { + +// client singleton for binder interface to service +Mutex MediaPlayer::mServiceLock; +sp<IMediaPlayerService> MediaPlayer::mMediaPlayerService; +sp<MediaPlayer::DeathNotifier> MediaPlayer::mDeathNotifier; + +// establish binder interface to service +const sp<IMediaPlayerService>& MediaPlayer::getMediaPlayerService() +{ + Mutex::Autolock _l(mServiceLock); + if (mMediaPlayerService.get() == 0) { + sp<IServiceManager> sm = defaultServiceManager(); + sp<IBinder> 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 (mDeathNotifier == NULL) { + mDeathNotifier = new DeathNotifier(); + } + binder->linkToDeath(mDeathNotifier); + mMediaPlayerService = interface_cast<IMediaPlayerService>(binder); + } + LOGE_IF(mMediaPlayerService==0, "no MediaPlayerService!?"); + return mMediaPlayerService; +} + +MediaPlayer::MediaPlayer() +{ + LOGV("constructor"); + mListener = NULL; + mCookie = NULL; + mDuration = -1; + mStreamType = AudioTrack::MUSIC; + mCurrentPosition = -1; + mSeekPosition = -1; + mCurrentState = MEDIA_PLAYER_IDLE; + mPrepareSync = false; + mPrepareStatus = NO_ERROR; + mLoop = false; + mLeftVolume = mRightVolume = 1.0; +} + +MediaPlayer::~MediaPlayer() +{ + LOGV("destructor"); + disconnect(); + IPCThreadState::self()->flushCommands(); +} + +void MediaPlayer::disconnect() +{ + LOGV("disconnect"); + sp<IMediaPlayer> p; + { + Mutex::Autolock _l(mLock); + p = mPlayer; + mPlayer.clear(); + } + + if (p != 0) { + p->disconnect(); + p->asBinder()->unlinkToDeath(this); + } +} + +// always call with lock held +void MediaPlayer::clear_l() +{ + mDuration = -1; + mCurrentPosition = -1; + mSeekPosition = -1; +} + +status_t MediaPlayer::setListener(const sp<MediaPlayerListener>& listener) +{ + LOGV("setListener"); + Mutex::Autolock _l(mLock); + mListener = listener; + return NO_ERROR; +} + + +status_t MediaPlayer::setDataSource(const sp<IMediaPlayer>& player) +{ + status_t err = UNKNOWN_ERROR; + sp<IMediaPlayer> 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; + player->asBinder()->linkToDeath(this); + err = NO_ERROR; + } else { + LOGE("Unable to to create media player"); + } + } + + if (p != 0) { + p->disconnect(); + p->asBinder()->unlinkToDeath(this); + } + return err; +} + +status_t MediaPlayer::setDataSource(const char *url) +{ + LOGV("setDataSource(%s)", url); + status_t err = UNKNOWN_ERROR; + if (url != NULL) { + const sp<IMediaPlayerService>& service(getMediaPlayerService()); + if (service != 0) { + sp<IMediaPlayer> 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<IMediaPlayerService>& service(getMediaPlayerService()); + if (service != 0) { + sp<IMediaPlayer> player(service->create(getpid(), this, fd, offset, length)); + err = setDataSource(player); + } + return err; +} + +status_t MediaPlayer::setVideoSurface(const sp<Surface>& surface) +{ + LOGV("setVideoSurface"); + Mutex::Autolock _l(mLock); + if (mPlayer == 0) return UNKNOWN_ERROR; + 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 UNKNOWN_ERROR; + 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; + ret = UNKNOWN_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; + ret = UNKNOWN_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; + ret = UNKNOWN_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) { + int h; + return mPlayer->getVideoSize(w, &h); + } + return INVALID_OPERATION; +} + +status_t MediaPlayer::getVideoHeight(int *h) +{ + LOGV("getVideoHeight"); + Mutex::Autolock _l(mLock); + if (mPlayer != 0) { + int w; + return mPlayer->getVideoSize(&w, h); + } + return INVALID_OPERATION; +} + +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) { + mCurrentState = MEDIA_PLAYER_STATE_ERROR; + ret = UNKNOWN_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; +} + +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); + 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: + LOGV("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; + default: + LOGV("unrecognized message: (%d, %d, %d)", msg, ext1, ext2); + break; + } + + sp<MediaPlayerListener> 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::binderDied(const wp<IBinder>& who) { + LOGW("IMediaplayer died"); + notify(MEDIA_ERROR, MEDIA_ERROR_SERVER_DIED, 0); +} + +void MediaPlayer::DeathNotifier::binderDied(const wp<IBinder>& who) { + Mutex::Autolock _l(MediaPlayer::mServiceLock); + MediaPlayer::mMediaPlayerService.clear(); + LOGW("MediaPlayer server died!"); +} + +MediaPlayer::DeathNotifier::~DeathNotifier() +{ + Mutex::Autolock _l(mServiceLock); + if (mMediaPlayerService != 0) { + mMediaPlayerService->asBinder()->unlinkToDeath(this); + } +} + +/*static*/ sp<IMemory> MediaPlayer::decode(const char* url, uint32_t *pSampleRate, int* pNumChannels) +{ + LOGV("decode(%s)", url); + sp<IMemory> p; + const sp<IMediaPlayerService>& service = getMediaPlayerService(); + if (service != 0) { + p = mMediaPlayerService->decode(url, pSampleRate, pNumChannels); + } else { + LOGE("Unable to locate media service"); + } + return p; + +} + +/*static*/ sp<IMemory> MediaPlayer::decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels) +{ + LOGV("decode(%d, %lld, %lld)", fd, offset, length); + sp<IMemory> p; + const sp<IMediaPlayerService>& service = getMediaPlayerService(); + if (service != 0) { + p = mMediaPlayerService->decode(fd, offset, length, pSampleRate, pNumChannels); + } else { + LOGE("Unable to locate media service"); + } + return p; + +} + +}; // namespace android |