Merge "audioflinger: refactor thread config events"
diff --git a/include/media/AudioRecord.h b/include/media/AudioRecord.h
index b3c44a8..6a68c94 100644
--- a/include/media/AudioRecord.h
+++ b/include/media/AudioRecord.h
@@ -490,10 +490,12 @@
     int                     mSessionId;
     transfer_type           mTransfer;
 
-    // Next 4 fields may be changed if IAudioRecord is re-created, but always != 0
+    // Next 5 fields may be changed if IAudioRecord is re-created, but always != 0
+    // provided the initial set() was successful
     sp<IAudioRecord>        mAudioRecord;
     sp<IMemory>             mCblkMemory;
     audio_track_cblk_t*     mCblk;              // re-load after mLock.unlock()
+    sp<IMemory>             mBufferMemory;
     audio_io_handle_t       mInput;             // returned by AudioSystem::getInput()
 
     int                     mPreviousPriority;  // before start()
diff --git a/include/media/IAudioFlinger.h b/include/media/IAudioFlinger.h
index 9101f06..7db6a48 100644
--- a/include/media/IAudioFlinger.h
+++ b/include/media/IAudioFlinger.h
@@ -88,6 +88,8 @@
                                 track_flags_t *flags,
                                 pid_t tid,  // -1 means unused, otherwise must be valid non-0
                                 int *sessionId,
