Merge "Stagefright: Use the ANW min undequeued bufs query."
diff --git a/drm/common/DrmSupportInfo.cpp b/drm/common/DrmSupportInfo.cpp
index ffc8953..3e02093 100644
--- a/drm/common/DrmSupportInfo.cpp
+++ b/drm/common/DrmSupportInfo.cpp
@@ -56,7 +56,7 @@
     for (unsigned int i = 0; i < mFileSuffixVector.size(); i++) {
         const String8 item = mFileSuffixVector.itemAt(i);
 
-        if (String8("") != fileType && item.find(fileType) != -1) {
+        if (item.find(fileType) != -1) {
             return true;
         }
     }
diff --git a/drm/drmserver/DrmManager.cpp b/drm/drmserver/DrmManager.cpp
index ef7d274..ec400b7 100644
--- a/drm/drmserver/DrmManager.cpp
+++ b/drm/drmserver/DrmManager.cpp
@@ -184,7 +184,10 @@
             IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
             result = rDrmEngine.canHandle(uniqueId, path);
         } else {
-            result = canHandle(uniqueId, path);
+            String8 extension = path.getPathExtension();
+            if (String8("") != extension) {
+                result = canHandle(uniqueId, path);
+            }
         }
     }
     return result;
diff --git a/include/media/IOMX.h b/include/media/IOMX.h
index cb36bbb..16a9342 100644
--- a/include/media/IOMX.h
+++ b/include/media/IOMX.h
@@ -85,6 +85,9 @@
     virtual status_t enableGraphicBuffers(
             node_id node, OMX_U32 port_index, OMX_BOOL enable) = 0;
 
+    virtual status_t getGraphicBufferUsage(
+            node_id node, OMX_U32 port_index, OMX_U32* usage) = 0;
+
     virtual status_t useBuffer(
             node_id node, OMX_U32 port_index, const sp<IMemory> &params,
             buffer_id *buffer) = 0;
diff --git a/include/media/IStreamSource.h b/include/media/IStreamSource.h
index 4b698e6..d310cee 100644
--- a/include/media/IStreamSource.h
+++ b/include/media/IStreamSource.h
@@ -45,6 +45,12 @@
 
     virtual void queueBuffer(size_t index, size_t size) = 0;
 
+    // When signalling a discontinuity you can optionally
+    // specify an int64_t PTS timestamp in "msg".
+    // If present, rendering of data following the discontinuity
+    // will be suppressed until media time reaches this timestamp.
+    static const char *const kKeyResumeAtPTS;
+
     virtual void issueCommand(
             Command cmd, bool synchronous, const sp<AMessage> &msg = NULL) = 0;
 };
diff --git a/include/media/MediaProfiles.h b/include/media/MediaProfiles.h
index aa97874..f2107ec 100644
--- a/include/media/MediaProfiles.h
+++ b/include/media/MediaProfiles.h
@@ -24,6 +24,7 @@
 namespace android {
 
 enum camcorder_quality {
+    CAMCORDER_QUALITY_LIST_START = 0,
     CAMCORDER_QUALITY_LOW  = 0,
     CAMCORDER_QUALITY_HIGH = 1,
     CAMCORDER_QUALITY_QCIF = 2,
@@ -31,14 +32,17 @@
     CAMCORDER_QUALITY_480P = 4,
     CAMCORDER_QUALITY_720P = 5,
     CAMCORDER_QUALITY_1080P = 6,
+    CAMCORDER_QUALITY_LIST_END = 6,
 
+    CAMCORDER_QUALITY_TIME_LAPSE_LIST_START = 1000,
     CAMCORDER_QUALITY_TIME_LAPSE_LOW  = 1000,
     CAMCORDER_QUALITY_TIME_LAPSE_HIGH = 1001,
     CAMCORDER_QUALITY_TIME_LAPSE_QCIF = 1002,
     CAMCORDER_QUALITY_TIME_LAPSE_CIF = 1003,
     CAMCORDER_QUALITY_TIME_LAPSE_480P = 1004,
     CAMCORDER_QUALITY_TIME_LAPSE_720P = 1005,
-    CAMCORDER_QUALITY_TIME_LAPSE_1080P = 1006
+    CAMCORDER_QUALITY_TIME_LAPSE_1080P = 1006,
+    CAMCORDER_QUALITY_TIME_LAPSE_LIST_END = 1006,
 };
 
 enum video_decoder {
@@ -147,6 +151,11 @@
     Vector<int> getImageEncodingQualityLevels(int cameraId) const;
 
 private:
+    enum {
+        // Camcorder profiles (high/low) and timelapse profiles (high/low)
+        kNumRequiredProfiles = 4,
+    };
+
     MediaProfiles& operator=(const MediaProfiles&);  // Don't call me
     MediaProfiles(const MediaProfiles&);             // Don't call me
     MediaProfiles() {}                               // Dummy default constructor
@@ -160,6 +169,14 @@
               mFrameHeight(frameHeight),
               mFrameRate(frameRate) {}
 
+        VideoCodec(const VideoCodec& copy) {
+            mCodec = copy.mCodec;
+            mBitRate = copy.mBitRate;
+            mFrameWidth = copy.mFrameWidth;
+            mFrameHeight = copy.mFrameHeight;
+            mFrameRate = copy.mFrameRate;
+        }
+
         ~VideoCodec() {}
 
         video_encoder mCodec;
@@ -176,6 +193,13 @@
               mSampleRate(sampleRate),
               mChannels(channels) {}
 
+        AudioCodec(const AudioCodec& copy) {
+            mCodec = copy.mCodec;
+            mBitRate = copy.mBitRate;
+            mSampleRate = copy.mSampleRate;
+            mChannels = copy.mChannels;
+        }
+
         ~AudioCodec() {}
 
         audio_encoder mCodec;
@@ -193,6 +217,15 @@
               mVideoCodec(0),
               mAudioCodec(0) {}
 
+        CamcorderProfile(const CamcorderProfile& copy) {
+            mCameraId = copy.mCameraId;
+            mFileFormat = copy.mFileFormat;
+            mQuality = copy.mQuality;
+            mDuration = copy.mDuration;
+            mVideoCodec = new VideoCodec(*copy.mVideoCodec);
+            mAudioCodec = new AudioCodec(*copy.mAudioCodec);
+        }
+
         ~CamcorderProfile() {
             delete mVideoCodec;
             delete mAudioCodec;
@@ -272,6 +305,8 @@
     };
 
     int getCamcorderProfileIndex(int cameraId, camcorder_quality quality) const;
+    void initRequiredProfileRefs(const Vector<int>& cameraIds);
+    int getRequiredProfileRefIndex(int cameraId);
 
     // Debug
     static void logVideoCodec(const VideoCodec& codec);
@@ -291,7 +326,10 @@
     static VideoDecoderCap* createVideoDecoderCap(const char **atts);
     static VideoEncoderCap* createVideoEncoderCap(const char **atts);
     static AudioEncoderCap* createAudioEncoderCap(const char **atts);
-    static CamcorderProfile* createCamcorderProfile(int cameraId, const char **atts);
+
+    static CamcorderProfile* createCamcorderProfile(
+                int cameraId, const char **atts, Vector<int>& cameraIds);
+
     static int getCameraId(const char **atts);
 
     ImageEncodingQualityLevels* findImageEncodingQualityLevels(int cameraId) const;
@@ -335,6 +373,21 @@
 
     static int findTagForName(const NameToTagMap *map, size_t nMappings, const char *name);
 
+    /**
+     * Check on existing profiles with the following criteria:
+     * 1. Low quality profile must have the lowest video
+     *    resolution product (width x height)
+     * 2. High quality profile must have the highest video
+     *    resolution product (width x height)
+     *
+     * and add required low/high quality camcorder/timelapse
+     * profiles if they are not found. This allows to remove
+     * duplicate profile definitions in the media_profiles.xml
+     * file.
+     */
+    void checkAndAddRequiredProfilesIfNecessary();
+
+
     // Mappings from name (for instance, codec name) to enum value
     static const NameToTagMap sVideoEncoderNameMap[];
     static const NameToTagMap sAudioEncoderNameMap[];
@@ -355,6 +408,20 @@
     Vector<VideoDecoderCap*>  mVideoDecoders;
     Vector<output_format>     mEncoderOutputFileFormats;
     Vector<ImageEncodingQualityLevels *>  mImageEncodingQualityLevels;
+
+    typedef struct {
+        bool mHasRefProfile;      // Refers to an existing profile
+        int  mRefProfileIndex;    // Reference profile index
+        int  mResolutionProduct;  // width x height
+    } RequiredProfileRefInfo;     // Required low and high profiles
+
+    typedef struct {
+        RequiredProfileRefInfo mRefs[kNumRequiredProfiles];
+        int mCameraId;
+    } RequiredProfiles;
+
+    RequiredProfiles *mRequiredProfileRefs;
+    Vector<int>              mCameraIds;
 };
 
 }; // namespace android
