/****************************************************************************
 *
 *  ACMSTRM.C
 *
 *  routine for compressing audio with the ACM
 *
 *  Copyright (c) 1992 - 1995 Microsoft Corporation.  All Rights Reserved.
 *
 *  You have a royalty-free right to use, modify, reproduce and
 *  distribute the Sample Files (and/or any modified version) in
 *  any way you find useful, provided that you agree that
 *  Microsoft has no warranty obligations or liability for any
 *  Sample Application Files which are modified.
 *
 ***************************************************************************/


//
// What this file does:
//
// Given an audio Stream (that is, essentially, a function that it can call
// to get audio samples), this presents the same sort of interface and allows
// other people to call it to get compressed audio.
//

#include <win32.h>
#include <compman.h>
#include <string.h>
#include <stdlib.h>
#include <malloc.h>
#include <ctype.h>
#include <avifmt.h>
#include <mmreg.h>
#include <msacm.h>
#include "avifile.h"
#include "avifilei.h"	// uUseCount
#include "acmcmprs.h"
#include "avifile.rc"	// for resource ids
#include "debug.h"

EXTERN_C HINSTANCE ghMod;


#define WAVEFORMATSIZE(pwf) \
	((((LPWAVEFORMAT)(pwf))->wFormatTag == WAVE_FORMAT_PCM) ? \
		sizeof(PCMWAVEFORMAT) : \
		sizeof(WAVEFORMATEX) + ((LPWAVEFORMATEX)(pwf))->cbSize)


HRESULT CACMCmpStream::MakeInst(
	IUnknown FAR*	pUnknownOuter,
	const IID FAR&	riid,
	void FAR* FAR*	ppv)
{
    IUnknown FAR*	pUnknown;
    CACMCmpStream FAR* pAVIStream;
    HRESULT	hresult;

    pAVIStream = new FAR CACMCmpStream(pUnknownOuter, &pUnknown);
    if (!pAVIStream)
	return ResultFromScode(E_OUTOFMEMORY);
    hresult = pUnknown->QueryInterface(riid, ppv);
    if (FAILED(GetScode(hresult)))
	delete pAVIStream;
    return hresult;
}

/*	-	-	-	-	-	-	-	-	*/

CACMCmpStream::CACMCmpStream(
	IUnknown FAR*	pUnknownOuter,
	IUnknown FAR* FAR* ppUnknown)
{
    m_pavi = 0;
    m_hs = 0;
    m_lpFormat = 0;
    m_lpFormatC = 0;
    m_lpIn = 0;
    m_lpOut = 0;

    if (pUnknownOuter)
	m_pUnknownOuter = pUnknownOuter;
    else
	m_pUnknownOuter = this;
    *ppUnknown = this;
    m_refs = 0;
}

/*	-	-	-	-	-	-	-	-	*/

STDMETHODIMP CACMCmpStream::QueryInterface(
	const IID FAR&	iid,
	void FAR* FAR*	ppv)
{
    if (iid == IID_IUnknown)
	*ppv = (IUnknown FAR *) this;
    else if (iid == IID_IAVIStream)
	*ppv = (IAVIStream FAR *) this;
    else {
	*ppv = NULL;
	return ResultFromScode(E_NOINTERFACE);
    }
    AddRef();
    return NULL;
}

/*	-	-	-	-	-	-	-	-	*/

STDMETHODIMP_(ULONG) CACMCmpStream::AddRef()
{
    uUseCount++;
    return ++m_refs;
}

/*	-	-	-	-	-	-	-	-	*/

