Wince摄像头缓冲管理三种模式:
typedef enum {
CSPROPERTY_BUFFER_DRIVER = 1,
CSPROPERTY_BUFFER_CLIENT_LIMITED = 2,
CSPROPERTY_BUFFER_CLIENT_UNLIMITED = 4
} CSPROPERTY_BUFFER_MODE;
1、CSPROPERTY_BUFFER_CLIENT的两种模式是由CLIENT分配内存,让底层来填充;但并不是如PB帮助所说:
Indicates that the driver's DMA preparation time is slow and that the then the driver needs to know in advance about all of the buffers that it will receive from the client.
Indicates that the driver's DMA preparation time is fast and that the client can allocate, initialize, and release buffers as they are needed.
供DMA来传递数据给该缓冲。如果利用提供的测试程序,可看出代码流程:
BOOL CStreamTest::AllocateBuffers()
{
BOOL bReturnValue = TRUE;
ULONG ulBufferIndex;
WaitForSingleObject(m_hMutex, INFINITE);
if(m_dwAllocationType != CSPROPERTY_BUFFER_CLIENT_LIMITED &&
m_dwAllocationType != CSPROPERTY_BUFFER_CLIENT_UNLIMITED &&
m_dwAllocationType != CSPROPERTY_BUFFER_DRIVER)
{
ERRFAIL(TEXT("AllocateBuffers : Invalid buffering mode."));
bReturnValue = FALSE;
goto cleanup;
}
PSTREAM_BUFFERNODE pcsCurrentBufferNode = m_pcsBufferNode;
// get to the end of the list.
if(pcsCurrentBufferNode)
{
while(pcsCurrentBufferNode->pNext)
pcsCurrentBufferNode = pcsCurrentBufferNode->pNext;
}
for(ulBufferIndex = 0; ulBufferIndex < m_dwDriverRequestedBufferCount; ulBufferIndex++)
{
// allocate our buffer node
// if our current node is non-zero, then we're in the middle of the list.
// allocate a new node,
if(pcsCurrentBufferNode)
{
pcsCurrentBufferNode->pNext = new(STREAM_BUFFERNODE);
pcsCurrentBufferNode = pcsCurrentBufferNode->pNext;
}
else // we're starting a new linked list, so set the current node, and set the beginning pointer
{
pcsCurrentBufferNode = new(STREAM_BUFFERNODE);
m_pcsBufferNode = pcsCurrentBufferNode;
}
if(!pcsCurrentBufferNode)
{
ERRFAIL(TEXT("AllocateBuffers : buffer node allocation failed."));
bReturnValue = FALSE;
goto cleanup;
}
// clear out the node
memset(pcsCurrentBufferNode, 0, sizeof(STREAM_BUFFERNODE));
// allocate the stream descriptor
pcsCurrentBufferNode->pCsStreamDesc = new(CS_STREAM_DESCRIPTOR);
if(!pcsCurrentBufferNode->pCsStreamDesc)
{
ERRFAIL(TEXT("AllocateBuffers : stream descriptor allocation failed."));
bReturnValue = FALSE;
goto cleanup;
}
memset(pcsCurrentBufferNode->pCsStreamDesc, 0, sizeof(CS_STREAM_DESCRIPTOR));
// if software allocate frame
if(m_dwAllocationType == CSPROPERTY_BUFFER_CLIENT_LIMITED || m_dwAllocationType == CSPROPERTY_BUFFER_CLIENT_UNLIMITED)
{
pcsCurrentBufferNode->pCsStreamDesc->CsStreamHeader.Data = (PVOID) new(BYTE[m_pCSDataFormat->SampleSize]);
//这句话即,如果为CSPROPERTY_BUFFER_CLIENT属性,则由CLIENT分配缓冲。这里的缓冲是new得到,也即在线程堆空间分配内存。
//明显不是提供DMA的连续物理内存。
if(!pcsCurrentBufferNode->pCsStreamDesc->CsStreamHeader.Data)
{
ERRFAIL(TEXT("AllocateBuffers : buffer frame allocation failed."));
bReturnValue = FALSE;
goto cleanup;
}
}
// pass the frame to the driver
if(FALSE == RegisterBuffer(pcsCurrentBufferNode->pCsStreamDesc))
{
ERRFAIL(TEXT("AllocateBuffers : Failed to register the buffer with the driver."));
bReturnValue = FALSE;
goto cleanup;
}
//这里开始层层传递缓冲指针
//-->TestStreamDeviceIOControl-->DeviceIoControl[PIN_IOControl]-->PinHandleBufferRequest-->
//AllocateBuffer-->SwSetupStreamDescriptor 下面在看看此函数。
// pass the frame to the driver
if(FALSE == EnqueueBuffer(pcsCurrentBufferNode->pCsStreamDesc))
{
ERRFAIL(TEXT("AllocateBuffers : Failed to queue the buffer up with the driver."));
bReturnValue = FALSE;
goto cleanup;
}
}
cleanup:
ReleaseMutex(m_hMutex);
return bReturnValue;
}
DWORD
CPinDevice::SwSetupStreamDescriptor(
DWORD dwIndex,
PCS_STREAM_DESCRIPTOR pCsStreamDesc,
LPVOID pBuffer // Warning: This is an unsafe buffer, use with caution
)
{
DWORD dwHandle;
PCS_STREAM_DESCRIPTOR pCsStreamDescExt = ( PCS_STREAM_DESCRIPTOR ) pBuffer;
if(( pCsStreamDesc == NULL ) || ( pBuffer == NULL ))
{
return ERROR_INVALID_PARAMETER;
}
dwHandle = CreateHandle( dwIndex, pBuffer );
__try
{
pCsStreamDescExt->CsStreamHeader.Handle = dwHandle;
}
__except( EXCEPTION_EXECUTE_HANDLER )
{
return ERROR_INVALID_PARAMETER;
}
// Note: This is the place to setup DMA for the buffer.
pCsStreamDesc->CsStreamHeader.Handle = dwHandle;
return m_pCamAdapter->PDDRegisterClientBuffer( m_ulPinId, pCsStreamDesc->CsStreamHeader.Data );
}
这里说为这缓冲建立DMA。。。。。。
继续向下-->CCameraDevice::PDDRegisterClientBuffer-->m_PDDFuncTbl.PDD_RegisterClientBuffer即到来PDD层里填充的函数:
DWORD CCameraPdd::RegisterClientBuffer( ULONG ulModeType, PVOID pBuffer )
{
// Real PDD may want to save pBuffer which is a pointer to buffer that DShow created.
return ERROR_SUCCESS;
}
从打印来看也是如此:
CCameraPdd::RegisterClientBuffer ulModeType=0 pBuffer=0x01290000
CCameraPdd::RegisterClientBuffer ulModeType=1 pBuffer=0x012e0000
其分配空间都在进程空间地址范围,故根本不可能实现DMA功能。只能在FillBuffer中使用低效的memcpy来搬运图像数据。
当然这是微软提供的例子程序如此,如若自身编写一程序,分配一固定大小连续缓冲,则DMA方式可行!!微软应该也是如此设计!!
2、CSPROPERTY_BUFFER_DRIVER 模式
使用该模式,缓冲在驱动内部分配,但使用HalAllocateCommonBuffer或者MmMapIoSpace来分配连续内存区域,通过AllocBuffer直接将该地址返回MDD层,会导致错误:
PIN_IOControl(d7873480): Unable to marshal data buffer for asynch access.
这是marshall出错,Marshal对该说明:
// Please see the description of CeOpenCallerBuffer and
// CeAllocAsynchronousBuffer for more information about the operation of
// this function.
//
// Once a MarshalledBuffer is marshalled (using the marshalling constructor
// or the Marshal() method, it cannot be re-used by calling Marshal(), until
// after Unmarshal() is called. An attempt to do so will return
// ERROR_ALREADY_EXISTS.
//
// If Marshal() fails, ptr() will return NULL and size() will return zero.
// Otherwise the marshalled buffer will be accessible via ptr() and size().
搞不清楚什么原因,为什么marshal会失败,默认使用RemoteLocalAlloc则可以。
也就是说本模式DMA提高传输不可能。。。。。。还是得使用FillBuffer函数进行memcpy拷贝搬运。