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.
6 #include "MediaTrackAudioSupplier.h"
13 #include <MediaFile.h>
14 #include <MediaTrack.h>
18 //#define TRACE_AUDIO_SUPPLIER
19 #ifdef TRACE_AUDIO_SUPPLIER
20 # define TRACE(x...) printf("MediaTrackAudioSupplier::" x)
26 // #pragma mark - Buffer
29 struct MediaTrackAudioSupplier::Buffer
{
35 static int CompareOffset(const void* a
, const void* b
);
40 MediaTrackAudioSupplier::Buffer::CompareOffset(const void* a
, const void* b
)
42 const Buffer
* buffer1
= *(const Buffer
**)a
;
43 const Buffer
* buffer2
= *(const Buffer
**)b
;
45 if (buffer1
->offset
< buffer2
->offset
)
47 else if (buffer1
->offset
> buffer2
->offset
)
53 // #pragma mark - MediaTrackAudioSupplier
56 MediaTrackAudioSupplier::MediaTrackAudioSupplier(BMediaTrack
* mediaTrack
,
60 fMediaTrack(mediaTrack
),
67 fReportSeekError(true),
68 fTrackIndex(trackIndex
)
74 MediaTrackAudioSupplier::~MediaTrackAudioSupplier()
82 MediaTrackAudioSupplier::Format() const
84 return AudioReader::Format();
89 MediaTrackAudioSupplier::GetEncodedFormat(media_format
* format
) const
93 return fMediaTrack
->EncodedFormat(format
);
98 MediaTrackAudioSupplier::GetCodecInfo(media_codec_info
* info
) const
102 return fMediaTrack
->GetCodecInfo(info
);
107 MediaTrackAudioSupplier::Duration() const
112 return fMediaTrack
->Duration();
116 // #pragma mark - AudioReader
120 MediaTrackAudioSupplier::InitialLatency() const
122 // TODO: this is just a wild guess, and not really founded on anything.
128 MediaTrackAudioSupplier::Read(void* buffer
, int64 pos
, int64 frames
)
130 TRACE("Read(%p, %lld, %lld)\n", buffer
, pos
,
132 TRACE(" this: %p, fOutOffset: %lld\n", this, fOutOffset
);
134 //printf("MediaTrackAudioSupplier::Read(%p, %lld, %lld)\n", buffer, pos, frames);
136 status_t error
= InitCheck();
138 TRACE("Read() InitCheck failed\n");
142 // convert pos according to our offset
144 // Fill the frames after the end of the track with silence.
145 if (fCountFrames
> 0 && pos
+ frames
> fCountFrames
) {
146 int64 size
= max((int64
)0, fCountFrames
- pos
);
147 ReadSilence(SkipFrames(buffer
, size
), frames
- size
);
151 TRACE(" after eliminating the frames after the track end: %p, %lld, %lld\n",
152 buffer
, pos
, frames
);
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
;
162 if (fMediaTrack
->CurrentFrame() != pos
) {
163 printf(" needing to seek: %lld (%lld)\n", pos
,
164 fMediaTrack
->CurrentFrame());
166 int64 keyFrame
= pos
;
167 error
= fMediaTrack
->FindKeyFrameForFrame(&keyFrame
,
168 B_MEDIA_SEEK_CLOSEST_BACKWARD
);
170 error
= fMediaTrack
->SeekToFrame(&keyFrame
,
171 B_MEDIA_SEEK_CLOSEST_BACKWARD
);
174 printf(" error seeking to position: %lld (%lld)\n", pos
,
175 fMediaTrack
->CurrentFrame());
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
;
189 int64 restSize
= pos
- keyFrame
;
191 printf(" skipped %lu frames (rest)\n", framesPerBuffer
);
192 fMediaTrack
->ReadFrames(dummyBuffer
, &restSize
);
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
;
206 // read the cached frames
207 bigtime_t time
= system_time();
209 _ReadCachedFrames(buffer
, pos
, frames
, time
);
211 TRACE(" after reading from cache: %p, %lld, %lld\n", buffer
, pos
, frames
);
213 // read the remaining (uncached) frames
215 _ReadUncachedFrames(buffer
, pos
, frames
, time
);
218 TRACE("Read() done\n");
225 MediaTrackAudioSupplier::InitCheck() const
227 status_t error
= AudioReader::InitCheck();
228 if (error
== B_OK
&& (!fMediaTrack
|| !fBuffer
))
237 MediaTrackAudioSupplier::_InitFromTrack()
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
) {
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
);
256 fBuffer
= new (nothrow
) char[fFormat
.u
.raw_audio
.buffer_size
];
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
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.
267 fMediaTrack
->FindKeyFrameForFrame(&keyFrame0
,
268 B_MEDIA_SEEK_CLOSEST_BACKWARD
);
269 fMediaTrack
->FindKeyFrameForFrame(&keyFrame1
,
270 B_MEDIA_SEEK_CLOSEST_BACKWARD
);
271 fHasKeyFrames
= (keyFrame0
== keyFrame1
);
273 fHasKeyFrames
= true;
276 // get the length of the track
277 fCountFrames
= fMediaTrack
->CountFrames();
279 TRACE("_InitFromTrack(): keyframes: %" B_PRId16
", frame count: %" B_PRId64
280 "\n", fHasKeyFrames
, fCountFrames
);
281 printf("_InitFromTrack(): keyframes: %" B_PRId16
", frame count: %"
282 B_PRId64
"\n", fHasKeyFrames
, fCountFrames
);
287 MediaTrackAudioSupplier::_FramesPerBuffer() const
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
;
297 // Given two buffers starting at different frame offsets, this function
298 // copies /frames/ frames at position /position/ from the source to the
300 // Note that no range checking is done.
302 MediaTrackAudioSupplier::_CopyFrames(void* source
, int64 sourceOffset
,
303 void* target
, int64 targetOffset
,
304 int64 position
, int64 frames
) const
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
);
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.
321 MediaTrackAudioSupplier::_CopyFrames(Buffer
* buffer
,
322 void* target
, int64 targetOffset
,
323 int64 position
, int64 frames
) const
325 _CopyFrames(buffer
->data
, buffer
->offset
, target
, targetOffset
, position
,
331 // Allocates a set of buffers.
333 MediaTrackAudioSupplier::_AllocateBuffers()
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
)) {
343 if (fBuffers
.CountItems() == 0)
351 buffer
->time_stamp
= 0;
357 // Frees the allocated buffers.
359 MediaTrackAudioSupplier::_FreeBuffers()
361 if (fBuffers
.CountItems() > 0) {
362 delete[] (char*)_BufferAt(0)->data
;
363 for (int32 i
= 0; Buffer
* buffer
= _BufferAt(i
); i
++)
365 fBuffers
.MakeEmpty();
371 // Returns the buffer at index /index/.
372 MediaTrackAudioSupplier::Buffer
*
373 MediaTrackAudioSupplier::_BufferAt(int32 index
) const
375 return (Buffer
*)fBuffers
.ItemAt(index
);
378 // _FindBufferAtFrame
380 // If any buffer starts at offset /frame/, it is returned, NULL otherwise.
381 MediaTrackAudioSupplier::Buffer
*
382 MediaTrackAudioSupplier::_FindBufferAtFrame(int64 frame
) const
384 Buffer
* buffer
= NULL
;
386 ((buffer
= _BufferAt(i
))) && buffer
->offset
!= frame
;
393 // Returns the first unused buffer or NULL if all buffers are used.
394 MediaTrackAudioSupplier::Buffer
*
395 MediaTrackAudioSupplier::_FindUnusedBuffer() const
397 Buffer
* buffer
= NULL
;
398 for (int32 i
= 0; ((buffer
= _BufferAt(i
))) && buffer
->size
!= 0; i
++)
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
411 Buffer
* result
= _FindUnusedBuffer();
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
)
423 // _FindUsableBufferFor
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
432 Buffer
* buffer
= _FindBufferAtFrame(position
);
434 buffer
= _FindUsableBuffer();
440 // Adds pointers to all buffers to the list that contain data of the
441 // supplied interval.
443 MediaTrackAudioSupplier::_GetBuffersFor(BList
& buffers
, int64 position
,
447 for (int32 i
= 0; Buffer
* buffer
= _BufferAt(i
); i
++) {
448 // Calculate the intersecting interval and add the buffer if it is
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
);
459 // Sets a buffer's time stamp to the current system time.
461 MediaTrackAudioSupplier::_TouchBuffer(Buffer
* buffer
)
463 buffer
->time_stamp
= system_time();
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.
471 MediaTrackAudioSupplier::_ReadBuffer(Buffer
* buffer
, int64 position
)
473 return _ReadBuffer(buffer
, position
, system_time());
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.
481 MediaTrackAudioSupplier::_ReadBuffer(Buffer
* buffer
, int64 position
,
484 status_t error
= fMediaTrack
->ReadFrames(buffer
->data
, &buffer
->size
);
486 TRACE("_ReadBuffer(%p, %lld): %s\n", buffer
->data
, buffer
->size
,
489 buffer
->offset
= position
;
490 buffer
->time_stamp
= time
;
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
503 MediaTrackAudioSupplier::_ReadCachedFrames(void*& dest
, int64
& pos
,
504 int64
& frames
, bigtime_t time
)
506 // Get a list of all cache buffers that contain data of the interval,
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
);
520 dest
= SkipFrames(dest
, size
);
521 buffer
->time_stamp
= time
;
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
);
529 if (buffer
->offset
< pos
+ frames
530 && buffer
->offset
+ buffer
->size
>= pos
+ frames
) {
532 int64 size
= min(frames
, pos
+ frames
- buffer
->offset
);
533 _CopyFrames(buffer
->data
, buffer
->offset
, dest
, pos
,
534 pos
+ frames
- size
, size
);
536 buffer
->time_stamp
= time
;
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.
548 MediaTrackAudioSupplier::_ReadUncachedFrames(void* buffer
, int64 position
,
549 int64 frames
, bigtime_t time
)
551 TRACE("_ReadUncachedFrames()\n");
552 status_t error
= B_OK
;
553 // seek to the position
554 int64 currentPos
= position
;
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);
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
);
571 int64 size
= min(position
+ frames
,
572 cacheBuffer
->offset
+ cacheBuffer
->size
) - position
;
574 _CopyFrames(cacheBuffer
, buffer
, position
, position
, size
);
575 buffer
= SkipFrames(buffer
, size
);
579 currentPos
+= cacheBuffer
->size
;
581 if (system_time() - time
> timeout
) {
588 // Ensure that all frames up to the next key frame are cached.
589 // This avoids, that each read reaches the BMediaTrack.
591 int64 nextKeyFrame
= currentPos
;
592 if (_FindKeyFrameForward(nextKeyFrame
) == B_OK
) {
593 while (currentPos
< nextKeyFrame
) {
594 // Check, if data at this position are cache.
596 Buffer
* cacheBuffer
= _FindBufferAtFrame(currentPos
);
597 if (!cacheBuffer
|| cacheBuffer
->size
== 0) {
598 cacheBuffer
= _FindUsableBufferFor(currentPos
);
599 if (_ReadBuffer(cacheBuffer
, currentPos
, time
) != B_OK
)
603 currentPos
+= cacheBuffer
->size
;
609 // on error fill up the buffer with silence
610 if (error
!= B_OK
&& frames
> 0)
611 ReadSilence(buffer
, frames
);
616 // _FindKeyFrameForward
618 MediaTrackAudioSupplier::_FindKeyFrameForward(int64
& position
)
620 status_t error
= B_OK
;
622 error
= fMediaTrack
->FindKeyFrameForFrame(
623 &position
, B_MEDIA_SEEK_CLOSEST_FORWARD
);
625 int64 framesPerBuffer
= _FramesPerBuffer();
626 position
+= framesPerBuffer
- 1;
627 position
= position
% framesPerBuffer
;
633 // _FindKeyFrameBackward
635 MediaTrackAudioSupplier::_FindKeyFrameBackward(int64
& position
)
637 status_t error
= B_OK
;
639 error
= fMediaTrack
->FindKeyFrameForFrame(
640 &position
, B_MEDIA_SEEK_CLOSEST_BACKWARD
);
642 position
-= position
% _FramesPerBuffer();
648 // _SeekToKeyFrameForward
650 MediaTrackAudioSupplier::_SeekToKeyFrameForward(int64
& position
)
652 if (position
== fMediaTrack
->CurrentFrame())
655 status_t error
= B_OK
;
657 #ifdef TRACE_AUDIO_SUPPLIER
658 int64 oldPosition
= position
;
660 error
= fMediaTrack
->SeekToFrame(&position
,
661 B_MEDIA_SEEK_CLOSEST_FORWARD
);
662 TRACE("_SeekToKeyFrameForward() - seek to key frame forward: "
663 "%lld -> %lld (%lld)\n", oldPosition
, position
,
664 fMediaTrack
->CurrentFrame());
666 _FindKeyFrameForward(position
);
667 error
= fMediaTrack
->SeekToFrame(&position
);
674 // _SeekToKeyFrameBackward
676 MediaTrackAudioSupplier::_SeekToKeyFrameBackward(int64
& position
)
678 int64 currentPosition
= fMediaTrack
->CurrentFrame();
679 if (position
== currentPosition
)
682 status_t error
= B_OK
;
684 int64 wantedPosition
= position
;
685 error
= fMediaTrack
->FindKeyFrameForFrame(&position
,
686 B_MEDIA_SEEK_CLOSEST_BACKWARD
);
687 if (error
== B_OK
&& currentPosition
> position
688 && currentPosition
< wantedPosition
) {
689 // The current position is before the wanted position,
690 // but later than the keyframe, so seeking is worse.
691 position
= currentPosition
;
694 if (error
== B_OK
&& position
> wantedPosition
) {
695 // We asked to seek backwards, but the extractor seeked
696 // forwards! Returning an error here will cause silence
701 error
= fMediaTrack
->SeekToFrame(&position
, 0);
703 position
= fMediaTrack
->CurrentFrame();
704 // if (fReportSeekError) {
705 printf(" seek to key frame backward: %" B_PRId64
" -> %"
706 B_PRId64
" (%" B_PRId64
") - %s\n", wantedPosition
,
707 position
, fMediaTrack
->CurrentFrame(), strerror(error
));
708 fReportSeekError
= false;
711 fReportSeekError
= true;
714 _FindKeyFrameBackward(position
);
715 error
= fMediaTrack
->SeekToFrame(&position
);