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 "ASessionDescription"
     19 #include <utils/Log.h>
     20 
     21 #include "ASessionDescription.h"
     22 
     23 #include <media/stagefright/foundation/ADebug.h>
     24 #include <media/stagefright/foundation/AString.h>
     25 
     26 #include <stdlib.h>
     27 
     28 namespace android {
     29 
     30 ASessionDescription::ASessionDescription()
     31     : mIsValid(false) {
     32 }
     33 
     34 ASessionDescription::~ASessionDescription() {
     35 }
     36 
     37 bool ASessionDescription::setTo(const void *data, size_t size) {
     38     mIsValid = parse(data, size);
     39 
     40     if (!mIsValid) {
     41         mTracks.clear();
     42         mFormats.clear();
     43     }
     44 
     45     return mIsValid;
     46 }
     47 
     48 bool ASessionDescription::parse(const void *data, size_t size) {
     49     mTracks.clear();
     50     mFormats.clear();
     51 
     52     mTracks.push(Attribs());
     53     mFormats.push(AString("[root]"));
     54 
     55     AString desc((const char *)data, size);
     56     LOGI("%s", desc.c_str());
     57 
     58     size_t i = 0;
     59     for (;;) {
     60         ssize_t eolPos = desc.find("\r\n", i);
     61         if (eolPos < 0) {
     62             break;
     63         }
     64 
     65         AString line(desc, i, eolPos - i);
     66 
     67         if (line.size() < 2 || line.c_str()[1] != '=') {
     68             return false;
     69         }
     70 
     71         switch (line.c_str()[0]) {
     72             case 'v':
     73             {
     74                 if (strcmp(line.c_str(), "v=0")) {
     75                     return false;
     76                 }
     77                 break;
     78             }
     79 
     80             case 'a':
     81             case 'b':
     82             {
     83                 AString key, value;
     84 
     85                 ssize_t colonPos = line.find(":", 2);
     86                 if (colonPos < 0) {
     87                     key = line;
     88                 } else {
     89                     key.setTo(line, 0, colonPos);
     90 
     91                     if (key == "a=fmtp" || key == "a=rtpmap"
     92                             || key == "a=framesize") {
     93                         ssize_t spacePos = line.find(" ", colonPos + 1);
     94                         if (spacePos < 0) {
     95                             return false;
     96                         }
     97 
     98                         key.setTo(line, 0, spacePos);
     99 
    100                         colonPos = spacePos;
    101                     }
    102 
    103                     value.setTo(line, colonPos + 1, line.size() - colonPos - 1);
    104                 }
    105 
    106                 key.trim();
    107                 value.trim();
    108 
    109                 LOGV("adding '%s' => '%s'", key.c_str(), value.c_str());
    110 
    111                 mTracks.editItemAt(mTracks.size() - 1).add(key, value);
    112                 break;
    113             }
    114 
    115             case 'm':
    116             {
    117                 LOGV("new section '%s'",
    118                      AString(line, 2, line.size() - 2).c_str());
    119 
    120                 mTracks.push(Attribs());
    121                 mFormats.push(AString(line, 2, line.size() - 2));
    122                 break;
    123             }
    124 
    125             default:
    126             {
    127                 AString key, value;
    128 
    129                 ssize_t equalPos = line.find("=");
    130 
    131                 key = AString(line, 0, equalPos + 1);
    132                 value = AString(line, equalPos + 1, line.size() - equalPos - 1);
    133 
    134                 key.trim();
    135                 value.trim();
    136 
    137                 LOGV("adding '%s' => '%s'", key.c_str(), value.c_str());
    138 
    139                 mTracks.editItemAt(mTracks.size() - 1).add(key, value);
    140                 break;
    141             }
    142         }
    143 
    144         i = eolPos + 2;
    145     }
    146 
    147     return true;
    148 }
    149 
    150 bool ASessionDescription::isValid() const {
    151     return mIsValid;
    152 }
    153 
    154 size_t ASessionDescription::countTracks() const {
    155     return mTracks.size();
    156 }
    157 
    158 void ASessionDescription::getFormat(size_t index, AString *value) const {
    159     CHECK_GE(index, 0u);
    160     CHECK_LT(index, mTracks.size());
    161 
    162     *value = mFormats.itemAt(index);
    163 }
    164 
    165 bool ASessionDescription::findAttribute(
    166         size_t index, const char *key, AString *value) const {
    167     CHECK_GE(index, 0u);
    168     CHECK_LT(index, mTracks.size());
    169 
    170     value->clear();
    171 
    172     const Attribs &track = mTracks.itemAt(index);
    173     ssize_t i = track.indexOfKey(AString(key));
    174 
    175     if (i < 0) {
    176         return false;
    177     }
    178 
    179     *value = track.valueAt(i);
    180 
    181     return true;
    182 }
    183 
    184 void ASessionDescription::getFormatType(
    185         size_t index, unsigned long *PT,
    186         AString *desc, AString *params) const {
    187     AString format;
    188     getFormat(index, &format);
    189 
    190     char *lastSpacePos = strrchr(format.c_str(), ' ');
    191     CHECK(lastSpacePos != NULL);
    192 
    193     char *end;
    194     unsigned long x = strtoul(lastSpacePos + 1, &end, 10);
    195     CHECK_GT(end, lastSpacePos + 1);
    196     CHECK_EQ(*end, '\0');
    197 
    198     *PT = x;
    199 
    200     char key[20];
    201     sprintf(key, "a=rtpmap:%lu", x);
    202 
    203     CHECK(findAttribute(index, key, desc));
    204 
    205     sprintf(key, "a=fmtp:%lu", x);
    206     if (!findAttribute(index, key, params)) {
    207         params->clear();
    208     }
    209 }
    210 
    211 bool ASessionDescription::getDimensions(
    212         size_t index, unsigned long PT,
    213         int32_t *width, int32_t *height) const {
    214     *width = 0;
    215     *height = 0;
    216 
    217     char key[20];
    218     sprintf(key, "a=framesize:%lu", PT);
    219     AString value;
    220     if (!findAttribute(index, key, &value)) {
    221         return false;
    222     }
    223 
    224     const char *s = value.c_str();
    225     char *end;
    226     *width = strtoul(s, &end, 10);
    227     CHECK_GT(end, s);
    228     CHECK_EQ(*end, '-');
    229 
    230     s = end + 1;
    231     *height = strtoul(s, &end, 10);
    232     CHECK_GT(end, s);
    233     CHECK_EQ(*end, '\0');
    234 
    235     return true;
    236 }
    237 
    238 bool ASessionDescription::getDurationUs(int64_t *durationUs) const {
    239     *durationUs = 0;
    240 
    241     CHECK(mIsValid);
    242 
    243     AString value;
    244     if (!findAttribute(0, "a=range", &value)) {
    245         return false;
    246     }
    247 
    248     if (value == "npt=now-") {
    249         return false;
    250     }
    251 
    252     if (strncmp(value.c_str(), "npt=", 4)) {
    253         return false;
    254     }
    255 
    256     const char *s = value.c_str() + 4;
    257     char *end;
    258     double from = strtod(s, &end);
    259     CHECK_GT(end, s);
    260     CHECK_EQ(*end, '-');
    261 
    262     s = end + 1;
    263     double to = strtod(s, &end);
    264     CHECK_GT(end, s);
    265     CHECK_EQ(*end, '\0');
    266 
    267     CHECK_GE(to, from);
    268 
    269     *durationUs = (int64_t)((to - from) * 1E6);
    270 
    271     return true;
    272 }
    273 
    274 // static
    275 void ASessionDescription::ParseFormatDesc(
    276         const char *desc, int32_t *timescale, int32_t *numChannels) {
    277     const char *slash1 = strchr(desc, '/');
    278     CHECK(slash1 != NULL);
    279 
    280     const char *s = slash1 + 1;
    281     char *end;
    282     unsigned long x = strtoul(s, &end, 10);
    283     CHECK_GT(end, s);
    284     CHECK(*end == '\0' || *end == '/');
    285 
    286     *timescale = x;
    287     *numChannels = 1;
    288 
    289     if (*end == '/') {
    290         s = end + 1;
    291         unsigned long x = strtoul(s, &end, 10);
    292         CHECK_GT(end, s);
    293         CHECK_EQ(*end, '\0');
    294 
    295         *numChannels = x;
    296     }
    297 }
    298 
    299 }  // namespace android
    300 
    301