ASessionDescription.cpp revision cf7b9c7aae758ac0b99833915053c63c2ac46e09
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(VERBOSE) << 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
121        i = eolPos + 2;
122    }
123
124    return true;
125}
126
127bool ASessionDescription::isValid() const {
128    return mIsValid;
129}
130
131size_t ASessionDescription::countTracks() const {
132    return mTracks.size();
133}
134
135void ASessionDescription::getFormat(size_t index, AString *value) const {
136    CHECK_GE(index, 0u);
137    CHECK_LT(index, mTracks.size());
138
139    *value = mFormats.itemAt(index);
140}
141
142bool ASessionDescription::findAttribute(
143        size_t index, const char *key, AString *value) const {
144    CHECK_GE(index, 0u);
145    CHECK_LT(index, mTracks.size());
146
147    value->clear();
148
149    const Attribs &track = mTracks.itemAt(index);
150    ssize_t i = track.indexOfKey(AString(key));
151
152    if (i < 0) {
153        return false;
154    }
155
156    *value = track.valueAt(i);
157
158    return true;
159}
160
161void ASessionDescription::getFormatType(
162        size_t index, unsigned long *PT,
163        AString *desc, AString *params) const {
164    AString format;
165    getFormat(index, &format);
166
167    char *lastSpacePos = strrchr(format.c_str(), ' ');
168    CHECK(lastSpacePos != NULL);
169
170    char *end;
171    unsigned long x = strtoul(lastSpacePos + 1, &end, 10);
172    CHECK_GT(end, lastSpacePos + 1);
173    CHECK_EQ(*end, '\0');
174
175    *PT = x;
176
177    char key[20];
178    sprintf(key, "a=rtpmap:%lu", x);
179
180    CHECK(findAttribute(index, key, desc));
181
182    sprintf(key, "a=fmtp:%lu", x);
183    if (!findAttribute(index, key, params)) {
184        params->clear();
185    }
186}
187
188void ASessionDescription::getDimensions(
189        size_t index, unsigned long PT,
190        int32_t *width, int32_t *height) const {
191    char key[20];
192    sprintf(key, "a=framesize:%lu", PT);
193    AString value;
194    CHECK(findAttribute(index, key, &value));
195
196    const char *s = value.c_str();
197    char *end;
198    *width = strtoul(s, &end, 10);
199    CHECK_GT(end, s);
200    CHECK_EQ(*end, '-');
201
202    s = end + 1;
203    *height = strtoul(s, &end, 10);
204    CHECK_GT(end, s);
205    CHECK_EQ(*end, '\0');
206}
207
208bool ASessionDescription::getDurationUs(int64_t *durationUs) const {
209    *durationUs = 0;
210
211    CHECK(mIsValid);
212
213    AString value;
214    if (!findAttribute(0, "a=range", &value)) {
215        return false;
216    }
217
218    if (value == "npt=now-") {
219        return false;
220    }
221
222    if (strncmp(value.c_str(), "npt=", 4)) {
223        return false;
224    }
225
226    const char *s = value.c_str() + 4;
227    char *end;
228    double from = strtod(s, &end);
229    CHECK_GT(end, s);
230    CHECK_EQ(*end, '-');
231
232    s = end + 1;
233    double to = strtod(s, &end);
234    CHECK_GT(end, s);
235    CHECK_EQ(*end, '\0');
236
237    CHECK_GE(to, from);
238
239    *durationUs = (int64_t)((to - from) * 1E6);
240
241    return true;
242}
243
244// static
245void ASessionDescription::ParseFormatDesc(
246        const char *desc, int32_t *timescale, int32_t *numChannels) {
247    const char *slash1 = strchr(desc, '/');
248    CHECK(slash1 != NULL);
249
250    const char *s = slash1 + 1;
251    char *end;
252    unsigned long x = strtoul(s, &end, 10);
253    CHECK_GT(end, s);
254    CHECK(*end == '\0' || *end == '/');
255
256    *timescale = x;
257    *numChannels = 1;
258
259    if (*end == '/') {
260        s = end + 1;
261        unsigned long x = strtoul(s, &end, 10);
262        CHECK_GT(end, s);
263        CHECK_EQ(*end, '\0');
264
265        *numChannels = x;
266    }
267}
268
269}  // namespace android
270
271