1646 lines
44 KiB
C++
1646 lines
44 KiB
C++
//*****************************************************************************
|
|
//
|
|
// Microsoft Windows Media
|
|
// Copyright (C) Microsoft Corporation. All rights reserved.
|
|
//
|
|
// FileName: Append.cpp
|
|
//
|
|
// Abstract: Implementation of CAppend class
|
|
//
|
|
//*****************************************************************************
|
|
|
|
#include "stdafx.h"
|
|
#include <assert.h>
|
|
#include "Append.h"
|
|
#include "StreamData.h"
|
|
|
|
// User-defined attribute
|
|
static const WCHAR * pwszUserAttribute1 = L"_MyAttribute";
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Name: CAppend::CAppend()
|
|
// Desc: Constructor.
|
|
//------------------------------------------------------------------------------
|
|
CAppend::CAppend()
|
|
{
|
|
m_hrAsync = S_OK;
|
|
m_hAsyncEvent = NULL;
|
|
m_pReader1 = NULL;
|
|
m_pReader2 = NULL;
|
|
m_pReaderAdv1 = NULL;
|
|
m_pReaderAdv2 = NULL;
|
|
m_pWriter = NULL;
|
|
m_pWriterAdv = NULL;
|
|
m_pFirstProfile = NULL;
|
|
m_pwszOutFile = NULL;
|
|
m_pRdrHdrInfo1 = NULL;
|
|
m_pRdrHdrInfo2 = NULL;
|
|
m_pwStreamNumMap = NULL;
|
|
m_pSecondProfile = NULL;
|
|
m_bEOF = FALSE;
|
|
m_qwFirstTime = 0;
|
|
m_qwSecondTime = 0;
|
|
m_nCurrentFile = 0;
|
|
m_dwStreamCount = 0;
|
|
m_cRef = 1;
|
|
//
|
|
// The variable WORD* m_pwStreamNumMap contains the mapping of stream numbers from the
|
|
// second file to the output file. It contains 2 * m_dwStreamCount number of WORDs.
|
|
// The first m_dwStreamCount number of WORDs contains the output file stream numbers,
|
|
// which are in the same order as the stream numbers of first input file.
|
|
// The second m_dwStreamCount number of WORDs contains the stream numbers from the
|
|
// second file in the same order as the first half. The corresponding streams in the
|
|
// first half and the second half are always of the same type.
|
|
//
|
|
InitializeCriticalSection( &m_crisecFile );
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Name: CAppend::~CAppend()
|
|
// Desc: Destructor.
|
|
//------------------------------------------------------------------------------
|
|
CAppend::~CAppend()
|
|
{
|
|
Exit();
|
|
|
|
DeleteCriticalSection( &m_crisecFile );
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Name: CAppend::OnSample()
|
|
// Desc: Implementation of IWMReaderCallback::OnSample.
|
|
//------------------------------------------------------------------------------
|
|
HRESULT CAppend::OnSample( /* [in] */ DWORD dwOutputNum,
|
|
/* [in] */ QWORD qwSampleTime,
|
|
/* [in] */ QWORD qwSampleDuration,
|
|
/* [in] */ DWORD dwFlags,
|
|
/* [in] */ INSSBuffer __RPC_FAR * pSample,
|
|
/* [in] */ void __RPC_FAR * pvContext )
|
|
{
|
|
if( m_hAsyncEvent != NULL )
|
|
{
|
|
//
|
|
// The samples are expected in OnStreamSample
|
|
//
|
|
_tprintf( _T( "Error: Received a decompressed sample from the reader.\n" ) );
|
|
|
|
m_hrAsync = E_UNEXPECTED;
|
|
SetEvent( m_hAsyncEvent );
|
|
}
|
|
|
|
return( S_OK );
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Name: CAppend::OnStreamSample()
|
|
// Desc: Implementation of IWMReaderCallbackAdvanced::OnStreamSample.
|
|
//------------------------------------------------------------------------------
|
|
HRESULT CAppend::OnStreamSample( /* [in] */ WORD wStreamNum,
|
|
/* [in] */ QWORD cnsSampleTime,
|
|
/* [in] */ QWORD cnsSampleDuration,
|
|
/* [in] */ DWORD dwFlags,
|
|
/* [in] */ INSSBuffer __RPC_FAR * pSample,
|
|
/* [in] */ void __RPC_FAR * pvContext )
|
|
{
|
|
short nFileNum = 0;
|
|
HRESULT hr = S_OK;
|
|
|
|
EnterCriticalSection( &m_crisecFile );
|
|
|
|
nFileNum = m_nCurrentFile;
|
|
|
|
LeaveCriticalSection( &m_crisecFile );
|
|
|
|
TCHAR tszFlags[ 200 ];
|
|
|
|
(void)StringCchCopy( tszFlags, ARRAYSIZE(tszFlags), _T("") );
|
|
|
|
if( dwFlags & WM_SF_CLEANPOINT )
|
|
{
|
|
(void)StringCchCat( tszFlags, ARRAYSIZE(tszFlags), _T(" WM_SF_CLEANPOINT ") );
|
|
}
|
|
if( dwFlags & WM_SF_DISCONTINUITY )
|
|
{
|
|
(void)StringCchCat( tszFlags, ARRAYSIZE(tszFlags), _T(" WM_SF_DISCONTINUITY ") );
|
|
}
|
|
if( dwFlags & WM_SF_DATALOSS )
|
|
{
|
|
(void)StringCchCat( tszFlags, ARRAYSIZE(tszFlags), _T(" WM_SF_DATALOSS ") );
|
|
}
|
|
|
|
//
|
|
// We've got a stream. Let's write it in the output file
|
|
//
|
|
if( nFileNum == 1 )
|
|
{
|
|
_tprintf ( _T( "FirstFile StreamSample: num=%d, time=%I64u, duration=%I64u, flags=%s.\n" ),
|
|
wStreamNum, cnsSampleTime, cnsSampleDuration, tszFlags );
|
|
|
|
hr = m_pWriterAdv->WriteStreamSample( wStreamNum,
|
|
cnsSampleTime,
|
|
0,
|
|
cnsSampleDuration,
|
|
dwFlags,
|
|
pSample );
|
|
}
|
|
else if( nFileNum == 2 )
|
|
{
|
|
_tprintf ( _T( "SecondFile StreamSample: num=%d, time=%I64u, duration=%I64u, flags=%s.\n" ),
|
|
wStreamNum, (cnsSampleTime + m_qwFirstTime), cnsSampleDuration, tszFlags );
|
|
|
|
WORD dwNum = MapStreamNum( wStreamNum );
|
|
|
|
if( dwNum )
|
|
{
|
|
hr = m_pWriterAdv->WriteStreamSample( dwNum,
|
|
cnsSampleTime + m_qwFirstTime,
|
|
0,
|
|
cnsSampleDuration,
|
|
dwFlags,
|
|
pSample );
|
|
}
|
|
else
|
|
{
|
|
hr = E_UNEXPECTED;
|
|
}
|
|
}
|
|
|
|
if( FAILED( hr ) )
|
|
{
|
|
_tprintf( _T( "Error in WriteStreamSample (hr=0x%08x).\n" ), hr );
|
|
|
|
m_hrAsync = hr;
|
|
SetEvent( m_hAsyncEvent );
|
|
}
|
|
|
|
return( hr );
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Name: CAppend::OnTime()
|
|
// Desc: Implementation of IWMReaderCallbackAdvanced::OnTime.
|
|
//------------------------------------------------------------------------------
|
|
HRESULT CAppend::OnTime( /* [in] */ QWORD cnsCurrentTime,
|
|
/* [in] */ void __RPC_FAR * pvContext)
|
|
{
|
|
if( m_bEOF || m_nCurrentFile > 2 )
|
|
{
|
|
return( S_OK );
|
|
}
|
|
|
|
short nFileNum = 0;
|
|
HRESULT hr = S_OK;
|
|
|
|
EnterCriticalSection( &m_crisecFile );
|
|
|
|
nFileNum = m_nCurrentFile;
|
|
|
|
LeaveCriticalSection( &m_crisecFile );
|
|
|
|
if( nFileNum == 1 )
|
|
{
|
|
m_qwFirstTime += 1000 * 10000;
|
|
|
|
hr = m_pReaderAdv1->DeliverTime( m_qwFirstTime );
|
|
}
|
|
else if( nFileNum == 2 )
|
|
{
|
|
m_qwSecondTime += 1000 * 10000;
|
|
|
|
hr = m_pReaderAdv2->DeliverTime( m_qwSecondTime );
|
|
}
|
|
|
|
if( FAILED( hr ) )
|
|
{
|
|
_tprintf( _T( "Error in DeliverTime (hr=0x%08x).\n" ), hr );
|
|
}
|
|
|
|
return( S_OK );
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Name: CAppend::OnStatus()
|
|
// Desc: Implementation of IWMStatusCallback::OnStatus.
|
|
//------------------------------------------------------------------------------
|
|
HRESULT CAppend::OnStatus( /* [in] */ WMT_STATUS Status,
|
|
/* [in] */ HRESULT hr,
|
|
/* [in] */ WMT_ATTR_DATATYPE dwType,
|
|
/* [in] */ BYTE __RPC_FAR * pValue,
|
|
/* [in] */ void __RPC_FAR * pvContext )
|
|
{
|
|
short nFileNum = 0;
|
|
|
|
switch(Status)
|
|
{
|
|
|
|
case WMT_OPENED:
|
|
|
|
_tprintf( _T( "Status WMT_OPENED detected.\n" ) );
|
|
|
|
EnterCriticalSection( &m_crisecFile );
|
|
|
|
nFileNum = m_nCurrentFile;
|
|
|
|
LeaveCriticalSection( &m_crisecFile );
|
|
//
|
|
// Set the event only for the first two open calls
|
|
//
|
|
if( nFileNum < 3 )
|
|
{
|
|
m_hrAsync = hr;
|
|
SetEvent( m_hAsyncEvent );
|
|
}
|
|
|
|
break;
|
|
|
|
case WMT_STARTED:
|
|
|
|
_tprintf( _T( "Status WMT_STARTED detected.\n" ) );
|
|
|
|
EnterCriticalSection( &m_crisecFile );
|
|
|
|
nFileNum = ++m_nCurrentFile;
|
|
|
|
LeaveCriticalSection( &m_crisecFile );
|
|
//
|
|
// Ask for the specific duration of the stream to be delivered
|
|
//
|
|
if( nFileNum == 1 )
|
|
{
|
|
m_qwFirstTime = 0;
|
|
m_qwFirstTime += 1000 * 10000;
|
|
hr = m_pReaderAdv1->DeliverTime( m_qwFirstTime );
|
|
}
|
|
else if( nFileNum == 2 )
|
|
{
|
|
m_qwSecondTime = 0;
|
|
m_qwSecondTime += 1000 * 10000;
|
|
hr = m_pReaderAdv2->DeliverTime( m_qwSecondTime );
|
|
}
|
|
|
|
assert( SUCCEEDED( hr ) );
|
|
m_bEOF = FALSE;
|
|
|
|
break;
|
|
|
|
case WMT_STOPPED:
|
|
|
|
_tprintf( _T( "Status WMT_STOPPED detected.\n" ) );
|
|
|
|
EnterCriticalSection( &m_crisecFile );
|
|
|
|
nFileNum = m_nCurrentFile;
|
|
|
|
if( m_nCurrentFile == 2 )
|
|
{
|
|
m_nCurrentFile++;
|
|
}
|
|
|
|
LeaveCriticalSection( &m_crisecFile );
|
|
|
|
m_hrAsync = S_OK;
|
|
SetEvent( m_hAsyncEvent );
|
|
|
|
break;
|
|
|
|
case WMT_ERROR:
|
|
case WMT_EOF:
|
|
|
|
_tprintf( _T( "Status WMT_EOF detected.\n" ) );
|
|
|
|
if( m_bEOF )
|
|
{
|
|
break;
|
|
}
|
|
m_bEOF = TRUE;
|
|
|
|
EnterCriticalSection( &m_crisecFile );
|
|
|
|
nFileNum = m_nCurrentFile;
|
|
|
|
LeaveCriticalSection( &m_crisecFile );
|
|
//
|
|
// Set the event only for the first two EOFs
|
|
//
|
|
if( nFileNum < 3 )
|
|
{
|
|
m_hrAsync = hr;
|
|
SetEvent( m_hAsyncEvent );
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
case WMT_CLOSED:
|
|
|
|
_tprintf( _T( "Status WMT_CLOSED detected.\n" ) );
|
|
|
|
m_hrAsync = hr;
|
|
SetEvent( m_hAsyncEvent);
|
|
|
|
break;
|
|
}
|
|
|
|
return( S_OK );
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Name: CAppend::Init()
|
|
// Desc: Creates an event, initializes COM, and creates readers.
|
|
//------------------------------------------------------------------------------
|
|
HRESULT CAppend::Init()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
do
|
|
{
|
|
//
|
|
// Create an event for handling asynchronous calls
|
|
//
|
|
m_hAsyncEvent = CreateEvent( NULL, FALSE, FALSE, WMVAPPEND_ASYNC_EVENT );
|
|
if( NULL == m_hAsyncEvent )
|
|
{
|
|
_tprintf( _T( "Could not create asynchronous event.\n" ) );
|
|
hr = E_FAIL;
|
|
break;
|
|
}
|
|
|
|
hr = CoInitialize( NULL );
|
|
if( FAILED( hr ) )
|
|
{
|
|
_tprintf( _T( "CoInitialize failed (hr=0x%08x).\n" ), hr );
|
|
break;
|
|
}
|
|
//
|
|
// Create two readers and two readeradvanced
|
|
//
|
|
hr = WMCreateReader( NULL, 0, &m_pReader1 );
|
|
if( FAILED( hr ) )
|
|
{
|
|
_tprintf( _T( "Could not create first reader (hr=0x%08x).\n" ), hr );
|
|
break;
|
|
}
|
|
|
|
hr = WMCreateReader( NULL, 0, &m_pReader2 );
|
|
if( FAILED( hr ) )
|
|
{
|
|
_tprintf( _T( "Could not create second reader (hr=0x%08x).\n" ), hr );
|
|
break;
|
|
}
|
|
|
|
hr = m_pReader1->QueryInterface( IID_IWMReaderAdvanced, ( VOID ** )&m_pReaderAdv1 );
|
|
if( FAILED( hr ) )
|
|
{
|
|
_tprintf( _T( "Could not QI for first IWMReaderAdvanced (hr=0x%08x).\n" ), hr );
|
|
break;
|
|
}
|
|
|
|
hr = m_pReader2->QueryInterface( IID_IWMReaderAdvanced, ( VOID ** )&m_pReaderAdv2 );
|
|
if( FAILED( hr ) )
|
|
{
|
|
_tprintf( _T( "Could not QI for second IWMReaderAdvanced (hr=0x%08x).\n" ), hr );
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Create a writer and a writeradvanced
|
|
//
|
|
hr = WMCreateWriter( NULL, &m_pWriter );
|
|
if( FAILED( hr ) )
|
|
{
|
|
_tprintf( _T( "Could not create writer (hr=0x%08x).\n" ), hr );
|
|
break;
|
|
}
|
|
|
|
hr = m_pWriter->QueryInterface( IID_IWMWriterAdvanced, ( VOID ** )&m_pWriterAdv );
|
|
if( FAILED( hr ) )
|
|
{
|
|
_tprintf( _T( "Could not QI for IWMWriterAdvanced (hr=0x%08x).\n" ), hr );
|
|
}
|
|
|
|
}while( FALSE );
|
|
|
|
return( hr );
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Name: CAppend::Exit()
|
|
// Desc: Closes readers and cleans up.
|
|
//------------------------------------------------------------------------------
|
|
HRESULT CAppend::Exit()
|
|
{
|
|
SAFE_ARRAYDELETE( m_pwszOutFile );
|
|
SAFE_ARRAYDELETE( m_pwStreamNumMap );
|
|
|
|
if( m_pReader1 )
|
|
{
|
|
m_pReader1->Close();
|
|
}
|
|
|
|
if( m_pReader2 )
|
|
{
|
|
m_pReader2->Close();
|
|
}
|
|
|
|
SAFE_RELEASE( m_pFirstProfile );
|
|
SAFE_RELEASE( m_pSecondProfile );
|
|
SAFE_RELEASE( m_pRdrHdrInfo1 );
|
|
SAFE_RELEASE( m_pRdrHdrInfo2 );
|
|
SAFE_RELEASE( m_pWriterAdv );
|
|
SAFE_RELEASE( m_pWriter );
|
|
SAFE_RELEASE( m_pReaderAdv2 );
|
|
SAFE_RELEASE( m_pReaderAdv1 );
|
|
SAFE_RELEASE( m_pReader2 );
|
|
SAFE_RELEASE( m_pReader1 );
|
|
|
|
CoUninitialize();
|
|
|
|
SAFE_CLOSEHANDLE( m_hAsyncEvent )
|
|
|
|
return( S_OK );
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Name: CAppend::CompareProfiles()
|
|
// Desc: Ascertains whether two files contain the same stream numbers and
|
|
// media types.
|
|
//------------------------------------------------------------------------------
|
|
HRESULT CAppend::CompareProfiles(__in LPWSTR pwszFirstInfile, __in LPWSTR pwszSecondInfile, BOOL* pIsEqual)
|
|
{
|
|
if( NULL == m_pReader1 || NULL == m_pReader2 )
|
|
{
|
|
return( E_INVALIDARG );
|
|
}
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
DWORD dwFirstStreams = 0;
|
|
DWORD dwSecondStreams = 0;
|
|
|
|
*pIsEqual = FALSE;
|
|
|
|
do
|
|
{
|
|
//
|
|
// Check if any of the files are protected.
|
|
//
|
|
hr = IsFileProtected( pwszFirstInfile );
|
|
if( FAILED ( hr ) )
|
|
{
|
|
_tprintf( _T( "First file is protected, cannot open, aborting...\n" ) );
|
|
break;
|
|
}
|
|
|
|
hr = IsFileProtected( pwszSecondInfile );
|
|
if( FAILED ( hr ) )
|
|
{
|
|
_tprintf( _T( "Second file is protected, cannot open, aborting...\n" ) );
|
|
break;
|
|
}
|
|
//
|
|
// Open both the files
|
|
//
|
|
hr = m_pReader1->Open( pwszFirstInfile, this, NULL );
|
|
if( FAILED ( hr ) )
|
|
{
|
|
_tprintf( _T( "Could not open first input file %ws (hr=0x%08x).\n" ), pwszFirstInfile, hr );
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Wait for the open to finish
|
|
//
|
|
WaitForSingleObject( m_hAsyncEvent, INFINITE );
|
|
if( FAILED( m_hrAsync ) )
|
|
{
|
|
hr = m_hrAsync;
|
|
_tprintf( _T( "Open failed for first file %ws (hr=0x%08x).\n"), pwszFirstInfile, m_hrAsync );
|
|
break;
|
|
}
|
|
//
|
|
// Same thing for second file
|
|
//
|
|
hr = m_pReader2->Open( pwszSecondInfile, this, NULL );
|
|
if( FAILED ( hr ) )
|
|
{
|
|
_tprintf( _T( "Could not open second input file %ws (hr=0x%08x).\n" ), pwszSecondInfile, hr );
|
|
break;
|
|
}
|
|
|
|
WaitForSingleObject( m_hAsyncEvent, INFINITE );
|
|
if( FAILED( m_hrAsync ) )
|
|
{
|
|
hr = m_hrAsync;
|
|
_tprintf( _T( "Open failed for second file %ws (hr=0x%08x).\n"), pwszSecondInfile, m_hrAsync );
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Get the profile interfaces
|
|
//
|
|
hr = m_pReader1->QueryInterface( IID_IWMProfile, ( VOID ** )&m_pFirstProfile );
|
|
if( FAILED( hr ) )
|
|
{
|
|
_tprintf( _T( "Could not QI for IWMProfile of first file (hr=0x%08x).\n" ), hr );
|
|
break;
|
|
}
|
|
|
|
hr = m_pReader2->QueryInterface( IID_IWMProfile, ( VOID ** )&m_pSecondProfile );
|
|
if( FAILED( hr ) )
|
|
{
|
|
_tprintf( _T( "Could not QI for IWMProfile of second file (hr=0x%08x).\n" ), hr );
|
|
break;
|
|
}
|
|
|
|
hr = m_pFirstProfile->GetStreamCount( &dwFirstStreams );
|
|
if( FAILED( hr ) )
|
|
{
|
|
_tprintf( _T( "GetStreamCount on IWMProfile failed for first file (hr=0x%08x).\n" ), hr );
|
|
break;
|
|
}
|
|
|
|
hr = m_pSecondProfile->GetStreamCount( &dwSecondStreams );
|
|
if( FAILED( hr ) )
|
|
{
|
|
_tprintf( _T( "GetStreamCount on IWMProfile failed for second file (hr=0x%08x).\n" ), hr );
|
|
break;
|
|
}
|
|
|
|
if( dwFirstStreams != dwSecondStreams )
|
|
{
|
|
_tprintf( _T( "Different Stream counts in both the files.\n" ) );
|
|
break;
|
|
}
|
|
|
|
m_dwStreamCount = dwFirstStreams;
|
|
//
|
|
// Create objects of CStreamData for each input file.
|
|
//
|
|
CStreamData streamdata1( dwFirstStreams );
|
|
CStreamData streamdata2( dwSecondStreams );
|
|
//
|
|
// Set all stream-related data from the profile
|
|
//
|
|
hr = streamdata1.SetAllStreamData( m_pFirstProfile );
|
|
if( FAILED( hr ) )
|
|
{
|
|
break;
|
|
}
|
|
|
|
hr = streamdata2.SetAllStreamData( m_pSecondProfile );
|
|
if( FAILED( hr ) )
|
|
{
|
|
break;
|
|
}
|
|
//
|
|
// Map the stream numbers of both the profiles
|
|
//
|
|
*pIsEqual = streamdata1.MapStreamNums( streamdata2, &m_pwStreamNumMap );
|
|
|
|
//
|
|
// This function assumes that streamdata1.m_ptrStreamBufferWindow[]
|
|
// already contains the sum of input BufferWindow values for each output stream
|
|
// which was calculated by the MapStreamNums function call
|
|
//
|
|
hr = streamdata1.SetAllStreamsBufferWindow( m_pFirstProfile );
|
|
if( FAILED( hr ) )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
while( FALSE );
|
|
|
|
return( hr );
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Name: CAppend::Configure()
|
|
// Desc: Configures the output.
|
|
//------------------------------------------------------------------------------
|
|
HRESULT CAppend::Configure( __in LPWSTR pwszOutFile )
|
|
{
|
|
|
|
if( NULL == m_pFirstProfile || NULL == m_pSecondProfile )
|
|
{
|
|
return( E_INVALIDARG );
|
|
}
|
|
|
|
IWMHeaderInfo* pWHeaderInfo = NULL;
|
|
HRESULT hr = S_OK;
|
|
DWORD cInputs = 0;
|
|
|
|
do
|
|
{
|
|
//
|
|
// Turn on manual stream selections, so we get all streams.
|
|
//
|
|
hr = m_pReaderAdv1->SetManualStreamSelection( TRUE );
|
|
if( FAILED( hr ) )
|
|
{
|
|
_tprintf( _T( "Failed to set manual stream selection for first file (hr=0x%08x).\n" ), hr );
|
|
break;
|
|
}
|
|
|
|
hr = m_pReaderAdv2->SetManualStreamSelection( TRUE );
|
|
if( FAILED( hr ) )
|
|
{
|
|
_tprintf( _T( "Failed to set manual stream selection for second file (hr=0x%08x).\n" ), hr );
|
|
break;
|
|
}
|
|
|
|
UINT i;
|
|
for( i = 0; i < m_dwStreamCount; i++ )
|
|
{
|
|
//
|
|
// Receive all the streams as compressed streams
|
|
//
|
|
hr = SetReceiveStreamSample( m_pReaderAdv1, m_pFirstProfile, i );
|
|
if( FAILED( hr ) )
|
|
{
|
|
break;
|
|
}
|
|
//
|
|
// Same thing for the second file
|
|
//
|
|
hr = SetReceiveStreamSample( m_pReaderAdv2, m_pSecondProfile, i );
|
|
if( FAILED( hr ) )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( FAILED( hr ) )
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Turn on the user clocks, so we get the streams faster than the real life speed.
|
|
//
|
|
hr = m_pReaderAdv1->SetUserProvidedClock( TRUE );
|
|
if( FAILED( hr ) )
|
|
{
|
|
_tprintf( _T("SetUserProvidedClock failed for first file (hr=0x%08x).\n" ), hr );
|
|
break;
|
|
}
|
|
|
|
hr = m_pReaderAdv2->SetUserProvidedClock( TRUE );
|
|
if( FAILED( hr ) )
|
|
{
|
|
_tprintf( _T("SetUserProvidedClock failed for second file (hr=0x%08x).\n" ), hr );
|
|
break;
|
|
}
|
|
|
|
hr = m_pWriter->SetProfile( m_pFirstProfile );
|
|
if( FAILED( hr ) )
|
|
{
|
|
_tprintf( _T( "Could not set profile on IWMWriter (hr=0x%08x).\n" ), hr );
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Keep a copy of the output file name
|
|
//
|
|
m_pwszOutFile = new WCHAR[ wcslen( pwszOutFile ) + 1 ];
|
|
if( NULL == m_pwszOutFile)
|
|
{
|
|
_tprintf( _T( "Internal Error: Out of memory\n" ) );
|
|
hr = E_OUTOFMEMORY;
|
|
break;
|
|
}
|
|
(void)StringCchCopyW( m_pwszOutFile, wcslen( pwszOutFile ) + 1, pwszOutFile );
|
|
|
|
hr = m_pWriter->SetOutputFilename( m_pwszOutFile );
|
|
if( FAILED( hr ) )
|
|
{
|
|
_tprintf( _T( "Could not set output file %ws on IWMWriter (hr=0x%08x).\n" ), pwszOutFile, hr );
|
|
break;
|
|
}
|
|
|
|
hr = m_pWriter->GetInputCount( &cInputs );
|
|
if( FAILED( hr ) )
|
|
{
|
|
_tprintf( _T( "Could not get input count from IWMWriter (hr=0x%08x).\n" ), hr );
|
|
break;
|
|
}
|
|
|
|
for( i = 0; i < cInputs; i++ )
|
|
{
|
|
//
|
|
// Set the input props to NULL to indicate that we don't need a codec.
|
|
//
|
|
m_pWriter->SetInputProps( i, NULL );
|
|
}
|
|
|
|
//
|
|
// QI for IWMHeaderInfo from readers and writer
|
|
//
|
|
hr = m_pReader1->QueryInterface( IID_IWMHeaderInfo, ( VOID ** )&m_pRdrHdrInfo1 );
|
|
if ( FAILED( hr ) )
|
|
{
|
|
_tprintf( _T( "Could not QI for IWMHeaderInfo from the reader (hr=0x%08x).\n" ), hr );
|
|
break;
|
|
}
|
|
|
|
hr = m_pWriter->QueryInterface( IID_IWMHeaderInfo, ( VOID ** )&pWHeaderInfo );
|
|
if( FAILED( hr ) )
|
|
{
|
|
_tprintf( _T( "Could not QI for IWMHeaderInfo from the writer (hr=0x%08x).\n" ), hr );
|
|
break;
|
|
}
|
|
|
|
hr = m_pReader2->QueryInterface( IID_IWMHeaderInfo, ( VOID ** )&m_pRdrHdrInfo2 );
|
|
if ( FAILED( hr ) )
|
|
{
|
|
_tprintf( _T( "Could not QI for IWMHeaderInfo from the reader (hr=0x%08x).\n" ), hr );
|
|
break;
|
|
}
|
|
|
|
hr = CopyAllAttributes( pWHeaderInfo );
|
|
if( FAILED( hr ) )
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Copy codec info
|
|
//
|
|
hr = CopyCodecInfo( pWHeaderInfo );
|
|
if( FAILED( hr ) )
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Header has been copied. Let's copy the script
|
|
//
|
|
hr = CopyScript( pWHeaderInfo , m_pRdrHdrInfo1, 0);
|
|
if( FAILED( hr ) )
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Get the offset time at which the script from
|
|
// the second file is to be copied to the output file
|
|
//
|
|
WORD wStreamNum = 0;
|
|
WMT_ATTR_DATATYPE type;
|
|
WORD cbLength = sizeof( m_qwFirstTime );
|
|
|
|
hr = m_pRdrHdrInfo1->GetAttributeByName( &wStreamNum, g_wszWMDuration, &type, (BYTE *)&m_qwFirstTime, &cbLength );
|
|
if( FAILED( hr ) )
|
|
{
|
|
_tprintf( _T( "Error in getting Duration attribute for first file (hr=0x%08x).\n" ), hr );
|
|
break;
|
|
}
|
|
|
|
hr = CopyScript( pWHeaderInfo , m_pRdrHdrInfo2, m_qwFirstTime);
|
|
if( FAILED( hr ) )
|
|
{
|
|
break;
|
|
}
|
|
|
|
}
|
|
while( FALSE );
|
|
|
|
SAFE_RELEASE( pWHeaderInfo );
|
|
|
|
return( hr );
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Name: CAppend::CopyAllAttributes()
|
|
// Desc: Copies all the header attributes which can be set, not related to a
|
|
// particular stream. It's illegal to read DRM attributes..
|
|
//------------------------------------------------------------------------------
|
|
HRESULT CAppend::CopyAllAttributes( IWMHeaderInfo * pWriterHeaderInfo )
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
do
|
|
{
|
|
|
|
hr = CopyAttribute( 0, 0, pWriterHeaderInfo, g_wszWMTitle );
|
|
if( FAILED( hr ) )
|
|
{
|
|
break;
|
|
}
|
|
|
|
hr = CopyAttribute( 0, 0, pWriterHeaderInfo, g_wszWMAuthor );
|
|
if( FAILED( hr ) )
|
|
{
|
|
break;
|
|
}
|
|
|
|
hr = CopyAttribute( 0, 0, pWriterHeaderInfo, g_wszWMDescription );
|
|
if( FAILED( hr ) )
|
|
{
|
|
break;
|
|
}
|
|
|
|
hr = CopyAttribute( 0, 0, pWriterHeaderInfo, g_wszWMRating );
|
|
if( FAILED( hr ) )
|
|
{
|
|
break;
|
|
}
|
|
|
|
hr = CopyAttribute( 0, 0, pWriterHeaderInfo, g_wszWMCopyright );
|
|
if( FAILED( hr ) )
|
|
{
|
|
break;
|
|
}
|
|
|
|
hr = CopyAttribute( 0, 0, pWriterHeaderInfo, g_wszWMAlbumTitle );
|
|
if( FAILED( hr ) )
|
|
{
|
|
break;
|
|
}
|
|
|
|
hr = CopyAttribute( 0, 0, pWriterHeaderInfo, g_wszWMTrack );
|
|
if( FAILED( hr ) )
|
|
{
|
|
break;
|
|
}
|
|
|
|
hr = CopyAttribute( 0, 0, pWriterHeaderInfo, g_wszWMPromotionURL );
|
|
if( FAILED( hr ) )
|
|
{
|
|
break;
|
|
}
|
|
|
|
hr = CopyAttribute( 0, 0, pWriterHeaderInfo, g_wszWMAlbumCoverURL );
|
|
if( FAILED( hr ) )
|
|
{
|
|
break;
|
|
}
|
|
|
|
hr = CopyAttribute( 0, 0, pWriterHeaderInfo, g_wszWMGenre );
|
|
if( FAILED( hr ) )
|
|
{
|
|
break;
|
|
}
|
|
|
|
hr = CopyAttribute( 0, 0, pWriterHeaderInfo, g_wszWMYear );
|
|
if( FAILED( hr ) )
|
|
{
|
|
break;
|
|
}
|
|
|
|
hr = CopyAttribute( 0, 0, pWriterHeaderInfo, g_wszWMGenreID );
|
|
if( FAILED( hr ) )
|
|
{
|
|
break;
|
|
}
|
|
|
|
hr = CopyAttribute( 0, 0, pWriterHeaderInfo, g_wszWMMCDI );
|
|
if( FAILED( hr ) )
|
|
{
|
|
break;
|
|
}
|
|
|
|
hr = CopyAttribute( 0, 0, pWriterHeaderInfo, g_wszWMBannerImageType );
|
|
if( FAILED( hr ) )
|
|
{
|
|
break;
|
|
}
|
|
|
|
hr = CopyAttribute( 0, 0, pWriterHeaderInfo, g_wszWMBannerImageData );
|
|
if( FAILED( hr ) )
|
|
{
|
|
break;
|
|
}
|
|
|
|
hr = CopyAttribute( 0, 0, pWriterHeaderInfo, g_wszWMBannerImageURL );
|
|
if( FAILED( hr ) )
|
|
{
|
|
break;
|
|
}
|
|
|
|
hr = CopyAttribute( 0, 0, pWriterHeaderInfo, g_wszWMCopyrightURL );
|
|
if( FAILED( hr ) )
|
|
{
|
|
break;
|
|
}
|
|
|
|
|
|
//
|
|
// Shows how to copy stream-based attribute
|
|
//
|
|
|
|
for( WORD cnt = 0; cnt < m_dwStreamCount; cnt++ )
|
|
{
|
|
|
|
hr = CopyAttribute( m_pwStreamNumMap[ cnt ],
|
|
m_pwStreamNumMap[ cnt + m_dwStreamCount ],
|
|
pWriterHeaderInfo,
|
|
pwszUserAttribute1 );
|
|
if( FAILED( hr ) )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
}while( FALSE );
|
|
|
|
return( hr );
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Name: CAppend::CopyAttribute()
|
|
// Desc: Copies an attribute from the reader to the writer.
|
|
//------------------------------------------------------------------------------
|
|
HRESULT CAppend::CopyAttribute( WORD nInStreamNum,
|
|
WORD nOutStreamNum,
|
|
IWMHeaderInfo * pWriterHeaderInfo,
|
|
LPCWSTR pwszName )
|
|
{
|
|
WORD cbLength = 0;
|
|
HRESULT hr = S_OK;
|
|
BYTE* pValue = NULL;
|
|
|
|
WMT_ATTR_DATATYPE type;
|
|
//
|
|
// Get the number of bytes to be allocated for pValue
|
|
//
|
|
hr = m_pRdrHdrInfo1->GetAttributeByName( &nInStreamNum,
|
|
pwszName,
|
|
&type,
|
|
NULL,
|
|
&cbLength );
|
|
if( FAILED( hr ) && hr != ASF_E_NOTFOUND )
|
|
{
|
|
_tprintf( _T( "GetAttributeByName failed for Attribute name %ws (hr=0x%08x).\n" ) , pwszName, hr );
|
|
return( hr );
|
|
}
|
|
|
|
if( cbLength == 0 && hr == ASF_E_NOTFOUND )
|
|
{
|
|
hr = S_OK;
|
|
return( hr );
|
|
}
|
|
|
|
pValue = new BYTE[ cbLength ];
|
|
if( NULL == pValue )
|
|
{
|
|
_tprintf( _T( "Internal Error: Out of memory\n" ) );
|
|
hr = E_OUTOFMEMORY;
|
|
return( hr );
|
|
}
|
|
|
|
do
|
|
{
|
|
|
|
//
|
|
// Get the value
|
|
//
|
|
hr = m_pRdrHdrInfo1->GetAttributeByName( &nInStreamNum,
|
|
pwszName,
|
|
&type,
|
|
pValue,
|
|
&cbLength );
|
|
if( FAILED( hr ) )
|
|
{
|
|
_tprintf( _T( "GetAttributeByName failed for Attribute name %ws (hr=0x%08x).\n" ), pwszName, hr );
|
|
break;
|
|
}
|
|
//
|
|
// Set the attribute
|
|
//
|
|
hr = pWriterHeaderInfo->SetAttribute( nOutStreamNum,
|
|
pwszName,
|
|
type,
|
|
pValue,
|
|
cbLength );
|
|
if( FAILED( hr ) )
|
|
{
|
|
_tprintf( _T("SetAttribute failed for Attribute name %ws (hr=0x%08x).\n" ), pwszName, hr );
|
|
break;
|
|
}
|
|
}
|
|
while( FALSE );
|
|
|
|
SAFE_ARRAYDELETE( pValue );
|
|
|
|
return( hr );
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Name: CAppend::CopyCodecInfo()
|
|
// Desc: Copies codec information from the reader header to the writer header.
|
|
//------------------------------------------------------------------------------
|
|
HRESULT CAppend::CopyCodecInfo( IWMHeaderInfo * pWHdrInfo )
|
|
{
|
|
HRESULT hr = S_OK;
|
|
DWORD i, cCodecInfo;
|
|
WORD cchName, cchDescription, cbCodecInfo;
|
|
LPWSTR pwszName = NULL, pwszDescription = NULL;
|
|
BYTE * pbCodecInfo = NULL;
|
|
|
|
WMT_CODEC_INFO_TYPE codecType;
|
|
IWMHeaderInfo3 * pWHdrInfo3 = NULL,
|
|
* pRHdrInfo3 = NULL;
|
|
|
|
hr = m_pRdrHdrInfo1->QueryInterface( IID_IWMHeaderInfo3,
|
|
( void ** )&pRHdrInfo3 );
|
|
if( SUCCEEDED( hr ) )
|
|
{
|
|
hr = pWHdrInfo->QueryInterface( IID_IWMHeaderInfo3,
|
|
( void ** )&pWHdrInfo3 );
|
|
}
|
|
if( FAILED( hr ) )
|
|
{
|
|
_tprintf( _T( "QI for IWMHeaderInfo3 failed (hr=0x%08x).\n" ), hr );
|
|
goto Exit;
|
|
}
|
|
|
|
hr = pRHdrInfo3->GetCodecInfoCount( &cCodecInfo );
|
|
if( FAILED( hr ) )
|
|
{
|
|
_tprintf( _T( "GetCodecInfoCount failed (hr=0x%08x).\n" ), hr );
|
|
goto Exit;
|
|
}
|
|
|
|
for( i = 0; i < cCodecInfo; i++ )
|
|
{
|
|
//
|
|
// Get codec info from the source
|
|
//
|
|
cchName = cchDescription = cbCodecInfo = 0;
|
|
hr = pRHdrInfo3->GetCodecInfo( i, &cchName, NULL,
|
|
&cchDescription, NULL, &codecType, &cbCodecInfo, NULL );
|
|
if( SUCCEEDED( hr ) )
|
|
{
|
|
SAFE_ARRAYDELETE( pwszName );
|
|
SAFE_ARRAYDELETE( pwszDescription );
|
|
SAFE_ARRAYDELETE( pbCodecInfo );
|
|
|
|
pwszName = new WCHAR [cchName+1];
|
|
pwszDescription = new WCHAR [cchDescription+1];
|
|
pbCodecInfo = new BYTE [cbCodecInfo];
|
|
if( NULL == pwszName || NULL == pwszDescription || NULL == pbCodecInfo )
|
|
{
|
|
_tprintf( _T( "Internal Error: Out of memory\n" ) );
|
|
hr = E_OUTOFMEMORY;
|
|
goto Exit;
|
|
}
|
|
|
|
hr = pRHdrInfo3->GetCodecInfo( i, &cchName, pwszName,
|
|
&cchDescription, pwszDescription, &codecType, &cbCodecInfo,
|
|
pbCodecInfo );
|
|
}
|
|
|
|
if( FAILED( hr ) )
|
|
{
|
|
_tprintf( _T( "GetCodecInfo failed (hr=0x%08x).\n" ), hr );
|
|
goto Exit;
|
|
}
|
|
|
|
pwszName[cchName] = pwszDescription[cchDescription] = L'\0';
|
|
|
|
//
|
|
// Add the codec info to the writer
|
|
//
|
|
hr = pWHdrInfo3->AddCodecInfo( pwszName, pwszDescription, codecType,
|
|
cbCodecInfo, pbCodecInfo );
|
|
if( FAILED( hr ) )
|
|
{
|
|
_tprintf( _T( "AddCodecInfo failed (hr=0x%08x).\n" ), hr );
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
SAFE_ARRAYDELETE( pwszName );
|
|
SAFE_ARRAYDELETE( pwszDescription );
|
|
SAFE_ARRAYDELETE( pbCodecInfo );
|
|
SAFE_RELEASE( pRHdrInfo3 );
|
|
SAFE_RELEASE( pWHdrInfo3 );
|
|
|
|
return( hr );
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Name: CAppend::CopyScript()
|
|
// Desc: Copies script from the reader header to the writer header.
|
|
//------------------------------------------------------------------------------
|
|
HRESULT CAppend::CopyScript( IWMHeaderInfo * pWHdrInfo, IWMHeaderInfo * pRHdrInfo, QWORD qwTimeOffset )
|
|
{
|
|
HRESULT hr = S_OK;
|
|
LPWSTR pwszType = NULL;
|
|
LPWSTR pwszCommand = NULL;
|
|
WORD cchTypeLen = 0;
|
|
WORD cScript = 0;
|
|
WORD cchCommandLen = 0;
|
|
QWORD cnsScriptTime = 0;
|
|
|
|
hr = pRHdrInfo->GetScriptCount( &cScript );
|
|
if( FAILED( hr ) )
|
|
{
|
|
_tprintf( _T( "GetScriptCount failed (hr=0x%08x).\n" ), hr );
|
|
return( hr );
|
|
}
|
|
|
|
|
|
for( WORD i = 0; i < cScript; i++)
|
|
{
|
|
//
|
|
// Get the memory required for this script
|
|
//
|
|
hr = pRHdrInfo->GetScript( i,
|
|
NULL,
|
|
&cchTypeLen,
|
|
NULL,
|
|
&cchCommandLen,
|
|
&cnsScriptTime );
|
|
if( FAILED( hr ) )
|
|
{
|
|
_tprintf( _T( "GetScript failed for Script no %d (hr=0x%08x).\n" ), i, hr );
|
|
break;
|
|
}
|
|
|
|
pwszType = new WCHAR[cchTypeLen];
|
|
pwszCommand = new WCHAR[cchCommandLen];
|
|
if( NULL == pwszType || NULL == pwszCommand )
|
|
{
|
|
_tprintf( _T( "Internal Error: Out of memory\n" ) );
|
|
hr = E_OUTOFMEMORY;
|
|
break;
|
|
}
|
|
//
|
|
// Get the script
|
|
//
|
|
hr = pRHdrInfo->GetScript( i,
|
|
pwszType,
|
|
&cchTypeLen,
|
|
pwszCommand,
|
|
&cchCommandLen,
|
|
&cnsScriptTime );
|
|
if( FAILED( hr ) )
|
|
{
|
|
_tprintf( _T( "GetScript failed for Script no %d (hr=0x%08x).\n" ), i, hr );
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Add the script to the writer
|
|
//
|
|
hr = pWHdrInfo->AddScript( pwszType, pwszCommand, cnsScriptTime + qwTimeOffset );
|
|
if( FAILED( hr ) )
|
|
{
|
|
_tprintf( _T("AddScript failed for Script no %d (hr=0x%08x).\n" ), i, hr );
|
|
break;
|
|
}
|
|
|
|
SAFE_ARRAYDELETE( pwszType );
|
|
SAFE_ARRAYDELETE( pwszCommand );
|
|
cchTypeLen = cchCommandLen = 0;
|
|
}
|
|
|
|
SAFE_ARRAYDELETE( pwszType );
|
|
SAFE_ARRAYDELETE( pwszCommand );
|
|
|
|
return( hr );
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Name: CAppend::StartAppending()
|
|
// Desc: Copies the input files to the output file.
|
|
//------------------------------------------------------------------------------
|
|
HRESULT CAppend::StartAppending()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
do
|
|
{
|
|
hr = m_pWriter->BeginWriting();
|
|
if( FAILED( hr ) )
|
|
{
|
|
_tprintf( _T( "BeginWriting on IWMWriter failed (hr=0x%08x).\n" ), hr );
|
|
break;
|
|
}
|
|
|
|
hr = m_pReader1->Start( 0, 0, 1.0, 0 );
|
|
if( FAILED( hr ) )
|
|
{
|
|
_tprintf( _T("Could not start IWMReader for first file (hr=0x%08x).\n" ), hr );
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Wait for it to finish
|
|
//
|
|
WaitForSingleObject( m_hAsyncEvent, INFINITE );
|
|
|
|
if( FAILED( m_hrAsync ) )
|
|
{
|
|
_tprintf( _T( "Stream copying failed for the first file (hr=0x%08x).\n" ), m_hrAsync );
|
|
hr = m_hrAsync;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Stop stuff
|
|
//
|
|
hr = m_pReader1->Stop( );
|
|
if( FAILED( hr ) )
|
|
{
|
|
_tprintf( _T( "Could not Stop IWMReader for the first file (hr=0x%08x).\n" ), hr );
|
|
break;
|
|
}
|
|
//
|
|
// Wait for it to finish
|
|
//
|
|
WaitForSingleObject( m_hAsyncEvent, INFINITE );
|
|
|
|
if( FAILED( m_hrAsync ) )
|
|
{
|
|
_tprintf( _T( "Could not Stop reader of the first file (hr=0x%08x).\n" ), m_hrAsync );
|
|
hr = m_hrAsync;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Append the second file
|
|
//
|
|
hr = m_pReader2->Start( 0, 0, 1.0, 0 );
|
|
if( FAILED( hr ) )
|
|
{
|
|
_tprintf( _T("Could not start IWMReader for second file (hr=0x%08x).\n" ), hr );
|
|
break;
|
|
}
|
|
|
|
WaitForSingleObject( m_hAsyncEvent, INFINITE );
|
|
|
|
if( FAILED( m_hrAsync ) )
|
|
{
|
|
_tprintf( _T( "Stream copying failed for the second file (hr=0x%08x).\n" ), m_hrAsync );
|
|
hr = m_hrAsync;
|
|
break;
|
|
}
|
|
|
|
hr = m_pReader2->Stop();
|
|
if( FAILED( hr ) )
|
|
{
|
|
_tprintf( _T( "Could not Stop IWMReader for the second file (hr=0x%08x).\n" ), hr );
|
|
break;
|
|
}
|
|
|
|
WaitForSingleObject( m_hAsyncEvent, INFINITE );
|
|
|
|
if( FAILED( m_hrAsync ) )
|
|
{
|
|
_tprintf( _T( "Could not Stop IWMReader for the second file (hr=0x%08x).\n" ), m_hrAsync );
|
|
hr = m_hrAsync;
|
|
break;
|
|
}
|
|
|
|
hr = m_pWriter->EndWriting();
|
|
if( FAILED( hr ) )
|
|
{
|
|
_tprintf( _T( "Could not EndWriting on IWMWriter (hr=0x%08x).\n" ), hr );
|
|
break;
|
|
}
|
|
|
|
hr = CopyAllMarkers();
|
|
if( FAILED( hr ) )
|
|
{
|
|
break;
|
|
}
|
|
|
|
}while( FALSE );
|
|
|
|
return( hr );
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Name: CAppend::SetReceiveStreamSample()
|
|
// Desc: Sets the specified stream to be received in compressed mode.
|
|
//------------------------------------------------------------------------------
|
|
HRESULT CAppend::SetReceiveStreamSample( IWMReaderAdvanced* pReaderAdv, IWMProfile* pProfile, DWORD nStreamIndex )
|
|
{
|
|
IWMStreamConfig * pStream = NULL;
|
|
|
|
WORD wStreamNumber = 0;
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
do
|
|
{
|
|
hr = pProfile->GetStream( nStreamIndex, &pStream );
|
|
if( FAILED( hr ) )
|
|
{
|
|
_tprintf( _T( "Could not get Stream %d from IWMProfile (hr=0x%08x).\n" ), nStreamIndex, hr );
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Get the stream number
|
|
//
|
|
hr = pStream->GetStreamNumber( &wStreamNumber );
|
|
if( FAILED( hr ) )
|
|
{
|
|
_tprintf( _T( "Could not get stream number from IWMStreamConfig for Stream Index %d (hr=0x%08x).\n" ), nStreamIndex, hr );
|
|
break;
|
|
}
|
|
|
|
SAFE_RELEASE( pStream );
|
|
|
|
//
|
|
// Set the stream to be received in compressed mode
|
|
//
|
|
hr = pReaderAdv->SetReceiveStreamSamples( wStreamNumber, TRUE );
|
|
if( FAILED( hr ) )
|
|
{
|
|
_tprintf( _T( "Could not SetReceivedStreamSamples for Stream Index %d (hr=0x%08x).\n" ), nStreamIndex, hr );
|
|
break;
|
|
}
|
|
}
|
|
while( FALSE );
|
|
|
|
SAFE_RELEASE( pStream );
|
|
|
|
return( hr );
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Name: CAppend::CopyAllMarkers()
|
|
// Desc: Copies markers from the input headers to the output header.
|
|
//------------------------------------------------------------------------------
|
|
HRESULT CAppend::CopyAllMarkers()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
IWMMetadataEditor * pEditor = NULL;
|
|
IWMHeaderInfo * pWriterHeaderInfo = NULL;
|
|
|
|
do
|
|
{
|
|
//
|
|
// Markers can be copied only by the Metadata Editor.
|
|
// Let's create one.
|
|
//
|
|
hr = WMCreateEditor( &pEditor );
|
|
if( FAILED( hr ) )
|
|
{
|
|
_tprintf( _T( "Could not create Metadata Editor (hr=0x%08x).\n" ), hr );
|
|
break;
|
|
}
|
|
|
|
hr = pEditor->Open( m_pwszOutFile );
|
|
if( FAILED ( hr ) )
|
|
{
|
|
_tprintf( _T( "Could not open outfile %ws (hr=0x%08x).\n" ), m_pwszOutFile ,hr );
|
|
break;
|
|
}
|
|
|
|
hr = pEditor->QueryInterface( IID_IWMHeaderInfo, ( void ** ) &pWriterHeaderInfo );
|
|
if( FAILED( hr ) )
|
|
{
|
|
_tprintf( _T( "Could not QI for IWMHeaderInfo (hr=0x%08x).\n" ), hr );
|
|
break;
|
|
}
|
|
|
|
hr = CopyMarkersFromHdr( m_pRdrHdrInfo1, pWriterHeaderInfo, 0 );
|
|
if( FAILED( hr ) )
|
|
{
|
|
_tprintf( _T( "Could not create Metadata Editor (hr=0x%08x).\n" ), hr );
|
|
break;
|
|
}
|
|
|
|
hr = CopyMarkersFromHdr( m_pRdrHdrInfo2, pWriterHeaderInfo, m_qwFirstTime );
|
|
if( FAILED( hr ) )
|
|
{
|
|
_tprintf( _T( "Could not create Metadata Editor (hr=0x%08x).\n" ), hr );
|
|
break;
|
|
}
|
|
|
|
hr = pEditor->Close();
|
|
if( FAILED( hr ) )
|
|
{
|
|
_tprintf( _T( "Could not close the Editor (hr=0x%08x).\n" ), hr);
|
|
break;
|
|
}
|
|
}
|
|
while( FALSE);
|
|
|
|
SAFE_RELEASE( pWriterHeaderInfo);
|
|
SAFE_RELEASE( pEditor );
|
|
|
|
return( hr );
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Name: CAppend::CopyMarkersFromHdr()
|
|
// Desc: Called by CopyAllMarkers to copy markers from one input file to the
|
|
// output file.
|
|
//------------------------------------------------------------------------------
|
|
HRESULT CAppend::CopyMarkersFromHdr(IWMHeaderInfo * pRHdrInfo, IWMHeaderInfo * pWHdrInfo, QWORD qwTimeOffset )
|
|
{
|
|
HRESULT hr = S_OK;
|
|
WORD cMarker = 0;
|
|
|
|
hr = pRHdrInfo->GetMarkerCount( &cMarker );
|
|
if( FAILED( hr ) )
|
|
{
|
|
_tprintf( _T("GetMarkerCount failed (hr=0x%08x).\n" ), hr);
|
|
return( hr );
|
|
}
|
|
|
|
if( cMarker == 0 )
|
|
{
|
|
return( S_OK );
|
|
}
|
|
|
|
LPWSTR pwszMarkerName = NULL;
|
|
WORD cchMarkerNameLen = 0;
|
|
QWORD cnsMarkerTime = 0;
|
|
|
|
for( WORD i = 0; i < cMarker; i++)
|
|
{
|
|
//
|
|
// Get the memory required for this marker
|
|
//
|
|
hr = pRHdrInfo->GetMarker( i,
|
|
NULL,
|
|
&cchMarkerNameLen,
|
|
&cnsMarkerTime );
|
|
if( FAILED( hr ) )
|
|
{
|
|
_tprintf( _T("GetMarker failed for Marker no %d (hr=0x%08x).\n" ), i, hr );
|
|
break;
|
|
}
|
|
|
|
pwszMarkerName = new WCHAR[cchMarkerNameLen];
|
|
if( pwszMarkerName == NULL )
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_tprintf( _T( "Internal Error: Out of memory\n" ) );
|
|
break;
|
|
}
|
|
|
|
hr = pRHdrInfo->GetMarker( i,
|
|
pwszMarkerName,
|
|
&cchMarkerNameLen,
|
|
&cnsMarkerTime );
|
|
if( FAILED( hr ) )
|
|
{
|
|
_tprintf( _T( "GetMarker failed for Marker no %d (hr=0x%08x).\n" ), i, hr );
|
|
break;
|
|
}
|
|
|
|
hr = pWHdrInfo->AddMarker( pwszMarkerName, cnsMarkerTime + qwTimeOffset );
|
|
if( FAILED( hr ) )
|
|
{
|
|
_tprintf( _T( "AddMarker failed for Marker no %d (hr=0x%08x).\n" ), i, hr );
|
|
break;
|
|
}
|
|
|
|
SAFE_ARRAYDELETE( pwszMarkerName );
|
|
pwszMarkerName = NULL;
|
|
cchMarkerNameLen = 0;
|
|
}
|
|
|
|
SAFE_ARRAYDELETE( pwszMarkerName );
|
|
|
|
return( hr );
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Name: CAppend::MapStreamNum()
|
|
// Desc: Maps the stream numbers of the second file to those of the output file.
|
|
//------------------------------------------------------------------------------
|
|
WORD CAppend::MapStreamNum( WORD dwNum )
|
|
{
|
|
for( WORD i =0; i < m_dwStreamCount; i++ )
|
|
{
|
|
if( m_pwStreamNumMap[i + m_dwStreamCount] == dwNum )
|
|
{
|
|
return( (WORD)m_pwStreamNumMap[i] );
|
|
}
|
|
}
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Name: CAppend::IsFileProtected()
|
|
// Desc: Ascertains whether the specified file is protected by DRM.
|
|
//------------------------------------------------------------------------------
|
|
HRESULT CAppend::IsFileProtected( __in LPWSTR pswzFileName )
|
|
{
|
|
IWMMetadataEditor * pEditor = NULL;
|
|
IWMHeaderInfo * pHeaderInfo = NULL;
|
|
|
|
HRESULT hr = S_OK;
|
|
//
|
|
// Open the files using metadata editor.
|
|
// Opening it using IWMReader would decrement the usage count,
|
|
// if it's a protected file.
|
|
//
|
|
do
|
|
{
|
|
hr = WMCreateEditor( &pEditor );
|
|
if( FAILED( hr ) )
|
|
{
|
|
_tprintf( _T( "Could not create Metadata Editor (hr=0x%08x).\n" ), hr );
|
|
break;
|
|
}
|
|
|
|
hr = pEditor->Open( pswzFileName );
|
|
if( FAILED ( hr ) )
|
|
{
|
|
_tprintf( _T( "Could not open file %ws (hr=0x%08x).\n" ), pswzFileName ,hr );
|
|
break;
|
|
}
|
|
|
|
hr = pEditor->QueryInterface( IID_IWMHeaderInfo, ( void ** ) &pHeaderInfo );
|
|
if( FAILED( hr ) )
|
|
{
|
|
_tprintf( _T( "Could not QI for IWMHeaderInfo (hr=0x%08x).\n" ), hr );
|
|
break;
|
|
}
|
|
|
|
WORD wStreamNum = 0;
|
|
WMT_ATTR_DATATYPE type;
|
|
BYTE value[4];
|
|
WORD cbLength = 4;
|
|
//
|
|
// Check the protected attribute of the header
|
|
//
|
|
hr = pHeaderInfo->GetAttributeByName( &wStreamNum, g_wszWMProtected, &type, value, &cbLength );
|
|
if( FAILED( hr ) )
|
|
{
|
|
_tprintf( _T( "Error in getting Protected attribute for file %ws (hr=0x%08x).\n" ), pswzFileName, hr );
|
|
break;
|
|
}
|
|
|
|
if( value[0] )
|
|
{
|
|
_tprintf( _T( "Protected attributes set for input file %ws .\n" ), pswzFileName );
|
|
hr = E_INVALIDARG;
|
|
break;
|
|
}
|
|
}
|
|
while( FALSE );
|
|
|
|
SAFE_RELEASE( pHeaderInfo );
|
|
SAFE_RELEASE( pEditor );
|
|
|
|
return( hr );
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Name: CAppend::QueryInterface()
|
|
// Desc: Implementation of the IUnknown method.
|
|
//------------------------------------------------------------------------------
|
|
HRESULT STDMETHODCALLTYPE CAppend::QueryInterface( REFIID riid, void ** ppvObject )
|
|
{
|
|
if ( riid == IID_IWMReaderCallback )
|
|
{
|
|
*ppvObject = ( IWMReaderCallback* )this;
|
|
AddRef();
|
|
}
|
|
else if( riid == IID_IWMReaderCallbackAdvanced )
|
|
{
|
|
*ppvObject = ( IWMReaderCallbackAdvanced * )this;
|
|
AddRef();
|
|
}
|
|
else
|
|
{
|
|
*ppvObject = NULL;
|
|
return( E_NOINTERFACE );
|
|
}
|
|
|
|
return( S_OK );
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Name: CAppend::AddRef()
|
|
// Desc: Implementation of the IUnknown method.
|
|
//------------------------------------------------------------------------------
|
|
ULONG STDMETHODCALLTYPE CAppend::AddRef( void )
|
|
{
|
|
return( InterlockedIncrement( &m_cRef ) );
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Name: CAppend::Release()
|
|
// Desc: Implementation of the IUnknown method.
|
|
//------------------------------------------------------------------------------
|
|
ULONG STDMETHODCALLTYPE CAppend::Release( void )
|
|
{
|
|
if ( 0 == InterlockedDecrement( &m_cRef ) )
|
|
{
|
|
delete this;
|
|
return 0;
|
|
}
|
|
|
|
return( m_cRef );
|
|
}
|
|
|