blob: 8ca07c1dcdfcf36a2cd2716c9fa8f4bdc1564b0f [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 <strmif.h>
#include <comdef.h>
#include <uuids.h>
#include "vp8encoderfilter.hpp"
#include "vp8encoderoutpin.hpp"
#include <vfwmsgs.h>
#include <amvideo.h>
#include <dvdmedia.h>
#include <cassert>
#ifdef _DEBUG
#include "odbgstream.hpp"
#include "iidstr.hpp"
using std::endl;
using std::dec;
using std::hex;
#endif
namespace VP8EncoderLib
{
Outpin::Outpin(
Filter* pFilter,
const wchar_t* id) :
Pin(pFilter, PINDIR_OUTPUT, id)
{
}
Outpin::~Outpin()
{
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
if (bool(m_pAllocator))
{
assert(bool(m_pInputPin));
const HRESULT hr = m_pAllocator->Commit();
hr;
assert(SUCCEEDED(hr)); //TODO
}
return S_OK;
}
void Outpin::Stop() //transition to stopped
{
if (m_pPinConnection == 0)
return; //nothing was done
if (bool(m_pAllocator))
{
assert(bool(m_pInputPin));
HRESULT hr = m_pAllocator->Decommit();
assert(SUCCEEDED(hr));
}
}
HRESULT Outpin::Connect(
IPin* pin,
const AM_MEDIA_TYPE* pmt)
{
if (pin == 0)
return E_POINTER;
Filter::Lock lock;
HRESULT 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;
hr = pin->ReceiveConnection(this, pmt);
if (FAILED(hr))
return hr;
const AM_MEDIA_TYPE& mt = *pmt;
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);
}
hr = PostConnect(pin);
if (FAILED(hr))
return hr;
m_pPinConnection = pin;
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::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;
}
void Outpin::OnInpinConnect()
{
const Inpin& inpin = m_pFilter->m_inpin;
const BITMAPINFOHEADER& bmihIn = inpin.GetBMIH();
const LONG ww = bmihIn.biWidth;
assert(ww > 0);
const LONG hh = labs(bmihIn.biHeight);
assert(hh > 0);
//TODO: does this really need to be a conditional expr?
const LONG w = (ww % 16) ? 16 * ((ww + 15) / 16) : ww;
const LONG h = (hh % 16) ? 16 * ((hh + 15) / 16) : hh;
const LONG cbBuffer = w*h + 2*(w/2)*(h/2);
m_preferred_mtv.Clear();
AM_MEDIA_TYPE mt;
mt.majortype = MEDIATYPE_Video;
GetSubtype(mt.subtype); //dispatch to subclass
mt.bFixedSizeSamples = FALSE;
mt.bTemporalCompression = TRUE;
mt.lSampleSize = 0;
mt.pUnk = 0;
// Decimation alters the output frame rate. Use input frame rate only if
// decimation is disabled.
const REFERENCE_TIME avg_time_per_frame =
(m_pFilter->m_decimate > 1) ?
inpin.GetAvgTimePerFrame() * m_pFilter->m_decimate :
inpin.GetAvgTimePerFrame();
{
VIDEOINFOHEADER vih;
BITMAPINFOHEADER& bmih = vih.bmiHeader;
mt.formattype = FORMAT_VideoInfo;
mt.cbFormat = sizeof vih;
mt.pbFormat = (BYTE*)&vih;
SetRectEmpty(&vih.rcSource); //TODO
SetRectEmpty(&vih.rcTarget); //TODO
vih.dwBitRate = 0;
vih.dwBitErrorRate = 0;
vih.AvgTimePerFrame = avg_time_per_frame;
bmih.biSize = sizeof bmih;
bmih.biWidth = ww;
bmih.biHeight = hh;
bmih.biPlanes = 1; //because Microsoft says so
bmih.biBitCount = 12; //?
bmih.biCompression = mt.subtype.Data1;
bmih.biSizeImage = cbBuffer;
bmih.biXPelsPerMeter = 0;
bmih.biYPelsPerMeter = 0;
bmih.biClrUsed = 0;
bmih.biClrImportant = 0;
m_preferred_mtv.Add(mt);
}
{
VIDEOINFOHEADER2 vih;
BITMAPINFOHEADER& bmih = vih.bmiHeader;
mt.formattype = FORMAT_VideoInfo2;
mt.cbFormat = sizeof vih;
mt.pbFormat = (BYTE*)&vih;
SetRectEmpty(&vih.rcSource); //TODO
SetRectEmpty(&vih.rcTarget); //TODO
vih.dwBitRate = 0;
vih.dwBitErrorRate = 0;
vih.AvgTimePerFrame = avg_time_per_frame;
vih.dwInterlaceFlags = 0;
vih.dwCopyProtectFlags = 0;
vih.dwPictAspectRatioX = w; //TODO
vih.dwPictAspectRatioY = h; //TODO
vih.dwReserved1 = 0; //TODO
vih.dwReserved2 = 0; //TODO
bmih.biSize = sizeof bmih;
bmih.biWidth = ww;
bmih.biHeight = hh;
bmih.biPlanes = 1; //because Microsoft says so
bmih.biBitCount = 12; //?
bmih.biCompression = mt.subtype.Data1;
bmih.biSizeImage = cbBuffer;
bmih.biXPelsPerMeter = 0;
bmih.biYPelsPerMeter = 0;
bmih.biClrUsed = 0;
bmih.biClrImportant = 0;
m_preferred_mtv.Add(mt);
}
}
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;
}
HRESULT Outpin::InitAllocator(
IMemInputPin* pInputPin,
IMemAllocator* pAllocator)
{
assert(pInputPin);
assert(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
HRESULT hr = pInputPin->GetAllocatorRequirements(&props);
if (props.cBuffers <= 0)
props.cBuffers = 1;
const BITMAPINFOHEADER& bmih = GetBMIH();
LONG w = bmih.biWidth;
assert(w > 0);
LONG h = labs(bmih.biHeight);
assert(h > 0);
if (w % 16)
w = 16 * ((w + 15) / 16);
if (h % 16)
h = 16 * ((h + 15) / 16);
const long cbBuffer = w*h + 2*(w/2)*(h/2);
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_pInputPin = pInputPin;
m_pAllocator = pAllocator;
return S_OK; //success
}
HRESULT Outpin::SetAvgTimePerFrame(__int64 AvgTimePerFrame)
{
if (m_pPinConnection)
return VFW_E_ALREADY_CONNECTED;
const int num_types = m_preferred_mtv.Size();
for (int i = 0; i < num_types; ++i)
{
AM_MEDIA_TYPE& mt = m_preferred_mtv[i];
assert(mt.pbFormat);
if (mt.formattype == FORMAT_VideoInfo)
{
assert(mt.cbFormat >= sizeof(VIDEOINFOHEADER));
VIDEOINFOHEADER& vih = (VIDEOINFOHEADER&)(*mt.pbFormat);
vih.AvgTimePerFrame = AvgTimePerFrame;
}
else
{
assert(mt.formattype == FORMAT_VideoInfo2);
assert(mt.cbFormat >= sizeof(VIDEOINFOHEADER2));
VIDEOINFOHEADER2& vih = (VIDEOINFOHEADER2&)(*mt.pbFormat);
vih.AvgTimePerFrame = AvgTimePerFrame;
}
}
return S_OK;
}
} //end namespace VP8EncoderLib