btrfs: Attempt to fix GCC2 build.
[haiku.git] / src / apps / mediaplayer / supplier / MediaTrackAudioSupplier.cpp
blob10572ab3c3d12d7a8491be97f67f717133fc76d4
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"
8 #include <new>
9 #include <algorithm>
10 #include <stdio.h>
11 #include <string.h>
13 #include <MediaFile.h>
14 #include <MediaTrack.h>
16 using namespace std;
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
26 // #pragma mark - Buffer
29 struct MediaTrackAudioSupplier::Buffer {
30 void* data;
31 int64 offset;
32 int64 size;
33 bigtime_t time_stamp;
35 static int CompareOffset(const void* a, const void* b);
39 int
40 MediaTrackAudioSupplier::Buffer::CompareOffset(const void* a, const void* b)
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;
53 // #pragma mark - MediaTrackAudioSupplier
56 MediaTrackAudioSupplier::MediaTrackAudioSupplier(BMediaTrack* mediaTrack,
57 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)
70 _InitFromTrack();
74 MediaTrackAudioSupplier::~MediaTrackAudioSupplier()
76 _FreeBuffers();
77 delete[] fBuffer;
81 const media_format&
82 MediaTrackAudioSupplier::Format() const
84 return AudioReader::Format();
88 status_t
89 MediaTrackAudioSupplier::GetEncodedFormat(media_format* format) const
91 if (!fMediaTrack)
92 return B_NO_INIT;
93 return fMediaTrack->EncodedFormat(format);
97 status_t
98 MediaTrackAudioSupplier::GetCodecInfo(media_codec_info* info) const
100 if (!fMediaTrack)
101 return B_NO_INIT;
102 return fMediaTrack->GetCodecInfo(info);
106 bigtime_t
107 MediaTrackAudioSupplier::Duration() const
109 if (!fMediaTrack)
110 return 0;
112 return fMediaTrack->Duration();
116 // #pragma mark - AudioReader
119 bigtime_t
120 MediaTrackAudioSupplier::InitialLatency() const
122 // TODO: this is just a wild guess, and not really founded on anything.
123 return 100000;
127 status_t
128 MediaTrackAudioSupplier::Read(void* buffer, int64 pos, int64 frames)
130 TRACE("Read(%p, %lld, %lld)\n", buffer, pos,
131 frames);
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();
137 if (error != B_OK) {
138 TRACE("Read() InitCheck failed\n");
139 return error;
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((int64)0, fCountFrames - pos);
147 ReadSilence(SkipFrames(buffer, size), frames - size);
148 frames = size;
151 TRACE(" after eliminating the frames after the track end: %p, %lld, %lld\n",
152 buffer, pos, frames);
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;
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);
169 if (error == B_OK) {
170 error = fMediaTrack->SeekToFrame(&keyFrame,
171 B_MEDIA_SEEK_CLOSEST_BACKWARD);
173 if (error != B_OK) {
174 printf(" error seeking to position: %lld (%lld)\n", pos,
175 fMediaTrack->CurrentFrame());
177 return error;
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;
190 if (restSize > 0) {
191 printf(" skipped %lu frames (rest)\n", framesPerBuffer);
192 fMediaTrack->ReadFrames(dummyBuffer, &restSize);
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;
203 printf(" done\n\n");
205 #else
206 // read the cached frames
207 bigtime_t time = system_time();
208 if (frames > 0)
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
214 if (frames > 0)
215 _ReadUncachedFrames(buffer, pos, frames, time);
217 #endif
218 TRACE("Read() done\n");
220 return B_OK;
223 // InitCheck
224 status_t
225 MediaTrackAudioSupplier::InitCheck() const
227 status_t error = AudioReader::InitCheck();
228 if (error == B_OK && (!fMediaTrack || !fBuffer))
229 error = B_NO_INIT;
230 return error;
233 // #pragma mark -
235 // _InitFromTrack
236 void
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) {
244 fMediaTrack = NULL;
245 return;
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
256 fBuffer = new (nothrow) char[fFormat.u.raw_audio.buffer_size];
257 _AllocateBuffers();
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
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);
285 // _FramesPerBuffer
286 int64
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;
295 // _CopyFrames
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
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);
314 // _CopyFrames
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
325 _CopyFrames(buffer->data, buffer->offset, target, targetOffset, position,
326 frames);
329 // _AllocateBuffers
331 // Allocates a set of buffers.
332 void
333 MediaTrackAudioSupplier::_AllocateBuffers()
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;
347 buffer->data = data;
348 data += bufferSize;
349 buffer->offset = 0;
350 buffer->size = 0;
351 buffer->time_stamp = 0;
355 // _FreeBuffers
357 // Frees the allocated buffers.
358 void
359 MediaTrackAudioSupplier::_FreeBuffers()
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();
369 // _BufferAt
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;
385 for (int32 i = 0;
386 ((buffer = _BufferAt(i))) && buffer->offset != frame;
387 i++);
388 return buffer;
391 // _FindUnusedBuffer
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++)
400 return buffer;
403 // _FindUsableBuffer
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();
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;
420 return result;
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);
433 if (buffer == NULL)
434 buffer = _FindUsableBuffer();
435 return buffer;
438 // _GetBuffersFor
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
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);
457 // _TouchBuffer
459 // Sets a buffer's time stamp to the current system time.
460 void
461 MediaTrackAudioSupplier::_TouchBuffer(Buffer* buffer)
463 buffer->time_stamp = system_time();
466 // _ReadBuffer
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)
473 return _ReadBuffer(buffer, position, system_time());
476 // _ReadBuffer
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)
484 status_t error = fMediaTrack->ReadFrames(buffer->data, &buffer->size);
486 TRACE("_ReadBuffer(%p, %lld): %s\n", buffer->data, buffer->size,
487 strerror(error));
489 buffer->offset = position;
490 buffer->time_stamp = time;
491 if (error != B_OK)
492 buffer->size = 0;
493 return error;
496 // _ReadCachedFrames
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)
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;
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;
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.
547 status_t
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;
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);
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;
579 currentPos += cacheBuffer->size;
581 if (system_time() - time > timeout) {
582 error = B_TIMED_OUT;
583 break;
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;
602 if (cacheBuffer)
603 currentPos += cacheBuffer->size;
607 #endif
609 // on error fill up the buffer with silence
610 if (error != B_OK && frames > 0)
611 ReadSilence(buffer, frames);
612 return error;
616 // _FindKeyFrameForward
617 status_t
618 MediaTrackAudioSupplier::_FindKeyFrameForward(int64& position)
620 status_t error = B_OK;
621 if (fHasKeyFrames) {
622 error = fMediaTrack->FindKeyFrameForFrame(
623 &position, B_MEDIA_SEEK_CLOSEST_FORWARD);
624 } else {
625 int64 framesPerBuffer = _FramesPerBuffer();
626 position += framesPerBuffer - 1;
627 position = position % framesPerBuffer;
629 return error;
633 // _FindKeyFrameBackward
634 status_t
635 MediaTrackAudioSupplier::_FindKeyFrameBackward(int64& position)
637 status_t error = B_OK;
638 if (fHasKeyFrames) {
639 error = fMediaTrack->FindKeyFrameForFrame(
640 &position, B_MEDIA_SEEK_CLOSEST_BACKWARD);
641 } else
642 position -= position % _FramesPerBuffer();
643 return error;
647 #if 0
648 // _SeekToKeyFrameForward
649 status_t
650 MediaTrackAudioSupplier::_SeekToKeyFrameForward(int64& position)
652 if (position == fMediaTrack->CurrentFrame())
653 return B_OK;
655 status_t error = B_OK;
656 if (fHasKeyFrames) {
657 #ifdef TRACE_AUDIO_SUPPLIER
658 int64 oldPosition = position;
659 #endif
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());
665 } else {
666 _FindKeyFrameForward(position);
667 error = fMediaTrack->SeekToFrame(&position);
669 return error;
671 #endif
674 // _SeekToKeyFrameBackward
675 status_t
676 MediaTrackAudioSupplier::_SeekToKeyFrameBackward(int64& position)
678 int64 currentPosition = fMediaTrack->CurrentFrame();
679 if (position == currentPosition)
680 return B_OK;
682 status_t error = B_OK;
683 if (fHasKeyFrames) {
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;
692 return B_OK;
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
697 // to be produced.
698 return B_ERROR;
700 if (error == B_OK)
701 error = fMediaTrack->SeekToFrame(&position, 0);
702 if (error != B_OK) {
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;
709 // }
710 } else {
711 fReportSeekError = true;
713 } else {
714 _FindKeyFrameBackward(position);
715 error = fMediaTrack->SeekToFrame(&position);
717 return error;