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