最近在做PCM音频合成项目,其中遇到了一个bug,花了大半天才解决,当然修改却很小,是一个很细节的问题。不过回想一下,之前也有类似的经历,觉得有点什么规律,写出来用以自勉,也许各位programmer也曾遇到过吧!:)
下面是部分摘录的代码(颜色visual assist生成的):
/**/
HRESULT CALMInputPin::Convert(IMediaSample *pIn, IMediaSample *pOut)
{
HRESULT hr = S_OK;
/* get suggested size of destination buffer */
long len = pIn->GetActualDataLength();
DWORD suggested_dest_size = 0;
hr = acmStreamSize(m_hStream, len, &suggested_dest_size, ACM_STREAMSIZEF_DESTINATION);
if (hr != MMSYSERR_NOERROR)
return hr;
ACMSTREAMHEADER acmhdr;
acmhdr.cbStruct = sizeof(acmhdr);
hr = pIn->GetPointer(&acmhdr.pbSrc);
hr = pOut->GetPointer(&acmhdr.pbDst);
acmhdr.cbSrcLength = pIn->GetActualDataLength();
acmhdr.cbDstLength = suggested_dest_size;
acmhdr.dwSrcUser = (DWORD)pIn;
acmhdr.dwDstUser = (DWORD)pOut;
acmhdr.dwUser = (DWORD)this;
acmhdr.fdwStatus = 0L;
// ???这里总是返回 error code 10
hr = acmStreamPrepareHeader(m_hStream, &acmhdr, 0);
if (hr != MMSYSERR_NOERROR)
return hr;
/* send buffer to compressor/uncompressor (ACM) */
hr = acmStreamConvert (m_hStream, &acmhdr, ACM_STREAMCONVERTF_BLOCKALIGN);
if (hr != MMSYSERR_NOERROR )
{
return hr;
}
hr = acmStreamUnprepareHeader(m_hStream, &acmhdr, 0);
if (hr != MMSYSERR_NOERROR)
return hr;
REFERENCE_TIME rtStart, rtEnd;
len = pIn->GetActualDataLength();
pIn->GetTime(&rtStart, &rtEnd);
rtEnd = rtStart + len;
pOut->SetTime(&rtStart, &rtEnd);
pOut->SetActualDataLength(acmhdr.cbDstLength);
return S_OK;
}
//
这里涉及到DirectShow和ACM的结构和函数,不过思路还比较清晰。细心的话可以看出这些代码,是“拼凑”出来的(提高开发速度嘛!),分别来自是网上的开源项目和DirectShow附带的源文件。从逻辑上说也没有什么问题,当然调试发现总是有一个错误。
唯一的线索是error code 10,Error Lookup解释为“环境错误”,这能说明什么问题呢?当然笔者对ACM也不是很了解,于是花了几个小时看了文档,了解了一些背景知识,但没有什么发现什么有价值的东西。又参考了其它项目的代码,不过别人使用的c++模板语法,而且要拆解出关键的代码需要很多时间,所以搞来搞去也没有思路。
思路在哪里?在经过了一些其它的尝试的时候,觉得还是不行。最后又去调试别人的代码,可以运行通过。有一点是可以肯定的,程序的流程没有问题,可能是其中遗漏了某些细节。对比两段代码后发现,ACMSTREAMHEADER的某些成员变量没有初始化。难道真是这个问题吗?于是在结构体变量申明后面添加了
memset (&acmhdr, 0, sizeof(acmhdr));
这样所有的成员先初始化为0。在调试,问题解决了!后来在MSDN里面发现了ACMAPP Sample工程,里面有这么一段代码:
…
pash->cbStruct = sizeof(*pash);
pash->fdwStatus = 0L;
pash->dwUser = 0L;
pash->pbSrc = paacd->pbSrc;
pash->cbSrcLength = paacd->cbSrcReadSize;
pash->cbSrcLengthUsed = 0L;
pash->dwSrcUser = paacd->cbSrcReadSize;
pash->pbDst = paacd->pbDst;
pash->cbDstLength = paacd->cbDstBufSize;
pash->cbDstLengthUsed = 0L;
pash->dwDstUser = paacd->cbDstBufSize;
…
这个结构体的定义如下:
typedef struct tACMSTREAMHEADER
{
DWORD cbStruct; // sizeof(ACMSTREAMHEADER)
DWORD fdwStatus; // ACMSTREAMHEADER_STATUSF_*
DWORD dwUser; // user instance data for hdr
LPBYTE pbSrc;
DWORD cbSrcLength;
DWORD cbSrcLengthUsed;
DWORD dwSrcUser; // user instance data for src
LPBYTE pbDst;
DWORD cbDstLength;
DWORD cbDstLengthUsed;
DWORD dwDstUser; // user instance data for dst
DWORD dwReservedDriver[10]; // driver reserved work space
} ACMSTREAMHEADER, *PACMSTREAMHEADER, FAR *LPACMSTREAMHEADER;
11个成员虽然没有全部清零,但是除了dwReservedDriver[10]都赋了值。
总结:这类错误是由于某些结构体成员没有正确的赋值而造成的,在Debug模式中没有赋值的变量一般初始化为0xcc,在Release模式中一般是0x00,都是按字节序。所以编译器为了做了赋值,但是这些值是错误的,所以在编码的过程中必须处理这个细节。要么先全部初始化为0,要么对所有相关的变量都赋值。
笔者写程序也有3年了,但是仍然犯这个错误,虽然是在追的比较急的时候。所以必须在平时养成良好的习惯,同时克服外部的压力,写出高质量的代码;而不是风风火火赶代码,最后还得花很多时间去找错,得不偿失!这就是笔者要与大家共勉的啊!