Merge "Send broadcast intent when configured location providers change." into gingerbread
diff --git a/api/current.xml b/api/current.xml
index 47a8472..1fef0ec 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -76089,6 +76089,19 @@
  visibility="public"
 >
 </method>
+<method name="getPreviewFpsRange"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="range" type="int[]">
+</parameter>
+</method>
 <method name="getPreviewFrameRate"
  return="int"
  abstract="false"
@@ -76096,7 +76109,7 @@
  synchronized="false"
  static="false"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 </method>
@@ -76221,6 +76234,17 @@
  visibility="public"
 >
 </method>
+<method name="getSupportedPreviewFpsRange"
+ return="java.util.List&lt;int[]&gt;"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="getSupportedPreviewFrameRates"
  return="java.util.List&lt;java.lang.Integer&gt;"
  abstract="false"
@@ -76228,7 +76252,7 @@
  synchronized="false"
  static="false"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 </method>
@@ -76610,7 +76634,7 @@
 <parameter name="pixel_format" type="int">
 </parameter>
 </method>
-<method name="setPreviewFrameRate"
+<method name="setPreviewFpsRange"
  return="void"
  abstract="false"
  native="false"
@@ -76620,6 +76644,21 @@
  deprecated="not deprecated"
  visibility="public"
 >
+<parameter name="min" type="int">
+</parameter>
+<parameter name="max" type="int">
+</parameter>
+</method>
+<method name="setPreviewFrameRate"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="deprecated"
+ visibility="public"
+>
 <parameter name="fps" type="int">
 </parameter>
 </method>
@@ -77033,6 +77072,28 @@
  visibility="public"
 >
 </field>
+<field name="PREVIEW_FPS_MAX_INDEX"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PREVIEW_FPS_MIN_INDEX"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="SCENE_MODE_ACTION"
  type="java.lang.String"
  transient="false"
@@ -78424,7 +78485,7 @@
  type="float"
  transient="false"
  volatile="false"
- value="0.001f"
+ value="0.0010f"
  static="true"
  final="true"
  deprecated="not deprecated"
