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 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
48 static bool InRange(int value
, int a
, int b
) {
49 return a
<= value
&& value
<= b
;
52 // Round up |value| to multiple of |mul|. |value| must be non-negative.
53 // |mul| must be positive.
54 static int RoundUp(int value
, int mul
) {
57 return (value
+ mul
- 1) / mul
* mul
;
60 // |frame_header| is already initialized to 0 in ParseJpegPicture.
61 static bool ParseSOF(const char* buffer
,
63 JpegFrameHeader
* frame_header
) {
64 // Spec B.2.2 Frame header syntax
67 BigEndianReader
reader(buffer
, length
);
70 READ_U8_OR_RETURN_FALSE(&precision
);
71 READ_U16_OR_RETURN_FALSE(&frame_header
->visible_height
);
72 READ_U16_OR_RETURN_FALSE(&frame_header
->visible_width
);
73 READ_U8_OR_RETURN_FALSE(&frame_header
->num_components
);
76 DLOG(ERROR
) << "Only support 8-bit precision, not "
77 << static_cast<int>(precision
) << " bit for baseline";
80 if (!InRange(frame_header
->num_components
, 1,
81 arraysize(frame_header
->components
))) {
82 DLOG(ERROR
) << "num_components="
83 << static_cast<int>(frame_header
->num_components
)
84 << " is not supported";
90 for (size_t i
= 0; i
< frame_header
->num_components
; i
++) {
91 JpegComponent
& component
= frame_header
->components
[i
];
92 READ_U8_OR_RETURN_FALSE(&component
.id
);
93 if (component
.id
> frame_header
->num_components
) {
94 DLOG(ERROR
) << "component id (" << static_cast<int>(component
.id
)
95 << ") should be <= num_components ("
96 << static_cast<int>(frame_header
->num_components
) << ")";
100 READ_U8_OR_RETURN_FALSE(&hv
);
101 component
.horizontal_sampling_factor
= hv
/ 16;
102 component
.vertical_sampling_factor
= hv
% 16;
103 if (component
.horizontal_sampling_factor
> max_h_factor
)
104 max_h_factor
= component
.horizontal_sampling_factor
;
105 if (component
.vertical_sampling_factor
> max_v_factor
)
106 max_v_factor
= component
.vertical_sampling_factor
;
107 if (!InRange(component
.horizontal_sampling_factor
, 1, 4)) {
108 DVLOG(1) << "Invalid horizontal sampling factor "
109 << static_cast<int>(component
.horizontal_sampling_factor
);
112 if (!InRange(component
.vertical_sampling_factor
, 1, 4)) {
113 DVLOG(1) << "Invalid vertical sampling factor "
114 << static_cast<int>(component
.horizontal_sampling_factor
);
117 READ_U8_OR_RETURN_FALSE(&component
.quantization_table_selector
);
120 // The size of data unit is 8*8 and the coded size should be extended
121 // to complete minimum coded unit, MCU. See Spec A.2.
122 frame_header
->coded_width
=
123 RoundUp(frame_header
->visible_width
, max_h_factor
* 8);
124 frame_header
->coded_height
=
125 RoundUp(frame_header
->visible_height
, max_v_factor
* 8);
130 // |q_table| is already initialized to 0 in ParseJpegPicture.
131 static bool ParseDQT(const char* buffer
,
133 JpegQuantizationTable
* q_table
) {
134 // Spec B.2.4.1 Quantization table-specification syntax
137 BigEndianReader
reader(buffer
, length
);
138 while (reader
.remaining() > 0) {
139 uint8_t precision_and_table_id
;
140 READ_U8_OR_RETURN_FALSE(&precision_and_table_id
);
141 uint8_t precision
= precision_and_table_id
/ 16;
142 uint8_t table_id
= precision_and_table_id
% 16;
143 if (!InRange(precision
, 0, 1)) {
144 DVLOG(1) << "Invalid precision " << static_cast<int>(precision
);
147 if (precision
== 1) { // 1 means 16-bit precision
148 DLOG(ERROR
) << "An 8-bit DCT-based process shall not use a 16-bit "
149 << "precision quantization table";
152 if (table_id
>= kJpegMaxQuantizationTableNum
) {
153 DLOG(ERROR
) << "Quantization table id (" << static_cast<int>(table_id
)
154 << ") exceeded " << kJpegMaxQuantizationTableNum
;
158 if (!reader
.ReadBytes(&q_table
[table_id
].value
,
159 sizeof(q_table
[table_id
].value
)))
161 q_table
[table_id
].valid
= true;
166 // |dc_table| and |ac_table| are already initialized to 0 in ParseJpegPicture.
167 static bool ParseDHT(const char* buffer
,
169 JpegHuffmanTable
* dc_table
,
170 JpegHuffmanTable
* ac_table
) {
171 // Spec B.2.4.2 Huffman table-specification syntax
175 BigEndianReader
reader(buffer
, length
);
176 while (reader
.remaining() > 0) {
177 uint8_t table_class_and_id
;
178 READ_U8_OR_RETURN_FALSE(&table_class_and_id
);
179 int table_class
= table_class_and_id
/ 16;
180 int table_id
= table_class_and_id
% 16;
181 if (!InRange(table_class
, 0, 1)) {
182 DVLOG(1) << "Invalid table class " << table_class
;
186 DLOG(ERROR
) << "Table id(" << table_id
187 << ") >= 2 is invalid for baseline profile";
191 JpegHuffmanTable
* table
;
192 if (table_class
== 1)
193 table
= &ac_table
[table_id
];
195 table
= &dc_table
[table_id
];
198 if (!reader
.ReadBytes(&table
->code_length
, sizeof(table
->code_length
)))
200 for (size_t i
= 0; i
< arraysize(table
->code_length
); i
++)
201 count
+= table
->code_length
[i
];
203 if (!InRange(count
, 0, sizeof(table
->code_value
))) {
204 DVLOG(1) << "Invalid code count " << count
;
207 if (!reader
.ReadBytes(&table
->code_value
, count
))
214 static bool ParseDRI(const char* buffer
,
216 uint16_t* restart_interval
) {
217 // Spec B.2.4.4 Restart interval definition syntax
219 DCHECK(restart_interval
);
220 BigEndianReader
reader(buffer
, length
);
221 return reader
.ReadU16(restart_interval
) && reader
.remaining() == 0;
224 // |scan| is already initialized to 0 in ParseJpegPicture.
225 static bool ParseSOS(const char* buffer
,
227 const JpegFrameHeader
& frame_header
,
228 JpegScanHeader
* scan
) {
229 // Spec B.2.3 Scan header syntax
232 BigEndianReader
reader(buffer
, length
);
233 READ_U8_OR_RETURN_FALSE(&scan
->num_components
);
234 if (scan
->num_components
!= frame_header
.num_components
) {
235 DLOG(ERROR
) << "The number of scan components ("
236 << static_cast<int>(scan
->num_components
)
237 << ") mismatches the number of image components ("
238 << static_cast<int>(frame_header
.num_components
) << ")";
242 for (int i
= 0; i
< scan
->num_components
; i
++) {
243 JpegScanHeader::Component
* component
= &scan
->components
[i
];
244 READ_U8_OR_RETURN_FALSE(&component
->component_selector
);
245 uint8_t dc_and_ac_selector
;
246 READ_U8_OR_RETURN_FALSE(&dc_and_ac_selector
);
247 component
->dc_selector
= dc_and_ac_selector
/ 16;
248 component
->ac_selector
= dc_and_ac_selector
% 16;
249 if (component
->component_selector
!= frame_header
.components
[i
].id
) {
250 DLOG(ERROR
) << "component selector mismatches image component id";
253 if (component
->dc_selector
>= kJpegMaxHuffmanTableNumBaseline
) {
254 DLOG(ERROR
) << "DC selector (" << static_cast<int>(component
->dc_selector
)
255 << ") should be 0 or 1 for baseline mode";
258 if (component
->ac_selector
>= kJpegMaxHuffmanTableNumBaseline
) {
259 DLOG(ERROR
) << "AC selector (" << static_cast<int>(component
->ac_selector
)
260 << ") should be 0 or 1 for baseline mode";
265 // Unused fields, only for value checking.
266 uint8_t spectral_selection_start
;
267 uint8_t spectral_selection_end
;
268 uint8_t point_transform
;
269 READ_U8_OR_RETURN_FALSE(&spectral_selection_start
);
270 READ_U8_OR_RETURN_FALSE(&spectral_selection_end
);
271 READ_U8_OR_RETURN_FALSE(&point_transform
);
272 if (spectral_selection_start
!= 0 || spectral_selection_end
!= 63) {
273 DLOG(ERROR
) << "Spectral selection should be 0,63 for baseline mode";
276 if (point_transform
!= 0) {
277 DLOG(ERROR
) << "Point transform should be 0 for baseline mode";
284 // |result| is already initialized to 0 in ParseJpegPicture.
285 static bool ParseSOI(const char* buffer
,
287 JpegParseResult
* result
) {
288 // Spec B.2.1 High-level syntax
291 BigEndianReader
reader(buffer
, length
);
294 bool has_marker_dqt
= false;
295 bool has_marker_sos
= false;
297 // Once reached SOS, all neccesary data are parsed.
298 while (!has_marker_sos
) {
299 READ_U8_OR_RETURN_FALSE(&marker1
);
300 if (marker1
!= MARKER1
)
304 READ_U8_OR_RETURN_FALSE(&marker2
);
305 } while (marker2
== MARKER1
); // skip fill bytes
308 READ_U16_OR_RETURN_FALSE(&size
);
309 if (reader
.remaining() < size
) {
310 DLOG(ERROR
) << "Ill-formed JPEG. Remaining size (" << reader
.remaining()
311 << ") is smaller than header specified (" << size
<< ")";
315 // The size includes the size field itself.
316 if (size
< sizeof(size
)) {
317 DLOG(ERROR
) << "Ill-formed JPEG. Segment size (" << size
318 << ") is smaller than size field (" << sizeof(size
) << ")";
321 size
-= sizeof(size
);
325 if (!ParseSOF(reader
.ptr(), size
, &result
->frame_header
)) {
326 DLOG(ERROR
) << "ParseSOF failed";
331 if (!ParseDQT(reader
.ptr(), size
, result
->q_table
)) {
332 DLOG(ERROR
) << "ParseDQT failed";
335 has_marker_dqt
= true;
338 if (!ParseDHT(reader
.ptr(), size
, result
->dc_table
, result
->ac_table
)) {
339 DLOG(ERROR
) << "ParseDHT failed";
344 if (!ParseDRI(reader
.ptr(), size
, &result
->restart_interval
)) {
345 DLOG(ERROR
) << "ParseDRI failed";
350 if (!ParseSOS(reader
.ptr(), size
, result
->frame_header
,
352 DLOG(ERROR
) << "ParseSOS failed";
355 has_marker_sos
= true;
358 DVLOG(4) << "unknown marker " << static_cast<int>(marker2
);
364 if (!has_marker_dqt
) {
365 DLOG(ERROR
) << "No DQT marker found";
369 // Scan data follows scan header immediately.
370 result
->data
= reader
.ptr();
371 result
->data_size
= reader
.remaining();
376 bool ParseJpegPicture(const uint8_t* buffer
,
378 JpegParseResult
* result
) {
381 BigEndianReader
reader(reinterpret_cast<const char*>(buffer
), length
);
382 memset(result
, 0, sizeof(JpegParseResult
));
384 uint8_t marker1
, marker2
;
385 READ_U8_OR_RETURN_FALSE(&marker1
);
386 READ_U8_OR_RETURN_FALSE(&marker2
);
387 if (marker1
!= MARKER1
|| marker2
!= SOI
) {
388 DLOG(ERROR
) << "Not a JPEG";
392 return ParseSOI(reader
.ptr(), reader
.remaining(), result
);