blob: 8ca6e6af895e8741942ad7fa009e3fa5cc02d6a2 [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 <comdef.h>
#include "webmccfilter.h"
#include "webmccoutpin.h"
#include "cmediasample.h"
#include "mediatypeutil.h"
#include "webmtypes.h"
#include <vfwmsgs.h>
#include <uuids.h>
#include <cassert>
#include <process.h>
//#include <amvideo.h>
#ifdef _DEBUG
#include "odbgstream.h"
#include <iomanip>
#include "iidstr.h"
using std::endl;
using std::dec;
using std::hex;
using std::fixed;
using std::setprecision;
#endif
using std::wstring;
namespace WebmColorConversion
{
Outpin::Outpin(Filter* pFilter) :
Pin(pFilter, PINDIR_OUTPUT, L"output"),
m_hThread(0),
m_rgb_to_yuv(0)
{
SetDefaultMediaTypes();
}
Outpin::~Outpin()
{
assert(m_hThread == 0);
assert(!bool(m_pAllocator));
assert(!bool(m_pInputPin));
}
HRESULT Outpin::Start() //transition from stopped
{
if (m_pPinConnection == 0)
return S_FALSE; //nothing we need to do
assert(bool(m_pAllocator));
assert(bool(m_pInputPin));
const HRESULT hr = m_pAllocator->Commit();
hr;
assert(SUCCEEDED(hr)); //TODO
StartThread();
return S_OK;
}
void Outpin::Stop() //transition to stopped
{
if (m_pPinConnection == 0)
return; //nothing was done
assert(bool(m_pAllocator));
assert(bool(m_pInputPin));
const HRESULT hr = m_pAllocator->Decommit();
hr;
assert(SUCCEEDED(hr));
StopThread();
}
HRESULT Outpin::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<IPin*>(this);
else if (iid == __uuidof(IPin))
pUnk = static_cast<IPin*>(this);
else if (iid == __uuidof(IMediaSeeking))
pUnk = static_cast<IMediaSeeking*>(this);
else
{
pUnk = 0;
return E_NOINTERFACE;
}
pUnk->AddRef();
return S_OK;
}
ULONG Outpin::AddRef()
{
return m_pFilter->AddRef();
}
ULONG Outpin::Release()
{
return m_pFilter->Release();
}
HRESULT Outpin::Connect(
IPin* pin,
const AM_MEDIA_TYPE* pmt)
{
if (pin == 0)
return E_POINTER;
GraphUtil::IMemInputPinPtr pInputPin;
HRESULT hr = pin->QueryInterface(&pInputPin);
if (hr != S_OK)
return hr;
Filter::Lock lock;
hr = lock.Seize(m_pFilter);
if (FAILED(hr))
return hr;
if (m_pFilter->m_state != State_Stopped)
return VFW_E_NOT_STOPPED;
if (bool(m_pPinConnection))
return VFW_E_ALREADY_CONNECTED;
if (!bool(m_pFilter->m_inpin.m_pPinConnection))
return VFW_E_NO_TYPES; //VFW_E_NOT_CONNECTED?
m_connection_mtv.Clear();
if (pmt)
{
hr = QueryAccept(pmt);
if (hr != S_OK)
return VFW_E_TYPE_NOT_ACCEPTED;
LONG idx = -1;
const ULONG n = m_preferred_mtv.Size();
for (ULONG i = 0; i < n; ++i)
{
const AM_MEDIA_TYPE& mt = m_preferred_mtv[i];
if (mt.subtype == mt.subtype)
{
idx = i;
break;
}
}
if (idx < 0) //weird
return VFW_E_TYPE_NOT_ACCEPTED;
const AM_MEDIA_TYPE& mt = m_preferred_mtv[idx];
hr = pin->ReceiveConnection(this, &mt);
if (FAILED(hr))
return hr;
m_connection_mtv.Add(mt);
}
else
{
ULONG i = 0;
const ULONG j = m_preferred_mtv.Size();
while (i < j)
{
const AM_MEDIA_TYPE& mt = m_preferred_mtv[i];
hr = pin->ReceiveConnection(this, &mt);
if (SUCCEEDED(hr))
break;
++i;
}
if (i >= j)
return VFW_E_NO_ACCEPTABLE_TYPES;
const AM_MEDIA_TYPE& mt = m_preferred_mtv[i];
m_connection_mtv.Add(mt);
}
GraphUtil::IMemAllocatorPtr pAllocator;
hr = pInputPin->GetAllocator(&pAllocator);
if (FAILED(hr))
{
hr = CMediaSample::CreateAllocator(&pAllocator);
if (FAILED(hr))
return VFW_E_NO_ALLOCATOR;
}
assert(bool(pAllocator));
ALLOCATOR_PROPERTIES props, actual;
props.cBuffers = -1; //number of buffers
props.cbBuffer = -1; //size of each buffer, excluding prefix
props.cbAlign = -1; //applies to prefix, too
props.cbPrefix = -1; //imediasample::getbuffer does NOT include prefix
hr = pInputPin->GetAllocatorRequirements(&props);
const long cBuffers = 2;
if (props.cBuffers < cBuffers)
props.cBuffers = cBuffers;
const VIDEOINFOHEADER* const pvih = this->GetVideoInfo();
assert(pvih);
const BITMAPINFOHEADER& bmih = pvih->bmiHeader;
const LONG w = bmih.biWidth;
const LONG h = labs(bmih.biHeight);
const LONG y = w * h;
const LONG uv = (w/2) * (h/2);
const LONG cbBuffer = y + 2 * uv;
if (props.cbBuffer < cbBuffer)
props.cbBuffer = cbBuffer;
if (props.cbAlign <= 0)
props.cbAlign = 1;
if (props.cbPrefix < 0)
props.cbPrefix = 0;
hr = pAllocator->SetProperties(&props, &actual);
if (FAILED(hr))
return hr;
hr = pInputPin->NotifyAllocator(pAllocator, 0); //allow writes
if (FAILED(hr) && (hr != E_NOTIMPL))
return hr;
m_pPinConnection = pin;
m_pAllocator = pAllocator;
m_pInputPin = pInputPin;
return S_OK;
}
HRESULT Outpin::OnDisconnect()
{
m_pInputPin = 0;
m_pAllocator = 0;
return S_OK;
}
HRESULT Outpin::ReceiveConnection(
IPin*,
const AM_MEDIA_TYPE*)
{
return E_UNEXPECTED; //for input pins only
}
HRESULT Outpin::QueryAccept(const AM_MEDIA_TYPE* pmt)
{
if (pmt == 0)
return E_INVALIDARG;
Filter::Lock lock;
const HRESULT hr = lock.Seize(m_pFilter);
if (FAILED(hr))
return hr;
const Inpin& inpin = m_pFilter->m_inpin;
if (!bool(inpin.m_pPinConnection))
return VFW_E_NO_TYPES;
const AM_MEDIA_TYPE& mtOut = *pmt;
if (mtOut.majortype != MEDIATYPE_Video)
return S_FALSE;
if (mtOut.subtype != MEDIASUBTYPE_YV12)
return S_FALSE;
if (mtOut.formattype == GUID_NULL)
return S_OK;
if (mtOut.formattype != FORMAT_VideoInfo)
return S_FALSE;
if (mtOut.pbFormat == 0)
return S_FALSE;
if (mtOut.cbFormat < sizeof(VIDEOINFOHEADER))
return S_FALSE;
const VIDEOINFOHEADER& vihOut = (VIDEOINFOHEADER&)(*mtOut.pbFormat);
const BITMAPINFOHEADER& bmihOut = vihOut.bmiHeader;
bmihOut;
//TODO: vet vih and bmih
return S_OK;
}
HRESULT Outpin::QueryInternalConnections(IPin** pa, ULONG* pn)
{
if (pn == 0)
return E_POINTER;
Filter::Lock lock;
HRESULT hr = lock.Seize(m_pFilter);
if (FAILED(hr))
return hr;
ULONG& n = *pn;
if (n == 0)
{
if (pa == 0) //query for required number
{
n = 1;
return S_OK;
}
return S_FALSE; //means "insufficient number of array elements"
}
if (n < 1)
{
n = 0;
return S_FALSE; //means "insufficient number of array elements"
}
if (pa == 0)
{
n = 0;
return E_POINTER;
}
IPin*& pPin = pa[0];
pPin = &m_pFilter->m_inpin;
pPin->AddRef();
n = 1;
return S_OK;
}
HRESULT Outpin::EndOfStream()
{
return E_UNEXPECTED; //for inpins only
}
HRESULT Outpin::NewSegment(
REFERENCE_TIME,
REFERENCE_TIME,
double)
{
return E_UNEXPECTED;
}
HRESULT Outpin::BeginFlush()
{
return E_UNEXPECTED;
}
HRESULT Outpin::EndFlush()
{
return E_UNEXPECTED;
}
HRESULT Outpin::GetCapabilities(DWORD* pdw)
{
const Inpin& inpin = m_pFilter->m_inpin;
const GraphUtil::IMediaSeekingPtr pSeek(inpin.m_pPinConnection);
if (bool(pSeek))
return pSeek->GetCapabilities(pdw);
if (pdw == 0)
return E_POINTER;
DWORD& dw = *pdw;
dw = 0;
return S_OK; //?
}
HRESULT Outpin::CheckCapabilities(DWORD* pdw)
{
const Inpin& inpin = m_pFilter->m_inpin;
const GraphUtil::IMediaSeekingPtr pSeek(inpin.m_pPinConnection);
if (bool(pSeek))
return pSeek->CheckCapabilities(pdw);
if (pdw == 0)
return E_POINTER;
DWORD& dw = *pdw;
const DWORD dwRequested = dw;
if (dwRequested == 0)
return E_INVALIDARG;
return E_FAIL;
}
HRESULT Outpin::IsFormatSupported(const GUID* p)
{
const Inpin& inpin = m_pFilter->m_inpin;
const GraphUtil::IMediaSeekingPtr pSeek(inpin.m_pPinConnection);
if (bool(pSeek))
return pSeek->IsFormatSupported(p);
if (p == 0)
return E_POINTER;
const GUID& g = *p;
if (g == TIME_FORMAT_MEDIA_TIME)
return S_OK;
return S_FALSE;
}
HRESULT Outpin::QueryPreferredFormat(GUID* p)
{
const Inpin& inpin = m_pFilter->m_inpin;
const GraphUtil::IMediaSeekingPtr pSeek(inpin.m_pPinConnection);
if (bool(pSeek))
return pSeek->QueryPreferredFormat(p);
if (p == 0)
return E_POINTER;
*p = TIME_FORMAT_MEDIA_TIME;
return S_OK;
}
HRESULT Outpin::GetTimeFormat(GUID* p)
{
const Inpin& inpin = m_pFilter->m_inpin;
const GraphUtil::IMediaSeekingPtr pSeek(inpin.m_pPinConnection);
if (bool(pSeek))
return pSeek->GetTimeFormat(p);
if (p == 0)
return E_POINTER;
*p = TIME_FORMAT_MEDIA_TIME;
return S_OK;
}
HRESULT Outpin::IsUsingTimeFormat(const GUID* p)
{
const Inpin& inpin = m_pFilter->m_inpin;
const GraphUtil::IMediaSeekingPtr pSeek(inpin.m_pPinConnection);
if (bool(pSeek))
return pSeek->IsUsingTimeFormat(p);
if (p == 0)
return E_INVALIDARG;
return (*p == TIME_FORMAT_MEDIA_TIME) ? S_OK : S_FALSE;
}
HRESULT Outpin::SetTimeFormat(const GUID* p)
{
const Inpin& inpin = m_pFilter->m_inpin;
const GraphUtil::IMediaSeekingPtr pSeek(inpin.m_pPinConnection);
if (bool(pSeek))
return pSeek->SetTimeFormat(p);
if (p == 0)
return E_INVALIDARG;
if (*p == TIME_FORMAT_MEDIA_TIME)
return S_OK;
return E_INVALIDARG;
}
HRESULT Outpin::GetDuration(LONGLONG* p)
{
const Inpin& inpin = m_pFilter->m_inpin;
const GraphUtil::IMediaSeekingPtr pSeek(inpin.m_pPinConnection);
if (bool(pSeek))
return pSeek->GetDuration(p);
if (p == 0)
return E_POINTER;
return E_FAIL;
}
HRESULT Outpin::GetStopPosition(LONGLONG* p)
{
const Inpin& inpin = m_pFilter->m_inpin;
const GraphUtil::IMediaSeekingPtr pSeek(inpin.m_pPinConnection);
if (bool(pSeek))
return pSeek->GetStopPosition(p);
if (p == 0)
return E_POINTER;
return E_FAIL;
}
HRESULT Outpin::GetCurrentPosition(LONGLONG* p)
{
const Inpin& inpin = m_pFilter->m_inpin;
const GraphUtil::IMediaSeekingPtr pSeek(inpin.m_pPinConnection);
if (bool(pSeek))
return pSeek->GetCurrentPosition(p);
if (p == 0)
return E_POINTER;
return E_FAIL;
}
HRESULT Outpin::ConvertTimeFormat(
LONGLONG* ptgt,
const GUID* ptgtfmt,
LONGLONG src,
const GUID* psrcfmt)
{
const Inpin& inpin = m_pFilter->m_inpin;
const GraphUtil::IMediaSeekingPtr pSeek(inpin.m_pPinConnection);
if (bool(pSeek))
return pSeek->ConvertTimeFormat(ptgt, ptgtfmt, src, psrcfmt);
if (ptgt == 0)
return E_POINTER;
LONGLONG& tgt = *ptgt;
const GUID& tgtfmt = ptgtfmt ? *ptgtfmt : TIME_FORMAT_MEDIA_TIME;
const GUID& srcfmt = psrcfmt ? *psrcfmt : TIME_FORMAT_MEDIA_TIME;
if (tgtfmt != TIME_FORMAT_MEDIA_TIME)
return E_INVALIDARG;
if (srcfmt != TIME_FORMAT_MEDIA_TIME)
return E_INVALIDARG;
tgt = src;
return S_OK;
}
HRESULT Outpin::SetPositions(
LONGLONG* pCurr,
DWORD dwCurr,
LONGLONG* pStop,
DWORD dwStop)
{
const Inpin& inpin = m_pFilter->m_inpin;
const GraphUtil::IMediaSeekingPtr pSeek(inpin.m_pPinConnection);
if (bool(pSeek))
return pSeek->SetPositions(pCurr, dwCurr, pStop, dwStop);
return E_FAIL;
}
HRESULT Outpin::GetPositions(
LONGLONG* pCurrPos,
LONGLONG* pStopPos)
{
const Inpin& inpin = m_pFilter->m_inpin;
const GraphUtil::IMediaSeekingPtr pSeek(inpin.m_pPinConnection);
if (bool(pSeek))
return pSeek->GetPositions(pCurrPos, pStopPos);
return E_FAIL;
}
HRESULT Outpin::GetAvailable(
LONGLONG* pEarliest,
LONGLONG* pLatest)
{
const Inpin& inpin = m_pFilter->m_inpin;
const GraphUtil::IMediaSeekingPtr pSeek(inpin.m_pPinConnection);
if (bool(pSeek))
return pSeek->GetAvailable(pEarliest, pLatest);
return E_FAIL;
}
HRESULT Outpin::SetRate(double r)
{
const Inpin& inpin = m_pFilter->m_inpin;
const GraphUtil::IMediaSeekingPtr pSeek(inpin.m_pPinConnection);
if (bool(pSeek))
return pSeek->SetRate(r);
return E_FAIL;
}
HRESULT Outpin::GetRate(double* p)
{
const Inpin& inpin = m_pFilter->m_inpin;
const GraphUtil::IMediaSeekingPtr pSeek(inpin.m_pPinConnection);
if (bool(pSeek))
return pSeek->GetRate(p);
return E_FAIL;
}
HRESULT Outpin::GetPreroll(LONGLONG* p)
{
const Inpin& inpin = m_pFilter->m_inpin;
const GraphUtil::IMediaSeekingPtr pSeek(inpin.m_pPinConnection);
if (bool(pSeek))
return pSeek->GetPreroll(p);
return E_FAIL;
}
HRESULT Outpin::GetName(PIN_INFO& info) const
{
const wchar_t* const name_ = L"YUV";
#if _MSC_VER >= 1400
enum { namelen = sizeof(info.achName) / sizeof(WCHAR) };
const errno_t e = wcscpy_s(info.achName, namelen, name_);
e;
assert(e == 0);
#else
wcscpy(info.achName, name_);
#endif
return S_OK;
}
void Outpin::OnInpinConnect(const AM_MEDIA_TYPE& mtIn)
{
assert(mtIn.formattype == FORMAT_VideoInfo); //TODO: liberalize
assert(mtIn.pbFormat);
assert(mtIn.cbFormat >= sizeof(VIDEOINFOHEADER));
const VIDEOINFOHEADER& vihIn = (VIDEOINFOHEADER&)(*mtIn.pbFormat);
const BITMAPINFOHEADER& bmihIn = vihIn.bmiHeader;
m_preferred_mtv.Clear();
AM_MEDIA_TYPE mt;
VIDEOINFOHEADER vih;
BITMAPINFOHEADER& bmih = vih.bmiHeader;
mt.majortype = MEDIATYPE_Video;
mt.subtype = MEDIASUBTYPE_YV12;
mt.bFixedSizeSamples = TRUE;
mt.bTemporalCompression = FALSE;
mt.lSampleSize = 0; //TODO
mt.formattype = FORMAT_VideoInfo;
mt.pUnk = 0;
mt.cbFormat = sizeof vih;
mt.pbFormat = (BYTE*)&vih;
SetRectEmpty(&vih.rcSource);
SetRectEmpty(&vih.rcTarget);
vih.dwBitRate = 0;
vih.dwBitErrorRate = 0;
vih.AvgTimePerFrame = vihIn.AvgTimePerFrame;
bmih.biSize = sizeof(BITMAPINFOHEADER); //40
bmih.biWidth = bmihIn.biWidth;
bmih.biHeight = bmihIn.biHeight;
bmih.biPlanes = 1;
bmih.biBitCount = 12;
memcpy(&bmih.biCompression, "YV12", 4);
bmih.biSizeImage = 0; //TODO
bmih.biXPelsPerMeter = 0;
bmih.biYPelsPerMeter = 0;
bmih.biClrUsed = 0;
bmih.biClrImportant = 0;
m_preferred_mtv.Add(mt);
img_fmt_t src;
if (mtIn.subtype == MEDIASUBTYPE_RGB24)
src = IMG_FMT_RGB24;
else
{
assert(mtIn.subtype == MEDIASUBTYPE_RGB32);
src = IMG_FMT_RGB32;
}
const img_fmt_t dst = IMG_FMT_YV12; //TODO: liberalize
m_rgb_to_yuv = on2_get_rgb_to_yuv(dst, src);
assert(m_rgb_to_yuv);
}
HRESULT Outpin::OnInpinDisconnect()
{
if (bool(m_pPinConnection))
{
IFilterGraph* const pGraph = m_pFilter->m_info.pGraph;
assert(pGraph);
HRESULT hr = pGraph->Disconnect(m_pPinConnection);
assert(SUCCEEDED(hr));
hr = pGraph->Disconnect(this);
assert(SUCCEEDED(hr));
assert(!bool(m_pPinConnection));
}
SetDefaultMediaTypes();
return S_OK;
}
void Outpin::SetDefaultMediaTypes()
{
m_preferred_mtv.Clear();
AM_MEDIA_TYPE mt;
mt.majortype = MEDIATYPE_Video;
mt.subtype = MEDIASUBTYPE_YV12;
mt.bFixedSizeSamples = TRUE;
mt.bTemporalCompression = FALSE;
mt.lSampleSize = 0;
mt.formattype = GUID_NULL;
mt.pUnk = 0;
mt.cbFormat = 0;
mt.pbFormat = 0;
m_preferred_mtv.Add(mt);
}
void Outpin::StartThread()
{
assert(m_hThread == 0);
const uintptr_t h = _beginthreadex(
0, //security
0, //stack size
&Outpin::ThreadProc,
this,
0, //run immediately
0); //thread id
m_hThread = reinterpret_cast<HANDLE>(h);
assert(m_hThread);
#ifdef _DEBUG
wodbgstream os;
os << "webmcc::Outpin[" << m_id << "]::StartThread: hThread=0x"
<< hex << h << dec
<< endl;
#endif
}
void Outpin::StopThread()
{
if (m_hThread == 0)
return;
#ifdef _DEBUG
wodbgstream os;
os << "webmcc::Outpin[" << m_id << "]::StopThread: hThread=0x"
<< hex << uintptr_t(m_hThread) << dec
<< endl;
#endif
const DWORD dw = WaitForSingleObject(m_hThread, 5000);
assert(dw == WAIT_OBJECT_0);
const BOOL b = CloseHandle(m_hThread);
assert(b);
m_hThread = 0;
}
unsigned Outpin::ThreadProc(void* pv)
{
Outpin* const pPin = static_cast<Outpin*>(pv);
assert(pPin);
#ifdef _DEBUG
wodbgstream os;
os << "webmcc::Outpin["
<< pPin->m_id
<< "]::ThreadProc(begin): hThread=0x"
<< hex << uintptr_t(pPin->m_hThread) << dec
<< endl;
#endif
pPin->Main();
#ifdef _DEBUG
os << "webmcc::Outpin["
<< pPin->m_id << "]::ThreadProc(end): hThread=0x"
<< hex << uintptr_t(pPin->m_hThread) << dec
<< endl;
#endif
return 0;
}
unsigned Outpin::Main()
{
assert(bool(m_pPinConnection));
assert(bool(m_pInputPin));
Inpin& inpin = m_pFilter->m_inpin;
const HANDLE hSamples = inpin.m_hSamples;
for (;;)
{
Filter::Lock lock;
HRESULT hrLock = lock.Seize(m_pFilter);
assert(SUCCEEDED(hrLock));
if (FAILED(hrLock))
return 0; //TODO: signal error
GraphUtil::IMediaSamplePtr pInSample;
const int status = inpin.GetSample(&pInSample);
hrLock = lock.Release();
assert(SUCCEEDED(hrLock));
if (FAILED(hrLock))
return 0;
if (status < -1) //terminate thread
return 0;
if (status == 0) //EOS (no payload)
{
#ifdef _DEBUG
odbgstream os;
os << "webmcc::outpin::EOS: calling pin->EOS" << endl;
#endif
const HRESULT hrEOS = m_pPinConnection->EndOfStream();
hrEOS;
#ifdef _DEBUG
os << "webmcc::outpin::EOS: called pin->EOS; hr=0x"
<< hex << hrEOS << dec
<< endl;
#endif
}
else if (status > 0) //have payload to send downstream
{
GraphUtil::IMediaSamplePtr pOutSample;
HRESULT hr = m_pAllocator->GetBuffer(&pOutSample, 0, 0, 0);
if (hr == S_OK)
{
PopulateSample(pInSample, pOutSample);
hr = m_pInputPin->Receive(pOutSample);
if (hr == S_OK)
continue;
}
pInSample = 0;
inpin.OnCompletion();
}
const DWORD dw = WaitForSingleObject(hSamples, INFINITE);
if (dw == WAIT_FAILED)
return 0; //signal error to FGM
assert(dw == WAIT_OBJECT_0);
}
}
void Outpin::PopulateSample(
IMediaSample* pIn,
IMediaSample* pOut)
{
assert(pIn);
assert(pOut);
//input
const Inpin& inpin = m_pFilter->m_inpin;
const VIDEOINFOHEADER* const pvih_in = inpin.GetVideoInfo();
assert(pvih_in);
const RECT& rc_in = pvih_in->rcSource;
const LONG rc_in_w = rc_in.right - rc_in.left;
assert(rc_in_w >= 0);
assert(rc_in_w % 2 == 0); //TODO
//BITMAPINFOHEADER Structure
//http://msdn.microsoft.com/en-us/library/dd183376%28v=vs.85%29.aspx
const BITMAPINFOHEADER& bmih_in = pvih_in->bmiHeader;
const LONG w_in = bmih_in.biWidth; //stride
assert(w_in > 0);
assert(w_in % 2 == 0); //TODO
//assert(w_in == rc_in_w);
const LONG hh_in = bmih_in.biHeight;
assert(hh_in);
const LONG h_in = labs(hh_in);
assert(h_in > 0);
assert(h_in % 2 == 0); //TODO
BYTE* buf_in;
HRESULT hr = pIn->GetPointer(&buf_in);
assert(SUCCEEDED(hr));
assert(buf_in);
const AM_MEDIA_TYPE* const pmt_in = inpin.GetMediaType();
assert(pmt_in);
const GUID& subtype_in = pmt_in->subtype;
LONG bytes_per_pixel;
if (subtype_in == MEDIASUBTYPE_RGB24)
bytes_per_pixel = 3;
else
{
assert(subtype_in == MEDIASUBTYPE_RGB32);
bytes_per_pixel = 4;
}
const LONG stride_in = w_in * bytes_per_pixel;
const ULONG size_in = stride_in * h_in; //calculated
const long actual_size_in = pIn->GetActualDataLength();
assert(actual_size_in >= 0);
assert(ULONG(actual_size_in) == size_in);
BYTE* const rgb = buf_in + stride_in * (h_in - 1);
//output
const VIDEOINFOHEADER* const pvih_out = this->GetVideoInfo();
assert(pvih_out);
const RECT& rc_out = pvih_out->rcSource;
const LONG rc_out_w = rc_out.right - rc_out.left;
assert(rc_out_w >= 0);
assert(rc_out_w % 2 == 0); //TODO
const BITMAPINFOHEADER& bmih_out = pvih_out->bmiHeader;
const LONG w_out = bmih_out.biWidth; //stride
assert(w_out > 0);
assert(w_out % 2 == 0); //TODO
const LONG hh_out = bmih_out.biHeight;
assert(hh_out);
const LONG h_out = labs(hh_out);
assert(h_out % 2 == 0); //TODO
BYTE* buf_out;
hr = pOut->GetPointer(&buf_out);
assert(SUCCEEDED(hr));
assert(buf_out);
const AM_MEDIA_TYPE* const pmt_out = this->GetMediaType();
assert(pmt_out);
assert(pmt_out->subtype == MEDIASUBTYPE_YV12);
BYTE* const y = buf_out;
const LONG y_stride = w_out;
const ULONG y_height = h_out;
const ULONG y_size = y_stride * y_height;
const ULONG uv_stride = y_stride / 2;
const ULONG uv_height = y_height / 2;
const ULONG uv_size = uv_stride * uv_height;
const ULONG size_out = y_size + 2 * uv_size; //calculated
const long actual_size_out = pOut->GetSize();
assert(actual_size_out >= 0);
assert(ULONG(actual_size_out) >= size_out);
BYTE* const v = y + y_size;
BYTE* const u = v + uv_size;
assert(m_rgb_to_yuv);
(*m_rgb_to_yuv)(rgb, w_in, h_in, y, u, v, -1 * stride_in, y_stride);
hr = pOut->SetActualDataLength(size_out);
assert(SUCCEEDED(hr));
REFERENCE_TIME st, sp;
hr = pIn->GetTime(&st, &sp);
if (hr == S_OK)
{
hr = pOut->SetTime(&st, &sp);
assert(SUCCEEDED(hr));
}
else if (SUCCEEDED(hr))
{
hr = pOut->SetTime(&st, 0);
assert(SUCCEEDED(hr));
}
else
{
hr = pOut->SetTime(0, 0);
assert(SUCCEEDED(hr));
}
hr = pOut->SetSyncPoint(TRUE);
assert(SUCCEEDED(hr));
hr = pOut->SetPreroll(FALSE); //TODO
assert(SUCCEEDED(hr));
hr = pOut->SetMediaType(0);
assert(SUCCEEDED(hr));
const HRESULT hrDiscontinuity = pIn->IsDiscontinuity();
assert(SUCCEEDED(hrDiscontinuity));
const bool bDiscontinuity = (hrDiscontinuity == S_OK);
hr = pOut->SetDiscontinuity(bDiscontinuity ? TRUE : FALSE);
assert(SUCCEEDED(hr));
hr = pOut->SetMediaTime(0, 0);
assert(SUCCEEDED(hr));
}
} //end namespace WebmColorConversion