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