2 * DirectShow parser filters
4 * Copyright 2010 Maarten Lankhorst for CodeWeavers
5 * Copyright 2010 Aric Stewart for CodeWeavers
6 * Copyright 2019-2020 Zebediah Figura
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23 #include "gst_private.h"
24 #include "gst_guids.h"
33 #include "wmcodecdsp.h"
36 WINE_DEFAULT_DEBUG_CHANNEL(gstreamer
);
38 static const GUID MEDIASUBTYPE_CVID
= {mmioFOURCC('c','v','i','d'), 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};
39 static const GUID MEDIASUBTYPE_MP3
= {WAVE_FORMAT_MPEGLAYER3
, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};
43 struct strmbase_filter filter
;
44 IAMStreamSelect IAMStreamSelect_iface
;
46 struct strmbase_sink sink
;
49 struct parser_source
**sources
;
50 unsigned int source_count
;
55 struct wg_parser
*wg_parser
;
57 /* FIXME: It would be nice to avoid duplicating these with strmbase.
58 * However, synchronization is tricky; we need access to be protected by a
60 bool streaming
, sink_connected
;
64 BOOL (*init_gst
)(struct parser
*filter
);
65 HRESULT (*source_query_accept
)(struct parser_source
*pin
, const AM_MEDIA_TYPE
*mt
);
66 HRESULT (*source_get_media_type
)(struct parser_source
*pin
, unsigned int index
, AM_MEDIA_TYPE
*mt
);
71 struct strmbase_source pin
;
72 IQualityControl IQualityControl_iface
;
74 struct wg_parser_stream
*wg_stream
;
78 CRITICAL_SECTION flushing_cs
;
82 static inline struct parser
*impl_from_strmbase_filter(struct strmbase_filter
*iface
)
84 return CONTAINING_RECORD(iface
, struct parser
, filter
);
87 static const IMediaSeekingVtbl GST_Seeking_Vtbl
;
88 static const IQualityControlVtbl GSTOutPin_QualityControl_Vtbl
;
90 static struct parser_source
*create_pin(struct parser
*filter
,
91 struct wg_parser_stream
*stream
, const WCHAR
*name
);
92 static HRESULT
GST_RemoveOutputPins(struct parser
*This
);
93 static HRESULT WINAPI
GST_ChangeCurrent(IMediaSeeking
*iface
);
94 static HRESULT WINAPI
GST_ChangeStop(IMediaSeeking
*iface
);
95 static HRESULT WINAPI
GST_ChangeRate(IMediaSeeking
*iface
);
97 static bool amt_from_wg_format_audio(AM_MEDIA_TYPE
*mt
, const struct wg_format
*format
)
99 mt
->majortype
= MEDIATYPE_Audio
;
100 mt
->formattype
= FORMAT_WaveFormatEx
;
102 switch (format
->u
.audio
.format
)
104 case WG_AUDIO_FORMAT_UNKNOWN
:
107 case WG_AUDIO_FORMAT_MPEG1_LAYER1
:
108 case WG_AUDIO_FORMAT_MPEG1_LAYER2
:
110 MPEG1WAVEFORMAT
*wave_format
;
112 if (!(wave_format
= CoTaskMemAlloc(sizeof(*wave_format
))))
114 memset(wave_format
, 0, sizeof(*wave_format
));
116 mt
->subtype
= MEDIASUBTYPE_MPEG1AudioPayload
;
117 mt
->cbFormat
= sizeof(*wave_format
);
118 mt
->pbFormat
= (BYTE
*)wave_format
;
119 wave_format
->wfx
.wFormatTag
= WAVE_FORMAT_MPEG
;
120 wave_format
->wfx
.nChannels
= format
->u
.audio
.channels
;
121 wave_format
->wfx
.nSamplesPerSec
= format
->u
.audio
.rate
;
122 wave_format
->wfx
.cbSize
= sizeof(*wave_format
) - sizeof(WAVEFORMATEX
);
123 wave_format
->fwHeadLayer
= (format
->u
.audio
.format
== WG_AUDIO_FORMAT_MPEG1_LAYER1
? 1 : 2);
127 case WG_AUDIO_FORMAT_MPEG1_LAYER3
:
129 MPEGLAYER3WAVEFORMAT
*wave_format
;
131 if (!(wave_format
= CoTaskMemAlloc(sizeof(*wave_format
))))
133 memset(wave_format
, 0, sizeof(*wave_format
));
135 mt
->subtype
= MEDIASUBTYPE_MP3
;
136 mt
->cbFormat
= sizeof(*wave_format
);
137 mt
->pbFormat
= (BYTE
*)wave_format
;
138 wave_format
->wfx
.wFormatTag
= WAVE_FORMAT_MPEGLAYER3
;
139 wave_format
->wfx
.nChannels
= format
->u
.audio
.channels
;
140 wave_format
->wfx
.nSamplesPerSec
= format
->u
.audio
.rate
;
141 wave_format
->wfx
.cbSize
= sizeof(*wave_format
) - sizeof(WAVEFORMATEX
);
142 /* FIXME: We can't get most of the MPEG data from the caps. We may have
143 * to manually parse the header. */
144 wave_format
->wID
= MPEGLAYER3_ID_MPEG
;
145 wave_format
->fdwFlags
= MPEGLAYER3_FLAG_PADDING_ON
;
146 wave_format
->nFramesPerBlock
= 1;
147 wave_format
->nCodecDelay
= 1393;
151 case WG_AUDIO_FORMAT_U8
:
152 case WG_AUDIO_FORMAT_S16LE
:
153 case WG_AUDIO_FORMAT_S24LE
:
154 case WG_AUDIO_FORMAT_S32LE
:
155 case WG_AUDIO_FORMAT_F32LE
:
156 case WG_AUDIO_FORMAT_F64LE
:
177 assert(format
->u
.audio
.format
< ARRAY_SIZE(format_table
));
178 is_float
= format_table
[format
->u
.audio
.format
].is_float
;
179 depth
= format_table
[format
->u
.audio
.format
].depth
;
181 if (is_float
|| format
->u
.audio
.channels
> 2)
183 WAVEFORMATEXTENSIBLE
*wave_format
;
185 if (!(wave_format
= CoTaskMemAlloc(sizeof(*wave_format
))))
187 memset(wave_format
, 0, sizeof(*wave_format
));
189 mt
->subtype
= is_float
? MEDIASUBTYPE_IEEE_FLOAT
: MEDIASUBTYPE_PCM
;
190 mt
->bFixedSizeSamples
= TRUE
;
191 mt
->pbFormat
= (BYTE
*)wave_format
;
192 mt
->cbFormat
= sizeof(*wave_format
);
193 wave_format
->Format
.wFormatTag
= WAVE_FORMAT_EXTENSIBLE
;
194 wave_format
->Format
.nChannels
= format
->u
.audio
.channels
;
195 wave_format
->Format
.nSamplesPerSec
= format
->u
.audio
.rate
;
196 wave_format
->Format
.nAvgBytesPerSec
= format
->u
.audio
.rate
* format
->u
.audio
.channels
* depth
/ 8;
197 wave_format
->Format
.nBlockAlign
= format
->u
.audio
.channels
* depth
/ 8;
198 wave_format
->Format
.wBitsPerSample
= depth
;
199 wave_format
->Format
.cbSize
= sizeof(*wave_format
) - sizeof(WAVEFORMATEX
);
200 wave_format
->Samples
.wValidBitsPerSample
= depth
;
201 wave_format
->dwChannelMask
= format
->u
.audio
.channel_mask
;
202 wave_format
->SubFormat
= is_float
? KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
: KSDATAFORMAT_SUBTYPE_PCM
;
203 mt
->lSampleSize
= wave_format
->Format
.nBlockAlign
;
207 WAVEFORMATEX
*wave_format
;
209 if (!(wave_format
= CoTaskMemAlloc(sizeof(*wave_format
))))
211 memset(wave_format
, 0, sizeof(*wave_format
));
213 mt
->subtype
= MEDIASUBTYPE_PCM
;
214 mt
->bFixedSizeSamples
= TRUE
;
215 mt
->pbFormat
= (BYTE
*)wave_format
;
216 mt
->cbFormat
= sizeof(*wave_format
);
217 wave_format
->wFormatTag
= WAVE_FORMAT_PCM
;
218 wave_format
->nChannels
= format
->u
.audio
.channels
;
219 wave_format
->nSamplesPerSec
= format
->u
.audio
.rate
;
220 wave_format
->nAvgBytesPerSec
= format
->u
.audio
.rate
* format
->u
.audio
.channels
* depth
/ 8;
221 wave_format
->nBlockAlign
= format
->u
.audio
.channels
* depth
/ 8;
222 wave_format
->wBitsPerSample
= depth
;
223 wave_format
->cbSize
= 0;
224 mt
->lSampleSize
= wave_format
->nBlockAlign
;
234 #define ALIGN(n, alignment) (((n) + (alignment) - 1) & ~((alignment) - 1))
236 static unsigned int get_image_size(const struct wg_format
*format
)
238 unsigned int width
= format
->u
.video
.width
, height
= format
->u
.video
.height
;
240 switch (format
->u
.video
.format
)
242 case WG_VIDEO_FORMAT_BGRA
:
243 case WG_VIDEO_FORMAT_BGRx
:
244 case WG_VIDEO_FORMAT_AYUV
:
245 return width
* height
* 4;
247 case WG_VIDEO_FORMAT_BGR
:
248 return ALIGN(width
* 3, 4) * height
;
250 case WG_VIDEO_FORMAT_RGB15
:
251 case WG_VIDEO_FORMAT_RGB16
:
252 case WG_VIDEO_FORMAT_UYVY
:
253 case WG_VIDEO_FORMAT_YUY2
:
254 case WG_VIDEO_FORMAT_YVYU
:
255 return ALIGN(width
* 2, 4) * height
;
257 case WG_VIDEO_FORMAT_I420
:
258 case WG_VIDEO_FORMAT_YV12
:
259 return ALIGN(width
, 4) * ALIGN(height
, 2) /* Y plane */
260 + 2 * ALIGN((width
+ 1) / 2, 4) * ((height
+ 1) / 2); /* U and V planes */
262 case WG_VIDEO_FORMAT_NV12
:
263 return ALIGN(width
, 4) * ALIGN(height
, 2) /* Y plane */
264 + ALIGN(width
, 4) * ((height
+ 1) / 2); /* U/V plane */
266 case WG_VIDEO_FORMAT_CINEPAK
:
267 /* Both ffmpeg's encoder and a Cinepak file seen in the wild report
268 * 24 bpp. ffmpeg sets biSizeImage as below; others may be smaller,
269 * but as long as every sample fits into our allocator, we're fine. */
270 return width
* height
* 3;
272 case WG_VIDEO_FORMAT_UNKNOWN
:
280 static bool amt_from_wg_format_video(AM_MEDIA_TYPE
*mt
, const struct wg_format
*format
)
291 {&MEDIASUBTYPE_ARGB32
, BI_RGB
, 32},
292 {&MEDIASUBTYPE_RGB32
, BI_RGB
, 32},
293 {&MEDIASUBTYPE_RGB24
, BI_RGB
, 24},
294 {&MEDIASUBTYPE_RGB555
, BI_RGB
, 16},
295 {&MEDIASUBTYPE_RGB565
, BI_BITFIELDS
, 16},
296 {&MEDIASUBTYPE_AYUV
, mmioFOURCC('A','Y','U','V'), 32},
297 {&MEDIASUBTYPE_I420
, mmioFOURCC('I','4','2','0'), 12},
298 {&MEDIASUBTYPE_NV12
, mmioFOURCC('N','V','1','2'), 12},
299 {&MEDIASUBTYPE_UYVY
, mmioFOURCC('U','Y','V','Y'), 16},
300 {&MEDIASUBTYPE_YUY2
, mmioFOURCC('Y','U','Y','2'), 16},
301 {&MEDIASUBTYPE_YV12
, mmioFOURCC('Y','V','1','2'), 12},
302 {&MEDIASUBTYPE_YVYU
, mmioFOURCC('Y','V','Y','U'), 16},
303 {&MEDIASUBTYPE_CVID
, mmioFOURCC('C','V','I','D'), 24},
306 VIDEOINFO
*video_format
;
309 if (format
->u
.video
.format
== WG_VIDEO_FORMAT_UNKNOWN
)
312 if (!(video_format
= CoTaskMemAlloc(sizeof(*video_format
))))
315 assert(format
->u
.video
.format
< ARRAY_SIZE(format_table
));
317 mt
->majortype
= MEDIATYPE_Video
;
318 mt
->subtype
= *format_table
[format
->u
.video
.format
].subtype
;
319 mt
->bTemporalCompression
= TRUE
;
321 mt
->formattype
= FORMAT_VideoInfo
;
322 mt
->cbFormat
= sizeof(VIDEOINFOHEADER
);
323 mt
->pbFormat
= (BYTE
*)video_format
;
325 memset(video_format
, 0, sizeof(*video_format
));
327 if ((frame_time
= MulDiv(10000000, format
->u
.video
.fps_d
, format
->u
.video
.fps_n
)) != -1)
328 video_format
->AvgTimePerFrame
= frame_time
;
329 video_format
->bmiHeader
.biSize
= sizeof(BITMAPINFOHEADER
);
330 video_format
->bmiHeader
.biWidth
= format
->u
.video
.width
;
331 video_format
->bmiHeader
.biHeight
= format
->u
.video
.height
;
332 video_format
->bmiHeader
.biPlanes
= 1;
333 video_format
->bmiHeader
.biBitCount
= format_table
[format
->u
.video
.format
].depth
;
334 video_format
->bmiHeader
.biCompression
= format_table
[format
->u
.video
.format
].compression
;
335 video_format
->bmiHeader
.biSizeImage
= get_image_size(format
);
337 if (format
->u
.video
.format
== WG_VIDEO_FORMAT_RGB16
)
339 mt
->cbFormat
= offsetof(VIDEOINFO
, dwBitMasks
[3]);
340 video_format
->dwBitMasks
[iRED
] = 0xf800;
341 video_format
->dwBitMasks
[iGREEN
] = 0x07e0;
342 video_format
->dwBitMasks
[iBLUE
] = 0x001f;
348 static bool amt_from_wg_format(AM_MEDIA_TYPE
*mt
, const struct wg_format
*format
)
350 memset(mt
, 0, sizeof(*mt
));
352 switch (format
->major_type
)
354 case WG_MAJOR_TYPE_UNKNOWN
:
357 case WG_MAJOR_TYPE_AUDIO
:
358 return amt_from_wg_format_audio(mt
, format
);
360 case WG_MAJOR_TYPE_VIDEO
:
361 return amt_from_wg_format_video(mt
, format
);
368 static bool amt_to_wg_format_audio(const AM_MEDIA_TYPE
*mt
, struct wg_format
*format
)
374 enum wg_audio_format format
;
378 {&MEDIASUBTYPE_PCM
, 8, WG_AUDIO_FORMAT_U8
},
379 {&MEDIASUBTYPE_PCM
, 16, WG_AUDIO_FORMAT_S16LE
},
380 {&MEDIASUBTYPE_PCM
, 24, WG_AUDIO_FORMAT_S24LE
},
381 {&MEDIASUBTYPE_PCM
, 32, WG_AUDIO_FORMAT_S32LE
},
382 {&MEDIASUBTYPE_IEEE_FLOAT
, 32, WG_AUDIO_FORMAT_F32LE
},
383 {&MEDIASUBTYPE_IEEE_FLOAT
, 64, WG_AUDIO_FORMAT_F64LE
},
386 const WAVEFORMATEX
*audio_format
= (const WAVEFORMATEX
*)mt
->pbFormat
;
389 if (!IsEqualGUID(&mt
->formattype
, &FORMAT_WaveFormatEx
))
391 FIXME("Unknown format type %s.\n", debugstr_guid(&mt
->formattype
));
394 if (mt
->cbFormat
< sizeof(WAVEFORMATEX
) || !mt
->pbFormat
)
396 ERR("Unexpected format size %u.\n", mt
->cbFormat
);
400 format
->major_type
= WG_MAJOR_TYPE_AUDIO
;
401 format
->u
.audio
.channels
= audio_format
->nChannels
;
402 format
->u
.audio
.rate
= audio_format
->nSamplesPerSec
;
404 if (audio_format
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
)
406 const WAVEFORMATEXTENSIBLE
*ext_format
= (const WAVEFORMATEXTENSIBLE
*)mt
->pbFormat
;
408 format
->u
.audio
.channel_mask
= ext_format
->dwChannelMask
;
412 if (audio_format
->nChannels
== 1)
413 format
->u
.audio
.channel_mask
= KSAUDIO_SPEAKER_MONO
;
414 else if (audio_format
->nChannels
== 2)
415 format
->u
.audio
.channel_mask
= KSAUDIO_SPEAKER_STEREO
;
418 ERR("Unexpected channel count %u.\n", audio_format
->nChannels
);
423 for (i
= 0; i
< ARRAY_SIZE(format_map
); ++i
)
425 if (IsEqualGUID(&mt
->subtype
, format_map
[i
].subtype
)
426 && audio_format
->wBitsPerSample
== format_map
[i
].depth
)
428 format
->u
.audio
.format
= format_map
[i
].format
;
433 FIXME("Unknown subtype %s, depth %u.\n", debugstr_guid(&mt
->subtype
), audio_format
->wBitsPerSample
);
437 static bool amt_to_wg_format_audio_mpeg1(const AM_MEDIA_TYPE
*mt
, struct wg_format
*format
)
439 const MPEG1WAVEFORMAT
*audio_format
= (const MPEG1WAVEFORMAT
*)mt
->pbFormat
;
441 if (!IsEqualGUID(&mt
->formattype
, &FORMAT_WaveFormatEx
))
443 FIXME("Unknown format type %s.\n", debugstr_guid(&mt
->formattype
));
446 if (mt
->cbFormat
< sizeof(*audio_format
) || !mt
->pbFormat
)
448 ERR("Unexpected format size %u.\n", mt
->cbFormat
);
452 format
->major_type
= WG_MAJOR_TYPE_AUDIO
;
453 format
->u
.audio
.channels
= audio_format
->wfx
.nChannels
;
454 format
->u
.audio
.rate
= audio_format
->wfx
.nSamplesPerSec
;
455 if (audio_format
->fwHeadLayer
== 1)
456 format
->u
.audio
.format
= WG_AUDIO_FORMAT_MPEG1_LAYER1
;
457 else if (audio_format
->fwHeadLayer
== 2)
458 format
->u
.audio
.format
= WG_AUDIO_FORMAT_MPEG1_LAYER2
;
459 else if (audio_format
->fwHeadLayer
== 3)
460 format
->u
.audio
.format
= WG_AUDIO_FORMAT_MPEG1_LAYER3
;
466 static bool amt_to_wg_format_audio_mpeg1_layer3(const AM_MEDIA_TYPE
*mt
, struct wg_format
*format
)
468 const MPEGLAYER3WAVEFORMAT
*audio_format
= (const MPEGLAYER3WAVEFORMAT
*)mt
->pbFormat
;
470 if (!IsEqualGUID(&mt
->formattype
, &FORMAT_WaveFormatEx
))
472 FIXME("Unknown format type %s.\n", debugstr_guid(&mt
->formattype
));
475 if (mt
->cbFormat
< sizeof(*audio_format
) || !mt
->pbFormat
)
477 ERR("Unexpected format size %u.\n", mt
->cbFormat
);
481 format
->major_type
= WG_MAJOR_TYPE_AUDIO
;
482 format
->u
.audio
.channels
= audio_format
->wfx
.nChannels
;
483 format
->u
.audio
.rate
= audio_format
->wfx
.nSamplesPerSec
;
484 format
->u
.audio
.format
= WG_AUDIO_FORMAT_MPEG1_LAYER3
;
488 static bool amt_to_wg_format_video(const AM_MEDIA_TYPE
*mt
, struct wg_format
*format
)
493 enum wg_video_format format
;
497 {&MEDIASUBTYPE_ARGB32
, WG_VIDEO_FORMAT_BGRA
},
498 {&MEDIASUBTYPE_RGB32
, WG_VIDEO_FORMAT_BGRx
},
499 {&MEDIASUBTYPE_RGB24
, WG_VIDEO_FORMAT_BGR
},
500 {&MEDIASUBTYPE_RGB555
, WG_VIDEO_FORMAT_RGB15
},
501 {&MEDIASUBTYPE_RGB565
, WG_VIDEO_FORMAT_RGB16
},
502 {&MEDIASUBTYPE_AYUV
, WG_VIDEO_FORMAT_AYUV
},
503 {&MEDIASUBTYPE_I420
, WG_VIDEO_FORMAT_I420
},
504 {&MEDIASUBTYPE_NV12
, WG_VIDEO_FORMAT_NV12
},
505 {&MEDIASUBTYPE_UYVY
, WG_VIDEO_FORMAT_UYVY
},
506 {&MEDIASUBTYPE_YUY2
, WG_VIDEO_FORMAT_YUY2
},
507 {&MEDIASUBTYPE_YV12
, WG_VIDEO_FORMAT_YV12
},
508 {&MEDIASUBTYPE_YVYU
, WG_VIDEO_FORMAT_YVYU
},
509 {&MEDIASUBTYPE_CVID
, WG_VIDEO_FORMAT_CINEPAK
},
512 const VIDEOINFOHEADER
*video_format
= (const VIDEOINFOHEADER
*)mt
->pbFormat
;
515 if (!IsEqualGUID(&mt
->formattype
, &FORMAT_VideoInfo
))
517 FIXME("Unknown format type %s.\n", debugstr_guid(&mt
->formattype
));
520 if (mt
->cbFormat
< sizeof(VIDEOINFOHEADER
) || !mt
->pbFormat
)
522 ERR("Unexpected format size %u.\n", mt
->cbFormat
);
526 format
->major_type
= WG_MAJOR_TYPE_VIDEO
;
527 format
->u
.video
.width
= video_format
->bmiHeader
.biWidth
;
528 format
->u
.video
.height
= video_format
->bmiHeader
.biHeight
;
529 format
->u
.video
.fps_n
= 10000000;
530 format
->u
.video
.fps_d
= video_format
->AvgTimePerFrame
;
532 for (i
= 0; i
< ARRAY_SIZE(format_map
); ++i
)
534 if (IsEqualGUID(&mt
->subtype
, format_map
[i
].subtype
))
536 format
->u
.video
.format
= format_map
[i
].format
;
541 FIXME("Unknown subtype %s.\n", debugstr_guid(&mt
->subtype
));
545 static bool amt_to_wg_format(const AM_MEDIA_TYPE
*mt
, struct wg_format
*format
)
547 memset(format
, 0, sizeof(*format
));
549 if (IsEqualGUID(&mt
->majortype
, &MEDIATYPE_Video
))
550 return amt_to_wg_format_video(mt
, format
);
551 if (IsEqualGUID(&mt
->majortype
, &MEDIATYPE_Audio
))
553 if (IsEqualGUID(&mt
->subtype
, &MEDIASUBTYPE_MPEG1AudioPayload
))
554 return amt_to_wg_format_audio_mpeg1(mt
, format
);
555 if (IsEqualGUID(&mt
->subtype
, &MEDIASUBTYPE_MP3
))
556 return amt_to_wg_format_audio_mpeg1_layer3(mt
, format
);
557 return amt_to_wg_format_audio(mt
, format
);
560 FIXME("Unknown major type %s.\n", debugstr_guid(&mt
->majortype
));
565 * scale_uint64() is based on gst_util_scale_int() from GStreamer, which is
566 * covered by the following license:
569 * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
570 * 2000 Wim Taymans <wtay@chello.be>
571 * 2002 Thomas Vander Stichele <thomas@apestaart.org>
572 * 2004 Wim Taymans <wim@fluendo.com>
573 * 2015 Jan Schmidt <jan@centricular.com>
575 * gstutils.c: Utility functions
577 * This library is free software; you can redistribute it and/or
578 * modify it under the terms of the GNU Library General Public
579 * License as published by the Free Software Foundation; either
580 * version 2 of the License, or (at your option) any later version.
582 * This library is distributed in the hope that it will be useful,
583 * but WITHOUT ANY WARRANTY; without even the implied warranty of
584 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
585 * Library General Public License for more details.
587 * You should have received a copy of the GNU Library General Public
588 * License along with this library; if not, write to the
589 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
590 * Boston, MA 02110-1301, USA.
592 static uint64_t scale_uint64(uint64_t value
, uint32_t numerator
, uint32_t denominator
)
594 ULARGE_INTEGER i
, high
, low
;
600 low
.QuadPart
= i
.u
.LowPart
* numerator
;
601 high
.QuadPart
= i
.u
.HighPart
* numerator
+ low
.u
.HighPart
;
604 if (high
.u
.HighPart
>= denominator
)
607 low
.QuadPart
+= (high
.QuadPart
% denominator
) << 32;
608 return ((high
.QuadPart
/ denominator
) << 32) + (low
.QuadPart
/ denominator
);
611 /* Fill and send a single IMediaSample. */
612 static HRESULT
send_sample(struct parser_source
*pin
, IMediaSample
*sample
,
613 const struct wg_parser_event
*event
, uint32_t offset
, uint32_t size
, DWORD bytes_per_second
)
618 TRACE("offset %u, size %u, sample size %u\n", offset
, size
, IMediaSample_GetSize(sample
));
620 hr
= IMediaSample_SetActualDataLength(sample
, size
);
622 WARN("SetActualDataLength failed: %08x\n", hr
);
626 IMediaSample_GetPointer(sample
, &ptr
);
628 if (!unix_funcs
->wg_parser_stream_copy_buffer(pin
->wg_stream
, ptr
, offset
, size
))
630 /* The GStreamer pin has been flushed. */
634 if (event
->u
.buffer
.has_pts
)
636 REFERENCE_TIME start_pts
= event
->u
.buffer
.pts
;
639 start_pts
+= scale_uint64(offset
, 10000000, bytes_per_second
);
640 start_pts
-= pin
->seek
.llCurrent
;
641 start_pts
*= pin
->seek
.dRate
;
643 if (event
->u
.buffer
.has_duration
)
645 REFERENCE_TIME end_pts
= event
->u
.buffer
.pts
+ event
->u
.buffer
.duration
;
647 if (offset
+ size
< event
->u
.buffer
.size
)
648 end_pts
= event
->u
.buffer
.pts
+ scale_uint64(offset
+ size
, 10000000, bytes_per_second
);
649 end_pts
-= pin
->seek
.llCurrent
;
650 end_pts
*= pin
->seek
.dRate
;
652 IMediaSample_SetTime(sample
, &start_pts
, &end_pts
);
653 IMediaSample_SetMediaTime(sample
, &start_pts
, &end_pts
);
657 IMediaSample_SetTime(sample
, &start_pts
, NULL
);
658 IMediaSample_SetMediaTime(sample
, NULL
, NULL
);
663 IMediaSample_SetTime(sample
, NULL
, NULL
);
664 IMediaSample_SetMediaTime(sample
, NULL
, NULL
);
667 IMediaSample_SetDiscontinuity(sample
, !offset
&& event
->u
.buffer
.discontinuity
);
668 IMediaSample_SetPreroll(sample
, event
->u
.buffer
.preroll
);
669 IMediaSample_SetSyncPoint(sample
, !event
->u
.buffer
.delta
);
671 if (!pin
->pin
.pin
.peer
)
672 hr
= VFW_E_NOT_CONNECTED
;
674 hr
= IMemInputPin_Receive(pin
->pin
.pMemInputPin
, sample
);
676 TRACE("sending sample returned: %08x\n", hr
);
681 /* Send a single GStreamer buffer (splitting it into multiple IMediaSamples if
683 static void send_buffer(struct parser_source
*pin
, const struct wg_parser_event
*event
)
686 IMediaSample
*sample
;
688 if (IsEqualGUID(&pin
->pin
.pin
.mt
.formattype
, &FORMAT_WaveFormatEx
)
689 && (IsEqualGUID(&pin
->pin
.pin
.mt
.subtype
, &MEDIASUBTYPE_PCM
)
690 || IsEqualGUID(&pin
->pin
.pin
.mt
.subtype
, &MEDIASUBTYPE_IEEE_FLOAT
)))
692 WAVEFORMATEX
*format
= (WAVEFORMATEX
*)pin
->pin
.pin
.mt
.pbFormat
;
695 while (offset
< event
->u
.buffer
.size
)
699 hr
= BaseOutputPinImpl_GetDeliveryBuffer(&pin
->pin
, &sample
, NULL
, NULL
, 0);
703 if (hr
!= VFW_E_NOT_CONNECTED
)
704 ERR("Could not get a delivery buffer (%x), returning GST_FLOW_FLUSHING\n", hr
);
708 advance
= min(IMediaSample_GetSize(sample
), event
->u
.buffer
.size
- offset
);
710 hr
= send_sample(pin
, sample
, event
, offset
, advance
, format
->nAvgBytesPerSec
);
712 IMediaSample_Release(sample
);
722 hr
= BaseOutputPinImpl_GetDeliveryBuffer(&pin
->pin
, &sample
, NULL
, NULL
, 0);
726 if (hr
!= VFW_E_NOT_CONNECTED
)
727 ERR("Could not get a delivery buffer (%x), returning GST_FLOW_FLUSHING\n", hr
);
731 hr
= send_sample(pin
, sample
, event
, 0, event
->u
.buffer
.size
, 0);
733 IMediaSample_Release(sample
);
737 unix_funcs
->wg_parser_stream_release_buffer(pin
->wg_stream
);
740 static DWORD CALLBACK
stream_thread(void *arg
)
742 struct parser_source
*pin
= arg
;
743 struct parser
*filter
= impl_from_strmbase_filter(pin
->pin
.pin
.filter
);
745 TRACE("Starting streaming thread for pin %p.\n", pin
);
747 while (filter
->streaming
)
749 struct wg_parser_event event
;
751 EnterCriticalSection(&pin
->flushing_cs
);
753 if (!unix_funcs
->wg_parser_stream_get_event(pin
->wg_stream
, &event
))
755 LeaveCriticalSection(&pin
->flushing_cs
);
759 TRACE("Got event of type %#x.\n", event
.type
);
763 case WG_PARSER_EVENT_BUFFER
:
764 send_buffer(pin
, &event
);
767 case WG_PARSER_EVENT_EOS
:
768 IPin_EndOfStream(pin
->pin
.pin
.peer
);
771 case WG_PARSER_EVENT_SEGMENT
:
772 IPin_NewSegment(pin
->pin
.pin
.peer
, event
.u
.segment
.position
,
773 event
.u
.segment
.stop
, event
.u
.segment
.rate
);
776 case WG_PARSER_EVENT_NONE
:
780 LeaveCriticalSection(&pin
->flushing_cs
);
783 TRACE("Streaming stopped; exiting.\n");
787 static DWORD CALLBACK
read_thread(void *arg
)
789 struct parser
*filter
= arg
;
791 TRACE("Starting read thread for filter %p.\n", filter
);
793 while (filter
->sink_connected
)
800 if (!unix_funcs
->wg_parser_get_read_request(filter
->wg_parser
, &data
, &offset
, &size
))
802 hr
= IAsyncReader_SyncRead(filter
->reader
, offset
, size
, data
);
803 unix_funcs
->wg_parser_complete_read_request(filter
->wg_parser
, SUCCEEDED(hr
));
806 TRACE("Streaming stopped; exiting.\n");
810 static inline struct parser_source
*impl_from_IMediaSeeking(IMediaSeeking
*iface
)
812 return CONTAINING_RECORD(iface
, struct parser_source
, seek
.IMediaSeeking_iface
);
815 static struct strmbase_pin
*parser_get_pin(struct strmbase_filter
*base
, unsigned int index
)
817 struct parser
*filter
= impl_from_strmbase_filter(base
);
819 if (filter
->enum_sink_first
)
822 return &filter
->sink
.pin
;
823 else if (index
<= filter
->source_count
)
824 return &filter
->sources
[index
- 1]->pin
.pin
;
828 if (index
< filter
->source_count
)
829 return &filter
->sources
[index
]->pin
.pin
;
830 else if (index
== filter
->source_count
)
831 return &filter
->sink
.pin
;
836 static void parser_destroy(struct strmbase_filter
*iface
)
838 struct parser
*filter
= impl_from_strmbase_filter(iface
);
841 /* Don't need to clean up output pins, disconnecting input pin will do that */
842 if (filter
->sink
.pin
.peer
)
844 hr
= IPin_Disconnect(filter
->sink
.pin
.peer
);
846 hr
= IPin_Disconnect(&filter
->sink
.pin
.IPin_iface
);
851 IAsyncReader_Release(filter
->reader
);
852 filter
->reader
= NULL
;
854 unix_funcs
->wg_parser_destroy(filter
->wg_parser
);
856 strmbase_sink_cleanup(&filter
->sink
);
857 strmbase_filter_cleanup(&filter
->filter
);
861 static HRESULT
parser_init_stream(struct strmbase_filter
*iface
)
863 struct parser
*filter
= impl_from_strmbase_filter(iface
);
864 DWORD stop_flags
= AM_SEEKING_NoPositioning
;
865 const SourceSeeking
*seeking
;
868 if (!filter
->sink_connected
)
871 filter
->streaming
= true;
872 unix_funcs
->wg_parser_end_flush(filter
->wg_parser
);
874 /* DirectShow retains the old seek positions, but resets to them every time
875 * it transitions from stopped -> paused. */
877 seeking
= &filter
->sources
[0]->seek
;
878 if (seeking
->llStop
&& seeking
->llStop
!= seeking
->llDuration
)
879 stop_flags
= AM_SEEKING_AbsolutePositioning
;
880 unix_funcs
->wg_parser_stream_seek(filter
->sources
[0]->wg_stream
, seeking
->dRate
,
881 seeking
->llCurrent
, seeking
->llStop
, AM_SEEKING_AbsolutePositioning
, stop_flags
);
883 for (i
= 0; i
< filter
->source_count
; ++i
)
887 if (!filter
->sources
[i
]->pin
.pin
.peer
)
890 if (FAILED(hr
= IMemAllocator_Commit(filter
->sources
[i
]->pin
.pAllocator
)))
891 ERR("Failed to commit allocator, hr %#x.\n", hr
);
893 filter
->sources
[i
]->thread
= CreateThread(NULL
, 0, stream_thread
, filter
->sources
[i
], 0, NULL
);
899 static HRESULT
parser_cleanup_stream(struct strmbase_filter
*iface
)
901 struct parser
*filter
= impl_from_strmbase_filter(iface
);
904 if (!filter
->sink_connected
)
907 filter
->streaming
= false;
908 unix_funcs
->wg_parser_begin_flush(filter
->wg_parser
);
910 for (i
= 0; i
< filter
->source_count
; ++i
)
912 struct parser_source
*pin
= filter
->sources
[i
];
914 if (!pin
->pin
.pin
.peer
)
917 IMemAllocator_Decommit(pin
->pin
.pAllocator
);
919 WaitForSingleObject(pin
->thread
, INFINITE
);
920 CloseHandle(pin
->thread
);
927 static const struct strmbase_filter_ops filter_ops
=
929 .filter_get_pin
= parser_get_pin
,
930 .filter_destroy
= parser_destroy
,
931 .filter_init_stream
= parser_init_stream
,
932 .filter_cleanup_stream
= parser_cleanup_stream
,
935 static inline struct parser
*impl_from_strmbase_sink(struct strmbase_sink
*iface
)
937 return CONTAINING_RECORD(iface
, struct parser
, sink
);
940 static HRESULT
sink_query_accept(struct strmbase_pin
*iface
, const AM_MEDIA_TYPE
*mt
)
942 if (IsEqualGUID(&mt
->majortype
, &MEDIATYPE_Stream
))
947 static HRESULT
parser_sink_connect(struct strmbase_sink
*iface
, IPin
*peer
, const AM_MEDIA_TYPE
*pmt
)
949 struct parser
*filter
= impl_from_strmbase_sink(iface
);
954 filter
->reader
= NULL
;
955 if (FAILED(hr
= IPin_QueryInterface(peer
, &IID_IAsyncReader
, (void **)&filter
->reader
)))
958 IAsyncReader_Length(filter
->reader
, &filter
->file_size
, &unused
);
960 filter
->sink_connected
= true;
961 filter
->read_thread
= CreateThread(NULL
, 0, read_thread
, filter
, 0, NULL
);
963 if (FAILED(hr
= unix_funcs
->wg_parser_connect(filter
->wg_parser
, filter
->file_size
)))
966 if (!filter
->init_gst(filter
))
969 for (i
= 0; i
< filter
->source_count
; ++i
)
971 struct parser_source
*pin
= filter
->sources
[i
];
973 pin
->seek
.llDuration
= pin
->seek
.llStop
= unix_funcs
->wg_parser_stream_get_duration(pin
->wg_stream
);
974 pin
->seek
.llCurrent
= 0;
979 GST_RemoveOutputPins(filter
);
980 IAsyncReader_Release(filter
->reader
);
981 filter
->reader
= NULL
;
985 static void parser_sink_disconnect(struct strmbase_sink
*iface
)
987 struct parser
*filter
= impl_from_strmbase_sink(iface
);
989 GST_RemoveOutputPins(filter
);
991 IAsyncReader_Release(filter
->reader
);
992 filter
->reader
= NULL
;
995 static const struct strmbase_sink_ops sink_ops
=
997 .base
.pin_query_accept
= sink_query_accept
,
998 .sink_connect
= parser_sink_connect
,
999 .sink_disconnect
= parser_sink_disconnect
,
1002 static BOOL
decodebin_parser_filter_init_gst(struct parser
*filter
)
1004 struct wg_parser
*parser
= filter
->wg_parser
;
1005 unsigned int i
, stream_count
;
1006 WCHAR source_name
[20];
1008 stream_count
= unix_funcs
->wg_parser_get_stream_count(parser
);
1009 for (i
= 0; i
< stream_count
; ++i
)
1011 swprintf(source_name
, ARRAY_SIZE(source_name
), L
"Stream %02u", i
);
1012 if (!create_pin(filter
, unix_funcs
->wg_parser_get_stream(parser
, i
), source_name
))
1019 static HRESULT
decodebin_parser_source_query_accept(struct parser_source
*pin
, const AM_MEDIA_TYPE
*mt
)
1021 struct wg_format format
;
1023 /* At least make sure we can convert it to wg_format. */
1024 return amt_to_wg_format(mt
, &format
) ? S_OK
: S_FALSE
;
1027 static HRESULT
decodebin_parser_source_get_media_type(struct parser_source
*pin
,
1028 unsigned int index
, AM_MEDIA_TYPE
*mt
)
1030 struct wg_format format
;
1032 static const enum wg_video_format video_formats
[] =
1034 /* Try to prefer YUV formats over RGB ones. Most decoders output in the
1035 * YUV color space, and it's generally much less expensive for
1036 * videoconvert to do YUV -> YUV transformations. */
1037 WG_VIDEO_FORMAT_AYUV
,
1038 WG_VIDEO_FORMAT_I420
,
1039 WG_VIDEO_FORMAT_YV12
,
1040 WG_VIDEO_FORMAT_YUY2
,
1041 WG_VIDEO_FORMAT_UYVY
,
1042 WG_VIDEO_FORMAT_YVYU
,
1043 WG_VIDEO_FORMAT_NV12
,
1044 WG_VIDEO_FORMAT_BGRA
,
1045 WG_VIDEO_FORMAT_BGRx
,
1046 WG_VIDEO_FORMAT_BGR
,
1047 WG_VIDEO_FORMAT_RGB16
,
1048 WG_VIDEO_FORMAT_RGB15
,
1051 unix_funcs
->wg_parser_stream_get_preferred_format(pin
->wg_stream
, &format
);
1053 memset(mt
, 0, sizeof(AM_MEDIA_TYPE
));
1055 if (amt_from_wg_format(mt
, &format
))
1062 if (format
.major_type
== WG_MAJOR_TYPE_VIDEO
&& index
< ARRAY_SIZE(video_formats
))
1064 format
.u
.video
.format
= video_formats
[index
];
1065 if (!amt_from_wg_format(mt
, &format
))
1066 return E_OUTOFMEMORY
;
1069 else if (format
.major_type
== WG_MAJOR_TYPE_AUDIO
&& !index
)
1071 format
.u
.audio
.format
= WG_AUDIO_FORMAT_S16LE
;
1072 if (!amt_from_wg_format(mt
, &format
))
1073 return E_OUTOFMEMORY
;
1077 return VFW_S_NO_MORE_ITEMS
;
1080 static BOOL
parser_init_gstreamer(void)
1082 if (!init_gstreamer())
1087 HRESULT
decodebin_parser_create(IUnknown
*outer
, IUnknown
**out
)
1089 struct parser
*object
;
1091 if (!parser_init_gstreamer())
1094 if (!(object
= calloc(1, sizeof(*object
))))
1095 return E_OUTOFMEMORY
;
1097 if (!(object
->wg_parser
= unix_funcs
->wg_decodebin_parser_create()))
1100 return E_OUTOFMEMORY
;
1103 strmbase_filter_init(&object
->filter
, outer
, &CLSID_decodebin_parser
, &filter_ops
);
1104 strmbase_sink_init(&object
->sink
, &object
->filter
, L
"input pin", &sink_ops
, NULL
);
1106 object
->init_gst
= decodebin_parser_filter_init_gst
;
1107 object
->source_query_accept
= decodebin_parser_source_query_accept
;
1108 object
->source_get_media_type
= decodebin_parser_source_get_media_type
;
1110 TRACE("Created GStreamer demuxer %p.\n", object
);
1111 *out
= &object
->filter
.IUnknown_inner
;
1115 static struct parser
*impl_from_IAMStreamSelect(IAMStreamSelect
*iface
)
1117 return CONTAINING_RECORD(iface
, struct parser
, IAMStreamSelect_iface
);
1120 static HRESULT WINAPI
stream_select_QueryInterface(IAMStreamSelect
*iface
, REFIID iid
, void **out
)
1122 struct parser
*filter
= impl_from_IAMStreamSelect(iface
);
1123 return IUnknown_QueryInterface(filter
->filter
.outer_unk
, iid
, out
);
1126 static ULONG WINAPI
stream_select_AddRef(IAMStreamSelect
*iface
)
1128 struct parser
*filter
= impl_from_IAMStreamSelect(iface
);
1129 return IUnknown_AddRef(filter
->filter
.outer_unk
);
1132 static ULONG WINAPI
stream_select_Release(IAMStreamSelect
*iface
)
1134 struct parser
*filter
= impl_from_IAMStreamSelect(iface
);
1135 return IUnknown_Release(filter
->filter
.outer_unk
);
1138 static HRESULT WINAPI
stream_select_Count(IAMStreamSelect
*iface
, DWORD
*count
)
1140 FIXME("iface %p, count %p, stub!\n", iface
, count
);
1144 static HRESULT WINAPI
stream_select_Info(IAMStreamSelect
*iface
, LONG index
,
1145 AM_MEDIA_TYPE
**mt
, DWORD
*flags
, LCID
*lcid
, DWORD
*group
, WCHAR
**name
,
1146 IUnknown
**object
, IUnknown
**unknown
)
1148 FIXME("iface %p, index %d, mt %p, flags %p, lcid %p, group %p, name %p, object %p, unknown %p, stub!\n",
1149 iface
, index
, mt
, flags
, lcid
, group
, name
, object
, unknown
);
1153 static HRESULT WINAPI
stream_select_Enable(IAMStreamSelect
*iface
, LONG index
, DWORD flags
)
1155 FIXME("iface %p, index %d, flags %#x, stub!\n", iface
, index
, flags
);
1159 static const IAMStreamSelectVtbl stream_select_vtbl
=
1161 stream_select_QueryInterface
,
1162 stream_select_AddRef
,
1163 stream_select_Release
,
1164 stream_select_Count
,
1166 stream_select_Enable
,
1169 static HRESULT WINAPI
GST_ChangeCurrent(IMediaSeeking
*iface
)
1171 struct parser_source
*This
= impl_from_IMediaSeeking(iface
);
1172 TRACE("(%p)\n", This
);
1176 static HRESULT WINAPI
GST_ChangeStop(IMediaSeeking
*iface
)
1178 struct parser_source
*This
= impl_from_IMediaSeeking(iface
);
1179 TRACE("(%p)\n", This
);
1183 static HRESULT WINAPI
GST_ChangeRate(IMediaSeeking
*iface
)
1185 struct parser_source
*pin
= impl_from_IMediaSeeking(iface
);
1187 unix_funcs
->wg_parser_stream_seek(pin
->wg_stream
, pin
->seek
.dRate
, 0, 0,
1188 AM_SEEKING_NoPositioning
, AM_SEEKING_NoPositioning
);
1192 static HRESULT WINAPI
GST_Seeking_QueryInterface(IMediaSeeking
*iface
, REFIID riid
, void **ppv
)
1194 struct parser_source
*This
= impl_from_IMediaSeeking(iface
);
1195 return IPin_QueryInterface(&This
->pin
.pin
.IPin_iface
, riid
, ppv
);
1198 static ULONG WINAPI
GST_Seeking_AddRef(IMediaSeeking
*iface
)
1200 struct parser_source
*This
= impl_from_IMediaSeeking(iface
);
1201 return IPin_AddRef(&This
->pin
.pin
.IPin_iface
);
1204 static ULONG WINAPI
GST_Seeking_Release(IMediaSeeking
*iface
)
1206 struct parser_source
*This
= impl_from_IMediaSeeking(iface
);
1207 return IPin_Release(&This
->pin
.pin
.IPin_iface
);
1210 static HRESULT WINAPI
GST_Seeking_SetPositions(IMediaSeeking
*iface
,
1211 LONGLONG
*current
, DWORD current_flags
, LONGLONG
*stop
, DWORD stop_flags
)
1213 struct parser_source
*pin
= impl_from_IMediaSeeking(iface
);
1214 struct parser
*filter
= impl_from_strmbase_filter(pin
->pin
.pin
.filter
);
1218 TRACE("pin %p, current %s, current_flags %#x, stop %s, stop_flags %#x.\n",
1219 pin
, current
? debugstr_time(*current
) : "<null>", current_flags
,
1220 stop
? debugstr_time(*stop
) : "<null>", stop_flags
);
1222 if (pin
->pin
.pin
.filter
->state
== State_Stopped
)
1224 SourceSeekingImpl_SetPositions(iface
, current
, current_flags
, stop
, stop_flags
);
1228 if (!(current_flags
& AM_SEEKING_NoFlush
))
1230 unix_funcs
->wg_parser_begin_flush(filter
->wg_parser
);
1232 for (i
= 0; i
< filter
->source_count
; ++i
)
1234 if (filter
->sources
[i
]->pin
.pin
.peer
)
1235 IPin_BeginFlush(filter
->sources
[i
]->pin
.pin
.peer
);
1239 IAsyncReader_BeginFlush(filter
->reader
);
1242 /* Acquire the flushing locks. This blocks the streaming threads, and
1243 * ensures the seek is serialized between flushes. */
1244 for (i
= 0; i
< filter
->source_count
; ++i
)
1246 if (filter
->sources
[i
]->pin
.pin
.peer
)
1247 EnterCriticalSection(&pin
->flushing_cs
);
1250 SourceSeekingImpl_SetPositions(iface
, current
, current_flags
, stop
, stop_flags
);
1252 if (!unix_funcs
->wg_parser_stream_seek(pin
->wg_stream
, pin
->seek
.dRate
,
1253 pin
->seek
.llCurrent
, pin
->seek
.llStop
, current_flags
, stop_flags
))
1255 ERR("Failed to seek (current %s, stop %s).\n",
1256 debugstr_time(pin
->seek
.llCurrent
), debugstr_time(pin
->seek
.llStop
));
1260 if (!(current_flags
& AM_SEEKING_NoFlush
))
1262 unix_funcs
->wg_parser_end_flush(filter
->wg_parser
);
1264 for (i
= 0; i
< filter
->source_count
; ++i
)
1266 if (filter
->sources
[i
]->pin
.pin
.peer
)
1267 IPin_EndFlush(filter
->sources
[i
]->pin
.pin
.peer
);
1271 IAsyncReader_EndFlush(filter
->reader
);
1274 /* Release the flushing locks. */
1275 for (i
= filter
->source_count
- 1; i
>= 0; --i
)
1277 if (filter
->sources
[i
]->pin
.pin
.peer
)
1278 LeaveCriticalSection(&pin
->flushing_cs
);
1284 static const IMediaSeekingVtbl GST_Seeking_Vtbl
=
1286 GST_Seeking_QueryInterface
,
1288 GST_Seeking_Release
,
1289 SourceSeekingImpl_GetCapabilities
,
1290 SourceSeekingImpl_CheckCapabilities
,
1291 SourceSeekingImpl_IsFormatSupported
,
1292 SourceSeekingImpl_QueryPreferredFormat
,
1293 SourceSeekingImpl_GetTimeFormat
,
1294 SourceSeekingImpl_IsUsingTimeFormat
,
1295 SourceSeekingImpl_SetTimeFormat
,
1296 SourceSeekingImpl_GetDuration
,
1297 SourceSeekingImpl_GetStopPosition
,
1298 SourceSeekingImpl_GetCurrentPosition
,
1299 SourceSeekingImpl_ConvertTimeFormat
,
1300 GST_Seeking_SetPositions
,
1301 SourceSeekingImpl_GetPositions
,
1302 SourceSeekingImpl_GetAvailable
,
1303 SourceSeekingImpl_SetRate
,
1304 SourceSeekingImpl_GetRate
,
1305 SourceSeekingImpl_GetPreroll
1308 static inline struct parser_source
*impl_from_IQualityControl( IQualityControl
*iface
)
1310 return CONTAINING_RECORD(iface
, struct parser_source
, IQualityControl_iface
);
1313 static HRESULT WINAPI
GST_QualityControl_QueryInterface(IQualityControl
*iface
, REFIID riid
, void **ppv
)
1315 struct parser_source
*pin
= impl_from_IQualityControl(iface
);
1316 return IPin_QueryInterface(&pin
->pin
.pin
.IPin_iface
, riid
, ppv
);
1319 static ULONG WINAPI
GST_QualityControl_AddRef(IQualityControl
*iface
)
1321 struct parser_source
*pin
= impl_from_IQualityControl(iface
);
1322 return IPin_AddRef(&pin
->pin
.pin
.IPin_iface
);
1325 static ULONG WINAPI
GST_QualityControl_Release(IQualityControl
*iface
)
1327 struct parser_source
*pin
= impl_from_IQualityControl(iface
);
1328 return IPin_Release(&pin
->pin
.pin
.IPin_iface
);
1331 static HRESULT WINAPI
GST_QualityControl_Notify(IQualityControl
*iface
, IBaseFilter
*sender
, Quality q
)
1333 struct parser_source
*pin
= impl_from_IQualityControl(iface
);
1337 TRACE("pin %p, sender %p, type %s, proportion %u, late %s, timestamp %s.\n",
1338 pin
, sender
, q
.Type
== Famine
? "Famine" : "Flood", q
.Proportion
,
1339 debugstr_time(q
.Late
), debugstr_time(q
.TimeStamp
));
1341 /* DirectShow filters sometimes pass negative timestamps (Audiosurf uses the
1342 * current time instead of the time of the last buffer). GstClockTime is
1343 * unsigned, so clamp it to 0. */
1344 timestamp
= max(q
.TimeStamp
, 0);
1346 /* The documentation specifies that timestamp + diff must be nonnegative. */
1348 if (diff
< 0 && timestamp
< (uint64_t)-diff
)
1351 /* DirectShow "Proportion" describes what percentage of buffers the upstream
1352 * filter should keep (i.e. dropping the rest). If frames are late, the
1353 * proportion will be less than 1. For example, a proportion of 500 means
1354 * that the element should drop half of its frames, essentially because
1355 * frames are taking twice as long as they should to arrive.
1357 * GStreamer "proportion" is the inverse of this; it describes how much
1358 * faster the upstream element should produce frames. I.e. if frames are
1359 * taking twice as long as they should to arrive, we want the frames to be
1360 * decoded twice as fast, and so we pass 2.0 to GStreamer. */
1364 WARN("Ignoring quality message with zero proportion.\n");
1368 /* GST_QOS_TYPE_OVERFLOW is also used for buffers that arrive on time, but
1369 * DirectShow filters might use Famine, so check that there actually is an
1371 unix_funcs
->wg_parser_stream_notify_qos(pin
->wg_stream
, q
.Type
== Famine
&& q
.Proportion
< 1000,
1372 1000.0 / q
.Proportion
, diff
, timestamp
);
1377 static HRESULT WINAPI
GST_QualityControl_SetSink(IQualityControl
*iface
, IQualityControl
*tonotify
)
1379 struct parser_source
*pin
= impl_from_IQualityControl(iface
);
1380 TRACE("(%p)->(%p)\n", pin
, pin
);
1385 static const IQualityControlVtbl GSTOutPin_QualityControl_Vtbl
= {
1386 GST_QualityControl_QueryInterface
,
1387 GST_QualityControl_AddRef
,
1388 GST_QualityControl_Release
,
1389 GST_QualityControl_Notify
,
1390 GST_QualityControl_SetSink
1393 static inline struct parser_source
*impl_source_from_IPin(IPin
*iface
)
1395 return CONTAINING_RECORD(iface
, struct parser_source
, pin
.pin
.IPin_iface
);
1398 static HRESULT
source_query_interface(struct strmbase_pin
*iface
, REFIID iid
, void **out
)
1400 struct parser_source
*pin
= impl_source_from_IPin(&iface
->IPin_iface
);
1402 if (IsEqualGUID(iid
, &IID_IMediaSeeking
))
1403 *out
= &pin
->seek
.IMediaSeeking_iface
;
1404 else if (IsEqualGUID(iid
, &IID_IQualityControl
))
1405 *out
= &pin
->IQualityControl_iface
;
1407 return E_NOINTERFACE
;
1409 IUnknown_AddRef((IUnknown
*)*out
);
1413 static HRESULT
source_query_accept(struct strmbase_pin
*iface
, const AM_MEDIA_TYPE
*mt
)
1415 struct parser_source
*pin
= impl_source_from_IPin(&iface
->IPin_iface
);
1416 struct parser
*filter
= impl_from_strmbase_filter(iface
->filter
);
1417 return filter
->source_query_accept(pin
, mt
);
1420 static HRESULT
source_get_media_type(struct strmbase_pin
*iface
, unsigned int index
, AM_MEDIA_TYPE
*mt
)
1422 struct parser_source
*pin
= impl_source_from_IPin(&iface
->IPin_iface
);
1423 struct parser
*filter
= impl_from_strmbase_filter(iface
->filter
);
1424 return filter
->source_get_media_type(pin
, index
, mt
);
1427 static HRESULT WINAPI
GSTOutPin_DecideBufferSize(struct strmbase_source
*iface
,
1428 IMemAllocator
*allocator
, ALLOCATOR_PROPERTIES
*props
)
1430 struct parser_source
*pin
= impl_source_from_IPin(&iface
->pin
.IPin_iface
);
1431 unsigned int buffer_size
= 16384;
1432 ALLOCATOR_PROPERTIES ret_props
;
1433 struct wg_format format
;
1436 if (IsEqualGUID(&pin
->pin
.pin
.mt
.formattype
, &FORMAT_VideoInfo
))
1438 VIDEOINFOHEADER
*format
= (VIDEOINFOHEADER
*)pin
->pin
.pin
.mt
.pbFormat
;
1439 buffer_size
= format
->bmiHeader
.biSizeImage
;
1441 else if (IsEqualGUID(&pin
->pin
.pin
.mt
.formattype
, &FORMAT_WaveFormatEx
)
1442 && (IsEqualGUID(&pin
->pin
.pin
.mt
.subtype
, &MEDIASUBTYPE_PCM
)
1443 || IsEqualGUID(&pin
->pin
.pin
.mt
.subtype
, &MEDIASUBTYPE_IEEE_FLOAT
)))
1445 WAVEFORMATEX
*format
= (WAVEFORMATEX
*)pin
->pin
.pin
.mt
.pbFormat
;
1446 buffer_size
= format
->nAvgBytesPerSec
;
1449 ret
= amt_to_wg_format(&pin
->pin
.pin
.mt
, &format
);
1451 unix_funcs
->wg_parser_stream_enable(pin
->wg_stream
, &format
);
1453 /* We do need to drop any buffers that might have been sent with the old
1454 * caps, but this will be handled in parser_init_stream(). */
1456 props
->cBuffers
= max(props
->cBuffers
, 1);
1457 props
->cbBuffer
= max(props
->cbBuffer
, buffer_size
);
1458 props
->cbAlign
= max(props
->cbAlign
, 1);
1459 return IMemAllocator_SetProperties(allocator
, props
, &ret_props
);
1462 static void source_disconnect(struct strmbase_source
*iface
)
1464 struct parser_source
*pin
= impl_source_from_IPin(&iface
->pin
.IPin_iface
);
1466 unix_funcs
->wg_parser_stream_disable(pin
->wg_stream
);
1469 static void free_source_pin(struct parser_source
*pin
)
1471 if (pin
->pin
.pin
.peer
)
1473 if (SUCCEEDED(IMemAllocator_Decommit(pin
->pin
.pAllocator
)))
1474 IPin_Disconnect(pin
->pin
.pin
.peer
);
1475 IPin_Disconnect(&pin
->pin
.pin
.IPin_iface
);
1478 pin
->flushing_cs
.DebugInfo
->Spare
[0] = 0;
1479 DeleteCriticalSection(&pin
->flushing_cs
);
1481 strmbase_seeking_cleanup(&pin
->seek
);
1482 strmbase_source_cleanup(&pin
->pin
);
1486 static const struct strmbase_source_ops source_ops
=
1488 .base
.pin_query_interface
= source_query_interface
,
1489 .base
.pin_query_accept
= source_query_accept
,
1490 .base
.pin_get_media_type
= source_get_media_type
,
1491 .pfnAttemptConnection
= BaseOutputPinImpl_AttemptConnection
,
1492 .pfnDecideAllocator
= BaseOutputPinImpl_DecideAllocator
,
1493 .pfnDecideBufferSize
= GSTOutPin_DecideBufferSize
,
1494 .source_disconnect
= source_disconnect
,
1497 static struct parser_source
*create_pin(struct parser
*filter
,
1498 struct wg_parser_stream
*stream
, const WCHAR
*name
)
1500 struct parser_source
*pin
, **new_array
;
1502 if (!(new_array
= realloc(filter
->sources
, (filter
->source_count
+ 1) * sizeof(*filter
->sources
))))
1504 filter
->sources
= new_array
;
1506 if (!(pin
= calloc(1, sizeof(*pin
))))
1509 pin
->wg_stream
= stream
;
1510 strmbase_source_init(&pin
->pin
, &filter
->filter
, name
, &source_ops
);
1511 pin
->IQualityControl_iface
.lpVtbl
= &GSTOutPin_QualityControl_Vtbl
;
1512 strmbase_seeking_init(&pin
->seek
, &GST_Seeking_Vtbl
, GST_ChangeStop
,
1513 GST_ChangeCurrent
, GST_ChangeRate
);
1514 BaseFilterImpl_IncrementPinVersion(&filter
->filter
);
1516 InitializeCriticalSection(&pin
->flushing_cs
);
1517 pin
->flushing_cs
.DebugInfo
->Spare
[0] = (DWORD_PTR
)(__FILE__
": pin.flushing_cs");
1519 filter
->sources
[filter
->source_count
++] = pin
;
1523 static HRESULT
GST_RemoveOutputPins(struct parser
*This
)
1527 TRACE("(%p)\n", This
);
1529 if (!This
->sink_connected
)
1532 unix_funcs
->wg_parser_disconnect(This
->wg_parser
);
1534 /* read_thread() needs to stay alive to service any read requests GStreamer
1535 * sends, so we can only shut it down after GStreamer stops. */
1536 This
->sink_connected
= false;
1537 WaitForSingleObject(This
->read_thread
, INFINITE
);
1538 CloseHandle(This
->read_thread
);
1540 for (i
= 0; i
< This
->source_count
; ++i
)
1542 if (This
->sources
[i
])
1543 free_source_pin(This
->sources
[i
]);
1546 This
->source_count
= 0;
1547 free(This
->sources
);
1548 This
->sources
= NULL
;
1550 BaseFilterImpl_IncrementPinVersion(&This
->filter
);
1554 static BOOL
compare_media_types(const AM_MEDIA_TYPE
*a
, const AM_MEDIA_TYPE
*b
)
1556 return IsEqualGUID(&a
->majortype
, &b
->majortype
)
1557 && IsEqualGUID(&a
->subtype
, &b
->subtype
)
1558 && IsEqualGUID(&a
->formattype
, &b
->formattype
)
1559 && a
->cbFormat
== b
->cbFormat
1560 && !memcmp(a
->pbFormat
, b
->pbFormat
, a
->cbFormat
);
1563 static HRESULT
wave_parser_sink_query_accept(struct strmbase_pin
*iface
, const AM_MEDIA_TYPE
*mt
)
1565 if (!IsEqualGUID(&mt
->majortype
, &MEDIATYPE_Stream
))
1567 if (IsEqualGUID(&mt
->subtype
, &MEDIASUBTYPE_WAVE
))
1569 if (IsEqualGUID(&mt
->subtype
, &MEDIASUBTYPE_AU
) || IsEqualGUID(&mt
->subtype
, &MEDIASUBTYPE_AIFF
))
1570 FIXME("AU and AIFF files are not yet supported.\n");
1574 static const struct strmbase_sink_ops wave_parser_sink_ops
=
1576 .base
.pin_query_accept
= wave_parser_sink_query_accept
,
1577 .sink_connect
= parser_sink_connect
,
1578 .sink_disconnect
= parser_sink_disconnect
,
1581 static BOOL
wave_parser_filter_init_gst(struct parser
*filter
)
1583 struct wg_parser
*parser
= filter
->wg_parser
;
1585 if (!create_pin(filter
, unix_funcs
->wg_parser_get_stream(parser
, 0), L
"output"))
1591 static HRESULT
wave_parser_source_query_accept(struct parser_source
*pin
, const AM_MEDIA_TYPE
*mt
)
1593 struct wg_format format
;
1594 AM_MEDIA_TYPE pad_mt
;
1597 unix_funcs
->wg_parser_stream_get_preferred_format(pin
->wg_stream
, &format
);
1598 if (!amt_from_wg_format(&pad_mt
, &format
))
1599 return E_OUTOFMEMORY
;
1600 hr
= compare_media_types(mt
, &pad_mt
) ? S_OK
: S_FALSE
;
1601 FreeMediaType(&pad_mt
);
1605 static HRESULT
wave_parser_source_get_media_type(struct parser_source
*pin
,
1606 unsigned int index
, AM_MEDIA_TYPE
*mt
)
1608 struct wg_format format
;
1611 return VFW_S_NO_MORE_ITEMS
;
1612 unix_funcs
->wg_parser_stream_get_preferred_format(pin
->wg_stream
, &format
);
1613 if (!amt_from_wg_format(mt
, &format
))
1614 return E_OUTOFMEMORY
;
1618 HRESULT
wave_parser_create(IUnknown
*outer
, IUnknown
**out
)
1620 struct parser
*object
;
1622 if (!parser_init_gstreamer())
1625 if (!(object
= calloc(1, sizeof(*object
))))
1626 return E_OUTOFMEMORY
;
1628 if (!(object
->wg_parser
= unix_funcs
->wg_wave_parser_create()))
1631 return E_OUTOFMEMORY
;
1634 strmbase_filter_init(&object
->filter
, outer
, &CLSID_WAVEParser
, &filter_ops
);
1635 strmbase_sink_init(&object
->sink
, &object
->filter
, L
"input pin", &wave_parser_sink_ops
, NULL
);
1636 object
->init_gst
= wave_parser_filter_init_gst
;
1637 object
->source_query_accept
= wave_parser_source_query_accept
;
1638 object
->source_get_media_type
= wave_parser_source_get_media_type
;
1640 TRACE("Created WAVE parser %p.\n", object
);
1641 *out
= &object
->filter
.IUnknown_inner
;
1645 static HRESULT
avi_splitter_sink_query_accept(struct strmbase_pin
*iface
, const AM_MEDIA_TYPE
*mt
)
1647 if (IsEqualGUID(&mt
->majortype
, &MEDIATYPE_Stream
)
1648 && IsEqualGUID(&mt
->subtype
, &MEDIASUBTYPE_Avi
))
1653 static const struct strmbase_sink_ops avi_splitter_sink_ops
=
1655 .base
.pin_query_accept
= avi_splitter_sink_query_accept
,
1656 .sink_connect
= parser_sink_connect
,
1657 .sink_disconnect
= parser_sink_disconnect
,
1660 static BOOL
avi_splitter_filter_init_gst(struct parser
*filter
)
1662 struct wg_parser
*parser
= filter
->wg_parser
;
1663 uint32_t i
, stream_count
;
1664 WCHAR source_name
[20];
1666 stream_count
= unix_funcs
->wg_parser_get_stream_count(parser
);
1667 for (i
= 0; i
< stream_count
; ++i
)
1669 swprintf(source_name
, ARRAY_SIZE(source_name
), L
"Stream %02u", i
);
1670 if (!create_pin(filter
, unix_funcs
->wg_parser_get_stream(parser
, i
), source_name
))
1677 static HRESULT
avi_splitter_source_query_accept(struct parser_source
*pin
, const AM_MEDIA_TYPE
*mt
)
1679 struct wg_format format
;
1680 AM_MEDIA_TYPE pad_mt
;
1683 unix_funcs
->wg_parser_stream_get_preferred_format(pin
->wg_stream
, &format
);
1684 if (!amt_from_wg_format(&pad_mt
, &format
))
1685 return E_OUTOFMEMORY
;
1686 hr
= compare_media_types(mt
, &pad_mt
) ? S_OK
: S_FALSE
;
1687 FreeMediaType(&pad_mt
);
1691 static HRESULT
avi_splitter_source_get_media_type(struct parser_source
*pin
,
1692 unsigned int index
, AM_MEDIA_TYPE
*mt
)
1694 struct wg_format format
;
1697 return VFW_S_NO_MORE_ITEMS
;
1698 unix_funcs
->wg_parser_stream_get_preferred_format(pin
->wg_stream
, &format
);
1699 if (!amt_from_wg_format(mt
, &format
))
1700 return E_OUTOFMEMORY
;
1704 HRESULT
avi_splitter_create(IUnknown
*outer
, IUnknown
**out
)
1706 struct parser
*object
;
1708 if (!parser_init_gstreamer())
1711 if (!(object
= calloc(1, sizeof(*object
))))
1712 return E_OUTOFMEMORY
;
1714 if (!(object
->wg_parser
= unix_funcs
->wg_avi_parser_create()))
1717 return E_OUTOFMEMORY
;
1720 strmbase_filter_init(&object
->filter
, outer
, &CLSID_AviSplitter
, &filter_ops
);
1721 strmbase_sink_init(&object
->sink
, &object
->filter
, L
"input pin", &avi_splitter_sink_ops
, NULL
);
1722 object
->init_gst
= avi_splitter_filter_init_gst
;
1723 object
->source_query_accept
= avi_splitter_source_query_accept
;
1724 object
->source_get_media_type
= avi_splitter_source_get_media_type
;
1726 TRACE("Created AVI splitter %p.\n", object
);
1727 *out
= &object
->filter
.IUnknown_inner
;
1731 static HRESULT
mpeg_splitter_sink_query_accept(struct strmbase_pin
*iface
, const AM_MEDIA_TYPE
*mt
)
1733 if (!IsEqualGUID(&mt
->majortype
, &MEDIATYPE_Stream
))
1735 if (IsEqualGUID(&mt
->subtype
, &MEDIASUBTYPE_MPEG1Audio
))
1737 if (IsEqualGUID(&mt
->subtype
, &MEDIASUBTYPE_MPEG1Video
)
1738 || IsEqualGUID(&mt
->subtype
, &MEDIASUBTYPE_MPEG1System
)
1739 || IsEqualGUID(&mt
->subtype
, &MEDIASUBTYPE_MPEG1VideoCD
))
1740 FIXME("Unsupported subtype %s.\n", wine_dbgstr_guid(&mt
->subtype
));
1744 static const struct strmbase_sink_ops mpeg_splitter_sink_ops
=
1746 .base
.pin_query_accept
= mpeg_splitter_sink_query_accept
,
1747 .sink_connect
= parser_sink_connect
,
1748 .sink_disconnect
= parser_sink_disconnect
,
1751 static BOOL
mpeg_splitter_filter_init_gst(struct parser
*filter
)
1753 struct wg_parser
*parser
= filter
->wg_parser
;
1755 if (!create_pin(filter
, unix_funcs
->wg_parser_get_stream(parser
, 0), L
"Audio"))
1761 static HRESULT
mpeg_splitter_source_query_accept(struct parser_source
*pin
, const AM_MEDIA_TYPE
*mt
)
1763 struct wg_format format
;
1764 AM_MEDIA_TYPE pad_mt
;
1767 unix_funcs
->wg_parser_stream_get_preferred_format(pin
->wg_stream
, &format
);
1768 if (!amt_from_wg_format(&pad_mt
, &format
))
1769 return E_OUTOFMEMORY
;
1770 hr
= compare_media_types(mt
, &pad_mt
) ? S_OK
: S_FALSE
;
1771 FreeMediaType(&pad_mt
);
1775 static HRESULT
mpeg_splitter_source_get_media_type(struct parser_source
*pin
,
1776 unsigned int index
, AM_MEDIA_TYPE
*mt
)
1778 struct wg_format format
;
1781 return VFW_S_NO_MORE_ITEMS
;
1782 unix_funcs
->wg_parser_stream_get_preferred_format(pin
->wg_stream
, &format
);
1783 if (!amt_from_wg_format(mt
, &format
))
1784 return E_OUTOFMEMORY
;
1788 static HRESULT
mpeg_splitter_query_interface(struct strmbase_filter
*iface
, REFIID iid
, void **out
)
1790 struct parser
*filter
= impl_from_strmbase_filter(iface
);
1792 if (IsEqualGUID(iid
, &IID_IAMStreamSelect
))
1794 *out
= &filter
->IAMStreamSelect_iface
;
1795 IUnknown_AddRef((IUnknown
*)*out
);
1799 return E_NOINTERFACE
;
1802 static const struct strmbase_filter_ops mpeg_splitter_ops
=
1804 .filter_query_interface
= mpeg_splitter_query_interface
,
1805 .filter_get_pin
= parser_get_pin
,
1806 .filter_destroy
= parser_destroy
,
1807 .filter_init_stream
= parser_init_stream
,
1808 .filter_cleanup_stream
= parser_cleanup_stream
,
1811 HRESULT
mpeg_splitter_create(IUnknown
*outer
, IUnknown
**out
)
1813 struct parser
*object
;
1815 if (!parser_init_gstreamer())
1818 if (!(object
= calloc(1, sizeof(*object
))))
1819 return E_OUTOFMEMORY
;
1821 if (!(object
->wg_parser
= unix_funcs
->wg_mpeg_audio_parser_create()))
1824 return E_OUTOFMEMORY
;
1827 strmbase_filter_init(&object
->filter
, outer
, &CLSID_MPEG1Splitter
, &mpeg_splitter_ops
);
1828 strmbase_sink_init(&object
->sink
, &object
->filter
, L
"Input", &mpeg_splitter_sink_ops
, NULL
);
1829 object
->IAMStreamSelect_iface
.lpVtbl
= &stream_select_vtbl
;
1831 object
->init_gst
= mpeg_splitter_filter_init_gst
;
1832 object
->source_query_accept
= mpeg_splitter_source_query_accept
;
1833 object
->source_get_media_type
= mpeg_splitter_source_get_media_type
;
1834 object
->enum_sink_first
= TRUE
;
1836 TRACE("Created MPEG-1 splitter %p.\n", object
);
1837 *out
= &object
->filter
.IUnknown_inner
;