diff options
author | Andreas Huber <andih@google.com> | 2011-02-15 10:39:48 -0800 |
---|---|---|
committer | Andreas Huber <andih@google.com> | 2011-02-15 12:00:07 -0800 |
commit | dc468c5f9d72ce54de0070493e9a23efb8907e06 (patch) | |
tree | 60bc0bbce4d9d765f13235d702443fecbe7e03ea /media/libstagefright | |
parent | f1958f9442bc937e1f8c8d9175901500b944b021 (diff) | |
download | frameworks_av-dc468c5f9d72ce54de0070493e9a23efb8907e06.zip frameworks_av-dc468c5f9d72ce54de0070493e9a23efb8907e06.tar.gz frameworks_av-dc468c5f9d72ce54de0070493e9a23efb8907e06.tar.bz2 |
Work around several issues with non-compliant RTSP servers.
In this particular case these RTSP servers were implemented as local services,
retransmitting live streams via a local RTSP server instance.
They picked wrong rtp/rtcp port pairs (odd rtp port), blank lines in the session
description, wrong case of the format description, relative base URLs...
Change-Id: I63fa90ca2398f19e8b52c147248bd2c5c2372426
related-to-bug: 3452103
Diffstat (limited to 'media/libstagefright')
-rw-r--r-- | media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp | 2 | ||||
-rw-r--r-- | media/libstagefright/rtsp/APacketSource.cpp | 2 | ||||
-rw-r--r-- | media/libstagefright/rtsp/ARTPConnection.cpp | 4 | ||||
-rw-r--r-- | media/libstagefright/rtsp/ARTPSource.cpp | 2 | ||||
-rw-r--r-- | media/libstagefright/rtsp/ASessionDescription.cpp | 5 | ||||
-rw-r--r-- | media/libstagefright/rtsp/MyHandler.h | 99 |
6 files changed, 89 insertions, 25 deletions
diff --git a/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp b/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp index 13988cd..9f6bd29 100644 --- a/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp +++ b/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp @@ -104,7 +104,7 @@ AMPEG4ElementaryAssembler::AMPEG4ElementaryAssembler( mNextExpectedSeqNoValid(false), mNextExpectedSeqNo(0), mAccessUnitDamaged(false) { - mIsGeneric = desc.startsWith("mpeg4-generic/"); + mIsGeneric = !strncasecmp(desc.c_str(),"mpeg4-generic/", 14); if (mIsGeneric) { AString value; diff --git a/media/libstagefright/rtsp/APacketSource.cpp b/media/libstagefright/rtsp/APacketSource.cpp index f0b858d..679dcab 100644 --- a/media/libstagefright/rtsp/APacketSource.cpp +++ b/media/libstagefright/rtsp/APacketSource.cpp @@ -637,7 +637,7 @@ APacketSource::APacketSource( mFormat->setInt32(kKeyWidth, width); mFormat->setInt32(kKeyHeight, height); - } else if (!strncmp(desc.c_str(), "mpeg4-generic/", 14)) { + } else if (!strncasecmp(desc.c_str(), "mpeg4-generic/", 14)) { AString val; if (!GetAttribute(params.c_str(), "mode", &val) || (strcasecmp(val.c_str(), "AAC-lbr") diff --git a/media/libstagefright/rtsp/ARTPConnection.cpp b/media/libstagefright/rtsp/ARTPConnection.cpp index 601f569..47de4e0 100644 --- a/media/libstagefright/rtsp/ARTPConnection.cpp +++ b/media/libstagefright/rtsp/ARTPConnection.cpp @@ -123,7 +123,7 @@ void ARTPConnection::MakePortPair( struct sockaddr_in addr; memset(addr.sin_zero, 0, sizeof(addr.sin_zero)); addr.sin_family = AF_INET; - addr.sin_addr.s_addr = INADDR_ANY; + addr.sin_addr.s_addr = htonl(INADDR_ANY); addr.sin_port = htons(port); if (bind(*rtpSocket, @@ -340,6 +340,8 @@ void ARTPConnection::onPollStreams() { } status_t ARTPConnection::receive(StreamInfo *s, bool receiveRTP) { + LOGV("receiving %s", receiveRTP ? "RTP" : "RTCP"); + CHECK(!s->mIsInjected); sp<ABuffer> buffer = new ABuffer(65536); diff --git a/media/libstagefright/rtsp/ARTPSource.cpp b/media/libstagefright/rtsp/ARTPSource.cpp index 893a387..84c666f 100644 --- a/media/libstagefright/rtsp/ARTPSource.cpp +++ b/media/libstagefright/rtsp/ARTPSource.cpp @@ -67,7 +67,7 @@ ARTPSource::ARTPSource( } else if (!strncmp(desc.c_str(), "AMR-WB/", 7)) { mAssembler = new AAMRAssembler(notify, true /* isWide */, params); } else if (!strncmp(desc.c_str(), "MP4V-ES/", 8) - || !strncmp(desc.c_str(), "mpeg4-generic/", 14)) { + || !strncasecmp(desc.c_str(), "mpeg4-generic/", 14)) { mAssembler = new AMPEG4ElementaryAssembler(notify, desc, params); mIssueFIRRequests = true; } else { diff --git a/media/libstagefright/rtsp/ASessionDescription.cpp b/media/libstagefright/rtsp/ASessionDescription.cpp index 3e710dc..f03f7a2 100644 --- a/media/libstagefright/rtsp/ASessionDescription.cpp +++ b/media/libstagefright/rtsp/ASessionDescription.cpp @@ -71,6 +71,11 @@ bool ASessionDescription::parse(const void *data, size_t size) { line.setTo(desc, i, eolPos - i); } + if (line.empty()) { + i = eolPos + 1; + continue; + } + if (line.size() < 2 || line.c_str()[1] != '=') { return false; } diff --git a/media/libstagefright/rtsp/MyHandler.h b/media/libstagefright/rtsp/MyHandler.h index ba7c1b2..e6ae52b 100644 --- a/media/libstagefright/rtsp/MyHandler.h +++ b/media/libstagefright/rtsp/MyHandler.h @@ -248,7 +248,7 @@ struct MyHandler : public AHandler { // In case we're behind NAT, fire off two UDP packets to the remote // rtp/rtcp ports to poke a hole into the firewall for future incoming // packets. We're going to send an RR/SDES RTCP packet to both of them. - void pokeAHole(int rtpSocket, int rtcpSocket, const AString &transport) { + bool pokeAHole(int rtpSocket, int rtcpSocket, const AString &transport) { AString source; AString server_port; if (!GetAttribute(transport.c_str(), @@ -257,16 +257,25 @@ struct MyHandler : public AHandler { || !GetAttribute(transport.c_str(), "server_port", &server_port)) { - return; + return false; } int rtpPort, rtcpPort; if (sscanf(server_port.c_str(), "%d-%d", &rtpPort, &rtcpPort) != 2 || rtpPort <= 0 || rtpPort > 65535 || rtcpPort <=0 || rtcpPort > 65535 - || rtcpPort != rtpPort + 1 - || (rtpPort & 1) != 0) { - return; + || rtcpPort != rtpPort + 1) { + LOGE("Server picked invalid RTP/RTCP port pair %s," + " RTP port must be even, RTCP port must be one higher.", + server_port.c_str()); + + return false; + } + + if (rtpPort & 1) { + LOGW("Server picked an odd RTP port, it should've picked an " + "even one, we'll let it pass for now, but this may break " + "in the future."); } struct sockaddr_in addr; @@ -275,7 +284,12 @@ struct MyHandler : public AHandler { addr.sin_addr.s_addr = inet_addr(source.c_str()); if (addr.sin_addr.s_addr == INADDR_NONE) { - return; + return true; + } + + if (IN_LOOPBACK(ntohl(addr.sin_addr.s_addr))) { + // No firewalls to traverse on the loopback interface. + return true; } // Make up an RR/SDES RTCP packet. @@ -289,16 +303,26 @@ struct MyHandler : public AHandler { ssize_t n = sendto( rtpSocket, buf->data(), buf->size(), 0, (const sockaddr *)&addr, sizeof(addr)); - CHECK_EQ(n, (ssize_t)buf->size()); + + if (n < (ssize_t)buf->size()) { + LOGE("failed to poke a hole for RTP packets"); + return false; + } addr.sin_port = htons(rtcpPort); n = sendto( rtcpSocket, buf->data(), buf->size(), 0, (const sockaddr *)&addr, sizeof(addr)); - CHECK_EQ(n, (ssize_t)buf->size()); + + if (n < (ssize_t)buf->size()) { + LOGE("failed to poke a hole for RTCP packets"); + return false; + } LOGV("successfully poked holes."); + + return true; } virtual void onMessageReceived(const sp<AMessage> &msg) { @@ -381,6 +405,7 @@ struct MyHandler : public AHandler { response->mContent->size()); if (!mSessionDesc->isValid()) { + LOGE("Failed to parse session description."); result = ERROR_MALFORMED; } else { ssize_t i = response->mHeaders.indexOfKey("content-base"); @@ -395,6 +420,25 @@ struct MyHandler : public AHandler { } } + if (!mBaseURL.startsWith("rtsp://")) { + // Some misbehaving servers specify a relative + // URL in one of the locations above, combine + // it with the absolute session URL to get + // something usable... + + LOGW("Server specified a non-absolute base URL" + ", combining it with the session URL to " + "get something usable..."); + + AString tmp; + CHECK(MakeURL( + mSessionURL.c_str(), + mBaseURL.c_str(), + &tmp)); + + mBaseURL = tmp; + } + CHECK_GT(mSessionDesc->countTracks(), 1u); setupTrack(1); } @@ -455,17 +499,22 @@ struct MyHandler : public AHandler { if (!track->mUsingInterleavedTCP) { AString transport = response->mHeaders.valueAt(i); - pokeAHole(track->mRTPSocket, - track->mRTCPSocket, - transport); + if (!pokeAHole( + track->mRTPSocket, + track->mRTCPSocket, + transport)) { + result = UNKNOWN_ERROR; + } } - mRTPConn->addStream( - track->mRTPSocket, track->mRTCPSocket, - mSessionDesc, index, - notify, track->mUsingInterleavedTCP); + if (result == OK) { + mRTPConn->addStream( + track->mRTPSocket, track->mRTCPSocket, + mSessionDesc, index, + notify, track->mUsingInterleavedTCP); - mSetupTracksSuccessful = true; + mSetupTracksSuccessful = true; + } } } @@ -853,17 +902,16 @@ struct MyHandler : public AHandler { case 'tiou': { if (!mReceivedFirstRTCPPacket) { - if (mTryFakeRTCP) { - LOGW("Never received any data, disconnecting."); - (new AMessage('abor', id()))->post(); - } else if (mTryTCPInterleaving && mReceivedFirstRTPPacket) { + if (mReceivedFirstRTPPacket && !mTryFakeRTCP) { LOGW("We received RTP packets but no RTCP packets, " "using fake timestamps."); mTryFakeRTCP = true; mReceivedFirstRTCPPacket = true; - } else { + + fakeTimestamps(); + } else if (!mReceivedFirstRTPPacket && !mTryTCPInterleaving) { LOGW("Never received any data, switching transports."); mTryTCPInterleaving = true; @@ -871,6 +919,9 @@ struct MyHandler : public AHandler { sp<AMessage> msg = new AMessage('abor', id()); msg->setInt32("reconnect", true); msg->post(); + } else { + LOGW("Never received any data, disconnecting."); + (new AMessage('abor', id()))->post(); } } break; @@ -1158,6 +1209,12 @@ private: return true; } + void fakeTimestamps() { + for (size_t i = 0; i < mTracks.size(); ++i) { + onTimeUpdate(i, 0, 0ll); + } + } + void onTimeUpdate(int32_t trackIndex, uint32_t rtpTime, uint64_t ntpTime) { LOGV("onTimeUpdate track %d, rtpTime = 0x%08x, ntpTime = 0x%016llx", trackIndex, rtpTime, ntpTime); |