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

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: 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 2014 The Chromium Authors. All rights reserved.
xhwang 2015/01/12 18:08:57 2015
kcwu 2015/01/13 14:57:00 Done.
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 #define IN_RANGE_OR_RETURN_FALSE(val, min, max) \
xhwang 2015/01/12 18:08:57 We don't like to overuse macros. Can we just have
kcwu 2015/01/13 14:57:00 Done.
35 do { \
36 if ((val) < (min) || (val) > (max)) { \
37 DVLOG(1) << "Error in stream: invalid value, expected " #val " to be" \
38 << " in range [" << (min) << ":" << (max) << "]" \
39 << " found " << (val) << " instead"; \
40 return false; \
41 } \
42 } while (0)
43
44 namespace media {
45
46 namespace {
47 enum JpegMarker {
48 SOF0 = 0xC0, // start of frame (baseline)
49 DHT = 0xC4, // define huffman table
50 SOI = 0xD8, // start of image
51 SOS = 0xDA, // start of scan
52 DQT = 0xDB, // define quantization table
53 DRI = 0xDD, // define restart internal
54 MARKER1 = 0xFF, // jpeg marker prefix
55 };
56 } // namespace
57
58 bool JpegParser::Parse(const uint8_t* buffer,
59 size_t length,
60 JpegParseResult* result) {
61 DCHECK(buffer);
62 DCHECK(result);
63 BigEndianReader reader(reinterpret_cast<const char*>(buffer), length);
64 memset(result, 0, sizeof(JpegParseResult));
65
66 uint8_t marker1, marker2;
67 READ_U8_OR_RETURN_FALSE(&marker1);
68 READ_U8_OR_RETURN_FALSE(&marker2);
69 if (marker1 != MARKER1 || marker2 != SOI) {
70 DLOG(ERROR) << "Not a JPEG";
71 return false;
72 }
73
74 return ParseSOI(reader.ptr(), reader.remaining(), result);
75 }
76
xhwang 2015/01/12 18:08:57 Can you move the definition of ParseSOI() closer t
kcwu 2015/01/13 14:57:00 Done.
77 bool JpegParser::ParseSOF(const char* buffer,
78 size_t length,
79 JpegParseResult* result) {
xhwang 2015/01/12 18:08:58 Here and below, can we make the output of these fu
kcwu 2015/01/13 14:57:00 Done.
80 // Spec B.2.2 Frame header syntax
81 DCHECK(buffer);
82 DCHECK(result);
83 BigEndianReader reader(buffer, length);
84
85 uint8_t precision;
86 READ_U8_OR_RETURN_FALSE(&precision);
87 READ_U16_OR_RETURN_FALSE(&result->visible_height);
88 READ_U16_OR_RETURN_FALSE(&result->visible_width);
89 READ_U8_OR_RETURN_FALSE(&result->num_components);
90
91 if (precision != 8) {
92 DLOG(ERROR) << "Only support 8-bit precision, not "
93 << static_cast<int>(precision) << " bit for baseline";
94 return false;
95 }
96 if (result->num_components >= arraysize(result->components)) {
97 DLOG(ERROR) << "num_components=" << static_cast<int>(result->num_components)
98 << " is not supported";
99 return false;
100 }
101
102 for (size_t i = 0; i < result->num_components; i++) {
103 JpegComponent& component = result->components[i];
104 READ_U8_OR_RETURN_FALSE(&component.id);
105 if (component.id > result->num_components) {
106 DLOG(ERROR) << "component id (" << static_cast<int>(component.id)
107 << ") should be <= num_components ("
108 << static_cast<int>(result->num_components) << ")";
109 return false;
110 }
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 IN_RANGE_OR_RETURN_FALSE(component.horizontal_sampling_factor, 1, 4);
116 IN_RANGE_OR_RETURN_FALSE(component.vertical_sampling_factor, 1, 4);
117 READ_U8_OR_RETURN_FALSE(&component.quantization_table_selector);
118 }
119
120 return true;
121 }
122
123 bool JpegParser::ParseDQT(const char* buffer,
124 size_t length,
125 JpegParseResult* result) {
126 // Spec B.2.4.1 Quantization table-specification syntax
127 DCHECK(buffer);
128 DCHECK(result);
129 BigEndianReader reader(buffer, length);
130 while (reader.remaining() > 0) {
131 uint8_t precision_and_table_id;
132 READ_U8_OR_RETURN_FALSE(&precision_and_table_id);
133 uint8_t precision = precision_and_table_id / 16;
134 uint8_t table_id = precision_and_table_id % 16;
135 IN_RANGE_OR_RETURN_FALSE(precision, 0, 1);
136 if (precision == 1) { // 1 means 16-bit precision
137 DLOG(ERROR) << "An 8-bit DCT-based process shall not use a 16-bit "
138 << "precision quantization table";
139 return false;
140 }
141 if (table_id >= kJpegMaxQuantizationTableNum) {
142 DLOG(ERROR) << "Quantization table id (" << static_cast<int>(table_id)
143 << ") exceeded " << kJpegMaxQuantizationTableNum;
144 return false;
145 }
146
147 if (!reader.ReadBytes(&result->q_table[table_id].value,
148 sizeof(result->q_table[table_id].value)))
149 return false;
150 result->q_table[table_id].valid = true;
151 }
152 return true;
153 }
154
155 bool JpegParser::ParseDHT(const char* buffer,
156 size_t length,
157 JpegParseResult* result) {
158 // Spec B.2.4.2 Huffman table-specification syntax
159 DCHECK(buffer);
160 DCHECK(result);
161 BigEndianReader reader(buffer, length);
162 while (reader.remaining() > 0) {
163 uint8_t table_class_and_id;
164 READ_U8_OR_RETURN_FALSE(&table_class_and_id);
165 int table_class = table_class_and_id / 16;
166 int table_id = table_class_and_id % 16;
167 IN_RANGE_OR_RETURN_FALSE(table_class, 0, 1);
168 if (table_id >= 2) {
169 DLOG(ERROR) << "Table id(" << table_id
170 << ") >= 2 is invalid for baseline profile";
171 return false;
172 }
173
174 JpegHuffmanTable* table;
175 if (table_class == 1)
176 table = &result->ac_table[table_id];
177 else
178 table = &result->dc_table[table_id];
179
180 size_t count = 0;
181 if (!reader.ReadBytes(&table->code_length, sizeof(table->code_length)))
182 return false;
183 for (size_t i = 0; i < arraysize(table->code_length); i++)
184 count += table->code_length[i];
185
186 IN_RANGE_OR_RETURN_FALSE(count, 0, sizeof(table->code_value));
187 if (!reader.ReadBytes(&table->code_value, count))
188 return false;
189 table->valid = true;
190 }
191 return true;
192 }
193
194 bool JpegParser::ParseDRI(const char* buffer,
195 size_t length,
196 JpegParseResult* result) {
197 // Spec B.2.4.4 Restart interval definition syntax
198 DCHECK(buffer);
199 DCHECK(result);
200 BigEndianReader reader(buffer, length);
201 return reader.ReadU16(&result->restart_interval) && reader.remaining() == 0;
202 }
203
204 bool JpegParser::ParseSOS(const char* buffer,
205 size_t length,
206 JpegParseResult* result) {
207 // Spec B.2.3 Scan header syntax
208 DCHECK(buffer);
209 DCHECK(result);
210 BigEndianReader reader(buffer, length);
211 READ_U8_OR_RETURN_FALSE(&result->scan.num_components);
212 if (result->scan.num_components != result->num_components) {
213 DLOG(ERROR) << "The number of scan components ("
214 << static_cast<int>(result->scan.num_components)
215 << ") mismatches the number of image components ("
216 << static_cast<int>(result->num_components) << ")";
217 return false;
218 }
219
220 for (int i = 0; i < result->scan.num_components; i++) {
221 JpegScan::Component* component = &result->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 != result->components[i].id) {
228 DLOG(ERROR) << "component selector mismatches image component id";
229 return false;
230 }
231 if (component->dc_selector >= kJpegMaxHuffmanTableNum_baseline) {
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 >= kJpegMaxHuffmanTableNum_baseline) {
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 bool JpegParser::ParseSOI(const char* buffer,
263 size_t length,
264 JpegParseResult* result) {
265 // Spec B.2.1 High-level syntax
266 DCHECK(buffer);
267 DCHECK(result);
268 BigEndianReader reader(buffer, length);
269 uint8_t marker1;
270 uint8_t marker2;
271 bool has_marker_dqt = false;
272 bool has_marker_sos = false;
273
274 // Once reached SOS, all neccesary data are parsed.
275 while (!has_marker_sos) {
276 READ_U8_OR_RETURN_FALSE(&marker1);
277 if (marker1 != MARKER1)
278 return false;
279
280 do {
281 READ_U8_OR_RETURN_FALSE(&marker2);
282 } while (marker2 == MARKER1); // skip fill bytes
283
284 uint16_t size;
285 READ_U16_OR_RETURN_FALSE(&size);
286 if (reader.remaining() < size) {
287 DLOG(ERROR) << "Ill-formed JPEG. Remaining size (" << reader.remaining()
288 << ") is smaller than header specified (" << size << ")";
289 return false;
290 }
291
292 // The size includes the size field itself.
293 if (size < sizeof(size)) {
294 DLOG(ERROR) << "Ill-formed JPEG. Segment size (" << size
295 << ") is smaller than size field (" << sizeof(size) << ")";
296 return false;
297 }
298 size -= sizeof(size);
299
300 switch (marker2) {
301 case SOF0:
302 if (!ParseSOF(reader.ptr(), size, result)) {
303 DLOG(ERROR) << "ParseSOF failed";
304 return false;
305 }
306 break;
307 case DQT:
308 if (!ParseDQT(reader.ptr(), size, result)) {
309 DLOG(ERROR) << "ParseDQT failed";
310 return false;
311 }
312 has_marker_dqt = true;
313 break;
314 case DHT:
315 if (!ParseDHT(reader.ptr(), size, result)) {
316 DLOG(ERROR) << "ParseDHT failed";
317 return false;
318 }
319 break;
320 case DRI:
321 if (!ParseDRI(reader.ptr(), size, result)) {
322 DLOG(ERROR) << "ParseDRI failed";
323 return false;
324 }
325 break;
326 case SOS:
327 if (!ParseSOS(reader.ptr(), size, result)) {
328 DLOG(ERROR) << "ParseSOS failed";
329 return false;
330 }
331 has_marker_sos = true;
332 break;
333 default:
334 DVLOG(4) << "unknown marker " << static_cast<int>(marker2);
335 break;
336 }
337 reader.Skip(size);
338 }
339
340 if (!has_marker_dqt) {
341 DLOG(ERROR) << "No DQT marker found";
342 return false;
343 }
344
345 // Scan data follows scan header immediately.
346 result->scan.data = reader.ptr();
347 result->scan.data_size = reader.remaining();
348
349 return true;
350 }
351
352 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698