DO NOT MERGE: Fix parsing of ntp= PLAY response.

related-to-bug: 3340186

Squashed commit of the following:

commit b61c36b7228aec9f5360883b1e1c1e0530488974
Author: Andreas Huber <andih@google.com>
Date:   Wed Oct 27 13:59:59 2010 -0700

    Better support for MP4A-LATM RTP disassembly. This used to fail if mNumSubFrames > 1 and the sub frames did not align with RTP packet boundaries.

commit b10f322c07e5bebcaf032e8624cb4a5d733dfc15
Author: Andreas Huber <andih@google.com>
Date:   Mon Oct 25 09:40:52 2010 -0700

    We don't have access to the md5 implementation on the simulator, let's disable digest authentication in rtsp for simulator targets.

commit 0aa83cf9e4637adf9501708fcdf7d0d6d4dc4fe1
Author: Andreas Huber <andih@google.com>
Date:   Wed Oct 20 15:00:34 2010 -0700

    Support for BASIC and DIGEST authentication schemes in RTSP. Support for malformed packet descriptions that end lines in LF only, instead of CRLF.

    related-to-bug: 3084183

Change-Id: I6e512cb73cc8d5624a83f7154aa5699f7fef7534
This commit is contained in:
Andreas Huber
2011-01-11 09:51:34 -08:00
parent 65ba2c421c
commit 549f12ad04
8 changed files with 690 additions and 39 deletions

View File

@ -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 @@ void AMPEG4AudioAssembler::submitAccessUnit() {
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 @@ void AMPEG4AudioAssembler::submitAccessUnit() {
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

View File

@ -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 @@ protected:
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 @@ private:
AssemblyStatus addPacket(const sp<ARTPSource> &source);
void submitAccessUnit();
sp<ABuffer> removeLATMFraming(const sp<ABuffer> &buffer);
DISALLOW_EVIL_CONSTRUCTORS(AMPEG4AudioAssembler);
};

View File

@ -57,7 +57,7 @@ ARTPSource::ARTPSource(
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);

View File

@ -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 @@ const int64_t ARTSPConnection::kSelectTimeoutUs = 1000ll;
ARTSPConnection::ARTSPConnection()
: mState(DISCONNECTED),
mAuthType(NONE),
mSocket(-1),
mConnectionID(0),
mNextCSeq(0),
@ -114,10 +117,13 @@ void ARTSPConnection::onMessageReceived(const sp<AMessage> &msg) {
// 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,7 +139,25 @@ bool ARTSPConnection::ParseURL(
path->setTo(slashPos);
}
char *colonPos = strchr(host->c_str(), ':');
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) {
unsigned long x;
@ -187,7 +211,12 @@ void ARTSPConnection::onConnect(const sp<AMessage> &msg) {
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 @@ void ARTSPConnection::onConnect(const sp<AMessage> &msg) {
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 @@ void ARTSPConnection::onDisconnect(const sp<AMessage> &msg) {
reply->setInt32("result", OK);
mState = DISCONNECTED;
mUser.clear();
mPass.clear();
mAuthType = NONE;
mNonce.clear();
reply->post();
}
@ -335,6 +373,12 @@ void ARTSPConnection::onSendRequest(const sp<AMessage> &msg) {
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 @@ void ARTSPConnection::onSendRequest(const sp<AMessage> &msg) {
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 @@ bool ARTSPConnection::receiveRTSPReponse() {
}
}
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 @@ bool ARTSPConnection::ParseSingleUnsignedLong(
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 @@ bool ARTSPConnection::notifyResponseListener(
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

View File

@ -42,6 +42,10 @@ struct ARTSPConnection : public AHandler {
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 @@ private:
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 @@ private:
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);

View File

@ -53,21 +53,30 @@ bool ASessionDescription::parse(const void *data, size_t size) {
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 @@ bool ASessionDescription::parse(const void *data, size_t size) {
}
}
i = eolPos + 2;
i = eolPos + 1;
}
return true;
@ -245,7 +254,7 @@ bool ASessionDescription::getDurationUs(int64_t *durationUs) const {
return false;
}
if (value == "npt=now-") {
if (value == "npt=now-" || value == "npt=0-") {
return false;
}

View File

@ -23,6 +23,7 @@ LOCAL_C_INCLUDES:= \
$(JNI_H_INCLUDE) \
$(TOP)/frameworks/base/include/media/stagefright/openmax \
$(TOP)/frameworks/base/media/libstagefright/include \
$(TOP)/external/openssl/include
LOCAL_MODULE:= libstagefright_rtsp

View File

@ -96,6 +96,7 @@ struct MyHandler : public AHandler {
mNetLooper(new ALooper),
mConn(new ARTSPConnection),
mRTPConn(new ARTPConnection),
mOriginalSessionURL(url),
mSessionURL(url),
mSetupTracksSuccessful(false),
mSeekPending(false),
@ -113,6 +114,23 @@ struct MyHandler : public AHandler {
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 @@ struct MyHandler : public AHandler {
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 @@ struct MyHandler : public AHandler {
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 @@ struct MyHandler : public AHandler {
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 @@ private:
sp<ARTSPConnection> mConn;
sp<ARTPConnection> mRTPConn;
sp<ASessionDescription> mSessionDesc;
AString mOriginalSessionURL; // This one still has user:pass@
AString mSessionURL;
AString mBaseURL;
AString mSessionID;