resolved conflicts for merge of e7fbfb6b to gingerbread-plus-aosp

Change-Id: Icef97302fb99e3dd346ec6ee04aa9e8eea7bff26
diff --git a/media/libstagefright/rtsp/AMPEG4AudioAssembler.cpp b/media/libstagefright/rtsp/AMPEG4AudioAssembler.cpp
index b0d2c64..bbde516 100644
--- a/media/libstagefright/rtsp/AMPEG4AudioAssembler.cpp
+++ b/media/libstagefright/rtsp/AMPEG4AudioAssembler.cpp
@@ -18,18 +18,381 @@
 
 #include "ARTPSource.h"
 
+#include <media/stagefright/foundation/hexdump.h>
+#include <media/stagefright/foundation/ABitReader.h>
 #include <media/stagefright/foundation/ABuffer.h>
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/MediaErrors.h>
+
+#include <ctype.h>
 
 namespace android {
 
-AMPEG4AudioAssembler::AMPEG4AudioAssembler(const sp<AMessage> &notify)
+static bool GetAttribute(const char *s, const char *key, AString *value) {
+    value->clear();
+
+    size_t keyLen = strlen(key);
+
+    for (;;) {
+        while (isspace(*s)) {
+            ++s;
+        }
+
+        const char *colonPos = strchr(s, ';');
+
+        size_t len =
+            (colonPos == NULL) ? strlen(s) : colonPos - s;
+
+        if (len >= keyLen + 1 && s[keyLen] == '=' && !strncmp(s, key, keyLen)) {
+            value->setTo(&s[keyLen + 1], len - keyLen - 1);
+            return true;
+        }
+
+        if (colonPos == NULL) {
+            return false;
+        }
+
+        s = colonPos + 1;
+    }
+}
+
+static sp<ABuffer> decodeHex(const AString &s) {
+    if ((s.size() % 2) != 0) {
+        return NULL;
+    }
+
+    size_t outLen = s.size() / 2;
+    sp<ABuffer> buffer = new ABuffer(outLen);
+    uint8_t *out = buffer->data();
+
+    uint8_t accum = 0;
+    for (size_t i = 0; i < s.size(); ++i) {
+        char c = s.c_str()[i];
+        unsigned value;
+        if (c >= '0' && c <= '9') {
+            value = c - '0';
+        } else if (c >= 'a' && c <= 'f') {
+            value = c - 'a' + 10;
+        } else if (c >= 'A' && c <= 'F') {
+            value = c - 'A' + 10;
+        } else {
+            return NULL;
+        }
+
+        accum = (accum << 4) | value;
+
+        if (i & 1) {
+            *out++ = accum;
+
+            accum = 0;
+        }
+    }
+
+    return buffer;
+}
+
+static status_t parseAudioObjectType(
+        ABitReader *bits, unsigned *audioObjectType) {
+    *audioObjectType = bits->getBits(5);
+    if ((*audioObjectType) == 31) {
+        *audioObjectType = 32 + bits->getBits(6);
+    }
+
+    return OK;
+}
+
+static status_t parseGASpecificConfig(
+        ABitReader *bits,
+        unsigned audioObjectType, unsigned channelConfiguration) {
+    unsigned frameLengthFlag = bits->getBits(1);
+    unsigned dependsOnCoreCoder = bits->getBits(1);
+    if (dependsOnCoreCoder) {
+        /* unsigned coreCoderDelay = */bits->getBits(1);
+    }
+    unsigned extensionFlag = bits->getBits(1);
+
+    if (!channelConfiguration) {
+        // program_config_element
+        return ERROR_UNSUPPORTED;  // XXX to be implemented
+    }
+
+    if (audioObjectType == 6 || audioObjectType == 20) {
+        /* unsigned layerNr = */bits->getBits(3);
+    }
+
+    if (extensionFlag) {
+        if (audioObjectType == 22) {
+            /* unsigned numOfSubFrame = */bits->getBits(5);
+            /* unsigned layerLength = */bits->getBits(11);
+        } else if (audioObjectType == 17 || audioObjectType == 19
+                || audioObjectType == 20 || audioObjectType == 23) {
+            /* unsigned aacSectionDataResilienceFlag = */bits->getBits(1);
+            /* unsigned aacScalefactorDataResilienceFlag = */bits->getBits(1);
+            /* unsigned aacSpectralDataResilienceFlag = */bits->getBits(1);
+        }
+
+        unsigned extensionFlag3 = bits->getBits(1);
+        CHECK_EQ(extensionFlag3, 0u);  // TBD in version 3
+    }
+
+    return OK;
+}
+
+static status_t parseAudioSpecificConfig(ABitReader *bits) {
+    unsigned audioObjectType;
+    CHECK_EQ(parseAudioObjectType(bits, &audioObjectType), (status_t)OK);
+
+    unsigned samplingFreqIndex = bits->getBits(4);
+    if (samplingFreqIndex == 0x0f) {
+        /* unsigned samplingFrequency = */bits->getBits(24);
+    }
+
+    unsigned channelConfiguration = bits->getBits(4);
+
+    unsigned extensionAudioObjectType = 0;
+    unsigned sbrPresent = 0;
+
+    if (audioObjectType == 5) {
+        extensionAudioObjectType = audioObjectType;
+        sbrPresent = 1;
+        unsigned extensionSamplingFreqIndex = bits->getBits(4);
+        if (extensionSamplingFreqIndex == 0x0f) {
+            /* unsigned extensionSamplingFrequency = */bits->getBits(24);
+        }
+        CHECK_EQ(parseAudioObjectType(bits, &audioObjectType), (status_t)OK);
+    }
+
+    CHECK((audioObjectType >= 1 && audioObjectType <= 4)
+        || (audioObjectType >= 6 && audioObjectType <= 7)
+        || audioObjectType == 17
+        || (audioObjectType >= 19 && audioObjectType <= 23));
+
+    CHECK_EQ(parseGASpecificConfig(
+                bits, audioObjectType, channelConfiguration), (status_t)OK);
+
+    if (audioObjectType == 17
+            || (audioObjectType >= 19 && audioObjectType <= 27)) {
+        unsigned epConfig = bits->getBits(2);
+        if (epConfig == 2 || epConfig == 3) {
+            // ErrorProtectionSpecificConfig
+            return ERROR_UNSUPPORTED;  // XXX to be implemented
+
+            if (epConfig == 3) {
+                unsigned directMapping = bits->getBits(1);
+                CHECK_EQ(directMapping, 1u);
+            }
+        }
+    }
+
+#if 0
+    // This is not supported here as the upper layers did not explicitly
+    // signal the length of AudioSpecificConfig.
+
+    if (extensionAudioObjectType != 5 && bits->numBitsLeft() >= 16) {
+        unsigned syncExtensionType = bits->getBits(11);
+        if (syncExtensionType == 0x2b7) {
+            CHECK_EQ(parseAudioObjectType(bits, &extensionAudioObjectType),
+                     (status_t)OK);
+
+            sbrPresent = bits->getBits(1);
+
+            if (sbrPresent == 1) {
+                unsigned extensionSamplingFreqIndex = bits->getBits(4);
+                if (extensionSamplingFreqIndex == 0x0f) {
+                    /* unsigned extensionSamplingFrequency = */bits->getBits(24);
+                }
+            }
+        }
+    }
+#endif
+
+    return OK;
+}
+
+static status_t parseStreamMuxConfig(
+        ABitReader *bits,
+        unsigned *numSubFrames,
+        unsigned *frameLengthType,
+        bool *otherDataPresent,
+        unsigned *otherDataLenBits) {
+    unsigned audioMuxVersion = bits->getBits(1);
+
+    unsigned audioMuxVersionA = 0;
+    if (audioMuxVersion == 1) {
+        audioMuxVersionA = bits->getBits(1);
+    }
+
+    CHECK_EQ(audioMuxVersionA, 0u);  // otherwise future spec
+
+    if (audioMuxVersion != 0) {
+        return ERROR_UNSUPPORTED;  // XXX to be implemented;
+    }
+    CHECK_EQ(audioMuxVersion, 0u);  // XXX to be implemented
+
+    unsigned allStreamsSameTimeFraming = bits->getBits(1);
+    CHECK_EQ(allStreamsSameTimeFraming, 1u);  // There's only one stream.
+
+    *numSubFrames = bits->getBits(6);
+    unsigned numProgram = bits->getBits(4);
+    CHECK_EQ(numProgram, 0u);  // disabled in RTP LATM
+
+    unsigned numLayer = bits->getBits(3);
+    CHECK_EQ(numLayer, 0u);  // disabled in RTP LATM
+
+    if (audioMuxVersion == 0) {
+        // AudioSpecificConfig
+        CHECK_EQ(parseAudioSpecificConfig(bits), (status_t)OK);
+    } else {
+        TRESPASS();  // XXX to be implemented
+    }
+
+    *frameLengthType = bits->getBits(3);
+    switch (*frameLengthType) {
+        case 0:
+        {
+            /* unsigned bufferFullness = */bits->getBits(8);
+
+            // The "coreFrameOffset" does not apply since there's only
+            // a single layer.
+            break;
+        }
+
+        case 1:
+        {
+            /* unsigned frameLength = */bits->getBits(9);
+            break;
+        }
+
+        case 3:
+        case 4:
+        case 5:
+        {
+            /* unsigned CELPframeLengthTableIndex = */bits->getBits(6);
+            break;
+        }
+
+        case 6:
+        case 7:
+        {
+            /* unsigned HVXCframeLengthTableIndex = */bits->getBits(1);
+            break;
+        }
+
+        default:
+            break;
+    }
+
+    *otherDataPresent = bits->getBits(1);
+    *otherDataLenBits = 0;
+    if (*otherDataPresent) {
+        if (audioMuxVersion == 1) {
+            TRESPASS();  // XXX to be implemented
+        } else {
+            *otherDataLenBits = 0;
+
+            unsigned otherDataLenEsc;
+            do {
+                (*otherDataLenBits) <<= 8;
+                otherDataLenEsc = bits->getBits(1);
+                unsigned otherDataLenTmp = bits->getBits(8);
+                (*otherDataLenBits) += otherDataLenTmp;
+            } while (otherDataLenEsc);
+        }
+    }
+
+    unsigned crcCheckPresent = bits->getBits(1);
+    if (crcCheckPresent) {
+        /* unsigned crcCheckSum = */bits->getBits(8);
+    }
+
+    return OK;
+}
+
+sp<ABuffer> AMPEG4AudioAssembler::removeLATMFraming(const sp<ABuffer> &buffer) {
+    CHECK(!mMuxConfigPresent);  // XXX to be implemented
+
+    sp<ABuffer> out = new ABuffer(buffer->size());
+    out->setRange(0, 0);
+
+    size_t offset = 0;
+    uint8_t *ptr = buffer->data();
+
+    for (size_t i = 0; i <= mNumSubFrames; ++i) {
+        // parse PayloadLengthInfo
+
+        unsigned payloadLength = 0;
+
+        switch (mFrameLengthType) {
+            case 0:
+            {
+                unsigned muxSlotLengthBytes = 0;
+                unsigned tmp;
+                do {
+                    CHECK_LT(offset, buffer->size());
+                    tmp = ptr[offset++];
+                    muxSlotLengthBytes += tmp;
+                } while (tmp == 0xff);
+
+                payloadLength = muxSlotLengthBytes;
+                break;
+            }
+
+            default:
+                TRESPASS();  // XXX to be implemented
+                break;
+        }
+
+        CHECK_LE(offset + payloadLength, buffer->size());
+
+        memcpy(out->data() + out->size(), &ptr[offset], payloadLength);
+        out->setRange(0, out->size() + payloadLength);
+
+        offset += payloadLength;
+
+        if (mOtherDataPresent) {
+            // We want to stay byte-aligned.
+
+            CHECK((mOtherDataLenBits % 8) == 0);
+            CHECK_LE(offset + (mOtherDataLenBits / 8), buffer->size());
+            offset += mOtherDataLenBits / 8;
+        }
+    }
+
+    CHECK_EQ(offset, buffer->size());
+
+    return out;
+}
+
+AMPEG4AudioAssembler::AMPEG4AudioAssembler(
+        const sp<AMessage> &notify, const AString &params)
     : mNotifyMsg(notify),
+      mMuxConfigPresent(false),
       mAccessUnitRTPTime(0),
       mNextExpectedSeqNoValid(false),
       mNextExpectedSeqNo(0),
       mAccessUnitDamaged(false) {
+    AString val;
+    if (!GetAttribute(params.c_str(), "cpresent", &val)) {
+        mMuxConfigPresent = true;
+    } else if (val == "0") {
+        mMuxConfigPresent = false;
+    } else {
+        CHECK(val == "1");
+        mMuxConfigPresent = true;
+    }
+
+    CHECK(GetAttribute(params.c_str(), "config", &val));
+
+    sp<ABuffer> config = decodeHex(val);
+    CHECK(config != NULL);
+
+    ABitReader bits(config->data(), config->size());
+    status_t err = parseStreamMuxConfig(
+            &bits, &mNumSubFrames, &mFrameLengthType,
+            &mOtherDataPresent, &mOtherDataLenBits);
+
+    CHECK_EQ(err, (status_t)NO_ERROR);
 }
 
 AMPEG4AudioAssembler::~AMPEG4AudioAssembler() {
@@ -108,13 +471,7 @@
     while (it != mPackets.end()) {
         const sp<ABuffer> &unit = *it;
 
-        size_t n = 0;
-        while (unit->data()[n] == 0xff) {
-            ++n;
-        }
-        ++n;
-
-        totalSize += unit->size() - n;
+        totalSize += unit->size();
         ++it;
     }
 
@@ -124,20 +481,13 @@
     while (it != mPackets.end()) {
         const sp<ABuffer> &unit = *it;
 
-        size_t n = 0;
-        while (unit->data()[n] == 0xff) {
-            ++n;
-        }
-        ++n;
-
         memcpy((uint8_t *)accessUnit->data() + offset,
-               unit->data() + n, unit->size() - n);
-
-        offset += unit->size() - n;
+               unit->data(), unit->size());
 
         ++it;
     }
 
+    accessUnit = removeLATMFraming(accessUnit);
     CopyTimes(accessUnit, *mPackets.begin());
 
 #if 0
diff --git a/media/libstagefright/rtsp/AMPEG4AudioAssembler.h b/media/libstagefright/rtsp/AMPEG4AudioAssembler.h
index bf9f204..9cef94c 100644
--- a/media/libstagefright/rtsp/AMPEG4AudioAssembler.h
+++ b/media/libstagefright/rtsp/AMPEG4AudioAssembler.h
@@ -27,9 +27,11 @@
 namespace android {
 
 struct AMessage;
+struct AString;
 
 struct AMPEG4AudioAssembler : public ARTPAssembler {
-    AMPEG4AudioAssembler(const sp<AMessage> &notify);
+    AMPEG4AudioAssembler(
+            const sp<AMessage> &notify, const AString &params);
 
 protected:
     virtual ~AMPEG4AudioAssembler();
@@ -40,6 +42,13 @@
 
 private:
     sp<AMessage> mNotifyMsg;
+
+    bool mMuxConfigPresent;
+    unsigned mNumSubFrames;
+    unsigned mFrameLengthType;
+    bool mOtherDataPresent;
+    unsigned mOtherDataLenBits;
+
     uint32_t mAccessUnitRTPTime;
     bool mNextExpectedSeqNoValid;
     uint32_t mNextExpectedSeqNo;
@@ -49,6 +58,8 @@
     AssemblyStatus addPacket(const sp<ARTPSource> &source);
     void submitAccessUnit();
 
+    sp<ABuffer> removeLATMFraming(const sp<ABuffer> &buffer);
+
     DISALLOW_EVIL_CONSTRUCTORS(AMPEG4AudioAssembler);
 };
 
diff --git a/media/libstagefright/rtsp/ARTPSource.cpp b/media/libstagefright/rtsp/ARTPSource.cpp
index 2518264..5aae4e7 100644
--- a/media/libstagefright/rtsp/ARTPSource.cpp
+++ b/media/libstagefright/rtsp/ARTPSource.cpp
@@ -57,7 +57,7 @@
         mAssembler = new AAVCAssembler(notify);
         mIssueFIRRequests = true;
     } else if (!strncmp(desc.c_str(), "MP4A-LATM/", 10)) {
-        mAssembler = new AMPEG4AudioAssembler(notify);
+        mAssembler = new AMPEG4AudioAssembler(notify, params);
     } else if (!strncmp(desc.c_str(), "H263-1998/", 10)
             || !strncmp(desc.c_str(), "H263-2000/", 10)) {
         mAssembler = new AH263Assembler(notify);
diff --git a/media/libstagefright/rtsp/ARTSPConnection.cpp b/media/libstagefright/rtsp/ARTSPConnection.cpp
index f928c06..e936923 100644
--- a/media/libstagefright/rtsp/ARTSPConnection.cpp
+++ b/media/libstagefright/rtsp/ARTSPConnection.cpp
@@ -23,11 +23,13 @@
 #include <media/stagefright/foundation/ABuffer.h>
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/foundation/base64.h>
 #include <media/stagefright/MediaErrors.h>
 
 #include <arpa/inet.h>
 #include <fcntl.h>
 #include <netdb.h>
+#include <openssl/md5.h>
 #include <sys/socket.h>
 
 namespace android {
@@ -37,6 +39,7 @@
 
 ARTSPConnection::ARTSPConnection()
     : mState(DISCONNECTED),
+      mAuthType(NONE),
       mSocket(-1),
       mConnectionID(0),
       mNextCSeq(0),
@@ -114,10 +117,13 @@
 
 // static
 bool ARTSPConnection::ParseURL(
-        const char *url, AString *host, unsigned *port, AString *path) {
+        const char *url, AString *host, unsigned *port, AString *path,
+        AString *user, AString *pass) {
     host->clear();
     *port = 0;
     path->clear();
+    user->clear();
+    pass->clear();
 
     if (strncasecmp("rtsp://", url, 7)) {
         return false;
@@ -133,6 +139,24 @@
         path->setTo(slashPos);
     }
 
+    ssize_t atPos = host->find("@");
+
+    if (atPos >= 0) {
+        // Split of user:pass@ from hostname.
+
+        AString userPass(*host, 0, atPos);
+        host->erase(0, atPos + 1);
+
+        ssize_t colonPos = userPass.find(":");
+
+        if (colonPos < 0) {
+            *user = userPass;
+        } else {
+            user->setTo(userPass, 0, colonPos);
+            pass->setTo(userPass, colonPos + 1, userPass.size() - colonPos - 1);
+        }
+    }
+
     const char *colonPos = strchr(host->c_str(), ':');
 
     if (colonPos != NULL) {
@@ -187,7 +211,12 @@
 
     AString host, path;
     unsigned port;
-    if (!ParseURL(url.c_str(), &host, &port, &path)) {
+    if (!ParseURL(url.c_str(), &host, &port, &path, &mUser, &mPass)
+            || (mUser.size() > 0 && mPass.size() == 0)) {
+        // If we have a user name but no password we have to give up
+        // right here, since we currently have no way of asking the user
+        // for this information.
+
         LOGE("Malformed rtsp url %s", url.c_str());
 
         reply->setInt32("result", ERROR_MALFORMED);
@@ -197,6 +226,10 @@
         return;
     }
 
+    if (mUser.size() > 0) {
+        LOGV("user = '%s', pass = '%s'", mUser.c_str(), mPass.c_str());
+    }
+
     struct hostent *ent = gethostbyname(host.c_str());
     if (ent == NULL) {
         LOGE("Unknown host %s", host.c_str());
@@ -262,6 +295,11 @@
     reply->setInt32("result", OK);
     mState = DISCONNECTED;
 
+    mUser.clear();
+    mPass.clear();
+    mAuthType = NONE;
+    mNonce.clear();
+
     reply->post();
 }
 
@@ -335,6 +373,12 @@
     AString request;
     CHECK(msg->findString("request", &request));
 
+    // Just in case we need to re-issue the request with proper authentication
+    // later, stash it away.
+    reply->setString("original-request", request.c_str(), request.size());
+
+    addAuthentication(&request);
+
     // Find the boundary between headers and the body.
     ssize_t i = request.find("\r\n\r\n");
     CHECK_GE(i, 0);
@@ -347,7 +391,7 @@
 
     request.insert(cseqHeader, i + 2);
 
-    LOGV("%s", request.c_str());
+    LOGV("request: '%s'", request.c_str());
 
     size_t numBytesSent = 0;
     while (numBytesSent < request.size()) {
@@ -612,6 +656,30 @@
         }
     }
 
+    if (response->mStatusCode == 401) {
+        if (mAuthType == NONE && mUser.size() > 0
+                && parseAuthMethod(response)) {
+            ssize_t i;
+            CHECK_EQ((status_t)OK, findPendingRequest(response, &i));
+            CHECK_GE(i, 0);
+
+            sp<AMessage> reply = mPendingRequests.valueAt(i);
+            mPendingRequests.removeItemsAt(i);
+
+            AString request;
+            CHECK(reply->findString("original-request", &request));
+
+            sp<AMessage> msg = new AMessage(kWhatSendRequest, id());
+            msg->setMessage("reply", reply);
+            msg->setString("request", request.c_str(), request.size());
+
+            LOGI("re-sending request with authentication headers...");
+            onSendRequest(msg);
+
+            return true;
+        }
+    }
+
     return notifyResponseListener(response);
 }
 
@@ -628,26 +696,47 @@
     return true;
 }
 
-bool ARTSPConnection::notifyResponseListener(
-        const sp<ARTSPResponse> &response) {
+status_t ARTSPConnection::findPendingRequest(
+        const sp<ARTSPResponse> &response, ssize_t *index) const {
+    *index = 0;
+
     ssize_t i = response->mHeaders.indexOfKey("cseq");
 
     if (i < 0) {
-        return true;
+        // This is an unsolicited server->client message.
+        return OK;
     }
 
     AString value = response->mHeaders.valueAt(i);
 
     unsigned long cseq;
     if (!ParseSingleUnsignedLong(value.c_str(), &cseq)) {
-        return false;
+        return ERROR_MALFORMED;
     }
 
     i = mPendingRequests.indexOfKey(cseq);
 
     if (i < 0) {
-        // Unsolicited response?
-        TRESPASS();
+        return -ENOENT;
+    }
+
+    *index = i;
+
+    return OK;
+}
+
+bool ARTSPConnection::notifyResponseListener(
+        const sp<ARTSPResponse> &response) {
+    ssize_t i;
+    status_t err = findPendingRequest(response, &i);
+
+    if (err == OK && i < 0) {
+        // An unsolicited server response is not a problem.
+        return true;
+    }
+
+    if (err != OK) {
+        return false;
     }
 
     sp<AMessage> reply = mPendingRequests.valueAt(i);
@@ -660,4 +749,160 @@
     return true;
 }
 
+bool ARTSPConnection::parseAuthMethod(const sp<ARTSPResponse> &response) {
+    ssize_t i = response->mHeaders.indexOfKey("www-authenticate");
+
+    if (i < 0) {
+        return false;
+    }
+
+    AString value = response->mHeaders.valueAt(i);
+
+    if (!strncmp(value.c_str(), "Basic", 5)) {
+        mAuthType = BASIC;
+    } else {
+#if !defined(HAVE_ANDROID_OS)
+        // We don't have access to the MD5 implementation on the simulator,
+        // so we won't support digest authentication.
+        return false;
+#endif
+
+        CHECK(!strncmp(value.c_str(), "Digest", 6));
+        mAuthType = DIGEST;
+
+        i = value.find("nonce=");
+        CHECK_GE(i, 0);
+        CHECK_EQ(value.c_str()[i + 6], '\"');
+        ssize_t j = value.find("\"", i + 7);
+        CHECK_GE(j, 0);
+
+        mNonce.setTo(value, i + 7, j - i - 7);
+    }
+
+    return true;
+}
+
+#if defined(HAVE_ANDROID_OS)
+static void H(const AString &s, AString *out) {
+    out->clear();
+
+    MD5_CTX m;
+    MD5_Init(&m);
+    MD5_Update(&m, s.c_str(), s.size());
+
+    uint8_t key[16];
+    MD5_Final(key, &m);
+
+    for (size_t i = 0; i < 16; ++i) {
+        char nibble = key[i] >> 4;
+        if (nibble <= 9) {
+            nibble += '0';
+        } else {
+            nibble += 'a' - 10;
+        }
+        out->append(&nibble, 1);
+
+        nibble = key[i] & 0x0f;
+        if (nibble <= 9) {
+            nibble += '0';
+        } else {
+            nibble += 'a' - 10;
+        }
+        out->append(&nibble, 1);
+    }
+}
+#endif
+
+static void GetMethodAndURL(
+        const AString &request, AString *method, AString *url) {
+    ssize_t space1 = request.find(" ");
+    CHECK_GE(space1, 0);
+
+    ssize_t space2 = request.find(" ", space1 + 1);
+    CHECK_GE(space2, 0);
+
+    method->setTo(request, 0, space1);
+    url->setTo(request, space1 + 1, space2 - space1);
+}
+
+void ARTSPConnection::addAuthentication(AString *request) {
+    if (mAuthType == NONE) {
+        return;
+    }
+
+    // Find the boundary between headers and the body.
+    ssize_t i = request->find("\r\n\r\n");
+    CHECK_GE(i, 0);
+
+    if (mAuthType == BASIC) {
+        AString tmp;
+        tmp.append(mUser);
+        tmp.append(":");
+        tmp.append(mPass);
+
+        AString out;
+        encodeBase64(tmp.c_str(), tmp.size(), &out);
+
+        AString fragment;
+        fragment.append("Authorization: Basic ");
+        fragment.append(out);
+        fragment.append("\r\n");
+
+        request->insert(fragment, i + 2);
+
+        return;
+    }
+
+#if defined(HAVE_ANDROID_OS)
+    CHECK_EQ((int)mAuthType, (int)DIGEST);
+
+    AString method, url;
+    GetMethodAndURL(*request, &method, &url);
+
+    AString A1;
+    A1.append(mUser);
+    A1.append(":");
+    A1.append("Streaming Server");
+    A1.append(":");
+    A1.append(mPass);
+
+    AString A2;
+    A2.append(method);
+    A2.append(":");
+    A2.append(url);
+
+    AString HA1, HA2;
+    H(A1, &HA1);
+    H(A2, &HA2);
+
+    AString tmp;
+    tmp.append(HA1);
+    tmp.append(":");
+    tmp.append(mNonce);
+    tmp.append(":");
+    tmp.append(HA2);
+
+    AString digest;
+    H(tmp, &digest);
+
+    AString fragment;
+    fragment.append("Authorization: Digest ");
+    fragment.append("nonce=\"");
+    fragment.append(mNonce);
+    fragment.append("\", ");
+    fragment.append("username=\"");
+    fragment.append(mUser);
+    fragment.append("\", ");
+    fragment.append("uri=\"");
+    fragment.append(url);
+    fragment.append("\", ");
+    fragment.append("response=\"");
+    fragment.append(digest);
+    fragment.append("\"");
+    fragment.append("\r\n");
+
+    request->insert(fragment, i + 2);
+#endif
+}
+
 }  // namespace android
diff --git a/media/libstagefright/rtsp/ARTSPConnection.h b/media/libstagefright/rtsp/ARTSPConnection.h
index 96e0d5b..19be2a6 100644
--- a/media/libstagefright/rtsp/ARTSPConnection.h
+++ b/media/libstagefright/rtsp/ARTSPConnection.h
@@ -42,6 +42,10 @@
 
     void observeBinaryData(const sp<AMessage> &reply);
 
+    static bool ParseURL(
+            const char *url, AString *host, unsigned *port, AString *path,
+            AString *user, AString *pass);
+
 protected:
     virtual ~ARTSPConnection();
     virtual void onMessageReceived(const sp<AMessage> &msg);
@@ -62,9 +66,18 @@
         kWhatObserveBinaryData  = 'obin',
     };
 
+    enum AuthType {
+        NONE,
+        BASIC,
+        DIGEST
+    };
+
     static const int64_t kSelectTimeoutUs;
 
     State mState;
+    AString mUser, mPass;
+    AuthType mAuthType;
+    AString mNonce;
     int mSocket;
     int32_t mConnectionID;
     int32_t mNextCSeq;
@@ -90,8 +103,11 @@
     sp<ABuffer> receiveBinaryData();
     bool notifyResponseListener(const sp<ARTSPResponse> &response);
 
-    static bool ParseURL(
-            const char *url, AString *host, unsigned *port, AString *path);
+    bool parseAuthMethod(const sp<ARTSPResponse> &response);
+    void addAuthentication(AString *request);
+
+    status_t findPendingRequest(
+            const sp<ARTSPResponse> &response, ssize_t *index) const;
 
     static bool ParseSingleUnsignedLong(
             const char *from, unsigned long *x);
diff --git a/media/libstagefright/rtsp/ASessionDescription.cpp b/media/libstagefright/rtsp/ASessionDescription.cpp
index 612caff..547fbab 100644
--- a/media/libstagefright/rtsp/ASessionDescription.cpp
+++ b/media/libstagefright/rtsp/ASessionDescription.cpp
@@ -53,21 +53,30 @@
     mFormats.push(AString("[root]"));
 
     AString desc((const char *)data, size);
-    LOGI("%s", desc.c_str());
 
     size_t i = 0;
     for (;;) {
-        ssize_t eolPos = desc.find("\r\n", i);
+        ssize_t eolPos = desc.find("\n", i);
+
         if (eolPos < 0) {
             break;
         }
 
-        AString line(desc, i, eolPos - i);
+        AString line;
+        if ((size_t)eolPos > i && desc.c_str()[eolPos - 1] == '\r') {
+            // We accept both '\n' and '\r\n' line endings, if it's
+            // the latter, strip the '\r' as well.
+            line.setTo(desc, i, eolPos - i - 1);
+        } else {
+            line.setTo(desc, i, eolPos - i);
+        }
 
         if (line.size() < 2 || line.c_str()[1] != '=') {
             return false;
         }
 
+        LOGI("%s", line.c_str());
+
         switch (line.c_str()[0]) {
             case 'v':
             {
@@ -141,7 +150,7 @@
             }
         }
 
-        i = eolPos + 2;
+        i = eolPos + 1;
     }
 
     return true;
@@ -245,7 +254,7 @@
         return false;
     }
 
-    if (value == "npt=now-") {
+    if (value == "npt=now-" || value == "npt=0-") {
         return false;
     }
 
diff --git a/media/libstagefright/rtsp/Android.mk b/media/libstagefright/rtsp/Android.mk
index 081ae32..0bbadc1 100644
--- a/media/libstagefright/rtsp/Android.mk
+++ b/media/libstagefright/rtsp/Android.mk
@@ -23,6 +23,7 @@
 	$(JNI_H_INCLUDE) \
 	$(TOP)/frameworks/base/include/media/stagefright/openmax \
         $(TOP)/frameworks/base/media/libstagefright/include \
+        $(TOP)/external/openssl/include
 
 LOCAL_MODULE:= libstagefright_rtsp
 
diff --git a/media/libstagefright/rtsp/MyHandler.h b/media/libstagefright/rtsp/MyHandler.h
index 6943608..9bb8c46 100644
--- a/media/libstagefright/rtsp/MyHandler.h
+++ b/media/libstagefright/rtsp/MyHandler.h
@@ -96,6 +96,7 @@
           mNetLooper(new ALooper),
           mConn(new ARTSPConnection),
           mRTPConn(new ARTPConnection),
+          mOriginalSessionURL(url),
           mSessionURL(url),
           mSetupTracksSuccessful(false),
           mSeekPending(false),
@@ -113,6 +114,23 @@
         mNetLooper->start(false /* runOnCallingThread */,
                           false /* canCallJava */,
                           PRIORITY_HIGHEST);
+
+        // Strip any authentication info from the session url, we don't
+        // want to transmit user/pass in cleartext.
+        AString host, path, user, pass;
+        unsigned port;
+        if (ARTSPConnection::ParseURL(
+                    mSessionURL.c_str(), &host, &port, &path, &user, &pass)
+                && user.size() > 0) {
+            mSessionURL.clear();
+            mSessionURL.append("rtsp://");
+            mSessionURL.append(host);
+            mSessionURL.append(":");
+            mSessionURL.append(StringPrintf("%u", port));
+            mSessionURL.append(path);
+
+            LOGI("rewritten session url: '%s'", mSessionURL.c_str());
+        }
     }
 
     void connect(const sp<AMessage> &doneMsg) {
@@ -126,7 +144,7 @@
         mConn->observeBinaryData(notify);
 
         sp<AMessage> reply = new AMessage('conn', id());
-        mConn->connect(mSessionURL.c_str(), reply);
+        mConn->connect(mOriginalSessionURL.c_str(), reply);
     }
 
     void disconnect(const sp<AMessage> &doneMsg) {
@@ -312,7 +330,7 @@
                 int32_t reconnect;
                 if (msg->findInt32("reconnect", &reconnect) && reconnect) {
                     sp<AMessage> reply = new AMessage('conn', id());
-                    mConn->connect(mSessionURL.c_str(), reply);
+                    mConn->connect(mOriginalSessionURL.c_str(), reply);
                 } else {
                     (new AMessage('quit', id()))->post();
                 }
@@ -922,7 +940,7 @@
         CHECK(GetAttribute(range.c_str(), "npt", &val));
         float npt1, npt2;
 
-        if (val == "now-") {
+        if (val == "now-" || val == "0-") {
             // This is a live stream and therefore not seekable.
             return;
         } else {
@@ -992,6 +1010,7 @@
     sp<ARTSPConnection> mConn;
     sp<ARTPConnection> mRTPConn;
     sp<ASessionDescription> mSessionDesc;
+    AString mOriginalSessionURL;  // This one still has user:pass@
     AString mSessionURL;
     AString mBaseURL;
     AString mSessionID;