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

709 lines
18 KiB
C++

//*****************************************************************************
//
// Microsoft Windows Media
// Copyright (C) Microsoft Corporation. All rights reserved.
//
// FileName: writer.cpp
//
// Abstract: Implementation of the CWriter class
//
//*****************************************************************************
#include "Writer.h"
//////////////////////////////////////////////////////////////////////
// The CWriter object is fed with stream data samples by the CReader object
// by calls to its WriteStreamSample method. It sends data to the output
// defined by a file or network sink. Data has to be compressed. All attributes
// have to be set up by CReader according to its input stream attributes
// The GetStats method enables you to get current output stream
// statistics at any time during data transmission.
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CWriter::CWriter()
{
m_pWriter = NULL;
m_pWriterAdvanced = NULL;
m_pWriterAdvanced3 = NULL;
m_pWriterHeaderInfo = NULL;
m_pFileSink = NULL;
m_pNetSink = NULL;
m_fWriterStarted = FALSE;
m_pStreamNumList = NULL;
m_nStreamNumListCount = 0;
}
///////////////////////////////////////////////////////////////
CWriter::~CWriter()
{
if( NULL != m_pFileSink )
{
RemoveFileSink();
}
if( NULL != m_pNetSink )
{
RemoveNetSink();
}
SAFE_RELEASE( m_pWriterHeaderInfo ) ;
SAFE_RELEASE( m_pWriterAdvanced ) ;
SAFE_RELEASE( m_pWriterAdvanced3 ) ;
SAFE_RELEASE( m_pFileSink );
SAFE_RELEASE( m_pNetSink );
SAFE_RELEASE( m_pWriter ) ;
if ( NULL != m_pStreamNumList )
{
delete [] m_pStreamNumList;
}
}
///////////////////////////////////////////////////////////////
// Initialize Writer object:
// create all necessary interfaces and an event
///////////////////////////////////////////////////////////////
HRESULT CWriter::Init()
{
HRESULT hr = S_OK;
if ( m_fWriterStarted || NULL != m_pWriterHeaderInfo )
return E_FAIL;
do
{
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_pWriterAdvanced );
if ( FAILED( hr ) )
{
_tprintf( _T( "Could not QI for IWMWriterAdvanced (hr=0x%08x).\n" ), hr );
break;
}
hr = m_pWriter->QueryInterface( IID_IWMWriterAdvanced3, ( void** ) &m_pWriterAdvanced3 );
if ( FAILED( hr ) )
{
_tprintf( _T( "Could not QI for IWMWriterAdvanced3 (hr=0x%08x).\n" ), hr );
break;
}
hr = m_pWriter->QueryInterface( IID_IWMHeaderInfo, ( VOID ** )&m_pWriterHeaderInfo );
if ( FAILED( hr ) )
{
_tprintf( _T( "Could not QI for IWMHeaderInfo (hr=0x%08x).\n" ), hr );
break;
}
}while ( FALSE );
if (FAILED( hr ) )
{
SAFE_RELEASE( m_pWriterHeaderInfo );
SAFE_RELEASE( m_pWriterAdvanced );
SAFE_RELEASE( m_pWriterAdvanced3 );
SAFE_RELEASE( m_pWriter );
}
return( hr );
}
///////////////////////////////////////////////////////////////
// Configure writer output according to profile
//
HRESULT CWriter::Configure( IWMProfile *pProfile )
{
HRESULT hr = S_OK;
if ( m_fWriterStarted || NULL == m_pWriterHeaderInfo || NULL == pProfile)
{
return( E_FAIL );
}
//
// Set the writer's properties
//
hr = m_pWriter->SetProfile( pProfile );
if ( FAILED( hr ) )
{
_tprintf( _T( "Could not set profile on IWMWriter (hr=0x%08x).\n" ), hr );
return( hr );
}
//
// Set compression off - samples are already compressed
//
hr = SetCodecOff();
if ( FAILED( hr ) )
{
return( hr );
}
//
// Create array of stream numbers for statistics reporting
//
CreateStreamNumList( pProfile );
return( hr );
}
///////////////////////////////////////////////////////////////
// Starts writing to output
//
HRESULT CWriter::Start()
{
HRESULT hr = S_OK ;
if ( NULL == m_pWriter || m_fWriterStarted)
return( E_FAIL );
//
// Start writing
//
hr = m_pWriter->BeginWriting();
if ( FAILED( hr ) )
{
_tprintf( _T( "BeginWriting on IWMWriter failed (hr=0x%08x).\n" ), hr );
return( hr );
}
m_fWriterStarted = TRUE;
return( hr );
}
///////////////////////////////////////////////////////////////
// Stop writing to output. All data is flushed out
//
HRESULT CWriter::Stop()
{
HRESULT hr = S_OK ;
if ( NULL == m_pWriter || !m_fWriterStarted)
{
return( E_FAIL );
}
hr = m_pWriter->Flush();
if ( FAILED( hr ) )
{
_tprintf( _T( "Could not Flush on IWMWriter (hr=0x%08x).\n" ), hr );
return( hr );
}
hr = m_pWriter->EndWriting();
if ( FAILED( hr ) )
{
_tprintf( _T( "Could not EndWriting on IWMWriter (hr=0x%08x).\n" ), hr );
return( hr );
}
m_fWriterStarted = FALSE;
if( NULL != m_pFileSink )
{
hr = RemoveFileSink();
if ( FAILED( hr ) )
{
_tprintf( _T( "Could not remove file sink (hr=0x%08x).\n" ), hr );
return( hr );
}
}
if( NULL != m_pNetSink )
{
hr = RemoveNetSink();
if ( FAILED( hr ) )
{
_tprintf( _T( "Could not remove net sink (hr=0x%08x).\n" ), hr );
return( hr );
}
}
return( hr );
}
///////////////////////////////////////////////////////////////
// Create and attach a file sink
// pwszFileName - output file name
//
HRESULT CWriter::CreateFileSink( __in LPWSTR pwszFileName )
{
HRESULT hr = S_OK ;
if ( NULL == m_pWriterAdvanced || m_fWriterStarted || NULL != m_pFileSink)
{
return( E_FAIL );
}
do
{
hr = WMCreateWriterFileSink( &m_pFileSink );
if ( FAILED( hr ) )
{
_tprintf( _T( "Could not create Writer File Sink (hr=0x%08x).\n" ), hr );
break;
}
//
// Configure the File Sink
//
hr = m_pFileSink->Open( pwszFileName );
if ( FAILED( hr ) )
{
_tprintf( _T( "File sink failed to open file %ws (hr=0x%08x).\n" ),
pwszFileName, hr ) ;
break;
}
//
// Add the network sink to the Writer
//
hr = m_pWriterAdvanced->AddSink(m_pFileSink);
if ( FAILED( hr ) )
{
_tprintf( _T( "Could not Add Sink (hr=0x%08x).\n" ), hr );
break;
}
} while( FALSE );
if (FAILED( hr ) )
{
SAFE_RELEASE( m_pFileSink );
}
return( hr );
}
///////////////////////////////////////////////////////////////
// Remove file sink
//
HRESULT CWriter::RemoveFileSink()
{
HRESULT hr = S_OK ;
if ( NULL == m_pWriterAdvanced || NULL == m_pFileSink || m_fWriterStarted)
{
return( E_FAIL );
}
hr = m_pWriterAdvanced->RemoveSink( m_pFileSink );
if ( FAILED( hr ) )
{
_tprintf( _T( "Could not remove the File Sink (hr=0x%08x).\n" ), hr );
return( hr );
}
SAFE_RELEASE( m_pFileSink );
SAFE_RELEASE( m_pWriterHeaderInfo );
SAFE_RELEASE( m_pWriterAdvanced );
SAFE_RELEASE( m_pWriter );
return( hr );
}
///////////////////////////////////////////////////////////////
// Create and attach a network sink
// dwPortNumber - port number
// nMaxClient - maximum number of connected clients
//
HRESULT CWriter::CreateNetSink( DWORD dwPortNumber, UINT nMaxClient )
{
HRESULT hr = S_OK ;
if ( NULL == m_pWriterAdvanced || m_fWriterStarted || NULL != m_pNetSink)
{
return( E_FAIL );
}
do
{
hr = WMCreateWriterNetworkSink( &m_pNetSink );
if ( FAILED( hr ) )
{
_tprintf( _T( "Could not create Writer Network Sink (hr=0x%08x).\n" ), hr );
break;
}
//
// Configure the Net Sink
//
hr = m_pNetSink->SetNetworkProtocol( WMT_PROTOCOL_HTTP );
if ( FAILED( hr ) )
{
_tprintf( _T( "Could not Set Network protocol (hr=0x%08x).\n" ), hr );
break;
}
hr = m_pNetSink->Open( &dwPortNumber);
if ( FAILED( hr ) )
{
_tprintf( _T( "Network sink failed to open port no %d (hr=0x%08x).\n" ),
dwPortNumber, hr ) ;
break;
}
//
// Set the maximum number of clients that can connect to the port
//
hr = m_pNetSink->SetMaximumClients( nMaxClient );
if ( FAILED( hr ) )
{
_tprintf( _T( "Could not Set maximum clients (hr=0x%08x).\n" ), hr );
break;
}
//
// Add the network sink to the Writer
//
hr = m_pWriterAdvanced->AddSink(m_pNetSink);
if ( FAILED( hr ) )
{
_tprintf( _T( "Could not Add Sink (hr=0x%08x).\n" ), hr );
break;
}
} while( FALSE );
if (FAILED( hr ) )
{
SAFE_RELEASE( m_pNetSink );
}
return( hr );
}
///////////////////////////////////////////////////////////////
// Remove network sink
//
HRESULT CWriter::RemoveNetSink()
{
HRESULT hr = S_OK ;
if ( NULL == m_pWriterAdvanced || NULL == m_pNetSink || m_fWriterStarted)
{
return( E_FAIL );
}
hr = m_pWriterAdvanced->RemoveSink( m_pNetSink );
if ( FAILED( hr ) )
{
_tprintf( _T( "Could not remove the Network Sink (hr=0x%08x).\n" ), hr );
return( hr );
}
hr = m_pNetSink->Close();
if ( FAILED( hr ) )
{
_tprintf( _T( "Could not close on IWMWriterNetworkSink (hr=0x%08x).\n" ), hr );
return( hr );
}
SAFE_RELEASE( m_pNetSink );
return( hr );
}
///////////////////////////////////////////////////////////////
// Return output statistics and number of connected users.
// A statistic with stream number 0 means summary statistics of all streams
// dwClientNum - number of currently connected clients
// nStatsNum - number of statistics stored in the buffer
// pStats - buffer containing summary statistics and statistics for
// each stream.
//
HRESULT CWriter::GetStats( DWORD &dwClientNum, int &nStatsNum, CWriterStats **pStats, CWriterStatsEx **pStatsEx )
{
HRESULT hr = S_OK;
WM_WRITER_STATISTICS Stats;
WM_WRITER_STATISTICS_EX StatsEx;
CWriterStats* pLocalStats = NULL;
CWriterStatsEx* pLocalStatsEx = NULL;
if ( NULL == pStats )
return E_INVALIDARG;
nStatsNum = 0;
if ( NULL != m_pNetSink )
{
IWMClientConnections *pIWMClientConnections = NULL;
hr = m_pNetSink->QueryInterface(IID_IWMClientConnections, (void**) &pIWMClientConnections);
if ( SUCCEEDED( hr ) )
{
hr = pIWMClientConnections->GetClientCount(&dwClientNum);
if (FAILED ( hr ) )
{
_tprintf( _T( "Could not get client number from IWMWriter (hr=0x%08x).\n" ), hr );
}
SAFE_RELEASE( pIWMClientConnections );
}
}
if ( NULL != m_pWriterAdvanced && NULL != m_pWriter )
{
//
// Get summary statistics
//
hr = m_pWriterAdvanced->GetStatistics( 0, &Stats );
if (FAILED ( hr ) )
{
_tprintf( _T( "Could not get statistics IWMWriter (hr=0x%08x).\n" ), hr );
}
}
if ( NULL != m_pWriterAdvanced3 && NULL != m_pWriter )
{
//
// Get summary statistics
//
hr = m_pWriterAdvanced3->GetStatisticsEx( 0, &StatsEx );
if (FAILED ( hr ) )
{
_tprintf( _T( "Could not get statistics IWMWriter (hr=0x%08x).\n" ), hr );
}
}
pLocalStats = new CWriterStats[ m_nStreamNumListCount + 1 ];
if ( NULL == pLocalStats )
{
return E_OUTOFMEMORY;
}
*pStats = pLocalStats;
pLocalStatsEx = new CWriterStatsEx;
if ( NULL == pLocalStatsEx )
{
return E_OUTOFMEMORY;
}
*pStatsEx = pLocalStatsEx;
pLocalStatsEx->m_nStreamNum = 0;
*pLocalStatsEx = StatsEx;
// Copy summary statistics
pLocalStats->m_nStreamNum = 0; // all streams;
*pLocalStats++ = Stats;
nStatsNum++;
DWORD nStreamsCount = m_nStreamNumListCount;
WORD *pStreamNumList = m_pStreamNumList;
while( 0 != nStreamsCount--
&& SUCCEEDED( hr = m_pWriterAdvanced->GetStatistics( *pStreamNumList, &Stats ) ) )
{
pLocalStats->m_nStreamNum = *pStreamNumList++;
*pLocalStats++ = Stats;
nStatsNum++;
}
return( hr );
}
///////////////////////////////////////////////////////////////
// Write stream sample to output
//
HRESULT CWriter::WriteStreamSample(WORD wStreamNum, QWORD cnsSampleTime, DWORD msSampleSendTime, QWORD cnsSampleDuration, DWORD dwFlags, INSSBuffer *pSample)
{
HRESULT hr = E_FAIL;
if (m_pWriterAdvanced && m_fWriterStarted)
hr = m_pWriterAdvanced->WriteStreamSample( wStreamNum,
cnsSampleTime,
msSampleSendTime,
cnsSampleDuration,
dwFlags,
pSample ) ;
return hr ;
}
///////////////////////////////////////////////////////////////
// Add a script to output profile
//
HRESULT CWriter::AddScript(__in LPWSTR pwszType, __in LPWSTR pwszCommand, QWORD cnsScriptTime)
{
HRESULT hr = S_OK;
if (!m_pWriterHeaderInfo)
return E_FAIL;
hr = m_pWriterHeaderInfo->AddScript( pwszType ,
pwszCommand ,
cnsScriptTime ) ;
if ( FAILED( hr ) )
{
_tprintf( _T( "AddScript failed (hr=0x%08x).\n" ), hr) ;
}
return hr;
}
///////////////////////////////////////////////////////////////
// Set attribute value for an output stream
//
HRESULT CWriter::SetAttribute(WORD nstreamNum, LPCWSTR pwszName, WMT_ATTR_DATATYPE type, const BYTE *pValue, WORD cbLength)
{
HRESULT hr = S_OK;
if (!m_pWriterHeaderInfo)
return E_FAIL;
hr = m_pWriterHeaderInfo->SetAttribute( nstreamNum,
pwszName,
type,
pValue,
cbLength );
if ( FAILED( hr ) )
{
_tprintf( _T( "SetAttribute failed for Attribute name %ws (hr=0x%08x).\n" ),
pwszName, hr) ;
}
return hr;
}
///////////////////////////////////////////////////////////////
// Switch codec off for all streams - samples are sent without change
//
HRESULT CWriter::SetCodecOff()
{
HRESULT hr = S_OK;
DWORD cInputs = 0 ;
if (!m_pWriter)
return E_FAIL;
if( FAILED( hr = m_pWriter->GetInputCount( &cInputs )))
{
_tprintf( _T( "Could not get input count from IWMWriter (hr=0x%08x).\n" ), hr );
return hr ;
}
for( DWORD i = 0; i < cInputs; i++ )
{
//
// Set the input props to NULL to indicate that we don't need a codec
// because we are writing compressed samples to the port
//
if( FAILED( hr = m_pWriter->SetInputProps( i, NULL )))
break;
}
return hr;
}
///////////////////////////////////////////////////////////////
// Create a global array of stream numbers for a given profile
//
HRESULT CWriter::CreateStreamNumList(IWMProfile* pProfile)
{
HRESULT hr = S_OK;
IWMStreamConfig* pStream = NULL ;
WORD* pStreamNumList = NULL;
if (!pProfile)
return E_INVALIDARG;
if ( FAILED( hr = pProfile->GetStreamCount( &m_nStreamNumListCount )))
{
_tprintf( _T( "GetStreamCount on IWMProfile failed (hr=0x%08x).\n" ), hr );
return hr ;
}
if (m_nStreamNumListCount)
m_pStreamNumList = new WORD[m_nStreamNumListCount];
else
return S_OK;
if (!m_pStreamNumList)
return E_FAIL;
pStreamNumList = m_pStreamNumList;
for ( DWORD i = 0; i < m_nStreamNumListCount; i++ )
{
if ( FAILED( hr = pProfile->GetStream( i, &pStream )))
{
_tprintf( _T( "Could not get Stream %d of %d from IWMProfile (hr=0x%08x).\n" ),
i, m_nStreamNumListCount, hr );
break;
}
//
//Get the stream number of the current stream
//
if ( FAILED( hr = pStream->GetStreamNumber( pStreamNumList )))
{
_tprintf( _T( "Could not get stream number from IWMStreamConfig %d of %d (hr=0x%08x).\n" ),
i, m_nStreamNumListCount, hr );
break;
}
pStreamNumList++;
SAFE_RELEASE( pStream );
}
return hr;
}
///////////////////////////////////////////////////////////////
const CWriterStats &CWriterStats::operator= (const WM_WRITER_STATISTICS &right)
{
qwSampleCount = right.qwSampleCount;
qwByteCount = right.qwByteCount;
qwDroppedSampleCount = right.qwDroppedSampleCount;
qwDroppedByteCount = right.qwDroppedByteCount;
dwCurrentBitrate = right.dwCurrentBitrate;
dwAverageBitrate = right.dwAverageBitrate;
dwExpectedBitrate = right.dwExpectedBitrate;
dwCurrentSampleRate = right.dwCurrentSampleRate;
dwAverageSampleRate = right.dwAverageSampleRate;
dwExpectedSampleRate = right.dwExpectedSampleRate;
return *this;
}
const CWriterStatsEx &CWriterStatsEx::operator= (const WM_WRITER_STATISTICS_EX &right)
{
dwBitratePlusOverhead = right.dwBitratePlusOverhead ;
dwCurrentSampleDropRateInQueue = right.dwCurrentSampleDropRateInQueue ;
dwCurrentSampleDropRateInCodec = right.dwCurrentSampleDropRateInCodec ;
dwCurrentSampleDropRateInMultiplexer = right.dwCurrentSampleDropRateInMultiplexer ;
dwTotalSampleDropsInQueue = right.dwTotalSampleDropsInQueue ;
dwTotalSampleDropsInCodec = right.dwTotalSampleDropsInCodec ;
dwTotalSampleDropsInMultiplexer = right.dwTotalSampleDropsInMultiplexer ;
return *this;
}