diff --git a/include/media/stagefright/HardwareAPI.h b/include/media/stagefright/HardwareAPI.h
index 17908b4..d1ecaaf 100644
--- a/include/media/stagefright/HardwareAPI.h
+++ b/include/media/stagefright/HardwareAPI.h
@@ -87,6 +87,18 @@
     const sp<android_native_buffer_t>& nativeBuffer;
 };
 
+// A pointer to this struct is passed to OMX_GetParameter when the extension
+// index for the 'OMX.google.android.index.getAndroidNativeBufferUsage'
+// extension is given.  The usage bits returned from this query will be used to
+// allocate the Gralloc buffers that get passed to the useAndroidNativeBuffer
+// command.
+struct GetAndroidNativeBufferUsageParams {
+    OMX_U32 nSize;              // IN
+    OMX_VERSIONTYPE nVersion;   // IN
+    OMX_U32 nPortIndex;         // IN
+    OMX_U32 nUsage;             // OUT
+};
+
 }  // namespace android
 
 extern android::OMXPluginBase *createOMXPlugin();
diff --git a/include/private/surfaceflinger/SharedBufferStack.h b/include/private/surfaceflinger/SharedBufferStack.h
index eb599b5..717f837 100644
--- a/include/private/surfaceflinger/SharedBufferStack.h
+++ b/include/private/surfaceflinger/SharedBufferStack.h
@@ -65,7 +65,7 @@
     // When changing these values, the COMPILE_TIME_ASSERT at the end of this
     // file need to be updated.
     static const unsigned int NUM_LAYERS_MAX  = 31;
-    static const unsigned int NUM_BUFFER_MAX  = 16;
+    static const unsigned int NUM_BUFFER_MAX  = 32;
     static const unsigned int NUM_BUFFER_MIN  = 2;
     static const unsigned int NUM_DISPLAY_MAX = 4;
 
@@ -123,7 +123,7 @@
 
 // ----------------------------------------------------------------------------
 
-// 32 KB max
+// 64 KB max
 class SharedClient
 {
 public:
@@ -394,7 +394,7 @@
 
 // ---------------------------------------------------------------------------
 
-COMPILE_TIME_ASSERT(sizeof(SharedClient) <= 32768)
+COMPILE_TIME_ASSERT(sizeof(SharedClient) <= 65536)
 COMPILE_TIME_ASSERT(sizeof(surface_flinger_cblk_t) <= 4096)
 
 // ---------------------------------------------------------------------------
diff --git a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
index ebe3302..0b061db 100644
--- a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
+++ b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
@@ -53,6 +53,11 @@
 namespace android {
 namespace {
 
+// Flag to allow a one time init of global memory, only happens on first call ever
+int LvmInitFlag = LVM_FALSE;
+SessionContext GlobalSessionMemory[LVM_MAX_SESSIONS];
+int SessionIndex[LVM_MAX_SESSIONS];
+
 /* local functions */
 #define CHECK_ARG(cond) {                     \
     if (!(cond)) {                            \
@@ -61,11 +66,6 @@
     }                                         \
 }
 
-// Flag to allow a one time init of global memory, only happens on first call ever
-int LvmInitFlag = LVM_FALSE;
-SessionContext GlobalSessionMemory[LVM_MAX_SESSIONS];
-
-int SessionIndex[LVM_MAX_SESSIONS];
 
 // NXP SW BassBoost UUID
 const effect_descriptor_t gBassBoostDescriptor = {
@@ -2588,9 +2588,11 @@
             pContext->pBundledContext->SamplesToExitCountBb -= outBuffer->frameCount * 2; // STEREO
             //LOGV("\tEffect_process: Waiting to turn off BASS_BOOST, %d samples left",
             //    pContext->pBundledContext->SamplesToExitCountBb);
-        } else {
+        }
+        if(pContext->pBundledContext->SamplesToExitCountBb <= 0) {
             status = -ENODATA;
             pContext->pBundledContext->NumberEffectsEnabled--;
+            LOGV("\tEffect_process() this is the last frame for LVM_BASS_BOOST");
         }
     }
     if ((pContext->pBundledContext->bVolumeEnabled == LVM_FALSE)&&
@@ -2606,9 +2608,11 @@
             pContext->pBundledContext->SamplesToExitCountEq -= outBuffer->frameCount * 2; // STEREO
             //LOGV("\tEffect_process: Waiting to turn off EQUALIZER, %d samples left",
             //    pContext->pBundledContext->SamplesToExitCountEq);
-        } else {
+        }
+        if(pContext->pBundledContext->SamplesToExitCountEq <= 0) {
             status = -ENODATA;
             pContext->pBundledContext->NumberEffectsEnabled--;
+            LOGV("\tEffect_process() this is the last frame for LVM_EQUALIZER");
         }
     }
     if ((pContext->pBundledContext->bVirtualizerEnabled == LVM_FALSE)&&
@@ -2618,9 +2622,11 @@
             pContext->pBundledContext->SamplesToExitCountVirt -= outBuffer->frameCount * 2;// STEREO
             //LOGV("\tEffect_process: Waiting for to turn off VIRTUALIZER, %d samples left",
             //    pContext->pBundledContext->SamplesToExitCountVirt);
-        } else {
+        }
+        if(pContext->pBundledContext->SamplesToExitCountVirt <= 0) {
             status = -ENODATA;
             pContext->pBundledContext->NumberEffectsEnabled--;
+            LOGV("\tEffect_process() this is the last frame for LVM_VIRTUALIZER");
         }
     }
 
diff --git a/media/libmedia/IOMX.cpp b/media/libmedia/IOMX.cpp
index af67175..d6a1757 100644
--- a/media/libmedia/IOMX.cpp
+++ b/media/libmedia/IOMX.cpp
@@ -33,6 +33,7 @@
     EMPTY_BUFFER,
     GET_EXTENSION_INDEX,
     OBSERVER_ON_MSG,
+    GET_GRAPHIC_BUFFER_USAGE,
 };
 
 class BpOMX : public BpInterface<IOMX> {
@@ -194,6 +195,19 @@
         return err;
     }
 
+    virtual status_t getGraphicBufferUsage(
+            node_id node, OMX_U32 port_index, OMX_U32* usage) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
+        data.writeIntPtr((intptr_t)node);
+        data.writeInt32(port_index);
+        remote()->transact(GET_GRAPHIC_BUFFER_USAGE, data, &reply);
+
+        status_t err = reply.readInt32();
+        *usage = reply.readInt32();
+        return err;
+    }
+
     virtual status_t useBuffer(
             node_id node, OMX_U32 port_index, const sp<IMemory> &params,
             buffer_id *buffer) {
@@ -508,6 +522,21 @@
             return NO_ERROR;
         }
 
