Merge "Add method to get redirected Uri"
diff --git a/include/media/IOMX.h b/include/media/IOMX.h
index 3db2c38..f6f9e7a 100644
--- a/include/media/IOMX.h
+++ b/include/media/IOMX.h
@@ -144,6 +144,7 @@
         INTERNAL_OPTION_REPEAT_PREVIOUS_FRAME_DELAY,  // data is an int64_t
         INTERNAL_OPTION_MAX_TIMESTAMP_GAP, // data is int64_t
         INTERNAL_OPTION_START_TIME, // data is an int64_t
+        INTERNAL_OPTION_TIME_LAPSE, // data is an int64_t[2]
     };
     virtual status_t setInternalOption(
             node_id node,
diff --git a/include/media/stagefright/ACodec.h b/include/media/stagefright/ACodec.h
index e284109..36f2a67 100644
--- a/include/media/stagefright/ACodec.h
+++ b/include/media/stagefright/ACodec.h
@@ -207,6 +207,9 @@
     int64_t mRepeatFrameDelayUs;
     int64_t mMaxPtsGapUs;
 
+    int64_t mTimePerFrameUs;
+    int64_t mTimePerCaptureUs;
+
     bool mCreateInputBuffersSuspended;
 
     status_t setCyclicIntraMacroblockRefresh(const sp<AMessage> &msg, int32_t mode);
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index d377acd..845a589 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -690,10 +690,10 @@
             return setParamTimeLapseEnable(timeLapseEnable);
         }
     } else if (key == "time-between-time-lapse-frame-capture") {
-        int64_t timeBetweenTimeLapseFrameCaptureMs;
-        if (safe_strtoi64(value.string(), &timeBetweenTimeLapseFrameCaptureMs)) {
+        int64_t timeBetweenTimeLapseFrameCaptureUs;
+        if (safe_strtoi64(value.string(), &timeBetweenTimeLapseFrameCaptureUs)) {
             return setParamTimeBetweenTimeLapseFrameCapture(
-                    1000LL * timeBetweenTimeLapseFrameCaptureMs);
+                    timeBetweenTimeLapseFrameCaptureUs);
         }
     } else {
         ALOGE("setParameter: failed to find key %s", key.string());
@@ -1436,6 +1436,17 @@
         format->setInt32("stride", mVideoWidth);
         format->setInt32("slice-height", mVideoWidth);
         format->setInt32("color-format", OMX_COLOR_FormatAndroidOpaque);
+
+        // set up time lapse/slow motion for surface source
+        if (mCaptureTimeLapse) {
+            if (mTimeBetweenTimeLapseFrameCaptureUs <= 0) {
+                ALOGE("Invalid mTimeBetweenTimeLapseFrameCaptureUs value: %lld",
+                    mTimeBetweenTimeLapseFrameCaptureUs);
+                return BAD_VALUE;
+            }
+            format->setInt64("time-lapse",
+                    mTimeBetweenTimeLapseFrameCaptureUs);
+        }
     }
 
     format->setInt32("bitrate", mVideoBitRate);
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index ac78d6c..4450d62 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -374,7 +374,9 @@
       mStoreMetaDataInOutputBuffers(false),
       mMetaDataBuffersToSubmit(0),
       mRepeatFrameDelayUs(-1ll),
-      mMaxPtsGapUs(-1l),
+      mMaxPtsGapUs(-1ll),
+      mTimePerCaptureUs(-1ll),
+      mTimePerFrameUs(-1ll),
       mCreateInputBuffersSuspended(false) {
     mUninitializedState = new UninitializedState(this);
     mLoadedState = new LoadedState(this);
@@ -1119,7 +1121,11 @@
         }
 
         if (!msg->findInt64("max-pts-gap-to-encoder", &mMaxPtsGapUs)) {
-            mMaxPtsGapUs = -1l;
+            mMaxPtsGapUs = -1ll;
+        }
+
+        if (!msg->findInt64("time-lapse", &mTimePerCaptureUs)) {
+            mTimePerCaptureUs = -1ll;
         }
 
         if (!msg->findInt32(
@@ -1916,6 +1922,7 @@
             return INVALID_OPERATION;
         }
         frameRate = (float)tmp;
+        mTimePerFrameUs = (int64_t) (1000000.0f / frameRate);
     }
 
     video_def->xFramerate = (OMX_U32)(frameRate * 65536.0f);
