blob: ad813cddc0c96a5225b32d31fffbc09877a943a8 [file] [log] [blame]
Andreas Huber7a747b82010-06-07 15:19:40 -07001/*
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);
Andreas Huber57648e42010-08-04 10:14:30 -070052 LOG(INFO) << desc;
Andreas Huber7a747b82010-06-07 15:19:40 -070053
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 }
Andreas Huber57648e42010-08-04 10:14:30 -0700119
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 }
Andreas Huber7a747b82010-06-07 15:19:40 -0700137 }
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