Re: IWMWriterNetworkSink::Close() - The code

From: Ard (ard_at_RemoveMe.dv-rec.com)
Date: 06/07/04


Date: Mon, 7 Jun 2004 11:05:01 +0200

Hi,

Below is the modified code of the WMVNetWrite sample from the Windows Media
Format 9 SDK. There are three files: Main.cpp, NetWrite.cpp and Netwrite.h.

After starting the program, connect to the second stream using a mediaplayer
and see what happens: On my computer (Win2000), closing the networksink on
the first stream (which is done automatically by the program after 30
seconds) will block until the second stream has stopped. The program will
output some debug-info before and after the Close() function call.

Could anyone test this code to see if the problem arises? It would help me a
lot.

Thanks,
Ard Oerlemans

//**************************************************************************
***
//
// Microsoft Windows Media
// Copyright ( C) Microsoft Corporation. All rights reserved.
//
// FileName: main.cpp
//
// Abstract: Entry point for WMVNetWrite sample. This sample
// demonstrates how to stream a Windows Media file over
// the network to other clients by sending
("Netwriting")its
// compressed samples.
//
//
//**************************************************************************
***

#include <TCHAR.H>
#include <stdio.h>
#include "wmsdk.h"
#include "NetWrite.h"

//--------------------------------------------------------------------------

