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 <algobase.h> 10 #include <stdio.h> 11 #include <string.h> 12 13 #include <MediaFile.h> 14 #include <MediaTrack.h> 15 16 using std::nothrow; 17 18 19 //#define TRACE_AUDIO_SUPPLIER 20 #ifdef TRACE_AUDIO_SUPPLIER 21 # define TRACE(x...) printf("MediaTrackAudioSupplier::"); printf(x) 22 #else 23 # define TRACE(x...) 24 #endif 25 26 27 // #pragma mark - Buffer 28 29 30 struct MediaTrackAudioSupplier::Buffer { 31 void* data; 32 int64 offset; 33 int64 size; 34 bigtime_t time_stamp; 35 36 static int CompareOffset(const void* a, const void* b); 37 }; 38 39 40 int 41 MediaTrackAudioSupplier::Buffer::CompareOffset(const void* a, const void* b) 42 { 43 const Buffer* buffer1 = *(const Buffer**)a; 44 const Buffer* buffer2 = *(const Buffer**)b; 45 int result = 0; 46 if (buffer1->offset < buffer2->offset) 47 result = -1; 48 else if (buffer1->offset > buffer2->offset) 49 result = 1; 50 return result; 51 } 52 53 54 // #pragma mark - MediaTrackAudioSupplier 55 56 57 MediaTrackAudioSupplier::MediaTrackAudioSupplier(BMediaTrack* mediaTrack, 58 int32 trackIndex) 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 // Read 120 status_t 121 MediaTrackAudioSupplier::Read(void* buffer, int64 pos, int64 frames) 122 { 123 TRACE("Read(%p, %lld, %lld)\n", buffer, pos, 124 frames); 125 TRACE(" this: %p, fOutOffset: %lld\n", this, fOutOffset); 126 127 status_t error = InitCheck(); 128 if (error != B_OK) { 129 TRACE("Read() done\n"); 130 return error; 131 } 132 133 // convert pos according to our offset 134 pos += fOutOffset; 135 // Fill the frames after the end of the track with silence. 136 if (pos + frames > fCountFrames) { 137 int64 size = max(0LL, fCountFrames - pos); 138 ReadSilence(SkipFrames(buffer, size), frames - size); 139 frames = size; 140 } 141 142 TRACE(" after eliminating the frames after the track end: %p, %lld, %lld\n", 143 buffer, pos, frames); 144 145 // read the cached frames 146 bigtime_t time = system_time(); 147 if (frames > 0) 148 _ReadCachedFrames(buffer, pos, frames, time); 149 150 TRACE(" after reading from cache: %p, %lld, %lld\n", buffer, pos, frames); 151 152 // read the remaining (uncached) frames 153 if (frames > 0) 154 _ReadUncachedFrames(buffer, pos, frames, time); 155 156 TRACE("Read() done\n"); 157 158 return B_OK; 159 } 160 161 // InitCheck 162 status_t 163 MediaTrackAudioSupplier::InitCheck() const 164 { 165 status_t error = AudioReader::InitCheck(); 166 if (error == B_OK && (!fMediaTrack || !fBuffer)) 167 error = B_NO_INIT; 168 return error; 169 } 170 171 // #pragma mark - 172 173 // _InitFromTrack 174 void 175 MediaTrackAudioSupplier::_InitFromTrack() 176 { 177 if (fMediaTrack && fMediaTrack->DecodedFormat(&fFormat) == B_OK 178 && fFormat.type == B_MEDIA_RAW_AUDIO) { 179 180 #ifdef TRACE_AUDIO_SUPPLIER 181 char formatString[256]; 182 string_for_format(fFormat, formatString, 256); 183 TRACE("MediaTrackAudioSupplier: format is: %s\n", formatString); 184 #endif 185 186 fBuffer = new (nothrow) char[fFormat.u.raw_audio.buffer_size]; 187 _AllocateBuffers(); 188 189 // Find out, if the track has key frames: as a heuristic we 190 // check, if the first and the second frame have the same backward 191 // key frame. 192 // Note: It shouldn't harm that much, if we're wrong and the 193 // track has key frame although we found out that it has not. 194 int64 keyFrame0 = 0; 195 int64 keyFrame1 = 1; 196 fMediaTrack->FindKeyFrameForFrame(&keyFrame0, 197 B_MEDIA_SEEK_CLOSEST_BACKWARD); 198 fMediaTrack->FindKeyFrameForFrame(&keyFrame1, 199 B_MEDIA_SEEK_CLOSEST_BACKWARD); 200 fHasKeyFrames = (keyFrame0 == keyFrame1); 201 202 // get the length of the track 203 fCountFrames = fMediaTrack->CountFrames(); 204 205 TRACE("MediaTrackAudioSupplier: keyframes: %d, frame count: %lld\n", 206 fHasKeyFrames, fCountFrames); 207 } else 208 fMediaTrack = NULL; 209 } 210 211 // _FramesPerBuffer 212 int64 213 MediaTrackAudioSupplier::_FramesPerBuffer() const 214 { 215 int64 sampleSize = fFormat.u.raw_audio.format 216 & media_raw_audio_format::B_AUDIO_SIZE_MASK; 217 int64 frameSize = sampleSize * fFormat.u.raw_audio.channel_count; 218 return fFormat.u.raw_audio.buffer_size / frameSize; 219 } 220 221 // _CopyFrames 222 // 223 // Given two buffers starting at different frame offsets, this function 224 // copies /frames/ frames at position /position/ from the source to the 225 // target buffer. 226 // Note that no range checking is done. 227 void 228 MediaTrackAudioSupplier::_CopyFrames(void* source, int64 sourceOffset, 229 void* target, int64 targetOffset, 230 int64 position, int64 frames) const 231 { 232 int64 sampleSize = fFormat.u.raw_audio.format 233 & media_raw_audio_format::B_AUDIO_SIZE_MASK; 234 int64 frameSize = sampleSize * fFormat.u.raw_audio.channel_count; 235 source = (char*)source + frameSize * (position - sourceOffset); 236 target = (char*)target + frameSize * (position - targetOffset); 237 memcpy(target, source, frames * frameSize); 238 } 239 240 // _CopyFrames 241 // 242 // Given two buffers starting at different frame offsets, this function 243 // copies /frames/ frames at position /position/ from the source to the 244 // target buffer. This version expects a cache buffer as source. 245 // Note that no range checking is done. 246 void 247 MediaTrackAudioSupplier::_CopyFrames(Buffer* buffer, 248 void* target, int64 targetOffset, 249 int64 position, int64 frames) const 250 { 251 _CopyFrames(buffer->data, buffer->offset, target, targetOffset, position, 252 frames); 253 } 254 255 // _AllocateBuffers 256 // 257 // Allocates a set of buffers. 258 void 259 MediaTrackAudioSupplier::_AllocateBuffers() 260 { 261 int32 count = 10; 262 _FreeBuffers(); 263 int32 bufferSize = fFormat.u.raw_audio.buffer_size; 264 char* data = new (nothrow) char[bufferSize * count]; 265 for (; count > 0; count--) { 266 Buffer* buffer = new (nothrow) Buffer; 267 if (!buffer || !fBuffers.AddItem(buffer)) { 268 delete buffer; 269 return; 270 } 271 buffer->data = data; 272 data += bufferSize; 273 buffer->offset = 0; 274 buffer->size = 0; 275 buffer->time_stamp = 0; 276 } 277 } 278 279 // _FreeBuffers 280 // 281 // Frees the allocated buffers. 282 void 283 MediaTrackAudioSupplier::_FreeBuffers() 284 { 285 if (fBuffers.CountItems() > 0) { 286 delete[] (char*)_BufferAt(0)->data; 287 for (int32 i = 0; Buffer* buffer = _BufferAt(i); i++) 288 delete buffer; 289 fBuffers.MakeEmpty(); 290 } 291 } 292 293 // _BufferAt 294 // 295 // Returns the buffer at index /index/. 296 MediaTrackAudioSupplier::Buffer* 297 MediaTrackAudioSupplier::_BufferAt(int32 index) const 298 { 299 return (Buffer*)fBuffers.ItemAt(index); 300 } 301 302 // _FindBufferAtFrame 303 // 304 // If any buffer starts at offset /frame/, it is returned, NULL otherwise. 305 MediaTrackAudioSupplier::Buffer* 306 MediaTrackAudioSupplier::_FindBufferAtFrame(int64 frame) const 307 { 308 Buffer* buffer = NULL; 309 for (int32 i = 0; 310 ((buffer = _BufferAt(i))) && buffer->offset != frame; 311 i++); 312 return buffer; 313 } 314 315 // _FindUnusedBuffer 316 // 317 // Returns the first unused buffer or NULL if all buffers are used. 318 MediaTrackAudioSupplier::Buffer* 319 MediaTrackAudioSupplier::_FindUnusedBuffer() const 320 { 321 Buffer* buffer = NULL; 322 for (int32 i = 0; ((buffer = _BufferAt(i))) && buffer->size != 0; i++); 323 return buffer; 324 } 325 326 // _FindUsableBuffer 327 // 328 // Returns either an unused buffer or, if all buffers are used, the least 329 // recently used buffer. 330 // In every case a buffer is returned. 331 MediaTrackAudioSupplier::Buffer* 332 MediaTrackAudioSupplier::_FindUsableBuffer() const 333 { 334 Buffer* result = _FindUnusedBuffer(); 335 if (!result) { 336 // find the least recently used buffer. 337 result = _BufferAt(0); 338 for (int32 i = 1; Buffer* buffer = _BufferAt(i); i++) { 339 if (buffer->time_stamp < result->time_stamp) 340 result = buffer; 341 } 342 } 343 return result; 344 } 345 346 // _FindUsableBufferFor 347 // 348 // In case there already exists a buffer that starts at position this 349 // one is returned. Otherwise the function returns either an unused 350 // buffer or, if all buffers are used, the least recently used buffer. 351 // In every case a buffer is returned. 352 MediaTrackAudioSupplier::Buffer* 353 MediaTrackAudioSupplier::_FindUsableBufferFor(int64 position) const 354 { 355 Buffer* buffer = _FindBufferAtFrame(position); 356 if (!buffer) 357 buffer = _FindUsableBuffer(); 358 return buffer; 359 } 360 361 // _GetBuffersFor 362 // 363 // Adds pointers to all buffers to the list that contain data of the 364 // supplied interval. 365 void 366 MediaTrackAudioSupplier::_GetBuffersFor(BList& buffers, int64 position, 367 int64 frames) const 368 { 369 buffers.MakeEmpty(); 370 for (int32 i = 0; Buffer* buffer = _BufferAt(i); i++) { 371 // Calculate the intersecting interval and add the buffer if it is 372 // not empty. 373 int32 startFrame = max(position, buffer->offset); 374 int32 endFrame = min(position + frames, buffer->offset + buffer->size); 375 if (startFrame < endFrame) 376 buffers.AddItem(buffer); 377 } 378 } 379 380 // _TouchBuffer 381 // 382 // Sets a buffer's time stamp to the current system time. 383 void 384 MediaTrackAudioSupplier::_TouchBuffer(Buffer* buffer) 385 { 386 buffer->time_stamp = system_time(); 387 } 388 389 // _ReadBuffer 390 // 391 // Read a buffer from the current position (which is supplied in /position/) 392 // into /buffer/. The buffer's time stamp is set to the current system time. 393 status_t 394 MediaTrackAudioSupplier::_ReadBuffer(Buffer* buffer, int64 position) 395 { 396 return _ReadBuffer(buffer, position, system_time()); 397 } 398 399 // _ReadBuffer 400 // 401 // Read a buffer from the current position (which is supplied in /position/) 402 // into /buffer/. The buffer's time stamp is set to the supplied time. 403 status_t 404 MediaTrackAudioSupplier::_ReadBuffer(Buffer* buffer, int64 position, 405 bigtime_t time) 406 { 407 status_t error = fMediaTrack->ReadFrames(buffer->data, &buffer->size); 408 409 TRACE("Read(%p, %lld): %s\n", buffer->data, buffer->size, strerror(error)); 410 411 buffer->offset = position; 412 buffer->time_stamp = time; 413 if (error != B_OK) 414 buffer->size = 0; 415 return error; 416 } 417 418 // _ReadCachedFrames 419 // 420 // Tries to read as much as possible data from the cache. The supplied 421 // buffer pointer as well as position and number of frames are adjusted 422 // accordingly. The used cache buffers are stamped with the current 423 // system time. 424 void 425 MediaTrackAudioSupplier::_ReadCachedFrames(void*& dest, int64& pos, 426 int64& frames) 427 { 428 _ReadCachedFrames(dest, pos, frames, system_time()); 429 } 430 431 // _ReadCachedFrames 432 // 433 // Tries to read as much as possible data from the cache. The supplied 434 // buffer pointer as well as position and number of frames are adjusted 435 // accordingly. The used cache buffers are stamped with the supplied 436 // time. 437 void 438 MediaTrackAudioSupplier::_ReadCachedFrames(void*& dest, int64& pos, 439 int64& frames, bigtime_t time) 440 { 441 // Get a list of all cache buffers that contain data of the interval, 442 // and sort it. 443 BList buffers(10); 444 _GetBuffersFor(buffers, pos, frames); 445 buffers.SortItems(Buffer::CompareOffset); 446 // Step forward through the list of cache buffers and try to read as 447 // much data from the beginning as possible. 448 for (int32 i = 0; Buffer* buffer = (Buffer*)buffers.ItemAt(i); i++) { 449 if (buffer->offset <= pos && buffer->offset + buffer->size > pos) { 450 // read from the beginning 451 int64 size = min(frames, buffer->offset + buffer->size - pos); 452 _CopyFrames(buffer->data, buffer->offset, dest, pos, pos, size); 453 pos += size; 454 frames -= size; 455 dest = SkipFrames(dest, size); 456 } 457 buffer->time_stamp = time; 458 } 459 // Step backward through the list of cache buffers and try to read as 460 // much data from the end as possible. 461 for (int32 i = buffers.CountItems() - 1; 462 Buffer* buffer = (Buffer*)buffers.ItemAt(i); 463 i++) { 464 if (buffer->offset < pos + frames 465 && buffer->offset + buffer->size >= pos + frames) { 466 // read from the end 467 int64 size = min(frames, pos + frames - buffer->offset); 468 _CopyFrames(buffer->data, buffer->offset, dest, pos, 469 pos + frames - size, size); 470 frames -= size; 471 } 472 } 473 } 474 475 // _ReadUncachedFrames 476 // 477 // Reads /frames/ frames from /position/ into /buffer/. The frames are not 478 // read from the cache, but read frames are cached, if possible. 479 // New cache buffers are stamped with the system time. 480 // If an error occurs, the untouched part of the buffer is set to 0. 481 status_t 482 MediaTrackAudioSupplier::_ReadUncachedFrames(void* buffer, int64 position, 483 int64 frames) 484 { 485 return _ReadUncachedFrames(buffer, position, frames, system_time()); 486 } 487 488 // _ReadUncachedFrames 489 // 490 // Reads /frames/ frames from /position/ into /buffer/. The frames are not 491 // read from the cache, but read frames are cached, if possible. 492 // New cache buffers are stamped with the supplied time. 493 // If an error occurs, the untouched part of the buffer is set to 0. 494 status_t 495 MediaTrackAudioSupplier::_ReadUncachedFrames(void* buffer, int64 position, 496 int64 frames, bigtime_t time) 497 { 498 status_t error = B_OK; 499 // seek to the position 500 int64 currentPos = position; 501 if (frames > 0) { 502 error = _SeekToKeyFrameBackward(currentPos); 503 TRACE("_ReadUncachedFrames() - seeked to position: %lld\n", currentPos); 504 } 505 // read the frames 506 while (error == B_OK && frames > 0) { 507 Buffer* cacheBuffer = _FindUsableBufferFor(currentPos); 508 TRACE("_ReadUncachedFrames() - usable buffer found: %p\n", cacheBuffer); 509 error = _ReadBuffer(cacheBuffer, currentPos, time); 510 if (error == B_OK) { 511 int64 size = min(position + frames, 512 cacheBuffer->offset + cacheBuffer->size) 513 - position; 514 if (size > 0) { 515 _CopyFrames(cacheBuffer, buffer, position, position, size); 516 buffer = SkipFrames(buffer, size); 517 position += size; 518 frames -= size; 519 } 520 currentPos += cacheBuffer->size; 521 } 522 } 523 // Ensure that all frames up to the next key frame are cached. 524 // This avoids, that each read 525 if (error == B_OK) { 526 int64 nextKeyFrame = currentPos; 527 if (_FindKeyFrameForward(nextKeyFrame) == B_OK) { 528 while (currentPos < nextKeyFrame) { 529 // Check, if data at this position are cache. 530 // If not read it. 531 Buffer* cacheBuffer = _FindBufferAtFrame(currentPos); 532 if (!cacheBuffer || cacheBuffer->size == 0) { 533 cacheBuffer = _FindUsableBufferFor(currentPos); 534 if (_ReadBuffer(cacheBuffer, currentPos, time) != B_OK) 535 break; 536 } 537 if (cacheBuffer) 538 currentPos += cacheBuffer->size; 539 } 540 } 541 } 542 // on error fill up the buffer with silence 543 if (error != B_OK && frames > 0) 544 ReadSilence(buffer, frames); 545 return error; 546 } 547 548 // _FindKeyFrameForward 549 status_t 550 MediaTrackAudioSupplier::_FindKeyFrameForward(int64& position) 551 { 552 status_t error = B_OK; 553 // NOTE: the keyframe version confuses the Frauenhofer MP3 decoder, 554 // it works fine with the non-keyframe version, so let's hope this 555 // is the case for all other keyframe based BeOS codecs... 556 // if (fHasKeyFrames) { 557 // error = fMediaTrack->FindKeyFrameForFrame( 558 // &position, B_MEDIA_SEEK_CLOSEST_FORWARD); 559 // } else { 560 int64 framesPerBuffer = _FramesPerBuffer(); 561 position += framesPerBuffer - 1; 562 position = position % framesPerBuffer; 563 // } 564 return error; 565 } 566 567 // _FindKeyFrameBackward 568 status_t 569 MediaTrackAudioSupplier::_FindKeyFrameBackward(int64& position) 570 { 571 status_t error = B_OK; 572 if (fHasKeyFrames) { 573 error = fMediaTrack->FindKeyFrameForFrame( 574 &position, B_MEDIA_SEEK_CLOSEST_BACKWARD); 575 } else 576 position -= position % _FramesPerBuffer(); 577 return error; 578 } 579 580 // _SeekToKeyFrameForward 581 status_t 582 MediaTrackAudioSupplier::_SeekToKeyFrameForward(int64& position) 583 { 584 if (position == fMediaTrack->CurrentFrame()) 585 return B_OK; 586 587 status_t error = B_OK; 588 if (fHasKeyFrames) { 589 #ifdef TRACE_AUDIO_SUPPLIER 590 int64 oldPosition = position; 591 #endif 592 error = fMediaTrack->SeekToFrame(&position, 593 B_MEDIA_SEEK_CLOSEST_FORWARD); 594 TRACE("_SeekToKeyFrameForward() - seek to key frame forward: " 595 "%lld -> %lld (%lld)\n", oldPosition, position, 596 fMediaTrack->CurrentFrame()); 597 } else { 598 _FindKeyFrameForward(position); 599 error = fMediaTrack->SeekToFrame(&position); 600 } 601 return error; 602 } 603 604 // _SeekToKeyFrameBackward 605 status_t 606 MediaTrackAudioSupplier::_SeekToKeyFrameBackward(int64& position) 607 { 608 if (position == fMediaTrack->CurrentFrame()) 609 return B_OK; 610 611 status_t error = B_OK; 612 if (fHasKeyFrames) { 613 int64 oldPosition = position; 614 error = fMediaTrack->FindKeyFrameForFrame(&position, 615 B_MEDIA_SEEK_CLOSEST_BACKWARD); 616 if (error >= B_OK) 617 error = fMediaTrack->SeekToFrame(&position, 0); 618 if (error < B_OK) { 619 position = fMediaTrack->CurrentFrame(); 620 if (fReportSeekError) { 621 printf(" seek to key frame backward: %lld -> %lld (%lld) " 622 "- %s\n", oldPosition, position, 623 fMediaTrack->CurrentFrame(), strerror(error)); 624 fReportSeekError = false; 625 } 626 } else { 627 fReportSeekError = true; 628 } 629 } else { 630 _FindKeyFrameBackward(position); 631 error = fMediaTrack->SeekToFrame(&position); 632 } 633 return error; 634 } 635 636