2025-11-28 00:35:46 +09:00

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 );
}