+        case GET_GRAPHIC_BUFFER_USAGE:
+        {
+            CHECK_INTERFACE(IOMX, data, reply);
+
+            node_id node = (void*)data.readIntPtr();
+            OMX_U32 port_index = data.readInt32();
+
+            OMX_U32 usage = 0;
+            status_t err = getGraphicBufferUsage(node, port_index, &usage);
+            reply->writeInt32(err);
+            reply->writeInt32(usage);
+
+            return NO_ERROR;
+        }
+
         case USE_BUFFER:
         {
             CHECK_INTERFACE(IOMX, data, reply);
diff --git a/media/libmedia/IStreamSource.cpp b/media/libmedia/IStreamSource.cpp
index 5069002..c14ee82 100644
--- a/media/libmedia/IStreamSource.cpp
+++ b/media/libmedia/IStreamSource.cpp
@@ -26,6 +26,9 @@
 
 namespace android {
 
+// static
+const char *const IStreamListener::kKeyResumeAtPTS = "resume-at-PTS";
+
 enum {
     // IStreamSource
     SET_LISTENER = IBinder::FIRST_CALL_TRANSACTION,
diff --git a/media/libmedia/MediaProfiles.cpp b/media/libmedia/MediaProfiles.cpp
index 9ad63f0..7fb7aed 100644
--- a/media/libmedia/MediaProfiles.cpp
+++ b/media/libmedia/MediaProfiles.cpp
@@ -284,8 +284,17 @@
     return static_cast<output_format>(format);
 }
 
+static bool isCameraIdFound(int cameraId, const Vector<int>& cameraIds) {
+    for (int i = 0, n = cameraIds.size(); i < n; ++i) {
+        if (cameraId == cameraIds[i]) {
+            return true;
+        }
+    }
+    return false;
+}
+
 /*static*/ MediaProfiles::CamcorderProfile*
-MediaProfiles::createCamcorderProfile(int cameraId, const char **atts)
+MediaProfiles::createCamcorderProfile(int cameraId, const char **atts, Vector<int>& cameraIds)
 {
     CHECK(!strcmp("quality",    atts[0]) &&
           !strcmp("fileFormat", atts[2]) &&
@@ -301,6 +310,9 @@
 
     MediaProfiles::CamcorderProfile *profile = new MediaProfiles::CamcorderProfile;
     profile->mCameraId = cameraId;
+    if (!isCameraIdFound(cameraId, cameraIds)) {
+        cameraIds.add(cameraId);
+    }
     profile->mFileFormat = static_cast<output_format>(fileFormat);
     profile->mQuality = static_cast<camcorder_quality>(quality);
     profile->mDuration = atoi(atts[5]);
@@ -370,12 +382,167 @@
         profiles->mCurrentCameraId = getCameraId(atts);
     } else if (strcmp("EncoderProfile", name) == 0) {
         profiles->mCamcorderProfiles.add(
-            createCamcorderProfile(profiles->mCurrentCameraId, atts));
+            createCamcorderProfile(profiles->mCurrentCameraId, atts, profiles->mCameraIds));
     } else if (strcmp("ImageEncoding", name) == 0) {
         profiles->addImageEncodingQualityLevel(profiles->mCurrentCameraId, atts);
     }
 }
 
+static bool isCamcorderProfile(camcorder_quality quality) {
+    return quality >= CAMCORDER_QUALITY_LIST_START &&
+           quality <= CAMCORDER_QUALITY_LIST_END;
+}
+
+static bool isTimelapseProfile(camcorder_quality quality) {
+    return quality >= CAMCORDER_QUALITY_TIME_LAPSE_LIST_START &&
+           quality <= CAMCORDER_QUALITY_TIME_LAPSE_LIST_END;
+}
+
+void MediaProfiles::initRequiredProfileRefs(const Vector<int>& cameraIds) {
+    LOGV("Number of camera ids: %d", cameraIds.size());
+    CHECK(cameraIds.size() > 0);
+    mRequiredProfileRefs = new RequiredProfiles[cameraIds.size()];
+    for (size_t i = 0, n = cameraIds.size(); i < n; ++i) {
+        mRequiredProfileRefs[i].mCameraId = cameraIds[i];
+        for (size_t j = 0; j < kNumRequiredProfiles; ++j) {
+            mRequiredProfileRefs[i].mRefs[j].mHasRefProfile = false;
+            mRequiredProfileRefs[i].mRefs[j].mRefProfileIndex = -1;
+            if ((j & 1) == 0) {  // low resolution
+                mRequiredProfileRefs[i].mRefs[j].mResolutionProduct = 0x7FFFFFFF;
+            } else {             // high resolution
+                mRequiredProfileRefs[i].mRefs[j].mResolutionProduct = 0;
+            }
+        }
+    }
+}
+
+int MediaProfiles::getRequiredProfileRefIndex(int cameraId) {
+    for (size_t i = 0, n = mCameraIds.size(); i < n; ++i) {
+        if (mCameraIds[i] == cameraId) {
+            return i;
+        }
+    }
+    return -1;
+}
+
+void MediaProfiles::checkAndAddRequiredProfilesIfNecessary() {
+    if (sIsInitialized) {
+        return;
+    }
+
+    initRequiredProfileRefs(mCameraIds);
+
+    for (size_t i = 0, n = mCamcorderProfiles.size(); i < n; ++i) {
+        int product = mCamcorderProfiles[i]->mVideoCodec->mFrameWidth *
+                      mCamcorderProfiles[i]->mVideoCodec->mFrameHeight;
+
+        camcorder_quality quality = mCamcorderProfiles[i]->mQuality;
+        int cameraId = mCamcorderProfiles[i]->mCameraId;
+        int index = -1;
+        int refIndex = getRequiredProfileRefIndex(cameraId);
+        CHECK(refIndex != -1);
+        RequiredProfileRefInfo *info;
+        camcorder_quality refQuality;
+        VideoCodec *codec = NULL;
+
+        // Check high and low from either camcorder profile or timelapse profile
+        // but not both. Default, check camcorder profile
+        size_t j = 0;
+        size_t n = 2;
+        if (isTimelapseProfile(quality)) {
+            // Check timelapse profile instead.
+            j = 2;
+            n = kNumRequiredProfiles;
+        } else {
+            // Must be camcorder profile.
+            CHECK(isCamcorderProfile(quality));
+        }
+        for (; j < n; ++j) {
+            info = &(mRequiredProfileRefs[refIndex].mRefs[j]);
+            if ((j % 2 == 0 && product > info->mResolutionProduct) ||  // low
+                (j % 2 != 0 && product < info->mResolutionProduct)) {  // high
+                continue;
+            }
+            switch (j) {
+                case 0:
+                   refQuality = CAMCORDER_QUALITY_LOW;
+                   break;
+                case 1:
+                   refQuality = CAMCORDER_QUALITY_HIGH;
+                   break;
+                case 2:
+                   refQuality = CAMCORDER_QUALITY_TIME_LAPSE_LOW;
+                   break;
+                case 3:
+                   refQuality = CAMCORDER_QUALITY_TIME_LAPSE_HIGH;
+                   break;
+                default:
+                    CHECK(!"Should never reach here");
+            }
+
+            if (!info->mHasRefProfile) {
+                index = getCamcorderProfileIndex(cameraId, refQuality);
+            }
+            if (index == -1) {
+                // New high or low quality profile is found.
+                // Update its reference.
+                info->mHasRefProfile = true;
+                info->mRefProfileIndex = i;
+                info->mResolutionProduct = product;
+            }
+        }
+    }
+
+    for (size_t cameraId = 0; cameraId < mCameraIds.size(); ++cameraId) {
+        for (size_t j = 0; j < kNumRequiredProfiles; ++j) {
+            int refIndex = getRequiredProfileRefIndex(cameraId);
+            CHECK(refIndex != -1);
+            RequiredProfileRefInfo *info =
+                    &mRequiredProfileRefs[refIndex].mRefs[j];
+
+            if (info->mHasRefProfile) {
+
+                CamcorderProfile *profile =
+                    new CamcorderProfile(
+                            *mCamcorderProfiles[info->mRefProfileIndex]);
+
+                // Overwrite the quality
+                switch (j % kNumRequiredProfiles) {
+                    case 0:
+                        profile->mQuality = CAMCORDER_QUALITY_LOW;
+                        break;
+                    case 1:
+                        profile->mQuality = CAMCORDER_QUALITY_HIGH;
+                        break;
+                    case 2:
+                        profile->mQuality = CAMCORDER_QUALITY_TIME_LAPSE_LOW;
+                        break;
+                    case 3:
+                        profile->mQuality = CAMCORDER_QUALITY_TIME_LAPSE_HIGH;
+                        break;
+                    default:
+                        CHECK(!"Should never come here");
+                }
+
+                int index = getCamcorderProfileIndex(cameraId, profile->mQuality);
+                if (index != -1) {
+                    LOGV("Profile quality %d for camera %d already exists",
+                        profile->mQuality, cameraId);
+                    CHECK(index == refIndex);
+                    continue;
+                }
+
+                // Insert the new profile
+                LOGV("Add a profile: quality %d=>%d for camera %d",
+                        mCamcorderProfiles[info->mRefProfileIndex]->mQuality,
+                        profile->mQuality, cameraId);
+
+                mCamcorderProfiles.add(profile);
+            }
+        }
+    }
+}
+
 /*static*/ MediaProfiles*
 MediaProfiles::getInstance()
 {
@@ -396,6 +563,9 @@
         } else {
             sInstance = createInstanceFromXmlFile(value);
         }
+        CHECK(sInstance != NULL);
+        sInstance->checkAndAddRequiredProfilesIfNecessary();
+        sIsInitialized = true;
     }
 
     return sInstance;
@@ -613,7 +783,6 @@
     createDefaultAudioDecoders(profiles);
     createDefaultEncoderOutputFileFormats(profiles);
     createDefaultImageEncodingQualityLevels(profiles);
