2 * Copyright (C) 2007 Sebastien Moutte <sebastien@moutte.net>
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
16 * You should have received a copy of the GNU Library General Public
17 * License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
26 #include "gstdshowaudiosrc.h"
28 GST_DEBUG_CATEGORY_STATIC (dshowaudiosrc_debug);
29 #define GST_CAT_DEFAULT dshowaudiosrc_debug
31 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
34 GST_STATIC_CAPS ("audio/x-raw, "
36 GST_AUDIO_NE (S16) ", "
37 GST_AUDIO_NE (U16) ", "
38 GST_AUDIO_NE (S8) ", "
41 "rate = " GST_AUDIO_RATE_RANGE ", "
42 "channels = (int) [ 1, 2 ]")
45 G_DEFINE_TYPE(GstDshowAudioSrc, gst_dshowaudiosrc, GST_TYPE_AUDIO_SRC);
55 #define DEFAULT_PROP_DEVICE_INDEX 0
58 static void gst_dshowaudiosrc_dispose (GObject * gobject);
59 static void gst_dshowaudiosrc_set_property (GObject * object, guint prop_id,
60 const GValue * value, GParamSpec * pspec);
61 static void gst_dshowaudiosrc_get_property (GObject * object, guint prop_id,
62 GValue * value, GParamSpec * pspec);
63 static GstCaps *gst_dshowaudiosrc_get_caps (GstBaseSrc * src, GstCaps * filter);
64 static GstStateChangeReturn gst_dshowaudiosrc_change_state (GstElement *
65 element, GstStateChange transition);
67 static gboolean gst_dshowaudiosrc_open (GstAudioSrc * asrc);
68 static gboolean gst_dshowaudiosrc_prepare (GstAudioSrc * asrc,
69 GstAudioRingBufferSpec * spec);
70 static gboolean gst_dshowaudiosrc_unprepare (GstAudioSrc * asrc);
71 static gboolean gst_dshowaudiosrc_close (GstAudioSrc * asrc);
72 static guint gst_dshowaudiosrc_read (GstAudioSrc * asrc, gpointer data,
73 guint length, GstClockTime *timestamp);
74 static guint gst_dshowaudiosrc_delay (GstAudioSrc * asrc);
75 static void gst_dshowaudiosrc_reset (GstAudioSrc * asrc);
78 static GstCaps *gst_dshowaudiosrc_getcaps_from_streamcaps (GstDshowAudioSrc *
79 src, IPin * pin, IAMStreamConfig * streamcaps);
80 static gboolean gst_dshowaudiosrc_push_buffer (guint8 * buffer, guint size,
81 gpointer src_object, GstClockTime duration);
84 gst_dshowaudiosrc_class_init (GstDshowAudioSrcClass * klass)
86 GObjectClass *gobject_class;
87 GstElementClass *gstelement_class;
88 GstBaseSrcClass *gstbasesrc_class;
89 GstAudioSrcClass *gstaudiosrc_class;
91 gobject_class = (GObjectClass *) klass;
92 gstelement_class = (GstElementClass *) klass;
93 gstbasesrc_class = (GstBaseSrcClass *) klass;
94 gstaudiosrc_class = (GstAudioSrcClass *) klass;
96 gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_dshowaudiosrc_dispose);
97 gobject_class->set_property =
98 GST_DEBUG_FUNCPTR (gst_dshowaudiosrc_set_property);
99 gobject_class->get_property =
100 GST_DEBUG_FUNCPTR (gst_dshowaudiosrc_get_property);
102 gstbasesrc_class->get_caps = GST_DEBUG_FUNCPTR (gst_dshowaudiosrc_get_caps);
104 gstelement_class->change_state =
105 GST_DEBUG_FUNCPTR (gst_dshowaudiosrc_change_state);
107 gstaudiosrc_class->open = GST_DEBUG_FUNCPTR (gst_dshowaudiosrc_open);
108 gstaudiosrc_class->prepare = GST_DEBUG_FUNCPTR (gst_dshowaudiosrc_prepare);
109 gstaudiosrc_class->unprepare =
110 GST_DEBUG_FUNCPTR (gst_dshowaudiosrc_unprepare);
111 gstaudiosrc_class->close = GST_DEBUG_FUNCPTR (gst_dshowaudiosrc_close);
112 gstaudiosrc_class->read = GST_DEBUG_FUNCPTR (gst_dshowaudiosrc_read);
113 gstaudiosrc_class->delay = GST_DEBUG_FUNCPTR (gst_dshowaudiosrc_delay);
114 gstaudiosrc_class->reset = GST_DEBUG_FUNCPTR (gst_dshowaudiosrc_reset);
116 g_object_class_install_property
117 (gobject_class, PROP_DEVICE,
118 g_param_spec_string ("device", "Device",
119 "Directshow device reference (classID/name)", NULL,
120 static_cast < GParamFlags > (G_PARAM_READWRITE)));
122 g_object_class_install_property
123 (gobject_class, PROP_DEVICE_NAME,
124 g_param_spec_string ("device-name", "Device name",
125 "Human-readable name of the sound device", NULL,
126 static_cast < GParamFlags > (G_PARAM_READWRITE)));
128 g_object_class_install_property
129 (gobject_class, PROP_DEVICE_INDEX,
130 g_param_spec_int ("device-index", "Device index",
131 "Index of the enumerated audio device", 0, G_MAXINT,
132 DEFAULT_PROP_DEVICE_INDEX,
133 static_cast < GParamFlags > (G_PARAM_READWRITE)));
135 gst_element_class_add_static_pad_template (gstelement_class, &src_template);
137 gst_element_class_set_static_metadata (gstelement_class,
138 "Directshow audio capture source", "Source/Audio",
139 "Receive data from a directshow audio capture graph",
140 "Sebastien Moutte <sebastien@moutte.net>");
142 GST_DEBUG_CATEGORY_INIT (dshowaudiosrc_debug, "dshowaudiosrc", 0,
143 "Directshow audio source");
147 gst_dshowaudiosrc_init (GstDshowAudioSrc * src)
150 src->device_name = NULL;
151 src->device_index = DEFAULT_PROP_DEVICE_INDEX;
152 src->audio_cap_filter = NULL;
153 src->dshow_fakesink = NULL;
154 src->media_filter = NULL;
155 src->filter_graph = NULL;
157 src->pins_mediatypes = NULL;
159 src->gbarray = g_byte_array_new ();
160 g_mutex_init(&src->gbarray_lock);
162 src->is_running = FALSE;
164 CoInitializeEx (NULL, COINIT_MULTITHREADED);
168 gst_dshowaudiosrc_dispose (GObject * gobject)
170 GstDshowAudioSrc *src = GST_DSHOWAUDIOSRC (gobject);
173 g_free (src->device);
177 if (src->device_name) {
178 g_free (src->device_name);
179 src->device_name = NULL;
183 gst_caps_unref (src->caps);
187 if (src->pins_mediatypes) {
188 gst_dshow_free_pins_mediatypes (src->pins_mediatypes);
189 src->pins_mediatypes = NULL;
193 g_byte_array_free (src->gbarray, TRUE);
197 g_mutex_clear(&src->gbarray_lock);
200 if (src->audio_cap_filter)
201 src->audio_cap_filter->Release ();
205 G_OBJECT_CLASS (gst_dshowaudiosrc_parent_class)->dispose (gobject);
210 gst_dshowaudiosrc_set_property (GObject * object, guint prop_id,
211 const GValue * value, GParamSpec * pspec)
213 GstDshowAudioSrc *src = GST_DSHOWAUDIOSRC (object);
219 g_free (src->device);
222 if (g_value_get_string (value)) {
223 src->device = g_value_dup_string (value);;
227 case PROP_DEVICE_NAME:
229 if (src->device_name) {
230 g_free (src->device_name);
231 src->device_name = NULL;
233 if (g_value_get_string (value)) {
234 src->device_name = g_value_dup_string (value);;
238 case PROP_DEVICE_INDEX:
240 src->device_index = g_value_get_int (value);
244 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
250 gst_dshowaudiosrc_get_property (GObject * object, guint prop_id,
251 GValue * value, GParamSpec * pspec)
253 GstDshowAudioSrc *src;
255 g_return_if_fail (GST_IS_DSHOWAUDIOSRC (object));
256 src = GST_DSHOWAUDIOSRC (object);
260 g_value_set_string (value, src->device);
262 case PROP_DEVICE_NAME:
263 g_value_set_string (value, src->device_name);
265 case PROP_DEVICE_INDEX:
266 g_value_set_int (value, src->device_index);
269 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
275 gst_dshowaudiosrc_get_caps (GstBaseSrc * basesrc, GstCaps * filter)
278 IBindCtx *lpbc = NULL;
279 IMoniker *audiom = NULL;
281 GstDshowAudioSrc *src = GST_DSHOWAUDIOSRC (basesrc);
282 gunichar2 *unidevice = NULL;
285 g_free (src->device);
290 gst_dshow_getdevice_from_devicename (&CLSID_AudioInputDeviceCategory,
291 &src->device_name, &src->device_index);
293 GST_ERROR ("No audio device found.");
297 g_utf8_to_utf16 (src->device, strlen (src->device), NULL, NULL, NULL);
299 if (!src->audio_cap_filter) {
300 hres = CreateBindCtx (0, &lpbc);
301 if (SUCCEEDED (hres)) {
303 MkParseDisplayName (lpbc, (LPCOLESTR) unidevice, &dwEaten, &audiom);
304 if (SUCCEEDED (hres)) {
305 hres = audiom->BindToObject (lpbc, NULL, IID_IBaseFilter,
306 (LPVOID *) & src->audio_cap_filter);
313 if (src->audio_cap_filter && !src->caps) {
314 /* get the capture pins supported types */
315 IPin *capture_pin = NULL;
316 IEnumPins *enumpins = NULL;
319 hres = src->audio_cap_filter->EnumPins (&enumpins);
320 if (SUCCEEDED (hres)) {
321 while (enumpins->Next (1, &capture_pin, NULL) == S_OK) {
322 IKsPropertySet *pKs = NULL;
325 capture_pin->QueryInterface (IID_IKsPropertySet, (LPVOID *) & pKs);
326 if (SUCCEEDED (hres) && pKs) {
329 RPC_STATUS rpcstatus;
332 pKs->Get (AMPROPSETID_Pin,
333 AMPROPERTY_PIN_CATEGORY, NULL, 0, &pin_category, sizeof (GUID),
336 /* we only want capture pins */
337 if (UuidCompare (&pin_category, (UUID *) & PIN_CATEGORY_CAPTURE,
339 IAMStreamConfig *streamcaps = NULL;
341 if (SUCCEEDED (capture_pin->QueryInterface (IID_IAMStreamConfig,
342 (LPVOID *) & streamcaps))) {
344 gst_dshowaudiosrc_getcaps_from_streamcaps (src, capture_pin,
346 streamcaps->Release ();
351 capture_pin->Release ();
353 enumpins->Release ();
365 caps = gst_caps_intersect_full (filter, src->caps, GST_CAPS_INTERSECT_FIRST);
367 caps = gst_caps_ref (src->caps);
376 static GstStateChangeReturn
377 gst_dshowaudiosrc_change_state (GstElement * element, GstStateChange transition)
379 HRESULT hres = S_FALSE;
380 GstDshowAudioSrc *src = GST_DSHOWAUDIOSRC (element);
382 switch (transition) {
383 case GST_STATE_CHANGE_NULL_TO_READY:
385 case GST_STATE_CHANGE_READY_TO_PAUSED:
387 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
388 if (src->media_filter) {
389 src->is_running = TRUE;
390 hres = src->media_filter->Run (0);
393 GST_ERROR ("Can't RUN the directshow capture graph (error=0x%x)", hres);
394 src->is_running = FALSE;
395 return GST_STATE_CHANGE_FAILURE;
398 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
399 if (src->media_filter)
400 hres = src->media_filter->Stop ();
402 GST_ERROR ("Can't STOP the directshow capture graph (error=0x%x)",
404 return GST_STATE_CHANGE_FAILURE;
406 src->is_running = FALSE;
409 case GST_STATE_CHANGE_PAUSED_TO_READY:
411 case GST_STATE_CHANGE_READY_TO_NULL:
417 return GST_ELEMENT_CLASS(gst_dshowaudiosrc_parent_class)->change_state(element, transition);
421 gst_dshowaudiosrc_open (GstAudioSrc * asrc)
423 HRESULT hres = S_FALSE;
424 GstDshowAudioSrc *src = GST_DSHOWAUDIOSRC (asrc);
426 hres = CoCreateInstance (CLSID_FilterGraph, NULL, CLSCTX_INPROC,
427 IID_IFilterGraph, (LPVOID *) & src->filter_graph);
428 if (hres != S_OK || !src->filter_graph) {
430 ("Can't create an instance of the directshow graph manager (error=0x%x)",
436 src->filter_graph->QueryInterface (IID_IMediaFilter,
437 (LPVOID *) & src->media_filter);
438 if (hres != S_OK || !src->media_filter) {
440 ("Can't get IMediacontrol interface from the graph manager (error=0x%x)",
445 src->dshow_fakesink = new CDshowFakeSink;
446 src->dshow_fakesink->AddRef ();
448 hres = src->filter_graph->AddFilter (src->audio_cap_filter, L"capture");
451 ("Can't add the directshow capture filter to the graph (error=0x%x)",
456 hres = src->filter_graph->AddFilter (src->dshow_fakesink, L"fakesink");
458 GST_ERROR ("Can't add our fakesink filter to the graph (error=0x%x)", hres);
465 if (src->dshow_fakesink) {
466 src->dshow_fakesink->Release ();
467 src->dshow_fakesink = NULL;
470 if (src->media_filter) {
471 src->media_filter->Release ();
472 src->media_filter = NULL;
474 if (src->filter_graph) {
475 src->filter_graph->Release ();
476 src->filter_graph = NULL;
483 gst_dshowaudiosrc_prepare (GstAudioSrc * asrc, GstAudioRingBufferSpec * spec)
486 IPin *input_pin = NULL;
487 GstDshowAudioSrc *src = GST_DSHOWAUDIOSRC (asrc);
488 GstCaps *current_caps = gst_pad_get_current_caps (GST_BASE_SRC_PAD (asrc));
491 if (gst_caps_is_equal (spec->caps, current_caps)) {
492 gst_caps_unref (current_caps);
495 gst_caps_unref (current_caps);
497 /* In 1.0, prepare() seems to be called in the PLAYING state. Most
498 of the time you can't do much on a running graph. */
500 gboolean was_running = src->is_running;
502 HRESULT hres = src->media_filter->Stop ();
504 GST_ERROR("Can't STOP the directshow capture graph for preparing (error=0x%x)", hres);
507 src->is_running = FALSE;
510 /* search the negotiated caps in our caps list to get its index and the corresponding mediatype */
511 if (gst_caps_is_subset (spec->caps, src->caps)) {
515 for (; i < gst_caps_get_size (src->caps) && res == -1; i++) {
516 GstCaps *capstmp = gst_caps_copy_nth (src->caps, i);
518 if (gst_caps_is_subset (spec->caps, capstmp)) {
521 gst_caps_unref (capstmp);
524 if (res != -1 && src->pins_mediatypes) {
525 /*get the corresponding media type and build the dshow graph */
526 GstCapturePinMediaType *pin_mediatype = NULL;
527 GList *type = g_list_nth (src->pins_mediatypes, res);
530 pin_mediatype = (GstCapturePinMediaType *) type->data;
532 src->dshow_fakesink->gst_set_media_type (pin_mediatype->mediatype);
533 src->dshow_fakesink->gst_set_buffer_callback (
534 (push_buffer_func) gst_dshowaudiosrc_push_buffer, src);
536 gst_dshow_get_pin_from_filter (src->dshow_fakesink, PINDIR_INPUT,
539 GST_ERROR ("Can't get input pin from our directshow fakesink filter");
543 spec->segsize = (gint) (spec->info.bpf * spec->info.rate * spec->latency_time /
545 spec->segtotal = (gint) ((gfloat) spec->buffer_time /
546 (gfloat) spec->latency_time + 0.5);
547 if (!gst_dshow_configure_latency (pin_mediatype->capture_pin,
550 GST_WARNING ("Could not change capture latency");
551 spec->segsize = spec->info.rate * spec->info.channels;
554 GST_INFO ("Configuring with segsize:%d segtotal:%d", spec->segsize, spec->segtotal);
556 if (gst_dshow_is_pin_connected (pin_mediatype->capture_pin)) {
557 GST_DEBUG_OBJECT (src,
558 "capture_pin already connected, disconnecting");
559 src->filter_graph->Disconnect (pin_mediatype->capture_pin);
562 if (gst_dshow_is_pin_connected (input_pin)) {
563 GST_DEBUG_OBJECT (src, "input_pin already connected, disconnecting");
564 src->filter_graph->Disconnect (input_pin);
567 hres = src->filter_graph->ConnectDirect (pin_mediatype->capture_pin,
569 input_pin->Release ();
573 ("Can't connect capture filter with fakesink filter (error=0x%x)",
583 HRESULT hres = src->media_filter->Run (0);
585 GST_ERROR("Can't RUN the directshow capture graph after prepare (error=0x%x)", hres);
589 src->is_running = TRUE;
595 /* Don't restart the graph, we're out anyway. */
600 gst_dshowaudiosrc_unprepare (GstAudioSrc * asrc)
602 IPin *input_pin = NULL, *output_pin = NULL;
603 HRESULT hres = S_FALSE;
604 GstDshowAudioSrc *src = GST_DSHOWAUDIOSRC (asrc);
606 /* disconnect filters */
607 gst_dshow_get_pin_from_filter (src->audio_cap_filter, PINDIR_OUTPUT,
610 hres = src->filter_graph->Disconnect (output_pin);
611 output_pin->Release ();
614 gst_dshow_get_pin_from_filter (src->dshow_fakesink, PINDIR_INPUT, &input_pin);
616 hres = src->filter_graph->Disconnect (input_pin);
617 input_pin->Release ();
624 gst_dshowaudiosrc_close (GstAudioSrc * asrc)
626 GstDshowAudioSrc *src = GST_DSHOWAUDIOSRC (asrc);
628 if (!src->filter_graph)
631 /*remove filters from the graph */
632 src->filter_graph->RemoveFilter (src->audio_cap_filter);
633 src->filter_graph->RemoveFilter (src->dshow_fakesink);
635 /*release our gstreamer dshow sink */
636 src->dshow_fakesink->Release ();
637 src->dshow_fakesink = NULL;
639 /*release media filter interface */
640 src->media_filter->Release ();
641 src->media_filter = NULL;
643 /*release the filter graph manager */
644 src->filter_graph->Release ();
645 src->filter_graph = NULL;
651 gst_dshowaudiosrc_read (GstAudioSrc * asrc, gpointer data, guint length, GstClockTime *timestamp)
653 GstDshowAudioSrc *src = GST_DSHOWAUDIOSRC (asrc);
656 if (!src->is_running)
661 if (src->gbarray->len >= length) {
662 g_mutex_lock (&src->gbarray_lock);
663 memcpy (data, src->gbarray->data + (src->gbarray->len - length), length);
664 g_byte_array_remove_range (src->gbarray, src->gbarray->len - length,
667 g_mutex_unlock (&src->gbarray_lock);
669 if (src->is_running) {
670 Sleep (GST_AUDIO_BASE_SRC(src)->ringbuffer->spec.latency_time /
681 gst_dshowaudiosrc_delay (GstAudioSrc * asrc)
683 GstDshowAudioSrc *src = GST_DSHOWAUDIOSRC (asrc);
687 g_mutex_lock (&src->gbarray_lock);
688 if (src->gbarray->len) {
689 ret = src->gbarray->len / 4;
691 g_mutex_unlock (&src->gbarray_lock);
698 gst_dshowaudiosrc_reset (GstAudioSrc * asrc)
700 GstDshowAudioSrc *src = GST_DSHOWAUDIOSRC (asrc);
702 g_mutex_lock (&src->gbarray_lock);
703 GST_DEBUG ("byte array size= %d", src->gbarray->len);
704 if (src->gbarray->len > 0)
705 g_byte_array_remove_range (src->gbarray, 0, src->gbarray->len);
706 g_mutex_unlock (&src->gbarray_lock);
710 gst_dshowaudiosrc_getcaps_from_streamcaps (GstDshowAudioSrc * src, IPin * pin,
711 IAMStreamConfig * streamcaps)
713 GstCaps *caps = NULL;
717 AUDIO_STREAM_CONFIG_CAPS ascc;
723 streamcaps->GetNumberOfCapabilities (&icount, &isize);
725 if (isize != sizeof (ascc))
728 for (; i < icount; i++) {
729 GstCapturePinMediaType *pin_mediatype = g_new0 (GstCapturePinMediaType, 1);
732 pin_mediatype->capture_pin = pin;
734 hres = streamcaps->GetStreamCaps (i, &pin_mediatype->mediatype,
736 if (hres == S_OK && pin_mediatype->mediatype) {
737 GstCaps *mediacaps = NULL;
740 caps = gst_caps_new_empty ();
742 if (gst_dshow_check_mediatype (pin_mediatype->mediatype, MEDIASUBTYPE_PCM,
743 FORMAT_WaveFormatEx)) {
744 GstAudioFormat format = GST_AUDIO_FORMAT_UNKNOWN;
745 WAVEFORMATEX *wavformat =
746 (WAVEFORMATEX *) pin_mediatype->mediatype->pbFormat;
748 switch (wavformat->wFormatTag) {
749 case WAVE_FORMAT_PCM:
750 format = gst_audio_format_build_integer (TRUE, G_BYTE_ORDER, wavformat->wBitsPerSample, wavformat->wBitsPerSample);
756 if (format != GST_AUDIO_FORMAT_UNKNOWN) {
759 gst_audio_info_init(&info);
760 gst_audio_info_set_format(&info,
762 wavformat->nSamplesPerSec,
763 wavformat->nChannels,
765 mediacaps = gst_audio_info_to_caps(&info);
769 src->pins_mediatypes =
770 g_list_append (src->pins_mediatypes, pin_mediatype);
771 gst_caps_append (caps, mediacaps);
773 gst_dshow_free_pin_mediatype (pin_mediatype);
776 gst_dshow_free_pin_mediatype (pin_mediatype);
779 gst_dshow_free_pin_mediatype (pin_mediatype);
783 if (caps && gst_caps_is_empty (caps)) {
784 gst_caps_unref (caps);
792 gst_dshowaudiosrc_push_buffer (guint8 * buffer, guint size, gpointer src_object,
793 GstClockTime duration)
795 GstDshowAudioSrc *src = GST_DSHOWAUDIOSRC (src_object);
797 if (!buffer || size == 0 || !src) {
801 g_mutex_lock (&src->gbarray_lock);
802 g_byte_array_prepend (src->gbarray, buffer, size);
803 g_mutex_unlock (&src->gbarray_lock);