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 // |frame_header| is already initialized to 0 in ParseJpegPicture.
53 static bool ParseSOF(const char* buffer
,
55 JpegFrameHeader
* frame_header
) {
56 // Spec B.2.2 Frame header syntax
59 BigEndianReader
reader(buffer
, length
);
62 READ_U8_OR_RETURN_FALSE(&precision
);
63 READ_U16_OR_RETURN_FALSE(&frame_header
->visible_height
);
64 READ_U16_OR_RETURN_FALSE(&frame_header
->visible_width
);
65 READ_U8_OR_RETURN_FALSE(&frame_header
->num_components
);
68 DLOG(ERROR
) << "Only support 8-bit precision, not "
69 << static_cast<int>(precision
) << " bit for baseline";
72 if (frame_header
->num_components
>= arraysize(frame_header
->components
)) {
73 DLOG(ERROR
) << "num_components="
74 << static_cast<int>(frame_header
->num_components
)
75 << " is not supported";
79 for (size_t i
= 0; i
< frame_header
->num_components
; i
++) {
80 JpegComponent
& component
= frame_header
->components
[i
];
81 READ_U8_OR_RETURN_FALSE(&component
.id
);
82 if (component
.id
> frame_header
->num_components
) {
83 DLOG(ERROR
) << "component id (" << static_cast<int>(component
.id
)
84 << ") should be <= num_components ("
85 << static_cast<int>(frame_header
->num_components
) << ")";
89 READ_U8_OR_RETURN_FALSE(&hv
);
90 component
.horizontal_sampling_factor
= hv
/ 16;
91 component
.vertical_sampling_factor
= hv
% 16;
92 if (!InRange(component
.horizontal_sampling_factor
, 1, 4)) {
93 DVLOG(1) << "Invalid horizontal sampling factor "
94 << static_cast<int>(component
.horizontal_sampling_factor
);
97 if (!InRange(component
.vertical_sampling_factor
, 1, 4)) {
98 DVLOG(1) << "Invalid vertical sampling factor "
99 << static_cast<int>(component
.horizontal_sampling_factor
);
102 READ_U8_OR_RETURN_FALSE(&component
.quantization_table_selector
);
108 // |q_table| is already initialized to 0 in ParseJpegPicture.
109 static bool ParseDQT(const char* buffer
,
111 JpegQuantizationTable
* q_table
) {
112 // Spec B.2.4.1 Quantization table-specification syntax
115 BigEndianReader
reader(buffer
, length
);
116 while (reader
.remaining() > 0) {
117 uint8_t precision_and_table_id
;
118 READ_U8_OR_RETURN_FALSE(&precision_and_table_id
);
119 uint8_t precision
= precision_and_table_id
/ 16;
120 uint8_t table_id
= precision_and_table_id
% 16;
121 if (!InRange(precision
, 0, 1)) {
122 DVLOG(1) << "Invalid precision " << static_cast<int>(precision
);
125 if (precision
== 1) { // 1 means 16-bit precision
126 DLOG(ERROR
) << "An 8-bit DCT-based process shall not use a 16-bit "
127 << "precision quantization table";
130 if (table_id
>= kJpegMaxQuantizationTableNum
) {
131 DLOG(ERROR
) << "Quantization table id (" << static_cast<int>(table_id
)
132 << ") exceeded " << kJpegMaxQuantizationTableNum
;
136 if (!reader
.ReadBytes(&q_table
[table_id
].value
,
137 sizeof(q_table
[table_id
].value
)))
139 q_table
[table_id
].valid
= true;
144 // |dc_table| and |ac_table| are already initialized to 0 in ParseJpegPicture.
145 static bool ParseDHT(const char* buffer
,
147 JpegHuffmanTable
* dc_table
,
148 JpegHuffmanTable
* ac_table
) {
149 // Spec B.2.4.2 Huffman table-specification syntax
153 BigEndianReader
reader(buffer
, length
);
154 while (reader
.remaining() > 0) {
155 uint8_t table_class_and_id
;
156 READ_U8_OR_RETURN_FALSE(&table_class_and_id
);
157 int table_class
= table_class_and_id
/ 16;
158 int table_id
= table_class_and_id
% 16;
159 if (!InRange(table_class
, 0, 1)) {
160 DVLOG(1) << "Invalid table class " << table_class
;
164 DLOG(ERROR
) << "Table id(" << table_id
165 << ") >= 2 is invalid for baseline profile";
169 JpegHuffmanTable
* table
;
170 if (table_class
== 1)
171 table
= &ac_table
[table_id
];
173 table
= &dc_table
[table_id
];
176 if (!reader
.ReadBytes(&table
->code_length
, sizeof(table
->code_length
)))
178 for (size_t i
= 0; i
< arraysize(table
->code_length
); i
++)
179 count
+= table
->code_length
[i
];
181 if (!InRange(count
, 0, sizeof(table
->code_value
))) {
182 DVLOG(1) << "Invalid code count " << count
;
185 if (!reader
.ReadBytes(&table
->code_value
, count
))
192 static bool ParseDRI(const char* buffer
,
194 uint16_t* restart_interval
) {
195 // Spec B.2.4.4 Restart interval definition syntax
197 DCHECK(restart_interval
);
198 BigEndianReader
reader(buffer
, length
);
199 return reader
.ReadU16(restart_interval
) && reader
.remaining() == 0;
202 // |scan| is already initialized to 0 in ParseJpegPicture.
203 static bool ParseSOS(const char* buffer
,
205 const JpegFrameHeader
& frame_header
,
206 JpegScanHeader
* scan
) {
207 // Spec B.2.3 Scan header syntax
210 BigEndianReader
reader(buffer
, length
);
211 READ_U8_OR_RETURN_FALSE(&scan
->num_components
);
212 if (scan
->num_components
!= frame_header
.num_components
) {
213 DLOG(ERROR
) << "The number of scan components ("
214 << static_cast<int>(scan
->num_components
)
215 << ") mismatches the number of image components ("
216 << static_cast<int>(frame_header
.num_components
) << ")";
220 for (int i
= 0; i
< scan
->num_components
; i
++) {
221 JpegScanHeader::Component
* component
= &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
!= frame_header
.components
[i
].id
) {
228 DLOG(ERROR
) << "component selector mismatches image component id";
231 if (component
->dc_selector
>= kJpegMaxHuffmanTableNumBaseline
) {
232 DLOG(ERROR
) << "DC selector (" << static_cast<int>(component
->dc_selector
)
233 << ") should be 0 or 1 for baseline mode";
236 if (component
->ac_selector
>= kJpegMaxHuffmanTableNumBaseline
) {
237 DLOG(ERROR
) << "AC selector (" << static_cast<int>(component
->ac_selector
)
238 << ") should be 0 or 1 for baseline mode";
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";
254 if (point_transform
!= 0) {
255 DLOG(ERROR
) << "Point transform should be 0 for baseline mode";
262 // |result| is already initialized to 0 in ParseJpegPicture.
263 static bool ParseSOI(const char* buffer
,
265 JpegParseResult
* result
) {
266 // Spec B.2.1 High-level syntax
269 BigEndianReader
reader(buffer
, length
);
272 bool has_marker_dqt
= false;
273 bool has_marker_sos
= false;
275 // Once reached SOS, all neccesary data are parsed.
276 while (!has_marker_sos
) {
277 READ_U8_OR_RETURN_FALSE(&marker1
);
278 if (marker1
!= MARKER1
)
282 READ_U8_OR_RETURN_FALSE(&marker2
);
283 } while (marker2
== MARKER1
); // skip fill bytes
286 READ_U16_OR_RETURN_FALSE(&size
);
287 if (reader
.remaining() < size
) {
288 DLOG(ERROR
) << "Ill-formed JPEG. Remaining size (" << reader
.remaining()
289 << ") is smaller than header specified (" << size
<< ")";
293 // The size includes the size field itself.
294 if (size
< sizeof(size
)) {
295 DLOG(ERROR
) << "Ill-formed JPEG. Segment size (" << size
296 << ") is smaller than size field (" << sizeof(size
) << ")";
299 size
-= sizeof(size
);
303 if (!ParseSOF(reader
.ptr(), size
, &result
->frame_header
)) {
304 DLOG(ERROR
) << "ParseSOF failed";
309 if (!ParseDQT(reader
.ptr(), size
, result
->q_table
)) {
310 DLOG(ERROR
) << "ParseDQT failed";
313 has_marker_dqt
= true;
316 if (!ParseDHT(reader
.ptr(), size
, result
->dc_table
, result
->ac_table
)) {
317 DLOG(ERROR
) << "ParseDHT failed";
322 if (!ParseDRI(reader
.ptr(), size
, &result
->restart_interval
)) {
323 DLOG(ERROR
) << "ParseDRI failed";
328 if (!ParseSOS(reader
.ptr(), size
, result
->frame_header
,
330 DLOG(ERROR
) << "ParseSOS failed";
333 has_marker_sos
= true;
336 DVLOG(4) << "unknown marker " << static_cast<int>(marker2
);
342 if (!has_marker_dqt
) {
343 DLOG(ERROR
) << "No DQT marker found";
347 // Scan data follows scan header immediately.
348 result
->data
= reader
.ptr();
349 result
->data_size
= reader
.remaining();
354 bool ParseJpegPicture(const uint8_t* buffer
,
356 JpegParseResult
* result
) {
359 BigEndianReader
reader(reinterpret_cast<const char*>(buffer
), length
);
360 memset(result
, 0, sizeof(JpegParseResult
));
362 uint8_t marker1
, marker2
;
363 READ_U8_OR_RETURN_FALSE(&marker1
);
364 READ_U8_OR_RETURN_FALSE(&marker2
);
365 if (marker1
!= MARKER1
|| marker2
!= SOI
) {
366 DLOG(ERROR
) << "Not a JPEG";
370 return ParseSOI(reader
.ptr(), reader
.remaining(), result
);