www.pudn.com > WavDest.rar > wavdest.cpp


//------------------------------------------------------------------------------ 
// File: WavDest.cpp 
// 
// Desc: DirectShow sample code - a filter for writing WAV audio files (based 
//       on CTransformFilter). 
// 
// Copyright (c) Microsoft Corporation.  All rights reserved. 
//------------------------------------------------------------------------------ 
 
 
// 
// To use this filter to write audio data into a WAV file:  
// 
// Use GraphEdit (or a custom DirectShow app) to build a filter graph 
// with an audio stream connected to this filter's input pin and the File Writer 
// filter connected to its output pin. Run the graph and you will have 
// written a wave file. 
// 
//============================================================================= 
//============================================================================= 
 
#include  
#include "wavdest.h" 
#include  
#include  
 
 
// {3C78B8E2-6C4D-11d1-ADE2-0000F8754B99} 
static const GUID CLSID_WavDest = 
{ 0x3c78b8e2, 0x6c4d, 0x11d1, { 0xad, 0xe2, 0x0, 0x0, 0xf8, 0x75, 0x4b, 0x99 } }; 
 
 
const AMOVIESETUP_FILTER sudWavDest = 
{ 
    &CLSID_WavDest,           // clsID 
    L"WAV Dest",              // strName 
    MERIT_DO_NOT_USE,         // dwMerit 
    0,                        // nPins 
    0                         // lpPin 
}; 
 
 
// Global data 
CFactoryTemplate g_Templates[]= { 
    {L"WAV Dest", &CLSID_WavDest, CWavDestFilter::CreateInstance, NULL, &sudWavDest}, 
}; 
 
int g_cTemplates = sizeof(g_Templates) / sizeof(g_Templates[0]); 
 
 
// ------------------------------------------------------------------------ 
// filter constructor 
 
#pragma warning(disable:4355) 
 
 
CWavDestFilter::CWavDestFilter(LPUNKNOWN pUnk, HRESULT *phr) : 
                CTransformFilter(NAME("WavDest filter"), pUnk, CLSID_WavDest) 
{ 
    ASSERT(m_pOutput == 0); 
    ASSERT(phr); 
 
    if(SUCCEEDED(*phr)) 
    { 
        // Create an output pin so we can have control over the connection 
        // media type. 
        CWavDestOutputPin *pOut = new CWavDestOutputPin(this, phr); 
 
        if(pOut) 
        { 
            if(SUCCEEDED(*phr)) 
            { 
                m_pOutput = pOut; 
            } 
            else 
            { 
                delete pOut; 
            } 
        } 
        else 
        { 
            *phr = E_OUTOFMEMORY; 
        } 
 
        // 
        // NOTE!: If we've created our own output pin we must also create 
        // the input pin ourselves because the CTransformFilter base class  
        // will create an extra output pin if the input pin wasn't created.         
        // 
        CTransformInputPin *pIn = new CTransformInputPin(NAME("Transform input pin"), 
                                        this,              // Owner filter 
                                        phr,               // Result code 
                                        L"In");            // Pin name 
        // a failed return code should delete the object 
        if(pIn) 
        { 
            if(SUCCEEDED(*phr)) 
            { 
                m_pInput = pIn; 
            } 
            else 
            { 
                delete pIn; 
            } 
        } 
        else 
        { 
            *phr = E_OUTOFMEMORY; 
        } 
    } 
} 
 
 
// ------------------------------------------------------------------------ 
// destructor 
 
CWavDestFilter::~CWavDestFilter() 
{ 
} 
 
 
CUnknown * WINAPI CWavDestFilter::CreateInstance(LPUNKNOWN pUnk, HRESULT * phr) 
{ 
    return new CWavDestFilter(pUnk, phr); 
} 
 
 
// 
// CWavDestFilter::CheckTransform 
// 
// To be able to transform, the formats must be identical 
// 
HRESULT CWavDestFilter::CheckTransform(const CMediaType *mtIn, const CMediaType *mtOut) 
{ 
    HRESULT hr; 
 
    if(FAILED(hr = CheckInputType(mtIn))) 
    { 
        return hr; 
    } 
 
    return NOERROR; 
 
} // CheckTransform 
 
 
// overridden because we need to know if Deliver() failed. 
 
HRESULT CWavDestFilter::Receive(IMediaSample *pSample) 
{ 
    ULONG cbOld = m_cbWavData; 
    HRESULT hr = CTransformFilter::Receive(pSample); 
 
    // don't update the count if Deliver() downstream fails. 
    if(hr != S_OK) 
    { 
        m_cbWavData = cbOld; 
    } 
 
    return hr; 
} 
 
