summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
Diffstat (limited to 'media')
-rw-r--r--media/libmedia/Android.mk3
-rw-r--r--media/libmedia/AudioRecord.cpp382
-rw-r--r--media/libmedia/AudioSystem.cpp56
-rw-r--r--media/libmedia/AudioTrack.cpp684
-rw-r--r--media/libmedia/IAudioFlinger.cpp74
-rw-r--r--media/libmedia/IMediaMetadataRetriever.cpp218
-rw-r--r--media/libmedia/IMediaPlayer.cpp20
-rw-r--r--media/libmedia/IMediaPlayerService.cpp49
-rw-r--r--media/libmedia/IMediaRecorder.cpp376
-rw-r--r--media/libmedia/ToneGenerator.cpp145
-rw-r--r--media/libmedia/mediametadataretriever.cpp228
-rw-r--r--media/libmedia/mediaplayer.cpp126
-rw-r--r--media/libmedia/mediarecorder.cpp518
-rw-r--r--media/libmediaplayerservice/Android.mk3
-rw-r--r--media/libmediaplayerservice/MediaPlayerService.cpp136
-rw-r--r--media/libmediaplayerservice/MediaPlayerService.h40
-rw-r--r--media/libmediaplayerservice/MediaRecorderClient.cpp251
-rw-r--r--media/libmediaplayerservice/MediaRecorderClient.h64
-rw-r--r--media/libmediaplayerservice/MetadataRetrieverClient.cpp250
-rw-r--r--media/libmediaplayerservice/MetadataRetrieverClient.h71
-rw-r--r--media/libmediaplayerservice/MidiFile.cpp2
-rw-r--r--media/libmediaplayerservice/VorbisPlayer.cpp2
22 files changed, 3138 insertions, 560 deletions
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 <stdint.h>
@@ -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<IAudioRecord> 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<IMemory> 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<audio_track_cblk_t*>(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<ClientRecordThread> 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<ClientRecordThread> 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<int8_t*>(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<int8_t*>(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<ClientRecordThread>& 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<IAudioFlinger> AudioSystem::gAudioFlinger;
sp<AudioSystem::DeathNotifier> 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<IAudioFlinger>& AudioSystem::get_audio_flinger()
@@ -47,11 +52,15 @@ const sp<IAudioFlinger>& AudioSystem::get_audio_flinger()
gDeathNotifier = new DeathNotifier();
} else {
if (gAudioErrorCallback) {
- gAudioErrorCallback(NO_ERROR);
+ gAudioErrorCallback(NO_ERROR);
}
}
binder->linkToDeath(gDeathNotifier);
gAudioFlinger = interface_cast<IAudioFlinger>(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<IAudioFlinger>& 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<IAudioFlinger>& 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<IAudioFlinger>& 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<IBinder>& who) {
+void AudioSystem::DeathNotifier::binderDied(const wp<IBinder>& 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 <stdint.h>
#include <sys/types.h>
+#include <limits.h>
#include <sched.h>
#include <sys/resource.h>
@@ -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<IAudioFlinger>& 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<IMemory>& 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<IMemory>& 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<IAudioTrack> 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<IMemory> 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<audio_track_cblk_t*>(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<IMemory>& 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<const int8_t*>(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<AudioTrackThread>& 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<String16>& 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<String16>& 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<IMemory>& 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<IAudioTrack>(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<IAudioRecord>(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<IAudioTrack> track = createTrack(pid,
+ sp<IMemory> buffer = interface_cast<IMemory>(data.readStrongBinder());
+ status_t status;
+ sp<IAudioTrack> 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<IAudioRecord> 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 <stdint.h>
+#include <sys/types.h>
+#include <utils/Parcel.h>
+#include <graphics/SkBitmap.h>
+#include <media/IMediaMetadataRetriever.h>
+
+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<IMediaMetadataRetriever>
+{
+public:
+ BpMediaMetadataRetriever(const sp<IBinder>& impl)
+ : BpInterface<IMediaMetadataRetriever>(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<IMemory> 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<IMemory>(reply.readStrongBinder());
+ }
+
+ sp<IMemory> 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<IMemory>(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<IMemory> 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<IMemory> 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 <utils/IMemory.h>
#include <media/IMediaPlayerService.h>
+#include <media/IMediaRecorder.h>
namespace android {
@@ -29,6 +30,8 @@ enum {
CREATE_FD,
DECODE_URL,
DECODE_FD,
+ CREATE_MEDIA_RECORDER,
+ CREATE_METADATA_RETRIEVER,
};
class BpMediaPlayerService: public BpInterface<IMediaPlayerService>
@@ -39,6 +42,15 @@ public:
{
}
+ virtual sp<IMediaMetadataRetriever> createMetadataRetriever(pid_t pid)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor());
+ data.writeInt32(pid);
+ remote()->transact(CREATE_METADATA_RETRIEVER, data, &reply);
+ return interface_cast<IMediaMetadataRetriever>(reply.readStrongBinder());
+ }
+
virtual sp<IMediaPlayer> create(pid_t pid, const sp<IMediaPlayerClient>& client, const char* url)
{
Parcel data, reply;
@@ -50,6 +62,15 @@ public:
return interface_cast<IMediaPlayer>(reply.readStrongBinder());
}
+ virtual sp<IMediaRecorder> createMediaRecorder(pid_t pid)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor());
+ data.writeInt32(pid);
+ remote()->transact(CREATE_MEDIA_RECORDER, data, &reply);
+ return interface_cast<IMediaRecorder>(reply.readStrongBinder());
+ }
+
virtual sp<IMediaPlayer> create(pid_t pid, const sp<IMediaPlayerClient>& client, int fd, int64_t offset, int64_t length)
{
Parcel data, reply;
@@ -63,7 +84,7 @@ public:
return interface_cast<IMediaPlayer>(reply.readStrongBinder());
}
- virtual sp<IMemory> decode(const char* url, uint32_t *pSampleRate, int* pNumChannels)
+ virtual sp<IMemory> 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<IMemory>(reply.readStrongBinder());
}
- virtual sp<IMemory> decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels)
+ virtual sp<IMemory> 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<IMemory>(reply.readStrongBinder());
}
};
@@ -127,9 +150,11 @@ status_t BnMediaPlayerService::onTransact(
const char* url = data.readCString();
uint32_t sampleRate;
int numChannels;
- sp<IMemory> player = decode(url, &sampleRate, &numChannels);
+ int format;
+ sp<IMemory> 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<IMemory> player = decode(fd, offset, length, &sampleRate, &numChannels);
+ int format;
+ sp<IMemory> 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<IMediaRecorder> 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<IMediaMetadataRetriever> 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 <utils/Log.h>
+#include <utils/Parcel.h>
+#include <ui/ISurface.h>
+#include <ui/ICamera.h>
+#include <media/IMediaRecorder.h>
+
+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<IMediaRecorder>
+{
+public:
+ BpMediaRecorder(const sp<IBinder>& impl)
+ : BpInterface<IMediaRecorder>(impl)
+ {
+ }
+
+ status_t setCamera(const sp<ICamera>& 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<ISurface>& 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<ISurface> surface = interface_cast<ISurface>(data.readStrongBinder());
+ reply->writeInt32(setPreviewSurface(surface));
+ return NO_ERROR;
+ } break;
+ case SET_CAMERA: {
+ LOGV("SET_CAMERA");
+ CHECK_INTERFACE(IMediaRecorder, data, reply);
+ sp<ICamera> camera = interface_cast<ICamera>(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<IAudioFlinger>& lpAudioFlinger = AudioSystem::get_audio_flinger();
LOGV("ToneGenerator constructor: streamType=%d, volume=%f\n", streamType, volume);
mState = TONE_IDLE;
- mpAudioTrack = 0;
- mpToneDesc = 0;
- mpNewToneDesc = 0;
- if (lpAudioFlinger == 0) {
+ 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<const AudioTrack::Buffer *>(info);
ToneGenerator *lpToneGen = static_cast<ToneGenerator *>(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 <media/mediametadataretriever.h>
-
-#ifdef LOG_TAG
-#undef LOG_TAG
+//#define LOG_NDEBUG 0
#define LOG_TAG "MediaMetadataRetriever"
-#endif
+#include <utils/IServiceManager.h>
+#include <utils/IPCThreadState.h>
+#include <media/mediametadataretriever.h>
+#include <media/IMediaPlayerService.h>
#include <utils/Log.h>
#include <dlfcn.h>
namespace android {
-// Factory class function in shared libpvplayer.so
-typedef MediaMetadataRetrieverImpl* (*createRetriever_f)();
+// client singleton for binder interface to service
+Mutex MediaMetadataRetriever::sServiceLock;
+sp<IMediaPlayerService> MediaMetadataRetriever::sService;
+sp<MediaMetadataRetriever::DeathNotifier> MediaMetadataRetriever::sDeathNotifier;
-MediaMetadataRetrieverImpl *MediaMetadataRetriever::mRetriever = NULL;
-void *MediaMetadataRetriever::mLibHandler = NULL;
-
-void MediaMetadataRetriever::create()
+const sp<IMediaPlayerService>& 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<IServiceManager> sm = defaultServiceManager();
+ sp<IBinder> binder;
+ do {
+ binder = sm->getService(String16("media.player"));
+ if (binder != 0) {
+ break;
+ }
+ LOGW("MediaPlayerService not published, waiting...");
+ usleep(500000); // 0.5 s
+ } while(true);
+ if (sDeathNotifier == NULL) {
+ sDeathNotifier = new DeathNotifier();
}
+ binder->linkToDeath(sDeathNotifier);
+ sService = interface_cast<IMediaPlayerService>(binder);
}
-
- // Each time create a new MediaMetadataRetrieverImpl object.
- if (mRetriever) {
- delete mRetriever;
- }
- createRetriever_f createRetriever = reinterpret_cast<createRetriever_f>(dlsym(mLibHandler, "createRetriever"));
- if (!createRetriever) {
- LOGE("setDataSource: dlsym failed on createRetriever in libpvplayer.so");
+ LOGE_IF(sService == 0, "no MediaPlayerService!?");
+ return sService;
+}
+
+MediaMetadataRetriever::MediaMetadataRetriever()
+{
+ LOGV("constructor");
+ const sp<IMediaPlayerService>& service(getService());
+ if (service == 0) {
+ LOGE("failed to obtain MediaMetadataRetrieverService");
return;
}
- mRetriever = createRetriever();
- if (!mRetriever) {
- LOGE("setDataSource: createRetriever failed in libpvplayer.so");
+ sp<IMediaMetadataRetriever> 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<IMediaMetadataRetriever> 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<IMemory> 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<IMemory> 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<IBinder>& 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 <utils/IPCThreadState.h>
#include <media/mediaplayer.h>
-#include <libsonivox/eas.h>
+#include <media/AudioTrack.h>
#include <utils/MemoryBase.h>
namespace android {
// client singleton for binder interface to service
-Mutex MediaPlayer::mServiceLock;
-sp<IMediaPlayerService> MediaPlayer::mMediaPlayerService;
-sp<MediaPlayer::DeathNotifier> MediaPlayer::mDeathNotifier;
+Mutex MediaPlayer::sServiceLock;
+sp<IMediaPlayerService> MediaPlayer::sMediaPlayerService;
+sp<MediaPlayer::DeathNotifier> MediaPlayer::sDeathNotifier;
+SortedVector< wp<MediaPlayer> > MediaPlayer::sObitRecipients;
// establish binder interface to service
const sp<IMediaPlayerService>& MediaPlayer::getMediaPlayerService()
{
- Mutex::Autolock _l(mServiceLock);
- if (mMediaPlayerService.get() == 0) {
+ Mutex::Autolock _l(sServiceLock);
+ if (sMediaPlayerService.get() == 0) {
sp<IServiceManager> sm = defaultServiceManager();
sp<IBinder> binder;
do {
@@ -53,14 +54,26 @@ const sp<IMediaPlayerService>& 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<IMediaPlayerService>(binder);
+ binder->linkToDeath(sDeathNotifier);
+ sMediaPlayerService = interface_cast<IMediaPlayerService>(binder);
}
- LOGE_IF(mMediaPlayerService==0, "no MediaPlayerService!?");
- return mMediaPlayerService;
+ LOGE_IF(sMediaPlayerService==0, "no MediaPlayerService!?");
+ return sMediaPlayerService;
+}
+
+void MediaPlayer::addObitRecipient(const wp<MediaPlayer>& recipient)
+{
+ Mutex::Autolock _l(sServiceLock);
+ sObitRecipients.add(recipient);
+}
+
+void MediaPlayer::removeObitRecipient(const wp<MediaPlayer>& 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<MediaPlayerListener>& listener)
@@ -136,7 +156,6 @@ status_t MediaPlayer::setDataSource(const sp<IMediaPlayer>& 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<IMediaPlayer>& 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<IBinder>& who) {
- LOGW("IMediaplayer died");
- notify(MEDIA_ERROR, MEDIA_ERROR_SERVER_DIED, 0);
-}
-
void MediaPlayer::DeathNotifier::binderDied(const wp<IBinder>& who) {
- Mutex::Autolock _l(MediaPlayer::mServiceLock);
- MediaPlayer::mMediaPlayerService.clear();
LOGW("MediaPlayer server died!");
+
+ // Need to do this with the lock held
+ SortedVector< wp<MediaPlayer> > 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<MediaPlayer> 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<IMemory> MediaPlayer::decode(const char* url, uint32_t *pSampleRate, int* pNumChannels)
+/*static*/ sp<IMemory> MediaPlayer::decode(const char* url, uint32_t *pSampleRate, int* pNumChannels, int* pFormat)
{
LOGV("decode(%s)", url);
sp<IMemory> p;
const sp<IMediaPlayerService>& 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<IMemory> MediaPlayer::decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels)
+/*static*/ sp<IMemory> 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<IMemory> p;
const sp<IMediaPlayerService>& 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 <utils/Log.h>
+#include <ui/Surface.h>
+#include <media/mediarecorder.h>
+#include <utils/IServiceManager.h>
+#include <media/IMediaPlayerService.h>
+#include <media/IMediaRecorder.h>
+
+namespace android {
+
+status_t MediaRecorder::setCamera(const sp<ICamera>& 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>& 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<IServiceManager> sm = defaultServiceManager();
+ sp<IBinder> 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<IMediaPlayerService> service = interface_cast<IMediaPlayerService>(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 <utils/IServiceManager.h>
#include <utils/MemoryHeapBase.h>
#include <utils/MemoryBase.h>
+#include <cutils/properties.h>
#include <media/MediaPlayerInterface.h>
+#include <media/mediarecorder.h>
+#include <media/MediaMetadataRetrieverInterface.h>
#include <media/AudioTrack.h>
+#include "MediaRecorderClient.h"
#include "MediaPlayerService.h"
+#include "MetadataRetrieverClient.h"
+
#include "MidiFile.h"
#include "VorbisPlayer.h"
#include <media/PVPlayer.h>
@@ -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<IMediaRecorder> MediaPlayerService::createMediaRecorder(pid_t pid)
+{
+ sp<MediaRecorderClient> recorder = new MediaRecorderClient(pid);
+ LOGV("Create new media recorder client from pid %d", pid);
+ return recorder;
+}
+
+sp<IMediaMetadataRetriever> MediaPlayerService::createMetadataRetriever(pid_t pid)
+{
+ sp<MetadataRetrieverClient> retriever = new MetadataRetrieverClient(pid);
+ LOGV("Create new media retriever from pid %d", pid);
+ return retriever;
+}
+
sp<IMediaPlayer> MediaPlayerService::create(pid_t pid, const sp<IMediaPlayerClient>& client, const char* url)
{
int32_t connId = android_atomic_inc(&mNextConnId);
@@ -237,8 +259,8 @@ status_t MediaPlayerService::AudioCache::dump(int fd, const Vector<String16>& 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<String16>& 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<String16>& 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<String16>& 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<String16>& 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<MediaPlayerBase> 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<IMemory> MediaPlayerService::decode(const char* url, uint32_t *pSampleRate, int* pNumChannels)
+sp<IMemory> MediaPlayerService::decode(const char* url, uint32_t *pSampleRate, int* pNumChannels, int* pFormat)
{
LOGV("decode(%s)", url);
sp<MemoryBase> mem;
@@ -856,14 +864,15 @@ sp<IMemory> 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<IMemory> MediaPlayerService::decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels)
+sp<IMemory> 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<MemoryBase> mem;
@@ -898,7 +907,8 @@ sp<IMemory> 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 <media/IMediaPlayerService.h>
#include <media/MediaPlayerInterface.h>
-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<String16>& 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<MemoryHeapBase> 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<IMediaRecorder> createMediaRecorder(pid_t pid);
+ virtual sp<IMediaMetadataRetriever> createMetadataRetriever(pid_t pid);
+
+ // House keeping for media player clients
virtual sp<IMediaPlayer> create(pid_t pid, const sp<IMediaPlayerClient>& client, const char* url);
virtual sp<IMediaPlayer> create(pid_t pid, const sp<IMediaPlayerClient>& client, int fd, int64_t offset, int64_t length);
- virtual sp<IMemory> decode(const char* url, uint32_t *pSampleRate, int* pNumChannels);
- virtual sp<IMemory> decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels);
+ virtual sp<IMemory> decode(const char* url, uint32_t *pSampleRate, int* pNumChannels, int* pFormat);
+ virtual sp<IMemory> decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels, int* pFormat);
virtual status_t dump(int fd, const Vector<String16>& 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 <utils/Log.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <string.h>
+#include <cutils/atomic.h>
+#include <android_runtime/ActivityManager.h>
+#include <utils/IPCThreadState.h>
+#include <utils/IServiceManager.h>
+#include <utils/MemoryHeapBase.h>
+#include <utils/MemoryBase.h>
+#include <media/PVMediaRecorder.h>
+
+#include "MediaRecorderClient.h"
+
+namespace android {
+
+status_t MediaRecorderClient::setCamera(const sp<ICamera>& 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<ISurface>& 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 <media/IMediaRecorder.h>
+
+namespace android {
+
+class PVMediaRecorder;
+class ISurface;
+
+class MediaRecorderClient : public BnMediaRecorder
+{
+public:
+ virtual status_t setCamera(const sp<ICamera>& camera);
+ virtual status_t setPreviewSurface(const sp<ISurface>& 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 <utils/Log.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <unistd.h>
+
+#include <string.h>
+#include <cutils/atomic.h>
+#include <utils/MemoryDealer.h>
+#include <android_runtime/ActivityManager.h>
+#include <utils/IPCThreadState.h>
+#include <utils/IServiceManager.h>
+#include <media/MediaMetadataRetrieverInterface.h>
+#include <media/MediaPlayerInterface.h>
+#include <media/PVMetadataRetriever.h>
+#include <private/media/VideoFrame.h>
+
+#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<String16>& 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<IMemory> 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<VideoFrame *>(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<IMemory> 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<MediaAlbumArt *>(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 <utils.h>
+#include <utils/KeyedVector.h>
+#include <utils/IMemory.h>
+
+#include <media/MediaMetadataRetrieverInterface.h>
+
+
+namespace android {
+
+class IMediaPlayerService;
+class MemoryDealer;
+
+class MetadataRetrieverClient : public BnMediaMetadataRetriever
+{
+public:
+ MetadataRetrieverClient(const sp<IMediaPlayerService>& 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<IMemory> captureFrame();
+ virtual sp<IMemory> extractAlbumArt();
+ virtual const char* extractMetadata(int keyCode);
+
+ virtual status_t dump(int fd, const Vector<String16>& args) const;
+
+private:
+ friend class MediaPlayerService;
+
+ explicit MetadataRetrieverClient(pid_t pid);
+ virtual ~MetadataRetrieverClient();
+
+ mutable Mutex mLock;
+ sp<MediaMetadataRetrieverBase> mRetriever;
+ pid_t mPid;
+
+ // Keep the shared memory copy of album art and capture frame (for thumbnail)
+ sp<MemoryDealer> mAlbumArtDealer;
+ sp<MemoryDealer> mThumbnailDealer;
+ sp<IMemory> mAlbumArt;
+ sp<IMemory> 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;
}