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 // VERBOSE level: 8 // 1 something wrong in bitstream 9 // 2 parsing steps 10 // 3 parsed values (selected) 11 12 #include "vp9_parser.h" 13 14 #include <algorithm> 15 16 #include "base/bind.h" 17 #include "base/logging.h" 18 #include "base/macros.h" 19 #include "base/numerics/safe_conversions.h" 20 #include "vp9_compressed_header_parser.h" 21 #include "vp9_uncompressed_header_parser.h" 22 23 namespace media { 24 25 bool Vp9FrameHeader::IsKeyframe() const { 26 // When show_existing_frame is true, the frame header does not precede an 27 // actual frame to be decoded, so frame_type does not apply (and is not read 28 // from the stream). 29 return !show_existing_frame && frame_type == KEYFRAME; 30 } 31 32 bool Vp9FrameHeader::IsIntra() const { 33 return !show_existing_frame && (frame_type == KEYFRAME || intra_only); 34 } 35 36 Vp9Parser::FrameInfo::FrameInfo(const uint8_t* ptr, off_t size) 37 : ptr(ptr), size(size) {} 38 39 bool Vp9FrameContext::IsValid() const { 40 // probs should be in [1, 255] range. 41 static_assert(sizeof(Vp9Prob) == 1, 42 "following checks assuming Vp9Prob is single byte"); 43 if (memchr(tx_probs_8x8, 0, sizeof(tx_probs_8x8))) 44 return false; 45 if (memchr(tx_probs_16x16, 0, sizeof(tx_probs_16x16))) 46 return false; 47 if (memchr(tx_probs_32x32, 0, sizeof(tx_probs_32x32))) 48 return false; 49 50 for (auto& a : coef_probs) { 51 for (auto& ai : a) { 52 for (auto& aj : ai) { 53 for (auto& ak : aj) { 54 int max_l = (ak == aj[0]) ? 3 : 6; 55 for (int l = 0; l < max_l; l++) { 56 for (auto& x : ak[l]) { 57 if (x == 0) 58 return false; 59 } 60 } 61 } 62 } 63 } 64 } 65 if (memchr(skip_prob, 0, sizeof(skip_prob))) 66 return false; 67 if (memchr(inter_mode_probs, 0, sizeof(inter_mode_probs))) 68 return false; 69 if (memchr(interp_filter_probs, 0, sizeof(interp_filter_probs))) 70 return false; 71 if (memchr(is_inter_prob, 0, sizeof(is_inter_prob))) 72 return false; 73 if (memchr(comp_mode_prob, 0, sizeof(comp_mode_prob))) 74 return false; 75 if (memchr(single_ref_prob, 0, sizeof(single_ref_prob))) 76 return false; 77 if (memchr(comp_ref_prob, 0, sizeof(comp_ref_prob))) 78 return false; 79 if (memchr(y_mode_probs, 0, sizeof(y_mode_probs))) 80 return false; 81 if (memchr(uv_mode_probs, 0, sizeof(uv_mode_probs))) 82 return false; 83 if (memchr(partition_probs, 0, sizeof(partition_probs))) 84 return false; 85 if (memchr(mv_joint_probs, 0, sizeof(mv_joint_probs))) 86 return false; 87 if (memchr(mv_sign_prob, 0, sizeof(mv_sign_prob))) 88 return false; 89 if (memchr(mv_class_probs, 0, sizeof(mv_class_probs))) 90 return false; 91 if (memchr(mv_class0_bit_prob, 0, sizeof(mv_class0_bit_prob))) 92 return false; 93 if (memchr(mv_bits_prob, 0, sizeof(mv_bits_prob))) 94 return false; 95 if (memchr(mv_class0_fr_probs, 0, sizeof(mv_class0_fr_probs))) 96 return false; 97 if (memchr(mv_fr_probs, 0, sizeof(mv_fr_probs))) 98 return false; 99 if (memchr(mv_class0_hp_prob, 0, sizeof(mv_class0_hp_prob))) 100 return false; 101 if (memchr(mv_hp_prob, 0, sizeof(mv_hp_prob))) 102 return false; 103 104 return true; 105 } 106 107 Vp9Parser::Context::Vp9FrameContextManager::Vp9FrameContextManager() 108 : weak_ptr_factory_(this) {} 109 110 Vp9Parser::Context::Vp9FrameContextManager::~Vp9FrameContextManager() {} 111 112 const Vp9FrameContext& 113 Vp9Parser::Context::Vp9FrameContextManager::frame_context() const { 114 DCHECK(initialized_); 115 DCHECK(!needs_client_update_); 116 return frame_context_; 117 } 118 119 void Vp9Parser::Context::Vp9FrameContextManager::Reset() { 120 initialized_ = false; 121 needs_client_update_ = false; 122 weak_ptr_factory_.InvalidateWeakPtrs(); 123 } 124 125 void Vp9Parser::Context::Vp9FrameContextManager::SetNeedsClientUpdate() { 126 DCHECK(!needs_client_update_); 127 initialized_ = true; 128 needs_client_update_ = true; 129 } 130 131 Vp9Parser::ContextRefreshCallback 132 Vp9Parser::Context::Vp9FrameContextManager::GetUpdateCb() { 133 if (needs_client_update_) 134 return base::Bind(&Vp9FrameContextManager::UpdateFromClient, 135 weak_ptr_factory_.GetWeakPtr()); 136 else 137 return Vp9Parser::ContextRefreshCallback(); 138 } 139 140 void Vp9Parser::Context::Vp9FrameContextManager::Update( 141 const Vp9FrameContext& frame_context) { 142 // DCHECK because we can trust values from our parser. 143 DCHECK(frame_context.IsValid()); 144 initialized_ = true; 145 frame_context_ = frame_context; 146 147 // For frame context we are updating, it may be still awaiting previous 148 // ContextRefreshCallback. Because we overwrite the value of context here and 149 // previous ContextRefreshCallback no longer matters, invalidate the weak ptr 150 // to prevent previous ContextRefreshCallback run. 151 // With this optimization, we may be able to parse more frames while previous 152 // are still decoding. 153 weak_ptr_factory_.InvalidateWeakPtrs(); 154 needs_client_update_ = false; 155 } 156 157 void Vp9Parser::Context::Vp9FrameContextManager::UpdateFromClient( 158 const Vp9FrameContext& frame_context) { 159 DVLOG(2) << "Got external frame_context update"; 160 DCHECK(needs_client_update_); 161 if (!frame_context.IsValid()) { 162 DLOG(ERROR) << "Invalid prob value in frame_context"; 163 return; 164 } 165 needs_client_update_ = false; 166 initialized_ = true; 167 frame_context_ = frame_context; 168 } 169 170 void Vp9Parser::Context::Reset() { 171 memset(&segmentation_, 0, sizeof(segmentation_)); 172 memset(&loop_filter_, 0, sizeof(loop_filter_)); 173 memset(&ref_slots_, 0, sizeof(ref_slots_)); 174 for (auto& manager : frame_context_managers_) 175 manager.Reset(); 176 } 177 178 void Vp9Parser::Context::MarkFrameContextForUpdate(size_t frame_context_idx) { 179 DCHECK_LT(frame_context_idx, arraysize(frame_context_managers_)); 180 frame_context_managers_[frame_context_idx].SetNeedsClientUpdate(); 181 } 182 183 void Vp9Parser::Context::UpdateFrameContext( 184 size_t frame_context_idx, 185 const Vp9FrameContext& frame_context) { 186 DCHECK_LT(frame_context_idx, arraysize(frame_context_managers_)); 187 frame_context_managers_[frame_context_idx].Update(frame_context); 188 } 189 190 const Vp9Parser::ReferenceSlot& Vp9Parser::Context::GetRefSlot( 191 size_t ref_type) const { 192 DCHECK_LT(ref_type, arraysize(ref_slots_)); 193 return ref_slots_[ref_type]; 194 } 195 196 void Vp9Parser::Context::UpdateRefSlot( 197 size_t ref_type, 198 const Vp9Parser::ReferenceSlot& ref_slot) { 199 DCHECK_LT(ref_type, arraysize(ref_slots_)); 200 ref_slots_[ref_type] = ref_slot; 201 } 202 203 Vp9Parser::Vp9Parser(bool parsing_compressed_header) 204 : parsing_compressed_header_(parsing_compressed_header) { 205 Reset(); 206 } 207 208 Vp9Parser::~Vp9Parser() {} 209 210 void Vp9Parser::SetStream(const uint8_t* stream, off_t stream_size) { 211 DCHECK(stream); 212 stream_ = stream; 213 bytes_left_ = stream_size; 214 frames_.clear(); 215 } 216 217 void Vp9Parser::Reset() { 218 stream_ = nullptr; 219 bytes_left_ = 0; 220 frames_.clear(); 221 curr_frame_info_.Reset(); 222 223 context_.Reset(); 224 } 225 226 Vp9Parser::Result Vp9Parser::ParseNextFrame(Vp9FrameHeader* fhdr) { 227 DCHECK(fhdr); 228 DVLOG(2) << "ParseNextFrame"; 229 230 // If |curr_frame_info_| is valid, uncompressed header was parsed into 231 // |curr_frame_header_| and we are awaiting context update to proceed with 232 // compressed header parsing. 233 if (!curr_frame_info_.IsValid()) { 234 if (frames_.empty()) { 235 // No frames to be decoded, if there is no more stream, request more. 236 if (!stream_) 237 return kEOStream; 238 239 // New stream to be parsed, parse it and fill frames_. 240 frames_ = ParseSuperframe(); 241 if (frames_.empty()) { 242 DVLOG(1) << "Failed parsing superframes"; 243 return kInvalidStream; 244 } 245 } 246 247 curr_frame_info_ = frames_.front(); 248 frames_.pop_front(); 249 250 memset(&curr_frame_header_, 0, sizeof(curr_frame_header_)); 251 252 Vp9UncompressedHeaderParser uncompressed_parser(&context_); 253 if (!uncompressed_parser.Parse(curr_frame_info_.ptr, curr_frame_info_.size, 254 &curr_frame_header_)) 255 return kInvalidStream; 256 257 if (curr_frame_header_.header_size_in_bytes == 0) { 258 // Verify padding bits are zero. 259 for (off_t i = curr_frame_header_.uncompressed_header_size; 260 i < curr_frame_info_.size; i++) { 261 if (curr_frame_info_.ptr[i] != 0) { 262 DVLOG(1) << "Padding bits are not zeros."; 263 return kInvalidStream; 264 } 265 } 266 *fhdr = curr_frame_header_; 267 curr_frame_info_.Reset(); 268 return kOk; 269 } 270 if (curr_frame_header_.uncompressed_header_size + 271 curr_frame_header_.header_size_in_bytes > 272 base::checked_cast<size_t>(curr_frame_info_.size)) { 273 DVLOG(1) << "header_size_in_bytes=" 274 << curr_frame_header_.header_size_in_bytes 275 << " is larger than bytes left in buffer: " 276 << curr_frame_info_.size - 277 curr_frame_header_.uncompressed_header_size; 278 return kInvalidStream; 279 } 280 } 281 282 if (parsing_compressed_header_) { 283 size_t frame_context_idx = curr_frame_header_.frame_context_idx; 284 const Context::Vp9FrameContextManager& context_to_load = 285 context_.frame_context_managers_[frame_context_idx]; 286 if (!context_to_load.initialized()) { 287 // 8.2 Frame order constraints 288 // must load an initialized set of probabilities. 289 DVLOG(1) << "loading uninitialized frame context, index=" 290 << frame_context_idx; 291 return kInvalidStream; 292 } 293 if (context_to_load.needs_client_update()) { 294 DVLOG(3) << "waiting frame_context_idx=" << frame_context_idx 295 << " to update"; 296 return kAwaitingRefresh; 297 } 298 curr_frame_header_.initial_frame_context = 299 curr_frame_header_.frame_context = context_to_load.frame_context(); 300 301 Vp9CompressedHeaderParser compressed_parser; 302 if (!compressed_parser.Parse( 303 curr_frame_info_.ptr + curr_frame_header_.uncompressed_header_size, 304 curr_frame_header_.header_size_in_bytes, &curr_frame_header_)) { 305 return kInvalidStream; 306 } 307 308 if (curr_frame_header_.refresh_frame_context) { 309 // In frame parallel mode, we can refresh the context without decoding 310 // tile data. 311 if (curr_frame_header_.frame_parallel_decoding_mode) { 312 context_.UpdateFrameContext(frame_context_idx, 313 curr_frame_header_.frame_context); 314 } else { 315 context_.MarkFrameContextForUpdate(frame_context_idx); 316 } 317 } 318 } 319 320 SetupSegmentationDequant(); 321 SetupLoopFilter(); 322 UpdateSlots(); 323 324 *fhdr = curr_frame_header_; 325 curr_frame_info_.Reset(); 326 return kOk; 327 } 328 329 Vp9Parser::ContextRefreshCallback Vp9Parser::GetContextRefreshCb( 330 size_t frame_context_idx) { 331 DCHECK_LT(frame_context_idx, arraysize(context_.frame_context_managers_)); 332 auto& frame_context_manager = 333 context_.frame_context_managers_[frame_context_idx]; 334 335 return frame_context_manager.GetUpdateCb(); 336 } 337 338 // Annex B Superframes 339 std::deque<Vp9Parser::FrameInfo> Vp9Parser::ParseSuperframe() { 340 const uint8_t* stream = stream_; 341 off_t bytes_left = bytes_left_; 342 343 // Make sure we don't parse stream_ more than once. 344 stream_ = nullptr; 345 bytes_left_ = 0; 346 347 if (bytes_left < 1) 348 return std::deque<FrameInfo>(); 349 350 // If this is a superframe, the last byte in the stream will contain the 351 // superframe marker. If not, the whole buffer contains a single frame. 352 uint8_t marker = *(stream + bytes_left - 1); 353 if ((marker & 0xe0) != 0xc0) { 354 return {FrameInfo(stream, bytes_left)}; 355 } 356 357 DVLOG(1) << "Parsing a superframe"; 358 359 // The bytes immediately before the superframe marker constitute superframe 360 // index, which stores information about sizes of each frame in it. 361 // Calculate its size and set index_ptr to the beginning of it. 362 size_t num_frames = (marker & 0x7) + 1; 363 size_t mag = ((marker >> 3) & 0x3) + 1; 364 off_t index_size = 2 + mag * num_frames; 365 366 if (bytes_left < index_size) 367 return std::deque<FrameInfo>(); 368 369 const uint8_t* index_ptr = stream + bytes_left - index_size; 370 if (marker != *index_ptr) 371 return std::deque<FrameInfo>(); 372 373 ++index_ptr; 374 bytes_left -= index_size; 375 376 // Parse frame information contained in the index and add a pointer to and 377 // size of each frame to frames. 378 std::deque<FrameInfo> frames; 379 for (size_t i = 0; i < num_frames; ++i) { 380 uint32_t size = 0; 381 for (size_t j = 0; j < mag; ++j) { 382 size |= *index_ptr << (j * 8); 383 ++index_ptr; 384 } 385 386 if (base::checked_cast<off_t>(size) > bytes_left) { 387 DVLOG(1) << "Not enough data in the buffer for frame " << i; 388 return std::deque<FrameInfo>(); 389 } 390 391 frames.push_back(FrameInfo(stream, size)); 392 stream += size; 393 bytes_left -= size; 394 395 DVLOG(1) << "Frame " << i << ", size: " << size; 396 } 397 398 return frames; 399 } 400 401 // 8.6.1 402 const size_t QINDEX_RANGE = 256; 403 const int16_t kDcQLookup[QINDEX_RANGE] = { 404 4, 8, 8, 9, 10, 11, 12, 12, 405 13, 14, 15, 16, 17, 18, 19, 19, 406 20, 21, 22, 23, 24, 25, 26, 26, 407 27, 28, 29, 30, 31, 32, 32, 33, 408 34, 35, 36, 37, 38, 38, 39, 40, 409 41, 42, 43, 43, 44, 45, 46, 47, 410 48, 48, 49, 50, 51, 52, 53, 53, 411 54, 55, 56, 57, 57, 58, 59, 60, 412 61, 62, 62, 63, 64, 65, 66, 66, 413 67, 68, 69, 70, 70, 71, 72, 73, 414 74, 74, 75, 76, 77, 78, 78, 79, 415 80, 81, 81, 82, 83, 84, 85, 85, 416 87, 88, 90, 92, 93, 95, 96, 98, 417 99, 101, 102, 104, 105, 107, 108, 110, 418 111, 113, 114, 116, 117, 118, 120, 121, 419 123, 125, 127, 129, 131, 134, 136, 138, 420 140, 142, 144, 146, 148, 150, 152, 154, 421 156, 158, 161, 164, 166, 169, 172, 174, 422 177, 180, 182, 185, 187, 190, 192, 195, 423 199, 202, 205, 208, 211, 214, 217, 220, 424 223, 226, 230, 233, 237, 240, 243, 247, 425 250, 253, 257, 261, 265, 269, 272, 276, 426 280, 284, 288, 292, 296, 300, 304, 309, 427 313, 317, 322, 326, 330, 335, 340, 344, 428 349, 354, 359, 364, 369, 374, 379, 384, 429 389, 395, 400, 406, 411, 417, 423, 429, 430 435, 441, 447, 454, 461, 467, 475, 482, 431 489, 497, 505, 513, 522, 530, 539, 549, 432 559, 569, 579, 590, 602, 614, 626, 640, 433 654, 668, 684, 700, 717, 736, 755, 775, 434 796, 819, 843, 869, 896, 925, 955, 988, 435 1022, 1058, 1098, 1139, 1184, 1232, 1282, 1336, 436 }; 437 438 const int16_t kAcQLookup[QINDEX_RANGE] = { 439 4, 8, 9, 10, 11, 12, 13, 14, 440 15, 16, 17, 18, 19, 20, 21, 22, 441 23, 24, 25, 26, 27, 28, 29, 30, 442 31, 32, 33, 34, 35, 36, 37, 38, 443 39, 40, 41, 42, 43, 44, 45, 46, 444 47, 48, 49, 50, 51, 52, 53, 54, 445 55, 56, 57, 58, 59, 60, 61, 62, 446 63, 64, 65, 66, 67, 68, 69, 70, 447 71, 72, 73, 74, 75, 76, 77, 78, 448 79, 80, 81, 82, 83, 84, 85, 86, 449 87, 88, 89, 90, 91, 92, 93, 94, 450 95, 96, 97, 98, 99, 100, 101, 102, 451 104, 106, 108, 110, 112, 114, 116, 118, 452 120, 122, 124, 126, 128, 130, 132, 134, 453 136, 138, 140, 142, 144, 146, 148, 150, 454 152, 155, 158, 161, 164, 167, 170, 173, 455 176, 179, 182, 185, 188, 191, 194, 197, 456 200, 203, 207, 211, 215, 219, 223, 227, 457 231, 235, 239, 243, 247, 251, 255, 260, 458 265, 270, 275, 280, 285, 290, 295, 300, 459 305, 311, 317, 323, 329, 335, 341, 347, 460 353, 359, 366, 373, 380, 387, 394, 401, 461 408, 416, 424, 432, 440, 448, 456, 465, 462 474, 483, 492, 501, 510, 520, 530, 540, 463 550, 560, 571, 582, 593, 604, 615, 627, 464 639, 651, 663, 676, 689, 702, 715, 729, 465 743, 757, 771, 786, 801, 816, 832, 848, 466 864, 881, 898, 915, 933, 951, 969, 988, 467 1007, 1026, 1046, 1066, 1087, 1108, 1129, 1151, 468 1173, 1196, 1219, 1243, 1267, 1292, 1317, 1343, 469 1369, 1396, 1423, 1451, 1479, 1508, 1537, 1567, 470 1597, 1628, 1660, 1692, 1725, 1759, 1793, 1828, 471 }; 472 473 static_assert(arraysize(kDcQLookup) == arraysize(kAcQLookup), 474 "quantizer lookup arrays of incorrect size"); 475 476 static size_t ClampQ(size_t q) { 477 return std::min(std::max(static_cast<size_t>(0), q), 478 arraysize(kDcQLookup) - 1); 479 } 480 481 // 8.6.1 Dequantization functions 482 size_t Vp9Parser::GetQIndex(const Vp9QuantizationParams& quant, 483 size_t segid) const { 484 const Vp9SegmentationParams& segmentation = context_.segmentation(); 485 486 if (segmentation.FeatureEnabled(segid, 487 Vp9SegmentationParams::SEG_LVL_ALT_Q)) { 488 int16_t feature_data = 489 segmentation.FeatureData(segid, Vp9SegmentationParams::SEG_LVL_ALT_Q); 490 size_t q_index = segmentation.abs_or_delta_update 491 ? feature_data 492 : quant.base_q_idx + feature_data; 493 return ClampQ(q_index); 494 } 495 496 return quant.base_q_idx; 497 } 498 499 // 8.6.1 Dequantization functions 500 void Vp9Parser::SetupSegmentationDequant() { 501 const Vp9QuantizationParams& quant = curr_frame_header_.quant_params; 502 Vp9SegmentationParams& segmentation = context_.segmentation_; 503 504 DLOG_IF(ERROR, curr_frame_header_.bit_depth > 8) 505 << "bit_depth > 8 is not supported " 506 "yet, kDcQLookup and kAcQLookup " 507 "need extended"; 508 if (segmentation.enabled) { 509 for (size_t i = 0; i < Vp9SegmentationParams::kNumSegments; ++i) { 510 const size_t q_index = GetQIndex(quant, i); 511 segmentation.y_dequant[i][0] = 512 kDcQLookup[ClampQ(q_index + quant.delta_q_y_dc)]; 513 segmentation.y_dequant[i][1] = kAcQLookup[ClampQ(q_index)]; 514 segmentation.uv_dequant[i][0] = 515 kDcQLookup[ClampQ(q_index + quant.delta_q_uv_dc)]; 516 segmentation.uv_dequant[i][1] = 517 kAcQLookup[ClampQ(q_index + quant.delta_q_uv_ac)]; 518 } 519 } else { 520 const size_t q_index = quant.base_q_idx; 521 segmentation.y_dequant[0][0] = 522 kDcQLookup[ClampQ(q_index + quant.delta_q_y_dc)]; 523 segmentation.y_dequant[0][1] = kAcQLookup[ClampQ(q_index)]; 524 segmentation.uv_dequant[0][0] = 525 kDcQLookup[ClampQ(q_index + quant.delta_q_uv_dc)]; 526 segmentation.uv_dequant[0][1] = 527 kAcQLookup[ClampQ(q_index + quant.delta_q_uv_ac)]; 528 } 529 } 530 531 static int ClampLf(int lf) { 532 const int kMaxLoopFilterLevel = 63; 533 return std::min(std::max(0, lf), kMaxLoopFilterLevel); 534 } 535 536 // 8.8.1 Loop filter frame init process 537 void Vp9Parser::SetupLoopFilter() { 538 Vp9LoopFilterParams& loop_filter = context_.loop_filter_; 539 if (!loop_filter.level) 540 return; 541 542 int scale = loop_filter.level < 32 ? 1 : 2; 543 544 for (size_t i = 0; i < Vp9SegmentationParams::kNumSegments; ++i) { 545 int level = loop_filter.level; 546 const Vp9SegmentationParams& segmentation = context_.segmentation(); 547 548 if (segmentation.FeatureEnabled(i, Vp9SegmentationParams::SEG_LVL_ALT_LF)) { 549 int feature_data = 550 segmentation.FeatureData(i, Vp9SegmentationParams::SEG_LVL_ALT_LF); 551 level = ClampLf(segmentation.abs_or_delta_update ? feature_data 552 : level + feature_data); 553 } 554 555 if (!loop_filter.delta_enabled) { 556 memset(loop_filter.lvl[i], level, sizeof(loop_filter.lvl[i])); 557 } else { 558 loop_filter.lvl[i][Vp9RefType::VP9_FRAME_INTRA][0] = ClampLf( 559 level + loop_filter.ref_deltas[Vp9RefType::VP9_FRAME_INTRA] * scale); 560 loop_filter.lvl[i][Vp9RefType::VP9_FRAME_INTRA][1] = 0; 561 562 for (size_t type = Vp9RefType::VP9_FRAME_LAST; 563 type < Vp9RefType::VP9_FRAME_MAX; ++type) { 564 for (size_t mode = 0; mode < Vp9LoopFilterParams::kNumModeDeltas; 565 ++mode) { 566 loop_filter.lvl[i][type][mode] = 567 ClampLf(level + loop_filter.ref_deltas[type] * scale + 568 loop_filter.mode_deltas[mode] * scale); 569 } 570 } 571 } 572 } 573 } 574 575 void Vp9Parser::UpdateSlots() { 576 // 8.10 Reference frame update process 577 for (size_t i = 0; i < kVp9NumRefFrames; i++) { 578 if (curr_frame_header_.RefreshFlag(i)) { 579 ReferenceSlot ref_slot; 580 ref_slot.initialized = true; 581 582 ref_slot.frame_width = curr_frame_header_.frame_width; 583 ref_slot.frame_height = curr_frame_header_.frame_height; 584 ref_slot.subsampling_x = curr_frame_header_.subsampling_x; 585 ref_slot.subsampling_y = curr_frame_header_.subsampling_y; 586 ref_slot.bit_depth = curr_frame_header_.bit_depth; 587 588 ref_slot.profile = curr_frame_header_.profile; 589 ref_slot.color_space = curr_frame_header_.color_space; 590 context_.UpdateRefSlot(i, ref_slot); 591 } 592 } 593 } 594 595 } // namespace media 596