From 7b5eb023f8d87cca6d830ae6c11c6aadbe02aca8 Mon Sep 17 00:00:00 2001 From: The Android Open Source Project Date: Wed, 17 Dec 2008 18:05:43 -0800 Subject: Code drop from //branches/cupcake/...@124589 --- media/libmedia/Android.mk | 3 + media/libmedia/AudioRecord.cpp | 382 ++++++++---- media/libmedia/AudioSystem.cpp | 56 +- media/libmedia/AudioTrack.cpp | 684 ++++++++++++++++----- media/libmedia/IAudioFlinger.cpp | 74 ++- media/libmedia/IMediaMetadataRetriever.cpp | 218 +++++++ media/libmedia/IMediaPlayer.cpp | 20 - media/libmedia/IMediaPlayerService.cpp | 49 +- media/libmedia/IMediaRecorder.cpp | 376 +++++++++++ media/libmedia/ToneGenerator.cpp | 145 +++-- media/libmedia/mediametadataretriever.cpp | 228 +++---- media/libmedia/mediaplayer.cpp | 126 ++-- media/libmedia/mediarecorder.cpp | 518 ++++++++++++++++ media/libmediaplayerservice/Android.mk | 3 + media/libmediaplayerservice/MediaPlayerService.cpp | 136 ++-- media/libmediaplayerservice/MediaPlayerService.h | 40 +- .../libmediaplayerservice/MediaRecorderClient.cpp | 251 ++++++++ media/libmediaplayerservice/MediaRecorderClient.h | 64 ++ .../MetadataRetrieverClient.cpp | 250 ++++++++ .../MetadataRetrieverClient.h | 71 +++ media/libmediaplayerservice/MidiFile.cpp | 2 +- media/libmediaplayerservice/VorbisPlayer.cpp | 2 +- 22 files changed, 3138 insertions(+), 560 deletions(-) create mode 100644 media/libmedia/IMediaMetadataRetriever.cpp create mode 100644 media/libmedia/IMediaRecorder.cpp create mode 100644 media/libmedia/mediarecorder.cpp create mode 100644 media/libmediaplayerservice/MediaRecorderClient.cpp create mode 100644 media/libmediaplayerservice/MediaRecorderClient.h create mode 100644 media/libmediaplayerservice/MetadataRetrieverClient.cpp create mode 100644 media/libmediaplayerservice/MetadataRetrieverClient.h (limited to 'media') diff --git a/media/libmedia/Android.mk b/media/libmedia/Android.mk index cfa837a..650684a 100644 --- a/media/libmedia/Android.mk +++ b/media/libmedia/Android.mk @@ -12,6 +12,9 @@ LOCAL_SRC_FILES:= \ IMediaPlayerService.cpp \ IMediaPlayerClient.cpp \ IMediaPlayer.cpp \ + IMediaRecorder.cpp \ + mediarecorder.cpp \ + IMediaMetadataRetriever.cpp \ mediametadataretriever.cpp \ ToneGenerator.cpp diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp index f3e4123..bbb9548 100644 --- a/media/libmedia/AudioRecord.cpp +++ b/media/libmedia/AudioRecord.cpp @@ -2,19 +2,20 @@ ** ** 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 +** 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 +** 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 +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and ** limitations under the License. */ +//#define LOG_NDEBUG 0 #define LOG_TAG "AudioRecord" #include @@ -53,24 +54,26 @@ AudioRecord::AudioRecord( uint32_t sampleRate, int format, int channelCount, - int bufferCount, + int frameCount, uint32_t flags, - callback_t cbf, void* user) + callback_t cbf, + void* user, + int notificationFrames) : mStatus(NO_INIT) { mStatus = set(streamType, sampleRate, format, channelCount, - bufferCount, flags, cbf, user); + frameCount, flags, cbf, user, notificationFrames); } AudioRecord::~AudioRecord() { if (mStatus == NO_ERROR) { - if (mPosition) { - releaseBuffer(&mAudioBuffer); - } - // obtainBuffer() will give up with an error - mAudioRecord->stop(); + // Make sure that callback function exits in the case where + // it is looping on buffer empty condition in obtainBuffer(). + // Otherwise the callback thread will never exit. + stop(); if (mClientRecordThread != 0) { + mCblk->cv.signal(); mClientRecordThread->requestExitAndWait(); mClientRecordThread.clear(); } @@ -84,11 +87,15 @@ status_t AudioRecord::set( uint32_t sampleRate, int format, int channelCount, - int bufferCount, + int frameCount, uint32_t flags, - callback_t cbf, void* user) + callback_t cbf, + void* user, + int notificationFrames, + bool threadCanCallJava) { + LOGV("set(): sampleRate %d, channelCount %d, frameCount %d",sampleRate, channelCount, frameCount); if (mAudioFlinger != 0) { return INVALID_OPERATION; } @@ -112,11 +119,6 @@ status_t AudioRecord::set( 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) { @@ -125,22 +127,34 @@ status_t AudioRecord::set( if (channelCount != 1 && channelCount != 2) { return BAD_VALUE; } - if (bufferCount < 2) { + + // TODO: Get input frame count from hardware. + int minFrameCount = 1024*2; + + if (frameCount == 0) { + frameCount = minFrameCount; + } else if (frameCount < minFrameCount) { return BAD_VALUE; } + if (notificationFrames == 0) { + notificationFrames = frameCount/2; + } + // open record channel + status_t status; sp record = audioFlinger->openRecord(getpid(), streamType, - sampleRate, format, channelCount, bufferCount, flags); + sampleRate, format, channelCount, frameCount, flags, &status); if (record == 0) { - return NO_INIT; + LOGE("AudioFlinger could not create record track, status: %d", status); + return status; } sp cblk = record->getCblk(); if (cblk == 0) { return NO_INIT; } if (cbf != 0) { - mClientRecordThread = new ClientRecordThread(*this); + mClientRecordThread = new ClientRecordThread(*this, threadCanCallJava); if (mClientRecordThread == 0) { return NO_INIT; } @@ -153,16 +167,23 @@ status_t AudioRecord::set( mCblkMemory = cblk; mCblk = static_cast(cblk->pointer()); mCblk->buffers = (char*)mCblk + sizeof(audio_track_cblk_t); + mCblk->out = 0; mSampleRate = sampleRate; - mFrameCount = audioFlinger->frameCount(); mFormat = format; - mBufferCount = bufferCount; + // Update buffer size in case it has been limited by AudioFlinger during track creation + mFrameCount = mCblk->frameCount; mChannelCount = channelCount; mActive = 0; mCbf = cbf; + mNotificationFrames = notificationFrames; + mRemainingFrames = notificationFrames; mUserData = user; - mLatency = seconds(mFrameCount) / mSampleRate; - mPosition = 0; + // TODO: add audio hardware input latency here + mLatency = (1000*mFrameCount) / mSampleRate; + mMarkerPosition = 0; + mNewPosition = 0; + mUpdatePeriod = 0; + return NO_ERROR; } @@ -173,7 +194,7 @@ status_t AudioRecord::initCheck() const // ------------------------------------------------------------------------- -nsecs_t AudioRecord::latency() const +uint32_t AudioRecord::latency() const { return mLatency; } @@ -193,9 +214,14 @@ int AudioRecord::channelCount() const return mChannelCount; } -int AudioRecord::bufferCount() const +uint32_t AudioRecord::frameCount() const +{ + return mFrameCount; +} + +int AudioRecord::frameSize() const { - return mBufferCount; + return channelCount()*((format() == AudioSystem::PCM_8_BIT) ? sizeof(uint8_t) : sizeof(int16_t)); } // ------------------------------------------------------------------------- @@ -203,54 +229,60 @@ int AudioRecord::bufferCount() const 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(); - } + sp t = mClientRecordThread; - 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); + LOGV("start"); + + if (t != 0) { + if (t->exitPending()) { + if (t->requestExitAndWait() == WOULD_BLOCK) { + LOGE("AudioRecord::start called from thread"); + return WOULD_BLOCK; } } + t->mLock.lock(); + } + + if (android_atomic_or(1, &mActive) == 0) { + mNewPosition = mCblk->user + mUpdatePeriod; + if (t != 0) { + t->run("ClientRecordThread", THREAD_PRIORITY_AUDIO_CLIENT); + } else { + setpriority(PRIO_PROCESS, 0, THREAD_PRIORITY_AUDIO_CLIENT); + } + ret = mAudioRecord->start(); } - - if (mClientRecordThread != 0) { - mRecordThreadLock.unlock(); + + if (t != 0) { + t->mLock.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(); - } + sp t = mClientRecordThread; + + LOGV("stop"); + + if (t != 0) { + t->mLock.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 (t != 0) { + t->requestExit(); + } else { + setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_NORMAL); } } - - if (mClientRecordThread != 0) { - mRecordThreadLock.unlock(); + + if (t != 0) { + t->mLock.unlock(); } - + return NO_ERROR; } @@ -259,22 +291,74 @@ bool AudioRecord::stopped() const return !mActive; } +status_t AudioRecord::setMarkerPosition(uint32_t marker) +{ + if (mCbf == 0) return INVALID_OPERATION; + + mMarkerPosition = marker; + + return NO_ERROR; +} + +status_t AudioRecord::getMarkerPosition(uint32_t *marker) +{ + if (marker == 0) return BAD_VALUE; + + *marker = mMarkerPosition; + + return NO_ERROR; +} + +status_t AudioRecord::setPositionUpdatePeriod(uint32_t updatePeriod) +{ + if (mCbf == 0) return INVALID_OPERATION; + + uint32_t curPosition; + getPosition(&curPosition); + mNewPosition = curPosition + updatePeriod; + mUpdatePeriod = updatePeriod; + + return NO_ERROR; +} + +status_t AudioRecord::getPositionUpdatePeriod(uint32_t *updatePeriod) +{ + if (updatePeriod == 0) return BAD_VALUE; + + *updatePeriod = mUpdatePeriod; + + return NO_ERROR; +} + +status_t AudioRecord::getPosition(uint32_t *position) +{ + if (position == 0) return BAD_VALUE; + + *position = mCblk->user; + + return NO_ERROR; +} + + // ------------------------------------------------------------------------- status_t AudioRecord::obtainBuffer(Buffer* audioBuffer, bool blocking) { - int active = mActive; + int active; int timeout = 0; status_t result; audio_track_cblk_t* cblk = mCblk; + uint32_t framesReq = audioBuffer->frameCount; + + audioBuffer->frameCount = 0; + audioBuffer->size = 0; - const uint32_t u = cblk->user; - uint32_t s = cblk->server; + uint32_t framesReady = cblk->framesReady(); - if (u == s) { + if (framesReady == 0) { Mutex::Autolock _l(cblk->lock); goto start_loop_here; - while (u == s) { + while (framesReady == 0) { active = mActive; if (UNLIKELY(!active)) return NO_MORE_BUFFERS; @@ -284,40 +368,45 @@ status_t AudioRecord::obtainBuffer(Buffer* audioBuffer, bool blocking) 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); + "user=%08x, server=%08x", cblk->user, cblk->server); timeout = 1; } // read the server count again start_loop_here: - s = cblk->server; + framesReady = cblk->framesReady(); } } LOGW_IF(timeout, "*** SERIOUS WARNING *** obtainBuffer() timed out " "but didn't need to be locked. We recovered, but " - "this shouldn't happen (user=%08x, server=%08x)", u, s); + "this shouldn't happen (user=%08x, server=%08x)", cblk->user, cblk->server); + + if (framesReq > framesReady) { + framesReq = framesReady; + } + + uint32_t u = cblk->user; + uint32_t bufferEnd = cblk->userBase + cblk->frameCount; + + if (u + framesReady > bufferEnd) { + framesReq = bufferEnd - u; + } 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); + audioBuffer->frameCount = framesReq; + audioBuffer->size = framesReq*mChannelCount*sizeof(int16_t); + audioBuffer->raw = (int8_t*)cblk->buffer(u); + active = mActive; return active ? status_t(NO_ERROR) : status_t(STOPPED); } void AudioRecord::releaseBuffer(Buffer* audioBuffer) { - // 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); + cblk->stepUser(audioBuffer->frameCount); } // ------------------------------------------------------------------------- @@ -325,32 +414,38 @@ void AudioRecord::releaseBuffer(Buffer* audioBuffer) 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); - } - } + Buffer audioBuffer; + int8_t *dst = static_cast(buffer); - size_t capacity = mAudioBuffer.size - mPosition; - size_t toRead = userSize < capacity ? userSize : capacity; + if (ssize_t(userSize) < 0) { + // sanity-check. user is most-likely passing an error code. + LOGE("AudioRecord::read(buffer=%p, size=%u (%d)", + buffer, userSize, userSize); + return BAD_VALUE; + } - memcpy(buffer, mAudioBuffer.i8 + mPosition, toRead); + LOGV("read size: %d", userSize); - buffer = static_cast(buffer) + toRead; - mPosition += toRead; - userSize -= toRead; - capacity -= toRead; - read += toRead; + do { - if (capacity == 0) { - mPosition = 0; - releaseBuffer(&mAudioBuffer); + audioBuffer.frameCount = userSize/mChannelCount/sizeof(int16_t); + + status_t err = obtainBuffer(&audioBuffer, true); + if (err < 0) { + // out of buffers, return #bytes written + if (err == status_t(NO_MORE_BUFFERS)) + break; + return ssize_t(err); } + + size_t bytesRead = audioBuffer.size; + memcpy(dst, audioBuffer.i8, bytesRead); + + dst += bytesRead; + userSize -= bytesRead; + read += bytesRead; + + releaseBuffer(&audioBuffer); } while (userSize); return read; @@ -361,28 +456,83 @@ ssize_t AudioRecord::read(void* buffer, size_t userSize) bool AudioRecord::processAudioBuffer(const sp& thread) { Buffer audioBuffer; - bool more; + uint32_t frames = mRemainingFrames; + size_t readSize = 0; + + // Manage marker callback + if (mMarkerPosition > 0) { + if (mCblk->user >= mMarkerPosition) { + mCbf(EVENT_MARKER, mUserData, (void *)&mMarkerPosition); + mMarkerPosition = 0; + } + } + + // Manage new position callback + if (mUpdatePeriod > 0) { + while (mCblk->user >= mNewPosition) { + mCbf(EVENT_NEW_POS, mUserData, (void *)&mNewPosition); + mNewPosition += mUpdatePeriod; + } + } do { - status_t err = obtainBuffer(&audioBuffer, true); + audioBuffer.frameCount = frames; + status_t err = obtainBuffer(&audioBuffer, false); if (err < NO_ERROR) { - LOGE("Error obtaining an audio buffer, giving up."); - return false; + if (err != WOULD_BLOCK) { + LOGE("Error obtaining an audio buffer, giving up."); + return false; + } } - more = mCbf(mUserData, audioBuffer); + if (err == status_t(STOPPED)) return false; + + if (audioBuffer.size == 0) break; + + size_t reqSize = audioBuffer.size; + mCbf(EVENT_MORE_DATA, mUserData, &audioBuffer); + readSize = audioBuffer.size; + + // Sanity check on returned size + if (ssize_t(readSize) <= 0) break; + if (readSize > reqSize) readSize = reqSize; + + audioBuffer.size = readSize; + audioBuffer.frameCount = readSize/mChannelCount/sizeof(int16_t); + frames -= audioBuffer.frameCount; + releaseBuffer(&audioBuffer); - } while (more && !thread->exitPending()); - // stop the track automatically - this->stop(); + } while (frames); + + + // Manage overrun callback + if (mActive && (mCblk->framesAvailable_l() == 0)) { + LOGV("Overrun user: %x, server: %x, flowControlFlag %d", mCblk->user, mCblk->server, mCblk->flowControlFlag); + if (mCblk->flowControlFlag == 0) { + mCbf(EVENT_OVERRUN, mUserData, 0); + mCblk->flowControlFlag = 1; + } + } + // If no data was read, it is likely that obtainBuffer() did + // not find available data in PCM buffer: we release the processor for + // a few millisecond before polling again for available data. + if (readSize == 0) { + usleep(5000); + } + + if (frames == 0) { + mRemainingFrames = mNotificationFrames; + } else { + mRemainingFrames = frames; + } return true; } // ========================================================================= -AudioRecord::ClientRecordThread::ClientRecordThread(AudioRecord& receiver) - : Thread(false), mReceiver(receiver) +AudioRecord::ClientRecordThread::ClientRecordThread(AudioRecord& receiver, bool bCanCallJava) + : Thread(bCanCallJava), mReceiver(receiver) { } diff --git a/media/libmedia/AudioSystem.cpp b/media/libmedia/AudioSystem.cpp index 22de463..a375b55 100644 --- a/media/libmedia/AudioSystem.cpp +++ b/media/libmedia/AudioSystem.cpp @@ -28,6 +28,11 @@ Mutex AudioSystem::gLock; sp AudioSystem::gAudioFlinger; sp AudioSystem::gDeathNotifier; audio_error_callback AudioSystem::gAudioErrorCallback = NULL; +// Cached values +int AudioSystem::gOutSamplingRate = 0; +int AudioSystem::gOutFrameCount = 0; +uint32_t AudioSystem::gOutLatency = 0; + // establish binder interface to AudioFlinger service const sp& AudioSystem::get_audio_flinger() @@ -47,11 +52,15 @@ const sp& AudioSystem::get_audio_flinger() gDeathNotifier = new DeathNotifier(); } else { if (gAudioErrorCallback) { - gAudioErrorCallback(NO_ERROR); + gAudioErrorCallback(NO_ERROR); } } binder->linkToDeath(gDeathNotifier); gAudioFlinger = interface_cast(binder); + // Cache frequently accessed parameters + gOutFrameCount = (int)gAudioFlinger->frameCount(); + gOutSamplingRate = (int)gAudioFlinger->sampleRate(); + gOutLatency = gAudioFlinger->latency(); } LOGE_IF(gAudioFlinger==0, "no AudioFlinger!?"); return gAudioFlinger; @@ -71,7 +80,7 @@ status_t AudioSystem::isSpeakerphoneOn(bool* state) { } status_t AudioSystem::bluetoothSco(bool state) { - uint32_t mask = ROUTE_BLUETOOTH; + uint32_t mask = ROUTE_BLUETOOTH_SCO; uint32_t routes = state ? mask : ROUTE_EARPIECE; return setRouting(MODE_IN_CALL, routes, ROUTE_ALL); } @@ -79,7 +88,7 @@ status_t AudioSystem::bluetoothSco(bool state) { status_t AudioSystem::isBluetoothScoOn(bool* state) { uint32_t routes = 0; status_t s = getRouting(MODE_IN_CALL, &routes); - *state = !!(routes & ROUTE_BLUETOOTH); + *state = !!(routes & ROUTE_BLUETOOTH_SCO); return s; } @@ -235,11 +244,50 @@ int AudioSystem::logToLinear(float volume) return volume ? 100 - int(dBConvertInverse * log(volume) + 0.5) : 0; } +status_t AudioSystem::getOutputSamplingRate(int* samplingRate) +{ + if (gOutSamplingRate == 0) { + const sp& af = AudioSystem::get_audio_flinger(); + if (af == 0) return PERMISSION_DENIED; + // gOutSamplingRate is updated by get_audio_flinger() + } + *samplingRate = gOutSamplingRate; + + return NO_ERROR; +} + +status_t AudioSystem::getOutputFrameCount(int* frameCount) +{ + if (gOutFrameCount == 0) { + const sp& af = AudioSystem::get_audio_flinger(); + if (af == 0) return PERMISSION_DENIED; + // gOutSamplingRate is updated by get_audio_flinger() + } + *frameCount = gOutFrameCount; + return NO_ERROR; +} + +status_t AudioSystem::getOutputLatency(uint32_t* latency) +{ + if (gOutLatency == 0) { + const sp& af = AudioSystem::get_audio_flinger(); + if (af == 0) return PERMISSION_DENIED; + // gOutLatency is updated by get_audio_flinger() + } + *latency = gOutLatency; + + return NO_ERROR; +} + // --------------------------------------------------------------------------- -void AudioSystem::DeathNotifier::binderDied(const wp& who) { +void AudioSystem::DeathNotifier::binderDied(const wp& who) { Mutex::Autolock _l(AudioSystem::gLock); AudioSystem::gAudioFlinger.clear(); + AudioSystem::gOutSamplingRate = 0; + AudioSystem::gOutFrameCount = 0; + AudioSystem::gOutLatency = 0; + if (gAudioErrorCallback) { gAudioErrorCallback(DEAD_OBJECT); } diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp index 298170a..d4f2e5a 100644 --- a/media/libmedia/AudioTrack.cpp +++ b/media/libmedia/AudioTrack.cpp @@ -2,16 +2,16 @@ ** ** 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 +** 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 +** 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 +** 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. */ @@ -21,6 +21,7 @@ #include #include +#include #include #include @@ -44,22 +45,25 @@ namespace android { // --------------------------------------------------------------------------- -static volatile size_t gFrameCount = 0; - -size_t AudioTrack::frameCount() +AudioTrack::AudioTrack() + : mStatus(NO_INIT) { - if (gFrameCount) return gFrameCount; - const sp& af = AudioSystem::get_audio_flinger(); - if (af == 0) return PERMISSION_DENIED; - gFrameCount = af->frameCount(); - return gFrameCount; } -// --------------------------------------------------------------------------- - -AudioTrack::AudioTrack() +AudioTrack::AudioTrack( + int streamType, + uint32_t sampleRate, + int format, + int channelCount, + int frameCount, + uint32_t flags, + callback_t cbf, + void* user, + int notificationFrames) : mStatus(NO_INIT) { + mStatus = set(streamType, sampleRate, format, channelCount, + frameCount, flags, cbf, user, notificationFrames, 0); } AudioTrack::AudioTrack( @@ -67,24 +71,28 @@ AudioTrack::AudioTrack( uint32_t sampleRate, int format, int channelCount, - int bufferCount, + const sp& sharedBuffer, uint32_t flags, - callback_t cbf, void* user) + callback_t cbf, + void* user, + int notificationFrames) : mStatus(NO_INIT) { mStatus = set(streamType, sampleRate, format, channelCount, - bufferCount, flags, cbf, user); + 0, flags, cbf, user, notificationFrames, sharedBuffer); } AudioTrack::~AudioTrack() { + LOGV_IF(mSharedBuffer != 0, "Destructor sharedBuffer: %p", mSharedBuffer->pointer()); + if (mStatus == NO_ERROR) { - if (mPosition) { - releaseBuffer(&mAudioBuffer); - } - // obtainBuffer() will give up with an error - mAudioTrack->stop(); + // Make sure that callback function exits in the case where + // it is looping on buffer full condition in obtainBuffer(). + // Otherwise the callback thread will never exit. + stop(); if (mAudioTrackThread != 0) { + mCblk->cv.signal(); mAudioTrackThread->requestExitAndWait(); mAudioTrackThread.clear(); } @@ -98,11 +106,17 @@ status_t AudioTrack::set( uint32_t sampleRate, int format, int channelCount, - int bufferCount, + int frameCount, uint32_t flags, - callback_t cbf, void* user) + callback_t cbf, + void* user, + int notificationFrames, + const sp& sharedBuffer, + bool threadCanCallJava) { + LOGV_IF(sharedBuffer != 0, "sharedBuffer: %p, size: %d", sharedBuffer->pointer(), sharedBuffer->size()); + if (mAudioFlinger != 0) { LOGE("Track already in use"); return INVALID_OPERATION; @@ -113,13 +127,26 @@ status_t AudioTrack::set( LOGE("Could not get audioflinger"); return NO_INIT; } + int afSampleRate; + if (AudioSystem::getOutputSamplingRate(&afSampleRate) != NO_ERROR) { + return NO_INIT; + } + int afFrameCount; + if (AudioSystem::getOutputFrameCount(&afFrameCount) != NO_ERROR) { + return NO_INIT; + } + uint32_t afLatency; + if (AudioSystem::getOutputLatency(&afLatency) != NO_ERROR) { + return NO_INIT; + } + // handle default values first. if (streamType == DEFAULT) { streamType = MUSIC; } if (sampleRate == 0) { - sampleRate = audioFlinger->sampleRate(); + sampleRate = afSampleRate; } // these below should probably come from the audioFlinger too... if (format == 0) { @@ -128,12 +155,10 @@ status_t AudioTrack::set( if (channelCount == 0) { channelCount = 2; } - if (bufferCount == 0) { - bufferCount = 2; - } // validate parameters - if (format != AudioSystem::PCM_16_BIT) { + if (((format != AudioSystem::PCM_8_BIT) || mSharedBuffer != 0) && + (format != AudioSystem::PCM_16_BIT)) { LOGE("Invalid format"); return BAD_VALUE; } @@ -141,17 +166,51 @@ status_t AudioTrack::set( LOGE("Invalid channel number"); return BAD_VALUE; } - if (bufferCount < 2) { - LOGE("Invalid buffer count"); - return BAD_VALUE; + + // Ensure that buffer depth covers at least audio hardware latency + uint32_t minBufCount = afLatency / ((1000 * afFrameCount)/afSampleRate); + // When playing from shared buffer, playback will start even if last audioflinger + // block is partly filled. + if (sharedBuffer != 0 && minBufCount > 1) { + minBufCount--; + } + + int minFrameCount = (afFrameCount*sampleRate*minBufCount)/afSampleRate; + + if (sharedBuffer == 0) { + if (frameCount == 0) { + frameCount = minFrameCount; + } + if (notificationFrames == 0) { + notificationFrames = frameCount/2; + } + // Make sure that application is notified with sufficient margin + // before underrun + if (notificationFrames > frameCount/2) { + notificationFrames = frameCount/2; + } + } else { + // Ensure that buffer alignment matches channelcount + if (((uint32_t)sharedBuffer->pointer() & (channelCount | 1)) != 0) { + LOGE("Invalid buffer alignement: address %p, channelCount %d", sharedBuffer->pointer(), channelCount); + return BAD_VALUE; + } + frameCount = sharedBuffer->size()/channelCount/sizeof(int16_t); + } + + if (frameCount < minFrameCount) { + LOGE("Invalid buffer size: minFrameCount %d, frameCount %d", minFrameCount, frameCount); + return BAD_VALUE; } // create the track + status_t status; sp track = audioFlinger->createTrack(getpid(), - streamType, sampleRate, format, channelCount, bufferCount, flags); + streamType, sampleRate, format, channelCount, frameCount, flags, sharedBuffer, &status); + if (track == 0) { - LOGE("AudioFlinger could not create track"); - return NO_INIT; + LOGE("AudioFlinger could not create track, status: %d", status); + return status; } sp cblk = track->getCblk(); if (cblk == 0) { @@ -159,7 +218,7 @@ status_t AudioTrack::set( return NO_INIT; } if (cbf != 0) { - mAudioTrackThread = new AudioTrackThread(*this); + mAudioTrackThread = new AudioTrackThread(*this, threadCanCallJava); if (mAudioTrackThread == 0) { LOGE("Could not create callback thread"); return NO_INIT; @@ -172,23 +231,34 @@ status_t AudioTrack::set( mAudioTrack = track; mCblkMemory = cblk; mCblk = static_cast(cblk->pointer()); - mCblk->buffers = (char*)mCblk + sizeof(audio_track_cblk_t); + if (sharedBuffer == 0) { + mCblk->buffers = (char*)mCblk + sizeof(audio_track_cblk_t); + } else { + mCblk->buffers = sharedBuffer->pointer(); + } + mCblk->out = 1; 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; + // Update buffer size in case it has been limited by AudioFlinger during track creation + mFrameCount = mCblk->frameCount; mChannelCount = channelCount; + mSharedBuffer = sharedBuffer; mMuted = false; mActive = 0; - mReserved = 0; mCbf = cbf; + mNotificationFrames = notificationFrames; + mRemainingFrames = notificationFrames; mUserData = user; - mLatency = seconds(mFrameCount) / mSampleRate; - mPosition = 0; + mLatency = afLatency + (1000*mFrameCount) / mSampleRate; + mLoopCount = 0; + mMarkerPosition = 0; + mNewPosition = 0; + mUpdatePeriod = 0; + return NO_ERROR; } @@ -199,7 +269,7 @@ status_t AudioTrack::initCheck() const // ------------------------------------------------------------------------- -nsecs_t AudioTrack::latency() const +uint32_t AudioTrack::latency() const { return mLatency; } @@ -224,9 +294,19 @@ int AudioTrack::channelCount() const return mChannelCount; } -int AudioTrack::bufferCount() const +uint32_t AudioTrack::frameCount() const +{ + return mFrameCount; +} + +int AudioTrack::frameSize() const { - return mBufferCount; + return channelCount()*((format() == AudioSystem::PCM_8_BIT) ? sizeof(uint8_t) : sizeof(int16_t)); +} + +sp& AudioTrack::sharedBuffer() +{ + return mSharedBuffer; } // ------------------------------------------------------------------------- @@ -247,6 +327,12 @@ void AudioTrack::start() } if (android_atomic_or(1, &mActive) == 0) { + if (mSharedBuffer != 0) { + // Force buffer full condition as data is already present in shared memory + mCblk->user = mFrameCount; + mCblk->flowControlFlag = 0; + } + mNewPosition = mCblk->server + mUpdatePeriod; if (t != 0) { t->run("AudioTrackThread", THREAD_PRIORITY_AUDIO_CLIENT); } else { @@ -270,15 +356,20 @@ void AudioTrack::stop() } 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); - } + // Cancel loops (If we are in the middle of a loop, playback + // would not stop until loopCount reaches 0). + setLoop(0, 0, 0); + // Force flush if a shared buffer is used otherwise audioflinger + // will not stop before end of buffer is reached. + if (mSharedBuffer != 0) { + flush(); + } + if (t != 0) { + t->requestExit(); + } else { + setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_NORMAL); + } } if (t != 0) { @@ -294,6 +385,7 @@ bool AudioTrack::stopped() const void AudioTrack::flush() { LOGV("flush"); + if (!mActive) { mCblk->lock.lock(); mAudioTrack->flush(); @@ -341,7 +433,16 @@ void AudioTrack::getVolume(float* left, float* right) void AudioTrack::setSampleRate(int rate) { + int afSamplingRate; + + if (AudioSystem::getOutputSamplingRate(&afSamplingRate) != NO_ERROR) { + return; + } + // Resampler implementation limits input sampling rate to 2 x output sampling rate. + if (rate > afSamplingRate*2) rate = afSamplingRate*2; + if (rate > MAX_SAMPLE_RATE) rate = MAX_SAMPLE_RATE; + mCblk->sampleRate = rate; } @@ -350,6 +451,129 @@ uint32_t AudioTrack::getSampleRate() return uint32_t(mCblk->sampleRate); } +status_t AudioTrack::setLoop(uint32_t loopStart, uint32_t loopEnd, int loopCount) +{ + audio_track_cblk_t* cblk = mCblk; + + + Mutex::Autolock _l(cblk->lock); + + if (loopCount == 0) { + cblk->loopStart = UINT_MAX; + cblk->loopEnd = UINT_MAX; + cblk->loopCount = 0; + mLoopCount = 0; + return NO_ERROR; + } + + if (loopStart >= loopEnd || + loopStart < cblk->user || + loopEnd - loopStart > mFrameCount) { + LOGW("setLoop invalid value: loopStart %d, loopEnd %d, loopCount %d, framecount %d, user %d", loopStart, loopEnd, loopCount, mFrameCount, cblk->user); + return BAD_VALUE; + } + // TODO handle shared buffer here: limit loop end to framecount + + cblk->loopStart = loopStart; + cblk->loopEnd = loopEnd; + cblk->loopCount = loopCount; + mLoopCount = loopCount; + + return NO_ERROR; +} + +status_t AudioTrack::getLoop(uint32_t *loopStart, uint32_t *loopEnd, int *loopCount) +{ + if (loopStart != 0) { + *loopStart = mCblk->loopStart; + } + if (loopEnd != 0) { + *loopEnd = mCblk->loopEnd; + } + if (loopCount != 0) { + if (mCblk->loopCount < 0) { + *loopCount = -1; + } else { + *loopCount = mCblk->loopCount; + } + } + + return NO_ERROR; +} + +status_t AudioTrack::setMarkerPosition(uint32_t marker) +{ + if (mCbf == 0) return INVALID_OPERATION; + + mMarkerPosition = marker; + + return NO_ERROR; +} + +status_t AudioTrack::getMarkerPosition(uint32_t *marker) +{ + if (marker == 0) return BAD_VALUE; + + *marker = mMarkerPosition; + + return NO_ERROR; +} + +status_t AudioTrack::setPositionUpdatePeriod(uint32_t updatePeriod) +{ + if (mCbf == 0) return INVALID_OPERATION; + + uint32_t curPosition; + getPosition(&curPosition); + mNewPosition = curPosition + updatePeriod; + mUpdatePeriod = updatePeriod; + + return NO_ERROR; +} + +status_t AudioTrack::getPositionUpdatePeriod(uint32_t *updatePeriod) +{ + if (updatePeriod == 0) return BAD_VALUE; + + *updatePeriod = mUpdatePeriod; + + return NO_ERROR; +} + +status_t AudioTrack::setPosition(uint32_t position) +{ + Mutex::Autolock _l(mCblk->lock); + + if (!stopped()) return INVALID_OPERATION; + + if (position > mCblk->user) return BAD_VALUE; + + mCblk->server = position; + mCblk->forceReady = 1; + + return NO_ERROR; +} + +status_t AudioTrack::getPosition(uint32_t *position) +{ + if (position == 0) return BAD_VALUE; + + *position = mCblk->server; + + return NO_ERROR; +} + +status_t AudioTrack::reload() +{ + if (!stopped()) return INVALID_OPERATION; + + flush(); + + mCblk->stepUser(mFrameCount); + + return NO_ERROR; +} + // ------------------------------------------------------------------------- status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, bool blocking) @@ -358,21 +582,17 @@ status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, bool blocking) int timeout = 0; status_t result; audio_track_cblk_t* cblk = mCblk; + uint32_t framesReq = audioBuffer->frameCount; - 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; + audioBuffer->frameCount = 0; + audioBuffer->size = 0; - 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; + uint32_t framesAvail = cblk->framesAvailable(); - LOGW_IF(u_seq < s_seq, "user doesn't fill buffers fast enough"); - - if (u_seq > s_seq && u_buf == s_buf) { + if (framesAvail == 0) { Mutex::Autolock _l(cblk->lock); goto start_loop_here; - while (u_seq > s_seq && u_buf == s_buf) { + while (framesAvail == 0) { active = mActive; if (UNLIKELY(!active)) { LOGV("Not active and NO_MORE_BUFFERS"); @@ -384,89 +604,101 @@ status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, bool blocking) 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); + "user=%08x, server=%08x", cblk->user, cblk->server); 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; + framesAvail = cblk->framesAvailable_l(); } } + if (framesReq > framesAvail) { + framesReq = framesAvail; + } + + uint32_t u = cblk->user; + uint32_t bufferEnd = cblk->userBase + cblk->frameCount; + + if (u + framesReq > bufferEnd) { + framesReq = bufferEnd - u; + } + LOGW_IF(timeout, "*** SERIOUS WARNING *** obtainBuffer() timed out " "but didn't need to be locked. We recovered, but " - "this shouldn't happen (user=%08x, server=%08x)", u, s); + "this shouldn't happen (user=%08x, server=%08x)", cblk->user, cblk->server); 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); + audioBuffer->format = AudioSystem::PCM_16_BIT; + audioBuffer->frameCount = framesReq; + audioBuffer->size = framesReq*mChannelCount*sizeof(int16_t); + audioBuffer->raw = (int8_t *)cblk->buffer(u); active = mActive; return active ? status_t(NO_ERROR) : status_t(STOPPED); } void AudioTrack::releaseBuffer(Buffer* audioBuffer) { - // 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); + cblk->stepUser(audioBuffer->frameCount); } // ------------------------------------------------------------------------- ssize_t AudioTrack::write(const void* buffer, size_t userSize) { + + if (mSharedBuffer != 0) return INVALID_OPERATION; + if (ssize_t(userSize) < 0) { // sanity-check. user is most-likely passing an error code. - LOGE("AudioTrack::write(buffer=%p, size=%u (%d)", + 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; + const int8_t *src = (const int8_t *)buffer; + Buffer audioBuffer; + 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); - } + audioBuffer.frameCount = userSize/mChannelCount; + if (mFormat == AudioSystem::PCM_16_BIT) { + audioBuffer.frameCount >>= 1; } - size_t capacity = mAudioBuffer.size - mPosition; - size_t toWrite = userSize < capacity ? userSize : capacity; + status_t err = obtainBuffer(&audioBuffer, true); + if (err < 0) { + // out of buffers, return #bytes written + if (err == status_t(NO_MORE_BUFFERS)) + break; + return ssize_t(err); + } - memcpy(mAudioBuffer.i8 + mPosition, buffer, toWrite); - buffer = static_cast(buffer) + toWrite; - mPosition += toWrite; + size_t toWrite; + if (mFormat == AudioSystem::PCM_8_BIT) { + // Divide capacity by 2 to take expansion into account + toWrite = audioBuffer.size>>1; + // 8 to 16 bit conversion + int count = toWrite; + int16_t *dst = (int16_t *)(audioBuffer.i8); + while(count--) { + *dst++ = (int16_t)(*src++^0x80) << 8; + } + }else { + toWrite = audioBuffer.size; + memcpy(audioBuffer.i8, src, toWrite); + src += toWrite; + } userSize -= toWrite; - capacity -= toWrite; written += toWrite; - if (capacity == 0) { - mPosition = 0; - releaseBuffer(&mAudioBuffer); - } + releaseBuffer(&audioBuffer); } while (userSize); return written; @@ -477,16 +709,115 @@ ssize_t AudioTrack::write(const void* buffer, size_t userSize) bool AudioTrack::processAudioBuffer(const sp& thread) { Buffer audioBuffer; + uint32_t frames; + size_t writtenSize = 0; + + // Manage underrun callback + if (mActive && (mCblk->framesReady() == 0)) { + LOGV("Underrun user: %x, server: %x, flowControlFlag %d", mCblk->user, mCblk->server, mCblk->flowControlFlag); + if (mCblk->flowControlFlag == 0) { + mCbf(EVENT_UNDERRUN, mUserData, 0); + if (mCblk->server == mCblk->frameCount) { + mCbf(EVENT_BUFFER_END, mUserData, 0); + } + mCblk->flowControlFlag = 1; + if (mSharedBuffer != 0) return false; + } + } + + // Manage loop end callback + while (mLoopCount > mCblk->loopCount) { + int loopCount = -1; + mLoopCount--; + if (mLoopCount >= 0) loopCount = mLoopCount; + + mCbf(EVENT_LOOP_END, mUserData, (void *)&loopCount); + } - status_t err = obtainBuffer(&audioBuffer, true); - if (err < NO_ERROR) { - LOGE("Error obtaining an audio buffer, giving up."); - return false; + // Manage marker callback + if(mMarkerPosition > 0) { + if (mCblk->server >= mMarkerPosition) { + mCbf(EVENT_MARKER, mUserData, (void *)&mMarkerPosition); + mMarkerPosition = 0; + } } - if (err == status_t(STOPPED)) return false; - mCbf(mUserData, audioBuffer); - releaseBuffer(&audioBuffer); + // Manage new position callback + if(mUpdatePeriod > 0) { + while (mCblk->server >= mNewPosition) { + mCbf(EVENT_NEW_POS, mUserData, (void *)&mNewPosition); + mNewPosition += mUpdatePeriod; + } + } + + // If Shared buffer is used, no data is requested from client. + if (mSharedBuffer != 0) { + frames = 0; + } else { + frames = mRemainingFrames; + } + + do { + + audioBuffer.frameCount = frames; + + status_t err = obtainBuffer(&audioBuffer, false); + if (err < NO_ERROR) { + if (err != WOULD_BLOCK) { + LOGE("Error obtaining an audio buffer, giving up."); + return false; + } + } + if (err == status_t(STOPPED)) return false; + + if (audioBuffer.size == 0) break; + + // Divide buffer size by 2 to take into account the expansion + // due to 8 to 16 bit conversion: the callback must fill only half + // of the destination buffer + if (mFormat == AudioSystem::PCM_8_BIT) { + audioBuffer.size >>= 1; + } + + size_t reqSize = audioBuffer.size; + mCbf(EVENT_MORE_DATA, mUserData, &audioBuffer); + writtenSize = audioBuffer.size; + + // Sanity check on returned size + if (ssize_t(writtenSize) <= 0) break; + if (writtenSize > reqSize) writtenSize = reqSize; + + if (mFormat == AudioSystem::PCM_8_BIT) { + // 8 to 16 bit conversion + const int8_t *src = audioBuffer.i8 + writtenSize-1; + int count = writtenSize; + int16_t *dst = audioBuffer.i16 + writtenSize-1; + while(count--) { + *dst-- = (int16_t)(*src--^0x80) << 8; + } + writtenSize <<= 1; + } + + audioBuffer.size = writtenSize; + audioBuffer.frameCount = writtenSize/mChannelCount/sizeof(int16_t); + frames -= audioBuffer.frameCount; + + releaseBuffer(&audioBuffer); + } + while (frames); + + // If no data was written, it is likely that obtainBuffer() did + // not find room in PCM buffer: we release the processor for + // a few millisecond before polling again for available room. + if (writtenSize == 0) { + usleep(5000); + } + + if (frames == 0) { + mRemainingFrames = mNotificationFrames; + } else { + mRemainingFrames = frames; + } return true; } @@ -500,11 +831,11 @@ status_t AudioTrack::dump(int fd, const Vector& args) const 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); + snprintf(buffer, 255, " format(%d), channel count(%d), frame count(%d)\n", mFormat, mChannelCount, mFrameCount); result.append(buffer); - snprintf(buffer, 255, " sample rate(%d), status(%d), muted(%d), reserved(%d)\n", mSampleRate, mStatus, mMuted, mReserved); + snprintf(buffer, 255, " sample rate(%d), status(%d), muted(%d)\n", mSampleRate, mStatus, mMuted); result.append(buffer); - snprintf(buffer, 255, " active(%d), latency (%lld), position(%d)\n", mActive, mLatency, mPosition); + snprintf(buffer, 255, " active(%d), latency (%d)\n", mActive, mLatency); result.append(buffer); ::write(fd, result.string(), result.size()); return NO_ERROR; @@ -512,8 +843,8 @@ status_t AudioTrack::dump(int fd, const Vector& args) const // ========================================================================= -AudioTrack::AudioTrackThread::AudioTrackThread(AudioTrack& receiver) - : Thread(false), mReceiver(receiver) +AudioTrack::AudioTrackThread::AudioTrackThread(AudioTrack& receiver, bool bCanCallJava) + : Thread(bCanCallJava), mReceiver(receiver) { } @@ -534,25 +865,35 @@ void AudioTrack::AudioTrackThread::onFirstRef() // ========================================================================= audio_track_cblk_t::audio_track_cblk_t() - : user(0), server(0), volumeLR(0), buffers(0), size(0) + : user(0), server(0), userBase(0), serverBase(0), buffers(0), frameCount(0), + loopStart(UINT_MAX), loopEnd(UINT_MAX), loopCount(0), volumeLR(0), flowControlFlag(1), forceReady(0) { } -uint32_t audio_track_cblk_t::stepUser(int bufferCount) +uint32_t audio_track_cblk_t::stepUser(uint32_t frameCount) { 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; + + u += frameCount; + // Ensure that user is never ahead of server for AudioRecord + if (!out && u > this->server) { + LOGW("stepServer occured after track reset"); + u = this->server; + } + + if (u >= userBase + this->frameCount) { + userBase += this->frameCount; + } + + this->user = u; + + // Clear flow control error condition as new data has been written/read to/from buffer. + flowControlFlag = 0; + return u; } -bool audio_track_cblk_t::stepServer(int bufferCount) +bool audio_track_cblk_t::stepServer(uint32_t frameCount) { // the code below simulates lock-with-timeout // we MUST do this to protect the AudioFlinger server @@ -570,24 +911,83 @@ bool audio_track_cblk_t::stepServer(int bufferCount) } 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 += frameCount; + // It is possible that we receive a flush() + // while the mixer is processing a block: in this case, + // stepServer() is called After the flush() has reset u & s and + // we have s > u + if (out && s > this->user) { + LOGW("stepServer occured after track reset"); + s = this->user; } - s = s_seq | s_buf; - this->server = s; + if (s >= loopEnd) { + LOGW_IF(s > loopEnd, "stepServer: s %u > loopEnd %u", s, loopEnd); + s = loopStart; + if (--loopCount == 0) { + loopEnd = UINT_MAX; + loopStart = UINT_MAX; + } + } + if (s >= serverBase + this->frameCount) { + serverBase += this->frameCount; + } + + this->server = s; + cv.signal(); lock.unlock(); return true; } -void* audio_track_cblk_t::buffer(int id) const +void* audio_track_cblk_t::buffer(uint32_t offset) const { - return (char*)this->buffers + id * this->size; + return (int16_t *)this->buffers + (offset-userBase)*this->channels; +} + +uint32_t audio_track_cblk_t::framesAvailable() +{ + Mutex::Autolock _l(lock); + return framesAvailable_l(); +} + +uint32_t audio_track_cblk_t::framesAvailable_l() +{ + uint32_t u = this->user; + uint32_t s = this->server; + + if (out) { + if (u < loopEnd) { + return s + frameCount - u; + } else { + uint32_t limit = (s < loopStart) ? s : loopStart; + return limit + frameCount - u; + } + } else { + return frameCount + u - s; + } +} + +uint32_t audio_track_cblk_t::framesReady() +{ + uint32_t u = this->user; + uint32_t s = this->server; + + if (out) { + if (u < loopEnd) { + return u - s; + } else { + Mutex::Autolock _l(lock); + if (loopCount >= 0) { + return (loopEnd - loopStart)*loopCount + u - s; + } else { + return UINT_MAX; + } + } + } else { + return s - u; + } } // ------------------------------------------------------------------------- diff --git a/media/libmedia/IAudioFlinger.cpp b/media/libmedia/IAudioFlinger.cpp index 474381b..018ea6c 100644 --- a/media/libmedia/IAudioFlinger.cpp +++ b/media/libmedia/IAudioFlinger.cpp @@ -2,16 +2,16 @@ ** ** 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 +** 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 +** 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 +** 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. */ @@ -34,6 +34,7 @@ enum { CHANNEL_COUNT, FORMAT, FRAME_COUNT, + LATENCY, SET_MASTER_VOLUME, SET_MASTER_MUTE, MASTER_VOLUME, @@ -66,8 +67,10 @@ public: uint32_t sampleRate, int format, int channelCount, - int bufferCount, - uint32_t flags) + int frameCount, + uint32_t flags, + const sp& sharedBuffer, + status_t *status) { Parcel data, reply; data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); @@ -76,13 +79,17 @@ public: data.writeInt32(sampleRate); data.writeInt32(format); data.writeInt32(channelCount); - data.writeInt32(bufferCount); + data.writeInt32(frameCount); data.writeInt32(flags); - status_t status = remote()->transact(CREATE_TRACK, data, &reply); - if ( status != NO_ERROR) { - LOGE("createTrack error: %s", strerror(-status)); + data.writeStrongBinder(sharedBuffer->asBinder()); + status_t lStatus = remote()->transact(CREATE_TRACK, data, &reply); + if (lStatus != NO_ERROR) { + LOGE("createTrack error: %s", strerror(-lStatus)); + } + lStatus = reply.readInt32(); + if (status) { + *status = lStatus; } - return interface_cast(reply.readStrongBinder()); } @@ -92,8 +99,9 @@ public: uint32_t sampleRate, int format, int channelCount, - int bufferCount, - uint32_t flags) + int frameCount, + uint32_t flags, + status_t *status) { Parcel data, reply; data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); @@ -102,9 +110,13 @@ public: data.writeInt32(sampleRate); data.writeInt32(format); data.writeInt32(channelCount); - data.writeInt32(bufferCount); + data.writeInt32(frameCount); data.writeInt32(flags); remote()->transact(OPEN_RECORD, data, &reply); + status_t lStatus = reply.readInt32(); + if (status) { + *status = lStatus; + } return interface_cast(reply.readStrongBinder()); } @@ -140,6 +152,14 @@ public: return reply.readInt32(); } + virtual uint32_t latency() const + { + Parcel data, reply; + data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); + remote()->transact(LATENCY, data, &reply); + return reply.readInt32(); + } + virtual status_t setMasterVolume(float value) { Parcel data, reply; @@ -308,9 +328,12 @@ status_t BnAudioFlinger::onTransact( int channelCount = data.readInt32(); size_t bufferCount = data.readInt32(); uint32_t flags = data.readInt32(); - sp track = createTrack(pid, + sp buffer = interface_cast(data.readStrongBinder()); + status_t status; + sp track = createTrack(pid, streamType, sampleRate, format, - channelCount, bufferCount, flags); + channelCount, bufferCount, flags, buffer, &status); + reply->writeInt32(status); reply->writeStrongBinder(track->asBinder()); return NO_ERROR; } break; @@ -323,8 +346,10 @@ status_t BnAudioFlinger::onTransact( int channelCount = data.readInt32(); size_t bufferCount = data.readInt32(); uint32_t flags = data.readInt32(); + status_t status; sp record = openRecord(pid, streamType, - sampleRate, format, channelCount, bufferCount, flags); + sampleRate, format, channelCount, bufferCount, flags, &status); + reply->writeInt32(status); reply->writeStrongBinder(record->asBinder()); return NO_ERROR; } break; @@ -348,7 +373,12 @@ status_t BnAudioFlinger::onTransact( reply->writeInt32( frameCount() ); return NO_ERROR; } break; - case SET_MASTER_VOLUME: { + case LATENCY: { + CHECK_INTERFACE(IAudioFlinger, data, reply); + reply->writeInt32( latency() ); + return NO_ERROR; + } break; + case SET_MASTER_VOLUME: { CHECK_INTERFACE(IAudioFlinger, data, reply); reply->writeInt32( setMasterVolume(data.readFloat()) ); return NO_ERROR; diff --git a/media/libmedia/IMediaMetadataRetriever.cpp b/media/libmedia/IMediaMetadataRetriever.cpp new file mode 100644 index 0000000..615ae37 --- /dev/null +++ b/media/libmedia/IMediaMetadataRetriever.cpp @@ -0,0 +1,218 @@ +/* +** +** Copyright (C) 2008 The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include +#include +#include +#include +#include + +namespace android { + +enum { + DISCONNECT = IBinder::FIRST_CALL_TRANSACTION, + SET_DATA_SOURCE_URL, + SET_DATA_SOURCE_FD, + SET_MODE, + GET_MODE, + CAPTURE_FRAME, + EXTARCT_ALBUM_ART, + EXTRACT_METADATA, +}; + +class BpMediaMetadataRetriever: public BpInterface +{ +public: + BpMediaMetadataRetriever(const sp& impl) + : BpInterface(impl) + { + } + + // disconnect from media metadata retriever service + void disconnect() + { + Parcel data, reply; + data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor()); + remote()->transact(DISCONNECT, data, &reply); + } + + status_t setDataSource(const char* srcUrl) + { + Parcel data, reply; + data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor()); + data.writeCString(srcUrl); + remote()->transact(SET_DATA_SOURCE_URL, data, &reply); + return reply.readInt32(); + } + + status_t setDataSource(int fd, int64_t offset, int64_t length) + { + Parcel data, reply; + data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor()); + data.writeFileDescriptor(fd); + data.writeInt64(offset); + data.writeInt64(length); + remote()->transact(SET_DATA_SOURCE_FD, data, &reply); + return reply.readInt32(); + } + + status_t setMode(int mode) + { + Parcel data, reply; + data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor()); + data.writeInt32(mode); + remote()->transact(SET_MODE, data, &reply); + return reply.readInt32(); + } + + status_t getMode(int* mode) const + { + Parcel data, reply; + data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor()); + remote()->transact(GET_MODE, data, &reply); + *mode = reply.readInt32(); + return reply.readInt32(); + } + + sp captureFrame() + { + Parcel data, reply; + data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor()); + remote()->transact(CAPTURE_FRAME, data, &reply); + status_t ret = reply.readInt32(); + if (ret != NO_ERROR) { + return NULL; + } + return interface_cast(reply.readStrongBinder()); + } + + sp extractAlbumArt() + { + Parcel data, reply; + data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor()); + remote()->transact(EXTARCT_ALBUM_ART, data, &reply); + status_t ret = reply.readInt32(); + if (ret != NO_ERROR) { + return NULL; + } + return interface_cast(reply.readStrongBinder()); + } + + const char* extractMetadata(int keyCode) + { + Parcel data, reply; + data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor()); + data.writeInt32(keyCode); + remote()->transact(EXTRACT_METADATA, data, &reply); + status_t ret = reply.readInt32(); + if (ret != NO_ERROR) { + return NULL; + } + return reply.readCString(); + } +}; + +IMPLEMENT_META_INTERFACE(MediaMetadataRetriever, "android.hardware.IMediaMetadataRetriever"); + +// ---------------------------------------------------------------------- + +#define CHECK_INTERFACE(interface, data, reply) \ + do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \ + LOGW("Call incorrectly routed to " #interface); \ + return PERMISSION_DENIED; \ + } } while (0) + +status_t BnMediaMetadataRetriever::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch (code) { + case DISCONNECT: { + CHECK_INTERFACE(IMediaMetadataRetriever, data, reply); + disconnect(); + return NO_ERROR; + } break; + case SET_DATA_SOURCE_URL: { + CHECK_INTERFACE(IMediaMetadataRetriever, data, reply); + const char* srcUrl = data.readCString(); + reply->writeInt32(setDataSource(srcUrl)); + return NO_ERROR; + } break; + case SET_DATA_SOURCE_FD: { + CHECK_INTERFACE(IMediaMetadataRetriever, data, reply); + int fd = dup(data.readFileDescriptor()); + int64_t offset = data.readInt64(); + int64_t length = data.readInt64(); + reply->writeInt32(setDataSource(fd, offset, length)); + return NO_ERROR; + } break; + case SET_MODE: { + CHECK_INTERFACE(IMediaMetadataRetriever, data, reply); + int mode = data.readInt32(); + reply->writeInt32(setMode(mode)); + return NO_ERROR; + } break; + case GET_MODE: { + CHECK_INTERFACE(IMediaMetadataRetriever, data, reply); + int mode; + status_t status = getMode(&mode); + reply->writeInt32(mode); + reply->writeInt32(status); + return NO_ERROR; + } break; + case CAPTURE_FRAME: { + CHECK_INTERFACE(IMediaMetadataRetriever, data, reply); + sp bitmap = captureFrame(); + if (bitmap != 0) { // Don't send NULL across the binder interface + reply->writeInt32(NO_ERROR); + reply->writeStrongBinder(bitmap->asBinder()); + } else { + reply->writeInt32(UNKNOWN_ERROR); + } + return NO_ERROR; + } break; + case EXTARCT_ALBUM_ART: { + CHECK_INTERFACE(IMediaMetadataRetriever, data, reply); + sp albumArt = extractAlbumArt(); + if (albumArt != 0) { // Don't send NULL across the binder interface + reply->writeInt32(NO_ERROR); + reply->writeStrongBinder(albumArt->asBinder()); + } else { + reply->writeInt32(UNKNOWN_ERROR); + } + return NO_ERROR; + } break; + case EXTRACT_METADATA: { + CHECK_INTERFACE(IMediaMetadataRetriever, data, reply); + int keyCode = data.readInt32(); + const char* value = extractMetadata(keyCode); + if (value != NULL) { // Don't send NULL across the binder interface + reply->writeInt32(NO_ERROR); + reply->writeCString(value); + } else { + reply->writeInt32(UNKNOWN_ERROR); + } + return NO_ERROR; + } break; + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + +// ---------------------------------------------------------------------------- + +}; // namespace android + diff --git a/media/libmedia/IMediaPlayer.cpp b/media/libmedia/IMediaPlayer.cpp index 8385114..f37519f 100644 --- a/media/libmedia/IMediaPlayer.cpp +++ b/media/libmedia/IMediaPlayer.cpp @@ -33,7 +33,6 @@ enum { STOP, IS_PLAYING, PAUSE, - GET_VIDEO_SIZE, SEEK_TO, GET_CURRENT_POSITION, GET_DURATION, @@ -109,16 +108,6 @@ public: 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; @@ -235,15 +224,6 @@ status_t BnMediaPlayer::onTransact( 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())); diff --git a/media/libmedia/IMediaPlayerService.cpp b/media/libmedia/IMediaPlayerService.cpp index b087100..370e3fb 100644 --- a/media/libmedia/IMediaPlayerService.cpp +++ b/media/libmedia/IMediaPlayerService.cpp @@ -21,6 +21,7 @@ #include #include +#include namespace android { @@ -29,6 +30,8 @@ enum { CREATE_FD, DECODE_URL, DECODE_FD, + CREATE_MEDIA_RECORDER, + CREATE_METADATA_RETRIEVER, }; class BpMediaPlayerService: public BpInterface @@ -39,6 +42,15 @@ public: { } + virtual sp createMetadataRetriever(pid_t pid) + { + Parcel data, reply; + data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor()); + data.writeInt32(pid); + remote()->transact(CREATE_METADATA_RETRIEVER, data, &reply); + return interface_cast(reply.readStrongBinder()); + } + virtual sp create(pid_t pid, const sp& client, const char* url) { Parcel data, reply; @@ -50,6 +62,15 @@ public: return interface_cast(reply.readStrongBinder()); } + virtual sp createMediaRecorder(pid_t pid) + { + Parcel data, reply; + data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor()); + data.writeInt32(pid); + remote()->transact(CREATE_MEDIA_RECORDER, data, &reply); + return interface_cast(reply.readStrongBinder()); + } + virtual sp create(pid_t pid, const sp& client, int fd, int64_t offset, int64_t length) { Parcel data, reply; @@ -63,7 +84,7 @@ public: return interface_cast(reply.readStrongBinder()); } - virtual sp decode(const char* url, uint32_t *pSampleRate, int* pNumChannels) + virtual sp decode(const char* url, uint32_t *pSampleRate, int* pNumChannels, int* pFormat) { Parcel data, reply; data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor()); @@ -71,10 +92,11 @@ public: remote()->transact(DECODE_URL, data, &reply); *pSampleRate = uint32_t(reply.readInt32()); *pNumChannels = reply.readInt32(); + *pFormat = reply.readInt32(); return interface_cast(reply.readStrongBinder()); } - virtual sp decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels) + virtual sp decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels, int* pFormat) { Parcel data, reply; data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor()); @@ -84,6 +106,7 @@ public: remote()->transact(DECODE_FD, data, &reply); *pSampleRate = uint32_t(reply.readInt32()); *pNumChannels = reply.readInt32(); + *pFormat = reply.readInt32(); return interface_cast(reply.readStrongBinder()); } }; @@ -127,9 +150,11 @@ status_t BnMediaPlayerService::onTransact( const char* url = data.readCString(); uint32_t sampleRate; int numChannels; - sp player = decode(url, &sampleRate, &numChannels); + int format; + sp player = decode(url, &sampleRate, &numChannels, &format); reply->writeInt32(sampleRate); reply->writeInt32(numChannels); + reply->writeInt32(format); reply->writeStrongBinder(player->asBinder()); return NO_ERROR; } break; @@ -140,12 +165,28 @@ status_t BnMediaPlayerService::onTransact( int64_t length = data.readInt64(); uint32_t sampleRate; int numChannels; - sp player = decode(fd, offset, length, &sampleRate, &numChannels); + int format; + sp player = decode(fd, offset, length, &sampleRate, &numChannels, &format); reply->writeInt32(sampleRate); reply->writeInt32(numChannels); + reply->writeInt32(format); reply->writeStrongBinder(player->asBinder()); return NO_ERROR; } break; + case CREATE_MEDIA_RECORDER: { + CHECK_INTERFACE(IMediaPlayerService, data, reply); + pid_t pid = data.readInt32(); + sp recorder = createMediaRecorder(pid); + reply->writeStrongBinder(recorder->asBinder()); + return NO_ERROR; + } break; + case CREATE_METADATA_RETRIEVER: { + CHECK_INTERFACE(IMediaPlayerService, data, reply); + pid_t pid = data.readInt32(); + sp retriever = createMetadataRetriever(pid); + reply->writeStrongBinder(retriever->asBinder()); + return NO_ERROR; + } break; default: return BBinder::onTransact(code, data, reply, flags); } diff --git a/media/libmedia/IMediaRecorder.cpp b/media/libmedia/IMediaRecorder.cpp new file mode 100644 index 0000000..1f6d599 --- /dev/null +++ b/media/libmedia/IMediaRecorder.cpp @@ -0,0 +1,376 @@ +/* + ** + ** Copyright 2008, HTC Inc. + ** + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** + ** http://www.apache.org/licenses/LICENSE-2.0 + ** + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "IMediaRecorder" +#include +#include +#include +#include +#include + +namespace android { + +enum { + RELEASE = IBinder::FIRST_CALL_TRANSACTION, + INIT, + CLOSE, + RESET, + STOP, + START, + PREPARE, + GET_MAX_AMPLITUDE, + SET_VIDEO_SOURCE, + SET_AUDIO_SOURCE, + SET_OUTPUT_FORMAT, + SET_VIDEO_ENCODER, + SET_AUDIO_ENCODER, + SET_OUTPUT_FILE, + SET_VIDEO_SIZE, + SET_VIDEO_FRAMERATE, + SET_PREVIEW_SURFACE, + SET_CAMERA +}; + +class BpMediaRecorder: public BpInterface +{ +public: + BpMediaRecorder(const sp& impl) + : BpInterface(impl) + { + } + + status_t setCamera(const sp& camera) + { + LOGV("setCamera(%p)", camera.get()); + Parcel data, reply; + data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor()); + data.writeStrongBinder(camera->asBinder()); + remote()->transact(SET_CAMERA, data, &reply); + return reply.readInt32(); + } + + status_t setPreviewSurface(const sp& surface) + { + LOGV("setPreviewSurface(%p)", surface.get()); + Parcel data, reply; + data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor()); + data.writeStrongBinder(surface->asBinder()); + remote()->transact(SET_PREVIEW_SURFACE, data, &reply); + return reply.readInt32(); + } + + status_t init() + { + LOGV("init"); + Parcel data, reply; + data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor()); + remote()->transact(INIT, data, &reply); + return reply.readInt32(); + } + + status_t setVideoSource(int vs) + { + LOGV("setVideoSource(%d)", vs); + Parcel data, reply; + data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor()); + data.writeInt32(vs); + remote()->transact(SET_VIDEO_SOURCE, data, &reply); + return reply.readInt32(); + } + + status_t setAudioSource(int as) + { + LOGV("setAudioSource(%d)", as); + Parcel data, reply; + data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor()); + data.writeInt32(as); + remote()->transact(SET_AUDIO_SOURCE, data, &reply); + return reply.readInt32(); + } + + status_t setOutputFormat(int of) + { + LOGV("setOutputFormat(%d)", of); + Parcel data, reply; + data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor()); + data.writeInt32(of); + remote()->transact(SET_OUTPUT_FORMAT, data, &reply); + return reply.readInt32(); + } + + status_t setVideoEncoder(int ve) + { + LOGV("setVideoEncoder(%d)", ve); + Parcel data, reply; + data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor()); + data.writeInt32(ve); + remote()->transact(SET_VIDEO_ENCODER, data, &reply); + return reply.readInt32(); + } + + status_t setAudioEncoder(int ae) + { + LOGV("setAudioEncoder(%d)", ae); + Parcel data, reply; + data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor()); + data.writeInt32(ae); + remote()->transact(SET_AUDIO_ENCODER, data, &reply); + return reply.readInt32(); + } + + status_t setOutputFile(const char* path) + { + LOGV("setOutputFile(%s)", path); + Parcel data, reply; + data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor()); + data.writeCString(path); + remote()->transact(SET_OUTPUT_FILE, data, &reply); + return reply.readInt32(); + } + + status_t setVideoSize(int width, int height) + { + LOGV("setVideoSize(%dx%d)", width, height); + Parcel data, reply; + data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor()); + data.writeInt32(width); + data.writeInt32(height); + remote()->transact(SET_VIDEO_SIZE, data, &reply); + return reply.readInt32(); + } + + status_t setVideoFrameRate(int frames_per_second) + { + LOGV("setVideoFrameRate(%d)", frames_per_second); + Parcel data, reply; + data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor()); + data.writeInt32(frames_per_second); + remote()->transact(SET_VIDEO_FRAMERATE, data, &reply); + return reply.readInt32(); + } + + status_t prepare() + { + LOGV("prepare"); + Parcel data, reply; + data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor()); + remote()->transact(PREPARE, data, &reply); + return reply.readInt32(); + } + + status_t getMaxAmplitude(int* max) + { + LOGV("getMaxAmplitude"); + Parcel data, reply; + data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor()); + remote()->transact(GET_MAX_AMPLITUDE, data, &reply); + *max = reply.readInt32(); + return reply.readInt32(); + } + + status_t start() + { + LOGV("start"); + Parcel data, reply; + data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor()); + remote()->transact(START, data, &reply); + return reply.readInt32(); + } + + status_t stop() + { + LOGV("stop"); + Parcel data, reply; + data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor()); + remote()->transact(STOP, data, &reply); + return reply.readInt32(); + } + + status_t reset() + { + LOGV("reset"); + Parcel data, reply; + data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor()); + remote()->transact(RESET, data, &reply); + return reply.readInt32(); + } + + status_t close() + { + LOGV("close"); + Parcel data, reply; + data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor()); + remote()->transact(CLOSE, data, &reply); + return reply.readInt32(); + } + + status_t release() + { + LOGV("release"); + Parcel data, reply; + data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor()); + remote()->transact(RELEASE, data, &reply); + return reply.readInt32(); + } +}; + +IMPLEMENT_META_INTERFACE(MediaRecorder, "android.hardware.IMediaRecorder"); + +// ---------------------------------------------------------------------- + +#define CHECK_INTERFACE(interface, data, reply) \ + do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \ + LOGW("Call incorrectly routed to " #interface); \ + return PERMISSION_DENIED; \ + } } while (0) + +status_t BnMediaRecorder::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch(code) { + case RELEASE: { + LOGV("RELEASE"); + CHECK_INTERFACE(IMediaRecorder, data, reply); + reply->writeInt32(release()); + return NO_ERROR; + } break; + case INIT: { + LOGV("INIT"); + CHECK_INTERFACE(IMediaRecorder, data, reply); + reply->writeInt32(init()); + return NO_ERROR; + } break; + case CLOSE: { + LOGV("CLOSE"); + CHECK_INTERFACE(IMediaRecorder, data, reply); + reply->writeInt32(close()); + return NO_ERROR; + } break; + case RESET: { + LOGV("RESET"); + CHECK_INTERFACE(IMediaRecorder, data, reply); + reply->writeInt32(reset()); + return NO_ERROR; + } break; + case STOP: { + LOGV("STOP"); + CHECK_INTERFACE(IMediaRecorder, data, reply); + reply->writeInt32(stop()); + return NO_ERROR; + } break; + case START: { + LOGV("START"); + CHECK_INTERFACE(IMediaRecorder, data, reply); + reply->writeInt32(start()); + return NO_ERROR; + } break; + case PREPARE: { + LOGV("PREPARE"); + CHECK_INTERFACE(IMediaRecorder, data, reply); + reply->writeInt32(prepare()); + return NO_ERROR; + } break; + case GET_MAX_AMPLITUDE: { + LOGV("GET_MAX_AMPLITUDE"); + CHECK_INTERFACE(IMediaRecorder, data, reply); + int max = 0; + status_t ret = getMaxAmplitude(&max); + reply->writeInt32(max); + reply->writeInt32(ret); + return NO_ERROR; + } break; + case SET_VIDEO_SOURCE: { + LOGV("SET_VIDEO_SOURCE"); + CHECK_INTERFACE(IMediaRecorder, data, reply); + int vs = data.readInt32(); + reply->writeInt32(setVideoSource(vs)); + return NO_ERROR; + } break; + case SET_AUDIO_SOURCE: { + LOGV("SET_AUDIO_SOURCE"); + CHECK_INTERFACE(IMediaRecorder, data, reply); + int as = data.readInt32(); + reply->writeInt32(setAudioSource(as)); + return NO_ERROR; + } break; + case SET_OUTPUT_FORMAT: { + LOGV("SET_OUTPUT_FORMAT"); + CHECK_INTERFACE(IMediaRecorder, data, reply); + int of = data.readInt32(); + reply->writeInt32(setOutputFormat(of)); + return NO_ERROR; + } break; + case SET_VIDEO_ENCODER: { + LOGV("SET_VIDEO_ENCODER"); + CHECK_INTERFACE(IMediaRecorder, data, reply); + int ve = data.readInt32(); + reply->writeInt32(setVideoEncoder(ve)); + return NO_ERROR; + } break; + case SET_AUDIO_ENCODER: { + LOGV("SET_AUDIO_ENCODER"); + CHECK_INTERFACE(IMediaRecorder, data, reply); + int ae = data.readInt32(); + reply->writeInt32(setAudioEncoder(ae)); + return NO_ERROR; + + } break; + case SET_OUTPUT_FILE: { + LOGV("SET_OUTPUT_FILE"); + CHECK_INTERFACE(IMediaRecorder, data, reply); + const char* path = data.readCString(); + reply->writeInt32(setOutputFile(path)); + return NO_ERROR; + } break; + case SET_VIDEO_SIZE: { + LOGV("SET_VIDEO_SIZE"); + CHECK_INTERFACE(IMediaRecorder, data, reply); + int width = data.readInt32(); + int height = data.readInt32(); + reply->writeInt32(setVideoSize(width, height)); + return NO_ERROR; + } break; + case SET_VIDEO_FRAMERATE: { + LOGV("SET_VIDEO_FRAMERATE"); + CHECK_INTERFACE(IMediaRecorder, data, reply); + int frames_per_second = data.readInt32(); + reply->writeInt32(setVideoFrameRate(frames_per_second)); + return NO_ERROR; + } break; + case SET_PREVIEW_SURFACE: { + LOGV("SET_PREVIEW_SURFACE"); + CHECK_INTERFACE(IMediaRecorder, data, reply); + sp surface = interface_cast(data.readStrongBinder()); + reply->writeInt32(setPreviewSurface(surface)); + return NO_ERROR; + } break; + case SET_CAMERA: { + LOGV("SET_CAMERA"); + CHECK_INTERFACE(IMediaRecorder, data, reply); + sp camera = interface_cast(data.readStrongBinder()); + reply->writeInt32(setCamera(camera)); + return NO_ERROR; + } break; + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + +// ---------------------------------------------------------------------------- + +}; // namespace android diff --git a/media/libmedia/ToneGenerator.cpp b/media/libmedia/ToneGenerator.cpp index 89ab2be..0dee1f6 100644 --- a/media/libmedia/ToneGenerator.cpp +++ b/media/libmedia/ToneGenerator.cpp @@ -88,56 +88,34 @@ const ToneGenerator::ToneDescriptor // //////////////////////////////////////////////////////////////////////////////// ToneGenerator::ToneGenerator(int streamType, float volume) { - const sp& 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) { + if (AudioSystem::getOutputSamplingRate(&mSamplingRate) != NO_ERROR) { LOGE("Unable to marshal AudioFlinger"); - goto ToneGenerator_exit; + return; } - - 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; + if (AudioSystem::getOutputFrameCount(&mBufferSize) != NO_ERROR) { + LOGE("Unable to marshal AudioFlinger"); + return; } - LOGV("Create Track: %p\n", mpAudioTrack); + mStreamType = streamType; + mVolume = volume; + mpAudioTrack = 0; + mpToneDesc = 0; + mpNewToneDesc = 0; - if (mpAudioTrack->initCheck() != NO_ERROR) { - LOGE("AudioTrack->initCheck failed"); - goto ToneGenerator_exit; + if (initAudioTrack()) { + LOGV("ToneGenerator INIT OK, time: %d\n", (unsigned int)(systemTime()/1000000)); + } else { + LOGV("!!!ToneGenerator INIT FAILED!!!\n"); } +} - 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"); -} //////////////////////////////////////////////////////////////////////////////// // @@ -179,9 +157,16 @@ ToneGenerator::~ToneGenerator() { bool ToneGenerator::startTone(int toneType) { bool lResult = false; - if (mState == TONE_IDLE || toneType >= NUM_TONES) + if (toneType >= NUM_TONES) return lResult; + if (mState == TONE_IDLE) { + LOGV("startTone: try to re-init AudioTrack"); + if (!initAudioTrack()) { + return lResult; + } + } + LOGV("startTone\n"); mLock.lock(); @@ -198,8 +183,10 @@ bool ToneGenerator::startTone(int toneType) { mpAudioTrack->start(); mLock.lock(); if (mState == TONE_STARTING) { - if (mWaitCbkCond.waitRelative(mLock, seconds(1)) != NO_ERROR) + if (mWaitCbkCond.waitRelative(mLock, seconds(1)) != NO_ERROR) { LOGE("--- timed out"); + mState = TONE_IDLE; + } } if (mState == TONE_PLAYING) @@ -216,6 +203,7 @@ bool ToneGenerator::startTone(int toneType) { LOGV("cond received"); } else { LOGE("--- timed out"); + mState = TONE_IDLE; } } mLock.unlock(); @@ -250,7 +238,8 @@ void ToneGenerator::stopTone() { LOGV("track stop complete, time %d", (unsigned int)(systemTime()/1000000)); } else { LOGE("--- timed out"); - mState = TONE_INIT; + mState = TONE_IDLE; + mpAudioTrack->stop(); } } @@ -262,6 +251,62 @@ void ToneGenerator::stopTone() { //---------------------------------- private methods --------------------------- + + +//////////////////////////////////////////////////////////////////////////////// +// +// Method: ToneGenerator::initAudioTrack() +// +// Description: Allocates and configures AudioTrack used for PCM output. +// +// Input: +// none +// +// Output: +// none +// +//////////////////////////////////////////////////////////////////////////////// +bool ToneGenerator::initAudioTrack() { + + if (mpAudioTrack) { + delete mpAudioTrack; + mpAudioTrack = 0; + } + + // Open audio track in mono, PCM 16bit, default sampling rate, 2 buffers of + mpAudioTrack + = new AudioTrack(mStreamType, 0, AudioSystem::PCM_16_BIT, 1, NUM_PCM_BUFFERS*mBufferSize, 0, audioCallback, this, mBufferSize); + + if (mpAudioTrack == 0) { + LOGE("AudioTrack allocation failed"); + goto initAudioTrack_exit; + } + LOGV("Create Track: %p\n", mpAudioTrack); + + if (mpAudioTrack->initCheck() != NO_ERROR) { + LOGE("AudioTrack->initCheck failed"); + goto initAudioTrack_exit; + } + + mpAudioTrack->setVolume(mVolume, mVolume); + + mState = TONE_INIT; + + return true; + +initAudioTrack_exit: + + // Cleanup + if (mpAudioTrack) { + LOGV("Delete Track I: %p\n", mpAudioTrack); + delete mpAudioTrack; + mpAudioTrack = 0; + } + + return false; +} + + //////////////////////////////////////////////////////////////////////////////// // // Method: ToneGenerator::audioCallback() @@ -278,19 +323,24 @@ void ToneGenerator::stopTone() { // returned value: always true. // //////////////////////////////////////////////////////////////////////////////// -void ToneGenerator::audioCallback(void* user, const AudioTrack::Buffer& info) { +void ToneGenerator::audioCallback(int event, void* user, void *info) { + + if (event != AudioTrack::EVENT_MORE_DATA) return; + + const AudioTrack::Buffer *buffer = static_cast(info); ToneGenerator *lpToneGen = static_cast(user); - short *lpOut = info.i16; - unsigned int lReqSmp = info.size/sizeof(short); + short *lpOut = buffer->i16; + unsigned int lReqSmp = buffer->size/sizeof(short); unsigned int lGenSmp; unsigned int lWaveCmd = WaveGenerator::WAVEGEN_CONT; bool lSignal = false; + if (buffer->size == 0) return; lpToneGen->mLock.lock(); // Clear output buffer: WaveGenerator accumulates into lpOut buffer - memset(lpOut, 0, info.size); + memset(lpOut, 0, buffer->size); // Update pcm frame count and end time (current time at the end of this process) lpToneGen->mTotalSmp += lReqSmp; @@ -317,8 +367,11 @@ void ToneGenerator::audioCallback(void* user, const AudioTrack::Buffer& info) { goto audioCallback_Exit; } - // Exit if to sequence is over + // Exit if tone sequence is over if (lpToneGen->mpToneDesc->segments[lpToneGen->mCurSegment] == 0) { + if (lpToneGen->mState == TONE_PLAYING) { + lpToneGen->mState = TONE_STOPPING; + } goto audioCallback_Exit; } @@ -327,7 +380,7 @@ void ToneGenerator::audioCallback(void* user, const AudioTrack::Buffer& info) { LOGV("End Segment, time: %d\n", (unsigned int)(systemTime()/1000000)); - lGenSmp = lReqSmp; + lGenSmp = lReqSmp; if (lpToneGen->mCurSegment & 0x0001) { // If odd segment, OFF -> ON transition : reset wave generator diff --git a/media/libmedia/mediametadataretriever.cpp b/media/libmedia/mediametadataretriever.cpp index 9cbafbc..09afc6c 100644 --- a/media/libmedia/mediametadataretriever.cpp +++ b/media/libmedia/mediametadataretriever.cpp @@ -15,168 +15,174 @@ ** limitations under the License. */ -#include - -#ifdef LOG_TAG -#undef LOG_TAG +//#define LOG_NDEBUG 0 #define LOG_TAG "MediaMetadataRetriever" -#endif +#include +#include +#include +#include #include #include namespace android { -// Factory class function in shared libpvplayer.so -typedef MediaMetadataRetrieverImpl* (*createRetriever_f)(); +// client singleton for binder interface to service +Mutex MediaMetadataRetriever::sServiceLock; +sp MediaMetadataRetriever::sService; +sp MediaMetadataRetriever::sDeathNotifier; -MediaMetadataRetrieverImpl *MediaMetadataRetriever::mRetriever = NULL; -void *MediaMetadataRetriever::mLibHandler = NULL; - -void MediaMetadataRetriever::create() +const sp& MediaMetadataRetriever::getService() { - // 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; + Mutex::Autolock lock(sServiceLock); + if (sService.get() == 0) { + sp sm = defaultServiceManager(); + sp binder; + do { + binder = sm->getService(String16("media.player")); + if (binder != 0) { + break; + } + LOGW("MediaPlayerService not published, waiting..."); + usleep(500000); // 0.5 s + } while(true); + if (sDeathNotifier == NULL) { + sDeathNotifier = new DeathNotifier(); } + binder->linkToDeath(sDeathNotifier); + sService = interface_cast(binder); } - - // Each time create a new MediaMetadataRetrieverImpl object. - if (mRetriever) { - delete mRetriever; - } - createRetriever_f createRetriever = reinterpret_cast(dlsym(mLibHandler, "createRetriever")); - if (!createRetriever) { - LOGE("setDataSource: dlsym failed on createRetriever in libpvplayer.so"); + LOGE_IF(sService == 0, "no MediaPlayerService!?"); + return sService; +} + +MediaMetadataRetriever::MediaMetadataRetriever() +{ + LOGV("constructor"); + const sp& service(getService()); + if (service == 0) { + LOGE("failed to obtain MediaMetadataRetrieverService"); return; } - mRetriever = createRetriever(); - if (!mRetriever) { - LOGE("setDataSource: createRetriever failed in libpvplayer.so"); + sp retriever(service->createMetadataRetriever(getpid())); + if (retriever == 0) { + LOGE("failed to create IMediaMetadataRetriever object from server"); } + mRetriever = retriever; } -status_t MediaMetadataRetriever::setDataSource(const char* srcUrl) +MediaMetadataRetriever::~MediaMetadataRetriever() { - if (srcUrl == NULL) { - return UNKNOWN_ERROR; + LOGV("destructor"); + disconnect(); + IPCThreadState::self()->flushCommands(); +} + +void MediaMetadataRetriever::disconnect() +{ + LOGV("disconnect"); + sp retriever; + { + Mutex::Autolock _l(mLock); + retriever = mRetriever; + mRetriever.clear(); } - - if (mRetriever) { - return mRetriever->setDataSource(srcUrl); + if (retriever != 0) { + retriever->disconnect(); } - return UNKNOWN_ERROR; } -const char* MediaMetadataRetriever::extractMetadata(int keyCode) +status_t MediaMetadataRetriever::setDataSource(const char* srcUrl) { - if (mRetriever) { - return mRetriever->extractMetadata(keyCode); + LOGV("setDataSource"); + if (mRetriever == 0) { + LOGE("retriever is not initialized"); + return INVALID_OPERATION; + } + if (srcUrl == NULL) { + LOGE("data source is a null pointer"); + return UNKNOWN_ERROR; } - return NULL; + LOGV("data source (%s)", srcUrl); + return mRetriever->setDataSource(srcUrl); } -MediaAlbumArt* MediaMetadataRetriever::extractAlbumArt() +status_t MediaMetadataRetriever::setDataSource(int fd, int64_t offset, int64_t length) { - if (mRetriever) { - return mRetriever->extractAlbumArt(); + LOGV("setDataSource(%d, %lld, %lld)", fd, offset, length); + if (mRetriever == 0) { + LOGE("retriever is not initialized"); + return INVALID_OPERATION; } - return NULL; + if (fd < 0 || offset < 0 || length < 0) { + LOGE("Invalid negative argument"); + return UNKNOWN_ERROR; + } + return mRetriever->setDataSource(fd, offset, length); } -SkBitmap* MediaMetadataRetriever::captureFrame() +status_t MediaMetadataRetriever::setMode(int mode) { - if (mRetriever) { - return mRetriever->captureFrame(); + LOGV("setMode(%d)", mode); + if (mRetriever == 0) { + LOGE("retriever is not initialized"); + return INVALID_OPERATION; } - return NULL; + return mRetriever->setMode(mode); } -void MediaMetadataRetriever::setMode(int mode) +status_t MediaMetadataRetriever::getMode(int* mode) { - if (mRetriever) { - mRetriever->setMode(mode); + LOGV("getMode"); + if (mRetriever == 0) { + LOGE("retriever is not initialized"); + return INVALID_OPERATION; } + return mRetriever->getMode(mode); } -void MediaMetadataRetriever::release() +sp MediaMetadataRetriever::captureFrame() { - if (!mLibHandler) { - dlclose(mLibHandler); - mLibHandler = NULL; - } - if (!mRetriever) { - delete mRetriever; - mRetriever = NULL; + LOGV("captureFrame"); + if (mRetriever == 0) { + LOGE("retriever is not initialized"); + return NULL; } + return mRetriever->captureFrame(); } -void MediaAlbumArt::clearData() { - if (data != NULL) { - delete []data; - data = NULL; +const char* MediaMetadataRetriever::extractMetadata(int keyCode) +{ + LOGV("extractMetadata(%d)", keyCode); + if (mRetriever == 0) { + LOGE("retriever is not initialized"); + return NULL; } - length = 0; + return mRetriever->extractMetadata(keyCode); } - -MediaAlbumArt::MediaAlbumArt(const char* url) +sp MediaMetadataRetriever::extractAlbumArt() { - 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."); + LOGV("extractAlbumArt"); + if (mRetriever == 0) { + LOGE("retriever is not initialized"); + return NULL; } - fclose(in); + return mRetriever->extractAlbumArt(); } -status_t MediaAlbumArt::setData(unsigned int len, const char* buf) { - clearData(); - length = len; - data = copyData(len, buf); - return (data != NULL)? OK: UNKNOWN_ERROR; +void MediaMetadataRetriever::DeathNotifier::binderDied(const wp& who) { + Mutex::Autolock lock(MediaMetadataRetriever::sServiceLock); + MediaMetadataRetriever::sService.clear(); + LOGW("MediaMetadataRetriever server died!"); } -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; +MediaMetadataRetriever::DeathNotifier::~DeathNotifier() +{ + Mutex::Autolock lock(sServiceLock); + if (sService != 0) { + sService->asBinder()->unlinkToDeath(this); } - memcpy(copy, buf, len); - return copy; } }; // namespace android diff --git a/media/libmedia/mediaplayer.cpp b/media/libmedia/mediaplayer.cpp index 736d84a..ebdbda8 100644 --- a/media/libmedia/mediaplayer.cpp +++ b/media/libmedia/mediaplayer.cpp @@ -28,22 +28,23 @@ #include #include -#include +#include #include namespace android { // client singleton for binder interface to service -Mutex MediaPlayer::mServiceLock; -sp MediaPlayer::mMediaPlayerService; -sp MediaPlayer::mDeathNotifier; +Mutex MediaPlayer::sServiceLock; +sp MediaPlayer::sMediaPlayerService; +sp MediaPlayer::sDeathNotifier; +SortedVector< wp > MediaPlayer::sObitRecipients; // establish binder interface to service const sp& MediaPlayer::getMediaPlayerService() { - Mutex::Autolock _l(mServiceLock); - if (mMediaPlayerService.get() == 0) { + Mutex::Autolock _l(sServiceLock); + if (sMediaPlayerService.get() == 0) { sp sm = defaultServiceManager(); sp binder; do { @@ -53,14 +54,26 @@ const sp& MediaPlayer::getMediaPlayerService() LOGW("MediaPlayerService not published, waiting..."); usleep(500000); // 0.5 s } while(true); - if (mDeathNotifier == NULL) { - mDeathNotifier = new DeathNotifier(); + if (sDeathNotifier == NULL) { + sDeathNotifier = new DeathNotifier(); } - binder->linkToDeath(mDeathNotifier); - mMediaPlayerService = interface_cast(binder); + binder->linkToDeath(sDeathNotifier); + sMediaPlayerService = interface_cast(binder); } - LOGE_IF(mMediaPlayerService==0, "no MediaPlayerService!?"); - return mMediaPlayerService; + LOGE_IF(sMediaPlayerService==0, "no MediaPlayerService!?"); + return sMediaPlayerService; +} + +void MediaPlayer::addObitRecipient(const wp& recipient) +{ + Mutex::Autolock _l(sServiceLock); + sObitRecipients.add(recipient); +} + +void MediaPlayer::removeObitRecipient(const wp& recipient) +{ + Mutex::Autolock _l(sServiceLock); + sObitRecipients.remove(recipient); } MediaPlayer::MediaPlayer() @@ -77,11 +90,18 @@ MediaPlayer::MediaPlayer() mPrepareStatus = NO_ERROR; mLoop = false; mLeftVolume = mRightVolume = 1.0; + mVideoWidth = mVideoHeight = 0; +} + +void MediaPlayer::onFirstRef() +{ + addObitRecipient(this); } MediaPlayer::~MediaPlayer() { LOGV("destructor"); + removeObitRecipient(this); disconnect(); IPCThreadState::self()->flushCommands(); } @@ -98,7 +118,6 @@ void MediaPlayer::disconnect() if (p != 0) { p->disconnect(); - p->asBinder()->unlinkToDeath(this); } } @@ -108,6 +127,7 @@ void MediaPlayer::clear_l() mDuration = -1; mCurrentPosition = -1; mSeekPosition = -1; + mVideoWidth = mVideoHeight = 0; } status_t MediaPlayer::setListener(const sp& listener) @@ -136,7 +156,6 @@ status_t MediaPlayer::setDataSource(const sp& player) mPlayer = player; if (player != 0) { mCurrentState = MEDIA_PLAYER_INITIALIZED; - player->asBinder()->linkToDeath(this); err = NO_ERROR; } else { LOGE("Unable to to create media player"); @@ -145,8 +164,8 @@ status_t MediaPlayer::setDataSource(const sp& player) if (p != 0) { p->disconnect(); - p->asBinder()->unlinkToDeath(this); } + return err; } @@ -307,22 +326,18 @@ 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; + if (mPlayer == 0) return INVALID_OPERATION; + *w = mVideoWidth; + return NO_ERROR; } status_t MediaPlayer::getVideoHeight(int *h) { LOGV("getVideoHeight"); Mutex::Autolock _l(mLock); - if (mPlayer != 0) { - int w; - return mPlayer->getVideoSize(&w, h); - } - return INVALID_OPERATION; + if (mPlayer == 0) return INVALID_OPERATION; + *h = mVideoHeight; + return NO_ERROR; } status_t MediaPlayer::getCurrentPosition(int *msec) @@ -405,6 +420,7 @@ status_t MediaPlayer::reset() if (mPlayer != 0) { status_t ret = mPlayer->reset(); if (ret != NO_ERROR) { + LOGE("reset() failed with return code (%d)", ret); mCurrentState = MEDIA_PLAYER_STATE_ERROR; ret = UNKNOWN_ERROR; } else { @@ -443,6 +459,16 @@ status_t MediaPlayer::setLooping(int loop) return OK; } +bool MediaPlayer::isLooping() { + LOGV("isLooping"); + Mutex::Autolock _l(mLock); + if (mPlayer != 0) { + return mLoop; + } + LOGV("isLooping: no active player"); + return false; +} + status_t MediaPlayer::setVolume(float leftVolume, float rightVolume) { LOGV("MediaPlayer::setVolume(%f, %f)", leftVolume, rightVolume); @@ -466,6 +492,7 @@ void MediaPlayer::notify(int msg, int ext1, int ext2) mLock.lock(); if (mPlayer == 0) { LOGV("notify(%d, %d, %d) callback on disconnected mediaplayer", msg, ext1, ext2); + mLock.unlock(); // release the lock when done. return; } @@ -489,7 +516,8 @@ void MediaPlayer::notify(int msg, int ext1, int ext2) } break; case MEDIA_ERROR: - LOGV("error (%d, %d)", ext1, ext2); + // Always log errors + LOGE("error (%d, %d)", ext1, ext2); mCurrentState = MEDIA_PLAYER_STATE_ERROR; if (mPrepareSync) { @@ -515,6 +543,11 @@ void MediaPlayer::notify(int msg, int ext1, int ext2) case MEDIA_BUFFERING_UPDATE: LOGV("buffering %d", ext1); break; + case MEDIA_SET_VIDEO_SIZE: + LOGV("New video size %d x %d", ext1, ext2); + mVideoWidth = ext1; + mVideoHeight = ext2; + break; default: LOGV("unrecognized message: (%d, %d, %d)", msg, ext1, ext2); break; @@ -532,32 +565,45 @@ void MediaPlayer::notify(int msg, int ext1, int ext2) } } -void MediaPlayer::binderDied(const wp& who) { - LOGW("IMediaplayer died"); - notify(MEDIA_ERROR, MEDIA_ERROR_SERVER_DIED, 0); -} - void MediaPlayer::DeathNotifier::binderDied(const wp& who) { - Mutex::Autolock _l(MediaPlayer::mServiceLock); - MediaPlayer::mMediaPlayerService.clear(); LOGW("MediaPlayer server died!"); + + // Need to do this with the lock held + SortedVector< wp > list; + { + Mutex::Autolock _l(MediaPlayer::sServiceLock); + MediaPlayer::sMediaPlayerService.clear(); + list = sObitRecipients; + } + + // Notify application when media server dies. + // Don't hold the static lock during callback in case app + // makes a call that needs the lock. + size_t count = list.size(); + for (size_t iter = 0; iter < count; ++iter) { + sp player = list[iter].promote(); + if ((player != 0) && (player->mPlayer != 0)) { + player->notify(MEDIA_ERROR, MEDIA_ERROR_SERVER_DIED, 0); + } + } } MediaPlayer::DeathNotifier::~DeathNotifier() { - Mutex::Autolock _l(mServiceLock); - if (mMediaPlayerService != 0) { - mMediaPlayerService->asBinder()->unlinkToDeath(this); + Mutex::Autolock _l(sServiceLock); + sObitRecipients.clear(); + if (sMediaPlayerService != 0) { + sMediaPlayerService->asBinder()->unlinkToDeath(this); } } -/*static*/ sp MediaPlayer::decode(const char* url, uint32_t *pSampleRate, int* pNumChannels) +/*static*/ sp MediaPlayer::decode(const char* url, uint32_t *pSampleRate, int* pNumChannels, int* pFormat) { LOGV("decode(%s)", url); sp p; const sp& service = getMediaPlayerService(); if (service != 0) { - p = mMediaPlayerService->decode(url, pSampleRate, pNumChannels); + p = sMediaPlayerService->decode(url, pSampleRate, pNumChannels, pFormat); } else { LOGE("Unable to locate media service"); } @@ -565,13 +611,13 @@ MediaPlayer::DeathNotifier::~DeathNotifier() } -/*static*/ sp MediaPlayer::decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels) +/*static*/ sp MediaPlayer::decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels, int* pFormat) { LOGV("decode(%d, %lld, %lld)", fd, offset, length); sp p; const sp& service = getMediaPlayerService(); if (service != 0) { - p = mMediaPlayerService->decode(fd, offset, length, pSampleRate, pNumChannels); + p = sMediaPlayerService->decode(fd, offset, length, pSampleRate, pNumChannels, pFormat); } else { LOGE("Unable to locate media service"); } diff --git a/media/libmedia/mediarecorder.cpp b/media/libmedia/mediarecorder.cpp new file mode 100644 index 0000000..825e145 --- /dev/null +++ b/media/libmedia/mediarecorder.cpp @@ -0,0 +1,518 @@ +/* + ** + ** Copyright (c) 2008 The Android Open Source Project + ** + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** + ** http://www.apache.org/licenses/LICENSE-2.0 + ** + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "MediaRecorder" +#include +#include +#include +#include +#include +#include + +namespace android { + +status_t MediaRecorder::setCamera(const sp& camera) +{ + LOGV("setCamera(%p)", camera.get()); + if(mMediaRecorder == NULL) { + LOGE("media recorder is not initialized yet"); + return INVALID_OPERATION; + } + if (!(mCurrentState & MEDIA_RECORDER_IDLE)) { + LOGE("setCamera called in an invalid state(%d)", mCurrentState); + return INVALID_OPERATION; + } + + status_t ret = mMediaRecorder->setCamera(camera); + if (OK != ret) { + LOGV("setCamera failed: %d", ret); + mCurrentState = MEDIA_RECORDER_ERROR; + return UNKNOWN_ERROR; + } + return ret; +} + +status_t MediaRecorder::setPreviewSurface(const sp& surface) +{ + LOGV("setPreviewSurface(%p)", surface.get()); + if(mMediaRecorder == NULL) { + LOGE("media recorder is not initialized yet"); + return INVALID_OPERATION; + } + if (!(mCurrentState & MEDIA_RECORDER_DATASOURCE_CONFIGURED)) { + LOGE("setPreviewSurface called in an invalid state(%d)", mCurrentState); + return INVALID_OPERATION; + } + + status_t ret = mMediaRecorder->setPreviewSurface(surface->getISurface()); + if (OK != ret) { + LOGV("setPreviewSurface failed: %d", ret); + mCurrentState = MEDIA_RECORDER_ERROR; + return UNKNOWN_ERROR; + } + return ret; +} + +status_t MediaRecorder::init() +{ + LOGV("init"); + if(mMediaRecorder == NULL) { + LOGE("media recorder is not initialized yet"); + return INVALID_OPERATION; + } + if (!(mCurrentState & MEDIA_RECORDER_IDLE)) { + LOGE("init called in an invalid state(%d)", mCurrentState); + return INVALID_OPERATION; + } + + status_t ret = mMediaRecorder->init(); + if (OK != ret) { + LOGV("init failed: %d", ret); + mCurrentState = MEDIA_RECORDER_ERROR; + return UNKNOWN_ERROR; + } + mCurrentState = MEDIA_RECORDER_INITIALIZED; + return ret; +} + +status_t MediaRecorder::setVideoSource(int vs) +{ + LOGV("setVideoSource(%d)", vs); + if(mMediaRecorder == NULL) { + LOGE("media recorder is not initialized yet"); + return INVALID_OPERATION; + } + if (mIsVideoSourceSet) { + LOGE("video source has already been set"); + return INVALID_OPERATION; + } + if (mCurrentState & MEDIA_RECORDER_IDLE) { + LOGV("Call init() since the media recorder is not initialized yet"); + status_t ret = init(); + if (OK != ret) { + return ret; + } + } + if (!(mCurrentState & MEDIA_RECORDER_INITIALIZED)) { + LOGE("setVideoSource called in an invalid state(%d)", mCurrentState); + return INVALID_OPERATION; + } + + status_t ret = mMediaRecorder->setVideoSource(vs); + if (OK != ret) { + LOGV("setVideoSource failed: %d", ret); + mCurrentState = MEDIA_RECORDER_ERROR; + return UNKNOWN_ERROR; + } + mIsVideoSourceSet = true; + return ret; +} + +status_t MediaRecorder::setAudioSource(int as) +{ + LOGV("setAudioSource(%d)", as); + if(mMediaRecorder == NULL) { + LOGE("media recorder is not initialized yet"); + return INVALID_OPERATION; + } + if (mCurrentState & MEDIA_RECORDER_IDLE) { + LOGV("Call init() since the media recorder is not initialized yet"); + status_t ret = init(); + if (OK != ret) { + return ret; + } + } + if (mIsAudioSourceSet) { + LOGE("audio source has already been set"); + return INVALID_OPERATION; + } + if (!(mCurrentState & MEDIA_RECORDER_INITIALIZED)) { + LOGE("setAudioSource called in an invalid state(%d)", mCurrentState); + return INVALID_OPERATION; + } + + status_t ret = mMediaRecorder->setAudioSource(as); + if (OK != ret) { + LOGV("setAudioSource failed: %d", ret); + mCurrentState = MEDIA_RECORDER_ERROR; + return UNKNOWN_ERROR; + } + mIsAudioSourceSet = true; + return ret; +} + +status_t MediaRecorder::setOutputFormat(int of) +{ + LOGV("setOutputFormat(%d)", of); + if(mMediaRecorder == NULL) { + LOGE("media recorder is not initialized yet"); + return INVALID_OPERATION; + } + if (!(mCurrentState & MEDIA_RECORDER_INITIALIZED)) { + LOGE("setOutputFormat called in an invalid state: %d", mCurrentState); + return INVALID_OPERATION; + } + + status_t ret = mMediaRecorder->setOutputFormat(of); + if (OK != ret) { + LOGE("setOutputFormat failed: %d", ret); + mCurrentState = MEDIA_RECORDER_ERROR; + return UNKNOWN_ERROR; + } + mCurrentState = MEDIA_RECORDER_DATASOURCE_CONFIGURED; + return ret; +} + +status_t MediaRecorder::setVideoEncoder(int ve) +{ + LOGV("setVideoEncoder(%d)", ve); + if(mMediaRecorder == NULL) { + LOGE("media recorder is not initialized yet"); + return INVALID_OPERATION; + } + if (mIsVideoEncoderSet) { + LOGE("video encoder has already been set"); + return INVALID_OPERATION; + } + if (!(mCurrentState & MEDIA_RECORDER_DATASOURCE_CONFIGURED)) { + LOGE("setVideoEncoder called in an invalid state(%d)", mCurrentState); + return INVALID_OPERATION; + } + + status_t ret = mMediaRecorder->setVideoEncoder(ve); + if (OK != ret) { + LOGV("setVideoEncoder failed: %d", ret); + mCurrentState = MEDIA_RECORDER_ERROR; + return UNKNOWN_ERROR; + } + mIsVideoEncoderSet = true; + return ret; +} + +status_t MediaRecorder::setAudioEncoder(int ae) +{ + LOGV("setAudioEncoder(%d)", ae); + if(mMediaRecorder == NULL) { + LOGE("media recorder is not initialized yet"); + return INVALID_OPERATION; + } + if (mIsAudioEncoderSet) { + LOGE("audio encoder has already been set"); + return INVALID_OPERATION; + } + if (!(mCurrentState & MEDIA_RECORDER_DATASOURCE_CONFIGURED)) { + LOGE("setAudioEncoder called in an invalid state(%d)", mCurrentState); + return INVALID_OPERATION; + } + + status_t ret = mMediaRecorder->setAudioEncoder(ae); + if (OK != ret) { + LOGV("setAudioEncoder failed: %d", ret); + mCurrentState = MEDIA_RECORDER_ERROR; + return UNKNOWN_ERROR; + } + mIsAudioEncoderSet = true; + return ret; +} + +status_t MediaRecorder::setOutputFile(const char* path) +{ + LOGV("setOutputFile(%s)", path); + if(mMediaRecorder == NULL) { + LOGE("media recorder is not initialized yet"); + return INVALID_OPERATION; + } + if (mIsOutputFileSet) { + LOGE("output file has already been set"); + return INVALID_OPERATION; + } + if (!(mCurrentState & MEDIA_RECORDER_DATASOURCE_CONFIGURED)) { + LOGE("setOutputFile called in an invalid state(%d)", mCurrentState); + return INVALID_OPERATION; + } + + status_t ret = mMediaRecorder->setOutputFile(path); + if (OK != ret) { + LOGV("setAudioEncoder failed: %d", ret); + mCurrentState = MEDIA_RECORDER_ERROR; + return UNKNOWN_ERROR; + } + mIsOutputFileSet = true; + return ret; +} + +status_t MediaRecorder::setVideoSize(int width, int height) +{ + LOGV("setVideoSize(%d, %d)", width, height); + if(mMediaRecorder == NULL) { + LOGE("media recorder is not initialized yet"); + return INVALID_OPERATION; + } + if (!(mCurrentState & MEDIA_RECORDER_DATASOURCE_CONFIGURED)) { + LOGE("setVideoSize called in an invalid state: %d", mCurrentState); + return INVALID_OPERATION; + } + + status_t ret = mMediaRecorder->setVideoSize(width, height); + if (OK != ret) { + LOGE("setVideoSize failed: %d", ret); + mCurrentState = MEDIA_RECORDER_ERROR; + return UNKNOWN_ERROR; + } + return ret; +} + +status_t MediaRecorder::setVideoFrameRate(int frames_per_second) +{ + LOGV("setVideoFrameRate(%d)", frames_per_second); + if(mMediaRecorder == NULL) { + LOGE("media recorder is not initialized yet"); + return INVALID_OPERATION; + } + if (!(mCurrentState & MEDIA_RECORDER_DATASOURCE_CONFIGURED)) { + LOGE("setVideoFrameRate called in an invalid state: %d", mCurrentState); + return INVALID_OPERATION; + } + + status_t ret = mMediaRecorder->setVideoFrameRate(frames_per_second); + if (OK != ret) { + LOGE("setVideoFrameRate failed: %d", ret); + mCurrentState = MEDIA_RECORDER_ERROR; + return UNKNOWN_ERROR; + } + return ret; +} + +status_t MediaRecorder::prepare() +{ + LOGV("prepare"); + if(mMediaRecorder == NULL) { + LOGE("media recorder is not initialized yet"); + return INVALID_OPERATION; + } + if (!(mCurrentState & MEDIA_RECORDER_DATASOURCE_CONFIGURED)) { + LOGE("setVideoFrameRate called in an invalid state: %d", mCurrentState); + return INVALID_OPERATION; + } + + status_t ret = mMediaRecorder->prepare(); + if (OK != ret) { + LOGE("prepare failed: %d", ret); + mCurrentState = MEDIA_RECORDER_ERROR; + return UNKNOWN_ERROR; + } + mCurrentState = MEDIA_RECORDER_PREPARED; + return ret; +} + +status_t MediaRecorder::getMaxAmplitude(int* max) +{ + LOGV("getMaxAmplitude"); + if(mMediaRecorder == NULL) { + LOGE("media recorder is not initialized yet"); + return INVALID_OPERATION; + } + if (mCurrentState & MEDIA_RECORDER_ERROR) { + LOGE("setVideoFrameRate called in an invalid state: %d", mCurrentState); + return INVALID_OPERATION; + } + + status_t ret = mMediaRecorder->getMaxAmplitude(max); + if (OK != ret) { + LOGE("getMaxAmplitude failed: %d", ret); + mCurrentState = MEDIA_RECORDER_ERROR; + return UNKNOWN_ERROR; + } + return ret; +} + +status_t MediaRecorder::start() +{ + LOGV("start"); + if (mMediaRecorder == NULL) { + LOGE("media recorder is not initialized yet"); + return INVALID_OPERATION; + } + if (!(mCurrentState & MEDIA_RECORDER_PREPARED)) { + LOGE("start called in an invalid state: %d", mCurrentState); + return INVALID_OPERATION; + } + + status_t ret = mMediaRecorder->start(); + if (OK != ret) { + LOGE("start failed: %d", ret); + mCurrentState = MEDIA_RECORDER_ERROR; + return UNKNOWN_ERROR; + } + mCurrentState = MEDIA_RECORDER_RECORDING; + return ret; +} + +status_t MediaRecorder::stop() +{ + LOGV("stop"); + if (mMediaRecorder == NULL) { + LOGE("media recorder is not initialized yet"); + return INVALID_OPERATION; + } + if (!(mCurrentState & MEDIA_RECORDER_RECORDING)) { + LOGE("stop called in an invalid state: %d", mCurrentState); + return INVALID_OPERATION; + } + + status_t ret = mMediaRecorder->stop(); + if (OK != ret) { + LOGE("stop failed: %d", ret); + mCurrentState = MEDIA_RECORDER_ERROR; + return UNKNOWN_ERROR; + } + mCurrentState = MEDIA_RECORDER_IDLE; + return ret; +} + +// Reset should be OK in any state +status_t MediaRecorder::reset() +{ + LOGV("reset"); + if (mMediaRecorder == NULL) { + LOGE("media recorder is not initialized yet"); + return INVALID_OPERATION; + } + + doCleanUp(); + status_t ret = UNKNOWN_ERROR; + switch(mCurrentState) { + case MEDIA_RECORDER_IDLE: + ret = OK; + break; + + case MEDIA_RECORDER_RECORDING: + case MEDIA_RECORDER_DATASOURCE_CONFIGURED: + case MEDIA_RECORDER_PREPARED: + case MEDIA_RECORDER_ERROR: { + ret = doReset(); + if (OK != ret) { + return ret; // No need to continue + } + } // Intentional fall through + case MEDIA_RECORDER_INITIALIZED: + ret = close(); + break; + + default: { + LOGE("Unexpected non-existing state: %d", mCurrentState); + break; + } + } + return ret; +} + +status_t MediaRecorder::close() +{ + LOGV("close"); + if (!(mCurrentState & MEDIA_RECORDER_INITIALIZED)) { + LOGE("close called in an invalid state: %d", mCurrentState); + return INVALID_OPERATION; + } + status_t ret = mMediaRecorder->close(); + if (OK != ret) { + LOGE("close failed: %d", ret); + mCurrentState = MEDIA_RECORDER_ERROR; + return UNKNOWN_ERROR; + } else { + mCurrentState = MEDIA_RECORDER_IDLE; + } + return ret; +} + +status_t MediaRecorder::doReset() +{ + LOGV("doReset"); + status_t ret = mMediaRecorder->reset(); + if (OK != ret) { + LOGE("doReset failed: %d", ret); + mCurrentState = MEDIA_RECORDER_ERROR; + return UNKNOWN_ERROR; + } else { + mCurrentState = MEDIA_RECORDER_INITIALIZED; + } + return ret; +} + +void MediaRecorder::doCleanUp() +{ + LOGV("doCleanUp"); + mIsAudioSourceSet = false; + mIsVideoSourceSet = false; + mIsAudioEncoderSet = false; + mIsVideoEncoderSet = false; + mIsOutputFileSet = false; +} + +// Release should be OK in any state +status_t MediaRecorder::release() +{ + LOGV("release"); + if (mMediaRecorder != NULL) { + return mMediaRecorder->release(); + } + return INVALID_OPERATION; +} + +MediaRecorder::MediaRecorder() +{ + LOGV("constructor"); + sp sm = defaultServiceManager(); + sp binder; + + do { + binder = sm->getService(String16("media.player")); + if (binder != NULL) { + break; + } + LOGW("MediaPlayerService not published, waiting..."); + usleep(500000); // 0.5 s + } while(true); + + sp service = interface_cast(binder); + if (service != NULL) { + mMediaRecorder = service->createMediaRecorder(getpid()); + } + + mMediaRecorder = service->createMediaRecorder(getpid()); + if (mMediaRecorder != NULL) { + mCurrentState = MEDIA_RECORDER_IDLE; + } + doCleanUp(); +} + +status_t MediaRecorder::initCheck() +{ + return mMediaRecorder != 0 ? NO_ERROR : NO_INIT; +} + +MediaRecorder::~MediaRecorder() +{ + LOGV("destructor"); + if (mMediaRecorder != NULL) { + mMediaRecorder.clear(); + } +} + +}; // namespace android + diff --git a/media/libmediaplayerservice/Android.mk b/media/libmediaplayerservice/Android.mk index b3a5747..f710921 100644 --- a/media/libmediaplayerservice/Android.mk +++ b/media/libmediaplayerservice/Android.mk @@ -7,7 +7,9 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ + MediaRecorderClient.cpp \ MediaPlayerService.cpp \ + MetadataRetrieverClient.cpp \ VorbisPlayer.cpp \ MidiFile.cpp @@ -21,6 +23,7 @@ LOCAL_SHARED_LIBRARIES := \ libvorbisidec \ libsonivox \ libopencoreplayer \ + libopencoreauthor \ libmedia \ libandroid_runtime diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp index fd5f0ed..5383171 100644 --- a/media/libmediaplayerservice/MediaPlayerService.cpp +++ b/media/libmediaplayerservice/MediaPlayerService.cpp @@ -34,11 +34,17 @@ #include #include #include +#include #include +#include +#include #include +#include "MediaRecorderClient.h" #include "MediaPlayerService.h" +#include "MetadataRetrieverClient.h" + #include "MidiFile.h" #include "VorbisPlayer.h" #include @@ -72,7 +78,7 @@ pid_t gettid() { return syscall(__NR_gettid);} restart continuously. */ #define USE_SIGBUS_HANDLER 0 - + // TODO: Temp hack until we can register players static const char* MIDI_FILE_EXTS[] = { @@ -87,8 +93,10 @@ static const char* MIDI_FILE_EXTS[] = namespace android { -// TODO: should come from audio driver -/* static */ const uint32_t MediaPlayerService::AudioOutput::kDriverLatencyInMsecs = 150; +// TODO: Find real cause of Audio/Video delay in PV framework and remove this workaround +/* static */ const uint32_t MediaPlayerService::AudioOutput::kAudioVideoDelayMs = 96; +/* static */ int MediaPlayerService::AudioOutput::mMinBufferCount = 4; +/* static */ bool MediaPlayerService::AudioOutput::mIsOnEmulator = false; static struct sigaction oldact; static pthread_key_t sigbuskey; @@ -172,7 +180,7 @@ MediaPlayerService::MediaPlayerService() pthread_key_create(&sigbuskey, NULL); - + #if USE_SIGBUS_HANDLER struct sigaction act; memset(&act,0, sizeof act); @@ -191,6 +199,20 @@ MediaPlayerService::~MediaPlayerService() LOGV("MediaPlayerService destroyed"); } +sp MediaPlayerService::createMediaRecorder(pid_t pid) +{ + sp recorder = new MediaRecorderClient(pid); + LOGV("Create new media recorder client from pid %d", pid); + return recorder; +} + +sp MediaPlayerService::createMetadataRetriever(pid_t pid) +{ + sp retriever = new MetadataRetrieverClient(pid); + LOGV("Create new media retriever from pid %d", pid); + return retriever; +} + sp MediaPlayerService::create(pid_t pid, const sp& client, const char* url) { int32_t connId = android_atomic_inc(&mNextConnId); @@ -237,8 +259,8 @@ status_t MediaPlayerService::AudioCache::dump(int fd, const Vector& ar mHeap->getBase(), mHeap->getSize(), mHeap->getFlags(), mHeap->getDevice()); result.append(buffer); } - snprintf(buffer, 255, " msec per frame(%f), channel count(%ld), frame count(%ld)\n", - mMsecsPerFrame, mChannelCount, mFrameCount); + snprintf(buffer, 255, " msec per frame(%f), channel count(%d), format(%d), frame count(%ld)\n", + mMsecsPerFrame, mChannelCount, mFormat, mFrameCount); result.append(buffer); snprintf(buffer, 255, " sample rate(%d), size(%d), error(%d), command complete(%s)\n", mSampleRate, mSize, mError, mCommandComplete?"true":"false"); @@ -257,8 +279,8 @@ status_t MediaPlayerService::AudioOutput::dump(int fd, const Vector& a snprintf(buffer, 255, " stream type(%d), left - right volume(%f, %f)\n", mStreamType, mLeftVolume, mRightVolume); result.append(buffer); - snprintf(buffer, 255, " msec per frame(%f), latency (%d), driver latency(%d)\n", - mMsecsPerFrame, mLatency, kDriverLatencyInMsecs); + snprintf(buffer, 255, " msec per frame(%f), latency (%d)\n", + mMsecsPerFrame, mLatency); result.append(buffer); ::write(fd, result.string(), result.size()); if (mTrack != 0) { @@ -315,7 +337,7 @@ status_t MediaPlayerService::dump(int fd, const Vector& args) if (f) { while (!feof(f)) { fgets(buffer, SIZE, f); - if (strstr(buffer, " /sdcard/") || + if (strstr(buffer, " /sdcard/") || strstr(buffer, " /system/sounds/") || strstr(buffer, " /system/media/")) { result.append(" "); @@ -334,7 +356,7 @@ status_t MediaPlayerService::dump(int fd, const Vector& args) if (d) { struct dirent *ent; while((ent = readdir(d)) != NULL) { - if (strcmp(ent->d_name,".") && strcmp(ent->d_name,"..")) { + if (strcmp(ent->d_name,".") && strcmp(ent->d_name,"..")) { snprintf(buffer, SIZE, "/proc/%d/fd/%s", myTid(), ent->d_name); struct stat s; if (lstat(buffer, &s) == 0) { @@ -350,7 +372,7 @@ status_t MediaPlayerService::dump(int fd, const Vector& args) } else { linkto[len] = 0; } - if (strstr(linkto, "/sdcard/") == linkto || + if (strstr(linkto, "/sdcard/") == linkto || strstr(linkto, "/system/sounds/") == linkto || strstr(linkto, "/system/media/") == linkto) { result.append(" "); @@ -683,20 +705,6 @@ status_t MediaPlayerService::Client::isPlaying(bool* state) return NO_ERROR; } -status_t MediaPlayerService::Client::getVideoSize(int *w, int *h) -{ - sp p = getPlayer(); - if (p == 0) return UNKNOWN_ERROR; - status_t ret = p->getVideoWidth(w); - if (ret == NO_ERROR) ret = p->getVideoHeight(h); - if (ret == NO_ERROR) { - LOGV("[%d] getVideoWidth = (%d, %d)", mConnId, *w, *h); - } else { - LOGE("getVideoSize returned %d", ret); - } - return ret; -} - status_t MediaPlayerService::Client::getCurrentPosition(int *msec) { LOGV("getCurrentPosition"); @@ -812,7 +820,7 @@ int Antagonizer::callbackThread(void* user) static size_t kDefaultHeapSize = 1024 * 1024; // 1MB -sp MediaPlayerService::decode(const char* url, uint32_t *pSampleRate, int* pNumChannels) +sp MediaPlayerService::decode(const char* url, uint32_t *pSampleRate, int* pNumChannels, int* pFormat) { LOGV("decode(%s)", url); sp mem; @@ -856,14 +864,15 @@ sp MediaPlayerService::decode(const char* url, uint32_t *pSampleRate, i mem = new MemoryBase(cache->getHeap(), 0, cache->size()); *pSampleRate = cache->sampleRate(); *pNumChannels = cache->channelCount(); - LOGV("return memory @ %p, sampleRate=%u, channelCount = %d", mem->pointer(), *pSampleRate, *pNumChannels); + *pFormat = cache->format(); + LOGV("return memory @ %p, sampleRate=%u, channelCount = %d, format = %d", mem->pointer(), *pSampleRate, *pNumChannels, *pFormat); Exit: if (player != 0) player->reset(); return mem; } -sp MediaPlayerService::decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels) +sp MediaPlayerService::decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels, int* pFormat) { LOGV("decode(%d, %lld, %lld)", fd, offset, length); sp mem; @@ -898,7 +907,8 @@ sp MediaPlayerService::decode(int fd, int64_t offset, int64_t length, u mem = new MemoryBase(cache->getHeap(), 0, cache->size()); *pSampleRate = cache->sampleRate(); *pNumChannels = cache->channelCount(); - LOGV("return memory @ %p, sampleRate=%u, channelCount = %d", mem->pointer(), *pSampleRate, *pNumChannels); + *pFormat = cache->format(); + LOGV("return memory @ %p, sampleRate=%u, channelCount = %d, format = %d", mem->pointer(), *pSampleRate, *pNumChannels, *pFormat); Exit: if (player != 0) player->reset(); @@ -916,6 +926,7 @@ MediaPlayerService::AudioOutput::AudioOutput() mRightVolume = 1.0; mLatency = 0; mMsecsPerFrame = 0; + setMinBufferCount(); } MediaPlayerService::AudioOutput::~AudioOutput() @@ -923,10 +934,31 @@ MediaPlayerService::AudioOutput::~AudioOutput() close(); } +void MediaPlayerService::AudioOutput::setMinBufferCount() +{ + char value[PROPERTY_VALUE_MAX]; + if (property_get("ro.kernel.qemu", value, 0)) { + mIsOnEmulator = true; + mMinBufferCount = 12; // to prevent systematic buffer underrun for emulator + } +} + +bool MediaPlayerService::AudioOutput::isOnEmulator() +{ + setMinBufferCount(); + return mIsOnEmulator; +} + +int MediaPlayerService::AudioOutput::getMinBufferCount() +{ + setMinBufferCount(); + return mMinBufferCount; +} + ssize_t MediaPlayerService::AudioOutput::bufferSize() const { if (mTrack == 0) return NO_INIT; - return mTrack->frameCount() * mTrack->channelCount() * sizeof(int16_t); + return mTrack->frameCount() * frameSize(); } ssize_t MediaPlayerService::AudioOutput::frameCount() const @@ -944,7 +976,7 @@ ssize_t MediaPlayerService::AudioOutput::channelCount() const ssize_t MediaPlayerService::AudioOutput::frameSize() const { if (mTrack == 0) return NO_INIT; - return mTrack->channelCount() * sizeof(int16_t); + return mTrack->frameSize(); } uint32_t MediaPlayerService::AudioOutput::latency () const @@ -957,12 +989,29 @@ float MediaPlayerService::AudioOutput::msecsPerFrame() const return mMsecsPerFrame; } -status_t MediaPlayerService::AudioOutput::open(uint32_t sampleRate, int channelCount, int bufferCount) +status_t MediaPlayerService::AudioOutput::open(uint32_t sampleRate, int channelCount, int format, int bufferCount) { - LOGV("open(%u, %d, %d)", sampleRate, channelCount, bufferCount); + // Check argument "bufferCount" against the mininum buffer count + if (bufferCount < mMinBufferCount) { + LOGD("bufferCount (%d) is too small and increased to %d", bufferCount, mMinBufferCount); + bufferCount = mMinBufferCount; + + } + LOGV("open(%u, %d, %d, %d)", sampleRate, channelCount, format, bufferCount); if (mTrack) close(); + int afSampleRate; + int afFrameCount; + int frameCount; - AudioTrack *t = new AudioTrack(mStreamType, sampleRate, AudioSystem::PCM_16_BIT, channelCount, bufferCount); + if (AudioSystem::getOutputFrameCount(&afFrameCount) != NO_ERROR) { + return NO_INIT; + } + if (AudioSystem::getOutputSamplingRate(&afSampleRate) != NO_ERROR) { + return NO_INIT; + } + + frameCount = (sampleRate*afFrameCount)/afSampleRate; + AudioTrack *t = new AudioTrack(mStreamType, sampleRate, format, channelCount, frameCount*bufferCount); if ((t == 0) || (t->initCheck() != NO_ERROR)) { LOGE("Unable to create audio track"); delete t; @@ -972,7 +1021,7 @@ status_t MediaPlayerService::AudioOutput::open(uint32_t sampleRate, int channelC LOGV("setVolume"); t->setVolume(mLeftVolume, mRightVolume); mMsecsPerFrame = 1.e3 / (float) sampleRate; - mLatency = (mMsecsPerFrame * bufferCount * t->frameCount()) + kDriverLatencyInMsecs; + mLatency = t->latency() + kAudioVideoDelayMs; mTrack = t; return NO_ERROR; } @@ -1031,7 +1080,7 @@ void MediaPlayerService::AudioOutput::setVolume(float left, float right) #undef LOG_TAG #define LOG_TAG "AudioCache" MediaPlayerService::AudioCache::AudioCache(const char* name) : - mChannelCount(0), mFrameCount(0), mSampleRate(0), mSize(0), + mChannelCount(0), mFrameCount(1024), mSampleRate(0), mSize(0), mError(NO_ERROR), mCommandComplete(false) { // create ashmem heap @@ -1048,12 +1097,13 @@ float MediaPlayerService::AudioCache::msecsPerFrame() const return mMsecsPerFrame; } -status_t MediaPlayerService::AudioCache::open(uint32_t sampleRate, int channelCount, int bufferCount) +status_t MediaPlayerService::AudioCache::open(uint32_t sampleRate, int channelCount, int format, int bufferCount) { - LOGV("open(%u, %d, %d)", sampleRate, channelCount, bufferCount); - if (mHeap->getHeapID() < 0) return NO_INIT; - mSampleRate = sampleRate; - mChannelCount = channelCount; + LOGV("open(%u, %d, %d, %d)", sampleRate, channelCount, format, bufferCount); + if (mHeap->getHeapID() < 0) return NO_INIT; + mSampleRate = sampleRate; + mChannelCount = (uint16_t)channelCount; + mFormat = (uint16_t)format; mMsecsPerFrame = 1.e3 / (float) sampleRate; return NO_ERROR; } @@ -1067,6 +1117,10 @@ ssize_t MediaPlayerService::AudioCache::write(const void* buffer, size_t size) if (p == NULL) return NO_INIT; p += mSize; LOGV("memcpy(%p, %p, %u)", p, buffer, size); + if (mSize + size > mHeap->getSize()) { + LOGE("Heap size overflow! req size: %d, max size: %d", (mSize + size), mHeap->getSize()); + size = mHeap->getSize() - mSize; + } memcpy(p, buffer, size); mSize += size; return size; diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h index c2007cb..f138886 100644 --- a/media/libmediaplayerservice/MediaPlayerService.h +++ b/media/libmediaplayerservice/MediaPlayerService.h @@ -25,10 +25,11 @@ #include #include -class SkBitmap; - namespace android { +class IMediaRecorder; +class IMediaMetadataRetriever; + #define CALLBACK_ANTAGONIZER 0 #if CALLBACK_ANTAGONIZER class Antagonizer { @@ -68,7 +69,7 @@ class MediaPlayerService : public BnMediaPlayerService virtual ssize_t frameSize() const; virtual uint32_t latency() const; virtual float msecsPerFrame() const; - virtual status_t open(uint32_t sampleRate, int channelCount, int bufferCount=4); + virtual status_t open(uint32_t sampleRate, int channelCount, int format, int bufferCount=4); virtual void start(); virtual ssize_t write(const void* buffer, size_t size); virtual void stop(); @@ -78,14 +79,24 @@ class MediaPlayerService : public BnMediaPlayerService void setAudioStreamType(int streamType) { mStreamType = streamType; } void setVolume(float left, float right); virtual status_t dump(int fd, const Vector& args) const; + + static bool isOnEmulator(); + static int getMinBufferCount(); private: + static void setMinBufferCount(); + AudioTrack* mTrack; int mStreamType; float mLeftVolume; float mRightVolume; float mMsecsPerFrame; uint32_t mLatency; - static const uint32_t kDriverLatencyInMsecs; + + // TODO: Find real cause of Audio/Video delay in PV framework and remove this workaround + static const uint32_t kAudioVideoDelayMs; + static bool mIsOnEmulator; + static int mMinBufferCount; // 12 for emulator; otherwise 4 + }; class AudioCache : public MediaPlayerBase::AudioSink @@ -96,13 +107,13 @@ class MediaPlayerService : public BnMediaPlayerService virtual bool ready() const { return (mChannelCount > 0) && (mHeap->getHeapID() > 0); } virtual bool realtime() const { return false; } - virtual ssize_t bufferSize() const { return 4096; } + virtual ssize_t bufferSize() const { return frameSize() * mFrameCount; } virtual ssize_t frameCount() const { return mFrameCount; } - virtual ssize_t channelCount() const { return mChannelCount; } - virtual ssize_t frameSize() const { return ssize_t(mChannelCount * sizeof(int16_t)); } + virtual ssize_t channelCount() const { return (ssize_t)mChannelCount; } + virtual ssize_t frameSize() const { return ssize_t(mChannelCount * ((mFormat == AudioSystem::PCM_16_BIT)?sizeof(int16_t):sizeof(u_int8_t))); } virtual uint32_t latency() const; virtual float msecsPerFrame() const; - virtual status_t open(uint32_t sampleRate, int channelCount, int bufferCount=1); + virtual status_t open(uint32_t sampleRate, int channelCount, int format, int bufferCount=1); virtual void start() {} virtual ssize_t write(const void* buffer, size_t size); virtual void stop() {} @@ -112,6 +123,7 @@ class MediaPlayerService : public BnMediaPlayerService void setAudioStreamType(int streamType) {} void setVolume(float left, float right) {} uint32_t sampleRate() const { return mSampleRate; } + uint32_t format() const { return (uint32_t)mFormat; } size_t size() const { return mSize; } status_t wait(); @@ -127,7 +139,8 @@ class MediaPlayerService : public BnMediaPlayerService Condition mSignal; sp mHeap; float mMsecsPerFrame; - ssize_t mChannelCount; + uint16_t mChannelCount; + uint16_t mFormat; ssize_t mFrameCount; uint32_t mSampleRate; uint32_t mSize; @@ -139,10 +152,14 @@ public: static void instantiate(); // IMediaPlayerService interface + virtual sp createMediaRecorder(pid_t pid); + virtual sp createMetadataRetriever(pid_t pid); + + // House keeping for media player clients virtual sp create(pid_t pid, const sp& client, const char* url); virtual sp create(pid_t pid, const sp& client, int fd, int64_t offset, int64_t length); - virtual sp decode(const char* url, uint32_t *pSampleRate, int* pNumChannels); - virtual sp decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels); + virtual sp decode(const char* url, uint32_t *pSampleRate, int* pNumChannels, int* pFormat); + virtual sp decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels, int* pFormat); virtual status_t dump(int fd, const Vector& args); @@ -160,7 +177,6 @@ private: virtual status_t stop(); virtual status_t pause(); virtual status_t isPlaying(bool* state); - virtual status_t getVideoSize(int* w, int* h); virtual status_t seekTo(int msec); virtual status_t getCurrentPosition(int* msec); virtual status_t getDuration(int* msec); diff --git a/media/libmediaplayerservice/MediaRecorderClient.cpp b/media/libmediaplayerservice/MediaRecorderClient.cpp new file mode 100644 index 0000000..f326a0e --- /dev/null +++ b/media/libmediaplayerservice/MediaRecorderClient.cpp @@ -0,0 +1,251 @@ +/* + ** Copyright 2008, HTC Inc. + ** + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** + ** http://www.apache.org/licenses/LICENSE-2.0 + ** + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "MediaRecorderService" +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "MediaRecorderClient.h" + +namespace android { + +status_t MediaRecorderClient::setCamera(const sp& camera) +{ + LOGV("setCamera"); + Mutex::Autolock lock(mLock); + if (mRecorder == NULL) { + LOGE("recorder is not initialized"); + return NO_INIT; + } + return mRecorder->setCamera(camera); +} + +status_t MediaRecorderClient::setPreviewSurface(const sp& surface) +{ + LOGV("setPreviewSurface"); + Mutex::Autolock lock(mLock); + if (mRecorder == NULL) { + LOGE("recorder is not initialized"); + return NO_INIT; + } + return mRecorder->setPreviewSurface(surface); +} + +status_t MediaRecorderClient::setVideoSource(int vs) +{ + LOGV("setVideoSource(%d)", vs); + Mutex::Autolock lock(mLock); + if (mRecorder == NULL) { + LOGE("recorder is not initialized"); + } + return mRecorder->setVideoSource((video_source)vs); +} + +status_t MediaRecorderClient::setAudioSource(int as) +{ + LOGV("setAudioSource(%d)", as); + Mutex::Autolock lock(mLock); + if (mRecorder == NULL) { + LOGE("recorder is not initialized"); + } + return mRecorder->setAudioSource((audio_source)as); +} + +status_t MediaRecorderClient::setOutputFormat(int of) +{ + LOGV("setOutputFormat(%d)", of); + Mutex::Autolock lock(mLock); + if (mRecorder == NULL) { + LOGE("recorder is not initialized"); + return NO_INIT; + } + return mRecorder->setOutputFormat((output_format)of); +} + +status_t MediaRecorderClient::setVideoEncoder(int ve) +{ + LOGV("setVideoEncoder(%d)", ve); + Mutex::Autolock lock(mLock); + if (mRecorder == NULL) { + LOGE("recorder is not initialized"); + return NO_INIT; + } + return mRecorder->setVideoEncoder((video_encoder)ve); +} + +status_t MediaRecorderClient::setAudioEncoder(int ae) +{ + LOGV("setAudioEncoder(%d)", ae); + Mutex::Autolock lock(mLock); + if (mRecorder == NULL) { + LOGE("recorder is not initialized"); + return NO_INIT; + } + return mRecorder->setAudioEncoder((audio_encoder)ae); +} + +status_t MediaRecorderClient::setOutputFile(const char* path) +{ + LOGV("setOutputFile(%s)", path); + Mutex::Autolock lock(mLock); + if (mRecorder == NULL) { + LOGE("recorder is not initialized"); + return NO_INIT; + } + return mRecorder->setOutputFile(path); +} + +status_t MediaRecorderClient::setVideoSize(int width, int height) +{ + LOGV("setVideoSize(%dx%d)", width, height); + Mutex::Autolock lock(mLock); + if (mRecorder == NULL) { + LOGE("recorder is not initialized"); + return NO_INIT; + } + return mRecorder->setVideoSize(width, height); +} + +status_t MediaRecorderClient::setVideoFrameRate(int frames_per_second) +{ + LOGV("setVideoFrameRate(%d)", frames_per_second); + Mutex::Autolock lock(mLock); + if (mRecorder == NULL) { + LOGE("recorder is not initialized"); + return NO_INIT; + } + return mRecorder->setVideoFrameRate(frames_per_second); +} + +status_t MediaRecorderClient::prepare() +{ + LOGV("prepare"); + Mutex::Autolock lock(mLock); + if (mRecorder == NULL) { + LOGE("recorder is not initialized"); + return NO_INIT; + } + return mRecorder->prepare(); +} + + +status_t MediaRecorderClient::getMaxAmplitude(int* max) +{ + LOGV("getMaxAmplitude"); + Mutex::Autolock lock(mLock); + if (mRecorder == NULL) { + LOGE("recorder is not initialized"); + return NO_INIT; + } + return mRecorder->getMaxAmplitude(max); +} + +status_t MediaRecorderClient::start() +{ + LOGV("start"); + Mutex::Autolock lock(mLock); + if (mRecorder == NULL) { + LOGE("recorder is not initialized"); + return NO_INIT; + } + return mRecorder->start(); + +} + +status_t MediaRecorderClient::stop() +{ + LOGV("stop"); + Mutex::Autolock lock(mLock); + if (mRecorder == NULL) { + LOGE("recorder is not initialized"); + return NO_INIT; + } + return mRecorder->stop(); +} + +status_t MediaRecorderClient::init() +{ + LOGV("init"); + Mutex::Autolock lock(mLock); + if (mRecorder == NULL) { + LOGE("recorder is not initialized"); + return NO_INIT; + } + return mRecorder->init(); +} + +status_t MediaRecorderClient::close() +{ + LOGV("close"); + Mutex::Autolock lock(mLock); + if (mRecorder == NULL) { + LOGE("recorder is not initialized"); + return NO_INIT; + } + return mRecorder->close(); +} + + +status_t MediaRecorderClient::reset() +{ + LOGV("reset"); + Mutex::Autolock lock(mLock); + if (mRecorder == NULL) { + LOGE("recorder is not initialized"); + return NO_INIT; + } + return mRecorder->reset(); +} + +status_t MediaRecorderClient::release() +{ + LOGV("release"); + Mutex::Autolock lock(mLock); + if (mRecorder != NULL) { + delete mRecorder; + mRecorder = NULL; + } + return NO_ERROR; +} + +MediaRecorderClient::MediaRecorderClient(pid_t pid) +{ + LOGV("Client constructor"); + mPid = pid; + mRecorder = new PVMediaRecorder(); +} + +MediaRecorderClient::~MediaRecorderClient() +{ + LOGV("Client destructor"); + release(); +} + +}; // namespace android + diff --git a/media/libmediaplayerservice/MediaRecorderClient.h b/media/libmediaplayerservice/MediaRecorderClient.h new file mode 100644 index 0000000..3158017 --- /dev/null +++ b/media/libmediaplayerservice/MediaRecorderClient.h @@ -0,0 +1,64 @@ +/* + ** + ** Copyright 2008, HTC Inc. + ** + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** + ** http://www.apache.org/licenses/LICENSE-2.0 + ** + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + */ + +#ifndef ANDROID_MEDIARECORDERCLIENT_H +#define ANDROID_MEDIARECORDERCLIENT_H + +#include + +namespace android { + +class PVMediaRecorder; +class ISurface; + +class MediaRecorderClient : public BnMediaRecorder +{ +public: + virtual status_t setCamera(const sp& camera); + virtual status_t setPreviewSurface(const sp& surface); + virtual status_t setVideoSource(int vs); + virtual status_t setAudioSource(int as); + virtual status_t setOutputFormat(int of); + virtual status_t setVideoEncoder(int ve); + virtual status_t setAudioEncoder(int ae); + virtual status_t setOutputFile(const char* path); + virtual status_t setVideoSize(int width, int height); + virtual status_t setVideoFrameRate(int frames_per_second); + virtual status_t prepare(); + virtual status_t getMaxAmplitude(int* max); + virtual status_t start(); + virtual status_t stop(); + virtual status_t reset(); + virtual status_t init(); + virtual status_t close(); + virtual status_t release(); + +private: + friend class MediaPlayerService; // for accessing private constructor + + MediaRecorderClient(pid_t pid); + virtual ~MediaRecorderClient(); + + pid_t mPid; + Mutex mLock; + PVMediaRecorder *mRecorder; +}; + +}; // namespace android + +#endif // ANDROID_MEDIARECORDERCLIENT_H + diff --git a/media/libmediaplayerservice/MetadataRetrieverClient.cpp b/media/libmediaplayerservice/MetadataRetrieverClient.cpp new file mode 100644 index 0000000..a320bd5 --- /dev/null +++ b/media/libmediaplayerservice/MetadataRetrieverClient.cpp @@ -0,0 +1,250 @@ +/* +** +** 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 "MetadataRetrieverClient" +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "MetadataRetrieverClient.h" + + +namespace android { + +MetadataRetrieverClient::MetadataRetrieverClient(pid_t pid) +{ + LOGV("MetadataRetrieverClient constructor pid(%d)", pid); + mPid = pid; + mThumbnailDealer = NULL; + mAlbumArtDealer = NULL; + mThumbnail = NULL; + mAlbumArt = NULL; + + mRetriever = new PVMetadataRetriever(); + if (mRetriever == NULL) { + LOGE("failed to initialize the retriever"); + } +} + +MetadataRetrieverClient::~MetadataRetrieverClient() +{ + LOGV("MetadataRetrieverClient destructor"); + disconnect(); +} + +status_t MetadataRetrieverClient::dump(int fd, const Vector& args) const +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + result.append(" MetadataRetrieverClient\n"); + snprintf(buffer, 255, " pid(%d)\n", mPid); + result.append(buffer); + write(fd, result.string(), result.size()); + write(fd, "\n", 1); + return NO_ERROR; +} + +void MetadataRetrieverClient::disconnect() +{ + LOGV("disconnect from pid %d", mPid); + Mutex::Autolock lock(mLock); + mRetriever.clear(); + mThumbnailDealer.clear(); + mAlbumArtDealer.clear(); + mThumbnail.clear(); + mAlbumArt.clear(); + IPCThreadState::self()->flushCommands(); +} + +status_t MetadataRetrieverClient::setDataSource(const char *url) +{ + LOGV("setDataSource(%s)", url); + Mutex::Autolock lock(mLock); + if (url == NULL) { + return UNKNOWN_ERROR; + } + if (mRetriever == NULL) { + LOGE("retriever is not initialized"); + return NO_INIT; + } + return mRetriever->setDataSource(url); +} + +status_t MetadataRetrieverClient::setDataSource(int fd, int64_t offset, int64_t length) +{ + LOGV("setDataSource fd=%d, offset=%lld, length=%lld", fd, offset, length); + Mutex::Autolock lock(mLock); + if (mRetriever == NULL) { + LOGE("retriever is not initialized"); + ::close(fd); + return NO_INIT; + } + + struct stat sb; + int ret = fstat(fd, &sb); + if (ret != 0) { + LOGE("fstat(%d) failed: %d, %s", fd, ret, strerror(errno)); + return UNKNOWN_ERROR; + } + LOGV("st_dev = %llu", sb.st_dev); + LOGV("st_mode = %u", sb.st_mode); + LOGV("st_uid = %lu", sb.st_uid); + LOGV("st_gid = %lu", sb.st_gid); + LOGV("st_size = %llu", sb.st_size); + + if (offset >= sb.st_size) { + LOGE("offset (%lld) bigger than file size (%llu)", offset, sb.st_size); + ::close(fd); + return UNKNOWN_ERROR; + } + if (offset + length > sb.st_size) { + length = sb.st_size - offset; + LOGE("calculated length = %lld", length); + } + status_t status = mRetriever->setDataSource(fd, offset, length); + ::close(fd); + return status; +} + +status_t MetadataRetrieverClient::setMode(int mode) +{ + LOGV("setMode"); + Mutex::Autolock lock(mLock); + if (mRetriever == NULL) { + LOGE("retriever is not initialized"); + return NO_INIT; + } + return mRetriever->setMode(mode); +} + +status_t MetadataRetrieverClient::getMode(int* mode) const +{ + LOGV("getMode"); + Mutex::Autolock lock(mLock); + if (mRetriever == NULL) { + LOGE("retriever is not initialized"); + return NO_INIT; + } + return mRetriever->getMode(mode); +} + +sp MetadataRetrieverClient::captureFrame() +{ + LOGV("captureFrame"); + Mutex::Autolock lock(mLock); + mThumbnail.clear(); + mThumbnailDealer.clear(); + if (mRetriever == NULL) { + LOGE("retriever is not initialized"); + return NULL; + } + VideoFrame *frame = mRetriever->captureFrame(); + if (frame == NULL) { + LOGE("failed to capture a video frame"); + return NULL; + } + size_t size = sizeof(VideoFrame) + frame->mSize; + mThumbnailDealer = new MemoryDealer(size); + if (mThumbnailDealer == NULL) { + LOGE("failed to create MemoryDealer"); + delete frame; + return NULL; + } + mThumbnail = mThumbnailDealer->allocate(size); + if (mThumbnail == NULL) { + LOGE("not enough memory for VideoFrame size=%u", size); + mThumbnailDealer.clear(); + delete frame; + return NULL; + } + VideoFrame *frameCopy = static_cast(mThumbnail->pointer()); + frameCopy->mWidth = frame->mWidth; + frameCopy->mHeight = frame->mHeight; + frameCopy->mDisplayWidth = frame->mDisplayWidth; + frameCopy->mDisplayHeight = frame->mDisplayHeight; + frameCopy->mSize = frame->mSize; + frameCopy->mData = (uint8_t *)frameCopy + sizeof(VideoFrame); + memcpy(frameCopy->mData, frame->mData, frame->mSize); + delete frame; // Fix memory leakage + return mThumbnail; +} + +sp MetadataRetrieverClient::extractAlbumArt() +{ + LOGV("extractAlbumArt"); + Mutex::Autolock lock(mLock); + mAlbumArt.clear(); + mAlbumArtDealer.clear(); + if (mRetriever == NULL) { + LOGE("retriever is not initialized"); + return NULL; + } + MediaAlbumArt *albumArt = mRetriever->extractAlbumArt(); + if (albumArt == NULL) { + LOGE("failed to extract an album art"); + return NULL; + } + size_t size = sizeof(MediaAlbumArt) + albumArt->mSize; + mAlbumArtDealer = new MemoryDealer(size); + if (mAlbumArtDealer == NULL) { + LOGE("failed to create MemoryDealer object"); + delete albumArt; + return NULL; + } + mAlbumArt = mAlbumArtDealer->allocate(size); + if (mAlbumArt == NULL) { + LOGE("not enough memory for MediaAlbumArt size=%u", size); + mAlbumArtDealer.clear(); + delete albumArt; + return NULL; + } + MediaAlbumArt *albumArtCopy = static_cast(mAlbumArt->pointer()); + albumArtCopy->mSize = albumArt->mSize; + albumArtCopy->mData = (uint8_t *)albumArtCopy + sizeof(MediaAlbumArt); + memcpy(albumArtCopy->mData, albumArt->mData, albumArt->mSize); + delete albumArt; // Fix memory leakage + return mAlbumArt; +} + +const char* MetadataRetrieverClient::extractMetadata(int keyCode) +{ + LOGV("extractMetadata"); + Mutex::Autolock lock(mLock); + if (mRetriever == NULL) { + LOGE("retriever is not initialized"); + return NULL; + } + return mRetriever->extractMetadata(keyCode); +} + +}; // namespace android diff --git a/media/libmediaplayerservice/MetadataRetrieverClient.h b/media/libmediaplayerservice/MetadataRetrieverClient.h new file mode 100644 index 0000000..ce29c98 --- /dev/null +++ b/media/libmediaplayerservice/MetadataRetrieverClient.h @@ -0,0 +1,71 @@ +/* +** +** 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. +*/ + +#ifndef ANDROID_MEDIAMETADATARETRIEVERSERVICE_H +#define ANDROID_MEDIAMETADATARETRIEVERSERVICE_H + +#include +#include +#include + +#include + + +namespace android { + +class IMediaPlayerService; +class MemoryDealer; + +class MetadataRetrieverClient : public BnMediaMetadataRetriever +{ +public: + MetadataRetrieverClient(const sp& service, pid_t pid, int32_t connId); + + // Implements IMediaMetadataRetriever interface + // These methods are called in IMediaMetadataRetriever.cpp? + virtual void disconnect(); + virtual status_t setDataSource(const char *url); + virtual status_t setDataSource(int fd, int64_t offset, int64_t length); + virtual status_t setMode(int mode); + virtual status_t getMode(int* mode) const; + virtual sp captureFrame(); + virtual sp extractAlbumArt(); + virtual const char* extractMetadata(int keyCode); + + virtual status_t dump(int fd, const Vector& args) const; + +private: + friend class MediaPlayerService; + + explicit MetadataRetrieverClient(pid_t pid); + virtual ~MetadataRetrieverClient(); + + mutable Mutex mLock; + sp mRetriever; + pid_t mPid; + + // Keep the shared memory copy of album art and capture frame (for thumbnail) + sp mAlbumArtDealer; + sp mThumbnailDealer; + sp mAlbumArt; + sp mThumbnail; +}; + +}; // namespace android + +#endif // ANDROID_MEDIAMETADATARETRIEVERSERVICE_H + diff --git a/media/libmediaplayerservice/MidiFile.cpp b/media/libmediaplayerservice/MidiFile.cpp index 538f7d4..cfad66c 100644 --- a/media/libmediaplayerservice/MidiFile.cpp +++ b/media/libmediaplayerservice/MidiFile.cpp @@ -431,7 +431,7 @@ status_t MidiFile::setLooping(int loop) } status_t MidiFile::createOutputTrack() { - if (mAudioSink->open(pLibConfig->sampleRate,pLibConfig->numChannels, 2) != NO_ERROR) { + if (mAudioSink->open(pLibConfig->sampleRate, pLibConfig->numChannels, AudioSystem::PCM_16_BIT, 2) != NO_ERROR) { LOGE("mAudioSink open failed"); return ERROR_OPEN_FAILED; } diff --git a/media/libmediaplayerservice/VorbisPlayer.cpp b/media/libmediaplayerservice/VorbisPlayer.cpp index a0e0f39..9a64403 100644 --- a/media/libmediaplayerservice/VorbisPlayer.cpp +++ b/media/libmediaplayerservice/VorbisPlayer.cpp @@ -385,7 +385,7 @@ status_t VorbisPlayer::createOutputTrack() { LOGV("Create AudioTrack object: rate=%ld, channels=%d\n", vi->rate, vi->channels); - if (mAudioSink->open(vi->rate, vi->channels, DEFAULT_AUDIOSINK_BUFFERCOUNT) != NO_ERROR) { + if (mAudioSink->open(vi->rate, vi->channels, AudioSystem::PCM_16_BIT, DEFAULT_AUDIOSINK_BUFFERCOUNT) != NO_ERROR) { LOGE("mAudioSink open failed"); return ERROR_OPEN_FAILED; } -- cgit v1.1