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