summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/media/AudioSystem.h23
-rw-r--r--include/media/AudioTrack.h1
-rw-r--r--include/media/IAudioFlinger.h10
-rw-r--r--include/media/IAudioFlingerClient.h55
-rw-r--r--include/media/JetPlayer.h16
-rw-r--r--include/media/MediaPlayerInterface.h32
-rw-r--r--include/media/PVPlayer.h14
-rw-r--r--include/media/ToneGenerator.h3
-rw-r--r--include/private/opengles/gl_context.h1
-rw-r--r--media/libmedia/Android.mk1
-rw-r--r--media/libmedia/AudioRecord.cpp18
-rw-r--r--media/libmedia/AudioSystem.cpp55
-rw-r--r--media/libmedia/AudioTrack.cpp14
-rw-r--r--media/libmedia/IAudioFlinger.cpp50
-rw-r--r--media/libmedia/IAudioFlingerClient.cpp81
-rw-r--r--media/libmedia/JetPlayer.cpp70
-rw-r--r--media/libmedia/ToneGenerator.cpp8
-rw-r--r--media/libmedia/mediaplayer.cpp10
-rw-r--r--media/libmediaplayerservice/MediaPlayerService.cpp147
-rw-r--r--media/libmediaplayerservice/MidiFile.cpp20
-rw-r--r--media/libmediaplayerservice/MidiFile.h2
-rw-r--r--media/libmediaplayerservice/VorbisPlayer.cpp4
22 files changed, 409 insertions, 226 deletions
diff --git a/include/media/AudioSystem.h b/include/media/AudioSystem.h
index 77676bf..6bd54ba 100644
--- a/include/media/AudioSystem.h
+++ b/include/media/AudioSystem.h
@@ -99,23 +99,31 @@ public:
static status_t getOutputSamplingRate(int* samplingRate);
static status_t getOutputFrameCount(int* frameCount);
static status_t getOutputLatency(uint32_t* latency);
+
+ static status_t getInputBufferSize(uint32_t sampleRate, int format, int channelCount,
+ size_t* buffSize);
// ----------------------------------------------------------------------------
private:
- class DeathNotifier: public IBinder::DeathRecipient
+ class AudioFlingerClient: public IBinder::DeathRecipient, public BnAudioFlingerClient
{
public:
- DeathNotifier() {
+ AudioFlingerClient() {
}
+ // DeathRecipient
virtual void binderDied(const wp<IBinder>& who);
+
+ // IAudioFlingerClient
+ virtual void audioOutputChanged(uint32_t frameCount, uint32_t samplingRate, uint32_t latency);
+
};
- static sp<DeathNotifier> gDeathNotifier;
+ static sp<AudioFlingerClient> gAudioFlingerClient;
- friend class DeathNotifier;
+ friend class AudioFlingerClient;
static Mutex gLock;
static sp<IAudioFlinger> gAudioFlinger;
@@ -123,6 +131,13 @@ private:
static int gOutSamplingRate;
static int gOutFrameCount;
static uint32_t gOutLatency;
+
+ static size_t gInBuffSize;
+ // previous parameters for recording buffer size queries
+ static uint32_t gPrevInSamplingRate;
+ static int gPrevInFormat;
+ static int gPrevInChannelCount;
+
};
}; // namespace android
diff --git a/include/media/AudioTrack.h b/include/media/AudioTrack.h
index fd62daa..5b2bab9 100644
--- a/include/media/AudioTrack.h
+++ b/include/media/AudioTrack.h
@@ -51,6 +51,7 @@ public:
MUSIC = 3,
ALARM = 4,
NOTIFICATION = 5,
+ BLUETOOTH_SCO = 6,
NUM_STREAM_TYPES
};
diff --git a/include/media/IAudioFlinger.h b/include/media/IAudioFlinger.h
index 69703b2..df601d7 100644
--- a/include/media/IAudioFlinger.h
+++ b/include/media/IAudioFlinger.h
@@ -26,6 +26,7 @@
#include <utils/IInterface.h>
#include <media/IAudioTrack.h>
#include <media/IAudioRecord.h>
+#include <media/IAudioFlingerClient.h>
namespace android {
@@ -107,6 +108,15 @@ public:
// Temporary interface, do not use
// TODO: Replace with a more generic key:value get/set mechanism
virtual status_t setParameter(const char* key, const char* value) = 0;
+
+ // register a current process for audio output change notifications
+ virtual void registerClient(const sp<IAudioFlingerClient>& client) = 0;
+
+ // retrieve the audio recording buffer size
+ virtual size_t getInputBufferSize(uint32_t sampleRate, int format, int channelCount) = 0;
+
+ // force AudioFlinger thread out of standby
+ virtual void wakeUp() = 0;
};
diff --git a/include/media/IAudioFlingerClient.h b/include/media/IAudioFlingerClient.h
new file mode 100644
index 0000000..10c3e0f
--- /dev/null
+++ b/include/media/IAudioFlingerClient.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_IAUDIOFLINGERCLIENT_H
+#define ANDROID_IAUDIOFLINGERCLIENT_H
+
+
+#include <utils/RefBase.h>
+#include <utils/IInterface.h>
+
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+class IAudioFlingerClient : public IInterface
+{
+public:
+ DECLARE_META_INTERFACE(AudioFlingerClient);
+
+ // Notifies a change of audio output from/to hardware to/from A2DP.
+ virtual void audioOutputChanged(uint32_t frameCount, uint32_t samplingRate, uint32_t latency) = 0;
+
+};
+
+
+// ----------------------------------------------------------------------------
+
+class BnAudioFlingerClient : public BnInterface<IAudioFlingerClient>
+{
+public:
+ virtual status_t onTransact( uint32_t code,
+ const Parcel& data,
+ Parcel* reply,
+ uint32_t flags = 0);
+};
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_IAUDIOFLINGERCLIENT_H
diff --git a/include/media/JetPlayer.h b/include/media/JetPlayer.h
index 4268170..16764a9 100644
--- a/include/media/JetPlayer.h
+++ b/include/media/JetPlayer.h
@@ -33,9 +33,12 @@ class JetPlayer {
public:
- static const int JET_USERID_UPDATE = 1;
- static const int JET_NUMQUEUEDSEGMENT_UPDATE = 2;
- static const int JET_PAUSE_UPDATE = 3;
+ // to keep in sync with the JetPlayer class constants
+ // defined in frameworks/base/media/java/android/media/JetPlayer.java
+ static const int JET_EVENT = 1;
+ static const int JET_USERID_UPDATE = 2;
+ static const int JET_NUMQUEUEDSEGMENT_UPDATE = 3;
+ static const int JET_PAUSE_UPDATE = 4;
JetPlayer(jobject javaJetPlayer,
int maxTracks = 32,
@@ -44,7 +47,8 @@ public:
int init();
int release();
- int openFile(const char* url);
+ int loadFromFile(const char* url);
+ int loadFromFD(const int fd, const long long offset, const long long length);
int closeFile();
int play();
int pause();
@@ -53,6 +57,7 @@ public:
int setMuteFlags(EAS_U32 muteFlags, bool sync);
int setMuteFlag(int trackNum, bool muteFlag, bool sync);
int triggerClip(int clipId);
+ int clearQueue();
void setEventCallback(jetevent_callback callback);
@@ -62,7 +67,8 @@ public:
private:
static int renderThread(void*);
int render();
- void fireEventOnStatusChange();
+ void fireUpdateOnStatusChange();
+ void fireEventsFromJetQueue();
JetPlayer() {} // no default constructor
void dump();
diff --git a/include/media/MediaPlayerInterface.h b/include/media/MediaPlayerInterface.h
index 30e4578..7f0e7b3 100644
--- a/include/media/MediaPlayerInterface.h
+++ b/include/media/MediaPlayerInterface.h
@@ -17,9 +17,6 @@
#ifndef ANDROID_MEDIAPLAYERINTERFACE_H
#define ANDROID_MEDIAPLAYERINTERFACE_H
-#include <pthread.h>
-#include <signal.h>
-
#ifdef __cplusplus
#include <ui/ISurface.h>
@@ -74,7 +71,6 @@ public:
virtual ~MediaPlayerBase() {}
virtual status_t initCheck() = 0;
virtual bool hardwareOutput() = 0;
- virtual status_t setSigBusHandlerStructTLSKey(pthread_key_t key) { return 0; }
virtual status_t setDataSource(const char *url) = 0;
virtual status_t setDataSource(int fd, int64_t offset, int64_t length) = 0;
virtual status_t setVideoSurface(const sp<ISurface>& surface) = 0;
@@ -125,34 +121,6 @@ public:
#endif // __cplusplus
-// A thread can set the thread local variable identified by the pthread_key_t
-// that was passed to the player using the setSigBusHandlerStructTLSKey()
-// method to the address of the following structure.
-// If 'handlesigbus' is non-NULL, the function it points to will be called,
-// and if it returns 0, the signal will be assumed to have been handled,
-// and no other action will be taken. If it returns non-zero, the old SIGBUS
-// handler will be called.
-// If 'handlesigbus is NULL, then sigbusvar must be non NULL. The system's
-// SIGBUS handler will map an accessible page filled with zeroes at the
-// location that caused the original fault, set the variable pointed to by
-// sigbusvar to a non-zero value, and exit (which causes the operation to
-// be retried, which should now succeed).
-// If base and len are non zero, which is strongly recommended, they will
-// be used as additional constraints on the signal handler. That is, when
-// specified, the fault address must be in the range specified by base and
-// len in order for handlesigbus() to be called or sigbusvar to be set.
-// If the fault address is outside of the range, the old SIGBUS handler
-// will be called.
-struct mediasigbushandler {
- int (*handlesigbus)(siginfo_t *, struct mediasigbushandler *);
- int *sigbusvar;
- char *base;
- int len;
- // these next two are free for application use
- struct mediasigbushandler *next;
- void *data;
-};
-
#endif // ANDROID_MEDIAPLAYERINTERFACE_H
diff --git a/include/media/PVPlayer.h b/include/media/PVPlayer.h
index 5f302ed..6d98852 100644
--- a/include/media/PVPlayer.h
+++ b/include/media/PVPlayer.h
@@ -20,6 +20,12 @@
#include <utils/Errors.h>
#include <media/MediaPlayerInterface.h>
+#define MAX_OPENCORE_INSTANCES 25
+
+#ifdef MAX_OPENCORE_INSTANCES
+#include <cutils/atomic.h>
+#endif
+
class PlayerDriver;
namespace android {
@@ -31,7 +37,6 @@ public:
virtual ~PVPlayer();
virtual status_t initCheck();
- virtual status_t setSigBusHandlerStructTLSKey(pthread_key_t key);
virtual status_t setDataSource(const char *url);
virtual status_t setDataSource(int fd, int64_t offset, int64_t length);
virtual status_t setVideoSurface(const sp<ISurface>& surface);
@@ -62,10 +67,13 @@ private:
char * mDataSourcePath;
bool mIsDataSourceSet;
sp<ISurface> mSurface;
- void * mMemBase;
- off_t mMemSize;
+ int mSharedFd;
status_t mInit;
int mDuration;
+
+#ifdef MAX_OPENCORE_INSTANCES
+ static volatile int32_t sNumInstances;
+#endif
};
}; // namespace android
diff --git a/include/media/ToneGenerator.h b/include/media/ToneGenerator.h
index 0cfdeec..ec64e4d 100644
--- a/include/media/ToneGenerator.h
+++ b/include/media/ToneGenerator.h
@@ -85,8 +85,6 @@ private:
TONE_RESTARTING //
};
- static const unsigned int NUM_PCM_BUFFERS = 2; // Number of AudioTrack pcm buffers
-
static const unsigned int TONEGEN_MAX_WAVES = 3;
static const unsigned int TONEGEN_MAX_SEGMENTS = 4; // Maximun number of elenemts in
static const unsigned int TONEGEN_INF = 0xFFFFFFFF; // Represents infinite time duration
@@ -127,7 +125,6 @@ private:
const ToneDescriptor *mpNewToneDesc; // pointer to next active tone descriptor
int mSamplingRate; // AudioFlinger Sampling rate
- int mBufferSize; // PCM buffer size in frames
AudioTrack *mpAudioTrack; // Pointer to audio track used for playback
Mutex mLock; // Mutex to control concurent access to ToneGenerator object from audio callback and application API
Mutex mCbkCondLock; // Mutex associated to mWaitCbkCond
diff --git a/include/private/opengles/gl_context.h b/include/private/opengles/gl_context.h
index 3056139..0c7ad46 100644
--- a/include/private/opengles/gl_context.h
+++ b/include/private/opengles/gl_context.h
@@ -28,6 +28,7 @@
#include <private/pixelflinger/ggl_context.h>
#include <GLES/gl.h>
+#include <GLES/glext.h>
namespace android {
diff --git a/media/libmedia/Android.mk b/media/libmedia/Android.mk
index 2a697b9..8020da2 100644
--- a/media/libmedia/Android.mk
+++ b/media/libmedia/Android.mk
@@ -4,6 +4,7 @@ include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
AudioTrack.cpp \
IAudioFlinger.cpp \
+ IAudioFlingerClient.cpp \
IAudioTrack.cpp \
IAudioRecord.cpp \
AudioRecord.cpp \
diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp
index 3d39181..a987b92 100644
--- a/media/libmedia/AudioRecord.cpp
+++ b/media/libmedia/AudioRecord.cpp
@@ -128,8 +128,22 @@ status_t AudioRecord::set(
return BAD_VALUE;
}
- // TODO: Get input frame count from hardware.
- int minFrameCount = 1024*2;
+ size_t inputBuffSizeInBytes = -1;
+ if (AudioSystem::getInputBufferSize(sampleRate, format, channelCount, &inputBuffSizeInBytes)
+ != NO_ERROR) {
+ LOGE("AudioSystem could not query the input buffer size.");
+ return NO_INIT;
+ }
+ if (inputBuffSizeInBytes == 0) {
+ LOGE("Recording parameters are not supported: sampleRate %d, channelCount %d, format %d",
+ sampleRate, channelCount, format);
+ return BAD_VALUE;
+ }
+ int frameSizeInBytes = channelCount * (format == AudioSystem::PCM_16_BIT ? 2 : 1);
+
+ // We use 2* size of input buffer for ping pong use of record buffer.
+ int minFrameCount = 2 * inputBuffSizeInBytes / frameSizeInBytes;
+ LOGV("AudioRecord::set() minFrameCount = %d", minFrameCount);
if (frameCount == 0) {
frameCount = minFrameCount;
diff --git a/media/libmedia/AudioSystem.cpp b/media/libmedia/AudioSystem.cpp
index a375b55..cf91105 100644
--- a/media/libmedia/AudioSystem.cpp
+++ b/media/libmedia/AudioSystem.cpp
@@ -15,6 +15,8 @@
*/
#define LOG_TAG "AudioSystem"
+//#define LOG_NDEBUG 0
+
#include <utils/Log.h>
#include <utils/IServiceManager.h>
#include <media/AudioSystem.h>
@@ -26,12 +28,17 @@ namespace android {
// client singleton for AudioFlinger binder interface
Mutex AudioSystem::gLock;
sp<IAudioFlinger> AudioSystem::gAudioFlinger;
-sp<AudioSystem::DeathNotifier> AudioSystem::gDeathNotifier;
+sp<AudioSystem::AudioFlingerClient> AudioSystem::gAudioFlingerClient;
audio_error_callback AudioSystem::gAudioErrorCallback = NULL;
// Cached values
int AudioSystem::gOutSamplingRate = 0;
int AudioSystem::gOutFrameCount = 0;
uint32_t AudioSystem::gOutLatency = 0;
+// Cached values for recording queries
+uint32_t AudioSystem::gPrevInSamplingRate = 16000;
+int AudioSystem::gPrevInFormat = AudioSystem::PCM_16_BIT;
+int AudioSystem::gPrevInChannelCount = 1;
+size_t AudioSystem::gInBuffSize = 0;
// establish binder interface to AudioFlinger service
@@ -48,15 +55,16 @@ const sp<IAudioFlinger>& AudioSystem::get_audio_flinger()
LOGW("AudioFlinger not published, waiting...");
usleep(500000); // 0.5 s
} while(true);
- if (gDeathNotifier == NULL) {
- gDeathNotifier = new DeathNotifier();
+ if (gAudioFlingerClient == NULL) {
+ gAudioFlingerClient = new AudioFlingerClient();
} else {
if (gAudioErrorCallback) {
gAudioErrorCallback(NO_ERROR);
}
}
- binder->linkToDeath(gDeathNotifier);
+ binder->linkToDeath(gAudioFlingerClient);
gAudioFlinger = interface_cast<IAudioFlinger>(binder);
+ gAudioFlinger->registerClient(gAudioFlingerClient);
// Cache frequently accessed parameters
gOutFrameCount = (int)gAudioFlinger->frameCount();
gOutSamplingRate = (int)gAudioFlinger->sampleRate();
@@ -250,7 +258,7 @@ status_t AudioSystem::getOutputSamplingRate(int* samplingRate)
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;
@@ -261,7 +269,7 @@ 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()
+ // gOutFrameCount is updated by get_audio_flinger()
}
*frameCount = gOutFrameCount;
return NO_ERROR;
@@ -279,14 +287,38 @@ status_t AudioSystem::getOutputLatency(uint32_t* latency)
return NO_ERROR;
}
+status_t AudioSystem::getInputBufferSize(uint32_t sampleRate, int format, int channelCount,
+ size_t* buffSize)
+{
+ // Do we have a stale gInBufferSize or are we requesting the input buffer size for new values
+ if ((gInBuffSize == 0) || (sampleRate != gPrevInSamplingRate) || (format != gPrevInFormat)
+ || (channelCount != gPrevInChannelCount)) {
+ // save the request params
+ gPrevInSamplingRate = sampleRate;
+ gPrevInFormat = format;
+ gPrevInChannelCount = channelCount;
+
+ gInBuffSize = 0;
+ const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
+ if (af == 0) {
+ return PERMISSION_DENIED;
+ }
+ gInBuffSize = af->getInputBufferSize(sampleRate, format, channelCount);
+ }
+ *buffSize = gInBuffSize;
+
+ return NO_ERROR;
+}
+
// ---------------------------------------------------------------------------
-void AudioSystem::DeathNotifier::binderDied(const wp<IBinder>& who) {
+void AudioSystem::AudioFlingerClient::binderDied(const wp<IBinder>& who) {
Mutex::Autolock _l(AudioSystem::gLock);
AudioSystem::gAudioFlinger.clear();
AudioSystem::gOutSamplingRate = 0;
AudioSystem::gOutFrameCount = 0;
AudioSystem::gOutLatency = 0;
+ AudioSystem::gInBuffSize = 0;
if (gAudioErrorCallback) {
gAudioErrorCallback(DEAD_OBJECT);
@@ -294,6 +326,15 @@ void AudioSystem::DeathNotifier::binderDied(const wp<IBinder>& who) {
LOGW("AudioFlinger server died!");
}
+void AudioSystem::AudioFlingerClient::audioOutputChanged(uint32_t frameCount, uint32_t samplingRate, uint32_t latency) {
+
+ AudioSystem::gOutFrameCount = frameCount;
+ AudioSystem::gOutSamplingRate = samplingRate;
+ AudioSystem::gOutLatency = latency;
+
+ LOGV("AudioFlinger output changed!");
+}
+
void AudioSystem::setErrorCallback(audio_error_callback cb) {
Mutex::Autolock _l(AudioSystem::gLock);
gAudioErrorCallback = cb;
diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp
index f9f8568..63b2012 100644
--- a/media/libmedia/AudioTrack.cpp
+++ b/media/libmedia/AudioTrack.cpp
@@ -603,13 +603,17 @@ status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, int32_t waitCount)
if (__builtin_expect(result!=NO_ERROR, false)) {
cblk->waitTimeMs += WAIT_PERIOD_MS;
if (cblk->waitTimeMs >= cblk->bufferTimeoutMs) {
- LOGW( "obtainBuffer timed out (is the CPU pegged?) "
- "user=%08x, server=%08x", cblk->user, cblk->server);
- mAudioTrack->start(); // FIXME: Wake up audioflinger
- timeout = 1;
+ // timing out when a loop has been set and we have already written upto loop end
+ // is a normal condition: no need to wake AudioFlinger up.
+ if (cblk->user < cblk->loopEnd) {
+ LOGW( "obtainBuffer timed out (is the CPU pegged?) "
+ "user=%08x, server=%08x", cblk->user, cblk->server);
+ mAudioFlinger->wakeUp();
+ timeout = 1;
+ }
cblk->waitTimeMs = 0;
}
- ;
+
if (--waitCount == 0) {
return TIMED_OUT;
}
diff --git a/media/libmedia/IAudioFlinger.cpp b/media/libmedia/IAudioFlinger.cpp
index 018ea6c..4215820 100644
--- a/media/libmedia/IAudioFlinger.cpp
+++ b/media/libmedia/IAudioFlinger.cpp
@@ -51,6 +51,9 @@ enum {
GET_MIC_MUTE,
IS_MUSIC_ACTIVE,
SET_PARAMETER,
+ REGISTER_CLIENT,
+ GET_INPUTBUFFERSIZE,
+ WAKE_UP
};
class BpAudioFlinger : public BpInterface<IAudioFlinger>
@@ -303,6 +306,33 @@ public:
remote()->transact(SET_PARAMETER, data, &reply);
return reply.readInt32();
}
+
+ virtual void registerClient(const sp<IAudioFlingerClient>& client)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
+ data.writeStrongBinder(client->asBinder());
+ remote()->transact(REGISTER_CLIENT, data, &reply);
+ }
+
+ virtual size_t getInputBufferSize(uint32_t sampleRate, int format, int channelCount)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
+ data.writeInt32(sampleRate);
+ data.writeInt32(format);
+ data.writeInt32(channelCount);
+ remote()->transact(GET_INPUTBUFFERSIZE, data, &reply);
+ return reply.readInt32();
+ }
+
+ virtual void wakeUp()
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
+ remote()->transact(WAKE_UP, data, &reply);
+ return;
+ }
};
IMPLEMENT_META_INTERFACE(AudioFlinger, "android.media.IAudioFlinger");
@@ -470,6 +500,26 @@ status_t BnAudioFlinger::onTransact(
reply->writeInt32( setParameter(key, value) );
return NO_ERROR;
} break;
+ case REGISTER_CLIENT: {
+ CHECK_INTERFACE(IAudioFlinger, data, reply);
+ sp<IAudioFlingerClient> client = interface_cast<IAudioFlingerClient>(data.readStrongBinder());
+ registerClient(client);
+ return NO_ERROR;
+ } break;
+ case GET_INPUTBUFFERSIZE: {
+ CHECK_INTERFACE(IAudioFlinger, data, reply);
+ uint32_t sampleRate = data.readInt32();
+ int format = data.readInt32();
+ int channelCount = data.readInt32();
+ reply->writeInt32( getInputBufferSize(sampleRate, format, channelCount) );
+ return NO_ERROR;
+ } break;
+ case WAKE_UP: {
+ CHECK_INTERFACE(IAudioFlinger, data, reply);
+ wakeUp();
+ return NO_ERROR;
+ } break;
+
default:
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/media/libmedia/IAudioFlingerClient.cpp b/media/libmedia/IAudioFlingerClient.cpp
new file mode 100644
index 0000000..d956266
--- /dev/null
+++ b/media/libmedia/IAudioFlingerClient.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "IAudioFlingerClient"
+#include <utils/Log.h>
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <utils/Parcel.h>
+
+#include <media/IAudioFlingerClient.h>
+
+namespace android {
+
+enum {
+ AUDIO_OUTPUT_CHANGED = IBinder::FIRST_CALL_TRANSACTION
+};
+
+class BpAudioFlingerClient : public BpInterface<IAudioFlingerClient>
+{
+public:
+ BpAudioFlingerClient(const sp<IBinder>& impl)
+ : BpInterface<IAudioFlingerClient>(impl)
+ {
+ }
+
+ void audioOutputChanged(uint32_t frameCount, uint32_t samplingRate, uint32_t latency)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAudioFlingerClient::getInterfaceDescriptor());
+ data.writeInt32(frameCount);
+ data.writeInt32(samplingRate);
+ data.writeInt32(latency);
+ remote()->transact(AUDIO_OUTPUT_CHANGED, data, &reply);
+ }
+};
+
+IMPLEMENT_META_INTERFACE(AudioFlingerClient, "android.media.IAudioFlingerClient");
+
+// ----------------------------------------------------------------------
+
+#define CHECK_INTERFACE(interface, data, reply) \
+ do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \
+ LOGW("Call incorrectly routed to " #interface); \
+ return PERMISSION_DENIED; \
+ } } while (0)
+
+status_t BnAudioFlingerClient::onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+ switch(code) {
+ case AUDIO_OUTPUT_CHANGED: {
+ CHECK_INTERFACE(IAudioFlingerClient, data, reply);
+ uint32_t frameCount = data.readInt32();
+ uint32_t samplingRate = data.readInt32();
+ uint32_t latency = data.readInt32();
+ audioOutputChanged(frameCount, samplingRate, latency);
+ return NO_ERROR;
+ } break;
+ default:
+ return BBinder::onTransact(code, data, reply, flags);
+ }
+}
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
diff --git a/media/libmedia/JetPlayer.cpp b/media/libmedia/JetPlayer.cpp
index f0edf88..ead24d4 100644
--- a/media/libmedia/JetPlayer.cpp
+++ b/media/libmedia/JetPlayer.cpp
@@ -214,12 +214,15 @@ int JetPlayer::render() {
}
p += count * pLibConfig->numChannels;
num_output += count * pLibConfig->numChannels * sizeof(EAS_PCM);
+
+ // send events that were generated (if any) to the event callback
+ fireEventsFromJetQueue();
}
// update playback state
//LOGV("JetPlayer::render(): updating state");
JET_Status(mEasData, &mJetStatus);
- fireEventOnStatusChange();
+ fireUpdateOnStatusChange();
mPaused = mJetStatus.paused;
mMutex.unlock(); // UNLOCK ]]]]]]]] -----------------------------------
@@ -261,9 +264,9 @@ threadExit:
//-------------------------------------------------------------------------------------------------
-// fire up an event if any of the status fields has changed
+// fire up an update if any of the status fields has changed
// precondition: mMutex locked
-void JetPlayer::fireEventOnStatusChange()
+void JetPlayer::fireUpdateOnStatusChange()
{
if( (mJetStatus.currentUserID != mPreviousJetStatus.currentUserID)
||(mJetStatus.segmentRepeatCount != mPreviousJetStatus.segmentRepeatCount) ) {
@@ -303,9 +306,31 @@ void JetPlayer::fireEventOnStatusChange()
//-------------------------------------------------------------------------------------------------
-int JetPlayer::openFile(const char* path)
+// fire up all the JET events in the JET engine queue (until the queue is empty)
+// precondition: mMutex locked
+void JetPlayer::fireEventsFromJetQueue()
+{
+ if(!mEventCallback) {
+ // no callback, just empty the event queue
+ while (JET_GetEvent(mEasData, NULL, NULL)) { }
+ return;
+ }
+
+ EAS_U32 rawEvent;
+ while (JET_GetEvent(mEasData, &rawEvent, NULL)) {
+ mEventCallback(
+ JetPlayer::JET_EVENT,
+ rawEvent,
+ -1,
+ mJavaJetPlayerRef);
+ }
+}
+
+
+//-------------------------------------------------------------------------------------------------
+int JetPlayer::loadFromFile(const char* path)
{
- LOGV("JetPlayer::openFile(): path=%s", path);
+ LOGV("JetPlayer::loadFromFile(): path=%s", path);
Mutex::Autolock lock(mMutex);
@@ -326,6 +351,29 @@ int JetPlayer::openFile(const char* path)
return( result );
}
+
+//-------------------------------------------------------------------------------------------------
+int JetPlayer::loadFromFD(const int fd, const long long offset, const long long length)
+{
+ LOGV("JetPlayer::loadFromFD(): fd=%d offset=%lld length=%lld", fd, offset, length);
+
+ Mutex::Autolock lock(mMutex);
+
+ mEasJetFileLoc = (EAS_FILE_LOCATOR) malloc(sizeof(EAS_FILE));
+ mEasJetFileLoc->fd = fd;
+ mEasJetFileLoc->offset = offset;
+ mEasJetFileLoc->length = length;
+ mEasJetFileLoc->path = NULL;
+
+ EAS_RESULT result = JET_OpenFile(mEasData, mEasJetFileLoc);
+ if(result != EAS_SUCCESS)
+ mState = EAS_STATE_ERROR;
+ else
+ mState = EAS_STATE_OPEN;
+ return( result );
+}
+
+
//-------------------------------------------------------------------------------------------------
int JetPlayer::closeFile()
{
@@ -348,7 +396,7 @@ int JetPlayer::play()
JET_Status(mEasData, &mJetStatus);
this->dumpJetStatus(&mJetStatus);
- fireEventOnStatusChange();
+ fireUpdateOnStatusChange();
// wake up render thread
LOGV("JetPlayer::play(): wakeup render thread");
@@ -368,7 +416,7 @@ int JetPlayer::pause()
JET_Status(mEasData, &mJetStatus);
this->dumpJetStatus(&mJetStatus);
- fireEventOnStatusChange();
+ fireUpdateOnStatusChange();
return result;
@@ -408,6 +456,14 @@ int JetPlayer::triggerClip(int clipId)
}
//-------------------------------------------------------------------------------------------------
+int JetPlayer::clearQueue()
+{
+ LOGV("JetPlayer::clearQueue");
+ Mutex::Autolock lock(mMutex);
+ return JET_Clear_Queue(mEasData);
+}
+
+//-------------------------------------------------------------------------------------------------
void JetPlayer::dump()
{
LOGE("JetPlayer dump: JET file=%s", mEasJetFileLoc->path);
diff --git a/media/libmedia/ToneGenerator.cpp b/media/libmedia/ToneGenerator.cpp
index 584d135..fa36460 100644
--- a/media/libmedia/ToneGenerator.cpp
+++ b/media/libmedia/ToneGenerator.cpp
@@ -97,10 +97,6 @@ ToneGenerator::ToneGenerator(int streamType, float volume) {
LOGE("Unable to marshal AudioFlinger");
return;
}
- if (AudioSystem::getOutputFrameCount(&mBufferSize) != NO_ERROR) {
- LOGE("Unable to marshal AudioFlinger");
- return;
- }
mStreamType = streamType;
mVolume = volume;
mpAudioTrack = 0;
@@ -275,9 +271,9 @@ bool ToneGenerator::initAudioTrack() {
mpAudioTrack = 0;
}
- // Open audio track in mono, PCM 16bit, default sampling rate, 2 buffers of
+ // Open audio track in mono, PCM 16bit, default sampling rate, default buffer size
mpAudioTrack
- = new AudioTrack(mStreamType, 0, AudioSystem::PCM_16_BIT, 1, NUM_PCM_BUFFERS*mBufferSize, 0, audioCallback, this, mBufferSize);
+ = new AudioTrack(mStreamType, 0, AudioSystem::PCM_16_BIT, 1, 0, 0, audioCallback, this, 0);
if (mpAudioTrack == 0) {
LOGE("AudioTrack allocation failed");
diff --git a/media/libmedia/mediaplayer.cpp b/media/libmedia/mediaplayer.cpp
index ebdbda8..31ff507 100644
--- a/media/libmedia/mediaplayer.cpp
+++ b/media/libmedia/mediaplayer.cpp
@@ -172,7 +172,7 @@ status_t MediaPlayer::setDataSource(const sp<IMediaPlayer>& player)
status_t MediaPlayer::setDataSource(const char *url)
{
LOGV("setDataSource(%s)", url);
- status_t err = UNKNOWN_ERROR;
+ status_t err = BAD_VALUE;
if (url != NULL) {
const sp<IMediaPlayerService>& service(getMediaPlayerService());
if (service != 0) {
@@ -199,7 +199,7 @@ status_t MediaPlayer::setVideoSurface(const sp<Surface>& surface)
{
LOGV("setVideoSurface");
Mutex::Autolock _l(mLock);
- if (mPlayer == 0) return UNKNOWN_ERROR;
+ if (mPlayer == 0) return NO_INIT;
return mPlayer->setVideoSurface(surface->getISurface());
}
@@ -219,7 +219,7 @@ status_t MediaPlayer::prepare()
{
LOGV("prepare");
Mutex::Autolock _l(mLock);
- if (mPrepareSync) return UNKNOWN_ERROR;
+ if (mPrepareSync) return -EALREADY;
mPrepareSync = true;
status_t ret = prepareAsync_l();
if (ret != NO_ERROR) return ret;
@@ -253,7 +253,6 @@ status_t MediaPlayer::start()
status_t ret = mPlayer->start();
if (ret != NO_ERROR) {
mCurrentState = MEDIA_PLAYER_STATE_ERROR;
- ret = UNKNOWN_ERROR;
} else {
if (mCurrentState == MEDIA_PLAYER_PLAYBACK_COMPLETE) {
LOGV("playback completed immediately following start()");
@@ -275,7 +274,6 @@ status_t MediaPlayer::stop()
status_t ret = mPlayer->stop();
if (ret != NO_ERROR) {
mCurrentState = MEDIA_PLAYER_STATE_ERROR;
- ret = UNKNOWN_ERROR;
} else {
mCurrentState = MEDIA_PLAYER_STOPPED;
}
@@ -295,7 +293,6 @@ status_t MediaPlayer::pause()
status_t ret = mPlayer->pause();
if (ret != NO_ERROR) {
mCurrentState = MEDIA_PLAYER_STATE_ERROR;
- ret = UNKNOWN_ERROR;
} else {
mCurrentState = MEDIA_PLAYER_PAUSED;
}
@@ -422,7 +419,6 @@ status_t MediaPlayer::reset()
if (ret != NO_ERROR) {
LOGE("reset() failed with return code (%d)", ret);
mCurrentState = MEDIA_PLAYER_STATE_ERROR;
- ret = UNKNOWN_ERROR;
} else {
mCurrentState = MEDIA_PLAYER_IDLE;
}
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index 5383171..9e366e2 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -61,113 +61,32 @@ pid_t gettid() { return syscall(__NR_gettid);}
#undef __KERNEL__
#endif
-/*
- When USE_SIGBUS_HANDLER is set to 1, a handler for SIGBUS will be
- installed, which allows us to recover when there is a read error
- when accessing an mmap'ed file. However, since the kernel folks
- don't seem to like it when non kernel folks install signal handlers
- in their own process, this is currently disabled.
- Without the handler, the process hosting this service will die and
- then be restarted. This is mostly OK right now because the process is
- not being shared with any other services, and clients of the service
- will be notified of its death in their MediaPlayer.onErrorListener
- callback, assuming they have installed one, and can then attempt to
- do their own recovery.
- It does open us up to a DOS attack against the media server, where
- a malicious application can trivially force the media server to
- restart continuously.
-*/
-#define USE_SIGBUS_HANDLER 0
+
+namespace android {
// TODO: Temp hack until we can register players
-static const char* MIDI_FILE_EXTS[] =
-{
- ".mid",
- ".smf",
- ".xmf",
- ".imy",
- ".rtttl",
- ".rtx",
- ".ota"
+typedef struct {
+ const char *extension;
+ const player_type playertype;
+} extmap;
+extmap FILE_EXTS [] = {
+ {".mid", SONIVOX_PLAYER},
+ {".midi", SONIVOX_PLAYER},
+ {".smf", SONIVOX_PLAYER},
+ {".xmf", SONIVOX_PLAYER},
+ {".imy", SONIVOX_PLAYER},
+ {".rtttl", SONIVOX_PLAYER},
+ {".rtx", SONIVOX_PLAYER},
+ {".ota", SONIVOX_PLAYER},
+ {".ogg", VORBIS_PLAYER},
+ {".oga", VORBIS_PLAYER},
};
-namespace android {
-
// 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;
-
-static void sigbushandler(int signal, siginfo_t *info, void *context)
-{
- char *faultaddr = (char*) info->si_addr;
- LOGE("SIGBUS at %p\n", faultaddr);
-
- struct mediasigbushandler* h = (struct mediasigbushandler*) pthread_getspecific(sigbuskey);
-
- if (h) {
- if (h->len) {
- if (faultaddr < h->base || faultaddr >= h->base + h->len) {
- // outside specified range, call old handler
- if (oldact.sa_flags & SA_SIGINFO) {
- oldact.sa_sigaction(signal, info, context);
- } else {
- oldact.sa_handler(signal);
- }
- return;
- }
- }
-
- // no range specified or address was in range
-
- if (h->handlesigbus) {
- if (h->handlesigbus(info, h)) {
- // thread's handler didn't handle the signal
- if (oldact.sa_flags & SA_SIGINFO) {
- oldact.sa_sigaction(signal, info, context);
- } else {
- oldact.sa_handler(signal);
- }
- }
- return;
- }
-
- if (h->sigbusvar) {
- // map in a zeroed out page so the operation can succeed
- long pagesize = sysconf(_SC_PAGE_SIZE);
- long pagemask = ~(pagesize - 1);
- void * pageaddr = (void*) (((long)(faultaddr)) & pagemask);
-
- void * bar = mmap( pageaddr, pagesize, PROT_READ, MAP_ANONYMOUS|MAP_PRIVATE|MAP_FIXED, -1, 0);
- if (bar == MAP_FAILED) {
- LOGE("couldn't map zero page at %p: %s", pageaddr, strerror(errno));
- if (oldact.sa_flags & SA_SIGINFO) {
- oldact.sa_sigaction(signal, info, context);
- } else {
- oldact.sa_handler(signal);
- }
- return;
- }
-
- LOGE("setting sigbusvar at %p", h->sigbusvar);
- *(h->sigbusvar) = 1;
- return;
- }
- }
-
- LOGE("SIGBUS: no handler, or improperly configured handler (%p)", h);
-
- if (oldact.sa_flags & SA_SIGINFO) {
- oldact.sa_sigaction(signal, info, context);
- } else {
- oldact.sa_handler(signal);
- }
- return;
-}
-
void MediaPlayerService::instantiate() {
defaultServiceManager()->addService(
String16("media.player"), new MediaPlayerService());
@@ -177,25 +96,10 @@ MediaPlayerService::MediaPlayerService()
{
LOGV("MediaPlayerService created");
mNextConnId = 1;
-
- pthread_key_create(&sigbuskey, NULL);
-
-
-#if USE_SIGBUS_HANDLER
- struct sigaction act;
- memset(&act,0, sizeof act);
- act.sa_sigaction = sigbushandler;
- act.sa_flags = SA_SIGINFO;
- sigaction(SIGBUS, &act, &oldact);
-#endif
}
MediaPlayerService::~MediaPlayerService()
{
-#if USE_SIGBUS_HANDLER
- sigaction(SIGBUS, &oldact, NULL);
-#endif
- pthread_key_delete(sigbuskey);
LOGV("MediaPlayerService destroyed");
}
@@ -481,7 +385,7 @@ static player_type getPlayerType(int fd, int64_t offset, int64_t length)
locator.offset = offset;
locator.length = length;
EAS_HANDLE eashandle;
- if (EAS_OpenFile(easdata, &locator, &eashandle, NULL) == EAS_SUCCESS) {
+ if (EAS_OpenFile(easdata, &locator, &eashandle) == EAS_SUCCESS) {
EAS_CloseFile(easdata, eashandle);
EAS_Shutdown(easdata);
return SONIVOX_PLAYER;
@@ -498,22 +402,16 @@ static player_type getPlayerType(const char* url)
// use MidiFile for MIDI extensions
int lenURL = strlen(url);
- for (int i = 0; i < NELEM(MIDI_FILE_EXTS); ++i) {
- int len = strlen(MIDI_FILE_EXTS[i]);
+ for (int i = 0; i < NELEM(FILE_EXTS); ++i) {
+ int len = strlen(FILE_EXTS[i].extension);
int start = lenURL - len;
if (start > 0) {
- if (!strncmp(url + start, MIDI_FILE_EXTS[i], len)) {
- LOGV("Type is MIDI");
- return SONIVOX_PLAYER;
+ if (!strncmp(url + start, FILE_EXTS[i].extension, len)) {
+ return FILE_EXTS[i].playertype;
}
}
}
- if (strcmp(url + strlen(url) - 4, ".ogg") == 0) {
- LOGV("Type is Vorbis");
- return VORBIS_PLAYER;
- }
-
// Fall through to PV
return PV_PLAYER;
}
@@ -539,7 +437,6 @@ static sp<MediaPlayerBase> createPlayer(player_type playerType, void* cookie,
if (p != NULL) {
if (p->initCheck() == NO_ERROR) {
p->setNotifyCallback(cookie, notifyFunc);
- p->setSigBusHandlerStructTLSKey(sigbuskey);
} else {
p.clear();
}
diff --git a/media/libmediaplayerservice/MidiFile.cpp b/media/libmediaplayerservice/MidiFile.cpp
index cfad66c..7ce2fab 100644
--- a/media/libmediaplayerservice/MidiFile.cpp
+++ b/media/libmediaplayerservice/MidiFile.cpp
@@ -40,8 +40,6 @@ static pid_t myTid() { return getpid(); }
// ----------------------------------------------------------------------------
-extern pthread_key_t EAS_sigbuskey;
-
namespace android {
// ----------------------------------------------------------------------------
@@ -132,7 +130,7 @@ status_t MidiFile::setDataSource(const char* path)
mFileLocator.fd = -1;
mFileLocator.offset = 0;
mFileLocator.length = 0;
- EAS_RESULT result = EAS_OpenFile(mEasData, &mFileLocator, &mEasHandle, &mMemFailedVar);
+ EAS_RESULT result = EAS_OpenFile(mEasData, &mFileLocator, &mEasHandle);
if (result == EAS_SUCCESS) {
updateState();
}
@@ -148,12 +146,6 @@ status_t MidiFile::setDataSource(const char* path)
return NO_ERROR;
}
-status_t MidiFile::setSigBusHandlerStructTLSKey(pthread_key_t key)
-{
- EAS_sigbuskey = key;
- return 0;
-}
-
status_t MidiFile::setDataSource(int fd, int64_t offset, int64_t length)
{
LOGV("MidiFile::setDataSource fd=%d", fd);
@@ -168,7 +160,7 @@ status_t MidiFile::setDataSource(int fd, int64_t offset, int64_t length)
mFileLocator.fd = dup(fd);
mFileLocator.offset = offset;
mFileLocator.length = length;
- EAS_RESULT result = EAS_OpenFile(mEasData, &mFileLocator, &mEasHandle, &mMemFailedVar);
+ EAS_RESULT result = EAS_OpenFile(mEasData, &mFileLocator, &mEasHandle);
updateState();
if (result != EAS_SUCCESS) {
@@ -332,7 +324,7 @@ status_t MidiFile::getDuration(int* duration)
EAS_HANDLE easHandle = NULL;
EAS_RESULT result = EAS_Init(&easData);
if (result == EAS_SUCCESS) {
- result = EAS_OpenFile(easData, &mFileLocator, &easHandle, NULL);
+ result = EAS_OpenFile(easData, &mFileLocator, &easHandle);
}
if (result == EAS_SUCCESS) {
result = EAS_Prepare(easData, easHandle);
@@ -451,8 +443,6 @@ int MidiFile::render() {
LOGV("MidiFile::render");
- struct mediasigbushandler sigbushandler;
-
// allocate render buffer
mAudioBuffer = new EAS_PCM[pLibConfig->mixBufferSize * pLibConfig->numChannels * NUM_BUFFERS];
if (!mAudioBuffer) {
@@ -468,10 +458,6 @@ int MidiFile::render() {
mCondition.signal();
}
- sigbushandler.handlesigbus = NULL;
- sigbushandler.sigbusvar = mMemFailedVar;
- pthread_setspecific(EAS_sigbuskey, &sigbushandler);
-
while (1) {
mMutex.lock();
diff --git a/media/libmediaplayerservice/MidiFile.h b/media/libmediaplayerservice/MidiFile.h
index 9d2dfdd..302f1cf 100644
--- a/media/libmediaplayerservice/MidiFile.h
+++ b/media/libmediaplayerservice/MidiFile.h
@@ -30,7 +30,6 @@ public:
~MidiFile();
virtual status_t initCheck();
- virtual status_t setSigBusHandlerStructTLSKey(pthread_key_t key);
virtual status_t setDataSource(const char* path);
virtual status_t setDataSource(int fd, int64_t offset, int64_t length);
virtual status_t setVideoSurface(const sp<ISurface>& surface) { return UNKNOWN_ERROR; }
@@ -57,7 +56,6 @@ private:
Mutex mMutex;
Condition mCondition;
- int* mMemFailedVar;
EAS_DATA_HANDLE mEasData;
EAS_HANDLE mEasHandle;
EAS_PCM* mAudioBuffer;
diff --git a/media/libmediaplayerservice/VorbisPlayer.cpp b/media/libmediaplayerservice/VorbisPlayer.cpp
index 9a64403..009d628 100644
--- a/media/libmediaplayerservice/VorbisPlayer.cpp
+++ b/media/libmediaplayerservice/VorbisPlayer.cpp
@@ -455,13 +455,15 @@ int VorbisPlayer::render() {
current_section = 0;
numread = ov_read(&mVorbisFile, mAudioBuffer, AUDIOBUFFER_SIZE, &current_section);
} else {
- sendEvent(MEDIA_PLAYBACK_COMPLETE);
mAudioSink->stop();
audioStarted = false;
mRender = false;
mPaused = true;
int endpos = ov_time_tell(&mVorbisFile);
+ LOGV("send MEDIA_PLAYBACK_COMPLETE");
+ sendEvent(MEDIA_PLAYBACK_COMPLETE);
+
// wait until we're started again
LOGV("playback complete - wait for signal");
mCondition.wait(mMutex);