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) \
15 if (!reader.ReadU8(&_out)) { \
17 << "Error in stream: unexpected EOS while trying to read " #out; \
23 #define READ_U16_OR_RETURN_FALSE(out) \
26 if (!reader.ReadU16(&_out)) { \
28 << "Error in stream: unexpected EOS while trying to read " #out; \
36 static bool InRange(int value
, int a
, int b
) {
37 return a
<= value
&& value
<= b
;
40 // Round up |value| to multiple of |mul|. |value| must be non-negative.
41 // |mul| must be positive.
42 static int RoundUp(int value
, int mul
) {
45 return (value
+ mul
- 1) / mul
* mul
;
48 // |frame_header| is already initialized to 0 in ParseJpegPicture.
49 static bool ParseSOF(const char* buffer
,
51 JpegFrameHeader
* frame_header
) {
52 // Spec B.2.2 Frame header syntax
55 BigEndianReader
reader(buffer
, length
);
58 READ_U8_OR_RETURN_FALSE(&precision
);
59 READ_U16_OR_RETURN_FALSE(&frame_header
->visible_height
);
60 READ_U16_OR_RETURN_FALSE(&frame_header
->visible_width
);
61 READ_U8_OR_RETURN_FALSE(&frame_header
->num_components
);
64 DLOG(ERROR
) << "Only support 8-bit precision, not "
65 << static_cast<int>(precision
) << " bit for baseline";
68 if (!InRange(frame_header
->num_components
, 1,
69 arraysize(frame_header
->components
))) {
70 DLOG(ERROR
) << "num_components="
71 << static_cast<int>(frame_header
->num_components
)
72 << " is not supported";
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
) << ")";
88 READ_U8_OR_RETURN_FALSE(&hv
);
89 component
.horizontal_sampling_factor
= hv
/ 16;
90 component
.vertical_sampling_factor
= hv
% 16;
91 if (component
.horizontal_sampling_factor
> max_h_factor
)
92 max_h_factor
= component
.horizontal_sampling_factor
;
93 if (component
.vertical_sampling_factor
> max_v_factor
)
94 max_v_factor
= component
.vertical_sampling_factor
;
95 if (!InRange(component
.horizontal_sampling_factor
, 1, 4)) {
96 DVLOG(1) << "Invalid horizontal sampling factor "
97 << static_cast<int>(component
.horizontal_sampling_factor
);
100 if (!InRange(component
.vertical_sampling_factor
, 1, 4)) {
101 DVLOG(1) << "Invalid vertical sampling factor "
102 << static_cast<int>(component
.horizontal_sampling_factor
);
105 READ_U8_OR_RETURN_FALSE(&component
.quantization_table_selector
);
108 // The size of data unit is 8*8 and the coded size should be extended
109 // to complete minimum coded unit, MCU. See Spec A.2.
110 frame_header
->coded_width
=
111 RoundUp(frame_header
->visible_width
, max_h_factor
* 8);
112 frame_header
->coded_height
=
113 RoundUp(frame_header
->visible_height
, max_v_factor
* 8);
118 // |q_table| is already initialized to 0 in ParseJpegPicture.
119 static bool ParseDQT(const char* buffer
,
121 JpegQuantizationTable
* q_table
) {
122 // Spec B.2.4.1 Quantization table-specification syntax
125 BigEndianReader
reader(buffer
, length
);
126 while (reader
.remaining() > 0) {
127 uint8_t precision_and_table_id
;
128 READ_U8_OR_RETURN_FALSE(&precision_and_table_id
);
129 uint8_t precision
= precision_and_table_id
/ 16;
130 uint8_t table_id
= precision_and_table_id
% 16;
131 if (!InRange(precision
, 0, 1)) {
132 DVLOG(1) << "Invalid precision " << static_cast<int>(precision
);
135 if (precision
== 1) { // 1 means 16-bit precision
136 DLOG(ERROR
) << "An 8-bit DCT-based process shall not use a 16-bit "
137 << "precision quantization table";
140 if (table_id
>= kJpegMaxQuantizationTableNum
) {
141 DLOG(ERROR
) << "Quantization table id (" << static_cast<int>(table_id
)
142 << ") exceeded " << kJpegMaxQuantizationTableNum
;
146 if (!reader
.ReadBytes(&q_table
[table_id
].value
,
147 sizeof(q_table
[table_id
].value
)))
149 q_table
[table_id
].valid
= true;
154 // |dc_table| and |ac_table| are already initialized to 0 in ParseJpegPicture.
155 static bool ParseDHT(const char* buffer
,
157 JpegHuffmanTable
* dc_table
,
158 JpegHuffmanTable
* ac_table
) {
159 // Spec B.2.4.2 Huffman table-specification syntax
163 BigEndianReader
reader(buffer
, length
);
164 while (reader
.remaining() > 0) {
165 uint8_t table_class_and_id
;
166 READ_U8_OR_RETURN_FALSE(&table_class_and_id
);
167 int table_class
= table_class_and_id
/ 16;
168 int table_id
= table_class_and_id
% 16;
169 if (!InRange(table_class
, 0, 1)) {
170 DVLOG(1) << "Invalid table class " << table_class
;
174 DLOG(ERROR
) << "Table id(" << table_id
175 << ") >= 2 is invalid for baseline profile";
179 JpegHuffmanTable
* table
;
180 if (table_class
== 1)
181 table
= &ac_table
[table_id
];
183 table
= &dc_table
[table_id
];
186 if (!reader
.ReadBytes(&table
->code_length
, sizeof(table
->code_length
)))
188 for (size_t i
= 0; i
< arraysize(table
->code_length
); i
++)
189 count
+= table
->code_length
[i
];
191 if (!InRange(count
, 0, sizeof(table
->code_value
))) {
192 DVLOG(1) << "Invalid code count " << count
;
195 if (!reader
.ReadBytes(&table
->code_value
, count
))
202 static bool ParseDRI(const char* buffer
,
204 uint16_t* restart_interval
) {
205 // Spec B.2.4.4 Restart interval definition syntax
207 DCHECK(restart_interval
);
208 BigEndianReader
reader(buffer
, length
);
209 return reader
.ReadU16(restart_interval
) && reader
.remaining() == 0;
212 // |scan| is already initialized to 0 in ParseJpegPicture.
213 static bool ParseSOS(const char* buffer
,
215 const JpegFrameHeader
& frame_header
,
216 JpegScanHeader
* scan
) {
217 // Spec B.2.3 Scan header syntax
220 BigEndianReader
reader(buffer
, length
);
221 READ_U8_OR_RETURN_FALSE(&scan
->num_components
);
222 if (scan
->num_components
!= frame_header
.num_components
) {
223 DLOG(ERROR
) << "The number of scan components ("
224 << static_cast<int>(scan
->num_components
)
225 << ") mismatches the number of image components ("
226 << static_cast<int>(frame_header
.num_components
) << ")";
230 for (int i
= 0; i
< scan
->num_components
; i
++) {
231 JpegScanHeader::Component
* component
= &scan
->components
[i
];
232 READ_U8_OR_RETURN_FALSE(&component
->component_selector
);
233 uint8_t dc_and_ac_selector
;
234 READ_U8_OR_RETURN_FALSE(&dc_and_ac_selector
);
235 component
->dc_selector
= dc_and_ac_selector
/ 16;
236 component
->ac_selector
= dc_and_ac_selector
% 16;
237 if (component
->component_selector
!= frame_header
.components
[i
].id
) {
238 DLOG(ERROR
) << "component selector mismatches image component id";
241 if (component
->dc_selector
>= kJpegMaxHuffmanTableNumBaseline
) {
242 DLOG(ERROR
) << "DC selector (" << static_cast<int>(component
->dc_selector
)
243 << ") should be 0 or 1 for baseline mode";
246 if (component
->ac_selector
>= kJpegMaxHuffmanTableNumBaseline
) {
247 DLOG(ERROR
) << "AC selector (" << static_cast<int>(component
->ac_selector
)
248 << ") should be 0 or 1 for baseline mode";
253 // Unused fields, only for value checking.
254 uint8_t spectral_selection_start
;
255 uint8_t spectral_selection_end
;
256 uint8_t point_transform
;
257 READ_U8_OR_RETURN_FALSE(&spectral_selection_start
);
258 READ_U8_OR_RETURN_FALSE(&spectral_selection_end
);
259 READ_U8_OR_RETURN_FALSE(&point_transform
);
260 if (spectral_selection_start
!= 0 || spectral_selection_end
!= 63) {
261 DLOG(ERROR
) << "Spectral selection should be 0,63 for baseline mode";
264 if (point_transform
!= 0) {
265 DLOG(ERROR
) << "Point transform should be 0 for baseline mode";
272 // |eoi_ptr| will point to the end of image (after EOI marker) after search
273 // succeeds. Returns true on EOI marker found, or false.
274 static bool SearchEOI(const char* buffer
, size_t length
, const char** eoi_ptr
) {
277 BigEndianReader
reader(buffer
, length
);
280 while (reader
.remaining() > 0) {
281 const char* marker1_ptr
= static_cast<const char*>(
282 memchr(reader
.ptr(), JPEG_MARKER_PREFIX
, reader
.remaining()));
285 reader
.Skip(marker1_ptr
- reader
.ptr() + 1);
288 READ_U8_OR_RETURN_FALSE(&marker2
);
289 } while (marker2
== JPEG_MARKER_PREFIX
); // skip fill bytes
292 // Compressed data escape.
306 *eoi_ptr
= reader
.ptr();
309 // Skip for other markers.
311 READ_U16_OR_RETURN_FALSE(&size
);
312 if (size
< sizeof(size
)) {
313 DLOG(ERROR
) << "Ill-formed JPEG. Segment size (" << size
314 << ") is smaller than size field (" << sizeof(size
)
318 size
-= sizeof(size
);
320 if (!reader
.Skip(size
)) {
321 DLOG(ERROR
) << "Ill-formed JPEG. Remaining size ("
322 << reader
.remaining()
323 << ") is smaller than header specified (" << size
<< ")";
332 // |result| is already initialized to 0 in ParseJpegPicture.
333 static bool ParseSOI(const char* buffer
,
335 JpegParseResult
* result
) {
336 // Spec B.2.1 High-level syntax
339 BigEndianReader
reader(buffer
, length
);
342 bool has_marker_dqt
= false;
343 bool has_marker_sos
= false;
345 // Once reached SOS, all neccesary data are parsed.
346 while (!has_marker_sos
) {
347 READ_U8_OR_RETURN_FALSE(&marker1
);
348 if (marker1
!= JPEG_MARKER_PREFIX
)
352 READ_U8_OR_RETURN_FALSE(&marker2
);
353 } while (marker2
== JPEG_MARKER_PREFIX
); // skip fill bytes
356 READ_U16_OR_RETURN_FALSE(&size
);
357 // The size includes the size field itself.
358 if (size
< sizeof(size
)) {
359 DLOG(ERROR
) << "Ill-formed JPEG. Segment size (" << size
360 << ") is smaller than size field (" << sizeof(size
) << ")";
363 size
-= sizeof(size
);
365 if (reader
.remaining() < size
) {
366 DLOG(ERROR
) << "Ill-formed JPEG. Remaining size (" << reader
.remaining()
367 << ") is smaller than header specified (" << size
<< ")";
373 if (!ParseSOF(reader
.ptr(), size
, &result
->frame_header
)) {
374 DLOG(ERROR
) << "ParseSOF failed";
390 DLOG(ERROR
) << "Only SOF0 (baseline) is supported, but got SOF"
391 << (marker2
- JPEG_SOF0
);
394 if (!ParseDQT(reader
.ptr(), size
, result
->q_table
)) {
395 DLOG(ERROR
) << "ParseDQT failed";
398 has_marker_dqt
= true;
401 if (!ParseDHT(reader
.ptr(), size
, result
->dc_table
, result
->ac_table
)) {
402 DLOG(ERROR
) << "ParseDHT failed";
407 if (!ParseDRI(reader
.ptr(), size
, &result
->restart_interval
)) {
408 DLOG(ERROR
) << "ParseDRI failed";
413 if (!ParseSOS(reader
.ptr(), size
, result
->frame_header
,
415 DLOG(ERROR
) << "ParseSOS failed";
418 has_marker_sos
= true;
421 DVLOG(4) << "unknown marker " << static_cast<int>(marker2
);
427 if (!has_marker_dqt
) {
428 DLOG(ERROR
) << "No DQT marker found";
432 // Scan data follows scan header immediately.
433 result
->data
= reader
.ptr();
434 result
->data_size
= reader
.remaining();
435 const size_t kSoiSize
= 2;
436 result
->image_size
= length
+ kSoiSize
;
441 bool ParseJpegPicture(const uint8_t* buffer
,
443 JpegParseResult
* result
) {
446 BigEndianReader
reader(reinterpret_cast<const char*>(buffer
), length
);
447 memset(result
, 0, sizeof(JpegParseResult
));
449 uint8_t marker1
, marker2
;
450 READ_U8_OR_RETURN_FALSE(&marker1
);
451 READ_U8_OR_RETURN_FALSE(&marker2
);
452 if (marker1
!= JPEG_MARKER_PREFIX
|| marker2
!= JPEG_SOI
) {
453 DLOG(ERROR
) << "Not a JPEG";
457 return ParseSOI(reader
.ptr(), reader
.remaining(), result
);
460 bool ParseJpegStream(const uint8_t* buffer
,
462 JpegParseResult
* result
) {
465 if (!ParseJpegPicture(buffer
, length
, result
))
468 BigEndianReader
reader(
469 reinterpret_cast<const char*>(result
->data
), result
->data_size
);
470 const char* eoi_ptr
= nullptr;
471 if (!SearchEOI(reader
.ptr(), reader
.remaining(), &eoi_ptr
)) {
472 DLOG(ERROR
) << "SearchEOI failed";
476 result
->data_size
= eoi_ptr
- result
->data
;
477 result
->image_size
= eoi_ptr
- reinterpret_cast<const char*>(buffer
);