LONG CACMCmpStream::SetUpCompression()
{
    LONG	    	    lRet = AVIERR_OK;
    MMRESULT		err;


    // Get the initial format
    AVIStreamFormatSize(m_pavi, AVIStreamStart(m_pavi), &m_cbFormat);
    m_lpFormat = (LPWAVEFORMATEX) GlobalAllocPtr(GHND | GMEM_SHARE, m_cbFormat);
    if (!m_lpFormat) {
	lRet = AVIERR_MEMORY;
	goto exit;
    }
    AVIStreamReadFormat(m_pavi, AVIStreamStart(m_pavi), m_lpFormat, &m_cbFormat);

    if (m_lpFormatC != NULL) {
	// we already have the format, let's hope it works...

	// We could check if the format matches the original format....
	if (m_cbFormat == m_cbFormatC &&
		(_fmemcmp(m_lpFormat, m_lpFormatC, (int) m_cbFormat) == 0))
	    goto sameformat;
	
    } else if (m_lpFormat->wFormatTag != WAVE_FORMAT_PCM) {
	acmMetrics(NULL, ACM_METRIC_MAX_SIZE_FORMAT, (LPVOID)&m_cbFormatC);
	m_lpFormatC = (LPWAVEFORMATEX) GlobalAllocPtr(GHND | GMEM_SHARE, m_cbFormatC);
	if (!m_lpFormatC) {
	    lRet = AVIERR_MEMORY;
	    goto exit;
	}

	m_lpFormatC->wFormatTag = WAVE_FORMAT_PCM;
	
	if (acmFormatSuggest(NULL, m_lpFormat, m_lpFormatC, m_cbFormatC, 0L) != 0)
	    goto sameformat;
    } else {
sameformat:
	DPF("Leaving the format unchanged....\n");
	m_lpFormatC = m_lpFormat;
	m_cbFormatC = m_cbFormat;
	m_lpFormat = NULL;
	m_cbFormat = 0;

	m_hs = (HACMSTREAM) -1;
	
	goto exit;
    }

    ACMFORMATDETAILS	afdU;
    ACMFORMATTAGDETAILS	aftdU;
    ACMFORMATDETAILS	afdC;
    ACMFORMATTAGDETAILS	aftdC;

    afdU.cbStruct = sizeof(afdU);
    afdU.pwfx = m_lpFormat;
    afdU.cbwfx = m_cbFormat;
    afdU.fdwSupport = 0;
    afdU.dwFormatTag = m_lpFormat->wFormatTag;

    acmFormatDetails(NULL, &afdU, ACM_FORMATDETAILSF_FORMAT);

    aftdU.cbStruct = sizeof(aftdU);
    aftdU.dwFormatTag = m_lpFormat->wFormatTag;
    aftdU.fdwSupport = 0;

    acmFormatTagDetails(NULL,
			&aftdU, ACM_FORMATTAGDETAILSF_FORMATTAG);

    afdC.cbStruct = sizeof(afdC);
    afdC.pwfx = m_lpFormatC;
    afdC.cbwfx = m_cbFormatC;
    afdC.dwFormatTag = m_lpFormatC->wFormatTag;
    afdC.fdwSupport = 0;

    acmFormatDetails(NULL, &afdC, ACM_FORMATDETAILSF_FORMAT);

    aftdC.cbStruct = sizeof(aftdC);
    aftdC.dwFormatTag = m_lpFormatC->wFormatTag;
    aftdC.fdwSupport = 0;

    acmFormatTagDetails(NULL,
			&aftdC,
			ACM_FORMATTAGDETAILSF_FORMATTAG);

    DPF("Converting %s %s to %s %s\n", (LPSTR) &aftdU.szFormatTag, (LPSTR) &afdU.szFormat, (LPSTR) &aftdC.szFormatTag, (LPSTR) &afdC.szFormat);

    // Open the compressor they asked for...
    lRet = acmStreamOpen(&m_hs,		    // returned stream handle
			 NULL,		    // use any converter you want
			 m_lpFormat,	    // starting format
			 m_lpFormatC,	    // ending format
			 0L,		    // no filter
			 0L,		    // no callback
			 0L,		    // instance data for callback
			 ACM_STREAMOPENF_NONREALTIME);//emph. quality not speed


    // !!! translate error code

    if (!m_hs) {
	DPF("Unable to convert!\n");
#if 0
	TCHAR *		pachMessage;
	TCHAR		achTemp[128];
	static int	iEntered = 0;
	LPTSTR		aStrings[4];
// !!! This isn't defined in dev\inc\windows.h.  I don't understand why.
#ifndef FORMAT_MESSAGE_ARGUMENT_ARRAY
#define FORMAT_MESSAGE_ARGUMENT_ARRAY  0
#endif

	aStrings[0] = (LPTSTR) &aftdU.szFormatTag;
	aStrings[1] = (LPTSTR) &afdU.szFormat;
	aStrings[2] = (LPTSTR) &aftdC.szFormatTag;
	aStrings[3] = (LPTSTR) &afdC.szFormat;
	
	FormatMessage(FORMAT_MESSAGE_FROM_HMODULE |
		      FORMAT_MESSAGE_ALLOCATE_BUFFER |
		      FORMAT_MESSAGE_ARGUMENT_ARRAY,
		      (LPVOID) ghInst,
		      IDS_CNVTERR,
		      0,  // !!! GetSystemDefaultLanguage?
		      (LPTSTR) &pachMessage,
		      999,
		      (LPDWORD) &aStrings);  // !!! needs to be va_list for NT?
	
	LoadString(ghInst, IDS_ACMERR, (LPTSTR)achTemp, sizeof(achTemp)/sizeof(TCHAR));
	
	if (iEntered++ == 0)	
	    MessageBox(NULL, pachMessage, achTemp, MB_OK);
	iEntered--;

	LocalFree((HLOCAL) pachMessage);
#endif
	
	lRet = AVIERR_ERROR;
	goto exit;
    }

    // Fix avistream header
    m_avistream.dwSampleSize = m_lpFormatC->nBlockAlign;
    m_avistream.dwScale = m_lpFormatC->nBlockAlign;
    m_avistream.dwRate = m_lpFormatC->nAvgBytesPerSec;

    acmStreamSize(m_hs,
		  AVIStreamLength(m_pavi) * m_lpFormat->nBlockAlign,
		  (LPDWORD) &m_avistream.dwLength,
		  ACM_STREAMSIZEF_SOURCE);

    // !!! acmStreamSize rounds up here, do we need to compensate?
    // !!! should we round off/up here?
    m_avistream.dwLength /= m_lpFormatC->nBlockAlign;

    m_avistream.dwQuality = 0; // !!!

    m_cbIn = 4096; // !!!
    m_cbIn -= m_cbIn % m_lpFormat->nBlockAlign; // round down to block aligned

    acmStreamSize(m_hs,
		  m_cbIn,
		  (LPDWORD) &m_cbOut,
		  ACM_STREAMSIZEF_SOURCE);

    DPF("ACM conversion: input %ld bytes, output %ld bytes\n", m_cbIn, m_cbOut);

    m_lpIn = (BYTE _huge *) GlobalAllocPtr(GHND, m_cbIn);
    m_lpOut = (BYTE _huge *) GlobalAllocPtr(GHND, m_cbOut);

    if (!m_lpIn || !m_lpOut) {
	lRet = AVIERR_MEMORY;
	goto exit;
    }

    m_acm.cbStruct = sizeof(m_acm);
    m_acm.fdwStatus = 0;
    m_acm.dwUser = 0;
    m_acm.pbSrc = m_lpIn;
    m_acm.cbSrcLength = m_cbIn;
    m_acm.cbSrcLengthUsed = 0;
    m_acm.pbDst = m_lpOut;
    m_acm.cbDstLength = m_cbOut;
    m_acm.cbDstLengthUsed = 0;

    // !!! add in start, end flags for ACM....
    err = acmStreamPrepareHeader(m_hs, &m_acm, 0);

    if (err != 0) {
	DPF("acmStreamPrepareHeader returns %u\n", err);

	return AVIERR_COMPRESSOR;
    }

    m_dwPosIn = m_dwPosOut = 0;

    m_dwSamplesLeft = 0;

exit:
    if (lRet != AVIERR_OK) {
	// Don't release here!
    }

    return lRet;
}

