1 /* 2 * Copyright © 2000-2004 Ingo Weinhold <ingo_weinhold@gmx.de> 3 * Copyright © 2006-2008 Stephan Aßmus <superstippi@gmx.de> 4 * All rights reserved. Distributed under the terms of the MIT License. 5 */ 6 #include "MediaTrackAudioSupplier.h" 7 8 #include <new> 9 #include <algorithm> 10 #include <stdio.h> 11 #include <string.h> 12 13 #include <MediaFile.h> 14 #include <MediaTrack.h> 15 16 using namespace std; 17 18 //#define TRACE_AUDIO_SUPPLIER 19 #ifdef TRACE_AUDIO_SUPPLIER 20 # define TRACE(x...) printf("MediaTrackAudioSupplier::" x) 21 #else 22 # define TRACE(x...) 23 #endif 24 25 26 // #pragma mark - Buffer 27 28 29 struct MediaTrackAudioSupplier::Buffer { 30 void* data; 31 int64 offset; 32 int64 size; 33 bigtime_t time_stamp; 34 35 static int CompareOffset(const void* a, const void* b); 36 }; 37 38 39 int 40 MediaTrackAudioSupplier::Buffer::CompareOffset(const void* a, const void* b) 41 { 42 const Buffer* buffer1 = *(const Buffer**)a; 43 const Buffer* buffer2 = *(const Buffer**)b; 44 int result = 0; 45 if (buffer1->offset < buffer2->offset) 46 result = -1; 47 else if (buffer1->offset > buffer2->offset) 48 result = 1; 49 return result; 50 } 51 52 53 // #pragma mark - MediaTrackAudioSupplier 54 55 56 MediaTrackAudioSupplier::MediaTrackAudioSupplier(BMediaTrack* mediaTrack, 57 int32 trackIndex) 58 : 59 AudioTrackSupplier(), 60 fMediaTrack(mediaTrack), 61 fBuffer(NULL), 62 fBufferOffset(0), 63 fBufferSize(0), 64 fBuffers(10), 65 fHasKeyFrames(false), 66 fCountFrames(0), 67 fReportSeekError(true), 68 fTrackIndex(trackIndex) 69 { 70 _InitFromTrack(); 71 } 72 73 74 MediaTrackAudioSupplier::~MediaTrackAudioSupplier() 75 { 76 _FreeBuffers(); 77 delete[] fBuffer; 78 } 79 80 81 const media_format& 82 MediaTrackAudioSupplier::Format() const 83 { 84 return AudioReader::Format(); 85 } 86 87 88 status_t 89 MediaTrackAudioSupplier::GetEncodedFormat(media_format* format) const 90 { 91 if (!fMediaTrack) 92 return B_NO_INIT; 93 return fMediaTrack->EncodedFormat(format); 94 } 95 96 97 status_t 98 MediaTrackAudioSupplier::GetCodecInfo(media_codec_info* info) const 99 { 100 if (!fMediaTrack) 101 return B_NO_INIT; 102 return fMediaTrack->GetCodecInfo(info); 103 } 104 105 106 bigtime_t 107 MediaTrackAudioSupplier::Duration() const 108 { 109 if (!fMediaTrack) 110 return 0; 111 112 return fMediaTrack->Duration(); 113 } 114 115 116 // #pragma mark - AudioReader 117 118 119 bigtime_t 120 MediaTrackAudioSupplier::InitialLatency() const 121 { 122 // TODO: this is just a wild guess, and not really founded on anything. 123 return 100000; 124 } 125 126 127 status_t 128 MediaTrackAudioSupplier::Read(void* buffer, int64 pos, int64 frames) 129 { 130 TRACE("Read(%p, %lld, %lld)\n", buffer, pos, 131 frames); 132 TRACE(" this: %p, fOutOffset: %lld\n", this, fOutOffset); 133 134 //printf("MediaTrackAudioSupplier::Read(%p, %lld, %lld)\n", buffer, pos, frames); 135 136 status_t error = InitCheck(); 137 if (error != B_OK) { 138 TRACE("Read() InitCheck failed\n"); 139 return error; 140 } 141 142 // convert pos according to our offset 143 pos += fOutOffset; 144 // Fill the frames after the end of the track with silence. 145 if (fCountFrames > 0 && pos + frames > fCountFrames) { 146 int64 size = max(0LL, fCountFrames - pos); 147 ReadSilence(SkipFrames(buffer, size), frames - size); 148 frames = size; 149 } 150 151 TRACE(" after eliminating the frames after the track end: %p, %lld, %lld\n", 152 buffer, pos, frames); 153 154 #if 0 155 const media_format& format = Format(); 156 int64 size = format.u.raw_audio.buffer_size; 157 uint32 bytesPerFrame = format.u.raw_audio.channel_count 158 * (format.u.raw_audio.format 159 & media_raw_audio_format::B_AUDIO_SIZE_MASK); 160 uint32 framesPerBuffer = size / bytesPerFrame; 161 162 if (fMediaTrack->CurrentFrame() != pos) { 163 printf(" needing to seek: %lld (%lld)\n", pos, 164 fMediaTrack->CurrentFrame()); 165 166 int64 keyFrame = pos; 167 error = fMediaTrack->FindKeyFrameForFrame(&keyFrame, 168 B_MEDIA_SEEK_CLOSEST_BACKWARD); 169 if (error == B_OK) { 170 error = fMediaTrack->SeekToFrame(&keyFrame, 171 B_MEDIA_SEEK_CLOSEST_BACKWARD); 172 } 173 if (error != B_OK) { 174 printf(" error seeking to position: %lld (%lld)\n", pos, 175 fMediaTrack->CurrentFrame()); 176 177 return error; 178 } 179 180 if (keyFrame < pos) { 181 printf(" need to skip %lld frames\n", pos - keyFrame); 182 uint8 dummyBuffer[size]; 183 while (pos - keyFrame >= framesPerBuffer) { 184 printf(" skipped %lu frames (full buffer)\n", framesPerBuffer); 185 int64 sizeToRead = size; 186 fMediaTrack->ReadFrames(dummyBuffer, &sizeToRead); 187 keyFrame += framesPerBuffer; 188 } 189 int64 restSize = pos - keyFrame; 190 if (restSize > 0) { 191 printf(" skipped %lu frames (rest)\n", framesPerBuffer); 192 fMediaTrack->ReadFrames(dummyBuffer, &restSize); 193 } 194 } 195 } 196 while (frames > 0) { 197 printf(" reading %lu frames (full buffer)\n", framesPerBuffer); 198 int64 sizeToRead = min_c(size, frames * bytesPerFrame); 199 fMediaTrack->ReadFrames(buffer, &sizeToRead); 200 buffer = (uint8*)buffer + sizeToRead; 201 frames -= framesPerBuffer; 202 } 203 printf(" done\n\n"); 204 205 #else 206 // read the cached frames 207 bigtime_t time = system_time(); 208 if (frames > 0) 209 _ReadCachedFrames(buffer, pos, frames, time); 210 211 TRACE(" after reading from cache: %p, %lld, %lld\n", buffer, pos, frames); 212 213 // read the remaining (uncached) frames 214 if (frames > 0) 215 _ReadUncachedFrames(buffer, pos, frames, time); 216 217 #endif 218 TRACE("Read() done\n"); 219 220 return B_OK; 221 } 222 223 // InitCheck 224 status_t 225 MediaTrackAudioSupplier::InitCheck() const 226 { 227 status_t error = AudioReader::InitCheck(); 228 if (error == B_OK && (!fMediaTrack || !fBuffer)) 229 error = B_NO_INIT; 230 return error; 231 } 232 233 // #pragma mark - 234 235 // _InitFromTrack 236 void 237 MediaTrackAudioSupplier::_InitFromTrack() 238 { 239 TRACE("_InitFromTrack()\n"); 240 // Try to suggest a big buffer size, we do a lot of caching... 241 fFormat.u.raw_audio.buffer_size = 16384; 242 if (fMediaTrack == NULL || fMediaTrack->DecodedFormat(&fFormat) != B_OK 243 || fFormat.type != B_MEDIA_RAW_AUDIO) { 244 fMediaTrack = NULL; 245 return; 246 } 247 248 #ifdef TRACE_AUDIO_SUPPLIER 249 char formatString[256]; 250 string_for_format(fFormat, formatString, 256); 251 TRACE("_InitFromTrack(): format is: %s\n", formatString); 252 TRACE("_InitFromTrack(): buffer size: %ld\n", 253 fFormat.u.raw_audio.buffer_size); 254 #endif 255 256 fBuffer = new (nothrow) char[fFormat.u.raw_audio.buffer_size]; 257 _AllocateBuffers(); 258 259 // Find out, if the track has key frames: as a heuristic we 260 // check, if the first and the second frame have the same backward 261 // key frame. 262 // Note: It shouldn't harm that much, if we're wrong and the 263 // track has key frame although we found out that it has not. 264 #if 0 265 int64 keyFrame0 = 0; 266 int64 keyFrame1 = 1; 267 fMediaTrack->FindKeyFrameForFrame(&keyFrame0, 268 B_MEDIA_SEEK_CLOSEST_BACKWARD); 269 fMediaTrack->FindKeyFrameForFrame(&keyFrame1, 270 B_MEDIA_SEEK_CLOSEST_BACKWARD); 271 fHasKeyFrames = (keyFrame0 == keyFrame1); 272 #else 273 fHasKeyFrames = true; 274 #endif 275 276 // get the length of the track 277 fCountFrames = fMediaTrack->CountFrames(); 278 279 TRACE("_InitFromTrack(): keyframes: %d, frame count: %lld\n", 280 fHasKeyFrames, fCountFrames); 281 printf("_InitFromTrack(): keyframes: %d, frame count: %lld\n", 282 fHasKeyFrames, fCountFrames); 283 } 284 285 // _FramesPerBuffer 286 int64 287 MediaTrackAudioSupplier::_FramesPerBuffer() const 288 { 289 int64 sampleSize = fFormat.u.raw_audio.format 290 & media_raw_audio_format::B_AUDIO_SIZE_MASK; 291 int64 frameSize = sampleSize * fFormat.u.raw_audio.channel_count; 292 return fFormat.u.raw_audio.buffer_size / frameSize; 293 } 294 295 // _CopyFrames 296 // 297 // Given two buffers starting at different frame offsets, this function 298 // copies /frames/ frames at position /position/ from the source to the 299 // target buffer. 300 // Note that no range checking is done. 301 void 302 MediaTrackAudioSupplier::_CopyFrames(void* source, int64 sourceOffset, 303 void* target, int64 targetOffset, 304 int64 position, int64 frames) const 305 { 306 int64 sampleSize = fFormat.u.raw_audio.format 307 & media_raw_audio_format::B_AUDIO_SIZE_MASK; 308 int64 frameSize = sampleSize * fFormat.u.raw_audio.channel_count; 309 source = (char*)source + frameSize * (position - sourceOffset); 310 target = (char*)target + frameSize * (position - targetOffset); 311 memcpy(target, source, frames * frameSize); 312 } 313 314 // _CopyFrames 315 // 316 // Given two buffers starting at different frame offsets, this function 317 // copies /frames/ frames at position /position/ from the source to the 318 // target buffer. This version expects a cache buffer as source. 319 // Note that no range checking is done. 320 void 321 MediaTrackAudioSupplier::_CopyFrames(Buffer* buffer, 322 void* target, int64 targetOffset, 323 int64 position, int64 frames) const 324 { 325 _CopyFrames(buffer->data, buffer->offset, target, targetOffset, position, 326 frames); 327 } 328 329 // _AllocateBuffers 330 // 331 // Allocates a set of buffers. 332 void 333 MediaTrackAudioSupplier::_AllocateBuffers() 334 { 335 int32 count = 10; 336 _FreeBuffers(); 337 int32 bufferSize = fFormat.u.raw_audio.buffer_size; 338 char* data = new (nothrow) char[bufferSize * count]; 339 for (; count > 0; count--) { 340 Buffer* buffer = new (nothrow) Buffer; 341 if (!buffer || !fBuffers.AddItem(buffer)) { 342 delete buffer; 343 if (fBuffers.CountItems() == 0) 344 delete[] data; 345 return; 346 } 347 buffer->data = data; 348 data += bufferSize; 349 buffer->offset = 0; 350 buffer->size = 0; 351 buffer->time_stamp = 0; 352 } 353 } 354 355 // _FreeBuffers 356 // 357 // Frees the allocated buffers. 358 void 359 MediaTrackAudioSupplier::_FreeBuffers() 360 { 361 if (fBuffers.CountItems() > 0) { 362 delete[] (char*)_BufferAt(0)->data; 363 for (int32 i = 0; Buffer* buffer = _BufferAt(i); i++) 364 delete buffer; 365 fBuffers.MakeEmpty(); 366 } 367 } 368 369 // _BufferAt 370 // 371 // Returns the buffer at index /index/. 372 MediaTrackAudioSupplier::Buffer* 373 MediaTrackAudioSupplier::_BufferAt(int32 index) const 374 { 375 return (Buffer*)fBuffers.ItemAt(index); 376 } 377 378 // _FindBufferAtFrame 379 // 380 // If any buffer starts at offset /frame/, it is returned, NULL otherwise. 381 MediaTrackAudioSupplier::Buffer* 382 MediaTrackAudioSupplier::_FindBufferAtFrame(int64 frame) const 383 { 384 Buffer* buffer = NULL; 385 for (int32 i = 0; 386 ((buffer = _BufferAt(i))) && buffer->offset != frame; 387 i++); 388 return buffer; 389 } 390 391 // _FindUnusedBuffer 392 // 393 // Returns the first unused buffer or NULL if all buffers are used. 394 MediaTrackAudioSupplier::Buffer* 395 MediaTrackAudioSupplier::_FindUnusedBuffer() const 396 { 397 Buffer* buffer = NULL; 398 for (int32 i = 0; ((buffer = _BufferAt(i))) && buffer->size != 0; i++) 399 ; 400 return buffer; 401 } 402 403 // _FindUsableBuffer 404 // 405 // Returns either an unused buffer or, if all buffers are used, the least 406 // recently used buffer. 407 // In every case a buffer is returned. 408 MediaTrackAudioSupplier::Buffer* 409 MediaTrackAudioSupplier::_FindUsableBuffer() const 410 { 411 Buffer* result = _FindUnusedBuffer(); 412 if (!result) { 413 // find the least recently used buffer. 414 result = _BufferAt(0); 415 for (int32 i = 1; Buffer* buffer = _BufferAt(i); i++) { 416 if (buffer->time_stamp < result->time_stamp) 417 result = buffer; 418 } 419 } 420 return result; 421 } 422 423 // _FindUsableBufferFor 424 // 425 // In case there already exists a buffer that starts at position this 426 // one is returned. Otherwise the function returns either an unused 427 // buffer or, if all buffers are used, the least recently used buffer. 428 // In every case a buffer is returned. 429 MediaTrackAudioSupplier::Buffer* 430 MediaTrackAudioSupplier::_FindUsableBufferFor(int64 position) const 431 { 432 Buffer* buffer = _FindBufferAtFrame(position); 433 if (buffer == NULL) 434 buffer = _FindUsableBuffer(); 435 return buffer; 436 } 437 438 // _GetBuffersFor 439 // 440 // Adds pointers to all buffers to the list that contain data of the 441 // supplied interval. 442 void 443 MediaTrackAudioSupplier::_GetBuffersFor(BList& buffers, int64 position, 444 int64 frames) const 445 { 446 buffers.MakeEmpty(); 447 for (int32 i = 0; Buffer* buffer = _BufferAt(i); i++) { 448 // Calculate the intersecting interval and add the buffer if it is 449 // not empty. 450 int32 startFrame = max(position, buffer->offset); 451 int32 endFrame = min(position + frames, buffer->offset + buffer->size); 452 if (startFrame < endFrame) 453 buffers.AddItem(buffer); 454 } 455 } 456 457 // _TouchBuffer 458 // 459 // Sets a buffer's time stamp to the current system time. 460 void 461 MediaTrackAudioSupplier::_TouchBuffer(Buffer* buffer) 462 { 463 buffer->time_stamp = system_time(); 464 } 465 466 // _ReadBuffer 467 // 468 // Read a buffer from the current position (which is supplied in /position/) 469 // into /buffer/. The buffer's time stamp is set to the current system time. 470 status_t 471 MediaTrackAudioSupplier::_ReadBuffer(Buffer* buffer, int64 position) 472 { 473 return _ReadBuffer(buffer, position, system_time()); 474 } 475 476 // _ReadBuffer 477 // 478 // Read a buffer from the current position (which is supplied in /position/) 479 // into /buffer/. The buffer's time stamp is set to the supplied time. 480 status_t 481 MediaTrackAudioSupplier::_ReadBuffer(Buffer* buffer, int64 position, 482 bigtime_t time) 483 { 484 status_t error = fMediaTrack->ReadFrames(buffer->data, &buffer->size); 485 486 TRACE("_ReadBuffer(%p, %lld): %s\n", buffer->data, buffer->size, 487 strerror(error)); 488 489 buffer->offset = position; 490 buffer->time_stamp = time; 491 if (error != B_OK) 492 buffer->size = 0; 493 return error; 494 } 495 496 // _ReadCachedFrames 497 // 498 // Tries to read as much as possible data from the cache. The supplied 499 // buffer pointer as well as position and number of frames are adjusted 500 // accordingly. The used cache buffers are stamped with the supplied 501 // time. 502 void 503 MediaTrackAudioSupplier::_ReadCachedFrames(void*& dest, int64& pos, 504 int64& frames, bigtime_t time) 505 { 506 // Get a list of all cache buffers that contain data of the interval, 507 // and sort it. 508 BList buffers(10); 509 _GetBuffersFor(buffers, pos, frames); 510 buffers.SortItems(Buffer::CompareOffset); 511 // Step forward through the list of cache buffers and try to read as 512 // much data from the beginning as possible. 513 for (int32 i = 0; Buffer* buffer = (Buffer*)buffers.ItemAt(i); i++) { 514 if (buffer->offset <= pos && buffer->offset + buffer->size > pos) { 515 // read from the beginning 516 int64 size = min(frames, buffer->offset + buffer->size - pos); 517 _CopyFrames(buffer->data, buffer->offset, dest, pos, pos, size); 518 pos += size; 519 frames -= size; 520 dest = SkipFrames(dest, size); 521 buffer->time_stamp = time; 522 } 523 } 524 // Step backward through the list of cache buffers and try to read as 525 // much data from the end as possible. 526 for (int32 i = buffers.CountItems() - 1; 527 Buffer* buffer = (Buffer*)buffers.ItemAt(i); 528 i++) { 529 if (buffer->offset < pos + frames 530 && buffer->offset + buffer->size >= pos + frames) { 531 // read from the end 532 int64 size = min(frames, pos + frames - buffer->offset); 533 _CopyFrames(buffer->data, buffer->offset, dest, pos, 534 pos + frames - size, size); 535 frames -= size; 536 buffer->time_stamp = time; 537 } 538 } 539 } 540 541 542 /*! Reads /frames/ frames from /position/ into /buffer/. The frames are not 543 read from the cache, but read frames are cached, if possible. 544 New cache buffers are stamped with the supplied time. 545 If an error occurs, the untouched part of the buffer is set to 0. 546 */ 547 status_t 548 MediaTrackAudioSupplier::_ReadUncachedFrames(void* buffer, int64 position, 549 int64 frames, bigtime_t time) 550 { 551 TRACE("_ReadUncachedFrames()\n"); 552 status_t error = B_OK; 553 // seek to the position 554 int64 currentPos = position; 555 if (frames > 0) { 556 error = _SeekToKeyFrameBackward(currentPos); 557 TRACE("_ReadUncachedFrames() - seeked to position: %lld\n", currentPos); 558 // if (position - currentPos > 100000) 559 // printf("MediaTrackAudioSupplier::_ReadUncachedFrames() - " 560 // "keyframe was far away: %lld -> %lld\n", position, currentPos); 561 } 562 // read the frames 563 // TODO: Calculate timeout, 0.25 times duration of "frames" seems good. 564 bigtime_t timeout = 10000; 565 while (error == B_OK && frames > 0) { 566 Buffer* cacheBuffer = _FindUsableBufferFor(currentPos); 567 TRACE("_ReadUncachedFrames() - usable buffer found: %p, " 568 "position: %lld/%lld\n", cacheBuffer, currentPos, position); 569 error = _ReadBuffer(cacheBuffer, currentPos, time); 570 if (error == B_OK) { 571 int64 size = min(position + frames, 572 cacheBuffer->offset + cacheBuffer->size) - position; 573 if (size > 0) { 574 _CopyFrames(cacheBuffer, buffer, position, position, size); 575 buffer = SkipFrames(buffer, size); 576 position += size; 577 frames -= size; 578 } 579 currentPos += cacheBuffer->size; 580 } 581 if (system_time() - time > timeout) { 582 error = B_TIMED_OUT; 583 break; 584 } 585 } 586 587 #if 0 588 // Ensure that all frames up to the next key frame are cached. 589 // This avoids, that each read reaches the BMediaTrack. 590 if (error == B_OK) { 591 int64 nextKeyFrame = currentPos; 592 if (_FindKeyFrameForward(nextKeyFrame) == B_OK) { 593 while (currentPos < nextKeyFrame) { 594 // Check, if data at this position are cache. 595 // If not read it. 596 Buffer* cacheBuffer = _FindBufferAtFrame(currentPos); 597 if (!cacheBuffer || cacheBuffer->size == 0) { 598 cacheBuffer = _FindUsableBufferFor(currentPos); 599 if (_ReadBuffer(cacheBuffer, currentPos, time) != B_OK) 600 break; 601 } 602 if (cacheBuffer) 603 currentPos += cacheBuffer->size; 604 } 605 } 606 } 607 #endif 608 609 // on error fill up the buffer with silence 610 if (error != B_OK && frames > 0) 611 ReadSilence(buffer, frames); 612 return error; 613 } 614 615 // _FindKeyFrameForward 616 status_t 617 MediaTrackAudioSupplier::_FindKeyFrameForward(int64& position) 618 { 619 status_t error = B_OK; 620 if (fHasKeyFrames) { 621 error = fMediaTrack->FindKeyFrameForFrame( 622 &position, B_MEDIA_SEEK_CLOSEST_FORWARD); 623 } else { 624 int64 framesPerBuffer = _FramesPerBuffer(); 625 position += framesPerBuffer - 1; 626 position = position % framesPerBuffer; 627 } 628 return error; 629 } 630 631 // _FindKeyFrameBackward 632 status_t 633 MediaTrackAudioSupplier::_FindKeyFrameBackward(int64& position) 634 { 635 status_t error = B_OK; 636 if (fHasKeyFrames) { 637 error = fMediaTrack->FindKeyFrameForFrame( 638 &position, B_MEDIA_SEEK_CLOSEST_BACKWARD); 639 } else 640 position -= position % _FramesPerBuffer(); 641 return error; 642 } 643 644 #if 0 645 // _SeekToKeyFrameForward 646 status_t 647 MediaTrackAudioSupplier::_SeekToKeyFrameForward(int64& position) 648 { 649 if (position == fMediaTrack->CurrentFrame()) 650 return B_OK; 651 652 status_t error = B_OK; 653 if (fHasKeyFrames) { 654 #ifdef TRACE_AUDIO_SUPPLIER 655 int64 oldPosition = position; 656 #endif 657 error = fMediaTrack->SeekToFrame(&position, 658 B_MEDIA_SEEK_CLOSEST_FORWARD); 659 TRACE("_SeekToKeyFrameForward() - seek to key frame forward: " 660 "%lld -> %lld (%lld)\n", oldPosition, position, 661 fMediaTrack->CurrentFrame()); 662 } else { 663 _FindKeyFrameForward(position); 664 error = fMediaTrack->SeekToFrame(&position); 665 } 666 return error; 667 } 668 #endif 669 670 // _SeekToKeyFrameBackward 671 status_t 672 MediaTrackAudioSupplier::_SeekToKeyFrameBackward(int64& position) 673 { 674 int64 currentPosition = fMediaTrack->CurrentFrame(); 675 if (position == currentPosition) 676 return B_OK; 677 678 status_t error = B_OK; 679 if (fHasKeyFrames) { 680 int64 wantedPosition = position; 681 error = fMediaTrack->FindKeyFrameForFrame(&position, 682 B_MEDIA_SEEK_CLOSEST_BACKWARD); 683 if (error == B_OK && currentPosition > position 684 && currentPosition < wantedPosition) { 685 // The current position is before the wanted position, 686 // but later than the keyframe, so seeking is worse. 687 position = currentPosition; 688 return B_OK; 689 } 690 if (error == B_OK && position > wantedPosition) { 691 // We asked to seek backwards, but the extractor seeked 692 // forwards! Returning an error here will cause silence 693 // to be produced. 694 return B_ERROR; 695 } 696 if (error == B_OK) 697 error = fMediaTrack->SeekToFrame(&position, 0); 698 if (error != B_OK) { 699 position = fMediaTrack->CurrentFrame(); 700 // if (fReportSeekError) { 701 printf(" seek to key frame backward: %lld -> %lld (%lld) " 702 "- %s\n", wantedPosition, position, 703 fMediaTrack->CurrentFrame(), strerror(error)); 704 fReportSeekError = false; 705 // } 706 } else { 707 fReportSeekError = true; 708 } 709 } else { 710 _FindKeyFrameBackward(position); 711 error = fMediaTrack->SeekToFrame(&position); 712 } 713 return error; 714 } 715 716