Merge "implement [2396050] Add ETC1 texture support to AGL"
diff --git a/include/media/stagefright/HTTPDataSource.h b/include/media/stagefright/HTTPDataSource.h
index 25a90bf..98ebc48 100644
--- a/include/media/stagefright/HTTPDataSource.h
+++ b/include/media/stagefright/HTTPDataSource.h
@@ -63,7 +63,6 @@
     void *mBuffer;
     size_t mBufferLength;
     off_t mBufferOffset;
-    bool mFirstRequest;
 
     bool mContentLengthValid;
     unsigned long long mContentLength;
diff --git a/media/libstagefright/HTTPDataSource.cpp b/media/libstagefright/HTTPDataSource.cpp
index bb3b43c..4b630b9 100644
--- a/media/libstagefright/HTTPDataSource.cpp
+++ b/media/libstagefright/HTTPDataSource.cpp
@@ -34,7 +34,7 @@
         HTTPStream *http, const String8 &headers,
         string *host, string *path, int *port) {
     String8 request;
-    request.append("HEAD ");
+    request.append("GET ");
     request.append(path->c_str());
     request.append(" HTTP/1.1\r\n");
     request.append(headers);
@@ -142,7 +142,6 @@
     mBuffer = malloc(kBufferSize);
     mBufferLength = 0;
     mBufferOffset = 0;
-    mFirstRequest = true;
     mContentLengthValid = false;
 
     initHeaders(headers);
@@ -153,13 +152,15 @@
     LOGI("Connecting to host '%s', port %d, path '%s'",
          host.c_str(), port, path.c_str());
 
+    int numRedirectsRemaining = 5;
     do {
         mInitCheck = mHttp->connect(host.c_str(), port);
 
         if (mInitCheck != OK) {
             return;
         }
-    } while (PerformRedirectIfNecessary(mHttp, mHeaders, &host, &path, &port));
+    } while (PerformRedirectIfNecessary(mHttp, mHeaders, &host, &path, &port)
+             && numRedirectsRemaining-- > 0);
 
     string value;
     if (mHttp->find_header_value("Content-Length", &value)) {
@@ -280,14 +281,11 @@
     }
 
     ssize_t contentLength = 0;
-    if (mFirstRequest || offset != (off_t)(mBufferOffset + mBufferLength)) {
-        if (!mFirstRequest) {
-            LOGV("new range offset=%ld (old=%ld)",
-                 offset, mBufferOffset + mBufferLength);
+    if (offset != (off_t)(mBufferOffset + mBufferLength)) {
+        LOGV("new range offset=%ld (old=%ld)",
+             offset, mBufferOffset + mBufferLength);
 
-            mHttp->disconnect();
-        }
-        mFirstRequest = false;
+        mHttp->disconnect();
 
         contentLength = sendRangeRequest(offset);
 
@@ -306,6 +304,12 @@
 
     ssize_t num_bytes_received = mHttp->receive(mBuffer, contentLength);
 
+    if (num_bytes_received < 0) {
+        mBufferLength = 0;
+
+        return num_bytes_received;
+    }
+
     mBufferLength = (size_t)num_bytes_received;
 
     size_t copy = mBufferLength;
diff --git a/media/libstagefright/HTTPStream.cpp b/media/libstagefright/HTTPStream.cpp
index 02f9439..3711aca 100644
--- a/media/libstagefright/HTTPStream.cpp
+++ b/media/libstagefright/HTTPStream.cpp
@@ -52,7 +52,7 @@
 
     CHECK_EQ(mSocket, -1);
     mSocket = socket(AF_INET, SOCK_STREAM, 0);
-    
+
     if (mSocket < 0) {
         return UNKNOWN_ERROR;
     }
@@ -132,6 +132,14 @@
     return send(data, strlen(data));
 }
 
+// A certain application spawns a local webserver that sends invalid responses,
+// specifically it terminates header line with only a newline instead of the
+// CRLF (carriage-return followed by newline) required by the HTTP specs.
+// The workaround accepts both behaviours but could potentially break
+// legitimate responses that use a single newline to "fold" headers, which is
+// why it's not yet on by default.
+#define WORKAROUND_FOR_MISSING_CR       0
+
 status_t HTTPStream::receive_line(char *line, size_t size) {
     if (mState != CONNECTED) {
         return ERROR_NOT_CONNECTED;
@@ -157,16 +165,27 @@
             return ERROR_CONNECTION_LOST;
         }
 
-        if (saw_CR && c == '\n') {
+#if WORKAROUND_FOR_MISSING_CR
+        if (c == '\n') {
+            // We have a complete line.
+
+            line[saw_CR ? length - 1 : length] = '\0';
+            return OK;
+        }
+#else
+        if (saw_CR &&  c == '\n') {
             // We have a complete line.
 
             line[length - 1] = '\0';
             return OK;
         }
+#endif
 
         saw_CR = (c == '\r');
 
-        CHECK(length + 1 < size);
+        if (length + 1 >= size) {
+            return ERROR_MALFORMED;
+        }
         line[length++] = c;
     }
 }
@@ -175,7 +194,7 @@
     *http_status = -1;
     mHeaders.clear();
 
-    char line[1024];
+    char line[2048];
     status_t err = receive_line(line, sizeof(line));
     if (err != OK) {
         return err;
@@ -257,11 +276,11 @@
             }
 
             disconnect();
-            return ERROR_IO;
+            return total == 0 ? ERROR_IO : total;
         } else if (n == 0) {
             disconnect();
 
-            return ERROR_CONNECTION_LOST;
+            return total == 0 ? ERROR_CONNECTION_LOST : total;
         }
 
         total += (size_t)n;
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index aee4d15..2cf0ddf 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -38,7 +38,7 @@
     void stop();
     bool reachedEOS();
 
