Move UpdateManifest test from unit_tests into extensions_unittests.
[chromium-blink-merge.git] / media / filters / jpeg_parser.cc
blob7400dc183a68da5c815d043d9b3ed6b44597e3a4
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 SOF1 = 0xC1, // start of frame (extended sequential)
40 SOF2 = 0xC2, // start of frame (progressive)
41 SOF3 = 0xC3, // start of frame (lossless))
42 SOF5 = 0xC5, // start of frame (differential, sequential)
43 SOF6 = 0xC6, // start of frame (differential, progressive)
44 SOF7 = 0xC7, // start of frame (differential, lossless)
45 SOF9 = 0xC9, // start of frame (arithmetic coding, extended)
46 SOF10 = 0xCA, // start of frame (arithmetic coding, progressive)
47 SOF11 = 0xCB, // start of frame (arithmetic coding, lossless)
48 SOF13 = 0xCD, // start of frame (differential, arithmetic, sequential)
49 SOF14 = 0xCE, // start of frame (differential, arithmetic, progressive)
50 SOF15 = 0xCF, // start of frame (differential, arithmetic, lossless)
51 DHT = 0xC4, // define huffman table
52 SOI = 0xD8, // start of image
53 SOS = 0xDA, // start of scan
54 DQT = 0xDB, // define quantization table
55 DRI = 0xDD, // define restart internal
56 MARKER1 = 0xFF, // jpeg marker prefix
60 static bool InRange(int value, int a, int b) {
61 return a <= value && value <= b;
64 // Round up |value| to multiple of |mul|. |value| must be non-negative.
65 // |mul| must be positive.
66 static int RoundUp(int value, int mul) {
67 DCHECK_GE(value, 0);
68 DCHECK_GE(mul, 1);
69 return (value + mul - 1) / mul * mul;
72 // |frame_header| is already initialized to 0 in ParseJpegPicture.
73 static bool ParseSOF(const char* buffer,
74 size_t length,
75 JpegFrameHeader* frame_header) {
76 // Spec B.2.2 Frame header syntax
77 DCHECK(buffer);
78 DCHECK(frame_header);
79 BigEndianReader reader(buffer, length);
81 uint8_t precision;
82 READ_U8_OR_RETURN_FALSE(&precision);
83 READ_U16_OR_RETURN_FALSE(&frame_header->visible_height);
84 READ_U16_OR_RETURN_FALSE(&frame_header->visible_width);
85 READ_U8_OR_RETURN_FALSE(&frame_header->num_components);
87 if (precision != 8) {
88 DLOG(ERROR) << "Only support 8-bit precision, not "
89 << static_cast<int>(precision) << " bit for baseline";
90 return false;
92 if (!InRange(frame_header->num_components, 1,
93 arraysize(frame_header->components))) {
94 DLOG(ERROR) << "num_components="
95 << static_cast<int>(frame_header->num_components)
96 << " is not supported";
97 return false;
100 int max_h_factor = 0;
101 int max_v_factor = 0;
102 for (size_t i = 0; i < frame_header->num_components; i++) {
103 JpegComponent& component = frame_header->components[i];
104 READ_U8_OR_RETURN_FALSE(&component.id);
105 if (component.id > frame_header->num_components) {
106 DLOG(ERROR) << "component id (" << static_cast<int>(component.id)
107 << ") should be <= num_components ("
108 << static_cast<int>(frame_header->num_components) << ")";
109 return false;
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 if (component.horizontal_sampling_factor > max_h_factor)
116 max_h_factor = component.horizontal_sampling_factor;
117 if (component.vertical_sampling_factor > max_v_factor)
118 max_v_factor = component.vertical_sampling_factor;
119 if (!InRange(component.horizontal_sampling_factor, 1, 4)) {
120 DVLOG(1) << "Invalid horizontal sampling factor "
121 << static_cast<int>(component.horizontal_sampling_factor);
122 return false;
124 if (!InRange(component.vertical_sampling_factor, 1, 4)) {
125 DVLOG(1) << "Invalid vertical sampling factor "
126 << static_cast<int>(component.horizontal_sampling_factor);
127 return false;
129 READ_U8_OR_RETURN_FALSE(&component.quantization_table_selector);
132 // The size of data unit is 8*8 and the coded size should be extended
133 // to complete minimum coded unit, MCU. See Spec A.2.
134 frame_header->coded_width =
135 RoundUp(frame_header->visible_width, max_h_factor * 8);
136 frame_header->coded_height =
137 RoundUp(frame_header->visible_height, max_v_factor * 8);
139 return true;
142 // |q_table| is already initialized to 0 in ParseJpegPicture.
143 static bool ParseDQT(const char* buffer,
144 size_t length,
145 JpegQuantizationTable* q_table) {
146 // Spec B.2.4.1 Quantization table-specification syntax
147 DCHECK(buffer);
148 DCHECK(q_table);
149 BigEndianReader reader(buffer, length);
150 while (reader.remaining() > 0) {
151 uint8_t precision_and_table_id;
152 READ_U8_OR_RETURN_FALSE(&precision_and_table_id);
153 uint8_t precision = precision_and_table_id / 16;
154 uint8_t table_id = precision_and_table_id % 16;
155 if (!InRange(precision, 0, 1)) {
156 DVLOG(1) << "Invalid precision " << static_cast<int>(precision);
157 return false;
159 if (precision == 1) { // 1 means 16-bit precision
160 DLOG(ERROR) << "An 8-bit DCT-based process shall not use a 16-bit "
161 << "precision quantization table";
162 return false;
164 if (table_id >= kJpegMaxQuantizationTableNum) {
165 DLOG(ERROR) << "Quantization table id (" << static_cast<int>(table_id)
166 << ") exceeded " << kJpegMaxQuantizationTableNum;
167 return false;
170 if (!reader.ReadBytes(&q_table[table_id].value,
171 sizeof(q_table[table_id].value)))
172 return false;
173 q_table[table_id].valid = true;
175 return true;
178 // |dc_table| and |ac_table| are already initialized to 0 in ParseJpegPicture.
179 static bool ParseDHT(const char* buffer,
180 size_t length,
181 JpegHuffmanTable* dc_table,
182 JpegHuffmanTable* ac_table) {
183 // Spec B.2.4.2 Huffman table-specification syntax
184 DCHECK(buffer);
185 DCHECK(dc_table);
186 DCHECK(ac_table);
187 BigEndianReader reader(buffer, length);
188 while (reader.remaining() > 0) {
189 uint8_t table_class_and_id;
190 READ_U8_OR_RETURN_FALSE(&table_class_and_id);
191 int table_class = table_class_and_id / 16;
192 int table_id = table_class_and_id % 16;
193 if (!InRange(table_class, 0, 1)) {
194 DVLOG(1) << "Invalid table class " << table_class;
195 return false;
197 if (table_id >= 2) {
198 DLOG(ERROR) << "Table id(" << table_id
199 << ") >= 2 is invalid for baseline profile";
200 return false;
203 JpegHuffmanTable* table;
204 if (table_class == 1)
205 table = &ac_table[table_id];
206 else
207 table = &dc_table[table_id];
209 size_t count = 0;
210 if (!reader.ReadBytes(&table->code_length, sizeof(table->code_length)))
211 return false;
212 for (size_t i = 0; i < arraysize(table->code_length); i++)
213 count += table->code_length[i];
215 if (!InRange(count, 0, sizeof(table->code_value))) {
216 DVLOG(1) << "Invalid code count " << count;
217 return false;
219 if (!reader.ReadBytes(&table->code_value, count))
220 return false;
221 table->valid = true;
223 return true;
226 static bool ParseDRI(const char* buffer,
227 size_t length,
228 uint16_t* restart_interval) {
229 // Spec B.2.4.4 Restart interval definition syntax
230 DCHECK(buffer);
231 DCHECK(restart_interval);
232 BigEndianReader reader(buffer, length);
233 return reader.ReadU16(restart_interval) && reader.remaining() == 0;
236 // |scan| is already initialized to 0 in ParseJpegPicture.
237 static bool ParseSOS(const char* buffer,
238 size_t length,
239 const JpegFrameHeader& frame_header,
240 JpegScanHeader* scan) {
241 // Spec B.2.3 Scan header syntax
242 DCHECK(buffer);
243 DCHECK(scan);
244 BigEndianReader reader(buffer, length);
245 READ_U8_OR_RETURN_FALSE(&scan->num_components);
246 if (scan->num_components != frame_header.num_components) {
247 DLOG(ERROR) << "The number of scan components ("
248 << static_cast<int>(scan->num_components)
249 << ") mismatches the number of image components ("
250 << static_cast<int>(frame_header.num_components) << ")";
251 return false;
254 for (int i = 0; i < scan->num_components; i++) {
255 JpegScanHeader::Component* component = &scan->components[i];
256 READ_U8_OR_RETURN_FALSE(&component->component_selector);
257 uint8_t dc_and_ac_selector;
258 READ_U8_OR_RETURN_FALSE(&dc_and_ac_selector);
259 component->dc_selector = dc_and_ac_selector / 16;
260 component->ac_selector = dc_and_ac_selector % 16;
261 if (component->component_selector != frame_header.components[i].id) {
262 DLOG(ERROR) << "component selector mismatches image component id";
263 return false;
265 if (component->dc_selector >= kJpegMaxHuffmanTableNumBaseline) {
266 DLOG(ERROR) << "DC selector (" << static_cast<int>(component->dc_selector)
267 << ") should be 0 or 1 for baseline mode";
268 return false;
270 if (component->ac_selector >= kJpegMaxHuffmanTableNumBaseline) {
271 DLOG(ERROR) << "AC selector (" << static_cast<int>(component->ac_selector)
272 << ") should be 0 or 1 for baseline mode";
273 return false;
277 // Unused fields, only for value checking.
278 uint8_t spectral_selection_start;
279 uint8_t spectral_selection_end;
280 uint8_t point_transform;
281 READ_U8_OR_RETURN_FALSE(&spectral_selection_start);
282 READ_U8_OR_RETURN_FALSE(&spectral_selection_end);
283 READ_U8_OR_RETURN_FALSE(&point_transform);
284 if (spectral_selection_start != 0 || spectral_selection_end != 63) {
285 DLOG(ERROR) << "Spectral selection should be 0,63 for baseline mode";
286 return false;
288 if (point_transform != 0) {
289 DLOG(ERROR) << "Point transform should be 0 for baseline mode";
290 return false;
293 return true;
296 // |result| is already initialized to 0 in ParseJpegPicture.
297 static bool ParseSOI(const char* buffer,
298 size_t length,
299 JpegParseResult* result) {
300 // Spec B.2.1 High-level syntax
301 DCHECK(buffer);
302 DCHECK(result);
303 BigEndianReader reader(buffer, length);
304 uint8_t marker1;
305 uint8_t marker2;
306 bool has_marker_dqt = false;
307 bool has_marker_sos = false;
309 // Once reached SOS, all neccesary data are parsed.
310 while (!has_marker_sos) {
311 READ_U8_OR_RETURN_FALSE(&marker1);
312 if (marker1 != MARKER1)
313 return false;
315 do {
316 READ_U8_OR_RETURN_FALSE(&marker2);
317 } while (marker2 == MARKER1); // skip fill bytes
319 uint16_t size;
320 READ_U16_OR_RETURN_FALSE(&size);
321 if (reader.remaining() < size) {
322 DLOG(ERROR) << "Ill-formed JPEG. Remaining size (" << reader.remaining()
323 << ") is smaller than header specified (" << size << ")";
324 return false;
327 // The size includes the size field itself.
328 if (size < sizeof(size)) {
329 DLOG(ERROR) << "Ill-formed JPEG. Segment size (" << size
330 << ") is smaller than size field (" << sizeof(size) << ")";
331 return false;
333 size -= sizeof(size);
335 switch (marker2) {
336 case SOF0:
337 if (!ParseSOF(reader.ptr(), size, &result->frame_header)) {
338 DLOG(ERROR) << "ParseSOF failed";
339 return false;
341 break;
342 case SOF1:
343 case SOF2:
344 case SOF3:
345 case SOF5:
346 case SOF6:
347 case SOF7:
348 case SOF9:
349 case SOF10:
350 case SOF11:
351 case SOF13:
352 case SOF14:
353 case SOF15:
354 DLOG(ERROR) << "Only SOF0 (baseline) is supported, but got SOF"
355 << (marker2 - SOF0);
356 return false;
357 case DQT:
358 if (!ParseDQT(reader.ptr(), size, result->q_table)) {
359 DLOG(ERROR) << "ParseDQT failed";
360 return false;
362 has_marker_dqt = true;
363 break;
364 case DHT:
365 if (!ParseDHT(reader.ptr(), size, result->dc_table, result->ac_table)) {
366 DLOG(ERROR) << "ParseDHT failed";
367 return false;
369 break;
370 case DRI:
371 if (!ParseDRI(reader.ptr(), size, &result->restart_interval)) {
372 DLOG(ERROR) << "ParseDRI failed";
373 return false;
375 break;
376 case SOS:
377 if (!ParseSOS(reader.ptr(), size, result->frame_header,
378 &result->scan)) {
379 DLOG(ERROR) << "ParseSOS failed";
380 return false;
382 has_marker_sos = true;
383 break;
384 default:
385 DVLOG(4) << "unknown marker " << static_cast<int>(marker2);
386 break;
388 reader.Skip(size);
391 if (!has_marker_dqt) {
392 DLOG(ERROR) << "No DQT marker found";
393 return false;
396 // Scan data follows scan header immediately.
397 result->data = reader.ptr();
398 result->data_size = reader.remaining();
400 return true;
403 bool ParseJpegPicture(const uint8_t* buffer,
404 size_t length,
405 JpegParseResult* result) {
406 DCHECK(buffer);
407 DCHECK(result);
408 BigEndianReader reader(reinterpret_cast<const char*>(buffer), length);
409 memset(result, 0, sizeof(JpegParseResult));
411 uint8_t marker1, marker2;
412 READ_U8_OR_RETURN_FALSE(&marker1);
413 READ_U8_OR_RETURN_FALSE(&marker2);
414 if (marker1 != MARKER1 || marker2 != SOI) {
415 DLOG(ERROR) << "Not a JPEG";
416 return false;
419 return ParseSOI(reader.ptr(), reader.remaining(), result);
422 } // namespace media