/*	-	-	-	-	-	-	-	-	*/

//
//  ACM stream:
//
//  lParam1 should be a PAVISTREAM (an audio one!)
//
//  lParam2 should be an LPWAVEFORMAT for the format you want converted
//  to.
//
STDMETHODIMP CACMCmpStream::Create(LONG lParam1, LONG lParam2)
{
    PAVISTREAM		    pavi = (PAVISTREAM) lParam1;
    LPAVICOMPRESSOPTIONS    lpOpts = (LPAVICOMPRESSOPTIONS) lParam2;
    LPWAVEFORMAT	    lpwfNew = NULL;
    LONG	    	    lRet = AVIERR_OK;

    DPF("Creating ACM compression stream....\n");
    // Get the stream header for future reference....
    pavi->Info(&m_avistream, sizeof(m_avistream));
    m_avistream.fccHandler = 0;
    if (m_avistream.fccType != streamtypeAUDIO) {
	DPF("Stream isn't audio!\n");
	lRet = AVIERR_INTERNAL;
	goto exit;
    }

    if (acmGetVersion() < 0x02000000L) {
	DPF("Bad ACM version!\n");
	lRet = AVIERR_INTERNAL;
	goto exit;
    }

    if (lpOpts && lpOpts->lpFormat) {
	lpwfNew = (LPWAVEFORMAT) lpOpts->lpFormat;

	if (lpOpts->cbFormat < WAVEFORMATSIZE(lpwfNew)) {
	    DPF("Bad format size!\n");
	    lRet = AVIERR_INTERNAL;
	    goto exit;
	}
	
	m_cbFormatC = WAVEFORMATSIZE(lpwfNew);
	m_lpFormatC = (LPWAVEFORMATEX) GlobalAllocPtr(GMEM_MOVEABLE, m_cbFormatC);
	if (m_lpFormatC == NULL) {
	    DPF("Out of memory for format!\n");
	    lRet = AVIERR_MEMORY;
	    goto exit;
	}

	hmemcpy(m_lpFormatC, lpOpts->lpFormat, m_cbFormatC);
    } else {
	m_cbFormatC = 0;
	m_lpFormatC = NULL;
    }

    // Make sure the uncompressed stream doesn't go away without our
    // knowledge....
    AVIStreamAddRef(pavi);

    // Don't put this in the structure until we've done the AddRef....
    m_pavi = pavi;

exit:
    return ResultFromScode(lRet);
}

