// 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 |