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