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

3369 lines
100 KiB
C++

//*****************************************************************************
//
// Microsoft Windows Media
// Copyright ( C) Microsoft Corporation. All rights reserved.
//
// FileName: UncompAVIToWMV.cpp
//
// Abstract: Implementation of CUncompAVIToWMV class.
//
//*****************************************************************************
#include <tchar.h>
#include <stdio.h>
#include <windows.h>
#include <assert.h>
#include <mmsystem.h>
#include <strsafe.h>
#include "UncompAVIToWMV.h"
//
// Maximum number of files specified in file
//
#define MAX_INPUT_FILE 63
//
// {AA1A7D50-0690-4c22-9578-4A129E5ECD63}
//
static const GUID WMMEDIATYPE_MyArbitrary =
{ 0xaa1a7d50, 0x690, 0x4c22, { 0x95, 0x78, 0x4a, 0x12, 0x9e, 0x5e, 0xcd, 0x63 } };
const LPWSTR wszDefaultConnectionName = L"UncompAVIToWMV";
//------------------------------------------------------------------------------
// Name: ConvertTCharToWChar()
// Desc: Converts TCHAR string to wide characters.
//------------------------------------------------------------------------------
HRESULT ConvertTCharToWChar( LPCTSTR ptszInput, __out LPWSTR * pwszOutput )
{
int cchOutput = 0;
if( NULL == ptszInput || NULL == pwszOutput )
{
return( E_INVALIDARG );
}
//
// Get output buffer size
//
#ifdef UNICODE
cchOutput = wcslen( ptszInput ) + 1;
#else //UNICODE
cchOutput = MultiByteToWideChar( CP_ACP, 0, ptszInput, -1, NULL, 0 );
if( 0 == cchOutput )
{
return( HRESULT_FROM_WIN32( GetLastError() ) );
}
#endif // UNICODE
*pwszOutput = new WCHAR[ cchOutput ];
if( NULL == *pwszOutput)
{
return( E_OUTOFMEMORY );
}
#ifdef UNICODE
wcsncpy_s( *pwszOutput, cchOutput, ptszInput, cchOutput - 1);
#else //UNICODE
if( 0 == MultiByteToWideChar( CP_ACP, 0, ptszInput, -1, *pwszOutput, cchOutput ) )
{
SAFE_ARRAYDELETE( *pwszOutput );
return( HRESULT_FROM_WIN32( GetLastError() ) );
}
#endif // UNICODE
return( S_OK );
}
//------------------------------------------------------------------------------
// Name: ConvertCharToTChar()
// Desc: Converts CHAR string to TCHAR.
//------------------------------------------------------------------------------
HRESULT ConvertCharToTChar( LPCSTR pszInput, __out LPTSTR * ptszOutput )
{
int cchOutput = 0;
if( NULL == pszInput || NULL == ptszOutput )
{
return( E_INVALIDARG );
}
//
// Get output buffer size
//
#ifdef UNICODE
cchOutput = MultiByteToWideChar( CP_ACP, 0, pszInput, -1, NULL, 0 );
if( 0 == cchOutput )
{
return( HRESULT_FROM_WIN32( GetLastError() ) );
}
#else //UNICODE
cchOutput = strlen( pszInput ) + 1;
#endif // UNICODE
*ptszOutput = new TCHAR[ cchOutput ];
if( NULL == *ptszOutput)
{
return( E_OUTOFMEMORY );
}
#ifdef UNICODE
if( 0 == MultiByteToWideChar( CP_ACP, 0, pszInput, -1, *ptszOutput, cchOutput ) )
{
SAFE_ARRAYDELETE( *ptszOutput );
return( HRESULT_FROM_WIN32( GetLastError() ) );
}
#else //UNICODE
(void)StringCchCopy( *ptszOutput, cchOutput, pszInput );
#endif // UNICODE
return( S_OK );
}
//------------------------------------------------------------------------------
// Name: ConvertTCharToChar()
// Desc: Converts TCHAR string to CHAR.
//------------------------------------------------------------------------------
HRESULT ConvertTCharToChar( LPCTSTR ptszInput, __out LPSTR * pszOutput )
{
int cchOutput = 0;
if( NULL == ptszInput || NULL == pszOutput )
{
return( E_INVALIDARG );
}
//
// Get output buffer size
//
#ifdef UNICODE
cchOutput = WideCharToMultiByte ( CP_ACP, 0, ptszInput, -1, NULL, 0, NULL, NULL );
if( 0 == cchOutput )
{
return( HRESULT_FROM_WIN32( GetLastError() ) );
}
#else //UNICODE
cchOutput = strlen( ptszInput ) + 1;
#endif // UNICODE
*pszOutput = new CHAR[ cchOutput ];
if( NULL == *pszOutput)
{
return( E_OUTOFMEMORY );
}
#ifdef UNICODE
if( 0 == WideCharToMultiByte( CP_ACP, 0, ptszInput, -1, *pszOutput, cchOutput, NULL, NULL ) )
{
SAFE_ARRAYDELETE( *pszOutput );
return( HRESULT_FROM_WIN32( GetLastError() ) );
}
#else //UNICODE
(void)StringCchCopy( *pszOutput, cchOutput, ptszInput );
#endif // UNICODE
return( S_OK );
}
//------------------------------------------------------------------------------
// Name: CWMInput::CWMInput()
// Desc: Constructor.
//------------------------------------------------------------------------------
CWMInput::CWMInput() : m_qwPresentTime( 0 ),
m_dwInput( 0 ),
m_dwSamples( 0 ),
m_dwCurrentSample( 0 ),
m_pAVIFile( NULL ),
m_pwszConnectionName( NULL ),
m_pAVIStream( NULL ),
m_fAddSMPTE( FALSE )
{
ZeroMemory( &m_Mt, sizeof( m_Mt ) );
ZeroMemory( &m_StreamInfo, sizeof( m_StreamInfo ) );
}
//------------------------------------------------------------------------------
// Name: CWMInput::Cleanup()
// Desc: Releases global allocations.
//------------------------------------------------------------------------------
void CWMInput::Cleanup()
{
if( NULL != m_pAVIStream )
{
AVIStreamRelease( m_pAVIStream );
m_pAVIStream = NULL;
}
SAFE_ARRAYDELETE( m_pwszConnectionName );
SAFE_ARRAYDELETE( m_Mt.pbFormat );
}
//------------------------------------------------------------------------------
// Name: CUncompAVIToWMV::CUncompAVIToWMV()
// Desc: Constructor.
//------------------------------------------------------------------------------
CUncompAVIToWMV::CUncompAVIToWMV()
{
m_pWMWriter = NULL;
m_pIWMWriterPreprocess = NULL;
m_dwArbitraryInput = 0;
m_fPreprocessing = FALSE;
m_fArbitrary = FALSE;
m_fPreserveProfile = TRUE;
AVIFileInit();
}
//------------------------------------------------------------------------------
// Name: CUncompAVIToWMV::~CUncompAVIToWMV()
// Desc: Constructor.
//------------------------------------------------------------------------------
CUncompAVIToWMV::~CUncompAVIToWMV()
{
SAFE_RELEASE( m_pIWMWriterPreprocess );
SAFE_RELEASE( m_pWMWriter );
AVIFileExit();
}
//------------------------------------------------------------------------------
// Name: CUncompAVIToWMV::Initial()
// Desc: Initializes the writer according to WAV/AVI input files and
// parameter settings.
//------------------------------------------------------------------------------
HRESULT CUncompAVIToWMV::Initial( __in LPTSTR ptszInFile,
BOOL fInFileListFile,
__in LPTSTR ptszOutFile,
IWMProfile* pProfile,
BOOL fArbitrary,
BOOL fPreserveProfile,
BOOL fAddSMPTE,
int nMaxDuration )
{
HRESULT hr = S_OK;
LPTSTR tszFileList[ MAX_INPUT_FILE ];
int nFileListSize = 0;
if( NULL == ptszInFile || NULL == ptszOutFile || NULL == pProfile )
{
return( E_INVALIDARG );
}
m_fArbitrary = fArbitrary;
m_fPreserveProfile = fPreserveProfile;
if( nMaxDuration > 0 )
{
m_qwMaxDuration = 10000000 * (QWORD)nMaxDuration;
}
else
{
m_qwMaxDuration = 0x7FFFFFFFFFFFFFFF;
}
if( fInFileListFile )
{
ZeroMemory( tszFileList, sizeof( tszFileList) );
nFileListSize = MAX_INPUT_FILE;
//
// Create list of input files and initialize AVI input list
//
hr = GetTokensFromFile( ptszInFile, tszFileList, &nFileListSize );
if( FAILED( hr ) || 0 == nFileListSize )
{
_tprintf( _T( "Failed to read AVI file list from %s (hr=0x%08x)\n" ), ptszInFile, hr );
return( E_INVALIDARG );
}
int i;
for( i = 0; i < nFileListSize; i++ )
{
hr = InitAVISource( tszFileList[ i ] );
if( FAILED( hr ) )
{
_tprintf( _T( "Failed to initial AVI source %s (hr=0x%08x)\n" ), tszFileList[ i ], hr );
break;
}
}
//
// Deallocate input file list
//
for( i = 0; i < nFileListSize; i++ )
{
SAFE_ARRAYDELETE( tszFileList[ i ] );
}
if( FAILED( hr ) )
{
return( hr );
}
}
else
{
//
// There is only one WAV/AVI file
//
hr = InitAVISource( ptszInFile );
if( FAILED( hr ) )
{
_tprintf( _T( "Failed to initial AVI source %s (hr=0x%08x)\n" ), ptszInFile, hr );
return( hr );
}
}
//
// Create a WMWriter for the output file and set the profile
//
hr = WMCreateWriter( NULL, &m_pWMWriter );
if( FAILED( hr ) )
{
_tprintf( _T( "Failed to Create WMWriter (hr=0x%08x)\n" ), hr );
return( hr );
}
if( m_fPreprocessing )
{
//
// Get a pointer to the preprocessing interface for multipass encoding
//
hr = m_pWMWriter->QueryInterface( IID_IWMWriterPreprocess,
(void **)&m_pIWMWriterPreprocess );
if( FAILED( hr ) )
{
_tprintf( _T( "Failed to query IWMWriterPreprocess interface %x\n" ), hr );
return( hr );
}
}
//
// Synchronize information in a current profile with AVI inputs.
// As a result, all inputs in the profile should match AVI inputs.
//
hr = UpdateProfile( pProfile );
if( FAILED( hr ) )
{
_tprintf( _T( "Failed to update profile : (hr=0x%08x)\n" ), hr );
return( hr );
}
if( m_fArbitrary )
{
//
// Add arbitrary stream to current profile
//
hr = AddArbitraryStream( pProfile );
if( FAILED( hr ) )
{
_tprintf( _T( "Failed to add arbitrary stream (hr=0x%08x)\n" ), hr );
return( hr );
}
}
if( fAddSMPTE )
{
//
// If fAddSMPTE is TRUE, we need to set up SMPTE.
//
hr = SetupSMPTE( pProfile );
if( FAILED( hr ) )
{
_tprintf( _T( "Failed to get the video frame rate for SMPTE (hr=0x%08x)\n" ), hr );
return( hr );
}
}
//
// Save profile to writer
//
hr = m_pWMWriter->SetProfile( pProfile );
if( FAILED( hr ) )
{
_tprintf( _T( "Failed to set profile (hr=0x%08x)\n" ), hr );
return( hr );
}
//
// Update writer's inputs according to AVI inputs
//
hr = UpdateWriterInputs();
if( FAILED( hr ) )
{
_tprintf( _T( "Failed to update writer inputs (hr=0x%08x)\n" ), hr );
return( hr );
}
WCHAR * pwszOutFile = NULL;
//
// Convert the output filename to a wide character string and give it
// to the writer.
//
hr = ConvertTCharToWChar( ptszOutFile, &pwszOutFile );
if( FAILED( hr ) )
{
_tprintf( _T( "Failed to convert output file name to wchar string (hr=0x%08x)\n" ), hr );
return( hr );
}
//
// Note: Indexing is automatically enabled when writing the uncompressed
// samples. We don't need to set up indexing manually. You can call
// IWMWriterFileSink3::SetAutoIndexing( FALSE ) to disable indexing.
//
hr = m_pWMWriter->SetOutputFilename( pwszOutFile );
SAFE_ARRAYDELETE( pwszOutFile );
if( FAILED( hr ) )
{
_tprintf( _T( "Failed to set output filename (hr=0x%08x)\n" ), hr );
}
return( hr );
}
//------------------------------------------------------------------------------
// Name: CUncompAVIToWMV::InitAVISource()
// Desc: Creates a list of all AVI inputs from AVI/WAV files.
//------------------------------------------------------------------------------
HRESULT CUncompAVIToWMV::InitAVISource( __in LPTSTR ptszInputFile )
{
HRESULT hr = S_OK;
BITMAPINFO* pBMI = NULL;
CWMInput input;
if( NULL == ptszInputFile )
{
return( E_INVALIDARG );
}
//
// Open the AVI file
//
hr = AVIFileOpen( &input.m_pAVIFile,
ptszInputFile,
OF_SHARE_DENY_NONE,
NULL );
if( FAILED( hr ) )
{
return( hr );
}
//
// First, get the audio stream information from the AVI file
//
hr = AVIFileGetStream( input.m_pAVIFile,
&input.m_pAVIStream,
streamtypeAUDIO,
0 );
if( SUCCEEDED( hr ) )
{
LONG cbWFX;
input.m_dwSamples = AVIStreamEnd( input.m_pAVIStream );
input.m_dwCurrentSample = AVIStreamStart( input.m_pAVIStream );
//
// If the stream contains any samples to play, put its parameters into the list
//
if( input.m_dwSamples > input.m_dwCurrentSample )
{
hr = AVIStreamFormatSize( input.m_pAVIStream, 0, &cbWFX );
if( FAILED( hr ) )
{
return( hr );
}
if( cbWFX < sizeof( WAVEFORMATEX ) )
{
cbWFX = sizeof( WAVEFORMATEX );
}
input.m_pWFX = (WAVEFORMATEX*)new BYTE[ cbWFX ];
if( NULL == input.m_pWFX )
{
return( E_OUTOFMEMORY );
}
hr = AVIStreamReadFormat( input.m_pAVIStream,
0,
input.m_pWFX,
&cbWFX );
if( FAILED( hr ) )
{
SAFE_ARRAYDELETE( input.m_pWFX );
return( hr );
}
if( WAVE_FORMAT_PCM == ( input.m_pWFX )->wFormatTag )
{
( input.m_pWFX )->cbSize = 0;
}
hr = AVIStreamInfo( input.m_pAVIStream,
&input.m_StreamInfo,
sizeof( AVISTREAMINFO ) );
if( FAILED( hr ) )
{
SAFE_ARRAYDELETE( input.m_pWFX );
return( hr );
}
//
// Set up the WM_MEDIA_TYPE structure and give it to the IWMInputMediaProps interface
//
input.m_Mt.majortype = WMMEDIATYPE_Audio;
input.m_Mt.subtype = WMMEDIASUBTYPE_PCM;
input.m_Mt.bFixedSizeSamples = TRUE;
input.m_Mt.bTemporalCompression = FALSE;
input.m_Mt.lSampleSize = input.m_StreamInfo.dwSampleSize;
input.m_Mt.formattype = WMFORMAT_WaveFormatEx;
input.m_Mt.pUnk = NULL;
input.m_Mt.cbFormat = sizeof( WAVEFORMATEX ) + ( input.m_pWFX )->cbSize;
input.m_Mt.pbFormat = (BYTE *) input.m_pWFX;
input.m_Type = WMMEDIATYPE_Audio;
//
// add to the list of AVI inputs
//
m_Inputs.Append( &input );
}
}
//
// Configure the video input format =========================================================
//
//
// First, get the video stream information from the AVI file
//
hr = AVIFileGetStream( input.m_pAVIFile, &input.m_pAVIStream, streamtypeVIDEO, 0 );
if( SUCCEEDED( hr ) )
{
LONG cbBMI;
input.m_pWFX = NULL;
input.m_dwSamples = AVIStreamEnd( input.m_pAVIStream );
input.m_dwCurrentSample = AVIStreamStart( input.m_pAVIStream );
//
// if input contains any samples to play, put its parameters into the list
//
if( input.m_dwSamples > input.m_dwCurrentSample )
{
hr = AVIStreamFormatSize( input.m_pAVIStream, 0, &cbBMI );
if( FAILED( hr ) )
{
return( hr );
}
pBMI = (BITMAPINFO *)new BYTE[ cbBMI ];
if( NULL == pBMI )
{
return( E_OUTOFMEMORY );
}
hr = AVIStreamReadFormat( input.m_pAVIStream, 0, pBMI, &cbBMI );
if( FAILED( hr ) )
{
SAFE_ARRAYDELETE( pBMI );
return( hr );
}
hr = AVIStreamInfo( input.m_pAVIStream, &input.m_StreamInfo, sizeof( AVISTREAMINFO ) );
if( FAILED( hr ) )
{
SAFE_ARRAYDELETE( pBMI );
return( hr );
}
//
// Set up the WM_MEDIA_TYPE structure and give it to the IWMInputMediaProps interface
//
DWORD cbVideoInfo = sizeof( WMVIDEOINFOHEADER ) - sizeof( BITMAPINFOHEADER ) + cbBMI;
WMVIDEOINFOHEADER* pVideoInfo = (WMVIDEOINFOHEADER *)new BYTE[ cbVideoInfo ];
if( NULL == pVideoInfo )
{
SAFE_ARRAYDELETE( pBMI );
return( E_OUTOFMEMORY );
}
pVideoInfo->rcSource.left = 0;
pVideoInfo->rcSource.top = 0;
pVideoInfo->rcSource.bottom = pBMI->bmiHeader.biHeight;
pVideoInfo->rcSource.right = pBMI->bmiHeader.biWidth;
pVideoInfo->rcTarget = pVideoInfo->rcSource;
pVideoInfo->dwBitRate = MulDiv( input.m_StreamInfo.dwSuggestedBufferSize * 8,
input.m_StreamInfo.dwRate,
input.m_StreamInfo.dwScale );
pVideoInfo->dwBitErrorRate = 0;
pVideoInfo->AvgTimePerFrame = 10000000 * (QWORD)input.m_StreamInfo.dwScale
/ input.m_StreamInfo.dwRate;
CopyMemory( &(pVideoInfo->bmiHeader), pBMI, cbBMI );
input.m_Mt.majortype = WMMEDIATYPE_Video;
//
// Map to the correct subtype GUID. If we don't map, just set it
// to GUID_NULL; this probably means the SDK doesn't support the
// input, but we'll still try to set the properties, just in case.
//
if( pBMI->bmiHeader.biCompression == BI_RGB )
{
if( pBMI->bmiHeader.biBitCount == 32 )
{
input.m_Mt.subtype = WMMEDIASUBTYPE_RGB32;
}
else if( pBMI->bmiHeader.biBitCount == 24 )
{
input.m_Mt.subtype = WMMEDIASUBTYPE_RGB24;
}
else if( pBMI->bmiHeader.biBitCount == 16 )
{
input.m_Mt.subtype = WMMEDIASUBTYPE_RGB555;
}
else if( pBMI->bmiHeader.biBitCount == 8 )
{
input.m_Mt.subtype = WMMEDIASUBTYPE_RGB8;
}
else if( pBMI->bmiHeader.biBitCount == 4 )
{
input.m_Mt.subtype = WMMEDIASUBTYPE_RGB4;
}
else if( pBMI->bmiHeader.biBitCount == 1 )
{
input.m_Mt.subtype = WMMEDIASUBTYPE_RGB1;
}
else
{
input.m_Mt.subtype = GUID_NULL;
}
}
else if( pBMI->bmiHeader.biCompression ==
MAKEFOURCC( _T('I'), _T('4'), _T('2'), _T('0') ) )
{
input.m_Mt.subtype = WMMEDIASUBTYPE_I420;
}
else if( pBMI->bmiHeader.biCompression ==
MAKEFOURCC( _T('I'), _T('Y'), _T('U'), _T('V') ) )
{
input.m_Mt.subtype = WMMEDIASUBTYPE_IYUV;
}
else if( pBMI->bmiHeader.biCompression ==
MAKEFOURCC( _T('Y'), _T('V'), _T('1'), _T('2') ) )
{
input.m_Mt.subtype = WMMEDIASUBTYPE_YV12;
}
else if( pBMI->bmiHeader.biCompression ==
MAKEFOURCC( _T('Y'), _T('U'), _T('Y'), _T('2') ) )
{
input.m_Mt.subtype = WMMEDIASUBTYPE_YUY2;
}
else if( pBMI->bmiHeader.biCompression ==
MAKEFOURCC( _T('U'), _T('Y'), _T('V'), _T('Y') ) )
{
input.m_Mt.subtype = WMMEDIASUBTYPE_UYVY;
}
else if( pBMI->bmiHeader.biCompression ==
MAKEFOURCC( _T('Y'), _T('V'), _T('Y'), _T('U') ) )
{
input.m_Mt.subtype = WMMEDIASUBTYPE_YVYU;
}
else if( pBMI->bmiHeader.biCompression ==
MAKEFOURCC( _T('Y'), _T('V'), _T('U'), _T('9') ) )
{
input.m_Mt.subtype = WMMEDIASUBTYPE_YVU9;
}
else
{
input.m_Mt.subtype = GUID_NULL;
}
input.m_Mt.bFixedSizeSamples = TRUE;
input.m_Mt.bTemporalCompression = FALSE;
input.m_Mt.lSampleSize = input.m_StreamInfo.dwSampleSize;
input.m_Mt.formattype = WMFORMAT_VideoInfo;
input.m_Mt.pUnk = NULL;
input.m_Mt.cbFormat = cbVideoInfo;
input.m_Mt.pbFormat = (BYTE *)pVideoInfo;
SAFE_ARRAYDELETE( pBMI );
input.m_Type = WMMEDIATYPE_Video;
//
// add to the list of AVI inputs
//
m_Inputs.Append( &input );
}
}
else
{
hr = S_OK;
}
return( hr );
}
//------------------------------------------------------------------------------
// Name: CUncompAVIToWMV::Start()
// Desc: Creates output ASF file.
//------------------------------------------------------------------------------
HRESULT CUncompAVIToWMV::Start()
{
HRESULT hr = S_OK;
if( NULL == m_pWMWriter )
{
return( E_INVALIDARG );
}
//
// Start writing ===========================================================
//
hr = m_pWMWriter->BeginWriting();
if( FAILED( hr ) )
{
if( NS_E_VIDEO_CODEC_NOT_INSTALLED == hr )
{
_tprintf( _T( "BeginWriting failed: Video Codec not installed\n" ) );
}
if( NS_E_AUDIO_CODEC_NOT_INSTALLED == hr )
{
_tprintf( _T( "BeginWriting failed: Audio Codec not installed\n" ) );
}
else if( NS_E_INVALID_OUTPUT_FORMAT == hr )
{
_tprintf( _T( "BeginWriting failed: Invalid Output Format \n" ) );
}
else if( NS_E_VIDEO_CODEC_ERROR == hr )
{
_tprintf( _T( "BeginWriting failed: An unexpected error occurred with the video codec \n" ) );
}
else if( NS_E_AUDIO_CODEC_ERROR == hr )
{
_tprintf( _T( "BeginWriting failed: An unexpected error occurred with the audio codec \n" ) );
}
else
{
_tprintf( _T( "BeginWriting failed: Error (hr=0x%08x)\n" ), hr );
}
return( hr );
}
if( m_fPreprocessing )
{
//
// PREPROCESSING : preprocess all video streams
//
hr = DoPreprocessing( );
if( FAILED( hr ) )
{
if( NS_E_INVALID_NUM_PASSES == hr )
{
_tprintf( _T( "Preprocessing failed: Invalid preprocessing passes. Don't use -m option\n" ) );
}
else
{
_tprintf( _T( "Preprocessing failed: Error (hr=0x%08x)\n" ), hr );
}
m_pWMWriter->EndWriting();
return( hr );
}
}
DWORD dwCurrentProgress = 0;
QWORD qwAllSamples = 0;
DWORD dwCurrentSample = 0;
//
// Calculate total number of samples - used for showing progress
//
CWMInput * pInput = m_Inputs.Iterate( ITER_FIRST );
while( NULL != pInput )
{
qwAllSamples += pInput->m_dwSamples;
pInput = m_Inputs.Iterate( ITER_NEXT );
}
_tprintf( _T( " 0%%--------20%%-------40%%-------60%%-------80%%-------100%%\n" ) );
_tprintf( _T( "convert: " ) );
//
// Get an AVI input with the lowest presentation time value and write it
// to the output
//
pInput = NULL;
El< CWMInput > * pElem = NULL;
pInput = m_Inputs.GetMinElement( &pElem );
while( NULL != pInput )
{
//
// Don't write sample again if the maximum duration time is reached.
//
if( pInput->m_qwPresentTime >= m_qwMaxDuration )
{
_tprintf( _T( "\nMax duration is reached" ) );
break;
}
//
// If current AVI input is finished, remove it from the list
//
if( pInput->m_dwCurrentSample >= pInput->m_dwSamples )
{
m_Inputs.Erase( pElem );
}
else
{
//
// Write samples from the chosen input
//
DWORD dwSample = pInput->m_dwCurrentSample;
hr = WriteSample( pInput );
if( FAILED( hr ) )
{
if( NS_E_INVALID_NUM_PASSES == hr )
{
_tprintf( _T( "WriteSample failed: Invalid preprocessing passes. Use -m option\n" ) );
}
else
{
_tprintf( _T( "WriteSample failed: Error (hr=0x%08x)\n" ), hr );
}
m_pWMWriter->EndWriting();
return( hr );
}
//
// Calculate current sample number to show the progress
//
dwCurrentSample += pInput->m_dwCurrentSample - dwSample;
//
// Show progress
//
while( dwCurrentProgress <= dwCurrentSample * 100 / qwAllSamples )
{
_tprintf( _T( "*" ) );
dwCurrentProgress += 2;
}
}
//
// Get next AVI input to be played
//
pInput = m_Inputs.GetMinElement( &pElem );
}
_tprintf( _T( "\n" ) );
//
// Tell the writer we're done.
//
hr = m_pWMWriter->EndWriting();
if( FAILED( hr ) )
{
_tprintf( _T( "EndWriting failed: Error (hr=0x%08x)\n" ), hr );
return( hr );
}
return( hr );
}
//------------------------------------------------------------------------------
// Name: CUncompAVIToWMV::UpdateProfile()
// Desc: Synchronizes information in a current profile with AVI inputs.
// As a result, all inputs in the profile should match AVI inputs.
//
// If there are more AVI inputs than profile inputs, additional
// streams are created and stored in the profile.
//
// If there are more profile inputs than AVI inputs, outstanding
// streams are removed from the profile.
//------------------------------------------------------------------------------
HRESULT CUncompAVIToWMV::UpdateProfile( IWMProfile * pProfile )
{
HRESULT hr = S_OK;
//
// List of streams defined in the current profile
//
CTSimpleList< CProfileStreams > ProfStreamList;
if( NULL == pProfile )
{
return( E_INVALIDARG );
}
//
// Create stream list from the current profile
//
hr = CreateProfileStreamList( pProfile, &ProfStreamList );
CProfileStreams * pProfStream = NULL;
CWMInput * pInput = NULL;
El< CProfileStreams > * pProfStreamElem = NULL;
El< CWMInput > * pInputElem = NULL;
El< CWMInput > * pInputElemRemove = NULL;
//
// Iterate through the list of AVI inputs and compare their types with inputs
// already defined in the current profile. If there is a match, corresponding
// connection names are stored in the AVI input, creating a link which is then
// used to initialize writer inputs and deliver samples to the writer.
//
pInput = m_Inputs.Iterate( ITER_FIRST, &pInputElem );
while( NULL != pInput )
{
//
// For a given AVI input, find first input in the current profile with matching media type
//
pProfStream = Find( &ProfStreamList, pInput, &pProfStreamElem );
if( NULL != pProfStream &&
NULL != pProfStream->m_pwszConnectionName )
{
//
// There is a matching input in the current profile. Save its number and connection
// name and remove it from the list of outstanding profile inputs.
//
pInput->m_pwszConnectionName = new WCHAR[ ( wcslen( pProfStream->m_pwszConnectionName ) + 1 ) ];
if( NULL == pInput->m_pwszConnectionName )
{
hr = E_OUTOFMEMORY;
break;
}
(void)StringCchCopyW( pInput->m_pwszConnectionName, wcslen( pProfStream->m_pwszConnectionName ) + 1, pProfStream->m_pwszConnectionName );
ProfStreamList.Erase( pProfStreamElem );
}
else
{
if( !m_fPreserveProfile )
{
//
// There is no matching input in the profile - create one
//
do
{
WORD wStreamNum = 0;
LPWSTR pwszConnectionName = NULL;
if( WMMEDIATYPE_Video == pInput->m_Type )
{
hr = AddVideoStream( pProfile,
(WMVIDEOINFOHEADER *)pInput->m_Mt.pbFormat,
&wStreamNum,
50,
5,
&pwszConnectionName );
}
else if( WMMEDIATYPE_Audio == pInput->m_Type )
{
hr = AddAudioStream( pProfile,
pInput->m_pWFX->nSamplesPerSec,
pInput->m_pWFX->nChannels,
pInput->m_pWFX->wBitsPerSample,
&wStreamNum,
&pwszConnectionName );
}
if( SUCCEEDED( hr ) )
{
//
// A new input in the profile has been created.
// Store the connection name with a corresponding AVI input.
//
pInput->m_pwszConnectionName = pwszConnectionName;
}
else
{
//
// Input creation failed, so mark current AVI input
// to remove from the list
//
pInputElemRemove = pInputElem;
}
} while( FALSE );
}
else
{
//
// There is no matching input in the profile.
// Since the profile cannot be extended, mark current AVI input
// to remove from the list.
//
pInputElemRemove = pInputElem;
}
}
pInput = m_Inputs.Iterate( ITER_NEXT, &pInputElem );
//
// Remove marked AVI input from the list
//
if( NULL != pInputElemRemove )
{
m_Inputs.Erase( pInputElemRemove );
pInputElemRemove = NULL;
}
}
//
// Remove all unused inputs from the profile.
//
pProfStream = ProfStreamList.Iterate( ITER_FIRST );
while( NULL != pProfStream )
{
hr = pProfile->RemoveStreamByNumber( pProfStream->m_wStreamNum );
pProfStream = ProfStreamList.Iterate( ITER_NEXT );
}
return( hr );
}
//------------------------------------------------------------------------------
// Name: CUncompAVIToWMV::CreateProfileStreamList()
// Desc: Creates a list of inputs for a given profile.
//------------------------------------------------------------------------------
HRESULT CUncompAVIToWMV::CreateProfileStreamList( IWMProfile* pProfile,
CTSimpleList< CProfileStreams >* pProfStreamList )
{
HRESULT hr = S_OK;
IWMStreamConfig * pIWMStreamConfig = NULL;
IWMMediaProps * pMediaProps = NULL;
WM_MEDIA_TYPE * pMediaType = NULL;
GUID guidInputType;
WORD wStreamNum = 0;
DWORD cbMediaType = 0;
DWORD cStreams = 0;
CProfileStreams * pProfStream = NULL;
WCHAR * pwszConnectionName = NULL;
WORD cchConnectionName = 0;
if( NULL == pProfile || NULL == pProfStreamList )
{
return( E_INVALIDARG );
}
hr = pProfile->GetStreamCount( &cStreams );
if( FAILED( hr ) )
{
return( hr );
}
//
// Create a list of inputs defined in the given profile
//
for( DWORD i = 0; i < cStreams; i++ )
{
hr = pProfile->GetStream( i, &pIWMStreamConfig );
if( FAILED( hr ) )
{
break;
}
hr = pIWMStreamConfig->GetStreamType( &guidInputType );
if( FAILED( hr ) )
{
break;
}
hr = pIWMStreamConfig->QueryInterface( IID_IWMMediaProps,
(void **)&pMediaProps );
if( FAILED( hr ) )
{
break;
}
hr = pMediaProps->GetMediaType( NULL, &cbMediaType );
if( FAILED( hr ) )
{
break;
}
pMediaType = (WM_MEDIA_TYPE *)new BYTE[ cbMediaType ];
if( NULL == pMediaType )
{
hr = E_OUTOFMEMORY;
break;
}
hr = pMediaProps->GetMediaType( pMediaType, &cbMediaType );
if( FAILED( hr ) )
{
break;
}
hr = pIWMStreamConfig->GetConnectionName( NULL, &cchConnectionName );
if( FAILED( hr ) )
{
break;
}
pwszConnectionName = new WCHAR[ cchConnectionName ];
if( NULL == pwszConnectionName )
{
hr = E_OUTOFMEMORY;
break;
}
hr = pIWMStreamConfig->GetConnectionName( pwszConnectionName,
&cchConnectionName );
if( FAILED( hr ) )
{
break;
}
hr = pIWMStreamConfig->GetStreamNumber( &wStreamNum );
if( FAILED( hr ) )
{
break;
}
//
// Only one stream for each connection is needed on this list
//
pProfStream = pProfStreamList->Iterate( ITER_FIRST );
while( NULL != pProfStream )
{
if( 0 == wcscmp( pwszConnectionName, pProfStream->m_pwszConnectionName ) )
{
//
// There is already a stream for this connection on the list; do not append
// this one. This could happen if the profile is MBR.
//
break;
}
pProfStream = pProfStreamList->Iterate( ITER_NEXT );
}
if( NULL == pProfStream )
{
//
// There is no stream for this connection on the list; append this one.
// pProfStreamList will not allocate memory to save pwszConnectionName
// and pMediaType, so the memory of pwszConnectionName and pMediaType
// should not be released now.
//
CProfileStreams ProfileStream( guidInputType,
wStreamNum,
pMediaType,
pwszConnectionName );
if ( pProfStreamList->Append( &ProfileStream ) )
{
//
// Set the pointers to NULL, so their memory will not be released
// by this function.
//
pMediaType = NULL;
pwszConnectionName = NULL;
}
}
SAFE_ARRAYDELETE( pwszConnectionName );
SAFE_ARRAYDELETE( pMediaType );
SAFE_RELEASE( pMediaProps );
SAFE_RELEASE( pIWMStreamConfig );
}
SAFE_ARRAYDELETE( pwszConnectionName );
SAFE_ARRAYDELETE( pMediaType );
SAFE_RELEASE( pMediaProps );
SAFE_RELEASE( pIWMStreamConfig );
return( hr );
}
//------------------------------------------------------------------------------
// Name: CUncompAVIToWMV::UpdateWriterInputs()
// Desc: Sets up properties of writer inputs using information
// stored in AVI inputs.
//------------------------------------------------------------------------------
HRESULT CUncompAVIToWMV::UpdateWriterInputs()
{
HRESULT hr = S_OK;
DWORD cInputs = 0;
GUID guidInputType;
IWMInputMediaProps * pInputProps = NULL;
IWMStreamConfig * pIWMStreamConfig = NULL;
WCHAR * pwszConnectionName = NULL;
WORD cchConnectionName = 0;
if( NULL == m_pWMWriter )
{
return( E_INVALIDARG );
}
hr = m_pWMWriter->GetInputCount( &cInputs );
if( FAILED( hr ) )
{
return( hr );
}
//
// Browse through all writer inputs and set properties according to
// corresponding AVI inputs
//
for( DWORD i = 0; i < cInputs; i++ )
{
hr = m_pWMWriter->GetInputProps( i, &pInputProps );
if( FAILED( hr ) )
{
break;
}
hr = pInputProps->QueryInterface( IID_IWMStreamConfig,
(void **) &pIWMStreamConfig );
if( FAILED( hr ) )
{
break;
}
hr = pIWMStreamConfig->GetStreamType( &guidInputType );
if( FAILED( hr ) )
{
return( hr );
}
//
// If this is an arbitrary stream, save its number
//
if( WMMEDIATYPE_MyArbitrary == guidInputType )
{
m_dwArbitraryInput = i;
}
else
{
hr = pIWMStreamConfig->GetConnectionName( NULL, &cchConnectionName );
if( FAILED( hr ) )
{
break;
}
pwszConnectionName = new WCHAR[ cchConnectionName ];
if( NULL == pwszConnectionName )
{
hr = E_OUTOFMEMORY;
break;
}
hr = pIWMStreamConfig->GetConnectionName( pwszConnectionName,
&cchConnectionName );
if( FAILED( hr ) )
{
break;
}
//
// Look for AVI input with the matching connection name and
// set writer input properties according to it
//
CWMInput * pInput = m_Inputs.Iterate( ITER_FIRST );
while( NULL != pInput )
{
if( 0 == wcscmp( pInput->m_pwszConnectionName, pwszConnectionName ) )
{
pInput->m_dwInput = i;
hr = pInputProps->SetMediaType( &pInput->m_Mt );
if( FAILED( hr ) )
{
break;
}
hr = m_pWMWriter->SetInputProps( i, pInputProps );
break;
}
pInput = m_Inputs.Iterate( ITER_NEXT );
}
if( NULL == pInput )
{
hr = E_INVALIDARG;
}
if ( FAILED( hr ) )
{
break;
}
}
SAFE_ARRAYDELETE( pwszConnectionName );
SAFE_RELEASE( pInputProps );
SAFE_RELEASE( pIWMStreamConfig );
}
SAFE_ARRAYDELETE( pwszConnectionName );
SAFE_RELEASE( pInputProps );
SAFE_RELEASE( pIWMStreamConfig );
return( hr );
}
//------------------------------------------------------------------------------
// Name: CUncompAVIToWMV::AddArbitraryStream()
// Desc: Adds an arbitrary stream of DWORDs to the current profile.
//------------------------------------------------------------------------------
HRESULT CUncompAVIToWMV::AddArbitraryStream( IWMProfile * pProfile )
{
HRESULT hr = S_OK;
IWMStreamConfig * pIWMStreamConfig = NULL;
IWMMediaProps * pIWMMediaProps = NULL;
WM_MEDIA_TYPE mt;
if( NULL == pProfile )
{
return( E_INVALIDARG );
}
hr = pProfile->CreateNewStream( WMMEDIATYPE_MyArbitrary, &pIWMStreamConfig );
if( FAILED( hr ) || NULL == pIWMStreamConfig )
{
return( hr );
}
ZeroMemory( &mt, sizeof( mt ) );
mt.majortype = WMMEDIATYPE_MyArbitrary;
mt.subtype = GUID_NULL;
mt.bFixedSizeSamples = FALSE;
mt.bTemporalCompression = FALSE;
mt.lSampleSize = 0;
mt.formattype = GUID_NULL;
mt.pUnk = NULL;
mt.cbFormat = 0;
mt.pbFormat = 0;
do
{
hr = pIWMStreamConfig->SetStreamName( L"Arbitrary Stream" );
if( FAILED( hr ) )
{
break;
}
hr = pIWMStreamConfig->SetConnectionName( L"Arbitrary Stream Connection" );
if( FAILED( hr ) )
{
break;
}
hr = pIWMStreamConfig->SetBitrate( 1024 );
if( FAILED( hr ) )
{
break;
}
hr = pIWMStreamConfig->QueryInterface( IID_IWMMediaProps,
(void **)&pIWMMediaProps );
if( FAILED( hr ) )
{
break;
}
hr = pIWMMediaProps->SetMediaType( &mt );
if( FAILED( hr ) )
{
break;
}
hr = pProfile->AddStream( pIWMStreamConfig );
if( FAILED( hr ) )
{
break;
}
} while( FALSE );
SAFE_RELEASE( pIWMMediaProps );
SAFE_RELEASE( pIWMStreamConfig );
return( hr );
}
//------------------------------------------------------------------------------
// Name: CUncompAVIToWMV::ListSystemProfile()
// Desc: Lists all system profiles (version 8.0), and displays their indexes and names.
//------------------------------------------------------------------------------
HRESULT CUncompAVIToWMV::ListSystemProfile()
{
HRESULT hr = S_OK;
DWORD dwIndex = 0;
DWORD cProfiles = 0;
IWMProfileManager * pIWMProfileManager = NULL;
IWMProfileManager2 * pIWMProfileManager2 = NULL;
IWMProfile * pIWMProfile = NULL;
WCHAR * pwszName = NULL;
DWORD cchName = 0;
do
{
//
// Create profile manager
//
hr = WMCreateProfileManager( &pIWMProfileManager );
if( FAILED( hr ) )
{
break;
}
hr = pIWMProfileManager->QueryInterface( IID_IWMProfileManager2,
( void ** )&pIWMProfileManager2 );
if( FAILED( hr ) )
{
break;
}
//
// Set system profile version to 8.0
//
hr = pIWMProfileManager2->SetSystemProfileVersion( WMT_VER_8_0 );
if( FAILED( hr ) )
{
break;
}
hr = pIWMProfileManager->GetSystemProfileCount( &cProfiles );
if( FAILED( hr ) )
{
break;
}
_tprintf( _T( "Profile Indexes are as follows:\n" ) );
//
// Iterate all system profiles
//
for( dwIndex = 0; dwIndex < cProfiles; dwIndex++ )
{
hr = pIWMProfileManager->LoadSystemProfile( dwIndex, &pIWMProfile );
if ( FAILED( hr ) )
{
break;
}
hr = pIWMProfile->GetName( NULL, &cchName );
if ( FAILED( hr ) )
{
break;
}
pwszName = new WCHAR[ cchName ];
if( NULL == pwszName )
{
hr = E_OUTOFMEMORY;
break;
}
hr = pIWMProfile->GetName( pwszName, &cchName );
if ( FAILED( hr ) )
{
break;
}
//
// Display the system profile index and name
//
_tprintf( _T( " %d - %ws \n" ), dwIndex + 1, pwszName );
SAFE_ARRAYDELETE( pwszName );
SAFE_RELEASE( pIWMProfile );
}
if( FAILED( hr ) )
{
break;
}
}
while( FALSE );
//
// Release all resources
//
SAFE_ARRAYDELETE( pwszName );
SAFE_RELEASE( pIWMProfile );
SAFE_RELEASE( pIWMProfileManager2 );
SAFE_RELEASE( pIWMProfileManager );
return( hr );
}
//------------------------------------------------------------------------------
// Name: CUncompAVIToWMV::LoadSystemProfile()
// Desc: Loads a system profile (version 8.0) by the index.
//------------------------------------------------------------------------------
HRESULT CUncompAVIToWMV::LoadSystemProfile( DWORD dwProfileIndex,
IWMProfile ** ppIWMProfile )
{
HRESULT hr = S_OK;
IWMProfileManager * pIWMProfileManager = NULL;
IWMProfileManager2 * pIWMProfileManager2 = NULL;
if( NULL == ppIWMProfile )
{
return( E_POINTER );
}
do
{
//
// Index starts from 0 but the user sees it as starting from 1
//
dwProfileIndex--;
//
// Create profile manager
//
hr = WMCreateProfileManager( &pIWMProfileManager );
if( FAILED( hr ) )
{
break;
}
hr = pIWMProfileManager->QueryInterface( IID_IWMProfileManager2,
( void ** )&pIWMProfileManager2 );
if( FAILED( hr ) )
{
break;
}
//
// Set system profile version to 8.0
//
hr = pIWMProfileManager2->SetSystemProfileVersion( WMT_VER_8_0 );
if( FAILED( hr ) )
{
break;
}
//
// Load the system profile by index
//
hr = pIWMProfileManager->LoadSystemProfile( dwProfileIndex,
ppIWMProfile );
if( FAILED( hr ) )
{
break;
}
}
while( FALSE );
//
// Release all resources
//
SAFE_RELEASE( pIWMProfileManager2 );
SAFE_RELEASE( pIWMProfileManager );
return( hr );
}
//------------------------------------------------------------------------------
// Name: CUncompAVIToWMV::LoadCustomProfile()
// Desc: Loads a custom profile from file.
//------------------------------------------------------------------------------
HRESULT CUncompAVIToWMV::LoadCustomProfile( LPCTSTR ptszProfileFile,
IWMProfile ** ppIWMProfile )
{
HRESULT hr = S_OK;
DWORD dwLength = 0;
DWORD dwBytesRead = 0;
IWMProfileManager * pProfileManager = NULL;
HANDLE hFile = INVALID_HANDLE_VALUE;
LPWSTR pProfile = NULL;
if( NULL == ptszProfileFile || NULL == ppIWMProfile )
{
return( E_POINTER );
}
do
{
//
// Create profile manager
//
hr = WMCreateProfileManager( &pProfileManager );
if( FAILED( hr ) )
{
break;
}
//
// Open the profile file
//
hFile = CreateFile( ptszProfileFile,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL );
if( INVALID_HANDLE_VALUE == hFile )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
break;
}
if( FILE_TYPE_DISK != GetFileType( hFile ) )
{
hr = NS_E_INVALID_NAME;
break;
}
dwLength = GetFileSize( hFile, NULL );
if( -1 == dwLength )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
break;
}
//
// Allocate memory for profile buffer
//
pProfile = (WCHAR *)new BYTE[ dwLength + sizeof(WCHAR) ];
if( NULL == pProfile )
{
hr = E_OUTOFMEMORY;
break;
}
// The buffer must be null-terminated.
memset( pProfile, 0, dwLength + sizeof(WCHAR) );
//
// Read the profile to a buffer
//
if( !ReadFile( hFile, pProfile, dwLength, &dwBytesRead, NULL ) )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
break;
}
//
// Load the profile from the buffer
//
hr = pProfileManager->LoadProfileByData( pProfile,
ppIWMProfile );
if( FAILED(hr) )
{
break;
}
}
while( FALSE );
//
// Release all resources
//
SAFE_ARRAYDELETE( pProfile );
SAFE_CLOSEFILEHANDLE( hFile );
SAFE_RELEASE( pProfileManager );
return( hr );
}
//------------------------------------------------------------------------------
// Name: CUncompAVIToWMV::CreateEmptyProfile()
// Desc: Creates an empty profile.
//------------------------------------------------------------------------------
HRESULT CUncompAVIToWMV::CreateEmptyProfile( IWMProfile ** ppIWMProfile )
{
HRESULT hr = S_OK;
IWMProfileManager* pIWMProfileManager = NULL;
if( NULL == ppIWMProfile )
{
return( E_POINTER );
}
do
{
//
// Create profile manager
//
hr = WMCreateProfileManager( &pIWMProfileManager );
if( FAILED( hr ) )
{
break;
}
hr = pIWMProfileManager->CreateEmptyProfile( WMT_VER_8_0, ppIWMProfile );
if( FAILED( hr ) )
{
break;
}
}
while( FALSE );
//
// Release all resources
//
SAFE_RELEASE( pIWMProfileManager );
return( hr );
}
//------------------------------------------------------------------------------
// Name: CUncompAVIToWMV::SaveProfile()
// Desc: Save the profile to a file.
//------------------------------------------------------------------------------
HRESULT CUncompAVIToWMV::SaveProfile( LPCTSTR ptszFileName,
IWMProfile * pIWMProfile )
{
HRESULT hr = S_OK;
IWMProfileManager * pIWMProfileManager = NULL;
DWORD dwLength = 0;
LPWSTR pBuffer = NULL;
HANDLE hFile = INVALID_HANDLE_VALUE;
DWORD dwBytesWritten = 0;
if( ( NULL == ptszFileName ) || ( NULL == pIWMProfile ) )
{
return( E_INVALIDARG );
}
do
{
//
// Create profile manager
//
hr = WMCreateProfileManager( &pIWMProfileManager );
if( FAILED( hr ) )
{
break;
}
//
// Save profile to a buffer
//
hr = pIWMProfileManager->SaveProfile( pIWMProfile, NULL, &dwLength );
if( FAILED( hr ) )
{
break;
}
pBuffer = new WCHAR[ dwLength ];
if( NULL == pBuffer )
{
hr = E_OUTOFMEMORY;
break;
}
hr = pIWMProfileManager->SaveProfile( pIWMProfile, pBuffer, &dwLength );
if( FAILED( hr ) )
{
break;
}
hFile = CreateFile( ptszFileName,
GENERIC_WRITE,
0,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL );
if( INVALID_HANDLE_VALUE == hFile )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
break;
}
if( FILE_TYPE_DISK != GetFileType( hFile ) )
{
hr = NS_E_INVALID_NAME;
break;
}
//
// Write profile buffer to file
//
if( !WriteFile( hFile, pBuffer, dwLength * sizeof(WCHAR), &dwBytesWritten, NULL) ||
dwLength*sizeof(WCHAR) != dwBytesWritten )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
break;
}
} while( FALSE );
SAFE_CLOSEFILEHANDLE( hFile );
SAFE_ARRAYDELETE( pBuffer );
SAFE_RELEASE( pIWMProfileManager );
return( hr );
}
//------------------------------------------------------------------------------
// Name: CUncompAVIToWMV::DoPreprocessing()
// Desc: Perform preprocessing on all input video and audio streams.
// This method does not update any stream info on the AVI input list.
//------------------------------------------------------------------------------
HRESULT CUncompAVIToWMV::DoPreprocessing()
{
HRESULT hr = S_OK;
DWORD cCurrentSamples = 0;
DWORD dwCurrentSample = 0;
LONG cSamplesToRead = 0;
INSSBuffer * pSample = NULL;
BYTE * pbBuffer = NULL;
DWORD cbBuffer = 0;
QWORD qwPresentTime = 0;
DWORD dwNumPasses = 0;
DWORD dwCurrentProgress = 0;
PAVISTREAM pCurrentStream = NULL;
DWORD dwCurrentInput = 0;
AVISTREAMINFO CurrentStreamInfo;
El< CWMInput > * pStreamElem = NULL;
CWMInput * pStream = NULL;
if( !m_fPreprocessing )
{
return( E_FAIL );
}
if( NULL == m_pWMWriter || NULL == m_pIWMWriterPreprocess )
{
return( E_INVALIDARG );
}
pStream = m_Inputs.Iterate( ITER_FIRST, &pStreamElem );
while( SUCCEEDED( hr ) && NULL != pStream )
{
if( WMMEDIATYPE_Video == pStream->m_Type ||
WMMEDIATYPE_Audio == pStream->m_Type )
{
dwCurrentInput = pStream->m_dwInput;
pCurrentStream = pStream->m_pAVIStream;
CurrentStreamInfo = pStream->m_StreamInfo;
cCurrentSamples = pStream->m_dwSamples;
hr = m_pIWMWriterPreprocess->GetMaxPreprocessingPasses( dwCurrentInput,
0,
&dwNumPasses );
if( FAILED( hr ) )
{
return( hr );
}
if( 0 != dwNumPasses )
{
//
// Use the recommended number of passes
//
hr = m_pIWMWriterPreprocess->SetNumPreprocessingPasses( dwCurrentInput,
0,
dwNumPasses );
if( FAILED( hr ) )
{
return( hr );
}
_tprintf( _T( " 0%%--------20%%-------40%%-------60%%-------80%%-------100%%\n" ) );
_tprintf( _T( "preprocess: " ) );
while( 0 != dwNumPasses )
{
if( WMMEDIATYPE_Audio == pStream->m_Type )
{
//
// We want to read half second of audio at a time
//
cSamplesToRead = ( pStream->m_pWFX->nAvgBytesPerSec * 4 )
/ ( pStream->m_pWFX->nChannels * pStream->m_pWFX->wBitsPerSample );
}
else if( WMMEDIATYPE_Video == pStream->m_Type )
{
cSamplesToRead = AVISTREAMREAD_CONVENIENT;
}
hr = m_pIWMWriterPreprocess->BeginPreprocessingPass( dwCurrentInput, 0 );
if( FAILED( hr ) )
{
return( hr );
}
dwCurrentSample = 0;
dwCurrentProgress = 0;
while( dwCurrentSample < cCurrentSamples )
{
LONG cbSample = 0;
LONG cSamples = 0;
hr = AVIStreamRead( pCurrentStream,
dwCurrentSample,
cSamplesToRead,
0,
0,
&cbSample,
&cSamples );
assert( SUCCEEDED( hr ) );
if( FAILED( hr ) )
{
return( hr );
}
if( 0 < cbSample )
{
hr = m_pWMWriter->AllocateSample( cbSample, &pSample );
assert( SUCCEEDED( hr ) );
if( SUCCEEDED( hr ) )
{
hr = pSample->GetBufferAndLength( &pbBuffer, &cbBuffer );
assert( SUCCEEDED( hr ) && (long)cbBuffer >= cbSample );
}
if( SUCCEEDED( hr ) )
{
hr = AVIStreamRead( pCurrentStream,
dwCurrentSample,
cSamples,
pbBuffer,
cbBuffer,
&cbSample,
&cSamples );
assert( SUCCEEDED( hr ) );
}
if( SUCCEEDED( hr ) )
{
hr = pSample->SetLength( cbSample );
assert( SUCCEEDED( hr ) );
}
if( SUCCEEDED( hr ) )
{
hr = m_pIWMWriterPreprocess->PreprocessSample( dwCurrentInput, // input number
qwPresentTime, // presentation time
0, // flags
pSample ); // the data
assert( SUCCEEDED( hr ) );
}
SAFE_RELEASE( pSample );
if( FAILED( hr ) )
{
return( hr );
}
}
else
{
//
// 0-sized sample; that's OK, just skip the frame.
//
cbBuffer = 0;
cSamples = 1;
}
dwCurrentSample += cSamples;
//
// Update presentation time for this AVI input.
// Note: The writer expects presentation times to be in 100-nanosecond units.
//
if( WMMEDIATYPE_Audio == pStream->m_Type )
{
qwPresentTime += 10000000 * (QWORD)cbBuffer
/ pStream->m_pWFX->nAvgBytesPerSec;
}
else if( WMMEDIATYPE_Video == pStream->m_Type )
{
qwPresentTime = 10000000 * (QWORD)dwCurrentSample
* CurrentStreamInfo.dwScale
/ CurrentStreamInfo.dwRate;
}
while( dwCurrentProgress <= dwCurrentSample * 100 / cCurrentSamples )
{
_tprintf( _T( "*" ) );
dwCurrentProgress += 2;
}
//
// Don't do preprocessing again if the maximum duration time is reached.
//
if( qwPresentTime >= m_qwMaxDuration )
{
_tprintf( _T( "\nMax duration is reached" ) );
break;
}
}
hr = m_pIWMWriterPreprocess->EndPreprocessingPass( dwCurrentInput, 0 );
assert( SUCCEEDED( hr ) );
if( FAILED( hr ) )
{
return( hr );
}
dwNumPasses--;
}
_tprintf( _T( "\n" ) );
}
}
pStream = m_Inputs.Iterate( ITER_NEXT, &pStreamElem );
}
return( hr );
}
//------------------------------------------------------------------------------
// Name: CUncompAVIToWMV::WriteSample()
// Desc: Writes next sample from AVI input to WMWriter. Updates presentation
// time for AVI input
//------------------------------------------------------------------------------
HRESULT CUncompAVIToWMV::WriteSample( CWMInput * pInput )
{
HRESULT hr = S_OK;
LONG cbSample = 0;
LONG cSamples = 0;
INSSBuffer * pSample = NULL;
BYTE * pbBuffer = NULL;
DWORD cbBuffer = 0;
LONG cSamplesToRead = 0;
if( NULL == pInput )
{
return( E_INVALIDARG );
}
if( WMMEDIATYPE_Audio == pInput->m_Type )
{
//
// We want to read one half-second of audio at a time
//
cSamplesToRead = ( pInput->m_pWFX->nAvgBytesPerSec * 4 )
/ ( pInput->m_pWFX->nChannels * pInput->m_pWFX->wBitsPerSample );
}
else if( WMMEDIATYPE_Video == pInput->m_Type )
{
cSamplesToRead = AVISTREAMREAD_CONVENIENT;
}
hr = AVIStreamRead( pInput->m_pAVIStream,
pInput->m_dwCurrentSample,
cSamplesToRead,
0,
0,
&cbSample,
&cSamples );
if( FAILED( hr ) )
{
return( hr );
}
if( 0 < cbSample )
{
hr = m_pWMWriter->AllocateSample( cbSample, &pSample );
assert( SUCCEEDED( hr ) );
if( SUCCEEDED( hr ) )
{
hr = pSample->GetBufferAndLength( &pbBuffer, &cbBuffer );
assert( SUCCEEDED( hr ) );
}
if( SUCCEEDED( hr ) )
{
hr = AVIStreamRead( pInput->m_pAVIStream,
pInput->m_dwCurrentSample,
cSamples,
pbBuffer,
cbBuffer,
&cbSample,
&cSamples );
assert( SUCCEEDED( hr ) );
}
if( SUCCEEDED( hr ) )
{
hr = pSample->SetLength( cbSample );
assert( SUCCEEDED( hr ) );
}
if( SUCCEEDED( hr ) )
{
//
// Add SMPTE time code to the first video stream.
//
if( pInput->m_fAddSMPTE )
{
hr = AddSMPTETimeCode( pSample, pInput->m_qwPresentTime );
assert( SUCCEEDED( hr ) );
}
}
if( SUCCEEDED( hr ) )
{
hr = m_pWMWriter->WriteSample( pInput->m_dwInput, // input number
pInput->m_qwPresentTime, // presentation time
0, // flags
pSample ); // the data
}
SAFE_RELEASE( pSample );
if( FAILED( hr ) )
{
return( hr );
}
}
else // 0 == cbSample
{
if( WMMEDIATYPE_Video == pInput->m_Type )
{
//
// 0-sized sample; that's OK, just skip the frame.
//
cSamples = 1;
}
else if( WMMEDIATYPE_Audio == pInput->m_Type )
{
return( hr );
}
}
if( m_fArbitrary && WMMEDIATYPE_Audio == pInput->m_Type )
{
//
// Arbitrary stream. This is for example purposes; there is no need
// to send an arbitrary stream for an audio stream.
//
hr = m_pWMWriter->AllocateSample( sizeof(DWORD), &pSample );
assert( SUCCEEDED( hr ) );
if( SUCCEEDED( hr ) )
{
hr = pSample->GetBuffer( &pbBuffer );
assert( SUCCEEDED( hr ) );
}
if( SUCCEEDED( hr ) )
{
memcpy( pbBuffer, &pInput->m_dwCurrentSample, sizeof(DWORD) );
hr = m_pWMWriter->WriteSample( m_dwArbitraryInput, // input number
pInput->m_qwPresentTime, // presentation time
0, // flags
pSample ); // the data
}
SAFE_RELEASE( pSample );
}
pInput->m_dwCurrentSample += cSamples;
//
// Update presentation time for this AVI input.
// Note: The writer expects presentation times to be in 100-nanosecond units.
//
if( WMMEDIATYPE_Audio == pInput->m_Type )
{
pInput->m_qwPresentTime += 10000000 * (QWORD)cbBuffer
/ pInput->m_pWFX->nAvgBytesPerSec;
}
else if( WMMEDIATYPE_Video == pInput->m_Type )
{
pInput->m_qwPresentTime = 10000000 * (QWORD)pInput->m_dwCurrentSample
* pInput->m_StreamInfo.dwScale
/ pInput->m_StreamInfo.dwRate;
}
return( hr );
}
//------------------------------------------------------------------------------
// Name: CUncompAVIToWMV::SetupSMPTE()
// Desc: 1. Finds the video stream to which SMPTE code will be written.
// 2. Adds a data unit extension to this video stream to store the SMPTE code.
// 3. Saves the frame rate and sets the m_fAddSMPTE flag of this video stream.
//------------------------------------------------------------------------------
HRESULT CUncompAVIToWMV::SetupSMPTE( IWMProfile * pProfile )
{
HRESULT hr = S_OK;
IWMStreamConfig * pIWMStreamConfig = NULL;
IWMStreamConfig2 * pIWMStreamConfig2 = NULL;
IWMMediaProps * pMediaProps = NULL;
WM_MEDIA_TYPE * pMediaType = NULL;
DWORD cbMediaType = 0;
DWORD cStreams = 0;
LPWSTR pwszConnectionName = NULL;
WORD cchConnectionName = 0;
GUID guidStreamType;
if( NULL == pProfile )
{
return( E_INVALIDARG );
}
hr = pProfile->GetStreamCount( &cStreams );
if( FAILED( hr ) )
{
return( hr );
}
//
// Find the first video stream.
// Currently, SMPTE only supports one video stream.
//
for( DWORD i = 0; i < cStreams; i++ )
{
hr = pProfile->GetStream( i, &pIWMStreamConfig );
if( FAILED( hr ) )
{
break;
}
hr = pIWMStreamConfig->GetStreamType( &guidStreamType );
if( FAILED( hr ) )
{
break;
}
if( WMMEDIATYPE_Video == guidStreamType )
{
break;
}
SAFE_RELEASE( pIWMStreamConfig );
}
if( FAILED( hr ) )
{
SAFE_RELEASE( pIWMStreamConfig );
return( hr );
}
if( NULL == pIWMStreamConfig )
{
return( E_INVALIDARG );
}
do
{
//
// We need to call IWMStreamConfig2::AddDataUnitExtension to add
// a data unit extension to store SMPTE code.
//
hr = pIWMStreamConfig->QueryInterface( IID_IWMStreamConfig2,
(void **)&pIWMStreamConfig2 );
if( FAILED( hr ) )
{
break;
}
hr = pIWMStreamConfig2->AddDataUnitExtension( WM_SampleExtensionGUID_Timecode,
sizeof(WMT_TIMECODE_EXTENSION_DATA),
NULL,
0 );
if( FAILED( hr ) )
{
break;
}
//
// Update the profile.
//
hr = hr = pProfile->ReconfigStream( pIWMStreamConfig );
if( FAILED( hr ) )
{
break;
}
//
// Get the frame rate and input number of this video stream
//
hr = pIWMStreamConfig->QueryInterface( IID_IWMMediaProps,
(void **)&pMediaProps );
if( FAILED( hr ) )
{
break;
}
hr = pMediaProps->GetMediaType( NULL, &cbMediaType );
if( FAILED( hr ) )
{
break;
}
pMediaType = (WM_MEDIA_TYPE *) new BYTE[ cbMediaType ];
if( !pMediaType )
{
hr = E_OUTOFMEMORY;
break;
}
hr = pMediaProps->GetMediaType( pMediaType, &cbMediaType );
if( FAILED( hr ) )
{
break;
}
//
// Save the AvgTimePerFrame of this stream
//
WMVIDEOINFOHEADER * pVIH = (WMVIDEOINFOHEADER *) pMediaType->pbFormat;
m_qwSMPTEAvgTimePerFrame = pVIH->AvgTimePerFrame;
hr = pIWMStreamConfig->GetConnectionName( NULL, &cchConnectionName );
if( FAILED( hr ) )
{
break;
}
pwszConnectionName = new WCHAR[ cchConnectionName + 1 ];
if( NULL == pwszConnectionName )
{
hr = E_OUTOFMEMORY;
break;
}
hr = pIWMStreamConfig->GetConnectionName( pwszConnectionName,
&cchConnectionName );
if( FAILED( hr ) )
{
break;
}
//
// Look for an AVI input with the matching connection name and
// set the m_fAddSMPTE flag
//
CWMInput * pInput = m_Inputs.Iterate( ITER_FIRST );
while( NULL != pInput )
{
if( 0 == wcscmp( pInput->m_pwszConnectionName, pwszConnectionName ) )
{
break;
}
pInput = m_Inputs.Iterate( ITER_NEXT );
}
if( NULL == pInput )
{
hr = E_INVALIDARG;
break;
}
else
{
pInput->m_fAddSMPTE = TRUE;
}
} while( FALSE );
SAFE_ARRAYDELETE( pMediaType );
SAFE_ARRAYDELETE( pwszConnectionName );
SAFE_RELEASE( pMediaProps );
SAFE_RELEASE( pIWMStreamConfig2 );
SAFE_RELEASE( pIWMStreamConfig );
return( hr );
}
//------------------------------------------------------------------------------
// Name: CUncompAVIToWMV::AddSMPTETimeCode()
// Desc: Adds SMPTE time code.
//------------------------------------------------------------------------------
HRESULT CUncompAVIToWMV::AddSMPTETimeCode( INSSBuffer * pSample,
QWORD qwPresTime )
{
HRESULT hr = S_OK;
INSSBuffer3 * pNSSBuffer3 = NULL;
if( NULL == pSample || 0 == m_qwSMPTEAvgTimePerFrame )
{
return( E_INVALIDARG );
}
hr = pSample->QueryInterface( IID_INSSBuffer3, (void**)&pNSSBuffer3 );
if( SUCCEEDED( hr ) )
{
WMT_TIMECODE_EXTENSION_DATA SMPTEExtData;
DWORD dwTimeCode;
DWORD dwFrameNumber;
ZeroMemory( &SMPTEExtData, sizeof( SMPTEExtData ) );
//
// wRange specifies the range to which the time code belongs.
// You can change 86400 to other small values if you like.
//
SMPTEExtData.wRange = (WORD)( qwPresTime / 10000000 / 86400 );
dwTimeCode = (DWORD)( ( qwPresTime / 10000000 ) % 86400 ); // time in seconds
dwFrameNumber = (DWORD)( ( qwPresTime % 10000000 ) / m_qwSMPTEAvgTimePerFrame );
//
// Time code is stored so that the hexadecimal value is read as if
// it were a decimal value. That is, the time code value 0x01133512
// does not represent decimal 18035986, rather it specifies 1 hour,
// 13 minutes, 35 seconds, and 12 frames.
//
SMPTEExtData.dwTimecode = ( ( dwTimeCode / 3600 ) << 24 ) | // Hours
( ( dwTimeCode / 60 % 60 ) << 16 ) | // Minutes
( ( dwTimeCode % 60 ) << 8 ) | // Seconds
dwFrameNumber; // Frames
hr = pNSSBuffer3->SetProperty( WM_SampleExtensionGUID_Timecode,
(BYTE *) &SMPTEExtData,
sizeof( SMPTEExtData ) );
}
SAFE_RELEASE( pNSSBuffer3 );
return( hr );
}
//------------------------------------------------------------------------------
// Name: CUncompAVIToWMV::SetAttribute()
// Desc: Add attribute to header info.
//------------------------------------------------------------------------------
HRESULT CUncompAVIToWMV::SetAttribute( CONTENT_DESC* pCntDesc )
{
HRESULT hr = S_OK;
WMT_ATTR_DATATYPE enumDataType;
DWORD dwValueLength = 0;
BYTE * pbValue = NULL;
IWMHeaderInfo * pHeaderInfo = NULL;
WCHAR * pwszName = NULL;
do
{
//
// Determine the attribute type and set the length and value properly
//
if( 0 == _tcsicmp( _T( "string" ), pCntDesc->ptszType ) )
{
enumDataType = WMT_TYPE_STRING;
//
// Convert TCHAR string value to WCHAR string value
//
hr = ConvertTCharToWChar( pCntDesc->ptszValue, (WCHAR **)&pbValue );
if( FAILED( hr ) )
{
_tprintf( _T( "Can not convert attribute value to wchar string (hr=0x%08x)\n" ), hr );
break;
}
dwValueLength = ( wcslen( (WCHAR *)pbValue ) + 1 ) * sizeof(WCHAR);
}
else if( 0 == _tcsicmp( _T( "qword" ), pCntDesc->ptszType ) )
{
enumDataType = WMT_TYPE_QWORD;
dwValueLength = sizeof(QWORD);
pbValue = new BYTE[ dwValueLength ];
if( NULL == pbValue )
{
hr = E_OUTOFMEMORY;
_tprintf( _T( "Insufficient Memory\n") );
break;
}
*(QWORD *)pbValue = _ttoi64( pCntDesc->ptszValue );
}
else if( 0 == _tcsicmp( _T( "dword" ), pCntDesc->ptszType ) )
{
enumDataType = WMT_TYPE_DWORD;
dwValueLength = sizeof(DWORD);
pbValue = new BYTE[ dwValueLength ];
if( NULL == pbValue )
{
hr = E_OUTOFMEMORY;
_tprintf( _T( "Insufficient Memory\n") );
break;
}
*(DWORD *)pbValue = _ttoi( pCntDesc->ptszValue );
}
else if( 0 == _tcsicmp( _T( "word" ), pCntDesc->ptszType ) )
{
enumDataType = WMT_TYPE_WORD;
dwValueLength = sizeof( WORD );
pbValue = new BYTE[ dwValueLength ];
if( NULL == pbValue )
{
hr = E_OUTOFMEMORY;
_tprintf( _T( "Insufficient Memory\n") );
break;
}
*(WORD *)pbValue = (WORD)_ttoi( pCntDesc->ptszValue );
}
else if( 0 == _tcsicmp( _T( "binary" ), pCntDesc->ptszType ) )
{
//
// If thehe binary data is read as Unicode, the string will have unwanted
// characters in it. For example, the binary data "abcd" will be read
// as "a\0b\0c\0d\0". Its essential to convert the data back to
// binary by removing all the unwanted characters.
//
enumDataType = WMT_TYPE_BINARY;
hr = ConvertTCharToChar( pCntDesc->ptszValue, (CHAR **)&pbValue );
if( FAILED( hr ) )
{
_tprintf( _T( "Can not convert attribute value to char string (hr=0x%08x)\n" ), hr );
break;
}
dwValueLength = strlen( (CHAR *)pbValue );
}
else if( 0 == _tcsicmp( _T( "bool" ), pCntDesc->ptszType ) )
{
enumDataType = WMT_TYPE_BOOL;
dwValueLength = sizeof( BOOL );
pbValue = new BYTE[ dwValueLength ];
if( NULL == pbValue )
{
hr = E_OUTOFMEMORY;
_tprintf( _T( "Insufficient Memory\n") );
break;
}
if( 0 == _tcsicmp( _T( "true" ), pCntDesc->ptszValue ) )
{
*(BOOL *)pbValue = TRUE;
}
else if( 0 == _tcsicmp( _T( "false" ), pCntDesc->ptszValue ) )
{
*(BOOL *)pbValue = TRUE;
}
else
{
hr = E_INVALIDARG;
_tprintf( _T( "Invalid boolean attribute\n") );
break;
}
}
else
{
hr = E_INVALIDARG;
_tprintf( _T( "Invalid attribute type\n") );
break;
}
//
// Get the header info interface and give it this new attribute
//
hr = m_pWMWriter->QueryInterface( IID_IWMHeaderInfo, (void **)&pHeaderInfo );
if( FAILED( hr ) )
{
_tprintf( _T( "Failed to query IWMHeaderInfo interface (hr=0x%08x)\n" ), hr );
break;
}
//
// Convert TCHAR string value to WCHAR string value
//
hr = ConvertTCharToWChar( pCntDesc->ptszName, &pwszName );
if( FAILED( hr ) )
{
_tprintf( _T( "Failed to convert attribute name to wchar string (hr=0x%08x)\n" ), hr );
break;
}
hr = pHeaderInfo->SetAttribute( pCntDesc->wStreamNum,
pwszName,
enumDataType,
pbValue,
(WORD)dwValueLength );
if( FAILED( hr ) )
{
_tprintf( _T( "Failed to set attribute %s (hr=0x%08x)\n" ), pCntDesc->ptszName, hr );
break;
}
}
while( FALSE );
SAFE_ARRAYDELETE( pwszName );
SAFE_ARRAYDELETE( pbValue );
SAFE_RELEASE( pHeaderInfo );
return( hr );
}
//------------------------------------------------------------------------------
// Name: CUncompAVIToWMV::SetDRM()
// Desc: Adds DRM attributes.
// When SUPPORT_DRM is not defined, link to wmvcore.lib.
// When SUPPORT_DRM is defined, link to wmstubdrm.lib, which
// contains the certificate that you must first acquire from
// Microsoft before working with DRM.
//------------------------------------------------------------------------------
HRESULT CUncompAVIToWMV::SetDRM()
{
#ifdef SUPPORT_DRM
//
// Tell the writer to turn on digital rights management by setting the use_drm attribute to 1
//
HRESULT hr = S_OK;
IWMHeaderInfo * pHeaderInfo = NULL;
do
{
hr = m_pWMWriter->QueryInterface( IID_IWMHeaderInfo, (void **)&pHeaderInfo );
if( FAILED( hr ) )
{
_tprintf( _T( "Failed to query IWMHeaderInfo interface (hr=0x%08x)\n" ), hr );
break;
}
BOOL fUseDRM = TRUE;
hr = pHeaderInfo->SetAttribute( 0,
g_wszWMUse_DRM,
WMT_TYPE_BOOL,
(BYTE *) &fUseDRM,
sizeof( BOOL ) );
if( FAILED( hr ) )
{
_tprintf( _T( "Failed to set Use_DRM attribute (hr=0x%08x)\n" ), hr );
break;
}
DWORD dwDRMFlags = WMT_RIGHT_PLAYBACK | WMT_RIGHT_COPY_TO_NON_SDMI_DEVICE | WMT_RIGHT_COPY_TO_CD;
hr = pHeaderInfo->SetAttribute( 0,
g_wszWMDRM_Flags,
WMT_TYPE_DWORD,
(BYTE *) &dwDRMFlags,
sizeof(DWORD) );
if( FAILED( hr ) )
{
_tprintf( _T( "Failed to set DRM_Flags attribute (hr=0x%08x)\n" ), hr );
break;
}
DWORD dwDRMLevel = 150;
hr = pHeaderInfo->SetAttribute( 0,
g_wszWMDRM_Level,
WMT_TYPE_DWORD,
(BYTE *) &dwDRMLevel,
sizeof(DWORD) );
if( FAILED( hr ) )
{
_tprintf( _T( "Failed to set DRM_Level attribute (hr=0x%08x)\n" ), hr );
break;
}
}
while( FALSE );
SAFE_RELEASE( pHeaderInfo );
return( hr );
#else
_tprintf( _T( "DRM is not implemented\n") );
return( E_NOTIMPL );
#endif
}
//------------------------------------------------------------------------------
// Name: CUncompAVIToWMV::GetTokensFromFile()
// Desc: Creates a list of tokens from an ASCII input file.
// Tokens should be separated by CR/LF..
//------------------------------------------------------------------------------
HRESULT CUncompAVIToWMV::GetTokensFromFile( LPCTSTR ptszFileName,
__out_ecount(*TokenNum) LPTSTR pptszTokens[],
int * TokenNum )
{
HRESULT hr = S_OK;
HANDLE hFile = NULL;
LPSTR pszBuffer = NULL;
DWORD dwFileSize = 0;
DWORD dwRead = 0;
int nTokenCount = 0;
if( NULL == ptszFileName || NULL == pptszTokens || NULL == TokenNum )
{
return( E_INVALIDARG );
}
hFile = CreateFile( ptszFileName,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL );
if( INVALID_HANDLE_VALUE == hFile )
{
return( HRESULT_FROM_WIN32( GetLastError() ) );
}
do
{
if( FILE_TYPE_DISK != GetFileType( hFile ) )
{
hr = NS_E_INVALID_NAME;
break;
}
dwFileSize = GetFileSize( hFile, NULL );
if( INVALID_FILE_SIZE == dwFileSize )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
break;
}
pszBuffer = ( LPSTR ) VirtualAlloc( 0,
dwFileSize + 1,
MEM_COMMIT,
PAGE_READWRITE);
if( NULL == pszBuffer )
{
hr = E_OUTOFMEMORY;
break;
}
if( !ReadFile( hFile, pszBuffer, dwFileSize, &dwRead, NULL ) ||
dwFileSize != dwRead )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
break;
}
pszBuffer[ dwFileSize ] = 0x0d;
nTokenCount = 0;
char* context = NULL;
LPSTR pToken = strtok_s( pszBuffer, "\r\n", &context);
while( NULL != pToken )
{
hr = ConvertCharToTChar( pToken, &pptszTokens[ nTokenCount ] );
if( FAILED( hr ) )
{
_tprintf( _T( "Failed to convert input file name to tchar string (hr=0x%08x)\n" ), hr );
break;
}
nTokenCount ++;
if( nTokenCount >= *TokenNum )
{
break;
}
char* context = NULL;
pToken = strtok_s( NULL, "\r\n", &context);
}
*TokenNum = nTokenCount;
} while( FALSE );
if( NULL != hFile )
{
CloseHandle( hFile );
}
if( NULL != pszBuffer )
{
VirtualFree( (LPVOID)pszBuffer, 0, MEM_RELEASE );
}
return( hr );
}
////////////////////////////////////////////////////////////////////////////////
// The following functions work in a very similar manner to their counterparts
// in the WmGenProfile sample. They are used to add a video or audio stream
// to a profile.
////////////////////////////////////////////////////////////////////////////////
//------------------------------------------------------------------------------
// Name: CUncompAVIToWMV::SetStreamBasics()
// Desc: Creates and configures a stream.
//------------------------------------------------------------------------------
HRESULT CUncompAVIToWMV::SetStreamBasics( IWMStreamConfig * pIWMStreamConfig,
IWMProfile * pIWMProfile,
__in LPWSTR pwszStreamName,
__in LPWSTR pwszConnectionName,
DWORD dwBitrate,
WM_MEDIA_TYPE * pmt )
{
HRESULT hr = S_OK;
IWMMediaProps * pIWMMediaProps = NULL;
IWMStreamConfig * pIWMStreamConfig2 = NULL;
WORD wStreamNum = 0;
if( NULL == pIWMStreamConfig || NULL == pIWMProfile || NULL == pmt )
{
return( E_INVALIDARG );
}
do
{
hr = pIWMProfile->CreateNewStream( pmt->majortype, &pIWMStreamConfig2 );
if( FAILED( hr ) )
{
break;
}
hr = pIWMStreamConfig2->GetStreamNumber( &wStreamNum );
SAFE_RELEASE( pIWMStreamConfig2 );
if( FAILED( hr ) )
{
break;
}
hr = pIWMStreamConfig->SetStreamNumber( wStreamNum );
if( FAILED( hr ) )
{
break;
}
hr = pIWMStreamConfig->SetStreamName( pwszStreamName );
if( FAILED( hr ) )
{
break;
}
hr = pIWMStreamConfig->SetConnectionName( pwszConnectionName );
if( FAILED( hr ) )
{
break;
}
hr = pIWMStreamConfig->SetBitrate( dwBitrate );
if( FAILED( hr ) )
{
break;
}
hr = pIWMStreamConfig->QueryInterface( IID_IWMMediaProps,
(void **) &pIWMMediaProps );
if( FAILED( hr ) )
{
break;
}
hr = pIWMMediaProps->SetMediaType( pmt );
if( FAILED( hr ) )
{
break;
}
}
while( FALSE );
SAFE_RELEASE( pIWMMediaProps );
return( hr );
}
//------------------------------------------------------------------------------
// Name: CUncompAVIToWMV::AddVideoStream()
// Desc: Configures and adds a video stream.
//------------------------------------------------------------------------------
HRESULT CUncompAVIToWMV::AddVideoStream( IWMProfile * pIWMProfile,
WMVIDEOINFOHEADER * pInputVIH,
WORD * pwStreamNum,
DWORD dwQuality,
DWORD dwSecPerKey,
__out LPWSTR * pwszConnectionName )
{
HRESULT hr = S_OK;
IWMProfileManager * pManager = NULL;
IWMCodecInfo * pCodecInfo = NULL;
IWMStreamConfig * pStreamConfig = NULL;
IWMVideoMediaProps * pMediaProps = NULL;
WM_MEDIA_TYPE * pMediaType = NULL;
if( NULL == pIWMProfile ||
NULL == pInputVIH ||
NULL == pwStreamNum ||
NULL == pwszConnectionName )
{
return( E_INVALIDARG );
}
do
{
hr = WMCreateProfileManager( &pManager );
if( FAILED( hr ) )
{
break;
}
hr = pManager->QueryInterface( IID_IWMCodecInfo,
(void **) &pCodecInfo );
if( FAILED( hr ) )
{
break;
}
DWORD cCodecs;
hr = pCodecInfo->GetCodecInfoCount( WMMEDIATYPE_Video, &cCodecs );
if( FAILED( hr ) )
{
break;
}
//
// Search from the last codec because the last codec usually
// is the newest codec.
//
for( int i = cCodecs-1; i >= 0; i-- )
{
DWORD cFormats;
hr = pCodecInfo->GetCodecFormatCount( WMMEDIATYPE_Video,
i,
&cFormats );
if( FAILED( hr ) )
{
break;
}
DWORD j;
for( j = 0; j < cFormats; j++ )
{
SAFE_RELEASE( pStreamConfig );
hr = pCodecInfo->GetCodecFormat( WMMEDIATYPE_Video,
i,
j,
&pStreamConfig );
if( FAILED( hr ) )
{
break;
}
SAFE_RELEASE( pMediaProps );
hr = pStreamConfig->QueryInterface( IID_IWMVideoMediaProps,
(void **) &pMediaProps );
if( FAILED( hr ) )
{
break;
}
DWORD cbMT;
hr = pMediaProps->GetMediaType( NULL, &cbMT );
if( FAILED( hr ) )
{
break;
}
SAFE_ARRAYDELETE( pMediaType );
pMediaType = (WM_MEDIA_TYPE *) new BYTE[ cbMT ];
if( !pMediaType )
{
hr = E_OUTOFMEMORY;
break;
}
hr = pMediaProps->GetMediaType( pMediaType, &cbMT );
if( FAILED( hr ) )
{
break;
}
if( pMediaType->formattype != WMFORMAT_VideoInfo )
{
SAFE_RELEASE( pStreamConfig );
continue;
}
WMVIDEOINFOHEADER * pVIH = (WMVIDEOINFOHEADER *) pMediaType->pbFormat;
if( pVIH->bmiHeader.biBitCount >= pInputVIH->bmiHeader.biBitCount )
{
break;
}
SAFE_RELEASE( pStreamConfig );
}
if( FAILED( hr ) || NULL != pStreamConfig )
{
break;
}
}
if( FAILED( hr ) )
{
break;
}
if( NULL == pStreamConfig )
{
hr = NS_E_VIDEO_CODEC_NOT_INSTALLED;
break;
}
WMVIDEOINFOHEADER * pVIH = (WMVIDEOINFOHEADER *) pMediaType->pbFormat;
//
// Set the target bitrate to 1/30 of the source bitrate
// since it's compressing.
//
pVIH->dwBitRate = pInputVIH->dwBitRate / 30;
pVIH->rcSource.right = pInputVIH->rcSource.right;
pVIH->rcSource.bottom = pInputVIH->rcSource.bottom;
pVIH->rcTarget.right = pInputVIH->rcTarget.right;
pVIH->rcTarget.bottom = pInputVIH->rcTarget.bottom;
pVIH->bmiHeader.biWidth = pInputVIH->bmiHeader.biWidth;
pVIH->bmiHeader.biHeight = pInputVIH->bmiHeader.biHeight;
pVIH->AvgTimePerFrame = pInputVIH->AvgTimePerFrame;
hr = pMediaProps->SetQuality( dwQuality );
if( FAILED( hr ) )
{
break;
}
hr = pMediaProps->SetMaxKeyFrameSpacing( 10000000 * (QWORD)dwSecPerKey );
if( FAILED( hr ) )
{
break;
}
hr = SetStreamBasics( pStreamConfig,
pIWMProfile,
L"Video Stream",
L"Video",
pVIH->dwBitRate, pMediaType );
if( FAILED( hr ) )
{
break;
}
*pwszConnectionName = new WCHAR[ wcslen( wszDefaultConnectionName ) + 4 ];
if( NULL == *pwszConnectionName )
{
hr = E_OUTOFMEMORY;
break;
}
hr = pIWMProfile->AddStream( pStreamConfig );
if( FAILED( hr ) )
{
SAFE_ARRAYDELETE( *pwszConnectionName );
break;
}
hr = pStreamConfig->GetStreamNumber( pwStreamNum );
if( FAILED( hr ) )
{
SAFE_ARRAYDELETE( *pwszConnectionName );
break;
}
//
// Each stream in the profile has to have a unique connection name.
// Let's use the stream number to create it.
//
if( *pwStreamNum > 127 )
{
hr = E_FAIL;
break;
}
(void)StringCchPrintfW( *pwszConnectionName, wcslen( wszDefaultConnectionName) + 4, L"%s%d", wszDefaultConnectionName, (DWORD)*pwStreamNum );
hr = pStreamConfig->SetConnectionName( *pwszConnectionName );
if( FAILED( hr ) )
{
SAFE_ARRAYDELETE( *pwszConnectionName );
break;
}
hr = pIWMProfile->ReconfigStream( pStreamConfig );
if( FAILED( hr ) )
{
SAFE_ARRAYDELETE( *pwszConnectionName );
break;
}
}
while( FALSE );
SAFE_RELEASE( pCodecInfo );
SAFE_RELEASE( pStreamConfig );
SAFE_RELEASE( pMediaProps );
SAFE_RELEASE( pManager );
SAFE_ARRAYDELETE( pMediaType );
return( hr );
}
//------------------------------------------------------------------------------
// Name: CUncompAVIToWMV::AddAudioStream()
// Desc: Configures and adds an audio stream.
//------------------------------------------------------------------------------
HRESULT CUncompAVIToWMV::AddAudioStream( IWMProfile * pIWMProfile,
DWORD dwSampleRate,
DWORD dwChannels,
WORD wBitsPerSample,
WORD * pwStreamNum,
__out LPWSTR * pwszConnectionName )
{
HRESULT hr = S_OK;
IWMProfileManager * pIWMProfileManager = NULL;
IWMStreamConfig * pIWMStreamConfig = NULL;
IWMMediaProps * pIMP = NULL;
IWMCodecInfo * pIWMInfo = NULL;
WAVEFORMATEX * pWfx = NULL;
WM_MEDIA_TYPE * pType = NULL;
if( NULL == pIWMProfile || NULL == pwStreamNum || NULL == pwszConnectionName )
{
return( E_INVALIDARG );
}
do
{
hr = WMCreateProfileManager( &pIWMProfileManager );
if( FAILED( hr ) )
{
break;
}
hr = pIWMProfileManager->QueryInterface( IID_IWMCodecInfo, (void **) &pIWMInfo );
if( FAILED( hr ) )
{
break;
}
DWORD i, j;
DWORD cCodecs;
hr = pIWMInfo->GetCodecInfoCount( WMMEDIATYPE_Audio, &cCodecs );
if( FAILED( hr ) )
{
break;
}
for( i = 0; i < cCodecs; i++ )
{
DWORD cFormats;
hr = pIWMInfo->GetCodecFormatCount( WMMEDIATYPE_Audio,
i,
&cFormats );
if( FAILED( hr ) )
{
break;
}
//
// Find a proper format in this codec
//
for( j = 0; j < cFormats; j++ )
{
if( NULL != pType )
{
SAFE_ARRAYDELETE( pType );
}
DWORD cbType = 0;
hr = pIWMInfo->GetCodecFormat( WMMEDIATYPE_Audio,
i,
j,
&pIWMStreamConfig );
if( FAILED( hr ) )
{
break;
}
SAFE_RELEASE( pIMP );
hr = pIWMStreamConfig->QueryInterface( IID_IWMMediaProps,
(void **)&pIMP );
if( FAILED( hr ) )
{
break;
}
hr = pIMP->GetMediaType( NULL, &cbType );
if( FAILED( hr ) )
{
break;
}
pType = (WM_MEDIA_TYPE *) new BYTE[ cbType ];
if( NULL == pType )
{
hr = E_OUTOFMEMORY;
break;
}
hr = pIMP->GetMediaType( pType, &cbType );
if( FAILED( hr ) )
{
break;
}
if( pType->formattype != WMFORMAT_WaveFormatEx )
{
hr = E_FAIL;
break;
}
pWfx = (WAVEFORMATEX *) pType->pbFormat;
//
// This sample will use this format only if it has the same
// sample rate, channels and more bits per sample.
// This is not necessary, because normally the codec can convert
// the sample rate and bits per sample for you.
//
if( pWfx->nSamplesPerSec == dwSampleRate &&
pWfx->nChannels == dwChannels &&
pWfx->wBitsPerSample >= wBitsPerSample )
{
break;
}
SAFE_RELEASE( pIWMStreamConfig );
}
if( FAILED( hr ) || NULL != pIWMStreamConfig )
{
break;
}
}
if( FAILED( hr ) )
{
break;
}
if( NULL == pIWMStreamConfig )
{
hr = NS_E_AUDIO_CODEC_NOT_INSTALLED;
break;
}
//
// We found a valid WAVEFORMATEX; go ahead and set up the stream.
//
hr = SetStreamBasics( pIWMStreamConfig,
pIWMProfile,
L"Audio Stream",
L"Audio",
pWfx->nAvgBytesPerSec * 8,
pType );
if( FAILED( hr ) )
{
break;
}
*pwszConnectionName = new WCHAR[ wcslen( wszDefaultConnectionName ) + 4 ];
if( NULL == *pwszConnectionName )
{
hr = E_OUTOFMEMORY;
break;
}
hr = pIWMProfile->AddStream( pIWMStreamConfig );
if( FAILED( hr ) )
{
SAFE_ARRAYDELETE( *pwszConnectionName );
break;
}
hr = pIWMStreamConfig->GetStreamNumber( pwStreamNum );
if( FAILED( hr ) )
{
SAFE_ARRAYDELETE( *pwszConnectionName );
break;
}
//
// Each stream in the profile has to have a unique connection name.
// Let's use the stream number to create it.
//
if( *pwStreamNum > 127 )
{
hr = E_FAIL;
break;
}
(void)StringCchPrintfW( *pwszConnectionName, wcslen( wszDefaultConnectionName ) + 4, L"%s%d", wszDefaultConnectionName, (DWORD)*pwStreamNum );
hr = pIWMStreamConfig->SetConnectionName( *pwszConnectionName );
if( FAILED( hr ) )
{
SAFE_ARRAYDELETE( *pwszConnectionName );
break;
}
hr = pIWMProfile->ReconfigStream( pIWMStreamConfig );
if( FAILED( hr ) )
{
SAFE_ARRAYDELETE( *pwszConnectionName );
break;
}
}
while( FALSE );
SAFE_ARRAYDELETE( pType );
SAFE_RELEASE( pIWMInfo );
SAFE_RELEASE( pIWMStreamConfig );
SAFE_RELEASE( pIMP );
SAFE_RELEASE( pIWMProfileManager );
return( hr );
}
//------------------------------------------------------------------------------
// Name: CompareMediaTypes()
// Desc: Used by CTSimpleList<CProfileStreams> to compare media types.
//------------------------------------------------------------------------------
BOOL CompareMediaTypes( const WM_MEDIA_TYPE * pMedia1, const WM_MEDIA_TYPE * pMedia2)
{
if( pMedia1->majortype != pMedia2->majortype )
{
return( FALSE );
}
return( TRUE );
}