ASessionDescription.cpp revision 4579b7d49f6dd4f37e6043e59debfd72d69b8e7b
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 LOGI("%s", desc.c_str()); 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.size() < 2 || line.c_str()[1] != '=') { 76 return false; 77 } 78 79 switch (line.c_str()[0]) { 80 case 'v': 81 { 82 if (strcmp(line.c_str(), "v=0")) { 83 return false; 84 } 85 break; 86 } 87 88 case 'a': 89 case 'b': 90 { 91 AString key, value; 92 93 ssize_t colonPos = line.find(":", 2); 94 if (colonPos < 0) { 95 key = line; 96 } else { 97 key.setTo(line, 0, colonPos); 98 99 if (key == "a=fmtp" || key == "a=rtpmap" 100 || key == "a=framesize") { 101 ssize_t spacePos = line.find(" ", colonPos + 1); 102 if (spacePos < 0) { 103 return false; 104 } 105 106 key.setTo(line, 0, spacePos); 107 108 colonPos = spacePos; 109 } 110 111 value.setTo(line, colonPos + 1, line.size() - colonPos - 1); 112 } 113 114 key.trim(); 115 value.trim(); 116 117 LOGV("adding '%s' => '%s'", key.c_str(), value.c_str()); 118 119 mTracks.editItemAt(mTracks.size() - 1).add(key, value); 120 break; 121 } 122 123 case 'm': 124 { 125 LOGV("new section '%s'", 126 AString(line, 2, line.size() - 2).c_str()); 127 128 mTracks.push(Attribs()); 129 mFormats.push(AString(line, 2, line.size() - 2)); 130 break; 131 } 132 133 default: 134 { 135 AString key, value; 136 137 ssize_t equalPos = line.find("="); 138 139 key = AString(line, 0, equalPos + 1); 140 value = AString(line, equalPos + 1, line.size() - equalPos - 1); 141 142 key.trim(); 143 value.trim(); 144 145 LOGV("adding '%s' => '%s'", key.c_str(), value.c_str()); 146 147 mTracks.editItemAt(mTracks.size() - 1).add(key, value); 148 break; 149 } 150 } 151 152 i = eolPos + 1; 153 } 154 155 return true; 156} 157 158bool ASessionDescription::isValid() const { 159 return mIsValid; 160} 161 162size_t ASessionDescription::countTracks() const { 163 return mTracks.size(); 164} 165 166void ASessionDescription::getFormat(size_t index, AString *value) const { 167 CHECK_GE(index, 0u); 168 CHECK_LT(index, mTracks.size()); 169 170 *value = mFormats.itemAt(index); 171} 172 173bool ASessionDescription::findAttribute( 174 size_t index, const char *key, AString *value) const { 175 CHECK_GE(index, 0u); 176 CHECK_LT(index, mTracks.size()); 177 178 value->clear(); 179 180 const Attribs &track = mTracks.itemAt(index); 181 ssize_t i = track.indexOfKey(AString(key)); 182 183 if (i < 0) { 184 return false; 185 } 186 187 *value = track.valueAt(i); 188 189 return true; 190} 191 192void ASessionDescription::getFormatType( 193 size_t index, unsigned long *PT, 194 AString *desc, AString *params) const { 195 AString format; 196 getFormat(index, &format); 197 198 const char *lastSpacePos = strrchr(format.c_str(), ' '); 199 CHECK(lastSpacePos != NULL); 200 201 char *end; 202 unsigned long x = strtoul(lastSpacePos + 1, &end, 10); 203 CHECK_GT(end, lastSpacePos + 1); 204 CHECK_EQ(*end, '\0'); 205 206 *PT = x; 207 208 char key[20]; 209 sprintf(key, "a=rtpmap:%lu", x); 210 211 CHECK(findAttribute(index, key, desc)); 212 213 sprintf(key, "a=fmtp:%lu", x); 214 if (!findAttribute(index, key, params)) { 215 params->clear(); 216 } 217} 218 219bool ASessionDescription::getDimensions( 220 size_t index, unsigned long PT, 221 int32_t *width, int32_t *height) const { 222 *width = 0; 223 *height = 0; 224 225 char key[20]; 226 sprintf(key, "a=framesize:%lu", PT); 227 AString value; 228 if (!findAttribute(index, key, &value)) { 229 return false; 230 } 231 232 const char *s = value.c_str(); 233 char *end; 234 *width = strtoul(s, &end, 10); 235 CHECK_GT(end, s); 236 CHECK_EQ(*end, '-'); 237 238 s = end + 1; 239 *height = strtoul(s, &end, 10); 240 CHECK_GT(end, s); 241 CHECK_EQ(*end, '\0'); 242 243 return true; 244} 245 246bool ASessionDescription::getDurationUs(int64_t *durationUs) const { 247 *durationUs = 0; 248 249 CHECK(mIsValid); 250 251 AString value; 252 if (!findAttribute(0, "a=range", &value)) { 253 return false; 254 } 255 256 if (value == "npt=now-" || value == "npt=0-") { 257 return false; 258 } 259 260 if (strncmp(value.c_str(), "npt=", 4)) { 261 return false; 262 } 263 264 const char *s = value.c_str() + 4; 265 char *end; 266 double from = strtod(s, &end); 267 CHECK_GT(end, s); 268 CHECK_EQ(*end, '-'); 269 270 s = end + 1; 271 double to = strtod(s, &end); 272 CHECK_GT(end, s); 273 CHECK_EQ(*end, '\0'); 274 275 CHECK_GE(to, from); 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} // namespace android 308 309