Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 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. | |
| 4 // | |
| 5 // This file contains an implementation of a VP9 bitstream parser. | |
| 6 | |
| 7 #include "media/filters/vp9_parser.h" | |
| 8 | |
| 9 #include "base/logging.h" | |
| 10 | |
| 11 namespace { | |
| 12 | |
| 13 int GetMinLog2TileCols(int sb64_cols) { | |
|
Pawel Osciak
2015/07/30 08:27:37
static?
Also please document what this function is
kcwu
2015/07/31 08:51:51
IIUC, no need to add static qualifier if it is alr
| |
| 14 const int kMaxTileWidthB64 = 64; | |
| 15 int min_log2 = 0; | |
| 16 while ((kMaxTileWidthB64 << min_log2) < sb64_cols) | |
| 17 min_log2++; | |
| 18 return min_log2; | |
| 19 } | |
| 20 | |
| 21 int GetMaxLog2TileCols(int sb64_cols) { | |
|
Pawel Osciak
2015/07/30 08:27:37
static?
kcwu
2015/07/31 08:51:51
Acknowledged.
| |
| 22 const int kMinTileWidthB64 = 4; | |
| 23 int max_log2 = 1; | |
| 24 while ((sb64_cols >> max_log2) >= kMinTileWidthB64) | |
| 25 max_log2++; | |
| 26 return max_log2 - 1; | |
| 27 } | |
| 28 | |
| 29 } // namespace | |
| 30 | |
| 31 namespace media { | |
| 32 | |
| 33 Vp9Parser::Vp9Parser() : stream_(nullptr), size_(0) { | |
| 34 memset(&ref_slots_, 0, sizeof(ref_slots_)); | |
| 35 } | |
| 36 | |
| 37 uint8_t Vp9Parser::ReadProfile() { | |
| 38 uint8_t profile = 0; | |
| 39 | |
| 40 // LSB first. | |
| 41 profile |= reader_.ReadBit(); | |
| 42 profile |= reader_.ReadBit() << 1; | |
| 43 if (profile > 2) | |
| 44 profile |= reader_.ReadBit() << 2; | |
| 45 return profile; | |
| 46 } | |
| 47 | |
| 48 bool Vp9Parser::VerifySyncCode() { | |
| 49 const int kSyncCode = 0x498342; | |
| 50 if (reader_.ReadLiteral(8 * 3) != kSyncCode) { | |
|
Pawel Osciak
2015/07/30 08:27:37
General comment: ReadLiteral/ReadBit should have a
kcwu
2015/07/31 08:51:51
Per our chat. We agreed checking reader failure at
| |
| 51 DLOG(ERROR) << "Invalid frame sync code"; | |
| 52 return false; | |
|
Pawel Osciak
2015/07/30 08:27:38
DVLOG please
kcwu1
2015/07/30 08:55:49
Only this one or all in this class?
Pawel Osciak
2015/07/30 08:59:24
All please.
kcwu1
2015/07/31 04:36:01
Done.
| |
| 53 } | |
| 54 return true; | |
| 55 } | |
| 56 | |
| 57 bool Vp9Parser::ReadBitDepthColorSpaceSampling(Vp9FrameHeader* fhdr) { | |
| 58 if (fhdr->profile >= 2) { | |
| 59 if (reader_.ReadBit()) | |
| 60 fhdr->bit_depth = 12; | |
| 61 else | |
| 62 fhdr->bit_depth = 10; | |
| 63 } else { | |
| 64 fhdr->bit_depth = 8; | |
| 65 } | |
| 66 | |
| 67 fhdr->color_space = static_cast<Vp9ColorSpace>(reader_.ReadLiteral(3)); | |
| 68 if (fhdr->color_space != Vp9ColorSpace::SRGB) { | |
| 69 fhdr->yuv_range = reader_.ReadBit(); | |
| 70 if (fhdr->profile == 1 || fhdr->profile == 3) { | |
| 71 fhdr->subsampling_x = reader_.ReadBit(); | |
| 72 fhdr->subsampling_y = reader_.ReadBit(); | |
| 73 if (fhdr->subsampling_x == 1 && fhdr->subsampling_y == 1) { | |
| 74 DLOG(ERROR) << "4:2:0 color not supported in profile 1 or 3"; | |
| 75 return false; | |
| 76 } | |
| 77 bool reserved = reader_.ReadBit(); | |
| 78 if (reserved) { | |
| 79 DLOG(ERROR) << "reserved bit set"; | |
| 80 return false; | |
| 81 } | |
| 82 } else { | |
| 83 fhdr->subsampling_x = fhdr->subsampling_y = 1; | |
| 84 } | |
| 85 } else { | |
| 86 if (fhdr->profile == 1 || fhdr->profile == 3) { | |
| 87 fhdr->subsampling_x = fhdr->subsampling_y = 0; | |
| 88 | |
| 89 // this bit is not specified in spec?? | |
|
Pawel Osciak
2015/07/30 08:27:37
s/this/This/
s/??/?/
kcwu1
2015/07/30 08:55:49
just keep note. I expect this will be removed befo
| |
| 90 bool reserved = reader_.ReadBit(); | |
| 91 if (reserved) { | |
| 92 DLOG(ERROR) << "reserved bit set"; | |
| 93 return false; | |
| 94 } | |
| 95 } else { | |
| 96 DLOG(ERROR) << "4:4:4 color not supported in profile 0 or 2"; | |
| 97 return false; | |
| 98 } | |
| 99 } | |
| 100 | |
| 101 return true; | |
| 102 } | |
| 103 | |
| 104 void Vp9Parser::ReadFrameSize(Vp9FrameHeader* fhdr) { | |
| 105 fhdr->width = reader_.ReadLiteral(16) + 1; | |
| 106 fhdr->height = reader_.ReadLiteral(16) + 1; | |
| 107 } | |
| 108 | |
| 109 void Vp9Parser::ReadFrameSizeFromRefs(Vp9FrameHeader* fhdr) { | |
| 110 for (int i = 0; i < kVp9RefsPerFrame; i++) { | |
|
Pawel Osciak
2015/07/30 08:27:37
s/int/size_t/
Please use size_t in general for it
kcwu1
2015/07/31 04:36:02
Done.
| |
| 111 if (reader_.ReadBit()) { | |
| 112 fhdr->width = ref_slots_[i].width; | |
| 113 fhdr->height = ref_slots_[i].height; | |
| 114 return; | |
| 115 } | |
| 116 } | |
| 117 | |
| 118 fhdr->width = reader_.ReadLiteral(16) + 1; | |
| 119 fhdr->height = reader_.ReadLiteral(16) + 1; | |
| 120 } | |
| 121 | |
| 122 void Vp9Parser::ReadDisplayFrameSize(Vp9FrameHeader* fhdr) { | |
| 123 if (reader_.ReadBit()) { | |
| 124 fhdr->display_width = reader_.ReadLiteral(16) + 1; | |
| 125 fhdr->display_height = reader_.ReadLiteral(16) + 1; | |
| 126 } else { | |
| 127 fhdr->display_width = fhdr->width; | |
| 128 fhdr->display_height = fhdr->height; | |
| 129 } | |
| 130 } | |
| 131 | |
| 132 Vp9InterpFilter Vp9Parser::ReadInterpFilter() { | |
| 133 if (reader_.ReadBit()) | |
| 134 return Vp9InterpFilter::INTERP_FILTER_SELECT; | |
| 135 | |
| 136 // The mapping table for next two bits. | |
| 137 Vp9InterpFilter table[] = { | |
|
Pawel Osciak
2015/07/30 08:27:37
const?
kcwu1
2015/07/31 04:36:01
Done.
| |
| 138 Vp9InterpFilter::EIGHTTAP_SMOOTH, Vp9InterpFilter::EIGHTTAP, | |
| 139 Vp9InterpFilter::EIGHTTAP_SHARP, Vp9InterpFilter::BILINEAR, | |
| 140 }; | |
| 141 return table[reader_.ReadLiteral(2)]; | |
|
Pawel Osciak
2015/07/30 08:27:38
Is this different from return ReadLiteral(2) + 1 ?
kcwu1
2015/07/30 08:55:49
No difference. I just followed libvpx and feel it
Pawel Osciak
2015/07/30 08:59:24
Acknowledged.
| |
| 142 } | |
| 143 | |
| 144 void Vp9Parser::ReadLoopFilter(Vp9LoopFilter* loop_filter) { | |
| 145 loop_filter->filter_level = reader_.ReadLiteral(6); | |
| 146 loop_filter->sharpness_level = reader_.ReadLiteral(3); | |
| 147 | |
| 148 loop_filter->mode_ref_delta_enabled = reader_.ReadBit(); | |
| 149 if (loop_filter->mode_ref_delta_enabled) { | |
| 150 loop_filter->mode_ref_delta_update = reader_.ReadBit(); | |
| 151 if (loop_filter->mode_ref_delta_update) { | |
| 152 for (int i = 0; i < Vp9LoopFilter::kNumRefDeltas; i++) { | |
| 153 loop_filter->update_ref_deltas[i] = reader_.ReadBit(); | |
| 154 if (loop_filter->update_ref_deltas[i]) | |
| 155 loop_filter->ref_deltas[i] = reader_.ReadSignedLiteral(6); | |
| 156 } | |
| 157 | |
| 158 for (int i = 0; i < Vp9LoopFilter::kNumModeDeltas; i++) { | |
| 159 loop_filter->update_mode_deltas[i] = reader_.ReadBit(); | |
| 160 if (loop_filter->update_mode_deltas[i]) | |
| 161 loop_filter->mode_deltas[i] = reader_.ReadLiteral(6); | |
| 162 } | |
| 163 } | |
| 164 } else { | |
| 165 loop_filter->mode_ref_delta_update = false; | |
| 166 } | |
| 167 } | |
| 168 | |
| 169 void Vp9Parser::ReadQuantization(Vp9QuantizationParams* quants) { | |
| 170 quants->base_qindex = reader_.ReadLiteral(8); | |
| 171 | |
| 172 if (reader_.ReadBit()) | |
| 173 quants->y_dc_delta = reader_.ReadSignedLiteral(4); | |
| 174 else | |
| 175 quants->y_dc_delta = 0; | |
| 176 | |
| 177 if (reader_.ReadBit()) | |
| 178 quants->uv_ac_delta = reader_.ReadSignedLiteral(4); | |
| 179 else | |
| 180 quants->uv_ac_delta = 0; | |
| 181 | |
| 182 if (reader_.ReadBit()) | |
| 183 quants->uv_dc_delta = reader_.ReadSignedLiteral(4); | |
| 184 else | |
| 185 quants->uv_dc_delta = 0; | |
| 186 } | |
| 187 | |
| 188 void Vp9Parser::ReadSegmentationMap(Vp9Segmentation* segment) { | |
| 189 for (int i = 0; i < Vp9Segmentation::kTreeProbs; i++) { | |
| 190 if (reader_.ReadBit()) | |
| 191 segment->tree_probs[i] = reader_.ReadLiteral(8); | |
| 192 else | |
| 193 segment->tree_probs[i] = kVp9MaxProb; | |
| 194 } | |
| 195 | |
| 196 for (int i = 0; i < Vp9Segmentation::kPredictionProbs; i++) | |
| 197 segment->pred_probs[i] = kVp9MaxProb; | |
| 198 if (reader_.ReadBit()) { | |
|
Pawel Osciak
2015/07/30 11:52:31
Please store this in frame header (as temporal_upd
kcwu1
2015/07/31 04:36:01
Done.
| |
| 199 for (int i = 0; i < Vp9Segmentation::kPredictionProbs; i++) { | |
| 200 if (reader_.ReadBit()) | |
| 201 segment->pred_probs[i] = reader_.ReadLiteral(8); | |
| 202 } | |
| 203 } | |
| 204 } | |
| 205 | |
| 206 void Vp9Parser::ReadSegmentationData(Vp9Segmentation* segment) { | |
| 207 segment->abs_delta = reader_.ReadBit(); | |
| 208 | |
| 209 const int kFeatureDataBits[] = {7, 6, 2, 0}; | |
| 210 const bool kFeatureDataSigned[] = {true, true, false, false}; | |
| 211 | |
| 212 for (int i = 0; i < Vp9Segmentation::kNumSegment; i++) { | |
| 213 for (int j = 0; j < Vp9Segmentation::kNumFeature; j++) { | |
| 214 int8_t data = 0; | |
| 215 segment->feature_enabled[i][j] = reader_.ReadBit(); | |
| 216 if (segment->feature_enabled[i][j]) { | |
| 217 data = reader_.ReadLiteral(kFeatureDataBits[j]); | |
| 218 if (kFeatureDataSigned[j]) | |
| 219 if (reader_.ReadBit()) | |
| 220 data = -data; | |
| 221 } | |
| 222 segment->feature_data[i][j] = data; | |
| 223 } | |
| 224 } | |
| 225 } | |
| 226 | |
| 227 void Vp9Parser::ReadSegmentation(Vp9Segmentation* segment) { | |
| 228 segment->enabled = reader_.ReadBit(); | |
| 229 | |
| 230 if (!segment->enabled) { | |
| 231 segment->update_map = false; | |
| 232 segment->update_data = false; | |
| 233 return; | |
| 234 } | |
| 235 | |
| 236 segment->update_map = reader_.ReadBit(); | |
| 237 if (segment->update_map) | |
| 238 ReadSegmentationMap(segment); | |
| 239 | |
| 240 segment->update_data = reader_.ReadBit(); | |
| 241 if (segment->update_data) | |
| 242 ReadSegmentationData(segment); | |
| 243 } | |
| 244 | |
| 245 void Vp9Parser::ReadTiles(Vp9FrameHeader* fhdr) { | |
| 246 int sb64_cols = (fhdr->width + 63) / 64; | |
| 247 | |
| 248 int min_log2_tile_cols = GetMinLog2TileCols(sb64_cols); | |
| 249 int max_log2_tile_cols = GetMaxLog2TileCols(sb64_cols); | |
| 250 | |
| 251 int max_ones = max_log2_tile_cols - min_log2_tile_cols; | |
| 252 fhdr->log2_tile_cols = min_log2_tile_cols; | |
| 253 while (max_ones-- && reader_.ReadBit()) | |
| 254 fhdr->log2_tile_cols++; | |
| 255 | |
| 256 if (reader_.ReadBit()) | |
| 257 fhdr->log2_tile_rows = reader_.ReadLiteral(2) - 1; | |
| 258 else | |
| 259 fhdr->log2_tile_rows = 0; | |
| 260 } | |
| 261 | |
| 262 bool Vp9Parser::ParseUncompressedHeader(Vp9FrameHeader* fhdr) { | |
| 263 reader_.Initialize(stream_, size_); | |
| 264 | |
| 265 // frame marker | |
| 266 if (reader_.ReadLiteral(2) != 0x2) | |
| 267 return false; | |
| 268 | |
| 269 fhdr->profile = ReadProfile(); | |
| 270 if (fhdr->profile >= kVp9MaxProfile) { | |
| 271 DLOG(ERROR) << "Unsupported bitstream profile"; | |
| 272 return false; | |
| 273 } | |
| 274 | |
| 275 fhdr->show_existing_frame = reader_.ReadBit(); | |
| 276 if (fhdr->show_existing_frame) { | |
| 277 fhdr->frame_to_show = reader_.ReadLiteral(3); | |
| 278 fhdr->loop_filter.filter_level = 0; | |
|
Pawel Osciak
2015/07/30 08:27:37
It seems that sometimes we initialize members to 0
kcwu1
2015/07/31 04:36:01
Done.
| |
| 279 fhdr->show_frame = true; | |
| 280 | |
| 281 fhdr->first_partition_size = 0; | |
| 282 fhdr->compressed_header = nullptr; | |
| 283 | |
| 284 if (reader_.IsOutOfBuffer()) { | |
|
Pawel Osciak
2015/07/30 08:27:37
This shouldn't be needed once we make ReadBit/Lite
kcwu1
2015/07/30 08:55:49
Since out of data is rare, I'd prefer checked only
Pawel Osciak
2015/07/30 08:59:24
We can't read beyond memory that we have available
kcwu1
2015/07/30 09:04:15
The reader returns 0 if out of buffer. The return
| |
| 285 DLOG(ERROR) << "parser reads beyond the end of buffer"; | |
| 286 return false; | |
| 287 } | |
| 288 return true; | |
| 289 } | |
| 290 | |
| 291 fhdr->frame_type = static_cast<Vp9FrameHeader::FrameType>(reader_.ReadBit()); | |
| 292 fhdr->show_frame = reader_.ReadBit(); | |
| 293 fhdr->error_resilient_mode = reader_.ReadBit(); | |
| 294 | |
| 295 if (fhdr->IsKeyframe()) { | |
| 296 if (!VerifySyncCode()) | |
| 297 return false; | |
| 298 | |
| 299 if (!ReadBitDepthColorSpaceSampling(fhdr)) | |
| 300 return false; | |
| 301 | |
| 302 memset(&ref_slots_, 0, sizeof(ref_slots_)); | |
| 303 for (int i = 0; i < kVp9RefFrames; i++) | |
| 304 fhdr->refresh_flag[i] = true; | |
| 305 | |
| 306 ReadFrameSize(fhdr); | |
| 307 ReadDisplayFrameSize(fhdr); | |
| 308 } else { | |
| 309 if (fhdr->show_frame) | |
| 310 fhdr->intra_only = false; | |
| 311 else | |
| 312 fhdr->intra_only = reader_.ReadBit(); | |
| 313 if (fhdr->error_resilient_mode) | |
|
Pawel Osciak
2015/07/30 08:27:37
Please add empty line above.
kcwu1
2015/07/31 04:36:01
Done.
| |
| 314 fhdr->reset_context = false; | |
| 315 else | |
| 316 fhdr->reset_context = reader_.ReadLiteral(2); | |
| 317 | |
| 318 if (fhdr->intra_only) { | |
| 319 if (!VerifySyncCode()) | |
| 320 return false; | |
| 321 | |
| 322 if (fhdr->profile > 0) { | |
| 323 if (!ReadBitDepthColorSpaceSampling(fhdr)) | |
| 324 return false; | |
| 325 } else { | |
| 326 fhdr->bit_depth = 8; | |
| 327 fhdr->color_space = Vp9ColorSpace::BT_601; | |
| 328 fhdr->subsampling_x = fhdr->subsampling_y = 1; | |
| 329 } | |
| 330 | |
| 331 for (int i = 0; i < kVp9RefFrames; i++) | |
| 332 fhdr->refresh_flag[i] = reader_.ReadBit(); | |
| 333 ReadFrameSize(fhdr); | |
| 334 ReadDisplayFrameSize(fhdr); | |
| 335 } else { | |
| 336 for (int i = 0; i < kVp9RefFrames; i++) | |
| 337 fhdr->refresh_flag[i] = reader_.ReadBit(); | |
| 338 | |
| 339 for (int i = 0; i < kVp9RefsPerFrame; i++) { | |
| 340 fhdr->frame_refs[i] = reader_.ReadLiteral(kVp9RefFramesLog2); | |
| 341 fhdr->ref_sign_biases[i] = reader_.ReadBit(); | |
| 342 } | |
| 343 | |
| 344 ReadFrameSizeFromRefs(fhdr); | |
| 345 ReadDisplayFrameSize(fhdr); | |
| 346 | |
| 347 fhdr->allow_high_precision_mv = reader_.ReadBit(); | |
| 348 fhdr->interp_filter = ReadInterpFilter(); | |
| 349 } | |
| 350 } | |
| 351 | |
| 352 if (fhdr->error_resilient_mode) { | |
| 353 fhdr->refresh_frame_context = false; | |
| 354 fhdr->frame_parallel_decoding_mode = true; | |
| 355 } else { | |
| 356 fhdr->refresh_frame_context = reader_.ReadBit(); | |
| 357 fhdr->frame_parallel_decoding_mode = reader_.ReadBit(); | |
| 358 } | |
| 359 | |
| 360 const int kFrameContextLog2 = 2; | |
|
Pawel Osciak
2015/07/30 08:27:37
Any reason to make this specifically into a consta
kcwu1
2015/07/30 08:55:49
Do you mean why not use literal 2 directly?
Pawel Osciak
2015/07/30 08:59:24
Yes.
kcwu1
2015/07/31 04:36:01
Done.
| |
| 361 fhdr->frame_context_idx = reader_.ReadLiteral(kFrameContextLog2); | |
| 362 | |
| 363 ReadLoopFilter(&fhdr->loop_filter); | |
| 364 ReadQuantization(&fhdr->quant_params); | |
| 365 ReadSegmentation(&fhdr->segment); | |
| 366 | |
| 367 ReadTiles(fhdr); | |
| 368 | |
| 369 fhdr->first_partition_size = reader_.ReadLiteral(16); | |
| 370 if (fhdr->first_partition_size == 0) { | |
| 371 DLOG(ERROR) << "invalid header size"; | |
| 372 return false; | |
| 373 } | |
| 374 fhdr->compressed_header = stream_ + reader_.GetBytesRead(); | |
|
Pawel Osciak
2015/07/30 11:52:31
Could we also store the size of uncompressed heade
kcwu1
2015/07/31 04:36:01
Done.
And remove |compressed_header| since it is r
| |
| 375 | |
| 376 if (reader_.IsOutOfBuffer()) { | |
| 377 DLOG(ERROR) << "parser reads beyond the end of buffer"; | |
| 378 return false; | |
| 379 } | |
| 380 | |
| 381 return true; | |
| 382 } | |
| 383 | |
| 384 void Vp9Parser::UpdateSlots(Vp9FrameHeader* fhdr) { | |
| 385 for (int i = 0; i < kVp9RefFrames; i++) { | |
| 386 if (fhdr->refresh_flag[i]) { | |
| 387 ref_slots_[i].used = true; | |
| 388 ref_slots_[i].width = fhdr->width; | |
| 389 ref_slots_[i].height = fhdr->height; | |
| 390 } | |
| 391 } | |
| 392 } | |
| 393 | |
| 394 bool Vp9Parser::ParseFrame(const uint8_t* ptr, | |
| 395 size_t frame_size, | |
| 396 Vp9FrameHeader* fhdr) { | |
| 397 stream_ = ptr; | |
| 398 size_ = frame_size; | |
| 399 memset(fhdr, 0, sizeof(*fhdr)); | |
| 400 | |
| 401 if (!ParseUncompressedHeader(fhdr)) | |
| 402 return false; | |
| 403 | |
| 404 UpdateSlots(fhdr); | |
| 405 | |
| 406 return true; | |
| 407 } | |
| 408 | |
| 409 } // namespace media | |
| OLD | NEW |