@@ -3939,7 +3946,7 @@
         }
     }
 
-    if (err == OK && mCodec->mMaxPtsGapUs > 0l) {
+    if (err == OK && mCodec->mMaxPtsGapUs > 0ll) {
         err = mCodec->mOMX->setInternalOption(
                 mCodec->mNode,
                 kPortIndexInput,
@@ -3951,8 +3958,27 @@
             ALOGE("[%s] Unable to configure max timestamp gap (err %d)",
                     mCodec->mComponentName.c_str(),
                     err);
-          }
-      }
+        }
+    }
+
+    if (err == OK && mCodec->mTimePerCaptureUs > 0ll
+            && mCodec->mTimePerFrameUs > 0ll) {
+        int64_t timeLapse[2];
+        timeLapse[0] = mCodec->mTimePerFrameUs;
+        timeLapse[1] = mCodec->mTimePerCaptureUs;
+        err = mCodec->mOMX->setInternalOption(
+                mCodec->mNode,
+                kPortIndexInput,
+                IOMX::INTERNAL_OPTION_TIME_LAPSE,
+                &timeLapse[0],
+                sizeof(timeLapse));
+
+        if (err != OK) {
+            ALOGE("[%s] Unable to configure time lapse (err %d)",
+                    mCodec->mComponentName.c_str(),
+                    err);
+        }
+    }
 
     if (err == OK && mCodec->mCreateInputBuffersSuspended) {
         bool suspend = true;
diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp
index 216a329..451e907 100644
--- a/media/libstagefright/Utils.cpp
+++ b/media/libstagefright/Utils.cpp
@@ -452,6 +452,11 @@
         }
     }
 
+    int32_t timeScale;
+    if (msg->findInt32("time-scale", &timeScale)) {
+        meta->setInt32(kKeyTimeScale, timeScale);
+    }
+
     // XXX TODO add whatever other keys there are
 
 #if 0
diff --git a/media/libstagefright/omx/GraphicBufferSource.cpp b/media/libstagefright/omx/GraphicBufferSource.cpp
index 7144efd..b81b116 100644
--- a/media/libstagefright/omx/GraphicBufferSource.cpp
+++ b/media/libstagefright/omx/GraphicBufferSource.cpp
@@ -51,7 +51,11 @@
     mLatestSubmittedBufferId(-1),
     mLatestSubmittedBufferFrameNum(0),
     mLatestSubmittedBufferUseCount(0),