+                                sp<IMemory>& cblk,
+                                sp<IMemory>& buffers,   // return value 0 means it follows cblk
                                 status_t *status) = 0;
 
     /* query the audio hardware state. This state never changes,
diff --git a/include/media/stagefright/MediaCodec.h b/include/media/stagefright/MediaCodec.h
index 276543b..39e57de 100644
--- a/include/media/stagefright/MediaCodec.h
+++ b/include/media/stagefright/MediaCodec.h
@@ -100,6 +100,7 @@
             uint32_t *flags,
             int64_t timeoutUs = 0ll);
 
+    status_t renderOutputBufferAndRelease(size_t index, int64_t timestampNs);
     status_t renderOutputBufferAndRelease(size_t index);
     status_t releaseOutputBuffer(size_t index);
 
diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp
index 2c8605c..97ab8f8 100644
--- a/media/libmedia/AudioRecord.cpp
+++ b/media/libmedia/AudioRecord.cpp
@@ -484,6 +484,8 @@
     size_t temp = frameCount;   // temp may be replaced by a revised value of frameCount,
                                 // but we will still need the original value also
     int originalSessionId = mSessionId;
+    sp<IMemory> iMem;           // for cblk
+    sp<IMemory> bufferMem;
     sp<IAudioRecord> record = audioFlinger->openRecord(input,
                                                        mSampleRate, mFormat,
                                                        mChannelMask,
@@ -491,6 +493,8 @@
                                                        &trackFlags,
                                                        tid,
                                                        &mSessionId,
+                                                       iMem,
+                                                       bufferMem,
                                                        &status);
     ALOGE_IF(originalSessionId != AUDIO_SESSION_ALLOCATE && mSessionId != originalSessionId,
             "session ID changed from %d to %d", originalSessionId, mSessionId);
@@ -504,7 +508,6 @@
     // AudioFlinger now owns the reference to the I/O handle,
     // so we are no longer responsible for releasing it.
 
-    sp<IMemory> iMem = record->getCblk();
     if (iMem == 0) {
         ALOGE("Could not get control block");
         return NO_INIT;
@@ -514,6 +517,22 @@
         ALOGE("Could not get control block pointer");
         return NO_INIT;
     }
+    audio_track_cblk_t* cblk = static_cast<audio_track_cblk_t*>(iMemPointer);
+
+    // Starting address of buffers in shared memory.
+    // The buffers are either immediately after the control block,
+    // or in a separate area at discretion of server.
+    void *buffers;
+    if (bufferMem == 0) {
+        buffers = cblk + 1;
+    } else {
+        buffers = bufferMem->pointer();
+        if (buffers == NULL) {
+            ALOGE("Could not get buffer pointer");
+            return NO_INIT;
+        }
+    }
+
     // invariant that mAudioRecord != 0 is true only after set() returns successfully
     if (mAudioRecord != 0) {
         mAudioRecord->asBinder()->unlinkToDeath(mDeathNotifier, this);
@@ -522,7 +541,7 @@
     mAudioRecord = record;
 
     mCblkMemory = iMem;
-    audio_track_cblk_t* cblk = static_cast<audio_track_cblk_t*>(iMemPointer);
+    mBufferMemory = bufferMem;
     mCblk = cblk;
     // note that temp is the (possibly revised) value of frameCount
     if (temp < frameCount || (frameCount == 0 && temp == 0)) {
@@ -552,11 +571,6 @@
     mInput = input;
     mRefreshRemaining = true;
 
-    // Starting address of buffers in shared memory, immediately after the control block.  This
-    // address is for the mapping within client address space.  AudioFlinger::TrackBase::mBuffer
-    // is for the server address space.
-    void *buffers = (char*)cblk + sizeof(audio_track_cblk_t);
-
     mFrameCount = frameCount;
     // If IAudioRecord is re-created, don't let the requested frameCount
     // decrease.  This can confuse clients that cache frameCount().
@@ -631,6 +645,7 @@
         // keep them from going away if another thread re-creates the track during obtainBuffer()
         sp<AudioRecordClientProxy> proxy;
         sp<IMemory> iMem;
+        sp<IMemory> bufferMem;
         {
             // start of lock scope
             AutoMutex lock(mLock);
@@ -654,6 +669,7 @@
             // Keep the extra references
             proxy = mProxy;
             iMem = mCblkMemory;
+            bufferMem = mBufferMemory;
 
             // Non-blocking if track is stopped
             if (!mActive) {
@@ -986,7 +1002,7 @@
     status_t result;
 
     // if the new IAudioRecord is created, openRecord_l() will modify the
-    // following member variables: mAudioRecord, mCblkMemory and mCblk.
+    // following member variables: mAudioRecord, mCblkMemory, mCblk, mBufferMemory.
     // It will also delete the strong references on previous IAudioRecord and IMemory
     size_t position = mProxy->getPosition();
     mNewPosition = position + mUpdatePeriod;
diff --git a/media/libmedia/IAudioFlinger.cpp b/media/libmedia/IAudioFlinger.cpp
index 1940fe7..0e2463e 100644
--- a/media/libmedia/IAudioFlinger.cpp
+++ b/media/libmedia/IAudioFlinger.cpp
@@ -169,6 +169,8 @@
                                 track_flags_t *flags,
                                 pid_t tid,
                                 int *sessionId,
+                                sp<IMemory>& cblk,
+                                sp<IMemory>& buffers,
                                 status_t *status)
     {
         Parcel data, reply;
@@ -188,6 +190,8 @@
             lSessionId = *sessionId;
         }
         data.writeInt32(lSessionId);
+        cblk.clear();
+        buffers.clear();
         status_t lStatus = remote()->transact(OPEN_RECORD, data, &reply);
         if (lStatus != NO_ERROR) {
             ALOGE("openRecord error: %s", strerror(-lStatus));
@@ -206,17 +210,34 @@
             }
             lStatus = reply.readInt32();
             record = interface_cast<IAudioRecord>(reply.readStrongBinder());
+            cblk = interface_cast<IMemory>(reply.readStrongBinder());
+            if (cblk != 0 && cblk->pointer() == NULL) {
+                cblk.clear();
+            }
+            buffers = interface_cast<IMemory>(reply.readStrongBinder());
+            if (buffers != 0 && buffers->pointer() == NULL) {
+                buffers.clear();
+            }
             if (lStatus == NO_ERROR) {
                 if (record == 0) {
                     ALOGE("openRecord should have returned an IAudioRecord");
                     lStatus = UNKNOWN_ERROR;
+                } else if (cblk == 0) {
+                    ALOGE("openRecord should have returned a cblk");
+                    lStatus = NO_MEMORY;
                 }
+                // buffers is permitted to be 0
             } else {
-                if (record != 0) {
-                    ALOGE("openRecord returned an IAudioRecord but with status %d", lStatus);
-                    record.clear();
+                if (record != 0 || cblk != 0 || buffers != 0) {
+                    ALOGE("openRecord returned an IAudioRecord, cblk, "
+                          "or buffers but with status %d", lStatus);
                 }
             }
+            if (lStatus != NO_ERROR) {
+                record.clear();
+                cblk.clear();
+                buffers.clear();
+            }
         }
         if (status != NULL) {
             *status = lStatus;
@@ -838,15 +859,20 @@
             track_flags_t flags = (track_flags_t) data.readInt32();
             pid_t tid = (pid_t) data.readInt32();
             int sessionId = data.readInt32();
+            sp<IMemory> cblk;
+            sp<IMemory> buffers;
             status_t status;
             sp<IAudioRecord> record = openRecord(input,
-                    sampleRate, format, channelMask, &frameCount, &flags, tid, &sessionId, &status);
+                    sampleRate, format, channelMask, &frameCount, &flags, tid, &sessionId,
+                    cblk, buffers, &status);
             LOG_ALWAYS_FATAL_IF((record != 0) != (status == NO_ERROR));
             reply->writeInt64(frameCount);
             reply->writeInt32(flags);
             reply->writeInt32(sessionId);
             reply->writeInt32(status);
             reply->writeStrongBinder(record->asBinder());
+            reply->writeStrongBinder(cblk->asBinder());
+            reply->writeStrongBinder(buffers->asBinder());
             return NO_ERROR;
         } break;
         case SAMPLE_RATE: {
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index 537d9de..0a3a3b6 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -3670,7 +3670,28 @@
         ATRACE_NAME("render");
         // The client wants this buffer to be rendered.
 
+        int64_t timestampNs = 0;
+        if (!msg->findInt64("timestampNs", &timestampNs)) {
+            // TODO: it seems like we should use the timestamp
+            // in the (media)buffer as it potentially came from
+            // an input surface, but we did not propagate it prior to
+            // API 20.  Perhaps check for target SDK version.
+#if 0
+            if (info->mData->meta()->findInt64("timeUs", &timestampNs)) {
+                ALOGI("using buffer PTS of %" PRId64, timestampNs);
+                timestampNs *= 1000;
+            }
+#endif
+        }
+
         status_t err;
+        err = native_window_set_buffers_timestamp(mCodec->mNativeWindow.get(), timestampNs);
+        if (err != OK) {
+            ALOGW("failed to set buffer timestamp: %d", err);
+        } else {
+            ALOGI("set PTS to %" PRId64, timestampNs);
+        }
+
         if ((err = mCodec->mNativeWindow->queueBuffer(
                     mCodec->mNativeWindow.get(),
                     info->mGraphicBuffer.get(), -1)) == OK) {
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index 8d3032b..d679be1 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -106,12 +106,15 @@
     }
 
     virtual void render(MediaBuffer *buffer) {
+        int64_t timeUs;
+        CHECK(buffer->meta_data()->findInt64(kKeyTime, &timeUs));
+
         render((const uint8_t *)buffer->data() + buffer->range_offset(),
-               buffer->range_length());
+               buffer->range_length(), timeUs * 1000);
     }
 
-    void render(const void *data, size_t size) {
-        mTarget->render(data, size, NULL);
+    void render(const void *data, size_t size, int64_t timestampNs) {
+        mTarget->render(data, size, timestampNs, NULL);
     }
 
 protected:
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
index 2a3fa04..e07b6aa 100644
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -3540,7 +3540,7 @@
 
     off64_t offset;
     size_t size;
-    uint32_t cts;
+    uint32_t cts, stts;
     bool isSyncSample;
     bool newBuffer = false;
     if (mBuffer == NULL) {
@@ -3548,7 +3548,7 @@
 
         status_t err =
             mSampleTable->getMetaDataForSample(
-                    mCurrentSampleIndex, &offset, &size, &cts, &isSyncSample);
+                    mCurrentSampleIndex, &offset, &size, &cts, &isSyncSample, &stts);
 
         if (err != OK) {
             return err;
@@ -3579,6 +3579,8 @@
             mBuffer->meta_data()->clear();
             mBuffer->meta_data()->setInt64(
                     kKeyTime, ((int64_t)cts * 1000000) / mTimescale);
+            mBuffer->meta_data()->setInt64(
+                    kKeyDuration, ((int64_t)stts * 1000000) / mTimescale);
 
             if (targetSampleTimeUs >= 0) {
                 mBuffer->meta_data()->setInt64(
@@ -3701,6 +3703,8 @@
         mBuffer->meta_data()->clear();
         mBuffer->meta_data()->setInt64(
                 kKeyTime, ((int64_t)cts * 1000000) / mTimescale);
+        mBuffer->meta_data()->setInt64(
+                kKeyDuration, ((int64_t)stts * 1000000) / mTimescale);
 
         if (targetSampleTimeUs >= 0) {
             mBuffer->meta_data()->setInt64(
@@ -3850,6 +3854,8 @@
             mBuffer->set_range(0, size);
             mBuffer->meta_data()->setInt64(
                     kKeyTime, ((int64_t)cts * 1000000) / mTimescale);
+            mBuffer->meta_data()->setInt64(
+                    kKeyDuration, ((int64_t)smpl->duration * 1000000) / mTimescale);
 
             if (targetSampleTimeUs >= 0) {
                 mBuffer->meta_data()->setInt64(
@@ -3973,6 +3979,8 @@
 
         mBuffer->meta_data()->setInt64(
                 kKeyTime, ((int64_t)cts * 1000000) / mTimescale);
+        mBuffer->meta_data()->setInt64(
+                kKeyDuration, ((int64_t)smpl->duration * 1000000) / mTimescale);
 
         if (targetSampleTimeUs >= 0) {
             mBuffer->meta_data()->setInt64(
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index 601dccf..5b525f2 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -17,6 +17,7 @@
 //#define LOG_NDEBUG 0
 #define LOG_TAG "MediaCodec"
 #include <utils/Log.h>
+#include <inttypes.h>
 
 #include <media/stagefright/MediaCodec.h>
 
@@ -323,6 +324,16 @@
     return PostAndAwaitResponse(msg, &response);
 }
 
+status_t MediaCodec::renderOutputBufferAndRelease(size_t index, int64_t timestampNs) {
+    sp<AMessage> msg = new AMessage(kWhatReleaseOutputBuffer, id());
+    msg->setSize("index", index);
+    msg->setInt32("render", true);
+    msg->setInt64("timestampNs", timestampNs);
+
+    sp<AMessage> response;
+    return PostAndAwaitResponse(msg, &response);
+}
+
 status_t MediaCodec::releaseOutputBuffer(size_t index) {
     sp<AMessage> msg = new AMessage(kWhatReleaseOutputBuffer, id());
     msg->setSize("index", index);
@@ -1707,9 +1718,25 @@
     if (render && info->mData != NULL && info->mData->size() != 0) {
         info->mNotify->setInt32("render", true);
 
+        int64_t timestampNs = 0;
+        if (msg->findInt64("timestampNs", &timestampNs)) {
+            info->mNotify->setInt64("timestampNs", timestampNs);
+        } else {
+            // TODO: it seems like we should use the timestamp
+            // in the (media)buffer as it potentially came from
+            // an input surface, but we did not propagate it prior to
+            // API 20.  Perhaps check for target SDK version.
+#if 0
+            if (info->mData->meta()->findInt64("timeUs", &timestampNs)) {
+                ALOGI("using buffer PTS of %" PRId64, timestampNs);
+                timestampNs *= 1000;
+            }
+#endif
+        }
+
         if (mSoftRenderer != NULL) {
             mSoftRenderer->render(
-                    info->mData->data(), info->mData->size(), NULL);
+                    info->mData->data(), info->mData->size(), timestampNs, NULL);
         }
     }
 
diff --git a/media/libstagefright/SampleIterator.cpp b/media/libstagefright/SampleIterator.cpp
index eae721b..2748349 100644
--- a/media/libstagefright/SampleIterator.cpp
+++ b/media/libstagefright/SampleIterator.cpp
@@ -133,7 +133,8 @@
     }
 
     status_t err;
-    if ((err = findSampleTime(sampleIndex, &mCurrentSampleTime)) != OK) {
+    if ((err = findSampleTimeAndDuration(
+            sampleIndex, &mCurrentSampleTime, &mCurrentSampleDuration)) != OK) {
         ALOGE("findSampleTime return error");
         return err;
     }
@@ -285,8 +286,8 @@
     return OK;
 }
 
-status_t SampleIterator::findSampleTime(
-        uint32_t sampleIndex, uint32_t *time) {
+status_t SampleIterator::findSampleTimeAndDuration(
+        uint32_t sampleIndex, uint32_t *time, uint32_t *duration) {
     if (sampleIndex >= mTable->mNumSampleSizes) {
         return ERROR_OUT_OF_RANGE;
     }
@@ -309,6 +310,8 @@
 
     *time += mTable->getCompositionTimeOffset(sampleIndex);
 
+    *duration = mTTSDuration;
+
     return OK;
 }
 
diff --git a/media/libstagefright/SampleTable.cpp b/media/libstagefright/SampleTable.cpp
index d9858d7..9a92805 100644
--- a/media/libstagefright/SampleTable.cpp
+++ b/media/libstagefright/SampleTable.cpp
@@ -778,7 +778,8 @@
         off64_t *offset,
         size_t *size,
         uint32_t *compositionTime,
-        bool *isSyncSample) {
+        bool *isSyncSample,
+        uint32_t *sampleDuration) {
     Mutex::Autolock autoLock(mLock);
 
     status_t err;
@@ -820,6 +821,10 @@
         }
     }
 
+    if (sampleDuration) {
+        *sampleDuration = mSampleIterator->getSampleDuration();
+    }
+
     return OK;
 }
 
diff --git a/media/libstagefright/colorconversion/SoftwareRenderer.cpp b/media/libstagefright/colorconversion/SoftwareRenderer.cpp
index 77f21b7..67dfcd2 100644
--- a/media/libstagefright/colorconversion/SoftwareRenderer.cpp
+++ b/media/libstagefright/colorconversion/SoftwareRenderer.cpp
@@ -138,7 +138,7 @@
 }
 
 void SoftwareRenderer::render(
-        const void *data, size_t size, void *platformPrivate) {
+        const void *data, size_t size, int64_t timestampNs, void *platformPrivate) {
     ANativeWindowBuffer *buf;
     int err;
     if ((err = native_window_dequeue_buffer_and_wait(mNativeWindow.get(),
@@ -230,6 +230,11 @@
 
     CHECK_EQ(0, mapper.unlock(buf->handle));
 
+    if ((err = native_window_set_buffers_timestamp(mNativeWindow.get(),
+            timestampNs)) != 0) {
+        ALOGW("Surface::set_buffers_timestamp returned error %d", err);
+    }
+
     if ((err = mNativeWindow->queueBuffer(mNativeWindow.get(), buf,
             -1)) != 0) {
         ALOGW("Surface::queueBuffer returned error %d", err);
diff --git a/media/libstagefright/httplive/PlaylistFetcher.cpp b/media/libstagefright/httplive/PlaylistFetcher.cpp
index c34f3cb..326d85b 100644
--- a/media/libstagefright/httplive/PlaylistFetcher.cpp
+++ b/media/libstagefright/httplive/PlaylistFetcher.cpp
@@ -69,6 +69,7 @@
       mNumRetries(0),
       mStartup(true),
       mPrepared(false),
+      mSkipToFirstIDRAfterConnect(false),
       mNextPTSTimeUs(-1ll),
       mMonitorQueueGeneration(0),
       mRefreshState(INITIAL_MINIMUM_RELOAD_DELAY),
@@ -1097,12 +1098,30 @@
             continue;
         }
 
+        if (stream == LiveSession::STREAMTYPE_VIDEO && mVideoMime.empty()) {
+            const char *mime;
+            if (source->getFormat()->findCString(kKeyMIMEType, &mime)) {
+                mVideoMime.setTo(mime);
+                if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) {
+                    mSkipToFirstIDRAfterConnect = true;
+                }
+            }
+        }
+
         int64_t timeUs;
         sp<ABuffer> accessUnit;
         status_t finalResult;
         while (source->hasBufferAvailable(&finalResult)
                 && source->dequeueAccessUnit(&accessUnit) == OK) {
 
+            if (stream == LiveSession::STREAMTYPE_VIDEO && mSkipToFirstIDRAfterConnect) {
+                if (!IsIDR(accessUnit)) {
+                    continue;
+                } else {
+                    mSkipToFirstIDRAfterConnect = false;
+                }
+            }
+
             CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs));
             if (mMinStartTimeUs > 0) {
                 if (timeUs < mMinStartTimeUs) {
@@ -1183,9 +1202,35 @@
     return OK;
 }
 
+/* static */
+bool PlaylistFetcher::bufferStartsWithWebVTTMagicSequence(
+        const sp<ABuffer> &buffer) {
+    size_t pos = 0;
+
+    // skip possible BOM
+    if (buffer->size() >= pos + 3 &&
+            !memcmp("\xef\xbb\xbf", buffer->data() + pos, 3)) {
+        pos += 3;
+    }
+
+    // accept WEBVTT followed by SPACE, TAB or (CR) LF
+    if (buffer->size() < pos + 6 ||
+            memcmp("WEBVTT", buffer->data() + pos, 6)) {
+        return false;
+    }
+    pos += 6;
+
+    if (buffer->size() == pos) {
+        return true;
+    }
+
+    uint8_t sep = buffer->data()[pos];
+    return sep == ' ' || sep == '\t' || sep == '\n' || sep == '\r';
+}
+
 status_t PlaylistFetcher::extractAndQueueAccessUnits(
         const sp<ABuffer> &buffer, const sp<AMessage> &itemMeta) {
-    if (buffer->size() >= 7 && !memcmp("WEBVTT\n", buffer->data(), 7)) {
+    if (bufferStartsWithWebVTTMagicSequence(buffer)) {
         if (mStreamTypeMask != LiveSession::STREAMTYPE_SUBTITLES) {
             ALOGE("This stream only contains subtitles.");
             return ERROR_MALFORMED;
diff --git a/media/libstagefright/httplive/PlaylistFetcher.h b/media/libstagefright/httplive/PlaylistFetcher.h
index 7e21523..e4fdbff 100644
--- a/media/libstagefright/httplive/PlaylistFetcher.h
+++ b/media/libstagefright/httplive/PlaylistFetcher.h
@@ -91,6 +91,7 @@
     static const int32_t kNumSkipFrames;
 
     static bool bufferStartsWithTsSyncByte(const sp<ABuffer>& buffer);
+    static bool bufferStartsWithWebVTTMagicSequence(const sp<ABuffer>& buffer);
 
     // notifications to mSession
     sp<AMessage> mNotify;
@@ -98,6 +99,7 @@
 
     sp<LiveSession> mSession;
     AString mURI;
+    AString mVideoMime;
 
     uint32_t mStreamTypeMask;
     int64_t mStartTimeUs;
@@ -115,6 +117,7 @@
     int32_t mNumRetries;
     bool mStartup;
     bool mPrepared;
+    bool mSkipToFirstIDRAfterConnect;
     int64_t mNextPTSTimeUs;
 
     int32_t mMonitorQueueGeneration;
diff --git a/media/libstagefright/include/SampleIterator.h b/media/libstagefright/include/SampleIterator.h
index b5a043c..60c9e7e 100644
--- a/media/libstagefright/include/SampleIterator.h
+++ b/media/libstagefright/include/SampleIterator.h
@@ -30,6 +30,7 @@
     off64_t getSampleOffset() const { return mCurrentSampleOffset; }
     size_t getSampleSize() const { return mCurrentSampleSize; }
     uint32_t getSampleTime() const { return mCurrentSampleTime; }
+    uint32_t getSampleDuration() const { return mCurrentSampleDuration; }
 
     status_t getSampleSizeDirect(
             uint32_t sampleIndex, size_t *size);
@@ -61,11 +62,12 @@
     off64_t mCurrentSampleOffset;
     size_t mCurrentSampleSize;
     uint32_t mCurrentSampleTime;
+    uint32_t mCurrentSampleDuration;
 
     void reset();
     status_t findChunkRange(uint32_t sampleIndex);
     status_t getChunkOffset(uint32_t chunk, off64_t *offset);
-    status_t findSampleTime(uint32_t sampleIndex, uint32_t *time);
+    status_t findSampleTimeAndDuration(uint32_t sampleIndex, uint32_t *time, uint32_t *duration);
 
     SampleIterator(const SampleIterator &);
     SampleIterator &operator=(const SampleIterator &);
diff --git a/media/libstagefright/include/SampleTable.h b/media/libstagefright/include/SampleTable.h
index 847dff7..fe146f2 100644
--- a/media/libstagefright/include/SampleTable.h
+++ b/media/libstagefright/include/SampleTable.h
@@ -66,7 +66,8 @@
             off64_t *offset,
             size_t *size,
             uint32_t *compositionTime,
-            bool *isSyncSample = NULL);
+            bool *isSyncSample = NULL,
+            uint32_t *sampleDuration = NULL);
 
     enum {
         kFlagBefore,
diff --git a/media/libstagefright/include/SoftwareRenderer.h b/media/libstagefright/include/SoftwareRenderer.h
index 7ab0042..0ba670c 100644
--- a/media/libstagefright/include/SoftwareRenderer.h
+++ b/media/libstagefright/include/SoftwareRenderer.h
@@ -34,7 +34,7 @@
     ~SoftwareRenderer();
 
     void render(
-            const void *data, size_t size, void *platformPrivate);
+            const void *data, size_t size, int64_t timestampNs, void *platformPrivate);
 
 private:
     enum YUVMode {
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 46d1211..e256f32 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -1318,6 +1318,8 @@
         IAudioFlinger::track_flags_t *flags,
         pid_t tid,
         int *sessionId,
+        sp<IMemory>& cblk,
+        sp<IMemory>& buffers,
         status_t *status)
 {
     sp<RecordThread::RecordTrack> recordTrack;
@@ -1326,6 +1328,9 @@
     status_t lStatus;
     int lSessionId;
 
+    cblk.clear();
+    buffers.clear();
+
     // check calling permissions
     if (!recordingAllowed()) {
         ALOGE("openRecord() permission denied: recording not allowed");
@@ -1401,6 +1406,9 @@
         goto Exit;
     }
 
+    cblk = recordTrack->getCblk();
+    buffers = recordTrack->getBuffers();
+
     // return handle to client
     recordHandle = new RecordHandle(recordTrack);
 
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index bfbc46c..e0965b8 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -120,6 +120,8 @@
                                 IAudioFlinger::track_flags_t *flags,
                                 pid_t tid,
                                 int *sessionId,
+                                sp<IMemory>& cblk,
+                                sp<IMemory>& buffers,
                                 status_t *status /*non-NULL*/);
 
     virtual     uint32_t    sampleRate(audio_io_handle_t output) const;
diff --git a/services/audioflinger/RecordTracks.h b/services/audioflinger/RecordTracks.h
index 6fc06d8..4ca2ad4 100644
--- a/services/audioflinger/RecordTracks.h
+++ b/services/audioflinger/RecordTracks.h
@@ -29,7 +29,8 @@
                                 audio_channel_mask_t channelMask,
                                 size_t frameCount,
                                 int sessionId,
-                                int uid);
+                                int uid,
+                                bool isFast);
     virtual             ~RecordTrack();
 
     virtual status_t    start(AudioSystem::sync_event_t event, int triggerSession);
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index cd3fa7f..77236b9 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -5153,7 +5153,8 @@
         Mutex::Autolock _l(mLock);
 
         track = new RecordTrack(this, client, sampleRate,
-                      format, channelMask, frameCount, sessionId, uid);
+                      format, channelMask, frameCount, sessionId, uid,
+                      (*flags & IAudioFlinger::TRACK_FAST) != 0);
 
         lStatus = track->initCheck();
         if (lStatus != NO_ERROR) {
diff --git a/services/audioflinger/TrackBase.h b/services/audioflinger/TrackBase.h
index 58705c4..06023fd 100644
--- a/services/audioflinger/TrackBase.h
+++ b/services/audioflinger/TrackBase.h
@@ -48,7 +48,8 @@
                                 const sp<IMemory>& sharedBuffer,
                                 int sessionId,
                                 int uid,
-                                bool isOut);
+                                bool isOut,
+                                bool useReadOnlyHeap = false);
     virtual             ~TrackBase();
     virtual status_t    initCheck() const { return getCblk() != 0 ? NO_ERROR : NO_MEMORY; }
 
