2025-04-27 07:49:33 -04:00

716 lines
22 KiB
C++

#include <windows.h>
#include <mediaobj.h>
#include <atlbase.h>
#include <s98inc.h>
extern CComModule _Module;
#include <atlcom.h>
#include <initguid.h>
#include <atlconv.h>
#include <filefmt.h>
#include <filerw.h>
#include <tchar.h>
#include "testobj.h"
#include "dmort.h"
#include "Error.h"
#include <streams.h>
//#include <wmsdkidl.h>
#include <mediaerr.h>
#ifdef ENABLE_SECURE_DMO
#include <wmsdkidl.h>
#endif
int interval = 100;
CDMOObject::CDMOObject() :
m_pInputBuffers(NULL),
m_processCounter(0)
{
}
HRESULT CDMOObject::Create(REFCLSID rclsidObject, BOOL bAggregated)
{
HRESULT hr;
if (bAggregated) {
CComObject<CMyAggregator> *pObject = new CComObject<CMyAggregator>;
if (pObject == NULL) {
//Error(ERROR_TYPE_TEST, E_OUTOFMEMORY, TEXT("Out of memory"));
g_IShell->Log(1, "TEST ERROR, Out of Memory.");
return E_OUTOFMEMORY;
}
hr = pObject->SetCLSID(rclsidObject);
if (FAILED(hr)) {
return hr;
}
hr = pObject->GetControllingUnknown()->QueryInterface(
IID_IMediaObject, (void **)&m_pMediaObject);
if (FAILED(hr)) {
//Error(ERROR_TYPE_DMO, hr, TEXT("Create aggregated failed"));
g_IShell->Log(1, "DMO ERROR, Create Aggregated Failed.");
return hr;
}
} else {
// See if we can create it
HRESULT hr = CoCreateInstance(rclsidObject,
NULL,
CLSCTX_INPROC,
IID_IMediaObject,
(void **)&m_pMediaObject);
if (FAILED(hr)) {
Error(ERROR_TYPE_DMO, hr, TEXT("Create non-aggregated failed"));
g_IShell->Log(1, "DMO ERROR, Create non-aggregated failed.");
return hr;
}
}
#ifdef ENABLE_SECURE_DMO
hr = CreateSecureChannel( m_pMediaObject, &m_pCertUnknown, &m_pTestAppSecureChannel );
if( FAILED( hr ) ) {
Error(ERROR_TYPE_DMO, hr, TEXT("The test application could not create a secure channel."));
g_IShell->Log(1, "DMO ERROR, The test application could not create a secure channel.");
return hr;
}
// CreateSecureChannel() has two valid success values: S_OK and S_FALSE.
ASSERT( (S_OK == hr) || (S_FALSE == hr) );
#endif // ENABLE_SECURE_DMO
return S_OK;
}
#if 0
HRESULT CDMOObject::Stream(LPCTSTR lpszFile)
{
// Open the index and real files
FILE *hFileIndex = _tfopen(lpszIndex, TEXT("r"));
if (hFileIndex == NULL) {
TCHAR szText[1000];
wsprintf(szFile, TEXT("Could not open index file %s"), lpszIndex);
Error(ERROR_TYPE_TEST, E_FAIL, szText);
return E_FAIL;
}
// Open the real file
HANDLE hFile = CreateFile(lpszFile,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
0,
NULL);
if (hFile == INVALID_FILE_HANDLE) {
DWORD dwError = GetLastError();
TCHAR szText[1000];
wsprintf(szFile, TEXT("Could not open index file %s"), lpszIndex);
Error(ERROR_TYPE_TEST, szText, E_FAIL);
return E_FAIL;
}
//
}
#endif
#define CALL_WITH_HANDLER(_method_, _x_) \
HRESULT hr; \
__try { \
hr = m_pMediaObject->_method_ _x_; \
} __except(EXCEPTION_EXECUTE_HANDLER) { \
hr = E_FAIL; \
Error(ERROR_TYPE_DMO, E_FAIL, TEXT(#_method_) TEXT(" exception code %d"),\
GetExceptionCode()); \
g_IShell->Log(1, "DMO ERROR: Exception Code %d", GetExceptionCode()); \
} \
#define CALL_OBJECT_WITH_HANDLER(_object_, _method_, _x_) \
HRESULT hr; \
__try { \
hr = _object_->_method_ _x_; \
} __except(EXCEPTION_EXECUTE_HANDLER) { \
hr = E_FAIL; \
Error(ERROR_TYPE_DMO, E_FAIL, TEXT(#_method_) TEXT(" exception code %d"),\
GetExceptionCode()); \
g_IShell->Log(1, "DMO ERROR: Exception Code %d", GetExceptionCode()); \
} \
// Shadow IMediaObject
HRESULT CDMOObject::GetStreamCount(
DWORD *pcInputStreams,
DWORD *pcOutputStreams
)
{
if(m_processCounter%interval == 0)
g_IShell->Log(1, " Testing GetStreamCount()");
CALL_WITH_HANDLER(GetStreamCount, (pcInputStreams, pcOutputStreams))
return hr;
}
HRESULT CDMOObject::GetInputStreamInfo(
DWORD dwInputStreamIndex, // 0-based
DWORD *pdwFlags // HOLDS_BUFFERS
)
{
if(m_processCounter%interval == 0)
g_IShell->Log(1, " Testing GetInputStreamInfo()");
CALL_WITH_HANDLER(GetInputStreamInfo, (dwInputStreamIndex, pdwFlags))
if (pdwFlags == NULL && hr != E_POINTER) {
Error(ERROR_TYPE_DMO, hr, TEXT("GetInputStreamInfo returned %x when passed in NULL pointer"),
hr);
g_IShell->Log(1, "DMO ERROR: GetInputStreamInfo returned %x when passed in NULL pointer",
hr);
}
// Check flags
if (SUCCEEDED(hr) && pdwFlags &&
(*pdwFlags & ~
(DMO_INPUT_STREAMF_WHOLE_SAMPLES |
DMO_INPUT_STREAMF_SINGLE_SAMPLE_PER_BUFFER |
DMO_INPUT_STREAMF_FIXED_SAMPLE_SIZE |
DMO_INPUT_STREAMF_HOLDS_BUFFERS
)
)
){
Error(ERROR_TYPE_DMO, E_FAIL, TEXT("Invalid GetStreamInfo flags %x"), *pdwFlags);
g_IShell->Log(1, "DMO ERROR: Invalid GetStreamInfo flags %x", *pdwFlags);
}
return hr;
}
HRESULT CDMOObject::GetOutputStreamInfo(
DWORD dwOutputStreamIndex, // 0-based
DWORD *pdwFlags // Media object sets to 0
)
{
if(m_processCounter%interval == 0)
g_IShell->Log(1, " Testing GetOutputStreamInfo()");
CALL_WITH_HANDLER(GetOutputStreamInfo, (dwOutputStreamIndex, pdwFlags))
return hr;
}
//
// GetType - iterate through media types supported by a stream.
// Returns S_FALSE if the type index is out of range ("no more types").
//
HRESULT CDMOObject::GetInputType(
DWORD dwInputStreamIndex,
DWORD dwTypeIndex, // 0-based
DMO_MEDIA_TYPE *pmt
)
{
g_IShell->Log(1, " Testing GetInputType");
CALL_WITH_HANDLER(GetInputType, (dwInputStreamIndex, dwTypeIndex, pmt));
return hr;
}
HRESULT CDMOObject::GetOutputType(
DWORD dwOutputStreamIndex,
DWORD dwTypeIndex, // 0-based
DMO_MEDIA_TYPE *pmt
)
{
g_IShell->Log(1, " Testing GetOutputType");
CALL_WITH_HANDLER(GetOutputType, (dwOutputStreamIndex, dwTypeIndex, pmt));
return hr;
}
//
// SetType - tell the object the type of data it will work with.
//
HRESULT CDMOObject::SetInputType(
DWORD dwInputStreamIndex,
const DMO_MEDIA_TYPE *pmt,
DWORD dwFlags // test only
)
{
g_IShell->Log(1, " Testing SetInputType");
CALL_WITH_HANDLER(SetInputType, (dwInputStreamIndex, pmt, dwFlags));
return hr;
}
HRESULT CDMOObject::SetOutputType(
DWORD dwOutputStreamIndex,
const DMO_MEDIA_TYPE *pmt,
DWORD dwFlags // test only
)
{
g_IShell->Log(1, " Testing SetOutputType");
CALL_WITH_HANDLER(SetOutputType, (dwOutputStreamIndex, pmt, dwFlags));
return hr;
}
//
// GetCurrentType - get the last mediatype supplied via SetType.
// Returns S_FALSE if SetType has not been called.
//
HRESULT CDMOObject::GetInputCurrentType(
DWORD dwInputStreamIndex,
DMO_MEDIA_TYPE *pmt
)
{
g_IShell->Log(1, " Testing GetInputCurrentType");
CALL_WITH_HANDLER(GetInputCurrentType, (dwInputStreamIndex, pmt));
return hr;
}
HRESULT CDMOObject::GetOutputCurrentType(
DWORD dwOutputStreamIndex,
DMO_MEDIA_TYPE *pmt
)
{
g_IShell->Log(1, " Testing GetOutputCurrentType");
CALL_WITH_HANDLER(GetOutputCurrentType, (dwOutputStreamIndex, pmt));
return hr;
}
//
// GetSizeInfo - Get buffer size requirementes of a stream.
//
// If buffer size depends on the media type used, the object should
// base its response on the most recent media type set for this stream.
// If no mediatype has been set, the object may return an error.
//
HRESULT CDMOObject::GetInputSizeInfo(
DWORD dwInputStreamIndex,
DWORD *pcbSize, // size of input 'quantum'
DWORD *pcbMaxLookahead, // max total bytes held
DWORD *pcbAlignment // buffer alignment requirement
)
{
g_IShell->Log(1, " Testing GetInputSizeInfo");
CALL_WITH_HANDLER(GetInputSizeInfo, (dwInputStreamIndex, pcbSize,pcbMaxLookahead, pcbAlignment));
return hr;
}
HRESULT CDMOObject::GetOutputSizeInfo(
DWORD dwOutputStreamIndex,
DWORD *pcbSize, // size of output 'quantum'
DWORD *pcbAlignment // buffer alignment requirement
)
{
if(m_processCounter%interval == 0)
g_IShell->Log(1, " Testing GetOutputSizeInfo");
CALL_WITH_HANDLER(GetOutputSizeInfo, (dwOutputStreamIndex, pcbSize, pcbAlignment))
return hr;
}
HRESULT CDMOObject::GetInputMaxLatency(
DWORD dwInputStreamIndex,
REFERENCE_TIME *prtMaxLatency
)
{
g_IShell->Log(1, " Testing GetInputMaxLatency");
CALL_WITH_HANDLER(GetInputMaxLatency, (dwInputStreamIndex, prtMaxLatency))
return hr;
}
HRESULT CDMOObject::SetInputMaxLatency(
DWORD dwInputStreamIndex,
REFERENCE_TIME rtMaxLatency
)
{
g_IShell->Log(1, " Testing SetInputMaxLatency");
CALL_WITH_HANDLER(SetInputMaxLatency, (dwInputStreamIndex, rtMaxLatency))
return hr;
}
//
// Flush() - discard any buffered data.
//
HRESULT CDMOObject::Flush()
{
g_IShell->Log(1, " Testing Flush" );
CALL_WITH_HANDLER(Flush, ());
if (m_pInputBuffers) {
Error(ERROR_TYPE_DMO, E_FAIL,
TEXT("Holding on to buffers after Flush()"));
g_IShell->Log(1, "DMO ERROR: Holding on to buffers after Flush()");
}
return hr;
}
//
// Send a discontinuity to an input stream. The object will not
// accept any more data on this input stream until the discontinuity
// has been completely processed, which may involve multiple
// ProcessOutput() calls.
//
HRESULT CDMOObject::Discontinuity(DWORD dwInputStreamIndex)
{
g_IShell->Log(1, " Testing Discontinuity ");
CALL_WITH_HANDLER(Discontinuity, (dwInputStreamIndex))
return hr;
}
//
// If a streaming object needs to perform any time consuming
// initialization before it can stream data, it should do it inside
// AllocateStreamingResources() rather than during the first process
// call.
//
// This method is NOT guaranteed to be called before streaming
// starts. If it is not called, the object should perform any
// required initialization during a process call.
//
HRESULT CDMOObject::AllocateStreamingResources()
{
g_IShell->Log(1, " Testing AllocateStreamingResources()");
CALL_WITH_HANDLER(AllocateStreamingResources, ())
return hr;
}
// Free anything allocated in AllocateStreamingResources().
HRESULT CDMOObject::FreeStreamingResources()
{
g_IShell->Log(1, " Testing FreeStreamingResources()");
CALL_WITH_HANDLER(FreeStreamingResources, ())
return hr;
}
// GetInputStatus - the only flag defined right now is ACCEPT_DATA.
HRESULT CDMOObject::GetInputStatus(
DWORD dwInputStreamIndex,
DWORD *dwFlags // ACCEPT_DATA
)
{
g_IShell->Log(1, " Testing GetInputStatus");
CALL_WITH_HANDLER(GetInputStatus, (dwInputStreamIndex, dwFlags));
return hr;
}
//
// Pass one new buffer to an input stream
//
HRESULT CDMOObject::ProcessInput(
DWORD dwInputStreamIndex,
IMediaBuffer *pBuffer, // must not be NULL
DWORD dwFlags, // DMO_INPUT_DATA_BUFFERF_XXX (syncpoint, etc.)
REFERENCE_TIME rtTimestamp, // valid if flag set
REFERENCE_TIME rtTimelength // valid if flag set
)
{
IMediaBuffer* pBufferUsed = pBuffer;
#ifdef ENABLE_SECURE_DMO
// Encrypt the buffer pointer if this is a secure DMO,
if( m_pTestAppSecureChannel ) { // NULL != m_pTestAppSecureChannel
IMediaBuffer* pEncryptedBuffer = pBuffer;
//g_IShell->Log(1, "Encrypt the buffer pointer if this is a secure DMO");
CALL_OBJECT_WITH_HANDLER( m_pTestAppSecureChannel, WMSC_Encrypt, ((BYTE*)&pEncryptedBuffer, sizeof(IMediaBuffer*)) );
if( FAILED( hr ) ) {
return hr;
}
pBufferUsed = pEncryptedBuffer;
}
#endif // ENABLE_SECURE_DMO
if(m_processCounter%interval == 0)
g_IShell->Log(1, " Testing ProcessInput().");
CALL_WITH_HANDLER(ProcessInput, (dwInputStreamIndex, pBufferUsed, dwFlags,
rtTimestamp, rtTimelength))
/* HRESULT hr;
hr = m_pMediaObject->ProcessInput(dwInputStreamIndex, pBufferUsed, dwFlags,
rtTimestamp, rtTimelength);*/
return hr;
}
//
// ProcessOutput() - generate output for current input buffers
//
// Output stream specific status information is returned in the
// dwStatus member of each buffer wrapper structure.
//
HRESULT CDMOObject::ProcessOutput(
DWORD dwReserved, // must be 0
DWORD cOutputBufferCount, // # returned by GetStreamCount()
DMO_OUTPUT_DATA_BUFFER *pOutputBuffers, // one per stream
DWORD *pdwStatus // TBD, must be set to 0
)
{
if(m_processCounter%interval == 0)
g_IShell->Log(1, " Testing ProcessOutput().");
DMO_OUTPUT_DATA_BUFFER* pOutputBuffersUsed = pOutputBuffers;
#ifdef ENABLE_SECURE_DMO
// Encrypt the buffer pointer if this is a secure DMO,
if( m_pTestAppSecureChannel ) { // NULL != m_pTestAppSecureChannel
DMO_OUTPUT_DATA_BUFFER* pEncryptedOutputBuffers = pOutputBuffers;
CALL_OBJECT_WITH_HANDLER( m_pTestAppSecureChannel, WMSC_Encrypt, ((BYTE*)&pEncryptedOutputBuffers, sizeof(DMO_OUTPUT_DATA_BUFFER*)) );
if( FAILED( hr ) ) {
return hr;
}
pOutputBuffersUsed = pEncryptedOutputBuffers;
}
#endif // ENABLE_SECURE_DMO
CALL_WITH_HANDLER(ProcessOutput, (dwReserved, cOutputBufferCount, pOutputBuffersUsed, pdwStatus))
if (SUCCEEDED(hr)) {
DWORD dwInputStreams = 0, dwOutputStreams = 0;
// Check outputs
BOOL bMore = FALSE;
for (DWORD dwIndex = 0; dwIndex < cOutputBufferCount; dwIndex++) {
if (pOutputBuffers[dwIndex].dwStatus &
DMO_OUTPUT_DATA_BUFFERF_INCOMPLETE) {
bMore = TRUE;
}
}
if (bMore && S_FALSE == hr) {
Error(ERROR_TYPE_DMO, E_FAIL,
TEXT("ProcessOutput returned S_FALSE but the output stream has DMO_OUTPUT_DATA_BUFFERF_INCOMPLETE flag."));
g_IShell->Log(1, "DMO ERROR: ProcessOutput returned S_FALSE but stream incomplete");
}
if (!bMore && !HoldsOnToBuffers()) {
// Check if we hold on to buffers
if (m_pInputBuffers) {
Error(ERROR_TYPE_DMO, E_FAIL,
TEXT("Holding on to buffers after ProcessOutput"));
g_IShell->Log(1, "DMO ERROR: Holding on to buffers after ProcessOutput");
}
}
}
return hr;
}
HRESULT CDMOObject::CreateInputBuffer(
DWORD dwSize,
CMediaBuffer **ppBuffer)
{
HRESULT hr = CreateBuffer(dwSize, ppBuffer, this);
if (SUCCEEDED(hr)) {
// Add it to the tail
CMediaBuffer **pSearch = &m_pInputBuffers;
while (*pSearch) {
pSearch = &(*pSearch)->m_pNext;
}
*pSearch = *ppBuffer;
}
return hr;
}
BOOL CDMOObject::HoldsOnToBuffers()
{
BOOL bHoldsOnToBuffers = FALSE;
DWORD cInputStreams = 0, cOutputStreams = 0;
GetStreamCount(&cInputStreams, &cOutputStreams);
for (DWORD dwIndex = 0; dwIndex < cInputStreams; dwIndex++) {
DWORD dwFlags;
GetInputStreamInfo(dwIndex, &dwFlags);
if (dwFlags & DMO_INPUT_STREAMF_HOLDS_BUFFERS) {
bHoldsOnToBuffers = TRUE;
}
}
return bHoldsOnToBuffers;
}
HRESULT CDMOObject::SetDefaultOutputTypes() {
HRESULT hrGet;
HRESULT hrSet;
DMO_MEDIA_TYPE mt;
DWORD dwOutputTypeIndex;
DWORD cInputStreams = 0, cOutputStreams = 0;
GetStreamCount(&cInputStreams, &cOutputStreams);
// try on each preferred type of the each of output stream.
for(DWORD dwStreamIndex = 0; dwStreamIndex < cOutputStreams; dwStreamIndex++)
{
dwOutputTypeIndex = 0;
DWORD dwFlags = 0;
HRESULT hr = GetOutputStreamInfo(dwStreamIndex, &dwFlags);
if (FAILED(hr)) {
//Error(ERROR_TYPE_DMO, hr, TEXT("Failed to get output size info"));
g_IShell->Log(1, "DMO ERROR, Failed in GetOutputStreamInfo() for stream %d. hr=%#08x",dwStreamIndex, hr);
break;
}
do {
hrGet = GetOutputType( dwStreamIndex, dwOutputTypeIndex, &mt );
if( FAILED( hrGet ) && (DMO_E_NO_MORE_ITEMS != hrGet) ) {
return hrGet;
}
wchar_t tempStr[50];
TCHAR tempStr2[50];
int r = ::StringFromGUID2(mt.formattype, tempStr, 50);
wcstombs(tempStr2, tempStr,50);
::CoTaskMemFree( tempStr );
if( mt.formattype == FORMAT_VideoInfo)
{
// g_IShell->Log(1, "formattype = FORMAT_VideoInfo");
// g_IShell->Log(1, "before set: dwBitRate = %d", ((VIDEOINFOHEADER *)(mt.pbFormat))->dwBitRate);
((VIDEOINFOHEADER *)(mt.pbFormat))->dwBitRate = 18140; //18140 22998 25913
// g_IShell->Log(1, "after set: dwBitRate = %d", ((VIDEOINFOHEADER *)(mt.pbFormat))->dwBitRate);
}
if( S_OK == hrGet ) {
hrSet = SetOutputType( dwStreamIndex, &mt, 0 );
MoFreeMediaType(&mt);
dwOutputTypeIndex++;
}
} while( (hrGet != DMO_E_NO_MORE_ITEMS) && FAILED( hrSet ) );
if( FAILED( hrSet ) ) {
//Error( ERROR_TYPE_DMO, TEXT("ERROR: Stream 0's inplementation of IMediaObject::SetOutputType() will not accept any of the media types returned by IMediaObject::GetOutputType().") );
g_IShell->Log(1, "DMO ERROR: Stream %d's implementation of IMediaObject::SetOutputType() will not accept any of the media types returned by IMediaObject::GetOutputType().",dwStreamIndex);
return E_FAIL;
}
}
return S_OK;
}
#ifdef ENABLE_SECURE_DMO
HRESULT CDMOObject::CreateSecureChannel( IMediaObject* pMediaObject, IUnknown** ppCertUnknown, IWMSecureChannel** ppSecureChannel )
{
// Acknoledgements: This code is based on the CMediaWrapperFilter::SetupSecureChannel() function
// which is located in the Direct Show DMO Wrapper Filter.
// Make sure the caller does not use random data.
*ppCertUnknown = NULL;
*ppSecureChannel = NULL;
#ifdef _X86
// next check whether this is a secure dmo
CComPtr<IWMGetSecureChannel> pGetSecureChannel;
HRESULT hr = pMediaObject->QueryInterface( IID_IWMGetSecureChannel, (void**)&pGetSecureChannel );
if( SUCCEEDED( hr ) )
{
CComPtr<IUnknown> pCertUnknown;
hr = WMCreateCertificate( &pCertUnknown );
if( SUCCEEDED( hr ) )
{
// pass app certification to dmo through secure channel
CComPtr<IWMSecureChannel> pCodecSecureChannel;
hr = pGetSecureChannel->GetPeerSecureChannelInterface( &pCodecSecureChannel );
if ( SUCCEEDED( hr ) )
{
CComPtr<IWMSecureChannel> pSecureChannel;
// setup a secure channel on our side (the dmo wrapper side)
hr = WMCreateSecureChannel( &pSecureChannel );
if( SUCCEEDED( hr ) )
{
CComPtr<IWMAuthorizer> pAuthorizer;
hr = pCertUnknown->QueryInterface( IID_IWMAuthorizer, (void**)&pAuthorizer );
if( SUCCEEDED( hr ) )
{
// pass the channel the app certificate
hr = pSecureChannel->WMSC_AddCertificate( pAuthorizer );
if( SUCCEEDED( hr ) )
{
// connect the dmo wrapper's secure channel to the codec's
hr = pSecureChannel->WMSC_Connect( pCodecSecureChannel );
if( SUCCEEDED( hr ) )
{
*ppCertUnknown = pCertUnknown;
*ppSecureChannel = pSecureChannel;
(*ppCertUnknown)->AddRef();
(*ppSecureChannel)->AddRef();
}
}
}
}
}
}
}
else
{
// this dmo's not secure so just return success and continue on
hr = S_FALSE;
}
return hr;
#else // !_X86
// wmsdk not supported on non-x86 and WIN64 platforms, for those just return success
return S_FALSE;
#endif // !_X86
}
#endif // ENABLE_SECURE_DMO
// End shadow IMediaObject
HRESULT CreateBuffer(
DWORD cbMaxLength,
CMediaBuffer **ppBuffer,
CDMOObject *pObject)
{
CMediaBuffer *pBuffer = new CMediaBuffer(cbMaxLength, pObject);
if (pBuffer == NULL || FAILED(pBuffer->Init())) {
delete pBuffer;
*ppBuffer = NULL;
Error(ERROR_TYPE_TEST, E_OUTOFMEMORY, TEXT("Could not allocate buffer size %d"), cbMaxLength);
g_IShell->Log(1, "TEST ERROR, Could not allocate buffer size %d", cbMaxLength);
return E_OUTOFMEMORY;
}
*ppBuffer = pBuffer;
(*ppBuffer)->AddRef();
return S_OK;
}