-    mRepeatBufferDeferred(false) {
+    mRepeatBufferDeferred(false),
+    mTimePerCaptureUs(-1ll),
+    mTimePerFrameUs(-1ll),
+    mPrevCaptureUs(-1ll),
+    mPrevFrameUs(-1ll) {
 
     ALOGV("GraphicBufferSource w=%u h=%u c=%u",
             bufferWidth, bufferHeight, bufferCount);
@@ -560,7 +564,30 @@
 int64_t GraphicBufferSource::getTimestamp(const BufferQueue::BufferItem &item) {
     int64_t timeUs = item.mTimestamp / 1000;
 
-    if (mMaxTimestampGapUs > 0ll) {
+    if (mTimePerCaptureUs > 0ll) {
+        // Time lapse or slow motion mode
+        if (mPrevCaptureUs < 0ll) {
+            // first capture
+            mPrevCaptureUs = timeUs;
+            mPrevFrameUs = timeUs;
+        } else {
+            // snap to nearest capture point
+            int64_t nFrames = (timeUs + mTimePerCaptureUs / 2 - mPrevCaptureUs)
+                    / mTimePerCaptureUs;
+            if (nFrames <= 0) {
+                // skip this frame as it's too close to previous capture
+                ALOGV("skipping frame, timeUs %lld", timeUs);
+                return -1;
+            }
+            mPrevCaptureUs = mPrevCaptureUs + nFrames * mTimePerCaptureUs;
+            mPrevFrameUs += mTimePerFrameUs * nFrames;
+        }
+
+        ALOGV("timeUs %lld, captureUs %lld, frameUs %lld",
+                timeUs, mPrevCaptureUs, mPrevFrameUs);
+
+        return mPrevFrameUs;
+    } else if (mMaxTimestampGapUs > 0ll) {
         /* Cap timestamp gap between adjacent frames to specified max
          *
          * In the scenario of cast mirroring, encoding could be suspended for
@@ -711,7 +738,7 @@
             // If this is the first time we're seeing this buffer, add it to our
             // slot table.
             if (item.mGraphicBuffer != NULL) {
-                ALOGV("fillCodecBuffer_l: setting mBufferSlot %d", item.mBuf);
+                ALOGV("onFrameAvailable: setting mBufferSlot %d", item.mBuf);
                 mBufferSlot[item.mBuf] = item.mGraphicBuffer;
             }
             mBufferQueue->releaseBuffer(item.mBuf, item.mFrameNumber,
@@ -782,6 +809,19 @@
             (skipFramesBeforeUs > 0) ? (skipFramesBeforeUs * 1000) : -1ll;
 }
 
+status_t GraphicBufferSource::setTimeLapseUs(int64_t* data) {
+    Mutex::Autolock autoLock(mMutex);
+
+    if (mExecuting || data[0] <= 0ll || data[1] <= 0ll) {
+        return INVALID_OPERATION;
+    }
+
+    mTimePerFrameUs = data[0];
+    mTimePerCaptureUs = data[1];
+
+    return OK;
+}
+
 void GraphicBufferSource::onMessageReceived(const sp<AMessage> &msg) {
     switch (msg->what()) {
         case kWhatRepeatLastFrame:
diff --git a/media/libstagefright/omx/GraphicBufferSource.h b/media/libstagefright/omx/GraphicBufferSource.h
index 153f2a0..fba42b7 100644
--- a/media/libstagefright/omx/GraphicBufferSource.h
+++ b/media/libstagefright/omx/GraphicBufferSource.h
@@ -118,6 +118,13 @@
     // of suspension on input.
     status_t setMaxTimestampGapUs(int64_t maxGapUs);
 
+    // Sets the time lapse (or slow motion) parameters.
+    // data[0] is the time (us) between two frames for playback
+    // data[1] is the time (us) between two frames for capture
+    // When set, the sample's timestamp will be modified to playback framerate,
+    // and capture timestamp will be modified to capture rate.
+    status_t setTimeLapseUs(int64_t* data);
+
     // Sets the start time us (in system time), samples before which should
     // be dropped and not submitted to encoder
     void setSkipFramesBeforeUs(int64_t startTimeUs);
@@ -250,6 +257,12 @@
     // no codec buffer was available at the time.
     bool mRepeatBufferDeferred;
 
+    // Time lapse / slow motion configuration
+    int64_t mTimePerCaptureUs;
+    int64_t mTimePerFrameUs;
+    int64_t mPrevCaptureUs;
+    int64_t mPrevFrameUs;
+
     void onMessageReceived(const sp<AMessage> &msg);
 
     DISALLOW_EVIL_CONSTRUCTORS(GraphicBufferSource);
diff --git a/media/libstagefright/omx/OMXNodeInstance.cpp b/media/libstagefright/omx/OMXNodeInstance.cpp
index aa96389..0fb38fa 100644
--- a/media/libstagefright/omx/OMXNodeInstance.cpp
+++ b/media/libstagefright/omx/OMXNodeInstance.cpp
@@ -851,6 +851,7 @@
         case IOMX::INTERNAL_OPTION_REPEAT_PREVIOUS_FRAME_DELAY:
         case IOMX::INTERNAL_OPTION_MAX_TIMESTAMP_GAP:
         case IOMX::INTERNAL_OPTION_START_TIME:
+        case IOMX::INTERNAL_OPTION_TIME_LAPSE:
         {
             const sp<GraphicBufferSource> &bufferSource =
                 getGraphicBufferSource();
@@ -884,7 +885,7 @@
                 int64_t maxGapUs = *(int64_t *)data;
 
                 return bufferSource->setMaxTimestampGapUs(maxGapUs);
-            } else { // IOMX::INTERNAL_OPTION_START_TIME
+            } else if (type == IOMX::INTERNAL_OPTION_START_TIME) {
                 if (size != sizeof(int64_t)) {
                     return INVALID_OPERATION;
                 }
@@ -892,6 +893,12 @@
                 int64_t skipFramesBeforeUs = *(int64_t *)data;
 
                 bufferSource->setSkipFramesBeforeUs(skipFramesBeforeUs);
+            } else { // IOMX::INTERNAL_OPTION_TIME_LAPSE
+                if (size != sizeof(int64_t) * 2) {
+                    return INVALID_OPERATION;
+                }
+
+                bufferSource->setTimeLapseUs((int64_t *)data);
             }
 
             return OK;
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 6f2dce3..7615086 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -183,6 +183,7 @@
         (void) property_get("af.tee", value, "0");
         teeEnabled = atoi(value);
     }
+    // FIXME symbolic constants here
     if (teeEnabled & 1) {
         mTeeSinkInputEnabled = true;
     }
@@ -1810,7 +1811,7 @@
             kind = TEE_SINK_NEW;
         } else if (mRecordTeeSink->getStrongCount() != 1) {
             kind = TEE_SINK_NO;
-        } else if (format == mRecordTeeSink->format()) {
+        } else if (Format_isEqual(format, mRecordTeeSink->format())) {
             kind = TEE_SINK_OLD;
         } else {
             kind = TEE_SINK_NEW;
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index c5b8d33..21d05d4 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -638,7 +638,7 @@
     // 0x200000 stereo 16-bit PCM frames = 47.5 seconds at 44.1 kHz, 8 megabytes
     static const size_t kTeeSinkInputFramesDefault = 0x200000;
     static const size_t kTeeSinkOutputFramesDefault = 0x200000;
-    static const size_t kTeeSinkTrackFramesDefault = 0x1000;
+    static const size_t kTeeSinkTrackFramesDefault = 0x200000;
 #endif
 
     // This method reads from a variable without mLock, but the variable is updated under mLock.  So
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 9145a0e..3e8c133 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -4694,12 +4694,12 @@
                     framesIn = 0;
                     activeTrack->mRsmpInFront = rear;
                     overrun = OVERRUN_TRUE;
-                } else if ((size_t) filled <= mRsmpInFramesP2) {
+                } else if ((size_t) filled <= mRsmpInFrames) {
                     framesIn = (size_t) filled;
                 } else {
                     // client is not keeping up with server, but give it latest data
-                    framesIn = mRsmpInFramesP2;
-                    activeTrack->mRsmpInFront = rear - framesIn;
+                    framesIn = mRsmpInFrames;
+                    activeTrack->mRsmpInFront = front = rear - framesIn;
                     overrun = OVERRUN_TRUE;
                 }
 
@@ -4747,32 +4747,38 @@
                     double inOverOut = (double) mSampleRate / activeTrack->mSampleRate;
                     double outOverIn = (double) activeTrack->mSampleRate / mSampleRate;
                     framesInNeeded = ceil(framesOut * inOverOut) + 1;
+                    ALOGV("need %u frames in to produce %u out given in/out ratio of %.4g",
+                                framesInNeeded, framesOut, inOverOut);
+                    // Although we theoretically have framesIn in circular buffer, some of those are
+                    // unreleased frames, and thus must be discounted for purpose of budgeting.
+                    size_t unreleased = activeTrack->mRsmpInUnrel;
+                    framesIn = framesIn > unreleased ? framesIn - unreleased : 0;
                     if (framesIn < framesInNeeded) {
-                        ALOGV("not enough to resample: have %u but need %u to produce %u "
-                                "given in/out ratio of %.4g",
+                        ALOGV("not enough to resample: have %u frames in but need %u in to "
+                                "produce %u out given in/out ratio of %.4g",
                                 framesIn, framesInNeeded, framesOut, inOverOut);
                         size_t newFramesOut = framesIn > 0 ? floor((framesIn - 1) * outOverIn) : 0;
-                        size_t newFramesInNeeded = ceil(newFramesOut * inOverOut) + 1;
-                        ALOGV("now need %u frames to produce %u given out/in ratio of %.4g",
-                                newFramesInNeeded, newFramesOut, outOverIn);
-                        if (framesIn < newFramesInNeeded) {
-                            ALOGE("failure: have %u but need %u", framesIn, newFramesInNeeded);
-                            framesOut = 0;
-                        } else {
-                            ALOGV("success 2: have %u and need %u to produce %u "
-                                  "given in/out ratio of %.4g",
-                                  framesIn, newFramesInNeeded, newFramesOut, inOverOut);
-                            LOG_ALWAYS_FATAL_IF(newFramesOut > framesOut);
-                            framesOut = newFramesOut;
+                        LOG_ALWAYS_FATAL_IF(newFramesOut >= framesOut);
+                        if (newFramesOut == 0) {
+                            break;
                         }
+                        framesInNeeded = ceil(newFramesOut * inOverOut) + 1;
+                        ALOGV("now need %u frames in to produce %u out given out/in ratio of %.4g",
+                                framesInNeeded, newFramesOut, outOverIn);
+                        LOG_ALWAYS_FATAL_IF(framesIn < framesInNeeded);
+                        ALOGV("success 2: have %u frames in and need %u in to produce %u out "
+                              "given in/out ratio of %.4g",
+                              framesIn, framesInNeeded, newFramesOut, inOverOut);
+                        framesOut = newFramesOut;
                     } else {
-                        ALOGI("success 1: have %u and need %u to produce %u "
+                        ALOGV("success 1: have %u in and need %u in to produce %u out "
                             "given in/out ratio of %.4g",
                             framesIn, framesInNeeded, framesOut, inOverOut);
                     }
 
                     // reallocate mRsmpOutBuffer as needed; we will grow but never shrink
                     if (activeTrack->mRsmpOutFrameCount < framesOut) {
+                        // FIXME why does each track need it's own mRsmpOutBuffer? can't they share?
                         delete[] activeTrack->mRsmpOutBuffer;
                         // resampler always outputs stereo
                         activeTrack->mRsmpOutBuffer = new int32_t[framesOut * FCC_2];
@@ -4782,6 +4788,7 @@
                     // resampler accumulates, but we only have one source track
                     memset(activeTrack->mRsmpOutBuffer, 0, framesOut * FCC_2 * sizeof(int32_t));
                     activeTrack->mResampler->resample(activeTrack->mRsmpOutBuffer, framesOut,
+                            // FIXME how about having activeTrack implement this interface itself?
                             activeTrack->mResamplerBufferProvider
                             /*this*/ /* AudioBufferProvider* */);
                     // ditherAndClamp() works as long as all buffers returned by
@@ -5245,6 +5252,7 @@
     sp<ThreadBase> threadBase = activeTrack->mThread.promote();
     if (threadBase == 0) {
         buffer->frameCount = 0;
+        buffer->raw = NULL;
         return NOT_ENOUGH_DATA;
     }
     RecordThread *recordThread = (RecordThread *) threadBase.get();
@@ -5253,7 +5261,7 @@
     ssize_t filled = rear - front;
     // FIXME should not be P2 (don't want to increase latency)
     // FIXME if client not keeping up, discard
-    ALOG_ASSERT(0 <= filled && (size_t) filled <= recordThread->mRsmpInFramesP2);
+    LOG_ALWAYS_FATAL_IF(!(0 <= filled && (size_t) filled <= recordThread->mRsmpInFrames));
     // 'filled' may be non-contiguous, so return only the first contiguous chunk
     front &= recordThread->mRsmpInFramesP2 - 1;
     size_t part1 = recordThread->mRsmpInFramesP2 - front;
@@ -5267,7 +5275,7 @@
     }
     if (part1 == 0) {
         // Higher-level should keep mRsmpInBuffer full, and not call resampler if empty
-        LOG_FATAL("RecordThread::getNextBuffer() starved");
+        LOG_ALWAYS_FATAL("RecordThread::getNextBuffer() starved");
         buffer->raw = NULL;
         buffer->frameCount = 0;
         activeTrack->mRsmpInUnrel = 0;