* Copyright 2016 Dario Casalinuovo. All rights reserved.
* Distributed under the terms of the MIT License.
*
*/
#include "AdapterIO.h"
#include <MediaIO.h>
#include <string.h>
#include "MediaDebug.h"
#define TIMEOUT_QUANTA 100000
class RelativePositionIO : public BPositionIO {
public:
RelativePositionIO(BAdapterIO* owner, BPositionIO* buffer,
bigtime_t timeout)
:
BPositionIO(),
fOwner(owner),
fBackPosition(0),
fStartOffset(0),
fBuffer(buffer),
fTimeout(timeout)
{
}
virtual ~RelativePositionIO()
{
delete fBuffer;
}
status_t ResetStartOffset(off_t offset)
{
status_t ret = fBuffer->SetSize(0);
if (ret != B_OK)
return ret;
fBackPosition = 0;
fStartOffset = offset;
return B_OK;
}
status_t FlushBefore(off_t position, BPositionIO* buffer, const void* oldBuffer,
size_t oldLength)
{
AutoWriteLocker _(fLock);
off_t relative = _PositionToRelative(position);
if (relative < 0)
return B_OK;
if (relative > (off_t)oldLength)
return B_BAD_VALUE;
status_t status = buffer->WriteAt(0, (void*)((addr_t)oldBuffer + relative),
oldLength - relative);
if (status < B_OK)
return status;
status = buffer->Seek(fBuffer->Position() - relative, SEEK_SET);
if (status < B_OK)
return status;
fBackPosition -= relative;
fStartOffset += relative;
SetBuffer(buffer);
return B_OK;
}
status_t EvaluatePosition(off_t position, off_t totalSize)
{
if (position < 0)
return B_ERROR;
if (position < fStartOffset)
return B_RESOURCE_UNAVAILABLE;
if (totalSize > 0 && position > totalSize) {
if (IsMutable())
return B_WOULD_BLOCK;
else
return B_ERROR;
}
return B_OK;
}
status_t WaitForData(off_t position, off_t size)
{
off_t bufferSize = 0;
status_t ret = GetSize(&bufferSize);
if (ret != B_OK)
return B_ERROR;
bigtime_t totalTimeOut = 0;
while (bufferSize < position + size) {
if (!fOwner->IsRunning())
return B_NOT_SUPPORTED;
if (fTimeout != B_INFINITE_TIMEOUT && totalTimeOut >= fTimeout)
return B_TIMED_OUT;
snooze(TIMEOUT_QUANTA);
totalTimeOut += TIMEOUT_QUANTA;
GetSize(&bufferSize);
}
return B_OK;
}
virtual ssize_t ReadAt(off_t position, void* buffer,
size_t size)
{
AutoReadLocker _(fLock);
return fBuffer->ReadAt(
_PositionToRelative(position), buffer, size);
}
virtual ssize_t WriteAt(off_t position,
const void* buffer, size_t size)
{
AutoWriteLocker _(fLock);
return fBuffer->WriteAt(
_PositionToRelative(position), buffer, size);
}
virtual off_t Seek(off_t position, uint32 seekMode)
{
AutoWriteLocker _(fLock);
if (seekMode == SEEK_SET)
return fBuffer->Seek(_PositionToRelative(position), seekMode);
return fBuffer->Seek(position, seekMode);
}
virtual off_t Position() const
{
AutoReadLocker _(fLock);
return _RelativeToPosition(fBuffer->Position());
}
virtual status_t SetSize(off_t size)
{
AutoWriteLocker _(fLock);
return fBuffer->SetSize(_PositionToRelative(size));
}
virtual status_t GetSize(off_t* size) const
{
AutoReadLocker _(fLock);
*size = _RelativeToPosition(fBackPosition);
return B_OK;
}
ssize_t BackWrite(const void* buffer, size_t size)
{
AutoWriteLocker _(fLock);
off_t ret = fBuffer->WriteAt(fBackPosition, buffer, size);
fBackPosition += ret;
return ret;
}
void SetBuffer(BPositionIO* buffer)
{
delete fBuffer;
fBuffer = buffer;
}
bool IsStreaming() const
{
int32 flags = 0;
fOwner->GetFlags(&flags);
return ((flags & B_MEDIA_STREAMING) == B_MEDIA_STREAMING);
}
bool IsMutable() const
{
int32 flags = 0;
fOwner->GetFlags(&flags);
return ((flags & B_MEDIA_MUTABLE_SIZE) == B_MEDIA_MUTABLE_SIZE);
}
bool IsSeekable() const
{
int32 flags = 0;
fOwner->GetFlags(&flags);
return ((flags & B_MEDIA_SEEKABLE) == B_MEDIA_SEEKABLE);
}
const BPositionIO* Buffer() const
{
return fBuffer;
}
private:
off_t _PositionToRelative(off_t position) const
{
return position - fStartOffset;
}
off_t _RelativeToPosition(off_t position) const
{
return position + fStartOffset;
}
BAdapterIO* fOwner;
off_t fBackPosition;
off_t fStartOffset;
BPositionIO* fBuffer;
mutable RWLocker fLock;
bigtime_t fTimeout;
};
BAdapterIO::BAdapterIO(int32 flags, bigtime_t timeout)
:
fFlags(flags),
fBuffer(NULL),
fTotalSize(0),
fOpened(false),
fSeekSem(-1),
fInputAdapter(NULL)
{
CALLED();
fBuffer = new RelativePositionIO(this, new BMallocIO(), timeout);
}
BAdapterIO::BAdapterIO(const BAdapterIO &)
{
}
BAdapterIO::~BAdapterIO()
{
CALLED();
delete fInputAdapter;
delete fBuffer;
}
void
BAdapterIO::GetFlags(int32* flags) const
{
CALLED();
*flags = fFlags;
}
ssize_t
BAdapterIO::ReadAt(off_t position, void* buffer, size_t size)
{
CALLED();
status_t ret = _EvaluateWait(position, size);
if (ret != B_OK)
return ret;
return fBuffer->ReadAt(position, buffer, size);
}
ssize_t
BAdapterIO::WriteAt(off_t position, const void* buffer, size_t size)
{
CALLED();
return fBuffer->WriteAt(position, buffer, size);
}
off_t
BAdapterIO::Seek(off_t position, uint32 seekMode)
{
CALLED();
off_t absolutePosition = 0;
off_t size = 0;
if (seekMode == SEEK_CUR)
absolutePosition = Position()+position;
else if (seekMode == SEEK_END) {
if (GetSize(&size) != B_OK)
return B_NOT_SUPPORTED;
absolutePosition = size-position;
}
status_t ret = _EvaluateWait(absolutePosition, 0);
if (ret == B_RESOURCE_UNAVAILABLE && fBuffer->IsStreaming()
&& fBuffer->IsSeekable()) {
fSeekSem = create_sem(0, "BAdapterIO seek sem");
if (SeekRequested(absolutePosition) != B_OK)
return B_NOT_SUPPORTED;
TRACE("BAdapterIO::Seek: Locking on backend seek\n");
acquire_sem(fSeekSem);
TRACE("BAdapterIO::Seek: Seek completed!\n");
fBuffer->ResetStartOffset(absolutePosition);
} else if (ret != B_OK)
return B_NOT_SUPPORTED;
return fBuffer->Seek(position, seekMode);
}
off_t
BAdapterIO::Position() const
{
CALLED();
return fBuffer->Position();
}
status_t
BAdapterIO::SetSize(off_t size)
{
CALLED();
if (!fBuffer->IsMutable()) {
fTotalSize = size;
return B_OK;
}
return fBuffer->SetSize(size);
}
status_t
BAdapterIO::GetSize(off_t* size) const
{
CALLED();
if (!fBuffer->IsMutable()) {
*size = fTotalSize;
return B_OK;
}
return fBuffer->GetSize(size);
}
status_t
BAdapterIO::Open()
{
CALLED();
fOpened = true;
return B_OK;
}
bool
BAdapterIO::IsRunning() const
{
return fOpened;
}
void
BAdapterIO::SeekCompleted()
{
CALLED();
release_sem(fSeekSem);
delete_sem(fSeekSem);
fSeekSem = -1;
}
status_t
BAdapterIO::SetBuffer(BPositionIO* buffer)
{
if (fOpened)
return B_ERROR;
fBuffer->SetBuffer(buffer);
return B_OK;
}
status_t
BAdapterIO::FlushBefore(off_t position)
{
BMallocIO* buffer = new BMallocIO();
BMallocIO* oldBuffer = (BMallocIO*)fBuffer->Buffer();
fBuffer->FlushBefore(position, buffer, oldBuffer->Buffer(), oldBuffer->BufferLength());
return B_OK;
}
BInputAdapter*
BAdapterIO::BuildInputAdapter()
{
if (fInputAdapter != NULL)
return fInputAdapter;
fInputAdapter = new BInputAdapter(this);
return fInputAdapter;
}
status_t
BAdapterIO::SeekRequested(off_t position)
{
CALLED();
return B_ERROR;
}
ssize_t
BAdapterIO::BackWrite(const void* buffer, size_t size)
{
return fBuffer->BackWrite(buffer, size);
}
status_t
BAdapterIO::_EvaluateWait(off_t pos, off_t size)
{
CALLED();
off_t totalSize = 0;
if (GetSize(&totalSize) != B_OK)
TRACE("BAdapterIO::ReadAt: Can't get our size!\n");
TRACE("BAdapterIO::_EvaluateWait TS %" B_PRId64 " P %" B_PRId64
" S %" B_PRId64 "\n", totalSize, pos, size);
status_t err = fBuffer->EvaluatePosition(pos, totalSize);
TRACE("BAdapterIO::_EvaluateWait: %s\n", strerror(err));
if (err != B_OK && err != B_WOULD_BLOCK)
return err;
TRACE("BAdapterIO::_EvaluateWait: waiting for data\n");
return fBuffer->WaitForData(pos, size);
}
BInputAdapter::BInputAdapter(BAdapterIO* io)
:
fIO(io)
{
}
BInputAdapter::~BInputAdapter()
{
}
ssize_t
BInputAdapter::Write(const void* buffer, size_t size)
{
return fIO->BackWrite(buffer, size);
}
void BAdapterIO::_ReservedAdapterIO1() {}
void BAdapterIO::_ReservedAdapterIO2() {}
void BAdapterIO::_ReservedAdapterIO3() {}
void BAdapterIO::_ReservedAdapterIO4() {}
void BAdapterIO::_ReservedAdapterIO5() {}
void BInputAdapter::_ReservedInputAdapter1() {}
void BInputAdapter::_ReservedInputAdapter2() {}