~ubuntu-branches/ubuntu/oneiric/empathy/oneiric-security

« back to all changes in this revision

Viewing changes to src/empathy-audio-src.c

  • Committer: Bazaar Package Importer
  • Author(s): Ken VanDine
  • Date: 2011-08-22 14:16:36 UTC
  • mfrom: (1.1.75 upstream)
  • Revision ID: james.westby@ubuntu.com-20110822141636-iiju0fvx1f9sl3oz
Tags: 3.1.5.1-1ubuntu1
* New upstream version
  - crashed with SIGSEGV in dbus_connection_dispatch (LP: #829826)

Show diffs side-by-side

added added

removed removed

Lines of Context:
22
22
#include <stdio.h>
23
23
#include <stdlib.h>
24
24
 
25
 
#include <pulse/pulseaudio.h>
26
 
#include <pulse/glib-mainloop.h>
27
 
 
28
25
#include <libempathy/empathy-utils.h>
29
26
#include <libempathy-gtk/empathy-call-utils.h>
30
27
 
31
28
#include "empathy-audio-src.h"
32
29
 
33
30
#include "src-marshal.h"
 
31
#include "empathy-mic-monitor.h"
34
32
 
35
33
#define DEBUG_FLAG EMPATHY_DEBUG_VOIP
36
34
#include <libempathy/empathy-debug.h>
42
40
{
43
41
    PEAK_LEVEL_CHANGED,
44
42
    RMS_LEVEL_CHANGED,
45
 
    MICROPHONE_ADDED,
46
 
    MICROPHONE_REMOVED,
47
43
    LAST_SIGNAL
48
44
};
49
45
 
66
62
  GstElement *volume;
67
63
  GstElement *level;
68
64
 
69
 
  pa_glib_mainloop *loop;
70
 
  pa_context *context;
71
 
  GQueue *operations;
 
65
  EmpathyMicMonitor *mic_monitor;
72
66
 
73
67
  /* 0 if not known yet */
74
68
  guint source_output_idx;
86
80
  (G_TYPE_INSTANCE_GET_PRIVATE ((o), EMPATHY_TYPE_GST_AUDIO_SRC, \
87
81
  EmpathyGstAudioSrcPrivate))
88
82
 
89
 
static gboolean
 
83
gboolean
90
84
empathy_audio_src_supports_changing_mic (EmpathyGstAudioSrc *self)
91
85
{
92
86
  EmpathyGstAudioSrcPrivate *priv = EMPATHY_GST_AUDIO_SRC_GET_PRIVATE (self);
98
92
          "source-output-index") != NULL);
99
93
}
100
94
 
101
 
typedef void (*OperationFunc) (EmpathyGstAudioSrc *, GSimpleAsyncResult *);
102
 
 
103
 
typedef struct
104
 
{
105
 
  OperationFunc func;
106
 
  GSimpleAsyncResult *result;
107
 
} Operation;
108
 
 
109
 
static Operation *
110
 
operation_new (OperationFunc func,
111
 
    GSimpleAsyncResult *result)
112
 
{
113
 
  Operation *o = g_slice_new0 (Operation);
114
 
 
115
 
  o->func = func;
116
 
  o->result = result;
117
 
 
118
 
  return o;
119
 
}
120
 
 
121
 
static void
122
 
operation_free (Operation *o,
123
 
    gboolean cancelled)
124
 
{
125
 
  if (cancelled)
126
 
    {
127
 
      g_simple_async_result_set_error (o->result,
128
 
          G_IO_ERROR, G_IO_ERROR_CANCELLED,
129
 
          "The audio source was disposed");
130
 
      g_simple_async_result_complete (o->result);
131
 
      g_object_unref (o->result);
132
 
    }
133
 
 
134
 
  g_slice_free (Operation, o);
135
 
}
136
 
 
137
 
static void
138
 
operation_get_microphones_free (gpointer data)
139
 