STDMETHODIMP_(ULONG) CACMCmpStream::Release()
{
    uUseCount--;

    if (--m_refs)
	return m_refs;

    if (m_lpFormat) {
	GlobalFreePtr(m_lpFormat);

	if (m_hs) {
	    m_acm.cbSrcLength = m_cbIn;
	    acmStreamUnprepareHeader(m_hs, &m_acm, 0);
	
	    acmStreamClose(m_hs, 0);
	}
    }

    if (m_lpIn) {
	GlobalFreePtr(m_lpIn);
    }

    if (m_lpOut) {
	GlobalFreePtr(m_lpOut);
    }

    if (m_pavi) {
	// Release our hold on the uncompressed stream....
	AVIStreamClose(m_pavi);
    }

    if (m_lpFormatC)
	GlobalFreePtr(m_lpFormatC);

    delete this;

    return 0;
}


STDMETHODIMP CACMCmpStream::Info(AVISTREAMINFOW FAR * psi, LONG lSize)
{
    if (m_hs == 0) {
	LONG	lRet;
	
	// !!! If they ask for info before writing or setting the
	// format, this will become a "read" stream!
	lRet = SetUpCompression();

	if (lRet != 0)
	    return ResultFromScode(lRet);
    }

    hmemcpy(psi, &m_avistream, min(lSize, sizeof(m_avistream)));

//    return sizeof(m_avistream);
    return 0;
}

STDMETHODIMP CACMCmpStream::ReadFormat(LONG lPos, LPVOID lpFormat, LONG FAR *lpcbFormat)
{
    LONG    lRet;

    if (m_hs == 0) {
	lRet = SetUpCompression();

	if (lRet != 0)
	    return ResultFromScode(lRet);
    }

    if (lpFormat)
	hmemcpy(lpFormat,
		m_lpFormatC,
		min(*lpcbFormat, (LONG) m_cbFormatC));

    *lpcbFormat = (LONG) m_cbFormatC;
    return 0;
}

