OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright (C) 2011, 2012 Apple Inc. All rights reserved. | |
3 * | |
4 * Redistribution and use in source and binary forms, with or without | |
5 * modification, are permitted provided that the following conditions | |
6 * are met: | |
7 * 1. Redistributions of source code must retain the above copyright | |
8 * notice, this list of conditions and the following disclaimer. | |
9 * 2. Redistributions in binary form must reproduce the above copyright | |
10 * notice, this list of conditions and the following disclaimer in the | |
11 * documentation and/or other materials provided with the distribution. | |
12 * | |
13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY | |
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | |
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR | |
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | |
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
24 */ | |
25 | |
26 #include "core/html/MediaFragmentURIParser.h" | |
27 | |
28 #include "wtf/text/CString.h" | |
29 #include "wtf/text/StringBuilder.h" | |
30 #include "wtf/text/WTFString.h" | |
31 | |
32 namespace blink { | |
33 | |
34 const unsigned nptIdentiferLength = 4; // "npt:" | |
35 | |
36 static String collectDigits(const LChar* input, | |
37 unsigned length, | |
38 unsigned& position) { | |
39 StringBuilder digits; | |
40 | |
41 // http://www.ietf.org/rfc/rfc2326.txt | |
42 // DIGIT ; any positive number | |
43 while (position < length && isASCIIDigit(input[position])) | |
44 digits.append(input[position++]); | |
45 return digits.toString(); | |
46 } | |
47 | |
48 static String collectFraction(const LChar* input, | |
49 unsigned length, | |
50 unsigned& position) { | |
51 StringBuilder digits; | |
52 | |
53 // http://www.ietf.org/rfc/rfc2326.txt | |
54 // [ "." *DIGIT ] | |
55 if (input[position] != '.') | |
56 return String(); | |
57 | |
58 digits.append(input[position++]); | |
59 while (position < length && isASCIIDigit(input[position])) | |
60 digits.append(input[position++]); | |
61 return digits.toString(); | |
62 } | |
63 | |
64 MediaFragmentURIParser::MediaFragmentURIParser(const KURL& url) | |
65 : m_url(url), | |
66 m_timeFormat(None), | |
67 m_startTime(std::numeric_limits<double>::quiet_NaN()), | |
68 m_endTime(std::numeric_limits<double>::quiet_NaN()) {} | |
69 | |
70 double MediaFragmentURIParser::startTime() { | |
71 if (!m_url.isValid()) | |
72 return std::numeric_limits<double>::quiet_NaN(); | |
73 if (m_timeFormat == None) | |
74 parseTimeFragment(); | |
75 return m_startTime; | |
76 } | |
77 | |
78 double MediaFragmentURIParser::endTime() { | |
79 if (!m_url.isValid()) | |
80 return std::numeric_limits<double>::quiet_NaN(); | |
81 if (m_timeFormat == None) | |
82 parseTimeFragment(); | |
83 return m_endTime; | |
84 } | |
85 | |
86 void MediaFragmentURIParser::parseFragments() { | |
87 if (!m_url.hasFragmentIdentifier()) | |
88 return; | |
89 String fragmentString = m_url.fragmentIdentifier(); | |
90 if (fragmentString.isEmpty()) | |
91 return; | |
92 | |
93 unsigned offset = 0; | |
94 unsigned end = fragmentString.length(); | |
95 while (offset < end) { | |
96 // http://www.w3.org/2008/WebVideo/Fragments/WD-media-fragments-spec/#proces
sing-name-value-components | |
97 // 1. Parse the octet string according to the namevalues syntax, yielding a | |
98 // list of name-value pairs, where name and value are both octet string. | |
99 // In accordance with RFC 3986, the name and value components must be | |
100 // parsed and separated before percent-encoded octets are decoded. | |
101 size_t parameterStart = offset; | |
102 size_t parameterEnd = fragmentString.find('&', offset); | |
103 if (parameterEnd == kNotFound) | |
104 parameterEnd = end; | |
105 | |
106 size_t equalOffset = fragmentString.find('=', offset); | |
107 if (equalOffset == kNotFound || equalOffset > parameterEnd) { | |
108 offset = parameterEnd + 1; | |
109 continue; | |
110 } | |
111 | |
112 // 2. For each name-value pair: | |
113 // a. Decode percent-encoded octets in name and value as defined by RFC | |
114 // 3986. If either name or value are not valid percent-encoded strings, | |
115 // then remove the name-value pair from the list. | |
116 String name = decodeURLEscapeSequences( | |
117 fragmentString.substring(parameterStart, equalOffset - parameterStart)); | |
118 String value; | |
119 if (equalOffset != parameterEnd) | |
120 value = decodeURLEscapeSequences(fragmentString.substring( | |
121 equalOffset + 1, parameterEnd - equalOffset - 1)); | |
122 | |
123 // b. Convert name and value to Unicode strings by interpreting them as | |
124 // UTF-8. If either name or value are not valid UTF-8 strings, then | |
125 // remove the name-value pair from the list. | |
126 bool validUTF8 = true; | |
127 if (!name.isEmpty()) { | |
128 name = name.utf8(StrictUTF8Conversion).data(); | |
129 validUTF8 = !name.isEmpty(); | |
130 } | |
131 if (validUTF8 && !value.isEmpty()) { | |
132 value = value.utf8(StrictUTF8Conversion).data(); | |
133 validUTF8 = !value.isEmpty(); | |
134 } | |
135 | |
136 if (validUTF8) | |
137 m_fragments.push_back(std::make_pair(name, value)); | |
138 | |
139 offset = parameterEnd + 1; | |
140 } | |
141 } | |
142 | |
143 void MediaFragmentURIParser::parseTimeFragment() { | |
144 DCHECK_EQ(m_timeFormat, None); | |
145 | |
146 if (m_fragments.isEmpty()) | |
147 parseFragments(); | |
148 | |
149 m_timeFormat = Invalid; | |
150 | |
151 for (const auto& fragment : m_fragments) { | |
152 DCHECK(fragment.first.is8Bit()); | |
153 DCHECK(fragment.second.is8Bit()); | |
154 | |
155 // http://www.w3.org/2008/WebVideo/Fragments/WD-media-fragments-spec/#naming
-time | |
156 // Temporal clipping is denoted by the name t, and specified as an interval | |
157 // with a begin time and an end time | |
158 if (fragment.first != "t") | |
159 continue; | |
160 | |
161 // http://www.w3.org/2008/WebVideo/Fragments/WD-media-fragments-spec/#npt-ti
me | |
162 // Temporal clipping can be specified either as Normal Play Time (npt) RFC | |
163 // 2326, as SMPTE timecodes, SMPTE, or as real-world clock time (clock) RFC | |
164 // 2326. Begin and end times are always specified in the same format. The | |
165 // format is specified by name, followed by a colon (:), with npt: being the | |
166 // default. | |
167 | |
168 double start = std::numeric_limits<double>::quiet_NaN(); | |
169 double end = std::numeric_limits<double>::quiet_NaN(); | |
170 if (parseNPTFragment(fragment.second.characters8(), | |
171 fragment.second.length(), start, end)) { | |
172 m_startTime = start; | |
173 m_endTime = end; | |
174 m_timeFormat = NormalPlayTime; | |
175 | |
176 // Although we have a valid fragment, don't return yet because when a | |
177 // fragment dimensions occurs multiple times, only the last occurrence of | |
178 // that dimension is used: | |
179 // http://www.w3.org/2008/WebVideo/Fragments/WD-media-fragments-spec/#erro
r-uri-general | |
180 // Multiple occurrences of the same dimension: only the last valid | |
181 // occurrence of a dimension (e.g., t=10 in #t=2&t=10) is interpreted, all | |
182 // previous occurrences (valid or invalid) SHOULD be ignored by the UA. | |
183 } | |
184 } | |
185 m_fragments.clear(); | |
186 } | |
187 | |
188 bool MediaFragmentURIParser::parseNPTFragment(const LChar* timeString, | |
189 unsigned length, | |
190 double& startTime, | |
191 double& endTime) { | |
192 unsigned offset = 0; | |
193 if (length >= nptIdentiferLength && timeString[0] == 'n' && | |
194 timeString[1] == 'p' && timeString[2] == 't' && timeString[3] == ':') | |
195 offset += nptIdentiferLength; | |
196 | |
197 if (offset == length) | |
198 return false; | |
199 | |
200 // http://www.w3.org/2008/WebVideo/Fragments/WD-media-fragments-spec/#naming-t
ime | |
201 // If a single number only is given, this corresponds to the begin time except | |
202 // if it is preceded by a comma that would in this case indicate the end time. | |
203 if (timeString[offset] == ',') { | |
204 startTime = 0; | |
205 } else { | |
206 if (!parseNPTTime(timeString, length, offset, startTime)) | |
207 return false; | |
208 } | |
209 | |
210 if (offset == length) | |
211 return true; | |
212 | |
213 if (timeString[offset] != ',') | |
214 return false; | |
215 if (++offset == length) | |
216 return false; | |
217 | |
218 if (!parseNPTTime(timeString, length, offset, endTime)) | |
219 return false; | |
220 | |
221 if (offset != length) | |
222 return false; | |
223 | |
224 if (startTime >= endTime) | |
225 return false; | |
226 | |
227 return true; | |
228 } | |
229 | |
230 bool MediaFragmentURIParser::parseNPTTime(const LChar* timeString, | |
231 unsigned length, | |
232 unsigned& offset, | |
233 double& time) { | |
234 enum Mode { Minutes, Hours }; | |
235 Mode mode = Minutes; | |
236 | |
237 if (offset >= length || !isASCIIDigit(timeString[offset])) | |
238 return false; | |
239 | |
240 // http://www.w3.org/2008/WebVideo/Fragments/WD-media-fragments-spec/#npttimed
ef | |
241 // Normal Play Time can either be specified as seconds, with an optional | |
242 // fractional part to indicate miliseconds, or as colon-separated hours, | |
243 // minutes and seconds (again with an optional fraction). Minutes and | |
244 // seconds must be specified as exactly two digits, hours and fractional | |
245 // seconds can be any number of digits. The hours, minutes and seconds | |
246 // specification for NPT is a convenience only, it does not signal frame | |
247 // accuracy. The specification of the "npt:" identifier is optional since | |
248 // NPT is the default time scheme. This specification builds on the RTSP | |
249 // specification of NPT RFC 2326. | |
250 // | |
251 // ; defined in RFC 2326 | |
252 // npt-sec = 1*DIGIT [ "." *DIGIT ] | |
253 // npt-hhmmss = npt-hh ":" npt-mm ":" npt-ss [ "." *DIGIT] | |
254 // npt-mmss = npt-mm ":" npt-ss [ "." *DIGIT] | |
255 // npt-hh = 1*DIGIT ; any positive number | |
256 // npt-mm = 2DIGIT ; 0-59 | |
257 // npt-ss = 2DIGIT ; 0-59 | |
258 | |
259 String digits1 = collectDigits(timeString, length, offset); | |
260 int value1 = digits1.toInt(); | |
261 if (offset >= length || timeString[offset] == ',') { | |
262 time = value1; | |
263 return true; | |
264 } | |
265 | |
266 double fraction = 0; | |
267 if (timeString[offset] == '.') { | |
268 if (offset == length) | |
269 return true; | |
270 String digits = collectFraction(timeString, length, offset); | |
271 fraction = digits.toDouble(); | |
272 time = value1 + fraction; | |
273 return true; | |
274 } | |
275 | |
276 if (digits1.length() < 2) | |
277 return false; | |
278 if (digits1.length() > 2) | |
279 mode = Hours; | |
280 | |
281 // Collect the next sequence of 0-9 after ':' | |
282 if (offset >= length || timeString[offset++] != ':') | |
283 return false; | |
284 if (offset >= length || !isASCIIDigit(timeString[(offset)])) | |
285 return false; | |
286 String digits2 = collectDigits(timeString, length, offset); | |
287 int value2 = digits2.toInt(); | |
288 if (digits2.length() != 2) | |
289 return false; | |
290 | |
291 // Detect whether this timestamp includes hours. | |
292 int value3; | |
293 if (mode == Hours || (offset < length && timeString[offset] == ':')) { | |
294 if (offset >= length || timeString[offset++] != ':') | |
295 return false; | |
296 if (offset >= length || !isASCIIDigit(timeString[offset])) | |
297 return false; | |
298 String digits3 = collectDigits(timeString, length, offset); | |
299 if (digits3.length() != 2) | |
300 return false; | |
301 value3 = digits3.toInt(); | |
302 } else { | |
303 value3 = value2; | |
304 value2 = value1; | |
305 value1 = 0; | |
306 } | |
307 | |
308 if (offset < length && timeString[offset] == '.') | |
309 fraction = collectFraction(timeString, length, offset).toDouble(); | |
310 | |
311 const int secondsPerHour = 3600; | |
312 const int secondsPerMinute = 60; | |
313 time = (value1 * secondsPerHour) + (value2 * secondsPerMinute) + value3 + | |
314 fraction; | |
315 return true; | |
316 } | |
317 | |
318 } // namespace blink | |
OLD | NEW |