Merge "Fix deadlock if the last reference of ICameraClient is removed in ICamera::connect()" into kraken
diff --git a/include/media/stagefright/AMRWriter.h b/include/media/stagefright/AMRWriter.h
index 34f3c4a..dd11809 100644
--- a/include/media/stagefright/AMRWriter.h
+++ b/include/media/stagefright/AMRWriter.h
@@ -49,9 +49,13 @@
     volatile bool mDone;
     volatile bool mReachedEOS;
     pthread_t mThread;
+    int64_t mEstimatedSizeBytes;
+    int64_t mEstimatedDurationUs;
 
     static void *ThreadWrapper(void *);
     void threadFunc();
+    bool exceedsFileSizeLimit();
+    bool exceedsFileDurationLimit();
 
     AMRWriter(const AMRWriter &);
     AMRWriter &operator=(const AMRWriter &);
diff --git a/include/media/stagefright/MPEG4Writer.h b/include/media/stagefright/MPEG4Writer.h
index 6064fc4..3c85eca 100644
--- a/include/media/stagefright/MPEG4Writer.h
+++ b/include/media/stagefright/MPEG4Writer.h
@@ -68,12 +68,16 @@
     bool mStreamableFile;
     off_t mEstimatedMoovBoxSize;
     uint32_t mInterleaveDurationUs;
+    int64_t mStartTimestampUs;
     Mutex mLock;
 
     List<Track *> mTracks;
 
     List<off_t> mBoxes;
 
+    void setStartTimestamp(int64_t timeUs);
+    int64_t getStartTimestamp();  // Not const
+
     void lock();
     void unlock();
 
@@ -82,6 +86,8 @@
     off_t addLengthPrefixedSample_l(MediaBuffer *buffer);
 
     inline size_t write(const void *ptr, size_t size, size_t nmemb, FILE* stream);
+    bool exceedsFileSizeLimit();
+    bool exceedsFileDurationLimit();
 
     MPEG4Writer(const MPEG4Writer &);
     MPEG4Writer &operator=(const MPEG4Writer &);
diff --git a/include/media/stagefright/MediaWriter.h b/include/media/stagefright/MediaWriter.h
index b8232c6..b15f69c 100644
--- a/include/media/stagefright/MediaWriter.h
+++ b/include/media/stagefright/MediaWriter.h
@@ -19,6 +19,7 @@
 #define MEDIA_WRITER_H_
 
 #include <utils/RefBase.h>
