| /* | |
| * Generic Implementation of COutputQueue | |
| * | |
| * Copyright 2011 Aric Stewart, CodeWeavers | |
| * | |
| * This library is free software; you can redistribute it and/or | |
| * modify it under the terms of the GNU Lesser General Public | |
| * License as published by the Free Software Foundation; either | |
| * version 2.1 of the License, or (at your option) any later version. | |
| * | |
| * This library is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| * Lesser General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU Lesser General Public | |
| * License along with this library; if not, write to the Free Software | |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA | |
| */ | |
| #define COBJMACROS | |
| #include "dshow.h" | |
| #include "wine/debug.h" | |
| #include "wine/unicode.h" | |
| #include "wine/list.h" | |
| #include "wine/strmbase.h" | |
| #include "uuids.h" | |
| #include "vfwmsgs.h" | |
| #include <assert.h> | |
| WINE_DEFAULT_DEBUG_CHANNEL(strmbase); | |
| enum {SAMPLE_PACKET, EOS_PACKET}; | |
| typedef struct tagQueuedEvent { | |
| int type; | |
| struct list entry; | |
| IMediaSample *pSample; | |
| } QueuedEvent; | |
| static DWORD WINAPI OutputQueue_InitialThreadProc(LPVOID data) | |
| { | |
| OutputQueue *This = (OutputQueue *)data; | |
| return This->pFuncsTable->pfnThreadProc(This); | |
| } | |
| static void OutputQueue_FreeSamples(OutputQueue *pOutputQueue) | |
| { | |
| struct list *cursor, *cursor2; | |
| LIST_FOR_EACH_SAFE(cursor, cursor2, &pOutputQueue->SampleList) | |
| { | |
| QueuedEvent *qev = LIST_ENTRY(cursor, QueuedEvent, entry); | |
| list_remove(cursor); | |
| HeapFree(GetProcessHeap(),0,qev); | |
| } | |
| } | |
| HRESULT WINAPI OutputQueue_Construct( | |
| BaseOutputPin *pInputPin, | |
| BOOL bAuto, | |
| BOOL bQueue, | |
| LONG lBatchSize, | |
| BOOL bBatchExact, | |
| DWORD dwPriority, | |
| const OutputQueueFuncTable* pFuncsTable, | |
| OutputQueue **ppOutputQueue ) | |
| { | |
| BOOL threaded = FALSE; | |
| DWORD tid; | |
| OutputQueue *This; | |
| if (!pInputPin || !pFuncsTable || !ppOutputQueue) | |
| return E_INVALIDARG; | |
| *ppOutputQueue = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(OutputQueue)); | |
| if (!*ppOutputQueue) | |
| return E_OUTOFMEMORY; | |
| This = *ppOutputQueue; | |
| This->pFuncsTable = pFuncsTable; | |
| This->lBatchSize = lBatchSize; | |
| This->bBatchExact = bBatchExact; | |
| InitializeCriticalSection(&This->csQueue); | |
| This->csQueue.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": OutputQueue.csQueue"); | |
| list_init(&This->SampleList); | |
| This->pInputPin = pInputPin; | |
| IPin_AddRef(&pInputPin->pin.IPin_iface); | |
| EnterCriticalSection(&This->csQueue); | |
| if (bAuto && pInputPin->pMemInputPin) | |
| threaded = IMemInputPin_ReceiveCanBlock(pInputPin->pMemInputPin) == S_OK; | |
| else | |
| threaded = bQueue; | |
| if (threaded) | |
| { | |
| This->hThread = CreateThread(NULL, 0, OutputQueue_InitialThreadProc, This, 0, &tid); | |
| if (This->hThread) | |
| { | |
| SetThreadPriority(This->hThread, dwPriority); | |
| This->hProcessQueue = CreateEventW(NULL, 0, 0, NULL); | |
| } | |
| } | |
| LeaveCriticalSection(&This->csQueue); | |
| return S_OK; | |
| } | |
| HRESULT WINAPI OutputQueue_Destroy(OutputQueue *pOutputQueue) | |
| { | |
| EnterCriticalSection(&pOutputQueue->csQueue); | |
| OutputQueue_FreeSamples(pOutputQueue); | |
| pOutputQueue->bTerminate = TRUE; | |
| SetEvent(pOutputQueue->hProcessQueue); | |
| LeaveCriticalSection(&pOutputQueue->csQueue); | |
| pOutputQueue->csQueue.DebugInfo->Spare[0] = 0; | |
| DeleteCriticalSection(&pOutputQueue->csQueue); | |
| CloseHandle(pOutputQueue->hProcessQueue); | |
| IPin_Release(&pOutputQueue->pInputPin->pin.IPin_iface); | |
| HeapFree(GetProcessHeap(),0,pOutputQueue); | |
| return S_OK; | |
| } | |
| HRESULT WINAPI OutputQueue_ReceiveMultiple(OutputQueue *pOutputQueue, IMediaSample **ppSamples, LONG nSamples, LONG *nSamplesProcessed) | |
| { | |
| HRESULT hr = S_OK; | |
| int i; | |
| if (!pOutputQueue->pInputPin->pin.pConnectedTo || !pOutputQueue->pInputPin->pMemInputPin) | |
| return VFW_E_NOT_CONNECTED; | |
| if (!pOutputQueue->hThread) | |
| { | |
| IMemInputPin_AddRef(pOutputQueue->pInputPin->pMemInputPin); | |
| hr = IMemInputPin_ReceiveMultiple(pOutputQueue->pInputPin->pMemInputPin,ppSamples, nSamples, nSamplesProcessed); | |
| IMemInputPin_Release(pOutputQueue->pInputPin->pMemInputPin); | |
| } | |
| else | |
| { | |
| EnterCriticalSection(&pOutputQueue->csQueue); | |
| *nSamplesProcessed = 0; | |
| for (i = 0; i < nSamples; i++) | |
| { | |
| QueuedEvent *qev = HeapAlloc(GetProcessHeap(),0,sizeof(QueuedEvent)); | |
| if (!qev) | |
| { | |
| ERR("Out of Memory\n"); | |
| hr = E_OUTOFMEMORY; | |
| break; | |
| } | |
| qev->type = SAMPLE_PACKET; | |
| qev->pSample = ppSamples[i]; | |
| IMediaSample_AddRef(ppSamples[i]); | |
| list_add_tail(&pOutputQueue->SampleList, &qev->entry); | |
| (*nSamplesProcessed)++; | |
| } | |
| if (!pOutputQueue->bBatchExact || list_count(&pOutputQueue->SampleList) >= pOutputQueue->lBatchSize) | |
| SetEvent(pOutputQueue->hProcessQueue); | |
| LeaveCriticalSection(&pOutputQueue->csQueue); | |
| } | |
| return hr; | |
| } | |
| HRESULT WINAPI OutputQueue_Receive(OutputQueue *pOutputQueue, IMediaSample *pSample) | |
| { | |
| LONG processed; | |
| return OutputQueue_ReceiveMultiple(pOutputQueue,&pSample,1,&processed); | |
| } | |
| VOID WINAPI OutputQueue_SendAnyway(OutputQueue *pOutputQueue) | |
| { | |
| if (pOutputQueue->hThread) | |
| { | |
| EnterCriticalSection(&pOutputQueue->csQueue); | |
| if (!list_empty(&pOutputQueue->SampleList)) | |
| { | |
| pOutputQueue->bSendAnyway = TRUE; | |
| SetEvent(pOutputQueue->hProcessQueue); | |
| } | |
| LeaveCriticalSection(&pOutputQueue->csQueue); | |
| } | |
| } | |
| VOID WINAPI OutputQueue_EOS(OutputQueue *pOutputQueue) | |
| { | |
| EnterCriticalSection(&pOutputQueue->csQueue); | |
| if (pOutputQueue->hThread) | |
| { | |
| QueuedEvent *qev = HeapAlloc(GetProcessHeap(),0,sizeof(QueuedEvent)); | |
| if (!qev) | |
| { | |
| ERR("Out of Memory\n"); | |
| LeaveCriticalSection(&pOutputQueue->csQueue); | |
| return; | |
| } | |
| qev->type = EOS_PACKET; | |
| qev->pSample = NULL; | |
| list_add_tail(&pOutputQueue->SampleList, &qev->entry); | |
| } | |
| else | |
| { | |
| IPin* ppin = NULL; | |
| IPin_ConnectedTo(&pOutputQueue->pInputPin->pin.IPin_iface, &ppin); | |
| if (ppin) | |
| { | |
| IPin_EndOfStream(ppin); | |
| IPin_Release(ppin); | |
| } | |
| } | |
| LeaveCriticalSection(&pOutputQueue->csQueue); | |
| /* Covers sending the Event to the worker Thread */ | |
| OutputQueue_SendAnyway(pOutputQueue); | |
| } | |
| DWORD WINAPI OutputQueueImpl_ThreadProc(OutputQueue *pOutputQueue) | |
| { | |
| do | |
| { | |
| EnterCriticalSection(&pOutputQueue->csQueue); | |
| if (!list_empty(&pOutputQueue->SampleList) && | |
| (!pOutputQueue->bBatchExact || | |
| list_count(&pOutputQueue->SampleList) >= pOutputQueue->lBatchSize || | |
| pOutputQueue->bSendAnyway | |
| ) | |
| ) | |
| { | |
| while (!list_empty(&pOutputQueue->SampleList)) | |
| { | |
| IMediaSample **ppSamples; | |
| LONG nSamples; | |
| LONG nSamplesProcessed; | |
| struct list *cursor, *cursor2; | |
| int i = 0; | |
| /* First Pass Process Samples */ | |
| i = list_count(&pOutputQueue->SampleList); | |
| ppSamples = HeapAlloc(GetProcessHeap(),0,sizeof(IMediaSample*) * i); | |
| nSamples = 0; | |
| LIST_FOR_EACH_SAFE(cursor, cursor2, &pOutputQueue->SampleList) | |
| { | |
| QueuedEvent *qev = LIST_ENTRY(cursor, QueuedEvent, entry); | |
| if (qev->type == SAMPLE_PACKET) | |
| ppSamples[nSamples++] = qev->pSample; | |
| else | |
| break; | |
| list_remove(cursor); | |
| HeapFree(GetProcessHeap(),0,qev); | |
| } | |
| if (pOutputQueue->pInputPin->pin.pConnectedTo && pOutputQueue->pInputPin->pMemInputPin) | |
| { | |
| IMemInputPin_AddRef(pOutputQueue->pInputPin->pMemInputPin); | |
| LeaveCriticalSection(&pOutputQueue->csQueue); | |
| IMemInputPin_ReceiveMultiple(pOutputQueue->pInputPin->pMemInputPin, ppSamples, nSamples, &nSamplesProcessed); | |
| EnterCriticalSection(&pOutputQueue->csQueue); | |
| IMemInputPin_Release(pOutputQueue->pInputPin->pMemInputPin); | |
| } | |
| for (i = 0; i < nSamples; i++) | |
| IMediaSample_Release(ppSamples[i]); | |
| HeapFree(GetProcessHeap(),0,ppSamples); | |
| /* Process Non-Samples */ | |
| LIST_FOR_EACH_SAFE(cursor, cursor2, &pOutputQueue->SampleList) | |
| { | |
| QueuedEvent *qev = LIST_ENTRY(cursor, QueuedEvent, entry); | |
| if (qev->type == EOS_PACKET) | |
| { | |
| IPin* ppin = NULL; | |
| IPin_ConnectedTo(&pOutputQueue->pInputPin->pin.IPin_iface, &ppin); | |
| if (ppin) | |
| { | |
| IPin_EndOfStream(ppin); | |
| IPin_Release(ppin); | |
| } | |
| } | |
| else if (qev->type == SAMPLE_PACKET) | |
| break; | |
| else | |
| FIXME("Unhandled Event type %i\n",qev->type); | |
| list_remove(cursor); | |
| HeapFree(GetProcessHeap(),0,qev); | |
| } | |
| } | |
| pOutputQueue->bSendAnyway = FALSE; | |
| } | |
| LeaveCriticalSection(&pOutputQueue->csQueue); | |
| WaitForSingleObject(pOutputQueue->hProcessQueue, INFINITE); | |
| } | |
| while (!pOutputQueue->bTerminate); | |
| return S_OK; | |
| } |