usp10: Free default_language items when freeing script cache.
[wine.git] / dlls / winegstreamer / gsttffilter.c
blob42220088ac7fe96ba1252a80a866bdb327a44aaf
1 /*
2 * GStreamer wrapper filter
4 * Copyright 2010 Maarten Lankhorst for CodeWeavers
5 * Copyright 2010 Aric Stewart for CodeWeavers
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.
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.
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
23 #include "config.h"
25 #include <gst/app/gstappsink.h>
26 #include <gst/app/gstappsrc.h>
27 #include <gst/app/gstappbuffer.h>
29 #include "gst_private.h"
30 #include "gst_guids.h"
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"
44 #include <assert.h>
46 #include "wine/unicode.h"
47 #include "wine/debug.h"
49 #include "initguid.h"
50 DEFINE_GUID(WMMEDIASUBTYPE_MP3, 0x00000055, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
52 WINE_DEFAULT_DEBUG_CHANNEL(gstreamer);
54 struct typeinfo {
55 GstCaps *caps;
56 const char *type;
59 static const IBaseFilterVtbl GSTTf_Vtbl;
61 static gboolean match_element(GstPluginFeature *feature, gpointer gdata) {
62 struct typeinfo *data = (struct typeinfo*)gdata;
63 GstElementFactory *factory;
64 const GList *list;
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;
83 return FALSE;
86 static const char *Gstreamer_FindMatch(const char *strcaps)
88 struct typeinfo data;
89 GList *list, *copy;
90 guint bestrank = 0;
91 GstElementFactory *bestfactory = NULL;
92 GstCaps *caps = gst_caps_from_string(strcaps);
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;
106 gst_caps_unref(caps);
107 g_list_free(copy);
109 if (!bestfactory) {
110 FIXME("Could not find plugin for %s\n", strcaps);
111 return NULL;
113 return gst_plugin_feature_get_name(GST_PLUGIN_FEATURE(bestfactory));
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;
125 static HRESULT WINAPI Gstreamer_transform_ProcessBegin(TransformFilter *iface) {
126 GstTfImpl *This = (GstTfImpl*)iface;
127 int ret;
129 ret = gst_element_set_state(This->filter, GST_STATE_PLAYING);
130 TRACE("Returned: %i\n", ret);
131 return S_OK;
134 static HRESULT WINAPI Gstreamer_transform_DecideBufferSize(TransformFilter *tf, IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES *ppropInputRequest)
136 GstTfImpl *This = (GstTfImpl*)tf;
137 ALLOCATOR_PROPERTIES actual;
139 if (!ppropInputRequest->cbAlign)
140 ppropInputRequest->cbAlign = 1;
142 ppropInputRequest->cbBuffer = This->cbBuffer;
144 if (ppropInputRequest->cBuffers < 2)
145 ppropInputRequest->cBuffers = 2;
147 return IMemAllocator_SetProperties(pAlloc, ppropInputRequest, &actual);
150 static void release_sample(void *data) {
151 TRACE("Releasing %p\n", data);
152 IMediaSample_Release((IMediaSample *)data);
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;
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);
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);
175 else
176 IMediaSample_SetMediaTime(sample, NULL, NULL);
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));
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;
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");
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;
204 IMediaSample_SetActualDataLength(sample, size);
205 IMediaSample_GetPointer(sample, &ptr);
206 *buf = gst_app_buffer_new(ptr, size, release_sample, sample);
208 if (!*buf) {
209 IMediaSample_Release(sample);
210 ERR("Out of memory\n");
211 return GST_FLOW_ERROR;
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;
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);
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;
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;
244 if (IMediaSample_GetMediaTime(sample, &tStart, &tStop) == S_OK) {
245 buf->offset = tStart * 100;
246 buf->offset_end = tStop * 100;
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;
267 static HRESULT WINAPI Gstreamer_transform_ProcessEnd(TransformFilter *iface) {
268 GstTfImpl *This = (GstTfImpl*)iface;
269 int ret;
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;
278 static void Gstreamer_transform_pad_added(GstElement *filter, GstPad *pad, GstTfImpl *This)
280 int ret;
281 if (!GST_PAD_IS_SRC(pad))
282 return;
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;
289 gst_pad_set_active(pad, TRUE);
290 gst_pad_set_active(This->my_sink, TRUE);
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;
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;
302 This->my_src = gst_pad_new(NULL, GST_PAD_SRC);
303 gst_pad_set_element_private (This->my_src, This);
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);
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;
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;
322 it = gst_element_iterate_sink_pads(This->filter);
323 while (!done) {
324 gpointer item;
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;
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;
344 it = gst_element_iterate_src_pads(This->filter);
345 gst_iterator_resync(it);
346 done = 0;
347 while (!done) {
348 gpointer item;
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;
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;
372 if (found)
373 Gstreamer_transform_pad_added(This->filter, This->their_src, This);
375 if (!gst_pad_is_linked(This->my_sink))
376 return E_FAIL;
378 TRACE("Connected\n");
379 return S_OK;
382 static HRESULT WINAPI Gstreamer_transform_Cleanup(TransformFilter *tf, PIN_DIRECTION dir) {
383 GstTfImpl *This = (GstTfImpl*)tf;
385 if (dir == PINDIR_INPUT)
387 if (This->filter) {
388 gst_element_set_state(This->filter, GST_STATE_NULL);
389 gst_object_unref(This->filter);
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);
396 if (This->my_sink) {
397 gst_pad_unlink(This->their_src, This->my_sink);
398 gst_object_unref(This->my_sink);
400 This->my_sink = This->my_src = This->their_sink = This->their_src = NULL;
402 return S_OK;
405 static HRESULT WINAPI Gstreamer_transform_EndOfStream(TransformFilter *iface) {
406 GstTfImpl *This = (GstTfImpl*)iface;
407 TRACE("%p\n", This);
409 gst_pad_push_event(This->my_src, gst_event_new_eos());
410 return S_OK;
413 static HRESULT WINAPI Gstreamer_transform_BeginFlush(TransformFilter *iface) {
414 GstTfImpl *This = (GstTfImpl*)iface;
415 TRACE("%p\n", This);
417 gst_pad_push_event(This->my_src, gst_event_new_flush_start());
418 return S_OK;
421 static HRESULT WINAPI Gstreamer_transform_EndFlush(TransformFilter *iface) {
422 GstTfImpl *This = (GstTfImpl*)iface;
423 TRACE("%p\n", This);
425 gst_pad_push_event(This->my_src, gst_event_new_flush_stop());
426 return S_OK;
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);
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;
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);
447 static HRESULT Gstreamer_transform_create(IUnknown *punkout, const CLSID *clsid, const char *name, const TransformFilterFuncTable *vtbl, void **obj)
449 GstTfImpl *This;
451 if (FAILED(TransformFilter_Construct(&GSTTf_Vtbl, sizeof(GstTfImpl), clsid, vtbl, (IBaseFilter**)&This)))
452 return E_OUTOFMEMORY;
453 else
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);
462 This->gstreamer_name = name;
463 *obj = This;
465 return S_OK;
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);
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;
480 return S_OK;
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;
491 if (dir != PINDIR_INPUT)
492 return S_OK;
494 if (Gstreamer_Mp3_QueryConnect(&This->tf, amt) == S_FALSE || !amt->pbFormat)
495 return VFW_E_TYPE_NOT_ACCEPTED;
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;
507 default:
508 FIXME("Unhandled tag %x\n", wfxin->wFormatTag);
509 return E_FAIL;
512 FreeMediaType(outpmt);
513 CopyMediaType(outpmt, amt);
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;
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);
544 hr = Gstreamer_transform_ConnectInput(This, amt, capsin, capsout);
545 gst_caps_unref(capsin);
546 gst_caps_unref(capsout);
548 This->cbBuffer = wfx->nAvgBytesPerSec / 4;
550 return hr;
553 static HRESULT WINAPI Gstreamer_Mp3_ConnectInput(TransformFilter *tf, PIN_DIRECTION dir, IPin *pin)
555 return S_OK;
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
574 IUnknown * CALLBACK Gstreamer_Mp3_create(IUnknown *punkout, HRESULT *phr)
576 const char *plugin;
577 IUnknown *obj = NULL;
578 if (!Gstreamer_init())
580 *phr = E_FAIL;
581 return NULL;
583 plugin = Gstreamer_FindMatch("audio/mpeg, mpegversion=(int) 1");
584 if (!plugin)
586 *phr = E_FAIL;
587 return NULL;
589 *phr = Gstreamer_transform_create(punkout, &CLSID_Gstreamer_Mp3, plugin, &Gstreamer_Mp3_vtbl, (LPVOID*)&obj);
590 return obj;
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);
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;
618 static HRESULT WINAPI Gstreamer_YUV_ConnectInput(TransformFilter *tf, PIN_DIRECTION dir, IPin *pin)
620 return S_OK;
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;
631 if (dir != PINDIR_INPUT)
632 return S_OK;
634 if (Gstreamer_YUV_QueryConnect(&This->tf, amt) == S_FALSE || !amt->pbFormat)
635 return E_FAIL;
637 FreeMediaType(outpmt);
638 CopyMediaType(outpmt, amt);
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;
661 if (!avgtime)
662 avgtime = 10000000 / 30;
664 outpmt->subtype = MEDIASUBTYPE_RGB24;
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);
684 hr = Gstreamer_transform_ConnectInput(This, amt, capsin, capsout);
685 gst_caps_unref(capsin);
686 gst_caps_unref(capsout);
688 This->cbBuffer = width * height * 4;
689 return hr;
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
708 IUnknown * CALLBACK Gstreamer_YUV_create(IUnknown *punkout, HRESULT *phr)
710 IUnknown *obj = NULL;
711 if (!Gstreamer_init())
713 *phr = E_FAIL;
714 return NULL;
716 *phr = Gstreamer_transform_create(punkout, &CLSID_Gstreamer_YUV, "ffmpegcolorspace", &Gstreamer_YUV_vtbl, (LPVOID*)&obj);
717 return obj;
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);
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;
732 static HRESULT WINAPI Gstreamer_AudioConvert_ConnectInput(TransformFilter *tf, PIN_DIRECTION dir, IPin *pin)
734 return S_OK;
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;
747 if (dir != PINDIR_INPUT)
748 return S_OK;
750 if (Gstreamer_AudioConvert_QueryConnect(&This->tf, amt) == S_FALSE || !amt->pbFormat)
751 return E_FAIL;
753 FreeMediaType(outpmt);
754 *outpmt = *amt;
755 outpmt->pUnk = NULL;
756 outpmt->cbFormat = sizeof(WAVEFORMATEXTENSIBLE);
757 outpmt->pbFormat = CoTaskMemAlloc(outpmt->cbFormat);
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;
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);
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;
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);
797 hr = Gstreamer_transform_ConnectInput(This, amt, capsin, capsout);
798 gst_caps_unref(capsin);
799 gst_caps_unref(capsout);
801 This->cbBuffer = inwfe->nAvgBytesPerSec;
802 return hr;
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
821 IUnknown * CALLBACK Gstreamer_AudioConvert_create(IUnknown *punkout, HRESULT *phr)
823 IUnknown *obj = NULL;
824 if (!Gstreamer_init())
826 *phr = E_FAIL;
827 return NULL;
829 *phr = Gstreamer_transform_create(punkout, &CLSID_Gstreamer_AudioConvert, "audioconvert", &Gstreamer_AudioConvert_vtbl, (LPVOID*)&obj);
830 return obj;
833 static HRESULT WINAPI GSTTf_QueryInterface(IBaseFilter * iface, REFIID riid, LPVOID * ppv)
835 HRESULT hr;
836 GstTfImpl *This = (GstTfImpl*)iface;
837 TRACE("(%p/%p)->(%s, %p)\n", This, iface, debugstr_guid(riid), ppv);
839 if (IsEqualIID(riid, &IID_IMediaSeeking) || IsEqualIID(riid, &IID_IMediaPosition))
840 return IUnknown_QueryInterface(This->seekthru_unk, riid, ppv);
842 hr = TransformFilterImpl_QueryInterface(iface, riid, ppv);
844 return hr;
847 static const IBaseFilterVtbl GSTTf_Vtbl =
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