jscript: Don't pass 'this' argument to DISPID_VALUE of pure IDispatch interfaces.
[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 < 2)
145         ppropInputRequest->cBuffers = 2;
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     if (GST_BUFFER_OFFSET_IS_VALID(buf) &&
170         GST_BUFFER_OFFSET_END_IS_VALID(buf)) {
171         tStart = buf->offset / 100;
172         tStop = buf->offset_end / 100;
173         IMediaSample_SetMediaTime(sample, &tStart, &tStop);
174     }
175     else
176         IMediaSample_SetMediaTime(sample, NULL, NULL);
177
178     IMediaSample_SetDiscontinuity(sample, GST_BUFFER_FLAG_IS_SET(buf, GST_BUFFER_FLAG_DISCONT));
179     IMediaSample_SetPreroll(sample, GST_BUFFER_FLAG_IS_SET(buf, GST_BUFFER_FLAG_PREROLL));
180     IMediaSample_SetSyncPoint(sample, !GST_BUFFER_FLAG_IS_SET(buf, GST_BUFFER_FLAG_DELTA_UNIT));
181     IMediaSample_SetActualDataLength(sample, GST_BUFFER_SIZE(buf));
182
183     hr = BaseOutputPinImpl_Deliver((BaseOutputPin*)This->tf.ppPins[1], sample);
184     gst_buffer_unref(buf);
185     if (FAILED(hr))
186         return GST_FLOW_WRONG_STATE;
187     if (hr != S_OK)
188         return GST_FLOW_RESEND;
189     return GST_FLOW_OK;
190 }
191
192 static GstFlowReturn request_buffer(GstPad *pad, guint64 ofs, guint size, GstCaps *caps, GstBuffer **buf) {
193     GstTfImpl *This = gst_pad_get_element_private(pad);
194     IMediaSample *sample;
195     BYTE *ptr;
196     HRESULT hr;
197     TRACE("Requesting buffer\n");
198
199     hr = BaseOutputPinImpl_GetDeliveryBuffer((BaseOutputPin*)This->tf.ppPins[1], &sample, NULL, NULL, 0);
200     if (FAILED(hr)) {
201         ERR("Could not get output buffer: %08x\n", hr);
202         return GST_FLOW_WRONG_STATE;
203     }
204     IMediaSample_SetActualDataLength(sample, size);
205     IMediaSample_GetPointer(sample, &ptr);
206     *buf = gst_app_buffer_new(ptr, size, release_sample, sample);
207
208     if (!*buf) {
209         IMediaSample_Release(sample);
210         ERR("Out of memory\n");
211         return GST_FLOW_ERROR;
212     }
213     if (!caps)
214         caps = gst_pad_get_caps_reffed(This->my_sink);
215     gst_buffer_set_caps(*buf, caps);
216     return GST_FLOW_OK;
217 }
218
219 static HRESULT WINAPI Gstreamer_transform_ProcessData(TransformFilter *iface, IMediaSample *sample) {
220     GstTfImpl *This = (GstTfImpl*)iface;
221     REFERENCE_TIME tStart, tStop;
222     BYTE *data;
223     GstBuffer *buf;
224     HRESULT hr;
225     int ret;
226     TRACE("Reading %p\n", sample);
227
228     EnterCriticalSection(&This->tf.csReceive);
229     IMediaSample_GetPointer(sample, &data);
230     buf = gst_app_buffer_new(data, IMediaSample_GetActualDataLength(sample), release_sample, sample);
231     if (!buf) {
232         LeaveCriticalSection(&This->tf.csReceive);
233         return S_OK;
234     }
235     gst_buffer_set_caps(buf, gst_pad_get_caps_reffed(This->my_src));
236     IMediaSample_AddRef(sample);
237     buf->duration = buf->timestamp = -1;
238     hr = IMediaSample_GetTime(sample, &tStart, &tStop);
239     if (SUCCEEDED(hr)) {
240         buf->timestamp = tStart * 100;
241         if (hr == S_OK)
242             buf->duration = (tStop - tStart)*100;
243     }
244     if (IMediaSample_GetMediaTime(sample, &tStart, &tStop) == S_OK) {
245         buf->offset = tStart * 100;
246         buf->offset_end = tStop * 100;
247     }
248     if (IMediaSample_IsDiscontinuity(sample) == S_OK)
249         GST_BUFFER_FLAG_SET(buf, GST_BUFFER_FLAG_DISCONT);
250     if (IMediaSample_IsPreroll(sample) == S_OK)
251         GST_BUFFER_FLAG_SET(buf, GST_BUFFER_FLAG_PREROLL);
252     if (IMediaSample_IsSyncPoint(sample) != S_OK)
253         GST_BUFFER_FLAG_SET(buf, GST_BUFFER_FLAG_DELTA_UNIT);
254     LeaveCriticalSection(&This->tf.csReceive);
255     ret = gst_pad_push(This->my_src, buf);
256     if (ret)
257         WARN("Sending returned: %i\n", ret);
258     if (ret == GST_FLOW_ERROR)
259         return E_FAIL;
260     if (ret == GST_FLOW_WRONG_STATE)
261         return VFW_E_WRONG_STATE;
262     if (ret == GST_FLOW_RESEND)
263         return S_FALSE;
264     return S_OK;
265 }
266
267 static HRESULT WINAPI Gstreamer_transform_ProcessEnd(TransformFilter *iface) {
268     GstTfImpl *This = (GstTfImpl*)iface;
269     int ret;
270
271     LeaveCriticalSection(&This->tf.csReceive);
272     ret = gst_element_set_state(This->filter, GST_STATE_READY);
273     EnterCriticalSection(&This->tf.csReceive);
274     TRACE("Returned: %i\n", ret);
275     return S_OK;
276 }
277
278 static void Gstreamer_transform_pad_added(GstElement *filter, GstPad *pad, GstTfImpl *This)
279 {
280     int ret;
281     if (!GST_PAD_IS_SRC(pad))
282         return;
283
284     ret = gst_pad_link(pad, This->my_sink);
285     if (ret < 0)
286         WARN("Failed to link with %i\n", ret);
287     This->their_src = pad;
288
289     gst_pad_set_active(pad, TRUE);
290     gst_pad_set_active(This->my_sink, TRUE);
291 }
292
293 static HRESULT Gstreamer_transform_ConnectInput(GstTfImpl *This, const AM_MEDIA_TYPE *amt, GstCaps *capsin, GstCaps *capsout) {
294     GstIterator *it;
295     int done = 0, found = 0, ret;
296
297     This->filter = gst_element_factory_make(This->gstreamer_name, NULL);
298     if (!This->filter) {
299         FIXME("Could not make %s filter\n", This->gstreamer_name);
300         return E_FAIL;
301     }
302     This->my_src = gst_pad_new(NULL, GST_PAD_SRC);
303     gst_pad_set_element_private (This->my_src, This);
304
305     This->my_sink = gst_pad_new(NULL, GST_PAD_SINK);
306     gst_pad_set_chain_function(This->my_sink, got_data);
307     gst_pad_set_bufferalloc_function(This->my_sink, request_buffer);
308     gst_pad_set_element_private (This->my_sink, This);
309
310     ret = gst_pad_set_caps(This->my_src, capsin);
311     if (ret < 0) {
312         WARN("Failed to set caps on own source with %i\n", ret);
313         return E_FAIL;
314     }
315
316     ret = gst_pad_set_caps(This->my_sink, capsout);
317     if (ret < 0) {
318         WARN("Failed to set caps on own sink with %i\n", ret);
319         return E_FAIL;
320     }
321
322     it = gst_element_iterate_sink_pads(This->filter);
323     while (!done) {
324         gpointer item;
325
326         switch (gst_iterator_next(it, &item)) {
327         case GST_ITERATOR_RESYNC:
328             gst_iterator_resync (it);
329             break;
330         case GST_ITERATOR_OK:
331             This->their_sink = item;
332         case GST_ITERATOR_ERROR:
333         case GST_ITERATOR_DONE:
334             done = 1;
335             break;
336         }
337     }
338     gst_iterator_free(it);
339     if (!This->their_sink) {
340         ERR("Could not find sink on filter %s\n", This->gstreamer_name);
341         return E_FAIL;
342     }
343
344     it = gst_element_iterate_src_pads(This->filter);
345     gst_iterator_resync(it);
346     done = 0;
347     while (!done) {
348         gpointer item;
349
350         switch (gst_iterator_next(it, &item)) {
351         case GST_ITERATOR_RESYNC:
352             gst_iterator_resync (it);
353             break;
354         case GST_ITERATOR_OK:
355             This->their_src = item;
356         case GST_ITERATOR_ERROR:
357         case GST_ITERATOR_DONE:
358             done = 1;
359             break;
360         }
361     }
362     gst_iterator_free(it);
363     found = !!This->their_src;
364     if (!found)
365         g_signal_connect(This->filter, "pad-added", G_CALLBACK(Gstreamer_transform_pad_added), This);
366     ret = gst_pad_link(This->my_src, This->their_sink);
367     if (ret < 0) {
368         WARN("Failed to link with %i\n", ret);
369         return E_FAIL;
370     }
371
372     if (found)
373         Gstreamer_transform_pad_added(This->filter, This->their_src, This);
374
375     if (!gst_pad_is_linked(This->my_sink))
376         return E_FAIL;
377
378     TRACE("Connected\n");
379     return S_OK;
380 }
381
382 static HRESULT WINAPI Gstreamer_transform_Cleanup(TransformFilter *tf, PIN_DIRECTION dir) {
383     GstTfImpl *This = (GstTfImpl*)tf;
384
385     if (dir == PINDIR_INPUT)
386     {
387         if (This->filter) {
388             gst_element_set_state(This->filter, GST_STATE_NULL);
389             gst_object_unref(This->filter);
390         }
391         This->filter = NULL;
392         if (This->my_src) {
393             gst_pad_unlink(This->my_src, This->their_sink);
394             gst_object_unref(This->my_src);
395         }
396         if (This->my_sink) {
397             gst_pad_unlink(This->their_src, This->my_sink);
398             gst_object_unref(This->my_sink);
399         }
400         This->my_sink = This->my_src = This->their_sink = This->their_src = NULL;
401     }
402     return S_OK;
403 }
404
405 static HRESULT WINAPI Gstreamer_transform_EndOfStream(TransformFilter *iface) {
406     GstTfImpl *This = (GstTfImpl*)iface;
407     TRACE("%p\n", This);
408
409     gst_pad_push_event(This->my_src, gst_event_new_eos());
410     return S_OK;
411 }
412
413 static HRESULT WINAPI Gstreamer_transform_BeginFlush(TransformFilter *iface) {
414     GstTfImpl *This = (GstTfImpl*)iface;
415     TRACE("%p\n", This);
416
417     gst_pad_push_event(This->my_src, gst_event_new_flush_start());
418     return S_OK;
419 }
420
421 static HRESULT WINAPI Gstreamer_transform_EndFlush(TransformFilter *iface) {
422     GstTfImpl *This = (GstTfImpl*)iface;
423     TRACE("%p\n", This);
424
425     gst_pad_push_event(This->my_src, gst_event_new_flush_stop());
426     return S_OK;
427 }
428
429 static HRESULT WINAPI Gstreamer_transform_NewSegment(TransformFilter *iface, REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate) {
430     GstTfImpl *This = (GstTfImpl*)iface;
431     TRACE("%p\n", This);
432
433     gst_pad_push_event(This->my_src, gst_event_new_new_segment_full(1,
434                        1.0, dRate, GST_FORMAT_TIME, 0, tStop <= tStart ? -1 : tStop * 100, tStart*100));
435     return S_OK;
436 }
437
438 static HRESULT WINAPI Gstreamer_transform_QOS(TransformFilter *iface, IBaseFilter *sender, Quality qm) {
439     GstTfImpl *This = (GstTfImpl*)iface;
440     REFERENCE_TIME late = qm.Late;
441     if (qm.Late < 0 && -qm.Late > qm.TimeStamp)
442         late = -qm.TimeStamp;
443     gst_pad_push_event(This->my_sink, gst_event_new_qos(1000. / qm.Proportion, late * 100, qm.TimeStamp * 100));
444     return TransformFilterImpl_Notify(iface, sender, qm);
445 }
446
447 static HRESULT Gstreamer_transform_create(IUnknown *punkout, const CLSID *clsid, const char *name, const TransformFilterFuncTable *vtbl, void **obj)
448 {
449     GstTfImpl *This;
450
451     if (FAILED(TransformFilter_Construct(&GSTTf_Vtbl, sizeof(GstTfImpl), clsid, vtbl, (IBaseFilter**)&This)))
452         return E_OUTOFMEMORY;
453     else
454     {
455         ISeekingPassThru *passthru;
456         CoCreateInstance(&CLSID_SeekingPassThru, (IUnknown*)This, CLSCTX_INPROC_SERVER, &IID_IUnknown, (void**)&This->seekthru_unk);
457         IUnknown_QueryInterface(This->seekthru_unk, &IID_ISeekingPassThru, (void**)&passthru);
458         ISeekingPassThru_Init(passthru, FALSE, (IPin*)This->tf.ppPins[0]);
459         ISeekingPassThru_Release(passthru);
460     }
461
462     This->gstreamer_name = name;
463     *obj = This;
464
465     return S_OK;
466 }
467
468 static HRESULT WINAPI Gstreamer_Mp3_QueryConnect(TransformFilter *iface, const AM_MEDIA_TYPE *amt) {
469     GstTfImpl *This = (GstTfImpl*)iface;
470     TRACE("%p %p\n", This, amt);
471     dump_AM_MEDIA_TYPE(amt);
472
473     if ( (!IsEqualGUID(&amt->majortype, &MEDIATYPE_Audio) &&
474           !IsEqualGUID(&amt->majortype, &MEDIATYPE_Stream)) ||
475          (!IsEqualGUID(&amt->subtype, &MEDIASUBTYPE_MPEG1AudioPayload) &&
476           !IsEqualGUID(&amt->subtype, &WMMEDIASUBTYPE_MP3))
477         || !IsEqualGUID(&amt->formattype, &FORMAT_WaveFormatEx))
478         return S_FALSE;
479
480     return S_OK;
481 }
482
483 static HRESULT WINAPI Gstreamer_Mp3_SetMediaType(TransformFilter *tf, PIN_DIRECTION dir, const AM_MEDIA_TYPE *amt) {
484     GstTfImpl *This = (GstTfImpl*)tf;
485     GstCaps *capsin, *capsout;
486     AM_MEDIA_TYPE *outpmt = &This->tf.pmt;
487     WAVEFORMATEX *wfx, *wfxin;
488     HRESULT hr;
489     int layer;
490
491     if (dir != PINDIR_INPUT)
492         return S_OK;
493
494     if (Gstreamer_Mp3_QueryConnect(&This->tf, amt) == S_FALSE || !amt->pbFormat)
495         return VFW_E_TYPE_NOT_ACCEPTED;
496
497     wfxin = (WAVEFORMATEX*)amt->pbFormat;
498     switch (wfxin->wFormatTag) {
499         case WAVE_FORMAT_MPEGLAYER3:
500             layer = 3;
501             break;
502         case WAVE_FORMAT_MPEG: {
503             MPEG1WAVEFORMAT *mpgformat = (MPEG1WAVEFORMAT*)wfxin;
504             layer = mpgformat->fwHeadLayer;
505             break;
506         }
507         default:
508             FIXME("Unhandled tag %x\n", wfxin->wFormatTag);
509             return E_FAIL;
510     }
511
512     FreeMediaType(outpmt);
513     CopyMediaType(outpmt, amt);
514
515     outpmt->subtype = MEDIASUBTYPE_PCM;
516     outpmt->formattype = FORMAT_WaveFormatEx;
517     outpmt->cbFormat = sizeof(*wfx);
518     CoTaskMemFree(outpmt->pbFormat);
519     wfx = CoTaskMemAlloc(outpmt->cbFormat);
520     outpmt->pbFormat = (BYTE*)wfx;
521     wfx->wFormatTag = WAVE_FORMAT_PCM;
522     wfx->wBitsPerSample = 16;
523     wfx->nSamplesPerSec = wfxin->nSamplesPerSec;
524     wfx->nChannels = wfxin->nChannels;
525     wfx->nBlockAlign = wfx->wBitsPerSample * wfx->nChannels / 8;
526     wfx->cbSize = 0;
527     wfx->nAvgBytesPerSec = wfx->nSamplesPerSec * wfx->nBlockAlign;
528
529     capsin = gst_caps_new_simple("audio/mpeg",
530                                  "mpegversion", G_TYPE_INT, 1,
531                                  "layer", G_TYPE_INT, layer,
532                                  "rate", G_TYPE_INT, wfx->nSamplesPerSec,
533                                  "channels", G_TYPE_INT, wfx->nChannels,
534                                  NULL);
535     capsout = gst_caps_new_simple("audio/x-raw-int",
536                                   "endianness", G_TYPE_INT, 1234,
537                                   "signed", G_TYPE_BOOLEAN, 1,
538                                   "width", G_TYPE_INT, 16,
539                                   "depth", G_TYPE_INT, 16,
540                                   "rate", G_TYPE_INT, wfx->nSamplesPerSec,
541                                   "channels", G_TYPE_INT, wfx->nChannels,
542                                    NULL);
543
544     hr = Gstreamer_transform_ConnectInput(This, amt, capsin, capsout);
545     gst_caps_unref(capsin);
546     gst_caps_unref(capsout);
547
548     This->cbBuffer = wfx->nAvgBytesPerSec / 4;
549
550     return hr;
551 }
552
553 static HRESULT WINAPI Gstreamer_Mp3_ConnectInput(TransformFilter *tf, PIN_DIRECTION dir, IPin *pin)
554 {
555     return S_OK;
556 }
557
558 static const TransformFilterFuncTable Gstreamer_Mp3_vtbl = {
559     Gstreamer_transform_DecideBufferSize,
560     Gstreamer_transform_ProcessBegin,
561     Gstreamer_transform_ProcessData,
562     Gstreamer_transform_ProcessEnd,
563     Gstreamer_Mp3_QueryConnect,
564     Gstreamer_Mp3_SetMediaType,
565     Gstreamer_Mp3_ConnectInput,
566     Gstreamer_transform_Cleanup,
567     Gstreamer_transform_EndOfStream,
568     Gstreamer_transform_BeginFlush,
569     Gstreamer_transform_EndFlush,
570     Gstreamer_transform_NewSegment,
571     Gstreamer_transform_QOS
572 };
573
574 IUnknown * CALLBACK Gstreamer_Mp3_create(IUnknown *punkout, HRESULT *phr)
575 {
576     const char *plugin;
577     IUnknown *obj = NULL;
578     if (!Gstreamer_init())
579     {
580         *phr = E_FAIL;
581         return NULL;
582     }
583     plugin = Gstreamer_FindMatch("audio/mpeg, mpegversion=(int) 1");
584     if (!plugin)
585     {
586         *phr = E_FAIL;
587         return NULL;
588     }
589     *phr = Gstreamer_transform_create(punkout, &CLSID_Gstreamer_Mp3, plugin, &Gstreamer_Mp3_vtbl, (LPVOID*)&obj);
590     return obj;
591 }
592
593 static HRESULT WINAPI Gstreamer_YUV_QueryConnect(TransformFilter *iface, const AM_MEDIA_TYPE *amt) {
594     GstTfImpl *This = (GstTfImpl*)iface;
595     TRACE("%p %p\n", This, amt);
596     dump_AM_MEDIA_TYPE(amt);
597
598     if (!IsEqualGUID(&amt->majortype, &MEDIATYPE_Video) ||
599         (!IsEqualGUID(&amt->formattype, &FORMAT_VideoInfo) &&
600          !IsEqualGUID(&amt->formattype, &FORMAT_VideoInfo2)))
601         return S_FALSE;
602     if (memcmp(&amt->subtype.Data2, &MEDIATYPE_Video.Data2, sizeof(GUID) - sizeof(amt->subtype.Data1)))
603         return S_FALSE;
604     switch (amt->subtype.Data1) {
605         case mmioFOURCC('I','4','2','0'):
606         case mmioFOURCC('Y','V','1','2'):
607         case mmioFOURCC('N','V','1','2'):
608         case mmioFOURCC('N','V','2','1'):
609         case mmioFOURCC('Y','U','Y','2'):
610         case mmioFOURCC('Y','V','Y','U'):
611             return S_OK;
612         default:
613             WARN("Unhandled fourcc %s\n", debugstr_an((char*)&amt->subtype.Data1, 4));
614             return S_FALSE;
615     }
616 }
617
618 static HRESULT WINAPI Gstreamer_YUV_ConnectInput(TransformFilter *tf, PIN_DIRECTION dir, IPin *pin)
619 {
620     return S_OK;
621 }
622
623 static HRESULT WINAPI Gstreamer_YUV_SetMediaType(TransformFilter *tf, PIN_DIRECTION dir,  const AM_MEDIA_TYPE *amt) {
624     GstTfImpl *This = (GstTfImpl*)tf;
625     GstCaps *capsin, *capsout;
626     AM_MEDIA_TYPE *outpmt = &This->tf.pmt;
627     HRESULT hr;
628     int avgtime;
629     LONG width, height;
630
631     if (dir != PINDIR_INPUT)
632         return S_OK;
633
634     if (Gstreamer_YUV_QueryConnect(&This->tf, amt) == S_FALSE || !amt->pbFormat)
635         return E_FAIL;
636
637     FreeMediaType(outpmt);
638     CopyMediaType(outpmt, amt);
639
640     if (IsEqualGUID(&amt->formattype, &FORMAT_VideoInfo)) {
641         VIDEOINFOHEADER *vih = (VIDEOINFOHEADER*)outpmt->pbFormat;
642         avgtime = vih->AvgTimePerFrame;
643         width = vih->bmiHeader.biWidth;
644         height = vih->bmiHeader.biHeight;
645         if (vih->bmiHeader.biHeight > 0)
646             vih->bmiHeader.biHeight = -vih->bmiHeader.biHeight;
647         vih->bmiHeader.biBitCount = 24;
648         vih->bmiHeader.biCompression = BI_RGB;
649         vih->bmiHeader.biSizeImage = width * abs(height) * 3;
650     } else {
651         VIDEOINFOHEADER2 *vih = (VIDEOINFOHEADER2*)outpmt->pbFormat;
652         avgtime = vih->AvgTimePerFrame;
653         width = vih->bmiHeader.biWidth;
654         height = vih->bmiHeader.biHeight;
655         if (vih->bmiHeader.biHeight > 0)
656             vih->bmiHeader.biHeight = -vih->bmiHeader.biHeight;
657         vih->bmiHeader.biBitCount = 24;
658         vih->bmiHeader.biCompression = BI_RGB;
659         vih->bmiHeader.biSizeImage = width * abs(height) * 3;
660     }
661     if (!avgtime)
662         avgtime = 10000000 / 30;
663
664     outpmt->subtype = MEDIASUBTYPE_RGB24;
665
666     capsin = gst_caps_new_simple("video/x-raw-yuv",
667                                  "format", GST_TYPE_FOURCC, amt->subtype.Data1,
668                                  "width", G_TYPE_INT, width,
669                                  "height", G_TYPE_INT, height,
670                                  "framerate", GST_TYPE_FRACTION, 10000000, avgtime,
671                                  NULL);
672     capsout = gst_caps_new_simple("video/x-raw-rgb",
673                                   "endianness", G_TYPE_INT, 4321,
674                                   "width", G_TYPE_INT, width,
675                                   "height", G_TYPE_INT, height,
676                                   "framerate", GST_TYPE_FRACTION, 10000000, avgtime,
677                                   "bpp", G_TYPE_INT, 24,
678                                   "depth", G_TYPE_INT, 24,
679                                   "red_mask", G_TYPE_INT, 0xff,
680                                   "green_mask", G_TYPE_INT, 0xff00,
681                                   "blue_mask", G_TYPE_INT, 0xff0000,
682                                    NULL);
683
684     hr = Gstreamer_transform_ConnectInput(This, amt, capsin, capsout);
685     gst_caps_unref(capsin);
686     gst_caps_unref(capsout);
687
688     This->cbBuffer = width * height * 4;
689     return hr;
690 }
691
692 static const TransformFilterFuncTable Gstreamer_YUV_vtbl = {
693     Gstreamer_transform_DecideBufferSize,
694     Gstreamer_transform_ProcessBegin,
695     Gstreamer_transform_ProcessData,
696     Gstreamer_transform_ProcessEnd,
697     Gstreamer_YUV_QueryConnect,
698     Gstreamer_YUV_SetMediaType,
699     Gstreamer_YUV_ConnectInput,
700     Gstreamer_transform_Cleanup,
701     Gstreamer_transform_EndOfStream,
702     Gstreamer_transform_BeginFlush,
703     Gstreamer_transform_EndFlush,
704     Gstreamer_transform_NewSegment,
705     Gstreamer_transform_QOS
706 };
707
708 IUnknown * CALLBACK Gstreamer_YUV_create(IUnknown *punkout, HRESULT *phr)
709 {
710     IUnknown *obj = NULL;
711     if (!Gstreamer_init())
712     {
713         *phr = E_FAIL;
714         return NULL;
715     }
716     *phr = Gstreamer_transform_create(punkout, &CLSID_Gstreamer_YUV, "ffmpegcolorspace", &Gstreamer_YUV_vtbl, (LPVOID*)&obj);
717     return obj;
718 }
719
720 static HRESULT WINAPI Gstreamer_AudioConvert_QueryConnect(TransformFilter *iface, const AM_MEDIA_TYPE *amt) {
721     GstTfImpl *This = (GstTfImpl*)iface;
722     TRACE("%p %p\n", This, amt);
723     dump_AM_MEDIA_TYPE(amt);
724
725     if (!IsEqualGUID(&amt->majortype, &MEDIATYPE_Audio) ||
726         !IsEqualGUID(&amt->subtype, &MEDIASUBTYPE_PCM) ||
727         !IsEqualGUID(&amt->formattype, &FORMAT_WaveFormatEx))
728         return S_FALSE;
729     return S_OK;
730 }
731
732 static HRESULT WINAPI Gstreamer_AudioConvert_ConnectInput(TransformFilter *tf, PIN_DIRECTION dir, IPin *pin)
733 {
734     return S_OK;
735 }
736
737 static HRESULT WINAPI Gstreamer_AudioConvert_SetMediaType(TransformFilter *tf, PIN_DIRECTION dir, const AM_MEDIA_TYPE *amt) {
738     GstTfImpl *This = (GstTfImpl*)tf;
739     GstCaps *capsin, *capsout;
740     AM_MEDIA_TYPE *outpmt = &This->tf.pmt;
741     WAVEFORMATEX *inwfe;
742     WAVEFORMATEX *outwfe;
743     WAVEFORMATEXTENSIBLE *outwfx;
744     HRESULT hr;
745     int inisfloat = 0, indepth;
746
747     if (dir != PINDIR_INPUT)
748         return S_OK;
749
750     if (Gstreamer_AudioConvert_QueryConnect(&This->tf, amt) == S_FALSE || !amt->pbFormat)
751         return E_FAIL;
752
753     FreeMediaType(outpmt);
754     *outpmt = *amt;
755     outpmt->pUnk = NULL;
756     outpmt->cbFormat = sizeof(WAVEFORMATEXTENSIBLE);
757     outpmt->pbFormat = CoTaskMemAlloc(outpmt->cbFormat);
758
759     inwfe = (WAVEFORMATEX*)amt->pbFormat;
760     indepth = inwfe->wBitsPerSample;
761     if (inwfe->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
762         WAVEFORMATEXTENSIBLE *inwfx = (WAVEFORMATEXTENSIBLE*)inwfe;
763         inisfloat = IsEqualGUID(&inwfx->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT);
764         if (inwfx->Samples.wValidBitsPerSample)
765             indepth = inwfx->Samples.wValidBitsPerSample;
766     }
767
768     capsin = gst_caps_new_simple(inisfloat ? "audio/x-raw-float" : "audio/x-raw-int",
769                                  "endianness", G_TYPE_INT, 1234,
770                                  "width", G_TYPE_INT, inwfe->wBitsPerSample,
771                                  "depth", G_TYPE_INT, indepth,
772                                  "channels", G_TYPE_INT, inwfe->nChannels,
773                                  "rate", G_TYPE_INT, inwfe->nSamplesPerSec,
774                                   NULL);
775
776     outwfe = (WAVEFORMATEX*)outpmt->pbFormat;
777     outwfx = (WAVEFORMATEXTENSIBLE*)outwfe;
778     outwfe->wFormatTag = WAVE_FORMAT_EXTENSIBLE;
779     outwfe->nChannels = 2;
780     outwfe->nSamplesPerSec = inwfe->nSamplesPerSec;
781     outwfe->wBitsPerSample = 16;
782     outwfe->nBlockAlign = outwfe->nChannels * outwfe->wBitsPerSample / 8;
783     outwfe->nAvgBytesPerSec = outwfe->nBlockAlign * outwfe->nSamplesPerSec;
784     outwfe->cbSize = sizeof(*outwfx) - sizeof(*outwfe);
785     outwfx->Samples.wValidBitsPerSample = outwfe->wBitsPerSample;
786     outwfx->dwChannelMask = SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT;
787     outwfx->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
788
789     capsout = gst_caps_new_simple("audio/x-raw-int",
790                                   "endianness", G_TYPE_INT, 1234,
791                                   "width", G_TYPE_INT, outwfe->wBitsPerSample,
792                                   "depth", G_TYPE_INT, outwfx->Samples.wValidBitsPerSample,
793                                   "channels", G_TYPE_INT, outwfe->nChannels,
794                                   "rate", G_TYPE_INT, outwfe->nSamplesPerSec,
795                                    NULL);
796
797     hr = Gstreamer_transform_ConnectInput(This, amt, capsin, capsout);
798     gst_caps_unref(capsin);
799     gst_caps_unref(capsout);
800
801     This->cbBuffer = inwfe->nAvgBytesPerSec;
802     return hr;
803 }
804
805 static const TransformFilterFuncTable Gstreamer_AudioConvert_vtbl = {
806     Gstreamer_transform_DecideBufferSize,
807     Gstreamer_transform_ProcessBegin,
808     Gstreamer_transform_ProcessData,
809     Gstreamer_transform_ProcessEnd,
810     Gstreamer_AudioConvert_QueryConnect,
811     Gstreamer_AudioConvert_SetMediaType,
812     Gstreamer_AudioConvert_ConnectInput,
813     Gstreamer_transform_Cleanup,
814     Gstreamer_transform_EndOfStream,
815     Gstreamer_transform_BeginFlush,
816     Gstreamer_transform_EndFlush,
817     Gstreamer_transform_NewSegment,
818     Gstreamer_transform_QOS
819 };
820
821 IUnknown * CALLBACK Gstreamer_AudioConvert_create(IUnknown *punkout, HRESULT *phr)
822 {
823     IUnknown *obj = NULL;
824     if (!Gstreamer_init())
825     {
826         *phr = E_FAIL;
827         return NULL;
828     }
829     *phr = Gstreamer_transform_create(punkout, &CLSID_Gstreamer_AudioConvert, "audioconvert", &Gstreamer_AudioConvert_vtbl, (LPVOID*)&obj);
830     return obj;
831 }
832
833 static HRESULT WINAPI GSTTf_QueryInterface(IBaseFilter * iface, REFIID riid, LPVOID * ppv)
834 {
835     HRESULT hr;
836     GstTfImpl *This = (GstTfImpl*)iface;
837     TRACE("(%p/%p)->(%s, %p)\n", This, iface, debugstr_guid(riid), ppv);
838
839     if (IsEqualIID(riid, &IID_IMediaSeeking) || IsEqualIID(riid, &IID_IMediaPosition))
840         return IUnknown_QueryInterface(This->seekthru_unk, riid, ppv);
841
842     hr = TransformFilterImpl_QueryInterface(iface, riid, ppv);
843
844     return hr;
845 }
846
847 static const IBaseFilterVtbl GSTTf_Vtbl =
848 {
849     GSTTf_QueryInterface,
850     BaseFilterImpl_AddRef,
851     TransformFilterImpl_Release,
852     BaseFilterImpl_GetClassID,
853     TransformFilterImpl_Stop,
854     TransformFilterImpl_Pause,
855     TransformFilterImpl_Run,
856     BaseFilterImpl_GetState,
857     BaseFilterImpl_SetSyncSource,
858     BaseFilterImpl_GetSyncSource,
859     BaseFilterImpl_EnumPins,
860     TransformFilterImpl_FindPin,
861     BaseFilterImpl_QueryFilterInfo,
862     BaseFilterImpl_JoinFilterGraph,
863     BaseFilterImpl_QueryVendorInfo
864 };