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