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