//------------------------------------------------------------------------------
// File: AppStream.cpp
//
// Desc: DirectShow sample code - implementation of CAppStream class.
//
// Copyright (c) 2000, Microsoft Corporation.  All rights reserved.
//------------------------------------------------------------------------------


#include <windows.h>
#include <malloc.h>
#include <mediaobj.h>
#include <mmsystem.h>

#include "uuids.h"
#include "dmo.h"
#include "wave.h"
#include "appstream.h"
#include "dxutil.h"     //to use free memory macro

//
// CAppStream - reads data from a WAV file, transforms it using a DMO and then 
//              copies the results into an output buffer.  
//
//-----------------------------------------------------------------------------
// Name: CAppStream::CAppStream()
// Desc: Constructor
//-----------------------------------------------------------------------------
CAppStream::CAppStream(): 
    m_pObject(NULL),
    m_pObjectInPlace(NULL),
    m_pbInData(NULL),
    m_pwfx(NULL),
    m_uDataSize(0)
{
    ZeroMemory(&m_mt, sizeof(m_mt));
}

//-----------------------------------------------------------------------------
// Name: CAppStream::~CAppStream()
// Desc: Destructor
//-----------------------------------------------------------------------------
CAppStream::~CAppStream() 
{

	SAFE_RELEASE( m_pObject);
	SAFE_RELEASE( m_pObjectInPlace);
	SafeGlobalFree(m_pbInData);

}

//-----------------------------------------------------------------------------
// Name: CAppStream::StreamData()
// Desc: Load data from input file, create media object and set input&output type
//-----------------------------------------------------------------------------

HRESULT CAppStream::StreamData( LPTSTR lpszInputFile, 
                                REFGUID rclsid, 
                                HWND hDlg,
                                BYTE **ppbOutData, 
                                ULONG *pbDataSize, 
                                LPWAVEFORMATEX  *ppwfx )

{
    HRESULT hr;
    hr = Init( lpszInputFile, rclsid, hDlg );
    if ( FAILED( hr ) ) {
       return hr;
    }

    hr = Stream( ppbOutData, pbDataSize, ppwfx );
    if ( FAILED( hr )) {
        MessageBox( hDlg, TEXT("Streaming data failed."), TEXT(DEMO_NAME), 
                                    MB_OK | MB_ICONERROR );
		return hr;
    } 
    return S_OK;
}


//-----------------------------------------------------------------------------
// Name: CAppStream::Init()
// Desc: Load data from input file, create media object and set input&output type
//-----------------------------------------------------------------------------

HRESULT CAppStream::Init(LPTSTR lpszInputFile, REFGUID rclsid, HWND hDlg) 
{

    // create DMO
    HRESULT hr = CoCreateInstance(rclsid,
                         NULL,
                         CLSCTX_INPROC,
                         IID_IMediaObject,
                         (void **) &m_pObject);
    if ( FAILED( hr ) ){
        MessageBox( hDlg, TEXT("Can't create this DMO."), TEXT(DEMO_NAME),MB_OK|MB_ICONERROR );
        return hr;
    }

    hr = m_pObject->QueryInterface(IID_IMediaObjectInPlace, (void**)&m_pObjectInPlace);
    
    // read wave file
    if( WaveLoadFile( lpszInputFile, (UINT*) &m_uDataSize, &m_pwfx, &m_pbInData ) != 0 ){
        MessageBox( hDlg, TEXT("Can't load input file."), TEXT(DEMO_NAME),MB_OK|MB_ICONERROR );
        return E_FAIL;
    }


    if( m_pwfx->wFormatTag != WAVE_FORMAT_PCM ) {
        MessageBox( hDlg, TEXT("Can't process compressed data."), TEXT(DEMO_NAME),MB_OK|MB_ICONERROR );
        return E_FAIL;
        
    }

    m_mt.majortype  = MEDIATYPE_Audio;
    m_mt.subtype    = MEDIASUBTYPE_PCM;
    m_mt.formattype = FORMAT_WaveFormatEx;
    m_mt.cbFormat = sizeof(WAVEFORMATEX);
    m_mt.pbFormat = (BYTE*) (m_pwfx);
    m_mt.pUnk = NULL;       // CopyMediaType will crash if we don't intialize this
   
    hr = m_pObject->SetInputType( 0,    //Input Stream index
                                  &m_mt, 
                                  0 );  // No flags specified
    if ( FAILED( hr ) ){
        MessageBox( hDlg, TEXT("Can't set input type."), TEXT(DEMO_NAME),MB_OK | MB_ICONERROR );
        return hr;
    }

    hr = m_pObject->SetOutputType( 0,       // Output Stream Index
                                   &m_mt, 
                                   0);  // No flags specified
    if ( FAILED( hr ) ){
       MessageBox( hDlg, TEXT("Can't set output type."), TEXT(DEMO_NAME),MB_OK | MB_ICONERROR );
       return hr;
    }
 
    return S_OK;
}


//-----------------------------------------------------------------------------
// Name: CAppStream::Stream()
// Desc: called to get the output from DMO
//-----------------------------------------------------------------------------

