Supervised user whitelists: Cleanup
[chromium-blink-merge.git] / media / filters / jpeg_parser.cc
blobecf8f174e2a000f217174ac0bff453b9f449a297
1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "media/filters/jpeg_parser.h"
7 #include "base/big_endian.h"
8 #include "base/logging.h"
10 using base::BigEndianReader;
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)
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)
34 namespace media {
36 namespace {
37 enum JpegMarker {
38 SOF0 = 0xC0, // start of frame (baseline)
39 DHT = 0xC4, // define huffman table
40 SOI = 0xD8, // start of image
41 SOS = 0xDA, // start of scan
42 DQT = 0xDB, // define quantization table
43 DRI = 0xDD, // define restart internal
44 MARKER1 = 0xFF, // jpeg marker prefix
48 static bool InRange(int value, int a, int b) {
49 return a <= value && value <= b;
52 // Round up |value| to multiple of |mul|. |value| must be non-negative.
53 // |mul| must be positive.
54 static int RoundUp(int value, int mul) {
55 DCHECK_GE(value, 0);
56 DCHECK_GE(mul, 1);
57 return (value + mul - 1) / mul * mul;
60 // |frame_header| is already initialized to 0 in ParseJpegPicture.
61 static bool ParseSOF(const char* buffer,
62 size_t length,
63 JpegFrameHeader* frame_header) {
64 // Spec B.2.2 Frame header syntax
65 DCHECK(buffer);
66 DCHECK(frame_header);
67 BigEndianReader reader(buffer, length);
69 uint8_t precision;
70 READ_U8_OR_RETURN_FALSE(&precision);
71 READ_U16_OR_RETURN_FALSE(&frame_header->visible_height);
72 READ_U16_OR_RETURN_FALSE(&frame_header->visible_width);
73 READ_U8_OR_RETURN_FALSE(&frame_header->num_components);
75 if (precision != 8) {
76 DLOG(ERROR) << "Only support 8-bit precision, not "
77 << static_cast<int>(precision) << " bit for baseline";
78 return false;
80 if (!InRange(frame_header->num_components, 1,
81 arraysize(frame_header->components))) {
82 DLOG(ERROR) << "num_components="
83 << static_cast<int>(frame_header->num_components)
84 << " is not supported";
85 return false;
88 int max_h_factor = 0;
89 int max_v_factor = 0;
90 for (size_t i = 0; i < frame_header->num_components; i++) {
91 JpegComponent& component = frame_header->components[i];
92 READ_U8_OR_RETURN_FALSE(&component.id);
93 if (component.id > frame_header->num_components) {
94 DLOG(ERROR) << "component id (" << static_cast<int>(component.id)
95 << ") should be <= num_components ("
96 << static_cast<int>(frame_header->num_components) << ")";
97 return false;
99 uint8_t hv;
100 READ_U8_OR_RETURN_FALSE(&hv);
101 component.horizontal_sampling_factor = hv / 16;
102 component.vertical_sampling_factor = hv % 16;
103 if (component.horizontal_sampling_factor > max_h_factor)
104 max_h_factor = component.horizontal_sampling_factor;
105 if (component.vertical_sampling_factor > max_v_factor)
106 max_v_factor = component.vertical_sampling_factor;
107 if (!InRange(component.horizontal_sampling_factor, 1, 4)) {
108 DVLOG(1) << "Invalid horizontal sampling factor "
109 << static_cast<int>(component.horizontal_sampling_factor);
110 return false;
112 if (!InRange(component.vertical_sampling_factor, 1, 4)) {
113 DVLOG(1) << "Invalid vertical sampling factor "
114 << static_cast<int>(component.horizontal_sampling_factor);
115 return false;
117 READ_U8_OR_RETURN_FALSE(&component.quantization_table_selector);
120 // The size of data unit is 8*8 and the coded size should be extended
121 // to complete minimum coded unit, MCU. See Spec A.2.
122 frame_header->coded_width =
123 RoundUp(frame_header->visible_width, max_h_factor * 8);
124 frame_header->coded_height =
125 RoundUp(frame_header->visible_height, max_v_factor * 8);
127 return true;
130 // |q_table| is already initialized to 0 in ParseJpegPicture.
131 static bool ParseDQT(const char* buffer,
132 size_t length,
133 JpegQuantizationTable* q_table) {
134 // Spec B.2.4.1 Quantization table-specification syntax
135 DCHECK(buffer);
136 DCHECK(q_table);
137 BigEndianReader reader(buffer, length);
138 while (reader.remaining() > 0) {
139 uint8_t precision_and_table_id;
140 READ_U8_OR_RETURN_FALSE(&precision_and_table_id);
141 uint8_t precision = precision_and_table_id / 16;
142 uint8_t table_id = precision_and_table_id % 16;
143 if (!InRange(precision, 0, 1)) {
144 DVLOG(1) << "Invalid precision " << static_cast<int>(precision);
145 return false;
147 if (precision == 1) { // 1 means 16-bit precision
148 DLOG(ERROR) << "An 8-bit DCT-based process shall not use a 16-bit "
149 << "precision quantization table";
150 return false;
152 if (table_id >= kJpegMaxQuantizationTableNum) {
153 DLOG(ERROR) << "Quantization table id (" << static_cast<int>(table_id)
154 << ") exceeded " << kJpegMaxQuantizationTableNum;
155 return false;
158 if (!reader.ReadBytes(&q_table[table_id].value,
159 sizeof(q_table[table_id].value)))
160 return false;
161 q_table[table_id].valid = true;
163 return true;
166 // |dc_table| and |ac_table| are already initialized to 0 in ParseJpegPicture.
167 static bool ParseDHT(const char* buffer,
168 size_t length,
169 JpegHuffmanTable* dc_table,
170 JpegHuffmanTable* ac_table) {
171 // Spec B.2.4.2 Huffman table-specification syntax
172 DCHECK(buffer);
173 DCHECK(dc_table);
174 DCHECK(ac_table);
175 BigEndianReader reader(buffer, length);
176 while (reader.remaining() > 0) {
177 uint8_t table_class_and_id;
178 READ_U8_OR_RETURN_FALSE(&table_class_and_id);
179 int table_class = table_class_and_id / 16;
180 int table_id = table_class_and_id % 16;
181 if (!InRange(table_class, 0, 1)) {
182 DVLOG(1) << "Invalid table class " << table_class;
183 return false;
185 if (table_id >= 2) {
186 DLOG(ERROR) << "Table id(" << table_id
187 << ") >= 2 is invalid for baseline profile";
188 return false;
191 JpegHuffmanTable* table;
192 if (table_class == 1)
193 table = &ac_table[table_id];
194 else
195 table = &dc_table[table_id];
197 size_t count = 0;
198 if (!reader.ReadBytes(&table->code_length, sizeof(table->code_length)))
199 return false;
200 for (size_t i = 0; i < arraysize(table->code_length); i++)
201 count += table->code_length[i];
203 if (!InRange(count, 0, sizeof(table->code_value))) {
204 DVLOG(1) << "Invalid code count " << count;
205 return false;
207 if (!reader.ReadBytes(&table->code_value, count))
208 return false;
209 table->valid = true;
211 return true;
214 static bool ParseDRI(const char* buffer,
215 size_t length,
216 uint16_t* restart_interval) {
217 // Spec B.2.4.4 Restart interval definition syntax
218 DCHECK(buffer);
219 DCHECK(restart_interval);
220 BigEndianReader reader(buffer, length);
221 return reader.ReadU16(restart_interval) && reader.remaining() == 0;
224 // |scan| is already initialized to 0 in ParseJpegPicture.
225 static bool ParseSOS(const char* buffer,
226 size_t length,
227 const JpegFrameHeader& frame_header,
228 JpegScanHeader* scan) {
229 // Spec B.2.3 Scan header syntax
230 DCHECK(buffer);
231 DCHECK(scan);
232 BigEndianReader reader(buffer, length);
233 READ_U8_OR_RETURN_FALSE(&scan->num_components);
234 if (scan->num_components != frame_header.num_components) {
235 DLOG(ERROR) << "The number of scan components ("
236 << static_cast<int>(scan->num_components)
237 << ") mismatches the number of image components ("
238 << static_cast<int>(frame_header.num_components) << ")";
239 return false;
242 for (int i = 0; i < scan->num_components; i++) {
243 JpegScanHeader::Component* component = &scan->components[i];
244 READ_U8_OR_RETURN_FALSE(&component->component_selector);
245 uint8_t dc_and_ac_selector;
246 READ_U8_OR_RETURN_FALSE(&dc_and_ac_selector);
247 component->dc_selector = dc_and_ac_selector / 16;
248 component->ac_selector = dc_and_ac_selector % 16;
249 if (component->component_selector != frame_header.components[i].id) {
250 DLOG(ERROR) << "component selector mismatches image component id";
251 return false;
253 if (component->dc_selector >= kJpegMaxHuffmanTableNumBaseline) {
254 DLOG(ERROR) << "DC selector (" << static_cast<int>(component->dc_selector)
255 << ") should be 0 or 1 for baseline mode";
256 return false;
258 if (component->ac_selector >= kJpegMaxHuffmanTableNumBaseline) {
259 DLOG(ERROR) << "AC selector (" << static_cast<int>(component->ac_selector)
260 << ") should be 0 or 1 for baseline mode";
261 return false;
265 // Unused fields, only for value checking.
266 uint8_t spectral_selection_start;
267 uint8_t spectral_selection_end;
268 uint8_t point_transform;
269 READ_U8_OR_RETURN_FALSE(&spectral_selection_start);
270 READ_U8_OR_RETURN_FALSE(&spectral_selection_end);
271 READ_U8_OR_RETURN_FALSE(&point_transform);
272 if (spectral_selection_start != 0 || spectral_selection_end != 63) {
273 DLOG(ERROR) << "Spectral selection should be 0,63 for baseline mode";
274 return false;
276 if (point_transform != 0) {
277 DLOG(ERROR) << "Point transform should be 0 for baseline mode";
278 return false;
281 return true;
284 // |result| is already initialized to 0 in ParseJpegPicture.
285 static bool ParseSOI(const char* buffer,
286 size_t length,
287 JpegParseResult* result) {
288 // Spec B.2.1 High-level syntax
289 DCHECK(buffer);
290 DCHECK(result);
291 BigEndianReader reader(buffer, length);
292 uint8_t marker1;
293 uint8_t marker2;
294 bool has_marker_dqt = false;
295 bool has_marker_sos = false;
297 // Once reached SOS, all neccesary data are parsed.
298 while (!has_marker_sos) {
299 READ_U8_OR_RETURN_FALSE(&marker1);
300 if (marker1 != MARKER1)
301 return false;
303 do {
304 READ_U8_OR_RETURN_FALSE(&marker2);
305 } while (marker2 == MARKER1); // skip fill bytes
307 uint16_t size;
308 READ_U16_OR_RETURN_FALSE(&size);
309 if (reader.remaining() < size) {
310 DLOG(ERROR) << "Ill-formed JPEG. Remaining size (" << reader.remaining()
311 << ") is smaller than header specified (" << size << ")";
312 return false;
315 // The size includes the size field itself.
316 if (size < sizeof(size)) {
317 DLOG(ERROR) << "Ill-formed JPEG. Segment size (" << size
318 << ") is smaller than size field (" << sizeof(size) << ")";
319 return false;
321 size -= sizeof(size);
323 switch (marker2) {
324 case SOF0:
325 if (!ParseSOF(reader.ptr(), size, &result->frame_header)) {
326 DLOG(ERROR) << "ParseSOF failed";
327 return false;
329 break;
330 case DQT:
331 if (!ParseDQT(reader.ptr(), size, result->q_table)) {
332 DLOG(ERROR) << "ParseDQT failed";
333 return false;
335 has_marker_dqt = true;
336 break;
337 case DHT:
338 if (!ParseDHT(reader.ptr(), size, result->dc_table, result->ac_table)) {
339 DLOG(ERROR) << "ParseDHT failed";
340 return false;
342 break;
343 case DRI:
344 if (!ParseDRI(reader.ptr(), size, &result->restart_interval)) {
345 DLOG(ERROR) << "ParseDRI failed";
346 return false;
348 break;
349 case SOS:
350 if (!ParseSOS(reader.ptr(), size, result->frame_header,
351 &result->scan)) {
352 DLOG(ERROR) << "ParseSOS failed";
353 return false;
355 has_marker_sos = true;
356 break;
357 default:
358 DVLOG(4) << "unknown marker " << static_cast<int>(marker2);
359 break;
361 reader.Skip(size);
364 if (!has_marker_dqt) {
365 DLOG(ERROR) << "No DQT marker found";
366 return false;
369 // Scan data follows scan header immediately.
370 result->data = reader.ptr();
371 result->data_size = reader.remaining();
373 return true;
376 bool ParseJpegPicture(const uint8_t* buffer,
377 size_t length,
378 JpegParseResult* result) {
379 DCHECK(buffer);
380 DCHECK(result);
381 BigEndianReader reader(reinterpret_cast<const char*>(buffer), length);
382 memset(result, 0, sizeof(JpegParseResult));
384 uint8_t marker1, marker2;
385 READ_U8_OR_RETURN_FALSE(&marker1);
386 READ_U8_OR_RETURN_FALSE(&marker2);
387 if (marker1 != MARKER1 || marker2 != SOI) {
388 DLOG(ERROR) << "Not a JPEG";
389 return false;
392 return ParseSOI(reader.ptr(), reader.remaining(), result);
395 } // namespace media