Implement A->W call for GetNamedSecurityInfo.
[wine] / dlls / quartz / avisplit.c
1 /*
2  * AVI Splitter Filter
3  *
4  * Copyright 2003 Robert Shearman
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20 /* FIXME:
21  * - we don't do anything with indices yet (we could use them when seeking)
22  * - we don't support multiple RIFF sections (i.e. large AVI files > 2Gb)
23  */
24
25 #include "quartz_private.h"
26 #include "control_private.h"
27 #include "pin.h"
28
29 #include "uuids.h"
30 #include "aviriff.h"
31 #include "mmreg.h"
32 #include "vfwmsgs.h"
33 #include "amvideo.h"
34
35 #include "fourcc.h"
36
37 #include "wine/unicode.h"
38 #include "wine/debug.h"
39
40 #include <math.h>
41 #include <assert.h>
42
43 WINE_DEFAULT_DEBUG_CHANNEL(quartz);
44
45 static const WCHAR wcsInputPinName[] = {'i','n','p','u','t',' ','p','i','n',0};
46 static const struct IBaseFilterVtbl AVISplitter_Vtbl;
47 static const struct IMediaSeekingVtbl AVISplitter_Seeking_Vtbl;
48 static const struct IPinVtbl AVISplitter_OutputPin_Vtbl;
49 static const struct IPinVtbl AVISplitter_InputPin_Vtbl;
50
51 static HRESULT AVISplitter_Sample(LPVOID iface, IMediaSample * pSample);
52 static HRESULT AVISplitter_OutputPin_QueryAccept(LPVOID iface, const AM_MEDIA_TYPE * pmt);
53 static HRESULT AVISplitter_QueryAccept(LPVOID iface, const AM_MEDIA_TYPE * pmt);
54 static HRESULT AVISplitter_InputPin_PreConnect(IPin * iface, IPin * pConnectPin);
55 static HRESULT AVISplitter_ChangeStart(LPVOID iface);
56 static HRESULT AVISplitter_ChangeStop(LPVOID iface);
57 static HRESULT AVISplitter_ChangeRate(LPVOID iface);
58
59 static HRESULT AVISplitter_InputPin_Construct(const PIN_INFO * pPinInfo, SAMPLEPROC pSampleProc, LPVOID pUserData, QUERYACCEPTPROC pQueryAccept, LPCRITICAL_SECTION pCritSec, IPin ** ppPin);
60
61 typedef struct AVISplitter
62 {
63     const IBaseFilterVtbl * lpVtbl;
64
65     ULONG refCount;
66     CRITICAL_SECTION csFilter;
67     FILTER_STATE state;
68     REFERENCE_TIME rtStreamStart;
69     IReferenceClock * pClock;
70     FILTER_INFO filterInfo;
71
72     PullPin * pInputPin;
73     ULONG cStreams;
74     IPin ** ppPins;
75     IMediaSample * pCurrentSample;
76     RIFFCHUNK CurrentChunk;
77     LONGLONG CurrentChunkOffset; /* in media time */
78     LONGLONG EndOfFile;
79     AVIMAINHEADER AviHeader;
80 } AVISplitter;
81
82 typedef struct AVISplitter_OutputPin
83 {
84     OutputPin pin;
85
86     AM_MEDIA_TYPE * pmt;
87     float fSamplesPerSec;
88     DWORD dwSamplesProcessed;
89     DWORD dwSampleSize;
90     DWORD dwLength;
91     MediaSeekingImpl mediaSeeking;
92 } AVISplitter_OutputPin;
93
94
95 #define _IMediaSeeking_Offset ((int)(&(((AVISplitter_OutputPin*)0)->mediaSeeking)))
96 #define ICOM_THIS_From_IMediaSeeking(impl, iface) impl* This = (impl*)(((char*)iface)-_IMediaSeeking_Offset);
97
98 HRESULT AVISplitter_create(IUnknown * pUnkOuter, LPVOID * ppv)
99 {
100     HRESULT hr;
101     PIN_INFO piInput;
102     AVISplitter * pAviSplit;
103
104     TRACE("(%p, %p)\n", pUnkOuter, ppv);
105
106     *ppv = NULL;
107
108     if (pUnkOuter)
109         return CLASS_E_NOAGGREGATION;
110     
111     pAviSplit = CoTaskMemAlloc(sizeof(AVISplitter));
112
113     pAviSplit->lpVtbl = &AVISplitter_Vtbl;
114     pAviSplit->refCount = 1;
115     InitializeCriticalSection(&pAviSplit->csFilter);
116     pAviSplit->state = State_Stopped;
117     pAviSplit->pClock = NULL;
118     pAviSplit->pCurrentSample = NULL;
119     ZeroMemory(&pAviSplit->filterInfo, sizeof(FILTER_INFO));
120
121     pAviSplit->cStreams = 0;
122     pAviSplit->ppPins = CoTaskMemAlloc(1 * sizeof(IPin *));
123
124     /* construct input pin */
125     piInput.dir = PINDIR_INPUT;
126     piInput.pFilter = (IBaseFilter *)pAviSplit;
127     strncpyW(piInput.achName, wcsInputPinName, sizeof(piInput.achName) / sizeof(piInput.achName[0]));
128
129     hr = AVISplitter_InputPin_Construct(&piInput, AVISplitter_Sample, (LPVOID)pAviSplit, AVISplitter_QueryAccept, &pAviSplit->csFilter, (IPin **)&pAviSplit->pInputPin);
130
131     if (SUCCEEDED(hr))
132     {
133         pAviSplit->ppPins[0] = (IPin *)pAviSplit->pInputPin;
134         pAviSplit->pInputPin->fnPreConnect = AVISplitter_InputPin_PreConnect;
135         *ppv = (LPVOID)pAviSplit;
136     }
137     else
138     {
139         CoTaskMemFree(pAviSplit->ppPins);
140         DeleteCriticalSection(&pAviSplit->csFilter);
141         CoTaskMemFree(pAviSplit);
142     }
143
144     return hr;
145 }
146
147 static HRESULT AVISplitter_OutputPin_Init(const PIN_INFO * pPinInfo, ALLOCATOR_PROPERTIES * props, LPVOID pUserData, QUERYACCEPTPROC pQueryAccept, const AM_MEDIA_TYPE * pmt, float fSamplesPerSec, LPCRITICAL_SECTION pCritSec, AVISplitter_OutputPin * pPinImpl)
148 {
149     pPinImpl->pmt = CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE));
150     CopyMediaType(pPinImpl->pmt, pmt);
151     pPinImpl->dwSamplesProcessed = 0;
152     pPinImpl->dwSampleSize = 0;
153     pPinImpl->fSamplesPerSec = fSamplesPerSec;
154
155     MediaSeekingImpl_Init((LPVOID)pPinInfo->pFilter, AVISplitter_ChangeStop, AVISplitter_ChangeStart, AVISplitter_ChangeRate, &pPinImpl->mediaSeeking);
156     pPinImpl->mediaSeeking.lpVtbl = &AVISplitter_Seeking_Vtbl;
157
158     return OutputPin_Init(pPinInfo, props, pUserData, pQueryAccept, pCritSec, &pPinImpl->pin);
159 }
160
161 static HRESULT AVISplitter_OutputPin_Construct(const PIN_INFO * pPinInfo, ALLOCATOR_PROPERTIES * props, LPVOID pUserData, QUERYACCEPTPROC pQueryAccept, const AM_MEDIA_TYPE * pmt, float fSamplesPerSec, LPCRITICAL_SECTION pCritSec, IPin ** ppPin)
162 {
163     AVISplitter_OutputPin * pPinImpl;
164
165     *ppPin = NULL;
166
167     assert(pPinInfo->dir == PINDIR_OUTPUT);
168
169     pPinImpl = CoTaskMemAlloc(sizeof(AVISplitter_OutputPin));
170
171     if (!pPinImpl)
172         return E_OUTOFMEMORY;
173
174     if (SUCCEEDED(AVISplitter_OutputPin_Init(pPinInfo, props, pUserData, pQueryAccept, pmt, fSamplesPerSec, pCritSec, pPinImpl)))
175     {
176         pPinImpl->pin.pin.lpVtbl = &AVISplitter_OutputPin_Vtbl;
177         
178         *ppPin = (IPin *)pPinImpl;
179         return S_OK;
180     }
181     return E_FAIL;
182 }
183
184 static HRESULT WINAPI AVISplitter_QueryInterface(IBaseFilter * iface, REFIID riid, LPVOID * ppv)
185 {
186     ICOM_THIS(AVISplitter, iface);
187     TRACE("(%s, %p)\n", qzdebugstr_guid(riid), ppv);
188
189     *ppv = NULL;
190
191     if (IsEqualIID(riid, &IID_IUnknown))
192         *ppv = (LPVOID)This;
193     else if (IsEqualIID(riid, &IID_IPersist))
194         *ppv = (LPVOID)This;
195     else if (IsEqualIID(riid, &IID_IMediaFilter))
196         *ppv = (LPVOID)This;
197     else if (IsEqualIID(riid, &IID_IBaseFilter))
198         *ppv = (LPVOID)This;
199
200     if (*ppv)
201     {
202         IUnknown_AddRef((IUnknown *)(*ppv));
203         return S_OK;
204     }
205
206     FIXME("No interface for %s!\n", qzdebugstr_guid(riid));
207
208     return E_NOINTERFACE;
209 }
210
211 static ULONG WINAPI AVISplitter_AddRef(IBaseFilter * iface)
212 {
213     ICOM_THIS(AVISplitter, iface);
214     TRACE("()\n");
215     return InterlockedIncrement(&This->refCount);
216 }
217
218 static ULONG WINAPI AVISplitter_Release(IBaseFilter * iface)
219 {
220     ICOM_THIS(AVISplitter, iface);
221     TRACE("()\n");
222     if (!InterlockedDecrement(&This->refCount))
223     {
224         ULONG i;
225
226         DeleteCriticalSection(&This->csFilter);
227         IReferenceClock_Release(This->pClock);
228         
229         for (i = 0; i < This->cStreams + 1; i++)
230             IPin_Release(This->ppPins[i]);
231         
232         HeapFree(GetProcessHeap(), 0, This->ppPins);
233         This->lpVtbl = NULL;
234         
235         TRACE("Destroying AVI splitter\n");
236         CoTaskMemFree(This);
237         
238         return 0;
239     }
240     else
241         return This->refCount;
242 }
243
244 /** IPersist methods **/
245
246 static HRESULT WINAPI AVISplitter_GetClassID(IBaseFilter * iface, CLSID * pClsid)
247 {
248     TRACE("(%p)\n", pClsid);
249
250     *pClsid = CLSID_AviSplitter;
251
252     return S_OK;
253 }
254
255 /** IMediaFilter methods **/
256
257 static HRESULT WINAPI AVISplitter_Stop(IBaseFilter * iface)
258 {
259     HRESULT hr;
260     ICOM_THIS(AVISplitter, iface);
261
262     TRACE("()\n");
263
264     EnterCriticalSection(&This->csFilter);
265     {
266         hr = PullPin_StopProcessing(This->pInputPin);
267
268         This->state = State_Stopped;
269     }
270     LeaveCriticalSection(&This->csFilter);
271     
272     return hr;
273 }
274
275 static HRESULT WINAPI AVISplitter_Pause(IBaseFilter * iface)
276 {
277     HRESULT hr = S_OK;
278     BOOL bInit;
279     ICOM_THIS(AVISplitter, iface);
280     
281     TRACE("()\n");
282
283     EnterCriticalSection(&This->csFilter);
284     {
285         bInit = (This->state == State_Stopped);
286         This->state = State_Paused;
287     }
288     LeaveCriticalSection(&This->csFilter);
289
290     if (bInit)
291     {
292         unsigned int i;
293
294         hr = PullPin_Seek(This->pInputPin, This->CurrentChunkOffset, This->EndOfFile);
295
296         if (SUCCEEDED(hr))
297             hr = PullPin_InitProcessing(This->pInputPin);
298
299         if (SUCCEEDED(hr))
300         {
301             for (i = 1; i < This->cStreams + 1; i++)
302             {
303                 OutputPin_DeliverNewSegment((OutputPin *)This->ppPins[i], 0, (LONGLONG)ceil(10000000.0 * (float)((AVISplitter_OutputPin *)This->ppPins[i])->dwLength / ((AVISplitter_OutputPin *)This->ppPins[i])->fSamplesPerSec), 1.0);
304                 ((AVISplitter_OutputPin *)This->ppPins[i])->mediaSeeking.llDuration = (LONGLONG)ceil(10000000.0 * (float)((AVISplitter_OutputPin *)This->ppPins[i])->dwLength / ((AVISplitter_OutputPin *)This->ppPins[i])->fSamplesPerSec);
305                 ((AVISplitter_OutputPin *)This->ppPins[i])->mediaSeeking.llStop = (LONGLONG)ceil(10000000.0 * (float)((AVISplitter_OutputPin *)This->ppPins[i])->dwLength / ((AVISplitter_OutputPin *)This->ppPins[i])->fSamplesPerSec);
306                 OutputPin_CommitAllocator((OutputPin *)This->ppPins[i]);
307             }
308
309             /* FIXME: this is a little hacky: we have to deliver (at least?) one sample
310              * to each renderer before they will complete their transitions. We should probably
311              * seek through the stream for the first of each, rather than do it this way which is
312              * probably a bit prone to deadlocking */
313             hr = PullPin_StartProcessing(This->pInputPin);
314         }
315     }
316     /* FIXME: else pause thread */
317
318     return hr;
319 }
320
321 static HRESULT WINAPI AVISplitter_Run(IBaseFilter * iface, REFERENCE_TIME tStart)
322 {
323     HRESULT hr = S_OK;
324     ICOM_THIS(AVISplitter, iface);
325
326     TRACE("(%s)\n", wine_dbgstr_longlong(tStart));
327
328     EnterCriticalSection(&This->csFilter);
329     {
330         This->rtStreamStart = tStart;
331
332         This->state = State_Running;
333     }
334     LeaveCriticalSection(&This->csFilter);
335
336     return hr;
337 }
338
339 static HRESULT WINAPI AVISplitter_GetState(IBaseFilter * iface, DWORD dwMilliSecsTimeout, FILTER_STATE *pState)
340 {
341     ICOM_THIS(AVISplitter, iface);
342
343     TRACE("(%ld, %p)\n", dwMilliSecsTimeout, pState);
344
345     EnterCriticalSection(&This->csFilter);
346     {
347         *pState = This->state;
348     }
349     LeaveCriticalSection(&This->csFilter);
350
351     /* FIXME: this is a little bit unsafe, but I don't see that we can do this
352      * while in the critical section. Maybe we could copy the pointer and addref in the
353      * critical section and then release after this.
354      */
355     if (This->pInputPin && (PullPin_WaitForStateChange(This->pInputPin, dwMilliSecsTimeout) == S_FALSE))
356         return VFW_S_STATE_INTERMEDIATE;
357
358     return S_OK;
359 }
360
361 static HRESULT WINAPI AVISplitter_SetSyncSource(IBaseFilter * iface, IReferenceClock *pClock)
362 {
363     ICOM_THIS(AVISplitter, iface);
364
365     TRACE("(%p)\n", pClock);
366
367     EnterCriticalSection(&This->csFilter);
368     {
369         if (This->pClock)
370             IReferenceClock_Release(This->pClock);
371         This->pClock = pClock;
372         if (This->pClock)
373             IReferenceClock_AddRef(This->pClock);
374     }
375     LeaveCriticalSection(&This->csFilter);
376
377     return S_OK;
378 }
379
380 static HRESULT WINAPI AVISplitter_GetSyncSource(IBaseFilter * iface, IReferenceClock **ppClock)
381 {
382     ICOM_THIS(AVISplitter, iface);
383
384     TRACE("(%p)\n", ppClock);
385
386     EnterCriticalSection(&This->csFilter);
387     {
388         *ppClock = This->pClock;
389         IReferenceClock_AddRef(This->pClock);
390     }
391     LeaveCriticalSection(&This->csFilter);
392     
393     return S_OK;
394 }
395
396 /** IBaseFilter implementation **/
397
398 static HRESULT WINAPI AVISplitter_EnumPins(IBaseFilter * iface, IEnumPins **ppEnum)
399 {
400     ENUMPINDETAILS epd;
401     ICOM_THIS(AVISplitter, iface);
402
403     TRACE("(%p)\n", ppEnum);
404
405     epd.cPins = This->cStreams + 1; /* +1 for input pin */
406     epd.ppPins = This->ppPins;
407     return IEnumPinsImpl_Construct(&epd, ppEnum);
408 }
409
410 static HRESULT WINAPI AVISplitter_FindPin(IBaseFilter * iface, LPCWSTR Id, IPin **ppPin)
411 {
412     FIXME("AVISplitter::FindPin(...)\n");
413
414     /* FIXME: critical section */
415
416     return E_NOTIMPL;
417 }
418
419 static HRESULT WINAPI AVISplitter_QueryFilterInfo(IBaseFilter * iface, FILTER_INFO *pInfo)
420 {
421     ICOM_THIS(AVISplitter, iface);
422
423     TRACE("(%p)\n", pInfo);
424
425     strcpyW(pInfo->achName, This->filterInfo.achName);
426     pInfo->pGraph = This->filterInfo.pGraph;
427
428     if (pInfo->pGraph)
429         IFilterGraph_AddRef(pInfo->pGraph);
430     
431     return S_OK;
432 }
433
434 static HRESULT WINAPI AVISplitter_JoinFilterGraph(IBaseFilter * iface, IFilterGraph *pGraph, LPCWSTR pName)
435 {
436     HRESULT hr = S_OK;
437     ICOM_THIS(AVISplitter, iface);
438
439     TRACE("(%p, %s)\n", pGraph, debugstr_w(pName));
440
441     EnterCriticalSection(&This->csFilter);
442     {
443         if (pName)
444             strcpyW(This->filterInfo.achName, pName);
445         else
446             *This->filterInfo.achName = '\0';
447         This->filterInfo.pGraph = pGraph; /* NOTE: do NOT increase ref. count */
448     }
449     LeaveCriticalSection(&This->csFilter);
450
451     return hr;
452 }
453
454 static HRESULT WINAPI AVISplitter_QueryVendorInfo(IBaseFilter * iface, LPWSTR *pVendorInfo)
455 {
456     TRACE("(%p)\n", pVendorInfo);
457     return E_NOTIMPL;
458 }
459
460 static const IBaseFilterVtbl AVISplitter_Vtbl =
461 {
462     ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
463     AVISplitter_QueryInterface,
464     AVISplitter_AddRef,
465     AVISplitter_Release,
466     AVISplitter_GetClassID,
467     AVISplitter_Stop,
468     AVISplitter_Pause,
469     AVISplitter_Run,
470     AVISplitter_GetState,
471     AVISplitter_SetSyncSource,
472     AVISplitter_GetSyncSource,
473     AVISplitter_EnumPins,
474     AVISplitter_FindPin,
475     AVISplitter_QueryFilterInfo,
476     AVISplitter_JoinFilterGraph,
477     AVISplitter_QueryVendorInfo
478 };
479
480 static HRESULT AVISplitter_NextChunk(LONGLONG * pllCurrentChunkOffset, RIFFCHUNK * pCurrentChunk, const REFERENCE_TIME * tStart, const REFERENCE_TIME * tStop, const BYTE * pbSrcStream)
481 {
482     *pllCurrentChunkOffset += MEDIATIME_FROM_BYTES(sizeof(RIFFCHUNK) + RIFFROUND(pCurrentChunk->cb));
483
484     if (*pllCurrentChunkOffset > *tStop)
485         return S_FALSE; /* no more data - we couldn't even get the next chunk header! */
486     else if (*pllCurrentChunkOffset + MEDIATIME_FROM_BYTES(sizeof(RIFFCHUNK)) >= *tStop)
487     {
488         memcpy(pCurrentChunk, pbSrcStream + (DWORD)BYTES_FROM_MEDIATIME(*pllCurrentChunkOffset - *tStart), (DWORD)BYTES_FROM_MEDIATIME(*tStop - *pllCurrentChunkOffset));
489         return S_FALSE; /* no more data */
490     }
491     else
492         memcpy(pCurrentChunk, pbSrcStream + (DWORD)BYTES_FROM_MEDIATIME(*pllCurrentChunkOffset - *tStart), sizeof(RIFFCHUNK));
493
494     return S_OK;
495 }
496
497 static HRESULT AVISplitter_Sample(LPVOID iface, IMediaSample * pSample)
498 {
499     ICOM_THIS(AVISplitter, iface);
500     LPBYTE pbSrcStream = NULL;
501     long cbSrcStream = 0;
502     REFERENCE_TIME tStart, tStop;
503     HRESULT hr;
504     BOOL bMoreData = TRUE;
505
506     hr = IMediaSample_GetPointer(pSample, &pbSrcStream);
507
508     hr = IMediaSample_GetTime(pSample, &tStart, &tStop);
509
510     cbSrcStream = IMediaSample_GetActualDataLength(pSample);
511
512     /* trace removed for performance reasons */
513 /*  TRACE("(%p)\n", pSample); */
514
515     assert(BYTES_FROM_MEDIATIME(tStop - tStart) == cbSrcStream);
516
517     if (This->CurrentChunkOffset <= tStart && This->CurrentChunkOffset + MEDIATIME_FROM_BYTES(sizeof(RIFFCHUNK)) > tStart)
518     {
519         DWORD offset = (DWORD)BYTES_FROM_MEDIATIME(tStart - This->CurrentChunkOffset);
520         assert(offset <= sizeof(RIFFCHUNK));
521         memcpy((BYTE *)&This->CurrentChunk + offset, pbSrcStream, sizeof(RIFFCHUNK) - offset);
522     }
523     else if (This->CurrentChunkOffset > tStart)
524     {
525         DWORD offset = (DWORD)BYTES_FROM_MEDIATIME(This->CurrentChunkOffset - tStart);
526         if (offset >= (DWORD)cbSrcStream)
527         {
528             FIXME("large offset\n");
529             return S_OK;
530         }
531
532         memcpy(&This->CurrentChunk, pbSrcStream + offset, sizeof(RIFFCHUNK));
533     }
534
535     assert(This->CurrentChunkOffset + MEDIATIME_FROM_BYTES(sizeof(RIFFCHUNK)) < tStop);
536
537     while (bMoreData)
538     {
539         BYTE * pbDstStream;
540         long cbDstStream;
541         long chunk_remaining_bytes = 0;
542         long offset_src;
543         WORD streamId;
544         AVISplitter_OutputPin * pOutputPin;
545         BOOL bSyncPoint = TRUE;
546
547         if (This->CurrentChunkOffset >= tStart)
548             offset_src = (long)BYTES_FROM_MEDIATIME(This->CurrentChunkOffset - tStart) + sizeof(RIFFCHUNK);
549         else
550             offset_src = 0;
551
552         switch (This->CurrentChunk.fcc)
553         {
554         case ckidJUNK:
555             /* silently ignore */
556             if (S_FALSE == AVISplitter_NextChunk(&This->CurrentChunkOffset, &This->CurrentChunk, &tStart, &tStop, pbSrcStream))
557                 bMoreData = FALSE;
558             continue;
559         default:
560             switch (TWOCCFromFOURCC(This->CurrentChunk.fcc))
561             {
562             case cktypeDIBcompressed:
563                 bSyncPoint = FALSE;
564                 /* fall-through */
565             case cktypeDIBbits:
566                 /* FIXME: check that pin is of type video */
567                 break;
568             case cktypeWAVEbytes:
569                 /* FIXME: check that pin is of type audio */
570                 break;
571             case cktypePALchange:
572                 FIXME("handle palette change\n");
573                 break;
574             default:
575                 FIXME("Skipping unknown chunk type: %s at file offset 0x%lx\n", debugstr_an((LPSTR)&This->CurrentChunk.fcc, 4), (DWORD)BYTES_FROM_MEDIATIME(This->CurrentChunkOffset));
576                 if (S_FALSE == AVISplitter_NextChunk(&This->CurrentChunkOffset, &This->CurrentChunk, &tStart, &tStop, pbSrcStream))
577                     bMoreData = FALSE;
578                 continue;
579             }
580         }
581
582         streamId = StreamFromFOURCC(This->CurrentChunk.fcc);
583
584         if (streamId > This->cStreams)
585         {
586             ERR("Corrupted AVI file (contains stream id %d, but supposed to only have %ld streams)\n", streamId, This->cStreams);
587             return E_FAIL;
588         }
589
590         pOutputPin = (AVISplitter_OutputPin *)This->ppPins[streamId + 1];
591
592         if (!This->pCurrentSample)
593         {
594             /* cache media sample until it is ready to be despatched
595              * (i.e. we reach the end of the chunk) */
596             hr = OutputPin_GetDeliveryBuffer(&pOutputPin->pin, &This->pCurrentSample, NULL, NULL, 0);
597
598             if (SUCCEEDED(hr))
599             {
600                 hr = IMediaSample_SetActualDataLength(This->pCurrentSample, 0);
601                 assert(hr == S_OK);
602             }
603             else
604             {
605                 TRACE("Skipping sending sample for stream %02d due to error (%lx)\n", streamId, hr);
606                 This->pCurrentSample = NULL;
607                 if (S_FALSE == AVISplitter_NextChunk(&This->CurrentChunkOffset, &This->CurrentChunk, &tStart, &tStop, pbSrcStream))
608                     bMoreData = FALSE;
609                 continue;
610             }
611         }
612
613         hr = IMediaSample_GetPointer(This->pCurrentSample, &pbDstStream);
614
615         if (SUCCEEDED(hr))
616         {
617             cbDstStream = IMediaSample_GetSize(This->pCurrentSample);
618
619             chunk_remaining_bytes = (long)BYTES_FROM_MEDIATIME(This->CurrentChunkOffset + MEDIATIME_FROM_BYTES(This->CurrentChunk.cb + sizeof(RIFFCHUNK)) - tStart) - offset_src;
620         
621             assert(chunk_remaining_bytes >= 0);
622             assert(chunk_remaining_bytes <= cbDstStream - IMediaSample_GetActualDataLength(This->pCurrentSample));
623
624             /* trace removed for performance reasons */
625 /*          TRACE("chunk_remaining_bytes: 0x%lx, cbSrcStream: 0x%lx, offset_src: 0x%lx\n", chunk_remaining_bytes, cbSrcStream, offset_src); */
626         }
627
628         if (chunk_remaining_bytes <= cbSrcStream - offset_src)
629         {
630             if (SUCCEEDED(hr))
631             {
632                 memcpy(pbDstStream + IMediaSample_GetActualDataLength(This->pCurrentSample), pbSrcStream + offset_src, chunk_remaining_bytes);
633                 hr = IMediaSample_SetActualDataLength(This->pCurrentSample, chunk_remaining_bytes + IMediaSample_GetActualDataLength(This->pCurrentSample));
634                 assert(hr == S_OK);
635             }
636
637             if (SUCCEEDED(hr))
638             {
639                 REFERENCE_TIME tAviStart, tAviStop;
640
641                 /* FIXME: hack */
642                 if (pOutputPin->dwSamplesProcessed == 0)
643                     IMediaSample_SetDiscontinuity(This->pCurrentSample, TRUE);
644
645                 IMediaSample_SetSyncPoint(This->pCurrentSample, bSyncPoint);
646
647                 pOutputPin->dwSamplesProcessed++;
648
649                 if (pOutputPin->dwSampleSize)
650                     tAviStart = (LONGLONG)ceil(10000000.0 * (float)(pOutputPin->dwSamplesProcessed - 1) * (float)IMediaSample_GetActualDataLength(This->pCurrentSample) / ((float)pOutputPin->dwSampleSize * pOutputPin->fSamplesPerSec));
651                 else
652                     tAviStart = (LONGLONG)ceil(10000000.0 * (float)(pOutputPin->dwSamplesProcessed - 1) / (float)pOutputPin->fSamplesPerSec);
653                 if (pOutputPin->dwSampleSize)
654                     tAviStop = (LONGLONG)ceil(10000000.0 * (float)pOutputPin->dwSamplesProcessed * (float)IMediaSample_GetActualDataLength(This->pCurrentSample) / ((float)pOutputPin->dwSampleSize * pOutputPin->fSamplesPerSec));
655                 else
656                     tAviStop = (LONGLONG)ceil(10000000.0 * (float)pOutputPin->dwSamplesProcessed / (float)pOutputPin->fSamplesPerSec);
657
658                 IMediaSample_SetTime(This->pCurrentSample, &tAviStart, &tAviStop);
659
660
661                 hr = OutputPin_SendSample(&pOutputPin->pin, This->pCurrentSample);
662                 if (hr != S_OK && hr != VFW_E_NOT_CONNECTED)
663                     ERR("Error sending sample (%lx)\n", hr);
664             }
665
666             if (This->pCurrentSample)
667                 IMediaSample_Release(This->pCurrentSample);
668             
669             This->pCurrentSample = NULL;
670
671             if (S_FALSE == AVISplitter_NextChunk(&This->CurrentChunkOffset, &This->CurrentChunk, &tStart, &tStop, pbSrcStream))
672                 bMoreData = FALSE;
673         }
674         else
675         {
676             if (SUCCEEDED(hr))
677             {
678                 memcpy(pbDstStream + IMediaSample_GetActualDataLength(This->pCurrentSample), pbSrcStream + offset_src, cbSrcStream - offset_src);
679                 IMediaSample_SetActualDataLength(This->pCurrentSample, cbSrcStream - offset_src + IMediaSample_GetActualDataLength(This->pCurrentSample));
680             }
681             bMoreData = FALSE;
682         }
683     }
684     return hr;
685 }
686
687 static HRESULT AVISplitter_QueryAccept(LPVOID iface, const AM_MEDIA_TYPE * pmt)
688 {
689     if (IsEqualIID(&pmt->majortype, &MEDIATYPE_Stream) && IsEqualIID(&pmt->subtype, &MEDIASUBTYPE_Avi))
690         return S_OK;
691     return S_FALSE;
692 }
693
694 static HRESULT AVISplitter_ProcessStreamList(AVISplitter * This, const BYTE * pData, DWORD cb)
695 {
696     PIN_INFO piOutput;
697     RIFFCHUNK * pChunk;
698     IPin ** ppOldPins;
699     HRESULT hr;
700     AM_MEDIA_TYPE amt;
701     float fSamplesPerSec = 0.0f;
702     DWORD dwSampleSize = 0;
703     DWORD dwLength = 0;
704     ALLOCATOR_PROPERTIES props;
705     static const WCHAR wszStreamTemplate[] = {'S','t','r','e','a','m',' ','%','0','2','d',0};
706
707     props.cbAlign = 1;
708     props.cbPrefix = 0;
709     props.cbBuffer = 0x20000;
710     props.cBuffers = 2;
711     
712     ZeroMemory(&amt, sizeof(amt));
713     piOutput.dir = PINDIR_OUTPUT;
714     piOutput.pFilter = (IBaseFilter *)This;
715     wsprintfW(piOutput.achName, wszStreamTemplate, This->cStreams);
716
717     for (pChunk = (RIFFCHUNK *)pData; ((BYTE *)pChunk >= pData) && ((BYTE *)pChunk + sizeof(RIFFCHUNK) < pData + cb) && (pChunk->cb > 0); pChunk = (RIFFCHUNK *)((BYTE*)pChunk + sizeof(RIFFCHUNK) + pChunk->cb))
718     {
719         switch (pChunk->fcc)
720         {
721         case ckidSTREAMHEADER:
722             {
723                 const AVISTREAMHEADER * pStrHdr = (const AVISTREAMHEADER *)pChunk;
724                 TRACE("processing stream header\n");
725
726                 fSamplesPerSec = (float)pStrHdr->dwRate / (float)pStrHdr->dwScale;
727
728                 switch (pStrHdr->fccType)
729                 {
730                 case streamtypeVIDEO:
731                     memcpy(&amt.formattype, &FORMAT_VideoInfo, sizeof(GUID));
732                     amt.pbFormat = NULL;
733                     amt.cbFormat = 0;
734                     break;
735                 case streamtypeAUDIO:
736                     memcpy(&amt.formattype, &FORMAT_WaveFormatEx, sizeof(GUID));
737                     break;
738                 default:
739                     memcpy(&amt.formattype, &FORMAT_None, sizeof(GUID));
740                 }
741                 memcpy(&amt.majortype, &MEDIATYPE_Video, sizeof(GUID));
742                 amt.majortype.Data1 = pStrHdr->fccType;
743                 memcpy(&amt.subtype, &MEDIATYPE_Video, sizeof(GUID));
744                 amt.subtype.Data1 = pStrHdr->fccHandler;
745                 TRACE("Subtype FCC: %.04s\n", (LPSTR)&pStrHdr->fccHandler);
746                 amt.lSampleSize = pStrHdr->dwSampleSize;
747                 amt.bFixedSizeSamples = (amt.lSampleSize != 0);
748
749                 /* FIXME: Is this right? */
750                 if (!amt.lSampleSize)
751                 {
752                     amt.lSampleSize = 1;
753                     dwSampleSize = 1;
754                 }
755
756                 amt.bTemporalCompression = IsEqualGUID(&amt.majortype, &MEDIATYPE_Video); /* FIXME? */
757                 dwSampleSize = pStrHdr->dwSampleSize;
758                 dwLength = pStrHdr->dwLength;
759                 if (!dwLength)
760                     dwLength = This->AviHeader.dwTotalFrames;
761
762                 if (pStrHdr->dwSuggestedBufferSize)
763                     props.cbBuffer = pStrHdr->dwSuggestedBufferSize;
764
765                 break;
766             }
767         case ckidSTREAMFORMAT:
768             TRACE("processing stream format data\n");
769             if (IsEqualIID(&amt.formattype, &FORMAT_VideoInfo))
770             {
771                 VIDEOINFOHEADER * pvi;
772                 /* biCompression member appears to override the value in the stream header.
773                  * i.e. the stream header can say something completely contradictory to what
774                  * is in the BITMAPINFOHEADER! */
775                 if (pChunk->cb < sizeof(BITMAPINFOHEADER))
776                 {
777                     ERR("Not enough bytes for BITMAPINFOHEADER\n");
778                     return E_FAIL;
779                 }
780                 amt.cbFormat = sizeof(VIDEOINFOHEADER) - sizeof(BITMAPINFOHEADER) + pChunk->cb;
781                 amt.pbFormat = CoTaskMemAlloc(amt.cbFormat);
782                 ZeroMemory(amt.pbFormat, amt.cbFormat);
783                 pvi = (VIDEOINFOHEADER *)amt.pbFormat;
784                 pvi->AvgTimePerFrame = (LONGLONG)(10000000.0 / fSamplesPerSec);
785                 CopyMemory(&pvi->bmiHeader, (const BYTE *)(pChunk + 1), pChunk->cb);
786                 if (pvi->bmiHeader.biCompression)
787                     amt.subtype.Data1 = pvi->bmiHeader.biCompression;
788             }
789             else
790             {
791                 amt.cbFormat = pChunk->cb;
792                 amt.pbFormat = CoTaskMemAlloc(amt.cbFormat);
793                 CopyMemory(amt.pbFormat, (const BYTE *)(pChunk + 1), amt.cbFormat);
794             }
795             break;
796         case ckidSTREAMNAME:
797             TRACE("processing stream name\n");
798             /* FIXME: this doesn't exactly match native version (we omit the "##)" prefix), but hey... */
799             MultiByteToWideChar(CP_ACP, 0, (LPCSTR)(pChunk + 1), pChunk->cb, piOutput.achName, sizeof(piOutput.achName) / sizeof(piOutput.achName[0]));
800             break;
801         case ckidSTREAMHANDLERDATA:
802             FIXME("process stream handler data\n");
803             break;
804         case ckidJUNK:
805             TRACE("JUNK chunk ignored\n");
806             break;
807         default:
808             FIXME("unknown chunk type \"%.04s\" ignored\n", (LPSTR)&pChunk->fcc);
809         }
810     }
811
812     if (IsEqualGUID(&amt.formattype, &FORMAT_WaveFormatEx))
813     {
814         memcpy(&amt.subtype, &MEDIATYPE_Video, sizeof(GUID));
815         amt.subtype.Data1 = ((WAVEFORMATEX *)amt.pbFormat)->wFormatTag;
816     }
817
818     dump_AM_MEDIA_TYPE(&amt);
819     FIXME("fSamplesPerSec = %f\n", (double)fSamplesPerSec);
820     FIXME("dwSampleSize = %lx\n", dwSampleSize);
821     FIXME("dwLength = %lx\n", dwLength);
822
823     ppOldPins = This->ppPins;
824
825     This->ppPins = HeapAlloc(GetProcessHeap(), 0, (This->cStreams + 2) * sizeof(IPin *));
826     memcpy(This->ppPins, ppOldPins, (This->cStreams + 1) * sizeof(IPin *));
827
828     hr = AVISplitter_OutputPin_Construct(&piOutput, &props, NULL, AVISplitter_OutputPin_QueryAccept, &amt, fSamplesPerSec, &This->csFilter, This->ppPins + This->cStreams + 1);
829
830     if (SUCCEEDED(hr))
831     {
832         ((AVISplitter_OutputPin *)(This->ppPins[This->cStreams + 1]))->dwSampleSize = dwSampleSize;
833         ((AVISplitter_OutputPin *)(This->ppPins[This->cStreams + 1]))->dwLength = dwLength;
834         ((AVISplitter_OutputPin *)(This->ppPins[This->cStreams + 1]))->pin.pin.pUserData = (LPVOID)This->ppPins[This->cStreams + 1];
835         This->cStreams++;
836         HeapFree(GetProcessHeap(), 0, ppOldPins);
837     }
838     else
839     {
840         HeapFree(GetProcessHeap(), 0, This->ppPins);
841         This->ppPins = ppOldPins;
842         ERR("Failed with error %lx\n", hr);
843     }
844
845     return hr;
846 }
847
848 static HRESULT AVISplitter_RemoveOutputPins(AVISplitter * This)
849 {
850     /* NOTE: should be in critical section when calling this function */
851
852     ULONG i;
853     IPin ** ppOldPins = This->ppPins;
854
855     /* reduce the pin array down to 1 (just our input pin) */
856     This->ppPins = HeapAlloc(GetProcessHeap(), 0, sizeof(IPin *) * 1);
857     memcpy(This->ppPins, ppOldPins, sizeof(IPin *) * 1);
858
859     for (i = 0; i < This->cStreams; i++)
860     {
861         OutputPin_DeliverDisconnect((OutputPin *)ppOldPins[i + 1]);
862         IPin_Release(ppOldPins[i + 1]);
863     }
864
865     This->cStreams = 0;
866     HeapFree(GetProcessHeap(), 0, ppOldPins);
867
868     return S_OK;
869 }
870
871 /* FIXME: fix leaks on failure here */
872 static HRESULT AVISplitter_InputPin_PreConnect(IPin * iface, IPin * pConnectPin)
873 {
874     ICOM_THIS(PullPin, iface);
875     HRESULT hr;
876     RIFFLIST list;
877     LONGLONG pos = 0; /* in bytes */
878     BYTE * pBuffer;
879     RIFFCHUNK * pCurrentChunk;
880     AVISplitter * pAviSplit = (AVISplitter *)This->pin.pinInfo.pFilter;
881
882     hr = IAsyncReader_SyncRead(This->pReader, pos, sizeof(list), (BYTE *)&list);
883     pos += sizeof(list);
884
885     if (list.fcc != ckidRIFF)
886     {
887         ERR("Input stream not a RIFF file\n");
888         return E_FAIL;
889     }
890     if (list.cb > 1 * 1024 * 1024 * 1024) /* cannot be more than 1Gb in size */
891     {
892         ERR("Input stream violates RIFF spec\n");
893         return E_FAIL;
894     }
895     if (list.fccListType != ckidAVI)
896     {
897         ERR("Input stream not an AVI RIFF file\n");
898         return E_FAIL;
899     }
900
901     hr = IAsyncReader_SyncRead(This->pReader, pos, sizeof(list), (BYTE *)&list);
902     if (list.fcc != ckidLIST)
903     {
904         ERR("Expected LIST chunk, but got %.04s\n", (LPSTR)&list.fcc);
905         return E_FAIL;
906     }
907     if (list.fccListType != ckidHEADERLIST)
908     {
909         ERR("Header list expected. Got: %.04s\n", (LPSTR)&list.fccListType);
910         return E_FAIL;
911     }
912
913     pBuffer = HeapAlloc(GetProcessHeap(), 0, list.cb - sizeof(RIFFLIST) + sizeof(RIFFCHUNK));
914     hr = IAsyncReader_SyncRead(This->pReader, pos + sizeof(list), list.cb - sizeof(RIFFLIST) + sizeof(RIFFCHUNK), pBuffer);
915
916     pAviSplit->AviHeader.cb = 0;
917
918     for (pCurrentChunk = (RIFFCHUNK *)pBuffer; (BYTE *)pCurrentChunk + sizeof(*pCurrentChunk) < pBuffer + list.cb; pCurrentChunk = (RIFFCHUNK *)(((BYTE *)pCurrentChunk) + sizeof(*pCurrentChunk) + pCurrentChunk->cb))
919     {
920         RIFFLIST * pList;
921
922         switch (pCurrentChunk->fcc)
923         {
924         case ckidMAINAVIHEADER:
925             /* AVIMAINHEADER includes the structure that is pCurrentChunk at the moment */
926             memcpy(&pAviSplit->AviHeader, pCurrentChunk, sizeof(pAviSplit->AviHeader));
927             break;
928         case ckidLIST:
929             pList = (RIFFLIST *)pCurrentChunk;
930             switch (pList->fccListType)
931             {
932             case ckidSTREAMLIST:
933                 hr = AVISplitter_ProcessStreamList(pAviSplit, (BYTE *)pCurrentChunk + sizeof(RIFFLIST), pCurrentChunk->cb + sizeof(RIFFCHUNK) - sizeof(RIFFLIST));
934                 break;
935             case ckidODML:
936                 FIXME("process ODML header\n");
937                 break;
938             }
939             break;
940         case ckidJUNK:
941             /* ignore */
942             break;
943         default:
944             FIXME("unrecognised header list type: %.04s\n", (LPSTR)&pCurrentChunk->fcc);
945         }
946     }
947     HeapFree(GetProcessHeap(), 0, pBuffer);
948
949     if (pAviSplit->AviHeader.cb != sizeof(pAviSplit->AviHeader) - sizeof(RIFFCHUNK))
950     {
951         ERR("Avi Header wrong size!\n");
952         return E_FAIL;
953     }
954
955     pos += sizeof(RIFFCHUNK) + list.cb;
956     hr = IAsyncReader_SyncRead(This->pReader, pos, sizeof(list), (BYTE *)&list);
957
958     if (list.fcc == ckidJUNK)
959     {
960         pos += sizeof(RIFFCHUNK) + list.cb;
961         hr = IAsyncReader_SyncRead(This->pReader, pos, sizeof(list), (BYTE *)&list);
962     }
963
964     if (list.fcc != ckidLIST)
965     {
966         ERR("Expected LIST, but got %.04s\n", (LPSTR)&list.fcc);
967         return E_FAIL;
968     }
969     if (list.fccListType != ckidAVIMOVIE)
970     {
971         ERR("Expected AVI movie list, but got %.04s\n", (LPSTR)&list.fccListType);
972         return E_FAIL;
973     }
974
975     if (hr == S_OK)
976     {
977         pAviSplit->CurrentChunkOffset = MEDIATIME_FROM_BYTES(pos + sizeof(RIFFLIST));
978         pAviSplit->EndOfFile = MEDIATIME_FROM_BYTES(pos + list.cb + sizeof(RIFFLIST));
979         hr = IAsyncReader_SyncRead(This->pReader, BYTES_FROM_MEDIATIME(pAviSplit->CurrentChunkOffset), sizeof(pAviSplit->CurrentChunk), (BYTE *)&pAviSplit->CurrentChunk);
980     }
981
982     if (hr != S_OK)
983         return E_FAIL;
984
985     TRACE("AVI File ok\n");
986
987     return hr;
988 }
989
990 static HRESULT AVISplitter_ChangeStart(LPVOID iface)
991 {
992     FIXME("(%p)\n", iface);
993     return S_OK;
994 }
995
996 static HRESULT AVISplitter_ChangeStop(LPVOID iface)
997 {
998     FIXME("(%p)\n", iface);
999     return S_OK;
1000 }
1001
1002 static HRESULT AVISplitter_ChangeRate(LPVOID iface)
1003 {
1004     FIXME("(%p)\n", iface);
1005     return S_OK;
1006 }
1007
1008
1009 static HRESULT WINAPI AVISplitter_Seeking_QueryInterface(IMediaSeeking * iface, REFIID riid, LPVOID * ppv)
1010 {
1011     ICOM_THIS_From_IMediaSeeking(AVISplitter_OutputPin, iface);
1012
1013     return IUnknown_QueryInterface((IUnknown *)This, riid, ppv);
1014 }
1015
1016 static ULONG WINAPI AVISplitter_Seeking_AddRef(IMediaSeeking * iface)
1017 {
1018     ICOM_THIS_From_IMediaSeeking(AVISplitter_OutputPin, iface);
1019
1020     return IUnknown_AddRef((IUnknown *)This);
1021 }
1022
1023 static ULONG WINAPI AVISplitter_Seeking_Release(IMediaSeeking * iface)
1024 {
1025     ICOM_THIS_From_IMediaSeeking(AVISplitter_OutputPin, iface);
1026
1027     return IUnknown_Release((IUnknown *)This);
1028 }
1029
1030 static const IMediaSeekingVtbl AVISplitter_Seeking_Vtbl =
1031 {
1032     ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
1033     AVISplitter_Seeking_QueryInterface,
1034     AVISplitter_Seeking_AddRef,
1035     AVISplitter_Seeking_Release,
1036     MediaSeekingImpl_GetCapabilities,
1037     MediaSeekingImpl_CheckCapabilities,
1038     MediaSeekingImpl_IsFormatSupported,
1039     MediaSeekingImpl_QueryPreferredFormat,
1040     MediaSeekingImpl_GetTimeFormat,
1041     MediaSeekingImpl_IsUsingTimeFormat,
1042     MediaSeekingImpl_SetTimeFormat,
1043     MediaSeekingImpl_GetDuration,
1044     MediaSeekingImpl_GetStopPosition,
1045     MediaSeekingImpl_GetCurrentPosition,
1046     MediaSeekingImpl_ConvertTimeFormat,
1047     MediaSeekingImpl_SetPositions,
1048     MediaSeekingImpl_GetPositions,
1049     MediaSeekingImpl_GetAvailable,
1050     MediaSeekingImpl_SetRate,
1051     MediaSeekingImpl_GetRate,
1052     MediaSeekingImpl_GetPreroll
1053 };
1054
1055 HRESULT WINAPI AVISplitter_OutputPin_QueryInterface(IPin * iface, REFIID riid, LPVOID * ppv)
1056 {
1057     ICOM_THIS(AVISplitter_OutputPin, iface);
1058
1059     TRACE("(%s, %p)\n", qzdebugstr_guid(riid), ppv);
1060
1061     *ppv = NULL;
1062
1063     if (IsEqualIID(riid, &IID_IUnknown))
1064         *ppv = (LPVOID)iface;
1065     else if (IsEqualIID(riid, &IID_IPin))
1066         *ppv = (LPVOID)iface;
1067     else if (IsEqualIID(riid, &IID_IMediaSeeking))
1068         *ppv = (LPVOID)&This->mediaSeeking;
1069
1070     if (*ppv)
1071     {
1072         IUnknown_AddRef((IUnknown *)(*ppv));
1073         return S_OK;
1074     }
1075
1076     FIXME("No interface for %s!\n", qzdebugstr_guid(riid));
1077
1078     return E_NOINTERFACE;
1079 }
1080
1081 static ULONG WINAPI AVISplitter_OutputPin_Release(IPin * iface)
1082 {
1083     ICOM_THIS(AVISplitter_OutputPin, iface);
1084     
1085     TRACE("()\n");
1086     
1087     if (!InterlockedDecrement(&This->pin.pin.refCount))
1088     {
1089         DeleteMediaType(This->pmt);
1090         CoTaskMemFree(This->pmt);
1091         DeleteMediaType(&This->pin.pin.mtCurrent);
1092         CoTaskMemFree(This);
1093         return 0;
1094     }
1095     return This->pin.pin.refCount;
1096 }
1097
1098 static HRESULT WINAPI AVISplitter_OutputPin_EnumMediaTypes(IPin * iface, IEnumMediaTypes ** ppEnum)
1099 {
1100     ENUMMEDIADETAILS emd;
1101     ICOM_THIS(AVISplitter_OutputPin, iface);
1102
1103     TRACE("(%p)\n", ppEnum);
1104
1105     /* override this method to allow enumeration of your types */
1106     emd.cMediaTypes = 1;
1107     emd.pMediaTypes = This->pmt;
1108
1109     return IEnumMediaTypesImpl_Construct(&emd, ppEnum);
1110 }
1111
1112 static HRESULT AVISplitter_OutputPin_QueryAccept(LPVOID iface, const AM_MEDIA_TYPE * pmt)
1113 {
1114     ICOM_THIS(AVISplitter_OutputPin, iface);
1115
1116     TRACE("()\n");
1117     dump_AM_MEDIA_TYPE(pmt);
1118
1119     return (memcmp(This->pmt, pmt, sizeof(AM_MEDIA_TYPE)) == 0);
1120 }
1121
1122 static const IPinVtbl AVISplitter_OutputPin_Vtbl = 
1123 {
1124     ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
1125     AVISplitter_OutputPin_QueryInterface,
1126     IPinImpl_AddRef,
1127     AVISplitter_OutputPin_Release,
1128     OutputPin_Connect,
1129     OutputPin_ReceiveConnection,
1130     OutputPin_Disconnect,
1131     IPinImpl_ConnectedTo,
1132     IPinImpl_ConnectionMediaType,
1133     IPinImpl_QueryPinInfo,
1134     IPinImpl_QueryDirection,
1135     IPinImpl_QueryId,
1136     IPinImpl_QueryAccept,
1137     AVISplitter_OutputPin_EnumMediaTypes,
1138     IPinImpl_QueryInternalConnections,
1139     OutputPin_EndOfStream,
1140     OutputPin_BeginFlush,
1141     OutputPin_EndFlush,
1142     OutputPin_NewSegment
1143 };
1144
1145 static HRESULT AVISplitter_InputPin_Construct(const PIN_INFO * pPinInfo, SAMPLEPROC pSampleProc, LPVOID pUserData, QUERYACCEPTPROC pQueryAccept, LPCRITICAL_SECTION pCritSec, IPin ** ppPin)
1146 {
1147     PullPin * pPinImpl;
1148
1149     *ppPin = NULL;
1150
1151     if (pPinInfo->dir != PINDIR_INPUT)
1152     {
1153         ERR("Pin direction(%x) != PINDIR_INPUT\n", pPinInfo->dir);
1154         return E_INVALIDARG;
1155     }
1156
1157     pPinImpl = CoTaskMemAlloc(sizeof(*pPinImpl));
1158
1159     if (!pPinImpl)
1160         return E_OUTOFMEMORY;
1161
1162     if (SUCCEEDED(PullPin_Init(pPinInfo, pSampleProc, pUserData, pQueryAccept, pCritSec, pPinImpl)))
1163     {
1164         pPinImpl->pin.lpVtbl = &AVISplitter_InputPin_Vtbl;
1165         
1166         *ppPin = (IPin *)(&pPinImpl->pin.lpVtbl);
1167         return S_OK;
1168     }
1169     return E_FAIL;
1170 }
1171
1172 static HRESULT WINAPI AVISplitter_InputPin_Disconnect(IPin * iface)
1173 {
1174     HRESULT hr;
1175     ICOM_THIS(IPinImpl, iface);
1176
1177     TRACE("()\n");
1178
1179     EnterCriticalSection(This->pCritSec);
1180     {
1181         if (This->pConnectedTo)
1182         {
1183             FILTER_STATE state;
1184
1185             hr = IBaseFilter_GetState(This->pinInfo.pFilter, 0, &state);
1186
1187             if (SUCCEEDED(hr) && (state == State_Stopped))
1188             {
1189                 IPin_Release(This->pConnectedTo);
1190                 This->pConnectedTo = NULL;
1191                 hr = AVISplitter_RemoveOutputPins((AVISplitter *)This->pinInfo.pFilter);
1192             }
1193             else
1194                 hr = VFW_E_NOT_STOPPED;
1195         }
1196         else
1197             hr = S_FALSE;
1198     }
1199     LeaveCriticalSection(This->pCritSec);
1200     
1201     return hr;
1202 }
1203
1204 static const IPinVtbl AVISplitter_InputPin_Vtbl =
1205 {
1206     ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
1207     PullPin_QueryInterface,
1208     IPinImpl_AddRef,
1209     PullPin_Release,
1210     OutputPin_Connect,
1211     PullPin_ReceiveConnection,
1212     AVISplitter_InputPin_Disconnect,
1213     IPinImpl_ConnectedTo,
1214     IPinImpl_ConnectionMediaType,
1215     IPinImpl_QueryPinInfo,
1216     IPinImpl_QueryDirection,
1217     IPinImpl_QueryId,
1218     IPinImpl_QueryAccept,
1219     IPinImpl_EnumMediaTypes,
1220     IPinImpl_QueryInternalConnections,
1221     PullPin_EndOfStream,
1222     PullPin_BeginFlush,
1223     PullPin_EndFlush,
1224     PullPin_NewSegment
1225 };