Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2014 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 "base/logging.h" | |
| 6 #include "content/common/gpu/media/vaapi_jpeg_parser.h" | |
| 7 | |
| 8 using base::BigEndianReader; | |
| 9 | |
| 10 namespace content { | |
| 11 | |
| 12 VaapiJpegParser::VaapiJpegParser(const uint8_t* buffer, size_t length) | |
| 13 : buffer_(buffer), length_(length) { | |
|
Owen Lin
2014/12/26 07:13:39
DCHECK on buffer and length ?
kcwu
2014/12/27 13:27:34
Acknowledged.
| |
| 14 } | |
| 15 | |
| 16 VaapiJpegParser::~VaapiJpegParser() { | |
| 17 } | |
| 18 | |
| 19 bool VaapiJpegParser::ParseSOF(BigEndianReader reader) { | |
|
Pawel Osciak
2014/12/26 04:47:40
Do we really want to copy readers passing them to
kcwu
2014/12/27 13:27:34
I want to make sure the reader of one marker won't
| |
| 20 // Spec B.2.2 Frame header syntax | |
| 21 uint8_t precision; | |
| 22 | |
| 23 if (!reader.ReadU8(&precision)) | |
| 24 return false; | |
|
Pawel Osciak
2014/12/26 04:47:40
Perhaps you could have a macro
READ_U8_OR_RETURN_F
kcwu
2014/12/27 13:27:34
Done.
| |
| 25 if (!reader.ReadU16(&result_.visible_height)) | |
| 26 return false; | |
| 27 if (!reader.ReadU16(&result_.visible_width)) | |
| 28 return false; | |
| 29 if (!reader.ReadU8(&result_.num_component)) | |
| 30 return false; | |
| 31 | |
| 32 if (precision != 8) { | |
| 33 DLOG(ERROR) << "Only support 8 bit precision, not " | |
| 34 << static_cast<int>(precision) << " bit"; | |
| 35 return false; | |
| 36 } | |
| 37 // Size 64k*64k is the maximum in the JPEG standard. VAAPI doesn't support | |
| 38 // larger than 16k*16k. | |
| 39 if (result_.visible_height > 16384 || result_.visible_width > 16384) { | |
|
Pawel Osciak
2014/12/26 04:47:39
We should not have vaapi specifics in the parser.
kcwu
2014/12/27 13:27:34
Done.
| |
| 40 DLOG(ERROR) << "VAAPI don't support size(" << result_.visible_width << "*" | |
| 41 << result_.visible_height << ") larger than 16k*16k"; | |
| 42 return false; | |
| 43 } | |
| 44 if (result_.num_component != 3 || | |
|
Pawel Osciak
2014/12/26 04:47:39
make this a constant?
kcwu
2014/12/27 13:27:34
Done.
| |
| 45 result_.num_component >= arraysize(result_.components)) { | |
|
Pawel Osciak
2014/12/26 04:47:40
What's the max num components by spec? Can we just
kcwu
2014/12/27 13:27:34
In theory (generic JPEG spec), it could be 255. Bu
| |
| 46 DLOG(ERROR) << "num_component=" << static_cast<int>(result_.num_component) | |
| 47 << " is not supported"; | |
| 48 return false; | |
| 49 } | |
| 50 | |
| 51 for (int i = 0; i < result_.num_component; i++) { | |
|
Pawel Osciak
2014/12/26 04:47:40
size_t i
kcwu
2014/12/27 13:27:34
Done.
| |
| 52 JpegComponent& component = result_.components[i]; | |
| 53 if (!reader.ReadU8(&component.id)) | |
| 54 return false; | |
| 55 if (component.id > result_.num_component) { | |
| 56 DLOG(ERROR) << "component id (" << static_cast<int>(component.id) | |
| 57 << ") should be <= num_component (" | |
| 58 << static_cast<int>(result_.num_component) << ")"; | |
| 59 return false; | |
| 60 } | |
| 61 uint8_t hv; | |
| 62 if (!reader.ReadU8(&hv)) | |
| 63 return false; | |
| 64 component.horizontal_sampling_factor = hv / 16; | |
|
Pawel Osciak
2014/12/26 04:47:40
Do we need to check for more things, like if it's
kcwu
2014/12/27 13:27:35
Done.
| |
| 65 component.vertical_sampling_factor = hv % 16; | |
| 66 if (!reader.ReadU8(&component.quantization_table_selector)) | |
| 67 return false; | |
| 68 } | |
| 69 | |
| 70 if (result_.components[0].horizontal_sampling_factor < | |
| 71 result_.components[1].horizontal_sampling_factor || | |
| 72 result_.components[0].horizontal_sampling_factor < | |
| 73 result_.components[2].horizontal_sampling_factor) { | |
| 74 DLOG(ERROR) << "VAAPI don't supports horizontal sampling factor of Y" | |
|
Pawel Osciak
2014/12/26 04:47:40
No vaapi anywhere please.
kcwu
2014/12/27 13:27:34
Done.
| |
| 75 << " smaller than Cb and Cr"; | |
| 76 return false; | |
| 77 } | |
| 78 if (result_.components[0].vertical_sampling_factor < | |
| 79 result_.components[1].vertical_sampling_factor || | |
| 80 result_.components[0].vertical_sampling_factor < | |
| 81 result_.components[2].vertical_sampling_factor) { | |
| 82 DLOG(ERROR) << "VAAPI don't supports vertical sampling factor of Y" | |
| 83 << " smaller than Cb and Cr"; | |
| 84 return false; | |
| 85 } | |
| 86 | |
| 87 return true; | |
| 88 } | |
| 89 | |
| 90 bool VaapiJpegParser::ParseDQT(BigEndianReader reader) { | |
| 91 // Spec B.2.4.1 Quantization table-specification syntax | |
| 92 while (reader.remaining() > 0) { | |
| 93 uint8_t tmp; | |
| 94 if (!reader.ReadU8(&tmp)) | |
| 95 return false; | |
| 96 uint8_t precision = tmp / 16; | |
| 97 uint8_t table_id = tmp % 16; | |
| 98 if (precision != 0) { | |
| 99 DLOG(ERROR) << "16 bits quantization table is not supported"; | |
| 100 return false; | |
| 101 } | |
| 102 if (table_id >= kJpegQuantizationTableNum) { | |
| 103 DLOG(ERROR) << "Quantization table id (" << static_cast<int>(table_id) | |
| 104 << ") exceeded " << kJpegQuantizationTableNum; | |
| 105 return false; | |
| 106 } | |
| 107 | |
| 108 if (!reader.ReadBytes(&result_.q_table[table_id].value, | |
| 109 sizeof(result_.q_table[table_id].value))) | |
| 110 return false; | |
| 111 result_.q_table[table_id].valid = true; | |
| 112 } | |
| 113 return true; | |
| 114 } | |
| 115 | |
| 116 bool VaapiJpegParser::ParseDHT(BigEndianReader reader) { | |
| 117 // Spec B.2.4.2 Huffman table-specification syntax | |
| 118 while (reader.remaining() > 0) { | |
| 119 uint8_t tmp; | |
| 120 if (!reader.ReadU8(&tmp)) | |
| 121 return false; | |
| 122 int table_class = tmp / 16; | |
| 123 int table_id = tmp % 16; | |
| 124 if (table_class >= 2) { | |
| 125 DLOG(ERROR) << "Invalid table class: " << table_class; | |
| 126 return false; | |
| 127 } | |
| 128 if (table_id >= 2) { | |
| 129 DLOG(ERROR) << "Table id(" << table_id << ") >= 2 is invalid for " | |
|
Owen Lin
2014/12/26 07:13:39
Is this the result of git cl format ? I thought th
kcwu
2014/12/27 13:27:34
Done.
| |
| 130 "baseline profile"; | |
| 131 return false; | |
| 132 } | |
| 133 | |
| 134 JpegHuffmanTable* table; | |
| 135 if (table_class == 1) | |
| 136 table = &result_.ac_table[table_id]; | |
| 137 else | |
| 138 table = &result_.dc_table[table_id]; | |
| 139 | |
| 140 size_t count = 0; | |
| 141 if (!reader.ReadBytes(&table->code_length, sizeof(table->code_length))) | |
|
Pawel Osciak
2014/12/26 04:47:40
Where does code_length come from? Do we need to va
kcwu
2014/12/27 13:27:34
I don't understand how to overflow. Please clarify
Owen Lin
2014/12/29 09:03:20
It's actually the length of the |code_length| ...
| |
| 142 return false; | |
| 143 for (size_t i = 0; i < arraysize(table->code_length); i++) | |
| 144 count += table->code_length[i]; | |
| 145 | |
| 146 if (count > sizeof(table->code_value)) { | |
| 147 DLOG(ERROR) << "Number of code values is invalid: " << count; | |
| 148 return false; | |
| 149 } | |
| 150 if (!reader.ReadBytes(&table->code_value, count)) | |
| 151 return false; | |
| 152 table->valid = true; | |
| 153 } | |
| 154 return true; | |
| 155 } | |
| 156 | |
| 157 bool VaapiJpegParser::ParseDRI(BigEndianReader reader) { | |
| 158 // Spec B.2.4.4 Restart interval definition syntax | |
| 159 return reader.ReadU16(&result_.restart_interval) && reader.remaining() == 0; | |
| 160 } | |
| 161 | |
| 162 bool VaapiJpegParser::ParseSOS(BigEndianReader reader) { | |
| 163 // Spec B.2.3 Scan header syntax | |
| 164 if (!reader.ReadU8(&result_.scan.num_component)) | |
| 165 return false; | |
| 166 if (result_.scan.num_component != result_.num_component) { | |
| 167 DLOG(ERROR) << "The number of scan components (" | |
| 168 << static_cast<int>(result_.scan.num_component) | |
| 169 << ") mismatches the number of image components (" | |
| 170 << static_cast<int>(result_.num_component) << ")"; | |
| 171 return false; | |
| 172 } | |
| 173 | |
| 174 for (int i = 0; i < result_.scan.num_component; i++) { | |
| 175 JpegScan::Component* component = &result_.scan.components[i]; | |
| 176 if (!reader.ReadU8(&component->component_selector)) | |
| 177 return false; | |
| 178 uint8_t tmp; | |
| 179 if (!reader.ReadU8(&tmp)) | |
| 180 return false; | |
| 181 component->dc_selector = tmp / 16; | |
| 182 component->ac_selector = tmp % 16; | |
| 183 if (component->component_selector != result_.components[i].id) { | |
| 184 DLOG(ERROR) << "component selector mismatches image component id"; | |
| 185 return false; | |
| 186 } | |
| 187 if (component->dc_selector >= kJpegHuffmanTableNum_baseline) { | |
| 188 DLOG(ERROR) << "DC selector (" << static_cast<int>(component->dc_selector) | |
| 189 << ") should be 0 or 1 for baseline mode"; | |
| 190 return false; | |
| 191 } | |
| 192 if (component->ac_selector >= kJpegHuffmanTableNum_baseline) { | |
| 193 DLOG(ERROR) << "AC selector (" << static_cast<int>(component->ac_selector) | |
| 194 << ") should be 0 or 1 for baseline mode"; | |
| 195 return false; | |
| 196 } | |
| 197 } | |
| 198 | |
| 199 // unused fields, only for value checking | |
|
Pawel Osciak
2014/12/26 04:47:40
Capital letter at beginning, dot at end.
kcwu
2014/12/27 13:27:34
Done.
| |
| 200 uint8_t spectral_selection_start; | |
| 201 uint8_t spectral_selection_end; | |
| 202 uint8_t point_transform; | |
| 203 if (!reader.ReadU8(&spectral_selection_start)) | |
| 204 return false; | |
| 205 if (!reader.ReadU8(&spectral_selection_end)) | |
| 206 return false; | |
| 207 if (!reader.ReadU8(&point_transform)) | |
| 208 return false; | |
| 209 if (spectral_selection_start != 0 || spectral_selection_end != 63) { | |
| 210 DLOG(ERROR) << "Spectral selection should be 0,63 for baseline mode"; | |
| 211 return false; | |
| 212 } | |
| 213 if (point_transform != 0) { | |
| 214 DLOG(ERROR) << "Point transform should be 0 for baseline mode"; | |
| 215 return false; | |
| 216 } | |
| 217 | |
| 218 return true; | |
| 219 } | |
| 220 | |
| 221 bool VaapiJpegParser::ParseSOI(BigEndianReader reader) { | |
| 222 // Spec B.2.1 High-level syntax | |
| 223 uint8_t marker1; | |
| 224 uint8_t marker2; | |
| 225 bool has_marker_dqt = false; | |
| 226 bool has_marker_sos = false; | |
| 227 | |
| 228 // Once reached SOS, all neccesary data are parsed. | |
| 229 while (!has_marker_sos) { | |
| 230 if (!reader.ReadU8(&marker1) || marker1 != MARKER1) | |
| 231 return false; | |
| 232 | |
| 233 do { | |
| 234 if (!reader.ReadU8(&marker2)) | |
| 235 return false; | |
| 236 } while (marker2 == MARKER1); // skip fill bytes | |
| 237 | |
| 238 uint16_t size; | |
| 239 if (!reader.ReadU16(&size)) | |
| 240 return false; | |
| 241 if (reader.remaining() < size) { | |
| 242 DLOG(ERROR) << "Ill-formed JPEG. Remaining size (" << reader.remaining() | |
| 243 << ") is smaller than header specified (" << size << ")"; | |
| 244 return false; | |
| 245 } | |
| 246 | |
| 247 // The size includes the size field itself. | |
| 248 if (size < sizeof(size)) { | |
| 249 DLOG(ERROR) << "Ill-formed JPEG. Segment size (" << size | |
| 250 << ") is smaller than size field (" << sizeof(size) << ")"; | |
| 251 return false; | |
| 252 } | |
| 253 size -= sizeof(size); | |
| 254 | |
| 255 switch (marker2) { | |
| 256 case SOF0: | |
| 257 if (!ParseSOF(BigEndianReader(reader.ptr(), size))) { | |
| 258 DLOG(ERROR) << "ParseSOF failed"; | |
| 259 return false; | |
| 260 } | |
| 261 break; | |
| 262 case DQT: | |
| 263 if (!ParseDQT(BigEndianReader(reader.ptr(), size))) { | |
| 264 DLOG(ERROR) << "ParseDQT failed"; | |
| 265 return false; | |
| 266 } | |
| 267 has_marker_dqt = true; | |
| 268 break; | |
| 269 case DHT: | |
| 270 if (!ParseDHT(BigEndianReader(reader.ptr(), size))) { | |
| 271 DLOG(ERROR) << "ParseDHT failed"; | |
| 272 return false; | |
| 273 } | |
| 274 break; | |
| 275 case DRI: | |
| 276 if (!ParseDRI(BigEndianReader(reader.ptr(), size))) { | |
| 277 DLOG(ERROR) << "ParseDRI failed"; | |
| 278 return false; | |
| 279 } | |
| 280 break; | |
| 281 case SOS: | |
| 282 if (!ParseSOS(BigEndianReader(reader.ptr(), size))) { | |
| 283 DLOG(ERROR) << "ParseSOS failed"; | |
| 284 return false; | |
| 285 } | |
| 286 has_marker_sos = true; | |
| 287 break; | |
| 288 default: | |
| 289 DVLOG(4) << "unknown marker " << static_cast<int>(marker2); | |
| 290 break; | |
| 291 } | |
| 292 reader.Skip(size); | |
|
Owen Lin
2014/12/26 07:13:39
We should check the return code.
kcwu
2014/12/27 13:27:34
I already check "reader.remaining() < size" above.
Owen Lin
2014/12/29 09:03:20
Acknowledged.
| |
| 293 } | |
| 294 | |
| 295 if (!has_marker_dqt) { | |
| 296 DLOG(ERROR) << "No DQT marker found"; | |
| 297 return false; | |
| 298 } | |
| 299 | |
| 300 // Scan data follows scan header immediately. | |
| 301 result_.scan.data = reader.ptr(); | |
| 302 result_.scan.data_size = reader.remaining(); | |
| 303 | |
| 304 return true; | |
| 305 } | |
| 306 | |
| 307 const JpegParseResult* VaapiJpegParser::Parse() { | |
| 308 BigEndianReader reader(reinterpret_cast<const char*>(buffer_), length_); | |
| 309 memset(&result_, 0, sizeof(result_)); | |
| 310 | |
| 311 uint8_t marker1, marker2; | |
| 312 if (!reader.ReadU8(&marker1) || marker1 != MARKER1 || | |
| 313 !reader.ReadU8(&marker2) || marker2 != SOI) { | |
| 314 LOG(ERROR) << "Not a JPEG"; | |
| 315 return NULL; | |
| 316 } | |
| 317 | |
| 318 if (ParseSOI(BigEndianReader(reader.ptr(), reader.remaining()))) | |
|
Pawel Osciak
2014/12/26 04:47:40
Why are we creating a separate reader for each par
kcwu
2014/12/27 13:27:34
Acknowledged.
| |
| 319 return &result_; | |
| 320 | |
| 321 return NULL; | |
| 322 } | |
| 323 | |
| 324 } // namespace content | |
| OLD | NEW |