Several improvements that make the audio playback

much more robust:
 * Use a much larger buffer size, since we cache
   a lot anyway.
 * When reading uncached frames, don't trip over the
   situation that the seekable keyframe was very far
   away from where we want to read. Don't get caught
   up in a loop decoding an entire movie, but return
   a time-out error instead. This will get us back
   on track eventually.
 * Before seeking to a keyframe, check if the current
   track position is actually nearer.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@38498 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Stephan Aßmus
2010-09-01 16:02:42 +00:00
parent b4560be299
commit f96246077d
@@ -55,16 +55,17 @@ MediaTrackAudioSupplier::Buffer::CompareOffset(const void* a, const void* b)
MediaTrackAudioSupplier::MediaTrackAudioSupplier(BMediaTrack* mediaTrack, MediaTrackAudioSupplier::MediaTrackAudioSupplier(BMediaTrack* mediaTrack,
int32 trackIndex) int32 trackIndex)
: AudioTrackSupplier(), :
fMediaTrack(mediaTrack), AudioTrackSupplier(),
fBuffer(NULL), fMediaTrack(mediaTrack),
fBufferOffset(0), fBuffer(NULL),
fBufferSize(0), fBufferOffset(0),
fBuffers(10), fBufferSize(0),
fHasKeyFrames(false), fBuffers(10),
fCountFrames(0), fHasKeyFrames(false),
fReportSeekError(true), fCountFrames(0),
fTrackIndex(trackIndex) fReportSeekError(true),
fTrackIndex(trackIndex)
{ {
_InitFromTrack(); _InitFromTrack();
} }
@@ -235,13 +236,18 @@ MediaTrackAudioSupplier::InitCheck() const
void void
MediaTrackAudioSupplier::_InitFromTrack() MediaTrackAudioSupplier::_InitFromTrack()
{ {
TRACE("_InitFromTrack()\n");
// Try to suggest a big buffer size, we do a lot of caching...
fFormat.u.raw_audio.buffer_size = 16384;
if (fMediaTrack && fMediaTrack->DecodedFormat(&fFormat) == B_OK if (fMediaTrack && fMediaTrack->DecodedFormat(&fFormat) == B_OK
&& fFormat.type == B_MEDIA_RAW_AUDIO) { && fFormat.type == B_MEDIA_RAW_AUDIO) {
#ifdef TRACE_AUDIO_SUPPLIER #ifdef TRACE_AUDIO_SUPPLIER
char formatString[256]; char formatString[256];
string_for_format(fFormat, formatString, 256); string_for_format(fFormat, formatString, 256);
TRACE("MediaTrackAudioSupplier: format is: %s\n", formatString); TRACE("_InitFromTrack(): format is: %s\n", formatString);
TRACE("_InitFromTrack(): buffer size: %ld\n",
fFormat.u.raw_audio.buffer_size);
#endif #endif
fBuffer = new (nothrow) char[fFormat.u.raw_audio.buffer_size]; fBuffer = new (nothrow) char[fFormat.u.raw_audio.buffer_size];
@@ -263,9 +269,9 @@ MediaTrackAudioSupplier::_InitFromTrack()
// get the length of the track // get the length of the track
fCountFrames = fMediaTrack->CountFrames(); fCountFrames = fMediaTrack->CountFrames();
TRACE("MediaTrackAudioSupplier: keyframes: %d, frame count: %lld\n", TRACE("_InitFromTrack(): keyframes: %d, frame count: %lld\n",
fHasKeyFrames, fCountFrames); fHasKeyFrames, fCountFrames);
printf("MediaTrackAudioSupplier: keyframes: %d, frame count: %lld\n", printf("_InitFromTrack(): keyframes: %d, frame count: %lld\n",
fHasKeyFrames, fCountFrames); fHasKeyFrames, fCountFrames);
} else } else
fMediaTrack = NULL; fMediaTrack = NULL;
@@ -384,7 +390,8 @@ MediaTrackAudioSupplier::Buffer*
MediaTrackAudioSupplier::_FindUnusedBuffer() const MediaTrackAudioSupplier::_FindUnusedBuffer() const
{ {
Buffer* buffer = NULL; Buffer* buffer = NULL;
for (int32 i = 0; ((buffer = _BufferAt(i))) && buffer->size != 0; i++); for (int32 i = 0; ((buffer = _BufferAt(i))) && buffer->size != 0; i++)
;
return buffer; return buffer;
} }
@@ -418,7 +425,7 @@ MediaTrackAudioSupplier::Buffer*
MediaTrackAudioSupplier::_FindUsableBufferFor(int64 position) const MediaTrackAudioSupplier::_FindUsableBufferFor(int64 position) const
{ {
Buffer* buffer = _FindBufferAtFrame(position); Buffer* buffer = _FindBufferAtFrame(position);
if (!buffer) if (buffer == NULL)
buffer = _FindUsableBuffer(); buffer = _FindUsableBuffer();
return buffer; return buffer;
} }
@@ -471,7 +478,8 @@ MediaTrackAudioSupplier::_ReadBuffer(Buffer* buffer, int64 position,
{ {
status_t error = fMediaTrack->ReadFrames(buffer->data, &buffer->size); status_t error = fMediaTrack->ReadFrames(buffer->data, &buffer->size);
TRACE("Read(%p, %lld): %s\n", buffer->data, buffer->size, strerror(error)); TRACE("_ReadBuffer(%p, %lld): %s\n", buffer->data, buffer->size,
strerror(error));
buffer->offset = position; buffer->offset = position;
buffer->time_stamp = time; buffer->time_stamp = time;
@@ -542,16 +550,21 @@ MediaTrackAudioSupplier::_ReadUncachedFrames(void* buffer, int64 position,
if (frames > 0) { if (frames > 0) {
error = _SeekToKeyFrameBackward(currentPos); error = _SeekToKeyFrameBackward(currentPos);
TRACE("_ReadUncachedFrames() - seeked to position: %lld\n", currentPos); TRACE("_ReadUncachedFrames() - seeked to position: %lld\n", currentPos);
if (position - currentPos > 100000)
printf("MediaTrackAudioSupplier::_ReadUncachedFrames() - "
"keyframe was far away: %lld -> %lld\n", position, currentPos);
} }
// read the frames // read the frames
// TODO: Calculate timeout, 0.25 times duration of "frames" seems good.
bigtime_t timeout = 10000;
while (error == B_OK && frames > 0) { while (error == B_OK && frames > 0) {
Buffer* cacheBuffer = _FindUsableBufferFor(currentPos); Buffer* cacheBuffer = _FindUsableBufferFor(currentPos);
TRACE("_ReadUncachedFrames() - usable buffer found: %p\n", cacheBuffer); TRACE("_ReadUncachedFrames() - usable buffer found: %p, "
"position: %lld/%lld\n", cacheBuffer, currentPos, position);
error = _ReadBuffer(cacheBuffer, currentPos, time); error = _ReadBuffer(cacheBuffer, currentPos, time);
if (error == B_OK) { if (error == B_OK) {
int64 size = min(position + frames, int64 size = min(position + frames,
cacheBuffer->offset + cacheBuffer->size) cacheBuffer->offset + cacheBuffer->size) - position;
- position;
if (size > 0) { if (size > 0) {
_CopyFrames(cacheBuffer, buffer, position, position, size); _CopyFrames(cacheBuffer, buffer, position, position, size);
buffer = SkipFrames(buffer, size); buffer = SkipFrames(buffer, size);
@@ -560,9 +573,13 @@ MediaTrackAudioSupplier::_ReadUncachedFrames(void* buffer, int64 position,
} }
currentPos += cacheBuffer->size; currentPos += cacheBuffer->size;
} }
if (system_time() - time > timeout) {
error = B_TIMED_OUT;
break;
}
} }
#if 1 #if 0
// Ensure that all frames up to the next key frame are cached. // Ensure that all frames up to the next key frame are cached.
// This avoids, that each read reaches the BMediaTrack. // This avoids, that each read reaches the BMediaTrack.
if (error == B_OK) { if (error == B_OK) {
@@ -652,21 +669,29 @@ MediaTrackAudioSupplier::_SeekToKeyFrameForward(int64& position)
status_t status_t
MediaTrackAudioSupplier::_SeekToKeyFrameBackward(int64& position) MediaTrackAudioSupplier::_SeekToKeyFrameBackward(int64& position)
{ {
if (position == fMediaTrack->CurrentFrame()) int64 currentPosition = fMediaTrack->CurrentFrame();
if (position == currentPosition)
return B_OK; return B_OK;
status_t error = B_OK; status_t error = B_OK;
if (fHasKeyFrames) { if (fHasKeyFrames) {
int64 oldPosition = position; int64 wantedPosition = position;
error = fMediaTrack->FindKeyFrameForFrame(&position, error = fMediaTrack->FindKeyFrameForFrame(&position,
B_MEDIA_SEEK_CLOSEST_BACKWARD); B_MEDIA_SEEK_CLOSEST_BACKWARD);
if (error == B_OK && currentPosition > position
&& currentPosition < wantedPosition) {
// The current position is before the wanted position,
// but later than the keyframe, so seeking is worse.
position = currentPosition;
return B_OK;
}
if (error == B_OK) if (error == B_OK)
error = fMediaTrack->SeekToFrame(&position, 0); error = fMediaTrack->SeekToFrame(&position, 0);
if (error != B_OK) { if (error != B_OK) {
position = fMediaTrack->CurrentFrame(); position = fMediaTrack->CurrentFrame();
// if (fReportSeekError) { // if (fReportSeekError) {
printf(" seek to key frame backward: %lld -> %lld (%lld) " printf(" seek to key frame backward: %lld -> %lld (%lld) "
"- %s\n", oldPosition, position, "- %s\n", wantedPosition, position,
fMediaTrack->CurrentFrame(), strerror(error)); fMediaTrack->CurrentFrame(), strerror(error));
fReportSeekError = false; fReportSeekError = false;
// } // }