Reconcile with ics-factoryrom-release
Change-Id: I4096cbbfd0bae1f7c08c9731f522529d3895d2d8
diff --git a/cmds/stagefright/sf2.cpp b/cmds/stagefright/sf2.cpp
index 6fa66cf..263ecd1 100644
--- a/cmds/stagefright/sf2.cpp
+++ b/cmds/stagefright/sf2.cpp
@@ -569,12 +569,16 @@
CHECK(control->isValid());
SurfaceComposerClient::openGlobalTransaction();
- CHECK_EQ(control->setLayer(30000), (status_t)OK);
+ CHECK_EQ(control->setLayer(INT_MAX), (status_t)OK);
CHECK_EQ(control->show(), (status_t)OK);
SurfaceComposerClient::closeGlobalTransaction();
surface = control->getSurface();
CHECK(surface != NULL);
+
+ CHECK_EQ((status_t)OK,
+ native_window_api_connect(
+ surface.get(), NATIVE_WINDOW_API_MEDIA));
}
sp<Controller> controller =
@@ -589,6 +593,10 @@
looper->unregisterHandler(controller->id());
if (!decodeAudio && useSurface) {
+ CHECK_EQ((status_t)OK,
+ native_window_api_disconnect(
+ surface.get(), NATIVE_WINDOW_API_MEDIA));
+
composerClient->dispose();
}
diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp
index 34f0a64..528d197 100644
--- a/cmds/stagefright/stagefright.cpp
+++ b/cmds/stagefright/stagefright.cpp
@@ -133,6 +133,39 @@
}
}
+static void dumpSource(const sp<MediaSource> &source, const String8 &filename) {
+ FILE *out = fopen(filename.string(), "wb");
+
+ CHECK_EQ((status_t)OK, source->start());
+
+ status_t err;
+ for (;;) {
+ MediaBuffer *mbuf;
+ err = source->read(&mbuf);
+
+ if (err == INFO_FORMAT_CHANGED) {
+ continue;
+ } else if (err != OK) {
+ break;
+ }
+
+ CHECK_EQ(
+ fwrite((const uint8_t *)mbuf->data() + mbuf->range_offset(),
+ 1,
+ mbuf->range_length(),
+ out),
+ (ssize_t)mbuf->range_length());
+
+ mbuf->release();
+ mbuf = NULL;
+ }
+
+ CHECK_EQ((status_t)OK, source->stop());
+
+ fclose(out);
+ out = NULL;
+}
+
static void playSource(OMXClient *client, sp<MediaSource> &source) {
sp<MetaData> meta = source->getFormat();
@@ -578,6 +611,7 @@
"(video only)\n");
fprintf(stderr, " -S allocate buffers from a surface\n");
fprintf(stderr, " -T allocate buffers from a surface texture\n");
+ fprintf(stderr, " -d(ump) filename (raw stream data to a file)\n");
}
int main(int argc, char **argv) {
@@ -590,6 +624,8 @@
bool seekTest = false;
bool useSurfaceAlloc = false;
bool useSurfaceTexAlloc = false;
+ bool dumpStream = false;
+ String8 dumpStreamFilename;
gNumRepetitions = 1;
gMaxNumFrames = 0;
gReproduceBug = -1;
@@ -604,7 +640,7 @@
sp<LiveSession> liveSession;
int res;
- while ((res = getopt(argc, argv, "han:lm:b:ptsrow:kxST")) >= 0) {
+ while ((res = getopt(argc, argv, "han:lm:b:ptsrow:kxSTd:")) >= 0) {
switch (res) {
case 'a':
{
@@ -612,6 +648,13 @@
break;
}
+ case 'd':
+ {
+ dumpStream = true;
+ dumpStreamFilename.setTo(optarg);
+ break;
+ }
+
case 'l':
{
listComponents = true;
@@ -874,7 +917,7 @@
CHECK(control->isValid());
SurfaceComposerClient::openGlobalTransaction();
- CHECK_EQ(control->setLayer(30000), (status_t)OK);
+ CHECK_EQ(control->setLayer(INT_MAX), (status_t)OK);
CHECK_EQ(control->show(), (status_t)OK);
SurfaceComposerClient::closeGlobalTransaction();
@@ -886,6 +929,10 @@
sp<SurfaceTexture> texture = new SurfaceTexture(0 /* tex */);
gSurface = new SurfaceTextureClient(texture);
}
+
+ CHECK_EQ((status_t)OK,
+ native_window_api_connect(
+ gSurface.get(), NATIVE_WINDOW_API_MEDIA));
}
DataSource::RegisterDefaultSniffers();
@@ -1062,6 +1109,8 @@
if (gWriteMP4) {
writeSourcesToMP4(mediaSources, syncInfoPresent);
+ } else if (dumpStream) {
+ dumpSource(mediaSource, dumpStreamFilename);
} else if (seekTest) {
performSeekTest(mediaSource);
} else {
@@ -1077,6 +1126,10 @@
}
if ((useSurfaceAlloc || useSurfaceTexAlloc) && !audioOnly) {
+ CHECK_EQ((status_t)OK,
+ native_window_api_disconnect(
+ gSurface.get(), NATIVE_WINDOW_API_MEDIA));
+
gSurface.clear();
if (useSurfaceAlloc) {
diff --git a/cmds/stagefright/stream.cpp b/cmds/stagefright/stream.cpp
index b13236a..2378345 100644
--- a/cmds/stagefright/stream.cpp
+++ b/cmds/stagefright/stream.cpp
@@ -323,7 +323,7 @@
CHECK(control->isValid());
SurfaceComposerClient::openGlobalTransaction();
- CHECK_EQ(control->setLayer(30000), (status_t)OK);
+ CHECK_EQ(control->setLayer(INT_MAX), (status_t)OK);
CHECK_EQ(control->show(), (status_t)OK);
SurfaceComposerClient::closeGlobalTransaction();
diff --git a/drm/drmserver/DrmManager.cpp b/drm/drmserver/DrmManager.cpp
index 3e4fe8c..b2fa053 100644
--- a/drm/drmserver/DrmManager.cpp
+++ b/drm/drmserver/DrmManager.cpp
@@ -98,21 +98,27 @@
}
status_t DrmManager::loadPlugIns() {
+
+ String8 vendorPluginDirPath("/vendor/lib/drm");
+ loadPlugIns(vendorPluginDirPath);
+
String8 pluginDirPath("/system/lib/drm");
- return loadPlugIns(pluginDirPath);
+ loadPlugIns(pluginDirPath);
+ return DRM_NO_ERROR;
+
}
status_t DrmManager::loadPlugIns(const String8& plugInDirPath) {
- if (mSupportInfoToPlugInIdMap.isEmpty()) {
- mPlugInManager.loadPlugIns(plugInDirPath);
- Vector<String8> plugInPathList = mPlugInManager.getPlugInIdList();
- for (unsigned int i = 0; i < plugInPathList.size(); ++i) {
- String8 plugInPath = plugInPathList[i];
- DrmSupportInfo* info = mPlugInManager.getPlugIn(plugInPath).getSupportInfo(0);
- if (NULL != info) {
+ mPlugInManager.loadPlugIns(plugInDirPath);
+ Vector<String8> plugInPathList = mPlugInManager.getPlugInIdList();
+ for (unsigned int i = 0; i < plugInPathList.size(); ++i) {
+ String8 plugInPath = plugInPathList[i];
+ DrmSupportInfo* info = mPlugInManager.getPlugIn(plugInPath).getSupportInfo(0);
+ if (NULL != info) {
+ if (mSupportInfoToPlugInIdMap.indexOfKey(*info) < 0) {
mSupportInfoToPlugInIdMap.add(*info, plugInPath);
- delete info;
}
+ delete info;
}
}
return DRM_NO_ERROR;
diff --git a/include/media/stagefright/ACodec.h b/include/media/stagefright/ACodec.h
index b7286e5..9da9907 100644
--- a/include/media/stagefright/ACodec.h
+++ b/include/media/stagefright/ACodec.h
@@ -113,6 +113,7 @@
Vector<BufferInfo> mBuffers[2];
bool mPortEOS[2];
+ status_t mInputEOSResult;
List<sp<AMessage> > mDeferredQueue;
diff --git a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp
index 605d056..fb14204 100644
--- a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp
@@ -110,7 +110,11 @@
if (n == -EWOULDBLOCK) {
break;
} else if (n < 0) {
- LOGI("input data EOS reached.");
+ if (n != ERROR_END_OF_STREAM) {
+ LOGI("input data EOS reached, error %d", n);
+ } else {
+ LOGI("input data EOS reached.");
+ }
mTSParser->signalEOS(n);
mEOS = true;
break;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index ee77f47..7bfd358 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -34,17 +34,21 @@
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/ACodec.h>
+#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MetaData.h>
#include <surfaceflinger/Surface.h>
#include <gui/ISurfaceTexture.h>
+#include "avc_utils.h"
+
namespace android {
////////////////////////////////////////////////////////////////////////////////
NuPlayer::NuPlayer()
: mUIDValid(false),
+ mVideoIsAVC(false),
mAudioEOS(false),
mVideoEOS(false),
mScanSourcesPending(false),
@@ -52,7 +56,12 @@
mFlushingAudio(NONE),
mFlushingVideo(NONE),
mResetInProgress(false),
- mResetPostponed(false) {
+ mResetPostponed(false),
+ mSkipRenderingAudioUntilMediaTimeUs(-1ll),
+ mSkipRenderingVideoUntilMediaTimeUs(-1ll),
+ mVideoLateByUs(0ll),
+ mNumFramesTotal(0ll),
+ mNumFramesDropped(0ll) {
}
NuPlayer::~NuPlayer() {
@@ -185,10 +194,14 @@
{
LOGV("kWhatStart");
+ mVideoIsAVC = false;
mAudioEOS = false;
mVideoEOS = false;
mSkipRenderingAudioUntilMediaTimeUs = -1;
mSkipRenderingVideoUntilMediaTimeUs = -1;
+ mVideoLateByUs = 0;
+ mNumFramesTotal = 0;
+ mNumFramesDropped = 0;
mSource->start();
@@ -259,7 +272,18 @@
}
}
} else if (what == ACodec::kWhatEOS) {
- mRenderer->queueEOS(audio, ERROR_END_OF_STREAM);
+ int32_t err;
+ CHECK(codecRequest->findInt32("err", &err));
+
+ if (err == ERROR_END_OF_STREAM) {
+ LOGV("got %s decoder EOS", audio ? "audio" : "video");
+ } else {
+ LOGV("got %s decoder EOS w/ error %d",
+ audio ? "audio" : "video",
+ err);
+ }
+
+ mRenderer->queueEOS(audio, err);
} else if (what == ACodec::kWhatFlushCompleted) {
bool needShutdown;
@@ -269,6 +293,8 @@
} else {
CHECK(IsFlushingState(mFlushingVideo, &needShutdown));
mFlushingVideo = FLUSHED;
+
+ mVideoLateByUs = 0;
}
LOGV("decoder %s flush completed", audio ? "audio" : "video");
@@ -299,7 +325,12 @@
sampleRate, numChannels);
mAudioSink->close();
- CHECK_EQ(mAudioSink->open(sampleRate, numChannels), (status_t)OK);
+ CHECK_EQ(mAudioSink->open(
+ sampleRate,
+ numChannels,
+ AUDIO_FORMAT_PCM_16_BIT,
+ 8 /* bufferCount */),
+ (status_t)OK);
mAudioSink->start();
mRenderer->signalAudioSinkChanged();
@@ -377,7 +408,7 @@
if (finalResult == ERROR_END_OF_STREAM) {
LOGV("reached %s EOS", audio ? "audio" : "video");
} else {
- LOGE("%s track encountered an error (0x%08x)",
+ LOGE("%s track encountered an error (%d)",
audio ? "audio" : "video", finalResult);
notifyListener(
@@ -392,13 +423,18 @@
int64_t positionUs;
CHECK(msg->findInt64("positionUs", &positionUs));
+ CHECK(msg->findInt64("videoLateByUs", &mVideoLateByUs));
+
if (mDriver != NULL) {
sp<NuPlayerDriver> driver = mDriver.promote();
if (driver != NULL) {
driver->notifyPosition(positionUs);
+
+ driver->notifyFrameStats(
+ mNumFramesTotal, mNumFramesDropped);
}
}
- } else {
+ } else if (what == Renderer::kWhatFlushComplete) {
CHECK_EQ(what, (int32_t)Renderer::kWhatFlushComplete);
int32_t audio;
@@ -560,6 +596,12 @@
return -EWOULDBLOCK;
}
+ if (!audio) {
+ const char *mime;
+ CHECK(meta->findCString(kKeyMIMEType, &mime));
+ mVideoIsAVC = !strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime);
+ }
+
sp<AMessage> notify =
new AMessage(audio ? kWhatAudioNotify : kWhatVideoNotify,
id());
@@ -593,53 +635,70 @@
}
sp<ABuffer> accessUnit;
- status_t err = mSource->dequeueAccessUnit(audio, &accessUnit);
- if (err == -EWOULDBLOCK) {
- return err;
- } else if (err != OK) {
- if (err == INFO_DISCONTINUITY) {
- int32_t type;
- CHECK(accessUnit->meta()->findInt32("discontinuity", &type));
+ bool dropAccessUnit;
+ do {
+ status_t err = mSource->dequeueAccessUnit(audio, &accessUnit);
- bool formatChange =
- type == ATSParser::DISCONTINUITY_FORMATCHANGE;
+ if (err == -EWOULDBLOCK) {
+ return err;
+ } else if (err != OK) {
+ if (err == INFO_DISCONTINUITY) {
+ int32_t type;
+ CHECK(accessUnit->meta()->findInt32("discontinuity", &type));
- LOGV("%s discontinuity (formatChange=%d)",
- audio ? "audio" : "video", formatChange);
+ bool formatChange =
+ type == ATSParser::DISCONTINUITY_FORMATCHANGE;
- if (audio) {
- mSkipRenderingAudioUntilMediaTimeUs = -1;
- } else {
- mSkipRenderingVideoUntilMediaTimeUs = -1;
- }
+ LOGV("%s discontinuity (formatChange=%d)",
+ audio ? "audio" : "video", formatChange);
- sp<AMessage> extra;
- if (accessUnit->meta()->findMessage("extra", &extra)
- && extra != NULL) {
- int64_t resumeAtMediaTimeUs;
- if (extra->findInt64(
- "resume-at-mediatimeUs", &resumeAtMediaTimeUs)) {
- LOGI("suppressing rendering of %s until %lld us",
- audio ? "audio" : "video", resumeAtMediaTimeUs);
+ if (audio) {
+ mSkipRenderingAudioUntilMediaTimeUs = -1;
+ } else {
+ mSkipRenderingVideoUntilMediaTimeUs = -1;
+ }
- if (audio) {
- mSkipRenderingAudioUntilMediaTimeUs =
- resumeAtMediaTimeUs;
- } else {
- mSkipRenderingVideoUntilMediaTimeUs =
- resumeAtMediaTimeUs;
+ sp<AMessage> extra;
+ if (accessUnit->meta()->findMessage("extra", &extra)
+ && extra != NULL) {
+ int64_t resumeAtMediaTimeUs;
+ if (extra->findInt64(
+ "resume-at-mediatimeUs", &resumeAtMediaTimeUs)) {
+ LOGI("suppressing rendering of %s until %lld us",
+ audio ? "audio" : "video", resumeAtMediaTimeUs);
+
+ if (audio) {
+ mSkipRenderingAudioUntilMediaTimeUs =
+ resumeAtMediaTimeUs;
+ } else {
+ mSkipRenderingVideoUntilMediaTimeUs =
+ resumeAtMediaTimeUs;
+ }
}
}
+
+ flushDecoder(audio, formatChange);
}
- flushDecoder(audio, formatChange);
+ reply->setInt32("err", err);
+ reply->post();
+ return OK;
}
- reply->setInt32("err", err);
- reply->post();
- return OK;
- }
+ if (!audio) {
+ ++mNumFramesTotal;
+ }
+
+ dropAccessUnit = false;
+ if (!audio
+ && mVideoLateByUs > 100000ll
+ && mVideoIsAVC
+ && !IsAVCReferenceFrame(accessUnit)) {
+ dropAccessUnit = true;
+ ++mNumFramesDropped;
+ }
+ } while (dropAccessUnit);
// LOGV("returned a valid buffer of %s data", audio ? "audio" : "video");
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.h b/media/libmediaplayerservice/nuplayer/NuPlayer.h
index cf9185b..a5382b4 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.h
@@ -70,19 +70,19 @@
struct StreamingSource;
enum {
- kWhatSetDataSource,
- kWhatSetVideoNativeWindow,
- kWhatSetAudioSink,
- kWhatMoreDataQueued,
- kWhatStart,
- kWhatScanSources,
- kWhatVideoNotify,
- kWhatAudioNotify,
- kWhatRendererNotify,
- kWhatReset,
- kWhatSeek,
- kWhatPause,
- kWhatResume,
+ kWhatSetDataSource = '=DaS',
+ kWhatSetVideoNativeWindow = '=NaW',
+ kWhatSetAudioSink = '=AuS',
+ kWhatMoreDataQueued = 'more',
+ kWhatStart = 'strt',
+ kWhatScanSources = 'scan',
+ kWhatVideoNotify = 'vidN',
+ kWhatAudioNotify = 'audN',
+ kWhatRendererNotify = 'renN',
+ kWhatReset = 'rset',
+ kWhatSeek = 'seek',
+ kWhatPause = 'paus',
+ kWhatResume = 'rsme',
};
wp<NuPlayerDriver> mDriver;
@@ -92,6 +92,7 @@
sp<NativeWindowWrapper> mNativeWindow;
sp<MediaPlayerBase::AudioSink> mAudioSink;
sp<Decoder> mVideoDecoder;
+ bool mVideoIsAVC;
sp<Decoder> mAudioDecoder;
sp<Renderer> mRenderer;
@@ -119,6 +120,9 @@
int64_t mSkipRenderingAudioUntilMediaTimeUs;
int64_t mSkipRenderingVideoUntilMediaTimeUs;
+ int64_t mVideoLateByUs;
+ int64_t mNumFramesTotal, mNumFramesDropped;
+
status_t instantiateDecoder(bool audio, sp<Decoder> *decoder);
status_t feedDecoderInputData(bool audio, const sp<AMessage> &msg);
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
index 81b41ef..56c2773 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
@@ -59,8 +59,21 @@
format->setObject("native-window", mNativeWindow);
}
+ // Current video decoders do not return from OMX_FillThisBuffer
+ // quickly, violating the OpenMAX specs, until that is remedied
+ // we need to invest in an extra looper to free the main event
+ // queue.
+ bool needDedicatedLooper = !strncasecmp(mime, "video/", 6);
+
mCodec = new ACodec;
- looper()->registerHandler(mCodec);
+
+ if (needDedicatedLooper && mCodecLooper == NULL) {
+ mCodecLooper = new ALooper;
+ mCodecLooper->setName("NuPlayerDecoder");
+ mCodecLooper->start(false, false, ANDROID_PRIORITY_AUDIO);
+ }
+
+ (needDedicatedLooper ? mCodecLooper : looper())->registerHandler(mCodec);
mCodec->setNotificationMessage(notifyMsg);
mCodec->initiateSetup(format);
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
index fabc606..3ab1fcf 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
@@ -43,13 +43,14 @@
private:
enum {
- kWhatCodecNotify,
+ kWhatCodecNotify = 'cdcN',
};
sp<AMessage> mNotify;
sp<NativeWindowWrapper> mNativeWindow;
sp<ACodec> mCodec;
+ sp<ALooper> mCodecLooper;
Vector<sp<ABuffer> > mCSD;
size_t mCSDIndex;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
index c6fca2c..b1e917d 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
@@ -31,6 +31,8 @@
: mResetInProgress(false),
mDurationUs(-1),
mPositionUs(-1),
+ mNumFramesTotal(0),
+ mNumFramesDropped(0),
mLooper(new ALooper),
mState(UNINITIALIZED),
mStartupSeekTimeUs(-1) {
@@ -292,4 +294,30 @@
sendEvent(MEDIA_SEEK_COMPLETE);
}
+void NuPlayerDriver::notifyFrameStats(
+ int64_t numFramesTotal, int64_t numFramesDropped) {
+ Mutex::Autolock autoLock(mLock);
+ mNumFramesTotal = numFramesTotal;
+ mNumFramesDropped = numFramesDropped;
+}
+
+status_t NuPlayerDriver::dump(int fd, const Vector<String16> &args) const {
+ Mutex::Autolock autoLock(mLock);
+
+ FILE *out = fdopen(dup(fd), "w");
+
+ fprintf(out, " NuPlayer\n");
+ fprintf(out, " numFramesTotal(%lld), numFramesDropped(%lld), "
+ "percentageDropped(%.2f)\n",
+ mNumFramesTotal,
+ mNumFramesDropped,
+ mNumFramesTotal == 0
+ ? 0.0 : (double)mNumFramesDropped / mNumFramesTotal);
+
+ fclose(out);
+ out = NULL;
+
+ return OK;
+}
+
} // namespace android
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h
index 1bb7ca2..181c37d 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h
@@ -60,16 +60,19 @@
virtual status_t getMetadata(
const media::Metadata::Filter& ids, Parcel *records);
+ virtual status_t dump(int fd, const Vector<String16> &args) const;
+
void notifyResetComplete();
void notifyDuration(int64_t durationUs);
void notifyPosition(int64_t positionUs);
void notifySeekComplete();
+ void notifyFrameStats(int64_t numFramesTotal, int64_t numFramesDropped);
protected:
virtual ~NuPlayerDriver();
private:
- Mutex mLock;
+ mutable Mutex mLock;
Condition mCondition;
// The following are protected through "mLock"
@@ -77,6 +80,8 @@
bool mResetInProgress;
int64_t mDurationUs;
int64_t mPositionUs;
+ int64_t mNumFramesTotal;
+ int64_t mNumFramesDropped;
// <<<
sp<ALooper> mLooper;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
index bf83849..07e347e 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
@@ -47,7 +47,8 @@
mHasVideo(false),
mSyncQueues(false),
mPaused(false),
- mLastPositionUpdateUs(-1ll) {
+ mLastPositionUpdateUs(-1ll),
+ mVideoLateByUs(0ll) {
}
NuPlayer::Renderer::~Renderer() {
@@ -118,9 +119,24 @@
mDrainAudioQueuePending = false;
- onDrainAudioQueue();
+ if (onDrainAudioQueue()) {
+ uint32_t numFramesPlayed;
+ CHECK_EQ(mAudioSink->getPosition(&numFramesPlayed),
+ (status_t)OK);
- postDrainAudioQueue();
+ uint32_t numFramesPendingPlayout =
+ mNumFramesWritten - numFramesPlayed;
+
+ // This is how long the audio sink will have data to
+ // play back.
+ int64_t delayUs =
+ mAudioSink->msecsPerFrame()
+ * numFramesPendingPlayout * 1000ll;
+
+ // Let's give it more data after about half that time
+ // has elapsed.
+ postDrainAudioQueue(delayUs / 2);
+ }
break;
}
@@ -182,7 +198,7 @@
}
}
-void NuPlayer::Renderer::postDrainAudioQueue() {
+void NuPlayer::Renderer::postDrainAudioQueue(int64_t delayUs) {
if (mDrainAudioQueuePending || mSyncQueues || mPaused) {
return;
}
@@ -194,19 +210,33 @@
mDrainAudioQueuePending = true;
sp<AMessage> msg = new AMessage(kWhatDrainAudioQueue, id());
msg->setInt32("generation", mAudioQueueGeneration);
- msg->post();
+ msg->post(delayUs);
}
void NuPlayer::Renderer::signalAudioSinkChanged() {
(new AMessage(kWhatAudioSinkChanged, id()))->post();
}
-void NuPlayer::Renderer::onDrainAudioQueue() {
- for (;;) {
- if (mAudioQueue.empty()) {
- break;
- }
+bool NuPlayer::Renderer::onDrainAudioQueue() {
+ uint32_t numFramesPlayed;
+ CHECK_EQ(mAudioSink->getPosition(&numFramesPlayed), (status_t)OK);
+ ssize_t numFramesAvailableToWrite =
+ mAudioSink->frameCount() - (mNumFramesWritten - numFramesPlayed);
+
+#if 0
+ if (numFramesAvailableToWrite == mAudioSink->frameCount()) {
+ LOGI("audio sink underrun");
+ } else {
+ LOGV("audio queue has %d frames left to play",
+ mAudioSink->frameCount() - numFramesAvailableToWrite);
+ }
+#endif
+
+ size_t numBytesAvailableToWrite =
+ numFramesAvailableToWrite * mAudioSink->frameSize();
+
+ while (numBytesAvailableToWrite > 0 && !mAudioQueue.empty()) {
QueueEntry *entry = &*mAudioQueue.begin();
if (entry->mBuffer == NULL) {
@@ -216,20 +246,7 @@
mAudioQueue.erase(mAudioQueue.begin());
entry = NULL;
- return;
- }
-
- uint32_t numFramesPlayed;
- CHECK_EQ(mAudioSink->getPosition(&numFramesPlayed), (status_t)OK);
-
- ssize_t numFramesAvailableToWrite =
- mAudioSink->frameCount() - (mNumFramesWritten - numFramesPlayed);
-
- size_t numBytesAvailableToWrite =
- numFramesAvailableToWrite * mAudioSink->frameSize();
-
- if (numBytesAvailableToWrite == 0) {
- break;
+ return false;
}
if (entry->mOffset == 0) {
@@ -274,10 +291,14 @@
entry = NULL;
}
- mNumFramesWritten += copy / mAudioSink->frameSize();
+ numBytesAvailableToWrite -= copy;
+ size_t copiedFrames = copy / mAudioSink->frameSize();
+ mNumFramesWritten += copiedFrames;
}
notifyPosition();
+
+ return !mAudioQueue.empty();
}
void NuPlayer::Renderer::postDrainVideoQueue() {
@@ -337,15 +358,26 @@
mVideoQueue.erase(mVideoQueue.begin());
entry = NULL;
+
+ mVideoLateByUs = 0ll;
+
+ notifyPosition();
return;
}
-#if 0
int64_t mediaTimeUs;
CHECK(entry->mBuffer->meta()->findInt64("timeUs", &mediaTimeUs));
- LOGI("rendering video at media time %.2f secs", mediaTimeUs / 1E6);
-#endif
+ int64_t realTimeUs = mediaTimeUs - mAnchorTimeMediaUs + mAnchorTimeRealUs;
+ mVideoLateByUs = ALooper::GetNowUs() - realTimeUs;
+
+ bool tooLate = (mVideoLateByUs > 40000);
+
+ if (tooLate) {
+ LOGV("video late by %lld us (%.2f secs)", lateByUs, lateByUs / 1E6);
+ } else {
+ LOGV("rendering video at media time %.2f secs", mediaTimeUs / 1E6);
+ }
entry->mNotifyConsumed->setInt32("render", true);
entry->mNotifyConsumed->post();
@@ -577,6 +609,7 @@
sp<AMessage> notify = mNotify->dup();
notify->setInt32("what", kWhatPosition);
notify->setInt64("positionUs", positionUs);
+ notify->setInt64("videoLateByUs", mVideoLateByUs);
notify->post();
}
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h
index 3a641a2..268628b 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h
@@ -45,9 +45,9 @@
void resume();
enum {
- kWhatEOS,
- kWhatFlushComplete,
- kWhatPosition,
+ kWhatEOS = 'eos ',
+ kWhatFlushComplete = 'fluC',
+ kWhatPosition = 'posi',
};
protected:
@@ -57,14 +57,14 @@
private:
enum {
- kWhatDrainAudioQueue,
- kWhatDrainVideoQueue,
- kWhatQueueBuffer,
- kWhatQueueEOS,
- kWhatFlush,
- kWhatAudioSinkChanged,
- kWhatPause,
- kWhatResume,
+ kWhatDrainAudioQueue = 'draA',
+ kWhatDrainVideoQueue = 'draV',
+ kWhatQueueBuffer = 'queB',
+ kWhatQueueEOS = 'qEOS',
+ kWhatFlush = 'flus',
+ kWhatAudioSinkChanged = 'auSC',
+ kWhatPause = 'paus',
+ kWhatResume = 'resm',
};
struct QueueEntry {
@@ -101,9 +101,10 @@
bool mPaused;
int64_t mLastPositionUpdateUs;
+ int64_t mVideoLateByUs;
- void onDrainAudioQueue();
- void postDrainAudioQueue();
+ bool onDrainAudioQueue();
+ void postDrainAudioQueue(int64_t delayUs = 0);
void onDrainVideoQueue();
void postDrainVideoQueue();
@@ -118,6 +119,7 @@
void notifyEOS(bool audio, status_t finalResult);
void notifyFlushComplete(bool audio);
void notifyPosition();
+ void notifyVideoLateBy(int64_t lateByUs);
void flushQueue(List<QueueEntry> *queue);
bool dropBufferWhileFlushing(bool audio, const sp<AMessage> &msg);
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.h b/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.h
index df0935d..1874d80 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.h
@@ -41,8 +41,8 @@
private:
enum {
- kNumBuffers = 16,
- kBufferSize = 188 * 20
+ kNumBuffers = 8,
+ kBufferSize = 188 * 10
};
struct QueueEntry {
diff --git a/media/libmediaplayerservice/nuplayer/StreamingSource.cpp b/media/libmediaplayerservice/nuplayer/StreamingSource.cpp
index a741987..7319e4c 100644
--- a/media/libmediaplayerservice/nuplayer/StreamingSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/StreamingSource.cpp
@@ -52,7 +52,7 @@
return false;
}
- for (int32_t i = 0; i < 10; ++i) {
+ for (int32_t i = 0; i < 50; ++i) {
char buffer[188];
sp<AMessage> extra;
ssize_t n = mStreamListener->read(buffer, sizeof(buffer), &extra);
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index 2ba2273..a3746cd 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -323,6 +323,7 @@
mFlushingState = new FlushingState(this);
mPortEOS[kPortIndexInput] = mPortEOS[kPortIndexOutput] = false;
+ mInputEOSResult = OK;
changeState(mUninitializedState);
}
@@ -1347,7 +1348,10 @@
case KEEP_BUFFERS:
{
if (buffer == NULL) {
- mCodec->mPortEOS[kPortIndexInput] = true;
+ if (!mCodec->mPortEOS[kPortIndexInput]) {
+ mCodec->mPortEOS[kPortIndexInput] = true;
+ mCodec->mInputEOSResult = err;
+ }
}
break;
}
@@ -1377,8 +1381,13 @@
memcpy(info->mData->data(), buffer->data(), buffer->size());
}
- LOGV("[%s] calling emptyBuffer %p",
- mCodec->mComponentName.c_str(), bufferID);
+ if (flags & OMX_BUFFERFLAG_CODECCONFIG) {
+ LOGV("[%s] calling emptyBuffer %p w/ codec specific data",
+ mCodec->mComponentName.c_str(), bufferID);
+ } else {
+ LOGV("[%s] calling emptyBuffer %p w/ time %lld us",
+ mCodec->mComponentName.c_str(), bufferID, timeUs);
+ }
CHECK_EQ(mCodec->mOMX->emptyBuffer(
mCodec->mNode,
@@ -1393,10 +1402,16 @@
getMoreInputDataIfPossible();
} else if (!mCodec->mPortEOS[kPortIndexInput]) {
- LOGV("[%s] Signalling EOS on the input port",
- mCodec->mComponentName.c_str());
+ if (err != ERROR_END_OF_STREAM) {
+ LOGV("[%s] Signalling EOS on the input port "
+ "due to error %d",
+ mCodec->mComponentName.c_str(), err);
+ } else {
+ LOGV("[%s] Signalling EOS on the input port",
+ mCodec->mComponentName.c_str());
+ }
- LOGV("[%s] calling emptyBuffer %p",
+ LOGV("[%s] calling emptyBuffer %p signalling EOS",
mCodec->mComponentName.c_str(), bufferID);
CHECK_EQ(mCodec->mOMX->emptyBuffer(
@@ -1411,6 +1426,7 @@
info->mStatus = BufferInfo::OWNED_BY_COMPONENT;
mCodec->mPortEOS[kPortIndexInput] = true;
+ mCodec->mInputEOSResult = err;
}
break;
@@ -1457,8 +1473,8 @@
int64_t timeUs,
void *platformPrivate,
void *dataPtr) {
- LOGV("[%s] onOMXFillBufferDone %p",
- mCodec->mComponentName.c_str(), bufferID);
+ LOGV("[%s] onOMXFillBufferDone %p time %lld us",
+ mCodec->mComponentName.c_str(), bufferID, timeUs);
ssize_t index;
BufferInfo *info =
@@ -1518,6 +1534,7 @@
if (flags & OMX_BUFFERFLAG_EOS) {
sp<AMessage> notify = mCodec->mNotify->dup();
notify->setInt32("what", ACodec::kWhatEOS);
+ notify->setInt32("err", mCodec->mInputEOSResult);
notify->post();
mCodec->mPortEOS[kPortIndexOutput] = true;
@@ -1686,7 +1703,11 @@
++matchIndex) {
componentName = matchingCodecs.itemAt(matchIndex).string();
+ pid_t tid = androidGetTid();
+ int prevPriority = androidGetThreadPriority(tid);
+ androidSetThreadPriority(tid, ANDROID_PRIORITY_FOREGROUND);
status_t err = omx->allocateNode(componentName.c_str(), observer, &node);
+ androidSetThreadPriority(tid, prevPriority);
if (err == OK) {
break;
@@ -1712,6 +1733,8 @@
mCodec->mPortEOS[kPortIndexInput] =
mCodec->mPortEOS[kPortIndexOutput] = false;
+ mCodec->mInputEOSResult = OK;
+
mCodec->configureCodec(mime.c_str(), msg);
sp<RefBase> obj;
@@ -2362,6 +2385,8 @@
mCodec->mPortEOS[kPortIndexInput] =
mCodec->mPortEOS[kPortIndexOutput] = false;
+ mCodec->mInputEOSResult = OK;
+
mCodec->changeState(mCodec->mExecutingState);
}
}
diff --git a/media/libstagefright/AVIExtractor.cpp b/media/libstagefright/AVIExtractor.cpp
index 4e46414..0be2ca4 100644
--- a/media/libstagefright/AVIExtractor.cpp
+++ b/media/libstagefright/AVIExtractor.cpp
@@ -18,6 +18,7 @@
#define LOG_TAG "AVIExtractor"
#include <utils/Log.h>
+#include "include/avc_utils.h"
#include "include/AVIExtractor.h"
#include <binder/ProcessState.h>
@@ -55,11 +56,36 @@
MediaBufferGroup *mBufferGroup;
size_t mSampleIndex;
+ sp<MP3Splitter> mSplitter;
+
DISALLOW_EVIL_CONSTRUCTORS(AVISource);
};
////////////////////////////////////////////////////////////////////////////////
+struct AVIExtractor::MP3Splitter : public RefBase {
+ MP3Splitter();
+
+ void clear();
+ void append(MediaBuffer *buffer);
+ status_t read(MediaBuffer **buffer);
+
+protected:
+ virtual ~MP3Splitter();
+
+private:
+ bool mFindSync;
+ int64_t mBaseTimeUs;
+ int64_t mNumSamplesRead;
+ sp<ABuffer> mBuffer;
+
+ bool resync();
+
+ DISALLOW_EVIL_CONSTRUCTORS(MP3Splitter);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
AVIExtractor::AVISource::AVISource(
const sp<AVIExtractor> &extractor, size_t trackIndex)
: mExtractor(extractor),
@@ -83,6 +109,15 @@
mBufferGroup->add_buffer(new MediaBuffer(mTrack.mMaxSampleSize));
mSampleIndex = 0;
+ const char *mime;
+ CHECK(mTrack.mMeta->findCString(kKeyMIMEType, &mime));
+
+ if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_MPEG)) {
+ mSplitter = new MP3Splitter;
+ } else {
+ mSplitter.clear();
+ }
+
return OK;
}
@@ -92,6 +127,8 @@
delete mBufferGroup;
mBufferGroup = NULL;
+ mSplitter.clear();
+
return OK;
}
@@ -115,39 +152,213 @@
if (err != OK) {
return ERROR_END_OF_STREAM;
}
+
+ if (mSplitter != NULL) {
+ mSplitter->clear();
+ }
}
- off64_t offset;
- size_t size;
- bool isKey;
- int64_t timeUs;
- status_t err = mExtractor->getSampleInfo(
- mTrackIndex, mSampleIndex, &offset, &size, &isKey, &timeUs);
+ for (;;) {
+ if (mSplitter != NULL) {
+ status_t err = mSplitter->read(buffer);
- ++mSampleIndex;
+ if (err == OK) {
+ break;
+ } else if (err != -EAGAIN) {
+ return err;
+ }
+ }
- if (err != OK) {
- return ERROR_END_OF_STREAM;
+ off64_t offset;
+ size_t size;
+ bool isKey;
+ int64_t timeUs;
+ status_t err = mExtractor->getSampleInfo(
+ mTrackIndex, mSampleIndex, &offset, &size, &isKey, &timeUs);
+
+ ++mSampleIndex;
+
+ if (err != OK) {
+ return ERROR_END_OF_STREAM;
+ }
+
+ MediaBuffer *out;
+ CHECK_EQ(mBufferGroup->acquire_buffer(&out), (status_t)OK);
+
+ ssize_t n = mExtractor->mDataSource->readAt(offset, out->data(), size);
+
+ if (n < (ssize_t)size) {
+ return n < 0 ? (status_t)n : (status_t)ERROR_MALFORMED;
+ }
+
+ out->set_range(0, size);
+
+ out->meta_data()->setInt64(kKeyTime, timeUs);
+
+ if (isKey) {
+ out->meta_data()->setInt32(kKeyIsSyncFrame, 1);
+ }
+
+ if (mSplitter == NULL) {
+ *buffer = out;
+ break;
+ }
+
+ mSplitter->append(out);
+ out->release();
+ out = NULL;
}
- MediaBuffer *out;
- CHECK_EQ(mBufferGroup->acquire_buffer(&out), (status_t)OK);
+ return OK;
+}
- ssize_t n = mExtractor->mDataSource->readAt(offset, out->data(), size);
+////////////////////////////////////////////////////////////////////////////////
- if (n < (ssize_t)size) {
- return n < 0 ? (status_t)n : (status_t)ERROR_MALFORMED;
+AVIExtractor::MP3Splitter::MP3Splitter()
+ : mFindSync(true),
+ mBaseTimeUs(-1ll),
+ mNumSamplesRead(0) {
+}
+
+AVIExtractor::MP3Splitter::~MP3Splitter() {
+}
+
+void AVIExtractor::MP3Splitter::clear() {
+ mFindSync = true;
+ mBaseTimeUs = -1ll;
+ mNumSamplesRead = 0;
+
+ if (mBuffer != NULL) {
+ mBuffer->setRange(0, 0);
+ }
+}
+
+void AVIExtractor::MP3Splitter::append(MediaBuffer *buffer) {
+ size_t prevCapacity = (mBuffer != NULL) ? mBuffer->capacity() : 0;
+
+ if (mBaseTimeUs < 0) {
+ CHECK(mBuffer == NULL || mBuffer->size() == 0);
+ CHECK(buffer->meta_data()->findInt64(kKeyTime, &mBaseTimeUs));
+ mNumSamplesRead = 0;
}
- out->set_range(0, size);
-
- out->meta_data()->setInt64(kKeyTime, timeUs);
-
- if (isKey) {
- out->meta_data()->setInt32(kKeyIsSyncFrame, 1);
+ if (mBuffer != NULL && mBuffer->offset() > 0) {
+ memmove(mBuffer->base(), mBuffer->data(), mBuffer->size());
+ mBuffer->setRange(0, mBuffer->size());
}
- *buffer = out;
+ if (mBuffer == NULL
+ || mBuffer->size() + buffer->range_length() > prevCapacity) {
+ size_t newCapacity =
+ (prevCapacity + buffer->range_length() + 1023) & ~1023;
+
+ sp<ABuffer> newBuffer = new ABuffer(newCapacity);
+ if (mBuffer != NULL) {
+ memcpy(newBuffer->data(), mBuffer->data(), mBuffer->size());
+ newBuffer->setRange(0, mBuffer->size());
+ } else {
+ newBuffer->setRange(0, 0);
+ }
+ mBuffer = newBuffer;
+ }
+
+ memcpy(mBuffer->data() + mBuffer->size(),
+ (const uint8_t *)buffer->data() + buffer->range_offset(),
+ buffer->range_length());
+
+ mBuffer->setRange(0, mBuffer->size() + buffer->range_length());
+}
+
+bool AVIExtractor::MP3Splitter::resync() {
+ if (mBuffer == NULL) {
+ return false;
+ }
+
+ bool foundSync = false;
+ for (size_t offset = 0; offset + 3 < mBuffer->size(); ++offset) {
+ uint32_t firstHeader = U32_AT(mBuffer->data() + offset);
+
+ size_t frameSize;
+ if (!GetMPEGAudioFrameSize(firstHeader, &frameSize)) {
+ continue;
+ }
+
+ size_t subsequentOffset = offset + frameSize;
+ size_t i = 3;
+ while (i > 0) {
+ if (subsequentOffset + 3 >= mBuffer->size()) {
+ break;
+ }
+
+ static const uint32_t kMask = 0xfffe0c00;
+
+ uint32_t header = U32_AT(mBuffer->data() + subsequentOffset);
+ if ((header & kMask) != (firstHeader & kMask)) {
+ break;
+ }
+
+ if (!GetMPEGAudioFrameSize(header, &frameSize)) {
+ break;
+ }
+
+ subsequentOffset += frameSize;
+ --i;
+ }
+
+ if (i == 0) {
+ foundSync = true;
+ memmove(mBuffer->data(),
+ mBuffer->data() + offset,
+ mBuffer->size() - offset);
+
+ mBuffer->setRange(0, mBuffer->size() - offset);
+ break;
+ }
+ }
+
+ return foundSync;
+}
+
+status_t AVIExtractor::MP3Splitter::read(MediaBuffer **out) {
+ *out = NULL;
+
+ if (mFindSync) {
+ if (!resync()) {
+ return -EAGAIN;
+ }
+
+ mFindSync = false;
+ }
+
+ if (mBuffer->size() < 4) {
+ return -EAGAIN;
+ }
+
+ uint32_t header = U32_AT(mBuffer->data());
+ size_t frameSize;
+ int sampleRate;
+ int numSamples;
+ if (!GetMPEGAudioFrameSize(
+ header, &frameSize, &sampleRate, NULL, NULL, &numSamples)) {
+ return ERROR_MALFORMED;
+ }
+
+ if (mBuffer->size() < frameSize) {
+ return -EAGAIN;
+ }
+
+ MediaBuffer *mbuf = new MediaBuffer(frameSize);
+ memcpy(mbuf->data(), mBuffer->data(), frameSize);
+
+ int64_t timeUs = mBaseTimeUs + (mNumSamplesRead * 1000000ll) / sampleRate;
+ mNumSamplesRead += numSamples;
+
+ mbuf->meta_data()->setInt64(kKeyTime, timeUs);
+
+ mBuffer->setRange(
+ mBuffer->offset() + frameSize, mBuffer->size() - frameSize);
+
+ *out = mbuf;
return OK;
}
@@ -362,6 +573,13 @@
case FOURCC('X', 'V', 'I', 'X'):
return MEDIA_MIMETYPE_VIDEO_MPEG4;
+ // from http://wiki.multimedia.cx/index.php?title=H264
+ case FOURCC('a', 'v', 'c', '1'):
+ case FOURCC('d', 'a', 'v', 'c'):
+ case FOURCC('x', '2', '6', '4'):
+ case FOURCC('v', 's', 's', 'h'):
+ return MEDIA_MIMETYPE_VIDEO_AVC;
+
default:
return NULL;
}
@@ -406,6 +624,14 @@
return ERROR_MALFORMED;
}
+ if (mime == NULL) {
+ LOGW("Unsupported video format '%c%c%c%c'",
+ (char)(handler >> 24),
+ (char)((handler >> 16) & 0xff),
+ (char)((handler >> 8) & 0xff),
+ (char)(handler & 0xff));
+ }
+
kind = Track::VIDEO;
} else if (type == FOURCC('a', 'u', 'd', 's')) {
if (mime && strncasecmp(mime, "audio/", 6)) {
@@ -433,6 +659,8 @@
track->mThumbnailSampleSize = 0;
track->mThumbnailSampleIndex = -1;
track->mMaxSampleSize = 0;
+ track->mAvgChunkSize = 1.0;
+ track->mFirstChunkSize = 0;
return OK;
}
@@ -451,8 +679,8 @@
bool isVideo = (track->mKind == Track::VIDEO);
- if ((isVideo && size < 40) || (!isVideo && size < 18)) {
- // Expected a BITMAPINFO or WAVEFORMATEX structure, respectively.
+ if ((isVideo && size < 40) || (!isVideo && size < 16)) {
+ // Expected a BITMAPINFO or WAVEFORMAT(EX) structure, respectively.
return ERROR_MALFORMED;
}
@@ -473,8 +701,11 @@
track->mMeta->setInt32(kKeyHeight, height);
} else {
uint32_t format = U16LE_AT(data);
+
if (format == 0x55) {
track->mMeta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_MPEG);
+ } else {
+ LOGW("Unsupported audio format = 0x%04x", format);
}
uint32_t numChannels = U16LE_AT(&data[2]);
@@ -632,6 +863,47 @@
for (size_t i = 0; i < mTracks.size(); ++i) {
Track *track = &mTracks.editItemAt(i);
+ if (track->mBytesPerSample > 0) {
+ // Assume all chunks are roughly the same size for now.
+
+ // Compute the avg. size of the first 128 chunks (if there are
+ // that many), but exclude the size of the first one, since
+ // it may be an outlier.
+ size_t numSamplesToAverage = track->mSamples.size();
+ if (numSamplesToAverage > 256) {
+ numSamplesToAverage = 256;
+ }
+
+ double avgChunkSize = 0;
+ size_t j;
+ for (j = 0; j <= numSamplesToAverage; ++j) {
+ off64_t offset;
+ size_t size;
+ bool isKey;
+ int64_t dummy;
+
+ status_t err =
+ getSampleInfo(
+ i, j,
+ &offset, &size, &isKey, &dummy);
+
+ if (err != OK) {
+ return err;
+ }
+
+ if (j == 0) {
+ track->mFirstChunkSize = size;
+ continue;
+ }
+
+ avgChunkSize += size;
+ }
+
+ avgChunkSize /= numSamplesToAverage;
+
+ track->mAvgChunkSize = avgChunkSize;
+ }
+
int64_t durationUs;
CHECK_EQ((status_t)OK,
getSampleTime(i, track->mSamples.size() - 1, &durationUs));
@@ -646,37 +918,27 @@
AString mime = tmp;
- if (!strncasecmp("video/", mime.c_str(), 6)
- && track->mThumbnailSampleIndex >= 0) {
- int64_t thumbnailTimeUs;
- CHECK_EQ((status_t)OK,
- getSampleTime(i, track->mThumbnailSampleIndex,
- &thumbnailTimeUs));
+ if (!strncasecmp("video/", mime.c_str(), 6)) {
+ if (track->mThumbnailSampleIndex >= 0) {
+ int64_t thumbnailTimeUs;
+ CHECK_EQ((status_t)OK,
+ getSampleTime(i, track->mThumbnailSampleIndex,
+ &thumbnailTimeUs));
- track->mMeta->setInt64(kKeyThumbnailTime, thumbnailTimeUs);
+ track->mMeta->setInt64(kKeyThumbnailTime, thumbnailTimeUs);
+ }
+
+ status_t err = OK;
if (!strcasecmp(mime.c_str(), MEDIA_MIMETYPE_VIDEO_MPEG4)) {
- status_t err = addMPEG4CodecSpecificData(i);
-
- if (err != OK) {
- return err;
- }
+ err = addMPEG4CodecSpecificData(i);
+ } else if (!strcasecmp(mime.c_str(), MEDIA_MIMETYPE_VIDEO_AVC)) {
+ err = addH264CodecSpecificData(i);
}
- }
- if (track->mBytesPerSample != 0) {
- // Assume all chunks are the same size for now.
-
- off64_t offset;
- size_t size;
- bool isKey;
- int64_t sampleTimeUs;
- CHECK_EQ((status_t)OK,
- getSampleInfo(
- i, 0,
- &offset, &size, &isKey, &sampleTimeUs));
-
- track->mRate *= size / track->mBytesPerSample;
+ if (err != OK) {
+ return err;
+ }
}
}
@@ -781,6 +1043,63 @@
return OK;
}
+status_t AVIExtractor::addH264CodecSpecificData(size_t trackIndex) {
+ Track *track = &mTracks.editItemAt(trackIndex);
+
+ off64_t offset;
+ size_t size;
+ bool isKey;
+ int64_t timeUs;
+
+ // Extract codec specific data from the first non-empty sample.
+
+ size_t sampleIndex = 0;
+ for (;;) {
+ status_t err =
+ getSampleInfo(
+ trackIndex, sampleIndex, &offset, &size, &isKey, &timeUs);
+
+ if (err != OK) {
+ return err;
+ }
+
+ if (size > 0) {
+ break;
+ }
+
+ ++sampleIndex;
+ }
+
+ sp<ABuffer> buffer = new ABuffer(size);
+ ssize_t n = mDataSource->readAt(offset, buffer->data(), buffer->size());
+
+ if (n < (ssize_t)size) {
+ return n < 0 ? (status_t)n : ERROR_MALFORMED;
+ }
+
+ sp<MetaData> meta = MakeAVCCodecSpecificData(buffer);
+
+ if (meta == NULL) {
+ LOGE("Unable to extract AVC codec specific data");
+ return ERROR_MALFORMED;
+ }
+
+ int32_t width, height;
+ CHECK(meta->findInt32(kKeyWidth, &width));
+ CHECK(meta->findInt32(kKeyHeight, &height));
+
+ uint32_t type;
+ const void *csd;
+ size_t csdSize;
+ CHECK(meta->findData(kKeyAVCC, &type, &csd, &csdSize));
+
+ track->mMeta->setInt32(kKeyWidth, width);
+ track->mMeta->setInt32(kKeyHeight, width);
+ track->mMeta->setData(kKeyAVCC, type, csd, csdSize);
+
+ return OK;
+}
+
status_t AVIExtractor::getSampleInfo(
size_t trackIndex, size_t sampleIndex,
off64_t *offset, size_t *size, bool *isKey,
@@ -823,6 +1142,18 @@
*isKey = info.mIsKey;
+ if (track.mBytesPerSample > 0) {
+ size_t sampleStartInBytes;
+ if (sampleIndex == 0) {
+ sampleStartInBytes = 0;
+ } else {
+ sampleStartInBytes =
+ track.mFirstChunkSize + track.mAvgChunkSize * (sampleIndex - 1);
+ }
+
+ sampleIndex = sampleStartInBytes / track.mBytesPerSample;
+ }
+
*sampleTimeUs = (sampleIndex * 1000000ll * track.mRate) / track.mScale;
return OK;
@@ -847,8 +1178,24 @@
const Track &track = mTracks.itemAt(trackIndex);
- ssize_t closestSampleIndex =
- timeUs / track.mRate * track.mScale / 1000000ll;
+ ssize_t closestSampleIndex;
+
+ if (track.mBytesPerSample > 0) {
+ size_t closestByteOffset =
+ (timeUs * track.mBytesPerSample)
+ / track.mRate * track.mScale / 1000000ll;
+
+ if (closestByteOffset <= track.mFirstChunkSize) {
+ closestSampleIndex = 0;
+ } else {
+ closestSampleIndex =
+ (closestByteOffset - track.mFirstChunkSize)
+ / track.mAvgChunkSize;
+ }
+ } else {
+ // Each chunk contains a single sample.
+ closestSampleIndex = timeUs / track.mRate * track.mScale / 1000000ll;
+ }
ssize_t numSamples = track.mSamples.size();
diff --git a/media/libstagefright/SampleTable.cpp b/media/libstagefright/SampleTable.cpp
index 2b9d99b..ebad321 100644
--- a/media/libstagefright/SampleTable.cpp
+++ b/media/libstagefright/SampleTable.cpp
@@ -23,8 +23,8 @@
#include <arpa/inet.h>
+#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/DataSource.h>
-#include <media/stagefright/MediaDebug.h>
#include <media/stagefright/Utils.h>
namespace android {
@@ -40,6 +40,71 @@
////////////////////////////////////////////////////////////////////////////////
+struct SampleTable::CompositionDeltaLookup {
+ CompositionDeltaLookup();
+
+ void setEntries(
+ const uint32_t *deltaEntries, size_t numDeltaEntries);
+
+ uint32_t getCompositionTimeOffset(uint32_t sampleIndex);
+
+private:
+ Mutex mLock;
+
+ const uint32_t *mDeltaEntries;
+ size_t mNumDeltaEntries;
+
+ size_t mCurrentDeltaEntry;
+ size_t mCurrentEntrySampleIndex;
+
+ DISALLOW_EVIL_CONSTRUCTORS(CompositionDeltaLookup);
+};
+
+SampleTable::CompositionDeltaLookup::CompositionDeltaLookup()
+ : mDeltaEntries(NULL),
+ mNumDeltaEntries(0),
+ mCurrentDeltaEntry(0),
+ mCurrentEntrySampleIndex(0) {
+}
+
+void SampleTable::CompositionDeltaLookup::setEntries(
+ const uint32_t *deltaEntries, size_t numDeltaEntries) {
+ Mutex::Autolock autolock(mLock);
+
+ mDeltaEntries = deltaEntries;
+ mNumDeltaEntries = numDeltaEntries;
+ mCurrentDeltaEntry = 0;
+ mCurrentEntrySampleIndex = 0;
+}
+
+uint32_t SampleTable::CompositionDeltaLookup::getCompositionTimeOffset(
+ uint32_t sampleIndex) {
+ Mutex::Autolock autolock(mLock);
+
+ if (mDeltaEntries == NULL) {
+ return 0;
+ }
+
+ if (sampleIndex < mCurrentEntrySampleIndex) {
+ mCurrentDeltaEntry = 0;
+ mCurrentEntrySampleIndex = 0;
+ }
+
+ while (mCurrentDeltaEntry < mNumDeltaEntries) {
+ uint32_t sampleCount = mDeltaEntries[2 * mCurrentDeltaEntry];
+ if (sampleIndex < mCurrentEntrySampleIndex + sampleCount) {
+ return mDeltaEntries[2 * mCurrentDeltaEntry + 1];
+ }
+
+ mCurrentEntrySampleIndex += sampleCount;
+ ++mCurrentDeltaEntry;
+ }
+
+ return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
SampleTable::SampleTable(const sp<DataSource> &source)
: mDataSource(source),
mChunkOffsetOffset(-1),
@@ -56,6 +121,7 @@
mSampleTimeEntries(NULL),
mCompositionTimeDeltaEntries(NULL),
mNumCompositionTimeDeltaEntries(0),
+ mCompositionDeltaLookup(new CompositionDeltaLookup),
mSyncSampleOffset(-1),
mNumSyncSamples(0),
mSyncSamples(NULL),
@@ -71,6 +137,9 @@
delete[] mSyncSamples;
mSyncSamples = NULL;
+ delete mCompositionDeltaLookup;
+ mCompositionDeltaLookup = NULL;
+
delete[] mCompositionTimeDeltaEntries;
mCompositionTimeDeltaEntries = NULL;
@@ -318,6 +387,9 @@
mCompositionTimeDeltaEntries[i] = ntohl(mCompositionTimeDeltaEntries[i]);
}
+ mCompositionDeltaLookup->setEntries(
+ mCompositionTimeDeltaEntries, mNumCompositionTimeDeltaEntries);
+
return OK;
}
@@ -430,8 +502,12 @@
mSampleTimeEntries[sampleIndex].mSampleIndex = sampleIndex;
+ uint32_t compTimeDelta =
+ mCompositionDeltaLookup->getCompositionTimeOffset(
+ sampleIndex);
+
mSampleTimeEntries[sampleIndex].mCompositionTime =
- sampleTime + getCompositionTimeOffset(sampleIndex);
+ sampleTime + compTimeDelta;
}
++sampleIndex;
@@ -739,25 +815,8 @@
return OK;
}
-uint32_t SampleTable::getCompositionTimeOffset(uint32_t sampleIndex) const {
- if (mCompositionTimeDeltaEntries == NULL) {
- return 0;
- }
-
- uint32_t curSample = 0;
- for (size_t i = 0; i < mNumCompositionTimeDeltaEntries; ++i) {
- uint32_t sampleCount = mCompositionTimeDeltaEntries[2 * i];
-
- if (sampleIndex < curSample + sampleCount) {
- uint32_t sampleDelta = mCompositionTimeDeltaEntries[2 * i + 1];
-
- return sampleDelta;
- }
-
- curSample += sampleCount;
- }
-
- return 0;
+uint32_t SampleTable::getCompositionTimeOffset(uint32_t sampleIndex) {
+ return mCompositionDeltaLookup->getCompositionTimeOffset(sampleIndex);
}
} // namespace android
diff --git a/media/libstagefright/avc_utils.cpp b/media/libstagefright/avc_utils.cpp
index 8a42e8b..153ee33 100644
--- a/media/libstagefright/avc_utils.cpp
+++ b/media/libstagefright/avc_utils.cpp
@@ -297,7 +297,7 @@
sp<MetaData> meta = new MetaData;
meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC);
- meta->setData(kKeyAVCC, 0, csd->data(), csd->size());
+ meta->setData(kKeyAVCC, kTypeAVCC, csd->data(), csd->size());
meta->setInt32(kKeyWidth, width);
meta->setInt32(kKeyHeight, height);
@@ -329,6 +329,28 @@
return foundIDR;
}
+bool IsAVCReferenceFrame(const sp<ABuffer> &accessUnit) {
+ const uint8_t *data = accessUnit->data();
+ size_t size = accessUnit->size();
+
+ const uint8_t *nalStart;
+ size_t nalSize;
+ while (getNextNALUnit(&data, &size, &nalStart, &nalSize, true) == OK) {
+ CHECK_GT(nalSize, 0u);
+
+ unsigned nalType = nalStart[0] & 0x1f;
+
+ if (nalType == 5) {
+ return true;
+ } else if (nalType == 1) {
+ unsigned nal_ref_idc = (nalStart[0] >> 5) & 3;
+ return nal_ref_idc != 0;
+ }
+ }
+
+ return true;
+}
+
sp<MetaData> MakeAACCodecSpecificData(
unsigned profile, unsigned sampling_freq_index,
unsigned channel_configuration) {
diff --git a/media/libstagefright/chromium_http/support.cpp b/media/libstagefright/chromium_http/support.cpp
index de936c4..f15014e 100644
--- a/media/libstagefright/chromium_http/support.cpp
+++ b/media/libstagefright/chromium_http/support.cpp
@@ -74,10 +74,32 @@
return false;
}
+struct AutoPrioritySaver {
+ AutoPrioritySaver()
+ : mTID(androidGetTid()),
+ mPrevPriority(androidGetThreadPriority(mTID)) {
+ androidSetThreadPriority(mTID, ANDROID_PRIORITY_NORMAL);
+ }
+
+ ~AutoPrioritySaver() {
+ androidSetThreadPriority(mTID, mPrevPriority);
+ }
+
+private:
+ pid_t mTID;
+ int mPrevPriority;
+
+ DISALLOW_EVIL_CONSTRUCTORS(AutoPrioritySaver);
+};
static void InitializeNetworkThreadIfNecessary() {
Mutex::Autolock autoLock(gNetworkThreadLock);
+
if (gNetworkThread == NULL) {
+ // Make sure any threads spawned by the chromium framework are
+ // running at normal priority instead of inheriting this thread's.
+ AutoPrioritySaver saver;
+
gNetworkThread = new base::Thread("network");
base::Thread::Options options;
options.message_loop_type = MessageLoop::TYPE_IO;
diff --git a/media/libstagefright/foundation/AMessage.cpp b/media/libstagefright/foundation/AMessage.cpp
index 582bdba..f039bc1 100644
--- a/media/libstagefright/foundation/AMessage.cpp
+++ b/media/libstagefright/foundation/AMessage.cpp
@@ -385,6 +385,15 @@
item.u.refValue)->debugString(
indent + strlen(item.mName) + 14).c_str());
break;
+ case kTypeRect:
+ tmp = StringPrintf(
+ "Rect %s(%d, %d, %d, %d)",
+ item.mName,
+ item.u.rectValue.mLeft,
+ item.u.rectValue.mTop,
+ item.u.rectValue.mRight,
+ item.u.rectValue.mBottom);
+ break;
default:
TRESPASS();
}
diff --git a/media/libstagefright/include/AVIExtractor.h b/media/libstagefright/include/AVIExtractor.h
index b575347..ff5dcb5 100644
--- a/media/libstagefright/include/AVIExtractor.h
+++ b/media/libstagefright/include/AVIExtractor.h
@@ -42,6 +42,7 @@
private:
struct AVISource;
+ struct MP3Splitter;
struct SampleInfo {
uint32_t mOffset;
@@ -70,6 +71,10 @@
size_t mThumbnailSampleSize;
ssize_t mThumbnailSampleIndex;
size_t mMaxSampleSize;
+
+ // If mBytesPerSample > 0:
+ double mAvgChunkSize;
+ size_t mFirstChunkSize;
};
sp<DataSource> mDataSource;
@@ -101,6 +106,7 @@
size_t *sampleIndex) const;
status_t addMPEG4CodecSpecificData(size_t trackIndex);
+ status_t addH264CodecSpecificData(size_t trackIndex);
static bool IsCorrectChunkType(
ssize_t trackIndex, Track::Kind kind, uint32_t chunkType);
diff --git a/media/libstagefright/include/SampleTable.h b/media/libstagefright/include/SampleTable.h
index a6a6524..847dff7 100644
--- a/media/libstagefright/include/SampleTable.h
+++ b/media/libstagefright/include/SampleTable.h
@@ -86,6 +86,8 @@
~SampleTable();
private:
+ struct CompositionDeltaLookup;
+
static const uint32_t kChunkOffsetType32;
static const uint32_t kChunkOffsetType64;
static const uint32_t kSampleSizeType32;
@@ -117,6 +119,7 @@
uint32_t *mCompositionTimeDeltaEntries;
size_t mNumCompositionTimeDeltaEntries;
+ CompositionDeltaLookup *mCompositionDeltaLookup;
off64_t mSyncSampleOffset;
uint32_t mNumSyncSamples;
@@ -135,8 +138,7 @@
friend struct SampleIterator;
status_t getSampleSize_l(uint32_t sample_index, size_t *sample_size);
-
- uint32_t getCompositionTimeOffset(uint32_t sampleIndex) const;
+ uint32_t getCompositionTimeOffset(uint32_t sampleIndex);
static int CompareIncreasingTime(const void *, const void *);
diff --git a/media/libstagefright/include/avc_utils.h b/media/libstagefright/include/avc_utils.h
index 15cd4d4..e418822 100644
--- a/media/libstagefright/include/avc_utils.h
+++ b/media/libstagefright/include/avc_utils.h
@@ -50,6 +50,7 @@
sp<MetaData> MakeAVCCodecSpecificData(const sp<ABuffer> &accessUnit);
bool IsIDR(const sp<ABuffer> &accessUnit);
+bool IsAVCReferenceFrame(const sp<ABuffer> &accessUnit);
const char *AVCProfileToString(uint8_t profile);
diff --git a/media/libstagefright/omx/OMX.cpp b/media/libstagefright/omx/OMX.cpp
index bc24dbb..33d3f30 100644
--- a/media/libstagefright/omx/OMX.cpp
+++ b/media/libstagefright/omx/OMX.cpp
@@ -85,7 +85,7 @@
: mOwner(owner),
mDone(false) {
mThread = new CallbackDispatcherThread(this);
- mThread->run("OMXCallbackDisp", ANDROID_PRIORITY_AUDIO);
+ mThread->run("OMXCallbackDisp", ANDROID_PRIORITY_FOREGROUND);
}
OMX::CallbackDispatcher::~CallbackDispatcher() {
diff --git a/media/libstagefright/omx/SimpleSoftOMXComponent.cpp b/media/libstagefright/omx/SimpleSoftOMXComponent.cpp
index f7330f3..b705d00 100644
--- a/media/libstagefright/omx/SimpleSoftOMXComponent.cpp
+++ b/media/libstagefright/omx/SimpleSoftOMXComponent.cpp
@@ -42,7 +42,7 @@
mLooper->start(
false, // runOnCallingThread
false, // canCallJava
- PRIORITY_AUDIO);
+ ANDROID_PRIORITY_FOREGROUND);
}
void SimpleSoftOMXComponent::prepareForDestruction() {
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index a58f64c..88a05b2 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -7440,12 +7440,21 @@
}
}
+
+// The volume effect is used for automated tests only
+#ifndef OPENSL_ES_H_
+static const effect_uuid_t SL_IID_VOLUME_ = { 0x09e8ede0, 0xddde, 0x11db, 0xb4f6,
+ { 0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b } };
+const effect_uuid_t * const SL_IID_VOLUME = &SL_IID_VOLUME_;
+#endif //OPENSL_ES_H_
+
bool AudioFlinger::EffectChain::isEffectEligibleForSuspend(const effect_descriptor_t& desc)
{
// auxiliary effects and visualizer are never suspended on output mix
if ((mSessionId == AUDIO_SESSION_OUTPUT_MIX) &&
(((desc.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) ||
- (memcmp(&desc.type, SL_IID_VISUALIZATION, sizeof(effect_uuid_t)) == 0))) {
+ (memcmp(&desc.type, SL_IID_VISUALIZATION, sizeof(effect_uuid_t)) == 0) ||
+ (memcmp(&desc.type, SL_IID_VOLUME, sizeof(effect_uuid_t)) == 0))) {
return false;
}
return true;
diff --git a/services/audioflinger/AudioResampler.cpp b/services/audioflinger/AudioResampler.cpp
index dca795c..9ee5a30 100644
--- a/services/audioflinger/AudioResampler.cpp
+++ b/services/audioflinger/AudioResampler.cpp
@@ -434,9 +434,9 @@
// the following loop works on 2 frames
- ".Y4L01:\n"
+ "1:\n"
" cmp r8, r2\n" // curOut - maxCurOut
- " bcs .Y4L02\n"
+ " bcs 2f\n"
#define MO_ONE_FRAME \
" add r0, r1, r7, asl #1\n" /* in + inputIndex */\
@@ -460,8 +460,8 @@
MO_ONE_FRAME // frame 2
" cmp r7, r3\n" // inputIndex - maxInIdx
- " bcc .Y4L01\n"
- ".Y4L02:\n"
+ " bcc 1b\n"
+ "2:\n"
" bic r6, r6, #0xC0000000\n" // phaseFraction & ...
// save modified values
@@ -541,9 +541,9 @@
// r13 sp
// r14
- ".Y5L01:\n"
+ "3:\n"
" cmp r8, r2\n" // curOut - maxCurOut
- " bcs .Y5L02\n"
+ " bcs 4f\n"
#define ST_ONE_FRAME \
" bic r6, r6, #0xC0000000\n" /* phaseFraction & ... */\
@@ -577,8 +577,8 @@
ST_ONE_FRAME // frame 1
" cmp r7, r3\n" // inputIndex - maxInIdx
- " bcc .Y5L01\n"
- ".Y5L02:\n"
+ " bcc 3b\n"
+ "4:\n"
" bic r6, r6, #0xC0000000\n" // phaseFraction & ...
// save modified values