STDMETHODIMP CACMCmpStream::Read(
                      LONG       lStart,
                      LONG       lSamples,
                      LPVOID     lpBuffer,
                      LONG       cbBuffer,
                      LONG FAR * plBytes,
                      LONG FAR * plSamples)
{
    LONG		lRet;

    MMRESULT		err;

    HRESULT		hr;

    if (plBytes)
	*plBytes = 0;
    if (plSamples)
	*plSamples = 0;

    if (m_hs == 0) {
	lRet = SetUpCompression();

	if (lRet != 0)
	    return ResultFromScode(lRet);
    }

    if (m_lpFormat == NULL) {
	// Just return original format....
	return AVIStreamRead(m_pavi, lStart, lSamples,
			     lpBuffer, cbBuffer, plBytes, plSamples);
    }

    if (lStart < 0 || lStart > (LONG) (m_avistream.dwStart + m_avistream.dwLength))
	return ResultFromScode(AVIERR_BADPARAM);

    if (lSamples == AVISTREAMREAD_CONVENIENT) {
	// If they didn't specify a number of samples, fill their buffer....
	lSamples = (cbBuffer ? cbBuffer : 32768L) / m_lpFormatC->nBlockAlign;
    }

    // Don't let anybody try to read past the end....
    if (lSamples + lStart >
		    (LONG) (m_avistream.dwStart + m_avistream.dwLength))
	lSamples = (LONG) (m_avistream.dwStart + m_avistream.dwLength) -
			       lStart;

    if (lSamples <= 0)
	return ResultFromScode(AVIERR_BADPARAM);

    if (lpBuffer) {
	LONG	lBytes;

	if (cbBuffer < lSamples * m_lpFormatC->nBlockAlign) {
	    DPF("Returning buffer too small\n");
	    if (plBytes)
		*plBytes = lSamples * m_lpFormatC->nBlockAlign;
	
	    return ResultFromScode(AVIERR_BUFFERTOOSMALL);
	}

	if (lStart < m_dwPosOut) {
	    // !!! return to beginning!
	    m_dwPosOut = 0;
	    m_dwPosIn = AVIStreamStart(m_pavi);
	    m_dwSamplesLeft = 0;
	}
	
	while (lSamples > 0) {
	    DPF("Want %ld samples at %ld  (PosOut=%ld, SamplesLeft=%ld)\n", lSamples, lStart, m_dwPosOut, m_dwSamplesLeft);
	    if (lStart >= m_dwPosOut + m_dwSamplesLeft) {
		// Throw away current converted buffer....
		m_dwPosOut += m_dwSamplesLeft;
		
		hr = AVIStreamRead(m_pavi,
			      m_dwPosIn, m_cbIn / m_lpFormat->nBlockAlign,
			      m_lpIn, m_cbIn, &lBytes, &lRet);

		if (lBytes != m_cbIn) {
		    DPF("AVIStreamRead: Asked for %lx bytes at %lx, got %lx!\n", m_cbIn, m_dwPosIn, lBytes);

		    if (lBytes < m_cbIn) {
			// Fill buffer with silence, and hope....
			BYTE _huge *hp;
			LONG	    cb;
			BYTE	    b;

			cb = (m_cbIn - lBytes);
			hp = (BYTE _huge *) m_lpIn + lBytes;

			if ((m_lpFormat->wFormatTag == WAVE_FORMAT_PCM) &&
					(m_lpFormat->wBitsPerSample == 8))
			    b = 0x80;
			else
			    b = 0;

			while (cb-- > 0)
			    *hp++ = b;

			// If we couldn't read anything, pretend we
			// really read enough.
			if (lBytes == 0 && m_dwPosIn >= AVIStreamLength(m_pavi)) {
			    lBytes = m_cbIn;
			    hr = NOERROR;
			}
		    }

		    // !!!
		    // lSampLen = lRet; // !!!
		    // lByteLen = lSampLen * m_lpFormat->nBlockAlign;
		}

		if (FAILED(GetScode(hr))) {
		    DPF("AVIStreamReadFailed! (start=%lx, len=%lx, err=%08lx)\n", m_dwPosIn, m_cbIn / m_lpFormat->nBlockAlign, hr);
		    return hr;
		}

		m_acm.cbSrcLength = lBytes;

		err = acmStreamConvert(m_hs, &m_acm, ACM_STREAMCONVERTF_BLOCKALIGN);

		if (err != 0) {
		    DPF("acmStreamConvert returns %u\n", err);

		    return ResultFromScode(AVIERR_COMPRESSOR);
		}

		DPF("Converted %lu of %lu bytes to %lu bytes (buffer size = %lu)\n", m_acm.cbSrcLengthUsed, m_acm.cbSrcLength, m_acm.cbDstLengthUsed, m_acm.cbDstLength);

		if (m_acm.cbSrcLengthUsed == 0) {
		    err = acmStreamConvert(m_hs, &m_acm, 0);

		    if (err != 0) {
			DPF("acmStreamConvert returns %u\n", err);

			return ResultFromScode(AVIERR_COMPRESSOR);
		    }

		    DPF("Converted (non-blockalign) %lu of %lu bytes to %lu bytes (buffer size = %lu)\n", m_acm.cbSrcLengthUsed, m_acm.cbSrcLength, m_acm.cbDstLengthUsed, m_acm.cbDstLength);
		}
		
		// Lie: say that the ACM returned a full block....
		// !!! acm.cbDstLengthUsed += m_lpFormatC->nBlockAlign - 1;
		// acm.cbDstLengthUsed -= acm.cbDstLengthUsed % m_lpFormatC->nBlockAlign;

		m_dwPosIn += m_acm.cbSrcLengthUsed / m_lpFormat->nBlockAlign;
		m_dwSamplesLeft = (m_acm.cbDstLengthUsed +  m_lpFormatC->nBlockAlign - 1) / m_lpFormatC->nBlockAlign;

		if (m_dwSamplesLeft == 0) {
		    // Instead, say that we got one block back, 0 bytes long.
		    DPF("ACM returned no data at all!  Ack!\n");
		    m_dwSamplesLeft = 1;
		}
		
		m_dwBytesMissing = m_dwSamplesLeft * m_lpFormatC->nBlockAlign - m_acm.cbDstLengthUsed;
	    }

	    if (lStart >= m_dwPosOut) {
		LONG		lSamplesRead;
		LONG		lBytesRead;
		
		lSamplesRead = min(m_dwSamplesLeft - (lStart - m_dwPosOut),
				   lSamples);

		lBytesRead = lSamplesRead * m_lpFormatC->nBlockAlign;

		if (m_dwBytesMissing &&
		    (lStart - m_dwPosOut + lSamplesRead == m_dwSamplesLeft)) {
		    DPF("Not copying %ld missing bytes....\n", m_dwBytesMissing);
		    lBytesRead -= m_dwBytesMissing;
		}
		
		DPF("Copying %ld samples... (%ld bytes)\n", lSamplesRead, lBytesRead);
		hmemcpy(lpBuffer,
			(BYTE _huge *) m_lpOut +
				(lStart - m_dwPosOut) * m_lpFormatC->nBlockAlign,
			lBytesRead);

		if (plBytes)
		    *plBytes += lBytesRead;

		if (plSamples)
		    *plSamples += lSamplesRead;

		lSamples -= lSamplesRead;
		lpBuffer = (BYTE _huge *) lpBuffer +
				   lSamplesRead * m_lpFormatC->nBlockAlign;
		lStart += lSamplesRead;
	    }
	}
    } else {
	// We always assume we could read whatever they asked for....
	if (plBytes)
	    *plBytes = lSamples * m_lpFormatC->nBlockAlign;

	if (plSamples)
	    *plSamples = lSamples;
    }
    return 0;
}

