Loading media/libstagefright/rtsp/AMPEG4AudioAssembler.cpp +367 −17 Original line number Diff line number Diff line Loading @@ -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> ¬ify) 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> ¬ify, const AString ¶ms) : 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() { Loading Loading @@ -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; } Loading @@ -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 Loading media/libstagefright/rtsp/AMPEG4AudioAssembler.h +12 −1 Original line number Diff line number Diff line Loading @@ -27,9 +27,11 @@ namespace android { struct AMessage; struct AString; struct AMPEG4AudioAssembler : public ARTPAssembler { AMPEG4AudioAssembler(const sp<AMessage> ¬ify); AMPEG4AudioAssembler( const sp<AMessage> ¬ify, const AString ¶ms); protected: virtual ~AMPEG4AudioAssembler(); Loading @@ -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; Loading @@ -49,6 +58,8 @@ private: AssemblyStatus addPacket(const sp<ARTPSource> &source); void submitAccessUnit(); sp<ABuffer> removeLATMFraming(const sp<ABuffer> &buffer); DISALLOW_EVIL_CONSTRUCTORS(AMPEG4AudioAssembler); }; Loading media/libstagefright/rtsp/ARTPSource.cpp +1 −1 Original line number Diff line number Diff line Loading @@ -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); Loading media/libstagefright/rtsp/ARTSPConnection.cpp +255 −10 Original line number Diff line number Diff line Loading @@ -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 { Loading @@ -37,6 +39,7 @@ const int64_t ARTSPConnection::kSelectTimeoutUs = 1000ll; ARTSPConnection::ARTSPConnection() : mState(DISCONNECTED), mAuthType(NONE), mSocket(-1), mConnectionID(0), mNextCSeq(0), Loading Loading @@ -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; Loading @@ -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; Loading Loading @@ -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); Loading @@ -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()); Loading Loading @@ -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(); } Loading Loading @@ -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); Loading @@ -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()) { Loading Loading @@ -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); } Loading @@ -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); Loading @@ -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 media/libstagefright/rtsp/ARTSPConnection.h +18 −2 Original line number Diff line number Diff line Loading @@ -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); Loading @@ -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; Loading @@ -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); Loading Loading
media/libstagefright/rtsp/AMPEG4AudioAssembler.cpp +367 −17 Original line number Diff line number Diff line Loading @@ -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> ¬ify) 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> ¬ify, const AString ¶ms) : 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() { Loading Loading @@ -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; } Loading @@ -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 Loading
media/libstagefright/rtsp/AMPEG4AudioAssembler.h +12 −1 Original line number Diff line number Diff line Loading @@ -27,9 +27,11 @@ namespace android { struct AMessage; struct AString; struct AMPEG4AudioAssembler : public ARTPAssembler { AMPEG4AudioAssembler(const sp<AMessage> ¬ify); AMPEG4AudioAssembler( const sp<AMessage> ¬ify, const AString ¶ms); protected: virtual ~AMPEG4AudioAssembler(); Loading @@ -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; Loading @@ -49,6 +58,8 @@ private: AssemblyStatus addPacket(const sp<ARTPSource> &source); void submitAccessUnit(); sp<ABuffer> removeLATMFraming(const sp<ABuffer> &buffer); DISALLOW_EVIL_CONSTRUCTORS(AMPEG4AudioAssembler); }; Loading
media/libstagefright/rtsp/ARTPSource.cpp +1 −1 Original line number Diff line number Diff line Loading @@ -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); Loading
media/libstagefright/rtsp/ARTSPConnection.cpp +255 −10 Original line number Diff line number Diff line Loading @@ -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 { Loading @@ -37,6 +39,7 @@ const int64_t ARTSPConnection::kSelectTimeoutUs = 1000ll; ARTSPConnection::ARTSPConnection() : mState(DISCONNECTED), mAuthType(NONE), mSocket(-1), mConnectionID(0), mNextCSeq(0), Loading Loading @@ -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; Loading @@ -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; Loading Loading @@ -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); Loading @@ -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()); Loading Loading @@ -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(); } Loading Loading @@ -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); Loading @@ -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()) { Loading Loading @@ -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); } Loading @@ -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); Loading @@ -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
media/libstagefright/rtsp/ARTSPConnection.h +18 −2 Original line number Diff line number Diff line Loading @@ -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); Loading @@ -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; Loading @@ -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); Loading