@@ -61,6 +62,8 @@
             int         uid() const { return mUid; }
     virtual status_t    setSyncEvent(const sp<SyncEvent>& event);
 
+            sp<IMemory> getBuffers() const { return mBufferMemory; }
+
 protected:
                         TrackBase(const TrackBase&);
                         TrackBase& operator = (const TrackBase&);
@@ -112,6 +115,7 @@
     /*const*/ sp<Client> mClient;   // see explanation at ~TrackBase() why not const
     sp<IMemory>         mCblkMemory;
     audio_track_cblk_t* mCblk;
+    sp<IMemory>         mBufferMemory;  // currently non-0 for fast RecordTrack only
     void*               mBuffer;    // start of track buffer, typically in shared memory
                                     // except for OutputTrack when it is in local memory
     // we don't really need a lock for these
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index 1064fd1..5889567 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -69,7 +69,8 @@
             const sp<IMemory>& sharedBuffer,
             int sessionId,
             int clientUid,
-            bool isOut)
+            bool isOut,
+            bool useReadOnlyHeap)
     :   RefBase(),
         mThread(thread),
         mClient(client),
@@ -110,7 +111,7 @@
     // ALOGD("Creating track with %d buffers @ %d bytes", bufferCount, bufferSize);
     size_t size = sizeof(audio_track_cblk_t);
     size_t bufferSize = (sharedBuffer == 0 ? roundup(frameCount) : frameCount) * mFrameSize;
