diff options
| -rw-r--r-- | media/libstagefright/NuHTTPDataSource.cpp | 106 | ||||
| -rw-r--r-- | media/libstagefright/httplive/LiveSource.cpp | 35 | ||||
| -rw-r--r-- | media/libstagefright/include/HTTPStream.h | 8 | ||||
| -rw-r--r-- | media/libstagefright/include/NuHTTPDataSource.h | 8 | 
4 files changed, 136 insertions, 21 deletions
diff --git a/media/libstagefright/NuHTTPDataSource.cpp b/media/libstagefright/NuHTTPDataSource.cpp index 40f501a98493..c3c0d819749a 100644 --- a/media/libstagefright/NuHTTPDataSource.cpp +++ b/media/libstagefright/NuHTTPDataSource.cpp @@ -69,6 +69,8 @@ NuHTTPDataSource::NuHTTPDataSource()        mOffset(0),        mContentLength(0),        mContentLengthValid(false), +      mHasChunkedTransferEncoding(false), +      mChunkDataBytesLeft(0),        mNumBandwidthHistoryItems(0),        mTotalTransferTimeUs(0),        mTotalTransferBytes(0), @@ -193,17 +195,27 @@ status_t NuHTTPDataSource::connect(              return ERROR_IO;          } +        mHasChunkedTransferEncoding = false; +          {              string value; -            if (mHTTP.find_header_value("Transfer-Encoding", &value)) { -                // We don't currently support any transfer encodings. - -                mState = DISCONNECTED; -                mHTTP.disconnect(); - -                LOGE("We don't support '%s' transfer encoding.", value.c_str()); - -                return ERROR_UNSUPPORTED; +            if (mHTTP.find_header_value("Transfer-Encoding", &value) +                    || mHTTP.find_header_value("Transfer-encoding", &value)) { +                // We don't currently support any transfer encodings but +                // chunked. + +                if (!strcasecmp(value.c_str(), "chunked")) { +                    LOGI("Chunked transfer encoding applied."); +                    mHasChunkedTransferEncoding = true; +                    mChunkDataBytesLeft = 0; +                } else { +                    mState = DISCONNECTED; +                    mHTTP.disconnect(); + +                    LOGE("We don't support '%s' transfer encoding.", value.c_str()); + +                    return ERROR_UNSUPPORTED; +                }              }          } @@ -216,8 +228,17 @@ status_t NuHTTPDataSource::connect(                      && ParseSingleUnsignedLong(value.c_str(), &x)) {                  mContentLength = (off_t)x;                  mContentLengthValid = true; +            } else { +                LOGW("Server did not give us the content length!");              }          } else { +            if (httpStatus != 206 /* Partial Content */) { +                // We requested a range but the server didn't support that. +                LOGE("We requested a range but the server didn't " +                     "support that."); +                return ERROR_UNSUPPORTED; +            } +              string value;              unsigned long x;              if (mHTTP.find_header_value(string("Content-Range"), &value)) { @@ -245,6 +266,71 @@ status_t NuHTTPDataSource::initCheck() const {      return mState == CONNECTED ? OK : NO_INIT;  } +ssize_t NuHTTPDataSource::internalRead(void *data, size_t size) { +    if (!mHasChunkedTransferEncoding) { +        return mHTTP.receive(data, size); +    } + +    if (mChunkDataBytesLeft < 0) { +        return 0; +    } else if (mChunkDataBytesLeft == 0) { +        char line[1024]; +        status_t err = mHTTP.receive_line(line, sizeof(line)); + +        if (err != OK) { +            return err; +        } + +        LOGV("line = '%s'", line); + +        char *end; +        unsigned long n = strtoul(line, &end, 16); + +        if (end == line || (*end != ';' && *end != '\0')) { +            LOGE("malformed HTTP chunk '%s'", line); +            return ERROR_MALFORMED; +        } + +        mChunkDataBytesLeft = n; +        LOGV("chunk data size = %lu", n); + +        if (mChunkDataBytesLeft == 0) { +            mChunkDataBytesLeft = -1; +            return 0; +        } + +        // fall through +    } + +    if (size > (size_t)mChunkDataBytesLeft) { +        size = mChunkDataBytesLeft; +    } + +    ssize_t n = mHTTP.receive(data, size); + +    if (n < 0) { +        return n; +    } + +    mChunkDataBytesLeft -= (size_t)n; + +    if (mChunkDataBytesLeft == 0) { +        char line[1024]; +        status_t err = mHTTP.receive_line(line, sizeof(line)); + +        if (err != OK) { +            return err; +        } + +        if (line[0] != '\0') { +            LOGE("missing HTTP chunk terminator."); +            return ERROR_MALFORMED; +        } +    } + +    return n; +} +  ssize_t NuHTTPDataSource::readAt(off_t offset, void *data, size_t size) {      LOGV("readAt offset %ld, size %d", offset, size); @@ -275,7 +361,7 @@ ssize_t NuHTTPDataSource::readAt(off_t offset, void *data, size_t size) {          int64_t startTimeUs = ALooper::GetNowUs();          ssize_t n = -            mHTTP.receive((uint8_t *)data + numBytesRead, size - numBytesRead); +            internalRead((uint8_t *)data + numBytesRead, size - numBytesRead);          if (n < 0) {              return n; diff --git a/media/libstagefright/httplive/LiveSource.cpp b/media/libstagefright/httplive/LiveSource.cpp index f9d27ebc0e0d..c19b6f7bf587 100644 --- a/media/libstagefright/httplive/LiveSource.cpp +++ b/media/libstagefright/httplive/LiveSource.cpp @@ -555,20 +555,41 @@ status_t LiveSource::fetchM3U(const char *url, sp<ABuffer> *out) {      status_t err = source->getSize(&size);      if (err != OK) { -        return err; +        size = 65536;      }      sp<ABuffer> buffer = new ABuffer(size); -    size_t offset = 0; -    while (offset < (size_t)size) { +    buffer->setRange(0, 0); + +    for (;;) { +        size_t bufferRemaining = buffer->capacity() - buffer->size(); + +        if (bufferRemaining == 0) { +            bufferRemaining = 32768; + +            LOGV("increasing download buffer to %d bytes", +                 buffer->size() + bufferRemaining); + +            sp<ABuffer> copy = new ABuffer(buffer->size() + bufferRemaining); +            memcpy(copy->data(), buffer->data(), buffer->size()); +            copy->setRange(0, buffer->size()); + +            buffer = copy; +        } +          ssize_t n = source->readAt( -                offset, buffer->data() + offset, size - offset); +                buffer->size(), buffer->data() + buffer->size(), +                bufferRemaining); -        if (n <= 0) { -            return ERROR_IO; +        if (n < 0) { +            return err; +        } + +        if (n == 0) { +            break;          } -        offset += n; +        buffer->setRange(0, buffer->size() + (size_t)n);      }      *out = buffer; diff --git a/media/libstagefright/include/HTTPStream.h b/media/libstagefright/include/HTTPStream.h index 35b086566040..793798f0942a 100644 --- a/media/libstagefright/include/HTTPStream.h +++ b/media/libstagefright/include/HTTPStream.h @@ -55,6 +55,10 @@ public:      // Pass a negative value to disable the timeout.      void setReceiveTimeout(int seconds); +    // Receive a line of data terminated by CRLF, line will be '\0' terminated +    // _excluding_ the termianting CRLF. +    status_t receive_line(char *line, size_t size); +  private:      enum State {          READY, @@ -68,10 +72,6 @@ private:      KeyedVector<string, string> mHeaders; -    // Receive a line of data terminated by CRLF, line will be '\0' terminated -    // _excluding_ the termianting CRLF. -    status_t receive_line(char *line, size_t size); -      HTTPStream(const HTTPStream &);      HTTPStream &operator=(const HTTPStream &);  }; diff --git a/media/libstagefright/include/NuHTTPDataSource.h b/media/libstagefright/include/NuHTTPDataSource.h index 0b840bd39113..c42691fec1d5 100644 --- a/media/libstagefright/include/NuHTTPDataSource.h +++ b/media/libstagefright/include/NuHTTPDataSource.h @@ -64,6 +64,11 @@ private:      off_t mOffset;      off_t mContentLength;      bool mContentLengthValid; +    bool mHasChunkedTransferEncoding; + +    // The number of data bytes in the current chunk before any subsequent +    // chunk header (or -1 if no more chunks). +    ssize_t mChunkDataBytesLeft;      List<BandwidthEntry> mBandwidthHistory;      size_t mNumBandwidthHistoryItems; @@ -81,6 +86,9 @@ private:              const String8 &headers,              off_t offset); +    // Read up to "size" bytes of data, respect transfer encoding. +    ssize_t internalRead(void *data, size_t size); +      void applyTimeoutResponse();      void addBandwidthMeasurement_l(size_t numBytes, int64_t delayUs);  |