am d1ba051a: DO NOT MERGE: Work around several issues with non-compliant RTSP servers.
* commit 'd1ba051a465518fa4325c364ed77025fc1a2a794': DO NOT MERGE: Work around several issues with non-compliant RTSP servers.
This commit is contained in:
@@ -104,7 +104,7 @@ AMPEG4ElementaryAssembler::AMPEG4ElementaryAssembler(
|
|||||||
mNextExpectedSeqNoValid(false),
|
mNextExpectedSeqNoValid(false),
|
||||||
mNextExpectedSeqNo(0),
|
mNextExpectedSeqNo(0),
|
||||||
mAccessUnitDamaged(false) {
|
mAccessUnitDamaged(false) {
|
||||||
mIsGeneric = desc.startsWith("mpeg4-generic/");
|
mIsGeneric = !strncasecmp(desc.c_str(),"mpeg4-generic/", 14);
|
||||||
|
|
||||||
if (mIsGeneric) {
|
if (mIsGeneric) {
|
||||||
AString value;
|
AString value;
|
||||||
|
|||||||
@@ -627,7 +627,7 @@ APacketSource::APacketSource(
|
|||||||
|
|
||||||
mFormat->setInt32(kKeyWidth, width);
|
mFormat->setInt32(kKeyWidth, width);
|
||||||
mFormat->setInt32(kKeyHeight, height);
|
mFormat->setInt32(kKeyHeight, height);
|
||||||
} else if (!strncmp(desc.c_str(), "mpeg4-generic/", 14)) {
|
} else if (!strncasecmp(desc.c_str(), "mpeg4-generic/", 14)) {
|
||||||
AString val;
|
AString val;
|
||||||
if (!GetAttribute(params.c_str(), "mode", &val)
|
if (!GetAttribute(params.c_str(), "mode", &val)
|
||||||
|| (strcasecmp(val.c_str(), "AAC-lbr")
|
|| (strcasecmp(val.c_str(), "AAC-lbr")
|
||||||
|
|||||||
@@ -123,7 +123,7 @@ void ARTPConnection::MakePortPair(
|
|||||||
struct sockaddr_in addr;
|
struct sockaddr_in addr;
|
||||||
memset(addr.sin_zero, 0, sizeof(addr.sin_zero));
|
memset(addr.sin_zero, 0, sizeof(addr.sin_zero));
|
||||||
addr.sin_family = AF_INET;
|
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);
|
addr.sin_port = htons(port);
|
||||||
|
|
||||||
if (bind(*rtpSocket,
|
if (bind(*rtpSocket,
|
||||||
@@ -346,6 +346,8 @@ void ARTPConnection::onPollStreams() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
status_t ARTPConnection::receive(StreamInfo *s, bool receiveRTP) {
|
status_t ARTPConnection::receive(StreamInfo *s, bool receiveRTP) {
|
||||||
|
LOGV("receiving %s", receiveRTP ? "RTP" : "RTCP");
|
||||||
|
|
||||||
CHECK(!s->mIsInjected);
|
CHECK(!s->mIsInjected);
|
||||||
|
|
||||||
sp<ABuffer> buffer = new ABuffer(65536);
|
sp<ABuffer> buffer = new ABuffer(65536);
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ ARTPSource::ARTPSource(
|
|||||||
} else if (!strncmp(desc.c_str(), "AMR-WB/", 7)) {
|
} else if (!strncmp(desc.c_str(), "AMR-WB/", 7)) {
|
||||||
mAssembler = new AAMRAssembler(notify, true /* isWide */, params);
|
mAssembler = new AAMRAssembler(notify, true /* isWide */, params);
|
||||||
} else if (!strncmp(desc.c_str(), "MP4V-ES/", 8)
|
} 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);
|
mAssembler = new AMPEG4ElementaryAssembler(notify, desc, params);
|
||||||
mIssueFIRRequests = true;
|
mIssueFIRRequests = true;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -71,6 +71,11 @@ bool ASessionDescription::parse(const void *data, size_t size) {
|
|||||||
line.setTo(desc, i, eolPos - i);
|
line.setTo(desc, i, eolPos - i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (line.empty()) {
|
||||||
|
i = eolPos + 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (line.size() < 2 || line.c_str()[1] != '=') {
|
if (line.size() < 2 || line.c_str()[1] != '=') {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -246,7 +246,7 @@ struct MyHandler : public AHandler {
|
|||||||
// In case we're behind NAT, fire off two UDP packets to the remote
|
// 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
|
// 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.
|
// 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 source;
|
||||||
AString server_port;
|
AString server_port;
|
||||||
if (!GetAttribute(transport.c_str(),
|
if (!GetAttribute(transport.c_str(),
|
||||||
@@ -255,16 +255,25 @@ struct MyHandler : public AHandler {
|
|||||||
|| !GetAttribute(transport.c_str(),
|
|| !GetAttribute(transport.c_str(),
|
||||||
"server_port",
|
"server_port",
|
||||||
&server_port)) {
|
&server_port)) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int rtpPort, rtcpPort;
|
int rtpPort, rtcpPort;
|
||||||
if (sscanf(server_port.c_str(), "%d-%d", &rtpPort, &rtcpPort) != 2
|
if (sscanf(server_port.c_str(), "%d-%d", &rtpPort, &rtcpPort) != 2
|
||||||
|| rtpPort <= 0 || rtpPort > 65535
|
|| rtpPort <= 0 || rtpPort > 65535
|
||||||
|| rtcpPort <=0 || rtcpPort > 65535
|
|| rtcpPort <=0 || rtcpPort > 65535
|
||||||
|| rtcpPort != rtpPort + 1
|
|| rtcpPort != rtpPort + 1) {
|
||||||
|| (rtpPort & 1) != 0) {
|
LOGE("Server picked invalid RTP/RTCP port pair %s,"
|
||||||
return;
|
" 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;
|
struct sockaddr_in addr;
|
||||||
@@ -273,7 +282,12 @@ struct MyHandler : public AHandler {
|
|||||||
addr.sin_addr.s_addr = inet_addr(source.c_str());
|
addr.sin_addr.s_addr = inet_addr(source.c_str());
|
||||||
|
|
||||||
if (addr.sin_addr.s_addr == INADDR_NONE) {
|
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.
|
// Make up an RR/SDES RTCP packet.
|
||||||
@@ -287,16 +301,26 @@ struct MyHandler : public AHandler {
|
|||||||
ssize_t n = sendto(
|
ssize_t n = sendto(
|
||||||
rtpSocket, buf->data(), buf->size(), 0,
|
rtpSocket, buf->data(), buf->size(), 0,
|
||||||
(const sockaddr *)&addr, sizeof(addr));
|
(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);
|
addr.sin_port = htons(rtcpPort);
|
||||||
|
|
||||||
n = sendto(
|
n = sendto(
|
||||||
rtcpSocket, buf->data(), buf->size(), 0,
|
rtcpSocket, buf->data(), buf->size(), 0,
|
||||||
(const sockaddr *)&addr, sizeof(addr));
|
(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.");
|
LOGV("successfully poked holes.");
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void onMessageReceived(const sp<AMessage> &msg) {
|
virtual void onMessageReceived(const sp<AMessage> &msg) {
|
||||||
@@ -379,6 +403,7 @@ struct MyHandler : public AHandler {
|
|||||||
response->mContent->size());
|
response->mContent->size());
|
||||||
|
|
||||||
if (!mSessionDesc->isValid()) {
|
if (!mSessionDesc->isValid()) {
|
||||||
|
LOGE("Failed to parse session description.");
|
||||||
result = ERROR_MALFORMED;
|
result = ERROR_MALFORMED;
|
||||||
} else {
|
} else {
|
||||||
ssize_t i = response->mHeaders.indexOfKey("content-base");
|
ssize_t i = response->mHeaders.indexOfKey("content-base");
|
||||||
@@ -393,6 +418,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);
|
CHECK_GT(mSessionDesc->countTracks(), 1u);
|
||||||
setupTrack(1);
|
setupTrack(1);
|
||||||
}
|
}
|
||||||
@@ -453,11 +497,15 @@ struct MyHandler : public AHandler {
|
|||||||
if (!track->mUsingInterleavedTCP) {
|
if (!track->mUsingInterleavedTCP) {
|
||||||
AString transport = response->mHeaders.valueAt(i);
|
AString transport = response->mHeaders.valueAt(i);
|
||||||
|
|
||||||
pokeAHole(track->mRTPSocket,
|
if (!pokeAHole(
|
||||||
|
track->mRTPSocket,
|
||||||
track->mRTCPSocket,
|
track->mRTCPSocket,
|
||||||
transport);
|
transport)) {
|
||||||
|
result = UNKNOWN_ERROR;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (result == OK) {
|
||||||
mRTPConn->addStream(
|
mRTPConn->addStream(
|
||||||
track->mRTPSocket, track->mRTCPSocket,
|
track->mRTPSocket, track->mRTCPSocket,
|
||||||
mSessionDesc, index,
|
mSessionDesc, index,
|
||||||
@@ -466,6 +514,7 @@ struct MyHandler : public AHandler {
|
|||||||
mSetupTracksSuccessful = true;
|
mSetupTracksSuccessful = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (result != OK) {
|
if (result != OK) {
|
||||||
if (track) {
|
if (track) {
|
||||||
@@ -865,10 +914,7 @@ struct MyHandler : public AHandler {
|
|||||||
case 'tiou':
|
case 'tiou':
|
||||||
{
|
{
|
||||||
if (!mReceivedFirstRTCPPacket) {
|
if (!mReceivedFirstRTCPPacket) {
|
||||||
if (mTryFakeRTCP) {
|
if (mReceivedFirstRTPPacket && !mTryFakeRTCP) {
|
||||||
LOGW("Never received any data, disconnecting.");
|
|
||||||
(new AMessage('abor', id()))->post();
|
|
||||||
} else if (mTryTCPInterleaving && mReceivedFirstRTPPacket) {
|
|
||||||
LOGW("We received RTP packets but no RTCP packets, "
|
LOGW("We received RTP packets but no RTCP packets, "
|
||||||
"using fake timestamps.");
|
"using fake timestamps.");
|
||||||
|
|
||||||
@@ -876,7 +922,7 @@ struct MyHandler : public AHandler {
|
|||||||
|
|
||||||
mReceivedFirstRTCPPacket = true;
|
mReceivedFirstRTCPPacket = true;
|
||||||
mRTPConn->fakeTimestamps();
|
mRTPConn->fakeTimestamps();
|
||||||
} else {
|
} else if (!mReceivedFirstRTPPacket && !mTryTCPInterleaving) {
|
||||||
LOGW("Never received any data, switching transports.");
|
LOGW("Never received any data, switching transports.");
|
||||||
|
|
||||||
mTryTCPInterleaving = true;
|
mTryTCPInterleaving = true;
|
||||||
@@ -884,6 +930,9 @@ struct MyHandler : public AHandler {
|
|||||||
sp<AMessage> msg = new AMessage('abor', id());
|
sp<AMessage> msg = new AMessage('abor', id());
|
||||||
msg->setInt32("reconnect", true);
|
msg->setInt32("reconnect", true);
|
||||||
msg->post();
|
msg->post();
|
||||||
|
} else {
|
||||||
|
LOGW("Never received any data, disconnecting.");
|
||||||
|
(new AMessage('abor', id()))->post();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|||||||
Reference in New Issue
Block a user