-    if (sharedBuffer == 0) {
+    if (sharedBuffer == 0 && !useReadOnlyHeap) {
         size += bufferSize;
     }
 
@@ -132,15 +133,31 @@
     // construct the shared structure in-place.
     if (mCblk != NULL) {
         new(mCblk) audio_track_cblk_t();
-        // clear all buffers
-        if (sharedBuffer == 0) {
-            mBuffer = (char*)mCblk + sizeof(audio_track_cblk_t);
+        if (useReadOnlyHeap) {
+            const sp<MemoryDealer> roHeap(thread->readOnlyHeap());
+            if (roHeap == 0 ||
+                    (mBufferMemory = roHeap->allocate(bufferSize)) == 0 ||
+                    (mBuffer = mBufferMemory->pointer()) == NULL) {
+                ALOGE("not enough memory for read-only buffer size=%zu", bufferSize);
+                if (roHeap != 0) {
+                    roHeap->dump("buffer");
+                }
+                mCblkMemory.clear();
+                mBufferMemory.clear();
+                return;
+            }
             memset(mBuffer, 0, bufferSize);
         } else {
-            mBuffer = sharedBuffer->pointer();
+            // clear all buffers
+            if (sharedBuffer == 0) {
+                mBuffer = (char*)mCblk + sizeof(audio_track_cblk_t);
+                memset(mBuffer, 0, bufferSize);
+            } else {
+                mBuffer = sharedBuffer->pointer();
 #if 0
-            mCblk->mFlags = CBLK_FORCEREADY;    // FIXME hack, need to fix the track ready logic
+                mCblk->mFlags = CBLK_FORCEREADY;    // FIXME hack, need to fix the track ready logic
 #endif
+            }
         }
 
 #ifdef TEE_SINK
@@ -1819,9 +1836,11 @@
             audio_channel_mask_t channelMask,
             size_t frameCount,
             int sessionId,
-            int uid)
+            int uid,
+            bool isFast)
     :   TrackBase(thread, client, sampleRate, format,
-                  channelMask, frameCount, 0 /*sharedBuffer*/, sessionId, uid, false /*isOut*/),
+                  channelMask, frameCount, 0 /*sharedBuffer*/, sessionId, uid, false /*isOut*/,
+                  isFast /*useReadOnlyHeap*/),
         mOverflow(false), mResampler(NULL), mRsmpOutBuffer(NULL), mRsmpOutFrameCount(0),
         // See real initialization of mRsmpInFront at RecordThread::start()
         mRsmpInUnrel(0), mRsmpInFront(0), mFramesToDrop(0), mResamplerBufferProvider(NULL)