OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | |
xhwang
2015/01/12 18:08:57
2015
kcwu
2015/01/13 14:57:00
Done.
| |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "media/filters/jpeg_parser.h" | |
6 | |
7 #include "base/big_endian.h" | |
8 #include "base/logging.h" | |
9 | |
10 using base::BigEndianReader; | |
11 | |
12 #define READ_U8_OR_RETURN_FALSE(out) \ | |
13 do { \ | |
14 uint8_t _out; \ | |
15 if (!reader.ReadU8(&_out)) { \ | |
16 DVLOG(1) \ | |
17 << "Error in stream: unexpected EOS while trying to read " #out; \ | |
18 return false; \ | |
19 } \ | |
20 *(out) = _out; \ | |
21 } while (0) | |
22 | |
23 #define READ_U16_OR_RETURN_FALSE(out) \ | |
24 do { \ | |
25 uint16_t _out; \ | |
26 if (!reader.ReadU16(&_out)) { \ | |
27 DVLOG(1) \ | |
28 << "Error in stream: unexpected EOS while trying to read " #out; \ | |
29 return false; \ | |
30 } \ | |
31 *(out) = _out; \ | |
32 } while (0) | |
33 | |
34 #define IN_RANGE_OR_RETURN_FALSE(val, min, max) \ | |
xhwang
2015/01/12 18:08:57
We don't like to overuse macros. Can we just have
kcwu
2015/01/13 14:57:00
Done.
| |
35 do { \ | |
36 if ((val) < (min) || (val) > (max)) { \ | |
37 DVLOG(1) << "Error in stream: invalid value, expected " #val " to be" \ | |
38 << " in range [" << (min) << ":" << (max) << "]" \ | |
39 << " found " << (val) << " instead"; \ | |
40 return false; \ | |
41 } \ | |
42 } while (0) | |
43 | |
44 namespace media { | |
45 | |
46 namespace { | |
47 enum JpegMarker { | |
48 SOF0 = 0xC0, // start of frame (baseline) | |
49 DHT = 0xC4, // define huffman table | |
50 SOI = 0xD8, // start of image | |
51 SOS = 0xDA, // start of scan | |
52 DQT = 0xDB, // define quantization table | |
53 DRI = 0xDD, // define restart internal | |
54 MARKER1 = 0xFF, // jpeg marker prefix | |
55 }; | |
56 } // namespace | |
57 | |
58 bool JpegParser::Parse(const uint8_t* buffer, | |
59 size_t length, | |
60 JpegParseResult* result) { | |
61 DCHECK(buffer); | |
62 DCHECK(result); | |
63 BigEndianReader reader(reinterpret_cast<const char*>(buffer), length); | |
64 memset(result, 0, sizeof(JpegParseResult)); | |
65 | |
66 uint8_t marker1, marker2; | |
67 READ_U8_OR_RETURN_FALSE(&marker1); | |
68 READ_U8_OR_RETURN_FALSE(&marker2); | |
69 if (marker1 != MARKER1 || marker2 != SOI) { | |
70 DLOG(ERROR) << "Not a JPEG"; | |
71 return false; | |
72 } | |
73 | |
74 return ParseSOI(reader.ptr(), reader.remaining(), result); | |
75 } | |
76 | |
xhwang
2015/01/12 18:08:57
Can you move the definition of ParseSOI() closer t
kcwu
2015/01/13 14:57:00
Done.
| |
77 bool JpegParser::ParseSOF(const char* buffer, | |
78 size_t length, | |
79 JpegParseResult* result) { | |
xhwang
2015/01/12 18:08:58
Here and below, can we make the output of these fu
kcwu
2015/01/13 14:57:00
Done.
| |
80 // Spec B.2.2 Frame header syntax | |
81 DCHECK(buffer); | |
82 DCHECK(result); | |
83 BigEndianReader reader(buffer, length); | |
84 | |
85 uint8_t precision; | |
86 READ_U8_OR_RETURN_FALSE(&precision); | |
87 READ_U16_OR_RETURN_FALSE(&result->visible_height); | |
88 READ_U16_OR_RETURN_FALSE(&result->visible_width); | |
89 READ_U8_OR_RETURN_FALSE(&result->num_components); | |
90 | |
91 if (precision != 8) { | |
92 DLOG(ERROR) << "Only support 8-bit precision, not " | |
93 << static_cast<int>(precision) << " bit for baseline"; | |
94 return false; | |
95 } | |
96 if (result->num_components >= arraysize(result->components)) { | |
97 DLOG(ERROR) << "num_components=" << static_cast<int>(result->num_components) | |
98 << " is not supported"; | |
99 return false; | |
100 } | |
101 | |
102 for (size_t i = 0; i < result->num_components; i++) { | |
103 JpegComponent& component = result->components[i]; | |
104 READ_U8_OR_RETURN_FALSE(&component.id); | |
105 if (component.id > result->num_components) { | |
106 DLOG(ERROR) << "component id (" << static_cast<int>(component.id) | |
107 << ") should be <= num_components (" | |
108 << static_cast<int>(result->num_components) << ")"; | |
109 return false; | |
110 } | |
111 uint8_t hv; | |
112 READ_U8_OR_RETURN_FALSE(&hv); | |
113 component.horizontal_sampling_factor = hv / 16; | |
114 component.vertical_sampling_factor = hv % 16; | |
115 IN_RANGE_OR_RETURN_FALSE(component.horizontal_sampling_factor, 1, 4); | |
116 IN_RANGE_OR_RETURN_FALSE(component.vertical_sampling_factor, 1, 4); | |
117 READ_U8_OR_RETURN_FALSE(&component.quantization_table_selector); | |
118 } | |
119 | |
120 return true; | |
121 } | |
122 | |
123 bool JpegParser::ParseDQT(const char* buffer, | |
124 size_t length, | |
125 JpegParseResult* result) { | |
126 // Spec B.2.4.1 Quantization table-specification syntax | |
127 DCHECK(buffer); | |
128 DCHECK(result); | |
129 BigEndianReader reader(buffer, length); | |
130 while (reader.remaining() > 0) { | |
131 uint8_t precision_and_table_id; | |
132 READ_U8_OR_RETURN_FALSE(&precision_and_table_id); | |
133 uint8_t precision = precision_and_table_id / 16; | |
134 uint8_t table_id = precision_and_table_id % 16; | |
135 IN_RANGE_OR_RETURN_FALSE(precision, 0, 1); | |
136 if (precision == 1) { // 1 means 16-bit precision | |
137 DLOG(ERROR) << "An 8-bit DCT-based process shall not use a 16-bit " | |
138 << "precision quantization table"; | |
139 return false; | |
140 } | |
141 if (table_id >= kJpegMaxQuantizationTableNum) { | |
142 DLOG(ERROR) << "Quantization table id (" << static_cast<int>(table_id) | |
143 << ") exceeded " << kJpegMaxQuantizationTableNum; | |
144 return false; | |
145 } | |
146 | |
147 if (!reader.ReadBytes(&result->q_table[table_id].value, | |
148 sizeof(result->q_table[table_id].value))) | |
149 return false; | |
150 result->q_table[table_id].valid = true; | |
151 } | |
152 return true; | |
153 } | |
154 | |
155 bool JpegParser::ParseDHT(const char* buffer, | |
156 size_t length, | |
157 JpegParseResult* result) { | |
158 // Spec B.2.4.2 Huffman table-specification syntax | |
159 DCHECK(buffer); | |
160 DCHECK(result); | |
161 BigEndianReader reader(buffer, length); | |
162 while (reader.remaining() > 0) { | |
163 uint8_t table_class_and_id; | |
164 READ_U8_OR_RETURN_FALSE(&table_class_and_id); | |
165 int table_class = table_class_and_id / 16; | |
166 int table_id = table_class_and_id % 16; | |
167 IN_RANGE_OR_RETURN_FALSE(table_class, 0, 1); | |
168 if (table_id >= 2) { | |
169 DLOG(ERROR) << "Table id(" << table_id | |
170 << ") >= 2 is invalid for baseline profile"; | |
171 return false; | |
172 } | |
173 | |
174 JpegHuffmanTable* table; | |
175 if (table_class == 1) | |
176 table = &result->ac_table[table_id]; | |
177 else | |
178 table = &result->dc_table[table_id]; | |
179 | |
180 size_t count = 0; | |
181 if (!reader.ReadBytes(&table->code_length, sizeof(table->code_length))) | |
182 return false; | |
183 for (size_t i = 0; i < arraysize(table->code_length); i++) | |
184 count += table->code_length[i]; | |
185 | |
186 IN_RANGE_OR_RETURN_FALSE(count, 0, sizeof(table->code_value)); | |
187 if (!reader.ReadBytes(&table->code_value, count)) | |
188 return false; | |
189 table->valid = true; | |
190 } | |
191 return true; | |
192 } | |
193 | |
194 bool JpegParser::ParseDRI(const char* buffer, | |
195 size_t length, | |
196 JpegParseResult* result) { | |
197 // Spec B.2.4.4 Restart interval definition syntax | |
198 DCHECK(buffer); | |
199 DCHECK(result); | |
200 BigEndianReader reader(buffer, length); | |
201 return reader.ReadU16(&result->restart_interval) && reader.remaining() == 0; | |
202 } | |
203 | |
204 bool JpegParser::ParseSOS(const char* buffer, | |
205 size_t length, | |
206 JpegParseResult* result) { | |
207 // Spec B.2.3 Scan header syntax | |
208 DCHECK(buffer); | |
209 DCHECK(result); | |
210 BigEndianReader reader(buffer, length); | |
211 READ_U8_OR_RETURN_FALSE(&result->scan.num_components); | |
212 if (result->scan.num_components != result->num_components) { | |
213 DLOG(ERROR) << "The number of scan components (" | |
214 << static_cast<int>(result->scan.num_components) | |
215 << ") mismatches the number of image components (" | |
216 << static_cast<int>(result->num_components) << ")"; | |
217 return false; | |
218 } | |
219 | |
220 for (int i = 0; i < result->scan.num_components; i++) { | |
221 JpegScan::Component* component = &result->scan.components[i]; | |
222 READ_U8_OR_RETURN_FALSE(&component->component_selector); | |
223 uint8_t dc_and_ac_selector; | |
224 READ_U8_OR_RETURN_FALSE(&dc_and_ac_selector); | |
225 component->dc_selector = dc_and_ac_selector / 16; | |
226 component->ac_selector = dc_and_ac_selector % 16; | |
227 if (component->component_selector != result->components[i].id) { | |
228 DLOG(ERROR) << "component selector mismatches image component id"; | |
229 return false; | |
230 } | |
231 if (component->dc_selector >= kJpegMaxHuffmanTableNum_baseline) { | |
232 DLOG(ERROR) << "DC selector (" << static_cast<int>(component->dc_selector) | |
233 << ") should be 0 or 1 for baseline mode"; | |
234 return false; | |
235 } | |
236 if (component->ac_selector >= kJpegMaxHuffmanTableNum_baseline) { | |
237 DLOG(ERROR) << "AC selector (" << static_cast<int>(component->ac_selector) | |
238 << ") should be 0 or 1 for baseline mode"; | |
239 return false; | |
240 } | |
241 } | |
242 | |
243 // Unused fields, only for value checking. | |
244 uint8_t spectral_selection_start; | |
245 uint8_t spectral_selection_end; | |
246 uint8_t point_transform; | |
247 READ_U8_OR_RETURN_FALSE(&spectral_selection_start); | |
248 READ_U8_OR_RETURN_FALSE(&spectral_selection_end); | |
249 READ_U8_OR_RETURN_FALSE(&point_transform); | |
250 if (spectral_selection_start != 0 || spectral_selection_end != 63) { | |
251 DLOG(ERROR) << "Spectral selection should be 0,63 for baseline mode"; | |
252 return false; | |
253 } | |
254 if (point_transform != 0) { | |
255 DLOG(ERROR) << "Point transform should be 0 for baseline mode"; | |
256 return false; | |
257 } | |
258 | |
259 return true; | |
260 } | |
261 | |
262 bool JpegParser::ParseSOI(const char* buffer, | |
263 size_t length, | |
264 JpegParseResult* result) { | |
265 // Spec B.2.1 High-level syntax | |
266 DCHECK(buffer); | |
267 DCHECK(result); | |
268 BigEndianReader reader(buffer, length); | |
269 uint8_t marker1; | |
270 uint8_t marker2; | |
271 bool has_marker_dqt = false; | |
272 bool has_marker_sos = false; | |
273 | |
274 // Once reached SOS, all neccesary data are parsed. | |
275 while (!has_marker_sos) { | |
276 READ_U8_OR_RETURN_FALSE(&marker1); | |
277 if (marker1 != MARKER1) | |
278 return false; | |
279 | |
280 do { | |
281 READ_U8_OR_RETURN_FALSE(&marker2); | |
282 } while (marker2 == MARKER1); // skip fill bytes | |
283 | |
284 uint16_t size; | |
285 READ_U16_OR_RETURN_FALSE(&size); | |
286 if (reader.remaining() < size) { | |
287 DLOG(ERROR) << "Ill-formed JPEG. Remaining size (" << reader.remaining() | |
288 << ") is smaller than header specified (" << size << ")"; | |
289 return false; | |
290 } | |
291 | |
292 // The size includes the size field itself. | |
293 if (size < sizeof(size)) { | |
294 DLOG(ERROR) << "Ill-formed JPEG. Segment size (" << size | |
295 << ") is smaller than size field (" << sizeof(size) << ")"; | |
296 return false; | |
297 } | |
298 size -= sizeof(size); | |
299 | |
300 switch (marker2) { | |
301 case SOF0: | |
302 if (!ParseSOF(reader.ptr(), size, result)) { | |
303 DLOG(ERROR) << "ParseSOF failed"; | |
304 return false; | |
305 } | |
306 break; | |
307 case DQT: | |
308 if (!ParseDQT(reader.ptr(), size, result)) { | |
309 DLOG(ERROR) << "ParseDQT failed"; | |
310 return false; | |
311 } | |
312 has_marker_dqt = true; | |
313 break; | |
314 case DHT: | |
315 if (!ParseDHT(reader.ptr(), size, result)) { | |
316 DLOG(ERROR) << "ParseDHT failed"; | |
317 return false; | |
318 } | |
319 break; | |
320 case DRI: | |
321 if (!ParseDRI(reader.ptr(), size, result)) { | |
322 DLOG(ERROR) << "ParseDRI failed"; | |
323 return false; | |
324 } | |
325 break; | |
326 case SOS: | |
327 if (!ParseSOS(reader.ptr(), size, result)) { | |
328 DLOG(ERROR) << "ParseSOS failed"; | |
329 return false; | |
330 } | |
331 has_marker_sos = true; | |
332 break; | |
333 default: | |
334 DVLOG(4) << "unknown marker " << static_cast<int>(marker2); | |
335 break; | |
336 } | |
337 reader.Skip(size); | |
338 } | |
339 | |
340 if (!has_marker_dqt) { | |
341 DLOG(ERROR) << "No DQT marker found"; | |
342 return false; | |
343 } | |
344 | |
345 // Scan data follows scan header immediately. | |
346 result->scan.data = reader.ptr(); | |
347 result->scan.data_size = reader.remaining(); | |
348 | |
349 return true; | |
350 } | |
351 | |
352 } // namespace media | |
OLD | NEW |