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