+#include <media/IMediaPlayerClient.h>
 
 namespace android {
 
@@ -31,10 +32,23 @@
     virtual bool reachedEOS() = 0;
     virtual status_t start() = 0;
     virtual void stop() = 0;
+    virtual void setMaxFileSize(int64_t bytes) { mMaxFileSizeLimitBytes = bytes; }
+    virtual void setMaxFileDuration(int64_t durationUs) { mMaxFileDurationLimitUs = durationUs; }
+    virtual void setListener(const sp<IMediaPlayerClient>& listener) {
+        mListener = listener;
+    }
 
 protected:
     virtual ~MediaWriter() {}
+    int64_t mMaxFileSizeLimitBytes;
+    int64_t mMaxFileDurationLimitUs;
+    sp<IMediaPlayerClient> mListener;
 
+    void notify(int msg, int ext1, int ext2) {
+        if (mListener != NULL) {
+            mListener->notify(msg, ext1, ext2);
+        }
+    }
 private:
     MediaWriter(const MediaWriter &);
     MediaWriter &operator=(const MediaWriter &);
diff --git a/include/private/media/AudioTrackShared.h b/include/private/media/AudioTrackShared.h
index 8e2db20..ab5ac64 100644
--- a/include/private/media/AudioTrackShared.h
+++ b/include/private/media/AudioTrackShared.h
@@ -59,7 +59,7 @@
                 // 8 bit PCM data: in this case,  mCblk->frameSize is based on a sample size of
                 // 16 bit because data is converted to 16 bit before being stored in buffer
                 uint32_t    frameSize;
-                uint8_t     channels;
+                uint8_t     channelCount;
                 uint8_t     flowControlFlag; // underrun (out) or overrrun (in) indication
                 uint8_t     out;        // out equals 1 for AudioTrack and 0 for AudioRecord
                 uint8_t     forceReady;
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index 3100f6e..11f3016 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -754,7 +754,7 @@
         int len = strlen(FILE_EXTS[i].extension);
         int start = lenURL - len;
         if (start > 0) {
-            if (!strncmp(url + start, FILE_EXTS[i].extension, len)) {
+            if (!strncasecmp(url + start, FILE_EXTS[i].extension, len)) {
                 if (FILE_EXTS[i].playertype == VORBIS_PLAYER
                     && !strncasecmp(url, "http://", 7)
                     && useStagefrightForHTTP) {
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index e41a716..cb08100 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -523,6 +523,14 @@
     CHECK(mOutputFd >= 0);
     mWriter = new AMRWriter(dup(mOutputFd));
     mWriter->addSource(audioEncoder);
+
+    if (mMaxFileDurationUs != 0) {
+        mWriter->setMaxFileDuration(mMaxFileDurationUs);
+    }
+    if (mMaxFileSizeBytes != 0) {
+        mWriter->setMaxFileSize(mMaxFileSizeBytes);
+    }
+    mWriter->setListener(mListener);
     mWriter->start();
 
     return OK;
@@ -641,6 +649,13 @@
         writer->setInterleaveDuration(mInterleaveDurationUs);
     }
 
+    if (mMaxFileDurationUs != 0) {
+        mWriter->setMaxFileDuration(mMaxFileDurationUs);
+    }
+    if (mMaxFileSizeBytes != 0) {
+        mWriter->setMaxFileSize(mMaxFileSizeBytes);
+    }
+    mWriter->setListener(mListener);
     mWriter->start();
     return OK;
 }
diff --git a/media/libstagefright/AMRWriter.cpp b/media/libstagefright/AMRWriter.cpp
index 73ea56d..0d54235 100644
--- a/media/libstagefright/AMRWriter.cpp
+++ b/media/libstagefright/AMRWriter.cpp
@@ -22,6 +22,7 @@
 #include <media/stagefright/MediaErrors.h>
 #include <media/stagefright/MediaSource.h>
 #include <media/stagefright/MetaData.h>
+#include <media/mediarecorder.h>
 
 namespace android {
 
@@ -137,6 +138,20 @@
     mStarted = false;
 }
 
+bool AMRWriter::exceedsFileSizeLimit() {
+    if (mMaxFileSizeLimitBytes == 0) {
+        return false;
+    }
+    return mEstimatedSizeBytes >= mMaxFileSizeLimitBytes;
+}
+
+bool AMRWriter::exceedsFileDurationLimit() {
+    if (mMaxFileDurationLimitUs == 0) {
+        return false;
+    }
+    return mEstimatedDurationUs >= mMaxFileDurationLimitUs;
+}
+
 // static
 void *AMRWriter::ThreadWrapper(void *me) {
     static_cast<AMRWriter *>(me)->threadFunc();
@@ -145,6 +160,8 @@
 }
 
 void AMRWriter::threadFunc() {
+    mEstimatedDurationUs = 0;
+    mEstimatedSizeBytes = 0;
     while (!mDone) {
         MediaBuffer *buffer;
         status_t err = mSource->read(&buffer);
@@ -153,6 +170,25 @@
             break;
         }
 
+        mEstimatedSizeBytes += buffer->range_length();
+        if (exceedsFileSizeLimit()) {
+            buffer->release();
+            buffer = NULL;
+            notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED, 0);
+            break;
+        }
+
+        int64_t timestampUs;
+        CHECK(buffer->meta_data()->findInt64(kKeyTime, &timestampUs));
+        if (timestampUs > mEstimatedDurationUs) {
+            mEstimatedDurationUs = timestampUs;
+        }
+        if (exceedsFileDurationLimit()) {
+            buffer->release();
+            buffer = NULL;
+            notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_MAX_DURATION_REACHED, 0);
+            break;
+        }
         ssize_t n = fwrite(
                 (const uint8_t *)buffer->data() + buffer->range_offset(),
                 1,
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index 19cccf7..788534d 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -31,6 +31,7 @@
 #include <media/stagefright/MediaErrors.h>
 #include <media/stagefright/MediaSource.h>
 #include <media/stagefright/Utils.h>
+#include <media/mediarecorder.h>
 
 namespace android {
 
@@ -44,6 +45,7 @@
     bool reachedEOS();
 
     int64_t getDurationUs() const;
+    int64_t getEstimatedTrackSizeBytes() const;
     void writeTrackHeader(int32_t trackID);
 
 private:
@@ -52,6 +54,7 @@
     sp<MediaSource> mSource;
     volatile bool mDone;
     int64_t mMaxTimeStampUs;
+    int64_t mEstimatedTrackSizeBytes;
 
     pthread_t mThread;
 
@@ -95,6 +98,7 @@
     bool mGotAllCodecSpecificData;
 
     bool mReachedEOS;
+    int64_t mStartTimestampUs;
 
     static void *ThreadWrapper(void *me);
     void threadEntry();
@@ -149,6 +153,7 @@
         return UNKNOWN_ERROR;
     }
 
+    mStartTimestampUs = 0;
     mStreamableFile = true;
     mWriteMoovBoxToMemory = false;
     mMoovBoxBuffer = NULL;
@@ -455,6 +460,35 @@
     write(data, 1, size, mFile);
 }
 
+bool MPEG4Writer::exceedsFileSizeLimit() {
+    // No limit
+    if (mMaxFileSizeLimitBytes == 0) {
+        return false;
+    }
+
+    int64_t nTotalBytesEstimate = mEstimatedMoovBoxSize;
+    for (List<Track *>::iterator it = mTracks.begin();
+         it != mTracks.end(); ++it) {
+        nTotalBytesEstimate += (*it)->getEstimatedTrackSizeBytes();
+    }
+    return (nTotalBytesEstimate >= mMaxFileSizeLimitBytes);
+}
+
+bool MPEG4Writer::exceedsFileDurationLimit() {
+    // No limit
+    if (mMaxFileDurationLimitUs == 0) {
+        return false;
+    }
+
+    for (List<Track *>::iterator it = mTracks.begin();
+         it != mTracks.end(); ++it) {
+        if ((*it)->getDurationUs() >= mMaxFileDurationLimitUs) {
+            return true;
+        }
+    }
+    return false;
+}
+
 bool MPEG4Writer::reachedEOS() {
     bool allDone = true;
     for (List<Track *>::iterator it = mTracks.begin();
@@ -468,6 +502,21 @@
     return allDone;
 }
 
+void MPEG4Writer::setStartTimestamp(int64_t timeUs) {
+    LOGI("setStartTimestamp: %lld", timeUs);
+    Mutex::Autolock autoLock(mLock);
+    if (mStartTimestampUs != 0) {
+        return;  // Sorry, too late
+    }
+    mStartTimestampUs = timeUs;
+}
+
+int64_t MPEG4Writer::getStartTimestamp() {
+    LOGI("getStartTimestamp: %lld", mStartTimestampUs);
+    Mutex::Autolock autoLock(mLock);
+    return mStartTimestampUs;
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 
 MPEG4Writer::Track::Track(
@@ -656,6 +705,7 @@
     int32_t sampleCount = 1;    // Sample count in the current stts table entry
     uint32_t previousSampleSize = 0;  // Size of the previous sample
 
+    mEstimatedTrackSizeBytes = 0;
     MediaBuffer *buffer;
     while (!mDone && mSource->read(&buffer) == OK) {
         if (buffer->range_length() == 0) {
@@ -784,10 +834,29 @@
 #endif
                 : buffer->range_length();
 
+        // Max file size or duration handling
+        mEstimatedTrackSizeBytes += info.size;
+        if (mOwner->exceedsFileSizeLimit()) {
+            buffer->release();
+            buffer = NULL;
+            mOwner->notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED, 0);
+            break;
+        }
+        if (mOwner->exceedsFileDurationLimit()) {
+            buffer->release();
+            buffer = NULL;
+            mOwner->notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_MAX_DURATION_REACHED, 0);
+            break;
+        }
+
         bool is_audio = !strncasecmp(mime, "audio/", 6);
 
         int64_t timestampUs;
         CHECK(buffer->meta_data()->findInt64(kKeyTime, &timestampUs));
+        if (mSampleInfos.empty()) {
+            mOwner->setStartTimestamp(timestampUs);
+            mStartTimestampUs = (timestampUs - mOwner->getStartTimestamp());
+        }
 
         if (timestampUs > mMaxTimeStampUs) {
             mMaxTimeStampUs = timestampUs;
@@ -904,6 +973,10 @@
     return mMaxTimeStampUs;
 }
 
+int64_t MPEG4Writer::Track::getEstimatedTrackSizeBytes() const {
+    return mEstimatedTrackSizeBytes;
+}
+
 void MPEG4Writer::Track::writeTrackHeader(int32_t trackID) {
     const char *mime;
     bool success = mMeta->findCString(kKeyMIMEType, &mime);
@@ -953,6 +1026,19 @@
         }
       mOwner->endBox();  // tkhd
 
+      if (mStartTimestampUs != 0) {
+        mOwner->beginBox("edts");
+          mOwner->writeInt32(0);             // version=0, flags=0
+          mOwner->beginBox("elst");
+            mOwner->writeInt32(0);           // version=0, flags=0
+            mOwner->writeInt32(1);           // a single entry
+            mOwner->writeInt32(mStartTimestampUs / 1000);  // edit duration
+            mOwner->writeInt32(0);           // edit media starting time
+            mOwner->writeInt32(1);           // x1 rate
+          mOwner->endBox();
+        mOwner->endBox();
+      }
+
       mOwner->beginBox("mdia");
 
         mOwner->beginBox("mdhd");