Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1047)

Side by Side Diff: media/filters/jpeg_parser.cc

Issue 748023002: Add JPEG parser for JPEG decode acceleration (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: addressed xhwang's comments Created 5 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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.
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 namespace media {
35
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
45 };
46 }
47
48 static bool InRange(int value, int a, int b) {
49 return a <= value && value <= b;
50 }
51
52 static bool ParseSOF(const char* buffer,
53 size_t length,
54 JpegFrameHeader* frame_header) {
xhwang 2015/01/13 17:07:14 Have a comment somewhere in this file about how th
kcwu 2015/01/14 06:22:46 Done.
55 // Spec B.2.2 Frame header syntax
56 DCHECK(buffer);
57 DCHECK(frame_header);
58 BigEndianReader reader(buffer, length);
59
60 uint8_t precision;
61 READ_U8_OR_RETURN_FALSE(&precision);
62 READ_U16_OR_RETURN_FALSE(&frame_header->visible_height);
63 READ_U16_OR_RETURN_FALSE(&frame_header->visible_width);
64 READ_U8_OR_RETURN_FALSE(&frame_header->num_components);
65
66 if (precision != 8) {
67 DLOG(ERROR) << "Only support 8-bit precision, not "
68 << static_cast<int>(precision) << " bit for baseline";
69 return false;
70 }
71 if (frame_header->num_components >= arraysize(frame_header->components)) {
72 DLOG(ERROR) << "num_components="
73 << static_cast<int>(frame_header->num_components)
74 << " is not supported";
75 return false;
76 }
77
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;
86 }
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 (!InRange(component.horizontal_sampling_factor, 1, 4)) {
92 DVLOG(1) << "Invalid horizontal sampling factor "
93 << static_cast<int>(component.horizontal_sampling_factor);
94 return false;
95 }
96 if (!InRange(component.vertical_sampling_factor, 1, 4)) {
97 DVLOG(1) << "Invalid vertical sampling factor "
98 << static_cast<int>(component.horizontal_sampling_factor);
99 return false;
100 }
101 READ_U8_OR_RETURN_FALSE(&component.quantization_table_selector);
102 }
103
104 return true;
105 }
106
107 static bool ParseDQT(const char* buffer,
108 size_t length,
109 JpegQuantizationTable* q_table) {
110 // Spec B.2.4.1 Quantization table-specification syntax
111 DCHECK(buffer);
112 DCHECK(q_table);
113 BigEndianReader reader(buffer, length);
114 while (reader.remaining() > 0) {
115 uint8_t precision_and_table_id;
116 READ_U8_OR_RETURN_FALSE(&precision_and_table_id);
117 uint8_t precision = precision_and_table_id / 16;
118 uint8_t table_id = precision_and_table_id % 16;
119 if (!InRange(precision, 0, 1)) {
120 DVLOG(1) << "Invalid precision " << static_cast<int>(precision);
121 return false;
122 }
123 if (precision == 1) { // 1 means 16-bit precision
124 DLOG(ERROR) << "An 8-bit DCT-based process shall not use a 16-bit "
125 << "precision quantization table";
126 return false;
127 }
128 if (table_id >= kJpegMaxQuantizationTableNum) {
129 DLOG(ERROR) << "Quantization table id (" << static_cast<int>(table_id)
130 << ") exceeded " << kJpegMaxQuantizationTableNum;
131 return false;
132 }
133
134 if (!reader.ReadBytes(&q_table[table_id].value,
135 sizeof(q_table[table_id].value)))
136 return false;
137 q_table[table_id].valid = true;
138 }
139 return true;
140 }
141
142 static bool ParseDHT(const char* buffer,
143 size_t length,
144 JpegHuffmanTable* dc_table,
145 JpegHuffmanTable* ac_table) {
146 // Spec B.2.4.2 Huffman table-specification syntax
147 DCHECK(buffer);
148 DCHECK(dc_table);
149 DCHECK(ac_table);
150 BigEndianReader reader(buffer, length);
151 while (reader.remaining() > 0) {
152 uint8_t table_class_and_id;
153 READ_U8_OR_RETURN_FALSE(&table_class_and_id);
154 int table_class = table_class_and_id / 16;
155 int table_id = table_class_and_id % 16;
156 if (!InRange(table_class, 0, 1)) {
157 DVLOG(1) << "Invalid table class " << table_class;
158 return false;
159 }
160 if (table_id >= 2) {
161 DLOG(ERROR) << "Table id(" << table_id
162 << ") >= 2 is invalid for baseline profile";
163 return false;
164 }
165
166 JpegHuffmanTable* table;
167 if (table_class == 1)
168 table = &ac_table[table_id];
169 else
170 table = &dc_table[table_id];
171
172 size_t count = 0;
173 if (!reader.ReadBytes(&table->code_length, sizeof(table->code_length)))
174 return false;
175 for (size_t i = 0; i < arraysize(table->code_length); i++)
176 count += table->code_length[i];
177
178 if (!InRange(count, 0, sizeof(table->code_value))) {
179 DVLOG(1) << "Invalid code count " << count;
180 return false;
181 }
182 if (!reader.ReadBytes(&table->code_value, count))
183 return false;
184 table->valid = true;
185 }
186 return true;
187 }
188
189 static bool ParseDRI(const char* buffer,
190 size_t length,
191 uint16_t* restart_interval) {
192 // Spec B.2.4.4 Restart interval definition syntax
193 DCHECK(buffer);
194 DCHECK(restart_interval);
195 BigEndianReader reader(buffer, length);
196 return reader.ReadU16(restart_interval) && reader.remaining() == 0;
197 }
198
199 static bool ParseSOS(const char* buffer,
200 size_t length,
201 const JpegFrameHeader& frame_header,
202 JpegScanHeader* scan) {
203 // Spec B.2.3 Scan header syntax
204 DCHECK(buffer);
205 DCHECK(scan);
206 BigEndianReader reader(buffer, length);
207 READ_U8_OR_RETURN_FALSE(&scan->num_components);
208 if (scan->num_components != frame_header.num_components) {
209 DLOG(ERROR) << "The number of scan components ("
210 << static_cast<int>(scan->num_components)
211 << ") mismatches the number of image components ("
212 << static_cast<int>(frame_header.num_components) << ")";
213 return false;
214 }
215
216 for (int i = 0; i < scan->num_components; i++) {
217 JpegScanHeader::Component* component = &scan->components[i];
218 READ_U8_OR_RETURN_FALSE(&component->component_selector);
219 uint8_t dc_and_ac_selector;
220 READ_U8_OR_RETURN_FALSE(&dc_and_ac_selector);
221 component->dc_selector = dc_and_ac_selector / 16;
222 component->ac_selector = dc_and_ac_selector % 16;
223 if (component->component_selector != frame_header.components[i].id) {
224 DLOG(ERROR) << "component selector mismatches image component id";
225 return false;
226 }
227 if (component->dc_selector >= kJpegMaxHuffmanTableNumBaseline) {
228 DLOG(ERROR) << "DC selector (" << static_cast<int>(component->dc_selector)
229 << ") should be 0 or 1 for baseline mode";
230 return false;
231 }
232 if (component->ac_selector >= kJpegMaxHuffmanTableNumBaseline) {
233 DLOG(ERROR) << "AC selector (" << static_cast<int>(component->ac_selector)
234 << ") should be 0 or 1 for baseline mode";
235 return false;
236 }
237 }
238
239 // Unused fields, only for value checking.
240 uint8_t spectral_selection_start;
241 uint8_t spectral_selection_end;
242 uint8_t point_transform;
243 READ_U8_OR_RETURN_FALSE(&spectral_selection_start);
244 READ_U8_OR_RETURN_FALSE(&spectral_selection_end);
245 READ_U8_OR_RETURN_FALSE(&point_transform);
246 if (spectral_selection_start != 0 || spectral_selection_end != 63) {
247 DLOG(ERROR) << "Spectral selection should be 0,63 for baseline mode";
248 return false;
249 }
250 if (point_transform != 0) {
251 DLOG(ERROR) << "Point transform should be 0 for baseline mode";
252 return false;
253 }
254
255 return true;
256 }
257
258 static bool ParseSOI(const char* buffer,
259 size_t length,
260 JpegParseResult* result) {
261 // Spec B.2.1 High-level syntax
262 DCHECK(buffer);
263 DCHECK(result);
264 BigEndianReader reader(buffer, length);
265 uint8_t marker1;
266 uint8_t marker2;
267 bool has_marker_dqt = false;
268 bool has_marker_sos = false;
269
270 // Once reached SOS, all neccesary data are parsed.
271 while (!has_marker_sos) {
272 READ_U8_OR_RETURN_FALSE(&marker1);
273 if (marker1 != MARKER1)
274 return false;
275
276 do {
277 READ_U8_OR_RETURN_FALSE(&marker2);
278 } while (marker2 == MARKER1); // skip fill bytes
279
280 uint16_t size;
281 READ_U16_OR_RETURN_FALSE(&size);
282 if (reader.remaining() < size) {
283 DLOG(ERROR) << "Ill-formed JPEG. Remaining size (" << reader.remaining()
284 << ") is smaller than header specified (" << size << ")";
285 return false;
286 }
287
288 // The size includes the size field itself.
289 if (size < sizeof(size)) {
290 DLOG(ERROR) << "Ill-formed JPEG. Segment size (" << size
291 << ") is smaller than size field (" << sizeof(size) << ")";
292 return false;
293 }
294 size -= sizeof(size);
295
296 switch (marker2) {
297 case SOF0:
298 if (!ParseSOF(reader.ptr(), size, &result->frame_header)) {
299 DLOG(ERROR) << "ParseSOF failed";
300 return false;
301 }
302 break;
303 case DQT:
304 if (!ParseDQT(reader.ptr(), size, result->q_table)) {
305 DLOG(ERROR) << "ParseDQT failed";
306 return false;
307 }
308 has_marker_dqt = true;
309 break;
310 case DHT:
311 if (!ParseDHT(reader.ptr(), size, result->dc_table, result->ac_table)) {
312 DLOG(ERROR) << "ParseDHT failed";
313 return false;
314 }
315 break;
316 case DRI:
317 if (!ParseDRI(reader.ptr(), size, &result->restart_interval)) {
318 DLOG(ERROR) << "ParseDRI failed";
319 return false;
320 }
321 break;
322 case SOS:
323 if (!ParseSOS(reader.ptr(), size, result->frame_header,
324 &result->scan)) {
325 DLOG(ERROR) << "ParseSOS failed";
326 return false;
327 }
328 has_marker_sos = true;
329 break;
330 default:
331 DVLOG(4) << "unknown marker " << static_cast<int>(marker2);
332 break;
333 }
334 reader.Skip(size);
335 }
336
337 if (!has_marker_dqt) {
338 DLOG(ERROR) << "No DQT marker found";
339 return false;
340 }
341
342 // Scan data follows scan header immediately.
343 result->data = reader.ptr();
344 result->data_size = reader.remaining();
345
346 return true;
347 }
348
349 bool ParseJpegPicture(const uint8_t* buffer,
350 size_t length,
351 JpegParseResult* result) {
352 DCHECK(buffer);
353 DCHECK(result);
354 BigEndianReader reader(reinterpret_cast<const char*>(buffer), length);
355 memset(result, 0, sizeof(JpegParseResult));
356
357 uint8_t marker1, marker2;
358 READ_U8_OR_RETURN_FALSE(&marker1);
359 READ_U8_OR_RETURN_FALSE(&marker2);
360 if (marker1 != MARKER1 || marker2 != SOI) {
361 DLOG(ERROR) << "Not a JPEG";
362 return false;
363 }
364
365 return ParseSOI(reader.ptr(), reader.remaining(), result);
366 }
367
368 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698