summaryrefslogtreecommitdiffstats
path: root/media/libmediaplayerservice/MediaPlayerService.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'media/libmediaplayerservice/MediaPlayerService.cpp')
-rw-r--r--media/libmediaplayerservice/MediaPlayerService.cpp360
1 files changed, 276 insertions, 84 deletions
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index 49032e7..6a6afa1 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -199,10 +199,14 @@ extmap FILE_EXTS [] = {
{".ota", SONIVOX_PLAYER},
{".ogg", VORBIS_PLAYER},
{".oga", VORBIS_PLAYER},
+#ifndef NO_OPENCORE
+ {".wma", PV_PLAYER},
+ {".wmv", PV_PLAYER},
+ {".asf", PV_PLAYER},
+#endif
};
// TODO: Find real cause of Audio/Video delay in PV framework and remove this workaround
-/* static */ const uint32_t MediaPlayerService::AudioOutput::kAudioVideoDelayMs = 0;
/* static */ int MediaPlayerService::AudioOutput::mMinBufferCount = 4;
/* static */ bool MediaPlayerService::AudioOutput::mIsOnEmulator = false;
@@ -250,12 +254,14 @@ sp<IMediaMetadataRetriever> MediaPlayerService::createMetadataRetriever(pid_t pi
return retriever;
}
-sp<IMediaPlayer> MediaPlayerService::create(pid_t pid, const sp<IMediaPlayerClient>& client, const char* url)
+sp<IMediaPlayer> MediaPlayerService::create(
+ pid_t pid, const sp<IMediaPlayerClient>& client, const char* url,
+ const KeyedVector<String8, String8> *headers)
{
int32_t connId = android_atomic_inc(&mNextConnId);
sp<Client> c = new Client(this, pid, connId, client);
LOGV("Create new client(%d) from pid %d, url=%s, connId=%d", connId, pid, url, connId);
- if (NO_ERROR != c->setDataSource(url))
+ if (NO_ERROR != c->setDataSource(url, headers))
{
c.clear();
return c;
@@ -366,11 +372,44 @@ extern "C" void get_malloc_leak_info(uint8_t** info, size_t* overallSize,
size_t* infoSize, size_t* totalMemory, size_t* backtraceSize);
extern "C" void free_malloc_leak_info(uint8_t* info);
+// Use the String-class below instead of String8 to allocate all memory
+// beforehand and not reenter the heap while we are examining it...
+struct MyString8 {
+ static const size_t MAX_SIZE = 256 * 1024;
+
+ MyString8()
+ : mPtr((char *)malloc(MAX_SIZE)) {
+ *mPtr = '\0';
+ }
+
+ ~MyString8() {
+ free(mPtr);
+ }
+
+ void append(const char *s) {
+ strcat(mPtr, s);
+ }
+
+ const char *string() const {
+ return mPtr;
+ }
+
+ size_t size() const {
+ return strlen(mPtr);
+ }
+
+private:
+ char *mPtr;
+
+ MyString8(const MyString8 &);
+ MyString8 &operator=(const MyString8 &);
+};
+
void memStatus(int fd, const Vector<String16>& args)
{
const size_t SIZE = 256;
char buffer[SIZE];
- String8 result;
+ MyString8 result;
typedef struct {
size_t size;
@@ -391,6 +430,8 @@ void memStatus(int fd, const Vector<String16>& args)
snprintf(buffer, SIZE, " Allocation count %i\n", count);
result.append(buffer);
+ snprintf(buffer, SIZE, " Total memory %i\n", totalMemory);
+ result.append(buffer);
AllocEntry * entries = new AllocEntry[count];
@@ -439,7 +480,7 @@ void memStatus(int fd, const Vector<String16>& args)
for (size_t i = 0; i < count; i++) {
AllocEntry *e = &entries[i];
- snprintf(buffer, SIZE, "size %8i, dup %4i", e->size, e->dups);
+ snprintf(buffer, SIZE, "size %8i, dup %4i, ", e->size, e->dups);
result.append(buffer);
for (size_t ct = 0; (ct < backtraceSize) && e->backtrace[ct]; ct++) {
if (ct) {
@@ -637,6 +678,26 @@ static player_type getDefaultPlayerType() {
return PV_PLAYER;
}
+// By default we use the VORBIS_PLAYER for vorbis playback (duh!),
+// but if the magic property is set we will use our new experimental
+// stagefright code instead.
+static player_type OverrideStagefrightForVorbis(player_type player) {
+ if (player != VORBIS_PLAYER) {
+ return player;
+ }
+
+#if BUILD_WITH_FULL_STAGEFRIGHT
+ char value[PROPERTY_VALUE_MAX];
+ if (property_get("media.stagefright.enable-vorbis", value, NULL)
+ && (!strcmp(value, "1") || !strcmp(value, "true"))) {
+ return STAGEFRIGHT_PLAYER;
+ }
+#endif
+
+ return VORBIS_PLAYER;
+}
+
+
player_type getPlayerType(int fd, int64_t offset, int64_t length)
{
char buf[20];
@@ -648,7 +709,15 @@ player_type getPlayerType(int fd, int64_t offset, int64_t length)
// Ogg vorbis?
if (ident == 0x5367674f) // 'OggS'
- return VORBIS_PLAYER;
+ return OverrideStagefrightForVorbis(VORBIS_PLAYER);
+
+#ifndef NO_OPENCORE
+ if (ident == 0x75b22630) {
+ // The magic number for .asf files, i.e. wmv and wma content.
+ // These are not currently supported through stagefright.
+ return PV_PLAYER;
+ }
+#endif
// Some kind of MIDI?
EAS_DATA_HANDLE easdata;
@@ -676,6 +745,13 @@ player_type getPlayerType(const char* url)
return TEST_PLAYER;
}
+ bool useStagefrightForHTTP = false;
+ char value[PROPERTY_VALUE_MAX];
+ if (property_get("media.stagefright.enable-http", value, NULL)
+ && (!strcmp(value, "1") || !strcasecmp(value, "true"))) {
+ useStagefrightForHTTP = true;
+ }
+
// use MidiFile for MIDI extensions
int lenURL = strlen(url);
for (int i = 0; i < NELEM(FILE_EXTS); ++i) {
@@ -683,11 +759,27 @@ player_type getPlayerType(const char* url)
int start = lenURL - len;
if (start > 0) {
if (!strncasecmp(url + start, FILE_EXTS[i].extension, len)) {
- return FILE_EXTS[i].playertype;
+ if (FILE_EXTS[i].playertype == VORBIS_PLAYER
+ && !strncasecmp(url, "http://", 7)
+ && useStagefrightForHTTP) {
+ return STAGEFRIGHT_PLAYER;
+ }
+ return OverrideStagefrightForVorbis(FILE_EXTS[i].playertype);
}
}
}
+ if (!strncasecmp(url, "http://", 7)) {
+ if (!useStagefrightForHTTP) {
+ return PV_PLAYER;
+ }
+ }
+
+ // Use PV_PLAYER for rtsp for now
+ if (!strncasecmp(url, "rtsp://", 7)) {
+ return PV_PLAYER;
+ }
+
return getDefaultPlayerType();
}
@@ -748,7 +840,8 @@ sp<MediaPlayerBase> MediaPlayerService::Client::createPlayer(player_type playerT
return p;
}
-status_t MediaPlayerService::Client::setDataSource(const char *url)
+status_t MediaPlayerService::Client::setDataSource(
+ const char *url, const KeyedVector<String8, String8> *headers)
{
LOGV("setDataSource(%s)", url);
if (url == NULL)
@@ -783,7 +876,7 @@ status_t MediaPlayerService::Client::setDataSource(const char *url)
// now set data source
LOGV(" setDataSource");
- mStatus = p->setDataSource(url);
+ mStatus = p->setDataSource(url, headers);
if (mStatus == NO_ERROR) {
mPlayer = p;
} else {
@@ -912,6 +1005,20 @@ status_t MediaPlayerService::Client::getMetadata(
return OK;
}
+status_t MediaPlayerService::Client::suspend() {
+ sp<MediaPlayerBase> p = getPlayer();
+ if (p == 0) return UNKNOWN_ERROR;
+
+ return p->suspend();
+}
+
+status_t MediaPlayerService::Client::resume() {
+ sp<MediaPlayerBase> p = getPlayer();
+ if (p == 0) return UNKNOWN_ERROR;
+
+ return p->resume();
+}
+
status_t MediaPlayerService::Client::prepareAsync()
{
LOGV("[%d] prepareAsync", mConnId);
@@ -1216,22 +1323,24 @@ Exit:
static const int NUMVIZBUF = 32;
static const int VIZBUFFRAMES = 1024;
-static const int TOTALBUFTIMEMSEC = NUMVIZBUF * VIZBUFFRAMES * 1000 / 44100;
+static const int BUFTIMEMSEC = NUMVIZBUF * VIZBUFFRAMES * 1000 / 44100;
+static const int TOTALBUFTIMEMSEC = NUMVIZBUF * BUFTIMEMSEC;
static bool gotMem = false;
+static sp<MemoryHeapBase> heap;
static sp<MemoryBase> mem[NUMVIZBUF];
-static uint64_t timeStamp[NUMVIZBUF];
+static uint64_t endTime;
static uint64_t lastReadTime;
static uint64_t lastWriteTime;
static int writeIdx = 0;
static void allocVizBufs() {
if (!gotMem) {
+ heap = new MemoryHeapBase(NUMVIZBUF * VIZBUFFRAMES * 2, 0, "snooper");
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;
+ mem[i] = new MemoryBase(heap, VIZBUFFRAMES * 2 * i, VIZBUFFRAMES * 2);
}
+ endTime = 0;
gotMem = true;
}
}
@@ -1248,68 +1357,48 @@ static sp<MemoryBase> getVizBuffer() {
allocVizBufs();
- lastReadTime = uptimeMillis() + 100; // account for renderer delay (we shouldn't be doing this here)
+ lastReadTime = uptimeMillis();
// if there is no recent buffer (yet), just return empty handed
if (lastWriteTime + TOTALBUFTIMEMSEC < lastReadTime) {
- //LOGI("@@@@ no audio data to look at yet");
+ //LOGI("@@@@ no audio data to look at yet: %d + %d < %d", (int)lastWriteTime, TOTALBUFTIMEMSEC, (int)lastReadTime);
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;
+ int timedelta = endTime - lastReadTime;
+ if (timedelta < 0) timedelta = 0;
+ int framedelta = timedelta * 44100 / 1000;
+ int headIdx = (writeIdx - framedelta) / VIZBUFFRAMES - 1;
+ while (headIdx < 0) {
+ headIdx += NUMVIZBUF;
}
+ return mem[headIdx];
}
+// Append the data to the vizualization buffer
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;
+ int offset = writeIdx;
+ int maxoff = heap->getSize() / 2; // in shorts
+ short *base = (short*)heap->getBase();
+ short *src = (short*)data;
+ while (len > 0) {
+
+ // Degrade quality by mixing to mono and clearing the lowest 3 bits.
+ // This should still be good enough for a visualization
+ base[offset++] = ((int(src[0]) + int(src[1])) >> 1) & ~0x7;
+ src += 2;
+ len -= frameSize;
+ if (offset >= maxoff) {
+ offset = 0;
+ }
}
+ writeIdx = offset;
+ endTime = time + (len / frameSize) / 44;
//LOGI("@@@ stored buffers from %d to %d", uint32_t(startTime), uint32_t(time));
}
@@ -1395,6 +1484,12 @@ float MediaPlayerService::AudioOutput::msecsPerFrame() const
return mMsecsPerFrame;
}
+status_t MediaPlayerService::AudioOutput::getPosition(uint32_t *position)
+{
+ if (mTrack == 0) return NO_INIT;
+ return mTrack->getPosition(position);
+}
+
status_t MediaPlayerService::AudioOutput::open(
uint32_t sampleRate, int channelCount, int format, int bufferCount,
AudioCallback cb, void *cookie)
@@ -1452,7 +1547,7 @@ status_t MediaPlayerService::AudioOutput::open(
LOGV("setVolume");
t->setVolume(mLeftVolume, mRightVolume);
mMsecsPerFrame = 1.e3 / (float) sampleRate;
- mLatency = t->latency() + kAudioVideoDelayMs;
+ mLatency = t->latency();
mTrack = t;
return NO_ERROR;
}
@@ -1467,30 +1562,35 @@ void MediaPlayerService::AudioOutput::start()
}
}
+void MediaPlayerService::AudioOutput::snoopWrite(const void* buffer, size_t size) {
+ // 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 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) {
- // 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;
- }
+ snoopWrite(buffer, size);
ssize_t ret = mTrack->write(buffer, size);
mNumFramesWritten += ret / 4; // assume 16 bit stereo
return ret;
@@ -1538,6 +1638,7 @@ void MediaPlayerService::AudioOutput::setVolume(float left, float right)
// static
void MediaPlayerService::AudioOutput::CallbackWrapper(
int event, void *cookie, void *info) {
+ //LOGV("callbackwrapper");
if (event != AudioTrack::EVENT_MORE_DATA) {
return;
}
@@ -1545,8 +1646,14 @@ void MediaPlayerService::AudioOutput::CallbackWrapper(
AudioOutput *me = (AudioOutput *)cookie;
AudioTrack::Buffer *buffer = (AudioTrack::Buffer *)info;
- (*me->mCallback)(
+ size_t actualSize = (*me->mCallback)(
me, buffer->raw, buffer->size, me->mCallbackCookie);
+
+ buffer->size = actualSize;
+
+ if (actualSize > 0) {
+ me->snoopWrite(buffer->raw, actualSize);
+ }
}
#undef LOG_TAG
@@ -1569,14 +1676,82 @@ float MediaPlayerService::AudioCache::msecsPerFrame() const
return mMsecsPerFrame;
}
+status_t MediaPlayerService::AudioCache::getPosition(uint32_t *position)
+{
+ if (position == 0) return BAD_VALUE;
+ *position = mSize;
+ return NO_ERROR;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+struct CallbackThread : public Thread {
+ CallbackThread(const wp<MediaPlayerBase::AudioSink> &sink,
+ MediaPlayerBase::AudioSink::AudioCallback cb,
+ void *cookie);
+
+protected:
+ virtual ~CallbackThread();
+
+ virtual bool threadLoop();
+
+private:
+ wp<MediaPlayerBase::AudioSink> mSink;
+ MediaPlayerBase::AudioSink::AudioCallback mCallback;
+ void *mCookie;
+ void *mBuffer;
+ size_t mBufferSize;
+
+ CallbackThread(const CallbackThread &);
+ CallbackThread &operator=(const CallbackThread &);
+};
+
+CallbackThread::CallbackThread(
+ const wp<MediaPlayerBase::AudioSink> &sink,
+ MediaPlayerBase::AudioSink::AudioCallback cb,
+ void *cookie)
+ : mSink(sink),
+ mCallback(cb),
+ mCookie(cookie),
+ mBuffer(NULL),
+ mBufferSize(0) {
+}
+
+CallbackThread::~CallbackThread() {
+ if (mBuffer) {
+ free(mBuffer);
+ mBuffer = NULL;
+ }
+}
+
+bool CallbackThread::threadLoop() {
+ sp<MediaPlayerBase::AudioSink> sink = mSink.promote();
+ if (sink == NULL) {
+ return false;
+ }
+
+ if (mBuffer == NULL) {
+ mBufferSize = sink->bufferSize();
+ mBuffer = malloc(mBufferSize);
+ }
+
+ size_t actualSize =
+ (*mCallback)(sink.get(), mBuffer, mBufferSize, mCookie);
+
+ if (actualSize > 0) {
+ sink->write(mBuffer, actualSize);
+ }
+
+ return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
status_t MediaPlayerService::AudioCache::open(
uint32_t sampleRate, int channelCount, int format, int bufferCount,
AudioCallback cb, void *cookie)
{
LOGV("open(%u, %d, %d, %d)", sampleRate, channelCount, format, bufferCount);
- if (cb != NULL) {
- return UNKNOWN_ERROR; // TODO: implement this.
- }
if (mHeap->getHeapID() < 0) {
return NO_INIT;
}
@@ -1585,9 +1760,25 @@ status_t MediaPlayerService::AudioCache::open(
mChannelCount = (uint16_t)channelCount;
mFormat = (uint16_t)format;
mMsecsPerFrame = 1.e3 / (float) sampleRate;
+
+ if (cb != NULL) {
+ mCallbackThread = new CallbackThread(this, cb, cookie);
+ }
return NO_ERROR;
}
+void MediaPlayerService::AudioCache::start() {
+ if (mCallbackThread != NULL) {
+ mCallbackThread->run("AudioCache callback");
+ }
+}
+
+void MediaPlayerService::AudioCache::stop() {
+ if (mCallbackThread != NULL) {
+ mCallbackThread->requestExitAndWait();
+ }
+}
+
ssize_t MediaPlayerService::AudioCache::write(const void* buffer, size_t size)
{
LOGV("write(%p, %u)", buffer, size);
@@ -1610,7 +1801,7 @@ ssize_t MediaPlayerService::AudioCache::write(const void* buffer, size_t size)
status_t MediaPlayerService::AudioCache::wait()
{
Mutex::Autolock lock(mLock);
- if (!mCommandComplete) {
+ while (!mCommandComplete) {
mSignal.wait(mLock);
}
mCommandComplete = false;
@@ -1647,6 +1838,7 @@ void MediaPlayerService::AudioCache::notify(void* cookie, int msg, int ext1, int
}
// wake up thread
+ Mutex::Autolock lock(p->mLock);
p->mCommandComplete = true;
p->mSignal.signal();
}