STDMETHODIMP_(LONG) CACMCmpStream::FindSample(LONG lPos, LONG lFlags)
{
    if (lFlags & FIND_FORMAT) {
	if (lFlags & FIND_PREV)
	    return 0;
	else {
	    if (lPos > 0)
		return -1;
	    else
		return 0;
	}
    }

    return lPos;
}


STDMETHODIMP CACMCmpStream::SetFormat(LONG lPos,LPVOID lpFormat,LONG cbFormat)
{
    // !!! It should really be possible to use SetFormat & Write on this
    // stream.....
    return ResultFromScode(AVIERR_UNSUPPORTED);
}

STDMETHODIMP CACMCmpStream::Write(LONG lStart,
				  LONG lSamples,
				  LPVOID lpBuffer,
				  LONG cbBuffer,
				  DWORD dwFlags,
				  LONG FAR *plSampWritten,
				  LONG FAR *plBytesWritten)
{
    // !!!
    // Maybe this is the place to decompress data and write it to the original
    // stream?
    return ResultFromScode(AVIERR_UNSUPPORTED);
}

STDMETHODIMP CACMCmpStream::Delete(LONG lStart,LONG lSamples)
{
    return ResultFromScode(AVIERR_UNSUPPORTED);
}

STDMETHODIMP CACMCmpStream::ReadData(DWORD fcc, LPVOID lp, LONG FAR *lpcb)
{
    return AVIStreamReadData(m_pavi, fcc, lp, lpcb);
}

STDMETHODIMP CACMCmpStream::WriteData(DWORD fcc, LPVOID lp, LONG cb)
{
    return ResultFromScode(AVIERR_UNSUPPORTED);
}

#if 0
STDMETHODIMP CACMCmpStream::Clone(PAVISTREAM FAR * ppaviNew)
{
    return ResultFromScode(AVIERR_UNSUPPORTED);
}
#endif


#ifdef _WIN32
STDMETHODIMP CACMCmpStream::SetInfo(AVISTREAMINFOW FAR *lpInfo, LONG cbInfo)
{
    return ResultFromScode(AVIERR_UNSUPPORTED);
}

#else
STDMETHODIMP CACMCmpStream::Reserved1(void)
{
    return ResultFromScode(AVIERR_UNSUPPORTED);
}

STDMETHODIMP CACMCmpStream::Reserved2(void)
{
    return ResultFromScode(AVIERR_UNSUPPORTED);
}

STDMETHODIMP CACMCmpStream::Reserved3(void)
{
    return ResultFromScode(AVIERR_UNSUPPORTED);
}

STDMETHODIMP CACMCmpStream::Reserved4(void)
{
    return ResultFromScode(AVIERR_UNSUPPORTED);
}

STDMETHODIMP CACMCmpStream::Reserved5(void)
{
    return ResultFromScode(AVIERR_UNSUPPORTED);
}


#endif