Home | History | Annotate | Download | only in rtsp
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 //#define LOG_NDEBUG 0
     18 #define LOG_TAG "ARTPSession"
     19 #include <utils/Log.h>
     20 
     21 #include "ARTPSession.h"
     22 
     23 #include <media/stagefright/foundation/ABuffer.h>
     24 #include <media/stagefright/foundation/ADebug.h>
     25 #include <media/stagefright/foundation/AMessage.h>
     26 #include <media/stagefright/foundation/hexdump.h>
     27 
     28 #include <ctype.h>
     29 #include <arpa/inet.h>
     30 #include <sys/socket.h>
     31 
     32 #include "APacketSource.h"
     33 #include "ARTPConnection.h"
     34 #include "ASessionDescription.h"
     35 
     36 namespace android {
     37 
     38 ARTPSession::ARTPSession()
     39     : mInitCheck(NO_INIT) {
     40 }
     41 
     42 status_t ARTPSession::setup(const sp<ASessionDescription> &desc) {
     43     CHECK_EQ(mInitCheck, (status_t)NO_INIT);
     44 
     45     mDesc = desc;
     46 
     47     mRTPConn = new ARTPConnection(
     48             ARTPConnection::kFakeTimestamps
     49                 | ARTPConnection::kRegularlyRequestFIR);
     50 
     51     looper()->registerHandler(mRTPConn);
     52 
     53     for (size_t i = 1; i < mDesc->countTracks(); ++i) {
     54         AString connection;
     55         if (!mDesc->findAttribute(i, "c=", &connection)) {
     56             // No per-stream connection information, try global fallback.
     57             if (!mDesc->findAttribute(0, "c=", &connection)) {
     58                 LOGE("Unable to find connection attribute.");
     59                 return mInitCheck;
     60             }
     61         }
     62         if (!(connection == "IN IP4 127.0.0.1")) {
     63             LOGE("We only support localhost connections for now.");
     64             return mInitCheck;
     65         }
     66 
     67         unsigned port;
     68         if (!validateMediaFormat(i, &port) || (port & 1) != 0) {
     69             LOGE("Invalid media format.");
     70             return mInitCheck;
     71         }
     72 
     73         sp<APacketSource> source = new APacketSource(mDesc, i);
     74         if (source->initCheck() != OK) {
     75             LOGE("Unsupported format.");
     76             return mInitCheck;
     77         }
     78 
     79         int rtpSocket = MakeUDPSocket(port);
     80         int rtcpSocket = MakeUDPSocket(port + 1);
     81 
     82         mTracks.push(TrackInfo());
     83         TrackInfo *info = &mTracks.editItemAt(mTracks.size() - 1);
     84         info->mRTPSocket = rtpSocket;
     85         info->mRTCPSocket = rtcpSocket;
     86 
     87         sp<AMessage> notify = new AMessage(kWhatAccessUnitComplete, id());
     88         notify->setSize("track-index", mTracks.size() - 1);
     89 
     90         mRTPConn->addStream(
     91                 rtpSocket, rtcpSocket, mDesc, i, notify, false /* injected */);
     92 
     93         info->mPacketSource = source;
     94     }
     95 
     96     mInitCheck = OK;
     97 
     98     return OK;
     99 }
    100 
    101 // static
    102 int ARTPSession::MakeUDPSocket(unsigned port) {
    103     int s = socket(AF_INET, SOCK_DGRAM, 0);
    104     CHECK_GE(s, 0);
    105 
    106     struct sockaddr_in addr;
    107     memset(addr.sin_zero, 0, sizeof(addr.sin_zero));
    108     addr.sin_family = AF_INET;
    109     addr.sin_addr.s_addr = INADDR_ANY;
    110     addr.sin_port = htons(port);
    111 
    112     CHECK_EQ(0, bind(s, (const struct sockaddr *)&addr, sizeof(addr)));
    113 
    114     return s;
    115 }
    116 
    117 ARTPSession::~ARTPSession() {
    118     for (size_t i = 0; i < mTracks.size(); ++i) {
    119         TrackInfo *info = &mTracks.editItemAt(i);
    120 
    121         info->mPacketSource->signalEOS(UNKNOWN_ERROR);
    122 
    123         close(info->mRTPSocket);
    124         close(info->mRTCPSocket);
    125     }
    126 }
    127 
    128 void ARTPSession::onMessageReceived(const sp<AMessage> &msg) {
    129     switch (msg->what()) {
    130         case kWhatAccessUnitComplete:
    131         {
    132             int32_t firstRTCP;
    133             if (msg->findInt32("first-rtcp", &firstRTCP)) {
    134                 // There won't be an access unit here, it's just a notification
    135                 // that the data communication worked since we got the first
    136                 // rtcp packet.
    137                 break;
    138             }
    139 
    140             size_t trackIndex;
    141             CHECK(msg->findSize("track-index", &trackIndex));
    142 
    143             int32_t eos;
    144             if (msg->findInt32("eos", &eos) && eos) {
    145                 TrackInfo *info = &mTracks.editItemAt(trackIndex);
    146                 info->mPacketSource->signalEOS(ERROR_END_OF_STREAM);
    147                 break;
    148             }
    149 
    150             sp<RefBase> obj;
    151             CHECK(msg->findObject("access-unit", &obj));
    152 
    153             sp<ABuffer> accessUnit = static_cast<ABuffer *>(obj.get());
    154 
    155             uint64_t ntpTime;
    156             CHECK(accessUnit->meta()->findInt64(
    157                         "ntp-time", (int64_t *)&ntpTime));
    158 
    159 #if 0
    160 #if 0
    161             printf("access unit complete size=%d\tntp-time=0x%016llx\n",
    162                    accessUnit->size(), ntpTime);
    163 #else
    164             LOGI("access unit complete, size=%d, ntp-time=%llu",
    165                  accessUnit->size(), ntpTime);
    166             hexdump(accessUnit->data(), accessUnit->size());
    167 #endif
    168 #endif
    169 
    170 #if 0
    171             CHECK_GE(accessUnit->size(), 5u);
    172             CHECK(!memcmp("\x00\x00\x00\x01", accessUnit->data(), 4));
    173             unsigned x = accessUnit->data()[4];
    174 
    175             LOGI("access unit complete: nalType=0x%02x, nalRefIdc=0x%02x",
    176                  x & 0x1f, (x & 0x60) >> 5);
    177 #endif
    178 
    179             accessUnit->meta()->setInt64("ntp-time", ntpTime);
    180             accessUnit->meta()->setInt64("timeUs", 0);
    181 
    182 #if 0
    183             int32_t damaged;
    184             if (accessUnit->meta()->findInt32("damaged", &damaged)
    185                     && damaged != 0) {
    186                 LOGI("ignoring damaged AU");
    187             } else
    188 #endif
    189             {
    190                 TrackInfo *info = &mTracks.editItemAt(trackIndex);
    191                 info->mPacketSource->queueAccessUnit(accessUnit);
    192             }
    193             break;
    194         }
    195 
    196         default:
    197             TRESPASS();
    198             break;
    199     }
    200 }
    201 
    202 bool ARTPSession::validateMediaFormat(size_t index, unsigned *port) const {
    203     AString format;
    204     mDesc->getFormat(index, &format);
    205 
    206     ssize_t i = format.find(" ");
    207     if (i < 0) {
    208         return false;
    209     }
    210 
    211     ++i;
    212     size_t j = i;
    213     while (isdigit(format.c_str()[j])) {
    214         ++j;
    215     }
    216     if (format.c_str()[j] != ' ') {
    217         return false;
    218     }
    219 
    220     AString portString(format, i, j - i);
    221 
    222     char *end;
    223     unsigned long x = strtoul(portString.c_str(), &end, 10);
    224     if (end == portString.c_str() || *end != '\0') {
    225         return false;
    226     }
    227 
    228     if (x == 0 || x > 65535) {
    229         return false;
    230     }
    231 
    232     *port = x;
    233 
    234     return true;
    235 }
    236 
    237 size_t ARTPSession::countTracks() {
    238     return mTracks.size();
    239 }
    240 
    241 sp<MediaSource> ARTPSession::trackAt(size_t index) {
    242     CHECK_LT(index, mTracks.size());
    243     return mTracks.editItemAt(index).mPacketSource;
    244 }
    245 
    246 }  // namespace android
    247