{
140
 
  GQueue *queue = data;
141
 
  GList *l;
142
 
 
143
 
  for (l = queue->head; l != NULL; l = l->next)
144
 
    {
145
 
      EmpathyAudioSrcMicrophone *mic = l->data;
146
 
 
147
 
      g_free (mic->name);
148
 
      g_free (mic->description);
149
 
      g_slice_free (EmpathyAudioSrcMicrophone, mic);
150
 
    }
151
 
 
152
 
  g_queue_free (queue);
153
 
}
154
 
 
155
 
static void
156
 
operation_get_microphones_cb (pa_context *context,
157
 
    const pa_source_info *info,
158
 
    int eol,
159
 
    void *userdata)
160
 
{
161
 
  GSimpleAsyncResult *result = userdata;
162
 
  EmpathyAudioSrcMicrophone *mic;
163
 
  GQueue *queue;
164
 
 
165
 
  if (eol)
166
 
    {
167
 
      g_simple_async_result_complete (result);
168
 
      g_object_unref (result);
 
95
static void
 
96
empathy_audio_src_microphone_changed_cb (EmpathyMicMonitor *monitor,
 
97
    guint source_output_idx,
 
98
    guint source_idx,
 
99
    gpointer user_data)
 
100
{
 
101
  EmpathyGstAudioSrc *self = user_data;
 
102
  EmpathyGstAudioSrcPrivate *priv = EMPATHY_GST_AUDIO_SRC_GET_PRIVATE (self);
 
103
  guint audio_src_idx = PA_INVALID_INDEX;
 
104
 
 
105
  g_object_get (priv->src, "source-output-index", &audio_src_idx, NULL);
 
106
 
 
107
  if (source_output_idx == PA_INVALID_INDEX
 
108
      || source_output_idx != audio_src_idx)
 
109
    return;
 
110
 
 
111
  if (priv->source_idx == source_idx)
 
112
    return;
 
113
 
 
114
  priv->source_idx = source_idx;
 
115
  g_object_notify (G_OBJECT (self), "microphone");
 
116
}
 
117
 
 
118
static void
 
119
empathy_audio_src_get_current_mic_cb (GObject *source_object,
 
120
    GAsyncResult *result,
 
121
    gpointer user_data)
 
122
{
 
123
  EmpathyMicMonitor *monitor = EMPATHY_MIC_MONITOR (source_object);
 
124
  EmpathyGstAudioSrc *self = user_data;
 
125
  EmpathyGstAudioSrcPrivate *priv = EMPATHY_GST_AUDIO_SRC_GET_PRIVATE (self);
 
126
  guint source_idx;
 
127
  GError *error = NULL;
 
128
 
 
129
  source_idx = empathy_mic_monitor_get_current_mic_finish (monitor, result, &error);
 
130
 
 
131
  if (error != NULL)
 
132
    {
 
133
      DEBUG ("Failed to get current mic: %s", error->message);
 
134
      g_clear_error (&error);
169
135
      return;
170
136
    }
171
137
 
172
 
  mic = g_slice_new0 (EmpathyAudioSrcMicrophone);
173
 
  mic->index = info->index;
174
 
  mic->name = g_strdup (info->name);
175
 
  mic->description = g_strdup (info->description);
176
 
  mic->is_monitor = (info->monitor_of_sink != PA_INVALID_INDEX);
177
 
 
178
 
  /* add it to the queue */
179
 
  queue = g_simple_async_result_get_op_res_gpointer (result);
180
 
  g_queue_push_tail (queue, mic);
181
 
}
182
 
 
183
 
static void
184
 
operation_get_microphones (EmpathyGstAudioSrc *self,
185
 
    GSimpleAsyncResult *result)
186
 
{
187
 
  EmpathyGstAudioSrcPrivate *priv = EMPATHY_GST_AUDIO_SRC_GET_PRIVATE (self);
188
 
 
189
 
  g_assert_cmpuint (pa_context_get_state (priv->context), ==, PA_CONTEXT_READY);
190
 
 
191
 
  g_simple_async_result_set_op_res_gpointer (result, g_queue_new (),
192
 
      operation_get_microphones_free);
193
 
 
194
 
  pa_context_get_source_info_list (priv->context,
195
 
      operation_get_microphones_cb, result);
196
 
}
197
 
 
198
 
static void
199
 
operation_change_microphone_cb (pa_context *context,
200
 
    int success,
201
 
    void *userdata)
202
 
{
203
 
  GSimpleAsyncResult *result = userdata;
204
 
 
205
 
  if (!success)
206
 
    {
207
 
      g_simple_async_result_set_error (result, G_IO_ERROR, G_IO_ERROR_FAILED,
208
 
          "Failed to change microphone. Reason unknown.");
209
 
    }
210
 
 
211
 
  g_simple_async_result_complete (result);
212
 
  g_object_unref (result);
213
 
}
214
 
 
215
 
static void
216
 
operation_change_microphone (EmpathyGstAudioSrc *self,
217
 
    GSimpleAsyncResult *result)
218
 
{
219
 
  EmpathyGstAudioSrcPrivate *priv = EMPATHY_GST_AUDIO_SRC_GET_PRIVATE (self);
220
 
  guint source_output_idx, microphone;
221
 
 
222
 
  g_object_get (priv->src, "source-output-index", &source_output_idx, NULL);
223
 
 
224
 
  g_assert_cmpuint (pa_context_get_state (priv->context), ==, PA_CONTEXT_READY);
225
 
  g_assert_cmpuint (source_output_idx, !=, PA_INVALID_INDEX);
226
 
 
227
 
  microphone = GPOINTER_TO_UINT (
228
 
      g_simple_async_result_get_op_res_gpointer (result));
229
 
 
230
 
  pa_context_move_source_output_by_index (priv->context, source_output_idx, microphone,
231
 
      operation_change_microphone_cb, result);
232
 
}
233
 
 
234
 
static void
235
 
operations_run (EmpathyGstAudioSrc *self)
236
 
{
237
 
  EmpathyGstAudioSrcPrivate *priv = EMPATHY_GST_AUDIO_SRC_GET_PRIVATE (self);
238
 
  pa_context_state_t state = pa_context_get_state (priv->context);
239
 
  GList *l;
240
 
 
241
 
  if (state != PA_CONTEXT_READY)
242
 
    return;
243
 
 
244
 
  for (l = priv->operations->head; l != NULL; l = l->next)
245
 
    {
246
 
      Operation *o = l->data;
247
 
 
248
 
      o->func (self, o->result);
249
 
 
250
 
      operation_free (o, FALSE);
251
 
    }
252
 
 
253
 
  g_queue_clear (priv->operations);
254
 
}
255
 
 
256
 
static void
257
 
empathy_audio_src_source_output_info_cb (pa_context *context,
258
 
    const pa_source_output_info *info,
259
 
    int eol,
260
 
    void *userdata)
261
 
{
262
 
  EmpathyGstAudioSrc *self = userdata;
263
 
  EmpathyGstAudioSrcPrivate *priv = EMPATHY_GST_AUDIO_SRC_GET_PRIVATE (self);
264
 
 
265
 
  if (eol)
266
 
    return;
267
 
 
268
 
  /* There should only be one call here. */
269
 
 
270
 
  if (priv->source_idx == info->source)
271
 
    return;
272
 
 
273
 
  priv->source_idx = info->source;
 
138
  if (priv->source_idx == source_idx)
 
139
    return;
 
140
 
 
141
  priv->source_idx = source_idx;
274
142
  g_object_notify (G_OBJECT (self), "microphone");
275
143
}
276
144
 
277
145
static void
278
 
empathy_audio_src_source_info_cb (pa_context *context,
279
 
    const pa_source_info *info,
280
 
    int eol,
281
 
    void *userdata)
282
 
{
283
 
  EmpathyGstAudioSrc *self = userdata;
284
 
  gboolean is_monitor;
285
 
 
286
 
  if (eol)
287
 
    return;
288
 
 
289
 
  is_monitor = (info->monitor_of_sink != PA_INVALID_INDEX);
290
 
 
291
 
  g_signal_emit (self, signals[MICROPHONE_ADDED], 0,
292
 
      info->index, info->name, info->description, is_monitor);
293
 
}
294
 
 
295
 
static void
296
 
empathy_audio_src_pa_event_cb (pa_context *context,
297
 
    pa_subscription_event_type_t type,
298
 
    uint32_t idx,
299
 
    void *userdata)
300
 
{
301
 
  EmpathyGstAudioSrc *self = userdata;
302
 
  EmpathyGstAudioSrcPrivate *priv = EMPATHY_GST_AUDIO_SRC_GET_PRIVATE (self);
303
 
 
304
 
  if ((type & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT
305
 
      && (type & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_CHANGE
306
 
      && idx == priv->source_output_idx)
307
 
    {
308
 
      /* Microphone in the source output has changed */
309
 
      pa_context_get_source_output_info (context, idx,
310
 
          empathy_audio_src_source_output_info_cb, self);
311
 
    }
312
 
  else if ((type & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE
313
 
      && (type & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE)
314
 
    {
315
 
      /* A mic has been removed */
316
 
      g_signal_emit (self, signals[MICROPHONE_REMOVED], 0, idx);
317
 
    }
318
 
  else if ((type & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE
319
 
      && (type & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW)
320
 
    {
321
 
      /* A mic has been plugged in */
322
 
      pa_context_get_source_info_by_index (context, idx,
323
 
          empathy_audio_src_source_info_cb, self);
324
 
    }
325
 
}
326
 
 
327
 
static void
328
 
empathy_audio_src_pa_subscribe_cb (pa_context *context,
329
 
    int success,
330
 
    void *userdata)
331
 
{
332
 
  if (!success)
333
 
    DEBUG ("Failed to subscribe to PulseAudio events");
334
 
}
335
 
 
336
 
static void
337
 
empathy_audio_src_pa_state_change_cb (pa_context *context,
338
 
    void *userdata)
339
 
{
340
 
  EmpathyGstAudioSrc *self = userdata;
341
 
  EmpathyGstAudioSrcPrivate *priv = EMPATHY_GST_AUDIO_SRC_GET_PRIVATE (self);
342
 
  pa_context_state_t state = pa_context_get_state (priv->context);
343
 
 
344
 
  if (state == PA_CONTEXT_READY)
345
 
    {
346
 
      /* Listen to pulseaudio events so we know when sources are
347
 
       * added and when the microphone is changed. */
348
 
      pa_context_set_subscribe_callback (priv->context,
349
 
          empathy_audio_src_pa_event_cb, self);
350
 
      pa_context_subscribe (priv->context,
351
 
          PA_SUBSCRIPTION_MASK_SOURCE | PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT,
352
 
          empathy_audio_src_pa_subscribe_cb, NULL);
353
 
 
354
 
      operations_run (self);
355
 
    }
356
 
}
357
 
 
358
 
static void
359
146
empathy_audio_src_source_output_index_notify (GObject *object,
360
147
    GParamSpec *pspec,
361
148
    EmpathyGstAudioSrc *self)
374
161
  /* It's actually changed. */
375
162
  priv->source_output_idx = source_output_idx;
376
163
 
377
 
  pa_context_get_source_output_info (priv->context, source_output_idx,
378
 
      empathy_audio_src_source_output_info_cb, self);
 
164
  empathy_mic_monitor_get_current_mic_async (priv->mic_monitor,
 
165
      source_output_idx, empathy_audio_src_get_current_mic_cb, self);
379
166
}
380
167
 
381
168
static GstElement *
442
229
 
443
230
  gst_object_unref (G_OBJECT (src));
444
231
 
445
 
  /* PulseAudio stuff: We need to create a dummy pa_glib_mainloop* so
446
 
   * Pulse can use the mainloop that GTK has created for us. */
447
 
  priv->loop = pa_glib_mainloop_new (NULL);
448
 
  priv->context = pa_context_new (pa_glib_mainloop_get_api (priv->loop),
449
 
      "EmpathyAudioSrc");
450
 
 
451
232
  /* Listen to changes to GstPulseSrc:source-output-index so we know when
452
233
   * it's no longer PA_INVALID_INDEX (starting for the first time) or if it
453
234
   * changes (READY->NULL->READY...) */
455
236
      G_CALLBACK (empathy_audio_src_source_output_index_notify),
456
237
      obj);
457
238
 
458
 
  /* Finally listen for state changes so we know when we've
459
 
   * connected. */
460
 
  pa_context_set_state_callback (priv->context,
461
 
      empathy_audio_src_pa_state_change_cb, obj);
462
 
  pa_context_connect (priv->context, NULL, 0, NULL);
 
239
  priv->mic_monitor = empathy_mic_monitor_new ();
 
240
  g_signal_connect (priv->mic_monitor, "microphone-changed",
 
241
      G_CALLBACK (empathy_audio_src_microphone_changed_cb), obj);
463
242
 
464
 
  priv->operations = g_queue_new ();
 
243
  priv->source_idx = PA_INVALID_INDEX;
465
244
}
466
245
 
467
246
static void empathy_audio_src_dispose (GObject *object);
565
344
    G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
566
345
  g_object_class_install_property (object_class, PROP_RMS_LEVEL, param_spec);
567
346
 
568
 
 
569
347
  signals[RMS_LEVEL_CHANGED] = g_signal_new ("rms-level-changed",
570
348
    G_TYPE_FROM_CLASS (empathy_audio_src_class),
571
349
    G_SIGNAL_RUN_LAST,
573
351
    NULL, NULL,
574
352
    g_cclosure_marshal_VOID__DOUBLE,
575
353
    G_TYPE_NONE, 1, G_TYPE_DOUBLE);
576
 
 
577
 
  signals[MICROPHONE_ADDED] = g_signal_new ("microphone-added",
578
 
    G_TYPE_FROM_CLASS (empathy_audio_src_class),
579
 
    G_SIGNAL_RUN_LAST,
580
 
    0,
581
 
    NULL, NULL,
582
 
    _src_marshal_VOID__UINT_STRING_STRING_BOOLEAN,
583
 
    G_TYPE_NONE, 4, G_TYPE_UINT, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN);
584
 
 
585
 
  signals[MICROPHONE_REMOVED] = g_signal_new ("microphone-removed",
586
 
    G_TYPE_FROM_CLASS (empathy_audio_src_class),
587
 
    G_SIGNAL_RUN_LAST,
588
 
    0,
589
 
    NULL, NULL,
590
 
    g_cclosure_marshal_VOID__UINT,
591
 
    G_TYPE_NONE, 1, G_TYPE_UINT);
592
354
}
593
355
 
594
356
void
607
369
 
608
370
  priv->idle_id = 0;
609
371
 
610
 
  if (priv->context != NULL)
611
 
    pa_context_unref (priv->context);
612
 
  priv->context = NULL;
613
 
 
614
 
  if (priv->loop != NULL)
615
 
    pa_glib_mainloop_free (priv->loop);
616
 
  priv->loop = NULL;
 
372
  tp_clear_object (&priv->mic_monitor);
617
373
 
618
374
  /* release any references held by the object here */
619
375
 
630
386
  /* free any data held directly by the object here */
631
387
  g_mutex_free (priv->lock);
632
388
 
633
 
  g_queue_foreach (priv->operations, (GFunc) operation_free,
634
 
      GUINT_TO_POINTER (TRUE));
635
 
  g_queue_free (priv->operations);
636
 
 
637
389
  G_OBJECT_CLASS (empathy_audio_src_parent_class)->finalize (object);
638
390
}
639
391
 
761
513
  return volume;
762
514
}
763
515
 
764
 
void
765
 
empathy_audio_src_get_microphones_async (EmpathyGstAudioSrc *src,
766
 
    GAsyncReadyCallback callback,
767
 
    gpointer user_data)
768
 
{
769
 
  EmpathyGstAudioSrcPrivate *priv = EMPATHY_GST_AUDIO_SRC_GET_PRIVATE (src);
770
 
  Operation *operation;
771
 
  GSimpleAsyncResult *simple;
772
 
 
773
 
  simple = g_simple_async_result_new (G_OBJECT (src), callback, user_data,
774
 
      empathy_audio_src_get_microphones_async);
775
 
 
776
 
  /* If we can't change mic let's not pretend we can by returning the
777
 
   * list of available mics. */
778
 
  if (!empathy_audio_src_supports_changing_mic (src))
779
 
    {
780
 
      g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_FAILED,
781
 
          "pulsesrc is not new enough to support changing microphone");
782
 
      g_simple_async_result_complete_in_idle (simple);
783
 
      g_object_unref (simple);
784
 
      return;
785
 
    }
786
 
 
787
 
  operation = operation_new (operation_get_microphones, simple);
788
 
  g_queue_push_tail (priv->operations, operation);
789
 
 
790
 
  /* gogogogo */
791
 
  operations_run (src);
792
 
}
793
 
 
794
 
const GList *
795
 
empathy_audio_src_get_microphones_finish (EmpathyGstAudioSrc *src,
796
 
    GAsyncResult *result,
797
 
    GError **error)
798
 
{
799
 
  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result);
800
 
  GQueue *queue;
801
 
 
802
 
  if (g_simple_async_result_propagate_error (simple, error))
803
 
      return NULL;
804
 
 
805
 
  g_return_val_if_fail (g_simple_async_result_is_valid (result,
806
 
          G_OBJECT (src), empathy_audio_src_get_microphones_async),
807
 
      NULL);
808
 
 
809
 
  queue = g_simple_async_result_get_op_res_gpointer (simple);
810
 
  return queue->head;
811
 
}
812
 
 
813
516
guint
814
517
empathy_audio_src_get_microphone (EmpathyGstAudioSrc *src)
815
518
{
818
521
  return priv->source_idx;
819
522
}
820
523
 
 
524
static void
 