-    sIsInitialized = true;
     return profiles;
 }
 
@@ -667,9 +836,6 @@
 exit:
     ::XML_ParserFree(parser);
     ::fclose(fp);
-    if (profiles) {
-        sIsInitialized = true;
-    }
     return profiles;
 }
 
diff --git a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp
index b3314be..d07ea1b 100644
--- a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp
@@ -96,10 +96,12 @@
         } else {
             if (buffer[0] == 0x00) {
                 // XXX legacy
+                sp<AMessage> extra;
                 mTSParser->signalDiscontinuity(
                         buffer[1] == 0x00
                             ? ATSParser::DISCONTINUITY_SEEK
-                            : ATSParser::DISCONTINUITY_FORMATCHANGE);
+                            : ATSParser::DISCONTINUITY_FORMATCHANGE,
+                        extra);
             } else {
                 mTSParser->feedTSPacket(buffer, sizeof(buffer));
             }
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index 474c056..d439f6e 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -191,6 +191,8 @@
 
             mAudioEOS = false;
             mVideoEOS = false;
+            mSkipRenderingAudioUntilMediaTimeUs = -1;
+            mSkipRenderingVideoUntilMediaTimeUs = -1;
 
             mSource->start();
 
@@ -592,6 +594,31 @@
             LOGV("%s discontinuity (formatChange=%d)",
                  audio ? "audio" : "video", formatChange);
 
+            if (audio) {
+                mSkipRenderingAudioUntilMediaTimeUs = -1;
+            } else {
+                mSkipRenderingVideoUntilMediaTimeUs = -1;
+            }
+
+            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);
         }
 
@@ -627,6 +654,27 @@
 
     sp<ABuffer> buffer = static_cast<ABuffer *>(obj.get());
 
+    int64_t &skipUntilMediaTimeUs =
+        audio
+            ? mSkipRenderingAudioUntilMediaTimeUs
+            : mSkipRenderingVideoUntilMediaTimeUs;
+
+    if (skipUntilMediaTimeUs >= 0) {
+        int64_t mediaTimeUs;
+        CHECK(buffer->meta()->findInt64("timeUs", &mediaTimeUs));
+
+        if (mediaTimeUs < skipUntilMediaTimeUs) {
+            LOGV("dropping %s buffer at time %lld as requested.",
+                 audio ? "audio" : "video",
+                 mediaTimeUs);
+
+            reply->post();
+            return;
+        }
+
+        skipUntilMediaTimeUs = -1;
+    }
+
     mRenderer->queueBuffer(audio, buffer, reply);
 }
 
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.h b/media/libmediaplayerservice/nuplayer/NuPlayer.h
index e7c6a42..fb5b001 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.h
@@ -112,6 +112,9 @@
     bool mResetInProgress;
     bool mResetPostponed;
 
+    int64_t mSkipRenderingAudioUntilMediaTimeUs;
+    int64_t mSkipRenderingVideoUntilMediaTimeUs;
+
     status_t instantiateDecoder(bool audio, sp<Decoder> *decoder);
 
     status_t feedDecoderInputData(bool audio, const sp<AMessage> &msg);
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.cpp
index a23beb7..885ebe4 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.cpp
@@ -92,9 +92,12 @@
     }
 }
 
