1 /* ------------------------------------------------------------------ 2 * Copyright (C) 1998-2009 PacketVideo 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 13 * express or implied. 14 * See the License for the specific language governing permissions 15 * and limitations under the License. 16 * ------------------------------------------------------------------- 17 */ 18 /*********************************************************************************/ 19 /* ------------------------------------------------------------------- */ 20 /* MPEG-4 TimeToSampleAtom Class */ 21 /* ------------------------------------------------------------------- */ 22 /*********************************************************************************/ 23 /* 24 This TimeToSampleAtom Class contains a compact version of a table that allows 25 indexing from decoding to sample number. 26 */ 27 28 29 #define IMPLEMENT_TimeToSampleAtom 30 31 #include "timetosampleatom.h" 32 #include "atomutils.h" 33 #include "atomdefs.h" 34 35 // Stream-in ctor 36 TimeToSampleAtom::TimeToSampleAtom(MP4_FF_FILE *fp, 37 uint32 mediaType, 38 uint32 size, 39 uint32 type, 40 OSCL_wString& filename, 41 uint32 parsingMode) 42 : FullAtom(fp, size, type) 43 { 44 45 _psampleCountVec = NULL; 46 _psampleDeltaVec = NULL; 47 48 _currGetSampleCount = 0; 49 _currGetIndex = -1; 50 _currGetTimeDelta = 0; 51 _currPeekSampleCount = 0; 52 _currPeekIndex = -1; 53 _currPeekTimeDelta = 0; 54 55 _mediaType = mediaType; 56 _parsed_entry_cnt = 0; 57 _fileptr = NULL; 58 _parsing_mode = 0; 59 _parsing_mode = parsingMode; 60 61 _stbl_buff_size = MAX_CACHED_TABLE_ENTRIES_FILE; 62 _next_buff_number = 0; 63 _curr_buff_number = 0; 64 _curr_entry_point = 0; 65 _stbl_fptr_vec = NULL; 66 67 iLogger = PVLogger::GetLoggerObject("mp4ffparser"); 68 iStateVarLogger = PVLogger::GetLoggerObject("mp4ffparser_mediasamplestats"); 69 iParsedDataLogger = PVLogger::GetLoggerObject("mp4ffparser_parseddata"); 70 71 72 if (_success) 73 { 74 if (!AtomUtils::read32(fp, _entryCount)) 75 { 76 _success = false; 77 } 78 PVMF_MP4FFPARSER_LOGPARSEDINFO((0, "TimeToSampleAtom::TimeToSampleAtom- _entryCount =%d", _entryCount)); 79 uint32 dataSize = _size - (DEFAULT_FULL_ATOM_SIZE + 4); 80 81 uint32 entrySize = (4 + 4); 82 83 if ((_entryCount*entrySize) > dataSize) 84 { 85 _success = false; 86 } 87 88 if (_success) 89 { 90 if (_entryCount > 0) 91 { 92 93 if (parsingMode == 1) 94 { 95 // cache size is 4K so that optimization should work if entry_count is greater than 4K 96 if ((_entryCount > _stbl_buff_size)) 97 { 98 uint32 fptrBuffSize = (_entryCount / _stbl_buff_size) + 1; 99 100 PV_MP4_FF_ARRAY_NEW(NULL, uint32, (fptrBuffSize), _stbl_fptr_vec); 101 if (_stbl_fptr_vec == NULL) 102 { 103 _success = false; 104 _mp4ErrorCode = MEMORY_ALLOCATION_FAILED; 105 return; 106 } 107 108 PV_MP4_FF_ARRAY_NEW(NULL, uint32, (_stbl_buff_size), _psampleCountVec); 109 if (_psampleCountVec == NULL) 110 { 111 _success = false; 112 _mp4ErrorCode = MEMORY_ALLOCATION_FAILED; 113 return; 114 } 115 PV_MP4_FF_ARRAY_NEW(NULL, uint32, (_stbl_buff_size), _psampleDeltaVec); 116 if (_psampleDeltaVec == NULL) 117 { 118 PV_MP4_ARRAY_DELETE(NULL, _psampleDeltaVec); 119 _psampleDeltaVec = NULL; 120 _success = false; 121 _mp4ErrorCode = MEMORY_ALLOCATION_FAILED; 122 return; 123 } 124 for (uint32 idx = 0; idx < _stbl_buff_size; idx++) //initialization 125 { 126 _psampleCountVec[idx] = 0; 127 _psampleDeltaVec[idx] = 0; 128 } 129 130 OsclAny* ptr = (MP4_FF_FILE *)(oscl_malloc(sizeof(MP4_FF_FILE))); 131 if (ptr == NULL) 132 { 133 _success = false; 134 _mp4ErrorCode = MEMORY_ALLOCATION_FAILED; 135 return; 136 } 137 _fileptr = OSCL_PLACEMENT_NEW(ptr, MP4_FF_FILE()); 138 _fileptr->_fileServSession = fp->_fileServSession; 139 _fileptr->_pvfile.SetCPM(fp->_pvfile.GetCPM()); 140 if (AtomUtils::OpenMP4File(filename, 141 Oscl_File::MODE_READ | Oscl_File::MODE_BINARY, 142 _fileptr) != 0) 143 { 144 _success = false; 145 _mp4ErrorCode = FILE_OPEN_FAILED; 146 } 147 148 _fileptr->_fileSize = fp->_fileSize; 149 150 int32 _head_offset = AtomUtils::getCurrentFilePosition(fp); 151 AtomUtils::seekFromCurrPos(fp, dataSize); 152 AtomUtils::seekFromStart(_fileptr, _head_offset); 153 return; 154 } 155 else 156 { 157 _parsing_mode = 0; 158 _stbl_buff_size = _entryCount; 159 } 160 } 161 else 162 { 163 _stbl_buff_size = _entryCount; 164 } 165 166 PV_MP4_FF_ARRAY_NEW(NULL, uint32, (_entryCount), _psampleCountVec); 167 if (_psampleCountVec == NULL) 168 { 169 _success = false; 170 _mp4ErrorCode = MEMORY_ALLOCATION_FAILED; 171 return; 172 } 173 PV_MP4_FF_ARRAY_NEW(NULL, uint32, (_entryCount), _psampleDeltaVec); 174 if (_psampleDeltaVec == NULL) 175 { 176 PV_MP4_ARRAY_DELETE(NULL, _psampleDeltaVec); 177 _psampleDeltaVec = NULL; 178 _success = false; 179 _mp4ErrorCode = MEMORY_ALLOCATION_FAILED; 180 return; 181 } 182 for (uint32 idx = 0; idx < _entryCount; idx++) //initialization 183 { 184 _psampleCountVec[idx] = 0; 185 _psampleDeltaVec[idx] = 0; 186 } 187 188 uint32 number = 0; 189 uint32 delta = 0; 190 for (_parsed_entry_cnt = 0; _parsed_entry_cnt < _entryCount; _parsed_entry_cnt++) 191 { 192 if (!AtomUtils::read32(fp, number)) 193 { 194 _success = false; 195 break; 196 } 197 if (!AtomUtils::read32(fp, delta)) 198 { 199 _success = false; 200 break; 201 } 202 _psampleCountVec[_parsed_entry_cnt] = (number); 203 _psampleDeltaVec[_parsed_entry_cnt] = (delta); 204 } 205 } 206 } 207 208 if (!_success) 209 { 210 _mp4ErrorCode = READ_TIME_TO_SAMPLE_ATOM_FAILED; 211 } 212 } 213 else 214 { 215 if (_mp4ErrorCode != ATOM_VERSION_NOT_SUPPORTED) 216 _mp4ErrorCode = READ_TIME_TO_SAMPLE_ATOM_FAILED; 217 } 218 } 219 220 bool TimeToSampleAtom::ParseEntryUnit(uint32 entry_cnt) 221 { 222 223 const uint32 threshold = 1024; 224 entry_cnt += threshold; 225 226 if (entry_cnt > _entryCount) 227 entry_cnt = _entryCount; 228 229 uint32 number, delta; 230 while (_parsed_entry_cnt < entry_cnt) 231 { 232 _curr_entry_point = _parsed_entry_cnt % _stbl_buff_size; 233 _curr_buff_number = _parsed_entry_cnt / _stbl_buff_size; 234 235 if (_curr_buff_number == _next_buff_number) 236 { 237 uint32 currFilePointer = AtomUtils::getCurrentFilePosition(_fileptr); 238 _stbl_fptr_vec[_curr_buff_number] = currFilePointer; 239 _next_buff_number++; 240 } 241 242 if (!_curr_entry_point) 243 { 244 uint32 currFilePointer = _stbl_fptr_vec[_curr_buff_number]; 245 AtomUtils::seekFromStart(_fileptr, currFilePointer); 246 } 247 248 if (!AtomUtils::read32(_fileptr, number)) 249 { 250 return false; 251 } 252 if (!AtomUtils::read32(_fileptr, delta)) 253 { 254 return false; 255 } 256 _psampleCountVec[_curr_entry_point] = (number); 257 _psampleDeltaVec[_curr_entry_point] = (delta); 258 _parsed_entry_cnt++; 259 } 260 return true; 261 } 262 263 // Destructor 264 TimeToSampleAtom::~TimeToSampleAtom() 265 { 266 if (_psampleCountVec != NULL) 267 PV_MP4_ARRAY_DELETE(NULL, _psampleCountVec); 268 269 if (_psampleDeltaVec != NULL) 270 PV_MP4_ARRAY_DELETE(NULL, _psampleDeltaVec); 271 272 if (_stbl_fptr_vec != NULL) 273 PV_MP4_ARRAY_DELETE(NULL, _stbl_fptr_vec); 274 275 if (_fileptr != NULL) 276 { 277 if (_fileptr->IsOpen()) 278 { 279 AtomUtils::CloseMP4File(_fileptr); 280 } 281 oscl_free(_fileptr); 282 } 283 } 284 285 // Return the number of samples at index 286 uint32 287 TimeToSampleAtom::getSampleCountAt(int32 index) 288 { 289 if (_psampleCountVec == NULL) 290 { 291 return (uint32) PV_ERROR; 292 } 293 294 if (index < (int32)_entryCount) 295 { 296 if (_parsing_mode == 1) 297 CheckAndParseEntry(index); 298 299 return (int32)(_psampleCountVec[index%_stbl_buff_size]); 300 } 301 else 302 { 303 PVMF_MP4FFPARSER_LOGERROR((0, "ERROR =>TimeToSampleAtom::getSampleCountAt index = %d", index)); 304 return (uint32) PV_ERROR; 305 } 306 } 307 308 // Return sample delta at index 309 int32 310 TimeToSampleAtom::getSampleDeltaAt(int32 index) 311 { 312 if (_psampleDeltaVec == NULL) 313 { 314 return PV_ERROR; 315 } 316 317 if (index < (int32)_entryCount) 318 { 319 if (_parsing_mode == 1) 320 CheckAndParseEntry(index); 321 322 return (int32)(_psampleDeltaVec[index%_stbl_buff_size]); 323 } 324 else 325 { 326 PVMF_MP4FFPARSER_LOGERROR((0, "ERROR =>TimeToSampleAtom::getSampleDeltaAt index = %d", index)); 327 return PV_ERROR; 328 } 329 } 330 331 // Return the samples corresponding to the timestamp ts. If there is not a sample 332 // exactly at ts, the very next sample is used. 333 // This atom maintains timestamp deltas between samples, i.e. delta[i] is the 334 // timestamp difference between sample[i] and sample[i+1]. It is implicit that 335 // sample[0] occurs at timestamp ts=0. 336 int32 337 TimeToSampleAtom::getSampleNumberFromTimestamp(uint32 ts, bool oAlwaysRetSampleCount) 338 { 339 // It is assumed that sample 0 has a ts of 0 - i.e. the first 340 // entry in the table starts with the delta between sample 1 and sample 0 341 uint32 sampleCount = 0; 342 uint32 timeCount = 0; 343 344 if ((_psampleDeltaVec == NULL) || 345 (_psampleCountVec == NULL) || 346 (_entryCount == 0)) 347 { 348 return PV_ERROR; 349 } 350 351 for (uint32 i = 0; i < _entryCount; i++) 352 { 353 if (_parsing_mode == 1) 354 CheckAndParseEntry(i); 355 356 if (ts < timeCount) 357 { // found range that the sample is in - need to backtrack 358 if (_parsing_mode == 1) 359 CheckAndParseEntry(i - 1); 360 361 uint32 samples = _psampleCountVec[(i-1)%_stbl_buff_size]; 362 sampleCount -= samples; 363 timeCount -= _psampleDeltaVec[(i-1)%_stbl_buff_size] * samples; 364 while (timeCount <= ts) 365 { 366 timeCount += _psampleDeltaVec[(i-1)%_stbl_buff_size]; 367 sampleCount += 1; 368 } 369 370 if (timeCount > ts) 371 { 372 if (sampleCount > 0) 373 { 374 sampleCount--; 375 } 376 return sampleCount; 377 } 378 } 379 else if (ts == timeCount) 380 { // Found sample at ts 381 return sampleCount; 382 } 383 else 384 { // Sample not yet found - advance 385 uint32 samples = _psampleCountVec[i%_stbl_buff_size]; //number of samples at this index 386 sampleCount += samples; 387 timeCount += _psampleDeltaVec[i%_stbl_buff_size] * samples; 388 } 389 } 390 391 // Timestamp is in last run of samples (or possibly beyond) 392 // - need to backtrack to beginning of last run to find it 393 uint32 samples = _psampleCountVec[(_entryCount-1)%_stbl_buff_size]; 394 uint32 delta = _psampleDeltaVec[(_entryCount-1)%_stbl_buff_size]; 395 sampleCount -= samples; 396 timeCount -= delta * samples; 397 398 for (uint32 count = 0; count < (samples - 1); count++) 399 { 400 timeCount += delta; 401 sampleCount += 1; 402 403 if (timeCount > ts) 404 { 405 if (sampleCount > 0) 406 { 407 sampleCount--; 408 } 409 return sampleCount; 410 } 411 else if (timeCount == ts) 412 { 413 return sampleCount; 414 } 415 } 416 417 418 sampleCount += 1; 419 if (ts >= timeCount) 420 { 421 if (oAlwaysRetSampleCount) 422 { 423 return sampleCount; 424 } 425 else 426 { 427 if (_mediaType == MEDIA_TYPE_VISUAL) 428 { 429 return sampleCount; 430 } 431 } 432 } 433 434 // Went past last sample in last run of samples - not a valid timestamp 435 return PV_ERROR; 436 } 437 438 // Returns the timestamp (ms) for the current sample given by num. This is used when 439 // randomly accessing a frame and the timestamp has not been accumulated. It is implicit 440 // that sample[0] occurs at timestamp ts=0. 441 int32 TimeToSampleAtom::getTimestampForSampleNumber(uint32 num) 442 { 443 if ((_psampleDeltaVec == NULL) || 444 (_psampleCountVec == NULL) || 445 (_entryCount == 0)) 446 { 447 return PV_ERROR; 448 } 449 // It is assumed that sample 0 has a ts of 0 - i.e. the first 450 // entry in the table starts with the delta between sample 1 and sample 0 451 if (num == 0) 452 return 0; 453 454 uint32 sampleCount = 0; 455 int32 ts = 0; // Timestamp value to return 456 for (uint32 i = 0; i < _entryCount; i++) 457 { 458 if (_parsing_mode == 1) 459 CheckAndParseEntry(i); 460 461 if (num <= (sampleCount + _psampleCountVec[i%_stbl_buff_size])) 462 { // Sample num within current entry 463 int32 count = num - sampleCount; 464 ts += _psampleDeltaVec[i%_stbl_buff_size] * count; 465 PVMF_MP4FFPARSER_LOGMEDIASAMPELSTATEVARIABLES((0, "TimeToSampleAtom::getTimestampForSampleNumber- Time Stamp =%d", ts)); 466 return ts; 467 } 468 else 469 { 470 sampleCount += _psampleCountVec[i%_stbl_buff_size]; 471 ts += (_psampleDeltaVec[i%_stbl_buff_size] * _psampleCountVec[i%_stbl_buff_size]); 472 } 473 } 474 475 // Went past end of list - not a valid sample number 476 return PV_ERROR; 477 } 478 479 // Returns the timestamp delta (ms) for the current sample given by num. This value 480 // is the difference in timestamps between the sample (num) and the previous sample 481 // in the track. It is implicit that sample[0] occurs at timestamp ts=0. 482 int32 483 TimeToSampleAtom::getTimeDeltaForSampleNumber(uint32 num) 484 { 485 // It is assumed that sample 0 has a ts of 0 - i.e. the first 486 // entry in the table starts with the delta between sample 1 and sample 0 487 if ((_psampleDeltaVec == NULL) || 488 (_psampleCountVec == NULL) || 489 (_entryCount == 0)) 490 { 491 return PV_ERROR; 492 } 493 494 if (num == 0) 495 return 0; 496 497 uint32 sampleCount = 0; 498 for (uint32 i = 0; i < _entryCount; i++) 499 { 500 if (_parsing_mode == 1) 501 CheckAndParseEntry(i); 502 503 if (num <= (sampleCount + _psampleCountVec[i%_stbl_buff_size])) 504 { // Sample num within current entry 505 return (_psampleDeltaVec[i%_stbl_buff_size]); 506 } 507 else 508 { 509 sampleCount += _psampleCountVec[i%_stbl_buff_size]; 510 } 511 } 512 513 // Went past end of list - not a valid sample number 514 return PV_ERROR; 515 } 516 517 518 519 520 // Returns the timestamp delta (ms) for the current sample given by num. This value 521 // is the difference in timestamps between the sample (num) and the previous sample 522 // in the track. It is implicit that sample[0] occurs at timestamp ts=0. 523 int32 524 TimeToSampleAtom::getTimeDeltaForSampleNumberPeek(uint32 sampleNum) 525 { 526 // It is assumed that sample 0 has a ts of 0 - i.e. the first 527 // entry in the table starts with the delta between sample 1 and sample 0 528 if ((_psampleDeltaVec == NULL) || 529 (_psampleCountVec == NULL) || 530 (_entryCount == 0)) 531 { 532 return PV_ERROR; 533 } 534 535 // note that sampleNum is a zero based index while _currGetSampleCount is 1 based index 536 if (sampleNum < _currPeekSampleCount) 537 { 538 return (_currPeekTimeDelta); 539 } 540 else 541 { 542 do 543 { 544 _currPeekIndex++; 545 if (_parsing_mode) 546 CheckAndParseEntry(_currPeekIndex); 547 548 _currPeekSampleCount += _psampleCountVec[_currPeekIndex%_stbl_buff_size]; 549 _currPeekTimeDelta = _psampleDeltaVec[_currPeekIndex%_stbl_buff_size]; 550 } 551 while (_currPeekSampleCount == 0); 552 553 PVMF_MP4FFPARSER_LOGMEDIASAMPELSTATEVARIABLES((0, "TimeToSampleAtom::getTimeDeltaForSampleNumberPeek- _currPeekIndex =%d", _currPeekIndex)); 554 PVMF_MP4FFPARSER_LOGMEDIASAMPELSTATEVARIABLES((0, "TimeToSampleAtom::getTimeDeltaForSampleNumberPeek- _currPeekSampleCount =%d", _currPeekSampleCount)); 555 PVMF_MP4FFPARSER_LOGMEDIASAMPELSTATEVARIABLES((0, "TimeToSampleAtom::getTimeDeltaForSampleNumberPeek- _currPeekTimeDelta =%d", _currPeekTimeDelta)); 556 557 if (sampleNum < _currPeekSampleCount) 558 { 559 return (_currPeekTimeDelta); 560 } 561 else 562 { 563 PVMF_MP4FFPARSER_LOGERROR((0, "ERROR =>TimeToSampleAtom::getTimeDeltaForSampleNumberPeek sampleNum = %d", sampleNum)); 564 return (PV_ERROR); 565 } 566 } 567 568 569 } 570 571 // Returns the timestamp delta (ms) for the current sample given by num. This value 572 // is the difference in timestamps between the sample (num) and the previous sample 573 // in the track. It is implicit that sample[0] occurs at timestamp ts=0. 574 int32 575 TimeToSampleAtom::getTimeDeltaForSampleNumberGet(uint32 sampleNum) 576 { 577 // It is assumed that sample 0 has a ts of 0 - i.e. the first 578 // entry in the table starts with the delta between sample 1 and sample 0 579 if ((_psampleDeltaVec == NULL) || 580 (_psampleCountVec == NULL) || 581 (_entryCount == 0)) 582 { 583 return PV_ERROR; 584 } 585 586 // note that sampleNum is a zero based index while _currGetSampleCount is 1 based index 587 if (sampleNum < _currGetSampleCount) 588 { 589 return (_currGetTimeDelta); 590 } 591 else 592 { 593 594 do 595 { 596 _currGetIndex++; 597 if (_parsing_mode) 598 CheckAndParseEntry(_currGetIndex); 599 _currGetSampleCount += _psampleCountVec[_currGetIndex%_stbl_buff_size]; 600 _currGetTimeDelta = _psampleDeltaVec[_currGetIndex%_stbl_buff_size]; 601 } 602 while (_currGetSampleCount == 0); 603 604 PVMF_MP4FFPARSER_LOGMEDIASAMPELSTATEVARIABLES((0, "TimeToSampleAtom::getTimeDeltaForSampleNumberGet- _currGetIndex =%d", _currGetIndex)); 605 PVMF_MP4FFPARSER_LOGMEDIASAMPELSTATEVARIABLES((0, "TimeToSampleAtom::getTimeDeltaForSampleNumberGet- _currGetSampleCount =%d", _currGetSampleCount)); 606 PVMF_MP4FFPARSER_LOGMEDIASAMPELSTATEVARIABLES((0, "TimeToSampleAtom::getTimeDeltaForSampleNumberGet- _currGetTimeDelta =%d", _currGetTimeDelta)); 607 608 if (sampleNum < _currGetSampleCount) 609 { 610 return (_currGetTimeDelta); 611 } 612 else 613 { 614 PVMF_MP4FFPARSER_LOGERROR((0, "ERROR =>TimeToSampleAtom::getTimeDeltaForSampleNumberGet sampleNum = %d", sampleNum)); 615 return (PV_ERROR); 616 } 617 } 618 619 620 } 621 622 int32 623 TimeToSampleAtom::resetStateVariables() 624 { 625 uint32 sampleNum = 0; 626 return (resetStateVariables(sampleNum)); 627 } 628 629 int32 630 TimeToSampleAtom::resetStateVariables(uint32 sampleNum) 631 { 632 _currGetSampleCount = 0; 633 _currGetIndex = -1; 634 _currGetTimeDelta = 0; 635 _currPeekSampleCount = 0; 636 _currPeekIndex = -1; 637 _currPeekTimeDelta = 0; 638 639 // It is assumed that sample 0 has a ts of 0 - i.e. the first 640 // entry in the table starts with the delta between sample 1 and sample 0 641 if ((_psampleDeltaVec == NULL) || 642 (_psampleCountVec == NULL) || 643 (_entryCount == 0)) 644 { 645 return PV_ERROR; 646 } 647 648 649 for (uint32 i = 0; i < _entryCount; i++) 650 { 651 if (_parsing_mode) 652 CheckAndParseEntry(i); 653 654 _currPeekIndex++; 655 _currPeekSampleCount += _psampleCountVec[i%_stbl_buff_size]; 656 _currPeekTimeDelta = _psampleDeltaVec[i%_stbl_buff_size]; 657 658 _currGetIndex++; 659 _currGetSampleCount += _psampleCountVec[i%_stbl_buff_size]; 660 _currGetTimeDelta = _psampleDeltaVec[i%_stbl_buff_size]; 661 662 if (sampleNum <= _currPeekSampleCount) 663 { 664 return (EVERYTHING_FINE); 665 } 666 667 } 668 669 // Went past end of list - not a valid sample number 670 return PV_ERROR; 671 } 672 673 int32 TimeToSampleAtom::resetPeekwithGet() 674 { 675 _currPeekSampleCount = _currGetSampleCount; 676 _currPeekIndex = _currGetIndex ; 677 _currPeekTimeDelta = _currGetTimeDelta; 678 return (EVERYTHING_FINE); 679 } 680 681 void TimeToSampleAtom::CheckAndParseEntry(uint32 i) 682 { 683 if (i >= _parsed_entry_cnt) 684 { 685 ParseEntryUnit(i); 686 } 687 else 688 { 689 uint32 entryLoc = i / _stbl_buff_size; 690 if (_curr_buff_number != entryLoc) 691 { 692 _parsed_entry_cnt = entryLoc * _stbl_buff_size; 693 while (_parsed_entry_cnt <= i) 694 ParseEntryUnit(_parsed_entry_cnt); 695 } 696 } 697 } 698 699