[MD settings] moving attached() code
[chromium-blink-merge.git] / media / filters / jpeg_parser.cc
blobd46a7e306a477cbc297dce3fae1fb293a0f9d2cb
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 static bool InRange(int value, int a, int b) {
37 return a <= value && value <= b;
40 // Round up |value| to multiple of |mul|. |value| must be non-negative.
41 // |mul| must be positive.
42 static int RoundUp(int value, int mul) {
43 DCHECK_GE(value, 0);
44 DCHECK_GE(mul, 1);
45 return (value + mul - 1) / mul * mul;
48 // |frame_header| is already initialized to 0 in ParseJpegPicture.
49 static bool ParseSOF(const char* buffer,
50 size_t length,
51 JpegFrameHeader* frame_header) {
52 // Spec B.2.2 Frame header syntax
53 DCHECK(buffer);
54 DCHECK(frame_header);
55 BigEndianReader reader(buffer, length);
57 uint8_t precision;
58 READ_U8_OR_RETURN_FALSE(&precision);
59 READ_U16_OR_RETURN_FALSE(&frame_header->visible_height);
60 READ_U16_OR_RETURN_FALSE(&frame_header->visible_width);
61 READ_U8_OR_RETURN_FALSE(&frame_header->num_components);
63 if (precision != 8) {
64 DLOG(ERROR) << "Only support 8-bit precision, not "
65 << static_cast<int>(precision) << " bit for baseline";
66 return false;
68 if (!InRange(frame_header->num_components, 1,
69 arraysize(frame_header->components))) {
70 DLOG(ERROR) << "num_components="
71 << static_cast<int>(frame_header->num_components)
72 << " is not supported";
73 return false;
76 int max_h_factor = 0;
77 int max_v_factor = 0;
78 for (size_t i = 0; i < frame_header->num_components; i++) {
79 JpegComponent& component = frame_header->components[i];
80 READ_U8_OR_RETURN_FALSE(&component.id);
81 if (component.id > frame_header->num_components) {
82 DLOG(ERROR) << "component id (" << static_cast<int>(component.id)
83 << ") should be <= num_components ("
84 << static_cast<int>(frame_header->num_components) << ")";
85 return false;
87 uint8_t hv;
88 READ_U8_OR_RETURN_FALSE(&hv);
89 component.horizontal_sampling_factor = hv / 16;
90 component.vertical_sampling_factor = hv % 16;
91 if (component.horizontal_sampling_factor > max_h_factor)
92 max_h_factor = component.horizontal_sampling_factor;
93 if (component.vertical_sampling_factor > max_v_factor)
94 max_v_factor = component.vertical_sampling_factor;
95 if (!InRange(component.horizontal_sampling_factor, 1, 4)) {
96 DVLOG(1) << "Invalid horizontal sampling factor "
97 << static_cast<int>(component.horizontal_sampling_factor);
98 return false;
100 if (!InRange(component.vertical_sampling_factor, 1, 4)) {
101 DVLOG(1) << "Invalid vertical sampling factor "
102 << static_cast<int>(component.horizontal_sampling_factor);
103 return false;
105 READ_U8_OR_RETURN_FALSE(&component.quantization_table_selector);
108 // The size of data unit is 8*8 and the coded size should be extended
109 // to complete minimum coded unit, MCU. See Spec A.2.
110 frame_header->coded_width =
111 RoundUp(frame_header->visible_width, max_h_factor * 8);
112 frame_header->coded_height =
113 RoundUp(frame_header->visible_height, max_v_factor * 8);
115 return true;
118 // |q_table| is already initialized to 0 in ParseJpegPicture.
119 static bool ParseDQT(const char* buffer,
120 size_t length,
121 JpegQuantizationTable* q_table) {
122 // Spec B.2.4.1 Quantization table-specification syntax
123 DCHECK(buffer);
124 DCHECK(q_table);
125 BigEndianReader reader(buffer, length);
126 while (reader.remaining() > 0) {
127 uint8_t precision_and_table_id;
128 READ_U8_OR_RETURN_FALSE(&precision_and_table_id);
129 uint8_t precision = precision_and_table_id / 16;
130 uint8_t table_id = precision_and_table_id % 16;
131 if (!InRange(precision, 0, 1)) {
132 DVLOG(1) << "Invalid precision " << static_cast<int>(precision);
133 return false;
135 if (precision == 1) { // 1 means 16-bit precision
136 DLOG(ERROR) << "An 8-bit DCT-based process shall not use a 16-bit "
137 << "precision quantization table";
138 return false;
140 if (table_id >= kJpegMaxQuantizationTableNum) {
141 DLOG(ERROR) << "Quantization table id (" << static_cast<int>(table_id)
142 << ") exceeded " << kJpegMaxQuantizationTableNum;
143 return false;
146 if (!reader.ReadBytes(&q_table[table_id].value,
147 sizeof(q_table[table_id].value)))
148 return false;
149 q_table[table_id].valid = true;
151 return true;
154 // |dc_table| and |ac_table| are already initialized to 0 in ParseJpegPicture.
155 static bool ParseDHT(const char* buffer,
156 size_t length,
157 JpegHuffmanTable* dc_table,
158 JpegHuffmanTable* ac_table) {
159 // Spec B.2.4.2 Huffman table-specification syntax
160 DCHECK(buffer);
161 DCHECK(dc_table);
162 DCHECK(ac_table);
163 BigEndianReader reader(buffer, length);
164 while (reader.remaining() > 0) {
165 uint8_t table_class_and_id;
166 READ_U8_OR_RETURN_FALSE(&table_class_and_id);
167 int table_class = table_class_and_id / 16;
168 int table_id = table_class_and_id % 16;
169 if (!InRange(table_class, 0, 1)) {
170 DVLOG(1) << "Invalid table class " << table_class;
171 return false;
173 if (table_id >= 2) {
174 DLOG(ERROR) << "Table id(" << table_id
175 << ") >= 2 is invalid for baseline profile";
176 return false;
179 JpegHuffmanTable* table;
180 if (table_class == 1)
181 table = &ac_table[table_id];
182 else
183 table = &dc_table[table_id];
185 size_t count = 0;
186 if (!reader.ReadBytes(&table->code_length, sizeof(table->code_length)))
187 return false;
188 for (size_t i = 0; i < arraysize(table->code_length); i++)
189 count += table->code_length[i];
191 if (!InRange(count, 0, sizeof(table->code_value))) {
192 DVLOG(1) << "Invalid code count " << count;
193 return false;
195 if (!reader.ReadBytes(&table->code_value, count))
196 return false;
197 table->valid = true;
199 return true;
202 static bool ParseDRI(const char* buffer,
203 size_t length,
204 uint16_t* restart_interval) {
205 // Spec B.2.4.4 Restart interval definition syntax
206 DCHECK(buffer);
207 DCHECK(restart_interval);
208 BigEndianReader reader(buffer, length);
209 return reader.ReadU16(restart_interval) && reader.remaining() == 0;
212 // |scan| is already initialized to 0 in ParseJpegPicture.
213 static bool ParseSOS(const char* buffer,
214 size_t length,
215 const JpegFrameHeader& frame_header,
216 JpegScanHeader* scan) {
217 // Spec B.2.3 Scan header syntax
218 DCHECK(buffer);
219 DCHECK(scan);
220 BigEndianReader reader(buffer, length);
221 READ_U8_OR_RETURN_FALSE(&scan->num_components);
222 if (scan->num_components != frame_header.num_components) {
223 DLOG(ERROR) << "The number of scan components ("
224 << static_cast<int>(scan->num_components)
225 << ") mismatches the number of image components ("
226 << static_cast<int>(frame_header.num_components) << ")";
227 return false;
230 for (int i = 0; i < scan->num_components; i++) {
231 JpegScanHeader::Component* component = &scan->components[i];
232 READ_U8_OR_RETURN_FALSE(&component->component_selector);
233 uint8_t dc_and_ac_selector;
234 READ_U8_OR_RETURN_FALSE(&dc_and_ac_selector);
235 component->dc_selector = dc_and_ac_selector / 16;
236 component->ac_selector = dc_and_ac_selector % 16;
237 if (component->component_selector != frame_header.components[i].id) {
238 DLOG(ERROR) << "component selector mismatches image component id";
239 return false;
241 if (component->dc_selector >= kJpegMaxHuffmanTableNumBaseline) {
242 DLOG(ERROR) << "DC selector (" << static_cast<int>(component->dc_selector)
243 << ") should be 0 or 1 for baseline mode";
244 return false;
246 if (component->ac_selector >= kJpegMaxHuffmanTableNumBaseline) {
247 DLOG(ERROR) << "AC selector (" << static_cast<int>(component->ac_selector)
248 << ") should be 0 or 1 for baseline mode";
249 return false;
253 // Unused fields, only for value checking.
254 uint8_t spectral_selection_start;
255 uint8_t spectral_selection_end;
256 uint8_t point_transform;
257 READ_U8_OR_RETURN_FALSE(&spectral_selection_start);
258 READ_U8_OR_RETURN_FALSE(&spectral_selection_end);
259 READ_U8_OR_RETURN_FALSE(&point_transform);
260 if (spectral_selection_start != 0 || spectral_selection_end != 63) {
261 DLOG(ERROR) << "Spectral selection should be 0,63 for baseline mode";
262 return false;
264 if (point_transform != 0) {
265 DLOG(ERROR) << "Point transform should be 0 for baseline mode";
266 return false;
269 return true;
272 // |eoi_ptr| will point to the end of image (after EOI marker) after search
273 // succeeds. Returns true on EOI marker found, or false.
274 static bool SearchEOI(const char* buffer, size_t length, const char** eoi_ptr) {
275 DCHECK(buffer);
276 DCHECK(eoi_ptr);
277 BigEndianReader reader(buffer, length);
278 uint8_t marker2;
280 while (reader.remaining() > 0) {
281 const char* marker1_ptr = static_cast<const char*>(
282 memchr(reader.ptr(), JPEG_MARKER_PREFIX, reader.remaining()));
283 if (!marker1_ptr)
284 return false;
285 reader.Skip(marker1_ptr - reader.ptr() + 1);
287 do {
288 READ_U8_OR_RETURN_FALSE(&marker2);
289 } while (marker2 == JPEG_MARKER_PREFIX); // skip fill bytes
291 switch (marker2) {
292 // Compressed data escape.
293 case 0x00:
294 break;
295 // Restart
296 case JPEG_RST0:
297 case JPEG_RST1:
298 case JPEG_RST2:
299 case JPEG_RST3:
300 case JPEG_RST4:
301 case JPEG_RST5:
302 case JPEG_RST6:
303 case JPEG_RST7:
304 break;
305 case JPEG_EOI:
306 *eoi_ptr = reader.ptr();
307 return true;
308 default:
309 // Skip for other markers.
310 uint16_t size;
311 READ_U16_OR_RETURN_FALSE(&size);
312 if (size < sizeof(size)) {
313 DLOG(ERROR) << "Ill-formed JPEG. Segment size (" << size
314 << ") is smaller than size field (" << sizeof(size)
315 << ")";
316 return false;
318 size -= sizeof(size);
320 if (!reader.Skip(size)) {
321 DLOG(ERROR) << "Ill-formed JPEG. Remaining size ("
322 << reader.remaining()
323 << ") is smaller than header specified (" << size << ")";
324 return false;
326 break;
329 return false;
332 // |result| is already initialized to 0 in ParseJpegPicture.
333 static bool ParseSOI(const char* buffer,
334 size_t length,
335 JpegParseResult* result) {
336 // Spec B.2.1 High-level syntax
337 DCHECK(buffer);
338 DCHECK(result);
339 BigEndianReader reader(buffer, length);
340 uint8_t marker1;
341 uint8_t marker2;
342 bool has_marker_dqt = false;
343 bool has_marker_sos = false;
345 // Once reached SOS, all neccesary data are parsed.
346 while (!has_marker_sos) {
347 READ_U8_OR_RETURN_FALSE(&marker1);
348 if (marker1 != JPEG_MARKER_PREFIX)
349 return false;
351 do {
352 READ_U8_OR_RETURN_FALSE(&marker2);
353 } while (marker2 == JPEG_MARKER_PREFIX); // skip fill bytes
355 uint16_t size;
356 READ_U16_OR_RETURN_FALSE(&size);
357 // The size includes the size field itself.
358 if (size < sizeof(size)) {
359 DLOG(ERROR) << "Ill-formed JPEG. Segment size (" << size
360 << ") is smaller than size field (" << sizeof(size) << ")";
361 return false;
363 size -= sizeof(size);
365 if (reader.remaining() < size) {
366 DLOG(ERROR) << "Ill-formed JPEG. Remaining size (" << reader.remaining()
367 << ") is smaller than header specified (" << size << ")";
368 return false;
371 switch (marker2) {
372 case JPEG_SOF0:
373 if (!ParseSOF(reader.ptr(), size, &result->frame_header)) {
374 DLOG(ERROR) << "ParseSOF failed";
375 return false;
377 break;
378 case JPEG_SOF1:
379 case JPEG_SOF2:
380 case JPEG_SOF3:
381 case JPEG_SOF5:
382 case JPEG_SOF6:
383 case JPEG_SOF7:
384 case JPEG_SOF9:
385 case JPEG_SOF10:
386 case JPEG_SOF11:
387 case JPEG_SOF13:
388 case JPEG_SOF14:
389 case JPEG_SOF15:
390 DLOG(ERROR) << "Only SOF0 (baseline) is supported, but got SOF"
391 << (marker2 - JPEG_SOF0);
392 return false;
393 case JPEG_DQT:
394 if (!ParseDQT(reader.ptr(), size, result->q_table)) {
395 DLOG(ERROR) << "ParseDQT failed";
396 return false;
398 has_marker_dqt = true;
399 break;
400 case JPEG_DHT:
401 if (!ParseDHT(reader.ptr(), size, result->dc_table, result->ac_table)) {
402 DLOG(ERROR) << "ParseDHT failed";
403 return false;
405 break;
406 case JPEG_DRI:
407 if (!ParseDRI(reader.ptr(), size, &result->restart_interval)) {
408 DLOG(ERROR) << "ParseDRI failed";
409 return false;
411 break;
412 case JPEG_SOS:
413 if (!ParseSOS(reader.ptr(), size, result->frame_header,
414 &result->scan)) {
415 DLOG(ERROR) << "ParseSOS failed";
416 return false;
418 has_marker_sos = true;
419 break;
420 default:
421 DVLOG(4) << "unknown marker " << static_cast<int>(marker2);
422 break;
424 reader.Skip(size);
427 if (!has_marker_dqt) {
428 DLOG(ERROR) << "No DQT marker found";
429 return false;
432 // Scan data follows scan header immediately.
433 result->data = reader.ptr();
434 result->data_size = reader.remaining();
435 const size_t kSoiSize = 2;
436 result->image_size = length + kSoiSize;
438 return true;
441 bool ParseJpegPicture(const uint8_t* buffer,
442 size_t length,
443 JpegParseResult* result) {
444 DCHECK(buffer);
445 DCHECK(result);
446 BigEndianReader reader(reinterpret_cast<const char*>(buffer), length);
447 memset(result, 0, sizeof(JpegParseResult));
449 uint8_t marker1, marker2;
450 READ_U8_OR_RETURN_FALSE(&marker1);
451 READ_U8_OR_RETURN_FALSE(&marker2);
452 if (marker1 != JPEG_MARKER_PREFIX || marker2 != JPEG_SOI) {
453 DLOG(ERROR) << "Not a JPEG";
454 return false;
457 return ParseSOI(reader.ptr(), reader.remaining(), result);
460 bool ParseJpegStream(const uint8_t* buffer,
461 size_t length,
462 JpegParseResult* result) {
463 DCHECK(buffer);
464 DCHECK(result);
465 if (!ParseJpegPicture(buffer, length, result))
466 return false;
468 BigEndianReader reader(
469 reinterpret_cast<const char*>(result->data), result->data_size);
470 const char* eoi_ptr = nullptr;
471 if (!SearchEOI(reader.ptr(), reader.remaining(), &eoi_ptr)) {
472 DLOG(ERROR) << "SearchEOI failed";
473 return false;
475 DCHECK(eoi_ptr);
476 result->data_size = eoi_ptr - result->data;
477 result->image_size = eoi_ptr - reinterpret_cast<const char*>(buffer);
478 return true;
481 } // namespace media