-ssize_t NuPlayer::NuPlayerStreamListener::read(void *data, size_t size) {
+ssize_t NuPlayer::NuPlayerStreamListener::read(
+        void *data, size_t size, sp<AMessage> *extra) {
     CHECK_GT(size, 0u);
 
+    extra->clear();
+
     Mutex::Autolock autoLock(mLock);
 
     if (mEOS) {
@@ -122,6 +125,8 @@
 
             case DISCONTINUITY:
             {
+                *extra = entry->mExtra;
+
                 mQueue.erase(mQueue.begin());
                 entry = NULL;
 
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.h b/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.h
index f88e945..df0935d 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.h
@@ -37,7 +37,7 @@
             Command cmd, bool synchronous, const sp<AMessage> &extra);
 
     void start();
-    ssize_t read(void *data, size_t size);
+    ssize_t read(void *data, size_t size, sp<AMessage> *extra);
 
 private:
     enum {
diff --git a/media/libmediaplayerservice/nuplayer/StreamingSource.cpp b/media/libmediaplayerservice/nuplayer/StreamingSource.cpp
index b85ac9f..2016282 100644
--- a/media/libmediaplayerservice/nuplayer/StreamingSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/StreamingSource.cpp
@@ -54,7 +54,8 @@
 
     for (int32_t i = 0; i < 10; ++i) {
         char buffer[188];
-        ssize_t n = mStreamListener->read(buffer, sizeof(buffer));
+        sp<AMessage> extra;
+        ssize_t n = mStreamListener->read(buffer, sizeof(buffer), &extra);
 
         if (n == 0) {
             LOGI("input data EOS reached.");
@@ -62,7 +63,8 @@
             mEOS = true;
             break;
         } else if (n == INFO_DISCONTINUITY) {
-            mTSParser->signalDiscontinuity(ATSParser::DISCONTINUITY_SEEK);
+            mTSParser->signalDiscontinuity(
+                    ATSParser::DISCONTINUITY_SEEK, extra);
         } else if (n < 0) {
             CHECK_EQ(n, -EWOULDBLOCK);
             break;
@@ -72,7 +74,8 @@
                 mTSParser->signalDiscontinuity(
                         buffer[1] == 0x00
                             ? ATSParser::DISCONTINUITY_SEEK
-                            : ATSParser::DISCONTINUITY_FORMATCHANGE);
+                            : ATSParser::DISCONTINUITY_FORMATCHANGE,
+                        extra);
             } else {
                 mTSParser->feedTSPacket(buffer, sizeof(buffer));
             }
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index 50b7782..d590ab9 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -430,10 +430,17 @@
     }
 
     // Set up the native window.
-    // XXX TODO: Get the gralloc usage flags from the OMX plugin!
+    OMX_U32 usage = 0;
+    err = mOMX->getGraphicBufferUsage(mNode, kPortIndexOutput, &usage);
+    if (err != 0) {
+        LOGW("querying usage flags from OMX IL component failed: %d", err);
+        // XXX: Currently this error is logged, but not fatal.
+        usage = 0;
+    }
+
     err = native_window_set_usage(
             mNativeWindow.get(),
-            GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_EXTERNAL_DISP);
+            usage | GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_EXTERNAL_DISP);
 
     if (err != 0) {
         LOGE("native_window_set_usage failed: %s (%d)", strerror(-err), -err);
@@ -476,16 +483,12 @@
         return err;
     }
 
-    // XXX TODO: Do something so the ANativeWindow knows that we'll need to get
-    // the same set of buffers.
-
     LOGV("[%s] Allocating %lu buffers from a native window of size %lu on "
          "output port",
          mComponentName.c_str(), def.nBufferCountActual, def.nBufferSize);
 
     // Dequeue buffers and send them to OMX
-    OMX_U32 i;
-    for (i = 0; i < def.nBufferCountActual; i++) {
+    for (OMX_U32 i = 0; i < def.nBufferCountActual; i++) {
         android_native_buffer_t *buf;
         err = mNativeWindow->dequeueBuffer(mNativeWindow.get(), &buf);
         if (err != 0) {
@@ -494,23 +497,26 @@
         }
 
         sp<GraphicBuffer> graphicBuffer(new GraphicBuffer(buf, false));
-        IOMX::buffer_id bufferId;
-        err = mOMX->useGraphicBuffer(mNode, kPortIndexOutput, graphicBuffer,
-                &bufferId);
-        if (err != 0) {
-            break;
-        }
-
-        LOGV("[%s] Registered graphic buffer with ID %p (pointer = %p)",
-             mComponentName.c_str(),
-             bufferId, graphicBuffer.get());
-
         BufferInfo info;
-        info.mBufferID = bufferId;
         info.mStatus = BufferInfo::OWNED_BY_US;
         info.mData = new ABuffer(0);
         info.mGraphicBuffer = graphicBuffer;
         mBuffers[kPortIndexOutput].push(info);
+
+        IOMX::buffer_id bufferId;
+        err = mOMX->useGraphicBuffer(mNode, kPortIndexOutput, graphicBuffer,
+                &bufferId);
+        if (err != 0) {
+            LOGE("registering GraphicBuffer %lu with OMX IL component failed: "
+                 "%d", i, err);
+            break;
+        }
+
+        mBuffers[kPortIndexOutput].editItemAt(i).mBufferID = bufferId;
+
+        LOGV("[%s] Registered graphic buffer with ID %p (pointer = %p)",
+             mComponentName.c_str(),
+             bufferId, graphicBuffer.get());
     }
 
     OMX_U32 cancelStart;
@@ -520,7 +526,7 @@
         // If an error occurred while dequeuing we need to cancel any buffers
         // that were dequeued.
         cancelStart = 0;
-        cancelEnd = i;
+        cancelEnd = mBuffers[kPortIndexOutput].size();
     } else {
         // Return the last two buffers to the native window.
         cancelStart = def.nBufferCountActual - minUndequeuedBufs;
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index 1a37928..4a94e0d 100644
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -1744,9 +1744,16 @@
     }
 
     // Set up the native window.
-    // XXX TODO: Get the gralloc usage flags from the OMX plugin!
+    OMX_U32 usage = 0;
+    err = mOMX->getGraphicBufferUsage(mNode, kPortIndexOutput, &usage);
+    if (err != 0) {
+        LOGW("querying usage flags from OMX IL component failed: %d", err);
+        // XXX: Currently this error is logged, but not fatal.
+        usage = 0;
+    }
+
     err = native_window_set_usage(
-            mNativeWindow.get(), GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_EXTERNAL_DISP);
+            mNativeWindow.get(), usage | GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_EXTERNAL_DISP);
     if (err != 0) {
         LOGE("native_window_set_usage failed: %s (%d)", strerror(-err), -err);
         return err;
@@ -1784,15 +1791,11 @@
         return err;
     }
 
-    // XXX TODO: Do something so the ANativeWindow knows that we'll need to get
-    // the same set of buffers.
-
     CODEC_LOGI("allocating %lu buffers from a native window of size %lu on "
             "output port", def.nBufferCountActual, def.nBufferSize);
 
     // Dequeue buffers and send them to OMX
-    OMX_U32 i;
-    for (i = 0; i < def.nBufferCountActual; i++) {
+    for (OMX_U32 i = 0; i < def.nBufferCountActual; i++) {
         android_native_buffer_t* buf;
         err = mNativeWindow->dequeueBuffer(mNativeWindow.get(), &buf);
         if (err != 0) {
@@ -1801,36 +1804,37 @@
         }
 
         sp<GraphicBuffer> graphicBuffer(new GraphicBuffer(buf, false));
-        IOMX::buffer_id bufferId;
-        err = mOMX->useGraphicBuffer(mNode, kPortIndexOutput, graphicBuffer,
-                &bufferId);
-        if (err != 0) {
-            break;
-        }
-
-        CODEC_LOGV("registered graphic buffer with ID %p (pointer = %p)",
-                bufferId, graphicBuffer.get());
-
         BufferInfo info;
         info.mData = NULL;
         info.mSize = def.nBufferSize;
-        info.mBuffer = bufferId;
         info.mStatus = OWNED_BY_US;
         info.mMem = NULL;
         info.mMediaBuffer = new MediaBuffer(graphicBuffer);
         info.mMediaBuffer->setObserver(this);
-
         mPortBuffers[kPortIndexOutput].push(info);
+
+        IOMX::buffer_id bufferId;
+        err = mOMX->useGraphicBuffer(mNode, kPortIndexOutput, graphicBuffer,
+                &bufferId);
+        if (err != 0) {
+            CODEC_LOGE("registering GraphicBuffer with OMX IL component "
+                    "failed: %d", err);
+            break;
+        }
+
+        mPortBuffers[kPortIndexOutput].editItemAt(i).mBuffer = bufferId;
+
+        CODEC_LOGV("registered graphic buffer with ID %p (pointer = %p)",
+                bufferId, graphicBuffer.get());
     }
 
     OMX_U32 cancelStart;
     OMX_U32 cancelEnd;
-
     if (err != 0) {
         // If an error occurred while dequeuing we need to cancel any buffers
         // that were dequeued.
         cancelStart = 0;
-        cancelEnd = i;
+        cancelEnd = mPortBuffers[kPortIndexOutput].size();
     } else {
         // Return the last two buffers to the native window.
         cancelStart = def.nBufferCountActual - minUndequeuedBufs;
diff --git a/media/libstagefright/OggExtractor.cpp b/media/libstagefright/OggExtractor.cpp
index 0e51caf..6538a05 100644
--- a/media/libstagefright/OggExtractor.cpp
+++ b/media/libstagefright/OggExtractor.cpp
@@ -73,6 +73,7 @@
     // Returns an approximate bitrate in bits per second.
     uint64_t approxBitrate();
 
+    status_t seekToTime(int64_t timeUs);
     status_t seekToOffset(off64_t offset);
     status_t readNextPacket(MediaBuffer **buffer);
 
@@ -90,6 +91,11 @@
         uint8_t mLace[255];
     };
 
+    struct TOCEntry {
+        off64_t mPageOffset;
+        int64_t mTimeUs;
+    };
+
     sp<DataSource> mSource;
     off64_t mOffset;
     Page mCurrentPage;
@@ -107,6 +113,8 @@
     sp<MetaData> mMeta;
     sp<MetaData> mFileMeta;
 
+    Vector<TOCEntry> mTableOfContents;
+
     ssize_t readPage(off64_t offset, Page *page);
     status_t findNextPage(off64_t startOffset, off64_t *pageOffset);
 
@@ -115,7 +123,9 @@
 
     void parseFileMetaData();
 
-    uint64_t findPrevGranulePosition(off64_t pageOffset);
+    status_t findPrevGranulePosition(off64_t pageOffset, uint64_t *granulePos);
+
+    void buildTableOfContents();
 
     MyVorbisExtractor(const MyVorbisExtractor &);
     MyVorbisExtractor &operator=(const MyVorbisExtractor &);
@@ -164,10 +174,7 @@
     int64_t seekTimeUs;
     ReadOptions::SeekMode mode;
     if (options && options->getSeekTo(&seekTimeUs, &mode)) {
-        off64_t pos = seekTimeUs * mExtractor->mImpl->approxBitrate() / 8000000ll;
-        LOGV("seeking to offset %ld", pos);
-
-        if (mExtractor->mImpl->seekToOffset(pos) != OK) {
+        if (mExtractor->mImpl->seekToTime(seekTimeUs) != OK) {
             return ERROR_END_OF_STREAM;
         }
     }
@@ -237,7 +244,7 @@
 
         if (!memcmp(signature, "OggS", 4)) {
             if (*pageOffset > startOffset) {
-                LOGV("skipped %ld bytes of junk to reach next frame",
+                LOGV("skipped %lld bytes of junk to reach next frame",
                      *pageOffset - startOffset);
             }
 
@@ -252,7 +259,10 @@
 // it (if any) and return its granule position.
 // To do this we back up from the "current" page's offset until we find any
 // page preceding it and then scan forward to just before the current page.
-uint64_t MyVorbisExtractor::findPrevGranulePosition(off64_t pageOffset) {
+status_t MyVorbisExtractor::findPrevGranulePosition(
+        off64_t pageOffset, uint64_t *granulePos) {
+    *granulePos = 0;
+
     off64_t prevPageOffset = 0;
     off64_t prevGuess = pageOffset;
     for (;;) {
@@ -262,9 +272,12 @@
             prevGuess = 0;
         }
 
-        LOGV("backing up %ld bytes", pageOffset - prevGuess);
+        LOGV("backing up %lld bytes", pageOffset - prevGuess);
 
-        CHECK_EQ(findNextPage(prevGuess, &prevPageOffset), (status_t)OK);
+        status_t err = findNextPage(prevGuess, &prevPageOffset);
+        if (err != OK) {
+            return err;
+        }
 
         if (prevPageOffset < pageOffset || prevGuess == 0) {
             break;
@@ -273,27 +286,64 @@
 
     if (prevPageOffset == pageOffset) {
         // We did not find a page preceding this one.
-        return 0;
+        return UNKNOWN_ERROR;
     }
 
-    LOGV("prevPageOffset at %ld, pageOffset at %ld", prevPageOffset, pageOffset);
+    LOGV("prevPageOffset at %lld, pageOffset at %lld",
+         prevPageOffset, pageOffset);
 
     for (;;) {
         Page prevPage;
         ssize_t n = readPage(prevPageOffset, &prevPage);
 
         if (n <= 0) {
-            return 0;
+            return (status_t)n;
         }
 
         prevPageOffset += n;
 
         if (prevPageOffset == pageOffset) {
-            return prevPage.mGranulePosition;
+            *granulePos = prevPage.mGranulePosition;
+            return OK;
         }
     }
 }
 
+status_t MyVorbisExtractor::seekToTime(int64_t timeUs) {
+    if (mTableOfContents.isEmpty()) {
+        // Perform approximate seeking based on avg. bitrate.
+
+        off64_t pos = timeUs * approxBitrate() / 8000000ll;
+
+        LOGV("seeking to offset %lld", pos);
+        return seekToOffset(pos);
+    }
+
+    size_t left = 0;
+    size_t right = mTableOfContents.size();
+    while (left < right) {
+        size_t center = left / 2 + right / 2 + (left & right & 1);
+
+        const TOCEntry &entry = mTableOfContents.itemAt(center);
+
+        if (timeUs < entry.mTimeUs) {
+            right = center;
+        } else if (timeUs > entry.mTimeUs) {
+            left = center + 1;
+        } else {
+            left = right = center;
+            break;
+        }
+    }
+
+    const TOCEntry &entry = mTableOfContents.itemAt(left);
+
+    LOGV("seeking to entry %d / %d at offset %lld",
+         left, mTableOfContents.size(), entry.mPageOffset);
+
+    return seekToOffset(entry.mPageOffset);
+}
+
 status_t MyVorbisExtractor::seekToOffset(off64_t offset) {
     if (mFirstDataOffset >= 0 && offset < mFirstDataOffset) {
         // Once we know where the actual audio data starts (past the headers)
@@ -311,7 +361,7 @@
     // We found the page we wanted to seek to, but we'll also need
     // the page preceding it to determine how many valid samples are on
     // this page.
-    mPrevGranulePosition = findPrevGranulePosition(pageOffset);
+    findPrevGranulePosition(pageOffset, &mPrevGranulePosition);
 
     mOffset = pageOffset;
 
@@ -330,7 +380,8 @@
     uint8_t header[27];
     if (mSource->readAt(offset, header, sizeof(header))
             < (ssize_t)sizeof(header)) {
-        LOGV("failed to read %d bytes at offset 0x%08lx", sizeof(header), offset);
+        LOGV("failed to read %d bytes at offset 0x%016llx",
+             sizeof(header), offset);
 
         return ERROR_IO;
     }
@@ -447,7 +498,8 @@
                     packetSize);
 
             if (n < (ssize_t)packetSize) {
-                LOGV("failed to read %d bytes at 0x%08lx", packetSize, dataOffset);
+                LOGV("failed to read %d bytes at 0x%016llx",
+                     packetSize, dataOffset);
                 return ERROR_IO;
             }
 
@@ -563,9 +615,66 @@
 
     mFirstDataOffset = mOffset + mCurrentPageSize;
 
+    off64_t size;
+    uint64_t lastGranulePosition;
+    if (!(mSource->flags() & DataSource::kIsCachingDataSource)
+            && mSource->getSize(&size) == OK
+            && findPrevGranulePosition(size, &lastGranulePosition) == OK) {
+        // Let's assume it's cheap to seek to the end.
+        // The granule position of the final page in the stream will
+        // give us the exact duration of the content, something that
+        // we can only approximate using avg. bitrate if seeking to
+        // the end is too expensive or impossible (live streaming).
+
+        int64_t durationUs = lastGranulePosition * 1000000ll / mVi.rate;
+
+        mMeta->setInt64(kKeyDuration, durationUs);
+
+        buildTableOfContents();
+    }
+
     return OK;
 }
 
+void MyVorbisExtractor::buildTableOfContents() {
+    off64_t offset = mFirstDataOffset;
+    Page page;
+    ssize_t pageSize;
+    while ((pageSize = readPage(offset, &page)) > 0) {
+        mTableOfContents.push();
+
+        TOCEntry &entry =
+            mTableOfContents.editItemAt(mTableOfContents.size() - 1);
+
+        entry.mPageOffset = offset;
+        entry.mTimeUs = page.mGranulePosition * 1000000ll / mVi.rate;
+
+        offset += (size_t)pageSize;
+    }
+
+    // Limit the maximum amount of RAM we spend on the table of contents,
+    // if necessary thin out the table evenly to trim it down to maximum
+    // size.
+
+    static const size_t kMaxTOCSize = 8192;
+    static const size_t kMaxNumTOCEntries = kMaxTOCSize / sizeof(TOCEntry);
+
+    size_t numerator = mTableOfContents.size();
+
+    if (numerator > kMaxNumTOCEntries) {
+        size_t denom = numerator - kMaxNumTOCEntries;
+
+        size_t accum = 0;
+        for (ssize_t i = mTableOfContents.size() - 1; i >= 0; --i) {
+            accum += denom;
+            if (accum >= numerator) {
+                mTableOfContents.removeAt(i);
+                accum -= numerator;
+            }
+        }
+    }
+}
+
 status_t MyVorbisExtractor::verifyHeader(
         MediaBuffer *buffer, uint8_t type) {
     const uint8_t *data =
diff --git a/media/libstagefright/codecs/mp3dec/src/pvmp3_decode_header.cpp b/media/libstagefright/codecs/mp3dec/src/pvmp3_decode_header.cpp
index 8b0250a..d443b7c 100644
--- a/media/libstagefright/codecs/mp3dec/src/pvmp3_decode_header.cpp
+++ b/media/libstagefright/codecs/mp3dec/src/pvmp3_decode_header.cpp
@@ -121,9 +121,11 @@
     uint32  temp;
 
     /*
-     *  Verify that at least the header is complete
+     * Verify that at least the header is complete
+     * Note that SYNC_WORD_LNGTH is in unit of bits, but inputBufferCurrentLength
+     * is in unit of bytes.
      */
-    if (inputStream->inputBufferCurrentLength < (SYNC_WORD_LNGTH + 21))
+    if (inputStream->inputBufferCurrentLength < ((SYNC_WORD_LNGTH + 21) >> 3))
     {
         return NO_ENOUGH_MAIN_DATA_ERROR;
     }
diff --git a/media/libstagefright/colorconversion/ColorConverter.cpp b/media/libstagefright/colorconversion/ColorConverter.cpp
index d518c97..3b92e5d 100644
--- a/media/libstagefright/colorconversion/ColorConverter.cpp
+++ b/media/libstagefright/colorconversion/ColorConverter.cpp
@@ -187,7 +187,7 @@
 
 status_t ColorConverter::convertYUV420Planar(
         const BitmapParams &src, const BitmapParams &dst) {
-    if (!((dst.mWidth & 3) == 0
+    if (!((dst.mWidth & 1) == 0
             && (src.mCropLeft & 1) == 0
             && src.cropWidth() == dst.cropWidth()
             && src.cropHeight() == dst.cropHeight())) {
diff --git a/media/libstagefright/include/OMX.h b/media/libstagefright/include/OMX.h
index 5fed98a..ec3e5fa 100644
--- a/media/libstagefright/include/OMX.h
+++ b/media/libstagefright/include/OMX.h
@@ -62,6 +62,9 @@
     virtual status_t enableGraphicBuffers(
             node_id node, OMX_U32 port_index, OMX_BOOL enable);
 
+    virtual status_t getGraphicBufferUsage(
+            node_id node, OMX_U32 port_index, OMX_U32* usage);
+
     virtual status_t storeMetaDataInBuffers(
             node_id node, OMX_U32 port_index, OMX_BOOL enable);
 
diff --git a/media/libstagefright/include/OMXNodeInstance.h b/media/libstagefright/include/OMXNodeInstance.h
index 86c102c..ca2578f 100644
--- a/media/libstagefright/include/OMXNodeInstance.h
+++ b/media/libstagefright/include/OMXNodeInstance.h
@@ -50,6 +50,9 @@
     status_t setConfig(OMX_INDEXTYPE index, const void *params, size_t size);
 
     status_t enableGraphicBuffers(OMX_U32 portIndex, OMX_BOOL enable);
+
+    status_t getGraphicBufferUsage(OMX_U32 portIndex, OMX_U32* usage);
+
     status_t storeMetaDataInBuffers(OMX_U32 portIndex, OMX_BOOL enable);
 
     status_t useBuffer(
diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp
index 6056739..7d4bc6e 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.cpp
+++ b/media/libstagefright/mpeg2ts/ATSParser.cpp
@@ -32,6 +32,7 @@
 #include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/MediaErrors.h>
 #include <media/stagefright/MetaData.h>
+#include <media/IStreamSource.h>
 #include <utils/KeyedVector.h>
 
 namespace android {
@@ -49,7 +50,9 @@
             unsigned pid, unsigned payload_unit_start_indicator,
             ABitReader *br);
 
-    void signalDiscontinuity(DiscontinuityType type);
+    void signalDiscontinuity(
+            DiscontinuityType type, const sp<AMessage> &extra);
+
     void signalEOS(status_t finalResult);
 
     sp<MediaSource> getSource(SourceType type);
@@ -83,7 +86,9 @@
             unsigned payload_unit_start_indicator,
             ABitReader *br);
 
-    void signalDiscontinuity(DiscontinuityType type);
+    void signalDiscontinuity(
+            DiscontinuityType type, const sp<AMessage> &extra);
+
     void signalEOS(status_t finalResult);
 
     sp<MediaSource> getSource(SourceType type);
@@ -100,6 +105,7 @@
     sp<AnotherPacketSource> mSource;
     bool mPayloadStarted;
     DiscontinuityType mPendingDiscontinuity;
+    sp<AMessage> mPendingDiscontinuityExtra;
 
     ElementaryStreamQueue mQueue;
 
@@ -112,7 +118,8 @@
 
     void extractAACFrames(const sp<ABuffer> &buffer);
 
-    void deferDiscontinuity(DiscontinuityType type);
+    void deferDiscontinuity(
+            DiscontinuityType type, const sp<AMessage> &extra);
 
     DISALLOW_EVIL_CONSTRUCTORS(Stream);
 };
@@ -150,9 +157,10 @@
     return true;
 }
 
-void ATSParser::Program::signalDiscontinuity(DiscontinuityType type) {
+void ATSParser::Program::signalDiscontinuity(
+        DiscontinuityType type, const sp<AMessage> &extra) {
     for (size_t i = 0; i < mStreams.size(); ++i) {
-        mStreams.editValueAt(i)->signalDiscontinuity(type);
+        mStreams.editValueAt(i)->signalDiscontinuity(type, extra);
     }
 }
 
@@ -283,7 +291,8 @@
             mStreams.add(info.mPID, stream);
 
             if (PIDsChanged) {
-                stream->signalDiscontinuity(DISCONTINUITY_FORMATCHANGE);
+                sp<AMessage> extra;
+                stream->signalDiscontinuity(DISCONTINUITY_FORMATCHANGE, extra);
             }
         }
     }
@@ -360,13 +369,25 @@
     size_t payloadSizeBits = br->numBitsLeft();
     CHECK_EQ(payloadSizeBits % 8, 0u);
 
-    CHECK_LE(mBuffer->size() + payloadSizeBits / 8, mBuffer->capacity());
+    size_t neededSize = mBuffer->size() + payloadSizeBits / 8;
+    if (mBuffer->capacity() < neededSize) {
+        // Increment in multiples of 64K.
+        neededSize = (neededSize + 65535) & ~65535;
+
+        LOGI("resizing buffer to %d bytes", neededSize);
+
+        sp<ABuffer> newBuffer = new ABuffer(neededSize);
+        memcpy(newBuffer->data(), mBuffer->data(), mBuffer->size());
+        newBuffer->setRange(0, mBuffer->size());
+        mBuffer = newBuffer;
+    }
 
     memcpy(mBuffer->data() + mBuffer->size(), br->data(), payloadSizeBits / 8);
     mBuffer->setRange(0, mBuffer->size() + payloadSizeBits / 8);
 }
 
-void ATSParser::Stream::signalDiscontinuity(DiscontinuityType type) {
+void ATSParser::Stream::signalDiscontinuity(
+        DiscontinuityType type, const sp<AMessage> &extra) {
     mPayloadStarted = false;
     mBuffer->setRange(0, 0);
 
@@ -378,10 +399,21 @@
 
             mQueue.clear(!isASeek);
 
+            uint64_t resumeAtPTS;
+            if (extra != NULL
+                    && extra->findInt64(
+                        IStreamListener::kKeyResumeAtPTS,
+                        (int64_t *)&resumeAtPTS)) {
+                int64_t resumeAtMediaTimeUs =
+                    mProgram->convertPTSToTimestamp(resumeAtPTS);
+
+                extra->setInt64("resume-at-mediatimeUs", resumeAtMediaTimeUs);
+            }
+
             if (mSource != NULL) {
-                mSource->queueDiscontinuity(type);
+                mSource->queueDiscontinuity(type, extra);
             } else {
-                deferDiscontinuity(type);
+                deferDiscontinuity(type, extra);
             }
             break;
         }
@@ -392,10 +424,12 @@
     }
 }
 
-void ATSParser::Stream::deferDiscontinuity(DiscontinuityType type) {
+void ATSParser::Stream::deferDiscontinuity(
+        DiscontinuityType type, const sp<AMessage> &extra) {
     if (type > mPendingDiscontinuity) {
         // Only upgrade discontinuities.
         mPendingDiscontinuity = type;
+        mPendingDiscontinuityExtra = extra;
     }
 }
 
@@ -596,8 +630,10 @@
                 mSource = new AnotherPacketSource(meta);
 
                 if (mPendingDiscontinuity != DISCONTINUITY_NONE) {
-                    mSource->queueDiscontinuity(mPendingDiscontinuity);
+                    mSource->queueDiscontinuity(
+                            mPendingDiscontinuity, mPendingDiscontinuityExtra);
                     mPendingDiscontinuity = DISCONTINUITY_NONE;
+                    mPendingDiscontinuityExtra.clear();
                 }
 
                 mSource->queueAccessUnit(accessUnit);
@@ -639,9 +675,10 @@
     parseTS(&br);
 }
 
-void ATSParser::signalDiscontinuity(DiscontinuityType type) {
+void ATSParser::signalDiscontinuity(
+        DiscontinuityType type, const sp<AMessage> &extra) {
     for (size_t i = 0; i < mPrograms.size(); ++i) {
-        mPrograms.editItemAt(i)->signalDiscontinuity(type);
+        mPrograms.editItemAt(i)->signalDiscontinuity(type, extra);
     }
 }
 
diff --git a/media/libstagefright/mpeg2ts/ATSParser.h b/media/libstagefright/mpeg2ts/ATSParser.h
index 455f9d5..3936f05 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.h
+++ b/media/libstagefright/mpeg2ts/ATSParser.h
@@ -41,7 +41,10 @@
     ATSParser();
 
     void feedTSPacket(const void *data, size_t size);
-    void signalDiscontinuity(DiscontinuityType type);
+
+    void signalDiscontinuity(
+            DiscontinuityType type, const sp<AMessage> &extra);
+
     void signalEOS(status_t finalResult);
 
     enum SourceType {
diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
index 0ad883b..59de17e 100644
--- a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
+++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
@@ -136,9 +136,11 @@
 }
 
 void AnotherPacketSource::queueDiscontinuity(
-        ATSParser::DiscontinuityType type) {
+        ATSParser::DiscontinuityType type,
+        const sp<AMessage> &extra) {
     sp<ABuffer> buffer = new ABuffer(0);
     buffer->meta()->setInt32("discontinuity", static_cast<int32_t>(type));
+    buffer->meta()->setMessage("extra", extra);
 
     Mutex::Autolock autoLock(mLock);
 
diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.h b/media/libstagefright/mpeg2ts/AnotherPacketSource.h
index 6fe93f8..439c785 100644
--- a/media/libstagefright/mpeg2ts/AnotherPacketSource.h
+++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.h
@@ -46,7 +46,10 @@
     status_t nextBufferTime(int64_t *timeUs);
 
     void queueAccessUnit(const sp<ABuffer> &buffer);
-    void queueDiscontinuity(ATSParser::DiscontinuityType type);
+
+    void queueDiscontinuity(
+            ATSParser::DiscontinuityType type, const sp<AMessage> &extra);
+
     void signalEOS(status_t result);
 
     status_t dequeueAccessUnit(sp<ABuffer> *buffer);
diff --git a/media/libstagefright/omx/OMX.cpp b/media/libstagefright/omx/OMX.cpp
index 3638f41..4b1c3a7 100644
--- a/media/libstagefright/omx/OMX.cpp
+++ b/media/libstagefright/omx/OMX.cpp
@@ -297,6 +297,11 @@
     return findInstance(node)->enableGraphicBuffers(port_index, enable);
 }
 
+status_t OMX::getGraphicBufferUsage(
+        node_id node, OMX_U32 port_index, OMX_U32* usage) {
+    return findInstance(node)->getGraphicBufferUsage(port_index, usage);
+}
+
 status_t OMX::storeMetaDataInBuffers(
         node_id node, OMX_U32 port_index, OMX_BOOL enable) {
     return findInstance(node)->storeMetaDataInBuffers(port_index, enable);
diff --git a/media/libstagefright/omx/OMXNodeInstance.cpp b/media/libstagefright/omx/OMXNodeInstance.cpp
index c7c1409..6cbd599 100644
--- a/media/libstagefright/omx/OMXNodeInstance.cpp
+++ b/media/libstagefright/omx/OMXNodeInstance.cpp
@@ -302,6 +302,45 @@
     return OK;
 }
 
+status_t OMXNodeInstance::getGraphicBufferUsage(
+        OMX_U32 portIndex, OMX_U32* usage) {
+    Mutex::Autolock autoLock(mLock);
+
+    OMX_INDEXTYPE index;
+    OMX_ERRORTYPE err = OMX_GetExtensionIndex(
+            mHandle,
+            const_cast<OMX_STRING>(
+                    "OMX.google.android.index.getAndroidNativeBufferUsage"),
+            &index);
+
+    if (err != OMX_ErrorNone) {
+        LOGE("OMX_GetExtensionIndex failed");
+
+        return StatusFromOMXError(err);
+    }
+
+    OMX_VERSIONTYPE ver;
+    ver.s.nVersionMajor = 1;
+    ver.s.nVersionMinor = 0;
+    ver.s.nRevision = 0;
+    ver.s.nStep = 0;
+    GetAndroidNativeBufferUsageParams params = {
+        sizeof(GetAndroidNativeBufferUsageParams), ver, portIndex, 0,
+    };
+
+    err = OMX_GetParameter(mHandle, index, &params);
+
+    if (err != OMX_ErrorNone) {
+        LOGE("OMX_GetAndroidNativeBufferUsage failed with error %d (0x%08x)",
+                err, err);
+        return UNKNOWN_ERROR;
+    }
+
+    *usage = params.nUsage;
+
+    return OK;
+}
+
 status_t OMXNodeInstance::storeMetaDataInBuffers(
         OMX_U32 portIndex,
         OMX_BOOL enable) {
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 704da72..a07ebfc 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -1629,6 +1629,7 @@
                     track->mState = TrackBase::ACTIVE;
                     param = AudioMixer::RAMP_VOLUME;
                 }
+                mAudioMixer->setParameter(AudioMixer::RESAMPLE, AudioMixer::RESET, NULL);
             } else if (cblk->server != 0) {
                 // If the track is stopped before the first frame was mixed,
                 // do not apply ramp
@@ -3855,9 +3856,12 @@
             mActiveTrack.clear();
             return status;
         }
-        mActiveTrack->mState = TrackBase::RESUMING;
         mRsmpInIndex = mFrameCount;
         mBytesRead = 0;
+        if (mResampler != NULL) {
+            mResampler->reset();
+        }
+        mActiveTrack->mState = TrackBase::RESUMING;
         // signal thread to start
         LOGV("Signal record thread");
         mWaitWorkCV.signal();
diff --git a/services/audioflinger/AudioMixer.cpp b/services/audioflinger/AudioMixer.cpp
index 433f1f7..50dcda7 100644
--- a/services/audioflinger/AudioMixer.cpp
+++ b/services/audioflinger/AudioMixer.cpp
@@ -220,6 +220,12 @@
                 return NO_ERROR;
             }
         }
+        if (name == RESET) {
+            track_t& track = mState.tracks[ mActiveTrack ];
+            track.resetResampler();
+            invalidateState(1<<mActiveTrack);
+            return NO_ERROR;
+        }
         break;
     case RAMP_VOLUME:
     case VOLUME:
@@ -289,6 +295,13 @@
     return resampler != 0;
 }
 
+void AudioMixer::track_t::resetResampler()
+{
+    if (resampler != 0) {
+        resampler->reset();
+    }
+}
+
 inline
 void AudioMixer::track_t::adjustVolumeRamp(bool aux)
 {
diff --git a/services/audioflinger/AudioMixer.h b/services/audioflinger/AudioMixer.h
index aee3e17..88408a7 100644
--- a/services/audioflinger/AudioMixer.h
+++ b/services/audioflinger/AudioMixer.h
@@ -67,6 +67,7 @@
         AUX_BUFFER      = 0x4003,
         // for TARGET RESAMPLE
         SAMPLE_RATE     = 0x4100,
+        RESET           = 0x4101,
         // for TARGET VOLUME (8 channels max)
         VOLUME0         = 0x4200,
         VOLUME1         = 0x4201,
@@ -163,6 +164,7 @@
 
         bool        setResampler(uint32_t sampleRate, uint32_t devSampleRate);
         bool        doesResample() const;
+        void        resetResampler();
         void        adjustVolumeRamp(bool aux);
     };
 
diff --git a/services/audioflinger/AudioPolicyManagerBase.cpp b/services/audioflinger/AudioPolicyManagerBase.cpp
index bfc80db..74be4e0 100644
--- a/services/audioflinger/AudioPolicyManagerBase.cpp
+++ b/services/audioflinger/AudioPolicyManagerBase.cpp
@@ -122,12 +122,12 @@
         // request routing change if necessary
         uint32_t newDevice = getNewDevice(mHardwareOutput, false);
 #ifdef WITH_A2DP
+        checkA2dpSuspend();
         checkOutputForAllStrategies();
         // A2DP outputs must be closed after checkOutputForAllStrategies() is executed
         if (state == AudioSystem::DEVICE_STATE_UNAVAILABLE && AudioSystem::isA2dpDevice(device)) {
             closeA2dpOutputs();
         }
-        checkA2dpSuspend();
 #endif
         updateDeviceForStrategy();
         setOutputDevice(mHardwareOutput, newDevice);
@@ -269,8 +269,8 @@
     // check for device and output changes triggered by new phone state
     newDevice = getNewDevice(mHardwareOutput, false);
 #ifdef WITH_A2DP
-    checkOutputForAllStrategies();
     checkA2dpSuspend();
+    checkOutputForAllStrategies();
 #endif
     updateDeviceForStrategy();
 
@@ -378,8 +378,8 @@
     // check for device and output changes triggered by new phone state
     uint32_t newDevice = getNewDevice(mHardwareOutput, false);
 #ifdef WITH_A2DP
-    checkOutputForAllStrategies();
     checkA2dpSuspend();
+    checkOutputForAllStrategies();
 #endif
     updateDeviceForStrategy();
     setOutputDevice(mHardwareOutput, newDevice);
@@ -1624,7 +1624,7 @@
             if (device) break;
 #ifdef WITH_A2DP
             // when not in a phone call, phone strategy should route STREAM_VOICE_CALL to A2DP
-            if (!isInCall()) {
+            if (!isInCall() && !mA2dpSuspended) {
                 device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP;
                 if (device) break;
                 device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES;
@@ -1647,7 +1647,7 @@
 #ifdef WITH_A2DP
             // when not in a phone call, phone strategy should route STREAM_VOICE_CALL to
             // A2DP speaker when forcing to speaker output
-            if (!isInCall()) {
+            if (!isInCall() && !mA2dpSuspended) {
                 device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER;
                 if (device) break;
             }
@@ -1687,7 +1687,7 @@
             device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADSET;
         }
 #ifdef WITH_A2DP
-        if ((mA2dpOutput != 0) &&
+        if ((mA2dpOutput != 0) && !mA2dpSuspended &&
                 (strategy != STRATEGY_SONIFICATION || a2dpUsedForSonification())) {
             if (device2 == 0) {
                 device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP;
diff --git a/services/audioflinger/AudioResampler.cpp b/services/audioflinger/AudioResampler.cpp
index 5dabacb..5c3b43f 100644
--- a/services/audioflinger/AudioResampler.cpp
+++ b/services/audioflinger/AudioResampler.cpp
@@ -148,6 +148,12 @@
     mVolume[1] = right;
 }
 
+void AudioResampler::reset() {
+    mInputIndex = 0;
+    mPhaseFraction = 0;
+    mBuffer.frameCount = 0;
+}
+
 // ----------------------------------------------------------------------------
 
 void AudioResamplerOrder1::resample(int32_t* out, size_t outFrameCount,
diff --git a/services/audioflinger/AudioResampler.h b/services/audioflinger/AudioResampler.h
index 2dfac76..9f06c1c 100644
--- a/services/audioflinger/AudioResampler.h
+++ b/services/audioflinger/AudioResampler.h
@@ -53,6 +53,8 @@
     virtual void resample(int32_t* out, size_t outFrameCount,
             AudioBufferProvider* provider) = 0;
 
+    virtual void reset();
+
 protected:
     // number of bits for phase fraction - 30 bits allows nearly 2x downsampling
     static const int kNumPhaseBits = 30;