ASessionDescription.cpp revision 39ddf8e0f18766f7ba1e3246b774aa6ebd93eea8
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#include "ASessionDescription.h" 18 19#include <media/stagefright/foundation/ADebug.h> 20#include <media/stagefright/foundation/AString.h> 21 22#include <stdlib.h> 23 24namespace android { 25 26ASessionDescription::ASessionDescription() 27 : mIsValid(false) { 28} 29 30ASessionDescription::~ASessionDescription() { 31} 32 33bool ASessionDescription::setTo(const void *data, size_t size) { 34 mIsValid = parse(data, size); 35 36 if (!mIsValid) { 37 mTracks.clear(); 38 mFormats.clear(); 39 } 40 41 return mIsValid; 42} 43 44bool ASessionDescription::parse(const void *data, size_t size) { 45 mTracks.clear(); 46 mFormats.clear(); 47 48 mTracks.push(Attribs()); 49 mFormats.push(AString("[root]")); 50 51 AString desc((const char *)data, size); 52 LOG(INFO) << desc; 53 54 size_t i = 0; 55 for (;;) { 56 ssize_t eolPos = desc.find("\r\n", i); 57 if (eolPos < 0) { 58 break; 59 } 60 61 AString line(desc, i, eolPos - i); 62 63 if (line.size() < 2 || line.c_str()[1] != '=') { 64 return false; 65 } 66 67 switch (line.c_str()[0]) { 68 case 'v': 69 { 70 if (strcmp(line.c_str(), "v=0")) { 71 return false; 72 } 73 break; 74 } 75 76 case 'a': 77 case 'b': 78 { 79 AString key, value; 80 81 ssize_t colonPos = line.find(":", 2); 82 if (colonPos < 0) { 83 key = line; 84 } else { 85 key.setTo(line, 0, colonPos); 86 87 if (key == "a=fmtp" || key == "a=rtpmap" 88 || key == "a=framesize") { 89 ssize_t spacePos = line.find(" ", colonPos + 1); 90 if (spacePos < 0) { 91 return false; 92 } 93 94 key.setTo(line, 0, spacePos); 95 96 colonPos = spacePos; 97 } 98 99 value.setTo(line, colonPos + 1, line.size() - colonPos - 1); 100 } 101 102 key.trim(); 103 value.trim(); 104 105 LOG(VERBOSE) << "adding '" << key << "' => '" << value << "'"; 106 107 mTracks.editItemAt(mTracks.size() - 1).add(key, value); 108 break; 109 } 110 111 case 'm': 112 { 113 LOG(VERBOSE) << "new section '" << AString(line, 2, line.size() - 2) << "'"; 114 115 mTracks.push(Attribs()); 116 mFormats.push(AString(line, 2, line.size() - 2)); 117 break; 118 } 119 120 default: 121 { 122 AString key, value; 123 124 ssize_t equalPos = line.find("="); 125 126 key = AString(line, 0, equalPos + 1); 127 value = AString(line, equalPos + 1, line.size() - equalPos - 1); 128 129 key.trim(); 130 value.trim(); 131 132 LOG(VERBOSE) << "adding '" << key << "' => '" << value << "'"; 133 134 mTracks.editItemAt(mTracks.size() - 1).add(key, value); 135 break; 136 } 137 } 138 139 i = eolPos + 2; 140 } 141 142 return true; 143} 144 145bool ASessionDescription::isValid() const { 146 return mIsValid; 147} 148 149size_t ASessionDescription::countTracks() const { 150 return mTracks.size(); 151} 152 153void ASessionDescription::getFormat(size_t index, AString *value) const { 154 CHECK_GE(index, 0u); 155 CHECK_LT(index, mTracks.size()); 156 157 *value = mFormats.itemAt(index); 158} 159 160bool ASessionDescription::findAttribute( 161 size_t index, const char *key, AString *value) const { 162 CHECK_GE(index, 0u); 163 CHECK_LT(index, mTracks.size()); 164 165 value->clear(); 166 167 const Attribs &track = mTracks.itemAt(index); 168 ssize_t i = track.indexOfKey(AString(key)); 169 170 if (i < 0) { 171 return false; 172 } 173 174 *value = track.valueAt(i); 175 176 return true; 177} 178 179void ASessionDescription::getFormatType( 180 size_t index, unsigned long *PT, 181 AString *desc, AString *params) const { 182 AString format; 183 getFormat(index, &format); 184 185 char *lastSpacePos = strrchr(format.c_str(), ' '); 186 CHECK(lastSpacePos != NULL); 187 188 char *end; 189 unsigned long x = strtoul(lastSpacePos + 1, &end, 10); 190 CHECK_GT(end, lastSpacePos + 1); 191 CHECK_EQ(*end, '\0'); 192 193 *PT = x; 194 195 char key[20]; 196 sprintf(key, "a=rtpmap:%lu", x); 197 198 CHECK(findAttribute(index, key, desc)); 199 200 sprintf(key, "a=fmtp:%lu", x); 201 if (!findAttribute(index, key, params)) { 202 params->clear(); 203 } 204} 205 206void ASessionDescription::getDimensions( 207 size_t index, unsigned long PT, 208 int32_t *width, int32_t *height) const { 209 char key[20]; 210 sprintf(key, "a=framesize:%lu", PT); 211 AString value; 212 CHECK(findAttribute(index, key, &value)); 213 214 const char *s = value.c_str(); 215 char *end; 216 *width = strtoul(s, &end, 10); 217 CHECK_GT(end, s); 218 CHECK_EQ(*end, '-'); 219 220 s = end + 1; 221 *height = strtoul(s, &end, 10); 222 CHECK_GT(end, s); 223 CHECK_EQ(*end, '\0'); 224} 225 226bool ASessionDescription::getDurationUs(int64_t *durationUs) const { 227 *durationUs = 0; 228 229 CHECK(mIsValid); 230 231 AString value; 232 if (!findAttribute(0, "a=range", &value)) { 233 return false; 234 } 235 236 if (value == "npt=now-") { 237 return false; 238 } 239 240 if (strncmp(value.c_str(), "npt=", 4)) { 241 return false; 242 } 243 244 const char *s = value.c_str() + 4; 245 char *end; 246 double from = strtod(s, &end); 247 CHECK_GT(end, s); 248 CHECK_EQ(*end, '-'); 249 250 s = end + 1; 251 double to = strtod(s, &end); 252 CHECK_GT(end, s); 253 CHECK_EQ(*end, '\0'); 254 255 CHECK_GE(to, from); 256 257 *durationUs = (int64_t)((to - from) * 1E6); 258 259 return true; 260} 261 262// static 263void ASessionDescription::ParseFormatDesc( 264 const char *desc, int32_t *timescale, int32_t *numChannels) { 265 const char *slash1 = strchr(desc, '/'); 266 CHECK(slash1 != NULL); 267 268 const char *s = slash1 + 1; 269 char *end; 270 unsigned long x = strtoul(s, &end, 10); 271 CHECK_GT(end, s); 272 CHECK(*end == '\0' || *end == '/'); 273 274 *timescale = x; 275 *numChannels = 1; 276 277 if (*end == '/') { 278 s = end + 1; 279 unsigned long x = strtoul(s, &end, 10); 280 CHECK_GT(end, s); 281 CHECK_EQ(*end, '\0'); 282 283 *numChannels = x; 284 } 285} 286 287} // namespace android 288 289