// 
// CWavDestFilter::Transform 
// 
// 
HRESULT CWavDestFilter::Transform(IMediaSample *pIn, IMediaSample *pOut) 
{ 
    REFERENCE_TIME rtStart, rtEnd; 
 
    // First just copy the data to the output sample 
    HRESULT hr = Copy(pIn, pOut); 
    if(FAILED(hr)) 
    { 
        return hr; 
    } 
 
    // Prepare it for writing     
    LONG lActual = pOut->GetActualDataLength(); 
 
    if(m_cbWavData + m_cbHeader + lActual < m_cbWavData + m_cbHeader) 
    {  
        return E_FAIL;      // overflow 
    } 
 
    rtStart = m_cbWavData + m_cbHeader; 
    rtEnd   = rtStart + lActual; 
    m_cbWavData += lActual; 
 
    EXECUTE_ASSERT(pOut->SetTime(&rtStart, &rtEnd) == S_OK); 
 
    return S_OK; 
} 
 
 
// 
// CWavDestFilter::Copy 
// 
// Make destination an identical copy of source 
// 
HRESULT CWavDestFilter::Copy(IMediaSample *pSource, IMediaSample *pDest) const 
{ 
    CheckPointer(pSource,E_POINTER); 
    CheckPointer(pDest,E_POINTER); 
 
    // Copy the sample data 
 
    BYTE *pSourceBuffer, *pDestBuffer; 
    long lSourceSize = pSource->GetActualDataLength(); 
 
#ifdef DEBUG     
    long lDestSize = pDest->GetSize(); 
    ASSERT(lDestSize >= lSourceSize); 
#endif 
 
    pSource->GetPointer(&pSourceBuffer); 
    pDest->GetPointer(&pDestBuffer); 
 
    CopyMemory((PVOID) pDestBuffer,(PVOID) pSourceBuffer,lSourceSize); 
 
    // Copy the sample times 
 
    REFERENCE_TIME TimeStart, TimeEnd; 
    if(NOERROR == pSource->GetTime(&TimeStart, &TimeEnd)) 
    { 
        pDest->SetTime(&TimeStart, &TimeEnd); 
    } 
 
    LONGLONG MediaStart, MediaEnd; 
    if(pSource->GetMediaTime(&MediaStart,&MediaEnd) == NOERROR) 
    { 
        pDest->SetMediaTime(&MediaStart,&MediaEnd); 
    } 
 
    // Copy the media type 
    AM_MEDIA_TYPE *pMediaType; 
    pSource->GetMediaType(&pMediaType); 
    pDest->SetMediaType(pMediaType); 
    DeleteMediaType(pMediaType); 
 
    // Copy the actual data length 
    long lDataLength = pSource->GetActualDataLength(); 
    pDest->SetActualDataLength(lDataLength); 
 
    return NOERROR; 
 
} // Copy 
 
 
// 
// CheckInputType 
// 
HRESULT CWavDestFilter::CheckInputType(const CMediaType* mtIn) 
{ 
    if(mtIn->formattype == FORMAT_WaveFormatEx) 
    { 
        return S_OK; 
    } 
    return S_FALSE; 
} 
 
// 
// GetMediaType 
// 
HRESULT CWavDestFilter::GetMediaType(int iPosition, CMediaType *pMediaType) 
{ 
    ASSERT(iPosition == 0 || iPosition == 1); 
 
    if(iPosition == 0) 
    { 
        CheckPointer(pMediaType,E_POINTER); 
 
        pMediaType->SetType(&MEDIATYPE_Stream); 
        pMediaType->SetSubtype(&MEDIASUBTYPE_WAVE); 
        return S_OK; 
    } 
 
    return VFW_S_NO_MORE_ITEMS; 
} 
 