--
// Name: ConvertTCharToWChar
// Desc: Helper function to convert a TCHAR string to a WCHAR string.
//
// ptszInput: Contains the input string.
// ppwszOutput: Receives the output string.
//
// The caller must delete the returned WCHAR string.
//--------------------------------------------------------------------------
--
HRESULT ConvertTCharToWChar( TCHAR * ptszInput, WCHAR ** ppwszOutput )
{
    int cchOutput = 0;
    if( NULL == ptszInput || NULL == ppwszOutput )
    {
        return( E_INVALIDARG );
    }
    //
    // Get the size needed for the output buffer.
    //
#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
    // Allocate the buffer.
    *ppwszOutput = new WCHAR[ cchOutput ];
    if( NULL == *ppwszOutput)
    {
        return( E_OUTOFMEMORY );
    }
    // Convert the input string.
#ifdef UNICODE
    wcsncpy( *ppwszOutput, ptszInput, cchOutput );
#else //UNICODE
    if( 0 == MultiByteToWideChar( CP_ACP, 0, ptszInput, -1, *ppwszOutput,
cchOutput ) )
    {
        if(*ppwszOutput )
        {
            delete *ppwszOutput;
            *ppwszOutput = NULL;
        }
        return( HRESULT_FROM_WIN32( GetLastError() ) );
    }
#endif// UNICODE
    return( S_OK );
}
//--------------------------------------------------------------------------
--
// Name: Usage
// Desc: Displays the valid command-line arguments for this application.
//--------------------------------------------------------------------------
--
void Usage()
{
   _tprintf( _T( "wmvnetwrite -i1 <infile1> -i <infile2> [-p1 <portnum1>]
[-p2 <portnum2>] [-c <maxclient>] [-s <server URL>]\n" ) );
   _tprintf( _T( "\tinfile1\t= First Input WMV file name\n" ) );
   _tprintf( _T( "\tinfile2\t= Second Input WMV file name\n" ) );
   _tprintf( _T( "\tportnum1\t= First Port Number (for incoming
connections)\n" ) );
   _tprintf( _T( "\tportnum2\t= Second Port Number (for incoming
connections)\n" ) );
   _tprintf( _T( "\tmaxclient = Maximum Clients allowed to connect\n" ) );
   _tprintf( _T( "\tserver URL = URL of Server for Push Distribution\n" ) );
}
//--------------------------------------------------------------------------
--
// Name: _tmain
// Desc: Entry point for the application.
//--------------------------------------------------------------------------
--
int __cdecl _tmain( int argc, TCHAR* argv[] )
{
    DWORD   dwPortNum1       = 8080;     // Default port number.
    DWORD   dwPortNum2       = 8081;     // Default port number.
    TCHAR*  ptszFileName1    = NULL;     // File to broadcast.
    TCHAR*  ptszFileName2    = NULL;     // File to broadcast.
    TCHAR*  ptszServerURL    = NULL;     // URL on the server, for push
distribution
    int     nMaxClient  = 10;           // Maximum number of clients that
can connect.
    HRESULT hr = S_OK;
    // Loop through the command-line arguments. On failure, display the
correct
    // usage and exit.
    for( int i = 1; i < argc; i ++ )
    {
        if( 0 == _tcsicmp( argv[i], _T( "-p1" ) ) )
        {
            i++;
            if( i == argc )
            {
                Usage();
                return( E_INVALIDARG );
            }
            int retval = _stscanf( argv[i], _T( "%d" ), &dwPortNum1 );
            if( retval == 0 )
            {
                Usage();
                return( E_INVALIDARG );
            }
            continue;
        }
        if( 0 == _tcsicmp( argv[i], _T( "-p2" ) ) )
        {
            i++;
            if( i == argc )
            {
                Usage();
                return( E_INVALIDARG );
            }
            int retval = _stscanf( argv[i], _T( "%d" ), &dwPortNum2 );
            if( retval == 0 )
            {
                Usage();
                return( E_INVALIDARG );
            }
            continue;
        }
        if( 0 == _tcsicmp( argv[i], _T( "-i1" ) ) )
        {
            i++;
            if( i == argc )
            {
                Usage();
                return( E_INVALIDARG );
            }
            ptszFileName1 = argv[i];
            continue;
        }
        if( 0 == _tcsicmp( argv[i], _T( "-i2" ) ) )
        {
            i++;
            if( i == argc )
            {
                Usage();
                return( E_INVALIDARG );
            }
            ptszFileName2 = argv[i];
            continue;
        }
        if( 0 == _tcsicmp( argv[i] , _T( "-c" ) ) )
        {
            i++;
            if(i == argc)
            {
                Usage();
                return ( E_INVALIDARG );
            }
            int retval = _stscanf( argv[i], _T( "%d" ), &nMaxClient );
            if( 0 == retval )
            {
                Usage();
                return( E_INVALIDARG );
            }
            continue;
        }
        if( 0 == _tcsicmp( argv[i], _T( "-s" ) ) )
        {
            i++;
            if( i == argc )
            {
                Usage();
                return( E_INVALIDARG );
            }
            ptszServerURL = argv[i];
            continue;
        }
    }
    if( NULL == ptszFileName1 || NULL == ptszFileName2 )
    {
        Usage();
        return( E_INVALIDARG );
    }
    CNetWrite netWriter1;  // Helper object that broadcasts the file.
    CNetWrite netWriter2;  // Helper object that broadcasts the file.
    WCHAR *pwszFile1 = NULL;
    WCHAR *pwszFile2 = NULL;
    WCHAR *pwszServerURL = NULL;
    // Declare a dummy 'do' loop. On failure, we can break from the loop.
    do
    {
        // Convert the file name to a wide-character string.
        hr = ConvertTCharToWChar( ptszFileName1, &pwszFile1 );
        if( FAILED( hr ) )
            break;
        // Convert the file name to a wide-character string.
        hr = ConvertTCharToWChar( ptszFileName2, &pwszFile2 );
        if( FAILED( hr ) )
            break;
        // The server URL is optional, so it might be NULL.
        if( ptszServerURL != NULL )
        {
            // Convert the server URL to wide-character string.
            hr = ConvertTCharToWChar( ptszServerURL, &pwszServerURL);
            if( FAILED( hr ) )
                break;
        }
        // Initialize our helper object.
        hr = netWriter1.Init();
        if( FAILED( hr ) )
        {
            break;
        }
        // Initialize our helper object.
        hr = netWriter2.Init();
        if( FAILED( hr ) )
        {
            break;
        }
        // Configure the helper object with the port number, file name,
maximum
        // number of clients, server URL and maximum duration.
        hr = netWriter1.Configure(dwPortNum1, pwszFile1, nMaxClient,
pwszServerURL, 30);
        if( FAILED( hr ) )
        {
            break;
        }
        // Configure the helper object with the port number, file name,
maximum
        // number of clients, server URL and maximum duration.
        hr = netWriter2.Configure(dwPortNum2, pwszFile2, nMaxClient,
pwszServerURL, 60);
        if( FAILED( hr ) )
        {
            break;
        }
        // Write all of the samples to the network.
        hr = netWriter1.WritetoNet();
        if(FAILED(hr))
        {
            break;
        }
        // Write all of the samples to the network.
        hr = netWriter2.WritetoNet();
        if(FAILED(hr))
        {
            break;
        }
  BOOL bNetWriter1Finished = FALSE;
  BOOL bNetWriter2Finished = FALSE;
  while ( bNetWriter1Finished == FALSE || bNetWriter2Finished == FALSE )
  {
   if (WaitForSingleObject(netWriter1.m_hEvent, 50) == WAIT_OBJECT_0)
   {
    netWriter1.EndWritetoNet();
    bNetWriter1Finished = TRUE;
   }
   if (WaitForSingleObject(netWriter2.m_hEvent, 50) == WAIT_OBJECT_0)
   {
    netWriter2.EndWritetoNet();
    bNetWriter2Finished = TRUE;
   }
  }
    }
    while(FALSE);  // Go through the dummy loop one time only.
    // Free memory.
    SAFE_ARRAYDELETE( pwszFile1 );
    SAFE_ARRAYDELETE( pwszFile2 );
    SAFE_ARRAYDELETE( pwszServerURL );
    return hr;
}
//**************************************************************************
***
//
// Microsoft Windows Media
// Copyright ( C) Microsoft Corporation. All rights reserved.
//
// FileName:            NetWrite.cpp
//
// Abstract:            CNetWrite class implementation.
//
//
//**************************************************************************
***
#include "wmsdk.h"
#include "NetWrite.h"
#include <TCHAR.H>
#include <stdio.h>
#include <assert.h>
#include <conio.h>
//--------------------------------------------------------------------------
--
// Name: CNetWrite::CNetWrite()
// Desc: Constructor.
//--------------------------------------------------------------------------
--
CNetWrite::CNetWrite()
{
    m_pReaderHeaderInfo = NULL;
    m_pWriterHeaderInfo = NULL;
    m_pWriterAdvanced = NULL;
    m_pReaderAdvanced = NULL;
    m_pReader  = NULL;
    m_pWriter  = NULL;
    m_pNetSink  = NULL;
    m_pPushSink         = NULL;
    m_pPushSinkCallbackCtrl = NULL;
    m_hEvent  = NULL;
    m_bEOF  = false;
    m_qwTime  = 0;
 m_qwStartTime   = 0;
    m_hrAsync  = S_OK;
 m_dwPortNum     = 0;
}
//--------------------------------------------------------------------------
--
// Name: CNetWrite::~CNetWrite()
// Desc: Destructor.
//--------------------------------------------------------------------------
--
CNetWrite::~CNetWrite()
{
    CloseHandle( m_hEvent );
    SAFE_RELEASE( m_pWriterHeaderInfo );
    SAFE_RELEASE( m_pWriterAdvanced );
    SAFE_RELEASE( m_pWriter );
    SAFE_RELEASE( m_pNetSink );
    SAFE_RELEASE( m_pPushSinkCallbackCtrl );
    SAFE_RELEASE( m_pPushSink );
    SAFE_RELEASE( m_pReaderHeaderInfo );
    SAFE_RELEASE( m_pReaderAdvanced );
    SAFE_RELEASE( m_pReader );
    CoUninitialize();
}
//--------------------------------------------------------------------------
--
// Name: CNetWrite::OnSample()
// Desc: Called when the reader object delivers an uncompressed sample.
// Note: This method implements the IWMReaderCallback::OnSample method. See
the
//       SDK documentation for a description of the parameters.
//
//       The CNetWrite object configures the reader to deliver compressed
//       samples, which happens via the OnStreamSample callback method.
//       Therefore, if this method is called, it is an error.
//--------------------------------------------------------------------------
--
HRESULT CNetWrite::OnSample( DWORD dwOutputNum,
                            QWORD cnsSampleTime,
                            QWORD cnsSampleDuration,
                            DWORD dwFlags,
                            INSSBuffer __RPC_FAR *pSample, void __RPC_FAR
*pvContext )
{
    if( m_hEvent != NULL )
    {
        //
        //The samples are expected in OnStreamSample.
        //
        _tprintf( _T( "Error: Received a decompressed sample from the
reader.\n" ) );
        m_hrAsync = E_UNEXPECTED;
        SetEvent( m_hEvent );
    }
    return S_OK;
}
//--------------------------------------------------------------------------
--
// Name: CNetWrite::OnStatus()
// Desc: Receives a status message from the reader object or the push sink.
//
// Note: This method implements the IWMStatusCallback::OnStatus method. See
the
//       SDK documentation for a description of the parameters.
//--------------------------------------------------------------------------
--
HRESULT CNetWrite::OnStatus( WMT_STATUS Status,
                            HRESULT hr,
                            WMT_ATTR_DATATYPE dwType,
                            BYTE __RPC_FAR *pValue,
                            void __RPC_FAR *pvContext )
{
    // Check whether the push sink object is the caller.
    BOOL fFromPushSink = ( m_pPushSink != NULL && pvContext == ( void * )
m_pPushSink );
    // For some status messages, we expect the CNetWrite object is waiting
    // to receive the message. Signal to the waiting thread by calling
SetEvent.
    // We also signal to the thread if there is an error message.
    // Handle status messages from the push sink.
    if( fFromPushSink )
    {
        switch(Status)
        {
        case WMT_LOCATING:
            _tprintf( _T(  "Locating server.\n" ) );
            break;
        case WMT_CONNECTING:
            _tprintf( _T(  "Connecting to server.\n" ) );
            break;
        case WMT_OPENED:
   _tprintf( _T(  "Connected to server.\n" ) );
            m_hrAsync = hr;
            SetEvent( m_hEvent );  // Unblock the thread that is waiting.
            break;
        case WMT_ERROR:
            _tprintf( _T(  "Error 0x%x during push distribution\n" ), hr );
            m_bEOF = true;
            m_hrAsync = hr;
            SetEvent( m_hEvent );
            break;
        }
        return S_OK;
    }
    // Handle status messages from the reader.
    switch(Status)
    {
    case WMT_OPENED:
  if( SUCCEEDED( hr ))
  {
   _tprintf( _T(  "The reader completed opening the input file.\n" ) );
  }
  m_hrAsync = hr;
        SetEvent( m_hEvent );  // Unblock the thread that is waiting.
        break;
    case WMT_EOF:
        m_bEOF = true;
        _tprintf( _T(  "EndOfStream detected in reader.\n" ) );
        m_hrAsync = hr;
        SetEvent( m_hEvent );
        break;
    case WMT_STARTED:
        // Ask the reader object to deliver another block of time.
        m_qwTime = 0;
        m_qwTime += 1000 * 10000;
        hr = m_pReaderAdvanced->DeliverTime( m_qwTime );
        m_hrAsync = hr;
        break;
    case WMT_CLOSED:
        m_hrAsync = hr;
        SetEvent( m_hEvent );  // Unblock the thread that is waiting.
        break;
    case WMT_ERROR:
        _tprintf( _T(  "Error 0x%x reported by the reader\n" ), hr );
        m_bEOF = true;
        m_hrAsync = hr;
        SetEvent( m_hEvent );
        break;
    }
    return S_OK;
}
//--------------------------------------------------------------------------
--
// Name: CNetWrite::OnStreamSample()
// Desc: Called when the reader object delivers a compressed sample.
//
// Note: This method implements the
IWMReaderCallbackAdvanced::OnStreamSample
//       method. See the SDK documentation for a description of the
parameters.
//--------------------------------------------------------------------------
--
HRESULT CNetWrite::OnStreamSample( WORD wStreamNum,
                                  QWORD cnsSampleTime,
                                  QWORD cnsSampleDuration,
                                  DWORD dwFlags,
                                  INSSBuffer __RPC_FAR *pSample,
                                  void __RPC_FAR *pvContext )
{
//    _tprintf( _T( "StreamSample: num=%d, time=%d, duration=%d,
flags=%d.\n" ),
//        wStreamNum, ( DWORD )cnsSampleTime, cnsSampleDuration, dwFlags );
    if( m_pWriterAdvanced != NULL )
    {
        // Give the sample to the writer object.
        HRESULT hr = m_pWriterAdvanced->WriteStreamSample( wStreamNum,
                                                           cnsSampleTime,
                                                           0,
cnsSampleDuration,
                                                           dwFlags,
                                                           pSample );
        if( FAILED( hr ) )
        {
            _tprintf( _T(  "Error 0x%x reported by the writer\n" ), hr );
            m_bEOF = true;
            m_hrAsync = hr;
            SetEvent( m_hEvent );
        }
  if ( m_qwStartTime == 0 )
   m_qwStartTime = cnsSampleTime;
  if ( (cnsSampleTime - m_qwStartTime) > m_qwMaxDuration )
   SetEvent( m_hEvent );
    }
    return S_OK;
}
//--------------------------------------------------------------------------
--
// Name: CNetWrite::OnTime()
// Desc: Called when the reader object has delivered all of the data that
this
//       object requested.
//
// Note: This method implements the IWMReaderCallbackAdvanced::OnTime
method.
//       See the SDK documentation for a description of the parameters.
//
//       This method gets called because the CNetWrite object is driving the
//       clock on the reader object.
//--------------------------------------------------------------------------
--
HRESULT CNetWrite::OnTime( QWORD cnsCurrentTime, void __RPC_FAR *pvContext)
{
    // Until the end of the file is reached, ask for another block of time.
    if( !m_bEOF )
    {
        m_qwTime += 10000000;
        HRESULT hr=m_pReaderAdvanced->DeliverTime( m_qwTime );
  if(FAILED(hr))
   return hr;
    }
    return S_OK;
}
//--------------------------------------------------------------------------
--
// Name: CNetWrite::Init()
// Desc: Initializes the CNetWrite object.
//--------------------------------------------------------------------------
--
HRESULT CNetWrite::Init()
{
    HRESULT hr = S_OK;
    // Initialize the COM library.
    hr = CoInitialize( NULL );
    if( FAILED( hr ) )
    {
        _tprintf( _T( "CoInitialize failed: hr = 0x%08x\n" ), hr );
        return hr;
    }
    //
    // Create the reader and writer. Query for additional interfaces.
    //
 hr = WMCreateReader( NULL, 0, &m_pReader );
 if( FAILED( hr ) )
 {
  _tprintf( _T( "Could not create reader (hr=0x%08x).\n" ), hr );
  return hr;
 }
    hr = m_pReader->QueryInterface( IID_IWMReaderAdvanced, (
void** )&m_pReaderAdvanced );
    if( FAILED( hr ) )
    {
        _tprintf( _T( "Could not QI for IWMReaderAdvanced (hr=0x%08x).\n" ),
hr );
        return hr;
    }
 hr = WMCreateWriter( NULL, &m_pWriter );
 if( FAILED( hr ) )
 {
  _tprintf( _T(  "Could not create Writer (hr=0x%08x).\n" ), hr );
  return hr;
 }
    hr = m_pWriter->QueryInterface( IID_IWMWriterAdvanced, ( void** )
&m_pWriterAdvanced );
    if( FAILED( hr ) )
    {
        _tprintf( _T( "Could not QI for IWMWriterAdvanced (hr=0x%08x).\n" ),
hr );
        return hr;
    }
    hr = m_pReader->QueryInterface( IID_IWMHeaderInfo, ( VOID
** )&m_pReaderHeaderInfo );
    if( FAILED( hr ) )
    {
        _tprintf( _T( "Could not QI for IWMHeaderInfo (hr=0x%08x).\n" ),
hr );
        return hr;
    }
    hr = m_pWriter->QueryInterface( IID_IWMHeaderInfo, ( VOID
** )&m_pWriterHeaderInfo );
    if( FAILED( hr ) )
    {
        _tprintf( _T( "Could not QI for IWMHeaderInfo (hr=0x%08x).\n" ),
hr );
        return hr;
    }
    return hr;
}
//--------------------------------------------------------------------------
--
// Name: CNetWrite::WritetoNet()
// Desc: Writes a file to the network. Call the Configure method first, to
set
//       the file name and other parameters.
//--------------------------------------------------------------------------
--
HRESULT CNetWrite::WritetoNet()
{
    if(   m_hEvent   == NULL ||
          m_pWriterAdvanced == NULL ||
          m_pReaderAdvanced == NULL ||
        ( m_pNetSink == NULL && m_pPushSink == NULL ) )
    {
        return E_UNEXPECTED;
    }
    HRESULT hr = S_OK;
    //
    // Start writing.
    //
    // First, prepare the writer object for writing. Then start the reader.
    hr = m_pWriter->BeginWriting( );
    if( FAILED( hr ) )
    {
        _tprintf( _T(  "BeginWriting on IWMWriter failed (hr=0x%08x).\n" ),
hr );
        return hr;
    }
    hr = m_pReader->Start( 0, 0, 1.0, 0 );
    if( FAILED( hr ) )
    {
        _tprintf( _T( "Could not start IWMReader (hr=0x%08x).\n" ), hr );
        return hr;
    }
 return hr;
}
//--------------------------------------------------------------------------
--
// Name: CNetWrite::EndWritetoNet()
// Desc: Closes all connections
//--------------------------------------------------------------------------
--
HRESULT CNetWrite::EndWritetoNet()
{
 HRESULT hr = S_OK;
    // Check the status of the operation.
    if( FAILED( m_hrAsync ) )
    {
        _tprintf( _T(  "Net writing failed (hr=0x%08x).\n" ), m_hrAsync );
        return hr;
    }
    //
    // Stop the reader and the writer, and close everything.
    //
    hr = m_pReader->Stop();
    if( FAILED( hr ) )
    {
        _tprintf( _T(  "Could not Stop IWMReader (hr=0x%08x).\n" ), hr );
        return hr;
    }
    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;
    }
    hr = m_pReader->Close();
    if( FAILED( hr ) )
    {
        _tprintf( _T(  "Could not close the file (hr=0x%08x).\n" ),  hr );
        return hr;
    }
    // Remove the network sink from the writer.
    if( m_pNetSink != NULL )
    {
        hr = m_pWriterAdvanced->RemoveSink( m_pNetSink );
        if( FAILED( hr ) )
        {
            _tprintf( _T(  "Could not remove the Network Sink
(hr=0x%08x).\n" ),  hr );
            return hr;
        }
        _tprintf( _T(  "Closing NetworkSink, port %d.\n" ), m_dwPortNum,
hr );
  hr = m_pNetSink->Close();
        if( FAILED( hr ) )
        {
            _tprintf( _T(  "Could not close on IWMWriterNetworkSink
(hr=0x%08x).\n" ), hr );
            return hr;
        }
        _tprintf( _T(  "NetworkSink closed, port %d.\n" ), m_dwPortNum,
hr );
    }
    // Remove the push sink from the writer.
    if( m_pPushSink != NULL )
    {
        hr = m_pWriterAdvanced->RemoveSink( m_pPushSink );
        if( FAILED( hr ) )
        {
            _tprintf( _T(  "Could not remove the Push Sink
(hr=0x%08x).\n" ),  hr );
            return hr;
        }
        // End the push session and cancel the advise connection with the
push sink.
        hr = m_pPushSink->EndSession();
        if( FAILED( hr ) )
        {
            _tprintf( _T(  "Could not close on IWMWriterPushSink
(hr=0x%08x).\n" ), hr );
            return hr;
        }
        if( m_pPushSinkCallbackCtrl != NULL )
        {
            hr = m_pPushSinkCallbackCtrl->Unadvise( this, ( void * )
m_pPushSink );
   if(FAILED(hr))
    return hr;
        }
    }
    //
    // Wait for the reader to tell us that it is closed.
    //
    WaitForSingleObject( m_hEvent, 10000 );
    return hr;
}
//--------------------------------------------------------------------------
--
// Name: CNetWrite::Configure()
// Desc: Configures the object to broadcast a file.
//
// dwPortNum:     Port number for the network sink. Use -1 for no network
sink.
// pwszFile:      The name of a local ASF file to broadcast. Cannot be NULL.
// nMaxClient:    Maximum number of client connections.
// pwszServerURL: URL of a publishing point, for the push sink. Use NULL for
no push sink.
//
// Note:  If dwPortNum is -1, pwszServerURL cannot be NULL, and vice-versa.
//--------------------------------------------------------------------------
--
HRESULT CNetWrite::Configure( DWORD dwPortNum, const WCHAR *pwszFile, UINT
nMaxClient, const WCHAR *pwszServerURL, DWORD dwMaxDuration )
{
    if( pwszFile == NULL || ( dwPortNum == (DWORD)-1 && pwszServerURL ==
NULL ) )
    {
        return E_INVALIDARG;
    }
 if( dwPortNum > 0xFFFF )
 {
  _tprintf( _T( "Invalid port number.\n" ) );
  return E_INVALIDARG;
 }
 else
  m_dwPortNum = dwPortNum;
 if( dwMaxDuration < 0 || dwMaxDuration > 3600 )
 {
  _tprintf( _T( "Invalid duration.\n" ) );
  return E_INVALIDARG;
 }
 else
  m_qwMaxDuration = dwMaxDuration * 10000000;
 if( m_pWriterAdvanced == NULL || m_pReaderAdvanced == NULL )
    {
        return E_UNEXPECTED;
    }
    HRESULT  hr = S_OK;
    // If a port number was specified, create the network sink object.
    if( dwPortNum != (DWORD)-1 )
    {
        hr = WMCreateWriterNetworkSink( &m_pNetSink );
        if( FAILED( hr ) )
        {
            _tprintf( _T( "Could not create Writer Network Sink
(hr=0x%08x).\n" ), hr );
            return hr;
        }
    }
    // If a publishing point URL was specified, create the push sink object.
    if( pwszServerURL != NULL )
    {
        hr = WMCreateWriterPushSink( &m_pPushSink );
        if( FAILED( hr ) )
        {
            _tprintf( _T( "Could not create Writer Push Sink
(hr=0x%08x).\n" ), hr );
            return hr;
        }
        // Set up a callback for event notifications.
        hr = m_pPushSink->QueryInterface( IID_IWMRegisterCallback,
                                          ( void ** )
&m_pPushSinkCallbackCtrl );
        if( FAILED( hr ) )
        {
   return hr;
  }
  hr = m_pPushSinkCallbackCtrl->Advise( this, ( void * ) m_pPushSink );
  if( FAILED( hr ) )
  {
   return hr;
        }
    }
    //
    // Create an event for handling asynchronous calls.
    //
    m_hrAsync = S_OK;
 if( m_hEvent != NULL)
 {
  CloseHandle(m_hEvent);
 }
 m_hEvent = CreateEvent(NULL, FALSE, FALSE, NULL );
 if( NULL ==  m_hEvent )
    {
        DWORD err = GetLastError();
        _tprintf( _T( "Could not Create Event: (hr=0x%08x).\n" ), err );
        return err;
    }
    if( m_pNetSink != NULL )
    {
        //
        // Configure the network sink.
        //
        hr = m_pNetSink->SetNetworkProtocol( WMT_PROTOCOL_HTTP );
        if( FAILED( hr ) )
        {
            _tprintf( _T( "Could not Set Network protocol (hr=0x%08x).\n" ),
hr );
            return hr;
        }
        hr = m_pNetSink->Open( &dwPortNum);
        if( FAILED( hr ) )
        {
            _tprintf( _T( "Network sink failed to open port no %d
(hr=0x%08x).\n" ),
                dwPortNum, hr );
            return hr;
        }
        // Find the host URL, to display to the user.
        DWORD cchURL = 0;
        hr = m_pNetSink->GetHostURL( NULL, &cchURL );
        if( FAILED( hr ) )
        {
            _tprintf( _T( "Could not get the host URL from
IWMWriterNEtworkSink (hr=0x%08x).\n" ),
                hr );
            return hr;
        }
        WCHAR *pwszURL = new WCHAR[cchURL];
        if( pwszURL == NULL )
        {
            _tprintf( _T( "Insufficient Memory" ) );
            return E_OUTOFMEMORY;
        }
        hr = m_pNetSink->GetHostURL( pwszURL, &cchURL );
        if( FAILED( hr ) )
        {
            _tprintf( _T( "Could not get the host URL from
IWMWriterNetworkSink (hr=0x%08x).\n" ),
                hr );
   SAFE_ARRAYDELETE (pwszURL);
            return hr;
        }
        _tprintf( _T( "Connect to %ws\n" ), pwszURL );
        Sleep( 1000 );
        SAFE_ARRAYDELETE (pwszURL);
        //
        // 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 );
            return hr;
        }
        //
        // 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 );
            return hr;
        }
    }
    if( m_pPushSink != NULL )
    {
        //
        // Configure the push sink.
        //
        _tprintf( _T( "Connecting to %ws\n" ), pwszServerURL );
        hr = m_pPushSink->Connect( pwszServerURL, NULL, TRUE );
        if( FAILED( hr ) )
        {
            _tprintf( _T( "Could not connect to server (hr=0x%08x).\n" ),
hr );
            return hr;
        }
        //
        // Wait for the connection to the server to be established.
        //
        if( WaitForSingleObject( m_hEvent, 30000 ) == WAIT_TIMEOUT )
        {
            _tprintf( _T( "Timed out whiletrying to connect to the
server\n" ) );
            return E_FAIL;
        }
        if( FAILED( m_hrAsync ) )
        {
            return m_hrAsync;
        }
        //
        // Add the push sink to the writer object.
        //
        hr = m_pWriterAdvanced->AddSink(m_pPushSink);
        if( FAILED( hr ) )
        {
            _tprintf( _T( "Could not Add Sink (hr=0x%08x).\n" ), hr );
            return hr;
        }
    }
    //
    // Open the requested file.
    //
    hr = m_pReader->Open(pwszFile, this, NULL );
    if( FAILED( hr ) )
    {
        _tprintf( _T( "Could not open file %ws (hr=0x%08x).\n" ), pwszFile
,hr );
        return hr;
    }
    //
    // Wait for the open to finish.
    //
    if( WaitForSingleObject( m_hEvent, 30000 ) == WAIT_TIMEOUT )
    {
        _tprintf( _T( "Timed out whiletrying to open the input file\n" ) );
        return E_FAIL;
    }
    if( FAILED( m_hrAsync ) )
    {
  hr = m_hrAsync;
        _tprintf( _T( "Open failed (hr=0x%08x).\n" ), m_hrAsync );
        return hr;
    }
    //
    // Turn on manual stream selection, so we get all streams. This prevents
the reader
    // from disabling any streams if there are mutual exclusions on some
streams.
    //
    hr = m_pReaderAdvanced->SetManualStreamSelection( TRUE );
    if( FAILED( hr ) )
    {
        _tprintf( _T(  "Failed to set manual stream selection
(hr=0x%08x).\n" ), hr );
        return hr;
    }
    IWMProfile   *pProfile = NULL;
    IWMStreamConfig  *pStream  = NULL;
    // Declare a dummy 'do' loop. On failure, we can break from the loop.
 do
 {
   hr = m_pReader->QueryInterface( IID_IWMProfile, ( VOID ** )&pProfile );
   if( FAILED( hr ) )
   {
    _tprintf( _T(  "Could not QI for IWMProfile (hr=0x%08x).\n" ), hr );
    break;
   }
            // Loop through all the output streams on the reader. For each
stream, configure
            // the reader to deliver compressed samples. This prevents the
reader from decoding
            // each sample. (We want to give the writer compressed samples.)
   DWORD dwStreams = 0;
   hr = pProfile->GetStreamCount( &dwStreams );
   if( FAILED( hr ) )
   {
    _tprintf( _T(  "GetStreamCount on IWMProfile failed (hr=0x%08x).\n" ),
hr );
    break;
   }
   for ( DWORD i = 0; i < dwStreams; i++ )
   {
    hr = pProfile->GetStream( i, &pStream );
    if( FAILED( hr ) )
    {
     _tprintf( _T(  "Could not get Stream %d of %d from IWMProfile
(hr=0x%08x).\n" ),
      i, dwStreams, hr );
     break;
    }
    WORD wStreamNumber = 0;
    //
    //Get the stream number of the current stream.
    //
    hr = pStream->GetStreamNumber( &wStreamNumber );
    if( FAILED( hr ) )
    {
     _tprintf( _T(  "Could not get stream number from IWMStreamConfig %d of
%d (hr=0x%08x).\n" ),
      i, dwStreams, hr );
     break;
    }
    SAFE_RELEASE( pStream );
    //
    //Set the stream to be received in compressed mode.
    //
    hr = m_pReaderAdvanced->SetReceiveStreamSamples( wStreamNumber, TRUE );
    if( FAILED( hr ) )
    {
     _tprintf( _T(  "Could not SetReceivedStreamSamples for stream number %d
(hr=0x%08x).\n" ),
      wStreamNumber, hr );
     break;
    }
   }
   if( FAILED( hr ) )
    break;
   //
   // Turn on the user clock. The CNetWrite object will drive the clock, in
order
            // to make the reader run as quickly as possible.
   //
   hr = m_pReaderAdvanced->SetUserProvidedClock( TRUE );
   if( FAILED( hr ) )
   {
    _tprintf( _T( "SetUserProvidedClock failed (hr=0x%08x).\n" ), hr );
    break;
   }
   //
   // Set same profile on the writer object that we got from the reader.
   //
   hr = m_pWriter->SetProfile( pProfile );
   if( FAILED( hr ) )
   {
    _tprintf( _T(  "Could not set profile on IWMWriter (hr=0x%08x).\n" ),
hr );
    break;
   }
 } while(FALSE);
 SAFE_RELEASE( pStream );
    SAFE_RELEASE( pProfile );
 if(FAILED(hr))
 {
  return hr;  // Something has failed within the do-while loop.
 }
    DWORD   cInputs = 0;
    hr = m_pWriter->GetInputCount( &cInputs );
    if( FAILED( hr ) )
    {
        _tprintf( _T(  "Could not get input count from IWMWriter
(hr=0x%08x).\n" ), hr );
        return hr;
    }
    // Loop through all of the inputs on the writer. For each input, set the
    // input properties to NULL. This tells the writer not to encode the
    // samples it receives. We do this because we are getting encoded
samples
    // from the reader.
    for( DWORD i = 0; i < cInputs; i++ )
    {
        hr = m_pWriter->SetInputProps( i, NULL );
  if(FAILED(hr))
  {
   return hr;
  }
    }
    //
    // Copy header attributes from the reader to the writer.
    //
    hr = WriteHeader( g_wszWMTitle);
    if( FAILED( hr ) )
        return hr;
    hr = WriteHeader( g_wszWMAuthor);
    if( FAILED( hr ) )
        return hr;
    hr = WriteHeader( g_wszWMDescription);
    if( FAILED( hr ) )
        return hr;
    hr = WriteHeader( g_wszWMRating);
    if( FAILED( hr ) )
        return hr;
    hr = WriteHeader( g_wszWMCopyright);
    if( FAILED( hr ) )
        return hr;
    hr = WriteHeader( g_wszWMAlbumTitle);
    if( FAILED( hr ) )
        return hr;
    hr = WriteHeader( g_wszWMTrack);
    if( FAILED( hr ) )
        return hr;
    hr = WriteHeader( g_wszWMPromotionURL);
    if( FAILED( hr ) )
        return hr;
    hr = WriteHeader( g_wszWMAlbumCoverURL);
    if( FAILED( hr ) )
        return hr;
    hr = WriteHeader( g_wszWMGenre);
    if( FAILED( hr ) )
        return hr;
    hr = WriteHeader( g_wszWMYear);
    if( FAILED( hr ) )
        return hr;
    hr = WriteHeader( g_wszWMGenreID);
    if( FAILED( hr ) )
        return hr;
    hr = WriteHeader( g_wszWMMCDI);
    if( FAILED( hr ) )
        return hr;
    hr = WriteHeader( g_wszWMBannerImageType );
    if( FAILED( hr ) )
        return hr;
    hr = WriteHeader( g_wszWMBannerImageData );
    if( FAILED( hr ) )
        return hr;
    hr = WriteHeader( g_wszWMBannerImageURL );
    if( FAILED( hr ) )
        return hr;
    hr = WriteHeader( g_wszWMCopyrightURL );
    if( FAILED( hr ) )
        return hr;
    //
    // Header has been written. Write the script (if any).
    //
    hr = WriteScript();
    return hr;
}
//--------------------------------------------------------------------------
--
// Name: CNetWrite::WriteHeader()
// Desc: Copies specified metadata from the reader to the writer.
//
// pwszName: Specifies the name of the metadata attribute to copy.
//--------------------------------------------------------------------------
--
HRESULT CNetWrite::WriteHeader(const WCHAR *pwszName)
{
    WORD    nstreamNum = 0;
    WORD    cbLength = 0;
    WMT_ATTR_DATATYPE type;
    HRESULT    hr = S_OK;
    BYTE*    pValue = NULL;
    //
    // Get the number of bytes to allocate for the attribute data.
    //
    hr = m_pReaderHeaderInfo->GetAttributeByName( &nstreamNum,
        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 )
    {
        return S_OK;
    }
    // Allocate a buffer.
    pValue = new BYTE[ cbLength ];
    if( NULL == pValue )
    {
        _tprintf( _T( "Unable to allocate memory for the Attribute name
%ws" ), pwszName);
        return E_OUTOFMEMORY;
    }
    // Declare a dummy 'do' loop. On failure, we can break from the loop.
    do
    {
        // Get the value of the attribute from the reader.
        hr = m_pReaderHeaderInfo->GetAttributeByName( &nstreamNum,
            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 on the writer.
        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);
            break;
        }
    }
    while(FALSE);
    SAFE_ARRAYDELETE( pValue );
    return hr;
}
//--------------------------------------------------------------------------
--
// Name: CNetWrite::WriteScript()
// Desc: Copies all scripts from the reader's header to the writer's header.
//--------------------------------------------------------------------------
--
HRESULT CNetWrite::WriteScript()
{
    HRESULT hr = S_OK;
    WCHAR* pwszCommand = NULL;
    WCHAR* pwszType = NULL;
    QWORD cnsScriptTime = 0;
    WORD cScript = 0;
    WORD   cchTypeLen = 0;
    WORD cchCommandLen = 0;
    // Get the number of scripts in the header. This might be zero.
    hr = m_pReaderHeaderInfo->GetScriptCount( &cScript );
    if( FAILED( hr ) )
    {
        _tprintf( _T( "GetScriptCount failed (hr=0x%08x).\n" ), hr);
        return hr;
    }
    // Loop through the scripts. For each script, copy it to the writer's
header.
    for( int i = 0; i < cScript; i++)
    {
        //
        // Get the memory required for this script.
        //
        hr = m_pReaderHeaderInfo->GetScript( (WORD)i ,
                              NULL ,
                                             &cchTypeLen ,
                                             NULL ,
                                             &cchCommandLen ,
                                             &cnsScriptTime );
        if( FAILED( hr ) )
        {
            _tprintf( _T( "GetScript failed for Script no %d
(hr=0x%08x).\n" ), i, hr );
            break;
        }
        // Allocate buffers.
        pwszType = new WCHAR[cchTypeLen];
        pwszCommand = new WCHAR[cchCommandLen];
        if( pwszType == NULL || pwszCommand == NULL)
        {
            _tprintf( _T( "Insufficient Memory" ) );
            hr = E_OUTOFMEMORY;
            break;
        }
        //
        // Get the script.
        //
        hr = m_pReaderHeaderInfo->GetScript( (WORD)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 = m_pWriterHeaderInfo->AddScript( pwszType ,
          pwszCommand ,
                                             cnsScriptTime );
        if( FAILED( hr ) )
        {
            _tprintf( _T( "AddScript failed for Script no %d
(hr=0x%08x).\n" ), i, hr);
            break;
        }
        SAFE_ARRAYDELETE( pwszType );
        SAFE_ARRAYDELETE( pwszCommand );
        pwszType = pwszCommand = NULL;
        cchTypeLen  = 0;
        cchCommandLen = 0;
    }
    SAFE_ARRAYDELETE( pwszType );
    SAFE_ARRAYDELETE( pwszCommand );
    return hr;
}
//**************************************************************************
***
//
// Microsoft Windows Media
// Copyright ( C) Microsoft Corporation. All rights reserved.
//
// FileName:            NetWrite.cpp
//
// Abstract:            CNetWrite class declaration
//
//
//**************************************************************************
***
#pragma once
#include "wmsdk.h"
#ifndef SAFE_RELEASE
#define SAFE_RELEASE( x )  \
    if( NULL != x )       \
    {                      \
        x->Release( );     \
        x = NULL;          \
    }
#endif
#ifndef SAFE_DELETE
#define SAFE_DELETE( x ) \
    if( NULL != x )  \
    {      \
        delete x;   \
        x = NULL;   \
    }
#endif
#ifndef SAFE_ARRAYDELETE
#define SAFE_ARRAYDELETE( x ) \
    if( NULL != x )  \
    {      \
        delete[] x;   \
        x = NULL;   \
    }
#endif
#define NETWRITE_ASYNC_EVENT _T( "6d12fe9b-d029-4d08-b2eb-92c8cab323c7" )
class CNetWrite :public IWMReaderCallback, public IWMReaderCallbackAdvanced
{
public:
    CNetWrite();
    ~CNetWrite();
    HRESULT Configure( DWORD dwPortNum, const WCHAR *pwszFile, UINT
nMaxClient, const WCHAR *pwszServerURL, DWORD dwMaxDuration );
    HRESULT WritetoNet();
 HRESULT EndWritetoNet();
    HRESULT Init();
    //
    //Methods of IWMReaderCallback
    //
    HRESULT STDMETHODCALLTYPE OnSample( /* [in] */ DWORD dwOutputNum,
        /* [in] */ QWORD cnsSampleTime,
        /* [in] */ QWORD cnsSampleDuration,
        /* [in] */ DWORD dwFlags,
        /* [in] */ INSSBuffer __RPC_FAR *pSample,
        /* [in] */ void __RPC_FAR *pvContext);
        HRESULT STDMETHODCALLTYPE OnStatus( /* [in] */ WMT_STATUS Status,
        /* [in] */ HRESULT hr,
        /* [in] */ WMT_ATTR_DATATYPE dwType,
        /* [in] */ BYTE __RPC_FAR *pValue,
        /* [in] */ void __RPC_FAR *pvContext);
        //
        //Methods of IWMReaderCallbackAdvanced
        //
        HRESULT STDMETHODCALLTYPE OnStreamSample( /* [in] */ WORD
wStreamNum,
        /* [in] */ QWORD cnsSampleTime,
        /* [in] */ QWORD cnsSampleDuration,
        /* [in] */ DWORD dwFlags,
        /* [in] */ INSSBuffer __RPC_FAR *pSample,
        /* [in] */ void __RPC_FAR *pvContext);
        HRESULT STDMETHODCALLTYPE OnTime( /* [in] */ QWORD cnsCurrentTime,
        /* [in] */ void __RPC_FAR *pvContext);
        HRESULT STDMETHODCALLTYPE OnStreamSelection( /* [in] */ WORD
wStreamCount,
        /* [in] */ WORD __RPC_FAR *pStreamNumbers,
        /* [in] */ WMT_STREAM_SELECTION __RPC_FAR *pSelections,
        /* [in] */ void __RPC_FAR *pvContext)
    {
        return S_OK;
    }
    HRESULT STDMETHODCALLTYPE OnOutputPropsChanged( /* [in] */ DWORD
dwOutputNum,
        /* [in] */ WM_MEDIA_TYPE __RPC_FAR *pMediaType,
        /* [in] */ void __RPC_FAR *pvContext)
    {
        return S_OK;
    }
    HRESULT STDMETHODCALLTYPE AllocateForStream( /* [in] */ WORD wStreamNum,
        /* [in] */ DWORD cbBuffer,
        /* [out] */ INSSBuffer __RPC_FAR *__RPC_FAR *ppBuffer,
        /* [in] */ void __RPC_FAR *pvContext)
    {
        return E_NOTIMPL;
    }
    HRESULT STDMETHODCALLTYPE AllocateForOutput( /* [in] */ DWORD
dwOutputNum,
        /* [in] */ DWORD cbBuffer,
        /* [out] */ INSSBuffer __RPC_FAR *__RPC_FAR *ppBuffer,
        /* [in] */ void __RPC_FAR *pvContext)
    {
        return E_NOTIMPL;
    }
    //
    //Methods of IUnknown
    //
    HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid,
        void __RPC_FAR *__RPC_FAR *ppvObject)
    {
        if( riid == IID_IWMReaderCallback )
        {
            *ppvObject = ( IWMReaderCallback* )this;
        }
        else if( riid == IID_IWMReaderCallbackAdvanced )
        {
            *ppvObject = ( IWMReaderCallbackAdvanced* )this;
        }
        else if( riid == IID_IWMStatusCallback )
        {
            *ppvObject = ( IWMStatusCallback* )this;
        }
        else
        {
            return E_NOINTERFACE;
        }
        return S_OK;
    }
    ULONG STDMETHODCALLTYPE AddRef( void ) { return 1; }
    ULONG STDMETHODCALLTYPE Release( void ) { return 1; }
private:
    HRESULT WriteHeader( const WCHAR * pwszName );
    HRESULT WriteScript();
public:
    HANDLE   m_hEvent;        // Event for handling asynchronous calls
private:
    HRESULT   m_hrAsync;       // Receives error or success codes for
asynchronous operations.
    QWORD   m_qwTime;        // Specifies the time interval for the next
batch of samples.
 QWORD   m_qwStartTime;   // The time at which the first sample was sent
 QWORD   m_qwMaxDuration; // Maximum duration of the video stream
 DWORD   m_dwPortNum;  // Port number for incoming connections
    // Miscellaneous interfaces that we need.
    IWMWriterAdvanced*  m_pWriterAdvanced;
    IWMReaderAdvanced*  m_pReaderAdvanced;
    IWMReader*   m_pReader;
    IWMWriter*   m_pWriter;
    IWMWriterNetworkSink* m_pNetSink;
    IWMWriterPushSink*          m_pPushSink;
    IWMRegisterCallback*        m_pPushSinkCallbackCtrl;
    bool   m_bEOF;
    IWMHeaderInfo*  m_pReaderHeaderInfo;
    IWMHeaderInfo*  m_pWriterHeaderInfo;
};


Relevant Pages