blob: a9b33308b1ad26a58c4b07b73df3e2469246df59 [file] [log] [blame]
Andreas Huber7a747b82010-06-07 15:19:40 -07001/*
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
Andreas Huber6e3fa442010-09-21 13:13:15 -070017//#define LOG_NDEBUG 0
18#define LOG_TAG "ASessionDescription"
19#include <utils/Log.h>
20
Andreas Huber7a747b82010-06-07 15:19:40 -070021#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);
Andreas Huber7a747b82010-06-07 15:19:40 -070056
57 size_t i = 0;
58 for (;;) {
Andreas Hubera0b442e2010-10-20 15:00:34 -070059 ssize_t eolPos = desc.find("\n", i);
60
Andreas Huber7a747b82010-06-07 15:19:40 -070061 if (eolPos < 0) {
62 break;
63 }
64
Andreas Hubera0b442e2010-10-20 15:00:34 -070065 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 }
Andreas Huber7a747b82010-06-07 15:19:40 -070073
Andreas Huber04072692011-02-15 10:39:48 -080074 if (line.empty()) {
75 i = eolPos + 1;
76 continue;
77 }
78
Andreas Huber7a747b82010-06-07 15:19:40 -070079 if (line.size() < 2 || line.c_str()[1] != '=') {
80 return false;
81 }
82
Steve Block6215d3f2012-01-04 20:05:49 +000083 ALOGI("%s", line.c_str());
Andreas Huberb0d25a02010-10-27 13:59:59 -070084
Andreas Huber7a747b82010-06-07 15:19:40 -070085 switch (line.c_str()[0]) {
86 case 'v':
87 {
88 if (strcmp(line.c_str(), "v=0")) {
89 return false;
90 }
91 break;
92 }
93
94 case 'a':
95 case 'b':
96 {
97 AString key, value;
98
99 ssize_t colonPos = line.find(":", 2);
100 if (colonPos < 0) {
101 key = line;
102 } else {
103 key.setTo(line, 0, colonPos);
104
105 if (key == "a=fmtp" || key == "a=rtpmap"
106 || key == "a=framesize") {
107 ssize_t spacePos = line.find(" ", colonPos + 1);
108 if (spacePos < 0) {
109 return false;
110 }
111
112 key.setTo(line, 0, spacePos);
113
114 colonPos = spacePos;
115 }
116
117 value.setTo(line, colonPos + 1, line.size() - colonPos - 1);
118 }
119
120 key.trim();
121 value.trim();
122
Steve Block71f2cf12011-10-20 11:56:00 +0100123 ALOGV("adding '%s' => '%s'", key.c_str(), value.c_str());
Andreas Huber7a747b82010-06-07 15:19:40 -0700124
125 mTracks.editItemAt(mTracks.size() - 1).add(key, value);
126 break;
127 }
128
129 case 'm':
130 {
Steve Block71f2cf12011-10-20 11:56:00 +0100131 ALOGV("new section '%s'",
Andreas Huber6e3fa442010-09-21 13:13:15 -0700132 AString(line, 2, line.size() - 2).c_str());
Andreas Huber7a747b82010-06-07 15:19:40 -0700133
134 mTracks.push(Attribs());
135 mFormats.push(AString(line, 2, line.size() - 2));
136 break;
137 }
Andreas Huber57648e42010-08-04 10:14:30 -0700138
139 default:
140 {
141 AString key, value;
142
143 ssize_t equalPos = line.find("=");
144
145 key = AString(line, 0, equalPos + 1);
146 value = AString(line, equalPos + 1, line.size() - equalPos - 1);
147
148 key.trim();
149 value.trim();
150
Steve Block71f2cf12011-10-20 11:56:00 +0100151 ALOGV("adding '%s' => '%s'", key.c_str(), value.c_str());
Andreas Huber57648e42010-08-04 10:14:30 -0700152
153 mTracks.editItemAt(mTracks.size() - 1).add(key, value);
154 break;
155 }
Andreas Huber7a747b82010-06-07 15:19:40 -0700156 }
157
Andreas Hubera0b442e2010-10-20 15:00:34 -0700158 i = eolPos + 1;
Andreas Huber7a747b82010-06-07 15:19:40 -0700159 }
160
161 return true;
162}
163
164bool ASessionDescription::isValid() const {
165 return mIsValid;
166}
167
168size_t ASessionDescription::countTracks() const {
169 return mTracks.size();
170}
171
172void ASessionDescription::getFormat(size_t index, AString *value) const {
173 CHECK_GE(index, 0u);
174 CHECK_LT(index, mTracks.size());
175
176 *value = mFormats.itemAt(index);
177}
178
179bool ASessionDescription::findAttribute(
180 size_t index, const char *key, AString *value) const {
181 CHECK_GE(index, 0u);
182 CHECK_LT(index, mTracks.size());
183
184 value->clear();
185
186 const Attribs &track = mTracks.itemAt(index);
187 ssize_t i = track.indexOfKey(AString(key));
188
189 if (i < 0) {
190 return false;
191 }
192
193 *value = track.valueAt(i);
194
195 return true;
196}
197
198void ASessionDescription::getFormatType(
199 size_t index, unsigned long *PT,
200 AString *desc, AString *params) const {
201 AString format;
202 getFormat(index, &format);
203
Mike Lockwood5a23f8c2010-07-15 14:58:25 -0400204 const char *lastSpacePos = strrchr(format.c_str(), ' ');
Andreas Huber7a747b82010-06-07 15:19:40 -0700205 CHECK(lastSpacePos != NULL);
206
207 char *end;
208 unsigned long x = strtoul(lastSpacePos + 1, &end, 10);
209 CHECK_GT(end, lastSpacePos + 1);
210 CHECK_EQ(*end, '\0');
211
212 *PT = x;
213
214 char key[20];
215 sprintf(key, "a=rtpmap:%lu", x);
216
217 CHECK(findAttribute(index, key, desc));
218
219 sprintf(key, "a=fmtp:%lu", x);
220 if (!findAttribute(index, key, params)) {
221 params->clear();
222 }
223}
224
Andreas Huberaf063a62010-08-18 10:17:18 -0700225bool ASessionDescription::getDimensions(
Andreas Huber7a747b82010-06-07 15:19:40 -0700226 size_t index, unsigned long PT,
227 int32_t *width, int32_t *height) const {
Andreas Huberaf063a62010-08-18 10:17:18 -0700228 *width = 0;
229 *height = 0;
230
Andreas Huber7a747b82010-06-07 15:19:40 -0700231 char key[20];
232 sprintf(key, "a=framesize:%lu", PT);
233 AString value;
Andreas Huberaf063a62010-08-18 10:17:18 -0700234 if (!findAttribute(index, key, &value)) {
235 return false;
236 }
Andreas Huber7a747b82010-06-07 15:19:40 -0700237
238 const char *s = value.c_str();
239 char *end;
240 *width = strtoul(s, &end, 10);
241 CHECK_GT(end, s);
242 CHECK_EQ(*end, '-');
243
244 s = end + 1;
245 *height = strtoul(s, &end, 10);
246 CHECK_GT(end, s);
247 CHECK_EQ(*end, '\0');
Andreas Huberaf063a62010-08-18 10:17:18 -0700248
249 return true;
Andreas Huber7a747b82010-06-07 15:19:40 -0700250}
251
252bool ASessionDescription::getDurationUs(int64_t *durationUs) const {
253 *durationUs = 0;
254
255 CHECK(mIsValid);
256
257 AString value;
258 if (!findAttribute(0, "a=range", &value)) {
259 return false;
260 }
261
Andreas Huber7a747b82010-06-07 15:19:40 -0700262 if (strncmp(value.c_str(), "npt=", 4)) {
263 return false;
264 }
265
Andreas Hubera2edd7d82011-01-28 09:19:12 -0800266 float from, to;
267 if (!parseNTPRange(value.c_str() + 4, &from, &to)) {
Andreas Huber4bca5e12011-01-12 09:57:23 -0800268 return false;
269 }
Andreas Huber7a747b82010-06-07 15:19:40 -0700270
271 *durationUs = (int64_t)((to - from) * 1E6);
272
273 return true;
274}
275
276// static
277void ASessionDescription::ParseFormatDesc(
278 const char *desc, int32_t *timescale, int32_t *numChannels) {
279 const char *slash1 = strchr(desc, '/');
280 CHECK(slash1 != NULL);
281
282 const char *s = slash1 + 1;
283 char *end;
284 unsigned long x = strtoul(s, &end, 10);
285 CHECK_GT(end, s);
286 CHECK(*end == '\0' || *end == '/');
287
288 *timescale = x;
289 *numChannels = 1;
290
291 if (*end == '/') {
292 s = end + 1;
293 unsigned long x = strtoul(s, &end, 10);
294 CHECK_GT(end, s);
295 CHECK_EQ(*end, '\0');
296
297 *numChannels = x;
298 }
299}
300
Andreas Hubera2edd7d82011-01-28 09:19:12 -0800301// static
302bool ASessionDescription::parseNTPRange(
303 const char *s, float *npt1, float *npt2) {
304 if (s[0] == '-') {
305 return false; // no start time available.
306 }
307
308 if (!strncmp("now", s, 3)) {
309 return false; // no absolute start time available
310 }
311
312 char *end;
313 *npt1 = strtof(s, &end);
314
315 if (end == s || *end != '-') {
316 // Failed to parse float or trailing "dash".
317 return false;
318 }
319
320 s = end + 1; // skip the dash.
321
322 if (!strncmp("now", s, 3)) {
323 return false; // no absolute end time available
324 }
325
326 *npt2 = strtof(s, &end);
327
328 if (end == s || *end != '\0') {
329 return false;
330 }
331
332 return *npt2 > *npt1;
333}
334
Andreas Huber7a747b82010-06-07 15:19:40 -0700335} // namespace android
336