880 lines
27 KiB
C++
880 lines
27 KiB
C++
|
#include <wchar.h>
|
||
|
#include <streams.h>
|
||
|
#include <atlbase.h>
|
||
|
#include <wmsecure.h>
|
||
|
#include <dmoreg.h>
|
||
|
#include <mediaerr.h>
|
||
|
#include "mediaobj.h"
|
||
|
#include "dmodshow.h"
|
||
|
#include "filter.h"
|
||
|
#include "inpin.h"
|
||
|
#include "outpin.h"
|
||
|
#include "wmcodecstrs.h" // from wm encoder group, not public currently
|
||
|
|
||
|
CWrapperOutputPin::CWrapperOutputPin(
|
||
|
CMediaWrapperFilter *pFilter,
|
||
|
ULONG Id,
|
||
|
BOOL bOptional,
|
||
|
HRESULT *phr) :
|
||
|
CBaseOutputPin(NAME("CWrapperOutputPin"),
|
||
|
pFilter,
|
||
|
pFilter->FilterLock(),
|
||
|
phr,
|
||
|
_PinName_(bOptional ? L"~out" : L"out", Id).Name()
|
||
|
),
|
||
|
m_Id(Id),
|
||
|
m_fNoPosPassThru(FALSE),
|
||
|
m_pPosPassThru(NULL),
|
||
|
m_pMediaSample(NULL),
|
||
|
// compression setting default values, move to struct eventually
|
||
|
m_lQuality( -1 ),
|
||
|
m_lKeyFrameRate( -1 ),
|
||
|
m_bUseIAMStreamConfigOnDMO( false ),
|
||
|
m_bUseIAMVideoCompressionOnDMO( false ),
|
||
|
m_pmtFromSetFormat( NULL )
|
||
|
{
|
||
|
}
|
||
|
|
||
|
CWrapperOutputPin::~CWrapperOutputPin() {
|
||
|
delete m_pPosPassThru;
|
||
|
|
||
|
if( m_pmtFromSetFormat )
|
||
|
{
|
||
|
// clean up any media type we might have cached from a SetFormat call
|
||
|
DeleteMediaType( m_pmtFromSetFormat );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
HRESULT CWrapperOutputPin::NonDelegatingQueryInterface(REFGUID riid, void **ppv) {
|
||
|
if (SUCCEEDED(CBaseOutputPin::NonDelegatingQueryInterface(riid, ppv)))
|
||
|
return NOERROR;
|
||
|
|
||
|
if ((riid == IID_IMediaPosition) || (riid == IID_IMediaSeeking)) {
|
||
|
CAutoLock l(&m_csPassThru);
|
||
|
|
||
|
// The first time we get here, we attempt to create a CPosPassThru
|
||
|
// object. If we succeed, we use the object in all subsequent QI
|
||
|
// calls. If we fail, we set m_fNoPassThru to TRUE so that we never
|
||
|
// try again. Trying again and succeeding would violate COM rules.
|
||
|
if (m_fNoPosPassThru)
|
||
|
return E_NOINTERFACE;
|
||
|
|
||
|
// Create a CPosPassThru if we don't have one already
|
||
|
if (!m_pPosPassThru) {
|
||
|
CWrapperInputPin* pInPin = Filter()->GetInputPinForPassThru();
|
||
|
if (pInPin) {
|
||
|
HRESULT hr = S_OK;
|
||
|
m_pPosPassThru = new CPosPassThru(TEXT("DMO wrapper PosPassThru"),
|
||
|
(IPin*)this,
|
||
|
&hr,
|
||
|
pInPin);
|
||
|
if (m_pPosPassThru && (FAILED(hr))) {
|
||
|
delete m_pPosPassThru;
|
||
|
m_pPosPassThru = NULL;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (m_pPosPassThru) {
|
||
|
return m_pPosPassThru->NonDelegatingQueryInterface(riid, ppv);
|
||
|
}
|
||
|
else {
|
||
|
m_fNoPosPassThru = TRUE;
|
||
|
return E_NOINTERFACE;
|
||
|
}
|
||
|
}
|
||
|
else if (riid == IID_IAMStreamConfig )
|
||
|
{
|
||
|
// we support this interface for audio and video encoders
|
||
|
if (IsAudioEncoder() || IsVideoEncoder() )
|
||
|
{
|
||
|
if( 0 == m_Id && !m_bUseIAMStreamConfigOnDMO )
|
||
|
{
|
||
|
// first check whether the dmo supports this natively and cache the interface pointer if so
|
||
|
// BUGBUG needs to be per output stream!!
|
||
|
// for now fail only ask if 1st output stream
|
||
|
CComQIPtr< IAMStreamConfig, &IID_IAMStreamConfig > pStreamConfigOnDMO( Filter()->m_pMediaObject );
|
||
|
if( pStreamConfigOnDMO )
|
||
|
{
|
||
|
// so it is supported natively, but we must release it since it winds up addref'ing the filter
|
||
|
m_bUseIAMStreamConfigOnDMO = true;
|
||
|
DbgLog((LOG_TRACE,3,TEXT("CWrapperOutputPin::NonDelegatingQI - DMO supports IAMStreamConfig natively")));
|
||
|
}
|
||
|
}
|
||
|
// either way it'll go through us
|
||
|
return GetInterface( static_cast<IAMStreamConfig *> (this), ppv );
|
||
|
}
|
||
|
}
|
||
|
else if (riid == IID_IAMVideoCompression )
|
||
|
{
|
||
|
// we support this interface for video encoders
|
||
|
if ( IsVideoEncoder() )
|
||
|
{
|
||
|
if( 0 == m_Id && !m_bUseIAMVideoCompressionOnDMO )
|
||
|
{
|
||
|
// first check whether the dmo supports this natively and cache the interface pointer if so
|
||
|
// BUGBUG needs to be per output stream!!
|
||
|
// for now fail only ask if 1st output stream
|
||
|
CComQIPtr< IAMVideoCompression, &IID_IAMVideoCompression > pVideoCompressionOnDMO( Filter()->m_pMediaObject );
|
||
|
if( pVideoCompressionOnDMO )
|
||
|
{
|
||
|
// so it is supported natively, but we must release it since it winds up addref'ing the filter
|
||
|
m_bUseIAMVideoCompressionOnDMO = true;
|
||
|
DbgLog((LOG_TRACE,3,TEXT("CWrapperOutputPin::NonDelegatingQI - DMO supports IAMVideoCompression natively")));
|
||
|
}
|
||
|
}
|
||
|
// either way it'll go through us
|
||
|
return GetInterface( static_cast<IAMVideoCompression *> (this), ppv);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return E_NOINTERFACE;
|
||
|
}
|
||
|
|
||
|
HRESULT CWrapperOutputPin::CheckMediaType(const CMediaType *pmt)
|
||
|
{
|
||
|
return Filter()->OutputCheckMediaType(m_Id, pmt);
|
||
|
}
|
||
|
HRESULT CWrapperOutputPin::SetMediaType(const CMediaType *pmt)
|
||
|
{
|
||
|
CAutoLock l(&m_csStream);
|
||
|
HRESULT hr = Filter()->OutputSetMediaType(m_Id, pmt);
|
||
|
if (SUCCEEDED(hr)) {
|
||
|
hr = CBaseOutputPin::SetMediaType(pmt);
|
||
|
if (SUCCEEDED(hr)) {
|
||
|
m_fVideo = pmt->majortype == MEDIATYPE_Video ? true : false;
|
||
|
}
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
HRESULT CWrapperOutputPin::GetMediaType(int iPosition, CMediaType *pMediaType)
|
||
|
{
|
||
|
if( m_pmtFromSetFormat )
|
||
|
{
|
||
|
// our SetFormat has been called so only offer that type from now on
|
||
|
if( iPosition != 0 )
|
||
|
return E_INVALIDARG;
|
||
|
|
||
|
*pMediaType = *m_pmtFromSetFormat;
|
||
|
return S_OK;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return Filter()->OutputGetMediaType(m_Id, (ULONG)iPosition, pMediaType);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// override primarily for the case where we're a wm dmo video encoder connecting directly
|
||
|
// to the ASF writer, in a desparate attempt to get an output type which a wm video encoder
|
||
|
// will accept in the default connection case
|
||
|
//
|
||
|
STDMETHODIMP CWrapperOutputPin::Connect(IPin *pReceivePin, const AM_MEDIA_TYPE *pmt)
|
||
|
{
|
||
|
DbgLog((LOG_TRACE,3,TEXT("CWrapperOutputPin::Connect")));
|
||
|
CAutoLock lck(&(Filter()->m_csFilter));
|
||
|
//
|
||
|
// if connecting to the asf writer try getting a default type from the writer
|
||
|
//
|
||
|
// note that, although we'd like to do this only if SetFormat hasn't been called,
|
||
|
// we have no guarantee that the writer format hasn't changed, so we need to
|
||
|
// continually call SetFormat with the type we get from the downstream pin's GetFormat
|
||
|
bool bSetFormatOnConnect = false;
|
||
|
|
||
|
if( !pmt && !m_pmtFromSetFormat && IsVideoEncoder() )
|
||
|
{
|
||
|
CComQIPtr< IAMStreamConfig, &IID_IAMStreamConfig > pStreamConfig( pReceivePin );
|
||
|
if( pStreamConfig )
|
||
|
{
|
||
|
AM_MEDIA_TYPE *pmt2;
|
||
|
HRESULT hrInt = pStreamConfig->GetFormat( &pmt2 );
|
||
|
if( SUCCEEDED( hrInt ) )
|
||
|
{
|
||
|
// now we'll only offer this type!
|
||
|
hrInt = SetFormat( pmt2 );
|
||
|
if( SUCCEEDED( hrInt ) )
|
||
|
{
|
||
|
bSetFormatOnConnect = true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
// call the base class connect
|
||
|
HRESULT hr = CBaseOutputPin::Connect(pReceivePin,pmt);
|
||
|
if( bSetFormatOnConnect )
|
||
|
{
|
||
|
// whether we failed or not, unset the format if we set one here in connect
|
||
|
if( m_pmtFromSetFormat )
|
||
|
{
|
||
|
// clean up any media type we might have cached from a SetFormat call
|
||
|
DeleteMediaType( m_pmtFromSetFormat );
|
||
|
m_pmtFromSetFormat = NULL;
|
||
|
}
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
// Remove any media type when breaking a connection
|
||
|
HRESULT CWrapperOutputPin::BreakConnect()
|
||
|
{
|
||
|
HRESULT hr = CBaseOutputPin::BreakConnect();
|
||
|
Filter()->m_pMediaObject->SetOutputType(m_Id, &CMediaType(), DMO_SET_TYPEF_CLEAR);
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
HRESULT CWrapperOutputPin::DecideBufferSize(
|
||
|
IMemAllocator * pAlloc,
|
||
|
ALLOCATOR_PROPERTIES * ppropInputRequest
|
||
|
)
|
||
|
{
|
||
|
return Filter()->OutputDecideBufferSize(m_Id, pAlloc, ppropInputRequest);
|
||
|
}
|
||
|
|
||
|
HRESULT CWrapperOutputPin::Notify(IBaseFilter * pSender, Quality q)
|
||
|
{
|
||
|
LogPublicEntry(LOG_STREAM,"Quality Notify");
|
||
|
HRESULT hr;
|
||
|
|
||
|
// If quality sink set, forward the quality request to it
|
||
|
if (m_pQSink) {
|
||
|
hr = m_pQSink->Notify(Filter(), q);
|
||
|
LogHResult(hr, LOG_STREAM, "Quality Notify", "m_pQSink->Notify");
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
// This will try the DMO, then the upstream pin
|
||
|
return Filter()->QualityNotify(m_Id, q);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// IAMStreamConfig
|
||
|
//
|
||
|
HRESULT CWrapperOutputPin::SetFormat(AM_MEDIA_TYPE *pmt)
|
||
|
{
|
||
|
DbgLog((LOG_TRACE,5,TEXT("CWrapperOutputPin - IAMStreamConfig::SetFormat")));
|
||
|
CAutoLock lck(&(Filter()->m_csFilter));
|
||
|
HRESULT hr = S_OK;
|
||
|
if (NULL == pmt)
|
||
|
{
|
||
|
// I'd rather use this to "unset" the type, but that's not how other encoders work
|
||
|
// previously they returned E_POINTER for this
|
||
|
// can we break tradition?
|
||
|
DeleteMediaType( m_pmtFromSetFormat );
|
||
|
m_pmtFromSetFormat = NULL;
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
if (Filter()->m_State != State_Stopped)
|
||
|
return VFW_E_NOT_STOPPED;
|
||
|
|
||
|
// ensure inputs connected to this output are connected
|
||
|
// since our possible output formats depend on the input format
|
||
|
if( !IsInputConnected() )
|
||
|
{
|
||
|
return VFW_E_NOT_CONNECTED;
|
||
|
}
|
||
|
|
||
|
if( m_bUseIAMStreamConfigOnDMO )
|
||
|
{
|
||
|
CComQIPtr< IAMStreamConfig, &IID_IAMStreamConfig > pStreamConfigOnDMO( Filter()->m_pMediaObject );
|
||
|
ASSERT( pStreamConfigOnDMO );
|
||
|
return pStreamConfigOnDMO->SetFormat( pmt );
|
||
|
}
|
||
|
|
||
|
#ifdef DEBUG
|
||
|
if(pmt->pbFormat && pmt->cbFormat > 0 )
|
||
|
{
|
||
|
if( IsVideoEncoder() )
|
||
|
{
|
||
|
DbgLog((LOG_TRACE,3,TEXT("CWrapperOutputPin - IAMStreamConfig::SetFormat %x %dbit %dx%d"),
|
||
|
HEADER(pmt->pbFormat)->biCompression,
|
||
|
HEADER(pmt->pbFormat)->biBitCount,
|
||
|
HEADER(pmt->pbFormat)->biWidth,
|
||
|
HEADER(pmt->pbFormat)->biHeight));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
DbgLog((LOG_TRACE,3,TEXT("CWrapperOutputPin - IAMStreamConfig::SetFormat to tag:%d %dbit %dchannel %dHz"),
|
||
|
((LPWAVEFORMATEX)(pmt->pbFormat))->wFormatTag,
|
||
|
((LPWAVEFORMATEX)(pmt->pbFormat))->wBitsPerSample,
|
||
|
((LPWAVEFORMATEX)(pmt->pbFormat))->nChannels,
|
||
|
((LPWAVEFORMATEX)(pmt->pbFormat))->nSamplesPerSec));
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
// If this is the same format as we already are using, don't bother
|
||
|
CMediaType cmt;
|
||
|
hr = GetMediaType(0,&cmt);
|
||
|
if (S_OK != hr)
|
||
|
return hr;
|
||
|
|
||
|
if (cmt == *pmt)
|
||
|
{
|
||
|
return NOERROR;
|
||
|
}
|
||
|
|
||
|
// see if we like this type
|
||
|
if ((hr = CheckMediaType((CMediaType *)pmt)) != NOERROR)
|
||
|
{
|
||
|
DbgLog((LOG_TRACE,2,TEXT("IAMStreamConfig::SetFormat rejected")));
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
// if we're connected, ask downstream
|
||
|
if (IsConnected())
|
||
|
{
|
||
|
hr = GetConnected()->QueryAccept(pmt);
|
||
|
if (hr != NOERROR)
|
||
|
{
|
||
|
return VFW_E_INVALIDMEDIATYPE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// this is now the preferred type (type 0)
|
||
|
hr = SetMediaType((CMediaType *)pmt);
|
||
|
if( S_OK == hr )
|
||
|
{
|
||
|
// only offer this type from now on!
|
||
|
if( m_pmtFromSetFormat )
|
||
|
DeleteMediaType( m_pmtFromSetFormat );
|
||
|
|
||
|
m_pmtFromSetFormat = CreateMediaType( ( AM_MEDIA_TYPE * ) pmt );
|
||
|
if( !m_pmtFromSetFormat )
|
||
|
return E_OUTOFMEMORY;
|
||
|
}
|
||
|
ASSERT(hr == S_OK);
|
||
|
|
||
|
// Changing the format means reconnecting if necessary
|
||
|
if (IsConnected())
|
||
|
Filter()->m_pGraph->Reconnect(this);
|
||
|
|
||
|
return NOERROR;
|
||
|
}
|
||
|
|
||
|
|
||
|
HRESULT CWrapperOutputPin::GetFormat(AM_MEDIA_TYPE **ppmt)
|
||
|
{
|
||
|
DbgLog((LOG_TRACE,2,TEXT("CWrapperOutputPin - IAMStreamConfig::GetFormat")));
|
||
|
|
||
|
if (ppmt == NULL)
|
||
|
return E_POINTER;
|
||
|
|
||
|
CAutoLock lck(&(Filter()->m_csFilter));
|
||
|
|
||
|
// ensure inputs connected to this output are connected
|
||
|
// since our possible output formats depend on the input format
|
||
|
if( !IsInputConnected() )
|
||
|
{
|
||
|
return VFW_E_NOT_CONNECTED;
|
||
|
}
|
||
|
|
||
|
if( m_bUseIAMStreamConfigOnDMO )
|
||
|
{
|
||
|
CComQIPtr< IAMStreamConfig, &IID_IAMStreamConfig > pStreamConfigOnDMO( Filter()->m_pMediaObject );
|
||
|
ASSERT( pStreamConfigOnDMO );
|
||
|
return pStreamConfigOnDMO->GetFormat( ppmt );
|
||
|
}
|
||
|
|
||
|
// type 0 is always the preferred type
|
||
|
// actually this isn't the case for at least wm encoders, but we'll fake it
|
||
|
*ppmt = (AM_MEDIA_TYPE *)CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE));
|
||
|
if (*ppmt == NULL)
|
||
|
return E_OUTOFMEMORY;
|
||
|
|
||
|
ZeroMemory(*ppmt, sizeof(AM_MEDIA_TYPE));
|
||
|
HRESULT hr = GetMediaType(0, (CMediaType *)*ppmt);
|
||
|
if (hr != NOERROR)
|
||
|
{
|
||
|
CoTaskMemFree(*ppmt);
|
||
|
*ppmt = NULL;
|
||
|
return hr;
|
||
|
}
|
||
|
return NOERROR;
|
||
|
}
|
||
|
|
||
|
|
||
|
HRESULT CWrapperOutputPin::GetNumberOfCapabilities(int *piCount, int *piSize)
|
||
|
{
|
||
|
DbgLog((LOG_TRACE,5,TEXT("CWrapperOutputPin - IAMStreamConfig::GetNumberOfCapabilities")));
|
||
|
if (piCount == NULL || piSize == NULL)
|
||
|
return E_POINTER;
|
||
|
|
||
|
if( m_bUseIAMStreamConfigOnDMO )
|
||
|
{
|
||
|
CComQIPtr< IAMStreamConfig, &IID_IAMStreamConfig > pStreamConfigOnDMO( Filter()->m_pMediaObject );
|
||
|
ASSERT( pStreamConfigOnDMO );
|
||
|
return pStreamConfigOnDMO->GetNumberOfCapabilities( piCount, piSize );
|
||
|
}
|
||
|
|
||
|
// find out how many output types the dmo enumerates
|
||
|
// note that it's ok to show possible output types before connecting input
|
||
|
int iType = 0;
|
||
|
HRESULT hr = S_OK;
|
||
|
while( S_OK == hr )
|
||
|
{
|
||
|
// just enumerating, no need to get mt
|
||
|
hr = GetMediaType( iType, NULL );
|
||
|
if( S_OK == hr )
|
||
|
iType++;
|
||
|
}
|
||
|
*piCount = iType;
|
||
|
|
||
|
if( IsVideoEncoder() )
|
||
|
{
|
||
|
*piSize = sizeof(VIDEO_STREAM_CONFIG_CAPS);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ASSERT( IsAudioEncoder() );
|
||
|
*piSize = sizeof(AUDIO_STREAM_CONFIG_CAPS);
|
||
|
}
|
||
|
return NOERROR;
|
||
|
}
|
||
|
|
||
|
HRESULT CWrapperOutputPin::GetStreamCaps(int i, AM_MEDIA_TYPE **ppmt, LPBYTE pSCC)
|
||
|
{
|
||
|
DbgLog((LOG_TRACE,5,TEXT("CWrapperOutputPin - IAMStreamConfig::GetStreamCaps")));
|
||
|
|
||
|
if (i < 0)
|
||
|
return E_INVALIDARG;
|
||
|
|
||
|
if (NULL == pSCC || NULL == ppmt)
|
||
|
return E_POINTER;
|
||
|
|
||
|
if( m_bUseIAMStreamConfigOnDMO )
|
||
|
{
|
||
|
CComQIPtr< IAMStreamConfig, &IID_IAMStreamConfig > pStreamConfigOnDMO( Filter()->m_pMediaObject );
|
||
|
ASSERT( pStreamConfigOnDMO );
|
||
|
return pStreamConfigOnDMO->GetStreamCaps( i, ppmt, pSCC );
|
||
|
}
|
||
|
|
||
|
*ppmt = (AM_MEDIA_TYPE *)CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE));
|
||
|
if (NULL == *ppmt)
|
||
|
return E_OUTOFMEMORY;
|
||
|
ZeroMemory(*ppmt, sizeof(AM_MEDIA_TYPE));
|
||
|
HRESULT hr = GetMediaType(i, (CMediaType *)*ppmt);
|
||
|
if (hr != NOERROR)
|
||
|
{
|
||
|
CoTaskMemFree(*ppmt);
|
||
|
*ppmt = NULL;
|
||
|
|
||
|
if( DMO_E_NO_MORE_ITEMS == hr || E_INVALIDARG == hr )
|
||
|
{
|
||
|
// is this spec'd to return S_FALSE if too high a type? Seems so from other encoders.
|
||
|
return S_FALSE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return hr;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if( IsVideoEncoder() )
|
||
|
{
|
||
|
VIDEO_STREAM_CONFIG_CAPS *pVSCC = (VIDEO_STREAM_CONFIG_CAPS *)pSCC;
|
||
|
|
||
|
ZeroMemory(pVSCC, sizeof(VIDEO_STREAM_CONFIG_CAPS));
|
||
|
pVSCC->guid = MEDIATYPE_Video;
|
||
|
|
||
|
if( (*ppmt)->pbFormat && (*ppmt)->cbFormat > 0 )
|
||
|
{
|
||
|
BITMAPINFOHEADER *pbmih = HEADER((*ppmt)->pbFormat);
|
||
|
pVSCC->InputSize.cx = pbmih->biWidth;
|
||
|
pVSCC->InputSize.cy = pbmih->biHeight;
|
||
|
pVSCC->MinCroppingSize.cx = pbmih->biWidth;
|
||
|
pVSCC->MinCroppingSize.cy = pbmih->biHeight;
|
||
|
pVSCC->MaxCroppingSize.cx = pbmih->biWidth;
|
||
|
pVSCC->MaxCroppingSize.cy = pbmih->biHeight;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
AUDIO_STREAM_CONFIG_CAPS *pASCC = (AUDIO_STREAM_CONFIG_CAPS *)pSCC;
|
||
|
|
||
|
ZeroMemory(pASCC, sizeof(AUDIO_STREAM_CONFIG_CAPS));
|
||
|
pASCC->guid = MEDIATYPE_Audio;
|
||
|
|
||
|
if( (*ppmt)->pbFormat && (*ppmt)->cbFormat > 0 )
|
||
|
{
|
||
|
LPWAVEFORMATEX pwfx = (LPWAVEFORMATEX)(*ppmt)->pbFormat;
|
||
|
// rather let's just offer exactly what the dmo offers (if filled in?)
|
||
|
|
||
|
pASCC->MinimumChannels = pwfx->nChannels;
|
||
|
pASCC->MaximumChannels = pwfx->nChannels;
|
||
|
pASCC->ChannelsGranularity = 1;
|
||
|
pASCC->MinimumBitsPerSample = pwfx->wBitsPerSample;
|
||
|
pASCC->MaximumBitsPerSample = pwfx->wBitsPerSample;
|
||
|
pASCC->BitsPerSampleGranularity = 8;
|
||
|
pASCC->MinimumSampleFrequency = pwfx->nSamplesPerSec;
|
||
|
pASCC->MaximumSampleFrequency = pwfx->nSamplesPerSec;
|
||
|
pASCC->SampleFrequencyGranularity = 1; //?
|
||
|
}
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// IAMVideoCompression
|
||
|
//
|
||
|
|
||
|
#define DMO_COMPRESSION_QUALITY_MAX 10000 // is this set in stone? Check this.
|
||
|
|
||
|
// make key frames this often
|
||
|
//
|
||
|
HRESULT CWrapperOutputPin::put_KeyFrameRate(long KeyFrameRate)
|
||
|
{
|
||
|
DbgLog((LOG_TRACE,5,TEXT("CWrapperOutputPin - IAMVideoCompression::put_KeyFrameRate")));
|
||
|
CAutoLock lck(&(Filter()->m_csFilter));
|
||
|
|
||
|
if( m_bUseIAMVideoCompressionOnDMO )
|
||
|
{
|
||
|
CComQIPtr< IAMVideoCompression, &IID_IAMVideoCompression > pVideoCompressionOnDMO( Filter()->m_pMediaObject );
|
||
|
ASSERT( pVideoCompressionOnDMO );
|
||
|
return pVideoCompressionOnDMO->put_KeyFrameRate( KeyFrameRate );
|
||
|
}
|
||
|
|
||
|
HRESULT hr = S_OK;
|
||
|
if( KeyFrameRate < 0 )
|
||
|
{
|
||
|
// used to set default key frame rate, which we don't know
|
||
|
// do nothing
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// check whether units match!
|
||
|
hr = SetCompressionParamUsingIPropBag( g_wszWMVCKeyframeDistance, KeyFrameRate );
|
||
|
if( SUCCEEDED( hr ) )
|
||
|
{
|
||
|
// update our internal copy
|
||
|
m_lKeyFrameRate = KeyFrameRate;
|
||
|
}
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
// make key frames this often
|
||
|
//
|
||
|
HRESULT CWrapperOutputPin::get_KeyFrameRate(long FAR* pKeyFrameRate)
|
||
|
{
|
||
|
DbgLog((LOG_TRACE,5,TEXT("CWrapperOutputPin - IAMVideoCompression::get_KeyFrameRate")));
|
||
|
if( NULL == pKeyFrameRate )
|
||
|
return E_POINTER;
|
||
|
|
||
|
if( m_bUseIAMVideoCompressionOnDMO )
|
||
|
{
|
||
|
CComQIPtr< IAMVideoCompression, &IID_IAMVideoCompression > pVideoCompressionOnDMO( Filter()->m_pMediaObject );
|
||
|
ASSERT( pVideoCompressionOnDMO );
|
||
|
return pVideoCompressionOnDMO->get_KeyFrameRate( pKeyFrameRate );
|
||
|
}
|
||
|
|
||
|
// wm codecs don't support a get, so just return the current internal value
|
||
|
*pKeyFrameRate = m_lKeyFrameRate;
|
||
|
|
||
|
return NOERROR;
|
||
|
}
|
||
|
|
||
|
// compress with this quality
|
||
|
//
|
||
|
HRESULT CWrapperOutputPin::put_Quality(double Quality)
|
||
|
{
|
||
|
DbgLog((LOG_TRACE,5,TEXT("CWrapperOutputPin - IAMVideoCompression::put_Quality")));
|
||
|
|
||
|
CAutoLock lck(&(Filter()->m_csFilter));
|
||
|
|
||
|
if( m_bUseIAMVideoCompressionOnDMO )
|
||
|
{
|
||
|
CComQIPtr< IAMVideoCompression, &IID_IAMVideoCompression > pVideoCompressionOnDMO( Filter()->m_pMediaObject );
|
||
|
ASSERT( pVideoCompressionOnDMO );
|
||
|
return pVideoCompressionOnDMO->put_Quality( Quality );
|
||
|
}
|
||
|
|
||
|
HRESULT hr = S_OK;
|
||
|
if (Quality < 0)
|
||
|
{
|
||
|
// used to set default quality, except we don't know how to find out what this is!
|
||
|
// so do nothing, for now
|
||
|
}
|
||
|
else if (Quality >= 0. && Quality <= 1.)
|
||
|
{
|
||
|
// check whether units match!
|
||
|
long lQuality = (long)( Quality * DMO_COMPRESSION_QUALITY_MAX );
|
||
|
hr = SetCompressionParamUsingIPropBag( g_wszWMVCCrisp, lQuality );
|
||
|
if( SUCCEEDED( hr ) )
|
||
|
{
|
||
|
// update our internal copy
|
||
|
m_lQuality = lQuality;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
hr = E_INVALIDARG;
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
// compress with this quality
|
||
|
//
|
||
|
HRESULT CWrapperOutputPin::get_Quality(double FAR* pQuality)
|
||
|
{
|
||
|
DbgLog((LOG_TRACE,5,TEXT("CWrapperOutputPin - IAMVideoCompression::get_Quality")));
|
||
|
if( NULL == pQuality )
|
||
|
return E_POINTER;
|
||
|
|
||
|
CAutoLock lck(&(Filter()->m_csFilter));
|
||
|
if( m_bUseIAMVideoCompressionOnDMO )
|
||
|
{
|
||
|
CComQIPtr< IAMVideoCompression, &IID_IAMVideoCompression > pVideoCompressionOnDMO( Filter()->m_pMediaObject );
|
||
|
ASSERT( pVideoCompressionOnDMO );
|
||
|
return pVideoCompressionOnDMO->get_Quality( pQuality );
|
||
|
}
|
||
|
|
||
|
// scale the dmo encoder's bounds to 0-1, hmm...?
|
||
|
if( m_lQuality < 0 )
|
||
|
{
|
||
|
// assume default
|
||
|
*pQuality = -1.;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// wm codecs don't support a get, so just return the current internal value
|
||
|
*pQuality = m_lQuality / (double)DMO_COMPRESSION_QUALITY_MAX; // ?
|
||
|
}
|
||
|
return NOERROR;
|
||
|
}
|
||
|
|
||
|
|
||
|
// every frame must fit in the data rate...
|
||
|
//
|
||
|
HRESULT CWrapperOutputPin::put_WindowSize(DWORDLONG WindowSize)
|
||
|
{
|
||
|
DbgLog((LOG_TRACE,5,TEXT("CWrapperOutputPin - IAMVideoCompression::put_WindowSize")));
|
||
|
|
||
|
CAutoLock lck(&(Filter()->m_csFilter));
|
||
|
|
||
|
if( m_bUseIAMVideoCompressionOnDMO )
|
||
|
{
|
||
|
CComQIPtr< IAMVideoCompression, &IID_IAMVideoCompression > pVideoCompressionOnDMO( Filter()->m_pMediaObject );
|
||
|
ASSERT( pVideoCompressionOnDMO );
|
||
|
return pVideoCompressionOnDMO->put_WindowSize( WindowSize );
|
||
|
}
|
||
|
|
||
|
return E_NOTIMPL;
|
||
|
}
|
||
|
|
||
|
|
||
|
// every frame must fit in the data rate... we don't do the WindowSize thing
|
||
|
//
|
||
|
HRESULT CWrapperOutputPin::get_WindowSize(DWORDLONG FAR* pWindowSize)
|
||
|
{
|
||
|
DbgLog((LOG_TRACE,5,TEXT("CWrapperOutputPin - IAMVideoCompression::get_WindowSize")));
|
||
|
|
||
|
if (pWindowSize == NULL)
|
||
|
return E_POINTER;
|
||
|
|
||
|
CAutoLock lck(&(Filter()->m_csFilter));
|
||
|
if( m_bUseIAMVideoCompressionOnDMO )
|
||
|
{
|
||
|
CComQIPtr< IAMVideoCompression, &IID_IAMVideoCompression > pVideoCompressionOnDMO( Filter()->m_pMediaObject );
|
||
|
ASSERT( pVideoCompressionOnDMO );
|
||
|
return pVideoCompressionOnDMO->get_WindowSize( pWindowSize );
|
||
|
}
|
||
|
|
||
|
*pWindowSize = 1; // we don't do windows
|
||
|
return NOERROR;
|
||
|
}
|
||
|
|
||
|
|
||
|
// make this frame a key frame, whenever it comes by
|
||
|
//
|
||
|
HRESULT CWrapperOutputPin::OverrideKeyFrame(long FrameNumber)
|
||
|
{
|
||
|
DbgLog((LOG_TRACE,5,TEXT("CWrapperOutputPin - IAMVideoCompression::OverrideKeyFrame")));
|
||
|
|
||
|
if( m_bUseIAMVideoCompressionOnDMO )
|
||
|
{
|
||
|
CComQIPtr< IAMVideoCompression, &IID_IAMVideoCompression > pVideoCompressionOnDMO( Filter()->m_pMediaObject );
|
||
|
ASSERT( pVideoCompressionOnDMO );
|
||
|
return pVideoCompressionOnDMO->OverrideKeyFrame( FrameNumber );
|
||
|
}
|
||
|
|
||
|
// not needed currently
|
||
|
return E_NOTIMPL;
|
||
|
}
|
||
|
|
||
|
// make this frame this size, whenever it comes by
|
||
|
//
|
||
|
HRESULT CWrapperOutputPin::OverrideFrameSize(long FrameNumber, long Size)
|
||
|
{
|
||
|
DbgLog((LOG_TRACE,5,TEXT("CWrapperOutputPin - IAMVideoCompression::OverrideFrameSize")));
|
||
|
|
||
|
if( m_bUseIAMVideoCompressionOnDMO )
|
||
|
{
|
||
|
CComQIPtr< IAMVideoCompression, &IID_IAMVideoCompression > pVideoCompressionOnDMO( Filter()->m_pMediaObject );
|
||
|
ASSERT( pVideoCompressionOnDMO );
|
||
|
return pVideoCompressionOnDMO->OverrideFrameSize( FrameNumber, Size );
|
||
|
}
|
||
|
|
||
|
// not needed currently
|
||
|
return E_NOTIMPL;
|
||
|
}
|
||
|
|
||
|
|
||
|
// Get some information about the codec
|
||
|
//
|
||
|
HRESULT CWrapperOutputPin::GetInfo
|
||
|
(
|
||
|
LPWSTR pstrVersion,
|
||
|
int *pcbVersion,
|
||
|
LPWSTR pstrDescription,
|
||
|
int *pcbDescription,
|
||
|
long FAR* pDefaultKeyFrameRate,
|
||
|
long FAR* pDefaultPFramesPerKey,
|
||
|
double FAR* pDefaultQuality,
|
||
|
long FAR* pCapabilities
|
||
|
)
|
||
|
{
|
||
|
DbgLog((LOG_TRACE,5,TEXT("CWrapperOutputPin - IAMVideoCompression::GetInfo")));
|
||
|
|
||
|
if( m_bUseIAMVideoCompressionOnDMO )
|
||
|
{
|
||
|
CComQIPtr< IAMVideoCompression, &IID_IAMVideoCompression > pVideoCompressionOnDMO( Filter()->m_pMediaObject );
|
||
|
ASSERT( pVideoCompressionOnDMO );
|
||
|
return pVideoCompressionOnDMO->GetInfo(
|
||
|
pstrVersion,
|
||
|
pcbVersion,
|
||
|
pstrDescription,
|
||
|
pcbDescription,
|
||
|
pDefaultKeyFrameRate,
|
||
|
pDefaultPFramesPerKey,
|
||
|
pDefaultQuality,
|
||
|
pCapabilities );
|
||
|
}
|
||
|
|
||
|
// there's no way to query default settings for wm codecs currently?
|
||
|
return E_NOTIMPL;
|
||
|
|
||
|
#if 0
|
||
|
CAutoLock lck(&(Filter()->m_csFilter));
|
||
|
|
||
|
// for ICM we did this...
|
||
|
if (pDefaultKeyFrameRate)
|
||
|
*pDefaultKeyFrameRate = ICGetDefaultKeyFrameRate(hic);
|
||
|
if (pDefaultPFramesPerKey)
|
||
|
*pDefaultPFramesPerKey = 0;
|
||
|
if (pDefaultQuality)
|
||
|
// scale this 0-1
|
||
|
*pDefaultQuality = ICGetDefaultQuality(hic) / (double)ICQUALITY_HIGH;
|
||
|
if (pCapabilities)
|
||
|
{
|
||
|
*pCapabilities = 0;
|
||
|
if (dw > 0)
|
||
|
{
|
||
|
*pCapabilities |= ((icinfo.dwFlags & VIDCF_QUALITY) ?
|
||
|
CompressionCaps_CanQuality : 0);
|
||
|
*pCapabilities |= ((icinfo.dwFlags & VIDCF_CRUNCH) ?
|
||
|
CompressionCaps_CanCrunch : 0);
|
||
|
*pCapabilities |= ((icinfo.dwFlags & VIDCF_TEMPORAL) ?
|
||
|
CompressionCaps_CanKeyFrame : 0);
|
||
|
// we don't do b frames
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// We have no version string, but we have a description
|
||
|
if (pstrVersion)
|
||
|
*pstrVersion = 0;
|
||
|
if (pcbVersion)
|
||
|
*pcbVersion = 0;
|
||
|
if (dw > 0)
|
||
|
{
|
||
|
if (pstrDescription && pcbDescription)
|
||
|
lstrcpynW(pstrDescription, (LPCWSTR)&icinfo.szDescription,
|
||
|
min(*pcbDescription / 2,
|
||
|
lstrlenW((LPCWSTR)&icinfo.szDescription) + 1));
|
||
|
if (pcbDescription)
|
||
|
// string length in bytes, incl. NULL
|
||
|
*pcbDescription = lstrlenW((LPCWSTR)&icinfo.szDescription) * 2 + 2;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (pstrDescription)
|
||
|
{
|
||
|
*pstrDescription = 0;
|
||
|
if (pcbDescription)
|
||
|
*pcbDescription = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return NOERROR;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
HRESULT CWrapperOutputPin::SetCompressionParamUsingIPropBag
|
||
|
(
|
||
|
const WCHAR * wszParam,
|
||
|
const LONG lValue
|
||
|
)
|
||
|
{
|
||
|
HRESULT hr = E_NOTIMPL;
|
||
|
|
||
|
//
|
||
|
// wm codecs support setting of compression properties through IPropertyBag, try this first
|
||
|
//
|
||
|
CComQIPtr< IPropertyBag, &IID_IPropertyBag > pPropBag( Filter()->m_pMediaObject );
|
||
|
if( !pPropBag )
|
||
|
{
|
||
|
DbgLog((LOG_TRACE,2,TEXT("CWrapperOutputPin::SetCompressionParamUsingIPropBag - DMO doesn't support IPropertyBag for compression setting") ) );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// attempt to set the property
|
||
|
VARIANT var;
|
||
|
|
||
|
V_VT( &var ) = VT_I4;
|
||
|
V_I4( &var ) = lValue;
|
||
|
|
||
|
hr = pPropBag->Write( wszParam, &var );
|
||
|
#ifdef DEBUG
|
||
|
if( FAILED( hr ) )
|
||
|
{
|
||
|
DbgLog((LOG_TRACE,
|
||
|
3,
|
||
|
TEXT("CWrapperOutputPin::SetCompressionParamUsingIPropBag - DMO supports IPropertyBag but not %ls setting"),
|
||
|
wszParam ) );
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
bool CWrapperOutputPin::IsAudioEncoder()
|
||
|
{
|
||
|
if(Filter()->m_guidCat == DMOCATEGORY_AUDIO_ENCODER)
|
||
|
return true;
|
||
|
else
|
||
|
return false;
|
||
|
}
|
||
|
bool CWrapperOutputPin::IsVideoEncoder()
|
||
|
{
|
||
|
if(Filter()->m_guidCat == DMOCATEGORY_VIDEO_ENCODER)
|
||
|
return true;
|
||
|
else
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
bool CWrapperOutputPin::IsInputConnected()
|
||
|
{
|
||
|
for (DWORD cIn = 0; cIn < Filter()->m_cInputPins; cIn++)
|
||
|
{
|
||
|
if (Filter()->InputMapsToOutput(cIn, m_Id) &&
|
||
|
!(Filter()->m_pInputPins[cIn]->IsConnected()))
|
||
|
{
|
||
|
// some input not connected
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
return true;
|
||
|
}
|