/* * Copyright (c) Linux community. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include #include #include #include #include "audio-sources.h" #include "pulseaudio.h" #include "support.h" // _(x) #include "dconf.h" #include "utility.h" #include "log.h" #include "dbus-player.h" // Note: The get_audio_devices() function has two variants. // This function returns the list of available audio-devices (audio sources) such as sound-cards, microphones/webcams. // The 1.st version uses PulseAudio to get the device list. // The second variant uses GStreamer, but GStreamer fails to return the device name (it is always NULL, at least in Ubuntu 10.x). // We should avoid the PulseAudio dependency and use GStreamer for all functions. // A List of audio devices (global to this module) // Contains: // * Real audio hardware, such as audio cards, microphones, webcams. // * Media Players like RhythmBox, Amarok and Banshee. // * Add also Skype if it's installed. Media Players and Skype can automatically control the recording via DBus. G_LOCK_DEFINE_STATIC(g_device_list); static GList *g_device_list = NULL; static GList *get_audio_devices(); static gchar *audio_sources_get_GNOME_default(); static GList *audio_sources_get_device_for_players(); static gchar *audio_sources_get_first_microphone(gboolean find_webcam); static GList *audio_sources_get_device_for_comm_programs(); static GList *audio_sources_get_device_from_settings(gint type); void audio_sources_init() { LOG_DEBUG("Init audio-sources.c.\n"); g_device_list = NULL; // Init Puleaudio module pulseaudio_module_init(); // Init DBus/Media Player modules dbus_player_init(); } void audio_sources_exit() { LOG_DEBUG("Clean up audio-sources.c.\n"); // Clean up pulseaudio molule pulseaudio_module_exit(); // Clean up media players/DBus dbus_player_exit(); audio_sources_free_list(g_device_list); g_device_list = NULL; } // --------------------------------------------------- // DeviceItem functions DeviceItem *device_item_create(gchar *id, gchar *description) { DeviceItem *item = g_malloc0(sizeof(DeviceItem)); if (id) item->id = g_strdup(id); if (description) item->description = g_strdup(description); item->icon_name = NULL; item->index = PA_INVALID_INDEX; item->sink_index = PA_INVALID_INDEX; return item; } DeviceItem *device_item_copy(DeviceItem *item) { // Make a copy of DeviceItem DeviceItem *copy = (DeviceItem*)g_memdup(item, sizeof(DeviceItem)); copy->id = g_strdup(item->id); copy->description = g_strdup(item->description); copy->icon_name = g_strdup(item->icon_name); return copy; } void device_item_free(DeviceItem *item) { if (!item) return; // Free values if (item->id) g_free(item->id); if (item->description) g_free(item->description); if (item->icon_name) g_free(item->icon_name); // Free the node g_free(item); } const gchar *device_item_get_type_name(guint type) { switch (type) { case NOT_DEFINED: return "NOT_DEFINED"; case DEFAULT_DEVICE: return "DEFAULT_DEVICE"; case AUDIO_SINK: return "AUDIO_SINK"; case AUDIO_SINK_MONITOR: return "AUDIO_SINK_MONITOR"; case AUDIO_INPUT: return "AUDIO_INPUT"; case MEDIA_PLAYER: return "MEDIA_PLAYER"; case COMM_PROGRAM: return "COMM_PROGRAM"; case USER_DEFINED: return "USER_DEFINED"; } return "UNKNOWN TYPE"; } // --------------------------------------------------- DeviceItem *audio_sources_find_in_list(GList *lst, gchar *device_id) { // Return DeviceItem for the given device_id. GList *l = g_list_first(lst); while (l) { DeviceItem *item = (DeviceItem*)l->data; // Compare ids // if (!g_strcmp0(item->id, device_id) || (device_id == NULL && item->id == NULL)) { if (!g_strcmp0(item->id, device_id)) { return item; } l = g_list_next(l); } return NULL; } DeviceItem *audio_sources_find_id(gchar *device_id) { // Return DeviceItem for the given device_id. // Lock G_LOCK(g_device_list); DeviceItem *found_item = audio_sources_find_in_list(g_device_list, device_id); // Unlock G_UNLOCK(g_device_list); return found_item; } // --------------------------------------------------- // Functions related to audio sources void audio_sources_free_list(GList *lst) { // Free the list and all its DeviceItems g_list_foreach(lst, (GFunc)device_item_free, NULL); g_list_free(lst); lst = NULL; } void audio_sources_print_list_ex() { // Print device list audio_sources_print_list(g_device_list, "Device list"); } void audio_sources_print_list(GList *list, gchar *tag) { // Print device list LOG_MSG("\n%s:\n", tag); GList *l = g_list_first(list); gint i = 0; while (l) { DeviceItem *item = (DeviceItem*)l->data; const gchar *type_name = device_item_get_type_name(item->type); LOG_MSG("#%d:type=%s(%d) pa.index=%d, id=%s, descr=%s\n", i++, type_name, item->type, item->index, item->id, item->description); LOG_MSG("\ticon_name=%s pa.sink_index=%d\n",item->icon_name, item->sink_index); l = g_list_next(l); } LOG_MSG("-------------------------------------------\n"); } GList *audio_sources_get_for_type(gint type) { // Return a GList of DeviceItems that match the type. The type can be a OR'ed value. // Eg. GList *lst = audio_sources_get_for_type(AUDIO_INPUT | AUDIO_SINK_MONITOR | DEFAULT_DEVICE); // ... // audio_sources_free_list(lst); // lst = NULL; // Set lock G_LOCK(g_device_list); GList *lst = NULL; GList *n = g_list_first(g_device_list); while (n) { DeviceItem *item = (DeviceItem*)n->data; // Test the type if (item->type & type) { DeviceItem *copy = device_item_copy(item); lst = g_list_append(lst, copy); } // Next item n = g_list_next(n); } // Free the lock G_UNLOCK(g_device_list); // Notice: // The returned list is a deep copy. You must free it with // audio_sources_free_list(lst); // lst = NULL; return lst; } void audio_sources_device_changed(gchar *device_id) { // Device selection in the main window has changed. // Disconnect/re-connect DBus signals (if the device_id is Media Player or Skype) dbus_player_player_changed(device_id/*=player->service_name*/); } GList *audio_sources_wash_device_list(GList *dev_list) { // Wash the given device list. // User may have unplugged webcams and microphones, etc. // This function gets a fresh device list from pulseaudio (or gstreamer) and removes invalid/unplugged items from the list. // Invalid devices may crash the GStreamer pipeline. // Create a new list for valid devs GList *new_list = NULL; // Get fresh device list from pulseaudio (or gstreamer) GList *fresh_dev_list = get_audio_devices(); GList *n = g_list_first(dev_list); while (n) { gchar *dev_id = (gchar*)n->data; // dev_id is in fresh_dev_list? if (audio_sources_find_in_list(fresh_dev_list, dev_id)) { // Yes new_list = g_list_append(new_list, g_strdup(dev_id)); } n = g_list_next(n); } // Free fresh_dev_list audio_sources_free_list(fresh_dev_list); // Return new_list. // Caller should free this with free_str_list(new_list). return new_list; } GList *audio_sources_get_device_NEW(gchar **audio_source) { // Return audio_source and device list as set in the GUI and [Additional settings] dialog. *audio_source = NULL; gchar *dev_id = NULL; GList *dev_list = NULL; gchar *source = NULL; gint type = -1; // These are set in the main window (in device/player combo) conf_get_string_value("audio-device-id", &dev_id); conf_get_int_value("audio-device-type", &type); // Normally "pulsesrc" source = g_strdup(DEFAULT_AUDIO_SOURCE); // Type is Media Player? if (type == MEDIA_PLAYER) { dev_list = audio_sources_get_device_for_players(); // Type is Skype or similar comm program? } else if (type == COMM_PROGRAM) { dev_list = audio_sources_get_device_for_comm_programs(); // Type is "User defined audio source" ? } else if (type == USER_DEFINED) { // Get the device list from GSettings (dconf). // Devices are set in the [Additional settings] dialog. // See dconf-editor, key: /apps/audio-recorder/players/ dev_list = audio_sources_get_device_from_settings(type); // Type is "System's default device"? } else if (type == DEFAULT_DEVICE) { g_free(source); source = audio_sources_get_GNOME_default(); // Let the audio-source settle the device dev_list = NULL; } else { // Type is AUDIO_SINK_MONITOR or AUDIO_INPUT device // Add the dev_id to dev_list dev_list = g_list_append(dev_list, g_strdup(dev_id)); } g_free(dev_id); // Return values *audio_source = source; // Debug print: // print_str_glist("Final device list", new_list); // Caller should g_free() the returned audio_source. // Caller should free dev_list with str_list_free(dev_list). return dev_list; } static gchar *audio_sources_get_GNOME_default() { // Try first "gconfaudiosrc" // Its real value is probably set in the DConf registry. // Start gconf-editor and browse to key /system/gstreamer/0.10/default (audiosrc). gchar *source = g_strdup("gconfaudiosrc"); GstElement *elem = gst_element_factory_make(source, "test-audio-source"); gboolean ret = (elem != NULL); g_object_unref(elem); if (!ret) { g_free(source); // Try "autoaudiosrc" source = g_strdup("autoaudiosrc"); elem = gst_element_factory_make(source, "test-audio-source"); ret = (elem != NULL); g_object_unref(elem); } if (!ret) { g_free(source); source = g_strdup(DEFAULT_AUDIO_SOURCE); } // Caller should g_free() this value return source; } #if 0 static gchar *audio_sources_get_first_audio_card() { // Return the first AUDIO_SINK_MONITOR (audiocard with loudspeakers that we can record from) gchar *device_id = NULL; GList *lst = audio_sources_get_for_type(AUDIO_SINK_MONITOR); GList *first = g_list_first(lst); if (first) { DeviceItem *item = (DeviceItem*)first->data; device_id = g_strdup(item->id); } // Free lst audio_sources_free_list(lst); // Caller should g_free() this value return device_id; } #endif static gchar *audio_sources_get_last_audio_card() { // Return the last AUDIO_SINK_MONITOR (audiocard with loudspeakers that we can record from) gchar *device_id = NULL; GList *lst = audio_sources_get_for_type(AUDIO_SINK_MONITOR); GList *last = g_list_last(lst); if (last) { DeviceItem *item = (DeviceItem*)last->data; device_id = g_strdup(item->id); } // Free lst audio_sources_free_list(lst); // Caller should g_free() this value return device_id; } static gchar *audio_sources_get_first_microphone(gboolean find_webcam) { // Return the first AUDIO_INPUT (input device; microphone or webcam with microphone) GList *lst = audio_sources_get_for_type(AUDIO_INPUT); if (!lst) return NULL; gchar *device_id = NULL; if (find_webcam) { GList *n = g_list_first(lst); while (n) { DeviceItem *item = (DeviceItem*)n->data; // Stupid test to find a "webcam". if (audio_sources_device_is_webcam(item->description)) { device_id = g_strdup(item->id); break; } n = g_list_next(n); } } if (!device_id) { // Take the first one in the list DeviceItem *item = (DeviceItem*)lst->data; device_id = g_strdup(item->id); } // Free lst audio_sources_free_list(lst); // Caller should g_free() this value return device_id; } static GList *audio_sources_get_device_for_players() { // Return device list for Media Players (RhythmBox, Banshee, Amarok, etc.) GList *dev_list = audio_sources_get_device_from_settings(MEDIA_PLAYER); // The list is empty? // There were no devives in GSettings for MEDIA_PLAYER. // See [Additional settings] dialog. if (!dev_list) { gchar *dev_id = audio_sources_get_last_audio_card(); if (dev_id) { dev_list = g_list_append(dev_list, dev_id/*steal the string*/); } } return dev_list; } static GList *audio_sources_get_device_for_comm_programs() { // Return device list for communication programs like Skype GList *dev_list = audio_sources_get_device_from_settings(COMM_PROGRAM); // The list is empty? // There were no devices in GSettings for COMM_PROGRAM. // See [Additional settings] dialog. if (!dev_list) { // For COMM_PROGRAMs, such as Skype, we need to record from two devices; audio-card and microphone. // Take last audio-card in the list (this is a guess. we could take the first one too) gchar *dev_id = audio_sources_get_last_audio_card(); if (dev_id) { dev_list = g_list_append(dev_list, dev_id/*steal the string*/); } // + Take the first microphone/webcam dev_id = audio_sources_get_first_microphone(TRUE/*find webcam if possible*/); if (dev_id) { dev_list = g_list_append(dev_list, dev_id/*steal the string*/); } } return dev_list; } static GList *audio_sources_get_device_from_settings(gint type) { // Get the device list from GSettings. // This is set in the [Additional settings] dialog. // See dconf-editor, key: /apps/audio-recorder/players/ GList *dev_lst = NULL; gchar *conf_key = g_strdup_printf("players/device-type-%d", type); conf_get_string_list(conf_key, &dev_lst); g_free(conf_key); return dev_lst; } gboolean audio_sources_device_is_webcam(gchar *dev_name) { // A very primitive test to determine if dev_name is a web-camera // TODO: Find a better way to test if the device is a webcam. // Some typical words we find in names of webcams // See: http://www.ideasonboard.org/uvc/ gchar *cam_names[] = {"cam ", "amera", "amcorder", "web", "motion", "islim", "eface", "pix ", "pixel", NULL}; // To lower case gchar *name = g_utf8_strdown(dev_name, -1); gint i = 0; for (i=0; cam_names[i]; i++) { gchar *p = g_strrstr(name, cam_names[i]); if (p) return TRUE; } return FALSE; } void audio_sources_load_device_list() { // Reload device list // Lock G_LOCK(g_device_list); // Free the old list audio_sources_free_list(g_device_list); g_device_list = NULL; // 1) Get list of real audio devices (fill to g_device_list) g_device_list = get_audio_devices(); // 2) Add Media Players, Skype etc. to g_device_list (these can control the recording via DBus) // See the dbus_xxxx.c modules // ---------------------------- // Get the player list (it's a GHashTable) GHashTable *player_list = dbus_player_get_player_list(); GHashTableIter hash_iter; gpointer key, value; // Add to the g_device_list g_hash_table_iter_init(&hash_iter, player_list); while (g_hash_table_iter_next(&hash_iter, &key, &value)) { MediaPlayerRec *p = (MediaPlayerRec*)value; gchar *t = p->app_name; if (!t) t = p->service_name; // New DeviceItem DeviceItem *item = device_item_create(p->service_name, p->app_name); item->icon_name = g_strdup(p->icon_name); // Set type (COMM_PROGRAM or MEDIA_PLAYER) if (p->type) item->type = p->type; else item->type = MEDIA_PLAYER; g_device_list = g_list_append(g_device_list, item); } // 3) Add "User defined audio source" (user defined group of devices, selected for recording) // ---------------------------- gchar *name = g_strdup(_("User defined audio source")); DeviceItem *item = device_item_create("user-defined", name); item->type = USER_DEFINED; item->icon_name = g_strdup("audio-card.png"); g_device_list = g_list_append(g_device_list, item); g_free(name); #if defined(ACTIVE_DEBUGGING) || defined(DEBUG_ALL) // Print device list audio_sources_print_list_ex(); #endif // The g_device_list is now loaded and ready // Unlock G_UNLOCK(g_device_list); } // -------------------------------------------- // Combobox related functions // -------------------------------------------- GtkWidget *audio_sources_create_combo() { // Create a GtkComboBox with N_DEVICE_COLUMNS // Create list store GtkListStore *store = gtk_list_store_new(N_DEVICE_COLUMNS, G_TYPE_INT, G_TYPE_STRING, GDK_TYPE_PIXBUF, G_TYPE_STRING); GtkWidget *combo = gtk_combo_box_new(); gtk_combo_box_set_model(GTK_COMBO_BOX(combo),GTK_TREE_MODEL(store)); // Unref store g_object_unref(G_OBJECT(store)); // Device type (see the description of DeviceType enum) GtkCellRenderer *cell = gtk_cell_renderer_text_new(); g_object_set(cell, "visible", 0, NULL); gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo), cell, FALSE); gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo), cell, "text", COL_DEVICE_TYPE, NULL); // Device id column, invisible cell = gtk_cell_renderer_text_new(); g_object_set(cell, "visible", 0, NULL); gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo), cell, FALSE); gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo), cell, "text", COL_DEVICE_ID, NULL); // Pixbuf (device icon) cell = gtk_cell_renderer_pixbuf_new(); gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo), cell, FALSE); gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo), cell, "pixbuf", COL_DEVICE_ICON, NULL); // Device description column, visible cell = gtk_cell_renderer_text_new(); gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo), cell, FALSE); gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo), cell, "text", COL_DEVICE_DESCR, NULL); return combo; } void audio_source_fill_combo(GtkWidget *combo) { GtkTreeModel *model = gtk_combo_box_get_model(GTK_COMBO_BOX(combo)); GtkTreeIter iter; if (!GTK_IS_LIST_STORE(GTK_LIST_STORE(model))) { return; } // Disable "changed" signal gulong signal_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(combo), "selection-changed-signal")); g_signal_handler_block(combo, signal_id); // Clear the old combo list gtk_list_store_clear(GTK_LIST_STORE(model)); // Get a new list of: // 1) Audio devices, audio-card w/ loudspeakers, webcams, microphones, etc. // 2) + Add media players we can see on the DBus. audio_sources_load_device_list(); // Set lock G_LOCK(g_device_list); // Then add items to the ComboBox GList *n = g_list_first(g_device_list); while (n) { DeviceItem *item = (DeviceItem*)n->data; // Exclude AUDIO_SINK devices if (item->type == AUDIO_SINK) { // Next item thanks n = g_list_next(n); continue; } // Add new row gtk_list_store_append(GTK_LIST_STORE(model), &iter); // Load icon const gchar *p = item->icon_name; GdkPixbuf *pixbuf = NULL; if (item->type == MEDIA_PLAYER || item->type == COMM_PROGRAM) { pixbuf = load_icon_pixbuf((gchar*)p); // Got icon?? if (!GDK_IS_PIXBUF(pixbuf)) { // No. Display default icon. p = "mediaplayer.png"; } } if (!p) { p = "loudspeaker.png"; } if (!GDK_IS_PIXBUF(pixbuf)) { // No. Load a default icon. gchar *path = get_image_path(p); pixbuf = get_pixbuf_from_file(path, 22, 22); g_free(path); } // Set column data gtk_list_store_set(GTK_LIST_STORE(model), &iter, COL_DEVICE_TYPE, item->type, /* type */ COL_DEVICE_ID, item->id, /* internal device id or media player id */ COL_DEVICE_ICON, pixbuf, /* icon pixbuf */ COL_DEVICE_DESCR, item->description /* visible text */, -1); // Pixbuf has a reference count of 2 now, as the list store has added its own if (GDK_IS_PIXBUF(pixbuf)) { g_object_unref(pixbuf); } // Next item n = g_list_next(n); } // Enable changed-signal g_signal_handler_unblock(combo, signal_id); // Free the lock G_UNLOCK(g_device_list); } void audio_sources_combo_set_id(GtkWidget *combo, gchar *device_id) { // Set combo selection by device id GtkTreeModel *model = gtk_combo_box_get_model(GTK_COMBO_BOX(combo)); if (!GTK_IS_TREE_MODEL(model)) { return; } GtkTreeIter iter; gboolean valid = gtk_tree_model_get_iter_first(model, &iter); while (valid) { gchar *id = NULL; gtk_tree_model_get(model, &iter, COL_DEVICE_ID, &id, -1); // Compare ids if (!g_strcmp0(device_id, id)) { // Select it gtk_combo_box_set_active_iter(GTK_COMBO_BOX(combo), &iter); return; } valid = gtk_tree_model_iter_next(model, &iter); } // str_value was not found. Select the first, 0'th row valid = gtk_tree_model_get_iter_first(model, &iter); if (valid) { gtk_combo_box_set_active_iter(GTK_COMBO_BOX(combo), &iter); } } gboolean audio_sources_combo_get_values(GtkWidget *combo, gchar **device_name, gchar **device_id, gint *device_type) { *device_name = NULL; *device_id = NULL; *device_type = -1; GtkTreeModel *model = gtk_combo_box_get_model(GTK_COMBO_BOX(combo)); if (!GTK_IS_TREE_MODEL(model)) { return FALSE; } GtkTreeIter iter; if (!gtk_combo_box_get_active_iter(GTK_COMBO_BOX(combo), &iter)) return FALSE; gtk_tree_model_get(model, &iter, COL_DEVICE_ID, device_id, COL_DEVICE_DESCR, device_name, COL_DEVICE_TYPE, device_type, -1); // The caller should g_free() both device_name and device_id. return TRUE; } // Choose one of these get_audio_devices() functions #if 1 static GList *get_audio_devices() { // Use PulseAudio to get the device ids and names. // This returns both device id and correct name/description. // Compilation requires -lpulse: pkg-config --cflags --libs libpulse GList *pa_list = pa_get_source_list(); // Take a copy of the PulseAudio's data. Let all modules (puleaudio.c, audio-source.c, etc.) have their own private lists. GList *lst = NULL; GList *n = g_list_first(pa_list); while (n) { DeviceItem *item = (DeviceItem*)n->data; // Take a copy DeviceItem *copy = device_item_copy(item); // Add to our private list lst = g_list_append(lst, copy); n = g_list_next(n); } // Add "Default" device // See also: http://pulseaudio.org/wiki/DefaultDevice // Translators: This is system's default audio device. DeviceItem *item = device_item_create("default-device"/*id*/, _("System's default device")/*description*/); item->type = DEFAULT_DEVICE; lst = g_list_append(lst, item); // Return lst return lst; } #endif #if 0 static GList *get_audio_devices() { // Get audio devices (audio sources) from GStreamer. // This returns correct device id, but the name/description is NULL (at least for some audio cards). GList *lst = NULL; gchar *pipe_name = DEFAULT_AUDIO_SOURCE; GstElementFactory *factory; GstElement *element; factory = gst_element_factory_find(pipe_name); if (!factory) { GST_WARNING("Failed to find factory for pipeline %s.\n", pipe_name); return lst; } element = gst_element_factory_create(factory, "test"); const gchar *longname = gst_element_factory_get_longname(factory); if (!element) { GST_WARNING("Failed to create instance of factory '%s' (%s)", longname, GST_PLUGIN_FEATURE (factory)->name); return lst; } // const gchar *klass_name = gst_element_factory_get_klass(factory); // const gchar *description = gst_element_factory_get_description(factory); // const gchar *author = gst_element_factory_get_author(factory); gint n; gchar *name; GValueArray *array; GObjectClass *klass = G_OBJECT_GET_CLASS(element); GstPropertyProbe *probe = GST_PROPERTY_PROBE(element); const GParamSpec *pspec = gst_property_probe_get_property(probe, "device"); /* Do we have a "device" property? */ if (!g_object_class_find_property(klass, "device") || !GST_IS_PROPERTY_PROBE(element) || !probe || !pspec) { GST_DEBUG ("Found source '%s' (%s) - no device", longname, GST_PLUGIN_FEATURE (factory)->name); return lst; } /* Set autoprobe[-fps] to FALSE to avoid delays when probing. */ if (g_object_class_find_property(klass, "autoprobe")) { g_object_set(G_OBJECT(element), "autoprobe", FALSE, NULL); } if (g_object_class_find_property(klass, "autoprobe-fps")) { g_object_set(G_OBJECT (element), "autoprobe-fps", FALSE, NULL); } array = gst_property_probe_probe_and_get_values(probe, pspec); if (array != NULL) { /* default device item, so we can let the element handle it */ if (array->n_values > 0) { g_print("This is the default device (default)\n"); } for (n = 0; n < array->n_values; n++) { GValue *device; device = g_value_array_get_nth(array, n); g_object_set_property(G_OBJECT(element), "device", device); if (gst_element_set_state(element, GST_STATE_READY) != GST_STATE_CHANGE_SUCCESS) { GST_WARNING("Found source '%s' (%s) - device %s failed to open", longname, GST_PLUGIN_FEATURE(factory)->name, g_value_get_string(device)); continue; } gchar *device_id = (gchar*)g_value_get_string(device); g_object_get(G_OBJECT(element), "device-name", &name, NULL); // TODO: Fix this bug. The name is always NULL. // See Launchpad bug: https://bugs.launchpad.net/ubuntu/+source/gstreamer0.10/+bug/607162 // NULL? if (!name) { // Show id name = device_id; } DeviceItem *item = device_item_create(device_id, name); // Is this an "Audio Output" device? // TODO: Find a better way to determine if this is a sink (audio output) device. if (g_str_has_suffix(device_id, ".monitor")) { // Set the type item->type = AUDIO_SINK_MONITOR; } // TODO: Detect microphones (type AUDIO_INPUT) lst = g_list_append(lst, item); // g_print("M1: Found source for '%s' (%s) - device %s '%s'\n", longname, GST_PLUGIN_FEATURE(factory)->name, g_value_get_string(device), name); } } gst_element_set_state(element, GST_STATE_NULL); gst_object_unref(GST_OBJECT(element)); gst_object_unref(GST_OBJECT(factory)); // Add "Default" device // See also: http://pulseaudio.org/wiki/DefaultDevice DeviceItem *item = device_item_create("default-device"/*id*/, _("System's default device")/*description*/); item->type = DEFAULT_DEVICE; lst = g_list_append(lst, item); return lst; } #endif #if 0 // A better solution that uses GStreamer directly. // This function shows how to get the device lists (both output and input devices) with GStreamer. // This can later replace the pulseaudio.[ch] module. // I got this prototype function from gst-plugins-good's source code. // $ apt-get source gst-plugins-good // $ ls -l gst-plugins-good/tests/examples/pulse/ static GList *get_gstreamer_devices(gchar *name) { // The name should be: "pulsesink" or "pulsesrc". GstElement *element; GstPropertyProbe *probe = NULL; const GParamSpec *pspec = NULL; GValueArray *array = NULL; guint i; g_print ("Listing devices for %s\n", name); element = gst_element_factory_make (name, NULL); g_assert (element); gst_element_set_state (element, GST_STATE_READY); probe = GST_PROPERTY_PROBE (element); pspec = gst_property_probe_get_property (probe, "device"); array = gst_property_probe_probe_and_get_values (probe, pspec); g_assert (array); for (i = 0; i < array->n_values; i++) { GValue *device = NULL; gchar *name = NULL; device = g_value_array_get_nth (array, i); g_object_set_property (G_OBJECT (element), "device", device); g_object_get (G_OBJECT (element), "device-name", &name, NULL); g_print ("device: %s (%s)\n", g_value_get_string (device), GST_STR_NULL (name)); } g_value_array_free (array); gst_element_set_state (element, GST_STATE_NULL); gst_object_unref (GST_OBJECT (element)); } /* get_gstreamer_devices("pulsesink"); Listing devices for pulsesink: (output devices = normally sound cards with loudspeakers) device: alsa_output.pci-0000_00_1b.0.analog-stereo (Intern lyd Analoginen stereo) device: alsa_output.pci-0000_06_01.0.analog-stereo (SB0400 Audigy2 Value Analoginen stereo) get_gstreamer_devices("pulsesrc"); Listing devices for pulsesrc (audio sources = input devices = devices we can tap sound from. Includes sound cards, webcams, microphones etc.) device: alsa_output.pci-0000_00_1b.0.analog-stereo.monitor (Monitor of Intern lyd Analoginen stereo) device: alsa_output.pci-0000_06_01.0.analog-stereo.monitor (Monitor of SB0400 Audigy2 Value Analoginen stereo) device: alsa_input.usb-Creative_Technology_Ltd._VF0610_Live__Cam_Socialize_HD_091214_b_00556-02-HD.analog-mono (VF0610 Live! Cam Socialize HD Analoginen mono) */ #endif