| /* |
| * Copyright (C) 2010 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "ARTPConnection.h" |
| |
| #include "ARTPSource.h" |
| #include "ASessionDescription.h" |
| |
| #include <media/stagefright/foundation/ABuffer.h> |
| #include <media/stagefright/foundation/ADebug.h> |
| #include <media/stagefright/foundation/AMessage.h> |
| #include <media/stagefright/foundation/AString.h> |
| |
| #include <arpa/inet.h> |
| #include <sys/socket.h> |
| |
| #define VERBOSE 0 |
| |
| #if VERBOSE |
| #include "hexdump.h" |
| #endif |
| |
| namespace android { |
| |
| static uint16_t u16at(const uint8_t *data) { |
| return data[0] << 8 | data[1]; |
| } |
| |
| static uint32_t u32at(const uint8_t *data) { |
| return u16at(data) << 16 | u16at(&data[2]); |
| } |
| |
| static uint64_t u64at(const uint8_t *data) { |
| return (uint64_t)(u32at(data)) << 32 | u32at(&data[4]); |
| } |
| |
| // static |
| const int64_t ARTPConnection::kSelectTimeoutUs = 1000ll; |
| |
| struct ARTPConnection::StreamInfo { |
| int mRTPSocket; |
| int mRTCPSocket; |
| sp<ASessionDescription> mSessionDesc; |
| size_t mIndex; |
| sp<AMessage> mNotifyMsg; |
| }; |
| |
| ARTPConnection::ARTPConnection() |
| : mPollEventPending(false) { |
| } |
| |
| ARTPConnection::~ARTPConnection() { |
| } |
| |
| void ARTPConnection::addStream( |
| int rtpSocket, int rtcpSocket, |
| const sp<ASessionDescription> &sessionDesc, |
| size_t index, |
| const sp<AMessage> ¬ify) { |
| sp<AMessage> msg = new AMessage(kWhatAddStream, id()); |
| msg->setInt32("rtp-socket", rtpSocket); |
| msg->setInt32("rtcp-socket", rtcpSocket); |
| msg->setObject("session-desc", sessionDesc); |
| msg->setSize("index", index); |
| msg->setMessage("notify", notify); |
| msg->post(); |
| } |
| |
| void ARTPConnection::removeStream(int rtpSocket, int rtcpSocket) { |
| sp<AMessage> msg = new AMessage(kWhatRemoveStream, id()); |
| msg->setInt32("rtp-socket", rtpSocket); |
| msg->setInt32("rtcp-socket", rtcpSocket); |
| msg->post(); |
| } |
| |
| static void bumpSocketBufferSize(int s) { |
| int size = 256 * 1024; |
| CHECK_EQ(setsockopt(s, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)), 0); |
| } |
| |
| // static |
| void ARTPConnection::MakePortPair( |
| int *rtpSocket, int *rtcpSocket, unsigned *rtpPort) { |
| *rtpSocket = socket(AF_INET, SOCK_DGRAM, 0); |
| CHECK_GE(*rtpSocket, 0); |
| |
| bumpSocketBufferSize(*rtpSocket); |
| |
| *rtcpSocket = socket(AF_INET, SOCK_DGRAM, 0); |
| CHECK_GE(*rtcpSocket, 0); |
| |
| bumpSocketBufferSize(*rtcpSocket); |
| |
| unsigned start = (rand() * 1000)/ RAND_MAX + 15550; |
| start &= ~1; |
| |
| for (unsigned port = start; port < 65536; port += 2) { |
| 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_port = htons(port); |
| |
| if (bind(*rtpSocket, |
| (const struct sockaddr *)&addr, sizeof(addr)) < 0) { |
| continue; |
| } |
| |
| addr.sin_port = htons(port + 1); |
| |
| if (bind(*rtcpSocket, |
| (const struct sockaddr *)&addr, sizeof(addr)) == 0) { |
| *rtpPort = port; |
| return; |
| } |
| } |
| |
| TRESPASS(); |
| } |
| |
| void ARTPConnection::onMessageReceived(const sp<AMessage> &msg) { |
| switch (msg->what()) { |
| case kWhatAddStream: |
| { |
| onAddStream(msg); |
| break; |
| } |
| |
| case kWhatRemoveStream: |
| { |
| onRemoveStream(msg); |
| break; |
| } |
| |
| case kWhatPollStreams: |
| { |
| onPollStreams(); |
| break; |
| } |
| |
| default: |
| { |
| TRESPASS(); |
| break; |
| } |
| } |
| } |
| |
| void ARTPConnection::onAddStream(const sp<AMessage> &msg) { |
| mStreams.push_back(StreamInfo()); |
| StreamInfo *info = &*--mStreams.end(); |
| |
| int32_t s; |
| CHECK(msg->findInt32("rtp-socket", &s)); |
| info->mRTPSocket = s; |
| CHECK(msg->findInt32("rtcp-socket", &s)); |
| info->mRTCPSocket = s; |
| |
| sp<RefBase> obj; |
| CHECK(msg->findObject("session-desc", &obj)); |
| info->mSessionDesc = static_cast<ASessionDescription *>(obj.get()); |
| |
| CHECK(msg->findSize("index", &info->mIndex)); |
| CHECK(msg->findMessage("notify", &info->mNotifyMsg)); |
| |
| postPollEvent(); |
| } |
| |
| void ARTPConnection::onRemoveStream(const sp<AMessage> &msg) { |
| int32_t rtpSocket, rtcpSocket; |
| CHECK(msg->findInt32("rtp-socket", &rtpSocket)); |
| CHECK(msg->findInt32("rtcp-socket", &rtcpSocket)); |
| |
| List<StreamInfo>::iterator it = mStreams.begin(); |
| while (it != mStreams.end() |
| && (it->mRTPSocket != rtpSocket || it->mRTCPSocket != rtcpSocket)) { |
| ++it; |
| } |
| |
| if (it == mStreams.end()) { |
| TRESPASS(); |
| } |
| |
| mStreams.erase(it); |
| } |
| |
| void ARTPConnection::postPollEvent() { |
| if (mPollEventPending) { |
| return; |
| } |
| |
| sp<AMessage> msg = new AMessage(kWhatPollStreams, id()); |
| msg->post(); |
| |
| mPollEventPending = true; |
| } |
| |
| void ARTPConnection::onPollStreams() { |
| mPollEventPending = false; |
| |
| if (mStreams.empty()) { |
| return; |
| } |
| |
| struct timeval tv; |
| tv.tv_sec = 0; |
| tv.tv_usec = kSelectTimeoutUs; |
| |
| fd_set rs; |
| FD_ZERO(&rs); |
| |
| int maxSocket = -1; |
| for (List<StreamInfo>::iterator it = mStreams.begin(); |
| it != mStreams.end(); ++it) { |
| FD_SET(it->mRTPSocket, &rs); |
| FD_SET(it->mRTCPSocket, &rs); |
| |
| if (it->mRTPSocket > maxSocket) { |
| maxSocket = it->mRTPSocket; |
| } |
| if (it->mRTCPSocket > maxSocket) { |
| maxSocket = it->mRTCPSocket; |
| } |
| } |
| |
| int res = select(maxSocket + 1, &rs, NULL, NULL, &tv); |
| CHECK_GE(res, 0); |
| |
| if (res > 0) { |
| for (List<StreamInfo>::iterator it = mStreams.begin(); |
| it != mStreams.end(); ++it) { |
| if (FD_ISSET(it->mRTPSocket, &rs)) { |
| receive(&*it, true); |
| } |
| if (FD_ISSET(it->mRTCPSocket, &rs)) { |
| receive(&*it, false); |
| } |
| } |
| } |
| |
| postPollEvent(); |
| } |
| |
| status_t ARTPConnection::receive(StreamInfo *s, bool receiveRTP) { |
| sp<ABuffer> buffer = new ABuffer(65536); |
| |
| struct sockaddr_in from; |
| socklen_t fromSize = sizeof(from); |
| |
| ssize_t nbytes = recvfrom( |
| receiveRTP ? s->mRTPSocket : s->mRTCPSocket, |
| buffer->data(), |
| buffer->capacity(), |
| 0, |
| (struct sockaddr *)&from, |
| &fromSize); |
| |
| if (nbytes < 0) { |
| return -1; |
| } |
| |
| buffer->setRange(0, nbytes); |
| |
| status_t err; |
| if (receiveRTP) { |
| err = parseRTP(s, buffer); |
| } else { |
| err = parseRTCP(s, buffer); |
| } |
| |
| return err; |
| } |
| |
| status_t ARTPConnection::parseRTP(StreamInfo *s, const sp<ABuffer> &buffer) { |
| size_t size = buffer->size(); |
| |
| if (size < 12) { |
| // Too short to be a valid RTP header. |
| return -1; |
| } |
| |
| const uint8_t *data = buffer->data(); |
| |
| if ((data[0] >> 6) != 2) { |
| // Unsupported version. |
| return -1; |
| } |
| |
| if (data[0] & 0x20) { |
| // Padding present. |
| |
| size_t paddingLength = data[size - 1]; |
| |
| if (paddingLength + 12 > size) { |
| // If we removed this much padding we'd end up with something |
| // that's too short to be a valid RTP header. |
| return -1; |
| } |
| |
| size -= paddingLength; |
| } |
| |
| int numCSRCs = data[0] & 0x0f; |
| |
| size_t payloadOffset = 12 + 4 * numCSRCs; |
| |
| if (size < payloadOffset) { |
| // Not enough data to fit the basic header and all the CSRC entries. |
| return -1; |
| } |
| |
| if (data[0] & 0x10) { |
| // Header eXtension present. |
| |
| if (size < payloadOffset + 4) { |
| // Not enough data to fit the basic header, all CSRC entries |
| // and the first 4 bytes of the extension header. |
| |
| return -1; |
| } |
| |
| const uint8_t *extensionData = &data[payloadOffset]; |
| |
| size_t extensionLength = |
| 4 * (extensionData[2] << 8 | extensionData[3]); |
| |
| if (size < payloadOffset + 4 + extensionLength) { |
| return -1; |
| } |
| |
| payloadOffset += 4 + extensionLength; |
| } |
| |
| uint32_t srcId = u32at(&data[8]); |
| |
| sp<ARTPSource> source; |
| ssize_t index = mSources.indexOfKey(srcId); |
| if (index < 0) { |
| index = mSources.size(); |
| |
| source = new ARTPSource( |
| srcId, s->mSessionDesc, s->mIndex, s->mNotifyMsg); |
| |
| mSources.add(srcId, source); |
| } else { |
| source = mSources.valueAt(index); |
| } |
| |
| uint32_t rtpTime = u32at(&data[4]); |
| |
| sp<AMessage> meta = buffer->meta(); |
| meta->setInt32("ssrc", srcId); |
| meta->setInt32("rtp-time", rtpTime); |
| meta->setInt32("PT", data[1] & 0x7f); |
| meta->setInt32("M", data[1] >> 7); |
| |
| buffer->setInt32Data(u16at(&data[2])); |
| |
| #if VERBOSE |
| printf("RTP = {\n" |
| " PT: %d\n" |
| " sequence number: %d\n" |
| " RTP-time: 0x%08x\n" |
| " M: %d\n" |
| " SSRC: 0x%08x\n" |
| "}\n", |
| data[1] & 0x7f, |
| u16at(&data[2]), |
| rtpTime, |
| data[1] >> 7, |
| srcId); |
| |
| // hexdump(&data[payloadOffset], size - payloadOffset); |
| #endif |
| |
| buffer->setRange(payloadOffset, size - payloadOffset); |
| |
| source->processRTPPacket(buffer); |
| |
| return OK; |
| } |
| |
| status_t ARTPConnection::parseRTCP(StreamInfo *s, const sp<ABuffer> &buffer) { |
| const uint8_t *data = buffer->data(); |
| size_t size = buffer->size(); |
| |
| while (size > 0) { |
| if (size < 8) { |
| // Too short to be a valid RTCP header |
| return -1; |
| } |
| |
| if ((data[0] >> 6) != 2) { |
| // Unsupported version. |
| return -1; |
| } |
| |
| if (data[0] & 0x20) { |
| // Padding present. |
| |
| size_t paddingLength = data[size - 1]; |
| |
| if (paddingLength + 12 > size) { |
| // If we removed this much padding we'd end up with something |
| // that's too short to be a valid RTP header. |
| return -1; |
| } |
| |
| size -= paddingLength; |
| } |
| |
| size_t headerLength = 4 * (data[2] << 8 | data[3]) + 4; |
| |
| if (size < headerLength) { |
| // Only received a partial packet? |
| return -1; |
| } |
| |
| switch (data[1]) { |
| case 200: |
| { |
| parseSR(s, data, headerLength); |
| break; |
| } |
| |
| default: |
| { |
| #if VERBOSE |
| printf("Unknown RTCP packet type %d of size %ld\n", |
| data[1], headerLength); |
| |
| hexdump(data, headerLength); |
| #endif |
| break; |
| } |
| } |
| |
| data += headerLength; |
| size -= headerLength; |
| } |
| |
| return OK; |
| } |
| |
| status_t ARTPConnection::parseSR( |
| StreamInfo *s, const uint8_t *data, size_t size) { |
| size_t RC = data[0] & 0x1f; |
| |
| if (size < (7 + RC * 6) * 4) { |
| // Packet too short for the minimal SR header. |
| return -1; |
| } |
| |
| uint32_t id = u32at(&data[4]); |
| uint64_t ntpTime = u64at(&data[8]); |
| uint32_t rtpTime = u32at(&data[16]); |
| |
| #if VERBOSE |
| printf("SR = {\n" |
| " SSRC: 0x%08x\n" |
| " NTP-time: 0x%016llx\n" |
| " RTP-time: 0x%08x\n" |
| "}\n", |
| id, ntpTime, rtpTime); |
| #endif |
| |
| sp<ARTPSource> source; |
| ssize_t index = mSources.indexOfKey(id); |
| if (index < 0) { |
| index = mSources.size(); |
| |
| source = new ARTPSource( |
| id, s->mSessionDesc, s->mIndex, s->mNotifyMsg); |
| |
| mSources.add(id, source); |
| } else { |
| source = mSources.valueAt(index); |
| } |
| |
| source->timeUpdate(rtpTime, ntpTime); |
| |
| return 0; |
| } |
| |
| } // namespace android |
| |