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

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: rebase 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
« no previous file with comments | « media/filters/jpeg_parser.h ('k') | media/filters/jpeg_parser_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 // |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);
60
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);
66
67 if (precision != 8) {
68 DLOG(ERROR) << "Only support 8-bit precision, not "
69 << static_cast<int>(precision) << " bit for baseline";
70 return false;
71 }
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;
77 }
78
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;
87 }
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;
96 }
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;
101 }
102 READ_U8_OR_RETURN_FALSE(&component.quantization_table_selector);
103 }
104
105 return true;
106 }
107
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;
124 }
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;
129 }
130 if (table_id >= kJpegMaxQuantizationTableNum) {
131 DLOG(ERROR) << "Quantization table id (" << static_cast<int>(table_id)
132 << ") exceeded " << kJpegMaxQuantizationTableNum;
133 return false;
134 }
135
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;
140 }
141 return true;
142 }
143
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;
162 }
163 if (table_id >= 2) {
164 DLOG(ERROR) << "Table id(" << table_id
165 << ") >= 2 is invalid for baseline profile";
166 return false;
167 }
168
169 JpegHuffmanTable* table;
170 if (table_class == 1)
171 table = &ac_table[table_id];
172 else
173 table = &dc_table[table_id];
174
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];
180
181 if (!InRange(count, 0, sizeof(table->code_value))) {
182 DVLOG(1) << "Invalid code count " << count;
183 return false;
184 }
185 if (!reader.ReadBytes(&table->code_value, count))
186 return false;
187 table->valid = true;
188 }
189 return true;
190 }
191
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;
200 }
201
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;
218 }
219
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;
230 }
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;
235 }
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;
240 }
241 }
242
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;
253 }
254 if (point_transform != 0) {
255 DLOG(ERROR) << "Point transform should be 0 for baseline mode";
256 return false;
257 }
258
259 return true;
260 }
261
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;
274
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;
280
281 do {
282 READ_U8_OR_RETURN_FALSE(&marker2);
283 } while (marker2 == MARKER1); // skip fill bytes
284
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;
291 }
292
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;
298 }
299 size -= sizeof(size);
300
301 switch (marker2) {
302 case SOF0:
303 if (!ParseSOF(reader.ptr(), size, &result->frame_header)) {
304 DLOG(ERROR) << "ParseSOF failed";
305 return false;
306 }
307 break;
308 case DQT:
309 if (!ParseDQT(reader.ptr(), size, result->q_table)) {
310 DLOG(ERROR) << "ParseDQT failed";
311 return false;
312 }
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;
319 }
320 break;
321 case DRI:
322 if (!ParseDRI(reader.ptr(), size, &result->restart_interval)) {
323 DLOG(ERROR) << "ParseDRI failed";
324 return false;
325 }
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;
332 }
333 has_marker_sos = true;
334 break;
335 default:
336 DVLOG(4) << "unknown marker " << static_cast<int>(marker2);
337 break;
338 }
339 reader.Skip(size);
340 }
341
342 if (!has_marker_dqt) {
343 DLOG(ERROR) << "No DQT marker found";
344 return false;
345 }
346
347 // Scan data follows scan header immediately.
348 result->data = reader.ptr();
349 result->data_size = reader.remaining();
350
351 return true;
352 }
353
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));
361
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;
368 }
369
370 return ParseSOI(reader.ptr(), reader.remaining(), result);
371 }
372
373 } // namespace media
OLDNEW
« no previous file with comments | « media/filters/jpeg_parser.h ('k') | media/filters/jpeg_parser_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698