// 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.hpp" | |
#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; | |
} |