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 
     57     size_t i = 0;
     58     for (;;) {
     59         ssize_t eolPos = desc.find("\n", i);
     60 
     61         if (eolPos < 0) {
     62             break;
     63         }
     64 
     65         AString line;
     66         if ((size_t)eolPos > i && desc.c_str()[eolPos - 1] == '\r') {
     67             // We accept both '\n' and '\r\n' line endings, if it's
     68             // the latter, strip the '\r' as well.
     69             line.setTo(desc, i, eolPos - i - 1);
     70         } else {
     71             line.setTo(desc, i, eolPos - i);
     72         }
     73 
     74         if (line.empty()) {
     75             i = eolPos + 1;
     76             continue;
     77         }
     78 
     79         if (line.size() < 2 || line.c_str()[1] != '=') {
     80             return false;
     81         }
     82 
     83         ALOGI("%s", line.c_str());
     84 
     85         switch (line.c_str()[0]) {
     86             case 'v':
     87             {
     88                 if (strcmp(line.c_str(), "v=0")) {
     89                     return false;
     90                 }
     91                 break;
     92             }
     93 
     94             case 'a':
     95             case 'b':
     96             {
     97                 AString key, value;
     98 
     99                 ssize_t colonPos = line.find(":", 2);
    100                 if (colonPos < 0) {
    101                     key = line;
    102                 } else {
    103                     key.setTo(line, 0, colonPos);
    104 
    105                     if (key == "a=fmtp" || key == "a=rtpmap"
    106                             || key == "a=framesize") {
    107                         ssize_t spacePos = line.find(" ", colonPos + 1);
    108                         if (spacePos < 0) {
    109                             return false;
    110                         }
    111 
    112                         key.setTo(line, 0, spacePos);
    113 
    114                         colonPos = spacePos;
    115                     }
    116 
    117                     value.setTo(line, colonPos + 1, line.size() - colonPos - 1);
    118                 }
    119 
    120                 key.trim();
    121                 value.trim();
    122 
    123                 ALOGV("adding '%s' => '%s'", key.c_str(), value.c_str());
    124 
    125                 mTracks.editItemAt(mTracks.size() - 1).add(key, value);
    126                 break;
    127             }
    128 
    129             case 'm':
    130             {
    131                 ALOGV("new section '%s'",
    132                      AString(line, 2, line.size() - 2).c_str());
    133 
    134                 mTracks.push(Attribs());
    135                 mFormats.push(AString(line, 2, line.size() - 2));
    136                 break;
    137             }
    138 
    139             default:
    140             {
    141                 AString key, value;
    142 
    143                 ssize_t equalPos = line.find("=");
    144 
    145                 key = AString(line, 0, equalPos + 1);
    146                 value = AString(line, equalPos + 1, line.size() - equalPos - 1);
    147 
    148                 key.trim();
    149                 value.trim();
    150 
    151                 ALOGV("adding '%s' => '%s'", key.c_str(), value.c_str());
    152 
    153                 mTracks.editItemAt(mTracks.size() - 1).add(key, value);
    154                 break;
    155             }
    156         }
    157 
    158         i = eolPos + 1;
    159     }
    160 
    161     return true;
    162 }
    163 
    164 bool ASessionDescription::isValid() const {
    165     return mIsValid;
    166 }
    167 
    168 size_t ASessionDescription::countTracks() const {
    169     return mTracks.size();
    170 }
    171 
    172 void ASessionDescription::getFormat(size_t index, AString *value) const {
    173     CHECK_GE(index, 0u);
    174     CHECK_LT(index, mTracks.size());
    175 
    176     *value = mFormats.itemAt(index);
    177 }
    178 
    179 bool ASessionDescription::findAttribute(
    180         size_t index, const char *key, AString *value) const {
    181     CHECK_GE(index, 0u);
    182     CHECK_LT(index, mTracks.size());
    183 
    184     value->clear();
    185 
    186     const Attribs &track = mTracks.itemAt(index);
    187     ssize_t i = track.indexOfKey(AString(key));
    188 
    189     if (i < 0) {
    190         return false;
    191     }
    192 
    193     *value = track.valueAt(i);
    194 
    195     return true;
    196 }
    197 
    198 void ASessionDescription::getFormatType(
    199         size_t index, unsigned long *PT,
    200         AString *desc, AString *params) const {
    201     AString format;
    202     getFormat(index, &format);
    203 
    204     const char *lastSpacePos = strrchr(format.c_str(), ' ');
    205     CHECK(lastSpacePos != NULL);
    206 
    207     char *end;
    208     unsigned long x = strtoul(lastSpacePos + 1, &end, 10);
    209     CHECK_GT(end, lastSpacePos + 1);
    210     CHECK_EQ(*end, '\0');
    211 
    212     *PT = x;
    213 
    214     char key[20];
    215     snprintf(key, sizeof(key), "a=rtpmap:%lu", x);
    216     if (findAttribute(index, key, desc)) {
    217         snprintf(key, sizeof(key), "a=fmtp:%lu", x);
    218         if (!findAttribute(index, key, params)) {
    219             params->clear();
    220         }
    221     } else {
    222         desc->clear();
    223         params->clear();
    224     }
    225 }
    226 
    227 bool ASessionDescription::getDimensions(
    228         size_t index, unsigned long PT,
    229         int32_t *width, int32_t *height) const {
    230     *width = 0;
    231     *height = 0;
    232 
    233     char key[20];
    234     snprintf(key, sizeof(key), "a=framesize:%lu", PT);
    235     AString value;
    236     if (!findAttribute(index, key, &value)) {
    237         return false;
    238     }
    239 
    240     const char *s = value.c_str();
    241     char *end;
    242     *width = strtoul(s, &end, 10);
    243     CHECK_GT(end, s);
    244     CHECK_EQ(*end, '-');
    245 
    246     s = end + 1;
    247     *height = strtoul(s, &end, 10);
    248     CHECK_GT(end, s);
    249     CHECK_EQ(*end, '\0');
    250 
    251     return true;
    252 }
    253 
    254 bool ASessionDescription::getDurationUs(int64_t *durationUs) const {
    255     *durationUs = 0;
    256 
    257     CHECK(mIsValid);
    258 
    259     AString value;
    260     if (!findAttribute(0, "a=range", &value)) {
    261         return false;
    262     }
    263 
    264     if (strncmp(value.c_str(), "npt=", 4)) {
    265         return false;
    266     }
    267 
    268     float from, to;
    269     if (!parseNTPRange(value.c_str() + 4, &from, &to)) {
    270         return false;
    271     }
    272 
    273     *durationUs = (int64_t)((to - from) * 1E6);
    274 
    275     return true;
    276 }
    277 
    278 // static
    279 void ASessionDescription::ParseFormatDesc(
    280         const char *desc, int32_t *timescale, int32_t *numChannels) {
    281     const char *slash1 = strchr(desc, '/');
    282     CHECK(slash1 != NULL);
    283 
    284     const char *s = slash1 + 1;
    285     char *end;
    286     unsigned long x = strtoul(s, &end, 10);
    287     CHECK_GT(end, s);
    288     CHECK(*end == '\0' || *end == '/');
    289 
    290     *timescale = x;
    291     *numChannels = 1;
    292 
    293     if (*end == '/') {
    294         s = end + 1;
    295         unsigned long x = strtoul(s, &end, 10);
    296         CHECK_GT(end, s);
    297         CHECK_EQ(*end, '\0');
    298 
    299         *numChannels = x;
    300     }
    301 }
    302 
    303 // static
    304 bool ASessionDescription::parseNTPRange(
    305         const char *s, float *npt1, float *npt2) {
    306     if (s[0] == '-') {
    307         return false;  // no start time available.
    308     }
    309 
    310     if (!strncmp("now", s, 3)) {
    311         return false;  // no absolute start time available
    312     }
    313 
    314     char *end;
    315     *npt1 = strtof(s, &end);
    316 
    317     if (end == s || *end != '-') {
    318         // Failed to parse float or trailing "dash".
    319         return false;
    320     }
    321 
    322     s = end + 1;  // skip the dash.
    323 
    324     if (*s == '\0') {
    325         *npt2 = FLT_MAX;  // open ended.
    326         return true;
    327     }
    328 
    329     if (!strncmp("now", s, 3)) {
    330         return false;  // no absolute end time available
    331     }
    332 
    333     *npt2 = strtof(s, &end);
    334 
    335     if (end == s || *end != '\0') {
    336         return false;
    337     }
    338 
    339     return *npt2 > *npt1;
    340 }
    341 
    342 }  // namespace android
    343 
    344