Re-subimission of https://codereview.chromium.org/1041213003/
[chromium-blink-merge.git] / media / filters / jpeg_parser.cc
blob66a3651210dc1add056c0b11dd71aea572b0f3ea
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 // |frame_header| is already initialized to 0 in ParseJpegPicture.
53 static bool ParseSOF(const char* buffer,
54 size_t length,
55 JpegFrameHeader* frame_header) {
56 // Spec B.2.2 Frame header syntax
57 DCHECK(buffer);
58 DCHECK(frame_header);
59 BigEndianReader reader(buffer, length);
61 uint8_t precision;
62 READ_U8_OR_RETURN_FALSE(&precision);
63 READ_U16_OR_RETURN_FALSE(&frame_header->visible_height);
64 READ_U16_OR_RETURN_FALSE(&frame_header->visible_width);
65 READ_U8_OR_RETURN_FALSE(&frame_header->num_components);
67 if (precision != 8) {
68 DLOG(ERROR) << "Only support 8-bit precision, not "
69 << static_cast<int>(precision) << " bit for baseline";
70 return false;
72 if (frame_header->num_components >= arraysize(frame_header->components)) {
73 DLOG(ERROR) << "num_components="
74 << static_cast<int>(frame_header->num_components)
75 << " is not supported";
76 return false;
79 for (size_t i = 0; i < frame_header->num_components; i++) {
80 JpegComponent& component = frame_header->components[i];
81 READ_U8_OR_RETURN_FALSE(&component.id);
82 if (component.id > frame_header->num_components) {
83 DLOG(ERROR) << "component id (" << static_cast<int>(component.id)
84 << ") should be <= num_components ("
85 << static_cast<int>(frame_header->num_components) << ")";
86 return false;
88 uint8_t hv;
89 READ_U8_OR_RETURN_FALSE(&hv);
90 component.horizontal_sampling_factor = hv / 16;
91 component.vertical_sampling_factor = hv % 16;
92 if (!InRange(component.horizontal_sampling_factor, 1, 4)) {
93 DVLOG(1) << "Invalid horizontal sampling factor "
94 << static_cast<int>(component.horizontal_sampling_factor);
95 return false;
97 if (!InRange(component.vertical_sampling_factor, 1, 4)) {
98 DVLOG(1) << "Invalid vertical sampling factor "
99 << static_cast<int>(component.horizontal_sampling_factor);
100 return false;
102 READ_U8_OR_RETURN_FALSE(&component.quantization_table_selector);
105 return true;
108 // |q_table| is already initialized to 0 in ParseJpegPicture.
109 static bool ParseDQT(const char* buffer,
110 size_t length,
111 JpegQuantizationTable* q_table) {
112 // Spec B.2.4.1 Quantization table-specification syntax
113 DCHECK(buffer);
114 DCHECK(q_table);
115 BigEndianReader reader(buffer, length);
116 while (reader.remaining() > 0) {
117 uint8_t precision_and_table_id;
118 READ_U8_OR_RETURN_FALSE(&precision_and_table_id);
119 uint8_t precision = precision_and_table_id / 16;
120 uint8_t table_id = precision_and_table_id % 16;
121 if (!InRange(precision, 0, 1)) {
122 DVLOG(1) << "Invalid precision " << static_cast<int>(precision);
123 return false;
125 if (precision == 1) { // 1 means 16-bit precision
126 DLOG(ERROR) << "An 8-bit DCT-based process shall not use a 16-bit "
127 << "precision quantization table";
128 return false;
130 if (table_id >= kJpegMaxQuantizationTableNum) {
131 DLOG(ERROR) << "Quantization table id (" << static_cast<int>(table_id)
132 << ") exceeded " << kJpegMaxQuantizationTableNum;
133 return false;
136 if (!reader.ReadBytes(&q_table[table_id].value,
137 sizeof(q_table[table_id].value)))
138 return false;
139 q_table[table_id].valid = true;
141 return true;
144 // |dc_table| and |ac_table| are already initialized to 0 in ParseJpegPicture.
145 static bool ParseDHT(const char* buffer,
146 size_t length,
147 JpegHuffmanTable* dc_table,
148 JpegHuffmanTable* ac_table) {
149 // Spec B.2.4.2 Huffman table-specification syntax
150 DCHECK(buffer);
151 DCHECK(dc_table);
152 DCHECK(ac_table);
153 BigEndianReader reader(buffer, length);
154 while (reader.remaining() > 0) {
155 uint8_t table_class_and_id;
156 READ_U8_OR_RETURN_FALSE(&table_class_and_id);
157 int table_class = table_class_and_id / 16;
158 int table_id = table_class_and_id % 16;
159 if (!InRange(table_class, 0, 1)) {
160 DVLOG(1) << "Invalid table class " << table_class;
161 return false;
163 if (table_id >= 2) {
164 DLOG(ERROR) << "Table id(" << table_id
165 << ") >= 2 is invalid for baseline profile";
166 return false;
169 JpegHuffmanTable* table;
170 if (table_class == 1)
171 table = &ac_table[table_id];
172 else
173 table = &dc_table[table_id];
175 size_t count = 0;
176 if (!reader.ReadBytes(&table->code_length, sizeof(table->code_length)))
177 return false;
178 for (size_t i = 0; i < arraysize(table->code_length); i++)
179 count += table->code_length[i];
181 if (!InRange(count, 0, sizeof(table->code_value))) {
182 DVLOG(1) << "Invalid code count " << count;
183 return false;
185 if (!reader.ReadBytes(&table->code_value, count))
186 return false;
187 table->valid = true;
189 return true;
192 static bool ParseDRI(const char* buffer,
193 size_t length,
194 uint16_t* restart_interval) {
195 // Spec B.2.4.4 Restart interval definition syntax
196 DCHECK(buffer);
197 DCHECK(restart_interval);
198 BigEndianReader reader(buffer, length);
199 return reader.ReadU16(restart_interval) && reader.remaining() == 0;
202 // |scan| is already initialized to 0 in ParseJpegPicture.
203 static bool ParseSOS(const char* buffer,
204 size_t length,
205 const JpegFrameHeader& frame_header,
206 JpegScanHeader* scan) {
207 // Spec B.2.3 Scan header syntax
208 DCHECK(buffer);
209 DCHECK(scan);
210 BigEndianReader reader(buffer, length);
211 READ_U8_OR_RETURN_FALSE(&scan->num_components);
212 if (scan->num_components != frame_header.num_components) {
213 DLOG(ERROR) << "The number of scan components ("
214 << static_cast<int>(scan->num_components)
215 << ") mismatches the number of image components ("
216 << static_cast<int>(frame_header.num_components) << ")";
217 return false;
220 for (int i = 0; i < scan->num_components; i++) {
221 JpegScanHeader::Component* component = &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 != frame_header.components[i].id) {
228 DLOG(ERROR) << "component selector mismatches image component id";
229 return false;
231 if (component->dc_selector >= kJpegMaxHuffmanTableNumBaseline) {
232 DLOG(ERROR) << "DC selector (" << static_cast<int>(component->dc_selector)
233 << ") should be 0 or 1 for baseline mode";
234 return false;
236 if (component->ac_selector >= kJpegMaxHuffmanTableNumBaseline) {
237 DLOG(ERROR) << "AC selector (" << static_cast<int>(component->ac_selector)
238 << ") should be 0 or 1 for baseline mode";
239 return false;
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;
254 if (point_transform != 0) {
255 DLOG(ERROR) << "Point transform should be 0 for baseline mode";
256 return false;
259 return true;
262 // |result| is already initialized to 0 in ParseJpegPicture.
263 static bool ParseSOI(const char* buffer,
264 size_t length,
265 JpegParseResult* result) {
266 // Spec B.2.1 High-level syntax
267 DCHECK(buffer);
268 DCHECK(result);
269 BigEndianReader reader(buffer, length);
270 uint8_t marker1;
271 uint8_t marker2;
272 bool has_marker_dqt = false;
273 bool has_marker_sos = false;
275 // Once reached SOS, all neccesary data are parsed.
276 while (!has_marker_sos) {
277 READ_U8_OR_RETURN_FALSE(&marker1);
278 if (marker1 != MARKER1)
279 return false;
281 do {
282 READ_U8_OR_RETURN_FALSE(&marker2);
283 } while (marker2 == MARKER1); // skip fill bytes
285 uint16_t size;
286 READ_U16_OR_RETURN_FALSE(&size);
287 if (reader.remaining() < size) {
288 DLOG(ERROR) << "Ill-formed JPEG. Remaining size (" << reader.remaining()
289 << ") is smaller than header specified (" << size << ")";
290 return false;
293 // The size includes the size field itself.
294 if (size < sizeof(size)) {
295 DLOG(ERROR) << "Ill-formed JPEG. Segment size (" << size
296 << ") is smaller than size field (" << sizeof(size) << ")";
297 return false;
299 size -= sizeof(size);
301 switch (marker2) {
302 case SOF0:
303 if (!ParseSOF(reader.ptr(), size, &result->frame_header)) {
304 DLOG(ERROR) << "ParseSOF failed";
305 return false;
307 break;
308 case DQT:
309 if (!ParseDQT(reader.ptr(), size, result->q_table)) {
310 DLOG(ERROR) << "ParseDQT failed";
311 return false;
313 has_marker_dqt = true;
314 break;
315 case DHT:
316 if (!ParseDHT(reader.ptr(), size, result->dc_table, result->ac_table)) {
317 DLOG(ERROR) << "ParseDHT failed";
318 return false;
320 break;
321 case DRI:
322 if (!ParseDRI(reader.ptr(), size, &result->restart_interval)) {
323 DLOG(ERROR) << "ParseDRI failed";
324 return false;
326 break;
327 case SOS:
328 if (!ParseSOS(reader.ptr(), size, result->frame_header,
329 &result->scan)) {
330 DLOG(ERROR) << "ParseSOS failed";
331 return false;
333 has_marker_sos = true;
334 break;
335 default:
336 DVLOG(4) << "unknown marker " << static_cast<int>(marker2);
337 break;
339 reader.Skip(size);
342 if (!has_marker_dqt) {
343 DLOG(ERROR) << "No DQT marker found";
344 return false;
347 // Scan data follows scan header immediately.
348 result->data = reader.ptr();
349 result->data_size = reader.remaining();
351 return true;
354 bool ParseJpegPicture(const uint8_t* buffer,
355 size_t length,
356 JpegParseResult* result) {
357 DCHECK(buffer);
358 DCHECK(result);
359 BigEndianReader reader(reinterpret_cast<const char*>(buffer), length);
360 memset(result, 0, sizeof(JpegParseResult));
362 uint8_t marker1, marker2;
363 READ_U8_OR_RETURN_FALSE(&marker1);
364 READ_U8_OR_RETURN_FALSE(&marker2);
365 if (marker1 != MARKER1 || marker2 != SOI) {
366 DLOG(ERROR) << "Not a JPEG";
367 return false;
370 return ParseSOI(reader.ptr(), reader.remaining(), result);
373 } // namespace media