summaryrefslogtreecommitdiff
path: root/media/libmediaplayerservice/MediaPlayerService.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'media/libmediaplayerservice/MediaPlayerService.cpp')
-rw-r--r--media/libmediaplayerservice/MediaPlayerService.cpp502
1 files changed, 483 insertions, 19 deletions
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index 8ef0dc6c42c4..0a6c365df5ad 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -27,18 +27,28 @@
#include <unistd.h>
#include <string.h>
+
#include <cutils/atomic.h>
+#include <cutils/properties.h> // for property_get
+
+#include <utils/misc.h>
#include <android_runtime/ActivityManager.h>
-#include <utils/IPCThreadState.h>
-#include <utils/IServiceManager.h>
-#include <utils/MemoryHeapBase.h>
-#include <utils/MemoryBase.h>
+
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/MemoryHeapBase.h>
+#include <binder/MemoryBase.h>
+#include <utils/Errors.h> // for status_t
+#include <utils/String8.h>
+#include <utils/SystemClock.h>
+#include <utils/Vector.h>
#include <cutils/properties.h>
#include <media/MediaPlayerInterface.h>
#include <media/mediarecorder.h>
#include <media/MediaMetadataRetrieverInterface.h>
+#include <media/Metadata.h>
#include <media/AudioTrack.h>
#include "MediaRecorderClient.h"
@@ -48,6 +58,10 @@
#include "MidiFile.h"
#include "VorbisPlayer.h"
#include <media/PVPlayer.h>
+#include "TestPlayerStub.h"
+#include "StagefrightPlayer.h"
+
+#include <OMX.h>
/* desktop Linux needs a little help with gettid() */
#if defined(HAVE_GETTID) && !defined(HAVE_ANDROID_OS)
@@ -61,6 +75,111 @@ pid_t gettid() { return syscall(__NR_gettid);}
#undef __KERNEL__
#endif
+namespace {
+using android::media::Metadata;
+using android::status_t;
+using android::OK;
+using android::BAD_VALUE;
+using android::NOT_ENOUGH_DATA;
+using android::Parcel;
+
+// Max number of entries in the filter.
+const int kMaxFilterSize = 64; // I pulled that out of thin air.
+
+// FIXME: Move all the metadata related function in the Metadata.cpp
+
+
+// Unmarshall a filter from a Parcel.
+// Filter format in a parcel:
+//
+// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | number of entries (n) |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | metadata type 1 |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | metadata type 2 |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// ....
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | metadata type n |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+//
+// @param p Parcel that should start with a filter.
+// @param[out] filter On exit contains the list of metadata type to be
+// filtered.
+// @param[out] status On exit contains the status code to be returned.
+// @return true if the parcel starts with a valid filter.
+bool unmarshallFilter(const Parcel& p,
+ Metadata::Filter *filter,
+ status_t *status)
+{
+ int32_t val;
+ if (p.readInt32(&val) != OK)
+ {
+ LOGE("Failed to read filter's length");
+ *status = NOT_ENOUGH_DATA;
+ return false;
+ }
+
+ if( val > kMaxFilterSize || val < 0)
+ {
+ LOGE("Invalid filter len %d", val);
+ *status = BAD_VALUE;
+ return false;
+ }
+
+ const size_t num = val;
+
+ filter->clear();
+ filter->setCapacity(num);
+
+ size_t size = num * sizeof(Metadata::Type);
+
+
+ if (p.dataAvail() < size)
+ {
+ LOGE("Filter too short expected %d but got %d", size, p.dataAvail());
+ *status = NOT_ENOUGH_DATA;
+ return false;
+ }
+
+ const Metadata::Type *data =
+ static_cast<const Metadata::Type*>(p.readInplace(size));
+
+ if (NULL == data)
+ {
+ LOGE("Filter had no data");
+ *status = BAD_VALUE;
+ return false;
+ }
+
+ // TODO: The stl impl of vector would be more efficient here
+ // because it degenerates into a memcpy on pod types. Try to
+ // replace later or use stl::set.
+ for (size_t i = 0; i < num; ++i)
+ {
+ filter->add(*data);
+ ++data;
+ }
+ *status = OK;
+ return true;
+}
+
+// @param filter Of metadata type.
+// @param val To be searched.
+// @return true if a match was found.
+bool findMetadata(const Metadata::Filter& filter, const int32_t val)
+{
+ // Deal with empty and ANY right away
+ if (filter.isEmpty()) return false;
+ if (filter[0] == Metadata::kAny) return true;
+
+ return filter.indexOf(val) >= 0;
+}
+
+} // anonymous namespace
+
namespace android {
@@ -83,7 +202,7 @@ extmap FILE_EXTS [] = {
};
// TODO: Find real cause of Audio/Video delay in PV framework and remove this workaround
-/* static */ const uint32_t MediaPlayerService::AudioOutput::kAudioVideoDelayMs = 96;
+/* static */ const uint32_t MediaPlayerService::AudioOutput::kAudioVideoDelayMs = 0;
/* static */ int MediaPlayerService::AudioOutput::mMinBufferCount = 4;
/* static */ bool MediaPlayerService::AudioOutput::mIsOnEmulator = false;
@@ -106,7 +225,10 @@ MediaPlayerService::~MediaPlayerService()
sp<IMediaRecorder> MediaPlayerService::createMediaRecorder(pid_t pid)
{
#ifndef NO_OPENCORE
- sp<MediaRecorderClient> recorder = new MediaRecorderClient(pid);
+ sp<MediaRecorderClient> recorder = new MediaRecorderClient(this, pid);
+ wp<MediaRecorderClient> w = recorder;
+ Mutex::Autolock lock(mLock);
+ mMediaRecorderClients.add(w);
#else
sp<MediaRecorderClient> recorder = NULL;
#endif
@@ -114,6 +236,13 @@ sp<IMediaRecorder> MediaPlayerService::createMediaRecorder(pid_t pid)
return recorder;
}
+void MediaPlayerService::removeMediaRecorderClient(wp<MediaRecorderClient> client)
+{
+ Mutex::Autolock lock(mLock);
+ mMediaRecorderClients.remove(client);
+ LOGV("Delete media recorder client");
+}
+
sp<IMediaMetadataRetriever> MediaPlayerService::createMetadataRetriever(pid_t pid)
{
sp<MetadataRetrieverClient> retriever = new MetadataRetrieverClient(pid);
@@ -155,6 +284,16 @@ sp<IMediaPlayer> MediaPlayerService::create(pid_t pid, const sp<IMediaPlayerClie
return c;
}
+sp<IOMX> MediaPlayerService::getOMX() {
+ Mutex::Autolock autoLock(mLock);
+
+ if (mOMX.get() == NULL) {
+ mOMX = new OMX;
+ }
+
+ return mOMX;
+}
+
status_t MediaPlayerService::AudioCache::dump(int fd, const Vector<String16>& args) const
{
const size_t SIZE = 256;
@@ -337,6 +476,13 @@ status_t MediaPlayerService::dump(int fd, const Vector<String16>& args)
sp<Client> c = mClients[i].promote();
if (c != 0) c->dump(fd, args);
}
+ for (int i = 0, n = mMediaRecorderClients.size(); i < n; ++i) {
+ result.append(" MediaRecorderClient\n");
+ sp<MediaRecorderClient> c = mMediaRecorderClients[i].promote();
+ snprintf(buffer, 255, " pid(%d)\n\n", c->mPid);
+ result.append(buffer);
+ }
+
result.append(" Files opened and/or mapped:\n");
snprintf(buffer, SIZE, "/proc/%d/maps", myTid());
FILE *f = fopen(buffer, "r");
@@ -461,6 +607,7 @@ void MediaPlayerService::Client::disconnect()
p = mPlayer;
}
mClient.clear();
+
mPlayer.clear();
// clear the notification to prevent callbacks to dead client
@@ -478,7 +625,19 @@ void MediaPlayerService::Client::disconnect()
IPCThreadState::self()->flushCommands();
}
-static player_type getPlayerType(int fd, int64_t offset, int64_t length)
+static player_type getDefaultPlayerType() {
+#if BUILD_WITH_FULL_STAGEFRIGHT
+ char value[PROPERTY_VALUE_MAX];
+ if (property_get("media.stagefright.enable-player", value, NULL)
+ && (!strcmp(value, "1") || !strcasecmp(value, "true"))) {
+ return STAGEFRIGHT_PLAYER;
+ }
+#endif
+
+ return PV_PLAYER;
+}
+
+player_type getPlayerType(int fd, int64_t offset, int64_t length)
{
char buf[20];
lseek(fd, offset, SEEK_SET);
@@ -508,12 +667,14 @@ static player_type getPlayerType(int fd, int64_t offset, int64_t length)
EAS_Shutdown(easdata);
}
- // Fall through to PV
- return PV_PLAYER;
+ return getDefaultPlayerType();
}
-static player_type getPlayerType(const char* url)
+player_type getPlayerType(const char* url)
{
+ if (TestPlayerStub::canBeUsed(url)) {
+ return TEST_PLAYER;
+ }
// use MidiFile for MIDI extensions
int lenURL = strlen(url);
@@ -527,8 +688,7 @@ static player_type getPlayerType(const char* url)
}
}
- // Fall through to PV
- return PV_PLAYER;
+ return getDefaultPlayerType();
}
static sp<MediaPlayerBase> createPlayer(player_type playerType, void* cookie,
@@ -550,6 +710,16 @@ static sp<MediaPlayerBase> createPlayer(player_type playerType, void* cookie,
LOGV(" create VorbisPlayer");
p = new VorbisPlayer();
break;
+#if BUILD_WITH_FULL_STAGEFRIGHT
+ case STAGEFRIGHT_PLAYER:
+ LOGV(" create StagefrightPlayer");
+ p = new StagefrightPlayer;
+ break;
+#endif
+ case TEST_PLAYER:
+ LOGV("Create Test Player stub");
+ p = new TestPlayerStub();
+ break;
}
if (p != NULL) {
if (p->initCheck() == NO_ERROR) {
@@ -614,7 +784,11 @@ status_t MediaPlayerService::Client::setDataSource(const char *url)
// now set data source
LOGV(" setDataSource");
mStatus = p->setDataSource(url);
- if (mStatus == NO_ERROR) mPlayer = p;
+ if (mStatus == NO_ERROR) {
+ mPlayer = p;
+ } else {
+ LOGE(" error: %d", mStatus);
+ }
return mStatus;
}
}
@@ -671,6 +845,73 @@ status_t MediaPlayerService::Client::setVideoSurface(const sp<ISurface>& surface
return p->setVideoSurface(surface);
}
+status_t MediaPlayerService::Client::invoke(const Parcel& request,
+ Parcel *reply)
+{
+ sp<MediaPlayerBase> p = getPlayer();
+ if (p == NULL) return UNKNOWN_ERROR;
+ return p->invoke(request, reply);
+}
+
+// This call doesn't need to access the native player.
+status_t MediaPlayerService::Client::setMetadataFilter(const Parcel& filter)
+{
+ status_t status;
+ media::Metadata::Filter allow, drop;
+
+ if (unmarshallFilter(filter, &allow, &status) &&
+ unmarshallFilter(filter, &drop, &status)) {
+ Mutex::Autolock lock(mLock);
+
+ mMetadataAllow = allow;
+ mMetadataDrop = drop;
+ }
+ return status;
+}
+
+status_t MediaPlayerService::Client::getMetadata(
+ bool update_only, bool apply_filter, Parcel *reply)
+{
+ sp<MediaPlayerBase> player = getPlayer();
+ if (player == 0) return UNKNOWN_ERROR;
+
+ status_t status;
+ // Placeholder for the return code, updated by the caller.
+ reply->writeInt32(-1);
+
+ media::Metadata::Filter ids;
+
+ // We don't block notifications while we fetch the data. We clear
+ // mMetadataUpdated first so we don't lose notifications happening
+ // during the rest of this call.
+ {
+ Mutex::Autolock lock(mLock);
+ if (update_only) {
+ ids = mMetadataUpdated;
+ }
+ mMetadataUpdated.clear();
+ }
+
+ media::Metadata metadata(reply);
+
+ metadata.appendHeader();
+ status = player->getMetadata(ids, reply);
+
+ if (status != OK) {
+ metadata.resetParcel();
+ LOGE("getMetadata failed %d", status);
+ return status;
+ }
+
+ // FIXME: Implement filtering on the result. Not critical since
+ // filtering takes place on the update notifications already. This
+ // would be when all the metadata are fetch and a filter is set.
+
+ // Everything is fine, update the metadata length.
+ metadata.updateLength();
+ return OK;
+}
+
status_t MediaPlayerService::Client::prepareAsync()
{
LOGV("[%d] prepareAsync", mConnId);
@@ -790,13 +1031,51 @@ status_t MediaPlayerService::Client::setVolume(float leftVolume, float rightVolu
return NO_ERROR;
}
+
void MediaPlayerService::Client::notify(void* cookie, int msg, int ext1, int ext2)
{
Client* client = static_cast<Client*>(cookie);
+
+ if (MEDIA_INFO == msg &&
+ MEDIA_INFO_METADATA_UPDATE == ext1) {
+ const media::Metadata::Type metadata_type = ext2;
+
+ if(client->shouldDropMetadata(metadata_type)) {
+ return;
+ }
+
+ // Update the list of metadata that have changed. getMetadata
+ // also access mMetadataUpdated and clears it.
+ client->addNewMetadataUpdate(metadata_type);
+ }
LOGV("[%d] notify (%p, %d, %d, %d)", client->mConnId, cookie, msg, ext1, ext2);
client->mClient->notify(msg, ext1, ext2);
}
+
+bool MediaPlayerService::Client::shouldDropMetadata(media::Metadata::Type code) const
+{
+ Mutex::Autolock lock(mLock);
+
+ if (findMetadata(mMetadataDrop, code)) {
+ return true;
+ }
+
+ if (mMetadataAllow.isEmpty() || findMetadata(mMetadataAllow, code)) {
+ return false;
+ } else {
+ return true;
+ }
+}
+
+
+void MediaPlayerService::Client::addNewMetadataUpdate(media::Metadata::Type metadata_type) {
+ Mutex::Autolock lock(mLock);
+ if (mMetadataUpdated.indexOf(metadata_type) < 0) {
+ mMetadataUpdated.add(metadata_type);
+ }
+}
+
#if CALLBACK_ANTAGONIZER
const int Antagonizer::interval = 10000; // 10 msecs
@@ -930,16 +1209,129 @@ Exit:
return mem;
}
+/*
+ * Avert your eyes, ugly hack ahead.
+ * The following is to support music visualizations.
+ */
+
+static const int NUMVIZBUF = 32;
+static const int VIZBUFFRAMES = 1024;
+static const int TOTALBUFTIMEMSEC = NUMVIZBUF * VIZBUFFRAMES * 1000 / 44100;
+
+static bool gotMem = false;
+static sp<MemoryBase> mem[NUMVIZBUF];
+static uint64_t timeStamp[NUMVIZBUF];
+static uint64_t lastReadTime;
+static uint64_t lastWriteTime;
+static int writeIdx = 0;
+
+static void allocVizBufs() {
+ if (!gotMem) {
+ for (int i=0;i<NUMVIZBUF;i++) {
+ sp<MemoryHeapBase> heap = new MemoryHeapBase(VIZBUFFRAMES*2, 0, "snooper");
+ mem[i] = new MemoryBase(heap, 0, heap->getSize());
+ timeStamp[i] = 0;
+ }
+ gotMem = true;
+ }
+}
+
+
+/*
+ * Get a buffer of audio data that is about to be played.
+ * We don't synchronize this because in practice the writer
+ * is ahead of the reader, and even if we did happen to catch
+ * a buffer while it's being written, it's just a visualization,
+ * so no harm done.
+ */
+static sp<MemoryBase> getVizBuffer() {
+
+ allocVizBufs();
+
+ lastReadTime = uptimeMillis() + 100; // account for renderer delay (we shouldn't be doing this here)
+
+ // if there is no recent buffer (yet), just return empty handed
+ if (lastWriteTime + TOTALBUFTIMEMSEC < lastReadTime) {
+ //LOGI("@@@@ no audio data to look at yet");
+ return NULL;
+ }
+
+ char buf[200];
+
+ int closestIdx = -1;
+ uint32_t closestTime = 0x7ffffff;
+
+ for (int i = 0; i < NUMVIZBUF; i++) {
+ uint64_t tsi = timeStamp[i];
+ uint64_t diff = tsi > lastReadTime ? tsi - lastReadTime : lastReadTime - tsi;
+ if (diff < closestTime) {
+ closestIdx = i;
+ closestTime = diff;
+ }
+ }
+
+
+ if (closestIdx >= 0) {
+ //LOGI("@@@ return buffer %d, %d/%d", closestIdx, uint32_t(lastReadTime), uint32_t(timeStamp[closestIdx]));
+ return mem[closestIdx];
+ }
+
+ // we won't get here, since we either bailed out early, or got a buffer
+ LOGD("Didn't expect to be here");
+ return NULL;
+}
+
+static void storeVizBuf(const void *data, int len, uint64_t time) {
+ // Copy the data in to the visualizer buffer
+ // Assume a 16 bit stereo source for now.
+ short *viz = (short*)mem[writeIdx]->pointer();
+ short *src = (short*)data;
+ for (int i = 0; i < VIZBUFFRAMES; i++) {
+ // Degrade quality by mixing to mono and clearing the lowest 3 bits.
+ // This should still be good enough for a visualization
+ *viz++ = ((int(src[0]) + int(src[1])) >> 1) & ~0x7;
+ src += 2;
+ }
+ timeStamp[writeIdx++] = time;
+ if (writeIdx >= NUMVIZBUF) {
+ writeIdx = 0;
+ }
+}
+
+static void makeVizBuffers(const char *data, int len, uint64_t time) {
+
+ allocVizBufs();
+
+ uint64_t startTime = time;
+ const int frameSize = 4; // 16 bit stereo sample is 4 bytes
+ while (len >= VIZBUFFRAMES * frameSize) {
+ storeVizBuf(data, len, time);
+ data += VIZBUFFRAMES * frameSize;
+ len -= VIZBUFFRAMES * frameSize;
+ time += 1000 * VIZBUFFRAMES / 44100;
+ }
+ //LOGI("@@@ stored buffers from %d to %d", uint32_t(startTime), uint32_t(time));
+}
+
+sp<IMemory> MediaPlayerService::snoop()
+{
+ sp<MemoryBase> mem = getVizBuffer();
+ return mem;
+}
+
+
#undef LOG_TAG
#define LOG_TAG "AudioSink"
MediaPlayerService::AudioOutput::AudioOutput()
-{
+ : mCallback(NULL),
+ mCallbackCookie(NULL) {
mTrack = 0;
mStreamType = AudioSystem::MUSIC;
mLeftVolume = 1.0;
mRightVolume = 1.0;
mLatency = 0;
mMsecsPerFrame = 0;
+ mNumFramesWritten = 0;
setMinBufferCount();
}
@@ -1003,8 +1395,13 @@ float MediaPlayerService::AudioOutput::msecsPerFrame() const
return mMsecsPerFrame;
}
-status_t MediaPlayerService::AudioOutput::open(uint32_t sampleRate, int channelCount, int format, int bufferCount)
+status_t MediaPlayerService::AudioOutput::open(
+ uint32_t sampleRate, int channelCount, int format, int bufferCount,
+ AudioCallback cb, void *cookie)
{
+ mCallback = cb;
+ mCallbackCookie = cookie;
+
// Check argument "bufferCount" against the mininum buffer count
if (bufferCount < mMinBufferCount) {
LOGD("bufferCount (%d) is too small and increased to %d", bufferCount, mMinBufferCount);
@@ -1025,7 +1422,27 @@ status_t MediaPlayerService::AudioOutput::open(uint32_t sampleRate, int channelC
}
frameCount = (sampleRate*afFrameCount*bufferCount)/afSampleRate;
- AudioTrack *t = new AudioTrack(mStreamType, sampleRate, format, channelCount, frameCount);
+
+ AudioTrack *t;
+ if (mCallback != NULL) {
+ t = new AudioTrack(
+ mStreamType,
+ sampleRate,
+ format,
+ (channelCount == 2) ? AudioSystem::CHANNEL_OUT_STEREO : AudioSystem::CHANNEL_OUT_MONO,
+ frameCount,
+ 0 /* flags */,
+ CallbackWrapper,
+ this);
+ } else {
+ t = new AudioTrack(
+ mStreamType,
+ sampleRate,
+ format,
+ (channelCount == 2) ? AudioSystem::CHANNEL_OUT_STEREO : AudioSystem::CHANNEL_OUT_MONO,
+ frameCount);
+ }
+
if ((t == 0) || (t->initCheck() != NO_ERROR)) {
LOGE("Unable to create audio track");
delete t;
@@ -1046,13 +1463,38 @@ void MediaPlayerService::AudioOutput::start()
if (mTrack) {
mTrack->setVolume(mLeftVolume, mRightVolume);
mTrack->start();
+ mTrack->getPosition(&mNumFramesWritten);
}
}
ssize_t MediaPlayerService::AudioOutput::write(const void* buffer, size_t size)
{
+ LOG_FATAL_IF(mCallback != NULL, "Don't call write if supplying a callback.");
+
//LOGV("write(%p, %u)", buffer, size);
- if (mTrack) return mTrack->write(buffer, size);
+ if (mTrack) {
+ // Only make visualization buffers if anyone recently requested visualization data
+ uint64_t now = uptimeMillis();
+ if (lastReadTime + TOTALBUFTIMEMSEC >= now) {
+ // Based on the current play counter, the number of frames written and
+ // the current real time we can calculate the approximate real start
+ // time of the buffer we're about to write.
+ uint32_t pos;
+ mTrack->getPosition(&pos);
+
+ // we're writing ahead by this many frames:
+ int ahead = mNumFramesWritten - pos;
+ //LOGI("@@@ written: %d, playpos: %d, latency: %d", mNumFramesWritten, pos, mTrack->latency());
+ // which is this many milliseconds, assuming 44100 Hz:
+ ahead /= 44;
+
+ makeVizBuffers((const char*)buffer, size, now + ahead + mTrack->latency());
+ lastWriteTime = now;
+ }
+ ssize_t ret = mTrack->write(buffer, size);
+ mNumFramesWritten += ret / 4; // assume 16 bit stereo
+ return ret;
+ }
return NO_INIT;
}
@@ -1060,6 +1502,7 @@ void MediaPlayerService::AudioOutput::stop()
{
LOGV("stop");
if (mTrack) mTrack->stop();
+ lastWriteTime = 0;
}
void MediaPlayerService::AudioOutput::flush()
@@ -1072,6 +1515,7 @@ void MediaPlayerService::AudioOutput::pause()
{
LOGV("pause");
if (mTrack) mTrack->pause();
+ lastWriteTime = 0;
}
void MediaPlayerService::AudioOutput::close()
@@ -1091,6 +1535,20 @@ void MediaPlayerService::AudioOutput::setVolume(float left, float right)
}
}
+// static
+void MediaPlayerService::AudioOutput::CallbackWrapper(
+ int event, void *cookie, void *info) {
+ if (event != AudioTrack::EVENT_MORE_DATA) {
+ return;
+ }
+
+ AudioOutput *me = (AudioOutput *)cookie;
+ AudioTrack::Buffer *buffer = (AudioTrack::Buffer *)info;
+
+ (*me->mCallback)(
+ me, buffer->raw, buffer->size, me->mCallbackCookie);
+}
+
#undef LOG_TAG
#define LOG_TAG "AudioCache"
MediaPlayerService::AudioCache::AudioCache(const char* name) :
@@ -1111,8 +1569,14 @@ float MediaPlayerService::AudioCache::msecsPerFrame() const
return mMsecsPerFrame;
}
-status_t MediaPlayerService::AudioCache::open(uint32_t sampleRate, int channelCount, int format, int bufferCount)
+status_t MediaPlayerService::AudioCache::open(
+ uint32_t sampleRate, int channelCount, int format, int bufferCount,
+ AudioCallback cb, void *cookie)
{
+ if (cb != NULL) {
+ return UNKNOWN_ERROR; // TODO: implement this.
+ }
+
LOGV("open(%u, %d, %d, %d)", sampleRate, channelCount, format, bufferCount);
if (mHeap->getHeapID() < 0) return NO_INIT;
mSampleRate = sampleRate;
@@ -1177,4 +1641,4 @@ void MediaPlayerService::AudioCache::notify(void* cookie, int msg, int ext1, int
p->mSignal.signal();
}
-}; // namespace android
+} // namespace android