#include "MediaTrackAudioSupplier.h"
#include <new>
#include <algorithm>
#include <stdio.h>
#include <string.h>
#include <MediaFile.h>
#include <MediaTrack.h>
using namespace std;
#ifdef TRACE_AUDIO_SUPPLIER
# define TRACE(x...) printf("MediaTrackAudioSupplier::" x)
#else
# define TRACE(x...)
#endif
struct MediaTrackAudioSupplier::Buffer {
void* data;
int64 offset;
int64 size;
bigtime_t time_stamp;
static int CompareOffset(const void* a, const void* b);
};
int
MediaTrackAudioSupplier::Buffer::CompareOffset(const void* a, const void* b)
{
const Buffer* buffer1 = *(const Buffer**)a;
const Buffer* buffer2 = *(const Buffer**)b;
int result = 0;
if (buffer1->offset < buffer2->offset)
result = -1;
else if (buffer1->offset > buffer2->offset)
result = 1;
return result;
}
MediaTrackAudioSupplier::MediaTrackAudioSupplier(BMediaTrack* mediaTrack,
int32 trackIndex)
:
AudioTrackSupplier(),
fMediaTrack(mediaTrack),
fBuffer(NULL),
fBufferOffset(0),
fBufferSize(0),
fBuffers(10),
fHasKeyFrames(false),
fCountFrames(0),
fReportSeekError(true),
fTrackIndex(trackIndex)
{
_InitFromTrack();
}
MediaTrackAudioSupplier::~MediaTrackAudioSupplier()
{
_FreeBuffers();
delete[] fBuffer;
}
const media_format&
MediaTrackAudioSupplier::Format() const
{
return AudioReader::Format();
}
status_t
MediaTrackAudioSupplier::GetEncodedFormat(media_format* format) const
{
if (!fMediaTrack)
return B_NO_INIT;
return fMediaTrack->EncodedFormat(format);
}
status_t
MediaTrackAudioSupplier::GetCodecInfo(media_codec_info* info) const
{
if (!fMediaTrack)
return B_NO_INIT;
return fMediaTrack->GetCodecInfo(info);
}
bigtime_t
MediaTrackAudioSupplier::Duration() const
{
if (!fMediaTrack)
return 0;
return fMediaTrack->Duration();
}
bigtime_t
MediaTrackAudioSupplier::InitialLatency() const
{
return 100000;
}
status_t
MediaTrackAudioSupplier::Read(void* buffer, int64 pos, int64 frames)
{
TRACE("Read(%p, %lld, %lld)\n", buffer, pos,
frames);
TRACE(" this: %p, fOutOffset: %lld\n", this, fOutOffset);
status_t error = InitCheck();
if (error != B_OK) {
TRACE("Read() InitCheck failed\n");
return error;
}
pos += fOutOffset;
if (fCountFrames > 0 && pos + frames > fCountFrames) {
int64 size = max((int64)0, fCountFrames - pos);
ReadSilence(SkipFrames(buffer, size), frames - size);
frames = size;
}
TRACE(" after eliminating the frames after the track end: %p, %lld, %lld\n",
buffer, pos, frames);
#if 0
const media_format& format = Format();
int64 size = format.u.raw_audio.buffer_size;
uint32 bytesPerFrame = format.u.raw_audio.channel_count
* (format.u.raw_audio.format
& media_raw_audio_format::B_AUDIO_SIZE_MASK);
uint32 framesPerBuffer = size / bytesPerFrame;
if (fMediaTrack->CurrentFrame() != pos) {
printf(" needing to seek: %lld (%lld)\n", pos,
fMediaTrack->CurrentFrame());
int64 keyFrame = pos;
error = fMediaTrack->FindKeyFrameForFrame(&keyFrame,
B_MEDIA_SEEK_CLOSEST_BACKWARD);
if (error == B_OK) {
error = fMediaTrack->SeekToFrame(&keyFrame,
B_MEDIA_SEEK_CLOSEST_BACKWARD);
}
if (error != B_OK) {
printf(" error seeking to position: %lld (%lld)\n", pos,
fMediaTrack->CurrentFrame());
return error;
}
if (keyFrame < pos) {
printf(" need to skip %lld frames\n", pos - keyFrame);
uint8 dummyBuffer[size];
while (pos - keyFrame >= framesPerBuffer) {
printf(" skipped %lu frames (full buffer)\n", framesPerBuffer);
int64 sizeToRead = size;
fMediaTrack->ReadFrames(dummyBuffer, &sizeToRead);
keyFrame += framesPerBuffer;
}
int64 restSize = pos - keyFrame;
if (restSize > 0) {
printf(" skipped %lu frames (rest)\n", framesPerBuffer);
fMediaTrack->ReadFrames(dummyBuffer, &restSize);
}
}
}
while (frames > 0) {
printf(" reading %lu frames (full buffer)\n", framesPerBuffer);
int64 sizeToRead = min_c(size, frames * bytesPerFrame);
fMediaTrack->ReadFrames(buffer, &sizeToRead);
buffer = (uint8*)buffer + sizeToRead;
frames -= framesPerBuffer;
}
printf(" done\n\n");
#else
bigtime_t time = system_time();
if (frames > 0)
_ReadCachedFrames(buffer, pos, frames, time);
TRACE(" after reading from cache: %p, %lld, %lld\n", buffer, pos, frames);
if (frames > 0)
_ReadUncachedFrames(buffer, pos, frames, time);
#endif
TRACE("Read() done\n");
return B_OK;
}
status_t
MediaTrackAudioSupplier::InitCheck() const
{
status_t error = AudioReader::InitCheck();
if (error == B_OK && (!fMediaTrack || !fBuffer))
error = B_NO_INIT;
return error;
}
void
MediaTrackAudioSupplier::_InitFromTrack()
{
TRACE("_InitFromTrack()\n");
fFormat.u.raw_audio.buffer_size = 16384;
if (fMediaTrack == NULL || fMediaTrack->DecodedFormat(&fFormat) != B_OK
|| fFormat.type != B_MEDIA_RAW_AUDIO) {
fMediaTrack = NULL;
return;
}
#ifdef TRACE_AUDIO_SUPPLIER
char formatString[256];
string_for_format(fFormat, formatString, 256);
TRACE("_InitFromTrack(): format is: %s\n", formatString);
TRACE("_InitFromTrack(): buffer size: %ld\n",
fFormat.u.raw_audio.buffer_size);
#endif
fBuffer = new (nothrow) char[fFormat.u.raw_audio.buffer_size];
_AllocateBuffers();
#if 0
int64 keyFrame0 = 0;
int64 keyFrame1 = 1;
fMediaTrack->FindKeyFrameForFrame(&keyFrame0,
B_MEDIA_SEEK_CLOSEST_BACKWARD);
fMediaTrack->FindKeyFrameForFrame(&keyFrame1,
B_MEDIA_SEEK_CLOSEST_BACKWARD);
fHasKeyFrames = (keyFrame0 == keyFrame1);
#else
fHasKeyFrames = true;
#endif
fCountFrames = fMediaTrack->CountFrames();
TRACE("_InitFromTrack(): keyframes: %" B_PRId16 ", frame count: %" B_PRId64
"\n", fHasKeyFrames, fCountFrames);
printf("_InitFromTrack(): keyframes: %" B_PRId16 ", frame count: %"
B_PRId64 "\n", fHasKeyFrames, fCountFrames);
}
int64
MediaTrackAudioSupplier::_FramesPerBuffer() const
{
int64 sampleSize = fFormat.u.raw_audio.format
& media_raw_audio_format::B_AUDIO_SIZE_MASK;
int64 frameSize = sampleSize * fFormat.u.raw_audio.channel_count;
return fFormat.u.raw_audio.buffer_size / frameSize;
}
void
MediaTrackAudioSupplier::_CopyFrames(void* source, int64 sourceOffset,
void* target, int64 targetOffset,
int64 position, int64 frames) const
{
int64 sampleSize = fFormat.u.raw_audio.format
& media_raw_audio_format::B_AUDIO_SIZE_MASK;
int64 frameSize = sampleSize * fFormat.u.raw_audio.channel_count;
source = (char*)source + frameSize * (position - sourceOffset);
target = (char*)target + frameSize * (position - targetOffset);
memcpy(target, source, frames * frameSize);
}
void
MediaTrackAudioSupplier::_CopyFrames(Buffer* buffer,
void* target, int64 targetOffset,
int64 position, int64 frames) const
{
_CopyFrames(buffer->data, buffer->offset, target, targetOffset, position,
frames);
}
void
MediaTrackAudioSupplier::_AllocateBuffers()
{
int32 count = 10;
_FreeBuffers();
int32 bufferSize = fFormat.u.raw_audio.buffer_size;
char* data = new (nothrow) char[bufferSize * count];
for (; count > 0; count--) {
Buffer* buffer = new (nothrow) Buffer;
if (!buffer || !fBuffers.AddItem(buffer)) {
delete buffer;
if (fBuffers.CountItems() == 0)
delete[] data;
return;
}
buffer->data = data;
data += bufferSize;
buffer->offset = 0;
buffer->size = 0;
buffer->time_stamp = 0;
}
}
void
MediaTrackAudioSupplier::_FreeBuffers()
{
if (fBuffers.CountItems() > 0) {
delete[] (char*)_BufferAt(0)->data;
for (int32 i = 0; Buffer* buffer = _BufferAt(i); i++)
delete buffer;
fBuffers.MakeEmpty();
}
}
MediaTrackAudioSupplier::Buffer*
MediaTrackAudioSupplier::_BufferAt(int32 index) const
{
return (Buffer*)fBuffers.ItemAt(index);
}
MediaTrackAudioSupplier::Buffer*
MediaTrackAudioSupplier::_FindBufferAtFrame(int64 frame) const
{
Buffer* buffer = NULL;
for (int32 i = 0;
((buffer = _BufferAt(i))) && buffer->offset != frame;
i++);
return buffer;
}
MediaTrackAudioSupplier::Buffer*
MediaTrackAudioSupplier::_FindUnusedBuffer() const
{
Buffer* buffer = NULL;
for (int32 i = 0; ((buffer = _BufferAt(i))) && buffer->size != 0; i++)
;
return buffer;
}
MediaTrackAudioSupplier::Buffer*
MediaTrackAudioSupplier::_FindUsableBuffer() const
{
Buffer* result = _FindUnusedBuffer();
if (!result) {
result = _BufferAt(0);
for (int32 i = 1; Buffer* buffer = _BufferAt(i); i++) {
if (buffer->time_stamp < result->time_stamp)
result = buffer;
}
}
return result;
}
MediaTrackAudioSupplier::Buffer*
MediaTrackAudioSupplier::_FindUsableBufferFor(int64 position) const
{
Buffer* buffer = _FindBufferAtFrame(position);
if (buffer == NULL)
buffer = _FindUsableBuffer();
return buffer;
}
void
MediaTrackAudioSupplier::_GetBuffersFor(BList& buffers, int64 position,
int64 frames) const
{
buffers.MakeEmpty();
for (int32 i = 0; Buffer* buffer = _BufferAt(i); i++) {
int32 startFrame = max(position, buffer->offset);
int32 endFrame = min(position + frames, buffer->offset + buffer->size);
if (startFrame < endFrame)
buffers.AddItem(buffer);
}
}
void
MediaTrackAudioSupplier::_TouchBuffer(Buffer* buffer)
{
buffer->time_stamp = system_time();
}
status_t
MediaTrackAudioSupplier::_ReadBuffer(Buffer* buffer, int64 position)
{
return _ReadBuffer(buffer, position, system_time());
}
status_t
MediaTrackAudioSupplier::_ReadBuffer(Buffer* buffer, int64 position,
bigtime_t time)
{
status_t error = fMediaTrack->ReadFrames(buffer->data, &buffer->size);
TRACE("_ReadBuffer(%p, %lld): %s\n", buffer->data, buffer->size,
strerror(error));
buffer->offset = position;
buffer->time_stamp = time;
if (error != B_OK)
buffer->size = 0;
return error;
}
void
MediaTrackAudioSupplier::_ReadCachedFrames(void*& dest, int64& pos,
int64& frames, bigtime_t time)
{
BList buffers(10);
_GetBuffersFor(buffers, pos, frames);
buffers.SortItems(Buffer::CompareOffset);
for (int32 i = 0; Buffer* buffer = (Buffer*)buffers.ItemAt(i); i++) {
if (buffer->offset <= pos && buffer->offset + buffer->size > pos) {
int64 size = min(frames, buffer->offset + buffer->size - pos);
_CopyFrames(buffer->data, buffer->offset, dest, pos, pos, size);
pos += size;
frames -= size;
dest = SkipFrames(dest, size);
buffer->time_stamp = time;
}
}
for (int32 i = buffers.CountItems() - 1;
Buffer* buffer = (Buffer*)buffers.ItemAt(i);
i++) {
if (buffer->offset < pos + frames
&& buffer->offset + buffer->size >= pos + frames) {
int64 size = min(frames, pos + frames - buffer->offset);
_CopyFrames(buffer->data, buffer->offset, dest, pos,
pos + frames - size, size);
frames -= size;
buffer->time_stamp = time;
}
}
}
status_t
MediaTrackAudioSupplier::_ReadUncachedFrames(void* buffer, int64 position,
int64 frames, bigtime_t time)
{
TRACE("_ReadUncachedFrames()\n");
status_t error = B_OK;
int64 currentPos = position;
if (frames > 0) {
error = _SeekToKeyFrameBackward(currentPos);
TRACE("_ReadUncachedFrames() - seeked to position: %lld\n", currentPos);
}
bigtime_t timeout = 10000;
while (error == B_OK && frames > 0) {
Buffer* cacheBuffer = _FindUsableBufferFor(currentPos);
TRACE("_ReadUncachedFrames() - usable buffer found: %p, "
"position: %lld/%lld\n", cacheBuffer, currentPos, position);
error = _ReadBuffer(cacheBuffer, currentPos, time);
if (error == B_OK) {
int64 size = min(position + frames,
cacheBuffer->offset + cacheBuffer->size) - position;
if (size > 0) {
_CopyFrames(cacheBuffer, buffer, position, position, size);
buffer = SkipFrames(buffer, size);
position += size;
frames -= size;
}
currentPos += cacheBuffer->size;
}
if (system_time() - time > timeout) {
error = B_TIMED_OUT;
break;
}
}
#if 0
if (error == B_OK) {
int64 nextKeyFrame = currentPos;
if (_FindKeyFrameForward(nextKeyFrame) == B_OK) {
while (currentPos < nextKeyFrame) {
Buffer* cacheBuffer = _FindBufferAtFrame(currentPos);
if (!cacheBuffer || cacheBuffer->size == 0) {
cacheBuffer = _FindUsableBufferFor(currentPos);
if (_ReadBuffer(cacheBuffer, currentPos, time) != B_OK)
break;
}
if (cacheBuffer)
currentPos += cacheBuffer->size;
}
}
}
#endif
if (error != B_OK && frames > 0)
ReadSilence(buffer, frames);
return error;
}
status_t
MediaTrackAudioSupplier::_FindKeyFrameForward(int64& position)
{
status_t error = B_OK;
if (fHasKeyFrames) {
error = fMediaTrack->FindKeyFrameForFrame(
&position, B_MEDIA_SEEK_CLOSEST_FORWARD);
} else {
int64 framesPerBuffer = _FramesPerBuffer();
position += framesPerBuffer - 1;
position = position % framesPerBuffer;
}
return error;
}
status_t
MediaTrackAudioSupplier::_FindKeyFrameBackward(int64& position)
{
status_t error = B_OK;
if (fHasKeyFrames) {
error = fMediaTrack->FindKeyFrameForFrame(
&position, B_MEDIA_SEEK_CLOSEST_BACKWARD);
} else
position -= position % _FramesPerBuffer();
return error;
}
#if 0
status_t
MediaTrackAudioSupplier::_SeekToKeyFrameForward(int64& position)
{
if (position == fMediaTrack->CurrentFrame())
return B_OK;
status_t error = B_OK;
if (fHasKeyFrames) {
#ifdef TRACE_AUDIO_SUPPLIER
int64 oldPosition = position;
#endif
error = fMediaTrack->SeekToFrame(&position,
B_MEDIA_SEEK_CLOSEST_FORWARD);
TRACE("_SeekToKeyFrameForward() - seek to key frame forward: "
"%lld -> %lld (%lld)\n", oldPosition, position,
fMediaTrack->CurrentFrame());
} else {
_FindKeyFrameForward(position);
error = fMediaTrack->SeekToFrame(&position);
}
return error;
}
#endif
status_t
MediaTrackAudioSupplier::_SeekToKeyFrameBackward(int64& position)
{
int64 currentPosition = fMediaTrack->CurrentFrame();
if (position == currentPosition)
return B_OK;
status_t error = B_OK;
if (fHasKeyFrames) {
int64 wantedPosition = position;
error = fMediaTrack->FindKeyFrameForFrame(&position,
B_MEDIA_SEEK_CLOSEST_BACKWARD);
if (error == B_OK && currentPosition > position
&& currentPosition < wantedPosition) {
position = currentPosition;
return B_OK;
}
if (error == B_OK && position > wantedPosition) {
return B_ERROR;
}
if (error == B_OK)
error = fMediaTrack->SeekToFrame(&position, 0);
if (error != B_OK) {
position = fMediaTrack->CurrentFrame();
printf(" seek to key frame backward: %" B_PRId64 " -> %"
B_PRId64 " (%" B_PRId64 ") - %s\n", wantedPosition,
position, fMediaTrack->CurrentFrame(), strerror(error));
fReportSeekError = false;
} else {
fReportSeekError = true;
}
} else {
_FindKeyFrameBackward(position);
error = fMediaTrack->SeekToFrame(&position);
}
return error;
}