@@ -224842,7 +224903,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="arg0" type="T">
+<parameter name="t" type="T">
 </parameter>
 </method>
 </interface>
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index e432a47..21cb3a8 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -1224,7 +1224,6 @@
          * The array index of minimum preview fps for use with {@link
          * #getPreviewFpsRange(int[])} or {@link
          * #getSupportedPreviewFpsRange()}.
-         * @hide
          */
         public static final int PREVIEW_FPS_MIN_INDEX = 0;
 
@@ -1232,7 +1231,6 @@
          * The array index of maximum preview fps for use with {@link
          * #getPreviewFpsRange(int[])} or {@link
          * #getSupportedPreviewFpsRange()}.
-         * @hide
          */
         public static final int PREVIEW_FPS_MAX_INDEX = 1;
 
@@ -1471,7 +1469,9 @@
          * target frame rate. The actual frame rate depends on the driver.
          *
          * @param fps the frame rate (frames per second)
+         * @deprecated replaced by {@link #setPreviewFpsRange(int,int)}
          */
+        @Deprecated
         public void setPreviewFrameRate(int fps) {
             set(KEY_PREVIEW_FRAME_RATE, fps);
         }
@@ -1482,7 +1482,9 @@
          * depends on the driver.
          *
          * @return the frame rate setting (frames per second)
+         * @deprecated replaced by {@link #getPreviewFpsRange(int[])}
          */
+        @Deprecated
         public int getPreviewFrameRate() {
             return getInt(KEY_PREVIEW_FRAME_RATE);
         }
@@ -1492,7 +1494,9 @@
          *
          * @return a list of supported preview frame rates. null if preview
          *         frame rate setting is not supported.
+         * @deprecated replaced by {@link #getSupportedPreviewFpsRange()}
          */
+        @Deprecated
         public List<Integer> getSupportedPreviewFrameRates() {
             String str = get(KEY_PREVIEW_FRAME_RATE + SUPPORTED_VALUES_SUFFIX);
             return splitInt(str);
@@ -1500,7 +1504,7 @@
 
         /**
          * Sets the maximum and maximum preview fps. This controls the rate of
-         * preview frames received in {@link #PreviewCallback}. The minimum and
+         * preview frames received in {@link PreviewCallback}. The minimum and
          * maximum preview fps must be one of the elements from {@link
          * #getSupportedPreviewFpsRange}.
          *
@@ -1509,7 +1513,6 @@
          * @throws RuntimeException if fps range is invalid.
          * @see #setPreviewCallbackWithBuffer(Camera.PreviewCallback)
          * @see #getSupportedPreviewFpsRange()
-         * @hide
          */
         public void setPreviewFpsRange(int min, int max) {
             set(KEY_PREVIEW_FPS_RANGE, "" + min + "," + max);
@@ -1523,12 +1526,11 @@
          * @see #PREVIEW_FPS_MIN_INDEX
          * @see #PREVIEW_FPS_MAX_INDEX
          * @see #getSupportedPreviewFpsRange()
-         * @hide
          */
         public void getPreviewFpsRange(int[] range) {
             if (range == null || range.length != 2) {
                 throw new IllegalArgumentException(
-                        "range must be an float array with two elements.");
+                        "range must be an array with two elements.");
             }
             splitInt(get(KEY_PREVIEW_FPS_RANGE), range);
         }
@@ -1549,7 +1551,6 @@
          *         minimum fps).
          * @see #PREVIEW_FPS_MIN_INDEX
          * @see #PREVIEW_FPS_MAX_INDEX
-         * @hide
          */
         public List<int[]> getSupportedPreviewFpsRange() {
             String str = get(KEY_PREVIEW_FPS_RANGE + SUPPORTED_VALUES_SUFFIX);
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 09fc0f0..03b721c 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1853,7 +1853,7 @@
     <!-- Item on EditText context menu. This action is used to select all text in the edit field. -->
     <string name="selectAll">Select all</string>
 
-    <!-- Item on EditText context menu. This action is used to start selecting text in the edit field. -->
+    <!-- Item on EditText context menu. This action is used to start selecting text in the edit field. [CHAR LIMIT=20] -->
     <string name="selectText">Select word</string>
 
     <!-- Item on EditText context menu.  This action is used to cut selected the text into the clipboard.  -->
diff --git a/include/media/stagefright/AudioSource.h b/include/media/stagefright/AudioSource.h
index 2597e9e..1af4254 100644
--- a/include/media/stagefright/AudioSource.h
+++ b/include/media/stagefright/AudioSource.h
@@ -49,7 +49,17 @@
     virtual ~AudioSource();
 
 private:
-    enum { kMaxBufferSize = 2048 };
+    enum {
+        kMaxBufferSize = 2048,
+
+        // After the initial mute, we raise the volume linearly
+        // over kAutoRampDurationUs.
+        kAutoRampDurationUs = 300000,
+
+        // This is the initial mute duration to suppress
+        // the video recording signal tone
+        kAutoRampStartUs = 700000,
+      };
 
     AudioRecord *mRecord;
     status_t mInitCheck;
@@ -67,6 +77,12 @@
 
     void trackMaxAmplitude(int16_t *data, int nSamples);
 
+    // This is used to raise the volume from mute to the
+    // actual level linearly.
+    void rampVolume(
+        int32_t startFrame, int32_t rampDurationFrames,
+        uint8_t *data,   size_t bytes);
+
     AudioSource(const AudioSource &);
     AudioSource &operator=(const AudioSource &);
 };
diff --git a/include/media/stagefright/OMXCodec.h b/include/media/stagefright/OMXCodec.h
index 010ded1..875bc5b 100644
--- a/include/media/stagefright/OMXCodec.h
+++ b/include/media/stagefright/OMXCodec.h
@@ -102,6 +102,7 @@
         kInputBufferSizesAreBogus             = 512,
         kSupportsMultipleFramesPerInputBuffer = 1024,
         kAvoidMemcopyInputRecordingFrames     = 2048,
+        kRequiresLargerEncoderOutputBuffer    = 4096,
     };
 
     struct BufferInfo {
diff --git a/libs/utils/ZipFileRO.cpp b/libs/utils/ZipFileRO.cpp
index 604f558..a0e01c6 100644
--- a/libs/utils/ZipFileRO.cpp
+++ b/libs/utils/ZipFileRO.cpp
@@ -508,8 +508,8 @@
         }
 
         if (get4LE(lfhBuf) != kLFHSignature) {
-            LOGW("didn't find signature at start of lfh, offset=%ld\n",
-                localHdrOffset);
+            LOGW("didn't find signature at start of lfh, offset=%ld (got 0x%08lx, expected 0x%08x)\n",
+                localHdrOffset, get4LE(lfhBuf), kLFHSignature);
             return false;
         }
 
diff --git a/media/libstagefright/AudioSource.cpp b/media/libstagefright/AudioSource.cpp
index c8dfede..4c729e4 100644
--- a/media/libstagefright/AudioSource.cpp
+++ b/media/libstagefright/AudioSource.cpp
@@ -40,6 +40,7 @@
       mGroup(NULL) {
 
     LOGV("sampleRate: %d, channels: %d", sampleRate, channels);
+    CHECK(channels == 1 || channels == 2);
     uint32_t flags = AudioRecord::RECORD_AGC_ENABLE |
                      AudioRecord::RECORD_NS_ENABLE  |
                      AudioRecord::RECORD_IIR_ENABLE;
@@ -158,6 +159,38 @@
 
 }
 
+void AudioSource::rampVolume(
+        int32_t startFrame, int32_t rampDurationFrames,
+        uint8_t *data,   size_t bytes) {
+
+    const int32_t kShift = 14;
+    int32_t fixedMultiplier = (startFrame << kShift) / rampDurationFrames;
+    const int32_t nChannels = mRecord->channelCount();
+    int32_t stopFrame = startFrame + bytes / sizeof(int16_t);
+    int16_t *frame = (int16_t *) data;
+    if (stopFrame > rampDurationFrames) {
+        stopFrame = rampDurationFrames;
+    }
+
+    while (startFrame < stopFrame) {
+        if (nChannels == 1) {  // mono
+            frame[0] = (frame[0] * fixedMultiplier) >> kShift;
+            ++frame;
+            ++startFrame;
+        } else {               // stereo
+            frame[0] = (frame[0] * fixedMultiplier) >> kShift;
+            frame[1] = (frame[1] * fixedMultiplier) >> kShift;
+            frame += 2;
+            startFrame += 2;
+        }
+
+        // Update the multiplier every 4 frames
+        if ((startFrame & 3) == 0) {
+            fixedMultiplier = (startFrame << kShift) / rampDurationFrames;
+        }
+    }
+}
+
 status_t AudioSource::read(
         MediaBuffer **out, const ReadOptions *options) {
     *out = NULL;
@@ -242,6 +275,19 @@
             continue;
         }
 
+        if (mPrevSampleTimeUs - mStartTimeUs < kAutoRampStartUs) {
+            // Mute the initial video recording signal
+            memset((uint8_t *) buffer->data(), 0, n);
+        } else if (mPrevSampleTimeUs - mStartTimeUs < kAutoRampStartUs + kAutoRampDurationUs) {
+            int32_t autoRampDurationFrames =
+                    (kAutoRampDurationUs * sampleRate + 500000LL) / 1000000LL;
+
+            int32_t autoRampStartFrames =
+                    (kAutoRampStartUs * sampleRate + 500000LL) / 1000000LL;
+
+            int32_t nFrames = numFramesRecorded - autoRampStartFrames;
+            rampVolume(nFrames, autoRampDurationFrames, (uint8_t *) buffer->data(), n);
+        }
         if (mTrackMaxAmplitude) {
             trackMaxAmplitude((int16_t *) buffer->data(), n >> 1);
         }
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index a0cd5c3..7e25e88 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -196,7 +196,6 @@
       mExtractorFlags(0),
       mLastVideoBuffer(NULL),
       mVideoBuffer(NULL),
-      mRTSPTimeOffset(0),
       mSuspensionState(NULL) {
     CHECK_EQ(mClient.connect(), OK);
 
@@ -739,7 +738,10 @@
 }
 
 status_t AwesomePlayer::getPosition(int64_t *positionUs) {
-    if (mSeeking) {
+    if (mRTSPController != NULL) {
+        *positionUs = mRTSPController->getNormalPlayTimeUs();
+    }
+    else if (mSeeking) {
         *positionUs = mSeekTimeUs;
     } else if (mVideoSource != NULL) {
         Mutex::Autolock autoLock(mMiscStateLock);
@@ -750,10 +752,6 @@
         *positionUs = 0;
     }
 
-    if (mRTSPController != NULL) {
-        *positionUs += mRTSPTimeOffset;
-    }
-
     return OK;
 }
 
@@ -770,13 +768,10 @@
 
 status_t AwesomePlayer::seekTo_l(int64_t timeUs) {
     if (mRTSPController != NULL) {
-        pause_l();
         mRTSPController->seek(timeUs);
-        play_l();
 
         notifyListener_l(MEDIA_SEEK_COMPLETE);
         mSeekNotificationSent = true;
-        mRTSPTimeOffset = timeUs;
         return OK;
     }
 
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index 4741b1d..165cec9 100644
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -353,6 +353,15 @@
         quirks |= kRequiresLoadedToIdleAfterAllocation;
         quirks |= kRequiresAllocateBufferOnInputPorts;
         quirks |= kRequiresAllocateBufferOnOutputPorts;
+        if (!strncmp(componentName, "OMX.qcom.video.encoder.avc", 26)) {
+
+            // The AVC encoder advertises the size of output buffers
+            // based on the input video resolution and assumes
+            // the worst/least compression ratio is 0.5. It is found that
+            // sometimes, the output buffer size is larger than
+            // size advertised by the encoder.
+            quirks |= kRequiresLargerEncoderOutputBuffer;
+        }
     }
     if (!strncmp(componentName, "OMX.qcom.7x30.video.encoder.", 28)) {
     }
@@ -906,6 +915,10 @@
     video_def->nBitrate = bitRate;  // Q16 format
     video_def->eCompressionFormat = compressionFormat;
     video_def->eColorFormat = OMX_COLOR_FormatUnused;
+    if (mQuirks & kRequiresLargerEncoderOutputBuffer) {
+        // Increases the output buffer size
+        def.nBufferSize = ((def.nBufferSize * 3) >> 1);
+    }
 
     err = mOMX->setParameter(
             mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
diff --git a/media/libstagefright/include/ARTSPController.h b/media/libstagefright/include/ARTSPController.h
index 7020564..7016880 100644
--- a/media/libstagefright/include/ARTSPController.h
+++ b/media/libstagefright/include/ARTSPController.h
@@ -41,6 +41,8 @@
     virtual sp<MetaData> getTrackMetaData(
             size_t index, uint32_t flags);
 
+    int64_t getNormalPlayTimeUs();
+
     void onMessageReceived(const sp<AMessage> &msg);
 
 protected:
diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h
index 49b5c78..55e2c36 100644
--- a/media/libstagefright/include/AwesomePlayer.h
+++ b/media/libstagefright/include/AwesomePlayer.h
@@ -180,7 +180,6 @@
 
     sp<ALooper> mLooper;
     sp<ARTSPController> mRTSPController;
-    int64_t mRTSPTimeOffset;
     sp<ARTPSession> mRTPSession;
     sp<UDPPusher> mRTPPusher, mRTCPPusher;
 
diff --git a/media/libstagefright/rtsp/AAMRAssembler.cpp b/media/libstagefright/rtsp/AAMRAssembler.cpp
index c56578b..154ba31 100644
--- a/media/libstagefright/rtsp/AAMRAssembler.cpp
+++ b/media/libstagefright/rtsp/AAMRAssembler.cpp
@@ -178,12 +178,8 @@
         }
     }
 
-    uint64_t ntpTime;
-    CHECK(buffer->meta()->findInt64(
-                "ntp-time", (int64_t *)&ntpTime));
-
     sp<ABuffer> accessUnit = new ABuffer(totalSize);
-    accessUnit->meta()->setInt64("ntp-time", ntpTime);
+    CopyTimes(accessUnit, buffer);
 
     size_t dstOffset = 0;
     for (size_t i = 0; i < tableOfContents.size(); ++i) {
diff --git a/media/libstagefright/rtsp/AAVCAssembler.cpp b/media/libstagefright/rtsp/AAVCAssembler.cpp
index b22de2c..6b1e292 100644
--- a/media/libstagefright/rtsp/AAVCAssembler.cpp
+++ b/media/libstagefright/rtsp/AAVCAssembler.cpp
@@ -155,7 +155,7 @@
         sp<ABuffer> unit = new ABuffer(nalSize);
         memcpy(unit->data(), &data[2], nalSize);
 
-        PropagateTimes(buffer, unit);
+        CopyTimes(unit, buffer);
 
         addSingleNALUnit(unit);
 
@@ -287,7 +287,7 @@
     ++totalSize;
 
     sp<ABuffer> unit = new ABuffer(totalSize);
-    PropagateTimes(buffer, unit);
+    CopyTimes(unit, *queue->begin());
 
     unit->data()[0] = (nri << 5) | nalType;
 
@@ -325,10 +325,6 @@
     LOG(VERBOSE) << "Access unit complete (" << mNALUnits.size() << " nal units)";
 #endif
 
-    uint64_t ntpTime;
-    CHECK((*mNALUnits.begin())->meta()->findInt64(
-                "ntp-time", (int64_t *)&ntpTime));
-
     size_t totalSize = 0;
     for (List<sp<ABuffer> >::iterator it = mNALUnits.begin();
          it != mNALUnits.end(); ++it) {
@@ -347,7 +343,7 @@
         offset += nal->size();
     }
 
-    accessUnit->meta()->setInt64("ntp-time", ntpTime);
+    CopyTimes(accessUnit, *mNALUnits.begin());
 
 #if 0
     printf(mAccessUnitDamaged ? "X" : ".");
diff --git a/media/libstagefright/rtsp/AH263Assembler.cpp b/media/libstagefright/rtsp/AH263Assembler.cpp
index 2818041..498295c 100644
--- a/media/libstagefright/rtsp/AH263Assembler.cpp
+++ b/media/libstagefright/rtsp/AH263Assembler.cpp
@@ -128,10 +128,6 @@
     LOG(VERBOSE) << "Access unit complete (" << mPackets.size() << " packets)";
 #endif
 
-    uint64_t ntpTime;
-    CHECK((*mPackets.begin())->meta()->findInt64(
-                "ntp-time", (int64_t *)&ntpTime));
-
     size_t totalSize = 0;
     List<sp<ABuffer> >::iterator it = mPackets.begin();
     while (it != mPackets.end()) {
@@ -155,7 +151,7 @@
         ++it;
     }
 
-    accessUnit->meta()->setInt64("ntp-time", ntpTime);
+    CopyTimes(accessUnit, *mPackets.begin());
 
 #if 0
     printf(mAccessUnitDamaged ? "X" : ".");
diff --git a/media/libstagefright/rtsp/AMPEG4AudioAssembler.cpp b/media/libstagefright/rtsp/AMPEG4AudioAssembler.cpp
index 6e46361..b0d2c64 100644
--- a/media/libstagefright/rtsp/AMPEG4AudioAssembler.cpp
+++ b/media/libstagefright/rtsp/AMPEG4AudioAssembler.cpp
@@ -103,10 +103,6 @@
     LOG(VERBOSE) << "Access unit complete (" << mPackets.size() << " packets)";
 #endif
 
-    uint64_t ntpTime;
-    CHECK((*mPackets.begin())->meta()->findInt64(
-                "ntp-time", (int64_t *)&ntpTime));
-
     size_t totalSize = 0;
     List<sp<ABuffer> >::iterator it = mPackets.begin();
     while (it != mPackets.end()) {
@@ -142,7 +138,7 @@
         ++it;
     }
 
-    accessUnit->meta()->setInt64("ntp-time", ntpTime);
+    CopyTimes(accessUnit, *mPackets.begin());
 
 #if 0
     printf(mAccessUnitDamaged ? "X" : ".");
diff --git a/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp b/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp
index 7e633d7..7dd3e3f 100644
--- a/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp
+++ b/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp
@@ -101,10 +101,6 @@
     LOG(VERBOSE) << "Access unit complete (" << mPackets.size() << " nal units)";
 #endif
 
-    uint64_t ntpTime;
-    CHECK((*mPackets.begin())->meta()->findInt64(
-                "ntp-time", (int64_t *)&ntpTime));
-
     size_t totalSize = 0;
     for (List<sp<ABuffer> >::iterator it = mPackets.begin();
          it != mPackets.end(); ++it) {
@@ -120,7 +116,7 @@
         offset += nal->size();
     }
 
-    accessUnit->meta()->setInt64("ntp-time", ntpTime);
+    CopyTimes(accessUnit, *mPackets.begin());
 
 #if 0
     printf(mAccessUnitDamaged ? "X" : ".");
diff --git a/media/libstagefright/rtsp/APacketSource.cpp b/media/libstagefright/rtsp/APacketSource.cpp
index b930184..2d7738b 100644
--- a/media/libstagefright/rtsp/APacketSource.cpp
+++ b/media/libstagefright/rtsp/APacketSource.cpp
@@ -402,16 +402,41 @@
     return csd;
 }
 
+static bool GetClockRate(const AString &desc, uint32_t *clockRate) {
+    ssize_t slashPos = desc.find("/");
+    if (slashPos < 0) {
+        return false;
+    }
+
+    const char *s = desc.c_str() + slashPos + 1;
+
+    char *end;
+    unsigned long x = strtoul(s, &end, 10);
+
+    if (end == s || (*end != '\0' && *end != '/')) {
+        return false;
+    }
+
+    *clockRate = x;
+
+    return true;
+}
+
 APacketSource::APacketSource(
         const sp<ASessionDescription> &sessionDesc, size_t index)
     : mInitCheck(NO_INIT),
       mFormat(new MetaData),
-      mEOSResult(OK) {
+      mEOSResult(OK),
+      mRTPTimeBase(0),
+      mNormalPlayTimeBaseUs(0),
+      mLastNormalPlayTimeUs(0) {
     unsigned long PT;
     AString desc;
     AString params;
     sessionDesc->getFormatType(index, &PT, &desc, &params);
 
+    CHECK(GetClockRate(desc, &mClockRate));
+
     int64_t durationUs;
     if (sessionDesc->getDurationUs(&durationUs)) {
         mFormat->setInt64(kKeyDuration, durationUs);
@@ -571,6 +596,8 @@
     if (!mBuffers.empty()) {
         const sp<ABuffer> buffer = *mBuffers.begin();
 
+        updateNormalPlayTime_l(buffer);
+
         MediaBuffer *mediaBuffer = new MediaBuffer(buffer->size());
 
         int64_t timeUs;
@@ -588,6 +615,16 @@
     return mEOSResult;
 }
 
+void APacketSource::updateNormalPlayTime_l(const sp<ABuffer> &buffer) {
+    uint32_t rtpTime;
+    CHECK(buffer->meta()->findInt32("rtp-time", (int32_t *)&rtpTime));
+
+    mLastNormalPlayTimeUs =
+        (((double)rtpTime - (double)mRTPTimeBase) / mClockRate)
+            * 1000000ll
+            + mNormalPlayTimeBaseUs;
+}
+
 void APacketSource::queueAccessUnit(const sp<ABuffer> &buffer) {
     int32_t damaged;
     if (buffer->meta()->findInt32("damaged", &damaged) && damaged) {
@@ -613,4 +650,17 @@
     mBuffers.clear();
 }
 
+int64_t APacketSource::getNormalPlayTimeUs() {
+    Mutex::Autolock autoLock(mLock);
+    return mLastNormalPlayTimeUs;
+}
+
+void APacketSource::setNormalPlayTimeMapping(
+        uint32_t rtpTime, int64_t normalPlayTimeUs) {
+    Mutex::Autolock autoLock(mLock);
+
+    mRTPTimeBase = rtpTime;
+    mNormalPlayTimeBaseUs = normalPlayTimeUs;
+}
+
 }  // namespace android
diff --git a/media/libstagefright/rtsp/APacketSource.h b/media/libstagefright/rtsp/APacketSource.h
index 197af3e..3833ab1 100644
--- a/media/libstagefright/rtsp/APacketSource.h
+++ b/media/libstagefright/rtsp/APacketSource.h
@@ -45,6 +45,11 @@
 
     void flushQueue();
 
+    int64_t getNormalPlayTimeUs();
+
+    void setNormalPlayTimeMapping(
+            uint32_t rtpTime, int64_t normalPlayTimeUs);
+
 protected:
     virtual ~APacketSource();
 
@@ -58,6 +63,15 @@
     List<sp<ABuffer> > mBuffers;
     status_t mEOSResult;
 
+    uint32_t mClockRate;
+
+    uint32_t mRTPTimeBase;
+    int64_t mNormalPlayTimeBaseUs;
+
+    int64_t mLastNormalPlayTimeUs;
+
+    void updateNormalPlayTime_l(const sp<ABuffer> &buffer);
+
     DISALLOW_EVIL_CONSTRUCTORS(APacketSource);
 };
 
diff --git a/media/libstagefright/rtsp/ARTPAssembler.cpp b/media/libstagefright/rtsp/ARTPAssembler.cpp
index 24225b8..9ba2b37 100644
--- a/media/libstagefright/rtsp/ARTPAssembler.cpp
+++ b/media/libstagefright/rtsp/ARTPAssembler.cpp
@@ -35,18 +35,6 @@
     : mFirstFailureTimeUs(-1) {
 }
 
-void ARTPAssembler::PropagateTimes(
-        const sp<ABuffer> &from, const sp<ABuffer> &to) {
-    uint32_t rtpTime;
-    CHECK(from->meta()->findInt32("rtp-time", (int32_t *)&rtpTime));
-
-    uint64_t ntpTime = 0;
-    CHECK(from->meta()->findInt64("ntp-time", (int64_t *)&ntpTime));
-
-    to->meta()->setInt32("rtp-time", rtpTime);
-    to->meta()->setInt64("ntp-time", ntpTime);
-}
-
 void ARTPAssembler::onPacketReceived(const sp<ARTPSource> &source) {
     AssemblyStatus status;
     for (;;) {
@@ -75,4 +63,19 @@
     }
 }
 
+// static
+void ARTPAssembler::CopyTimes(const sp<ABuffer> &to, const sp<ABuffer> &from) {
+    uint64_t ntpTime;
+    CHECK(from->meta()->findInt64("ntp-time", (int64_t *)&ntpTime));
+
+    uint32_t rtpTime;
+    CHECK(from->meta()->findInt32("rtp-time", (int32_t *)&rtpTime));
+
+    to->meta()->setInt64("ntp-time", ntpTime);
+    to->meta()->setInt32("rtp-time", rtpTime);
+
+    // Copy the seq number.
+    to->setInt32Data(from->int32Data());
+}
+
 }  // namespace android
diff --git a/media/libstagefright/rtsp/ARTPAssembler.h b/media/libstagefright/rtsp/ARTPAssembler.h
index e598088..70ea1866 100644
--- a/media/libstagefright/rtsp/ARTPAssembler.h
+++ b/media/libstagefright/rtsp/ARTPAssembler.h
@@ -40,12 +40,11 @@
     virtual void onByeReceived() = 0;
 
 protected:
-    static void PropagateTimes(
-        const sp<ABuffer> &from, const sp<ABuffer> &to);
-
     virtual AssemblyStatus assembleMore(const sp<ARTPSource> &source) = 0;
     virtual void packetLost() = 0;
 
+    static void CopyTimes(const sp<ABuffer> &to, const sp<ABuffer> &from);
+
 private:
     int64_t mFirstFailureTimeUs;
 
diff --git a/media/libstagefright/rtsp/ARTSPController.cpp b/media/libstagefright/rtsp/ARTSPController.cpp
index 9df17cba..a89946b 100644
--- a/media/libstagefright/rtsp/ARTSPController.cpp
+++ b/media/libstagefright/rtsp/ARTSPController.cpp
@@ -138,4 +138,9 @@
     }
 }
 
+int64_t ARTSPController::getNormalPlayTimeUs() {
+    CHECK(mHandler != NULL);
+    return mHandler->getNormalPlayTimeUs();
+}
+
 }  // namespace android
diff --git a/media/libstagefright/rtsp/MyHandler.h b/media/libstagefright/rtsp/MyHandler.h
index 4c6f058..0685a47 100644
--- a/media/libstagefright/rtsp/MyHandler.h
+++ b/media/libstagefright/rtsp/MyHandler.h
@@ -23,6 +23,8 @@
 #include "ARTSPConnection.h"
 #include "ASessionDescription.h"
 
+#include <ctype.h>
+
 #include <media/stagefright/foundation/ABuffer.h>
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/ALooper.h>
@@ -33,6 +35,34 @@
 
 namespace android {
 
+static bool GetAttribute(const char *s, const char *key, AString *value) {
+    value->clear();
+
+    size_t keyLen = strlen(key);
+
+    for (;;) {
+        while (isspace(*s)) {
+            ++s;
+        }
+
+        const char *colonPos = strchr(s, ';');
+
+        size_t len =
+            (colonPos == NULL) ? strlen(s) : colonPos - s;
+
+        if (len >= keyLen + 1 && s[keyLen] == '=' && !strncmp(s, key, keyLen)) {
+            value->setTo(&s[keyLen + 1], len - keyLen - 1);
+            return true;
+        }
+
+        if (colonPos == NULL) {
+            return false;
+        }
+
+        s = colonPos + 1;
+    }
+}
+
 struct MyHandler : public AHandler {
     MyHandler(const char *url, const sp<ALooper> &looper)
         : mLooper(looper),
@@ -43,7 +73,9 @@
           mSetupTracksSuccessful(false),
           mSeekPending(false),
           mFirstAccessUnit(true),
-          mFirstAccessUnitNTP(0) {
+          mFirstAccessUnitNTP(0),
+          mNumAccessUnitsReceived(0),
+          mCheckPending(false) {
 
         mNetLooper->start(false /* runOnCallingThread */,
                           false /* canCallJava */,
@@ -76,6 +108,20 @@
         msg->post();
     }
 
+    int64_t getNormalPlayTimeUs() {
+        int64_t maxTimeUs = 0;
+        for (size_t i = 0; i < mTracks.size(); ++i) {
+            int64_t timeUs = mTracks.editItemAt(i).mPacketSource
+                ->getNormalPlayTimeUs();
+
+            if (i == 0 || timeUs > maxTimeUs) {
+                maxTimeUs = timeUs;
+            }
+        }
+
+        return maxTimeUs;
+    }
+
     virtual void onMessageReceived(const sp<AMessage> &msg) {
         switch (msg->what()) {
             case 'conn':
@@ -269,6 +315,8 @@
 
                     CHECK_EQ(response->mStatusCode, 200u);
 
+                    parsePlayResponse(response);
+
                     mDoneMsg->setInt32("result", OK);
                     mDoneMsg->post();
                     mDoneMsg = NULL;
@@ -332,16 +380,38 @@
                 break;
             }
 
+            case 'chek':
+            {
+                if (mNumAccessUnitsReceived == 0) {
+                    LOG(INFO) << "stream ended? aborting.";
+                    (new AMessage('abor', id()))->post();
+                    break;
+                }
+
+                mNumAccessUnitsReceived = 0;
+                msg->post(500000);
+                break;
+            }
+
             case 'accu':
             {
+                ++mNumAccessUnitsReceived;
+
+                if (!mCheckPending) {
+                    mCheckPending = true;
+                    sp<AMessage> check = new AMessage('chek', id());
+                    check->post(500000);
+                }
+
                 size_t trackIndex;
                 CHECK(msg->findSize("track-index", &trackIndex));
 
+                TrackInfo *track = &mTracks.editItemAt(trackIndex);
+
                 int32_t eos;
                 if (msg->findInt32("eos", &eos)) {
                     LOG(INFO) << "received BYE on track index " << trackIndex;
 #if 0
-                    TrackInfo *track = &mTracks.editItemAt(trackIndex);
                     track->mPacketSource->signalEOS(ERROR_END_OF_STREAM);
 #endif
                     return;
@@ -352,10 +422,32 @@
 
                 sp<ABuffer> accessUnit = static_cast<ABuffer *>(obj.get());
 
+                uint32_t seqNum = (uint32_t)accessUnit->int32Data();
+
+                if (seqNum < track->mFirstSeqNumInSegment) {
+                    LOG(INFO) << "dropping stale access-unit "
+                              << "(" << seqNum << " < "
+                              << track->mFirstSeqNumInSegment << ")";
+                    break;
+                }
+
                 uint64_t ntpTime;
                 CHECK(accessUnit->meta()->findInt64(
                             "ntp-time", (int64_t *)&ntpTime));
 
+                uint32_t rtpTime;
+                CHECK(accessUnit->meta()->findInt32(
+                            "rtp-time", (int32_t *)&rtpTime));
+
+                if (track->mNewSegment) {
+                    track->mNewSegment = false;
+
+                    LOG(VERBOSE) << "first segment unit ntpTime="
+                              << StringPrintf("0x%016llx", ntpTime)
+                              << " rtpTime=" << rtpTime
+                              << " seq=" << seqNum;
+                }
+
                 if (mFirstAccessUnit) {
                     mFirstAccessUnit = false;
                     mFirstAccessUnitNTP = ntpTime;
@@ -414,6 +506,11 @@
 
             case 'see1':
             {
+                // Session is paused now.
+                for (size_t i = 0; i < mTracks.size(); ++i) {
+                    mTracks.editItemAt(i).mPacketSource->flushQueue();
+                }
+
                 int64_t timeUs;
                 CHECK(msg->findInt64("time", &timeUs));
 
@@ -440,15 +537,13 @@
             {
                 CHECK(mSeekPending);
 
-                LOG(INFO) << "seek completed.";
-                mSeekPending = false;
-
                 int32_t result;
                 CHECK(msg->findInt32("result", &result));
-                if (result != OK) {
-                    LOG(ERROR) << "seek FAILED";
-                    break;
-                }
+
+                LOG(INFO) << "PLAY completed with result "
+                     << result << " (" << strerror(-result) << ")";
+
+                CHECK_EQ(result, (status_t)OK);
 
                 sp<RefBase> obj;
                 CHECK(msg->findObject("response", &obj));
@@ -457,9 +552,10 @@
 
                 CHECK_EQ(response->mStatusCode, 200u);
 
-                for (size_t i = 0; i < mTracks.size(); ++i) {
-                    mTracks.editItemAt(i).mPacketSource->flushQueue();
-                }
+                parsePlayResponse(response);
+
+                LOG(INFO) << "seek completed.";
+                mSeekPending = false;
                 break;
             }
 
@@ -480,9 +576,8 @@
             {
                 if (mFirstAccessUnit) {
                     LOG(WARNING) << "Never received any data, disconnecting.";
-
+                    (new AMessage('abor', id()))->post();
                 }
-                (new AMessage('abor', id()))->post();
                 break;
             }
 
@@ -492,6 +587,90 @@
         }
     }
 
+    static void SplitString(
+            const AString &s, const char *separator, List<AString> *items) {
+        items->clear();
+        size_t start = 0;
+        while (start < s.size()) {
+            ssize_t offset = s.find(separator, start);
+
+            if (offset < 0) {
+                items->push_back(AString(s, start, s.size() - start));
+                break;
+            }
+
+            items->push_back(AString(s, start, offset - start));
+            start = offset + strlen(separator);
+        }
+    }
+
+    void parsePlayResponse(const sp<ARTSPResponse> &response) {
+        ssize_t i = response->mHeaders.indexOfKey("range");
+        if (i < 0) {
+            // Server doesn't even tell use what range it is going to
+            // play, therefore we won't support seeking.
+            return;
+        }
+
+        AString range = response->mHeaders.valueAt(i);
+        LOG(VERBOSE) << "Range: " << range;
+
+        AString val;
+        CHECK(GetAttribute(range.c_str(), "npt", &val));
+        float npt1, npt2;
+
+        if (val == "now-") {
+            // This is a live stream and therefore not seekable.
+            return;
+        } else {
+            CHECK_EQ(sscanf(val.c_str(), "%f-%f", &npt1, &npt2), 2);
+        }
+
+        i = response->mHeaders.indexOfKey("rtp-info");
+        CHECK_GE(i, 0);
+
+        AString rtpInfo = response->mHeaders.valueAt(i);
+        List<AString> streamInfos;
+        SplitString(rtpInfo, ",", &streamInfos);
+
+        int n = 1;
+        for (List<AString>::iterator it = streamInfos.begin();
+             it != streamInfos.end(); ++it) {
+            (*it).trim();
+            LOG(VERBOSE) << "streamInfo[" << n << "] = " << *it;
+
+            CHECK(GetAttribute((*it).c_str(), "url", &val));
+
+            size_t trackIndex = 0;
+            while (trackIndex < mTracks.size()
+                    && !(val == mTracks.editItemAt(trackIndex).mURL)) {
+                ++trackIndex;
+            }
+            CHECK_LT(trackIndex, mTracks.size());
+
+            CHECK(GetAttribute((*it).c_str(), "seq", &val));
+
+            char *end;
+            unsigned long seq = strtoul(val.c_str(), &end, 10);
+
+            TrackInfo *info = &mTracks.editItemAt(trackIndex);
+            info->mFirstSeqNumInSegment = seq;
+            info->mNewSegment = true;
+
+            CHECK(GetAttribute((*it).c_str(), "rtptime", &val));
+
+            uint32_t rtpTime = strtoul(val.c_str(), &end, 10);
+
+            LOG(VERBOSE) << "track #" << n
+                      << ": rtpTime=" << rtpTime << " <=> npt=" << npt1;
+
+            info->mPacketSource->setNormalPlayTimeMapping(
+                    rtpTime, (int64_t)(npt1 * 1E6));
+
+            ++n;
+        }
+    }
+
     sp<APacketSource> getPacketSource(size_t index) {
         CHECK_GE(index, 0u);
         CHECK_LT(index, mTracks.size());
@@ -516,11 +695,16 @@
     bool mSeekPending;
     bool mFirstAccessUnit;
     uint64_t mFirstAccessUnitNTP;
+    int64_t mNumAccessUnitsReceived;
+    bool mCheckPending;
 
     struct TrackInfo {
+        AString mURL;
         int mRTPSocket;
         int mRTCPSocket;
         bool mUsingInterleavedTCP;
+        uint32_t mFirstSeqNumInSegment;
+        bool mNewSegment;
 
         sp<APacketSource> mPacketSource;
     };
@@ -550,8 +734,13 @@
 
         mTracks.push(TrackInfo());
         TrackInfo *info = &mTracks.editItemAt(mTracks.size() - 1);
+        info->mURL = trackURL;
         info->mPacketSource = source;
         info->mUsingInterleavedTCP = false;
+        info->mFirstSeqNumInSegment = 0;
+        info->mNewSegment = true;
+
+        LOG(VERBOSE) << "track #" << mTracks.size() << " URL=" << trackURL;
 
         AString request = "SETUP ";
         request.append(trackURL);
diff --git a/opengl/include/EGL/eglext.h b/opengl/include/EGL/eglext.h
index b121158..1ffcd56 100644
--- a/opengl/include/EGL/eglext.h
+++ b/opengl/include/EGL/eglext.h
@@ -229,14 +229,6 @@
 #define EGL_NATIVE_BUFFER_ANDROID       0x3140  /* eglCreateImageKHR target */
 #endif
 
-#ifndef EGL_ANDROID_get_render_buffer
-#define EGL_ANDROID_get_render_buffer 1
-#ifdef EGL_EGLEXT_PROTOTYPES
-EGLAPI EGLClientBuffer EGLAPIENTRY eglGetRenderBufferANDROID(EGLDisplay dpy, EGLSurface draw);
-#endif
-typedef EGLClientBuffer (EGLAPIENTRYP PFNEGLGETRENDERBUFFERANDROIDPROC) (EGLDisplay dpy, EGLSurface draw);
-#endif
-
 #ifndef EGL_ANDROID_swap_rectangle
 #define EGL_ANDROID_swap_rectangle 1
 #ifdef EGL_EGLEXT_PROTOTYPES
diff --git a/opengl/libagl/egl.cpp b/opengl/libagl/egl.cpp
index 9e25681..163b2dbc 100644
--- a/opengl/libagl/egl.cpp
+++ b/opengl/libagl/egl.cpp
@@ -158,7 +158,6 @@
     virtual     EGLint      getSwapBehavior() const;
     virtual     EGLBoolean  swapBuffers();
     virtual     EGLBoolean  setSwapRectangle(EGLint l, EGLint t, EGLint w, EGLint h);
-    virtual     EGLClientBuffer getRenderBuffer() const;
 protected:
     GGLSurface              depth;
 };
@@ -202,9 +201,6 @@
 {
     return EGL_FALSE;
 }
-EGLClientBuffer egl_surface_t::getRenderBuffer() const {
-    return 0;
-}
 
 // ----------------------------------------------------------------------------
 
@@ -230,7 +226,6 @@
     virtual     EGLint      getRefreshRate() const;
     virtual     EGLint      getSwapBehavior() const;
     virtual     EGLBoolean  setSwapRectangle(EGLint l, EGLint t, EGLint w, EGLint h);
-    virtual     EGLClientBuffer  getRenderBuffer() const;
     
 private:
     status_t lock(android_native_buffer_t* buf, int usage, void** vaddr);
@@ -626,11 +621,6 @@
     return EGL_TRUE;
 }
 
-EGLClientBuffer egl_window_surface_v2_t::getRenderBuffer() const
-{
-    return buffer;
-}
-
 EGLBoolean egl_window_surface_v2_t::bindDrawSurface(ogles_context_t* gl)
 {
     GGLSurface buffer;
@@ -857,7 +847,6 @@
         // "KHR_image_pixmap "
         "EGL_ANDROID_image_native_buffer "
         "EGL_ANDROID_swap_rectangle "
-        "EGL_ANDROID_get_render_buffer "
         ;
 
 // ----------------------------------------------------------------------------
@@ -910,8 +899,6 @@
             (__eglMustCastToProperFunctionPointerType)&eglDestroyImageKHR }, 
     { "eglSetSwapRectangleANDROID", 
             (__eglMustCastToProperFunctionPointerType)&eglSetSwapRectangleANDROID }, 
-    { "eglGetRenderBufferANDROID", 
-            (__eglMustCastToProperFunctionPointerType)&eglGetRenderBufferANDROID }, 
 };
 
 /*
@@ -2129,18 +2116,3 @@
 
     return EGL_TRUE;
 }
-
-EGLClientBuffer eglGetRenderBufferANDROID(EGLDisplay dpy, EGLSurface draw)
-{
-    if (egl_display_t::is_valid(dpy) == EGL_FALSE)
-        return setError(EGL_BAD_DISPLAY, (EGLClientBuffer)0);
-
-    egl_surface_t* d = static_cast<egl_surface_t*>(draw);
-    if (!d->isValid())
-        return setError(EGL_BAD_SURFACE, (EGLClientBuffer)0);
-    if (d->dpy != dpy)
-        return setError(EGL_BAD_DISPLAY, (EGLClientBuffer)0);
-
-    // post the surface
-    return d->getRenderBuffer();
-}
diff --git a/opengl/libs/EGL/egl.cpp b/opengl/libs/EGL/egl.cpp
index 94b60a1..5e61607 100644
--- a/opengl/libs/EGL/egl.cpp
+++ b/opengl/libs/EGL/egl.cpp
@@ -61,7 +61,6 @@
         "EGL_KHR_image_pixmap "
         "EGL_ANDROID_image_native_buffer "
         "EGL_ANDROID_swap_rectangle "
-        "EGL_ANDROID_get_render_buffer "
         ;
 
 // ----------------------------------------------------------------------------
@@ -408,8 +407,6 @@
             (__eglMustCastToProperFunctionPointerType)&eglDestroyImageKHR }, 
     { "eglSetSwapRectangleANDROID", 
             (__eglMustCastToProperFunctionPointerType)&eglSetSwapRectangleANDROID }, 
-    { "eglGetRenderBufferANDROID", 
-            (__eglMustCastToProperFunctionPointerType)&eglGetRenderBufferANDROID }, 
 };
 
 extern const __eglMustCastToProperFunctionPointerType gExtensionForwarders[MAX_NUMBER_OF_GL_EXTENSIONS];
@@ -1810,19 +1807,3 @@
     }
     return setError(EGL_BAD_DISPLAY, NULL);
 }
-
-EGLClientBuffer eglGetRenderBufferANDROID(EGLDisplay dpy, EGLSurface draw)
-{
-    SurfaceRef _s(draw);
-    if (!_s.get()) return setError(EGL_BAD_SURFACE, (EGLClientBuffer*)0);
-
-    if (!validate_display_surface(dpy, draw))
-        return 0;    
-    egl_display_t const * const dp = get_display(dpy);
-    egl_surface_t const * const s = get_surface(draw);
-    if (s->cnx->egl.eglGetRenderBufferANDROID) {
-        return s->cnx->egl.eglGetRenderBufferANDROID(
-                dp->disp[s->impl].dpy, s->surface);
-    }
-    return setError(EGL_BAD_DISPLAY, (EGLClientBuffer*)0);
-}