hlink: Site data should only be set if the hlink has an HlinkSite.
[wine] / dlls / winegstreamer / gsttffilter.c
1 /*
2  * GStreamer wrapper filter
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
23 #include "config.h"
24
25 #include <gst/app/gstappsink.h>
26 #include <gst/app/gstappsrc.h>
27 #include <gst/app/gstappbuffer.h>
28
29 #include "gst_private.h"
30 #include "gst_guids.h"
31
32 #include "uuids.h"
33 #include "mmreg.h"
34 #include "windef.h"
35 #include "winbase.h"
36 #include "dshow.h"
37 #include "strmif.h"
38 #include "vfwmsgs.h"
39 #include "dvdmedia.h"
40 #include "ks.h"
41 #include "ksmedia.h"
42 #include "msacm.h"
43
44 #include <assert.h>
45
46 #include "wine/unicode.h"
47 #include "wine/debug.h"
48
49 #include "initguid.h"
50 DEFINE_GUID(WMMEDIASUBTYPE_MP3, 0x00000055, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
51
52 WINE_DEFAULT_DEBUG_CHANNEL(gstreamer);
53
54 struct typeinfo {
55     GstCaps *caps;
56     const char *type;
57 };
58
59 static const IBaseFilterVtbl GSTTf_Vtbl;
60
61 static gboolean match_element(GstPluginFeature *feature, gpointer gdata) {
62     struct typeinfo *data = (struct typeinfo*)gdata;
63     GstElementFactory *factory;
64     const GList *list;
65
66     if (!GST_IS_ELEMENT_FACTORY(feature))
67         return FALSE;
68     factory = GST_ELEMENT_FACTORY(feature);
69     if (!strstr(gst_element_factory_get_klass(factory), data->type))
70         return FALSE;
71     for (list = gst_element_factory_get_static_pad_templates(factory); list; list = list->next) {
72         GstStaticPadTemplate *pad = (GstStaticPadTemplate*)list->data;
73         GstCaps *caps;
74         gboolean ret;
75         if (pad->direction != GST_PAD_SINK)
76             continue;
77         caps = gst_static_caps_get(&pad->static_caps);
78         ret = gst_caps_is_always_compatible(caps, data->caps);
79         gst_caps_unref(caps);
80         if (ret)
81             return TRUE;
82     }
83     return FALSE;
84 }
85
86 static const char *Gstreamer_FindMatch(const char *strcaps)
87 {
88     struct typeinfo data;
89     GList *list, *copy;
90     guint bestrank = 0;
91     GstElementFactory *bestfactory = NULL;
92     GstCaps *caps = gst_caps_from_string(strcaps);
93
94     data.caps = caps;
95     data.type = "Decoder";
96     copy = gst_default_registry_feature_filter(match_element, 0, &data);
97     for (list = copy; list; list = list->next) {
98         GstElementFactory *factory = (GstElementFactory*)list->data;
99         guint rank;
100         rank = gst_plugin_feature_get_rank(GST_PLUGIN_FEATURE(factory));
101         if (rank > bestrank || !bestrank) {
102             bestrank = rank;
103             bestfactory = factory;
104         }
105     }
106     gst_caps_unref(caps);
107     g_list_free(copy);
108
109     if (!bestfactory) {
110         FIXME("Could not find plugin for %s\n", strcaps);
111         return NULL;
112     }
113     return gst_plugin_feature_get_name(GST_PLUGIN_FEATURE(bestfactory));
114 }
115
116 typedef struct GstTfImpl {
117     TransformFilter tf;
118     IUnknown *seekthru_unk;
119     const char *gstreamer_name;
120     GstElement *filter;
121     GstPad *my_src, *my_sink, *their_src, *their_sink;
122     LONG cbBuffer;
123 } GstTfImpl;
124
125 static HRESULT WINAPI Gstreamer_transform_ProcessBegin(TransformFilter *iface) {
126     GstTfImpl *This = (GstTfImpl*)iface;
127     int ret;
128
129     ret = gst_element_set_state(This->filter, GST_STATE_PLAYING);
130     TRACE("Returned: %i\n", ret);
131     return S_OK;
132 }
133
134 static HRESULT WINAPI Gstreamer_transform_DecideBufferSize(TransformFilter *tf, IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES *ppropInputRequest)
135 {
136     GstTfImpl *This = (GstTfImpl*)tf;
137     ALLOCATOR_PROPERTIES actual;
138
139     if (!ppropInputRequest->cbAlign)
140         ppropInputRequest->cbAlign = 1;
141
142     ppropInputRequest->cbBuffer = This->cbBuffer;
143
144     if (!ppropInputRequest->cBuffers)
145         ppropInputRequest->cBuffers = 1;
146
147     return IMemAllocator_SetProperties(pAlloc, ppropInputRequest, &actual);
148 }
149
150 static void release_sample(void *data) {
151     TRACE("Releasing %p\n", data);
152     IMediaSample_Release((IMediaSample *)data);
153 }
154
155 static GstFlowReturn got_data(GstPad *pad, GstBuffer *buf) {
156     GstTfImpl *This = gst_pad_get_element_private(pad);
157     IMediaSample *sample = GST_APP_BUFFER(buf)->priv;
158     REFERENCE_TIME tStart, tStop;
159     HRESULT hr;
160
161     if (GST_BUFFER_TIMESTAMP_IS_VALID(buf) &&
162         GST_BUFFER_DURATION_IS_VALID(buf)) {
163         tStart = buf->timestamp / 100;
164         tStop = tStart + buf->duration / 100;
165         IMediaSample_SetTime(sample, &tStart, &tStop);
166     }
167     else
168         IMediaSample_SetTime(sample, NULL, NULL);
169
170     IMediaSample_SetDiscontinuity(sample, GST_BUFFER_FLAG_IS_SET(buf, GST_BUFFER_FLAG_DISCONT));
171     IMediaSample_SetPreroll(sample, GST_BUFFER_FLAG_IS_SET(buf, GST_BUFFER_FLAG_PREROLL));
172     IMediaSample_SetSyncPoint(sample, !GST_BUFFER_FLAG_IS_SET(buf, GST_BUFFER_FLAG_DELTA_UNIT));
173
174     hr = BaseOutputPinImpl_Deliver((BaseOutputPin*)This->tf.ppPins[1], sample);
175     gst_buffer_unref(buf);
176     if (FAILED(hr))
177         return GST_FLOW_WRONG_STATE;
178     if (hr != S_OK)
179         return GST_FLOW_RESEND;
180     return GST_FLOW_OK;
181 }
182
183 static GstFlowReturn request_buffer(GstPad *pad, guint64 ofs, guint size, GstCaps *caps, GstBuffer **buf) {
184     GstTfImpl *This = gst_pad_get_element_private(pad);
185     IMediaSample *sample;
186     BYTE *ptr;
187     HRESULT hr;
188     TRACE("Requesting buffer\n");
189
190     hr = BaseOutputPinImpl_GetDeliveryBuffer((BaseOutputPin*)This->tf.ppPins[1], &sample, NULL, NULL, 0);
191     if (FAILED(hr)) {
192         ERR("Could not get output buffer: %08x\n", hr);
193         return GST_FLOW_WRONG_STATE;
194     }
195     IMediaSample_SetActualDataLength(sample, size);
196     IMediaSample_GetPointer(sample, &ptr);
197     *buf = gst_app_buffer_new(ptr, size, release_sample, sample);
198
199     if (!*buf) {
200         IMediaSample_Release(sample);
201         ERR("Out of memory\n");
202         return GST_FLOW_ERROR;
203     }
204     if (!caps)
205         caps = gst_pad_get_caps_reffed(This->my_sink);
206     gst_buffer_set_caps(*buf, caps);
207     return GST_FLOW_OK;
208 }
209
210 static HRESULT WINAPI Gstreamer_transform_ProcessData(TransformFilter *iface, IMediaSample *sample) {
211     GstTfImpl *This = (GstTfImpl*)iface;
212     REFERENCE_TIME tStart, tStop;
213     BYTE *data;
214     GstBuffer *buf;
215     HRESULT hr;
216     int ret;
217     TRACE("Reading %p\n", sample);
218
219     EnterCriticalSection(&This->tf.filter.csFilter);
220     IMediaSample_GetPointer(sample, &data);
221     buf = gst_app_buffer_new(data, IMediaSample_GetActualDataLength(sample), release_sample, sample);
222     if (!buf) {
223         LeaveCriticalSection(&This->tf.filter.csFilter);
224         return S_OK;
225     }
226     gst_buffer_set_caps(buf, gst_pad_get_caps_reffed(This->my_src));
227     IMediaSample_AddRef(sample);
228     buf->duration = buf->timestamp = -1;
229     hr = IMediaSample_GetTime(sample, &tStart, &tStop);
230     if (SUCCEEDED(hr)) {
231         buf->timestamp = tStart * 100;
232         if (hr == S_OK)
233             buf->duration = (tStop - tStart)*100;
234     }
235     if (IMediaSample_IsDiscontinuity(sample) == S_OK)
236         GST_BUFFER_FLAG_SET(buf, GST_BUFFER_FLAG_DISCONT);
237     if (IMediaSample_IsPreroll(sample) == S_OK)
238         GST_BUFFER_FLAG_SET(buf, GST_BUFFER_FLAG_PREROLL);
239     if (IMediaSample_IsSyncPoint(sample) != S_OK)
240         GST_BUFFER_FLAG_SET(buf, GST_BUFFER_FLAG_DELTA_UNIT);
241     LeaveCriticalSection(&This->tf.filter.csFilter);
242     ret = gst_pad_push(This->my_src, buf);
243     if (ret)
244         WARN("Sending returned: %i\n", ret);
245     if (ret == GST_FLOW_ERROR)
246         return E_FAIL;
247     if (ret == GST_FLOW_WRONG_STATE)
248         return VFW_E_WRONG_STATE;
249     if (ret == GST_FLOW_RESEND)
250         return S_FALSE;
251     return S_OK;
252 }
253
254 static HRESULT WINAPI Gstreamer_transform_ProcessEnd(TransformFilter *iface) {
255     GstTfImpl *This = (GstTfImpl*)iface;
256     int ret;
257
258     ret = gst_element_set_state(This->filter, GST_STATE_PAUSED);
259     TRACE("Returned: %i\n", ret);
260     return S_OK;
261 }
262
263 static void Gstreamer_transform_pad_added(GstElement *filter, GstPad *pad, GstTfImpl *This)
264 {
265     int ret;
266     if (!GST_PAD_IS_SRC(pad))
267         return;
268
269     ret = gst_pad_link(pad, This->my_sink);
270     if (ret < 0)
271         WARN("Failed to link with %i\n", ret);
272     This->their_src = pad;
273
274     gst_pad_set_active(pad, TRUE);
275     gst_pad_set_active(This->my_sink, TRUE);
276 }
277
278 static HRESULT Gstreamer_transform_ConnectInput(GstTfImpl *This, const AM_MEDIA_TYPE *amt, GstCaps *capsin, GstCaps *capsout) {
279     GstIterator *it;
280     int done = 0, found = 0, ret;
281
282     This->filter = gst_element_factory_make(This->gstreamer_name, NULL);
283     if (!This->filter) {
284         FIXME("Could not make %s filter\n", This->gstreamer_name);
285         return E_FAIL;
286     }
287     This->my_src = gst_pad_new(NULL, GST_PAD_SRC);
288     gst_pad_set_element_private (This->my_src, This);
289
290     This->my_sink = gst_pad_new(NULL, GST_PAD_SINK);
291     gst_pad_set_chain_function(This->my_sink, got_data);
292     gst_pad_set_bufferalloc_function(This->my_sink, request_buffer);
293     gst_pad_set_element_private (This->my_sink, This);
294
295     ret = gst_pad_set_caps(This->my_src, capsin);
296     if (ret < 0) {
297         WARN("Failed to set caps on own source with %i\n", ret);
298         return E_FAIL;
299     }
300
301     ret = gst_pad_set_caps(This->my_sink, capsout);
302     if (ret < 0) {
303         WARN("Failed to set caps on own sink with %i\n", ret);
304         return E_FAIL;
305     }
306
307     it = gst_element_iterate_sink_pads(This->filter);
308     while (!done) {
309         gpointer item;
310
311         switch (gst_iterator_next(it, &item)) {
312         case GST_ITERATOR_RESYNC:
313             gst_iterator_resync (it);
314             break;
315         case GST_ITERATOR_OK:
316             This->their_sink = item;
317         case GST_ITERATOR_ERROR:
318         case GST_ITERATOR_DONE:
319             done = 1;
320             break;
321         }
322     }
323     gst_iterator_free(it);
324     if (!This->their_sink) {
325         ERR("Could not find sink on filter %s\n", This->gstreamer_name);
326         return E_FAIL;
327     }
328
329     it = gst_element_iterate_src_pads(This->filter);
330     gst_iterator_resync(it);
331     done = 0;
332     while (!done) {
333         gpointer item;
334
335         switch (gst_iterator_next(it, &item)) {
336         case GST_ITERATOR_RESYNC:
337             gst_iterator_resync (it);
338             break;
339         case GST_ITERATOR_OK:
340             This->their_src = item;
341         case GST_ITERATOR_ERROR:
342         case GST_ITERATOR_DONE:
343             done = 1;
344             break;
345         }
346     }
347     gst_iterator_free(it);
348     found = !!This->their_src;
349     if (!found)
350         g_signal_connect(This->filter, "pad-added", G_CALLBACK(Gstreamer_transform_pad_added), This);
351     ret = gst_pad_link(This->my_src, This->their_sink);
352     if (ret < 0) {
353         WARN("Failed to link with %i\n", ret);
354         return E_FAIL;
355     }
356
357     if (found)
358         Gstreamer_transform_pad_added(This->filter, This->their_src, This);
359
360     if (!gst_pad_is_linked(This->my_sink))
361         return E_FAIL;
362
363     TRACE("Connected\n");
364     return S_OK;
365 }
366
367 static HRESULT WINAPI Gstreamer_transform_Cleanup(TransformFilter *tf, PIN_DIRECTION dir) {
368     GstTfImpl *This = (GstTfImpl*)tf;
369
370     if (dir == PINDIR_INPUT)
371     {
372         if (This->filter) {
373             gst_element_set_state(This->filter, GST_STATE_NULL);
374             gst_object_unref(This->filter);
375         }
376         This->filter = NULL;
377         if (This->my_src) {
378             gst_pad_unlink(This->my_src, This->their_sink);
379             gst_object_unref(This->my_src);
380         }
381         if (This->my_sink) {
382             gst_pad_unlink(This->their_src, This->my_sink);
383             gst_object_unref(This->my_sink);
384         }
385         This->my_sink = This->my_src = This->their_sink = This->their_src = NULL;
386         FIXME("%p stub\n", This);
387     }
388     return S_OK;
389 }
390
391 static HRESULT WINAPI Gstreamer_transform_EndOfStream(TransformFilter *iface) {
392     GstTfImpl *This = (GstTfImpl*)iface;
393     TRACE("%p\n", This);
394
395     gst_pad_push_event(This->my_src, gst_event_new_eos());
396     return S_OK;
397 }
398
399 static HRESULT WINAPI Gstreamer_transform_BeginFlush(TransformFilter *iface) {
400     GstTfImpl *This = (GstTfImpl*)iface;
401     TRACE("%p\n", This);
402
403     gst_pad_push_event(This->my_src, gst_event_new_flush_start());
404     return S_OK;
405 }
406
407 static HRESULT WINAPI Gstreamer_transform_EndFlush(TransformFilter *iface) {
408     GstTfImpl *This = (GstTfImpl*)iface;
409     TRACE("%p\n", This);
410
411     gst_pad_push_event(This->my_src, gst_event_new_flush_stop());
412     return S_OK;
413 }
414
415 static HRESULT WINAPI Gstreamer_transform_NewSegment(TransformFilter *iface, REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate) {
416     GstTfImpl *This = (GstTfImpl*)iface;
417     TRACE("%p\n", This);
418
419     gst_pad_push_event(This->my_src, gst_event_new_new_segment_full(1,
420                        1.0, dRate, GST_FORMAT_TIME, tStart*100, tStop <= tStart ? -1 : tStop * 100, tStart*100));
421     return S_OK;
422 }
423
424 static HRESULT Gstreamer_transform_create(IUnknown *punkout, const CLSID *clsid, const char *name, const TransformFilterFuncTable *vtbl, void **obj)
425 {
426     GstTfImpl *This;
427
428     if (FAILED(TransformFilter_Construct(&GSTTf_Vtbl, sizeof(GstTfImpl), clsid, vtbl, (IBaseFilter**)&This)))
429         return E_OUTOFMEMORY;
430     else
431     {
432         ISeekingPassThru *passthru;
433         CoCreateInstance(&CLSID_SeekingPassThru, (IUnknown*)This, CLSCTX_INPROC_SERVER, &IID_IUnknown, (void**)&This->seekthru_unk);
434         IUnknown_QueryInterface(This->seekthru_unk, &IID_ISeekingPassThru, (void**)&passthru);
435         ISeekingPassThru_Init(passthru, FALSE, (IPin*)This->tf.ppPins[0]);
436         ISeekingPassThru_Release(passthru);
437     }
438
439     This->gstreamer_name = name;
440     *obj = This;
441
442     return S_OK;
443 }
444
445 static HRESULT WINAPI Gstreamer_Mp3_QueryConnect(TransformFilter *iface, const AM_MEDIA_TYPE *amt) {
446     GstTfImpl *This = (GstTfImpl*)iface;
447     TRACE("%p %p\n", This, amt);
448     dump_AM_MEDIA_TYPE(amt);
449
450     if ( (!IsEqualGUID(&amt->majortype, &MEDIATYPE_Audio) &&
451           !IsEqualGUID(&amt->majortype, &MEDIATYPE_Stream)) ||
452          (!IsEqualGUID(&amt->subtype, &MEDIASUBTYPE_MPEG1AudioPayload) &&
453           !IsEqualGUID(&amt->subtype, &WMMEDIASUBTYPE_MP3))
454         || !IsEqualGUID(&amt->formattype, &FORMAT_WaveFormatEx))
455         return S_FALSE;
456
457     return S_OK;
458 }
459
460 static HRESULT WINAPI Gstreamer_Mp3_SetMediaType(TransformFilter *tf, PIN_DIRECTION dir, const AM_MEDIA_TYPE *amt) {
461     GstTfImpl *This = (GstTfImpl*)tf;
462     GstCaps *capsin, *capsout;
463     AM_MEDIA_TYPE *outpmt = &This->tf.pmt;
464     WAVEFORMATEX *wfx, *wfxin;
465     HRESULT hr;
466     int layer;
467
468     if (dir != PINDIR_INPUT)
469         return S_OK;
470
471     if (Gstreamer_Mp3_QueryConnect(&This->tf, amt) == S_FALSE || !amt->pbFormat)
472         return VFW_E_TYPE_NOT_ACCEPTED;
473
474     wfxin = (WAVEFORMATEX*)amt->pbFormat;
475     switch (wfxin->wFormatTag) {
476         case WAVE_FORMAT_MPEGLAYER3:
477             layer = 3;
478             break;
479         case WAVE_FORMAT_MPEG: {
480             MPEG1WAVEFORMAT *mpgformat = (MPEG1WAVEFORMAT*)wfxin;
481             layer = mpgformat->fwHeadLayer;
482             break;
483         }
484         default:
485             FIXME("Unhandled tag %x\n", wfxin->wFormatTag);
486             return E_FAIL;
487     }
488
489     FreeMediaType(outpmt);
490     CopyMediaType(outpmt, amt);
491
492     outpmt->subtype = MEDIASUBTYPE_PCM;
493     outpmt->formattype = FORMAT_WaveFormatEx;
494     outpmt->cbFormat = sizeof(*wfx);
495     CoTaskMemFree(outpmt->pbFormat);
496     wfx = CoTaskMemAlloc(outpmt->cbFormat);
497     outpmt->pbFormat = (BYTE*)wfx;
498     wfx->wFormatTag = WAVE_FORMAT_PCM;
499     wfx->wBitsPerSample = 16;
500     wfx->nSamplesPerSec = wfxin->nSamplesPerSec;
501     wfx->nChannels = wfxin->nChannels;
502     wfx->nBlockAlign = wfx->wBitsPerSample * wfx->nChannels / 8;
503     wfx->cbSize = 0;
504     wfx->nAvgBytesPerSec = wfx->nSamplesPerSec * wfx->nBlockAlign;
505
506     capsin = gst_caps_new_simple("audio/mpeg",
507                                  "mpegversion", G_TYPE_INT, 1,
508                                  "layer", G_TYPE_INT, layer,
509                                  "rate", G_TYPE_INT, wfx->nSamplesPerSec,
510                                  "channels", G_TYPE_INT, wfx->nChannels,
511                                  NULL);
512     capsout = gst_caps_new_simple("audio/x-raw-int",
513                                   "endianness", G_TYPE_INT, 1234,
514                                   "signed", G_TYPE_BOOLEAN, 1,
515                                   "width", G_TYPE_INT, 16,
516                                   "depth", G_TYPE_INT, 16,
517                                   "rate", G_TYPE_INT, wfx->nSamplesPerSec,
518                                   "channels", G_TYPE_INT, wfx->nChannels,
519                                    NULL);
520
521     hr = Gstreamer_transform_ConnectInput(This, amt, capsin, capsout);
522     gst_caps_unref(capsin);
523     gst_caps_unref(capsout);
524
525     This->cbBuffer = wfx->nAvgBytesPerSec / 4;
526
527     return hr;
528 }
529
530 static HRESULT WINAPI Gstreamer_Mp3_ConnectInput(TransformFilter *tf, PIN_DIRECTION dir, IPin *pin)
531 {
532     return S_OK;
533 }
534
535 static const TransformFilterFuncTable Gstreamer_Mp3_vtbl = {
536     Gstreamer_transform_DecideBufferSize,
537     Gstreamer_transform_ProcessBegin,
538     Gstreamer_transform_ProcessData,
539     Gstreamer_transform_ProcessEnd,
540     Gstreamer_Mp3_QueryConnect,
541     Gstreamer_Mp3_SetMediaType,
542     Gstreamer_Mp3_ConnectInput,
543     Gstreamer_transform_Cleanup,
544     Gstreamer_transform_EndOfStream,
545     Gstreamer_transform_BeginFlush,
546     Gstreamer_transform_EndFlush,
547     Gstreamer_transform_NewSegment
548 };
549
550 IUnknown * CALLBACK Gstreamer_Mp3_create(IUnknown *punkout, HRESULT *phr)
551 {
552     const char *plugin;
553     IUnknown *obj = NULL;
554     if (!Gstreamer_init())
555     {
556         *phr = E_FAIL;
557         return NULL;
558     }
559     plugin = Gstreamer_FindMatch("audio/mpeg, mpegversion=(int) 1");
560     if (!plugin)
561     {
562         *phr = E_FAIL;
563         return NULL;
564     }
565     *phr = Gstreamer_transform_create(punkout, &CLSID_Gstreamer_Mp3, plugin, &Gstreamer_Mp3_vtbl, (LPVOID*)&obj);
566     return obj;
567 }
568
569 static HRESULT WINAPI Gstreamer_YUV_QueryConnect(TransformFilter *iface, const AM_MEDIA_TYPE *amt) {
570     GstTfImpl *This = (GstTfImpl*)iface;
571     TRACE("%p %p\n", This, amt);
572     dump_AM_MEDIA_TYPE(amt);
573
574     if (!IsEqualGUID(&amt->majortype, &MEDIATYPE_Video) ||
575         (!IsEqualGUID(&amt->formattype, &FORMAT_VideoInfo) &&
576          !IsEqualGUID(&amt->formattype, &FORMAT_VideoInfo2)))
577         return S_FALSE;
578     if (memcmp(&amt->subtype.Data2, &MEDIATYPE_Video.Data2, sizeof(GUID) - sizeof(amt->subtype.Data1)))
579         return S_FALSE;
580     switch (amt->subtype.Data1) {
581         case mmioFOURCC('I','4','2','0'):
582         case mmioFOURCC('Y','V','1','2'):
583         case mmioFOURCC('N','V','1','2'):
584         case mmioFOURCC('N','V','2','1'):
585         case mmioFOURCC('Y','U','Y','2'):
586         case mmioFOURCC('Y','V','Y','U'):
587             return S_OK;
588         default:
589             WARN("Unhandled fourcc %s\n", debugstr_an((char*)&amt->subtype.Data1, 4));
590             return S_FALSE;
591     }
592 }
593
594 static HRESULT WINAPI Gstreamer_YUV_ConnectInput(TransformFilter *tf, PIN_DIRECTION dir, IPin *pin)
595 {
596     return S_OK;
597 }
598
599 static HRESULT WINAPI Gstreamer_YUV_SetMediaType(TransformFilter *tf, PIN_DIRECTION dir,  const AM_MEDIA_TYPE *amt) {
600     GstTfImpl *This = (GstTfImpl*)tf;
601     GstCaps *capsin, *capsout;
602     AM_MEDIA_TYPE *outpmt = &This->tf.pmt;
603     HRESULT hr;
604     int avgtime;
605     DWORD width, height;
606
607     if (dir != PINDIR_INPUT)
608         return S_OK;
609
610     if (Gstreamer_YUV_QueryConnect(&This->tf, amt) == S_FALSE || !amt->pbFormat)
611         return E_FAIL;
612
613     FreeMediaType(outpmt);
614     CopyMediaType(outpmt, amt);
615
616     if (IsEqualGUID(&amt->formattype, &FORMAT_VideoInfo)) {
617         VIDEOINFOHEADER *vih = (VIDEOINFOHEADER*)outpmt->pbFormat;
618         avgtime = vih->AvgTimePerFrame;
619         width = vih->bmiHeader.biWidth;
620         height = vih->bmiHeader.biHeight;
621         if ((LONG)vih->bmiHeader.biHeight > 0)
622             vih->bmiHeader.biHeight = -vih->bmiHeader.biHeight;
623         vih->bmiHeader.biBitCount = 24;
624         vih->bmiHeader.biCompression = BI_RGB;
625     } else {
626         VIDEOINFOHEADER2 *vih = (VIDEOINFOHEADER2*)outpmt->pbFormat;
627         avgtime = vih->AvgTimePerFrame;
628         width = vih->bmiHeader.biWidth;
629         height = vih->bmiHeader.biHeight;
630         if ((LONG)vih->bmiHeader.biHeight > 0)
631             vih->bmiHeader.biHeight = -vih->bmiHeader.biHeight;
632         vih->bmiHeader.biBitCount = 24;
633         vih->bmiHeader.biCompression = BI_RGB;
634     }
635     if (!avgtime)
636         avgtime = 10000000 / 30;
637
638     outpmt->subtype = MEDIASUBTYPE_RGB24;
639
640     capsin = gst_caps_new_simple("video/x-raw-yuv",
641                                  "format", GST_TYPE_FOURCC, amt->subtype.Data1,
642                                  "width", G_TYPE_INT, width,
643                                  "height", G_TYPE_INT, height,
644                                  "framerate", GST_TYPE_FRACTION, 10000000, avgtime,
645                                  NULL);
646     capsout = gst_caps_new_simple("video/x-raw-rgb",
647                                   "endianness", G_TYPE_INT, 4321,
648                                   "width", G_TYPE_INT, width,
649                                   "height", G_TYPE_INT, height,
650                                   "framerate", GST_TYPE_FRACTION, 10000000, avgtime,
651                                   "bpp", G_TYPE_INT, 24,
652                                   "depth", G_TYPE_INT, 24,
653                                   "red_mask", G_TYPE_INT, 0xff,
654                                   "green_mask", G_TYPE_INT, 0xff00,
655                                   "blue_mask", G_TYPE_INT, 0xff0000,
656                                    NULL);
657
658     hr = Gstreamer_transform_ConnectInput(This, amt, capsin, capsout);
659     gst_caps_unref(capsin);
660     gst_caps_unref(capsout);
661
662     This->cbBuffer = width * height * 4;
663     return hr;
664 }
665
666 static const TransformFilterFuncTable Gstreamer_YUV_vtbl = {
667     Gstreamer_transform_DecideBufferSize,
668     Gstreamer_transform_ProcessBegin,
669     Gstreamer_transform_ProcessData,
670     Gstreamer_transform_ProcessEnd,
671     Gstreamer_YUV_QueryConnect,
672     Gstreamer_YUV_SetMediaType,
673     Gstreamer_YUV_ConnectInput,
674     Gstreamer_transform_Cleanup,
675     Gstreamer_transform_EndOfStream,
676     Gstreamer_transform_BeginFlush,
677     Gstreamer_transform_EndFlush,
678     Gstreamer_transform_NewSegment
679 };
680
681 IUnknown * CALLBACK Gstreamer_YUV_create(IUnknown *punkout, HRESULT *phr)
682 {
683     IUnknown *obj = NULL;
684     if (!Gstreamer_init())
685     {
686         *phr = E_FAIL;
687         return NULL;
688     }
689     *phr = Gstreamer_transform_create(punkout, &CLSID_Gstreamer_YUV, "ffmpegcolorspace", &Gstreamer_YUV_vtbl, (LPVOID*)&obj);
690     return obj;
691 }
692
693 static HRESULT WINAPI Gstreamer_AudioConvert_QueryConnect(TransformFilter *iface, const AM_MEDIA_TYPE *amt) {
694     GstTfImpl *This = (GstTfImpl*)iface;
695     TRACE("%p %p\n", This, amt);
696     dump_AM_MEDIA_TYPE(amt);
697
698     if (!IsEqualGUID(&amt->majortype, &MEDIATYPE_Audio) ||
699         !IsEqualGUID(&amt->subtype, &MEDIASUBTYPE_PCM) ||
700         !IsEqualGUID(&amt->formattype, &FORMAT_WaveFormatEx))
701         return S_FALSE;
702     return S_OK;
703 }
704
705 static HRESULT WINAPI Gstreamer_AudioConvert_ConnectInput(TransformFilter *tf, PIN_DIRECTION dir, IPin *pin)
706 {
707     return S_OK;
708 }
709
710 static HRESULT WINAPI Gstreamer_AudioConvert_SetMediaType(TransformFilter *tf, PIN_DIRECTION dir, const AM_MEDIA_TYPE *amt) {
711     GstTfImpl *This = (GstTfImpl*)tf;
712     GstCaps *capsin, *capsout;
713     AM_MEDIA_TYPE *outpmt = &This->tf.pmt;
714     WAVEFORMATEX *inwfe;
715     WAVEFORMATEX *outwfe;
716     WAVEFORMATEXTENSIBLE *outwfx;
717     HRESULT hr;
718     int inisfloat = 0, indepth;
719
720     if (dir != PINDIR_INPUT)
721         return S_OK;
722
723     if (Gstreamer_AudioConvert_QueryConnect(&This->tf, amt) == S_FALSE || !amt->pbFormat)
724         return E_FAIL;
725
726     FreeMediaType(outpmt);
727     *outpmt = *amt;
728     outpmt->pUnk = NULL;
729     outpmt->cbFormat = sizeof(WAVEFORMATEXTENSIBLE);
730     outpmt->pbFormat = CoTaskMemAlloc(outpmt->cbFormat);
731
732     inwfe = (WAVEFORMATEX*)amt->pbFormat;
733     indepth = inwfe->wBitsPerSample;
734     if (inwfe->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
735         WAVEFORMATEXTENSIBLE *inwfx = (WAVEFORMATEXTENSIBLE*)inwfe;
736         inisfloat = IsEqualGUID(&inwfx->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT);
737         if (inwfx->Samples.wValidBitsPerSample)
738             indepth = inwfx->Samples.wValidBitsPerSample;
739     }
740
741     capsin = gst_caps_new_simple(inisfloat ? "audio/x-raw-float" : "audio/x-raw-int",
742                                  "endianness", G_TYPE_INT, 1234,
743                                  "width", G_TYPE_INT, inwfe->wBitsPerSample,
744                                  "depth", G_TYPE_INT, indepth,
745                                  "channels", G_TYPE_INT, inwfe->nChannels,
746                                  "rate", G_TYPE_INT, inwfe->nSamplesPerSec,
747                                   NULL);
748
749     outwfe = (WAVEFORMATEX*)outpmt->pbFormat;
750     outwfx = (WAVEFORMATEXTENSIBLE*)outwfe;
751     outwfe->wFormatTag = WAVE_FORMAT_EXTENSIBLE;
752     outwfe->nChannels = 2;
753     outwfe->nSamplesPerSec = inwfe->nSamplesPerSec;
754     outwfe->wBitsPerSample = 16;
755     outwfe->nBlockAlign = outwfe->nChannels * outwfe->wBitsPerSample / 8;
756     outwfe->nAvgBytesPerSec = outwfe->nBlockAlign * outwfe->nSamplesPerSec;
757     outwfe->cbSize = sizeof(*outwfx) - sizeof(*outwfe);
758     outwfx->Samples.wValidBitsPerSample = outwfe->wBitsPerSample;
759     outwfx->dwChannelMask = SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT;
760     outwfx->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
761
762     capsout = gst_caps_new_simple("audio/x-raw-int",
763                                   "endianness", G_TYPE_INT, 1234,
764                                   "width", G_TYPE_INT, outwfe->wBitsPerSample,
765                                   "depth", G_TYPE_INT, outwfx->Samples.wValidBitsPerSample,
766                                   "channels", G_TYPE_INT, outwfe->nChannels,
767                                   "rate", G_TYPE_INT, outwfe->nSamplesPerSec,
768                                    NULL);
769
770     hr = Gstreamer_transform_ConnectInput(This, amt, capsin, capsout);
771     FIXME("%08x\n", hr);
772     gst_caps_unref(capsin);
773     gst_caps_unref(capsout);
774
775     This->cbBuffer = inwfe->nAvgBytesPerSec;
776     return hr;
777 }
778
779 static const TransformFilterFuncTable Gstreamer_AudioConvert_vtbl = {
780     Gstreamer_transform_DecideBufferSize,
781     Gstreamer_transform_ProcessBegin,
782     Gstreamer_transform_ProcessData,
783     Gstreamer_transform_ProcessEnd,
784     Gstreamer_AudioConvert_QueryConnect,
785     Gstreamer_AudioConvert_SetMediaType,
786     Gstreamer_AudioConvert_ConnectInput,
787     Gstreamer_transform_Cleanup,
788     Gstreamer_transform_EndOfStream,
789     Gstreamer_transform_BeginFlush,
790     Gstreamer_transform_EndFlush,
791     Gstreamer_transform_NewSegment
792 };
793
794 IUnknown * CALLBACK Gstreamer_AudioConvert_create(IUnknown *punkout, HRESULT *phr)
795 {
796     IUnknown *obj = NULL;
797     if (!Gstreamer_init())
798     {
799         *phr = E_FAIL;
800         return NULL;
801     }
802     *phr = Gstreamer_transform_create(punkout, &CLSID_Gstreamer_AudioConvert, "audioconvert", &Gstreamer_AudioConvert_vtbl, (LPVOID*)&obj);
803     return obj;
804 }
805
806 HRESULT WINAPI GSTTf_QueryInterface(IBaseFilter * iface, REFIID riid, LPVOID * ppv)
807 {
808     HRESULT hr;
809     GstTfImpl *This = (GstTfImpl*)iface;
810     TRACE("(%p/%p)->(%s, %p)\n", This, iface, debugstr_guid(riid), ppv);
811
812     if (IsEqualIID(riid, &IID_IMediaSeeking))
813         return IUnknown_QueryInterface(This->seekthru_unk, riid, ppv);
814
815     hr = TransformFilterImpl_QueryInterface(iface, riid, ppv);
816
817     return hr;
818 }
819
820 static const IBaseFilterVtbl GSTTf_Vtbl =
821 {
822     GSTTf_QueryInterface,
823     BaseFilterImpl_AddRef,
824     TransformFilterImpl_Release,
825     BaseFilterImpl_GetClassID,
826     TransformFilterImpl_Stop,
827     TransformFilterImpl_Pause,
828     TransformFilterImpl_Run,
829     BaseFilterImpl_GetState,
830     BaseFilterImpl_SetSyncSource,
831     BaseFilterImpl_GetSyncSource,
832     BaseFilterImpl_EnumPins,
833     TransformFilterImpl_FindPin,
834     BaseFilterImpl_QueryFilterInfo,
835     BaseFilterImpl_JoinFilterGraph,
836     BaseFilterImpl_QueryVendorInfo
837 };