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
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"
46 #include "wine/unicode.h"
47 #include "wine/debug.h"
50 DEFINE_GUID(WMMEDIASUBTYPE_MP3, 0x00000055, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
52 WINE_DEFAULT_DEBUG_CHANNEL(gstreamer);
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;
66 if (!GST_IS_ELEMENT_FACTORY(feature))
68 factory = GST_ELEMENT_FACTORY(feature);
69 if (!strstr(gst_element_factory_get_klass(factory), data->type))
71 for (list = gst_element_factory_get_static_pad_templates(factory); list; list = list->next) {
72 GstStaticPadTemplate *pad = (GstStaticPadTemplate*)list->data;
75 if (pad->direction != GST_PAD_SINK)
77 caps = gst_static_caps_get(&pad->static_caps);
78 ret = gst_caps_is_always_compatible(caps, data->caps);
86 static const char *Gstreamer_FindMatch(const char *strcaps)
91 GstElementFactory *bestfactory = NULL;
92 GstCaps *caps = gst_caps_from_string(strcaps);
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;
100 rank = gst_plugin_feature_get_rank(GST_PLUGIN_FEATURE(factory));
101 if (rank > bestrank || !bestrank) {
103 bestfactory = factory;
106 gst_caps_unref(caps);
110 FIXME("Could not find plugin for %s\n", strcaps);
113 return gst_plugin_feature_get_name(GST_PLUGIN_FEATURE(bestfactory));
116 typedef struct GstTfImpl {
118 IUnknown *seekthru_unk;
119 const char *gstreamer_name;
121 GstPad *my_src, *my_sink, *their_src, *their_sink;
125 static HRESULT WINAPI Gstreamer_transform_ProcessBegin(TransformFilter *iface) {
126 GstTfImpl *This = (GstTfImpl*)iface;
129 ret = gst_element_set_state(This->filter, GST_STATE_PLAYING);
130 TRACE("Returned: %i\n", ret);
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)
145 ppropInputRequest->cBuffers = 1;
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;
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);
168 IMediaSample_SetTime(sample, NULL, NULL);
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));
174 hr = BaseOutputPinImpl_Deliver((BaseOutputPin*)This->tf.ppPins[1], sample);
175 gst_buffer_unref(buf);
177 return GST_FLOW_WRONG_STATE;
179 return GST_FLOW_RESEND;
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;
188 TRACE("Requesting buffer\n");
190 hr = BaseOutputPinImpl_GetDeliveryBuffer((BaseOutputPin*)This->tf.ppPins[1], &sample, NULL, NULL, 0);
192 ERR("Could not get output buffer: %08x\n", hr);
193 return GST_FLOW_WRONG_STATE;
195 IMediaSample_SetActualDataLength(sample, size);
196 IMediaSample_GetPointer(sample, &ptr);
197 *buf = gst_app_buffer_new(ptr, size, release_sample, sample);
200 IMediaSample_Release(sample);
201 ERR("Out of memory\n");
202 return GST_FLOW_ERROR;
205 caps = gst_pad_get_caps_reffed(This->my_sink);
206 gst_buffer_set_caps(*buf, caps);
210 static HRESULT WINAPI Gstreamer_transform_ProcessData(TransformFilter *iface, IMediaSample *sample) {
211 GstTfImpl *This = (GstTfImpl*)iface;
212 REFERENCE_TIME tStart, tStop;
217 TRACE("Reading %p\n", sample);
219 EnterCriticalSection(&This->tf.filter.csFilter);
220 IMediaSample_GetPointer(sample, &data);
221 buf = gst_app_buffer_new(data, IMediaSample_GetActualDataLength(sample), release_sample, sample);
223 LeaveCriticalSection(&This->tf.filter.csFilter);
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);
231 buf->timestamp = tStart * 100;
233 buf->duration = (tStop - tStart)*100;
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);
244 WARN("Sending returned: %i\n", ret);
245 if (ret == GST_FLOW_ERROR)
247 if (ret == GST_FLOW_WRONG_STATE)
248 return VFW_E_WRONG_STATE;
249 if (ret == GST_FLOW_RESEND)
254 static HRESULT WINAPI Gstreamer_transform_ProcessEnd(TransformFilter *iface) {
255 GstTfImpl *This = (GstTfImpl*)iface;
258 ret = gst_element_set_state(This->filter, GST_STATE_PAUSED);
259 TRACE("Returned: %i\n", ret);
263 static void Gstreamer_transform_pad_added(GstElement *filter, GstPad *pad, GstTfImpl *This)
266 if (!GST_PAD_IS_SRC(pad))
269 ret = gst_pad_link(pad, This->my_sink);
271 WARN("Failed to link with %i\n", ret);
272 This->their_src = pad;
274 gst_pad_set_active(pad, TRUE);
275 gst_pad_set_active(This->my_sink, TRUE);
278 static HRESULT Gstreamer_transform_ConnectInput(GstTfImpl *This, const AM_MEDIA_TYPE *amt, GstCaps *capsin, GstCaps *capsout) {
280 int done = 0, found = 0, ret;
282 This->filter = gst_element_factory_make(This->gstreamer_name, NULL);
284 FIXME("Could not make %s filter\n", This->gstreamer_name);
287 This->my_src = gst_pad_new(NULL, GST_PAD_SRC);
288 gst_pad_set_element_private (This->my_src, This);
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);
295 ret = gst_pad_set_caps(This->my_src, capsin);
297 WARN("Failed to set caps on own source with %i\n", ret);
301 ret = gst_pad_set_caps(This->my_sink, capsout);
303 WARN("Failed to set caps on own sink with %i\n", ret);
307 it = gst_element_iterate_sink_pads(This->filter);
311 switch (gst_iterator_next(it, &item)) {
312 case GST_ITERATOR_RESYNC:
313 gst_iterator_resync (it);
315 case GST_ITERATOR_OK:
316 This->their_sink = item;
317 case GST_ITERATOR_ERROR:
318 case GST_ITERATOR_DONE:
323 gst_iterator_free(it);
324 if (!This->their_sink) {
325 ERR("Could not find sink on filter %s\n", This->gstreamer_name);
329 it = gst_element_iterate_src_pads(This->filter);
330 gst_iterator_resync(it);
335 switch (gst_iterator_next(it, &item)) {
336 case GST_ITERATOR_RESYNC:
337 gst_iterator_resync (it);
339 case GST_ITERATOR_OK:
340 This->their_src = item;
341 case GST_ITERATOR_ERROR:
342 case GST_ITERATOR_DONE:
347 gst_iterator_free(it);
348 found = !!This->their_src;
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);
353 WARN("Failed to link with %i\n", ret);
358 Gstreamer_transform_pad_added(This->filter, This->their_src, This);
360 if (!gst_pad_is_linked(This->my_sink))
363 TRACE("Connected\n");
367 static HRESULT WINAPI Gstreamer_transform_Cleanup(TransformFilter *tf, PIN_DIRECTION dir) {
368 GstTfImpl *This = (GstTfImpl*)tf;
370 if (dir == PINDIR_INPUT)
373 gst_element_set_state(This->filter, GST_STATE_NULL);
374 gst_object_unref(This->filter);
378 gst_pad_unlink(This->my_src, This->their_sink);
379 gst_object_unref(This->my_src);
382 gst_pad_unlink(This->their_src, This->my_sink);
383 gst_object_unref(This->my_sink);
385 This->my_sink = This->my_src = This->their_sink = This->their_src = NULL;
386 FIXME("%p stub\n", This);
391 static HRESULT WINAPI Gstreamer_transform_EndOfStream(TransformFilter *iface) {
392 GstTfImpl *This = (GstTfImpl*)iface;
395 gst_pad_push_event(This->my_src, gst_event_new_eos());
399 static HRESULT WINAPI Gstreamer_transform_BeginFlush(TransformFilter *iface) {
400 GstTfImpl *This = (GstTfImpl*)iface;
403 gst_pad_push_event(This->my_src, gst_event_new_flush_start());
407 static HRESULT WINAPI Gstreamer_transform_EndFlush(TransformFilter *iface) {
408 GstTfImpl *This = (GstTfImpl*)iface;
411 gst_pad_push_event(This->my_src, gst_event_new_flush_stop());
415 static HRESULT WINAPI Gstreamer_transform_NewSegment(TransformFilter *iface, REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate) {
416 GstTfImpl *This = (GstTfImpl*)iface;
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));
424 static HRESULT Gstreamer_transform_create(IUnknown *punkout, const CLSID *clsid, const char *name, const TransformFilterFuncTable *vtbl, void **obj)
428 if (FAILED(TransformFilter_Construct(&GSTTf_Vtbl, sizeof(GstTfImpl), clsid, vtbl, (IBaseFilter**)&This)))
429 return E_OUTOFMEMORY;
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);
439 This->gstreamer_name = name;
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);
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))
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;
468 if (dir != PINDIR_INPUT)
471 if (Gstreamer_Mp3_QueryConnect(&This->tf, amt) == S_FALSE || !amt->pbFormat)
472 return VFW_E_TYPE_NOT_ACCEPTED;
474 wfxin = (WAVEFORMATEX*)amt->pbFormat;
475 switch (wfxin->wFormatTag) {
476 case WAVE_FORMAT_MPEGLAYER3:
479 case WAVE_FORMAT_MPEG: {
480 MPEG1WAVEFORMAT *mpgformat = (MPEG1WAVEFORMAT*)wfxin;
481 layer = mpgformat->fwHeadLayer;
485 FIXME("Unhandled tag %x\n", wfxin->wFormatTag);
489 FreeMediaType(outpmt);
490 CopyMediaType(outpmt, amt);
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;
504 wfx->nAvgBytesPerSec = wfx->nSamplesPerSec * wfx->nBlockAlign;
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,
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,
521 hr = Gstreamer_transform_ConnectInput(This, amt, capsin, capsout);
522 gst_caps_unref(capsin);
523 gst_caps_unref(capsout);
525 This->cbBuffer = wfx->nAvgBytesPerSec / 4;
530 static HRESULT WINAPI Gstreamer_Mp3_ConnectInput(TransformFilter *tf, PIN_DIRECTION dir, IPin *pin)
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
550 IUnknown * CALLBACK Gstreamer_Mp3_create(IUnknown *punkout, HRESULT *phr)
553 IUnknown *obj = NULL;
554 if (!Gstreamer_init())
559 plugin = Gstreamer_FindMatch("audio/mpeg, mpegversion=(int) 1");
565 *phr = Gstreamer_transform_create(punkout, &CLSID_Gstreamer_Mp3, plugin, &Gstreamer_Mp3_vtbl, (LPVOID*)&obj);
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);
574 if (!IsEqualGUID(&amt->majortype, &MEDIATYPE_Video) ||
575 (!IsEqualGUID(&amt->formattype, &FORMAT_VideoInfo) &&
576 !IsEqualGUID(&amt->formattype, &FORMAT_VideoInfo2)))
578 if (memcmp(&amt->subtype.Data2, &MEDIATYPE_Video.Data2, sizeof(GUID) - sizeof(amt->subtype.Data1)))
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'):
589 WARN("Unhandled fourcc %s\n", debugstr_an((char*)&amt->subtype.Data1, 4));
594 static HRESULT WINAPI Gstreamer_YUV_ConnectInput(TransformFilter *tf, PIN_DIRECTION dir, IPin *pin)
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;
607 if (dir != PINDIR_INPUT)
610 if (Gstreamer_YUV_QueryConnect(&This->tf, amt) == S_FALSE || !amt->pbFormat)
613 FreeMediaType(outpmt);
614 CopyMediaType(outpmt, amt);
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;
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;
636 avgtime = 10000000 / 30;
638 outpmt->subtype = MEDIASUBTYPE_RGB24;
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,
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,
658 hr = Gstreamer_transform_ConnectInput(This, amt, capsin, capsout);
659 gst_caps_unref(capsin);
660 gst_caps_unref(capsout);
662 This->cbBuffer = width * height * 4;
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
681 IUnknown * CALLBACK Gstreamer_YUV_create(IUnknown *punkout, HRESULT *phr)
683 IUnknown *obj = NULL;
684 if (!Gstreamer_init())
689 *phr = Gstreamer_transform_create(punkout, &CLSID_Gstreamer_YUV, "ffmpegcolorspace", &Gstreamer_YUV_vtbl, (LPVOID*)&obj);
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);
698 if (!IsEqualGUID(&amt->majortype, &MEDIATYPE_Audio) ||
699 !IsEqualGUID(&amt->subtype, &MEDIASUBTYPE_PCM) ||
700 !IsEqualGUID(&amt->formattype, &FORMAT_WaveFormatEx))
705 static HRESULT WINAPI Gstreamer_AudioConvert_ConnectInput(TransformFilter *tf, PIN_DIRECTION dir, IPin *pin)
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;
715 WAVEFORMATEX *outwfe;
716 WAVEFORMATEXTENSIBLE *outwfx;
718 int inisfloat = 0, indepth;
720 if (dir != PINDIR_INPUT)
723 if (Gstreamer_AudioConvert_QueryConnect(&This->tf, amt) == S_FALSE || !amt->pbFormat)
726 FreeMediaType(outpmt);
729 outpmt->cbFormat = sizeof(WAVEFORMATEXTENSIBLE);
730 outpmt->pbFormat = CoTaskMemAlloc(outpmt->cbFormat);
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;
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,
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;
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,
770 hr = Gstreamer_transform_ConnectInput(This, amt, capsin, capsout);
772 gst_caps_unref(capsin);
773 gst_caps_unref(capsout);
775 This->cbBuffer = inwfe->nAvgBytesPerSec;
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
794 IUnknown * CALLBACK Gstreamer_AudioConvert_create(IUnknown *punkout, HRESULT *phr)
796 IUnknown *obj = NULL;
797 if (!Gstreamer_init())
802 *phr = Gstreamer_transform_create(punkout, &CLSID_Gstreamer_AudioConvert, "audioconvert", &Gstreamer_AudioConvert_vtbl, (LPVOID*)&obj);
806 HRESULT WINAPI GSTTf_QueryInterface(IBaseFilter * iface, REFIID riid, LPVOID * ppv)
809 GstTfImpl *This = (GstTfImpl*)iface;
810 TRACE("(%p/%p)->(%s, %p)\n", This, iface, debugstr_guid(riid), ppv);
812 if (IsEqualIID(riid, &IID_IMediaSeeking))
813 return IUnknown_QueryInterface(This->seekthru_unk, riid, ppv);
815 hr = TransformFilterImpl_QueryInterface(iface, riid, ppv);
820 static const IBaseFilterVtbl GSTTf_Vtbl =
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