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 LOGI("%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 LOGV("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 LOGV("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 LOGV("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 sprintf(key, "a=rtpmap:%lu", x); 216 217 CHECK(findAttribute(index, key, desc)); 218 219 sprintf(key, "a=fmtp:%lu", x); 220 if (!findAttribute(index, key, params)) { 221 params->clear(); 222 } 223 } 224 225 bool ASessionDescription::getDimensions( 226 size_t index, unsigned long PT, 227 int32_t *width, int32_t *height) const { 228 *width = 0; 229 *height = 0; 230 231 char key[20]; 232 sprintf(key, "a=framesize:%lu", PT); 233 AString value; 234 if (!findAttribute(index, key, &value)) { 235 return false; 236 } 237 238 const char *s = value.c_str(); 239 char *end; 240 *width = strtoul(s, &end, 10); 241 CHECK_GT(end, s); 242 CHECK_EQ(*end, '-'); 243 244 s = end + 1; 245 *height = strtoul(s, &end, 10); 246 CHECK_GT(end, s); 247 CHECK_EQ(*end, '\0'); 248 249 return true; 250 } 251 252 bool ASessionDescription::getDurationUs(int64_t *durationUs) const { 253 *durationUs = 0; 254 255 CHECK(mIsValid); 256 257 AString value; 258 if (!findAttribute(0, "a=range", &value)) { 259 return false; 260 } 261 262 if (strncmp(value.c_str(), "npt=", 4)) { 263 return false; 264 } 265 266 float from, to; 267 if (!parseNTPRange(value.c_str() + 4, &from, &to)) { 268 return false; 269 } 270 271 *durationUs = (int64_t)((to - from) * 1E6); 272 273 return true; 274 } 275 276 // static 277 void ASessionDescription::ParseFormatDesc( 278 const char *desc, int32_t *timescale, int32_t *numChannels) { 279 const char *slash1 = strchr(desc, '/'); 280 CHECK(slash1 != NULL); 281 282 const char *s = slash1 + 1; 283 char *end; 284 unsigned long x = strtoul(s, &end, 10); 285 CHECK_GT(end, s); 286 CHECK(*end == '\0' || *end == '/'); 287 288 *timescale = x; 289 *numChannels = 1; 290 291 if (*end == '/') { 292 s = end + 1; 293 unsigned long x = strtoul(s, &end, 10); 294 CHECK_GT(end, s); 295 CHECK_EQ(*end, '\0'); 296 297 *numChannels = x; 298 } 299 } 300 301 // static 302 bool ASessionDescription::parseNTPRange( 303 const char *s, float *npt1, float *npt2) { 304 if (s[0] == '-') { 305 return false; // no start time available. 306 } 307 308 if (!strncmp("now", s, 3)) { 309 return false; // no absolute start time available 310 } 311 312 char *end; 313 *npt1 = strtof(s, &end); 314 315 if (end == s || *end != '-') { 316 // Failed to parse float or trailing "dash". 317 return false; 318 } 319 320 s = end + 1; // skip the dash. 321 322 if (!strncmp("now", s, 3)) { 323 return false; // no absolute end time available 324 } 325 326 *npt2 = strtof(s, &end); 327 328 if (end == s || *end != '\0') { 329 return false; 330 } 331 332 return *npt2 > *npt1; 333 } 334 335 } // namespace android 336 337