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; \
38 SOF0
= 0xC0, // start of frame (baseline)
39 SOF1
= 0xC1, // start of frame (extended sequential)
40 SOF2
= 0xC2, // start of frame (progressive)
41 SOF3
= 0xC3, // start of frame (lossless))
42 SOF5
= 0xC5, // start of frame (differential, sequential)
43 SOF6
= 0xC6, // start of frame (differential, progressive)
44 SOF7
= 0xC7, // start of frame (differential, lossless)
45 SOF9
= 0xC9, // start of frame (arithmetic coding, extended)
46 SOF10
= 0xCA, // start of frame (arithmetic coding, progressive)
47 SOF11
= 0xCB, // start of frame (arithmetic coding, lossless)
48 SOF13
= 0xCD, // start of frame (differential, arithmetic, sequential)
49 SOF14
= 0xCE, // start of frame (differential, arithmetic, progressive)
50 SOF15
= 0xCF, // start of frame (differential, arithmetic, lossless)
51 DHT
= 0xC4, // define huffman table
52 SOI
= 0xD8, // start of image
53 SOS
= 0xDA, // start of scan
54 DQT
= 0xDB, // define quantization table
55 DRI
= 0xDD, // define restart internal
56 MARKER1
= 0xFF, // jpeg marker prefix
60 static bool InRange(int value
, int a
, int b
) {
61 return a
<= value
&& value
<= b
;
64 // Round up |value| to multiple of |mul|. |value| must be non-negative.
65 // |mul| must be positive.
66 static int RoundUp(int value
, int mul
) {
69 return (value
+ mul
- 1) / mul
* mul
;
72 // |frame_header| is already initialized to 0 in ParseJpegPicture.
73 static bool ParseSOF(const char* buffer
,
75 JpegFrameHeader
* frame_header
) {
76 // Spec B.2.2 Frame header syntax
79 BigEndianReader
reader(buffer
, length
);
82 READ_U8_OR_RETURN_FALSE(&precision
);
83 READ_U16_OR_RETURN_FALSE(&frame_header
->visible_height
);
84 READ_U16_OR_RETURN_FALSE(&frame_header
->visible_width
);
85 READ_U8_OR_RETURN_FALSE(&frame_header
->num_components
);
88 DLOG(ERROR
) << "Only support 8-bit precision, not "
89 << static_cast<int>(precision
) << " bit for baseline";
92 if (!InRange(frame_header
->num_components
, 1,
93 arraysize(frame_header
->components
))) {
94 DLOG(ERROR
) << "num_components="
95 << static_cast<int>(frame_header
->num_components
)
96 << " is not supported";
100 int max_h_factor
= 0;
101 int max_v_factor
= 0;
102 for (size_t i
= 0; i
< frame_header
->num_components
; i
++) {
103 JpegComponent
& component
= frame_header
->components
[i
];
104 READ_U8_OR_RETURN_FALSE(&component
.id
);
105 if (component
.id
> frame_header
->num_components
) {
106 DLOG(ERROR
) << "component id (" << static_cast<int>(component
.id
)
107 << ") should be <= num_components ("
108 << static_cast<int>(frame_header
->num_components
) << ")";
112 READ_U8_OR_RETURN_FALSE(&hv
);
113 component
.horizontal_sampling_factor
= hv
/ 16;
114 component
.vertical_sampling_factor
= hv
% 16;
115 if (component
.horizontal_sampling_factor
> max_h_factor
)
116 max_h_factor
= component
.horizontal_sampling_factor
;
117 if (component
.vertical_sampling_factor
> max_v_factor
)
118 max_v_factor
= component
.vertical_sampling_factor
;
119 if (!InRange(component
.horizontal_sampling_factor
, 1, 4)) {
120 DVLOG(1) << "Invalid horizontal sampling factor "
121 << static_cast<int>(component
.horizontal_sampling_factor
);
124 if (!InRange(component
.vertical_sampling_factor
, 1, 4)) {
125 DVLOG(1) << "Invalid vertical sampling factor "
126 << static_cast<int>(component
.horizontal_sampling_factor
);
129 READ_U8_OR_RETURN_FALSE(&component
.quantization_table_selector
);
132 // The size of data unit is 8*8 and the coded size should be extended
133 // to complete minimum coded unit, MCU. See Spec A.2.
134 frame_header
->coded_width
=
135 RoundUp(frame_header
->visible_width
, max_h_factor
* 8);
136 frame_header
->coded_height
=
137 RoundUp(frame_header
->visible_height
, max_v_factor
* 8);
142 // |q_table| is already initialized to 0 in ParseJpegPicture.
143 static bool ParseDQT(const char* buffer
,
145 JpegQuantizationTable
* q_table
) {
146 // Spec B.2.4.1 Quantization table-specification syntax
149 BigEndianReader
reader(buffer
, length
);
150 while (reader
.remaining() > 0) {
151 uint8_t precision_and_table_id
;
152 READ_U8_OR_RETURN_FALSE(&precision_and_table_id
);
153 uint8_t precision
= precision_and_table_id
/ 16;
154 uint8_t table_id
= precision_and_table_id
% 16;
155 if (!InRange(precision
, 0, 1)) {
156 DVLOG(1) << "Invalid precision " << static_cast<int>(precision
);
159 if (precision
== 1) { // 1 means 16-bit precision
160 DLOG(ERROR
) << "An 8-bit DCT-based process shall not use a 16-bit "
161 << "precision quantization table";
164 if (table_id
>= kJpegMaxQuantizationTableNum
) {
165 DLOG(ERROR
) << "Quantization table id (" << static_cast<int>(table_id
)
166 << ") exceeded " << kJpegMaxQuantizationTableNum
;
170 if (!reader
.ReadBytes(&q_table
[table_id
].value
,
171 sizeof(q_table
[table_id
].value
)))
173 q_table
[table_id
].valid
= true;
178 // |dc_table| and |ac_table| are already initialized to 0 in ParseJpegPicture.
179 static bool ParseDHT(const char* buffer
,
181 JpegHuffmanTable
* dc_table
,
182 JpegHuffmanTable
* ac_table
) {
183 // Spec B.2.4.2 Huffman table-specification syntax
187 BigEndianReader
reader(buffer
, length
);
188 while (reader
.remaining() > 0) {
189 uint8_t table_class_and_id
;
190 READ_U8_OR_RETURN_FALSE(&table_class_and_id
);
191 int table_class
= table_class_and_id
/ 16;
192 int table_id
= table_class_and_id
% 16;
193 if (!InRange(table_class
, 0, 1)) {
194 DVLOG(1) << "Invalid table class " << table_class
;
198 DLOG(ERROR
) << "Table id(" << table_id
199 << ") >= 2 is invalid for baseline profile";
203 JpegHuffmanTable
* table
;
204 if (table_class
== 1)
205 table
= &ac_table
[table_id
];
207 table
= &dc_table
[table_id
];
210 if (!reader
.ReadBytes(&table
->code_length
, sizeof(table
->code_length
)))
212 for (size_t i
= 0; i
< arraysize(table
->code_length
); i
++)
213 count
+= table
->code_length
[i
];
215 if (!InRange(count
, 0, sizeof(table
->code_value
))) {
216 DVLOG(1) << "Invalid code count " << count
;
219 if (!reader
.ReadBytes(&table
->code_value
, count
))
226 static bool ParseDRI(const char* buffer
,
228 uint16_t* restart_interval
) {
229 // Spec B.2.4.4 Restart interval definition syntax
231 DCHECK(restart_interval
);
232 BigEndianReader
reader(buffer
, length
);
233 return reader
.ReadU16(restart_interval
) && reader
.remaining() == 0;
236 // |scan| is already initialized to 0 in ParseJpegPicture.
237 static bool ParseSOS(const char* buffer
,
239 const JpegFrameHeader
& frame_header
,
240 JpegScanHeader
* scan
) {
241 // Spec B.2.3 Scan header syntax
244 BigEndianReader
reader(buffer
, length
);
245 READ_U8_OR_RETURN_FALSE(&scan
->num_components
);
246 if (scan
->num_components
!= frame_header
.num_components
) {
247 DLOG(ERROR
) << "The number of scan components ("
248 << static_cast<int>(scan
->num_components
)
249 << ") mismatches the number of image components ("
250 << static_cast<int>(frame_header
.num_components
) << ")";
254 for (int i
= 0; i
< scan
->num_components
; i
++) {
255 JpegScanHeader::Component
* component
= &scan
->components
[i
];
256 READ_U8_OR_RETURN_FALSE(&component
->component_selector
);
257 uint8_t dc_and_ac_selector
;
258 READ_U8_OR_RETURN_FALSE(&dc_and_ac_selector
);
259 component
->dc_selector
= dc_and_ac_selector
/ 16;
260 component
->ac_selector
= dc_and_ac_selector
% 16;
261 if (component
->component_selector
!= frame_header
.components
[i
].id
) {
262 DLOG(ERROR
) << "component selector mismatches image component id";
265 if (component
->dc_selector
>= kJpegMaxHuffmanTableNumBaseline
) {
266 DLOG(ERROR
) << "DC selector (" << static_cast<int>(component
->dc_selector
)
267 << ") should be 0 or 1 for baseline mode";
270 if (component
->ac_selector
>= kJpegMaxHuffmanTableNumBaseline
) {
271 DLOG(ERROR
) << "AC selector (" << static_cast<int>(component
->ac_selector
)
272 << ") should be 0 or 1 for baseline mode";
277 // Unused fields, only for value checking.
278 uint8_t spectral_selection_start
;
279 uint8_t spectral_selection_end
;
280 uint8_t point_transform
;
281 READ_U8_OR_RETURN_FALSE(&spectral_selection_start
);
282 READ_U8_OR_RETURN_FALSE(&spectral_selection_end
);
283 READ_U8_OR_RETURN_FALSE(&point_transform
);
284 if (spectral_selection_start
!= 0 || spectral_selection_end
!= 63) {
285 DLOG(ERROR
) << "Spectral selection should be 0,63 for baseline mode";
288 if (point_transform
!= 0) {
289 DLOG(ERROR
) << "Point transform should be 0 for baseline mode";
296 // |result| is already initialized to 0 in ParseJpegPicture.
297 static bool ParseSOI(const char* buffer
,
299 JpegParseResult
* result
) {
300 // Spec B.2.1 High-level syntax
303 BigEndianReader
reader(buffer
, length
);
306 bool has_marker_dqt
= false;
307 bool has_marker_sos
= false;
309 // Once reached SOS, all neccesary data are parsed.
310 while (!has_marker_sos
) {
311 READ_U8_OR_RETURN_FALSE(&marker1
);
312 if (marker1
!= MARKER1
)
316 READ_U8_OR_RETURN_FALSE(&marker2
);
317 } while (marker2
== MARKER1
); // skip fill bytes
320 READ_U16_OR_RETURN_FALSE(&size
);
321 if (reader
.remaining() < size
) {
322 DLOG(ERROR
) << "Ill-formed JPEG. Remaining size (" << reader
.remaining()
323 << ") is smaller than header specified (" << size
<< ")";
327 // The size includes the size field itself.
328 if (size
< sizeof(size
)) {
329 DLOG(ERROR
) << "Ill-formed JPEG. Segment size (" << size
330 << ") is smaller than size field (" << sizeof(size
) << ")";
333 size
-= sizeof(size
);
337 if (!ParseSOF(reader
.ptr(), size
, &result
->frame_header
)) {
338 DLOG(ERROR
) << "ParseSOF failed";
354 DLOG(ERROR
) << "Only SOF0 (baseline) is supported, but got SOF"
358 if (!ParseDQT(reader
.ptr(), size
, result
->q_table
)) {
359 DLOG(ERROR
) << "ParseDQT failed";
362 has_marker_dqt
= true;
365 if (!ParseDHT(reader
.ptr(), size
, result
->dc_table
, result
->ac_table
)) {
366 DLOG(ERROR
) << "ParseDHT failed";
371 if (!ParseDRI(reader
.ptr(), size
, &result
->restart_interval
)) {
372 DLOG(ERROR
) << "ParseDRI failed";
377 if (!ParseSOS(reader
.ptr(), size
, result
->frame_header
,
379 DLOG(ERROR
) << "ParseSOS failed";
382 has_marker_sos
= true;
385 DVLOG(4) << "unknown marker " << static_cast<int>(marker2
);
391 if (!has_marker_dqt
) {
392 DLOG(ERROR
) << "No DQT marker found";
396 // Scan data follows scan header immediately.
397 result
->data
= reader
.ptr();
398 result
->data_size
= reader
.remaining();
403 bool ParseJpegPicture(const uint8_t* buffer
,
405 JpegParseResult
* result
) {
408 BigEndianReader
reader(reinterpret_cast<const char*>(buffer
), length
);
409 memset(result
, 0, sizeof(JpegParseResult
));
411 uint8_t marker1
, marker2
;
412 READ_U8_OR_RETURN_FALSE(&marker1
);
413 READ_U8_OR_RETURN_FALSE(&marker2
);
414 if (marker1
!= MARKER1
|| marker2
!= SOI
) {
415 DLOG(ERROR
) << "Not a JPEG";
419 return ParseSOI(reader
.ptr(), reader
.remaining(), result
);