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 #include <cutils/log.h>
21
22 #include "ASessionDescription.h"
23
24 #include <media/stagefright/foundation/ADebug.h>
25 #include <media/stagefright/foundation/AString.h>
26
27 #include <stdlib.h>
28
29 namespace android {
30
ASessionDescription()31 ASessionDescription::ASessionDescription()
32 : mIsValid(false) {
33 }
34
~ASessionDescription()35 ASessionDescription::~ASessionDescription() {
36 }
37
setTo(const void * data,size_t size)38 bool ASessionDescription::setTo(const void *data, size_t size) {
39 mIsValid = parse(data, size);
40
41 if (!mIsValid) {
42 mTracks.clear();
43 mFormats.clear();
44 }
45
46 return mIsValid;
47 }
48
parse(const void * data,size_t size)49 bool ASessionDescription::parse(const void *data, size_t size) {
50 mTracks.clear();
51 mFormats.clear();
52
53 mTracks.push(Attribs());
54 mFormats.push(AString("[root]"));
55
56 AString desc((const char *)data, size);
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.empty()) {
76 i = eolPos + 1;
77 continue;
78 }
79
80 if (line.size() < 2 || line.c_str()[1] != '=') {
81 return false;
82 }
83
84 ALOGI("%s", line.c_str());
85
86 switch (line.c_str()[0]) {
87 case 'v':
88 {
89 if (strcmp(line.c_str(), "v=0")) {
90 return false;
91 }
92 break;
93 }
94
95 case 'a':
96 case 'b':
97 {
98 AString key, value;
99
100 ssize_t colonPos = line.find(":", 2);
101 if (colonPos < 0) {
102 key = line;
103 } else {
104 key.setTo(line, 0, colonPos);
105
106 if (key == "a=fmtp" || key == "a=rtpmap"
107 || key == "a=framesize") {
108 ssize_t spacePos = line.find(" ", colonPos + 1);
109 if (spacePos < 0) {
110 return false;
111 }
112
113 key.setTo(line, 0, spacePos);
114
115 colonPos = spacePos;
116 }
117
118 value.setTo(line, colonPos + 1, line.size() - colonPos - 1);
119 }
120
121 key.trim();
122 value.trim();
123
124 ALOGV("adding '%s' => '%s'", key.c_str(), value.c_str());
125
126 mTracks.editItemAt(mTracks.size() - 1).add(key, value);
127 break;
128 }
129
130 case 'm':
131 {
132 ALOGV("new section '%s'",
133 AString(line, 2, line.size() - 2).c_str());
134
135 mTracks.push(Attribs());
136 mFormats.push(AString(line, 2, line.size() - 2));
137 break;
138 }
139
140 default:
141 {
142 AString key, value;
143
144 ssize_t equalPos = line.find("=");
145
146 key = AString(line, 0, equalPos + 1);
147 value = AString(line, equalPos + 1, line.size() - equalPos - 1);
148
149 key.trim();
150 value.trim();
151
152 ALOGV("adding '%s' => '%s'", key.c_str(), value.c_str());
153
154 mTracks.editItemAt(mTracks.size() - 1).add(key, value);
155 break;
156 }
157 }
158
159 i = eolPos + 1;
160 }
161
162 return true;
163 }
164
isValid() const165 bool ASessionDescription::isValid() const {
166 return mIsValid;
167 }
168
countTracks() const169 size_t ASessionDescription::countTracks() const {
170 return mTracks.size();
171 }
172
getFormat(size_t index,AString * value) const173 void ASessionDescription::getFormat(size_t index, AString *value) const {
174 CHECK_GE(index, 0u);
175 CHECK_LT(index, mTracks.size());
176
177 *value = mFormats.itemAt(index);
178 }
179
findAttribute(size_t index,const char * key,AString * value) const180 bool ASessionDescription::findAttribute(
181 size_t index, const char *key, AString *value) const {
182 CHECK_GE(index, 0u);
183 CHECK_LT(index, mTracks.size());
184
185 value->clear();
186
187 const Attribs &track = mTracks.itemAt(index);
188 ssize_t i = track.indexOfKey(AString(key));
189
190 if (i < 0) {
191 return false;
192 }
193
194 *value = track.valueAt(i);
195
196 return true;
197 }
198
getFormatType(size_t index,unsigned long * PT,AString * desc,AString * params) const199 void ASessionDescription::getFormatType(
200 size_t index, unsigned long *PT,
201 AString *desc, AString *params) const {
202 AString format;
203 getFormat(index, &format);
204
205 const char *lastSpacePos = strrchr(format.c_str(), ' ');
206 CHECK(lastSpacePos != NULL);
207
208 char *end;
209 unsigned long x = strtoul(lastSpacePos + 1, &end, 10);
210 CHECK_GT(end, lastSpacePos + 1);
211 CHECK_EQ(*end, '\0');
212
213 *PT = x;
214
215 char key[32];
216 snprintf(key, sizeof(key), "a=rtpmap:%lu", x);
217 if (findAttribute(index, key, desc)) {
218 snprintf(key, sizeof(key), "a=fmtp:%lu", x);
219 if (!findAttribute(index, key, params)) {
220 params->clear();
221 }
222 } else {
223 desc->clear();
224 params->clear();
225 }
226 }
227
getDimensions(size_t index,unsigned long PT,int32_t * width,int32_t * height) const228 bool ASessionDescription::getDimensions(
229 size_t index, unsigned long PT,
230 int32_t *width, int32_t *height) const {
231 *width = 0;
232 *height = 0;
233
234 char key[33];
235 snprintf(key, sizeof(key), "a=framesize:%lu", PT);
236 if (PT > 9999999) {
237 android_errorWriteLog(0x534e4554, "25747670");
238 }
239 AString value;
240 if (!findAttribute(index, key, &value)) {
241 return false;
242 }
243
244 const char *s = value.c_str();
245 char *end;
246 *width = strtoul(s, &end, 10);
247 CHECK_GT(end, s);
248 CHECK_EQ(*end, '-');
249
250 s = end + 1;
251 *height = strtoul(s, &end, 10);
252 CHECK_GT(end, s);
253 CHECK_EQ(*end, '\0');
254
255 return true;
256 }
257
getDurationUs(int64_t * durationUs) const258 bool ASessionDescription::getDurationUs(int64_t *durationUs) const {
259 *durationUs = 0;
260
261 CHECK(mIsValid);
262
263 AString value;
264 if (!findAttribute(0, "a=range", &value)) {
265 return false;
266 }
267
268 if (strncmp(value.c_str(), "npt=", 4)) {
269 return false;
270 }
271
272 float from, to;
273 if (!parseNTPRange(value.c_str() + 4, &from, &to)) {
274 return false;
275 }
276
277 *durationUs = (int64_t)((to - from) * 1E6);
278
279 return true;
280 }
281
282 // static
ParseFormatDesc(const char * desc,int32_t * timescale,int32_t * numChannels)283 void 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 // static
parseNTPRange(const char * s,float * npt1,float * npt2)308 bool ASessionDescription::parseNTPRange(
309 const char *s, float *npt1, float *npt2) {
310 if (s[0] == '-') {
311 return false; // no start time available.
312 }
313
314 if (!strncmp("now", s, 3)) {
315 return false; // no absolute start time available
316 }
317
318 char *end;
319 *npt1 = strtof(s, &end);
320
321 if (end == s || *end != '-') {
322 // Failed to parse float or trailing "dash".
323 return false;
324 }
325
326 s = end + 1; // skip the dash.
327
328 if (*s == '\0') {
329 *npt2 = FLT_MAX; // open ended.
330 return true;
331 }
332
333 if (!strncmp("now", s, 3)) {
334 return false; // no absolute end time available
335 }
336
337 *npt2 = strtof(s, &end);
338
339 if (end == s || *end != '\0') {
340 return false;
341 }
342
343 return *npt2 > *npt1;
344 }
345
346 } // namespace android
347
348