-    int64_t getDuration() const;
+    int64_t getDurationUs() const;
     void writeTrackHeader(int32_t trackID);
 
 private:
@@ -46,6 +46,7 @@
     sp<MetaData> mMeta;
     sp<MediaSource> mSource;
     volatile bool mDone;
+    int64_t mMaxTimeStampUs;
 
     pthread_t mThread;
 
@@ -140,7 +141,7 @@
          it != mTracks.end(); ++it) {
         (*it)->stop();
 
-        int64_t duration = (*it)->getDuration();
+        int64_t duration = (*it)->getDurationUs();
         if (duration > max_duration) {
             max_duration = duration;
         }
@@ -162,7 +163,7 @@
         writeInt32(now);           // creation time
         writeInt32(now);           // modification time
         writeInt32(1000);          // timescale
-        writeInt32(max_duration);
+        writeInt32(max_duration / 1000);
         writeInt32(0x10000);       // rate
         writeInt16(0x100);         // volume
         writeInt16(0);             // reserved
@@ -316,6 +317,7 @@
       mMeta(source->getFormat()),
       mSource(source),
       mDone(false),
+      mMaxTimeStampUs(0),
       mCodecSpecificData(NULL),
       mCodecSpecificDataSize(0),
       mReachedEOS(false) {
@@ -343,6 +345,7 @@
     pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
 
     mDone = false;
+    mMaxTimeStampUs = 0;
     mReachedEOS = false;
 
     pthread_create(&mThread, &attr, ThreadWrapper, this);
@@ -483,6 +486,10 @@
         int64_t timestampUs;
         CHECK(buffer->meta_data()->findInt64(kKeyTime, &timestampUs));
 
+        if (timestampUs > mMaxTimeStampUs) {
+            mMaxTimeStampUs = timestampUs;
+        }
+
         // Our timestamp is in ms.
         info.timestamp = (timestampUs + 500) / 1000;
 
@@ -495,8 +502,8 @@
     mReachedEOS = true;
 }
 
-int64_t MPEG4Writer::Track::getDuration() const {
-    return 10000;  // XXX
+int64_t MPEG4Writer::Track::getDurationUs() const {
+    return mMaxTimeStampUs;
 }
 
 void MPEG4Writer::Track::writeTrackHeader(int32_t trackID) {
@@ -516,7 +523,7 @@
         mOwner->writeInt32(now);           // modification time
         mOwner->writeInt32(trackID);
         mOwner->writeInt32(0);             // reserved
-        mOwner->writeInt32(getDuration());
+        mOwner->writeInt32(getDurationUs() / 1000);
         mOwner->writeInt32(0);             // reserved
         mOwner->writeInt32(0);             // reserved
         mOwner->writeInt16(0);             // layer
@@ -555,7 +562,7 @@
           mOwner->writeInt32(now);           // creation time
           mOwner->writeInt32(now);           // modification time
           mOwner->writeInt32(1000);          // timescale
-          mOwner->writeInt32(getDuration());
+          mOwner->writeInt32(getDurationUs() / 1000);
           mOwner->writeInt16(0);             // language code XXX
           mOwner->writeInt16(0);             // predefined
         mOwner->endBox();