• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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