HRESULT CAppStream::Stream( BYTE **ppbOutData, ULONG *pbDataSize, LPWAVEFORMATEX *ppwfx )
{
    HRESULT         hr = S_OK;
    BYTE            *pOut;

    *pbDataSize     = m_uDataSize;
    *ppwfx          = m_pwfx;

    if ( m_pObjectInPlace ){

        pOut = new BYTE [m_uDataSize];

		if( pOut == 0 ){
			return E_OUTOFMEMORY;
		}
        CopyMemory(pOut, m_pbInData, m_uDataSize);

        // pass the number of samples to Process()
        hr = m_pObjectInPlace->Process( m_uDataSize, 
                                        pOut, 
                                        0, 
                                        DMO_INPLACE_NORMAL);
        if( FAILED( hr ) ){
            return hr;
        }       
        *ppbOutData = pOut; 
        SAFE_RELEASE( m_pObjectInPlace );
    }

    else
    {
        CMediaBuffer            *pInputBuffer;
        const REFERENCE_TIME    rtStart     = 0;
        const REFERENCE_TIME    rtStop      = 0;
        BYTE*                   pBuffer;
        DWORD                   dwLength;

        // create and fill CMediaBuffer
        hr = CreateBuffer(m_uDataSize, &pInputBuffer);
        if( FAILED( hr ) ){
            return hr;
        }
        hr = pInputBuffer->GetBufferAndLength( &pBuffer, &dwLength );
        if( FAILED( hr ) ){
            return hr;
        }
        CopyMemory(pBuffer, m_pbInData, m_uDataSize);
        hr = pInputBuffer->SetLength( m_uDataSize );
        if( FAILED( hr ) ){
            return hr;
        }

        // call processInput
        hr = m_pObject->ProcessInput( 0,
                                pInputBuffer,
                                DMO_INPUT_DATA_BUFFERF_SYNCPOINT,
                                rtStart,
                                rtStop - rtStart);
        if( FAILED( hr ) ){
            return hr;
        }
    
        //release input buffer
        SAFE_RELEASE( pInputBuffer );

        // retrieve the output data from DMO and put into pOut 
        if(S_FALSE == hr){
            return E_FAIL;
        } else {
            hr = ProcessOutputs( &pOut );
            if( FAILED( hr ) ){
                return hr;
            }
        }

        *ppbOutData = pOut;
        SAFE_RELEASE( m_pObject );
    }

    return S_OK;
}


//-----------------------------------------------------------------------------
// Name: CAppStream::ProcessOutputs()
// Desc: retrieve the output data from DMO
//-----------------------------------------------------------------------------

HRESULT CAppStream::ProcessOutputs( BYTE **ppbOutData )
{
    HRESULT hr          = S_OK;
    DWORD   dwStatus;
    ULONG   ulSize;
    BYTE    *pOut;

    CMediaBuffer            *pOutputBuffer;
    DMO_OUTPUT_DATA_BUFFER  dataBufferStruct;

    hr = CreateBuffer( m_uDataSize,&pOutputBuffer );
    if ( FAILED( hr ) ) {
        return hr;
    }
    dataBufferStruct.pBuffer = pOutputBuffer;
    dataBufferStruct.dwStatus = 0;      // No flag is set
    dataBufferStruct.rtTimestamp = 0;   // not used in ProcessOutput()
    dataBufferStruct.rtTimelength = 0;  // not used in ProcessOutput()

    *ppbOutData = new BYTE[m_uDataSize];    
    if( *ppbOutData == 0 ){
       return E_OUTOFMEMORY;
    }
    //process until no more data
    if (SUCCEEDED(hr)) do {
        hr = m_pObject->ProcessOutput(  DMO_PROCESS_OUTPUT_DISCARD_WHEN_NO_BUFFER,
                                        1, //output buffer count
                                        &dataBufferStruct,
                                        &dwStatus );
        if ( FAILED( hr ) ) {             
            return hr;
        }
        
        if( SUCCEEDED(hr) && (hr != S_FALSE) ) {            
            hr = dataBufferStruct.pBuffer->GetBufferAndLength(&pOut, &ulSize);
            if ( FAILED( hr ) ) {
                return hr;
            }
    
            CopyMemory(*ppbOutData, pOut, m_uDataSize);

            hr = dataBufferStruct.pBuffer->SetLength( 0 );
            if( FAILED( hr ) ) {
                break;
            }
        }

    } while ( dataBufferStruct.dwStatus & DMO_OUTPUT_DATA_BUFFERF_INCOMPLETE );

    // free output buffer allocated:       
    SAFE_RELEASE( pOutputBuffer );

    // Send Discontinuity on output stream
    hr = m_pObject->Discontinuity( 0 );
    if ( FAILED( hr ) ) {      
        return hr;
    }
    
    return hr;
}

//-----------------------------------------------------------------------------
// Name: CreateBuffer()
// Desc: create a CMediaBuffer
//-----------------------------------------------------------------------------
HRESULT CreateBuffer(
    DWORD cbMaxLength,
    CMediaBuffer **ppBuffer)
{
    CMediaBuffer *pBuffer = new CMediaBuffer( cbMaxLength );
    if ( pBuffer == NULL || FAILED( pBuffer->Init() ) ) {
        delete pBuffer;
        *ppBuffer = NULL;
        return E_OUTOFMEMORY;
    }
    *ppBuffer = pBuffer;
    (*ppBuffer)->AddRef();
    return S_OK;
}


