blob: 8887125d54fb13b45f9e7c5391060404cdd29f93 [file] [log] [blame]
// Copyright (c) 2010 The WebM project authors. All Rights Reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree. An additional intellectual property rights grant can be found
// in the file PATENTS. All contributing project authors may
// be found in the AUTHORS file in the root of the source tree.
#include "CVP8Sample.h"
#include <new>
#include <cassert>
#include <vfwmsgs.h>
HRESULT CVP8Sample::CreateAllocator(IMemAllocator** pp)
{
if (pp == 0)
return E_POINTER;
IMemAllocator*& pResult = *pp;
pResult = 0;
SampleFactory* pSampleFactory = new (std::nothrow) SampleFactory;
if (pSampleFactory == 0)
return E_OUTOFMEMORY;
const HRESULT hr = CMemAllocator::CreateInstance(pSampleFactory, &pResult);
if (FAILED(hr))
{
pSampleFactory->Destroy(0);
return VFW_E_NO_ALLOCATOR;
}
return S_OK;
}
HRESULT CVP8Sample::GetFrame(CMemAllocator* pAlloc, IVP8Sample::Frame& f)
{
CMemAllocator::Lock lock;
HRESULT hr = lock.Seize(pAlloc);
if (FAILED(hr))
return hr;
ALLOCATOR_PROPERTIES props;
hr = pAlloc->GetProperties(&props);
assert(SUCCEEDED(hr));
assert(props.cBuffers > 0);
assert(props.cbBuffer > 0);
assert(props.cbAlign >= 1);
assert(props.cbPrefix >= 0);
const long buflen = props.cbAlign - 1 + props.cbPrefix + props.cbBuffer;
CMemAllocator::ISampleFactory* const pFactory_ = pAlloc->m_pSampleFactory;
assert(pFactory_);
SampleFactory* const pFactory = static_cast<SampleFactory*>(pFactory_);
SampleFactory::frames_t& pool = pFactory->m_pool;
if (!pool.empty())
{
f = pool.front();
assert(f.buf);
assert(f.buflen >= buflen);
pool.pop_front();
}
else
{
BYTE* const buf = new (std::nothrow) BYTE[buflen];
if (buf == 0)
return E_OUTOFMEMORY;
f.buf = buf;
f.buflen = buflen;
long off = props.cbPrefix;
if (intptr_t n = intptr_t(buf) % props.cbAlign)
off += props.cbAlign - n;
f.off = off;
}
BYTE* const ptr = f.buf + f.off;
ptr;
assert(intptr_t(ptr - props.cbPrefix) % props.cbAlign == 0);
return S_OK;
}
CVP8Sample::SampleFactory::SampleFactory()
{
}
CVP8Sample::SampleFactory::~SampleFactory()
{
PurgePool();
}
HRESULT CVP8Sample::SampleFactory::CreateSample(
CMemAllocator* pAllocator,
IMemSample*& pResult)
{
pResult = 0;
CVP8Sample* pSample;
const HRESULT hr = CVP8Sample::CreateInstance(pAllocator, pSample);
if (FAILED(hr))
return hr;
assert(pSample);
assert(pSample->GetCount() == 0);
pResult = pSample;
return S_OK;
}
HRESULT CVP8Sample::SampleFactory::InitializeSample(IMemSample* p)
{
assert(p);
const HRESULT hr = p->Initialize();
assert(SUCCEEDED(hr));
assert(p->GetCount() == 0);
return S_OK;
}
HRESULT CVP8Sample::SampleFactory::FinalizeSample(IMemSample* p)
{
assert(p);
//Note that FinalizeSample is called by the allocator while
//it holds its own lock. There's no special locking we need
//to here, because the allocator owns the sample factory
//object it was given when it (the allocator) was created.
IVP8Sample* pSample;
HRESULT hr = p->QueryInterface(&pSample);
assert(SUCCEEDED(hr));
assert(pSample);
//NOTE: we don't bother releasing the IVP8Sample ptr, because
//the sample is in the process of being destroyed. We don't want
//to trigger another call to IMemAllocator::ReleaseBuffer from
//IMediaSample::Release.
IVP8Sample::Frame& f = pSample->GetFrame();
assert(f.buf);
m_pool.push_back(f);
f.buf = 0;
hr = p->Finalize();
assert(SUCCEEDED(hr));
return S_OK;
}
HRESULT CVP8Sample::SampleFactory::DestroySample(IMemSample* pSample)
{
assert(pSample);
return pSample->Destroy();
}
HRESULT CVP8Sample::SampleFactory::Destroy(CMemAllocator*)
{
delete this;
return S_OK;
}
void CVP8Sample::SampleFactory::PurgePool()
{
while (!m_pool.empty())
{
IVP8Sample::Frame& f = m_pool.front();
assert(f.buf);
delete[] f.buf;
m_pool.pop_front();
}
}
HRESULT CVP8Sample::CreateInstance(
CMemAllocator* pAllocator,
CVP8Sample*& pSample)
{
if (pAllocator == 0)
return E_INVALIDARG;
pSample = new (std::nothrow) CVP8Sample(pAllocator);
return pSample ? S_OK : E_OUTOFMEMORY;
}
CVP8Sample::CVP8Sample(CMemAllocator* p)
: m_pAllocator(p),
m_cRef(0) //allocator will adjust
{
m_frame.buf = 0;
}
CVP8Sample::~CVP8Sample()
{
assert(m_frame.buf == 0);
}
HRESULT CVP8Sample::QueryInterface(const IID& iid, void** ppv)
{
if (ppv == 0)
return E_POINTER;
IUnknown*& pUnk = reinterpret_cast<IUnknown*&>(*ppv);
if (iid == __uuidof(IUnknown))
pUnk = static_cast<IMediaSample*>(this);
else if (iid == __uuidof(IMediaSample))
pUnk = static_cast<IMediaSample*>(this);
else if (iid == __uuidof(IMemSample))
pUnk = static_cast<IMemSample*>(this);
else if (iid == __uuidof(IVP8Sample))
pUnk = static_cast<IVP8Sample*>(this);
else
{
pUnk = 0;
return E_NOINTERFACE;
}
pUnk->AddRef();
return S_OK;
}
ULONG CVP8Sample::AddRef()
{
return InterlockedIncrement((LONG*)&m_cRef);
}
ULONG CVP8Sample::Release()
{
assert(m_cRef > 0);
if (LONG n = InterlockedDecrement((LONG*)&m_cRef))
return n;
m_pAllocator->ReleaseBuffer(this);
return 0;
}
CVP8Sample::Frame& CVP8Sample::GetFrame()
{
return m_frame;
}
ULONG CVP8Sample::GetCount()
{
return m_cRef;
}
HRESULT CVP8Sample::Initialize()
{
assert(m_frame.buf == 0);
m_cRef = 0;
return S_OK;
}
HRESULT CVP8Sample::Finalize()
{
assert(m_frame.buf == 0);
return S_OK;
}
HRESULT CVP8Sample::Destroy()
{
delete this;
return S_OK;
}
HRESULT CVP8Sample::GetPointer(BYTE** pp)
{
if (pp == 0)
return E_POINTER;
BYTE*& p = *pp;
const Frame& f = m_frame;
assert(f.buf);
assert(f.buflen >= 0);
assert(f.off >= 0);
assert(f.off <= f.buflen);
p = f.buf + f.off;
#ifdef _DEBUG
ALLOCATOR_PROPERTIES props;
const HRESULT hr = m_pAllocator->GetProperties(&props);
assert(SUCCEEDED(hr));
assert(props.cbAlign >= 1);
assert(props.cbPrefix >= 0);
assert(intptr_t(p - props.cbPrefix) % props.cbAlign == 0);
#endif
return S_OK;
}
long CVP8Sample::GetSize()
{
const Frame& f = m_frame;
assert(f.buf);
assert(f.off <= f.buflen);
const long size = f.buflen - f.off;
assert(size >= 0);
#ifdef _DEBUG
ALLOCATOR_PROPERTIES props;
const HRESULT hr = m_pAllocator->GetProperties(&props);
assert(SUCCEEDED(hr));
assert(size >= props.cbBuffer);
#endif
return size;
}
HRESULT CVP8Sample::GetTime(
REFERENCE_TIME* pstart,
REFERENCE_TIME* pstop)
{
const Frame& f = m_frame;
assert(f.buf);
assert(f.start >= 0);
if (pstart == 0)
return E_POINTER;
*pstart = f.start;
if (pstop == 0)
return S_OK;
if (f.stop < f.start) //no stop time
{
*pstop = f.start + 1;
return VFW_S_NO_STOP_TIME;
}
*pstop = f.stop;
return S_OK;
}
HRESULT CVP8Sample::SetTime(
REFERENCE_TIME*,
REFERENCE_TIME*)
{
return E_NOTIMPL;
}
HRESULT CVP8Sample::IsSyncPoint()
{
assert(m_frame.buf);
return m_frame.key ? S_OK : S_FALSE;
}
HRESULT CVP8Sample::SetSyncPoint(BOOL)
{
return E_NOTIMPL;
}
HRESULT CVP8Sample::IsPreroll()
{
assert(m_frame.buf);
//TODO:
return m_preroll ? S_OK : S_FALSE;
}
HRESULT CVP8Sample::SetPreroll(BOOL b)
{
assert(m_frame.buf);
//TODO:
m_preroll = bool(b != 0);
return S_OK;
}
long CVP8Sample::GetActualDataLength()
{
assert(m_frame.buf);
assert(m_frame.len >= 0);
return m_frame.len;
}
HRESULT CVP8Sample::SetActualDataLength(long)
{
return E_NOTIMPL;
}
HRESULT CVP8Sample::GetMediaType(
AM_MEDIA_TYPE** pp)
{
if (pp == 0)
return E_POINTER;
AM_MEDIA_TYPE*& p = *pp;
p = 0;
return S_FALSE; //means "no media type"
}
HRESULT CVP8Sample::SetMediaType(
AM_MEDIA_TYPE*)
{
return E_NOTIMPL;
}
HRESULT CVP8Sample::IsDiscontinuity()
{
assert(m_frame.buf);
//TODO:
return m_discontinuity ? S_OK : S_FALSE;
}
HRESULT CVP8Sample::SetDiscontinuity(BOOL b)
{
assert(m_frame.buf);
//TODO:
m_discontinuity = bool(b != 0);
return S_OK;
}
HRESULT CVP8Sample::GetMediaTime(
LONGLONG*,
LONGLONG*)
{
return VFW_E_MEDIA_TIME_NOT_SET;
}
HRESULT CVP8Sample::SetMediaTime(
LONGLONG*,
LONGLONG*)
{
return E_NOTIMPL;
}