Revision control
Copy as Markdown
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "mfstream_p.h"
#include <QtCore/qcoreapplication.h>
QT_BEGIN_NAMESPACE
//MFStream is added for supporting QIODevice type of media source.
//It is used to delegate invocations from media foundation(through IMFByteStream) to QIODevice.
MFStream::MFStream(QIODevice *stream, bool ownStream, bool synchronous)
: m_stream(stream), m_ownStream(ownStream), m_synchronous(synchronous)
{
//Move to the thread of the stream object
//to make sure invocations on stream
//are happened in the same thread of stream object
if (!synchronous)
this->moveToThread(stream->thread());
}
MFStream::~MFStream()
{
if (m_ownStream)
m_stream->deleteLater();
}
//from IMFByteStream
STDMETHODIMP MFStream::GetCapabilities(DWORD *pdwCapabilities)
{
if (!pdwCapabilities)
return E_INVALIDARG;
*pdwCapabilities = MFBYTESTREAM_IS_READABLE;
if (!m_stream->isSequential())
*pdwCapabilities |= MFBYTESTREAM_IS_SEEKABLE;
return S_OK;
}
STDMETHODIMP MFStream::GetLength(QWORD *pqwLength)
{
if (!pqwLength)
return E_INVALIDARG;
QMutexLocker locker(&m_mutex);
*pqwLength = QWORD(m_stream->size());
return S_OK;
}
STDMETHODIMP MFStream::SetLength(QWORD)
{
return E_NOTIMPL;
}
STDMETHODIMP MFStream::GetCurrentPosition(QWORD *pqwPosition)
{
if (!pqwPosition)
return E_INVALIDARG;
QMutexLocker locker(&m_mutex);
*pqwPosition = m_stream->pos();
return S_OK;
}
STDMETHODIMP MFStream::SetCurrentPosition(QWORD qwPosition)
{
QMutexLocker locker(&m_mutex);
//SetCurrentPosition may happend during the BeginRead/EndRead pair,
//refusing to execute SetCurrentPosition during that time seems to be
//the simplest workable solution
if (m_currentReadResult)
return S_FALSE;
bool seekOK = m_stream->seek(qint64(qwPosition));
if (seekOK)
return S_OK;
else
return S_FALSE;
}
STDMETHODIMP MFStream::IsEndOfStream(BOOL *pfEndOfStream)
{
if (!pfEndOfStream)
return E_INVALIDARG;
QMutexLocker locker(&m_mutex);
*pfEndOfStream = m_stream->atEnd() ? TRUE : FALSE;
return S_OK;
}
STDMETHODIMP MFStream::Read(BYTE *pb, ULONG cb, ULONG *pcbRead)
{
QMutexLocker locker(&m_mutex);
qint64 read = m_stream->read((char*)(pb), qint64(cb));
if (pcbRead)
*pcbRead = ULONG(read);
return S_OK;
}
STDMETHODIMP MFStream::BeginRead(BYTE *pb, ULONG cb, IMFAsyncCallback *pCallback,
IUnknown *punkState)
{
if (!pCallback || !pb)
return E_INVALIDARG;
if (m_synchronous) {
// Synchronous mode: read immediately and invoke callback.
ULONG cbRead = 0;
Read(pb, cb, &cbRead);
ComPtr<IMFAsyncResult> result;
HRESULT hr = MFCreateAsyncResult(nullptr, pCallback, punkState, &result);
if (FAILED(hr))
return hr;
m_syncReadCount = cbRead;
result->SetStatus(S_OK);
hr = MFInvokeCallback(result.Get());
return hr;
}
Q_ASSERT(m_currentReadResult == nullptr);
ComPtr<AsyncReadState> state = makeComObject<AsyncReadState>(pb, cb);
HRESULT hr = MFCreateAsyncResult(state.Get(), pCallback, punkState, &m_currentReadResult);
if (FAILED(hr))
return hr;
QCoreApplication::postEvent(this, new QEvent(QEvent::User));
return hr;
}
STDMETHODIMP MFStream::EndRead(IMFAsyncResult* pResult, ULONG *pcbRead)
{
if (!pcbRead)
return E_INVALIDARG;
if (m_synchronous) {
*pcbRead = m_syncReadCount;
return S_OK;
}
ComPtr<IUnknown> pUnk;
pResult->GetObject(&pUnk);
auto *state = static_cast<AsyncReadState *>(pUnk.Get());
*pcbRead = state->bytesRead();
m_currentReadResult = nullptr;
return S_OK;
}
STDMETHODIMP MFStream::Write(const BYTE *, ULONG, ULONG *)
{
return E_NOTIMPL;
}
STDMETHODIMP MFStream::BeginWrite(const BYTE *, ULONG ,
IMFAsyncCallback *,
IUnknown *)
{
return E_NOTIMPL;
}
STDMETHODIMP MFStream::EndWrite(IMFAsyncResult *,
ULONG *)
{
return E_NOTIMPL;
}
STDMETHODIMP MFStream::Seek(
MFBYTESTREAM_SEEK_ORIGIN SeekOrigin,
LONGLONG llSeekOffset,
DWORD,
QWORD *pqwCurrentPosition)
{
QMutexLocker locker(&m_mutex);
if (m_currentReadResult)
return S_FALSE;
qint64 pos = qint64(llSeekOffset);
switch (SeekOrigin) {
case msoBegin:
break;
case msoCurrent:
pos += m_stream->pos();
break;
}
bool seekOK = m_stream->seek(pos);
if (pqwCurrentPosition)
*pqwCurrentPosition = pos;
if (seekOK)
return S_OK;
else
return S_FALSE;
}
STDMETHODIMP MFStream::Flush()
{
return E_NOTIMPL;
}
STDMETHODIMP MFStream::Close()
{
QMutexLocker locker(&m_mutex);
if (m_ownStream)
m_stream->close();
return S_OK;
}
void MFStream::doRead()
{
if (!m_stream)
return;
bool readDone = true;
ComPtr<IUnknown> pUnk;
HRESULT hr = m_currentReadResult->GetObject(&pUnk);
if (SUCCEEDED(hr)) {
//do actual read
auto *state = static_cast<AsyncReadState *>(pUnk.Get());
ULONG cbRead;
Read(state->pb(), state->cb() - state->bytesRead(), &cbRead);
state->setBytesRead(cbRead + state->bytesRead());
if (state->cb() > state->bytesRead() && !m_stream->atEnd()) {
readDone = false;
}
}
if (readDone) {
//now inform the original caller
m_currentReadResult->SetStatus(hr);
MFInvokeCallback(m_currentReadResult.Get());
}
}
void MFStream::customEvent(QEvent *event)
{
if (event->type() != QEvent::User) {
QObject::customEvent(event);
return;
}
doRead();
}
//AsyncReadState is a helper class used in BeginRead for asynchronous operation
//to record some BeginRead parameters, so these parameters could be
//used later when actually executing the read operation in another thread.
MFStream::AsyncReadState::AsyncReadState(BYTE *pb, ULONG cb)
: m_pb(pb)
, m_cb(cb)
, m_cbRead(0)
{
}
BYTE* MFStream::AsyncReadState::pb() const
{
return m_pb;
}
ULONG MFStream::AsyncReadState::cb() const
{
return m_cb;
}
ULONG MFStream::AsyncReadState::bytesRead() const
{
return m_cbRead;
}
void MFStream::AsyncReadState::setBytesRead(ULONG cbRead)
{
m_cbRead = cbRead;
}
QT_END_NAMESPACE
#include "moc_mfstream_p.cpp"