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/parsers/jpeg_parser.h"
6
7 #include <cstring>
8
9 #include "base/big_endian.h"
10 #include "base/logging.h"
11 #include "base/stl_util.h"
12
13 using base::BigEndianReader;
14
15 #define READ_U8_OR_RETURN_FALSE(out) \
16 do { \
17 uint8_t _out; \
18 if (!reader.ReadU8(&_out)) { \
19 DVLOG(1) \
20 << "Error in stream: unexpected EOS while trying to read " #out; \
21 return false; \
22 } \
23 *(out) = _out; \
24 } while (0)
25
26 #define READ_U16_OR_RETURN_FALSE(out) \
27 do { \
28 uint16_t _out; \
29 if (!reader.ReadU16(&_out)) { \
30 DVLOG(1) \
31 << "Error in stream: unexpected EOS while trying to read " #out; \
32 return false; \
33 } \
34 *(out) = _out; \
35 } while (0)
36
37 namespace media {
38
39 const JpegHuffmanTable kDefaultDcTable[kJpegMaxHuffmanTableNumBaseline] = {
40 // luminance DC coefficients
41 {
42 true,
43 {0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0},
44 {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
45 0x0b},
46 },
47 // chrominance DC coefficients
48 {
49 true,
50 {0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0},
51 {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb},
52 },
53 };
54
55 const JpegHuffmanTable kDefaultAcTable[kJpegMaxHuffmanTableNumBaseline] = {
56 // luminance AC coefficients
57 {
58 true,
59 {0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7d},
60 {0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06,
61 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08,
62 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, 0x24, 0x33, 0x62, 0x72,
63 0x82, 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28,
64 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45,
65 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
66 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75,
67 0x76, 0x77, 0x78, 0x79, 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
68 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3,
69 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6,
70 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9,
71 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2,
72 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1, 0xf2, 0xf3, 0xf4,
73 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa},
74 },
75 // chrominance AC coefficients
76 {
77 true,
78 {0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 0x77},
79 {0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41,
80 0x51, 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91,
81 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, 0x15, 0x62, 0x72, 0xd1,
82 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26,
83 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44,
84 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
85 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74,
86 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
87 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a,
88 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4,
89 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
90 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda,
91 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf2, 0xf3, 0xf4,
92 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa},
93 },
94 };
95
96 constexpr uint8_t kZigZag8x8[64] = {
97 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5,
98 12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28,
99 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51,
100 58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63};
101
102 constexpr JpegQuantizationTable kDefaultQuantTable[2] = {
103 // Table K.1 Luminance quantization table values.
104 {
105 true,
106 {16, 11, 10, 16, 24, 40, 51, 61, 12, 12, 14, 19, 26, 58, 60, 55,
107 14, 13, 16, 24, 40, 57, 69, 56, 14, 17, 22, 29, 51, 87, 80, 62,
108 18, 22, 37, 56, 68, 109, 103, 77, 24, 35, 55, 64, 81, 104, 113, 92,
109 49, 64, 78, 87, 103, 121, 120, 101, 72, 92, 95, 98, 112, 100, 103, 99},
110 },
111 // Table K.2 Chrominance quantization table values.
112 {
113 true,
114 {17, 18, 24, 47, 99, 99, 99, 99, 18, 21, 26, 66, 99, 99, 99, 99,
115 24, 26, 56, 99, 99, 99, 99, 99, 47, 66, 99, 99, 99, 99, 99, 99,
116 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
117 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99},
118 },
119 };
120
InRange(int value,int a,int b)121 static bool InRange(int value, int a, int b) {
122 return a <= value && value <= b;
123 }
124
125 // Round up |value| to multiple of |mul|. |value| must be non-negative.
126 // |mul| must be positive.
RoundUp(int value,int mul)127 static int RoundUp(int value, int mul) {
128 DCHECK_GE(value, 0);
129 DCHECK_GE(mul, 1);
130 return (value + mul - 1) / mul * mul;
131 }
132
133 // |frame_header| is already initialized to 0 in ParseJpegPicture.
ParseSOF(const char * buffer,size_t length,JpegFrameHeader * frame_header)134 static bool ParseSOF(const char* buffer,
135 size_t length,
136 JpegFrameHeader* frame_header) {
137 // Spec B.2.2 Frame header syntax
138 DCHECK(buffer);
139 DCHECK(frame_header);
140 BigEndianReader reader(buffer, length);
141
142 uint8_t precision;
143 READ_U8_OR_RETURN_FALSE(&precision);
144 READ_U16_OR_RETURN_FALSE(&frame_header->visible_height);
145 READ_U16_OR_RETURN_FALSE(&frame_header->visible_width);
146 READ_U8_OR_RETURN_FALSE(&frame_header->num_components);
147
148 if (precision != 8) {
149 DLOG(ERROR) << "Only support 8-bit precision, not "
150 << static_cast<int>(precision) << " bit for baseline";
151 return false;
152 }
153 if (!InRange(frame_header->num_components, 1,
154 base::size(frame_header->components))) {
155 DLOG(ERROR) << "num_components="
156 << static_cast<int>(frame_header->num_components)
157 << " is not supported";
158 return false;
159 }
160
161 int max_h_factor = 0;
162 int max_v_factor = 0;
163 for (size_t i = 0; i < frame_header->num_components; i++) {
164 JpegComponent& component = frame_header->components[i];
165 READ_U8_OR_RETURN_FALSE(&component.id);
166 if (component.id > frame_header->num_components) {
167 DLOG(ERROR) << "component id (" << static_cast<int>(component.id)
168 << ") should be <= num_components ("
169 << static_cast<int>(frame_header->num_components) << ")";
170 return false;
171 }
172 uint8_t hv;
173 READ_U8_OR_RETURN_FALSE(&hv);
174 component.horizontal_sampling_factor = hv / 16;
175 component.vertical_sampling_factor = hv % 16;
176 if (component.horizontal_sampling_factor > max_h_factor)
177 max_h_factor = component.horizontal_sampling_factor;
178 if (component.vertical_sampling_factor > max_v_factor)
179 max_v_factor = component.vertical_sampling_factor;
180 if (!InRange(component.horizontal_sampling_factor, 1, 4)) {
181 DVLOG(1) << "Invalid horizontal sampling factor "
182 << static_cast<int>(component.horizontal_sampling_factor);
183 return false;
184 }
185 if (!InRange(component.vertical_sampling_factor, 1, 4)) {
186 DVLOG(1) << "Invalid vertical sampling factor "
187 << static_cast<int>(component.horizontal_sampling_factor);
188 return false;
189 }
190 READ_U8_OR_RETURN_FALSE(&component.quantization_table_selector);
191 }
192
193 // The size of data unit is 8*8 and the coded size should be extended
194 // to complete minimum coded unit, MCU. See Spec A.2.
195 frame_header->coded_width =
196 RoundUp(frame_header->visible_width, max_h_factor * 8);
197 frame_header->coded_height =
198 RoundUp(frame_header->visible_height, max_v_factor * 8);
199
200 return true;
201 }
202
203 // |q_table| is already initialized to 0 in ParseJpegPicture.
ParseDQT(const char * buffer,size_t length,JpegQuantizationTable * q_table)204 static bool ParseDQT(const char* buffer,
205 size_t length,
206 JpegQuantizationTable* q_table) {
207 // Spec B.2.4.1 Quantization table-specification syntax
208 DCHECK(buffer);
209 DCHECK(q_table);
210 BigEndianReader reader(buffer, length);
211 while (reader.remaining() > 0) {
212 uint8_t precision_and_table_id;
213 READ_U8_OR_RETURN_FALSE(&precision_and_table_id);
214 uint8_t precision = precision_and_table_id / 16;
215 uint8_t table_id = precision_and_table_id % 16;
216 if (!InRange(precision, 0, 1)) {
217 DVLOG(1) << "Invalid precision " << static_cast<int>(precision);
218 return false;
219 }
220 if (precision == 1) { // 1 means 16-bit precision
221 DLOG(ERROR) << "An 8-bit DCT-based process shall not use a 16-bit "
222 << "precision quantization table";
223 return false;
224 }
225 if (table_id >= kJpegMaxQuantizationTableNum) {
226 DLOG(ERROR) << "Quantization table id (" << static_cast<int>(table_id)
227 << ") exceeded " << kJpegMaxQuantizationTableNum;
228 return false;
229 }
230
231 if (!reader.ReadBytes(&q_table[table_id].value,
232 sizeof(q_table[table_id].value)))
233 return false;
234 q_table[table_id].valid = true;
235 }
236 return true;
237 }
238
239 // |dc_table| and |ac_table| are already initialized to 0 in ParseJpegPicture.
ParseDHT(const char * buffer,size_t length,JpegHuffmanTable * dc_table,JpegHuffmanTable * ac_table)240 static bool ParseDHT(const char* buffer,
241 size_t length,
242 JpegHuffmanTable* dc_table,
243 JpegHuffmanTable* ac_table) {
244 // Spec B.2.4.2 Huffman table-specification syntax
245 DCHECK(buffer);
246 DCHECK(dc_table);
247 DCHECK(ac_table);
248 BigEndianReader reader(buffer, length);
249 while (reader.remaining() > 0) {
250 uint8_t table_class_and_id;
251 READ_U8_OR_RETURN_FALSE(&table_class_and_id);
252 int table_class = table_class_and_id / 16;
253 int table_id = table_class_and_id % 16;
254 if (!InRange(table_class, 0, 1)) {
255 DVLOG(1) << "Invalid table class " << table_class;
256 return false;
257 }
258 if (table_id >= 2) {
259 DLOG(ERROR) << "Table id(" << table_id
260 << ") >= 2 is invalid for baseline profile";
261 return false;
262 }
263
264 JpegHuffmanTable* table;
265 if (table_class == 1)
266 table = &ac_table[table_id];
267 else
268 table = &dc_table[table_id];
269
270 size_t count = 0;
271 if (!reader.ReadBytes(&table->code_length, sizeof(table->code_length)))
272 return false;
273 for (size_t i = 0; i < base::size(table->code_length); i++)
274 count += table->code_length[i];
275
276 if (!InRange(count, 0, sizeof(table->code_value))) {
277 DVLOG(1) << "Invalid code count " << count;
278 return false;
279 }
280 if (!reader.ReadBytes(&table->code_value, count))
281 return false;
282 table->valid = true;
283 }
284 return true;
285 }
286
ParseDRI(const char * buffer,size_t length,uint16_t * restart_interval)287 static bool ParseDRI(const char* buffer,
288 size_t length,
289 uint16_t* restart_interval) {
290 // Spec B.2.4.4 Restart interval definition syntax
291 DCHECK(buffer);
292 DCHECK(restart_interval);
293 BigEndianReader reader(buffer, length);
294 return reader.ReadU16(restart_interval) && reader.remaining() == 0;
295 }
296
297 // |scan| is already initialized to 0 in ParseJpegPicture.
ParseSOS(const char * buffer,size_t length,const JpegFrameHeader & frame_header,JpegScanHeader * scan)298 static bool ParseSOS(const char* buffer,
299 size_t length,
300 const JpegFrameHeader& frame_header,
301 JpegScanHeader* scan) {
302 // Spec B.2.3 Scan header syntax
303 DCHECK(buffer);
304 DCHECK(scan);
305 BigEndianReader reader(buffer, length);
306 READ_U8_OR_RETURN_FALSE(&scan->num_components);
307 if (scan->num_components != frame_header.num_components) {
308 DLOG(ERROR) << "The number of scan components ("
309 << static_cast<int>(scan->num_components)
310 << ") mismatches the number of image components ("
311 << static_cast<int>(frame_header.num_components) << ")";
312 return false;
313 }
314
315 for (int i = 0; i < scan->num_components; i++) {
316 JpegScanHeader::Component* component = &scan->components[i];
317 READ_U8_OR_RETURN_FALSE(&component->component_selector);
318 uint8_t dc_and_ac_selector;
319 READ_U8_OR_RETURN_FALSE(&dc_and_ac_selector);
320 component->dc_selector = dc_and_ac_selector / 16;
321 component->ac_selector = dc_and_ac_selector % 16;
322 if (component->component_selector != frame_header.components[i].id) {
323 DLOG(ERROR) << "component selector mismatches image component id";
324 return false;
325 }
326 if (component->dc_selector >= kJpegMaxHuffmanTableNumBaseline) {
327 DLOG(ERROR) << "DC selector (" << static_cast<int>(component->dc_selector)
328 << ") should be 0 or 1 for baseline mode";
329 return false;
330 }
331 if (component->ac_selector >= kJpegMaxHuffmanTableNumBaseline) {
332 DLOG(ERROR) << "AC selector (" << static_cast<int>(component->ac_selector)
333 << ") should be 0 or 1 for baseline mode";
334 return false;
335 }
336 }
337
338 // Unused fields, only for value checking.
339 uint8_t spectral_selection_start;
340 uint8_t spectral_selection_end;
341 uint8_t point_transform;
342 READ_U8_OR_RETURN_FALSE(&spectral_selection_start);
343 READ_U8_OR_RETURN_FALSE(&spectral_selection_end);
344 READ_U8_OR_RETURN_FALSE(&point_transform);
345 if (spectral_selection_start != 0 || spectral_selection_end != 63) {
346 DLOG(ERROR) << "Spectral selection should be 0,63 for baseline mode";
347 return false;
348 }
349 if (point_transform != 0) {
350 DLOG(ERROR) << "Point transform should be 0 for baseline mode";
351 return false;
352 }
353
354 return true;
355 }
356
357 // |eoi_begin_ptr| will point to the beginning of the EOI marker (the FF byte)
358 // and |eoi_end_ptr| will point to the end of image (right after the end of the
359 // EOI marker) after search succeeds. Returns true on EOI marker found, or false
360 // otherwise.
SearchEOI(const char * buffer,size_t length,const char ** eoi_begin_ptr,const char ** eoi_end_ptr)361 static bool SearchEOI(const char* buffer,
362 size_t length,
363 const char** eoi_begin_ptr,
364 const char** eoi_end_ptr) {
365 DCHECK(buffer);
366 DCHECK(eoi_begin_ptr);
367 DCHECK(eoi_end_ptr);
368 BigEndianReader reader(buffer, length);
369 uint8_t marker2;
370
371 while (reader.remaining() > 0) {
372 const char* marker1_ptr = static_cast<const char*>(
373 memchr(reader.ptr(), JPEG_MARKER_PREFIX, reader.remaining()));
374 if (!marker1_ptr)
375 return false;
376 reader.Skip(marker1_ptr - reader.ptr() + 1);
377
378 do {
379 READ_U8_OR_RETURN_FALSE(&marker2);
380 } while (marker2 == JPEG_MARKER_PREFIX); // skip fill bytes
381
382 switch (marker2) {
383 // Compressed data escape.
384 case 0x00:
385 break;
386 // Restart
387 case JPEG_RST0:
388 case JPEG_RST1:
389 case JPEG_RST2:
390 case JPEG_RST3:
391 case JPEG_RST4:
392 case JPEG_RST5:
393 case JPEG_RST6:
394 case JPEG_RST7:
395 break;
396 case JPEG_EOI:
397 *eoi_begin_ptr = marker1_ptr;
398 *eoi_end_ptr = reader.ptr();
399 return true;
400 default:
401 // Skip for other markers.
402 uint16_t size;
403 READ_U16_OR_RETURN_FALSE(&size);
404 if (size < sizeof(size)) {
405 DLOG(ERROR) << "Ill-formed JPEG. Segment size (" << size
406 << ") is smaller than size field (" << sizeof(size)
407 << ")";
408 return false;
409 }
410 size -= sizeof(size);
411
412 if (!reader.Skip(size)) {
413 DLOG(ERROR) << "Ill-formed JPEG. Remaining size ("
414 << reader.remaining()
415 << ") is smaller than header specified (" << size << ")";
416 return false;
417 }
418 break;
419 }
420 }
421 return false;
422 }
423
424 // |result| is already initialized to 0 in ParseJpegPicture.
ParseSOI(const char * buffer,size_t length,JpegParseResult * result)425 static bool ParseSOI(const char* buffer,
426 size_t length,
427 JpegParseResult* result) {
428 // Spec B.2.1 High-level syntax
429 DCHECK(buffer);
430 DCHECK(result);
431 BigEndianReader reader(buffer, length);
432 uint8_t marker1;
433 uint8_t marker2;
434 bool has_marker_dqt = false;
435 bool has_marker_sos = false;
436
437 // Once reached SOS, all neccesary data are parsed.
438 while (!has_marker_sos) {
439 READ_U8_OR_RETURN_FALSE(&marker1);
440 if (marker1 != JPEG_MARKER_PREFIX)
441 return false;
442
443 do {
444 READ_U8_OR_RETURN_FALSE(&marker2);
445 } while (marker2 == JPEG_MARKER_PREFIX); // skip fill bytes
446
447 uint16_t size;
448 READ_U16_OR_RETURN_FALSE(&size);
449 // The size includes the size field itself.
450 if (size < sizeof(size)) {
451 DLOG(ERROR) << "Ill-formed JPEG. Segment size (" << size
452 << ") is smaller than size field (" << sizeof(size) << ")";
453 return false;
454 }
455 size -= sizeof(size);
456
457 if (reader.remaining() < size) {
458 DLOG(ERROR) << "Ill-formed JPEG. Remaining size (" << reader.remaining()
459 << ") is smaller than header specified (" << size << ")";
460 return false;
461 }
462
463 switch (marker2) {
464 case JPEG_SOF0:
465 if (!ParseSOF(reader.ptr(), size, &result->frame_header)) {
466 DLOG(ERROR) << "ParseSOF failed";
467 return false;
468 }
469 break;
470 case JPEG_SOF1:
471 case JPEG_SOF2:
472 case JPEG_SOF3:
473 case JPEG_SOF5:
474 case JPEG_SOF6:
475 case JPEG_SOF7:
476 case JPEG_SOF9:
477 case JPEG_SOF10:
478 case JPEG_SOF11:
479 case JPEG_SOF13:
480 case JPEG_SOF14:
481 case JPEG_SOF15:
482 DLOG(ERROR) << "Only SOF0 (baseline) is supported, but got SOF"
483 << (marker2 - JPEG_SOF0);
484 return false;
485 case JPEG_DQT:
486 if (!ParseDQT(reader.ptr(), size, result->q_table)) {
487 DLOG(ERROR) << "ParseDQT failed";
488 return false;
489 }
490 has_marker_dqt = true;
491 break;
492 case JPEG_DHT:
493 if (!ParseDHT(reader.ptr(), size, result->dc_table, result->ac_table)) {
494 DLOG(ERROR) << "ParseDHT failed";
495 return false;
496 }
497 break;
498 case JPEG_DRI:
499 if (!ParseDRI(reader.ptr(), size, &result->restart_interval)) {
500 DLOG(ERROR) << "ParseDRI failed";
501 return false;
502 }
503 break;
504 case JPEG_SOS:
505 if (!ParseSOS(reader.ptr(), size, result->frame_header,
506 &result->scan)) {
507 DLOG(ERROR) << "ParseSOS failed";
508 return false;
509 }
510 has_marker_sos = true;
511 break;
512 default:
513 DVLOG(4) << "unknown marker " << static_cast<int>(marker2);
514 break;
515 }
516 reader.Skip(size);
517 }
518
519 if (!has_marker_dqt) {
520 DLOG(ERROR) << "No DQT marker found";
521 return false;
522 }
523
524 // Scan data follows scan header immediately.
525 result->data = reader.ptr();
526 result->data_size = reader.remaining();
527 return true;
528 }
529
ParseJpegPicture(const uint8_t * buffer,size_t length,JpegParseResult * result)530 bool ParseJpegPicture(const uint8_t* buffer,
531 size_t length,
532 JpegParseResult* result) {
533 DCHECK(buffer);
534 DCHECK(result);
535 BigEndianReader reader(reinterpret_cast<const char*>(buffer), length);
536 memset(result, 0, sizeof(JpegParseResult));
537
538 uint8_t marker1, marker2;
539 READ_U8_OR_RETURN_FALSE(&marker1);
540 READ_U8_OR_RETURN_FALSE(&marker2);
541 if (marker1 != JPEG_MARKER_PREFIX || marker2 != JPEG_SOI) {
542 DLOG(ERROR) << "Not a JPEG";
543 return false;
544 }
545
546 if (!ParseSOI(reader.ptr(), reader.remaining(), result))
547 return false;
548
549 // Update the sizes: |result->data_size| should not include the EOI marker or
550 // beyond.
551 BigEndianReader eoi_reader(result->data, result->data_size);
552 const char* eoi_begin_ptr = nullptr;
553 const char* eoi_end_ptr = nullptr;
554 if (!SearchEOI(eoi_reader.ptr(), eoi_reader.remaining(), &eoi_begin_ptr,
555 &eoi_end_ptr)) {
556 DLOG(ERROR) << "SearchEOI failed";
557 return false;
558 }
559 DCHECK(eoi_begin_ptr);
560 DCHECK(eoi_end_ptr);
561 result->data_size = eoi_begin_ptr - result->data;
562 result->image_size = eoi_end_ptr - reinterpret_cast<const char*>(buffer);
563 return true;
564 }
565
566 // TODO(andrescj): this function no longer seems necessary. Fix call sites to
567 // use ParseJpegPicture() directly.
ParseJpegStream(const uint8_t * buffer,size_t length,JpegParseResult * result)568 bool ParseJpegStream(const uint8_t* buffer,
569 size_t length,
570 JpegParseResult* result) {
571 DCHECK(buffer);
572 DCHECK(result);
573 return ParseJpegPicture(buffer, length, result);
574 }
575
576 } // namespace media
577