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