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(ARTPConnection::kRegularlyRequestFIR);
     48 
     49     looper()->registerHandler(mRTPConn);
     50 
     51     for (size_t i = 1; i < mDesc->countTracks(); ++i) {
     52         AString connection;
     53         if (!mDesc->findAttribute(i, "c=", &connection)) {
     54             // No per-stream connection information, try global fallback.
     55             if (!mDesc->findAttribute(0, "c=", &connection)) {
     56                 ALOGE("Unable to find connection attribute.");
     57                 return mInitCheck;
     58             }
     59         }
     60         if (!(connection == "IN IP4 127.0.0.1")) {
     61             ALOGE("We only support localhost connections for now.");
     62             return mInitCheck;
     63         }
     64 
     65         unsigned port;
     66         if (!validateMediaFormat(i, &port) || (port & 1) != 0) {
     67             ALOGE("Invalid media format.");
     68             return mInitCheck;
     69         }
     70 
     71         sp<APacketSource> source = new APacketSource(mDesc, i);
     72         if (source->initCheck() != OK) {
     73             ALOGE("Unsupported format.");
     74             return mInitCheck;
     75         }
     76 
     77         int rtpSocket = MakeUDPSocket(port);
     78         int rtcpSocket = MakeUDPSocket(port + 1);
     79 
     80         mTracks.push(TrackInfo());
     81         TrackInfo *info = &mTracks.editItemAt(mTracks.size() - 1);
     82         info->mRTPSocket = rtpSocket;
     83         info->mRTCPSocket = rtcpSocket;
     84 
     85         sp<AMessage> notify = new AMessage(kWhatAccessUnitComplete, this);
     86         notify->setSize("track-index", mTracks.size() - 1);
     87 
     88         mRTPConn->addStream(
     89                 rtpSocket, rtcpSocket, mDesc, i, notify, false /* injected */);
     90 
     91         info->mPacketSource = source;
     92     }
     93 
     94     mInitCheck = OK;
     95 
     96     return OK;
     97 }
     98 
     99 // static
    100 int ARTPSession::MakeUDPSocket(unsigned port) {
    101     int s = socket(AF_INET, SOCK_DGRAM, 0);
    102     CHECK_GE(s, 0);
    103 
    104     struct sockaddr_in addr;
    105     memset(addr.sin_zero, 0, sizeof(addr.sin_zero));
    106     addr.sin_family = AF_INET;
    107     addr.sin_addr.s_addr = INADDR_ANY;
    108     addr.sin_port = htons(port);
    109 
    110     CHECK_EQ(0, bind(s, (const struct sockaddr *)&addr, sizeof(addr)));
    111 
    112     return s;
    113 }
    114 
    115 ARTPSession::~ARTPSession() {
    116     for (size_t i = 0; i < mTracks.size(); ++i) {
    117         TrackInfo *info = &mTracks.editItemAt(i);
    118 
    119         info->mPacketSource->signalEOS(UNKNOWN_ERROR);
    120 
    121         close(info->mRTPSocket);
    122         close(info->mRTCPSocket);
    123     }
    124 }
    125 
    126 void ARTPSession::onMessageReceived(const sp<AMessage> &msg) {
    127     switch (msg->what()) {
    128         case kWhatAccessUnitComplete:
    129         {
    130             int32_t firstRTCP;
    131             if (msg->findInt32("first-rtcp", &firstRTCP)) {
    132                 // There won't be an access unit here, it's just a notification
    133                 // that the data communication worked since we got the first
    134                 // rtcp packet.
    135                 break;
    136             }
    137 
    138             size_t trackIndex;
    139             CHECK(msg->findSize("track-index", &trackIndex));
    140 
    141             int32_t eos;
    142             if (msg->findInt32("eos", &eos) && eos) {
    143                 TrackInfo *info = &mTracks.editItemAt(trackIndex);
    144                 info->mPacketSource->signalEOS(ERROR_END_OF_STREAM);
    145                 break;
    146             }
    147 
    148             sp<ABuffer> accessUnit;
    149             CHECK(msg->findBuffer("access-unit", &accessUnit));
    150 
    151             uint64_t ntpTime;
    152             CHECK(accessUnit->meta()->findInt64(
    153                         "ntp-time", (int64_t *)&ntpTime));
    154 
    155 #if 0
    156 #if 0
    157             printf("access unit complete size=%d\tntp-time=0x%016llx\n",
    158                    accessUnit->size(), ntpTime);
    159 #else
    160             ALOGI("access unit complete, size=%d, ntp-time=%llu",
    161                  accessUnit->size(), ntpTime);
    162             hexdump(accessUnit->data(), accessUnit->size());
    163 #endif
    164 #endif
    165 
    166 #if 0
    167             CHECK_GE(accessUnit->size(), 5u);
    168             CHECK(!memcmp("\x00\x00\x00\x01", accessUnit->data(), 4));
    169             unsigned x = accessUnit->data()[4];
    170 
    171             ALOGI("access unit complete: nalType=0x%02x, nalRefIdc=0x%02x",
    172                  x & 0x1f, (x & 0x60) >> 5);
    173 #endif
    174 
    175             accessUnit->meta()->setInt64("ntp-time", ntpTime);
    176             accessUnit->meta()->setInt64("timeUs", 0);
    177 
    178 #if 0
    179             int32_t damaged;
    180             if (accessUnit->meta()->findInt32("damaged", &damaged)
    181                     && damaged != 0) {
    182                 ALOGI("ignoring damaged AU");
    183             } else
    184 #endif
    185             {
    186                 TrackInfo *info = &mTracks.editItemAt(trackIndex);
    187                 info->mPacketSource->queueAccessUnit(accessUnit);
    188             }
    189             break;
    190         }
    191 
    192         default:
    193             TRESPASS();
    194             break;
    195     }
    196 }
    197 
    198 bool ARTPSession::validateMediaFormat(size_t index, unsigned *port) const {
    199     AString format;
    200     mDesc->getFormat(index, &format);
    201 
    202     ssize_t i = format.find(" ");
    203     if (i < 0) {
    204         return false;
    205     }
    206 
    207     ++i;
    208     size_t j = i;
    209     while (isdigit(format.c_str()[j])) {
    210         ++j;
    211     }
    212     if (format.c_str()[j] != ' ') {
    213         return false;
    214     }
    215 
    216     AString portString(format, i, j - i);
    217 
    218     char *end;
    219     unsigned long x = strtoul(portString.c_str(), &end, 10);
    220     if (end == portString.c_str() || *end != '\0') {
    221         return false;
    222     }
    223 
    224     if (x == 0 || x > 65535) {
    225         return false;
    226     }
    227 
    228     *port = x;
    229 
    230     return true;
    231 }
    232 
    233 size_t ARTPSession::countTracks() {
    234     return mTracks.size();
    235 }
    236 
    237 sp<MediaSource> ARTPSession::trackAt(size_t index) {
    238     CHECK_LT(index, mTracks.size());
    239     return mTracks.editItemAt(index).mPacketSource;
    240 }
    241 
    242 }  // namespace android
    243