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