// 
// DecideBufferSize 
// 
// Tell the output pin's allocator what size buffers we 
// require. Can only do this when the input is connected 
// 
HRESULT CWavDestFilter::DecideBufferSize(IMemAllocator *pAlloc, 
                                         ALLOCATOR_PROPERTIES *pProperties) 
{ 
    HRESULT hr = NOERROR; 
 
    // Is the input pin connected 
    if(m_pInput->IsConnected() == FALSE) 
    { 
        return E_UNEXPECTED; 
    } 
 
    CheckPointer(pAlloc,E_POINTER); 
    CheckPointer(pProperties,E_POINTER); 
 
    pProperties->cBuffers = 1; 
    pProperties->cbAlign  = 1; 
 
    // Get input pin's allocator size and use that 
    ALLOCATOR_PROPERTIES InProps; 
    IMemAllocator * pInAlloc = NULL; 
 
    hr = m_pInput->GetAllocator(&pInAlloc); 
    if(SUCCEEDED(hr)) 
    { 
        hr = pInAlloc->GetProperties(&InProps); 
        if(SUCCEEDED(hr)) 
        { 
            pProperties->cbBuffer = InProps.cbBuffer; 
        } 
        pInAlloc->Release(); 
    } 
 
    if(FAILED(hr)) 
        return hr; 
 
    ASSERT(pProperties->cbBuffer); 
 
    // Ask the allocator to reserve us some sample memory, NOTE the function 
    // can succeed (that is return NOERROR) but still not have allocated the 
    // memory that we requested, so we must check we got whatever we wanted 
 
    ALLOCATOR_PROPERTIES Actual; 
    hr = pAlloc->SetProperties(pProperties,&Actual); 
    if(FAILED(hr)) 
    { 
        return hr; 
    } 
 
    ASSERT(Actual.cBuffers == 1); 
 
    if(pProperties->cBuffers > Actual.cBuffers || 
        pProperties->cbBuffer > Actual.cbBuffer) 
    { 
        return E_FAIL; 
    } 
 
    return NOERROR; 
 
} // DecideBufferSize 
 
 
// 
// StartStreaming 
// 
// Compute the header size to allow space for us to write it at the end. 
// 
// 00000000    RIFF (00568BFE) 'WAVE' 
// 0000000C        fmt  (00000010) 
// 00000024        data (00568700) 
// 0056872C 
// 
HRESULT CWavDestFilter::StartStreaming() 
{ 
    // leave space for the header 
    m_cbHeader = sizeof(RIFFLIST) +  
                 sizeof(RIFFCHUNK) +  
                 m_pInput->CurrentMediaType().FormatLength() +  
                 sizeof(RIFFCHUNK); 
 
    m_cbWavData = 0; 
    return S_OK; 
} 
 
 
// 
// StopStreaming 
// 
// Write out the header 
// 
HRESULT CWavDestFilter::StopStreaming() 
{ 
    IStream *pStream; 
    if(m_pOutput->IsConnected() == FALSE) 
        return E_FAIL; 
 
    IPin * pDwnstrmInputPin = m_pOutput->GetConnected(); 
 
    if(!pDwnstrmInputPin) 
        return E_FAIL; 
 
    HRESULT hr = ((IMemInputPin *) pDwnstrmInputPin)->QueryInterface(IID_IStream,  
                                                                    (void **)&pStream); 
    if(SUCCEEDED(hr)) 
    { 
        BYTE *pb = (BYTE *)_alloca(m_cbHeader); 
 
        RIFFLIST  *pRiffWave = (RIFFLIST *)pb; 
        RIFFCHUNK *pRiffFmt  = (RIFFCHUNK *)(pRiffWave + 1); 
        RIFFCHUNK *pRiffData = (RIFFCHUNK *)(((BYTE *)(pRiffFmt + 1)) +  
                               m_pInput->CurrentMediaType().FormatLength()); 
 
        pRiffData->fcc = FCC('data'); 
        pRiffData->cb = m_cbWavData; 
 
        pRiffFmt->fcc = FCC('fmt '); 
        pRiffFmt->cb = m_pInput->CurrentMediaType().FormatLength(); 
        CopyMemory(pRiffFmt + 1, m_pInput->CurrentMediaType().Format(), pRiffFmt->cb); 
 
        pRiffWave->fcc = FCC('RIFF'); 
        pRiffWave->cb = m_cbWavData + m_cbHeader - sizeof(RIFFCHUNK); 
        pRiffWave->fccListType = FCC('WAVE'); 
 
        LARGE_INTEGER li; 
        ZeroMemory(&li, sizeof(li)); 
 
        hr = pStream->Seek(li, STREAM_SEEK_SET, 0); 
        if(SUCCEEDED(hr)) 
        { 
            hr = pStream->Write(pb, m_cbHeader, 0); 
        } 
        pStream->Release(); 
    } 
 
    return hr; 
} 
 
// 
// CWavDestOutputPin::CWavDestOutputPin  
// 
CWavDestOutputPin::CWavDestOutputPin(CTransformFilter *pFilter, HRESULT * phr) : 
        CTransformOutputPin(NAME("WavDest output pin"), pFilter, phr, L"Out") 
{ 
    // Empty 
} 
 
 
// 
// CWavDestOutputPin::EnumMediaTypes 
// 
STDMETHODIMP CWavDestOutputPin::EnumMediaTypes( IEnumMediaTypes **ppEnum ) 
{ 
    return CBaseOutputPin::EnumMediaTypes(ppEnum); 
} 
 
// 
// CWavDestOutputPin::CheckMediaType 
// 
// Make sure it's our default type 
// 
HRESULT CWavDestOutputPin::CheckMediaType(const CMediaType* pmt) 
{ 
    CheckPointer(pmt,E_POINTER); 
 
    if(pmt->majortype == MEDIATYPE_Stream && pmt->subtype == MEDIASUBTYPE_WAVE) 
        return S_OK; 
    else 
        return S_FALSE; 
} 
 
 
//////////////////////////////////////////////////////////////////////// 
// 
// Exported entry points for registration and unregistration  
// (in this case they only call through to default implementations). 
// 
//////////////////////////////////////////////////////////////////////// 
 
STDAPI DllRegisterServer() 
{ 
    return AMovieDllRegisterServer2(TRUE); 
} 
 
STDAPI DllUnregisterServer() 
{ 
    return AMovieDllRegisterServer2(FALSE); 
} 
 
// 
// DllEntryPoint 
// 
extern "C" BOOL WINAPI DllEntryPoint(HINSTANCE, ULONG, LPVOID); 
 
BOOL APIENTRY DllMain(HANDLE hModule,  
                      DWORD  dwReason,  
                      LPVOID lpReserved) 
{ 
	return DllEntryPoint((HINSTANCE)(hModule), dwReason, lpReserved); 
}