blob: 1740602b454338444caf0a62a2fd4d273e90598b [file]
// 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 <strmif.h>
#include <uuids.h>
#include "vp8encoderfilter.hpp"
#include "vp8encoderidl.h"
#include "cenumpins.hpp"
#include <new>
#include <cassert>
#include <vfwmsgs.h>
#ifdef _DEBUG
#include "iidstr.hpp"
#include "odbgstream.hpp"
using std::endl;
using std::hex;
using std::dec;
#endif
using std::wstring;
namespace VP8EncoderLib
{
extern const CLSID CLSID_PropPage;
HRESULT CreateFilter(
IClassFactory* pClassFactory,
IUnknown* pOuter,
const IID& iid,
void** ppv)
{
if (ppv == 0)
return E_POINTER;
*ppv = 0;
if ((pOuter != 0) && (iid != __uuidof(IUnknown)))
return E_INVALIDARG;
Filter* p = new (std::nothrow) Filter(pClassFactory, pOuter);
if (p == 0)
return E_OUTOFMEMORY;
assert(p->m_nondelegating.m_cRef == 0);
const HRESULT hr = p->m_nondelegating.QueryInterface(iid, ppv);
if (SUCCEEDED(hr))
{
assert(*ppv);
assert(p->m_nondelegating.m_cRef == 1);
return S_OK;
}
assert(*ppv == 0);
assert(p->m_nondelegating.m_cRef == 0);
delete p;
p = 0;
return hr;
}
#pragma warning(disable:4355) //'this' ptr in member init list
Filter::Filter(IClassFactory* pClassFactory, IUnknown* pOuter)
: m_pClassFactory(pClassFactory),
m_nondelegating(this),
m_pOuter(pOuter ? pOuter : &m_nondelegating),
m_state(State_Stopped),
m_clock(0),
m_inpin(this),
m_outpin_video(this),
m_outpin_preview(this),
m_bDirty(false),
m_bForceKeyframe(false),
m_keyframe_interval(0),
m_decimate(0)
{
m_pClassFactory->LockServer(TRUE);
const HRESULT hr = CLockable::Init();
assert(SUCCEEDED(hr));
m_info.pGraph = 0;
m_info.achName[0] = L'\0';
m_cfg.Init();
#ifdef _DEBUG
odbgstream os;
os << "vp8enc::filter::ctor" << endl;
#endif
}
#pragma warning(default:4355)
Filter::~Filter()
{
#ifdef _DEBUG
odbgstream os;
os << "vp8enc::filter::dtor" << endl;
#endif
m_pClassFactory->LockServer(FALSE);
}
void Filter::Config::Init()
{
deadline = -1;
threads = -1;
error_resilient = -1;
lag_in_frames = -1;
target_bitrate = -1;
min_quantizer = -1;
max_quantizer = -1;
undershoot_pct = -1;
overshoot_pct = -1;
decoder_buffer_size = -1;
decoder_buffer_initial_size = -1;
decoder_buffer_optimal_size = -1;
keyframe_mode = -1;
keyframe_min_interval = -1;
keyframe_max_interval = -1;
dropframe_thresh = -1;
resize_allowed = -1;
resize_up_thresh = -1;
resize_down_thresh = -1;
end_usage = -1;
token_partitions = -1;
pass_mode = -1;
two_pass_stats_buf = 0;
two_pass_stats_buflen = -1;
two_pass_vbr_bias_pct = -1;
two_pass_vbr_minsection_pct = -1;
two_pass_vbr_maxsection_pct = -1;
auto_alt_ref = -1;
arnr_max_frames = -1;
arnr_strength = -1;
arnr_type = -1;
cpu_used = -17;
static_threshold = -1;
}
Filter::CNondelegating::CNondelegating(Filter* p)
: m_pFilter(p),
m_cRef(0) //see CreateInstance
{
}
Filter::CNondelegating::~CNondelegating()
{
}
HRESULT Filter::CNondelegating::QueryInterface(
const IID& iid,
void** ppv)
{
if (ppv == 0)
return E_POINTER;
IUnknown*& pUnk = reinterpret_cast<IUnknown*&>(*ppv);
if (iid == __uuidof(IUnknown))
{
pUnk = this; //must be nondelegating
}
else if ((iid == __uuidof(IBaseFilter)) ||
(iid == __uuidof(IMediaFilter)) ||
(iid == __uuidof(IPersist)))
{
pUnk = static_cast<IBaseFilter*>(m_pFilter);
}
else if (iid == __uuidof(IPersistStream))
{
pUnk = static_cast<IPersistStream*>(m_pFilter);
}
else if (iid == __uuidof(IVP8Encoder))
{
pUnk = static_cast<IVP8Encoder*>(m_pFilter);
}
else if (iid == __uuidof(ISpecifyPropertyPages))
{
pUnk = static_cast<ISpecifyPropertyPages*>(m_pFilter);
}
else
{
#if 0
wodbgstream os;
os << "vp8enc::filter::QI: iid=" << IIDStr(iid) << std::endl;
#endif
pUnk = 0;
return E_NOINTERFACE;
}
pUnk->AddRef();
return S_OK;
}
ULONG Filter::CNondelegating::AddRef()
{
return InterlockedIncrement(&m_cRef);
}
ULONG Filter::CNondelegating::Release()
{
const LONG n = InterlockedDecrement(&m_cRef);
//odbgstream os;
//os << "Filter::Release: n=" << n << endl;
if (n > 0)
return n;
delete m_pFilter;
return 0;
}
HRESULT Filter::QueryInterface(const IID& iid, void** ppv)
{
return m_pOuter->QueryInterface(iid, ppv);
}
ULONG Filter::AddRef()
{
return m_pOuter->AddRef();
}
ULONG Filter::Release()
{
return m_pOuter->Release();
}
HRESULT Filter::GetClassID(CLSID* p)
{
if (p == 0)
return E_POINTER;
*p = CLSID_VP8Encoder;
return S_OK;
}
HRESULT Filter::Stop()
{
//Stop is a synchronous operation: when it completes,
//the filter is stopped.
//odbgstream os;
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
//odbgstream os;
//os << "mkvsplit::Filter::Stop" << endl;
switch (m_state)
{
case State_Paused:
case State_Running:
m_state = State_Stopped;
OnStop(); //decommit outpin's allocator
break;
case State_Stopped:
default:
break;
}
return S_OK;
}
HRESULT Filter::Pause()
{
//Unlike Stop(), Pause() can be asynchronous (that's why you have
//GetState()).
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
//odbgstream os;
//os << "mkvsplit::Filter::Pause" << endl;
switch (m_state)
{
case State_Stopped:
hr = OnStart(); //commit outpin's allocator
if (FAILED(hr))
return hr;
break;
case State_Running:
case State_Paused:
default:
break;
}
m_state = State_Paused;
return S_OK;
}
HRESULT Filter::Run(REFERENCE_TIME start)
{
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
//odbgstream os;
//os << "mkvsplit::Filter::Run" << endl;
switch (m_state)
{
case State_Stopped:
hr = OnStart(); //commit outpin's allocator
if (FAILED(hr))
return hr;
break;
case State_Paused:
case State_Running:
default:
break;
}
m_start = start;
m_state = State_Running;
return S_OK;
}
HRESULT Filter::GetState(
DWORD,
FILTER_STATE* p)
{
if (p == 0)
return E_POINTER;
Lock lock;
const HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
*p = m_state;
return S_OK;
}
HRESULT Filter::SetSyncSource(
IReferenceClock* clock)
{
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
if (m_clock)
m_clock->Release();
m_clock = clock;
if (m_clock)
m_clock->AddRef();
return S_OK;
}
HRESULT Filter::GetSyncSource(
IReferenceClock** pclock)
{
if (pclock == 0)
return E_POINTER;
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
IReferenceClock*& clock = *pclock;
clock = m_clock;
if (clock)
clock->AddRef();
return S_OK;
}
HRESULT Filter::EnumPins(IEnumPins** pp)
{
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
IPin* pins[3] =
{
&m_inpin,
&m_outpin_video,
&m_outpin_preview
};
return CEnumPins::CreateInstance(pins, 3, pp);
}
HRESULT Filter::FindPin(
LPCWSTR id1,
IPin** pp)
{
if (pp == 0)
return E_POINTER;
IPin*& p = *pp;
p = 0;
if (id1 == 0)
return E_INVALIDARG;
Pin* pins[3] =
{
&m_inpin,
&m_outpin_video,
&m_outpin_preview
};
Pin** iter = pins;
for (int i = 0; i < 3; ++i)
{
Pin* const pin = *iter++;
const wstring& id2_ = pin->m_id;
const wchar_t* const id2 = id2_.c_str();
if (wcscmp(id1, id2) == 0) //case-sensitive
{
p = pin;
p->AddRef();
return S_OK;
}
}
return VFW_E_NOT_FOUND;
}
HRESULT Filter::QueryFilterInfo(FILTER_INFO* p)
{
if (p == 0)
return E_POINTER;
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
enum { size = sizeof(p->achName)/sizeof(WCHAR) };
const errno_t e = wcscpy_s(p->achName, size, m_info.achName);
e;
assert(e == 0);
p->pGraph = m_info.pGraph;
if (p->pGraph)
p->pGraph->AddRef();
return S_OK;
}
HRESULT Filter::JoinFilterGraph(
IFilterGraph *pGraph,
LPCWSTR name)
{
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
//NOTE:
//No, do not adjust reference counts here!
//Read the docs for the reasons why.
//ENDNOTE.
m_info.pGraph = pGraph;
if (name == 0)
m_info.achName[0] = L'\0';
else
{
enum { size = sizeof(m_info.achName)/sizeof(WCHAR) };
const errno_t e = wcscpy_s(m_info.achName, size, name);
e;
assert(e == 0); //TODO
}
return S_OK;
}
HRESULT Filter::QueryVendorInfo(LPWSTR* pstr)
{
if (pstr == 0)
return E_POINTER;
wchar_t*& str = *pstr;
str = 0;
return E_NOTIMPL;
}
HRESULT Filter::ApplySettings()
{
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
if (m_state == State_Stopped)
return S_FALSE;
wstring msg;
return m_inpin.OnApplySettings(msg);
}
HRESULT Filter::ResetSettings()
{
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
m_cfg.Init();
m_bDirty = true;
return S_OK;
}
HRESULT Filter::SetDeadline(int deadline)
{
if (deadline < 0)
return E_INVALIDARG;
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
m_cfg.deadline = deadline;
m_bDirty = true;
return S_OK;
}
HRESULT Filter::GetDeadline(int* p)
{
if (p == 0)
return E_POINTER;
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
*p = m_cfg.deadline;
return S_OK;
}
HRESULT Filter::SetThreadCount(int count)
{
if (count < 0)
return E_INVALIDARG;
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
m_cfg.threads = count;
m_bDirty = true;
return S_OK;
}
HRESULT Filter::GetThreadCount(int* pCount)
{
if (pCount == 0)
return E_POINTER;
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
*pCount = m_cfg.threads;
return S_OK;
}
HRESULT Filter::SetErrorResilient(int val)
{
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
m_cfg.error_resilient = val;
m_bDirty = true;
return S_OK;
}
HRESULT Filter::GetErrorResilient(int* p)
{
if (p == 0)
return E_POINTER;
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
*p = m_cfg.error_resilient;
return S_OK;
}
HRESULT Filter::SetDropframeThreshold(int val)
{
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
m_cfg.dropframe_thresh = val;
m_bDirty = true;
return S_OK;
}
HRESULT Filter::GetDropframeThreshold(int* p)
{
if (p == 0)
return E_POINTER;
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
*p = m_cfg.dropframe_thresh;
return S_OK;
}
HRESULT Filter::SetResizeAllowed(int val)
{
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
m_cfg.resize_allowed = val;
m_bDirty = true;
return S_OK;
}
HRESULT Filter::GetResizeAllowed(int* p)
{
if (p == 0)
return E_POINTER;
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
*p = m_cfg.resize_allowed;
return S_OK;
}
HRESULT Filter::SetResizeUpThreshold(int val)
{
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
m_cfg.resize_up_thresh = val;
m_bDirty = true;
return S_OK;
}
HRESULT Filter::GetResizeUpThreshold(int* p)
{
if (p == 0)
return E_POINTER;
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
*p = m_cfg.resize_up_thresh;
return S_OK;
}
HRESULT Filter::SetResizeDownThreshold(int val)
{
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
m_cfg.resize_down_thresh = val;
m_bDirty = true;
return S_OK;
}
HRESULT Filter::GetResizeDownThreshold(int* p)
{
if (p == 0)
return E_POINTER;
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
*p = m_cfg.resize_down_thresh;
return S_OK;
}
HRESULT Filter::SetEndUsage(VP8EndUsage val_)
{
const int val = val_;
switch (val)
{
case kEndUsageVBR:
case kEndUsageCBR:
case kEndUsageDefault:
break;
default:
return E_INVALIDARG;
}
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
m_cfg.end_usage = val;
m_bDirty = true;
return S_OK;
}
HRESULT Filter::GetEndUsage(VP8EndUsage* p)
{
if (p == 0)
return E_POINTER;
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
*p = static_cast<VP8EndUsage>(m_cfg.end_usage);
return S_OK;
}
HRESULT Filter::SetLagInFrames(int LagInFrames)
{
if (LagInFrames < 0)
return E_INVALIDARG;
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
m_cfg.lag_in_frames = LagInFrames;
m_bDirty = true;
return S_OK;
}
HRESULT Filter::GetLagInFrames(int* p)
{
if (p == 0)
return E_POINTER;
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
*p = m_cfg.lag_in_frames;
return S_OK;
}
HRESULT Filter::SetTokenPartitions(int val)
{
if (val > 3)
return E_INVALIDARG;
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
if (m_state != State_Stopped)
return VFW_E_NOT_STOPPED;
m_cfg.token_partitions = val;
m_bDirty = true;
return S_OK;
}
HRESULT Filter::GetTokenPartitions(int* p)
{
if (p == 0)
return E_POINTER;
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
*p = m_cfg.token_partitions;
return S_OK;
}
HRESULT Filter::SetTargetBitrate(int value)
{
if (value < 0)
return E_INVALIDARG;
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
m_cfg.target_bitrate = value;
m_bDirty = true;
return S_OK;
}
HRESULT Filter::GetTargetBitrate(int* p)
{
if (p == 0)
return E_POINTER;
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
*p = m_cfg.target_bitrate;
return S_OK;
}
HRESULT Filter::SetMinQuantizer(int val)
{
if (val > 63)
return E_INVALIDARG;
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
m_cfg.min_quantizer = val;
m_bDirty = true;
return S_OK;
}
HRESULT Filter::GetMinQuantizer(int* p)
{
if (p == 0)
return E_POINTER;
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
*p = m_cfg.min_quantizer;
return S_OK;
}
HRESULT Filter::SetMaxQuantizer(int val)
{
if (val > 63)
return E_INVALIDARG;
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
m_cfg.max_quantizer = val;
m_bDirty = true;
return S_OK;
}
HRESULT Filter::GetMaxQuantizer(int* p)
{
if (p == 0)
return E_POINTER;
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
*p = m_cfg.max_quantizer;
return S_OK;
}
HRESULT Filter::SetUndershootPct(int val)
{
if (val > 100)
return E_INVALIDARG;
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
m_cfg.undershoot_pct = val;
m_bDirty = true;
return S_OK;
}
HRESULT Filter::GetUndershootPct(int* pval)
{
if (pval == 0)
return E_POINTER;
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
*pval = m_cfg.undershoot_pct;
return S_OK;
}
HRESULT Filter::SetOvershootPct(int val)
{
if (val > 100)
return E_INVALIDARG;
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
m_cfg.overshoot_pct = val;
m_bDirty = true;
return S_OK;
}
HRESULT Filter::GetOvershootPct(int* pval)
{
if (pval == 0)
return E_POINTER;
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
*pval = m_cfg.overshoot_pct;
return S_OK;
}
HRESULT Filter::SetDecoderBufferSize(int val)
{
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
m_cfg.decoder_buffer_size = val;
m_bDirty = true;
return S_OK;
}
HRESULT Filter::GetDecoderBufferSize(int* pval)
{
if (pval == 0)
return E_POINTER;
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
*pval = m_cfg.decoder_buffer_size;
return S_OK;
}
HRESULT Filter::SetDecoderBufferInitialSize(int val)
{
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
m_cfg.decoder_buffer_initial_size = val;
m_bDirty = true;
return S_OK;
}
HRESULT Filter::GetDecoderBufferInitialSize(int* pval)
{
if (pval == 0)
return E_POINTER;
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
*pval = m_cfg.decoder_buffer_initial_size;
return S_OK;
}
HRESULT Filter::SetDecoderBufferOptimalSize(int val)
{
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
m_cfg.decoder_buffer_optimal_size = val;
m_bDirty = true;
return S_OK;
}
HRESULT Filter::GetDecoderBufferOptimalSize(int* pval)
{
if (pval == 0)
return E_POINTER;
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
*pval = m_cfg.decoder_buffer_optimal_size;
return S_OK;
}
HRESULT Filter::SetKeyframeMode(VP8KeyframeMode m)
{
switch (m)
{
case kKeyframeModeDefault:
case kKeyframeModeDisabled:
case kKeyframeModeAuto:
break;
default:
return E_INVALIDARG;
}
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
m_cfg.keyframe_mode = m;
m_bDirty = true;
return S_OK;
}
HRESULT Filter::GetKeyframeMode(VP8KeyframeMode* pm)
{
if (pm == 0)
return E_POINTER;
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
*pm = static_cast<VP8KeyframeMode>(m_cfg.keyframe_mode);
return S_OK;
}
HRESULT Filter::SetKeyframeMinInterval(int val)
{
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
m_cfg.keyframe_min_interval = val;
m_bDirty = true;
return S_OK;
}
HRESULT Filter::GetKeyframeMinInterval(int* pval)
{
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
*pval = m_cfg.keyframe_min_interval;
return S_OK;
}
HRESULT Filter::SetKeyframeMaxInterval(int val)
{
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
m_cfg.keyframe_max_interval = val;
m_bDirty = true;
return S_OK;
}
HRESULT Filter::GetKeyframeMaxInterval(int* pval)
{
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
*pval = m_cfg.keyframe_max_interval;
return S_OK;
}
HRESULT Filter::SetPassMode(VP8PassMode m)
{
switch (m)
{
//case kPassModeDefault: //TODO
case kPassModeOnePass:
case kPassModeFirstPass:
case kPassModeLastPass:
break;
default:
return E_INVALIDARG;
}
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
if (m_state != State_Stopped)
return VFW_E_NOT_STOPPED;
//TODO: for now, don't serialize pass mode
Config::int32_t& tgt = m_cfg.pass_mode;
if (m == tgt) //no need for any other checks
return S_OK;
OutpinVideo& outpin = m_outpin_video;
hr = outpin.OnSetPassMode(m);
if (FAILED(hr))
return hr;
tgt = m;
return S_OK;
}
HRESULT Filter::GetPassMode(VP8PassMode* p)
{
if (p == 0)
return E_POINTER;
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
if (m_cfg.pass_mode < 0)
*p = kPassModeOnePass;
else
*p = static_cast<VP8PassMode>(m_cfg.pass_mode);
return S_OK;
}
HRESULT Filter::SetTwoPassStatsBuf(const BYTE* buf, LONGLONG len)
{
if ((len < 0) || ((len > 0) && (buf == 0)))
return E_INVALIDARG;
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
if (m_state != State_Stopped)
return VFW_E_NOT_STOPPED;
m_cfg.two_pass_stats_buf = buf;
m_cfg.two_pass_stats_buflen = len;
return S_OK;
}
HRESULT Filter::GetTwoPassStatsBuf(const BYTE** pbuf, LONGLONG* plen)
{
if (pbuf)
*pbuf = 0;
if (plen)
*plen = 0;
if ((pbuf == 0) || (plen == 0))
return E_POINTER;
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
*pbuf = m_cfg.two_pass_stats_buf;
const LONGLONG src = m_cfg.two_pass_stats_buflen;
*plen = (src <= 0) ? 0 : src;
return S_OK;
}
HRESULT Filter::SetTwoPassVbrBiasPct(int val)
{
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
m_cfg.two_pass_vbr_bias_pct = val;
m_bDirty = true;
return S_OK;
}
HRESULT Filter::GetTwoPassVbrBiasPct(int* p)
{
if (p == 0)
return E_POINTER;
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
*p = m_cfg.two_pass_vbr_bias_pct;
return S_OK;
}
HRESULT Filter::SetTwoPassVbrMinsectionPct(int val)
{
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
m_cfg.two_pass_vbr_minsection_pct = val;
m_bDirty = true;
return S_OK;
}
HRESULT Filter::GetTwoPassVbrMinsectionPct(int* p)
{
if (p == 0)
return E_POINTER;
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
*p = m_cfg.two_pass_vbr_minsection_pct;
return S_OK;
}
HRESULT Filter::SetTwoPassVbrMaxsectionPct(int val)
{
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
m_cfg.two_pass_vbr_maxsection_pct = val;
m_bDirty = true;
return S_OK;
}
HRESULT Filter::GetTwoPassVbrMaxsectionPct(int* p)
{
if (p == 0)
return E_POINTER;
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
*p = m_cfg.two_pass_vbr_maxsection_pct;
return S_OK;
}
HRESULT Filter::SetForceKeyframe()
{
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
m_bForceKeyframe = true;
return S_OK;
}
HRESULT Filter::ClearForceKeyframe()
{
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
m_bForceKeyframe = false;
return S_OK;
}
HRESULT Filter::SetAutoAltRef(int val)
{
if (val > 1)
return E_INVALIDARG;
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
if (m_state != State_Stopped)
return VFW_E_NOT_STOPPED;
m_cfg.auto_alt_ref = val;
m_bDirty = true;
return S_OK;
}
HRESULT Filter::GetAutoAltRef(int* p)
{
if (p == 0)
return E_POINTER;
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
*p = m_cfg.auto_alt_ref;
return S_OK;
}
HRESULT Filter::SetARNRMaxFrames(int val)
{
if (val > 15)
return E_INVALIDARG;
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
if (m_state != State_Stopped)
return VFW_E_NOT_STOPPED;
m_cfg.arnr_max_frames = val;
m_bDirty = true;
return S_OK;
}
HRESULT Filter::GetARNRMaxFrames(int* p)
{
if (p == 0)
return E_POINTER;
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
*p = m_cfg.arnr_max_frames;
return S_OK;
}
HRESULT Filter::SetARNRStrength(int val)
{
if (val > 6)
return E_INVALIDARG;
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
if (m_state != State_Stopped)
return VFW_E_NOT_STOPPED;
m_cfg.arnr_strength = val;
m_bDirty = true;
return S_OK;
}
HRESULT Filter::GetARNRStrength(int* p)
{
if (p == 0)
return E_POINTER;
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
*p = m_cfg.arnr_strength;
return S_OK;
}
HRESULT Filter::SetARNRType(int val)
{
if (val > 3)
return E_INVALIDARG;
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
if (m_state != State_Stopped)
return VFW_E_NOT_STOPPED;
m_cfg.arnr_type = val;
m_bDirty = true;
return S_OK;
}
HRESULT Filter::GetARNRType(int* p)
{
if (p == 0)
return E_POINTER;
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
*p = m_cfg.arnr_type;
return S_OK;
}
HRESULT Filter::SetFixedKeyframeInterval(REFERENCE_TIME interval)
{
if (interval < 0)
return E_INVALIDARG;
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
m_keyframe_interval = interval;
m_bDirty = true;
if (m_cfg.keyframe_mode != kKeyframeModeDisabled)
return S_FALSE;
return S_OK;
}
HRESULT Filter::GetFixedKeyframeInterval(REFERENCE_TIME* pInterval)
{
if (pInterval == 0)
return E_POINTER;
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
*pInterval = m_keyframe_interval;
return S_OK;
}
HRESULT Filter::SetCPUUsed(int cpuUsed)
{
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
m_cfg.cpu_used = cpuUsed;
m_bDirty = true;
return S_OK;
}
HRESULT Filter::GetCPUUsed(int* pCPUUsed)
{
if (pCPUUsed == 0)
return E_POINTER;
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
*pCPUUsed = m_cfg.cpu_used;
return S_OK;
}
HRESULT Filter::SetStaticThreshold(int threshold)
{
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
m_cfg.static_threshold = threshold;
m_bDirty = true;
return S_OK;
}
HRESULT Filter::GetStaticThreshold(int* pThreshold)
{
if (pThreshold == 0)
return E_POINTER;
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
*pThreshold = m_cfg.static_threshold;
return S_OK;
}
HRESULT Filter::SetDecimate(int decimate)
{
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
if (m_state != State_Stopped)
return VFW_E_NOT_STOPPED;
if (!m_inpin.m_pPinConnection)
return VFW_E_NOT_CONNECTED;
if (m_outpin_video.m_pPinConnection || m_outpin_preview.m_pPinConnection)
return VFW_E_ALREADY_CONNECTED;
if (decimate < 2)
decimate = 1;
const REFERENCE_TIME avg_time_per_frame =
m_inpin.GetAvgTimePerFrame() * decimate;
hr = m_outpin_video.SetAvgTimePerFrame(avg_time_per_frame);
if (FAILED(hr))
return hr;
hr = m_outpin_preview.SetAvgTimePerFrame(avg_time_per_frame);
if (FAILED(hr))
return hr;
m_decimate = decimate;
m_bDirty = true;
return hr;
}
HRESULT Filter::GetDecimate(int* pDecimate)
{
if (pDecimate == 0)
return E_POINTER;
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
*pDecimate = m_decimate;
return S_OK;
}
HRESULT Filter::IsDirty()
{
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
return m_bDirty ? S_OK : S_FALSE;
}
HRESULT Filter::Load(IStream* pStream)
{
if (pStream == 0)
return E_INVALIDARG;
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
if (m_state != State_Stopped)
return VFW_E_NOT_STOPPED;
unsigned __int32 size;
ULONG cbRead;
hr = pStream->Read(&size, 4, &cbRead);
if (FAILED(hr))
return hr;
if (cbRead != 4)
return E_FAIL;
if (size != sizeof(Config))
return E_FAIL;
Config cfg;
hr = pStream->Read(&cfg, size, &cbRead);
if (FAILED(hr))
return hr;
if (cbRead != size)
return E_FAIL;
m_cfg = cfg;
m_cfg.pass_mode = -1;
m_cfg.two_pass_stats_buf = 0;
m_cfg.two_pass_stats_buflen = -1;
return S_OK;
}
HRESULT Filter::Save(IStream* pStm, BOOL fClearDirty)
{
if (pStm == 0)
return E_INVALIDARG;
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
typedef unsigned __int32 uint32_t;
const uint32_t size = static_cast<uint32_t>(sizeof(Config));
ULONG cbWritten;
hr = pStm->Write(&size, 4, &cbWritten);
if (FAILED(hr))
return hr;
if (cbWritten != 4)
return E_FAIL; //?
hr = pStm->Write(&m_cfg, size, &cbWritten);
if (FAILED(hr))
return hr;
if (cbWritten != size)
return E_FAIL;
if (fClearDirty)
m_bDirty = false;
return S_OK;
}
HRESULT Filter::GetSizeMax(ULARGE_INTEGER* pcb)
{
if (pcb == 0)
return E_POINTER;
ULARGE_INTEGER& cb = *pcb;
cb.QuadPart = 4 + sizeof(Config);
return S_OK;
}
HRESULT Filter::OnStart()
{
HRESULT hr = m_inpin.Start();
if (FAILED(hr))
return hr;
hr = m_outpin_video.Start();
if (FAILED(hr))
{
m_inpin.Stop();
return hr;
}
hr = m_outpin_preview.Start();
if (FAILED(hr))
{
m_outpin_video.Stop();
m_inpin.Stop();
return hr;
}
return S_OK;
}
void Filter::OnStop()
{
m_outpin_preview.Stop();
m_outpin_video.Stop();
m_inpin.Stop();
}
VP8PassMode Filter::GetPassMode() const
{
const Config::int32_t m = m_cfg.pass_mode;
if (m < 0)
return kPassModeOnePass; //default
return static_cast<VP8PassMode>(m);
}
HRESULT Filter::GetPages(CAUUID* p)
{
if (p == 0)
return E_POINTER;
CAUUID& s = *p;
void* const pv = CoTaskMemAlloc(sizeof(GUID));
if (pv == 0)
{
s.cElems = 0;
s.pElems = 0;
return E_OUTOFMEMORY;
}
s.cElems = 1;
s.pElems = static_cast<GUID*>(pv);
s.pElems[0] = CLSID_PropPage;
return S_OK;
}
} //end namespace VP8EncoderLib