ASessionDescription.cpp revision 934ca8cb1bcffcf1781a576ca625d2f901e5f0a9
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 (value == "npt=now-" || value == "npt=0-") { 258 return false; 259 } 260 261 if (strncmp(value.c_str(), "npt=", 4)) { 262 return false; 263 } 264 265 const char *s = value.c_str() + 4; 266 char *end; 267 double from = strtod(s, &end); 268 269 if (end == s || *end != '-') { 270 return false; 271 } 272 273 s = end + 1; 274 double to = strtod(s, &end); 275 276 if (end == s || *end != '\0' || to < from) { 277 return false; 278 } 279 280 *durationUs = (int64_t)((to - from) * 1E6); 281 282 return true; 283} 284 285// static 286void ASessionDescription::ParseFormatDesc( 287 const char *desc, int32_t *timescale, int32_t *numChannels) { 288 const char *slash1 = strchr(desc, '/'); 289 CHECK(slash1 != NULL); 290 291 const char *s = slash1 + 1; 292 char *end; 293 unsigned long x = strtoul(s, &end, 10); 294 CHECK_GT(end, s); 295 CHECK(*end == '\0' || *end == '/'); 296 297 *timescale = x; 298 *numChannels = 1; 299 300 if (*end == '/') { 301 s = end + 1; 302 unsigned long x = strtoul(s, &end, 10); 303 CHECK_GT(end, s); 304 CHECK_EQ(*end, '\0'); 305 306 *numChannels = x; 307 } 308} 309 310} // namespace android 311 312