ASessionDescription.cpp revision 39ddf8e0f18766f7ba1e3246b774aa6ebd93eea8
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#include "ASessionDescription.h"
18
19#include <media/stagefright/foundation/ADebug.h>
20#include <media/stagefright/foundation/AString.h>
21
22#include <stdlib.h>
23
24namespace android {
25
26ASessionDescription::ASessionDescription()
27    : mIsValid(false) {
28}
29
30ASessionDescription::~ASessionDescription() {
31}
32
33bool ASessionDescription::setTo(const void *data, size_t size) {
34    mIsValid = parse(data, size);
35
36    if (!mIsValid) {
37        mTracks.clear();
38        mFormats.clear();
39    }
40
41    return mIsValid;
42}
43
44bool ASessionDescription::parse(const void *data, size_t size) {
45    mTracks.clear();
46    mFormats.clear();
47
48    mTracks.push(Attribs());
49    mFormats.push(AString("[root]"));
50
51    AString desc((const char *)data, size);
52    LOG(INFO) << desc;
53
54    size_t i = 0;
55    for (;;) {
56        ssize_t eolPos = desc.find("\r\n", i);
57        if (eolPos < 0) {
58            break;
59        }
60
61        AString line(desc, i, eolPos - i);
62
63        if (line.size() < 2 || line.c_str()[1] != '=') {
64            return false;
65        }
66
67        switch (line.c_str()[0]) {
68            case 'v':
69            {
70                if (strcmp(line.c_str(), "v=0")) {
71                    return false;
72                }
73                break;
74            }
75
76            case 'a':
77            case 'b':
78            {
79                AString key, value;
80
81                ssize_t colonPos = line.find(":", 2);
82                if (colonPos < 0) {
83                    key = line;
84                } else {
85                    key.setTo(line, 0, colonPos);
86
87                    if (key == "a=fmtp" || key == "a=rtpmap"
88                            || key == "a=framesize") {
89                        ssize_t spacePos = line.find(" ", colonPos + 1);
90                        if (spacePos < 0) {
91                            return false;
92                        }
93
94                        key.setTo(line, 0, spacePos);
95
96                        colonPos = spacePos;
97                    }
98
99                    value.setTo(line, colonPos + 1, line.size() - colonPos - 1);
100                }
101
102                key.trim();
103                value.trim();
104
105                LOG(VERBOSE) << "adding '" << key << "' => '" << value << "'";
106
107                mTracks.editItemAt(mTracks.size() - 1).add(key, value);
108                break;
109            }
110
111            case 'm':
112            {
113                LOG(VERBOSE) << "new section '" << AString(line, 2, line.size() - 2) << "'";
114
115                mTracks.push(Attribs());
116                mFormats.push(AString(line, 2, line.size() - 2));
117                break;
118            }
119
120            default:
121            {
122                AString key, value;
123
124                ssize_t equalPos = line.find("=");
125
126                key = AString(line, 0, equalPos + 1);
127                value = AString(line, equalPos + 1, line.size() - equalPos - 1);
128
129                key.trim();
130                value.trim();
131
132                LOG(VERBOSE) << "adding '" << key << "' => '" << value << "'";
133
134                mTracks.editItemAt(mTracks.size() - 1).add(key, value);
135                break;
136            }
137        }
138
139        i = eolPos + 2;
140    }
141
142    return true;
143}
144
145bool ASessionDescription::isValid() const {
146    return mIsValid;
147}
148
149size_t ASessionDescription::countTracks() const {
150    return mTracks.size();
151}
152
153void ASessionDescription::getFormat(size_t index, AString *value) const {
154    CHECK_GE(index, 0u);
155    CHECK_LT(index, mTracks.size());
156
157    *value = mFormats.itemAt(index);
158}
159
160bool ASessionDescription::findAttribute(
161        size_t index, const char *key, AString *value) const {
162    CHECK_GE(index, 0u);
163    CHECK_LT(index, mTracks.size());
164
165    value->clear();
166
167    const Attribs &track = mTracks.itemAt(index);
168    ssize_t i = track.indexOfKey(AString(key));
169
170    if (i < 0) {
171        return false;
172    }
173
174    *value = track.valueAt(i);
175
176    return true;
177}
178
179void ASessionDescription::getFormatType(
180        size_t index, unsigned long *PT,
181        AString *desc, AString *params) const {
182    AString format;
183    getFormat(index, &format);
184
185    char *lastSpacePos = strrchr(format.c_str(), ' ');
186    CHECK(lastSpacePos != NULL);
187
188    char *end;
189    unsigned long x = strtoul(lastSpacePos + 1, &end, 10);
190    CHECK_GT(end, lastSpacePos + 1);
191    CHECK_EQ(*end, '\0');
192
193    *PT = x;
194
195    char key[20];
196    sprintf(key, "a=rtpmap:%lu", x);
197
198    CHECK(findAttribute(index, key, desc));
199
200    sprintf(key, "a=fmtp:%lu", x);
201    if (!findAttribute(index, key, params)) {
202        params->clear();
203    }
204}
205
206void ASessionDescription::getDimensions(
207        size_t index, unsigned long PT,
208        int32_t *width, int32_t *height) const {
209    char key[20];
210    sprintf(key, "a=framesize:%lu", PT);
211    AString value;
212    CHECK(findAttribute(index, key, &value));
213
214    const char *s = value.c_str();
215    char *end;
216    *width = strtoul(s, &end, 10);
217    CHECK_GT(end, s);
218    CHECK_EQ(*end, '-');
219
220    s = end + 1;
221    *height = strtoul(s, &end, 10);
222    CHECK_GT(end, s);
223    CHECK_EQ(*end, '\0');
224}
225
226bool ASessionDescription::getDurationUs(int64_t *durationUs) const {
227    *durationUs = 0;
228
229    CHECK(mIsValid);
230
231    AString value;
232    if (!findAttribute(0, "a=range", &value)) {
233        return false;
234    }
235
236    if (value == "npt=now-") {
237        return false;
238    }
239
240    if (strncmp(value.c_str(), "npt=", 4)) {
241        return false;
242    }
243
244    const char *s = value.c_str() + 4;
245    char *end;
246    double from = strtod(s, &end);
247    CHECK_GT(end, s);
248    CHECK_EQ(*end, '-');
249
250    s = end + 1;
251    double to = strtod(s, &end);
252    CHECK_GT(end, s);
253    CHECK_EQ(*end, '\0');
254
255    CHECK_GE(to, from);
256
257    *durationUs = (int64_t)((to - from) * 1E6);
258
259    return true;
260}
261
262// static
263void ASessionDescription::ParseFormatDesc(
264        const char *desc, int32_t *timescale, int32_t *numChannels) {
265    const char *slash1 = strchr(desc, '/');
266    CHECK(slash1 != NULL);
267
268    const char *s = slash1 + 1;
269    char *end;
270    unsigned long x = strtoul(s, &end, 10);
271    CHECK_GT(end, s);
272    CHECK(*end == '\0' || *end == '/');
273
274    *timescale = x;
275    *numChannels = 1;
276
277    if (*end == '/') {
278        s = end + 1;
279        unsigned long x = strtoul(s, &end, 10);
280        CHECK_GT(end, s);
281        CHECK_EQ(*end, '\0');
282
283        *numChannels = x;
284    }
285}
286
287}  // namespace android
288
289