winegstreamer: Fix deadlock when changing state.
[wine] / dlls / winegstreamer / gstdemux.c
1 /*
2  * GStreamer splitter + decoder, adapted from parser.c
3  *
4  * Copyright 2010 Maarten Lankhorst for CodeWeavers
5  * Copyright 2010 Aric Stewart for CodeWeavers
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21
22 #include "config.h"
23 #include <gst/app/gstappsink.h>
24 #include <gst/app/gstappsrc.h>
25 #include <gst/app/gstappbuffer.h>
26 #include <gst/gstutils.h>
27
28 #include "gst_private.h"
29 #include "gst_guids.h"
30
31 #include "vfwmsgs.h"
32 #include "amvideo.h"
33
34 #include "wine/unicode.h"
35 #include "wine/debug.h"
36
37 #include <assert.h>
38
39 #include "dvdmedia.h"
40 #include "mmreg.h"
41 #include "ks.h"
42 #include "initguid.h"
43 #include "ksmedia.h"
44
45 WINE_DEFAULT_DEBUG_CHANNEL(gstreamer);
46
47 typedef struct GSTOutPin GSTOutPin;
48 typedef struct GSTInPin {
49     BasePin pin;
50     IAsyncReader *pReader;
51     IMemAllocator *pAlloc;
52 } GSTInPin;
53
54 typedef struct GSTImpl {
55     BaseFilter filter;
56
57     GSTInPin pInputPin;
58     GSTOutPin **ppPins;
59     LONG cStreams;
60
61     LONGLONG filesize;
62
63     BOOL discont, initial;
64     GstElement *gstfilter;
65     GstPad *my_src, *their_sink;
66     GstBus *bus;
67     guint64 start, nextofs, nextpullofs, stop;
68     ALLOCATOR_PROPERTIES props;
69     HANDLE event, changed_ofs;
70
71     HANDLE push_thread;
72 } GSTImpl;
73
74 struct GSTOutPin {
75     BaseOutputPin pin;
76
77     GstPad *their_src;
78     GstPad *my_sink;
79     int isaud, isvid;
80     AM_MEDIA_TYPE * pmt;
81     HANDLE caps_event;
82     GstSegment *segment;
83     QualityControlImpl qcimpl;
84     SourceSeeking seek;
85 };
86
87 static const WCHAR wcsInputPinName[] = {'i','n','p','u','t',' ','p','i','n',0};
88 static const IMediaSeekingVtbl GST_Seeking_Vtbl;
89 static const IPinVtbl GST_OutputPin_Vtbl;
90 static const IPinVtbl GST_InputPin_Vtbl;
91 static const IBaseFilterVtbl GST_Vtbl;
92 static const IQualityControlVtbl GSTOutPin_QualityControl_Vtbl;
93
94 static HRESULT GST_AddPin(GSTImpl *This, const PIN_INFO *piOutput, const AM_MEDIA_TYPE *amt);
95 static HRESULT GST_RemoveOutputPins(GSTImpl *This);
96 static HRESULT WINAPI GST_ChangeCurrent(IMediaSeeking *iface);
97 static HRESULT WINAPI GST_ChangeStop(IMediaSeeking *iface);
98 static HRESULT WINAPI GST_ChangeRate(IMediaSeeking *iface);
99
100 static int amt_from_gst_caps_audio(GstCaps *caps, AM_MEDIA_TYPE *amt) {
101     WAVEFORMATEXTENSIBLE *wfe;
102     WAVEFORMATEX *wfx;
103     GstStructure *arg;
104     gint32 depth = 0, bpp = 0;
105     const char *typename;
106     arg = gst_caps_get_structure(caps, 0);
107     typename = gst_structure_get_name(arg);
108     if (!typename)
109         return 0;
110
111     wfe = CoTaskMemAlloc(sizeof(*wfe));
112     wfx = (WAVEFORMATEX*)wfe;
113     amt->majortype = MEDIATYPE_Audio;
114     amt->subtype = MEDIASUBTYPE_PCM;
115     amt->formattype = FORMAT_WaveFormatEx;
116     amt->pbFormat = (BYTE*)wfe;
117     amt->cbFormat = sizeof(*wfe);
118     amt->bFixedSizeSamples = 0;
119     amt->bTemporalCompression = 1;
120     amt->lSampleSize = 0;
121     amt->pUnk = NULL;
122
123     wfx->wFormatTag = WAVE_FORMAT_EXTENSIBLE;
124     if (!gst_structure_get_int(arg, "channels", (INT*)&wfx->nChannels))
125         return 0;
126     if (!gst_structure_get_int(arg, "rate", (INT*)&wfx->nSamplesPerSec))
127         return 0;
128     gst_structure_get_int(arg, "width", &depth);
129     gst_structure_get_int(arg, "depth", &bpp);
130     if (!depth || depth > 32 || depth % 8)
131         depth = bpp;
132     else if (!bpp)
133         bpp = depth;
134     wfe->Samples.wValidBitsPerSample = depth;
135     wfx->wBitsPerSample = bpp;
136     wfx->cbSize = sizeof(*wfe)-sizeof(*wfx);
137     switch (wfx->nChannels) {
138         case 1: wfe->dwChannelMask = KSAUDIO_SPEAKER_MONO; break;
139         case 2: wfe->dwChannelMask = KSAUDIO_SPEAKER_STEREO; break;
140         case 4: wfe->dwChannelMask = KSAUDIO_SPEAKER_SURROUND; break;
141         case 5: wfe->dwChannelMask = (KSAUDIO_SPEAKER_5POINT1 & ~SPEAKER_LOW_FREQUENCY); break;
142         case 6: wfe->dwChannelMask = KSAUDIO_SPEAKER_5POINT1; break;
143         case 8: wfe->dwChannelMask = KSAUDIO_SPEAKER_7POINT1; break;
144         default:
145         wfe->dwChannelMask = 0;
146     }
147     if (!strcmp(typename, "audio/x-raw-float")) {
148         wfe->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
149         wfx->wBitsPerSample = wfe->Samples.wValidBitsPerSample = 32;
150     } else
151         wfe->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
152     wfx->nBlockAlign = wfx->nChannels * wfx->wBitsPerSample/8;
153     wfx->nAvgBytesPerSec = wfx->nSamplesPerSec * wfx->nBlockAlign;
154     return 1;
155 }
156
157 static int amt_from_gst_caps_video(GstCaps *caps, AM_MEDIA_TYPE *amt) {
158     VIDEOINFOHEADER *vih = CoTaskMemAlloc(sizeof(*vih));
159     BITMAPINFOHEADER *bih = &vih->bmiHeader;
160     GstStructure *arg;
161     gint32 width = 0, height = 0, nom = 0, denom = 0;
162     const char *typename;
163     arg = gst_caps_get_structure(caps, 0);
164     typename = gst_structure_get_name(arg);
165     if (!typename)
166         return 0;
167     if (!gst_structure_get_int(arg, "width", &width) ||
168         !gst_structure_get_int(arg, "height", &height) ||
169         !gst_structure_get_fraction(arg, "framerate", &nom, &denom))
170         return 0;
171     amt->formattype = FORMAT_VideoInfo;
172     amt->pbFormat = (BYTE*)vih;
173     amt->cbFormat = sizeof(*vih);
174     amt->bFixedSizeSamples = amt->bTemporalCompression = 1;
175     amt->lSampleSize = 0;
176     amt->pUnk = NULL;
177     ZeroMemory(vih, sizeof(*vih));
178     amt->majortype = MEDIATYPE_Video;
179     if (!strcmp(typename, "video/x-raw-rgb")) {
180         if (!gst_structure_get_int(arg, "bpp", (INT*)&bih->biBitCount))
181             return 0;
182         switch (bih->biBitCount) {
183             case 16: amt->subtype = MEDIASUBTYPE_RGB555; break;
184             case 24: amt->subtype = MEDIASUBTYPE_RGB24; break;
185             case 32: amt->subtype = MEDIASUBTYPE_RGB32; break;
186             default:
187                 FIXME("Unknown bpp %u\n", bih->biBitCount);
188                 return 0;
189         }
190         bih->biCompression = BI_RGB;
191     } else {
192         amt->subtype = MEDIATYPE_Video;
193         if (!gst_structure_get_fourcc(arg, "format", &amt->subtype.Data1))
194             return 0;
195         switch (amt->subtype.Data1) {
196             case mmioFOURCC('I','4','2','0'):
197             case mmioFOURCC('Y','V','1','2'):
198             case mmioFOURCC('N','V','1','2'):
199             case mmioFOURCC('N','V','2','1'):
200                 bih->biBitCount = 12; break;
201             case mmioFOURCC('Y','U','Y','2'):
202             case mmioFOURCC('Y','V','Y','U'):
203                 bih->biBitCount = 16; break;
204         }
205         bih->biCompression = amt->subtype.Data1;
206     }
207     bih->biSizeImage = width * height * bih->biBitCount / 8;
208     vih->AvgTimePerFrame = 10000000;
209     vih->AvgTimePerFrame *= denom;
210     vih->AvgTimePerFrame /= nom;
211     vih->rcSource.left = 0;
212     vih->rcSource.right = width;
213     vih->rcSource.top = height;
214     vih->rcSource.bottom = 0;
215     vih->rcTarget = vih->rcSource;
216     bih->biSize = sizeof(*bih);
217     bih->biWidth = width;
218     bih->biHeight = height;
219     bih->biPlanes = 1;
220     return 1;
221 }
222
223 static gboolean accept_caps_sink(GstPad *pad, GstCaps *caps) {
224     GSTOutPin *pin = gst_pad_get_element_private(pad);
225     AM_MEDIA_TYPE amt;
226     GstStructure *arg;
227     const char *typename;
228     int ret;
229     arg = gst_caps_get_structure(caps, 0);
230     typename = gst_structure_get_name(arg);
231     if (!strcmp(typename, "audio/x-raw-int") ||
232         !strcmp(typename, "audio/x-raw-float")) {
233         if (!pin->isaud) {
234             ERR("Setting audio caps on non-audio pad?\n");
235             return 0;
236         }
237         ret = amt_from_gst_caps_audio(caps, &amt);
238         FreeMediaType(&amt);
239         TRACE("+%i\n", ret);
240         return ret;
241     } else if (!strcmp(typename, "video/x-raw-rgb")
242                || !strcmp(typename, "video/x-raw-yuv")) {
243         if (!pin->isvid) {
244             ERR("Setting video caps on non-video pad?\n");
245             return 0;
246         }
247         ret = amt_from_gst_caps_video(caps, &amt);
248         FreeMediaType(&amt);
249         TRACE("-%i\n", ret);
250         return ret;
251     } else {
252         FIXME("Unhandled type \"%s\"\n", typename);
253         return 0;
254     }
255 }
256
257 static gboolean setcaps_sink(GstPad *pad, GstCaps *caps) {
258     GSTOutPin *pin = gst_pad_get_element_private(pad);
259     GSTImpl *This = (GSTImpl *)pin->pin.pin.pinInfo.pFilter;
260     AM_MEDIA_TYPE amt;
261     GstStructure *arg;
262     const char *typename;
263     int ret;
264     arg = gst_caps_get_structure(caps, 0);
265     typename = gst_structure_get_name(arg);
266     if (!strcmp(typename, "audio/x-raw-int") ||
267         !strcmp(typename, "audio/x-raw-float")) {
268         if (!pin->isaud) {
269             ERR("Setting audio caps on non-audio pad?\n");
270             return 0;
271         }
272         ret = amt_from_gst_caps_audio(caps, &amt);
273     } else if (!strcmp(typename, "video/x-raw-rgb")
274                || !strcmp(typename, "video/x-raw-yuv")) {
275         if (!pin->isvid) {
276             ERR("Setting video caps on non-video pad?\n");
277             return 0;
278         }
279         ret = amt_from_gst_caps_video(caps, &amt);
280         if (ret)
281             This->props.cbBuffer = max(This->props.cbBuffer, ((VIDEOINFOHEADER*)amt.pbFormat)->bmiHeader.biSizeImage);
282     } else {
283         FIXME("Unhandled type \"%s\"\n", typename);
284         return 0;
285     }
286     TRACE("Linking returned %i for %s\n", ret, typename);
287     if (!ret)
288         return 0;
289     FreeMediaType(pin->pmt);
290     *pin->pmt = amt;
291     return 1;
292 }
293
294 static gboolean gst_base_src_perform_seek(GSTImpl *This, GstEvent *event)
295 {
296     gboolean res = TRUE;
297     gdouble rate;
298     GstFormat seek_format;
299     GstSeekFlags flags;
300     GstSeekType cur_type, stop_type;
301     gint64 cur, stop;
302     gboolean flush;
303     guint32 seqnum;
304     GstEvent *tevent;
305     BOOL thread = !!This->push_thread;
306
307     gst_event_parse_seek(event, &rate, &seek_format, &flags,
308                          &cur_type, &cur, &stop_type, &stop);
309
310     if (seek_format != GST_FORMAT_BYTES) {
311         FIXME("Not handling other format %i\n", seek_format);
312         return 0;
313     }
314
315     flush = flags & GST_SEEK_FLAG_FLUSH;
316     seqnum = gst_event_get_seqnum(event);
317
318     /* send flush start */
319     if (flush) {
320         tevent = gst_event_new_flush_start();
321         gst_event_set_seqnum(tevent, seqnum);
322         gst_pad_push_event(This->my_src, tevent);
323         if (This->pInputPin.pReader)
324             IAsyncReader_BeginFlush(This->pInputPin.pReader);
325         if (thread)
326             gst_pad_activate_push(This->my_src, 0);
327     }
328
329     TRACE("++++++++++++++++ perform byte seek ------------------\n");
330
331     /* and prepare to continue streaming */
332     if (flush) {
333         tevent = gst_event_new_flush_stop();
334         gst_event_set_seqnum(tevent, seqnum);
335         gst_pad_push_event(This->my_src, tevent);
336         if (This->pInputPin.pReader)
337             IAsyncReader_EndFlush(This->pInputPin.pReader);
338         if (thread)
339             gst_pad_activate_push(This->my_src, 1);
340     }
341
342     return res;
343 }
344
345 static gboolean event_src(GstPad *pad, GstEvent *event) {
346     GSTImpl *This = gst_pad_get_element_private(pad);
347     switch (event->type) {
348         case GST_EVENT_SEEK:
349             return gst_base_src_perform_seek(This, event);
350         case GST_EVENT_FLUSH_START:
351             EnterCriticalSection(&This->filter.csFilter);
352             if (This->pInputPin.pReader)
353                 IAsyncReader_BeginFlush(This->pInputPin.pReader);
354             LeaveCriticalSection(&This->filter.csFilter);
355             break;
356         case GST_EVENT_FLUSH_STOP:
357             EnterCriticalSection(&This->filter.csFilter);
358             if (This->pInputPin.pReader)
359                 IAsyncReader_EndFlush(This->pInputPin.pReader);
360             LeaveCriticalSection(&This->filter.csFilter);
361             break;
362         default:
363             FIXME("%p stub\n", event);
364             return gst_pad_event_default(pad, event);
365     }
366     return 1;
367 }
368
369 static gboolean event_sink(GstPad *pad, GstEvent *event) {
370     GSTOutPin *pin = gst_pad_get_element_private(pad);
371     switch (event->type) {
372         case GST_EVENT_NEWSEGMENT: {
373             gboolean update;
374             gdouble rate, applied_rate;
375             GstFormat format;
376             gint64 start, stop, pos;
377             gst_event_parse_new_segment_full(event, &update, &rate, &applied_rate, &format, &start, &stop, &pos);
378             if (format != GST_FORMAT_TIME) {
379                 FIXME("Ignoring new segment because of format %i\n", format);
380                 return 1;
381             }
382             gst_segment_set_newsegment_full(pin->segment, update, rate, applied_rate, format, start, stop, pos);
383             pos /= 100;
384             if (stop > 0)
385                 stop /= 100;
386             if (pin->pin.pin.pConnectedTo)
387                 IPin_NewSegment(pin->pin.pin.pConnectedTo, pos, stop, rate*applied_rate);
388             return 1;
389         }
390         case GST_EVENT_EOS:
391             if (pin->pin.pin.pConnectedTo)
392                 IPin_EndOfStream(pin->pin.pin.pConnectedTo);
393             return 1;
394         case GST_EVENT_FLUSH_START:
395             if (pin->pin.pin.pConnectedTo)
396                 IPin_BeginFlush(pin->pin.pin.pConnectedTo);
397             return 1;
398         case GST_EVENT_FLUSH_STOP:
399             gst_segment_init(pin->segment, GST_FORMAT_TIME);
400             if (pin->pin.pin.pConnectedTo)
401                 IPin_EndFlush(pin->pin.pin.pConnectedTo);
402             return 1;
403         default:
404             FIXME("%p stub %s\n", event, gst_event_type_get_name(event->type));
405             return gst_pad_event_default(pad, event);
406     }
407 }
408
409 static void release_sample(void *data) {
410     ULONG ret;
411     ret = IMediaSample_Release((IMediaSample *)data);
412     TRACE("Releasing %p returns %u\n", data, ret);
413 }
414
415 static DWORD CALLBACK push_data(LPVOID iface) {
416     LONGLONG maxlen, curlen;
417     GSTImpl *This = iface;
418     IMediaSample *buf;
419     DWORD_PTR user;
420     HRESULT hr;
421
422     IAsyncReader_Length(This->pInputPin.pReader, &maxlen, &curlen);
423     TRACE("Starting..\n");
424     for (;;) {
425         REFERENCE_TIME tStart, tStop;
426         ULONG len;
427         GstBuffer *gstbuf;
428         BYTE *data;
429         int ret;
430
431         hr = IMemAllocator_GetBuffer(This->pInputPin.pAlloc, &buf, NULL, NULL, 0);
432         if (FAILED(hr))
433             break;
434
435         if (This->nextofs >= maxlen)
436             break;
437         len = IMediaSample_GetSize(buf);
438         if (This->nextofs + len > maxlen)
439             len = maxlen - This->nextofs;
440
441         tStart = MEDIATIME_FROM_BYTES(This->nextofs);
442         tStop = tStart + MEDIATIME_FROM_BYTES(len);
443         IMediaSample_SetTime(buf, &tStart, &tStop);
444
445         hr = IAsyncReader_Request(This->pInputPin.pReader, buf, 0);
446         if (FAILED(hr)) {
447             IMediaSample_Release(buf);
448             break;
449         }
450         This->nextofs += len;
451         hr = IAsyncReader_WaitForNext(This->pInputPin.pReader, -1, &buf, &user);
452         if (FAILED(hr) || !buf) {
453             if (buf)
454                 IMediaSample_Release(buf);
455             break;
456         }
457
458         IMediaSample_GetPointer(buf, &data);
459         gstbuf = gst_app_buffer_new(data, IMediaSample_GetActualDataLength(buf), release_sample, buf);
460         if (!gstbuf) {
461             IMediaSample_Release(buf);
462             break;
463         }
464         gstbuf->duration = gstbuf->timestamp = -1;
465         ret = gst_pad_push(This->my_src, gstbuf);
466         if (ret >= 0)
467             hr = S_OK;
468         else
469             ERR("Sending returned: %i\n", ret);
470         if (ret == GST_FLOW_ERROR)
471             hr = E_FAIL;
472         else if (ret == GST_FLOW_WRONG_STATE)
473             hr = VFW_E_WRONG_STATE;
474         else if (ret == GST_FLOW_RESEND)
475             hr = S_FALSE;
476         if (hr != S_OK)
477             break;
478     }
479
480     gst_pad_push_event(This->my_src, gst_event_new_eos());
481
482     TRACE("Almost stopping.. %08x\n", hr);
483     do {
484         IAsyncReader_WaitForNext(This->pInputPin.pReader, 0, &buf, &user);
485         if (buf)
486             IMediaSample_Release(buf);
487     } while (buf);
488
489     TRACE("Stopping.. %08x\n", hr);
490     return 0;
491 }
492
493 static HRESULT WINAPI GST_OutPin_QueryAccept(IPin *iface, const AM_MEDIA_TYPE *pmt) {
494     GSTOutPin *pin = (GSTOutPin*)iface;
495     FIXME("stub %p\n", pin);
496     return S_OK;
497 }
498
499 static GstFlowReturn got_data_sink(GstPad *pad, GstBuffer *buf) {
500     GSTOutPin *pin = gst_pad_get_element_private(pad);
501     GSTImpl *This = (GSTImpl *)pin->pin.pin.pinInfo.pFilter;
502     IMediaSample *sample;
503     HRESULT hr;
504     BOOL freeSamp = FALSE;
505
506     if (This->initial) {
507         gst_buffer_unref(buf);
508         FIXME("Triggering %p %p\n", pad, pin->caps_event);
509         SetEvent(pin->caps_event);
510         return GST_FLOW_NOT_LINKED;
511     }
512
513     if (GST_IS_APP_BUFFER(buf)) {
514         sample = GST_APP_BUFFER(buf)->priv;
515         TRACE("Pushing buffer\n");
516     } else if (buf->parent && GST_IS_APP_BUFFER(buf->parent)) {
517         sample = GST_APP_BUFFER(buf->parent)->priv;
518         TRACE("Pushing sub-buffer\n");
519     } else {
520         BYTE *ptr = NULL;
521         hr = BaseOutputPinImpl_GetDeliveryBuffer(&pin->pin, &sample, NULL, NULL, 0);
522         freeSamp = TRUE;
523         if (hr == VFW_E_NOT_CONNECTED) {
524             gst_buffer_unref(buf);
525             return GST_FLOW_NOT_LINKED;
526         }
527         if (FAILED(hr)) {
528             gst_buffer_unref(buf);
529             ERR("Didn't get a GST_APP_BUFFER, and could not get a delivery buffer (%x), returning GST_FLOW_WRONG_STATE\n", hr);
530             return GST_FLOW_WRONG_STATE;
531         }
532         FIXME("Did not get a GST_APP_BUFFER, creating a sample\n");
533         IMediaSample_SetActualDataLength(sample, GST_BUFFER_SIZE(buf));
534         IMediaSample_GetPointer(sample, &ptr);
535         memcpy(ptr, GST_BUFFER_DATA(buf), GST_BUFFER_SIZE(buf));
536     }
537
538     if (GST_BUFFER_TIMESTAMP_IS_VALID(buf)) {
539         REFERENCE_TIME rtStart = gst_segment_to_running_time(pin->segment, GST_FORMAT_TIME, buf->timestamp);
540         if (rtStart >= 0)
541             rtStart /= 100;
542
543         if (GST_BUFFER_DURATION_IS_VALID(buf)) {
544             REFERENCE_TIME tStart = buf->timestamp / 100;
545             REFERENCE_TIME tStop = (buf->timestamp + buf->duration) / 100;
546             REFERENCE_TIME rtStop;
547             rtStop = gst_segment_to_running_time(pin->segment, GST_FORMAT_TIME, buf->timestamp + buf->duration);
548             if (rtStop >= 0)
549                 rtStop /= 100;
550             TRACE("Current time on %p: %i to %i ms\n", pin, (int)(rtStart / 10000), (int)(rtStop / 10000));
551             IMediaSample_SetTime(sample, &rtStart, rtStop >= 0 ? &rtStop : NULL);
552             IMediaSample_SetMediaTime(sample, &tStart, &tStop);
553         } else {
554             IMediaSample_SetTime(sample, rtStart >= 0 ? &rtStart : NULL, NULL);
555             IMediaSample_SetMediaTime(sample, NULL, NULL);
556         }
557     } else {
558         IMediaSample_SetTime(sample, NULL, NULL);
559         IMediaSample_SetMediaTime(sample, NULL, NULL);
560     }
561
562     IMediaSample_SetDiscontinuity(sample, GST_BUFFER_FLAG_IS_SET(buf, GST_BUFFER_FLAG_DISCONT));
563     IMediaSample_SetPreroll(sample, GST_BUFFER_FLAG_IS_SET(buf, GST_BUFFER_FLAG_PREROLL));
564     IMediaSample_SetSyncPoint(sample, !GST_BUFFER_FLAG_IS_SET(buf, GST_BUFFER_FLAG_DELTA_UNIT));
565
566     if (!pin->pin.pin.pConnectedTo)
567         hr = VFW_E_NOT_CONNECTED;
568     else
569         hr = IMemInputPin_Receive(pin->pin.pMemInputPin, sample);
570     TRACE("sending sample: %08x\n", hr);
571     gst_buffer_unref(buf);
572     if (freeSamp)
573         IMediaSample_Release(sample);
574     if (hr == VFW_E_NOT_CONNECTED)
575         return GST_FLOW_NOT_LINKED;
576     else if (FAILED(hr))
577         return GST_FLOW_WRONG_STATE;
578     if (hr != S_OK)
579         return GST_FLOW_RESEND;
580     return GST_FLOW_OK;
581 }
582
583 static GstFlowReturn request_buffer_sink(GstPad *pad, guint64 ofs, guint size, GstCaps *caps, GstBuffer **buf) {
584     GSTOutPin *pin = gst_pad_get_element_private(pad);
585     GSTImpl *This = (GSTImpl *)pin->pin.pin.pinInfo.pFilter;
586     IMediaSample *sample;
587     BYTE *ptr;
588     HRESULT hr;
589
590     TRACE("Requesting buffer\n");
591     if (This->initial) {
592         int ret;
593         ret = setcaps_sink(pad, caps);
594         if (!ret)
595             return GST_FLOW_NOT_NEGOTIATED;
596         *buf = gst_buffer_new_and_alloc(size);
597         return GST_FLOW_OK;
598     }
599
600     if (caps && caps != GST_PAD_CAPS(pad))
601         if (!setcaps_sink(pad, caps))
602             return GST_FLOW_NOT_NEGOTIATED;
603
604     hr = BaseOutputPinImpl_GetDeliveryBuffer(&pin->pin, &sample, NULL, NULL, 0);
605     if (hr == VFW_E_NOT_CONNECTED)
606         return GST_FLOW_NOT_LINKED;
607     if (FAILED(hr)) {
608         ERR("Could not get output buffer: %08x\n", hr);
609         *buf = NULL;
610         return GST_FLOW_WRONG_STATE;
611     }
612     IMediaSample_SetActualDataLength(sample, size);
613     IMediaSample_GetPointer(sample, &ptr);
614     *buf = gst_app_buffer_new(ptr, size, release_sample, sample);
615     if (!*buf) {
616         IMediaSample_Release(sample);
617         ERR("Out of memory\n");
618         return GST_FLOW_ERROR;
619     }
620     gst_buffer_set_caps(*buf, caps);
621     return GST_FLOW_OK;
622 }
623
624 static GstFlowReturn request_buffer_src(GstPad *pad, guint64 ofs, guint len, GstBuffer **buf) {
625     GSTImpl *This = gst_pad_get_element_private(pad);
626     int ret;
627
628     *buf = NULL;
629     TRACE("Requesting %s %u\n", wine_dbgstr_longlong(ofs), len);
630     if (ofs == (guint64)-1)
631         ofs = This->nextpullofs;
632     if (ofs >= This->filesize) {
633         WARN("Reading past eof: %s, %u\n", wine_dbgstr_longlong(ofs), len);
634         return GST_FLOW_UNEXPECTED;
635     }
636     if (len + ofs > This->filesize)
637         len = This->filesize - ofs;
638     This->nextpullofs = ofs + len;
639
640     ret = gst_pad_alloc_buffer(This->my_src, ofs, len, NULL, buf);
641     if (ret >= 0) {
642         HRESULT hr;
643         hr = IAsyncReader_SyncRead(This->pInputPin.pReader, ofs, len, GST_BUFFER_DATA(*buf));
644         if (FAILED(hr)) {
645             ERR("Returned %08x\n", hr);
646             return GST_FLOW_ERROR;
647         }
648     }
649     return ret;
650 }
651
652 static DWORD CALLBACK push_data_init(LPVOID iface) {
653     GSTImpl *This = iface;
654     DWORD64 ofs = 0;
655
656     TRACE("Starting..\n");
657     for (;;) {
658         GstBuffer *buf;
659         GstFlowReturn ret = request_buffer_src(This->my_src, ofs, 4096, &buf);
660         if (ret < 0) {
661             ERR("Obtaining buffer returned: %i\n", ret);
662             break;
663         }
664         ret = gst_pad_push(This->my_src, buf);
665         ofs += 4096;
666         if (ret)
667             TRACE("Sending returned: %i\n", ret);
668         if (ret < 0)
669             break;
670     }
671     TRACE("Stopping..\n");
672     return 0;
673 }
674
675 static void removed_decoded_pad(GstElement *bin, GstPad *pad, GSTImpl *This) {
676     int x;
677     GSTOutPin *pin;
678
679     EnterCriticalSection(&This->filter.csFilter);
680     for (x = 0; x < This->cStreams; ++x) {
681         if (This->ppPins[x]->their_src == pad)
682             break;
683     }
684     if (x == This->cStreams)
685         goto out;
686     pin = This->ppPins[x];
687     gst_pad_unlink(pin->their_src, pin->my_sink);
688     gst_object_unref(pin->their_src);
689     pin->their_src = NULL;
690 out:
691     TRACE("Removed %i/%i\n", x, This->cStreams);
692     LeaveCriticalSection(&This->filter.csFilter);
693 }
694
695 static void init_new_decoded_pad(GstElement *bin, GstPad *pad, gboolean last, GSTImpl *This) {
696     HRESULT hr;
697     PIN_INFO piOutput;
698     const char *typename;
699     char *name;
700     AM_MEDIA_TYPE amt = { };
701     GstCaps *caps;
702     GstStructure *arg;
703     GstPad *mypad;
704     GSTOutPin *pin;
705     int ret;
706     int isvid = 0, isaud = 0;
707
708     piOutput.dir = PINDIR_OUTPUT;
709     piOutput.pFilter = (IBaseFilter *)This;
710     name = gst_pad_get_name(pad);
711     MultiByteToWideChar(CP_UNIXCP, 0, name, -1, piOutput.achName, sizeof(piOutput.achName) / sizeof(piOutput.achName[0]) - 1);
712     TRACE("Name: %s\n", name);
713     g_free(name);
714     piOutput.achName[sizeof(piOutput.achName) / sizeof(piOutput.achName[0]) - 1] = 0;
715
716     caps = gst_pad_get_caps_reffed(pad);
717     arg = gst_caps_get_structure(caps, 0);
718     typename = gst_structure_get_name(arg);
719
720     mypad = gst_pad_new(NULL, GST_PAD_SINK);
721     gst_pad_set_chain_function(mypad, got_data_sink);
722     gst_pad_set_event_function(mypad, event_sink);
723     gst_pad_set_bufferalloc_function(mypad, request_buffer_sink);
724     gst_pad_set_acceptcaps_function(mypad, accept_caps_sink);
725     gst_pad_set_acceptcaps_function(mypad, setcaps_sink);
726
727     if (!strcmp(typename, "audio/x-raw-int") ||
728         !strcmp(typename, "audio/x-raw-float")) {
729         isaud = 1;
730     } else if (!strcmp(typename, "video/x-raw-rgb")
731                || !strcmp(typename, "video/x-raw-yuv")) {
732         isvid = 1;
733     } else {
734         FIXME("Unknown type \'%s\'\n", typename);
735         return;
736     }
737     GST_PAD_CAPS(mypad) = GST_CAPS_ANY;
738     hr = GST_AddPin(This, &piOutput, &amt);
739     if (FAILED(hr)) {
740         ERR("%08x\n", hr);
741         return;
742     }
743     pin = This->ppPins[This->cStreams - 1];
744     gst_pad_set_element_private(mypad, pin);
745     pin->my_sink = mypad;
746     pin->isaud = isaud;
747     pin->isvid = isvid;
748
749     gst_segment_init(pin->segment, GST_FORMAT_TIME);
750     ret = gst_pad_link(pad, mypad);
751     gst_pad_activate_push(mypad, 1);
752     FIXME("Linking: %i\n", ret);
753     if (ret >= 0) {
754         pin->their_src = pad;
755         gst_object_ref(pin->their_src);
756     }
757 }
758
759 static void existing_new_pad(GstElement *bin, GstPad *pad, gboolean last, GSTImpl *This) {
760     int x;
761
762     if (gst_pad_is_linked(pad))
763         return;
764
765     /* Still holding our own lock */
766     if (This->initial) {
767         init_new_decoded_pad(bin, pad, last, This);
768         return;
769     }
770
771     EnterCriticalSection(&This->filter.csFilter);
772     for (x = 0; x < This->cStreams; ++x) {
773         GSTOutPin *pin = This->ppPins[x];
774         if (!pin->their_src) {
775             gst_segment_init(pin->segment, GST_FORMAT_TIME);
776             if (gst_pad_link(pad, pin->my_sink) >= 0) {
777                 pin->their_src = pad;
778                 gst_object_ref(pin->their_src);
779                 TRACE("Relinked\n");
780                 LeaveCriticalSection(&This->filter.csFilter);
781                 return;
782             }
783         }
784     }
785     init_new_decoded_pad(bin, pad, last, This);
786     LeaveCriticalSection(&This->filter.csFilter);
787 }
788
789 static gboolean check_get_range(GstPad *pad) {
790     return 1;
791 }
792
793 static gboolean query_function(GstPad *pad, GstQuery *query) {
794     GSTImpl *This = gst_pad_get_element_private(pad);
795     GstFormat format;
796     int ret;
797     LONGLONG duration;
798
799     switch (GST_QUERY_TYPE(query)) {
800         case GST_QUERY_DURATION:
801             gst_query_parse_duration (query, &format, NULL);
802             if (format == GST_FORMAT_PERCENT) {
803                 gst_query_set_duration (query, GST_FORMAT_PERCENT, GST_FORMAT_PERCENT_MAX);
804                 return 1;
805             }
806             ret = gst_pad_query_convert (pad, GST_FORMAT_BYTES, This->filesize, &format, &duration);
807             gst_query_set_duration(query, format, duration);
808             return ret;
809         case GST_QUERY_SEEKING:
810             gst_query_parse_seeking (query, &format, NULL, NULL, NULL);
811             TRACE("Seeking %i %i\n", format, GST_FORMAT_BYTES);
812             if (format != GST_FORMAT_BYTES)
813                 return 0;
814             gst_query_set_seeking(query, GST_FORMAT_BYTES, 1, 0, This->filesize);
815             return 1;
816         default:
817             FIXME("Unhandled query type %i\n", GST_QUERY_TYPE(query));
818         case GST_QUERY_URI:
819             return 1;
820     }
821 }
822
823 static gboolean activate_push(GstPad *pad, gboolean activate) {
824     GSTImpl *This = gst_pad_get_element_private(pad);
825     EnterCriticalSection(&This->filter.csFilter);
826     if (!activate) {
827         TRACE("Deactivating\n");
828         if (This->push_thread) {
829             WaitForSingleObject(This->push_thread, -1);
830             CloseHandle(This->push_thread);
831             This->push_thread = NULL;
832         }
833     } else if (!This->push_thread) {
834         TRACE("Activating\n");
835         if (This->initial)
836             This->push_thread = CreateThread(NULL, 0, push_data_init, This, 0, NULL);
837         else
838             This->push_thread = CreateThread(NULL, 0, push_data, This, 0, NULL);
839     }
840     LeaveCriticalSection(&This->filter.csFilter);
841     return 1;
842 }
843
844 static void no_more_pads(GstElement *decodebin, GSTImpl *This) {
845     FIXME("Done\n");
846     SetEvent(This->event);
847 }
848
849 typedef enum {
850   GST_AUTOPLUG_SELECT_TRY,
851   GST_AUTOPLUG_SELECT_EXPOSE,
852   GST_AUTOPLUG_SELECT_SKIP
853 } GstAutoplugSelectResult;
854
855 static GstAutoplugSelectResult autoplug_blacklist(GstElement *bin, GstPad *pad, GstCaps *caps, GstElementFactory *fact, GSTImpl *This) {
856     const char *name = gst_element_factory_get_longname(fact);
857
858     if (strstr(name, "Player protection")) {
859         WARN("Blacklisted a/52 decoder because it only works in Totem\n");
860         return GST_AUTOPLUG_SELECT_SKIP;
861     }
862     if (!strcmp(name, "Fluendo Hardware Accelerated Video Decoder")) {
863         WARN("Disabled video acceleration since it breaks in wine\n");
864         return GST_AUTOPLUG_SELECT_SKIP;
865     }
866     TRACE("using \"%s\"\n", name);
867     return GST_AUTOPLUG_SELECT_TRY;
868 }
869
870 static GstBusSyncReply watch_bus(GstBus *bus, GstMessage *msg, gpointer data) {
871     GSTImpl *This = data;
872     GError *err = NULL;
873     gchar *dbg_info = NULL;
874     if (GST_MESSAGE_TYPE(msg) & GST_MESSAGE_ERROR) {
875         gst_message_parse_error(msg, &err, &dbg_info);
876         FIXME("%s: %s\n", GST_OBJECT_NAME(msg->src), err->message);
877         WARN("%s\n", dbg_info);
878         SetEvent(This->event);
879     } else if (GST_MESSAGE_TYPE(msg) & GST_MESSAGE_WARNING) {
880         gst_message_parse_warning(msg, &err, &dbg_info);
881         WARN("%s: %s\n", GST_OBJECT_NAME(msg->src), err->message);
882         WARN("%s\n", dbg_info);
883     }
884     if (err)
885         g_error_free(err);
886     g_free(dbg_info);
887     return GST_BUS_DROP;
888 }
889
890 static void unknown_type(GstElement *bin, GstPad *pad, GstCaps *caps, GSTImpl *This) {
891     gchar *strcaps = gst_caps_to_string(caps);
892     FIXME("Could not find a filter for caps: %s\n", strcaps);
893     g_free(strcaps);
894 }
895
896 static HRESULT GST_Connect(GSTInPin *pPin, IPin *pConnectPin, ALLOCATOR_PROPERTIES *props) {
897     GSTImpl *This = (GSTImpl*)pPin->pin.pinInfo.pFilter;
898     HRESULT hr;
899     int ret, i;
900     LONGLONG avail, duration;
901     GstFormat format = GST_FORMAT_TIME;
902     GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE(
903         "quartz_src",
904         GST_PAD_SRC,
905         GST_PAD_ALWAYS,
906         GST_STATIC_CAPS_ANY);
907
908     TRACE("%p %p %p\n", pPin, pConnectPin, props);
909     This->props = *props;
910     IAsyncReader_Length(pPin->pReader, &This->filesize, &avail);
911
912     if (!This->bus) {
913         This->bus = gst_bus_new();
914         gst_bus_set_sync_handler(This->bus, watch_bus, This);
915     }
916
917     This->gstfilter = gst_element_factory_make("decodebin2", NULL);
918     if (!This->gstfilter) {
919         FIXME("Could not make source filter, are gstreamer-plugins-* installed for %u bits?\n",
920               8 * (int)sizeof(void*));
921         return E_FAIL;
922     }
923     gst_element_set_bus(This->gstfilter, This->bus);
924     g_signal_connect(This->gstfilter, "new-decoded-pad", G_CALLBACK(existing_new_pad), This);
925     g_signal_connect(This->gstfilter, "pad-removed", G_CALLBACK(removed_decoded_pad), This);
926     g_signal_connect(This->gstfilter, "autoplug-select", G_CALLBACK(autoplug_blacklist), This);
927     g_signal_connect(This->gstfilter, "unknown-type", G_CALLBACK(unknown_type), This);
928
929     This->my_src = gst_pad_new_from_static_template(&src_template, "quartz-src");
930     gst_pad_set_getrange_function(This->my_src, request_buffer_src);
931     gst_pad_set_checkgetrange_function(This->my_src, check_get_range);
932     gst_pad_set_query_function(This->my_src, query_function);
933     gst_pad_set_activatepush_function(This->my_src, activate_push);
934     gst_pad_set_event_function(This->my_src, event_src);
935     gst_pad_set_element_private (This->my_src, This);
936     This->their_sink = gst_element_get_static_pad(This->gstfilter, "sink");
937
938     g_signal_connect(This->gstfilter, "no-more-pads", G_CALLBACK(no_more_pads), This);
939     ret = gst_pad_link(This->my_src, This->their_sink);
940     gst_object_unref(This->their_sink);
941     if (ret < 0) {
942         ERR("Returns: %i\n", ret);
943         return E_FAIL;
944     }
945     This->start = This->nextofs = This->nextpullofs = 0;
946     This->stop = This->filesize;
947
948     /* Add initial pins */
949     This->initial = This->discont = 1;
950     ResetEvent(This->event);
951     gst_element_set_state(This->gstfilter, GST_STATE_PLAYING);
952     gst_pad_set_active(This->my_src, 1);
953     WaitForSingleObject(This->event, -1);
954     gst_element_get_state(This->gstfilter, NULL, NULL, -1);
955
956     if (ret < 0) {
957         WARN("Ret: %i\n", ret);
958         hr = E_FAIL;
959     } else if (!This->cStreams) {
960         FIXME("Gstreamer could not find any streams\n");
961         hr = E_FAIL;
962     } else {
963         gst_pad_query_duration(This->ppPins[0]->their_src, &format, &duration);
964         for (i = 0; i < This->cStreams; ++i) {
965             This->ppPins[i]->seek.llDuration = This->ppPins[i]->seek.llStop = duration / 100;
966             WaitForSingleObject(This->ppPins[i]->caps_event, -1);
967         }
968         hr = S_OK;
969     }
970     *props = This->props;
971     gst_element_set_state(This->gstfilter, GST_STATE_READY);
972     gst_element_get_state(This->gstfilter, NULL, NULL, -1);
973
974     This->initial = 0;
975     This->nextofs = This->nextpullofs = 0;
976     return hr;
977 }
978
979 static inline GSTOutPin *impl_from_IMediaSeeking( IMediaSeeking *iface ) {
980     return (GSTOutPin *)((char*)iface - FIELD_OFFSET(GSTOutPin, seek.lpVtbl));
981 }
982
983 static IPin* WINAPI GST_GetPin(BaseFilter *iface, int pos)
984 {
985     GSTImpl *This = (GSTImpl *)iface;
986     TRACE("Asking for pos %x\n", pos);
987
988     if (pos > This->cStreams || pos < 0)
989         return NULL;
990     if (!pos)
991     {
992         IPin_AddRef((IPin*)&This->pInputPin);
993         return (IPin*)&This->pInputPin;
994     }
995     else
996     {
997         IPin_AddRef((IPin*)This->ppPins[pos - 1]);
998         return (IPin*)This->ppPins[pos - 1];
999     }
1000 }
1001
1002 static LONG WINAPI GST_GetPinCount(BaseFilter *iface)
1003 {
1004     GSTImpl *This = (GSTImpl *)iface;
1005     return (This->cStreams + 1);
1006 }
1007
1008 static const BaseFilterFuncTable BaseFuncTable = {
1009     GST_GetPin,
1010     GST_GetPinCount
1011 };
1012
1013 IUnknown * CALLBACK Gstreamer_Splitter_create(IUnknown *punkout, HRESULT *phr) {
1014     IUnknown *obj = NULL;
1015     PIN_INFO *piInput;
1016     GSTImpl *This;
1017
1018     if (!Gstreamer_init())
1019     {
1020         *phr = E_FAIL;
1021         return NULL;
1022     }
1023
1024     This = CoTaskMemAlloc(sizeof(*This));
1025     obj = (IUnknown*)This;
1026     if (!This)
1027     {
1028         *phr = E_OUTOFMEMORY;
1029         return NULL;
1030     }
1031
1032     BaseFilter_Init(&This->filter, &GST_Vtbl, &CLSID_Gstreamer_Splitter, (DWORD_PTR)(__FILE__ ": GSTImpl.csFilter"), &BaseFuncTable);
1033
1034     This->cStreams = 0;
1035     This->ppPins = NULL;
1036     This->push_thread = NULL;
1037     This->event = CreateEventW(NULL, 0, 0, NULL);
1038     This->bus = NULL;
1039
1040     piInput = &This->pInputPin.pin.pinInfo;
1041     piInput->dir = PINDIR_INPUT;
1042     piInput->pFilter = (IBaseFilter *)This;
1043     lstrcpynW(piInput->achName, wcsInputPinName, sizeof(piInput->achName) / sizeof(piInput->achName[0]));
1044     This->pInputPin.pin.lpVtbl = &GST_InputPin_Vtbl;
1045     This->pInputPin.pin.refCount = 1;
1046     This->pInputPin.pin.pConnectedTo = NULL;
1047     This->pInputPin.pin.pCritSec = &This->filter.csFilter;
1048     ZeroMemory(&This->pInputPin.pin.mtCurrent, sizeof(AM_MEDIA_TYPE));
1049     *phr = S_OK;
1050     return obj;
1051 }
1052
1053 static void GST_Destroy(GSTImpl *This) {
1054     IPin *connected = NULL;
1055     ULONG pinref;
1056
1057     TRACE("Destroying\n");
1058
1059     CloseHandle(This->event);
1060
1061     /* Don't need to clean up output pins, disconnecting input pin will do that */
1062     IPin_ConnectedTo((IPin *)&This->pInputPin, &connected);
1063     if (connected) {
1064         assert(IPin_Disconnect(connected) == S_OK);
1065         IPin_Release(connected);
1066         assert(IPin_Disconnect((IPin *)&This->pInputPin) == S_OK);
1067     }
1068     pinref = IPin_Release((IPin *)&This->pInputPin);
1069     if (pinref) {
1070         /* Valgrind could find this, if I kill it here */
1071         ERR("pinref should be null, is %u, destroying anyway\n", pinref);
1072         assert((LONG)pinref > 0);
1073
1074         while (pinref)
1075             pinref = IPin_Release((IPin *)&This->pInputPin);
1076     }
1077     if (This->bus) {
1078         gst_bus_set_sync_handler(This->bus, NULL, NULL);
1079         gst_object_unref(This->bus);
1080     }
1081     CoTaskMemFree(This);
1082 }
1083
1084 static HRESULT WINAPI GST_QueryInterface(IBaseFilter *iface, REFIID riid, LPVOID *ppv) {
1085     GSTImpl *This = (GSTImpl *)iface;
1086     TRACE("(%s, %p)\n", debugstr_guid(riid), ppv);
1087
1088     *ppv = NULL;
1089
1090     if (IsEqualIID(riid, &IID_IUnknown))
1091         *ppv = This;
1092     else if (IsEqualIID(riid, &IID_IPersist))
1093         *ppv = This;
1094     else if (IsEqualIID(riid, &IID_IMediaFilter))
1095         *ppv = This;
1096     else if (IsEqualIID(riid, &IID_IBaseFilter))
1097         *ppv = This;
1098
1099     if (*ppv) {
1100         IUnknown_AddRef((IUnknown *)(*ppv));
1101         return S_OK;
1102     }
1103
1104     if (!IsEqualIID(riid, &IID_IPin) && !IsEqualIID(riid, &IID_IVideoWindow) &&
1105         !IsEqualIID(riid, &IID_IAMFilterMiscFlags))
1106         FIXME("No interface for %s!\n", debugstr_guid(riid));
1107
1108     return E_NOINTERFACE;
1109 }
1110
1111 static ULONG WINAPI GST_Release(IBaseFilter *iface) {
1112     GSTImpl *This = (GSTImpl *)iface;
1113     ULONG refCount = BaseFilterImpl_Release(iface);
1114
1115     TRACE("(%p)->() Release from %d\n", This, refCount + 1);
1116
1117     if (!refCount)
1118         GST_Destroy(This);
1119
1120     return refCount;
1121 }
1122
1123 static HRESULT WINAPI GST_Stop(IBaseFilter *iface) {
1124     GSTImpl *This = (GSTImpl *)iface;
1125
1126     TRACE("()\n");
1127
1128     if (This->gstfilter) {
1129         IAsyncReader_BeginFlush(This->pInputPin.pReader);
1130         gst_element_set_state(This->gstfilter, GST_STATE_READY);
1131         IAsyncReader_EndFlush(This->pInputPin.pReader);
1132     }
1133     return S_OK;
1134 }
1135
1136 static HRESULT WINAPI GST_Pause(IBaseFilter *iface) {
1137     HRESULT hr = S_OK;
1138     GSTImpl *This = (GSTImpl *)iface;
1139     GstState now;
1140     GstStateChangeReturn ret;
1141     TRACE("()\n");
1142
1143     if (!This->gstfilter)
1144         return VFW_E_NOT_CONNECTED;
1145
1146     gst_element_get_state(This->gstfilter, &now, NULL, -1);
1147     if (now == GST_STATE_PAUSED)
1148         return S_OK;
1149     if (now != GST_STATE_PLAYING)
1150         hr = IBaseFilter_Run(iface, -1);
1151     if (FAILED(hr))
1152         return hr;
1153     ret = gst_element_set_state(This->gstfilter, GST_STATE_PAUSED);
1154     if (ret == GST_STATE_CHANGE_ASYNC)
1155         hr = S_FALSE;
1156     return hr;
1157 }
1158
1159 static HRESULT WINAPI GST_Run(IBaseFilter *iface, REFERENCE_TIME tStart) {
1160     HRESULT hr = S_OK;
1161     GSTImpl *This = (GSTImpl *)iface;
1162     ULONG i;
1163     GstState now;
1164     HRESULT hr_any = VFW_E_NOT_CONNECTED;
1165
1166     TRACE("(%s)\n", wine_dbgstr_longlong(tStart));
1167
1168     if (!This->gstfilter)
1169         return VFW_E_NOT_CONNECTED;
1170
1171     gst_element_get_state(This->gstfilter, &now, NULL, -1);
1172     if (now == GST_STATE_PLAYING)
1173         return S_OK;
1174     if (now == GST_STATE_PAUSED) {
1175         GstStateChangeReturn ret;
1176         ret = gst_element_set_state(This->gstfilter, GST_STATE_PAUSED);
1177         if (ret == GST_STATE_CHANGE_ASYNC)
1178             return S_FALSE;
1179         return S_OK;
1180     }
1181
1182     EnterCriticalSection(&This->filter.csFilter);
1183     gst_pad_set_blocked(This->my_src, 0);
1184     gst_pad_set_blocked(This->their_sink, 0);
1185     gst_element_set_state(This->gstfilter, GST_STATE_PLAYING);
1186     This->filter.rtStreamStart = tStart;
1187
1188     for (i = 0; i < This->cStreams; i++) {
1189         hr = BaseOutputPinImpl_Active((BaseOutputPin *)This->ppPins[i]);
1190         if (SUCCEEDED(hr)) {
1191             gst_pad_set_blocked(This->ppPins[i]->my_sink, 0);
1192             if (This->ppPins[i]->their_src)
1193                 gst_pad_set_blocked(This->ppPins[i]->their_src, 0);
1194             hr_any = hr;
1195         }
1196     }
1197     hr = hr_any;
1198     if (SUCCEEDED(hr))
1199         gst_pad_set_active(This->my_src, 1);
1200     LeaveCriticalSection(&This->filter.csFilter);
1201
1202     return hr;
1203 }
1204
1205 static HRESULT WINAPI GST_GetState(IBaseFilter *iface, DWORD dwMilliSecsTimeout, FILTER_STATE *pState) {
1206     GSTImpl *This = (GSTImpl *)iface;
1207     HRESULT hr = S_OK;
1208     GstState now, pending;
1209     GstStateChangeReturn ret;
1210
1211     TRACE("(%d, %p)\n", dwMilliSecsTimeout, pState);
1212
1213     if (!This->gstfilter) {
1214         *pState = State_Stopped;
1215         return S_OK;
1216     }
1217
1218     ret = gst_element_get_state(This->gstfilter, &now, &pending, dwMilliSecsTimeout == INFINITE ? -1 : dwMilliSecsTimeout * 1000);
1219
1220     if (ret == GST_STATE_CHANGE_ASYNC)
1221         hr = VFW_S_STATE_INTERMEDIATE;
1222     else
1223         pending = now;
1224
1225     switch (pending) {
1226         case GST_STATE_PAUSED: *pState = State_Paused; return hr;
1227         case GST_STATE_PLAYING: *pState = State_Running; return hr;
1228         default: *pState = State_Stopped; return hr;
1229     }
1230 }
1231
1232 static HRESULT WINAPI GST_FindPin(IBaseFilter *iface, LPCWSTR Id, IPin **ppPin) {
1233     FIXME("(%p)->(%s,%p) stub\n", iface, debugstr_w(Id), ppPin);
1234     return E_NOTIMPL;
1235 }
1236
1237 static const IBaseFilterVtbl GST_Vtbl = {
1238     GST_QueryInterface,
1239     BaseFilterImpl_AddRef,
1240     GST_Release,
1241     BaseFilterImpl_GetClassID,
1242     GST_Stop,
1243     GST_Pause,
1244     GST_Run,
1245     GST_GetState,
1246     BaseFilterImpl_SetSyncSource,
1247     BaseFilterImpl_GetSyncSource,
1248     BaseFilterImpl_EnumPins,
1249     GST_FindPin,
1250     BaseFilterImpl_QueryFilterInfo,
1251     BaseFilterImpl_JoinFilterGraph,
1252     BaseFilterImpl_QueryVendorInfo
1253 };
1254
1255 static HRESULT WINAPI GST_ChangeCurrent(IMediaSeeking *iface) {
1256     GSTOutPin *This = impl_from_IMediaSeeking(iface);
1257     GstEvent *ev = gst_event_new_seek(This->seek.dRate, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH, GST_SEEK_TYPE_SET, This->seek.llCurrent * 100, GST_SEEK_TYPE_NONE, -1);
1258     TRACE("(%p) going to %i.%i!\n", iface, (int)(This->seek.llCurrent / 10000000), (int)((This->seek.llCurrent / 10000)%1000));
1259     gst_pad_push_event(This->my_sink, ev);
1260     return S_OK;
1261 }
1262
1263 static HRESULT WINAPI GST_ChangeStop(IMediaSeeking *iface) {
1264     GSTOutPin *This = impl_from_IMediaSeeking(iface);
1265     GstEvent *ev = gst_event_new_seek(This->seek.dRate, GST_FORMAT_TIME, 0, GST_SEEK_TYPE_NONE, -1, GST_SEEK_TYPE_SET, This->seek.llStop);
1266     TRACE("(%p) going to %i.%i!\n", iface, (int)(This->seek.llCurrent / 10000000), (int)((This->seek.llCurrent / 10000)%1000));
1267     gst_pad_push_event(This->my_sink, ev);
1268     return S_OK;
1269 }
1270
1271 static HRESULT WINAPI GST_ChangeRate(IMediaSeeking *iface) {
1272     GSTOutPin *This = impl_from_IMediaSeeking(iface);
1273     GstEvent *ev = gst_event_new_seek(This->seek.dRate, GST_FORMAT_TIME, 0, GST_SEEK_TYPE_NONE, -1, GST_SEEK_TYPE_NONE, -1);
1274     TRACE("(%p) New rate %g\n", iface, This->seek.dRate);
1275     gst_pad_push_event(This->my_sink, ev);
1276     return S_OK;
1277 }
1278
1279 static HRESULT WINAPI GST_Seeking_QueryInterface(IMediaSeeking *iface, REFIID riid, void **ppv) {
1280     GSTOutPin *This = impl_from_IMediaSeeking(iface);
1281     return IUnknown_QueryInterface((IUnknown *)This, riid, ppv);
1282 }
1283
1284 static ULONG WINAPI GST_Seeking_AddRef(IMediaSeeking *iface) {
1285     GSTOutPin *This = impl_from_IMediaSeeking(iface);
1286     return IUnknown_AddRef((IUnknown *)This);
1287 }
1288
1289 static ULONG WINAPI GST_Seeking_Release(IMediaSeeking *iface) {
1290     GSTOutPin *This = impl_from_IMediaSeeking(iface);
1291     return IUnknown_Release((IUnknown *)This);
1292 }
1293
1294 static const IMediaSeekingVtbl GST_Seeking_Vtbl =
1295 {
1296     GST_Seeking_QueryInterface,
1297     GST_Seeking_AddRef,
1298     GST_Seeking_Release,
1299     SourceSeekingImpl_GetCapabilities,
1300     SourceSeekingImpl_CheckCapabilities,
1301     SourceSeekingImpl_IsFormatSupported,
1302     SourceSeekingImpl_QueryPreferredFormat,
1303     SourceSeekingImpl_GetTimeFormat,
1304     SourceSeekingImpl_IsUsingTimeFormat,
1305     SourceSeekingImpl_SetTimeFormat,
1306     SourceSeekingImpl_GetDuration,
1307     SourceSeekingImpl_GetStopPosition,
1308     SourceSeekingImpl_GetCurrentPosition,
1309     SourceSeekingImpl_ConvertTimeFormat,
1310     SourceSeekingImpl_SetPositions,
1311     SourceSeekingImpl_GetPositions,
1312     SourceSeekingImpl_GetAvailable,
1313     SourceSeekingImpl_SetRate,
1314     SourceSeekingImpl_GetRate,
1315     SourceSeekingImpl_GetPreroll
1316 };
1317
1318 static HRESULT WINAPI GST_QualityControl_Notify(IQualityControl *iface, IBaseFilter *sender, Quality qm) {
1319     QualityControlImpl *This = (QualityControlImpl*)iface;
1320     GSTOutPin *pin = (GSTOutPin*)This->self;
1321     gst_pad_push_event(pin->my_sink, gst_event_new_qos(1000./qm.Proportion, qm.Late*100, qm.TimeStamp*100));
1322     return S_OK;
1323 }
1324
1325 static const IQualityControlVtbl GSTOutPin_QualityControl_Vtbl = {
1326     QualityControlImpl_QueryInterface,
1327     QualityControlImpl_AddRef,
1328     QualityControlImpl_Release,
1329     GST_QualityControl_Notify,
1330     QualityControlImpl_SetSink
1331 };
1332
1333 static HRESULT WINAPI GSTOutPin_QueryInterface(IPin *iface, REFIID riid, void **ppv) {
1334     GSTOutPin *This = (GSTOutPin *)iface;
1335
1336     TRACE("(%s, %p)\n", debugstr_guid(riid), ppv);
1337
1338     *ppv = NULL;
1339
1340     if (IsEqualIID(riid, &IID_IUnknown))
1341         *ppv = iface;
1342     else if (IsEqualIID(riid, &IID_IPin))
1343         *ppv = iface;
1344     else if (IsEqualIID(riid, &IID_IMediaSeeking))
1345         *ppv = &This->seek;
1346     else if (IsEqualIID(riid, &IID_IQualityControl))
1347         *ppv = &This->qcimpl;
1348
1349     if (*ppv) {
1350         IUnknown_AddRef((IUnknown *)(*ppv));
1351         return S_OK;
1352     }
1353     FIXME("No interface for %s!\n", debugstr_guid(riid));
1354     return E_NOINTERFACE;
1355 }
1356
1357 static ULONG WINAPI GSTOutPin_Release(IPin *iface) {
1358     GSTOutPin *This = (GSTOutPin *)iface;
1359     ULONG refCount = InterlockedDecrement(&This->pin.pin.refCount);
1360     TRACE("(%p)->() Release from %d\n", iface, refCount + 1);
1361
1362     if (!refCount) {
1363         if (This->their_src)
1364             gst_pad_unlink(This->their_src, This->my_sink);
1365         gst_object_unref(This->my_sink);
1366         CloseHandle(This->caps_event);
1367         DeleteMediaType(This->pmt);
1368         FreeMediaType(&This->pin.pin.mtCurrent);
1369         gst_segment_free(This->segment);
1370         CoTaskMemFree(This);
1371         return 0;
1372     }
1373     return refCount;
1374 }
1375
1376 static HRESULT WINAPI GSTOutPin_GetMediaType(BasePin *iface, int iPosition, AM_MEDIA_TYPE *pmt)
1377 {
1378     GSTOutPin *This = (GSTOutPin *)iface;
1379
1380     if (iPosition < 0)
1381         return E_INVALIDARG;
1382     if (iPosition > 0)
1383         return VFW_S_NO_MORE_ITEMS;
1384     CopyMediaType(pmt, This->pmt);
1385     return S_OK;
1386 }
1387
1388 static HRESULT WINAPI GSTOutPin_DecideBufferSize(BaseOutputPin *iface, IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES *ppropInputRequest)
1389 {
1390     /* Unused */
1391     return S_OK;
1392 }
1393
1394 static HRESULT WINAPI GSTOutPin_DecideAllocator(BaseOutputPin *iface, IMemInputPin *pPin, IMemAllocator **pAlloc)
1395 {
1396     HRESULT hr;
1397     GSTOutPin *This = (GSTOutPin *)iface;
1398     GSTImpl *GSTfilter = (GSTImpl*)This->pin.pin.pinInfo.pFilter;
1399
1400     pAlloc = NULL;
1401     if (GSTfilter->pInputPin.pAlloc)
1402         hr = IMemInputPin_NotifyAllocator(pPin, GSTfilter->pInputPin.pAlloc, FALSE);
1403     else
1404         hr = VFW_E_NO_ALLOCATOR;
1405
1406     return hr;
1407 }
1408
1409 static HRESULT WINAPI GSTOutPin_BreakConnect(BaseOutputPin *This)
1410 {
1411     HRESULT hr;
1412
1413     TRACE("(%p)->()\n", This);
1414
1415     EnterCriticalSection(This->pin.pCritSec);
1416     if (!This->pin.pConnectedTo || !This->pMemInputPin)
1417         hr = VFW_E_NOT_CONNECTED;
1418     else
1419     {
1420         hr = IPin_Disconnect(This->pin.pConnectedTo);
1421         IPin_Disconnect((IPin *)This);
1422     }
1423     LeaveCriticalSection(This->pin.pCritSec);
1424
1425     return hr;
1426 }
1427
1428 static const IPinVtbl GST_OutputPin_Vtbl = {
1429     GSTOutPin_QueryInterface,
1430     BasePinImpl_AddRef,
1431     GSTOutPin_Release,
1432     BaseOutputPinImpl_Connect,
1433     BaseOutputPinImpl_ReceiveConnection,
1434     BaseOutputPinImpl_Disconnect,
1435     BasePinImpl_ConnectedTo,
1436     BasePinImpl_ConnectionMediaType,
1437     BasePinImpl_QueryPinInfo,
1438     BasePinImpl_QueryDirection,
1439     BasePinImpl_QueryId,
1440     GST_OutPin_QueryAccept,
1441     BasePinImpl_EnumMediaTypes,
1442     BasePinImpl_QueryInternalConnections,
1443     BaseOutputPinImpl_EndOfStream,
1444     BaseOutputPinImpl_BeginFlush,
1445     BaseOutputPinImpl_EndFlush,
1446     BasePinImpl_NewSegment
1447 };
1448
1449 static const BasePinFuncTable output_BaseFuncTable = {
1450     NULL,
1451     BaseOutputPinImpl_AttemptConnection,
1452     BasePinImpl_GetMediaTypeVersion,
1453     GSTOutPin_GetMediaType
1454 };
1455
1456 static const BaseOutputPinFuncTable output_BaseOutputFuncTable = {
1457     GSTOutPin_DecideBufferSize,
1458     GSTOutPin_DecideAllocator,
1459     GSTOutPin_BreakConnect
1460 };
1461
1462 static HRESULT GST_AddPin(GSTImpl *This, const PIN_INFO *piOutput, const AM_MEDIA_TYPE *amt) {
1463     HRESULT hr;
1464     This->ppPins = CoTaskMemRealloc(This->ppPins, (This->cStreams + 1) * sizeof(IPin *));
1465
1466     hr = BaseOutputPin_Construct(&GST_OutputPin_Vtbl, sizeof(GSTOutPin), piOutput, &output_BaseFuncTable, &output_BaseOutputFuncTable, &This->filter.csFilter, (IPin**)(This->ppPins + This->cStreams));
1467     if (SUCCEEDED(hr)) {
1468         GSTOutPin *pin = This->ppPins[This->cStreams];
1469         pin->pmt = CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE));
1470         CopyMediaType(pin->pmt, amt);
1471         pin->pin.pin.pinInfo.pFilter = (LPVOID)This;
1472         pin->caps_event = CreateEventW(NULL, 0, 0, NULL);
1473         pin->segment = gst_segment_new();
1474         This->cStreams++;
1475         QualityControlImpl_init(&pin->qcimpl, NULL, (IBaseFilter*)pin);
1476         pin->qcimpl.lpVtbl = &GSTOutPin_QualityControl_Vtbl;
1477         SourceSeeking_Init(&pin->seek, &GST_Seeking_Vtbl, GST_ChangeStop, GST_ChangeCurrent, GST_ChangeRate, &This->filter.csFilter);
1478         BaseFilterImpl_IncrementPinVersion((BaseFilter*)This);
1479     } else
1480         ERR("Failed with error %x\n", hr);
1481     return hr;
1482 }
1483
1484 static HRESULT GST_RemoveOutputPins(GSTImpl *This) {
1485     HRESULT hr;
1486     ULONG i;
1487     GSTOutPin **ppOldPins = This->ppPins;
1488     TRACE("(%p)\n", This);
1489
1490     if (!This->gstfilter)
1491         return S_OK;
1492     gst_element_set_bus(This->gstfilter, NULL);
1493     gst_element_set_state(This->gstfilter, GST_STATE_NULL);
1494     gst_pad_unlink(This->my_src, This->their_sink);
1495     This->my_src = This->their_sink = NULL;
1496
1497     for (i = 0; i < This->cStreams; i++) {
1498         hr = BaseOutputPinImpl_BreakConnect(&ppOldPins[i]->pin);
1499         TRACE("Disconnect: %08x\n", hr);
1500         IPin_Release((IPin*)ppOldPins[i]);
1501     }
1502     This->cStreams = 0;
1503     This->ppPins = NULL;
1504     gst_object_unref(This->gstfilter);
1505     This->gstfilter = NULL;
1506     BaseFilterImpl_IncrementPinVersion((BaseFilter*)This);
1507     CoTaskMemFree(ppOldPins);
1508     return S_OK;
1509 }
1510
1511 static ULONG WINAPI GSTInPin_Release(IPin *iface) {
1512     GSTInPin *This = (GSTInPin*)iface;
1513     ULONG refCount = InterlockedDecrement(&This->pin.refCount);
1514
1515     TRACE("(%p)->() Release from %d\n", iface, refCount + 1);
1516     if (!refCount) {
1517         FreeMediaType(&This->pin.mtCurrent);
1518         if (This->pAlloc)
1519             IMemAllocator_Release(This->pAlloc);
1520         This->pAlloc = NULL;
1521         This->pin.lpVtbl = NULL;
1522         return 0;
1523     } else
1524         return refCount;
1525 }
1526
1527 static HRESULT WINAPI GSTInPin_ReceiveConnection(IPin *iface, IPin *pReceivePin, const AM_MEDIA_TYPE *pmt) {
1528     PIN_DIRECTION pindirReceive;
1529     HRESULT hr = S_OK;
1530     GSTInPin *This = (GSTInPin*)iface;
1531
1532     TRACE("(%p/%p)->(%p, %p)\n", This, iface, pReceivePin, pmt);
1533     dump_AM_MEDIA_TYPE(pmt);
1534
1535     EnterCriticalSection(This->pin.pCritSec);
1536     if (!This->pin.pConnectedTo) {
1537         ALLOCATOR_PROPERTIES props;
1538
1539         props.cBuffers = 8;
1540         props.cbBuffer = 16384;
1541         props.cbAlign = 1;
1542         props.cbPrefix = 0;
1543
1544         if (SUCCEEDED(hr) && IPin_QueryAccept(iface, pmt) != S_OK)
1545             hr = VFW_E_TYPE_NOT_ACCEPTED;
1546         if (SUCCEEDED(hr)) {
1547             IPin_QueryDirection(pReceivePin, &pindirReceive);
1548             if (pindirReceive != PINDIR_OUTPUT) {
1549                 ERR("Can't connect from non-output pin\n");
1550                 hr = VFW_E_INVALID_DIRECTION;
1551             }
1552         }
1553
1554         This->pReader = NULL;
1555         This->pAlloc = NULL;
1556         if (SUCCEEDED(hr))
1557             hr = IPin_QueryInterface(pReceivePin, &IID_IAsyncReader, (LPVOID *)&This->pReader);
1558         if (SUCCEEDED(hr))
1559             hr = GST_Connect(This, pReceivePin, &props);
1560         if (SUCCEEDED(hr))
1561             hr = IAsyncReader_RequestAllocator(This->pReader, NULL, &props, &This->pAlloc);
1562         if (SUCCEEDED(hr)) {
1563             CopyMediaType(&This->pin.mtCurrent, pmt);
1564             This->pin.pConnectedTo = pReceivePin;
1565             IPin_AddRef(pReceivePin);
1566             hr = IMemAllocator_Commit(This->pAlloc);
1567         } else {
1568             GST_RemoveOutputPins((GSTImpl *)This->pin.pinInfo.pFilter);
1569             if (This->pReader)
1570                 IAsyncReader_Release(This->pReader);
1571             This->pReader = NULL;
1572             if (This->pAlloc)
1573                 IMemAllocator_Release(This->pAlloc);
1574             This->pAlloc = NULL;
1575         }
1576         TRACE("Size: %i\n", props.cbBuffer);
1577     } else
1578         hr = VFW_E_ALREADY_CONNECTED;
1579     LeaveCriticalSection(This->pin.pCritSec);
1580     return hr;
1581 }
1582
1583 static HRESULT WINAPI GSTInPin_Disconnect(IPin *iface) {
1584     HRESULT hr;
1585     GSTInPin *This = (GSTInPin*)iface;
1586     FILTER_STATE state;
1587     TRACE("()\n");
1588
1589     hr = IBaseFilter_GetState(This->pin.pinInfo.pFilter, INFINITE, &state);
1590     EnterCriticalSection(This->pin.pCritSec);
1591     if (This->pin.pConnectedTo) {
1592         GSTImpl *Parser = (GSTImpl *)This->pin.pinInfo.pFilter;
1593
1594         if (SUCCEEDED(hr) && state == State_Stopped) {
1595             IMemAllocator_Decommit(This->pAlloc);
1596             IPin_Disconnect(This->pin.pConnectedTo);
1597             This->pin.pConnectedTo = NULL;
1598             hr = GST_RemoveOutputPins(Parser);
1599         } else
1600             hr = VFW_E_NOT_STOPPED;
1601     } else
1602         hr = S_FALSE;
1603     LeaveCriticalSection(This->pin.pCritSec);
1604     return hr;
1605 }
1606
1607 static HRESULT WINAPI GSTInPin_QueryAccept(IPin *iface, const AM_MEDIA_TYPE *pmt) {
1608     GSTInPin *This = (GSTInPin*)iface;
1609
1610     TRACE("(%p)->(%p)\n", This, pmt);
1611     dump_AM_MEDIA_TYPE(pmt);
1612
1613     if (IsEqualIID(&pmt->majortype, &MEDIATYPE_Stream))
1614         return S_OK;
1615     return S_FALSE;
1616 }
1617
1618 static HRESULT WINAPI GSTInPin_EndOfStream(IPin *iface) {
1619     GSTInPin *pin = (GSTInPin*)iface;
1620     GSTImpl *This = (GSTImpl*)pin->pin.pinInfo.pFilter;
1621
1622     FIXME("Propagate message on %p\n", This);
1623     return S_OK;
1624 }
1625
1626 static HRESULT WINAPI GSTInPin_BeginFlush(IPin *iface) {
1627     GSTInPin *pin = (GSTInPin*)iface;
1628     GSTImpl *This = (GSTImpl*)pin->pin.pinInfo.pFilter;
1629
1630     FIXME("Propagate message on %p\n", This);
1631     return S_OK;
1632 }
1633
1634 static HRESULT WINAPI GSTInPin_EndFlush(IPin *iface) {
1635     GSTInPin *pin = (GSTInPin*)iface;
1636     GSTImpl *This = (GSTImpl*)pin->pin.pinInfo.pFilter;
1637
1638     FIXME("Propagate message on %p\n", This);
1639     return S_OK;
1640 }
1641
1642 static HRESULT WINAPI GSTInPin_NewSegment(IPin *iface, REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate) {
1643     GSTInPin *pin = (GSTInPin*)iface;
1644     GSTImpl *This = (GSTImpl*)pin->pin.pinInfo.pFilter;
1645
1646     BasePinImpl_NewSegment(iface, tStart, tStop, dRate);
1647     FIXME("Propagate message on %p\n", This);
1648     return S_OK;
1649 }
1650
1651 static HRESULT WINAPI GSTInPin_QueryInterface(IPin * iface, REFIID riid, LPVOID * ppv)
1652 {
1653     GSTInPin *This = (GSTInPin*)iface;
1654
1655     TRACE("(%p/%p)->(%s, %p)\n", This, iface, debugstr_guid(riid), ppv);
1656
1657     *ppv = NULL;
1658
1659     if (IsEqualIID(riid, &IID_IUnknown))
1660         *ppv = iface;
1661     else if (IsEqualIID(riid, &IID_IPin))
1662         *ppv = iface;
1663     else if (IsEqualIID(riid, &IID_IMediaSeeking))
1664     {
1665         return IBaseFilter_QueryInterface(This->pin.pinInfo.pFilter, &IID_IMediaSeeking, ppv);
1666     }
1667
1668     if (*ppv)
1669     {
1670         IUnknown_AddRef((IUnknown *)(*ppv));
1671         return S_OK;
1672     }
1673
1674     FIXME("No interface for %s!\n", debugstr_guid(riid));
1675
1676     return E_NOINTERFACE;
1677 }
1678
1679 static HRESULT WINAPI GSTInPin_EnumMediaTypes(IPin *iface, IEnumMediaTypes **ppEnum)
1680 {
1681     BasePin *This = (BasePin *)iface;
1682
1683     TRACE("(%p/%p)->(%p)\n", This, iface, ppEnum);
1684
1685     return EnumMediaTypes_Construct(This, BasePinImpl_GetMediaType, BasePinImpl_GetMediaTypeVersion, ppEnum);
1686 }
1687
1688 static const IPinVtbl GST_InputPin_Vtbl = {
1689     GSTInPin_QueryInterface,
1690     BasePinImpl_AddRef,
1691     GSTInPin_Release,
1692     BaseInputPinImpl_Connect,
1693     GSTInPin_ReceiveConnection,
1694     GSTInPin_Disconnect,
1695     BasePinImpl_ConnectedTo,
1696     BasePinImpl_ConnectionMediaType,
1697     BasePinImpl_QueryPinInfo,
1698     BasePinImpl_QueryDirection,
1699     BasePinImpl_QueryId,
1700     GSTInPin_QueryAccept,
1701     GSTInPin_EnumMediaTypes,
1702     BasePinImpl_QueryInternalConnections,
1703     GSTInPin_EndOfStream,
1704     GSTInPin_BeginFlush,
1705     GSTInPin_EndFlush,
1706     GSTInPin_NewSegment
1707 };