525
empathy_audio_src_change_microphone_cb (GObject *source_object,
 
526
    GAsyncResult *result,
 
527
    gpointer user_data)
 
528
{
 
529
  EmpathyMicMonitor *monitor = EMPATHY_MIC_MONITOR (source_object);
 
530
  GSimpleAsyncResult *simple = user_data;
 
531
  GError *error = NULL;
 
532
 
 
533
  if (!empathy_mic_monitor_change_microphone_finish (monitor,
 
534
          result, &error))
 
535
    {
 
536
      g_simple_async_result_take_error (simple, error);
 
537
    }
 
538
 
 
539
  g_simple_async_result_complete (simple);
 
540
  g_object_unref (simple);
 
541
}
 
542
 
821
543
void
822
544
empathy_audio_src_change_microphone_async (EmpathyGstAudioSrc *src,
823
545
    guint microphone,
827
549
  EmpathyGstAudioSrcPrivate *priv = EMPATHY_GST_AUDIO_SRC_GET_PRIVATE (src);
828
550
  guint source_output_idx;
829
551
  GSimpleAsyncResult *simple;
830
 
  Operation *operation;
831
552
 
832
553
  simple = g_simple_async_result_new (G_OBJECT (src), callback, user_data,
833
554
      empathy_audio_src_change_microphone_async);
852
573
      return;
853
574
    }
854
575
 
855
 
  g_simple_async_result_set_op_res_gpointer (simple,
856
 
      GUINT_TO_POINTER (microphone), NULL);
857
 
 
858
 
  operation = operation_new (operation_change_microphone, simple);
859
 
  g_queue_push_tail (priv->operations, operation);
860
 
 
861
 
  /* gogogogo */
862
 
  operations_run (src);
 
576
  empathy_mic_monitor_change_microphone_async (priv->mic_monitor,
 
577
      source_output_idx, microphone, empathy_audio_src_change_microphone_cb,
 
578
      simple);
863
579
}
864
580
 
865
581
gboolean